diff --git a/plugins/ActivityVerb/ActivityVerbPlugin.php b/plugins/ActivityVerb/ActivityVerbPlugin.php index 70c5682479..dab4cb4d0b 100644 --- a/plugins/ActivityVerb/ActivityVerbPlugin.php +++ b/plugins/ActivityVerb/ActivityVerbPlugin.php @@ -31,6 +31,7 @@ if (!defined('GNUSOCIAL')) { exit(1); } class ActivityVerbPlugin extends Plugin { + public function onRouterInitialized(URLMapper $m) { $m->connect('notice/:id/:verb', diff --git a/plugins/ActivityVerb/actions/activityverb.php b/plugins/ActivityVerb/actions/activityverb.php index 1121056de6..0abfacd645 100644 --- a/plugins/ActivityVerb/actions/activityverb.php +++ b/plugins/ActivityVerb/actions/activityverb.php @@ -35,8 +35,15 @@ class ActivityverbAction extends ManagedAction protected $canPost = true; protected $verb = null; - - protected function doPreparation($args) + + public function title() + { + $title = null; + Event::handle('ActivityVerbTitle', array($this, $this->verb, $this->notice, $this->scoped, &$title)); + return $title; + } + + protected function doPreparation() { $this->verb = $this->trimmed('verb'); if (empty($this->verb)) { @@ -50,13 +57,15 @@ class ActivityverbAction extends ManagedAction throw new ClientException(sprintf(_('%1$s has no access to notice %2$d.'), $this->scoped->getNickname(), $this->notice->getID()), 403); } + + Event::handle('ActivityVerbDoPreparation', array($this, $this->verb, $this->notice, $this->scoped)); } protected function doPost() { if (Event::handle('ActivityVerbDoPost', array($this, $this->verb, $this->notice, $this->scoped))) { // TRANS: Error when a POST method for an activity verb has not been handled by a plugin. - throw new ClientException(_('Could not show content for verb "%1$s".')); + throw new ClientException(sprintf(_('Could not handle POST for verb "%1$s".'), $this->verb)); } } @@ -64,7 +73,7 @@ class ActivityverbAction extends ManagedAction { if (Event::handle('ActivityVerbShowContent', array($this, $this->verb, $this->notice, $this->scoped))) { // TRANS: Error when a page for an activity verb has not been handled by a plugin. - throw new ClientException(_('Could not show content for verb "%1$s".')); + $this->element('div', 'error', sprintf(_('Could not show content for verb "%1$s".'), $this->verb)); } } } diff --git a/plugins/ActivityVerb/lib/activityverbhandlerplugin.php b/plugins/ActivityVerb/lib/activityverbhandlerplugin.php new file mode 100644 index 0000000000..0d06266664 --- /dev/null +++ b/plugins/ActivityVerb/lib/activityverbhandlerplugin.php @@ -0,0 +1,82 @@ +. + */ + +if (!defined('GNUSOCIAL')) { exit(1); } + +/** + * @package Activity + * @maintainer Mikael Nordfeldth + */ +abstract class ActivityVerbHandlerPlugin extends ActivityHandlerPlugin +{ + public function onActivityVerbTitle(ManagedAction $action, $verb, Notice $target, Profile $scoped, &$title) + { + if (!$this->isMyVerb($verb)) { + return true; + } + + $title = $this->getActionTitle($action, $verb, $target, $scoped); + return false; + } + abstract protected function getActionTitle(ManagedAction $action, $verb, Notice $target, Profile $scoped); + + public function onActivityVerbShowContent(ManagedAction $action, $verb, Notice $target, Profile $scoped) + { + if (!$this->isMyVerb($verb)) { + return true; + } + + return $this->showActionContent($action, $verb, $target, $scoped); + } + protected function showActionContent(ManagedAction $action, $verb, Notice $target, Profile $scoped) + { + if (!GNUsocial::isAjax()) { + $nl = new NoticeListItem($target, $action, array('options'=>false, 'attachments'=>false, + 'item_tag'=>'div', 'id_prefix'=>'fave')); + $nl->show(); + } + + $form = $this->getActivityForm($action, $verb, $target, $scoped); + $form->show(); + + return false; + } + + public function onActivityVerbDoPreparation(ManagedAction $action, $verb, Notice $target, Profile $scoped) + { + if (!$this->isMyVerb($verb)) { + return true; + } + + return $this->doActionPreparation($action, $verb, $target, $scoped); + } + abstract protected function doActionPreparation(ManagedAction $action, $verb, Notice $target, Profile $scoped); + + public function onActivityVerbDoPost(ManagedAction $action, $verb, Notice $target, Profile $scoped) + { + if (!$this->isMyVerb($verb)) { + return true; + } + + return $this->doActionPost($action, $verb, $target, $scoped); + } + abstract protected function doActionPost(ManagedAction $action, $verb, Notice $target, Profile $scoped); + + abstract protected function getActivityForm(ManagedAction $action, $verb, Notice $target, Profile $scoped); +} diff --git a/plugins/Favorite/FavoritePlugin.php b/plugins/Favorite/FavoritePlugin.php index 9b22484851..f298fc3e92 100644 --- a/plugins/Favorite/FavoritePlugin.php +++ b/plugins/Favorite/FavoritePlugin.php @@ -23,7 +23,7 @@ if (!defined('GNUSOCIAL')) { exit(1); } * @package Activity * @maintainer Mikael Nordfeldth */ -class FavoritePlugin extends ActivityHandlerPlugin +class FavoritePlugin extends ActivityVerbHandlerPlugin { protected $email_notify_fave = 1; @@ -39,9 +39,10 @@ class FavoritePlugin extends ActivityHandlerPlugin public function verbs() { - return array(ActivityVerb::FAVORITE); + return array(ActivityVerb::FAVORITE, ActivityVerb::LIKE, + ActivityVerb::UNFAVORITE, ActivityVerb::UNLIKE); } - + public function onCheckSchema() { $schema = Schema::get(); @@ -78,14 +79,14 @@ class FavoritePlugin extends ActivityHandlerPlugin printfnq("DONE.\n"); } } - + public function onEndUpgrade() { printfnq("Ensuring all faves have a URI..."); - + $fave = new Fave(); $fave->whereAdd('uri IS NULL'); - + if ($fave->find()) { while ($fave->fetch()) { try { @@ -104,7 +105,7 @@ class FavoritePlugin extends ActivityHandlerPlugin } } } - + printfnq("DONE.\n"); } @@ -180,7 +181,7 @@ class FavoritePlugin extends ActivityHandlerPlugin } // FIXME: Set this to abstract public in lib/activityhandlerplugin.php ddwhen all plugins have migrated! - protected function saveObjectFromActivity(Activity $act, Notice $stored, array $options=array()) + protected function saveObjectFromActivity(Activity $act, Notice $stored, array $options=array()) { assert($this->isMyActivity($act)); @@ -263,7 +264,7 @@ class FavoritePlugin extends ActivityHandlerPlugin } return true; } - + public function onNoticeDeleteRelated(Notice $notice) { parent::onNoticeDeleteRelated($notice); @@ -503,6 +504,62 @@ class FavoritePlugin extends ActivityHandlerPlugin } } + protected function getActionTitle(ManagedAction $action, $verb, Notice $target, Profile $scoped) + { + return Fave::existsForProfile($target, $scoped) + // TRANS: Page/dialog box title when a notice is marked as favorite already + ? _m('TITLE', 'Unmark notice as favorite') + // TRANS: Page/dialog box title when a notice is not marked as favorite + : _m('TITLE', 'Mark notice as favorite'); + } + + protected function doActionPreparation(ManagedAction $action, $verb, Notice $target, Profile $scoped) + { + if ($action->isPost()) { + // The below tests are only for presenting to the user. POSTs which inflict + // duplicate favorite entries are handled with AlreadyFulfilledException. + return false; + } + + $exists = Fave::existsForProfile($target, $scoped); + $expected_verb = $exists ? ActivityVerb::UNFAVORITE : ActivityVerb::FAVORITE; + + switch (true) { + case $exists && ActivityUtils::compareTypes($verb, array(ActivityVerb::FAVORITE, ActivityVerb::LIKE)): + case !$exists && ActivityUtils::compareTypes($verb, array(ActivityVerb::UNFAVORITE, ActivityVerb::UNLIKE)): + common_redirect(common_local_url('activityverb', + array('id' => $target->getID(), + 'verb' => ActivityUtils::resolveUri($expected_verb, true)))); + break; + default: + // No need to redirect as we are on the correct action already. + } + + return false; + } + + protected function doActionPost(ManagedAction $action, $verb, Notice $target, Profile $scoped) + { + switch (true) { + case ActivityUtils::compareTypes($verb, array(ActivityVerb::FAVORITE, ActivityVerb::LIKE)): + Fave::addNew($scoped, $target); + break; + case ActivityUtils::compareTypes($verb, array(ActivityVerb::UNFAVORITE, ActivityVerb::UNLIKE)): + Fave::removeEntry($scoped, $target); + break; + default: + throw new ServerException('ActivityVerb POST not handled by plugin that was supposed to do it.'); + } + return false; + } + + protected function getActivityForm(ManagedAction $action, $verb, Notice $target, Profile $scoped) + { + return Fave::existsForProfile($target, $scoped) + ? new DisfavorForm($action, $target) + : new FavorForm($action, $target); + } + public function onPluginVersion(array &$versions) { $versions[] = array('name' => 'Favorite', diff --git a/plugins/Favorite/actions/disfavor.php b/plugins/Favorite/actions/disfavor.php deleted file mode 100644 index 926bbeca58..0000000000 --- a/plugins/Favorite/actions/disfavor.php +++ /dev/null @@ -1,84 +0,0 @@ - - * @author Robin Millette - * @author Mikael Nordfeldth - * @license http://www.fsf.org/licensing/licenses/agpl.html AGPLv3 - * @link http://www.gnu.org/software/social/ - * - * 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('GNUSOCIAL')) { exit(1); } - -/** - * DisfavorAction class. - * - * @category Action - * @package GNUsocial - * @author Evan Prodromou - * @author Robin Millette - * @author Mikael Nordfeldth - * @license http://www.fsf.org/licensing/licenses/agpl.html AGPLv3 - * @link http://www.gnu.org/software/social/ - */ -class DisfavorAction extends FormAction -{ - protected $needPost = true; - - protected function doPreparation() - { - $this->target = Notice::getKV($this->trimmed('notice')); - if (!$this->target instanceof Notice) { - throw new ServerException(_m('No such notice.')); - } - } - - protected function doPost() - { - $fave = new Fave(); - $fave->user_id = $this->scoped->getID(); - $fave->notice_id = $this->target->getID(); - if (!$fave->find(true)) { - // TRANS: Client error displayed when trying to remove a 'favor' when there is none in the first place. - throw new AlreadyFulfilledException(_('This is already not favorited.')); - } - $result = $fave->delete(); - if ($result === false) { - common_log_db_error($fave, 'DELETE', __FILE__); - // TRANS: Server error displayed when removing a favorite from the database fails. - throw new ServerException(_('Could not delete favorite.')); - } - Fave::blowCacheForProfileId($this->scoped->getID()); - - // TRANS: Message when a disfavor action has been taken for a notice. - return _('Disfavored the notice.'); - } - - protected function showContent() - { - // We show the 'Favor' form because right now all calls to Disfavor will directly disfavor a notice. - $disfavor = new FavorForm($this, $this->target); - $disfavor->show(); - } -} diff --git a/plugins/Favorite/actions/favor.php b/plugins/Favorite/actions/favor.php deleted file mode 100644 index 0d52f77961..0000000000 --- a/plugins/Favorite/actions/favor.php +++ /dev/null @@ -1,76 +0,0 @@ - - * @author Robin Millette - * @author Mikael Nordfeldth - * @license http://www.fsf.org/licensing/licenses/agpl.html AGPLv3 - * @link http://www.gnu.org/software/social/ - * - * 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('GNUSOCIAL')) { exit(1); } - -/** - * FavorAction class. - * - * @category Action - * @package GNUsocial - * @author Evan Prodromou - * @author Robin Millette - * @author Mikael Nordfeldth - * @license http://www.fsf.org/licensing/licenses/agpl.html AGPLv3 - * @link http://www.gnu.org/software/social/ - */ -class FavorAction extends FormAction -{ - protected $needPost = true; - - protected function doPreparation() - { - $this->target = Notice::getKV($this->trimmed('notice')); - if (!$this->target instanceof Notice) { - throw new ServerException(_m('No such notice.')); - } - if (!$this->target->inScope($this->scoped)) { - // TRANS: Client error displayed when trying to interact with a notice a the target has no access to. - // TRANS: %1$s is a user nickname, %2$d is a notice ID (number). - throw new ClientException(sprintf(_m('%1$s has no right to interact with notice %2$d.'), $this->scoped->getNickname(), $this->target->getID()), 403); - } - } - - protected function doPost() - { - // throws exception on failure, might be an AlreadyFulfilledException - $stored = Fave::addNew($this->scoped, $this->target); - - // TRANS: Message when a favor action has been taken for a notice. - return _('Favorited the notice'); - } - - protected function showContent() - { - $disfavor = new DisfavorForm($this, $this->target); - $disfavor->show(); - } -} diff --git a/plugins/Favorite/classes/Fave.php b/plugins/Favorite/classes/Fave.php index ba0d04ba25..48ed3ba93b 100644 --- a/plugins/Favorite/classes/Fave.php +++ b/plugins/Favorite/classes/Fave.php @@ -79,6 +79,27 @@ class Fave extends Managed_DataObject return $stored; } + public function removeEntry(Profile $actor, Notice $target) + { + $fave = new Fave(); + $fave->user_id = $actor->getID(); + $fave->notice_id = $target->getID(); + if (!$fave->find(true)) { + // TRANS: Client error displayed when trying to remove a 'favor' when there is none in the first place. + throw new AlreadyFulfilledException(_('This is already not favorited.')); + } + + $result = $fave->delete(); + if ($result === false) { + common_log_db_error($fave, 'DELETE', __FILE__); + // TRANS: Server error displayed when removing a favorite from the database fails. + throw new ServerException(_('Could not delete favorite.')); + } + + Fave::blowCacheForProfileId($actor->getID()); + Fave::blowCacheForNoticeId($target->getID()); + } + // exception throwing takeover! public function insert() { diff --git a/plugins/Favorite/forms/disfavor.php b/plugins/Favorite/forms/disfavor.php index 51903b6cb2..f6702ebf29 100644 --- a/plugins/Favorite/forms/disfavor.php +++ b/plugins/Favorite/forms/disfavor.php @@ -81,7 +81,9 @@ class DisfavorForm extends Form */ function action() { - return common_local_url('disfavor'); + return common_local_url('activityverb', + array('id' => $this->notice->getID(), + 'verb' => ActivityUtils::resolveUri(ActivityVerb::UNFAVORITE, true))); } /** diff --git a/plugins/Favorite/forms/favor.php b/plugins/Favorite/forms/favor.php index cd956f67ff..e7d05ce097 100644 --- a/plugins/Favorite/forms/favor.php +++ b/plugins/Favorite/forms/favor.php @@ -81,7 +81,9 @@ class FavorForm extends Form */ function action() { - return common_local_url('favor'); + return common_local_url('activityverb', + array('id' => $this->notice->getID(), + 'verb' => ActivityUtils::resolveUri(ActivityVerb::FAVORITE, true))); } /**