# convertkit/trunk/includes/class-convertkit-settings.php

Kit (formerly ConvertKit) – Email Newsletter, Email Marketing, Membership, Subscribers and Landing Pages, version trunk. 971 lines.

- Page: https://pluginprobe.com/plugins/convertkit/trunk/code/includes/class-convertkit-settings.php
- Raw: https://pluginprobe.com/plugins/convertkit/trunk/raw/includes/class-convertkit-settings.php
- Modified: 2026-08-28T10:49:42+00:00

Line numbers below start at 1. Link to a line or a range by appending a fragment to the
page URL, for example `https://pluginprobe.com/plugins/convertkit/trunk/code/includes/class-convertkit-settings.php#L10-L20`.

```php
<?php
/**
 * ConvertKit Plugin Settings class.
 *
 * @package ConvertKit
 * @author ConvertKit
 */

/**
 * Class to read ConvertKit Plugin Settings.
 *
 * @since   1.9.6
 */
class ConvertKit_Settings {

	/**
	 * Holds the Settings Key that stores site wide ConvertKit settings
	 *
	 * @var     string
	 */
	const SETTINGS_NAME = '_wp_convertkit_settings';

	/**
	 * Holds the Settings
	 *
	 * @var     array
	 */
	private $settings = array();

	/**
	 * Constructor. Reads settings from options table, falling back to defaults
	 * if no settings exist.
	 *
	 * @since   1.9.6
	 */
	public function __construct() {

		// Get Settings.
		$settings = get_option( self::SETTINGS_NAME );

		// If no Settings exist, falback to default settings.
		if ( ! $settings ) {
			$this->settings = $this->get_defaults();
		} else {
			$this->settings = array_merge( $this->get_defaults(), $settings );
		}

	}

	/**
	 * Returns Plugin settings.
	 *
	 * @since   1.9.6
	 *
	 * @return  array
	 */
	public function get() {

		return $this->settings;

	}

	/**
	 * Returns the API Key Plugin setting.
	 *
	 * @since   1.9.6
	 *
	 * @return  string
	 */
	public function get_api_key() {

		// Return API Key from constant, if defined.
		if ( defined( 'CONVERTKIT_API_KEY' ) ) {
			return CONVERTKIT_API_KEY;
		}

		// Return API Key from settings.
		return $this->settings['api_key'];

	}

	/**
	 * Returns whether the API Key has been set in the Plugin settings.
	 *
	 * @since   1.9.6
	 *
	 * @return  bool
	 */
	public function has_api_key() {

		return ( ! empty( $this->get_api_key() ) ? true : false );

	}

	/**
	 * Returns whether the API Key is stored as a constant in the wp-config.php file.
	 *
	 * @since   1.9.6
	 *
	 * @return  bool
	 */
	public function is_api_key_a_constant() {

		return defined( 'CONVERTKIT_API_KEY' );

	}

	/**
	 * Returns the API Secret Plugin setting.
	 *
	 * @since   1.9.6
	 *
	 * @return  string
	 */
	public function get_api_secret() {

		// Return API Secret from constant, if defined.
		if ( defined( 'CONVERTKIT_API_SECRET' ) ) {
			return CONVERTKIT_API_SECRET;
		}

		// Return API Secret from settings.
		return $this->settings['api_secret'];

	}

	/**
	 * Returns whether the API Secret has been set in the Plugin settings.
	 *
	 * @since   1.9.6
	 *
	 * @return  bool
	 */
	public function has_api_secret() {

		return ( ! empty( $this->get_api_secret() ) ? true : false );

	}

	/**
	 * Returns whether the API Secret is stored as a constant in the wp-config.php file.
	 *
	 * @since   1.9.6
	 *
	 * @return  bool
	 */
	public function is_api_secret_a_constant() {

		return defined( 'CONVERTKIT_API_SECRET' );

	}

	/**
	 * Returns whether the API Key and Secret have been set in the Plugin settings.
	 *
	 * @since   1.9.6
	 *
	 * @return  bool
	 */
	public function has_api_key_and_secret() {

		_deprecated_function( __FUNCTION__, '2.6.3', 'has_access_and_refresh_token()' );

		// Use check for access and refresh token.
		return $this->has_access_and_refresh_token();

	}

	/**
	 * Returns the Access Token Plugin setting.
	 *
	 * @since   2.5.0
	 *
	 * @return  string
	 */
	public function get_access_token() {

		// Reload settings from options table, to ensure we have the latest tokens.
		$this->refresh_settings();

		// Return Access Token from settings.
		return $this->settings['access_token'];

	}

	/**
	 * Returns whether the Access Token has been set in the Plugin settings.
	 *
	 * @since   2.5.0
	 *
	 * @return  bool
	 */
	public function has_access_token() {

		return ( ! empty( $this->get_access_token() ) ? true : false );

	}

	/**
	 * Returns the Refresh Token Plugin setting.
	 *
	 * @since   2.5.0
	 *
	 * @return  string
	 */
	public function get_refresh_token() {

		// Reload settings from options table, to ensure we have the latest tokens.
		$this->refresh_settings();

		// Return Refresh Token from settings.
		return $this->settings['refresh_token'];

	}

	/**
	 * Returns whether the Refresh Token has been set in the Plugin settings.
	 *
	 * @since   2.5.0
	 *
	 * @return  bool
	 */
	public function has_refresh_token() {

		return ( ! empty( $this->get_refresh_token() ) ? true : false );

	}

	/**
	 * Returns whether to use Access and Refresh Tokens for API requests,
	 * based on whether an Access Token and Refresh Token have been saved
	 * in the Plugin settings.
	 *
	 * @since   2.5.0
	 *
	 * @return  bool
	 */
	public function has_access_and_refresh_token() {

		return $this->has_access_token() && $this->has_refresh_token();

	}

	/**
	 * Returns the Access Token expiry timestamp.
	 *
	 * @since   2.5.0
	 *
	 * @return  int
	 */
	public function get_token_expiry() {

		// Return Token Expiry from settings.
		return $this->settings['token_expires'];

	}

	/**
	 * Returns the Default Form Plugin setting.
	 *
	 * @since   1.9.6
	 *
	 * @param   string $post_type  Post Type.
	 * @return  string|int          Default Form (default|form id)
	 */
	public function get_default_form( $post_type ) {

		// Return default if this Post Type doesn't exist as a setting.
		if ( ! array_key_exists( $post_type . '_form', $this->settings ) ) {
			return 'default';
		}

		// Backward compat. where older Plugin versions would store API errors in the option value
		// with id = -2 and name = 'Error contacting API'.
		if ( is_array( $this->settings[ $post_type . '_form' ] ) ) {
			return 'default';
		}

		return $this->settings[ $post_type . '_form' ];

	}

	/**
	 * Returns whether the Default Form has been set in the Plugin settings.
	 *
	 * @since   1.9.6
	 *
	 * @param   string $post_type  Post Type.
	 * @return  bool                Post Type has a Default Form setting specified in Plugin Settings.
	 */
	public function has_default_form( $post_type ) {

		return ( ! empty( $this->settings[ $post_type . '_form' ] ) ? true : false );

	}

	/**
	 * Returns the Default Form Position Plugin setting.
	 *
	 * @since   2.5.8
	 *
	 * @param   string $post_type  Post Type.
	 * @return  string|int          Default Form (default|form id)
	 */
	public function get_default_form_position( $post_type ) {

		// Return after_content if this Post Type's position doesn't exist as a setting.
		if ( ! array_key_exists( $post_type . '_form_position', $this->settings ) ) {
			return 'after_content';
		}

		return $this->settings[ $post_type . '_form_position' ];

	}

	/**
	 * Returns the Default Form Position Element Plugin setting.
	 *
	 * @since   2.6.1
	 *
	 * @param   string $post_type  Post Type.
	 * @return  string             Element to insert form after
	 */
	public function get_default_form_position_element( $post_type ) {

		// Return after_content if this Post Type's position doesn't exist as a setting.
		if ( ! array_key_exists( $post_type . '_form_position_element', $this->settings ) ) {
			return 'p';
		}

		return $this->settings[ $post_type . '_form_position_element' ];

	}

	/**
	 * Returns the Default Form Position Index Plugin setting.
	 *
	 * @since   2.6.1
	 *
	 * @param   string $post_type  Post Type.
	 * @return  int                Number of elements before inserting form
	 */
	public function get_default_form_position_element_index( $post_type ) {

		// Return 1 if this Post Type's position index doesn't exist as a setting.
		if ( ! array_key_exists( $post_type . '_form_position_element_index', $this->settings ) ) {
			return 1;
		}

		return (int) $this->settings[ $post_type . '_form_position_element_index' ];

	}

	/**
	 * Returns the Global non-inline Form Plugin setting.
	 *
	 * @since   2.3.3
	 *
	 * @return  array
	 */
	public function get_non_inline_form() {

		// Return blank array if no inline form is specified.
		if ( ! $this->has_non_inline_form() ) {
			return array();
		}

		// 2.6.8 and earlier stored a single Form ID in a string.
		if ( is_string( $this->settings['non_inline_form'] ) ) {
			return array( (int) $this->settings['non_inline_form'] );
		}

		// Cast values to integers and return.
		return array_map( 'intval', $this->settings['non_inline_form'] );

	}

	/**
	 * Returns whether the Global non-inline Form has been set in the Plugin settings.
	 *
	 * @since   2.3.3
	 *
	 * @return  bool    Global non-inline Form setting specified in Plugin Settings.
	 */
	public function has_non_inline_form() {

		// 2.6.8 and earlier stored a single Form ID in a string.
		if ( is_string( $this->settings['non_inline_form'] ) ) {
			if ( ! empty( $this->settings['non_inline_form'] ) ) {
				return true;
			}

			return false;
		}

		return ( count( $this->settings['non_inline_form'] ) > 0 ? true : false );

	}

	/**
	 * Returns whether the Global non-inline Form setting should honor the Page / Post
	 * None setting.
	 *
	 * @since   2.7.3
	 *
	 * @return  bool
	 */
	public function non_inline_form_honor_none_setting() {

		return ( $this->settings['non_inline_form_honor_none_setting'] === 'on' ? true : false );

	}

	/**
	 * Returns whether the Non-inline Form Limit per Session setting has been set in the Plugin settings.
	 *
	 * @since   3.0.0
	 *
	 * @return  bool
	 */
	public function non_inline_form_limit_per_session() {

		return ( $this->settings['non_inline_form_limit_per_session'] === 'on' ? true : false );

	}

	/**
	 * Returns the active spam protection provider Plugin setting.
	 *
	 * @since   3.3.7
	 *
	 * @return  string  none|recaptcha|turnstile
	 */
	public function spam_protection_provider() {

		return $this->settings['spam_protection_provider'];

	}

	/**
	 * Returns the reCAPTCHA Site Key Plugin setting.
	 *
	 * @since   3.0.0
	 *
	 * @return  string
	 */
	public function recaptcha_site_key() {

		return $this->settings['recaptcha_site_key'];

	}

	/**
	 * Returns whether the reCAPTCHA Site Key has been set in the Plugin settings.
	 *
	 * @since   3.0.0
	 *
	 * @return  bool
	 */
	public function has_recaptcha_site_key() {

		return ! empty( $this->recaptcha_site_key() );

	}

	/**
	 * Returns the reCAPTCHA Secret Key Plugin setting.
	 *
	 * @since   3.0.0
	 *
	 * @return  string
	 */
	public function recaptcha_secret_key() {

		return $this->settings['recaptcha_secret_key'];

	}

	/**
	 * Returns whether the reCAPTCHA Secret Key has been set in the Plugin settings.
	 *
	 * @since   3.0.0
	 *
	 * @return  bool
	 */
	public function has_recaptcha_secret_key() {

		return ! empty( $this->recaptcha_secret_key() );

	}

	/**
	 * Returns whether the reCAPTCH Site Key and Secret Key are defined
	 * in the Plugin settings.
	 *
	 * @since   3.0.0
	 *
	 * @return  bool
	 */
	public function has_recaptcha_site_and_secret_keys() {

		return $this->has_recaptcha_site_key() && $this->has_recaptcha_secret_key();

	}

	/**
	 * Returns the reCAPTCHA minimum score Plugin setting.
	 *
	 * @since   3.0.0
	 *
	 * @return  float
	 */
	public function recaptcha_minimum_score() {

		return (float) $this->settings['recaptcha_minimum_score'];

	}

	/**
	 * Returns the Cloudflare Turnstile Site Key Plugin setting.
	 *
	 * @since   3.3.7
	 *
	 * @return  string
	 */
	public function cloudflare_turnstile_site_key() {

		return $this->settings['cloudflare_turnstile_site_key'];

	}

	/**
	 * Returns whether the Cloudflare Turnstile Site Key has been set in the Plugin settings.
	 *
	 * @since   3.3.7
	 *
	 * @return  bool
	 */
	public function has_cloudflare_turnstile_site_key() {

		return ! empty( $this->cloudflare_turnstile_site_key() );

	}

	/**
	 * Returns the Cloudflare Turnstile Secret Key Plugin setting.
	 *
	 * @since   3.3.7
	 *
	 * @return  string
	 */
	public function cloudflare_turnstile_secret_key() {

		return $this->settings['cloudflare_turnstile_secret_key'];

	}

	/**
	 * Returns whether the Cloudflare Turnstile Secret Key has been set in the Plugin settings.
	 *
	 * @since   3.3.7
	 *
	 * @return  bool
	 */
	public function has_cloudflare_turnstile_secret_key() {

		return ! empty( $this->cloudflare_turnstile_secret_key() );

	}

	/**
	 * Returns whether the Cloudflare Turnstile Site Key and Secret Key are defined
	 * in the Plugin settings.
	 *
	 * @since   3.3.7
	 *
	 * @return  bool
	 */
	public function has_cloudflare_turnstile_site_and_secret_keys() {

		return $this->has_cloudflare_turnstile_site_key() && $this->has_cloudflare_turnstile_secret_key();

	}

	/**
	 * Returns whether debugging is enabled in the Plugin settings.
	 *
	 * @since   1.9.6
	 *
	 * @return  bool
	 */
	public function debug_enabled() {

		return ( $this->settings['debug'] === 'on' ? true : false );

	}

	/**
	 * Returns whether scripts are disabled in the Plugin settings.
	 *
	 * @since   1.9.6
	 *
	 * @return  bool
	 */
	public function scripts_disabled() {

		return ( $this->settings['no_scripts'] === 'on' ? true : false );

	}

	/**
	 * Returns whether stylesheets are disabled in the Plugin settings.
	 *
	 * @since   1.9.6.9
	 *
	 * @return  bool
	 */
	public function css_disabled() {

		return ( $this->settings['no_css'] === 'on' ? true : false );

	}

	/**
	 * Returns whether the Add New Landing Page / Member Content button is disabled in the Plugin settings.
	 *
	 * @since   3.2.0
	 *
	 * @return  bool
	 */
	public function add_new_button_disabled() {

		return ( $this->settings['no_add_new_button'] === 'on' ? true : false );

	}

	/**
	 * Returns whether usage tracking is enabled in the Plugin settings.
	 *
	 * @since   3.0.4
	 *
	 * @return  bool
	 */
	public function usage_tracking() {

		return ( $this->settings['usage_tracking'] === 'on' ? true : false );

	}

	/**
	 * Returns this settings group's programmatic name.
	 *
	 * @since   3.4.0
	 *
	 * @return  string
	 */
	public function get_name() {

		return 'general';

	}

	/**
	 * Returns the title of this settings group.
	 *
	 * @since   3.4.0
	 *
	 * @return  string
	 */
	public function get_title() {

		return __( 'General Settings', 'convertkit' );

	}

	/**
	 * Returns the keys in this settings group that hold credentials or other
	 * sensitive values.
	 *
	 * @since   3.4.0
	 *
	 * @return  string[]
	 */
	public function get_secret_keys() {

		return array(
			'access_token',
			'refresh_token',
			'token_expires',
			'api_key',
			'api_secret',
			'recaptcha_secret_key',
		);

	}

	/**
	 * Returns the JSON Schema describing this settings group, in the shape
	 * stored by save() / returned by get(), excluding secret keys.
	 *
	 * @since   3.4.0
	 *
	 * @return  array
	 */
	public function get_schema() {

		$properties = array(
			'non_inline_form'                    => array(
				'type'        => 'array',
				'items'       => array( 'type' => 'integer' ),
				'description' => __( 'IDs of non-inline Forms to display site-wide.', 'convertkit' ),
			),
			'non_inline_form_honor_none_setting' => array(
				'type'        => 'string',
				'enum'        => array( '', 'on' ),
				'description' => __( 'Whether the site-wide non-inline Form honors a per-Page / per-Post "None" Form setting.', 'convertkit' ),
			),
			'non_inline_form_limit_per_session'  => array(
				'type'        => 'string',
				'enum'        => array( '', 'on' ),
				'description' => __( 'Whether to limit non-inline Form display to once per session.', 'convertkit' ),
			),
			'recaptcha_site_key'                 => array(
				'type'        => 'string',
				'description' => __( 'Google reCAPTCHA v3 site key.', 'convertkit' ),
			),
			'recaptcha_minimum_score'            => array(
				'type'        => 'number',
				'minimum'     => 0,
				'maximum'     => 1,
				'description' => __( 'Minimum Google reCAPTCHA v3 score (0.0 - 1.0) below which a request is treated as spam.', 'convertkit' ),
			),
			'debug'                              => array(
				'type'        => 'string',
				'enum'        => array( '', 'on' ),
				'description' => __( 'Whether debug logging is enabled.', 'convertkit' ),
			),
			'no_scripts'                         => array(
				'type'        => 'string',
				'enum'        => array( '', 'on' ),
				'description' => __( 'Whether the Plugin\'s frontend JavaScript is disabled.', 'convertkit' ),
			),
			'no_css'                             => array(
				'type'        => 'string',
				'enum'        => array( '', 'on' ),
				'description' => __( 'Whether the Plugin\'s frontend CSS is disabled.', 'convertkit' ),
			),
			'no_add_new_button'                  => array(
				'type'        => 'string',
				'enum'        => array( '', 'on' ),
				'description' => __( 'Whether the "Add New" button for Landing Pages / Member Content is hidden in the WordPress Admin.', 'convertkit' ),
			),
			'usage_tracking'                     => array(
				'type'        => 'string',
				'enum'        => array( '', 'on' ),
				'description' => __( 'Whether anonymous usage tracking is enabled.', 'convertkit' ),
			),
		);

		// Per-post-type Default Form settings, mirroring get_defaults().
		foreach ( convertkit_get_supported_post_types() as $post_type ) {
			$properties[ $post_type . '_form' ] = array(
				'type'        => 'integer',
				'minimum'     => -1,
				'description' => sprintf(
					/* translators: Post type slug. */
					__( 'Default Form ID to display on %s. `-1` = Plugin Default; `0` = None; positive integer = specific Kit Form ID.', 'convertkit' ),
					$post_type
				),
			);

			$properties[ $post_type . '_form_position' ] = array(
				'type'        => 'string',
				'enum'        => array( 'before_content', 'after_content', 'before_after_content', 'after_element' ),
				'description' => sprintf(
					/* translators: Post type slug. */
					__( 'Where the Default Form displays relative to the %s content.', 'convertkit' ),
					$post_type
				),
			);

			$properties[ $post_type . '_form_position_element' ] = array(
				'type'        => 'string',
				'enum'        => array( 'p', 'h2', 'h3', 'h4', 'h5', 'h6', 'img' ),
				'description' => sprintf(
					/* translators: 1: Post type slug, 2: Post type slug. */
					__( 'HTML element after which to display the Default Form on %1$s. Only used when `%2$s_form_position` is `after_element`.', 'convertkit' ),
					$post_type,
					$post_type
				),
			);

			$properties[ $post_type . '_form_position_element_index' ] = array(
				'type'        => 'integer',
				'minimum'     => 1,
				'maximum'     => 999,
				'description' => sprintf(
					/* translators: 1: Post type slug, 2: Post type slug. */
					__( 'Nth occurrence of the element after which to display the Default Form on %1$s. Only used when `%2$s_form_position` is `after_element`.', 'convertkit' ),
					$post_type,
					$post_type
				),
			);
		}

		return array(
			'type'                 => 'object',
			'additionalProperties' => false,
			'properties'           => $properties,
		);

	}

	/**
	 * The default settings, used when the ConvertKit Plugin Settings haven't been saved
	 * e.g. on a new installation.
	 *
	 * @since   1.9.6
	 *
	 * @return  array
	 */
	public function get_defaults() {

		$defaults = array(
			// OAuth.
			'access_token'                       => '', // string.
			'refresh_token'                      => '', // string.
			'token_expires'                      => '', // integer.

			// API Key. Retained if needed for backward compat.
			'api_key'                            => '', // string.
			'api_secret'                         => '', // string.

			// Site Wide.
			'non_inline_form'                    => array(), // array.
			'non_inline_form_honor_none_setting' => '', // blank|on.
			'non_inline_form_limit_per_session'  => '', // blank|on.

			// Spam Protection.
			'spam_protection_provider'           => 'recaptcha', // recaptcha|turnstile.

			// reCAPTCHA.
			'recaptcha_site_key'                 => '', // string.
			'recaptcha_secret_key'               => '', // string.
			'recaptcha_minimum_score'            => 0.5, // float.

			// Cloudflare Turnstile.
			'cloudflare_turnstile_site_key'      => '', // string.
			'cloudflare_turnstile_secret_key'    => '', // string.

			// Advanced.
			'debug'                              => '', // blank|on.
			'no_scripts'                         => '', // blank|on.
			'no_css'                             => '', // blank|on.
			'no_add_new_button'                  => '', // blank|on.
			'usage_tracking'                     => '', // blank|on.
		);

		// Add Post Type Default Forms.
		foreach ( convertkit_get_supported_post_types() as $post_type ) {
			$defaults[ $post_type . '_form' ]                        = 0; // -1, 0 or Form ID.
			$defaults[ $post_type . '_form_position' ]               = 'after_content'; // before_content,after_content,before_after_content,element.
			$defaults[ $post_type . '_form_position_element' ]       = 'p';
			$defaults[ $post_type . '_form_position_element_index' ] = 1;
		}

		/**
		 * The default settings, used when the ConvertKit Plugin Settings haven't been saved
		 * e.g. on a new installation.
		 *
		 * @since   1.9.6
		 *
		 * @param   array   $defaults   Default Settings.
		 */
		$defaults = apply_filters( 'convertkit_settings_get_defaults', $defaults );

		return $defaults;

	}

	/**
	 * Saves the new access token, refresh token and its expiry, and schedules
	 * a WordPress Cron event to refresh the token on expiry.
	 *
	 * @since   2.8.3
	 *
	 * @param   array $result      New Access Token, Refresh Token and Expiry.
	 */
	public function update_credentials( $result ) {

		// Remove any existing persistent notice.
		WP_ConvertKit()->get_class( 'admin_notices' )->delete( 'authorization_failed' );

		$this->save(
			array(
				'access_token'  => $result['access_token'],
				'refresh_token' => $result['refresh_token'],
				'token_expires' => ( time() + $result['expires_in'] ),
			)
		);

		// Clear any existing scheduled WordPress Cron event.
		wp_clear_scheduled_hook( 'convertkit_refresh_token' );

		// Schedule a WordPress Cron event to refresh the token on expiry.
		wp_schedule_single_event( ( time() + $result['expires_in'] ), 'convertkit_refresh_token' );

	}

	/**
	 * Deletes any existing access token, refresh token and its expiry from the Plugin settings,
	 * and clears any existing scheduled WordPress Cron event to refresh the token on expiry.
	 *
	 * @since   2.5.0
	 */
	public function delete_credentials() {

		$this->save(
			array(
				// OAuth.
				'access_token'  => '',
				'refresh_token' => '',
				'token_expires' => '',

				// API Key.
				'api_key'       => '',
				'api_secret'    => '',
			)
		);

		// Clear any existing scheduled WordPress Cron event.
		wp_clear_scheduled_hook( 'convertkit_refresh_token' );

	}

	/**
	 * Saves the given array of settings to the WordPress options table.
	 *
	 * @since   1.9.8.4
	 *
	 * @param   array $settings   Settings.
	 */
	public function save( $settings ) {

		update_option( self::SETTINGS_NAME, array_merge( $this->get(), $settings ) );

		// Reload settings in class, to reflect changes.
		$this->refresh_settings();

	}

	/**
	 * Reloads settings from the options table so this instance has the latest values.
	 *
	 * @since  3.1.1
	 */
	private function refresh_settings() {

		$settings = get_option( self::SETTINGS_NAME );

		if ( ! $settings ) {
			$this->settings = $this->get_defaults();
			return;
		}

		$this->settings = array_merge( $this->get_defaults(), $settings );

	}

}

```
