Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 104
0.00% covered (danger)
0.00%
0 / 8
CRAP
n/a
0 / 0
wpcom_lm_maybe_add_map_meta_cap_filter
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
42
wpcom_lm_register_settings
0.00% covered (danger)
0.00%
0 / 15
0.00% covered (danger)
0.00%
0 / 1
6
wpcom_lm_get_options_v1_api
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
6
wpcom_lm_update_options_v1_api
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
6
wpcom_lm_settings_page_init
0.00% covered (danger)
0.00%
0 / 18
0.00% covered (danger)
0.00%
0 / 1
6
wpcom_locked_mode_render
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
2
wpcom_lm_remove_post_capabilities
0.00% covered (danger)
0.00%
0 / 46
0.00% covered (danger)
0.00%
0 / 1
1892
wpcom_lm_disable_comments
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
12
1<?php
2/**
3 * Locked Mode.
4 *
5 * @package automattic/jetpack-mu-wpcom
6 */
7
8/**
9 * Adds the action to limit capabilities for locked mode sites when the Locked Mode feature is available
10 * and the option enabled.
11 */
12function wpcom_lm_maybe_add_map_meta_cap_filter() {
13    // On REST API requests, wait until we switch to the correct blog to add the filter.
14    if ( defined( 'REST_REQUEST' ) && REST_REQUEST && 'plugins_loaded' === current_filter() ) {
15        return;
16    }
17
18    if ( get_option( 'wpcom_locked_mode' ) && wpcom_site_has_feature( WPCOM_Features::LOCKED_MODE ) ) {
19        add_filter( 'map_meta_cap', 'wpcom_lm_remove_post_capabilities', 10, 2 );
20    } else {
21        remove_filter( 'map_meta_cap', 'wpcom_lm_remove_post_capabilities' );
22    }
23}
24add_action( 'plugins_loaded', 'wpcom_lm_maybe_add_map_meta_cap_filter', 11 );
25add_action( 'rest_api_switched_to_blog', 'wpcom_lm_maybe_add_map_meta_cap_filter' );
26
27/**
28 * Registers Locked Mode settings.
29 */
30function wpcom_lm_register_settings() {
31    if ( ! wpcom_site_has_feature( WPCOM_Features::LOCKED_MODE ) ) {
32        return;
33    }
34
35    register_setting(
36        'general',
37        'wpcom_locked_mode',
38        array(
39            'type'              => 'boolean',
40            'description'       => 'Whether the site is in locked mode.',
41            'show_in_rest'      => true,
42            'default'           => false,
43            'sanitize_callback' => function ( $value ) {
44                return (bool) $value;
45            },
46        )
47    );
48}
49add_action( 'admin_init', 'wpcom_lm_register_settings' );
50add_action( 'rest_api_init', 'wpcom_lm_register_settings' );
51
52/**
53 * Adds settings to the v1 API site settings endpoint.
54 *
55 * @param array $settings A single site's settings.
56 * @return array
57 */
58function wpcom_lm_get_options_v1_api( $settings ) {
59    if ( wpcom_site_has_feature( WPCOM_Features::LOCKED_MODE ) ) {
60        $settings['wpcom_locked_mode'] = (bool) get_option( 'wpcom_locked_mode' );
61    }
62
63    return $settings;
64}
65add_filter( 'site_settings_endpoint_get', 'wpcom_lm_get_options_v1_api' );
66
67/**
68 * Updates settings via public-api.wordpress.com.
69 *
70 * @param array $input             Associative array of site settings to be updated.
71 *                                 Cast and filtered based on documentation.
72 * @param array $unfiltered_input  Associative array of site settings to be updated.
73 *                                 Neither cast nor filtered. Contains raw input.
74 * @return array
75 */
76function wpcom_lm_update_options_v1_api( $input, $unfiltered_input ) {
77    if ( isset( $unfiltered_input['wpcom_locked_mode'] ) ) {
78        $input['wpcom_locked_mode'] = (bool) $unfiltered_input['wpcom_locked_mode'];
79    }
80
81    return $input;
82}
83add_filter( 'rest_api_update_site_settings', 'wpcom_lm_update_options_v1_api', 10, 2 );
84
85/**
86 * Registers the settings section and fields.
87 */
88function wpcom_lm_settings_page_init() {
89    if ( ! wpcom_site_has_feature( WPCOM_Features::LOCKED_MODE ) ) {
90        return;
91    }
92
93    add_settings_section(
94        'wpcom_enhanced_ownership_section',
95        __( 'Enhanced Ownership', 'jetpack-mu-wpcom' ),
96        '', // No callback needed.
97        'general'
98    );
99
100    add_settings_field(
101        'wpcom_locked_mode',
102        __( 'Locked Mode', 'jetpack-mu-wpcom' ),
103        'wpcom_locked_mode_render',
104        'general',
105        'wpcom_enhanced_ownership_section',
106        array(
107            'label_for' => 'wpcom_locked_mode',
108        )
109    );
110}
111add_action( 'admin_init', 'wpcom_lm_settings_page_init' );
112
113/**
114 * Renders the Locked Mode settings markup.
115 */
116function wpcom_locked_mode_render() {
117    ?>
118    <input type="checkbox" id="wpcom_locked_mode" name="wpcom_locked_mode" <?php checked( get_option( 'wpcom_locked_mode' ) ); ?>>
119    <label for="wpcom_locked_mode"><?php esc_html_e( 'Enable Locked Mode', 'jetpack-mu-wpcom' ); ?></label>
120    <p class="description"><?php esc_html_e( 'Prevents new post and page from being created as well as existing posts and pages from being edited, and closes comments site wide.', 'jetpack-mu-wpcom' ); ?></p>
121    <?php
122}
123
124/**
125 * Removes post and page creation/editing capabilities for locked mode sites.
126 *
127 * @param array  $caps The user's capabilities.
128 * @param string $cap  The capability name.
129 * @return array
130 */
131function wpcom_lm_remove_post_capabilities( $caps, $cap ) {
132    switch ( $cap ) {
133        case 'edit_posts':
134            if ( ! defined( 'REST_API_REQUEST' ) || ! REST_API_REQUEST ) {
135                $caps = array( 'do_not_allow' );
136                break;
137            }
138
139            // This is only called on REST API requests on sites that have the Locked Mode feature, where Locked Mode is enabled.
140            $trace = debug_backtrace( DEBUG_BACKTRACE_IGNORE_ARGS, 8 ); // phpcs:ignore WordPress.PHP.DevelopmentFunctions
141
142            // phpcs:ignore Generic.CodeAnalysis.EmptyStatement.DetectedIf
143            if ( isset( $trace[7]['class'] ) && 'WPCOM_JSON_API_Site_Settings_Endpoint' === $trace[7]['class'] && 'get_settings_response' === $trace[7]['function'] ) {
144                /*
145                 * This targets the edit_posts check in WPCOM_JSON_API_Site_Settings_Endpoint::get_settings_response().
146                 * We still want to allow the endpoint to return the settings for the site, even if the site is in locked mode.
147                 * So in this case we don't change the capabilities, for all other cases we do.
148                 */
149            } else {
150                $caps = array( 'do_not_allow' );
151            }
152            break;
153
154        case 'install_themes':
155        case 'switch_themes':
156        case 'edit_themes':
157        case 'delete_themes':
158        case 'edit_theme_options':
159        case 'customize':
160        case 'install_plugins':
161        case 'activate_plugins':
162        case 'edit_plugins':
163        case 'delete_plugins':
164        case 'edit_files':
165        case 'upload_files':
166        case 'edit_comment':
167        case 'moderate_comments':
168        case 'manage_categories':
169        case 'manage_links':
170        case 'import':
171        case 'edit_others_posts':
172        case 'edit_published_posts':
173        case 'publish_posts':
174        case 'delete_posts':
175        case 'delete_others_posts':
176        case 'delete_published_posts':
177        case 'delete_private_posts':
178        case 'edit_private_posts':
179        case 'edit_pages':
180        case 'edit_others_pages':
181        case 'edit_published_pages':
182        case 'publish_pages':
183        case 'delete_pages':
184        case 'delete_others_pages':
185        case 'delete_published_pages':
186        case 'delete_private_pages':
187        case 'edit_private_pages':
188        case 'create_users':
189        case 'delete_users':
190            $caps = array( 'do_not_allow' );
191    }
192
193    return $caps;
194}
195
196/**
197 * Disables comments site-wide for locked mode sites.
198 *
199 * @param bool $comments_open Whether the current post is open for comments.
200 */
201function wpcom_lm_disable_comments( $comments_open ) {
202    if ( get_option( 'wpcom_locked_mode' ) && wpcom_site_has_feature( WPCOM_Features::LOCKED_MODE ) ) {
203        return false;
204    }
205
206    return $comments_open;
207}
208add_filter( 'comments_open', 'wpcom_lm_disable_comments' );