* @license GNU General Public License, version 2
* @link http://addthis.com AddThis website
*/
class AddThisPlugin
{
protected $enabledFeatures = array();
protected $registrationObject = null;
protected $globalOptionsObject = null;
protected $followButtonsObject = null;
protected $sharingButtonsObject = null;
protected $recommendedContentObject = null;
protected $registrationStatus = true;
protected $globalOptionsStatus = true;
protected $followButtonsStatus = false;
protected $sharingButtonsStatus = false;
protected $recommendedContentStatus = false;
protected $baseName = null;
protected $settingsLinkObject = 'globalOptionsObject';
protected static $cmsName = 'WordPress';
// adminJavaScriptAction needs to match here and in AddThisFeature
protected $adminJavaScriptAction = 'addthis_admin_variables';
// the order of the array below matters for JavaScript variable
// precidence make a protected variable that matches objectName above
// require the files with the objectName classes at the top of this file
protected $features = array(
'globalOptions' => array(
'name' => 'Global Options',
'objectName' => 'AddThisGlobalOptionsFeature',
),
'registration' => array(
'name' => 'Registration',
'objectName' => 'AddThisRegistrationFeature',
),
'followButtons' => array(
'name' => 'Follow Buttons',
'objectName' => 'AddThisFollowButtonsFeature',
),
'sharingButtons' => array(
'name' => 'Sharing Buttons',
'objectName' => 'AddThisSharingButtonsFeature',
),
'recommendedContent' => array(
'name' => 'Recommended Content',
'objectName' => 'AddThisRecommendedContentFeature',
),
);
/**
* The constructor.
*
* @param string $baseName the base name for this plugin
*
* @return null
*/
public function __construct($baseName)
{
$this->baseName = $baseName;
}
/**
* Returns the version of this plugin
*
* @return string
*/
public function getVersion()
{
return $this->version;
}
/**
* Returns the product version of this plugin for lojson params
*
* @return string
*/
public function getProductVersion()
{
$productVersion = $this->productPrefix . '-' . $this->getVersion();
return $productVersion;
}
/**
* Adds a a filter for the plugin_action_links area for this plugin, and
* adds the main feature's addSettingsLinkToPlugin function as a filter
* onto it. Essentially, this adds the settings link to the plugin
* listing page.
*
* @return null
*/
protected function addSettingsLinkToPlugin()
{
$callback = array($this, 'settingLinksFilter');
$filterName = 'plugin_action_links_'.$this->baseName;
if ($this->validateCallback($callback)) {
add_filter($filterName, $callback);
}
}
/**
* This must be public as it's used in a callback for
* plugin_action_links_$plugin filter
*
* Adds a link to the settings and registration pages for this plugin
* the WordPress plugin listing
*
* @param string[] $links an array of strings of HTML anchor tags
*
* @return string[] the input with added links
*/
public function settingLinksFilter($links)
{
$variableName = $this->settingsLinkObject;
if (is_object($this->$variableName)) {
$link = $this->$variableName->addSettingsLinkToPlugin();
array_push($links, $link);
}
if (is_object($this->registrationObject)) {
$link = $this->registrationObject->addSettingsLinkToPlugin();
array_push($links, $link);
}
return $links;
}
/**
* Marks each feature enabled by this plugin as disabled -- if another
* plugin also enables any of these feature, then it'll get enabled the
* next time that plugin bootraps
*
* @return null
*/
public function deactivate()
{
$gooConfigs = $this->globalOptionsObject->getConfigs();
foreach ($this->features as $feature => $info) {
$objectVariable = $feature . 'Object';
$enabledVariable = $feature . 'Status';
$enabledByPlugin = $this->$enabledVariable;
if ($enabledByPlugin && $feature != 'globalOptions') {
$enabledField = $this->$objectVariable->globalEnabledField;
$jsonField = $this->$objectVariable->globalLayersJsonField;
$globalOptionsConfigs[$enabledField] = false;
$globalOptionsConfigs[$jsonField] = '';
}
}
$this->globalOptionsObject->saveConfigs($globalOptionsConfigs);
}
/**
* This must be public as it's called from bootstrap.php
*
* This bootstraps this plugin into WordPress, including adding our
* JavaScript and CSS onto relevant pages and calling the bootstrap
* function for every enabled feature
*
* @return null
*/
public function bootstrap()
{
$goo = new $this->features['globalOptions']['objectName']();
$this->globalOptionsObject = $goo;
$reg = new $this->features['registration']['objectName']($goo);
$this->registrationObject = $reg;
$reg->bootstrap();
$goo->bootstrap();
$gooConfigs = $goo->getConfigs();
$saveGooConfigs = false;
/**
* We don't want to run updateProfileEdition unnecessarily as it
* talks to AddThis and blocks futher action until AddThis responds
* (to check the profile edition). It will only run here if this
* is an admin page that might be setting up widgets. We need this
* when registering widgets to know which ones to skip. See
* AddThisFeature::registerWidgets for details on why we do this
* here.
*/
if (is_admin()
&& !empty($_SERVER['REQUEST_URI'])
&& (substr($_SERVER['REQUEST_URI'], -11) == 'widgets.php'
|| strpos($_SERVER['REQUEST_URI'], 'customize.php'))
) {
$updatedProfileEdition = $goo->updateProfileEdition();
if ($updatedProfileEdition) {
$gooConfigs = $goo->getConfigs();
$saveGooConfigs = true;
}
}
foreach ($this->features as $feature => $info) {
$objectVariable = $feature . 'Object';
$objectName = $info['objectName'];
$enabledVariable = $feature . 'Status';
if ($feature != 'globalOptions' && $feature != 'registration') {
$featureObject = new $objectName($goo);
$this->$objectVariable = $featureObject;
// Does this plugin enable this feature? If so, bootstrap
if ($this->$enabledVariable) {
$this->enabledFeatures[] = $feature;
$this->$objectVariable->bootstrap();
// Does the plugin know it's enabled? If not, fix it.
$pluginKnowsItIsEnabled = $this->$objectVariable->isEnabled();
if (!$pluginKnowsItIsEnabled) {
$field = $this->$objectVariable->globalEnabledField;
$gooConfigs[$field] = true;
$saveGooConfigs = true;
}
} else {
$this->$objectVariable->getConfigs();
}
}
}
if (!isset($gooConfigs['addthis_plugin_controls'])) {
if ($gooConfigs['follow_buttons_feature_enabled'] === true
|| $gooConfigs['recommended_content_feature_enabled'] === true
|| $gooConfigs['sharing_buttons_feature_enabled'] === true
|| $gooConfigs['trending_content_feature_enabled'] === true
) {
$gooConfigs['addthis_plugin_controls'] = 'WordPress';
} else {
$gooConfigs['addthis_plugin_controls'] = 'AddThis';
}
$saveGooConfigs = true;
}
if ($saveGooConfigs) {
$goo->saveConfigs($gooConfigs);
}
// Add a link to the main settings page & the registration page
$this->addSettingsLinkToPlugin();
add_filter(
'language_attributes',
array($this, 'htmlNameSpacesAttributes')
);
if (is_admin()) {
// load our language files -- we only use them on admin pages
add_action(
'plugins_loaded',
array($this, 'loadTextDomain')
);
// addthis_share, addthis_config, addthis_layers and
// addthis_plugin_info for all public pages
add_action(
'wp_ajax_'.$this->globalOptionsObject->publicJavaScriptAction,
array($this, 'printJavaScriptForGlobalVariables')
);
add_action(
'wp_ajax_nopriv_'.$this->globalOptionsObject->publicJavaScriptAction,
array($this, 'printJavaScriptForGlobalVariables')
);
// make JavaScript file to ui relevant variables
add_action(
'wp_ajax_'.$this->adminJavaScriptAction,
array($this, 'printJavascriptForAdminUi')
);
} else {
$this->addScripts();
}
register_deactivation_hook(
$this->baseName,
array($this, 'deactivate')
);
}
/**
* This must be public as it's used for a callback for the
* plugins_loaded action
*
* Loads our language files needed in PHP for this plugin
*
* @return null
*/
public function loadTextDomain()
{
$path = '/' . $this->globalOptionsObject->getPluginFolder() . '/frontend/build/l10n';
load_plugin_textdomain(AddThisFeature::$l10n_domain, false, $path);
}
/**
* Setup out scripts to enqueue (if async is off), adds an action that
* echos our script onto page if async is turned on, and adds an action
* to enqueue our styles late. For all public pages.
*
* @return null
*/
protected function addScripts()
{
$gooConfigs = $this->globalOptionsObject->getConfigs();
if (!empty($gooConfigs['script_location'])
&& $gooConfigs['script_location'] == 'footer'
) {
$pageScriptHook = 'wp_footer';
} else {
$pageScriptHook = 'wp_head';
}
if (!empty($gooConfigs['addthis_asynchronous_loading'])) {
add_action(
$pageScriptHook,
array($this, 'printAddThisWidgetScript'),
19
);
} else {
add_action('wp_enqueue_scripts', array($this, 'enqueueScripts'));
}
// we want this to get enqueued after the theme css, thus the 18
add_action('wp_enqueue_scripts', array($this, 'enqueueStyles'), 18);
}
/**
* This must be public as it's used for a callback in the
* wp_enqueue_scripts action
*
* Enqueues our scripts the correct way when we don't have to do hacky
* async loading stuff
*
* @return null
*/
public function enqueueScripts()
{
$gooConfigs = $this->globalOptionsObject->getConfigs();
if (!empty($gooConfigs['script_location'])
&& $gooConfigs['script_location'] == 'footer'
) {
$enqueueInFooter = true;
} else {
$enqueueInFooter = false;
}
$bootstrapSettingsUrl = admin_url('admin-ajax.php') . '?action='.$this->globalOptionsObject->publicJavaScriptAction;
wp_enqueue_script(
'addthis_global_options',
$bootstrapSettingsUrl,
array(),
false,
$enqueueInFooter
);
$addThisWidgetUrl = $this->globalOptionsObject->getAddThisWidgetJavaScriptUrl();
$dependencies = array('addthis_global_options');
wp_enqueue_script(
'addthis_widget',
$addThisWidgetUrl,
$dependencies,
false,
$enqueueInFooter
);
}
/**
* This must be public as it's used for a callback in the
* wp_enqueue_scripts action
*
* Enqueues our styles super late, in the hopes that nothing else will
* enquque after us, and we win! This is done so that our CSS can win
* out over template CSS more often. For all public pages.
*
* @return null
*/
public function enqueueStyles()
{
$gooConfigs = $this->globalOptionsObject->getConfigs();
if (!empty($gooConfigs['script_location'])
&& $gooConfigs['script_location'] == 'footer'
) {
$enqueueInFooter = true;
} else {
$enqueueInFooter = false;
}
$cssRoot = $this->globalOptionsObject->getSettingsUiBaseUrl() . 'build/';
wp_enqueue_style(
'addthis_all_pages',
$cssRoot . 'addthis_wordpress_public.min.css',
array(),
false
);
}
/**
* This must be public as it's used for a callback in the filter
* language_attributes
*
* Checks for the Facebook Graph namespace and the AddThis namespace,
* and adds it if it doesn't already exist in the input string.
*
* @param string $input the string of attributes to be added the pages
* HTML tag
*
* @return string the string of attributes to be added to the pages HTML
* tag with Facebook and AddThis names spaces added if thye wheren't
* there already
*/
public function htmlNameSpacesAttributes($input)
{
$gooConfigs = $this->globalOptionsObject->getConfigs();
if (empty($gooConfigs['xmlns_attrs'])) {
return $input;
}
$output = trim($input);
$attrs = array(
array(
'attr' => 'xmlns:fb',
'value' => 'http://ogp.me/ns/fb#',
),
array(
'attr' => 'xmlns:addthis',
'value' => 'http://www.addthis.com/help/api-spec',
),
);
foreach ($attrs as $info) {
if (strpos($input, $info['attr']) === false) {
$output .= ' ' . $info['attr']
. '="' . $info['value'] . '"';
}
}
$output .= ' ';
return $output;
}
/**
* Prints the JavaScript that should be used to set addthis_share,
* addthis_config, addthis_plugin_info, as well as the params to use
* when loading addthis.layers
*
* @return null
*/
public function printJavaScriptForGlobalVariables()
{
global $post;
if (is_object($post) && !empty($post->ID) && is_feed()) {
return null;
}
header('Content-type: application/x-javascript');
$javaScript = $this->getJavascriptForGlobalVariables();
echo $javaScript;
die();
}
/**
* Returns a string of JavaScript that should be used to set
* addthis_share, addthis_config, addthis_plugin_info, as well as the
* params to use when loading addthis.layers
*
* @return string
*/
public function getJavascriptForGlobalVariables()
{
$share = array();
$config = array();
$layers = array();
$layersTools = array();
$pluginInfo = $this->getAddThisPluginInfo();
$gooSettings = $this->globalOptionsObject->getConfigs();
$updateGlobalOptionsSettings = false;
reset($this->features);
foreach ($this->features as $feature => $info) {
$objectVariable = $feature . 'Object';
$featureObject = $this->$objectVariable;
if ($featureObject->isEnabled()) {
$featureShare = $featureObject->getAddThisShare();
$share = array_replace_recursive($share, $featureShare);
$featureConfig = $featureObject->getAddThisConfig();
$config = array_replace_recursive($config, $featureConfig);
$featureLayers = $featureObject->getAddThisLayers();
if ($this->globalOptionsObject->inAnonymousMode()) {
$layers = array_replace_recursive($layers, $featureLayers);
}
$featureLayersTools = $featureObject->getAddThisLayersTools();
if ($this->globalOptionsObject->inAnonymousMode()) {
$layersTools = array_merge($layersTools, $featureLayersTools);
}
// save a layers json string for the sharing buttons plugin
if (!empty($featureObject->globalLayersJsonField)) {
$new = json_encode((object)$featureLayers);
$field = $featureObject->globalLayersJsonField;
if (isset($gooSettings[$field])) {
$old = $gooSettings[$field];
}
if ((empty($old) && !empty($featureLayers))
|| (!empty($old) && $old != $new)
) {
$gooSettings[$field] = $new;
$updateGlobalOptionsSettings = true;
}
}
}
}
// save a layers json string for the sharing buttons plugin
if ($updateGlobalOptionsSettings) {
$this->globalOptionsObject->saveConfigs($gooSettings);
}
if (!empty($gooSettings['addthis_share_json'])) {
$shareFromSettings = json_decode($gooSettings['addthis_share_json'], true);
$share = array_replace_recursive($share, $shareFromSettings);
}
$share = apply_filters('addthis_share_array', $share);
$shareJson = json_encode((object)$share);
$shareJson = apply_filters('addthis_share_json', $shareJson, $share);
if (!empty($gooSettings['addthis_config_json'])) {
$configFromSettings = json_decode($gooSettings['addthis_config_json'], true);
$config = array_replace_recursive($config, $configFromSettings);
}
$config = apply_filters('addthis_config_array', $config);
$configJson = json_encode((object)$config);
$configJson = apply_filters('addthis_config_json', $configJson, $config);
if (!empty($gooSettings['addthis_layers_json'])) {
$layersFromSettings = json_decode($gooSettings['addthis_layers_json'], true);
$layers = array_replace_recursive($layers, $layersFromSettings);
}
$layers = apply_filters('addthis_layers_array', $layers);
$layersJson = json_encode((object)$layers);
$layersJson = apply_filters('addthis_layers_json', $layersJson, $layers);
$layersToolsJson = json_encode((array)$layersTools);
$layersToolsAlreadyDefined = '';
foreach ($layersTools as $toolLayersConfig) {
$toolLayersConfig = json_encode((object)$toolLayersConfig);
$layersToolsAlreadyDefined .= 'window.addthis_layers_tools.push('.$toolLayersConfig.'); ';
}
$pluginInfoJson = json_encode((object)$pluginInfo);
$javaScript = '
if (window.addthis_product === undefined) {
window.addthis_product = "' . $this->productPrefix . '";
}
if (window.wp_product_version === undefined) {
window.wp_product_version = "' . $this->getProductVersion() . '";
}
if (window.wp_blog_version === undefined) {
window.wp_blog_version = "' . $this->getCmsVersion() . '";
}
if (window.addthis_share === undefined) {
window.addthis_share = ' . $shareJson . ';
}
if (window.addthis_config === undefined) {
window.addthis_config = ' . $configJson . ';
}
if (window.addthis_layers === undefined) {
window.addthis_layers = ' . $layersJson . ';
}
if (window.addthis_layers_tools === undefined) {
window.addthis_layers_tools = ' . $layersToolsJson . ';
} else {
'.$layersToolsAlreadyDefined.'
}
if (window.addthis_plugin_info === undefined) {
window.addthis_plugin_info = ' . $pluginInfoJson . ';
}
'.$this->getJavaScriptToLoadTools().'
';
$javaScript = apply_filters('addthis_config_javascript', $javaScript);
return $javaScript;
}
/**
* Returns a string of JavaScript that can be used to bootstrap tools
*
* @return string JavaScript for bootstraping AddThis tools
*/
public function getJavaScriptToLoadTools()
{
$gooSettings = $this->globalOptionsObject->getConfigs();
if (empty($gooSettings['ajax_support'])) {
$javaScript = '
(function() {
var first_load_interval_id = setInterval(function () {
if (typeof window.addthis !== \'undefined\') {
window.clearInterval(first_load_interval_id);
window.addthis.layers(window.addthis_layers);
for (i = 0; i < window.addthis_layers_tools.length; i++) {
window.addthis.layers(window.addthis_layers_tools[i]);
}
}
},1000)
}());
';
} else {
$javaScript = '
(function() {
var new_tools_timeout = false;
var refresh_tools = function() {
new_tools_timeout = false;
addthis.layers.refresh();
};
var first_load_check = function () {
if (typeof window.addthis !== \'undefined\') {
window.clearInterval(first_load_interval_id);
window.addthis.layers(window.addthis_layers);
for (i = 0; i < window.addthis_layers_tools.length; i++) {
window.addthis.layers(window.addthis_layers_tools[i]);
}
window.atnt = function() {
if (new_tools_timeout !== false) {
window.clearTimeout(new_tools_timeout);
}
new_tools_timeout = window.setTimeout(refresh_tools, 15);
};
}
};
var first_load_interval_id = window.setInterval(first_load_check, 1000);
}());
';
}
return $javaScript;
}
/**
* Creates plugin specific settings for the JavaScript variable
* addthis_plugin_info
*
* @return array an associative array
*/
public function getAddThisPluginInfo()
{
$pluginInfo = array();
$pluginInfo = array();
$pluginInfo['info_status'] = 'enabled';
$pluginInfo['cms_name'] = $this->getCmsName();
$pluginInfo['cms_version'] = $this->getCmsVersion();
$pluginInfo['plugin_name'] = $this->name;
$pluginInfo['plugin_version'] = $this->getVersion();
if ($this->globalOptionsObject->inAnonymousMode()) {
$pluginInfo['plugin_mode'] = $this->getCmsName();
} else {
$pluginInfo['plugin_mode'] = 'AddThis';
}
$pluginInfo['anonymous_profile_id'] = $this->globalOptionsObject->getAnonymousProfileId();
if ($this->globalOptionsObject->checkForEditPermissions(false)) {
$pluginInfo['php_version'] = phpversion();
}
// post specific stuff that requreis wp_query
global $wp_query;
if (isset($wp_query)) {
$pluginInfo['page_info']['template'] = AddThisTool::currentTemplateType();
if (isset($wp_query->query_vars['post_type'])) {
$pluginInfo['page_info']['post_type'] = $wp_query->query_vars['post_type'];
}
}
// post specific meta box selection
global $post;
if (isset($post)) {
$at_flag = get_post_meta($post->ID, '_at_widget', true);
if ($at_flag === '0') {
$pluginInfo['sharing_enabled_on_post_via_metabox'] = false;
} else {
$pluginInfo['sharing_enabled_on_post_via_metabox'] = true;
}
}
return $pluginInfo;
}
/**
* Prints the JavaScript that should be used to include
* addthis_widget.js on the page.
*
* @return null
*/
public function printAddThisWidgetScript()
{
global $post;
if ($post->ID && is_feed()) {
return null;
}
$configs = $this->globalOptionsObject->getConfigs();
$addthisWidgetUrl = $this->globalOptionsObject->getAddThisWidgetJavaScriptUrl();
$script = '
';
echo $script;
}
/**
* The profile ID that should be used when loading addthis_widget.js
*
* @return string a profile ID
*/
public function getUsableProfileId()
{
$profileId = $this->globalOptionsObject ->getUsableProfileId();
return $profileId;
}
/**
* Validates that the WordPress callback passed is valid.
*
* @param string|array $callback The name of a global function as a
* string, or an array with the first item being an object, and the
* second being a string naming a function in that object.
*
* @return boolean true for valid, false otherwise
*/
public static function validateCallback($callback)
{
if (empty($callback[0])) {
return false;
}
if (empty($callback[1])) {
// check if string
$methodName = $callback;
if (!is_string($methodName)) {
return false;
}
if (!function_exists($methodName)) {
return false;
}
return true;
}
$object = $callback[0];
$methodName = $callback[1];
if (!is_object($object)) {
return false;
} elseif (!method_exists($object, $methodName)) {
return false;
}
return true;
}
/**
* The name of the content management system this plugin runs within
*
* @return string
*/
private static function getCmsName()
{
return self::$cmsName;
}
/**
* The full version number of the CMS (ie. 4.2.2, not 4.2)
*
* @return string
*/
private static function getCmsVersion()
{
$version = get_bloginfo('version');
return $version;
}
/**
* The major and minor version of the CMS (ie. 4.2, not 4.2.2)
*
* @return string
*/
private static function getCmsMinorVersion()
{
$stringVersion = substr(self::getCmsVersion(), 0, 3);
$version = (float)$stringVersion;
return $version;
}
/**
* This must be public as it's used in a callback for add_action
*
* Prints JavaScript to make site specific variables available to the UI
*
* @return null
*/
public function printJavascriptForAdminUi()
{
$this->globalOptionsObject->checkForEditPermissions(true);
header('Content-type: application/x-javascript');
$current_user = wp_get_current_user();
$config = array(
'ignore_server_config' => true,
);
$ui = array(
'defaults' => array(
'rss' => get_bloginfo('rss2_url'),
'email' => $current_user->user_email,
'domain' => $this->globalOptionsObject->getSiteDomain(),
),
'urls' => array(
'home' => home_url(),
'admin' => admin_url(),
'ajax' => admin_url('admin-ajax.php'),
'widgets' => admin_url('widgets.php'),
'ui' => $this->globalOptionsObject->getSettingsUiBaseUrl(),
'settings' => $this->globalOptionsObject->getSettingsPageUrl(),
),
'plugin' => array(
'slug' => $this->pluginSlug,
'pco' => $this->productPrefix,
'version' => $this->getVersion(),
),
'siteName' => get_bloginfo('name'),
'language' => get_bloginfo('language'),
'locale' => get_locale(),
);
$configJson = json_encode((object)$config);
$uiJson = json_encode((object)$ui);
$javaScript = '
window.addthis_config = ' . $configJson . ';
window.addthis_ui = ' . $uiJson . ';
';
echo $javaScript;
die();
}
}
}