]> git.mxchange.org Git - quix0rs-gnu-social.git/blob - plugins/Bookmark/Bookmark.php
Merge branch '1.1.x' of gitorious.org:statusnet/mainline into 1.1.x
[quix0rs-gnu-social.git] / plugins / Bookmark / Bookmark.php
1 <?php
2 /**
3  * Data class to mark notices as bookmarks
4  *
5  * PHP version 5
6  *
7  * @category Data
8  * @package  StatusNet
9  * @author   Evan Prodromou <evan@status.net>
10  * @license  http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
11  * @link     http://status.net/
12  *
13  * StatusNet - the distributed open-source microblogging tool
14  * Copyright (C) 2009, StatusNet, Inc.
15  *
16  * This program is free software: you can redistribute it and/or modify
17  * it under the terms of the GNU Affero General Public License as published by
18  * the Free Software Foundation, either version 3 of the License, or
19  * (at your option) any later version.
20  *
21  * This program is distributed in the hope that it will be useful,
22  * but WITHOUT ANY WARRANTY; without even the implied warranty of
23  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.     See the
24  * GNU Affero General Public License for more details.
25  *
26  * You should have received a copy of the GNU Affero General Public License
27  * along with this program. If not, see <http://www.gnu.org/licenses/>.
28  */
29
30 if (!defined('STATUSNET')) {
31     exit(1);
32 }
33
34 /**
35  * For storing the fact that a notice is a bookmark
36  *
37  * @category Bookmark
38  * @package  StatusNet
39  * @author   Evan Prodromou <evan@status.net>
40  * @license  http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
41  * @link     http://status.net/
42  *
43  * @see      DB_DataObject
44  */
45 class Bookmark extends Memcached_DataObject
46 {
47     public $__table = 'bookmark'; // table name
48     public $id;          // char(36) primary_key not_null
49     public $profile_id;  // int(4) not_null
50     public $url;         // varchar(255) not_null
51     public $title;       // varchar(255)
52     public $description; // text
53     public $uri;         // varchar(255)
54     public $created;     // datetime
55
56     /**
57      * Get an instance by key
58      *
59      * This is a utility method to get a single instance with a given key value.
60      *
61      * @param string $k Key to use to lookup (usually 'user_id' for this class)
62      * @param mixed  $v Value to lookup
63      *
64      * @return User_greeting_count object found, or null for no hits
65      *
66      */
67     function staticGet($k, $v=null)
68     {
69         return Memcached_DataObject::staticGet('Bookmark', $k, $v);
70     }
71
72     /**
73      * Get an instance by compound key
74      *
75      * This is a utility method to get a single instance with a given set of
76      * key-value pairs. Usually used for the primary key for a compound key; thus
77      * the name.
78      *
79      * @param array $kv array of key-value mappings
80      *
81      * @return Bookmark object found, or null for no hits
82      *
83      */
84     function pkeyGet($kv)
85     {
86         return Memcached_DataObject::pkeyGet('Bookmark', $kv);
87     }
88
89     /**
90      * return table definition for DB_DataObject
91      *
92      * DB_DataObject needs to know something about the table to manipulate
93      * instances. This method provides all the DB_DataObject needs to know.
94      *
95      * @return array array of column definitions
96      */
97     function table()
98     {
99         return array('id' => DB_DATAOBJECT_STR + DB_DATAOBJECT_NOTNULL,
100                      'profile_id' => DB_DATAOBJECT_INT + DB_DATAOBJECT_NOTNULL,
101                      'url' => DB_DATAOBJECT_STR,
102                      'title' => DB_DATAOBJECT_STR,
103                      'description' => DB_DATAOBJECT_STR,
104                      'uri' => DB_DATAOBJECT_STR,
105                      'created' => DB_DATAOBJECT_STR + DB_DATAOBJECT_DATE +
106                      DB_DATAOBJECT_TIME + DB_DATAOBJECT_NOTNULL);
107     }
108
109     /**
110      * return key definitions for DB_DataObject
111      *
112      * @return array list of key field names
113      */
114     function keys()
115     {
116         return array_keys($this->keyTypes());
117     }
118
119     /**
120      * return key definitions for Memcached_DataObject
121      *
122      * @return array associative array of key definitions
123      */
124     function keyTypes()
125     {
126         return array('id' => 'K',
127                      'uri' => 'U');
128     }
129
130     /**
131      * Magic formula for non-autoincrementing integer primary keys
132      *
133      * @return array magic three-false array that stops auto-incrementing.
134      */
135     function sequenceKey()
136     {
137         return array(false, false, false);
138     }
139
140     /**
141      * Get a bookmark based on a notice
142      *
143      * @param Notice $notice Notice to check for
144      *
145      * @return Bookmark found bookmark or null
146      */
147     function getByNotice($notice)
148     {
149         return self::staticGet('uri', $notice->uri);
150     }
151
152     /**
153      * Get the bookmark that a user made for an URL
154      *
155      * @param Profile $profile Profile to check for
156      * @param string  $url     URL to check for
157      *
158      * @return Bookmark bookmark found or null
159      */
160     static function getByURL($profile, $url)
161     {
162         $nb = new Bookmark();
163
164         $nb->profile_id = $profile->id;
165         $nb->url        = $url;
166
167         if ($nb->find(true)) {
168             return $nb;
169         } else {
170             return null;
171         }
172     }
173
174     /**
175      * Save a new notice bookmark
176      *
177      * @param Profile $profile     To save the bookmark for
178      * @param string  $title       Title of the bookmark
179      * @param string  $url         URL of the bookmark
180      * @param mixed   $rawtags     array of tags or string
181      * @param string  $description Description of the bookmark
182      * @param array   $options     Options for the Notice::saveNew()
183      *
184      * @return Notice saved notice
185      */
186     static function saveNew($profile, $title, $url, $rawtags, $description,
187                             $options=null)
188     {
189         $nb = self::getByURL($profile, $url);
190
191         if (!empty($nb)) {
192             // TRANS: Client exception thrown when trying to save a new bookmark that already exists.
193             throw new ClientException(_m('Bookmark already exists.'));
194         }
195
196         if (empty($options)) {
197             $options = array();
198         }
199
200         if (array_key_exists('uri', $options)) {
201             $other = Bookmark::staticGet('uri', $options['uri']);
202             if (!empty($other)) {
203                 // TRANS: Client exception thrown when trying to save a new bookmark that already exists.
204                 throw new ClientException(_m('Bookmark already exists.'));
205             }
206         }
207
208         if (is_string($rawtags)) {
209             if (empty($rawtags)) {
210                 $rawtags = array();
211             } else {
212                 $rawtags = preg_split('/[\s,]+/', $rawtags);
213             }
214         }
215
216         $nb = new Bookmark();
217
218         $nb->id          = UUID::gen();
219         $nb->profile_id  = $profile->id;
220         $nb->url         = $url;
221         $nb->title       = $title;
222         $nb->description = $description;
223
224         if (array_key_exists('created', $options)) {
225             $nb->created = $options['created'];
226         } else {
227             $nb->created = common_sql_now();
228         }
229
230         if (array_key_exists('uri', $options)) {
231             $nb->uri = $options['uri'];
232         } else {
233             // FIXME: hacks to work around router bugs in
234             // queue daemons
235
236             $r = Router::get();
237
238             $path = $r->build('showbookmark',
239                               array('id' => $nb->id));
240
241             if (empty($path)) {
242                 $nb->uri = common_path('bookmark/'.$nb->id, false, false);
243             } else {
244                 $nb->uri = common_local_url('showbookmark',
245                                             array('id' => $nb->id),
246                                             null,
247                                             null,
248                                             false);
249             }
250         }
251
252         $nb->insert();
253
254         $tags    = array();
255         $replies = array();
256
257         // filter "for:nickname" tags
258
259         foreach ($rawtags as $tag) {
260             if (strtolower(mb_substr($tag, 0, 4)) == 'for:') {
261                 // skip if done by caller
262                 if (!array_key_exists('replies', $options)) {
263                     $nickname = mb_substr($tag, 4);
264                     $other    = common_relative_profile($profile,
265                                                         $nickname);
266                     if (!empty($other)) {
267                         $replies[] = $other->getUri();
268                     }
269                 }
270             } else {
271                 $tags[] = common_canonical_tag($tag);
272             }
273         }
274
275         $hashtags = array();
276         $taglinks = array();
277
278         foreach ($tags as $tag) {
279             $hashtags[] = '#'.$tag;
280             $attrs      = array('href' => Notice_tag::url($tag),
281                                 'rel'  => $tag,
282                                 'class' => 'tag');
283             $taglinks[] = XMLStringer::estring('a', $attrs, $tag);
284         }
285
286         // Use user's preferences for short URLs, if possible
287
288         try {
289             $user = User::staticGet('id', $profile->id);
290
291             $shortUrl = File_redirection::makeShort($url,
292                                                     empty($user) ? null : $user);
293         } catch (Exception $e) {
294             // Don't let this stop us.
295             $shortUrl = $url;
296         }
297
298         // TRANS: Bookmark content.
299         // TRANS: %1$s is a title, %2$s is a short URL, %3$s is the bookmark description,
300         // TRANS: %4$s is space separated list of hash tags.
301         $content = sprintf(_m('"%1$s" %2$s %3$s %4$s'),
302                            $title,
303                            $shortUrl,
304                            $description,
305                            implode(' ', $hashtags));
306
307         // TRANS: Rendered bookmark content.
308         // TRANS: %1$s is a URL, %2$s the bookmark title, %3$s is the bookmark description,
309         // TRANS: %4$s is space separated list of hash tags.
310         $rendered = sprintf(_m('<span class="xfolkentry">'.
311                               '<a class="taggedlink" href="%1$s">%2$s</a> '.
312                               '<span class="description">%3$s</span> '.
313                               '<span class="meta">%4$s</span>'.
314                               '</span>'),
315                             htmlspecialchars($url),
316                             htmlspecialchars($title),
317                             htmlspecialchars($description),
318                             implode(' ', $taglinks));
319
320         $options = array_merge(array('urls' => array($url),
321                                      'rendered' => $rendered,
322                                      'tags' => $tags,
323                                      'replies' => $replies,
324                                      'object_type' => ActivityObject::BOOKMARK),
325                                $options);
326
327         if (!array_key_exists('uri', $options)) {
328             $options['uri'] = $nb->uri;
329         }
330
331         try {
332             $saved = Notice::saveNew($profile->id,
333                                      $content,
334                                      array_key_exists('source', $options) ?
335                                      $options['source'] : 'web',
336                                      $options);
337         } catch (Exception $e) {
338             $nb->delete();
339             throw $e;
340         }
341
342         if (empty($saved)) {
343             $nb->delete();
344         }
345
346         return $saved;
347     }
348 }