Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 102
0.00% covered (danger)
0.00%
0 / 9
CRAP
0.00% covered (danger)
0.00%
0 / 1
Deprecate
0.00% covered (danger)
0.00%
0 / 102
0.00% covered (danger)
0.00%
0 / 9
930
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 22
0.00% covered (danger)
0.00%
0 / 1
6
 instance
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
6
 enqueue_admin_scripts
0.00% covered (danger)
0.00%
0 / 16
0.00% covered (danger)
0.00%
0 / 1
12
 set_notices
0.00% covered (danger)
0.00%
0 / 16
0.00% covered (danger)
0.00%
0 / 1
156
 render_admin_notices
0.00% covered (danger)
0.00%
0 / 21
0.00% covered (danger)
0.00%
0 / 1
12
 add_my_jetpack_red_bubbles
0.00% covered (danger)
0.00%
0 / 14
0.00% covered (danger)
0.00%
0 / 1
12
 render_notice
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
2
 has_notices
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
12
 show_feature_notice
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
1<?php
2/**
3 * Place to properly deprecate Jetpack features.
4 *
5 * @package automattic/jetpack
6 */
7
8namespace Automattic\Jetpack\Plugin;
9
10use Automattic\Jetpack\Assets;
11use Automattic\Jetpack\Redirect;
12use Automattic\Jetpack\Status\Host;
13
14/**
15 * Place to properly deprecate Jetpack features.
16 */
17class Deprecate {
18
19    /**
20     * The singleton instance.
21     *
22     * @var Deprecate
23     */
24    private static $instance;
25
26    /**
27     * An array of notices to display.
28     *
29     * @var array
30     */
31    private $notices = array();
32
33    /**
34     * Initialize the class.
35     */
36    private function __construct() {
37        // Modify the notices array to include the notices you want to display.
38        // For more information, see /docs/deprecating-features.md.
39        $this->notices = array(
40            'my-admin' => array(
41                'title'       => __( "Retired feature: Jetpack's XYZ Feature", 'jetpack' ),
42                'message'     => __( 'This feature is being retired and will be removed effective November, 2024. Please use the Classic Theme Helper plugin instead.', 'jetpack' ),
43                'link'        => array(
44                    'label' => __( 'Learn more', 'jetpack' ),
45                    'url'   => 'jetpack-support-xyz',
46                ),
47                'show'        => false, // 'show' is not required, but setting it to false will ensure that the notice will not be displayed.
48                'hide_in_woa' => true, // 'hide_in_woa' is not required, but setting it to true will ensure that the notice will not be displayed in the WoA admin (none will display in Simple regardless).
49            ),
50        );
51        $this->set_notices();
52
53        if ( $this->has_notices() ) {
54            // We only want the notice to appear on the main WP Admin dashboard, which hooking into load-index.php will allow.
55            add_action(
56                'load-index.php',
57                function () {
58                    add_action( 'admin_notices', array( $this, 'render_admin_notices' ) );
59                }
60            );
61            add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_admin_scripts' ) );
62            add_filter( 'my_jetpack_red_bubble_notification_slugs', array( $this, 'add_my_jetpack_red_bubbles' ) );
63        }
64    }
65
66    /**
67     * Create/get the singleton instance.
68     *
69     * @return static
70     */
71    public static function instance() {
72        if ( null === static::$instance ) {
73            static::$instance = new static();
74        }
75
76        return static::$instance;
77    }
78
79    /**
80     * Enqueue the scripts.
81     *
82     * @return void
83     */
84    public function enqueue_admin_scripts() {
85        if ( ! $this->has_notices() ) {
86            return;
87        }
88
89        if ( ! wp_script_is( 'jetpack-deprecate', 'registered' ) ) {
90            wp_register_script(
91                'jetpack-deprecate',
92                Assets::get_file_url_for_environment( '_inc/build/deprecate.min.js', '_inc/deprecate.js' ),
93                array(),
94                JETPACK__VERSION,
95                true
96            );
97        }
98
99        wp_enqueue_script( 'jetpack-deprecate' );
100        wp_add_inline_script(
101            'jetpack-deprecate',
102            'window.noticeInfo = ' . wp_json_encode( $this->notices, JSON_UNESCAPED_SLASHES | JSON_HEX_TAG | JSON_HEX_AMP ) . ';',
103            'before'
104        );
105    }
106
107    /**
108     * Ensure the notices variable is properly formatted and includes the required suffix and show value.
109     *
110     * @return void
111     */
112    private function set_notices() {
113        $notices            = array();
114        $required_id_suffix = '-deprecate-feature';
115        $host               = new Host();
116
117        foreach ( $this->notices as $id => $notice ) {
118            if ( $host->is_woa_site() && isset( $notice['hide_in_woa'] ) && true === $notice['hide_in_woa'] ) {
119                continue;
120            }
121
122            if ( isset( $notice['show'] ) && false === $notice['show'] ) {
123                continue;
124            }
125
126            if ( empty( $notice['title'] ) || empty( $notice['message'] ) || empty( $notice['link']['url'] ) ) {
127                continue;
128            }
129
130            if ( empty( $notice['link']['label'] ) ) {
131                $notice['link']['label'] = __( 'Learn more', 'jetpack' );
132            }
133
134            if ( strpos( $id, $required_id_suffix ) === false ) {
135                $id .= $required_id_suffix;
136            }
137
138            $notices[ $id ] = $notice;
139        }
140
141        $this->notices = $notices;
142    }
143
144    /**
145     * Render deprecation notices for relevant features.
146     *
147     * @return void
148     */
149    public function render_admin_notices() {
150
151        foreach ( $this->notices as $id => $notice ) {
152            if ( $this->show_feature_notice( $id ) ) {
153                $support_url = Redirect::get_url( $notice['link']['url'] );
154
155                $this->render_notice(
156                    $id,
157                    '<div class="jetpack-deprecation-notice-container">' .
158                        '<div class="jetpack-deprecation-notice-svg">' .
159                            '<svg class="jetpack-deprecation-notice-icon gridicon gridicons-info-outline needs-offset" height="20" width="20" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" color="#000000">' .
160                                '<g><path d="M13 9h-2V7h2v2zm0 2h-2v6h2v-6zm-1-7c-4.41 0-8 3.59-8 8s3.59 8 8 8 8-3.59 8-8-3.59-8-8-8m0-2c5.523 0 10 4.477 10 10s-4.477 10-10 10S2 17.523 2 12 6.477 2 12 2z"></path></g>' .
161                            '</svg>' .
162                        '</div>' .
163                        '<div class="jetpack-deprecation-notice-text">' .
164                            '<p class="jetpack-deprection-notice-title">' . esc_html( $notice['title'] ) . '</p>' .
165                            '<p>' . esc_html( $notice['message'] ) . '</p>' .
166                            '<a href="' . $support_url . '" target="_blank" class="jetpack-deprecation-notice-link"> ' . esc_html( $notice['link']['label'] ) . '</a>' .
167                            '<svg class="gridicons-external" height="14" width="14" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 22 20">' .
168                                '<g><path d="M19 13v6c0 1.105-.895 2-2 2H5c-1.105 0-2-.895-2-2V7c0-1.105.895-2 2-2h6v2H5v12h12v-6h2zM13 3v2h4.586l-7.793 7.793 1.414 1.414L19 6.414V11h2V3h-8z"></path></g>' .
169                                '</svg>' .
170                        '</div>' .
171                    '</div>'
172                );
173            }
174        }
175    }
176
177    /**
178     * Add the deprecation notices to My Jetpack.
179     *
180     * @param array $slugs Already added bubbles.
181     *
182     * @return array
183     */
184    public function add_my_jetpack_red_bubbles( $slugs ) {
185
186        foreach ( $this->notices as $id => $notice ) {
187            if ( $this->show_feature_notice( $id ) ) {
188                $slugs[ $id ] = array(
189                    'data' => array(
190                        'text'  => $notice['message'],
191                        'title' => $notice['title'],
192                        'link'  => array(
193                            'label' => esc_html( $notice['link']['label'] ),
194                            'url'   => Redirect::get_url( $notice['link']['url'] ),
195                        ),
196                        'id'    => $id,
197                    ),
198                );
199            }
200        }
201        return $slugs;
202    }
203
204    /**
205     * Render the notice.
206     *
207     * @param string $id The notice ID.
208     * @param string $text The notice text.
209     *
210     * @return void
211     */
212    private function render_notice( $id, $text ) {
213        printf(
214            '<div id="%1$s" class="notice notice-warning is-dismissible jetpack-deprecate-dismissible" style="border-left-color: #000000;">%2$s</div>',
215            esc_html( $id ),
216            $text // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Output already escaped in render_admin_notices
217        );
218    }
219
220    /**
221     * Check if there are any notices to be displayed, so we wouldn't load unnecessary JS and run excessive hooks.
222     *
223     * @return bool
224     */
225    private function has_notices() {
226        foreach ( $this->notices as $id => $notice ) {
227            if ( $this->show_feature_notice( $id ) ) {
228                return true;
229            }
230        }
231        return false;
232    }
233
234    /**
235     * Check if the feature notice should be shown, based on the existence of the cookie.
236     *
237     * @param string $id The notice ID.
238     *
239     * @return bool
240     */
241    private function show_feature_notice( $id ) {
242        return empty( $_COOKIE['jetpack_deprecate_dismissed'][ $id ] );
243    }
244}