Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
10.94% covered (danger)
10.94%
7 / 64
0.00% covered (danger)
0.00%
0 / 9
CRAP
0.00% covered (danger)
0.00%
0 / 1
Jetpack_Subscribe_Floating_Button
12.73% covered (danger)
12.73%
7 / 55
0.00% covered (danger)
0.00%
0 / 9
475.35
0.00% covered (danger)
0.00%
0 / 1
 init
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
6
 __construct
63.64% covered (warning)
63.64%
7 / 11
0.00% covered (danger)
0.00%
0 / 1
2.19
 get_block_template_part_id
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 get_block_template_filter
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
20
 get_template
0.00% covered (danger)
0.00%
0 / 14
0.00% covered (danger)
0.00%
0 / 1
2
 get_floating_subscribe_button_template_content
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 enqueue_assets
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
6
 add_subscribe_floating_button_to_frontend
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
6
 should_user_see_floating_button
0.00% covered (danger)
0.00%
0 / 12
0.00% covered (danger)
0.00%
0 / 1
132
1<?php
2/**
3 * Adds support for Jetpack floating Subscribe button feature
4 *
5 * @package automattic/jetpack-subscriptions
6 * @since 14.0
7 */
8
9if ( ! defined( 'ABSPATH' ) ) {
10    exit( 0 );
11}
12
13/**
14 * Jetpack_Subscribe_Floating_Button class.
15 */
16class Jetpack_Subscribe_Floating_Button {
17    /**
18     * Jetpack_Subscribe_Floating_Button singleton instance.
19     *
20     * @var Jetpack_Subscribe_Floating_Button|null
21     */
22    private static $instance;
23
24    /**
25     * Jetpack_Subscribe_Floating_Button instance init.
26     */
27    public static function init() {
28        if ( self::$instance === null ) {
29            self::$instance = new Jetpack_Subscribe_Floating_Button();
30        }
31
32        return self::$instance;
33    }
34
35    const BLOCK_TEMPLATE_PART_SLUG = 'jetpack-subscribe-floating-button';
36
37    /**
38     * Jetpack_Subscribe_Floating_Button class constructor.
39     */
40    public function __construct() {
41        if ( get_option( 'jetpack_subscribe_floating_button_enabled', false ) ) {
42            add_action( 'wp_enqueue_scripts', array( $this, 'enqueue_assets' ) );
43            add_action( 'wp_footer', array( $this, 'add_subscribe_floating_button_to_frontend' ) );
44        }
45
46        add_filter( 'get_block_template', array( $this, 'get_block_template_filter' ), 10, 3 );
47
48        add_filter(
49            'jetpack_options_whitelist',
50            function ( $options ) {
51                $options[] = 'jetpack_subscribe_floating_button_enabled';
52
53                return $options;
54            }
55        );
56    }
57
58    /**
59     * Returns the block template part ID.
60     *
61     * @return string
62     */
63    public static function get_block_template_part_id() {
64        return get_stylesheet() . '//' . self::BLOCK_TEMPLATE_PART_SLUG;
65    }
66
67    /**
68     * Makes get_block_template return the WP_Block_Template for the floating Subscribe button.
69     *
70     * @param WP_Block_Template $block_template The block template to be returned.
71     * @param string            $id Template unique identifier (example: theme_slug//template_slug).
72     * @param string            $template_type Template type: `'wp_template'` or '`wp_template_part'`.
73     *
74     * @return WP_Block_Template|null
75     */
76    public function get_block_template_filter( $block_template, $id, $template_type ) {
77        if ( empty( $block_template ) && $template_type === 'wp_template_part' ) {
78            if ( $id === self::get_block_template_part_id() ) {
79                return $this->get_template();
80            }
81        }
82
83        return $block_template;
84    }
85
86    /**
87     * Returns a custom template for the floating Subscribe button.
88     *
89     * @return WP_Block_Template
90     */
91    public function get_template() {
92        $template                 = new WP_Block_Template();
93        $template->theme          = get_stylesheet();
94        $template->slug           = self::BLOCK_TEMPLATE_PART_SLUG;
95        $template->id             = self::get_block_template_part_id();
96        $template->area           = 'uncategorized';
97        $template->content        = $this->get_floating_subscribe_button_template_content();
98        $template->source         = 'plugin';
99        $template->type           = 'wp_template_part';
100        $template->title          = __( 'Jetpack Subscribe floating button', 'jetpack' );
101        $template->status         = 'publish';
102        $template->has_theme_file = false;
103        $template->is_custom      = true;
104        $template->description    = __( 'A floating subscribe button that shows up when someone visits your site.', 'jetpack' );
105
106        return $template;
107    }
108
109    /**
110     * Returns the initial content of the floating Subscribe button template.
111     * This can then be edited by the user.
112     *
113     * @return string
114     */
115    public function get_floating_subscribe_button_template_content() {
116        $block_name = esc_attr__( 'Floating subscribe button', 'jetpack' );
117
118        return '<!-- wp:jetpack/subscriptions {"className":"is-style-button","appSource":"subscribe-floating-button","lock":{"move":false,"remove":true},"style":{"spacing":{"margin":{"right":"20px","left":"20px","top":"20px","bottom":"20px"}}},"metadata":{"name":"' . $block_name . '"}} /-->';
119    }
120
121    /**
122     * Enqueues styles.
123     *
124     * @return void
125     */
126    public function enqueue_assets() {
127        if ( $this->should_user_see_floating_button() ) {
128            wp_enqueue_style( 'subscribe-floating-button-css', plugins_url( 'subscribe-floating-button.css', __FILE__ ), array(), JETPACK__VERSION );
129
130            // Disables WP.com action bar as the features collide/overlap
131            add_filter( 'wpcom_disable_logged_out_follow', '__return_true', 10, 1 );
132        }
133    }
134
135    /**
136     * Adds floating Subscribe button HTML wrapper
137     *
138     * @return void
139     */
140    public function add_subscribe_floating_button_to_frontend() {
141        if ( $this->should_user_see_floating_button() ) { ?>
142                <div class="jetpack-subscribe-floating-button">
143                    <?php block_template_part( self::BLOCK_TEMPLATE_PART_SLUG ); ?>
144                </div>
145            <?php
146        }
147    }
148
149    /**
150     * Returns true if a site visitor should see
151     * the floating Subscribe button.
152     *
153     * @return bool
154     */
155    public function should_user_see_floating_button() {
156        // Only show when viewing frontend.
157        if ( is_admin() ) {
158            return false;
159        }
160
161        // Needed because Elementor editor makes is_admin() return false
162        // See https://coreysalzano.com/wordpress/why-elementor-disobeys-is_admin/
163        // Ignore nonce warning as just checking if is set
164        // phpcs:ignore WordPress.Security.NonceVerification.Recommended
165        if ( isset( $_GET['elementor-preview'] ) ) {
166            return false;
167        }
168
169        // Don't show when previewing blog posts or site's theme
170        // phpcs:ignore WordPress.Security.NonceVerification.Recommended
171        if ( isset( $_GET['preview'] ) || isset( $_GET['theme_preview'] ) || isset( $_GET['customize_preview'] ) || isset( $_GET['hide_banners'] ) ) {
172            return false;
173        }
174
175        // Don't show if one of subscribe query params is set.
176        // They are set when user submits the subscribe form.
177        // The nonce is checked elsewhere before redirect back to this page with query params.
178        // phpcs:ignore WordPress.Security.NonceVerification.Recommended
179        if ( isset( $_GET['subscribe'] ) || isset( $_GET['blogsub'] ) ) {
180            return false;
181        }
182
183        // Don't show if user is subscribed to blog.
184        require_once __DIR__ . '/../views.php';
185        if ( ! class_exists( 'Jetpack_Memberships' ) || Jetpack_Memberships::is_current_user_subscribed() ) {
186            return false;
187        }
188
189        return true;
190    }
191}
192
193Jetpack_Subscribe_Floating_Button::init();
194
195add_action(
196    'rest_api_switched_to_blog',
197    function () {
198        Jetpack_Subscribe_Floating_Button::init();
199    }
200);