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