PluginProbe
Jetpack – WP Security, Backup, Speed, & Growth / 16.2
Jetpack – WP Security, Backup, Speed, & Growth v16.2
16.2 16.2-beta 12.0.3 12.1.3 12.2.3 12.3.2 12.4.2 12.5.2 12.6.4 12.7.3 12.8.3 12.9.5 13.0.2 13.1.5 13.2.4 13.3.3 13.4.5 13.5.2 13.6.2 13.7.2 13.8.3 13.9.2 14.0.1 14.1.1 14.2.2 All 502 releases
jetpack / sal / class.json-api-site-base.php

class.json-api-site-base.php in Jetpack – WP Security, Backup, Speed, & Growth 16.2, at sal/class.json-api-site-base.php

1,885 lines 48.7 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php // phpcs:ignore WordPress.Files.FileName.InvalidClassFileName
2 /**
3 * This file defines the base class for the Site Abstraction Layer (SAL).
4 * Note that this is the site "as seen by user $user_id with token $token", which
5 * is why we pass the token to the platform; these site instances are value objects
6 * to be used in the context of a single request for a single user.
7 * Also note that at present this class _assumes_ you've "switched to"
8 * the site in question, and functions like `get_bloginfo( 'name' )` will
9 * therefore return the correct value.
10 *
11 * @package automattic/jetpack
12 **/
13
14 use Automattic\Jetpack\Blaze;
15 use Automattic\Jetpack\Status;
16 use Automattic\Jetpack\Status\Host;
17
18 if ( ! defined( 'ABSPATH' ) ) {
19 exit( 0 );
20 }
21
22 require_once __DIR__ . '/class.json-api-date.php';
23 require_once __DIR__ . '/class.json-api-post-base.php';
24
25 /**
26 * Base class for SAL_Site.
27 * The abstract functions here are extended by Abstract_Jetpack_Site in class.json-api-site-jetpack-base.php.
28 */
29 abstract class SAL_Site {
30
31 /**
32 * The Jetpack blog ID for the site.
33 *
34 * @var int
35 */
36 public $blog_id;
37
38 /**
39 * A new WPORG_Platform instance.
40 *
41 * @see class.json-api-platform-jetpack.php.
42 *
43 * @var WPORG_Platform
44 */
45 public $platform;
46
47 /**
48 * Contructs the SAL_Site instance.
49 *
50 * @param int $blog_id The Jetpack blog ID for the site.
51 * @param WPORG_Platform $platform A new WPORG_Platform instance.
52 */
53 public function __construct( $blog_id, $platform ) {
54 $this->blog_id = $blog_id;
55 $this->platform = $platform;
56 }
57
58 /**
59 * Get the blog_id property.
60 *
61 * @return int
62 */
63 public function get_id() {
64 return $this->blog_id;
65 }
66
67 /**
68 * Returns the site slug.
69 *
70 * @return string
71 */
72 public function get_slug() {
73 return ( new Status() )->get_site_suffix();
74 }
75
76 /**
77 * Returns the site name.
78 *
79 * @return string
80 */
81 public function get_name() {
82 $name = get_bloginfo( 'name' );
83 return is_string( $name ) ? htmlspecialchars_decode( $name, ENT_QUOTES ) : '';
84 }
85
86 /**
87 * Returns the site description.
88 *
89 * @return string
90 */
91 public function get_description() {
92 $description = get_bloginfo( 'description' );
93 return is_string( $description ) ? htmlspecialchars_decode( $description, ENT_QUOTES ) : '';
94 }
95
96 /**
97 * Returns the URL for the current site.
98 *
99 * @return string
100 */
101 public function get_url() {
102 return (string) home_url();
103 }
104
105 /**
106 * Returns the number of published posts with the 'post' post-type.
107 *
108 * @return int
109 */
110 public function get_post_count() {
111 return (int) wp_count_posts( 'post' )->publish;
112 }
113
114 /**
115 * A prototype function for get_quota - currently returns null.
116 *
117 * @return null
118 */
119 public function get_quota() {
120 return null;
121 }
122
123 /**
124 * Returns an array of blogging prompt settings. Only applicable on WordPress.com.
125 *
126 * Data comes from .com since the fearture requires a .com connection to work.
127 *
128 * @param int $user_id the current user_id.
129 * @param int $blog_id the blog id in this context.
130 */
131 public function get_blogging_prompts_settings( $user_id, $blog_id ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
132 return false;
133 }
134
135 /**
136 * Returns true if a site has the 'videopress' option enabled, false otherwise.
137 *
138 * @see class.json-api-site-jetpack.php for implementation.
139 */
140 abstract public function has_videopress();
141
142 /**
143 * Returns VideoPress storage used, in MB.
144 *
145 * @see class.json-api-site-jetpack-shadow.php on WordPress.com for implementation. Only applicable on WordPress.com.
146 */
147 abstract public function get_videopress_storage_used();
148
149 /**
150 * Sets the upgraded_filetypes_enabled Jetpack option to true as a default. Only relevant for WordPress.com sites.
151 *
152 * @see class.json-api-site-jetpack.php for implementation.
153 */
154 abstract public function upgraded_filetypes_enabled();
155
156 /**
157 * Sets the is_mapped_domain Jetpack option to true as a default.
158 *
159 * Primarily used in WordPress.com to confirm the current blog's domain does or doesn't match the primary redirect.
160 *
161 * @see class.json-api-site-jetpack.php for implementation.
162 */
163 abstract public function is_mapped_domain();
164
165 /**
166 * Fallback to the home URL since all Jetpack sites don't have an unmapped *.wordpress.com domain.
167 *
168 * @see class.json-api-site-jetpack.php for implementation.
169 */
170 abstract public function get_unmapped_url();
171
172 /**
173 * Whether the domain is a site redirect or not. Defaults to false on a Jetpack site.
174 *
175 * Primarily used in WordPress.com where it is determined if a HTTP status check is a redirect or not and whether an exception should be thrown.
176 *
177 * @see class.json-api-site-jetpack.php for implementation.
178 */
179 abstract public function is_redirect();
180
181 /**
182 * Defaults to false on Jetpack sites, however is used on WordPress.com sites, where it returns true if the headstart-fresh blog sticker is present.
183 *
184 * @see class.json-api-site-jetpack.php for implementation.
185 */
186 abstract public function is_headstart_fresh();
187
188 /**
189 * If the site's current theme supports post thumbnails, return true (otherwise return false).
190 *
191 * @see class.json-api-site-jetpack-base.php for implementation.
192 */
193 abstract public function featured_images_enabled();
194
195 /**
196 * Whether or not the Jetpack 'wordads' module is active on the site.
197 *
198 * @see class.json-api-site-jetpack.php for implementation.
199 */
200 abstract public function has_wordads();
201
202 /**
203 * Defaults to false on Jetpack sites, however is used on WordPress.com sites. This nonce is used for previews on Jetpack sites.
204 *
205 * @see class.json-api-site-jetpack.php for implementation.
206 */
207 abstract public function get_frame_nonce();
208
209 /**
210 * Defaults to false on Jetpack sites, however is used on WordPress.com sites where
211 * it creates a nonce to be used with iframed block editor requests to a Jetpack site.
212 *
213 * @see class.json-api-site-jetpack.php for implementation.
214 */
215 abstract public function get_jetpack_frame_nonce();
216
217 /**
218 * Returns the allowed mime types and file extensions for a site.
219 *
220 * @see class.json-api-site-jetpack.php for implementation.
221 */
222 abstract public function allowed_file_types();
223
224 /**
225 * Returns an array of supported post formats.
226 *
227 * @see class.json-api-site-jetpack-base.php for implementation.
228 */
229 abstract public function get_post_formats();
230
231 /**
232 * Return site's privacy status.
233 *
234 * @see class.json-api-site-jetpack.php for implementation.
235 */
236 abstract public function is_private();
237
238 /**
239 * Return site's coming soon status.
240 *
241 * @see class.json-api-site-jetpack.php for implementation.
242 */
243 abstract public function is_coming_soon();
244
245 /**
246 * Whether or not the current user is following this blog. Defaults to false.
247 *
248 * @see class.json-api-site-jetpack.php for implementation.
249 */
250 abstract public function is_following();
251
252 /**
253 * Defaults to 0 for the number of WordPress.com subscribers - this is filled in on the WordPress.com side.
254 *
255 * @see class.json-api-site-jetpack.php for implementation.
256 */
257 abstract public function get_subscribers_count();
258
259 /**
260 * Returns the language code for the current site.
261 *
262 * @see class.json-api-site-jetpack.php for implementation.
263 */
264 abstract public function get_locale();
265
266 /**
267 * The flag indicates that the site has Jetpack installed.
268 *
269 * @see class.json-api-site-jetpack.php for implementation.
270 */
271 abstract public function is_jetpack();
272
273 /**
274 * The flag indicates that the site is connected to WP.com via Jetpack Connection.
275 *
276 * @see class.json-api-site-jetpack.php for implementation.
277 */
278 abstract public function is_jetpack_connection();
279
280 /**
281 * This function returns the values of any active Jetpack modules.
282 *
283 * @see class.json-api-site-jetpack-base.php for implementation.
284 */
285 abstract public function get_jetpack_modules();
286
287 /**
288 * This function returns true if a specified Jetpack module is active, false otherwise.
289 *
290 * @see class.json-api-site-jetpack-base.php for implementation.
291 *
292 * @param string $module The Jetpack module name to check.
293 */
294 abstract public function is_module_active( $module );
295
296 /**
297 * This function returns false for a check as to whether a site is a VIP site or not.
298 *
299 * @see class.json-api-site-jetpack-base.php for implementation.
300 */
301 abstract public function is_vip();
302
303 /**
304 * Returns true if Multisite is enabled, false otherwise.
305 *
306 * @see class.json-api-site-jetpack.php for implementation.
307 */
308 abstract public function is_multisite();
309
310 /**
311 * Points to the user ID of the site owner
312 *
313 * @see class.json-api-site-jetpack.php for implementation.
314 */
315 abstract public function get_site_owner();
316
317 /**
318 * Returns true if the current site is a single user site, false otherwise.
319 *
320 * @see class.json-api-site-jetpack.php for implementation.
321 */
322 abstract public function is_single_user_site();
323
324 /**
325 * Defaults to false instead of returning the current site plan.
326 *
327 * @see class.json-api-site-jetpack.php for implementation.
328 */
329 abstract public function get_plan();
330
331 /**
332 * Empty function declaration - this function is filled out on the WordPress.com side, returning true if the site has an AK / VP bundle.
333 *
334 * @see class.json-api-site-jetpack.php and /wpcom/public.api/rest/sal/class.json-api-site-jetpack-shadow.php.
335 */
336 abstract public function get_ak_vp_bundle_enabled();
337
338 /**
339 * Returns null for Jetpack sites. For WordPress.com sites this returns the value of the 'podcasting_archive' option.
340 *
341 * @see class.json-api-site-jetpack.php for implementation.
342 */
343 abstract public function get_podcasting_archive();
344
345 /**
346 * Return the last engine used for an import on the site. Not used in Jetpack.
347 *
348 * @see class.json-api-site-jetpack.php for implementation.
349 */
350 abstract public function get_import_engine();
351
352 /**
353 * Returns the front page meta description for current site.
354 *
355 * @see class.json-api-site-jetpack.php for implementation.
356 */
357 abstract public function get_jetpack_seo_front_page_description();
358
359 /**
360 * Returns custom title formats from site option.
361 *
362 * @see class.json-api-site-jetpack.php for implementation.
363 */
364 abstract public function get_jetpack_seo_title_formats();
365
366 /**
367 * Returns website verification codes. Allowed keys include: google, pinterest, bing, yandex, facebook.
368 *
369 * @see class.json-api-site-jetpack.php for implementation.
370 */
371 abstract public function get_verification_services_codes();
372
373 /**
374 * This function is implemented on WPCom sites, where a filter is removed which forces the URL to http.
375 *
376 * @see class.json-api-site-jetpack-base.php and /wpcom/public.api/rest/sal/class.json-api-site-jetpack-shadow.php.
377 */
378 abstract public function before_render();
379
380 /**
381 * If a user has manage options permissions and the site is the main site of the network, make updates visible.
382 *
383 * Called after response_keys have been rendered, which itself is used to return all the necessary information for a site’s response.
384 *
385 * @see class.json-api-site-jetpack-base.php for implementation.
386 *
387 * @param array $response an array of the response keys.
388 */
389 abstract public function after_render( &$response );
390
391 /**
392 * Extends the Jetpack options array with details including site constraints, WordPress and Jetpack versions, and plugins using the Jetpack connection.
393 *
394 * @see class.json-api-site-jetpack-base.php for implementation.
395 * @todo factor this out? Seems an odd thing to have on a site
396 *
397 * @param array $options an array of the Jetpack options.
398 */
399 abstract public function after_render_options( &$options );
400
401 /**
402 * Wrap a WP_Post object with SAL methods, returning a Jetpack_Post object.
403 *
404 * @see class.json-api-site-jetpack.php for implementation.
405 *
406 * @param WP_Post $post A WP_Post object.
407 * @param string $context The post request context (for example 'edit' or 'display').
408 */
409 abstract public function wrap_post( $post, $context );
410
411 /**
412 * For Jetpack sites this will always return false.
413 *
414 * @see class.json-api-site-jetpack-base.php for implementation.
415 *
416 * @param int $post_id The post id.
417 */
418 abstract protected function is_a8c_publication( $post_id );
419
420 /**
421 * Return the user interactions with a site. Not used in Jetpack.
422 *
423 * @see class.json-api-site-jetpack.php for implementation.
424 */
425 abstract public function get_user_interactions();
426
427 /**
428 * Flag a site as deleted. Not used in Jetpack.
429 *
430 * @see class.json-api-site-jetpack.php for implementation.
431 */
432 abstract public function is_deleted();
433
434 /**
435 * Indicates that a site is an A4A client. Not used in Jetpack.
436 *
437 * @see class.json-api-site-jetpack.php for implementation.
438 */
439 abstract public function is_a4a_client();
440
441 /**
442 * Indicates that a site is an A4A dev site.
443 *
444 * @return bool
445 */
446 public function is_a4a_dev_site() {
447 if ( function_exists( 'has_blog_sticker' ) ) {
448 return has_blog_sticker( 'a4a-is-dev-site' );
449 }
450 return false;
451 }
452
453 /**
454 * Return the user interactions with a site. Not used in Jetpack.
455 *
456 * @param string $role The capability to check.
457 * @return bool
458 * @see class.json-api-site-jetpack.php for implementation.
459 * @see class.json-api-site-wpcom.php (on WPCOM) for Simple-site implementation.
460 * @see class.json-api-site-jetpack-shadow.php (on WPCOM) for Atomic-site implementation.
461 */
462 abstract public function current_user_can( $role );
463
464 /**
465 * Defines a filter to set whether a site is an automated_transfer site or not.
466 *
467 * Default is false.
468 *
469 * @return bool
470 */
471 public function is_automated_transfer() {
472 /**
473 * Filter if a site is an automated-transfer site.
474 *
475 * @module json-api
476 *
477 * @since 6.4.0
478 *
479 * @param bool is_automated_transfer( $this->blog_id )
480 * @param int $blog_id Blog identifier.
481 */
482 return apply_filters(
483 'jetpack_site_automated_transfer',
484 false,
485 $this->blog_id
486 );
487 }
488
489 /**
490 * Defaulting to false and not relevant for Jetpack sites, this is expanded on the WordPress.com side for a specific wp.com/start 'WP for teams' flow.
491 *
492 * @see class.json-api-site-jetpack.php for implementation.
493 */
494 abstract public function is_wpforteams_site();
495
496 /**
497 * Get hub blog id for P2 sites.
498 *
499 * @return null
500 */
501 public function get_p2_hub_blog_id() {
502 return null;
503 }
504
505 /**
506 * Getter for the p2 organization ID.
507 *
508 * @return int
509 */
510 public function get_p2_organization_id() {
511 return 0; // WPForTeams\Constants\NO_ORG_ID not loaded.
512 }
513
514 /**
515 * Get details used to render a thumbnail of the site. P2020 themed sites only.
516 *
517 * @return ?array
518 */
519 public function get_p2_thumbnail_elements() {
520 return null;
521 }
522
523 /**
524 * Detect whether a site is a WordPress.com on Atomic site.
525 *
526 * @return bool
527 */
528 public function is_wpcom_atomic() {
529 return ( new Host() )->is_woa_site();
530 }
531
532 /**
533 * Detect whether a site is an automated transfer site and WooCommerce is active.
534 *
535 * @see /wpcom/public.api/rest/sal/class.json-api-site-jetpack-shadow.php.
536 *
537 * @return bool - False for Jetpack-connected sites.
538 */
539 public function is_wpcom_store() {
540 return false;
541 }
542
543 /**
544 * Indicate whether this site was ever a specific trial.
545 *
546 * @param string $trial The trial type to check for.
547 *
548 * @return bool
549 */
550 public function was_trial( $trial ) {
551 if ( function_exists( 'has_blog_sticker' ) ) {
552 return has_blog_sticker( "had-{$trial}-trial" );
553 }
554 return false;
555 }
556
557 /**
558 * Indicate whether this site was upgraded from a trial plan at some point.
559 *
560 * @return bool
561 */
562 public function was_upgraded_from_trial() {
563 if ( function_exists( 'has_blog_sticker' ) ) {
564 return has_blog_sticker( 'has-upgraded-from-ecommerce-trial' );
565 }
566 return false;
567 }
568
569 /**
570 * Detect whether a site has the WooCommerce plugin active.
571 *
572 * @see /wpcom/public.api/rest/sal/class.json-api-site-jetpack-shadow.php.
573 *
574 * @return bool - Default false for Jetpack-connected sites.
575 */
576 public function woocommerce_is_active() {
577 return false;
578 }
579
580 /**
581 * Whether the Editing Toolkit plugin is active (relevant only on WordPress.com).
582 *
583 * @return true
584 */
585 public function editing_toolkit_is_active() {
586 return true;
587 }
588
589 /**
590 * Detect whether a site has access to the Jetpack cloud.
591 *
592 * @see /wpcom/public.api/rest/sal/class.json-api-site-jetpack-shadow.php.
593 *
594 * @return bool - Default false for Jetpack-connected sites.
595 */
596 public function is_cloud_eligible() {
597 return false;
598 }
599
600 /**
601 * Returns an array of WPCOM_Store products.
602 *
603 * @see /wpcom/public.api/rest/sal/class.json-api-site-jetpack-shadow.php.
604 *
605 * @return bool - Default empty array for Jetpack-connected sites.
606 */
607 public function get_products() {
608 return array();
609 }
610
611 /**
612 * Get post by ID
613 *
614 * @param int $post_id The ID of the post.
615 * @param string $context The context by which the post data is required (display or edit).
616 *
617 * @return Jetpack_Post Post object on success, WP_Error object on failure
618 **/
619 public function get_post_by_id( $post_id, $context ) {
620 $post = get_post( $post_id, OBJECT, $context );
621
622 if ( ! $post ) {
623 return new WP_Error( 'unknown_post', 'Unknown post', 404 );
624 }
625
626 $wrapped_post = $this->wrap_post( $post, $context );
627 // validate access
628 return $this->validate_access( $wrapped_post );
629 }
630
631 /**
632 * Validate current user can access the post
633 *
634 * @param Jetpack_Post $post Post object.
635 *
636 * @return WP_Error|Jetpack_Post
637 */
638 private function validate_access( $post ) {
639 $context = $post->context;
640
641 if (
642 ! $this->is_post_type_allowed( $post->post_type )
643 && ! $this->is_a8c_publication( $post->ID )
644 ) {
645 return new WP_Error( 'unknown_post', 'Unknown post', 404 );
646 }
647
648 switch ( $context ) {
649 case 'edit':
650 if ( ! current_user_can( 'edit_post', $post->ID ) ) {
651 return new WP_Error( 'unauthorized', 'User cannot edit post', 403 );
652 }
653 break;
654 case 'display':
655 $can_view = $this->user_can_view_post( $post );
656 if ( is_wp_error( $can_view ) ) {
657 return $can_view;
658 }
659 break;
660 default:
661 return new WP_Error( 'invalid_context', 'Invalid API CONTEXT', 400 );
662 }
663
664 return $post;
665 }
666
667 /**
668 * Validate whether the current user can access the specified post type.
669 *
670 * @param string $post_type The post type to check.
671 * @param string $context The context by which the post data is required (display or edit).
672 *
673 * @return bool
674 */
675 public function current_user_can_access_post_type( $post_type, $context ) {
676 $post_type_object = $this->get_post_type_object( $post_type );
677 if ( ! $post_type_object ) {
678 return false;
679 }
680
681 switch ( $context ) {
682 case 'edit':
683 return current_user_can( $post_type_object->cap->edit_posts );
684 case 'display':
685 return $post_type_object->public || current_user_can( $post_type_object->cap->read_private_posts );
686 default:
687 return false;
688 }
689 }
690
691 /**
692 * Retrieves a post type object by name.
693 *
694 * @param string $post_type The post type to check.
695 *
696 * @return WP_Post_Type|null
697 */
698 protected function get_post_type_object( $post_type ) {
699 return get_post_type_object( $post_type );
700 }
701
702 /**
703 * Is the post type allowed?
704 *
705 * Function copied from class.json-api-endpoints.php.
706 *
707 * @param string $post_type Post type.
708 *
709 * @return bool
710 */
711 public function is_post_type_allowed( $post_type ) {
712 // if the post type is empty, that's fine, WordPress will default to post
713 if ( empty( $post_type ) ) {
714 return true;
715 }
716
717 // allow special 'any' type
718 if ( 'any' === $post_type ) {
719 return true;
720 }
721
722 // check for allowed types
723 if ( in_array( $post_type, $this->get_whitelisted_post_types(), true ) ) {
724 return true;
725 }
726
727 $post_type_object = get_post_type_object( $post_type );
728 if ( $post_type_object ) {
729 if ( ! empty( $post_type_object->show_in_rest ) ) {
730 return $post_type_object->show_in_rest;
731 }
732 if ( ! empty( $post_type_object->publicly_queryable ) ) {
733 return $post_type_object->publicly_queryable;
734 }
735 }
736
737 return ! empty( $post_type_object->public );
738 }
739
740 /**
741 * Gets the whitelisted post types that JP should allow access to.
742 *
743 * Function copied from class.json-api-endpoints.php.
744 *
745 * @return array Whitelisted post types.
746 */
747 public function get_whitelisted_post_types() {
748 $allowed_types = array( 'post', 'page', 'revision' );
749
750 /**
751 * Filter the post types Jetpack has access to, and can synchronize with WordPress.com.
752 *
753 * @module json-api
754 *
755 * @since 2.2.3
756 *
757 * @param array $allowed_types Array of whitelisted post types. Default to `array( 'post', 'page', 'revision' )`.
758 */
759 $allowed_types = apply_filters( 'rest_api_allowed_post_types', $allowed_types );
760
761 return array_unique( $allowed_types );
762 }
763
764 /**
765 * Can the user view the post?
766 *
767 * Function copied from class.json-api-endpoints.php and modified.
768 *
769 * @param Jetpack_Post $post Post object.
770 * @return bool|WP_Error
771 */
772 private function user_can_view_post( $post ) {
773 if ( ! $post || is_wp_error( $post ) ) {
774 return false;
775 }
776 // If the post is of status inherit, check if the parent exists ( different to 0 ) to check for the parent status object.
777 if ( 'inherit' === $post->post_status && 0 !== (int) $post->post_parent ) {
778 $parent_post = get_post( $post->post_parent );
779 $post_status_obj = $parent_post ? get_post_status_object( $parent_post->post_status ) : null;
780 } else {
781 $post_status_obj = get_post_status_object( $post->post_status );
782 }
783
784 $authorized = false;
785
786 if ( $post_status_obj ) {
787 $authorized = $post_status_obj->public
788 || ( is_user_logged_in() && (
789 ( $post_status_obj->protected && current_user_can( 'edit_post', $post->ID ) )
790 || ( $post_status_obj->private && current_user_can( 'read_post', $post->ID ) )
791 ) );
792 }
793
794 $authorized = $authorized || (
795 ( 'trash' === $post->post_status && current_user_can( 'edit_post', $post->ID ) )
796 || 'auto-draft' === $post->post_status
797 );
798
799 if ( ! $authorized ) {
800 return new WP_Error( 'unauthorized', 'User cannot view post', 403 );
801 }
802
803 if (
804 ( new Status() )->is_private_site() &&
805 /**
806 * Filter access to a specific post.
807 *
808 * @module json-api
809 *
810 * @since 3.4.0
811 *
812 * @param bool current_user_can( 'read_post', $post->ID ) Can the current user access the post.
813 * @param WP_Post $post Post data.
814 */
815 ! apply_filters(
816 'wpcom_json_api_user_can_view_post',
817 current_user_can( 'read_post', $post->ID ),
818 $post
819 )
820 ) {
821 return new WP_Error(
822 'unauthorized',
823 'User cannot view post',
824 array(
825 'status_code' => 403,
826 'error' => 'private_blog',
827 )
828 );
829 }
830
831 if ( strlen( $post->post_password ) && ! current_user_can( 'edit_post', $post->ID ) ) {
832 return new WP_Error(
833 'unauthorized',
834 'User cannot view password protected post',
835 array(
836 'status_code' => 403,
837 'error' => 'password_protected',
838 )
839 );
840 }
841
842 return true;
843 }
844
845 /**
846 * Get post ID by name
847 *
848 * Attempts to match name on post title and page path
849 *
850 * @param string $name The post name.
851 *
852 * @return int|WP_Error Post ID on success, WP_Error object on failure
853 */
854 public function get_post_id_by_name( $name ) {
855 $name = sanitize_title( $name );
856
857 if ( ! $name ) {
858 return new WP_Error( 'invalid_post', 'Invalid post', 400 );
859 }
860
861 $posts = get_posts(
862 array(
863 'name' => $name,
864 'numberposts' => 1,
865 'post_type' => $this->get_whitelisted_post_types(),
866 )
867 );
868
869 if ( ! $posts || ! isset( $posts[0]->ID ) || ! $posts[0]->ID ) {
870 $page = get_page_by_path( $name );
871
872 if ( ! $page ) {
873 return new WP_Error( 'unknown_post', 'Unknown post', 404 );
874 }
875
876 return $page->ID;
877 }
878
879 return (int) $posts[0]->ID;
880 }
881
882 /**
883 * Get post by name
884 *
885 * Attempts to match name on post title and page path
886 *
887 * @param string $name The post name.
888 * @param string $context (display or edit).
889 *
890 * @return Jetpack_Post|WP_Error Post object on success, WP_Error object on failure
891 **/
892 public function get_post_by_name( $name, $context ) {
893 $post_id = $this->get_post_id_by_name( $name );
894 if ( is_wp_error( $post_id ) ) {
895 return $post_id;
896 }
897
898 return $this->get_post_by_id( $post_id, $context );
899 }
900
901 /**
902 * Whether or not the current user is an admin (has option management capabilities).
903 *
904 * @return bool
905 **/
906 public function user_can_manage() {
907 return current_user_can( 'manage_options' );
908 }
909
910 /**
911 * Returns the XMLRPC URL - the site URL including the URL scheme that is used when querying your site's REST API endpoint.
912 *
913 * @return string
914 **/
915 public function get_xmlrpc_url() {
916 $xmlrpc_scheme = apply_filters( 'wpcom_json_api_xmlrpc_scheme', wp_parse_url( get_option( 'home' ), PHP_URL_SCHEME ) );
917 return site_url( 'xmlrpc.php', $xmlrpc_scheme );
918 }
919
920 /**
921 * Returns a date/time string with the date the site was registered, or a default date/time string otherwise.
922 *
923 * @return string
924 **/
925 public function get_registered_date() {
926 if ( function_exists( 'get_blog_details' ) ) {
927 $blog_details = get_blog_details();
928 if ( ! empty( $blog_details->registered ) ) {
929 return WPCOM_JSON_API_Date::format_date( $blog_details->registered );
930 }
931 }
932
933 return '0000-00-00T00:00:00+00:00';
934 }
935
936 /**
937 * Returns a date/time string with the date the site was last updated, or a default date/time string otherwise.
938 *
939 * @return string
940 **/
941 public function get_last_update_date() {
942 if ( function_exists( 'get_blog_details' ) ) {
943 $blog_details = get_blog_details();
944 if ( ! empty( $blog_details->last_updated ) ) {
945 return WPCOM_JSON_API_Date::format_date( $blog_details->last_updated );
946 }
947 }
948
949 return '0000-00-00T00:00:00+00:00';
950 }
951
952 /**
953 * Returns an array including the current users relevant capabilities.
954 *
955 * @return array
956 **/
957 public function get_capabilities() {
958 $is_wpcom_blog_owner = wpcom_get_blog_owner() === (int) get_current_user_id();
959
960 return array(
961 'edit_pages' => $this->current_user_can( 'edit_pages' ),
962 'edit_posts' => $this->current_user_can( 'edit_posts' ),
963 'edit_others_posts' => $this->current_user_can( 'edit_others_posts' ),
964 'edit_others_pages' => $this->current_user_can( 'edit_others_pages' ),
965 'delete_posts' => $this->current_user_can( 'delete_posts' ),
966 'delete_others_posts' => $this->current_user_can( 'delete_others_posts' ),
967 'edit_theme_options' => $this->current_user_can( 'edit_theme_options' ),
968 'edit_users' => $this->current_user_can( 'edit_users' ),
969 'list_users' => $this->current_user_can( 'list_users' ),
970 'manage_categories' => $this->current_user_can( 'manage_categories' ),
971 'manage_options' => $this->current_user_can( 'manage_options' ),
972 'moderate_comments' => $this->current_user_can( 'moderate_comments' ),
973 'activate_wordads' => $is_wpcom_blog_owner,
974 'promote_users' => $this->current_user_can( 'promote_users' ),
975 'publish_posts' => $this->current_user_can( 'publish_posts' ),
976 'upload_files' => $this->current_user_can( 'upload_files' ),
977 'delete_users' => $this->current_user_can( 'delete_users' ),
978 'remove_users' => $this->current_user_can( 'remove_users' ),
979 'own_site' => $is_wpcom_blog_owner,
980 'view_stats' => stats_is_blog_user( $this->blog_id ),
981 'activate_plugins' => $this->current_user_can( 'activate_plugins' ),
982 'update_plugins' => $this->current_user_can( 'update_plugins' ),
983 'export' => $this->current_user_can( 'export' ),
984 'import' => $this->current_user_can( 'import' ),
985 );
986 }
987
988 /**
989 * Whether or not a site is public.
990 *
991 * @return bool
992 **/
993 public function is_visible() {
994 if ( is_user_logged_in() ) {
995 $current_user = wp_get_current_user();
996 $visible = (array) get_user_meta( $current_user->ID, 'blog_visibility', true );
997
998 $is_visible = true;
999 if ( isset( $visible[ $this->blog_id ] ) ) {
1000 $is_visible = (bool) $visible[ $this->blog_id ];
1001 }
1002
1003 // null and true are visible
1004 return $is_visible;
1005 }
1006
1007 return null;
1008 }
1009
1010 /**
1011 * Creates and returns an array with logo settings.
1012 *
1013 * @return array
1014 **/
1015 public function get_logo() {
1016 // Set an empty response array.
1017 $logo_setting = array(
1018 'id' => 0,
1019 'sizes' => array(),
1020 'url' => '',
1021 );
1022
1023 // Get current site logo values.
1024 $logo_id = get_option( 'site_logo' );
1025
1026 // Update the response array if there's a site logo currenty active.
1027 if ( $logo_id ) {
1028 $logo_setting['id'] = $logo_id;
1029 $logo_setting['url'] = wp_get_attachment_url( $logo_id );
1030 }
1031
1032 return $logo_setting;
1033 }
1034
1035 /**
1036 * Returns the timezone string from the site's settings (eg. 'Europe/London').
1037 *
1038 * @return string
1039 **/
1040 public function get_timezone() {
1041 return (string) get_option( 'timezone_string' );
1042 }
1043
1044 /**
1045 * Returns the GMT offset from the site's settings (eg. 5.5).
1046 *
1047 * @return float
1048 **/
1049 public function get_gmt_offset() {
1050 return (float) get_option( 'gmt_offset' );
1051 }
1052
1053 /**
1054 * Returns the site's login URL.
1055 *
1056 * @return string
1057 **/
1058 public function get_login_url() {
1059 return wp_login_url();
1060 }
1061
1062 /**
1063 * Returns the URL for a site's admin area.
1064 *
1065 * @return string
1066 **/
1067 public function get_admin_url() {
1068 return get_admin_url();
1069 }
1070
1071 /**
1072 * Returns the theme's slug (eg. 'twentytwentytwo')
1073 *
1074 * @return string
1075 **/
1076 public function get_theme_slug() {
1077 return get_option( 'stylesheet' );
1078 }
1079
1080 /**
1081 * Returns a list of errors for broken themes on the site.
1082 *
1083 * @return array
1084 */
1085 public function get_theme_errors() {
1086 $themes_with_errors = wp_get_themes( array( 'errors' => true ) );
1087 $theme_errors = array();
1088
1089 foreach ( $themes_with_errors as $theme ) {
1090 $errors = $theme->errors();
1091
1092 if ( is_wp_error( $errors ) && ! empty( $errors->get_error_messages() ) ) {
1093 $theme_errors[] = array(
1094 'name' => sanitize_title( $theme->get( 'Name' ) ),
1095 'errors' => (array) $errors->get_error_messages(),
1096 );
1097 }
1098 }
1099
1100 return $theme_errors;
1101 }
1102
1103 /**
1104 * Gets the header image data.
1105 *
1106 * @return bool|object
1107 **/
1108 public function get_header_image() {
1109 return get_theme_mod( 'header_image_data' );
1110 }
1111
1112 /**
1113 * Gets the theme background color.
1114 *
1115 * @return bool|string
1116 **/
1117 public function get_background_color() {
1118 return get_theme_mod( 'background_color' );
1119 }
1120
1121 /**
1122 * Get the image default link type.
1123 *
1124 * @return string
1125 **/
1126 public function get_image_default_link_type() {
1127 return get_option( 'image_default_link_type' );
1128 }
1129
1130 /**
1131 * Gets the image thumbnails width.
1132 *
1133 * @return int
1134 **/
1135 public function get_image_thumbnail_width() {
1136 return (int) get_option( 'thumbnail_size_w' );
1137 }
1138
1139 /**
1140 * Gets the image thumbnails height.
1141 *
1142 * @return int
1143 **/
1144 public function get_image_thumbnail_height() {
1145 return (int) get_option( 'thumbnail_size_h' );
1146 }
1147
1148 /**
1149 * Whether cropping is enabled for thumbnails.
1150 *
1151 * @return string
1152 **/
1153 public function get_image_thumbnail_crop() {
1154 return get_option( 'thumbnail_crop' );
1155 }
1156
1157 /**
1158 * Gets the medium sized image setting's width.
1159 *
1160 * @return int
1161 **/
1162 public function get_image_medium_width() {
1163 return (int) get_option( 'medium_size_w' );
1164 }
1165
1166 /**
1167 * Gets the medium sized image setting's height.
1168 *
1169 * @return int
1170 **/
1171 public function get_image_medium_height() {
1172 return (int) get_option( 'medium_size_h' );
1173 }
1174
1175 /**
1176 * Gets the large sized image setting's width.
1177 *
1178 * @return int
1179 **/
1180 public function get_image_large_width() {
1181 return (int) get_option( 'large_size_w' );
1182 }
1183
1184 /**
1185 * Gets the large sized image setting's height.
1186 *
1187 * @return int
1188 **/
1189 public function get_image_large_height() {
1190 return (int) get_option( 'large_size_h' );
1191 }
1192
1193 /**
1194 * Gets the permalink structure as defined in the site's settings.
1195 *
1196 * @return string
1197 **/
1198 public function get_permalink_structure() {
1199 return get_option( 'permalink_structure' );
1200 }
1201
1202 /**
1203 * Gets the default post format
1204 *
1205 * @return string
1206 **/
1207 public function get_default_post_format() {
1208 return get_option( 'default_post_format' );
1209 }
1210
1211 /**
1212 * Gets the default post category
1213 *
1214 * @return int
1215 **/
1216 public function get_default_category() {
1217 return (int) get_option( 'default_category' );
1218 }
1219
1220 /**
1221 * Returns what should be shown on the front page (eg. page or posts)
1222 *
1223 * @return string
1224 **/
1225 public function get_show_on_front() {
1226 return get_option( 'show_on_front' );
1227 }
1228
1229 /**
1230 * Whether or not the front page is set as 'page' to allow a custom front page
1231 *
1232 * @return bool
1233 **/
1234 public function is_custom_front_page() {
1235 return ( 'page' === $this->get_show_on_front() );
1236 }
1237
1238 /**
1239 * Whether or not likes have been enabled on all site posts
1240 *
1241 * @return bool
1242 **/
1243 public function get_default_likes_enabled() {
1244 return (bool) apply_filters( 'wpl_is_enabled_sitewide', ! get_option( 'disabled_likes' ) );
1245 }
1246
1247 /**
1248 * If sharing has been enabled and there are visible blog services (eg. 'facebook', 'twitter'), returns true.
1249 *
1250 * @return bool
1251 **/
1252 public function get_default_sharing_status() {
1253 $default_sharing_status = false;
1254 if ( class_exists( 'Sharing_Service' ) ) {
1255 $ss = new Sharing_Service();
1256 $blog_services = $ss->get_blog_services();
1257 $default_sharing_status = ! empty( $blog_services['visible'] );
1258 }
1259 return $default_sharing_status;
1260 }
1261
1262 /**
1263 * Displays the current comment status
1264 *
1265 * @return bool False if closed, true for all other comment statuses.
1266 **/
1267 public function get_default_comment_status() {
1268 return 'closed' !== get_option( 'default_comment_status' );
1269 }
1270
1271 /**
1272 * Displays the current site-wide post ping status (for pingbacks and trackbacks)
1273 *
1274 * @return bool False if closed, true for all other ping statuses.
1275 **/
1276 public function default_ping_status() {
1277 return 'closed' !== get_option( 'default_ping_status' );
1278 }
1279
1280 /**
1281 * Whether or not Publicize has been permanently disabled on the site
1282 *
1283 * @see wpcom/wp-content/admin-plugins/publicize/publicize-wpcom.php
1284 *
1285 * @return bool Default false.
1286 **/
1287 public function is_publicize_permanently_disabled() {
1288 $publicize_permanently_disabled = false;
1289 if ( function_exists( 'is_publicize_permanently_disabled' ) ) {
1290 $publicize_permanently_disabled = is_publicize_permanently_disabled( $this->blog_id );
1291 }
1292 return $publicize_permanently_disabled;
1293 }
1294
1295 /**
1296 * Returns the post ID of the static front page.
1297 *
1298 * @return int
1299 **/
1300 public function get_page_on_front() {
1301 return (int) get_option( 'page_on_front' );
1302 }
1303
1304 /**
1305 * Returns the post ID of the page designated as the posts page.
1306 *
1307 * @return int
1308 **/
1309 public function get_page_for_posts() {
1310 return (int) get_option( 'page_for_posts' );
1311 }
1312
1313 /**
1314 * Whether or not headstart is enabled for the site
1315 *
1316 * @return bool
1317 **/
1318 public function is_headstart() {
1319 return get_option( 'headstart' );
1320 }
1321
1322 /**
1323 * The WordPress version on the site.
1324 *
1325 * @return string
1326 **/
1327 public function get_wordpress_version() {
1328 global $wp_version;
1329 return $wp_version;
1330 }
1331
1332 /**
1333 * Whether or not this is a domain-only site (only relevant on WordPress.com simple sites - false otherwise)
1334 *
1335 * @return bool
1336 **/
1337 public function is_domain_only() {
1338 $options = get_option( 'options' );
1339 return ! empty( $options['is_domain_only'] ) ? (bool) $options['is_domain_only'] : false;
1340 }
1341
1342 /**
1343 * Whether or not the blog is set to public (not hidden from search engines)
1344 *
1345 * @return int 1 for true, 0 for false.
1346 **/
1347 public function get_blog_public() {
1348 return (int) get_option( 'blog_public' );
1349 }
1350
1351 /**
1352 * Whether or not the site is in a 'pending automated transfer' state.
1353 *
1354 * @return bool
1355 **/
1356 public function has_pending_automated_transfer() {
1357 /**
1358 * Filter if a site is in pending automated transfer state.
1359 *
1360 * @module json-api
1361 *
1362 * @since 6.4.0
1363 *
1364 * @param bool has_site_pending_automated_transfer( $this->blog_id )
1365 * @param int $blog_id Blog identifier.
1366 */
1367 return apply_filters(
1368 'jetpack_site_pending_automated_transfer',
1369 false,
1370 $this->blog_id
1371 );
1372 }
1373
1374 /**
1375 * Whether or not the site has a 'designType' option set as 'store'
1376 *
1377 * @return bool
1378 **/
1379 public function signup_is_store() {
1380 return $this->get_design_type() === 'store';
1381 }
1382
1383 /**
1384 * Return a new WP_Roles instance, which implements a user roles API
1385 *
1386 * @return WP_Roles
1387 **/
1388 public function get_roles() {
1389 return new WP_Roles();
1390 }
1391
1392 /**
1393 * Returns the 'designType' option if set (the site design type), null otherwise.
1394 *
1395 * @return string|null
1396 **/
1397 public function get_design_type() {
1398 $options = get_option( 'options' );
1399 return empty( $options['designType'] ) ? null : $options['designType'];
1400 }
1401
1402 /**
1403 * Returns the 'site_goals' option if set (eg. share, promote, educate, sell, showcase).
1404 *
1405 * @return array
1406 **/
1407 public function get_site_goals() {
1408 $site_goals_option = get_option( 'site_goals' );
1409
1410 if ( is_array( $site_goals_option ) ) {
1411 return $site_goals_option;
1412 }
1413
1414 return array();
1415 }
1416 /**
1417 * Return site's launch status. Expanded in class.json-api-site-jetpack.php.
1418 *
1419 * @return bool False in this case.
1420 */
1421 public function get_launch_status() {
1422 return false;
1423 }
1424
1425 /**
1426 * Whether a site has any migration meta details - only applicable on WordPress.com
1427 *
1428 * @see /wpcom/public.api/rest/sal/class.json-api-site-jetpack-shadow.php
1429 *
1430 * @return null
1431 */
1432 public function get_migration_meta() {
1433 return null;
1434 }
1435
1436 /**
1437 * Whether a site has a site segment - only applicable on WordPress.com
1438 *
1439 * @see /wpcom/public.api/rest/sal/class.json-api-site-wpcom.php
1440 *
1441 * @return false
1442 */
1443 public function get_site_segment() {
1444 return false;
1445 }
1446
1447 /**
1448 * Whether a site has Vertical ID (used for Starter Templates) - default to only applicable on WordPress.com
1449 *
1450 * @see /wpcom/public.api/rest/sal/class.json-api-site-wpcom.php
1451 *
1452 * @return false
1453 */
1454 public function get_site_vertical_id() {
1455 return false;
1456 }
1457
1458 /**
1459 * Whether a site has a 'site_creation_flow' option set (eg gutenboarding, mobile) - only applicable on WordPress.com
1460 *
1461 * @see /wpcom-json-endpoints/class.wpcom-json-api-new-site-endpoint.php for more on the option.
1462 *
1463 * @return bool
1464 */
1465 public function get_site_creation_flow() {
1466 return get_option( 'site_creation_flow' );
1467 }
1468
1469 /**
1470 * Whether a site has a 'site_source_slug' option set - only applicable on WordPress.com
1471 *
1472 * @see /wpcom-json-endpoints/class.wpcom-json-api-new-site-endpoint.php for more on the option.
1473 *
1474 * @return bool
1475 */
1476 public function get_site_source_slug() {
1477 return get_option( 'site_source_slug' );
1478 }
1479
1480 /**
1481 * Return any selected features (used to help recommend plans)
1482 *
1483 * @return string
1484 */
1485 public function get_selected_features() {
1486 return get_option( 'selected_features' );
1487 }
1488
1489 /**
1490 * Return true if the site design was created with a Blank Canvas (empty homepage template), false otherwise.
1491 *
1492 * @return bool
1493 */
1494 public function was_created_with_blank_canvas_design() {
1495 return (bool) get_option( 'was_created_with_blank_canvas_design' );
1496 }
1497
1498 /**
1499 * Get the option storing the Anchor podcast ID that identifies a site as a podcasting site.
1500 *
1501 * @return string
1502 */
1503 public function get_anchor_podcast() {
1504 return get_option( 'anchor_podcast' );
1505 }
1506
1507 /**
1508 * Check if the site is currently being built by the DIFM Lite team.
1509 *
1510 * @return bool
1511 */
1512 public function is_difm_lite_in_progress() {
1513 if ( function_exists( 'has_blog_sticker' ) ) {
1514 return has_blog_sticker( 'difm-lite-in-progress' );
1515 } elseif ( function_exists( 'wpcomsh_is_site_sticker_active' ) ) {
1516 // For atomic sites
1517 return wpcomsh_is_site_sticker_active( 'difm-lite-in-progress' );
1518 }
1519 return false;
1520 }
1521
1522 /**
1523 * Get the DIFM Lite site options exposed to the frontend while a build is in progress.
1524 *
1525 * Returns null unless the site has an active DIFM Lite build and the code is
1526 * running on WordPress.com, where the DIFM Lite library exists.
1527 *
1528 * @return array|null
1529 */
1530 public function get_difm_lite_site_options() {
1531 if ( ! $this->is_difm_lite_in_progress() ) {
1532 return null;
1533 }
1534 if ( ! defined( 'IS_WPCOM' ) || ! IS_WPCOM || ! function_exists( 'require_lib' ) ) {
1535 return null;
1536 }
1537 require_lib( 'difm-lite' );
1538 if ( ! class_exists( '\DIFM_Lite_Options' ) ) {
1539 // Fail closed if the library did not define the class; this method
1540 // documents null for every path where the options are unavailable.
1541 return null;
1542 }
1543 // The submission state is canonical on the purchase blog. This site may be
1544 // the stickered staging build target, whose own options blob is empty, so
1545 // resolve to the purchase blog before reading — otherwise a post-submit
1546 // staging target reports itself as pre-submit.
1547 //
1548 // resolve_purchase_site_id() ships in the wpcom DIFM Lite site-role
1549 // pointers change. This Jetpack code can deploy before that lands, so
1550 // fall back to the current blog when it is unavailable — no retargeted
1551 // builds can exist until that change is live, so the current blog is the
1552 // purchase blog in that window.
1553 $purchase_blog_id = $this->blog_id;
1554 // @phan-suppress-next-line PhanUndeclaredClassReference -- wpcom-only class, guarded above.
1555 if ( method_exists( '\DIFM_Lite_Options', 'resolve_purchase_site_id' ) ) {
1556 // @phan-suppress-next-line PhanUndeclaredClassMethod -- wpcom-only class, guarded above.
1557 $purchase_blog_id = \DIFM_Lite_Options::resolve_purchase_site_id( $this->blog_id );
1558 }
1559 // @phan-suppress-next-line PhanUndeclaredClassMethod -- wpcom-only class, guarded above.
1560 $difm_lite_options = new \DIFM_Lite_Options( $purchase_blog_id );
1561 return array(
1562 // @phan-suppress-next-line PhanUndeclaredClassProperty -- wpcom-only class, guarded above.
1563 'is_website_content_submitted' => (bool) $difm_lite_options->is_website_content_submitted,
1564 );
1565 }
1566
1567 /**
1568 * Check if the site has the gating-business-q1 blog sticker.
1569 *
1570 * @return bool
1571 */
1572 public function is_gating_business_q1() {
1573 if ( function_exists( 'has_blog_sticker' ) ) {
1574 return has_blog_sticker( 'gating-business-q1' );
1575 } elseif ( function_exists( 'wpcomsh_is_site_sticker_active' ) ) {
1576 // For atomic sites
1577 return wpcomsh_is_site_sticker_active( 'gating-business-q1' );
1578 }
1579 return false;
1580 }
1581
1582 /**
1583 * Get the option of site intent which value is coming from the Hero Flow
1584 *
1585 * @return string
1586 */
1587 public function get_site_intent() {
1588 return get_option( 'site_intent', '' );
1589 }
1590
1591 /**
1592 * Get the option of site partner bundle which value is coming from the Partner Flow
1593 *
1594 * @return string
1595 */
1596 public function get_site_partner_bundle() {
1597 return get_option( 'site_partner_bundle', '' );
1598 }
1599
1600 /**
1601 * Get site option to determine if and how to display launchpad onboarding
1602 *
1603 * @return string
1604 */
1605 public function get_launchpad_screen() {
1606 return get_option( 'launchpad_screen' );
1607 }
1608
1609 /**
1610 * Get the option onboarding_segment coming from the Guided Flow
1611 *
1612 * @return string
1613 */
1614 public function get_onboarding_segment() {
1615 return get_option( 'onboarding_segment', '' );
1616 }
1617
1618 /**
1619 * Get site option for completed launchpad checklist tasks
1620 *
1621 * @return string
1622 */
1623 public function get_launchpad_checklist_tasks_statuses() {
1624 $launchpad_checklist_tasks_statuses_option = get_option( 'launchpad_checklist_tasks_statuses' );
1625
1626 if ( is_array( $launchpad_checklist_tasks_statuses_option ) ) {
1627 return $launchpad_checklist_tasks_statuses_option;
1628 }
1629
1630 return array();
1631 }
1632
1633 /**
1634 * Whether the AI Launchpad is enabled for this site, for the requesting user.
1635 *
1636 * The launchpad-personalization assignment is user-scoped: every site of an
1637 * ai_launchpad user gets the AI Launchpad, however the site was created. Mirrors
1638 * AI_Launchpad::is_enabled_for_site() in jetpack-mu-wpcom, so wp-admin and the
1639 * Calypso-facing payload agree. Like the capabilities in this payload, the value
1640 * is relative to the current user.
1641 *
1642 * @return bool
1643 */
1644 public function is_ai_launchpad_enabled() {
1645 if ( (bool) get_option( 'wpcom_ai_launchpad_enabled' ) ) {
1646 return true;
1647 }
1648
1649 return class_exists( '\Automattic\Jetpack\Jetpack_Mu_Wpcom\Launchpad_Personalization_Experiment' )
1650 // @phan-suppress-next-line PhanUndeclaredClassMethod -- Lives in jetpack-mu-wpcom, outside this plugin's dependency graph; the class_exists guard above covers contexts where it isn't loaded.
1651 && 'ai_launchpad' === \Automattic\Jetpack\Jetpack_Mu_Wpcom\Launchpad_Personalization_Experiment::get_variation();
1652 }
1653
1654 /**
1655 * Whether the AI Launchpad was dismissed, reverting the site to the regular launchpad.
1656 *
1657 * @return bool
1658 */
1659 public function is_ai_launchpad_dismissed() {
1660 return (bool) get_option( 'wpcom_ai_launchpad_dismissed' );
1661 }
1662
1663 /**
1664 * Whether every AI Launchpad task has been completed.
1665 *
1666 * @return bool
1667 */
1668 public function is_ai_launchpad_completed() {
1669 return (bool) get_option( 'wpcom_ai_launchpad_completed' );
1670 }
1671
1672 /**
1673 * Get site option for migration source site domain
1674 *
1675 * @return string
1676 */
1677 public function get_migration_source_site_domain() {
1678 return get_option( 'migration_source_site_domain', '' );
1679 }
1680
1681 /**
1682 * Detect whether a site is WordPress.com Staging Site.
1683 *
1684 * @see class.json-api-site-jetpack.php for implementation.
1685 */
1686 abstract public function is_wpcom_staging_site();
1687
1688 /**
1689 * Get site option for the production blog id (if is a WP.com Staging Site).
1690 *
1691 * @see class.json-api-site-jetpack.php for implementation.
1692 */
1693 abstract public function get_wpcom_production_blog_id();
1694
1695 /**
1696 * Get site option for the staging blog ids (if it has them)
1697 *
1698 * @see class.json-api-site-jetpack.php for implementation.
1699 */
1700 abstract public function get_wpcom_staging_blog_ids();
1701
1702 /**
1703 * Get the site's Blaze eligibility status.
1704 *
1705 * @return bool
1706 */
1707 public function can_blaze() {
1708 return (bool) Blaze::site_supports_blaze( $this->blog_id );
1709 }
1710
1711 /**
1712 * Return site's setup identifier.
1713 *
1714 * @return string
1715 */
1716 public function get_wpcom_site_setup() {
1717 return get_option( 'wpcom_site_setup' );
1718 }
1719
1720 /**
1721 * Returns whether the site is commercial.
1722 *
1723 * @return mixed
1724 *
1725 * - `true`: the site is commercial
1726 * - `false`: the site is not commercial
1727 * - `null`: the commercial status is not yet determined
1728 */
1729 public function is_commercial() {
1730 // Override if blog has the commercial stickers.
1731 if ( function_exists( 'has_blog_sticker' ) ) {
1732 $has_not_commercial_sticker = has_blog_sticker( 'jetpack-site-is-not-commercial-override', $this->blog_id );
1733 if ( $has_not_commercial_sticker ) {
1734 return false;
1735 }
1736 $has_commercial_sticker = has_blog_sticker( 'jetpack-site-is-commercial-override', $this->blog_id );
1737 if ( $has_commercial_sticker ) {
1738 return true;
1739 }
1740 }
1741
1742 $is_commercial = get_option( '_jetpack_site_is_commercial', null );
1743 return $is_commercial === null ? null : (bool) $is_commercial;
1744 }
1745
1746 /**
1747 * Returns an array of reasons why the site is considered commercial.
1748 *
1749 * @return array|null
1750 */
1751 public function get_is_commercial_reasons() {
1752 $reasons = get_option( '_jetpack_site_is_commercial_reason', array() );
1753
1754 // Add override as reason if blog has the commercial stickers.
1755 if ( empty( $reasons ) && $this->is_commercial() ) {
1756 return array( 'manual-override' );
1757 } elseif ( ! is_array( $reasons ) ) {
1758 return array();
1759 }
1760
1761 return $reasons;
1762 }
1763
1764 /**
1765 * Returns the site's interface selection e.g. calypso vs. wp-admin
1766 *
1767 * @return string
1768 **/
1769 public function get_wpcom_admin_interface() {
1770 return (string) get_option( 'wpcom_admin_interface' );
1771 }
1772
1773 /**
1774 * Returns whether the site is part of the classic view early release.
1775 *
1776 * @return bool
1777 **/
1778 public function get_wpcom_classic_early_release() {
1779 return ! empty( get_option( 'wpcom_classic_early_release' ) );
1780 }
1781
1782 /**
1783 * Returns whether APM (Application Performance Monitoring) is enabled for the site.
1784 *
1785 * APM is an Atomic-only hosting feature. Non-Atomic site types (Simple wpcom, real
1786 * Jetpack) return false; Jetpack_Shadow_Site overrides with the actual read.
1787 *
1788 * @return bool
1789 **/
1790 public function get_apm_enabled() {
1791 return false;
1792 }
1793
1794 /**
1795 * Get Zendesk site meta.
1796 *
1797 * @return array|null
1798 */
1799 abstract public function get_zendesk_site_meta();
1800
1801 /**
1802 * Detect whether there's a pending plan for this site.
1803 *
1804 * @return bool
1805 */
1806 abstract public function is_pending_plan();
1807
1808 /**
1809 * Detect whether the site is a Garden site.
1810 *
1811 * @return bool
1812 */
1813 public function is_garden() {
1814 return false;
1815 }
1816
1817 /**
1818 * Get the Garden name.
1819 *
1820 * @return string
1821 */
1822 public function garden_name() {
1823 return null;
1824 }
1825
1826 /**
1827 * Get the Garden partner.
1828 *
1829 * @return string
1830 */
1831 public function garden_partner() {
1832 return null;
1833 }
1834
1835 /**
1836 * Detect whether the Garden site is provisioned.
1837 *
1838 * @return bool|null
1839 */
1840 public function garden_is_provisioned() {
1841 return null;
1842 }
1843
1844 /**
1845 * Detect whether the site is a Flex site.
1846 *
1847 * @return bool
1848 */
1849 public function is_wpcom_flex() {
1850 if ( function_exists( 'has_blog_sticker' ) ) {
1851 return has_blog_sticker( 'flex-cache-site' );
1852 }
1853 return false;
1854 }
1855
1856 /**
1857 * Detect whether Big Sky AI assistant is enabled for this site.
1858 *
1859 * @return bool
1860 */
1861 public function is_big_sky_enabled() {
1862 return false;
1863 }
1864
1865 /**
1866 * Get the state of any block on the site's outgoing email.
1867 *
1868 * @return array|null `status` (`blocked` or `at_risk`), `reason` and `expires_on`,
1869 * or null if the site has never been blocked.
1870 */
1871 public function get_atomic_email_block() {
1872 return null;
1873 }
1874
1875 /**
1876 * Get Jetpack recovery mode status.
1877 *
1878 * @return array|null
1879 */
1880 public function get_jetpack_recovery_mode_status() {
1881 $status = get_option( 'jetpack_recovery_mode_status' );
1882 return is_array( $status ) ? $status : null;
1883 }
1884 }
1885