]> git.mxchange.org Git - friendica-addons.git/blob - facebook/facebook.php
correct item reference
[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
529                 $u = q("SELECT * FROM user where uid = %d limit 1",
530                         intval($b['uid'])
531                 );
532                 if(! count($u))
533                         return;
534
535                 // only accept comments from the item owner. Other contacts are unknown to FB.
536  
537                 if(! link_compare($b['author-link'], $a->get_baseurl() . '/profile/' . $u[0]['nickname']))
538                         return;
539                 
540
541                 logger('facebook reply id=' . $reply);
542         }
543
544         if(strstr($b['postopts'],'facebook') || ($b['private']) || ($reply)) {
545
546                 if($b['private'] && $reply === false) {
547                         $allow_people = expand_acl($b['allow_cid']);
548                         $allow_groups = expand_groups(expand_acl($b['allow_gid']));
549                         $deny_people  = expand_acl($b['deny_cid']);
550                         $deny_groups  = expand_groups(expand_acl($b['deny_gid']));
551
552                         $recipients = array_unique(array_merge($allow_people,$allow_groups));
553                         $deny = array_unique(array_merge($deny_people,$deny_groups));
554
555                         $allow_str = dbesc(implode(', ',$recipients));
556                         if($allow_str) {
557                                 $r = q("SELECT `notify` FROM `contact` WHERE `id` IN ( $allow_str ) AND `network` = 'face'"); 
558                                 $allow_arr = array();
559                                 if(count($r)) 
560                                         foreach($r as $rr)
561                                                 $allow_arr[] = $rr['notify'];
562                         }
563
564                         $deny_str = dbesc(implode(', ',$deny));
565                         if($deny_str) {
566                                 $r = q("SELECT `notify` FROM `contact` WHERE `id` IN ( $deny_str ) AND `network` = 'face'"); 
567                                 $deny_arr = array();
568                                 if(count($r)) 
569                                         foreach($r as $rr)
570                                                 $deny_arr[] = $rr['notify'];
571                         }
572
573                         if(count($deny_arr) && (! count($allow_arr))) {
574
575                                 // One or more FB folks were denied access but nobody on FB was specifically allowed access.
576                                 // This might cause the post to be open to public on Facebook, but only to selected members
577                                 // on another network. Since this could potentially leak a post to somebody who was denied, 
578                                 // we will skip posting it to Facebook with a slightly vague but relevant message that will 
579                                 // hopefully lead somebody to this code comment for a better explanation of what went wrong.
580
581                                 notice( t('Post to Facebook cancelled because of multi-network access permission conflict.') . EOL);
582                                 return;
583                         }
584
585
586                         // if it's a private message but no Facebook members are allowed or denied, skip Facebook post
587
588                         if((! count($allow_arr)) && (! count($deny_arr)))
589                                 return;
590                 }
591
592                 if($b['verb'] == ACTIVITY_LIKE)
593                         $likes = true;                          
594
595
596                 $appid  = get_config('facebook', 'appid'  );
597                 $secret = get_config('facebook', 'appsecret' );
598
599                 if($appid && $secret) {
600
601                         logger('facebook: have appid+secret');
602
603                         $fb_token  = get_pconfig($b['uid'],'facebook','access_token');
604
605
606                         // post to facebook if it's a public post and we've ticked the 'post to Facebook' box, 
607                         // or it's a private message with facebook participants
608                         // or it's a reply or likes action to an existing facebook post                 
609
610                         if($fb_token && ($toplevel || $b['private'] || $reply)) {
611                                 logger('facebook: able to post');
612                                 require_once('library/facebook.php');
613                                 require_once('include/bbcode.php');     
614
615                                 $msg = $b['body'];
616
617                                 logger('Facebook post: original msg=' . $msg, LOGGER_DATA);
618
619                                 // make links readable before we strip the code
620
621                                 // unless it's a dislike - just send the text as a comment
622
623                                 if($b['verb'] == ACTIVITY_DISLIKE)
624                                         $msg = trim(strip_tags(bbcode($msg)));
625
626                                 $search_str = $a->get_baseurl() . '/search';
627
628                                 if(preg_match("/\[url=(.*?)\](.*?)\[\/url\]/is",$msg,$matches)) {
629
630                                         // don't use hashtags for message link
631
632                                         if(strpos($matches[2],$search_str) === false) {
633                                                 $link = $matches[1];
634                                                 if(substr($matches[2],0,5) != '[img]')
635                                                         $linkname = $matches[2];
636                                         }
637                                 }
638
639                                 $msg = preg_replace("/\[url=(.*?)\](.*?)\[\/url\]/is",'$2 $1',$msg);
640
641                                 if(preg_match("/\[img\](.*?)\[\/img\]/is",$msg,$matches))
642                                         $image = $matches[1];
643
644                                 $msg = preg_replace("/\[img\](.*?)\[\/img\]/is", t('Image: ') . '$1', $msg);
645
646                                 if((strpos($link,z_root()) !== false) && (! $image))
647                                         $image = $a->get_baseurl() . '/images/friendica-64.jpg';
648
649                                 $msg = trim(strip_tags(bbcode($msg)));
650                                 $msg = html_entity_decode($msg,ENT_QUOTES,'UTF-8');
651
652                                 // add any attachments as text urls
653
654                             $arr = explode(',',$b['attach']);
655
656                             if(count($arr)) {
657                                         $msg .= "\n";
658                                 foreach($arr as $r) {
659                                 $matches = false;
660                                                 $cnt = preg_match('|\[attach\]href=\"(.*?)\" size=\"(.*?)\" type=\"(.*?)\" title=\"(.*?)\"\[\/attach\]|',$r,$matches);
661                                                 if($cnt) {
662                                                         $msg .= $matches[1];
663                                                 }
664                                         }
665                                 }
666
667                                 if (strlen($msg) > FACEBOOK_MAXPOSTLEN) {
668                                         $shortlink = "";
669                                         require_once('library/slinky.php');
670
671                                         $display_url = $a->get_baseurl() . '/display/' . $a->user['nickname'] . '/' . $b['id'];
672                                         $slinky = new Slinky( $display_url );
673                                         // setup a cascade of shortening services
674                                         // try to get a short link from these services
675                                         // in the order ur1.ca, trim, id.gd, tinyurl
676                                         $slinky->set_cascade( array( new Slinky_UR1ca(), new Slinky_Trim(), new Slinky_IsGd(), new Slinky_TinyURL() ) );
677                                         $shortlink = $slinky->short();
678                                         // the new message will be shortened such that "... $shortlink"
679                                         // will fit into the character limit
680                                         $msg = substr($msg, 0, FACEBOOK_MAXPOSTLEN - strlen($shortlink) - 4);
681                                         $msg .= '... ' . $shortlink;
682                                 }
683                                 if(! strlen($msg))
684                                         return;
685
686                                 logger('Facebook post: msg=' . $msg, LOGGER_DATA);
687
688                                 if($likes) { 
689                                         $postvars = array('access_token' => $fb_token);
690                                 }
691                                 else {
692                                         $postvars = array(
693                                                 'access_token' => $fb_token, 
694                                                 'message' => $msg
695                                         );
696                                         if(isset($image))
697                                                 $postvars['picture'] = $image;
698                                         if(isset($link))
699                                                 $postvars['link'] = $link;
700                                         if(isset($linkname))
701                                                 $postvars['name'] = $linkname;
702                                 }
703
704                                 if(($b['private']) && (! $b['parent'])) {
705                                         $postvars['privacy'] = '{"value": "CUSTOM", "friends": "SOME_FRIENDS"';
706                                         if(count($allow_arr))
707                                                 $postvars['privacy'] .= ',"allow": "' . implode(',',$allow_arr) . '"';
708                                         if(count($deny_arr))
709                                                 $postvars['privacy'] .= ',"deny": "' . implode(',',$deny_arr) . '"';
710                                         $postvars['privacy'] .= '}';
711
712                                 }
713
714                                 if($reply) {
715                                         $url = 'https://graph.facebook.com/' . $reply . '/' . (($likes) ? 'likes' : 'comments');
716                                 }
717                                 else { 
718                                         $url = 'https://graph.facebook.com/me/feed';
719                                         if($b['plink'])
720                                                 $postvars['actions'] = '{"name": "' . t('View on Friendica') . '", "link": "' .  $b['plink'] . '"}';
721                                 }
722
723                                 logger('facebook: post to ' . $url);
724                                 logger('facebook: postvars: ' . print_r($postvars,true));
725
726                                 // "test_mode" prevents anything from actually being posted.
727                                 // Otherwise, let's do it. 
728
729                                 if(! get_config('facebook','test_mode')) {
730                                         $x = post_url($url, $postvars);
731
732                                         $retj = json_decode($x);
733                                         if($retj->id) {
734                                                 q("UPDATE `item` SET `extid` = '%s' WHERE `id` = %d LIMIT 1",
735                                                         dbesc('fb::' . $retj->id),
736                                                         intval($b['id'])
737                                                 );
738                                         }
739                                         else {
740                                                 if(! $likes) {
741                                                         $s = serialize(array('url' => $url, 'item' => $b['id'], 'post' => $postvars));
742                                                         require_once('include/queue_fn.php');
743                                                         add_to_queue($a->contact,NETWORK_FACEBOOK,$s);
744                                                         notice( t('Facebook post failed. Queued for retry.') . EOL);
745                                                 }
746                                         }
747                                         
748                                         logger('Facebook post returns: ' . $x, LOGGER_DEBUG);
749                                 }
750                         }
751                 }
752         }
753 }
754
755
756 function facebook_post_local(&$a,&$b) {
757
758         // Figure out if Facebook posting is enabled for this post and file it in 'postopts'
759         // where we will discover it during background delivery.
760
761         // This can only be triggered by a local user posting to their own wall.
762
763         if((local_user()) && (local_user() == $b['uid'])) {
764
765                 $fb_post   = intval(get_pconfig(local_user(),'facebook','post'));
766                 $fb_enable = (($fb_post && x($_REQUEST,'facebook_enable')) ? intval($_REQUEST['facebook_enable']) : 0);
767
768                 // if API is used, default to the chosen settings
769                 if($_REQUEST['api_source'] && intval(get_pconfig(local_user(),'facebook','post_by_default')))
770                         $fb_enable = 1;
771
772                 if(! $fb_enable)
773                         return;
774
775                 if(strlen($b['postopts']))
776                         $b['postopts'] .= ',';
777                 $b['postopts'] .= 'facebook';
778         }
779 }
780
781
782 function fb_queue_hook(&$a,&$b) {
783
784         $qi = q("SELECT * FROM `queue` WHERE `network` = '%s'",
785                 dbesc(NETWORK_FACEBOOK)
786         );
787         if(! count($qi))
788                 return;
789
790         require_once('include/queue_fn.php');
791
792         foreach($qi as $x) {
793                 if($x['network'] !== NETWORK_FACEBOOK)
794                         continue;
795
796                 logger('facebook_queue: run');
797
798                 $r = q("SELECT `user`.* FROM `user` LEFT JOIN `contact` on `contact`.`uid` = `user`.`uid` 
799                         WHERE `contact`.`self` = 1 AND `contact`.`id` = %d LIMIT 1",
800                         intval($x['cid'])
801                 );
802                 if(! count($r))
803                         continue;
804
805                 $user = $r[0];
806
807                 $appid  = get_config('facebook', 'appid'  );
808                 $secret = get_config('facebook', 'appsecret' );
809
810                 if($appid && $secret) {
811                         $fb_post   = intval(get_pconfig($user['uid'],'facebook','post'));
812                         $fb_token  = get_pconfig($user['uid'],'facebook','access_token');
813
814                         if($fb_post && $fb_token) {
815                                 logger('facebook_queue: able to post');
816                                 require_once('library/facebook.php');
817
818                                 $z = unserialize($x['content']);
819                                 $item = $z['item'];
820                                 $j = post_url($z['url'],$z['post']);
821
822                                 $retj = json_decode($j);
823                                 if($retj->id) {
824                                         q("UPDATE `item` SET `extid` = '%s' WHERE `id` = %d LIMIT 1",
825                                                 dbesc('fb::' . $retj->id),
826                                                 intval($item)
827                                         );
828                                         logger('facebook_queue: success: ' . $j); 
829                                         remove_queue_item($x['id']);
830                                 }
831                                 else {
832                                         logger('facebook_queue: failed: ' . $j);
833                                         update_queue_time($x['id']);
834                                 }
835                         }
836                 }
837         }
838 }
839
840 function fb_consume_all($uid) {
841
842         require_once('include/items.php');
843
844         $access_token = get_pconfig($uid,'facebook','access_token');
845         if(! $access_token)
846                 return;
847         
848         if(! get_pconfig($uid,'facebook','no_wall')) {
849                 $private_wall = intval(get_pconfig($uid,'facebook','private_wall'));
850                 $s = fetch_url('https://graph.facebook.com/me/feed?access_token=' . $access_token);
851                 if($s) {
852                         $j = json_decode($s);
853                         logger('fb_consume_stream: wall: ' . print_r($j,true), LOGGER_DATA);
854                         fb_consume_stream($uid,$j,($private_wall) ? false : true);
855                 }
856         }
857         $s = fetch_url('https://graph.facebook.com/me/home?access_token=' . $access_token);
858         if($s) {
859                 $j = json_decode($s);
860                 logger('fb_consume_stream: feed: ' . print_r($j,true), LOGGER_DATA);
861                 fb_consume_stream($uid,$j,false);
862         }
863
864 }
865
866 function fb_consume_stream($uid,$j,$wall = false) {
867
868         $a = get_app();
869
870
871         $user = q("SELECT `nickname`, `blockwall` FROM `user` WHERE `uid` = %d AND `account_expired` = 0 LIMIT 1",
872                 intval($uid)
873         );
874         if(! count($user))
875                 return;
876
877         $my_local_url = $a->get_baseurl() . '/profile/' . $user[0]['nickname'];
878
879         $no_linking = get_pconfig($uid,'facebook','no_linking');
880         if($no_linking)
881                 return;
882
883         $self = q("SELECT * FROM `contact` WHERE `self` = 1 AND `uid` = %d LIMIT 1",
884                 intval($uid)
885         );
886
887         $blocked_apps = get_pconfig($uid,'facebook','blocked_apps');
888         $blocked_apps_arr = explode(',',$blocked_apps);
889
890         $self_id = get_pconfig($uid,'facebook','self_id');
891         if(! count($j->data) || (! strlen($self_id)))
892                 return;
893
894         foreach($j->data as $entry) {
895                 logger('fb_consume: entry: ' . print_r($entry,true), LOGGER_DATA);
896                 $datarray = array();
897
898                 $r = q("SELECT * FROM `item` WHERE ( `uri` = '%s' OR `extid` = '%s') AND `uid` = %d LIMIT 1",
899                                 dbesc('fb::' . $entry->id),
900                                 dbesc('fb::' . $entry->id),
901                                 intval($uid)
902                 );
903                 if(count($r)) {
904                         $post_exists = true;
905                         $orig_post = $r[0];
906                         $top_item = $r[0]['id'];
907                 }
908                 else {
909                         $post_exists = false;
910                         $orig_post = null;
911                 }
912
913                 if(! $orig_post) {
914                         $datarray['gravity'] = 0;
915                         $datarray['uid'] = $uid;
916                         $datarray['wall'] = (($wall) ? 1 : 0);
917                         $datarray['uri'] = $datarray['parent-uri'] = 'fb::' . $entry->id;
918                         $from = $entry->from;
919                         if($from->id == $self_id)
920                                 $datarray['contact-id'] = $self[0]['id'];
921                         else {
922                                 $r = q("SELECT * FROM `contact` WHERE `notify` = '%s' AND `uid` = %d AND `blocked` = 0 AND `readonly` = 0 LIMIT 1",
923                                         dbesc($from->id),
924                                         intval($uid)
925                                 );
926                                 if(count($r))
927                                         $datarray['contact-id'] = $r[0]['id'];
928                         }
929
930                         // don't store post if we don't have a contact
931
932                         if(! x($datarray,'contact-id')) {
933                                 logger('no contact: post ignored');
934                                 continue; 
935                         }
936
937                         $datarray['verb'] = ACTIVITY_POST;                                              
938                         if($wall) {
939                                 $datarray['owner-name'] = $self[0]['name'];
940                                 $datarray['owner-link'] = $self[0]['url'];
941                                 $datarray['owner-avatar'] = $self[0]['thumb'];
942                         }
943                         if(isset($entry->application) && isset($entry->application->name) && strlen($entry->application->name))
944                                 $datarray['app'] = strip_tags($entry->application->name);
945                         else
946                                 $datarray['app'] = 'facebook';
947
948                         $found_blocked = false;
949
950                         if(count($blocked_apps_arr)) {
951                                 foreach($blocked_apps_arr as $bad_appl) {
952                                         if(strlen(trim($bad_appl)) && (stristr($datarray['app'],trim($bad_appl)))) {
953                                                 $found_blocked = true;
954                                         }
955                                 }
956                         }
957                                 
958                         if($found_blocked) {
959                                 logger('facebook: blocking application: ' . $datarray['app']);
960                                 continue;
961                         }
962
963                         $datarray['author-name'] = $from->name;
964                         $datarray['author-link'] = 'http://facebook.com/profile.php?id=' . $from->id;
965                         $datarray['author-avatar'] = 'https://graph.facebook.com/' . $from->id . '/picture';
966                         $datarray['plink'] = $datarray['author-link'] . '&v=wall&story_fbid=' . substr($entry->id,strpos($entry->id,'_') + 1);
967
968                         $datarray['body'] = escape_tags($entry->message);
969
970                         if($entry->picture && $entry->link) {
971                                 $datarray['body'] .= "\n\n" . '[url=' . $entry->link . '][img]' . $entry->picture . '[/img][/url]';
972                         }
973                         else {
974                                 if($entry->picture)
975                                         $datarray['body'] .= "\n\n" . '[img]' . $entry->picture . '[/img]';
976                                 if($entry->link)
977                                         $datarray['body'] .= "\n" . '[url=' . $entry->link . ']' . t('link') . '[/url]';
978                         }
979                         if($entry->name)
980                                 $datarray['body'] .= "\n" . $entry->name;
981                         if($entry->caption)
982                                 $datarray['body'] .= "\n" . $entry->caption;
983                         if($entry->description)
984                                 $datarray['body'] .= "\n" . $entry->description;
985                         $datarray['created'] = datetime_convert('UTC','UTC',$entry->created_time);
986                         $datarray['edited'] = datetime_convert('UTC','UTC',$entry->updated_time);
987
988                         // If the entry has a privacy policy, we cannot assume who can or cannot see it,
989                         // as the identities are from a foreign system. Mark it as private to the owner.
990
991                         if($entry->privacy && $entry->privacy->value !== 'EVERYONE') {
992                                 $datarray['private'] = 1;
993                                 $datarray['allow_cid'] = '<' . $uid . '>';
994                         }
995                         
996                         $top_item = item_store($datarray);
997                         $r = q("SELECT * FROM `item` WHERE `id` = %d AND `uid` = %d LIMIT 1",
998                                 intval($top_item),
999                                 intval($uid)
1000                         );                      
1001                         if(count($r)) {
1002                                 $orig_post = $r[0];
1003                                 logger('fb: new top level item posted');
1004                         }
1005                 }
1006
1007                 if(isset($entry->likes) && isset($entry->likes->data))
1008                         $likers = $entry->likes->data;
1009                 else
1010                         $likers = null;
1011
1012                 if(isset($entry->comments) && isset($entry->comments->data))
1013                         $comments = $entry->comments->data;
1014                 else
1015                         $comments = null;
1016
1017                 if(is_array($likers)) {
1018                         foreach($likers as $likes) {
1019
1020                                 if(! $orig_post)
1021                                         continue;
1022
1023                                 // If we posted the like locally, it will be found with our url, not the FB url.
1024
1025                                 $second_url = (($likes->id == $self_id) ? $self[0]['url'] : 'http://facebook.com/profile.php?id=' . $likes->id); 
1026
1027                                 $r = q("SELECT * FROM `item` WHERE `parent-uri` = '%s' AND `uid` = %d AND `verb` = '%s' 
1028                                         AND ( `author-link` = '%s' OR `author-link` = '%s' ) LIMIT 1",
1029                                         dbesc($orig_post['uri']),
1030                                         intval($uid),
1031                                         dbesc(ACTIVITY_LIKE),
1032                                         dbesc('http://facebook.com/profile.php?id=' . $likes->id),
1033                                         dbesc($second_url)
1034                                 );
1035
1036                                 if(count($r))
1037                                         continue;
1038                                         
1039                                 $likedata = array();
1040                                 $likedata['parent'] = $top_item;
1041                                 $likedata['verb'] = ACTIVITY_LIKE;
1042                                 $likedata['gravity'] = 3;
1043                                 $likedata['uid'] = $uid;
1044                                 $likedata['wall'] = (($wall) ? 1 : 0);
1045                                 $likedata['uri'] = item_new_uri($a->get_baseurl(), $uid);
1046                                 $likedata['parent-uri'] = $orig_post['uri'];
1047                                 if($likes->id == $self_id)
1048                                         $likedata['contact-id'] = $self[0]['id'];
1049                                 else {
1050                                         $r = q("SELECT * FROM `contact` WHERE `notify` = '%s' AND `uid` = %d AND `blocked` = 0 AND `readonly` = 0 LIMIT 1",
1051                                                 dbesc($likes->id),
1052                                                 intval($uid)
1053                                         );
1054                                         if(count($r))
1055                                                 $likedata['contact-id'] = $r[0]['id'];
1056                                 }
1057                                 if(! x($likedata,'contact-id'))
1058                                         $likedata['contact-id'] = $orig_post['contact-id'];
1059
1060                                 $likedata['app'] = 'facebook';
1061                                 $likedata['verb'] = ACTIVITY_LIKE;                                              
1062                                 $likedata['author-name'] = $likes->name;
1063                                 $likedata['author-link'] = 'http://facebook.com/profile.php?id=' . $likes->id;
1064                                 $likedata['author-avatar'] = 'https://graph.facebook.com/' . $likes->id . '/picture';
1065                                 
1066                                 $author  = '[url=' . $likedata['author-link'] . ']' . $likedata['author-name'] . '[/url]';
1067                                 $objauthor =  '[url=' . $orig_post['author-link'] . ']' . $orig_post['author-name'] . '[/url]';
1068                                 $post_type = t('status');
1069                         $plink = '[url=' . $orig_post['plink'] . ']' . $post_type . '[/url]';
1070                                 $likedata['object-type'] = ACTIVITY_OBJ_NOTE;
1071
1072                                 $likedata['body'] = sprintf( t('%1$s likes %2$s\'s %3$s'), $author, $objauthor, $plink);
1073                                 $likedata['object'] = '<object><type>' . ACTIVITY_OBJ_NOTE . '</type><local>1</local>' . 
1074                                         '<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>';  
1075
1076                                 $item = item_store($likedata);                  
1077                         }
1078                 }
1079                 if(is_array($comments)) {
1080                         foreach($comments as $cmnt) {
1081
1082                                 if(! $orig_post)
1083                                         continue;
1084
1085                                 $r = q("SELECT * FROM `item` WHERE `uid` = %d AND ( `uri` = '%s' OR `extid` = '%s' ) LIMIT 1",
1086                                         intval($uid),
1087                                         dbesc('fb::' . $cmnt->id),
1088                                         dbesc('fb::' . $cmnt->id)
1089                                 );
1090                                 if(count($r))
1091                                         continue;
1092
1093                                 $cmntdata = array();
1094                                 $cmntdata['parent'] = $top_item;
1095                                 $cmntdata['verb'] = ACTIVITY_POST;
1096                                 $cmntdata['gravity'] = 6;
1097                                 $cmntdata['uid'] = $uid;
1098                                 $cmntdata['wall'] = (($wall) ? 1 : 0);
1099                                 $cmntdata['uri'] = 'fb::' . $cmnt->id;
1100                                 $cmntdata['parent-uri'] = $orig_post['uri'];
1101                                 if($cmnt->from->id == $self_id) {
1102                                         $cmntdata['contact-id'] = $self[0]['id'];
1103                                 }
1104                                 else {
1105                                         $r = q("SELECT * FROM `contact` WHERE `notify` = '%s' AND `uid` = %d LIMIT 1",
1106                                                 dbesc($cmnt->from->id),
1107                                                 intval($uid)
1108                                         );
1109                                         if(count($r)) {
1110                                                 $cmntdata['contact-id'] = $r[0]['id'];
1111                                                 if($r[0]['blocked'] || $r[0]['readonly'])
1112                                                         continue;
1113                                         }
1114                                 }
1115                                 if(! x($cmntdata,'contact-id'))
1116                                         $cmntdata['contact-id'] = $orig_post['contact-id'];
1117
1118                                 $cmntdata['app'] = 'facebook';
1119                                 $cmntdata['created'] = datetime_convert('UTC','UTC',$cmnt->created_time);
1120                                 $cmntdata['edited']  = datetime_convert('UTC','UTC',$cmnt->created_time);
1121                                 $cmntdata['verb'] = ACTIVITY_POST;                                              
1122                                 $cmntdata['author-name'] = $cmnt->from->name;
1123                                 $cmntdata['author-link'] = 'http://facebook.com/profile.php?id=' . $cmnt->from->id;
1124                                 $cmntdata['author-avatar'] = 'https://graph.facebook.com/' . $cmnt->from->id . '/picture';
1125                                 $cmntdata['body'] = $cmnt->message;
1126                                 $item = item_store($cmntdata);                  
1127                         }
1128                 }
1129         }
1130 }
1131