diff options
Diffstat (limited to 'lib')
-rw-r--r-- | lib/Shorturl_api.php | 3 | ||||
-rw-r--r-- | lib/action.php | 12 | ||||
-rw-r--r-- | lib/attachmentlist.php | 300 | ||||
-rw-r--r-- | lib/noticelist.php | 71 | ||||
-rw-r--r-- | lib/router.php | 14 | ||||
-rw-r--r-- | lib/util.php | 114 |
6 files changed, 414 insertions, 100 deletions
diff --git a/lib/Shorturl_api.php b/lib/Shorturl_api.php index fe106cb83..924aa93a8 100644 --- a/lib/Shorturl_api.php +++ b/lib/Shorturl_api.php @@ -22,6 +22,7 @@ if (!defined('LACONICA')) { exit(1); } class ShortUrlApi { protected $service_url; + protected $long_limit = 27; function __construct($service_url) { @@ -39,7 +40,7 @@ class ShortUrlApi } private function is_long($url) { - return strlen($url) >= 30; + return strlen($url) >= $this->long_limit; } protected function http_post($data) { diff --git a/lib/action.php b/lib/action.php index 3e43ffe3e..fc123a633 100644 --- a/lib/action.php +++ b/lib/action.php @@ -98,15 +98,15 @@ class Action extends HTMLOutputter // lawsuit Event::handle('EndShowHTML', array($this)); } if (Event::handle('StartShowHead', array($this))) { - $this->showHead(); + $this->showHead(); Event::handle('EndShowHead', array($this)); } if (Event::handle('StartShowBody', array($this))) { - $this->showBody(); + $this->showBody(); Event::handle('EndShowBody', array($this)); } if (Event::handle('StartEndHTML', array($this))) { - $this->endHTML(); + $this->endHTML(); Event::handle('EndEndHTML', array($this)); } } @@ -243,6 +243,12 @@ class Action extends HTMLOutputter // lawsuit $this->element('script', array('type' => 'text/javascript', 'src' => common_path('js/jquery.form.js')), ' '); + + $this->element('script', array('type' => 'text/javascript', + 'src' => common_path('js/jquery.joverlay.min.js')), + ' '); + + Event::handle('EndShowJQueryScripts', array($this)); } if (Event::handle('StartShowLaconicaScripts', array($this))) { diff --git a/lib/attachmentlist.php b/lib/attachmentlist.php new file mode 100644 index 000000000..9485fe3d6 --- /dev/null +++ b/lib/attachmentlist.php @@ -0,0 +1,300 @@ +<?php +/** + * Laconica, the distributed open-source microblogging tool + * + * widget for displaying a list of notice attachments + * + * PHP version 5 + * + * LICENCE: This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * @category UI + * @package Laconica + * @author Evan Prodromou <evan@controlyourself.ca> + * @author Sarven Capadisli <csarven@controlyourself.ca> + * @copyright 2008 Control Yourself, Inc. + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://laconi.ca/ + */ + +if (!defined('LACONICA')) { + exit(1); +} + +/** + * widget for displaying a list of notice attachments + * + * There are a number of actions that display a list of notices, in + * reverse chronological order. This widget abstracts out most of the + * code for UI for notice lists. It's overridden to hide some + * data for e.g. the profile page. + * + * @category UI + * @package Laconica + * @author Evan Prodromou <evan@controlyourself.ca> + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://laconi.ca/ + * @see Notice + * @see StreamAction + * @see NoticeListItem + * @see ProfileNoticeList + */ + +class AttachmentList extends Widget +{ + /** the current stream of notices being displayed. */ + + var $notice = null; + + /** + * constructor + * + * @param Notice $notice stream of notices from DB_DataObject + */ + + function __construct($notice, $out=null) + { + parent::__construct($out); + $this->notice = $notice; + } + + /** + * show the list of notices + * + * "Uses up" the stream by looping through it. So, probably can't + * be called twice on the same list. + * + * @return int count of notices listed. + */ + + function show() + { +// $this->out->elementStart('div', array('id' =>'attachments_primary')); + $this->out->elementStart('div', array('id' =>'content')); + $this->out->element('h2', null, _('Attachments')); + $this->out->elementStart('ul', array('class' => 'attachments')); + + $atts = new File; + $att = $atts->getAttachments($this->notice->id); + foreach ($att as $n=>$attachment) { + $item = $this->newListItem($attachment); + $item->show(); + } + + $this->out->elementEnd('ul'); + $this->out->elementEnd('div'); + + return count($att); + } + + /** + * returns a new list item for the current notice + * + * Recipe (factory?) method; overridden by sub-classes to give + * a different list item class. + * + * @param Notice $notice the current notice + * + * @return NoticeListItem a list item for displaying the notice + */ + + function newListItem($attachment) + { + return new AttachmentListItem($attachment, $this->out); + } +} + +/** + * widget for displaying a single notice + * + * This widget has the core smarts for showing a single notice: what to display, + * where, and under which circumstances. Its key method is show(); this is a recipe + * that calls all the other show*() methods to build up a single notice. The + * ProfileNoticeListItem subclass, for example, overrides showAuthor() to skip + * author info (since that's implicit by the data in the page). + * + * @category UI + * @package Laconica + * @author Evan Prodromou <evan@controlyourself.ca> + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://laconi.ca/ + * @see NoticeList + * @see ProfileNoticeListItem + */ + +class AttachmentListItem extends Widget +{ + /** The attachment this item will show. */ + + var $attachment = null; + + var $oembed = null; + + /** + * constructor + * + * Also initializes the profile attribute. + * + * @param Notice $notice The notice we'll display + */ + + function __construct($attachment, $out=null) + { + parent::__construct($out); + $this->attachment = $attachment; + $this->oembed = File_oembed::staticGet('file_id', $this->attachment->id); + } + + function title() { + if (empty($this->attachment->title)) { + if (empty($this->oembed->title)) { + $title = $this->attachment->url; + } else { + $title = $this->oembed->title; + } + } else { + $title = $this->attachment->title; + } + + return $title; + } + + function linkTitle() { + return 'Our page for ' . $this->title(); + } + + /** + * recipe function for displaying a single notice. + * + * This uses all the other methods to correctly display a notice. Override + * it or one of the others to fine-tune the output. + * + * @return void + */ + + function show() + { + $this->showStart(); + $this->showNoticeAttachment(); + $this->showEnd(); + } + + function linkAttr() { + return array('class' => 'attachment', 'href' => common_local_url('attachment', array('attachment' => $this->attachment->id))); + } + + function showLink() { + $attr = $this->linkAttr(); + $text = $this->linkTitle(); + $this->out->elementStart('h4'); + $this->out->element('a', $attr, $text); + + if ($this->attachment->url !== $this->title()) + $this->out->element('span', null, " ({$this->attachment->url})"); + + + $this->out->elementEnd('h4'); + } + + function showNoticeAttachment() + { + $this->showLink(); + $this->showRepresentation(); + } + + function showRepresentation() { + $thumbnail = File_thumbnail::staticGet('file_id', $this->attachment->id); + if (!empty($thumbnail)) { + $this->out->elementStart('a', $this->linkAttr()/*'href' => $this->linkTo()*/); + $this->out->element('img', array('alt' => 'nothing to say', 'src' => $thumbnail->url, 'width' => $thumbnail->width, 'height' => $thumbnail->height)); + $this->out->elementEnd('a'); + } + } + + /** + * start a single notice. + * + * @return void + */ + + function showStart() + { + // XXX: RDFa + // TODO: add notice_type class e.g., notice_video, notice_image + $this->out->elementStart('li'); + } + + /** + * finish the notice + * + * Close the last elements in the notice list item + * + * @return void + */ + + function showEnd() + { + $this->out->elementEnd('li'); + } +} + +class Attachment extends AttachmentListItem +{ + function show() { + $this->showNoticeAttachment(); + } + + function linkAttr() { + return array('class' => 'external', 'href' => $this->attachment->url); + } + + function linkTitle() { + return 'Direct link to ' . $this->title(); + } + + function showRepresentation() { + if (empty($this->oembed->type)) { + if (empty($this->attachment->mimetype)) { + $this->out->element('pre', null, 'oh well... not sure how to handle the following: ' . print_r($this->attachment, true)); + } else { + switch ($this->attachment->mimetype) { + case 'image/gif': + case 'image/png': + case 'image/jpg': + case 'image/jpeg': + $this->out->element('img', array('src' => $this->attachment->url, 'alt' => 'alt')); + break; + } + } + } else { + switch ($this->oembed->type) { + case 'rich': + case 'video': + case 'link': + if (!empty($this->oembed->html)) { + $this->out->raw($this->oembed->html); + } + break; + + case 'photo': + $this->out->element('img', array('src' => $this->oembed->url, 'width' => $this->oembed->width, 'height' => $this->oembed->height, 'alt' => 'alt')); + break; + + default: + $this->out->element('pre', null, 'oh well... not sure how to handle the following oembed: ' . print_r($this->oembed, true)); + } + } + } +} + diff --git a/lib/noticelist.php b/lib/noticelist.php index 8fccba73e..55dd902b4 100644 --- a/lib/noticelist.php +++ b/lib/noticelist.php @@ -179,22 +179,86 @@ class NoticeListItem extends Widget { $this->showStart(); $this->showNotice(); - $this->showNoticeInfo(); + $this->showNoticeAttachments(); $this->showNoticeOptions(); + $this->showNoticeInfo(); $this->showEnd(); } function showNotice() { - $this->out->elementStart('div', 'entry-title'); +if (0) + $this->out->elementStart('entry-title'); +else + + if ('shownotice' === $this->out->args['action']) { + $width = '85%'; + } else { + $width = '90%'; + } + + + $this->out->elementStart('div', array('class' => 'entry-title', 'style' => "float: left; width: $width;")); $this->showAuthor(); $this->showContent(); $this->out->elementEnd('div'); } + function showNoticeAttachments() + { + $f2p = new File_to_post; + $f2p->post_id = $this->notice->id; + $file = new File; + $file->joinAdd($f2p); + $file->selectAdd(); + $file->selectAdd('file.id as id'); + $count = $file->find(true); + if (!$count) return; + if (1 === $count) { + $href = common_local_url('attachment', array('attachment' => $file->id)); + $att_class = 'attachment'; + } else { + $href = common_local_url('attachments', array('notice' => $this->notice->id)); + $att_class = 'attachments'; + } + + $clip = theme_path('images/icons/clip', 'base'); + if ('shownotice' === $this->out->args['action']) { + $height = '96px'; + $width = '83%'; + $width_att = '15%'; + $clip .= '-big.png'; + $top = '70px'; + } else { + $height = '48px'; + $width = '90%'; + $width_att = '8%'; + $clip .= '.png'; + $top = '20px'; + } +if (0) + $this->out->elementStart('div', 'entry-attachments'); +else + $this->out->elementStart('p', array('class' => 'entry-attachments', 'style' => "float: right; width: $width_att; background: url($clip) no-repeat; text-align: right; height: $height;")); + $this->out->element('a', array('class' => $att_class, 'style' => "text-decoration: none; padding-top: $top; display: block; height: $height;", 'href' => $href, 'title' => "# of attachments: $count"), $count === 1 ? '' : $count); + + + $this->out->elementEnd('p'); + } + function showNoticeInfo() { +if(0) $this->out->elementStart('div', 'entry-content'); +else + + if ('shownotice' === $this->out->args['action']) { + $width = '85%'; + } else { + $width = '90%'; + } + + $this->out->elementStart('div', array('class' => 'entry-content', 'style' => "float: left; width: $width;")); $this->showNoticeLink(); $this->showNoticeSource(); $this->showContext(); @@ -205,7 +269,10 @@ class NoticeListItem extends Widget { $user = common_current_user(); if ($user) { +if(0) $this->out->elementStart('div', 'notice-options'); +else + $this->out->elementStart('div', array('class' => 'notice-options', 'style' => 'float: right; width: 16%;')); $this->showFaveForm(); $this->showReplyLink(); $this->showDeleteLink(); diff --git a/lib/router.php b/lib/router.php index 9308c818a..635e1d77e 100644 --- a/lib/router.php +++ b/lib/router.php @@ -151,12 +151,26 @@ class Router $m->connect('search/notice/rss?q=:q', array('action' => 'noticesearchrss'), array('q' => '.+')); + $m->connect('attachment/:attachment/ajax', + array('action' => 'attachment_ajax'), + array('notice' => '[0-9]+')); + + $m->connect('attachment/:attachment', + array('action' => 'attachment'), + array('notice' => '[0-9]+')); + // notice $m->connect('notice/new', array('action' => 'newnotice')); $m->connect('notice/new?replyto=:replyto', array('action' => 'newnotice'), array('replyto' => '[A-Za-z0-9_-]+')); + $m->connect('notice/:notice/attachments/ajax', + array('action' => 'attachments_ajax'), + array('notice' => '[0-9]+')); + $m->connect('notice/:notice/attachments', + array('action' => 'attachments'), + array('notice' => '[0-9]+')); $m->connect('notice/:notice', array('action' => 'shownotice'), array('notice' => '[0-9]+')); diff --git a/lib/util.php b/lib/util.php index 198185338..25c0fb0a1 100644 --- a/lib/util.php +++ b/lib/util.php @@ -395,7 +395,7 @@ function common_render_text($text) return $r; } -function common_replace_urls_callback($text, $callback) { +function common_replace_urls_callback($text, $callback, $notice_id = null) { // Start off with a regex $regex = '#'. '(?:'. @@ -466,7 +466,11 @@ function common_replace_urls_callback($text, $callback) { $url = (mb_strpos($orig_url, htmlspecialchars($url)) === FALSE) ? $url:htmlspecialchars($url); // Call user specified func - $modified_url = call_user_func($callback, $url); + if (empty($notice_id)) { + $modified_url = call_user_func($callback, $url); + } else { + $modified_url = call_user_func($callback, array($url, $notice_id)); + } // Replace it! $start = mb_strpos($text, $url, $offset); @@ -481,107 +485,29 @@ function common_linkify($url) { // It comes in special'd, so we unspecial it before passing to the stringifying // functions $url = htmlspecialchars_decode($url); - $display = $url; - $url = (!preg_match('#^([a-z]+://|(mailto|aim|tel):)#i', $url)) ? 'http://'.$url : $url; - - $attrs = array('href' => $url, 'rel' => 'external'); + $display = File_redirection::_canonUrl($url); + $longurl_data = File_redirection::where($url); + if (is_array($longurl_data)) { + $longurl = $longurl_data['url']; + } elseif (is_string($longurl_data)) { + $longurl = $longurl_data; + } else { + die('impossible to linkify'); + } - if ($longurl = common_longurl($url)) { + $attrs = array('href' => $longurl, 'rel' => 'external'); +if(0){ + if ($longurl !== $url) { $attrs['title'] = $longurl; } - - return XMLStringer::estring('a', $attrs, $display); -} - -function common_longurl($short_url) -{ - $long_url = common_shorten_link($short_url, true); - if ($long_url === $short_url) return false; - return $long_url; } - -function common_longurl2($uri) -{ - $uri_e = urlencode($uri); - $longurl = unserialize(file_get_contents("http://api.longurl.org/v1/expand?format=php&url=$uri_e")); - if (empty($longurl['long_url']) || $uri === $longurl['long_url']) return false; - return stripslashes($longurl['long_url']); + return XMLStringer::estring('a', $attrs, $display); } function common_shorten_links($text) { if (mb_strlen($text) <= 140) return $text; - static $cache = array(); - if (isset($cache[$text])) return $cache[$text]; - // \s = not a horizontal whitespace character (since PHP 5.2.4) - return $cache[$text] = common_replace_urls_callback($text, 'common_shorten_link');; -} - -function common_shorten_link($url, $reverse = false) -{ - - static $url_cache = array(); - if ($reverse) return isset($url_cache[$url]) ? $url_cache[$url] : $url; - - $user = common_current_user(); - if (!isset($user)) { - // common current user does not find a user when called from the XMPP daemon - // therefore we'll set one here fix, so that XMPP given URLs may be shortened - $user->urlshorteningservice = 'ur1.ca'; - } - $curlh = curl_init(); - curl_setopt($curlh, CURLOPT_CONNECTTIMEOUT, 20); // # seconds to wait - curl_setopt($curlh, CURLOPT_USERAGENT, 'Laconica'); - curl_setopt($curlh, CURLOPT_RETURNTRANSFER, true); - - switch($user->urlshorteningservice) { - case 'ur1.ca': - $short_url_service = new LilUrl; - $short_url = $short_url_service->shorten($url); - break; - - case '2tu.us': - $short_url_service = new TightUrl; - $short_url = $short_url_service->shorten($url); - break; - - case 'ptiturl.com': - $short_url_service = new PtitUrl; - $short_url = $short_url_service->shorten($url); - break; - - case 'bit.ly': - curl_setopt($curlh, CURLOPT_URL, 'http://bit.ly/api?method=shorten&long_url='.urlencode($url)); - $short_url = current(json_decode(curl_exec($curlh))->results)->hashUrl; - break; - - case 'is.gd': - curl_setopt($curlh, CURLOPT_URL, 'http://is.gd/api.php?longurl='.urlencode($url)); - $short_url = curl_exec($curlh); - break; - case 'snipr.com': - curl_setopt($curlh, CURLOPT_URL, 'http://snipr.com/site/snip?r=simple&link='.urlencode($url)); - $short_url = curl_exec($curlh); - break; - case 'metamark.net': - curl_setopt($curlh, CURLOPT_URL, 'http://metamark.net/api/rest/simple?long_url='.urlencode($url)); - $short_url = curl_exec($curlh); - break; - case 'tinyurl.com': - curl_setopt($curlh, CURLOPT_URL, 'http://tinyurl.com/api-create.php?url='.urlencode($url)); - $short_url = curl_exec($curlh); - break; - default: - $short_url = false; - } - - curl_close($curlh); - - if ($short_url) { - $url_cache[(string)$short_url] = $url; - return (string)$short_url; - } - return $url; + return common_replace_urls_callback($text, array('File_redirection', 'makeShort')); } function common_xml_safe_str($str) |