Completed
Push — master ( db7a23...fdf30b )
by
unknown
240:37 queued 229:48
created

Themes::get_objects_by_id()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
nc 2
nop 2
dl 0
loc 7
rs 10
c 0
b 0
f 0
1
<?php
2
/**
3
 * Themes sync module.
4
 *
5
 * @package automattic/jetpack-sync
6
 */
7
8
namespace Automattic\Jetpack\Sync\Modules;
9
10
/**
11
 * Class to handle sync for themes.
12
 */
13
class Themes extends Module {
14
	/**
15
	 * Sync module name.
16
	 *
17
	 * @access public
18
	 *
19
	 * @return string
20
	 */
21
	public function name() {
22
		return 'themes';
23
	}
24
25
	/**
26
	 * Initialize themes action listeners.
27
	 *
28
	 * @access public
29
	 *
30
	 * @param callable $callable Action handler callable.
31
	 */
32
	public function init_listeners( $callable ) {
33
		add_action( 'switch_theme', array( $this, 'sync_theme_support' ), 10, 3 );
34
		add_action( 'jetpack_sync_current_theme_support', $callable, 10, 2 );
35
		add_action( 'upgrader_process_complete', array( $this, 'check_upgrader' ), 10, 2 );
36
		add_action( 'jetpack_installed_theme', $callable, 10, 2 );
37
		add_action( 'jetpack_updated_themes', $callable, 10, 2 );
38
		add_action( 'delete_site_transient_update_themes', array( $this, 'detect_theme_deletion' ) );
39
		add_action( 'jetpack_deleted_theme', $callable, 10, 2 );
40
		add_filter( 'wp_redirect', array( $this, 'detect_theme_edit' ) );
41
		add_action( 'jetpack_edited_theme', $callable, 10, 2 );
42
		add_action( 'wp_ajax_edit-theme-plugin-file', array( $this, 'theme_edit_ajax' ), 0 );
43
		add_action( 'update_site_option_allowedthemes', array( $this, 'sync_network_allowed_themes_change' ), 10, 4 );
44
		add_action( 'jetpack_network_disabled_themes', $callable, 10, 2 );
45
		add_action( 'jetpack_network_enabled_themes', $callable, 10, 2 );
46
47
		// Sidebar updates.
48
		add_action( 'update_option_sidebars_widgets', array( $this, 'sync_sidebar_widgets_actions' ), 10, 2 );
49
50
		add_action( 'jetpack_widget_added', $callable, 10, 4 );
51
		add_action( 'jetpack_widget_removed', $callable, 10, 4 );
52
		add_action( 'jetpack_widget_moved_to_inactive', $callable, 10, 2 );
53
		add_action( 'jetpack_cleared_inactive_widgets', $callable );
54
		add_action( 'jetpack_widget_reordered', $callable, 10, 2 );
55
		add_filter( 'widget_update_callback', array( $this, 'sync_widget_edit' ), 10, 4 );
56
		add_action( 'jetpack_widget_edited', $callable );
57
	}
58
59
	/**
60
	 * Sync handler for a widget edit.
61
	 *
62
	 * @access public
63
	 *
64
	 * @todo Implement nonce verification
65
	 *
66
	 * @param array      $instance      The current widget instance's settings.
67
	 * @param array      $new_instance  Array of new widget settings.
68
	 * @param array      $old_instance  Array of old widget settings.
69
	 * @param \WP_Widget $widget_object The current widget instance.
70
	 * @return array The current widget instance's settings.
71
	 */
72
	public function sync_widget_edit( $instance, $new_instance, $old_instance, $widget_object ) {
73
		if ( empty( $old_instance ) ) {
74
			return $instance;
75
		}
76
77
		// Don't trigger sync action if this is an ajax request, because Customizer makes them during preview before saving changes.
78
		// phpcs:disable WordPress.Security.NonceVerification.Missing
79
		if ( defined( 'DOING_AJAX' ) && DOING_AJAX && isset( $_POST['customized'] ) ) {
80
			return $instance;
81
		}
82
83
		$widget = array(
84
			'name'  => $widget_object->name,
85
			'id'    => $widget_object->id,
86
			'title' => isset( $new_instance['title'] ) ? $new_instance['title'] : '',
87
		);
88
		/**
89
		 * Trigger action to alert $callable sync listener that a widget was edited.
90
		 *
91
		 * @since 5.0.0
92
		 *
93
		 * @param string $widget_name , Name of edited widget
94
		 */
95
		do_action( 'jetpack_widget_edited', $widget );
96
97
		return $instance;
98
	}
99
100
	/**
101
	 * Sync handler for network allowed themes change.
102
	 *
103
	 * @access public
104
	 *
105
	 * @param string $option     Name of the network option.
106
	 * @param mixed  $value      Current value of the network option.
107
	 * @param mixed  $old_value  Old value of the network option.
108
	 * @param int    $network_id ID of the network.
109
	 */
110
	public function sync_network_allowed_themes_change( $option, $value, $old_value, $network_id ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
111
		$all_enabled_theme_slugs = array_keys( $value );
112
113
		if ( count( $old_value ) > count( $value ) ) {
114
115
			// Suppress jetpack_network_disabled_themes sync action when theme is deleted.
116
			$delete_theme_call = $this->get_delete_theme_call();
117
			if ( ! empty( $delete_theme_call ) ) {
118
				return;
119
			}
120
121
			$newly_disabled_theme_names = array_keys( array_diff_key( $old_value, $value ) );
122
			$newly_disabled_themes      = $this->get_theme_details_for_slugs( $newly_disabled_theme_names );
123
			/**
124
			 * Trigger action to alert $callable sync listener that network themes were disabled.
125
			 *
126
			 * @since 5.0.0
127
			 *
128
			 * @param mixed $newly_disabled_themes, Array of info about network disabled themes
129
			 * @param mixed $all_enabled_theme_slugs, Array of slugs of all enabled themes
130
			 */
131
			do_action( 'jetpack_network_disabled_themes', $newly_disabled_themes, $all_enabled_theme_slugs );
132
			return;
133
		}
134
135
		$newly_enabled_theme_names = array_keys( array_diff_key( $value, $old_value ) );
136
		$newly_enabled_themes      = $this->get_theme_details_for_slugs( $newly_enabled_theme_names );
137
		/**
138
		 * Trigger action to alert $callable sync listener that network themes were enabled
139
		 *
140
		 * @since 5.0.0
141
		 *
142
		 * @param mixed $newly_enabled_themes , Array of info about network enabled themes
143
		 * @param mixed $all_enabled_theme_slugs, Array of slugs of all enabled themes
144
		 */
145
		do_action( 'jetpack_network_enabled_themes', $newly_enabled_themes, $all_enabled_theme_slugs );
146
	}
147
148
	/**
149
	 * Retrieve details for one or more themes by their slugs.
150
	 *
151
	 * @access private
152
	 *
153
	 * @param array $theme_slugs Theme slugs.
154
	 * @return array Details for the themes.
155
	 */
156
	private function get_theme_details_for_slugs( $theme_slugs ) {
157
		$theme_data = array();
158 View Code Duplication
		foreach ( $theme_slugs as $slug ) {
159
			$theme               = wp_get_theme( $slug );
160
			$theme_data[ $slug ] = array(
161
				'name'    => $theme->get( 'Name' ),
162
				'version' => $theme->get( 'Version' ),
163
				'uri'     => $theme->get( 'ThemeURI' ),
164
				'slug'    => $slug,
165
			);
166
		}
167
		return $theme_data;
168
	}
169
170
	/**
171
	 * Detect a theme edit during a redirect.
172
	 *
173
	 * @access public
174
	 *
175
	 * @param string $redirect_url Redirect URL.
176
	 * @return string Redirect URL.
177
	 */
178
	public function detect_theme_edit( $redirect_url ) {
179
		$url              = wp_parse_url( admin_url( $redirect_url ) );
180
		$theme_editor_url = wp_parse_url( admin_url( 'theme-editor.php' ) );
181
182
		if ( $theme_editor_url['path'] !== $url['path'] ) {
183
			return $redirect_url;
184
		}
185
186
		$query_params = array();
187
		wp_parse_str( $url['query'], $query_params );
188
		if (
189
			! isset( $_POST['newcontent'] ) ||
190
			! isset( $query_params['file'] ) ||
191
			! isset( $query_params['theme'] ) ||
192
			! isset( $query_params['updated'] )
193
		) {
194
			return $redirect_url;
195
		}
196
		$theme      = wp_get_theme( $query_params['theme'] );
197
		$theme_data = array(
198
			'name'    => $theme->get( 'Name' ),
199
			'version' => $theme->get( 'Version' ),
200
			'uri'     => $theme->get( 'ThemeURI' ),
201
		);
202
203
		/**
204
		 * Trigger action to alert $callable sync listener that a theme was edited.
205
		 *
206
		 * @since 5.0.0
207
		 *
208
		 * @param string $query_params['theme'], Slug of edited theme
209
		 * @param string $theme_data, Information about edited them
210
		 */
211
		do_action( 'jetpack_edited_theme', $query_params['theme'], $theme_data );
212
213
		return $redirect_url;
214
	}
215
216
	/**
217
	 * Handler for AJAX theme editing.
218
	 *
219
	 * @todo Refactor to use WP_Filesystem instead of fopen()/fclose().
220
	 */
221
	public function theme_edit_ajax() {
222
		$args = wp_unslash( $_POST );
223
224
		if ( empty( $args['theme'] ) ) {
225
			return;
226
		}
227
228
		if ( empty( $args['file'] ) ) {
229
			return;
230
		}
231
		$file = $args['file'];
232
		if ( 0 !== validate_file( $file ) ) {
233
			return;
234
		}
235
236
		if ( ! isset( $args['newcontent'] ) ) {
237
			return;
238
		}
239
240
		if ( ! isset( $args['nonce'] ) ) {
241
			return;
242
		}
243
244
		$stylesheet = $args['theme'];
245
		if ( 0 !== validate_file( $stylesheet ) ) {
246
			return;
247
		}
248
249
		if ( ! current_user_can( 'edit_themes' ) ) {
250
			return;
251
		}
252
253
		$theme = wp_get_theme( $stylesheet );
254
		if ( ! $theme->exists() ) {
255
			return;
256
		}
257
258
		if ( ! wp_verify_nonce( $args['nonce'], 'edit-theme_' . $stylesheet . '_' . $file ) ) {
259
			return;
260
		}
261
262
		if ( $theme->errors() && 'theme_no_stylesheet' === $theme->errors()->get_error_code() ) {
263
			return;
264
		}
265
266
		$editable_extensions = wp_get_theme_file_editable_extensions( $theme );
267
268
		$allowed_files = array();
269
		foreach ( $editable_extensions as $type ) {
270
			switch ( $type ) {
271
				case 'php':
272
					$allowed_files = array_merge( $allowed_files, $theme->get_files( 'php', -1 ) );
273
					break;
274
				case 'css':
275
					$style_files                = $theme->get_files( 'css', -1 );
276
					$allowed_files['style.css'] = $style_files['style.css'];
277
					$allowed_files              = array_merge( $allowed_files, $style_files );
278
					break;
279
				default:
280
					$allowed_files = array_merge( $allowed_files, $theme->get_files( $type, -1 ) );
281
					break;
282
			}
283
		}
284
285
		$real_file = $theme->get_stylesheet_directory() . '/' . $file;
286
		if ( 0 !== validate_file( $real_file, $allowed_files ) ) {
287
			return;
288
		}
289
290
		// Ensure file is real.
291
		if ( ! is_file( $real_file ) ) {
292
			return;
293
		}
294
295
		// Ensure file extension is allowed.
296
		$extension = null;
0 ignored issues
show
Unused Code introduced by
$extension is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
297
		if ( preg_match( '/\.([^.]+)$/', $real_file, $matches ) ) {
298
			$extension = strtolower( $matches[1] );
299
			if ( ! in_array( $extension, $editable_extensions, true ) ) {
300
				return;
301
			}
302
		}
303
304
		if ( ! is_writeable( $real_file ) ) {
305
			return;
306
		}
307
308
		// phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_read_fopen
309
		$file_pointer = fopen( $real_file, 'w+' );
310
		if ( false === $file_pointer ) {
311
			return;
312
		}
313
		// phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_read_fclose
314
		fclose( $file_pointer );
315
316
		$theme_data = array(
317
			'name'    => $theme->get( 'Name' ),
318
			'version' => $theme->get( 'Version' ),
319
			'uri'     => $theme->get( 'ThemeURI' ),
320
		);
321
322
		/**
323
		 * This action is documented already in this file.
324
		 */
325
		do_action( 'jetpack_edited_theme', $stylesheet, $theme_data );
326
	}
327
328
	/**
329
	 * Detect a theme deletion.
330
	 *
331
	 * @access public
332
	 */
333
	public function detect_theme_deletion() {
334
		$delete_theme_call = $this->get_delete_theme_call();
335
		if ( empty( $delete_theme_call ) ) {
336
			return;
337
		}
338
339
		$slug       = $delete_theme_call['args'][0];
340
		$theme      = wp_get_theme( $slug );
341
		$theme_data = array(
342
			'name'    => $theme->get( 'Name' ),
343
			'version' => $theme->get( 'Version' ),
344
			'uri'     => $theme->get( 'ThemeURI' ),
345
			'slug'    => $slug,
346
		);
347
348
		/**
349
		 * Signals to the sync listener that a theme was deleted and a sync action
350
		 * reflecting the deletion and theme slug should be sent
351
		 *
352
		 * @since 5.0.0
353
		 *
354
		 * @param string $slug Theme slug
355
		 * @param array $theme_data Theme info Since 5.3
356
		 */
357
		do_action( 'jetpack_deleted_theme', $slug, $theme_data );
358
	}
359
360
	/**
361
	 * Handle an upgrader completion action.
362
	 *
363
	 * @access public
364
	 *
365
	 * @param \WP_Upgrader $upgrader The upgrader instance.
366
	 * @param array        $details  Array of bulk item update data.
367
	 */
368
	public function check_upgrader( $upgrader, $details ) {
369
		if ( ! isset( $details['type'] ) ||
370
			'theme' !== $details['type'] ||
371
			is_wp_error( $upgrader->skin->result ) ||
372
			! method_exists( $upgrader, 'theme_info' )
373
		) {
374
			return;
375
		}
376
377
		if ( 'install' === $details['action'] ) {
378
			$theme = $upgrader->theme_info();
379
			if ( ! $theme instanceof \WP_Theme ) {
0 ignored issues
show
Bug introduced by
The class WP_Theme does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
380
				return;
381
			}
382
			$theme_info = array(
383
				'name'    => $theme->get( 'Name' ),
384
				'version' => $theme->get( 'Version' ),
385
				'uri'     => $theme->get( 'ThemeURI' ),
386
			);
387
388
			/**
389
			 * Signals to the sync listener that a theme was installed and a sync action
390
			 * reflecting the installation and the theme info should be sent
391
			 *
392
			 * @since 4.9.0
393
			 *
394
			 * @param string $theme->theme_root Text domain of the theme
395
			 * @param mixed $theme_info Array of abbreviated theme info
396
			 */
397
			do_action( 'jetpack_installed_theme', $theme->stylesheet, $theme_info );
398
		}
399
400
		if ( 'update' === $details['action'] ) {
401
			$themes = array();
402
403
			if ( empty( $details['themes'] ) && isset( $details['theme'] ) ) {
404
				$details['themes'] = array( $details['theme'] );
405
			}
406
407 View Code Duplication
			foreach ( $details['themes'] as $theme_slug ) {
408
				$theme = wp_get_theme( $theme_slug );
409
410
				if ( ! $theme instanceof \WP_Theme ) {
0 ignored issues
show
Bug introduced by
The class WP_Theme does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
411
					continue;
412
				}
413
414
				$themes[ $theme_slug ] = array(
415
					'name'       => $theme->get( 'Name' ),
416
					'version'    => $theme->get( 'Version' ),
417
					'uri'        => $theme->get( 'ThemeURI' ),
418
					'stylesheet' => $theme->stylesheet,
419
				);
420
			}
421
422
			if ( empty( $themes ) ) {
423
				return;
424
			}
425
426
			/**
427
			 * Signals to the sync listener that one or more themes was updated and a sync action
428
			 * reflecting the update and the theme info should be sent
429
			 *
430
			 * @since 6.2.0
431
			 *
432
			 * @param mixed $themes Array of abbreviated theme info
433
			 */
434
			do_action( 'jetpack_updated_themes', $themes );
435
		}
436
	}
437
438
	/**
439
	 * Initialize themes action listeners for full sync.
440
	 *
441
	 * @access public
442
	 *
443
	 * @param callable $callable Action handler callable.
444
	 */
445
	public function init_full_sync_listeners( $callable ) {
446
		add_action( 'jetpack_full_sync_theme_data', $callable );
447
	}
448
449
	/**
450
	 * Handle a theme switch.
451
	 *
452
	 * @access public
453
	 *
454
	 * @param string    $new_name  Name of the new theme.
455
	 * @param \WP_Theme $new_theme The new theme.
0 ignored issues
show
Documentation introduced by
Should the type for parameter $new_theme not be \WP_Theme|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
456
	 * @param \WP_Theme $old_theme The previous theme.
0 ignored issues
show
Documentation introduced by
Should the type for parameter $old_theme not be \WP_Theme|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
457
	 */
458
	public function sync_theme_support( $new_name, $new_theme = null, $old_theme = null ) {
459
		$previous_theme = $this->get_theme_info( $old_theme );
460
461
		/**
462
		 * Fires when the client needs to sync theme support info
463
		 *
464
		 * @since 4.2.0
465
		 *
466
		 * @param array the theme support array
467
		 * @param array the previous theme since Jetpack 6.5.0
468
		 */
469
		do_action( 'jetpack_sync_current_theme_support', $this->get_theme_info(), $previous_theme );
470
	}
471
472
	/**
473
	 * Enqueue the themes actions for full sync.
474
	 *
475
	 * @access public
476
	 *
477
	 * @param array   $config               Full sync configuration for this sync module.
478
	 * @param int     $max_items_to_enqueue Maximum number of items to enqueue.
479
	 * @param boolean $state                True if full sync has finished enqueueing this module, false otherwise.
480
	 * @return array  Number of actions enqueued, and next module state.
481
	 */
482
	public function enqueue_full_sync_actions( $config, $max_items_to_enqueue, $state ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
483
		/**
484
		 * Tells the client to sync all theme data to the server
485
		 *
486
		 * @since 4.2.0
487
		 *
488
		 * @param boolean Whether to expand theme data (should always be true)
489
		 */
490
		do_action( 'jetpack_full_sync_theme_data', true );
491
492
		// The number of actions enqueued, and next module state (true == done).
493
		return array( 1, true );
494
	}
495
496
	/**
497
	 * Send the themes actions for full sync.
498
	 *
499
	 * @access public
500
	 *
501
	 * @param array $config Full sync configuration for this sync module.
502
	 * @param int   $send_until The timestamp until the current request can send.
503
	 * @param array $state This module Full Sync status.
504
	 *
505
	 * @return array This module Full Sync status.
506
	 */
507
	public function send_full_sync_actions( $config, $send_until, $state ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
508
		// we call this instead of do_action when sending immediately.
509
		$this->send_action( 'jetpack_full_sync_theme_data', array( true ) );
510
511
		// The number of actions enqueued, and next module state (true == done).
512
		return array( 'finished' => true );
513
	}
514
515
	/**
516
	 * Retrieve an estimated number of actions that will be enqueued.
517
	 *
518
	 * @access public
519
	 *
520
	 * @param array $config Full sync configuration for this sync module.
521
	 * @return array Number of items yet to be enqueued.
522
	 */
523
	public function estimate_full_sync_actions( $config ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
524
		return 1;
525
	}
526
527
	/**
528
	 * Initialize the module in the sender.
529
	 *
530
	 * @access public
531
	 */
532
	public function init_before_send() {
533
		add_filter( 'jetpack_sync_before_send_jetpack_full_sync_theme_data', array( $this, 'expand_theme_data' ) );
534
	}
535
536
	/**
537
	 * Retrieve the actions that will be sent for this module during a full sync.
538
	 *
539
	 * @access public
540
	 *
541
	 * @return array Full sync actions of this module.
542
	 */
543
	public function get_full_sync_actions() {
544
		return array( 'jetpack_full_sync_theme_data' );
545
	}
546
547
	/**
548
	 * Expand the theme within a hook before it is serialized and sent to the server.
549
	 *
550
	 * @access public
551
	 *
552
	 * @return array Theme data.
553
	 */
554
	public function expand_theme_data() {
555
		return array( $this->get_theme_info() );
556
	}
557
558
	/**
559
	 * Retrieve the name of the widget by the widget ID.
560
	 *
561
	 * @access public
562
	 * @global $wp_registered_widgets
563
	 *
564
	 * @param string $widget_id Widget ID.
565
	 * @return string Name of the widget, or null if not found.
566
	 */
567
	public function get_widget_name( $widget_id ) {
568
		global $wp_registered_widgets;
569
		return ( isset( $wp_registered_widgets[ $widget_id ] ) ? $wp_registered_widgets[ $widget_id ]['name'] : null );
570
	}
571
572
	/**
573
	 * Retrieve the name of the sidebar by the sidebar ID.
574
	 *
575
	 * @access public
576
	 * @global $wp_registered_sidebars
577
	 *
578
	 * @param string $sidebar_id Sidebar ID.
579
	 * @return string Name of the sidebar, or null if not found.
580
	 */
581
	public function get_sidebar_name( $sidebar_id ) {
582
		global $wp_registered_sidebars;
583
		return ( isset( $wp_registered_sidebars[ $sidebar_id ] ) ? $wp_registered_sidebars[ $sidebar_id ]['name'] : null );
584
	}
585
586
	/**
587
	 * Sync addition of widgets to a sidebar.
588
	 *
589
	 * @access public
590
	 *
591
	 * @param array  $new_widgets New widgets.
592
	 * @param array  $old_widgets Old widgets.
593
	 * @param string $sidebar     Sidebar ID.
594
	 * @return array All widgets that have been moved to the sidebar.
595
	 */
596
	public function sync_add_widgets_to_sidebar( $new_widgets, $old_widgets, $sidebar ) {
597
		$added_widgets = array_diff( $new_widgets, $old_widgets );
598
		if ( empty( $added_widgets ) ) {
599
			return array();
600
		}
601
		$moved_to_sidebar = array();
602
		$sidebar_name     = $this->get_sidebar_name( $sidebar );
603
604
		// Don't sync jetpack_widget_added if theme was switched.
605
		if ( $this->is_theme_switch() ) {
606
			return array();
607
		}
608
609
		foreach ( $added_widgets as $added_widget ) {
610
			$moved_to_sidebar[] = $added_widget;
611
			$added_widget_name  = $this->get_widget_name( $added_widget );
612
			/**
613
			 * Helps Sync log that a widget got added
614
			 *
615
			 * @since 4.9.0
616
			 *
617
			 * @param string $sidebar, Sidebar id got changed
618
			 * @param string $added_widget, Widget id got added
619
			 * @param string $sidebar_name, Sidebar id got changed Since 5.0.0
620
			 * @param string $added_widget_name, Widget id got added Since 5.0.0
621
			 */
622
			do_action( 'jetpack_widget_added', $sidebar, $added_widget, $sidebar_name, $added_widget_name );
623
		}
624
		return $moved_to_sidebar;
625
	}
626
627
	/**
628
	 * Sync removal of widgets from a sidebar.
629
	 *
630
	 * @access public
631
	 *
632
	 * @param array  $new_widgets      New widgets.
633
	 * @param array  $old_widgets      Old widgets.
634
	 * @param string $sidebar          Sidebar ID.
635
	 * @param array  $inactive_widgets Current inactive widgets.
636
	 * @return array All widgets that have been moved to inactive.
637
	 */
638
	public function sync_remove_widgets_from_sidebar( $new_widgets, $old_widgets, $sidebar, $inactive_widgets ) {
639
		$removed_widgets = array_diff( $old_widgets, $new_widgets );
640
641
		if ( empty( $removed_widgets ) ) {
642
			return array();
643
		}
644
645
		$moved_to_inactive = array();
646
		$sidebar_name      = $this->get_sidebar_name( $sidebar );
647
648
		foreach ( $removed_widgets as $removed_widget ) {
649
			// Lets check if we didn't move the widget to in_active_widgets.
650
			if ( isset( $inactive_widgets ) && ! in_array( $removed_widget, $inactive_widgets, true ) ) {
651
				$removed_widget_name = $this->get_widget_name( $removed_widget );
652
				/**
653
				 * Helps Sync log that a widgte got removed
654
				 *
655
				 * @since 4.9.0
656
				 *
657
				 * @param string $sidebar, Sidebar id got changed
658
				 * @param string $removed_widget, Widget id got removed
659
				 * @param string $sidebar_name, Name of the sidebar that changed  Since 5.0.0
660
				 * @param string $removed_widget_name, Name of the widget that got removed Since 5.0.0
661
				 */
662
				do_action( 'jetpack_widget_removed', $sidebar, $removed_widget, $sidebar_name, $removed_widget_name );
663
			} else {
664
				$moved_to_inactive[] = $removed_widget;
665
			}
666
		}
667
		return $moved_to_inactive;
668
669
	}
670
671
	/**
672
	 * Sync a reorder of widgets within a sidebar.
673
	 *
674
	 * @access public
675
	 *
676
	 * @todo Refactor serialize() to a json_encode().
677
	 *
678
	 * @param array  $new_widgets New widgets.
679
	 * @param array  $old_widgets Old widgets.
680
	 * @param string $sidebar     Sidebar ID.
681
	 */
682
	public function sync_widgets_reordered( $new_widgets, $old_widgets, $sidebar ) {
683
		$added_widgets = array_diff( $new_widgets, $old_widgets );
684
		if ( ! empty( $added_widgets ) ) {
685
			return;
686
		}
687
		$removed_widgets = array_diff( $old_widgets, $new_widgets );
688
		if ( ! empty( $removed_widgets ) ) {
689
			return;
690
		}
691
692
		// phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.serialize_serialize
693
		if ( serialize( $old_widgets ) !== serialize( $new_widgets ) ) {
694
			$sidebar_name = $this->get_sidebar_name( $sidebar );
695
			/**
696
			 * Helps Sync log that a sidebar id got reordered
697
			 *
698
			 * @since 4.9.0
699
			 *
700
			 * @param string $sidebar, Sidebar id got changed
701
			 * @param string $sidebar_name, Name of the sidebar that changed  Since 5.0.0
702
			 */
703
			do_action( 'jetpack_widget_reordered', $sidebar, $sidebar_name );
704
		}
705
706
	}
707
708
	/**
709
	 * Handle the update of the sidebars and widgets mapping option.
710
	 *
711
	 * @access public
712
	 *
713
	 * @param mixed $old_value The old option value.
714
	 * @param mixed $new_value The new option value.
715
	 */
716
	public function sync_sidebar_widgets_actions( $old_value, $new_value ) {
717
		// Don't really know how to deal with different array_values yet.
718
		if (
719
			( isset( $old_value['array_version'] ) && 3 !== $old_value['array_version'] ) ||
720
			( isset( $new_value['array_version'] ) && 3 !== $new_value['array_version'] )
721
		) {
722
			return;
723
		}
724
725
		$moved_to_inactive_ids = array();
726
		$moved_to_sidebar      = array();
727
728
		foreach ( $new_value as $sidebar => $new_widgets ) {
729
			if ( in_array( $sidebar, array( 'array_version', 'wp_inactive_widgets' ), true ) ) {
730
				continue;
731
			}
732
			$old_widgets = isset( $old_value[ $sidebar ] )
733
				? $old_value[ $sidebar ]
734
				: array();
735
736
			if ( ! is_array( $new_widgets ) ) {
737
				$new_widgets = array();
738
			}
739
740
			$moved_to_inactive_recently = $this->sync_remove_widgets_from_sidebar( $new_widgets, $old_widgets, $sidebar, $new_value['wp_inactive_widgets'] );
741
			$moved_to_inactive_ids      = array_merge( $moved_to_inactive_ids, $moved_to_inactive_recently );
742
743
			$moved_to_sidebar_recently = $this->sync_add_widgets_to_sidebar( $new_widgets, $old_widgets, $sidebar );
744
			$moved_to_sidebar          = array_merge( $moved_to_sidebar, $moved_to_sidebar_recently );
745
746
			$this->sync_widgets_reordered( $new_widgets, $old_widgets, $sidebar );
747
748
		}
749
750
		// Don't sync either jetpack_widget_moved_to_inactive or jetpack_cleared_inactive_widgets if theme was switched.
751
		if ( $this->is_theme_switch() ) {
752
			return;
753
		}
754
755
		// Treat inactive sidebar a bit differently.
756
		if ( ! empty( $moved_to_inactive_ids ) ) {
757
			$moved_to_inactive_name = array_map( array( $this, 'get_widget_name' ), $moved_to_inactive_ids );
758
			/**
759
			 * Helps Sync log that a widgets IDs got moved to in active
760
			 *
761
			 * @since 4.9.0
762
			 *
763
			 * @param array $moved_to_inactive_ids, Array of widgets id that moved to inactive id got changed
764
			 * @param array $moved_to_inactive_names, Array of widgets names that moved to inactive id got changed Since 5.0.0
765
			 */
766
			do_action( 'jetpack_widget_moved_to_inactive', $moved_to_inactive_ids, $moved_to_inactive_name );
767
		} elseif ( empty( $moved_to_sidebar ) && empty( $new_value['wp_inactive_widgets'] ) && ! empty( $old_value['wp_inactive_widgets'] ) ) {
768
			/**
769
			 * Helps Sync log that a got cleared from inactive.
770
			 *
771
			 * @since 4.9.0
772
			 */
773
			do_action( 'jetpack_cleared_inactive_widgets' );
774
		}
775
	}
776
777
	/**
778
	 * Retrieve the theme data for the current or a specific theme.
779
	 *
780
	 * @access private
781
	 *
782
	 * @param \WP_Theme $theme Theme object. Optional, will default to the current theme.
0 ignored issues
show
Documentation introduced by
Should the type for parameter $theme not be \WP_Theme|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
783
	 *
784
	 * @return array Theme data.
785
	 */
786
	private function get_theme_info( $theme = null ) {
787
		$theme_support = array();
788
789
		// We are trying to get the current theme info.
790
		if ( null === $theme ) {
791
			$theme = wp_get_theme();
792
		}
793
794
		$theme_support['name']    = $theme->get( 'Name' );
795
		$theme_support['version'] = $theme->get( 'Version' );
796
		$theme_support['slug']    = $theme->get_stylesheet();
797
		$theme_support['uri']     = $theme->get( 'ThemeURI' );
798
799
		return $theme_support;
800
	}
801
802
	/**
803
	 * Whether we've deleted a theme in the current request.
804
	 *
805
	 * @access private
806
	 *
807
	 * @return boolean True if this is a theme deletion request, false otherwise.
808
	 */
809
	private function get_delete_theme_call() {
810
		// Intentional usage of `debug_backtrace()` for production needs.
811
		// phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_debug_backtrace
812
		$backtrace         = debug_backtrace();
813
		$delete_theme_call = null;
814
		foreach ( $backtrace as $call ) {
815
			if ( isset( $call['function'] ) && 'delete_theme' === $call['function'] ) {
816
				$delete_theme_call = $call;
817
				break;
818
			}
819
		}
820
		return $delete_theme_call;
821
	}
822
823
	/**
824
	 * Whether we've switched to another theme in the current request.
825
	 *
826
	 * @access private
827
	 *
828
	 * @return boolean True if this is a theme switch request, false otherwise.
829
	 */
830
	private function is_theme_switch() {
831
		return did_action( 'after_switch_theme' );
832
	}
833
834
	/**
835
	 * Return Total number of objects.
836
	 *
837
	 * @param array $config Full Sync config.
838
	 *
839
	 * @return int total
840
	 */
841
	public function total( $config ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
842
		return 1;
843
	}
844
845
	/**
846
	 * Retrieve a set of constants by their IDs.
847
	 *
848
	 * @access public
849
	 *
850
	 * @param string $object_type Object type.
851
	 * @param array  $ids         Object IDs.
852
	 * @return array Array of objects.
853
	 */
854
	public function get_objects_by_id( $object_type, $ids ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
855
		if ( 'theme-info' !== $object_type ) {
856
			return array();
857
		}
858
859
		return array( $this->get_theme_info() );
860
	}
861
862
}
863