--- /dev/null
+<?php
+/**
+ * StatusNet - the distributed open-source microblogging tool
+ * Copyright (C) 2010, StatusNet, Inc.
+ *
+ * A plugin for integrating Facebook with StatusNet. Includes single-sign-on
+ * and publishing notices to Facebook using Facebook's Graph API.
+ *
+ * PHP version 5
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @category Pugin
+ * @package StatusNet
+ * @author Zach Copley <zach@status.net>
+ * @copyright 2010 StatusNet, Inc.
+ * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link http://status.net/
+ */
+
+if (!defined('STATUSNET')) {
+ exit(1);
+}
+
+define("FACEBOOK_SERVICE", 2);
+
+/**
+ * Main class for Facebook plugin
+ *
+ * @category Plugin
+ * @package StatusNet
+ * @author Zach Copley <zach@status.net>
+ * @copyright 2010 StatusNet, Inc.
+ * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link http://status.net/
+ */
+class FacebookBridgePlugin extends Plugin
+{
+ public $appId = null; // Facebook application ID
+ public $secret = null; // Facebook application secret
+ public $facebook = null; // Facebook application instance
+ public $dir = null; // Facebook SSO plugin dir
+
+ /**
+ * Initializer for this plugin
+ *
+ * Gets an instance of the Facebook API client object
+ *
+ * @return boolean hook value; true means continue processing, false means stop.
+ */
+ function initialize()
+ {
+ $this->facebook = Facebookclient::getFacebook(
+ $this->appId,
+ $this->secret
+ );
+
+ return true;
+ }
+
+ /**
+ * Load related modules when needed
+ *
+ * @param string $cls Name of the class to be loaded
+ *
+ * @return boolean hook value; true means continue processing, false means stop.
+ */
+ function onAutoload($cls)
+ {
+
+ $dir = dirname(__FILE__);
+
+ //common_debug("class = " . $cls);
+
+ switch ($cls)
+ {
+ case 'Facebook': // Facebook PHP SDK
+ include_once $dir . '/extlib/facebook.php';
+ return false;
+ case 'FacebookloginAction':
+ case 'FacebookfinishloginAction':
+ case 'FacebookadminpanelAction':
+ case 'FacebooksettingsAction':
+ case 'FacebookdeauthorizeAction':
+ include_once $dir . '/actions/' . strtolower(mb_substr($cls, 0, -6)) . '.php';
+ return false;
+ case 'Facebookclient':
+ case 'FacebookQueueHandler':
+ include_once $dir . '/lib/' . strtolower($cls) . '.php';
+ return false;
+ case 'Notice_to_item':
+ include_once $dir . '/classes/' . $cls . '.php';
+ return false;
+ default:
+ return true;
+ }
+
+ }
+
+ /**
+ * Database schema setup
+ *
+ * We maintain a table mapping StatusNet notices to Facebook items
+ *
+ * @see Schema
+ * @see ColumnDef
+ *
+ * @return boolean hook value; true means continue processing, false means stop.
+ */
+ function onCheckSchema()
+ {
+ $schema = Schema::get();
+ $schema->ensureTable('notice_to_item', Notice_to_item::schemaDef());
+ return true;
+ }
+
+ /*
+ * Does this $action need the Facebook JavaScripts?
+ */
+ function needsScripts($action)
+ {
+ static $needy = array(
+ 'FacebookloginAction',
+ 'FacebookfinishloginAction',
+ 'FacebookadminpanelAction',
+ 'FacebooksettingsAction'
+ );
+
+ if (in_array(get_class($action), $needy)) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Map URLs to actions
+ *
+ * @param Net_URL_Mapper $m path-to-action mapper
+ *
+ * @return boolean hook value; true means continue processing, false means stop.
+ */
+ function onRouterInitialized($m)
+ {
+ // Always add the admin panel route
+ $m->connect('admin/facebook', array('action' => 'facebookadminpanel'));
+
+ // Only add these routes if an application has been setup on
+ // Facebook for the plugin to use.
+ if ($this->hasApplication()) {
+
+ $m->connect(
+ 'main/facebooklogin',
+ array('action' => 'facebooklogin')
+ );
+ $m->connect(
+ 'main/facebookfinishlogin',
+ array('action' => 'facebookfinishlogin')
+ );
+ $m->connect(
+ 'settings/facebook',
+ array('action' => 'facebooksettings')
+ );
+ $m->connect(
+ 'facebook/deauthorize',
+ array('action' => 'facebookdeauthorize')
+ );
+
+ }
+
+ return true;
+ }
+
+ /*
+ * Add a login tab for Facebook, but only if there's a Facebook
+ * application defined for the plugin to use.
+ *
+ * @param Action &action the current action
+ *
+ * @return void
+ */
+ function onEndLoginGroupNav(&$action)
+ {
+ $action_name = $action->trimmed('action');
+
+ if ($this->hasApplication()) {
+
+ $action->menuItem(
+ common_local_url('facebooklogin'),
+ _m('MENU', 'Facebook'),
+ // TRANS: Tooltip for menu item "Facebook".
+ _m('Login or register using Facebook'),
+ 'facebooklogin' === $action_name
+ );
+ }
+
+ return true;
+ }
+
+ /**
+ * Add a Facebook tab to the admin panels
+ *
+ * @param Widget $nav Admin panel nav
+ *
+ * @return boolean hook value
+ */
+ function onEndAdminPanelNav($nav)
+ {
+ if (AdminPanelAction::canAdmin('facebook')) {
+
+ $action_name = $nav->action->trimmed('action');
+
+ $nav->out->menuItem(
+ common_local_url('facebookadminpanel'),
+ // TRANS: Menu item.
+ _m('MENU','Facebook'),
+ // TRANS: Tooltip for menu item "Facebook".
+ _m('Facebook integration configuration'),
+ $action_name == 'facebookadminpanel',
+ 'nav_facebook_admin_panel'
+ );
+ }
+
+ return true;
+ }
+
+ /*
+ * Add a tab for user-level Facebook settings
+ *
+ * @param Action &action the current action
+ *
+ * @return void
+ */
+ function onEndConnectSettingsNav(&$action)
+ {
+ if ($this->hasApplication()) {
+ $action_name = $action->trimmed('action');
+
+ $action->menuItem(
+ common_local_url('facebooksettings'),
+ // TRANS: Menu item tab.
+ _m('MENU','Facebook'),
+ // TRANS: Tooltip for menu item "Facebook".
+ _m('Facebook settings'),
+ $action_name === 'facebooksettings'
+ );
+ }
+
+ return true;
+ }
+
+ /*
+ * Is there a Facebook application for the plugin to use?
+ *
+ * Checks to see if a Facebook application ID and secret
+ * have been configured and a valid Facebook API client
+ * object exists.
+ *
+ */
+ function hasApplication()
+ {
+ if (!empty($this->facebook)) {
+
+ $appId = $this->facebook->getAppId();
+ $secret = $this->facebook->getApiSecret();
+
+ if (!empty($appId) && !empty($secret)) {
+ return true;
+ }
+
+ }
+
+ return false;
+ }
+
+ /*
+ * Output a Facebook div for the Facebook JavaSsript SDK to use
+ *
+ * @param Action $action the current action
+ *
+ */
+ function onStartShowHeader($action)
+ {
+ // output <div id="fb-root"></div> as close to <body> as possible
+ $action->element('div', array('id' => 'fb-root'));
+ return true;
+ }
+
+ /*
+ * Load the Facebook JavaScript SDK on pages that need them.
+ *
+ * @param Action $action the current action
+ *
+ */
+ function onEndShowScripts($action)
+ {
+ if ($this->needsScripts($action)) {
+
+ $action->script('https://connect.facebook.net/en_US/all.js');
+
+ $script = <<<ENDOFSCRIPT
+FB.init({appId: %1\$s, session: %2\$s, status: true, cookie: true, xfbml: true});
+
+$('#facebook_button').bind('click', function(event) {
+
+ event.preventDefault();
+
+ FB.login(function(response) {
+ if (response.session && response.perms) {
+ window.location.href = '%3\$s';
+ } else {
+ // NOP (user cancelled login)
+ }
+ }, {perms:'read_stream,publish_stream,offline_access,user_status,user_location,user_website,email'});
+});
+ENDOFSCRIPT;
+
+ $action->inlineScript(
+ sprintf($script,
+ json_encode($this->facebook->getAppId()),
+ json_encode($this->facebook->getSession()),
+ common_local_url('facebookfinishlogin')
+ )
+ );
+ }
+ }
+
+ /*
+ * Log the user out of Facebook, per the Facebook authentication guide
+ *
+ * @param Action action the current action
+ */
+ function onEndLogout($action)
+ {
+ if ($this->hasApplication()) {
+ $session = $this->facebook->getSession();
+ $fbuser = null;
+ $fbuid = null;
+
+ if ($session) {
+ try {
+ $fbuid = $this->facebook->getUser();
+ $fbuser = $this->facebook->api('/me');
+ } catch (FacebookApiException $e) {
+ common_log(LOG_ERROR, $e, __FILE__);
+ }
+ }
+
+ if (!empty($fbuser)) {
+
+ $logoutUrl = $this->facebook->getLogoutUrl(
+ array('next' => common_local_url('public'))
+ );
+
+ common_log(
+ LOG_INFO,
+ sprintf(
+ "Logging user out of Facebook (fbuid = %s)",
+ $fbuid
+ ),
+ __FILE__
+ );
+ common_debug("LOGOUT URL = $logoutUrl");
+ common_redirect($logoutUrl, 303);
+ }
+
+ }
+ }
+
+ /*
+ * Add fbml namespace to our HTML, so Facebook's JavaScript SDK can parse
+ * and render XFBML tags
+ *
+ * @param Action $action the current action
+ * @param array $attrs array of attributes for the HTML tag
+ *
+ * @return nothing
+ */
+ function onStartHtmlElement($action, $attrs) {
+
+ if ($this->needsScripts($action)) {
+ $attrs = array_merge(
+ $attrs,
+ array('xmlns:fb' => 'http://www.facebook.com/2008/fbml')
+ );
+ }
+
+ return true;
+ }
+
+ /**
+ * Add a Facebook queue item for each notice
+ *
+ * @param Notice $notice the notice
+ * @param array &$transports the list of transports (queues)
+ *
+ * @return boolean hook return
+ */
+ function onStartEnqueueNotice($notice, &$transports)
+ {
+ if (self::hasApplication() && $notice->isLocal()) {
+ array_push($transports, 'facebook');
+ }
+ return true;
+ }
+
+ /**
+ * Register Facebook notice queue handler
+ *
+ * @param QueueManager $manager
+ *
+ * @return boolean hook return
+ */
+ function onEndInitializeQueueManager($manager)
+ {
+ if (self::hasApplication()) {
+ $manager->connect('facebook', 'FacebookQueueHandler');
+ }
+ return true;
+ }
+
+ /*
+ * Use SSL for Facebook stuff
+ *
+ * @param string $action name
+ * @param boolean $ssl outval to force SSL
+ * @return mixed hook return value
+ */
+ function onSensitiveAction($action, &$ssl)
+ {
+ $sensitive = array(
+ 'facebookadminpanel',
+ 'facebooksettings',
+ 'facebooklogin',
+ 'facebookfinishlogin'
+ );
+
+ if (in_array($action, $sensitive)) {
+ $ssl = true;
+ return false;
+ } else {
+ return true;
+ }
+ }
+
+ /**
+ * If a notice gets deleted, remove the Notice_to_item mapping and
+ * delete the item on Facebook
+ *
+ * @param User $user The user doing the deleting
+ * @param Notice $notice The notice getting deleted
+ *
+ * @return boolean hook value
+ */
+ function onStartDeleteOwnNotice(User $user, Notice $notice)
+ {
+ $client = new Facebookclient($notice);
+ $client->streamRemove();
+
+ return true;
+ }
+
+ /**
+ * Notify remote users when their notices get favorited.
+ *
+ * @param Profile or User $profile of local user doing the faving
+ * @param Notice $notice being favored
+ * @return hook return value
+ */
+ function onEndFavorNotice(Profile $profile, Notice $notice)
+ {
+ $client = new Facebookclient($notice);
+ $client->like();
+
+ return true;
+ }
+
+ /**
+ * Notify remote users when their notices get de-favorited.
+ *
+ * @param Profile $profile Profile person doing the de-faving
+ * @param Notice $notice Notice being favored
+ *
+ * @return hook return value
+ */
+ function onEndDisfavorNotice(Profile $profile, Notice $notice)
+ {
+ $client = new Facebookclient($notice);
+ $client->unLike();
+
+ return true;
+ }
+
+ /*
+ * Add version info for this plugin
+ *
+ * @param array &$versions plugin version descriptions
+ */
+ function onPluginVersion(&$versions)
+ {
+ $versions[] = array(
+ 'name' => 'Facebook Single-Sign-On',
+ 'version' => STATUSNET_VERSION,
+ 'author' => 'Craig Andrews, Zach Copley',
+ 'homepage' => 'http://status.net/wiki/Plugin:FacebookBridge',
+ 'rawdescription' =>
+ _m('A plugin for integrating StatusNet with Facebook.')
+ );
+
+ return true;
+ }
+}
+++ /dev/null
-<?php
-/**
- * StatusNet - the distributed open-source microblogging tool
- * Copyright (C) 2010, StatusNet, Inc.
- *
- * A plugin for integrating Facebook with StatusNet. Includes single-sign-on
- * and publishing notices to Facebook using Facebook's Graph API.
- *
- * PHP version 5
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- * @category Pugin
- * @package StatusNet
- * @author Zach Copley <zach@status.net>
- * @copyright 2010 StatusNet, Inc.
- * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
- * @link http://status.net/
- */
-
-if (!defined('STATUSNET')) {
- exit(1);
-}
-
-define("FACEBOOK_SERVICE", 2);
-
-/**
- * Main class for Facebook plugin
- *
- * @category Plugin
- * @package StatusNet
- * @author Zach Copley <zach@status.net>
- * @copyright 2010 StatusNet, Inc.
- * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
- * @link http://status.net/
- */
-class FacebookSSOPlugin extends Plugin
-{
- public $appId = null; // Facebook application ID
- public $apikey = null; // Facebook API key (for deprecated "Old REST API")
- public $secret = null; // Facebook application secret
- public $facebook = null; // Facebook application instance
- public $dir = null; // Facebook SSO plugin dir
-
- /**
- * Initializer for this plugin
- *
- * Gets an instance of the Facebook API client object
- *
- * @return boolean hook value; true means continue processing, false means stop.
- */
- function initialize()
- {
- $this->facebook = Facebookclient::getFacebook(
- $this->appId,
- $this->apikey,
- $this->secret
- );
-
- return true;
- }
-
- /**
- * Load related modules when needed
- *
- * @param string $cls Name of the class to be loaded
- *
- * @return boolean hook value; true means continue processing, false means stop.
- */
- function onAutoload($cls)
- {
-
- $dir = dirname(__FILE__);
-
- //common_debug("class = " . $cls);
-
- switch ($cls)
- {
- case 'Facebook': // Facebook PHP SDK
- include_once $dir . '/extlib/facebook.php';
- return false;
- case 'FacebookloginAction':
- case 'FacebookfinishloginAction':
- case 'FacebookadminpanelAction':
- case 'FacebooksettingsAction':
- case 'FacebookdeauthorizeAction':
- include_once $dir . '/actions/' . strtolower(mb_substr($cls, 0, -6)) . '.php';
- return false;
- case 'Facebookclient':
- case 'FacebookQueueHandler':
- include_once $dir . '/lib/' . strtolower($cls) . '.php';
- return false;
- default:
- return true;
- }
-
- }
-
- /*
- * Does this $action need the Facebook JavaScripts?
- */
- function needsScripts($action)
- {
- static $needy = array(
- 'FacebookloginAction',
- 'FacebookfinishloginAction',
- 'FacebookadminpanelAction',
- 'FacebooksettingsAction'
- );
-
- if (in_array(get_class($action), $needy)) {
- return true;
- } else {
- return false;
- }
- }
-
- /**
- * Map URLs to actions
- *
- * @param Net_URL_Mapper $m path-to-action mapper
- *
- * @return boolean hook value; true means continue processing, false means stop.
- */
- function onRouterInitialized($m)
- {
- // Always add the admin panel route
- $m->connect('admin/facebook', array('action' => 'facebookadminpanel'));
-
- // Only add these routes if an application has been setup on
- // Facebook for the plugin to use.
- if ($this->hasApplication()) {
-
- $m->connect(
- 'main/facebooklogin',
- array('action' => 'facebooklogin')
- );
- $m->connect(
- 'main/facebookfinishlogin',
- array('action' => 'facebookfinishlogin')
- );
- $m->connect(
- 'settings/facebook',
- array('action' => 'facebooksettings')
- );
- $m->connect(
- 'facebook/deauthorize',
- array('action' => 'facebookdeauthorize')
- );
-
- }
-
- return true;
- }
-
- /*
- * Add a login tab for Facebook, but only if there's a Facebook
- * application defined for the plugin to use.
- *
- * @param Action &action the current action
- *
- * @return void
- */
- function onEndLoginGroupNav(&$action)
- {
- $action_name = $action->trimmed('action');
-
- if ($this->hasApplication()) {
-
- $action->menuItem(
- common_local_url('facebooklogin'),
- _m('MENU', 'Facebook'),
- // TRANS: Tooltip for menu item "Facebook".
- _m('Login or register using Facebook'),
- 'facebooklogin' === $action_name
- );
- }
-
- return true;
- }
-
- /**
- * Add a Facebook tab to the admin panels
- *
- * @param Widget $nav Admin panel nav
- *
- * @return boolean hook value
- */
- function onEndAdminPanelNav($nav)
- {
- if (AdminPanelAction::canAdmin('facebook')) {
-
- $action_name = $nav->action->trimmed('action');
-
- $nav->out->menuItem(
- common_local_url('facebookadminpanel'),
- // TRANS: Menu item.
- _m('MENU','Facebook'),
- // TRANS: Tooltip for menu item "Facebook".
- _m('Facebook integration configuration'),
- $action_name == 'facebookadminpanel',
- 'nav_facebook_admin_panel'
- );
- }
-
- return true;
- }
-
- /*
- * Add a tab for user-level Facebook settings
- *
- * @param Action &action the current action
- *
- * @return void
- */
- function onEndConnectSettingsNav(&$action)
- {
- if ($this->hasApplication()) {
- $action_name = $action->trimmed('action');
-
- $action->menuItem(
- common_local_url('facebooksettings'),
- // TRANS: Menu item tab.
- _m('MENU','Facebook'),
- // TRANS: Tooltip for menu item "Facebook".
- _m('Facebook settings'),
- $action_name === 'facebooksettings'
- );
- }
-
- return true;
- }
-
- /*
- * Is there a Facebook application for the plugin to use?
- *
- * Checks to see if a Facebook application ID and secret
- * have been configured and a valid Facebook API client
- * object exists.
- *
- */
- function hasApplication()
- {
- if (!empty($this->facebook)) {
-
- $appId = $this->facebook->getAppId();
- $secret = $this->facebook->getApiSecret();
-
- if (!empty($appId) && !empty($secret)) {
- return true;
- }
-
- }
-
- return false;
- }
-
- /*
- * Output a Facebook div for the Facebook JavaSsript SDK to use
- *
- * @param Action $action the current action
- *
- */
- function onStartShowHeader($action)
- {
- // output <div id="fb-root"></div> as close to <body> as possible
- $action->element('div', array('id' => 'fb-root'));
- return true;
- }
-
- /*
- * Load the Facebook JavaScript SDK on pages that need them.
- *
- * @param Action $action the current action
- *
- */
- function onEndShowScripts($action)
- {
- if ($this->needsScripts($action)) {
-
- $action->script('https://connect.facebook.net/en_US/all.js');
-
- $script = <<<ENDOFSCRIPT
-FB.init({appId: %1\$s, session: %2\$s, status: true, cookie: true, xfbml: true});
-
-$('#facebook_button').bind('click', function(event) {
-
- event.preventDefault();
-
- FB.login(function(response) {
- if (response.session && response.perms) {
- window.location.href = '%3\$s';
- } else {
- // NOP (user cancelled login)
- }
- }, {perms:'read_stream,publish_stream,offline_access,user_status,user_location,user_website,email'});
-});
-ENDOFSCRIPT;
-
- $action->inlineScript(
- sprintf($script,
- json_encode($this->facebook->getAppId()),
- json_encode($this->facebook->getSession()),
- common_local_url('facebookfinishlogin')
- )
- );
- }
- }
-
- /*
- * Log the user out of Facebook, per the Facebook authentication guide
- *
- * @param Action action the current action
- */
- function onEndLogout($action)
- {
- if ($this->hasApplication()) {
- $session = $this->facebook->getSession();
- $fbuser = null;
- $fbuid = null;
-
- if ($session) {
- try {
- $fbuid = $this->facebook->getUser();
- $fbuser = $this->facebook->api('/me');
- } catch (FacebookApiException $e) {
- common_log(LOG_ERROR, $e, __FILE__);
- }
- }
-
- if (!empty($fbuser)) {
-
- $logoutUrl = $this->facebook->getLogoutUrl(
- array('next' => common_local_url('public'))
- );
-
- common_log(
- LOG_INFO,
- sprintf(
- "Logging user out of Facebook (fbuid = %s)",
- $fbuid
- ),
- __FILE__
- );
- common_debug("LOGOUT URL = $logoutUrl");
- common_redirect($logoutUrl, 303);
- }
-
- }
- }
-
- /*
- * Add fbml namespace to our HTML, so Facebook's JavaScript SDK can parse
- * and render XFBML tags
- *
- * @param Action $action the current action
- * @param array $attrs array of attributes for the HTML tag
- *
- * @return nothing
- */
- function onStartHtmlElement($action, $attrs) {
-
- if ($this->needsScripts($action)) {
- $attrs = array_merge(
- $attrs,
- array('xmlns:fb' => 'http://www.facebook.com/2008/fbml')
- );
- }
-
- return true;
- }
-
- /**
- * Add a Facebook queue item for each notice
- *
- * @param Notice $notice the notice
- * @param array &$transports the list of transports (queues)
- *
- * @return boolean hook return
- */
- function onStartEnqueueNotice($notice, &$transports)
- {
- if (self::hasApplication() && $notice->isLocal()) {
- array_push($transports, 'facebook');
- }
- return true;
- }
-
- /**
- * Register Facebook notice queue handler
- *
- * @param QueueManager $manager
- *
- * @return boolean hook return
- */
- function onEndInitializeQueueManager($manager)
- {
- if (self::hasApplication()) {
- $manager->connect('facebook', 'FacebookQueueHandler');
- }
- return true;
- }
-
- /*
- * Use SSL for Facebook stuff
- *
- * @param string $action name
- * @param boolean $ssl outval to force SSL
- * @return mixed hook return value
- */
- function onSensitiveAction($action, &$ssl)
- {
- $sensitive = array(
- 'facebookadminpanel',
- 'facebooksettings',
- 'facebooklogin',
- 'facebookfinishlogin'
- );
-
- if (in_array($action, $sensitive)) {
- $ssl = true;
- return false;
- } else {
- return true;
- }
- }
-
- /*
- * Add version info for this plugin
- *
- * @param array &$versions plugin version descriptions
- */
- function onPluginVersion(&$versions)
- {
- $versions[] = array(
- 'name' => 'Facebook Single-Sign-On',
- 'version' => STATUSNET_VERSION,
- 'author' => 'Craig Andrews, Zach Copley',
- 'homepage' => 'http://status.net/wiki/Plugin:FacebookSSO',
- 'rawdescription' =>
- _m('A plugin for integrating StatusNet with Facebook.')
- );
-
- return true;
- }
-}
common_log(
LOG_WARNING,
sprintf(
- '%s (%d), fbuid $s has deauthorized his/her Facebook '
+ '%s (%d), fbuid %d has deauthorized his/her Facebook '
. 'connection but hasn\'t set a password so s/he '
. 'is locked out.',
$user->nickname,
);
} else {
// It probably wasn't Facebook that hit this action,
- // so redirect to the login page
- common_redirect(common_local_url('login'), 303);
+ // so redirect to the public timeline
+ common_redirect(common_local_url('public'), 303);
}
}
}
parent::handle($args);
if (common_is_real_login()) {
-
+
// User is already logged in, are her accounts already linked?
$flink = Foreign_link::getByForeignID($this->fbuid, FACEBOOK_SERVICE);
} else {
// Possibly reconnect an existing account
-
+
$this->connectUser();
}
} else if ($_SERVER['REQUEST_METHOD'] == 'POST') {
+ $this->handlePost();
+ } else {
+ $this->tryLogin();
+ }
+ }
- $token = $this->trimmed('token');
+ function handlePost()
+ {
+ $token = $this->trimmed('token');
- if (!$token || $token != common_session_token()) {
+ if (!$token || $token != common_session_token()) {
+ $this->showForm(
+ _m('There was a problem with your session token. Try again, please.')
+ );
+ return;
+ }
+
+ if ($this->arg('create')) {
+
+ if (!$this->boolean('license')) {
$this->showForm(
- _m('There was a problem with your session token. Try again, please.'));
+ _m('You can\'t register if you don\'t agree to the license.'),
+ $this->trimmed('newname')
+ );
return;
}
- if ($this->arg('create')) {
-
- if (!$this->boolean('license')) {
- $this->showForm(
- _m('You can\'t register if you don\'t agree to the license.'),
- $this->trimmed('newname')
- );
- return;
- }
-
- // We has a valid Facebook session and the Facebook user has
- // agreed to the SN license, so create a new user
- $this->createNewUser();
-
- } else if ($this->arg('connect')) {
+ // We has a valid Facebook session and the Facebook user has
+ // agreed to the SN license, so create a new user
+ $this->createNewUser();
- $this->connectNewUser();
+ } else if ($this->arg('connect')) {
- } else {
+ $this->connectNewUser();
- $this->showForm(
- _m('An unknown error has occured.'),
- $this->trimmed('newname')
- );
- }
} else {
- $this->tryLogin();
+ $this->showForm(
+ _m('An unknown error has occured.'),
+ $this->trimmed('newname')
+ );
}
}
$this->element('div', array('class' => 'error'), $this->error);
} else {
-
+
$this->element(
'div', 'instructions',
// TRANS: %s is the site name.
'nickname' => $nickname,
'fullname' => $this->fbuser['first_name']
. ' ' . $this->fbuser['last_name'],
- 'email' => $this->fbuser['email'],
- 'email_confirmed' => true,
'homepage' => $this->fbuser['website'],
'bio' => $this->fbuser['about'],
'location' => $this->fbuser['location']['name']
);
+ // It's possible that the email address is already in our
+ // DB. It's a unique key, so we need to check
+ if ($this->isNewEmail($this->fbuser['email'])) {
+ $args['email'] = $this->fbuser['email'];
+ $args['email_confirmed'] = true;
+ }
+
if (!empty($invite)) {
$args['code'] = $invite->code;
}
- $user = User::register($args);
-
+ $user = User::register($args);
$result = $this->flinkUser($user->id, $this->fbuid);
if (!$result) {
return;
}
+ // Add a Foreign_user record
+ Facebookclient::addFacebookUser($this->fbuser);
+
$this->setAvatar($user);
common_set_user($user);
common_log(
LOG_INFO,
sprintf(
- 'Registered new user %d from Facebook user %s',
+ 'Registered new user %s (%d) from Facebook user %s, (fbuid %d)',
+ $user->nickname,
$user->id,
+ $this->fbuser['name'],
$this->fbuid
),
__FILE__
);
- common_redirect(
- common_local_url(
- 'showstream',
- array('nickname' => $user->nickname)
- ),
- 303
- );
+ $this->goHome($user->nickname);
}
/*
// fetch the picture from Facebook
$client = new HTTPClient();
- common_debug("status = $status - " . $finalUrl , __FILE__);
-
// fetch the actual picture
$response = $client->get($picUrl);
if ($response->isOk()) {
$finalUrl = $client->getUrl();
- $filename = 'facebook-' . substr(strrchr($finalUrl, '/'), 1 );
- common_debug("Filename = " . $filename, __FILE__);
+ // Make sure the filename is unique becuase it's possible for a user
+ // to deauthorize our app, and then come back in as a new user but
+ // have the same Facebook picture (avatar URLs have a unique index
+ // and their URLs are based on the filenames).
+ $filename = 'facebook-' . common_good_rand(4) . '-'
+ . substr(strrchr($finalUrl, '/'), 1);
$ok = file_put_contents(
Avatar::path($filename),
} else {
+ // save it as an avatar
$profile = $user->getProfile();
if ($profile->setOriginal($filename)) {
common_log(
LOG_INFO,
sprintf(
- 'Saved avatar for %s (%d) from Facebook profile %s, filename = %s',
+ 'Saved avatar for %s (%d) from Facebook picture for '
+ . '%s (fbuid %d), filename = %s',
$user->nickname,
$user->id,
+ $this->fbuser['name'],
$this->fbuid,
- $picture
+ $filename
),
__FILE__
);
$user = User::staticGet('nickname', $nickname);
if (!empty($user)) {
- common_debug('Facebook Connect Plugin - ' .
- "Legit user to connect to Facebook: $nickname");
- }
-
- $result = $this->flinkUser($user->id, $this->fbuid);
-
- if (!$result) {
- $this->serverError(_m('Error connecting user to Facebook.'));
- return;
+ common_debug(
+ sprintf(
+ 'Found a legit user to connect to Facebook: %s (%d)',
+ $user->nickname,
+ $user->id
+ ),
+ __FILE__
+ );
}
- common_debug('Facebook Connnect Plugin - ' .
- "Connected Facebook user $this->fbuid to local user $user->id");
+ $this->tryLinkUser($user);
common_set_user($user);
common_real_login(true);
function connectUser()
{
$user = common_current_user();
+ $this->tryLinkUser($user);
+ common_redirect(common_local_url('facebookfinishlogin'), 303);
+ }
+ function tryLinkUser($user)
+ {
$result = $this->flinkUser($user->id, $this->fbuid);
if (empty($result)) {
common_debug(
sprintf(
- 'Connected Facebook user %s to local user %d',
+ 'Connected Facebook user %s (fbuid %d) to local user %s (%d)',
+ $this->fbuser['name'],
$this->fbuid,
+ $user->nickname,
$user->id
),
__FILE__
);
-
- common_redirect(common_local_url('facebookfinishlogin'), 303);
}
function tryLogin()
$flink->user_id = $user_id;
$flink->foreign_id = $fbuid;
$flink->service = FACEBOOK_SERVICE;
-
+
// Pull the access token from the Facebook cookies
$flink->credentials = $this->facebook->getAccessToken();
// Try the full name
- $fullname = trim($this->fbuser['firstname'] .
- ' ' . $this->fbuser['lastname']);
+ $fullname = trim($this->fbuser['first_name'] .
+ ' ' . $this->fbuser['last_name']);
if (!empty($fullname)) {
$fullname = $this->nicknamize($fullname);
return strtolower($str);
}
- function isNewNickname($str)
- {
- if (!Validate::string($str, array('min_length' => 1,
- 'max_length' => 64,
- 'format' => NICKNAME_FMT))) {
+ /*
+ * Is the desired nickname already taken?
+ *
+ * @return boolean result
+ */
+ function isNewNickname($str)
+ {
+ if (
+ !Validate::string(
+ $str,
+ array(
+ 'min_length' => 1,
+ 'max_length' => 64,
+ 'format' => NICKNAME_FMT
+ )
+ )
+ ) {
return false;
}
+
if (!User::allowed_nickname($str)) {
return false;
}
+
if (User::staticGet('nickname', $str)) {
return false;
}
+
return true;
}
+ /*
+ * Do we already have a user record with this email?
+ * (emails have to be unique but they can change)
+ *
+ * @param string $email the email address to check
+ *
+ * @return boolean result
+ */
+ function isNewEmail($email)
+ {
+ // we shouldn't have to validate the format
+ $result = User::staticGet('email', $email);
+
+ if (empty($result)) {
+ common_debug("XXXXXXXXXXXXXXXXXX We've never seen this email before!!!");
+ return true;
+ }
+ common_debug("XXXXXXXXXXXXXXXXXX dupe email address!!!!");
+
+ return false;
+ }
+
}
$attrs = array(
'src' => common_path(
- 'plugins/FacebookSSO/images/login-button.png',
+ 'plugins/FacebookBridge/images/login-button.png',
true
),
'alt' => 'Login with Facebook',
--- /dev/null
+<?php
+/**
+ * Data class for storing notice-to-Facebook-item mappings
+ *
+ * PHP version 5
+ *
+ * @category Data
+ * @package StatusNet
+ * @author Zach Copley <zach@status.net>
+ * @license http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
+ * @link http://status.net/
+ *
+ * StatusNet - the distributed open-source microblogging tool
+ * Copyright (C) 2010, StatusNet, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+if (!defined('STATUSNET')) {
+ exit(1);
+}
+
+require_once INSTALLDIR . '/classes/Memcached_DataObject.php';
+
+/**
+ * Data class for mapping notices to Facebook stream items
+ *
+ * Note that notice_id is unique only within a single database; if you
+ * want to share this data for some reason, get the notice's URI and use
+ * that instead, since it's universally unique.
+ *
+ * @category Action
+ * @package StatusNet
+ * @author Zach Copley <zach@status.net>
+ * @license http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
+ * @link http://status.net/
+ *
+ * @see DB_DataObject
+ */
+
+class Notice_to_item extends Memcached_DataObject
+{
+ public $__table = 'notice_to_item'; // table name
+ public $notice_id; // int(4) primary_key not_null
+ public $item_id; // varchar(255) not null
+ public $created; // datetime
+
+ /**
+ * Get an instance by key
+ *
+ * This is a utility method to get a single instance with a given key value.
+ *
+ * @param string $k Key to use to lookup
+ * @param mixed $v Value to lookup
+ *
+ * @return Notice_to_item object found, or null for no hits
+ *
+ */
+
+ function staticGet($k, $v=null)
+ {
+ return Memcached_DataObject::staticGet('Notice_to_item', $k, $v);
+ }
+
+ /**
+ * return table definition for DB_DataObject
+ *
+ * DB_DataObject needs to know something about the table to manipulate
+ * instances. This method provides all the DB_DataObject needs to know.
+ *
+ * @return array array of column definitions
+ */
+
+ function table()
+ {
+ return array(
+ 'notice_id' => DB_DATAOBJECT_INT + DB_DATAOBJECT_NOTNULL,
+ 'item_id' => DB_DATAOBJECT_STR + DB_DATAOBJECT_NOTNULL,
+ 'created' => DB_DATAOBJECT_STR + DB_DATAOBJECT_DATE + DB_DATAOBJECT_TIME + DB_DATAOBJECT_NOTNULL
+ );
+ }
+
+ static function schemaDef()
+ {
+ return array(
+ new ColumnDef('notice_id', 'integer', null, false, 'PRI'),
+ new ColumnDef('item_id', 'varchar', 255, false, 'UNI'),
+ new ColumnDef('created', 'datetime', null, false)
+ );
+ }
+
+ /**
+ * return key definitions for DB_DataObject
+ *
+ * DB_DataObject needs to know about keys that the table has, since it
+ * won't appear in StatusNet's own keys list. In most cases, this will
+ * simply reference your keyTypes() function.
+ *
+ * @return array list of key field names
+ */
+
+ function keys()
+ {
+ return array_keys($this->keyTypes());
+ }
+
+ /**
+ * return key definitions for Memcached_DataObject
+ *
+ * Our caching system uses the same key definitions, but uses a different
+ * method to get them. This key information is used to store and clear
+ * cached data, so be sure to list any key that will be used for static
+ * lookups.
+ *
+ * @return array associative array of key definitions, field name to type:
+ * 'K' for primary key: for compound keys, add an entry for each component;
+ * 'U' for unique keys: compound keys are not well supported here.
+ */
+
+ function keyTypes()
+ {
+ return array('notice_id' => 'K', 'item_id' => 'U');
+ }
+
+ /**
+ * Magic formula for non-autoincrementing integer primary keys
+ *
+ * If a table has a single integer column as its primary key, DB_DataObject
+ * assumes that the column is auto-incrementing and makes a sequence table
+ * to do this incrementation. Since we don't need this for our class, we
+ * overload this method and return the magic formula that DB_DataObject needs.
+ *
+ * @return array magic three-false array that stops auto-incrementing.
+ */
+
+ function sequenceKey()
+ {
+ return array(false, false, false);
+ }
+
+ /**
+ * Save a mapping between a notice and a Facebook item
+ *
+ * @param integer $notice_id ID of the notice in StatusNet
+ * @param integer $item_id ID of the stream item on Facebook
+ *
+ * @return Notice_to_item new object for this value
+ */
+
+ static function saveNew($notice_id, $item_id)
+ {
+ $n2i = Notice_to_item::staticGet('notice_id', $notice_id);
+
+ if (!empty($n2i)) {
+ return $n2i;
+ }
+
+ $n2i = Notice_to_item::staticGet('item_id', $item_id);
+
+ if (!empty($n2i)) {
+ return $n2i;
+ }
+
+ common_debug(
+ "Mapping notice {$notice_id} to Facebook item {$item_id}",
+ __FILE__
+ );
+
+ $n2i = new Notice_to_item();
+
+ $n2i->notice_id = $notice_id;
+ $n2i->item_id = $item_id;
+ $n2i->created = common_sql_now();
+
+ $n2i->insert();
+
+ return $n2i;
+ }
+}
if ($this->isFacebookBound()) {
common_debug("notice is facebook bound", __FILE__);
if (empty($this->flink->credentials)) {
- $this->sendOldRest();
+ return $this->sendOldRest();
} else {
// Otherwise we most likely have an access token
- $this->sendGraph();
+ return $this->sendGraph();
}
} else {
$params = array(
'access_token' => $this->flink->credentials,
+ // XXX: Need to worrry about length of the message?
'message' => $this->notice->content
);
if (!empty($attachments)) {
- // We can only send one attachment with the Graph API
+ // We can only send one attachment with the Graph API :(
$first = array_shift($attachments);
sprintf('/%s/feed', $fbuid), 'post', $params
);
+ // Save a mapping
+ Notice_to_item::saveNew($this->notice->id, $result['id']);
+
+ common_log(
+ LOG_INFO,
+ sprintf(
+ "Posted notice %d as a stream item for %s (%d), fbuid %s",
+ $this->notice->id,
+ $this->user->nickname,
+ $this->user->id,
+ $fbuid
+ ),
+ __FILE__
+ );
+
} catch (FacebookApiException $e) {
return $this->handleFacebookError($e);
}
$result = $this->facebook->api(
array(
'method' => 'users.setStatus',
- 'status' => $this->notice->content,
+ 'status' => $this->formatMessage(),
'status_includes_verb' => true,
'uid' => $fbuid
)
);
- common_log(
- LOG_INFO,
- sprintf(
- "Posted notice %s as a status update for %s (%d), fbuid %s",
+ if ($result == 1) { // 1 is success
+
+ common_log(
+ LOG_INFO,
+ sprintf(
+ "Posted notice %s as a status update for %s (%d), fbuid %s",
+ $this->notice->id,
+ $this->user->nickname,
+ $this->user->id,
+ $fbuid
+ ),
+ __FILE__
+ );
+
+ // There is no item ID returned for status update so we can't
+ // save a Notice_to_item mapping
+
+ } else {
+
+ $msg = sprintf(
+ "Error posting notice %s as a status update for %s (%d), fbuid %s - error code: %s",
$this->notice->id,
$this->user->nickname,
$this->user->id,
- $fbuid
- ),
- __FILE__
- );
+ $fbuid,
+ $result // will contain 0, or an error
+ );
+ throw new FacebookApiException($msg, $result);
+ }
}
/*
$result = $this->facebook->api(
array(
'method' => 'stream.publish',
- 'message' => $this->notice->content,
+ 'message' => $this->formatMessage(),
'attachment' => $fbattachment,
'uid' => $fbuid
)
);
- common_log(
- LOG_INFO,
- sprintf(
- 'Posted notice %d as a %s for %s (%d), fbuid %s',
+ if (!empty($result)) { // result will contain the item ID
+
+ // Save a mapping
+ Notice_to_item::saveNew($this->notice->id, $result);
+
+ common_log(
+ LOG_INFO,
+ sprintf(
+ 'Posted notice %d as a %s for %s (%d), fbuid %s',
+ $this->notice->id,
+ empty($fbattachment) ? 'stream item' : 'stream item with attachment',
+ $this->user->nickname,
+ $this->user->id,
+ $fbuid
+ ),
+ __FILE__
+ );
+
+ } else {
+
+ $msg = sprintf(
+ 'Could not post notice %d as a %s for %s (%d), fbuid %s - error code: %s',
$this->notice->id,
empty($fbattachment) ? 'stream item' : 'stream item with attachment',
$this->user->nickname,
$this->user->id,
+ $result, // result will contain an error code
$fbuid
- ),
- __FILE__
- );
+ );
+
+ throw new FacebookApiException($msg, $result);
+ }
+ }
+ /*
+ * Format the text message of a stream item so it's appropriate for
+ * sending to Facebook. If the notice is too long, truncate it, and
+ * add a linkback to the original notice at the end.
+ *
+ * @return String $txt the formated message
+ */
+ function formatMessage()
+ {
+ // Start with the plaintext source of this notice...
+ $txt = $this->notice->content;
+
+ // Facebook has a 420-char hardcoded max.
+ if (mb_strlen($statustxt) > 420) {
+ $noticeUrl = common_shorten_url($this->notice->uri);
+ $urlLen = mb_strlen($noticeUrl);
+ $txt = mb_substr($statustxt, 0, 420 - ($urlLen + 3)) . ' … ' . $noticeUrl;
+ }
+
+ return $txt;
}
/*
return mail_to_user($this->user, $subject, $body);
}
+ /*
+ * Check to see if we have a mapping to a copy of this notice
+ * on Facebook
+ *
+ * @param Notice $notice the notice to check
+ *
+ * @return mixed null if it can't find one, or the id of the Facebook
+ * stream item
+ */
+ static function facebookStatusId($notice)
+ {
+ $n2i = Notice_to_item::staticGet('notice_id', $notice->id);
+
+ if (empty($n2i)) {
+ return null;
+ } else {
+ return $n2i->item_id;
+ }
+ }
+
+ /*
+ * Save a Foreign_user record of a Facebook user
+ *
+ * @param object $fbuser a Facebook Graph API user obj
+ * See: http://developers.facebook.com/docs/reference/api/user
+ * @return mixed $result Id or key
+ *
+ */
+ static function addFacebookUser($fbuser)
+ {
+ // remove any existing, possibly outdated, record
+ $luser = Foreign_user::getForeignUser($fbuser['id'], FACEBOOK_SERVICE);
+
+ if (!empty($luser)) {
+
+ $result = $luser->delete();
+
+ if ($result != false) {
+ common_log(
+ LOG_INFO,
+ sprintf(
+ 'Removed old Facebook user: %s, fbuid %d',
+ $fbuid['name'],
+ $fbuid['id']
+ ),
+ __FILE__
+ );
+ }
+ }
+
+ $fuser = new Foreign_user();
+
+ $fuser->nickname = $fbuser['name'];
+ $fuser->uri = $fbuser['link'];
+ $fuser->id = $fbuser['id'];
+ $fuser->service = FACEBOOK_SERVICE;
+ $fuser->created = common_sql_now();
+
+ $result = $fuser->insert();
+
+ if (empty($result)) {
+ common_log(
+ LOG_WARNING,
+ sprintf(
+ 'Failed to add new Facebook user: %s, fbuid %d',
+ $fbuser['name'],
+ $fbuser['id']
+ ),
+ __FILE__
+ );
+
+ common_log_db_error($fuser, 'INSERT', __FILE__);
+ } else {
+ common_log(
+ LOG_INFO,
+ sprintf(
+ 'Added new Facebook user: %s, fbuid %d',
+ $fbuser['name'],
+ $fbuser['id']
+ ),
+ __FILE__
+ );
+ }
+
+ return $result;
+ }
+
+ /*
+ * Remove an item from a Facebook user's feed if we have a mapping
+ * for it.
+ */
+ function streamRemove()
+ {
+ $n2i = Notice_to_item::staticGet('notice_id', $this->notice->id);
+
+ if (!empty($this->flink) && !empty($n2i)) {
+
+ $result = $this->facebook->api(
+ array(
+ 'method' => 'stream.remove',
+ 'post_id' => $n2i->item_id,
+ 'uid' => $this->flink->foreign_id
+ )
+ );
+
+ if (!empty($result) && result == true) {
+
+ common_log(
+ LOG_INFO,
+ sprintf(
+ 'Deleted Facebook item: %s for %s (%d), fbuid %d',
+ $n2i->item_id,
+ $this->user->nickname,
+ $this->user->id,
+ $this->flink->foreign_id
+ ),
+ __FILE__
+ );
+
+ $n2i->delete();
+
+ } else {
+
+ common_log(
+ LOG_WARNING,
+ sprintf(
+ 'Could not deleted Facebook item: %s for %s (%d), fbuid %d',
+ $n2i->item_id,
+ $this->user->nickname,
+ $this->user->id,
+ $this->flink->foreign_id
+ ),
+ __FILE__
+ );
+ }
+ }
+ }
+
+ /*
+ * Like an item in a Facebook user's feed if we have a mapping
+ * for it.
+ */
+ function like()
+ {
+ $n2i = Notice_to_item::staticGet('notice_id', $this->notice->id);
+
+ if (!empty($this->flink) && !empty($n2i)) {
+
+ $result = $this->facebook->api(
+ array(
+ 'method' => 'stream.addlike',
+ 'post_id' => $n2i->item_id,
+ 'uid' => $this->flink->foreign_id
+ )
+ );
+
+ if (!empty($result) && result == true) {
+
+ common_log(
+ LOG_INFO,
+ sprintf(
+ 'Added like for item: %s for %s (%d), fbuid %d',
+ $n2i->item_id,
+ $this->user->nickname,
+ $this->user->id,
+ $this->flink->foreign_id
+ ),
+ __FILE__
+ );
+
+ } else {
+
+ common_log(
+ LOG_WARNING,
+ sprintf(
+ 'Could not like Facebook item: %s for %s (%d), fbuid %d',
+ $n2i->item_id,
+ $this->user->nickname,
+ $this->user->id,
+ $this->flink->foreign_id
+ ),
+ __FILE__
+ );
+ }
+ }
+ }
+
+ /*
+ * Unlike an item in a Facebook user's feed if we have a mapping
+ * for it.
+ */
+ function unLike()
+ {
+ $n2i = Notice_to_item::staticGet('notice_id', $this->notice->id);
+
+ if (!empty($this->flink) && !empty($n2i)) {
+
+ $result = $this->facebook->api(
+ array(
+ 'method' => 'stream.removeLike',
+ 'post_id' => $n2i->item_id,
+ 'uid' => $this->flink->foreign_id
+ )
+ );
+
+ if (!empty($result) && result == true) {
+
+ common_log(
+ LOG_INFO,
+ sprintf(
+ 'Removed like for item: %s for %s (%d), fbuid %d',
+ $n2i->item_id,
+ $this->user->nickname,
+ $this->user->id,
+ $this->flink->foreign_id
+ ),
+ __FILE__
+ );
+
+ } else {
+
+ common_log(
+ LOG_WARNING,
+ sprintf(
+ 'Could not remove like for Facebook item: %s for %s (%d), fbuid %d',
+ $n2i->item_id,
+ $this->user->nickname,
+ $this->user->id,
+ $this->flink->foreign_id
+ ),
+ __FILE__
+ );
+ }
+ }
+ }
+
}