Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
61.11% covered (warning)
61.11%
22 / 36
57.14% covered (warning)
57.14%
12 / 21
CRAP
0.00% covered (danger)
0.00%
0 / 1
Environment
61.11% covered (warning)
61.11%
22 / 36
57.14% covered (warning)
57.14%
12 / 21
134.10
0.00% covered (danger)
0.00%
0 / 1
 is_block_theme
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 is_simple_site
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 sharing_module_running
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
2
 legacy_sharing_switched_off
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
12
 legacy_sharing_supported
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
3
 settings_screen_supported
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 likes_supported
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
3
 is_connected
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 is_offline_mode
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 likes_module_running
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
2
 legacy_likes_switched_off
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
12
 comment_likes_module_running
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
2
 comment_likes_enabled
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
2
 comment_likes_follow_likes_settings
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
2
 likes_settings_in_use
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
2
 twitter_site_tag_used
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 module_active
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
2
 sharing_block_registered
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 like_block_registered
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 single_template_editor_url
90.91% covered (success)
90.91%
10 / 11
0.00% covered (danger)
0.00%
0 / 1
5.02
 block_is_registered
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
1<?php
2/**
3 * Reads the site state that Settings > Sharing branches on.
4 *
5 * @package automattic/jetpack-sharing-likes
6 */
7
8declare( strict_types = 1 );
9
10namespace Automattic\Jetpack\Sharing_Likes\Settings;
11
12use Automattic\Jetpack\Connection\Manager as Connection_Manager;
13use Automattic\Jetpack\Modules;
14use Automattic\Jetpack\Status;
15use Automattic\Jetpack\Status\Host;
16
17/**
18 * The facts Settings > Sharing branches on, read in one place so the sections
19 * and the state resolver stay free of environment lookups.
20 */
21final class Environment {
22
23    /**
24     * Memoised Site Editor URL, since resolving the template hits the theme's files.
25     *
26     * @var string|null
27     */
28    private static $single_template_editor_url = null;
29
30    /**
31     * Whether the active theme is a block theme.
32     */
33    public static function is_block_theme(): bool {
34        return wp_is_block_theme();
35    }
36
37    /**
38     * Whether this is a WordPress.com Simple site.
39     */
40    public static function is_simple_site(): bool {
41        return ( new Host() )->is_wpcom_simple();
42    }
43
44    /**
45     * Whether sharing buttons can still produce output.
46     */
47    public static function sharing_module_running(): bool {
48        return self::legacy_sharing_supported() && self::module_active( 'sharedaddy' );
49    }
50
51    /**
52     * Whether every sharing service has been removed, so sharedaddy renders nothing.
53     *
54     * An absent option is not that: `Sharing_Service::get_blog_services()` falls
55     * back to its default services when the option was never saved.
56     */
57    public static function legacy_sharing_switched_off(): bool {
58        $services = get_option( 'sharing-services' );
59
60        if ( ! is_array( $services ) ) {
61            return false;
62        }
63
64        return empty( $services['visible'] ) && empty( $services['hidden'] );
65    }
66
67    /**
68     * Whether this site can have legacy sharing buttons at all.
69     *
70     * The module needs no connection, but `Jetpack::load_modules()` includes
71     * nothing on a site that is neither connected nor in offline mode, and
72     * `Modules::activate()` refuses there too: an active module renders no
73     * buttons, and offering to turn it on would do nothing. Simple loads
74     * sharedaddy from `post-flair.php` without either.
75     */
76    public static function legacy_sharing_supported(): bool {
77        return self::is_simple_site() || self::is_connected() || self::is_offline_mode();
78    }
79
80    /**
81     * Whether Settings > Sharing has anything to offer this site.
82     *
83     * Neither the legacy features nor their blocks load on a site that is neither
84     * connected nor in offline mode. Checking sharing alone is enough: any site
85     * that can have Like buttons (connected, or Simple) passes that check too.
86     */
87    public static function settings_screen_supported(): bool {
88        return self::legacy_sharing_supported();
89    }
90
91    /**
92     * Whether this site can have Like buttons at all.
93     *
94     * The Likes module declares `Requires Connection: Yes`, so without a
95     * WordPress.com connection it cannot be activated and its buttons cannot
96     * render: the widget is keyed on the blog ID a connection provides.
97     * Offline mode keeps a connected site's tokens, but `Jetpack::load_modules()`
98     * skips every module that requires a connection until it ends.
99     */
100    public static function likes_supported(): bool {
101        return self::is_simple_site() || ( self::is_connected() && ! self::is_offline_mode() );
102    }
103
104    /**
105     * Whether the site holds a WordPress.com connection.
106     */
107    private static function is_connected(): bool {
108        return ( new Connection_Manager( 'jetpack' ) )->is_connected();
109    }
110
111    /**
112     * Whether the site is in offline mode, where it cannot connect to WordPress.com either.
113     */
114    public static function is_offline_mode(): bool {
115        return ( new Status() )->is_offline_mode();
116    }
117
118    /**
119     * Whether Like buttons can still produce output.
120     *
121     * The module can be left active in the options through a disconnect,
122     * which is why support is checked too.
123     */
124    public static function likes_module_running(): bool {
125        return self::likes_supported() && self::module_active( 'likes' );
126    }
127
128    /**
129     * Whether Simple's posts show neither a Like nor a Reblog button, bar those that opted in individually.
130     *
131     * Both count because the legacy widget renders for either. Jetpack and Atomic never
132     * render Reblog, and switch to the block by turning the module off instead.
133     */
134    public static function legacy_likes_switched_off(): bool {
135        return self::is_simple_site()
136            && ! Likes_Options::likes_enabled_sitewide()
137            && ! Likes_Options::reblogs_enabled_sitewide();
138    }
139
140    /**
141     * Whether comments can still be liked.
142     *
143     * Comment Likes is its own module, but it reads the Likes settings: both
144     * `disabled_likes` and the shared placement gate `is_likes_visible()`. So a
145     * site running it without the Likes module still needs those controls.
146     */
147    public static function comment_likes_module_running(): bool {
148        return self::likes_supported() && self::module_active( 'comment-likes' );
149    }
150
151    /**
152     * Whether Comment Likes are switched on for this site.
153     *
154     * - On WordPress.com Simple, which has no modules: the `jetpack_comment_likes_enabled` option.
155     * - On Atomic and self-hosted Jetpack sites: the Comment Likes module, which never reads that option.
156     */
157    public static function comment_likes_enabled(): bool {
158        return self::is_simple_site() ? Likes_Options::comment_likes_enabled() : self::comment_likes_module_running();
159    }
160
161    /**
162     * Whether Comment Likes read the Likes settings: the sitewide default and placement. Simple's do not.
163     */
164    public static function comment_likes_follow_likes_settings(): bool {
165        return ! self::is_simple_site() && self::comment_likes_module_running();
166    }
167
168    /**
169     * Whether anything on this site still reads the Likes settings.
170     *
171     * Both features are gated on `disabled_likes` and the shared placement, so
172     * either one running makes the Likes placement defaults the ones that apply.
173     */
174    public static function likes_settings_in_use(): bool {
175        return self::likes_module_running() || self::comment_likes_module_running();
176    }
177
178    /**
179     * Whether anything on this site reads the Twitter Site Tag option.
180     *
181     * Twitter Cards read it, and through them the Sharing Buttons block's X `via`.
182     * Simple's Twitter Cards read `twitter_via`, which wpcom serves and saves this option from.
183     */
184    public static function twitter_site_tag_used(): bool {
185        /** This filter is documented in projects/plugins/jetpack/class.jetpack.php */
186        return ! apply_filters( 'jetpack_disable_twitter_cards', false );
187    }
188
189    /**
190     * Whether a module is active.
191     *
192     * Simple has no modules: wpcom loads both features on every request, so
193     * every module counts as active there whatever the option holds.
194     *
195     * @param string $slug Module slug.
196     */
197    private static function module_active( string $slug ): bool {
198        return self::is_simple_site() || ( new Modules() )->is_active( $slug );
199    }
200
201    /**
202     * Whether the Sharing Buttons block is available to offer as an alternative.
203     */
204    public static function sharing_block_registered(): bool {
205        return self::block_is_registered( 'jetpack/sharing-buttons' );
206    }
207
208    /**
209     * Whether the Like block is available to offer as an alternative.
210     */
211    public static function like_block_registered(): bool {
212        return self::block_is_registered( 'jetpack/like' );
213    }
214
215    /**
216     * Site Editor URL for the active theme's single post template.
217     *
218     * Empty when the theme has no `single` template, which callers treat as
219     * "offer no Site Editor link" rather than linking somewhere that 404s: a
220     * block theme shipping only `index.html` is the case that hits this.
221     */
222    public static function single_template_editor_url(): string {
223        if ( null !== self::$single_template_editor_url ) {
224            return self::$single_template_editor_url;
225        }
226
227        $stylesheet = get_stylesheet();
228        $template   = $stylesheet ? $stylesheet . '//single' : '';
229
230        if ( ! $template || ! get_block_template( $template ) ) {
231            self::$single_template_editor_url = '';
232
233            return self::$single_template_editor_url;
234        }
235
236        // `add_query_arg()` does not encode, and the Site Editor router wants `p` percent-encoded.
237        self::$single_template_editor_url = admin_url(
238            'site-editor.php?p=' . rawurlencode( '/wp_template/' . $template ) . '&canvas=edit'
239        );
240
241        return self::$single_template_editor_url;
242    }
243
244    /**
245     * Whether a block is registered, and so available to add to a template.
246     *
247     * Registration happens on `init`, well before this screen renders.
248     *
249     * @param string $name Fully qualified block name.
250     */
251    private static function block_is_registered( string $name ): bool {
252        return \WP_Block_Type_Registry::get_instance()->is_registered( $name );
253    }
254}