From 2c9b30a31b97a4d03d0f246c9ad938e44c1efc23 Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Thu, 10 Dec 2009 15:17:16 -0500 Subject: Revert "remove the Repeat plugin (for now)" This reverts commit c10fa99b04ef544e1ea082a694ad714f9246df86. --- plugins/Repeat/Forward.php | 127 ++++++++++++++++++++++++++++++++ plugins/Repeat/forward.php | 122 ++++++++++++++++++++++++++++++ plugins/Repeat/forwardform.php | 147 +++++++++++++++++++++++++++++++++++++ plugins/Repeat/inboxnoticelist.php | 85 +++++++++++++++++++++ 4 files changed, 481 insertions(+) create mode 100644 plugins/Repeat/Forward.php create mode 100644 plugins/Repeat/forward.php create mode 100644 plugins/Repeat/forwardform.php create mode 100644 plugins/Repeat/inboxnoticelist.php diff --git a/plugins/Repeat/Forward.php b/plugins/Repeat/Forward.php new file mode 100644 index 000000000..09b2d6a4e --- /dev/null +++ b/plugins/Repeat/Forward.php @@ -0,0 +1,127 @@ +. + */ + +if (!defined('STATUSNET')) { + exit(1); +} + +/** + * Table Definition for location_namespace + */ + +require_once INSTALLDIR.'/classes/Memcached_DataObject.php'; + +class Forward extends Memcached_DataObject +{ + ###START_AUTOCODE + /* the code below is auto generated do not remove the above tag */ + + public $__table = 'forward'; // table name + public $profile_id; // int(4) primary_key not_null + public $notice_id; // int(4) primary_key not_null + public $created; // datetime not_null default_0000-00-00%2000%3A00%3A00 + + /* Static get */ + function staticGet($k,$v=NULL) { return Memcached_DataObject::staticGet('Forward',$k,$v); } + + /* the code above is auto generated do not remove the tag below */ + ###END_AUTOCODE + + function &pkeyGet($kv) + { + return Memcached_DataObject::pkeyGet('Forward', $kv); + } + + static function saveNew($profile_id, $notice_id) + { + $forward = new Forward(); + + $forward->profile_id = $profile_id; + $forward->notice_id = $notice_id; + $forward->created = common_sql_now(); + + $forward->query('BEGIN'); + + if (!$forward->insert()) { + throw new ServerException(_("Couldn't insert forward.")); + } + + $ni = $forward->addToInboxes(); + + $forward->query('COMMIT'); + + $forward->blowCache($ni); + + return $forward; + } + + function addToInboxes() + { + $inbox = new Notice_inbox(); + + $user = new User(); + + $usertable = common_database_tablename('user'); + $user->query("SELECT $usertable.* FROM $usertable INNER JOIN subscription ON $usertable.id = subscription.subscriber ". + 'WHERE subscription.subscribed = '.$this->profile_id); + + $ni = array(); + + $notice = Notice::staticGet('id', $this->notice_id); + + $author = Profile::staticGet('id', $notice->profile_id); + + while ($user->fetch()) { + $inbox = Notice_inbox::pkeyGet(array('user_id' => $user->id, + 'notice_id' => $this->notice_id)); + + if (empty($inbox)) { + if (!$user->hasBlocked($author)) { + $ni[$user->id] = NOTICE_INBOX_SOURCE_FORWARD; + } + } else { + $inbox->free(); + } + } + + $user->free(); + $author->free(); + + unset($user); + unset($author); + + Notice_inbox::bulkInsert($this->notice_id, $this->created, $ni); + + return $ni; + } + + function blowCache($ni) + { + $cache = common_memcache(); + + if (!empty($cache)) { + foreach ($ni as $id => $source) { + $cache->delete(common_cache_key('notice_inbox:by_user:'.$id)); + $cache->delete(common_cache_key('notice_inbox:by_user_own:'.$id)); + $cache->delete(common_cache_key('notice_inbox:by_user:'.$id.';last')); + $cache->delete(common_cache_key('notice_inbox:by_user_own:'.$id.';last')); + } + } + } +} diff --git a/plugins/Repeat/forward.php b/plugins/Repeat/forward.php new file mode 100644 index 000000000..194833fe0 --- /dev/null +++ b/plugins/Repeat/forward.php @@ -0,0 +1,122 @@ + + * @license http://www.fsf.org/licensing/licenses/agpl.html AGPLv3 + * @link http://status.net/ + * + * StatusNet - the distributed open-source microblogging tool + * Copyright (C) 2008, 2009, StatusNet, 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 . + */ + +if (!defined('STATUSNET')) { + exit(1); +} + +/** + * Forward action + * + * @category Action + * @package StatusNet + * @author Evan Prodromou + * @license http://www.fsf.org/licensing/licenses/agpl.html AGPLv3 + * @link http://status.net/ + */ + +class ForwardAction extends Action +{ + var $user = null; + var $notice = null; + + function prepare($args) + { + parent::prepare($args); + + $this->user = common_current_user(); + + if (empty($this->user)) { + $this->clientError(_("Only logged-in users can forward notices.")); + return false; + } + + $id = $this->trimmed('notice'); + + if (empty($id)) { + $this->clientError(_("No notice specified.")); + return false; + } + + $this->notice = Notice::staticGet('id', $id); + + if (empty($this->notice)) { + $this->clientError(_("No notice specified.")); + return false; + } + + if ($this->user->id == $this->notice->profile_id) { + $this->clientError(_("You can't forward your own notice.")); + return false; + } + + $token = $this->trimmed('token-'.$id); + + if (empty($token) || $token != common_session_token()) { + $this->clientError(_("There was a problem with your session token. Try again, please.")); + return false; + } + + $profile = $this->user->getProfile(); + + if ($profile->hasForwarded($id)) { + $this->clientError(_("You already forwarded that notice.")); + return false; + } + + return true; + } + + /** + * Class handler. + * + * @param array $args query arguments + * + * @return void + */ + + function handle($args) + { + $forward = Forward::saveNew($this->user->id, $this->notice->id); + + if ($this->boolean('ajax')) { + $this->startHTML('text/xml;charset=utf-8'); + $this->elementStart('head'); + $this->element('title', null, _('Forwarded')); + $this->elementEnd('head'); + $this->elementStart('body'); + $this->element('p', array('id' => 'forward_response'), _('Forwarded!')); + $this->elementEnd('body'); + $this->elementEnd('html'); + } else { + // FIXME! + } + } +} diff --git a/plugins/Repeat/forwardform.php b/plugins/Repeat/forwardform.php new file mode 100644 index 000000000..2052856ae --- /dev/null +++ b/plugins/Repeat/forwardform.php @@ -0,0 +1,147 @@ +. + * + * @category Form + * @package StatusNet + * @author Evan Prodromou + * @copyright 2009 StatusNet, Inc. + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://status.net/ + */ + +if (!defined('STATUSNET') && !defined('LACONICA')) { + exit(1); +} + +require_once INSTALLDIR.'/lib/form.php'; + +/** + * Form for forwarding a notice + * + * @category Form + * @package StatusNet + * @author Evan Prodromou + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://status.net/ + */ + +class ForwardForm extends Form +{ + /** + * Notice to forward + */ + + var $notice = null; + + /** + * Constructor + * + * @param HTMLOutputter $out output channel + * @param Notice $notice notice to forward + */ + + function __construct($out=null, $notice=null) + { + parent::__construct($out); + + $this->notice = $notice; + } + + /** + * ID of the form + * + * @return int ID of the form + */ + + function id() + { + return 'forward-' . $this->notice->id; + } + + /** + * Action of the form + * + * @return string URL of the action + */ + + function action() + { + return common_local_url('forward'); + } + + /** + * Include a session token for CSRF protection + * + * @return void + */ + + function sessionToken() + { + $this->out->hidden('token-' . $this->notice->id, + common_session_token()); + } + + /** + * Legend of the Form + * + * @return void + */ + function formLegend() + { + $this->out->element('legend', null, _('Forward this notice')); + } + + /** + * Data elements + * + * @return void + */ + + function formData() + { + $this->out->hidden('notice-n'.$this->notice->id, + $this->notice->id, + 'notice'); + } + + /** + * Action elements + * + * @return void + */ + + function formActions() + { + $this->out->submit('forward-submit-' . $this->notice->id, + _('Forward'), 'submit', null, _('Forward this notice')); + } + + /** + * Class of the form. + * + * @return string the form's class + */ + + function formClass() + { + return 'form_forward'; + } +} diff --git a/plugins/Repeat/inboxnoticelist.php b/plugins/Repeat/inboxnoticelist.php new file mode 100644 index 000000000..809fbe8af --- /dev/null +++ b/plugins/Repeat/inboxnoticelist.php @@ -0,0 +1,85 @@ +owner = $owner; + } + + function newListItem($notice) + { + return new InboxNoticeListItem($notice, $this->owner, $this->out); + } +} + +class InboxNoticeListItem extends NoticeListItem +{ + var $owner = null; + var $ib = null; + + function __construct($notice, $owner, $out=null) + { + parent::__construct($notice, $out); + $this->owner = $owner; + + $this->ib = Notice_inbox::pkeyGet(array('user_id' => $owner->id, + 'notice_id' => $notice->id)); + } + + function showAuthor() + { + parent::showAuthor(); + if ($this->ib->source == NOTICE_INBOX_SOURCE_FORWARD) { + $this->out->element('span', 'forward', _('Fwd')); + } + } + + function showEnd() + { + if ($this->ib->source == NOTICE_INBOX_SOURCE_FORWARD) { + + $forward = new Forward(); + + // FIXME: scary join! + + $forward->query('SELECT profile_id '. + 'FROM forward JOIN subscription ON forward.profile_id = subscription.subscribed '. + 'WHERE subscription.subscriber = ' . $this->owner->id . ' '. + 'AND forward.notice_id = ' . $this->notice->id . ' '. + 'ORDER BY forward.created '); + + $n = 0; + + $firstForwarder = null; + + while ($forward->fetch()) { + if (empty($firstForwarder)) { + $firstForwarder = Profile::staticGet('id', $forward->profile_id); + } + $n++; + } + + $forward->free(); + unset($forward); + + $this->out->elementStart('span', 'forwards'); + + $link = XMLStringer::estring('a', array('href' => $firstForwarder->profileurl), + $firstForwarder->nickname); + + if ($n == 1) { + $this->out->raw(sprintf(_('Forwarded by %s'), $link)); + } else { + // XXX: use that cool ngettext thing + $this->out->raw(sprintf(_('Forwarded by %s and %d other(s)'), $link, $n - 1)); + } + + $this->out->elementEnd('span'); + } + parent::showEnd(); + } +} -- cgit v1.2.3-54-g00ecf From a5724cca4956e3fcfe359bd72b4d874832b31393 Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Fri, 11 Dec 2009 10:10:25 -0500 Subject: do some moving around of forwarding stuff --- actions/repeat.php | 122 ++++++++++++++++++++++++++++++ lib/repeatform.php | 147 +++++++++++++++++++++++++++++++++++++ plugins/Repeat/Forward.php | 127 -------------------------------- plugins/Repeat/forward.php | 122 ------------------------------ plugins/Repeat/forwardform.php | 147 ------------------------------------- plugins/Repeat/inboxnoticelist.php | 85 --------------------- 6 files changed, 269 insertions(+), 481 deletions(-) create mode 100644 actions/repeat.php create mode 100644 lib/repeatform.php delete mode 100644 plugins/Repeat/Forward.php delete mode 100644 plugins/Repeat/forward.php delete mode 100644 plugins/Repeat/forwardform.php delete mode 100644 plugins/Repeat/inboxnoticelist.php diff --git a/actions/repeat.php b/actions/repeat.php new file mode 100644 index 000000000..194833fe0 --- /dev/null +++ b/actions/repeat.php @@ -0,0 +1,122 @@ + + * @license http://www.fsf.org/licensing/licenses/agpl.html AGPLv3 + * @link http://status.net/ + * + * StatusNet - the distributed open-source microblogging tool + * Copyright (C) 2008, 2009, StatusNet, 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 . + */ + +if (!defined('STATUSNET')) { + exit(1); +} + +/** + * Forward action + * + * @category Action + * @package StatusNet + * @author Evan Prodromou + * @license http://www.fsf.org/licensing/licenses/agpl.html AGPLv3 + * @link http://status.net/ + */ + +class ForwardAction extends Action +{ + var $user = null; + var $notice = null; + + function prepare($args) + { + parent::prepare($args); + + $this->user = common_current_user(); + + if (empty($this->user)) { + $this->clientError(_("Only logged-in users can forward notices.")); + return false; + } + + $id = $this->trimmed('notice'); + + if (empty($id)) { + $this->clientError(_("No notice specified.")); + return false; + } + + $this->notice = Notice::staticGet('id', $id); + + if (empty($this->notice)) { + $this->clientError(_("No notice specified.")); + return false; + } + + if ($this->user->id == $this->notice->profile_id) { + $this->clientError(_("You can't forward your own notice.")); + return false; + } + + $token = $this->trimmed('token-'.$id); + + if (empty($token) || $token != common_session_token()) { + $this->clientError(_("There was a problem with your session token. Try again, please.")); + return false; + } + + $profile = $this->user->getProfile(); + + if ($profile->hasForwarded($id)) { + $this->clientError(_("You already forwarded that notice.")); + return false; + } + + return true; + } + + /** + * Class handler. + * + * @param array $args query arguments + * + * @return void + */ + + function handle($args) + { + $forward = Forward::saveNew($this->user->id, $this->notice->id); + + if ($this->boolean('ajax')) { + $this->startHTML('text/xml;charset=utf-8'); + $this->elementStart('head'); + $this->element('title', null, _('Forwarded')); + $this->elementEnd('head'); + $this->elementStart('body'); + $this->element('p', array('id' => 'forward_response'), _('Forwarded!')); + $this->elementEnd('body'); + $this->elementEnd('html'); + } else { + // FIXME! + } + } +} diff --git a/lib/repeatform.php b/lib/repeatform.php new file mode 100644 index 000000000..2052856ae --- /dev/null +++ b/lib/repeatform.php @@ -0,0 +1,147 @@ +. + * + * @category Form + * @package StatusNet + * @author Evan Prodromou + * @copyright 2009 StatusNet, Inc. + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://status.net/ + */ + +if (!defined('STATUSNET') && !defined('LACONICA')) { + exit(1); +} + +require_once INSTALLDIR.'/lib/form.php'; + +/** + * Form for forwarding a notice + * + * @category Form + * @package StatusNet + * @author Evan Prodromou + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://status.net/ + */ + +class ForwardForm extends Form +{ + /** + * Notice to forward + */ + + var $notice = null; + + /** + * Constructor + * + * @param HTMLOutputter $out output channel + * @param Notice $notice notice to forward + */ + + function __construct($out=null, $notice=null) + { + parent::__construct($out); + + $this->notice = $notice; + } + + /** + * ID of the form + * + * @return int ID of the form + */ + + function id() + { + return 'forward-' . $this->notice->id; + } + + /** + * Action of the form + * + * @return string URL of the action + */ + + function action() + { + return common_local_url('forward'); + } + + /** + * Include a session token for CSRF protection + * + * @return void + */ + + function sessionToken() + { + $this->out->hidden('token-' . $this->notice->id, + common_session_token()); + } + + /** + * Legend of the Form + * + * @return void + */ + function formLegend() + { + $this->out->element('legend', null, _('Forward this notice')); + } + + /** + * Data elements + * + * @return void + */ + + function formData() + { + $this->out->hidden('notice-n'.$this->notice->id, + $this->notice->id, + 'notice'); + } + + /** + * Action elements + * + * @return void + */ + + function formActions() + { + $this->out->submit('forward-submit-' . $this->notice->id, + _('Forward'), 'submit', null, _('Forward this notice')); + } + + /** + * Class of the form. + * + * @return string the form's class + */ + + function formClass() + { + return 'form_forward'; + } +} diff --git a/plugins/Repeat/Forward.php b/plugins/Repeat/Forward.php deleted file mode 100644 index 09b2d6a4e..000000000 --- a/plugins/Repeat/Forward.php +++ /dev/null @@ -1,127 +0,0 @@ -. - */ - -if (!defined('STATUSNET')) { - exit(1); -} - -/** - * Table Definition for location_namespace - */ - -require_once INSTALLDIR.'/classes/Memcached_DataObject.php'; - -class Forward extends Memcached_DataObject -{ - ###START_AUTOCODE - /* the code below is auto generated do not remove the above tag */ - - public $__table = 'forward'; // table name - public $profile_id; // int(4) primary_key not_null - public $notice_id; // int(4) primary_key not_null - public $created; // datetime not_null default_0000-00-00%2000%3A00%3A00 - - /* Static get */ - function staticGet($k,$v=NULL) { return Memcached_DataObject::staticGet('Forward',$k,$v); } - - /* the code above is auto generated do not remove the tag below */ - ###END_AUTOCODE - - function &pkeyGet($kv) - { - return Memcached_DataObject::pkeyGet('Forward', $kv); - } - - static function saveNew($profile_id, $notice_id) - { - $forward = new Forward(); - - $forward->profile_id = $profile_id; - $forward->notice_id = $notice_id; - $forward->created = common_sql_now(); - - $forward->query('BEGIN'); - - if (!$forward->insert()) { - throw new ServerException(_("Couldn't insert forward.")); - } - - $ni = $forward->addToInboxes(); - - $forward->query('COMMIT'); - - $forward->blowCache($ni); - - return $forward; - } - - function addToInboxes() - { - $inbox = new Notice_inbox(); - - $user = new User(); - - $usertable = common_database_tablename('user'); - $user->query("SELECT $usertable.* FROM $usertable INNER JOIN subscription ON $usertable.id = subscription.subscriber ". - 'WHERE subscription.subscribed = '.$this->profile_id); - - $ni = array(); - - $notice = Notice::staticGet('id', $this->notice_id); - - $author = Profile::staticGet('id', $notice->profile_id); - - while ($user->fetch()) { - $inbox = Notice_inbox::pkeyGet(array('user_id' => $user->id, - 'notice_id' => $this->notice_id)); - - if (empty($inbox)) { - if (!$user->hasBlocked($author)) { - $ni[$user->id] = NOTICE_INBOX_SOURCE_FORWARD; - } - } else { - $inbox->free(); - } - } - - $user->free(); - $author->free(); - - unset($user); - unset($author); - - Notice_inbox::bulkInsert($this->notice_id, $this->created, $ni); - - return $ni; - } - - function blowCache($ni) - { - $cache = common_memcache(); - - if (!empty($cache)) { - foreach ($ni as $id => $source) { - $cache->delete(common_cache_key('notice_inbox:by_user:'.$id)); - $cache->delete(common_cache_key('notice_inbox:by_user_own:'.$id)); - $cache->delete(common_cache_key('notice_inbox:by_user:'.$id.';last')); - $cache->delete(common_cache_key('notice_inbox:by_user_own:'.$id.';last')); - } - } - } -} diff --git a/plugins/Repeat/forward.php b/plugins/Repeat/forward.php deleted file mode 100644 index 194833fe0..000000000 --- a/plugins/Repeat/forward.php +++ /dev/null @@ -1,122 +0,0 @@ - - * @license http://www.fsf.org/licensing/licenses/agpl.html AGPLv3 - * @link http://status.net/ - * - * StatusNet - the distributed open-source microblogging tool - * Copyright (C) 2008, 2009, StatusNet, 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 . - */ - -if (!defined('STATUSNET')) { - exit(1); -} - -/** - * Forward action - * - * @category Action - * @package StatusNet - * @author Evan Prodromou - * @license http://www.fsf.org/licensing/licenses/agpl.html AGPLv3 - * @link http://status.net/ - */ - -class ForwardAction extends Action -{ - var $user = null; - var $notice = null; - - function prepare($args) - { - parent::prepare($args); - - $this->user = common_current_user(); - - if (empty($this->user)) { - $this->clientError(_("Only logged-in users can forward notices.")); - return false; - } - - $id = $this->trimmed('notice'); - - if (empty($id)) { - $this->clientError(_("No notice specified.")); - return false; - } - - $this->notice = Notice::staticGet('id', $id); - - if (empty($this->notice)) { - $this->clientError(_("No notice specified.")); - return false; - } - - if ($this->user->id == $this->notice->profile_id) { - $this->clientError(_("You can't forward your own notice.")); - return false; - } - - $token = $this->trimmed('token-'.$id); - - if (empty($token) || $token != common_session_token()) { - $this->clientError(_("There was a problem with your session token. Try again, please.")); - return false; - } - - $profile = $this->user->getProfile(); - - if ($profile->hasForwarded($id)) { - $this->clientError(_("You already forwarded that notice.")); - return false; - } - - return true; - } - - /** - * Class handler. - * - * @param array $args query arguments - * - * @return void - */ - - function handle($args) - { - $forward = Forward::saveNew($this->user->id, $this->notice->id); - - if ($this->boolean('ajax')) { - $this->startHTML('text/xml;charset=utf-8'); - $this->elementStart('head'); - $this->element('title', null, _('Forwarded')); - $this->elementEnd('head'); - $this->elementStart('body'); - $this->element('p', array('id' => 'forward_response'), _('Forwarded!')); - $this->elementEnd('body'); - $this->elementEnd('html'); - } else { - // FIXME! - } - } -} diff --git a/plugins/Repeat/forwardform.php b/plugins/Repeat/forwardform.php deleted file mode 100644 index 2052856ae..000000000 --- a/plugins/Repeat/forwardform.php +++ /dev/null @@ -1,147 +0,0 @@ -. - * - * @category Form - * @package StatusNet - * @author Evan Prodromou - * @copyright 2009 StatusNet, Inc. - * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 - * @link http://status.net/ - */ - -if (!defined('STATUSNET') && !defined('LACONICA')) { - exit(1); -} - -require_once INSTALLDIR.'/lib/form.php'; - -/** - * Form for forwarding a notice - * - * @category Form - * @package StatusNet - * @author Evan Prodromou - * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 - * @link http://status.net/ - */ - -class ForwardForm extends Form -{ - /** - * Notice to forward - */ - - var $notice = null; - - /** - * Constructor - * - * @param HTMLOutputter $out output channel - * @param Notice $notice notice to forward - */ - - function __construct($out=null, $notice=null) - { - parent::__construct($out); - - $this->notice = $notice; - } - - /** - * ID of the form - * - * @return int ID of the form - */ - - function id() - { - return 'forward-' . $this->notice->id; - } - - /** - * Action of the form - * - * @return string URL of the action - */ - - function action() - { - return common_local_url('forward'); - } - - /** - * Include a session token for CSRF protection - * - * @return void - */ - - function sessionToken() - { - $this->out->hidden('token-' . $this->notice->id, - common_session_token()); - } - - /** - * Legend of the Form - * - * @return void - */ - function formLegend() - { - $this->out->element('legend', null, _('Forward this notice')); - } - - /** - * Data elements - * - * @return void - */ - - function formData() - { - $this->out->hidden('notice-n'.$this->notice->id, - $this->notice->id, - 'notice'); - } - - /** - * Action elements - * - * @return void - */ - - function formActions() - { - $this->out->submit('forward-submit-' . $this->notice->id, - _('Forward'), 'submit', null, _('Forward this notice')); - } - - /** - * Class of the form. - * - * @return string the form's class - */ - - function formClass() - { - return 'form_forward'; - } -} diff --git a/plugins/Repeat/inboxnoticelist.php b/plugins/Repeat/inboxnoticelist.php deleted file mode 100644 index 809fbe8af..000000000 --- a/plugins/Repeat/inboxnoticelist.php +++ /dev/null @@ -1,85 +0,0 @@ -owner = $owner; - } - - function newListItem($notice) - { - return new InboxNoticeListItem($notice, $this->owner, $this->out); - } -} - -class InboxNoticeListItem extends NoticeListItem -{ - var $owner = null; - var $ib = null; - - function __construct($notice, $owner, $out=null) - { - parent::__construct($notice, $out); - $this->owner = $owner; - - $this->ib = Notice_inbox::pkeyGet(array('user_id' => $owner->id, - 'notice_id' => $notice->id)); - } - - function showAuthor() - { - parent::showAuthor(); - if ($this->ib->source == NOTICE_INBOX_SOURCE_FORWARD) { - $this->out->element('span', 'forward', _('Fwd')); - } - } - - function showEnd() - { - if ($this->ib->source == NOTICE_INBOX_SOURCE_FORWARD) { - - $forward = new Forward(); - - // FIXME: scary join! - - $forward->query('SELECT profile_id '. - 'FROM forward JOIN subscription ON forward.profile_id = subscription.subscribed '. - 'WHERE subscription.subscriber = ' . $this->owner->id . ' '. - 'AND forward.notice_id = ' . $this->notice->id . ' '. - 'ORDER BY forward.created '); - - $n = 0; - - $firstForwarder = null; - - while ($forward->fetch()) { - if (empty($firstForwarder)) { - $firstForwarder = Profile::staticGet('id', $forward->profile_id); - } - $n++; - } - - $forward->free(); - unset($forward); - - $this->out->elementStart('span', 'forwards'); - - $link = XMLStringer::estring('a', array('href' => $firstForwarder->profileurl), - $firstForwarder->nickname); - - if ($n == 1) { - $this->out->raw(sprintf(_('Forwarded by %s'), $link)); - } else { - // XXX: use that cool ngettext thing - $this->out->raw(sprintf(_('Forwarded by %s and %d other(s)'), $link, $n - 1)); - } - - $this->out->elementEnd('span'); - } - parent::showEnd(); - } -} -- cgit v1.2.3-54-g00ecf From f5bfa11db2df2362ec5f81699b26942d52e9b60b Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Fri, 11 Dec 2009 10:10:53 -0500 Subject: Revert "remove NoticeForward from util.js" This reverts commit 038fbddd02d2f144936357ed2ed40c09b1441c6b. --- js/util.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/js/util.js b/js/util.js index 336cd8cfe..71514324b 100644 --- a/js/util.js +++ b/js/util.js @@ -315,6 +315,10 @@ var SN = { // StatusNet $('.form_disfavor').each(function() { SN.U.FormXHR($(this)); }); }, + NoticeForward: function() { + $('.form_forward').each(function() { SN.U.FormXHR($(this)); }); + }, + NoticeAttachments: function() { $('.notice a.attachment').each(function() { SN.U.NoticeWithAttachment($(this).closest('.notice')); @@ -448,6 +452,7 @@ var SN = { // StatusNet Notices: function() { if ($('body.user_in').length > 0) { SN.U.NoticeFavor(); + SN.U.NoticeForward(); SN.U.NoticeReply(); } -- cgit v1.2.3-54-g00ecf From e75444b30d9c38f40f7a15422b46b5caae6bb16a Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Fri, 11 Dec 2009 10:11:06 -0500 Subject: Revert "remove form_forward from base css" This reverts commit a2fb1f5f9dda42b8bac689d3b5bfa86d7660416d. --- theme/base/css/display.css | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/theme/base/css/display.css b/theme/base/css/display.css index e88e1f222..5abbe159d 100644 --- a/theme/base/css/display.css +++ b/theme/base/css/display.css @@ -994,11 +994,13 @@ float:left; } .notice-options .notice_delete, .notice-options .notice_reply, +.notice-options .form_forward, .notice-options .form_favor, .notice-options .form_disfavor { float:left; margin-left:20%; } +.notice-options .form_forward, .notice-options .form_favor, .notice-options .form_disfavor { margin-left:0; @@ -1024,10 +1026,12 @@ border-radius:0; -moz-border-radius:0; -webkit-border-radius:0; } +.notice-options .form_forward legend, .notice-options .form_favor legend, .notice-options .form_disfavor legend { display:none; } +.notice-options .form_forward fieldset, .notice-options .form_favor fieldset, .notice-options .form_disfavor fieldset { border:0; -- cgit v1.2.3-54-g00ecf From 1204178660d34088f49bdaeb21189651c48c591e Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Fri, 11 Dec 2009 10:12:34 -0500 Subject: rename forward form to repeat form in JS --- js/util.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/js/util.js b/js/util.js index 71514324b..f60b5d313 100644 --- a/js/util.js +++ b/js/util.js @@ -315,8 +315,8 @@ var SN = { // StatusNet $('.form_disfavor').each(function() { SN.U.FormXHR($(this)); }); }, - NoticeForward: function() { - $('.form_forward').each(function() { SN.U.FormXHR($(this)); }); + NoticeRepeat: function() { + $('.form_repeat').each(function() { SN.U.FormXHR($(this)); }); }, NoticeAttachments: function() { @@ -452,7 +452,7 @@ var SN = { // StatusNet Notices: function() { if ($('body.user_in').length > 0) { SN.U.NoticeFavor(); - SN.U.NoticeForward(); + SN.U.NoticeRepeat(); SN.U.NoticeReply(); } -- cgit v1.2.3-54-g00ecf From 63642dccf1d2ad6a42eafed5d4ede86635b554a2 Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Fri, 11 Dec 2009 10:13:33 -0500 Subject: rename form_forward to form_repeat in CSS --- theme/base/css/display.css | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/theme/base/css/display.css b/theme/base/css/display.css index 5abbe159d..7446a42cd 100644 --- a/theme/base/css/display.css +++ b/theme/base/css/display.css @@ -994,13 +994,13 @@ float:left; } .notice-options .notice_delete, .notice-options .notice_reply, -.notice-options .form_forward, +.notice-options .form_repeat, .notice-options .form_favor, .notice-options .form_disfavor { float:left; margin-left:20%; } -.notice-options .form_forward, +.notice-options .form_repeat, .notice-options .form_favor, .notice-options .form_disfavor { margin-left:0; @@ -1026,12 +1026,12 @@ border-radius:0; -moz-border-radius:0; -webkit-border-radius:0; } -.notice-options .form_forward legend, +.notice-options .form_repeat legend, .notice-options .form_favor legend, .notice-options .form_disfavor legend { display:none; } -.notice-options .form_forward fieldset, +.notice-options .form_repeat fieldset, .notice-options .form_favor fieldset, .notice-options .form_disfavor fieldset { border:0; -- cgit v1.2.3-54-g00ecf From 3d06431787430f7102087ee4a8a66319fcb50350 Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Fri, 11 Dec 2009 10:20:32 -0500 Subject: add repeat_of column to notice table --- db/08to09.sql | 4 +++- db/08to09_pg.sql | 1 + db/statusnet.sql | 2 ++ db/statusnet_pg.sql | 4 +++- 4 files changed, 9 insertions(+), 2 deletions(-) diff --git a/db/08to09.sql b/db/08to09.sql index 64640f4ce..28ec3ec16 100644 --- a/db/08to09.sql +++ b/db/08to09.sql @@ -4,8 +4,10 @@ alter table notice add column lon decimal(10,7) comment 'longitude', add column location_id integer comment 'location id if possible', add column location_ns integer comment 'namespace for location', + add column repeat_of integer comment 'notice this is a repeat of' references notice (id), drop index notice_profile_id_idx, - add index notice_profile_id_idx (profile_id,created,id); + add index notice_profile_id_idx (profile_id,created,id), + add index notice_repeatof_idx (repeat_of); alter table message modify column content text comment 'message content'; diff --git a/db/08to09_pg.sql b/db/08to09_pg.sql index 1df8c249b..0398952f6 100644 --- a/db/08to09_pg.sql +++ b/db/08to09_pg.sql @@ -74,6 +74,7 @@ ALTER TABLE notice ADD COLUMN lat decimal(10, 7) /* comment 'latitude'*/; ALTER TABLE notice ADD COLUMN lon decimal(10,7) /* comment 'longitude'*/; ALTER TABLE notice ADD COLUMN location_id integer /* comment 'location id if possible'*/ ; ALTER TABLE notice ADD COLUMN location_ns integer /* comment 'namespace for location'*/; +ALTER TABLE notice ADD COLUMN repeat_of integer / * comment 'notice this is a repeat of' */ references notice (id); ALTER TABLE profile ADD COLUMN lat decimal(10,7) /*comment 'latitude'*/ ; ALTER TABLE profile ADD COLUMN lon decimal(10,7) /*comment 'longitude'*/; diff --git a/db/statusnet.sql b/db/statusnet.sql index 18abcdfdb..6b3c2ca06 100644 --- a/db/statusnet.sql +++ b/db/statusnet.sql @@ -129,11 +129,13 @@ create table notice ( lon decimal(10,7) comment 'longitude', location_id integer comment 'location id if possible', location_ns integer comment 'namespace for location', + repeat_of integer comment 'notice this is a repeat of' references notice (id), index notice_profile_id_idx (profile_id,created,id), index notice_conversation_idx (conversation), index notice_created_idx (created), index notice_replyto_idx (reply_to), + index notice_repeatof_idx (repeat_of), FULLTEXT(content) ) ENGINE=MyISAM CHARACTER SET utf8 COLLATE utf8_general_ci; diff --git a/db/statusnet_pg.sql b/db/statusnet_pg.sql index c37fa81de..020bfd967 100644 --- a/db/statusnet_pg.sql +++ b/db/statusnet_pg.sql @@ -135,7 +135,9 @@ create table notice ( lat decimal(10,7) /* comment 'latitude'*/ , lon decimal(10,7) /* comment 'longitude'*/ , location_id integer /* comment 'location id if possible'*/ , - location_ns integer /* comment 'namespace for location'*/ + location_ns integer /* comment 'namespace for location'*/ , + repeat_of integer /* comment 'notice this is a repeat of' */ references notice (id) , + /* FULLTEXT(content) */ ); -- cgit v1.2.3-54-g00ecf From da3089098817234627e48619f3a8b217970f8627 Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Fri, 11 Dec 2009 10:22:56 -0500 Subject: add repeat_of column to notice class --- classes/Notice.php | 14 ++++++++------ classes/statusnet.ini | 2 ++ 2 files changed, 10 insertions(+), 6 deletions(-) mode change 100644 => 100755 classes/statusnet.ini diff --git a/classes/Notice.php b/classes/Notice.php index c36c5a9c6..0a2f16271 100644 --- a/classes/Notice.php +++ b/classes/Notice.php @@ -55,13 +55,13 @@ class Notice extends Memcached_DataObject public $__table = 'notice'; // table name public $id; // int(4) primary_key not_null - public $profile_id; // int(4) not_null + public $profile_id; // int(4) multiple_key not_null public $uri; // varchar(255) unique_key - public $content; // text() - public $rendered; // text() + public $content; // text + public $rendered; // text public $url; // varchar(255) - public $created; // datetime() not_null - public $modified; // timestamp() not_null default_CURRENT_TIMESTAMP + public $created; // datetime multiple_key not_null default_0000-00-00%2000%3A00%3A00 + public $modified; // timestamp not_null default_CURRENT_TIMESTAMP public $reply_to; // int(4) public $is_local; // tinyint(1) public $source; // varchar(32) @@ -70,9 +70,11 @@ class Notice extends Memcached_DataObject public $lon; // decimal(10,7) public $location_id; // int(4) public $location_ns; // int(4) + public $repeat_of; // int(4) /* Static get */ - function staticGet($k,$v=NULL) { + function staticGet($k,$v=NULL) + { return Memcached_DataObject::staticGet('Notice',$k,$v); } diff --git a/classes/statusnet.ini b/classes/statusnet.ini old mode 100644 new mode 100755 index 835faeb0b..ff4ef2c14 --- a/classes/statusnet.ini +++ b/classes/statusnet.ini @@ -1,3 +1,4 @@ + [avatar] profile_id = 129 original = 17 @@ -306,6 +307,7 @@ lat = 1 lon = 1 location_id = 1 location_ns = 1 +repeat_of = 1 [notice__keys] id = N -- cgit v1.2.3-54-g00ecf From c9649f932153e6636214841858fd265b1476c05b Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Fri, 11 Dec 2009 10:23:36 -0500 Subject: reset executable bit on Notice.php and statusnet.ini --- classes/statusnet.ini | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100755 => 100644 classes/statusnet.ini diff --git a/classes/statusnet.ini b/classes/statusnet.ini old mode 100755 new mode 100644 -- cgit v1.2.3-54-g00ecf From 81843f2acd5375a9072d091fd58c6a6af079295e Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Fri, 11 Dec 2009 10:49:26 -0500 Subject: show the repeat form in notice lists --- classes/Profile.php | 11 +++++++++++ lib/noticelist.php | 21 +++++++++++++++++++++ lib/repeatform.php | 26 ++++++++++++-------------- 3 files changed, 44 insertions(+), 14 deletions(-) diff --git a/classes/Profile.php b/classes/Profile.php index 4b2e09006..03196447b 100644 --- a/classes/Profile.php +++ b/classes/Profile.php @@ -716,4 +716,15 @@ class Profile extends Memcached_DataObject } return $result; } + + function hasRepeated($notice_id) + { + // XXX: not really a pkey, but should work + + $notice = Memcached_DataObject::pkeyGet('Notice', + array('profile_id' => $this->id, + 'repeat_of' => $notice_id)); + + return !empty($notice); + } } diff --git a/lib/noticelist.php b/lib/noticelist.php index 21cec528f..924056ece 100644 --- a/lib/noticelist.php +++ b/lib/noticelist.php @@ -212,6 +212,7 @@ class NoticeListItem extends Widget $this->out->elementStart('div', 'notice-options'); $this->showFaveForm(); $this->showReplyLink(); + $this->showRepeatForm(); $this->showDeleteLink(); $this->out->elementEnd('div'); } @@ -551,6 +552,26 @@ class NoticeListItem extends Widget } } + /** + * show the form to repeat a notice + * + * @return void + */ + + function showRepeatForm() + { + $user = common_current_user(); + if ($user && $user->id != $this->notice->profile_id) { + $profile = $user->getProfile(); + if ($profile->hasRepeated($this->notice->id)) { + $this->out->text(_('Repeated')); + } else { + $rf = new RepeatForm($this->out, $this->notice); + $rf->show(); + } + } + } + /** * finish the notice * diff --git a/lib/repeatform.php b/lib/repeatform.php index 2052856ae..50e5d6dbe 100644 --- a/lib/repeatform.php +++ b/lib/repeatform.php @@ -2,7 +2,7 @@ /** * StatusNet, the distributed open-source microblogging tool * - * Form for forwarding a notice + * Form for repeating a notice * * PHP version 5 * @@ -27,14 +27,12 @@ * @link http://status.net/ */ -if (!defined('STATUSNET') && !defined('LACONICA')) { +if (!defined('STATUSNET')) { exit(1); } -require_once INSTALLDIR.'/lib/form.php'; - /** - * Form for forwarding a notice + * Form for repeating a notice * * @category Form * @package StatusNet @@ -43,10 +41,10 @@ require_once INSTALLDIR.'/lib/form.php'; * @link http://status.net/ */ -class ForwardForm extends Form +class RepeatForm extends Form { /** - * Notice to forward + * Notice to repeat */ var $notice = null; @@ -55,7 +53,7 @@ class ForwardForm extends Form * Constructor * * @param HTMLOutputter $out output channel - * @param Notice $notice notice to forward + * @param Notice $notice notice to repeat */ function __construct($out=null, $notice=null) @@ -73,7 +71,7 @@ class ForwardForm extends Form function id() { - return 'forward-' . $this->notice->id; + return 'repeat-' . $this->notice->id; } /** @@ -84,7 +82,7 @@ class ForwardForm extends Form function action() { - return common_local_url('forward'); + return common_local_url('repeat'); } /** @@ -106,7 +104,7 @@ class ForwardForm extends Form */ function formLegend() { - $this->out->element('legend', null, _('Forward this notice')); + $this->out->element('legend', null, _('Repeat this notice')); } /** @@ -130,8 +128,8 @@ class ForwardForm extends Form function formActions() { - $this->out->submit('forward-submit-' . $this->notice->id, - _('Forward'), 'submit', null, _('Forward this notice')); + $this->out->submit('repeat-submit-' . $this->notice->id, + _('Repeat'), 'submit', null, _('Repeat this notice')); } /** @@ -142,6 +140,6 @@ class ForwardForm extends Form function formClass() { - return 'form_forward'; + return 'form_repeat'; } } -- cgit v1.2.3-54-g00ecf From afc86a86d36dc8101c81d5d009546e4629db34a3 Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Fri, 11 Dec 2009 11:51:09 -0500 Subject: save repeats from the form --- actions/repeat.php | 20 ++++++++++---------- classes/Notice.php | 23 ++++++++++++++++++++++- lib/router.php | 1 + 3 files changed, 33 insertions(+), 11 deletions(-) diff --git a/actions/repeat.php b/actions/repeat.php index 194833fe0..a1c5f443f 100644 --- a/actions/repeat.php +++ b/actions/repeat.php @@ -1,7 +1,7 @@ user = common_current_user(); if (empty($this->user)) { - $this->clientError(_("Only logged-in users can forward notices.")); + $this->clientError(_("Only logged-in users can repeat notices.")); return false; } @@ -73,7 +73,7 @@ class ForwardAction extends Action } if ($this->user->id == $this->notice->profile_id) { - $this->clientError(_("You can't forward your own notice.")); + $this->clientError(_("You can't repeat your own notice.")); return false; } @@ -86,8 +86,8 @@ class ForwardAction extends Action $profile = $this->user->getProfile(); - if ($profile->hasForwarded($id)) { - $this->clientError(_("You already forwarded that notice.")); + if ($profile->hasRepeated($id)) { + $this->clientError(_("You already repeated that notice.")); return false; } @@ -104,15 +104,15 @@ class ForwardAction extends Action function handle($args) { - $forward = Forward::saveNew($this->user->id, $this->notice->id); + $repeat = $this->notice->repeat($this->user->id, 'web'); if ($this->boolean('ajax')) { $this->startHTML('text/xml;charset=utf-8'); $this->elementStart('head'); - $this->element('title', null, _('Forwarded')); + $this->element('title', null, _('Repeated')); $this->elementEnd('head'); $this->elementStart('body'); - $this->element('p', array('id' => 'forward_response'), _('Forwarded!')); + $this->element('p', array('id' => 'repeat_response'), _('Repeated!')); $this->elementEnd('body'); $this->elementEnd('html'); } else { diff --git a/classes/Notice.php b/classes/Notice.php index 228201188..82753fbdd 100644 --- a/classes/Notice.php +++ b/classes/Notice.php @@ -236,7 +236,14 @@ class Notice extends Memcached_DataObject $notice->source = $source; $notice->uri = $uri; - $notice->reply_to = self::getReplyTo($reply_to, $profile_id, $source, $final); + // Handle repeat case + + if (isset($repeat_of)) { + $notice->repeat_of = $repeat_of; + $notice->reply_to = $repeat_of; + } else { + $notice->reply_to = self::getReplyTo($reply_to, $profile_id, $source, $final); + } if (!empty($notice->reply_to)) { $reply = Notice::staticGet('id', $notice->reply_to); @@ -1434,4 +1441,18 @@ class Notice extends Memcached_DataObject return $location; } + + function repeat($repeater_id, $source) + { + $author = Profile::staticGet('id', $this->profile_id); + + // FIXME: truncate on long repeats...? + + $content = sprintf(_('RT @%1$s %2$s'), + $author->nickname, + $this->content); + + return self::saveNew($repeater_id, $content, $source, + array('repeat_of' => $this->id)); + } } diff --git a/lib/router.php b/lib/router.php index 37525319f..142206c16 100644 --- a/lib/router.php +++ b/lib/router.php @@ -99,6 +99,7 @@ class Router 'groupblock', 'groupunblock', 'sandbox', 'unsandbox', 'silence', 'unsilence', + 'repeat', 'deleteuser'); foreach ($main as $a) { -- cgit v1.2.3-54-g00ecf From 962f391f3ebd4908ae7187673426f03e57083b02 Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Fri, 11 Dec 2009 12:10:58 -0500 Subject: show original notice in repeat, with repeat info below --- lib/noticelist.php | 60 ++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 58 insertions(+), 2 deletions(-) diff --git a/lib/noticelist.php b/lib/noticelist.php index 924056ece..7319a62ea 100644 --- a/lib/noticelist.php +++ b/lib/noticelist.php @@ -147,6 +147,10 @@ class NoticeListItem extends Widget var $notice = null; + /** The notice that was repeated. */ + + var $repeat = null; + /** The profile of the author of the notice, extracted once for convenience. */ var $profile = null; @@ -162,8 +166,13 @@ class NoticeListItem extends Widget function __construct($notice, $out=null) { parent::__construct($out); - $this->notice = $notice; - $this->profile = $notice->getProfile(); + if (!empty($notice->repeat_of)) { + $this->notice = Notice::staticGet('id', $notice->repeat_of); + $this->repeat = $notice; + } else { + $this->notice = $notice; + } + $this->profile = $this->notice->getProfile(); } /** @@ -202,6 +211,7 @@ class NoticeListItem extends Widget $this->showNoticeSource(); $this->showNoticeLocation(); $this->showContext(); + $this->showRepeat(); $this->out->elementEnd('div'); } @@ -508,6 +518,52 @@ class NoticeListItem extends Widget } } + /** + * show a link to the author of repeat + * + * @return void + */ + + function showRepeat() + { + if (!empty($this->repeat)) { + + $repeater = Profile::staticGet('id', $this->repeat->profile_id); + + $attrs = array('href' => $repeater->profileurl, + 'class' => 'url'); + + if (!empty($repeater->fullname)) { + $attrs['title'] = $repeater->fullname . ' (' . $repeater->nickname . ')'; + } + + $this->out->elementStart('span', 'repeat'); + + $this->out->elementStart('a', $attrs); + + $avatar = $repeater->getAvatar(AVATAR_MINI_SIZE); + + $this->out->element('img', array('src' => ($avatar) ? + $avatar->displayUrl() : + Avatar::defaultImage(AVATAR_MINI_SIZE), + 'class' => 'avatar photo', + 'width' => AVATAR_MINI_SIZE, + 'height' => AVATAR_MINI_SIZE, + 'alt' => + ($repeater->fullname) ? + $repeater->fullname : + $repeater->nickname)); + + $this->out->elementEnd('a'); + + $text_link = XMLStringer::estring('a', $attrs, $repeater->nickname); + + $this->out->raw(sprintf(_('Repeated by %s'), $text_link)); + + $this->out->elementEnd('span'); + } + } + /** * show a link to reply to the current notice * -- cgit v1.2.3-54-g00ecf From 0cb1feed51522abc35bfd4252089ca8c4df52bda Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Fri, 11 Dec 2009 12:16:11 -0500 Subject: show repeated notice's author on profile page --- actions/showstream.php | 46 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/actions/showstream.php b/actions/showstream.php index 663638c18..74b46cc95 100644 --- a/actions/showstream.php +++ b/actions/showstream.php @@ -269,4 +269,50 @@ class ProfileNoticeListItem extends NoticeListItem { return; } + + /** + * show a link to the author of repeat + * + * @return void + */ + + function showRepeat() + { + if (!empty($this->repeat)) { + + // FIXME: this code is almost identical to default; need to refactor + + $attrs = array('href' => $this->profile->profileurl, + 'class' => 'url'); + + if (!empty($this->profile->fullname)) { + $attrs['title'] = $this->profile->fullname . ' (' . $this->profile->nickname . ')'; + } + + $this->out->elementStart('span', 'repeat'); + + $this->out->elementStart('a', $attrs); + + $avatar = $this->profile->getAvatar(AVATAR_MINI_SIZE); + + $this->out->element('img', array('src' => ($avatar) ? + $avatar->displayUrl() : + Avatar::defaultImage(AVATAR_MINI_SIZE), + 'class' => 'avatar photo', + 'width' => AVATAR_MINI_SIZE, + 'height' => AVATAR_MINI_SIZE, + 'alt' => + ($this->profile->fullname) ? + $this->profile->fullname : + $this->profile->nickname)); + + $this->out->elementEnd('a'); + + $text_link = XMLStringer::estring('a', $attrs, $this->profile->nickname); + + $this->out->raw(sprintf(_('Repeat of %s'), $text_link)); + + $this->out->elementEnd('span'); + } + } } -- cgit v1.2.3-54-g00ecf From 1c370bb277d9b707a5f1956dd5e6856a9b278f5e Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Fri, 11 Dec 2009 12:39:29 -0500 Subject: show repeated notices correctly in API output --- lib/api.php | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/lib/api.php b/lib/api.php index eacb80dbe..12e3ba531 100644 --- a/lib/api.php +++ b/lib/api.php @@ -213,6 +213,20 @@ class ApiAction extends Action } function twitterStatusArray($notice, $include_user=true) + { + $base = $this->twitterSimpleStatusArray($notice, $include_user); + + if (empty($notice->repeat_of)) { + return $base; + } else { + $original = Notice::staticGet('id', $notice->repeat_of); + $original_array = $this->twitterSimpleStatusArray($original, $include_user); + $original_array['retweeted_status'] = $base; + return $original_array; + } + } + + function twitterSimpleStatusArray($notice, $include_user=true) { $profile = $notice->getProfile(); @@ -446,9 +460,9 @@ class ApiAction extends Action } } - function showTwitterXmlStatus($twitter_status) + function showTwitterXmlStatus($twitter_status, $tag='status') { - $this->elementStart('status'); + $this->elementStart($tag); foreach($twitter_status as $element => $value) { switch ($element) { case 'user': @@ -463,11 +477,14 @@ class ApiAction extends Action case 'geo': $this->showGeoRSS($value); break; + case 'retweeted_status': + $this->showTwitterXmlStatus($value, 'retweeted_status'); + break; default: $this->element($element, null, $value); } } - $this->elementEnd('status'); + $this->elementEnd($tag); } function showTwitterXmlGroup($twitter_group) -- cgit v1.2.3-54-g00ecf From 683edfd199ae150afa74993521c4c65d76b1e19d Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Fri, 11 Dec 2009 12:40:05 -0500 Subject: show repeated notices correctly in API output --- actions/apistatusesretweet.php | 136 +++++++++++++++++++++++++++++++++++++++++ lib/router.php | 5 ++ 2 files changed, 141 insertions(+) create mode 100644 actions/apistatusesretweet.php diff --git a/actions/apistatusesretweet.php b/actions/apistatusesretweet.php new file mode 100644 index 000000000..fc71d2274 --- /dev/null +++ b/actions/apistatusesretweet.php @@ -0,0 +1,136 @@ +. + * + * @category API + * @package StatusNet + * @author Evan Prodromou + * @copyright 2009 StatusNet, Inc. + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://status.net/ + */ + +if (!defined('STATUSNET')) { + exit(1); +} + +require_once INSTALLDIR . '/lib/apiauth.php'; +require_once INSTALLDIR . '/lib/mediafile.php'; + +/** + * Repeat a notice through the API + * + * @category API + * @package StatusNet + * @author Evan Prodromou + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://status.net/ + */ + +class ApiStatusesRetweetAction extends ApiAuthAction +{ + var $original = null; + + /** + * Take arguments for running + * + * @param array $args $_REQUEST args + * + * @return boolean success flag + * + */ + + function prepare($args) + { + parent::prepare($args); + + if ($_SERVER['REQUEST_METHOD'] != 'POST') { + $this->clientError(_('This method requires a POST.'), + 400, $this->format); + return false; + } + + $id = $this->trimmed('id'); + + $this->original = Notice::staticGet('id', $id); + + if (empty($this->original)) { + $this->clientError(_('No such notice'), + 400, $this->format); + return false; + } + + $this->user = $this->auth_user; + + if ($this->user->id == $notice->profile_id) { + $this->clientError(_('Cannot repeat your own notice')); + 400, $this->format); + return false; + } + + $profile = $this->user->getProfile(); + + if ($profile->hasRepeated($id)) { + $this->clientError(_('Already repeated that notice'), + 400, $this->format); + return false; + } + + return true; + } + + /** + * Handle the request + * + * Make a new notice for the update, save it, and show it + * + * @param array $args $_REQUEST data (unused) + * + * @return void + */ + + function handle($args) + { + parent::handle($args); + + $repeat = $this->original->repeat($this->user->id, $this->source); + + common_broadcast_notice($repeat); + + $this->showNotice($repeat); + } + + /** + * Show the resulting notice + * + * @return void + */ + + function showNotice($notice) + { + if (!empty($notice)) { + if ($this->format == 'xml') { + $this->showSingleXmlStatus($notice); + } elseif ($this->format == 'json') { + $this->show_single_json_status($notice); + } + } + } +} diff --git a/lib/router.php b/lib/router.php index 142206c16..835339720 100644 --- a/lib/router.php +++ b/lib/router.php @@ -359,6 +359,11 @@ class Router 'id' => '[0-9]+', 'format' => '(xml|json)')); + $m->connect('api/statuses/retweet/:id.:format', + array('action' => 'ApiStatusesRetweet', + 'id' => '[0-9]+', + 'format' => '(xml|json)')); + // users $m->connect('api/users/show.:format', -- cgit v1.2.3-54-g00ecf From c622d144400026dac65c39303994d11f114c0f5b Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Sat, 12 Dec 2009 14:46:24 -0500 Subject: add statuses/retweets to API --- actions/apistatusesretweets.php | 116 ++++++++++++++++++++++++++++++++++++++++ classes/Notice.php | 67 +++++++++++++++++++++++ lib/router.php | 5 ++ 3 files changed, 188 insertions(+) create mode 100644 actions/apistatusesretweets.php diff --git a/actions/apistatusesretweets.php b/actions/apistatusesretweets.php new file mode 100644 index 000000000..c54a374e2 --- /dev/null +++ b/actions/apistatusesretweets.php @@ -0,0 +1,116 @@ +. + * + * @category API + * @package StatusNet + * @author Evan Prodromou + * @copyright 2009 StatusNet, Inc. + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://status.net/ + */ + +if (!defined('STATUSNET')) { + exit(1); +} + +require_once INSTALLDIR . '/lib/apiauth.php'; +require_once INSTALLDIR . '/lib/mediafile.php'; + +/** + * Show up to 100 repeats of a notice + * + * @category API + * @package StatusNet + * @author Evan Prodromou + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://status.net/ + */ + +class ApiStatusesRetweetsAction extends ApiAuthAction +{ + const MAXCOUNT = 100; + + var $original = null; + var $cnt = self::MAXCOUNT; + + /** + * Take arguments for running + * + * @param array $args $_REQUEST args + * + * @return boolean success flag + * + */ + + function prepare($args) + { + parent::prepare($args); + + $id = $this->trimmed('id'); + + $this->original = Notice::staticGet('id', $id); + + if (empty($this->original)) { + $this->clientError(_('No such notice'), + 400, $this->format); + return false; + } + + $cnt = $this->trimmed('count'); + + if (empty($cnt) || !is_integer($cnt)) { + $cnt = 100; + } else { + $this->cnt = min((int)$cnt, self::MAXCOUNT); + } + + return true; + } + + /** + * Handle the request + * + * Make a new notice for the update, save it, and show it + * + * @param array $args $_REQUEST data (unused) + * + * @return void + */ + + function handle($args) + { + parent::handle($args); + + $strm = $this->original->repeatStream($this->cnt); + + switch ($this->format) { + case 'xml': + $this->showXmlTimeline($strm); + break; + case 'json': + $this->showJsonTimeline($strm); + break; + default: + $this->clientError(_('API method not found!'), $code = 404); + break; + } + } +} diff --git a/classes/Notice.php b/classes/Notice.php index 82753fbdd..ec80f763f 100644 --- a/classes/Notice.php +++ b/classes/Notice.php @@ -441,10 +441,23 @@ class Notice extends Memcached_DataObject $this->blowTagCache($blowLast); $this->blowGroupCache($blowLast); $this->blowConversationCache($blowLast); + $this->blowRepeatCache(); $profile = Profile::staticGet($this->profile_id); $profile->blowNoticeCount(); } + function blowRepeatCache() + { + if (!empty($this->repeat_of)) { + $cache = common_memcache(); + if (!empty($cache)) { + // XXX: only blow if <100 in cache + $ck = common_cache_key('notice:repeats:'.$this->repeat_of); + $result = $cache->delete($ck); + } + } + } + function blowConversationCache($blowLast=false) { $cache = common_memcache(); @@ -1455,4 +1468,58 @@ class Notice extends Memcached_DataObject return self::saveNew($repeater_id, $content, $source, array('repeat_of' => $this->id)); } + + // These are supposed to be in chron order! + + function repeatStream($limit=100) + { + $cache = common_memcache(); + + if (empty($cache)) { + $ids = $this->_repeatStreamDirect($limit); + } else { + $idstr = $cache->get(common_cache_key('notice:repeats:'.$this->id)); + if (!empty($idstr)) { + $ids = explode(',', $idstr); + } else { + $ids = $this->_repeatStreamDirect(100); + $cache->set(common_cache_key('notice:repeats:'.$this->id), implode(',', $ids)); + } + if ($limit < 100) { + // We do a max of 100, so slice down to limit + $ids = array_slice($ids, 0, $limit); + } + } + + return Notice::getStreamByIds($ids); + } + + function _repeatStreamDirect($limit) + { + $notice = new Notice(); + + $notice->selectAdd(); // clears it + $notice->selectAdd('id'); + + $notice->repeat_of = $this->id; + + $notice->orderBy('created'); // NB: asc! + + if (!is_null($offset)) { + $notice->limit($offset, $limit); + } + + $ids = array(); + + if ($notice->find()) { + while ($notice->fetch()) { + $ids[] = $notice->id; + } + } + + $notice->free(); + $notice = NULL; + + return $ids; + } } diff --git a/lib/router.php b/lib/router.php index 835339720..b0b95b080 100644 --- a/lib/router.php +++ b/lib/router.php @@ -364,6 +364,11 @@ class Router 'id' => '[0-9]+', 'format' => '(xml|json)')); + $m->connect('api/statuses/retweets/:id.:format', + array('action' => 'ApiStatusesRetweets', + 'id' => '[0-9]+', + 'format' => '(xml|json)')); + // users $m->connect('api/users/show.:format', -- cgit v1.2.3-54-g00ecf From 138ce0cd05e2e59c79b29f5eeea5c11d1e56e931 Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Sat, 12 Dec 2009 15:35:05 -0500 Subject: add statuses/retweeted_by_me api action --- actions/apitimelineretweetedbyme.php | 126 +++++++++++++++++++++++++++++++++++ classes/Notice.php | 9 +++ classes/User.php | 52 +++++++++++++++ lib/router.php | 4 ++ 4 files changed, 191 insertions(+) create mode 100644 actions/apitimelineretweetedbyme.php diff --git a/actions/apitimelineretweetedbyme.php b/actions/apitimelineretweetedbyme.php new file mode 100644 index 000000000..1e65678ad --- /dev/null +++ b/actions/apitimelineretweetedbyme.php @@ -0,0 +1,126 @@ +. + * + * @category API + * @package StatusNet + * @author Evan Prodromou + * @copyright 2009 StatusNet, Inc. + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://status.net/ + */ + +if (!defined('STATUSNET')) { + exit(1); +} + +require_once INSTALLDIR . '/lib/apiauth.php'; +require_once INSTALLDIR . '/lib/mediafile.php'; + +/** + * Show authenticating user's most recent repeats + * + * @category API + * @package StatusNet + * @author Evan Prodromou + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://status.net/ + */ + +class ApiTimelineRetweetedByMeAction extends ApiAuthAction +{ + const DEFAULTCOUNT = 20; + const MAXCOUNT = 200; + const MAXNOTICES = 3200; + + var $repeats = null; + var $cnt = self::DEFAULTCOUNT; + var $page = 1; + var $since_id = null; + var $max_id = null; + + /** + * Take arguments for running + * + * @param array $args $_REQUEST args + * + * @return boolean success flag + * + */ + + function prepare($args) + { + parent::prepare($args); + + $cnt = $this->int('count', self::DEFAULTCOUNT, self::MAXCOUNT, 1); + + $page = $this->int('page', 1, (self::MAXNOTICES/$this->cnt)); + + $since_id = $this->int('since_id'); + + $max_id = $this->int('max_id'); + + return true; + } + + /** + * Handle the request + * + * show a timeline of the user's repeated notices + * + * @param array $args $_REQUEST data (unused) + * + * @return void + */ + + function handle($args) + { + parent::handle($args); + + $offset = ($this->page-1) * $this->cnt; + $limit = $this->cnt; + + $strm = $this->auth_user->repeatedByMe($offset, $limit, $this->since_id, $this->max_id); + + switch ($this->format) { + case 'xml': + $this->showXmlTimeline($strm); + break; + case 'json': + $this->showJsonTimeline($strm); + break; + case 'atom': + $profile = $this->auth_user->getProfile(); + + $title = sprintf(_("Repeated by %s"), $this->auth_user->nickname); + $taguribase = common_config('integration', 'taguri'); + $id = "tag:$taguribase:RepeatedByMe:" . $this->auth_user->id; + $link = common_local_url('showstream', + array('nickname' => $this->auth_user->nickname)); + + $this->showAtomTimeline($strm, $title, $id, $link); + break; + + default: + $this->clientError(_('API method not found!'), $code = 404); + break; + } + } +} diff --git a/classes/Notice.php b/classes/Notice.php index ec80f763f..6a701ae0c 100644 --- a/classes/Notice.php +++ b/classes/Notice.php @@ -454,6 +454,15 @@ class Notice extends Memcached_DataObject // XXX: only blow if <100 in cache $ck = common_cache_key('notice:repeats:'.$this->repeat_of); $result = $cache->delete($ck); + + $user = User::staticGet('id', $this->profile_id); + + if (!empty($user)) { + $uk = common_cache_key('user:repeated_by_me:'.$user->id); + $cache->delete($uk); + $user->free(); + unset($user); + } } } } diff --git a/classes/User.php b/classes/User.php index 2a4fab7d4..4b7365fc7 100644 --- a/classes/User.php +++ b/classes/User.php @@ -741,4 +741,56 @@ class User extends Memcached_DataObject $profile = $this->getProfile(); return $profile->isSilenced(); } + + function repeatedByMe($offset=0, $limit=20, $since_id=null, $max_id=null) + { + $ids = Notice::stream(array($this, '_repeatedByMeDirect'), + array(), + 'user:repeated_by_me:'.$this->id, + $offset, $limit, $since_id, $max_id, null); + + return Notice::getStreamByIds($ids); + } + + function _repeatedByMeDirect($offset, $limit, $since_id, $max_id, $since) + { + $notice = new Notice(); + + $notice->selectAdd(); // clears it + $notice->selectAdd('id'); + + $notice->profile_id = $this->id; + $notice->whereAdd('repeat_of IS NOT NULL'); + + $notice->orderBy('id DESC'); + + if (!is_null($offset)) { + $notice->limit($offset, $limit); + } + + if ($since_id != 0) { + $notice->whereAdd('id > ' . $since_id); + } + + if ($max_id != 0) { + $notice->whereAdd('id <= ' . $max_id); + } + + if (!is_null($since)) { + $notice->whereAdd('created > \'' . date('Y-m-d H:i:s', $since) . '\''); + } + + $ids = array(); + + if ($notice->find()) { + while ($notice->fetch()) { + $ids[] = $notice->id; + } + } + + $notice->free(); + $notice = NULL; + + return $ids; + } } diff --git a/lib/router.php b/lib/router.php index b0b95b080..5c1bd3c4f 100644 --- a/lib/router.php +++ b/lib/router.php @@ -319,6 +319,10 @@ class Router 'id' => '[a-zA-Z0-9]+', 'format' => '(xml|json|rss|atom)')); + $m->connect('api/statuses/retweeted_by_me.:format', + array('action' => 'ApiTimelineRetweetedByMe', + 'format' => '(xml|json|atom)')); + $m->connect('api/statuses/friends.:format', array('action' => 'ApiUserFriends', 'format' => '(xml|json)')); -- cgit v1.2.3-54-g00ecf From cfe67a9c0192129448ea3f0b98aadf49e962fd4d Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Sat, 12 Dec 2009 16:00:27 -0500 Subject: add statuses/retweets_of_me to API --- actions/apitimelineretweetsofme.php | 126 ++++++++++++++++++++++++++++++++++++ classes/Notice.php | 14 ++++ classes/User.php | 53 +++++++++++++++ lib/router.php | 4 ++ 4 files changed, 197 insertions(+) create mode 100644 actions/apitimelineretweetsofme.php diff --git a/actions/apitimelineretweetsofme.php b/actions/apitimelineretweetsofme.php new file mode 100644 index 000000000..479bff431 --- /dev/null +++ b/actions/apitimelineretweetsofme.php @@ -0,0 +1,126 @@ +. + * + * @category API + * @package StatusNet + * @author Evan Prodromou + * @copyright 2009 StatusNet, Inc. + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://status.net/ + */ + +if (!defined('STATUSNET')) { + exit(1); +} + +require_once INSTALLDIR . '/lib/apiauth.php'; +require_once INSTALLDIR . '/lib/mediafile.php'; + +/** + * Show authenticating user's most recent notices that have been repeated + * + * @category API + * @package StatusNet + * @author Evan Prodromou + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://status.net/ + */ + +class ApiTimelineRetweetsOfMeAction extends ApiAuthAction +{ + const DEFAULTCOUNT = 20; + const MAXCOUNT = 200; + const MAXNOTICES = 3200; + + var $repeats = null; + var $cnt = self::DEFAULTCOUNT; + var $page = 1; + var $since_id = null; + var $max_id = null; + + /** + * Take arguments for running + * + * @param array $args $_REQUEST args + * + * @return boolean success flag + * + */ + + function prepare($args) + { + parent::prepare($args); + + $cnt = $this->int('count', self::DEFAULTCOUNT, self::MAXCOUNT, 1); + + $page = $this->int('page', 1, (self::MAXNOTICES/$this->cnt)); + + $since_id = $this->int('since_id'); + + $max_id = $this->int('max_id'); + + return true; + } + + /** + * Handle the request + * + * show a timeline of the user's repeated notices + * + * @param array $args $_REQUEST data (unused) + * + * @return void + */ + + function handle($args) + { + parent::handle($args); + + $offset = ($this->page-1) * $this->cnt; + $limit = $this->cnt; + + $strm = $this->auth_user->repeatsOfMe($offset, $limit, $this->since_id, $this->max_id); + + switch ($this->format) { + case 'xml': + $this->showXmlTimeline($strm); + break; + case 'json': + $this->showJsonTimeline($strm); + break; + case 'atom': + $profile = $this->auth_user->getProfile(); + + $title = sprintf(_("Repeats of %s"), $this->auth_user->nickname); + $taguribase = common_config('integration', 'taguri'); + $id = "tag:$taguribase:RepeatsOfMe:" . $this->auth_user->id; + $link = common_local_url('showstream', + array('nickname' => $this->auth_user->nickname)); + + $this->showAtomTimeline($strm, $title, $id, $link); + break; + + default: + $this->clientError(_('API method not found!'), $code = 404); + break; + } + } +} diff --git a/classes/Notice.php b/classes/Notice.php index 6a701ae0c..eb611f314 100644 --- a/classes/Notice.php +++ b/classes/Notice.php @@ -463,6 +463,20 @@ class Notice extends Memcached_DataObject $user->free(); unset($user); } + + $original = Notice::staticGet('id', $this->repeat_of); + + if (!empty($original)) { + $originalUser = User::staticGet('id', $original->profile_id); + if (!empty($originalUser)) { + $ouk = common_cache_key('user:repeats_of_me:'.$originalUser->id); + $cache->delete($ouk); + $originalUser->free(); + unset($originalUser); + } + $original->free(); + unset($original); + } } } } diff --git a/classes/User.php b/classes/User.php index 4b7365fc7..b3da448a6 100644 --- a/classes/User.php +++ b/classes/User.php @@ -793,4 +793,57 @@ class User extends Memcached_DataObject return $ids; } + + function repeatsOfMe($offset=0, $limit=20, $since_id=null, $max_id=null) + { + $ids = Notice::stream(array($this, '_repeatsOfMeDirect'), + array(), + 'user:repeats_of_me:'.$this->id, + $offset, $limit, $since_id, $max_id, null); + + return Notice::getStreamByIds($ids); + } + + function _repeatsOfMeDirect($offset, $limit, $since_id, $max_id, $since) + { + $qry = + 'SELECT DISTINCT original.id AS id ' . + 'FROM notice original JOIN notice rept ON original.id = rept.repeat_of ' . + 'WHERE original.profile_id = ' . $this->id . ' '; + + if ($since_id != 0) { + $qry .= 'AND original.id > ' . $since_id . ' '; + } + + if ($max_id != 0) { + $qry .= 'AND original.id <= ' . $max_id . ' '; + } + + if (!is_null($since)) { + $qry .= 'AND original.modified > \'' . date('Y-m-d H:i:s', $since) . '\' '; + } + + // NOTE: we sort by fave time, not by notice time! + + $qry .= 'ORDER BY original.id DESC '; + + if (!is_null($offset)) { + $qry .= "LIMIT $limit OFFSET $offset"; + } + + $ids = array(); + + $notice = new Notice(); + + $notice->query($qry); + + while ($notice->fetch()) { + $ids[] = $notice->id; + } + + $notice->free(); + $notice = NULL; + + return $ids; + } } diff --git a/lib/router.php b/lib/router.php index 5c1bd3c4f..732caa1c7 100644 --- a/lib/router.php +++ b/lib/router.php @@ -323,6 +323,10 @@ class Router array('action' => 'ApiTimelineRetweetedByMe', 'format' => '(xml|json|atom)')); + $m->connect('api/statuses/retweets_of_me.:format', + array('action' => 'ApiTimelineRetweetsOfMe', + 'format' => '(xml|json|atom)')); + $m->connect('api/statuses/friends.:format', array('action' => 'ApiUserFriends', 'format' => '(xml|json)')); -- cgit v1.2.3-54-g00ecf From 698b28c95c9d4fa5cb404395b2c90b10a438fcd5 Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Sat, 12 Dec 2009 16:02:44 -0500 Subject: clear repeat_of flag when a notice is deleted --- classes/Notice.php | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/classes/Notice.php b/classes/Notice.php index eb611f314..a7b0f8cdb 100644 --- a/classes/Notice.php +++ b/classes/Notice.php @@ -115,6 +115,12 @@ class Notice extends Memcached_DataObject //Null any notices that are replies to this notice $this->query(sprintf("UPDATE notice set reply_to = null WHERE reply_to = %d", $this->id)); + + //Null any notices that are repeats of this notice + //XXX: probably need to uncache these, too + + $this->query(sprintf("UPDATE notice set repeat_of = null WHERE repeat_of = %d", $this->id)); + $related = array('Reply', 'Fave', 'Notice_tag', -- cgit v1.2.3-54-g00ecf From 1ec54d3433a79f56c14c0a99114bb1e607348899 Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Sat, 12 Dec 2009 16:15:23 -0500 Subject: add statuses/retweeted_to_me to API --- actions/apitimelineretweetedtome.php | 125 +++++++++++++++++++++++++++++++++++ classes/Notice.php | 14 ++++ classes/User.php | 54 +++++++++++++++ lib/router.php | 4 ++ 4 files changed, 197 insertions(+) create mode 100644 actions/apitimelineretweetedtome.php diff --git a/actions/apitimelineretweetedtome.php b/actions/apitimelineretweetedtome.php new file mode 100644 index 000000000..681b0b9e9 --- /dev/null +++ b/actions/apitimelineretweetedtome.php @@ -0,0 +1,125 @@ +. + * + * @category API + * @package StatusNet + * @author Evan Prodromou + * @copyright 2009 StatusNet, Inc. + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://status.net/ + */ + +if (!defined('STATUSNET')) { + exit(1); +} + +require_once INSTALLDIR . '/lib/apiauth.php'; + +/** + * Show most recent notices that are repeats in user's inbox + * + * @category API + * @package StatusNet + * @author Evan Prodromou + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://status.net/ + */ + +class ApiTimelineRetweetedToMeAction extends ApiAuthAction +{ + const DEFAULTCOUNT = 20; + const MAXCOUNT = 200; + const MAXNOTICES = 3200; + + var $repeats = null; + var $cnt = self::DEFAULTCOUNT; + var $page = 1; + var $since_id = null; + var $max_id = null; + + /** + * Take arguments for running + * + * @param array $args $_REQUEST args + * + * @return boolean success flag + * + */ + + function prepare($args) + { + parent::prepare($args); + + $cnt = $this->int('count', self::DEFAULTCOUNT, self::MAXCOUNT, 1); + + $page = $this->int('page', 1, (self::MAXNOTICES/$this->cnt)); + + $since_id = $this->int('since_id'); + + $max_id = $this->int('max_id'); + + return true; + } + + /** + * Handle the request + * + * show a timeline of the user's repeated notices + * + * @param array $args $_REQUEST data (unused) + * + * @return void + */ + + function handle($args) + { + parent::handle($args); + + $offset = ($this->page-1) * $this->cnt; + $limit = $this->cnt; + + $strm = $this->auth_user->repeatedToMe($offset, $limit, $this->since_id, $this->max_id); + + switch ($this->format) { + case 'xml': + $this->showXmlTimeline($strm); + break; + case 'json': + $this->showJsonTimeline($strm); + break; + case 'atom': + $profile = $this->auth_user->getProfile(); + + $title = sprintf(_("Repeated to %s"), $this->auth_user->nickname); + $taguribase = common_config('integration', 'taguri'); + $id = "tag:$taguribase:RepeatedToMe:" . $this->auth_user->id; + $link = common_local_url('all', + array('nickname' => $this->auth_user->nickname)); + + $this->showAtomTimeline($strm, $title, $id, $link); + break; + + default: + $this->clientError(_('API method not found!'), $code = 404); + break; + } + } +} diff --git a/classes/Notice.php b/classes/Notice.php index a7b0f8cdb..7d2b898d2 100644 --- a/classes/Notice.php +++ b/classes/Notice.php @@ -483,6 +483,20 @@ class Notice extends Memcached_DataObject $original->free(); unset($original); } + + $ni = new Notice_inbox(); + + $ni->notice_id = $this->id; + + if ($ni->find()) { + while ($ni->fetch()) { + $tmk = common_cache_key('user:repeated_to_me:'.$ni->user_id); + $cache->delete($tmk); + } + } + + $ni->free(); + unset($ni); } } } diff --git a/classes/User.php b/classes/User.php index b3da448a6..9c071e06b 100644 --- a/classes/User.php +++ b/classes/User.php @@ -846,4 +846,58 @@ class User extends Memcached_DataObject return $ids; } + + function repeatedToMe($offset=0, $limit=20, $since_id=null, $max_id=null) + { + $ids = Notice::stream(array($this, '_repeatedToMeDirect'), + array(), + 'user:repeated_to_me:'.$this->id, + $offset, $limit, $since_id, $max_id, null); + + return Notice::getStreamByIds($ids); + } + + function _repeatedToMeDirect($offset, $limit, $since_id, $max_id, $since) + { + $qry = + 'SELECT notice.id AS id ' . + 'FROM notice JOIN notice_inbox ON notice.id = notice_inbox.notice_id ' . + 'WHERE notice_inbox.user_id = ' . $this->id . ' ' . + 'AND notice.repeat_of IS NOT NULL '; + + if ($since_id != 0) { + $qry .= 'AND notice.id > ' . $since_id . ' '; + } + + if ($max_id != 0) { + $qry .= 'AND notice.id <= ' . $max_id . ' '; + } + + if (!is_null($since)) { + $qry .= 'AND notice.modified > \'' . date('Y-m-d H:i:s', $since) . '\' '; + } + + // NOTE: we sort by fave time, not by notice time! + + $qry .= 'ORDER BY notice.id DESC '; + + if (!is_null($offset)) { + $qry .= "LIMIT $limit OFFSET $offset"; + } + + $ids = array(); + + $notice = new Notice(); + + $notice->query($qry); + + while ($notice->fetch()) { + $ids[] = $notice->id; + } + + $notice->free(); + $notice = NULL; + + return $ids; + } } diff --git a/lib/router.php b/lib/router.php index 732caa1c7..8f68f86ac 100644 --- a/lib/router.php +++ b/lib/router.php @@ -323,6 +323,10 @@ class Router array('action' => 'ApiTimelineRetweetedByMe', 'format' => '(xml|json|atom)')); + $m->connect('api/statuses/retweeted_to_me.:format', + array('action' => 'ApiTimelineRetweetedToMe', + 'format' => '(xml|json|atom)')); + $m->connect('api/statuses/retweets_of_me.:format', array('action' => 'ApiTimelineRetweetsOfMe', 'format' => '(xml|json|atom)')); -- cgit v1.2.3-54-g00ecf