Permalink
Fetching contributors…
Cannot retrieve contributors at this time
executable file 802 lines (717 sloc) 20.9 KB
<?php
/**
* @package Social
* @subpackage services
*/
abstract class Social_Service {
/**
* @var string service key
*/
protected $_key = '';
/**
* @var array collection of account objects
*/
protected $_accounts = array();
/**
* Instantiates the
*
* @param array $accounts
*/
public function __construct(array $accounts = array()) {
$this->accounts($accounts);
}
/**
* Returns the service key.
*
* @return string
*/
public function key() {
return $this->_key;
}
/**
* Gets the title for the service.
*
* @return string
*/
public function title() {
return ucwords(str_replace('_', ' ', $this->_key));
}
/**
* The max length a post can be when broadcasted.
*
* @return int
*/
public function max_broadcast_length() {
return 140; // default to Twitter length
}
/**
* Builds the authorize URL for the service.
*
* @return string
*/
public function authorize_url() {
global $post;
$params = '?social_controller=auth&social_action=authorize&key='.$this->_key;
if (is_admin()) {
$url = (defined('IS_PROFILE_PAGE') ? 'profile.php' : 'options-general.php');
$url = admin_url($url.$params);
}
else {
$url = home_url('index.php'.$params.'&post_id='.$post->ID);
}
return $url;
}
/**
* Returns the disconnect URL.
*
* @static
*
* @param object $account
* @param bool $is_admin
* @param string $before
* @param string $after
*
* @return string
*/
public function disconnect_url($account, $is_admin = false, $before = '', $after = '') {
$params = array(
'social_controller' => 'auth',
'social_action' => 'disconnect',
'id' => $account->id(),
'service' => $this->_key
);
if ($is_admin) {
$personal = defined('IS_PROFILE_PAGE');
$url = Social::settings_url($params, $personal);
}
else {
$params['redirect_to'] = (isset($_GET['redirect_to']) ? $_GET['redirect_to'] : $_SERVER['REQUEST_URI']);
foreach ($params as $key => $value) {
$params[$key] = urlencode($value);
}
$url = add_query_arg($params, home_url());
}
return $url;
}
/**
* Returns the disconnect link.
*
* @static
*
* @param object $account
* @param bool $is_admin
* @param string $before
* @param string $after
*
* @return string
*/
public function disconnect_link($account, $is_admin = false, $before = '', $after = '') {
$url = $this->disconnect_url($account, $is_admin, $before, $after);
if ($is_admin) {
$text = '<span title="'.__('Disconnect', 'social').'" class="social-disconnect">'.__('Disconnect', 'social').'</span>';
}
else {
$text = __('Disconnect', 'social');
}
return sprintf('%s<a href="%s">%s</a>%s', $before, esc_url($url), $text, $after);
}
/**
* Creates a WordPress user with the passed in account.
*
* @param Social_Service_Account $account
* @param string $nonce
* @return int|bool
*/
public function create_user($account, $nonce = null) {
$username = $account->username();
$username = sanitize_user($username, true);
if (!empty($username)) {
$user = get_user_by('login', $this->_key.'_'.$username);
if ($user === false) {
$id = wp_create_user($this->_key.'_'.$username, wp_generate_password(20, false), $this->_key.'.'.$username.'@example.com');
if (is_wp_error($id)) {
Social::log('Failed to create/find user with username of :username.', array(
'username' => $username,
));
return false;
}
$role = '';
if (get_option('users_can_register') == '1') {
$role = get_option('default_role');
}
else {
// Set commenter flag
update_user_meta($id, 'social_commenter', 'true');
}
$user = new WP_User($id);
$user->set_role($role);
$user->show_admin_bar_front = 'false';
wp_update_user(get_object_vars($user));
}
else {
$id = $user->ID;
}
// Set the nonce
if ($nonce !== null) {
wp_set_current_user($id);
update_user_meta($id, 'social_commenter', 'true');
update_user_meta($id, 'social_auth_nonce_'.$nonce, 'true');
}
Social::log('Created/found user :username. (#:id)', array(
'username' => $username,
'id' => $id,
));
return $id;
}
Social::log('Failed to create/find user with username of :username.', array(
'username' => $username,
));
return false;
}
/**
* Saves the accounts on the service.
*
* @param bool $personal personal account?
* @return void
*/
public function save($personal = false) {
// Flush the cache
wp_cache_delete('services', 'social');
$accounts = array();
if ($personal) {
foreach ($this->_accounts AS $account) {
if ($account->personal()) {
$accounts[$account->id()] = $account->as_object();
}
$account->universal(false);
}
$current = get_user_meta(get_current_user_id(), 'social_accounts', true);
Social::log('Current accounts: :accounts', array(
'accounts' => print_r($current, true)
));
if (count($accounts)) {
$current[$this->_key] = $accounts;
}
else if (isset($current[$this->_key])) {
unset($current[$this->_key]);
}
if (count($current)) {
Social::log('New accounts: :accounts', array(
'accounts' => print_r($current, true)
));
update_user_meta(get_current_user_id(), 'social_accounts', $current);
}
else {
Social::log('No accounts, deleting user meta for user #:user_id social_accounts', array(
'user_id' => get_current_user_id(),
));
delete_user_meta(get_current_user_id(), 'social_accounts');
}
}
else {
foreach ($this->_accounts AS $account) {
if ($account->universal()) {
$accounts[$account->id()] = $account->as_object();
}
$account->personal(false);
}
$current = Social::option('accounts');
if ($current == null) {
$current = array();
}
Social::log('Current accounts: :accounts', array(
'accounts' => print_r($current, true)
));
if (count($accounts)) {
$current[$this->_key] = $accounts;
}
else if (isset($current[$this->_key])) {
unset($current[$this->_key]);
}
if (count($current)) {
Social::log('New accounts: :accounts', array(
'accounts' => print_r($current, true)
));
Social::option('accounts', $current);
}
else {
Social::log('No accounts, deleting option social_accounts');
delete_option('social_accounts');
}
}
}
/**
* Checks to see if the account exists on the object.
*
* @param int $id account id
* @return bool
*/
public function account_exists($id) {
return isset($this->_accounts[$id]);
}
/**
* Gets the requested account.
*
* @param int|Social_Service_Account $account account id/object
* @return Social_Service_Account|Social_Service|bool
*/
public function account($account) {
if ($account instanceof Social_Service_Account) {
$this->_accounts[$account->id()] = $account;
return $this;
}
if ($this->account_exists($account)) {
return $this->_accounts[$account];
}
return false;
}
/**
* Gets the specified "api" account.
*
* @return Social_Service_Account|Social_Service|bool
*/
public function api_account() {
if ($social_api_accounts = Social::option('social_api_accounts')) {
if (isset($social_api_accounts[$this->key()])) {
return $this->account($social_api_accounts[$this->key()]);
}
}
return null;
}
/**
* Acts as a getter and setter for service accounts.
*
* @param array $accounts accounts to add to the service
* @return array|Social_Service
*/
public function accounts(array $accounts = null) {
if ($accounts === null) {
return $this->_accounts;
}
$class = 'Social_Service_'.$this->_key.'_Account';
foreach ($accounts as $account) {
$account = new $class($account);
if (!$this->account_exists($account->id())) {
$this->_accounts[$account->id()] = $account;
}
}
return $this;
}
/**
* Removes an account from the service.
*
* @abstract
* @param int|Social_Service_Account $account
* @return Social_Service
*/
public function remove_account($account) {
Social::log('Starting account removal...');
if (is_int($account)) {
$account = $this->account($account);
}
Social::log('Accounts: :accounts', array(
'accounts' => print_r($this->_accounts, true),
));
if ($account !== false) {
Social::log('Removing...');
unset($this->_accounts[$account->id()]);
}
Social::log('Accounts: :accounts', array(
'accounts' => print_r($this->_accounts, true)
));
return $this;
}
/**
* Removes all accounts from the service.
*
* @abstract
* @return Social_Service
*/
public function clear_accounts() {
$this->_accounts = array();
return $this;
}
/**
* Formats the broadcast content.
*
* @param object $post
* @param string $format
* @return string
*/
public function format_content($post, $format) {
// Filter the format
$format = apply_filters('social_broadcast_format', $format, $post, $this);
$_format = $format;
$available = $this->max_broadcast_length();
foreach (Social::broadcast_tokens() as $token => $description) {
$_format = str_replace($token, '', $_format);
}
$available = $available - social_strlen($_format);
$_format = explode(' ', $format);
foreach (Social::broadcast_tokens() as $token => $description) {
$content = '';
switch ($token) {
case '{url}':
$url = social_get_shortlink($post->ID);
if (empty($url)) {
$url = home_url('?p='.$post->ID);
}
$url = apply_filters('social_broadcast_permalink', $url, $post, $this);
$content = esc_url_raw($url);
break;
case '{title}':
$content = htmlspecialchars_decode($post->post_title);
break;
case '{content}':
$content = do_shortcode($post->post_content);
$content = htmlspecialchars_decode(strip_tags($content));
$content = preg_replace('/[^\S\n]+/', ' ', $content);
break;
case '{author}':
$user = get_userdata($post->post_author);
$content = htmlspecialchars_decode($user->display_name);
break;
case '{date}':
$content = get_date_from_gmt($post->post_date_gmt);
break;
}
if (social_strlen($content) > $available) {
if (in_array($token, array('{date}', '{author}'))
) {
$content = '';
}
else {
$content = social_substr($content, 0, ($available - 3)).'...';
}
}
// Filter the content
$content = apply_filters('social_format_content', $content, $post, $format, $this);
foreach ($_format as $haystack) {
if (strpos($haystack, $token) !== false and $available > 0) {
$haystack = str_replace($token, $content, $haystack);
$available = $available - social_strlen($haystack);
$format = str_replace($token, $content, $format);
break;
}
}
}
// Filter the content
$format = apply_filters('social_broadcast_content_formatted', $format, $post, $this);
return $format;
}
/**
* Formats a comment before it's broadcasted.
*
* @param WP_Comment $comment
* @param array $format
* @return string
*/
public function format_comment_content($comment, $format) {
// Filter the format
$format = apply_filters('social_comment_broadcast_format', $format, $comment, $this);
$_format = $format;
$available = $this->max_broadcast_length();
$used_tokens = array();
// Gather used tokens and subtract remaining characters from available length
foreach (Social::comment_broadcast_tokens() as $token => $description) {
$replaced = 0;
$_format = str_replace($token, '', $_format, $replaced);
if ($replaced) {
$used_tokens[$token] = '';
}
}
$available = $available - social_strlen($_format);
// Prep token replacement content
foreach ($used_tokens as $token => $content) {
switch ($token) {
case '{url}':
$url = social_get_shortlink($comment->comment_post_ID);
if (empty($url)) {
$url = home_url('?p='.$comment->comment_post_ID);
}
$url .= '#comment-'.$comment->comment_ID;
$url = apply_filters('social_comment_broadcast_permalink', $url, $comment, $this);
$used_tokens[$token] = esc_url($url);
break;
case '{content}':
$used_tokens[$token] = strip_tags($comment->comment_content);
$used_tokens[$token] = str_replace('&nbsp;', '', $used_tokens[$token]);
break;
}
}
// if {url} is used, pre-allocate its length
if (isset($used_tokens['{url}'])) {
$available = $available - social_strlen($used_tokens['{url}']);
}
$used_tokens['{content}'] = apply_filters('social_format_comment_content', $used_tokens['{content}'], $comment, $format, $this);
// Truncate content to size limit
if (social_strlen($used_tokens['{content}']) > $available) {
$used_tokens['{content}'] = social_substr($used_tokens['{content}'], 0, ($available - 3)).'...';
}
foreach ($used_tokens as $token => $replacement) {
if (strpos($format, $token) !== false) {
$format = str_replace($token, $replacement, $format);
}
}
$format = apply_filters('social_comment_broadcast_content_formatted', $format, $comment, $this);
return $format;
}
/**
* Handles the requests to the proxy.
*
* @param Social_Service_Account|int $account
* @param string $api
* @param array $args
* @param string $method
* @return Social_Response|bool
*/
public function request($account, $api, array $args = array(), $method = 'GET') {
if (!is_object($account)) {
$account = $this->account($account);
}
if ($account !== false) {
$proxy = apply_filters('social_api_proxy', Social::$api_url.$this->_key, $this->_key);
$api = apply_filters('social_api_endpoint', $api, $this->_key);
$method = apply_filters('social_api_endpoint_method', $method, $this->_key);
$args = apply_filters('social_api_endpoint_args', $args, $this->_key);
$request = wp_remote_post($proxy, array(
'timeout' => 60, // default of 5 seconds if not set here
'sslverify' => false,
'body' => array(
'api' => $api,
'method' => $method,
'public_key' => $account->public_key(),
'hash' => sha1($account->public_key().$account->private_key()),
'params' => json_encode($args)
)
));
if (!is_wp_error($request)) {
$request['body'] = apply_filters('social_response_body', $request['body'], $this->_key);
if (is_string($request['body'])) {
// slashes are normalized (always added) by WordPress
$request['body'] = json_decode(stripslashes_deep($request['body']));
}
return Social_Response::factory($this, $request, $account);
}
else {
Social::log('Service::request() error: '.$request->get_error_message());
}
}
return false;
}
/**
* Show full comment?
*
* @param string $type
* @return bool
*/
public function show_full_comment($type) {
return (!in_array($type, self::comment_types_meta()));
}
/**
* Disconnects an account from the user's account.
*
* @param int $id
* @return void
*/
public function disconnect($id) {
if (!is_admin() or defined('IS_PROFILE_PAGE')) {
$accounts = get_user_meta(get_current_user_id(), 'social_accounts', true);
if (isset($accounts[$this->_key][$id])) {
if (defined('IS_PROFILE_PAGE')) {
unset($accounts[$this->_key][$id]);
}
else {
unset($accounts[$this->_key][$id]->user);
}
if (!count($accounts[$this->_key])) {
unset($accounts[$this->_key]);
}
update_user_meta(get_current_user_id(), 'social_accounts', $accounts);
}
}
else {
$accounts = Social::option('accounts');
if (isset($accounts[$this->_key][$id])) {
unset($accounts[$this->_key][$id]);
if (!count($accounts[$this->_key])) {
unset($accounts[$this->_key]);
}
Social::option('accounts', $accounts);
}
}
do_action('social_account_disconnected', $this->_key, $id);
}
/**
* Loads all of the accounts to use for aggregation.
*
* Format of returned data:
*
* $accounts = array(
* 'twitter' => array(
* '1234567890' => Social_Service_Twitter_Account,
* '0987654321' => Social_Service_Twitter_Account,
* // ... Other connected accounts
* ),
* 'facebook' => array(
* '1234567890' => Social_Service_Facebook_Account,
* '0987654321' => Social_Service_Facebook_Account,
* // ... Other connected accounts
* ),
* // ... Other registered services
* );
*
* @param object $post
* @return array
*/
protected function get_aggregation_accounts($post) {
$accounts = array();
foreach ($this->accounts() as $account) {
if (!isset($accounts[$this->_key])) {
$accounts[$this->_key] = array();
}
if (!isset($accounts[$this->_key][$account->id()])) {
$accounts[$this->_key][$account->id()] = $account;
}
}
return $accounts;
}
/**
* Checks to see if the result ID is the original broadcasted ID.
*
* @param WP_Post|int $post
* @param int $result_id
* @return bool
*/
public function is_original_broadcast($post, $result_id) {
if (!is_object($post)) {
$broadcasted_ids = get_post_meta($post, '_social_broadcasted_ids', true);
if (empty($broadcasted_ids)) {
$broadcasted_ids = array();
}
$post = (object) array(
'broadcasted_ids' => $broadcasted_ids,
);
}
if (isset($post->broadcasted_ids[$this->_key])) {
foreach ($post->broadcasted_ids[$this->_key] as $account_id => $broadcasted) {
if (isset($broadcasted[$result_id])) {
Social::log('This is the original broadcast. (:result_id)', array('result_id' => $result_id));
return true;
}
}
}
return false;
}
/**
* Checks to make sure the comment hasn't already been created.
*
* @param object $post
* @param int $result_id
* @return bool
*/
public function is_duplicate_comment($post, $result_id) {
global $wpdb;
$results = $wpdb->get_results($wpdb->prepare("
SELECT meta_value
FROM $wpdb->commentmeta AS cm, $wpdb->comments AS c
WHERE cm.comment_id = c.comment_ID
AND c.comment_post_id = %s
AND cm.meta_key = 'social_status_id'
", $post->ID));
foreach ($results as $result) {
if ($result->meta_value == $result_id) {
return true;
}
}
return false;
}
/**
* Builds the social item output.
*
* @param object $item social item being rendered
* @param int $count current display count
* @param array $avatar_size array containing the width and height attributes
* @return string
*/
public function social_item_output($item, $count, array $avatar_size = array()) {
$style = '';
if ($count >= 10) {
$style = ' style="display:none"';
}
$width = '24';
$height = '24';
if (isset($avatar_size['width'])) {
$width = $avatar_size['width'];
}
if (isset($avatar_size['height'])) {
$height = $avatar_size['height'];
}
$status_url = $this->status_url($item->comment_author, $item->social_status_id);
$title = apply_filters('social_item_output_title', $item->comment_author, $this->key());
$image_format = apply_filters('social_item_output_image_format', '<img src="%1$s" width="%2$s" height="%3$s" alt="%4$s" />');
$image = sprintf($image_format, esc_url($item->social_profile_image_url), esc_attr($width), esc_attr($height), esc_attr($title));
return sprintf('<a href="%s" title="%s"%s>%s</a>', esc_url($status_url), esc_attr($title), $style, $image);
}
/**
* Checks to see if the comment is allowed.
*
* [!!] Handles the exception for duplicate comments.
*
* @param array $commentdata
* @param int $result_id
* @param object $post
* @return array|bool
*/
public function allow_comment(array $commentdata, $result_id, &$post) {
try {
add_filter('wp_die_handler', array('Social', 'wp_die_handler'));
$commentdata['comment_approved'] = wp_allow_comment($commentdata);
remove_filter('wp_die_handler', array('Social', 'wp_die_handler'));
return $commentdata;
} catch (Exception $e) {
remove_filter('wp_die_handler', array('Social', 'wp_die_handler'));
if ($e->getMessage() == Social::$duplicate_comment_message) {
// Remove the aggregation ID from the stack
unset($post->results[$this->_key][$result_id]);
$aggregated_ids = array();
foreach ($post->aggregated_ids[$this->_key] as $id) {
if ($id != $result_id) {
$aggregated_ids[] = $id;
}
}
$post->aggregated_ids[$this->_key] = $aggregated_ids;
// Mark the result as ignored
Social_Aggregation_Log::instance($post->ID)->ignore($result_id);
}
}
return false;
}
/**
* Comment types for this service.
*
* @static
* @return array
*/
public static function comment_types() {
return array();
}
/**
* Comment types that are "meta" (not displayed in full).
*
* @static
* @return array
*/
public static function comment_types_meta() {
return array();
}
/**
* Any additional parameters that should be passed with a broadcast.
*
* @static
* @return array
*/
public function get_broadcast_extras($account_id, $post, $args = array()) {
return apply_filters($this->key().'_broadcast_extras', $args, $this, $account_id, $post);
}
} // End Social_Service