--- /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’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;"> (
+ <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('&', '&', 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