PluginProbe
Kit (formerly ConvertKit) – Email Newsletter, Email Marketing, Membership, Subscribers and Landing Pages / 2.5.5
Kit (formerly ConvertKit) – Email Newsletter, Email Marketing, Membership, Subscribers and Landing Pages v2.5.5
3.4.4 3.4.3 3.4.2 3.4.1 3.4.0 3.3.9 3.3.8 3.3.7 3.3.6 3.3.5 3.3.4 3.3.3 3.3.2 3.3.1 2.2.0 2.2.1 2.2.2 2.2.3 2.2.4 2.2.5 2.2.6 2.2.7 2.2.8 2.2.9 2.3.0 All 197 releases
← All changes | includes/class-convertkit-output.php +71 -296 3.4.02.5.5 View file →
@@ -66,8 +66,9 @@
66 66 * @since 1.9.6
67 67 */
68 68 public function __construct() {
69 69
70 + add_action( 'init', array( $this, 'get_subscriber_id_from_request' ) );
70 71 add_action( 'wp', array( $this, 'maybe_tag_subscriber' ) );
71 72 add_action( 'template_redirect', array( $this, 'output_form' ) );
72 73 add_action( 'template_redirect', array( $this, 'page_takeover' ) );
73 74 add_action( 'wp_enqueue_scripts', array( $this, 'enqueue_scripts' ) );
@@ -87,8 +88,13 @@
87 88 * @since 2.4.9.1
88 89 */
89 90 public function maybe_tag_subscriber() {
90 91
92 + // Bail if no subscriber ID detected.
93 + if ( ! $this->subscriber_id ) {
94 + return;
95 + }
96 +
91 97 // Bail if not a singular Post Type supported by ConvertKit.
92 98 if ( ! is_singular( convertkit_get_supported_post_types() ) ) {
93 99 return;
94 100 }
@@ -105,10 +111,10 @@
105 111 if ( ! $this->settings ) {
106 112 $this->settings = new ConvertKit_Settings();
107 113 }
108 114
109 - // Bail if the API hasn't been configured.
110 - if ( ! $this->settings->has_access_and_refresh_token() ) {
115 + // Bail if the API if an API Key and Secret is not defined.
116 + if ( ! $this->settings->has_api_key_and_secret() ) {
111 117 return;
112 118 }
113 119
114 120 // Get ConvertKit Post's Settings, if they have not yet been loaded.
@@ -120,16 +126,8 @@
120 126 if ( ! $this->post_settings->has_tag() ) {
121 127 return;
122 128 }
123 129
124 - // Get subscriber ID from URL or cookie.
125 - $this->get_subscriber_id_from_request();
126 -
127 - // Bail if no subscriber ID detected.
128 - if ( ! $this->subscriber_id ) {
129 - return;
130 - }
131 -
132 130 // Initialize the API.
133 131 $api = new ConvertKit_API_V4(
134 132 CONVERTKIT_OAUTH_CLIENT_ID,
135 133 CONVERTKIT_OAUTH_CLIENT_REDIRECT_URI,
@@ -138,23 +136,8 @@
138 136 $this->settings->debug_enabled(),
139 137 'output'
140 138 );
141 139
142 - // If the subscriber ID is not numeric, it's a cryptographically-signed subscriber ID, set
143 - // when using the Member Content functionality by Kit's API.
144 - // Fetch the underlying subscriber ID for the tag_subscriber() method.
145 - if ( ! is_numeric( $this->subscriber_id ) ) {
146 - $result = $api->profile( $this->subscriber_id );
147 -
148 - // If an error occurred, the subscriber ID is invalid.
149 - if ( is_wp_error( $result ) ) {
150 - return;
151 - }
152 -
153 - // Set the subscriber ID.
154 - $this->subscriber_id = $result['id'];
155 - }
156 -
157 140 // Tag subscriber.
158 141 $api->tag_subscriber( $this->post_settings->get_tag(), $this->subscriber_id );
159 142
160 143 }
@@ -234,9 +217,9 @@
234 217
235 218 // Get Landing Page.
236 219 $landing_page = $this->landing_pages->get_html( $this->post_settings->get_landing_page() );
237 220
238 - // Bail if an error occurred.
221 + // Bail if an error occured.
239 222 if ( is_wp_error( $landing_page ) ) {
240 223 return;
241 224 }
242 225
@@ -265,9 +248,9 @@
265 248
266 249 }
267 250
268 251 /**
269 - * Inserts a form to the singular Page, Post or Custom Post Type's Content.
252 + * Appends a form to the singular Page, Post or Custom Post Type's Content.
270 253 *
271 254 * @param string $content Post Content.
272 255 * @return string Post Content with Form Appended, if applicable
273 256 */
@@ -277,17 +260,10 @@
277 260 if ( ! is_singular( convertkit_get_supported_post_types() ) ) {
278 261 return $content;
279 262 }
280 263
281 - // Get Post ID.
264 + // Get Post ID and ConvertKit Form ID for the Post.
282 265 $post_id = get_the_ID();
283 -
284 - // Bail if Post Type is not supported by Kit.
285 - if ( ! in_array( get_post_type( $post_id ), convertkit_get_supported_post_types(), true ) ) {
286 - return $content;
287 - }
288 -
289 - // Get Form ID for the Post.
290 266 $form_id = $this->get_post_form_id( $post_id );
291 267
292 268 /**
293 269 * Define the ConvertKit Form ID to display for the given Post ID,
@@ -312,17 +288,17 @@
312 288 $this->forms = new ConvertKit_Resource_Forms( 'output_form' );
313 289 }
314 290
315 291 // Get Form HTML.
316 - $form = $this->forms->get_html( $form_id, $post_id );
292 + $form = $this->forms->get_html( $form_id );
317 293
318 - // If an error occurred, it could be because the specified Form ID for the Post either:
294 + // If an error occured, it could be because the specified Form ID for the Post either:
319 295 // - belongs to another ConvertKit account (i.e. API credentials were changed in the Plugin, but this Post's specified Form was not changed), or
320 296 // - the form was deleted from the ConvertKit account.
321 297 // Attempt to fallback to the default form for this Post Type.
322 298 if ( is_wp_error( $form ) ) {
323 299 if ( $this->settings->debug_enabled() ) {
324 - $content .= '<!-- Kit append_form_to_content(): ' . $form->get_error_message() . ' Attempting fallback to Default Form. -->';
300 + $content .= '<!-- ConvertKit append_form_to_content(): ' . $form->get_error_message() . ' Attempting fallback to Default Form. -->';
325 301 }
326 302
327 303 // Get Default Form ID for this Post's Type.
328 304 $form_id = $this->settings->get_default_form( get_post_type( $post_id ) );
@@ -329,9 +305,9 @@
329 305
330 306 // If no Default Form is specified, just return the Post Content, unedited.
331 307 if ( ! $form_id ) {
332 308 if ( $this->settings->debug_enabled() ) {
333 - $content .= '<!-- Kit append_form_to_content(): No Default Form exists as a fallback. -->';
309 + $content .= '<!-- ConvertKit append_form_to_content(): No Default Form exists as a fallback. -->';
334 310 }
335 311
336 312 return $content;
337 313 }
@@ -336,15 +312,15 @@
336 312 return $content;
337 313 }
338 314
339 315 // Get Form HTML.
340 - $form = $this->forms->get_html( $form_id, $post_id );
316 + $form = $this->forms->get_html( $form_id );
341 317
342 - // If an error occurred again, the default form doesn't exist in this ConvertKit account.
318 + // If an error occured again, the default form doesn't exist in this ConvertKit account.
343 319 // Just return the Post Content, unedited.
344 320 if ( is_wp_error( $form ) ) {
345 321 if ( $this->settings->debug_enabled() ) {
346 - $content .= '<!-- Kit append_form_to_content(): Default Form: ' . $form->get_error_message() . ' -->';
322 + $content .= '<!-- ConvertKit append_form_to_content(): Default Form: ' . $form->get_error_message() . ' -->';
347 323 }
348 324
349 325 return $content;
350 326 }
@@ -349,70 +325,23 @@
349 325 return $content;
350 326 }
351 327 }
352 328
353 - // If the Form HTML is empty, it's a modal form that has been set to load in the footer of the site.
354 - // We don't need to append anything to the content.
355 - if ( empty( $form ) ) {
356 - if ( $this->settings->debug_enabled() ) {
357 - $content .= '<!-- Kit append_form_to_content(): Form is non-inline, appended to footer. -->';
358 - }
359 -
360 - return $content;
361 - }
362 -
363 329 // If here, we have a ConvertKit Form.
364 - // Append form to Post's Content, based on the position setting.
365 - $form_position = $this->settings->get_default_form_position( get_post_type( $post_id ) );
330 + // Append form to Post's Content.
331 + $content = $content .= $form;
366 332
367 - if ( $this->settings->debug_enabled() ) {
368 - $content .= '<!-- Kit append_form_to_content(): Form Position: ' . esc_html( $form_position ) . ' -->';
369 - }
370 -
371 - switch ( $form_position ) {
372 - case 'before_after_content':
373 - $content = $form . $content . $form;
374 - break;
375 -
376 - case 'before_content':
377 - $content = $form . $content;
378 - break;
379 -
380 - case 'after_element':
381 - $element = $this->settings->get_default_form_position_element( get_post_type( $post_id ) );
382 - $index = $this->settings->get_default_form_position_element_index( get_post_type( $post_id ) );
383 -
384 - // Check if DOMDocument is installed.
385 - // It should be installed as mosts hosts include php-dom and php-xml modules.
386 - // If not, fallback to using preg_match_all(), which is less reliable.
387 - if ( ! class_exists( 'DOMDocument' ) ) {
388 - $content = $this->inject_form_after_element_fallback( $content, $element, $index, $form );
389 - break;
390 - }
391 -
392 - // Use DOMDocument.
393 - $content = $this->inject_form_after_element( $content, $element, $index, $form );
394 - break;
395 -
396 - case 'after_content':
397 - default:
398 - // Default behaviour < 2.5.8 was to append the Form after the content.
399 - $content .= $form;
400 - break;
401 - }
402 -
403 333 /**
404 334 * Filter the Post's Content, which includes a ConvertKit Form, immediately before it is output.
405 335 *
406 336 * @since 1.9.6
407 337 *
408 - * @param string $content Post Content
409 - * @param string $form ConvertKit Form HTML
410 - * @param int $post_id Post ID
411 - * @param int $form_id ConvertKit Form ID
412 - * @param string $form_position Form Position setting for the Post's Type.
338 + * @param string $content Post Content
339 + * @param string $form ConvertKit Form HTML
340 + * @param int $post_id Post ID
341 + * @param int $form_id ConvertKit Form ID
413 342 */
414 - $content = apply_filters( 'convertkit_frontend_append_form', $content, $form, $post_id, $form_id, $form_position );
343 + $content = apply_filters( 'convertkit_frontend_append_form', $content, $form, $post_id, $form_id );
415 344
416 345 return $content;
417 346
418 347 }
@@ -417,119 +346,8 @@
417 346
418 347 }
419 348
420 349 /**
421 - * Injects the form after the given element and index, using DOMDocument.
422 - *
423 - * @since 2.6.2
424 - *
425 - * @param string $content Page / Post Content.
426 - * @param string $tag HTML tag to insert form after.
427 - * @param int $index Number of $tag elements to find before inserting form.
428 - * @param string $form Form HTML to inject.
429 - * @return string
430 - */
431 - private function inject_form_after_element( $content, $tag, $index, $form ) {
432 -
433 - // If the form is empty, don't inject anything.
434 - if ( empty( $form ) ) {
435 - return $content;
436 - }
437 -
438 - // Load the content into the parser.
439 - $parser = new ConvertKit_HTML_Parser( $content, LIBXML_HTML_NODEFDTD );
440 -
441 - // Find the element to append the form to.
442 - // item() is a zero based index.
443 - $element_node = $parser->html->getElementsByTagName( $tag )->item( $index - 1 );
444 -
445 - // If the element could not be found, either the number of elements by tag name is less
446 - // than the requested position the form be inserted in, or no element exists.
447 - // Append the form to the original content and return.
448 - if ( is_null( $element_node ) ) {
449 - return $content . $form;
450 - }
451 -
452 - // Load the form into the parser.
453 - $form_parser = new ConvertKit_HTML_Parser( $form, LIBXML_HTML_NODEFDTD );
454 - $form_body = $form_parser->html->getElementsByTagName( 'body' )->item( 0 );
455 -
456 - // Collect nodes first to avoid live NodeList mutation issues.
457 - $nodes_to_insert = array();
458 - foreach ( $form_body->childNodes as $child ) { // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase
459 - $nodes_to_insert[] = $parser->html->importNode( $child, true );
460 - }
461 -
462 - // Inject the form node(s) after the element node e.g. after the paragraph, heading etc.
463 - $next_sibling = $element_node->nextSibling; // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase
464 - foreach ( $nodes_to_insert as $node ) {
465 - $element_node->parentNode->insertBefore( $node, $element_node->nextSibling ); // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase
466 - }
467 -
468 - // Return modified HTML string.
469 - return $parser->get_body_html();
470 -
471 - }
472 -
473 - /**
474 - * Injects the form after the given element and index, using preg_match_all().
475 - * This is less reliable than DOMDocument, and is called if DOMDocument is
476 - * not installed on the server.
477 - *
478 - * @since 2.6.2
479 - *
480 - * @param string $content Page / Post Content.
481 - * @param string $tag HTML tag to insert form after.
482 - * @param int $index Number of $tag elements to find before inserting form.
483 - * @param string $form Form HTML to inject.
484 - * @return string
485 - */
486 - private function inject_form_after_element_fallback( $content, $tag, $index, $form ) {
487 -
488 - // If the form is empty, don't inject anything.
489 - if ( empty( $form ) ) {
490 - return $content;
491 - }
492 -
493 - // Calculate tag length.
494 - $tag_length = ( strlen( $tag ) + 3 );
495 -
496 - // Find all closing elements.
497 - preg_match_all( '/<\/' . $tag . '>/', $content, $matches );
498 -
499 - // If no elements exist, just append the form.
500 - if ( count( $matches[0] ) === 0 ) {
501 - $content = $content . $form;
502 - return $content;
503 - }
504 -
505 - // If the number of elements is less than the index, we don't have enough elements to add the form to.
506 - // Just add the form after the content.
507 - if ( count( $matches[0] ) <= $index ) {
508 - $content = $content . $form;
509 - return $content;
510 - }
511 -
512 - // Iterate through the content to find the element at the configured index e.g. find the 4th closing paragraph.
513 - $offset = 0;
514 - foreach ( $matches[0] as $element_index => $element ) {
515 - $position = strpos( $content, $element, $offset );
516 - if ( ( $element_index + 1 ) === $index ) {
517 - return substr( $content, 0, $position + 4 ) . $form . substr( $content, $position + 4 );
518 - }
519 -
520 - // Increment offset.
521 - $offset = $position + 1;
522 - }
523 -
524 - // If here, something went wrong.
525 - // Just add the form after the content.
526 - $content = $content . $form;
527 - return $content;
528 -
529 - }
530 -
531 - /**
532 350 * Registers the ConvertKit Form block to before or after the Query Loop block, when viewing a Category archive.
533 351 *
534 352 * See append_form_block_on_category_archive() configures the block to display the applicable category's Form.
535 353 *
@@ -695,17 +513,11 @@
695 513 // If a Form ID exists, return it now.
696 514 if ( $term_settings->has_form() ) {
697 515 return $term_settings->get_form();
698 516 }
699 -
700 - // If the Term specifies that no Form should be used, return false.
701 - if ( $term_settings->uses_no_form() ) {
702 - return false;
703 - }
704 517 }
705 518
706 - // If here, all Terms were set to display the Default Form.
707 - // Therefore use the Plugin's Default Form.
519 + // If here, use the Plugin's Default Form.
708 520 return $this->settings->get_default_form( get_post_type( $post_id ) );
709 521
710 522 }
711 523
@@ -746,29 +558,47 @@
746 558 * @since 1.9.6
747 559 */
748 560 public function enqueue_scripts() {
749 561
750 - // Get ConvertKit Settings and Post's Settings.
751 - $settings = new ConvertKit_Settings();
562 + // Get Post.
563 + $post = get_post();
752 564
753 - // Bail if the no scripts setting is enabled.
754 - if ( $settings->scripts_disabled() ) {
565 + // Bail if no Post could be fetched.
566 + if ( ! $post ) {
755 567 return;
756 568 }
757 569
758 - // Enqueue frontend JS.
759 - convertkit_enqueue_frontend_js();
570 + // Get ConvertKit Settings and Post's Settings.
571 + $settings = new ConvertKit_Settings();
572 + $convertkit_post = new ConvertKit_Post( $post->ID );
760 573
761 - // Define variables.
574 + // Register scripts that we might use.
575 + wp_register_script(
576 + 'convertkit-js',
577 + CONVERTKIT_PLUGIN_URL . 'resources/frontend/js/convertkit.js',
578 + array(),
579 + CONVERTKIT_PLUGIN_VERSION,
580 + true
581 + );
762 582 wp_localize_script(
763 583 'convertkit-js',
764 584 'convertkit',
765 585 array(
586 + 'ajaxurl' => admin_url( 'admin-ajax.php' ),
766 587 'debug' => $settings->debug_enabled(),
588 + 'nonce' => wp_create_nonce( 'convertkit' ),
767 589 'subscriber_id' => $this->subscriber_id,
768 590 )
769 591 );
770 592
593 + // Bail if the no scripts setting is enabled.
594 + if ( $settings->scripts_disabled() ) {
595 + return;
596 + }
597 +
598 + // Enqueue.
599 + wp_enqueue_script( 'convertkit-js' );
600 +
771 601 }
772 602
773 603 /**
774 604 * Gets the subscriber ID from the request (either the cookie or the URL).
@@ -780,9 +610,9 @@
780 610 // Use ConvertKit_Subscriber class to fetch and validate the subscriber ID.
781 611 $subscriber = new ConvertKit_Subscriber();
782 612 $subscriber_id = $subscriber->get_subscriber_id();
783 613
784 - // If an error occurred, the subscriber ID in the request/cookie is not a valid subscriber.
614 + // If an error occured, the subscriber ID in the request/cookie is not a valid subscriber.
785 615 if ( is_wp_error( $subscriber_id ) ) {
786 616 return;
787 617 }
788 618
@@ -790,10 +620,10 @@
790 620
791 621 }
792 622
793 623 /**
794 - * Outputs a non-inline forms if defined in the Plugin's settings >
795 - * Default Forms (Site Wide) setting.
624 + * Outputs a non-inline form if defined in the Plugin's settings >
625 + * Default Non-Inline Form (Global) setting.
796 626 *
797 627 * @since 2.3.3
798 628 */
799 629 public function output_global_non_inline_form() {
@@ -807,48 +637,33 @@
807 637 if ( ! $this->settings->has_non_inline_form() ) {
808 638 return;
809 639 }
810 640
811 - // Bail if the Page, Post or Custom Post Type's Form setting is set to 'None'
812 - // and the Plugin is set to honor this setting.
813 - if ( $this->post_settings !== false && $this->post_settings->uses_no_form() && $this->settings->non_inline_form_honor_none_setting() ) {
641 + // Get form.
642 + $convertkit_forms = new ConvertKit_Resource_Forms();
643 + $form = $convertkit_forms->get_by_id( (int) $this->settings->get_non_inline_form() );
644 +
645 + // Bail if the Form doesn't exist (this shouldn't happen, but you never know).
646 + if ( ! $form ) {
814 647 return;
815 648 }
816 649
817 - // Determine if the Non-inline Form Limit per Session setting is enabled.
818 - $limit_per_session = $this->settings->non_inline_form_limit_per_session();
650 + // Add the form to the scripts array so it is included in the output.
651 + add_filter(
652 + 'convertkit_output_scripts_footer',
653 + function ( $scripts ) use ( $form ) {
819 654
820 - // Get form.
821 - $convertkit_forms = new ConvertKit_Resource_Forms();
655 + $scripts[] = array(
656 + 'async' => true,
657 + 'data-uid' => $form['uid'],
658 + 'src' => $form['embed_js'],
659 + );
822 660
823 - // Iterate through forms.
824 - foreach ( $this->settings->get_non_inline_form() as $form_id ) {
825 - // Get Form.
826 - $form = $convertkit_forms->get_by_id( (int) $form_id );
661 + return $scripts;
827 662
828 - // Bail if the Form doesn't exist (this shouldn't happen, but you never know).
829 - if ( ! $form ) {
830 - continue;
831 663 }
664 + );
832 665
833 - // Add the form to the scripts array so it is included in the output.
834 - add_filter(
835 - 'convertkit_output_scripts_footer',
836 - function ( $scripts ) use ( $form, $limit_per_session ) {
837 -
838 - $scripts[] = array(
839 - 'async' => true,
840 - 'data-uid' => $form['uid'],
841 - 'src' => $form['embed_js'],
842 - 'data-kit-limit-per-session' => $limit_per_session ? '1' : '0',
843 - );
844 -
845 - return $scripts;
846 -
847 - }
848 - );
849 - }
850 -
851 666 }
852 667
853 668 /**
854 669 * Outputs any JS <script> tags registered with the convertkit_output_scripts_footer
@@ -857,13 +672,8 @@
857 672 * @since 2.1.4
858 673 */
859 674 public function output_scripts_footer() {
860 675
861 - // Don't output scripts if the request is for a search page or 404.
862 - if ( is_search() || is_404() ) {
863 - return;
864 - }
865 -
866 676 // Define array of scripts.
867 677 $scripts = array();
868 678
869 679 /**
@@ -893,13 +703,8 @@
893 703 * @param array $script Form script key/value pairs to output as <script> tag.
894 704 */
895 705 $script = apply_filters( 'convertkit_output_script_footer', $script );
896 706
897 - // Skip script if it is limited by the Non-inline Form Limit per Session setting.
898 - if ( $this->is_script_output_limited_by_session( $script ) ) {
899 - continue;
900 - }
901 -
902 707 // Build output.
903 708 $output = '<script';
904 709 foreach ( $script as $attribute => $value ) {
905 710 // If the value is true, just output the attribute.
@@ -936,38 +741,8 @@
936 741 // Output scripts.
937 742 foreach ( $output_scripts as $output_script ) {
938 743 echo $output_script . "\n"; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
939 744 }
940 -
941 - }
942 -
943 - /**
944 - * Checks if a script is limited by the Non-inline Form Limit per Session setting.
945 - *
946 - * @since 3.0.0
947 - *
948 - * @param array $script Script.
949 - * @return bool
950 - */
951 - private function is_script_output_limited_by_session( $script ) {
952 -
953 - // Get Settings, if they have not yet been loaded.
954 - if ( ! $this->settings ) {
955 - $this->settings = new ConvertKit_Settings();
956 - }
957 -
958 - // Display script if the "Display Limit" setting isn't enabled.
959 - if ( ! $this->settings->non_inline_form_limit_per_session() ) {
960 - return false;
961 - }
962 -
963 - // Display script if the "Display Limit" setting should not be applied to this script.
964 - if ( ! isset( $script['data-kit-limit-per-session'] ) ) {
965 - return false;
966 - }
967 -
968 - // Display script if this is the first time the visitor has seen any non-inline form.
969 - return isset( $_COOKIE['ck_non_inline_form_displayed'] );
970 745
971 746 }
972 747
973 748 }