]> git.mxchange.org Git - quix0rs-gnu-social.git/blob - plugins/Bookmark/BookmarkPlugin.php
771a46c1ccfa296453e0ec7d5e865c8302abe807
[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('GNUSOCIAL')) { exit(1); }
32
33 /**
34  * Bookmark plugin main class
35  *
36  * @category  Bookmark
37  * @package   StatusNet
38  * @author    Brion Vibber <brionv@status.net>
39  * @author    Evan Prodromou <evan@status.net>
40  * @copyright 2010 StatusNet, Inc.
41  * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
42  * @link      http://status.net/
43  */
44 class BookmarkPlugin extends MicroAppPlugin
45 {
46     const PLUGIN_VERSION         = '0.1.0';
47     const IMPORTDELICIOUS = 'BookmarkPlugin:IMPORTDELICIOUS';
48
49     /**
50      * Authorization for importing delicious bookmarks
51      *
52      * By default, everyone can import bookmarks except silenced people.
53      *
54      * @param Profile $profile Person whose rights to check
55      * @param string  $right   Right to check; const value
56      * @param boolean &$result Result of the check, writeable
57      *
58      * @return boolean hook value
59      */
60     function onUserRightsCheck($profile, $right, &$result)
61     {
62         if ($right == self::IMPORTDELICIOUS) {
63             $result = !$profile->isSilenced();
64             return false;
65         }
66         return true;
67     }
68
69     /**
70      * Database schema setup
71      *
72      * @see Schema
73      * @see ColumnDef
74      *
75      * @return boolean hook value; true means continue processing, false means stop.
76      */
77     function onCheckSchema()
78     {
79         $schema = Schema::get();
80
81         $schema->ensureTable('bookmark', Bookmark::schemaDef());
82
83         return true;
84     }
85
86     /**
87      * Show the CSS necessary for this plugin
88      *
89      * @param Action $action the action being run
90      *
91      * @return boolean hook value
92      */
93     function onEndShowStyles($action)
94     {
95         $action->cssLink($this->path('css/bookmark.css'));
96         return true;
97     }
98
99     function onEndShowScripts($action)
100     {
101         $action->script($this->path('js/bookmark.js'));
102         return true;
103     }
104
105     /**
106      * Map URLs to actions
107      *
108      * @param URLMapper $m path-to-action mapper
109      *
110      * @return boolean hook value; true means continue processing, false means stop.
111      */
112     public function onRouterInitialized(URLMapper $m)
113     {
114         if (common_config('singleuser', 'enabled')) {
115             $nickname = User::singleUserNickname();
116             $m->connect('bookmarks',
117                         ['action'   => 'bookmarks',
118                          'nickname' => $nickname]);
119             $m->connect('bookmarks/rss',
120                         ['action'   => 'bookmarksrss',
121                          'nickname' => $nickname]);
122         } else {
123             $m->connect(':nickname/bookmarks',
124                         ['action' => 'bookmarks'],
125                         ['nickname' => Nickname::DISPLAY_FMT]);
126             $m->connect(':nickname/bookmarks/rss',
127                         ['action' => 'bookmarksrss'],
128                         ['nickname' => Nickname::DISPLAY_FMT]);
129         }
130
131         $m->connect('api/bookmarks/:id.:format',
132                     ['action' => 'ApiTimelineBookmarks'],
133                     ['id' => Nickname::INPUT_FMT,
134                      'format' => '(xml|json|rss|atom|as)']);
135
136         $m->connect('main/bookmark/new',
137                     ['action' => 'newbookmark']);
138
139         $m->connect('main/bookmark/popup',
140                     ['action' => 'bookmarkpopup']);
141
142         $m->connect('main/bookmark/import',
143                     ['action' => 'importdelicious']);
144
145         $m->connect('main/bookmark/forurl',
146                     ['action' => 'bookmarkforurl']);
147
148         $m->connect('bookmark/:id',
149                     ['action' => 'showbookmark'],
150                     ['id' => '[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}']);
151
152         $m->connect('notice/by-url/:id',
153                     ['action' => 'noticebyurl'],
154                     ['id' => '[0-9]+']);
155
156         return true;
157     }
158
159
160     /**
161      * Add our two queue handlers to the queue manager
162      *
163      * @param QueueManager $qm current queue manager
164      *
165      * @return boolean hook value
166      */
167     function onEndInitializeQueueManager($qm)
168     {
169         $qm->connect('dlcsback', 'DeliciousBackupImporter');
170         $qm->connect('dlcsbkmk', 'DeliciousBookmarkImporter');
171         return true;
172     }
173
174     /**
175      * Plugin version data
176      *
177      * @param array &$versions array of version data
178      *
179      * @return value
180      */
181     function onPluginVersion(array &$versions)
182     {
183         $versions[] = array('name' => 'Bookmark',
184                             'version' => self::PLUGIN_VERSION,
185                             'author' => 'Evan Prodromou, Stephane Berube, Jean Baptiste Favre, Mikael Nordfeldth',
186                             'homepage' => 'https://gnu.io/social',
187                             'description' =>
188                             // TRANS: Plugin description.
189                             _m('Plugin for posting bookmarks. ') .
190                             'BookmarkList feature has been developped by Stephane Berube. ' .
191                             'Integration has been done by Jean Baptiste Favre.');
192         return true;
193     }
194
195     /**
196      * Load our document if requested
197      *
198      * @param string &$title  Title to fetch
199      * @param string &$output HTML to output
200      *
201      * @return boolean hook value
202      */
203     function onStartLoadDoc(&$title, &$output)
204     {
205         if ($title == 'bookmarklet') {
206             $filename = INSTALLDIR.'/plugins/Bookmark/bookmarklet';
207
208             $c      = file_get_contents($filename);
209             $output = common_markup_to_html($c);
210             return false; // success!
211         }
212
213         return true;
214     }
215
216     /**
217      * Show a link to our delicious import page on profile settings form
218      *
219      * @param Action $action Profile settings action being shown
220      *
221      * @return boolean hook value
222      */
223     function onEndProfileSettingsActions($action)
224     {
225         $user = common_current_user();
226
227         if (!empty($user) && $user->hasRight(self::IMPORTDELICIOUS)) {
228             $action->elementStart('li');
229             $action->element('a',
230                              array('href' => common_local_url('importdelicious')),
231                              // TRANS: Link text in proile leading to import form.
232                              _m('Import del.icio.us bookmarks'));
233             $action->elementEnd('li');
234         }
235
236         return true;
237     }
238
239     /**
240      * Modify the default menu to link to our custom action
241      *
242      * Using event handlers, it's possible to modify the default UI for pages
243      * almost without limit. In this method, we add a menu item to the default
244      * primary menu for the interface to link to our action.
245      *
246      * The Action class provides a rich set of events to hook, as well as output
247      * methods.
248      *
249      * @param Action $action The current action handler. Use this to
250      * do any output.
251      *
252      * @return boolean hook value; true means continue processing, false means stop.
253      *
254      * @see Action
255      */
256     function onEndPersonalGroupNav(Menu $menu, Profile $target, Profile $scoped=null)
257     {
258         $menu->menuItem(common_local_url('bookmarks', array('nickname' => $target->getNickname())),
259                           // TRANS: Menu item in sample plugin.
260                           _m('Bookmarks'),
261                           // TRANS: Menu item title in sample plugin.
262                           _m('A list of your bookmarks'), false, 'nav_timeline_bookmarks');
263         return true;
264     }
265
266     function types()
267     {
268         return array(ActivityObject::BOOKMARK);
269     }
270
271     /**
272      * When a notice is deleted, delete the related Bookmark
273      *
274      * @param Notice $notice Notice being deleted
275      *
276      * @return boolean hook value
277      */
278     function deleteRelated(Notice $notice)
279     {
280         try {
281             $nb = Bookmark::fromStored($notice);
282         } catch (NoResultException $e) {
283             throw new AlreadyFulfilledException('Bookmark already gone when deleting: '.$e->getMessage());
284         }
285         $nb->delete();
286         
287         return true;
288     }
289
290     /**
291      * Save a bookmark from an activity
292      *
293      * @param Activity $activity Activity to save
294      * @param Profile  $actor    Profile to use as author
295      * @param array    $options  Options to pass to bookmark-saving code
296      *
297      * @return Notice resulting notice
298      */
299     protected function saveObjectFromActivity(Activity $activity, Notice $stored, array $options=array())
300     {
301         return Bookmark::saveActivityObject($activity->objects[0], $stored);
302     }
303
304     public function onEndNoticeAsActivity(Notice $stored, Activity $act, Profile $scoped=null)
305     {
306         if (!$this->isMyNotice($stored)) {
307             return true;
308         }
309
310         $this->extendActivity($stored, $act, $scoped);
311         return false;
312     }
313
314     public function extendActivity(Notice $stored, Activity $act, Profile $scoped=null)
315     {
316         /*$hashtags = array();
317         $taglinks = array();
318
319         foreach ($tags as $tag) {
320             $hashtags[] = '#'.$tag;
321             $attrs      = array('href' => Notice_tag::url($tag),
322                                 'rel'  => $tag,
323                                 'class' => 'tag');
324             $taglinks[] = XMLStringer::estring('a', $attrs, $tag);
325         }*/
326     }
327
328     function activityObjectFromNotice(Notice $notice)
329     {
330         return Bookmark::fromStored($notice)->asActivityObject();
331     }
332
333     function entryForm($out)
334     {
335         return new InitialBookmarkForm($out);
336     }
337
338     function tag()
339     {
340         return 'bookmark';
341     }
342
343     function appTitle()
344     {
345         // TRANS: Application title.
346         return _m('TITLE','Bookmark');
347     }
348
349     function onEndUpgrade()
350     {
351         printfnq('Making sure Bookmark notices have correct verb and object_type...');
352
353         // Version 0.9.x of the plugin didn't stamp notices
354         // with verb and object-type (for obvious reasons). Update
355         // those notices here.
356
357         $notice = new Notice();
358         
359         $notice->joinAdd(array('uri', 'bookmark:uri'));
360         $notice->whereAdd('object_type IS NULL OR object_type = '.$notice->_quote(ActivityObject::NOTE));
361
362         $notice->find();
363
364         while ($notice->fetch()) {
365             $original = clone($notice);
366             $notice->verb        = ActivityVerb::POST;
367             $notice->object_type = ActivityObject::BOOKMARK;
368             $notice->update($original);
369         }
370
371         printfnq("DONE.\n");
372     }
373
374     public function activityObjectOutputJson(ActivityObject $obj, array &$out)
375     {
376         assert($obj->type == ActivityObject::BOOKMARK);
377
378         $bm = Bookmark::getByPK(array('uri' => $obj->id));
379
380         $out['displayName'] = $bm->getTitle();
381         $out['targetUrl']   = $bm->getUrl();
382
383         return true;
384     }
385
386     protected function showNoticeContent(Notice $stored, HTMLOutputter $out, Profile $scoped=null)
387     {
388         $nb = Bookmark::fromStored($stored);
389
390         // Whether to nofollow
391         $attrs = array('href' => $nb->getUrl(), 'class' => 'bookmark-title');
392
393         $nf = common_config('nofollow', 'external');
394
395         if ($nf == 'never' || ($nf == 'sometimes' and $out instanceof ShowstreamAction)) {
396             $attrs['rel'] = 'external';
397         } else {
398             $attrs['rel'] = 'nofollow external';
399         }
400
401         $out->elementStart('h3');
402         $out->element('a', $attrs, $nb->getTitle());
403         $out->elementEnd('h3');
404
405         // Replies look like "for:" tags
406         $replies = $stored->getReplies();
407         $tags = $stored->getTags();
408
409         if (!empty($nb->description)) {
410             $out->element('p',
411                           array('class' => 'bookmark-description'),
412                           $nb->description);
413         }
414
415         if (!empty($replies) || !empty($tags)) {
416
417             $out->elementStart('ul', array('class' => 'bookmark-tags'));
418
419             foreach ($replies as $reply) {
420                 $other = Profile::getByPK($reply);
421                 $out->elementStart('li');
422                 $out->element('a', array('rel' => 'tag',
423                                          'href' => $other->getUrl(),
424                                          'title' => $other->getBestName()),
425                               sprintf('for:%s', $other->getNickname()));
426                 $out->elementEnd('li');
427                 $out->text(' ');
428             }
429
430             foreach ($tags as $tag) {
431                 $tag = trim($tag);
432                 if (!empty($tag)) {
433                     $out->elementStart('li');
434                     $out->element('a',
435                                   array('rel' => 'tag',
436                                         'href' => Notice_tag::url($tag)),
437                                   $tag);
438                     $out->elementEnd('li');
439                     $out->text(' ');
440                 }
441             }
442
443             $out->elementEnd('ul');
444         }
445
446     }
447 }