]> git.mxchange.org Git - friendica-addons.git/blob - facebook/facebook.php
more FB link improvements
[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                 $linkvalue = ((x($_POST,'facebook_linking')) ? intval($_POST['facebook_linking']) : 0);
298                 set_pconfig($uid,'facebook','no_linking', (($linkvalue) ? 0 : 1));
299
300                 // FB linkage was allowed but has just been turned off - remove all FB contacts and posts
301
302                 if((! intval($no_linking)) && (! intval($linkvalue))) {
303                         $r = q("SELECT `id` FROM `contact` WHERE `uid` = %d AND `network` = '%s' ",
304                                 intval($uid),
305                                 dbesc(NETWORK_FACEBOOK)
306                         );
307                         if(count($r)) {
308                                 require_once('include/Contact.php');
309                                 foreach($r as $rr)
310                                         contact_remove($rr['id']);
311                         }
312                 }
313                 elseif(intval($no_linking) && intval($linkvalue)) {
314                         // FB linkage is now allowed - import stuff.
315                         fb_get_self($uid);
316                         fb_get_friends($uid);
317                         fb_consume_all($uid);
318                 }
319
320                 info( t('Settings updated.') . EOL);
321         } 
322
323         return;         
324 }
325
326 // Facebook settings form
327
328 function facebook_content(&$a) {
329
330         if(! local_user()) {
331                 notice( t('Permission denied.') . EOL);
332                 return '';
333         }
334
335         if($a->argc > 1 && $a->argv[1] === 'remove') {
336                 del_pconfig(local_user(),'facebook','post');
337                 info( t('Facebook disabled') . EOL);
338         }
339
340         if($a->argc > 1 && $a->argv[1] === 'friends') {
341                 fb_get_friends(local_user());
342                 info( t('Updating contacts') . EOL);
343         }
344
345
346         $fb_installed = get_pconfig(local_user(),'facebook','post');
347
348         $appid = get_config('facebook','appid');
349
350         if(! $appid) {
351                 notice( t('Facebook API key is missing.') . EOL);
352                 return '';
353         }
354
355         $a->page['htmlhead'] .= '<link rel="stylesheet" type="text/css" href="' 
356                 . $a->get_baseurl() . '/addon/facebook/facebook.css' . '" media="all" />' . "\r\n";
357
358         $o .= '<h3>' . t('Facebook Connect') . '</h3>';
359
360         if(! $fb_installed) { 
361                 $o .= '<div id="facebook-enable-wrapper">';
362
363                 $o .= '<a href="https://www.facebook.com/dialog/oauth?client_id=' . $appid . '&redirect_uri=' 
364                         . $a->get_baseurl() . '/facebook/' . $a->user['nickname'] . '&scope=publish_stream,read_stream,offline_access">' . t('Install Facebook connector for this account.') . '</a>';
365                 $o .= '</div>';
366         }
367
368         if($fb_installed) {
369                 $o .= '<div id="facebook-disable-wrapper">';
370
371                 $o .= '<a href="' . $a->get_baseurl() . '/facebook/remove' . '">' . t('Remove Facebook connector') . '</a></div>';
372
373                 $o .= '<div id="facebook-enable-wrapper">';
374
375                 $o .= '<a href="https://www.facebook.com/dialog/oauth?client_id=' . $appid . '&redirect_uri=' 
376                         . $a->get_baseurl() . '/facebook/' . $a->user['nickname'] . '&scope=publish_stream,read_stream,offline_access">' . t('Re-authenticate [This is necessary whenever your Facebook password is changed.]') . '</a>';
377                 $o .= '</div>';
378         
379                 $o .= '<div id="facebook-post-default-form">';
380                 $o .= '<form action="facebook" method="post" >';
381                 $post_by_default = get_pconfig(local_user(),'facebook','post_by_default');
382                 $checked = (($post_by_default) ? ' checked="checked" ' : '');
383                 $o .= '<input type="checkbox" name="post_by_default" value="1"' . $checked . '/>' . ' ' . t('Post to Facebook by default') . EOL;
384
385                 $no_linking = get_pconfig(local_user(),'facebook','no_linking');
386                 $checked = (($no_linking) ? '' : ' checked="checked" ');
387                 $o .= '<input type="checkbox" name="facebook_linking" value="1"' . $checked . '/>' . ' ' . t('Link all your Facebook friends and conversations on this website') . EOL ;
388
389                 $o .= '<p>' . t('Facebook conversations consist of your <em>profile wall</em> and your friend <em>stream</em>.');
390                 $o .= ' ' . t('On this website, your Facebook friend stream is only visible to you.');
391                 $o .= ' ' . t('The following settings determine the privacy of your Facebook profile wall on this website.') . '</p>';
392
393                 $private_wall = get_pconfig(local_user(),'facebook','private_wall');
394                 $checked = (($private_wall) ? ' checked="checked" ' : '');
395                 $o .= '<input type="checkbox" name="facebook_private_wall" value="1"' . $checked . '/>' . ' ' . t('On this website your Facebook profile wall conversations will only be visible to you') . EOL ;
396
397
398                 $no_wall = get_pconfig(local_user(),'facebook','no_wall');
399                 $checked = (($no_wall) ? ' checked="checked" ' : '');
400                 $o .= '<input type="checkbox" name="facebook_no_wall" value="1"' . $checked . '/>' . ' ' . t('Do not import your Facebook profile wall conversations') . EOL ;
401
402                 $o .= '<p>' . t('If you choose to link conversations and leave both of these boxes unchecked, your Facebook profile wall will be merged with your profile wall on this website and your privacy settings on this website will be used to determine who may see the conversations.') . '</p>';
403
404                 $o .= '<input type="submit" name="submit" value="' . t('Submit') . '" /></form></div>';
405         }
406
407         return $o;
408 }
409
410
411
412 function facebook_cron($a,$b) {
413
414         $last = get_config('facebook','last_poll');
415         
416         $poll_interval = intval(get_config('facebook','poll_interval'));
417         if(! $poll_interval)
418                 $poll_interval = 3600;
419
420         if($last) {
421                 $next = $last + $poll_interval;
422                 if($next > time()) 
423                         return;
424         }
425
426         logger('facebook_cron');
427
428
429         // Find the FB users on this site and randomize in case one of them
430         // uses an obscene amount of memory. It may kill this queue run
431         // but hopefully we'll get a few others through on each run. 
432
433         $r = q("SELECT * FROM `pconfig` WHERE `cat` = 'facebook' AND `k` = 'post' AND `v` = '1' ORDER BY RAND() ");
434         if(count($r)) {
435                 foreach($r as $rr) {
436                         if(get_pconfig($rr['uid'],'facebook','no_linking'))
437                                 continue;
438                         $ab = intval(get_config('system','account_abandon_days'));
439                         if($ab > 0) {
440                                 $z = q("SELECT `uid` FROM `user` WHERE `uid` = %d AND `login_date` > UTC_TIMESTAMP() - INTERVAL %d DAY LIMIT 1",
441                                         intval($rr['uid']),
442                                         intval($ab)
443                                 );
444                                 if(! count($z))
445                                         continue;
446                         }
447
448                         // check for new friends once a day
449                         $last_friend_check = get_pconfig($rr['uid'],'facebook','friend_check');
450                         if($last_friend_check) 
451                                 $next_friend_check = $last_friend_check + 86400;
452                         if($next_friend_check <= time()) {
453                                 fb_get_friends($rr['uid']);
454                                 set_pconfig($rr['uid'],'facebook','friend_check',time());
455                         }
456                         fb_consume_all($rr['uid']);
457                 }
458         }       
459
460         set_config('facebook','last_poll', time());
461
462 }
463
464
465
466 function facebook_plugin_settings(&$a,&$b) {
467
468         $b .= '<div class="settings-block">';
469         $b .= '<h3>' . t('Facebook') . '</h3>';
470         $b .= '<a href="facebook">' . t('Facebook Connector Settings') . '</a><br />';
471         $b .= '</div>';
472
473 }
474
475 function facebook_jot_nets(&$a,&$b) {
476         if(! local_user())
477                 return;
478
479         $fb_post = get_pconfig(local_user(),'facebook','post');
480         if(intval($fb_post) == 1) {
481                 $fb_defpost = get_pconfig(local_user(),'facebook','post_by_default');
482                 $selected = ((intval($fb_defpost) == 1) ? ' checked="checked" ' : '');
483                 $b .= '<div class="profile-jot-net"><input type="checkbox" name="facebook_enable"' . $selected . ' value="1" /> ' 
484                         . t('Post to Facebook') . '</div>';     
485         }
486 }
487
488
489 function facebook_post_hook(&$a,&$b) {
490
491
492         if($b['deleted'] || ($b['created'] !== $b['edited']))
493                 return;
494
495         /**
496          * Post to Facebook stream
497          */
498
499         require_once('include/group.php');
500
501         logger('Facebook post');
502
503         $reply = false;
504         $likes = false;
505
506         $toplevel = (($b['id'] == $b['parent']) ? true : false);
507
508         if(strstr($b['postopts'],'facebook')) {
509
510                 $linking = ((get_pconfig($b['uid'],'facebook','no_linking')) ? 0 : 1);
511
512                 if((! $toplevel) && ($linking)) {
513                         $r = q("SELECT * FROM `item` WHERE `id` = %d AND `uid` = %d LIMIT 1",
514                                 intval($b['parent']),
515                                 intval($b['uid'])
516                         );
517                         if(count($r) && substr($r[0]['uri'],0,4) === 'fb::')
518                                 $reply = substr($r[0]['uri'],4);
519                         elseif(count($r) && substr($r[0]['extid'],0,4) === 'fb::')
520                                 $reply = substr($r[0]['extid'],4);
521                         else
522                                 return;
523                         logger('facebook reply id=' . $reply);
524                 }
525
526                 if($b['private'] && $reply === false) {
527                         $allow_people = expand_acl($b['allow_cid']);
528                         $allow_groups = expand_groups(expand_acl($b['allow_gid']));
529                         $deny_people  = expand_acl($b['deny_cid']);
530                         $deny_groups  = expand_groups(expand_acl($b['deny_gid']));
531
532                         $recipients = array_unique(array_merge($allow_people,$allow_groups));
533                         $deny = array_unique(array_merge($deny_people,$deny_groups));
534
535                         $allow_str = dbesc(implode(', ',$recipients));
536                         if($allow_str) {
537                                 $r = q("SELECT `notify` FROM `contact` WHERE `id` IN ( $allow_str ) AND `network` = 'face'"); 
538                                 $allow_arr = array();
539                                 if(count($r)) 
540                                         foreach($r as $rr)
541                                                 $allow_arr[] = $rr['notify'];
542                         }
543
544                         $deny_str = dbesc(implode(', ',$deny));
545                         if($deny_str) {
546                                 $r = q("SELECT `notify` FROM `contact` WHERE `id` IN ( $deny_str ) AND `network` = 'face'"); 
547                                 $deny_arr = array();
548                                 if(count($r)) 
549                                         foreach($r as $rr)
550                                                 $deny_arr[] = $rr['notify'];
551                         }
552
553                         if(count($deny_arr) && (! count($allow_arr))) {
554
555                                 // One or more FB folks were denied access but nobody on FB was specifically allowed access.
556                                 // This might cause the post to be open to public on Facebook, but only to selected members
557                                 // on another network. Since this could potentially leak a post to somebody who was denied, 
558                                 // we will skip posting it to Facebook with a slightly vague but relevant message that will 
559                                 // hopefully lead somebody to this code comment for a better explanation of what went wrong.
560
561                                 notice( t('Post to Facebook cancelled because of multi-network access permission conflict.') . EOL);
562                                 return;
563                         }
564
565
566                         // if it's a private message but no Facebook members are allowed or denied, skip Facebook post
567
568                         if((! count($allow_arr)) && (! count($deny_arr)))
569                                 return;
570                 }
571
572                 if($b['verb'] == ACTIVITY_LIKE)
573                         $likes = true;                          
574
575
576                 $appid  = get_config('facebook', 'appid'  );
577                 $secret = get_config('facebook', 'appsecret' );
578
579                 if($appid && $secret) {
580
581                         logger('facebook: have appid+secret');
582
583                         $fb_token  = get_pconfig($b['uid'],'facebook','access_token');
584
585
586                         // post to facebook if it's a public post and we've ticked the 'post to Facebook' box, 
587                         // or it's a private message with facebook participants
588                         // or it's a reply or likes action to an existing facebook post                 
589
590                         if($fb_token && ($toplevel || $b['private'] || $reply)) {
591                                 logger('facebook: able to post');
592                                 require_once('library/facebook.php');
593                                 require_once('include/bbcode.php');     
594
595                                 $msg = $b['body'];
596
597                                 logger('Facebook post: original msg=' . $msg, LOGGER_DATA);
598
599                                 // make links readable before we strip the code
600
601                                 // unless it's a dislike - just send the text as a comment
602
603                                 if($b['verb'] == ACTIVITY_DISLIKE)
604                                         $msg = trim(strip_tags(bbcode($msg)));
605
606                                 $search_str = $a->get_baseurl() . '/search';
607
608                                 if(preg_match("/\[url=(.*?)\](.*?)\[\/url\]/is",$msg,$matches)) {
609
610                                         // don't use hashtags for message link
611
612                                         if(strpos($matches[2],$search_str) === false) {
613                                                 $link = $matches[1];
614                                                 if(substr($matches[2],0,5) != '[img]')
615                                                         $linkname = $matches[2];
616                                         }
617                                 }
618
619                                 $msg = preg_replace("/\[url=(.*?)\](.*?)\[\/url\]/is",'$2 $1',$msg);
620
621                                 if(preg_match("/\[img\](.*?)\[\/img\]/is",$msg,$matches))
622                                         $image = $matches[1];
623
624                                 $msg = preg_replace("/\[img\](.*?)\[\/img\]/is", t('Image: ') . '$1', $msg);
625
626                                 if((strpos($link,z_root()) !== false) && (! $image))
627                                         $image = $a->get_baseurl() . '/images/friendika-64.jpg';
628
629                                 $msg = trim(strip_tags(bbcode($msg)));
630                                 $msg = html_entity_decode($msg,ENT_QUOTES,'UTF-8');
631
632                                 // add any attachments as text urls
633
634                             $arr = explode(',',$b['attach']);
635
636                             if(count($arr)) {
637                                         $msg .= "\n";
638                                 foreach($arr as $r) {
639                                 $matches = false;
640                                                 $cnt = preg_match('|\[attach\]href=\"(.*?)\" size=\"(.*?)\" type=\"(.*?)\" title=\"(.*?)\"\[\/attach\]|',$r,$matches);
641                                                 if($cnt) {
642                                                         $msg .= $matches[1];
643                                                 }
644                                         }
645                                 }
646
647                                 if (strlen($msg) > FACEBOOK_MAXPOSTLEN) {
648                                         $shortlink = "";
649                                         require_once('library/slinky.php');
650
651                                         $display_url = $a->get_baseurl() . '/display/' . $a->user['nickname'] . '/' . $b['id'];
652                                         $slinky = new Slinky( $display_url );
653                                         // setup a cascade of shortening services
654                                         // try to get a short link from these services
655                                         // in the order ur1.ca, trim, id.gd, tinyurl
656                                         $slinky->set_cascade( array( new Slinky_UR1ca(), new Slinky_Trim(), new Slinky_IsGd(), new Slinky_TinyURL() ) );
657                                         $shortlink = $slinky->short();
658                                         // the new message will be shortened such that "... $shortlink"
659                                         // will fit into the character limit
660                                         $msg = substr($msg, 0, FACEBOOK_MAXPOSTLEN - strlen($shortlink) - 4);
661                                         $msg .= '... ' . $shortlink;
662                                 }
663                                 if(! strlen($msg))
664                                         return;
665
666                                 logger('Facebook post: msg=' . $msg, LOGGER_DATA);
667
668                                 if($likes) { 
669                                         $postvars = array('access_token' => $fb_token);
670                                 }
671                                 else {
672                                         $postvars = array(
673                                                 'access_token' => $fb_token, 
674                                                 'message' => $msg
675                                         );
676                                         if(isset($image))
677                                                 $postvars['picture'] = $image;
678                                         if(isset($link))
679                                                 $postvars['link'] = $link;
680                                         if(isset($linkname))
681                                                 $postvars['name'] = $linkname;
682                                 }
683
684                                 if(($b['private']) && (! $b['parent'])) {
685                                         $postvars['privacy'] = '{"value": "CUSTOM", "friends": "SOME_FRIENDS"';
686                                         if(count($allow_arr))
687                                                 $postvars['privacy'] .= ',"allow": "' . implode(',',$allow_arr) . '"';
688                                         if(count($deny_arr))
689                                                 $postvars['privacy'] .= ',"deny": "' . implode(',',$deny_arr) . '"';
690                                         $postvars['privacy'] .= '}';
691
692                                 }
693
694                                 if($reply) {
695                                         $url = 'https://graph.facebook.com/' . $reply . '/' . (($likes) ? 'likes' : 'comments');
696                                 }
697                                 else { 
698                                         $url = 'https://graph.facebook.com/me/feed';
699                                         if($b['plink'])
700                                                 $postvars['actions'] = '{"name": "' . t('View on Friendika') . '", "link": "' .  $b['plink'] . '"}';
701                                 }
702
703                                 logger('facebook: post to ' . $url);
704                                 logger('facebook: postvars: ' . print_r($postvars,true));
705
706                                 // "test_mode" prevents anything from actually being posted.
707                                 // Otherwise, let's do it. 
708
709                                 if(! get_config('facebook','test_mode')) {
710                                         $x = post_url($url, $postvars);
711
712                                         $retj = json_decode($x);
713                                         if($retj->id) {
714                                                 q("UPDATE `item` SET `extid` = '%s' WHERE `id` = %d LIMIT 1",
715                                                         dbesc('fb::' . $retj->id),
716                                                         intval($b['id'])
717                                                 );
718                                         }
719                                         else {
720                                                 if(! $likes) {
721                                                         $s = serialize(array('url' => $url, 'item' => $b['id'], 'post' => $postvars));
722                                                         require_once('include/queue_fn.php');
723                                                         add_to_queue($a->contact,NETWORK_FACEBOOK,$s);
724                                                         notice( t('Facebook post failed. Queued for retry.') . EOL);
725                                                 }
726                                         }
727                                         
728                                         logger('Facebook post returns: ' . $x, LOGGER_DEBUG);
729                                 }
730                         }
731                 }
732         }
733 }
734
735
736 function facebook_post_local(&$a,&$b) {
737
738         // Figure out if Facebook posting is enabled for this post and file it in 'postopts'
739         // where we will discover it during background delivery.
740
741         // This can only be triggered by a local user posting to their own wall.
742
743         if((local_user()) && (local_user() == $b['uid'])) {
744
745                 $fb_post   = intval(get_pconfig(local_user(),'facebook','post'));
746                 $fb_enable = (($fb_post && x($_REQUEST,'facebook_enable')) ? intval($_REQUEST['facebook_enable']) : 0);
747
748                 // if API is used, default to the chosen settings
749                 if($_REQUEST['api_source'] && intval(get_pconfig(local_user(),'facebook','post_by_default')))
750                         $fb_enable = 1;
751
752                 if(! $fb_enable)
753                         return;
754
755                 if(strlen($b['postopts']))
756                         $b['postopts'] .= ',';
757                 $b['postopts'] .= 'facebook';
758         }
759 }
760
761
762 function fb_queue_hook(&$a,&$b) {
763
764         $qi = q("SELECT * FROM `queue` WHERE `network` = '%s'",
765                 dbesc(NETWORK_FACEBOOK)
766         );
767         if(! count($qi))
768                 return;
769
770         require_once('include/queue_fn.php');
771
772         foreach($qi as $x) {
773                 if($x['network'] !== NETWORK_FACEBOOK)
774                         continue;
775
776                 logger('facebook_queue: run');
777
778                 $r = q("SELECT `user`.* FROM `user` LEFT JOIN `contact` on `contact`.`uid` = `user`.`uid` 
779                         WHERE `contact`.`self` = 1 AND `contact`.`id` = %d LIMIT 1",
780                         intval($x['cid'])
781                 );
782                 if(! count($r))
783                         continue;
784
785                 $user = $r[0];
786
787                 $appid  = get_config('facebook', 'appid'  );
788                 $secret = get_config('facebook', 'appsecret' );
789
790                 if($appid && $secret) {
791                         $fb_post   = intval(get_pconfig($user['uid'],'facebook','post'));
792                         $fb_token  = get_pconfig($user['uid'],'facebook','access_token');
793
794                         if($fb_post && $fb_token) {
795                                 logger('facebook_queue: able to post');
796                                 require_once('library/facebook.php');
797
798                                 $z = unserialize($x['content']);
799                                 $item = $z['item'];
800                                 $j = post_url($z['url'],$z['post']);
801
802                                 $retj = json_decode($j);
803                                 if($retj->id) {
804                                         q("UPDATE `item` SET `extid` = '%s' WHERE `id` = %d LIMIT 1",
805                                                 dbesc('fb::' . $retj->id),
806                                                 intval($item)
807                                         );
808                                         logger('facebook_queue: success: ' . $j); 
809                                         remove_queue_item($x['id']);
810                                 }
811                                 else {
812                                         logger('facebook_queue: failed: ' . $j);
813                                         update_queue_time($x['id']);
814                                 }
815                         }
816                 }
817         }
818 }
819
820 function fb_consume_all($uid) {
821
822         require_once('include/items.php');
823
824         $access_token = get_pconfig($uid,'facebook','access_token');
825         if(! $access_token)
826                 return;
827         
828         if(! get_pconfig($uid,'facebook','no_wall')) {
829                 $private_wall = intval(get_pconfig($uid,'facebook','private_wall'));
830                 $s = fetch_url('https://graph.facebook.com/me/feed?access_token=' . $access_token);
831                 if($s) {
832                         $j = json_decode($s);
833                         logger('fb_consume_stream: wall: ' . print_r($j,true), LOGGER_DATA);
834                         fb_consume_stream($uid,$j,($private_wall) ? false : true);
835                 }
836         }
837         $s = fetch_url('https://graph.facebook.com/me/home?access_token=' . $access_token);
838         if($s) {
839                 $j = json_decode($s);
840                 logger('fb_consume_stream: feed: ' . print_r($j,true), LOGGER_DATA);
841                 fb_consume_stream($uid,$j,false);
842         }
843
844 }
845
846 function fb_consume_stream($uid,$j,$wall = false) {
847
848         $a = get_app();
849
850
851         $user = q("SELECT `nickname`, `blockwall` FROM `user` WHERE `uid` = %d AND `account_expired` = 0 LIMIT 1",
852                 intval($uid)
853         );
854         if(! count($user))
855                 return;
856
857         $my_local_url = $a->get_baseurl() . '/profile/' . $user[0]['nickname'];
858
859         $no_linking = get_pconfig($uid,'facebook','no_linking');
860         if($no_linking)
861                 return;
862
863         $self = q("SELECT * FROM `contact` WHERE `self` = 1 AND `uid` = %d LIMIT 1",
864                 intval($uid)
865         );
866
867
868         $self_id = get_pconfig($uid,'facebook','self_id');
869         if(! count($j->data) || (! strlen($self_id)))
870                 return;
871
872         foreach($j->data as $entry) {
873                 logger('fb_consume: entry: ' . print_r($entry,true), LOGGER_DATA);
874                 $datarray = array();
875
876                 $r = q("SELECT * FROM `item` WHERE ( `uri` = '%s' OR `extid` = '%s') AND `uid` = %d LIMIT 1",
877                                 dbesc('fb::' . $entry->id),
878                                 dbesc('fb::' . $entry->id),
879                                 intval($uid)
880                 );
881                 if(count($r)) {
882                         $post_exists = true;
883                         $orig_post = $r[0];
884                         $top_item = $r[0]['id'];
885                 }
886                 else {
887                         $post_exists = false;
888                         $orig_post = null;
889                 }
890
891                 if(! $orig_post) {
892                         $datarray['gravity'] = 0;
893                         $datarray['uid'] = $uid;
894                         $datarray['wall'] = (($wall) ? 1 : 0);
895                         $datarray['uri'] = $datarray['parent-uri'] = 'fb::' . $entry->id;
896                         $from = $entry->from;
897                         if($from->id == $self_id)
898                                 $datarray['contact-id'] = $self[0]['id'];
899                         else {
900                                 $r = q("SELECT * FROM `contact` WHERE `notify` = '%s' AND `uid` = %d AND `blocked` = 0 AND `readonly` = 0 LIMIT 1",
901                                         dbesc($from->id),
902                                         intval($uid)
903                                 );
904                                 if(count($r))
905                                         $datarray['contact-id'] = $r[0]['id'];
906                         }
907
908                         // don't store post if we don't have a contact
909
910                         if(! x($datarray,'contact-id')) {
911                                 logger('no contact: post ignored');
912                                 continue; 
913                         }
914
915                         $datarray['verb'] = ACTIVITY_POST;                                              
916                         if($wall) {
917                                 $datarray['owner-name'] = $self[0]['name'];
918                                 $datarray['owner-link'] = $self[0]['url'];
919                                 $datarray['owner-avatar'] = $self[0]['thumb'];
920                         }
921                         if(isset($entry->application) && isset($entry->application->name) && strlen($entry->application->name))
922                                 $datarray['app'] = strip_tags($entry->application->name);
923                         else
924                                 $datarray['app'] = 'facebook';
925                         $datarray['author-name'] = $from->name;
926                         $datarray['author-link'] = 'http://facebook.com/profile.php?id=' . $from->id;
927                         $datarray['author-avatar'] = 'https://graph.facebook.com/' . $from->id . '/picture';
928                         $datarray['plink'] = $datarray['author-link'] . '&v=wall&story_fbid=' . substr($entry->id,strpos($entry->id,'_') + 1);
929
930                         $datarray['body'] = $entry->message;
931
932                         if($entry->picture && $entry->link) {
933                                 $datarray['body'] .= "\n\n" . '[url=' . $entry->link . '][img]' . $entry->picture . '[/img][/url]';
934                         }
935                         else {
936                                 if($entry->picture)
937                                         $datarray['body'] .= "\n\n" . '[img]' . $entry->picture . '[/img]';
938                                 if($entry->link)
939                                         $datarray['body'] .= "\n" . '[url=' . $entry->link . ']' . t('link') . '[/url]';
940                         }
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