PluginProbe
Jetpack – WP Security, Backup, Speed, & Growth / 16.3-a.5
Jetpack – WP Security, Backup, Speed, & Growth v16.3-a.5
16.3-beta 16.3-a.5 16.3-a.7 16.3-a.3 16.3-a.1 16.2 16.2-beta 12.0.3 12.1.3 12.2.3 12.3.2 12.4.2 12.5.2 12.6.4 12.7.3 12.8.3 12.9.5 13.0.2 13.1.5 13.2.4 13.3.3 13.4.5 13.5.2 13.6.2 13.7.2 All 507 releases
← All changes | jetpack_vendor/automattic/jetpack-sync/src/modules/class-updates.php +603 -0 16.2-beta → 16.3-a.5 View file →
@@ -1,0 +1,603 @@
1 +<?php
2 +/**
3 + * Updates sync module.
4 + *
5 + * @package automattic/jetpack-sync
6 + */
7 +
8 +namespace Automattic\Jetpack\Sync\Modules;
9 +
10 +use Automattic\Jetpack\Constants as Jetpack_Constants;
11 +
12 +if ( ! defined( 'ABSPATH' ) ) {
13 + exit( 0 );
14 +}
15 +
16 +/**
17 + * Class to handle sync for updates.
18 + */
19 +class Updates extends Module {
20 + /**
21 + * Name of the updates checksum option.
22 + *
23 + * @var string
24 + */
25 + const UPDATES_CHECKSUM_OPTION_NAME = 'jetpack_updates_sync_checksum';
26 +
27 + /**
28 + * WordPress Version.
29 + *
30 + * @access private
31 + *
32 + * @var string
33 + */
34 + private $old_wp_version = null;
35 +
36 + /**
37 + * The current updates.
38 + *
39 + * @access private
40 + *
41 + * @var array
42 + */
43 + private $updates = array();
44 +
45 + /**
46 + * Set module defaults.
47 + *
48 + * @access public
49 + */
50 + public function set_defaults() {
51 + $this->updates = array();
52 + }
53 +
54 + /**
55 + * Sync module name.
56 + *
57 + * @access public
58 + *
59 + * @return string
60 + */
61 + public function name() {
62 + return 'updates';
63 + }
64 +
65 + /**
66 + * Initialize updates action listeners.
67 + *
68 + * @access public
69 + *
70 + * @param callable $callable Action handler callable.
71 + */
72 + public function init_listeners( $callable ) {
73 + global $wp_version;
74 + $this->old_wp_version = $wp_version;
75 + add_action( 'set_site_transient_update_plugins', array( $this, 'validate_update_change' ), 10, 3 );
76 + add_action( 'set_site_transient_update_themes', array( $this, 'validate_update_change' ), 10, 3 );
77 + add_action( 'set_site_transient_update_core', array( $this, 'validate_update_change' ), 10, 3 );
78 +
79 + add_action( 'jetpack_update_plugins_change', $callable );
80 + add_action( 'jetpack_update_themes_change', $callable );
81 + add_action( 'jetpack_update_core_change', $callable );
82 +
83 + add_filter(
84 + 'jetpack_sync_before_enqueue_jetpack_update_themes_change',
85 + array(
86 + $this,
87 + 'expand_themes',
88 + )
89 + );
90 +
91 + add_filter(
92 + 'jetpack_sync_before_enqueue_jetpack_update_plugins_change',
93 + array(
94 + $this,
95 + 'filter_update_keys',
96 + ),
97 + 10,
98 + 2
99 + );
100 + add_filter(
101 + 'jetpack_sync_before_enqueue_upgrader_process_complete',
102 + array(
103 + $this,
104 + 'filter_upgrader_process_complete',
105 + ),
106 + 10,
107 + 2
108 + );
109 +
110 + if ( is_multisite() ) {
111 + add_filter( 'pre_update_site_option_wpmu_upgrade_site', array( $this, 'update_core_network_event' ), 10, 2 );
112 + add_action( 'jetpack_sync_core_update_network', $callable, 10, 3 );
113 + }
114 +
115 + // Send data when update completes.
116 + add_action( '_core_updated_successfully', array( $this, 'update_core' ) );
117 + add_action( 'jetpack_sync_core_reinstalled_successfully', $callable );
118 + add_action( 'jetpack_sync_core_autoupdated_successfully', $callable, 10, 2 );
119 + add_action( 'jetpack_sync_core_updated_successfully', $callable, 10, 2 );
120 + }
121 +
122 + /**
123 + * Initialize updates action listeners for full sync.
124 + *
125 + * @access public
126 + *
127 + * @param callable $callable Action handler callable.
128 + */
129 + public function init_full_sync_listeners( $callable ) {
130 + add_action( 'jetpack_full_sync_updates', $callable );
131 + }
132 +
133 + /**
134 + * Initialize the module in the sender.
135 + *
136 + * @access public
137 + */
138 + public function init_before_send() {
139 + add_filter( 'jetpack_sync_before_send_jetpack_full_sync_updates', array( $this, 'expand_updates' ) );
140 + }
141 +
142 + /**
143 + * Handle a core network update.
144 + *
145 + * @access public
146 + *
147 + * @param int $wp_db_version Current version of the WordPress database.
148 + * @param int $old_wp_db_version Old version of the WordPress database.
149 + * @return int Current version of the WordPress database.
150 + */
151 + public function update_core_network_event( $wp_db_version, $old_wp_db_version ) {
152 + global $wp_version;
153 + /**
154 + * Sync event for when core wp network updates to a new db version
155 + *
156 + * @since 1.6.3
157 + * @since-jetpack 5.0.0
158 + *
159 + * @param int $wp_db_version the latest wp_db_version
160 + * @param int $old_wp_db_version previous wp_db_version
161 + * @param string $wp_version the latest wp_version
162 + */
163 + do_action( 'jetpack_sync_core_update_network', $wp_db_version, $old_wp_db_version, $wp_version );
164 + return $wp_db_version;
165 + }
166 +
167 + /**
168 + * Handle a core update.
169 + *
170 + * @access public
171 + *
172 + * @todo Implement nonce or refactor to use `admin_post_{$action}` hooks instead.
173 + *
174 + * @param string $new_wp_version The new WP core version.
175 + */
176 + public function update_core( $new_wp_version ) {
177 + global $pagenow;
178 +
179 + // // phpcs:ignore WordPress.Security.NonceVerification.Recommended
180 + if ( isset( $_GET['action'] ) && 'do-core-reinstall' === $_GET['action'] ) {
181 + /**
182 + * Sync event that fires when core reinstall was successful
183 + *
184 + * @since 1.6.3
185 + * @since-jetpack 5.0.0
186 + *
187 + * @param string $new_wp_version the updated WordPress version
188 + */
189 + do_action( 'jetpack_sync_core_reinstalled_successfully', $new_wp_version );
190 + return;
191 + }
192 +
193 + // Core was autoupdated.
194 + if (
195 + 'update-core.php' !== $pagenow &&
196 + ! Jetpack_Constants::is_true( 'REST_API_REQUEST' ) // WP.com rest api calls should never be marked as a core autoupdate.
197 + ) {
198 + /**
199 + * Sync event that fires when core autoupdate was successful
200 + *
201 + * @since 1.6.3
202 + * @since-jetpack 5.0.0
203 + *
204 + * @param string $new_wp_version the updated WordPress version
205 + * @param string $old_wp_version the previous WordPress version
206 + */
207 + do_action( 'jetpack_sync_core_autoupdated_successfully', $new_wp_version, $this->old_wp_version );
208 + return;
209 + }
210 + /**
211 + * Sync event that fires when core update was successful
212 + *
213 + * @since 1.6.3
214 + * @since-jetpack 5.0.0
215 + *
216 + * @param string $new_wp_version the updated WordPress version
217 + * @param string $old_wp_version the previous WordPress version
218 + */
219 + do_action( 'jetpack_sync_core_updated_successfully', $new_wp_version, $this->old_wp_version );
220 + }
221 +
222 + /**
223 + * Retrieve the checksum for an update.
224 + *
225 + * @access public
226 + *
227 + * @param object $update The update object.
228 + * @param string $transient The transient we're retrieving a checksum for.
229 + * @return int The checksum.
230 + */
231 + public function get_update_checksum( $update, $transient ) {
232 + $updates = array();
233 + $no_updated = array();
234 + switch ( $transient ) {
235 + case 'update_plugins':
236 + if ( ! empty( $update->response ) && is_array( $update->response ) ) {
237 + foreach ( $update->response as $plugin_slug => $plugin_data ) {
238 + $plugin_data = (array) $plugin_data;
239 + if ( ! empty( $plugin_slug ) && isset( $plugin_data['new_version'] ) ) {
240 + $updates[] = array( $plugin_slug => $plugin_data['new_version'] );
241 + }
242 + }
243 + }
244 + if ( ! empty( $update->no_update ) ) {
245 + $no_updated = array_keys( $update->no_update );
246 + }
247 +
248 + if ( ! isset( $no_updated['jetpack/jetpack.php'] ) && isset( $updates['jetpack/jetpack.php'] ) ) {
249 + return false;
250 + }
251 +
252 + break;
253 + case 'update_themes':
254 + if ( ! empty( $update->response ) && is_array( $update->response ) ) {
255 + foreach ( $update->response as $theme_slug => $theme_data ) {
256 + $theme_data = (array) $theme_data;
257 + if ( ! empty( $theme_slug ) && isset( $theme_data['new_version'] ) ) {
258 + $updates[] = array( $theme_slug => $theme_data['new_version'] );
259 + }
260 + }
261 + }
262 +
263 + if ( ! empty( $update->checked ) ) {
264 + $no_updated = $update->checked;
265 + }
266 +
267 + break;
268 + case 'update_core':
269 + if ( ! empty( $update->updates ) && is_array( $update->updates ) ) {
270 + foreach ( $update->updates as $core_update ) {
271 + if ( ! empty( $core_update->response ) && 'latest' === $core_update->response ) {
272 + continue;
273 + }
274 + if ( ! empty( $core_update->response ) && isset( $core_update->packages->full ) ) {
275 + $updates[] = array( $core_update->response => $core_update->packages->full );
276 + }
277 + }
278 + }
279 +
280 + if ( ! empty( $update->version_checked ) ) {
281 + $no_updated = $update->version_checked;
282 + }
283 +
284 + if ( empty( $updates ) ) {
285 + return false;
286 + }
287 + break;
288 +
289 + }
290 + if ( empty( $updates ) && empty( $no_updated ) ) {
291 + return false;
292 + }
293 + return $this->get_check_sum( array( $no_updated, $updates ) );
294 + }
295 +
296 + /**
297 + * Validate a change coming from an update before sending for sync.
298 + *
299 + * @access public
300 + *
301 + * @param mixed $value Site transient value.
302 + * @param int $expiration Time until transient expiration in seconds.
303 + * @param string $transient Transient name.
304 + */
305 + public function validate_update_change( $value, $expiration, $transient ) {
306 + $new_checksum = $this->get_update_checksum( $value, $transient );
307 +
308 + if ( false === $new_checksum ) {
309 + return;
310 + }
311 +
312 + $checksums = get_option( self::UPDATES_CHECKSUM_OPTION_NAME, array() );
313 +
314 + if ( isset( $checksums[ $transient ] ) && $checksums[ $transient ] === $new_checksum ) {
315 + return;
316 + }
317 +
318 + $checksums[ $transient ] = $new_checksum;
319 +
320 + update_option( self::UPDATES_CHECKSUM_OPTION_NAME, $checksums );
321 + if ( 'update_core' === $transient ) {
322 + /**
323 + * Trigger a change to core update that we want to sync.
324 + *
325 + * @since 1.6.3
326 + * @since-jetpack 5.1.0
327 + *
328 + * @param array $value Contains info that tells us what needs updating.
329 + */
330 + do_action( 'jetpack_update_core_change', $value );
331 + return;
332 + }
333 + if ( empty( $this->updates ) ) {
334 + // Lets add the shutdown method once and only when the updates move from empty to filled with something.
335 + add_action( 'shutdown', array( $this, 'sync_last_event' ), 9 );
336 + }
337 + if ( ! isset( $this->updates[ $transient ] ) ) {
338 + $this->updates[ $transient ] = array();
339 + }
340 + $this->updates[ $transient ][] = $value;
341 + }
342 +
343 + /**
344 + * Sync the last update only.
345 + *
346 + * @access public
347 + */
348 + public function sync_last_event() {
349 + foreach ( $this->updates as $transient => $values ) {
350 + $value = end( $values ); // Only send over the last value.
351 + /**
352 + * Trigger a change to a specific update that we want to sync.
353 + * Triggers one of the following actions:
354 + * - jetpack_{$transient}_change
355 + * - jetpack_update_plugins_change
356 + * - jetpack_update_themes_change
357 + *
358 + * @since 1.6.3
359 + * @since-jetpack 5.1.0
360 + *
361 + * @param array $value Contains info that tells us what needs updating.
362 + */
363 + do_action( "jetpack_{$transient}_change", $value );
364 + }
365 + }
366 +
367 + /**
368 + * Enqueue the updates actions for full sync.
369 + *
370 + * @access public
371 + *
372 + * @param array $config Full sync configuration for this sync module.
373 + * @param int $max_items_to_enqueue Maximum number of items to enqueue.
374 + * @param boolean $state True if full sync has finished enqueueing this module, false otherwise.
375 + * @return array Number of actions enqueued, and next module state.
376 + */
377 + public function enqueue_full_sync_actions( $config, $max_items_to_enqueue, $state ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
378 + /**
379 + * Tells the client to sync all updates to the server
380 + *
381 + * @since 1.6.3
382 + * @since-jetpack 4.2.0
383 + *
384 + * @param boolean Whether to expand updates (should always be true)
385 + */
386 + do_action( 'jetpack_full_sync_updates', true );
387 +
388 + // The number of actions enqueued, and next module state (true == done).
389 + return array( 1, true );
390 + }
391 +
392 + /**
393 + * Send the updates actions for full sync.
394 + *
395 + * @access public
396 + *
397 + * @param array $config Full sync configuration for this sync module.
398 + * @param array $status This module Full Sync status.
399 + * @param int $send_until The timestamp until the current request can send.
400 + * @param int $started The timestamp when the full sync started.
401 + *
402 + * @return array This module Full Sync status.
403 + */
404 + public function send_full_sync_actions( $config, $status, $send_until, $started ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
405 + // we call this instead of do_action when sending immediately.
406 + $result = $this->send_action( 'jetpack_full_sync_updates', array( true ) );
407 +
408 + if ( is_wp_error( $result ) ) {
409 + $status['error'] = true;
410 + return $status;
411 + }
412 + $status['finished'] = true;
413 + $status['sent'] = $status['total'];
414 + return $status;
415 + }
416 +
417 + /**
418 + * Retrieve an estimated number of actions that will be enqueued.
419 + *
420 + * @access public
421 + *
422 + * @param array $config Full sync configuration for this sync module.
423 + * @return int Number of items yet to be enqueued.
424 + */
425 + public function estimate_full_sync_actions( $config ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
426 + return 1;
427 + }
428 +
429 + /**
430 + * Retrieve the actions that will be sent for this module during a full sync.
431 + *
432 + * @access public
433 + *
434 + * @return array Full sync actions of this module.
435 + */
436 + public function get_full_sync_actions() {
437 + return array( 'jetpack_full_sync_updates' );
438 + }
439 +
440 + /**
441 + * Retrieve all updates that we're interested in.
442 + *
443 + * @access public
444 + *
445 + * @return array All updates.
446 + */
447 + public function get_all_updates() {
448 + return array(
449 + 'core' => get_site_transient( 'update_core' ),
450 + 'plugins' => get_site_transient( 'update_plugins' ),
451 + 'themes' => get_site_transient( 'update_themes' ),
452 + );
453 + }
454 +
455 + /**
456 + * Remove unnecessary keys from synced updates data.
457 + *
458 + * @access public
459 + *
460 + * @param array $args Hook arguments.
461 + * @return array $args Hook arguments.
462 + */
463 + public function filter_update_keys( $args ) {
464 + $updates = $args[0];
465 +
466 + if ( isset( $updates->no_update ) ) {
467 + unset( $updates->no_update );
468 + }
469 +
470 + return $args;
471 + }
472 +
473 + /**
474 + * Filter out upgrader object from the completed upgrader action args.
475 + *
476 + * @access public
477 + *
478 + * @param array $args Hook arguments.
479 + * @return array $args Filtered hook arguments.
480 + */
481 + public function filter_upgrader_process_complete( $args ) {
482 + array_shift( $args );
483 +
484 + return $args;
485 + }
486 +
487 + /**
488 + * Expand the updates within a hook before they are serialized and sent to the server.
489 + *
490 + * @access public
491 + *
492 + * @param array $args The hook parameters.
493 + * @return array $args The hook parameters.
494 + */
495 + public function expand_updates( $args ) {
496 + if ( $args[0] ) {
497 + return $this->get_all_updates();
498 + }
499 +
500 + return $args;
501 + }
502 +
503 + /**
504 + * Expand the themes within a hook before they are serialized and sent to the server.
505 + *
506 + * @access public
507 + *
508 + * @param array $args The hook parameters.
509 + * @return array $args The hook parameters.
510 + */
511 + public function expand_themes( $args ) {
512 + if ( ! isset( $args[0]->response ) ) {
513 + return $args;
514 + }
515 + if ( ! is_array( $args[0]->response ) ) {
516 + // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_trigger_error
517 + trigger_error( 'Warning: Not an Array as expected but -> ' . wp_json_encode( $args[0]->response, JSON_UNESCAPED_SLASHES ) . ' instead', E_USER_WARNING );
518 + return $args;
519 + }
520 + foreach ( $args[0]->response as $stylesheet => &$theme_data ) {
521 + $theme_data = (array) $theme_data;
522 + // Make sure the theme data array is not empty and has data that would indicate it is in the correct format.
523 + if ( isset( $theme_data['theme'] ) ) {
524 + $theme = wp_get_theme( $stylesheet );
525 + $theme_data['name'] = $theme->name;
526 + }
527 + }
528 + return $args;
529 + }
530 +
531 + /**
532 + * Perform module cleanup.
533 + * Deletes any transients and options that this module uses.
534 + * Usually triggered when uninstalling the plugin.
535 + *
536 + * @access public
537 + */
538 + public function reset_data() {
539 + delete_option( self::UPDATES_CHECKSUM_OPTION_NAME );
540 + }
541 +
542 + /**
543 + * Return Total number of objects.
544 + *
545 + * @param array $config Full Sync config.
546 + *
547 + * @return int total
548 + */
549 + public function total( $config ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
550 + return 3;
551 + }
552 +
553 + /**
554 + * Retrieve a set of updates by their IDs.
555 + *
556 + * @access public
557 + *
558 + * @param string $object_type Object type.
559 + * @param array $ids Object IDs.
560 + * @return array Array of objects.
561 + */
562 + public function get_objects_by_id( $object_type, $ids ) {
563 + if ( empty( $ids ) || empty( $object_type ) || 'update' !== $object_type ) {
564 + return array();
565 + }
566 +
567 + $objects = array();
568 + foreach ( (array) $ids as $id ) {
569 + $object = $this->get_object_by_id( $object_type, $id );
570 +
571 + if ( 'all' === $id ) {
572 + // If all was requested it contains all updates and can simply be returned.
573 + return $object;
574 + }
575 + $objects[ $id ] = $object;
576 + }
577 +
578 + return $objects;
579 + }
580 +
581 + /**
582 + * Retrieve a update by its id.
583 + *
584 + * @access public
585 + *
586 + * @param string $object_type Type of the sync object.
587 + * @param string $id ID of the sync object.
588 + * @return mixed Value of Update.
589 + */
590 + public function get_object_by_id( $object_type, $id ) {
591 + if ( 'update' === $object_type ) {
592 +
593 + // Only whitelisted constants can be returned.
594 + if ( in_array( $id, array( 'core', 'plugins', 'themes' ), true ) ) {
595 + return get_site_transient( 'update_' . $id );
596 + } elseif ( 'all' === $id ) {
597 + return $this->get_all_updates();
598 + }
599 + }
600 +
601 + return false;
602 + }
603 +}