]> git.mxchange.org Git - friendica-addons.git/blob - facebook/facebook.php
3cf5ee8d6a74380eee584c0e9e5755a2e5353afc
[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 = $b['plink'];
672
673                                         $slinky = new Slinky( $display_url );
674                                         // setup a cascade of shortening services
675                                         // try to get a short link from these services
676                                         // in the order ur1.ca, trim, id.gd, tinyurl
677                                         $slinky->set_cascade( array( new Slinky_UR1ca(), new Slinky_Trim(), new Slinky_IsGd(), new Slinky_TinyURL() ) );
678                                         $shortlink = $slinky->short();
679                                         // the new message will be shortened such that "... $shortlink"
680                                         // will fit into the character limit
681                                         $msg = substr($msg, 0, FACEBOOK_MAXPOSTLEN - strlen($shortlink) - 4);
682                                         $msg .= '... ' . $shortlink;
683                                 }
684                                 if(! strlen($msg))
685                                         return;
686
687                                 logger('Facebook post: msg=' . $msg, LOGGER_DATA);
688
689                                 if($likes) { 
690                                         $postvars = array('access_token' => $fb_token);
691                                 }
692                                 else {
693                                         $postvars = array(
694                                                 'access_token' => $fb_token, 
695                                                 'message' => $msg
696                                         );
697                                         if(isset($image))
698                                                 $postvars['picture'] = $image;
699                                         if(isset($link))
700                                                 $postvars['link'] = $link;
701                                         if(isset($linkname))
702                                                 $postvars['name'] = $linkname;
703                                 }
704
705                                 if(($b['private']) && ($toplevel)) {
706                                         $postvars['privacy'] = '{"value": "CUSTOM", "friends": "SOME_FRIENDS"';
707                                         if(count($allow_arr))
708                                                 $postvars['privacy'] .= ',"allow": "' . implode(',',$allow_arr) . '"';
709                                         if(count($deny_arr))
710                                                 $postvars['privacy'] .= ',"deny": "' . implode(',',$deny_arr) . '"';
711                                         $postvars['privacy'] .= '}';
712
713                                 }
714
715                                 if($reply) {
716                                         $url = 'https://graph.facebook.com/' . $reply . '/' . (($likes) ? 'likes' : 'comments');
717                                 }
718                                 else { 
719                                         $url = 'https://graph.facebook.com/me/feed';
720                                         if($b['plink'])
721                                                 $postvars['actions'] = '{"name": "' . t('View on Friendica') . '", "link": "' .  $b['plink'] . '"}';
722                                 }
723
724                                 logger('facebook: post to ' . $url);
725                                 logger('facebook: postvars: ' . print_r($postvars,true));
726
727                                 // "test_mode" prevents anything from actually being posted.
728                                 // Otherwise, let's do it. 
729
730                                 if(! get_config('facebook','test_mode')) {
731                                         $x = post_url($url, $postvars);
732
733                                         $retj = json_decode($x);
734                                         if($retj->id) {
735                                                 q("UPDATE `item` SET `extid` = '%s' WHERE `id` = %d LIMIT 1",
736                                                         dbesc('fb::' . $retj->id),
737                                                         intval($b['id'])
738                                                 );
739                                         }
740                                         else {
741                                                 if(! $likes) {
742                                                         $s = serialize(array('url' => $url, 'item' => $b['id'], 'post' => $postvars));
743                                                         require_once('include/queue_fn.php');
744                                                         add_to_queue($a->contact,NETWORK_FACEBOOK,$s);
745                                                         notice( t('Facebook post failed. Queued for retry.') . EOL);
746                                                 }
747                                         }
748                                         
749                                         logger('Facebook post returns: ' . $x, LOGGER_DEBUG);
750                                 }
751                         }
752                 }
753         }
754 }
755
756
757 function facebook_post_local(&$a,&$b) {
758
759         // Figure out if Facebook posting is enabled for this post and file it in 'postopts'
760         // where we will discover it during background delivery.
761
762         // This can only be triggered by a local user posting to their own wall.
763
764         if((local_user()) && (local_user() == $b['uid'])) {
765
766                 $fb_post   = intval(get_pconfig(local_user(),'facebook','post'));
767                 $fb_enable = (($fb_post && x($_REQUEST,'facebook_enable')) ? intval($_REQUEST['facebook_enable']) : 0);
768
769                 // if API is used, default to the chosen settings
770                 if($_REQUEST['api_source'] && intval(get_pconfig(local_user(),'facebook','post_by_default')))
771                         $fb_enable = 1;
772
773                 if(! $fb_enable)
774                         return;
775
776                 if(strlen($b['postopts']))
777                         $b['postopts'] .= ',';
778                 $b['postopts'] .= 'facebook';
779         }
780 }
781
782
783 function fb_queue_hook(&$a,&$b) {
784
785         $qi = q("SELECT * FROM `queue` WHERE `network` = '%s'",
786                 dbesc(NETWORK_FACEBOOK)
787         );
788         if(! count($qi))
789                 return;
790
791         require_once('include/queue_fn.php');
792
793         foreach($qi as $x) {
794                 if($x['network'] !== NETWORK_FACEBOOK)
795                         continue;
796
797                 logger('facebook_queue: run');
798
799                 $r = q("SELECT `user`.* FROM `user` LEFT JOIN `contact` on `contact`.`uid` = `user`.`uid` 
800                         WHERE `contact`.`self` = 1 AND `contact`.`id` = %d LIMIT 1",
801                         intval($x['cid'])
802                 );
803                 if(! count($r))
804                         continue;
805
806                 $user = $r[0];
807
808                 $appid  = get_config('facebook', 'appid'  );
809                 $secret = get_config('facebook', 'appsecret' );
810
811                 if($appid && $secret) {
812                         $fb_post   = intval(get_pconfig($user['uid'],'facebook','post'));
813                         $fb_token  = get_pconfig($user['uid'],'facebook','access_token');
814
815                         if($fb_post && $fb_token) {
816                                 logger('facebook_queue: able to post');
817                                 require_once('library/facebook.php');
818
819                                 $z = unserialize($x['content']);
820                                 $item = $z['item'];
821                                 $j = post_url($z['url'],$z['post']);
822
823                                 $retj = json_decode($j);
824                                 if($retj->id) {
825                                         q("UPDATE `item` SET `extid` = '%s' WHERE `id` = %d LIMIT 1",
826                                                 dbesc('fb::' . $retj->id),
827                                                 intval($item)
828                                         );
829                                         logger('facebook_queue: success: ' . $j); 
830                                         remove_queue_item($x['id']);
831                                 }
832                                 else {
833                                         logger('facebook_queue: failed: ' . $j);
834                                         update_queue_time($x['id']);
835                                 }
836                         }
837                 }
838         }
839 }
840
841 function fb_consume_all($uid) {
842
843         require_once('include/items.php');
844
845         $access_token = get_pconfig($uid,'facebook','access_token');
846         if(! $access_token)
847                 return;
848         
849         if(! get_pconfig($uid,'facebook','no_wall')) {
850                 $private_wall = intval(get_pconfig($uid,'facebook','private_wall'));
851                 $s = fetch_url('https://graph.facebook.com/me/feed?access_token=' . $access_token);
852                 if($s) {
853                         $j = json_decode($s);
854                         logger('fb_consume_stream: wall: ' . print_r($j,true), LOGGER_DATA);
855                         fb_consume_stream($uid,$j,($private_wall) ? false : true);
856                 }
857         }
858         $s = fetch_url('https://graph.facebook.com/me/home?access_token=' . $access_token);
859         if($s) {
860                 $j = json_decode($s);
861                 logger('fb_consume_stream: feed: ' . print_r($j,true), LOGGER_DATA);
862                 fb_consume_stream($uid,$j,false);
863         }
864
865 }
866
867 function fb_get_photo($uid,$link) {
868         $access_token = get_pconfig($uid,'facebook','access_token');
869         if(! $access_token || (! stristr($link,'facebook.com/photo.php')))
870                 return "\n" . '[url=' . $link . ']' . t('link') . '[/url]';
871         $ret = preg_match('/fbid=([0-9]*)/',$link,$match);
872         if($ret)
873                 $photo_id = $match[1];
874         $x = fetch_url('https://graph.facebook.com/' . $photo_id . '?access_token=' . $access_token);
875         $j = json_decode($x);
876         if($j->picture)
877                 return "\n\n" . '[url=' . $link . '][img]' . $j->picture . '[/img][/url]';
878         else
879                 return "\n" . '[url=' . $link . ']' . t('link') . '[/url]';
880 }
881
882 function fb_consume_stream($uid,$j,$wall = false) {
883
884         $a = get_app();
885
886
887         $user = q("SELECT `nickname`, `blockwall` FROM `user` WHERE `uid` = %d AND `account_expired` = 0 LIMIT 1",
888                 intval($uid)
889         );
890         if(! count($user))
891                 return;
892
893         $my_local_url = $a->get_baseurl() . '/profile/' . $user[0]['nickname'];
894
895         $no_linking = get_pconfig($uid,'facebook','no_linking');
896         if($no_linking)
897                 return;
898
899         $self = q("SELECT * FROM `contact` WHERE `self` = 1 AND `uid` = %d LIMIT 1",
900                 intval($uid)
901         );
902
903         $blocked_apps = get_pconfig($uid,'facebook','blocked_apps');
904         $blocked_apps_arr = explode(',',$blocked_apps);
905
906         $self_id = get_pconfig($uid,'facebook','self_id');
907         if(! count($j->data) || (! strlen($self_id)))
908                 return;
909
910         foreach($j->data as $entry) {
911                 logger('fb_consume: entry: ' . print_r($entry,true), LOGGER_DATA);
912                 $datarray = array();
913
914                 $r = q("SELECT * FROM `item` WHERE ( `uri` = '%s' OR `extid` = '%s') AND `uid` = %d LIMIT 1",
915                                 dbesc('fb::' . $entry->id),
916                                 dbesc('fb::' . $entry->id),
917                                 intval($uid)
918                 );
919                 if(count($r)) {
920                         $post_exists = true;
921                         $orig_post = $r[0];
922                         $top_item = $r[0]['id'];
923                 }
924                 else {
925                         $post_exists = false;
926                         $orig_post = null;
927                 }
928
929                 if(! $orig_post) {
930                         $datarray['gravity'] = 0;
931                         $datarray['uid'] = $uid;
932                         $datarray['wall'] = (($wall) ? 1 : 0);
933                         $datarray['uri'] = $datarray['parent-uri'] = 'fb::' . $entry->id;
934                         $from = $entry->from;
935                         if($from->id == $self_id)
936                                 $datarray['contact-id'] = $self[0]['id'];
937                         else {
938                                 $r = q("SELECT * FROM `contact` WHERE `notify` = '%s' AND `uid` = %d AND `blocked` = 0 AND `readonly` = 0 LIMIT 1",
939                                         dbesc($from->id),
940                                         intval($uid)
941                                 );
942                                 if(count($r))
943                                         $datarray['contact-id'] = $r[0]['id'];
944                         }
945
946                         // don't store post if we don't have a contact
947
948                         if(! x($datarray,'contact-id')) {
949                                 logger('no contact: post ignored');
950                                 continue; 
951                         }
952
953                         $datarray['verb'] = ACTIVITY_POST;                                              
954                         if($wall) {
955                                 $datarray['owner-name'] = $self[0]['name'];
956                                 $datarray['owner-link'] = $self[0]['url'];
957                                 $datarray['owner-avatar'] = $self[0]['thumb'];
958                         }
959                         if(isset($entry->application) && isset($entry->application->name) && strlen($entry->application->name))
960                                 $datarray['app'] = strip_tags($entry->application->name);
961                         else
962                                 $datarray['app'] = 'facebook';
963
964                         $found_blocked = false;
965
966                         if(count($blocked_apps_arr)) {
967                                 foreach($blocked_apps_arr as $bad_appl) {
968                                         if(strlen(trim($bad_appl)) && (stristr($datarray['app'],trim($bad_appl)))) {
969                                                 $found_blocked = true;
970                                         }
971                                 }
972                         }
973                                 
974                         if($found_blocked) {
975                                 logger('facebook: blocking application: ' . $datarray['app']);
976                                 continue;
977                         }
978
979                         $datarray['author-name'] = $from->name;
980                         $datarray['author-link'] = 'http://facebook.com/profile.php?id=' . $from->id;
981                         $datarray['author-avatar'] = 'https://graph.facebook.com/' . $from->id . '/picture';
982                         $datarray['plink'] = $datarray['author-link'] . '&v=wall&story_fbid=' . substr($entry->id,strpos($entry->id,'_') + 1);
983
984                         $datarray['body'] = escape_tags($entry->message);
985
986                         if($entry->picture && $entry->link) {
987                                 $datarray['body'] .= "\n\n" . '[url=' . $entry->link . '][img]' . $entry->picture . '[/img][/url]';
988                         }
989                         else {
990                                 if($entry->picture)
991                                         $datarray['body'] .= "\n\n" . '[img]' . $entry->picture . '[/img]';
992                                 // if just a link, it may be a wall photo - check
993                                 if($entry->link)
994                                         $datarray['body'] .= fb_get_photo($uid,$entry->link);
995                         }
996                         if($entry->name)
997                                 $datarray['body'] .= "\n" . $entry->name;
998                         if($entry->caption)
999                                 $datarray['body'] .= "\n" . $entry->caption;
1000                         if($entry->description)
1001                                 $datarray['body'] .= "\n" . $entry->description;
1002                         $datarray['created'] = datetime_convert('UTC','UTC',$entry->created_time);
1003                         $datarray['edited'] = datetime_convert('UTC','UTC',$entry->updated_time);
1004
1005                         // If the entry has a privacy policy, we cannot assume who can or cannot see it,
1006                         // as the identities are from a foreign system. Mark it as private to the owner.
1007
1008                         if($entry->privacy && $entry->privacy->value !== 'EVERYONE') {
1009                                 $datarray['private'] = 1;
1010                                 $datarray['allow_cid'] = '<' . $uid . '>';
1011                         }
1012                         
1013                         $top_item = item_store($datarray);
1014                         $r = q("SELECT * FROM `item` WHERE `id` = %d AND `uid` = %d LIMIT 1",
1015                                 intval($top_item),
1016                                 intval($uid)
1017                         );                      
1018                         if(count($r)) {
1019                                 $orig_post = $r[0];
1020                                 logger('fb: new top level item posted');
1021                         }
1022                 }
1023
1024                 if(isset($entry->likes) && isset($entry->likes->data))
1025                         $likers = $entry->likes->data;
1026                 else
1027                         $likers = null;
1028
1029                 if(isset($entry->comments) && isset($entry->comments->data))
1030                         $comments = $entry->comments->data;
1031                 else
1032                         $comments = null;
1033
1034                 if(is_array($likers)) {
1035                         foreach($likers as $likes) {
1036
1037                                 if(! $orig_post)
1038                                         continue;
1039
1040                                 // If we posted the like locally, it will be found with our url, not the FB url.
1041
1042                                 $second_url = (($likes->id == $self_id) ? $self[0]['url'] : 'http://facebook.com/profile.php?id=' . $likes->id); 
1043
1044                                 $r = q("SELECT * FROM `item` WHERE `parent-uri` = '%s' AND `uid` = %d AND `verb` = '%s' 
1045                                         AND ( `author-link` = '%s' OR `author-link` = '%s' ) LIMIT 1",
1046                                         dbesc($orig_post['uri']),
1047                                         intval($uid),
1048                                         dbesc(ACTIVITY_LIKE),
1049                                         dbesc('http://facebook.com/profile.php?id=' . $likes->id),
1050                                         dbesc($second_url)
1051                                 );
1052
1053                                 if(count($r))
1054                                         continue;
1055                                         
1056                                 $likedata = array();
1057                                 $likedata['parent'] = $top_item;
1058                                 $likedata['verb'] = ACTIVITY_LIKE;
1059                                 $likedata['gravity'] = 3;
1060                                 $likedata['uid'] = $uid;
1061                                 $likedata['wall'] = (($wall) ? 1 : 0);
1062                                 $likedata['uri'] = item_new_uri($a->get_baseurl(), $uid);
1063                                 $likedata['parent-uri'] = $orig_post['uri'];
1064                                 if($likes->id == $self_id)
1065                                         $likedata['contact-id'] = $self[0]['id'];
1066                                 else {
1067                                         $r = q("SELECT * FROM `contact` WHERE `notify` = '%s' AND `uid` = %d AND `blocked` = 0 AND `readonly` = 0 LIMIT 1",
1068                                                 dbesc($likes->id),
1069                                                 intval($uid)
1070                                         );
1071                                         if(count($r))
1072                                                 $likedata['contact-id'] = $r[0]['id'];
1073                                 }
1074                                 if(! x($likedata,'contact-id'))
1075                                         $likedata['contact-id'] = $orig_post['contact-id'];
1076
1077                                 $likedata['app'] = 'facebook';
1078                                 $likedata['verb'] = ACTIVITY_LIKE;                                              
1079                                 $likedata['author-name'] = $likes->name;
1080                                 $likedata['author-link'] = 'http://facebook.com/profile.php?id=' . $likes->id;
1081                                 $likedata['author-avatar'] = 'https://graph.facebook.com/' . $likes->id . '/picture';
1082                                 
1083                                 $author  = '[url=' . $likedata['author-link'] . ']' . $likedata['author-name'] . '[/url]';
1084                                 $objauthor =  '[url=' . $orig_post['author-link'] . ']' . $orig_post['author-name'] . '[/url]';
1085                                 $post_type = t('status');
1086                         $plink = '[url=' . $orig_post['plink'] . ']' . $post_type . '[/url]';
1087                                 $likedata['object-type'] = ACTIVITY_OBJ_NOTE;
1088
1089                                 $likedata['body'] = sprintf( t('%1$s likes %2$s\'s %3$s'), $author, $objauthor, $plink);
1090                                 $likedata['object'] = '<object><type>' . ACTIVITY_OBJ_NOTE . '</type><local>1</local>' . 
1091                                         '<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>';  
1092
1093                                 $item = item_store($likedata);                  
1094                         }
1095                 }
1096                 if(is_array($comments)) {
1097                         foreach($comments as $cmnt) {
1098
1099                                 if(! $orig_post)
1100                                         continue;
1101
1102                                 $r = q("SELECT * FROM `item` WHERE `uid` = %d AND ( `uri` = '%s' OR `extid` = '%s' ) LIMIT 1",
1103                                         intval($uid),
1104                                         dbesc('fb::' . $cmnt->id),
1105                                         dbesc('fb::' . $cmnt->id)
1106                                 );
1107                                 if(count($r))
1108                                         continue;
1109
1110                                 $cmntdata = array();
1111                                 $cmntdata['parent'] = $top_item;
1112                                 $cmntdata['verb'] = ACTIVITY_POST;
1113                                 $cmntdata['gravity'] = 6;
1114                                 $cmntdata['uid'] = $uid;
1115                                 $cmntdata['wall'] = (($wall) ? 1 : 0);
1116                                 $cmntdata['uri'] = 'fb::' . $cmnt->id;
1117                                 $cmntdata['parent-uri'] = $orig_post['uri'];
1118                                 if($cmnt->from->id == $self_id) {
1119                                         $cmntdata['contact-id'] = $self[0]['id'];
1120                                 }
1121                                 else {
1122                                         $r = q("SELECT * FROM `contact` WHERE `notify` = '%s' AND `uid` = %d LIMIT 1",
1123                                                 dbesc($cmnt->from->id),
1124                                                 intval($uid)
1125                                         );
1126                                         if(count($r)) {
1127                                                 $cmntdata['contact-id'] = $r[0]['id'];
1128                                                 if($r[0]['blocked'] || $r[0]['readonly'])
1129                                                         continue;
1130                                         }
1131                                 }
1132                                 if(! x($cmntdata,'contact-id'))
1133                                         $cmntdata['contact-id'] = $orig_post['contact-id'];
1134
1135                                 $cmntdata['app'] = 'facebook';
1136                                 $cmntdata['created'] = datetime_convert('UTC','UTC',$cmnt->created_time);
1137                                 $cmntdata['edited']  = datetime_convert('UTC','UTC',$cmnt->created_time);
1138                                 $cmntdata['verb'] = ACTIVITY_POST;                                              
1139                                 $cmntdata['author-name'] = $cmnt->from->name;
1140                                 $cmntdata['author-link'] = 'http://facebook.com/profile.php?id=' . $cmnt->from->id;
1141                                 $cmntdata['author-avatar'] = 'https://graph.facebook.com/' . $cmnt->from->id . '/picture';
1142                                 $cmntdata['body'] = $cmnt->message;
1143                                 $item = item_store($cmntdata);                  
1144                         }
1145                 }
1146         }
1147 }
1148