X-Git-Url: https://git.mxchange.org/?a=blobdiff_plain;f=plugins%2FBookmark%2FBookmarkPlugin.php;h=6dacdb085e88a167492d50f64893d07ca1b31950;hb=de55d8f83bb2ecf9461510768fe7147aec592055;hp=339b03e0561ba988317063a602ea841f97bbc04d;hpb=753bc7302a608e089d87abbc19535b845b226992;p=quix0rs-gnu-social.git diff --git a/plugins/Bookmark/BookmarkPlugin.php b/plugins/Bookmark/BookmarkPlugin.php index 339b03e056..6dacdb085e 100644 --- a/plugins/Bookmark/BookmarkPlugin.php +++ b/plugins/Bookmark/BookmarkPlugin.php @@ -43,8 +43,7 @@ if (!defined('STATUSNET')) { * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0 * @link http://status.net/ */ - -class BookmarkPlugin extends Plugin +class BookmarkPlugin extends MicroAppPlugin { const VERSION = '0.1'; const IMPORTDELICIOUS = 'BookmarkPlugin:IMPORTDELICIOUS'; @@ -60,7 +59,6 @@ class BookmarkPlugin extends Plugin * * @return boolean hook value */ - function onUserRightsCheck($profile, $right, &$result) { if ($right == self::IMPORTDELICIOUS) { @@ -78,7 +76,6 @@ class BookmarkPlugin extends Plugin * * @return boolean hook value; true means continue processing, false means stop. */ - function onCheckSchema() { $schema = Schema::get(); @@ -120,25 +117,6 @@ class BookmarkPlugin extends Plugin return true; } - /** - * When a notice is deleted, delete the related Bookmark - * - * @param Notice $notice Notice being deleted - * - * @return boolean hook value - */ - - function onNoticeDeleteRelated($notice) - { - $nb = Bookmark::getByNotice($notice); - - if (!empty($nb)) { - $nb->delete(); - } - - return true; - } - /** * Show the CSS necessary for this plugin * @@ -146,45 +124,16 @@ class BookmarkPlugin extends Plugin * * @return boolean hook value */ - function onEndShowStyles($action) { $action->cssLink($this->path('bookmark.css')); return true; } - /** - * Load related modules when needed - * - * @param string $cls Name of the class to be loaded - * - * @return boolean hook value; true means continue processing, false means stop. - */ - - function onAutoload($cls) + function onEndShowScripts($action) { - $dir = dirname(__FILE__); - - switch ($cls) - { - case 'ShowbookmarkAction': - case 'NewbookmarkAction': - case 'BookmarkpopupAction': - case 'NoticebyurlAction': - case 'ImportdeliciousAction': - include_once $dir . '/' . strtolower(mb_substr($cls, 0, -6)) . '.php'; - return false; - case 'Bookmark': - include_once $dir.'/'.$cls.'.php'; - return false; - case 'BookmarkForm': - case 'DeliciousBackupImporter': - case 'DeliciousBookmarkImporter': - include_once $dir.'/'.strtolower($cls).'.php'; - return false; - default: - return true; - } + $action->script($this->path('js/bookmark.js')); + return true; } /** @@ -194,9 +143,28 @@ class BookmarkPlugin extends Plugin * * @return boolean hook value; true means continue processing, false means stop. */ - function onRouterInitialized($m) { + if (common_config('singleuser', 'enabled')) { + $nickname = User::singleUserNickname(); + $m->connect('bookmarks', + array('action' => 'bookmarks', 'nickname' => $nickname)); + $m->connect('bookmarks/rss', + array('action' => 'bookmarksrss', 'nickname' => $nickname)); + } else { + $m->connect(':nickname/bookmarks', + array('action' => 'bookmarks'), + array('nickname' => Nickname::DISPLAY_FMT)); + $m->connect(':nickname/bookmarks/rss', + array('action' => 'bookmarksrss'), + array('nickname' => Nickname::DISPLAY_FMT)); + } + + $m->connect('api/bookmarks/:id.:format', + array('action' => 'ApiTimelineBookmarks', + 'id' => Nickname::INPUT_FMT, + 'format' => '(xml|json|rss|atom|as)')); + $m->connect('main/bookmark/new', array('action' => 'newbookmark'), array('id' => '[0-9]+')); @@ -207,6 +175,9 @@ class BookmarkPlugin extends Plugin $m->connect('main/bookmark/import', array('action' => 'importdelicious')); + $m->connect('main/bookmark/forurl', + array('action' => 'bookmarkforurl')); + $m->connect('bookmark/:id', array('action' => 'showbookmark'), array('id' => '[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}')); @@ -218,221 +189,14 @@ class BookmarkPlugin extends Plugin return true; } - /** - * Output the HTML for a bookmark in a list - * - * @param NoticeListItem $nli The list item being shown. - * - * @return boolean hook value - */ - - function onStartShowNoticeItem($nli) - { - $nb = Bookmark::getByNotice($nli->notice); - - if (!empty($nb)) { - - $out = $nli->out; - $notice = $nli->notice; - $profile = $nli->profile; - - $atts = $notice->attachments(); - - if (count($atts) < 1) { - // Something wrong; let default code deal with it. - return true; - } - - $att = $atts[0]; - - // XXX: only show the bookmark URL for non-single-page stuff - - if ($out instanceof ShowbookmarkAction) { - } else { - $out->elementStart('h3'); - $out->element('a', - array('href' => $att->url, - 'class' => 'bookmark-title entry-title'), - $nb->title); - $out->elementEnd('h3'); - - $countUrl = common_local_url('noticebyurl', - array('id' => $att->id)); - - $out->element('a', array('class' => 'bookmark-notice-count', - 'href' => $countUrl), - $att->noticeCount()); - } - - // Replies look like "for:" tags - - $replies = $nli->notice->getReplies(); - $tags = $nli->notice->getTags(); - - if (!empty($replies) || !empty($tags)) { - - $out->elementStart('ul', array('class' => 'bookmark-tags')); - - foreach ($replies as $reply) { - $other = Profile::staticGet('id', $reply); - $out->elementStart('li'); - $out->element('a', array('rel' => 'tag', - 'href' => $other->profileurl, - 'title' => $other->getBestName()), - sprintf('for:%s', $other->nickname)); - $out->elementEnd('li'); - $out->text(' '); - } - - foreach ($tags as $tag) { - $out->elementStart('li'); - $out->element('a', - array('rel' => 'tag', - 'href' => Notice_tag::url($tag)), - $tag); - $out->elementEnd('li'); - $out->text(' '); - } - - $out->elementEnd('ul'); - } - - if (!empty($nb->description)) { - $out->element('p', - array('class' => 'bookmark-description'), - $nb->description); - } - - if (common_config('attachments', 'show_thumbs')) { - $haveThumbs = false; - foreach ($atts as $check) { - $thumbnail = File_thumbnail::staticGet('file_id', $check->id); - if (!empty($thumbnail)) { - $haveThumbs = true; - break; - } - } - if ($haveThumbs) { - $al = new InlineAttachmentList($notice, $out); - $al->show(); - } - } - - $out->elementStart('div', array('class' => 'bookmark-info entry-content')); - - $avatar = $profile->getAvatar(AVATAR_MINI_SIZE); - - $out->element('img', - array('src' => ($avatar) ? - $avatar->displayUrl() : - Avatar::defaultImage(AVATAR_MINI_SIZE), - 'class' => 'avatar photo bookmark-avatar', - 'width' => AVATAR_MINI_SIZE, - 'height' => AVATAR_MINI_SIZE, - 'alt' => $profile->getBestName())); - - $out->raw(' '); - - $out->element('a', - array('href' => $profile->profileurl, - 'title' => $profile->getBestName()), - $profile->nickname); - - $nli->showNoticeLink(); - $nli->showNoticeSource(); - $nli->showNoticeLocation(); - $nli->showContext(); - $nli->showRepeat(); - - $out->elementEnd('div'); - - $nli->showNoticeOptions(); - - return false; - } - return true; - } - - /** - * Render a notice as a Bookmark object - * - * @param Notice $notice Notice to render - * @param ActivityObject &$object Empty object to fill - * - * @return boolean hook value - */ - - function onStartActivityObjectFromNotice($notice, &$object) - { - common_log(LOG_INFO, - "Checking {$notice->uri} to see if it's a bookmark."); - - $nb = Bookmark::getByNotice($notice); - - if (!empty($nb)) { - - common_log(LOG_INFO, - "Formatting notice {$notice->uri} as a bookmark."); - - $object->id = $notice->uri; - $object->type = ActivityObject::BOOKMARK; - $object->title = $nb->title; - $object->summary = $nb->description; - $object->link = $notice->bestUrl(); - - // Attributes of the URL - - $attachments = $notice->attachments(); - - if (count($attachments) != 1) { - throw new ServerException(_('Bookmark notice with the '. - 'wrong number of attachments.')); - } - - $target = $attachments[0]; - - $attrs = array('rel' => 'related', - 'href' => $target->url); - - if (!empty($target->title)) { - $attrs['title'] = $target->title; - } - - $object->extra[] = array('link', $attrs, null); - - // Attributes of the thumbnail, if any - - $thumbnail = $target->getThumbnail(); - - if (!empty($thumbnail)) { - $tattrs = array('rel' => 'preview', - 'href' => $thumbnail->url); - - if (!empty($thumbnail->width)) { - $tattrs['media:width'] = $thumbnail->width; - } - - if (!empty($thumbnail->height)) { - $tattrs['media:height'] = $thumbnail->height; - } - - $object->extra[] = array('link', $attrs, null); - } - - return false; - } - - return true; - } /** * Add our two queue handlers to the queue manager * * @param QueueManager $qm current queue manager - * + * * @return boolean hook value */ - function onEndInitializeQueueManager($qm) { $qm->connect('dlcsback', 'DeliciousBackupImporter'); @@ -444,18 +208,20 @@ class BookmarkPlugin extends Plugin * Plugin version data * * @param array &$versions array of version data - * + * * @return value */ - function onPluginVersion(&$versions) { $versions[] = array('name' => 'Bookmark', 'version' => self::VERSION, - 'author' => 'Evan Prodromou', + 'author' => 'Evan Prodromou, Stephane Berube, Jean Baptiste Favre', 'homepage' => 'http://status.net/wiki/Plugin:Bookmark', - 'rawdescription' => - _m('Simple extension for supporting bookmarks.')); + 'description' => + // TRANS: Plugin description. + _m('Simple extension for supporting bookmarks. ') . + 'BookmarkList feature has been developped by Stephane Berube. ' . + 'Integration has been done by Jean Baptiste Favre.'); return true; } @@ -467,7 +233,6 @@ class BookmarkPlugin extends Plugin * * @return boolean hook value */ - function onStartLoadDoc(&$title, &$output) { if ($title == 'bookmarklet') { @@ -482,235 +247,174 @@ class BookmarkPlugin extends Plugin } /** - * Handle a posted bookmark from PuSH + * Show a link to our delicious import page on profile settings form * - * @param Activity $activity activity to handle - * @param Ostatus_profile $oprofile Profile for the feed + * @param Action $action Profile settings action being shown * * @return boolean hook value */ - - function onStartHandleFeedEntryWithProfile($activity, $oprofile) + function onEndProfileSettingsActions($action) { - common_log(LOG_INFO, "BookmarkPlugin called for new feed entry."); - - if (self::_isPostBookmark($activity)) { - - common_log(LOG_INFO, - "Importing activity {$activity->id} as a bookmark."); - - $author = $oprofile->checkAuthorship($activity); - - if (empty($author)) { - throw new ClientException(_('Can\'t get author for activity.')); - } - - self::_postRemoteBookmark($author, - $activity); + $user = common_current_user(); - return false; + if (!empty($user) && $user->hasRight(self::IMPORTDELICIOUS)) { + $action->elementStart('li'); + $action->element('a', + array('href' => common_local_url('importdelicious')), + // TRANS: Link text in proile leading to import form. + _m('Import del.icio.us bookmarks')); + $action->elementEnd('li'); } return true; } /** - * Handle a posted bookmark from Salmon + * Output our CSS class for bookmark notice list elements * - * @param Activity $activity activity to handle - * @param mixed $target user or group targeted + * @param NoticeListItem $nli The item being shown * * @return boolean hook value */ - function onStartHandleSalmonTarget($activity, $target) + function onStartOpenNoticeListItemElement($nli) { - if (self::_isPostBookmark($activity)) { - - $this->log(LOG_INFO, "Checking {$activity->id} as a valid Salmon slap."); - - if ($target instanceof User_group) { - $uri = $target->getUri(); - if (!in_array($uri, $activity->context->attention)) { - throw new ClientException(_("Bookmark not posted ". - "to this group.")); - } - } else if ($target instanceof User) { - $uri = $target->uri; - $original = null; - if (!empty($activity->context->replyToID)) { - $original = Notice::staticGet('uri', - $activity->context->replyToID); - } - if (!in_array($uri, $activity->context->attention) && - (empty($original) || - $original->profile_id != $target->id)) { - throw new ClientException(_("Bookmark not posted ". - "to this user.")); - } - } else { - throw new ServerException(_("Don't know how to handle ". - "this kind of target.")); - } - - $author = Ostatus_profile::ensureActivityObjectProfile($activity->actor); - - self::_postRemoteBookmark($author, - $activity); - - return false; + if (!$this->isMyNotice($nli->notice)) { + return true; + } + + $nb = Bookmark::getByNotice($nli->notice); + + if (empty($nb)) { + $this->log(LOG_INFO, "Notice {$nli->notice->id} has bookmark class but no matching Bookmark record."); + return true; } - - return true; + + $id = (empty($nli->repeat)) ? $nli->notice->id : $nli->repeat->id; + $class = 'hentry notice bookmark'; + if ($nli->notice->scope != 0 && $nli->notice->scope != 1) { + $class .= ' limited-scope'; + } + $nli->out->elementStart('li', array('class' => $class, + 'id' => 'notice-' . $id)); + + Event::handle('EndOpenNoticeListItemElement', array($nli)); + return false; } /** - * Handle bookmark posted via AtomPub + * Modify the default menu to link to our custom action * - * @param Activity &$activity Activity that was posted - * @param User $user User that posted it - * @param Notice &$notice Resulting notice + * Using event handlers, it's possible to modify the default UI for pages + * almost without limit. In this method, we add a menu item to the default + * primary menu for the interface to link to our action. * - * @return boolean hook value - */ - - function onStartAtomPubNewActivity(&$activity, $user, &$notice) - { - if (self::_isPostBookmark($activity)) { - $options = array('source' => 'atompub'); - $notice = self::_postBookmark($user->getProfile(), - $activity, - $options); - return false; - } - - return true; - } - - /** - * Handle bookmark imported from a backup file + * The Action class provides a rich set of events to hook, as well as output + * methods. * - * @param User $user User to import for - * @param ActivityObject $author Original author per import file - * @param Activity $activity Activity to import - * @param boolean $trusted Is this a trusted user? - * @param boolean &$done Is this done (success or unrecoverable error) + * @param Action $action The current action handler. Use this to + * do any output. * - * @return boolean hook value + * @return boolean hook value; true means continue processing, false means stop. + * + * @see Action */ - - function onStartImportActivity($user, $author, $activity, $trusted, &$done) + function onEndPersonalGroupNav($action) { - if (self::_isPostBookmark($activity)) { - - $bookmark = $activity->objects[0]; - - $this->log(LOG_INFO, - 'Importing Bookmark ' . $bookmark->id . - ' for user ' . $user->nickname); - - $options = array('uri' => $bookmark->id, - 'url' => $bookmark->link, - 'source' => 'restore'); - - $saved = self::_postBookmark($user->getProfile(), $activity, $options); - - if (!empty($saved)) { - $done = true; - } + $this->user = common_current_user(); + if (!$this->user) { + // TRANS: Client error displayed when trying to display bookmarks for a non-existing user. + $this->clientError(_('No such user.')); return false; } + $action->menuItem(common_local_url('bookmarks', array('nickname' => $this->user->nickname)), + // TRANS: Menu item in sample plugin. + _m('Bookmarks'), + // TRANS: Menu item title in sample plugin. + _m('A list of your bookmarks'), false, 'nav_timeline_bookmarks'); return true; } /** - * Show a link to our delicious import page on profile settings form + * Save a remote bookmark (from Salmon or PuSH) * - * @param Action $action Profile settings action being shown + * @param Ostatus_profile $author Author of the bookmark + * @param Activity $activity Activity to save * - * @return boolean hook value + * @return Notice resulting notice. */ - - function onEndProfileSettingsActions($action) + static private function _postRemoteBookmark(Ostatus_profile $author, + Activity $activity) { - $user = common_current_user(); - - if (!empty($user) && $user->hasRight(self::IMPORTDELICIOUS)) { - $action->elementStart('li'); - $action->element('a', - array('href' => common_local_url('importdelicious')), - _('Import del.icio.us bookmarks')); - $action->elementEnd('li'); - } + $bookmark = $activity->objects[0]; - return true; + $options = array('uri' => $bookmark->id, + 'url' => $bookmark->link, + 'is_local' => Notice::REMOTE, + 'source' => 'ostatus'); + + return self::_postBookmark($author->localProfile(), $activity, $options); } /** - * Output our CSS class for bookmark notice list elements + * Test if an activity represents posting a bookmark * - * @param NoticeListItem $nli The item being shown + * @param Activity $activity Activity to test * - * @return boolean hook value + * @return true if it's a Post of a Bookmark, else false */ + static private function _isPostBookmark($activity) + { + return ($activity->verb == ActivityVerb::POST && + $activity->objects[0]->type == ActivityObject::BOOKMARK); + } - function onStartOpenNoticeListItemElement($nli) + function types() { - $nb = Bookmark::getByNotice($nli->notice); - if (!empty($nb)) { - $id = (empty($nli->repeat)) ? $nli->notice->id : $nli->repeat->id; - $nli->out->elementStart('li', array('class' => 'hentry notice bookmark', - 'id' => 'notice-' . $id)); - Event::handle('EndOpenNoticeListItemElement', array($nli)); - return false; - } - return true; + return array(ActivityObject::BOOKMARK); } /** - * Save a remote bookmark (from Salmon or PuSH) + * When a notice is deleted, delete the related Bookmark * - * @param Ostatus_profile $author Author of the bookmark - * @param Activity $activity Activity to save + * @param Notice $notice Notice being deleted * - * @return Notice resulting notice. + * @return boolean hook value */ - - static private function _postRemoteBookmark(Ostatus_profile $author, - Activity $activity) + function deleteRelated($notice) { - $bookmark = $activity->objects[0]; - - $options = array('uri' => $bookmark->id, - 'url' => $bookmark->link, - 'is_local' => Notice::REMOTE_OMB, - 'source' => 'ostatus'); - - return self::_postBookmark($author->localProfile(), $activity, $options); + if ($this->isMyNotice($notice)) { + + $nb = Bookmark::getByNotice($notice); + + if (!empty($nb)) { + $nb->delete(); + } + } + + return true; } /** * Save a bookmark from an activity * - * @param Profile $profile Profile to use as author * @param Activity $activity Activity to save + * @param Profile $profile Profile to use as author * @param array $options Options to pass to bookmark-saving code * * @return Notice resulting notice */ - - static private function _postBookmark(Profile $profile, - Activity $activity, - $options=array()) + function saveNoticeFromActivity($activity, $profile, $options=array()) { $bookmark = $activity->objects[0]; $relLinkEls = ActivityUtils::getLinks($bookmark->element, 'related'); if (count($relLinkEls) < 1) { - throw new ClientException(_('Expected exactly 1 link '. + // TRANS: Client exception thrown when a bookmark is formatted incorrectly. + throw new ClientException(_m('Expected exactly 1 link '. 'rel=related in a Bookmark.')); } @@ -756,7 +460,7 @@ class BookmarkPlugin extends Plugin if (!empty($other)) { $options['replies'][] = $replyURI; } else { - $group = User_group::staticGet('uri', $replyURI); + $group = User_group::getKV('uri', $replyURI); if (!empty($group)) { $options['groups'][] = $replyURI; } @@ -767,7 +471,7 @@ class BookmarkPlugin extends Plugin // @fixme what about conversation ID? if (!empty($activity->context->replyToID)) { - $orig = Notice::staticGet('uri', + $orig = Notice::getKV('uri', $activity->context->replyToID); if (!empty($orig)) { $options['reply_to'] = $orig->id; @@ -782,17 +486,128 @@ class BookmarkPlugin extends Plugin $options); } + function activityObjectFromNotice($notice) + { + assert($this->isMyNotice($notice)); + + common_log(LOG_INFO, + "Formatting notice {$notice->uri} as a bookmark."); + + $object = new ActivityObject(); + $nb = Bookmark::getByNotice($notice); + + $object->id = $notice->uri; + $object->type = ActivityObject::BOOKMARK; + $object->title = $nb->title; + $object->summary = $nb->description; + $object->link = $notice->bestUrl(); + + // Attributes of the URL + + $attachments = $notice->attachments(); + + if (count($attachments) != 1) { + // TRANS: Server exception thrown when a bookmark has multiple attachments. + throw new ServerException(_m('Bookmark notice with the '. + 'wrong number of attachments.')); + } + + $target = $attachments[0]; + + $attrs = array('rel' => 'related', + 'href' => $target->url); + + if (!empty($target->title)) { + $attrs['title'] = $target->title; + } + + $object->extra[] = array('link', $attrs, null); + + // Attributes of the thumbnail, if any + + $thumbnail = $target->getThumbnail(); + + if (!empty($thumbnail)) { + $tattrs = array('rel' => 'preview', + 'href' => $thumbnail->url); + + if (!empty($thumbnail->width)) { + $tattrs['media:width'] = $thumbnail->width; + } + + if (!empty($thumbnail->height)) { + $tattrs['media:height'] = $thumbnail->height; + } + + $object->extra[] = array('link', $attrs, null); + } + + return $object; + } + /** - * Test if an activity represents posting a bookmark + * Given a notice list item, returns an adapter specific + * to this plugin. * - * @param Activity $activity Activity to test + * @param NoticeListItem $nli item to adapt * - * @return true if it's a Post of a Bookmark, else false + * @return NoticeListItemAdapter adapter or null */ + function adaptNoticeListItem($nli) + { + return new BookmarkListItem($nli); + } - static private function _isPostBookmark($activity) + function entryForm($out) { - return ($activity->verb == ActivityVerb::POST && - $activity->objects[0]->type == ActivityObject::BOOKMARK); + return new InitialBookmarkForm($out); + } + + function tag() + { + return 'bookmark'; + } + + function appTitle() + { + // TRANS: Application title. + return _m('TITLE','Bookmark'); + } + + function onEndUpgrade() + { + // Version 0.9.x of the plugin didn't stamp notices + // with verb and object-type (for obvious reasons). Update + // those notices here. + + $notice = new Notice(); + + $notice->whereAdd('exists (select uri from bookmark where bookmark.uri = notice.uri)'); + $notice->whereAdd('((object_type is null) or (object_type = "' .ActivityObject::NOTE.'"))'); + + $notice->find(); + + while ($notice->fetch()) { + $original = clone($notice); + $notice->verb = ActivityVerb::POST; + $notice->object_type = ActivityObject::BOOKMARK; + $notice->update($original); + } + } + + public function activityObjectOutputJson(ActivityObject $obj, array &$out) + { + assert($obj->type == ActivityObject::BOOKMARK); + + $bm = Bookmark::getKV('uri', $obj->id); + + if (empty($bm)) { + throw new ServerException("Unknown bookmark: " . $obj->id); + } + + $out['displayName'] = $bm->title; + $out['targetUrl'] = $bm->url; + + return true; } }