Plugin Directory

Changeset 3399438


Ignore:
Timestamp:
11/20/2025 06:41:53 AM (2 months ago)
Author:
convertkit
Message:

Update to version 3.1.0 from GitHub

Location:
convertkit
Files:
8 added
2 deleted
62 edited
1 copied

Legend:

Unmodified
Added
Removed
  • convertkit/tags/3.1.0/CHANGELOG.md

    r3390429 r3399438  
     1### 3.1.0 2025-11-20
     2* Added: Settings: Tools: Mailchimp for WordPress to Kit Form Importer
     3* Fix: Settings: Non-inline Forms: Correctly output `data-kit-limit-per-session`
     4* Fix: Settings: Automatically delete invalid Access Tokens
     5* Fix: Member Content: Courses: Next/Previous Links compatibility for WordPress 6.9
     6* Fix: Landing Pages: Prevent LazyLoad Plugin from lazy loading images on Landing Pages, which would result in missing images
     7* Updated: Blocks: Refresh Resources: Use REST API, replacing admin-ajax.php
     8* Updated: Refresh Resources: Use REST API, replacing admin-ajax.php
     9* Updated: Member Content: Use REST API, replacing admin-ajax.php
     10* Updated: Use WordPress Libraries 2.1.1
     11* Removed: Unused `convertkit_store_subscriber_id_in_cookie` AJAX function
     12
    113### 3.0.8 2025-11-05
    214* Fix: Member Content: Product: Display 'no access' notice when logged in and no access
  • convertkit/tags/3.1.0/admin/class-convertkit-admin-refresh-resources.php

    r3251976 r3399438  
    2323    public function __construct() {
    2424
    25         add_action( 'wp_ajax_convertkit_admin_refresh_resources', array( $this, 'refresh_resources' ) );
    2625        add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_scripts' ) );
     26        add_action( 'rest_api_init', array( $this, 'register_routes' ) );
     27
     28    }
     29
     30    /**
     31     * Register REST API routes.
     32     *
     33     * @since   3.1.0
     34     */
     35    public function register_routes() {
     36
     37        // Register route to return all blocks registered by the Plugin.
     38        register_rest_route(
     39            'kit/v1',
     40            '/resources/refresh/(?P<resource>[a-zA-Z0-9-_]+)',
     41            array(
     42                'methods'             => WP_REST_Server::CREATABLE,
     43                'callback'            => array( $this, 'refresh_resources' ),
     44                'permission_callback' => function () {
     45                    return current_user_can( 'edit_posts' );
     46                },
     47            )
     48        );
    2749
    2850    }
     
    3254     *
    3355     * @since   1.9.8.0
     56     *
     57     * @param   WP_REST_Request $request    Request object.
     58     * @return  WP_REST_Response|WP_Error               Response object.
    3459     */
    35     public function refresh_resources() {
    36 
    37         // Check nonce.
    38         check_ajax_referer( 'convertkit_admin_refresh_resources', 'nonce' );
     60    public function refresh_resources( $request ) {
    3961
    4062        // Get resource type.
    41         $resource = ( isset( $_REQUEST['resource'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['resource'] ) ) : '' );
     63        $resource = $request->get_param( 'resource' );
    4264
    4365        // Fetch resources.
     
    7597                // Bail if an error occured.
    7698                if ( is_wp_error( $results_tags ) ) {
    77                     wp_send_json_error( $results_tags->get_error_message() );
     99                    return rest_ensure_response( $results_tags );
    78100                }
    79101
     
    84106                // Bail if an error occured.
    85107                if ( is_wp_error( $results_products ) ) {
    86                     wp_send_json_error( $results_products->get_error_message() );
     108                    return rest_ensure_response( $results_products );
    87109                }
    88110
    89111                // Return resources.
    90                 wp_send_json_success(
     112                return rest_ensure_response(
    91113                    array(
    92114                        'tags'     => array_values( $results_tags ),
     
    94116                    )
    95117                );
    96                 // no break as wp_send_json_success terminates.
    97118
    98119            default:
     
    109130        // Bail if an error occured.
    110131        if ( is_wp_error( $results ) ) {
    111             wp_send_json_error( $results->get_error_message() );
     132            return rest_ensure_response( $results );
    112133        }
    113134
    114135        // Return resources as a zero based sequential array, so that JS retains the order of resources.
    115         wp_send_json_success( array_values( $results ) );
     136        return rest_ensure_response( array_values( $results ) );
    116137
    117138    }
     
    145166            'convertkit_admin_refresh_resources',
    146167            array(
    147                 'action'  => 'convertkit_admin_refresh_resources',
    148                 'ajaxurl' => admin_url( 'admin-ajax.php' ),
     168                'ajaxurl' => rest_url( 'kit/v1/resources/refresh/' ),
    149169                'debug'   => $settings->debug_enabled(),
    150                 'nonce'   => wp_create_nonce( 'convertkit_admin_refresh_resources' ),
     170                'nonce'   => wp_create_nonce( 'wp_rest' ),
    151171            )
    152172        );
  • convertkit/tags/3.1.0/admin/section/class-convertkit-admin-section-general.php

    r3386855 r3399438  
    134134        // Bail if no access and refresh token exist.
    135135        if ( ! $this->settings->has_access_and_refresh_token() ) {
    136             return;
     136            // Redirect to General screen, which will now show the ConvertKit_Admin_Section_OAuth screen, because
     137            // the Plugin has no access token.
     138            wp_safe_redirect(
     139                add_query_arg(
     140                    array(
     141                        'page' => $this->settings_key,
     142                    ),
     143                    'options-general.php'
     144                )
     145            );
     146            exit();
    137147        }
    138148
     
    153163        // If the request succeeded, no need to perform further actions.
    154164        if ( ! is_wp_error( $this->account ) ) {
    155             // Remove any existing persistent notice.
    156             WP_ConvertKit()->get_class( 'admin_notices' )->delete( 'authorization_failed' );
    157 
    158165            return;
    159166        }
    160167
    161         // Depending on the error code, maybe persist a notice in the WordPress Administration until the user
     168        // Depending on the error code, display an error notice in the settings screen until the user
    162169        // fixes the problem.
    163         switch ( $this->account->get_error_data( $this->account->get_error_code() ) ) {
    164             case 401:
    165                 // Access token either expired or was revoked in ConvertKit.
    166                 // Remove from settings.
    167                 $this->settings->delete_credentials();
    168 
    169                 // Display a site wide notice.
    170                 WP_ConvertKit()->get_class( 'admin_notices' )->add( 'authorization_failed' );
    171 
    172                 // Redirect to General screen, which will now show the ConvertKit_Admin_Section_OAuth screen, because
    173                 // the Plugin has no access token.
    174                 wp_safe_redirect(
    175                     add_query_arg(
    176                         array(
    177                             'page' => $this->settings_key,
    178                         ),
    179                         'options-general.php'
    180                     )
    181                 );
    182                 exit();
    183         }
    184 
    185         // Output a non-401 error now.
    186170        $this->output_error( $this->account->get_error_message() );
    187171
  • convertkit/tags/3.1.0/admin/section/class-convertkit-admin-section-tools.php

    r3357832 r3399438  
    5858                'import_configuration_empty'             => __( 'The uploaded configuration file contains no settings.', 'convertkit' ),
    5959                'import_configuration_success'           => __( 'Configuration imported successfully.', 'convertkit' ),
     60                'migrate_mc4wp_configuration_success'    => __( 'MC4WP forms migrated successfully.', 'convertkit' ),
    6061            )
    6162        );
     
    7677        $this->maybe_export_configuration();
    7778        $this->maybe_import_configuration();
     79        $this->maybe_migrate_mc4wp_configuration();
    7880
    7981    }
     
    316318
    317319    /**
     320     * Replaces MC4WP Form Shortcodes with Kit Form Shortcodes, if the user submitted the
     321     * MC4WP Migrate Configuration section.
     322     *
     323     * @since   3.1.0
     324     */
     325    private function maybe_migrate_mc4wp_configuration() {
     326
     327        // Bail if nonce verification fails.
     328        if ( ! isset( $_REQUEST['_convertkit_settings_tools_nonce'] ) ) {
     329            return;
     330        }
     331
     332        if ( ! wp_verify_nonce( sanitize_key( $_REQUEST['_convertkit_settings_tools_nonce'] ), 'convertkit-settings-tools' ) ) {
     333            return;
     334        }
     335
     336        // Bail if no MC4WP Form IDs were submitted.
     337        if ( ! isset( $_REQUEST['_wp_convertkit_integration_mc4wp_settings'] ) ) {
     338            return;
     339        }
     340
     341        // Initialise the importer.
     342        $mc4wp = new ConvertKit_Admin_Importer_MC4WP();
     343
     344        // Iterate through the MC4WP Form IDs and replace the shortcodes with the Kit Form Shortcodes.
     345        foreach ( array_map( 'sanitize_text_field', wp_unslash( $_REQUEST['_wp_convertkit_integration_mc4wp_settings'] ) ) as $mc4wp_form_id => $kit_form_id ) {
     346            $mc4wp->replace_shortcodes_in_posts( (int) $mc4wp_form_id, (int) $kit_form_id );
     347        }
     348
     349        // Redirect to Tools screen.
     350        $this->redirect_with_success_notice( 'migrate_mc4wp_configuration_success' );
     351
     352    }
     353
     354    /**
    318355     * Outputs the Debug Log and System Info view.
    319356     *
     
    333370        $system_info = $this->get_system_info();
    334371
     372        // Get Forms.
     373        $forms = new ConvertKit_Resource_Forms();
     374
     375        // Get Importers.
     376        $mc4wp = new ConvertKit_Admin_Importer_MC4WP();
     377
    335378        // Output view.
    336379        require_once CONVERTKIT_PLUGIN_PATH . '/views/backend/settings/tools.php';
  • convertkit/tags/3.1.0/includes/blocks/class-convertkit-block-broadcasts.php

    r3383204 r3399438  
    273273    public function get_fields() {
    274274
    275         // Bail if the request is not for the WordPress Administration or frontend editor.
    276         if ( ! WP_ConvertKit()->is_admin_or_frontend_editor() ) {
     275        // Bail if the request is not for the WordPress Administration, frontend editor or REST API request.
     276        if ( ! $this->is_admin_frontend_editor_or_rest_request() ) {
    277277            return false;
    278278        }
     
    381381    public function get_panels() {
    382382
    383         // Bail if the request is not for the WordPress Administration or frontend editor.
    384         if ( ! WP_ConvertKit()->is_admin_or_frontend_editor() ) {
     383        // Bail if the request is not for the WordPress Administration, frontend editor or REST API request.
     384        if ( ! $this->is_admin_frontend_editor_or_rest_request() ) {
    385385            return false;
    386386        }
  • convertkit/tags/3.1.0/includes/blocks/class-convertkit-block-content.php

    r3160977 r3399438  
    105105    public function get_fields() {
    106106
    107         // Bail if the request is not for the WordPress Administration or frontend editor.
    108         if ( ! WP_ConvertKit()->is_admin_or_frontend_editor() ) {
     107        // Bail if the request is not for the WordPress Administration, frontend editor or REST API request.
     108        if ( ! $this->is_admin_frontend_editor_or_rest_request() ) {
    109109            return false;
    110110        }
     
    138138    public function get_panels() {
    139139
    140         // Bail if the request is not for the WordPress Administration or frontend editor.
    141         if ( ! WP_ConvertKit()->is_admin_or_frontend_editor() ) {
     140        // Bail if the request is not for the WordPress Administration, frontend editor or REST API request.
     141        if ( ! $this->is_admin_frontend_editor_or_rest_request() ) {
    142142            return false;
    143143        }
  • convertkit/tags/3.1.0/includes/blocks/class-convertkit-block-form-builder-field-custom.php

    r3357832 r3399438  
    102102    public function get_fields() {
    103103
    104         // Bail if the request is not for the WordPress Administration or frontend editor.
    105         if ( ! WP_ConvertKit()->is_admin_or_frontend_editor() ) {
     104        // Bail if the request is not for the WordPress Administration, frontend editor or REST API request.
     105        if ( ! $this->is_admin_frontend_editor_or_rest_request() ) {
    106106            return false;
    107107        }
     
    151151    public function get_panels() {
    152152
    153         // Bail if the request is not for the WordPress Administration or frontend editor.
    154         if ( ! WP_ConvertKit()->is_admin_or_frontend_editor() ) {
     153        // Bail if the request is not for the WordPress Administration, frontend editor or REST API request.
     154        if ( ! $this->is_admin_frontend_editor_or_rest_request() ) {
    155155            return false;
    156156        }
  • convertkit/tags/3.1.0/includes/blocks/class-convertkit-block-form-builder-field-email.php

    r3357832 r3399438  
    121121    public function get_fields() {
    122122
    123         // Bail if the request is not for the WordPress Administration or frontend editor.
    124         if ( ! WP_ConvertKit()->is_admin_or_frontend_editor() ) {
     123        // Bail if the request is not for the WordPress Administration, frontend editor or REST API request.
     124        if ( ! $this->is_admin_frontend_editor_or_rest_request() ) {
    125125            return false;
    126126        }
  • convertkit/tags/3.1.0/includes/blocks/class-convertkit-block-form-builder-field.php

    r3359712 r3399438  
    195195    public function get_fields() {
    196196
    197         // Bail if the request is not for the WordPress Administration or frontend editor.
    198         if ( ! WP_ConvertKit()->is_admin_or_frontend_editor() ) {
     197        // Bail if the request is not for the WordPress Administration, frontend editor or REST API request.
     198        if ( ! $this->is_admin_frontend_editor_or_rest_request() ) {
    199199            return false;
    200200        }
     
    224224    public function get_panels() {
    225225
    226         // Bail if the request is not for the WordPress Administration or frontend editor.
    227         if ( ! WP_ConvertKit()->is_admin_or_frontend_editor() ) {
     226        // Bail if the request is not for the WordPress Administration, frontend editor or REST API request.
     227        if ( ! $this->is_admin_frontend_editor_or_rest_request() ) {
    228228            return false;
    229229        }
  • convertkit/tags/3.1.0/includes/blocks/class-convertkit-block-form-builder.php

    r3383204 r3399438  
    476476    public function get_fields() {
    477477
    478         // Bail if the request is not for the WordPress Administration or frontend editor.
    479         if ( ! WP_ConvertKit()->is_admin_or_frontend_editor() ) {
     478        // Bail if the request is not for the WordPress Administration, frontend editor or REST API request.
     479        if ( ! $this->is_admin_frontend_editor_or_rest_request() ) {
    480480            return false;
    481481        }
     
    569569    public function get_panels() {
    570570
    571         // Bail if the request is not for the WordPress Administration or frontend editor.
    572         if ( ! WP_ConvertKit()->is_admin_or_frontend_editor() ) {
     571        // Bail if the request is not for the WordPress Administration, frontend editor or REST API request.
     572        if ( ! $this->is_admin_frontend_editor_or_rest_request() ) {
    573573            return false;
    574574        }
  • convertkit/tags/3.1.0/includes/blocks/class-convertkit-block-form-trigger.php

    r3337204 r3399438  
    213213    public function get_fields() {
    214214
    215         // Bail if the request is not for the WordPress Administration or frontend editor.
    216         if ( ! WP_ConvertKit()->is_admin_or_frontend_editor() ) {
     215        // Bail if the request is not for the WordPress Administration, frontend editor or REST API request.
     216        if ( ! $this->is_admin_frontend_editor_or_rest_request() ) {
    217217            return false;
    218218        }
     
    271271    public function get_panels() {
    272272
    273         // Bail if the request is not for the WordPress Administration or frontend editor.
    274         if ( ! WP_ConvertKit()->is_admin_or_frontend_editor() ) {
     273        // Bail if the request is not for the WordPress Administration, frontend editor or REST API request.
     274        if ( ! $this->is_admin_frontend_editor_or_rest_request() ) {
    275275            return false;
    276276        }
  • convertkit/tags/3.1.0/includes/blocks/class-convertkit-block-form.php

    r3357832 r3399438  
    241241    public function get_fields() {
    242242
    243         // Bail if the request is not for the WordPress Administration or frontend editor.
    244         if ( ! WP_ConvertKit()->is_admin_or_frontend_editor() ) {
     243        // Bail if the request is not for the WordPress Administration, frontend editor or REST API request.
     244        if ( ! $this->is_admin_frontend_editor_or_rest_request() ) {
    245245            return false;
    246246        }
     
    286286    public function get_panels() {
    287287
    288         // Bail if the request is not for the WordPress Administration or frontend editor.
    289         if ( ! WP_ConvertKit()->is_admin_or_frontend_editor() ) {
     288        // Bail if the request is not for the WordPress Administration, frontend editor or REST API request.
     289        if ( ! $this->is_admin_frontend_editor_or_rest_request() ) {
    290290            return false;
    291291        }
  • convertkit/tags/3.1.0/includes/blocks/class-convertkit-block-product.php

    r3337204 r3399438  
    246246    public function get_fields() {
    247247
    248         // Bail if the request is not for the WordPress Administration or frontend editor.
    249         if ( ! WP_ConvertKit()->is_admin_or_frontend_editor() ) {
     248        // Bail if the request is not for the WordPress Administration, frontend editor or REST API request.
     249        if ( ! $this->is_admin_frontend_editor_or_rest_request() ) {
    250250            return false;
    251251        }
     
    313313    public function get_panels() {
    314314
    315         // Bail if the request is not for the WordPress Administration or frontend editor.
    316         if ( ! WP_ConvertKit()->is_admin_or_frontend_editor() ) {
     315        // Bail if the request is not for the WordPress Administration, frontend editor or REST API request.
     316        if ( ! $this->is_admin_frontend_editor_or_rest_request() ) {
    317317            return false;
    318318        }
  • convertkit/tags/3.1.0/includes/blocks/class-convertkit-block.php

    r3337204 r3399438  
    397397
    398398    /**
     399     * Determines if the request is a WordPress REST API request.
     400     *
     401     * @since   3.1.0
     402     *
     403     * @return  bool
     404     */
     405    public function is_rest_request() {
     406
     407        return defined( 'REST_REQUEST' ) && REST_REQUEST;
     408
     409    }
     410
     411    /**
     412     * Determines if the request is for the WordPress Administration, frontend editor or REST API request.
     413     *
     414     * @since   3.1.0
     415     *
     416     * @return  bool
     417     */
     418    public function is_admin_frontend_editor_or_rest_request() {
     419
     420        return WP_ConvertKit()->is_admin_or_frontend_editor() || $this->is_rest_request();
     421
     422    }
     423
     424    /**
    399425     * Determines if the request for the block is from the block editor or the frontend site.
    400426     *
     
    406432
    407433        // Return false if not a WordPress REST API request, which Gutenberg uses.
    408         if ( ! defined( 'REST_REQUEST' ) ) {
    409             return false;
    410         }
    411         if ( REST_REQUEST !== true ) {
     434        if ( ! $this->is_rest_request() ) {
    412435            return false;
    413436        }
  • convertkit/tags/3.1.0/includes/class-convertkit-ajax.php

    r3380693 r3399438  
    2121    public function __construct() {
    2222
    23         add_action( 'wp_ajax_convertkit_get_blocks', array( $this, 'get_blocks' ) );
    24 
    25         add_action( 'wp_ajax_nopriv_convertkit_store_subscriber_id_in_cookie', array( $this, 'store_subscriber_id_in_cookie' ) );
    26         add_action( 'wp_ajax_convertkit_store_subscriber_id_in_cookie', array( $this, 'store_subscriber_id_in_cookie' ) );
    27 
    2823        add_action( 'wp_ajax_nopriv_convertkit_store_subscriber_email_as_id_in_cookie', array( $this, 'store_subscriber_email_as_id_in_cookie' ) );
    2924        add_action( 'wp_ajax_convertkit_store_subscriber_email_as_id_in_cookie', array( $this, 'store_subscriber_email_as_id_in_cookie' ) );
    30 
    31         add_action( 'wp_ajax_nopriv_convertkit_subscriber_authentication_send_code', array( $this, 'subscriber_authentication_send_code' ) );
    32         add_action( 'wp_ajax_convertkit_subscriber_authentication_send_code', array( $this, 'subscriber_authentication_send_code' ) );
    33 
    34         add_action( 'wp_ajax_nopriv_convertkit_subscriber_verification', array( $this, 'subscriber_verification' ) );
    35         add_action( 'wp_ajax_convertkit_subscriber_verification', array( $this, 'subscriber_verification' ) );
    36 
    37     }
    38 
    39     /**
    40      * Returns all ConvertKit registered blocks.
    41      *
    42      * Typically used when a refresh button in a block has been pressed when
    43      * displayNoticeWithLink() is called, because either
    44      * no Access Token is specified, or no resources exist in ConvertKit.
    45      *
    46      * @since   2.2.6
    47      */
    48     public function get_blocks() {
    49 
    50         // Check nonce.
    51         check_ajax_referer( 'convertkit_get_blocks', 'nonce' );
    52 
    53         // Refresh resources from the API, to reflect any changes.
    54         $forms = new ConvertKit_Resource_Forms( 'block_edit' );
    55         $forms->refresh();
    56 
    57         $posts = new ConvertKit_Resource_Posts( 'block_edit' );
    58         $posts->refresh();
    59 
    60         $products = new ConvertKit_Resource_Products( 'block_edit' );
    61         $products->refresh();
    62 
    63         // Return blocks.
    64         wp_send_json_success( convertkit_get_blocks() );
    65 
    66     }
    67 
    68     /**
    69      * Stores the ConvertKit Subscriber's ID in a cookie.
    70      *
    71      * Typically performed when the user subscribes via a ConvertKit Form on the web site
    72      * that is set to "Send subscriber to thank you page", and the Plugin's JavaScript is not
    73      * disabled, permitting convertkit.js to run.
    74      *
    75      * @since   1.9.6
    76      */
    77     public function store_subscriber_id_in_cookie() {
    78 
    79         // Check nonce.
    80         check_ajax_referer( 'convertkit', 'convertkit_nonce' );
    81 
    82         // Bail if required request parameters not submitted.
    83         if ( ! isset( $_REQUEST['subscriber_id'] ) ) {
    84             wp_send_json_error( __( 'Kit: Required parameter `subscriber_id` not included in AJAX request.', 'convertkit' ) );
    85         }
    86 
    87         // Bail if no subscriber ID provided.
    88         $id = absint( sanitize_text_field( wp_unslash( $_REQUEST['subscriber_id'] ) ) );
    89         if ( empty( $id ) ) {
    90             wp_send_json_error( __( 'Kit: Required parameter `subscriber_id` empty in AJAX request.', 'convertkit' ) );
    91         }
    92 
    93         // Get subscriber ID.
    94         $subscriber    = new ConvertKit_Subscriber();
    95         $subscriber_id = $subscriber->validate_and_store_subscriber_id( $id );
    96 
    97         // Bail if an error occured i.e. API hasn't been configured, subscriber ID does not exist in ConvertKit etc.
    98         if ( is_wp_error( $subscriber_id ) ) {
    99             wp_send_json_error( $subscriber_id->get_error_message() );
    100         }
    101 
    102         // Return the subscriber ID.
    103         wp_send_json_success(
    104             array(
    105                 'id' => $subscriber_id,
    106             )
    107         );
    10825
    10926    }
     
    15673    }
    15774
    158     /**
    159      * Calls the API to send the subscriber a magic link by email containing a code when
    160      * the modal version of Restrict Content is used, and the user has submitted their email address.
    161      *
    162      * Returns a view of either:
    163      * - an error message and email input i.e. the user entered an invalid email address,
    164      * - the code input, which is then displayed in the modal for the user to enter the code sent by email.
    165      *
    166      * See maybe_run_subscriber_verification() for logic once they enter the code on screen.
    167      *
    168      * @since   2.3.8
    169      */
    170     public function subscriber_authentication_send_code() {
    171 
    172         // Load Restrict Content class.
    173         $output_restrict_content = WP_ConvertKit()->get_class( 'output_restrict_content' );
    174 
    175         // Run subscriber authentication.
    176         $output_restrict_content->maybe_run_subscriber_authentication();
    177 
    178         // Fetch Post ID, Resource Type and Resource ID for the view.
    179         $post_id       = $output_restrict_content->post_id;
    180         $resource_type = $output_restrict_content->resource_type;
    181         $resource_id   = $output_restrict_content->resource_id;
    182 
    183         // If an error occured, build the email form view with the error message.
    184         if ( is_wp_error( $output_restrict_content->error ) ) {
    185             ob_start();
    186             include CONVERTKIT_PLUGIN_PATH . '/views/frontend/restrict-content/login-modal-content-email.php';
    187             $output = trim( ob_get_clean() );
    188             wp_send_json_success( $output );
    189         }
    190 
    191         // Build authentication code view to return for output.
    192         ob_start();
    193         include CONVERTKIT_PLUGIN_PATH . '/views/frontend/restrict-content/login-modal-content-code.php';
    194         $output = trim( ob_get_clean() );
    195         wp_send_json_success( $output );
    196 
    197     }
    198 
    199     /**
    200      * Calls the API to verify the token and entered subscriber code, which tells us that the email
    201      * address supplied truly belongs to the user, and that we can safely trust their subscriber ID
    202      * to be valid.
    203      *
    204      * @since   2.3.8
    205      */
    206     public function subscriber_verification() {
    207 
    208         // Load Restrict Content class.
    209         $output_restrict_content = WP_ConvertKit()->get_class( 'output_restrict_content' );
    210 
    211         // Run subscriber authentication.
    212         $output_restrict_content->maybe_run_subscriber_verification();
    213 
    214         // Fetch Post ID, Resource Type and Resource ID for the view.
    215         $post_id       = $output_restrict_content->post_id;
    216         $resource_type = $output_restrict_content->resource_type;
    217         $resource_id   = $output_restrict_content->resource_id;
    218 
    219         // If an error occured, build the code form view with the error message.
    220         if ( is_wp_error( $output_restrict_content->error ) ) {
    221             ob_start();
    222             include CONVERTKIT_PLUGIN_PATH . '/views/frontend/restrict-content/login-modal-content-code.php';
    223             $output = trim( ob_get_clean() );
    224             wp_send_json_error( $output );
    225         }
    226 
    227         // Return success with the URL to the Post, including the `ck-cache-bust` parameter.
    228         // JS will load the given URL to show the restricted content.
    229         wp_send_json_success( $output_restrict_content->get_url( true ) );
    230 
    231     }
    232 
    23375}
  • convertkit/tags/3.1.0/includes/class-convertkit-cache-plugins.php

    r3359712 r3399438  
    1616
    1717    /**
    18      * Holds external hosts to exclude from CSS and JS minification.
     18     * Holds external hosts to exclude from CSS and JS minification
     19     * and lazy loading of images.
    1920     *
    2021     * @since   2.4.6
     
    2829        'pages.convertkit.com',
    2930        'convertkit.com',
     31        'filekitcdn.com',
    3032    );
    3133
     
    5557
    5658        // Debloat: Exclude Forms from Delay Load JS.
    57         add_filter( 'debloat/defer_js_excludes', array( $this, 'exclude_hosts_from_minification' ) );
    58         add_filter( 'debloat/delay_js_excludes', array( $this, 'exclude_hosts_from_minification' ) );
     59        add_filter( 'debloat/defer_js_excludes', array( $this, 'exclude_hosts' ) );
     60        add_filter( 'debloat/delay_js_excludes', array( $this, 'exclude_hosts' ) );
    5961
    6062        // Jetpack Boost: Exclude Forms from JS defer.
     
    7577        add_filter( 'convertkit_resource_forms_output_script', array( $this, 'siteground_speed_optimizer_exclude_js_combine' ) );
    7678
     79        // Rocket LazyLoad: Exclude images from lazy loading.
     80        add_filter( 'rocket_lazyload_excluded_src', array( $this, 'exclude_hosts' ) );
     81
    7782        // WP Rocket: Disable Caching and Minification on Landing Pages.
    7883        add_action( 'convertkit_output_landing_page_before', array( $this, 'wp_rocket_disable_caching_and_minification_on_landing_pages' ) );
    7984
    8085        // WP Rocket: Exclude Forms from JS minification and combine.
    81         add_filter( 'rocket_minify_excluded_external_js', array( $this, 'exclude_hosts_from_minification' ) );
     86        add_filter( 'rocket_minify_excluded_external_js', array( $this, 'exclude_hosts' ) );
    8287
    8388        // WP Rocket: Exclude Forms from Delay JavaScript execution.
     
    239244    public function wp_rocket_disable_caching_and_minification_on_landing_pages() {
    240245
    241         add_filter( 'rocket_minify_excluded_external_js', array( $this, 'exclude_hosts_from_minification' ) );
    242         add_filter( 'rocket_exclude_css', array( $this, 'exclude_hosts_from_minification' ) );
     246        add_filter( 'rocket_minify_excluded_external_js', array( $this, 'exclude_hosts' ) );
     247        add_filter( 'rocket_exclude_css', array( $this, 'exclude_hosts' ) );
    243248        add_filter( 'rocket_exclude_js', array( $this, 'exclude_local_js_from_minification' ) );
    244         add_filter( 'do_rocket_lazyload', '__return_false' );
    245249
    246250    }
     
    278282    public function exclude_hosts_from_minification( $hosts ) {
    279283
     284        _doing_it_wrong( __FUNCTION__, 'Use exclude_hosts() instead.', '3.1.0' );
     285
     286        return $this->exclude_hosts( $hosts );
     287
     288    }
     289
     290    /**
     291     * Appends the $exclude_hosts property to an array of existing hosts.
     292     *
     293     * @since   3.1.0
     294     *
     295     * @param   array $hosts  External hosts to ignore.
     296     * @return  array
     297     */
     298    public function exclude_hosts( $hosts ) {
     299
    280300        return array_merge( $hosts, $this->exclude_hosts );
    281301
  • convertkit/tags/3.1.0/includes/class-convertkit-gutenberg.php

    r3203903 r3399438  
    3232        // Register Gutenberg Blocks.
    3333        add_action( 'init', array( $this, 'add_blocks' ) );
     34
     35        // Register REST API routes.
     36        add_action( 'rest_api_init', array( $this, 'register_routes' ) );
     37
     38    }
     39
     40    /**
     41     * Register REST API routes.
     42     *
     43     * @since   3.1.0
     44     */
     45    public function register_routes() {
     46
     47        // Register route to return all blocks registered by the Plugin.
     48        register_rest_route(
     49            'kit/v1',
     50            '/blocks',
     51            array(
     52                'methods'             => WP_REST_Server::READABLE,
     53
     54                // Refresh resources and return blocks.
     55                'callback'            => function () {
     56                    // Refresh resources from the API, to reflect any changes.
     57                    $forms = new ConvertKit_Resource_Forms( 'block_edit' );
     58                    $forms->refresh();
     59
     60                    $posts = new ConvertKit_Resource_Posts( 'block_edit' );
     61                    $posts->refresh();
     62
     63                    $products = new ConvertKit_Resource_Products( 'block_edit' );
     64                    $products->refresh();
     65
     66                    // Return blocks.
     67                    return rest_ensure_response( convertkit_get_blocks() );
     68                },
     69
     70                'permission_callback' => function () {
     71                    return current_user_can( 'edit_posts' );
     72                },
     73            )
     74        );
    3475
    3576    }
     
    158199            'convertkit_gutenberg',
    159200            array(
    160                 'get_blocks_nonce' => wp_create_nonce( 'convertkit_get_blocks' ),
     201                'ajaxurl'          => rest_url( 'kit/v1/blocks' ),
     202                'get_blocks_nonce' => wp_create_nonce( 'wp_rest' ),
    161203            )
    162204        );
  • convertkit/tags/3.1.0/includes/class-convertkit-output-restrict-content.php

    r3380693 r3399438  
    107107    public function __construct() {
    108108
    109         // Initialize classes that will be used.
    110         $this->settings                  = new ConvertKit_Settings();
    111         $this->restrict_content_settings = new ConvertKit_Settings_Restrict_Content();
    112 
    113         // Don't register any hooks if this is an AJAX request, otherwise
    114         // maybe_run_subscriber_authentication() and maybe_run_subscriber_verification() will run
    115         // twice in an AJAX request (once here, and once when called by the ConvertKit_AJAX class).
    116         if ( wp_doing_ajax() ) {
    117             return;
    118         }
    119 
     109        add_action( 'rest_api_init', array( $this, 'register_routes' ) );
     110        add_action( 'init', array( $this, 'initialize_classes' ), 2 );
    120111        add_action( 'init', array( $this, 'maybe_run_subscriber_authentication' ), 3 );
    121112        add_action( 'wp', array( $this, 'maybe_run_subscriber_verification' ), 4 );
     
    129120
    130121    /**
    131      * Checks if the request is a Restrict Content request with an email address.
    132      * If so, calls the API depending on the Restrict Content resource that's required:
    133      * - tag: subscribes the email address to the tag, storing the subscriber ID in a cookie and redirecting
    134      * - product: calls the API to send the subscriber a magic link by email containing a code. See maybe_run_subscriber_verification()
    135      * for logic once they click the link in the email or enter the code on screen.
    136      *
    137      * @since   2.1.0
    138      */
    139     public function maybe_run_subscriber_authentication() {
    140 
    141         // Bail if no nonce was specified.
    142         if ( ! array_key_exists( '_wpnonce', $_REQUEST ) ) {
    143             return;
    144         }
    145 
    146         // Bail if the nonce failed validation.
    147         if ( ! wp_verify_nonce( sanitize_key( $_REQUEST['_wpnonce'] ), 'convertkit_restrict_content_login' ) ) {
    148             return;
    149         }
    150 
    151         // Bail if the expected email, resource ID or Post ID are missing.
    152         if ( ! array_key_exists( 'convertkit_email', $_REQUEST ) ) {
    153             return;
    154         }
    155         if ( ! array_key_exists( 'convertkit_resource_type', $_REQUEST ) ) {
    156             return;
    157         }
    158         if ( ! array_key_exists( 'convertkit_resource_id', $_REQUEST ) ) {
    159             return;
    160         }
    161         if ( ! array_key_exists( 'convertkit_post_id', $_REQUEST ) ) {
    162             return;
    163         }
    164 
    165         // If the Plugin Access Token has not been configured, we can't get this subscriber's ID by email.
    166         if ( ! $this->settings->has_access_and_refresh_token() ) {
    167             return;
    168         }
    169 
    170         // Initialize the API.
    171         $this->api = new ConvertKit_API_V4(
     122     * Register REST API routes.
     123     *
     124     * @since   3.1.0
     125     */
     126    public function register_routes() {
     127
     128        // Register route to run subscriber authentication.
     129        register_rest_route(
     130            'kit/v1',
     131            '/restrict-content/subscriber-authentication',
     132            array(
     133                'methods'             => WP_REST_Server::CREATABLE,
     134                'callback'            => function ( $request ) {
     135
     136                    // Initialize classes that will be used.
     137                    $output_restrict_content = WP_ConvertKit()->get_class( 'output_restrict_content' );
     138                    $output_restrict_content->initialize_classes();
     139
     140                    // Fetch Post ID, Resource Type and Resource ID for the view.
     141                    $email         = $request->get_param( 'convertkit_email' );
     142                    $post_id       = $request->get_param( 'convertkit_post_id' );
     143                    $resource_type = $request->get_param( 'convertkit_resource_type' );
     144                    $resource_id   = $request->get_param( 'convertkit_resource_id' );
     145
     146                    // Run subscriber authentication.
     147                    $result = $output_restrict_content->subscriber_authentication_send_code(
     148                        $email,
     149                        $post_id
     150                    );
     151
     152                    // If an error occured, build the email form view with the error message.
     153                    if ( is_wp_error( $result ) ) {
     154                        // Set error to display on screen.
     155                        $output_restrict_content->error = $result;
     156
     157                        // Build email form view to return for output with error message.
     158                        ob_start();
     159                        include CONVERTKIT_PLUGIN_PATH . '/views/frontend/restrict-content/login-modal-content-email.php';
     160                        $output = trim( ob_get_clean() );
     161                        return rest_ensure_response(
     162                            array(
     163                                'success' => false,
     164                                'data'    => $output,
     165                            )
     166                        );
     167                    }
     168
     169                    // Set token and Post ID for authentication code view.
     170                    $output_restrict_content->token = $result;
     171                    $output_restrict_content->post_id = $post_id;
     172
     173                    // Build authentication code view to return for output.
     174                    ob_start();
     175                    include CONVERTKIT_PLUGIN_PATH . '/views/frontend/restrict-content/login-modal-content-code.php';
     176                    $output = trim( ob_get_clean() );
     177                    return rest_ensure_response(
     178                        array(
     179                            'success' => true,
     180                            'data'    => $output,
     181                        )
     182                    );
     183                },
     184                'permission_callback' => '__return_true',
     185            )
     186        );
     187
     188        // Register route to run subscriber verification.
     189        register_rest_route(
     190            'kit/v1',
     191            '/restrict-content/subscriber-verification',
     192            array(
     193                'methods'             => WP_REST_Server::CREATABLE,
     194                'callback'            => function ( $request ) {
     195
     196                    // Initialize classes that will be used.
     197                    $output_restrict_content = WP_ConvertKit()->get_class( 'output_restrict_content' );
     198                    $output_restrict_content->initialize_classes();
     199
     200                    // Fetch Post ID, Resource Type and Resource ID for the view.
     201                    $post_id       = $request->get_param( 'convertkit_post_id' );
     202                    $token         = $request->get_param( 'token' );
     203                    $subscriber_code = $request->get_param( 'subscriber_code' );
     204
     205                    // Run subscriber authentication.
     206                    $result = $output_restrict_content->subscriber_authentication_verify( $post_id, $token, $subscriber_code );
     207
     208                    // If an error occured, build the code form view with the error message.
     209                    if ( is_wp_error( $result ) ) {
     210                        // Set error to display on screen.
     211                        $output_restrict_content->error = $result;
     212
     213                        // Set token and post ID for authentication code view.
     214                        $output_restrict_content->token = $token;
     215                        $output_restrict_content->post_id = $post_id;
     216
     217                        // Build code form view to return for output with error message.
     218                        ob_start();
     219                        include CONVERTKIT_PLUGIN_PATH . '/views/frontend/restrict-content/login-modal-content-code.php';
     220                        $output = trim( ob_get_clean() );
     221                        return rest_ensure_response(
     222                            array(
     223                                'success' => false,
     224                                'data'    => $output,
     225                            )
     226                        );
     227                    }
     228
     229                    // Return success with the URL to the Post, including the `ck-cache-bust` parameter.
     230                    return rest_ensure_response(
     231                        array(
     232                            'success' => true,
     233                            'url'     => $output_restrict_content->get_url( $post_id, true ),
     234                        )
     235                    );
     236                },
     237                'permission_callback' => '__return_true',
     238            )
     239        );
     240    }
     241
     242    /**
     243     * Initialize classes that will be used.
     244     *
     245     * @since   3.1.0
     246     */
     247    public function initialize_classes() {
     248
     249        $this->settings                  = new ConvertKit_Settings();
     250        $this->restrict_content_settings = new ConvertKit_Settings_Restrict_Content();
     251        $this->api                       = new ConvertKit_API_V4(
    172252            CONVERTKIT_OAUTH_CLIENT_ID,
    173253            CONVERTKIT_OAUTH_CLIENT_REDIRECT_URI,
     
    178258        );
    179259
     260    }
     261
     262    /**
     263     * If the user isn't using JavaScript, or the Plugin's Disable JS is enabled, checks if the request is a Restrict Content request with an email address.
     264     * Also runs if restrict content by tag and require login is disabled, as we immediately tag and redirect if this is the case.
     265     * If so, calls the API depending on the Restrict Content resource that's required:
     266     * - tag: subscribes the email address to the tag, storing the subscriber ID in a cookie and redirecting
     267     * - product: calls the API to send the subscriber a magic link by email containing a code. See maybe_run_subscriber_verification()
     268     * for logic once they click the link in the email or enter the code on screen.
     269     *
     270     * @since   2.1.0
     271     */
     272    public function maybe_run_subscriber_authentication() {
     273
     274        // Bail if no nonce was specified via form submission.
     275        if ( ! array_key_exists( '_wpnonce', $_REQUEST ) ) {
     276            return;
     277        }
     278
     279        // Bail if the request is a form submission and the nonce failed validation.
     280        if ( ! wp_verify_nonce( sanitize_key( $_REQUEST['_wpnonce'] ), 'convertkit_restrict_content_login' ) ) {
     281            return;
     282        }
     283
     284        // Bail if the expected email, resource type, resource ID or Post ID are missing from the request.
     285        if ( ! array_key_exists( 'convertkit_email', $_REQUEST ) ) {
     286            return;
     287        }
     288        if ( ! array_key_exists( 'convertkit_resource_type', $_REQUEST ) ) {
     289            return;
     290        }
     291        if ( ! array_key_exists( 'convertkit_resource_id', $_REQUEST ) ) {
     292            return;
     293        }
     294        if ( ! array_key_exists( 'convertkit_post_id', $_REQUEST ) ) {
     295            return;
     296        }
     297
     298        // If the Plugin Access Token has not been configured, we can't get this subscriber's ID by email.
     299        if ( ! $this->settings->has_access_and_refresh_token() ) {
     300            return;
     301        }
     302
    180303        // Sanitize inputs.
    181304        $email               = sanitize_text_field( wp_unslash( $_REQUEST['convertkit_email'] ) );
     
    184307        $this->post_id       = absint( $_REQUEST['convertkit_post_id'] );
    185308
    186         // Run subscriber authentication / subscription depending on the resource type.
    187         switch ( $this->resource_type ) {
    188             case 'product':
    189             case 'form':
    190                 // Send email to subscriber with a link to authenticate they have access to the email address submitted.
    191                 $result = $this->api->subscriber_authentication_send_code(
    192                     $email,
    193                     $this->get_url()
    194                 );
    195 
    196                 // Bail if an error occured.
    197                 if ( is_wp_error( $result ) ) {
    198                     $this->error = $result;
    199                     return;
    200                 }
    201 
     309        // If Restrict Content is by tag, tag the subscriber.
     310        if ( $this->resource_type === 'tag' ) {
     311            // Check reCAPTCHA.
     312            $recaptcha          = new ConvertKit_Recaptcha();
     313            $recaptcha_response = $recaptcha->verify_recaptcha(
     314                ( isset( $_POST['g-recaptcha-response'] ) ? sanitize_text_field( wp_unslash( $_POST['g-recaptcha-response'] ) ) : '' ),
     315                'convertkit_restrict_content_tag'
     316            );
     317
     318            // Bail if reCAPTCHA failed.
     319            if ( is_wp_error( $recaptcha_response ) ) {
     320                $this->error = $recaptcha_response;
     321                return;
     322            }
     323
     324            // Tag subscriber.
     325            $result = $this->api->tag_subscribe( $this->resource_id, $email );
     326
     327            // Bail if an error occured.
     328            if ( is_wp_error( $result ) ) {
     329                $this->error = $result;
     330                return;
     331            }
     332
     333            // If require login is disabled, return now.
     334            if ( ! $this->restrict_content_settings->require_tag_login() ) {
    202335                // Clear any existing subscriber ID cookie, as the authentication flow has started by sending the email.
    203336                $subscriber = new ConvertKit_Subscriber();
    204337                $subscriber->forget();
    205338
    206                 // Store the token so it's included in the subscriber code form.
    207                 $this->token = $result;
    208                 break;
    209 
    210             case 'tag':
    211                 // If require login is enabled, show the login screen.
    212                 if ( $this->restrict_content_settings->require_tag_login() ) {
    213                     // Tag the subscriber, unless this is an AJAX request.
    214                     if ( ! wp_doing_ajax() ) {
    215                         $result = $this->api->tag_subscribe( $this->resource_id, $email );
    216 
    217                         // Bail if an error occured.
    218                         if ( is_wp_error( $result ) ) {
    219                             $this->error = $result;
    220                             return;
    221                         }
    222                     }
    223 
    224                     // Send email to subscriber with a link to authenticate they have access to the email address submitted.
    225                     $result = $this->api->subscriber_authentication_send_code(
    226                         $email,
    227                         $this->get_url()
    228                     );
    229 
    230                     // Bail if an error occured.
    231                     if ( is_wp_error( $result ) ) {
    232                         $this->error = $result;
    233                         return;
    234                     }
    235 
    236                     // Clear any existing subscriber ID cookie, as the authentication flow has started by sending the email.
    237                     $subscriber = new ConvertKit_Subscriber();
    238                     $subscriber->forget();
    239 
    240                     // Store the token so it's included in the subscriber code form.
    241                     $this->token = $result;
    242                     break;
    243                 }
    244 
    245                 // If here, require login is disabled.
    246                 // Check reCAPTCHA, tag subscriber and assign subscriber ID integer to cookie
    247                 // without email link.
    248                 $recaptcha          = new ConvertKit_Recaptcha();
    249                 $recaptcha_response = $recaptcha->verify_recaptcha(
    250                     ( isset( $_POST['g-recaptcha-response'] ) ? sanitize_text_field( wp_unslash( $_POST['g-recaptcha-response'] ) ) : '' ),
    251                     'convertkit_restrict_content_tag'
    252                 );
    253 
    254                 // Bail if reCAPTCHA failed.
    255                 if ( is_wp_error( $recaptcha_response ) ) {
    256                     $this->error = $recaptcha_response;
    257                     return;
    258                 }
    259 
    260                 // Tag the subscriber.
    261                 $result = $this->api->tag_subscribe( $this->resource_id, $email );
    262 
    263                 // Bail if an error occured.
    264                 if ( is_wp_error( $result ) ) {
    265                     $this->error = $result;
    266                     return;
    267                 }
    268 
    269                 // Clear any existing subscriber ID cookie, as the authentication flow has started by sending the email.
    270                 $subscriber = new ConvertKit_Subscriber();
    271                 $subscriber->forget();
    272 
    273339                // Fetch the subscriber ID from the result.
    274340                $subscriber_id = $result['subscriber']['id'];
     
    277343                $this->store_subscriber_id_in_cookie( $subscriber_id );
    278344
    279                 // If this isn't an AJAX request, redirect now to reload the Post.
    280                 if ( ! wp_doing_ajax() ) {
    281                     $this->redirect();
    282                 }
    283                 break;
    284 
    285         }
    286 
    287     }
    288 
    289     /**
    290      * Checks if the request contains a token and subscriber_code i.e. the subscriber clicked
    291      * the link in the email sent by the maybe_run_subscriber_authentication() function above.
     345                // Redirect.
     346                $this->redirect( $this->post_id );
     347                return;
     348            }
     349        }
     350
     351        // If here, require login is enabled for tags or this is a product/form.
     352        // Run subscriber authentication.
     353        $result = $this->subscriber_authentication_send_code( $email, $this->post_id );
     354
     355        // Bail if an error occured.
     356        if ( is_wp_error( $result ) ) {
     357            $this->error = $result;
     358            return;
     359        }
     360
     361        // Store the token so it's included in the subscriber code form.
     362        $this->token = $result;
     363
     364    }
     365
     366    /**
     367     * If the user isn't using JavaScript, or the Plugin's Disable JS is enabled, checks if the request contains a token and subscriber_code,
     368     * which happens when the subscriber either:
     369     * - clicked the link in the email sent by run_subscriber_authentication(), or
     370     * - entered the code from the email on the screen
    292371     *
    293372     * This calls the API to verify the token and subscriber code, which tells us that the email
     
    309388        // If a nonce was specified, validate it now.
    310389        // It won't be provided if clicking the link in the magic link email.
    311         if ( array_key_exists( '_wpnonce', $_REQUEST ) ) {
     390        if ( array_key_exists( '_wpnonce', $_REQUEST ) && ! is_null( $_REQUEST['_wpnonce'] ) ) {
    312391            if ( ! wp_verify_nonce( sanitize_key( $_REQUEST['_wpnonce'] ), 'convertkit_restrict_content_subscriber_code' ) ) {
    313392                return;
     
    332411        }
    333412
    334         // Initialize the API.
    335         $this->api = new ConvertKit_API_V4(
    336             CONVERTKIT_OAUTH_CLIENT_ID,
    337             CONVERTKIT_OAUTH_CLIENT_REDIRECT_URI,
    338             $this->settings->get_access_token(),
    339             $this->settings->get_refresh_token(),
    340             $this->settings->debug_enabled(),
    341             'restrict_content'
    342         );
    343 
    344         // Verify the token and subscriber code.
    345         $subscriber_id = $this->api->subscriber_authentication_verify(
    346             sanitize_text_field( wp_unslash( $_REQUEST['token'] ) ),
    347             sanitize_text_field( wp_unslash( $_REQUEST['subscriber_code'] ) )
    348         );
     413        // Run subscriber verification.
     414        $subscriber_id = $this->subscriber_authentication_verify( $this->post_id, sanitize_text_field( wp_unslash( $_REQUEST['token'] ) ), sanitize_text_field( wp_unslash( $_REQUEST['subscriber_code'] ) ) );
    349415
    350416        // Bail if an error occured.
     
    354420        }
    355421
     422        // Redirect now to reload the Post.
     423        $this->redirect( $this->post_id );
     424
     425    }
     426
     427    /**
     428     * Sends an email to the subscriber with a code and link to authenticate they have access to the email address submitted.
     429     *
     430     * @since   3.1.0
     431     *
     432     * @param   string $email         Email address.
     433     * @param   int    $post_id       Post ID.
     434     *
     435     * @return WP_Error|string        Error or Token.
     436     */
     437    public function subscriber_authentication_send_code( $email, $post_id ) {
     438
     439        // Send email to subscriber with a link to authenticate they have access to the email address submitted.
     440        $token = $this->api->subscriber_authentication_send_code(
     441            $email,
     442            $this->get_url( $post_id )
     443        );
     444
     445        // Bail if an error occured.
     446        if ( is_wp_error( $token ) ) {
     447            return $token;
     448        }
     449
     450        // Clear any existing subscriber ID cookie, as the authentication flow has started by sending the email.
     451        $subscriber = new ConvertKit_Subscriber();
     452        $subscriber->forget();
     453
     454        // Return the token.
     455        return $token;
     456
     457    }
     458
     459    /**
     460     * Verifies the token and subscriber code, which tells us that the email
     461     * address supplied truly belongs to the user, and that we can safely
     462     * trust their subscriber ID to be valid.
     463     *
     464     * @since   3.1.0
     465     *
     466     * @param   int    $post_id         Post ID.
     467     * @param   string $token           Token.
     468     * @param   string $subscriber_code Subscriber code.
     469     *
     470     * @return WP_Error|string          Error or Signed Subscriber ID.
     471     */
     472    public function subscriber_authentication_verify( $post_id, $token, $subscriber_code ) {
     473
     474        // Verify the token and subscriber code.
     475        $subscriber_id = $this->api->subscriber_authentication_verify( $token, $subscriber_code );
     476
     477        // Bail if an error occured.
     478        if ( is_wp_error( $subscriber_id ) ) {
     479            return $subscriber_id;
     480        }
     481
    356482        // Store subscriber ID in cookie.
    357483        $this->store_subscriber_id_in_cookie( $subscriber_id );
    358484
    359         // If this isn't an AJAX request, redirect now to reload the Post.
    360         if ( ! wp_doing_ajax() ) {
    361             $this->redirect();
    362         }
     485        // Return signed subscriber ID.
     486        return $subscriber_id;
    363487
    364488    }
     
    513637
    514638        // Replace existing where statement with new statement.
    515         $where = 'WHERE ' . $new_where . ' ' . substr( $where, strpos( $where, 'AND' ) );
     639        $where = 'WHERE ' . $new_where . ' ' . substr( $where, strpos( $where, 'AND p.post_type = \'' . $post->post_type . '\' ' ) );
    516640
    517641        // Return.
     
    552676
    553677        // Replace existing where statement with new statement.
    554         $where = 'WHERE ' . $new_where . ' ' . substr( $where, strpos( $where, 'AND' ) );
     678        $where = 'WHERE ' . $new_where . ' ' . substr( $where, strpos( $where, 'AND p.post_type = \'' . $post->post_type . '\' ' ) );
    555679
    556680        // Return.
     
    612736     *
    613737     * @since   2.3.7
    614      */
    615     private function redirect() {
     738     *
     739     * @param   int $post_id      Post ID.
     740     */
     741    private function redirect( $post_id ) {
    616742
    617743        // Redirect to the Post, appending a query parameter to the URL to prevent caching plugins and
     
    619745        // result in maybe_restrict_content() not showing an error message or permitting
    620746        // access to the content.
    621         wp_safe_redirect( $this->get_url( true ) );
     747        wp_safe_redirect( $this->get_url( $post_id, true ) );
    622748        exit;
    623749
     
    629755     * @since   2.1.0
    630756     *
     757     * @param   int  $post_id        Post ID.
    631758     * @param   bool $cache_bust     Include `ck-cache-bust` parameter in URL.
    632      * @return  string  URL.
    633      */
    634     public function get_url( $cache_bust = false ) {
     759     * @return  string                 URL.
     760     */
     761    public function get_url( $post_id, $cache_bust = false ) {
    635762
    636763        // Get URL of Post.
    637         $url = get_permalink( $this->post_id );
     764        $url = get_permalink( $post_id );
    638765
    639766        // If no cache busting required, return the URL now.
     
    867994    private function subscriber_has_access( $subscriber_id ) { // phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter
    868995
    869         // Initialize the API.
    870         $this->api = new ConvertKit_API_V4(
    871             CONVERTKIT_OAUTH_CLIENT_ID,
    872             CONVERTKIT_OAUTH_CLIENT_REDIRECT_URI,
    873             $this->settings->get_access_token(),
    874             $this->settings->get_refresh_token(),
    875             $this->settings->debug_enabled(),
    876             'restrict_content'
    877         );
    878 
    879996        // Depending on the resource type, determine if the subscriber has access to it.
    880997        // This is deliberately a switch statement, because we will likely add in support
     
    12151332                'convertkit_restrict_content',
    12161333                array(
    1217                     'ajaxurl' => admin_url( 'admin-ajax.php' ),
    1218                     'debug'   => $this->settings->debug_enabled(),
     1334                    'nonce'                         => wp_create_nonce( 'wp_rest' ),
     1335                    'subscriber_authentication_url' => rest_url( 'kit/v1/restrict-content/subscriber-authentication' ),
     1336                    'subscriber_verification_url'   => rest_url( 'kit/v1/restrict-content/subscriber-verification' ),
     1337                    'debug'                         => $this->settings->debug_enabled(),
    12191338                )
    12201339            );
  • convertkit/tags/3.1.0/includes/class-convertkit-output.php

    r3357832 r3399438  
    813813        }
    814814
     815        // Determine if the Non-inline Form Limit per Session setting is enabled.
     816        $limit_per_session = $this->settings->non_inline_form_limit_per_session();
     817
    815818        // Get form.
    816819        $convertkit_forms = new ConvertKit_Resource_Forms();
     
    829832            add_filter(
    830833                'convertkit_output_scripts_footer',
    831                 function ( $scripts ) use ( $form ) {
     834                function ( $scripts ) use ( $form, $limit_per_session ) {
    832835
    833836                    $scripts[] = array(
     
    835838                        'data-uid'                   => $form['uid'],
    836839                        'src'                        => $form['embed_js'],
    837                         'data-kit-limit-per-session' => true,
     840                        'data-kit-limit-per-session' => $limit_per_session ? '1' : '0',
    838841                    );
    839842
  • convertkit/tags/3.1.0/includes/class-convertkit-resource-forms.php

    r3357832 r3399438  
    545545                        'data-uid'                   => $this->resources[ $id ]['uid'],
    546546                        'src'                        => $this->resources[ $id ]['embed_js'],
    547                         'data-kit-limit-per-session' => $settings->non_inline_form_limit_per_session(),
     547                        'data-kit-limit-per-session' => $settings->non_inline_form_limit_per_session() ? '1' : '0',
    548548                    );
    549549
  • convertkit/tags/3.1.0/includes/class-convertkit-settings.php

    r3376520 r3399438  
    4949        add_action( 'convertkit_api_get_access_token', array( $this, 'update_credentials' ), 10, 2 );
    5050        add_action( 'convertkit_api_refresh_token', array( $this, 'update_credentials' ), 10, 2 );
     51
     52        // Delete credentials if the API class uses a invalid access token.
     53        // This prevents the Plugin making repetitive API requests that will 401.
     54        add_action( 'convertkit_api_access_token_invalid', array( $this, 'maybe_delete_credentials' ), 10, 2 );
    5155
    5256    }
     
    630634        }
    631635
     636        // Remove any existing persistent notice.
     637        WP_ConvertKit()->get_class( 'admin_notices' )->delete( 'authorization_failed' );
     638
    632639        $this->save(
    633640            array(
     
    647654
    648655    /**
    649      * Deletes any existing access token, refresh token and its expiry from the Plugin settings.
     656     * Deletes the stored access token, refresh token and its expiry from the Plugin settings,
     657     * and clears any existing scheduled WordPress Cron event to refresh the token on expiry,
     658     * when either:
     659     * - The access token is invalid
     660     * - The access token expired, and refreshing failed
     661     *
     662     * @since   3.1.0
     663     *
     664     * @param   WP_Error $result      Error result.
     665     * @param   string   $client_id   OAuth Client ID used for the Access and Refresh Tokens.
     666     */
     667    public function maybe_delete_credentials( $result, $client_id ) {
     668
     669        // Don't delete these credentials if they're not for this Client ID.
     670        // They're for another Kit Plugin that uses OAuth.
     671        if ( $client_id !== CONVERTKIT_OAUTH_CLIENT_ID ) {
     672            return;
     673        }
     674
     675        // Persist an error notice in the WordPress Administration until the user fixes the problem.
     676        WP_ConvertKit()->get_class( 'admin_notices' )->add( 'authorization_failed' );
     677
     678        // Delete the credentials from the Plugin settings.
     679        $this->delete_credentials();
     680
     681    }
     682
     683    /**
     684     * Deletes any existing access token, refresh token and its expiry from the Plugin settings,
     685     * and clears any existing scheduled WordPress Cron event to refresh the token on expiry.
    650686     *
    651687     * @since   2.5.0
     
    661697        );
    662698
     699        // Clear any existing scheduled WordPress Cron event.
     700        wp_clear_scheduled_hook( 'convertkit_refresh_token' );
     701
    663702    }
    664703
  • convertkit/tags/3.1.0/includes/class-wp-convertkit.php

    r3357832 r3399438  
    8484        $this->classes['admin_category']                      = new ConvertKit_Admin_Category();
    8585        $this->classes['admin_landing_page']                  = new ConvertKit_Admin_Landing_Page();
    86         $this->classes['admin_notices']                       = new ConvertKit_Admin_Notices();
    8786        $this->classes['admin_post']                          = new ConvertKit_Admin_Post();
    8887        $this->classes['admin_quick_edit']                    = new ConvertKit_Admin_Quick_Edit();
    89         $this->classes['admin_refresh_resources']             = new ConvertKit_Admin_Refresh_Resources();
    9088        $this->classes['admin_restrict_content']              = new ConvertKit_Admin_Restrict_Content();
    9189        $this->classes['admin_settings']                      = new ConvertKit_Admin_Settings();
     
    179177    private function initialize_global() {
    180178
     179        $this->classes['admin_notices']                               = new ConvertKit_Admin_Notices();
     180        $this->classes['admin_refresh_resources']                     = new ConvertKit_Admin_Refresh_Resources();
    181181        $this->classes['ajax']                                        = new ConvertKit_AJAX();
    182182        $this->classes['blocks_convertkit_broadcasts']                = new ConvertKit_Block_Broadcasts();
  • convertkit/tags/3.1.0/languages/convertkit.pot

    r3390429 r3399438  
    33msgid ""
    44msgstr ""
    5 "Project-Id-Version: Kit (formerly ConvertKit) 3.0.8\n"
     5"Project-Id-Version: Kit (formerly ConvertKit) 3.1.0\n"
    66"Report-Msgid-Bugs-To: https://wordpress.org/support/plugin/convertkit\n"
    77"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
     
    1010"Content-Type: text/plain; charset=UTF-8\n"
    1111"Content-Transfer-Encoding: 8bit\n"
    12 "POT-Creation-Date: 2025-11-05T03:11:47+00:00\n"
     12"POT-Creation-Date: 2025-11-20T04:42:45+00:00\n"
    1313"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
    1414"X-Generator: WP-CLI 2.12.0\n"
     
    8888msgstr ""
    8989
    90 #: admin/class-convertkit-admin-notices.php:69
    91 msgid "Kit: Authorization failed. Please"
    92 msgstr ""
    93 
    94 #: admin/class-convertkit-admin-notices.php:73
    95 #: views/backend/post/no-api-key.php:19
    96 msgid "connect your Kit account."
    97 msgstr ""
    98 
    9990#: admin/class-convertkit-admin-post.php:93
    10091msgid "Add New"
     
    276267
    277268#: admin/section/class-convertkit-admin-section-broadcasts.php:501
    278 #: admin/section/class-convertkit-admin-section-general.php:689
     269#: admin/section/class-convertkit-admin-section-general.php:673
    279270#: views/backend/post/bulk-edit.php:30
    280271#: views/backend/post/bulk-edit.php:53
     
    396387msgstr ""
    397388
    398 #: admin/section/class-convertkit-admin-section-general.php:295
     389#: admin/section/class-convertkit-admin-section-general.php:279
    399390msgid "Account Name"
    400391msgstr ""
    401392
    402393#. translators: Post Type Name, plural
    403 #: admin/section/class-convertkit-admin-section-general.php:316
     394#: admin/section/class-convertkit-admin-section-general.php:300
    404395#, php-format
    405396msgid "Default Form (%s)"
     
    407398
    408399#. translators: Post Type Name, plural
    409 #: admin/section/class-convertkit-admin-section-general.php:334
     400#: admin/section/class-convertkit-admin-section-general.php:318
    410401#, php-format
    411402msgid "Form Position (%s)"
    412403msgstr ""
    413404
    414 #: admin/section/class-convertkit-admin-section-general.php:365
     405#: admin/section/class-convertkit-admin-section-general.php:349
    415406msgid "Default Forms (Site Wide)"
    416407msgstr ""
    417408
    418 #: admin/section/class-convertkit-admin-section-general.php:376
     409#: admin/section/class-convertkit-admin-section-general.php:360
    419410msgid "Behavior"
    420411msgstr ""
    421412
    422 #: admin/section/class-convertkit-admin-section-general.php:388
     413#: admin/section/class-convertkit-admin-section-general.php:372
    423414msgid "Limit Display"
    424415msgstr ""
    425416
    426 #: admin/section/class-convertkit-admin-section-general.php:400
     417#: admin/section/class-convertkit-admin-section-general.php:384
    427418msgid "reCAPTCHA: Site Key"
    428419msgstr ""
    429420
    430 #: admin/section/class-convertkit-admin-section-general.php:407
     421#: admin/section/class-convertkit-admin-section-general.php:391
    431422msgid "Enter your Google reCAPTCHA v3 Site Key. When specified, this will be used to reduce spam signups."
    432423msgstr ""
    433424
    434 #: admin/section/class-convertkit-admin-section-general.php:413
     425#: admin/section/class-convertkit-admin-section-general.php:397
    435426msgid "reCAPTCHA: Secret Key"
    436427msgstr ""
    437428
     429#: admin/section/class-convertkit-admin-section-general.php:404
     430msgid "Enter your Google reCAPTCHA v3 Secret Key. When specified, this will be used to reduce spam signups."
     431msgstr ""
     432
     433#: admin/section/class-convertkit-admin-section-general.php:410
     434msgid "reCAPTCHA: Minimum Score"
     435msgstr ""
     436
    438437#: admin/section/class-convertkit-admin-section-general.php:420
    439 msgid "Enter your Google reCAPTCHA v3 Secret Key. When specified, this will be used to reduce spam signups."
    440 msgstr ""
    441 
    442 #: admin/section/class-convertkit-admin-section-general.php:426
    443 msgid "reCAPTCHA: Minimum Score"
    444 msgstr ""
    445 
    446 #: admin/section/class-convertkit-admin-section-general.php:436
    447438msgid "Enter the minimum threshold for a subscriber to pass Google reCAPTCHA. A higher number will reduce spam signups (1.0 is very likely a good interaction, 0.0 is very likely a bot)."
    448439msgstr ""
    449440
    450 #: admin/section/class-convertkit-admin-section-general.php:444
     441#: admin/section/class-convertkit-admin-section-general.php:428
    451442msgid "Debug"
    452443msgstr ""
    453444
    454 #: admin/section/class-convertkit-admin-section-general.php:455
     445#: admin/section/class-convertkit-admin-section-general.php:439
    455446msgid "Disable JavaScript"
    456447msgstr ""
    457448
    458 #: admin/section/class-convertkit-admin-section-general.php:466
     449#: admin/section/class-convertkit-admin-section-general.php:450
    459450msgid "Disable CSS"
    460451msgstr ""
    461452
    462 #: admin/section/class-convertkit-admin-section-general.php:477
     453#: admin/section/class-convertkit-admin-section-general.php:461
    463454msgid "Usage Tracking"
    464455msgstr ""
    465456
    466 #: admin/section/class-convertkit-admin-section-general.php:494
     457#: admin/section/class-convertkit-admin-section-general.php:478
    467458msgid "Forms can be embedded before and/or after (or following number of elements) on every post or page (in single view only) across your site by using the settings below."
    468459msgstr ""
    469460
    470 #: admin/section/class-convertkit-admin-section-general.php:495
     461#: admin/section/class-convertkit-admin-section-general.php:479
    471462msgid "You can also configure non-inline forms to display site wide under the \"Non-inline Forms\" section below."
    472463msgstr ""
    473464
    474 #: admin/section/class-convertkit-admin-section-general.php:496
     465#: admin/section/class-convertkit-admin-section-general.php:480
    475466msgid "These default form settings can be overridden using the Kit meta box on a page or post's edit screen. For site wide non-inline forms to respect when a post/page has forms disabled, enable the \"Behavior\" option in the \"Non-inline Forms\" section below."
    476467msgstr ""
    477468
    478469#. translators: [convertkit] shortcode, wrapped in <code> tags
    479 #: admin/section/class-convertkit-admin-section-general.php:501
     470#: admin/section/class-convertkit-admin-section-general.php:485
    480471#, php-format
    481472msgid "The default form can be inserted into the middle of post or page content by using either the %s shortcode or block."
    482473msgstr ""
    483474
    484 #: admin/section/class-convertkit-admin-section-general.php:518
     475#: admin/section/class-convertkit-admin-section-general.php:502
    485476msgid "Defines non-inline forms to display site wide, and if these forms should display on Pages / Posts that have the Kit Form setting = None."
    486477msgstr ""
    487478
    488 #: admin/section/class-convertkit-admin-section-general.php:530
     479#: admin/section/class-convertkit-admin-section-general.php:514
    489480msgid "Configure reCAPTCHA to protect the Member Content signup form and Form Builder block from spam and abuse."
    490481msgstr ""
    491482
    492 #: admin/section/class-convertkit-admin-section-general.php:542
     483#: admin/section/class-convertkit-admin-section-general.php:526
    493484msgid "Defines advanced configuration settings, usually when working with support or needing to disable JS or CSS."
    494485msgstr ""
    495486
    496 #: admin/section/class-convertkit-admin-section-general.php:571
     487#: admin/section/class-convertkit-admin-section-general.php:555
    497488msgid "(Not specified)"
    498489msgstr ""
    499490
    500 #: admin/section/class-convertkit-admin-section-general.php:586
     491#: admin/section/class-convertkit-admin-section-general.php:570
    501492msgid "Disconnect"
    502493msgstr ""
    503494
    504 #: admin/section/class-convertkit-admin-section-general.php:649
     495#: admin/section/class-convertkit-admin-section-general.php:633
    505496msgid "No Forms exist in Kit."
    506497msgstr ""
    507498
     499#: admin/section/class-convertkit-admin-section-general.php:634
     500msgid "Click here to create your first form"
     501msgstr ""
     502
     503#. translators: Post Type name, plural
     504#: admin/section/class-convertkit-admin-section-general.php:647
     505#: admin/section/class-convertkit-admin-section-general.php:657
     506#, php-format
     507msgid "Select a form above to automatically output below all %s."
     508msgstr ""
     509
    508510#: admin/section/class-convertkit-admin-section-general.php:650
    509 msgid "Click here to create your first form"
    510 msgstr ""
    511 
    512 #. translators: Post Type name, plural
    513 #: admin/section/class-convertkit-admin-section-general.php:663
    514 #: admin/section/class-convertkit-admin-section-general.php:673
    515 #, php-format
    516 msgid "Select a form above to automatically output below all %s."
    517 msgstr ""
    518 
    519 #: admin/section/class-convertkit-admin-section-general.php:666
    520 #: admin/section/class-convertkit-admin-section-general.php:803
     511#: admin/section/class-convertkit-admin-section-general.php:787
    521512#: includes/class-convertkit-broadcasts-exporter.php:150
    522513#: views/backend/setup-wizard/convertkit-setup/content-2.php:79
     
    525516msgstr ""
    526517
    527 #: admin/section/class-convertkit-admin-section-general.php:667
    528 #: admin/section/class-convertkit-admin-section-general.php:804
     518#: admin/section/class-convertkit-admin-section-general.php:651
     519#: admin/section/class-convertkit-admin-section-general.php:788
    529520msgid "to preview how this will display."
    530521msgstr ""
    531522
    532523#. translators: Post type singular name
    533 #: admin/section/class-convertkit-admin-section-general.php:716
     524#: admin/section/class-convertkit-admin-section-general.php:700
    534525#, php-format
    535526msgid "Before %s content"
     
    537528
    538529#. translators: Post type singular name
    539 #: admin/section/class-convertkit-admin-section-general.php:721
     530#: admin/section/class-convertkit-admin-section-general.php:705
    540531#, php-format
    541532msgid "After %s content"
     
    543534
    544535#. translators: Post type singular name
    545 #: admin/section/class-convertkit-admin-section-general.php:726
     536#: admin/section/class-convertkit-admin-section-general.php:710
    546537#, php-format
    547538msgid "Before and after %s content"
    548539msgstr ""
    549540
    550 #: admin/section/class-convertkit-admin-section-general.php:729
     541#: admin/section/class-convertkit-admin-section-general.php:713
    551542msgid "After element"
    552543msgstr ""
    553544
    554545#. translators: Post Type name, plural
    555 #: admin/section/class-convertkit-admin-section-general.php:733
     546#: admin/section/class-convertkit-admin-section-general.php:717
    556547#, php-format
    557548msgid "Where forms should display relative to the %s content"
    558549msgstr ""
    559550
    560 #: admin/section/class-convertkit-admin-section-general.php:768
     551#: admin/section/class-convertkit-admin-section-general.php:752
    561552msgid "Paragraphs"
    562553msgstr ""
    563554
    564 #: admin/section/class-convertkit-admin-section-general.php:769
     555#: admin/section/class-convertkit-admin-section-general.php:753
    565556msgid "Headings <h2>"
    566557msgstr ""
    567558
    568 #: admin/section/class-convertkit-admin-section-general.php:770
     559#: admin/section/class-convertkit-admin-section-general.php:754
    569560msgid "Headings <h3>"
    570561msgstr ""
    571562
    572 #: admin/section/class-convertkit-admin-section-general.php:771
     563#: admin/section/class-convertkit-admin-section-general.php:755
    573564msgid "Headings <h4>"
    574565msgstr ""
    575566
    576 #: admin/section/class-convertkit-admin-section-general.php:772
     567#: admin/section/class-convertkit-admin-section-general.php:756
    577568msgid "Headings <h5>"
    578569msgstr ""
    579570
    580 #: admin/section/class-convertkit-admin-section-general.php:773
     571#: admin/section/class-convertkit-admin-section-general.php:757
    581572msgid "Headings <h6>"
    582573msgstr ""
    583574
    584 #: admin/section/class-convertkit-admin-section-general.php:774
     575#: admin/section/class-convertkit-admin-section-general.php:758
    585576msgid "Images"
    586577msgstr ""
    587578
    588 #: admin/section/class-convertkit-admin-section-general.php:776
     579#: admin/section/class-convertkit-admin-section-general.php:760
    589580msgid "The number of elements before outputting the form."
    590581msgstr ""
    591582
    592 #: admin/section/class-convertkit-admin-section-general.php:793
     583#: admin/section/class-convertkit-admin-section-general.php:777
    593584msgid "No non-inline Forms exist in Kit."
    594585msgstr ""
    595586
    596 #: admin/section/class-convertkit-admin-section-general.php:794
     587#: admin/section/class-convertkit-admin-section-general.php:778
    597588msgid "Click here to create your first modal, slide in or sticky bar form"
    598589msgstr ""
    599590
    600 #: admin/section/class-convertkit-admin-section-general.php:802
     591#: admin/section/class-convertkit-admin-section-general.php:786
    601592msgid "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."
    602593msgstr ""
    603594
    604 #: admin/section/class-convertkit-admin-section-general.php:840
     595#: admin/section/class-convertkit-admin-section-general.php:824
    605596msgid "If checked, do not display the site wide form(s) above on Pages / Posts that have their Kit Form setting = None."
    606597msgstr ""
    607598
    608 #: admin/section/class-convertkit-admin-section-general.php:860
     599#: admin/section/class-convertkit-admin-section-general.php:844
    609600msgid "If checked, one non-inline form will be displayed per session. This applies to all non-inline forms defined on this screen, Page / Post / Category settings, and any Form blocks or shortcodes specifying a non-inline form."
    610601msgstr ""
    611602
    612 #: admin/section/class-convertkit-admin-section-general.php:943
     603#: admin/section/class-convertkit-admin-section-general.php:927
    613604msgid "Log requests to file and output browser console messages."
    614605msgstr ""
    615606
    616 #: admin/section/class-convertkit-admin-section-general.php:944
     607#: admin/section/class-convertkit-admin-section-general.php:928
    617608msgid "You can ignore this unless you're working with our support team to resolve an issue. Decheck this option to improve performance."
    618609msgstr ""
    619610
    620 #: admin/section/class-convertkit-admin-section-general.php:961
     611#: admin/section/class-convertkit-admin-section-general.php:945
    621612msgid "Prevent plugin JavaScript files loading on the frontend site. This will disable the custom content and tagging features of the plugin. Does not apply to embedding forms or landing pages. Use with caution!"
    622613msgstr ""
    623614
    624 #: admin/section/class-convertkit-admin-section-general.php:978
     615#: admin/section/class-convertkit-admin-section-general.php:962
    625616msgid "Prevents loading plugin CSS files. This will disable styling on broadcasts, form trigger buttons, product buttons and member's content. Use with caution!"
    626617msgstr ""
    627618
    628 #: admin/section/class-convertkit-admin-section-general.php:982
     619#: admin/section/class-convertkit-admin-section-general.php:966
    629620msgid "To customize forms and their styling, use the"
    630621msgstr ""
    631622
    632 #: admin/section/class-convertkit-admin-section-general.php:984
     623#: admin/section/class-convertkit-admin-section-general.php:968
    633624msgid "Kit form editor"
    634625msgstr ""
    635626
    636 #: admin/section/class-convertkit-admin-section-general.php:986
     627#: admin/section/class-convertkit-admin-section-general.php:970
    637628msgid "For creators who require form designs to follow their WordPress theme, use the Kit Form Builder block in the block editor."
    638629msgstr ""
    639630
    640 #: admin/section/class-convertkit-admin-section-general.php:989
     631#: admin/section/class-convertkit-admin-section-general.php:973
    641632msgid "For developers who require custom form designs through use of CSS, consider using the"
    642633msgstr ""
    643634
    644 #: admin/section/class-convertkit-admin-section-general.php:990
     635#: admin/section/class-convertkit-admin-section-general.php:974
    645636msgid "or"
    646637msgstr ""
    647638
    648 #: admin/section/class-convertkit-admin-section-general.php:991
     639#: admin/section/class-convertkit-admin-section-general.php:975
    649640msgid "integrations."
    650641msgstr ""
    651642
    652 #: admin/section/class-convertkit-admin-section-general.php:1010
     643#: admin/section/class-convertkit-admin-section-general.php:994
    653644msgid "By allowing us to collect usage data, we can better understand which WordPress configurations, themes and plugins we should test."
    654645msgstr ""
    655646
    656 #: admin/section/class-convertkit-admin-section-general.php:1014
     647#: admin/section/class-convertkit-admin-section-general.php:998
    657648msgid "Complete documentation on usage tracking can be found"
    658649msgstr ""
    659650
    660 #: admin/section/class-convertkit-admin-section-general.php:1016
     651#: admin/section/class-convertkit-admin-section-general.php:1000
    661652#: views/backend/setup-wizard/convertkit-setup/content-2.php:150
    662653msgid "here"
     
    844835msgstr ""
    845836
    846 #: admin/section/class-convertkit-admin-section-tools.php:353
     837#: admin/section/class-convertkit-admin-section-tools.php:60
     838msgid "MC4WP forms migrated successfully."
     839msgstr ""
     840
     841#: admin/section/class-convertkit-admin-section-tools.php:396
    847842msgid "Tools to help you manage Kit on your site."
    848843msgstr ""
    849844
    850 #: admin/section/class-convertkit-admin-section-tools.php:381
     845#: admin/section/class-convertkit-admin-section-tools.php:424
    851846msgid "WordPress 5.2 or higher is required for system information report."
    852847msgstr ""
     
    14181413
    14191414#: includes/blocks/class-convertkit-block-form.php:106
     1415#: views/backend/settings/tools.php:119
    14201416#: views/backend/term/fields-add.php:11
    14211417#: views/backend/term/fields-edit.php:12
     
    15131509msgstr ""
    15141510
    1515 #: includes/class-convertkit-ajax.php:84
    1516 msgid "Kit: Required parameter `subscriber_id` not included in AJAX request."
    1517 msgstr ""
    1518 
    1519 #: includes/class-convertkit-ajax.php:90
    1520 msgid "Kit: Required parameter `subscriber_id` empty in AJAX request."
    1521 msgstr ""
    1522 
    1523 #: includes/class-convertkit-ajax.php:126
     1511#: includes/class-convertkit-admin-notices.php:69
     1512msgid "Kit: Authorization failed. Please"
     1513msgstr ""
     1514
     1515#: includes/class-convertkit-admin-notices.php:73
     1516#: views/backend/post/no-api-key.php:19
     1517msgid "connect your Kit account."
     1518msgstr ""
     1519
     1520#: includes/class-convertkit-ajax.php:43
    15241521msgid "Kit: Required parameter `email` not included in AJAX request."
    15251522msgstr ""
    15261523
    1527 #: includes/class-convertkit-ajax.php:132
     1524#: includes/class-convertkit-ajax.php:49
    15281525msgid "Kit: Required parameter `email` is empty."
    15291526msgstr ""
    15301527
    1531 #: includes/class-convertkit-ajax.php:137
     1528#: includes/class-convertkit-ajax.php:54
    15321529msgid "Kit: Required parameter `email` is not an email address."
    15331530msgstr ""
     
    21082105#: views/backend/settings/tools.php:95
    21092106msgid "Import"
     2107msgstr ""
     2108
     2109#: views/backend/settings/tools.php:109
     2110msgid "MC4WP: Migrate Configuration"
     2111msgstr ""
     2112
     2113#: views/backend/settings/tools.php:112
     2114msgid "Automatically replace MC4WP form shortcodes with Kit forms."
     2115msgstr ""
     2116
     2117#: views/backend/settings/tools.php:118
     2118msgid "MC4WP Form"
     2119msgstr ""
     2120
     2121#: views/backend/settings/tools.php:149
     2122msgid "Migrate"
    21102123msgstr ""
    21112124
  • convertkit/tags/3.1.0/readme.txt

    r3390429 r3399438  
    66Tested up to: 6.8
    77Requires PHP: 7.1
    8 Stable tag: 3.0.8
     8Stable tag: 3.1.0
    99License: GPLv3 or later
    1010License URI: https://www.gnu.org/licenses/gpl-3.0.html
     
    180180== Changelog ==
    181181
     182### 3.1.0 2025-11-20
     183* Added: Settings: Tools: Mailchimp for WordPress to Kit Form Importer
     184* Fix: Settings: Non-inline Forms: Correctly output `data-kit-limit-per-session`
     185* Fix: Settings: Automatically delete invalid Access Tokens
     186* Fix: Member Content: Courses: Next/Previous Links compatibility for WordPress 6.9
     187* Fix: Landing Pages: Prevent LazyLoad Plugin from lazy loading images on Landing Pages, which would result in missing images
     188* Updated: Blocks: Refresh Resources: Use REST API, replacing admin-ajax.php
     189* Updated: Refresh Resources: Use REST API, replacing admin-ajax.php
     190* Updated: Member Content: Use REST API, replacing admin-ajax.php
     191* Updated: Use WordPress Libraries 2.1.1
     192* Removed: Unused `convertkit_store_subscriber_id_in_cookie` AJAX function
     193
    182194### 3.0.8 2025-11-05
    183195* Fix: Member Content: Product: Display 'no access' notice when logged in and no access
  • convertkit/tags/3.1.0/resources/backend/js/gutenberg.js

    r3390429 r3399438  
    747747         */
    748748        const refreshBlocksDefinitions = function (props, setButtonDisabled) {
    749             // Define data for WordPress AJAX request.
    750             const data = new FormData();
    751             data.append('action', 'convertkit_get_blocks');
    752             data.append('nonce', convertkit_gutenberg.get_blocks_nonce);
    753 
    754749            // Disable the button.
    755750            if (typeof setButtonDisabled !== 'undefined') {
     
    758753
    759754            // Send AJAX request.
    760             fetch(ajaxurl, {
    761                 method: 'POST',
    762                 credentials: 'same-origin',
    763                 body: data,
     755            fetch(convertkit_gutenberg.ajaxurl, {
     756                method: 'GET',
     757                headers: {
     758                    'Content-Type': 'application/json',
     759                    'X-WP-Nonce': convertkit_gutenberg.get_blocks_nonce,
     760                },
    764761            })
    765762                .then(function (response) {
     
    768765                })
    769766                .then(function (response) {
     767                    // If the response includes a code, show an error notice.
     768                    if (typeof response.code !== 'undefined') {
     769                        // Show an error in the Gutenberg editor.
     770                        wp.data
     771                            .dispatch('core/notices')
     772                            .createErrorNotice('Kit: ' + response.message, {
     773                                id: 'convertkit-error',
     774                            });
     775
     776                        // Enable refresh button.
     777                        if (typeof setButtonDisabled !== 'undefined') {
     778                            setButtonDisabled(false);
     779                        }
     780                        return;
     781                    }
     782
    770783                    // Update global ConvertKit Blocks object, so that any updated resources
    771784                    // are reflected when adding new ConvertKit Blocks.
    772                     convertkit_blocks = response.data;
     785                    convertkit_blocks = response;
    773786
    774787                    // Update this block's properties, so that has_access_token, has_resources
     
    791804                    wp.data
    792805                        .dispatch('core/notices')
    793                         .createErrorNotice('ConvertKit: ' + error, {
     806                        .createErrorNotice('Kit: ' + error, {
    794807                            id: 'convertkit-error',
    795808                        });
    796809
    797810                    // Enable refresh button.
    798                     setButtonDisabled(false);
     811                    if (typeof setButtonDisabled !== 'undefined') {
     812                        setButtonDisabled(false);
     813                    }
    799814                });
    800815        };
  • convertkit/tags/3.1.0/resources/backend/js/refresh-resources.js

    r3390429 r3399438  
    5050
    5151    // Perform AJAX request to refresh resource.
    52     fetch(convertkit_admin_refresh_resources.ajaxurl, {
     52    fetch(convertkit_admin_refresh_resources.ajaxurl + resource, {
    5353        method: 'POST',
    5454        headers: {
    55             'Content-Type': 'application/x-www-form-urlencoded',
     55            'Content-Type': 'application/json',
     56            'X-WP-Nonce': convertkit_admin_refresh_resources.nonce,
    5657        },
    57         body: new URLSearchParams({
    58             action: 'convertkit_admin_refresh_resources',
    59             nonce: convertkit_admin_refresh_resources.nonce,
    60             resource, // e.g. forms, landing_pages, tags.
    61         }),
    6258    })
    6359        .then(function (response) {
     
    7167
    7268            // Show an error if the request wasn't successful.
    73             if (!response.success) {
     69            if (typeof response.code !== 'undefined') {
    7470                // Show error notice.
    75                 convertKitRefreshResourcesOutputErrorNotice(response.data);
     71                convertKitRefreshResourcesOutputErrorNotice(response.message);
    7672
    7773                // Enable button and remove is-refreshing class.
     
    10399                    // Populate select `optgroup`` from response data, which comprises of Tags and Products.
    104100                    // Tags.
    105                     response.data.tags.forEach(function (item) {
     101                    response.tags.forEach(function (item) {
    106102                        document
    107103                            .querySelector(
     
    119115
    120116                    // Products.
    121                     response.data.products.forEach(function (item) {
     117                    response.products.forEach(function (item) {
    122118                        document
    123119                            .querySelector(
     
    137133                default:
    138134                    // Populate select options from response data.
    139                     response.data.forEach(function (item) {
     135                    response.forEach(function (item) {
    140136                        // Define label.
    141137                        let label = '';
  • convertkit/tags/3.1.0/resources/frontend/js/convertkit.js

    r3369648 r3399438  
    213213    // Set a cookie if any scripts with data-kit-limit-per-session attribute exist.
    214214    if (
    215         document.querySelectorAll('script[data-kit-limit-per-session]').length >
    216         0
     215        document.querySelectorAll('script[data-kit-limit-per-session="1"]')
     216            .length > 0
    217217    ) {
    218218        document.cookie = 'ck_non_inline_form_displayed=1; path=/';
  • convertkit/tags/3.1.0/resources/frontend/js/restrict-content.js

    r3369648 r3399438  
    7878        // Code submission.
    7979        convertKitRestrictContentSubscriberVerification(
    80             e.target.querySelector('input[name="_wpnonce"]').value,
     80            convertkit_restrict_content.nonce,
    8181            e.target.querySelector('input[name="subscriber_code"]').value,
    8282            e.target.querySelector('input[name="token"]').value,
     
    8989    // Email submission.
    9090    convertKitRestrictContentSubscriberAuthenticationSendCode(
    91         e.target.querySelector('input[name="_wpnonce"]').value,
     91        convertkit_restrict_content.nonce,
    9292        e.target.querySelector('input[name="convertkit_email"]').value,
    9393        e.target.querySelector('input[name="convertkit_resource_type"]').value,
     
    128128
    129129/**
    130  * Submits the given email address to maybe_run_subscriber_authentication(), which
    131  * will return either:
     130 * Submits the given email address to the WP REST API kit/v1/restrict-content/subscriber-authentication
     131 * endpoint, which will return either:
    132132 * - the email form view, with an error message e.g. invalid email,
    133133 * - the code form view, where the user can enter the OTP.
     
    136136 *
    137137 * @param {string} nonce         WordPress nonce.
    138  * @param {string} email         Email address.  resource_type   Resource Type (tag|product).
    139  * @param {string} resource_type Resource Type (tag|product).
    140  * @param {string} resource_id   Resource ID (ConvertKit Tag or Product ID).
     138 * @param {string} email         Email address.
     139 * @param {string} resource_type Resource Type (form|tag|product).
     140 * @param {string} resource_id   Resource ID (Kit Form,Tag or Product ID).
    141141 * @param {number} post_id       WordPress Post ID being viewed / accessed.
    142142 */
     
    148148    post_id
    149149) {
    150     fetch(convertkit_restrict_content.ajaxurl, {
     150    fetch(convertkit_restrict_content.subscriber_authentication_url, {
    151151        method: 'POST',
    152152        headers: {
    153153            'Content-Type': 'application/x-www-form-urlencoded',
     154            'X-WP-Nonce': nonce,
    154155        },
    155156        body: new URLSearchParams({
    156             action: 'convertkit_subscriber_authentication_send_code',
    157             _wpnonce: nonce,
    158157            convertkit_email: email,
    159158            convertkit_resource_type: resource_type,
     
    174173            }
    175174
    176             // Output response, which will be a form with/without an error message.
    177             document.querySelector(
    178                 '#convertkit-restrict-content-modal-content'
    179             ).innerHTML = result.data;
     175            // Output error message if the response contains a code.
     176            if (typeof result.code !== 'undefined') {
     177                document.querySelector(
     178                    '#convertkit-restrict-content-modal-content'
     179                ).innerHTML = result.message;
     180            } else {
     181                // Output response, which will be either:
     182                // - the email form view, with an error message e.g. invalid email,
     183                // - the code form view, where the user can enter the OTP.
     184                document.querySelector(
     185                    '#convertkit-restrict-content-modal-content'
     186                ).innerHTML = result.data;
     187            }
    180188
    181189            // Hide loading overlay.
     
    195203
    196204/**
    197  * Submits the given email address to maybe_run_subscriber_verification(), which
    198  * will return either:
     205 * Submits the given email address to the WP REST API kit/v1/restrict-content/subscriber-verification
     206 * endpoint, which will return either:
    199207 * - the code form view, with an error message e.g. invalid code entered,
    200208 * - the Post's URL, with a `ck-cache-bust` parameter appended, which can then be loaded to show the content.
     
    213221    post_id
    214222) {
    215     fetch(convertkit_restrict_content.ajaxurl, {
     223    fetch(convertkit_restrict_content.subscriber_verification_url, {
    216224        method: 'POST',
    217225        headers: {
    218226            'Content-Type': 'application/x-www-form-urlencoded',
     227            'X-WP-Nonce': nonce,
    219228        },
    220229        body: new URLSearchParams({
    221             action: 'convertkit_subscriber_verification',
    222             _wpnonce: nonce,
    223230            subscriber_code,
    224231            token,
     
    255262
    256263            // Code entered is valid; load the URL in the response data.
    257             window.location = result.data;
     264            window.location = result.url;
    258265        })
    259266        .catch(function (error) {
  • convertkit/tags/3.1.0/vendor/convertkit/convertkit-wordpress-libraries/src/class-convertkit-api-v4.php

    r3369648 r3399438  
    378378        if ( is_wp_error( $result ) ) {
    379379            $this->log( 'API: Error: ' . $result->get_error_message() );
     380
     381            /**
     382             * Perform any actions when obtaining an access token fails.
     383             *
     384             * @since   2.1.1
     385             *
     386             * @param   WP_Error  $result     Error from API.
     387             * @param   string    $client_id  OAuth Client ID.
     388             */
     389            do_action( 'convertkit_api_get_access_token_error', $result, $this->client_id );
     390
    380391            return $result;
    381392        }
     
    415426        if ( is_wp_error( $result ) ) {
    416427            $this->log( 'API: Error: ' . $result->get_error_message() );
     428
     429            /**
     430             * Perform any actions when refreshing an expired access token fails.
     431             *
     432             * @since   2.1.1
     433             *
     434             * @param   WP_Error  $result     Error from API.
     435             * @param   string    $client_id  OAuth Client ID.
     436             */
     437            do_action( 'convertkit_api_refresh_token_error', $result, $this->client_id );
     438
    417439            return $result;
    418440        }
     
    14331455        // Return the API error message as a WP_Error if the HTTP response code is a 4xx code.
    14341456        if ( $http_response_code >= 400 ) {
     1457
    14351458            // Define the error message.
    14361459            $error = $this->get_error_message_string( $response );
     
    14391462
    14401463            switch ( $http_response_code ) {
    1441                 // If the HTTP response code is 401, and the error matches 'The access token expired', refresh the access token now
    1442                 // and re-attempt the request.
    14431464                case 401:
    1444                     if ( $error !== 'The access token expired' ) {
    1445                         break;
     1465                    switch ( $error ) {
     1466                        case 'The access token expired':
     1467                            // Attempt to refresh the access token.
     1468                            $result = $this->refresh_token();
     1469
     1470                            // If an error occured, bail.
     1471                            if ( is_wp_error( $result ) ) {
     1472                                return $result;
     1473                            }
     1474
     1475                            // Attempt the request again, now we have a new access token.
     1476                            return $this->request( $endpoint, $method, $params, false );
     1477
     1478                        case 'The access token is invalid':
     1479                            $error = new WP_Error(
     1480                                'convertkit_api_error',
     1481                                $error,
     1482                                $http_response_code
     1483                            );
     1484
     1485                            /**
     1486                             * Perform any actions when an invalid access token was used.
     1487                             *
     1488                             * @since   2.1.1
     1489                             *
     1490                             * @param   WP_Error  $error      WP_Error object.
     1491                             * @param   string    $client_id  OAuth Client ID.
     1492                             */
     1493                            do_action( 'convertkit_api_access_token_invalid', $error, $this->client_id );
     1494
     1495                            // Return error.
     1496                            return $error;
     1497
     1498                        default:
     1499                            return new WP_Error(
     1500                                'convertkit_api_error',
     1501                                $error,
     1502                                $http_response_code
     1503                            );
    14461504                    }
    14471505
    1448                     // Don't automatically refresh the expired access token if we're not on a production environment.
    1449                     // This prevents the same ConvertKit account used on both a staging and production site from
    1450                     // reaching a race condition where the staging site refreshes the token first, resulting in
    1451                     // the production site unable to later refresh its same expired access token.
    1452                     if ( ! $this->is_production_site() ) {
    1453                         break;
    1454                     }
    1455 
    1456                     // Refresh the access token.
    1457                     $result = $this->refresh_token();
    1458 
    1459                     // If an error occured, bail.
    1460                     if ( is_wp_error( $result ) ) {
    1461                         return $result;
    1462                     }
    1463 
    1464                     // Attempt the request again, now we have a new access token.
    1465                     return $this->request( $endpoint, $method, $params, false );
    1466 
    1467                 // If a rate limit was hit, maybe try again.
    14681506                case 429:
    14691507                    // If retry on rate limit hit is disabled, return a WP_Error.
     
    14891527
    14901528        return $response;
    1491 
    1492     }
    1493 
    1494     /**
    1495      * Helper method to determine the WordPress environment type, checking
    1496      * if the wp_get_environment_type() function exists in WordPress (versions
    1497      * older than WordPress 5.5 won't have this function).
    1498      *
    1499      * @since   2.0.2
    1500      *
    1501      * @return  bool
    1502      */
    1503     private function is_production_site() {
    1504 
    1505         // If the WordPress wp_get_environment_type() function isn't available,
    1506         // assume this is a production site.
    1507         if ( ! function_exists( 'wp_get_environment_type' ) ) {
    1508             return true;
    1509         }
    1510 
    1511         return ( wp_get_environment_type() === 'production' );
    15121529
    15131530    }
  • convertkit/tags/3.1.0/views/backend/settings/tools.php

    r3164741 r3399438  
    103103
    104104    <?php
     105    // Mailchimp for WordPress (MC4WP).
     106    if ( $mc4wp->has_forms_in_posts() && $mc4wp->has_forms() && $forms->exist() ) {
     107        ?>
     108        <div id="import-mc4wp" class="postbox">
     109            <h2><?php esc_html_e( 'MC4WP: Migrate Configuration', 'convertkit' ); ?></h2>
     110
     111            <p class="description">
     112                <?php esc_html_e( 'Automatically replace MC4WP form shortcodes with Kit forms.', 'convertkit' ); ?><br />
     113            </p>
     114
     115            <table class="widefat striped">
     116                <thead>
     117                    <tr>
     118                        <th><?php esc_html_e( 'MC4WP Form', 'convertkit' ); ?></th>
     119                        <th><?php esc_html_e( 'Kit Form', 'convertkit' ); ?></th>
     120                    </tr>
     121                </thead>
     122                <tbody>
     123                    <?php
     124                    foreach ( $mc4wp->get_forms() as $mc4wp_form_id => $mc4wp_form_title ) {
     125                        ?>
     126                        <tr>
     127                            <td><?php echo esc_html( $mc4wp_form_title ); ?></td>
     128                            <td>
     129                                <select name="_wp_convertkit_integration_mc4wp_settings[<?php echo esc_attr( $mc4wp_form_id ); ?>]">
     130                                    <?php
     131                                    foreach ( $forms->get() as $form ) {
     132                                        ?>
     133                                        <option value="<?php echo esc_attr( $form['id'] ); ?>"><?php echo esc_html( $form['name'] ); ?></option>
     134                                        <?php
     135                                    }
     136                                    ?>
     137                                </select>
     138                            </td>
     139                        </tr>
     140                        <?php
     141                    }
     142                    ?>
     143                </tbody>
     144            </table>
     145
     146            <p>
     147                <?php
     148                submit_button(
     149                    __( 'Migrate', 'convertkit' ),
     150                    'primary',
     151                    'convertkit-import-mc4wp',
     152                    false
     153                );
     154                ?>
     155            </p>
     156        </div><!-- .postbox -->
     157        <?php
     158    }
     159
    105160    wp_nonce_field( 'convertkit-settings-tools', '_convertkit_settings_tools_nonce' );
    106161    ?>
  • convertkit/tags/3.1.0/wp-convertkit.php

    r3390429 r3399438  
    1010 * Plugin URI: https://kit.com/
    1111 * Description: Display Kit (formerly ConvertKit) email subscription forms, landing pages, products, broadcasts and more.
    12  * Version: 3.0.8
     12 * Version: 3.1.0
    1313 * Author: Kit
    1414 * Author URI: https://kit.com/
     
    2828define( 'CONVERTKIT_PLUGIN_URL', plugin_dir_url( __FILE__ ) );
    2929define( 'CONVERTKIT_PLUGIN_PATH', __DIR__ );
    30 define( 'CONVERTKIT_PLUGIN_VERSION', '3.0.8' );
     30define( 'CONVERTKIT_PLUGIN_VERSION', '3.1.0' );
    3131define( 'CONVERTKIT_OAUTH_CLIENT_ID', 'HXZlOCj-K5r0ufuWCtyoyo3f688VmMAYSsKg1eGvw0Y' );
    3232define( 'CONVERTKIT_OAUTH_CLIENT_REDIRECT_URI', 'https://app.kit.com/wordpress/redirect' );
     
    5353require_once CONVERTKIT_PLUGIN_PATH . '/includes/functions.php';
    5454require_once CONVERTKIT_PLUGIN_PATH . '/includes/class-wp-convertkit.php';
     55require_once CONVERTKIT_PLUGIN_PATH . '/includes/class-convertkit-admin-notices.php';
    5556require_once CONVERTKIT_PLUGIN_PATH . '/includes/class-convertkit-ajax.php';
    5657require_once CONVERTKIT_PLUGIN_PATH . '/includes/class-convertkit-broadcasts-exporter.php';
     
    109110require_once CONVERTKIT_PLUGIN_PATH . '/admin/class-convertkit-admin-category.php';
    110111require_once CONVERTKIT_PLUGIN_PATH . '/admin/class-convertkit-admin-landing-page.php';
    111 require_once CONVERTKIT_PLUGIN_PATH . '/admin/class-convertkit-admin-notices.php';
    112112require_once CONVERTKIT_PLUGIN_PATH . '/admin/class-convertkit-admin-post.php';
    113113require_once CONVERTKIT_PLUGIN_PATH . '/admin/class-convertkit-admin-refresh-resources.php';
     
    117117require_once CONVERTKIT_PLUGIN_PATH . '/admin/class-convertkit-admin-setup-wizard.php';
    118118require_once CONVERTKIT_PLUGIN_PATH . '/admin/class-convertkit-wp-list-table.php';
     119require_once CONVERTKIT_PLUGIN_PATH . '/admin/importers/class-convertkit-admin-importer.php';
     120require_once CONVERTKIT_PLUGIN_PATH . '/admin/importers/class-convertkit-admin-importer-mc4wp.php';
    119121require_once CONVERTKIT_PLUGIN_PATH . '/admin/section/class-convertkit-admin-section-base.php';
    120122require_once CONVERTKIT_PLUGIN_PATH . '/admin/section/class-convertkit-admin-section-broadcasts.php';
  • convertkit/trunk/CHANGELOG.md

    r3390429 r3399438  
     1### 3.1.0 2025-11-20
     2* Added: Settings: Tools: Mailchimp for WordPress to Kit Form Importer
     3* Fix: Settings: Non-inline Forms: Correctly output `data-kit-limit-per-session`
     4* Fix: Settings: Automatically delete invalid Access Tokens
     5* Fix: Member Content: Courses: Next/Previous Links compatibility for WordPress 6.9
     6* Fix: Landing Pages: Prevent LazyLoad Plugin from lazy loading images on Landing Pages, which would result in missing images
     7* Updated: Blocks: Refresh Resources: Use REST API, replacing admin-ajax.php
     8* Updated: Refresh Resources: Use REST API, replacing admin-ajax.php
     9* Updated: Member Content: Use REST API, replacing admin-ajax.php
     10* Updated: Use WordPress Libraries 2.1.1
     11* Removed: Unused `convertkit_store_subscriber_id_in_cookie` AJAX function
     12
    113### 3.0.8 2025-11-05
    214* Fix: Member Content: Product: Display 'no access' notice when logged in and no access
  • convertkit/trunk/admin/class-convertkit-admin-refresh-resources.php

    r3251976 r3399438  
    2323    public function __construct() {
    2424
    25         add_action( 'wp_ajax_convertkit_admin_refresh_resources', array( $this, 'refresh_resources' ) );
    2625        add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_scripts' ) );
     26        add_action( 'rest_api_init', array( $this, 'register_routes' ) );
     27
     28    }
     29
     30    /**
     31     * Register REST API routes.
     32     *
     33     * @since   3.1.0
     34     */
     35    public function register_routes() {
     36
     37        // Register route to return all blocks registered by the Plugin.
     38        register_rest_route(
     39            'kit/v1',
     40            '/resources/refresh/(?P<resource>[a-zA-Z0-9-_]+)',
     41            array(
     42                'methods'             => WP_REST_Server::CREATABLE,
     43                'callback'            => array( $this, 'refresh_resources' ),
     44                'permission_callback' => function () {
     45                    return current_user_can( 'edit_posts' );
     46                },
     47            )
     48        );
    2749
    2850    }
     
    3254     *
    3355     * @since   1.9.8.0
     56     *
     57     * @param   WP_REST_Request $request    Request object.
     58     * @return  WP_REST_Response|WP_Error               Response object.
    3459     */
    35     public function refresh_resources() {
    36 
    37         // Check nonce.
    38         check_ajax_referer( 'convertkit_admin_refresh_resources', 'nonce' );
     60    public function refresh_resources( $request ) {
    3961
    4062        // Get resource type.
    41         $resource = ( isset( $_REQUEST['resource'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['resource'] ) ) : '' );
     63        $resource = $request->get_param( 'resource' );
    4264
    4365        // Fetch resources.
     
    7597                // Bail if an error occured.
    7698                if ( is_wp_error( $results_tags ) ) {
    77                     wp_send_json_error( $results_tags->get_error_message() );
     99                    return rest_ensure_response( $results_tags );
    78100                }
    79101
     
    84106                // Bail if an error occured.
    85107                if ( is_wp_error( $results_products ) ) {
    86                     wp_send_json_error( $results_products->get_error_message() );
     108                    return rest_ensure_response( $results_products );
    87109                }
    88110
    89111                // Return resources.
    90                 wp_send_json_success(
     112                return rest_ensure_response(
    91113                    array(
    92114                        'tags'     => array_values( $results_tags ),
     
    94116                    )
    95117                );
    96                 // no break as wp_send_json_success terminates.
    97118
    98119            default:
     
    109130        // Bail if an error occured.
    110131        if ( is_wp_error( $results ) ) {
    111             wp_send_json_error( $results->get_error_message() );
     132            return rest_ensure_response( $results );
    112133        }
    113134
    114135        // Return resources as a zero based sequential array, so that JS retains the order of resources.
    115         wp_send_json_success( array_values( $results ) );
     136        return rest_ensure_response( array_values( $results ) );
    116137
    117138    }
     
    145166            'convertkit_admin_refresh_resources',
    146167            array(
    147                 'action'  => 'convertkit_admin_refresh_resources',
    148                 'ajaxurl' => admin_url( 'admin-ajax.php' ),
     168                'ajaxurl' => rest_url( 'kit/v1/resources/refresh/' ),
    149169                'debug'   => $settings->debug_enabled(),
    150                 'nonce'   => wp_create_nonce( 'convertkit_admin_refresh_resources' ),
     170                'nonce'   => wp_create_nonce( 'wp_rest' ),
    151171            )
    152172        );
  • convertkit/trunk/admin/section/class-convertkit-admin-section-general.php

    r3386855 r3399438  
    134134        // Bail if no access and refresh token exist.
    135135        if ( ! $this->settings->has_access_and_refresh_token() ) {
    136             return;
     136            // Redirect to General screen, which will now show the ConvertKit_Admin_Section_OAuth screen, because
     137            // the Plugin has no access token.
     138            wp_safe_redirect(
     139                add_query_arg(
     140                    array(
     141                        'page' => $this->settings_key,
     142                    ),
     143                    'options-general.php'
     144                )
     145            );
     146            exit();
    137147        }
    138148
     
    153163        // If the request succeeded, no need to perform further actions.
    154164        if ( ! is_wp_error( $this->account ) ) {
    155             // Remove any existing persistent notice.
    156             WP_ConvertKit()->get_class( 'admin_notices' )->delete( 'authorization_failed' );
    157 
    158165            return;
    159166        }
    160167
    161         // Depending on the error code, maybe persist a notice in the WordPress Administration until the user
     168        // Depending on the error code, display an error notice in the settings screen until the user
    162169        // fixes the problem.
    163         switch ( $this->account->get_error_data( $this->account->get_error_code() ) ) {
    164             case 401:
    165                 // Access token either expired or was revoked in ConvertKit.
    166                 // Remove from settings.
    167                 $this->settings->delete_credentials();
    168 
    169                 // Display a site wide notice.
    170                 WP_ConvertKit()->get_class( 'admin_notices' )->add( 'authorization_failed' );
    171 
    172                 // Redirect to General screen, which will now show the ConvertKit_Admin_Section_OAuth screen, because
    173                 // the Plugin has no access token.
    174                 wp_safe_redirect(
    175                     add_query_arg(
    176                         array(
    177                             'page' => $this->settings_key,
    178                         ),
    179                         'options-general.php'
    180                     )
    181                 );
    182                 exit();
    183         }
    184 
    185         // Output a non-401 error now.
    186170        $this->output_error( $this->account->get_error_message() );
    187171
  • convertkit/trunk/admin/section/class-convertkit-admin-section-tools.php

    r3357832 r3399438  
    5858                'import_configuration_empty'             => __( 'The uploaded configuration file contains no settings.', 'convertkit' ),
    5959                'import_configuration_success'           => __( 'Configuration imported successfully.', 'convertkit' ),
     60                'migrate_mc4wp_configuration_success'    => __( 'MC4WP forms migrated successfully.', 'convertkit' ),
    6061            )
    6162        );
     
    7677        $this->maybe_export_configuration();
    7778        $this->maybe_import_configuration();
     79        $this->maybe_migrate_mc4wp_configuration();
    7880
    7981    }
     
    316318
    317319    /**
     320     * Replaces MC4WP Form Shortcodes with Kit Form Shortcodes, if the user submitted the
     321     * MC4WP Migrate Configuration section.
     322     *
     323     * @since   3.1.0
     324     */
     325    private function maybe_migrate_mc4wp_configuration() {
     326
     327        // Bail if nonce verification fails.
     328        if ( ! isset( $_REQUEST['_convertkit_settings_tools_nonce'] ) ) {
     329            return;
     330        }
     331
     332        if ( ! wp_verify_nonce( sanitize_key( $_REQUEST['_convertkit_settings_tools_nonce'] ), 'convertkit-settings-tools' ) ) {
     333            return;
     334        }
     335
     336        // Bail if no MC4WP Form IDs were submitted.
     337        if ( ! isset( $_REQUEST['_wp_convertkit_integration_mc4wp_settings'] ) ) {
     338            return;
     339        }
     340
     341        // Initialise the importer.
     342        $mc4wp = new ConvertKit_Admin_Importer_MC4WP();
     343
     344        // Iterate through the MC4WP Form IDs and replace the shortcodes with the Kit Form Shortcodes.
     345        foreach ( array_map( 'sanitize_text_field', wp_unslash( $_REQUEST['_wp_convertkit_integration_mc4wp_settings'] ) ) as $mc4wp_form_id => $kit_form_id ) {
     346            $mc4wp->replace_shortcodes_in_posts( (int) $mc4wp_form_id, (int) $kit_form_id );
     347        }
     348
     349        // Redirect to Tools screen.
     350        $this->redirect_with_success_notice( 'migrate_mc4wp_configuration_success' );
     351
     352    }
     353
     354    /**
    318355     * Outputs the Debug Log and System Info view.
    319356     *
     
    333370        $system_info = $this->get_system_info();
    334371
     372        // Get Forms.
     373        $forms = new ConvertKit_Resource_Forms();
     374
     375        // Get Importers.
     376        $mc4wp = new ConvertKit_Admin_Importer_MC4WP();
     377
    335378        // Output view.
    336379        require_once CONVERTKIT_PLUGIN_PATH . '/views/backend/settings/tools.php';
  • convertkit/trunk/includes/blocks/class-convertkit-block-broadcasts.php

    r3383204 r3399438  
    273273    public function get_fields() {
    274274
    275         // Bail if the request is not for the WordPress Administration or frontend editor.
    276         if ( ! WP_ConvertKit()->is_admin_or_frontend_editor() ) {
     275        // Bail if the request is not for the WordPress Administration, frontend editor or REST API request.
     276        if ( ! $this->is_admin_frontend_editor_or_rest_request() ) {
    277277            return false;
    278278        }
     
    381381    public function get_panels() {
    382382
    383         // Bail if the request is not for the WordPress Administration or frontend editor.
    384         if ( ! WP_ConvertKit()->is_admin_or_frontend_editor() ) {
     383        // Bail if the request is not for the WordPress Administration, frontend editor or REST API request.
     384        if ( ! $this->is_admin_frontend_editor_or_rest_request() ) {
    385385            return false;
    386386        }
  • convertkit/trunk/includes/blocks/class-convertkit-block-content.php

    r3160977 r3399438  
    105105    public function get_fields() {
    106106
    107         // Bail if the request is not for the WordPress Administration or frontend editor.
    108         if ( ! WP_ConvertKit()->is_admin_or_frontend_editor() ) {
     107        // Bail if the request is not for the WordPress Administration, frontend editor or REST API request.
     108        if ( ! $this->is_admin_frontend_editor_or_rest_request() ) {
    109109            return false;
    110110        }
     
    138138    public function get_panels() {
    139139
    140         // Bail if the request is not for the WordPress Administration or frontend editor.
    141         if ( ! WP_ConvertKit()->is_admin_or_frontend_editor() ) {
     140        // Bail if the request is not for the WordPress Administration, frontend editor or REST API request.
     141        if ( ! $this->is_admin_frontend_editor_or_rest_request() ) {
    142142            return false;
    143143        }
  • convertkit/trunk/includes/blocks/class-convertkit-block-form-builder-field-custom.php

    r3357832 r3399438  
    102102    public function get_fields() {
    103103
    104         // Bail if the request is not for the WordPress Administration or frontend editor.
    105         if ( ! WP_ConvertKit()->is_admin_or_frontend_editor() ) {
     104        // Bail if the request is not for the WordPress Administration, frontend editor or REST API request.
     105        if ( ! $this->is_admin_frontend_editor_or_rest_request() ) {
    106106            return false;
    107107        }
     
    151151    public function get_panels() {
    152152
    153         // Bail if the request is not for the WordPress Administration or frontend editor.
    154         if ( ! WP_ConvertKit()->is_admin_or_frontend_editor() ) {
     153        // Bail if the request is not for the WordPress Administration, frontend editor or REST API request.
     154        if ( ! $this->is_admin_frontend_editor_or_rest_request() ) {
    155155            return false;
    156156        }
  • convertkit/trunk/includes/blocks/class-convertkit-block-form-builder-field-email.php

    r3357832 r3399438  
    121121    public function get_fields() {
    122122
    123         // Bail if the request is not for the WordPress Administration or frontend editor.
    124         if ( ! WP_ConvertKit()->is_admin_or_frontend_editor() ) {
     123        // Bail if the request is not for the WordPress Administration, frontend editor or REST API request.
     124        if ( ! $this->is_admin_frontend_editor_or_rest_request() ) {
    125125            return false;
    126126        }
  • convertkit/trunk/includes/blocks/class-convertkit-block-form-builder-field.php

    r3359712 r3399438  
    195195    public function get_fields() {
    196196
    197         // Bail if the request is not for the WordPress Administration or frontend editor.
    198         if ( ! WP_ConvertKit()->is_admin_or_frontend_editor() ) {
     197        // Bail if the request is not for the WordPress Administration, frontend editor or REST API request.
     198        if ( ! $this->is_admin_frontend_editor_or_rest_request() ) {
    199199            return false;
    200200        }
     
    224224    public function get_panels() {
    225225
    226         // Bail if the request is not for the WordPress Administration or frontend editor.
    227         if ( ! WP_ConvertKit()->is_admin_or_frontend_editor() ) {
     226        // Bail if the request is not for the WordPress Administration, frontend editor or REST API request.
     227        if ( ! $this->is_admin_frontend_editor_or_rest_request() ) {
    228228            return false;
    229229        }
  • convertkit/trunk/includes/blocks/class-convertkit-block-form-builder.php

    r3383204 r3399438  
    476476    public function get_fields() {
    477477
    478         // Bail if the request is not for the WordPress Administration or frontend editor.
    479         if ( ! WP_ConvertKit()->is_admin_or_frontend_editor() ) {
     478        // Bail if the request is not for the WordPress Administration, frontend editor or REST API request.
     479        if ( ! $this->is_admin_frontend_editor_or_rest_request() ) {
    480480            return false;
    481481        }
     
    569569    public function get_panels() {
    570570
    571         // Bail if the request is not for the WordPress Administration or frontend editor.
    572         if ( ! WP_ConvertKit()->is_admin_or_frontend_editor() ) {
     571        // Bail if the request is not for the WordPress Administration, frontend editor or REST API request.
     572        if ( ! $this->is_admin_frontend_editor_or_rest_request() ) {
    573573            return false;
    574574        }
  • convertkit/trunk/includes/blocks/class-convertkit-block-form-trigger.php

    r3337204 r3399438  
    213213    public function get_fields() {
    214214
    215         // Bail if the request is not for the WordPress Administration or frontend editor.
    216         if ( ! WP_ConvertKit()->is_admin_or_frontend_editor() ) {
     215        // Bail if the request is not for the WordPress Administration, frontend editor or REST API request.
     216        if ( ! $this->is_admin_frontend_editor_or_rest_request() ) {
    217217            return false;
    218218        }
     
    271271    public function get_panels() {
    272272
    273         // Bail if the request is not for the WordPress Administration or frontend editor.
    274         if ( ! WP_ConvertKit()->is_admin_or_frontend_editor() ) {
     273        // Bail if the request is not for the WordPress Administration, frontend editor or REST API request.
     274        if ( ! $this->is_admin_frontend_editor_or_rest_request() ) {
    275275            return false;
    276276        }
  • convertkit/trunk/includes/blocks/class-convertkit-block-form.php

    r3357832 r3399438  
    241241    public function get_fields() {
    242242
    243         // Bail if the request is not for the WordPress Administration or frontend editor.
    244         if ( ! WP_ConvertKit()->is_admin_or_frontend_editor() ) {
     243        // Bail if the request is not for the WordPress Administration, frontend editor or REST API request.
     244        if ( ! $this->is_admin_frontend_editor_or_rest_request() ) {
    245245            return false;
    246246        }
     
    286286    public function get_panels() {
    287287
    288         // Bail if the request is not for the WordPress Administration or frontend editor.
    289         if ( ! WP_ConvertKit()->is_admin_or_frontend_editor() ) {
     288        // Bail if the request is not for the WordPress Administration, frontend editor or REST API request.
     289        if ( ! $this->is_admin_frontend_editor_or_rest_request() ) {
    290290            return false;
    291291        }
  • convertkit/trunk/includes/blocks/class-convertkit-block-product.php

    r3337204 r3399438  
    246246    public function get_fields() {
    247247
    248         // Bail if the request is not for the WordPress Administration or frontend editor.
    249         if ( ! WP_ConvertKit()->is_admin_or_frontend_editor() ) {
     248        // Bail if the request is not for the WordPress Administration, frontend editor or REST API request.
     249        if ( ! $this->is_admin_frontend_editor_or_rest_request() ) {
    250250            return false;
    251251        }
     
    313313    public function get_panels() {
    314314
    315         // Bail if the request is not for the WordPress Administration or frontend editor.
    316         if ( ! WP_ConvertKit()->is_admin_or_frontend_editor() ) {
     315        // Bail if the request is not for the WordPress Administration, frontend editor or REST API request.
     316        if ( ! $this->is_admin_frontend_editor_or_rest_request() ) {
    317317            return false;
    318318        }
  • convertkit/trunk/includes/blocks/class-convertkit-block.php

    r3337204 r3399438  
    397397
    398398    /**
     399     * Determines if the request is a WordPress REST API request.
     400     *
     401     * @since   3.1.0
     402     *
     403     * @return  bool
     404     */
     405    public function is_rest_request() {
     406
     407        return defined( 'REST_REQUEST' ) && REST_REQUEST;
     408
     409    }
     410
     411    /**
     412     * Determines if the request is for the WordPress Administration, frontend editor or REST API request.
     413     *
     414     * @since   3.1.0
     415     *
     416     * @return  bool
     417     */
     418    public function is_admin_frontend_editor_or_rest_request() {
     419
     420        return WP_ConvertKit()->is_admin_or_frontend_editor() || $this->is_rest_request();
     421
     422    }
     423
     424    /**
    399425     * Determines if the request for the block is from the block editor or the frontend site.
    400426     *
     
    406432
    407433        // Return false if not a WordPress REST API request, which Gutenberg uses.
    408         if ( ! defined( 'REST_REQUEST' ) ) {
    409             return false;
    410         }
    411         if ( REST_REQUEST !== true ) {
     434        if ( ! $this->is_rest_request() ) {
    412435            return false;
    413436        }
  • convertkit/trunk/includes/class-convertkit-ajax.php

    r3380693 r3399438  
    2121    public function __construct() {
    2222
    23         add_action( 'wp_ajax_convertkit_get_blocks', array( $this, 'get_blocks' ) );
    24 
    25         add_action( 'wp_ajax_nopriv_convertkit_store_subscriber_id_in_cookie', array( $this, 'store_subscriber_id_in_cookie' ) );
    26         add_action( 'wp_ajax_convertkit_store_subscriber_id_in_cookie', array( $this, 'store_subscriber_id_in_cookie' ) );
    27 
    2823        add_action( 'wp_ajax_nopriv_convertkit_store_subscriber_email_as_id_in_cookie', array( $this, 'store_subscriber_email_as_id_in_cookie' ) );
    2924        add_action( 'wp_ajax_convertkit_store_subscriber_email_as_id_in_cookie', array( $this, 'store_subscriber_email_as_id_in_cookie' ) );
    30 
    31         add_action( 'wp_ajax_nopriv_convertkit_subscriber_authentication_send_code', array( $this, 'subscriber_authentication_send_code' ) );
    32         add_action( 'wp_ajax_convertkit_subscriber_authentication_send_code', array( $this, 'subscriber_authentication_send_code' ) );
    33 
    34         add_action( 'wp_ajax_nopriv_convertkit_subscriber_verification', array( $this, 'subscriber_verification' ) );
    35         add_action( 'wp_ajax_convertkit_subscriber_verification', array( $this, 'subscriber_verification' ) );
    36 
    37     }
    38 
    39     /**
    40      * Returns all ConvertKit registered blocks.
    41      *
    42      * Typically used when a refresh button in a block has been pressed when
    43      * displayNoticeWithLink() is called, because either
    44      * no Access Token is specified, or no resources exist in ConvertKit.
    45      *
    46      * @since   2.2.6
    47      */
    48     public function get_blocks() {
    49 
    50         // Check nonce.
    51         check_ajax_referer( 'convertkit_get_blocks', 'nonce' );
    52 
    53         // Refresh resources from the API, to reflect any changes.
    54         $forms = new ConvertKit_Resource_Forms( 'block_edit' );
    55         $forms->refresh();
    56 
    57         $posts = new ConvertKit_Resource_Posts( 'block_edit' );
    58         $posts->refresh();
    59 
    60         $products = new ConvertKit_Resource_Products( 'block_edit' );
    61         $products->refresh();
    62 
    63         // Return blocks.
    64         wp_send_json_success( convertkit_get_blocks() );
    65 
    66     }
    67 
    68     /**
    69      * Stores the ConvertKit Subscriber's ID in a cookie.
    70      *
    71      * Typically performed when the user subscribes via a ConvertKit Form on the web site
    72      * that is set to "Send subscriber to thank you page", and the Plugin's JavaScript is not
    73      * disabled, permitting convertkit.js to run.
    74      *
    75      * @since   1.9.6
    76      */
    77     public function store_subscriber_id_in_cookie() {
    78 
    79         // Check nonce.
    80         check_ajax_referer( 'convertkit', 'convertkit_nonce' );
    81 
    82         // Bail if required request parameters not submitted.
    83         if ( ! isset( $_REQUEST['subscriber_id'] ) ) {
    84             wp_send_json_error( __( 'Kit: Required parameter `subscriber_id` not included in AJAX request.', 'convertkit' ) );
    85         }
    86 
    87         // Bail if no subscriber ID provided.
    88         $id = absint( sanitize_text_field( wp_unslash( $_REQUEST['subscriber_id'] ) ) );
    89         if ( empty( $id ) ) {
    90             wp_send_json_error( __( 'Kit: Required parameter `subscriber_id` empty in AJAX request.', 'convertkit' ) );
    91         }
    92 
    93         // Get subscriber ID.
    94         $subscriber    = new ConvertKit_Subscriber();
    95         $subscriber_id = $subscriber->validate_and_store_subscriber_id( $id );
    96 
    97         // Bail if an error occured i.e. API hasn't been configured, subscriber ID does not exist in ConvertKit etc.
    98         if ( is_wp_error( $subscriber_id ) ) {
    99             wp_send_json_error( $subscriber_id->get_error_message() );
    100         }
    101 
    102         // Return the subscriber ID.
    103         wp_send_json_success(
    104             array(
    105                 'id' => $subscriber_id,
    106             )
    107         );
    10825
    10926    }
     
    15673    }
    15774
    158     /**
    159      * Calls the API to send the subscriber a magic link by email containing a code when
    160      * the modal version of Restrict Content is used, and the user has submitted their email address.
    161      *
    162      * Returns a view of either:
    163      * - an error message and email input i.e. the user entered an invalid email address,
    164      * - the code input, which is then displayed in the modal for the user to enter the code sent by email.
    165      *
    166      * See maybe_run_subscriber_verification() for logic once they enter the code on screen.
    167      *
    168      * @since   2.3.8
    169      */
    170     public function subscriber_authentication_send_code() {
    171 
    172         // Load Restrict Content class.
    173         $output_restrict_content = WP_ConvertKit()->get_class( 'output_restrict_content' );
    174 
    175         // Run subscriber authentication.
    176         $output_restrict_content->maybe_run_subscriber_authentication();
    177 
    178         // Fetch Post ID, Resource Type and Resource ID for the view.
    179         $post_id       = $output_restrict_content->post_id;
    180         $resource_type = $output_restrict_content->resource_type;
    181         $resource_id   = $output_restrict_content->resource_id;
    182 
    183         // If an error occured, build the email form view with the error message.
    184         if ( is_wp_error( $output_restrict_content->error ) ) {
    185             ob_start();
    186             include CONVERTKIT_PLUGIN_PATH . '/views/frontend/restrict-content/login-modal-content-email.php';
    187             $output = trim( ob_get_clean() );
    188             wp_send_json_success( $output );
    189         }
    190 
    191         // Build authentication code view to return for output.
    192         ob_start();
    193         include CONVERTKIT_PLUGIN_PATH . '/views/frontend/restrict-content/login-modal-content-code.php';
    194         $output = trim( ob_get_clean() );
    195         wp_send_json_success( $output );
    196 
    197     }
    198 
    199     /**
    200      * Calls the API to verify the token and entered subscriber code, which tells us that the email
    201      * address supplied truly belongs to the user, and that we can safely trust their subscriber ID
    202      * to be valid.
    203      *
    204      * @since   2.3.8
    205      */
    206     public function subscriber_verification() {
    207 
    208         // Load Restrict Content class.
    209         $output_restrict_content = WP_ConvertKit()->get_class( 'output_restrict_content' );
    210 
    211         // Run subscriber authentication.
    212         $output_restrict_content->maybe_run_subscriber_verification();
    213 
    214         // Fetch Post ID, Resource Type and Resource ID for the view.
    215         $post_id       = $output_restrict_content->post_id;
    216         $resource_type = $output_restrict_content->resource_type;
    217         $resource_id   = $output_restrict_content->resource_id;
    218 
    219         // If an error occured, build the code form view with the error message.
    220         if ( is_wp_error( $output_restrict_content->error ) ) {
    221             ob_start();
    222             include CONVERTKIT_PLUGIN_PATH . '/views/frontend/restrict-content/login-modal-content-code.php';
    223             $output = trim( ob_get_clean() );
    224             wp_send_json_error( $output );
    225         }
    226 
    227         // Return success with the URL to the Post, including the `ck-cache-bust` parameter.
    228         // JS will load the given URL to show the restricted content.
    229         wp_send_json_success( $output_restrict_content->get_url( true ) );
    230 
    231     }
    232 
    23375}
  • convertkit/trunk/includes/class-convertkit-cache-plugins.php

    r3359712 r3399438  
    1616
    1717    /**
    18      * Holds external hosts to exclude from CSS and JS minification.
     18     * Holds external hosts to exclude from CSS and JS minification
     19     * and lazy loading of images.
    1920     *
    2021     * @since   2.4.6
     
    2829        'pages.convertkit.com',
    2930        'convertkit.com',
     31        'filekitcdn.com',
    3032    );
    3133
     
    5557
    5658        // Debloat: Exclude Forms from Delay Load JS.
    57         add_filter( 'debloat/defer_js_excludes', array( $this, 'exclude_hosts_from_minification' ) );
    58         add_filter( 'debloat/delay_js_excludes', array( $this, 'exclude_hosts_from_minification' ) );
     59        add_filter( 'debloat/defer_js_excludes', array( $this, 'exclude_hosts' ) );
     60        add_filter( 'debloat/delay_js_excludes', array( $this, 'exclude_hosts' ) );
    5961
    6062        // Jetpack Boost: Exclude Forms from JS defer.
     
    7577        add_filter( 'convertkit_resource_forms_output_script', array( $this, 'siteground_speed_optimizer_exclude_js_combine' ) );
    7678
     79        // Rocket LazyLoad: Exclude images from lazy loading.
     80        add_filter( 'rocket_lazyload_excluded_src', array( $this, 'exclude_hosts' ) );
     81
    7782        // WP Rocket: Disable Caching and Minification on Landing Pages.
    7883        add_action( 'convertkit_output_landing_page_before', array( $this, 'wp_rocket_disable_caching_and_minification_on_landing_pages' ) );
    7984
    8085        // WP Rocket: Exclude Forms from JS minification and combine.
    81         add_filter( 'rocket_minify_excluded_external_js', array( $this, 'exclude_hosts_from_minification' ) );
     86        add_filter( 'rocket_minify_excluded_external_js', array( $this, 'exclude_hosts' ) );
    8287
    8388        // WP Rocket: Exclude Forms from Delay JavaScript execution.
     
    239244    public function wp_rocket_disable_caching_and_minification_on_landing_pages() {
    240245
    241         add_filter( 'rocket_minify_excluded_external_js', array( $this, 'exclude_hosts_from_minification' ) );
    242         add_filter( 'rocket_exclude_css', array( $this, 'exclude_hosts_from_minification' ) );
     246        add_filter( 'rocket_minify_excluded_external_js', array( $this, 'exclude_hosts' ) );
     247        add_filter( 'rocket_exclude_css', array( $this, 'exclude_hosts' ) );
    243248        add_filter( 'rocket_exclude_js', array( $this, 'exclude_local_js_from_minification' ) );
    244         add_filter( 'do_rocket_lazyload', '__return_false' );
    245249
    246250    }
     
    278282    public function exclude_hosts_from_minification( $hosts ) {
    279283
     284        _doing_it_wrong( __FUNCTION__, 'Use exclude_hosts() instead.', '3.1.0' );
     285
     286        return $this->exclude_hosts( $hosts );
     287
     288    }
     289
     290    /**
     291     * Appends the $exclude_hosts property to an array of existing hosts.
     292     *
     293     * @since   3.1.0
     294     *
     295     * @param   array $hosts  External hosts to ignore.
     296     * @return  array
     297     */
     298    public function exclude_hosts( $hosts ) {
     299
    280300        return array_merge( $hosts, $this->exclude_hosts );
    281301
  • convertkit/trunk/includes/class-convertkit-gutenberg.php

    r3203903 r3399438  
    3232        // Register Gutenberg Blocks.
    3333        add_action( 'init', array( $this, 'add_blocks' ) );
     34
     35        // Register REST API routes.
     36        add_action( 'rest_api_init', array( $this, 'register_routes' ) );
     37
     38    }
     39
     40    /**
     41     * Register REST API routes.
     42     *
     43     * @since   3.1.0
     44     */
     45    public function register_routes() {
     46
     47        // Register route to return all blocks registered by the Plugin.
     48        register_rest_route(
     49            'kit/v1',
     50            '/blocks',
     51            array(
     52                'methods'             => WP_REST_Server::READABLE,
     53
     54                // Refresh resources and return blocks.
     55                'callback'            => function () {
     56                    // Refresh resources from the API, to reflect any changes.
     57                    $forms = new ConvertKit_Resource_Forms( 'block_edit' );
     58                    $forms->refresh();
     59
     60                    $posts = new ConvertKit_Resource_Posts( 'block_edit' );
     61                    $posts->refresh();
     62
     63                    $products = new ConvertKit_Resource_Products( 'block_edit' );
     64                    $products->refresh();
     65
     66                    // Return blocks.
     67                    return rest_ensure_response( convertkit_get_blocks() );
     68                },
     69
     70                'permission_callback' => function () {
     71                    return current_user_can( 'edit_posts' );
     72                },
     73            )
     74        );
    3475
    3576    }
     
    158199            'convertkit_gutenberg',
    159200            array(
    160                 'get_blocks_nonce' => wp_create_nonce( 'convertkit_get_blocks' ),
     201                'ajaxurl'          => rest_url( 'kit/v1/blocks' ),
     202                'get_blocks_nonce' => wp_create_nonce( 'wp_rest' ),
    161203            )
    162204        );
  • convertkit/trunk/includes/class-convertkit-output-restrict-content.php

    r3380693 r3399438  
    107107    public function __construct() {
    108108
    109         // Initialize classes that will be used.
    110         $this->settings                  = new ConvertKit_Settings();
    111         $this->restrict_content_settings = new ConvertKit_Settings_Restrict_Content();
    112 
    113         // Don't register any hooks if this is an AJAX request, otherwise
    114         // maybe_run_subscriber_authentication() and maybe_run_subscriber_verification() will run
    115         // twice in an AJAX request (once here, and once when called by the ConvertKit_AJAX class).
    116         if ( wp_doing_ajax() ) {
    117             return;
    118         }
    119 
     109        add_action( 'rest_api_init', array( $this, 'register_routes' ) );
     110        add_action( 'init', array( $this, 'initialize_classes' ), 2 );
    120111        add_action( 'init', array( $this, 'maybe_run_subscriber_authentication' ), 3 );
    121112        add_action( 'wp', array( $this, 'maybe_run_subscriber_verification' ), 4 );
     
    129120
    130121    /**
    131      * Checks if the request is a Restrict Content request with an email address.
    132      * If so, calls the API depending on the Restrict Content resource that's required:
    133      * - tag: subscribes the email address to the tag, storing the subscriber ID in a cookie and redirecting
    134      * - product: calls the API to send the subscriber a magic link by email containing a code. See maybe_run_subscriber_verification()
    135      * for logic once they click the link in the email or enter the code on screen.
    136      *
    137      * @since   2.1.0
    138      */
    139     public function maybe_run_subscriber_authentication() {
    140 
    141         // Bail if no nonce was specified.
    142         if ( ! array_key_exists( '_wpnonce', $_REQUEST ) ) {
    143             return;
    144         }
    145 
    146         // Bail if the nonce failed validation.
    147         if ( ! wp_verify_nonce( sanitize_key( $_REQUEST['_wpnonce'] ), 'convertkit_restrict_content_login' ) ) {
    148             return;
    149         }
    150 
    151         // Bail if the expected email, resource ID or Post ID are missing.
    152         if ( ! array_key_exists( 'convertkit_email', $_REQUEST ) ) {
    153             return;
    154         }
    155         if ( ! array_key_exists( 'convertkit_resource_type', $_REQUEST ) ) {
    156             return;
    157         }
    158         if ( ! array_key_exists( 'convertkit_resource_id', $_REQUEST ) ) {
    159             return;
    160         }
    161         if ( ! array_key_exists( 'convertkit_post_id', $_REQUEST ) ) {
    162             return;
    163         }
    164 
    165         // If the Plugin Access Token has not been configured, we can't get this subscriber's ID by email.
    166         if ( ! $this->settings->has_access_and_refresh_token() ) {
    167             return;
    168         }
    169 
    170         // Initialize the API.
    171         $this->api = new ConvertKit_API_V4(
     122     * Register REST API routes.
     123     *
     124     * @since   3.1.0
     125     */
     126    public function register_routes() {
     127
     128        // Register route to run subscriber authentication.
     129        register_rest_route(
     130            'kit/v1',
     131            '/restrict-content/subscriber-authentication',
     132            array(
     133                'methods'             => WP_REST_Server::CREATABLE,
     134                'callback'            => function ( $request ) {
     135
     136                    // Initialize classes that will be used.
     137                    $output_restrict_content = WP_ConvertKit()->get_class( 'output_restrict_content' );
     138                    $output_restrict_content->initialize_classes();
     139
     140                    // Fetch Post ID, Resource Type and Resource ID for the view.
     141                    $email         = $request->get_param( 'convertkit_email' );
     142                    $post_id       = $request->get_param( 'convertkit_post_id' );
     143                    $resource_type = $request->get_param( 'convertkit_resource_type' );
     144                    $resource_id   = $request->get_param( 'convertkit_resource_id' );
     145
     146                    // Run subscriber authentication.
     147                    $result = $output_restrict_content->subscriber_authentication_send_code(
     148                        $email,
     149                        $post_id
     150                    );
     151
     152                    // If an error occured, build the email form view with the error message.
     153                    if ( is_wp_error( $result ) ) {
     154                        // Set error to display on screen.
     155                        $output_restrict_content->error = $result;
     156
     157                        // Build email form view to return for output with error message.
     158                        ob_start();
     159                        include CONVERTKIT_PLUGIN_PATH . '/views/frontend/restrict-content/login-modal-content-email.php';
     160                        $output = trim( ob_get_clean() );
     161                        return rest_ensure_response(
     162                            array(
     163                                'success' => false,
     164                                'data'    => $output,
     165                            )
     166                        );
     167                    }
     168
     169                    // Set token and Post ID for authentication code view.
     170                    $output_restrict_content->token = $result;
     171                    $output_restrict_content->post_id = $post_id;
     172
     173                    // Build authentication code view to return for output.
     174                    ob_start();
     175                    include CONVERTKIT_PLUGIN_PATH . '/views/frontend/restrict-content/login-modal-content-code.php';
     176                    $output = trim( ob_get_clean() );
     177                    return rest_ensure_response(
     178                        array(
     179                            'success' => true,
     180                            'data'    => $output,
     181                        )
     182                    );
     183                },
     184                'permission_callback' => '__return_true',
     185            )
     186        );
     187
     188        // Register route to run subscriber verification.
     189        register_rest_route(
     190            'kit/v1',
     191            '/restrict-content/subscriber-verification',
     192            array(
     193                'methods'             => WP_REST_Server::CREATABLE,
     194                'callback'            => function ( $request ) {
     195
     196                    // Initialize classes that will be used.
     197                    $output_restrict_content = WP_ConvertKit()->get_class( 'output_restrict_content' );
     198                    $output_restrict_content->initialize_classes();
     199
     200                    // Fetch Post ID, Resource Type and Resource ID for the view.
     201                    $post_id       = $request->get_param( 'convertkit_post_id' );
     202                    $token         = $request->get_param( 'token' );
     203                    $subscriber_code = $request->get_param( 'subscriber_code' );
     204
     205                    // Run subscriber authentication.
     206                    $result = $output_restrict_content->subscriber_authentication_verify( $post_id, $token, $subscriber_code );
     207
     208                    // If an error occured, build the code form view with the error message.
     209                    if ( is_wp_error( $result ) ) {
     210                        // Set error to display on screen.
     211                        $output_restrict_content->error = $result;
     212
     213                        // Set token and post ID for authentication code view.
     214                        $output_restrict_content->token = $token;
     215                        $output_restrict_content->post_id = $post_id;
     216
     217                        // Build code form view to return for output with error message.
     218                        ob_start();
     219                        include CONVERTKIT_PLUGIN_PATH . '/views/frontend/restrict-content/login-modal-content-code.php';
     220                        $output = trim( ob_get_clean() );
     221                        return rest_ensure_response(
     222                            array(
     223                                'success' => false,
     224                                'data'    => $output,
     225                            )
     226                        );
     227                    }
     228
     229                    // Return success with the URL to the Post, including the `ck-cache-bust` parameter.
     230                    return rest_ensure_response(
     231                        array(
     232                            'success' => true,
     233                            'url'     => $output_restrict_content->get_url( $post_id, true ),
     234                        )
     235                    );
     236                },
     237                'permission_callback' => '__return_true',
     238            )
     239        );
     240    }
     241
     242    /**
     243     * Initialize classes that will be used.
     244     *
     245     * @since   3.1.0
     246     */
     247    public function initialize_classes() {
     248
     249        $this->settings                  = new ConvertKit_Settings();
     250        $this->restrict_content_settings = new ConvertKit_Settings_Restrict_Content();
     251        $this->api                       = new ConvertKit_API_V4(
    172252            CONVERTKIT_OAUTH_CLIENT_ID,
    173253            CONVERTKIT_OAUTH_CLIENT_REDIRECT_URI,
     
    178258        );
    179259
     260    }
     261
     262    /**
     263     * If the user isn't using JavaScript, or the Plugin's Disable JS is enabled, checks if the request is a Restrict Content request with an email address.
     264     * Also runs if restrict content by tag and require login is disabled, as we immediately tag and redirect if this is the case.
     265     * If so, calls the API depending on the Restrict Content resource that's required:
     266     * - tag: subscribes the email address to the tag, storing the subscriber ID in a cookie and redirecting
     267     * - product: calls the API to send the subscriber a magic link by email containing a code. See maybe_run_subscriber_verification()
     268     * for logic once they click the link in the email or enter the code on screen.
     269     *
     270     * @since   2.1.0
     271     */
     272    public function maybe_run_subscriber_authentication() {
     273
     274        // Bail if no nonce was specified via form submission.
     275        if ( ! array_key_exists( '_wpnonce', $_REQUEST ) ) {
     276            return;
     277        }
     278
     279        // Bail if the request is a form submission and the nonce failed validation.
     280        if ( ! wp_verify_nonce( sanitize_key( $_REQUEST['_wpnonce'] ), 'convertkit_restrict_content_login' ) ) {
     281            return;
     282        }
     283
     284        // Bail if the expected email, resource type, resource ID or Post ID are missing from the request.
     285        if ( ! array_key_exists( 'convertkit_email', $_REQUEST ) ) {
     286            return;
     287        }
     288        if ( ! array_key_exists( 'convertkit_resource_type', $_REQUEST ) ) {
     289            return;
     290        }
     291        if ( ! array_key_exists( 'convertkit_resource_id', $_REQUEST ) ) {
     292            return;
     293        }
     294        if ( ! array_key_exists( 'convertkit_post_id', $_REQUEST ) ) {
     295            return;
     296        }
     297
     298        // If the Plugin Access Token has not been configured, we can't get this subscriber's ID by email.
     299        if ( ! $this->settings->has_access_and_refresh_token() ) {
     300            return;
     301        }
     302
    180303        // Sanitize inputs.
    181304        $email               = sanitize_text_field( wp_unslash( $_REQUEST['convertkit_email'] ) );
     
    184307        $this->post_id       = absint( $_REQUEST['convertkit_post_id'] );
    185308
    186         // Run subscriber authentication / subscription depending on the resource type.
    187         switch ( $this->resource_type ) {
    188             case 'product':
    189             case 'form':
    190                 // Send email to subscriber with a link to authenticate they have access to the email address submitted.
    191                 $result = $this->api->subscriber_authentication_send_code(
    192                     $email,
    193                     $this->get_url()
    194                 );
    195 
    196                 // Bail if an error occured.
    197                 if ( is_wp_error( $result ) ) {
    198                     $this->error = $result;
    199                     return;
    200                 }
    201 
     309        // If Restrict Content is by tag, tag the subscriber.
     310        if ( $this->resource_type === 'tag' ) {
     311            // Check reCAPTCHA.
     312            $recaptcha          = new ConvertKit_Recaptcha();
     313            $recaptcha_response = $recaptcha->verify_recaptcha(
     314                ( isset( $_POST['g-recaptcha-response'] ) ? sanitize_text_field( wp_unslash( $_POST['g-recaptcha-response'] ) ) : '' ),
     315                'convertkit_restrict_content_tag'
     316            );
     317
     318            // Bail if reCAPTCHA failed.
     319            if ( is_wp_error( $recaptcha_response ) ) {
     320                $this->error = $recaptcha_response;
     321                return;
     322            }
     323
     324            // Tag subscriber.
     325            $result = $this->api->tag_subscribe( $this->resource_id, $email );
     326
     327            // Bail if an error occured.
     328            if ( is_wp_error( $result ) ) {
     329                $this->error = $result;
     330                return;
     331            }
     332
     333            // If require login is disabled, return now.
     334            if ( ! $this->restrict_content_settings->require_tag_login() ) {
    202335                // Clear any existing subscriber ID cookie, as the authentication flow has started by sending the email.
    203336                $subscriber = new ConvertKit_Subscriber();
    204337                $subscriber->forget();
    205338
    206                 // Store the token so it's included in the subscriber code form.
    207                 $this->token = $result;
    208                 break;
    209 
    210             case 'tag':
    211                 // If require login is enabled, show the login screen.
    212                 if ( $this->restrict_content_settings->require_tag_login() ) {
    213                     // Tag the subscriber, unless this is an AJAX request.
    214                     if ( ! wp_doing_ajax() ) {
    215                         $result = $this->api->tag_subscribe( $this->resource_id, $email );
    216 
    217                         // Bail if an error occured.
    218                         if ( is_wp_error( $result ) ) {
    219                             $this->error = $result;
    220                             return;
    221                         }
    222                     }
    223 
    224                     // Send email to subscriber with a link to authenticate they have access to the email address submitted.
    225                     $result = $this->api->subscriber_authentication_send_code(
    226                         $email,
    227                         $this->get_url()
    228                     );
    229 
    230                     // Bail if an error occured.
    231                     if ( is_wp_error( $result ) ) {
    232                         $this->error = $result;
    233                         return;
    234                     }
    235 
    236                     // Clear any existing subscriber ID cookie, as the authentication flow has started by sending the email.
    237                     $subscriber = new ConvertKit_Subscriber();
    238                     $subscriber->forget();
    239 
    240                     // Store the token so it's included in the subscriber code form.
    241                     $this->token = $result;
    242                     break;
    243                 }
    244 
    245                 // If here, require login is disabled.
    246                 // Check reCAPTCHA, tag subscriber and assign subscriber ID integer to cookie
    247                 // without email link.
    248                 $recaptcha          = new ConvertKit_Recaptcha();
    249                 $recaptcha_response = $recaptcha->verify_recaptcha(
    250                     ( isset( $_POST['g-recaptcha-response'] ) ? sanitize_text_field( wp_unslash( $_POST['g-recaptcha-response'] ) ) : '' ),
    251                     'convertkit_restrict_content_tag'
    252                 );
    253 
    254                 // Bail if reCAPTCHA failed.
    255                 if ( is_wp_error( $recaptcha_response ) ) {
    256                     $this->error = $recaptcha_response;
    257                     return;
    258                 }
    259 
    260                 // Tag the subscriber.
    261                 $result = $this->api->tag_subscribe( $this->resource_id, $email );
    262 
    263                 // Bail if an error occured.
    264                 if ( is_wp_error( $result ) ) {
    265                     $this->error = $result;
    266                     return;
    267                 }
    268 
    269                 // Clear any existing subscriber ID cookie, as the authentication flow has started by sending the email.
    270                 $subscriber = new ConvertKit_Subscriber();
    271                 $subscriber->forget();
    272 
    273339                // Fetch the subscriber ID from the result.
    274340                $subscriber_id = $result['subscriber']['id'];
     
    277343                $this->store_subscriber_id_in_cookie( $subscriber_id );
    278344
    279                 // If this isn't an AJAX request, redirect now to reload the Post.
    280                 if ( ! wp_doing_ajax() ) {
    281                     $this->redirect();
    282                 }
    283                 break;
    284 
    285         }
    286 
    287     }
    288 
    289     /**
    290      * Checks if the request contains a token and subscriber_code i.e. the subscriber clicked
    291      * the link in the email sent by the maybe_run_subscriber_authentication() function above.
     345                // Redirect.
     346                $this->redirect( $this->post_id );
     347                return;
     348            }
     349        }
     350
     351        // If here, require login is enabled for tags or this is a product/form.
     352        // Run subscriber authentication.
     353        $result = $this->subscriber_authentication_send_code( $email, $this->post_id );
     354
     355        // Bail if an error occured.
     356        if ( is_wp_error( $result ) ) {
     357            $this->error = $result;
     358            return;
     359        }
     360
     361        // Store the token so it's included in the subscriber code form.
     362        $this->token = $result;
     363
     364    }
     365
     366    /**
     367     * If the user isn't using JavaScript, or the Plugin's Disable JS is enabled, checks if the request contains a token and subscriber_code,
     368     * which happens when the subscriber either:
     369     * - clicked the link in the email sent by run_subscriber_authentication(), or
     370     * - entered the code from the email on the screen
    292371     *
    293372     * This calls the API to verify the token and subscriber code, which tells us that the email
     
    309388        // If a nonce was specified, validate it now.
    310389        // It won't be provided if clicking the link in the magic link email.
    311         if ( array_key_exists( '_wpnonce', $_REQUEST ) ) {
     390        if ( array_key_exists( '_wpnonce', $_REQUEST ) && ! is_null( $_REQUEST['_wpnonce'] ) ) {
    312391            if ( ! wp_verify_nonce( sanitize_key( $_REQUEST['_wpnonce'] ), 'convertkit_restrict_content_subscriber_code' ) ) {
    313392                return;
     
    332411        }
    333412
    334         // Initialize the API.
    335         $this->api = new ConvertKit_API_V4(
    336             CONVERTKIT_OAUTH_CLIENT_ID,
    337             CONVERTKIT_OAUTH_CLIENT_REDIRECT_URI,
    338             $this->settings->get_access_token(),
    339             $this->settings->get_refresh_token(),
    340             $this->settings->debug_enabled(),
    341             'restrict_content'
    342         );
    343 
    344         // Verify the token and subscriber code.
    345         $subscriber_id = $this->api->subscriber_authentication_verify(
    346             sanitize_text_field( wp_unslash( $_REQUEST['token'] ) ),
    347             sanitize_text_field( wp_unslash( $_REQUEST['subscriber_code'] ) )
    348         );
     413        // Run subscriber verification.
     414        $subscriber_id = $this->subscriber_authentication_verify( $this->post_id, sanitize_text_field( wp_unslash( $_REQUEST['token'] ) ), sanitize_text_field( wp_unslash( $_REQUEST['subscriber_code'] ) ) );
    349415
    350416        // Bail if an error occured.
     
    354420        }
    355421
     422        // Redirect now to reload the Post.
     423        $this->redirect( $this->post_id );
     424
     425    }
     426
     427    /**
     428     * Sends an email to the subscriber with a code and link to authenticate they have access to the email address submitted.
     429     *
     430     * @since   3.1.0
     431     *
     432     * @param   string $email         Email address.
     433     * @param   int    $post_id       Post ID.
     434     *
     435     * @return WP_Error|string        Error or Token.
     436     */
     437    public function subscriber_authentication_send_code( $email, $post_id ) {
     438
     439        // Send email to subscriber with a link to authenticate they have access to the email address submitted.
     440        $token = $this->api->subscriber_authentication_send_code(
     441            $email,
     442            $this->get_url( $post_id )
     443        );
     444
     445        // Bail if an error occured.
     446        if ( is_wp_error( $token ) ) {
     447            return $token;
     448        }
     449
     450        // Clear any existing subscriber ID cookie, as the authentication flow has started by sending the email.
     451        $subscriber = new ConvertKit_Subscriber();
     452        $subscriber->forget();
     453
     454        // Return the token.
     455        return $token;
     456
     457    }
     458
     459    /**
     460     * Verifies the token and subscriber code, which tells us that the email
     461     * address supplied truly belongs to the user, and that we can safely
     462     * trust their subscriber ID to be valid.
     463     *
     464     * @since   3.1.0
     465     *
     466     * @param   int    $post_id         Post ID.
     467     * @param   string $token           Token.
     468     * @param   string $subscriber_code Subscriber code.
     469     *
     470     * @return WP_Error|string          Error or Signed Subscriber ID.
     471     */
     472    public function subscriber_authentication_verify( $post_id, $token, $subscriber_code ) {
     473
     474        // Verify the token and subscriber code.
     475        $subscriber_id = $this->api->subscriber_authentication_verify( $token, $subscriber_code );
     476
     477        // Bail if an error occured.
     478        if ( is_wp_error( $subscriber_id ) ) {
     479            return $subscriber_id;
     480        }
     481
    356482        // Store subscriber ID in cookie.
    357483        $this->store_subscriber_id_in_cookie( $subscriber_id );
    358484
    359         // If this isn't an AJAX request, redirect now to reload the Post.
    360         if ( ! wp_doing_ajax() ) {
    361             $this->redirect();
    362         }
     485        // Return signed subscriber ID.
     486        return $subscriber_id;
    363487
    364488    }
     
    513637
    514638        // Replace existing where statement with new statement.
    515         $where = 'WHERE ' . $new_where . ' ' . substr( $where, strpos( $where, 'AND' ) );
     639        $where = 'WHERE ' . $new_where . ' ' . substr( $where, strpos( $where, 'AND p.post_type = \'' . $post->post_type . '\' ' ) );
    516640
    517641        // Return.
     
    552676
    553677        // Replace existing where statement with new statement.
    554         $where = 'WHERE ' . $new_where . ' ' . substr( $where, strpos( $where, 'AND' ) );
     678        $where = 'WHERE ' . $new_where . ' ' . substr( $where, strpos( $where, 'AND p.post_type = \'' . $post->post_type . '\' ' ) );
    555679
    556680        // Return.
     
    612736     *
    613737     * @since   2.3.7
    614      */
    615     private function redirect() {
     738     *
     739     * @param   int $post_id      Post ID.
     740     */
     741    private function redirect( $post_id ) {
    616742
    617743        // Redirect to the Post, appending a query parameter to the URL to prevent caching plugins and
     
    619745        // result in maybe_restrict_content() not showing an error message or permitting
    620746        // access to the content.
    621         wp_safe_redirect( $this->get_url( true ) );
     747        wp_safe_redirect( $this->get_url( $post_id, true ) );
    622748        exit;
    623749
     
    629755     * @since   2.1.0
    630756     *
     757     * @param   int  $post_id        Post ID.
    631758     * @param   bool $cache_bust     Include `ck-cache-bust` parameter in URL.
    632      * @return  string  URL.
    633      */
    634     public function get_url( $cache_bust = false ) {
     759     * @return  string                 URL.
     760     */
     761    public function get_url( $post_id, $cache_bust = false ) {
    635762
    636763        // Get URL of Post.
    637         $url = get_permalink( $this->post_id );
     764        $url = get_permalink( $post_id );
    638765
    639766        // If no cache busting required, return the URL now.
     
    867994    private function subscriber_has_access( $subscriber_id ) { // phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter
    868995
    869         // Initialize the API.
    870         $this->api = new ConvertKit_API_V4(
    871             CONVERTKIT_OAUTH_CLIENT_ID,
    872             CONVERTKIT_OAUTH_CLIENT_REDIRECT_URI,
    873             $this->settings->get_access_token(),
    874             $this->settings->get_refresh_token(),
    875             $this->settings->debug_enabled(),
    876             'restrict_content'
    877         );
    878 
    879996        // Depending on the resource type, determine if the subscriber has access to it.
    880997        // This is deliberately a switch statement, because we will likely add in support
     
    12151332                'convertkit_restrict_content',
    12161333                array(
    1217                     'ajaxurl' => admin_url( 'admin-ajax.php' ),
    1218                     'debug'   => $this->settings->debug_enabled(),
     1334                    'nonce'                         => wp_create_nonce( 'wp_rest' ),
     1335                    'subscriber_authentication_url' => rest_url( 'kit/v1/restrict-content/subscriber-authentication' ),
     1336                    'subscriber_verification_url'   => rest_url( 'kit/v1/restrict-content/subscriber-verification' ),
     1337                    'debug'                         => $this->settings->debug_enabled(),
    12191338                )
    12201339            );
  • convertkit/trunk/includes/class-convertkit-output.php

    r3357832 r3399438  
    813813        }
    814814
     815        // Determine if the Non-inline Form Limit per Session setting is enabled.
     816        $limit_per_session = $this->settings->non_inline_form_limit_per_session();
     817
    815818        // Get form.
    816819        $convertkit_forms = new ConvertKit_Resource_Forms();
     
    829832            add_filter(
    830833                'convertkit_output_scripts_footer',
    831                 function ( $scripts ) use ( $form ) {
     834                function ( $scripts ) use ( $form, $limit_per_session ) {
    832835
    833836                    $scripts[] = array(
     
    835838                        'data-uid'                   => $form['uid'],
    836839                        'src'                        => $form['embed_js'],
    837                         'data-kit-limit-per-session' => true,
     840                        'data-kit-limit-per-session' => $limit_per_session ? '1' : '0',
    838841                    );
    839842
  • convertkit/trunk/includes/class-convertkit-resource-forms.php

    r3357832 r3399438  
    545545                        'data-uid'                   => $this->resources[ $id ]['uid'],
    546546                        'src'                        => $this->resources[ $id ]['embed_js'],
    547                         'data-kit-limit-per-session' => $settings->non_inline_form_limit_per_session(),
     547                        'data-kit-limit-per-session' => $settings->non_inline_form_limit_per_session() ? '1' : '0',
    548548                    );
    549549
  • convertkit/trunk/includes/class-convertkit-settings.php

    r3376520 r3399438  
    4949        add_action( 'convertkit_api_get_access_token', array( $this, 'update_credentials' ), 10, 2 );
    5050        add_action( 'convertkit_api_refresh_token', array( $this, 'update_credentials' ), 10, 2 );
     51
     52        // Delete credentials if the API class uses a invalid access token.
     53        // This prevents the Plugin making repetitive API requests that will 401.
     54        add_action( 'convertkit_api_access_token_invalid', array( $this, 'maybe_delete_credentials' ), 10, 2 );
    5155
    5256    }
     
    630634        }
    631635
     636        // Remove any existing persistent notice.
     637        WP_ConvertKit()->get_class( 'admin_notices' )->delete( 'authorization_failed' );
     638
    632639        $this->save(
    633640            array(
     
    647654
    648655    /**
    649      * Deletes any existing access token, refresh token and its expiry from the Plugin settings.
     656     * Deletes the stored access token, refresh token and its expiry from the Plugin settings,
     657     * and clears any existing scheduled WordPress Cron event to refresh the token on expiry,
     658     * when either:
     659     * - The access token is invalid
     660     * - The access token expired, and refreshing failed
     661     *
     662     * @since   3.1.0
     663     *
     664     * @param   WP_Error $result      Error result.
     665     * @param   string   $client_id   OAuth Client ID used for the Access and Refresh Tokens.
     666     */
     667    public function maybe_delete_credentials( $result, $client_id ) {
     668
     669        // Don't delete these credentials if they're not for this Client ID.
     670        // They're for another Kit Plugin that uses OAuth.
     671        if ( $client_id !== CONVERTKIT_OAUTH_CLIENT_ID ) {
     672            return;
     673        }
     674
     675        // Persist an error notice in the WordPress Administration until the user fixes the problem.
     676        WP_ConvertKit()->get_class( 'admin_notices' )->add( 'authorization_failed' );
     677
     678        // Delete the credentials from the Plugin settings.
     679        $this->delete_credentials();
     680
     681    }
     682
     683    /**
     684     * Deletes any existing access token, refresh token and its expiry from the Plugin settings,
     685     * and clears any existing scheduled WordPress Cron event to refresh the token on expiry.
    650686     *
    651687     * @since   2.5.0
     
    661697        );
    662698
     699        // Clear any existing scheduled WordPress Cron event.
     700        wp_clear_scheduled_hook( 'convertkit_refresh_token' );
     701
    663702    }
    664703
  • convertkit/trunk/includes/class-wp-convertkit.php

    r3357832 r3399438  
    8484        $this->classes['admin_category']                      = new ConvertKit_Admin_Category();
    8585        $this->classes['admin_landing_page']                  = new ConvertKit_Admin_Landing_Page();
    86         $this->classes['admin_notices']                       = new ConvertKit_Admin_Notices();
    8786        $this->classes['admin_post']                          = new ConvertKit_Admin_Post();
    8887        $this->classes['admin_quick_edit']                    = new ConvertKit_Admin_Quick_Edit();
    89         $this->classes['admin_refresh_resources']             = new ConvertKit_Admin_Refresh_Resources();
    9088        $this->classes['admin_restrict_content']              = new ConvertKit_Admin_Restrict_Content();
    9189        $this->classes['admin_settings']                      = new ConvertKit_Admin_Settings();
     
    179177    private function initialize_global() {
    180178
     179        $this->classes['admin_notices']                               = new ConvertKit_Admin_Notices();
     180        $this->classes['admin_refresh_resources']                     = new ConvertKit_Admin_Refresh_Resources();
    181181        $this->classes['ajax']                                        = new ConvertKit_AJAX();
    182182        $this->classes['blocks_convertkit_broadcasts']                = new ConvertKit_Block_Broadcasts();
  • convertkit/trunk/languages/convertkit.pot

    r3390429 r3399438  
    33msgid ""
    44msgstr ""
    5 "Project-Id-Version: Kit (formerly ConvertKit) 3.0.8\n"
     5"Project-Id-Version: Kit (formerly ConvertKit) 3.1.0\n"
    66"Report-Msgid-Bugs-To: https://wordpress.org/support/plugin/convertkit\n"
    77"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
     
    1010"Content-Type: text/plain; charset=UTF-8\n"
    1111"Content-Transfer-Encoding: 8bit\n"
    12 "POT-Creation-Date: 2025-11-05T03:11:47+00:00\n"
     12"POT-Creation-Date: 2025-11-20T04:42:45+00:00\n"
    1313"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
    1414"X-Generator: WP-CLI 2.12.0\n"
     
    8888msgstr ""
    8989
    90 #: admin/class-convertkit-admin-notices.php:69
    91 msgid "Kit: Authorization failed. Please"
    92 msgstr ""
    93 
    94 #: admin/class-convertkit-admin-notices.php:73
    95 #: views/backend/post/no-api-key.php:19
    96 msgid "connect your Kit account."
    97 msgstr ""
    98 
    9990#: admin/class-convertkit-admin-post.php:93
    10091msgid "Add New"
     
    276267
    277268#: admin/section/class-convertkit-admin-section-broadcasts.php:501
    278 #: admin/section/class-convertkit-admin-section-general.php:689
     269#: admin/section/class-convertkit-admin-section-general.php:673
    279270#: views/backend/post/bulk-edit.php:30
    280271#: views/backend/post/bulk-edit.php:53
     
    396387msgstr ""
    397388
    398 #: admin/section/class-convertkit-admin-section-general.php:295
     389#: admin/section/class-convertkit-admin-section-general.php:279
    399390msgid "Account Name"
    400391msgstr ""
    401392
    402393#. translators: Post Type Name, plural
    403 #: admin/section/class-convertkit-admin-section-general.php:316
     394#: admin/section/class-convertkit-admin-section-general.php:300
    404395#, php-format
    405396msgid "Default Form (%s)"
     
    407398
    408399#. translators: Post Type Name, plural
    409 #: admin/section/class-convertkit-admin-section-general.php:334
     400#: admin/section/class-convertkit-admin-section-general.php:318
    410401#, php-format
    411402msgid "Form Position (%s)"
    412403msgstr ""
    413404
    414 #: admin/section/class-convertkit-admin-section-general.php:365
     405#: admin/section/class-convertkit-admin-section-general.php:349
    415406msgid "Default Forms (Site Wide)"
    416407msgstr ""
    417408
    418 #: admin/section/class-convertkit-admin-section-general.php:376
     409#: admin/section/class-convertkit-admin-section-general.php:360
    419410msgid "Behavior"
    420411msgstr ""
    421412
    422 #: admin/section/class-convertkit-admin-section-general.php:388
     413#: admin/section/class-convertkit-admin-section-general.php:372
    423414msgid "Limit Display"
    424415msgstr ""
    425416
    426 #: admin/section/class-convertkit-admin-section-general.php:400
     417#: admin/section/class-convertkit-admin-section-general.php:384
    427418msgid "reCAPTCHA: Site Key"
    428419msgstr ""
    429420
    430 #: admin/section/class-convertkit-admin-section-general.php:407
     421#: admin/section/class-convertkit-admin-section-general.php:391
    431422msgid "Enter your Google reCAPTCHA v3 Site Key. When specified, this will be used to reduce spam signups."
    432423msgstr ""
    433424
    434 #: admin/section/class-convertkit-admin-section-general.php:413
     425#: admin/section/class-convertkit-admin-section-general.php:397
    435426msgid "reCAPTCHA: Secret Key"
    436427msgstr ""
    437428
     429#: admin/section/class-convertkit-admin-section-general.php:404
     430msgid "Enter your Google reCAPTCHA v3 Secret Key. When specified, this will be used to reduce spam signups."
     431msgstr ""
     432
     433#: admin/section/class-convertkit-admin-section-general.php:410
     434msgid "reCAPTCHA: Minimum Score"
     435msgstr ""
     436
    438437#: admin/section/class-convertkit-admin-section-general.php:420
    439 msgid "Enter your Google reCAPTCHA v3 Secret Key. When specified, this will be used to reduce spam signups."
    440 msgstr ""
    441 
    442 #: admin/section/class-convertkit-admin-section-general.php:426
    443 msgid "reCAPTCHA: Minimum Score"
    444 msgstr ""
    445 
    446 #: admin/section/class-convertkit-admin-section-general.php:436
    447438msgid "Enter the minimum threshold for a subscriber to pass Google reCAPTCHA. A higher number will reduce spam signups (1.0 is very likely a good interaction, 0.0 is very likely a bot)."
    448439msgstr ""
    449440
    450 #: admin/section/class-convertkit-admin-section-general.php:444
     441#: admin/section/class-convertkit-admin-section-general.php:428
    451442msgid "Debug"
    452443msgstr ""
    453444
    454 #: admin/section/class-convertkit-admin-section-general.php:455
     445#: admin/section/class-convertkit-admin-section-general.php:439
    455446msgid "Disable JavaScript"
    456447msgstr ""
    457448
    458 #: admin/section/class-convertkit-admin-section-general.php:466
     449#: admin/section/class-convertkit-admin-section-general.php:450
    459450msgid "Disable CSS"
    460451msgstr ""
    461452
    462 #: admin/section/class-convertkit-admin-section-general.php:477
     453#: admin/section/class-convertkit-admin-section-general.php:461
    463454msgid "Usage Tracking"
    464455msgstr ""
    465456
    466 #: admin/section/class-convertkit-admin-section-general.php:494
     457#: admin/section/class-convertkit-admin-section-general.php:478
    467458msgid "Forms can be embedded before and/or after (or following number of elements) on every post or page (in single view only) across your site by using the settings below."
    468459msgstr ""
    469460
    470 #: admin/section/class-convertkit-admin-section-general.php:495
     461#: admin/section/class-convertkit-admin-section-general.php:479
    471462msgid "You can also configure non-inline forms to display site wide under the \"Non-inline Forms\" section below."
    472463msgstr ""
    473464
    474 #: admin/section/class-convertkit-admin-section-general.php:496
     465#: admin/section/class-convertkit-admin-section-general.php:480
    475466msgid "These default form settings can be overridden using the Kit meta box on a page or post's edit screen. For site wide non-inline forms to respect when a post/page has forms disabled, enable the \"Behavior\" option in the \"Non-inline Forms\" section below."
    476467msgstr ""
    477468
    478469#. translators: [convertkit] shortcode, wrapped in <code> tags
    479 #: admin/section/class-convertkit-admin-section-general.php:501
     470#: admin/section/class-convertkit-admin-section-general.php:485
    480471#, php-format
    481472msgid "The default form can be inserted into the middle of post or page content by using either the %s shortcode or block."
    482473msgstr ""
    483474
    484 #: admin/section/class-convertkit-admin-section-general.php:518
     475#: admin/section/class-convertkit-admin-section-general.php:502
    485476msgid "Defines non-inline forms to display site wide, and if these forms should display on Pages / Posts that have the Kit Form setting = None."
    486477msgstr ""
    487478
    488 #: admin/section/class-convertkit-admin-section-general.php:530
     479#: admin/section/class-convertkit-admin-section-general.php:514
    489480msgid "Configure reCAPTCHA to protect the Member Content signup form and Form Builder block from spam and abuse."
    490481msgstr ""
    491482
    492 #: admin/section/class-convertkit-admin-section-general.php:542
     483#: admin/section/class-convertkit-admin-section-general.php:526
    493484msgid "Defines advanced configuration settings, usually when working with support or needing to disable JS or CSS."
    494485msgstr ""
    495486
    496 #: admin/section/class-convertkit-admin-section-general.php:571
     487#: admin/section/class-convertkit-admin-section-general.php:555
    497488msgid "(Not specified)"
    498489msgstr ""
    499490
    500 #: admin/section/class-convertkit-admin-section-general.php:586
     491#: admin/section/class-convertkit-admin-section-general.php:570
    501492msgid "Disconnect"
    502493msgstr ""
    503494
    504 #: admin/section/class-convertkit-admin-section-general.php:649
     495#: admin/section/class-convertkit-admin-section-general.php:633
    505496msgid "No Forms exist in Kit."
    506497msgstr ""
    507498
     499#: admin/section/class-convertkit-admin-section-general.php:634
     500msgid "Click here to create your first form"
     501msgstr ""
     502
     503#. translators: Post Type name, plural
     504#: admin/section/class-convertkit-admin-section-general.php:647
     505#: admin/section/class-convertkit-admin-section-general.php:657
     506#, php-format
     507msgid "Select a form above to automatically output below all %s."
     508msgstr ""
     509
    508510#: admin/section/class-convertkit-admin-section-general.php:650
    509 msgid "Click here to create your first form"
    510 msgstr ""
    511 
    512 #. translators: Post Type name, plural
    513 #: admin/section/class-convertkit-admin-section-general.php:663
    514 #: admin/section/class-convertkit-admin-section-general.php:673
    515 #, php-format
    516 msgid "Select a form above to automatically output below all %s."
    517 msgstr ""
    518 
    519 #: admin/section/class-convertkit-admin-section-general.php:666
    520 #: admin/section/class-convertkit-admin-section-general.php:803
     511#: admin/section/class-convertkit-admin-section-general.php:787
    521512#: includes/class-convertkit-broadcasts-exporter.php:150
    522513#: views/backend/setup-wizard/convertkit-setup/content-2.php:79
     
    525516msgstr ""
    526517
    527 #: admin/section/class-convertkit-admin-section-general.php:667
    528 #: admin/section/class-convertkit-admin-section-general.php:804
     518#: admin/section/class-convertkit-admin-section-general.php:651
     519#: admin/section/class-convertkit-admin-section-general.php:788
    529520msgid "to preview how this will display."
    530521msgstr ""
    531522
    532523#. translators: Post type singular name
    533 #: admin/section/class-convertkit-admin-section-general.php:716
     524#: admin/section/class-convertkit-admin-section-general.php:700
    534525#, php-format
    535526msgid "Before %s content"
     
    537528
    538529#. translators: Post type singular name
    539 #: admin/section/class-convertkit-admin-section-general.php:721
     530#: admin/section/class-convertkit-admin-section-general.php:705
    540531#, php-format
    541532msgid "After %s content"
     
    543534
    544535#. translators: Post type singular name
    545 #: admin/section/class-convertkit-admin-section-general.php:726
     536#: admin/section/class-convertkit-admin-section-general.php:710
    546537#, php-format
    547538msgid "Before and after %s content"
    548539msgstr ""
    549540
    550 #: admin/section/class-convertkit-admin-section-general.php:729
     541#: admin/section/class-convertkit-admin-section-general.php:713
    551542msgid "After element"
    552543msgstr ""
    553544
    554545#. translators: Post Type name, plural
    555 #: admin/section/class-convertkit-admin-section-general.php:733
     546#: admin/section/class-convertkit-admin-section-general.php:717
    556547#, php-format
    557548msgid "Where forms should display relative to the %s content"
    558549msgstr ""
    559550
    560 #: admin/section/class-convertkit-admin-section-general.php:768
     551#: admin/section/class-convertkit-admin-section-general.php:752
    561552msgid "Paragraphs"
    562553msgstr ""
    563554
    564 #: admin/section/class-convertkit-admin-section-general.php:769
     555#: admin/section/class-convertkit-admin-section-general.php:753
    565556msgid "Headings <h2>"
    566557msgstr ""
    567558
    568 #: admin/section/class-convertkit-admin-section-general.php:770
     559#: admin/section/class-convertkit-admin-section-general.php:754
    569560msgid "Headings <h3>"
    570561msgstr ""
    571562
    572 #: admin/section/class-convertkit-admin-section-general.php:771
     563#: admin/section/class-convertkit-admin-section-general.php:755
    573564msgid "Headings <h4>"
    574565msgstr ""
    575566
    576 #: admin/section/class-convertkit-admin-section-general.php:772
     567#: admin/section/class-convertkit-admin-section-general.php:756
    577568msgid "Headings <h5>"
    578569msgstr ""
    579570
    580 #: admin/section/class-convertkit-admin-section-general.php:773
     571#: admin/section/class-convertkit-admin-section-general.php:757
    581572msgid "Headings <h6>"
    582573msgstr ""
    583574
    584 #: admin/section/class-convertkit-admin-section-general.php:774
     575#: admin/section/class-convertkit-admin-section-general.php:758
    585576msgid "Images"
    586577msgstr ""
    587578
    588 #: admin/section/class-convertkit-admin-section-general.php:776
     579#: admin/section/class-convertkit-admin-section-general.php:760
    589580msgid "The number of elements before outputting the form."
    590581msgstr ""
    591582
    592 #: admin/section/class-convertkit-admin-section-general.php:793
     583#: admin/section/class-convertkit-admin-section-general.php:777
    593584msgid "No non-inline Forms exist in Kit."
    594585msgstr ""
    595586
    596 #: admin/section/class-convertkit-admin-section-general.php:794
     587#: admin/section/class-convertkit-admin-section-general.php:778
    597588msgid "Click here to create your first modal, slide in or sticky bar form"
    598589msgstr ""
    599590
    600 #: admin/section/class-convertkit-admin-section-general.php:802
     591#: admin/section/class-convertkit-admin-section-general.php:786
    601592msgid "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."
    602593msgstr ""
    603594
    604 #: admin/section/class-convertkit-admin-section-general.php:840
     595#: admin/section/class-convertkit-admin-section-general.php:824
    605596msgid "If checked, do not display the site wide form(s) above on Pages / Posts that have their Kit Form setting = None."
    606597msgstr ""
    607598
    608 #: admin/section/class-convertkit-admin-section-general.php:860
     599#: admin/section/class-convertkit-admin-section-general.php:844
    609600msgid "If checked, one non-inline form will be displayed per session. This applies to all non-inline forms defined on this screen, Page / Post / Category settings, and any Form blocks or shortcodes specifying a non-inline form."
    610601msgstr ""
    611602
    612 #: admin/section/class-convertkit-admin-section-general.php:943
     603#: admin/section/class-convertkit-admin-section-general.php:927
    613604msgid "Log requests to file and output browser console messages."
    614605msgstr ""
    615606
    616 #: admin/section/class-convertkit-admin-section-general.php:944
     607#: admin/section/class-convertkit-admin-section-general.php:928
    617608msgid "You can ignore this unless you're working with our support team to resolve an issue. Decheck this option to improve performance."
    618609msgstr ""
    619610
    620 #: admin/section/class-convertkit-admin-section-general.php:961
     611#: admin/section/class-convertkit-admin-section-general.php:945
    621612msgid "Prevent plugin JavaScript files loading on the frontend site. This will disable the custom content and tagging features of the plugin. Does not apply to embedding forms or landing pages. Use with caution!"
    622613msgstr ""
    623614
    624 #: admin/section/class-convertkit-admin-section-general.php:978
     615#: admin/section/class-convertkit-admin-section-general.php:962
    625616msgid "Prevents loading plugin CSS files. This will disable styling on broadcasts, form trigger buttons, product buttons and member's content. Use with caution!"
    626617msgstr ""
    627618
    628 #: admin/section/class-convertkit-admin-section-general.php:982
     619#: admin/section/class-convertkit-admin-section-general.php:966
    629620msgid "To customize forms and their styling, use the"
    630621msgstr ""
    631622
    632 #: admin/section/class-convertkit-admin-section-general.php:984
     623#: admin/section/class-convertkit-admin-section-general.php:968
    633624msgid "Kit form editor"
    634625msgstr ""
    635626
    636 #: admin/section/class-convertkit-admin-section-general.php:986
     627#: admin/section/class-convertkit-admin-section-general.php:970
    637628msgid "For creators who require form designs to follow their WordPress theme, use the Kit Form Builder block in the block editor."
    638629msgstr ""
    639630
    640 #: admin/section/class-convertkit-admin-section-general.php:989
     631#: admin/section/class-convertkit-admin-section-general.php:973
    641632msgid "For developers who require custom form designs through use of CSS, consider using the"
    642633msgstr ""
    643634
    644 #: admin/section/class-convertkit-admin-section-general.php:990
     635#: admin/section/class-convertkit-admin-section-general.php:974
    645636msgid "or"
    646637msgstr ""
    647638
    648 #: admin/section/class-convertkit-admin-section-general.php:991
     639#: admin/section/class-convertkit-admin-section-general.php:975
    649640msgid "integrations."
    650641msgstr ""
    651642
    652 #: admin/section/class-convertkit-admin-section-general.php:1010
     643#: admin/section/class-convertkit-admin-section-general.php:994
    653644msgid "By allowing us to collect usage data, we can better understand which WordPress configurations, themes and plugins we should test."
    654645msgstr ""
    655646
    656 #: admin/section/class-convertkit-admin-section-general.php:1014
     647#: admin/section/class-convertkit-admin-section-general.php:998
    657648msgid "Complete documentation on usage tracking can be found"
    658649msgstr ""
    659650
    660 #: admin/section/class-convertkit-admin-section-general.php:1016
     651#: admin/section/class-convertkit-admin-section-general.php:1000
    661652#: views/backend/setup-wizard/convertkit-setup/content-2.php:150
    662653msgid "here"
     
    844835msgstr ""
    845836
    846 #: admin/section/class-convertkit-admin-section-tools.php:353
     837#: admin/section/class-convertkit-admin-section-tools.php:60
     838msgid "MC4WP forms migrated successfully."
     839msgstr ""
     840
     841#: admin/section/class-convertkit-admin-section-tools.php:396
    847842msgid "Tools to help you manage Kit on your site."
    848843msgstr ""
    849844
    850 #: admin/section/class-convertkit-admin-section-tools.php:381
     845#: admin/section/class-convertkit-admin-section-tools.php:424
    851846msgid "WordPress 5.2 or higher is required for system information report."
    852847msgstr ""
     
    14181413
    14191414#: includes/blocks/class-convertkit-block-form.php:106
     1415#: views/backend/settings/tools.php:119
    14201416#: views/backend/term/fields-add.php:11
    14211417#: views/backend/term/fields-edit.php:12
     
    15131509msgstr ""
    15141510
    1515 #: includes/class-convertkit-ajax.php:84
    1516 msgid "Kit: Required parameter `subscriber_id` not included in AJAX request."
    1517 msgstr ""
    1518 
    1519 #: includes/class-convertkit-ajax.php:90
    1520 msgid "Kit: Required parameter `subscriber_id` empty in AJAX request."
    1521 msgstr ""
    1522 
    1523 #: includes/class-convertkit-ajax.php:126
     1511#: includes/class-convertkit-admin-notices.php:69
     1512msgid "Kit: Authorization failed. Please"
     1513msgstr ""
     1514
     1515#: includes/class-convertkit-admin-notices.php:73
     1516#: views/backend/post/no-api-key.php:19
     1517msgid "connect your Kit account."
     1518msgstr ""
     1519
     1520#: includes/class-convertkit-ajax.php:43
    15241521msgid "Kit: Required parameter `email` not included in AJAX request."
    15251522msgstr ""
    15261523
    1527 #: includes/class-convertkit-ajax.php:132
     1524#: includes/class-convertkit-ajax.php:49
    15281525msgid "Kit: Required parameter `email` is empty."
    15291526msgstr ""
    15301527
    1531 #: includes/class-convertkit-ajax.php:137
     1528#: includes/class-convertkit-ajax.php:54
    15321529msgid "Kit: Required parameter `email` is not an email address."
    15331530msgstr ""
     
    21082105#: views/backend/settings/tools.php:95
    21092106msgid "Import"
     2107msgstr ""
     2108
     2109#: views/backend/settings/tools.php:109
     2110msgid "MC4WP: Migrate Configuration"
     2111msgstr ""
     2112
     2113#: views/backend/settings/tools.php:112
     2114msgid "Automatically replace MC4WP form shortcodes with Kit forms."
     2115msgstr ""
     2116
     2117#: views/backend/settings/tools.php:118
     2118msgid "MC4WP Form"
     2119msgstr ""
     2120
     2121#: views/backend/settings/tools.php:149
     2122msgid "Migrate"
    21102123msgstr ""
    21112124
  • convertkit/trunk/readme.txt

    r3390429 r3399438  
    66Tested up to: 6.8
    77Requires PHP: 7.1
    8 Stable tag: 3.0.8
     8Stable tag: 3.1.0
    99License: GPLv3 or later
    1010License URI: https://www.gnu.org/licenses/gpl-3.0.html
     
    180180== Changelog ==
    181181
     182### 3.1.0 2025-11-20
     183* Added: Settings: Tools: Mailchimp for WordPress to Kit Form Importer
     184* Fix: Settings: Non-inline Forms: Correctly output `data-kit-limit-per-session`
     185* Fix: Settings: Automatically delete invalid Access Tokens
     186* Fix: Member Content: Courses: Next/Previous Links compatibility for WordPress 6.9
     187* Fix: Landing Pages: Prevent LazyLoad Plugin from lazy loading images on Landing Pages, which would result in missing images
     188* Updated: Blocks: Refresh Resources: Use REST API, replacing admin-ajax.php
     189* Updated: Refresh Resources: Use REST API, replacing admin-ajax.php
     190* Updated: Member Content: Use REST API, replacing admin-ajax.php
     191* Updated: Use WordPress Libraries 2.1.1
     192* Removed: Unused `convertkit_store_subscriber_id_in_cookie` AJAX function
     193
    182194### 3.0.8 2025-11-05
    183195* Fix: Member Content: Product: Display 'no access' notice when logged in and no access
  • convertkit/trunk/resources/backend/js/gutenberg.js

    r3390429 r3399438  
    747747         */
    748748        const refreshBlocksDefinitions = function (props, setButtonDisabled) {
    749             // Define data for WordPress AJAX request.
    750             const data = new FormData();
    751             data.append('action', 'convertkit_get_blocks');
    752             data.append('nonce', convertkit_gutenberg.get_blocks_nonce);
    753 
    754749            // Disable the button.
    755750            if (typeof setButtonDisabled !== 'undefined') {
     
    758753
    759754            // Send AJAX request.
    760             fetch(ajaxurl, {
    761                 method: 'POST',
    762                 credentials: 'same-origin',
    763                 body: data,
     755            fetch(convertkit_gutenberg.ajaxurl, {
     756                method: 'GET',
     757                headers: {
     758                    'Content-Type': 'application/json',
     759                    'X-WP-Nonce': convertkit_gutenberg.get_blocks_nonce,
     760                },
    764761            })
    765762                .then(function (response) {
     
    768765                })
    769766                .then(function (response) {
     767                    // If the response includes a code, show an error notice.
     768                    if (typeof response.code !== 'undefined') {
     769                        // Show an error in the Gutenberg editor.
     770                        wp.data
     771                            .dispatch('core/notices')
     772                            .createErrorNotice('Kit: ' + response.message, {
     773                                id: 'convertkit-error',
     774                            });
     775
     776                        // Enable refresh button.
     777                        if (typeof setButtonDisabled !== 'undefined') {
     778                            setButtonDisabled(false);
     779                        }
     780                        return;
     781                    }
     782
    770783                    // Update global ConvertKit Blocks object, so that any updated resources
    771784                    // are reflected when adding new ConvertKit Blocks.
    772                     convertkit_blocks = response.data;
     785                    convertkit_blocks = response;
    773786
    774787                    // Update this block's properties, so that has_access_token, has_resources
     
    791804                    wp.data
    792805                        .dispatch('core/notices')
    793                         .createErrorNotice('ConvertKit: ' + error, {
     806                        .createErrorNotice('Kit: ' + error, {
    794807                            id: 'convertkit-error',
    795808                        });
    796809
    797810                    // Enable refresh button.
    798                     setButtonDisabled(false);
     811                    if (typeof setButtonDisabled !== 'undefined') {
     812                        setButtonDisabled(false);
     813                    }
    799814                });
    800815        };
  • convertkit/trunk/resources/backend/js/refresh-resources.js

    r3390429 r3399438  
    5050
    5151    // Perform AJAX request to refresh resource.
    52     fetch(convertkit_admin_refresh_resources.ajaxurl, {
     52    fetch(convertkit_admin_refresh_resources.ajaxurl + resource, {
    5353        method: 'POST',
    5454        headers: {
    55             'Content-Type': 'application/x-www-form-urlencoded',
     55            'Content-Type': 'application/json',
     56            'X-WP-Nonce': convertkit_admin_refresh_resources.nonce,
    5657        },
    57         body: new URLSearchParams({
    58             action: 'convertkit_admin_refresh_resources',
    59             nonce: convertkit_admin_refresh_resources.nonce,
    60             resource, // e.g. forms, landing_pages, tags.
    61         }),
    6258    })
    6359        .then(function (response) {
     
    7167
    7268            // Show an error if the request wasn't successful.
    73             if (!response.success) {
     69            if (typeof response.code !== 'undefined') {
    7470                // Show error notice.
    75                 convertKitRefreshResourcesOutputErrorNotice(response.data);
     71                convertKitRefreshResourcesOutputErrorNotice(response.message);
    7672
    7773                // Enable button and remove is-refreshing class.
     
    10399                    // Populate select `optgroup`` from response data, which comprises of Tags and Products.
    104100                    // Tags.
    105                     response.data.tags.forEach(function (item) {
     101                    response.tags.forEach(function (item) {
    106102                        document
    107103                            .querySelector(
     
    119115
    120116                    // Products.
    121                     response.data.products.forEach(function (item) {
     117                    response.products.forEach(function (item) {
    122118                        document
    123119                            .querySelector(
     
    137133                default:
    138134                    // Populate select options from response data.
    139                     response.data.forEach(function (item) {
     135                    response.forEach(function (item) {
    140136                        // Define label.
    141137                        let label = '';
  • convertkit/trunk/resources/frontend/js/convertkit.js

    r3369648 r3399438  
    213213    // Set a cookie if any scripts with data-kit-limit-per-session attribute exist.
    214214    if (
    215         document.querySelectorAll('script[data-kit-limit-per-session]').length >
    216         0
     215        document.querySelectorAll('script[data-kit-limit-per-session="1"]')
     216            .length > 0
    217217    ) {
    218218        document.cookie = 'ck_non_inline_form_displayed=1; path=/';
  • convertkit/trunk/resources/frontend/js/restrict-content.js

    r3369648 r3399438  
    7878        // Code submission.
    7979        convertKitRestrictContentSubscriberVerification(
    80             e.target.querySelector('input[name="_wpnonce"]').value,
     80            convertkit_restrict_content.nonce,
    8181            e.target.querySelector('input[name="subscriber_code"]').value,
    8282            e.target.querySelector('input[name="token"]').value,
     
    8989    // Email submission.
    9090    convertKitRestrictContentSubscriberAuthenticationSendCode(
    91         e.target.querySelector('input[name="_wpnonce"]').value,
     91        convertkit_restrict_content.nonce,
    9292        e.target.querySelector('input[name="convertkit_email"]').value,
    9393        e.target.querySelector('input[name="convertkit_resource_type"]').value,
     
    128128
    129129/**
    130  * Submits the given email address to maybe_run_subscriber_authentication(), which
    131  * will return either:
     130 * Submits the given email address to the WP REST API kit/v1/restrict-content/subscriber-authentication
     131 * endpoint, which will return either:
    132132 * - the email form view, with an error message e.g. invalid email,
    133133 * - the code form view, where the user can enter the OTP.
     
    136136 *
    137137 * @param {string} nonce         WordPress nonce.
    138  * @param {string} email         Email address.  resource_type   Resource Type (tag|product).
    139  * @param {string} resource_type Resource Type (tag|product).
    140  * @param {string} resource_id   Resource ID (ConvertKit Tag or Product ID).
     138 * @param {string} email         Email address.
     139 * @param {string} resource_type Resource Type (form|tag|product).
     140 * @param {string} resource_id   Resource ID (Kit Form,Tag or Product ID).
    141141 * @param {number} post_id       WordPress Post ID being viewed / accessed.
    142142 */
     
    148148    post_id
    149149) {
    150     fetch(convertkit_restrict_content.ajaxurl, {
     150    fetch(convertkit_restrict_content.subscriber_authentication_url, {
    151151        method: 'POST',
    152152        headers: {
    153153            'Content-Type': 'application/x-www-form-urlencoded',
     154            'X-WP-Nonce': nonce,
    154155        },
    155156        body: new URLSearchParams({
    156             action: 'convertkit_subscriber_authentication_send_code',
    157             _wpnonce: nonce,
    158157            convertkit_email: email,
    159158            convertkit_resource_type: resource_type,
     
    174173            }
    175174
    176             // Output response, which will be a form with/without an error message.
    177             document.querySelector(
    178                 '#convertkit-restrict-content-modal-content'
    179             ).innerHTML = result.data;
     175            // Output error message if the response contains a code.
     176            if (typeof result.code !== 'undefined') {
     177                document.querySelector(
     178                    '#convertkit-restrict-content-modal-content'
     179                ).innerHTML = result.message;
     180            } else {
     181                // Output response, which will be either:
     182                // - the email form view, with an error message e.g. invalid email,
     183                // - the code form view, where the user can enter the OTP.
     184                document.querySelector(
     185                    '#convertkit-restrict-content-modal-content'
     186                ).innerHTML = result.data;
     187            }
    180188
    181189            // Hide loading overlay.
     
    195203
    196204/**
    197  * Submits the given email address to maybe_run_subscriber_verification(), which
    198  * will return either:
     205 * Submits the given email address to the WP REST API kit/v1/restrict-content/subscriber-verification
     206 * endpoint, which will return either:
    199207 * - the code form view, with an error message e.g. invalid code entered,
    200208 * - the Post's URL, with a `ck-cache-bust` parameter appended, which can then be loaded to show the content.
     
    213221    post_id
    214222) {
    215     fetch(convertkit_restrict_content.ajaxurl, {
     223    fetch(convertkit_restrict_content.subscriber_verification_url, {
    216224        method: 'POST',
    217225        headers: {
    218226            'Content-Type': 'application/x-www-form-urlencoded',
     227            'X-WP-Nonce': nonce,
    219228        },
    220229        body: new URLSearchParams({
    221             action: 'convertkit_subscriber_verification',
    222             _wpnonce: nonce,
    223230            subscriber_code,
    224231            token,
     
    255262
    256263            // Code entered is valid; load the URL in the response data.
    257             window.location = result.data;
     264            window.location = result.url;
    258265        })
    259266        .catch(function (error) {
  • convertkit/trunk/vendor/convertkit/convertkit-wordpress-libraries/src/class-convertkit-api-v4.php

    r3369648 r3399438  
    378378        if ( is_wp_error( $result ) ) {
    379379            $this->log( 'API: Error: ' . $result->get_error_message() );
     380
     381            /**
     382             * Perform any actions when obtaining an access token fails.
     383             *
     384             * @since   2.1.1
     385             *
     386             * @param   WP_Error  $result     Error from API.
     387             * @param   string    $client_id  OAuth Client ID.
     388             */
     389            do_action( 'convertkit_api_get_access_token_error', $result, $this->client_id );
     390
    380391            return $result;
    381392        }
     
    415426        if ( is_wp_error( $result ) ) {
    416427            $this->log( 'API: Error: ' . $result->get_error_message() );
     428
     429            /**
     430             * Perform any actions when refreshing an expired access token fails.
     431             *
     432             * @since   2.1.1
     433             *
     434             * @param   WP_Error  $result     Error from API.
     435             * @param   string    $client_id  OAuth Client ID.
     436             */
     437            do_action( 'convertkit_api_refresh_token_error', $result, $this->client_id );
     438
    417439            return $result;
    418440        }
     
    14331455        // Return the API error message as a WP_Error if the HTTP response code is a 4xx code.
    14341456        if ( $http_response_code >= 400 ) {
     1457
    14351458            // Define the error message.
    14361459            $error = $this->get_error_message_string( $response );
     
    14391462
    14401463            switch ( $http_response_code ) {
    1441                 // If the HTTP response code is 401, and the error matches 'The access token expired', refresh the access token now
    1442                 // and re-attempt the request.
    14431464                case 401:
    1444                     if ( $error !== 'The access token expired' ) {
    1445                         break;
     1465                    switch ( $error ) {
     1466                        case 'The access token expired':
     1467                            // Attempt to refresh the access token.
     1468                            $result = $this->refresh_token();
     1469
     1470                            // If an error occured, bail.
     1471                            if ( is_wp_error( $result ) ) {
     1472                                return $result;
     1473                            }
     1474
     1475                            // Attempt the request again, now we have a new access token.
     1476                            return $this->request( $endpoint, $method, $params, false );
     1477
     1478                        case 'The access token is invalid':
     1479                            $error = new WP_Error(
     1480                                'convertkit_api_error',
     1481                                $error,
     1482                                $http_response_code
     1483                            );
     1484
     1485                            /**
     1486                             * Perform any actions when an invalid access token was used.
     1487                             *
     1488                             * @since   2.1.1
     1489                             *
     1490                             * @param   WP_Error  $error      WP_Error object.
     1491                             * @param   string    $client_id  OAuth Client ID.
     1492                             */
     1493                            do_action( 'convertkit_api_access_token_invalid', $error, $this->client_id );
     1494
     1495                            // Return error.
     1496                            return $error;
     1497
     1498                        default:
     1499                            return new WP_Error(
     1500                                'convertkit_api_error',
     1501                                $error,
     1502                                $http_response_code
     1503                            );
    14461504                    }
    14471505
    1448                     // Don't automatically refresh the expired access token if we're not on a production environment.
    1449                     // This prevents the same ConvertKit account used on both a staging and production site from
    1450                     // reaching a race condition where the staging site refreshes the token first, resulting in
    1451                     // the production site unable to later refresh its same expired access token.
    1452                     if ( ! $this->is_production_site() ) {
    1453                         break;
    1454                     }
    1455 
    1456                     // Refresh the access token.
    1457                     $result = $this->refresh_token();
    1458 
    1459                     // If an error occured, bail.
    1460                     if ( is_wp_error( $result ) ) {
    1461                         return $result;
    1462                     }
    1463 
    1464                     // Attempt the request again, now we have a new access token.
    1465                     return $this->request( $endpoint, $method, $params, false );
    1466 
    1467                 // If a rate limit was hit, maybe try again.
    14681506                case 429:
    14691507                    // If retry on rate limit hit is disabled, return a WP_Error.
     
    14891527
    14901528        return $response;
    1491 
    1492     }
    1493 
    1494     /**
    1495      * Helper method to determine the WordPress environment type, checking
    1496      * if the wp_get_environment_type() function exists in WordPress (versions
    1497      * older than WordPress 5.5 won't have this function).
    1498      *
    1499      * @since   2.0.2
    1500      *
    1501      * @return  bool
    1502      */
    1503     private function is_production_site() {
    1504 
    1505         // If the WordPress wp_get_environment_type() function isn't available,
    1506         // assume this is a production site.
    1507         if ( ! function_exists( 'wp_get_environment_type' ) ) {
    1508             return true;
    1509         }
    1510 
    1511         return ( wp_get_environment_type() === 'production' );
    15121529
    15131530    }
  • convertkit/trunk/views/backend/settings/tools.php

    r3164741 r3399438  
    103103
    104104    <?php
     105    // Mailchimp for WordPress (MC4WP).
     106    if ( $mc4wp->has_forms_in_posts() && $mc4wp->has_forms() && $forms->exist() ) {
     107        ?>
     108        <div id="import-mc4wp" class="postbox">
     109            <h2><?php esc_html_e( 'MC4WP: Migrate Configuration', 'convertkit' ); ?></h2>
     110
     111            <p class="description">
     112                <?php esc_html_e( 'Automatically replace MC4WP form shortcodes with Kit forms.', 'convertkit' ); ?><br />
     113            </p>
     114
     115            <table class="widefat striped">
     116                <thead>
     117                    <tr>
     118                        <th><?php esc_html_e( 'MC4WP Form', 'convertkit' ); ?></th>
     119                        <th><?php esc_html_e( 'Kit Form', 'convertkit' ); ?></th>
     120                    </tr>
     121                </thead>
     122                <tbody>
     123                    <?php
     124                    foreach ( $mc4wp->get_forms() as $mc4wp_form_id => $mc4wp_form_title ) {
     125                        ?>
     126                        <tr>
     127                            <td><?php echo esc_html( $mc4wp_form_title ); ?></td>
     128                            <td>
     129                                <select name="_wp_convertkit_integration_mc4wp_settings[<?php echo esc_attr( $mc4wp_form_id ); ?>]">
     130                                    <?php
     131                                    foreach ( $forms->get() as $form ) {
     132                                        ?>
     133                                        <option value="<?php echo esc_attr( $form['id'] ); ?>"><?php echo esc_html( $form['name'] ); ?></option>
     134                                        <?php
     135                                    }
     136                                    ?>
     137                                </select>
     138                            </td>
     139                        </tr>
     140                        <?php
     141                    }
     142                    ?>
     143                </tbody>
     144            </table>
     145
     146            <p>
     147                <?php
     148                submit_button(
     149                    __( 'Migrate', 'convertkit' ),
     150                    'primary',
     151                    'convertkit-import-mc4wp',
     152                    false
     153                );
     154                ?>
     155            </p>
     156        </div><!-- .postbox -->
     157        <?php
     158    }
     159
    105160    wp_nonce_field( 'convertkit-settings-tools', '_convertkit_settings_tools_nonce' );
    106161    ?>
  • convertkit/trunk/wp-convertkit.php

    r3390429 r3399438  
    1010 * Plugin URI: https://kit.com/
    1111 * Description: Display Kit (formerly ConvertKit) email subscription forms, landing pages, products, broadcasts and more.
    12  * Version: 3.0.8
     12 * Version: 3.1.0
    1313 * Author: Kit
    1414 * Author URI: https://kit.com/
     
    2828define( 'CONVERTKIT_PLUGIN_URL', plugin_dir_url( __FILE__ ) );
    2929define( 'CONVERTKIT_PLUGIN_PATH', __DIR__ );
    30 define( 'CONVERTKIT_PLUGIN_VERSION', '3.0.8' );
     30define( 'CONVERTKIT_PLUGIN_VERSION', '3.1.0' );
    3131define( 'CONVERTKIT_OAUTH_CLIENT_ID', 'HXZlOCj-K5r0ufuWCtyoyo3f688VmMAYSsKg1eGvw0Y' );
    3232define( 'CONVERTKIT_OAUTH_CLIENT_REDIRECT_URI', 'https://app.kit.com/wordpress/redirect' );
     
    5353require_once CONVERTKIT_PLUGIN_PATH . '/includes/functions.php';
    5454require_once CONVERTKIT_PLUGIN_PATH . '/includes/class-wp-convertkit.php';
     55require_once CONVERTKIT_PLUGIN_PATH . '/includes/class-convertkit-admin-notices.php';
    5556require_once CONVERTKIT_PLUGIN_PATH . '/includes/class-convertkit-ajax.php';
    5657require_once CONVERTKIT_PLUGIN_PATH . '/includes/class-convertkit-broadcasts-exporter.php';
     
    109110require_once CONVERTKIT_PLUGIN_PATH . '/admin/class-convertkit-admin-category.php';
    110111require_once CONVERTKIT_PLUGIN_PATH . '/admin/class-convertkit-admin-landing-page.php';
    111 require_once CONVERTKIT_PLUGIN_PATH . '/admin/class-convertkit-admin-notices.php';
    112112require_once CONVERTKIT_PLUGIN_PATH . '/admin/class-convertkit-admin-post.php';
    113113require_once CONVERTKIT_PLUGIN_PATH . '/admin/class-convertkit-admin-refresh-resources.php';
     
    117117require_once CONVERTKIT_PLUGIN_PATH . '/admin/class-convertkit-admin-setup-wizard.php';
    118118require_once CONVERTKIT_PLUGIN_PATH . '/admin/class-convertkit-wp-list-table.php';
     119require_once CONVERTKIT_PLUGIN_PATH . '/admin/importers/class-convertkit-admin-importer.php';
     120require_once CONVERTKIT_PLUGIN_PATH . '/admin/importers/class-convertkit-admin-importer-mc4wp.php';
    119121require_once CONVERTKIT_PLUGIN_PATH . '/admin/section/class-convertkit-admin-section-base.php';
    120122require_once CONVERTKIT_PLUGIN_PATH . '/admin/section/class-convertkit-admin-section-broadcasts.php';
Note: See TracChangeset for help on using the changeset viewer.