Changeset 3399438
- Timestamp:
- 11/20/2025 06:41:53 AM (2 months ago)
- Location:
- convertkit
- Files:
-
- 8 added
- 2 deleted
- 62 edited
- 1 copied
-
tags/3.1.0 (copied) (copied from convertkit/trunk)
-
tags/3.1.0/CHANGELOG.md (modified) (1 diff)
-
tags/3.1.0/admin/class-convertkit-admin-notices.php (deleted)
-
tags/3.1.0/admin/class-convertkit-admin-refresh-resources.php (modified) (7 diffs)
-
tags/3.1.0/admin/importers (added)
-
tags/3.1.0/admin/importers/class-convertkit-admin-importer-mc4wp.php (added)
-
tags/3.1.0/admin/importers/class-convertkit-admin-importer.php (added)
-
tags/3.1.0/admin/section/class-convertkit-admin-section-general.php (modified) (2 diffs)
-
tags/3.1.0/admin/section/class-convertkit-admin-section-tools.php (modified) (4 diffs)
-
tags/3.1.0/includes/blocks/class-convertkit-block-broadcasts.php (modified) (2 diffs)
-
tags/3.1.0/includes/blocks/class-convertkit-block-content.php (modified) (2 diffs)
-
tags/3.1.0/includes/blocks/class-convertkit-block-form-builder-field-custom.php (modified) (2 diffs)
-
tags/3.1.0/includes/blocks/class-convertkit-block-form-builder-field-email.php (modified) (1 diff)
-
tags/3.1.0/includes/blocks/class-convertkit-block-form-builder-field.php (modified) (2 diffs)
-
tags/3.1.0/includes/blocks/class-convertkit-block-form-builder.php (modified) (2 diffs)
-
tags/3.1.0/includes/blocks/class-convertkit-block-form-trigger.php (modified) (2 diffs)
-
tags/3.1.0/includes/blocks/class-convertkit-block-form.php (modified) (2 diffs)
-
tags/3.1.0/includes/blocks/class-convertkit-block-product.php (modified) (2 diffs)
-
tags/3.1.0/includes/blocks/class-convertkit-block.php (modified) (2 diffs)
-
tags/3.1.0/includes/class-convertkit-admin-notices.php (added)
-
tags/3.1.0/includes/class-convertkit-ajax.php (modified) (2 diffs)
-
tags/3.1.0/includes/class-convertkit-cache-plugins.php (modified) (6 diffs)
-
tags/3.1.0/includes/class-convertkit-gutenberg.php (modified) (2 diffs)
-
tags/3.1.0/includes/class-convertkit-output-restrict-content.php (modified) (15 diffs)
-
tags/3.1.0/includes/class-convertkit-output.php (modified) (3 diffs)
-
tags/3.1.0/includes/class-convertkit-resource-forms.php (modified) (1 diff)
-
tags/3.1.0/includes/class-convertkit-settings.php (modified) (4 diffs)
-
tags/3.1.0/includes/class-wp-convertkit.php (modified) (2 diffs)
-
tags/3.1.0/languages/convertkit.pot (modified) (13 diffs)
-
tags/3.1.0/readme.txt (modified) (2 diffs)
-
tags/3.1.0/resources/backend/js/gutenberg.js (modified) (4 diffs)
-
tags/3.1.0/resources/backend/js/refresh-resources.js (modified) (5 diffs)
-
tags/3.1.0/resources/frontend/js/convertkit.js (modified) (1 diff)
-
tags/3.1.0/resources/frontend/js/restrict-content.js (modified) (9 diffs)
-
tags/3.1.0/vendor/convertkit/convertkit-wordpress-libraries/src/class-convertkit-api-v4.php (modified) (5 diffs)
-
tags/3.1.0/views/backend/settings/tools.php (modified) (1 diff)
-
tags/3.1.0/wp-convertkit.php (modified) (5 diffs)
-
trunk/CHANGELOG.md (modified) (1 diff)
-
trunk/admin/class-convertkit-admin-notices.php (deleted)
-
trunk/admin/class-convertkit-admin-refresh-resources.php (modified) (7 diffs)
-
trunk/admin/importers (added)
-
trunk/admin/importers/class-convertkit-admin-importer-mc4wp.php (added)
-
trunk/admin/importers/class-convertkit-admin-importer.php (added)
-
trunk/admin/section/class-convertkit-admin-section-general.php (modified) (2 diffs)
-
trunk/admin/section/class-convertkit-admin-section-tools.php (modified) (4 diffs)
-
trunk/includes/blocks/class-convertkit-block-broadcasts.php (modified) (2 diffs)
-
trunk/includes/blocks/class-convertkit-block-content.php (modified) (2 diffs)
-
trunk/includes/blocks/class-convertkit-block-form-builder-field-custom.php (modified) (2 diffs)
-
trunk/includes/blocks/class-convertkit-block-form-builder-field-email.php (modified) (1 diff)
-
trunk/includes/blocks/class-convertkit-block-form-builder-field.php (modified) (2 diffs)
-
trunk/includes/blocks/class-convertkit-block-form-builder.php (modified) (2 diffs)
-
trunk/includes/blocks/class-convertkit-block-form-trigger.php (modified) (2 diffs)
-
trunk/includes/blocks/class-convertkit-block-form.php (modified) (2 diffs)
-
trunk/includes/blocks/class-convertkit-block-product.php (modified) (2 diffs)
-
trunk/includes/blocks/class-convertkit-block.php (modified) (2 diffs)
-
trunk/includes/class-convertkit-admin-notices.php (added)
-
trunk/includes/class-convertkit-ajax.php (modified) (2 diffs)
-
trunk/includes/class-convertkit-cache-plugins.php (modified) (6 diffs)
-
trunk/includes/class-convertkit-gutenberg.php (modified) (2 diffs)
-
trunk/includes/class-convertkit-output-restrict-content.php (modified) (15 diffs)
-
trunk/includes/class-convertkit-output.php (modified) (3 diffs)
-
trunk/includes/class-convertkit-resource-forms.php (modified) (1 diff)
-
trunk/includes/class-convertkit-settings.php (modified) (4 diffs)
-
trunk/includes/class-wp-convertkit.php (modified) (2 diffs)
-
trunk/languages/convertkit.pot (modified) (13 diffs)
-
trunk/readme.txt (modified) (2 diffs)
-
trunk/resources/backend/js/gutenberg.js (modified) (4 diffs)
-
trunk/resources/backend/js/refresh-resources.js (modified) (5 diffs)
-
trunk/resources/frontend/js/convertkit.js (modified) (1 diff)
-
trunk/resources/frontend/js/restrict-content.js (modified) (9 diffs)
-
trunk/vendor/convertkit/convertkit-wordpress-libraries/src/class-convertkit-api-v4.php (modified) (5 diffs)
-
trunk/views/backend/settings/tools.php (modified) (1 diff)
-
trunk/wp-convertkit.php (modified) (5 diffs)
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 1 13 ### 3.0.8 2025-11-05 2 14 * 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 23 23 public function __construct() { 24 24 25 add_action( 'wp_ajax_convertkit_admin_refresh_resources', array( $this, 'refresh_resources' ) );26 25 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 ); 27 49 28 50 } … … 32 54 * 33 55 * @since 1.9.8.0 56 * 57 * @param WP_REST_Request $request Request object. 58 * @return WP_REST_Response|WP_Error Response object. 34 59 */ 35 public function refresh_resources() { 36 37 // Check nonce. 38 check_ajax_referer( 'convertkit_admin_refresh_resources', 'nonce' ); 60 public function refresh_resources( $request ) { 39 61 40 62 // Get resource type. 41 $resource = ( isset( $_REQUEST['resource'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['resource'] ) ) : '' );63 $resource = $request->get_param( 'resource' ); 42 64 43 65 // Fetch resources. … … 75 97 // Bail if an error occured. 76 98 if ( is_wp_error( $results_tags ) ) { 77 wp_send_json_error( $results_tags->get_error_message());99 return rest_ensure_response( $results_tags ); 78 100 } 79 101 … … 84 106 // Bail if an error occured. 85 107 if ( is_wp_error( $results_products ) ) { 86 wp_send_json_error( $results_products->get_error_message());108 return rest_ensure_response( $results_products ); 87 109 } 88 110 89 111 // Return resources. 90 wp_send_json_success(112 return rest_ensure_response( 91 113 array( 92 114 'tags' => array_values( $results_tags ), … … 94 116 ) 95 117 ); 96 // no break as wp_send_json_success terminates.97 118 98 119 default: … … 109 130 // Bail if an error occured. 110 131 if ( is_wp_error( $results ) ) { 111 wp_send_json_error( $results->get_error_message());132 return rest_ensure_response( $results ); 112 133 } 113 134 114 135 // 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 ) ); 116 137 117 138 } … … 145 166 'convertkit_admin_refresh_resources', 146 167 array( 147 'action' => 'convertkit_admin_refresh_resources', 148 'ajaxurl' => admin_url( 'admin-ajax.php' ), 168 'ajaxurl' => rest_url( 'kit/v1/resources/refresh/' ), 149 169 'debug' => $settings->debug_enabled(), 150 'nonce' => wp_create_nonce( ' convertkit_admin_refresh_resources' ),170 'nonce' => wp_create_nonce( 'wp_rest' ), 151 171 ) 152 172 ); -
convertkit/tags/3.1.0/admin/section/class-convertkit-admin-section-general.php
r3386855 r3399438 134 134 // Bail if no access and refresh token exist. 135 135 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(); 137 147 } 138 148 … … 153 163 // If the request succeeded, no need to perform further actions. 154 164 if ( ! is_wp_error( $this->account ) ) { 155 // Remove any existing persistent notice.156 WP_ConvertKit()->get_class( 'admin_notices' )->delete( 'authorization_failed' );157 158 165 return; 159 166 } 160 167 161 // Depending on the error code, maybe persist a notice in the WordPress Administration until the user168 // Depending on the error code, display an error notice in the settings screen until the user 162 169 // 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, because173 // 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.186 170 $this->output_error( $this->account->get_error_message() ); 187 171 -
convertkit/tags/3.1.0/admin/section/class-convertkit-admin-section-tools.php
r3357832 r3399438 58 58 'import_configuration_empty' => __( 'The uploaded configuration file contains no settings.', 'convertkit' ), 59 59 'import_configuration_success' => __( 'Configuration imported successfully.', 'convertkit' ), 60 'migrate_mc4wp_configuration_success' => __( 'MC4WP forms migrated successfully.', 'convertkit' ), 60 61 ) 61 62 ); … … 76 77 $this->maybe_export_configuration(); 77 78 $this->maybe_import_configuration(); 79 $this->maybe_migrate_mc4wp_configuration(); 78 80 79 81 } … … 316 318 317 319 /** 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 /** 318 355 * Outputs the Debug Log and System Info view. 319 356 * … … 333 370 $system_info = $this->get_system_info(); 334 371 372 // Get Forms. 373 $forms = new ConvertKit_Resource_Forms(); 374 375 // Get Importers. 376 $mc4wp = new ConvertKit_Admin_Importer_MC4WP(); 377 335 378 // Output view. 336 379 require_once CONVERTKIT_PLUGIN_PATH . '/views/backend/settings/tools.php'; -
convertkit/tags/3.1.0/includes/blocks/class-convertkit-block-broadcasts.php
r3383204 r3399438 273 273 public function get_fields() { 274 274 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() ) { 277 277 return false; 278 278 } … … 381 381 public function get_panels() { 382 382 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() ) { 385 385 return false; 386 386 } -
convertkit/tags/3.1.0/includes/blocks/class-convertkit-block-content.php
r3160977 r3399438 105 105 public function get_fields() { 106 106 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() ) { 109 109 return false; 110 110 } … … 138 138 public function get_panels() { 139 139 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() ) { 142 142 return false; 143 143 } -
convertkit/tags/3.1.0/includes/blocks/class-convertkit-block-form-builder-field-custom.php
r3357832 r3399438 102 102 public function get_fields() { 103 103 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() ) { 106 106 return false; 107 107 } … … 151 151 public function get_panels() { 152 152 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() ) { 155 155 return false; 156 156 } -
convertkit/tags/3.1.0/includes/blocks/class-convertkit-block-form-builder-field-email.php
r3357832 r3399438 121 121 public function get_fields() { 122 122 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() ) { 125 125 return false; 126 126 } -
convertkit/tags/3.1.0/includes/blocks/class-convertkit-block-form-builder-field.php
r3359712 r3399438 195 195 public function get_fields() { 196 196 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() ) { 199 199 return false; 200 200 } … … 224 224 public function get_panels() { 225 225 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() ) { 228 228 return false; 229 229 } -
convertkit/tags/3.1.0/includes/blocks/class-convertkit-block-form-builder.php
r3383204 r3399438 476 476 public function get_fields() { 477 477 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() ) { 480 480 return false; 481 481 } … … 569 569 public function get_panels() { 570 570 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() ) { 573 573 return false; 574 574 } -
convertkit/tags/3.1.0/includes/blocks/class-convertkit-block-form-trigger.php
r3337204 r3399438 213 213 public function get_fields() { 214 214 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() ) { 217 217 return false; 218 218 } … … 271 271 public function get_panels() { 272 272 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() ) { 275 275 return false; 276 276 } -
convertkit/tags/3.1.0/includes/blocks/class-convertkit-block-form.php
r3357832 r3399438 241 241 public function get_fields() { 242 242 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() ) { 245 245 return false; 246 246 } … … 286 286 public function get_panels() { 287 287 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() ) { 290 290 return false; 291 291 } -
convertkit/tags/3.1.0/includes/blocks/class-convertkit-block-product.php
r3337204 r3399438 246 246 public function get_fields() { 247 247 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() ) { 250 250 return false; 251 251 } … … 313 313 public function get_panels() { 314 314 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() ) { 317 317 return false; 318 318 } -
convertkit/tags/3.1.0/includes/blocks/class-convertkit-block.php
r3337204 r3399438 397 397 398 398 /** 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 /** 399 425 * Determines if the request for the block is from the block editor or the frontend site. 400 426 * … … 406 432 407 433 // 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() ) { 412 435 return false; 413 436 } -
convertkit/tags/3.1.0/includes/class-convertkit-ajax.php
r3380693 r3399438 21 21 public function __construct() { 22 22 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 28 23 add_action( 'wp_ajax_nopriv_convertkit_store_subscriber_email_as_id_in_cookie', array( $this, 'store_subscriber_email_as_id_in_cookie' ) ); 29 24 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 when43 * displayNoticeWithLink() is called, because either44 * no Access Token is specified, or no resources exist in ConvertKit.45 *46 * @since 2.2.647 */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 site72 * that is set to "Send subscriber to thank you page", and the Plugin's JavaScript is not73 * disabled, permitting convertkit.js to run.74 *75 * @since 1.9.676 */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 );108 25 109 26 } … … 156 73 } 157 74 158 /**159 * Calls the API to send the subscriber a magic link by email containing a code when160 * 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.8169 */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 email201 * address supplied truly belongs to the user, and that we can safely trust their subscriber ID202 * to be valid.203 *204 * @since 2.3.8205 */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 233 75 } -
convertkit/tags/3.1.0/includes/class-convertkit-cache-plugins.php
r3359712 r3399438 16 16 17 17 /** 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. 19 20 * 20 21 * @since 2.4.6 … … 28 29 'pages.convertkit.com', 29 30 'convertkit.com', 31 'filekitcdn.com', 30 32 ); 31 33 … … 55 57 56 58 // 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' ) ); 59 61 60 62 // Jetpack Boost: Exclude Forms from JS defer. … … 75 77 add_filter( 'convertkit_resource_forms_output_script', array( $this, 'siteground_speed_optimizer_exclude_js_combine' ) ); 76 78 79 // Rocket LazyLoad: Exclude images from lazy loading. 80 add_filter( 'rocket_lazyload_excluded_src', array( $this, 'exclude_hosts' ) ); 81 77 82 // WP Rocket: Disable Caching and Minification on Landing Pages. 78 83 add_action( 'convertkit_output_landing_page_before', array( $this, 'wp_rocket_disable_caching_and_minification_on_landing_pages' ) ); 79 84 80 85 // 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' ) ); 82 87 83 88 // WP Rocket: Exclude Forms from Delay JavaScript execution. … … 239 244 public function wp_rocket_disable_caching_and_minification_on_landing_pages() { 240 245 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' ) ); 243 248 add_filter( 'rocket_exclude_js', array( $this, 'exclude_local_js_from_minification' ) ); 244 add_filter( 'do_rocket_lazyload', '__return_false' );245 249 246 250 } … … 278 282 public function exclude_hosts_from_minification( $hosts ) { 279 283 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 280 300 return array_merge( $hosts, $this->exclude_hosts ); 281 301 -
convertkit/tags/3.1.0/includes/class-convertkit-gutenberg.php
r3203903 r3399438 32 32 // Register Gutenberg Blocks. 33 33 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 ); 34 75 35 76 } … … 158 199 'convertkit_gutenberg', 159 200 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' ), 161 203 ) 162 204 ); -
convertkit/tags/3.1.0/includes/class-convertkit-output-restrict-content.php
r3380693 r3399438 107 107 public function __construct() { 108 108 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 ); 120 111 add_action( 'init', array( $this, 'maybe_run_subscriber_authentication' ), 3 ); 121 112 add_action( 'wp', array( $this, 'maybe_run_subscriber_verification' ), 4 ); … … 129 120 130 121 /** 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( 172 252 CONVERTKIT_OAUTH_CLIENT_ID, 173 253 CONVERTKIT_OAUTH_CLIENT_REDIRECT_URI, … … 178 258 ); 179 259 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 180 303 // Sanitize inputs. 181 304 $email = sanitize_text_field( wp_unslash( $_REQUEST['convertkit_email'] ) ); … … 184 307 $this->post_id = absint( $_REQUEST['convertkit_post_id'] ); 185 308 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() ) { 202 335 // Clear any existing subscriber ID cookie, as the authentication flow has started by sending the email. 203 336 $subscriber = new ConvertKit_Subscriber(); 204 337 $subscriber->forget(); 205 338 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 cookie247 // 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 273 339 // Fetch the subscriber ID from the result. 274 340 $subscriber_id = $result['subscriber']['id']; … … 277 343 $this->store_subscriber_id_in_cookie( $subscriber_id ); 278 344 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 292 371 * 293 372 * This calls the API to verify the token and subscriber code, which tells us that the email … … 309 388 // If a nonce was specified, validate it now. 310 389 // 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'] ) ) { 312 391 if ( ! wp_verify_nonce( sanitize_key( $_REQUEST['_wpnonce'] ), 'convertkit_restrict_content_subscriber_code' ) ) { 313 392 return; … … 332 411 } 333 412 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'] ) ) ); 349 415 350 416 // Bail if an error occured. … … 354 420 } 355 421 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 356 482 // Store subscriber ID in cookie. 357 483 $this->store_subscriber_id_in_cookie( $subscriber_id ); 358 484 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; 363 487 364 488 } … … 513 637 514 638 // 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 . '\' ' ) ); 516 640 517 641 // Return. … … 552 676 553 677 // 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 . '\' ' ) ); 555 679 556 680 // Return. … … 612 736 * 613 737 * @since 2.3.7 614 */ 615 private function redirect() { 738 * 739 * @param int $post_id Post ID. 740 */ 741 private function redirect( $post_id ) { 616 742 617 743 // Redirect to the Post, appending a query parameter to the URL to prevent caching plugins and … … 619 745 // result in maybe_restrict_content() not showing an error message or permitting 620 746 // access to the content. 621 wp_safe_redirect( $this->get_url( true ) );747 wp_safe_redirect( $this->get_url( $post_id, true ) ); 622 748 exit; 623 749 … … 629 755 * @since 2.1.0 630 756 * 757 * @param int $post_id Post ID. 631 758 * @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 ) { 635 762 636 763 // Get URL of Post. 637 $url = get_permalink( $ this->post_id );764 $url = get_permalink( $post_id ); 638 765 639 766 // If no cache busting required, return the URL now. … … 867 994 private function subscriber_has_access( $subscriber_id ) { // phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter 868 995 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 879 996 // Depending on the resource type, determine if the subscriber has access to it. 880 997 // This is deliberately a switch statement, because we will likely add in support … … 1215 1332 'convertkit_restrict_content', 1216 1333 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(), 1219 1338 ) 1220 1339 ); -
convertkit/tags/3.1.0/includes/class-convertkit-output.php
r3357832 r3399438 813 813 } 814 814 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 815 818 // Get form. 816 819 $convertkit_forms = new ConvertKit_Resource_Forms(); … … 829 832 add_filter( 830 833 'convertkit_output_scripts_footer', 831 function ( $scripts ) use ( $form ) {834 function ( $scripts ) use ( $form, $limit_per_session ) { 832 835 833 836 $scripts[] = array( … … 835 838 'data-uid' => $form['uid'], 836 839 'src' => $form['embed_js'], 837 'data-kit-limit-per-session' => true,840 'data-kit-limit-per-session' => $limit_per_session ? '1' : '0', 838 841 ); 839 842 -
convertkit/tags/3.1.0/includes/class-convertkit-resource-forms.php
r3357832 r3399438 545 545 'data-uid' => $this->resources[ $id ]['uid'], 546 546 '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', 548 548 ); 549 549 -
convertkit/tags/3.1.0/includes/class-convertkit-settings.php
r3376520 r3399438 49 49 add_action( 'convertkit_api_get_access_token', array( $this, 'update_credentials' ), 10, 2 ); 50 50 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 ); 51 55 52 56 } … … 630 634 } 631 635 636 // Remove any existing persistent notice. 637 WP_ConvertKit()->get_class( 'admin_notices' )->delete( 'authorization_failed' ); 638 632 639 $this->save( 633 640 array( … … 647 654 648 655 /** 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. 650 686 * 651 687 * @since 2.5.0 … … 661 697 ); 662 698 699 // Clear any existing scheduled WordPress Cron event. 700 wp_clear_scheduled_hook( 'convertkit_refresh_token' ); 701 663 702 } 664 703 -
convertkit/tags/3.1.0/includes/class-wp-convertkit.php
r3357832 r3399438 84 84 $this->classes['admin_category'] = new ConvertKit_Admin_Category(); 85 85 $this->classes['admin_landing_page'] = new ConvertKit_Admin_Landing_Page(); 86 $this->classes['admin_notices'] = new ConvertKit_Admin_Notices();87 86 $this->classes['admin_post'] = new ConvertKit_Admin_Post(); 88 87 $this->classes['admin_quick_edit'] = new ConvertKit_Admin_Quick_Edit(); 89 $this->classes['admin_refresh_resources'] = new ConvertKit_Admin_Refresh_Resources();90 88 $this->classes['admin_restrict_content'] = new ConvertKit_Admin_Restrict_Content(); 91 89 $this->classes['admin_settings'] = new ConvertKit_Admin_Settings(); … … 179 177 private function initialize_global() { 180 178 179 $this->classes['admin_notices'] = new ConvertKit_Admin_Notices(); 180 $this->classes['admin_refresh_resources'] = new ConvertKit_Admin_Refresh_Resources(); 181 181 $this->classes['ajax'] = new ConvertKit_AJAX(); 182 182 $this->classes['blocks_convertkit_broadcasts'] = new ConvertKit_Block_Broadcasts(); -
convertkit/tags/3.1.0/languages/convertkit.pot
r3390429 r3399438 3 3 msgid "" 4 4 msgstr "" 5 "Project-Id-Version: Kit (formerly ConvertKit) 3. 0.8\n"5 "Project-Id-Version: Kit (formerly ConvertKit) 3.1.0\n" 6 6 "Report-Msgid-Bugs-To: https://wordpress.org/support/plugin/convertkit\n" 7 7 "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" … … 10 10 "Content-Type: text/plain; charset=UTF-8\n" 11 11 "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" 13 13 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" 14 14 "X-Generator: WP-CLI 2.12.0\n" … … 88 88 msgstr "" 89 89 90 #: admin/class-convertkit-admin-notices.php:6991 msgid "Kit: Authorization failed. Please"92 msgstr ""93 94 #: admin/class-convertkit-admin-notices.php:7395 #: views/backend/post/no-api-key.php:1996 msgid "connect your Kit account."97 msgstr ""98 99 90 #: admin/class-convertkit-admin-post.php:93 100 91 msgid "Add New" … … 276 267 277 268 #: admin/section/class-convertkit-admin-section-broadcasts.php:501 278 #: admin/section/class-convertkit-admin-section-general.php:6 89269 #: admin/section/class-convertkit-admin-section-general.php:673 279 270 #: views/backend/post/bulk-edit.php:30 280 271 #: views/backend/post/bulk-edit.php:53 … … 396 387 msgstr "" 397 388 398 #: admin/section/class-convertkit-admin-section-general.php:2 95389 #: admin/section/class-convertkit-admin-section-general.php:279 399 390 msgid "Account Name" 400 391 msgstr "" 401 392 402 393 #. translators: Post Type Name, plural 403 #: admin/section/class-convertkit-admin-section-general.php:3 16394 #: admin/section/class-convertkit-admin-section-general.php:300 404 395 #, php-format 405 396 msgid "Default Form (%s)" … … 407 398 408 399 #. translators: Post Type Name, plural 409 #: admin/section/class-convertkit-admin-section-general.php:3 34400 #: admin/section/class-convertkit-admin-section-general.php:318 410 401 #, php-format 411 402 msgid "Form Position (%s)" 412 403 msgstr "" 413 404 414 #: admin/section/class-convertkit-admin-section-general.php:3 65405 #: admin/section/class-convertkit-admin-section-general.php:349 415 406 msgid "Default Forms (Site Wide)" 416 407 msgstr "" 417 408 418 #: admin/section/class-convertkit-admin-section-general.php:3 76409 #: admin/section/class-convertkit-admin-section-general.php:360 419 410 msgid "Behavior" 420 411 msgstr "" 421 412 422 #: admin/section/class-convertkit-admin-section-general.php:3 88413 #: admin/section/class-convertkit-admin-section-general.php:372 423 414 msgid "Limit Display" 424 415 msgstr "" 425 416 426 #: admin/section/class-convertkit-admin-section-general.php: 400417 #: admin/section/class-convertkit-admin-section-general.php:384 427 418 msgid "reCAPTCHA: Site Key" 428 419 msgstr "" 429 420 430 #: admin/section/class-convertkit-admin-section-general.php: 407421 #: admin/section/class-convertkit-admin-section-general.php:391 431 422 msgid "Enter your Google reCAPTCHA v3 Site Key. When specified, this will be used to reduce spam signups." 432 423 msgstr "" 433 424 434 #: admin/section/class-convertkit-admin-section-general.php: 413425 #: admin/section/class-convertkit-admin-section-general.php:397 435 426 msgid "reCAPTCHA: Secret Key" 436 427 msgstr "" 437 428 429 #: admin/section/class-convertkit-admin-section-general.php:404 430 msgid "Enter your Google reCAPTCHA v3 Secret Key. When specified, this will be used to reduce spam signups." 431 msgstr "" 432 433 #: admin/section/class-convertkit-admin-section-general.php:410 434 msgid "reCAPTCHA: Minimum Score" 435 msgstr "" 436 438 437 #: 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:426443 msgid "reCAPTCHA: Minimum Score"444 msgstr ""445 446 #: admin/section/class-convertkit-admin-section-general.php:436447 438 msgid "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)." 448 439 msgstr "" 449 440 450 #: admin/section/class-convertkit-admin-section-general.php:4 44441 #: admin/section/class-convertkit-admin-section-general.php:428 451 442 msgid "Debug" 452 443 msgstr "" 453 444 454 #: admin/section/class-convertkit-admin-section-general.php:4 55445 #: admin/section/class-convertkit-admin-section-general.php:439 455 446 msgid "Disable JavaScript" 456 447 msgstr "" 457 448 458 #: admin/section/class-convertkit-admin-section-general.php:4 66449 #: admin/section/class-convertkit-admin-section-general.php:450 459 450 msgid "Disable CSS" 460 451 msgstr "" 461 452 462 #: admin/section/class-convertkit-admin-section-general.php:4 77453 #: admin/section/class-convertkit-admin-section-general.php:461 463 454 msgid "Usage Tracking" 464 455 msgstr "" 465 456 466 #: admin/section/class-convertkit-admin-section-general.php:4 94457 #: admin/section/class-convertkit-admin-section-general.php:478 467 458 msgid "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." 468 459 msgstr "" 469 460 470 #: admin/section/class-convertkit-admin-section-general.php:4 95461 #: admin/section/class-convertkit-admin-section-general.php:479 471 462 msgid "You can also configure non-inline forms to display site wide under the \"Non-inline Forms\" section below." 472 463 msgstr "" 473 464 474 #: admin/section/class-convertkit-admin-section-general.php:4 96465 #: admin/section/class-convertkit-admin-section-general.php:480 475 466 msgid "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." 476 467 msgstr "" 477 468 478 469 #. translators: [convertkit] shortcode, wrapped in <code> tags 479 #: admin/section/class-convertkit-admin-section-general.php: 501470 #: admin/section/class-convertkit-admin-section-general.php:485 480 471 #, php-format 481 472 msgid "The default form can be inserted into the middle of post or page content by using either the %s shortcode or block." 482 473 msgstr "" 483 474 484 #: admin/section/class-convertkit-admin-section-general.php:5 18475 #: admin/section/class-convertkit-admin-section-general.php:502 485 476 msgid "Defines non-inline forms to display site wide, and if these forms should display on Pages / Posts that have the Kit Form setting = None." 486 477 msgstr "" 487 478 488 #: admin/section/class-convertkit-admin-section-general.php:5 30479 #: admin/section/class-convertkit-admin-section-general.php:514 489 480 msgid "Configure reCAPTCHA to protect the Member Content signup form and Form Builder block from spam and abuse." 490 481 msgstr "" 491 482 492 #: admin/section/class-convertkit-admin-section-general.php:5 42483 #: admin/section/class-convertkit-admin-section-general.php:526 493 484 msgid "Defines advanced configuration settings, usually when working with support or needing to disable JS or CSS." 494 485 msgstr "" 495 486 496 #: admin/section/class-convertkit-admin-section-general.php:5 71487 #: admin/section/class-convertkit-admin-section-general.php:555 497 488 msgid "(Not specified)" 498 489 msgstr "" 499 490 500 #: admin/section/class-convertkit-admin-section-general.php:5 86491 #: admin/section/class-convertkit-admin-section-general.php:570 501 492 msgid "Disconnect" 502 493 msgstr "" 503 494 504 #: admin/section/class-convertkit-admin-section-general.php:6 49495 #: admin/section/class-convertkit-admin-section-general.php:633 505 496 msgid "No Forms exist in Kit." 506 497 msgstr "" 507 498 499 #: admin/section/class-convertkit-admin-section-general.php:634 500 msgid "Click here to create your first form" 501 msgstr "" 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 507 msgid "Select a form above to automatically output below all %s." 508 msgstr "" 509 508 510 #: 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 521 512 #: includes/class-convertkit-broadcasts-exporter.php:150 522 513 #: views/backend/setup-wizard/convertkit-setup/content-2.php:79 … … 525 516 msgstr "" 526 517 527 #: admin/section/class-convertkit-admin-section-general.php:6 67528 #: admin/section/class-convertkit-admin-section-general.php: 804518 #: admin/section/class-convertkit-admin-section-general.php:651 519 #: admin/section/class-convertkit-admin-section-general.php:788 529 520 msgid "to preview how this will display." 530 521 msgstr "" 531 522 532 523 #. translators: Post type singular name 533 #: admin/section/class-convertkit-admin-section-general.php:7 16524 #: admin/section/class-convertkit-admin-section-general.php:700 534 525 #, php-format 535 526 msgid "Before %s content" … … 537 528 538 529 #. translators: Post type singular name 539 #: admin/section/class-convertkit-admin-section-general.php:7 21530 #: admin/section/class-convertkit-admin-section-general.php:705 540 531 #, php-format 541 532 msgid "After %s content" … … 543 534 544 535 #. translators: Post type singular name 545 #: admin/section/class-convertkit-admin-section-general.php:7 26536 #: admin/section/class-convertkit-admin-section-general.php:710 546 537 #, php-format 547 538 msgid "Before and after %s content" 548 539 msgstr "" 549 540 550 #: admin/section/class-convertkit-admin-section-general.php:7 29541 #: admin/section/class-convertkit-admin-section-general.php:713 551 542 msgid "After element" 552 543 msgstr "" 553 544 554 545 #. translators: Post Type name, plural 555 #: admin/section/class-convertkit-admin-section-general.php:7 33546 #: admin/section/class-convertkit-admin-section-general.php:717 556 547 #, php-format 557 548 msgid "Where forms should display relative to the %s content" 558 549 msgstr "" 559 550 560 #: admin/section/class-convertkit-admin-section-general.php:7 68551 #: admin/section/class-convertkit-admin-section-general.php:752 561 552 msgid "Paragraphs" 562 553 msgstr "" 563 554 564 #: admin/section/class-convertkit-admin-section-general.php:7 69555 #: admin/section/class-convertkit-admin-section-general.php:753 565 556 msgid "Headings <h2>" 566 557 msgstr "" 567 558 568 #: admin/section/class-convertkit-admin-section-general.php:7 70559 #: admin/section/class-convertkit-admin-section-general.php:754 569 560 msgid "Headings <h3>" 570 561 msgstr "" 571 562 572 #: admin/section/class-convertkit-admin-section-general.php:7 71563 #: admin/section/class-convertkit-admin-section-general.php:755 573 564 msgid "Headings <h4>" 574 565 msgstr "" 575 566 576 #: admin/section/class-convertkit-admin-section-general.php:7 72567 #: admin/section/class-convertkit-admin-section-general.php:756 577 568 msgid "Headings <h5>" 578 569 msgstr "" 579 570 580 #: admin/section/class-convertkit-admin-section-general.php:7 73571 #: admin/section/class-convertkit-admin-section-general.php:757 581 572 msgid "Headings <h6>" 582 573 msgstr "" 583 574 584 #: admin/section/class-convertkit-admin-section-general.php:7 74575 #: admin/section/class-convertkit-admin-section-general.php:758 585 576 msgid "Images" 586 577 msgstr "" 587 578 588 #: admin/section/class-convertkit-admin-section-general.php:7 76579 #: admin/section/class-convertkit-admin-section-general.php:760 589 580 msgid "The number of elements before outputting the form." 590 581 msgstr "" 591 582 592 #: admin/section/class-convertkit-admin-section-general.php:7 93583 #: admin/section/class-convertkit-admin-section-general.php:777 593 584 msgid "No non-inline Forms exist in Kit." 594 585 msgstr "" 595 586 596 #: admin/section/class-convertkit-admin-section-general.php:7 94587 #: admin/section/class-convertkit-admin-section-general.php:778 597 588 msgid "Click here to create your first modal, slide in or sticky bar form" 598 589 msgstr "" 599 590 600 #: admin/section/class-convertkit-admin-section-general.php: 802591 #: admin/section/class-convertkit-admin-section-general.php:786 601 592 msgid "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." 602 593 msgstr "" 603 594 604 #: admin/section/class-convertkit-admin-section-general.php:8 40595 #: admin/section/class-convertkit-admin-section-general.php:824 605 596 msgid "If checked, do not display the site wide form(s) above on Pages / Posts that have their Kit Form setting = None." 606 597 msgstr "" 607 598 608 #: admin/section/class-convertkit-admin-section-general.php:8 60599 #: admin/section/class-convertkit-admin-section-general.php:844 609 600 msgid "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." 610 601 msgstr "" 611 602 612 #: admin/section/class-convertkit-admin-section-general.php:9 43603 #: admin/section/class-convertkit-admin-section-general.php:927 613 604 msgid "Log requests to file and output browser console messages." 614 605 msgstr "" 615 606 616 #: admin/section/class-convertkit-admin-section-general.php:9 44607 #: admin/section/class-convertkit-admin-section-general.php:928 617 608 msgid "You can ignore this unless you're working with our support team to resolve an issue. Decheck this option to improve performance." 618 609 msgstr "" 619 610 620 #: admin/section/class-convertkit-admin-section-general.php:9 61611 #: admin/section/class-convertkit-admin-section-general.php:945 621 612 msgid "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!" 622 613 msgstr "" 623 614 624 #: admin/section/class-convertkit-admin-section-general.php:9 78615 #: admin/section/class-convertkit-admin-section-general.php:962 625 616 msgid "Prevents loading plugin CSS files. This will disable styling on broadcasts, form trigger buttons, product buttons and member's content. Use with caution!" 626 617 msgstr "" 627 618 628 #: admin/section/class-convertkit-admin-section-general.php:9 82619 #: admin/section/class-convertkit-admin-section-general.php:966 629 620 msgid "To customize forms and their styling, use the" 630 621 msgstr "" 631 622 632 #: admin/section/class-convertkit-admin-section-general.php:9 84623 #: admin/section/class-convertkit-admin-section-general.php:968 633 624 msgid "Kit form editor" 634 625 msgstr "" 635 626 636 #: admin/section/class-convertkit-admin-section-general.php:9 86627 #: admin/section/class-convertkit-admin-section-general.php:970 637 628 msgid "For creators who require form designs to follow their WordPress theme, use the Kit Form Builder block in the block editor." 638 629 msgstr "" 639 630 640 #: admin/section/class-convertkit-admin-section-general.php:9 89631 #: admin/section/class-convertkit-admin-section-general.php:973 641 632 msgid "For developers who require custom form designs through use of CSS, consider using the" 642 633 msgstr "" 643 634 644 #: admin/section/class-convertkit-admin-section-general.php:9 90635 #: admin/section/class-convertkit-admin-section-general.php:974 645 636 msgid "or" 646 637 msgstr "" 647 638 648 #: admin/section/class-convertkit-admin-section-general.php:9 91639 #: admin/section/class-convertkit-admin-section-general.php:975 649 640 msgid "integrations." 650 641 msgstr "" 651 642 652 #: admin/section/class-convertkit-admin-section-general.php: 1010643 #: admin/section/class-convertkit-admin-section-general.php:994 653 644 msgid "By allowing us to collect usage data, we can better understand which WordPress configurations, themes and plugins we should test." 654 645 msgstr "" 655 646 656 #: admin/section/class-convertkit-admin-section-general.php: 1014647 #: admin/section/class-convertkit-admin-section-general.php:998 657 648 msgid "Complete documentation on usage tracking can be found" 658 649 msgstr "" 659 650 660 #: admin/section/class-convertkit-admin-section-general.php:10 16651 #: admin/section/class-convertkit-admin-section-general.php:1000 661 652 #: views/backend/setup-wizard/convertkit-setup/content-2.php:150 662 653 msgid "here" … … 844 835 msgstr "" 845 836 846 #: admin/section/class-convertkit-admin-section-tools.php:353 837 #: admin/section/class-convertkit-admin-section-tools.php:60 838 msgid "MC4WP forms migrated successfully." 839 msgstr "" 840 841 #: admin/section/class-convertkit-admin-section-tools.php:396 847 842 msgid "Tools to help you manage Kit on your site." 848 843 msgstr "" 849 844 850 #: admin/section/class-convertkit-admin-section-tools.php: 381845 #: admin/section/class-convertkit-admin-section-tools.php:424 851 846 msgid "WordPress 5.2 or higher is required for system information report." 852 847 msgstr "" … … 1418 1413 1419 1414 #: includes/blocks/class-convertkit-block-form.php:106 1415 #: views/backend/settings/tools.php:119 1420 1416 #: views/backend/term/fields-add.php:11 1421 1417 #: views/backend/term/fields-edit.php:12 … … 1513 1509 msgstr "" 1514 1510 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 1512 msgid "Kit: Authorization failed. Please" 1513 msgstr "" 1514 1515 #: includes/class-convertkit-admin-notices.php:73 1516 #: views/backend/post/no-api-key.php:19 1517 msgid "connect your Kit account." 1518 msgstr "" 1519 1520 #: includes/class-convertkit-ajax.php:43 1524 1521 msgid "Kit: Required parameter `email` not included in AJAX request." 1525 1522 msgstr "" 1526 1523 1527 #: includes/class-convertkit-ajax.php: 1321524 #: includes/class-convertkit-ajax.php:49 1528 1525 msgid "Kit: Required parameter `email` is empty." 1529 1526 msgstr "" 1530 1527 1531 #: includes/class-convertkit-ajax.php: 1371528 #: includes/class-convertkit-ajax.php:54 1532 1529 msgid "Kit: Required parameter `email` is not an email address." 1533 1530 msgstr "" … … 2108 2105 #: views/backend/settings/tools.php:95 2109 2106 msgid "Import" 2107 msgstr "" 2108 2109 #: views/backend/settings/tools.php:109 2110 msgid "MC4WP: Migrate Configuration" 2111 msgstr "" 2112 2113 #: views/backend/settings/tools.php:112 2114 msgid "Automatically replace MC4WP form shortcodes with Kit forms." 2115 msgstr "" 2116 2117 #: views/backend/settings/tools.php:118 2118 msgid "MC4WP Form" 2119 msgstr "" 2120 2121 #: views/backend/settings/tools.php:149 2122 msgid "Migrate" 2110 2123 msgstr "" 2111 2124 -
convertkit/tags/3.1.0/readme.txt
r3390429 r3399438 6 6 Tested up to: 6.8 7 7 Requires PHP: 7.1 8 Stable tag: 3. 0.88 Stable tag: 3.1.0 9 9 License: GPLv3 or later 10 10 License URI: https://www.gnu.org/licenses/gpl-3.0.html … … 180 180 == Changelog == 181 181 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 182 194 ### 3.0.8 2025-11-05 183 195 * 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 747 747 */ 748 748 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 754 749 // Disable the button. 755 750 if (typeof setButtonDisabled !== 'undefined') { … … 758 753 759 754 // 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 }, 764 761 }) 765 762 .then(function (response) { … … 768 765 }) 769 766 .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 770 783 // Update global ConvertKit Blocks object, so that any updated resources 771 784 // are reflected when adding new ConvertKit Blocks. 772 convertkit_blocks = response .data;785 convertkit_blocks = response; 773 786 774 787 // Update this block's properties, so that has_access_token, has_resources … … 791 804 wp.data 792 805 .dispatch('core/notices') 793 .createErrorNotice(' ConvertKit: ' + error, {806 .createErrorNotice('Kit: ' + error, { 794 807 id: 'convertkit-error', 795 808 }); 796 809 797 810 // Enable refresh button. 798 setButtonDisabled(false); 811 if (typeof setButtonDisabled !== 'undefined') { 812 setButtonDisabled(false); 813 } 799 814 }); 800 815 }; -
convertkit/tags/3.1.0/resources/backend/js/refresh-resources.js
r3390429 r3399438 50 50 51 51 // Perform AJAX request to refresh resource. 52 fetch(convertkit_admin_refresh_resources.ajaxurl , {52 fetch(convertkit_admin_refresh_resources.ajaxurl + resource, { 53 53 method: 'POST', 54 54 headers: { 55 'Content-Type': 'application/x-www-form-urlencoded', 55 'Content-Type': 'application/json', 56 'X-WP-Nonce': convertkit_admin_refresh_resources.nonce, 56 57 }, 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 }),62 58 }) 63 59 .then(function (response) { … … 71 67 72 68 // Show an error if the request wasn't successful. 73 if ( !response.success) {69 if (typeof response.code !== 'undefined') { 74 70 // Show error notice. 75 convertKitRefreshResourcesOutputErrorNotice(response. data);71 convertKitRefreshResourcesOutputErrorNotice(response.message); 76 72 77 73 // Enable button and remove is-refreshing class. … … 103 99 // Populate select `optgroup`` from response data, which comprises of Tags and Products. 104 100 // Tags. 105 response. data.tags.forEach(function (item) {101 response.tags.forEach(function (item) { 106 102 document 107 103 .querySelector( … … 119 115 120 116 // Products. 121 response. data.products.forEach(function (item) {117 response.products.forEach(function (item) { 122 118 document 123 119 .querySelector( … … 137 133 default: 138 134 // Populate select options from response data. 139 response. data.forEach(function (item) {135 response.forEach(function (item) { 140 136 // Define label. 141 137 let label = ''; -
convertkit/tags/3.1.0/resources/frontend/js/convertkit.js
r3369648 r3399438 213 213 // Set a cookie if any scripts with data-kit-limit-per-session attribute exist. 214 214 if ( 215 document.querySelectorAll('script[data-kit-limit-per-session ]').length >216 0215 document.querySelectorAll('script[data-kit-limit-per-session="1"]') 216 .length > 0 217 217 ) { 218 218 document.cookie = 'ck_non_inline_form_displayed=1; path=/'; -
convertkit/tags/3.1.0/resources/frontend/js/restrict-content.js
r3369648 r3399438 78 78 // Code submission. 79 79 convertKitRestrictContentSubscriberVerification( 80 e.target.querySelector('input[name="_wpnonce"]').value,80 convertkit_restrict_content.nonce, 81 81 e.target.querySelector('input[name="subscriber_code"]').value, 82 82 e.target.querySelector('input[name="token"]').value, … … 89 89 // Email submission. 90 90 convertKitRestrictContentSubscriberAuthenticationSendCode( 91 e.target.querySelector('input[name="_wpnonce"]').value,91 convertkit_restrict_content.nonce, 92 92 e.target.querySelector('input[name="convertkit_email"]').value, 93 93 e.target.querySelector('input[name="convertkit_resource_type"]').value, … … 128 128 129 129 /** 130 * Submits the given email address to maybe_run_subscriber_authentication(), which131 * 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: 132 132 * - the email form view, with an error message e.g. invalid email, 133 133 * - the code form view, where the user can enter the OTP. … … 136 136 * 137 137 * @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 ( ConvertKitTag 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). 141 141 * @param {number} post_id WordPress Post ID being viewed / accessed. 142 142 */ … … 148 148 post_id 149 149 ) { 150 fetch(convertkit_restrict_content. ajaxurl, {150 fetch(convertkit_restrict_content.subscriber_authentication_url, { 151 151 method: 'POST', 152 152 headers: { 153 153 'Content-Type': 'application/x-www-form-urlencoded', 154 'X-WP-Nonce': nonce, 154 155 }, 155 156 body: new URLSearchParams({ 156 action: 'convertkit_subscriber_authentication_send_code',157 _wpnonce: nonce,158 157 convertkit_email: email, 159 158 convertkit_resource_type: resource_type, … … 174 173 } 175 174 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 } 180 188 181 189 // Hide loading overlay. … … 195 203 196 204 /** 197 * Submits the given email address to maybe_run_subscriber_verification(), which198 * 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: 199 207 * - the code form view, with an error message e.g. invalid code entered, 200 208 * - the Post's URL, with a `ck-cache-bust` parameter appended, which can then be loaded to show the content. … … 213 221 post_id 214 222 ) { 215 fetch(convertkit_restrict_content. ajaxurl, {223 fetch(convertkit_restrict_content.subscriber_verification_url, { 216 224 method: 'POST', 217 225 headers: { 218 226 'Content-Type': 'application/x-www-form-urlencoded', 227 'X-WP-Nonce': nonce, 219 228 }, 220 229 body: new URLSearchParams({ 221 action: 'convertkit_subscriber_verification',222 _wpnonce: nonce,223 230 subscriber_code, 224 231 token, … … 255 262 256 263 // Code entered is valid; load the URL in the response data. 257 window.location = result. data;264 window.location = result.url; 258 265 }) 259 266 .catch(function (error) { -
convertkit/tags/3.1.0/vendor/convertkit/convertkit-wordpress-libraries/src/class-convertkit-api-v4.php
r3369648 r3399438 378 378 if ( is_wp_error( $result ) ) { 379 379 $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 380 391 return $result; 381 392 } … … 415 426 if ( is_wp_error( $result ) ) { 416 427 $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 417 439 return $result; 418 440 } … … 1433 1455 // Return the API error message as a WP_Error if the HTTP response code is a 4xx code. 1434 1456 if ( $http_response_code >= 400 ) { 1457 1435 1458 // Define the error message. 1436 1459 $error = $this->get_error_message_string( $response ); … … 1439 1462 1440 1463 switch ( $http_response_code ) { 1441 // If the HTTP response code is 401, and the error matches 'The access token expired', refresh the access token now1442 // and re-attempt the request.1443 1464 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 ); 1446 1504 } 1447 1505 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 from1450 // reaching a race condition where the staging site refreshes the token first, resulting in1451 // 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.1468 1506 case 429: 1469 1507 // If retry on rate limit hit is disabled, return a WP_Error. … … 1489 1527 1490 1528 return $response; 1491 1492 }1493 1494 /**1495 * Helper method to determine the WordPress environment type, checking1496 * if the wp_get_environment_type() function exists in WordPress (versions1497 * older than WordPress 5.5 won't have this function).1498 *1499 * @since 2.0.21500 *1501 * @return bool1502 */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' );1512 1529 1513 1530 } -
convertkit/tags/3.1.0/views/backend/settings/tools.php
r3164741 r3399438 103 103 104 104 <?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 105 160 wp_nonce_field( 'convertkit-settings-tools', '_convertkit_settings_tools_nonce' ); 106 161 ?> -
convertkit/tags/3.1.0/wp-convertkit.php
r3390429 r3399438 10 10 * Plugin URI: https://kit.com/ 11 11 * Description: Display Kit (formerly ConvertKit) email subscription forms, landing pages, products, broadcasts and more. 12 * Version: 3. 0.812 * Version: 3.1.0 13 13 * Author: Kit 14 14 * Author URI: https://kit.com/ … … 28 28 define( 'CONVERTKIT_PLUGIN_URL', plugin_dir_url( __FILE__ ) ); 29 29 define( 'CONVERTKIT_PLUGIN_PATH', __DIR__ ); 30 define( 'CONVERTKIT_PLUGIN_VERSION', '3. 0.8' );30 define( 'CONVERTKIT_PLUGIN_VERSION', '3.1.0' ); 31 31 define( 'CONVERTKIT_OAUTH_CLIENT_ID', 'HXZlOCj-K5r0ufuWCtyoyo3f688VmMAYSsKg1eGvw0Y' ); 32 32 define( 'CONVERTKIT_OAUTH_CLIENT_REDIRECT_URI', 'https://app.kit.com/wordpress/redirect' ); … … 53 53 require_once CONVERTKIT_PLUGIN_PATH . '/includes/functions.php'; 54 54 require_once CONVERTKIT_PLUGIN_PATH . '/includes/class-wp-convertkit.php'; 55 require_once CONVERTKIT_PLUGIN_PATH . '/includes/class-convertkit-admin-notices.php'; 55 56 require_once CONVERTKIT_PLUGIN_PATH . '/includes/class-convertkit-ajax.php'; 56 57 require_once CONVERTKIT_PLUGIN_PATH . '/includes/class-convertkit-broadcasts-exporter.php'; … … 109 110 require_once CONVERTKIT_PLUGIN_PATH . '/admin/class-convertkit-admin-category.php'; 110 111 require_once CONVERTKIT_PLUGIN_PATH . '/admin/class-convertkit-admin-landing-page.php'; 111 require_once CONVERTKIT_PLUGIN_PATH . '/admin/class-convertkit-admin-notices.php';112 112 require_once CONVERTKIT_PLUGIN_PATH . '/admin/class-convertkit-admin-post.php'; 113 113 require_once CONVERTKIT_PLUGIN_PATH . '/admin/class-convertkit-admin-refresh-resources.php'; … … 117 117 require_once CONVERTKIT_PLUGIN_PATH . '/admin/class-convertkit-admin-setup-wizard.php'; 118 118 require_once CONVERTKIT_PLUGIN_PATH . '/admin/class-convertkit-wp-list-table.php'; 119 require_once CONVERTKIT_PLUGIN_PATH . '/admin/importers/class-convertkit-admin-importer.php'; 120 require_once CONVERTKIT_PLUGIN_PATH . '/admin/importers/class-convertkit-admin-importer-mc4wp.php'; 119 121 require_once CONVERTKIT_PLUGIN_PATH . '/admin/section/class-convertkit-admin-section-base.php'; 120 122 require_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 1 13 ### 3.0.8 2025-11-05 2 14 * 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 23 23 public function __construct() { 24 24 25 add_action( 'wp_ajax_convertkit_admin_refresh_resources', array( $this, 'refresh_resources' ) );26 25 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 ); 27 49 28 50 } … … 32 54 * 33 55 * @since 1.9.8.0 56 * 57 * @param WP_REST_Request $request Request object. 58 * @return WP_REST_Response|WP_Error Response object. 34 59 */ 35 public function refresh_resources() { 36 37 // Check nonce. 38 check_ajax_referer( 'convertkit_admin_refresh_resources', 'nonce' ); 60 public function refresh_resources( $request ) { 39 61 40 62 // Get resource type. 41 $resource = ( isset( $_REQUEST['resource'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['resource'] ) ) : '' );63 $resource = $request->get_param( 'resource' ); 42 64 43 65 // Fetch resources. … … 75 97 // Bail if an error occured. 76 98 if ( is_wp_error( $results_tags ) ) { 77 wp_send_json_error( $results_tags->get_error_message());99 return rest_ensure_response( $results_tags ); 78 100 } 79 101 … … 84 106 // Bail if an error occured. 85 107 if ( is_wp_error( $results_products ) ) { 86 wp_send_json_error( $results_products->get_error_message());108 return rest_ensure_response( $results_products ); 87 109 } 88 110 89 111 // Return resources. 90 wp_send_json_success(112 return rest_ensure_response( 91 113 array( 92 114 'tags' => array_values( $results_tags ), … … 94 116 ) 95 117 ); 96 // no break as wp_send_json_success terminates.97 118 98 119 default: … … 109 130 // Bail if an error occured. 110 131 if ( is_wp_error( $results ) ) { 111 wp_send_json_error( $results->get_error_message());132 return rest_ensure_response( $results ); 112 133 } 113 134 114 135 // 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 ) ); 116 137 117 138 } … … 145 166 'convertkit_admin_refresh_resources', 146 167 array( 147 'action' => 'convertkit_admin_refresh_resources', 148 'ajaxurl' => admin_url( 'admin-ajax.php' ), 168 'ajaxurl' => rest_url( 'kit/v1/resources/refresh/' ), 149 169 'debug' => $settings->debug_enabled(), 150 'nonce' => wp_create_nonce( ' convertkit_admin_refresh_resources' ),170 'nonce' => wp_create_nonce( 'wp_rest' ), 151 171 ) 152 172 ); -
convertkit/trunk/admin/section/class-convertkit-admin-section-general.php
r3386855 r3399438 134 134 // Bail if no access and refresh token exist. 135 135 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(); 137 147 } 138 148 … … 153 163 // If the request succeeded, no need to perform further actions. 154 164 if ( ! is_wp_error( $this->account ) ) { 155 // Remove any existing persistent notice.156 WP_ConvertKit()->get_class( 'admin_notices' )->delete( 'authorization_failed' );157 158 165 return; 159 166 } 160 167 161 // Depending on the error code, maybe persist a notice in the WordPress Administration until the user168 // Depending on the error code, display an error notice in the settings screen until the user 162 169 // 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, because173 // 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.186 170 $this->output_error( $this->account->get_error_message() ); 187 171 -
convertkit/trunk/admin/section/class-convertkit-admin-section-tools.php
r3357832 r3399438 58 58 'import_configuration_empty' => __( 'The uploaded configuration file contains no settings.', 'convertkit' ), 59 59 'import_configuration_success' => __( 'Configuration imported successfully.', 'convertkit' ), 60 'migrate_mc4wp_configuration_success' => __( 'MC4WP forms migrated successfully.', 'convertkit' ), 60 61 ) 61 62 ); … … 76 77 $this->maybe_export_configuration(); 77 78 $this->maybe_import_configuration(); 79 $this->maybe_migrate_mc4wp_configuration(); 78 80 79 81 } … … 316 318 317 319 /** 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 /** 318 355 * Outputs the Debug Log and System Info view. 319 356 * … … 333 370 $system_info = $this->get_system_info(); 334 371 372 // Get Forms. 373 $forms = new ConvertKit_Resource_Forms(); 374 375 // Get Importers. 376 $mc4wp = new ConvertKit_Admin_Importer_MC4WP(); 377 335 378 // Output view. 336 379 require_once CONVERTKIT_PLUGIN_PATH . '/views/backend/settings/tools.php'; -
convertkit/trunk/includes/blocks/class-convertkit-block-broadcasts.php
r3383204 r3399438 273 273 public function get_fields() { 274 274 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() ) { 277 277 return false; 278 278 } … … 381 381 public function get_panels() { 382 382 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() ) { 385 385 return false; 386 386 } -
convertkit/trunk/includes/blocks/class-convertkit-block-content.php
r3160977 r3399438 105 105 public function get_fields() { 106 106 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() ) { 109 109 return false; 110 110 } … … 138 138 public function get_panels() { 139 139 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() ) { 142 142 return false; 143 143 } -
convertkit/trunk/includes/blocks/class-convertkit-block-form-builder-field-custom.php
r3357832 r3399438 102 102 public function get_fields() { 103 103 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() ) { 106 106 return false; 107 107 } … … 151 151 public function get_panels() { 152 152 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() ) { 155 155 return false; 156 156 } -
convertkit/trunk/includes/blocks/class-convertkit-block-form-builder-field-email.php
r3357832 r3399438 121 121 public function get_fields() { 122 122 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() ) { 125 125 return false; 126 126 } -
convertkit/trunk/includes/blocks/class-convertkit-block-form-builder-field.php
r3359712 r3399438 195 195 public function get_fields() { 196 196 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() ) { 199 199 return false; 200 200 } … … 224 224 public function get_panels() { 225 225 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() ) { 228 228 return false; 229 229 } -
convertkit/trunk/includes/blocks/class-convertkit-block-form-builder.php
r3383204 r3399438 476 476 public function get_fields() { 477 477 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() ) { 480 480 return false; 481 481 } … … 569 569 public function get_panels() { 570 570 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() ) { 573 573 return false; 574 574 } -
convertkit/trunk/includes/blocks/class-convertkit-block-form-trigger.php
r3337204 r3399438 213 213 public function get_fields() { 214 214 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() ) { 217 217 return false; 218 218 } … … 271 271 public function get_panels() { 272 272 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() ) { 275 275 return false; 276 276 } -
convertkit/trunk/includes/blocks/class-convertkit-block-form.php
r3357832 r3399438 241 241 public function get_fields() { 242 242 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() ) { 245 245 return false; 246 246 } … … 286 286 public function get_panels() { 287 287 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() ) { 290 290 return false; 291 291 } -
convertkit/trunk/includes/blocks/class-convertkit-block-product.php
r3337204 r3399438 246 246 public function get_fields() { 247 247 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() ) { 250 250 return false; 251 251 } … … 313 313 public function get_panels() { 314 314 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() ) { 317 317 return false; 318 318 } -
convertkit/trunk/includes/blocks/class-convertkit-block.php
r3337204 r3399438 397 397 398 398 /** 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 /** 399 425 * Determines if the request for the block is from the block editor or the frontend site. 400 426 * … … 406 432 407 433 // 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() ) { 412 435 return false; 413 436 } -
convertkit/trunk/includes/class-convertkit-ajax.php
r3380693 r3399438 21 21 public function __construct() { 22 22 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 28 23 add_action( 'wp_ajax_nopriv_convertkit_store_subscriber_email_as_id_in_cookie', array( $this, 'store_subscriber_email_as_id_in_cookie' ) ); 29 24 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 when43 * displayNoticeWithLink() is called, because either44 * no Access Token is specified, or no resources exist in ConvertKit.45 *46 * @since 2.2.647 */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 site72 * that is set to "Send subscriber to thank you page", and the Plugin's JavaScript is not73 * disabled, permitting convertkit.js to run.74 *75 * @since 1.9.676 */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 );108 25 109 26 } … … 156 73 } 157 74 158 /**159 * Calls the API to send the subscriber a magic link by email containing a code when160 * 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.8169 */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 email201 * address supplied truly belongs to the user, and that we can safely trust their subscriber ID202 * to be valid.203 *204 * @since 2.3.8205 */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 233 75 } -
convertkit/trunk/includes/class-convertkit-cache-plugins.php
r3359712 r3399438 16 16 17 17 /** 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. 19 20 * 20 21 * @since 2.4.6 … … 28 29 'pages.convertkit.com', 29 30 'convertkit.com', 31 'filekitcdn.com', 30 32 ); 31 33 … … 55 57 56 58 // 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' ) ); 59 61 60 62 // Jetpack Boost: Exclude Forms from JS defer. … … 75 77 add_filter( 'convertkit_resource_forms_output_script', array( $this, 'siteground_speed_optimizer_exclude_js_combine' ) ); 76 78 79 // Rocket LazyLoad: Exclude images from lazy loading. 80 add_filter( 'rocket_lazyload_excluded_src', array( $this, 'exclude_hosts' ) ); 81 77 82 // WP Rocket: Disable Caching and Minification on Landing Pages. 78 83 add_action( 'convertkit_output_landing_page_before', array( $this, 'wp_rocket_disable_caching_and_minification_on_landing_pages' ) ); 79 84 80 85 // 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' ) ); 82 87 83 88 // WP Rocket: Exclude Forms from Delay JavaScript execution. … … 239 244 public function wp_rocket_disable_caching_and_minification_on_landing_pages() { 240 245 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' ) ); 243 248 add_filter( 'rocket_exclude_js', array( $this, 'exclude_local_js_from_minification' ) ); 244 add_filter( 'do_rocket_lazyload', '__return_false' );245 249 246 250 } … … 278 282 public function exclude_hosts_from_minification( $hosts ) { 279 283 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 280 300 return array_merge( $hosts, $this->exclude_hosts ); 281 301 -
convertkit/trunk/includes/class-convertkit-gutenberg.php
r3203903 r3399438 32 32 // Register Gutenberg Blocks. 33 33 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 ); 34 75 35 76 } … … 158 199 'convertkit_gutenberg', 159 200 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' ), 161 203 ) 162 204 ); -
convertkit/trunk/includes/class-convertkit-output-restrict-content.php
r3380693 r3399438 107 107 public function __construct() { 108 108 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 ); 120 111 add_action( 'init', array( $this, 'maybe_run_subscriber_authentication' ), 3 ); 121 112 add_action( 'wp', array( $this, 'maybe_run_subscriber_verification' ), 4 ); … … 129 120 130 121 /** 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( 172 252 CONVERTKIT_OAUTH_CLIENT_ID, 173 253 CONVERTKIT_OAUTH_CLIENT_REDIRECT_URI, … … 178 258 ); 179 259 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 180 303 // Sanitize inputs. 181 304 $email = sanitize_text_field( wp_unslash( $_REQUEST['convertkit_email'] ) ); … … 184 307 $this->post_id = absint( $_REQUEST['convertkit_post_id'] ); 185 308 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() ) { 202 335 // Clear any existing subscriber ID cookie, as the authentication flow has started by sending the email. 203 336 $subscriber = new ConvertKit_Subscriber(); 204 337 $subscriber->forget(); 205 338 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 cookie247 // 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 273 339 // Fetch the subscriber ID from the result. 274 340 $subscriber_id = $result['subscriber']['id']; … … 277 343 $this->store_subscriber_id_in_cookie( $subscriber_id ); 278 344 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 292 371 * 293 372 * This calls the API to verify the token and subscriber code, which tells us that the email … … 309 388 // If a nonce was specified, validate it now. 310 389 // 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'] ) ) { 312 391 if ( ! wp_verify_nonce( sanitize_key( $_REQUEST['_wpnonce'] ), 'convertkit_restrict_content_subscriber_code' ) ) { 313 392 return; … … 332 411 } 333 412 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'] ) ) ); 349 415 350 416 // Bail if an error occured. … … 354 420 } 355 421 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 356 482 // Store subscriber ID in cookie. 357 483 $this->store_subscriber_id_in_cookie( $subscriber_id ); 358 484 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; 363 487 364 488 } … … 513 637 514 638 // 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 . '\' ' ) ); 516 640 517 641 // Return. … … 552 676 553 677 // 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 . '\' ' ) ); 555 679 556 680 // Return. … … 612 736 * 613 737 * @since 2.3.7 614 */ 615 private function redirect() { 738 * 739 * @param int $post_id Post ID. 740 */ 741 private function redirect( $post_id ) { 616 742 617 743 // Redirect to the Post, appending a query parameter to the URL to prevent caching plugins and … … 619 745 // result in maybe_restrict_content() not showing an error message or permitting 620 746 // access to the content. 621 wp_safe_redirect( $this->get_url( true ) );747 wp_safe_redirect( $this->get_url( $post_id, true ) ); 622 748 exit; 623 749 … … 629 755 * @since 2.1.0 630 756 * 757 * @param int $post_id Post ID. 631 758 * @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 ) { 635 762 636 763 // Get URL of Post. 637 $url = get_permalink( $ this->post_id );764 $url = get_permalink( $post_id ); 638 765 639 766 // If no cache busting required, return the URL now. … … 867 994 private function subscriber_has_access( $subscriber_id ) { // phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter 868 995 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 879 996 // Depending on the resource type, determine if the subscriber has access to it. 880 997 // This is deliberately a switch statement, because we will likely add in support … … 1215 1332 'convertkit_restrict_content', 1216 1333 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(), 1219 1338 ) 1220 1339 ); -
convertkit/trunk/includes/class-convertkit-output.php
r3357832 r3399438 813 813 } 814 814 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 815 818 // Get form. 816 819 $convertkit_forms = new ConvertKit_Resource_Forms(); … … 829 832 add_filter( 830 833 'convertkit_output_scripts_footer', 831 function ( $scripts ) use ( $form ) {834 function ( $scripts ) use ( $form, $limit_per_session ) { 832 835 833 836 $scripts[] = array( … … 835 838 'data-uid' => $form['uid'], 836 839 'src' => $form['embed_js'], 837 'data-kit-limit-per-session' => true,840 'data-kit-limit-per-session' => $limit_per_session ? '1' : '0', 838 841 ); 839 842 -
convertkit/trunk/includes/class-convertkit-resource-forms.php
r3357832 r3399438 545 545 'data-uid' => $this->resources[ $id ]['uid'], 546 546 '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', 548 548 ); 549 549 -
convertkit/trunk/includes/class-convertkit-settings.php
r3376520 r3399438 49 49 add_action( 'convertkit_api_get_access_token', array( $this, 'update_credentials' ), 10, 2 ); 50 50 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 ); 51 55 52 56 } … … 630 634 } 631 635 636 // Remove any existing persistent notice. 637 WP_ConvertKit()->get_class( 'admin_notices' )->delete( 'authorization_failed' ); 638 632 639 $this->save( 633 640 array( … … 647 654 648 655 /** 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. 650 686 * 651 687 * @since 2.5.0 … … 661 697 ); 662 698 699 // Clear any existing scheduled WordPress Cron event. 700 wp_clear_scheduled_hook( 'convertkit_refresh_token' ); 701 663 702 } 664 703 -
convertkit/trunk/includes/class-wp-convertkit.php
r3357832 r3399438 84 84 $this->classes['admin_category'] = new ConvertKit_Admin_Category(); 85 85 $this->classes['admin_landing_page'] = new ConvertKit_Admin_Landing_Page(); 86 $this->classes['admin_notices'] = new ConvertKit_Admin_Notices();87 86 $this->classes['admin_post'] = new ConvertKit_Admin_Post(); 88 87 $this->classes['admin_quick_edit'] = new ConvertKit_Admin_Quick_Edit(); 89 $this->classes['admin_refresh_resources'] = new ConvertKit_Admin_Refresh_Resources();90 88 $this->classes['admin_restrict_content'] = new ConvertKit_Admin_Restrict_Content(); 91 89 $this->classes['admin_settings'] = new ConvertKit_Admin_Settings(); … … 179 177 private function initialize_global() { 180 178 179 $this->classes['admin_notices'] = new ConvertKit_Admin_Notices(); 180 $this->classes['admin_refresh_resources'] = new ConvertKit_Admin_Refresh_Resources(); 181 181 $this->classes['ajax'] = new ConvertKit_AJAX(); 182 182 $this->classes['blocks_convertkit_broadcasts'] = new ConvertKit_Block_Broadcasts(); -
convertkit/trunk/languages/convertkit.pot
r3390429 r3399438 3 3 msgid "" 4 4 msgstr "" 5 "Project-Id-Version: Kit (formerly ConvertKit) 3. 0.8\n"5 "Project-Id-Version: Kit (formerly ConvertKit) 3.1.0\n" 6 6 "Report-Msgid-Bugs-To: https://wordpress.org/support/plugin/convertkit\n" 7 7 "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" … … 10 10 "Content-Type: text/plain; charset=UTF-8\n" 11 11 "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" 13 13 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" 14 14 "X-Generator: WP-CLI 2.12.0\n" … … 88 88 msgstr "" 89 89 90 #: admin/class-convertkit-admin-notices.php:6991 msgid "Kit: Authorization failed. Please"92 msgstr ""93 94 #: admin/class-convertkit-admin-notices.php:7395 #: views/backend/post/no-api-key.php:1996 msgid "connect your Kit account."97 msgstr ""98 99 90 #: admin/class-convertkit-admin-post.php:93 100 91 msgid "Add New" … … 276 267 277 268 #: admin/section/class-convertkit-admin-section-broadcasts.php:501 278 #: admin/section/class-convertkit-admin-section-general.php:6 89269 #: admin/section/class-convertkit-admin-section-general.php:673 279 270 #: views/backend/post/bulk-edit.php:30 280 271 #: views/backend/post/bulk-edit.php:53 … … 396 387 msgstr "" 397 388 398 #: admin/section/class-convertkit-admin-section-general.php:2 95389 #: admin/section/class-convertkit-admin-section-general.php:279 399 390 msgid "Account Name" 400 391 msgstr "" 401 392 402 393 #. translators: Post Type Name, plural 403 #: admin/section/class-convertkit-admin-section-general.php:3 16394 #: admin/section/class-convertkit-admin-section-general.php:300 404 395 #, php-format 405 396 msgid "Default Form (%s)" … … 407 398 408 399 #. translators: Post Type Name, plural 409 #: admin/section/class-convertkit-admin-section-general.php:3 34400 #: admin/section/class-convertkit-admin-section-general.php:318 410 401 #, php-format 411 402 msgid "Form Position (%s)" 412 403 msgstr "" 413 404 414 #: admin/section/class-convertkit-admin-section-general.php:3 65405 #: admin/section/class-convertkit-admin-section-general.php:349 415 406 msgid "Default Forms (Site Wide)" 416 407 msgstr "" 417 408 418 #: admin/section/class-convertkit-admin-section-general.php:3 76409 #: admin/section/class-convertkit-admin-section-general.php:360 419 410 msgid "Behavior" 420 411 msgstr "" 421 412 422 #: admin/section/class-convertkit-admin-section-general.php:3 88413 #: admin/section/class-convertkit-admin-section-general.php:372 423 414 msgid "Limit Display" 424 415 msgstr "" 425 416 426 #: admin/section/class-convertkit-admin-section-general.php: 400417 #: admin/section/class-convertkit-admin-section-general.php:384 427 418 msgid "reCAPTCHA: Site Key" 428 419 msgstr "" 429 420 430 #: admin/section/class-convertkit-admin-section-general.php: 407421 #: admin/section/class-convertkit-admin-section-general.php:391 431 422 msgid "Enter your Google reCAPTCHA v3 Site Key. When specified, this will be used to reduce spam signups." 432 423 msgstr "" 433 424 434 #: admin/section/class-convertkit-admin-section-general.php: 413425 #: admin/section/class-convertkit-admin-section-general.php:397 435 426 msgid "reCAPTCHA: Secret Key" 436 427 msgstr "" 437 428 429 #: admin/section/class-convertkit-admin-section-general.php:404 430 msgid "Enter your Google reCAPTCHA v3 Secret Key. When specified, this will be used to reduce spam signups." 431 msgstr "" 432 433 #: admin/section/class-convertkit-admin-section-general.php:410 434 msgid "reCAPTCHA: Minimum Score" 435 msgstr "" 436 438 437 #: 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:426443 msgid "reCAPTCHA: Minimum Score"444 msgstr ""445 446 #: admin/section/class-convertkit-admin-section-general.php:436447 438 msgid "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)." 448 439 msgstr "" 449 440 450 #: admin/section/class-convertkit-admin-section-general.php:4 44441 #: admin/section/class-convertkit-admin-section-general.php:428 451 442 msgid "Debug" 452 443 msgstr "" 453 444 454 #: admin/section/class-convertkit-admin-section-general.php:4 55445 #: admin/section/class-convertkit-admin-section-general.php:439 455 446 msgid "Disable JavaScript" 456 447 msgstr "" 457 448 458 #: admin/section/class-convertkit-admin-section-general.php:4 66449 #: admin/section/class-convertkit-admin-section-general.php:450 459 450 msgid "Disable CSS" 460 451 msgstr "" 461 452 462 #: admin/section/class-convertkit-admin-section-general.php:4 77453 #: admin/section/class-convertkit-admin-section-general.php:461 463 454 msgid "Usage Tracking" 464 455 msgstr "" 465 456 466 #: admin/section/class-convertkit-admin-section-general.php:4 94457 #: admin/section/class-convertkit-admin-section-general.php:478 467 458 msgid "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." 468 459 msgstr "" 469 460 470 #: admin/section/class-convertkit-admin-section-general.php:4 95461 #: admin/section/class-convertkit-admin-section-general.php:479 471 462 msgid "You can also configure non-inline forms to display site wide under the \"Non-inline Forms\" section below." 472 463 msgstr "" 473 464 474 #: admin/section/class-convertkit-admin-section-general.php:4 96465 #: admin/section/class-convertkit-admin-section-general.php:480 475 466 msgid "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." 476 467 msgstr "" 477 468 478 469 #. translators: [convertkit] shortcode, wrapped in <code> tags 479 #: admin/section/class-convertkit-admin-section-general.php: 501470 #: admin/section/class-convertkit-admin-section-general.php:485 480 471 #, php-format 481 472 msgid "The default form can be inserted into the middle of post or page content by using either the %s shortcode or block." 482 473 msgstr "" 483 474 484 #: admin/section/class-convertkit-admin-section-general.php:5 18475 #: admin/section/class-convertkit-admin-section-general.php:502 485 476 msgid "Defines non-inline forms to display site wide, and if these forms should display on Pages / Posts that have the Kit Form setting = None." 486 477 msgstr "" 487 478 488 #: admin/section/class-convertkit-admin-section-general.php:5 30479 #: admin/section/class-convertkit-admin-section-general.php:514 489 480 msgid "Configure reCAPTCHA to protect the Member Content signup form and Form Builder block from spam and abuse." 490 481 msgstr "" 491 482 492 #: admin/section/class-convertkit-admin-section-general.php:5 42483 #: admin/section/class-convertkit-admin-section-general.php:526 493 484 msgid "Defines advanced configuration settings, usually when working with support or needing to disable JS or CSS." 494 485 msgstr "" 495 486 496 #: admin/section/class-convertkit-admin-section-general.php:5 71487 #: admin/section/class-convertkit-admin-section-general.php:555 497 488 msgid "(Not specified)" 498 489 msgstr "" 499 490 500 #: admin/section/class-convertkit-admin-section-general.php:5 86491 #: admin/section/class-convertkit-admin-section-general.php:570 501 492 msgid "Disconnect" 502 493 msgstr "" 503 494 504 #: admin/section/class-convertkit-admin-section-general.php:6 49495 #: admin/section/class-convertkit-admin-section-general.php:633 505 496 msgid "No Forms exist in Kit." 506 497 msgstr "" 507 498 499 #: admin/section/class-convertkit-admin-section-general.php:634 500 msgid "Click here to create your first form" 501 msgstr "" 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 507 msgid "Select a form above to automatically output below all %s." 508 msgstr "" 509 508 510 #: 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 521 512 #: includes/class-convertkit-broadcasts-exporter.php:150 522 513 #: views/backend/setup-wizard/convertkit-setup/content-2.php:79 … … 525 516 msgstr "" 526 517 527 #: admin/section/class-convertkit-admin-section-general.php:6 67528 #: admin/section/class-convertkit-admin-section-general.php: 804518 #: admin/section/class-convertkit-admin-section-general.php:651 519 #: admin/section/class-convertkit-admin-section-general.php:788 529 520 msgid "to preview how this will display." 530 521 msgstr "" 531 522 532 523 #. translators: Post type singular name 533 #: admin/section/class-convertkit-admin-section-general.php:7 16524 #: admin/section/class-convertkit-admin-section-general.php:700 534 525 #, php-format 535 526 msgid "Before %s content" … … 537 528 538 529 #. translators: Post type singular name 539 #: admin/section/class-convertkit-admin-section-general.php:7 21530 #: admin/section/class-convertkit-admin-section-general.php:705 540 531 #, php-format 541 532 msgid "After %s content" … … 543 534 544 535 #. translators: Post type singular name 545 #: admin/section/class-convertkit-admin-section-general.php:7 26536 #: admin/section/class-convertkit-admin-section-general.php:710 546 537 #, php-format 547 538 msgid "Before and after %s content" 548 539 msgstr "" 549 540 550 #: admin/section/class-convertkit-admin-section-general.php:7 29541 #: admin/section/class-convertkit-admin-section-general.php:713 551 542 msgid "After element" 552 543 msgstr "" 553 544 554 545 #. translators: Post Type name, plural 555 #: admin/section/class-convertkit-admin-section-general.php:7 33546 #: admin/section/class-convertkit-admin-section-general.php:717 556 547 #, php-format 557 548 msgid "Where forms should display relative to the %s content" 558 549 msgstr "" 559 550 560 #: admin/section/class-convertkit-admin-section-general.php:7 68551 #: admin/section/class-convertkit-admin-section-general.php:752 561 552 msgid "Paragraphs" 562 553 msgstr "" 563 554 564 #: admin/section/class-convertkit-admin-section-general.php:7 69555 #: admin/section/class-convertkit-admin-section-general.php:753 565 556 msgid "Headings <h2>" 566 557 msgstr "" 567 558 568 #: admin/section/class-convertkit-admin-section-general.php:7 70559 #: admin/section/class-convertkit-admin-section-general.php:754 569 560 msgid "Headings <h3>" 570 561 msgstr "" 571 562 572 #: admin/section/class-convertkit-admin-section-general.php:7 71563 #: admin/section/class-convertkit-admin-section-general.php:755 573 564 msgid "Headings <h4>" 574 565 msgstr "" 575 566 576 #: admin/section/class-convertkit-admin-section-general.php:7 72567 #: admin/section/class-convertkit-admin-section-general.php:756 577 568 msgid "Headings <h5>" 578 569 msgstr "" 579 570 580 #: admin/section/class-convertkit-admin-section-general.php:7 73571 #: admin/section/class-convertkit-admin-section-general.php:757 581 572 msgid "Headings <h6>" 582 573 msgstr "" 583 574 584 #: admin/section/class-convertkit-admin-section-general.php:7 74575 #: admin/section/class-convertkit-admin-section-general.php:758 585 576 msgid "Images" 586 577 msgstr "" 587 578 588 #: admin/section/class-convertkit-admin-section-general.php:7 76579 #: admin/section/class-convertkit-admin-section-general.php:760 589 580 msgid "The number of elements before outputting the form." 590 581 msgstr "" 591 582 592 #: admin/section/class-convertkit-admin-section-general.php:7 93583 #: admin/section/class-convertkit-admin-section-general.php:777 593 584 msgid "No non-inline Forms exist in Kit." 594 585 msgstr "" 595 586 596 #: admin/section/class-convertkit-admin-section-general.php:7 94587 #: admin/section/class-convertkit-admin-section-general.php:778 597 588 msgid "Click here to create your first modal, slide in or sticky bar form" 598 589 msgstr "" 599 590 600 #: admin/section/class-convertkit-admin-section-general.php: 802591 #: admin/section/class-convertkit-admin-section-general.php:786 601 592 msgid "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." 602 593 msgstr "" 603 594 604 #: admin/section/class-convertkit-admin-section-general.php:8 40595 #: admin/section/class-convertkit-admin-section-general.php:824 605 596 msgid "If checked, do not display the site wide form(s) above on Pages / Posts that have their Kit Form setting = None." 606 597 msgstr "" 607 598 608 #: admin/section/class-convertkit-admin-section-general.php:8 60599 #: admin/section/class-convertkit-admin-section-general.php:844 609 600 msgid "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." 610 601 msgstr "" 611 602 612 #: admin/section/class-convertkit-admin-section-general.php:9 43603 #: admin/section/class-convertkit-admin-section-general.php:927 613 604 msgid "Log requests to file and output browser console messages." 614 605 msgstr "" 615 606 616 #: admin/section/class-convertkit-admin-section-general.php:9 44607 #: admin/section/class-convertkit-admin-section-general.php:928 617 608 msgid "You can ignore this unless you're working with our support team to resolve an issue. Decheck this option to improve performance." 618 609 msgstr "" 619 610 620 #: admin/section/class-convertkit-admin-section-general.php:9 61611 #: admin/section/class-convertkit-admin-section-general.php:945 621 612 msgid "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!" 622 613 msgstr "" 623 614 624 #: admin/section/class-convertkit-admin-section-general.php:9 78615 #: admin/section/class-convertkit-admin-section-general.php:962 625 616 msgid "Prevents loading plugin CSS files. This will disable styling on broadcasts, form trigger buttons, product buttons and member's content. Use with caution!" 626 617 msgstr "" 627 618 628 #: admin/section/class-convertkit-admin-section-general.php:9 82619 #: admin/section/class-convertkit-admin-section-general.php:966 629 620 msgid "To customize forms and their styling, use the" 630 621 msgstr "" 631 622 632 #: admin/section/class-convertkit-admin-section-general.php:9 84623 #: admin/section/class-convertkit-admin-section-general.php:968 633 624 msgid "Kit form editor" 634 625 msgstr "" 635 626 636 #: admin/section/class-convertkit-admin-section-general.php:9 86627 #: admin/section/class-convertkit-admin-section-general.php:970 637 628 msgid "For creators who require form designs to follow their WordPress theme, use the Kit Form Builder block in the block editor." 638 629 msgstr "" 639 630 640 #: admin/section/class-convertkit-admin-section-general.php:9 89631 #: admin/section/class-convertkit-admin-section-general.php:973 641 632 msgid "For developers who require custom form designs through use of CSS, consider using the" 642 633 msgstr "" 643 634 644 #: admin/section/class-convertkit-admin-section-general.php:9 90635 #: admin/section/class-convertkit-admin-section-general.php:974 645 636 msgid "or" 646 637 msgstr "" 647 638 648 #: admin/section/class-convertkit-admin-section-general.php:9 91639 #: admin/section/class-convertkit-admin-section-general.php:975 649 640 msgid "integrations." 650 641 msgstr "" 651 642 652 #: admin/section/class-convertkit-admin-section-general.php: 1010643 #: admin/section/class-convertkit-admin-section-general.php:994 653 644 msgid "By allowing us to collect usage data, we can better understand which WordPress configurations, themes and plugins we should test." 654 645 msgstr "" 655 646 656 #: admin/section/class-convertkit-admin-section-general.php: 1014647 #: admin/section/class-convertkit-admin-section-general.php:998 657 648 msgid "Complete documentation on usage tracking can be found" 658 649 msgstr "" 659 650 660 #: admin/section/class-convertkit-admin-section-general.php:10 16651 #: admin/section/class-convertkit-admin-section-general.php:1000 661 652 #: views/backend/setup-wizard/convertkit-setup/content-2.php:150 662 653 msgid "here" … … 844 835 msgstr "" 845 836 846 #: admin/section/class-convertkit-admin-section-tools.php:353 837 #: admin/section/class-convertkit-admin-section-tools.php:60 838 msgid "MC4WP forms migrated successfully." 839 msgstr "" 840 841 #: admin/section/class-convertkit-admin-section-tools.php:396 847 842 msgid "Tools to help you manage Kit on your site." 848 843 msgstr "" 849 844 850 #: admin/section/class-convertkit-admin-section-tools.php: 381845 #: admin/section/class-convertkit-admin-section-tools.php:424 851 846 msgid "WordPress 5.2 or higher is required for system information report." 852 847 msgstr "" … … 1418 1413 1419 1414 #: includes/blocks/class-convertkit-block-form.php:106 1415 #: views/backend/settings/tools.php:119 1420 1416 #: views/backend/term/fields-add.php:11 1421 1417 #: views/backend/term/fields-edit.php:12 … … 1513 1509 msgstr "" 1514 1510 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 1512 msgid "Kit: Authorization failed. Please" 1513 msgstr "" 1514 1515 #: includes/class-convertkit-admin-notices.php:73 1516 #: views/backend/post/no-api-key.php:19 1517 msgid "connect your Kit account." 1518 msgstr "" 1519 1520 #: includes/class-convertkit-ajax.php:43 1524 1521 msgid "Kit: Required parameter `email` not included in AJAX request." 1525 1522 msgstr "" 1526 1523 1527 #: includes/class-convertkit-ajax.php: 1321524 #: includes/class-convertkit-ajax.php:49 1528 1525 msgid "Kit: Required parameter `email` is empty." 1529 1526 msgstr "" 1530 1527 1531 #: includes/class-convertkit-ajax.php: 1371528 #: includes/class-convertkit-ajax.php:54 1532 1529 msgid "Kit: Required parameter `email` is not an email address." 1533 1530 msgstr "" … … 2108 2105 #: views/backend/settings/tools.php:95 2109 2106 msgid "Import" 2107 msgstr "" 2108 2109 #: views/backend/settings/tools.php:109 2110 msgid "MC4WP: Migrate Configuration" 2111 msgstr "" 2112 2113 #: views/backend/settings/tools.php:112 2114 msgid "Automatically replace MC4WP form shortcodes with Kit forms." 2115 msgstr "" 2116 2117 #: views/backend/settings/tools.php:118 2118 msgid "MC4WP Form" 2119 msgstr "" 2120 2121 #: views/backend/settings/tools.php:149 2122 msgid "Migrate" 2110 2123 msgstr "" 2111 2124 -
convertkit/trunk/readme.txt
r3390429 r3399438 6 6 Tested up to: 6.8 7 7 Requires PHP: 7.1 8 Stable tag: 3. 0.88 Stable tag: 3.1.0 9 9 License: GPLv3 or later 10 10 License URI: https://www.gnu.org/licenses/gpl-3.0.html … … 180 180 == Changelog == 181 181 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 182 194 ### 3.0.8 2025-11-05 183 195 * Fix: Member Content: Product: Display 'no access' notice when logged in and no access -
convertkit/trunk/resources/backend/js/gutenberg.js
r3390429 r3399438 747 747 */ 748 748 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 754 749 // Disable the button. 755 750 if (typeof setButtonDisabled !== 'undefined') { … … 758 753 759 754 // 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 }, 764 761 }) 765 762 .then(function (response) { … … 768 765 }) 769 766 .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 770 783 // Update global ConvertKit Blocks object, so that any updated resources 771 784 // are reflected when adding new ConvertKit Blocks. 772 convertkit_blocks = response .data;785 convertkit_blocks = response; 773 786 774 787 // Update this block's properties, so that has_access_token, has_resources … … 791 804 wp.data 792 805 .dispatch('core/notices') 793 .createErrorNotice(' ConvertKit: ' + error, {806 .createErrorNotice('Kit: ' + error, { 794 807 id: 'convertkit-error', 795 808 }); 796 809 797 810 // Enable refresh button. 798 setButtonDisabled(false); 811 if (typeof setButtonDisabled !== 'undefined') { 812 setButtonDisabled(false); 813 } 799 814 }); 800 815 }; -
convertkit/trunk/resources/backend/js/refresh-resources.js
r3390429 r3399438 50 50 51 51 // Perform AJAX request to refresh resource. 52 fetch(convertkit_admin_refresh_resources.ajaxurl , {52 fetch(convertkit_admin_refresh_resources.ajaxurl + resource, { 53 53 method: 'POST', 54 54 headers: { 55 'Content-Type': 'application/x-www-form-urlencoded', 55 'Content-Type': 'application/json', 56 'X-WP-Nonce': convertkit_admin_refresh_resources.nonce, 56 57 }, 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 }),62 58 }) 63 59 .then(function (response) { … … 71 67 72 68 // Show an error if the request wasn't successful. 73 if ( !response.success) {69 if (typeof response.code !== 'undefined') { 74 70 // Show error notice. 75 convertKitRefreshResourcesOutputErrorNotice(response. data);71 convertKitRefreshResourcesOutputErrorNotice(response.message); 76 72 77 73 // Enable button and remove is-refreshing class. … … 103 99 // Populate select `optgroup`` from response data, which comprises of Tags and Products. 104 100 // Tags. 105 response. data.tags.forEach(function (item) {101 response.tags.forEach(function (item) { 106 102 document 107 103 .querySelector( … … 119 115 120 116 // Products. 121 response. data.products.forEach(function (item) {117 response.products.forEach(function (item) { 122 118 document 123 119 .querySelector( … … 137 133 default: 138 134 // Populate select options from response data. 139 response. data.forEach(function (item) {135 response.forEach(function (item) { 140 136 // Define label. 141 137 let label = ''; -
convertkit/trunk/resources/frontend/js/convertkit.js
r3369648 r3399438 213 213 // Set a cookie if any scripts with data-kit-limit-per-session attribute exist. 214 214 if ( 215 document.querySelectorAll('script[data-kit-limit-per-session ]').length >216 0215 document.querySelectorAll('script[data-kit-limit-per-session="1"]') 216 .length > 0 217 217 ) { 218 218 document.cookie = 'ck_non_inline_form_displayed=1; path=/'; -
convertkit/trunk/resources/frontend/js/restrict-content.js
r3369648 r3399438 78 78 // Code submission. 79 79 convertKitRestrictContentSubscriberVerification( 80 e.target.querySelector('input[name="_wpnonce"]').value,80 convertkit_restrict_content.nonce, 81 81 e.target.querySelector('input[name="subscriber_code"]').value, 82 82 e.target.querySelector('input[name="token"]').value, … … 89 89 // Email submission. 90 90 convertKitRestrictContentSubscriberAuthenticationSendCode( 91 e.target.querySelector('input[name="_wpnonce"]').value,91 convertkit_restrict_content.nonce, 92 92 e.target.querySelector('input[name="convertkit_email"]').value, 93 93 e.target.querySelector('input[name="convertkit_resource_type"]').value, … … 128 128 129 129 /** 130 * Submits the given email address to maybe_run_subscriber_authentication(), which131 * 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: 132 132 * - the email form view, with an error message e.g. invalid email, 133 133 * - the code form view, where the user can enter the OTP. … … 136 136 * 137 137 * @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 ( ConvertKitTag 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). 141 141 * @param {number} post_id WordPress Post ID being viewed / accessed. 142 142 */ … … 148 148 post_id 149 149 ) { 150 fetch(convertkit_restrict_content. ajaxurl, {150 fetch(convertkit_restrict_content.subscriber_authentication_url, { 151 151 method: 'POST', 152 152 headers: { 153 153 'Content-Type': 'application/x-www-form-urlencoded', 154 'X-WP-Nonce': nonce, 154 155 }, 155 156 body: new URLSearchParams({ 156 action: 'convertkit_subscriber_authentication_send_code',157 _wpnonce: nonce,158 157 convertkit_email: email, 159 158 convertkit_resource_type: resource_type, … … 174 173 } 175 174 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 } 180 188 181 189 // Hide loading overlay. … … 195 203 196 204 /** 197 * Submits the given email address to maybe_run_subscriber_verification(), which198 * 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: 199 207 * - the code form view, with an error message e.g. invalid code entered, 200 208 * - the Post's URL, with a `ck-cache-bust` parameter appended, which can then be loaded to show the content. … … 213 221 post_id 214 222 ) { 215 fetch(convertkit_restrict_content. ajaxurl, {223 fetch(convertkit_restrict_content.subscriber_verification_url, { 216 224 method: 'POST', 217 225 headers: { 218 226 'Content-Type': 'application/x-www-form-urlencoded', 227 'X-WP-Nonce': nonce, 219 228 }, 220 229 body: new URLSearchParams({ 221 action: 'convertkit_subscriber_verification',222 _wpnonce: nonce,223 230 subscriber_code, 224 231 token, … … 255 262 256 263 // Code entered is valid; load the URL in the response data. 257 window.location = result. data;264 window.location = result.url; 258 265 }) 259 266 .catch(function (error) { -
convertkit/trunk/vendor/convertkit/convertkit-wordpress-libraries/src/class-convertkit-api-v4.php
r3369648 r3399438 378 378 if ( is_wp_error( $result ) ) { 379 379 $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 380 391 return $result; 381 392 } … … 415 426 if ( is_wp_error( $result ) ) { 416 427 $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 417 439 return $result; 418 440 } … … 1433 1455 // Return the API error message as a WP_Error if the HTTP response code is a 4xx code. 1434 1456 if ( $http_response_code >= 400 ) { 1457 1435 1458 // Define the error message. 1436 1459 $error = $this->get_error_message_string( $response ); … … 1439 1462 1440 1463 switch ( $http_response_code ) { 1441 // If the HTTP response code is 401, and the error matches 'The access token expired', refresh the access token now1442 // and re-attempt the request.1443 1464 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 ); 1446 1504 } 1447 1505 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 from1450 // reaching a race condition where the staging site refreshes the token first, resulting in1451 // 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.1468 1506 case 429: 1469 1507 // If retry on rate limit hit is disabled, return a WP_Error. … … 1489 1527 1490 1528 return $response; 1491 1492 }1493 1494 /**1495 * Helper method to determine the WordPress environment type, checking1496 * if the wp_get_environment_type() function exists in WordPress (versions1497 * older than WordPress 5.5 won't have this function).1498 *1499 * @since 2.0.21500 *1501 * @return bool1502 */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' );1512 1529 1513 1530 } -
convertkit/trunk/views/backend/settings/tools.php
r3164741 r3399438 103 103 104 104 <?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 105 160 wp_nonce_field( 'convertkit-settings-tools', '_convertkit_settings_tools_nonce' ); 106 161 ?> -
convertkit/trunk/wp-convertkit.php
r3390429 r3399438 10 10 * Plugin URI: https://kit.com/ 11 11 * Description: Display Kit (formerly ConvertKit) email subscription forms, landing pages, products, broadcasts and more. 12 * Version: 3. 0.812 * Version: 3.1.0 13 13 * Author: Kit 14 14 * Author URI: https://kit.com/ … … 28 28 define( 'CONVERTKIT_PLUGIN_URL', plugin_dir_url( __FILE__ ) ); 29 29 define( 'CONVERTKIT_PLUGIN_PATH', __DIR__ ); 30 define( 'CONVERTKIT_PLUGIN_VERSION', '3. 0.8' );30 define( 'CONVERTKIT_PLUGIN_VERSION', '3.1.0' ); 31 31 define( 'CONVERTKIT_OAUTH_CLIENT_ID', 'HXZlOCj-K5r0ufuWCtyoyo3f688VmMAYSsKg1eGvw0Y' ); 32 32 define( 'CONVERTKIT_OAUTH_CLIENT_REDIRECT_URI', 'https://app.kit.com/wordpress/redirect' ); … … 53 53 require_once CONVERTKIT_PLUGIN_PATH . '/includes/functions.php'; 54 54 require_once CONVERTKIT_PLUGIN_PATH . '/includes/class-wp-convertkit.php'; 55 require_once CONVERTKIT_PLUGIN_PATH . '/includes/class-convertkit-admin-notices.php'; 55 56 require_once CONVERTKIT_PLUGIN_PATH . '/includes/class-convertkit-ajax.php'; 56 57 require_once CONVERTKIT_PLUGIN_PATH . '/includes/class-convertkit-broadcasts-exporter.php'; … … 109 110 require_once CONVERTKIT_PLUGIN_PATH . '/admin/class-convertkit-admin-category.php'; 110 111 require_once CONVERTKIT_PLUGIN_PATH . '/admin/class-convertkit-admin-landing-page.php'; 111 require_once CONVERTKIT_PLUGIN_PATH . '/admin/class-convertkit-admin-notices.php';112 112 require_once CONVERTKIT_PLUGIN_PATH . '/admin/class-convertkit-admin-post.php'; 113 113 require_once CONVERTKIT_PLUGIN_PATH . '/admin/class-convertkit-admin-refresh-resources.php'; … … 117 117 require_once CONVERTKIT_PLUGIN_PATH . '/admin/class-convertkit-admin-setup-wizard.php'; 118 118 require_once CONVERTKIT_PLUGIN_PATH . '/admin/class-convertkit-wp-list-table.php'; 119 require_once CONVERTKIT_PLUGIN_PATH . '/admin/importers/class-convertkit-admin-importer.php'; 120 require_once CONVERTKIT_PLUGIN_PATH . '/admin/importers/class-convertkit-admin-importer-mc4wp.php'; 119 121 require_once CONVERTKIT_PLUGIN_PATH . '/admin/section/class-convertkit-admin-section-base.php'; 120 122 require_once CONVERTKIT_PLUGIN_PATH . '/admin/section/class-convertkit-admin-section-broadcasts.php';
Note: See TracChangeset
for help on using the changeset viewer.