Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 136
0.00% covered (danger)
0.00%
0 / 8
CRAP
n/a
0 / 0
is_jetpack_connected
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
6
wp_ajax_wpcom_generate_site_preview_link
0.00% covered (danger)
0.00%
0 / 14
0.00% covered (danger)
0.00%
0 / 1
6
wp_ajax_wpcom_delete_site_preview_link
0.00% covered (danger)
0.00%
0 / 23
0.00% covered (danger)
0.00%
0 / 1
12
wpcom_get_site_preview_link
0.00% covered (danger)
0.00%
0 / 10
0.00% covered (danger)
0.00%
0 / 1
12
replace_site_visibility_load_assets
0.00% covered (danger)
0.00%
0 / 29
0.00% covered (danger)
0.00%
0 / 1
110
replace_site_visibility
0.00% covered (danger)
0.00%
0 / 15
0.00% covered (danger)
0.00%
0 / 1
30
allowed_options_remove_site_visibility
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
2
load_options_update_site_visibility
0.00% covered (danger)
0.00%
0 / 34
0.00% covered (danger)
0.00%
0 / 1
156
1<?php
2/**
3 * Replaces the 'Site Visibility' privacy options selector with a Calypso link.
4 *
5 * @package automattic/jetpack-mu-wpcom
6 */
7
8use Automattic\Jetpack\Connection\Client;
9use Automattic\Jetpack\Status\Host;
10
11/**
12 * Load dependencies.
13 */
14require_once __DIR__ . '/../../utils.php';
15
16/**
17 * Whether the current site is connected to Jetpack.
18 *
19 * @return bool
20 */
21function is_jetpack_connected() {
22    // @phan-suppress-next-line PhanUndeclaredClassMethod
23    return class_exists( 'Jetpack' ) && Jetpack::is_connection_ready();
24}
25
26/**
27 * Generate the links for sharing the site.
28 */
29function wp_ajax_wpcom_generate_site_preview_link() {
30    check_ajax_referer( 'wpcom_site_visibility_site_preview_link' );
31
32    $blog_id = get_wpcom_blog_id();
33    $body    = Client::wpcom_json_api_request_as_user(
34        "/sites/$blog_id/preview-links",
35        '2',
36        array(
37            'method' => 'POST',
38        )
39    );
40
41    if ( is_wp_error( $body ) ) {
42        // @phan-suppress-next-line PhanTypeMismatchArgumentProbablyReal -- It takes null, but its phpdoc only says int.
43        wp_send_json_error( $body, null, JSON_UNESCAPED_SLASHES );
44        return;
45    }
46
47    $response = json_decode( wp_remote_retrieve_body( $body ) );
48    // @phan-suppress-next-line PhanTypeMismatchArgumentProbablyReal -- It takes null, but its phpdoc only says int.
49    wp_send_json( $response[0] ?? $response, null, JSON_UNESCAPED_SLASHES );
50}
51add_action( 'wp_ajax_wpcom_generate_site_preview_link', 'wp_ajax_wpcom_generate_site_preview_link' );
52
53/**
54 * Delete the links for sharing the site.
55 */
56function wp_ajax_wpcom_delete_site_preview_link() {
57    check_ajax_referer( 'wpcom_site_visibility_site_preview_link' );
58
59    if ( ! isset( $_POST['code'] ) ) {
60        wp_send_json_error(
61            array(
62                'error' => 'Missing code',
63            ),
64            null, // @phan-suppress-current-line PhanTypeMismatchArgumentProbablyReal -- It takes null, but its phpdoc only says int.
65            JSON_UNESCAPED_SLASHES
66        );
67        return;
68    }
69
70    $code    = sanitize_text_field( wp_unslash( $_POST['code'] ) );
71    $blog_id = get_wpcom_blog_id();
72    $body    = Client::wpcom_json_api_request_as_user(
73        "/sites/$blog_id/preview-links/$code",
74        '2',
75        array(
76            'method' => 'DELETE',
77        )
78    );
79
80    if ( is_wp_error( $body ) ) {
81        return $body;
82    }
83
84    $response = json_decode( wp_remote_retrieve_body( $body ) );
85    return rest_ensure_response( $response );
86}
87add_action( 'wp_ajax_wpcom_delete_site_preview_link', 'wp_ajax_wpcom_delete_site_preview_link' );
88
89/**
90 * Get the links for sharing the site.
91 */
92function wpcom_get_site_preview_link() {
93    $blog_id = get_wpcom_blog_id();
94    $body    = Client::wpcom_json_api_request_as_user(
95        "/sites/$blog_id/preview-links"
96    );
97
98    if ( is_wp_error( $body ) ) {
99        return $body;
100    }
101
102    $response = json_decode( wp_remote_retrieve_body( $body ) );
103    if ( ! is_array( $response ) ) {
104        return $response;
105    }
106    return $response[0];
107}
108
109/**
110 * Load assets
111 */
112function replace_site_visibility_load_assets() {
113    $handle = jetpack_mu_wpcom_enqueue_assets( 'wpcom-replace-site-visibility', array( 'js', 'css' ) );
114
115    $bundles      = wp_list_filter( wpcom_get_site_purchases(), array( 'product_type' => 'bundle' ) );
116    $current_plan = array_pop( $bundles );
117
118    $data = array(
119        'blogId'                 => get_current_blog_id(),
120        'homeUrl'                => home_url( '/' ),
121        'siteTitle'              => get_bloginfo( 'name' ),
122        'isWpcomStagingSite'     => (bool) get_option( 'wpcom_is_staging_site' ),
123        'isUnlaunchedSite'       => get_option( 'launch-status' ) === 'unlaunched',
124        'hasSitePreviewLink'     => function_exists( 'wpcom_site_has_feature' ) && wpcom_site_has_feature( \WPCOM_Features::SITE_PREVIEW_LINKS ),
125        'sitePreviewLink'        => wpcom_get_site_preview_link(),
126        'sitePreviewLinkNonce'   => wp_create_nonce( 'wpcom_site_visibility_site_preview_link' ),
127        'blogPublic'             => get_option( 'blog_public' ),
128        'wpcomComingSoon'        => get_option( 'wpcom_coming_soon' ),
129        'wpcomPublicComingSoon'  => get_option( 'wpcom_public_coming_soon' ),
130        'wpcomDataSharingOptOut' => (bool) get_option( 'wpcom_data_sharing_opt_out' ),
131        'siteDomain'             => wp_parse_url( home_url(), PHP_URL_HOST ),
132        'sitePlan'               => $current_plan,
133        'hasCustomDomain'        => function_exists( 'wpcom_site_has_feature' ) && wpcom_site_has_feature( 'custom-domain' ),
134    );
135
136    // If the site is launched, replace the option value with the actual site visibility.
137    if ( ( new Host() )->is_woa_site() && function_exists( '\Private_Site\site_is_private' )
138        && ! $data['isUnlaunchedSite'] && ! $data['wpcomPublicComingSoon'] && ! $data['wpcomComingSoon'] && (string) $data['blogPublic'] !== '0'
139    ) {
140        // @phan-suppress-next-line PhanUndeclaredFunction
141        $data['blogPublic'] = \Private_Site\site_is_private() ? '-1' : '1';
142    }
143
144    $encoded_data = wp_json_encode( $data, JSON_UNESCAPED_SLASHES | JSON_HEX_TAG | JSON_HEX_AMP );
145    wp_add_inline_script(
146        $handle,
147        "var JETPACK_MU_WPCOM_SITE_VISIBILITY = $encoded_data;",
148        'before'
149    );
150}
151
152/**
153 * Replaces the 'Site Visibility' privacy options selector with a Calypso link.
154 */
155function replace_site_visibility() {
156    // We are not either in Simple or Atomic.
157    if ( ! class_exists( 'Automattic\Jetpack\Status' ) ) {
158        return;
159    }
160
161    $jetpack_status = new Automattic\Jetpack\Status();
162
163    if ( ! is_jetpack_connected() && $jetpack_status->is_private_site() ) {
164        $settings_url    = esc_url_raw( sprintf( '/wp-admin/admin.php?page=jetpack' ) );
165        $manage_label    = __( 'Jetpack is disconnected & site is private. Reconnect Jetpack to manage site visibility settings.', 'jetpack-mu-wpcom' );
166        $escaped_content = '<a href="' . esc_url( $settings_url ) . '">' . esc_html( $manage_label ) . '</a>';
167    } elseif ( ! is_jetpack_connected() ) {
168        return;
169    } else {
170        $escaped_content = <<<'HTML'
171<fieldset id="wpcom-site-visibility">
172    <img src="images/loading.gif" alt="Loading..." width="16" height="16">
173</fieldset>
174HTML;
175
176        replace_site_visibility_load_assets();
177    }
178
179    ?>
180<noscript>
181<p><?php echo wp_json_encode( $escaped_content, JSON_UNESCAPED_SLASHES | JSON_HEX_TAG | JSON_HEX_AMP ); ?></p>
182</noscript>
183<script>
184( function() {
185    var widgetArea = document.querySelector( '.option-site-visibility td' );
186    if ( ! widgetArea ) {
187        return;
188    }
189    widgetArea.innerHTML = <?php echo wp_json_encode( $escaped_content, JSON_UNESCAPED_SLASHES | JSON_HEX_TAG | JSON_HEX_AMP ); ?>;
190} )()
191</script>
192        <?php
193}
194add_action( 'blog_privacy_selector', 'replace_site_visibility' );
195
196/**
197 * Filter out the settings related to the site visibility.
198 *
199 * @param array $allowed_options The allowed options list.
200 * @return array
201 */
202function allowed_options_remove_site_visibility( $allowed_options ) {
203    $del_options = array(
204        'reading' => array( 'blog_public' ),
205    );
206
207    $allowed_options = remove_allowed_options( $del_options, $allowed_options );
208
209    return $allowed_options;
210}
211
212/**
213 * Update the site options that are related to the site visibility.
214 */
215function load_options_update_site_visibility() {
216    $action      = ! empty( $_REQUEST['action'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['action'] ) ) : '';
217    $option_page = ! empty( $_REQUEST['option_page'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['option_page'] ) ) : '';
218    if ( $action !== 'update' || $option_page !== 'reading' || ! current_user_can( 'manage_options' ) ) {
219        return;
220    }
221
222    check_admin_referer( 'reading-options' );
223
224    $data = array();
225
226    $allowed_options = array(
227        'blog_public',
228        'wpcom_coming_soon',
229        'wpcom_public_coming_soon',
230        'wpcom_data_sharing_opt_out',
231    );
232
233    foreach ( $allowed_options as $option ) {
234        if ( isset( $_POST[ $option ] ) ) {
235            $data[ $option ] = sanitize_text_field( wp_unslash( $_POST[ $option ] ) );
236        }
237    }
238
239    if ( empty( $data ) ) {
240        add_filter( 'allowed_options', 'allowed_options_remove_site_visibility' );
241        return;
242    }
243
244    $blog_id = get_wpcom_blog_id();
245
246    // wpcom_json_api_request_as_user does not support internal requests.
247    $request  = defined( 'IS_WPCOM' ) && IS_WPCOM ? 'wpcom_json_api_request_as_blog' : 'wpcom_json_api_request_as_user';
248    $response = Client::$request(
249        "/sites/$blog_id/site-visibility",
250        'v2',
251        array(
252            'method'  => 'POST',
253            'headers' => array(
254                'content-type' => 'application/json',
255            ),
256        ),
257        wp_json_encode( $data, JSON_UNESCAPED_SLASHES ),
258        'wpcom'
259    );
260
261    if ( wp_remote_retrieve_response_code( $response ) !== 200 ) {
262        add_settings_error( 'general', 'settings_updated', __( 'Settings save failed.', 'jetpack-mu-wpcom' ), 'error' );
263    }
264}
265add_action( 'load-options.php', 'load_options_update_site_visibility' );