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