]> git.mxchange.org Git - friendica.git/blob - addon/facebook/facebook.php
cripple account when expired
[friendica.git] / addon / 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                         // check for new friends once a day
432                         $last_friend_check = get_pconfig($rr['uid'],'facebook','friend_check');
433                         if($last_friend_check) 
434                                 $next_friend_check = $last_friend_check + 86400;
435                         if($next_friend_check <= time()) {
436                                 fb_get_friends($rr['uid']);
437                                 set_pconfig($rr['uid'],'facebook','friend_check',time());
438                         }
439                         fb_consume_all($rr['uid']);
440                 }
441         }       
442
443         set_config('facebook','last_poll', time());
444
445 }
446
447
448
449 function facebook_plugin_settings(&$a,&$b) {
450
451         $b .= '<div class="settings-block">';
452         $b .= '<h3>' . t('Facebook') . '</h3>';
453         $b .= '<a href="facebook">' . t('Facebook Connector Settings') . '</a><br />';
454         $b .= '</div>';
455
456 }
457
458 function facebook_jot_nets(&$a,&$b) {
459         if(! local_user())
460                 return;
461
462         $fb_post = get_pconfig(local_user(),'facebook','post');
463         if(intval($fb_post) == 1) {
464                 $fb_defpost = get_pconfig(local_user(),'facebook','post_by_default');
465                 $selected = ((intval($fb_defpost) == 1) ? ' checked="checked" ' : '');
466                 $b .= '<div class="profile-jot-net"><input type="checkbox" name="facebook_enable"' . $selected . 'value="1" /> ' 
467                         . t('Post to Facebook') . '</div>';     
468         }
469 }
470
471
472 function facebook_post_hook(&$a,&$b) {
473
474         /**
475          * Post to Facebook stream
476          */
477
478         require_once('include/group.php');
479
480         logger('Facebook post');
481
482         $reply = false;
483         $likes = false;
484
485         if((local_user()) && (local_user() == $b['uid'])) {
486
487                 // Facebook is not considered a private network
488                 if($b['prvnets'] && $b['private'])
489                         return;
490
491                 if($b['parent']) {
492                         $r = q("SELECT * FROM `item` WHERE `id` = %d AND `uid` = %d LIMIT 1",
493                                 intval($b['parent']),
494                                 intval(local_user())
495                         );
496                         if(count($r) && substr($r[0]['uri'],0,4) === 'fb::')
497                                 $reply = substr($r[0]['uri'],4);
498                         elseif(count($r) && substr($r[0]['extid'],0,4) === 'fb::')
499                                 $reply = substr($r[0]['extid'],4);
500                         else
501                                 return;
502                         logger('facebook reply id=' . $reply);
503                 }
504
505                 if($b['private'] && $reply === false) {
506                         $allow_people = expand_acl($b['allow_cid']);
507                         $allow_groups = expand_groups(expand_acl($b['allow_gid']));
508                         $deny_people  = expand_acl($b['deny_cid']);
509                         $deny_groups  = expand_groups(expand_acl($b['deny_gid']));
510
511                         $recipients = array_unique(array_merge($allow_people,$allow_groups));
512                         $deny = array_unique(array_merge($deny_people,$deny_groups));
513
514                         $allow_str = dbesc(implode(', ',$recipients));
515                         if($allow_str) {
516                                 $r = q("SELECT `notify` FROM `contact` WHERE `id` IN ( $allow_str ) AND `network` = 'face'"); 
517                                 $allow_arr = array();
518                                 if(count($r)) 
519                                         foreach($r as $rr)
520                                                 $allow_arr[] = $rr['notify'];
521                         }
522
523                         $deny_str = dbesc(implode(', ',$deny));
524                         if($deny_str) {
525                                 $r = q("SELECT `notify` FROM `contact` WHERE `id` IN ( $deny_str ) AND `network` = 'face'"); 
526                                 $deny_arr = array();
527                                 if(count($r)) 
528                                         foreach($r as $rr)
529                                                 $deny_arr[] = $rr['notify'];
530                         }
531
532                         if(count($deny_arr) && (! count($allow_arr))) {
533
534                                 // One or more FB folks were denied access but nobody on FB was specifically allowed access.
535                                 // This might cause the post to be open to public on Facebook, but only to selected members
536                                 // on another network. Since this could potentially leak a post to somebody who was denied, 
537                                 // we will skip posting it to Facebook with a slightly vague but relevant message that will 
538                                 // hopefully lead somebody to this code comment for a better explanation of what went wrong.
539
540                                 notice( t('Post to Facebook cancelled because of multi-network access permission conflict.') . EOL);
541                                 return;
542                         }
543
544
545                         // if it's a private message but no Facebook members are allowed or denied, skip Facebook post
546
547                         if((! count($allow_arr)) && (! count($deny_arr)))
548                                 return;
549                 }
550
551                 if($b['verb'] == ACTIVITY_LIKE)
552                         $likes = true;                          
553
554
555                 $appid  = get_config('facebook', 'appid'  );
556                 $secret = get_config('facebook', 'appsecret' );
557
558                 if($appid && $secret) {
559
560                         logger('facebook: have appid+secret');
561
562                         $fb_post   = intval(get_pconfig(local_user(),'facebook','post'));
563                         $fb_enable = (($fb_post && x($_POST,'facebook_enable')) ? intval($_POST['facebook_enable']) : 0);
564                         $fb_token  = get_pconfig(local_user(),'facebook','access_token');
565
566                         // if API is used, default to the chosen settings
567                         if($_POST['api_source'] && intval(get_pconfig(local_user(),'facebook','post_by_default')))
568                                 $fb_enable = 1;
569
570
571
572
573                         logger('facebook: $fb_post: ' . $fb_post . ' $fb_enable: ' . $fb_enable . ' $fb_token: ' . $fb_token,LOGGER_DEBUG); 
574
575                         // post to facebook if it's a public post and we've ticked the 'post to Facebook' box, 
576                         // or it's a private message with facebook participants
577                         // or it's a reply or likes action to an existing facebook post                 
578
579                         if($fb_post && $fb_token && ($fb_enable || $b['private'] || $reply)) {
580                                 logger('facebook: able to post');
581                                 require_once('library/facebook.php');
582                                 require_once('include/bbcode.php');     
583
584                                 $msg = $b['body'];
585
586                                 logger('Facebook post: original msg=' . $msg, LOGGER_DATA);
587
588                                 // make links readable before we strip the code
589
590                                 // unless it's a dislike - just send the text as a comment
591
592                                 if($b['verb'] == ACTIVITY_DISLIKE)
593                                         $msg = trim(strip_tags(bbcode($msg)));
594
595                                 $search_str = $a->get_baseurl() . '/search';
596
597                                 if(preg_match("/\[url=(.*?)\](.*?)\[\/url\]/is",$msg,$matches)) {
598
599                                         // don't use hashtags for message link
600
601                                         if(strpos($matches[2],$search_str) === false) {
602                                                 $link = $matches[1];
603                                                 if(substr($matches[2],0,5) != '[img]')
604                                                         $linkname = $matches[2];
605                                         }
606                                 }
607
608                                 $msg = preg_replace("/\[url=(.*?)\](.*?)\[\/url\]/is",'$2 $1',$msg);
609
610                                 if(preg_match("/\[img\](.*?)\[\/img\]/is",$msg,$matches))
611                                         $image = $matches[1];
612
613                                 $msg = preg_replace("/\[img\](.*?)\[\/img\]/is", t('Image: ') . '$1', $msg);
614
615                                 if((strpos($link,z_root()) !== false) && (! $image))
616                                         $image = $a->get_baseurl() . '/images/friendika-64.jpg';
617
618                                 $msg = trim(strip_tags(bbcode($msg)));
619                                 $msg = html_entity_decode($msg,ENT_QUOTES,'UTF-8');
620
621                                 // add any attachments as text urls
622
623                             $arr = explode(',',$b['attach']);
624
625                             if(count($arr)) {
626                                         $msg .= "\n";
627                                 foreach($arr as $r) {
628                                 $matches = false;
629                                                 $cnt = preg_match('|\[attach\]href=\"(.*?)\" size=\"(.*?)\" type=\"(.*?)\" title=\"(.*?)\"\[\/attach\]|',$r,$matches);
630                                                 if($cnt) {
631                                                         $msg .= $matches[1];
632                                                 }
633                                         }
634                                 }
635
636                                 if (strlen($msg) > FACEBOOK_MAXPOSTLEN) {
637                                         $shortlink = "";
638                                         require_once('library/slinky.php');
639
640                                         $display_url = $a->get_baseurl() . '/display/' . $a->user['nickname'] . '/' . $b['id'];
641                                         $slinky = new Slinky( $display_url );
642                                         // setup a cascade of shortening services
643                                         // try to get a short link from these services
644                                         // in the order ur1.ca, trim, id.gd, tinyurl
645                                         $slinky->set_cascade( array( new Slinky_UR1ca(), new Slinky_Trim(), new Slinky_IsGd(), new Slinky_TinyURL() ) );
646                                         $shortlink = $slinky->short();
647                                         // the new message will be shortened such that "... $shortlink"
648                                         // will fit into the character limit
649                                         $msg = substr($msg, 0, FACEBOOK_MAXPOSTLEN - strlen($shortlink) - 4);
650                                         $msg .= '... ' . $shortlink;
651                                 }
652                                 if(! strlen($msg))
653                                         return;
654
655                                 logger('Facebook post: msg=' . $msg, LOGGER_DATA);
656
657                                 if($likes) { 
658                                         $postvars = array('access_token' => $fb_token);
659                                 }
660                                 else {
661                                         $postvars = array(
662                                                 'access_token' => $fb_token, 
663                                                 'message' => $msg
664                                         );
665                                         if(isset($image))
666                                                 $postvars['picture'] = $image;
667                                         if(isset($link))
668                                                 $postvars['link'] = $link;
669                                         if(isset($linkname))
670                                                 $postvars['name'] = $linkname;
671                                 }
672
673                                 if(($b['private']) && (! $b['parent'])) {
674                                         $postvars['privacy'] = '{"value": "CUSTOM", "friends": "SOME_FRIENDS"';
675                                         if(count($allow_arr))
676                                                 $postvars['privacy'] .= ',"allow": "' . implode(',',$allow_arr) . '"';
677                                         if(count($deny_arr))
678                                                 $postvars['privacy'] .= ',"deny": "' . implode(',',$deny_arr) . '"';
679                                         $postvars['privacy'] .= '}';
680
681                                 }
682
683                                 if($reply) {
684                                         $url = 'https://graph.facebook.com/' . $reply . '/' . (($likes) ? 'likes' : 'comments');
685                                 }
686                                 else { 
687                                         $url = 'https://graph.facebook.com/me/feed';
688                                         if($b['plink'])
689                                                 $postvars['actions'] = '{"name": "' . t('View on Friendika') . '", "link": "' .  $b['plink'] . '"}';
690                                 }
691
692                                 logger('facebook: post to ' . $url);
693                                 logger('facebook: postvars: ' . print_r($postvars,true));
694
695                                 // "test_mode" prevents anything from actually being posted.
696                                 // Otherwise, let's do it. 
697
698                                 if(! get_config('facebook','test_mode')) {
699                                         $x = post_url($url, $postvars);
700
701                                         $retj = json_decode($x);
702                                         if($retj->id) {
703                                                 q("UPDATE `item` SET `extid` = '%s' WHERE `id` = %d LIMIT 1",
704                                                         dbesc('fb::' . $retj->id),
705                                                         intval($b['id'])
706                                                 );
707                                         }
708                                         else {
709                                                 if(! $likes) {
710                                                         $s = serialize(array('url' => $url, 'item' => $b['id'], 'post' => $postvars));
711                                                         q("INSERT INTO `queue` ( `network`, `cid`, `created`, `last`, `content`)
712                                                                 VALUES ( '%s', %d, '%s', '%s', '%s') ",
713                                                                 dbesc(NETWORK_FACEBOOK),
714                                                                 intval($a->contact),
715                                                                 dbesc(datetime_convert()),
716                                                                 dbesc(datetime_convert()),
717                                                                 dbesc($s)
718                                                         );                                                              
719
720                                                         notice( t('Facebook post failed. Queued for retry.') . EOL);
721                                                 }
722                                         }
723                                         
724                                         logger('Facebook post returns: ' . $x, LOGGER_DEBUG);
725                                 }
726                         }
727                 }
728         }
729 }
730
731
732 function fb_queue_hook(&$a,&$b) {
733
734         $qi = q("SELECT * FROM `queue` WHERE `network` = '%s'",
735                 dbesc(NETWORK_FACEBOOK)
736         );
737         if(! count($qi))
738                 return;
739
740         require_once('include/queue_fn.php');
741
742         foreach($qi as $x) {
743                 if($x['network'] !== NETWORK_FACEBOOK)
744                         continue;
745
746                 logger('facebook_queue: run');
747
748                 $r = q("SELECT `user`.* FROM `user` LEFT JOIN `contact` on `contact`.`uid` = `user`.`uid` 
749                         WHERE `contact`.`self` = 1 AND `contact`.`id` = %d LIMIT 1",
750                         intval($x['cid'])
751                 );
752                 if(! count($r))
753                         continue;
754
755                 $user = $r[0];
756
757                 $appid  = get_config('facebook', 'appid'  );
758                 $secret = get_config('facebook', 'appsecret' );
759
760                 if($appid && $secret) {
761                         $fb_post   = intval(get_pconfig($user['uid'],'facebook','post'));
762                         $fb_token  = get_pconfig($user['uid'],'facebook','access_token');
763
764                         if($fb_post && $fb_token) {
765                                 logger('facebook_queue: able to post');
766                                 require_once('library/facebook.php');
767
768                                 $z = unserialize($x['content']);
769                                 $item = $z['item'];
770                                 $j = post_url($z['url'],$z['post']);
771
772                                 $retj = json_decode($j);
773                                 if($retj->id) {
774                                         q("UPDATE `item` SET `extid` = '%s' WHERE `id` = %d LIMIT 1",
775                                                 dbesc('fb::' . $retj->id),
776                                                 intval($item)
777                                         );
778                                         logger('facebook_queue: success: ' . $j); 
779                                         remove_queue_item($x['id']);
780                                 }
781                                 else {
782                                         logger('facebook_queue: failed: ' . $j);
783                                         update_queue_time($x['id']);
784                                 }
785                         }
786                 }
787         }
788 }
789
790 function fb_consume_all($uid) {
791
792         require_once('include/items.php');
793
794         $access_token = get_pconfig($uid,'facebook','access_token');
795         if(! $access_token)
796                 return;
797         
798         if(! get_pconfig($uid,'facebook','no_wall')) {
799                 $private_wall = intval(get_pconfig($uid,'facebook','private_wall'));
800                 $s = fetch_url('https://graph.facebook.com/me/feed?access_token=' . $access_token);
801                 if($s) {
802                         $j = json_decode($s);
803                         logger('fb_consume_stream: wall: ' . print_r($j,true), LOGGER_DATA);
804                         fb_consume_stream($uid,$j,($private_wall) ? false : true);
805                 }
806         }
807         $s = fetch_url('https://graph.facebook.com/me/home?access_token=' . $access_token);
808         if($s) {
809                 $j = json_decode($s);
810                 logger('fb_consume_stream: feed: ' . print_r($j,true), LOGGER_DATA);
811                 fb_consume_stream($uid,$j,false);
812         }
813
814 }
815
816 function fb_consume_stream($uid,$j,$wall = false) {
817
818         $a = get_app();
819
820
821         $user = q("SELECT `nickname`, `blockwall` FROM `user` WHERE `uid` = %d AND `account_expired` = 0 LIMIT 1",
822                 intval($uid)
823         );
824         if(! count($user))
825                 return;
826
827         $my_local_url = $a->get_baseurl() . '/profile/' . $user[0]['nickname'];
828
829         $no_linking = get_pconfig($uid,'facebook','no_linking');
830         if($no_linking)
831                 return;
832
833         $self = q("SELECT * FROM `contact` WHERE `self` = 1 AND `uid` = %d LIMIT 1",
834                 intval($uid)
835         );
836
837
838         $self_id = get_pconfig($uid,'facebook','self_id');
839         if(! count($j->data) || (! strlen($self_id)))
840                 return;
841
842         foreach($j->data as $entry) {
843                 logger('fb_consume: entry: ' . print_r($entry,true), LOGGER_DATA);
844                 $datarray = array();
845
846                 $r = q("SELECT * FROM `item` WHERE ( `uri` = '%s' OR `extid` = '%s') AND `uid` = %d LIMIT 1",
847                                 dbesc('fb::' . $entry->id),
848                                 dbesc('fb::' . $entry->id),
849                                 intval($uid)
850                 );
851                 if(count($r)) {
852                         $post_exists = true;
853                         $orig_post = $r[0];
854                         $top_item = $r[0]['id'];
855                 }
856                 else {
857                         $post_exists = false;
858                         $orig_post = null;
859                 }
860
861                 if(! $orig_post) {
862                         $datarray['gravity'] = 0;
863                         $datarray['uid'] = $uid;
864                         $datarray['wall'] = (($wall) ? 1 : 0);
865                         $datarray['uri'] = $datarray['parent-uri'] = 'fb::' . $entry->id;
866                         $from = $entry->from;
867                         if($from->id == $self_id)
868                                 $datarray['contact-id'] = $self[0]['id'];
869                         else {
870                                 $r = q("SELECT * FROM `contact` WHERE `notify` = '%s' AND `uid` = %d AND `blocked` = 0 AND `readonly` = 0 LIMIT 1",
871                                         dbesc($from->id),
872                                         intval($uid)
873                                 );
874                                 if(count($r))
875                                         $datarray['contact-id'] = $r[0]['id'];
876                         }
877
878                         // don't store post if we don't have a contact
879
880                         if(! x($datarray,'contact-id')) {
881                                 logger('no contact: post ignored');
882                                 continue; 
883                         }
884
885                         $datarray['verb'] = ACTIVITY_POST;                                              
886                         if($wall) {
887                                 $datarray['owner-name'] = $self[0]['name'];
888                                 $datarray['owner-link'] = $self[0]['url'];
889                                 $datarray['owner-avatar'] = $self[0]['thumb'];
890                         }
891                         if(isset($entry->application) && isset($entry->application->name) && strlen($entry->application->name))
892                                 $datarray['app'] = strip_tags($entry->application->name);
893                         else
894                                 $datarray['app'] = 'facebook';
895                         $datarray['author-name'] = $from->name;
896                         $datarray['author-link'] = 'http://facebook.com/profile.php?id=' . $from->id;
897                         $datarray['author-avatar'] = 'https://graph.facebook.com/' . $from->id . '/picture';
898                         $datarray['plink'] = $datarray['author-link'] . '&v=wall&story_fbid=' . substr($entry->id,strpos($entry->id,'_') + 1);
899
900                         $datarray['body'] = $entry->message;
901                         if($entry->picture)
902                                 $datarray['body'] .= "\n\n" . '[img]' . $entry->picture . '[/img]';
903                         if($entry->link)
904                                 $datarray['body'] .= "\n" . linkify($entry->link);
905                         if($entry->name)
906                                 $datarray['body'] .= "\n" . $entry->name;
907                         if($entry->caption)
908                                 $datarray['body'] .= "\n" . $entry->caption;
909                         if($entry->description)
910                                 $datarray['body'] .= "\n" . $entry->description;
911                         $datarray['created'] = datetime_convert('UTC','UTC',$entry->created_time);
912                         $datarray['edited'] = datetime_convert('UTC','UTC',$entry->updated_time);
913
914                         // If the entry has a privacy policy, we cannot assume who can or cannot see it,
915                         // as the identities are from a foreign system. Mark it as private to the owner.
916
917                         if($entry->privacy && $entry->privacy->value !== 'EVERYONE') {
918                                 $datarray['private'] = 1;
919                                 $datarray['allow_cid'] = '<' . $uid . '>';
920                         }
921                         
922                         $top_item = item_store($datarray);
923                         $r = q("SELECT * FROM `item` WHERE `id` = %d AND `uid` = %d LIMIT 1",
924                                 intval($top_item),
925                                 intval($uid)
926                         );                      
927                         if(count($r)) {
928                                 $orig_post = $r[0];
929                                 logger('fb: new top level item posted');
930                         }
931                 }
932
933                 if(isset($entry->likes) && isset($entry->likes->data))
934                         $likers = $entry->likes->data;
935                 else
936                         $likers = null;
937
938                 if(isset($entry->comments) && isset($entry->comments->data))
939                         $comments = $entry->comments->data;
940                 else
941                         $comments = null;
942
943                 if(is_array($likers)) {
944                         foreach($likers as $likes) {
945
946                                 if(! $orig_post)
947                                         continue;
948
949                                 // If we posted the like locally, it will be found with our url, not the FB url.
950
951                                 $second_url = (($likes->id == $self_id) ? $self[0]['url'] : 'http://facebook.com/profile.php?id=' . $likes->id); 
952
953                                 $r = q("SELECT * FROM `item` WHERE `parent-uri` = '%s' AND `uid` = %d AND `verb` = '%s' 
954                                         AND ( `author-link` = '%s' OR `author-link` = '%s' ) LIMIT 1",
955                                         dbesc($orig_post['uri']),
956                                         intval($uid),
957                                         dbesc(ACTIVITY_LIKE),
958                                         dbesc('http://facebook.com/profile.php?id=' . $likes->id),
959                                         dbesc($second_url)
960                                 );
961
962                                 if(count($r))
963                                         continue;
964                                         
965                                 $likedata = array();
966                                 $likedata['parent'] = $top_item;
967                                 $likedata['verb'] = ACTIVITY_LIKE;
968                                 $likedata['gravity'] = 3;
969                                 $likedata['uid'] = $uid;
970                                 $likedata['wall'] = (($wall) ? 1 : 0);
971                                 $likedata['uri'] = item_new_uri($a->get_baseurl(), $uid);
972                                 $likedata['parent-uri'] = $orig_post['uri'];
973                                 if($likes->id == $self_id)
974                                         $likedata['contact-id'] = $self[0]['id'];
975                                 else {
976                                         $r = q("SELECT * FROM `contact` WHERE `notify` = '%s' AND `uid` = %d AND `blocked` = 0 AND `readonly` = 0 LIMIT 1",
977                                                 dbesc($likes->id),
978                                                 intval($uid)
979                                         );
980                                         if(count($r))
981                                                 $likedata['contact-id'] = $r[0]['id'];
982                                 }
983                                 if(! x($likedata,'contact-id'))
984                                         $likedata['contact-id'] = $orig_post['contact-id'];
985
986                                 $likedata['app'] = 'facebook';
987                                 $likedata['verb'] = ACTIVITY_LIKE;                                              
988                                 $likedata['author-name'] = $likes->name;
989                                 $likedata['author-link'] = 'http://facebook.com/profile.php?id=' . $likes->id;
990                                 $likedata['author-avatar'] = 'https://graph.facebook.com/' . $likes->id . '/picture';
991                                 
992                                 $author  = '[url=' . $likedata['author-link'] . ']' . $likedata['author-name'] . '[/url]';
993                                 $objauthor =  '[url=' . $orig_post['author-link'] . ']' . $orig_post['author-name'] . '[/url]';
994                                 $post_type = t('status');
995                         $plink = '[url=' . $orig_post['plink'] . ']' . $post_type . '[/url]';
996                                 $likedata['object-type'] = ACTIVITY_OBJ_NOTE;
997
998                                 $likedata['body'] = sprintf( t('%1$s likes %2$s\'s %3$s'), $author, $objauthor, $plink);
999                                 $likedata['object'] = '<object><type>' . ACTIVITY_OBJ_NOTE . '</type><local>1</local>' . 
1000                                         '<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>';  
1001
1002                                 $item = item_store($likedata);                  
1003                         }
1004                 }
1005                 if(is_array($comments)) {
1006                         foreach($comments as $cmnt) {
1007
1008                                 if(! $orig_post)
1009                                         continue;
1010
1011                                 $r = q("SELECT * FROM `item` WHERE `uid` = %d AND ( `uri` = '%s' OR `extid` = '%s' ) LIMIT 1",
1012                                         intval($uid),
1013                                         dbesc('fb::' . $cmnt->id),
1014                                         dbesc('fb::' . $cmnt->id)
1015                                 );
1016                                 if(count($r))
1017                                         continue;
1018
1019                                 $cmntdata = array();
1020                                 $cmntdata['parent'] = $top_item;
1021                                 $cmntdata['verb'] = ACTIVITY_POST;
1022                                 $cmntdata['gravity'] = 6;
1023                                 $cmntdata['uid'] = $uid;
1024                                 $cmntdata['wall'] = (($wall) ? 1 : 0);
1025                                 $cmntdata['uri'] = 'fb::' . $cmnt->id;
1026                                 $cmntdata['parent-uri'] = $orig_post['uri'];
1027                                 if($cmnt->from->id == $self_id) {
1028                                         $cmntdata['contact-id'] = $self[0]['id'];
1029                                 }
1030                                 else {
1031                                         $r = q("SELECT * FROM `contact` WHERE `notify` = '%s' AND `uid` = %d LIMIT 1",
1032                                                 dbesc($cmnt->from->id),
1033                                                 intval($uid)
1034                                         );
1035                                         if(count($r)) {
1036                                                 $cmntdata['contact-id'] = $r[0]['id'];
1037                                                 if($r[0]['blocked'] || $r[0]['readonly'])
1038                                                         continue;
1039                                         }
1040                                 }
1041                                 if(! x($cmntdata,'contact-id'))
1042                                         $cmntdata['contact-id'] = $orig_post['contact-id'];
1043
1044                                 $cmntdata['app'] = 'facebook';
1045                                 $cmntdata['created'] = datetime_convert('UTC','UTC',$cmnt->created_time);
1046                                 $cmntdata['edited']  = datetime_convert('UTC','UTC',$cmnt->created_time);
1047                                 $cmntdata['verb'] = ACTIVITY_POST;                                              
1048                                 $cmntdata['author-name'] = $cmnt->from->name;
1049                                 $cmntdata['author-link'] = 'http://facebook.com/profile.php?id=' . $cmnt->from->id;
1050                                 $cmntdata['author-avatar'] = 'https://graph.facebook.com/' . $cmnt->from->id . '/picture';
1051                                 $cmntdata['body'] = $cmnt->message;
1052                                 $item = item_store($cmntdata);                  
1053                         }
1054                 }
1055         }
1056 }
1057