PluginProbe
Jetpack – WP Security, Backup, Speed, & Growth / 10.3
Jetpack – WP Security, Backup, Speed, & Growth v10.3
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 14.3.1 All 501 releases
jetpack / sal / class.json-api-site-base.php

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

730 lines 18.2 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 use Automattic\Jetpack\Status\Host;
4
5 require_once dirname( __FILE__ ) . '/class.json-api-date.php';
6 require_once dirname( __FILE__ ) . '/class.json-api-post-base.php';
7
8 /**
9 * Base class for the Site Abstraction Layer (SAL)
10 * Note that this is the site "as seen by user $user_id with token $token", which
11 * is why we pass the token to the platform; these site instances are value objects
12 * to be used in the context of a single request for a single user.
13 * Also note that at present this class _assumes_ you've "switched to"
14 * the site in question, and functions like `get_bloginfo( 'name' )` will
15 * therefore return the correct value
16 **/
17 abstract class SAL_Site {
18 public $blog_id;
19 public $platform;
20
21 public function __construct( $blog_id, $platform ) {
22 $this->blog_id = $blog_id;
23 $this->platform = $platform;
24 }
25
26 public function get_id() {
27 return $this->blog_id;
28 }
29
30 public function get_name() {
31 return (string) htmlspecialchars_decode( get_bloginfo( 'name' ), ENT_QUOTES );
32 }
33
34 public function get_description() {
35 return (string) htmlspecialchars_decode( get_bloginfo( 'description' ), ENT_QUOTES );
36 }
37
38 public function get_url() {
39 return (string) home_url();
40 }
41
42 public function get_post_count() {
43 return (int) wp_count_posts( 'post' )->publish;
44 }
45
46 public function get_quota() {
47 return null;
48 }
49
50 abstract public function has_videopress();
51
52 abstract public function upgraded_filetypes_enabled();
53
54 abstract public function is_mapped_domain();
55
56 abstract public function get_unmapped_url();
57
58 abstract public function is_redirect();
59
60 abstract public function is_headstart_fresh();
61
62 abstract public function featured_images_enabled();
63
64 abstract public function has_wordads();
65
66 abstract public function get_frame_nonce();
67
68 abstract public function get_jetpack_frame_nonce();
69
70 abstract public function allowed_file_types();
71
72 abstract public function get_post_formats();
73
74 abstract public function is_private();
75
76 abstract public function is_coming_soon();
77
78 abstract public function is_following();
79
80 abstract public function get_subscribers_count();
81
82 abstract public function get_locale();
83
84 /**
85 * The flag indicates that the site has Jetpack installed
86 *
87 * @return bool
88 */
89 abstract public function is_jetpack();
90
91 /**
92 * The flag indicates that the site is connected to WP.com via Jetpack Connection
93 *
94 * @return bool
95 */
96 abstract public function is_jetpack_connection();
97
98 abstract public function get_jetpack_modules();
99
100 abstract public function is_module_active( $module );
101
102 abstract public function is_vip();
103
104 abstract public function is_multisite();
105
106 /**
107 * Points to the user ID of the site owner
108 *
109 * @return int for WP.com, null for Jetpack
110 */
111 abstract public function get_site_owner();
112
113 abstract public function is_single_user_site();
114
115 abstract public function get_plan();
116
117 abstract public function get_ak_vp_bundle_enabled();
118
119 abstract public function get_podcasting_archive();
120
121 abstract public function get_import_engine();
122
123 abstract public function get_jetpack_seo_front_page_description();
124
125 abstract public function get_jetpack_seo_title_formats();
126
127 abstract public function get_verification_services_codes();
128
129 abstract public function before_render();
130
131 abstract public function after_render( &$response );
132
133 // TODO - factor this out? Seems an odd thing to have on a site
134 abstract public function after_render_options( &$options );
135
136 // wrap a WP_Post object with SAL methods
137 abstract public function wrap_post( $post, $context );
138
139 abstract protected function is_a8c_publication( $post_id );
140
141 public function is_automated_transfer() {
142 /**
143 * Filter if a site is an automated-transfer site.
144 *
145 * @module json-api
146 *
147 * @since 6.4.0
148 *
149 * @param bool is_automated_transfer( $this->blog_id )
150 * @param int $blog_id Blog identifier.
151 */
152 return apply_filters(
153 'jetpack_site_automated_transfer',
154 false,
155 $this->blog_id
156 );
157 }
158
159 abstract protected function is_wpforteams_site();
160
161 /**
162 * Get hub blog id for P2 sites.
163 *
164 * @return null
165 */
166 public function get_p2_hub_blog_id() {
167 return null;
168 }
169
170 /**
171 * Getter for the p2 organization ID.
172 *
173 * @return int
174 */
175 public function get_p2_organization_id() {
176 return 0; // WPForTeams\Constants\NO_ORG_ID not loaded.
177 }
178
179 /**
180 * Detect whether a site is a WordPress.com on Atomic site.
181 *
182 * @return bool
183 */
184 public function is_wpcom_atomic() {
185 return ( new Host() )->is_woa_site();
186 }
187
188 public function is_wpcom_store() {
189 return false;
190 }
191
192 public function woocommerce_is_active() {
193 return false;
194 }
195
196 public function is_cloud_eligible() {
197 return false;
198 }
199
200 public function get_products() {
201 return array();
202 }
203
204 public function get_post_by_id( $post_id, $context ) {
205 $post = get_post( $post_id, OBJECT, $context );
206
207 if ( ! $post ) {
208 return new WP_Error( 'unknown_post', 'Unknown post', 404 );
209 }
210
211 $wrapped_post = $this->wrap_post( $post, $context );
212
213 // validate access
214 return $this->validate_access( $wrapped_post );
215 }
216
217 /**
218 * Validate current user can access the post
219 *
220 * @return WP_Error or post
221 */
222 private function validate_access( $post ) {
223 $context = $post->context;
224
225 if (
226 ! $this->is_post_type_allowed( $post->post_type )
227 && ! $this->is_a8c_publication( $post->ID )
228 ) {
229 return new WP_Error( 'unknown_post', 'Unknown post', 404 );
230 }
231
232 switch ( $context ) {
233 case 'edit' :
234 if ( ! current_user_can( 'edit_post', $post->ID ) ) {
235 return new WP_Error( 'unauthorized', 'User cannot edit post', 403 );
236 }
237 break;
238 case 'display' :
239 $can_view = $this->user_can_view_post( $post );
240 if ( is_wp_error( $can_view ) ) {
241 return $can_view;
242 }
243 break;
244 default :
245 return new WP_Error( 'invalid_context', 'Invalid API CONTEXT', 400 );
246 }
247
248 return $post;
249 }
250
251 public function current_user_can_access_post_type( $post_type, $context ) {
252 $post_type_object = $this->get_post_type_object( $post_type );
253 if ( ! $post_type_object ) {
254 return false;
255 }
256
257 switch( $context ) {
258 case 'edit':
259 return current_user_can( $post_type_object->cap->edit_posts );
260 case 'display':
261 return $post_type_object->public || current_user_can( $post_type_object->cap->read_private_posts );
262 default:
263 return false;
264 }
265 }
266
267 protected function get_post_type_object( $post_type ) {
268 return get_post_type_object( $post_type );
269 }
270
271 // copied from class.json-api-endpoints.php
272 public function is_post_type_allowed( $post_type ) {
273 // if the post type is empty, that's fine, WordPress will default to post
274 if ( empty( $post_type ) ) {
275 return true;
276 }
277
278 // allow special 'any' type
279 if ( 'any' == $post_type ) {
280 return true;
281 }
282
283 // check for allowed types
284 if ( in_array( $post_type, $this->get_whitelisted_post_types() ) ) {
285 return true;
286 }
287
288 if ( $post_type_object = get_post_type_object( $post_type ) ) {
289 if ( ! empty( $post_type_object->show_in_rest ) ) {
290 return $post_type_object->show_in_rest;
291 }
292 if ( ! empty( $post_type_object->publicly_queryable ) ) {
293 return $post_type_object->publicly_queryable;
294 }
295 }
296
297 return ! empty( $post_type_object->public );
298 }
299
300 // copied from class.json-api-endpoints.php
301 /**
302 * Gets the whitelisted post types that JP should allow access to.
303 *
304 * @return array Whitelisted post types.
305 */
306 public function get_whitelisted_post_types() {
307 $allowed_types = array( 'post', 'page', 'revision' );
308
309 /**
310 * Filter the post types Jetpack has access to, and can synchronize with WordPress.com.
311 *
312 * @module json-api
313 *
314 * @since 2.2.3
315 *
316 * @param array $allowed_types Array of whitelisted post types. Default to `array( 'post', 'page', 'revision' )`.
317 */
318 $allowed_types = apply_filters( 'rest_api_allowed_post_types', $allowed_types );
319
320 return array_unique( $allowed_types );
321 }
322
323 // copied and modified a little from class.json-api-endpoints.php
324 private function user_can_view_post( $post ) {
325 if ( !$post || is_wp_error( $post ) ) {
326 return false;
327 }
328
329 if ( 'inherit' === $post->post_status ) {
330 $parent_post = get_post( $post->post_parent );
331 $post_status_obj = get_post_status_object( $parent_post->post_status );
332 } else {
333 $post_status_obj = get_post_status_object( $post->post_status );
334 }
335
336 $authorized = (
337 $post_status_obj->public ||
338 ( is_user_logged_in() &&
339 (
340 ( $post_status_obj->protected && current_user_can( 'edit_post', $post->ID ) ) ||
341 ( $post_status_obj->private && current_user_can( 'read_post', $post->ID ) ) ||
342 ( 'trash' === $post->post_status && current_user_can( 'edit_post', $post->ID ) ) ||
343 'auto-draft' === $post->post_status
344 )
345 )
346 );
347
348 if ( ! $authorized ) {
349 return new WP_Error( 'unauthorized', 'User cannot view post', 403 );
350 }
351
352 if (
353 -1 == get_option( 'blog_public' ) &&
354 /**
355 * Filter access to a specific post.
356 *
357 * @module json-api
358 *
359 * @since 3.4.0
360 *
361 * @param bool current_user_can( 'read_post', $post->ID ) Can the current user access the post.
362 * @param WP_Post $post Post data.
363 */
364 ! apply_filters(
365 'wpcom_json_api_user_can_view_post',
366 current_user_can( 'read_post', $post->ID ),
367 $post
368 )
369 ) {
370 return new WP_Error( 'unauthorized', 'User cannot view post', array( 'status_code' => 403, 'error' => 'private_blog' ) );
371 }
372
373 if ( strlen( $post->post_password ) && !current_user_can( 'edit_post', $post->ID ) ) {
374 return new WP_Error( 'unauthorized', 'User cannot view password protected post', array( 'status_code' => 403, 'error' => 'password_protected' ) );
375 }
376
377 return true;
378 }
379
380 /**
381 * Get post ID by name
382 *
383 * Attempts to match name on post title and page path
384 *
385 * @param string $name
386 *
387 * @return int|object Post ID on success, WP_Error object on failure
388 */
389 public function get_post_id_by_name( $name ) {
390 $name = sanitize_title( $name );
391
392 if ( ! $name ) {
393 return new WP_Error( 'invalid_post', 'Invalid post', 400 );
394 }
395
396 $posts = get_posts( array(
397 'name' => $name,
398 'numberposts' => 1,
399 'post_type' => $this->get_whitelisted_post_types(),
400 ) );
401
402 if ( ! $posts || ! isset( $posts[0]->ID ) || ! $posts[0]->ID ) {
403 $page = get_page_by_path( $name );
404
405 if ( ! $page ) {
406 return new WP_Error( 'unknown_post', 'Unknown post', 404 );
407 }
408
409 return $page->ID;
410 }
411
412 return (int) $posts[0]->ID;
413 }
414
415 /**
416 * Get post by name
417 *
418 * Attempts to match name on post title and page path
419 *
420 * @param string $name
421 * @param string $context (display or edit)
422 *
423 * @return object Post object on success, WP_Error object on failure
424 **/
425 public function get_post_by_name( $name, $context ) {
426 $post_id = $this->get_post_id_by_name( $name );
427 if ( is_wp_error( $post_id ) ) {
428 return $post_id;
429 }
430
431 return $this->get_post_by_id( $post_id, $context );
432 }
433
434 function user_can_manage() {
435 current_user_can( 'manage_options' );
436 }
437
438 function get_xmlrpc_url() {
439 $xmlrpc_scheme = apply_filters( 'wpcom_json_api_xmlrpc_scheme', wp_parse_url( get_option( 'home' ), PHP_URL_SCHEME ) );
440 return site_url( 'xmlrpc.php', $xmlrpc_scheme );
441 }
442
443 function get_registered_date() {
444 if ( function_exists( 'get_blog_details' ) ) {
445 $blog_details = get_blog_details();
446 if ( ! empty( $blog_details->registered ) ) {
447 return WPCOM_JSON_API_Date::format_date( $blog_details->registered );
448 }
449 }
450
451 return '0000-00-00T00:00:00+00:00';
452 }
453
454 function get_capabilities() {
455 $is_wpcom_blog_owner = wpcom_get_blog_owner() === (int) get_current_user_id();
456
457 return array(
458 'edit_pages' => current_user_can( 'edit_pages' ),
459 'edit_posts' => current_user_can( 'edit_posts' ),
460 'edit_others_posts' => current_user_can( 'edit_others_posts' ),
461 'edit_others_pages' => current_user_can( 'edit_others_pages' ),
462 'delete_posts' => current_user_can( 'delete_posts' ),
463 'delete_others_posts' => current_user_can( 'delete_others_posts' ),
464 'edit_theme_options' => current_user_can( 'edit_theme_options' ),
465 'edit_users' => current_user_can( 'edit_users' ),
466 'list_users' => current_user_can( 'list_users' ),
467 'manage_categories' => current_user_can( 'manage_categories' ),
468 'manage_options' => current_user_can( 'manage_options' ),
469 'moderate_comments' => current_user_can( 'moderate_comments' ),
470 'activate_wordads' => $is_wpcom_blog_owner,
471 'promote_users' => current_user_can( 'promote_users' ),
472 'publish_posts' => current_user_can( 'publish_posts' ),
473 'upload_files' => current_user_can( 'upload_files' ),
474 'delete_users' => current_user_can( 'delete_users' ),
475 'remove_users' => current_user_can( 'remove_users' ),
476 'own_site' => $is_wpcom_blog_owner,
477 /**
478 * Filter whether the Hosting section in Calypso should be available for site.
479 *
480 * @module json-api
481 *
482 * @since 8.2.0
483 *
484 * @param bool $view_hosting Can site access Hosting section. Default to false.
485 */
486 'view_hosting' => apply_filters( 'jetpack_json_api_site_can_view_hosting', false ),
487 'view_stats' => stats_is_blog_user( $this->blog_id ),
488 'activate_plugins' => current_user_can( 'activate_plugins' ),
489 );
490 }
491
492 function is_visible() {
493 if ( is_user_logged_in() ) {
494 $current_user = wp_get_current_user();
495 $visible = (array) get_user_meta( $current_user->ID, 'blog_visibility', true );
496
497 $is_visible = true;
498 if ( isset( $visible[ $this->blog_id ] ) ) {
499 $is_visible = (bool) $visible[ $this->blog_id ];
500 }
501
502 // null and true are visible
503 return $is_visible;
504 }
505
506 return null;
507 }
508
509 function get_logo() {
510 // Set an empty response array.
511 $logo_setting = array(
512 'id' => (int) 0,
513 'sizes' => array(),
514 'url' => '',
515 );
516
517 // Get current site logo values.
518 $logo_id = get_option( 'site_logo' );
519
520 // Update the response array if there's a site logo currenty active.
521 if ( $logo_id ) {
522 $logo_setting['id'] = $logo_id;
523 $logo_setting['url'] = wp_get_attachment_url( $logo_id );
524 }
525
526 return $logo_setting;
527 }
528
529 function get_timezone() {
530 return (string) get_option( 'timezone_string' );
531 }
532
533 function get_gmt_offset() {
534 return (float) get_option( 'gmt_offset' );
535 }
536
537 function get_login_url() {
538 return wp_login_url();
539 }
540
541 function get_admin_url() {
542 return get_admin_url();
543 }
544
545 function get_theme_slug() {
546 return get_option( 'stylesheet' );
547 }
548
549 function get_header_image() {
550 return get_theme_mod( 'header_image_data' );
551 }
552
553 function get_background_color() {
554 return get_theme_mod( 'background_color' );
555 }
556
557 function get_image_default_link_type() {
558 return get_option( 'image_default_link_type' );
559 }
560
561 function get_image_thumbnail_width() {
562 return (int) get_option( 'thumbnail_size_w' );
563 }
564
565 function get_image_thumbnail_height() {
566 return (int) get_option( 'thumbnail_size_h' );
567 }
568
569 function get_image_thumbnail_crop() {
570 return get_option( 'thumbnail_crop' );
571 }
572
573 function get_image_medium_width() {
574 return (int) get_option( 'medium_size_w' );
575 }
576
577 function get_image_medium_height() {
578 return (int) get_option( 'medium_size_h' );
579 }
580
581 function get_image_large_width() {
582 return (int) get_option( 'large_size_w' );
583 }
584
585 function get_image_large_height() {
586 return (int) get_option( 'large_size_h' );
587 }
588
589 function get_permalink_structure() {
590 return get_option( 'permalink_structure' );
591 }
592
593 function get_default_post_format() {
594 return get_option( 'default_post_format' );
595 }
596
597 function get_default_category() {
598 return (int) get_option( 'default_category' );
599 }
600
601 function get_show_on_front() {
602 return get_option( 'show_on_front' );
603 }
604
605 function is_custom_front_page() {
606 return ( 'page' === $this->get_show_on_front() );
607 }
608
609 function get_default_likes_enabled() {
610 return (bool) apply_filters( 'wpl_is_enabled_sitewide', ! get_option( 'disabled_likes' ) );
611 }
612
613 function get_default_sharing_status() {
614 $default_sharing_status = false;
615 if ( class_exists( 'Sharing_Service' ) ) {
616 $ss = new Sharing_Service();
617 $blog_services = $ss->get_blog_services();
618 $default_sharing_status = ! empty( $blog_services['visible'] );
619 }
620 return (bool) $default_sharing_status;
621 }
622
623 function get_default_comment_status() {
624 return 'closed' !== get_option( 'default_comment_status' );
625 }
626
627 function default_ping_status() {
628 return 'closed' !== get_option( 'default_ping_status' );
629 }
630
631 function is_publicize_permanently_disabled() {
632 $publicize_permanently_disabled = false;
633 if ( function_exists( 'is_publicize_permanently_disabled' ) ) {
634 $publicize_permanently_disabled = is_publicize_permanently_disabled( $this->blog_id );
635 }
636 return $publicize_permanently_disabled;
637 }
638
639 function get_page_on_front() {
640 return (int) get_option( 'page_on_front' );
641 }
642
643 function get_page_for_posts() {
644 return (int) get_option( 'page_for_posts' );
645 }
646
647 function is_headstart() {
648 return get_option( 'headstart' );
649 }
650
651 function get_wordpress_version() {
652 global $wp_version;
653 return $wp_version;
654 }
655
656 function is_domain_only() {
657 $options = get_option( 'options' );
658 return ! empty ( $options['is_domain_only'] ) ? (bool) $options['is_domain_only'] : false;
659 }
660
661 function get_blog_public() {
662 return (int) get_option( 'blog_public' );
663 }
664
665 function has_pending_automated_transfer() {
666 /**
667 * Filter if a site is in pending automated transfer state.
668 *
669 * @module json-api
670 *
671 * @since 6.4.0
672 *
673 * @param bool has_site_pending_automated_transfer( $this->blog_id )
674 * @param int $blog_id Blog identifier.
675 */
676 return apply_filters(
677 'jetpack_site_pending_automated_transfer',
678 false,
679 $this->blog_id
680 );
681 }
682
683 function signup_is_store() {
684 return $this->get_design_type() === 'store';
685 }
686
687 function get_roles() {
688 return new WP_Roles();
689 }
690
691 function get_design_type() {
692 $options = get_option( 'options' );
693 return empty( $options[ 'designType'] ) ? null : $options[ 'designType' ];
694 }
695
696 function get_site_goals() {
697 $options = get_option( 'options' );
698 return empty( $options[ 'siteGoals'] ) ? null : $options[ 'siteGoals' ];
699 }
700
701 function get_launch_status() {
702 return false;
703 }
704
705 function get_migration_meta() {
706 return null;
707 }
708
709 function get_site_segment() {
710 return false;
711 }
712
713 function get_site_creation_flow() {
714 return get_option( 'site_creation_flow' );
715 }
716
717 public function get_selected_features() {
718 return get_option( 'selected_features' );
719 }
720
721 /**
722 * Get the option storing the Anchor podcast ID that identifies a site as a podcasting site.
723 *
724 * @return string
725 */
726 public function get_anchor_podcast() {
727 return get_option( 'anchor_podcast' );
728 }
729 }
730