diff options
-rw-r--r-- | actions/attachment.php | 250 | ||||
-rw-r--r-- | actions/attachment_ajax.php | 141 | ||||
-rw-r--r-- | actions/attachments.php | 292 | ||||
-rw-r--r-- | actions/attachments_ajax.php | 115 | ||||
-rw-r--r-- | actions/tag.php | 12 | ||||
-rw-r--r-- | classes/File.php | 11 | ||||
-rw-r--r-- | classes/File_oembed.php | 11 | ||||
-rw-r--r-- | classes/File_thumbnail.php | 10 | ||||
-rw-r--r-- | classes/Notice.php | 12 | ||||
-rw-r--r-- | js/jquery.joverlay.min.js | 6 | ||||
-rw-r--r-- | js/util.js | 4 | ||||
-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-- | theme/base/images/icons/clip-big.png | bin | 0 -> 11245 bytes | |||
-rw-r--r-- | theme/base/images/icons/clip.png | bin | 0 -> 2298 bytes |
17 files changed, 1243 insertions, 18 deletions
diff --git a/actions/attachment.php b/actions/attachment.php new file mode 100644 index 000000000..981882a5b --- /dev/null +++ b/actions/attachment.php @@ -0,0 +1,250 @@ +<?php +/** + * Laconica, the distributed open-source microblogging tool + * + * Show 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 Personal + * @package Laconica + * @author Evan Prodromou <evan@controlyourself.ca> + * @copyright 2008-2009 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); +} + +//require_once INSTALLDIR.'/lib/personalgroupnav.php'; +//require_once INSTALLDIR.'/lib/feedlist.php'; +require_once INSTALLDIR.'/lib/attachmentlist.php'; + +/** + * Show notice attachments + * + * @category Personal + * @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/ + */ + +class AttachmentAction extends Action +{ + /** + * Attachment object to show + */ + + var $attachment = null; + + /** + * Profile of the notice object + */ + +// var $profile = null; + + /** + * Avatar of the profile of the notice object + */ + +// var $avatar = null; + + /** + * Load attributes based on database arguments + * + * Loads all the DB stuff + * + * @param array $args $_REQUEST array + * + * @return success flag + */ + + function prepare($args) + { + parent::prepare($args); + + $id = $this->arg('attachment'); + + $this->attachment = File::staticGet($id); + + if (!$this->attachment) { + $this->clientError(_('No such attachment.'), 404); + return false; + } + return true; + } + + /** + * Is this action read-only? + * + * @return boolean true + */ + + function isReadOnly($args) + { + return true; + } + + /** + * Title of the page + * + * @return string title of the page + */ + function title() + { + $a = new Attachment($this->attachment); + return $a->title(); + } + + + + /** + * Last-modified date for page + * + * When was the content of this page last modified? Based on notice, + * profile, avatar. + * + * @return int last-modified date as unix timestamp + */ +/* + function lastModified() + { + return max(strtotime($this->notice->created), + strtotime($this->profile->modified), + ($this->avatar) ? strtotime($this->avatar->modified) : 0); + } +*/ + + /** + * An entity tag for this page + * + * Shows the ETag for the page, based on the notice ID and timestamps + * for the notice, profile, and avatar. It's weak, since we change + * the date text "one hour ago", etc. + * + * @return string etag + */ +/* + function etag() + { + $avtime = ($this->avatar) ? + strtotime($this->avatar->modified) : 0; + + return 'W/"' . implode(':', array($this->arg('action'), + common_language(), + $this->notice->id, + strtotime($this->notice->created), + strtotime($this->profile->modified), + $avtime)) . '"'; + } +*/ + + + /** + * Handle input + * + * Only handles get, so just show the page. + * + * @param array $args $_REQUEST data (unused) + * + * @return void + */ + + function handle($args) + { + parent::handle($args); + $this->showPage(); + } + + /** + * Don't show local navigation + * + * @return void + */ + + function showLocalNavBlock() + { + } + + /** + * Fill the content area of the page + * + * Shows a single notice list item. + * + * @return void + */ + + function showContent() + { + $this->elementStart('ul', array('class' => 'attachments')); + $ali = new Attachment($this->attachment, $this); + $cnt = $ali->show(); + $this->elementEnd('ul'); + } + + /** + * Don't show page notice + * + * @return void + */ + + function showPageNoticeBlock() + { + } + + /** + * Show aside: this attachments appears in what notices + * + * @return void + */ + + function showAside() { + $notice = new Notice; + $f2p = new File_to_post; + $f2p->file_id = $this->attachment->id; + $notice->joinAdd($f2p); + $notice->orderBy('created desc'); + $x = $notice->find(); + $this->elementStart('ol'); + while($notice->fetch()) { + $this->elementStart('li'); + $profile = $notice->getProfile(); + $this->element('a', array('href' => $notice->uri), $profile->nickname . ' on ' . $notice->created); + $this->elementEnd('li'); + } + $this->elementEnd('ol'); + $notice->free(); + $f2p->free(); + + $notice_tag = new Notice_tag; + $attachment = new File; + + $query = 'select tag,count(tag) as c from notice_tag join file_to_post on (notice_tag.notice_id=post_id) join notice on notice_id = notice.id where file_id=' . $notice_tag->escape($this->attachment->id) . ' group by tag order by c desc'; + + $notice_tag->query($query); + $this->elementStart('ol'); + while($notice_tag->fetch()) { + $this->elementStart('li'); + $href = common_local_url('tag', array('tag' => $notice_tag->tag)); + $this->element('a', array('href' => $href), $notice_tag->tag . ' (' . $notice_tag->c . ')'); + $this->elementEnd('li'); + } + $this->elementEnd('ol'); + } +} diff --git a/actions/attachment_ajax.php b/actions/attachment_ajax.php new file mode 100644 index 000000000..1620b27dd --- /dev/null +++ b/actions/attachment_ajax.php @@ -0,0 +1,141 @@ +<?php +/** + * Laconica, the distributed open-source microblogging tool + * + * Show 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 Personal + * @package Laconica + * @author Evan Prodromou <evan@controlyourself.ca> + * @copyright 2008-2009 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); +} + +require_once INSTALLDIR.'/actions/attachment.php'; + +/** + * Show notice attachments + * + * @category Personal + * @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/ + */ + +class Attachment_ajaxAction extends AttachmentAction +{ + /** + * Load attributes based on database arguments + * + * Loads all the DB stuff + * + * @param array $args $_REQUEST array + * + * @return success flag + */ + + function prepare($args) + { + parent::prepare($args); + if (!$this->attachment) { + $this->clientError(_('No such attachment.'), 404); + return false; + } + return true; + } + + /** + * Show page, a template method. + * + * @return nothing + */ + function showPage() + { + if (Event::handle('StartShowBody', array($this))) { + $this->showCore(); + Event::handle('EndShowBody', array($this)); + } + } + + /** + * Show core. + * + * Shows local navigation, content block and aside. + * + * @return nothing + */ + function showCore() + { + $this->elementStart('div', array('id' => 'core')); + if (Event::handle('StartShowContentBlock', array($this))) { + $this->showContentBlock(); + Event::handle('EndShowContentBlock', array($this)); + } + $this->elementEnd('div'); + } + + + + /** + * Last-modified date for page + * + * When was the content of this page last modified? Based on notice, + * profile, avatar. + * + * @return int last-modified date as unix timestamp + */ +/* + function lastModified() + { + return max(strtotime($this->notice->created), + strtotime($this->profile->modified), + ($this->avatar) ? strtotime($this->avatar->modified) : 0); + } +*/ + + /** + * An entity tag for this page + * + * Shows the ETag for the page, based on the notice ID and timestamps + * for the notice, profile, and avatar. It's weak, since we change + * the date text "one hour ago", etc. + * + * @return string etag + */ +/* + function etag() + { + $avtime = ($this->avatar) ? + strtotime($this->avatar->modified) : 0; + + return 'W/"' . implode(':', array($this->arg('action'), + common_language(), + $this->notice->id, + strtotime($this->notice->created), + strtotime($this->profile->modified), + $avtime)) . '"'; + } +*/ +} + diff --git a/actions/attachments.php b/actions/attachments.php new file mode 100644 index 000000000..6b31c839d --- /dev/null +++ b/actions/attachments.php @@ -0,0 +1,292 @@ +<?php +/** + * Laconica, the distributed open-source microblogging tool + * + * Show 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 Personal + * @package Laconica + * @author Evan Prodromou <evan@controlyourself.ca> + * @copyright 2008-2009 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); +} + +//require_once INSTALLDIR.'/lib/personalgroupnav.php'; +//require_once INSTALLDIR.'/lib/feedlist.php'; +require_once INSTALLDIR.'/lib/attachmentlist.php'; + +/** + * Show notice attachments + * + * @category Personal + * @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/ + */ + +class AttachmentsAction extends Action +{ + /** + * Notice object to show + */ + + var $notice = null; + + /** + * Profile of the notice object + */ + + var $profile = null; + + /** + * Avatar of the profile of the notice object + */ + + var $avatar = null; + + /** + * Is this action read-only? + * + * @return boolean true + */ + + function isReadOnly($args) + { + return true; + } + + /** + * Last-modified date for page + * + * When was the content of this page last modified? Based on notice, + * profile, avatar. + * + * @return int last-modified date as unix timestamp + */ + + function lastModified() + { + return max(strtotime($this->notice->created), + strtotime($this->profile->modified), + ($this->avatar) ? strtotime($this->avatar->modified) : 0); + } + + /** + * An entity tag for this page + * + * Shows the ETag for the page, based on the notice ID and timestamps + * for the notice, profile, and avatar. It's weak, since we change + * the date text "one hour ago", etc. + * + * @return string etag + */ + + function etag() + { + $avtime = ($this->avatar) ? + strtotime($this->avatar->modified) : 0; + + return 'W/"' . implode(':', array($this->arg('action'), + common_language(), + $this->notice->id, + strtotime($this->notice->created), + strtotime($this->profile->modified), + $avtime)) . '"'; + } + + /** + * Title of the page + * + * @return string title of the page + */ + + function title() + { + return sprintf(_('%1$s\'s status on %2$s'), + $this->profile->nickname, + common_exact_date($this->notice->created)); + } + + + /** + * Load attributes based on database arguments + * + * Loads all the DB stuff + * + * @param array $args $_REQUEST array + * + * @return success flag + */ + + function prepare($args) + { + parent::prepare($args); + + $id = $this->arg('notice'); + + $this->notice = Notice::staticGet($id); + + if (!$this->notice) { + $this->clientError(_('No such notice.'), 404); + return false; + } + + +/* +// STOP if there are no attachments +// maybe even redirect if there's a single one +// RYM FIXME TODO + $this->clientError(_('No such attachment.'), 404); + return false; + +*/ + + + + + $this->profile = $this->notice->getProfile(); + + if (!$this->profile) { + $this->serverError(_('Notice has no profile'), 500); + return false; + } + + $this->avatar = $this->profile->getAvatar(AVATAR_PROFILE_SIZE); + return true; + } + + + + /** + * Handle input + * + * Only handles get, so just show the page. + * + * @param array $args $_REQUEST data (unused) + * + * @return void + */ + + function handle($args) + { + parent::handle($args); + + if ($this->notice->is_local == 0) { + if (!empty($this->notice->url)) { + common_redirect($this->notice->url, 301); + } else if (!empty($this->notice->uri) && preg_match('/^https?:/', $this->notice->uri)) { + common_redirect($this->notice->uri, 301); + } + } else { + $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) { + common_redirect(common_local_url('attachment', array('attachment' => $file->id)), 301); + } else { + $this->showPage(); + } + } + } + + /** + * Don't show local navigation + * + * @return void + */ + + function showLocalNavBlock() + { + } + + /** + * Fill the content area of the page + * + * Shows a single notice list item. + * + * @return void + */ + + function showContent() + { + $al = new AttachmentList($this->notice, $this); + $cnt = $al->show(); + } + + /** + * Don't show page notice + * + * @return void + */ + + function showPageNoticeBlock() + { + } + + /** + * Don't show aside + * + * @return void + */ + + function showAside() { + } + + /** + * Extra <head> content + * + * We show the microid(s) for the author, if any. + * + * @return void + */ + + function extraHead() + { + $user = User::staticGet($this->profile->id); + + if (!$user) { + return; + } + + if ($user->emailmicroid && $user->email && $this->notice->uri) { + $id = new Microid('mailto:'. $user->email, + $this->notice->uri); + $this->element('meta', array('name' => 'microid', + 'content' => $id->toString())); + } + + if ($user->jabbermicroid && $user->jabber && $this->notice->uri) { + $id = new Microid('xmpp:', $user->jabber, + $this->notice->uri); + $this->element('meta', array('name' => 'microid', + 'content' => $id->toString())); + } + } +} + diff --git a/actions/attachments_ajax.php b/actions/attachments_ajax.php new file mode 100644 index 000000000..402d8b5e7 --- /dev/null +++ b/actions/attachments_ajax.php @@ -0,0 +1,115 @@ +<?php +/** + * Laconica, the distributed open-source microblogging tool + * + * Show 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 Personal + * @package Laconica + * @author Evan Prodromou <evan@controlyourself.ca> + * @copyright 2008-2009 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); +} + +//require_once INSTALLDIR.'/lib/personalgroupnav.php'; +//require_once INSTALLDIR.'/lib/feedlist.php'; +require_once INSTALLDIR.'/actions/attachments.php'; + +/** + * Show notice attachments + * + * @category Personal + * @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/ + */ + +class Attachments_ajaxAction extends AttachmentsAction +{ + function showContent() + { + } + + /** + * Fill the content area of the page + * + * Shows a single notice list item. + * + * @return void + */ + + function showContentBlock() + { + $al = new AttachmentList($this->notice, $this); + $cnt = $al->show(); + } + + /** + * Extra <head> content + * + * We show the microid(s) for the author, if any. + * + * @return void + */ + + function extraHead() + { + } + + + /** + * Show page, a template method. + * + * @return nothing + */ + function showPage() + { + if (Event::handle('StartShowBody', array($this))) { + $this->showCore(); + Event::handle('EndShowBody', array($this)); + } + } + + /** + * Show core. + * + * Shows local navigation, content block and aside. + * + * @return nothing + */ + function showCore() + { + $this->elementStart('div', array('id' => 'core')); + if (Event::handle('StartShowContentBlock', array($this))) { + $this->showContentBlock(); + Event::handle('EndShowContentBlock', array($this)); + } + $this->elementEnd('div'); + } + + + + +} + diff --git a/actions/tag.php b/actions/tag.php index 02f3e3522..2202f9bb0 100644 --- a/actions/tag.php +++ b/actions/tag.php @@ -49,8 +49,18 @@ class TagAction extends Action { $pop = new PopularNoticeSection($this); $pop->show(); - } + $notice_tag = new Notice_tag; + $query = 'select file_id, count(file_id) as c from notice_tag join file_to_post on post_id = notice_id where tag="' . $notice_tag->escape($this->tag) . '" group by file_id order by c desc'; + $notice_tag->query($query); + $this->elementStart('ol'); + while ($notice_tag->fetch()) { + $this->elementStart('li'); + $this->element('a', array('class' => 'attachment', 'href' => common_local_url('attachment', array('attachment' => $notice_tag->file_id))), "Attachment tagged {$notice_tag->c} times"); + $this->elementEnd('li'); + } + $this->elementEnd('ol'); + } function title() { diff --git a/classes/File.php b/classes/File.php index 2ddc5deb8..e5913115b 100644 --- a/classes/File.php +++ b/classes/File.php @@ -54,6 +54,17 @@ class File extends Memcached_DataObject return 'http://www.facebook.com/login.php' === $url; } + function getAttachments($post_id) { + $query = "select file.* from file join file_to_post on (file_id = file.id) join notice on (post_id = notice.id) where post_id = " . $this->escape($post_id); + $this->query($query); + $att = array(); + while ($this->fetch()) { + $att[] = clone($this); + } + $this->free(); + return $att; + } + function saveNew($redir_data, $given_url) { $x = new File; $x->url = $given_url; diff --git a/classes/File_oembed.php b/classes/File_oembed.php index 2846f49db..f1b2cb13c 100644 --- a/classes/File_oembed.php +++ b/classes/File_oembed.php @@ -78,18 +78,9 @@ class File_oembed extends Memcached_DataObject if (!empty($data['author_url'])) $file_oembed->author_url = $data['author_url']; if (!empty($data['url'])) $file_oembed->url = $data['url']; $file_oembed->insert(); - if (!empty($data['thumbnail_url'])) { - $tn = new File_thumbnail; - $tn->file_id = $file_id; - $tn->url = $data['thumbnail_url']; - $tn->width = intval($data['thumbnail_width']); - $tn->height = intval($data['thumbnail_height']); - $tn->insert(); + File_thumbnail::saveNew($data, $file_id); } - - - } } diff --git a/classes/File_thumbnail.php b/classes/File_thumbnail.php index 7b906a07c..1a65b92c9 100644 --- a/classes/File_thumbnail.php +++ b/classes/File_thumbnail.php @@ -42,4 +42,14 @@ class File_thumbnail extends Memcached_DataObject /* the code above is auto generated do not remove the tag below */ ###END_AUTOCODE + + function saveNew($data, $file_id) { + $tn = new File_thumbnail; + $tn->file_id = $file_id; + $tn->url = $data['thumbnail_url']; + $tn->width = intval($data['thumbnail_width']); + $tn->height = intval($data['thumbnail_height']); + $tn->insert(); + } } + diff --git a/classes/Notice.php b/classes/Notice.php index c2fa2d19e..30508070e 100644 --- a/classes/Notice.php +++ b/classes/Notice.php @@ -124,8 +124,6 @@ class Notice extends Memcached_DataObject $profile = Profile::staticGet($profile_id); -// $final = common_shorten_links($content); - if (!$profile) { common_log(LOG_ERR, 'Problem saving notice. Unknown user.'); return _('Problem saving notice. Unknown user.'); @@ -279,6 +277,16 @@ class Notice extends Memcached_DataObject return true; } + function hasAttachments() { + $post = clone($this); + $query = "select count(file_id) as n_attachments from file join file_to_post on (file_id = file.id) join notice on (post_id = notice.id) where post_id = " . $post->escape($this->id); + $post->query($query); + $post->fetch(); + $n_attachments = intval($post->n_attachments); + $post->free(); + return $n_attachments; + } + function blowCaches($blowLast=false) { $this->blowSubsCache($blowLast); diff --git a/js/jquery.joverlay.min.js b/js/jquery.joverlay.min.js new file mode 100644 index 000000000..c9168506a --- /dev/null +++ b/js/jquery.joverlay.min.js @@ -0,0 +1,6 @@ +/* Copyright (c) 2009 Alvaro A. Lima Jr http://alvarojunior.com/jquery/joverlay.html + * Licensed under the MIT (http://www.opensource.org/licenses/mit-license.php) + * Version: 0.6 (Abr 23, 2009) + * Requires: jQuery 1.3+ + */ +(function($){var f=$.browser.msie&&$.browser.version==6.0;var g=null;$.fn.jOverlay=function(b){var b=$.extend({},$.fn.jOverlay.options,b);if(g!=null){clearTimeout(g)}var c=this.is('*')?this:'#jOverlayContent';var d=f?'absolute':'fixed';var e=b.imgLoading?"<img id='jOverlayLoading' src='"+b.imgLoading+"' style='position:"+d+"; z-index:"+(b.zIndex+9)+";'/>":'';$('body').prepend(e+"<div id='jOverlay' />"+"<div id='jOverlayContent' style='position:"+d+"; z-index:"+(b.zIndex+5)+"; display:none;'/>");$('#jOverlayLoading').load(function(){if(b.center){$.center(this)}});if(f){$("select").hide();$("#jOverlayContent select").show()}$('#jOverlay').css({backgroundColor:b.color,position:d,top:'0px',left:'0px',filter:'alpha(opacity='+(b.opacity*100)+')',opacity:b.opacity,zIndex:b.zIndex,width:!f?'100%':$(window).width()+'px',height:!f?'100%':$(document).height()+'px'}).show();if(this.is('*')){$('#jOverlayContent').html(this.addClass('jOverlayChildren').show()).show();if(b.center){$.center('#jOverlayContent')}if(!b.url&&$.isFunction(b.success)){b.success(this.html())}}if(b.url){$.ajax({type:b.method,data:b.data,url:b.url,success:function(a){$('#jOverlayLoading').fadeOut(600);$(c).html(a).show();if(b.center){$.center('#jOverlayContent')}if($.isFunction(b.success)){b.success(a)}}})}if(f){$(window).scroll(function(){if(b.center){$.center('#jOverlayContent')}});$(window).resize(function(){$('#jOverlay').css({width:$(window).width()+'px',height:$(document).height()+'px'});if(b.center){$.center('#jOverlayContent')}})}$(document).keydown(function(a){if(a.keyCode==27){$.closeOverlay()}});if(b.bgClickToClose){$('#jOverlay').click($.closeOverlay)}if(Number(b.timeout)>0){g=setTimeout($.closeOverlay,Number(b.timeout))}};$.center=function(a){var a=$(a);var b=a.height();var c=a.width();a.css({width:c+'px',marginLeft:'-'+(c/2)+'px',marginTop:'-'+b/2+'px',height:'auto',top:!f?'50%':$(window).scrollTop()+($(window).height()/2)+"px",left:'50%'})};$.fn.jOverlay.options={method:'GET',data:'',url:'',color:'#000',opacity:'0.6',zIndex:9999,center:true,imgLoading:'',bgClickToClose:true,success:null,timeout:0};$.closeOverlay=function(){if(f){$("select").show()}$('#jOverlayContent .jOverlayChildren').hide().prependTo($('body'));$('#jOverlayLoading, #jOverlayContent, #jOverlay').remove()}})(jQuery);
\ No newline at end of file diff --git a/js/util.js b/js/util.js index 3f14bc61c..31d9eb4f5 100644 --- a/js/util.js +++ b/js/util.js @@ -17,6 +17,10 @@ */ $(document).ready(function(){ + $('.attachments').click(function() {$().jOverlay({zIndex:999, success:function(html) {$('.attachment').click(function() {$().jOverlay({url:$(this).attr('href') + '/ajax'}); return false; }); + }, url:$(this).attr('href') + '/ajax'}); return false; }); + $('.attachment').click(function() {$().jOverlay({url:$(this).attr('href') + '/ajax'}); return false; }); + // count character on keyup function counter(event){ var maxLength = 140; 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/theme/base/images/icons/clip-big.png b/theme/base/images/icons/clip-big.png Binary files differnew file mode 100644 index 000000000..3945f56cc --- /dev/null +++ b/theme/base/images/icons/clip-big.png diff --git a/theme/base/images/icons/clip.png b/theme/base/images/icons/clip.png Binary files differnew file mode 100644 index 000000000..3c5a17d18 --- /dev/null +++ b/theme/base/images/icons/clip.png |