Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
63.64% covered (warning)
63.64%
21 / 33
85.71% covered (warning)
85.71%
6 / 7
CRAP
n/a
0 / 0
wpcomsh_is_staging_site_get_atomic_persistent_data
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
6
wpcomsh_is_staging_environment
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
wpcomsh_disable_outgoing_pings_in_non_production_envs
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
2
wpcomsh_disable_incoming_pings_in_non_production_envs
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
2
wpcomsh_force_pingback_flag_off_on_staging
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
2
wpcomsh_force_ping_status_closed_on_staging
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
2
wpcomsh_disable_pingback_ui_on_staging
100.00% covered (success)
100.00%
9 / 9
100.00% covered (success)
100.00%
1 / 1
4
1<?php
2/**
3 * Customizations for the staging sites.
4 *
5 * @package wpcomsh
6 */
7
8/**
9 * Returns Atomic persistent data value for wpcom_is_staging_site.
10 *
11 * @param string $wpcom_is_staging_site Value for the preview links option.
12 *
13 * @return string The value of WPCOM_IS_STAGING_SITE if set, otherwise the option value.
14 */
15function wpcomsh_is_staging_site_get_atomic_persistent_data( $wpcom_is_staging_site ) {
16    $persistent_data                       = new Atomic_Persistent_Data();
17    $persistent_data_is_staging_site_value = $persistent_data->WPCOM_IS_STAGING_SITE; // phpcs:ignore WordPress.NamingConventions.ValidVariableName
18
19    if ( $persistent_data_is_staging_site_value !== null ) {
20        return json_decode( $persistent_data_is_staging_site_value );
21    }
22
23    return $wpcom_is_staging_site;
24}
25// need to hook to default_option_* too because if this option doesn't exist, the hook wouldn't run.
26add_filter( 'default_option_wpcom_is_staging_site', 'wpcomsh_is_staging_site_get_atomic_persistent_data' );
27add_filter( 'option_wpcom_is_staging_site', 'wpcomsh_is_staging_site_get_atomic_persistent_data' );
28
29/**
30 * Whether the site is running as a staging environment.
31 *
32 * @return bool True if the site should be treated as staging.
33 */
34function wpcomsh_is_staging_environment() {
35    /**
36     * Filters whether the site is treated as a staging environment.
37     *
38     * Core's `wp_get_environment_type()` caches for the life of the request and has no filter of its own.
39     *
40     * @since 10.0.0
41     *
42     * @param bool $is_staging Whether the environment type is 'staging'.
43     */
44    return (bool) apply_filters( 'wpcomsh_is_staging_environment', 'staging' === wp_get_environment_type() );
45}
46
47/**
48 * Disables outgoing pingbacks/trackbacks in staging environments.
49 *
50 * Prevents the dispatch of pingbacks when the environment type is 'staging'
51 * by clearing the list of URLs to ping. The `pre_ping` action passes
52 * `$post_links` by reference, so emptying it prevents all outgoing pings.
53 *
54 * This can be removed once WordPress core addresses the issue.
55 *
56 * @see https://core.trac.wordpress.org/ticket/64837
57 *
58 * @param string[] $post_links Array of URLs to ping (passed by reference).
59 */
60function wpcomsh_disable_outgoing_pings_in_non_production_envs( &$post_links ) {
61    if ( wpcomsh_is_staging_environment() ) {
62        $post_links = array();
63    }
64}
65add_action( 'pre_ping', 'wpcomsh_disable_outgoing_pings_in_non_production_envs' );
66
67/**
68 * Disables incoming pingbacks in staging environments by removing
69 * the 'pingback.ping' XML-RPC method.
70 *
71 * Prevents WordPress from processing incoming pingbacks when the environment
72 * type is 'staging'. This can be removed once WordPress core addresses the issue.
73 *
74 * @see https://core.trac.wordpress.org/ticket/64837
75 *
76 * @param array<string, callable> $methods Associative array of XML-RPC methods.
77 * @return array<string, callable> Modified associative array of XML-RPC methods.
78 */
79function wpcomsh_disable_incoming_pings_in_non_production_envs( $methods ) {
80    if ( wpcomsh_is_staging_environment() ) {
81        unset( $methods['pingback.ping'] );
82    }
83
84    return $methods;
85}
86add_filter( 'xmlrpc_methods', 'wpcomsh_disable_incoming_pings_in_non_production_envs' );
87
88/**
89 * Forces the default_pingback_flag option to '0' on staging sites so the
90 * Discussion Settings UI reflects that outgoing pingbacks are disabled.
91 *
92 * @return string|false '0' on staging sites, false to allow normal behavior otherwise.
93 */
94function wpcomsh_force_pingback_flag_off_on_staging() {
95    if ( wpcomsh_is_staging_environment() ) {
96        return '0';
97    }
98
99    return false;
100}
101add_filter( 'pre_option_default_pingback_flag', 'wpcomsh_force_pingback_flag_off_on_staging' );
102
103/**
104 * Forces the default_ping_status option to 'closed' on staging sites so the
105 * Discussion Settings UI reflects that incoming pingbacks are disabled.
106 *
107 * @return string|false 'closed' on staging sites, false to allow normal behavior otherwise.
108 */
109function wpcomsh_force_ping_status_closed_on_staging() {
110    if ( wpcomsh_is_staging_environment() ) {
111        return 'closed';
112    }
113
114    return false;
115}
116add_filter( 'pre_option_default_ping_status', 'wpcomsh_force_ping_status_closed_on_staging' );
117
118/**
119 * Disables pingback checkboxes and adds an explanation on the Discussion
120 * Settings page for staging sites.
121 */
122function wpcomsh_disable_pingback_ui_on_staging() {
123    if ( ! wpcomsh_is_staging_environment() ) {
124        return;
125    }
126
127    $screen = get_current_screen();
128    if ( ! $screen || 'options-discussion' !== $screen->id ) {
129        return;
130    }
131
132    ?>
133    <script>
134    ( function() {
135        var ids = [ 'default_pingback_flag', 'default_ping_status' ];
136        var message = ' — ' + <?php echo wp_json_encode( __( 'Pingbacks are disabled on staging sites to prevent unintended outbound requests.', 'wpcomsh' ), JSON_UNESCAPED_SLASHES | JSON_HEX_TAG | JSON_HEX_AMP ); ?>;
137        ids.forEach( function( id ) {
138            var checkbox = document.getElementById( id );
139            if ( checkbox ) {
140                checkbox.disabled = true;
141                var note = document.createElement( 'em' );
142                note.textContent = message;
143                checkbox.parentNode.appendChild( note );
144            }
145        } );
146    } )();
147    </script>
148    <?php
149}
150add_action( 'admin_print_footer_scripts', 'wpcomsh_disable_pingback_ui_on_staging' );