X-Git-Url: https://git.mxchange.org/?a=blobdiff_plain;f=plugins%2FBookmark%2FBookmarkPlugin.php;h=db2a32f39228824be11e687acacd8e447cc5ba07;hb=524b98bfa3fd340ade6ce8b0e7f23ce773db5d92;hp=01dd6f1eaae87a60c658ccf7e60a417b4af7a22a;hpb=4048d1ec3dfe68be8d8e31b859b2f219adc34397;p=quix0rs-gnu-social.git diff --git a/plugins/Bookmark/BookmarkPlugin.php b/plugins/Bookmark/BookmarkPlugin.php index 01dd6f1eaa..db2a32f392 100644 --- a/plugins/Bookmark/BookmarkPlugin.php +++ b/plugins/Bookmark/BookmarkPlugin.php @@ -43,10 +43,30 @@ 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 VERSION = '0.1'; + const IMPORTDELICIOUS = 'BookmarkPlugin:IMPORTDELICIOUS'; + + /** + * Authorization for importing delicious bookmarks + * + * By default, everyone can import bookmarks except silenced people. + * + * @param Profile $profile Person whose rights to check + * @param string $right Right to check; const value + * @param boolean &$result Result of the check, writeable + * + * @return boolean hook value + */ + function onUserRightsCheck($profile, $right, &$result) + { + if ($right == self::IMPORTDELICIOUS) { + $result = !$profile->isSilenced(); + return false; + } + return true; + } /** * Database schema setup @@ -56,7 +76,6 @@ class BookmarkPlugin extends Plugin * * @return boolean hook value; true means continue processing, false means stop. */ - function onCheckSchema() { $schema = Schema::get(); @@ -64,16 +83,21 @@ class BookmarkPlugin extends Plugin // For storing user-submitted flags on profiles $schema->ensureTable('bookmark', - array(new ColumnDef('profile_id', + array(new ColumnDef('id', + 'char', + 36, + false, + 'PRI'), + new ColumnDef('profile_id', 'integer', null, false, - 'PRI'), + 'MUL'), new ColumnDef('url', 'varchar', 255, false, - 'PRI'), + 'MUL'), new ColumnDef('title', 'varchar', 255), @@ -84,45 +108,12 @@ class BookmarkPlugin extends Plugin 255, false, 'UNI'), - new ColumnDef('url_crc32', - 'integer', - null, - false, - 'MUL'), new ColumnDef('created', 'datetime', null, false, 'MUL'))); - try { - $schema->createIndex('bookmark', - array('profile_id', - 'url_crc32'), - 'bookmark_profile_url_idx'); - } catch (Exception $e) { - common_log(LOG_ERR, $e->getMessage()); - } - - 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; } @@ -133,10 +124,9 @@ class BookmarkPlugin extends Plugin * * @return boolean hook value */ - function onEndShowStyles($action) { - $action->cssLink('plugins/Bookmark/bookmark.css'); + $action->cssLink($this->path('bookmark.css')); return true; } @@ -147,15 +137,17 @@ class BookmarkPlugin extends Plugin * * @return boolean hook value; true means continue processing, false means stop. */ - function onAutoload($cls) { $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': @@ -178,207 +170,37 @@ class BookmarkPlugin extends Plugin * * @return boolean hook value; true means continue processing, false means stop. */ - function onRouterInitialized($m) { $m->connect('main/bookmark/new', array('action' => 'newbookmark'), array('id' => '[0-9]+')); - $m->connect('main/bookmark/popup', array('action' => 'bookmarkpopup')); - - $m->connect('bookmark/:user/:created/:crc32', - array('action' => 'showbookmark'), - array('user' => '[0-9]+', - 'created' => '[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}Z', - 'crc32' => '[0-9A-F]{8}')); - - return true; - } - - /** - * Output the HTML for a bookmark in a list - * - * @param NoticeListItem $nli The list item being shown. - * - * @return boolean hook value - */ + $m->connect('main/bookmark/popup', + array('action' => 'bookmarkpopup')); - function onStartShowNoticeItem($nli) - { - $nb = Bookmark::getByNotice($nli->notice); + $m->connect('main/bookmark/import', + array('action' => 'importdelicious')); - 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]; - - $out->elementStart('h3'); - $out->element('a', - array('href' => $att->url), - $nb->title); - $out->elementEnd('h3'); - - $out->elementStart('ul', array('class' => 'bookmark_tags')); - - // Replies look like "for:" tags - - $replies = $nli->notice->getReplies(); - - if (!empty($replies)) { - 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(' '); - } - } - - $tags = $nli->notice->getTags(); - - 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'); - - $out->element('p', - array('class' => 'bookmark_description'), - $nb->description); - - $nli->showNoticeAttachments(); - - $out->elementStart('p', array('style' => 'float: left')); - - $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('p'); + $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}')); - $nli->showNoticeOptions(); + $m->connect('notice/by-url/:id', + array('action' => 'noticebyurl'), + array('id' => '[0-9]+')); - 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); - - // 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); - } - - 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'); @@ -390,10 +212,9 @@ class BookmarkPlugin extends Plugin * Plugin version data * * @param array &$versions array of version data - * + * * @return value */ - function onPluginVersion(&$versions) { $versions[] = array('name' => 'Sample', @@ -401,6 +222,7 @@ class BookmarkPlugin extends Plugin 'author' => 'Evan Prodromou', 'homepage' => 'http://status.net/wiki/Plugin:Bookmark', 'rawdescription' => + // TRANS: Plugin description. _m('Simple extension for supporting bookmarks.')); return true; } @@ -413,7 +235,6 @@ class BookmarkPlugin extends Plugin * * @return boolean hook value */ - function onStartLoadDoc(&$title, &$output) { if ($title == 'bookmarklet') { @@ -428,50 +249,134 @@ 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 onEndProfileSettingsActions($action) + { + $user = common_current_user(); + + 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'); + } - function onStartHandleFeedEntryWithProfile($activity, $oprofile) { + return true; + } - common_log(LOG_INFO, "BookmarkPlugin called for new feed entry."); + /** + * Output our CSS class for bookmark notice list elements + * + * @param NoticeListItem $nli The item being shown + * + * @return boolean hook value + */ - if ($activity->verb == ActivityVerb::POST && - $activity->objects[0]->type == ActivityObject::BOOKMARK) { + function onStartOpenNoticeListItemElement($nli) + { + $nb = Bookmark::getByNotice($nli->notice); + if (!empty($nb)) { + $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; + } + return true; + } - common_log(LOG_INFO, "Importing activity {$activity->id} as a bookmark."); + /** + * Save a remote bookmark (from Salmon or PuSH) + * + * @param Ostatus_profile $author Author of the bookmark + * @param Activity $activity Activity to save + * + * @return Notice resulting notice. + */ + static private function _postRemoteBookmark(Ostatus_profile $author, + Activity $activity) + { + $bookmark = $activity->objects[0]; - $author = $oprofile->checkAuthorship($activity); + $options = array('uri' => $bookmark->id, + 'url' => $bookmark->link, + 'is_local' => Notice::REMOTE_OMB, + 'source' => 'ostatus'); - if (empty($author)) { - throw new ClientException(_('Can\'t get author for activity.')); - } + return self::_postBookmark($author->localProfile(), $activity, $options); + } - self::_postRemoteBookmark($author, - $activity); + /** + * Test if an activity represents posting a bookmark + * + * @param Activity $activity Activity to test + * + * @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); + } - return false; + function types() + { + return array(ActivityObject::BOOKMARK); + } + + /** + * When a notice is deleted, delete the related Bookmark + * + * @param Notice $notice Notice being deleted + * + * @return boolean hook value + */ + function deleteRelated($notice) + { + $nb = Bookmark::getByNotice($notice); + + if (!empty($nb)) { + $nb->delete(); } return true; } - static private function _postRemoteBookmark(Ostatus_profile $author, Activity $activity) + /** + * Save a bookmark from an activity + * + * @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 + */ + 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 rel=related in a Bookmark.')); + // TRANS: Client exception thrown when a bookmark is formatted incorrectly. + throw new ClientException(_m('Expected exactly 1 link '. + 'rel=related in a Bookmark.')); } if (count($relLinkEls) > 1) { - common_log(LOG_WARNING, "Got too many link rel=related in a Bookmark."); + common_log(LOG_WARNING, + "Got too many link rel=related in a Bookmark."); } $linkEl = $relLinkEls[0]; @@ -484,11 +389,9 @@ class BookmarkPlugin extends Plugin $tags[] = common_canonical_tag($category->term); } - $options = array('uri' => $bookmark->id, - 'url' => $bookmark->link, - 'created' => common_sql_time($activity->time), - 'is_local' => Notice::REMOTE_OMB, - 'source' => 'ostatus'); + if (!empty($activity->time)) { + $options['created'] = common_sql_date($activity->time); + } // Fill in location if available @@ -504,8 +407,21 @@ class BookmarkPlugin extends Plugin } $replies = $activity->context->attention; - $options['groups'] = $author->filterReplies($author, $replies); - $options['replies'] = $replies; + + $options['groups'] = array(); + $options['replies'] = array(); + + foreach ($replies as $replyURI) { + $other = Profile::fromURI($replyURI); + if (!empty($other)) { + $options['replies'][] = $replyURI; + } else { + $group = User_group::staticGet('uri', $replyURI); + if (!empty($group)) { + $options['groups'][] = $replyURI; + } + } + } // Maintain direct reply associations // @fixme what about conversation ID? @@ -518,12 +434,208 @@ class BookmarkPlugin extends Plugin } } - Bookmark::saveNew($author->localProfile(), + return Bookmark::saveNew($profile, $bookmark->title, $url, $tags, $bookmark->summary, $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; + } + + /** + * @fixme WARNING WARNING WARNING this opens a 'div' that is apparently closed by MicroAppPlugin + * @fixme that's probably wrong? + * + * @param Notice $notice + * @param HTMLOutputter $out + */ + function showNotice($notice, $out) + { + $nb = Bookmark::getByNotice($notice); + + $profile = $notice->getProfile(); + + $atts = $notice->attachments(); + + if (count($atts) < 1) { + // Something wrong; let default code deal with it. + // TRANS: Exception thrown when a bookmark has no attachments. + throw new Exception(_m('Bookmark has no attachments.')); + } + + $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 = $notice->getReplies(); + $tags = $notice->getTags(); + + if (!empty($replies) || !empty($tags)) { + + $out->elementStart('ul', array('class' => 'bookmark-tags')); + + foreach ($replies as $reply) { + $other = Profile::staticGet('id', $reply); + if (!empty($other)) { + $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(' '); // avoid   for AJAX XML compatibility + + $out->elementStart('span', 'vcard author'); // hack for belongsOnTimeline; JS needs to be able to find the author + $out->element('a', + array('class' => 'url', + 'href' => $profile->profileurl, + 'title' => $profile->getBestName()), + $profile->nickname); + $out->elementEnd('span'); + } + + function entryForm($out) + { + return new BookmarkForm($out); + } + + function tag() + { + return 'bookmark'; + } + + function appTitle() + { + // TRANS: Application title. + return _m('TITLE','Bookmark'); + } +}