web/wp-content/plugins/social/social-twitter.php
changeset 196 5e8dcbe22c24
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/web/wp-content/plugins/social/social-twitter.php	Tue Dec 04 18:43:10 2012 -0800
@@ -0,0 +1,387 @@
+<?php
+/**
+ * Twitter implementation for Social.
+ *
+ * @package    Social
+ * @subpackage plugins
+ */
+if (class_exists('Social') and !class_exists('Social_Twitter')) {
+
+final class Social_Twitter {
+
+	/**
+	 * Registers Twitter to Social.
+	 *
+	 * @static
+	 * @wp-filter  social_register_service
+	 *
+	 * @param  array  $services
+	 *
+	 * @return array
+	 */
+	public static function register_service(array $services) {
+		$services[] = 'twitter';
+		return $services;
+	}
+
+	/**
+	 * Adds to the avatar comment types array.
+	 *
+	 * @static
+	 * @param  array  $types
+	 * @return array
+	 */
+	public static function get_avatar_comment_types(array $types) {
+		return array_merge($types, Social_Service_Twitter::comment_types());
+	}
+
+	/**
+	 * Pre-processor to the comments.
+	 *
+	 * @wp-filter social_comments_array
+	 * @static
+	 * @param  array  $comments
+	 * @param  int    $post_id
+	 * @return array
+	 */
+	public static function comments_array(array $comments, $post_id) {
+		// pre-load the hashes for broadcasted tweets
+		$broadcasted_ids = get_post_meta($post_id, '_social_broadcasted_ids', true);
+		if (empty($broadcasted_ids)) {
+			$broadcasted_ids = array();
+		}
+		global $wpdb;
+
+		// we need comments to be keyed by ID, check for Tweet comments
+		$tweet_comments = $_comments = $comment_ids = array();
+		foreach ($comments as $key => $comment) {
+			if (is_object($comment)) {
+				$_comments['id_'.$comment->comment_ID] = $comment;
+				if (in_array($comment->comment_type, Social_Service_Twitter::comment_types())) {
+					$comment_ids[] = $comment->comment_ID;
+					$tweet_comments['id_'.$comment->comment_ID] = $comment;
+				}
+			}
+			else { // social items
+				$_comments[$key] = $comment;
+			}
+		}
+
+		// if no tweet comments, get out now
+		if (!count($tweet_comments)) {
+			return $comments;
+		}
+
+		// use our keyed array
+		$comments = $_comments;
+		unset($_comments);
+
+		$social_map = array(); // key = social id, value = comment_ID
+		$hash_map = array(); // key = hash, value = comment_ID
+		$broadcasted_social_ids = array();
+ 		$broadcast_retweets = array(); // array of comments
+
+		if (isset($broadcasted_ids['twitter'])) {
+			foreach ($broadcasted_ids['twitter'] as $account_id => $broadcasted) {
+				foreach ($broadcasted as $id => $data) {
+					$broadcasted_social_ids[] = $id;
+					// if we don't have a message saved for a tweet, try to get it so that we can use it next time
+					if (empty($data['message'])) {
+						$url = wp_nonce_url(home_url('index.php?social_controller=aggregation&social_action=retrieve_twitter_content&broadcasted_id='.$id.'&post_id='.$post_id), 'retrieve_twitter_content');
+						wp_remote_get(str_replace('&amp;', '&', $url), array(
+							'timeout' => 0.01,
+							'blocking' => false,
+						));
+					}
+					else {
+						// create a hash from the broadcast so we can match retweets to it
+						$hash = self::build_hash($data['message']);
+
+						// This is stored as broadcasted and not the ID so we can easily store broadcasted retweets
+						// instead of attaching retweets to non-existent comments.
+						$hash_map[$hash] = 'broadcasted';
+					}
+				}
+			}
+		}
+
+		// Load the comment meta
+		$results = $wpdb->get_results("
+			SELECT meta_key, meta_value, comment_id
+			  FROM $wpdb->commentmeta
+			 WHERE comment_id IN (".implode(',', $comment_ids).")
+			   AND (
+			       meta_key = 'social_in_reply_to_status_id'
+			    OR meta_key = 'social_status_id'
+			    OR meta_key = 'social_raw_data'
+			    OR meta_key = 'social_profile_image_url'
+			    OR meta_key = 'social_comment_type'
+			)
+		");
+
+		// Set up social data for twitter comments
+		foreach ($tweet_comments as $key => &$comment) {
+			$comment->social_items = array();
+
+			// Attach meta
+			foreach ($results as $result) {
+				if ($comment->comment_ID == $result->comment_id) {
+					switch ($result->meta_key) {
+						case 'social_raw_data':
+							$comment->social_raw_data = json_decode(base64_decode($result->meta_value));
+							break;
+						case 'social_status_id':
+							$social_map[$result->meta_value] = $result->comment_id;
+						default:
+							$comment->{$result->meta_key} = $result->meta_value;
+					}
+				}
+			}
+
+			// Attach hash
+			if (isset($comment->social_raw_data) and isset($comment->social_raw_data->text)) {
+				$text = trim($comment->social_raw_data->text);
+			}
+			else {
+				$text = trim($comment->comment_content);
+			}
+			$comment->social_hash = self::build_hash($text);
+
+			if (!isset($hash_map[$comment->social_hash])) {
+				$hash_map[$comment->social_hash] = $comment->comment_ID;
+			}
+		}
+
+		// merge data so that $comments has the data we've set up
+		$comments = array_merge($comments, $tweet_comments);
+
+		// set-up replies and retweets
+		foreach ($tweet_comments as $key => &$comment) {
+			if (is_object($comment)) {
+				// set reply/comment parent
+				if (!empty($comment->social_in_reply_to_status_id) and isset($social_map[$comment->social_in_reply_to_status_id])) {
+					$comments[$key]->comment_parent = $social_map[$comment->social_in_reply_to_status_id];
+				}
+
+				// set retweets
+				$rt_matched = false;
+				if (isset($comment->social_raw_data) and isset($comment->social_raw_data->retweeted_status)) {
+					// explicit match via API data
+					$rt_id = $comment->social_raw_data->retweeted_status->id_str;
+					if (in_array($rt_id, $broadcasted_social_ids)) {
+						$broadcast_retweets[] = $comment;
+						unset($comments[$key]);
+						$rt_matched = true;
+					}
+					else if (isset($social_map[$rt_id]) and isset($comments['id_'.$social_map[$rt_id]])) {
+						$comments['id_'.$social_map[$rt_id]]->social_items[$key] = $comment;
+						unset($comments[$key]);
+						$rt_matched = true;
+					}
+				}
+
+				if (!$rt_matched) {
+					// best guess via hashes
+					$hash_match = $hash_map[$comment->social_hash];
+					if ($hash_match != $comment->comment_ID) { // hash match to own tweet is expected, at minimum - set above
+						if ($hash_match == 'broadcasted') {
+							$broadcast_retweets[] = $comment;
+						}
+						else if (isset($comments['id_'.$hash_match])) {
+							$comments['id_'.$hash_match]->social_items[$key] = $comment;
+						}
+						else {
+							// Loop through the broadcasted retweets and see if this is a retweet of one of those.
+							foreach ($broadcast_retweets as $retweet) {
+								if ($retweet->comment_ID == $hash_match) {
+									$broadcast_retweets[] = $comment;
+									break;
+								}
+							}
+						}
+						unset($comments[$key]);
+					}
+				}
+			}
+		}
+
+		if (!isset($comments['social_items'])) {
+			$comments['social_items'] = array();
+		}
+
+		if (count($broadcast_retweets)) {
+			$comments['social_items']['twitter'] = $broadcast_retweets;
+		}
+
+		return $comments;
+	}
+
+
+	/**
+	 * Sets the raw data for the broadcasted post.
+	 *
+	 * @wp-filter social_broadcast_response
+	 * @static
+	 * @param  array                   $data
+	 * @param  Social_Service_Account  $account
+	 * @param  string                  $service_key
+	 * @param  int                     $post_id
+	 * @param  Social_Response         $response
+	 * @return array
+	 */
+	public static function social_save_broadcasted_ids_data(array $data, Social_Service_Account $account, $service_key, $post_id, Social_Response $response = null) {
+		if ($service_key == 'twitter') {
+			if (!empty($response)) {
+				$data['message'] = base64_encode(json_encode($response->body()->response));
+			}
+			$data['account'] = (object) array(
+				'user' => $account->as_object()->user
+			);
+		}
+
+		return $data;
+	}
+
+	/**
+	 * Strips extra retweet data before comparing.
+	 *
+	 * @static
+	 * @param  string  $text
+	 * @return string
+	 */
+	private static function build_hash($text) {
+		$text = explode(' ', $text);
+		$content = '';
+		foreach ($text as $_content) {
+			if (!empty($_content) and strpos($_content, 'http://') === false) {
+				if ($_content == 'RT' or preg_match('/@([\w_]+):/i', $_content)) {
+					continue;
+				}
+
+				$content .= $_content.' ';
+			}
+		}
+
+		return md5(trim($content));
+	}
+
+	/**
+	 * Checks for a retweet via twitter API data and user perception.
+	 *
+	 * @static
+	 * @param  stdClass  $comment
+	 * @return bool
+	 */
+	public static function is_retweet($comment = null, $tweet = null) {
+		$is_retweet = false;
+		if (!is_null($comment)) {
+			if (isset($comment->social_raw_data) and !empty($comment->social_raw_data->retweeted_status)) {
+				$is_retweet = true;
+			}
+			if (substr($comment->comment_content, 0, 4) == 'RT @') {
+				$is_retweet = true;
+			}
+		}
+		else if (!is_null($tweet)) {
+			if (!empty($tweet->retweeted_status)) {
+				$is_retweet = true;
+			}
+			if (substr($tweet->text, 0, 4) == 'RT @') {
+				$is_retweet = true;
+			}
+		}
+		return $is_retweet;
+	}
+
+	/**
+	 * Adds a retweet to the original broadcasted post social items stack.
+	 *
+	 * @static
+	 * @param  int    $comment_id
+	 * @param  array  $comments
+	 * @param  array  $social_items
+	 */
+	private static function add_to_social_items($comment_id, &$comments, &$social_items) {
+		$object = null;
+		$_comments = array();
+		foreach ($comments as $id => $comment) {
+			if (is_int($id)) {
+				if ($comment->comment_ID == $comment_id) {
+					$object = $comment;
+				}
+				else {
+					$_comments[] = $comment;
+				}
+			}
+			else {
+				if (isset($_comments[$id])) {
+					$_comments[$id] = array_merge($_comments[$id], $comment);
+				}
+				else {
+					$_comments[$id] = $comment;
+				}
+			}
+		}
+		$comments = $_comments;
+
+		if ($object !== null) {
+			if (!isset($social_items['twitter'])) {
+				$social_items['twitter'] = array();
+			}
+
+			$social_items['twitter'][$comment_id] = $object;
+		}
+	}
+
+	/**
+	 * Adds messaging to the title.
+	 *
+	 * @static
+	 * @param  string  $title
+	 * @param  string  $key
+	 * @return string
+	 */
+	public static function social_item_output_title($title, $key) {
+		if ($key == 'twitter') {
+			$title .= __(' retweeted this', 'social');
+		}
+
+		return $title;
+	}
+
+	/**
+	 * Add a "reply to" field to broadcast form.
+	 *
+	 * @static
+	 * @param  obj  $post
+	 * @param  obj  $service
+	 * @param  obj  $account
+	 * @return void
+	 */
+	public static function social_broadcast_form_item_edit($post, $service, $account) {
+		if ($service->key() != 'twitter') {
+			return;
+		}
+		$field_name = str_replace('_content', '_in_reply_to', $account['field_name_content']);
+?>
+<a href="#" class="tweet-reply-link"><?php _e('Send as a reply', 'social'); ?></a>
+<div class="tweet-reply-fields">
+	<label for="<?php echo esc_attr($field_name); ?>"><?php _e('URL of Tweet (to reply to)', 'social'); ?></label>
+	<input type="text" class="tweet-reply-field" name="<?php echo esc_attr($field_name); ?>" value="" id="<?php echo esc_attr($field_name); ?>" />
+</div>
+<?php
+	}
+
+} // End Social_Twitter
+
+define('SOCIAL_TWITTER_FILE', __FILE__);
+
+// Filters
+add_filter('social_register_service', array('Social_Twitter', 'register_service'));
+add_filter('get_avatar_comment_types', array('Social_Twitter', 'get_avatar_comment_types'));
+add_filter('social_comments_array', array('Social_Twitter', 'comments_array'), 10, 2);
+add_filter('social_save_broadcasted_ids_data', array('Social_Twitter', 'social_save_broadcasted_ids_data'), 10, 5);
+add_filter('social_item_output_title', array('Social_Twitter', 'social_item_output_title'), 10, 2);
+add_action('social_broadcast_form_item_edit', array('Social_Twitter', 'social_broadcast_form_item_edit'), 10, 3);
+
+}