]> git.mxchange.org Git - friendica.git/blob - include/like.php
Merge pull request #2347 from annando/1602-dfrn-forum
[friendica.git] / include / like.php
1 <?php
2
3 /**
4  * @brief add/remove activity to an item
5  *
6  * Toggle activities as like,dislike,attend of an item
7  *
8  * @param string $item_id
9  * @param string $verb
10  *              Activity verb. One of
11  *                      like, unlike, dislike, undislike, attendyes, unattendyes,
12  *                      attendno, unattendno, attendmaybe, unattendmaybe
13  * @hook 'post_local_end'
14  *              array $arr
15  *                      'post_id' => ID of posted item
16  */
17 function do_like($item_id, $verb) {
18         $a = get_app();
19
20         if(! local_user() && ! remote_user()) {
21                 return false;
22         }
23
24         switch($verb) {
25                 case 'like':
26                 case 'unlike':
27                         $activity = ACTIVITY_LIKE;
28                         break;
29                 case 'dislike':
30                 case 'undislike':
31                         $activity = ACTIVITY_DISLIKE;
32                         break;
33                 case 'attendyes':
34                 case 'unattendyes':
35                         $activity = ACTIVITY_ATTEND;
36                         break;
37                 case 'attendno':
38                 case 'unattendno':
39                         $activity = ACTIVITY_ATTENDNO;
40                         break;
41                 case 'attendmaybe':
42                 case 'unattendmaybe':
43                         $activity = ACTIVITY_ATTENDMAYBE;
44                         break;
45                 default:
46                         return false;
47                         break;
48         }
49
50
51         logger('like: verb ' . $verb . ' item ' . $item_id);
52
53
54         $r = q("SELECT * FROM `item` WHERE `id` = '%s' OR `uri` = '%s' LIMIT 1",
55                 dbesc($item_id),
56                 dbesc($item_id)
57         );
58
59         if(! $item_id || (! count($r))) {
60                 logger('like: no item ' . $item_id);
61                 return false;
62         }
63
64         $item = $r[0];
65
66         $owner_uid = $item['uid'];
67
68         if(! can_write_wall($a,$owner_uid)) {
69                 return false;
70         }
71
72         $remote_owner = null;
73
74         if(! $item['wall']) {
75                 // The top level post may have been written by somebody on another system
76                 $r = q("SELECT * FROM `contact` WHERE `id` = %d AND `uid` = %d LIMIT 1",
77                         intval($item['contact-id']),
78                         intval($item['uid'])
79                 );
80                 if(! count($r))
81                         return false;
82                 if(! $r[0]['self'])
83                         $remote_owner = $r[0];
84         }
85
86         // this represents the post owner on this system.
87
88         $r = q("SELECT `contact`.*, `user`.`nickname` FROM `contact` LEFT JOIN `user` ON `contact`.`uid` = `user`.`uid`
89                 WHERE `contact`.`self` = 1 AND `contact`.`uid` = %d LIMIT 1",
90                 intval($owner_uid)
91         );
92         if(count($r))
93                 $owner = $r[0];
94
95         if(! $owner) {
96                 logger('like: no owner');
97                 return false;
98         }
99
100         if(! $remote_owner)
101                 $remote_owner = $owner;
102
103
104         // This represents the person posting
105
106         if((local_user()) && (local_user() == $owner_uid)) {
107                 $contact = $owner;
108         }
109         else {
110                 $r = q("SELECT * FROM `contact` WHERE `id` = %d AND `uid` = %d LIMIT 1",
111                         intval($_SESSION['visitor_id']),
112                         intval($owner_uid)
113                 );
114                 if(count($r))
115                         $contact = $r[0];
116         }
117         if(! $contact) {
118                 return false;
119         }
120
121
122         $verbs = " '".dbesc($activity)."' ";
123
124         // event participation are essentially radio toggles. If you make a subsequent choice,
125         // we need to eradicate your first choice.
126         if($activity === ACTIVITY_ATTEND || $activity === ACTIVITY_ATTENDNO || $activity === ACTIVITY_ATTENDMAYBE) {
127                 $verbs = " '" . dbesc(ACTIVITY_ATTEND) . "','" . dbesc(ACTIVITY_ATTENDNO) . "','" . dbesc(ACTIVITY_ATTENDMAYBE) . "' ";
128         }
129
130         $r = q("SELECT `id`, `guid` FROM `item` WHERE `verb` IN ( $verbs ) AND `deleted` = 0
131                 AND `contact-id` = %d AND `uid` = %d
132                 AND (`parent` = '%s' OR `parent-uri` = '%s' OR `thr-parent` = '%s') LIMIT 1",
133                 intval($contact['id']), intval($owner_uid),
134                 dbesc($item_id), dbesc($item_id), dbesc($item['uri'])
135         );
136
137         if(count($r)) {
138                 $like_item = $r[0];
139
140                 // Already voted, undo it
141                 $r = q("UPDATE `item` SET `deleted` = 1, `unseen` = 1, `changed` = '%s' WHERE `id` = %d",
142                         dbesc(datetime_convert()),
143                         intval($like_item['id'])
144                 );
145
146
147                 // Clean up the Diaspora signatures for this like
148                 // Go ahead and do it even if Diaspora support is disabled. We still want to clean up
149                 // if it had been enabled in the past
150                 $r = q("DELETE FROM `sign` WHERE `iid` = %d",
151                         intval($like_item['id'])
152                 );
153
154                 // Save the author information for the unlike in case we need to relay to Diaspora
155                 store_diaspora_like_retract_sig($activity, $item, $like_item, $contact);
156
157                 $like_item_id = $like_item['id'];
158                 proc_run('php',"include/notifier.php","like","$like_item_id");
159
160                 return true;
161         }
162
163         $uri = item_new_uri($a->get_hostname(),$owner_uid);
164
165         $post_type = (($item['resource-id']) ? t('photo') : t('status'));
166         if($item['obj_type'] === ACTIVITY_OBJ_EVENT)
167                 $post_type = t('event');
168         $objtype = (($item['resource-id']) ? ACTIVITY_OBJ_PHOTO : ACTIVITY_OBJ_NOTE );
169         $link = xmlify('<link rel="alternate" type="text/html" href="' . $a->get_baseurl() . '/display/' . $owner['nickname'] . '/' . $item['id'] . '" />' . "\n") ;
170         $body = $item['body'];
171
172         $obj = <<< EOT
173
174         <object>
175                 <type>$objtype</type>
176                 <local>1</local>
177                 <id>{$item['uri']}</id>
178                 <link>$link</link>
179                 <title></title>
180                 <content>$body</content>
181         </object>
182 EOT;
183         if($verb === 'like')
184                 $bodyverb = t('%1$s likes %2$s\'s %3$s');
185         if($verb === 'dislike')
186                 $bodyverb = t('%1$s doesn\'t like %2$s\'s %3$s');
187         if($verb === 'attendyes')
188                 $bodyverb = t('%1$s is attending %2$s\'s %3$s');
189         if($verb === 'attendno')
190                 $bodyverb = t('%1$s is not attending %2$s\'s %3$s');
191         if($verb === 'attendmaybe')
192                 $bodyverb = t('%1$s may attend %2$s\'s %3$s');
193
194         if(! isset($bodyverb))
195                         return false;
196
197         $arr = array();
198
199         $arr['uri'] = $uri;
200         $arr['uid'] = $owner_uid;
201         $arr['contact-id'] = $contact['id'];
202         $arr['type'] = 'activity';
203         $arr['wall'] = $item['wall'];
204         $arr['origin'] = 1;
205         $arr['gravity'] = GRAVITY_LIKE;
206         $arr['parent'] = $item['id'];
207         $arr['parent-uri'] = $item['uri'];
208         $arr['thr-parent'] = $item['uri'];
209         $arr['owner-name'] = $remote_owner['name'];
210         $arr['owner-link'] = $remote_owner['url'];
211         $arr['owner-avatar'] = $remote_owner['thumb'];
212         $arr['author-name'] = $contact['name'];
213         $arr['author-link'] = $contact['url'];
214         $arr['author-avatar'] = $contact['thumb'];
215
216         $ulink = '[url=' . $contact['url'] . ']' . $contact['name'] . '[/url]';
217         $alink = '[url=' . $item['author-link'] . ']' . $item['author-name'] . '[/url]';
218         $plink = '[url=' . $a->get_baseurl() . '/display/' . $owner['nickname'] . '/' . $item['id'] . ']' . $post_type . '[/url]';
219         $arr['body'] =  sprintf( $bodyverb, $ulink, $alink, $plink );
220
221         $arr['verb'] = $activity;
222         $arr['object-type'] = $objtype;
223         $arr['object'] = $obj;
224         $arr['allow_cid'] = $item['allow_cid'];
225         $arr['allow_gid'] = $item['allow_gid'];
226         $arr['deny_cid'] = $item['deny_cid'];
227         $arr['deny_gid'] = $item['deny_gid'];
228         $arr['visible'] = 1;
229         $arr['unseen'] = 1;
230         $arr['last-child'] = 0;
231
232         $post_id = item_store($arr);
233
234         if(! $item['visible']) {
235                 $r = q("UPDATE `item` SET `visible` = 1 WHERE `id` = %d AND `uid` = %d",
236                         intval($item['id']),
237                         intval($owner_uid)
238                 );
239         }
240
241
242         // Save the author information for the like in case we need to relay to Diaspora
243         store_diaspora_like_sig($activity, $post_type, $contact, $post_id);
244
245         $arr['id'] = $post_id;
246
247         call_hooks('post_local_end', $arr);
248
249         proc_run('php',"include/notifier.php","like","$post_id");
250
251         return true;
252 }
253
254
255
256 function store_diaspora_like_retract_sig($activity, $item, $like_item, $contact) {
257         // Note that we can only create a signature for a user of the local server. We don't have
258         // a key for remote users. That is ok, because if a remote user is "unlike"ing a post, it
259         // means we are the relay, and for relayable_retractions, Diaspora
260         // only checks the parent_author_signature if it doesn't have to relay further
261         //
262         // If $item['resource-id'] exists, it means the item is a photo. Diaspora doesn't support
263         // likes on photos, so don't bother.
264
265         $enabled = intval(get_config('system','diaspora_enabled'));
266         if(! $enabled) {
267                 logger('mod_like: diaspora support disabled, not storing like retraction signature', LOGGER_DEBUG);
268                 return;
269         }
270
271         logger('mod_like: storing diaspora like retraction signature');
272
273         if(($activity === ACTIVITY_LIKE) && (! $item['resource-id'])) {
274                 $signed_text = $like_item['guid'] . ';' . 'Like';
275
276                 // Only works for NETWORK_DFRN
277                 $contact_baseurl_start = strpos($contact['url'],'://') + 3;
278                 $contact_baseurl_length = strpos($contact['url'],'/profile') - $contact_baseurl_start;
279                 $contact_baseurl = substr($contact['url'], $contact_baseurl_start, $contact_baseurl_length);
280                 $diaspora_handle = $contact['nick'] . '@' . $contact_baseurl;
281
282                 // This code could never had worked (the return values form the queries were used in a wrong way.
283                 // Additionally it is needlessly complicated. Either the contact is owner or not. And we have this data already.
284 /*
285                 // Get contact's private key if he's a user of the local Friendica server
286                 $r = q("SELECT `contact`.`uid` FROM `contact` WHERE `url` = '%s' AND `self` = 1 LIMIT 1",
287                         dbesc($contact['url'])
288                 );
289
290                 if( $r) {
291                         $contact_uid = $r['uid'];
292                         $r = q("SELECT prvkey FROM user WHERE uid = %d LIMIT 1",
293                                 intval($contact_uid)
294                         );
295 */
296                 // Is the contact the owner? Then fetch the private key
297                 if ($contact['self'] AND ($contact['uid'] > 0)) {
298                         $r = q("SELECT prvkey FROM user WHERE uid = %d LIMIT 1",
299                                 intval($contact['uid'])
300                         );
301
302                         if($r)
303                                 $authorsig = base64_encode(rsa_sign($signed_text,$r[0]['prvkey'],'sha256'));
304                 }
305
306                 if(! isset($authorsig))
307                         $authorsig = '';
308
309                 q("insert into sign (`retract_iid`,`signed_text`,`signature`,`signer`) values (%d,'%s','%s','%s') ",
310                         intval($like_item['id']),
311                         dbesc($signed_text),
312                         dbesc($authorsig),
313                         dbesc($diaspora_handle)
314                 );
315         }
316
317         return;
318 }
319
320 function store_diaspora_like_sig($activity, $post_type, $contact, $post_id) {
321         // Note that we can only create a signature for a user of the local server. We don't have
322         // a key for remote users. That is ok, because if a remote user is "unlike"ing a post, it
323         // means we are the relay, and for relayable_retractions, Diaspora
324         // only checks the parent_author_signature if it doesn't have to relay further
325
326         $enabled = intval(get_config('system','diaspora_enabled'));
327         if(! $enabled) {
328                 logger('mod_like: diaspora support disabled, not storing like signature', LOGGER_DEBUG);
329                 return;
330         }
331
332         logger('mod_like: storing diaspora like signature');
333
334         if(($activity === ACTIVITY_LIKE) && ($post_type === t('status'))) {
335                 // Only works for NETWORK_DFRN
336                 $contact_baseurl_start = strpos($contact['url'],'://') + 3;
337                 $contact_baseurl_length = strpos($contact['url'],'/profile') - $contact_baseurl_start;
338                 $contact_baseurl = substr($contact['url'], $contact_baseurl_start, $contact_baseurl_length);
339                 $diaspora_handle = $contact['nick'] . '@' . $contact_baseurl;
340
341
342                 // This code could never had worked (the return values form the queries were used in a wrong way.
343                 // Additionally it is needlessly complicated. Either the contact is owner or not. And we have this data already.
344 /*
345                 // Get contact's private key if he's a user of the local Friendica server
346                 $r = q("SELECT `contact`.`uid` FROM `contact` WHERE `url` = '%s' AND `self` = 1 LIMIT 1",
347                         dbesc($contact['url'])
348                 );
349
350                 if( $r) {
351                         $contact_uid = $r['uid'];
352                         $r = q("SELECT prvkey FROM user WHERE uid = %d LIMIT 1",
353                                 intval($contact_uid)
354                         );
355
356                         if( $r)
357                                 $contact_uprvkey = $r['prvkey'];
358                 }
359 */
360
361                 // Is the contact the owner? Then fetch the private key
362                 if ($contact['self'] AND ($contact['uid'] > 0)) {
363                         $r = q("SELECT prvkey FROM user WHERE uid = %d LIMIT 1",
364                                 intval($contact['uid'])
365                         );
366
367                         if($r)
368                                 $contact_uprvkey = $r[0]['prvkey'];
369                 }
370
371                 $r = q("SELECT guid, parent FROM `item` WHERE id = %d LIMIT 1",
372                         intval($post_id)
373                 );
374                 if( $r) {
375                         $p = q("SELECT guid FROM `item` WHERE id = %d AND parent = %d LIMIT 1",
376                                 intval($r[0]['parent']),
377                                 intval($r[0]['parent'])
378                         );
379                         if( $p) {
380                                 $signed_text = 'true;'.$r[0]['guid'].';Post;'.$p[0]['guid'].';'.$diaspora_handle;
381
382                                 if(isset($contact_uprvkey))
383                                         $authorsig = base64_encode(rsa_sign($signed_text,$contact_uprvkey,'sha256'));
384                                 else
385                                         $authorsig = '';
386
387                                 q("insert into sign (`iid`,`signed_text`,`signature`,`signer`) values (%d,'%s','%s','%s') ",
388                                         intval($post_id),
389                                         dbesc($signed_text),
390                                         dbesc($authorsig),
391                                         dbesc($diaspora_handle)
392                                 );
393                         }
394                 }
395         }
396
397         return;
398 }