summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--actions/attachment.php250
-rw-r--r--actions/attachment_ajax.php141
-rw-r--r--actions/attachments.php292
-rw-r--r--actions/attachments_ajax.php115
-rw-r--r--actions/newnotice.php23
-rw-r--r--actions/tag.php12
-rw-r--r--classes/File.php123
-rw-r--r--classes/File_oembed.php87
-rw-r--r--classes/File_redirection.php276
-rw-r--r--classes/File_thumbnail.php55
-rw-r--r--classes/File_to_post.php60
-rw-r--r--classes/Notice.php16
-rw-r--r--[-rwxr-xr-x]classes/laconica.ini61
-rw-r--r--classes/laconica.links.ini14
-rw-r--r--js/jquery.joverlay.min.js6
-rw-r--r--js/util.js4
-rw-r--r--lib/Shorturl_api.php3
-rw-r--r--lib/action.php12
-rw-r--r--lib/attachmentlist.php300
-rw-r--r--lib/noticelist.php71
-rw-r--r--lib/router.php14
-rw-r--r--lib/util.php114
-rw-r--r--theme/base/images/icons/clip-big.pngbin0 -> 11245 bytes
-rw-r--r--theme/base/images/icons/clip.pngbin0 -> 2298 bytes
24 files changed, 1942 insertions, 107 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/newnotice.php b/actions/newnotice.php
index cbd04c58b..ae0ff9636 100644
--- a/actions/newnotice.php
+++ b/actions/newnotice.php
@@ -158,7 +158,8 @@ class NewnoticeAction extends Action
$replyto = 'false';
}
- $notice = Notice::saveNew($user->id, $content, 'web', 1,
+// $notice = Notice::saveNew($user->id, $content_shortened, 'web', 1,
+ $notice = Notice::saveNew($user->id, $content_shortened, 'web', 1,
($replyto == 'false') ? null : $replyto);
if (is_string($notice)) {
@@ -166,6 +167,8 @@ class NewnoticeAction extends Action
return;
}
+ $this->saveUrls($notice);
+
common_broadcast_notice($notice);
if ($this->boolean('ajax')) {
@@ -191,6 +194,24 @@ class NewnoticeAction extends Action
}
}
+ /** save all urls in the notice to the db
+ *
+ * follow redirects and save all available file information
+ * (mimetype, date, size, oembed, etc.)
+ *
+ * @param class $notice Notice to pull URLs from
+ *
+ * @return void
+ */
+ function saveUrls($notice) {
+ common_replace_urls_callback($notice->content, array($this, 'saveUrl'), $notice->id);
+ }
+
+ function saveUrl($data) {
+ list($url, $notice_id) = $data;
+ $zzz = File::processNew($url, $notice_id);
+ }
+
/**
* Show an Ajax-y error message
*
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
new file mode 100644
index 000000000..e5913115b
--- /dev/null
+++ b/classes/File.php
@@ -0,0 +1,123 @@
+<?php
+/*
+ * Laconica - a distributed open-source microblogging tool
+ * Copyright (C) 2008, Controlez-Vous, Inc.
+ *
+ * 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/>.
+ */
+
+if (!defined('LACONICA')) { exit(1); }
+
+require_once INSTALLDIR.'/classes/Memcached_DataObject.php';
+require_once INSTALLDIR.'/classes/File_redirection.php';
+require_once INSTALLDIR.'/classes/File_oembed.php';
+require_once INSTALLDIR.'/classes/File_thumbnail.php';
+require_once INSTALLDIR.'/classes/File_to_post.php';
+//require_once INSTALLDIR.'/classes/File_redirection.php';
+
+/**
+ * Table Definition for file
+ */
+
+class File extends Memcached_DataObject
+{
+ ###START_AUTOCODE
+ /* the code below is auto generated do not remove the above tag */
+
+ public $__table = 'file'; // table name
+ public $id; // int(11) not_null primary_key group_by
+ public $url; // varchar(255) unique_key
+ public $mimetype; // varchar(50)
+ public $size; // int(11) group_by
+ public $title; // varchar(255)
+ public $date; // int(11) group_by
+ public $protected; // int(1) group_by
+
+ /* Static get */
+ function staticGet($k,$v=NULL) { return DB_DataObject::staticGet('File',$k,$v); }
+
+ /* the code above is auto generated do not remove the tag below */
+ ###END_AUTOCODE
+
+ function isProtected($url) {
+ 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;
+ if (!empty($redir_data['protected'])) $x->protected = $redir_data['protected'];
+ if (!empty($redir_data['title'])) $x->title = $redir_data['title'];
+ if (!empty($redir_data['type'])) $x->mimetype = $redir_data['type'];
+ if (!empty($redir_data['size'])) $x->size = intval($redir_data['size']);
+ if (isset($redir_data['time']) && $redir_data['time'] > 0) $x->date = intval($redir_data['time']);
+ $file_id = $x->insert();
+
+ if (isset($redir_data['type'])
+ && ('text/html' === substr($redir_data['type'], 0, 9))
+ && ($oembed_data = File_oembed::_getOembed($given_url))
+ && isset($oembed_data['json'])) {
+
+ File_oembed::saveNew($oembed_data['json'], $file_id);
+ }
+ return $x;
+ }
+
+ function processNew($given_url, $notice_id) {
+ if (empty($given_url)) return -1; // error, no url to process
+ $given_url = File_redirection::_canonUrl($given_url);
+ if (empty($given_url)) return -1; // error, no url to process
+ $file = File::staticGet('url', $given_url);
+ if (empty($file->id)) {
+ $file_redir = File_redirection::staticGet('url', $given_url);
+ if (empty($file_redir->id)) {
+ $redir_data = File_redirection::where($given_url);
+ $redir_url = $redir_data['url'];
+ if ($redir_url === $given_url) {
+ $x = File::saveNew($redir_data, $given_url);
+ $file_id = $x->id;
+
+ } else {
+ $x = File::processNew($redir_url, $notice_id);
+ $file_id = $x->id;
+ File_redirection::saveNew($redir_data, $file_id, $given_url);
+ }
+ } else {
+ $file_id = $file_redir->file_id;
+ }
+ } else {
+ $file_id = $file->id;
+ $x = $file;
+ }
+
+ if (empty($x)) {
+ $x = File::staticGet($file_id);
+ if (empty($x)) die('Impossible!');
+ }
+
+ File_to_post::processNew($file_id, $notice_id);
+ return $x;
+ }
+}
diff --git a/classes/File_oembed.php b/classes/File_oembed.php
new file mode 100644
index 000000000..f1b2cb13c
--- /dev/null
+++ b/classes/File_oembed.php
@@ -0,0 +1,87 @@
+<?php
+/*
+ * Laconica - a distributed open-source microblogging tool
+ * Copyright (C) 2008, Controlez-Vous, Inc.
+ *
+ * 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/>.
+ */
+
+if (!defined('LACONICA')) { exit(1); }
+
+require_once INSTALLDIR.'/classes/Memcached_DataObject.php';
+
+/**
+ * Table Definition for file_oembed
+ */
+
+class File_oembed extends Memcached_DataObject
+{
+ ###START_AUTOCODE
+ /* the code below is auto generated do not remove the above tag */
+
+ public $__table = 'file_oembed'; // table name
+ public $id; // int(11) not_null primary_key group_by
+ public $file_id; // int(11) unique_key group_by
+ public $version; // varchar(20)
+ public $type; // varchar(20)
+ public $provider; // varchar(50)
+ public $provider_url; // varchar(255)
+ public $width; // int(11) group_by
+ public $height; // int(11) group_by
+ public $html; // blob(65535) blob
+ public $title; // varchar(255)
+ public $author_name; // varchar(50)
+ public $author_url; // varchar(255)
+ public $url; // varchar(255)
+
+ /* Static get */
+ function staticGet($k,$v=NULL) { return DB_DataObject::staticGet('File_oembed',$k,$v); }
+
+ /* the code above is auto generated do not remove the tag below */
+ ###END_AUTOCODE
+
+
+ function _getOembed($url, $maxwidth = 500, $maxheight = 400, $format = 'json') {
+ $cmd = 'http://oohembed.com/oohembed/?url=' . urlencode($url);
+ if (is_int($maxwidth)) $cmd .= "&maxwidth=$maxwidth";
+ if (is_int($maxheight)) $cmd .= "&maxheight=$maxheight";
+ if (is_string($format)) $cmd .= "&format=$format";
+ $oe = @file_get_contents($cmd);
+ if (false === $oe) return false;
+ return array($format => (('json' === $format) ? json_decode($oe, true) : $oe));
+ }
+
+ function saveNew($data, $file_id) {
+ $file_oembed = new File_oembed;
+ $file_oembed->file_id = $file_id;
+ $file_oembed->version = $data['version'];
+ $file_oembed->type = $data['type'];
+ if (!empty($data['provider_name'])) $file_oembed->provider = $data['provider_name'];
+ if (!isset($file_oembed->provider) && !empty($data['provide'])) $file_oembed->provider = $data['provider'];
+ if (!empty($data['provide_url'])) $file_oembed->provider_url = $data['provider_url'];
+ if (!empty($data['width'])) $file_oembed->width = intval($data['width']);
+ if (!empty($data['height'])) $file_oembed->height = intval($data['height']);
+ if (!empty($data['html'])) $file_oembed->html = $data['html'];
+ if (!empty($data['title'])) $file_oembed->title = $data['title'];
+ if (!empty($data['author_name'])) $file_oembed->author_name = $data['author_name'];
+ 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'])) {
+ File_thumbnail::saveNew($data, $file_id);
+ }
+ }
+}
+
+
diff --git a/classes/File_redirection.php b/classes/File_redirection.php
new file mode 100644
index 000000000..a71d1c083
--- /dev/null
+++ b/classes/File_redirection.php
@@ -0,0 +1,276 @@
+<?php
+/*
+ * Laconica - a distributed open-source microblogging tool
+ * Copyright (C) 2008, Controlez-Vous, Inc.
+ *
+ * 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/>.
+ */
+
+if (!defined('LACONICA')) { exit(1); }
+
+require_once INSTALLDIR.'/classes/Memcached_DataObject.php';
+require_once INSTALLDIR.'/classes/File.php';
+require_once INSTALLDIR.'/classes/File_oembed.php';
+
+define('USER_AGENT', 'Laconica user agent / file probe');
+
+
+/**
+ * Table Definition for file_redirection
+ */
+
+class File_redirection extends Memcached_DataObject
+{
+ ###START_AUTOCODE
+ /* the code below is auto generated do not remove the above tag */
+
+ public $__table = 'file_redirection'; // table name
+ public $id; // int(11) not_null primary_key group_by
+ public $url; // varchar(255) unique_key
+ public $file_id; // int(11) group_by
+ public $redirections; // int(11) group_by
+ public $httpcode; // int(11) group_by
+
+ /* Static get */
+ function staticGet($k,$v=NULL) { return DB_DataObject::staticGet('File_redirection',$k,$v); }
+
+ /* the code above is auto generated do not remove the tag below */
+ ###END_AUTOCODE
+
+
+
+ function _commonCurl($url, $redirs) {
+ $curlh = curl_init();
+ curl_setopt($curlh, CURLOPT_URL, $url);
+ curl_setopt($curlh, CURLOPT_AUTOREFERER, true); // # setup referer header when folowing redirects
+ curl_setopt($curlh, CURLOPT_CONNECTTIMEOUT, 10); // # seconds to wait
+ curl_setopt($curlh, CURLOPT_MAXREDIRS, $redirs); // # max number of http redirections to follow
+ curl_setopt($curlh, CURLOPT_USERAGENT, USER_AGENT);
+ curl_setopt($curlh, CURLOPT_FOLLOWLOCATION, true); // Follow redirects
+ curl_setopt($curlh, CURLOPT_RETURNTRANSFER, true);
+ curl_setopt($curlh, CURLOPT_FILETIME, true);
+ curl_setopt($curlh, CURLOPT_HEADER, true); // Include header in output
+ return $curlh;
+ }
+
+ function _redirectWhere_imp($short_url, $redirs = 10, $protected = false) {
+ if ($redirs < 0) return false;
+
+ // let's see if we know this...
+ $a = File::staticGet('url', $short_url);
+ if (empty($a->id)) {
+ $b = File_redirection::staticGet('url', $short_url);
+ if (empty($b->id)) {
+ // we'll have to figure it out
+ } else {
+ // this is a redirect to $b->file_id
+ $a = File::staticGet($b->file_id);
+ $url = $a->url;
+ }
+ } else {
+ // this is a direct link to $a->url
+ $url = $a->url;
+ }
+ if (isset($url)) {
+ return $url;
+ }
+
+
+
+ $curlh = File_redirection::_commonCurl($short_url, $redirs);
+ // Don't include body in output
+ curl_setopt($curlh, CURLOPT_NOBODY, true);
+ curl_exec($curlh);
+ $info = curl_getinfo($curlh);
+ curl_close($curlh);
+
+ if (405 == $info['http_code']) {
+ $curlh = File_redirection::_commonCurl($short_url, $redirs);
+ curl_exec($curlh);
+ $info = curl_getinfo($curlh);
+ curl_close($curlh);
+ }
+
+ if (!empty($info['redirect_count']) && File::isProtected($info['url'])) {
+ return File_redirection::_redirectWhere_imp($short_url, $info['redirect_count'] - 1, true);
+ }
+
+ $ret = array('code' => $info['http_code']
+ , 'redirects' => $info['redirect_count']
+ , 'url' => $info['url']);
+
+ if (!empty($info['content_type'])) $ret['type'] = $info['content_type'];
+ if ($protected) $ret['protected'] = true;
+ if (!empty($info['download_content_length'])) $ret['size'] = $info['download_content_length'];
+ if (isset($info['filetime']) && ($info['filetime'] > 0)) $ret['time'] = $info['filetime'];
+ return $ret;
+ }
+
+ function where($in_url) {
+ $ret = File_redirection::_redirectWhere_imp($in_url);
+ return $ret;
+ }
+
+ function makeShort($long_url) {
+ $long_url = File_redirection::_canonUrl($long_url);
+ // do we already know this long_url and have a short redirection for it?
+ $file = new File;
+ $file_redir = new File_redirection;
+ $file->url = $long_url;
+ $file->joinAdd($file_redir);
+ $file->selectAdd('length(file_redirection.url) as len');
+ $file->limit(1);
+ $file->orderBy('len');
+ $file->find(true);
+ if (!empty($file->id)) {
+ return $file->url;
+ }
+
+ // if yet unknown, we must find a short url according to user settings
+ $short_url = File_redirection::_userMakeShort($long_url, common_current_user());
+ return $short_url;
+ }
+
+ function _userMakeShort($long_url, $user) {
+ if (empty($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':
+ require_once INSTALLDIR.'/lib/Shorturl_api.php';
+ $short_url_service = new LilUrl;
+ $short_url = $short_url_service->shorten($long_url);
+ break;
+
+ case '2tu.us':
+ $short_url_service = new TightUrl;
+ require_once INSTALLDIR.'/lib/Shorturl_api.php';
+ $short_url = $short_url_service->shorten($long_url);
+ break;
+
+ case 'ptiturl.com':
+ require_once INSTALLDIR.'/lib/Shorturl_api.php';
+ $short_url_service = new PtitUrl;
+ $short_url = $short_url_service->shorten($long_url);
+ break;
+
+ case 'bit.ly':
+ curl_setopt($curlh, CURLOPT_URL, 'http://bit.ly/api?method=shorten&long_url='.urlencode($long_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($long_url));
+ $short_url = curl_exec($curlh);
+ break;
+ case 'snipr.com':
+ curl_setopt($curlh, CURLOPT_URL, 'http://snipr.com/site/snip?r=simple&link='.urlencode($long_url));
+ $short_url = curl_exec($curlh);
+ break;
+ case 'metamark.net':
+ curl_setopt($curlh, CURLOPT_URL, 'http://metamark.net/api/rest/simple?long_url='.urlencode($long_url));
+ $short_url = curl_exec($curlh);
+ break;
+ case 'tinyurl.com':
+ curl_setopt($curlh, CURLOPT_URL, 'http://tinyurl.com/api-create.php?url='.urlencode($long_url));
+ $short_url = curl_exec($curlh);
+ break;
+ default:
+ $short_url = false;
+ }
+
+ curl_close($curlh);
+
+ if ($short_url) {
+ $short_url = (string)$short_url;
+if(1) {
+ // store it
+ $file = File::staticGet('url', $long_url);
+ if (empty($file)) {
+ $redir_data = File_redirection::where($long_url);
+ $file = File::saveNew($redir_data, $long_url);
+ $file_id = $file->id;
+ if (!empty($redir_data['oembed']['json'])) {
+ File_oembed::saveNew($redir_data['oembed']['json'], $file_id);
+ }
+ } else {
+ $file_id = $file->id;
+ }
+ $file_redir = File_redirection::staticGet('url', $short_url);
+ if (empty($file_redir)) {
+ $file_redir = new File_redirection;
+ $file_redir->url = $short_url;
+ $file_redir->file_id = $file_id;
+ $file_redir->insert();
+ }
+}
+ return $short_url;
+ }
+ return $long_url;
+ }
+
+ function _canonUrl($in_url, $default_scheme = 'http://') {
+ if (empty($in_url)) return false;
+ $out_url = $in_url;
+ $p = parse_url($out_url);
+ if (empty($p['host']) || empty($p['scheme'])) {
+ list($scheme) = explode(':', $in_url, 2);
+ switch ($scheme) {
+ case 'fax':
+ case 'tel':
+ $out_url = str_replace('.-()', '', $out_url);
+ break;
+
+ case 'mailto':
+ case 'aim':
+ case 'jabber':
+ case 'xmpp':
+ // don't touch anything
+ break;
+
+ default:
+ $out_url = $default_scheme . ltrim($out_url, '/');
+ $p = parse_url($out_url);
+ if (empty($p['scheme'])) return false;
+ break;
+ }
+ }
+
+ if (('ftp' == $p['scheme']) || ('http' == $p['scheme']) || ('https' == $p['scheme'])) {
+ if (empty($p['host'])) return false;
+ if (empty($p['path'])) {
+ $out_url .= '/';
+ }
+ }
+
+ return $out_url;
+ }
+
+ function saveNew($data, $file_id, $url) {
+ $file_redir = new File_redirection;
+ $file_redir->url = $url;
+ $file_redir->file_id = $file_id;
+ $file_redir->redirections = intval($data['redirects']);
+ $file_redir->httpcode = intval($data['code']);
+ $file_redir->insert();
+ }
+}
+
diff --git a/classes/File_thumbnail.php b/classes/File_thumbnail.php
new file mode 100644
index 000000000..1a65b92c9
--- /dev/null
+++ b/classes/File_thumbnail.php
@@ -0,0 +1,55 @@
+<?php
+/*
+ * Laconica - a distributed open-source microblogging tool
+ * Copyright (C) 2008, Controlez-Vous, Inc.
+ *
+ * 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/>.
+ */
+
+if (!defined('LACONICA')) { exit(1); }
+
+require_once INSTALLDIR.'/classes/Memcached_DataObject.php';
+
+/**
+ * Table Definition for file_thumbnail
+ */
+
+class File_thumbnail extends Memcached_DataObject
+{
+ ###START_AUTOCODE
+ /* the code below is auto generated do not remove the above tag */
+
+ public $__table = 'file_thumbnail'; // table name
+ public $id; // int(11) not_null primary_key group_by
+ public $file_id; // int(11) unique_key group_by
+ public $url; // varchar(255) unique_key
+ public $width; // int(11) group_by
+ public $height; // int(11) group_by
+
+ /* Static get */
+ function staticGet($k,$v=NULL) { return DB_DataObject::staticGet('File_thumbnail',$k,$v); }
+
+ /* 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/File_to_post.php b/classes/File_to_post.php
new file mode 100644
index 000000000..00ddebe6b
--- /dev/null
+++ b/classes/File_to_post.php
@@ -0,0 +1,60 @@
+<?php
+/*
+ * Laconica - a distributed open-source microblogging tool
+ * Copyright (C) 2008, Controlez-Vous, Inc.
+ *
+ * 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/>.
+ */
+
+if (!defined('LACONICA')) { exit(1); }
+
+require_once INSTALLDIR.'/classes/Memcached_DataObject.php';
+
+/**
+ * Table Definition for file_to_post
+ */
+
+class File_to_post extends Memcached_DataObject
+{
+ ###START_AUTOCODE
+ /* the code below is auto generated do not remove the above tag */
+
+ public $__table = 'file_to_post'; // table name
+ public $id; // int(11) not_null primary_key group_by
+ public $file_id; // int(11) multiple_key group_by
+ public $post_id; // int(11) group_by
+
+ /* Static get */
+ function staticGet($k,$v=NULL) { return DB_DataObject::staticGet('File_to_post',$k,$v); }
+
+ /* the code above is auto generated do not remove the tag below */
+ ###END_AUTOCODE
+
+ function processNew($file_id, $notice_id) {
+ static $seen = array();
+ if (empty($seen[$notice_id]) || !in_array($file_id, $seen[$notice_id])) {
+ $f2p = new File_to_post;
+ $f2p->file_id = $file_id;
+ $f2p->post_id = $notice_id;
+ $f2p->insert();
+ if (empty($seen[$notice_id])) {
+ $seen[$notice_id] = array($file_id);
+ } else {
+ $seen[$notice_id][] = $file_id;
+ }
+ }
+
+ }
+}
+
diff --git a/classes/Notice.php b/classes/Notice.php
index 382d160ab..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.');
@@ -167,8 +165,8 @@ class Notice extends Memcached_DataObject
$notice->reply_to = $reply_to;
$notice->created = common_sql_now();
- $notice->content = $final;
- $notice->rendered = common_render_content($final, $notice);
+ $notice->content = $content;
+ $notice->rendered = common_render_content($content, $notice);
$notice->source = $source;
$notice->uri = $uri;
@@ -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/classes/laconica.ini b/classes/laconica.ini
index 5a905a4bb..316923af0 100755..100644
--- a/classes/laconica.ini
+++ b/classes/laconica.ini
@@ -1,4 +1,3 @@
-
[avatar]
profile_id = 129
original = 17
@@ -393,3 +392,63 @@ modified = 384
[user_openid__keys]
canonical = K
display = U
+
+[file]
+id = 129
+url = 2
+mimetype = 2
+size = 1
+title = 2
+date = 1
+protected = 1
+
+[file__keys]
+id = N
+
+[file_oembed]
+id = 129
+file_id = 129
+version = 2
+type = 2
+provider = 2
+provider_url = 2
+width = 1
+height = 1
+html = 34
+title = 2
+author_name = 2
+author_url = 2
+url = 2
+
+[file_oembed__keys]
+id = N
+
+[file_redirection]
+id = 129
+url = 2
+file_id = 129
+redirections = 1
+httpcode = 1
+
+[file_redirection__keys]
+id = N
+
+[file_thumbnail]
+id = 129
+file_id = 129
+url = 2
+width = 1
+height = 1
+
+[file_thumbnail__keys]
+id = N
+
+[file_to_post]
+id = 129
+file_id = 129
+post_id = 129
+
+[file_to_post__keys]
+id = N
+
+
diff --git a/classes/laconica.links.ini b/classes/laconica.links.ini
index 173b18726..95c63f3c0 100644
--- a/classes/laconica.links.ini
+++ b/classes/laconica.links.ini
@@ -41,3 +41,17 @@ subscribed = profile:id
[fave]
notice_id = notice:id
user_id = user:id
+
+[file_oembed]
+file_id = file:id
+
+[file_redirection]
+file_id = file:id
+
+[file_thumbnail]
+file_id = file:id
+
+[file_to_post]
+file_id = file:id
+post_id = notice:id
+
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/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)
diff --git a/theme/base/images/icons/clip-big.png b/theme/base/images/icons/clip-big.png
new file mode 100644
index 000000000..3945f56cc
--- /dev/null
+++ b/theme/base/images/icons/clip-big.png
Binary files differ
diff --git a/theme/base/images/icons/clip.png b/theme/base/images/icons/clip.png
new file mode 100644
index 000000000..3c5a17d18
--- /dev/null
+++ b/theme/base/images/icons/clip.png
Binary files differ