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