Completed
Push — try/woocommerce-analytics ( 2b8c13...0a25c9 )
by
unknown
32:37 queued 22:26
created

Jetpack_Sync_Module_Themes::theme_edit_ajax()   D

Complexity

Conditions 21
Paths 46

Size

Total Lines 104
Code Lines 60

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 21
eloc 60
nc 46
nop 0
dl 0
loc 104
rs 4.6955
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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( 'wp_ajax_edit-theme-plugin-file', array( $this, 'theme_edit_ajax' ), 0 );
19
		add_action( 'update_site_option_allowedthemes', array( $this, 'sync_network_allowed_themes_change' ), 10, 4 );
20
		add_action( 'jetpack_network_disabled_themes', $callable, 10, 2 );
21
		add_action( 'jetpack_network_enabled_themes', $callable, 10, 2 );
22
23
		// Sidebar updates.
24
		add_action( 'update_option_sidebars_widgets', array( $this, 'sync_sidebar_widgets_actions' ), 10, 2 );
25
26
		add_action( 'jetpack_widget_added', $callable, 10, 4 );
27
		add_action( 'jetpack_widget_removed', $callable, 10, 4 );
28
		add_action( 'jetpack_widget_moved_to_inactive', $callable, 10, 2 );
29
		add_action( 'jetpack_cleared_inactive_widgets', $callable );
30
		add_action( 'jetpack_widget_reordered', $callable, 10, 2 );
31
		add_filter( 'widget_update_callback', array( $this, 'sync_widget_edit' ), 10, 4 );
32
		add_action( 'jetpack_widget_edited', $callable );
33
	}
34
35
	public function sync_widget_edit( $instance, $new_instance, $old_instance, $widget_object ) {
36
		if ( empty( $old_instance ) ) {
37
			return $instance;
38
		}
39
40
		$widget = array(
41
			'name' => $widget_object->name,
42
			'id' => $widget_object->id,
43
			'title' => isset( $new_instance['title'] ) ? $new_instance['title'] : '',
44
		);
45
		/**
46
		 * Trigger action to alert $callable sync listener that a widget was edited
47
		 *
48
		 * @since 5.0.0
49
		 *
50
		 * @param string $widget_name , Name of edited widget
51
		 */
52
		do_action( 'jetpack_widget_edited', $widget );
53
54
		return $instance;
55
	}
56
57
	public function sync_network_allowed_themes_change( $option, $value, $old_value, $network_id ) {
58
		$all_enabled_theme_slugs = array_keys( $value );
59
60
		if ( count( $old_value ) > count( $value ) )  {
61
62
			//Suppress jetpack_network_disabled_themes sync action when theme is deleted
63
			$delete_theme_call = $this->get_delete_theme_call();
64
			if ( ! empty( $delete_theme_call ) ) {
65
				return;
66
			}
67
68
			$newly_disabled_theme_names = array_keys( array_diff_key( $old_value, $value ) );
69
			$newly_disabled_themes = $this->get_theme_details_for_slugs( $newly_disabled_theme_names );
70
			/**
71
			 * Trigger action to alert $callable sync listener that network themes were disabled
72
			 *
73
			 * @since 5.0.0
74
			 *
75
			 * @param mixed $newly_disabled_themes, Array of info about network disabled themes
76
			 * @param mixed $all_enabled_theme_slugs, Array of slugs of all enabled themes
77
			 */
78
			do_action( 'jetpack_network_disabled_themes', $newly_disabled_themes, $all_enabled_theme_slugs );
79
			return;
80
		}
81
82
		$newly_enabled_theme_names = array_keys( array_diff_key( $value, $old_value ) );
83
		$newly_enabled_themes = $this->get_theme_details_for_slugs( $newly_enabled_theme_names );
84
		/**
85
		 * Trigger action to alert $callable sync listener that network themes were enabled
86
		 *
87
		 * @since 5.0.0
88
		 *
89
		 * @param mixed $newly_enabled_themes , Array of info about network enabled themes
90
		 * @param mixed $all_enabled_theme_slugs, Array of slugs of all enabled themes
91
		 */
92
		do_action( 'jetpack_network_enabled_themes', $newly_enabled_themes, $all_enabled_theme_slugs );
93
	}
94
95
	private function get_theme_details_for_slugs( $theme_slugs ) {
96
		$theme_data = array();
97
		foreach ( $theme_slugs as $slug ) {
98
			$theme = wp_get_theme( $slug );
99
			$theme_data[ $slug ] = array(
100
				'name' => $theme->get( 'Name' ),
101
				'version' => $theme->get( 'Version' ),
102
				'uri' => $theme->get( 'ThemeURI' ),
103
				'slug' => $slug,
104
			);
105
		}
106
		return $theme_data;
107
	}
108
109
	public function detect_theme_edit( $redirect_url ) {
110
		$url = wp_parse_url( admin_url( $redirect_url ) );
111
		$theme_editor_url = wp_parse_url( admin_url( 'theme-editor.php' ) );
112
113
		if ( $theme_editor_url['path'] !== $url['path'] ) {
114
			return $redirect_url;
115
		}
116
117
		$query_params = array();
118
		wp_parse_str( $url['query'], $query_params );
119
		if (
120
			! isset( $_POST['newcontent'] ) ||
121
			! isset( $query_params['file'] ) ||
122
			! isset( $query_params['theme'] ) ||
123
			! isset( $query_params['updated'] )
124
		) {
125
			return $redirect_url;
126
		}
127
		$theme = wp_get_theme( $query_params['theme'] );
128
		$theme_data = array(
129
			'name' => $theme->get('Name'),
130
			'version' => $theme->get('Version'),
131
			'uri' => $theme->get( 'ThemeURI' ),
132
		);
133
134
		/**
135
		 * Trigger action to alert $callable sync listener that a theme was edited
136
		 *
137
		 * @since 5.0.0
138
		 *
139
		 * @param string $query_params['theme'], Slug of edited theme
140
		 * @param string $theme_data, Information about edited them
141
		 */
142
		do_action( 'jetpack_edited_theme', $query_params['theme'], $theme_data );
143
144
		return $redirect_url;
145
	}
146
147
	public function theme_edit_ajax() {
148
		$args = wp_unslash( $_POST );
149
150
		if ( empty( $args['theme'] ) ) {
151
			return;
152
		}
153
154
		if ( empty( $args['file'] ) ) {
155
			return;
156
		}
157
		$file = $args['file'];
158
		if ( 0 !== validate_file( $file ) ) {
159
			return;
160
		}
161
162
		if ( ! isset( $args['newcontent'] ) ) {
163
			return;
164
		}
165
166
		if ( ! isset( $args['nonce'] ) ) {
167
			return;
168
		}
169
170
		$stylesheet = $args['theme'];
171
		if ( 0 !== validate_file( $stylesheet ) ) {
172
			return;
173
		}
174
175
		if ( ! current_user_can( 'edit_themes' ) ) {
176
			return;
177
		}
178
179
		$theme = wp_get_theme( $stylesheet );
180
		if ( ! $theme->exists() ) {
181
			return;
182
		}
183
184
		$real_file = $theme->get_stylesheet_directory() . '/' . $file;
185
		if ( ! wp_verify_nonce( $args['nonce'], 'edit-theme_' . $real_file . $stylesheet ) ) {
186
			return;
187
		}
188
189
		if ( $theme->errors() && 'theme_no_stylesheet' === $theme->errors()->get_error_code() ) {
190
			return;
191
		}
192
193
		$editable_extensions = wp_get_theme_file_editable_extensions( $theme );
194
195
		$allowed_files = array();
196
		foreach ( $editable_extensions as $type ) {
197
			switch ( $type ) {
198
				case 'php':
199
					$allowed_files = array_merge( $allowed_files, $theme->get_files( 'php', -1 ) );
200
					break;
201
				case 'css':
202
					$style_files = $theme->get_files( 'css', -1 );
203
					$allowed_files['style.css'] = $style_files['style.css'];
204
					$allowed_files = array_merge( $allowed_files, $style_files );
205
					break;
206
				default:
207
					$allowed_files = array_merge( $allowed_files, $theme->get_files( $type, -1 ) );
208
					break;
209
			}
210
		}
211
212
		if ( 0 !== validate_file( $real_file, $allowed_files ) ) {
213
			return;
214
		}
215
216
		// Ensure file is real.
217
		if ( ! is_file( $real_file ) ) {
218
			return;
219
		}
220
221
		// Ensure file extension is allowed.
222
		$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...
223
		if ( preg_match( '/\.([^.]+)$/', $real_file, $matches ) ) {
224
			$extension = strtolower( $matches[1] );
225
			if ( ! in_array( $extension, $editable_extensions, true ) ) {
226
				return;
227
			}
228
		}
229
230
		if ( ! is_writeable( $real_file ) ) {
231
			return;
232
		}
233
234
		$file_pointer = fopen( $real_file, 'w+' );
235
		if ( false === $file_pointer ) {
236
			return;
237
		}
238
239
		$theme_data = array(
240
			'name' => $theme->get('Name'),
241
			'version' => $theme->get('Version'),
242
			'uri' => $theme->get( 'ThemeURI' ),
243
		);
244
245
		/**
246
		 * This action is documented already in this file
247
		 */
248
		do_action( 'jetpack_edited_theme', $stylesheet, $theme_data );
249
250
	}
251
252
	public function detect_theme_deletion() {
253
		$delete_theme_call = $this->get_delete_theme_call();
254
		if ( empty( $delete_theme_call ) ) {
255
			return;
256
		}
257
258
		$slug = $delete_theme_call['args'][0];
259
		$theme = wp_get_theme( $slug );
260
		$theme_data = array(
261
			'name' => $theme->get('Name'),
262
			'version' => $theme->get('Version'),
263
			'uri' => $theme->get( 'ThemeURI' ),
264
			'slug' => $slug,
265
		);
266
267
		/**
268
		 * Signals to the sync listener that a theme was deleted and a sync action
269
		 * reflecting the deletion and theme slug should be sent
270
		 *
271
		 * @since 5.0.0
272
		 *
273
		 * @param string $slug Theme slug
274
		 * @param array $theme_data Theme info Since 5.3
275
		 */
276
		do_action( 'jetpack_deleted_theme', $slug, $theme_data );
277
	}
278
279
	public function check_upgrader( $upgrader, $details) {
280
		if ( ! isset( $details['type'] ) ||
281
		     'theme' !== $details['type'] ||
282
		     is_wp_error( $upgrader->skin->result ) ||
283
		     ! method_exists( $upgrader, 'theme_info' )
284
		) {
285
			return;
286
		}
287
288
		$theme = $upgrader->theme_info();
289
		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...
290
			return;
291
		}
292
		$theme_info = array(
293
			'name' => $theme->get( 'Name' ),
294
			'version' => $theme->get( 'Version' ),
295
			'uri' => $theme->get( 'ThemeURI' ),
296
		);
297
298
		if ( 'install' === $details['action'] ) {
299
			/**
300
			 * Signals to the sync listener that a theme was installed and a sync action
301
			 * reflecting the installation and the theme info should be sent
302
			 *
303
			 * @since 4.9.0
304
			 *
305
			 * @param string $theme->theme_root Text domain of the theme
306
			 * @param mixed $theme_info Array of abbreviated theme info
307
			 */
308
			do_action( 'jetpack_installed_theme', $theme->stylesheet, $theme_info );
309
		}
310
311
		if ( 'update' === $details['action'] ) {
312
			/**
313
			 * Signals to the sync listener that a theme was updated and a sync action
314
			 * reflecting the update and the theme info should be sent
315
			 *
316
			 * @since 4.9.0
317
			 *
318
			 * @param string $theme->theme_root Text domain of the theme
319
			 * @param mixed $theme_info Array of abbreviated theme info
320
			 */
321
			do_action( 'jetpack_updated_theme', $theme->stylesheet, $theme_info );
322
		}
323
	}
324
325
	public function init_full_sync_listeners( $callable ) {
326
		add_action( 'jetpack_full_sync_theme_data', $callable );
327
	}
328
329
	public function sync_theme_support() {
330
		/**
331
		 * Fires when the client needs to sync theme support info
332
		 * Only sends theme support attributes whitelisted in Jetpack_Sync_Defaults::$default_theme_support_whitelist
333
		 *
334
		 * @since 4.2.0
335
		 *
336
		 * @param object the theme support hash
337
		 */
338
		do_action( 'jetpack_sync_current_theme_support' , $this->get_theme_support_info() );
339
	}
340
341
	public function enqueue_full_sync_actions( $config, $max_items_to_enqueue, $state ) {
342
		/**
343
		 * Tells the client to sync all theme data to the server
344
		 *
345
		 * @since 4.2.0
346
		 *
347
		 * @param boolean Whether to expand theme data (should always be true)
348
		 */
349
		do_action( 'jetpack_full_sync_theme_data', true );
350
351
		// The number of actions enqueued, and next module state (true == done)
352
		return array( 1, true );
353
	}
354
355
	public function estimate_full_sync_actions( $config ) {
356
		return 1;
357
	}
358
359
	public function init_before_send() {
360
		add_filter( 'jetpack_sync_before_send_jetpack_full_sync_theme_data', array( $this, 'expand_theme_data' ) );
361
	}
362
363
	function get_full_sync_actions() {
364
		return array( 'jetpack_full_sync_theme_data' );
365
	}
366
367
	function expand_theme_data() {
368
		return array( $this->get_theme_support_info() );
369
	}
370
371
	function get_widget_name( $widget_id ) {
372
		global $wp_registered_widgets;
373
		return ( isset( $wp_registered_widgets[ $widget_id ] ) ? $wp_registered_widgets[ $widget_id ]['name'] : null );
374
	}
375
376
	function get_sidebar_name( $sidebar_id ) {
377
		global $wp_registered_sidebars;
378
		return ( isset( $wp_registered_sidebars[ $sidebar_id ] ) ? $wp_registered_sidebars[ $sidebar_id ]['name'] : null );
379
	}
380
381
	function sync_add_widgets_to_sidebar( $new_widgets, $old_widgets, $sidebar ) {
382
		$added_widgets = array_diff( $new_widgets, $old_widgets );
383
		if ( empty( $added_widgets ) ) {
384
			return array();
385
		}
386
		$moved_to_sidebar = array();
387
		$sidebar_name = $this->get_sidebar_name( $sidebar );
388
389
		//Don't sync jetpack_widget_added if theme was switched
390
		if ( $this->is_theme_switch() ) {
391
			return array();
392
		}
393
394
		foreach ( $added_widgets as $added_widget ) {
395
			$moved_to_sidebar[] = $added_widget;
396
			$added_widget_name = $this->get_widget_name( $added_widget );
397
			/**
398
			 * Helps Sync log that a widget got added
399
			 *
400
			 * @since 4.9.0
401
			 *
402
			 * @param string $sidebar, Sidebar id got changed
403
			 * @param string $added_widget, Widget id got added
404
			 * @param string $sidebar_name, Sidebar id got changed Since 5.0.0
405
			 * @param string $added_widget_name, Widget id got added Since 5.0.0
406
			 *
407
			 */
408
			do_action( 'jetpack_widget_added', $sidebar, $added_widget,  $sidebar_name, $added_widget_name );
409
		}
410
		return $moved_to_sidebar;
411
	}
412
413
	function sync_remove_widgets_from_sidebar( $new_widgets, $old_widgets, $sidebar, $inactive_widgets  ) {
414
		$removed_widgets = array_diff( $old_widgets, $new_widgets );
415
416
		if ( empty( $removed_widgets ) ) {
417
			return array();
418
		}
419
420
		$moved_to_inactive = array();
421
		$sidebar_name = $this->get_sidebar_name( $sidebar );
422
423
		foreach( $removed_widgets as $removed_widget ) {
424
			// Lets check if we didn't move the widget to in_active_widgets
425
			if ( isset( $inactive_widgets ) && ! in_array( $removed_widget, $inactive_widgets ) ) {
426
				$removed_widget_name = $this->get_widget_name( $removed_widget );
427
				/**
428
				 * Helps Sync log that a widgte got removed
429
				 *
430
				 * @since 4.9.0
431
				 *
432
				 * @param string $sidebar, Sidebar id got changed
433
				 * @param string $removed_widget, Widget id got removed
434
				 * @param string $sidebar_name, Name of the sidebar that changed  Since 5.0.0
435
				 * @param string $removed_widget_name, Name of the widget that got removed Since 5.0.0
436
				 */
437
				do_action( 'jetpack_widget_removed', $sidebar, $removed_widget, $sidebar_name, $removed_widget_name );
438
			} else {
439
				$moved_to_inactive[] = $removed_widget;
440
			}
441
		}
442
		return $moved_to_inactive;
443
444
	}
445
446
	function sync_widgets_reordered( $new_widgets, $old_widgets, $sidebar ) {
447
		$added_widgets = array_diff( $new_widgets, $old_widgets );
448
		if ( ! empty( $added_widgets ) ) {
449
			return;
450
		}
451
		$removed_widgets = array_diff( $old_widgets, $new_widgets );
452
		if ( ! empty( $removed_widgets ) ) {
453
			return;
454
		}
455
456
		if ( serialize( $old_widgets ) !== serialize( $new_widgets ) ) {
457
			$sidebar_name = $this->get_sidebar_name( $sidebar );
458
			/**
459
			 * Helps Sync log that a sidebar id got reordered
460
			 *
461
			 * @since 4.9.0
462
			 *
463
			 * @param string $sidebar, Sidebar id got changed
464
			 * @param string $sidebar_name, Name of the sidebar that changed  Since 5.0.0
465
			 */
466
			do_action( 'jetpack_widget_reordered', $sidebar, $sidebar_name );
467
		}
468
469
	}
470
471
	function sync_sidebar_widgets_actions( $old_value, $new_value ) {
472
		// Don't really know how to deal with different array_values yet.
473
		if (
474
			( isset( $old_value['array_version'] ) && $old_value['array_version'] !== 3 ) ||
475
			( isset( $new_value['array_version'] ) && $new_value['array_version'] !== 3 )
476
		) {
477
			return;
478
		}
479
480
		$moved_to_inactive_ids = array();
481
		$moved_to_sidebar = array();
482
483
		foreach ( $new_value as $sidebar => $new_widgets ) {
484
			if ( in_array( $sidebar, array( 'array_version', 'wp_inactive_widgets' ) ) ) {
485
				continue;
486
			}
487
			$old_widgets = isset( $old_value[ $sidebar ] )
488
				? $old_value[ $sidebar ]
489
				: array();
490
491
			if ( ! is_array( $new_widgets ) ) {
492
				$new_widgets = array();
493
			}
494
495
			$moved_to_inactive_recently = $this->sync_remove_widgets_from_sidebar( $new_widgets, $old_widgets, $sidebar, $new_value['wp_inactive_widgets'] );
496
			$moved_to_inactive_ids = array_merge( $moved_to_inactive_ids, $moved_to_inactive_recently );
497
498
			$moved_to_sidebar_recently = $this->sync_add_widgets_to_sidebar( $new_widgets, $old_widgets, $sidebar );
499
			$moved_to_sidebar = array_merge( $moved_to_sidebar, $moved_to_sidebar_recently );
500
501
			$this->sync_widgets_reordered( $new_widgets, $old_widgets, $sidebar );
502
503
		}
504
505
		//Don't sync either jetpack_widget_moved_to_inactive or jetpack_cleared_inactive_widgets if theme was switched
506
		if ( $this->is_theme_switch() ) {
507
			return;
508
		}
509
510
		// Treat inactive sidebar a bit differently
511
		if ( ! empty( $moved_to_inactive_ids ) ) {
512
			$moved_to_inactive_name = array_map( array( $this, 'get_widget_name' ), $moved_to_inactive_ids );
513
			/**
514
			 * Helps Sync log that a widgets IDs got moved to in active
515
			 *
516
			 * @since 4.9.0
517
			 *
518
			 * @param array $moved_to_inactive_ids, Array of widgets id that moved to inactive id got changed
519
			 * @param array $moved_to_inactive_names, Array of widgets names that moved to inactive id got changed Since 5.0.0
520
			 */
521
			do_action( 'jetpack_widget_moved_to_inactive', $moved_to_inactive_ids, $moved_to_inactive_name );
522
		} elseif ( empty( $moved_to_sidebar ) &&
523
		           empty( $new_value['wp_inactive_widgets']) &&
524
		           ! empty( $old_value['wp_inactive_widgets'] ) ) {
525
			/**
526
			 * Helps Sync log that a got cleared from inactive.
527
			 *
528
			 * @since 4.9.0
529
			 */
530
			do_action( 'jetpack_cleared_inactive_widgets' );
531
		}
532
	}
533
534
	private function get_theme_support_info() {
535
		global $_wp_theme_features;
536
537
		$theme_support = array();
538
539
		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...
540
			$has_support = current_theme_supports( $theme_feature );
541
			if ( $has_support ) {
542
				$theme_support[ $theme_feature ] = $_wp_theme_features[ $theme_feature ];
543
			}
544
		}
545
546
		$theme = wp_get_theme();
547
		$theme_support['name'] = $theme->get('Name');
548
		$theme_support['version'] =  $theme->get('Version');
549
		$theme_support['slug'] = $theme->get_stylesheet();
550
		$theme_support['uri'] = $theme->get('ThemeURI');
551
552
553
		return $theme_support;
554
	}
555
556
	private function get_delete_theme_call() {
557
		$backtrace = debug_backtrace();
558
		$delete_theme_call = null;
559
		foreach ( $backtrace as $call ) {
560
			if ( isset( $call['function'] ) && 'delete_theme' === $call['function'] ) {
561
				$delete_theme_call = $call;
562
				break;
563
			}
564
		}
565
		return $delete_theme_call;
566
	}
567
568
	private function is_theme_switch() {
569
		return did_action( 'after_switch_theme' );
570
	}
571
}
572