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