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