X-Git-Url: https://git.mxchange.org/?a=blobdiff_plain;f=plugins%2FShare%2FSharePlugin.php;h=d34bcc779e2b2253efcd5d907270af17e16d0af4;hb=e4a1dff98d6a15c8f513bdb2ef55c699f8e70bcd;hp=7f71adfd8367cf37cce41225eb172769c036e6cb;hpb=5bea746fa81d70335717f592fadb80f3892f7d73;p=quix0rs-gnu-social.git diff --git a/plugins/Share/SharePlugin.php b/plugins/Share/SharePlugin.php index 7f71adfd83..d34bcc779e 100644 --- a/plugins/Share/SharePlugin.php +++ b/plugins/Share/SharePlugin.php @@ -40,6 +40,14 @@ class SharePlugin extends ActivityVerbHandlerPlugin return array(ActivityVerb::SHARE); } + // Share is a bit special and $act->objects[0] should be an Activity + // instead of ActivityObject! Therefore also $act->objects[0]->type is not set. + public function isMyActivity(Activity $act) { + return (count($act->objects) == 1 + && ($act->objects[0] instanceof Activity) + && $this->isMyVerb($act->verb)); + } + public function onRouterInitialized(URLMapper $m) { // Web UI actions @@ -74,9 +82,58 @@ class SharePlugin extends ActivityVerbHandlerPlugin { assert($this->isMyActivity($act)); + // The below algorithm is mainly copied from the previous Ostatus_profile->processShare() + + if (count($act->objects) !== 1) { + // TRANS: Client exception thrown when trying to share multiple activities at once. + throw new ClientException(_m('Can only handle share activities with exactly one object.')); + } + + $shared = $act->objects[0]; + if (!$shared instanceof Activity) { + // TRANS: Client exception thrown when trying to share a non-activity object. + throw new ClientException(_m('Can only handle shared activities.')); + } + + $sharedUri = $shared->id; + if (!empty($shared->objects[0]->id)) { + // Because StatusNet since commit 8cc4660 sets $shared->id to a TagURI which + // fucks up federation, because the URI is no longer recognised by the origin. + // So we set it to the object ID if it exists, otherwise we trust $shared->id + $sharedUri = $shared->objects[0]->id; + } + if (empty($sharedUri)) { + throw new ClientException(_m('Shared activity does not have an id')); + } + + try { + // First check if we have the shared activity. This has to be done first, because + // we can't use these functions to "ensureActivityObjectProfile" of a local user, + // who might be the creator of the shared activity in question. + $sharedNotice = Notice::getByUri($sharedUri); + } catch (NoResultException $e) { + // If no locally stored notice is found, process it! + // TODO: Remember to check Deleted_notice! + // TODO: If a post is shared that we can't retrieve - what to do? + $other = Ostatus_profile::ensureActivityObjectProfile($shared->actor); + $sharedNotice = Notice::saveActivity($shared, $other->localProfile(), array('source'=>'share')); + } catch (FeedSubException $e) { + // Remote feed could not be found or verified, should we + // transform this into an "RT @user Blah, blah, blah..."? + common_log(LOG_INFO, __METHOD__ . ' got a ' . get_class($e) . ': ' . $e->getMessage()); + return false; + } + + // Setting this here because when the algorithm gets back to + // Notice::saveActivity it will update the Notice object. + $stored->repeat_of = $sharedNotice->getID(); + $stored->conversation = $sharedNotice->conversation; + // We don't have to save a repeat in a separate table, we can // find repeats by just looking at the notice.repeat_of field. + // By returning true here instead of something that evaluates + // to false, we show that we have processed everything properly. return true; } @@ -97,7 +154,7 @@ class SharePlugin extends ActivityVerbHandlerPlugin public function extendActivity(Notice $stored, Activity $act, Profile $scoped=null) { // TODO: How to handle repeats of deleted notices? - $target = Notice::getById($stored->repeat_of); + $target = Notice::getByID($stored->repeat_of); // TRANS: A repeat activity's title. %1$s is repeater's nickname // and %2$s is the repeated user's nickname. $act->title = sprintf(_('%1$s repeated a notice by %2$s'), @@ -106,129 +163,95 @@ class SharePlugin extends ActivityVerbHandlerPlugin $act->objects[] = $target->asActivity($scoped); } - public function activityObjectFromNotice(Notice $notice) + public function activityObjectFromNotice(Notice $stored) { // Repeat is a little bit special. As it's an activity, our // ActivityObject is instead turned into an Activity $object = new Activity(); + $object->actor = $stored->getProfile()->asActivityObject(); $object->verb = ActivityVerb::SHARE; - $object->type = $notice->object_type; - $object->title = sprintf(_('%1$s repeated a notice by %2$s'), - $object->getProfile()->getNickname(), - $target->getProfile()->getNickname()); - $object->content = $notice->rendered; + $object->content = $stored->getRendered(); + $this->extendActivity($stored, $object); + return $object; } public function deleteRelated(Notice $notice) { - try { - $fave = Fave::fromStored($notice); - $fave->delete(); - } catch (NoResultException $e) { - // Cool, no problem. We wanted to get rid of it anyway. - } + // No action needed as we don't have a separate table for share objects. + return true; } - // API stuff + // Layout stuff /** - * Typically just used to fill out Twitter-compatible API status data. + * show a link to the author of repeat * - * FIXME: Make all the calls before this end up with a Notice instead of ArrayWrapper please... + * FIXME: Some repeat stuff still in lib/noticelistitem.php! ($nli->repeat etc.) */ - public function onNoticeSimpleStatusArray($notice, array &$status, Profile $scoped=null, array $args=array()) - { - if ($scoped instanceof Profile) { - $status['favorited'] = Fave::existsForProfile($notice, $scoped); - } else { - $status['favorited'] = false; - } - return true; - } - - public function onTwitterUserArray(Profile $profile, array &$userdata, Profile $scoped=null, array $args=array()) + public function onEndShowNoticeInfo(NoticeListItem $nli) { - $userdata['favourites_count'] = Fave::countByProfile($profile); - } + if (!empty($nli->repeat)) { + $repeater = $nli->repeat->getProfile(); - /** - * Typically just used to fill out StatusNet specific data in API calls in the referenced $info array. - */ - public function onStatusNetApiNoticeInfo(Notice $notice, array &$info, Profile $scoped=null, array $args=array()) - { - if ($scoped instanceof Profile) { - $info['favorite'] = Fave::existsForProfile($notice, $scoped) ? 'true' : 'false'; - } - return true; - } + $attrs = array('href' => $repeater->getUrl(), + 'class' => 'h-card p-author', + 'title' => $repeater->getFancyName()); - public function onNoticeDeleteRelated(Notice $notice) - { - parent::onNoticeDeleteRelated($notice); + $nli->out->elementStart('span', 'repeat'); - // The below algorithm is because we want to delete fave - // activities on any notice which _has_ faves, and not as - // in the parent function only ones that _are_ faves. + // TRANS: Addition in notice list item if notice was repeated. Followed by a span with a nickname. + $nli->out->raw(_('Repeated by').' '); - $fave = new Fave(); - $fave->notice_id = $notice->id; + $nli->out->element('a', $attrs, $repeater->getNickname()); - if ($fave->find()) { - while ($fave->fetch()) { - $fave->delete(); - } + $nli->out->elementEnd('span'); } - - $fave->free(); - } - - public function onProfileDeleteRelated(Profile $profile, array &$related) - { - $fave = new Fave(); - $fave->user_id = $profile->id; - $fave->delete(); // Will perform a DELETE matching "user_id = {$user->id}" - $fave->free(); - - Fave::blowCacheForProfileId($profile->id); - return true; } - public function onStartNoticeListPrefill(array &$notices, array $notice_ids, Profile $scoped=null) + public function onEndShowThreadedNoticeTailItems(NoticeListItem $nli, Notice $notice, &$threadActive) { - // prefill array of objects, before pluginfication it was Notice::fillFaves($notices) - Fave::fillFaves($notice_ids); - - // DB caching - if ($scoped instanceof Profile) { - Fave::pivotGet('notice_id', $notice_ids, array('user_id' => $scoped->id)); + if ($nli instanceof ThreadedNoticeListSubItem) { + // The sub-items are replies to a conversation, thus we use different HTML elements etc. + $item = new ThreadedNoticeListInlineRepeatsItem($notice, $nli->out); + } else { + $item = new ThreadedNoticeListRepeatsItem($notice, $nli->out); } + $threadActive = $item->show() || $threadActive; + return true; } /** - * show the "favorite" form in the notice options element + * show the "repeat" form in the notice options element * FIXME: Don't let a NoticeListItemAdapter slip in here (or extend that from NoticeListItem) * * @return void */ - public function onStartShowNoticeOptionItems($nli) + public function onEndShowNoticeOptionItems($nli) { - if (Event::handle('StartShowFaveForm', array($nli))) { + // FIXME: Use bitmasks (but be aware that PUBLIC_SCOPE is 0!) + // Also: AHHH, $scope and $scoped are scarily similar looking. + $scope = $nli->notice->getScope(); + if ($scope === Notice::PUBLIC_SCOPE || $scope === Notice::SITE_SCOPE) { $scoped = Profile::current(); - if ($scoped instanceof Profile) { - if (Fave::existsForProfile($nli->notice, $scoped)) { - $disfavor = new DisfavorForm($nli->out, $nli->notice); - $disfavor->show(); + if ($scoped instanceof Profile && + $scoped->getID() !== $nli->notice->getProfile()->getID()) { + + if ($scoped->hasRepeated($nli->notice)) { + $nli->out->element('span', array('class' => 'repeated', + // TRANS: Title for repeat form status in notice list when a notice has been repeated. + 'title' => _('Notice repeated.')), + // TRANS: Repeat form status in notice list when a notice has been repeated. + _('Repeated')); } else { - $favor = new FavorForm($nli->out, $nli->notice); - $favor->show(); + $repeat = new RepeatForm($nli->out, $nli->notice); + $repeat->show(); } } - Event::handle('EndShowFaveForm', array($nli)); } } - public function showNoticeListItem(NoticeListItem $nli) + protected function showNoticeListItem(NoticeListItem $nli) { // pass } @@ -241,55 +264,38 @@ class SharePlugin extends ActivityVerbHandlerPlugin // pass } - public function onAppendUserActivityStreamObjects(UserActivityStream $uas, array &$objs) - { - $fave = new Fave(); - $fave->user_id = $uas->getUser()->id; + // API stuff - if (!empty($uas->after)) { - $fave->whereAdd("modified > '" . common_sql_date($uas->after) . "'"); - } + /** + * Typically just used to fill out Twitter-compatible API status data. + * + * FIXME: Make all the calls before this end up with a Notice instead of ArrayWrapper please... + */ + public function onNoticeSimpleStatusArray($notice, array &$status, Profile $scoped=null, array $args=array()) + { + $status['repeated'] = $scoped instanceof Profile + ? $scoped->hasRepeated($notice) + : false; - if ($fave->find()) { - while ($fave->fetch()) { - $objs[] = clone($fave); - } + if ($status['repeated'] === true) { + // Qvitter API wants the "repeated_id" value set too. + $repeated = Notice::pkeyGet(array('profile_id' => $scoped->getID(), + 'repeat_of' => $notice->getID(), + 'verb' => ActivityVerb::SHARE)); + $status['repeated_id'] = $repeated->getID(); } - - return true; } - public function onStartShowThreadedNoticeTailItems(NoticeListItem $nli, Notice $notice, &$threadActive) + public function onTwitterUserArray(Profile $profile, array &$userdata, Profile $scoped=null, array $args=array()) { - if ($nli instanceof ThreadedNoticeListSubItem) { - // The sub-items are replies to a conversation, thus we use different HTML elements etc. - $item = new ThreadedNoticeListInlineFavesItem($notice, $nli->out); - } else { - $item = new ThreadedNoticeListFavesItem($notice, $nli->out); - } - $threadActive = $item->show() || $threadActive; - return true; + $userdata['favourites_count'] = Fave::countByProfile($profile); } - public function onEndFavorNotice(Profile $actor, Notice $target) - { - try { - $notice_author = $target->getProfile(); - // Don't notify ourselves of our own favorite on our own notice, - // or if it's a remote user (since we don't know their email addresses etc.) - if ($notice_author->id == $actor->id || !$notice_author->isLocal()) { - return true; - } - $local_user = $notice_author->getUser(); - mail_notify_fave($local_user, $actor, $target); - } catch (Exception $e) { - // Mm'kay, probably not a local user. Let's skip this favor notification. - } - } + // Command stuff /** - * EndInterpretCommand for FavoritePlugin will handle the 'fav' command - * using the class FavCommand. + * EndInterpretCommand for RepeatPlugin will handle the 'repeat' command + * using the class RepeatCommand. * * @param string $cmd Command being run * @param string $arg Rest of the message (including address) @@ -300,7 +306,7 @@ class SharePlugin extends ActivityVerbHandlerPlugin */ public function onStartInterpretCommand($cmd, $arg, $user, &$result) { - if ($result === false && $cmd == 'fav') { + if ($result === false && in_array($cmd, array('repeat', 'rp', 'rt', 'rd'))) { if (empty($arg)) { $result = null; } else { @@ -308,7 +314,7 @@ class SharePlugin extends ActivityVerbHandlerPlugin if (!empty($extra)) { $result = null; } else { - $result = new FavCommand($user, $other); + $result = new RepeatCommand($user, $other); } } return false; @@ -318,10 +324,10 @@ class SharePlugin extends ActivityVerbHandlerPlugin public function onHelpCommandMessages(HelpCommand $help, array &$commands) { - // TRANS: Help message for IM/SMS command "fav ". - $commands['fav '] = _m('COMMANDHELP', "add user's last notice as a 'fave'"); - // TRANS: Help message for IM/SMS command "fav #". - $commands['fav #'] = _m('COMMANDHELP', "add notice with the given id as a 'fave'"); + // TRANS: Help message for IM/SMS command "repeat #". + $commands['repeat #'] = _m('COMMANDHELP', "repeat a notice with a given id"); + // TRANS: Help message for IM/SMS command "repeat ". + $commands['repeat '] = _m('COMMANDHELP', "repeat the last notice from user"); } /** @@ -329,211 +335,39 @@ class SharePlugin extends ActivityVerbHandlerPlugin */ public function onCommandSupportedAPI(Command $cmd, &$supported) { - $supported = $supported || $cmd instanceof FavCommand; - } - - // Form stuff (settings etc.) - - public function onEndEmailFormData(Action $action, Profile $scoped) - { - $emailfave = $scoped->getConfigPref('email', 'notify_fave') ? 1 : 0; - - $action->elementStart('li'); - $action->checkbox('email-notify_fave', - // TRANS: Checkbox label in e-mail preferences form. - _('Send me email when someone adds my notice as a favorite.'), - $emailfave); - $action->elementEnd('li'); - - return true; - } - - public function onStartEmailSaveForm(Action $action, Profile $scoped) - { - $emailfave = $action->booleanintstring('email-notify_fave'); - try { - if ($emailfave == $scoped->getPref('email', 'notify_fave')) { - // No need to update setting - return true; - } - } catch (NoResultException $e) { - // Apparently there's no previously stored setting, then continue to save it as it is now. - } - - $scoped->setPref('email', 'notify_fave', $emailfave); - - return true; - } - - // Layout stuff - - public function onEndPersonalGroupNav(Menu $menu, Profile $target, Profile $scoped=null) - { - $menu->out->menuItem(common_local_url('showfavorites', array('nickname' => $target->getNickname())), - // TRANS: Menu item in personal group navigation menu. - _m('MENU','Favorites'), - // @todo i18n FIXME: Need to make this two messages. - // TRANS: Menu item title in personal group navigation menu. - // TRANS: %s is a username. - sprintf(_('%s\'s favorite notices'), $target->getBestName()), - $scoped instanceof Profile && $target->id === $scoped->id && $menu->actionName =='showfavorites', - 'nav_timeline_favorites'); - } - - public function onEndPublicGroupNav(Menu $menu) - { - if (!common_config('singleuser', 'enabled')) { - // TRANS: Menu item in search group navigation panel. - $menu->out->menuItem(common_local_url('favorited'), _m('MENU','Popular'), - // TRANS: Menu item title in search group navigation panel. - _('Popular notices'), $menu->actionName == 'favorited', 'nav_timeline_favorited'); - } - } - - public function onEndShowSections(Action $action) - { - if (!$action->isAction(array('all', 'public'))) { - return true; - } - - if (!common_config('performance', 'high')) { - $section = new PopularNoticeSection($action, $action->getScoped()); - $section->show(); - } + $supported = $supported || $cmd instanceof RepeatCommand; } 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'); + // return page title } 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; + // prepare Action? } 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; + // handle repeat POST } protected function getActivityForm(ManagedAction $action, $verb, Notice $target, Profile $scoped) { - return Fave::existsForProfile($target, $scoped) - ? new DisfavorForm($action, $target) - : new FavorForm($action, $target); + return new RepeatForm($action, $target); } public function onPluginVersion(array &$versions) { - $versions[] = array('name' => 'Favorite', + $versions[] = array('name' => 'Share verb', 'version' => GNUSOCIAL_VERSION, 'author' => 'Mikael Nordfeldth', - 'homepage' => 'http://gnu.io/', + 'homepage' => 'https://gnu.io/', 'rawdescription' => // TRANS: Plugin description. - _m('Favorites (likes) using ActivityStreams.')); + _m('Shares (repeats) using ActivityStreams.')); return true; } } - -/** - * Notify a user that one of their notices has been chosen as a 'fave' - * - * @param User $rcpt The user whose notice was faved - * @param Profile $sender The user who faved the notice - * @param Notice $notice The notice that was faved - * - * @return void - */ -function mail_notify_fave(User $rcpt, Profile $sender, Notice $notice) -{ - if (!$rcpt->receivesEmailNotifications() || !$rcpt->getConfigPref('email', 'notify_fave')) { - return; - } - - // This test is actually "if the sender is sandboxed" - if (!$sender->hasRight(Right::EMAILONFAVE)) { - return; - } - - if ($rcpt->hasBlocked($sender)) { - // If the author has blocked us, don't spam them with a notification. - return; - } - - // We need the global mail.php for various mail related functions below. - require_once INSTALLDIR.'/lib/mail.php'; - - $bestname = $sender->getBestName(); - - common_switch_locale($rcpt->language); - - // TRANS: Subject for favorite notification e-mail. - // TRANS: %1$s is the adding user's long name, %2$s is the adding user's nickname. - $subject = sprintf(_('%1$s (@%2$s) added your notice as a favorite'), $bestname, $sender->getNickname()); - - // TRANS: Body for favorite notification e-mail. - // TRANS: %1$s is the adding user's long name, $2$s is the date the notice was created, - // TRANS: %3$s is a URL to the faved notice, %4$s is the faved notice text, - // TRANS: %5$s is a URL to all faves of the adding user, %6$s is the StatusNet sitename, - // TRANS: %7$s is the adding user's nickname. - $body = sprintf(_("%1\$s (@%7\$s) just added your notice from %2\$s". - " as one of their favorites.\n\n" . - "The URL of your notice is:\n\n" . - "%3\$s\n\n" . - "The text of your notice is:\n\n" . - "%4\$s\n\n" . - "You can see the list of %1\$s's favorites here:\n\n" . - "%5\$s"), - $bestname, - common_exact_date($notice->created), - common_local_url('shownotice', - array('notice' => $notice->id)), - $notice->content, - common_local_url('showfavorites', - array('nickname' => $sender->getNickname())), - common_config('site', 'name'), - $sender->getNickname()) . - mail_footer_block(); - - $headers = _mail_prepare_headers('fave', $rcpt->getNickname(), $sender->getNickname()); - - common_switch_locale(); - mail_to_user($rcpt, $subject, $body, $headers); -}