PluginProbe ʕ •ᴥ•ʔ
Jetpack – WP Security, Backup, Speed, & Growth / 13.9.2
Jetpack – WP Security, Backup, Speed, & Growth v13.9.2
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 14.4.2 14.5.1 14.6.1 14.7.1 14.8.1 14.9.2 15.0.3 15.1.2 15.2.1 15.3.2 15.4.1 15.5.1 15.6.1 15.7.2 15.8.1 15.9.2 16.0.2 16.1.3 16.2-a.5 16.2-a.3 16.1.2 16.2-a.1 16.1.1 16.1 16.1-beta 16.1-beta.2 16.1-beta.3 16.1-a.5 16.1-a.3 16.0.1 16.1-a.1 16.0 16.0-beta 16.0-a.7 16.0-a.5 15.9.1 16.0-a.3 16.0-a.1 15.9 15.9-beta 15.9-a.7 15.9-a.5 15.9-a.3 15.9-a.1 15.8 15.8-beta 15.8-a.7 15.8-a.5 5.2.5 5.3.4 5.4.4 5.5.5 5.6.5 5.7.5 5.8.4 5.9.4 6.0.4 6.1 6.1.1 6.1.2 6.1.3 6.1.4 6.1.5 6.2 6.2.1 6.2.2 6.2.3 6.2.4 6.2.5 6.3 6.3.1 6.3.2 6.3.3 6.3.4 6.3.5 6.3.6 6.3.7 6.4 6.4.1 6.4.2 6.4.3 6.4.4 6.4.5 6.4.6 6.5 6.5.1 6.5.2 6.5.3 6.5.4 6.6 6.6.1 6.6.2 6.6.3 6.6.4 6.6.5 6.7 6.7.1 6.7.2 6.7.3 6.7.4 6.8 6.8.1 6.8.2 6.8.3 6.8.4 6.8.5 6.9 6.9.1 6.9.2 6.9.3 6.9.4 7.0 7.0.1 7.0.2 7.0.3 7.0.4 7.0.5 7.1 7.1.1 7.1.2 7.1.3 7.1.4 7.1.5 7.2 7.2.1 7.2.1.1 7.2.2 7.2.3 7.2.4 7.2.5 7.3 7.3.0.1 7.3.1 7.3.1.1 7.3.2 7.3.3 7.3.4 7.3.5 7.4 7.4.1 7.4.2 7.4.3 7.4.4 7.4.5 7.5 7.5.0.1 7.5.1 7.5.2 7.5.3 7.5.4 7.5.5 7.5.6 7.5.7 7.6 7.6.1 7.6.2 7.6.3 7.6.4 7.7 7.7.1 7.7.2 7.7.3 7.7.4 7.7.5 7.7.6 7.8 7.8.1 7.8.2 7.8.3 7.8.4 7.9 7.9.1 7.9.2 7.9.3 7.9.4 8.0 8.0.1 8.0.2 8.0.3 8.1 8.1.1 8.1.2 8.1.3 8.1.4 8.2 8.2.0.1 8.2.1 8.2.2 8.2.3 8.2.4 8.2.5 8.2.6 8.3 8.3.1 8.3.2 8.3.3 8.4 8.4.1 8.4.2 8.4.3 8.4.4 8.4.5 8.5 8.5.1 8.5.2 8.5.3 8.6 8.6.1 8.6.2 8.6.3 8.6.4 8.7 8.7.0.1 8.7.1 8.7.2 8.7.3 8.7.4 8.8 8.8.1 8.8.2 8.8.3 8.8.4 8.8.5 8.9 8.9.1 8.9.2 8.9.3 8.9.4 9.0 9.0.1 9.0.2 9.0.3 9.0.4 9.0.5 9.1 9.1.1 9.1.2 9.1.3 9.2 9.2.1 9.2.2 9.2.3 9.2.4 9.3 9.3.1 9.3.2 9.3.3 9.3.4 9.3.5 9.4 9.4.1 9.4.2 9.4.3 9.4.4 9.5 9.5.1 9.5.2 9.5.3 9.5.4 9.5.5 9.6 9.6.1 9.6.2 9.6.3 9.6.4 9.7 9.7.1 9.7.2 15.7-beta.2 9.7.3 15.7.1 9.8 15.8-a.1 9.8.1 15.8-a.3 9.8.2 2.0.9 9.8.3 2.1.7 9.9 2.2.10 9.9.1 2.3.10 9.9.2 2.4.7 9.9.3 2.5.5 2.6.6 2.7.5 2.8.5 2.9.6 3.0.6 3.1.5 3.2.5 3.3.6 3.4.6 3.5.6 3.6.4 3.7.5 3.8.5 3.9.10 4.0.7 4.1.4 4.2.5 4.3.5 4.4.5 4.5.3 4.6.3 4.7.4 4.8.5 4.9.3 5.0.3 5.1.4 trunk 10.0 10.0.1 10.0.2 10.1 10.1.1 10.1.2 10.2 10.2.1 10.2.2 10.2.3 10.3 10.3.1 10.3.2 10.4 10.4.1 10.4.2 10.5 10.5.1 10.5.2 10.5.3 10.6 10.6.1 10.6.2 10.7 10.7.1 10.7.2 10.8 10.8.1 10.8.2 10.9 10.9.1 10.9.2 10.9.3 11.0 11.0.1 11.0.2 11.1 11.1.1 11.1.2 11.1.3 11.1.4 11.2 11.2.1 11.2.2 11.3 11.3.1 11.3.2 11.3.3 11.3.4 11.4 11.4.1 11.4.2 11.5 11.5.1 11.5.2 11.5.3 11.6 11.6.1 11.6.2 11.7 11.7.1 11.7.2 11.7.3 11.8 11.8.3 11.8.4 11.8.5 11.8.6 11.9 11.9.1 11.9.2 11.9.3 12.0 12.0.1 12.0.2 12.1 12.1.1 12.1.2 12.2 12.2.1 12.2.2 12.3 12.3.1 12.4 12.4.1 12.5 12.5.1 12.6 12.6.1 12.6.2 12.6.3 12.7 12.7.1 12.7.2 12.8 12.8.1 12.8.2 12.9 12.9.1 12.9.2 12.9.3 12.9.4 13.0 13.0.1 13.1 13.1.1 13.1.2 13.1.3 13.1.4 13.2 13.2.1 13.2.2 13.2.3 13.3 13.3.1 13.3.2 13.4 13.4.1 13.4.2 13.4.3 13.4.4 13.5 13.5.1 13.6 13.6.1 13.7 13.7.1 13.8 13.8.1 13.8.2 13.9 13.9.1 14.0 14.1 14.2 14.2.1 14.3 14.4 14.4.1 14.5 14.6 14.7 14.8 14.9 14.9.1 15.0 15.0.1 15.0.2 15.1 15.1.1 15.2 15.3 15.3.1 15.4 15.5 15.6 15.7 15.7-a.1 15.7-a.3 15.7-a.5 15.7-a.7 15.7-beta
jetpack / json-endpoints / class.wpcom-json-api-site-settings-endpoint.php
jetpack / json-endpoints Last commit date
jetpack 2 years ago class.wpcom-json-api-add-widget-endpoint.php 2 years ago class.wpcom-json-api-autosave-post-v1-1-endpoint.php 5 years ago class.wpcom-json-api-bulk-delete-post-endpoint.php 4 years ago class.wpcom-json-api-bulk-restore-post-endpoint.php 2 years ago class.wpcom-json-api-bulk-update-comments-endpoint.php 3 years ago class.wpcom-json-api-comment-endpoint.php 2 years ago class.wpcom-json-api-delete-media-endpoint.php 4 years ago class.wpcom-json-api-delete-media-v1-1-endpoint.php 4 years ago class.wpcom-json-api-edit-media-v1-2-endpoint.php 2 years ago class.wpcom-json-api-get-autosave-v1-1-endpoint.php 5 years ago class.wpcom-json-api-get-comment-counts-endpoint.php 4 years ago class.wpcom-json-api-get-comment-endpoint.php 4 years ago class.wpcom-json-api-get-comment-history-endpoint.php 4 years ago class.wpcom-json-api-get-comments-tree-endpoint.php 4 years ago class.wpcom-json-api-get-comments-tree-v1-1-endpoint.php 4 years ago class.wpcom-json-api-get-comments-tree-v1-2-endpoint.php 4 years ago class.wpcom-json-api-get-customcss.php 2 years ago class.wpcom-json-api-get-media-endpoint.php 4 years ago class.wpcom-json-api-get-media-v1-1-endpoint.php 4 years ago class.wpcom-json-api-get-media-v1-2-endpoint.php 2 years ago class.wpcom-json-api-get-post-counts-v1-1-endpoint.php 2 years ago class.wpcom-json-api-get-post-endpoint.php 2 years ago class.wpcom-json-api-get-post-v1-1-endpoint.php 2 years ago class.wpcom-json-api-get-site-endpoint.php 1 year ago class.wpcom-json-api-get-site-v1-2-endpoint.php 2 years ago class.wpcom-json-api-get-taxonomies-endpoint.php 2 years ago class.wpcom-json-api-get-taxonomy-endpoint.php 4 years ago class.wpcom-json-api-get-term-endpoint.php 4 years ago class.wpcom-json-api-list-comments-endpoint.php 2 years ago class.wpcom-json-api-list-dropdown-pages-endpoint.php 3 years ago class.wpcom-json-api-list-embeds-endpoint.php 4 years ago class.wpcom-json-api-list-media-endpoint.php 2 years ago class.wpcom-json-api-list-media-v1-1-endpoint.php 2 years ago class.wpcom-json-api-list-media-v1-2-endpoint.php 2 years ago class.wpcom-json-api-list-post-type-taxonomies-endpoint.php 4 years ago class.wpcom-json-api-list-post-types-endpoint.php 3 years ago class.wpcom-json-api-list-posts-endpoint.php 2 years ago class.wpcom-json-api-list-posts-v1-1-endpoint.php 3 years ago class.wpcom-json-api-list-posts-v1-2-endpoint.php 3 years ago class.wpcom-json-api-list-roles-endpoint.php 2 years ago class.wpcom-json-api-list-shortcodes-endpoint.php 4 years ago class.wpcom-json-api-list-terms-endpoint.php 2 years ago class.wpcom-json-api-list-users-endpoint.php 2 years ago class.wpcom-json-api-menus-v1-1-endpoint.php 2 years ago class.wpcom-json-api-post-endpoint.php 2 years ago class.wpcom-json-api-post-v1-1-endpoint.php 2 years ago class.wpcom-json-api-render-embed-endpoint.php 2 years ago class.wpcom-json-api-render-embed-reversal-endpoint.php 2 years ago class.wpcom-json-api-render-endpoint.php 3 years ago class.wpcom-json-api-render-shortcode-endpoint.php 3 years ago class.wpcom-json-api-sharing-buttons-endpoint.php 2 years ago class.wpcom-json-api-site-settings-endpoint.php 1 year ago class.wpcom-json-api-site-settings-v1-2-endpoint.php 2 years ago class.wpcom-json-api-site-settings-v1-3-endpoint.php 2 years ago class.wpcom-json-api-site-settings-v1-4-endpoint.php 1 year ago class.wpcom-json-api-site-user-endpoint.php 2 years ago class.wpcom-json-api-taxonomy-endpoint.php 4 years ago class.wpcom-json-api-update-comment-endpoint.php 2 years ago class.wpcom-json-api-update-customcss.php 2 years ago class.wpcom-json-api-update-media-endpoint.php 4 years ago class.wpcom-json-api-update-media-v1-1-endpoint.php 2 years ago class.wpcom-json-api-update-post-endpoint.php 3 years ago class.wpcom-json-api-update-post-v1-1-endpoint.php 2 years ago class.wpcom-json-api-update-post-v1-2-endpoint.php 2 years ago class.wpcom-json-api-update-site-homepage-endpoint.php 4 years ago class.wpcom-json-api-update-site-logo-endpoint.php 2 years ago class.wpcom-json-api-update-taxonomy-endpoint.php 4 years ago class.wpcom-json-api-update-term-endpoint.php 4 years ago class.wpcom-json-api-update-user-endpoint.php 3 years ago class.wpcom-json-api-upload-media-endpoint.php 3 years ago class.wpcom-json-api-upload-media-v1-1-endpoint.php 2 years ago
class.wpcom-json-api-site-settings-endpoint.php
1346 lines
1 <?php // phpcs:ignore WordPress.Files.FileName.InvalidClassFileName
2 /**
3 * Manage settings via the WordPress.com REST API.
4 *
5 * @package automattic/jetpack
6 */
7
8 use Automattic\Jetpack\Waf\Brute_Force_Protection\Brute_Force_Protection_Shared_Functions;
9
10 new WPCOM_JSON_API_Site_Settings_Endpoint(
11 array(
12 'description' => 'Get detailed settings information about a site.',
13 'group' => '__do_not_document',
14 'stat' => 'sites:X',
15 'max_version' => '1.1',
16 'new_version' => '1.2',
17 'method' => 'GET',
18 'path' => '/sites/%s/settings',
19 'path_labels' => array(
20 '$site' => '(int|string) Site ID or domain',
21 ),
22
23 'query_parameters' => array(
24 'context' => false,
25 ),
26
27 'response_format' => WPCOM_JSON_API_Site_Settings_Endpoint::$site_format,
28
29 'example_request' => 'https://public-api.wordpress.com/rest/v1/sites/en.blog.wordpress.com/settings',
30 )
31 );
32
33 new WPCOM_JSON_API_Site_Settings_Endpoint(
34 array(
35 'description' => 'Update settings for a site.',
36 'group' => '__do_not_document',
37 'stat' => 'sites:X',
38 'max_version' => '1.1',
39 'new_version' => '1.2',
40 'method' => 'POST',
41 'path' => '/sites/%s/settings',
42 'a_new_very_long_key' => 'blabla',
43 'path_labels' => array(
44 '$site' => '(int|string) Site ID or domain',
45 ),
46
47 'request_format' => array(
48 'migration_source_site_domain' => '(string) The source site URL, from the migration flow',
49 'in_site_migration_flow' => '(string) The migration flow the site is in',
50 'blogname' => '(string) Blog name',
51 'blogdescription' => '(string) Blog description',
52 'default_pingback_flag' => '(bool) Notify blogs linked from article?',
53 'default_ping_status' => '(bool) Allow link notifications from other blogs?',
54 'default_comment_status' => '(bool) Allow comments on new articles?',
55 'blog_public' => '(string) Site visibility; -1: private, 0: discourage search engines, 1: allow search engines',
56 'wpcom_data_sharing_opt_out' => '(bool) Did the site opt out of sharing public content with third parties and research partners?',
57 'jetpack_sync_non_public_post_stati' => '(bool) allow sync of post and pages with non-public posts stati',
58 'jetpack_relatedposts_enabled' => '(bool) Enable related posts?',
59 'jetpack_relatedposts_show_context' => '(bool) Show post\'s tags and category in related posts?',
60 'jetpack_relatedposts_show_date' => '(bool) Show date in related posts?',
61 'jetpack_relatedposts_show_headline' => '(bool) Show headline in related posts?',
62 'jetpack_relatedposts_show_thumbnails' => '(bool) Show thumbnails in related posts?',
63 'jetpack_protect_whitelist' => '(array) List of IP addresses to always allow',
64 'instant_search_enabled' => '(bool) Enable the new Jetpack Instant Search interface',
65 'jetpack_search_enabled' => '(bool) Enable Jetpack Search',
66 'jetpack_search_supported' => '(bool) Jetpack Search is supported',
67 'infinite_scroll' => '(bool) Support infinite scroll of posts?',
68 'default_category' => '(int) Default post category',
69 'default_post_format' => '(string) Default post format',
70 'require_name_email' => '(bool) Require comment authors to fill out name and email?',
71 'comment_registration' => '(bool) Require users to be registered and logged in to comment?',
72 'close_comments_for_old_posts' => '(bool) Automatically close comments on old posts?',
73 'close_comments_days_old' => '(int) Age at which to close comments',
74 'thread_comments' => '(bool) Enable threaded comments?',
75 'thread_comments_depth' => '(int) Depth to thread comments',
76 'page_comments' => '(bool) Break comments into pages?',
77 'comments_per_page' => '(int) Number of comments to display per page',
78 'default_comments_page' => '(string) newest|oldest Which page of comments to display first',
79 'comment_order' => '(string) asc|desc Order to display comments within page',
80 'comments_notify' => '(bool) Email me when someone comments?',
81 'moderation_notify' => '(bool) Email me when a comment is helf for moderation?',
82 'social_notifications_like' => '(bool) Email me when someone likes my post?',
83 'social_notifications_reblog' => '(bool) Email me when someone reblogs my post?',
84 'social_notifications_subscribe' => '(bool) Email me when someone subscribes to my blog?',
85 'comment_moderation' => '(bool) Moderate comments for manual approval?',
86 'comment_previously_approved' => '(bool) Moderate comments unless author has a previously-approved comment?',
87 'comment_max_links' => '(int) Moderate comments that contain X or more links',
88 'moderation_keys' => '(string) Words or phrases that trigger comment moderation, one per line',
89 'disallowed_keys' => '(string) Words or phrases that mark comment spam, one per line',
90 'lang_id' => '(int) ID for language blog is written in',
91 'wga' => '(array) Google Analytics Settings',
92 'disabled_likes' => '(bool) Are likes globally disabled (they can still be turned on per post)?',
93 'disabled_reblogs' => '(bool) Are reblogs disabled on posts?',
94 'jetpack_comment_likes_enabled' => '(bool) Are comment likes enabled for all comments?',
95 'sharing_button_style' => '(string) Style to use for sharing buttons (icon-text, icon, text, or official)',
96 'sharing_label' => '(string) Label to use for sharing buttons, e.g. "Share this:"',
97 'sharing_show' => '(string|array:string) Post type or array of types where sharing buttons are to be displayed',
98 'sharing_open_links' => '(string) Link target for sharing buttons (same or new)',
99 'twitter_via' => '(string) Twitter username to include in tweets when people share using the Twitter button',
100 'jetpack-twitter-cards-site-tag' => '(string) The Twitter username of the owner of the site\'s domain.',
101 'eventbrite_api_token' => '(int) The Keyring token ID for an Eventbrite token to associate with the site',
102 'timezone_string' => '(string) PHP-compatible timezone string like \'UTC-5\'',
103 'gmt_offset' => '(int) Site offset from UTC in hours',
104 'date_format' => '(string) PHP Date-compatible date format',
105 'time_format' => '(string) PHP Date-compatible time format',
106 'start_of_week' => '(int) Starting day of week (0 = Sunday, 6 = Saturday)',
107 'jetpack_testimonial' => '(bool) Whether testimonial custom post type is enabled for the site',
108 'jetpack_testimonial_posts_per_page' => '(int) Number of testimonials to show per page',
109 'jetpack_portfolio' => '(bool) Whether portfolio custom post type is enabled for the site',
110 'jetpack_portfolio_posts_per_page' => '(int) Number of portfolio projects to show per page',
111 Jetpack_SEO_Utils::FRONT_PAGE_META_OPTION => '(string) The seo meta description for the site.',
112 Jetpack_SEO_Titles::TITLE_FORMATS_OPTION => '(array) SEO meta title formats. Allowed keys: front_page, posts, pages, groups, archives',
113 'verification_services_codes' => '(array) Website verification codes. Allowed keys: google, pinterest, bing, yandex, facebook',
114 'markdown_supported' => '(bool) Whether markdown is supported for this site',
115 'wpcom_publish_posts_with_markdown' => '(bool) Whether markdown is enabled for posts',
116 'wpcom_publish_comments_with_markdown' => '(bool) Whether markdown is enabled for comments',
117 'site_icon' => '(int) Media attachment ID to use as site icon. Set to zero or an otherwise empty value to clear',
118 'api_cache' => '(bool) Turn on/off the Jetpack JSON API cache',
119 'posts_per_page' => '(int) Number of posts to show on blog pages',
120 'posts_per_rss' => '(int) Number of posts to show in the RSS feed',
121 'rss_use_excerpt' => '(bool) Whether the RSS feed will use post excerpts',
122 'launchpad_screen' => '(string) Whether or not launchpad is presented and what size it will be',
123 'sm_enabled' => '(bool) Whether the newsletter subscribe modal is enabled',
124 'jetpack_subscribe_overlay_enabled' => '(bool) Whether the newsletter subscribe overlay is enabled',
125 'jetpack_subscriptions_subscribe_post_end_enabled' => '(bool) Whether the Subscribe block at the end of each post placement is enabled',
126 'jetpack_subscriptions_login_navigation_enabled' => '(bool) Whether the Subscriber Login block navigation placement is enabled',
127 'jetpack_subscriptions_subscribe_navigation_enabled' => '(Bool) Whether the Subscribe block navigation placement is enabled',
128 'wpcom_ai_site_prompt' => '(string) User input in the AI site prompt',
129 'jetpack_waf_automatic_rules' => '(bool) Whether the WAF should enforce automatic firewall rules',
130 'jetpack_waf_ip_allow_list' => '(string) List of IP addresses to always allow',
131 'jetpack_waf_ip_allow_list_enabled' => '(bool) Whether the IP allow list is enabled',
132 'jetpack_waf_ip_block_list' => '(string) List of IP addresses the WAF should always block',
133 'jetpack_waf_ip_block_list_enabled' => '(bool) Whether the IP block list is enabled',
134 'jetpack_waf_share_data' => '(bool) Whether the WAF should share basic data with Jetpack',
135 'jetpack_waf_share_debug_data' => '(bool) Whether the WAF should share debug data with Jetpack',
136 'jetpack_waf_automatic_rules_last_updated_timestamp' => '(int) Timestamp of the last time the automatic rules were updated',
137 ),
138
139 'response_format' => array(
140 'updated' => '(array)',
141 ),
142
143 'example_request' => 'https://public-api.wordpress.com/rest/v1/sites/en.blog.wordpress.com/settings',
144 )
145 );
146
147 /**
148 * Manage Site settings endpoint.
149 */
150 class WPCOM_JSON_API_Site_Settings_Endpoint extends WPCOM_JSON_API_Endpoint {
151
152 /**
153 * Site format.
154 *
155 * @var array
156 */
157 public static $site_format = array(
158 'ID' => '(int) Site ID',
159 'name' => '(string) Title of site',
160 'description' => '(string) Tagline or description of site',
161 'URL' => '(string) Full URL to the site',
162 'lang' => '(string) Primary language code of the site',
163 'locale_variant' => '(string) Locale variant code for the site, if set',
164 'settings' => '(array) An array of options/settings for the blog. Only viewable by users with post editing rights to the site.',
165 );
166
167 /**
168 * Endpoint response
169 *
170 * GET /sites/%s/settings
171 * POST /sites/%s/settings
172 *
173 * @param string $path Path.
174 * @param int $blog_id Blog ID.
175 */
176 public function callback( $path = '', $blog_id = 0 ) {
177 $blog_id = $this->api->switch_to_blog_and_validate_user( $this->api->get_blog_id( $blog_id ) );
178 if ( is_wp_error( $blog_id ) ) {
179 return $blog_id;
180 }
181
182 if ( defined( 'IS_WPCOM' ) && IS_WPCOM ) {
183 // Source & include the infinite scroll compatibility files prior to loading theme functions.
184 add_filter( 'restapi_theme_action_copy_dirs', array( 'WPCOM_JSON_API_Site_Settings_Endpoint', 'wpcom_restapi_copy_theme_plugin_actions' ) );
185 $this->load_theme_functions();
186 }
187
188 if ( ! is_user_logged_in() ) {
189 return new WP_Error( 'Unauthorized', 'You must be logged-in to manage settings.', 401 );
190 } elseif ( ! current_user_can( 'manage_options' ) ) {
191 return new WP_Error( 'Forbidden', 'You do not have the capability to manage settings for this site.', 403 );
192 }
193
194 if ( 'GET' === $this->api->method ) {
195 /**
196 * Fires on each GET request to a specific endpoint.
197 *
198 * @module json-api
199 *
200 * @since 3.2.0
201 *
202 * @param string sites.
203 */
204 do_action( 'wpcom_json_api_objects', 'sites' );
205 return $this->get_settings_response();
206 } elseif ( 'POST' === $this->api->method ) {
207 return $this->update_settings();
208 } else {
209 return new WP_Error( 'bad_request', 'An unsupported request method was used.' );
210 }
211 }
212
213 /**
214 * Includes additional theme-specific files to be included in REST API theme
215 * context loading action copying.
216 *
217 * @see WPCOM_JSON_API_Endpoint#load_theme_functions
218 * @see the_neverending_home_page_theme_support
219 *
220 * @param array $copy_dirs Array of files to be included in theme context.
221 */
222 public static function wpcom_restapi_copy_theme_plugin_actions( $copy_dirs ) {
223 $theme_name = get_stylesheet();
224 $default_file_name = WP_CONTENT_DIR . "/mu-plugins/infinity/themes/{$theme_name}.php";
225
226 /**
227 * Filter the path to the Infinite Scroll compatibility file.
228 *
229 * @module infinite-scroll
230 *
231 * @since 2.0.0
232 *
233 * @param string $str IS compatibility file path.
234 * @param string $theme_name Theme name.
235 */
236 $customization_file = apply_filters( 'infinite_scroll_customization_file', $default_file_name, $theme_name );
237
238 if ( is_readable( $customization_file ) ) {
239 require_once $customization_file;
240 $copy_dirs[] = $customization_file;
241 }
242
243 return $copy_dirs;
244 }
245
246 /**
247 * Determines whether jetpack_relatedposts is supported
248 *
249 * @return bool
250 */
251 public function jetpack_relatedposts_supported() {
252 $wpcom_related_posts_theme_blacklist = array(
253 'Expound',
254 'Traveler',
255 'Opti',
256 'Currents',
257 );
258 return ( ! in_array( wp_get_theme()->get( 'Name' ), $wpcom_related_posts_theme_blacklist, true ) );
259 }
260
261 /**
262 * Returns category details
263 *
264 * @param WP_Term $category Category object.
265 *
266 * @return array
267 */
268 public function get_category_details( $category ) {
269 return array(
270 'value' => $category->term_id,
271 'name' => $category->name,
272 );
273 }
274
275 /**
276 * Returns an option value as the result of the callable being applied to
277 * it if a value is set, otherwise null.
278 *
279 * @param string $option_name Option name.
280 * @param callable $cast_callable Callable to invoke on option value.
281 *
282 * @return int|null Numeric option value or null.
283 */
284 protected function get_cast_option_value_or_null( $option_name, $cast_callable ) {
285 $option_value = get_option( $option_name, null );
286 if ( $option_value === null ) {
287 return $option_value;
288 }
289
290 return call_user_func( $cast_callable, $option_value );
291 }
292
293 /**
294 * Collects the necessary information to return for a get settings response.
295 *
296 * @return array
297 */
298 public function get_settings_response() {
299 $response = array();
300
301 // Allow update in later versions.
302 /**
303 * Filter the structure of site settings to return.
304 *
305 * @module json-api
306 *
307 * @since 3.9.3
308 *
309 * @param array $site_format Data structure.
310 */
311 $response_format = apply_filters( 'site_settings_site_format', self::$site_format );
312
313 $blog_id = (int) $this->api->get_blog_id_for_output();
314 $site = $this->get_platform()->get_site( $blog_id );
315
316 foreach ( array_keys( $response_format ) as $key ) {
317
318 // refactoring to change lang parameter to locale in 1.2.
319 $lang_or_locale = $this->get_locale( $key );
320 if ( $lang_or_locale ) {
321 $response[ $key ] = $lang_or_locale;
322 continue;
323 }
324
325 switch ( $key ) {
326 case 'ID':
327 $response[ $key ] = $blog_id;
328 break;
329 case 'name':
330 $response[ $key ] = (string) htmlspecialchars_decode( get_bloginfo( 'name' ), ENT_QUOTES );
331 break;
332 case 'description':
333 $response[ $key ] = (string) htmlspecialchars_decode( get_bloginfo( 'description' ), ENT_QUOTES );
334 break;
335 case 'URL':
336 $response[ $key ] = (string) home_url();
337 break;
338 case 'locale_variant':
339 if ( function_exists( 'wpcom_l10n_get_blog_locale_variant' ) ) {
340 $blog_locale_variant = wpcom_l10n_get_blog_locale_variant();
341 if ( $blog_locale_variant ) {
342 $response[ $key ] = $blog_locale_variant;
343 }
344 }
345 break;
346 case 'settings':
347 $jetpack_relatedposts_options = Jetpack_Options::get_option( 'relatedposts', array() );
348 // If the option's enabled key is NOT SET, it is considered enabled by the plugin.
349 if ( ! isset( $jetpack_relatedposts_options['enabled'] ) ) {
350 $jetpack_relatedposts_options['enabled'] = true;
351 }
352
353 $jetpack_relatedposts_options['enabled'] =
354 $jetpack_relatedposts_options['enabled']
355 && $site->is_module_active( 'related-posts' );
356
357 $jetpack_search_supported = false;
358 if ( function_exists( 'wpcom_is_jetpack_search_supported' ) ) {
359 $jetpack_search_supported = wpcom_is_jetpack_search_supported( $blog_id );
360 }
361
362 $jetpack_search_active =
363 $jetpack_search_supported
364 && $site->is_module_active( 'search' );
365
366 // array_values() is necessary to ensure the array starts at index 0.
367 $post_categories = array_values(
368 array_map(
369 array( $this, 'get_category_details' ),
370 get_categories( array( 'hide_empty' => false ) )
371 )
372 );
373
374 $newsletter_categories = maybe_unserialize( get_option( 'wpcom_newsletter_categories', array() ) );
375 $newsletter_category_ids = array_map(
376 function ( $newsletter_category ) {
377 return $newsletter_category['term_id'];
378 },
379 $newsletter_categories
380 );
381
382 $api_cache = $site->is_jetpack() ? (bool) get_option( 'jetpack_api_cache_enabled' ) : true;
383
384 $response[ $key ] = array(
385 // also exists as "options".
386 'admin_url' => get_admin_url(),
387 'default_ping_status' => 'closed' !== get_option( 'default_ping_status' ),
388 'default_comment_status' => 'closed' !== get_option( 'default_comment_status' ),
389
390 // new stuff starts here.
391 'instant_search_enabled' => (bool) get_option( 'instant_search_enabled' ),
392 'blog_public' => (int) get_option( 'blog_public' ),
393 'wpcom_data_sharing_opt_out' => (bool) get_option( 'wpcom_data_sharing_opt_out' ),
394 'jetpack_sync_non_public_post_stati' => (bool) Jetpack_Options::get_option( 'sync_non_public_post_stati' ),
395 'jetpack_relatedposts_allowed' => (bool) $this->jetpack_relatedposts_supported(),
396 'jetpack_relatedposts_enabled' => (bool) $jetpack_relatedposts_options['enabled'],
397 'jetpack_relatedposts_show_context' => ! empty( $jetpack_relatedposts_options['show_context'] ),
398 'jetpack_relatedposts_show_date' => ! empty( $jetpack_relatedposts_options['show_date'] ),
399 'jetpack_relatedposts_show_headline' => ! empty( $jetpack_relatedposts_options['show_headline'] ),
400 'jetpack_relatedposts_show_thumbnails' => ! empty( $jetpack_relatedposts_options['show_thumbnails'] ),
401 'jetpack_search_enabled' => (bool) $jetpack_search_active,
402 'jetpack_search_supported' => (bool) $jetpack_search_supported,
403 'default_category' => (int) get_option( 'default_category' ),
404 'post_categories' => (array) $post_categories,
405 'default_post_format' => get_option( 'default_post_format' ),
406 'default_pingback_flag' => (bool) get_option( 'default_pingback_flag' ),
407 'require_name_email' => (bool) get_option( 'require_name_email' ),
408 'comment_registration' => (bool) get_option( 'comment_registration' ),
409 'close_comments_for_old_posts' => (bool) get_option( 'close_comments_for_old_posts' ),
410 'close_comments_days_old' => (int) get_option( 'close_comments_days_old' ),
411 'thread_comments' => (bool) get_option( 'thread_comments' ),
412 'thread_comments_depth' => (int) get_option( 'thread_comments_depth' ),
413 'page_comments' => (bool) get_option( 'page_comments' ),
414 'comments_per_page' => (int) get_option( 'comments_per_page' ),
415 'default_comments_page' => get_option( 'default_comments_page' ),
416 'comment_order' => get_option( 'comment_order' ),
417 'comments_notify' => (bool) get_option( 'comments_notify' ),
418 'moderation_notify' => (bool) get_option( 'moderation_notify' ),
419 'social_notifications_like' => ( 'on' === get_option( 'social_notifications_like' ) ),
420 'social_notifications_reblog' => ( 'on' === get_option( 'social_notifications_reblog' ) ),
421 'social_notifications_subscribe' => ( 'on' === get_option( 'social_notifications_subscribe' ) ),
422 'comment_moderation' => (bool) get_option( 'comment_moderation' ),
423 'comment_whitelist' => (bool) get_option( 'comment_previously_approved' ),
424 'comment_previously_approved' => (bool) get_option( 'comment_previously_approved' ),
425 'comment_max_links' => (int) get_option( 'comment_max_links' ),
426 'moderation_keys' => get_option( 'moderation_keys' ),
427 'blacklist_keys' => get_option( 'disallowed_keys' ),
428 'disallowed_keys' => get_option( 'disallowed_keys' ),
429 'lang_id' => defined( 'IS_WPCOM' ) && IS_WPCOM
430 ? get_lang_id_by_code( wpcom_l10n_get_blog_locale_variant( $blog_id, true ) )
431 : get_option( 'lang_id' ),
432 'site_vertical_id' => (string) get_option( 'site_vertical_id' ),
433 'jetpack_cloudflare_analytics' => get_option( 'jetpack_cloudflare_analytics' ),
434 'disabled_likes' => (bool) get_option( 'disabled_likes' ),
435 'disabled_reblogs' => (bool) get_option( 'disabled_reblogs' ),
436 'jetpack_comment_likes_enabled' => (bool) get_option( 'jetpack_comment_likes_enabled', false ),
437 'twitter_via' => (string) get_option( 'twitter_via' ),
438 'jetpack-twitter-cards-site-tag' => (string) get_option( 'jetpack-twitter-cards-site-tag' ),
439 'eventbrite_api_token' => $this->get_cast_option_value_or_null( 'eventbrite_api_token', 'intval' ),
440 'gmt_offset' => get_option( 'gmt_offset' ),
441 'timezone_string' => get_option( 'timezone_string' ),
442 'date_format' => get_option( 'date_format' ),
443 'time_format' => get_option( 'time_format' ),
444 'start_of_week' => get_option( 'start_of_week' ),
445 'woocommerce_onboarding_profile' => (array) get_option( 'woocommerce_onboarding_profile', array() ),
446 'woocommerce_store_address' => (string) get_option( 'woocommerce_store_address' ),
447 'woocommerce_store_address_2' => (string) get_option( 'woocommerce_store_address_2' ),
448 'woocommerce_store_city' => (string) get_option( 'woocommerce_store_city' ),
449 'woocommerce_default_country' => (string) get_option( 'woocommerce_default_country' ),
450 'woocommerce_store_postcode' => (string) get_option( 'woocommerce_store_postcode' ),
451 'jetpack_testimonial' => (bool) get_option( 'jetpack_testimonial', '0' ),
452 'jetpack_testimonial_posts_per_page' => (int) get_option( 'jetpack_testimonial_posts_per_page', '10' ),
453 'jetpack_portfolio' => (bool) get_option( 'jetpack_portfolio', '0' ),
454 'jetpack_portfolio_posts_per_page' => (int) get_option( 'jetpack_portfolio_posts_per_page', '10' ),
455 'markdown_supported' => true,
456 'site_icon' => $this->get_cast_option_value_or_null( 'site_icon', 'intval' ),
457 Jetpack_SEO_Utils::FRONT_PAGE_META_OPTION => get_option( Jetpack_SEO_Utils::FRONT_PAGE_META_OPTION, '' ),
458 Jetpack_SEO_Titles::TITLE_FORMATS_OPTION => get_option( Jetpack_SEO_Titles::TITLE_FORMATS_OPTION, array() ),
459 'api_cache' => $api_cache,
460 'posts_per_page' => (int) get_option( 'posts_per_page' ),
461 'posts_per_rss' => (int) get_option( 'posts_per_rss' ),
462 'rss_use_excerpt' => (bool) get_option( 'rss_use_excerpt' ),
463 'launchpad_screen' => (string) get_option( 'launchpad_screen' ),
464 'wpcom_featured_image_in_email' => (bool) get_option( 'wpcom_featured_image_in_email' ),
465 'jetpack_gravatar_in_email' => (bool) get_option( 'jetpack_gravatar_in_email', true ),
466 'jetpack_author_in_email' => (bool) get_option( 'jetpack_author_in_email', true ),
467 'jetpack_post_date_in_email' => (bool) get_option( 'jetpack_post_date_in_email', true ),
468 'wpcom_newsletter_categories' => $newsletter_category_ids,
469 'wpcom_newsletter_categories_enabled' => (bool) get_option( 'wpcom_newsletter_categories_enabled' ),
470 'sm_enabled' => (bool) get_option( 'sm_enabled' ),
471 'jetpack_subscribe_overlay_enabled' => (bool) get_option( 'jetpack_subscribe_overlay_enabled' ),
472 'jetpack_subscriptions_subscribe_post_end_enabled' => (bool) get_option( 'jetpack_subscriptions_subscribe_post_end_enabled' ),
473 'jetpack_subscriptions_login_navigation_enabled' => (bool) get_option( 'jetpack_subscriptions_login_navigation_enabled' ),
474 'jetpack_subscriptions_subscribe_navigation_enabled' => (bool) get_option( 'jetpack_subscriptions_subscribe_navigation_enabled' ),
475 'wpcom_gifting_subscription' => (bool) get_option( 'wpcom_gifting_subscription', $this->get_wpcom_gifting_subscription_default() ),
476 'wpcom_reader_views_enabled' => (bool) get_option( 'wpcom_reader_views_enabled', true ),
477 'wpcom_subscription_emails_use_excerpt' => $this->get_wpcom_subscription_emails_use_excerpt_option(),
478 'jetpack_subscriptions_reply_to' => (string) $this->get_subscriptions_reply_to_option(),
479 'jetpack_subscriptions_from_name' => (string) get_option( 'jetpack_subscriptions_from_name' ),
480 'show_on_front' => (string) get_option( 'show_on_front' ),
481 'page_on_front' => (string) get_option( 'page_on_front' ),
482 'page_for_posts' => (string) get_option( 'page_for_posts' ),
483 'subscription_options' => (array) get_option( 'subscription_options' ),
484 'jetpack_verbum_subscription_modal' => (bool) get_option( 'jetpack_verbum_subscription_modal', true ),
485 'enable_verbum_commenting' => (bool) get_option( 'enable_verbum_commenting', true ),
486 'enable_blocks_comments' => (bool) get_option( 'enable_blocks_comments', true ),
487 'highlander_comment_form_prompt' => $this->get_highlander_comment_form_prompt_option(),
488 'jetpack_comment_form_color_scheme' => (string) get_option( 'jetpack_comment_form_color_scheme' ),
489 'in_site_migration_flow' => (string) get_option( 'in_site_migration_flow', '' ),
490 'migration_source_site_domain' => (string) get_option( 'migration_source_site_domain' ),
491 'jetpack_waf_automatic_rules' => (bool) get_option( 'jetpack_waf_automatic_rules' ),
492 'jetpack_waf_ip_allow_list' => (string) get_option( 'jetpack_waf_ip_allow_list' ),
493 'jetpack_waf_ip_allow_list_enabled' => (bool) get_option( 'jetpack_waf_ip_allow_list_enabled' ),
494 'jetpack_waf_ip_block_list' => (string) get_option( 'jetpack_waf_ip_block_list' ),
495 'jetpack_waf_ip_block_list_enabled' => (bool) get_option( 'jetpack_waf_ip_block_list_enabled' ),
496 'jetpack_waf_share_data' => (bool) get_option( 'jetpack_waf_share_data' ),
497 'jetpack_waf_share_debug_data' => (bool) get_option( 'jetpack_waf_share_debug_data' ),
498 'jetpack_waf_automatic_rules_last_updated_timestamp' => (int) get_option( 'jetpack_waf_automatic_rules_last_updated_timestamp' ),
499 'is_fully_managed_agency_site' => (bool) get_option( 'is_fully_managed_agency_site' ),
500 );
501
502 if ( defined( 'IS_WPCOM' ) && IS_WPCOM ) {
503 $response[ $key ]['wpcom_publish_posts_with_markdown'] = (bool) WPCom_Markdown::get_instance()->is_posting_enabled();
504 $response[ $key ]['wpcom_publish_comments_with_markdown'] = (bool) WPCom_Markdown::get_instance()->is_commenting_enabled();
505
506 // WPCOM-specific Infinite Scroll Settings.
507 if ( is_callable( array( 'The_Neverending_Home_Page', 'get_settings' ) ) ) {
508 /**
509 * Clear the cached copy of widget info so it's pulled fresh from blog options.
510 * It was primed during the initial load under the __REST API site__'s context.
511 *
512 * @see wp_get_sidebars_widgets https://core.trac.wordpress.org/browser/trunk/src/wp-includes/widgets.php?rev=42374#L931
513 */
514 $GLOBALS['_wp_sidebars_widgets'] = array(); // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
515
516 $infinite_scroll_settings = The_Neverending_Home_Page::get_settings();
517 $response[ $key ]['infinite_scroll'] = get_option( 'infinite_scroll', true ) && 'scroll' === $infinite_scroll_settings->type;
518 if ( $infinite_scroll_settings->footer_widgets || 'click' === $infinite_scroll_settings->requested_type ) {
519 // The blog has footer widgets -- infinite scroll is blocked.
520 $response[ $key ]['infinite_scroll_blocked'] = 'footer';
521 } else {
522 $response[ $key ]['infinite_scroll_blocked'] = false;
523 }
524 }
525 }
526
527 // allow future versions of this endpoint to support additional settings keys.
528 /**
529 * Filter the current site setting in the returned response.
530 *
531 * @module json-api
532 *
533 * @since 3.9.3
534 * @since 13.6 Added the API object parameter.
535 *
536 * @param mixed $response_item A single site setting.
537 * @param WPCOM_JSON_API_Site_Settings_Endpoint $this The API object.
538 */
539 $response[ $key ] = apply_filters( 'site_settings_endpoint_get', $response[ $key ], $this );
540
541 if ( class_exists( 'Sharing_Service' ) ) {
542 $ss = new Sharing_Service();
543 $sharing = $ss->get_global_options();
544 $response[ $key ]['sharing_button_style'] = (string) $sharing['button_style'];
545 $response[ $key ]['sharing_label'] = (string) $sharing['sharing_label'];
546 $response[ $key ]['sharing_show'] = (array) $sharing['show'];
547 $response[ $key ]['sharing_open_links'] = (string) $sharing['open_links'];
548 }
549
550 $response[ $key ]['jetpack_protect_whitelist'] = Brute_Force_Protection_Shared_Functions::format_allow_list();
551
552 if ( ! current_user_can( 'edit_posts' ) ) {
553 unset( $response[ $key ] );
554 }
555 break;
556 }
557 }
558 return $response;
559 }
560
561 /**
562 * Get the default value for the wpcom_gifting_subscription option.
563 * The default value is the inverse of the plan's auto_renew setting.
564 *
565 * @return bool
566 */
567 protected function get_wpcom_gifting_subscription_default() {
568 if ( function_exists( 'wpcom_get_site_purchases' ) && function_exists( 'wpcom_purchase_has_feature' ) ) {
569 $purchases = wpcom_get_site_purchases();
570
571 foreach ( $purchases as $purchase ) {
572 if ( wpcom_purchase_has_feature( $purchase, \WPCOM_Features::SUBSCRIPTION_GIFTING ) ) {
573 /*
574 * We set default value as false when expiration date not match the following:
575 * - 54 days before the annual plan expiration.
576 * - 5 days before the monthly plan expiration.
577 * This is to match the gifting banner logic.
578 */
579 $days_of_warning = str_contains( $purchase->product_slug, 'monthly' ) ? 5 : 54;
580 $seconds_until_expiration = strtotime( $purchase->expiry_date ) - time();
581 if ( $seconds_until_expiration >= $days_of_warning * DAY_IN_SECONDS ) {
582 return false;
583 }
584
585 // We set default to the inverse of auto-renew.
586 if ( isset( $purchase->auto_renew ) ) {
587 return ! $purchase->auto_renew;
588 } elseif ( isset( $purchase->user_allows_auto_renew ) ) {
589 return ! $purchase->user_allows_auto_renew;
590 }
591 }
592 }
593 }
594 return false;
595 }
596
597 /**
598 * Get locale.
599 *
600 * @param string $key Language.
601 */
602 protected function get_locale( $key ) {
603 if ( 'lang' === $key ) {
604 if ( defined( 'IS_WPCOM' ) && IS_WPCOM ) {
605 return (string) get_blog_lang_code();
606 } else {
607 return get_locale();
608 }
609 }
610
611 return false;
612 }
613
614 /**
615 * Updates site settings for authorized users
616 *
617 * @return array|WP_Error
618 */
619 public function update_settings() {
620 /*
621 * $this->input() retrieves posted arguments whitelisted and casted to the $request_format
622 * specs that get passed in when this class is instantiated
623 */
624 $input = $this->input();
625 $unfiltered_input = $this->input( false, false );
626 /**
627 * Filters the settings to be updated on the site.
628 *
629 * @module json-api
630 *
631 * @since 3.6.0
632 * @since 6.1.1 Added $unfiltered_input parameter.
633 *
634 * @param array $input Associative array of site settings to be updated.
635 * Cast and filtered based on documentation.
636 * @param array $unfiltered_input Associative array of site settings to be updated.
637 * Neither cast nor filtered. Contains raw input.
638 */
639 $input = apply_filters( 'rest_api_update_site_settings', $input, $unfiltered_input );
640
641 $blog_id = get_current_blog_id();
642
643 $jetpack_relatedposts_options = array();
644 $sharing_options = array();
645 $updated = array();
646
647 foreach ( $input as $key => $value ) {
648
649 if ( ! is_array( $value ) ) {
650 $value = trim( $value );
651 }
652
653 // preserve the raw value before unslashing the value. The slashes need to be preserved for date and time formats.
654 $raw_value = $value;
655 $value = wp_unslash( $value );
656
657 switch ( $key ) {
658
659 case 'default_ping_status':
660 case 'default_comment_status':
661 // settings are stored as closed|open.
662 $coerce_value = ( $value ) ? 'open' : 'closed';
663 if ( update_option( $key, $coerce_value ) ) {
664 $updated[ $key ] = $value;
665 }
666 break;
667 case 'launchpad_screen':
668 if ( in_array( $value, array( 'full', 'off', 'minimized' ), true ) ) {
669 if ( update_option( $key, $value ) ) {
670 $updated[ $key ] = $value;
671 }
672 }
673 break;
674 case 'jetpack_protect_whitelist':
675 if ( class_exists( 'Brute_Force_Protection_Shared_Functions' ) ) {
676 $result = Brute_Force_Protection_Shared_Functions::save_allow_list( $value );
677 if ( is_wp_error( $result ) ) {
678 return $result;
679 }
680 $updated[ $key ] = Brute_Force_Protection_Shared_Functions::format_allow_list();
681 }
682 break;
683 case 'jetpack_sync_non_public_post_stati':
684 Jetpack_Options::update_option( 'sync_non_public_post_stati', $value );
685 break;
686 case 'jetpack_search_enabled':
687 if ( $value ) {
688 Jetpack::activate_module( $blog_id, 'search' );
689 } else {
690 Jetpack::deactivate_module( $blog_id, 'search' );
691 }
692 $updated[ $key ] = (bool) $value;
693 break;
694 case 'jetpack_relatedposts_enabled':
695 case 'jetpack_relatedposts_show_context':
696 case 'jetpack_relatedposts_show_date':
697 case 'jetpack_relatedposts_show_thumbnails':
698 case 'jetpack_relatedposts_show_headline':
699 if ( ! $this->jetpack_relatedposts_supported() ) {
700 break;
701 }
702 if ( 'jetpack_relatedposts_enabled' === $key ) {
703 if ( $value ) {
704 Jetpack::activate_module( $blog_id, 'related-posts' );
705 } else {
706 Jetpack::deactivate_module( $blog_id, 'related-posts' );
707 }
708 }
709 $just_the_key = substr( $key, 21 );
710 $jetpack_relatedposts_options[ $just_the_key ] = $value;
711 break;
712
713 case 'social_notifications_like':
714 case 'social_notifications_reblog':
715 case 'social_notifications_subscribe':
716 // settings are stored as on|off.
717 $coerce_value = ( $value ) ? 'on' : 'off';
718 if ( update_option( $key, $coerce_value ) ) {
719 $updated[ $key ] = $value;
720 }
721 break;
722
723 case 'cloudflare_analytics':
724 if ( ! isset( $value['code'] ) || ! preg_match( '/^$|^[a-fA-F0-9]+$/i', $value['code'] ) ) {
725 return new WP_Error( 'invalid_code', __( 'Invalid Cloudflare Analytics ID', 'jetpack' ) );
726 }
727
728 if ( update_option( $key, $value ) ) {
729 $updated[ $key ] = $value;
730 }
731 break;
732
733 case 'jetpack_testimonial':
734 case 'jetpack_portfolio':
735 case 'jetpack_comment_likes_enabled':
736 case 'wpcom_reader_views_enabled':
737 case 'jetpack_verbum_subscription_modal':
738 // settings are stored as 1|0.
739 $coerce_value = (int) $value;
740 if ( update_option( $key, $coerce_value ) ) {
741 $updated[ $key ] = (bool) $value;
742 }
743 break;
744
745 case 'jetpack_testimonial_posts_per_page':
746 case 'jetpack_portfolio_posts_per_page':
747 // settings are stored as numeric.
748 $coerce_value = (int) $value;
749 if ( update_option( $key, $coerce_value ) ) {
750 $updated[ $key ] = $coerce_value;
751 }
752 break;
753
754 // Sharing options.
755 case 'sharing_button_style':
756 case 'sharing_show':
757 case 'sharing_open_links':
758 $sharing_options[ preg_replace( '/^sharing_/', '', $key ) ] = $value;
759 break;
760 case 'sharing_label':
761 $sharing_options[ $key ] = $value;
762 break;
763
764 // Keyring token option.
765 case 'eventbrite_api_token':
766 // These options can only be updated for sites hosted on WordPress.com.
767 if ( defined( 'IS_WPCOM' ) && IS_WPCOM ) {
768 if ( empty( $value ) || WPCOM_JSON_API::is_falsy( $value ) ) {
769 if ( delete_option( $key ) ) {
770 $updated[ $key ] = null;
771 }
772 } elseif ( update_option( $key, $value ) ) {
773 $updated[ $key ] = (int) $value;
774 }
775 }
776 break;
777
778 case 'api_cache':
779 if ( empty( $value ) || WPCOM_JSON_API::is_falsy( $value ) ) {
780 if ( delete_option( 'jetpack_api_cache_enabled' ) ) {
781 $updated[ $key ] = false;
782 }
783 } elseif ( update_option( 'jetpack_api_cache_enabled', true ) ) {
784 $updated[ $key ] = true;
785 }
786 break;
787
788 case 'timezone_string':
789 /*
790 * Map UTC+- timezones to gmt_offsets and set timezone_string to empty
791 * https://github.com/WordPress/WordPress/blob/4.4.2/wp-admin/options.php#L175
792 */
793 if ( ! empty( $value ) && preg_match( '/^UTC[+-]/', $value ) ) {
794 $gmt_offset = preg_replace( '/UTC\+?/', '', $value );
795 if ( update_option( 'gmt_offset', $gmt_offset ) ) {
796 $updated['gmt_offset'] = $gmt_offset;
797 }
798
799 $value = '';
800 }
801
802 /*
803 * Always set timezone_string either with the given value or with an
804 * empty string
805 */
806 if ( update_option( $key, $value ) ) {
807 $updated[ $key ] = $value;
808 }
809 break;
810
811 case 'subscription_options':
812 if ( ! is_array( $value ) ) {
813 break;
814 }
815
816 $allowed_keys = array( 'invitation', 'comment_follow', 'welcome' );
817 $filtered_value = array_filter(
818 $value,
819 function ( $key ) use ( $allowed_keys ) {
820 return in_array( $key, $allowed_keys, true );
821 },
822 ARRAY_FILTER_USE_KEY
823 );
824
825 if ( empty( $filtered_value ) ) {
826 break;
827 }
828
829 array_walk_recursive(
830 $filtered_value,
831 function ( &$value ) {
832 $value = wp_kses(
833 $value,
834 array(
835 'a' => array(
836 'href' => array(),
837 ),
838 )
839 );
840 }
841 );
842
843 $old_subscription_options = get_option( 'subscription_options' );
844 $new_subscription_options = array_merge( $old_subscription_options, $filtered_value );
845
846 if ( update_option( $key, $new_subscription_options ) ) {
847 $updated[ $key ] = $filtered_value;
848 }
849 break;
850
851 case 'woocommerce_onboarding_profile':
852 // Allow boolean values but sanitize_text_field everything else.
853 $sanitized_value = (array) $value;
854 array_walk_recursive(
855 $sanitized_value,
856 function ( &$value ) {
857 if ( ! is_bool( $value ) ) {
858 $value = sanitize_text_field( $value );
859 }
860 }
861 );
862 if ( update_option( $key, $sanitized_value ) ) {
863 $updated[ $key ] = $sanitized_value;
864 }
865 break;
866
867 case 'woocommerce_store_address':
868 case 'woocommerce_store_address_2':
869 case 'woocommerce_store_city':
870 case 'woocommerce_default_country':
871 case 'woocommerce_store_postcode':
872 $sanitized_value = sanitize_text_field( $value );
873 if ( update_option( $key, $sanitized_value ) ) {
874 $updated[ $key ] = $sanitized_value;
875 }
876 break;
877
878 case 'date_format':
879 case 'time_format':
880 // settings are stored as strings.
881 // raw_value is used to help preserve any escaped characters that might exist in the formatted string.
882 $sanitized_value = sanitize_text_field( $raw_value );
883 if ( update_option( $key, $sanitized_value ) ) {
884 $updated[ $key ] = $sanitized_value;
885 }
886 break;
887
888 case 'start_of_week':
889 // setting is stored as int in 0-6 range (days of week).
890 $coerce_value = (int) $value;
891 $limit_value = ( $coerce_value >= 0 && $coerce_value <= 6 ) ? $coerce_value : 0;
892 if ( update_option( $key, $limit_value ) ) {
893 $updated[ $key ] = $limit_value;
894 }
895 break;
896
897 case 'site_icon':
898 /*
899 * settings are stored as deletable numeric (all empty
900 * values as delete intent), validated as media image
901 */
902 if ( empty( $value ) || WPCOM_JSON_API::is_falsy( $value ) ) {
903 /**
904 * Fallback mechanism to clear a third party site icon setting. Can be used
905 * to unset the option when an API request instructs the site to remove the site icon.
906 *
907 * @module json-api
908 *
909 * @since 4.10
910 */
911 if ( delete_option( $key ) || apply_filters( 'rest_api_site_icon_cleared', false ) ) {
912 $updated[ $key ] = null;
913 }
914 } elseif ( is_numeric( $value ) ) {
915 $coerce_value = (int) $value;
916 if ( wp_attachment_is_image( $coerce_value ) && update_option( $key, $coerce_value ) ) {
917 $updated[ $key ] = $coerce_value;
918 }
919 }
920 break;
921
922 case Jetpack_SEO_Utils::FRONT_PAGE_META_OPTION:
923 if ( ! Jetpack_SEO_Utils::is_enabled_jetpack_seo() && ! Jetpack_SEO_Utils::has_legacy_front_page_meta() ) {
924 return new WP_Error( 'unauthorized', __( 'SEO tools are not enabled for this site.', 'jetpack' ), 403 );
925 }
926
927 if ( ! is_string( $value ) ) {
928 return new WP_Error( 'invalid_input', __( 'Invalid SEO meta description value.', 'jetpack' ), 400 );
929 }
930
931 $new_description = Jetpack_SEO_Utils::update_front_page_meta_description( $value );
932
933 if ( ! empty( $new_description ) ) {
934 $updated[ $key ] = $new_description;
935 }
936 break;
937
938 case Jetpack_SEO_Titles::TITLE_FORMATS_OPTION:
939 if ( ! Jetpack_SEO_Utils::is_enabled_jetpack_seo() ) {
940 if ( Jetpack_SEO_Utils::has_legacy_front_page_meta() ) {
941 break;
942 }
943 return new WP_Error( 'unauthorized', __( 'SEO tools are not enabled for this site.', 'jetpack' ), 403 );
944 }
945
946 if ( ! Jetpack_SEO_Titles::are_valid_title_formats( $value ) ) {
947 return new WP_Error( 'invalid_input', __( 'Invalid SEO title format.', 'jetpack' ), 400 );
948 }
949
950 $new_title_formats = Jetpack_SEO_Titles::update_title_formats( $value );
951
952 if ( ! empty( $new_title_formats ) ) {
953 $updated[ $key ] = $new_title_formats;
954 }
955 break;
956
957 case 'verification_services_codes':
958 $verification_codes = jetpack_verification_validate( $value );
959
960 if ( update_option( 'verification_services_codes', $verification_codes ) ) {
961 $updated[ $key ] = $verification_codes;
962 }
963 break;
964
965 case 'wpcom_publish_posts_with_markdown':
966 case 'wpcom_publish_comments_with_markdown':
967 $coerce_value = (bool) $value;
968 if ( update_option( $key, $coerce_value ) ) {
969 $updated[ $key ] = $coerce_value;
970 }
971 break;
972
973 case 'wpcom_gifting_subscription':
974 $coerce_value = (bool) $value;
975
976 /*
977 * get_option returns a boolean false if the option doesn't exist, otherwise it always returns
978 * a serialized value. Knowing that we can check if the option already exists.
979 */
980 $gift_toggle = get_option( $key );
981 if ( false === $gift_toggle ) {
982 // update_option will not create a new option if the initial value is false. So use add_option.
983 if ( add_option( $key, $coerce_value ) ) {
984 $updated[ $key ] = $coerce_value;
985 }
986 } elseif ( update_option( $key, $coerce_value ) ) { // If the option already exists use update_option.
987 $updated[ $key ] = $coerce_value;
988 }
989 break;
990
991 case 'rss_use_excerpt':
992 $sanitized_value = (int) (bool) $value;
993 update_option( $key, $sanitized_value );
994 $updated[ $key ] = $sanitized_value;
995 break;
996
997 case 'wpcom_subscription_emails_use_excerpt':
998 update_option( 'wpcom_subscription_emails_use_excerpt', (bool) $value );
999 $updated[ $key ] = (bool) $value;
1000 break;
1001
1002 case 'jetpack_subscriptions_reply_to':
1003 require_once JETPACK__PLUGIN_DIR . 'modules/subscriptions/class-settings.php';
1004 $to_set_value = Automattic\Jetpack\Modules\Subscriptions\Settings::is_valid_reply_to( $value )
1005 ? (string) $value
1006 : Automattic\Jetpack\Modules\Subscriptions\Settings::get_default_reply_to();
1007
1008 if ( update_option( $key, $to_set_value ) ) {
1009 $updated[ $key ] = $to_set_value;
1010 }
1011 break;
1012
1013 case 'jetpack_subscriptions_from_name':
1014 $sanitized_value = sanitize_text_field( $value );
1015 if ( update_option( $key, $sanitized_value ) ) {
1016 $updated[ $key ] = $sanitized_value;
1017 }
1018 break;
1019
1020 case 'instant_search_enabled':
1021 update_option( 'instant_search_enabled', (bool) $value );
1022 $updated[ $key ] = (bool) $value;
1023 break;
1024
1025 case 'lang_id':
1026 /*
1027 * Due to the fact that locale variants are set in a locale_variant option,
1028 * changing locale from variant to primary
1029 * would look like the same lang_id is being saved and update_option would return false,
1030 * even though the correct options would be set by pre_update_option_lang_id,
1031 * so we should always return lang_id as updated.
1032 */
1033 update_option( 'lang_id', (int) $value );
1034 $updated[ $key ] = (int) $value;
1035 break;
1036
1037 case 'wpcom_featured_image_in_email':
1038 update_option( 'wpcom_featured_image_in_email', (int) (bool) $value );
1039 $updated[ $key ] = (int) (bool) $value;
1040 break;
1041
1042 case 'wpcom_newsletter_categories':
1043 $sanitized_category_ids = (array) $value;
1044
1045 array_walk_recursive(
1046 $sanitized_category_ids,
1047 function ( &$value ) {
1048 if ( is_int( $value ) && $value > 0 ) {
1049 return;
1050 }
1051
1052 $value = (int) $value;
1053 if ( $value <= 0 ) {
1054 $value = null;
1055 }
1056 }
1057 );
1058
1059 $sanitized_category_ids = array_unique(
1060 array_filter(
1061 $sanitized_category_ids,
1062 function ( $category_id ) {
1063 return $category_id !== null;
1064 }
1065 )
1066 );
1067
1068 $new_value = array_map(
1069 function ( $category_id ) {
1070 return array( 'term_id' => $category_id );
1071 },
1072 $sanitized_category_ids
1073 );
1074
1075 if ( update_option( $key, $new_value ) ) {
1076 $updated[ $key ] = $sanitized_category_ids;
1077 }
1078 break;
1079
1080 case 'wpcom_newsletter_categories_enabled':
1081 update_option( 'wpcom_newsletter_categories_enabled', (int) (bool) $value );
1082 $updated[ $key ] = (int) (bool) $value;
1083 break;
1084
1085 case 'sm_enabled':
1086 update_option( 'sm_enabled', (int) (bool) $value );
1087 $updated[ $key ] = (int) (bool) $value;
1088 break;
1089
1090 case 'jetpack_subscribe_overlay_enabled':
1091 update_option( 'jetpack_subscribe_overlay_enabled', (int) (bool) $value );
1092 $updated[ $key ] = (int) (bool) $value;
1093 break;
1094
1095 case 'jetpack_subscriptions_subscribe_post_end_enabled':
1096 update_option( 'jetpack_subscriptions_subscribe_post_end_enabled', (int) (bool) $value );
1097 $updated[ $key ] = (int) (bool) $value;
1098 break;
1099
1100 case 'jetpack_subscriptions_login_navigation_enabled':
1101 update_option( 'jetpack_subscriptions_login_navigation_enabled', (int) (bool) $value );
1102 $updated[ $key ] = (int) (bool) $value;
1103 break;
1104
1105 case 'jetpack_subscriptions_subscribe_navigation_enabled':
1106 update_option( 'jetpack_subscriptions_subscribe_navigation_enabled', (int) (bool) $value );
1107 $updated[ $key ] = (int) (bool) $value;
1108 break;
1109
1110 case 'show_on_front':
1111 if ( in_array( $value, array( 'page', 'posts' ), true ) && update_option( $key, $value ) ) {
1112 $updated[ $key ] = $value;
1113 }
1114 break;
1115
1116 case 'page_on_front':
1117 case 'page_for_posts':
1118 if ( $value === '' ) { // empty function is not applicable here because '0' may be a valid page id
1119 if ( delete_option( $key ) ) {
1120 $updated[ $key ] = null;
1121 }
1122
1123 break;
1124 }
1125
1126 if ( ! $this->is_valid_page_id( $value ) ) {
1127 break;
1128 }
1129
1130 $related_option_key = $key === 'page_on_front' ? 'page_for_posts' : 'page_on_front';
1131 $related_option_value = get_option( $related_option_key );
1132 if ( $related_option_value === $value ) {
1133 // page_on_front and page_for_posts are not allowed to be the same
1134 break;
1135 }
1136
1137 if ( update_option( $key, $value ) ) {
1138 $updated[ $key ] = $value;
1139 }
1140
1141 break;
1142
1143 case 'in_site_migration_flow':
1144 if ( empty( $value ) ) {
1145 delete_option( 'in_site_migration_flow' );
1146 break;
1147 }
1148
1149 $migration_flow_whitelist = array(
1150 'site-migration',
1151 'migration-signup',
1152 );
1153
1154 if ( ! in_array( $value, $migration_flow_whitelist, true ) ) {
1155 break;
1156 }
1157
1158 update_option( 'in_site_migration_flow', $value );
1159 $updated[ $key ] = $value;
1160 break;
1161
1162 case 'migration_source_site_domain':
1163 // If we get an empty value, delete the option
1164 if ( empty( $value ) ) {
1165 delete_option( 'migration_source_site_domain' );
1166 break;
1167 }
1168
1169 // If we get a non-url value, don't update the option.
1170 if ( wp_http_validate_url( $value ) === false ) {
1171 break;
1172 }
1173
1174 update_option( 'migration_source_site_domain', $value );
1175 $updated[ $key ] = $value;
1176 break;
1177
1178 case 'is_fully_managed_agency_site':
1179 $coerce_value = (int) (bool) $value;
1180 if ( update_option( $key, $coerce_value ) ) {
1181 $updated[ $key ] = (bool) $coerce_value;
1182 }
1183 break;
1184
1185 default:
1186 // allow future versions of this endpoint to support additional settings keys.
1187 if ( has_filter( 'site_settings_endpoint_update_' . $key ) ) {
1188 /**
1189 * Filter current site setting value to be updated.
1190 *
1191 * @module json-api
1192 *
1193 * @since 3.9.3
1194 * @since 13.6 Added the API object parameter.
1195 *
1196 * @param mixed $response_item A single site setting value.
1197 * @param WPCOM_JSON_API_Site_Settings_Endpoint The API object parameter.
1198 */
1199 $value = apply_filters( 'site_settings_endpoint_update_' . $key, $value, $this );
1200
1201 if ( is_wp_error( $value ) ) {
1202 return $value;
1203 }
1204
1205 if ( $value ) {
1206 $updated[ $key ] = $value;
1207 }
1208 break;
1209 }
1210 // no worries, we've already whitelisted and casted arguments above.
1211 if ( update_option( $key, $value ) ) {
1212 $updated[ $key ] = $value;
1213 }
1214 }
1215 }
1216
1217 if ( $jetpack_relatedposts_options !== array() ) {
1218 // track new jetpack_relatedposts options against old.
1219 $old_relatedposts_options = Jetpack_Options::get_option( 'relatedposts' );
1220
1221 $jetpack_relatedposts_options_to_save = $old_relatedposts_options;
1222 foreach ( $jetpack_relatedposts_options as $key => $value ) {
1223 $jetpack_relatedposts_options_to_save[ $key ] = $value;
1224 }
1225
1226 if ( Jetpack_Options::update_option( 'relatedposts', $jetpack_relatedposts_options_to_save ) ) {
1227 foreach ( $jetpack_relatedposts_options as $key => $value ) {
1228 if ( in_array( $key, array( 'show_context', 'show_date' ), true ) ) {
1229 $has_initialized_option = ! isset( $old_relatedposts_options[ $key ] ) && $value;
1230 $has_updated_option = isset( $old_relatedposts_options[ $key ] ) && $value !== $old_relatedposts_options[ $key ];
1231
1232 if ( $has_initialized_option || $has_updated_option ) {
1233 $updated[ 'jetpack_relatedposts_' . $key ] = (bool) $value;
1234 }
1235 } elseif ( isset( $old_relatedposts_options[ $key ] ) && $value !== $old_relatedposts_options[ $key ] ) {
1236 $updated[ 'jetpack_relatedposts_' . $key ] = $value;
1237 }
1238 }
1239 }
1240 }
1241
1242 if ( ! empty( $sharing_options ) && class_exists( 'Sharing_Service' ) ) {
1243 $ss = new Sharing_Service();
1244
1245 /*
1246 * Merge current values with updated, since Sharing_Service expects
1247 * all values to be included when updating
1248 */
1249 $current_sharing_options = $ss->get_global_options();
1250 foreach ( $current_sharing_options as $key => $val ) {
1251 if ( ! isset( $sharing_options[ $key ] ) ) {
1252 $sharing_options[ $key ] = $val;
1253 }
1254 }
1255
1256 $updated_social_options = $ss->set_global_options( $sharing_options );
1257
1258 if ( isset( $input['sharing_button_style'] ) ) {
1259 $updated['sharing_button_style'] = (string) $updated_social_options['button_style'];
1260 }
1261 if ( isset( $input['sharing_label'] ) ) {
1262 // Sharing_Service won't report label as updated if set to default.
1263 $updated['sharing_label'] = (string) $sharing_options['sharing_label'];
1264 }
1265 if ( isset( $input['sharing_show'] ) ) {
1266 $updated['sharing_show'] = (array) $updated_social_options['show'];
1267 }
1268 if ( isset( $input['sharing_open_links'] ) ) {
1269 $updated['sharing_open_links'] = (string) $updated_social_options['open_links'];
1270 }
1271 }
1272
1273 return array(
1274 'updated' => $updated,
1275 );
1276 }
1277
1278 /**
1279 * Get the value of the wpcom_subscription_emails_use_excerpt option.
1280 * When the option is not set, it will return the value of the rss_use_excerpt option.
1281 *
1282 * @return bool
1283 */
1284 protected function get_wpcom_subscription_emails_use_excerpt_option() {
1285 $wpcom_subscription_emails_use_excerpt = get_option( 'wpcom_subscription_emails_use_excerpt', null );
1286
1287 if ( $wpcom_subscription_emails_use_excerpt === null ) {
1288 $rss_use_excerpt = get_option( 'rss_use_excerpt', null );
1289 $wpcom_subscription_emails_use_excerpt = $rss_use_excerpt === null ? false : $rss_use_excerpt;
1290 }
1291
1292 return (bool) $wpcom_subscription_emails_use_excerpt;
1293 }
1294
1295 /**
1296 * Get the string value of the jetpack_subscriptions_reply_to option.
1297 * When the option is not set, it will retun 'no-reply'.
1298 *
1299 * @return string
1300 */
1301 protected function get_subscriptions_reply_to_option() {
1302 $reply_to = get_option( 'jetpack_subscriptions_reply_to', null );
1303 if ( $reply_to === null ) {
1304 require_once JETPACK__PLUGIN_DIR . 'modules/subscriptions/class-settings.php';
1305 return Automattic\Jetpack\Modules\Subscriptions\Settings::get_default_reply_to();
1306 }
1307 return $reply_to;
1308 }
1309
1310 /**
1311 * Check if the given value is a valid page ID for the current site.
1312 *
1313 * @param mixed $value The value to check.
1314 * @return bool True if the value is a valid page ID for the current site, false otherwise.
1315 */
1316 protected function is_valid_page_id( $value ) {
1317 $all_page_ids = get_all_page_ids();
1318
1319 $valid_page_id = false;
1320 foreach ( $all_page_ids as $page_id ) {
1321 if ( $page_id === (string) $value ) {
1322 $valid_page_id = true;
1323 break;
1324 }
1325 }
1326
1327 return $valid_page_id;
1328 }
1329
1330 /**
1331 * Get the value of the highlander_comment_form_prompt option.
1332 * When the option is not set, it will return the default value.
1333 *
1334 * @return string
1335 */
1336 protected function get_highlander_comment_form_prompt_option() {
1337 $highlander_comment_form_prompt_option = get_option( 'highlander_comment_form_prompt' );
1338
1339 if ( empty( $highlander_comment_form_prompt_option ) ) {
1340 return (string) __( 'Leave a comment', 'jetpack' );
1341 }
1342
1343 return (string) $highlander_comment_form_prompt_option;
1344 }
1345 }
1346