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