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