web/wp-content/plugins/social/social.php
changeset 196 5e8dcbe22c24
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/web/wp-content/plugins/social/social.php	Tue Dec 04 18:43:10 2012 -0800
@@ -0,0 +1,2253 @@
+<?php
+/*
+Plugin Name: Social
+Plugin URI: http://mailchimp.com/social-plugin-for-wordpress/
+Description: Broadcast newly published posts and pull in discussions using integrations with Twitter and Facebook. Brought to you by <a href="http://mailchimp.com">MailChimp</a>.
+Version: 2.6
+Author: Crowd Favorite
+Author URI: http://crowdfavorite.com/
+*/
+
+if (!class_exists('Social')) { // try to avoid double-loading...
+
+/**
+ * Social Core
+ *
+ * @package Social
+ */
+final class Social {
+
+	/**
+	 * @var  string  URL of the API
+	 */
+	public static $api_url = 'https://sopresto.mailchimp.com/';
+
+	/**
+	 * @var  string  version number
+	 */
+	public static $version = '2.6';
+
+	/**
+	 * @var  string  CRON lock directory.
+	 */
+	public static $cron_lock_dir = null;
+
+	/**
+	 * @var  string  plugins URL
+	 */
+	public static $plugins_url = '';
+
+	/**
+	 * @var  string  plugins file path
+	 */
+	public static $plugins_path = '';
+
+	/**
+	 * @var  bool  loaded by theme?
+	 */
+	public static $loaded_by_theme = false;
+
+	/**
+	 * @var  string  duplicate comment message
+	 */
+	public static $duplicate_comment_message = 'duplicate comment';
+
+	/**
+	 * @var  Social_Log  logger
+	 */
+	private static $log = null;
+
+	/**
+	 * @var  array  default options
+	 */
+	protected static $options = array(
+		'debug' => false,
+		'install_date' => 0,
+		'installed_version' => 0,
+		'broadcast_format' => '{title}: {content} {url}',
+		'comment_broadcast_format' => '{content} {url}',
+		'system_cron_api_key' => null,
+		'fetch_comments' => '1',
+		'broadcast_by_default' => '0',
+		'use_standard_comments' => '0',
+	);
+
+	/**
+	 * @var  Social  instance of Social
+	 */
+	public static $instance = null;
+
+	/**
+	 * Loads the instance of Social.
+	 *
+	 * @static
+	 * @return Social
+	 */
+	public static function instance() {
+		if (self::$instance === null) {
+			self::$instance = new self;
+		}
+
+		return self::$instance;
+	}
+
+	/**
+	 * Handles the auto loading of classes.
+	 *
+	 * @static
+	 *
+	 * @param  string  $class
+	 *
+	 * @return bool
+	 */
+	public static function auto_load($class) {
+		if (substr($class, 0, 7) == 'Social_' or substr($class, 0, 7) == 'Kohana_') {
+			try {
+				$file = Social::$plugins_path.'lib/'.str_replace('_', '/', strtolower($class)).'.php';
+				$file = apply_filters('social_auto_load_file', $file, $class);
+				if (file_exists($file)) {
+					require $file;
+
+					return true;
+				}
+
+				return false;
+			}
+			catch (Exception $e) {
+				Social::log(sprintf(__('Failed to auto load class %s.', 'social'), $class));
+			}
+		}
+
+		return true;
+	}
+
+	/**
+	 * Returns the broadcast format tokens.
+	 *
+	 * Format:
+	 *
+	 *     {key} => __('Description', 'social')
+	 *
+	 * @static
+	 * @return array
+	 */
+	public static function broadcast_tokens() {
+		$query = new WP_Query(array(
+			'posts_per_page' => 1
+		));
+		if (count($query->posts) and $post = $query->posts[0]) {
+			$url = wp_get_shortlink($post->ID);
+			$date = get_date_from_gmt($post->post_date_gmt);
+		}
+		else {
+			$url = home_url('?p=123');
+			$date = get_date_from_gmt(current_time('mysql', true));
+		}
+
+		$defaults = array(
+			'{url}' => sprintf(__('Example: %s', 'social'), $url),
+			'{title}' => '',
+			'{content}' => '',
+			'{date}' => sprintf(__('Example: %s', 'social'), $date),
+			'{author}' => '',
+		);
+
+		return apply_filters('social_broadcast_tokens', $defaults);
+	}
+
+	/**
+	 * Returns the comment broadcast format tokens.
+	 *
+	 * Format:
+	 *
+	 *     {key} => __('Description', 'social')
+	 *
+	 * @static
+	 * @return mixed
+	 */
+	public static function comment_broadcast_tokens() {
+		$defaults = array(
+			'{content}' => '',
+			'{url}' => '',
+		);
+		return apply_filters('social_comment_broadcast_tokens', $defaults);
+	}
+
+	/**
+	 * Sets or gets an option based on the key defined.
+	 *
+	 * Get Format:
+	 *
+	 *     Running Social::option('option_name') will load "social_option_name" using get_option()
+	 *
+	 * Set Format:
+	 *
+	 *     Running Social::option('option_name', 'new_value') will update "social_option_name" to "new_value"
+	 *     using update_option().
+	 *
+	 * @static
+	 * @param  string  $key     option key
+	 * @param  mixed   $value   option value
+	 * @return bool|mixed
+	 * @uses get_option()
+	 * @uses update_option()
+	 */
+	public static function option($key, $value = null) {
+		if ($value === null) {
+			$default = null;
+			if (isset(Social::$options[$key])) {
+				$default = Social::$options[$key];
+			}
+
+			return get_option('social_'.$key, $default);
+		}
+
+		update_option('social_'.$key, $value);
+		return false;
+	}
+
+	/**
+	 * Add a message to the log.
+	 *
+	 * @static
+	 * @param  string  $message    message to add to the log
+	 * @param  array   $args       arguments to pass to the writer
+	 * @param  string  $context    context of the log message
+	 * @param  bool    $backtrace  show the backtrace
+	 * @return void
+	 */
+	public static function log($message, array $args = null, $context = null, $backtrace = false) {
+		Social::$log->write($message, $args, $context, $backtrace);
+	}
+
+	/**
+	 * Sets the loaded by theme.
+	 *
+	 * @static
+	 * @return void
+	 */
+	public static function social_loaded_by_theme() {
+		self::$loaded_by_theme = true;
+	}
+
+	/**
+	 * Sets the customer die handler.
+	 *
+	 * @static
+	 * @param  string  $handler
+	 * @return array
+	 */
+	public static function wp_die_handler($handler) {
+		return array('Social', 'wp_comment_die_handler');
+	}
+
+	/**
+	 * Don't actually die for aggregation runs.
+	 *
+	 * @static
+	 * @param  string  $message
+	 * @param  string  $title
+	 * @param  array   $args
+	 * @return mixed
+	 */
+	public static function wp_comment_die_handler($message, $title, $args) {
+		if ($message == __('Duplicate comment detected; it looks as though you&#8217;ve already said that!')) {
+			// Keep going
+			throw new Exception(Social::$duplicate_comment_message);
+		}
+	}
+
+	/**
+	 * @var  bool  is Social enabled?
+	 */
+	private $_enabled = null;
+
+	/**
+	 * Returns an array of all of the services.
+	 *
+	 * Format of the data returned:
+	 *
+	 *     $services = array(
+	 *         'twitter' => Social_Service_Twitter,
+	 *         'facebook' => Social_Service_Facebook,
+	 *         // ... any other services registered
+	 *     )
+	 *
+	 * @return array
+	 */
+	public function services() {
+		return $this->load_services();
+	}
+
+	/**
+	 * Returns a service by access key.
+	 *
+	 * Loading a service:
+	 *
+	 *     $twitter = Social::instance()->service('twitter');
+	 *
+	 * @param  string  $key    service key
+	 * @return mixed Social_Service|Social_Service_Twitter|Social_Service_Facebook|false
+	 */
+	public function service($key) {
+		$services = $this->load_services();
+		if (!isset($services[$key])) {
+			return false;
+		}
+		return $services[$key];
+	}
+	
+	/**
+	 * Returns a service by comment type.
+	 *
+	 * Loading a service:
+	 *
+	 *     $twitter = Social::instance()->service_for_comment_type('social-twitter-rt');
+	 *
+	 * @param  string  $key    service key
+	 * @return mixed  Social_Service|Social_Service_Twitter|Social_Service_Facebook|false
+	 */
+	public function service_for_comment_type($comment_type) {
+		$services = $this->load_services();
+		foreach ($services as $service) {
+			if (in_array($comment_type, $service->comment_types())) {
+				return $service;
+			}
+		}
+		return false;
+	}
+
+	/**
+	 * Initializes Social.
+	 *
+	 * @wp-action  init
+	 * @return void
+	 */
+	public function init() {
+		// Load the language translations
+		if (Social::$loaded_by_theme) {
+			$path = trailingslashit(Social::$plugins_path).'lang';
+			load_theme_textdomain('social', $path);
+		}
+		else {
+			$plugin_dir = basename(dirname(SOCIAL_FILE)).'/lang';
+			load_plugin_textdomain('social', false, $plugin_dir);
+		}
+
+		if (version_compare(PHP_VERSION, '5.2.4', '<')) {
+			deactivate_plugins(basename(__FILE__)); // Deactivate ourself
+			wp_die(__("Sorry, Social requires PHP 5.2.4 or higher. Ask your host how to enable PHP 5 as the default on your servers.", 'social'));
+		}
+
+		// Just activated?
+		if (!Social::option('install_date')) {
+			Social::option('install_date', current_time('timestamp', 1));
+			Social::option('system_cron_api_key', wp_generate_password(16, false));
+		}
+
+		// Plugins URL
+		$url = plugins_url('', SOCIAL_FILE);
+		Social::$plugins_url = trailingslashit(apply_filters('social_plugins_url', $url));
+
+		Social::$plugins_path = trailingslashit(apply_filters('social_plugins_path', SOCIAL_PATH));
+
+		// Set the logger
+		Social::$log = Social_Log::factory();
+
+		// Require Facebook and Twitter by default.
+		require Social::$plugins_path.'social-twitter.php';
+		require Social::$plugins_path.'social-facebook.php';
+	}
+
+	/**
+	 * Auth Cookie expiration for API users.
+	 *
+	 * @return int
+	 */
+	public function auth_cookie_expiration() {
+		return 31536000; // 1 Year
+	}
+
+	/**
+	 * Enqueues the assets for Social.
+	 *
+	 * @wp-action  wp_enqueue_scripts
+	 * @wp-action  load-post-new.php
+	 * @wp-action  load-post.php
+	 * @wp-action  load-profile.php
+	 * @wp-action  load-settings_page_social
+	 * @return void
+	 */
+	public function enqueue_assets() {
+		if (Social::option('use_standard_comments') == '1') {
+			return;
+		}
+		// JS/CSS
+		if (!defined('SOCIAL_COMMENTS_JS')) {
+			define('SOCIAL_COMMENTS_JS', Social::$plugins_url.'assets/social.js');
+		}
+		if (SOCIAL_COMMENTS_JS !== false) {
+			wp_enqueue_script('jquery');
+			wp_enqueue_script('social_js', SOCIAL_COMMENTS_JS, array('jquery'), Social::$version, true);
+			wp_localize_script('social_js', 'Sociali18n', array(
+				'commentReplyTitle' => __('Post a Reply', 'social'),
+			));
+		}
+
+		if (!is_admin()) {
+			if (!defined('SOCIAL_COMMENTS_CSS')) {
+				define('SOCIAL_COMMENTS_CSS', Social::$plugins_url.'assets/comments.css');
+			}
+			if (SOCIAL_COMMENTS_CSS !== false) {
+				wp_enqueue_style('social_comments', SOCIAL_COMMENTS_CSS, array(), Social::$version, 'screen');
+			}
+		}
+
+	}
+
+	/**
+	 * Enqueues the assets for Social.
+	 *
+	 * @wp-action  admin_enqueue_scripts
+	 * @return void
+	 */
+	public function admin_enqueue_assets() {
+		if (!defined('SOCIAL_ADMIN_JS')) {
+			define('SOCIAL_ADMIN_JS', Social::$plugins_url.'assets/admin.js');
+		}
+
+		if (!defined('SOCIAL_ADMIN_CSS')) {
+			define('SOCIAL_ADMIN_CSS', Social::$plugins_url.'assets/admin.css');
+		}
+
+		if (SOCIAL_ADMIN_CSS !== false) {
+			wp_enqueue_style('social_admin', SOCIAL_ADMIN_CSS, array(), Social::$version, 'screen');
+		}
+
+		if (SOCIAL_ADMIN_JS !== false) {
+			wp_enqueue_script('social_admin', SOCIAL_ADMIN_JS, array(), Social::$version, true);
+			$data = apply_filters('social_admin_js_strings', array(
+				'protectedTweet' => __('Protected Tweet', 'social'),
+				'invalidUrl' => __('Invalid URL', 'social'),
+			));
+			wp_localize_script('social_admin', 'socialAdminL10n', $data);
+		}
+	}
+
+	/**
+	 * Loads the services on every page if the user is an admin.
+	 *
+	 * @wp-action  admin_init
+	 * @return void
+	 */
+	public function admin_init() {
+		if (current_user_can('manage_options') or current_user_can('publish_posts')) {
+			// Trigger upgrade?
+			if (isset($_GET['page']) and $_GET['page'] == basename(SOCIAL_FILE)) {
+				global $wpdb;
+
+				// First check for the semaphore options, they need to be added before the upgrade starts.
+				$results = $wpdb->get_results("
+					SELECT option_id
+					  FROM $wpdb->options
+					 WHERE option_name IN ('social_locked', 'social_unlocked')
+				");
+				if (!count($results)) {
+					update_option('social_unlocked', '1');
+					update_option('social_last_lock_time', current_time('mysql', 1));
+					update_option('social_semaphore', '0');
+				}
+
+				if (version_compare(Social::option('installed_version'), Social::$version, '<')) {
+					$this->_enabled = false;
+					$this->upgrade();
+				}
+			}
+
+			if ($this->_enabled === null) {
+				$this->load_services();
+			}
+		}
+
+		// Redirect to the home_url() if the user is a commenter.
+		if (!current_user_can('publish_posts')) {
+			$commenter = get_user_meta(get_current_user_id(), 'social_commenter', true);
+			if (!empty($commenter) and $commenter == 'true') {
+				wp_redirect(trailingslashit(home_url()));
+			}
+		}
+	}
+
+	/**
+	 * Checks to see if system crons are disabled.
+	 *
+	 * @wp-action  load-settings_page_social
+	 * @return void
+	 */
+	public function check_system_cron() {
+		Social::log('Checking system CRON');
+		// Schedule CRONs
+		if (Social::option('fetch_comments') == '1') {
+			if (wp_next_scheduled('social_cron_15_init') === false) {
+				Social::log('Adding Social 15 CRON schedule');
+				wp_schedule_event(time() + 900, 'every15min', 'social_cron_15_init');
+			}
+			wp_remote_get(
+				admin_url('options_general.php?'.http_build_query(array(
+					'social_controller' => 'cron',
+					'social_action' => 'check_crons',
+					'social_api_key' => Social::option('system_cron_api_key')
+				), null, '&')),
+				array(
+					'timeout' => 0.01,
+					'blocking' => false,
+					'sslverify' => apply_filters('https_local_ssl_verify', true),
+				)
+			);
+		}
+	}
+
+	/**
+	 * Handlers requests.
+	 *
+	 * @wp-action  init
+	 * @return void
+	 */
+	public function request_handler() {
+		if (isset($_GET['social_controller'])) {
+			Social_Request::factory()->execute();
+		}
+	}
+
+	/**
+	 * Adds a link to the "Settings" menu in WP-Admin.
+	 *
+	 * @wp-action  admin_menu
+	 * @return void
+	 */
+	public function admin_menu() {
+		add_options_page(
+			__('Social Options', 'social'),
+			__('Social', 'social'),
+			'manage_options',
+			basename(SOCIAL_FILE),
+			array(
+				$this,
+				'admin_options_form'
+			)
+		);
+	}
+
+	/**
+	 * Add Settings link to plugins - code from GD Star Ratings
+	 *
+	 * @wp-filter  plugin_action_links
+	 * @param  array   $links
+	 * @param  string  $file
+	 * @return array
+	 */
+	public function add_settings_link($links, $file) {
+		static $this_plugin;
+		if (!$this_plugin) {
+			$this_plugin = plugin_basename(__FILE__);
+		}
+
+		if ($file == $this_plugin) {
+			$settings_link = '<a href="'.esc_url(admin_url('options-general.php?page=social.php')).'">'.__('Settings', 'social').'</a>';
+			array_unshift($links, $settings_link);
+		}
+		return $links;
+	}
+
+	/**
+	 * Handles the display of different messages for admin notices.
+	 *
+	 * @wp-action  admin_notices
+	 * @action     admin_notices
+	 */
+	public function admin_notices() {
+		if (current_user_can('manage_options') or current_user_can('publish_posts')) {
+			// Upgrade notice
+			if (version_compare(Social::option('installed_version'), Social::$version, '<')) {
+				$message = sprintf(__('Social is shiny and new! Please <a href="%s">verify and save your settings</a> to complete the upgrade.', 'social'), esc_url(Social::settings_url()));
+				echo '<div class="error"><p>'.$message.'</p></div>';
+			}
+
+			$suppress_no_accounts_notice = get_user_meta(get_current_user_id(), 'social_suppress_no_accounts_notice', true);
+			if (!$this->_enabled and (!isset($_GET['page']) or $_GET['page'] != basename(SOCIAL_FILE)) and empty($suppress_no_accounts_notice)) {
+				$dismiss = sprintf(__('<a href="%s" class="social_dismiss">[Dismiss]</a>', 'social'), esc_url(admin_url('options-general.php?social_controller=settings&social_action=suppress_no_accounts_notice')));
+				$message = sprintf(__('To start using Social, please <a href="%s">add an account</a>.', 'social'), esc_url(Social::settings_url()));
+				echo '<div class="error"><p>'.$message.' '.$dismiss.'</p></div>';
+			}
+
+			if (isset($_GET['page']) and $_GET['page'] == basename(SOCIAL_FILE)) {
+				// CRON Lock
+				if (Social::option('cron_lock_error') !== null) {
+					$upload_dir = wp_upload_dir();
+					if (is_writeable(Social::$plugins_path) or (isset($upload_dir['basedir']) and is_writeable($upload_dir['basedir']))) {
+						delete_option('social_cron_lock_error');
+					}
+					else {
+						if (isset($upload_dir['basedir'])) {
+							$message = sprintf(__('Social requires that either %s or %s be writable for CRON jobs.', 'social'), esc_html(Social::$plugins_path), esc_html($upload_dir['basedir']));
+						}
+						else {
+							$message = sprintf(__('Social requires that %s is writable for CRON jobs.', 'social'), esc_html(Social::$plugins_path));
+						}
+
+						echo '<div class="error"><p>'.esc_html($message).'</p></div>';
+					}
+				}
+
+				// Enable notice?
+				$suppress_enable_notice = get_user_meta(get_current_user_id(), 'social_suppress_enable_notice', true);
+				if (empty($suppress_enable_notice)) {
+					$message = __('When you enable Social, users will be created when they log in with Facebook or Twitter to comment. These users are created without a role and will be prevented from accessing the admin side of WordPress until an administrator edits the user to give them a role.', 'social');
+					$dismiss = sprintf(__('<a href="%s" class="social_dismiss">[Dismiss]</a>', 'social'), esc_url(admin_url('options-general.php?social_controller=settings&social_action=suppress_enable_notice')));
+					echo '<div class="updated"><p>'.$message.' '.$dismiss.'</p></div>';
+				}
+			}
+
+			// Log write error
+			$error = Social::option('log_write_error');
+			if ($error == '1') {
+				echo '<div class="error"><p>'.
+					sprintf(__('%s needs to be writable for Social\'s logging. <a href="%" class="social_dismiss">[Dismiss]</a>', 'social'), esc_html(Social::$plugins_path), esc_url(admin_url('options-general.php?social_controller=settings&social_action=clear_log_write_error'))).
+					'</p></div>';
+			}
+		}
+
+		// Deauthed accounts
+		$deauthed = Social::option('deauthed');
+		if (!empty($deauthed)) {
+			foreach ($deauthed as $service => $data) {
+				foreach ($data as $id => $message) {
+					$dismiss = sprintf(__('<a href="%s" class="%s">[Dismiss]</a>', 'social'), esc_url(admin_url('options-general.php?social_controller=settings&social_action=clear_deauth&id='.$id.'&service='.$service)), 'social_dismiss');
+					echo '<div class="error"><p>'.esc_html($message).' '.$dismiss.'</p></div>';
+				}
+			}
+		}
+
+		// Errored broadcasting?
+		global $post;
+		if (isset($post->ID)) {
+			$error_accounts = get_post_meta($post->ID, '_social_broadcast_error', true);
+			if (!empty($error_accounts)) {
+				$message = Social_View::factory('wp-admin/post/broadcast/error/notice', array(
+					'social' => $this,
+					'accounts' => $error_accounts,
+					'post' => $post,
+				));
+				echo '<div class="error" id="social-broadcast-error">'.$message.'</div>';
+
+				delete_post_meta($post->ID, '_social_broadcast_error');
+			}
+		}
+
+		// 2.0 Upgrade?
+		$upgrade_2_0 = get_user_meta(get_current_user_id(), 'social_2.0_upgrade', true);
+		if (!empty($upgrade_2_0)) {
+			if (current_user_can('manage_options')) {
+				$output = __('Social needs to re-authorize your Facebook account(s). Please re-connect your <a href="%s">global</a> and <a href="%s">personal</a> accounts.', 'social');
+				$output = sprintf($output, esc_url(Social::settings_url()), esc_url(admin_url('profile.php#social-accounts')));
+			}
+			else {
+				$output = __('Social needs to re-authorize your Facebook account(s).. Please re-connect your <a href="%s">personal</a> accounts.', 'social');
+				$output = sprintf($output, esc_url(admin_url('profile.php#social-networks')));
+			}
+
+			$dismiss = sprintf(__('<a href="%s" class="%s">[Dismiss]</a>', 'social'), esc_url(admin_url('options-general.php?social_controller=settings&social_action=clear_2_0_upgrade')), 'social_dismiss');
+			echo '<div class="error"><p>'.$output.' '.$dismiss.'</p></div>';
+		}
+	}
+
+	/**
+	 * Displays the admin options form.
+	 *
+	 * @return void
+	 */
+	public function admin_options_form() {
+		Social_Request::factory('settings/index')->execute();
+	}
+
+	/**
+	 * Shows the user's social network accounts.
+	 *
+	 * @wp-action  show_user_profile
+	 * @param  object  $profileuser
+	 * @return void
+	 */
+	public function show_user_profile($profileuser) {
+		$default_accounts = get_user_meta($profileuser->ID, 'social_default_accounts', true);
+		if (empty($default_accounts)) {
+			$default_accounts = array();
+		}
+		$accounts = array();
+		foreach ($this->services() as $key => $service) {
+			if (!isset($accounts[$key])) {
+				$accounts[$key] = array();
+			}
+			foreach ($service->accounts() as $account) {
+				if ($account->personal()) {
+					$accounts[$key][] = $account->id();
+				}
+			}
+		}
+		echo Social_View::factory('wp-admin/profile', array(
+			'defaults' => $default_accounts,
+			'services' => $this->services(),
+			'accounts' => $accounts,
+		));
+	}
+
+	/**
+	 * Saves the default accounts for the user.
+	 *
+	 * @wp-action personal_options_update
+	 * @param  int  $user_id
+	 * @return void
+	 */
+	public function personal_options_update($user_id) {
+		// Store the default accounts
+		$accounts = array();
+		if (isset($_POST['social_default_accounts']) and is_array($_POST['social_default_accounts'])) {
+			foreach ($_POST['social_default_accounts'] as $account) {
+				$account = explode('|', $account);
+				$accounts[$account[0]][] = $account[1];
+			}
+		}
+
+		// TODO abstract this to the facebook plugin
+		if (isset($_POST['social_default_pages']) and is_array($_POST['social_default_pages'])) {
+			if (!isset($accounts['facebook'])) {
+				$accounts['facebook'] = array(
+					'pages' => array()
+				);
+			}
+			$accounts['facebook']['pages'] = $_POST['social_default_pages'];
+		}
+
+		if (count($accounts)) {
+			update_user_meta($user_id, 'social_default_accounts', $accounts);
+		}
+		else {
+			delete_user_meta($user_id, 'social_default_accounts');
+		}
+
+		// Save Enabled child accounts
+		$is_profile = true;
+		$enabled_child_accounts = is_array($_POST['social_enabled_child_accounts']) ? $_POST['social_enabled_child_accounts'] : array();
+		foreach ($this->services() as $service_key => $service) {
+			$updated_accounts = array();
+			foreach ($service->accounts() as $account) {
+				//default service to empty array in case it is not set
+				$enabled_child_accounts[$service_key] = isset($enabled_child_accounts[$service_key]) ? $enabled_child_accounts[$service_key] : array();
+
+				$account->update_enabled_child_accounts($enabled_child_accounts[$service_key]);
+				$updated_accounts[$account->id()] = $account->as_object();
+			}
+			$service->accounts($updated_accounts)->save($is_profile);
+		}
+	}
+
+	/**
+	 * Return array of enabled social broadcasting post types
+	 *
+	 * @static
+	 * @return array
+	 */
+	public static function broadcasting_enabled_post_types() {
+		return apply_filters('social_broadcasting_enabled_post_types', get_post_types(array(
+			'public' => true,
+			'hierarchical' => false
+		)));
+	}
+	
+	/**
+	 * Check if a post type has broadcasting enabled
+	 *
+	 * @static
+	 * @param  string  $post_type  post type to check for
+	 * @return bool
+	 */
+	public static function broadcasting_enabled_for_post_type($post_type = null) {
+		return (bool) in_array($post_type, self::broadcasting_enabled_post_types());
+	}
+
+	/**
+	 * Add Meta Boxes
+	 *
+	 * @wp-action  do_meta_boxes
+	 * @return void
+	 */
+	public function do_meta_boxes() {
+		global $post;
+
+		if ($post !== null && Social::option('disable_broadcasting') != 1) {
+			foreach (self::broadcasting_enabled_post_types() as $post_type) {
+				add_meta_box('social_meta_broadcast', __('Social Broadcasting', 'social'), array(
+					$this,
+					'add_meta_box_broadcast'
+				), $post_type, 'side', 'high');
+	
+				$fetch = Social::option('fetch_comments');
+				if ($this->_enabled and !empty($fetch)) {
+					if ($post->post_status == 'publish') {
+						add_meta_box('social_meta_aggregation_log', __('Social Comments', 'social'), array(
+							$this,
+							'add_meta_box_log'
+						), $post_type, 'normal', 'core');
+					}
+				}
+			}
+		}
+	}
+
+	/**
+	 * Adds the broadcasting meta box.
+	 *
+	 * @return void
+	 */
+	public function add_meta_box_broadcast() {
+		global $post;
+
+		$broadcasted = '';
+		$broadcasted_ids = get_post_meta($post->ID, '_social_broadcasted_ids', true);
+		if (!empty($broadcasted_ids)) {
+			$broadcasted = Social_View::factory('wp-admin/post/meta/broadcast/parts/broadcasted', array(
+				'services' => $this->services(),
+				'ids' => $broadcasted_ids,
+				'post' => $post,
+			));
+		}
+
+		$show_broadcast = false;
+		foreach ($this->services() as $service) {
+			if (count($service->accounts())) {
+				$show_broadcast = true;
+				break;
+			}
+		}
+
+		// Content
+		$button = '';
+		$content = '';
+		if ($show_broadcast) {
+			if ($post->post_status != 'private') {
+				switch ($post->post_status) {
+					case 'pending':
+						$button = 'Edit';
+						$accounts = get_post_meta($post->ID, '_social_broadcast_accounts', true);
+						$content = Social_View::factory('wp-admin/post/meta/broadcast/pending', array(
+							'accounts' => $accounts,
+							'services' => $this->services(),
+						));
+						break;
+					case 'future':
+						$button = 'Edit';
+						$accounts = get_post_meta($post->ID, '_social_broadcast_accounts', true);
+						$content = Social_View::factory('wp-admin/post/meta/broadcast/scheduled', array(
+							'services' => $this->services(),
+							'accounts' => $accounts,
+						));
+						break;
+					case 'publish':
+						$button = 'Broadcast';
+						break;
+					default:
+						if ($post->post_status == 'draft' and !empty($broadcasted_ids)) {
+							$content = '';
+						}
+						else {
+							$notify = false;
+							if (get_post_meta($post->ID, '_social_notify', true) == '1') {
+								$notify = true;
+							}
+							else if (Social::option('broadcast_by_default') == '1') {
+								$notify = true;
+							}
+
+							$content = Social_View::factory('wp-admin/post/meta/broadcast/default', array(
+								'post' => $post,
+								'notify' => $notify,
+							));
+						}
+						break;
+				}
+			}
+			else {
+				$content = Social_View::factory('wp-admin/post/meta/broadcast/private');
+			}
+
+			// Button
+			if (!empty($button)) {
+				$button = Social_View::factory('wp-admin/post/meta/broadcast/parts/button', array(
+					'broadcasted' => $broadcasted,
+					'button_text' => $button,
+				));
+			}
+		}
+
+		echo Social_View::factory('wp-admin/post/meta/broadcast/shell', array(
+			'post' => $post,
+			'content' => $content,
+			'broadcasted' => $broadcasted,
+			'button' => $button
+		));
+	}
+
+	/**
+	 * Adds the aggregation log meta box.
+	 *
+	 * @return void
+	 */
+	public function add_meta_box_log() {
+		global $post;
+
+		$next_run = get_post_meta($post->ID, '_social_aggregation_next_run', true);
+		if (empty($next_run)) {
+			$next_run = __('Not Scheduled', 'social');
+		}
+		else {
+			$next_run = Social_Aggregation_Queue::next_run($next_run);
+		}
+
+		echo Social_View::factory('wp-admin/post/meta/log/shell', array(
+			'post' => $post,
+			'next_run' => $next_run,
+		));
+	}
+
+	/**
+	 * Show the broadcast options if publishing.
+	 *
+	 * @wp-filter  redirect_post_location
+	 * @param  string  $location  default post-publish location
+	 * @param  int     $post_id   post ID
+	 * @return string|void
+	 */
+	public function redirect_post_location($location, $post_id) {
+		if ((isset($_POST['social_notify']) and $_POST['social_notify'] == '1') and
+			(isset($_POST['visibility']) and $_POST['visibility'] !== 'private')
+		) {
+			update_post_meta($post_id, '_social_notify', '1');
+			if (isset($_POST['publish']) or isset($_POST['social_broadcast'])) {
+				Social_Request::factory('broadcast/options')->post(array(
+					'post_ID' => $post_id,
+					'location' => $location,
+				))->execute();
+			}
+		}
+		else {
+			delete_post_meta($post_id, '_social_notify');
+		}
+		return $location;
+	}
+
+	/**
+	 * Removes post meta if the post is going to private.
+	 *
+	 * @wp-action  transition_post_status
+	 * @param  string  $new
+	 * @param  string  $old
+	 * @param  object  $post
+	 * @return void
+	 */
+	public function transition_post_status($new, $old, $post) {
+		if ($new == 'private') {
+			delete_post_meta($post->ID, '_social_notify');
+			delete_post_meta($post->ID, '_social_broadcast_accounts');
+
+			foreach ($this->services() as $key => $service) {
+				delete_post_meta($post->ID, '_social_'.$key.'_content');
+			}
+		}
+		else {
+			$xmlrpc = false;
+			if ($new == 'publish') {
+				if ( ( defined('XMLRPC_REQUEST') or defined('SOCIAL_MAIL_PUBLISH') ) and $old != 'publish') {
+					$xmlrpc = true;
+					$this->xmlrpc_publish_post($post);
+				}
+				if (self::broadcasting_enabled_for_post_type($post->post_type)) {
+					Social_Aggregation_Queue::factory()->add($post->ID)->save();
+				}
+			}
+
+			// Sends previously saved broadcast information
+			if ($xmlrpc or ($old == 'future' and !in_array($new, array('future', 'draft')))) {
+				Social_Request::factory('broadcast/run')->query(array(
+					'post_ID' => $post->ID
+				))->execute();
+			}
+		}
+	}
+
+	/**
+	 * Broadcasts the post on XML RPC requests.
+	 *
+	 * @param  object  $post
+	 * @return void
+	 */
+	public function xmlrpc_publish_post($post) {
+		if ($post and Social::option('broadcast_by_default') == '1') {
+			Social::log('Broadcasting triggered by XML-RPC.');
+
+			$broadcast_accounts = array();
+			$broadcast_content = array();
+			$broadcast_meta = array();
+
+			foreach ($this->default_accounts($post) as $service_key => $accounts) {
+				$service = $this->service($service_key);
+				if ($service !== false) {
+					$broadcast_content[$service_key] = array();
+					$broadcast_meta[$service_key] = array();
+					foreach ($accounts as $key => $id) {
+						// TODO abstract this to the Facebook plugin
+						if ($service_key == 'facebook' and $key === 'pages') {
+							foreach ($id as $account_id => $pages) {
+								$account = $service->account($account_id);
+
+								// TODO This could use some DRY love
+								$universal_pages = $account->pages();
+								$personal_pages = $account->pages(null, true);
+
+								foreach ($pages as $page_id) {
+									if (!isset($broadcast_accounts[$service_key])) {
+										$broadcast_accounts[$service_key] = array();
+									}
+
+									if (!isset($broadcast_accounts[$service_key][$page_id])) {
+										if (isset($universal_pages[$page_id])) {
+											$broadcast_accounts[$service_key][$page_id] = (object) array(
+												'id' => $page_id,
+												'name' => $universal_pages[$page_id]->name,
+												'universal' => true,
+												'page' => true,
+											);
+										}
+										else if (isset($personal_pages[$page_id])) {
+											$broadcast_accounts[$service_key][$page_id] = (object) array(
+												'id' => $page_id,
+												'name' => $personal_pages[$page_id]->name,
+												'universal' => false,
+												'page' => true,
+											);
+										}
+									}
+									$broadcast_content[$service_key][$page_id] = $service->format_content($post, Social::option('broadcast_format'));
+									$broadcast_meta[$service_key][$page_id] = $service->get_broadcast_extras($page_id, $post);
+								}
+							}
+						}
+						else {
+							$account = $service->account($id);
+							if ($account !== false) {
+								if (!isset($broadcast_accounts[$service_key])) {
+									$broadcast_accounts[$service_key] = array();
+								}
+
+								$broadcast_accounts[$service_key][$account->id()] = (object) array(
+									'id' => $account->id(),
+									'universal' => $account->universal()
+								);
+
+								$broadcast_content[$service_key][$account->id()] = $service->format_content($post, Social::option('broadcast_format'));
+								$broadcast_meta[$service_key][$account->id()] = $service->get_broadcast_extras($account->id(), $post);
+							}
+						}
+					}
+				}
+			}
+
+			update_post_meta($post->ID, '_social_broadcast_content', addslashes_deep($broadcast_content));
+			update_post_meta($post->ID, '_social_broadcast_meta', addslashes_deep($broadcast_meta));
+
+			if (count($broadcast_accounts)) {
+				Social::log('There are default accounts, running broadcast');
+				update_post_meta($post->ID, '_social_broadcast_accounts', addslashes_deep($broadcast_accounts));
+			}
+		}
+	}
+
+	/**
+	 * Loads the default accounts for the post.
+	 *
+	 * @param  object  $post
+	 * @return array
+	 */
+	public function default_accounts($post) {
+		$default_accounts = Social::option('default_accounts');
+		$author_default_accounts = get_user_meta($post->post_author, 'social_default_accounts', true);
+		if (is_array($author_default_accounts)) {
+			foreach ($author_default_accounts as $service_key => $accounts) {
+				if (!isset($default_accounts[$service_key])) {
+					$default_accounts[$service_key] = $accounts;
+				}
+				else {
+					foreach ($accounts as $key => $account) {
+						if ($key === 'pages') {
+							if (!isset($default_accounts[$key]['pages'])) {
+								$default_accounts[$key]['pages'] = $account;
+							}
+							else {
+								foreach ($account as $page_id) {
+									if (!in_array($page_id, $default_accounts[$key]['pages'])) {
+										$default_accounts[$key]['pages'][] = $page_id;
+									}
+								}
+							}
+						}
+						else {
+							$default_accounts[$service_key][] = $account;
+						}
+					}
+				}
+			}
+		}
+
+		return apply_filters('social_default_accounts', $default_accounts, $post);
+	}
+
+	/**
+	 * Sets the broadcasted IDs for the post.
+	 *
+	 * @param  int  $post_id  post id
+	 * @param  string  $service  service key
+	 * @param  string  $broadcasted_id  broadcasted id
+	 * @param  string  $message  broadcasted message
+	 * @param  Social_Service_Account  $account  account
+	 * @param  Social_Response  $response  response object
+	 * @return void
+	 */
+	public function add_broadcasted_id($post_id, $service, $broadcasted_id, $message, $account, Social_Response $response = null) {
+		$broadcasted_ids = get_post_meta($post_id, '_social_broadcasted_ids', true);
+		if (empty($broadcasted_ids)) {
+			$broadcasted_ids = array();
+		}
+
+		if (!isset($broadcasted_ids[$service])) {
+			$broadcasted_ids[$service] = array();
+		}
+
+		if (!isset($broadcasted_ids[$service][$account->id()])) {
+			$broadcasted_ids[$service][$account->id()] = array();
+		}
+
+		if (!isset($broadcasted_ids[$service][$account->id()][$broadcasted_id])) {
+			$urls = array(
+				get_permalink($post_id)
+			);
+
+			$shortlink = wp_get_shortlink($post_id);
+			if (!in_array($shortlink, $urls)) {
+				$urls[] = $shortlink;
+			}
+
+			$home_url = home_url('?p='.$post_id);
+			if (!in_array($home_url, $urls)) {
+				$urls[] = $home_url;
+			}
+
+			$data = array(
+				'message' => $message,
+				'urls' => $urls
+			);
+			$data = apply_filters('social_save_broadcasted_ids_data', $data, $account, $service, $post_id, $response);
+			$broadcasted_ids[$service][$account->id()][$broadcasted_id] = $data;
+			update_post_meta($post_id, '_social_broadcasted_ids', $broadcasted_ids);
+		}
+	}
+
+	/**
+	 * Adds the 15 minute interval.
+	 *
+	 * @wp-filter  cron_schedules
+	 * @param  array  $schedules
+	 * @return array
+	 */
+	public function cron_schedules($schedules) {
+		$schedules['every15min'] = array(
+			'interval' => 900,
+			'display' => 'Every 15 minutes'
+		);
+		return $schedules;
+	}
+
+	/**
+	 * Sends a request to initialize CRON 15.
+	 *
+	 * @wp-action  social_cron_15_init
+	 * @return void
+	 */
+	public function cron_15_init() {
+		Social::log('Running cron_15_init');
+		Social_Request::factory('cron/cron_15')->query('api_key', Social::option('system_cron_api_key'))->execute();
+	}
+
+	/**
+	 * Runs the aggregation loop.
+	 *
+	 * @wp-action  social_cron_15
+	 * @return void
+	 */
+	public function run_aggregation() {
+		$semaphore = Social_Semaphore::factory();
+		$queue = Social_Aggregation_Queue::factory();
+
+		foreach ($queue->runnable() as $timestamp => $posts) {
+			foreach ($posts as $id => $interval) {
+				$post = get_post($id);
+				if ($post !== null) {
+					$queue->add($id, $interval)->save();
+					$semaphore->increment();
+					$this->request(home_url('index.php?social_controller=aggregation&social_action=run&post_id='.$id), 'run');
+				}
+				else {
+					$queue->remove($id, $timestamp)->save();
+				}
+			}
+		}
+	}
+
+	/**
+	 * Removes the post from the aggregation queue.
+	 *
+	 * @wp-action  delete_post
+	 * @param  int  $post_id
+	 * @return void
+	 */
+	public function delete_post($post_id) {
+		Social_Aggregation_Queue::factory()->remove($post_id);
+	}
+
+	/**
+	 * Hides the Site Admin link for social-based users.
+	 *
+	 * @wp-filter register
+	 * @param  string  $link
+	 * @return string
+	 */
+	public function register($link) {
+		if (is_user_logged_in()) {
+			$commenter = get_user_meta(get_current_user_id(), 'social_commenter', true);
+			if (!empty($commenter)) {
+				return '';
+			}
+		}
+
+		return $link;
+	}
+
+	/**
+	 * Sets the user role.
+	 *
+	 * @wp-action set_user_role
+	 * @param  int     $user_id
+	 * @param  string  $role
+	 */
+	public function set_user_role($user_id, $role) {
+		if (!empty($role)) {
+			delete_user_meta($user_id, 'social_commenter');
+		}
+	}
+
+	/**
+	 * Show the disconnect link for social-based users.
+	 *
+	 * @wp-filter loginout
+	 * @param  string  $link
+	 * @return string
+	 */
+	public function loginout($link) {
+		if (is_user_logged_in()) {
+			$commenter = get_user_meta(get_current_user_id(), 'social_commenter', true);
+			if (!empty($commenter)) {
+				foreach ($this->services() as $key => $service) {
+					$account = reset($service->accounts());
+					if ($account) {
+						return $service->disconnect_link($account);
+					}
+				}
+			}
+		}
+		else {
+			$link = explode('>'.__('Log in'), $link);
+			$link = $link[0].' id="social_login">'.__('Log in').$link[1];
+		}
+
+		return $link;
+	}
+
+	/**
+	 * Increments the service comment counter.
+	 *
+	 * @static
+	 * @param  array  $items
+	 * @param  array  $groups
+	 */
+	public static function add_social_items_count($items, &$groups) {
+		foreach ($items as $group => $_items) {
+			if ($group == 'parent') {
+				self::add_social_items_count($_items, $groups);
+			}
+			else {
+				if (!isset($groups['social-'.$group])) {
+					$groups['social-'.$group] = 0;
+				}
+
+				$groups['social-'.$group] = $groups['social-'.$group] + count($_items);
+			}
+		}
+	}
+
+	/**
+	 * Overrides the default WordPress comments_template function.
+	 *
+	 * @wp-filter  comments_template
+	 * @return string
+	 */
+	public function comments_template($path) {
+		global $post;
+
+		if (!(
+			is_singular() and 
+			(have_comments() or $post->comment_status == 'open') and 
+			Social::option('use_standard_comments') != '1'
+		)) {
+			return $path;
+		}
+
+		if (!defined('SOCIAL_COMMENTS_FILE')) {
+			define('SOCIAL_COMMENTS_FILE', trailingslashit(dirname(SOCIAL_FILE)).'views/comments.php');
+		}
+
+		return SOCIAL_COMMENTS_FILE;
+	}
+
+	/**
+	 * Returns an array of comment types that display avatars.
+	 *
+	 * @wp-filter  get_avatar_comment_types
+	 * @param  array  $types  default WordPress types
+	 * @return array
+	 */
+	public function get_avatar_comment_types($types) {
+		$types[] = 'wordpress';
+		return $types;
+	}
+
+	/**
+	 * Gets the avatar based on the comment type.
+	 *
+	 * @wp-filter  get_avatar
+	 * @param  string  $avatar
+	 * @param  object  $comment
+	 * @param  int     $size
+	 * @param  string  $default
+	 * @param  string  $alt
+	 * @return string
+	 */
+	public function get_avatar($avatar, $comment, $size, $default, $alt) {
+		$image = null;
+		if (is_object($comment)) {
+			$image = get_comment_meta($comment->comment_ID, 'social_profile_image_url', true);
+			if (empty($image)) {
+				$image = null;
+			}
+		}
+		else {
+			// Commenter?
+			$social_avatar = get_user_meta($comment, 'social_avatar', true);
+			if (!empty($social_avatar)) {
+				$image = $social_avatar;
+			}
+		}
+
+		if ($image !== null) {
+			$image = esc_url($image);
+			$size = esc_attr($size);
+			$type = '';
+			if (is_object($comment)) {
+				$type = esc_attr($comment->comment_type);
+			}
+
+			$image = esc_url($image);
+			$image_format = apply_filters('social_get_avatar_image_format', '<img alt="%1$s" src="%2$s" class="avatar avatar-%3$s photo %4$s" height="%3$s" width="%3$s" />');
+			return sprintf($image_format, $alt, $image, $size, $type);
+		}
+
+		return $avatar;
+	}
+
+	/**
+	 * Sets the comment type upon being saved.
+	 *
+	 * @wp-action  comment_post
+	 * @param  int  $comment_ID
+	 */
+	public function comment_post($comment_ID) {
+		global $wpdb;
+
+		$comment = get_comment($comment_ID);
+		$services = $this->services();
+		if (!empty($services)) {
+			$account_id = $_POST['social_post_account'];
+			foreach ($services as $key => $service) {
+				$output = $service->format_comment_content($comment, Social::option('comment_broadcast_format'));
+				foreach ($service->accounts() as $account) {
+					if ($account_id == $account->id()) {
+						if (isset($_POST['post_to_service'])) {
+							$in_reply_to_status_id = get_comment_meta($_POST['comment_parent'], 'social_status_id', true);
+							if ($comment->comment_approved == '0') {
+								update_comment_meta($comment_ID, 'social_to_broadcast', $_POST['social_post_account']);
+								if (!empty($in_reply_to_status_id)) {
+									update_comment_meta($comment_ID, 'social_in_reply_to_status_id', addslashes_deep($in_reply_to_status_id));
+								}
+							}
+							else {
+								$args = array();
+								if (!empty($in_reply_to_status_id)) {
+									$args['in_reply_to_status_id'] = $in_reply_to_status_id;
+									delete_comment_meta($comment_ID, 'social_in_reply_to_status_id');
+								}
+								Social::log(sprintf(__('Broadcasting comment #%s to %s using account #%s.', 'social'), $comment_ID, $service->title(), $account->id()));
+								$response = $service->broadcast($account, $output, $args, null, $comment_ID);
+								if ($response === false or $response->id() === '0') {
+									wp_delete_comment($comment_ID);
+									Social::log(sprintf(__('Error: Broadcast comment #%s to %s using account #%s, please go back and try again.', 'social'), $comment_ID, esc_html($service->title()), esc_html($account->id())));
+									wp_die(sprintf(__('Error: Your comment could not be sent to %s, please go back and try again.', 'social'), esc_html($service->title())));
+								}
+
+								$wpdb->query($wpdb->prepare("
+									UPDATE $wpdb->comments
+									   SET comment_type = %s
+									 WHERE comment_ID = %s
+								", 'social-'.$service->key(), $comment_ID));
+
+								$this->set_comment_aggregated_id($comment_ID, $service->key(), $response->id());
+								update_comment_meta($comment_ID, 'social_status_id', addslashes_deep($response->id()));
+								update_comment_meta($comment_ID, 'social_raw_data', addslashes_deep(base64_encode(json_encode($response->body()->response))));
+								Social::log(sprintf(__('Broadcasting comment #%s to %s using account #%s COMPLETE.', 'social'), $comment_ID, $service->title(), $account->id()));
+							}
+						}
+
+						update_comment_meta($comment_ID, 'social_account_id', addslashes_deep($account_id));
+						update_comment_meta($comment_ID, 'social_profile_image_url', addslashes_deep($account->avatar()));
+						update_comment_meta($comment_ID, 'social_comment_type', addslashes_deep('social-'.$service->key()));
+
+						if ($comment->user_id != '0') {
+							$comment->comment_author = $account->name();
+							$comment->comment_author_url = $account->url();
+							wp_update_comment(get_object_vars($comment));
+						}
+						Social::log(sprintf(__('Comment #%s saved.', 'social'), $comment_ID));
+						break;
+					}
+				}
+			}
+		}
+	}
+
+	/**
+	 * Sets the comment to be approved.
+	 *
+	 * @wp-action  wp_set_comment_status
+	 * @param  int     $comment_id
+	 * @param  string  $comment_status
+	 * @return void
+	 */
+	public function wp_set_comment_status($comment_id, $comment_status) {
+		if ($comment_status == 'approve') {
+			global $wpdb;
+			$results = $wpdb->get_results($wpdb->prepare("
+				SELECT user_id, m.meta_value
+				  FROM $wpdb->commentmeta AS m
+				  JOIN $wpdb->comments AS c
+				    ON m.comment_id = c.comment_ID
+				 WHERE m.meta_key = %s
+				   AND m.comment_id = %s
+			", 'social_to_broadcast', $comment_id));
+			if (!empty($results)) {
+				$result = reset($results);
+				$accounts = get_user_meta($result->user_id, 'social_accounts', true);
+				if (!empty($accounts)) {
+					foreach ($accounts as $service => $accounts) {
+						$service = $this->service($service);
+						if ($service !== false) {
+							$account = null;
+							if (!$service->account_exists($result->meta_value)) {
+								foreach ($accounts as $id => $account) {
+									if ($id == $result->meta_value) {
+										$class = 'Social_Service_'.$service->key().'_Account';
+										$account = new $class($account);
+										break;
+									}
+								}
+							}
+							else {
+								$account = $service->account($result->meta_value);
+							}
+
+							if ($account !== null) {
+								Social::log(sprintf(__('Broadcasting comment #%s to %s using account #%s.', 'social'), $comment_id, $service->title(), $account->id()));
+								$comment = get_comment($comment_id);
+
+								$in_reply_to_status_id = get_comment_meta($comment_id, 'social_in_reply_to_status_id', true);
+								$args = array();
+								if (!empty($in_reply_to_status_id)) {
+									$args['in_reply_to_status_id'] = $in_reply_to_status_id;
+									delete_comment_meta($comment_id, 'social_in_reply_to_status_id');
+								}
+
+								$output = $service->format_comment_content($comment, Social::option('comment_broadcast_format'));
+								$response = $service->broadcast($account, $output, $args, null, $comment_id);
+								if ($response === false or $response->id() === false) {
+									wp_delete_comment($comment_id);
+									Social::log(sprintf(__('Error: Broadcast comment #%s to %s using account #%s, please go back and try again.', 'social'), $comment_id, $service->title(), $account->id()));
+								}
+
+								$wpdb->query($wpdb->prepare("
+									UPDATE $wpdb->comments
+									   SET comment_type = %s
+									 WHERE comment_ID = %s
+								", 'social-'.$service->key(), $comment_id));
+
+								$this->set_comment_aggregated_id($comment_id, $service->key(), $response->id());
+								update_comment_meta($comment_id, 'social_status_id', addslashes_deep($response->id()));
+								update_comment_meta($comment_id, 'social_raw_data', addslashes_deep(base64_encode(json_encode($response->body()->response))));
+								Social::log(sprintf(__('Broadcasting comment #%s to %s using account #%s COMPLETE.', 'social'), $comment_id, $service->title(), $account->id()));
+							}
+						}
+					}
+				}
+
+				delete_comment_meta($comment_id, 'social_to_broadcast');
+			}
+		}
+	}
+
+	/**
+	 * Sets the comment aggregation ID.
+	 *
+	 * Format of the stored data (serialized):
+	 *
+	 *     array(
+	 *         'twitter' => array(
+	 *             1234567890,
+	 *             0987654321,
+	 *             // ... Other aggregated IDs
+	 *         ),
+	 *         'facebook' => array(
+	 *             1234567890_1234567890,
+	 *             0987654321_0987654321,
+	 *             // ... Other aggregated IDs
+	 *         )
+	 *     )
+	 *
+	 * @param  int     $comment_id
+	 * @param  string  $service
+	 * @param  int     $broadcasted_id
+	 * @return void
+	 */
+	private function set_comment_aggregated_id($comment_id, $service, $broadcasted_id) {
+		$comment = get_comment($comment_id);
+		if (is_object($comment)) {
+			$aggregated_ids = get_post_meta($comment->comment_post_ID, '_social_aggregated_ids', true);
+			if (empty($aggregated_ids)) {
+				$aggregated_ids = array();
+			}
+
+			if (!isset($aggregated_ids[$service])) {
+				$aggregated_ids[$service] = array();
+			}
+
+			if (!in_array($broadcasted_id, $aggregated_ids[$service])) {
+				$aggregated_ids[$service][] = $broadcasted_id;
+			}
+
+			update_post_meta($comment->comment_post_ID, '_social_aggregated_ids', $aggregated_ids);
+		}
+	}
+
+	/**
+	 * Counts the different types of comments.
+	 *
+	 * @wp-filter social_comments_array
+	 * @static
+	 * @param  array  $comments
+	 * @param  int    $post_id
+	 * @return array
+	 */
+	public function comments_array(array $comments, $post_id) {
+		$groups = array();
+		if (isset($comments['social_groups'])) {
+			$groups = $comments['social_groups'];
+		}
+
+		// count the comment types for output in tab headers
+		foreach ($comments as $comment) {
+			if (is_object($comment)) {
+				if (empty($comment->comment_type)) {
+					$comment->comment_type = 'wordpress';
+				}
+
+				if (!isset($groups[$comment->comment_type])) {
+					$groups[$comment->comment_type] = 1;
+				}
+				else {
+					++$groups[$comment->comment_type];
+				}
+				if (isset($comment->social_items) and is_array($comment->social_items)) {
+					$groups[$comment->comment_type] += count($comment->social_items);
+				}
+			}
+		}
+
+		$comments['social_groups'] = apply_filters('social_comments_array_groups', $groups, $comments);
+
+		return $comments;
+	}
+
+	/**
+	 * Displays a comment.
+	 *
+	 * @param  object  $comment  comment object
+	 * @param  array   $args
+	 * @param  int     $depth
+	 */
+	public function comment($comment, array $args = array(), $depth = 0) {
+		$GLOBALS['comment'] = $comment;
+
+		$social_items = '';
+		$status_url = null;
+		$comment_type = $comment->comment_type;
+		$ignored_types = apply_filters('social_ignored_comment_types', array(
+			'wordpress',
+			'pingback'
+		));
+		// set the comment type to WordPress if we can't load the Social service (perhaps it was deactivated)
+		// and the type isn't an ignored type
+		if (!($service = $this->service_for_comment_type($comment->comment_type)) && !in_array($comment->comment_type, $ignored_types)) {
+			$comment_type = 'wordpress';
+		}
+		// set Social Items for Social comments
+		if (!in_array($comment->comment_type, $ignored_types)) {
+			$status_id = get_comment_meta($comment->comment_ID, 'social_status_id', true);
+			if (!empty($status_id)) {
+				$status_url = $service->status_url(get_comment_author(), $status_id);
+			}
+			// Social items?
+			if (!empty($comment->social_items)) {
+				if (is_object($service) && method_exists($service, 'key')) {
+					$avatar_size = apply_filters('social_items_comment_avatar_size', array(
+						'width' => 18,
+						'height' => 18,
+					));
+					$social_items = Social_View::factory('comment/social_item', array(
+						'items' => $comment->social_items,
+						'service' => $service,
+						'avatar_size' => $avatar_size
+					));
+				}
+				else {
+					Social::log('service not set for: '.print_r($comment, true));
+					ob_start();
+					var_dump($service);
+					Social::log('$service: '.ob_get_clean());
+				}
+			}
+		}
+
+		echo Social_View::factory('comment/comment', array(
+			'comment_type' => $comment_type,
+			'comment' => $comment,
+			'service' => $service,
+			'status_url' => $status_url,
+			'depth' => $depth,
+			'args' => $args,
+			'social_items' => $social_items,
+		));
+	}
+
+	/**
+	 * Adds the Aggregate Comments link to the post row actions.
+	 *
+	 * @param  array    $actions
+	 * @param  WP_Post  $post
+	 * @return array
+	 */
+	public function post_row_actions(array $actions, $post) {
+		if ($post->post_status == 'publish' && in_array(Social::option('fetch_comments'), array('1', '2'))) {
+			$actions['social_aggregation'] = sprintf(__('<a href="%s" rel="%s">Social Comments</a>', 'social'), esc_url(wp_nonce_url(admin_url('options-general.php?social_controller=aggregation&social_action=run&post_id='.$post->ID), 'run')), $post->ID).
+				'<img src="'.esc_url(admin_url('images/wpspin_light.gif')).'" class="social_run_aggregation_loader" />';
+		}
+		return $actions;
+	}
+
+	/**
+	 * Adds the aggregation functionality to the admin bar.
+	 *
+	 * @return void
+	 */
+	public function admin_bar_menu() {
+		global $wp_admin_bar;
+
+		$current_object = get_queried_object();
+
+		if (empty($current_object)) {
+			return;
+		}
+
+		if (!empty($current_object->post_type)
+			and ($post_type_object = get_post_type_object($current_object->post_type))
+				and current_user_can($post_type_object->cap->edit_post, $current_object->ID)
+					and ($post_type_object->show_ui or 'attachment' == $current_object->post_type)
+						and (in_array(Social::option('fetch_comments'), array('1', '2')))
+		) {
+			$wp_admin_bar->add_menu(array(
+				'parent' => 'comments',
+				'id' => 'social-find-comments',
+				'title' => __('Find Social Comments', 'social')
+					.'<span class="social-aggregation-spinner" style="display: none;">&nbsp;(
+						<span class="social-dot dot-active">.</span>
+						<span class="social-dot">.</span>
+						<span class="social-dot">.</span>
+					)</span>',
+				'href' => esc_url(wp_nonce_url(admin_url('options-general.php?social_controller=aggregation&social_action=run&post_id='.$current_object->ID), 'run')),
+			));
+			$wp_admin_bar->add_menu(array(
+				'parent' => 'comments',
+				'id' => 'social-add-tweet-by-url',
+				'title' => __('Add Tweet by URL', 'social')
+					.'<form class="social-add-tweet" style="display: none;" method="get" action="'.esc_url(wp_nonce_url(admin_url('options-general.php?social_controller=import&social_action=from_url&social_service=twitter&post_id='.$current_object->ID), 'from_url')).'">
+						<input type="text" size="20" name="url" value="" autocomplete="off" />
+						<input type="submit" name="social-add-tweet-button" name="social-add-tweet-button" value="'.__('Add Tweet by URL', 'social').'" />
+					</form>',
+				'href' => esc_url(get_edit_post_link($current_object->ID)),
+			));
+		}
+	}
+	
+	function admin_bar_footer_css() {
+?>
+<style class="text/css">
+#wpadminbar #wp-admin-bar-comments .social-aggregation-spinner {
+	background: transparent;
+	white-space: nowrap;
+}
+#wpadminbar .social-aggregation-spinner .dot-active {
+	font-weight: bold;
+}
+#wpadminbar #wp-admin-bar-social-add-tweet-by-url form {
+	display: block;
+	line-height: 100%;
+	margin: 0;
+	padding: 5px;
+}
+#wpadminbar #wp-admin-bar-social-add-tweet-by-url input {
+	color: #333;
+	font-size: 11px;
+	font-weight: normal;
+	line-height: 1;
+	margin-bottom: 3px;
+	padding: 3px;
+	text-shadow: none;
+	width: 90%;
+}
+#wpadminbar #wp-admin-bar-social-add-tweet-by-url input[type="submit"] {
+	margin: 0;
+}
+#wpadminbar #wp-admin-bar-social-add-tweet-by-url .loading {
+	background: url(<?php echo admin_url('images/wpspin_light.gif'); ?>) center center no-repeat;
+}
+#wpadminbar #wp-admin-bar-social-add-tweet-by-url p.msg {
+	color: #333;
+	font-size: 12px;
+	font-weight: normal;
+	margin: 0;
+	padding: 0;
+	text-align: center;
+	text-shadow: none;
+}
+#wpadminbar #wp-admin-bar-social-add-tweet-by-url p.error {
+	color: #900;
+}
+</style>
+<?php
+	}
+
+	function admin_bar_footer_js() {
+?>
+<script type="text/javascript">
+var socialAdminBarMsgs = {
+	'protected': '<?php echo esc_js(__('Protected Tweet', 'social')); ?>',
+	'invalid': '<?php echo esc_js(__('Invalid URL', 'social')); ?>',
+	'success': '<?php echo esc_js(__('Tweet Imported!', 'social')); ?>'
+};
+</script>
+<?php
+	}
+
+	/**
+	 * Runs the upgrade only if the installed version is older than the current version.
+	 *
+	 * @return void
+	 */
+	private function upgrade() {
+		define('SOCIAL_UPGRADE', true);
+		global $wpdb; // Don't delete, this is used in upgrade files.
+
+		$upgrades = array(
+			Social::$plugins_path.'upgrades/2.0.php',
+		);
+		$upgrades = apply_filters('social_upgrade_files', $upgrades);
+		foreach ($upgrades as $file) {
+			if (file_exists($file)) {
+				include_once $file;
+			}
+		}
+
+		Social::option('installed_version', Social::$version);
+	}
+
+	/**
+	 * Are there accounts connected?
+	 *
+	 * @return bool
+	 */
+	public function have_accounts() {
+		foreach ($this->services() as $service) {
+			if (count($service->accounts())) {
+				return true;
+			}
+		}
+
+		return false;
+	}
+
+	/**
+	 * Removes an account from the default broadcast accounts.
+	 *
+	 * @param  string  $service
+	 * @param  int     $id
+	 * @return void
+	 */
+	public function remove_from_default_accounts($service, $id) {
+		Social::log('Removing from default accounts #:id', array('id' => $id));
+		if (defined('IS_PROFILE_PAGE')) {
+			$defaults = get_user_meta(get_current_user_id(), 'social_default_accounts', true);
+		}
+		else {
+			$defaults = Social::option('default_accounts');
+		}
+
+		if (!empty($defaults) and isset($defaults[$service])) {
+			Social::log('Old default accounts: :accounts', array('accounts' => print_r($defaults, true)));
+
+			$_ids = array();
+			foreach ($defaults[$service] as $key => $_id) {
+				if ($_id != $id) {
+					$_ids[$key] = $_id;
+				}
+			}
+
+			// TODO abstract this to the Facebook plugin
+			if ($service == 'facebook' and isset($_ids['pages'])) {
+				$pages = $_ids['pages'];
+				unset($_ids['pages']);
+				sort($_ids);
+				foreach ($pages as $account_id => $account_pages) {
+					if ($account_id != $id) {
+						$_ids['pages'][$account_id] = $account_pages;
+					}
+				}
+			}
+
+			$defaults[$service] = $_ids;
+			if (!count($defaults[$service])) {
+				unset($defaults[$service]);
+			}
+			Social::log('New default accounts: :accounts', array('accounts' => print_r($defaults, true)));
+			if (defined('IS_PROFILE_PAGE')) {
+				update_user_meta(get_current_user_id(), 'social_default_accounts', $defaults);
+			}
+			else {
+				Social::option('default_accounts', $defaults);
+			}
+		}
+	}
+
+	/**
+	 * Recursively applies wp_kses() to an array/stdClass.
+	 *
+	 * @param  mixed  $object
+	 * @return mixed
+	 */
+	public function kses($object) {
+		if (is_object($object)) {
+			$_object = new stdClass;
+		}
+		else {
+			$_object = array();
+		}
+
+		foreach ($object as $key => $val) {
+			if (is_object($val) or is_array($val)) {
+				if (is_object($_object)) {
+					$_object->$key = $this->kses($val);
+				}
+				else if (is_array($_object)) {
+					$_object[$key] = $this->kses($val);
+				}
+			}
+			else {
+				if (is_object($_object)) {
+					$_object->$key = wp_kses($val, array());
+				}
+				else {
+					$_object[$key] = wp_kses($val, array());
+				}
+			}
+		}
+
+		return $_object;
+	}
+
+	/**
+	 * Handles the remote timeout requests for Social.
+	 *
+	 * @param  string  $url        url to request
+	 * @param  string  $nonce_key  key to use when generating the nonce
+	 * @param  bool    $post       set to true to do a wp_remote_post
+	 * @return void
+	 */
+	private function request($url, $nonce_key = null, $post = false) {
+		if ($nonce_key !== null) {
+			$url = str_replace('&amp;', '&', wp_nonce_url($url, $nonce_key));
+		}
+
+		$data = array(
+			'timeout' => 0.01,
+			'blocking' => false,
+			'sslverify' => apply_filters('https_local_ssl_verify', true),
+		);
+
+		if ($post) {
+			Social::log('POST request to: :url', array(
+				'url' => $url
+			));
+			wp_remote_post($url, $data);
+		}
+		else {
+			Social::log('GET request to: :url', array(
+				'url' => $url
+			));
+			wp_remote_get($url, $data);
+		}
+	}
+
+	/**
+	 * Loads the services.
+	 *
+	 * @return array
+	 */
+	private function load_services() {
+		if ((isset($_GET['page']) and $_GET['page'] == basename(SOCIAL_FILE)) or defined('IS_PROFILE_PAGE')) {
+			$services = false;
+		}
+		else {
+			$services = wp_cache_get('services', 'social');
+		}
+
+		if ($services === false) {
+			$services = array();
+			// Register services
+			$registered_services = apply_filters('social_register_service', array());
+			if (is_array($registered_services) and count($registered_services)) {
+				$accounts = Social::option('accounts');
+				foreach ($registered_services as $service) {
+					if (!isset($services[$service])) {
+						$service_accounts = array();
+
+						if (isset($accounts[$service]) and count($accounts[$service])) {
+							// Flag social as enabled, we have at least one account.
+							if ($this->_enabled === null) {
+								$this->_enabled = true;
+							}
+
+							foreach ($accounts[$service] as $account_id => $account) {
+								// TODO Shouldn't have to do this. Fix later.
+								$account->personal = '0';
+								$service_accounts[$account_id] = $account;
+							}
+						}
+
+						$class = 'Social_Service_'.$service;
+						$services[$service] = new $class($service_accounts);
+					}
+				}
+				wp_cache_set('services', $services, 'social');
+			}
+		}
+		else if ($this->_enabled === null and is_array($services)) {
+			foreach ($services as $service) {
+				if (count($service->accounts())) {
+					$this->_enabled = true;
+					break;
+				}
+			}
+		}
+
+// don't return global services for commenters
+		$commenter = get_user_meta(get_current_user_id(), 'social_commenter', true);
+		if ($commenter == 'true' && !current_user_can('publish_posts')) {
+			foreach ($services as $key => $accounts) {
+				$services[$key]->clear_accounts();
+			}
+		}
+
+		$personal_accounts = get_user_meta(get_current_user_id(), 'social_accounts', true);
+		if (is_array($personal_accounts)) {
+			foreach ($personal_accounts as $key => $_accounts) {
+				if (count($_accounts) and isset($services[$key])) {
+					$class = 'Social_Service_'.$key.'_Account';
+					foreach ($_accounts as $account_id => $account) {
+						// TODO Shouldn't have to do this. Fix later.
+						$account->universal = '0';
+						if ($services[$key]->account_exists($account_id) and !defined('IS_PROFILE_PAGE')) {
+							$account = $this->merge_accounts($services[$key]->account($account_id)->as_object(), $account, $key);
+						}
+						$account = new $class((object) $account);
+						$services[$key]->account($account);
+						// Flag social as enabled, we have at least one account.
+						if ($this->_enabled === null) {
+							$this->_enabled = true;
+						}
+					}
+				}
+			}
+		}
+
+		return $services;
+	}
+
+	/**
+	 * Merges universal with personal account.
+	 *
+	 * @param  array   $arr1
+	 * @param  array   $arr2
+	 * @param  string  $service_key
+	 * @return object
+	 */
+	private function merge_accounts($arr1, $arr2, $service_key) {
+		$arr1->personal = true;
+		return apply_filters('social_merge_accounts', $arr1, $arr2, $service_key);
+	}
+
+	/**
+	 * Checks to see if the array is an associative array.
+	 *
+	 * @param  array  $arr
+	 * @return bool
+	 */
+	private function is_assoc($arr) {
+		$keys = array_keys($arr);
+		return array_keys($keys) !== $keys;
+	}
+
+	/**
+	 * Builds the settings URL for the plugin.
+	 *
+	 * @param  array  $params
+	 * @param  bool   $personal
+	 * @return string
+	 */
+	public static function settings_url(array $params = null, $personal = false) {
+		if ($params !== null) {
+			foreach ($params as $key => $value) {
+				$params[$key] = urlencode($value);
+			}
+		}
+
+		if (!current_user_can('manage_options') or $personal) {
+			$file = 'profile.php';
+		}
+		else {
+			$file = 'options-general.php';
+			$params['page'] = basename(SOCIAL_FILE);
+		}
+
+		$url = add_query_arg($params, admin_url($file));
+
+		if (!current_user_can('manage_options') or $personal) {
+			$url .= '#social-networks';
+		}
+
+		return $url;
+	}
+	
+	/**
+	 * Filter the where clause for pulling comments for feeds (to exclude meta comments).
+	 *
+	 * @param  string  $where
+	 * @return string
+	 */
+	public static function comments_feed_exclusions($where) {
+		global $wpdb;
+		$meta_types = array();
+// get services
+		$services = Social::instance()->services();
+// ask each service for it's "meta" comment types
+		foreach ($services as $service) {
+			$meta_types = array_merge($meta_types, $service->comment_types_meta());
+		}
+		$meta_types = array_unique($meta_types);
+		if (count($meta_types)) {
+			$where .= " AND comment_type NOT IN ('".implode("', '", array_map('social_wpdb_escape', $meta_types))."') ";
+		}
+		return $where;
+	}
+	
+	/**
+	 * Filter the image tag to implement lazy loading support for meta comments.
+	 *
+	 * @param  string  $image_format
+	 * @return string
+	 */
+	public static function social_item_output_image_format($image_format) {
+		if (class_exists('LazyLoad_Images')) {
+			// would be great if the plugin provided an API for this, until then we'll copy the code
+			$placeholder_image = apply_filters( 'lazyload_images_placeholder_image', LazyLoad_Images::get_url( 'images/1x1.trans.gif' ) );
+			$image_format = '<img src="'.esc_url($placeholder_image).'" data-lazy-src="%1$s" width="%2$s" height="%3$s" alt="%4$s" /><noscript>'.$image_format.'</noscript>';
+		}
+		return $image_format;
+	}
+
+	/**
+	 * Filter the image tag to implement lazy loading support for avatars.
+	 *
+	 * @param  string  $image_format
+	 * @return string
+	 */
+	public static function social_get_avatar_image_format($image_format) {
+		if (class_exists('LazyLoad_Images')) {
+			// would be great if the plugin provided an API for this, until then we'll copy the code
+			$placeholder_image = apply_filters( 'lazyload_images_placeholder_image', LazyLoad_Images::get_url( 'images/1x1.trans.gif' ) );
+			$image_format = '<img alt="%1$s" src="'.esc_url($placeholder_image).'" data-lazy-src="%2$s" class="avatar avatar-%3$s photo %4$s" height="%3$s" width="%3$s" /><noscript>'.$image_format.'</noscript>';
+		}
+		return $image_format;
+	}
+
+} // End Social
+
+if (!function_exists('addslashes_deep')) {
+/**
+ * Navigates through an array and adds slashes to the values.
+ *
+ * If an array is passed, the array_map() function causes a callback to pass the
+ * value back to the function. Slashes will be added to this value.
+ *
+ * @since 3.4.0?
+ *
+ * @param array|string $value The array or string to be slashed.
+ * @return array|string Slashed array (or string in the callback).
+ */
+function addslashes_deep($value) {
+	if ( is_array($value) ) {
+		$value = array_map('addslashes_deep', $value);
+	} elseif ( is_object($value) ) {
+		$vars = get_object_vars( $value );
+		foreach ($vars as $key=>$data) {
+			$value->{$key} = addslashes_deep( $data );
+		}
+	} else {
+		$value = addslashes($value);
+	}
+
+	return $value;
+}
+}
+
+function social_wpdb_escape($str) {
+	global $wpdb;
+	return $wpdb->escape($str);
+}
+
+function social_wp_mail_indicator() {
+	define('SOCIAL_MAIL_PUBLISH', true);
+}
+
+$social_file = __FILE__;
+if (isset($plugin)) {
+	$social_file = $plugin;
+}
+else if (isset($mu_plugin)) {
+	$social_file = $mu_plugin;
+}
+else if (isset($network_plugin)) {
+	$social_file = $network_plugin;
+}
+
+define('SOCIAL_FILE', $social_file);
+define('SOCIAL_PATH', WP_PLUGIN_DIR.'/'.basename(dirname($social_file)).'/');
+
+// Register Social's autoloading
+spl_autoload_register(array('Social', 'auto_load'));
+
+$social = Social::instance();
+
+// General Actions
+add_action('init', array($social, 'init'), 1);
+add_action('init', array($social, 'request_handler'), 2);
+add_action('admin_init', array($social, 'admin_init'), 1);
+add_action('load-settings_page_social', array($social, 'check_system_cron'));
+add_action('load-social.php', array($social, 'check_system_cron'));
+add_action('comment_post', array($social, 'comment_post'));
+add_action('wp_set_comment_status', array($social, 'wp_set_comment_status'), 10, 3);
+add_action('admin_notices', array($social, 'admin_notices'));
+add_action('transition_post_status', array($social, 'transition_post_status'), 10, 3);
+add_action('show_user_profile', array($social, 'show_user_profile'));
+add_action('do_meta_boxes', array($social, 'do_meta_boxes'));
+add_action('delete_post', array($social, 'delete_post'));
+add_action('wp_enqueue_scripts', array($social, 'enqueue_assets'));
+add_action('load-post-new.php', array($social, 'enqueue_assets'));
+add_action('load-post.php', array($social, 'enqueue_assets'));
+add_action('load-profile.php', array($social, 'enqueue_assets'));
+add_action('load-settings_page_social', array($social, 'enqueue_assets'));
+add_action('admin_enqueue_scripts', array($social, 'admin_enqueue_assets'));
+add_action('admin_bar_menu', array($social, 'admin_bar_menu'), 95);
+add_action('wp_after_admin_bar_render', array($social, 'admin_bar_footer_css'));
+add_action('wp_after_admin_bar_render', array($social, 'admin_bar_footer_js'));
+add_action('set_user_role', array($social, 'set_user_role'), 10, 2);
+add_action('wp-mail.php', 'social_wp_mail_indicator');
+add_action('social_settings_save', array('Social_Service_Facebook', 'social_settings_save'));
+
+// CRON Actions
+add_action('social_cron_15_init', array($social, 'cron_15_init'));
+add_action('social_cron_15', array($social, 'run_aggregation'));
+
+// Admin Actions
+add_action('admin_menu', array($social, 'admin_menu'));
+add_action('personal_options_update', array($social, 'personal_options_update'));
+
+// Filters
+add_filter('cron_schedules', array($social,'cron_schedules'));
+add_filter('plugin_action_links', array($social, 'add_settings_link'), 10, 2);
+add_filter('redirect_post_location', array($social, 'redirect_post_location'), 10, 2);
+add_filter('comments_template', array($social, 'comments_template'));
+add_filter('get_avatar_comment_types', array($social, 'get_avatar_comment_types'));
+add_filter('get_avatar', array($social, 'get_avatar'), 10, 5);
+add_filter('register', array($social, 'register'));
+add_filter('loginout', array($social, 'loginout'));
+add_filter('post_row_actions', array($social, 'post_row_actions'), 10, 2);
+add_filter('social_comments_array', array($social, 'comments_array'), 100, 2);
+add_filter('comment_feed_where', array($social, 'comments_feed_exclusions'));
+add_filter('social_settings_default_accounts', array('Social_Service_Facebook', 'social_settings_default_accounts'), 10, 2);
+add_filter('social_item_output_image_format', array($social, 'social_item_output_image_format'));
+add_filter('social_get_avatar_image_format', array($social, 'social_get_avatar_image_format'));
+
+// Service filters
+add_filter('social_auto_load_class', array($social, 'auto_load_class'));
+
+} // End class_exists check