Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
78.91% covered (warning)
78.91%
116 / 147
33.33% covered (danger)
33.33%
2 / 6
CRAP
0.00% covered (danger)
0.00%
0 / 1
Jetpack_Settings_Page
79.02% covered (warning)
79.02%
113 / 143
33.33% covered (danger)
33.33%
2 / 6
38.31
0.00% covered (danger)
0.00%
0 / 1
 add_page_actions
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 maybe_redirect_to_features
100.00% covered (success)
100.00%
18 / 18
100.00% covered (success)
100.00%
1 / 1
7
 get_page_hook
50.00% covered (danger)
50.00%
9 / 18
0.00% covered (danger)
0.00%
0 / 1
4.12
 page_render
86.73% covered (warning)
86.73%
85 / 98
0.00% covered (danger)
0.00%
0 / 1
17.67
 additional_styles
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 page_admin_scripts
0.00% covered (danger)
0.00%
0 / 7
0.00% covered (danger)
0.00%
0 / 1
2
1<?php // phpcs:ignore WordPress.Files.FileName.InvalidClassFileName
2
3use Automattic\Jetpack\Admin_UI\Admin_Menu;
4use Automattic\Jetpack\Assets;
5use Automattic\Jetpack\Redirect;
6use Automattic\Jetpack\Status;
7use Automattic\Jetpack\Tracking;
8
9if ( ! defined( 'ABSPATH' ) ) {
10    exit( 0 );
11}
12
13require_once __DIR__ . '/class.jetpack-admin-page.php';
14require_once JETPACK__PLUGIN_DIR . 'class.jetpack-modules-list-table.php';
15
16/**
17 * Builds the settings page and its menu
18 */
19class Jetpack_Settings_Page extends Jetpack_Admin_Page {
20
21    /**
22     * Show the settings page only when Jetpack is connected or in dev mode.
23     *
24     * @var boolean
25     */
26    protected $dont_show_if_not_active = true;
27
28    /**
29     * Add page action.
30     *
31     * @param string $hook Hook of current page.
32     * @return void
33     */
34    public function add_page_actions( $hook ) {
35        add_action( "load-$hook", array( $this, 'maybe_redirect_to_features' ), PHP_INT_MAX );
36    }
37
38    /**
39     * On the classic Modules screen, send the browser to the matching Features view, with the same search and tag, when the site can manage modules there.
40     *
41     * @return void
42     */
43    public function maybe_redirect_to_features() {
44        $initializer = 'Automattic\\Jetpack\\My_Jetpack\\Initializer';
45        if (
46            'GET' !== ( $_SERVER['REQUEST_METHOD'] ?? 'GET' ) // phpcs:ignore WordPress.Security.ValidatedSanitizedInput -- Only the exact GET method is accepted.
47            || Jetpack::state( 'error' ) || Jetpack::state( 'message' )
48            || $this->block_page_rendering_for_idc()
49            || ! method_exists( $initializer, 'get_modules_management_url' )
50        ) {
51            return;
52        }
53        // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only view arguments; actions are rejected by the URL helper.
54        $args = wp_unslash( $_GET );
55        unset( $args['page'] );
56        $eligible = false;
57        $url      = $initializer::get_modules_management_url( $args, $eligible );
58        if ( ! $eligible ) {
59            return;
60        }
61        add_action(
62            'admin_head',
63            static function () use ( $url ) {
64                wp_print_inline_script_tag( 'window.location.replace(' . wp_json_encode( $url, JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX_APOS | JSON_HEX_QUOT ) . ');' );
65            }
66        );
67    }
68
69    /**
70     * Adds the Settings sub menu.
71     */
72    public function get_page_hook() {
73        /*
74         * In Offline Mode My Jetpack does not initialize, so nothing holds the top slot and
75         * WordPress points the top-level "Jetpack" link at whichever item sorts first. Pin
76         * Modules above the alphabetical run so that link lands here rather than on whatever
77         * feature page happens to sort first. Only reachable offline, where My Jetpack's -10
78         * is never registered.
79         */
80        if ( ( new Status() )->is_offline_mode() ) {
81            $position = defined( Admin_Menu::class . '::POSITION_FIRST_FALLBACK' ) ? Admin_Menu::POSITION_FIRST_FALLBACK : -5;
82
83            return Admin_Menu::add_menu(
84                __( 'Jetpack Settings', 'jetpack' ),
85                __( 'Modules', 'jetpack' ),
86                'jetpack_manage_modules',
87                'jetpack_modules',
88                array( $this, 'render' ),
89                $position
90            );
91        }
92
93        return add_submenu_page(
94            '',
95            __( 'Jetpack Settings', 'jetpack' ),
96            __( 'Settings', 'jetpack' ),
97            'jetpack_manage_modules',
98            'jetpack_modules',
99            array( $this, 'render' )
100        );
101    }
102
103    /**
104     * Renders the module list table where you can use bulk action or row
105     * actions to activate/deactivate and configure modules
106     */
107    public function page_render() {
108        $list_table = new Jetpack_Modules_List_Table();
109
110        // Currently selected product group filter (used to highlight the active View button).
111        // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- This is view logic.
112        $current_group = isset( $_GET['product_group'] ) ? sanitize_text_field( wp_unslash( $_GET['product_group'] ) ) : '';
113
114        $is_offline_mode = ( new Status() )->is_offline_mode();
115        // Currently selected offline-availability filter (only meaningful in Offline Mode).
116        // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- This is view logic.
117        $current_availability = isset( $_GET['offline_available'] ) ? sanitize_text_field( wp_unslash( $_GET['offline_available'] ) ) : '';
118
119        $noscript_notice = @file_get_contents( JETPACK__PLUGIN_DIR . '_inc/build/static-noscript-notice.html' ); //phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged, WordPress.WP.AlternativeFunctions.file_get_contents_file_get_contents, Not fetching a remote file.
120        $rest_api_notice = @file_get_contents( JETPACK__PLUGIN_DIR . '_inc/build/static-version-notice.html' ); //phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged, WordPress.WP.AlternativeFunctions.file_get_contents_file_get_contents, Not fetching a remote file.
121
122        $noscript_notice = str_replace(
123            '#HEADER_TEXT#',
124            esc_html__( 'You have JavaScript disabled', 'jetpack' ),
125            $noscript_notice
126        );
127        $noscript_notice = str_replace(
128            '#TEXT#',
129            esc_html__( "Turn on JavaScript to unlock Jetpack's full potential!", 'jetpack' ),
130            $noscript_notice
131        );
132
133        $rest_api_notice = str_replace(
134            '#HEADER_TEXT#',
135            esc_html( __( 'WordPress REST API is disabled', 'jetpack' ) ),
136            $rest_api_notice
137        );
138        $rest_api_notice = str_replace(
139            '#TEXT#',
140            esc_html( __( "Enable WordPress REST API to unlock Jetpack's full potential!", 'jetpack' ) ),
141            $rest_api_notice
142        );
143
144        if ( ! $this->is_rest_api_enabled() ) {
145            echo $rest_api_notice; //phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
146        }
147        echo $noscript_notice; //phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
148        ?>
149
150        <!-- Core relocates other plugins' notices to just after this marker. -->
151        <div class="wrap"><div class="wp-header-end"></div></div>
152        <div class="jetpack-module-list">
153            <?php if ( $is_offline_mode ) : ?>
154                <div class="wrap">
155                    <div class="jetpack-offline-notice">
156                        <p class="jetpack-offline-notice__title"><?php esc_html_e( "You're working in Offline Mode", 'jetpack' ); ?></p>
157                        <p class="jetpack-offline-notice__text">
158                            <?php esc_html_e( 'Jetpack is running in Offline Mode, so features that need a connection to WordPress.com are paused for now â€” this is completely normal for local and development sites. Go ahead and activate or configure any of the modules below that work offline.', 'jetpack' ); ?>
159                            <a href="<?php echo esc_url( Redirect::get_url( 'jetpack-support-development-mode' ) ); ?>" target="_blank" rel="noopener noreferrer"><?php esc_html_e( 'Learn more about Offline Mode.', 'jetpack' ); ?></a>
160                        </p>
161                    </div>
162                </div>
163            <?php endif; ?>
164            <div class="wrap">
165                <div class="manage-left">
166                    <div class="jetpack-modules-card">
167                        <div class="jetpack-modules-card__bulk">
168                            <label class="jetpack-modules-card__checkall">
169                                <input type="checkbox" class="checkall" />
170                                <span><?php esc_html_e( 'Select all', 'jetpack' ); ?></span>
171                            </label>
172                            <?php $list_table->unprotected_display_tablenav( 'top' ); ?>
173                            <span class="filter-search">
174                                <button type="button" class="button"><?php esc_html_e( 'Filter', 'jetpack' ); ?></button>
175                            </span>
176                        </div>
177                        <form class="jetpack-modules-list-table-form" onsubmit="return false;">
178                            <table class="<?php echo esc_attr( implode( ' ', $list_table->get_table_classes() ) ); ?> jetpack-modules">
179                                <tbody id="the-list">
180                                <?php $list_table->display_rows_or_placeholder(); ?>
181                                </tbody>
182                            </table>
183                        </form>
184                    </div>
185                </div>
186                <div class="manage-right">
187                    <div class="bumper">
188                        <form class="navbar-form" role="search">
189                            <input type="hidden" name="page" value="jetpack_modules" />
190                            <?php // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only navigation marker.
191                            if ( '1' === sanitize_text_field( wp_unslash( $_GET['modules_fallback'] ?? '' ) ) ) :
192                                ?>
193                                <input type="hidden" name="modules_fallback" value="1" />
194                            <?php endif; ?>
195                            <?php $list_table->search_box( __( 'Search modules…', 'jetpack' ), 'srch-term' ); ?>
196                            <?php if ( $is_offline_mode ) : ?>
197                                <p><?php esc_html_e( 'Available in offline mode', 'jetpack' ); ?></p>
198                                <span class="dops-button-group button-group availability-filter">
199                                    <button type="button" class="dops-button is-compact button
200                                    <?php echo 'all' === $current_availability ? 'active' : ''; ?>
201                                        " data-availability="all"><?php esc_html_e( 'All', 'jetpack' ); ?></button>
202                                    <button type="button" class="dops-button button
203                                    <?php echo 'all' === $current_availability ? '' : 'active'; ?>
204                                    " data-availability="hide-unavailable"><?php esc_html_e( 'Hide unavailable', 'jetpack' ); ?></button>
205                                </span>
206                            <?php endif; ?>
207                            <p><?php esc_html_e( 'State', 'jetpack' ); ?></p>
208                            <span class="dops-button-group button-group module-filter filter-state">
209                                <button type="button" class="dops-button is-compact button
210                                <?php // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- This is view logic.
211                                if ( empty( $_GET['activated'] ) ) {
212                                    echo 'active';
213                                }
214                                ?>
215                                    ">
216                                <?php esc_html_e( 'All', 'jetpack' ); ?></button>
217                                <button type="button" class="dops-button button
218                                <?php // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- This is view logic.
219                                if ( ! empty( $_GET['activated'] ) && 'true' === $_GET['activated'] ) {
220                                    echo 'active';
221                                }
222                                ?>
223                                " data-filter-by="activated" data-filter-value="true"><?php esc_html_e( 'Active', 'jetpack' ); ?></button>
224                                <button type="button" class="dops-button button
225                                <?php // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- This is view logic.
226                                if ( ! empty( $_GET['activated'] ) && 'false' === $_GET['activated'] ) {
227                                    echo 'active';
228                                }
229                                ?>
230                                " data-filter-by="activated" data-filter-value="false"><?php esc_html_e( 'Inactive', 'jetpack' ); ?></button>
231                            </span>
232                            <p><?php esc_html_e( 'Purpose', 'jetpack' ); ?></p>
233                            <ul class="purpose-filter">
234                                <li class="all<?php echo '' === $current_group ? ' current' : ''; ?>"><a href="#" data-group=""><?php esc_html_e( 'All', 'jetpack' ); ?></a> <span class="count"></span></li>
235                                <?php
236                                foreach ( Jetpack_Modules_List_Table::PRODUCT_GROUP_ORDER as $group_slug ) {
237                                    printf(
238                                        '<li class="%1$s%2$s"><a href="#" data-group="%1$s">%3$s</a> <span class="count"></span></li>',
239                                        esc_attr( $group_slug ),
240                                        $group_slug === $current_group ? ' current' : '',
241                                        esc_html( Jetpack_Modules_List_Table::get_product_group_name( $group_slug ) )
242                                    );
243                                }
244                                ?>
245                            </ul>
246                            <p><?php esc_html_e( 'Tags', 'jetpack' ); ?></p>
247                            <?php $list_table->views(); ?>
248                        </form>
249                    </div>
250                </div>
251            </div>
252        </div><!-- /.jetpack-module-list -->
253        <?php
254
255        $tracking = new Tracking();
256        $tracking->record_user_event( 'wpa_page_view', array( 'path' => 'old_settings' ) );
257    }
258
259    /**
260     * Load styles for static page.
261     *
262     * @since 4.3.0
263     */
264    public function additional_styles() {
265        Jetpack_Admin_Page::load_wrapper_styles();
266    }
267
268    /**
269     * Javascript logic specific to the list table
270     */
271    public function page_admin_scripts() {
272        wp_enqueue_script(
273            'jetpack-admin-js',
274            Assets::get_file_url_for_environment( '_inc/build/jetpack-admin.min.js', '_inc/jetpack-admin.js' ),
275            array( 'jquery' ),
276            JETPACK__VERSION,
277            true
278        );
279    }
280}