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