- Map notices to Facebook stream items
- rename plugin FacebookBridgePlugin - delete/like/unlike notices across the bridge
This commit is contained in:
parent
3c921f38de
commit
ca4c0a1601
@ -45,10 +45,9 @@ define("FACEBOOK_SERVICE", 2);
|
||||
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
|
||||
* @link http://status.net/
|
||||
*/
|
||||
class FacebookSSOPlugin extends Plugin
|
||||
class FacebookBridgePlugin 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
|
||||
@ -64,7 +63,6 @@ class FacebookSSOPlugin extends Plugin
|
||||
{
|
||||
$this->facebook = Facebookclient::getFacebook(
|
||||
$this->appId,
|
||||
$this->apikey,
|
||||
$this->secret
|
||||
);
|
||||
|
||||
@ -101,12 +99,32 @@ class FacebookSSOPlugin extends Plugin
|
||||
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?
|
||||
*/
|
||||
@ -436,6 +454,54 @@ ENDOFSCRIPT;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
*
|
||||
@ -447,7 +513,7 @@ ENDOFSCRIPT;
|
||||
'name' => 'Facebook Single-Sign-On',
|
||||
'version' => STATUSNET_VERSION,
|
||||
'author' => 'Craig Andrews, Zach Copley',
|
||||
'homepage' => 'http://status.net/wiki/Plugin:FacebookSSO',
|
||||
'homepage' => 'http://status.net/wiki/Plugin:FacebookBridge',
|
||||
'rawdescription' =>
|
||||
_m('A plugin for integrating StatusNet with Facebook.')
|
||||
);
|
@ -112,7 +112,7 @@ class FacebookdeauthorizeAction extends Action
|
||||
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,
|
||||
@ -135,8 +135,8 @@ class FacebookdeauthorizeAction extends Action
|
||||
);
|
||||
} 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -97,7 +97,7 @@ class FacebookfinishloginAction extends Action
|
||||
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);
|
||||
@ -121,48 +121,52 @@ class FacebookfinishloginAction extends Action
|
||||
} 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')) {
|
||||
// We has a valid Facebook session and the Facebook user has
|
||||
// agreed to the SN license, so create a new user
|
||||
$this->createNewUser();
|
||||
|
||||
if (!$this->boolean('license')) {
|
||||
$this->showForm(
|
||||
_m('You can\'t register if you don\'t agree to the license.'),
|
||||
$this->trimmed('newname')
|
||||
);
|
||||
return;
|
||||
}
|
||||
} 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')) {
|
||||
|
||||
$this->connectNewUser();
|
||||
|
||||
} else {
|
||||
|
||||
$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')
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -173,7 +177,7 @@ class FacebookfinishloginAction extends Action
|
||||
$this->element('div', array('class' => 'error'), $this->error);
|
||||
|
||||
} else {
|
||||
|
||||
|
||||
$this->element(
|
||||
'div', 'instructions',
|
||||
// TRANS: %s is the site name.
|
||||
@ -343,19 +347,23 @@ class FacebookfinishloginAction extends Action
|
||||
'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) {
|
||||
@ -363,6 +371,9 @@ class FacebookfinishloginAction extends Action
|
||||
return;
|
||||
}
|
||||
|
||||
// Add a Foreign_user record
|
||||
Facebookclient::addFacebookUser($this->fbuser);
|
||||
|
||||
$this->setAvatar($user);
|
||||
|
||||
common_set_user($user);
|
||||
@ -371,20 +382,16 @@ class FacebookfinishloginAction extends Action
|
||||
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);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -401,17 +408,19 @@ class FacebookfinishloginAction extends Action
|
||||
// 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),
|
||||
@ -430,17 +439,20 @@ class FacebookfinishloginAction extends Action
|
||||
|
||||
} 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__
|
||||
);
|
||||
@ -462,19 +474,17 @@ class FacebookfinishloginAction extends Action
|
||||
$user = User::staticGet('nickname', $nickname);
|
||||
|
||||
if (!empty($user)) {
|
||||
common_debug('Facebook Connect Plugin - ' .
|
||||
"Legit user to connect to Facebook: $nickname");
|
||||
common_debug(
|
||||
sprintf(
|
||||
'Found a legit user to connect to Facebook: %s (%d)',
|
||||
$user->nickname,
|
||||
$user->id
|
||||
),
|
||||
__FILE__
|
||||
);
|
||||
}
|
||||
|
||||
$result = $this->flinkUser($user->id, $this->fbuid);
|
||||
|
||||
if (!$result) {
|
||||
$this->serverError(_m('Error connecting user to Facebook.'));
|
||||
return;
|
||||
}
|
||||
|
||||
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);
|
||||
@ -485,7 +495,12 @@ class FacebookfinishloginAction extends Action
|
||||
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)) {
|
||||
@ -495,14 +510,14 @@ class FacebookfinishloginAction extends Action
|
||||
|
||||
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()
|
||||
@ -573,7 +588,7 @@ class FacebookfinishloginAction extends Action
|
||||
$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();
|
||||
|
||||
@ -595,8 +610,8 @@ class FacebookfinishloginAction extends Action
|
||||
|
||||
// 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);
|
||||
@ -617,20 +632,57 @@ class FacebookfinishloginAction extends Action
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -89,7 +89,7 @@ class FacebookloginAction extends Action
|
||||
|
||||
$attrs = array(
|
||||
'src' => common_path(
|
||||
'plugins/FacebookSSO/images/login-button.png',
|
||||
'plugins/FacebookBridge/images/login-button.png',
|
||||
true
|
||||
),
|
||||
'alt' => 'Login with Facebook',
|
||||
|
190
plugins/FacebookSSO/classes/Notice_to_item.php
Normal file
190
plugins/FacebookSSO/classes/Notice_to_item.php
Normal file
@ -0,0 +1,190 @@
|
||||
<?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;
|
||||
}
|
||||
}
|
@ -173,11 +173,11 @@ class Facebookclient
|
||||
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 {
|
||||
@ -213,6 +213,7 @@ class Facebookclient
|
||||
|
||||
$params = array(
|
||||
'access_token' => $this->flink->credentials,
|
||||
// XXX: Need to worrry about length of the message?
|
||||
'message' => $this->notice->content
|
||||
);
|
||||
|
||||
@ -220,7 +221,7 @@ class Facebookclient
|
||||
|
||||
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);
|
||||
|
||||
@ -240,6 +241,21 @@ class Facebookclient
|
||||
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);
|
||||
}
|
||||
@ -481,24 +497,42 @@ class Facebookclient
|
||||
$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);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
@ -524,25 +558,66 @@ class Facebookclient
|
||||
$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;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -708,4 +783,240 @@ BODY;
|
||||
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__
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user