]> git.mxchange.org Git - friendica-addons.git/blob - facebook/facebook.php
queue api
[friendica-addons.git] / facebook / facebook.php
1 <?php
2 /**
3  * Name: Facebook Connector
4  * Version: 1.0
5  * Author: Mike Macgirvin <http://macgirvin.com/profile/mike>
6  */
7
8 /**
9  * Installing the Friendika/Facebook connector
10  *
11  * 1. register an API key for your site from developer.facebook.com
12  *   a. We'd be very happy if you include "Friendika" in the application name
13  *      to increase name recognition. The Friendika icons are also present
14  *      in the images directory and may be uploaded as a Facebook app icon.
15  *      Use images/friendika-16.jpg for the Icon and images/friendika-128.jpg for the Logo.
16  *   b. The url should be your site URL with a trailing slash.
17  *      You may use http://portal.friendika.com/privacy as the privacy policy
18  *      URL unless your site has different requirements, and 
19  *      http://portal.friendika.com as the Terms of Service URL unless
20  *      you have different requirements. (Friendika is a software application
21  *      and does not require Terms of Service, though your installation of it might).
22  *   c. Set the following values in your .htconfig.php file
23  *         $a->config['facebook']['appid'] = 'xxxxxxxxxxx';
24  *         $a->config['facebook']['appsecret'] = 'xxxxxxxxxxxxxxx';
25  *      Replace with the settings Facebook gives you.
26  *   d. Navigate to Set Web->Site URL & Domain -> Website Settings.  Set 
27  *      Site URL to yoursubdomain.yourdomain.com. Set Site Domain to your 
28  *      yourdomain.com.
29  * 2. Enable the facebook plugin by including it in .htconfig.php - e.g. 
30  *     $a->config['system']['addon'] = 'plugin1,plugin2,facebook';
31  * 3. Visit the Facebook Settings section of the "Settings->Plugin Settings" page.
32  *    and click 'Install Facebook Connector'.
33  * 4. This will ask you to login to Facebook and grant permission to the 
34  *    plugin to do its stuff. Allow it to do so. 
35  * 5. You're done. To turn it off visit the Plugin Settings page again and
36  *    'Remove Facebook posting'.
37  *
38  * Vidoes and embeds will not be posted if there is no other content. Links 
39  * and images will be converted to a format suitable for the Facebook API and 
40  * long posts truncated - with a link to view the full post. 
41  *
42  * Facebook contacts will not be able to view private photos, as they are not able to
43  * authenticate to your site to establish identity. We will address this 
44  * in a future release.
45  */
46
47 define('FACEBOOK_MAXPOSTLEN', 420);
48
49
50 function facebook_install() {
51         register_hook('post_local',       'addon/facebook/facebook.php', 'facebook_post_local');
52         register_hook('notifier_normal',  'addon/facebook/facebook.php', 'facebook_post_hook');
53         register_hook('jot_networks',     'addon/facebook/facebook.php', 'facebook_jot_nets');
54         register_hook('connector_settings',  'addon/facebook/facebook.php', 'facebook_plugin_settings');
55         register_hook('cron',             'addon/facebook/facebook.php', 'facebook_cron');
56         register_hook('queue_predeliver', 'addon/facebook/facebook.php', 'fb_queue_hook');
57 }
58
59
60 function facebook_uninstall() {
61         unregister_hook('post_local',       'addon/facebook/facebook.php', 'facebook_post_local');
62         unregister_hook('notifier_normal',  'addon/facebook/facebook.php', 'facebook_post_hook');
63         unregister_hook('jot_networks',     'addon/facebook/facebook.php', 'facebook_jot_nets');
64         unregister_hook('connector_settings',  'addon/facebook/facebook.php', 'facebook_plugin_settings');
65         unregister_hook('cron',             'addon/facebook/facebook.php', 'facebook_cron');
66         unregister_hook('queue_predeliver', 'addon/facebook/facebook.php', 'fb_queue_hook');
67
68         // hook moved
69         unregister_hook('post_local_end',  'addon/facebook/facebook.php', 'facebook_post_hook');
70         unregister_hook('plugin_settings',  'addon/facebook/facebook.php', 'facebook_plugin_settings');
71 }
72
73
74 /* declare the facebook_module function so that /facebook url requests will land here */
75
76 function facebook_module() {}
77
78
79
80 /* If a->argv[1] is a nickname, this is a callback from Facebook oauth requests. */
81
82 function facebook_init(&$a) {
83
84         if($a->argc != 2)
85                 return;
86         $nick = $a->argv[1];
87         if(strlen($nick))
88                 $r = q("SELECT `uid` FROM `user` WHERE `nickname` = '%s' LIMIT 1",
89                                 dbesc($nick)
90                 );
91         if(! count($r))
92                 return;
93
94         $uid           = $r[0]['uid'];
95         $auth_code     = (($_GET['code']) ? $_GET['code'] : '');
96         $error         = (($_GET['error_description']) ? $_GET['error_description'] : '');
97
98
99         if($error)
100                 logger('facebook_init: Error: ' . $error);
101
102         if($auth_code && $uid) {
103
104                 $appid = get_config('facebook','appid');
105                 $appsecret = get_config('facebook', 'appsecret');
106
107                 $x = fetch_url('https://graph.facebook.com/oauth/access_token?client_id='
108                         . $appid . '&client_secret=' . $appsecret . '&redirect_uri='
109                         . urlencode($a->get_baseurl() . '/facebook/' . $nick) 
110                         . '&code=' . $auth_code);
111
112                 logger('facebook_init: returned access token: ' . $x, LOGGER_DATA);
113
114                 if(strpos($x,'access_token=') !== false) {
115                         $token = str_replace('access_token=', '', $x);
116                         if(strpos($token,'&') !== false)
117                                 $token = substr($token,0,strpos($token,'&'));
118                         set_pconfig($uid,'facebook','access_token',$token);
119                         set_pconfig($uid,'facebook','post','1');
120                         if(get_pconfig($uid,'facebook','no_linking') === false)
121                                 set_pconfig($uid,'facebook','no_linking',1);
122                         fb_get_self($uid);
123                         fb_get_friends($uid);
124                         fb_consume_all($uid);
125
126                 }
127
128         }
129
130 }
131
132
133 function fb_get_self($uid) {
134         $access_token = get_pconfig($uid,'facebook','access_token');
135         if(! $access_token)
136                 return;
137         $s = fetch_url('https://graph.facebook.com/me/?access_token=' . $access_token);
138         if($s) {
139                 $j = json_decode($s);
140                 set_pconfig($uid,'facebook','self_id',(string) $j->id);
141         }
142 }
143
144
145
146 function fb_get_friends($uid) {
147
148         $r = q("SELECT `uid` FROM `user` WHERE `uid` = %d AND `account_expired` = 0 LIMIT 1",
149                 intval($uid)
150         );
151         if(! count($r))
152                 return;
153
154         $access_token = get_pconfig($uid,'facebook','access_token');
155
156         $no_linking = get_pconfig($uid,'facebook','no_linking');
157         if($no_linking)
158                 return;
159
160         if(! $access_token)
161                 return;
162         $s = fetch_url('https://graph.facebook.com/me/friends?access_token=' . $access_token);
163         if($s) {
164                 logger('facebook: fb_get_friends: ' . $s, LOGGER_DATA);
165                 $j = json_decode($s);
166                 logger('facebook: fb_get_friends: json: ' . print_r($j,true), LOGGER_DATA);
167                 if(! $j->data)
168                         return;
169                 foreach($j->data as $person) {
170                         $s = fetch_url('https://graph.facebook.com/' . $person->id . '?access_token=' . $access_token);
171                         if($s) {
172                                 $jp = json_decode($s);
173                                 logger('fb_get_friends: info: ' . print_r($jp,true), LOGGER_DATA);
174
175                                 // always use numeric link for consistency
176
177                                 $jp->link = 'http://facebook.com/profile.php?id=' . $person->id;
178
179                                 // check if we already have a contact
180
181                                 $r = q("SELECT * FROM `contact` WHERE `uid` = %d AND `url` = '%s' LIMIT 1",
182                                         intval($uid),
183                                         dbesc($jp->link)
184                                 );                      
185
186                                 if(count($r)) {
187
188                                         // check that we have all the photos, this has been known to fail on occasion
189
190                                         if((! $r[0]['photo']) || (! $r[0]['thumb']) || (! $r[0]['micro'])) {  
191                                                 require_once("Photo.php");
192
193                                                 $photos = import_profile_photo('https://graph.facebook.com/' . $jp->id . '/picture', $uid, $r[0]['id']);
194
195                                                 $r = q("UPDATE `contact` SET `photo` = '%s', 
196                                                         `thumb` = '%s',
197                                                         `micro` = '%s', 
198                                                         `name-date` = '%s', 
199                                                         `uri-date` = '%s', 
200                                                         `avatar-date` = '%s'
201                                                         WHERE `id` = %d LIMIT 1
202                                                 ",
203                                                         dbesc($photos[0]),
204                                                         dbesc($photos[1]),
205                                                         dbesc($photos[2]),
206                                                         dbesc(datetime_convert()),
207                                                         dbesc(datetime_convert()),
208                                                         dbesc(datetime_convert()),
209                                                         intval($r[0]['id'])
210                                                 );                      
211                                         }       
212                                         continue;
213                                 }
214                                 else {
215
216                                         // create contact record 
217                                         $r = q("INSERT INTO `contact` ( `uid`, `created`, `url`, `addr`, `alias`, `notify`, `poll`, 
218                                                 `name`, `nick`, `photo`, `network`, `rel`, `priority`,
219                                                 `writable`, `blocked`, `readonly`, `pending` )
220                                                 VALUES ( %d, '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', %d, %d, %d, 0, 0, 0 ) ",
221                                                 intval($uid),
222                                                 dbesc(datetime_convert()),
223                                                 dbesc($jp->link),
224                                                 dbesc(''),
225                                                 dbesc(''),
226                                                 dbesc($jp->id),
227                                                 dbesc('facebook ' . $jp->id),
228                                                 dbesc($jp->name),
229                                                 dbesc(($jp->nickname) ? $jp->nickname : strtolower($jp->first_name)),
230                                                 dbesc('https://graph.facebook.com/' . $jp->id . '/picture'),
231                                                 dbesc(NETWORK_FACEBOOK),
232                                                 intval(CONTACT_IS_FRIEND),
233                                                 intval(1),
234                                                 intval(1)
235                                         );
236                                 }
237
238                                 $r = q("SELECT * FROM `contact` WHERE `url` = '%s' AND `uid` = %d LIMIT 1",
239                                         dbesc($jp->link),
240                                         intval($uid)
241                                 );
242
243                                 if(! count($r)) {
244                                         continue;
245                                 }
246
247                                 $contact = $r[0];
248                                 $contact_id  = $r[0]['id'];
249
250                                 require_once("Photo.php");
251
252                                 $photos = import_profile_photo($r[0]['photo'],$uid,$contact_id);
253
254                                 $r = q("UPDATE `contact` SET `photo` = '%s', 
255                                         `thumb` = '%s',
256                                         `micro` = '%s', 
257                                         `name-date` = '%s', 
258                                         `uri-date` = '%s', 
259                                         `avatar-date` = '%s'
260                                         WHERE `id` = %d LIMIT 1
261                                 ",
262                                         dbesc($photos[0]),
263                                         dbesc($photos[1]),
264                                         dbesc($photos[2]),
265                                         dbesc(datetime_convert()),
266                                         dbesc(datetime_convert()),
267                                         dbesc(datetime_convert()),
268                                         intval($contact_id)
269                                 );                      
270
271                         }
272                 }
273         }
274 }
275
276 // This is the POST method to the facebook settings page
277 // Content is posted to Facebook in the function facebook_post_hook() 
278
279 function facebook_post(&$a) {
280
281         $uid = local_user();
282         if($uid){
283
284                 $value = ((x($_POST,'post_by_default')) ? intval($_POST['post_by_default']) : 0);
285                 set_pconfig($uid,'facebook','post_by_default', $value);
286
287                 $no_linking = get_pconfig($uid,'facebook','no_linking');
288
289                 $no_wall = ((x($_POST,'facebook_no_wall')) ? intval($_POST['facebook_no_wall']) : 0);
290                 set_pconfig($uid,'facebook','no_wall',$no_wall);
291
292                 $private_wall = ((x($_POST,'facebook_private_wall')) ? intval($_POST['facebook_private_wall']) : 0);
293                 set_pconfig($uid,'facebook','private_wall',$private_wall);
294         
295
296                 $linkvalue = ((x($_POST,'facebook_linking')) ? intval($_POST['facebook_linking']) : 0);
297                 set_pconfig($uid,'facebook','no_linking', (($linkvalue) ? 0 : 1));
298
299                 // FB linkage was allowed but has just been turned off - remove all FB contacts and posts
300
301                 if((! intval($no_linking)) && (! intval($linkvalue))) {
302                         $r = q("SELECT `id` FROM `contact` WHERE `uid` = %d AND `network` = '%s' ",
303                                 intval($uid),
304                                 dbesc(NETWORK_FACEBOOK)
305                         );
306                         if(count($r)) {
307                                 require_once('include/Contact.php');
308                                 foreach($r as $rr)
309                                         contact_remove($rr['id']);
310                         }
311                 }
312                 elseif(intval($no_linking) && intval($linkvalue)) {
313                         // FB linkage is now allowed - import stuff.
314                         fb_get_self($uid);
315                         fb_get_friends($uid);
316                         fb_consume_all($uid);
317                 }
318
319                 info( t('Settings updated.') . EOL);
320         } 
321
322         return;         
323 }
324
325 // Facebook settings form
326
327 function facebook_content(&$a) {
328
329         if(! local_user()) {
330                 notice( t('Permission denied.') . EOL);
331                 return '';
332         }
333
334         if($a->argc > 1 && $a->argv[1] === 'remove') {
335                 del_pconfig(local_user(),'facebook','post');
336                 info( t('Facebook disabled') . EOL);
337         }
338
339         if($a->argc > 1 && $a->argv[1] === 'friends') {
340                 fb_get_friends(local_user());
341                 info( t('Updating contacts') . EOL);
342         }
343
344
345         $fb_installed = get_pconfig(local_user(),'facebook','post');
346
347         $appid = get_config('facebook','appid');
348
349         if(! $appid) {
350                 notice( t('Facebook API key is missing.') . EOL);
351                 return '';
352         }
353
354         $a->page['htmlhead'] .= '<link rel="stylesheet" type="text/css" href="' 
355                 . $a->get_baseurl() . '/addon/facebook/facebook.css' . '" media="all" />' . "\r\n";
356
357         $o .= '<h3>' . t('Facebook Connect') . '</h3>';
358
359         if(! $fb_installed) { 
360                 $o .= '<div id="facebook-enable-wrapper">';
361
362                 $o .= '<a href="https://www.facebook.com/dialog/oauth?client_id=' . $appid . '&redirect_uri=' 
363                         . $a->get_baseurl() . '/facebook/' . $a->user['nickname'] . '&scope=publish_stream,read_stream,offline_access">' . t('Install Facebook connector for this account.') . '</a>';
364                 $o .= '</div>';
365         }
366
367         if($fb_installed) {
368                 $o .= '<div id="facebook-disable-wrapper">';
369
370                 $o .= '<a href="' . $a->get_baseurl() . '/facebook/remove' . '">' . t('Remove Facebook connector') . '</a></div>';
371
372                 $o .= '<div id="facebook-enable-wrapper">';
373
374                 $o .= '<a href="https://www.facebook.com/dialog/oauth?client_id=' . $appid . '&redirect_uri=' 
375                         . $a->get_baseurl() . '/facebook/' . $a->user['nickname'] . '&scope=publish_stream,read_stream,offline_access">' . t('Re-authenticate [This is necessary whenever your Facebook password is changed.]') . '</a>';
376                 $o .= '</div>';
377         
378                 $o .= '<div id="facebook-post-default-form">';
379                 $o .= '<form action="facebook" method="post" >';
380                 $post_by_default = get_pconfig(local_user(),'facebook','post_by_default');
381                 $checked = (($post_by_default) ? ' checked="checked" ' : '');
382                 $o .= '<input type="checkbox" name="post_by_default" value="1"' . $checked . '/>' . ' ' . t('Post to Facebook by default') . EOL;
383
384                 $no_linking = get_pconfig(local_user(),'facebook','no_linking');
385                 $checked = (($no_linking) ? '' : ' checked="checked" ');
386                 $o .= '<input type="checkbox" name="facebook_linking" value="1"' . $checked . '/>' . ' ' . t('Link all your Facebook friends and conversations on this website') . EOL ;
387
388                 $o .= '<p>' . t('Facebook conversations consist of your <em>profile wall</em> and your friend <em>stream</em>.');
389                 $o .= ' ' . t('On this website, your Facebook friend stream is only visible to you.');
390                 $o .= ' ' . t('The following settings determine the privacy of your Facebook profile wall on this website.') . '</p>';
391
392                 $private_wall = get_pconfig(local_user(),'facebook','private_wall');
393                 $checked = (($private_wall) ? ' checked="checked" ' : '');
394                 $o .= '<input type="checkbox" name="facebook_private_wall" value="1"' . $checked . '/>' . ' ' . t('On this website your Facebook profile wall conversations will only be visible to you') . EOL ;
395
396
397                 $no_wall = get_pconfig(local_user(),'facebook','no_wall');
398                 $checked = (($no_wall) ? ' checked="checked" ' : '');
399                 $o .= '<input type="checkbox" name="facebook_no_wall" value="1"' . $checked . '/>' . ' ' . t('Do not import your Facebook profile wall conversations') . EOL ;
400
401                 $o .= '<p>' . t('If you choose to link conversations and leave both of these boxes unchecked, your Facebook profile wall will be merged with your profile wall on this website and your privacy settings on this website will be used to determine who may see the conversations.') . '</p>';
402
403                 $o .= '<input type="submit" name="submit" value="' . t('Submit') . '" /></form></div>';
404         }
405
406         return $o;
407 }
408
409
410
411 function facebook_cron($a,$b) {
412
413         $last = get_config('facebook','last_poll');
414         
415         $poll_interval = intval(get_config('facebook','poll_interval'));
416         if(! $poll_interval)
417                 $poll_interval = 3600;
418
419         if($last) {
420                 $next = $last + $poll_interval;
421                 if($next > time()) 
422                         return;
423         }
424
425         logger('facebook_cron');
426
427
428         // Find the FB users on this site and randomize in case one of them
429         // uses an obscene amount of memory. It may kill this queue run
430         // but hopefully we'll get a few others through on each run. 
431
432         $r = q("SELECT * FROM `pconfig` WHERE `cat` = 'facebook' AND `k` = 'post' AND `v` = '1' ORDER BY RAND() ");
433         if(count($r)) {
434                 foreach($r as $rr) {
435                         if(get_pconfig($rr['uid'],'facebook','no_linking'))
436                                 continue;
437                         $ab = intval(get_config('system','account_abandon_days'));
438                         if($ab > 0) {
439                                 $z = q("SELECT `uid` FROM `user` WHERE `uid` = %d AND `login_date` > UTC_TIMESTAMP() - INTERVAL %d DAY LIMIT 1",
440                                         intval($rr['uid']),
441                                         intval($ab)
442                                 );
443                                 if(! count($z))
444                                         continue;
445                         }
446
447                         // check for new friends once a day
448                         $last_friend_check = get_pconfig($rr['uid'],'facebook','friend_check');
449                         if($last_friend_check) 
450                                 $next_friend_check = $last_friend_check + 86400;
451                         if($next_friend_check <= time()) {
452                                 fb_get_friends($rr['uid']);
453                                 set_pconfig($rr['uid'],'facebook','friend_check',time());
454                         }
455                         fb_consume_all($rr['uid']);
456                 }
457         }       
458
459         set_config('facebook','last_poll', time());
460
461 }
462
463
464
465 function facebook_plugin_settings(&$a,&$b) {
466
467         $b .= '<div class="settings-block">';
468         $b .= '<h3>' . t('Facebook') . '</h3>';
469         $b .= '<a href="facebook">' . t('Facebook Connector Settings') . '</a><br />';
470         $b .= '</div>';
471
472 }
473
474 function facebook_jot_nets(&$a,&$b) {
475         if(! local_user())
476                 return;
477
478         $fb_post = get_pconfig(local_user(),'facebook','post');
479         if(intval($fb_post) == 1) {
480                 $fb_defpost = get_pconfig(local_user(),'facebook','post_by_default');
481                 $selected = ((intval($fb_defpost) == 1) ? ' checked="checked" ' : '');
482                 $b .= '<div class="profile-jot-net"><input type="checkbox" name="facebook_enable"' . $selected . ' value="1" /> ' 
483                         . t('Post to Facebook') . '</div>';     
484         }
485 }
486
487
488 function facebook_post_hook(&$a,&$b) {
489
490
491         if($b['deleted'] || ($b['created'] !== $b['edited']))
492                 return;
493
494         /**
495          * Post to Facebook stream
496          */
497
498         require_once('include/group.php');
499
500         logger('Facebook post');
501
502         $reply = false;
503         $likes = false;
504
505         $toplevel = (($b['id'] == $b['parent']) ? true : false);
506
507         if(strstr($b['postopts'],'facebook')) {
508
509                 $linking = ((get_pconfig($b['uid'],'facebook','no_linking')) ? 0 : 1);
510
511                 if((! $toplevel) && ($linking)) {
512                         $r = q("SELECT * FROM `item` WHERE `id` = %d AND `uid` = %d LIMIT 1",
513                                 intval($b['parent']),
514                                 intval($b['uid'])
515                         );
516                         if(count($r) && substr($r[0]['uri'],0,4) === 'fb::')
517                                 $reply = substr($r[0]['uri'],4);
518                         elseif(count($r) && substr($r[0]['extid'],0,4) === 'fb::')
519                                 $reply = substr($r[0]['extid'],4);
520                         else
521                                 return;
522                         logger('facebook reply id=' . $reply);
523                 }
524
525                 if($b['private'] && $reply === false) {
526                         $allow_people = expand_acl($b['allow_cid']);
527                         $allow_groups = expand_groups(expand_acl($b['allow_gid']));
528                         $deny_people  = expand_acl($b['deny_cid']);
529                         $deny_groups  = expand_groups(expand_acl($b['deny_gid']));
530
531                         $recipients = array_unique(array_merge($allow_people,$allow_groups));
532                         $deny = array_unique(array_merge($deny_people,$deny_groups));
533
534                         $allow_str = dbesc(implode(', ',$recipients));
535                         if($allow_str) {
536                                 $r = q("SELECT `notify` FROM `contact` WHERE `id` IN ( $allow_str ) AND `network` = 'face'"); 
537                                 $allow_arr = array();
538                                 if(count($r)) 
539                                         foreach($r as $rr)
540                                                 $allow_arr[] = $rr['notify'];
541                         }
542
543                         $deny_str = dbesc(implode(', ',$deny));
544                         if($deny_str) {
545                                 $r = q("SELECT `notify` FROM `contact` WHERE `id` IN ( $deny_str ) AND `network` = 'face'"); 
546                                 $deny_arr = array();
547                                 if(count($r)) 
548                                         foreach($r as $rr)
549                                                 $deny_arr[] = $rr['notify'];
550                         }
551
552                         if(count($deny_arr) && (! count($allow_arr))) {
553
554                                 // One or more FB folks were denied access but nobody on FB was specifically allowed access.
555                                 // This might cause the post to be open to public on Facebook, but only to selected members
556                                 // on another network. Since this could potentially leak a post to somebody who was denied, 
557                                 // we will skip posting it to Facebook with a slightly vague but relevant message that will 
558                                 // hopefully lead somebody to this code comment for a better explanation of what went wrong.
559
560                                 notice( t('Post to Facebook cancelled because of multi-network access permission conflict.') . EOL);
561                                 return;
562                         }
563
564
565                         // if it's a private message but no Facebook members are allowed or denied, skip Facebook post
566
567                         if((! count($allow_arr)) && (! count($deny_arr)))
568                                 return;
569                 }
570
571                 if($b['verb'] == ACTIVITY_LIKE)
572                         $likes = true;                          
573
574
575                 $appid  = get_config('facebook', 'appid'  );
576                 $secret = get_config('facebook', 'appsecret' );
577
578                 if($appid && $secret) {
579
580                         logger('facebook: have appid+secret');
581
582                         $fb_token  = get_pconfig($b['uid'],'facebook','access_token');
583
584
585                         // post to facebook if it's a public post and we've ticked the 'post to Facebook' box, 
586                         // or it's a private message with facebook participants
587                         // or it's a reply or likes action to an existing facebook post                 
588
589                         if($fb_token && ($toplevel || $b['private'] || $reply)) {
590                                 logger('facebook: able to post');
591                                 require_once('library/facebook.php');
592                                 require_once('include/bbcode.php');     
593
594                                 $msg = $b['body'];
595
596                                 logger('Facebook post: original msg=' . $msg, LOGGER_DATA);
597
598                                 // make links readable before we strip the code
599
600                                 // unless it's a dislike - just send the text as a comment
601
602                                 if($b['verb'] == ACTIVITY_DISLIKE)
603                                         $msg = trim(strip_tags(bbcode($msg)));
604
605                                 $search_str = $a->get_baseurl() . '/search';
606
607                                 if(preg_match("/\[url=(.*?)\](.*?)\[\/url\]/is",$msg,$matches)) {
608
609                                         // don't use hashtags for message link
610
611                                         if(strpos($matches[2],$search_str) === false) {
612                                                 $link = $matches[1];
613                                                 if(substr($matches[2],0,5) != '[img]')
614                                                         $linkname = $matches[2];
615                                         }
616                                 }
617
618                                 $msg = preg_replace("/\[url=(.*?)\](.*?)\[\/url\]/is",'$2 $1',$msg);
619
620                                 if(preg_match("/\[img\](.*?)\[\/img\]/is",$msg,$matches))
621                                         $image = $matches[1];
622
623                                 $msg = preg_replace("/\[img\](.*?)\[\/img\]/is", t('Image: ') . '$1', $msg);
624
625                                 if((strpos($link,z_root()) !== false) && (! $image))
626                                         $image = $a->get_baseurl() . '/images/friendika-64.jpg';
627
628                                 $msg = trim(strip_tags(bbcode($msg)));
629                                 $msg = html_entity_decode($msg,ENT_QUOTES,'UTF-8');
630
631                                 // add any attachments as text urls
632
633                             $arr = explode(',',$b['attach']);
634
635                             if(count($arr)) {
636                                         $msg .= "\n";
637                                 foreach($arr as $r) {
638                                 $matches = false;
639                                                 $cnt = preg_match('|\[attach\]href=\"(.*?)\" size=\"(.*?)\" type=\"(.*?)\" title=\"(.*?)\"\[\/attach\]|',$r,$matches);
640                                                 if($cnt) {
641                                                         $msg .= $matches[1];
642                                                 }
643                                         }
644                                 }
645
646                                 if (strlen($msg) > FACEBOOK_MAXPOSTLEN) {
647                                         $shortlink = "";
648                                         require_once('library/slinky.php');
649
650                                         $display_url = $a->get_baseurl() . '/display/' . $a->user['nickname'] . '/' . $b['id'];
651                                         $slinky = new Slinky( $display_url );
652                                         // setup a cascade of shortening services
653                                         // try to get a short link from these services
654                                         // in the order ur1.ca, trim, id.gd, tinyurl
655                                         $slinky->set_cascade( array( new Slinky_UR1ca(), new Slinky_Trim(), new Slinky_IsGd(), new Slinky_TinyURL() ) );
656                                         $shortlink = $slinky->short();
657                                         // the new message will be shortened such that "... $shortlink"
658                                         // will fit into the character limit
659                                         $msg = substr($msg, 0, FACEBOOK_MAXPOSTLEN - strlen($shortlink) - 4);
660                                         $msg .= '... ' . $shortlink;
661                                 }
662                                 if(! strlen($msg))
663                                         return;
664
665                                 logger('Facebook post: msg=' . $msg, LOGGER_DATA);
666
667                                 if($likes) { 
668                                         $postvars = array('access_token' => $fb_token);
669                                 }
670                                 else {
671                                         $postvars = array(
672                                                 'access_token' => $fb_token, 
673                                                 'message' => $msg
674                                         );
675                                         if(isset($image))
676                                                 $postvars['picture'] = $image;
677                                         if(isset($link))
678                                                 $postvars['link'] = $link;
679                                         if(isset($linkname))
680                                                 $postvars['name'] = $linkname;
681                                 }
682
683                                 if(($b['private']) && (! $b['parent'])) {
684                                         $postvars['privacy'] = '{"value": "CUSTOM", "friends": "SOME_FRIENDS"';
685                                         if(count($allow_arr))
686                                                 $postvars['privacy'] .= ',"allow": "' . implode(',',$allow_arr) . '"';
687                                         if(count($deny_arr))
688                                                 $postvars['privacy'] .= ',"deny": "' . implode(',',$deny_arr) . '"';
689                                         $postvars['privacy'] .= '}';
690
691                                 }
692
693                                 if($reply) {
694                                         $url = 'https://graph.facebook.com/' . $reply . '/' . (($likes) ? 'likes' : 'comments');
695                                 }
696                                 else { 
697                                         $url = 'https://graph.facebook.com/me/feed';
698                                         if($b['plink'])
699                                                 $postvars['actions'] = '{"name": "' . t('View on Friendika') . '", "link": "' .  $b['plink'] . '"}';
700                                 }
701
702                                 logger('facebook: post to ' . $url);
703                                 logger('facebook: postvars: ' . print_r($postvars,true));
704
705                                 // "test_mode" prevents anything from actually being posted.
706                                 // Otherwise, let's do it. 
707
708                                 if(! get_config('facebook','test_mode')) {
709                                         $x = post_url($url, $postvars);
710
711                                         $retj = json_decode($x);
712                                         if($retj->id) {
713                                                 q("UPDATE `item` SET `extid` = '%s' WHERE `id` = %d LIMIT 1",
714                                                         dbesc('fb::' . $retj->id),
715                                                         intval($b['id'])
716                                                 );
717                                         }
718                                         else {
719                                                 if(! $likes) {
720                                                         $s = serialize(array('url' => $url, 'item' => $b['id'], 'post' => $postvars));
721                                                         require_once('include/queue_fn.php');
722                                                         add_to_queue($a->contact,NETWORK_FACEBOOK,$s);
723                                                         notice( t('Facebook post failed. Queued for retry.') . EOL);
724                                                 }
725                                         }
726                                         
727                                         logger('Facebook post returns: ' . $x, LOGGER_DEBUG);
728                                 }
729                         }
730                 }
731         }
732 }
733
734
735 function facebook_post_local(&$a,&$b) {
736
737         // Figure out if Facebook posting is enabled for this post and file it in 'postopts'
738         // where we will discover it during background delivery.
739
740         // This can only be triggered by a local user posting to their own wall.
741
742         if((local_user()) && (local_user() == $b['uid'])) {
743
744                 $fb_post   = intval(get_pconfig(local_user(),'facebook','post'));
745                 $fb_enable = (($fb_post && x($_POST,'facebook_enable')) ? intval($_POST['facebook_enable']) : 0);
746
747                 // if API is used, default to the chosen settings
748                 if($_POST['api_source'] && intval(get_pconfig(local_user(),'facebook','post_by_default')))
749                         $fb_enable = 1;
750
751                 if(! $fb_enable)
752                         return;
753
754                 if(strlen($b['postopts']))
755                         $b['postopts'] .= ',';
756                 $b['postopts'] .= 'facebook';
757         }
758 }
759
760
761 function fb_queue_hook(&$a,&$b) {
762
763         $qi = q("SELECT * FROM `queue` WHERE `network` = '%s'",
764                 dbesc(NETWORK_FACEBOOK)
765         );
766         if(! count($qi))
767                 return;
768
769         require_once('include/queue_fn.php');
770
771         foreach($qi as $x) {
772                 if($x['network'] !== NETWORK_FACEBOOK)
773                         continue;
774
775                 logger('facebook_queue: run');
776
777                 $r = q("SELECT `user`.* FROM `user` LEFT JOIN `contact` on `contact`.`uid` = `user`.`uid` 
778                         WHERE `contact`.`self` = 1 AND `contact`.`id` = %d LIMIT 1",
779                         intval($x['cid'])
780                 );
781                 if(! count($r))
782                         continue;
783
784                 $user = $r[0];
785
786                 $appid  = get_config('facebook', 'appid'  );
787                 $secret = get_config('facebook', 'appsecret' );
788
789                 if($appid && $secret) {
790                         $fb_post   = intval(get_pconfig($user['uid'],'facebook','post'));
791                         $fb_token  = get_pconfig($user['uid'],'facebook','access_token');
792
793                         if($fb_post && $fb_token) {
794                                 logger('facebook_queue: able to post');
795                                 require_once('library/facebook.php');
796
797                                 $z = unserialize($x['content']);
798                                 $item = $z['item'];
799                                 $j = post_url($z['url'],$z['post']);
800
801                                 $retj = json_decode($j);
802                                 if($retj->id) {
803                                         q("UPDATE `item` SET `extid` = '%s' WHERE `id` = %d LIMIT 1",
804                                                 dbesc('fb::' . $retj->id),
805                                                 intval($item)
806                                         );
807                                         logger('facebook_queue: success: ' . $j); 
808                                         remove_queue_item($x['id']);
809                                 }
810                                 else {
811                                         logger('facebook_queue: failed: ' . $j);
812                                         update_queue_time($x['id']);
813                                 }
814                         }
815                 }
816         }
817 }
818
819 function fb_consume_all($uid) {
820
821         require_once('include/items.php');
822
823         $access_token = get_pconfig($uid,'facebook','access_token');
824         if(! $access_token)
825                 return;
826         
827         if(! get_pconfig($uid,'facebook','no_wall')) {
828                 $private_wall = intval(get_pconfig($uid,'facebook','private_wall'));
829                 $s = fetch_url('https://graph.facebook.com/me/feed?access_token=' . $access_token);
830                 if($s) {
831                         $j = json_decode($s);
832                         logger('fb_consume_stream: wall: ' . print_r($j,true), LOGGER_DATA);
833                         fb_consume_stream($uid,$j,($private_wall) ? false : true);
834                 }
835         }
836         $s = fetch_url('https://graph.facebook.com/me/home?access_token=' . $access_token);
837         if($s) {
838                 $j = json_decode($s);
839                 logger('fb_consume_stream: feed: ' . print_r($j,true), LOGGER_DATA);
840                 fb_consume_stream($uid,$j,false);
841         }
842
843 }
844
845 function fb_consume_stream($uid,$j,$wall = false) {
846
847         $a = get_app();
848
849
850         $user = q("SELECT `nickname`, `blockwall` FROM `user` WHERE `uid` = %d AND `account_expired` = 0 LIMIT 1",
851                 intval($uid)
852         );
853         if(! count($user))
854                 return;
855
856         $my_local_url = $a->get_baseurl() . '/profile/' . $user[0]['nickname'];
857
858         $no_linking = get_pconfig($uid,'facebook','no_linking');
859         if($no_linking)
860                 return;
861
862         $self = q("SELECT * FROM `contact` WHERE `self` = 1 AND `uid` = %d LIMIT 1",
863                 intval($uid)
864         );
865
866
867         $self_id = get_pconfig($uid,'facebook','self_id');
868         if(! count($j->data) || (! strlen($self_id)))
869                 return;
870
871         foreach($j->data as $entry) {
872                 logger('fb_consume: entry: ' . print_r($entry,true), LOGGER_DATA);
873                 $datarray = array();
874
875                 $r = q("SELECT * FROM `item` WHERE ( `uri` = '%s' OR `extid` = '%s') AND `uid` = %d LIMIT 1",
876                                 dbesc('fb::' . $entry->id),
877                                 dbesc('fb::' . $entry->id),
878                                 intval($uid)
879                 );
880                 if(count($r)) {
881                         $post_exists = true;
882                         $orig_post = $r[0];
883                         $top_item = $r[0]['id'];
884                 }
885                 else {
886                         $post_exists = false;
887                         $orig_post = null;
888                 }
889
890                 if(! $orig_post) {
891                         $datarray['gravity'] = 0;
892                         $datarray['uid'] = $uid;
893                         $datarray['wall'] = (($wall) ? 1 : 0);
894                         $datarray['uri'] = $datarray['parent-uri'] = 'fb::' . $entry->id;
895                         $from = $entry->from;
896                         if($from->id == $self_id)
897                                 $datarray['contact-id'] = $self[0]['id'];
898                         else {
899                                 $r = q("SELECT * FROM `contact` WHERE `notify` = '%s' AND `uid` = %d AND `blocked` = 0 AND `readonly` = 0 LIMIT 1",
900                                         dbesc($from->id),
901                                         intval($uid)
902                                 );
903                                 if(count($r))
904                                         $datarray['contact-id'] = $r[0]['id'];
905                         }
906
907                         // don't store post if we don't have a contact
908
909                         if(! x($datarray,'contact-id')) {
910                                 logger('no contact: post ignored');
911                                 continue; 
912                         }
913
914                         $datarray['verb'] = ACTIVITY_POST;                                              
915                         if($wall) {
916                                 $datarray['owner-name'] = $self[0]['name'];
917                                 $datarray['owner-link'] = $self[0]['url'];
918                                 $datarray['owner-avatar'] = $self[0]['thumb'];
919                         }
920                         if(isset($entry->application) && isset($entry->application->name) && strlen($entry->application->name))
921                                 $datarray['app'] = strip_tags($entry->application->name);
922                         else
923                                 $datarray['app'] = 'facebook';
924                         $datarray['author-name'] = $from->name;
925                         $datarray['author-link'] = 'http://facebook.com/profile.php?id=' . $from->id;
926                         $datarray['author-avatar'] = 'https://graph.facebook.com/' . $from->id . '/picture';
927                         $datarray['plink'] = $datarray['author-link'] . '&v=wall&story_fbid=' . substr($entry->id,strpos($entry->id,'_') + 1);
928
929                         $datarray['body'] = $entry->message;
930                         if($entry->picture)
931                                 $datarray['body'] .= "\n\n" . '[img]' . $entry->picture . '[/img]';
932                         if($entry->link)
933                                 $datarray['body'] .= "\n" . linkify($entry->link);
934                         if($entry->name)
935                                 $datarray['body'] .= "\n" . $entry->name;
936                         if($entry->caption)
937                                 $datarray['body'] .= "\n" . $entry->caption;
938                         if($entry->description)
939                                 $datarray['body'] .= "\n" . $entry->description;
940                         $datarray['created'] = datetime_convert('UTC','UTC',$entry->created_time);
941                         $datarray['edited'] = datetime_convert('UTC','UTC',$entry->updated_time);
942
943                         // If the entry has a privacy policy, we cannot assume who can or cannot see it,
944                         // as the identities are from a foreign system. Mark it as private to the owner.
945
946                         if($entry->privacy && $entry->privacy->value !== 'EVERYONE') {
947                                 $datarray['private'] = 1;
948                                 $datarray['allow_cid'] = '<' . $uid . '>';
949                         }
950                         
951                         $top_item = item_store($datarray);
952                         $r = q("SELECT * FROM `item` WHERE `id` = %d AND `uid` = %d LIMIT 1",
953                                 intval($top_item),
954                                 intval($uid)
955                         );                      
956                         if(count($r)) {
957                                 $orig_post = $r[0];
958                                 logger('fb: new top level item posted');
959                         }
960                 }
961
962                 if(isset($entry->likes) && isset($entry->likes->data))
963                         $likers = $entry->likes->data;
964                 else
965                         $likers = null;
966
967                 if(isset($entry->comments) && isset($entry->comments->data))
968                         $comments = $entry->comments->data;
969                 else
970                         $comments = null;
971
972                 if(is_array($likers)) {
973                         foreach($likers as $likes) {
974
975                                 if(! $orig_post)
976                                         continue;
977
978                                 // If we posted the like locally, it will be found with our url, not the FB url.
979
980                                 $second_url = (($likes->id == $self_id) ? $self[0]['url'] : 'http://facebook.com/profile.php?id=' . $likes->id); 
981
982                                 $r = q("SELECT * FROM `item` WHERE `parent-uri` = '%s' AND `uid` = %d AND `verb` = '%s' 
983                                         AND ( `author-link` = '%s' OR `author-link` = '%s' ) LIMIT 1",
984                                         dbesc($orig_post['uri']),
985                                         intval($uid),
986                                         dbesc(ACTIVITY_LIKE),
987                                         dbesc('http://facebook.com/profile.php?id=' . $likes->id),
988                                         dbesc($second_url)
989                                 );
990
991                                 if(count($r))
992                                         continue;
993                                         
994                                 $likedata = array();
995                                 $likedata['parent'] = $top_item;
996                                 $likedata['verb'] = ACTIVITY_LIKE;
997                                 $likedata['gravity'] = 3;
998                                 $likedata['uid'] = $uid;
999                                 $likedata['wall'] = (($wall) ? 1 : 0);
1000                                 $likedata['uri'] = item_new_uri($a->get_baseurl(), $uid);
1001                                 $likedata['parent-uri'] = $orig_post['uri'];
1002                                 if($likes->id == $self_id)
1003                                         $likedata['contact-id'] = $self[0]['id'];
1004                                 else {
1005                                         $r = q("SELECT * FROM `contact` WHERE `notify` = '%s' AND `uid` = %d AND `blocked` = 0 AND `readonly` = 0 LIMIT 1",
1006                                                 dbesc($likes->id),
1007                                                 intval($uid)
1008                                         );
1009                                         if(count($r))
1010                                                 $likedata['contact-id'] = $r[0]['id'];
1011                                 }
1012                                 if(! x($likedata,'contact-id'))
1013                                         $likedata['contact-id'] = $orig_post['contact-id'];
1014
1015                                 $likedata['app'] = 'facebook';
1016                                 $likedata['verb'] = ACTIVITY_LIKE;                                              
1017                                 $likedata['author-name'] = $likes->name;
1018                                 $likedata['author-link'] = 'http://facebook.com/profile.php?id=' . $likes->id;
1019                                 $likedata['author-avatar'] = 'https://graph.facebook.com/' . $likes->id . '/picture';
1020                                 
1021                                 $author  = '[url=' . $likedata['author-link'] . ']' . $likedata['author-name'] . '[/url]';
1022                                 $objauthor =  '[url=' . $orig_post['author-link'] . ']' . $orig_post['author-name'] . '[/url]';
1023                                 $post_type = t('status');
1024                         $plink = '[url=' . $orig_post['plink'] . ']' . $post_type . '[/url]';
1025                                 $likedata['object-type'] = ACTIVITY_OBJ_NOTE;
1026
1027                                 $likedata['body'] = sprintf( t('%1$s likes %2$s\'s %3$s'), $author, $objauthor, $plink);
1028                                 $likedata['object'] = '<object><type>' . ACTIVITY_OBJ_NOTE . '</type><local>1</local>' . 
1029                                         '<id>' . $orig_post['uri'] . '</id><link>' . xmlify('<link rel="alternate" type="text/html" href="' . xmlify($orig_post['plink']) . '" />') . '</link><title>' . $orig_post['title'] . '</title><content>' . $orig_post['body'] . '</content></object>';  
1030
1031                                 $item = item_store($likedata);                  
1032                         }
1033                 }
1034                 if(is_array($comments)) {
1035                         foreach($comments as $cmnt) {
1036
1037                                 if(! $orig_post)
1038                                         continue;
1039
1040                                 $r = q("SELECT * FROM `item` WHERE `uid` = %d AND ( `uri` = '%s' OR `extid` = '%s' ) LIMIT 1",
1041                                         intval($uid),
1042                                         dbesc('fb::' . $cmnt->id),
1043                                         dbesc('fb::' . $cmnt->id)
1044                                 );
1045                                 if(count($r))
1046                                         continue;
1047
1048                                 $cmntdata = array();
1049                                 $cmntdata['parent'] = $top_item;
1050                                 $cmntdata['verb'] = ACTIVITY_POST;
1051                                 $cmntdata['gravity'] = 6;
1052                                 $cmntdata['uid'] = $uid;
1053                                 $cmntdata['wall'] = (($wall) ? 1 : 0);
1054                                 $cmntdata['uri'] = 'fb::' . $cmnt->id;
1055                                 $cmntdata['parent-uri'] = $orig_post['uri'];
1056                                 if($cmnt->from->id == $self_id) {
1057                                         $cmntdata['contact-id'] = $self[0]['id'];
1058                                 }
1059                                 else {
1060                                         $r = q("SELECT * FROM `contact` WHERE `notify` = '%s' AND `uid` = %d LIMIT 1",
1061                                                 dbesc($cmnt->from->id),
1062                                                 intval($uid)
1063                                         );
1064                                         if(count($r)) {
1065                                                 $cmntdata['contact-id'] = $r[0]['id'];
1066                                                 if($r[0]['blocked'] || $r[0]['readonly'])
1067                                                         continue;
1068                                         }
1069                                 }
1070                                 if(! x($cmntdata,'contact-id'))
1071                                         $cmntdata['contact-id'] = $orig_post['contact-id'];
1072
1073                                 $cmntdata['app'] = 'facebook';
1074                                 $cmntdata['created'] = datetime_convert('UTC','UTC',$cmnt->created_time);
1075                                 $cmntdata['edited']  = datetime_convert('UTC','UTC',$cmnt->created_time);
1076                                 $cmntdata['verb'] = ACTIVITY_POST;                                              
1077                                 $cmntdata['author-name'] = $cmnt->from->name;
1078                                 $cmntdata['author-link'] = 'http://facebook.com/profile.php?id=' . $cmnt->from->id;
1079                                 $cmntdata['author-avatar'] = 'https://graph.facebook.com/' . $cmnt->from->id . '/picture';
1080                                 $cmntdata['body'] = $cmnt->message;
1081                                 $item = item_store($cmntdata);                  
1082                         }
1083                 }
1084         }
1085 }
1086