]> git.mxchange.org Git - quix0rs-gnu-social.git/blob - plugins/Bookmark/Bookmark.php
61fe3c5b976e0d340d0025b50ee148bcf0be9c1b
[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
46 class Bookmark extends Memcached_DataObject
47 {
48     public $__table = 'bookmark'; // table name
49     public $profile_id;           // int(4)  primary_key not_null
50     public $url;                  // varchar(255) primary_key not_null
51     public $title;                // varchar(255)
52     public $description;          // text
53     public $uri;                  // varchar(255)
54     public $url_crc32;            // int(4) not_null
55     public $created;              // datetime
56
57     /**
58      * Get an instance by key
59      *
60      * This is a utility method to get a single instance with a given key value.
61      *
62      * @param string $k Key to use to lookup (usually 'user_id' for this class)
63      * @param mixed  $v Value to lookup
64      *
65      * @return User_greeting_count object found, or null for no hits
66      *
67      */
68
69     function staticGet($k, $v=null)
70     {
71         return Memcached_DataObject::staticGet('Bookmark', $k, $v);
72     }
73
74     /**
75      * Get an instance by compound key
76      *
77      * This is a utility method to get a single instance with a given set of
78      * key-value pairs. Usually used for the primary key for a compound key; thus
79      * the name.
80      *
81      * @param array $kv array of key-value mappings
82      *
83      * @return Bookmark object found, or null for no hits
84      *
85      */
86
87     function pkeyGet($kv)
88     {
89         return Memcached_DataObject::pkeyGet('Bookmark', $kv);
90     }
91
92     /**
93      * return table definition for DB_DataObject
94      *
95      * DB_DataObject needs to know something about the table to manipulate
96      * instances. This method provides all the DB_DataObject needs to know.
97      *
98      * @return array array of column definitions
99      */
100
101     function table()
102     {
103         return array('profile_id' => DB_DATAOBJECT_INT + DB_DATAOBJECT_NOTNULL,
104                      'url' => DB_DATAOBJECT_STR,
105                      'title' => DB_DATAOBJECT_STR,
106                      'description' => DB_DATAOBJECT_STR,
107                      'uri' => DB_DATAOBJECT_STR,
108                      'url_crc32' => DB_DATAOBJECT_INT + DB_DATAOBJECT_NOTNULL,
109                      'created' => DB_DATAOBJECT_STR + DB_DATAOBJECT_DATE + 
110                      DB_DATAOBJECT_TIME + DB_DATAOBJECT_NOTNULL);
111     }
112
113     /**
114      * return key definitions for DB_DataObject
115      *
116      * @return array list of key field names
117      */
118
119     function keys()
120     {
121         return array_keys($this->keyTypes());
122     }
123
124     /**
125      * return key definitions for Memcached_DataObject
126      *
127      * @return array associative array of key definitions
128      */
129
130     function keyTypes()
131     {
132         return array('profile_id' => 'K',
133                      'url' => 'K',
134                      'uri' => 'U');
135     }
136
137     /**
138      * Magic formula for non-autoincrementing integer primary keys
139      *
140      * @return array magic three-false array that stops auto-incrementing.
141      */
142
143     function sequenceKey()
144     {
145         return array(false, false, false);
146     }
147
148     /**
149      * Get a bookmark based on a notice
150      * 
151      * @param Notice $notice Notice to check for
152      *
153      * @return Bookmark found bookmark or null
154      */
155     
156     function getByNotice($notice)
157     {
158         return self::staticGet('uri', $notice->uri);
159     }
160
161     /**
162      * Get the bookmark that a user made for an URL
163      *
164      * @param Profile $profile Profile to check for
165      * @param string  $url     URL to check for
166      *
167      * @return Bookmark bookmark found or null
168      */
169      
170     static function getByURL($profile, $url)
171     {
172         return self::pkeyGet(array('profile_id' => $profile->id,
173                                    'url' => $url));
174         return null;
175     }
176
177     /**
178      * Get the bookmark that a user made for an URL
179      *
180      * @param Profile $profile Profile to check for
181      * @param integer $crc32   CRC-32 of URL to check for
182      *
183      * @return array Bookmark objects found (usually 1 or 0)
184      */
185      
186     static function getByCRC32($profile, $crc32)
187     {
188         $bookmarks = array();
189
190         $nb = new Bookmark();
191         
192         $nb->profile_id = $profile->id;
193         $nb->url_crc32  = $crc32;
194
195         if ($nb->find()) {
196             while ($nb->fetch()) {
197                 $bookmarks[] = clone($nb);
198             }
199         }
200
201         return $bookmarks;
202     }
203
204     /**
205      * Save a new notice bookmark
206      *
207      * @param Profile $profile     To save the bookmark for
208      * @param string  $title       Title of the bookmark
209      * @param string  $url         URL of the bookmark
210      * @param mixed   $rawtags     array of tags or string
211      * @param string  $description Description of the bookmark
212      * @param array   $options     Options for the Notice::saveNew()
213      *
214      * @return Notice saved notice
215      */
216
217     static function saveNew($profile, $title, $url, $rawtags, $description,
218                             $options=null)
219     {
220         $nb = self::getByURL($profile, $url);
221
222         if (!empty($nb)) {
223             throw new ClientException(_('Bookmark already exists.'));
224         }
225
226         if (empty($options)) {
227             $options = array();
228         }
229
230         if (array_key_exists('uri', $options)) {
231             $other = Bookmark::staticGet('uri', $options['uri']);
232             if (!empty($other)) {
233                 throw new ClientException(_('Bookmark already exists.'));
234             }
235         }
236
237         if (is_string($rawtags)) {
238             $rawtags = preg_split('/[\s,]+/', $rawtags);
239         }
240
241         $nb = new Bookmark();
242
243         $nb->profile_id  = $profile->id;
244         $nb->url         = $url;
245         $nb->title       = $title;
246         $nb->description = $description;
247         $nb->url_crc32   = crc32($nb->url);
248
249         if (array_key_exists('created', $options)) {
250             $nb->created = $options['created'];
251         } else {
252             $nb->created = common_sql_now();
253         }
254
255         if (array_key_exists('uri', $options)) {
256             $nb->uri = $options['uri'];
257         } else {
258             $dt = new DateTime($nb->created, new DateTimeZone('UTC'));
259
260             // I posit that it's sufficiently impossible
261             // for the same user to generate two CRC-32-clashing
262             // URLs in the same second that this is a safe unique identifier.
263             // If you find a real counterexample, contact me at acct:evan@status.net
264             // and I will publicly apologize for my hubris.
265
266             $created = $dt->format('YmdHis');
267
268             $crc32 = sprintf('%08x', $nb->url_crc32);
269
270             $nb->uri = common_local_url('showbookmark',
271                                         array('user' => $profile->id,
272                                               'created' => $created,
273                                               'crc32' => $crc32));
274         }
275
276         $nb->insert();
277
278         $tags    = array();
279         $replies = array();
280
281         // filter "for:nickname" tags
282
283         foreach ($rawtags as $tag) {
284             if (strtolower(mb_substr($tag, 0, 4)) == 'for:') {
285                 // skip if done by caller
286                 if (!array_key_exists('replies', $options)) {
287                     $nickname = mb_substr($tag, 4);
288                     $other    = common_relative_profile($profile,
289                                                         $nickname);
290                     if (!empty($other)) {
291                         $replies[] = $other->getUri();
292                     }
293                 }
294             } else {
295                 $tags[] = common_canonical_tag($tag);
296             }
297         }
298
299         $hashtags = array();
300         $taglinks = array();
301
302         foreach ($tags as $tag) {
303             $hashtags[] = '#'.$tag;
304             $attrs      = array('href' => Notice_tag::url($tag),
305                                 'rel'  => $tag,
306                                 'class' => 'tag');
307             $taglinks[] = XMLStringer::estring('a', $attrs, $tag);
308         }
309
310         // Use user's preferences for short URLs, if possible
311
312         $user = User::staticGet('id', $profile->id);
313
314         $shortUrl = File_redirection::makeShort($url, 
315                                                 empty($user) ? null : $user);
316
317         $content = sprintf(_('"%s" %s %s %s'),
318                            $title,
319                            $shortUrl,
320                            $description,
321                            implode(' ', $hashtags));
322
323         $rendered = sprintf(_('<span class="xfolkentry">'.
324                               '<a class="taggedlink" href="%s">%s</a> '.
325                               '<span class="description">%s</span> '.
326                               '<span class="meta">%s</span>'.
327                               '</span>'),
328                             htmlspecialchars($url),
329                             htmlspecialchars($title),
330                             htmlspecialchars($description),
331                             implode(' ', $taglinks));
332
333         $options = array_merge(array('urls' => array($url),
334                                      'rendered' => $rendered,
335                                      'tags' => $tags,
336                                      'replies' => $replies),
337                                $options);
338
339         if (!array_key_exists('uri', $options)) {
340             $options['uri'] = $nb->uri;
341         }
342
343         $saved = Notice::saveNew($profile->id,
344                                  $content,
345                                  array_key_exists('source', $options) ?
346                                  $options['source'] : 'web',
347                                  $options);
348
349         return $saved;
350     }
351 }