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', 'addon/facebook/facebook.php', 'facebook_post_local');
52 register_hook('notifier_normal', 'addon/facebook/facebook.php', 'facebook_post_hook');
53 register_hook('jot_networks', 'addon/facebook/facebook.php', 'facebook_jot_nets');
54 register_hook('connector_settings', 'addon/facebook/facebook.php', 'facebook_plugin_settings');
55 register_hook('cron', 'addon/facebook/facebook.php', 'facebook_cron');
56 register_hook('queue_predeliver', 'addon/facebook/facebook.php', 'fb_queue_hook');
60 function facebook_uninstall() {
61 unregister_hook('post_local', 'addon/facebook/facebook.php', 'facebook_post_local');
62 unregister_hook('notifier_normal', 'addon/facebook/facebook.php', 'facebook_post_hook');
63 unregister_hook('jot_networks', 'addon/facebook/facebook.php', 'facebook_jot_nets');
64 unregister_hook('connector_settings', 'addon/facebook/facebook.php', 'facebook_plugin_settings');
65 unregister_hook('cron', 'addon/facebook/facebook.php', 'facebook_cron');
66 unregister_hook('queue_predeliver', 'addon/facebook/facebook.php', 'fb_queue_hook');
69 unregister_hook('post_local_end', 'addon/facebook/facebook.php', 'facebook_post_hook');
70 unregister_hook('plugin_settings', 'addon/facebook/facebook.php', 'facebook_plugin_settings');
74 /* declare the facebook_module function so that /facebook url requests will land here */
76 function facebook_module() {}
80 /* If a->argv[1] is a nickname, this is a callback from Facebook oauth requests. */
82 function facebook_init(&$a) {
88 $r = q("SELECT `uid` FROM `user` WHERE `nickname` = '%s' LIMIT 1",
95 $auth_code = (($_GET['code']) ? $_GET['code'] : '');
96 $error = (($_GET['error_description']) ? $_GET['error_description'] : '');
100 logger('facebook_init: Error: ' . $error);
102 if($auth_code && $uid) {
104 $appid = get_config('facebook','appid');
105 $appsecret = get_config('facebook', 'appsecret');
107 $x = fetch_url('https://graph.facebook.com/oauth/access_token?client_id='
108 . $appid . '&client_secret=' . $appsecret . '&redirect_uri='
109 . urlencode($a->get_baseurl() . '/facebook/' . $nick)
110 . '&code=' . $auth_code);
112 logger('facebook_init: returned access token: ' . $x, LOGGER_DATA);
114 if(strpos($x,'access_token=') !== false) {
115 $token = str_replace('access_token=', '', $x);
116 if(strpos($token,'&') !== false)
117 $token = substr($token,0,strpos($token,'&'));
118 set_pconfig($uid,'facebook','access_token',$token);
119 set_pconfig($uid,'facebook','post','1');
120 if(get_pconfig($uid,'facebook','no_linking') === false)
121 set_pconfig($uid,'facebook','no_linking',1);
123 fb_get_friends($uid);
124 fb_consume_all($uid);
133 function fb_get_self($uid) {
134 $access_token = get_pconfig($uid,'facebook','access_token');
137 $s = fetch_url('https://graph.facebook.com/me/?access_token=' . $access_token);
139 $j = json_decode($s);
140 set_pconfig($uid,'facebook','self_id',(string) $j->id);
146 function fb_get_friends($uid) {
148 $r = q("SELECT `uid` FROM `user` WHERE `uid` = %d AND `account_expired` = 0 LIMIT 1",
154 $access_token = get_pconfig($uid,'facebook','access_token');
156 $no_linking = get_pconfig($uid,'facebook','no_linking');
162 $s = fetch_url('https://graph.facebook.com/me/friends?access_token=' . $access_token);
164 logger('facebook: fb_get_friends: ' . $s, LOGGER_DATA);
165 $j = json_decode($s);
166 logger('facebook: fb_get_friends: json: ' . print_r($j,true), LOGGER_DATA);
169 foreach($j->data as $person) {
170 $s = fetch_url('https://graph.facebook.com/' . $person->id . '?access_token=' . $access_token);
172 $jp = json_decode($s);
173 logger('fb_get_friends: info: ' . print_r($jp,true), LOGGER_DATA);
175 // always use numeric link for consistency
177 $jp->link = 'http://facebook.com/profile.php?id=' . $person->id;
179 // check if we already have a contact
181 $r = q("SELECT * FROM `contact` WHERE `uid` = %d AND `url` = '%s' LIMIT 1",
188 // check that we have all the photos, this has been known to fail on occasion
190 if((! $r[0]['photo']) || (! $r[0]['thumb']) || (! $r[0]['micro'])) {
191 require_once("Photo.php");
193 $photos = import_profile_photo('https://graph.facebook.com/' . $jp->id . '/picture', $uid, $r[0]['id']);
195 $r = q("UPDATE `contact` SET `photo` = '%s',
201 WHERE `id` = %d LIMIT 1
206 dbesc(datetime_convert()),
207 dbesc(datetime_convert()),
208 dbesc(datetime_convert()),
216 // create contact record
217 $r = q("INSERT INTO `contact` ( `uid`, `created`, `url`, `nurl`, `addr`, `alias`, `notify`, `poll`,
218 `name`, `nick`, `photo`, `network`, `rel`, `priority`,
219 `writable`, `blocked`, `readonly`, `pending` )
220 VALUES ( %d, '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', %d, %d, %d, 0, 0, 0 ) ",
222 dbesc(datetime_convert()),
224 dbesc(normalise_link($jp->link)),
228 dbesc('facebook ' . $jp->id),
230 dbesc(($jp->nickname) ? $jp->nickname : strtolower($jp->first_name)),
231 dbesc('https://graph.facebook.com/' . $jp->id . '/picture'),
232 dbesc(NETWORK_FACEBOOK),
233 intval(CONTACT_IS_FRIEND),
239 $r = q("SELECT * FROM `contact` WHERE `url` = '%s' AND `uid` = %d LIMIT 1",
249 $contact_id = $r[0]['id'];
251 require_once("Photo.php");
253 $photos = import_profile_photo($r[0]['photo'],$uid,$contact_id);
255 $r = q("UPDATE `contact` SET `photo` = '%s',
261 WHERE `id` = %d LIMIT 1
266 dbesc(datetime_convert()),
267 dbesc(datetime_convert()),
268 dbesc(datetime_convert()),
277 // This is the POST method to the facebook settings page
278 // Content is posted to Facebook in the function facebook_post_hook()
280 function facebook_post(&$a) {
285 $value = ((x($_POST,'post_by_default')) ? intval($_POST['post_by_default']) : 0);
286 set_pconfig($uid,'facebook','post_by_default', $value);
288 $no_linking = get_pconfig($uid,'facebook','no_linking');
290 $no_wall = ((x($_POST,'facebook_no_wall')) ? intval($_POST['facebook_no_wall']) : 0);
291 set_pconfig($uid,'facebook','no_wall',$no_wall);
293 $private_wall = ((x($_POST,'facebook_private_wall')) ? intval($_POST['facebook_private_wall']) : 0);
294 set_pconfig($uid,'facebook','private_wall',$private_wall);
297 set_pconfig($uid,'facebook','blocked_apps',escape_tags(trim($_POST['blocked_apps'])));
299 $linkvalue = ((x($_POST,'facebook_linking')) ? intval($_POST['facebook_linking']) : 0);
300 set_pconfig($uid,'facebook','no_linking', (($linkvalue) ? 0 : 1));
302 // FB linkage was allowed but has just been turned off - remove all FB contacts and posts
304 if((! intval($no_linking)) && (! intval($linkvalue))) {
305 $r = q("SELECT `id` FROM `contact` WHERE `uid` = %d AND `network` = '%s' ",
307 dbesc(NETWORK_FACEBOOK)
310 require_once('include/Contact.php');
312 contact_remove($rr['id']);
315 elseif(intval($no_linking) && intval($linkvalue)) {
316 // FB linkage is now allowed - import stuff.
318 fb_get_friends($uid);
319 fb_consume_all($uid);
322 info( t('Settings updated.') . EOL);
328 // Facebook settings form
330 function facebook_content(&$a) {
333 notice( t('Permission denied.') . EOL);
337 if($a->argc > 1 && $a->argv[1] === 'remove') {
338 del_pconfig(local_user(),'facebook','post');
339 info( t('Facebook disabled') . EOL);
342 if($a->argc > 1 && $a->argv[1] === 'friends') {
343 fb_get_friends(local_user());
344 info( t('Updating contacts') . EOL);
348 $fb_installed = get_pconfig(local_user(),'facebook','post');
350 $appid = get_config('facebook','appid');
353 notice( t('Facebook API key is missing.') . EOL);
357 $a->page['htmlhead'] .= '<link rel="stylesheet" type="text/css" href="'
358 . $a->get_baseurl() . '/addon/facebook/facebook.css' . '" media="all" />' . "\r\n";
360 $o .= '<h3>' . t('Facebook Connect') . '</h3>';
362 if(! $fb_installed) {
363 $o .= '<div id="facebook-enable-wrapper">';
365 $o .= '<a href="https://www.facebook.com/dialog/oauth?client_id=' . $appid . '&redirect_uri='
366 . $a->get_baseurl() . '/facebook/' . $a->user['nickname'] . '&scope=publish_stream,read_stream,offline_access">' . t('Install Facebook connector for this account.') . '</a>';
371 $o .= '<div id="facebook-disable-wrapper">';
373 $o .= '<a href="' . $a->get_baseurl() . '/facebook/remove' . '">' . t('Remove Facebook connector') . '</a></div>';
375 $o .= '<div id="facebook-enable-wrapper">';
377 $o .= '<a href="https://www.facebook.com/dialog/oauth?client_id=' . $appid . '&redirect_uri='
378 . $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>';
381 $o .= '<div id="facebook-post-default-form">';
382 $o .= '<form action="facebook" method="post" >';
383 $post_by_default = get_pconfig(local_user(),'facebook','post_by_default');
384 $checked = (($post_by_default) ? ' checked="checked" ' : '');
385 $o .= '<input type="checkbox" name="post_by_default" value="1"' . $checked . '/>' . ' ' . t('Post to Facebook by default') . EOL;
387 $no_linking = get_pconfig(local_user(),'facebook','no_linking');
388 $checked = (($no_linking) ? '' : ' checked="checked" ');
389 $o .= '<input type="checkbox" name="facebook_linking" value="1"' . $checked . '/>' . ' ' . t('Link all your Facebook friends and conversations on this website') . EOL ;
391 $o .= '<p>' . t('Facebook conversations consist of your <em>profile wall</em> and your friend <em>stream</em>.');
392 $o .= ' ' . t('On this website, your Facebook friend stream is only visible to you.');
393 $o .= ' ' . t('The following settings determine the privacy of your Facebook profile wall on this website.') . '</p>';
395 $private_wall = get_pconfig(local_user(),'facebook','private_wall');
396 $checked = (($private_wall) ? ' checked="checked" ' : '');
397 $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 ;
400 $no_wall = get_pconfig(local_user(),'facebook','no_wall');
401 $checked = (($no_wall) ? ' checked="checked" ' : '');
402 $o .= '<input type="checkbox" name="facebook_no_wall" value="1"' . $checked . '/>' . ' ' . t('Do not import your Facebook profile wall conversations') . EOL ;
404 $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>';
407 $blocked_apps = get_pconfig(local_user(),'facebook','blocked_apps');
409 $o .= '<div><label id="blocked-apps-label" for="blocked-apps">' . t('Comma separated applications to ignore') . ' </label></div>';
410 $o .= '<div><textarea id="blocked-apps" name="blocked_apps" >' . htmlspecialchars($blocked_apps) . '</textarea></div>';
412 $o .= '<input type="submit" name="submit" value="' . t('Submit') . '" /></form></div>';
420 function facebook_cron($a,$b) {
422 $last = get_config('facebook','last_poll');
424 $poll_interval = intval(get_config('facebook','poll_interval'));
426 $poll_interval = 3600;
429 $next = $last + $poll_interval;
434 logger('facebook_cron');
437 // Find the FB users on this site and randomize in case one of them
438 // uses an obscene amount of memory. It may kill this queue run
439 // but hopefully we'll get a few others through on each run.
441 $r = q("SELECT * FROM `pconfig` WHERE `cat` = 'facebook' AND `k` = 'post' AND `v` = '1' ORDER BY RAND() ");
444 if(get_pconfig($rr['uid'],'facebook','no_linking'))
446 $ab = intval(get_config('system','account_abandon_days'));
448 $z = q("SELECT `uid` FROM `user` WHERE `uid` = %d AND `login_date` > UTC_TIMESTAMP() - INTERVAL %d DAY LIMIT 1",
456 // check for new friends once a day
457 $last_friend_check = get_pconfig($rr['uid'],'facebook','friend_check');
458 if($last_friend_check)
459 $next_friend_check = $last_friend_check + 86400;
460 if($next_friend_check <= time()) {
461 fb_get_friends($rr['uid']);
462 set_pconfig($rr['uid'],'facebook','friend_check',time());
464 fb_consume_all($rr['uid']);
468 set_config('facebook','last_poll', time());
474 function facebook_plugin_settings(&$a,&$b) {
476 $b .= '<div class="settings-block">';
477 $b .= '<h3>' . t('Facebook') . '</h3>';
478 $b .= '<a href="facebook">' . t('Facebook Connector Settings') . '</a><br />';
483 function facebook_jot_nets(&$a,&$b) {
487 $fb_post = get_pconfig(local_user(),'facebook','post');
488 if(intval($fb_post) == 1) {
489 $fb_defpost = get_pconfig(local_user(),'facebook','post_by_default');
490 $selected = ((intval($fb_defpost) == 1) ? ' checked="checked" ' : '');
491 $b .= '<div class="profile-jot-net"><input type="checkbox" name="facebook_enable"' . $selected . ' value="1" /> '
492 . t('Post to Facebook') . '</div>';
497 function facebook_post_hook(&$a,&$b) {
500 if($b['deleted'] || ($b['created'] !== $b['edited']))
504 * Post to Facebook stream
507 require_once('include/group.php');
509 logger('Facebook post');
514 $toplevel = (($b['id'] == $b['parent']) ? true : false);
516 if(strstr($b['postopts'],'facebook')) {
518 $linking = ((get_pconfig($b['uid'],'facebook','no_linking')) ? 0 : 1);
520 if((! $toplevel) && ($linking)) {
521 $r = q("SELECT * FROM `item` WHERE `id` = %d AND `uid` = %d LIMIT 1",
522 intval($b['parent']),
525 if(count($r) && substr($r[0]['uri'],0,4) === 'fb::')
526 $reply = substr($r[0]['uri'],4);
527 elseif(count($r) && substr($r[0]['extid'],0,4) === 'fb::')
528 $reply = substr($r[0]['extid'],4);
531 logger('facebook reply id=' . $reply);
534 if($b['private'] && $reply === false) {
535 $allow_people = expand_acl($b['allow_cid']);
536 $allow_groups = expand_groups(expand_acl($b['allow_gid']));
537 $deny_people = expand_acl($b['deny_cid']);
538 $deny_groups = expand_groups(expand_acl($b['deny_gid']));
540 $recipients = array_unique(array_merge($allow_people,$allow_groups));
541 $deny = array_unique(array_merge($deny_people,$deny_groups));
543 $allow_str = dbesc(implode(', ',$recipients));
545 $r = q("SELECT `notify` FROM `contact` WHERE `id` IN ( $allow_str ) AND `network` = 'face'");
546 $allow_arr = array();
549 $allow_arr[] = $rr['notify'];
552 $deny_str = dbesc(implode(', ',$deny));
554 $r = q("SELECT `notify` FROM `contact` WHERE `id` IN ( $deny_str ) AND `network` = 'face'");
558 $deny_arr[] = $rr['notify'];
561 if(count($deny_arr) && (! count($allow_arr))) {
563 // One or more FB folks were denied access but nobody on FB was specifically allowed access.
564 // This might cause the post to be open to public on Facebook, but only to selected members
565 // on another network. Since this could potentially leak a post to somebody who was denied,
566 // we will skip posting it to Facebook with a slightly vague but relevant message that will
567 // hopefully lead somebody to this code comment for a better explanation of what went wrong.
569 notice( t('Post to Facebook cancelled because of multi-network access permission conflict.') . EOL);
574 // if it's a private message but no Facebook members are allowed or denied, skip Facebook post
576 if((! count($allow_arr)) && (! count($deny_arr)))
580 if($b['verb'] == ACTIVITY_LIKE)
584 $appid = get_config('facebook', 'appid' );
585 $secret = get_config('facebook', 'appsecret' );
587 if($appid && $secret) {
589 logger('facebook: have appid+secret');
591 $fb_token = get_pconfig($b['uid'],'facebook','access_token');
594 // post to facebook if it's a public post and we've ticked the 'post to Facebook' box,
595 // or it's a private message with facebook participants
596 // or it's a reply or likes action to an existing facebook post
598 if($fb_token && ($toplevel || $b['private'] || $reply)) {
599 logger('facebook: able to post');
600 require_once('library/facebook.php');
601 require_once('include/bbcode.php');
605 logger('Facebook post: original msg=' . $msg, LOGGER_DATA);
607 // make links readable before we strip the code
609 // unless it's a dislike - just send the text as a comment
611 if($b['verb'] == ACTIVITY_DISLIKE)
612 $msg = trim(strip_tags(bbcode($msg)));
614 $search_str = $a->get_baseurl() . '/search';
616 if(preg_match("/\[url=(.*?)\](.*?)\[\/url\]/is",$msg,$matches)) {
618 // don't use hashtags for message link
620 if(strpos($matches[2],$search_str) === false) {
622 if(substr($matches[2],0,5) != '[img]')
623 $linkname = $matches[2];
627 $msg = preg_replace("/\[url=(.*?)\](.*?)\[\/url\]/is",'$2 $1',$msg);
629 if(preg_match("/\[img\](.*?)\[\/img\]/is",$msg,$matches))
630 $image = $matches[1];
632 $msg = preg_replace("/\[img\](.*?)\[\/img\]/is", t('Image: ') . '$1', $msg);
634 if((strpos($link,z_root()) !== false) && (! $image))
635 $image = $a->get_baseurl() . '/images/friendika-64.jpg';
637 $msg = trim(strip_tags(bbcode($msg)));
638 $msg = html_entity_decode($msg,ENT_QUOTES,'UTF-8');
640 // add any attachments as text urls
642 $arr = explode(',',$b['attach']);
646 foreach($arr as $r) {
648 $cnt = preg_match('|\[attach\]href=\"(.*?)\" size=\"(.*?)\" type=\"(.*?)\" title=\"(.*?)\"\[\/attach\]|',$r,$matches);
655 if (strlen($msg) > FACEBOOK_MAXPOSTLEN) {
657 require_once('library/slinky.php');
659 $display_url = $a->get_baseurl() . '/display/' . $a->user['nickname'] . '/' . $b['id'];
660 $slinky = new Slinky( $display_url );
661 // setup a cascade of shortening services
662 // try to get a short link from these services
663 // in the order ur1.ca, trim, id.gd, tinyurl
664 $slinky->set_cascade( array( new Slinky_UR1ca(), new Slinky_Trim(), new Slinky_IsGd(), new Slinky_TinyURL() ) );
665 $shortlink = $slinky->short();
666 // the new message will be shortened such that "... $shortlink"
667 // will fit into the character limit
668 $msg = substr($msg, 0, FACEBOOK_MAXPOSTLEN - strlen($shortlink) - 4);
669 $msg .= '... ' . $shortlink;
674 logger('Facebook post: msg=' . $msg, LOGGER_DATA);
677 $postvars = array('access_token' => $fb_token);
681 'access_token' => $fb_token,
685 $postvars['picture'] = $image;
687 $postvars['link'] = $link;
689 $postvars['name'] = $linkname;
692 if(($b['private']) && (! $b['parent'])) {
693 $postvars['privacy'] = '{"value": "CUSTOM", "friends": "SOME_FRIENDS"';
694 if(count($allow_arr))
695 $postvars['privacy'] .= ',"allow": "' . implode(',',$allow_arr) . '"';
697 $postvars['privacy'] .= ',"deny": "' . implode(',',$deny_arr) . '"';
698 $postvars['privacy'] .= '}';
703 $url = 'https://graph.facebook.com/' . $reply . '/' . (($likes) ? 'likes' : 'comments');
706 $url = 'https://graph.facebook.com/me/feed';
708 $postvars['actions'] = '{"name": "' . t('View on Friendika') . '", "link": "' . $b['plink'] . '"}';
711 logger('facebook: post to ' . $url);
712 logger('facebook: postvars: ' . print_r($postvars,true));
714 // "test_mode" prevents anything from actually being posted.
715 // Otherwise, let's do it.
717 if(! get_config('facebook','test_mode')) {
718 $x = post_url($url, $postvars);
720 $retj = json_decode($x);
722 q("UPDATE `item` SET `extid` = '%s' WHERE `id` = %d LIMIT 1",
723 dbesc('fb::' . $retj->id),
729 $s = serialize(array('url' => $url, 'item' => $b['id'], 'post' => $postvars));
730 require_once('include/queue_fn.php');
731 add_to_queue($a->contact,NETWORK_FACEBOOK,$s);
732 notice( t('Facebook post failed. Queued for retry.') . EOL);
736 logger('Facebook post returns: ' . $x, LOGGER_DEBUG);
744 function facebook_post_local(&$a,&$b) {
746 // Figure out if Facebook posting is enabled for this post and file it in 'postopts'
747 // where we will discover it during background delivery.
749 // This can only be triggered by a local user posting to their own wall.
751 if((local_user()) && (local_user() == $b['uid'])) {
753 $fb_post = intval(get_pconfig(local_user(),'facebook','post'));
754 $fb_enable = (($fb_post && x($_REQUEST,'facebook_enable')) ? intval($_REQUEST['facebook_enable']) : 0);
756 // if API is used, default to the chosen settings
757 if($_REQUEST['api_source'] && intval(get_pconfig(local_user(),'facebook','post_by_default')))
763 if(strlen($b['postopts']))
764 $b['postopts'] .= ',';
765 $b['postopts'] .= 'facebook';
770 function fb_queue_hook(&$a,&$b) {
772 $qi = q("SELECT * FROM `queue` WHERE `network` = '%s'",
773 dbesc(NETWORK_FACEBOOK)
778 require_once('include/queue_fn.php');
781 if($x['network'] !== NETWORK_FACEBOOK)
784 logger('facebook_queue: run');
786 $r = q("SELECT `user`.* FROM `user` LEFT JOIN `contact` on `contact`.`uid` = `user`.`uid`
787 WHERE `contact`.`self` = 1 AND `contact`.`id` = %d LIMIT 1",
795 $appid = get_config('facebook', 'appid' );
796 $secret = get_config('facebook', 'appsecret' );
798 if($appid && $secret) {
799 $fb_post = intval(get_pconfig($user['uid'],'facebook','post'));
800 $fb_token = get_pconfig($user['uid'],'facebook','access_token');
802 if($fb_post && $fb_token) {
803 logger('facebook_queue: able to post');
804 require_once('library/facebook.php');
806 $z = unserialize($x['content']);
808 $j = post_url($z['url'],$z['post']);
810 $retj = json_decode($j);
812 q("UPDATE `item` SET `extid` = '%s' WHERE `id` = %d LIMIT 1",
813 dbesc('fb::' . $retj->id),
816 logger('facebook_queue: success: ' . $j);
817 remove_queue_item($x['id']);
820 logger('facebook_queue: failed: ' . $j);
821 update_queue_time($x['id']);
828 function fb_consume_all($uid) {
830 require_once('include/items.php');
832 $access_token = get_pconfig($uid,'facebook','access_token');
836 if(! get_pconfig($uid,'facebook','no_wall')) {
837 $private_wall = intval(get_pconfig($uid,'facebook','private_wall'));
838 $s = fetch_url('https://graph.facebook.com/me/feed?access_token=' . $access_token);
840 $j = json_decode($s);
841 logger('fb_consume_stream: wall: ' . print_r($j,true), LOGGER_DATA);
842 fb_consume_stream($uid,$j,($private_wall) ? false : true);
845 $s = fetch_url('https://graph.facebook.com/me/home?access_token=' . $access_token);
847 $j = json_decode($s);
848 logger('fb_consume_stream: feed: ' . print_r($j,true), LOGGER_DATA);
849 fb_consume_stream($uid,$j,false);
854 function fb_consume_stream($uid,$j,$wall = false) {
859 $user = q("SELECT `nickname`, `blockwall` FROM `user` WHERE `uid` = %d AND `account_expired` = 0 LIMIT 1",
865 $my_local_url = $a->get_baseurl() . '/profile/' . $user[0]['nickname'];
867 $no_linking = get_pconfig($uid,'facebook','no_linking');
871 $self = q("SELECT * FROM `contact` WHERE `self` = 1 AND `uid` = %d LIMIT 1",
875 $blocked_apps = get_pconfig($uid,'facebook','blocked_apps');
876 $blocked_apps_arr = explode(',',$blocked_apps);
878 $self_id = get_pconfig($uid,'facebook','self_id');
879 if(! count($j->data) || (! strlen($self_id)))
882 foreach($j->data as $entry) {
883 logger('fb_consume: entry: ' . print_r($entry,true), LOGGER_DATA);
886 $r = q("SELECT * FROM `item` WHERE ( `uri` = '%s' OR `extid` = '%s') AND `uid` = %d LIMIT 1",
887 dbesc('fb::' . $entry->id),
888 dbesc('fb::' . $entry->id),
894 $top_item = $r[0]['id'];
897 $post_exists = false;
902 $datarray['gravity'] = 0;
903 $datarray['uid'] = $uid;
904 $datarray['wall'] = (($wall) ? 1 : 0);
905 $datarray['uri'] = $datarray['parent-uri'] = 'fb::' . $entry->id;
906 $from = $entry->from;
907 if($from->id == $self_id)
908 $datarray['contact-id'] = $self[0]['id'];
910 $r = q("SELECT * FROM `contact` WHERE `notify` = '%s' AND `uid` = %d AND `blocked` = 0 AND `readonly` = 0 LIMIT 1",
915 $datarray['contact-id'] = $r[0]['id'];
918 // don't store post if we don't have a contact
920 if(! x($datarray,'contact-id')) {
921 logger('no contact: post ignored');
925 $datarray['verb'] = ACTIVITY_POST;
927 $datarray['owner-name'] = $self[0]['name'];
928 $datarray['owner-link'] = $self[0]['url'];
929 $datarray['owner-avatar'] = $self[0]['thumb'];
931 if(isset($entry->application) && isset($entry->application->name) && strlen($entry->application->name))
932 $datarray['app'] = strip_tags($entry->application->name);
934 $datarray['app'] = 'facebook';
936 $found_blocked = false;
938 if(count($blocked_apps_arr)) {
939 foreach($blocked_apps_arr as $bad_appl) {
940 if(strlen(trim($bad_appl)) && (stristr($datarray['app'],trim($bad_appl)))) {
941 $found_blocked = true;
947 logger('facebook: blocking application: ' . $datarray['app']);
951 $datarray['author-name'] = $from->name;
952 $datarray['author-link'] = 'http://facebook.com/profile.php?id=' . $from->id;
953 $datarray['author-avatar'] = 'https://graph.facebook.com/' . $from->id . '/picture';
954 $datarray['plink'] = $datarray['author-link'] . '&v=wall&story_fbid=' . substr($entry->id,strpos($entry->id,'_') + 1);
956 $datarray['body'] = escape_tags($entry->message);
958 if($entry->picture && $entry->link) {
959 $datarray['body'] .= "\n\n" . '[url=' . $entry->link . '][img]' . $entry->picture . '[/img][/url]';
963 $datarray['body'] .= "\n\n" . '[img]' . $entry->picture . '[/img]';
965 $datarray['body'] .= "\n" . '[url=' . $entry->link . ']' . t('link') . '[/url]';
968 $datarray['body'] .= "\n" . $entry->name;
970 $datarray['body'] .= "\n" . $entry->caption;
971 if($entry->description)
972 $datarray['body'] .= "\n" . $entry->description;
973 $datarray['created'] = datetime_convert('UTC','UTC',$entry->created_time);
974 $datarray['edited'] = datetime_convert('UTC','UTC',$entry->updated_time);
976 // If the entry has a privacy policy, we cannot assume who can or cannot see it,
977 // as the identities are from a foreign system. Mark it as private to the owner.
979 if($entry->privacy && $entry->privacy->value !== 'EVERYONE') {
980 $datarray['private'] = 1;
981 $datarray['allow_cid'] = '<' . $uid . '>';
984 $top_item = item_store($datarray);
985 $r = q("SELECT * FROM `item` WHERE `id` = %d AND `uid` = %d LIMIT 1",
991 logger('fb: new top level item posted');
995 if(isset($entry->likes) && isset($entry->likes->data))
996 $likers = $entry->likes->data;
1000 if(isset($entry->comments) && isset($entry->comments->data))
1001 $comments = $entry->comments->data;
1005 if(is_array($likers)) {
1006 foreach($likers as $likes) {
1011 // If we posted the like locally, it will be found with our url, not the FB url.
1013 $second_url = (($likes->id == $self_id) ? $self[0]['url'] : 'http://facebook.com/profile.php?id=' . $likes->id);
1015 $r = q("SELECT * FROM `item` WHERE `parent-uri` = '%s' AND `uid` = %d AND `verb` = '%s'
1016 AND ( `author-link` = '%s' OR `author-link` = '%s' ) LIMIT 1",
1017 dbesc($orig_post['uri']),
1019 dbesc(ACTIVITY_LIKE),
1020 dbesc('http://facebook.com/profile.php?id=' . $likes->id),
1027 $likedata = array();
1028 $likedata['parent'] = $top_item;
1029 $likedata['verb'] = ACTIVITY_LIKE;
1030 $likedata['gravity'] = 3;
1031 $likedata['uid'] = $uid;
1032 $likedata['wall'] = (($wall) ? 1 : 0);
1033 $likedata['uri'] = item_new_uri($a->get_baseurl(), $uid);
1034 $likedata['parent-uri'] = $orig_post['uri'];
1035 if($likes->id == $self_id)
1036 $likedata['contact-id'] = $self[0]['id'];
1038 $r = q("SELECT * FROM `contact` WHERE `notify` = '%s' AND `uid` = %d AND `blocked` = 0 AND `readonly` = 0 LIMIT 1",
1043 $likedata['contact-id'] = $r[0]['id'];
1045 if(! x($likedata,'contact-id'))
1046 $likedata['contact-id'] = $orig_post['contact-id'];
1048 $likedata['app'] = 'facebook';
1049 $likedata['verb'] = ACTIVITY_LIKE;
1050 $likedata['author-name'] = $likes->name;
1051 $likedata['author-link'] = 'http://facebook.com/profile.php?id=' . $likes->id;
1052 $likedata['author-avatar'] = 'https://graph.facebook.com/' . $likes->id . '/picture';
1054 $author = '[url=' . $likedata['author-link'] . ']' . $likedata['author-name'] . '[/url]';
1055 $objauthor = '[url=' . $orig_post['author-link'] . ']' . $orig_post['author-name'] . '[/url]';
1056 $post_type = t('status');
1057 $plink = '[url=' . $orig_post['plink'] . ']' . $post_type . '[/url]';
1058 $likedata['object-type'] = ACTIVITY_OBJ_NOTE;
1060 $likedata['body'] = sprintf( t('%1$s likes %2$s\'s %3$s'), $author, $objauthor, $plink);
1061 $likedata['object'] = '<object><type>' . ACTIVITY_OBJ_NOTE . '</type><local>1</local>' .
1062 '<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>';
1064 $item = item_store($likedata);
1067 if(is_array($comments)) {
1068 foreach($comments as $cmnt) {
1073 $r = q("SELECT * FROM `item` WHERE `uid` = %d AND ( `uri` = '%s' OR `extid` = '%s' ) LIMIT 1",
1075 dbesc('fb::' . $cmnt->id),
1076 dbesc('fb::' . $cmnt->id)
1081 $cmntdata = array();
1082 $cmntdata['parent'] = $top_item;
1083 $cmntdata['verb'] = ACTIVITY_POST;
1084 $cmntdata['gravity'] = 6;
1085 $cmntdata['uid'] = $uid;
1086 $cmntdata['wall'] = (($wall) ? 1 : 0);
1087 $cmntdata['uri'] = 'fb::' . $cmnt->id;
1088 $cmntdata['parent-uri'] = $orig_post['uri'];
1089 if($cmnt->from->id == $self_id) {
1090 $cmntdata['contact-id'] = $self[0]['id'];
1093 $r = q("SELECT * FROM `contact` WHERE `notify` = '%s' AND `uid` = %d LIMIT 1",
1094 dbesc($cmnt->from->id),
1098 $cmntdata['contact-id'] = $r[0]['id'];
1099 if($r[0]['blocked'] || $r[0]['readonly'])
1103 if(! x($cmntdata,'contact-id'))
1104 $cmntdata['contact-id'] = $orig_post['contact-id'];
1106 $cmntdata['app'] = 'facebook';
1107 $cmntdata['created'] = datetime_convert('UTC','UTC',$cmnt->created_time);
1108 $cmntdata['edited'] = datetime_convert('UTC','UTC',$cmnt->created_time);
1109 $cmntdata['verb'] = ACTIVITY_POST;
1110 $cmntdata['author-name'] = $cmnt->from->name;
1111 $cmntdata['author-link'] = 'http://facebook.com/profile.php?id=' . $cmnt->from->id;
1112 $cmntdata['author-avatar'] = 'https://graph.facebook.com/' . $cmnt->from->id . '/picture';
1113 $cmntdata['body'] = $cmnt->message;
1114 $item = item_store($cmntdata);