]> git.mxchange.org Git - quix0rs-gnu-social.git/blob - plugins/Bookmark/BookmarkPlugin.php
e18ea25eaa100db123ea74d1024ec6799dc5b8ed
[quix0rs-gnu-social.git] / plugins / Bookmark / BookmarkPlugin.php
1 <?php
2 /**
3  * StatusNet - the distributed open-source microblogging tool
4  * Copyright (C) 2010, StatusNet, Inc.
5  *
6  * A plugin to enable social-bookmarking functionality
7  *
8  * PHP version 5
9  *
10  * This program is free software: you can redistribute it and/or modify
11  * it under the terms of the GNU Affero General Public License as published by
12  * the Free Software Foundation, either version 3 of the License, or
13  * (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU Affero General Public License for more details.
19  *
20  * You should have received a copy of the GNU Affero General Public License
21  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
22  *
23  * @category  SocialBookmark
24  * @package   StatusNet
25  * @author    Evan Prodromou <evan@status.net>
26  * @copyright 2010 StatusNet, Inc.
27  * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
28  * @link      http://status.net/
29  */
30
31 if (!defined('STATUSNET')) {
32     exit(1);
33 }
34
35 /**
36  * Bookmark plugin main class
37  *
38  * @category  Bookmark
39  * @package   StatusNet
40  * @author    Brion Vibber <brionv@status.net>
41  * @author    Evan Prodromou <evan@status.net>
42  * @copyright 2010 StatusNet, Inc.
43  * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
44  * @link      http://status.net/
45  */
46
47 class BookmarkPlugin extends Plugin
48 {
49     const VERSION = '0.1';
50
51     /**
52      * Database schema setup
53      *
54      * @see Schema
55      * @see ColumnDef
56      *
57      * @return boolean hook value; true means continue processing, false means stop.
58      */
59
60     function onCheckSchema()
61     {
62         $schema = Schema::get();
63
64         // For storing user-submitted flags on profiles
65
66         $schema->ensureTable('notice_bookmark',
67                              array(new ColumnDef('notice_id',
68                                                  'integer',
69                                                  null,
70                                                  false,
71                                                  'PRI'),
72                                    new ColumnDef('title',
73                                                  'varchar',
74                                                  255),
75                                    new ColumnDef('description',
76                                                  'text')));
77
78         return true;
79     }
80
81     /**
82      * When a notice is deleted, delete the related Notice_bookmark
83      *
84      * @param Notice $notice Notice being deleted
85      * 
86      * @return boolean hook value
87      */
88
89     function onNoticeDeleteRelated($notice)
90     {
91         $nb = Notice_bookmark::staticGet('notice_id', $notice->id);
92
93         if (!empty($nb)) {
94             $nb->delete();
95         }
96
97         return true;
98     }
99
100     /**
101      * Show the CSS necessary for this plugin
102      *
103      * @param Action $action the action being run
104      *
105      * @return boolean hook value
106      */
107
108     function onEndShowStyles($action)
109     {
110         $action->cssLink('plugins/Bookmark/bookmark.css');
111         return true;
112     }
113
114     /**
115      * Load related modules when needed
116      *
117      * @param string $cls Name of the class to be loaded
118      *
119      * @return boolean hook value; true means continue processing, false means stop.
120      */
121
122     function onAutoload($cls)
123     {
124         $dir = dirname(__FILE__);
125
126         switch ($cls)
127         {
128         case 'NewbookmarkAction':
129         case 'BookmarkpopupAction':
130             include_once $dir . '/' . strtolower(mb_substr($cls, 0, -6)) . '.php';
131             return false;
132         case 'Notice_bookmark':
133             include_once $dir.'/'.$cls.'.php';
134             return false;
135         case 'BookmarkForm':
136         case 'DeliciousBackupImporter':
137         case 'DeliciousBookmarkImporter':
138             include_once $dir.'/'.strtolower($cls).'.php';
139             return false;
140         default:
141             return true;
142         }
143     }
144
145     /**
146      * Map URLs to actions
147      *
148      * @param Net_URL_Mapper $m path-to-action mapper
149      *
150      * @return boolean hook value; true means continue processing, false means stop.
151      */
152
153     function onRouterInitialized($m)
154     {
155         $m->connect('main/bookmark/new',
156                     array('action' => 'newbookmark'),
157                     array('id' => '[0-9]+'));
158
159         $m->connect('main/bookmark/popup', array('action' => 'bookmarkpopup'));
160
161         return true;
162     }
163
164     /**
165      * Output the HTML for a bookmark in a list
166      *
167      * @param NoticeListItem $nli The list item being shown.
168      *
169      * @return boolean hook value
170      */
171
172     function onStartShowNoticeItem($nli)
173     {
174         $nb = Notice_bookmark::staticGet('notice_id',
175                                          $nli->notice->id);
176
177         if (!empty($nb)) {
178
179             $out     = $nli->out;
180             $notice  = $nli->notice;
181             $profile = $nli->profile;
182
183             $atts = $notice->attachments();
184
185             if (count($atts) < 1) {
186                 // Something wrong; let default code deal with it.
187                 return true;
188             }
189
190             $att = $atts[0];
191
192             $out->elementStart('h3');
193             $out->element('a',
194                           array('href' => $att->url),
195                           $nb->title);
196             $out->elementEnd('h3');
197
198             $out->elementStart('ul', array('class' => 'bookmark_tags'));
199             
200             // Replies look like "for:" tags
201
202             $replies = $nli->notice->getReplies();
203
204             if (!empty($replies)) {
205                 foreach ($replies as $reply) {
206                     $other = Profile::staticGet('id', $reply);
207                     $out->elementStart('li');
208                     $out->element('a', array('rel' => 'tag',
209                                              'href' => $other->profileurl,
210                                              'title' => $other->getBestName()),
211                                   sprintf('for:%s', $other->nickname));
212                     $out->elementEnd('li');
213                     $out->text(' ');
214                 }
215             }
216
217             $tags = $nli->notice->getTags();
218
219             foreach ($tags as $tag) {
220                 $out->elementStart('li');
221                 $out->element('a', 
222                               array('rel' => 'tag',
223                                     'href' => Notice_tag::url($tag)),
224                               $tag);
225                 $out->elementEnd('li');
226                 $out->text(' ');
227             }
228
229             $out->elementEnd('ul');
230
231             $out->element('p',
232                           array('class' => 'bookmark_description'),
233                           $nb->description);
234
235             $nli->showNoticeAttachments();
236
237             $out->elementStart('p', array('style' => 'float: left'));
238
239             $avatar = $profile->getAvatar(AVATAR_MINI_SIZE);
240
241             $out->element('img', array('src' => ($avatar) ?
242                                        $avatar->displayUrl() :
243                                        Avatar::defaultImage(AVATAR_MINI_SIZE),
244                                        'class' => 'avatar photo bookmark_avatar',
245                                        'width' => AVATAR_MINI_SIZE,
246                                        'height' => AVATAR_MINI_SIZE,
247                                        'alt' => $profile->getBestName()));
248             $out->raw('&nbsp;');
249             $out->element('a', array('href' => $profile->profileurl,
250                                      'title' => $profile->getBestName()),
251                           $profile->nickname);
252
253             $nli->showNoticeLink();
254             $nli->showNoticeSource();
255             $nli->showNoticeLocation();
256             $nli->showContext();
257             $nli->showRepeat();
258
259             $out->elementEnd('p');
260
261             $nli->showNoticeOptions();
262
263             return false;
264         }
265         return true;
266     }
267
268     /**
269      * Render a notice as a Bookmark object
270      *
271      * @param Notice         $notice  Notice to render
272      * @param ActivityObject &$object Empty object to fill
273      *
274      * @return boolean hook value
275      */
276      
277     function onStartActivityObjectFromNotice($notice, &$object)
278     {
279         common_log(LOG_INFO,
280                    "Checking {$notice->uri} to see if it's a bookmark.");
281
282         $nb = Notice_bookmark::staticGet('notice_id',
283                                          $notice->id);
284                                          
285         if (!empty($nb)) {
286
287             common_log(LOG_INFO,
288                        "Formatting notice {$notice->uri} as a bookmark.");
289
290             $object->id      = $notice->uri;
291             $object->type    = ActivityObject::BOOKMARK;
292             $object->title   = $nb->title;
293             $object->summary = $nb->description;
294             $object->link    = $notice->bestUrl();
295
296             // Attributes of the URL
297
298             $attachments = $notice->attachments();
299
300             if (count($attachments) != 1) {
301                 throw new ServerException(_('Bookmark notice with the '.
302                                             'wrong number of attachments.'));
303             }
304
305             $target = $attachments[0];
306
307             $attrs = array('rel' => 'related',
308                            'href' => $target->url);
309
310             if (!empty($target->title)) {
311                 $attrs['title'] = $target->title;
312             }
313
314             $object->extra[] = array('link', $attrs);
315                                                    
316             // Attributes of the thumbnail, if any
317
318             $thumbnail = $target->getThumbnail();
319
320             if (!empty($thumbnail)) {
321                 $tattrs = array('rel' => 'preview',
322                                 'href' => $thumbnail->url);
323
324                 if (!empty($thumbnail->width)) {
325                     $tattrs['media:width'] = $thumbnail->width;
326                 }
327
328                 if (!empty($thumbnail->height)) {
329                     $tattrs['media:height'] = $thumbnail->height;
330                 }
331
332                 $object->extra[] = array('link', $attrs);
333             }
334
335             return false;
336         }
337
338         return true;
339     }
340
341     /**
342      * Add our two queue handlers to the queue manager
343      *
344      * @param QueueManager $qm current queue manager
345      * 
346      * @return boolean hook value
347      */
348
349     function onEndInitializeQueueManager($qm)
350     {
351         $qm->connect('dlcsback', 'DeliciousBackupImporter');
352         $qm->connect('dlcsbkmk', 'DeliciousBookmarkImporter');
353         return true;
354     }
355
356     /**
357      * Plugin version data
358      *
359      * @param array &$versions array of version data
360      * 
361      * @return value
362      */
363
364     function onPluginVersion(&$versions)
365     {
366         $versions[] = array('name' => 'Sample',
367                             'version' => self::VERSION,
368                             'author' => 'Evan Prodromou',
369                             'homepage' => 'http://status.net/wiki/Plugin:Bookmark',
370                             'rawdescription' =>
371                             _m('Simple extension for supporting bookmarks.'));
372         return true;
373     }
374
375     /**
376      * Load our document if requested
377      *
378      * @param string &$title  Title to fetch
379      * @param string &$output HTML to output
380      *
381      * @return boolean hook value
382      */
383
384     function onStartLoadDoc(&$title, &$output)
385     {
386         if ($title == 'bookmarklet') {
387             $filename = INSTALLDIR.'/plugins/Bookmark/bookmarklet';
388
389             $c      = file_get_contents($filename);
390             $output = common_markup_to_html($c);
391             return false; // success!
392         }
393
394         return true;
395     }
396
397     /**
398      * Handle a posted bookmark from PuSH
399      *
400      * @param Activity        $activity activity to handle
401      * @param Ostatus_profile $oprofile Profile for the feed
402      *
403      * @return boolean hook value
404      */
405
406     function onStartHandleFeedEntryWithProfile($activity, $oprofile) {
407
408         common_log(LOG_INFO, "BookmarkPlugin called for new feed entry.");
409
410         if ($activity->verb == ActivityVerb::POST &&
411             $activity->objects[0]->type == ActivityObject::BOOKMARK) {
412
413             common_log(LOG_INFO, "Importing activity {$activity->id} as a bookmark.");
414
415             $author = $oprofile->checkAuthorship($activity);
416
417             if (empty($author)) {
418                 throw new ClientException(_('Can\'t get author for activity.'));
419             }
420
421             self::_postRemoteBookmark($author,
422                                       $activity);
423
424             return false;
425         }
426
427         return true;
428     }
429
430     static private function _postRemoteBookmark(Ostatus_profile $author, Activity $activity)
431     {
432         $bookmark = $activity->objects[0];
433
434         $relLinkEls = ActivityUtils::getLinks($bookmark->element, 'related');
435
436         if (count($relLinkEls) < 1) {
437             throw new ClientException(_('Expected exactly 1 link rel=related in a Bookmark.'));
438         }
439
440         if (count($relLinkEls) > 1) {
441             common_log(LOG_WARNING, "Got too many link rel=related in a Bookmark.");
442         }
443
444         $linkEl = $relLinkEls[0];
445
446         $url = $linkEl->getAttribute('href');
447
448         $tags = array();
449
450         foreach ($activity->categories as $category) {
451             $tags[] = common_canonical_tag($category->term);
452         }
453
454         $options = array('uri' => $bookmark->id,
455                          'url' => $bookmark->link,
456                          'created' => common_sql_time($activity->time),
457                          'is_local' => Notice::REMOTE_OMB,
458                          'source' => 'ostatus');
459
460         // Fill in location if available
461
462         $location = $activity->context->location;
463
464         if ($location) {
465             $options['lat'] = $location->lat;
466             $options['lon'] = $location->lon;
467             if ($location->location_id) {
468                 $options['location_ns'] = $location->location_ns;
469                 $options['location_id'] = $location->location_id;
470             }
471         }
472
473         $replies = $activity->context->attention;
474         $options['groups'] = $author->filterReplies($author, $replies);
475         $options['replies'] = $replies;
476
477         // Maintain direct reply associations
478         // @fixme what about conversation ID?
479
480         if (!empty($activity->context->replyToID)) {
481             $orig = Notice::staticGet('uri',
482                                       $activity->context->replyToID);
483             if (!empty($orig)) {
484                 $options['reply_to'] = $orig->id;
485             }
486         }
487
488         Notice_bookmark::saveNew($author->localProfile(),
489                                  $bookmark->title,
490                                  $url,
491                                  $tags,
492                                  $bookmark->summary,
493                                  $options);
494     }
495 }
496