array(),
'error' => array(),
);
/**
* Constructor
*
* @since 3.0.0
*
* @param object $base Base Plugin Class.
*/
public function __construct( $base ) {
// Store base class.
$this->base = $base;
// Actions.
add_action( 'init', array( $this, 'maybe_get_access_token' ) );
add_action( 'init', array( $this, 'oauth' ) );
add_action( 'init', array( $this, 'check_plugin_setup' ) );
add_action( 'admin_notices', array( $this, 'admin_notices' ) );
add_action( 'admin_enqueue_scripts', array( $this, 'admin_scripts_css' ) );
add_action( 'admin_menu', array( $this, 'admin_menu' ) );
add_filter( 'plugin_action_links_' . $this->base->plugin->name . '/' . $this->base->plugin->name . '.php', array( $this, 'plugin_action_links_settings_page' ) );
}
/**
* Exchanges the authorization code for an access token, if included in the request.
*
* @since 6.0.0
*/
public function maybe_get_access_token() {
// If a code is included in the request, exchange it for an access token.
if ( ! filter_has_var( INPUT_GET, $this->base->plugin->settingsName . '-code' ) ) {
return;
}
// Bail if nonce is not valid.
if ( ! isset( $_REQUEST['_wpnonce'] ) || ! wp_verify_nonce( sanitize_key( wp_unslash( $_REQUEST['_wpnonce'] ) ), $this->base->plugin->filter_name . '_oauth' ) ) {
return;
}
// Bail if the current user cannot manage the plugin's settings.
if ( ! current_user_can( 'manage_options' ) ) {
return;
}
// Setup notices class.
$this->base->get_class( 'notices' )->set_key_prefix( $this->base->plugin->filter_name . '_' . wp_get_current_user()->ID );
// Sanitize token.
$authorization_code = filter_input( INPUT_GET, $this->base->plugin->settingsName . '-code', FILTER_SANITIZE_FULL_SPECIAL_CHARS );
// Exchange the authorization code and verifier for an access token.
$tokens = $this->base->get_class( 'api' )->get_access_token( $authorization_code );
// If an error occured, add it to the notices.
if ( is_wp_error( $tokens ) ) {
$this->base->get_class( 'notices' )->add_error_notice( $tokens->get_error_message() );
return;
}
// Store messages.
$this->base->get_class( 'notices' )->enable_store();
// Fetch Organizations.
$organizations = $this->base->get_class( 'api' )->organizations( true );
// If an error occured, add it to the notices.
if ( is_wp_error( $organizations ) ) {
$this->base->get_class( 'notices' )->add_error_notice( $organizations->get_error_message() );
return;
}
// If an account ID is included in the request, delete that account before adding the account.
// This handles account re-connection where we're coming from the old API.
$existing_account_id = false;
if ( filter_has_var( INPUT_GET, 'account_id' ) ) {
$existing_account_id = filter_input( INPUT_GET, 'account_id', FILTER_SANITIZE_FULL_SPECIAL_CHARS );
$this->base->get_class( 'settings' )->delete_account( $existing_account_id );
}
// For each organization, fetch the profiles and store the organization as an account in the Plugin.
foreach ( $organizations as $account ) {
// If the existing account ID is set, and it matches the current account ID, skip.
if ( $existing_account_id && $existing_account_id !== 'default' && $existing_account_id !== $account['id'] ) {
continue;
}
// Fetch Profiles.
$profiles = $this->base->get_class( 'api' )->profiles( true, $account['id'] );
// If something went wrong, show an error.
if ( is_wp_error( $profiles ) ) {
$this->base->get_class( 'notices' )->add_error_notice( $profiles->get_error_message() );
continue;
}
// Update account.
$this->base->get_class( 'settings' )->update_account(
$tokens['access_token'],
$tokens['refresh_token'],
$tokens['token_expires'],
$account['id'],
$account['name'],
$account['email'],
$account['channel_limit'],
$account['plan'],
array_keys( $profiles )
);
}
// Store success message.
$this->base->get_class( 'notices' )->add_success_notice(
sprintf(
/* translators: %1$s: Social Media Service Name (Buffer, Hootsuite), %2$s: Social Media Service Name (Buffer, Hootsuite) */
__( 'Thanks! You\'ve connected our Plugin to %1$s. Now select profiles below to enable, and define your statuses to start sending Posts to %2$s', 'wp-to-buffer' ),
$this->base->plugin->account,
$this->base->plugin->account
)
);
// Redirect to Post tab.
wp_safe_redirect( 'admin.php?page=' . $this->base->plugin->name . '-settings&tab=post&type=post' );
die();
}
/**
* Handles displaying any errors from the OAuth process, and storing the access token if supplied,
* when the OAuth gateway exchanges the authorization code for an access token.
*
* Used by:
* - WP to Hootsuite
* - WP to Hootsuite Pro
*
* @since 3.3.3
*/
public function oauth() {
// Setup notices class.
$this->base->get_class( 'notices' )->set_key_prefix( $this->base->plugin->filter_name . '_' . wp_get_current_user()->ID );
// Bail if nonce is not valid, to prevent OAuth callback CSRF.
if ( ! isset( $_REQUEST['_wpnonce'] ) || ! wp_verify_nonce( sanitize_key( wp_unslash( $_REQUEST['_wpnonce'] ) ), $this->base->plugin->filter_name . '_oauth' ) ) {
return;
}
// Bail if the current user cannot manage the plugin's settings.
if ( ! current_user_can( 'manage_options' ) ) {
return;
}
/**
* Perform any pre-oAuth actions now, such as starting the oAuth process
*
* @since 4.2.0
*/
do_action( $this->base->plugin->filter_name . '_save_settings_auth' );
// If we've returned from the oAuth process and an error occured, add it to the notices.
if ( filter_has_var( INPUT_GET, $this->base->plugin->settingsName . '-oauth-error' ) ) {
$oauth_error = filter_input( INPUT_GET, $this->base->plugin->settingsName . '-oauth-error', FILTER_SANITIZE_FULL_SPECIAL_CHARS );
switch ( $oauth_error ) {
/**
* Access Denied
* - User denied our app access
*/
case 'access_denied':
$this->base->get_class( 'notices' )->add_error_notice(
sprintf(
/* translators: %1$s: Social Media Service Name (Buffer, Hootsuite), %2$s: Social Media Service Name (Buffer, Hootsuite) */
__( 'You did not grant our Plugin access to your %1$s account. We are unable to post to %2$s until you do this. Please click on the Authorize Plugin button.', 'wp-to-buffer' ),
$this->base->plugin->account,
$this->base->plugin->account
)
);
break;
/**
* Invalid Grant
* - A parameter sent by the oAuth gateway is wrong
*/
case 'invalid_grant':
$this->base->get_class( 'notices' )->add_error_notice(
sprintf(
'%1$s %3$s',
sprintf(
/* translators: Social Media Service Name (Buffer, Hootsuite) */
__( 'We were unable to complete authentication with %s. Please try again, or', 'wp-to-buffer' ),
$this->base->plugin->account
),
esc_html( $this->base->plugin->support_url ),
__( 'contact us for support', 'wp-to-buffer' )
)
);
break;
/**
* Expired Token
* - The oAuth gateway did not exchange the code for an access token within 30 seconds
*/
case 'expired_token':
$this->base->get_class( 'notices' )->add_error_notice(
sprintf(
'%1$s %3$s %4$s',
__( 'The oAuth process has expired. Please try again, or', 'wp-to-buffer' ),
esc_html( $this->base->plugin->support_url ),
__( 'contact us for support', 'wp-to-buffer' ),
__( 'if this issue persists.', 'wp-to-buffer' )
)
);
break;
/**
* Other Error
*/
default:
$this->base->get_class( 'notices' )->add_error_notice(
filter_input( INPUT_GET, $this->base->plugin->settingsName . '-oauth-error', FILTER_SANITIZE_FULL_SPECIAL_CHARS )
);
break;
}
}
// If an Access Token is included in the request, store it and show a success message.
if ( filter_has_var( INPUT_GET, $this->base->plugin->settingsName . '-oauth-access-token' ) ) {
// Define tokens and expiry.
$access_token = filter_input( INPUT_GET, $this->base->plugin->settingsName . '-oauth-access-token', FILTER_SANITIZE_FULL_SPECIAL_CHARS );
$refresh_token = filter_input( INPUT_GET, $this->base->plugin->settingsName . '-oauth-refresh-token', FILTER_SANITIZE_FULL_SPECIAL_CHARS );
$expiry = filter_input( INPUT_GET, $this->base->plugin->settingsName . '-oauth-expires', FILTER_SANITIZE_FULL_SPECIAL_CHARS );
if ( $expiry > 0 ) {
$expiry = strtotime( '+' . $expiry . ' seconds' );
}
// Setup API.
$this->base->get_class( 'api' )->set_tokens( $access_token, $refresh_token, $expiry );
// Fetch Account.
$account = $this->base->get_class( 'api' )->account();
// If something went wrong, show an error.
if ( is_wp_error( $account ) ) {
$this->base->get_class( 'notices' )->add_error_notice( $account->get_error_message() );
return;
}
// Fetch Profiles.
$profiles = $this->base->get_class( 'api' )->profiles( true, $account['id'] );
// If something went wrong, show an error.
if ( is_wp_error( $profiles ) ) {
$this->base->get_class( 'notices' )->add_error_notice( $profiles->get_error_message() );
return;
}
// Test worked! Save Tokens and Expiry.
$this->base->get_class( 'settings' )->update_account(
$access_token,
$refresh_token,
$expiry,
$account['id'],
$account['name'],
$account['email'],
$account['channel_limit'],
$account['plan'],
array_keys( $profiles )
);
// Store success message.
$this->base->get_class( 'notices' )->enable_store();
$this->base->get_class( 'notices' )->add_success_notice(
sprintf(
/* translators: %1$s: Social Media Service Name (Buffer, Hootsuite), %2$s: Social Media Service Name (Buffer, Hootsuite) */
__( 'Thanks! You\'ve connected our Plugin to %1$s. Now select profiles below to enable, and define your statuses to start sending Posts to %2$s', 'wp-to-buffer' ),
$this->base->plugin->account,
$this->base->plugin->account
)
);
// Redirect to Post tab.
wp_safe_redirect( 'admin.php?page=' . $this->base->plugin->name . '-settings&tab=post&type=post' );
die();
}
}
/**
* Checks that the oAuth authorization flow has been completed, and that
* at least one Post Type with one Social Media account has been enabled.
*
* Displays a dismissible WordPress notification if this has not been done.
*
* @since 1.0.0
*/
public function check_plugin_setup() {
// Show an error if cURL hasn't been installed.
if ( ! function_exists( 'curl_init' ) ) {
$this->base->get_class( 'notices' )->add_error_notice(
sprintf(
/* translators: Plugin Name */
__( '%s requires the PHP cURL extension to be installed and enabled by your web host.', 'wp-to-buffer' ),
$this->base->plugin->displayName
)
);
}
// Don't display the notice if this request is for the settings auth screen.
$screen = $this->base->get_class( 'screen' )->get_current_screen();
if ( $screen['screen'] === 'settings' && $screen['section'] === 'auth' ) {
return;
}
// Check the API is connected.
if ( ! $this->base->get_class( 'settings' )->account_connected() ) {
// Display the notice.
$this->base->get_class( 'notices' )->add_error_notice(
sprintf(
'%1$s %3$s',
sprintf(
/* translators: %1$s: Plugin Name, %2$s, %3$s: Social Media Service Name (Buffer, Hootsuite), %4$s: URL to Authorize Plugin Screen, %5$s: URL to Register Account with Service */
esc_html__( '%1$s needs to be authorized with %2$s before you can start sending Posts to %3$s.', 'wp-to-buffer' ),
$this->base->plugin->displayName,
$this->base->plugin->account,
$this->base->plugin->account
),
admin_url( 'admin.php?page=' . $this->base->plugin->name . '-settings' ),
esc_html__( 'Click here to Authorize.', 'wp-to-buffer' )
)
);
}
// Buffer: If an access token begins with '2/', it's from the old API.
$accounts = $this->base->get_class( 'settings' )->get_accounts();
foreach ( $accounts as $account ) {
if ( strpos( $account['access_token'], '2/' ) === 0 ) {
$this->base->get_class( 'notices' )->add_error_notice(
sprintf(
/* translators: %1$s: Plugin Name, %2$s: Social Media Service Name (Buffer, Hootsuite) */
__( '%1$s uses a new API. Please click the `Reconnect` button at %2$s Settings > Authentication to reconnect your account. You won\'t need to do this again.', 'wp-to-buffer' ),
$this->base->plugin->displayName,
$this->base->plugin->account
)
);
}
}
}
/**
* Checks the transient to see if any admin notices need to be output now.
*
* @since 3.9.6
*/
public function admin_notices() {
// Output notices.
$this->base->get_class( 'notices' )->set_key_prefix( $this->base->plugin->filter_name . '_' . wp_get_current_user()->ID );
$this->base->get_class( 'notices' )->output_notices();
}
/**
* Register and enqueue any JS and CSS for the WordPress Administration
*
* @since 1.0.0
*/
public function admin_scripts_css() {
global $id, $post;
// Get current screen.
$screen = $this->base->get_class( 'screen' )->get_current_screen();
// CSS - always load.
wp_enqueue_style( $this->base->plugin->name, $this->base->plugin->url . 'lib/social/assets/css/admin.css', array(), $this->base->plugin->version );
// Define CSS variables for design.
wp_register_style( $this->base->plugin->name . '-vars', false, array(), $this->base->plugin->version );
wp_enqueue_style( $this->base->plugin->name . '-vars' );
wp_add_inline_style(
$this->base->plugin->name . '-vars',
trim(
':root {
--wpzinc-logo: url(\'' . esc_attr( $this->base->plugin->logo ) . '\');
--wpzinc-header-background-color: ' . esc_attr( $this->base->plugin->header_background_color ) . ';
--wpzinc-header-primary-text-color: ' . esc_attr( $this->base->plugin->header_primary_text_color ) . ';
--wpzinc-header-secondary-text-color: ' . esc_attr( $this->base->plugin->header_secondary_text_color ) . ';
--wpzinc-plugin-display-name: "' . esc_attr( $this->base->plugin->displayName ) . ' ";
}'
)
);
// Don't load anything else if we're not on a Plugin or Post screen.
if ( ! $screen['screen'] ) {
return;
}
// Determine whether to load minified versions of JS.
$minified = $this->base->dashboard->should_load_minified_js();
// Define JS and localization.
wp_register_script( $this->base->plugin->name . '-log', $this->base->plugin->url . 'lib/social/assets/js/' . ( $minified ? 'min/' : '' ) . 'log' . ( $minified ? '-min' : '' ) . '.js', array( 'jquery' ), $this->base->plugin->version, true );
wp_register_script( $this->base->plugin->name . '-statuses', $this->base->plugin->url . 'lib/social/assets/js/' . ( $minified ? 'min/' : '' ) . 'statuses' . ( $minified ? '-min' : '' ) . '.js', array( 'jquery' ), $this->base->plugin->version, true );
// Define localization for statuses.
$localization = array(
'ajax' => admin_url( 'admin-ajax.php' ),
'clear_log_nonce' => wp_create_nonce( $this->base->plugin->name . '-clear-log' ),
'clear_log_completed' => sprintf(
/* translators: Social Media Service Name (Buffer, Hootsuite) */
__( 'No log entries exist, or no status updates have been sent to %s.', 'wp-to-buffer' ),
$this->base->plugin->account
),
'get_log_nonce' => wp_create_nonce( $this->base->plugin->name . '-get-log' ),
'delete_condition_message' => __( 'Are you sure you want to delete this condition?', 'wp-to-buffer' ),
'delete_status_message' => __( 'Are you sure you want to delete this status?', 'wp-to-buffer' ),
'get_status_row_action' => $this->base->plugin->filter_name . '_get_status_row',
'get_status_row_nonce' => wp_create_nonce( $this->base->plugin->name . '-get-status-row' ),
'post_id' => ( isset( $post->ID ) ? $post->ID : (int) $id ),
// Plugin specific Status Form Container and Status Form, so statuses.js knows where to look for the form
// relative to this Plugin.
'plugin_name' => $this->base->plugin->name,
'status_form_container' => '#' . $this->base->plugin->name . '-status-form-container',
'status_form' => '#' . $this->base->plugin->name . '-status-form',
// status.js appends profile service to this e.g. twitter,facebook.
'usernames_search_action' => $this->base->plugin->filter_name . '_usernames_search_',
);
// If here, we're on a Plugin or Post screen.
// Conditionally load scripts and styles depending on which section of the Plugin we're loading.
switch ( $screen['screen'] ) {
/**
* Post
*/
case 'post':
switch ( $screen['section'] ) {
/**
* WP_List_Table
*/
case 'wp_list_table':
break;
/**
* Add/Edit
*/
case 'edit':
// Plugin JS.
wp_enqueue_script( $this->base->plugin->name . '-log' );
// Localize.
wp_localize_script( $this->base->plugin->name . '-log', 'wpzinc_social', $localization );
break;
}
break;
/**
* Settings
*/
case 'settings':
// JS.
wp_enqueue_script( 'wpzinc-admin-conditional' );
wp_enqueue_media();
wp_enqueue_script( 'wpzinc-admin-tabs' );
wp_enqueue_script( 'wpzinc-admin' );
switch ( $screen['section'] ) {
/**
* General
*/
case 'auth':
break;
/**
* Post Type
*/
default:
// JS.
wp_enqueue_script( 'wpzinc-admin-autocomplete' );
wp_enqueue_script( 'wpzinc-admin-autosize' );
wp_enqueue_script( 'wpzinc-admin-modal' );
wp_enqueue_script( 'jquery-ui-sortable' );
// Plugin JS.
wp_enqueue_script( $this->base->plugin->name . '-statuses' );
// Add Twitter Username Save Action and Nonce.
$localization['username_save_twitter_action'] = $this->base->plugin->filter_name . '_username_save_twitter';
$localization['username_save_twitter_nonce'] = wp_create_nonce( $this->base->plugin->name . '-username-save-twitter' );
// Localize.
wp_localize_script( $this->base->plugin->name . '-settings', 'wpzinc_social', $localization );
// Add Post Type, Action and Nonce to allow AJAX saving.
$localization['post_type'] = $this->get_post_type_tab();
$localization['prompt_unsaved_changes'] = true;
$localization['save_statuses_action'] = $this->base->plugin->filter_name . '_save_statuses';
$localization['save_statuses_modal'] = array(
'title' => __( 'Saving', 'wp-to-buffer' ),
'title_success' => __( 'Saved!', 'wp-to-buffer' ),
);
$localization['save_statuses_nonce'] = wp_create_nonce( $this->base->plugin->name . '-save-statuses' );
// Localize Statuses.
wp_localize_script( $this->base->plugin->name . '-statuses', 'wpzinc_social', $localization );
// Localize Autocomplete.
wp_localize_script( 'wpzinc-admin-autocomplete', 'wpzinc_autocomplete', $this->get_autocomplete_configuration( $localization['post_type'] ) );
break;
}
break;
/**
* Log
*/
case 'log':
// Plugin JS.
wp_enqueue_script( $this->base->plugin->name . '-log' );
// Localize.
wp_localize_script( $this->base->plugin->name . '-log', 'wpzinc_social', $localization );
break;
}
}
/**
* Returns configuration for tribute.js autocomplete instances for Tags, Facebook Pages and Twitter Username mentions.
*
* @since 4.5.7
*
* @param string $post_type Post Type.
* @return array Javascript Autocomplete Configuration
*/
private function get_autocomplete_configuration( $post_type ) {
$autocomplete_configuration = array(
// Tags.
array(
'fields' => array(
'textarea.message',
'input.url',
),
'triggers' => array(
// Tags.
array(
'trigger' => '{',
'values' => $this->base->get_class( 'common' )->get_tags_flat( $post_type ),
),
),
),
);
/**
* Defines configuration for tribute.js autocomplete instances for Tags, Facebook Pages and Twitter Username mentions.
*
* @since 4.5.7
*
* @param array $autocomplete_configuration Javascript Autocomplete Configuration.
* @param string $post_type Post Type.
*/
$autocomplete_configuration = apply_filters( $this->base->plugin->filter_name . '_admin_get_autocomplete_configuration', $autocomplete_configuration );
// Return.
return $autocomplete_configuration;
}
/**
* Add the Plugin to the WordPress Administration Menu
*
* @since 1.0.0
*/
public function admin_menu() {
// Define the minimum capability required to access settings.
$minimum_capability = 'manage_options';
/**
* Defines the minimum capability required to access the Plugin's
* Menu and Sub Menus
*
* @since 4.3.6
*
* @param string $capability Minimum Required Capability.
* @return string Minimum Required Capability
*/
$minimum_capability = apply_filters( $this->base->plugin->filter_name . '_admin_admin_menu_minimum_capability', $minimum_capability );
/**
* Add settings menus and sub menus for the Plugin's settings.
*
* @since 5.2.4
*
* @param string $minimum_capability Minimum capability required.
*/
do_action( $this->base->plugin->filter_name . '_admin_admin_menu', $minimum_capability );
}
/**
* Define links to display below the Plugin Name on the WP_List_Table at in the Plugins screen.
*
* @since 5.0.2
*
* @param array $links Links.
* @return array Links
*/
public function plugin_action_links_settings_page( $links ) {
// Add link to Plugin settings screen.
$links['settings'] = sprintf(
'%s',
add_query_arg(
array(
'page' => $this->base->plugin->name . '-settings',
),
admin_url( 'admin.php' )
),
__( 'Settings', 'wp-to-buffer' )
);
// Return.
return $links;
}
/**
* Upgrade Screen
*
* @since 3.2.5
*/
public function upgrade_screen() {
// We never reach here, as we redirect earlier in the process.
}
/**
* Outputs the Settings Screen
*
* @since 3.0.0
*/
public function settings_screen() {
// Setup notices class.
$this->base->get_class( 'notices' )->set_key_prefix( $this->base->plugin->filter_name . '_' . wp_get_current_user()->ID );
// Maybe disconnect an account.
$this->maybe_disconnect_account();
// Maybe refresh profiles.
$this->maybe_refresh_profiles();
// Maybe save settings.
$result = $this->save_settings();
if ( is_wp_error( $result ) ) {
// Error notice.
$this->base->get_class( 'notices' )->add_error_notice( $result->get_error_message() );
} elseif ( $result === true ) {
// Success notice.
$this->base->get_class( 'notices' )->add_success_notice( __( 'Settings saved successfully.', 'wp-to-buffer' ) );
}
// If the Plugin isn't connected an account, show the screen to do this now.
if ( ! $this->base->get_class( 'settings' )->account_connected() ) {
$this->auth_screen();
return;
}
// Get Profiles for accounts.
$profiles = $this->get_cached_profiles();
// Get Settings Tab and Post Type we're managing settings for.
$tab = $this->get_tab( $profiles );
$post_type = $this->get_post_type_tab();
$disable_save_button = false;
// Post Types.
$post_types = $this->base->get_class( 'common' )->get_post_types();
// Accounts.
$accounts = $this->base->get_class( 'settings' )->get_accounts();
// Depending on the screen we're on, load specific options.
switch ( $tab ) {
/**
* Settings
*/
case 'auth':
// Log Settings.
$log_levels = $this->base->get_class( 'log' )->get_level_options();
// Documentation URL.
$documentation_url = $this->base->plugin->documentation_url . '/authentication-settings';
break;
/**
* No Profiles
*/
case 'profiles-missing':
// Disable Save button, as there are no settings displayed to save.
$disable_save_button = true;
// Documentation URL.
$documentation_url = $this->base->plugin->documentation_url . '/status-settings';
break;
/**
* Profiles Error
*/
case 'profiles-error':
// Disable Save button, as there are no settings displayed to save.
$disable_save_button = true;
// Documentation URL.
$documentation_url = $this->base->plugin->documentation_url . '/status-settings';
break;
/**
* Post Type
*/
default:
// Get original statuses that will be stored in a hidden field so they are preserved if the screen is saved
// with no changes that trigger an update to the hidden field.
$original_statuses = $this->base->get_class( 'settings' )->get_settings( $post_type );
// Get some other information.
$post_type_object = get_post_type_object( $post_type );
$actions_plural = $this->base->get_class( 'common' )->get_post_actions_past_tense();
$post_actions = $this->base->get_class( 'common' )->get_post_actions();
$documentation_url = $this->base->plugin->documentation_url . '/status-settings';
$is_post_screen = false; // Disables the 'specific' schedule option, which can only be used on individual Per-Post Settings.
// Check if this Post Type is enabled.
if ( ! $this->base->get_class( 'settings' )->is_post_type_enabled( $post_type ) ) {
$this->base->get_class( 'notices' )->add_warning_notice(
sprintf(
'%1$s %3$s',
sprintf(
/* translators: %1$s: Post Type, %2$s: Social Media Service Name (Buffer, Hootsuite), %3$s: Documentation URL */
__( 'To send %1$s to %2$s, at least one action on the Defaults tab must be enabled with a status defined, and at least one social media profile must be enabled below by clicking the applicable profile name and ticking the "Account Enabled" box.', 'wp-to-buffer' ),
$post_type_object->label,
$this->base->plugin->account
),
$documentation_url,
__( 'See Documentation', 'wp-to-buffer' )
)
);
}
break;
}
// Load View.
include_once $this->base->plugin->folder . 'lib/social/views/settings.php';
// Add footer action to output overlay modal markup.
add_action( 'admin_footer', array( $this, 'output_modal' ) );
}
/**
* Outputs the auth screen, allowing the user to begin the process of connecting the Plugin
* to the API, without showing other settings.
*
* @since 4.6.4
*/
public function auth_screen() {
// Load View.
include_once $this->base->plugin->folder . 'lib/social/views/settings-auth-required.php';
}
/**
* Outputs the hidden Javascript Modal and Overlay in the Footer
*
* @since 1.0.0
*/
public function output_modal() {
// Load view.
require_once $this->base->plugin->folder . 'lib/shared/views/modal.php';
}
/**
* Outputs the Log Screen
*
* @since 3.9.6
*/
public function log_screen() {
// Init table.
$table = new \WPZinc\Social\Log_Table( $this->base );
$table->prepare_items();
// Load View.
include_once $this->base->plugin->folder . 'lib/social/views/log.php';
}
/**
* Helper method to get the setting value from the plugin settings
*
* @since 3.0.0
*
* @param string $type Setting Type.
* @param string $key Setting Key.
* @param mixed $default_value Default Value if Setting does not exist.
* @return mixed Value
*/
public function get_setting( $type = '', $key = '', $default_value = '' ) {
// Post Type Setting or Bulk Setting.
if ( post_type_exists( $type ) ) {
return $this->base->get_class( 'settings' )->get_setting( $type, $key, $default_value );
}
// Depending on the type, return settings / options.
switch ( $type ) {
case 'text_to_image':
case 'log':
case 'hide_meta_box_by_roles':
case 'roles':
case 'custom_tags':
case 'repost':
return $this->base->get_class( 'settings' )->get_setting( $type, $key, $default_value );
default:
return $this->base->get_class( 'settings' )->get_option( $key, $default_value );
}
}
/**
* Fetches fresh profiles from the API for the given account, if the
* user clicks the refresh profiles link. Bypasses the transient cache
* and updates the stored profile IDs on the account.
*
* @since 6.1.2
*/
private function maybe_refresh_profiles() {
// Bail if no nonce.
if ( ! isset( $_GET['nonce'] ) ) {
return;
}
// Bail if nonce is invalid.
if ( ! wp_verify_nonce( sanitize_key( $_GET['nonce'] ), $this->base->plugin->name . '-refresh-profiles' ) ) {
return;
}
// Bail if account ID is not set.
if ( ! isset( $_GET[ $this->base->plugin->name . '-refresh-profiles' ] ) ) {
return;
}
// Get account.
$account_id = sanitize_text_field( wp_unslash( $_GET[ $this->base->plugin->name . '-refresh-profiles' ] ) );
$accounts = $this->base->get_class( 'settings' )->get_accounts();
if ( ! isset( $accounts[ $account_id ] ) ) {
return;
}
$account = $accounts[ $account_id ];
// Configure API for this account.
$this->base->get_class( 'api' )->set_tokens( $account['access_token'], $account['refresh_token'], $account['token_expires'] );
// Fetch fresh profiles from the API.
$profiles = $this->base->get_class( 'api' )->profiles( true, $account_id );
// Display error and bail.
if ( is_wp_error( $profiles ) ) {
$this->base->get_class( 'notices' )->add_error_notice( $profiles->get_error_message() );
return;
}
// If the service supports organizations, refresh the stored account
// information (name, email, channel limit, plan) alongside the profiles.
$organizations = array();
$api = $this->base->get_class( 'api' );
if ( method_exists( $api, 'organizations' ) ) {
$organizations = $api->organizations( true );
// Display error and bail.
if ( is_wp_error( $organizations ) ) {
$this->base->get_class( 'notices' )->add_error_notice( $organizations->get_error_message() );
return;
}
}
// Update the stored account information (where available) and profile IDs.
if ( isset( $organizations[ $account_id ] ) ) {
$this->base->get_class( 'settings' )->update_account_information(
$account_id,
$organizations[ $account_id ]['name'],
$organizations[ $account_id ]['email'],
$organizations[ $account_id ]['channel_limit'],
$organizations[ $account_id ]['plan'],
array_keys( $profiles )
);
// Schedule the event to refresh this account's access token before it expires.
$this->base->get_class( 'cron' )->reschedule_refresh_token_event();
} else {
$this->base->get_class( 'settings' )->update_account_profile_ids( $account_id, array_keys( $profiles ) );
}
$this->base->get_class( 'notices' )->add_success_notice(
__( 'Profiles refreshed successfully.', 'wp-to-buffer' )
);
}
/**
* Disconnects an account if the user clicks the disconnect link.
*
* @since 5.4.0
*/
private function maybe_disconnect_account() {
// Bail if no nonce.
if ( ! isset( $_GET['nonce'] ) ) {
return;
}
// Bail if nonce is invalid.
if ( ! wp_verify_nonce( sanitize_key( $_GET['nonce'] ), $this->base->plugin->name . '-disconnect' ) ) {
return;
}
// Bail if account ID is not set.
if ( ! isset( $_GET[ $this->base->plugin->name . '-disconnect' ] ) ) {
return;
}
// Disconnect account.
$this->base->get_class( 'settings' )->delete_account( sanitize_text_field( wp_unslash( $_GET[ $this->base->plugin->name . '-disconnect' ] ) ) );
$this->base->get_class( 'notices' )->add_success_notice(
sprintf(
/* translators: Social Media Service Name (Buffer, Hootsuite) */
__( '%s account disconnected successfully.', 'wp-to-buffer' ),
$this->base->plugin->account
)
);
}
/**
* Helper method to save settings
*
* @since 3.0.0
*
* @return mixed WP_Error | bool
*/
public function save_settings() {
// Check if a POST request was made.
if ( ! isset( $_POST['submit'] ) ) {
return false;
}
// Missing nonce.
if ( ! isset( $_POST[ $this->base->plugin->name . '_nonce' ] ) ) {
return new \WP_Error(
$this->base->plugin->filter_name . '_admin_save_settings_error',
__( 'Nonce field is missing. Settings NOT saved.', 'wp-to-buffer' )
);
}
// Invalid nonce.
if ( ! wp_verify_nonce( sanitize_key( $_POST[ $this->base->plugin->name . '_nonce' ] ), $this->base->plugin->name ) ) {
return new \WP_Error(
$this->base->plugin->filter_name . '_admin_save_settings_error',
__( 'Invalid nonce specified. Settings NOT saved.', 'wp-to-buffer' )
);
}
// Get URL parameters.
$tab = $this->get_tab();
$post_type = $this->get_post_type_tab();
switch ( $tab ) {
/**
* Authentication
*/
case 'auth':
// oAuth settings are now handled by this class' oauth() function.
// Save other Settings.
$settings = map_deep( $_POST, 'sanitize_text_field' );
// General Settings.
$this->base->get_class( 'settings' )->update_option( 'test_mode', ( isset( $settings['test_mode'] ) ? 1 : 0 ) );
$this->base->get_class( 'settings' )->update_option( 'force_trailing_forwardslash', ( isset( $settings['force_trailing_forwardslash'] ) ? 1 : 0 ) );
$this->base->get_class( 'settings' )->update_option( 'proxy', ( isset( $settings['proxy'] ) ? 1 : 0 ) );
// Log Settings.
// Always force errors.
$log = isset( $settings['log'] ) ? $settings['log'] : array();
if ( ! isset( $log['log_level'] ) ) {
$log['log_level'] = array(
'error',
);
} else {
// 'Error' is disabled on the form and not sent if another option is chosen.
// We always want errors to be logged so add it to the log levels now.
$log['log_level'][] = 'error';
}
$this->base->get_class( 'settings' )->update_option( 'log', $log );
// Reschedule CRON events.
$this->base->get_class( 'cron' )->reschedule_log_cleanup_event();
$this->base->get_class( 'cron' )->reschedule_media_cleanup_event();
// Done.
return true;
/**
* Post Type
*/
default:
if ( ! isset( $_POST[ $this->base->plugin->name ]['statuses'] ) ) {
return new \WP_Error(
$this->base->plugin->filter_name . '_admin_save_settings_error',
__( 'Statuses field is missing. Settings NOT saved.', 'wp-to-buffer' )
);
}
// Unslash and decode JSON field.
$settings = json_decode( wp_unslash( $_POST[ $this->base->plugin->name ]['statuses'] ), true ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
// Save Settings for this Post Type.
return $this->base->get_class( 'settings' )->update_settings( $post_type, $settings );
}
}
/**
* Returns the profiles for all accounts from the cache.
* Queries the API if the cache is empty.
*
* @since 6.0.5
*
* @return array
*/
private function get_cached_profiles() {
$profiles = array();
foreach ( $this->base->get_class( 'settings' )->get_accounts() as $account_id => $account ) {
// Configure API for this account.
$this->base->get_class( 'api' )->set_tokens( $account['access_token'], $account['refresh_token'], $account['token_expires'] );
// Get account profiles.
$account_profiles = $this->base->get_class( 'api' )->profiles( false, $account_id );
// Display an error.
if ( is_wp_error( $account_profiles ) ) {
$this->base->get_class( 'notices' )->add_error_notice( $account_profiles->get_error_message() );
continue;
}
// Merge profiles with existing profiles from other accounts.
// array_merge() is not used here as it will re-index numeric keys.
foreach ( $account_profiles as $profile ) {
$profiles[ $profile['id'] ] = $profile;
}
}
return $profiles;
}
/**
* Returns the settings tab that the user has selected.
*
* @since 3.7.2
*
* @param mixed $profiles API Profiles (false|WP_Error|array).
* @return string Tab
*/
private function get_tab( $profiles = false ) {
// If no tab, default to auth.
if ( ! filter_has_var( INPUT_GET, 'tab' ) ) {
return 'auth';
}
// Get current tab.
$tab = filter_input( INPUT_GET, 'tab', FILTER_SANITIZE_FULL_SPECIAL_CHARS );
// If Profiles are an error, show error.
if ( is_wp_error( $profiles ) ) {
return 'profiles-error';
}
// If no Profiles exist, show error.
if ( is_array( $profiles ) && ! count( $profiles ) ) {
return 'profiles-missing';
}
// Return tab.
return $tab;
}
/**
* Returns the Post Type tab that the user has selected.
*
* @since 3.7.2
*
* @return string Tab
*/
private function get_post_type_tab() {
// If no type, default to empty string.
if ( ! filter_has_var( INPUT_GET, 'type' ) ) {
return '';
}
// Get supported post types.
$post_types = array_keys( $this->base->get_class( 'common' )->get_post_types() );
$post_type = filter_input( INPUT_GET, 'type', FILTER_SANITIZE_FULL_SPECIAL_CHARS );
// If the post type is not supported, return empty string.
if ( ! in_array( $post_type, $post_types, true ) ) {
return '';
}
return $post_type;
}
}