PluginProbe ʕ •ᴥ•ʔ
Jetpack – WP Security, Backup, Speed, & Growth / 16.2-a.1
Jetpack – WP Security, Backup, Speed, & Growth v16.2-a.1
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 / jetpack_vendor / automattic / jetpack-connection / legacy / class-jetpack-options.php
jetpack / jetpack_vendor / automattic / jetpack-connection / legacy Last commit date
class-jetpack-ixr-client.php 5 days ago class-jetpack-ixr-clientmulticall.php 9 months ago class-jetpack-options.php 5 days ago class-jetpack-signature.php 5 days ago class-jetpack-tracks-client.php 3 months ago class-jetpack-tracks-event.php 3 years ago class-jetpack-xmlrpc-server.php 3 months ago
class-jetpack-options.php
754 lines
1 <?php
2 /**
3 * Legacy Jetpack_Options class.
4 *
5 * @package automattic/jetpack-connection
6 */
7
8 use Automattic\Jetpack\Constants;
9
10 /**
11 * Class Jetpack_Options
12 */
13 class Jetpack_Options {
14
15 /**
16 * An array that maps a grouped option type to an option name.
17 *
18 * @var array
19 */
20 private static $grouped_options = array(
21 'compact' => 'jetpack_options',
22 'private' => 'jetpack_private_options',
23 );
24
25 /**
26 * Returns an array of option names for a given type.
27 *
28 * @param string $type The type of option to return. Defaults to 'compact'.
29 *
30 * @return array
31 */
32 public static function get_option_names( $type = 'compact' ) {
33 switch ( $type ) {
34 case 'non-compact':
35 case 'non_compact':
36 return array(
37 'activated',
38 'active_modules',
39 'active_modules_initialized', // (bool) used to determine that all the default modules were activated, so we know how to act on a reconnection.
40 'allowed_xsite_search_ids', // (array) Array of WP.com blog ids that are allowed to search the content of this site
41 'available_modules',
42 'do_activate',
43 'log',
44 'slideshow_background_color',
45 'widget_twitter',
46 'wpcc_options',
47 'relatedposts',
48 'file_data',
49 'autoupdate_plugins', // (array) An array of plugin ids ( eg. jetpack/jetpack ) that should be autoupdated
50 'autoupdate_plugins_translations', // (array) An array of plugin ids ( eg. jetpack/jetpack ) that should be autoupdated translation files.
51 'autoupdate_themes', // (array) An array of theme ids ( eg. twentyfourteen ) that should be autoupdated
52 'autoupdate_themes_translations', // (array) An array of theme ids ( eg. twentyfourteen ) that should autoupdated translation files.
53 'autoupdate_core', // (bool) Whether or not to autoupdate core
54 'autoupdate_translations', // (bool) Whether or not to autoupdate all translations
55 'json_api_full_management', // (bool) Allow full management (eg. Activate, Upgrade plugins) of the site via the JSON API.
56 'sync_non_public_post_stati', // (bool) Allow synchronisation of posts and pages with non-public status.
57 'site_icon_url', // (string) url to the full site icon
58 'site_icon_id', // (int) Attachment id of the site icon file
59 'dismissed_manage_banner', // (bool) Dismiss Jetpack manage banner allows the user to dismiss the banner permanently
60 'unique_connection', // (array) A flag to determine a unique connection to wordpress.com two values "connected" and "disconnected" with values for how many times each has occurred
61 'unique_registrations', // (integer) A counter of how many times the site was registered
62 'protect_whitelist', // (array) IP Address for the Protect module to ignore
63 'sync_error_idc', // (bool|array) false or array containing the site's home and siteurl at time of IDC error
64 'sync_health_status', // (bool|array) An array of data relating to Jetpack's sync health.
65 'safe_mode_confirmed', // (bool) True if someone confirms that this site was correctly put into safe mode automatically after an identity crisis is discovered.
66 'migrate_for_idc', // (bool) True if someone confirms that this site should migrate stats and subscribers from its previous URL
67 'ab_connect_banner_green_bar', // (int) Version displayed of the A/B test for the green bar at the top of the connect banner.
68 'tos_agreed', // (bool) Whether or not the TOS for connection has been agreed upon.
69 'static_asset_cdn_files', // (array) An nested array of files that we can swap out for cdn versions.
70 'mapbox_api_key', // (string) Mapbox API Key, for use with Map block.
71 'mailchimp', // (string) Mailchimp keyring data, for mailchimp block.
72 'xmlrpc_errors', // (array) Keys are XML-RPC signature error codes. Values are truthy.
73 'dismissed_wizard_banner', // (int) (DEPRECATED) True if the Wizard banner has been dismissed.
74 );
75
76 case 'private':
77 return array(
78 'blog_token', // (string) The Client Secret/Blog Token of this site.
79 'user_token', // (string) The User Token of this site. (deprecated)
80 'user_tokens', // (array) User Tokens for each user of this site who has connected to jetpack.wordpress.com.
81 'purchase_token', // (string) Token for logged out user purchases.
82 'token_lock', // (string) Token lock in format `expiration_date|||site_url`.
83 );
84
85 case 'network':
86 return array(
87 'file_data', // (array) List of absolute paths to all Jetpack modules
88 );
89 }
90
91 return array(
92 'id', // (int) The Client ID/WP.com Blog ID of this site.
93 'publicize_connections', // (array) An array of Publicize connections from WordPress.com.
94 'master_user', // (int) The local User ID of the user who connected this site to jetpack.wordpress.com.
95 'protected_owner', // (array) Anchor identifying the locked connection owner. WordPress.com is authoritative; this is a local cache. Keys: wpcom_user_id, email, local_user_id, locked, confirmed_at, confirmed_by.
96 'version', // (string) Used during upgrade procedure to auto-activate new modules. version:time.
97 'old_version', // (string) Used to determine which modules are the most recently added. previous_version:time.
98 'fallback_no_verify_ssl_certs', // (int) Flag for determining if this host must skip SSL Certificate verification due to misconfigured SSL.
99 'time_diff', // (int) Offset between Jetpack server's clocks and this server's clocks. Jetpack Server Time = time() + (int) Jetpack_Options::get_option( 'time_diff' )
100 'public', // (int|bool) If we think this site is public or not (1, 0), false if we haven't yet tried to figure it out.
101 'videopress', // (array) VideoPress options array.
102 'is_network_site', // (int|bool) If we think this site is a network or a single blog (1, 0), false if we haven't yet tried to figue it out.
103 'social_links', // (array) The specified links for each social networking site.
104 'identity_crisis_whitelist', // (array) An array of options, each having an array of the values whitelisted for it.
105 'gplus_authors', // (array) The Google+ authorship information for connected users.
106 'last_heartbeat', // (int) The timestamp of the last heartbeat that fired.
107 'hide_jitm', // (array) A list of just in time messages that we should not show because they have been dismissed by the user.
108 'custom_css_4.7_migration', // (bool) Whether Custom CSS has scanned for and migrated any legacy CSS CPT entries to the new Core format.
109 'image_widget_migration', // (bool) Whether any legacy Image Widgets have been converted to the new Core widget.
110 'gallery_widget_migration', // (bool) Whether any legacy Gallery Widgets have been converted to the new Core widget.
111 'sso_first_login', // (bool) Is this the first time the user logins via SSO.
112 'dismissed_hints', // (array) Part of Plugin Search Hints. List of cards that have been dismissed.
113 'first_admin_view', // (bool) Set to true the first time the user views the admin. Usually after the initial connection.
114 'setup_wizard_questionnaire', // (array) (DEPRECATED) List of user choices from the setup wizard.
115 'setup_wizard_status', // (string) (DEPRECATED) Status of the setup wizard.
116 'licensing_error', // (string) Last error message occurred while attaching licenses that is yet to be surfaced to the user.
117 'recommendations_data', // (array) The user choice and other data for the recommendations.
118 'recommendations_step', // (string) The current step of the recommendations.
119 'recommendations_conditional', // (array) An array of action-based recommendations.
120 'licensing_activation_notice_dismiss', // (array) The `last_detached_count` and the `last_dismissed_time` for the user-license activation notice.
121 'has_seen_wc_connection_modal', // (bool) Whether the site has displayed the WooCommerce Connection modal
122 'partner_coupon', // (string) A Jetpack partner issued coupon to promote a sale together with Jetpack.
123 'partner_coupon_added', // (string) A date for when `partner_coupon` was added, so we can auto-purge after a certain time interval.
124 'dismissed_backup_review_restore', // (bool) Determines if the component review request is dismissed for successful restore requests.
125 'dismissed_backup_review_backups', // (bool) Determines if the component review request is dismissed for successful backup requests.
126 'identity_crisis_url_secret', // (array) The IDC URL secret and its expiration date.
127 'identity_crisis_ip_requester', // (array) The IDC IP address and its expiration date.
128 'dismissed_welcome_banner', // (bool) Determines if the welcome banner has been dismissed or not.
129 'recommendations_evaluation', // (object) Catalog of recommended modules with corresponding score following successful site evaluation in Welcome Banner.
130 'dismissed_recommendations', // (bool) Determines if the recommendations have been dismissed or not.
131 'recommendations_first_run', // (bool) Determines if the current recommendations are the initial default auto-loaded ones (without user input).
132 'historically_active_modules', // (array) List of installed plugins/enabled modules that have at one point in time been active and working
133 );
134 }
135
136 /**
137 * Is the option name valid?
138 *
139 * @param string $name The name of the option.
140 * @param string|null $group The name of the group that the option is in. Default to null, which will search non_compact.
141 *
142 * @return bool Is the option name valid?
143 */
144 public static function is_valid( $name, $group = null ) {
145 if ( is_array( $name ) ) {
146 $compact_names = array();
147 foreach ( array_keys( self::$grouped_options ) as $_group ) {
148 $compact_names = array_merge( $compact_names, self::get_option_names( $_group ) );
149 }
150
151 $result = array_diff( $name, self::get_option_names( 'non_compact' ), $compact_names );
152
153 return empty( $result );
154 }
155
156 if ( $group === null || 'non_compact' === $group ) {
157 if ( in_array( $name, self::get_option_names( $group ), true ) ) {
158 return true;
159 }
160 }
161
162 foreach ( array_keys( self::$grouped_options ) as $_group ) {
163 if ( $group === null || $group === $_group ) {
164 if ( in_array( $name, self::get_option_names( $_group ), true ) ) {
165 return true;
166 }
167 }
168 }
169
170 return false;
171 }
172
173 /**
174 * Checks if an option must be saved for the whole network in WP Multisite
175 *
176 * @param string $option_name Option name. It must come _without_ `jetpack_%` prefix. The method will prefix the option name.
177 *
178 * @return bool
179 */
180 public static function is_network_option( $option_name ) {
181 if ( ! is_multisite() ) {
182 return false;
183 }
184 return in_array( $option_name, self::get_option_names( 'network' ), true );
185 }
186
187 /**
188 * Filters the requested option.
189 * This is a wrapper around `get_option_from_database` so that we can filter the option.
190 *
191 * @param string $name Option name. It must come _without_ `jetpack_%` prefix. The method will prefix the option name.
192 * @param mixed $default (optional).
193 *
194 * @return mixed
195 */
196 public static function get_option( $name, $default = false ) {
197 // Check if external storage should be used for this option
198 if ( self::should_use_external_storage( $name ) ) {
199 // Try external storage
200 if ( class_exists( 'Automattic\Jetpack\Connection\External_Storage' ) ) {
201 $external_value = \Automattic\Jetpack\Connection\External_Storage::get_value( $name );
202 if ( null !== $external_value ) {
203 return $external_value;
204 }
205 }
206 }
207
208 /**
209 * Filter Jetpack Options.
210 * Can be useful in environments when Jetpack is running with a different setup
211 *
212 * @since 1.7.0
213 *
214 * @param string $value The value from the database.
215 * @param string $name Option name, _without_ `jetpack_%` prefix.
216 * @return string $value, unless the filters modify it.
217 */
218 return apply_filters( 'jetpack_options', self::get_option_from_database( $name, $default ), $name );
219 }
220
221 /**
222 * Checks whether an option has a stored value, distinguishing an absent option from one stored
223 * as a falsy value. Reads from the same storage as `get_option` (external storage or database)
224 * but does not apply the `jetpack_options` filter, so a filter override is not mistaken for a
225 * stored value.
226 *
227 * @since 8.11.0
228 *
229 * @param string $name Option name. It must come _without_ `jetpack_%` prefix.
230 *
231 * @return bool Whether the option is stored.
232 */
233 public static function option_exists( $name ) {
234 if ( self::should_use_external_storage( $name )
235 && class_exists( 'Automattic\Jetpack\Connection\External_Storage' )
236 && null !== \Automattic\Jetpack\Connection\External_Storage::get_value( $name )
237 ) {
238 return true;
239 }
240
241 // A value no caller would ever store, so getting it back means the option is absent.
242 $sentinel = '__jetpack_option_absent__';
243 return $sentinel !== self::get_option_from_database( $name, $sentinel );
244 }
245
246 /**
247 * Returns the requested option. Looks in jetpack_options or jetpack_$name as appropriate.
248 *
249 * @param string $name Option name. It must come _without_ `jetpack_%` prefix. The method will prefix the option name.
250 * @param mixed $default (optional).
251 *
252 * @return mixed
253 */
254 private static function get_option_from_database( $name, $default = false ) {
255 if ( self::is_valid( $name, 'non_compact' ) ) {
256 if ( self::is_network_option( $name ) ) {
257 return get_site_option( "jetpack_$name", $default );
258 }
259
260 return get_option( "jetpack_$name", $default );
261 }
262
263 foreach ( array_keys( self::$grouped_options ) as $group ) {
264 if ( self::is_valid( $name, $group ) ) {
265 return self::get_grouped_option( $group, $name, $default );
266 }
267 }
268
269 return $default;
270 }
271
272 /**
273 * Options that can be stored in external storage.
274 *
275 * @since 6.18.0
276 *
277 * @var array
278 */
279 private static $external_storage_allowlist = array( 'blog_token', 'id', 'master_user', 'protected_owner', 'user_tokens' );
280
281 /**
282 * Determines if external storage should be used for a given option.
283 * Simple allowlist check with global killswitch.
284 *
285 * @since 6.17.0
286 *
287 * @param string $name Option name, _without_ `jetpack_%` prefix.
288 * @return bool True if external storage should be checked for this option.
289 */
290 private static function should_use_external_storage( $name ) {
291 // Check allowlist and global killswitch
292 if ( ! in_array( $name, self::$external_storage_allowlist, true ) ||
293 ( defined( 'JETPACK_EXTERNAL_STORAGE_DISABLED' ) && constant( 'JETPACK_EXTERNAL_STORAGE_DISABLED' ) ) ) {
294 return false;
295 }
296
297 return true;
298 }
299
300 /**
301 * Returns the requested option, and ensures it's autoloaded in the future.
302 * This does _not_ adjust the prefix in any way (does not prefix jetpack_%)
303 *
304 * @param string $name Option name.
305 * @param mixed $default (optional).
306 *
307 * @return mixed
308 */
309 public static function get_option_and_ensure_autoload( $name, $default ) {
310 // In this function the name is not adjusted by prefixing jetpack_
311 // so if it has already prefixed, we'll replace it and then
312 // check if the option name is a network option or not.
313 $jetpack_name = preg_replace( '/^jetpack_/', '', $name, 1 );
314 $is_network_option = self::is_network_option( $jetpack_name );
315 $value = $is_network_option ? get_site_option( $name ) : get_option( $name );
316
317 if ( false === $value && false !== $default ) {
318 if ( $is_network_option ) {
319 add_site_option( $name, $default );
320 } else {
321 add_option( $name, $default );
322 }
323 $value = $default;
324 }
325
326 return $value;
327 }
328
329 /**
330 * Update grouped option
331 *
332 * @param string $group Options group.
333 * @param string $name Options name.
334 * @param mixed $value Options value.
335 *
336 * @return bool Success or failure.
337 */
338 private static function update_grouped_option( $group, $name, $value ) {
339 $options = get_option( self::$grouped_options[ $group ] );
340 if ( ! is_array( $options ) ) {
341 $options = array();
342 }
343 $options[ $name ] = $value;
344
345 return update_option( self::$grouped_options[ $group ], $options );
346 }
347
348 /**
349 * Updates the single given option. Updates jetpack_options or jetpack_$name as appropriate.
350 *
351 * @param string $name Option name. It must come _without_ `jetpack_%` prefix. The method will prefix the option name.
352 * @param mixed $value Option value.
353 * @param bool|null $autoload If not compact option, allows specifying whether to autoload or not.
354 *
355 * @return bool Was the option successfully updated?
356 */
357 public static function update_option( $name, $value, $autoload = null ) {
358 /**
359 * Fires before Jetpack updates a specific option.
360 *
361 * @since 1.1.2
362 * @since-jetpack 3.0.0
363 *
364 * @param string $name The name of the option being updated.
365 * @param mixed $value The new value of the option.
366 */
367 do_action( 'pre_update_jetpack_option_' . $name, $name, $value );
368 if ( self::is_valid( $name, 'non_compact' ) ) {
369 if ( self::is_network_option( $name ) ) {
370 return update_site_option( "jetpack_$name", $value );
371 }
372
373 return update_option( "jetpack_$name", $value, $autoload );
374
375 }
376
377 foreach ( array_keys( self::$grouped_options ) as $group ) {
378 if ( self::is_valid( $name, $group ) ) {
379 return self::update_grouped_option( $group, $name, $value );
380 }
381 }
382
383 trigger_error( sprintf( 'Invalid Jetpack option name: %s', esc_html( $name ) ), E_USER_WARNING ); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_trigger_error -- Don't want to change legacy behavior.
384
385 return false;
386 }
387
388 /**
389 * Updates the multiple given options. Updates jetpack_options and/or jetpack_$name as appropriate.
390 *
391 * @param array $array array( option name => option value, ... ).
392 */
393 public static function update_options( $array ) {
394 $names = array_keys( $array );
395
396 foreach ( array_diff( $names, self::get_option_names(), self::get_option_names( 'non_compact' ), self::get_option_names( 'private' ) ) as $unknown_name ) {
397 trigger_error( sprintf( 'Invalid Jetpack option name: %s', esc_html( $unknown_name ) ), E_USER_WARNING ); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_trigger_error -- Don't change legacy behavior.
398 unset( $array[ $unknown_name ] );
399 }
400
401 foreach ( $names as $name ) {
402 self::update_option( $name, $array[ $name ] );
403 }
404 }
405
406 /**
407 * Deletes the given option. May be passed multiple option names as an array.
408 * Updates jetpack_options and/or deletes jetpack_$name as appropriate.
409 *
410 * @param string|array $names Option names. They must come _without_ `jetpack_%` prefix. The method will prefix the option names.
411 *
412 * @return bool Was the option successfully deleted?
413 */
414 public static function delete_option( $names ) {
415 $result = true;
416 $names = (array) $names;
417
418 if ( ! self::is_valid( $names ) ) {
419 // phpcs:disable -- This line triggers a handful of errors; ignoring to avoid changing legacy behavior.
420 trigger_error( sprintf( 'Invalid Jetpack option names: %s', print_r( $names, 1 ) ), E_USER_WARNING );
421 // phpcs:enable
422 return false;
423 }
424
425 foreach ( array_intersect( $names, self::get_option_names( 'non_compact' ) ) as $name ) {
426 if ( self::is_network_option( $name ) ) {
427 $result = delete_site_option( "jetpack_$name" );
428 } else {
429 $result = delete_option( "jetpack_$name" );
430 }
431 }
432
433 foreach ( array_keys( self::$grouped_options ) as $group ) {
434 if ( ! self::delete_grouped_option( $group, $names ) ) {
435 $result = false;
436 }
437 }
438
439 return $result;
440 }
441
442 /**
443 * Get group option.
444 *
445 * @param string $group Option group name.
446 * @param string $name Option name.
447 * @param mixed $default Default option value.
448 *
449 * @return mixed Option.
450 */
451 private static function get_grouped_option( $group, $name, $default ) {
452 $options = get_option( self::$grouped_options[ $group ] );
453 if ( is_array( $options ) && isset( $options[ $name ] ) ) {
454 return $options[ $name ];
455 }
456
457 return $default;
458 }
459
460 /**
461 * Delete grouped option.
462 *
463 * @param string $group Option group name.
464 * @param array $names Option names.
465 *
466 * @return bool Success or failure.
467 */
468 private static function delete_grouped_option( $group, $names ) {
469 $options = get_option( self::$grouped_options[ $group ], array() );
470
471 $to_delete = array_intersect( $names, self::get_option_names( $group ), array_keys( $options ) );
472 if ( $to_delete ) {
473 foreach ( $to_delete as $name ) {
474 unset( $options[ $name ] );
475 }
476
477 return update_option( self::$grouped_options[ $group ], $options );
478 }
479
480 return true;
481 }
482
483 /*
484 * Raw option methods allow Jetpack to get / update / delete options via direct DB queries, including options
485 * that are not created by the Jetpack plugin. This is helpful only in rare cases when we need to bypass
486 * cache and filters.
487 */
488
489 /**
490 * Deletes an option via $wpdb query.
491 *
492 * @param string $name Option name.
493 *
494 * @return bool Is the option deleted?
495 */
496 public static function delete_raw_option( $name ) {
497 if ( self::bypass_raw_option( $name ) ) {
498 return delete_option( $name );
499 }
500 global $wpdb;
501 $result = $wpdb->query( $wpdb->prepare( "DELETE FROM $wpdb->options WHERE option_name = %s", $name ) );
502 return $result;
503 }
504
505 /**
506 * Updates an option via $wpdb query.
507 *
508 * @param string $name Option name.
509 * @param mixed $value Option value.
510 * @param bool $autoload Specifying whether to autoload or not.
511 *
512 * @return bool Is the option updated?
513 */
514 public static function update_raw_option( $name, $value, $autoload = false ) {
515 if ( self::bypass_raw_option( $name ) ) {
516 return update_option( $name, $value, $autoload );
517 }
518 global $wpdb;
519 $autoload_value = $autoload ? 'yes' : 'no';
520
521 $old_value = $wpdb->get_var(
522 $wpdb->prepare(
523 "SELECT option_value FROM $wpdb->options WHERE option_name = %s LIMIT 1",
524 $name
525 )
526 );
527 if ( $old_value === $value ) {
528 return false;
529 }
530
531 $serialized_value = maybe_serialize( $value );
532 // below we used "insert ignore" to at least suppress the resulting error.
533 $updated_num = $wpdb->query(
534 $wpdb->prepare(
535 "UPDATE $wpdb->options SET option_value = %s WHERE option_name = %s",
536 $serialized_value,
537 $name
538 )
539 );
540
541 // Try inserting the option if the value doesn't exits.
542 if ( ! $updated_num ) {
543 $updated_num = $wpdb->query(
544 $wpdb->prepare(
545 "INSERT IGNORE INTO $wpdb->options ( option_name, option_value, autoload ) VALUES ( %s, %s, %s )",
546 $name,
547 $serialized_value,
548 $autoload_value
549 )
550 );
551 }
552 return (bool) $updated_num;
553 }
554
555 /**
556 * Gets an option via $wpdb query.
557 *
558 * @since 1.1.2
559 * @since-jetpack 5.4.0
560 *
561 * @param string $name Option name.
562 * @param mixed $default Default option value if option is not found.
563 *
564 * @return mixed Option value, or null if option is not found and default is not specified.
565 */
566 public static function get_raw_option( $name, $default = null ) {
567 if ( self::bypass_raw_option( $name ) ) {
568 return get_option( $name, $default );
569 }
570
571 global $wpdb;
572 $value = $wpdb->get_var(
573 $wpdb->prepare(
574 "SELECT option_value FROM $wpdb->options WHERE option_name = %s LIMIT 1",
575 $name
576 )
577 );
578 $value = maybe_unserialize( $value );
579
580 if ( null === $value && null !== $default ) {
581 return $default;
582 }
583
584 return $value;
585 }
586
587 /**
588 * This function checks for a constant that, if present, will disable direct DB queries Jetpack uses to manage certain options and force Jetpack to always use Options API instead.
589 * Options can be selectively managed via a blocklist by filtering option names via the jetpack_disabled_raw_option filter.
590 *
591 * @param string $name Option name.
592 *
593 * @return bool
594 */
595 public static function bypass_raw_option( $name ) {
596
597 if ( Constants::get_constant( 'JETPACK_DISABLE_RAW_OPTIONS' ) ) {
598 return true;
599 }
600 /**
601 * Allows to disable particular raw options.
602 *
603 * @since 1.1.2
604 * @since-jetpack 5.5.0
605 *
606 * @param array $disabled_raw_options An array of option names that you can selectively blocklist from being managed via direct database queries.
607 */
608 $disabled_raw_options = apply_filters( 'jetpack_disabled_raw_options', array() );
609 return isset( $disabled_raw_options[ $name ] );
610 }
611
612 /**
613 * Gets all known options that are used by Jetpack and managed by Jetpack_Options.
614 *
615 * @since 1.1.2
616 * @since-jetpack 5.4.0
617 *
618 * @param boolean $strip_unsafe_options If true, and by default, will strip out options necessary for the connection to WordPress.com.
619 * @return array An array of all options managed via the Jetpack_Options class.
620 */
621 public static function get_all_jetpack_options( $strip_unsafe_options = true ) {
622 $jetpack_options = self::get_option_names();
623 $jetpack_options_non_compat = self::get_option_names( 'non_compact' );
624 $jetpack_options_private = self::get_option_names( 'private' );
625
626 $all_jp_options = array_merge( $jetpack_options, $jetpack_options_non_compat, $jetpack_options_private );
627
628 if ( $strip_unsafe_options ) {
629 // Flag some Jetpack options as unsafe.
630 $unsafe_options = array(
631 'id', // (int) The Client ID/WP.com Blog ID of this site.
632 'master_user', // (int) The local User ID of the user who connected this site to jetpack.wordpress.com.
633 'protected_owner', // (array) Anchor identifying the locked connection owner. Resetting it would unlock ownership while the connection survives.
634 'version', // (string) Used during upgrade procedure to auto-activate new modules. version:time
635
636 // non_compact.
637 'activated',
638
639 // private.
640 'register',
641 'blog_token', // (string) The Client Secret/Blog Token of this site.
642 'user_token', // (string) The User Token of this site. (deprecated)
643 'user_tokens',
644 );
645
646 // Remove the unsafe Jetpack options.
647 foreach ( $unsafe_options as $unsafe_option ) {
648 $key = array_search( $unsafe_option, $all_jp_options, true );
649 if ( false !== $key ) {
650 unset( $all_jp_options[ $key ] );
651 }
652 }
653 }
654
655 return $all_jp_options;
656 }
657
658 /**
659 * Get all options that are not managed by the Jetpack_Options class that are used by Jetpack.
660 *
661 * @since 1.1.2
662 * @since-jetpack 5.4.0
663 *
664 * @return array
665 */
666 public static function get_all_wp_options() {
667 // A manual build of the wp options.
668 return array(
669 'sharing-options',
670 'disabled_likes',
671 'disabled_reblogs',
672 'jetpack_comments_likes_enabled',
673 'stats_options',
674 'stats_dashboard_widget',
675 'safecss_preview_rev',
676 'safecss_rev',
677 'safecss_revision_migrated',
678 'nova_menu_order',
679 'jetpack_portfolio',
680 'jetpack_portfolio_posts_per_page',
681 'jetpack_testimonial',
682 'jetpack_testimonial_posts_per_page',
683 'sharedaddy_disable_resources',
684 'sharing-options',
685 'sharing-services',
686 'site_icon_temp_data',
687 'featured-content',
688 'site_logo',
689 'jetpack_dismissed_notices',
690 'jetpack-twitter-cards-site-tag',
691 'jetpack-sitemap-state',
692 'jetpack_sitemap_post_types',
693 'jetpack_sitemap_location',
694 'jetpack_protect_key',
695 'jetpack_protect_blocked_attempts',
696 'jetpack_protect_activating',
697 'jetpack_active_plan',
698 'jetpack_activation_source',
699 'jetpack_site_products',
700 'jetpack_sso_match_by_email',
701 'jetpack_sso_require_two_step',
702 'jetpack_sso_remove_login_form',
703 'jetpack_last_connect_url_check',
704 'jetpack_excluded_extensions',
705 );
706 }
707
708 /**
709 * Gets all options that can be safely reset by CLI.
710 *
711 * @since 1.1.2
712 * @since-jetpack 5.4.0
713 *
714 * @return array array Associative array containing jp_options which are managed by the Jetpack_Options class and wp_options which are not.
715 */
716 public static function get_options_for_reset() {
717 $all_jp_options = self::get_all_jetpack_options();
718
719 $wp_options = self::get_all_wp_options();
720
721 $options = array(
722 'jp_options' => $all_jp_options,
723 'wp_options' => $wp_options,
724 );
725
726 return $options;
727 }
728
729 /**
730 * Delete all known options
731 *
732 * @since 1.1.2
733 * @since-jetpack 5.4.0
734 *
735 * @return void
736 */
737 public static function delete_all_known_options() {
738 // Delete all compact options.
739 foreach ( (array) self::$grouped_options as $option_name ) {
740 delete_option( $option_name );
741 }
742
743 // Delete all non-compact Jetpack options.
744 foreach ( (array) self::get_option_names( 'non-compact' ) as $option_name ) {
745 self::delete_option( $option_name );
746 }
747
748 // Delete all options that can be reset via CLI, that aren't Jetpack options.
749 foreach ( (array) self::get_all_wp_options() as $option_name ) {
750 delete_option( $option_name );
751 }
752 }
753 }
754