From c51ae6551d9d3fd0afe1a8ea5c48663de05feca9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobias=20H=C3=B6=C3=9Fl?= Date: Sun, 26 Feb 2012 14:45:26 +0000 Subject: [PATCH 1/5] Support for FB Real-Time Updates according to http://developers.facebook.com/docs/reference/api/realtime/ . Plugin needs to be deactivated and activated again in order to enable the settings --- facebook/facebook.php | 283 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 282 insertions(+), 1 deletion(-) diff --git a/facebook/facebook.php b/facebook/facebook.php index 6b6c7f84..cc8b85cd 100755 --- a/facebook/facebook.php +++ b/facebook/facebook.php @@ -76,10 +76,90 @@ function facebook_module() {} -/* If a->argv[1] is a nickname, this is a callback from Facebook oauth requests. */ +// If a->argv[1] is a nickname, this is a callback from Facebook oauth requests. +// If $_REQUEST["realtime_cb"] is set, this is a callback from the Real-Time Updates API function facebook_init(&$a) { + + if (x($_REQUEST, "realtime_cb") && x($_REQUEST, "realtime_cb")) { + logger("facebook_init: Facebook Real-Time callback called", LOGGER_DEBUG); + + if (x($_REQUEST["hub_verify_token"])) { + // this is the verification callback while registering for real time updates + + $verify_token = get_config('facebook', 'cb_verify_token'); + if ($verify_token != $_REQUEST["hub_verify_token"]) { + logger('facebook_init: Wrong Facebook Callback Verifier - expected ' . $verify_token . ', got ' . $_REQUEST["hub_verify_token"]); + return; + } + + if (x($_REQUEST, "hub_challenge")) { + logger('facebook_init: Answering Challenge: ' . $_REQUEST["hub_challenge"], LOGGER_DATA); + echo $_REQUEST["hub_challenge"]; + die(); + } + } + + require_once('include/items.php'); + + // this is a status update + $content = file_get_contents("php://input"); + if (is_numeric($content)) $content = file_get_contents("php://input"); + $js = json_decode($content); + logger(print_r($js, true), LOGGER_DATA); + + if (!isset($js->object) || $js->object != "user" || !isset($js->entry)) { + logger('facebook_init: Could not parse Real-Time Update data', LOGGER_DEBUG); + return; + } + + $affected_users = array("feed" => array(), "friends" => array(), "activities" => array()); + + foreach ($js->entry as $entry) { + $fbuser = $entry->uid; + foreach ($entry->changed_fields as $field) { + if (!isset($affected_users[$field])) { + logger('facebook_init: Unknown field "' . $field . '"'); + continue; + } + if (in_array($fbuser, $affected_users[$field])) continue; + switch ($field) { + case "feed": + logger('facebook_init: FB-User ' . $fbuser . ' / feed', LOGGER_DEBUG); + + $r = q("SELECT `uid` FROM `pconfig` WHERE `cat` = 'facebook' AND `k` = 'self_id' AND `v` = '%s' LIMIT 1", dbesc($fbuser)); + if(! count($r)) + continue; + $uid = $r[0]['uid']; + + $access_token = get_pconfig($uid,'facebook','access_token'); + if(! $access_token) + return; + + if(! get_pconfig($uid,'facebook','no_wall')) { + $private_wall = intval(get_pconfig($uid,'facebook','private_wall')); + $s = fetch_url('https://graph.facebook.com/me/feed?access_token=' . $access_token); + if($s) { + $j = json_decode($s); + logger('facebook_init: wall: ' . print_r($j,true), LOGGER_DATA); + fb_consume_stream($uid,$j,($private_wall) ? false : true); + } + } + + break; + case "friend": + // @TODO + break; + case "activities": + //@TODO + break; + } + $affected_users[$field][] = $fbuser; + } + } + } + if($a->argc != 2) return; $nick = $a->argv[1]; @@ -479,6 +559,32 @@ function facebook_plugin_settings(&$a,&$b) { } + +function facebook_plugin_admin(&$a, &$o){ + + $activated = false; + $access_token = fb_get_app_access_token(); + if ($access_token) { + $ret = facebook_subscriptions_get(); + if (is_array($ret)) foreach ($ret as $re) if (is_object($re) && $re->object == "user") $activated = true; + } + if ($activated) { + $o = t('Real-Time Updates are activated.') . '

'; + $o .= ''; + } else { + $o = t('Real-Time Updates not activated.') . '
'; + } +} + +function facebook_plugin_admin_post(&$a, &$o){ + if (x($_REQUEST,'real_time_activate')) { + facebook_subscription_add_users(); + } + if (x($_REQUEST,'real_time_deactivate')) { + facebook_subscription_del_users(); + } +} + function facebook_jot_nets(&$a,&$b) { if(! local_user()) return; @@ -1153,3 +1259,178 @@ function fb_consume_stream($uid,$j,$wall = false) { } } + +function fb_get_app_access_token() { + + $acc_token = get_config('facebook','app_access_token'); + + if ($acc_token !== false) return $acc_token; + + $appid = get_config('facebook','appid'); + $appsecret = get_config('facebook', 'appsecret'); + + if ($appid === false || $appsecret === false) { + logger('fb_get_app_access_token: appid and/or appsecret not set', LOGGER_DEBUG); + return false; + } + + $x = fetch_url('https://graph.facebook.com/oauth/access_token?client_id=' . $appid . '&client_secret=' . $appsecret . "&grant_type=client_credentials"); + + if(strpos($x,'access_token=') !== false) { + logger('fb_get_app_access_token: returned access token: ' . $x, LOGGER_DATA); + + $token = str_replace('access_token=', '', $x); + if(strpos($token,'&') !== false) + $token = substr($token,0,strpos($token,'&')); + + if ($token == "") { + logger('fb_get_app_access_token: empty token: ' . $x, LOGGER_DEBUG); + return false; + } + set_config('facebook','app_access_token',$token); + return $token; + } else { + logger('fb_get_app_access_token: response did not contain an access_token: ' . $x, LOGGER_DATA); + return false; + } +} + +function facebook_subscription_del_users() { + $a = get_app(); + $access_token = fb_get_app_access_token(); + + $url = "https://graph.facebook.com/" . get_config('facebook', 'appid' ) . "/subscriptions?access_token=" . $access_token; + delete_url($url); +} + +function facebook_subscription_add_users() { + + $a = get_app(); + $access_token = fb_get_app_access_token(); + + $url = "https://graph.facebook.com/" . get_config('facebook', 'appid' ) . "/subscriptions?access_token=" . $access_token; + + list($usec, $sec) = explode(" ", microtime()); + $verify_token = sha1($usec . $sec . rand(0, 999999999)); + set_config('facebook', 'cb_verify_token', $verify_token); + + $cb = $a->get_baseurl() . '/facebook/?realtime_cb=1'; + + $j = post_url($url,array( + "object" => "user", + "fields" => "feed,friends,activities", + "callback_url" => $cb, + "verify_token" => $verify_token, + )); + del_config('facebook', 'cb_verify_token'); + + if ($j) { + logger("Facebook reponse: " . $j, LOGGER_DATA); + }; +} + +function facebook_subscriptions_get() { + + $access_token = fb_get_app_access_token(); + + $url = "https://graph.facebook.com/" . get_config('facebook', 'appid' ) . "/subscriptions?access_token=" . $access_token; + $j = fetch_url($url); + $ret = null; + if ($j) { + $x = json_decode($j); + if (isset($x->data)) $ret = $x->data; + } + return $ret; +} + + + + + + + +// DELETE-request to $url + +if(! function_exists('delete_url')) { +function delete_url($url,$headers = null, &$redirects = 0, $timeout = 0) { + $a = get_app(); + $ch = curl_init($url); + if(($redirects > 8) || (! $ch)) + return false; + + curl_setopt($ch, CURLOPT_HEADER, true); + curl_setopt($ch, CURLOPT_RETURNTRANSFER,true); + curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "DELETE"); + curl_setopt($ch, CURLOPT_USERAGENT, "Friendica"); + + if(intval($timeout)) { + curl_setopt($ch, CURLOPT_TIMEOUT, $timeout); + } + else { + $curl_time = intval(get_config('system','curl_timeout')); + curl_setopt($ch, CURLOPT_TIMEOUT, (($curl_time !== false) ? $curl_time : 60)); + } + + if(defined('LIGHTTPD')) { + if(!is_array($headers)) { + $headers = array('Expect:'); + } else { + if(!in_array('Expect:', $headers)) { + array_push($headers, 'Expect:'); + } + } + } + if($headers) + curl_setopt($ch, CURLOPT_HTTPHEADER, $headers); + + $check_cert = get_config('system','verifyssl'); + curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, (($check_cert) ? true : false)); + $prx = get_config('system','proxy'); + if(strlen($prx)) { + curl_setopt($ch, CURLOPT_HTTPPROXYTUNNEL, 1); + curl_setopt($ch, CURLOPT_PROXY, $prx); + $prxusr = get_config('system','proxyuser'); + if(strlen($prxusr)) + curl_setopt($ch, CURLOPT_PROXYUSERPWD, $prxusr); + } + + $a->set_curl_code(0); + + // don't let curl abort the entire application + // if it throws any errors. + + $s = @curl_exec($ch); + + $base = $s; + $curl_info = curl_getinfo($ch); + $http_code = $curl_info['http_code']; + + $header = ''; + + // Pull out multiple headers, e.g. proxy and continuation headers + // allow for HTTP/2.x without fixing code + + while(preg_match('/^HTTP\/[1-2].+? [1-5][0-9][0-9]/',$base)) { + $chunk = substr($base,0,strpos($base,"\r\n\r\n")+4); + $header .= $chunk; + $base = substr($base,strlen($chunk)); + } + + if($http_code == 301 || $http_code == 302 || $http_code == 303) { + $matches = array(); + preg_match('/(Location:|URI:)(.*?)\n/', $header, $matches); + $url = trim(array_pop($matches)); + $url_parsed = @parse_url($url); + if (isset($url_parsed)) { + $redirects++; + return delete_url($url,$headers,$redirects,$timeout); + } + } + $a->set_curl_code($http_code); + $body = substr($s,strlen($header)); + + $a->set_curl_headers($header); + + curl_close($ch); + return($body); +}} \ No newline at end of file From b83a5a74241c2abeea45ad674fed958468896b0b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobias=20H=C3=B6=C3=9Fl?= Date: Sun, 26 Feb 2012 20:36:24 +0000 Subject: [PATCH 2/5] Bugfix --- facebook/facebook.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/facebook/facebook.php b/facebook/facebook.php index cc8b85cd..039b27ff 100755 --- a/facebook/facebook.php +++ b/facebook/facebook.php @@ -84,7 +84,7 @@ function facebook_init(&$a) { if (x($_REQUEST, "realtime_cb") && x($_REQUEST, "realtime_cb")) { logger("facebook_init: Facebook Real-Time callback called", LOGGER_DEBUG); - if (x($_REQUEST["hub_verify_token"])) { + if (x($_REQUEST, "hub_verify_token")) { // this is the verification callback while registering for real time updates $verify_token = get_config('facebook', 'cb_verify_token'); From 5a4ca9459ddf60d4b19748625b73015ff6149a92 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobias=20H=C3=B6=C3=9Fl?= Date: Wed, 29 Feb 2012 14:06:19 +0000 Subject: [PATCH 3/5] Real-Time Updates --- facebook/README | 5 +- facebook/facebook.php | 348 +++++++++++++++++++++++++----------------- 2 files changed, 210 insertions(+), 143 deletions(-) diff --git a/facebook/README b/facebook/README index ee7e489b..e2a3c838 100755 --- a/facebook/README +++ b/facebook/README @@ -23,7 +23,10 @@ Installing the Friendica/Facebook connector and click 'Install Facebook Connector'. 4. This will ask you to login to Facebook and grant permission to the plugin to do its stuff. Allow it to do so. -5. You're done. To turn it off visit the Plugin Settings page again and +5. Optional step: If you want to use Facebook Real Time Updates (so new messages + and new contacts are added ~1min after they are postet / added on FB), go to + Settings -> plugins -> facebook and press the "Activate Real-Time Updates"-button. +6. You're done. To turn it off visit the Plugin Settings page again and 'Remove Facebook posting'. Vidoes and embeds will not be posted if there is no other content. Links diff --git a/facebook/facebook.php b/facebook/facebook.php index 039b27ff..0e30b62b 100755 --- a/facebook/facebook.php +++ b/facebook/facebook.php @@ -1,8 +1,9 @@ + * Tobias Hößl */ /** @@ -31,7 +32,10 @@ * and click 'Install Facebook Connector'. * 4. This will ask you to login to Facebook and grant permission to the * plugin to do its stuff. Allow it to do so. - * 5. You're done. To turn it off visit the Plugin Settings page again and + * 5. Optional step: If you want to use Facebook Real Time Updates (so new messages + * and new contacts are added ~1min after they are postet / added on FB), go to + * Settings -> plugins -> facebook and press the "Activate Real-Time Updates"-button. + * 6. You're done. To turn it off visit the Plugin Settings page again and * 'Remove Facebook posting'. * * Vidoes and embeds will not be posted if there is no other content. Links @@ -53,6 +57,8 @@ function facebook_install() { register_hook('connector_settings', 'addon/facebook/facebook.php', 'facebook_plugin_settings'); register_hook('cron', 'addon/facebook/facebook.php', 'facebook_cron'); register_hook('queue_predeliver', 'addon/facebook/facebook.php', 'fb_queue_hook'); + + if (get_config('facebook', 'realtime_active') == 1) facebook_subscription_add_users(); // Restore settings, if the plugin was installed before } @@ -67,6 +73,8 @@ function facebook_uninstall() { // hook moved unregister_hook('post_local_end', 'addon/facebook/facebook.php', 'facebook_post_hook'); unregister_hook('plugin_settings', 'addon/facebook/facebook.php', 'facebook_plugin_settings'); + + if (get_config('facebook', 'realtime_active') == 1) facebook_subscription_del_users(); } @@ -113,7 +121,7 @@ function facebook_init(&$a) { return; } - $affected_users = array("feed" => array(), "friends" => array(), "activities" => array()); + $affected_users = array("feed" => array(), "friends" => array()); foreach ($js->entry as $entry) { $fbuser = $entry->uid; @@ -123,19 +131,20 @@ function facebook_init(&$a) { continue; } if (in_array($fbuser, $affected_users[$field])) continue; + + $r = q("SELECT `uid` FROM `pconfig` WHERE `cat` = 'facebook' AND `k` = 'self_id' AND `v` = '%s' LIMIT 1", dbesc($fbuser)); + if(! count($r)) + continue; + $uid = $r[0]['uid']; + + $access_token = get_pconfig($uid,'facebook','access_token'); + if(! $access_token) + return; + switch ($field) { case "feed": logger('facebook_init: FB-User ' . $fbuser . ' / feed', LOGGER_DEBUG); - $r = q("SELECT `uid` FROM `pconfig` WHERE `cat` = 'facebook' AND `k` = 'self_id' AND `v` = '%s' LIMIT 1", dbesc($fbuser)); - if(! count($r)) - continue; - $uid = $r[0]['uid']; - - $access_token = get_pconfig($uid,'facebook','access_token'); - if(! $access_token) - return; - if(! get_pconfig($uid,'facebook','no_wall')) { $private_wall = intval(get_pconfig($uid,'facebook','private_wall')); $s = fetch_url('https://graph.facebook.com/me/feed?access_token=' . $access_token); @@ -147,12 +156,14 @@ function facebook_init(&$a) { } break; - case "friend": - // @TODO - break; - case "activities": - //@TODO + case "friends": + logger('facebook_init: FB-User ' . $fbuser . ' / friends', LOGGER_DEBUG); + + fb_get_friends($uid, false); + set_pconfig($uid,'facebook','friend_check',time()); break; + default: + logger('facebook_init: Unknown callback field for ' . $fbuser, LOGGER_NORMAL); } $affected_users[$field][] = $fbuser; } @@ -171,8 +182,8 @@ function facebook_init(&$a) { return; $uid = $r[0]['uid']; - $auth_code = (($_GET['code']) ? $_GET['code'] : ''); - $error = (($_GET['error_description']) ? $_GET['error_description'] : ''); + $auth_code = (x($_GET, 'code') ? $_GET['code'] : ''); + $error = (x($_GET, 'error_description') ? $_GET['error_description'] : ''); if($error) @@ -199,7 +210,7 @@ function facebook_init(&$a) { if(get_pconfig($uid,'facebook','no_linking') === false) set_pconfig($uid,'facebook','no_linking',1); fb_get_self($uid); - fb_get_friends($uid); + fb_get_friends($uid, true); fb_consume_all($uid); } @@ -220,9 +231,130 @@ function fb_get_self($uid) { } } +function fb_get_friends_sync_new($uid, $access_token, $person) { + $link = 'http://facebook.com/profile.php?id=' . $person->id; + + $r = q("SELECT * FROM `contact` WHERE `uid` = %d AND `url` = '%s' LIMIT 1", + intval($uid), + dbesc($link) + ); + + if (count($r) == 0) { + logger('fb_get_friends: new contact found: ' . $link, LOGGER_DEBUG); + + fb_get_friends_sync_full($uid, $access_token, $person); + } +} +function fb_get_friends_sync_full($uid, $access_token, $person) { + $s = fetch_url('https://graph.facebook.com/' . $person->id . '?access_token=' . $access_token); + if($s) { + $jp = json_decode($s); + logger('fb_get_friends: info: ' . print_r($jp,true), LOGGER_DATA); -function fb_get_friends($uid) { + // always use numeric link for consistency + + $jp->link = 'http://facebook.com/profile.php?id=' . $person->id; + + // check if we already have a contact + + $r = q("SELECT * FROM `contact` WHERE `uid` = %d AND `url` = '%s' LIMIT 1", + intval($uid), + dbesc($jp->link) + ); + + if(count($r)) { + + // check that we have all the photos, this has been known to fail on occasion + + if((! $r[0]['photo']) || (! $r[0]['thumb']) || (! $r[0]['micro'])) { + require_once("Photo.php"); + + $photos = import_profile_photo('https://graph.facebook.com/' . $jp->id . '/picture', $uid, $r[0]['id']); + + $r = q("UPDATE `contact` SET `photo` = '%s', + `thumb` = '%s', + `micro` = '%s', + `name-date` = '%s', + `uri-date` = '%s', + `avatar-date` = '%s' + WHERE `id` = %d LIMIT 1 + ", + dbesc($photos[0]), + dbesc($photos[1]), + dbesc($photos[2]), + dbesc(datetime_convert()), + dbesc(datetime_convert()), + dbesc(datetime_convert()), + intval($r[0]['id']) + ); + } + continue; + } + else { + + // create contact record + $r = q("INSERT INTO `contact` ( `uid`, `created`, `url`, `nurl`, `addr`, `alias`, `notify`, `poll`, + `name`, `nick`, `photo`, `network`, `rel`, `priority`, + `writable`, `blocked`, `readonly`, `pending` ) + VALUES ( %d, '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', %d, %d, %d, 0, 0, 0 ) ", + intval($uid), + dbesc(datetime_convert()), + dbesc($jp->link), + dbesc(normalise_link($jp->link)), + dbesc(''), + dbesc(''), + dbesc($jp->id), + dbesc('facebook ' . $jp->id), + dbesc($jp->name), + dbesc(($jp->nickname) ? $jp->nickname : strtolower($jp->first_name)), + dbesc('https://graph.facebook.com/' . $jp->id . '/picture'), + dbesc(NETWORK_FACEBOOK), + intval(CONTACT_IS_FRIEND), + intval(1), + intval(1) + ); + } + + $r = q("SELECT * FROM `contact` WHERE `url` = '%s' AND `uid` = %d LIMIT 1", + dbesc($jp->link), + intval($uid) + ); + + if(! count($r)) { + continue; + } + + $contact = $r[0]; + $contact_id = $r[0]['id']; + + require_once("Photo.php"); + + $photos = import_profile_photo($r[0]['photo'],$uid,$contact_id); + + $r = q("UPDATE `contact` SET `photo` = '%s', + `thumb` = '%s', + `micro` = '%s', + `name-date` = '%s', + `uri-date` = '%s', + `avatar-date` = '%s' + WHERE `id` = %d LIMIT 1 + ", + dbesc($photos[0]), + dbesc($photos[1]), + dbesc($photos[2]), + dbesc(datetime_convert()), + dbesc(datetime_convert()), + dbesc(datetime_convert()), + intval($contact_id) + ); + + } +} + +// if $fullsync is true, only new contacts are searched for + +function fb_get_friends($uid, $fullsync = true) { $r = q("SELECT `uid` FROM `user` WHERE `uid` = %d AND `account_expired` = 0 LIMIT 1", intval($uid) @@ -245,111 +377,11 @@ function fb_get_friends($uid) { logger('facebook: fb_get_friends: json: ' . print_r($j,true), LOGGER_DATA); if(! $j->data) return; - foreach($j->data as $person) { - $s = fetch_url('https://graph.facebook.com/' . $person->id . '?access_token=' . $access_token); - if($s) { - $jp = json_decode($s); - logger('fb_get_friends: info: ' . print_r($jp,true), LOGGER_DATA); - - // always use numeric link for consistency - - $jp->link = 'http://facebook.com/profile.php?id=' . $person->id; - - // check if we already have a contact - - $r = q("SELECT * FROM `contact` WHERE `uid` = %d AND `url` = '%s' LIMIT 1", - intval($uid), - dbesc($jp->link) - ); - - if(count($r)) { - - // check that we have all the photos, this has been known to fail on occasion - - if((! $r[0]['photo']) || (! $r[0]['thumb']) || (! $r[0]['micro'])) { - require_once("Photo.php"); - - $photos = import_profile_photo('https://graph.facebook.com/' . $jp->id . '/picture', $uid, $r[0]['id']); - - $r = q("UPDATE `contact` SET `photo` = '%s', - `thumb` = '%s', - `micro` = '%s', - `name-date` = '%s', - `uri-date` = '%s', - `avatar-date` = '%s' - WHERE `id` = %d LIMIT 1 - ", - dbesc($photos[0]), - dbesc($photos[1]), - dbesc($photos[2]), - dbesc(datetime_convert()), - dbesc(datetime_convert()), - dbesc(datetime_convert()), - intval($r[0]['id']) - ); - } - continue; - } - else { - - // create contact record - $r = q("INSERT INTO `contact` ( `uid`, `created`, `url`, `nurl`, `addr`, `alias`, `notify`, `poll`, - `name`, `nick`, `photo`, `network`, `rel`, `priority`, - `writable`, `blocked`, `readonly`, `pending` ) - VALUES ( %d, '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', %d, %d, %d, 0, 0, 0 ) ", - intval($uid), - dbesc(datetime_convert()), - dbesc($jp->link), - dbesc(normalise_link($jp->link)), - dbesc(''), - dbesc(''), - dbesc($jp->id), - dbesc('facebook ' . $jp->id), - dbesc($jp->name), - dbesc(($jp->nickname) ? $jp->nickname : strtolower($jp->first_name)), - dbesc('https://graph.facebook.com/' . $jp->id . '/picture'), - dbesc(NETWORK_FACEBOOK), - intval(CONTACT_IS_FRIEND), - intval(1), - intval(1) - ); - } - - $r = q("SELECT * FROM `contact` WHERE `url` = '%s' AND `uid` = %d LIMIT 1", - dbesc($jp->link), - intval($uid) - ); - - if(! count($r)) { - continue; - } - - $contact = $r[0]; - $contact_id = $r[0]['id']; - - require_once("Photo.php"); - - $photos = import_profile_photo($r[0]['photo'],$uid,$contact_id); - - $r = q("UPDATE `contact` SET `photo` = '%s', - `thumb` = '%s', - `micro` = '%s', - `name-date` = '%s', - `uri-date` = '%s', - `avatar-date` = '%s' - WHERE `id` = %d LIMIT 1 - ", - dbesc($photos[0]), - dbesc($photos[1]), - dbesc($photos[2]), - dbesc(datetime_convert()), - dbesc(datetime_convert()), - dbesc(datetime_convert()), - intval($contact_id) - ); - - } - } + foreach($j->data as $person) + if ($fullsync) + fb_get_friends_sync_full($uid, $access_token, $person); + else + fb_get_friends_sync_new($uid, $access_token, $person); } } @@ -394,7 +426,7 @@ function facebook_post(&$a) { elseif(intval($no_linking) && intval($linkvalue)) { // FB linkage is now allowed - import stuff. fb_get_self($uid); - fb_get_friends($uid); + fb_get_friends($uid, true); fb_consume_all($uid); } @@ -419,7 +451,7 @@ function facebook_content(&$a) { } if($a->argc > 1 && $a->argv[1] === 'friends') { - fb_get_friends(local_user()); + fb_get_friends(local_user(), true); info( t('Updating contacts') . EOL); } @@ -537,13 +569,40 @@ function facebook_cron($a,$b) { if($last_friend_check) $next_friend_check = $last_friend_check + 86400; if($next_friend_check <= time()) { - fb_get_friends($rr['uid']); + fb_get_friends($rr['uid'], true); set_pconfig($rr['uid'],'facebook','friend_check',time()); } fb_consume_all($rr['uid']); } - } - + } + + if (get_config('facebook', 'realtime_active') == 1) { + if (!facebook_check_realtime_active()) { + + logger('facebook_cron: Facebook is not sending Real-Time Updates any more, although it is supposed to. Trying to fix it...', LOGGER_NORMAL); + facebook_subscription_add_users(); + + if (facebook_check_realtime_active()) + logger('facebook_cron: Successful', LOGGER_NORMAL); + else { + logger('facebook_cron: Failed', LOGGER_NORMAL); + + if(strlen($a->config['admin_email']) && !get_config('facebook', 'realtime_err_mailsent')) { + $res = mail($a->config['admin_email'], t('Problems with Facebook Real-Time Updates'), + "Hi!\n\nThere's a problem with the Facebook Real-Time Updates that cannob be solved automatically. Maybe an permission issue?\n\nThis e-mail will only be sent once.", + 'From: ' . t('Administrator') . '@' . $_SERVER['SERVER_NAME'] . "\n" + . 'Content-type: text/plain; charset=UTF-8' . "\n" + . 'Content-transfer-encoding: 8bit' + ); + + set_config('facebook', 'realtime_err_mailsent', 1); + } + } + } else { // !facebook_check_realtime_active() + del_config('facebook', 'realtime_err_mailsent'); + } + } + set_config('facebook','last_poll', time()); } @@ -562,12 +621,7 @@ function facebook_plugin_settings(&$a,&$b) { function facebook_plugin_admin(&$a, &$o){ - $activated = false; - $access_token = fb_get_app_access_token(); - if ($access_token) { - $ret = facebook_subscriptions_get(); - if (is_array($ret)) foreach ($ret as $re) if (is_object($re) && $re->object == "user") $activated = true; - } + $activated = facebook_check_realtime_active(); if ($activated) { $o = t('Real-Time Updates are activated.') . '

'; $o .= ''; @@ -1300,7 +1354,9 @@ function facebook_subscription_del_users() { $access_token = fb_get_app_access_token(); $url = "https://graph.facebook.com/" . get_config('facebook', 'appid' ) . "/subscriptions?access_token=" . $access_token; - delete_url($url); + facebook_delete_url($url); + + del_config('facebook', 'realtime_active'); } function facebook_subscription_add_users() { @@ -1318,7 +1374,7 @@ function facebook_subscription_add_users() { $j = post_url($url,array( "object" => "user", - "fields" => "feed,friends,activities", + "fields" => "feed,friends", "callback_url" => $cb, "verify_token" => $verify_token, )); @@ -1326,12 +1382,15 @@ function facebook_subscription_add_users() { if ($j) { logger("Facebook reponse: " . $j, LOGGER_DATA); + + if (facebook_check_realtime_active()) set_config('facebook', 'realtime_active', 1); }; } function facebook_subscriptions_get() { $access_token = fb_get_app_access_token(); + if (!$access_token) return null; $url = "https://graph.facebook.com/" . get_config('facebook', 'appid' ) . "/subscriptions?access_token=" . $access_token; $j = fetch_url($url); @@ -1344,15 +1403,20 @@ function facebook_subscriptions_get() { } - +function facebook_check_realtime_active() { + $ret = facebook_subscriptions_get(); + if (is_null($ret)) return false; + if (is_array($ret)) foreach ($ret as $re) if (is_object($re) && $re->object == "user") return true; + return false; +} // DELETE-request to $url -if(! function_exists('delete_url')) { -function delete_url($url,$headers = null, &$redirects = 0, $timeout = 0) { +if(! function_exists('facebook_delete_url')) { +function facebook_delete_url($url,$headers = null, &$redirects = 0, $timeout = 0) { $a = get_app(); $ch = curl_init($url); if(($redirects > 8) || (! $ch)) From 49f2d04a152b651b8e47699fb28fffcc50abf1b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobias=20H=C3=B6=C3=9Fl?= Date: Mon, 5 Mar 2012 11:43:38 +0000 Subject: [PATCH 4/5] Bugfix --- facebook/facebook.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/facebook/facebook.php b/facebook/facebook.php index 0e30b62b..35338fa1 100755 --- a/facebook/facebook.php +++ b/facebook/facebook.php @@ -289,7 +289,7 @@ function fb_get_friends_sync_full($uid, $access_token, $person) { intval($r[0]['id']) ); } - continue; + return; } else { @@ -322,7 +322,7 @@ function fb_get_friends_sync_full($uid, $access_token, $person) { ); if(! count($r)) { - continue; + return; } $contact = $r[0]; From f68ba9b87d4b6b60dc3ee52787a1baaaec345110 Mon Sep 17 00:00:00 2001 From: Cat Gray Date: Thu, 8 Mar 2012 21:57:57 +0200 Subject: [PATCH 5/5] Added tags support to LJ and DW connectors. (Friendica tags are converted to LJ/DW tags.) --- dwpost.tgz | Bin 0 -> 2630 bytes dwpost/dwpost.php | 10 +++++++++- ljpost.tgz | Bin 2655 -> 2784 bytes ljpost/ljpost.php | 12 ++++++++++++ 4 files changed, 21 insertions(+), 1 deletion(-) create mode 100644 dwpost.tgz diff --git a/dwpost.tgz b/dwpost.tgz new file mode 100644 index 0000000000000000000000000000000000000000..7841e41938f22b4077f7a64c300ceb066f61bf89 GIT binary patch literal 2630 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 literal 0 HcmV?d00001 diff --git a/dwpost/dwpost.php b/dwpost/dwpost.php index c7bfd939..5f8dcc73 100644 --- a/dwpost/dwpost.php +++ b/dwpost/dwpost.php @@ -6,6 +6,7 @@ * Version: 1.0 * Author: Tony Baldwin * Author: Michael Johnston + * Author: Cat Gray */ function dwpost_install() { @@ -173,6 +174,7 @@ function dwpost_send(&$a,&$b) { $title = $b['title']; $post = bbcode($b['body']); $post = xmlify($post); + $tags = dwpost_get_tags($b['tag']); $date = datetime_convert('UTC',$tz,$b['created'],'Y-m-d H:i:s'); $year = intval(substr($date,0,4)); @@ -200,7 +202,7 @@ function dwpost_send(&$a,&$b) { props useragentFriendica -taglistfriendica +taglist$tags @@ -217,3 +219,9 @@ EOT; } } +function dwpost_get_tags($post) +{ + preg_match_all("/\]([^\[#]+)\[/",$post,$matches); + $tags = implode(', ',$matches[1]); + return $tags; +} diff --git a/ljpost.tgz b/ljpost.tgz index 0132a7526e110a74659620cfb3fe1589ea418b3c..60c223f489d7291a5c360061f118859727d65c5f 100644 GIT binary patch literal 2784 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 literal 2655 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 N=zqABqfG!%002}4H5~u| diff --git a/ljpost/ljpost.php b/ljpost/ljpost.php index 16863f67..e1bd2d63 100755 --- a/ljpost/ljpost.php +++ b/ljpost/ljpost.php @@ -6,6 +6,7 @@ * Version: 1.0 * Author: Tony Baldwin * Author: Michael Johnston + * Author: Cat Gray */ function ljpost_install() { @@ -179,6 +180,7 @@ function ljpost_send(&$a,&$b) { $title = xmlify($b['title']); $post = bbcode($b['body']); $post = xmlify($post); + $tags = ljpost_get_tags($b['tag']); $date = datetime_convert('UTC',$tz,$b['created'],'Y-m-d H:i:s'); $year = intval(substr($date,0,4)); @@ -213,6 +215,10 @@ function ljpost_send(&$a,&$b) { useragent Friendica + + taglist + $tags + @@ -232,3 +238,9 @@ EOT; } } +function ljpost_get_tags($post) +{ + preg_match_all("/\]([^\[#]+)\[/",$post,$matches); + $tags = implode(', ',$matches[1]); + return $tags; +}