Completed
Push — branch-5.2 ( 0e7d1b...f6ff92 )
by
unknown
09:28
created

Jetpack_Sync_Module_Themes::is_theme_switch()   A

Complexity

Conditions 4
Paths 3

Size

Total Lines 9
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 4
eloc 6
nc 3
nop 0
dl 0
loc 9
rs 9.2
c 0
b 0
f 0
1
<?php
2
3
class Jetpack_Sync_Module_Themes extends Jetpack_Sync_Module {
4
	function name() {
5
		return 'themes';
6
	}
7
8
	public function init_listeners( $callable ) {
9
		add_action( 'switch_theme', array( $this, 'sync_theme_support' ) );
10
		add_action( 'jetpack_sync_current_theme_support', $callable );
11
		add_action( 'upgrader_process_complete', array( $this, 'check_upgrader'), 10, 2 );
12
		add_action( 'jetpack_installed_theme', $callable, 10, 2 );
13
		add_action( 'jetpack_updated_theme', $callable, 10, 2 );
14
		add_action( 'delete_site_transient_update_themes', array( $this, 'detect_theme_deletion') );
15
		add_action( 'jetpack_deleted_theme', $callable, 10, 2 );
16
		add_filter( 'wp_redirect', array( $this, 'detect_theme_edit' ) );
17
		add_action( 'jetpack_edited_theme', $callable, 10, 2 );
18
		add_action( 'update_site_option_allowedthemes', array( $this, 'sync_network_allowed_themes_change' ), 10, 4 );
19
		add_action( 'jetpack_network_disabled_themes', $callable, 10, 2 );
20
		add_action( 'jetpack_network_enabled_themes', $callable, 10, 2 );
21
22
		// Sidebar updates.
23
		add_action( 'update_option_sidebars_widgets', array( $this, 'sync_sidebar_widgets_actions' ), 10, 2 );
24
25
		add_action( 'jetpack_widget_added', $callable, 10, 4 );
26
		add_action( 'jetpack_widget_removed', $callable, 10, 4 );
27
		add_action( 'jetpack_widget_moved_to_inactive', $callable, 10, 2 );
28
		add_action( 'jetpack_cleared_inactive_widgets', $callable );
29
		add_action( 'jetpack_widget_reordered', $callable, 10, 2 );
30
		add_filter( 'widget_update_callback', array( $this, 'sync_widget_edit' ), 10, 4 );
31
		add_action( 'jetpack_widget_edited', $callable );
32
	}
33
34
	public function sync_widget_edit( $instance, $new_instance, $old_instance, $widget_object ) {
35
		if ( empty( $old_instance ) ) {
36
			return;
37
		}
38
39
		$widget = array(
40
			'name' => $widget_object->name,
41
			'id' => $widget_object->id,
42
		);
43
		/**
44
		 * Trigger action to alert $callable sync listener that a widget was edited
45
		 *
46
		 * @since 5.0.0
47
		 *
48
		 * @param string $widget_name , Name of edited widget
49
		 */
50
		do_action( 'jetpack_widget_edited', $widget );
51
52
		return $instance;
53
	}
54
55
	public function sync_network_allowed_themes_change( $option, $value, $old_value, $network_id ) {
56
		$all_enabled_theme_slugs = array_keys( $value );
57
58
		if ( count( $old_value ) > count( $value ) )  {
59
60
			//Suppress jetpack_network_disabled_themes sync action when theme is deleted
61
			$delete_theme_call = $this->get_delete_theme_call();
62
			if ( ! empty( $delete_theme_call ) ) {
63
				return;
64
			}
65
66
			$newly_disabled_theme_names = array_keys( array_diff_key( $old_value, $value ) );
67
			$newly_disabled_themes = $this->get_theme_details_for_slugs( $newly_disabled_theme_names );
68
			/**
69
			 * Trigger action to alert $callable sync listener that network themes were disabled
70
			 *
71
			 * @since 5.0.0
72
			 *
73
			 * @param mixed $newly_disabled_themes, Array of info about network disabled themes
74
			 * @param mixed $all_enabled_theme_slugs, Array of slugs of all enabled themes
75
			 */
76
			do_action( 'jetpack_network_disabled_themes', $newly_disabled_themes, $all_enabled_theme_slugs );
77
			return;
78
		}
79
80
		$newly_enabled_theme_names = array_keys( array_diff_key( $value, $old_value ) );
81
		$newly_enabled_themes = $this->get_theme_details_for_slugs( $newly_enabled_theme_names );
82
		/**
83
		 * Trigger action to alert $callable sync listener that network themes were enabled
84
		 *
85
		 * @since 5.0.0
86
		 *
87
		 * @param mixed $newly_enabled_themes , Array of info about network enabled themes
88
		 * @param mixed $all_enabled_theme_slugs, Array of slugs of all enabled themes
89
		 */
90
		do_action( 'jetpack_network_enabled_themes', $newly_enabled_themes, $all_enabled_theme_slugs );
91
	}
92
93
	private function get_theme_details_for_slugs( $theme_slugs ) {
94
		$theme_data = array();
95
		foreach ( $theme_slugs as $slug ) {
96
			$theme = wp_get_theme( $slug );
97
			$theme_data[ $slug ] = array(
98
				'name' => $theme->get( 'Name' ),
99
				'version' => $theme->get( 'Version' ),
100
				'uri' => $theme->get( 'ThemeURI' ),
101
				'slug' => $slug,
102
			);
103
		}
104
		return $theme_data;
105
	}
106
107
	public function detect_theme_edit( $redirect_url ) {
108
		$url = wp_parse_url( admin_url( $redirect_url ) );
109
		$theme_editor_url = wp_parse_url( admin_url( 'theme-editor.php' ) );
110
111
		if ( $theme_editor_url['path'] !== $url['path'] ) {
112
			return $redirect_url;
113
		}
114
115
		$query_params = array();
116
		wp_parse_str( $url['query'], $query_params );
117
		if (
118
			! isset( $_POST['newcontent'] ) ||
119
			! isset( $query_params['file'] ) ||
120
			! isset( $query_params['theme'] ) ||
121
			! isset( $query_params['updated'] )
122
		) {
123
			return $redirect_url;
124
		}
125
		$theme = wp_get_theme( $query_params['theme'] );
126
		$theme_data = array(
127
			'name' => $theme->get('Name'),
128
			'version' => $theme->get('Version'),
129
			'uri' => $theme->get( 'ThemeURI' ),
130
		);
131
132
		/**
133
		 * Trigger action to alert $callable sync listener that a theme was edited
134
		 *
135
		 * @since 5.0.0
136
		 *
137
		 * @param string $query_params['theme'], Slug of edited theme
138
		 * @param string $theme_data, Information about edited them
139
		 */
140
		do_action( 'jetpack_edited_theme', $query_params['theme'], $theme_data );
141
142
		return $redirect_url;
143
	}
144
145
	public function detect_theme_deletion() {
146
		$delete_theme_call = $this->get_delete_theme_call();
147
		if ( empty( $delete_theme_call ) ) {
148
			return;
149
		}
150
151
		$slug = $delete_theme_call['args'][0];
152
		$theme = wp_get_theme( $slug );
153
		$theme_data = array(
154
			'name' => $theme->get('Name'),
155
			'version' => $theme->get('Version'),
156
			'uri' => $theme->get( 'ThemeURI' ),
157
			'slug' => $slug,
158
		);
159
160
		/**
161
		 * Signals to the sync listener that a theme was deleted and a sync action
162
		 * reflecting the deletion and theme slug should be sent
163
		 *
164
		 * @since 5.0.0
165
		 *
166
		 * @param string $slug Theme slug
167
		 * @param array $theme_data Theme info Since 5.3
168
		 */
169
		do_action( 'jetpack_deleted_theme', $slug, $theme_data );
170
	}
171
172
	public function check_upgrader( $upgrader, $details) {
173 View Code Duplication
		if ( ! isset( $details['type'] ) ||
174
			'theme' !== $details['type'] ||
175
			is_wp_error( $upgrader->skin->result ) ||
176
			! method_exists( $upgrader, 'theme_info' )
177
		) {
178
			return;
179
		}
180
181
		$theme = $upgrader->theme_info();
182
		$theme_info = array(
183
			'name' => $theme->get( 'Name' ),
184
			'version' => $theme->get( 'Version' ),
185
			'uri' => $theme->get( 'ThemeURI' ),
186
		);
187
188
		if ( 'install' === $details['action'] ) {
189
			/**
190
			 * Signals to the sync listener that a theme was installed and a sync action
191
			 * reflecting the installation and the theme info should be sent
192
			 *
193
			 * @since 4.9.0
194
			 *
195
			 * @param string $theme->theme_root Text domain of the theme
196
			 * @param mixed $theme_info Array of abbreviated theme info
197
			 */
198
			do_action( 'jetpack_installed_theme', $theme->stylesheet, $theme_info );
199
		}
200
201
		if ( 'update' === $details['action'] ) {
202
			/**
203
			 * Signals to the sync listener that a theme was updated and a sync action
204
			 * reflecting the update and the theme info should be sent
205
			 *
206
			 * @since 4.9.0
207
			 *
208
			 * @param string $theme->theme_root Text domain of the theme
209
			 * @param mixed $theme_info Array of abbreviated theme info
210
			 */
211
			do_action( 'jetpack_updated_theme', $theme->stylesheet, $theme_info );
212
		}
213
	}
214
215
	public function init_full_sync_listeners( $callable ) {
216
		add_action( 'jetpack_full_sync_theme_data', $callable );
217
	}
218
219
	public function sync_theme_support() {
220
		/**
221
		 * Fires when the client needs to sync theme support info
222
		 * Only sends theme support attributes whitelisted in Jetpack_Sync_Defaults::$default_theme_support_whitelist
223
		 *
224
		 * @since 4.2.0
225
		 *
226
		 * @param object the theme support hash
227
		 */
228
		do_action( 'jetpack_sync_current_theme_support' , $this->get_theme_support_info() );
229
	}
230
231
	public function enqueue_full_sync_actions( $config, $max_items_to_enqueue, $state ) {
232
		/**
233
		 * Tells the client to sync all theme data to the server
234
		 *
235
		 * @since 4.2.0
236
		 *
237
		 * @param boolean Whether to expand theme data (should always be true)
238
		 */
239
		do_action( 'jetpack_full_sync_theme_data', true );
240
241
		// The number of actions enqueued, and next module state (true == done)
242
		return array( 1, true );
243
	}
244
245
	public function estimate_full_sync_actions( $config ) {
246
		return 1;
247
	}
248
	
249
	public function init_before_send() {
250
		add_filter( 'jetpack_sync_before_send_jetpack_full_sync_theme_data', array( $this, 'expand_theme_data' ) );
251
	}
252
253
	function get_full_sync_actions() {
254
		return array( 'jetpack_full_sync_theme_data' );
255
	}
256
257
	function expand_theme_data() {
258
		return array( $this->get_theme_support_info() );
259
	}
260
261
	function get_widget_name( $widget_id ) {
262
		global $wp_registered_widgets;
263
		return ( isset( $wp_registered_widgets[ $widget_id ] ) ? $wp_registered_widgets[ $widget_id ]['name'] : null );
264
	}
265
266
	function get_sidebar_name( $sidebar_id ) {
267
		global $wp_registered_sidebars;
268
		return ( isset( $wp_registered_sidebars[ $sidebar_id ] ) ? $wp_registered_sidebars[ $sidebar_id ]['name'] : null );
269
	}
270
271
	function sync_add_widgets_to_sidebar( $new_widgets, $old_widgets, $sidebar ) {
272
		$added_widgets = array_diff( $new_widgets, $old_widgets );
273
		if ( empty( $added_widgets ) ) {
274
			return array();
275
		}
276
		$moved_to_sidebar = array();
277
		$sidebar_name = $this->get_sidebar_name( $sidebar );
278
279
		//Don't sync jetpack_widget_added if theme was switched
280
		if ( $this->is_theme_switch() ) {
281
			return array();
282
		}
283
284
		foreach ( $added_widgets as $added_widget ) {
285
			$moved_to_sidebar[] = $added_widget;
286
			$added_widget_name = $this->get_widget_name( $added_widget );
287
			/**
288
			 * Helps Sync log that a widget got added
289
			 *
290
			 * @since 4.9.0
291
			 *
292
			 * @param string $sidebar, Sidebar id got changed
293
			 * @param string $added_widget, Widget id got added
294
			 * @param string $sidebar_name, Sidebar id got changed Since 5.0.0
295
			 * @param string $added_widget_name, Widget id got added Since 5.0.0
296
			 *
297
			 */
298
			do_action( 'jetpack_widget_added', $sidebar, $added_widget,  $sidebar_name, $added_widget_name );
299
		}
300
		return $moved_to_sidebar;
301
	}
302
303
	function sync_remove_widgets_from_sidebar( $new_widgets, $old_widgets, $sidebar, $inactive_widgets  ) {
304
		$removed_widgets = array_diff( $old_widgets, $new_widgets );
305
306
		if ( empty( $removed_widgets ) ) {
307
			return array();
308
		}
309
310
		$moved_to_inactive = array();
311
		$sidebar_name = $this->get_sidebar_name( $sidebar );
312
313
		foreach( $removed_widgets as $removed_widget ) {
314
			// Lets check if we didn't move the widget to in_active_widgets
315
			if ( isset( $inactive_widgets ) && ! in_array( $removed_widget, $inactive_widgets ) ) {
316
				$removed_widget_name = $this->get_widget_name( $removed_widget );
317
				/**
318
				 * Helps Sync log that a widgte got removed
319
				 *
320
				 * @since 4.9.0
321
				 *
322
				 * @param string $sidebar, Sidebar id got changed
323
				 * @param string $removed_widget, Widget id got removed
324
				 * @param string $sidebar_name, Name of the sidebar that changed  Since 5.0.0
325
				 * @param string $removed_widget_name, Name of the widget that got removed Since 5.0.0
326
				 */
327
				do_action( 'jetpack_widget_removed', $sidebar, $removed_widget, $sidebar_name, $removed_widget_name );
328
			} else {
329
				$moved_to_inactive[] = $removed_widget;
330
			}
331
		}
332
		return $moved_to_inactive;
333
334
	}
335
336
	function sync_widgets_reordered( $new_widgets, $old_widgets, $sidebar ) {
337
		$added_widgets = array_diff( $new_widgets, $old_widgets );
338
		if ( ! empty( $added_widgets ) ) {
339
			return;
340
		}
341
		$removed_widgets = array_diff( $old_widgets, $new_widgets );
342
		if ( ! empty( $removed_widgets ) ) {
343
			return;
344
		}
345
346
		if ( serialize( $old_widgets ) !== serialize( $new_widgets ) ) {
347
			$sidebar_name = $this->get_sidebar_name( $sidebar );
348
			/**
349
			 * Helps Sync log that a sidebar id got reordered
350
			 *
351
			 * @since 4.9.0
352
			 *
353
			 * @param string $sidebar, Sidebar id got changed
354
			 * @param string $sidebar_name, Name of the sidebar that changed  Since 5.0.0
355
			 */
356
			do_action( 'jetpack_widget_reordered', $sidebar, $sidebar_name );
357
		}
358
359
	}
360
361
	function sync_sidebar_widgets_actions( $old_value, $new_value ) {
362
		// Don't really know how to deal with different array_values yet.
363
		if ( $old_value['array_version'] !== 3 || $new_value['array_version'] !== 3 ) {
364
			return;
365
		}
366
367
		$moved_to_inactive_ids = array();
368
		$moved_to_sidebar = array();
369
370
		foreach ( $new_value as $sidebar => $new_widgets ) {
371
			if ( in_array( $sidebar, array( 'array_version', 'wp_inactive_widgets' ) ) ) {
372
				continue;
373
			}
374
			$old_widgets = isset( $old_value[ $sidebar ] )
375
				? $old_value[ $sidebar ]
376
				: array();
377
378
			if ( ! is_array( $new_widgets ) ) {
379
				$new_widgets = array();
380
			}
381
382
			$moved_to_inactive_recently = $this->sync_remove_widgets_from_sidebar( $new_widgets, $old_widgets, $sidebar, $new_value['wp_inactive_widgets'] );
383
			$moved_to_inactive_ids = array_merge( $moved_to_inactive_ids, $moved_to_inactive_recently );
384
385
			$moved_to_sidebar_recently = $this->sync_add_widgets_to_sidebar( $new_widgets, $old_widgets, $sidebar );
386
			$moved_to_sidebar = array_merge( $moved_to_sidebar, $moved_to_sidebar_recently );
387
388
			$this->sync_widgets_reordered( $new_widgets, $old_widgets, $sidebar );
389
390
		}
391
392
		//Don't sync either jetpack_widget_moved_to_inactive or jetpack_cleared_inactive_widgets if theme was switched
393
		if ( $this->is_theme_switch() ) {
394
			return;
395
		}
396
397
		// Treat inactive sidebar a bit differently
398
		if ( ! empty( $moved_to_inactive_ids ) ) {
399
			$moved_to_inactive_name = array_map( array( $this, 'get_widget_name' ), $moved_to_inactive_ids );
400
			/**
401
			 * Helps Sync log that a widgets IDs got moved to in active
402
			 *
403
			 * @since 4.9.0
404
			 *
405
			 * @param array $moved_to_inactive_ids, Array of widgets id that moved to inactive id got changed
406
			 * @param array $moved_to_inactive_names, Array of widgets names that moved to inactive id got changed Since 5.0.0
407
			 */
408
			do_action( 'jetpack_widget_moved_to_inactive', $moved_to_inactive_ids, $moved_to_inactive_name );
409
		} elseif ( empty( $moved_to_sidebar ) &&
410
		           empty( $new_value['wp_inactive_widgets']) &&
411
		           ! empty( $old_value['wp_inactive_widgets'] ) ) {
412
			/**
413
			 * Helps Sync log that a got cleared from inactive.
414
			 *
415
			 * @since 4.9.0
416
			 */
417
			do_action( 'jetpack_cleared_inactive_widgets' );
418
		} 
419
	}
420
421
	private function get_theme_support_info() {
422
		global $_wp_theme_features;
423
424
		$theme_support = array();
425
426
		foreach ( Jetpack_Sync_Defaults::$default_theme_support_whitelist as $theme_feature ) {
0 ignored issues
show
Bug introduced by
The property default_theme_support_whitelist cannot be accessed from this context as it is declared private in class Jetpack_Sync_Defaults.

This check looks for access to properties that are not accessible from the current context.

If you need to make a property accessible to another context you can either raise its visibility level or provide an accessible getter in the defining class.

Loading history...
427
			$has_support = current_theme_supports( $theme_feature );
428
			if ( $has_support ) {
429
				$theme_support[ $theme_feature ] = $_wp_theme_features[ $theme_feature ];
430
			}
431
		}
432
433
		$theme = wp_get_theme();
434
		$theme_support['name'] = $theme->get('Name');
435
		$theme_support['version'] =  $theme->get('Version');
436
		$theme_support['slug'] = $theme->get_stylesheet();
437
		$theme_support['uri'] = $theme->get('ThemeURI');
438
439
440
		return $theme_support;
441
	}
442
443
	private function get_delete_theme_call() {
444
		$backtrace = debug_backtrace();
445
		$delete_theme_call = null;
446
		foreach ( $backtrace as $call ) {
447
			if ( isset( $call['function'] ) && 'delete_theme' === $call['function'] ) {
448
				$delete_theme_call = $call;
449
				break;
450
			}
451
		}
452
		return $delete_theme_call;
453
	}
454
455
	private function is_theme_switch() {
456
		$backtrace = debug_backtrace();
457
		foreach ( $backtrace as $call ) {
458
			if ( isset( $call['args'][0] ) && 'after_switch_theme' === $call['args'][0] ) {
459
				return true;
460
			}
461
		}
462
		return false;
463
	}
464
}
465