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