| @@ -66,18 +66,84 @@ | ||
| 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' ), 1 ); | |
| 70 | + add_action( 'init', array( $this, 'get_subscriber_id_from_request' ) ); | |
| 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' ) ); |
| 74 | 75 | add_filter( 'the_content', array( $this, 'append_form_to_content' ) ); |
| 76 | + add_filter( 'hooked_block_types', array( $this, 'maybe_register_form_block_on_category_archive' ), 10, 4 ); | |
| 77 | + add_filter( 'hooked_block_convertkit/form', array( $this, 'append_form_block_to_category_archive' ), 10, 1 ); | |
| 78 | + add_action( 'wp_footer', array( $this, 'output_global_non_inline_form' ), 1 ); | |
| 75 | 79 | add_action( 'wp_footer', array( $this, 'output_scripts_footer' ) ); |
| 76 | 80 | |
| 77 | 81 | } |
| 78 | 82 | |
| 79 | 83 | /** |
| 84 | + * Tags the subscriber, if: | |
| 85 | + * - a subscriber ID exists in the cookie or URL, | |
| 86 | + * - the WordPress Page has the "Add a Tag" setting specified | |
| 87 | + * | |
| 88 | + * @since 2.4.9.1 | |
| 89 | + */ | |
| 90 | + public function maybe_tag_subscriber() { | |
| 91 | + | |
| 92 | + // Bail if no subscriber ID detected. | |
| 93 | + if ( ! $this->subscriber_id ) { | |
| 94 | + return; | |
| 95 | + } | |
| 96 | + | |
| 97 | + // Bail if not a singular Post Type supported by ConvertKit. | |
| 98 | + if ( ! is_singular( convertkit_get_supported_post_types() ) ) { | |
| 99 | + return; | |
| 100 | + } | |
| 101 | + | |
| 102 | + // Get Post ID. | |
| 103 | + $post_id = get_the_ID(); | |
| 104 | + | |
| 105 | + // Bail if a Post ID couldn't be identified. | |
| 106 | + if ( ! $post_id ) { | |
| 107 | + return; | |
| 108 | + } | |
| 109 | + | |
| 110 | + // Get Settings, if they have not yet been loaded. | |
| 111 | + if ( ! $this->settings ) { | |
| 112 | + $this->settings = new ConvertKit_Settings(); | |
| 113 | + } | |
| 114 | + | |
| 115 | + // Bail if the API if an API Key and Secret is not defined. | |
| 116 | + if ( ! $this->settings->has_api_key_and_secret() ) { | |
| 117 | + return; | |
| 118 | + } | |
| 119 | + | |
| 120 | + // Get ConvertKit Post's Settings, if they have not yet been loaded. | |
| 121 | + if ( ! $this->post_settings ) { | |
| 122 | + $this->post_settings = new ConvertKit_Post( $post_id ); | |
| 123 | + } | |
| 124 | + | |
| 125 | + // Bail if no "Add a Tag" setting specified for this Page. | |
| 126 | + if ( ! $this->post_settings->has_tag() ) { | |
| 127 | + return; | |
| 128 | + } | |
| 129 | + | |
| 130 | + // Initialize the API. | |
| 131 | + $api = new ConvertKit_API_V4( | |
| 132 | + CONVERTKIT_OAUTH_CLIENT_ID, | |
| 133 | + CONVERTKIT_OAUTH_CLIENT_REDIRECT_URI, | |
| 134 | + $this->settings->get_access_token(), | |
| 135 | + $this->settings->get_refresh_token(), | |
| 136 | + $this->settings->debug_enabled(), | |
| 137 | + 'output' | |
| 138 | + ); | |
| 139 | + | |
| 140 | + // Tag subscriber. | |
| 141 | + $api->tag_subscriber( $this->post_settings->get_tag(), $this->subscriber_id ); | |
| 142 | + | |
| 143 | + } | |
| 144 | + | |
| 145 | + /** | |
| 80 | 146 | * Runs the `convertkit_output_output_form` action for singular Post Types that don't use the_content() |
| 81 | 147 | * or apply_filters( 'the_content' ) to output a ConvertKit Form. |
| 82 | 148 | * |
| 83 | 149 | * @since 1.9.6 |
| @@ -159,8 +225,23 @@ | ||
| 159 | 225 | |
| 160 | 226 | // Replace the favicon with the WordPress site's favicon, if specified. |
| 161 | 227 | $landing_page = $this->landing_pages->replace_favicon( $landing_page ); |
| 162 | 228 | |
| 229 | + /** | |
| 230 | + * Perform any actions immediately prior to outputting the Landing Page. | |
| 231 | + * | |
| 232 | + * Caching and minification Plugins may need to hook here to prevent | |
| 233 | + * CSS / JS minification and lazy loading images, which can interfere | |
| 234 | + * with Landing Pages. | |
| 235 | + * | |
| 236 | + * @since 2.4.4 | |
| 237 | + * | |
| 238 | + * @param string $landing_page ConvertKit Landing Page HTML. | |
| 239 | + * @param int $landing_page_id ConvertKit Landing Page ID. | |
| 240 | + * @param int $post_id WordPress Page ID. | |
| 241 | + */ | |
| 242 | + do_action( 'convertkit_output_landing_page_before', $landing_page, $landing_page_id, $post_id ); | |
| 243 | + | |
| 163 | 244 | // Output Landing Page. |
| 164 | 245 | // Output is supplied from ConvertKit's API, which is already sanitized. |
| 165 | 246 | echo $landing_page; // phpcs:ignore WordPress.Security.EscapeOutput |
| 166 | 247 | exit; |
| @@ -174,10 +255,10 @@ | ||
| 174 | 255 | * @return string Post Content with Form Appended, if applicable |
| 175 | 256 | */ |
| 176 | 257 | public function append_form_to_content( $content ) { |
| 177 | 258 | |
| 178 | - // Bail if not a singular Post Type. | |
| 179 | - if ( ! is_singular() ) { | |
| 259 | + // Bail if not a singular Post Type supported by ConvertKit. | |
| 260 | + if ( ! is_singular( convertkit_get_supported_post_types() ) ) { | |
| 180 | 261 | return $content; |
| 181 | 262 | } |
| 182 | 263 | |
| 183 | 264 | // Get Post ID and ConvertKit Form ID for the Post. |
| @@ -265,8 +346,108 @@ | ||
| 265 | 346 | |
| 266 | 347 | } |
| 267 | 348 | |
| 268 | 349 | /** |
| 350 | + * Registers the ConvertKit Form block to before or after the Query Loop block, when viewing a Category archive. | |
| 351 | + * | |
| 352 | + * See append_form_block_on_category_archive() configures the block to display the applicable category's Form. | |
| 353 | + * | |
| 354 | + * @since 2.4.9.1 | |
| 355 | + * | |
| 356 | + * @param array $hooked_blocks The list of hooked block types. | |
| 357 | + * @param string $position The relative position of the hooked blocks. | |
| 358 | + * @param string $anchor_block The anchor block type. | |
| 359 | + * @param WP_Block_Template|WP_Post|array $context The block template, template part, wp_navigation post type, or pattern that the anchor block belongs to. | |
| 360 | + * @return array | |
| 361 | + */ | |
| 362 | + public function maybe_register_form_block_on_category_archive( $hooked_blocks, $position, $anchor_block, $context ) { | |
| 363 | + | |
| 364 | + // Don't append if we're not viewing a category archive. | |
| 365 | + if ( ! is_category() ) { | |
| 366 | + return $hooked_blocks; | |
| 367 | + } | |
| 368 | + | |
| 369 | + if ( $context instanceof WP_Block_Template && $context->slug !== 'archive' ) { | |
| 370 | + return $hooked_blocks; | |
| 371 | + } | |
| 372 | + | |
| 373 | + // Don't append if the anchor block isn't the Query Loop block. | |
| 374 | + if ( $anchor_block !== 'core/query' ) { | |
| 375 | + return $hooked_blocks; | |
| 376 | + } | |
| 377 | + | |
| 378 | + // Don't append if the Category's form position setting is not defined. | |
| 379 | + $form_position = $this->get_term_form_position(); | |
| 380 | + if ( ! $form_position ) { | |
| 381 | + // Unhook this function as we don't need to check again in this request, as we'll | |
| 382 | + // never output a form on the Category archive. | |
| 383 | + remove_filter( 'hooked_block_types', array( $this, 'maybe_register_form_block_on_category_archive' ), 10 ); | |
| 384 | + | |
| 385 | + return $hooked_blocks; | |
| 386 | + } | |
| 387 | + | |
| 388 | + // Don't append if the position doesn't match. | |
| 389 | + if ( $form_position !== $position ) { | |
| 390 | + return $hooked_blocks; | |
| 391 | + } | |
| 392 | + | |
| 393 | + // Hook the ConvertKit Form block. | |
| 394 | + $hooked_blocks[] = 'convertkit/form'; | |
| 395 | + | |
| 396 | + // Unhook this function as we don't need to check again in this request, as | |
| 397 | + // we have now appended the form. | |
| 398 | + remove_filter( 'hooked_block_types', array( $this, 'maybe_register_form_block_on_category_archive' ), 10 ); | |
| 399 | + | |
| 400 | + return $hooked_blocks; | |
| 401 | + | |
| 402 | + } | |
| 403 | + | |
| 404 | + /** | |
| 405 | + * Configures the ConvertKit Form block that was hooked below the Query Loop block by maybe_register_form_block_on_category_archive, | |
| 406 | + * defining the Form ID based on the current Category's Form ID. | |
| 407 | + * | |
| 408 | + * @since 2.4.9.1 | |
| 409 | + * | |
| 410 | + * @param array $parsed_hooked_block The parsed block array for the given hooked block type, or null to suppress the block. | |
| 411 | + * @return null|array | |
| 412 | + */ | |
| 413 | + public function append_form_block_to_category_archive( $parsed_hooked_block ) { | |
| 414 | + | |
| 415 | + // Sanity check that we're still viewing a Category archive. | |
| 416 | + if ( ! is_category() ) { | |
| 417 | + // Returning null will unregister the Form block from displaying. | |
| 418 | + return null; | |
| 419 | + } | |
| 420 | + | |
| 421 | + // Get Category archive being viewed. | |
| 422 | + $category = get_category( get_query_var( 'cat' ) ); | |
| 423 | + | |
| 424 | + // Bail if the Category could be found. | |
| 425 | + if ( is_wp_error( $category ) || is_null( $category ) ) { | |
| 426 | + // Returning null will unregister the Form block from displaying. | |
| 427 | + return null; | |
| 428 | + } | |
| 429 | + | |
| 430 | + // Load Term Settings. | |
| 431 | + $term_settings = new ConvertKit_Term( $category->term_id ); | |
| 432 | + | |
| 433 | + // Bail if no Form specified for the Category. | |
| 434 | + if ( ! $term_settings->has_form() ) { | |
| 435 | + // Returning null will unregister the Form block from displaying. | |
| 436 | + return null; | |
| 437 | + } | |
| 438 | + | |
| 439 | + // Define the form block attributes to display the given Form ID. | |
| 440 | + $parsed_hooked_block['attrs'] = array( | |
| 441 | + 'id' => absint( $term_settings->get_form() ), | |
| 442 | + ); | |
| 443 | + | |
| 444 | + // Return the Form block with its attributes. | |
| 445 | + return $parsed_hooked_block; | |
| 446 | + | |
| 447 | + } | |
| 448 | + | |
| 449 | + /** | |
| 269 | 450 | * Returns the Post, Category or Plugin ConvertKit Form ID for the given Post. |
| 270 | 451 | * |
| 271 | 452 | * If the Post specifies a form to use, returns that Form ID. |
| 272 | 453 | * If the Post uses the 'Default' setting, and an assigned Category has a Form ID, uses the Category's Form ID. |
| @@ -340,8 +521,39 @@ | ||
| 340 | 521 | |
| 341 | 522 | } |
| 342 | 523 | |
| 343 | 524 | /** |
| 525 | + * Returns the Form Position setting for the currently viewed Category. | |
| 526 | + * | |
| 527 | + * @since 2.4.9.1 | |
| 528 | + * | |
| 529 | + * @return bool|string | |
| 530 | + */ | |
| 531 | + private function get_term_form_position() { | |
| 532 | + | |
| 533 | + // Get Category archive being viewed. | |
| 534 | + $category = get_category( get_query_var( 'cat' ) ); | |
| 535 | + | |
| 536 | + // Bail if the Category could be found. | |
| 537 | + if ( is_wp_error( $category ) || is_null( $category ) ) { | |
| 538 | + return false; | |
| 539 | + } | |
| 540 | + | |
| 541 | + // Load Term Settings. | |
| 542 | + $term_settings = new ConvertKit_Term( $category->term_id ); | |
| 543 | + | |
| 544 | + // Return false if no form position is defined i.e. we don't want to display | |
| 545 | + // it on the Category archive. | |
| 546 | + if ( ! $term_settings->has_form_position() ) { | |
| 547 | + return false; | |
| 548 | + } | |
| 549 | + | |
| 550 | + // Return form position. | |
| 551 | + return $term_settings->get_form_position(); | |
| 552 | + | |
| 553 | + } | |
| 554 | + | |
| 555 | + /** | |
| 344 | 556 | * Enqueue scripts. |
| 345 | 557 | * |
| 346 | 558 | * @since 1.9.6 |
| 347 | 559 | */ |
| @@ -362,9 +574,9 @@ | ||
| 362 | 574 | // Register scripts that we might use. |
| 363 | 575 | wp_register_script( |
| 364 | 576 | 'convertkit-js', |
| 365 | 577 | CONVERTKIT_PLUGIN_URL . 'resources/frontend/js/convertkit.js', |
| 366 | - array( 'jquery' ), | |
| 578 | + array(), | |
| 367 | 579 | CONVERTKIT_PLUGIN_VERSION, |
| 368 | 580 | true |
| 369 | 581 | ); |
| 370 | 582 | wp_localize_script( |
| @@ -374,10 +586,8 @@ | ||
| 374 | 586 | 'ajaxurl' => admin_url( 'admin-ajax.php' ), |
| 375 | 587 | 'debug' => $settings->debug_enabled(), |
| 376 | 588 | 'nonce' => wp_create_nonce( 'convertkit' ), |
| 377 | 589 | 'subscriber_id' => $this->subscriber_id, |
| 378 | - 'tag' => ( ( is_singular() && $convertkit_post->has_tag() ) ? $convertkit_post->get_tag() : false ), | |
| 379 | - 'post_id' => $post->ID, | |
| 380 | 590 | ) |
| 381 | 591 | ); |
| 382 | 592 | |
| 383 | 593 | // Bail if the no scripts setting is enabled. |
| @@ -410,8 +620,53 @@ | ||
| 410 | 620 | |
| 411 | 621 | } |
| 412 | 622 | |
| 413 | 623 | /** |
| 624 | + * Outputs a non-inline form if defined in the Plugin's settings > | |
| 625 | + * Default Non-Inline Form (Global) setting. | |
| 626 | + * | |
| 627 | + * @since 2.3.3 | |
| 628 | + */ | |
| 629 | + public function output_global_non_inline_form() { | |
| 630 | + | |
| 631 | + // Get Settings, if they have not yet been loaded. | |
| 632 | + if ( ! $this->settings ) { | |
| 633 | + $this->settings = new ConvertKit_Settings(); | |
| 634 | + } | |
| 635 | + | |
| 636 | + // Bail if no non-inline form setting is specified. | |
| 637 | + if ( ! $this->settings->has_non_inline_form() ) { | |
| 638 | + return; | |
| 639 | + } | |
| 640 | + | |
| 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 ) { | |
| 647 | + return; | |
| 648 | + } | |
| 649 | + | |
| 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 ) { | |
| 654 | + | |
| 655 | + $scripts[] = array( | |
| 656 | + 'async' => true, | |
| 657 | + 'data-uid' => $form['uid'], | |
| 658 | + 'src' => $form['embed_js'], | |
| 659 | + ); | |
| 660 | + | |
| 661 | + return $scripts; | |
| 662 | + | |
| 663 | + } | |
| 664 | + ); | |
| 665 | + | |
| 666 | + } | |
| 667 | + | |
| 668 | + /** | |
| 414 | 669 | * Outputs any JS <script> tags registered with the convertkit_output_scripts_footer |
| 415 | 670 | * filter |
| 416 | 671 | * |
| 417 | 672 | * @since 2.1.4 |
| @@ -439,10 +694,19 @@ | ||
| 439 | 694 | $output_scripts = array(); |
| 440 | 695 | |
| 441 | 696 | // Iterate through scripts, building the <script> tag for each. |
| 442 | 697 | foreach ( $scripts as $script ) { |
| 698 | + /** | |
| 699 | + * Filter the form <script> key/value pairs immediately before the script is output. | |
| 700 | + * | |
| 701 | + * @since 2.4.5 | |
| 702 | + * | |
| 703 | + * @param array $script Form script key/value pairs to output as <script> tag. | |
| 704 | + */ | |
| 705 | + $script = apply_filters( 'convertkit_output_script_footer', $script ); | |
| 706 | + | |
| 707 | + // Build output. | |
| 443 | 708 | $output = '<script'; |
| 444 | - | |
| 445 | 709 | foreach ( $script as $attribute => $value ) { |
| 446 | 710 | // If the value is true, just output the attribute. |
| 447 | 711 | if ( $value === true ) { |
| 448 | 712 | $output .= ' ' . esc_attr( $attribute ); |
| @@ -448,11 +712,21 @@ | ||
| 448 | 712 | $output .= ' ' . esc_attr( $attribute ); |
| 449 | 713 | continue; |
| 450 | 714 | } |
| 451 | 715 | |
| 716 | + // Sanitize attribute and value. | |
| 717 | + $attribute = esc_attr( $attribute ); | |
| 718 | + $value = ( $attribute === 'src' ? esc_url( $value ) : esc_attr( $value ) ); | |
| 719 | + | |
| 452 | 720 | // Output the attribute and value. |
| 453 | - $output .= ' ' . esc_attr( $attribute ) . '="' . esc_attr( $value ) . '"'; | |
| 721 | + $output .= ' ' . $attribute; | |
| 722 | + | |
| 723 | + // Output the value, if it's not a blank string. | |
| 724 | + if ( strlen( $value ) > 0 ) { | |
| 725 | + $output .= '="' . $value . '"'; | |
| 726 | + } | |
| 454 | 727 | } |
| 728 | + | |
| 455 | 729 | $output .= '></script>'; |
| 456 | 730 | |
| 457 | 731 | // Add to array. |
| 458 | 732 | $output_scripts[] = $output; |