Kit > General.
*
* @package ConvertKit
* @author ConvertKit
*/
class ConvertKit_Admin_Section_General extends ConvertKit_Admin_Section_Base {
/**
* Holds the API instance.
*
* @since 1.9.6
*
* @var ConvertKit_API_V4
*/
private $api;
/**
* Holds the ConvertKit Account Name.
*
* @since 1.9.6
*
* @var bool|WP_Error|array
*/
private $account = false;
/**
* Holds the ConvertKit Forms Resource.
*
* @since 1.9.6
*
* @var bool|ConvertKit_Resource_Forms
*/
private $forms = false;
/**
* Constructor.
*/
public function __construct() {
// Define the class that reads/writes settings.
$this->settings = new ConvertKit_Settings();
// Define the settings key.
$this->settings_key = $this->settings::SETTINGS_NAME;
// Define the programmatic name, Title and Tab Text.
$this->name = 'general';
$this->title = __( 'General Settings', 'convertkit' );
$this->tab_text = __( 'General', 'convertkit' );
// Define settings sections.
$this->settings_sections = array(
'general' => array(
'title' => $this->title,
'callback' => array( $this, 'print_section_info' ),
'wrap' => true,
),
'site-wide' => array(
'title' => __( 'Site Wide', 'convertkit' ),
'callback' => array( $this, 'print_section_info_site_wide' ),
'wrap' => true,
),
'advanced' => array(
'title' => __( 'Advanced', 'convertkit' ),
'callback' => array( $this, 'print_section_info_advanced' ),
'wrap' => true,
),
);
// Register and maybe output notices for this settings screen, and the Intercom messenger.
if ( $this->on_settings_screen( $this->name ) ) {
add_filter( 'convertkit_settings_base_register_notices', array( $this, 'register_notices' ) );
add_action( 'convertkit_settings_base_render_before', array( $this, 'maybe_output_notices' ) );
add_action( 'admin_footer', array( $this, 'output_intercom' ) );
}
// Enqueue scripts and CSS.
add_action( 'convertkit_admin_settings_enqueue_scripts', array( $this, 'enqueue_scripts' ) );
add_action( 'convertkit_admin_settings_enqueue_styles', array( $this, 'enqueue_styles' ) );
parent::__construct();
$this->check_credentials();
$this->maybe_disconnect();
}
/**
* Registers success and error notices for the General screen, to be displayed
* depending on the action.
*
* @since 2.5.1
*
* @param array $notices Regsitered success and error notices.
* @return array
*/
public function register_notices( $notices ) {
return array_merge(
$notices,
array(
'oauth2_success' => __( 'Successfully authorized with Kit.', 'convertkit' ),
)
);
}
/**
* Test the access token, if it exists.
* If the access token has been revoked or is invalid, remove it from the settings now.
*
* @since 2.5.0
*/
private function check_credentials() {
// Bail if we're not on the settings screen.
if ( ! $this->on_settings_screen( $this->name ) ) {
return;
}
// Bail if no access and refresh token exist.
if ( ! $this->settings->has_access_and_refresh_token() ) {
return;
}
// Initialize the API.
$this->api = new ConvertKit_API_V4(
CONVERTKIT_OAUTH_CLIENT_ID,
CONVERTKIT_OAUTH_CLIENT_REDIRECT_URI,
$this->settings->get_access_token(),
$this->settings->get_refresh_token(),
$this->settings->debug_enabled(),
'settings'
);
// Get Account Details, which we'll use in account_name_callback(), but also lets us test
// whether the API credentials are valid.
$this->account = $this->api->get_account();
// If the request succeeded, no need to perform further actions.
if ( ! is_wp_error( $this->account ) ) {
// Remove any existing persistent notice.
WP_ConvertKit()->get_class( 'admin_notices' )->delete( 'authorization_failed' );
return;
}
// Depending on the error code, maybe persist a notice in the WordPress Administration until the user
// fixes the problem.
switch ( $this->account->get_error_data( $this->account->get_error_code() ) ) {
case 401:
// Access token either expired or was revoked in ConvertKit.
// Remove from settings.
$this->settings->delete_credentials();
// Display a site wide notice.
WP_ConvertKit()->get_class( 'admin_notices' )->add( 'authorization_failed' );
// Redirect to General screen, which will now show the ConvertKit_Admin_Section_OAuth screen, because
// the Plugin has no access token.
wp_safe_redirect(
add_query_arg(
array(
'page' => $this->settings_key,
),
'options-general.php'
)
);
exit();
}
// Output a non-401 error now.
$this->output_error( $this->account->get_error_message() );
}
/**
* Deletes the OAuth Access Token, Refresh Token and Expiry from the Plugin's settings, if the user
* clicked the Disconnect button.
*
* @since 2.5.0
*/
private function maybe_disconnect() {
// Bail if we're not on the settings screen.
if ( ! $this->on_settings_screen( $this->name ) ) {
return;
}
// Bail if nonce verification fails.
if ( ! isset( $_REQUEST['_convertkit_settings_oauth_disconnect'] ) ) {
return;
}
if ( ! wp_verify_nonce( sanitize_key( $_REQUEST['_convertkit_settings_oauth_disconnect'] ), 'convertkit-oauth-disconnect' ) ) {
return;
}
// Delete Access Token.
$settings = new ConvertKit_Settings();
$settings->delete_credentials();
// Delete cached resources.
$creator_network = new ConvertKit_Resource_Creator_Network_Recommendations();
$forms = new ConvertKit_Resource_Forms();
$landing_pages = new ConvertKit_Resource_Landing_Pages();
$posts = new ConvertKit_Resource_Posts();
$products = new ConvertKit_Resource_Products();
$tags = new ConvertKit_Resource_Tags();
$creator_network->delete();
$forms->delete();
$landing_pages->delete();
$posts->delete();
$products->delete();
$tags->delete();
// Redirect to General screen, which will now show the ConvertKit_Settings_OAuth screen, because
// the Plugin has no access token.
wp_safe_redirect(
add_query_arg(
array(
'page' => $this->settings_key,
),
'options-general.php'
)
);
exit();
}
/**
* Enqueues scripts for the Settings > General screen.
*
* @since 2.2.4
*
* @param string $section Settings section / tab (general|tools|restrict-content).
*/
public function enqueue_scripts( $section ) {
// Bail if we're not on the general section.
if ( $section !== $this->name ) {
return;
}
// Enqueue Select2 JS.
convertkit_select2_enqueue_scripts();
// Enqueue JS.
wp_enqueue_script( 'convertkit-admin-preview-output', CONVERTKIT_PLUGIN_URL . 'resources/backend/js/preview-output.js', array( 'jquery' ), CONVERTKIT_PLUGIN_VERSION, true );
wp_enqueue_script( 'convertkit-admin-settings-conditional-display', CONVERTKIT_PLUGIN_URL . 'resources/backend/js/settings-conditional-display.js', array( 'jquery' ), CONVERTKIT_PLUGIN_VERSION, true );
}
/**
* Enqueues styles for the Settings > General screen.
*
* @since 2.2.4
*
* @param string $section Settings section / tab (general|tools|restrict-content).
*/
public function enqueue_styles( $section ) {
// Bail if we're not on the general section.
if ( $section !== $this->name ) {
return;
}
// Enqueue Select2 CSS.
convertkit_select2_enqueue_styles();
}
/**
* Registers settings fields for this section.
*/
public function register_fields() {
// Initialize resource classes.
$this->maybe_initialize_and_refresh_resources();
add_settings_field(
'account_name',
__( 'Account Name', 'convertkit' ),
array( $this, 'account_name_callback' ),
$this->settings_key,
$this->name
);
// Initialize resource classes and perform a refresh if this hasn't yet been done.
foreach ( convertkit_get_supported_post_types() as $supported_post_type ) {
// Get Post Type's Label.
$post_type = get_post_type_object( $supported_post_type );
// Skip if the Post Type doesn't exist.
if ( ! $post_type ) {
continue;
}
// Add Settings Fields.
add_settings_field(
$supported_post_type . '_form',
sprintf(
/* translators: Post Type Name, plural */
__( 'Default Form (%s)', 'convertkit' ),
$post_type->label
),
array( $this, 'default_form_callback' ),
$this->settings_key,
$this->name,
array(
'label_for' => '_wp_convertkit_settings_' . $supported_post_type . '_form',
'post_type' => $supported_post_type,
'post_type_object' => $post_type,
)
);
if ( $this->forms->exist() ) {
add_settings_field(
$supported_post_type . '_form_position',
sprintf(
/* translators: Post Type Name, plural */
__( 'Form Position (%s)', 'convertkit' ),
$post_type->label
),
array( $this, 'default_form_position_callback' ),
$this->settings_key,
$this->name,
array(
'label_for' => '_wp_convertkit_settings_' . $supported_post_type . '_form_position',
'post_type' => $supported_post_type,
'post_type_object' => $post_type,
)
);
add_settings_field(
$supported_post_type . '_form_position_element',
'',
array( $this, 'default_form_position_element_callback' ),
$this->settings_key,
$this->name,
array(
'label_for' => '_wp_convertkit_settings_' . $supported_post_type . '_form_position_element',
'post_type' => $supported_post_type,
'post_type_object' => $post_type,
)
);
}
}
// Site Wide.
add_settings_field(
'non_inline_form',
__( 'Default Forms (Site Wide)', 'convertkit' ),
array( $this, 'non_inline_form_callback' ),
$this->settings_key,
$this->name . '-site-wide',
array(
'label_for' => 'non_inline_form',
)
);
add_settings_field(
'non_inline_form_honor_none_setting',
__( 'Behavior', 'convertkit' ),
array( $this, 'non_inline_form_honor_none_setting_callback' ),
$this->settings_key,
$this->name . '-site-wide',
array(
'label_for' => 'non_inline_form_honor_none_setting',
)
);
// Advanced.
add_settings_field(
'debug',
__( 'Debug', 'convertkit' ),
array( $this, 'debug_callback' ),
$this->settings_key,
$this->name . '-advanced',
array(
'label_for' => 'debug',
)
);
add_settings_field(
'no_scripts',
__( 'Disable JavaScript', 'convertkit' ),
array( $this, 'no_scripts_callback' ),
$this->settings_key,
$this->name . '-advanced',
array(
'label_for' => 'no_scripts',
)
);
add_settings_field(
'no_css',
__( 'Disable CSS', 'convertkit' ),
array( $this, 'no_css_callback' ),
$this->settings_key,
$this->name . '-advanced',
array(
'label_for' => 'no_css',
)
);
}
/**
* Prints help info for this section
*/
public function print_section_info() {
?>
tags */
esc_html__( 'The default form can be inserted into the middle of post or page content by using either the %s shortcode or block.', 'convertkit' ),
'[convertkit]'
);
?>
%s',
isset( $this->account['account']['name'] ) ? esc_attr( $this->account['account']['name'] ) : esc_html__( '(Not specified)', 'convertkit' )
);
// Display an option to disconnect.
$html .= sprintf(
'%2$s
',
esc_url(
add_query_arg(
array(
'page' => '_wp_convertkit_settings',
'_convertkit_settings_oauth_disconnect' => wp_create_nonce( 'convertkit-oauth-disconnect' ),
),
'options-general.php'
)
),
esc_html__( 'Disconnect', 'convertkit' )
);
// Output has already been run through escaping functions above.
echo $html; // phpcs:ignore WordPress.Security.EscapeOutput
}
/**
* Initialize resource classes and perform a and refresh of resources,
* if initialization has not yet taken place.
*
* @since 2.5.9
*/
public function maybe_initialize_and_refresh_resources() {
// If the Forms resource class is initialized, this has already been done.
if ( $this->forms !== false ) {
return;
}
// Initialize forms resource class.
$this->forms = new ConvertKit_Resource_Forms( 'settings' );
// Don't refresh resources if we're not on the settings screen, as
// it's a resource intense process that can take several seconds.
// We don't want to block other parts of the admin UI.
if ( ! $this->on_settings_screen( $this->name ) ) {
return;
}
// Refresh Forms.
$this->forms->refresh();
// Also refresh Landing Pages, Tags and Posts. Whilst not displayed in the Plugin Settings, this ensures up to date
// lists are stored for when editing e.g. Pages.
$landing_pages = new ConvertKit_Resource_Landing_Pages( 'settings' );
$landing_pages->refresh();
remove_all_actions( 'convertkit_resource_refreshed_posts' );
$posts = new ConvertKit_Resource_Posts( 'settings' );
$posts->refresh();
$products = new ConvertKit_Resource_Products( 'settings' );
$products->refresh();
$sequences = new ConvertKit_Resource_Sequences( 'settings' );
$sequences->refresh();
$tags = new ConvertKit_Resource_Tags( 'settings' );
$tags->refresh();
}
/**
* Renders the input for the Default Form setting for the given Post Type.
*
* @since 1.9.6
*
* @param array $args Field arguments.
*/
public function default_form_callback( $args ) {
// Bail if no Forms exist.
if ( ! $this->forms->exist() ) {
esc_html_e( 'No Forms exist in Kit.', 'convertkit' );
echo '
' . esc_html__( 'Click here to create your first form', 'convertkit' ) . '';
return;
}
// Build description with preview link.
$description = false;
$preview_url = WP_ConvertKit()->get_class( 'preview_output' )->get_preview_form_url( $args['post_type'] );
if ( $preview_url ) {
// Include a preview link in the description.
$description = sprintf(
'%s %s %s',
sprintf(
/* translators: Post Type name, plural */
esc_html__( 'Select a form above to automatically output below all %s.', 'convertkit' ),
$args['post_type_object']->label
),
'' . esc_html__( 'Click here', 'convertkit' ) . '',
esc_html__( 'to preview how this will display.', 'convertkit' )
);
} else {
// Just output the field's description.
$description = sprintf(
/* translators: Post Type name, plural */
esc_html__( 'Select a form above to automatically output below all %s.', 'convertkit' ),
$args['post_type_object']->label
);
}
// Build field.
$select_field = $this->forms->get_select_field_all(
$this->settings_key . '[' . $args['post_type'] . '_form]',
$this->settings_key . '_' . $args['post_type'] . '_form',
array(
'convertkit-select2',
'convertkit-preview-output-link',
),
$this->settings->get_default_form( $args['post_type'] ),
array(
'default' => esc_html__( 'None', 'convertkit' ),
),
array(
'data-target' => '#convertkit-preview-form-' . esc_attr( $args['post_type'] ),
'data-link' => esc_attr( $preview_url ) . '&convertkit_form_id=',
),
$description
);
// Output field.
echo '' . $select_field . '
'; // phpcs:ignore WordPress.Security.EscapeOutput
}
/**
* Renders the input for the Default Form Position setting for the given Post Type.
*
* @since 2.5.8
*
* @param array $args Field arguments.
*/
public function default_form_position_callback( $args ) {
echo $this->get_select_field( // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
$args['post_type'] . '_form_position',
esc_attr( $this->settings->get_default_form_position( $args['post_type'] ) ),
array(
'before_content' => sprintf(
/* translators: Post type singular name */
esc_attr__( 'Before %s content', 'convertkit' ),
esc_attr( $args['post_type_object']->labels->singular_name )
),
'after_content' => sprintf(
/* translators: Post type singular name */
esc_attr__( 'After %s content', 'convertkit' ),
esc_attr( $args['post_type_object']->labels->singular_name )
),
'before_after_content' => sprintf(
/* translators: Post type singular name */
esc_attr__( 'Before and after %s content', 'convertkit' ),
esc_attr( $args['post_type_object']->labels->singular_name )
),
'after_element' => esc_html__( 'After element', 'convertkit' ),
),
sprintf(
/* translators: Post Type name, plural */
esc_html__( 'Where forms should display relative to the %s content', 'convertkit' ),
esc_html( $args['post_type_object']->labels->singular_name )
),
array( 'convertkit-conditional-display' ),
array(
'data-conditional-value' => 'after_element',
'data-conditional-element' => esc_attr( $args['post_type'] ) . '_form_position_element_index',
)
);
}
/**
* Renders the input for the Default Form Position Index setting for the given Post Type.
*
* @since 2.6.1
*
* @param array $args Field arguments.
*/
public function default_form_position_element_callback( $args ) {
echo $this->get_number_field( // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
$args['post_type'] . '_form_position_element_index',
esc_attr( (string) $this->settings->get_default_form_position_element_index( $args['post_type'] ) ),
1,
999,
1,
false,
array( 'after_element' )
);
echo $this->get_select_field( // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
$args['post_type'] . '_form_position_element',
esc_attr( $this->settings->get_default_form_position_element( $args['post_type'] ) ),
array(
'p' => esc_html__( 'Paragraphs', 'convertkit' ),
'h2' => esc_html__( 'Headings ', 'convertkit' ),
'h3' => esc_html__( 'Headings ', 'convertkit' ),
'h4' => esc_html__( 'Headings ', 'convertkit' ),
'h5' => esc_html__( 'Headings ', 'convertkit' ),
'h6' => esc_html__( 'Headings ', 'convertkit' ),
'img' => esc_html__( 'Images', 'convertkit' ),
),
esc_html__( 'The number of elements before outputting the form.', 'convertkit' ),
array( 'after_element' )
);
}
/**
* Renders the input for the Non-inline Form setting.
*
* @since 2.2.3
*
* @param array $args Field arguments.
*/
public function non_inline_form_callback( $args ) {
// Bail if no non-inline Forms exist.
if ( ! $this->forms->non_inline_exist() ) {
esc_html_e( 'No non-inline Forms exist in Kit.', 'convertkit' );
echo '
' . esc_html__( 'Click here to create your first modal, slide in or sticky bar form', 'convertkit' ) . '';
return;
}
// Build description with preview link.
$preview_url = WP_ConvertKit()->get_class( 'preview_output' )->get_preview_form_home_url();
$description = sprintf(
'%s %s %s',
esc_html__( 'Automatically display one or more modal, slide-in, or sticky bar forms across your site. This setting is overridden if a default non-inline form is set above, a specific non-inline form or "None" option is chosen for a post/page, or a non-inline form is specified in a block/shortcode.', 'convertkit' ),
'' . esc_html__( 'Click here', 'convertkit' ) . '',
esc_html__( 'to preview how this will display.', 'convertkit' )
);
// Build field.
$select_field = $this->forms->get_select_field_non_inline(
$this->settings_key . '[non_inline_form]',
$this->settings_key . '_non_inline_form',
array(
'convertkit-select2',
'convertkit-preview-output-link',
),
$this->settings->get_non_inline_form(),
false,
array(
'data-target' => '#convertkit-preview-non-inline-form',
'data-link' => esc_attr( $preview_url ) . '&convertkit_form_id=',
),
$description
);
// Output field.
echo '
' . $select_field . '
'; // phpcs:ignore WordPress.Security.EscapeOutput
}
/**
* Renders the input for the Non-inline Form override setting.
*
* @since 2.7.3
*/
public function non_inline_form_honor_none_setting_callback() {
// Output field.
echo $this->get_checkbox_field( // phpcs:ignore WordPress.Security.EscapeOutput
'non_inline_form_honor_none_setting',
'on',
$this->settings->non_inline_form_honor_none_setting(), // phpcs:ignore WordPress.Security.EscapeOutput
esc_html__( 'If checked, do not display the site wide form(s) above on Pages / Posts that have their Kit Form setting = None.', 'convertkit' )
);
}
/**
* Renders the input for the Debug setting.
*
* @since 1.9.6
*/
public function debug_callback() {
// Output field.
echo $this->get_checkbox_field( // phpcs:ignore WordPress.Security.EscapeOutput
'debug',
'on',
$this->settings->debug_enabled(), // phpcs:ignore WordPress.Security.EscapeOutput
esc_html__( 'Log requests to file and output browser console messages.', 'convertkit' ),
esc_html__( 'You can ignore this unless you\'re working with our support team to resolve an issue. Decheck this option to improve performance.', 'convertkit' )
);
}
/**
* Renders the input for the Disable Javascript setting.
*
* @since 1.9.6
*/
public function no_scripts_callback() {
// Output field.
echo $this->get_checkbox_field( // phpcs:ignore WordPress.Security.EscapeOutput
'no_scripts',
'on',
$this->settings->scripts_disabled(), // phpcs:ignore WordPress.Security.EscapeOutput
esc_html__( 'Prevent plugin from loading JavaScript files. This will disable the custom content and tagging features of the plugin. Does not apply to landing pages. Use with caution!', 'convertkit' )
);
}
/**
* Renders the input for the Disable CSS setting.
*
* @since 1.9.6.9
*/
public function no_css_callback() {
// Output field.
echo $this->get_checkbox_field( // phpcs:ignore WordPress.Security.EscapeOutput
'no_css',
'on',
$this->settings->css_disabled(), // phpcs:ignore WordPress.Security.EscapeOutput
esc_html__( 'Prevents loading plugin CSS files. This will disable styling on broadcasts, form trigger buttons, product buttons and member\'s content. Use with caution!', 'convertkit' ),
array(
sprintf(
'%s %s',
esc_html__( 'To customize forms and their styling, use the', 'convertkit' ),
esc_url( convertkit_get_form_editor_url() ),
esc_html__( 'Kit form editor', 'convertkit' )
),
sprintf(
'%s Contact Form 7, Gravity Forms %s WPForms %s',
esc_html__( 'For developers who require custom form designs through use of CSS, consider using the', 'convertkit' ),
esc_html__( 'or', 'convertkit' ),
esc_html__( 'integrations.', 'convertkit' )
),
)
);
}
/**
* Sanitizes the settings prior to being saved.
*
* @since 2.4.3
*
* @param null|array $settings Submitted Settings Fields.
* @return array Sanitized Settings with Defaults
*/
public function sanitize_settings( $settings ) {
// If no Access Token, Refresh Token or Token Expiry keys were specified in the settings
// prior to save, don't overwrite them with the blank setting from get_defaults().
// This ensures we only blank these values if we explicitly do so via $settings,
// as they won't be included in the Settings screen for security.
if ( ! array_key_exists( 'disconnect', $_REQUEST ) ) { // phpcs:ignore WordPress.Security.NonceVerification
// If settings are null, no checkboxes were ticked and no other form elements
// were submitted i.e. the Kit account has no forms.
if ( is_null( $settings ) ) {
$settings = array();
}
if ( ! array_key_exists( 'access_token', $settings ) ) {
$settings['access_token'] = $this->settings->get_access_token();
}
if ( ! array_key_exists( 'refresh_token', $settings ) ) {
$settings['refresh_token'] = $this->settings->get_refresh_token();
}
if ( ! array_key_exists( 'token_expires', $settings ) ) {
$settings['token_expires'] = $this->settings->get_token_expiry();
}
}
// Call parent class to merge settings with defaults.
$settings = parent::sanitize_settings( $settings );
// If a Form or Landing Page was specified that isn't the default,
// request a review.
// Since switching to OAuth means the settings screen will only display
// settings if the access token is valid, the Default Forms options will
// always be submitted. Previously, if no API Key/Secret was specified,
// no Default Forms options would render.
// This can safely be called multiple times, as the review request
// class will ensure once a review request is dismissed by the user,
// it is never displayed again.
if ( ( isset( $settings['page_form'] ) && $settings['page_form'] !== 'default' ) ||
( isset( $settings['post_form'] ) && $settings['post_form'] !== 'default' ) ) {
WP_ConvertKit()->get_class( 'review_request' )->request_review();
}
// Return settings to be saved.
return $settings;
}
}