3 * Name: Facebook Connector
5 * Author: Mike Macgirvin <http://macgirvin.com/profile/mike>
9 * Installing the Friendika/Facebook connector
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
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'.
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.
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.
47 define('FACEBOOK_MAXPOSTLEN', 420);
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');
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');
68 /* declare the facebook_module function so that /facebook url requests will land here */
70 function facebook_module() {}
74 /* If a->argv[1] is a nickname, this is a callback from Facebook oauth requests. */
76 function facebook_init(&$a) {
82 $r = q("SELECT `uid` FROM `user` WHERE `nickname` = '%s' LIMIT 1",
89 $auth_code = (($_GET['code']) ? $_GET['code'] : '');
90 $error = (($_GET['error_description']) ? $_GET['error_description'] : '');
94 logger('facebook_init: Error: ' . $error);
96 if($auth_code && $uid) {
98 $appid = get_config('facebook','appid');
99 $appsecret = get_config('facebook', 'appsecret');
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);
106 logger('facebook_init: returned access token: ' . $x, LOGGER_DATA);
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);
117 fb_get_friends($uid);
118 fb_consume_all($uid);
127 function fb_get_self($uid) {
128 $access_token = get_pconfig($uid,'facebook','access_token');
131 $s = fetch_url('https://graph.facebook.com/me/?access_token=' . $access_token);
133 $j = json_decode($s);
134 set_pconfig($uid,'facebook','self_id',(string) $j->id);
140 function fb_get_friends($uid) {
142 $r = q("SELECT `id` FROM `user` WHERE `uid` = %d AND `account_expired` = 0 LIMIT 1",
148 $access_token = get_pconfig($uid,'facebook','access_token');
150 $no_linking = get_pconfig($uid,'facebook','no_linking');
156 $s = fetch_url('https://graph.facebook.com/me/friends?access_token=' . $access_token);
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);
163 foreach($j->data as $person) {
164 $s = fetch_url('https://graph.facebook.com/' . $person->id . '?access_token=' . $access_token);
166 $jp = json_decode($s);
167 logger('fb_get_friends: info: ' . print_r($jp,true), LOGGER_DATA);
169 // always use numeric link for consistency
171 $jp->link = 'http://facebook.com/profile.php?id=' . $person->id;
173 // check if we already have a contact
175 $r = q("SELECT * FROM `contact` WHERE `uid` = %d AND `url` = '%s' LIMIT 1",
182 // check that we have all the photos, this has been known to fail on occasion
184 if((! $r[0]['photo']) || (! $r[0]['thumb']) || (! $r[0]['micro'])) {
185 require_once("Photo.php");
187 $photos = import_profile_photo('https://graph.facebook.com/' . $jp->id . '/picture', $uid, $r[0]['id']);
189 $r = q("UPDATE `contact` SET `photo` = '%s',
195 WHERE `id` = %d LIMIT 1
200 dbesc(datetime_convert()),
201 dbesc(datetime_convert()),
202 dbesc(datetime_convert()),
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 ) ",
216 dbesc(datetime_convert()),
221 dbesc('facebook ' . $jp->id),
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),
232 $r = q("SELECT * FROM `contact` WHERE `url` = '%s' AND `uid` = %d LIMIT 1",
242 $contact_id = $r[0]['id'];
244 require_once("Photo.php");
246 $photos = import_profile_photo($r[0]['photo'],$uid,$contact_id);
248 $r = q("UPDATE `contact` SET `photo` = '%s',
254 WHERE `id` = %d LIMIT 1
259 dbesc(datetime_convert()),
260 dbesc(datetime_convert()),
261 dbesc(datetime_convert()),
270 // This is the POST method to the facebook settings page
271 // Content is posted to Facebook in the function facebook_post_hook()
273 function facebook_post(&$a) {
278 $value = ((x($_POST,'post_by_default')) ? intval($_POST['post_by_default']) : 0);
279 set_pconfig($uid,'facebook','post_by_default', $value);
281 $no_linking = get_pconfig($uid,'facebook','no_linking');
283 $no_wall = ((x($_POST,'facebook_no_wall')) ? intval($_POST['facebook_no_wall']) : 0);
284 set_pconfig($uid,'facebook','no_wall',$no_wall);
286 $private_wall = ((x($_POST,'facebook_private_wall')) ? intval($_POST['facebook_private_wall']) : 0);
287 set_pconfig($uid,'facebook','private_wall',$private_wall);
290 $linkvalue = ((x($_POST,'facebook_linking')) ? intval($_POST['facebook_linking']) : 0);
291 set_pconfig($uid,'facebook','no_linking', (($linkvalue) ? 0 : 1));
293 // FB linkage was allowed but has just been turned off - remove all FB contacts and posts
295 if((! intval($no_linking)) && (! intval($linkvalue))) {
296 $r = q("SELECT `id` FROM `contact` WHERE `uid` = %d AND `network` = '%s' ",
298 dbesc(NETWORK_FACEBOOK)
301 require_once('include/Contact.php');
303 contact_remove($rr['id']);
306 elseif(intval($no_linking) && intval($linkvalue)) {
307 // FB linkage is now allowed - import stuff.
309 fb_get_friends($uid);
310 fb_consume_all($uid);
313 info( t('Settings updated.') . EOL);
319 // Facebook settings form
321 function facebook_content(&$a) {
324 notice( t('Permission denied.') . EOL);
328 if($a->argc > 1 && $a->argv[1] === 'remove') {
329 del_pconfig(local_user(),'facebook','post');
330 info( t('Facebook disabled') . EOL);
333 if($a->argc > 1 && $a->argv[1] === 'friends') {
334 fb_get_friends(local_user());
335 info( t('Updating contacts') . EOL);
339 $fb_installed = get_pconfig(local_user(),'facebook','post');
341 $appid = get_config('facebook','appid');
344 notice( t('Facebook API key is missing.') . EOL);
348 $a->page['htmlhead'] .= '<link rel="stylesheet" type="text/css" href="'
349 . $a->get_baseurl() . '/addon/facebook/facebook.css' . '" media="all" />' . "\r\n";
351 $o .= '<h3>' . t('Facebook Connect') . '</h3>';
353 if(! $fb_installed) {
354 $o .= '<div id="facebook-enable-wrapper">';
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>';
362 $o .= '<div id="facebook-disable-wrapper">';
364 $o .= '<a href="' . $a->get_baseurl() . '/facebook/remove' . '">' . t('Remove Facebook connector') . '</a></div>';
366 $o .= '<div id="facebook-enable-wrapper">';
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>';
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;
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 ;
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>';
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 ;
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 ;
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>';
397 $o .= '<input type="submit" name="submit" value="' . t('Submit') . '" /></form></div>';
405 function facebook_cron($a,$b) {
407 $last = get_config('facebook','last_poll');
409 $poll_interval = intval(get_config('facebook','poll_interval'));
411 $poll_interval = 3600;
414 $next = $last + $poll_interval;
419 logger('facebook_cron');
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.
426 $r = q("SELECT * FROM `pconfig` WHERE `cat` = 'facebook' AND `k` = 'post' AND `v` = '1' ORDER BY RAND() ");
429 if(get_pconfig($rr['uid'],'facebook','no_linking'))
431 $ab = intval(get_config('system','account_abandon_days'));
433 $z = q("SELECT `uid` FROM `user` WHERE `uid` = %d AND `login_date` > UTC_TIMESTAMP() - INTERVAL %d DAY LIMIT 1",
441 // check for new friends once a day
442 $last_friend_check = get_pconfig($rr['uid'],'facebook','friend_check');
443 if($last_friend_check)
444 $next_friend_check = $last_friend_check + 86400;
445 if($next_friend_check <= time()) {
446 fb_get_friends($rr['uid']);
447 set_pconfig($rr['uid'],'facebook','friend_check',time());
449 fb_consume_all($rr['uid']);
453 set_config('facebook','last_poll', time());
459 function facebook_plugin_settings(&$a,&$b) {
461 $b .= '<div class="settings-block">';
462 $b .= '<h3>' . t('Facebook') . '</h3>';
463 $b .= '<a href="facebook">' . t('Facebook Connector Settings') . '</a><br />';
468 function facebook_jot_nets(&$a,&$b) {
472 $fb_post = get_pconfig(local_user(),'facebook','post');
473 if(intval($fb_post) == 1) {
474 $fb_defpost = get_pconfig(local_user(),'facebook','post_by_default');
475 $selected = ((intval($fb_defpost) == 1) ? ' checked="checked" ' : '');
476 $b .= '<div class="profile-jot-net"><input type="checkbox" name="facebook_enable"' . $selected . ' value="1" /> '
477 . t('Post to Facebook') . '</div>';
482 function facebook_post_hook(&$a,&$b) {
485 * Post to Facebook stream
488 require_once('include/group.php');
490 logger('Facebook post');
495 if((local_user()) && (local_user() == $b['uid'])) {
497 // Facebook is not considered a private network
498 if($b['prvnets'] && $b['private'])
501 $linking = ((get_pconfig(local_user(),'facebook','no_linking')) ? 0 : 1);
503 if(($b['parent']) && ($linking)) {
504 $r = q("SELECT * FROM `item` WHERE `id` = %d AND `uid` = %d LIMIT 1",
505 intval($b['parent']),
508 if(count($r) && substr($r[0]['uri'],0,4) === 'fb::')
509 $reply = substr($r[0]['uri'],4);
510 elseif(count($r) && substr($r[0]['extid'],0,4) === 'fb::')
511 $reply = substr($r[0]['extid'],4);
514 logger('facebook reply id=' . $reply);
517 if($b['private'] && $reply === false) {
518 $allow_people = expand_acl($b['allow_cid']);
519 $allow_groups = expand_groups(expand_acl($b['allow_gid']));
520 $deny_people = expand_acl($b['deny_cid']);
521 $deny_groups = expand_groups(expand_acl($b['deny_gid']));
523 $recipients = array_unique(array_merge($allow_people,$allow_groups));
524 $deny = array_unique(array_merge($deny_people,$deny_groups));
526 $allow_str = dbesc(implode(', ',$recipients));
528 $r = q("SELECT `notify` FROM `contact` WHERE `id` IN ( $allow_str ) AND `network` = 'face'");
529 $allow_arr = array();
532 $allow_arr[] = $rr['notify'];
535 $deny_str = dbesc(implode(', ',$deny));
537 $r = q("SELECT `notify` FROM `contact` WHERE `id` IN ( $deny_str ) AND `network` = 'face'");
541 $deny_arr[] = $rr['notify'];
544 if(count($deny_arr) && (! count($allow_arr))) {
546 // One or more FB folks were denied access but nobody on FB was specifically allowed access.
547 // This might cause the post to be open to public on Facebook, but only to selected members
548 // on another network. Since this could potentially leak a post to somebody who was denied,
549 // we will skip posting it to Facebook with a slightly vague but relevant message that will
550 // hopefully lead somebody to this code comment for a better explanation of what went wrong.
552 notice( t('Post to Facebook cancelled because of multi-network access permission conflict.') . EOL);
557 // if it's a private message but no Facebook members are allowed or denied, skip Facebook post
559 if((! count($allow_arr)) && (! count($deny_arr)))
563 if($b['verb'] == ACTIVITY_LIKE)
567 $appid = get_config('facebook', 'appid' );
568 $secret = get_config('facebook', 'appsecret' );
570 if($appid && $secret) {
572 logger('facebook: have appid+secret');
574 $fb_post = intval(get_pconfig(local_user(),'facebook','post'));
575 $fb_enable = (($fb_post && x($_POST,'facebook_enable')) ? intval($_POST['facebook_enable']) : 0);
576 $fb_token = get_pconfig(local_user(),'facebook','access_token');
578 // if API is used, default to the chosen settings
579 if($_POST['api_source'] && intval(get_pconfig(local_user(),'facebook','post_by_default')))
585 logger('facebook: $fb_post: ' . $fb_post . ' $fb_enable: ' . $fb_enable . ' $fb_token: ' . $fb_token,LOGGER_DEBUG);
587 // post to facebook if it's a public post and we've ticked the 'post to Facebook' box,
588 // or it's a private message with facebook participants
589 // or it's a reply or likes action to an existing facebook post
591 if($fb_post && $fb_token && ($fb_enable || $b['private'] || $reply)) {
592 logger('facebook: able to post');
593 require_once('library/facebook.php');
594 require_once('include/bbcode.php');
598 logger('Facebook post: original msg=' . $msg, LOGGER_DATA);
600 // make links readable before we strip the code
602 // unless it's a dislike - just send the text as a comment
604 if($b['verb'] == ACTIVITY_DISLIKE)
605 $msg = trim(strip_tags(bbcode($msg)));
607 $search_str = $a->get_baseurl() . '/search';
609 if(preg_match("/\[url=(.*?)\](.*?)\[\/url\]/is",$msg,$matches)) {
611 // don't use hashtags for message link
613 if(strpos($matches[2],$search_str) === false) {
615 if(substr($matches[2],0,5) != '[img]')
616 $linkname = $matches[2];
620 $msg = preg_replace("/\[url=(.*?)\](.*?)\[\/url\]/is",'$2 $1',$msg);
622 if(preg_match("/\[img\](.*?)\[\/img\]/is",$msg,$matches))
623 $image = $matches[1];
625 $msg = preg_replace("/\[img\](.*?)\[\/img\]/is", t('Image: ') . '$1', $msg);
627 if((strpos($link,z_root()) !== false) && (! $image))
628 $image = $a->get_baseurl() . '/images/friendika-64.jpg';
630 $msg = trim(strip_tags(bbcode($msg)));
631 $msg = html_entity_decode($msg,ENT_QUOTES,'UTF-8');
633 // add any attachments as text urls
635 $arr = explode(',',$b['attach']);
639 foreach($arr as $r) {
641 $cnt = preg_match('|\[attach\]href=\"(.*?)\" size=\"(.*?)\" type=\"(.*?)\" title=\"(.*?)\"\[\/attach\]|',$r,$matches);
648 if (strlen($msg) > FACEBOOK_MAXPOSTLEN) {
650 require_once('library/slinky.php');
652 $display_url = $a->get_baseurl() . '/display/' . $a->user['nickname'] . '/' . $b['id'];
653 $slinky = new Slinky( $display_url );
654 // setup a cascade of shortening services
655 // try to get a short link from these services
656 // in the order ur1.ca, trim, id.gd, tinyurl
657 $slinky->set_cascade( array( new Slinky_UR1ca(), new Slinky_Trim(), new Slinky_IsGd(), new Slinky_TinyURL() ) );
658 $shortlink = $slinky->short();
659 // the new message will be shortened such that "... $shortlink"
660 // will fit into the character limit
661 $msg = substr($msg, 0, FACEBOOK_MAXPOSTLEN - strlen($shortlink) - 4);
662 $msg .= '... ' . $shortlink;
667 logger('Facebook post: msg=' . $msg, LOGGER_DATA);
670 $postvars = array('access_token' => $fb_token);
674 'access_token' => $fb_token,
678 $postvars['picture'] = $image;
680 $postvars['link'] = $link;
682 $postvars['name'] = $linkname;
685 if(($b['private']) && (! $b['parent'])) {
686 $postvars['privacy'] = '{"value": "CUSTOM", "friends": "SOME_FRIENDS"';
687 if(count($allow_arr))
688 $postvars['privacy'] .= ',"allow": "' . implode(',',$allow_arr) . '"';
690 $postvars['privacy'] .= ',"deny": "' . implode(',',$deny_arr) . '"';
691 $postvars['privacy'] .= '}';
696 $url = 'https://graph.facebook.com/' . $reply . '/' . (($likes) ? 'likes' : 'comments');
699 $url = 'https://graph.facebook.com/me/feed';
701 $postvars['actions'] = '{"name": "' . t('View on Friendika') . '", "link": "' . $b['plink'] . '"}';
704 logger('facebook: post to ' . $url);
705 logger('facebook: postvars: ' . print_r($postvars,true));
707 // "test_mode" prevents anything from actually being posted.
708 // Otherwise, let's do it.
710 if(! get_config('facebook','test_mode')) {
711 $x = post_url($url, $postvars);
713 $retj = json_decode($x);
715 q("UPDATE `item` SET `extid` = '%s' WHERE `id` = %d LIMIT 1",
716 dbesc('fb::' . $retj->id),
722 $s = serialize(array('url' => $url, 'item' => $b['id'], 'post' => $postvars));
723 q("INSERT INTO `queue` ( `network`, `cid`, `created`, `last`, `content`)
724 VALUES ( '%s', %d, '%s', '%s', '%s') ",
725 dbesc(NETWORK_FACEBOOK),
727 dbesc(datetime_convert()),
728 dbesc(datetime_convert()),
732 notice( t('Facebook post failed. Queued for retry.') . EOL);
736 logger('Facebook post returns: ' . $x, LOGGER_DEBUG);
744 function fb_queue_hook(&$a,&$b) {
746 $qi = q("SELECT * FROM `queue` WHERE `network` = '%s'",
747 dbesc(NETWORK_FACEBOOK)
752 require_once('include/queue_fn.php');
755 if($x['network'] !== NETWORK_FACEBOOK)
758 logger('facebook_queue: run');
760 $r = q("SELECT `user`.* FROM `user` LEFT JOIN `contact` on `contact`.`uid` = `user`.`uid`
761 WHERE `contact`.`self` = 1 AND `contact`.`id` = %d LIMIT 1",
769 $appid = get_config('facebook', 'appid' );
770 $secret = get_config('facebook', 'appsecret' );
772 if($appid && $secret) {
773 $fb_post = intval(get_pconfig($user['uid'],'facebook','post'));
774 $fb_token = get_pconfig($user['uid'],'facebook','access_token');
776 if($fb_post && $fb_token) {
777 logger('facebook_queue: able to post');
778 require_once('library/facebook.php');
780 $z = unserialize($x['content']);
782 $j = post_url($z['url'],$z['post']);
784 $retj = json_decode($j);
786 q("UPDATE `item` SET `extid` = '%s' WHERE `id` = %d LIMIT 1",
787 dbesc('fb::' . $retj->id),
790 logger('facebook_queue: success: ' . $j);
791 remove_queue_item($x['id']);
794 logger('facebook_queue: failed: ' . $j);
795 update_queue_time($x['id']);
802 function fb_consume_all($uid) {
804 require_once('include/items.php');
806 $access_token = get_pconfig($uid,'facebook','access_token');
810 if(! get_pconfig($uid,'facebook','no_wall')) {
811 $private_wall = intval(get_pconfig($uid,'facebook','private_wall'));
812 $s = fetch_url('https://graph.facebook.com/me/feed?access_token=' . $access_token);
814 $j = json_decode($s);
815 logger('fb_consume_stream: wall: ' . print_r($j,true), LOGGER_DATA);
816 fb_consume_stream($uid,$j,($private_wall) ? false : true);
819 $s = fetch_url('https://graph.facebook.com/me/home?access_token=' . $access_token);
821 $j = json_decode($s);
822 logger('fb_consume_stream: feed: ' . print_r($j,true), LOGGER_DATA);
823 fb_consume_stream($uid,$j,false);
828 function fb_consume_stream($uid,$j,$wall = false) {
833 $user = q("SELECT `nickname`, `blockwall` FROM `user` WHERE `uid` = %d AND `account_expired` = 0 LIMIT 1",
839 $my_local_url = $a->get_baseurl() . '/profile/' . $user[0]['nickname'];
841 $no_linking = get_pconfig($uid,'facebook','no_linking');
845 $self = q("SELECT * FROM `contact` WHERE `self` = 1 AND `uid` = %d LIMIT 1",
850 $self_id = get_pconfig($uid,'facebook','self_id');
851 if(! count($j->data) || (! strlen($self_id)))
854 foreach($j->data as $entry) {
855 logger('fb_consume: entry: ' . print_r($entry,true), LOGGER_DATA);
858 $r = q("SELECT * FROM `item` WHERE ( `uri` = '%s' OR `extid` = '%s') AND `uid` = %d LIMIT 1",
859 dbesc('fb::' . $entry->id),
860 dbesc('fb::' . $entry->id),
866 $top_item = $r[0]['id'];
869 $post_exists = false;
874 $datarray['gravity'] = 0;
875 $datarray['uid'] = $uid;
876 $datarray['wall'] = (($wall) ? 1 : 0);
877 $datarray['uri'] = $datarray['parent-uri'] = 'fb::' . $entry->id;
878 $from = $entry->from;
879 if($from->id == $self_id)
880 $datarray['contact-id'] = $self[0]['id'];
882 $r = q("SELECT * FROM `contact` WHERE `notify` = '%s' AND `uid` = %d AND `blocked` = 0 AND `readonly` = 0 LIMIT 1",
887 $datarray['contact-id'] = $r[0]['id'];
890 // don't store post if we don't have a contact
892 if(! x($datarray,'contact-id')) {
893 logger('no contact: post ignored');
897 $datarray['verb'] = ACTIVITY_POST;
899 $datarray['owner-name'] = $self[0]['name'];
900 $datarray['owner-link'] = $self[0]['url'];
901 $datarray['owner-avatar'] = $self[0]['thumb'];
903 if(isset($entry->application) && isset($entry->application->name) && strlen($entry->application->name))
904 $datarray['app'] = strip_tags($entry->application->name);
906 $datarray['app'] = 'facebook';
907 $datarray['author-name'] = $from->name;
908 $datarray['author-link'] = 'http://facebook.com/profile.php?id=' . $from->id;
909 $datarray['author-avatar'] = 'https://graph.facebook.com/' . $from->id . '/picture';
910 $datarray['plink'] = $datarray['author-link'] . '&v=wall&story_fbid=' . substr($entry->id,strpos($entry->id,'_') + 1);
912 $datarray['body'] = $entry->message;
914 $datarray['body'] .= "\n\n" . '[img]' . $entry->picture . '[/img]';
916 $datarray['body'] .= "\n" . linkify($entry->link);
918 $datarray['body'] .= "\n" . $entry->name;
920 $datarray['body'] .= "\n" . $entry->caption;
921 if($entry->description)
922 $datarray['body'] .= "\n" . $entry->description;
923 $datarray['created'] = datetime_convert('UTC','UTC',$entry->created_time);
924 $datarray['edited'] = datetime_convert('UTC','UTC',$entry->updated_time);
926 // If the entry has a privacy policy, we cannot assume who can or cannot see it,
927 // as the identities are from a foreign system. Mark it as private to the owner.
929 if($entry->privacy && $entry->privacy->value !== 'EVERYONE') {
930 $datarray['private'] = 1;
931 $datarray['allow_cid'] = '<' . $uid . '>';
934 $top_item = item_store($datarray);
935 $r = q("SELECT * FROM `item` WHERE `id` = %d AND `uid` = %d LIMIT 1",
941 logger('fb: new top level item posted');
945 if(isset($entry->likes) && isset($entry->likes->data))
946 $likers = $entry->likes->data;
950 if(isset($entry->comments) && isset($entry->comments->data))
951 $comments = $entry->comments->data;
955 if(is_array($likers)) {
956 foreach($likers as $likes) {
961 // If we posted the like locally, it will be found with our url, not the FB url.
963 $second_url = (($likes->id == $self_id) ? $self[0]['url'] : 'http://facebook.com/profile.php?id=' . $likes->id);
965 $r = q("SELECT * FROM `item` WHERE `parent-uri` = '%s' AND `uid` = %d AND `verb` = '%s'
966 AND ( `author-link` = '%s' OR `author-link` = '%s' ) LIMIT 1",
967 dbesc($orig_post['uri']),
969 dbesc(ACTIVITY_LIKE),
970 dbesc('http://facebook.com/profile.php?id=' . $likes->id),
978 $likedata['parent'] = $top_item;
979 $likedata['verb'] = ACTIVITY_LIKE;
980 $likedata['gravity'] = 3;
981 $likedata['uid'] = $uid;
982 $likedata['wall'] = (($wall) ? 1 : 0);
983 $likedata['uri'] = item_new_uri($a->get_baseurl(), $uid);
984 $likedata['parent-uri'] = $orig_post['uri'];
985 if($likes->id == $self_id)
986 $likedata['contact-id'] = $self[0]['id'];
988 $r = q("SELECT * FROM `contact` WHERE `notify` = '%s' AND `uid` = %d AND `blocked` = 0 AND `readonly` = 0 LIMIT 1",
993 $likedata['contact-id'] = $r[0]['id'];
995 if(! x($likedata,'contact-id'))
996 $likedata['contact-id'] = $orig_post['contact-id'];
998 $likedata['app'] = 'facebook';
999 $likedata['verb'] = ACTIVITY_LIKE;
1000 $likedata['author-name'] = $likes->name;
1001 $likedata['author-link'] = 'http://facebook.com/profile.php?id=' . $likes->id;
1002 $likedata['author-avatar'] = 'https://graph.facebook.com/' . $likes->id . '/picture';
1004 $author = '[url=' . $likedata['author-link'] . ']' . $likedata['author-name'] . '[/url]';
1005 $objauthor = '[url=' . $orig_post['author-link'] . ']' . $orig_post['author-name'] . '[/url]';
1006 $post_type = t('status');
1007 $plink = '[url=' . $orig_post['plink'] . ']' . $post_type . '[/url]';
1008 $likedata['object-type'] = ACTIVITY_OBJ_NOTE;
1010 $likedata['body'] = sprintf( t('%1$s likes %2$s\'s %3$s'), $author, $objauthor, $plink);
1011 $likedata['object'] = '<object><type>' . ACTIVITY_OBJ_NOTE . '</type><local>1</local>' .
1012 '<id>' . $orig_post['uri'] . '</id><link>' . xmlify('<link rel="alternate" type="text/html" href="' . xmlify($orig_post['plink']) . '" />') . '</link><title>' . $orig_post['title'] . '</title><content>' . $orig_post['body'] . '</content></object>';
1014 $item = item_store($likedata);
1017 if(is_array($comments)) {
1018 foreach($comments as $cmnt) {
1023 $r = q("SELECT * FROM `item` WHERE `uid` = %d AND ( `uri` = '%s' OR `extid` = '%s' ) LIMIT 1",
1025 dbesc('fb::' . $cmnt->id),
1026 dbesc('fb::' . $cmnt->id)
1031 $cmntdata = array();
1032 $cmntdata['parent'] = $top_item;
1033 $cmntdata['verb'] = ACTIVITY_POST;
1034 $cmntdata['gravity'] = 6;
1035 $cmntdata['uid'] = $uid;
1036 $cmntdata['wall'] = (($wall) ? 1 : 0);
1037 $cmntdata['uri'] = 'fb::' . $cmnt->id;
1038 $cmntdata['parent-uri'] = $orig_post['uri'];
1039 if($cmnt->from->id == $self_id) {
1040 $cmntdata['contact-id'] = $self[0]['id'];
1043 $r = q("SELECT * FROM `contact` WHERE `notify` = '%s' AND `uid` = %d LIMIT 1",
1044 dbesc($cmnt->from->id),
1048 $cmntdata['contact-id'] = $r[0]['id'];
1049 if($r[0]['blocked'] || $r[0]['readonly'])
1053 if(! x($cmntdata,'contact-id'))
1054 $cmntdata['contact-id'] = $orig_post['contact-id'];
1056 $cmntdata['app'] = 'facebook';
1057 $cmntdata['created'] = datetime_convert('UTC','UTC',$cmnt->created_time);
1058 $cmntdata['edited'] = datetime_convert('UTC','UTC',$cmnt->created_time);
1059 $cmntdata['verb'] = ACTIVITY_POST;
1060 $cmntdata['author-name'] = $cmnt->from->name;
1061 $cmntdata['author-link'] = 'http://facebook.com/profile.php?id=' . $cmnt->from->id;
1062 $cmntdata['author-avatar'] = 'https://graph.facebook.com/' . $cmnt->from->id . '/picture';
1063 $cmntdata['body'] = $cmnt->message;
1064 $item = item_store($cmntdata);