Completed
Push — add/visibility-minor-filter ( d0cdd4...9b8b80 )
by
unknown
228:20 queued 215:17
created

Jetpack_Widget_Conditions   D

Complexity

Total Complexity 174

Size/Duplication

Total Lines 896
Duplicated Lines 5.47 %

Coupling/Cohesion

Components 1
Dependencies 0

Importance

Changes 0
Metric Value
dl 49
loc 896
rs 4.4444
c 0
b 0
f 0
wmc 174
lcom 1
cbo 0

12 Methods

Rating   Name   Duplication   Size   Complexity  
A init() 0 11 3
A condition_minor_visible() 0 17 1
F widget_admin_setup() 0 133 13
F widget_conditions_admin() 0 195 19
C widget_update() 0 81 14
C sidebars_widgets() 0 44 13
A template_redirect() 0 3 1
A generate_condition_key() 0 6 2
F filter_widget() 49 274 89
A strcasecmp_name() 0 3 1
A maybe_get_split_term() 0 13 4
C migrate_post_type_rules() 0 61 14

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like Jetpack_Widget_Conditions often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Jetpack_Widget_Conditions, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
4
/**
5
 * Hide or show widgets conditionally.
6
 */
7
8
class Jetpack_Widget_Conditions {
9
	static $passed_template_redirect = false;
0 ignored issues
show
Coding Style introduced by
The visibility should be declared for property $passed_template_redirect.

The PSR-2 coding standard requires that all properties in a class have their visibility explicitly declared. If you declare a property using

class A {
    var $property;
}

the property is implicitly global.

To learn more about the PSR-2, please see the PHP-FIG site on the PSR-2.

Loading history...
10
11
	public static function init() {
12
		if ( is_admin() ) {
13
			add_action( 'sidebar_admin_setup', array( __CLASS__, 'widget_admin_setup' ) );
14
			add_filter( 'widget_update_callback', array( __CLASS__, 'widget_update' ), 10, 3 );
15
			add_action( 'in_widget_form', array( __CLASS__, 'widget_conditions_admin' ), 10, 3 );
16
		} else if ( ! in_array( $GLOBALS['pagenow'], array( 'wp-login.php', 'wp-register.php' ) ) ) {
17
			add_filter( 'widget_display_callback', array( __CLASS__, 'filter_widget' ) );
18
			add_filter( 'sidebars_widgets', array( __CLASS__, 'sidebars_widgets' ) );
19
			add_action( 'template_redirect', array( __CLASS__, 'template_redirect' ) );
20
		}
21
	}
22
23
	public static function condition_minor_visible( $rule ) {
24
		/**
25
		 * Filters the visibility of a minor rule for the current rule.
26
		 *
27
		 * @since 4.8.0
28
		 *
29
		 * @module widget-visibility
30
		 *
31
		 * @param Boolean $is_visible if the minor rule should be visible.
32
		 * @param array $rule (
33
		 *   'major' => Array,
34
		 *   'minor' => Array,
35
		 *   'has_children' => Boolean
36
		 * ).
37
		 */
38
		return apply_filters( 'jetpack_widget_visibility_minor_visible', true, $rule );
39
	}
40
41
	public static function widget_admin_setup() {
42
		if( is_rtl() ) {
43
			wp_enqueue_style( 'widget-conditions', plugins_url( 'widget-conditions/rtl/widget-conditions-rtl.css', __FILE__ ) );
44
		} else {
45
			wp_enqueue_style( 'widget-conditions', plugins_url( 'widget-conditions/widget-conditions.css', __FILE__ ) );
46
		}
47
		wp_enqueue_style( 'widget-conditions', plugins_url( 'widget-conditions/widget-conditions.css', __FILE__ ) );
48
		wp_enqueue_script( 'widget-conditions', plugins_url( 'widget-conditions/widget-conditions.js', __FILE__ ), array( 'jquery', 'jquery-ui-core' ), 20140721, true );
49
50
		// Set up a single copy of all of the data that Widget Visibility needs.
51
		// This allows all widget conditions to reuse the same data, keeping page size down
52
		// and eliminating the AJAX calls we used to have to use to fetch the minor rule options.
53
		$widget_conditions_data = array();
54
55
		$widget_conditions_data['category'] = array();
56
		$widget_conditions_data['category'][] = array( '', __( 'All category pages', 'jetpack' ) );
57
58
		$categories = get_categories( array( 'number' => 1000, 'orderby' => 'count', 'order' => 'DESC' ) );
59
		usort( $categories, array( __CLASS__, 'strcasecmp_name' ) );
60
61
		foreach ( $categories as $category ) {
62
			$widget_conditions_data['category'][] = array( (string) $category->term_id, $category->name );
63
		}
64
65
		$widget_conditions_data['loggedin'] = array();
66
		$widget_conditions_data['loggedin'][] = array( 'loggedin', __( 'Logged In', 'jetpack' ) );
67
		$widget_conditions_data['loggedin'][] = array( 'loggedout', __( 'Logged Out', 'jetpack' ) );
68
69
		$widget_conditions_data['author'] = array();
70
		$widget_conditions_data['author'][] = array( '', __( 'All author pages', 'jetpack' ) );
71
72
		$authors = get_users( array( 'orderby' => 'name', 'exclude_admin' => true ) );
73
74
		foreach ( $authors as $author ) {
75
			$widget_conditions_data['author'][] = array( (string) $author->ID, $author->display_name );
76
		}
77
78
		$widget_conditions_data['role'] = array();
79
80
		global $wp_roles;
81
82
		foreach ( $wp_roles->roles as $role_key => $role ) {
83
			$widget_conditions_data['role'][] = array( (string) $role_key, $role['name'] );
84
		}
85
86
		$widget_conditions_data['tag'] = array();
87
		$widget_conditions_data['tag'][] = array( '', __( 'All tag pages', 'jetpack' ) );
88
89
		$tags = get_tags( array( 'number' => 1000, 'orderby' => 'count', 'order' => 'DESC' ) );
90
		usort( $tags, array( __CLASS__, 'strcasecmp_name' ) );
91
92
		foreach ( $tags as $tag ) {
93
			$widget_conditions_data['tag'][] = array( (string) $tag->term_id, $tag->name );
94
		}
95
96
		$widget_conditions_data['date'] = array();
97
		$widget_conditions_data['date'][] = array( '', __( 'All date archives', 'jetpack' ) );
98
		$widget_conditions_data['date'][] = array( 'day', __( 'Daily archives', 'jetpack' ) );
99
		$widget_conditions_data['date'][] = array( 'month', __( 'Monthly archives', 'jetpack' ) );
100
		$widget_conditions_data['date'][] = array( 'year', __( 'Yearly archives', 'jetpack' ) );
101
102
		$widget_conditions_data['page'] = array();
103
		$widget_conditions_data['page'][] = array( 'front', __( 'Front page', 'jetpack' ) );
104
		$widget_conditions_data['page'][] = array( 'posts', __( 'Posts page', 'jetpack' ) );
105
		$widget_conditions_data['page'][] = array( 'archive', __( 'Archive page', 'jetpack' ) );
106
		$widget_conditions_data['page'][] = array( '404', __( '404 error page', 'jetpack' ) );
107
		$widget_conditions_data['page'][] = array( 'search', __( 'Search results', 'jetpack' ) );
108
109
		$post_types = get_post_types( array( 'public' => true ), 'objects' );
110
111
		$widget_conditions_post_types = array();
112
		$widget_conditions_post_type_archives = array();
113
114
		foreach ( $post_types as $post_type ) {
115
			$widget_conditions_post_types[] = array( 'post_type-' . $post_type->name, $post_type->labels->singular_name );
116
			$widget_conditions_post_type_archives[] = array( 'post_type_archive-' . $post_type->name, $post_type->labels->name );
117
		}
118
119
		$widget_conditions_data['page'][] = array( __( 'Post type:', 'jetpack' ), $widget_conditions_post_types );
120
121
		$widget_conditions_data['page'][] = array( __( 'Post type Archives:', 'jetpack' ), $widget_conditions_post_type_archives );
122
123
		$pages_dropdown = preg_replace( '/<\/?select[^>]*?>/i', '', wp_dropdown_pages( array( 'echo' => false ) ) );
124
125
		preg_match_all( '/value=.([0-9]+).[^>]*>([^<]+)</', $pages_dropdown, $page_ids_and_titles, PREG_SET_ORDER );
126
127
		$static_pages = array();
128
129
		foreach ( $page_ids_and_titles as $page_id_and_title ) {
0 ignored issues
show
Bug introduced by
The expression $page_ids_and_titles of type null|array<integer,array<integer,string>> is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
130
			$static_pages[] = array( (string) $page_id_and_title[1], $page_id_and_title[2] );
131
		}
132
133
		$widget_conditions_data['page'][] = array( __( 'Static page:', 'jetpack' ), $static_pages );
134
135
		$widget_conditions_data['taxonomy'] = array();
136
		$widget_conditions_data['taxonomy'][] = array( '', __( 'All taxonomy pages', 'jetpack' ) );
137
138
		$taxonomies = get_taxonomies( array( '_builtin' => false ), 'objects' );
139
		usort( $taxonomies, array( __CLASS__, 'strcasecmp_name' ) );
140
141
		foreach ( $taxonomies as $taxonomy ) {
142
			$taxonomy_terms = get_terms( array( $taxonomy->name ), array( 'number' => 250, 'hide_empty' => false ) );
143
144
			$widget_conditions_terms = array();
145
			$widget_conditions_terms[] = array( $taxonomy->name, __( 'All pages', 'jetpack' ) );
146
147
			foreach ( $taxonomy_terms as $term ) {
148
				$widget_conditions_terms[] = array( $taxonomy->name . '_tax_' . $term->term_id, $term->name );
149
			}
150
151
			$widget_conditions_data['taxonomy'][] = array( $taxonomy->labels->name . ':', $widget_conditions_terms );
152
		}
153
154
		wp_localize_script( 'widget-conditions', 'widget_conditions_data', $widget_conditions_data );
155
156
		// Save a list of the IDs of all pages that have children for dynamically showing the "Include children" checkbox.
157
		$all_pages = get_pages();
158
		$all_parents = array();
159
160
		foreach ( $all_pages as $page ) {
161
			if ( $page->post_parent ) {
162
				$all_parents[ (string) $page->post_parent ] = true;
163
			}
164
		}
165
166
		$front_page_id = get_option( 'page_on_front' );
167
168
		if ( isset( $all_parents[ $front_page_id ] ) ) {
169
			$all_parents[ 'front' ] = true;
170
		}
171
172
		wp_localize_script( 'widget-conditions', 'widget_conditions_parent_pages', $all_parents );
173
	}
174
175
	/**
176
	 * Add the widget conditions to each widget in the admin.
177
	 *
178
	 * @param $widget unused.
179
	 * @param $return unused.
180
	 * @param array $instance The widget settings.
181
	 */
182
	public static function widget_conditions_admin( $widget, $return, $instance ) {
183
		$conditions = array();
184
185
		if ( isset( $instance['conditions'] ) )
186
			$conditions = $instance['conditions'];
187
188
		if ( ! isset( $conditions['action'] ) )
189
			$conditions['action'] = 'show';
190
191
		if ( empty( $conditions['rules'] ) )
192
			/**
193
			 * Filters initial widget visibility conditions.
194
			 *
195
			 * @since 4.8.0
196
			 *
197
			 * @module widget-visibility
198
			 *
199
			 * @param array $args Widget Visibility initial condition array.
200
			 */
201
			$conditions['rules'][] = apply_filters(
202
				'jetpack_widget_visibility_conditions',
203
				array( 'major' => '', 'minor' => '', 'has_children' => '' )
204
			);
205
206
		if ( empty( $conditions['match_all'] ) ) {
207
			$conditions['match_all'] = false;
208
		}
209
210
		?>
211
		<div
212
			class="
213
				widget-conditional
214
				<?php
215
					if (
216
						empty( $_POST['widget-conditions-visible'] )
217
						|| $_POST['widget-conditions-visible'] == '0'
218
					) {
219
						?>widget-conditional-hide<?php
220
					}
221
				?>
222
				<?php
223
					if ( ! empty( $conditions['match_all'] ) && $conditions['match_all'] ) {
224
						?>intersection<?php
225
					} else {
226
						?>conjunction<?php
227
					}
228
				?>
229
			">
230
			<input type="hidden" name="widget-conditions-visible" value="<?php if ( isset( $_POST['widget-conditions-visible'] ) ) { echo esc_attr( $_POST['widget-conditions-visible'] ); } else { ?>0<?php } ?>" />
231
			<?php if ( ! isset( $_POST['widget-conditions-visible'] ) ) { ?><a href="#" class="button display-options"><?php _e( 'Visibility', 'jetpack' ); ?></a><?php } ?>
232
			<div class="widget-conditional-inner">
233
				<div class="condition-top">
234
					<?php printf( _x( '%s if:', 'placeholder: dropdown menu to select widget visibility; hide if or show if', 'jetpack' ), '<select name="conditions[action]"><option value="show" ' . selected( $conditions['action'], 'show', false ) . '>' . esc_html_x( 'Show', 'Used in the "%s if:" translation for the widget visibility dropdown', 'jetpack' ) . '</option><option value="hide" ' . selected( $conditions['action'], 'hide', false ) . '>' . esc_html_x( 'Hide', 'Used in the "%s if:" translation for the widget visibility dropdown', 'jetpack' ) . '</option></select>' ); ?>
235
				</div><!-- .condition-top -->
236
237
				<div class="conditions">
238
					<?php
239
240
					foreach ( $conditions['rules'] as $rule_index => $rule ) {
241
						$rule = wp_parse_args( $rule, array( 'major' => '', 'minor' => '', 'has_children' => '' ) );
242
						?>
243
						<div class="condition" data-rule-major="<?php echo esc_attr( $rule['major'] ); ?>" data-rule-minor="<?php echo esc_attr( $rule['minor'] ); ?>" data-rule-has-children="<?php echo esc_attr( $rule['has_children'] ); ?>">
244
							<div class="selection alignleft">
245
								<select class="conditions-rule-major" name="conditions[rules_major][]">
246
									<option value="" <?php selected( "", $rule['major'] ); ?>><?php echo esc_html_x( '-- Select --', 'Used as the default option in a dropdown list', 'jetpack' ); ?></option>
247
									<option value="category" <?php selected( "category", $rule['major'] ); ?>><?php esc_html_e( 'Category', 'jetpack' ); ?></option>
248
									<option value="author" <?php selected( "author", $rule['major'] ); ?>><?php echo esc_html_x( 'Author', 'Noun, as in: "The author of this post is..."', 'jetpack' ); ?></option>
249
250
									<?php if( ! ( defined( 'IS_WPCOM' ) && IS_WPCOM ) ) { // this doesn't work on .com because of caching ?>
251
										<option value="loggedin" <?php selected( "loggedin", $rule['major'] ); ?>><?php echo esc_html_x( 'User', 'Noun', 'jetpack' ); ?></option>
252
										<option value="role" <?php selected( "role", $rule['major'] ); ?>><?php echo esc_html_x( 'Role', 'Noun, as in: "The user role of that can access this widget is..."', 'jetpack' ); ?></option>
253
									<?php } ?>
254
255
									<option value="tag" <?php selected( "tag", $rule['major'] ); ?>><?php echo esc_html_x( 'Tag', 'Noun, as in: "This post has one tag."', 'jetpack' ); ?></option>
256
									<option value="date" <?php selected( "date", $rule['major'] ); ?>><?php echo esc_html_x( 'Date', 'Noun, as in: "This page is a date archive."', 'jetpack' ); ?></option>
257
									<option value="page" <?php selected( "page", $rule['major'] ); ?>><?php echo esc_html_x( 'Page', 'Example: The user is looking at a page, not a post.', 'jetpack' ); ?></option>
258
									<?php if ( get_taxonomies( array( '_builtin' => false ) ) ) : ?>
259
										<option value="taxonomy" <?php selected( "taxonomy", $rule['major'] ); ?>><?php echo esc_html_x( 'Taxonomy', 'Noun, as in: "This post has one taxonomy."', 'jetpack' ); ?></option>
260
									<?php endif; ?>
261
									<?php
262
									/**
263
									 * Triggered when the output of available major rule options is over.
264
									 * Can be used to add new options for major rules.
265
									 *
266
									 * @since 4.8.0
267
									 *
268
									 * @module widget-visibility
269
									 *
270
									 * @param array $rule (
271
									 *   'major' => Array,
272
									 *   'minor' => Array,
273
									 *   'has_children' => Boolean
274
									 * ).
275
									 */
276
									do_action( 'jetpack_widget_visibility_condition_major', $rule );
277
									?>
278
								</select>
279
280
								<?php
281
								/**
282
								 * Filters the minor rule visibility. Return false to hide
283
								 * the minor rule for the current rule.
284
								 *
285
								 * @since 4.8.0
286
								 *
287
								 * @module widget-visibility
288
								 *
289
								 * @param array $rule (
290
								 *   'major' => Array,
291
								 *   'minor' => Array,
292
								 *   'has_children' => Boolean
293
								 * ).
294
								 */
295
								$show_minor_rule = self::condition_minor_visible( $rule );
296
								?>
297
298
								<?php if ( $show_minor_rule ) {
299
									_ex( 'is', 'Widget Visibility: {Rule Major [Page]} is {Rule Minor [Search results]}', 'jetpack' );
300
								} ?>
301
302
								<select
303
									class="conditions-rule-minor <?php
304
										echo $show_minor_rule ? '' : 'hidden'
305
									?>"
306
									name="conditions[rules_minor][]"
307
									<?php if ( ! $rule['major'] ) { ?>
308
										disabled="disabled"
309
									<?php } ?>>
310
								</select>
311
									<?php /* Include the currently selected value so that if the widget is saved without
312
									         expanding the Visibility section, we don't lose the minor part of the rule.
313
									         If it is opened, this list is cleared out and populated with all the values. */ ?>
314
									<option value="<?php echo esc_attr( $rule['minor'] ); ?>" selected="selected"></option>
315
								</select>
316
317
								<?php
318
								/**
319
								 * Triggered on the end of a major rule output.
320
								 * Can be used to add additional fields to the rule.
321
								 *
322
								 * @since 4.8.0
323
								 *
324
								 * @module widget-visibility
325
								 *
326
								 * @param array $rule (
327
								 *   'major' => Array,
328
								 *   'minor' => Array,
329
								 *   'has_children' => Boolean
330
								 * ).
331
								 */
332
								do_action( 'jetpack_widget_visibility_additional_fields', $rule );
333
								?>
334
								<span class="conditions-rule-has-children" <?php if ( ! $rule['has_children'] ) { ?> style="display: none;"<?php } ?>>
335
									<label>
336
										<input type="checkbox" name="conditions[page_children][<?php echo $rule_index; ?>]" value="has" <?php checked( $rule['has_children'], true ); ?> />
337
										<?php echo esc_html_x( "Include children", 'Checkbox on Widget Visibility if children of the selected page should be included in the visibility rule.', 'jetpack' ); ?>
338
									</label>
339
								</span>
340
							</div>
341
342
							<div class="condition-control">
343
								<span class="condition-conjunction">
344
									<?php echo esc_html_x( 'or', 'Shown between widget visibility conditions.', 'jetpack' ); ?>
345
								</span>
346
								<span class="condition-intersection">
347
									<?php echo esc_html_x( 'and', 'Shown between widget visibility conditions.', 'jetpack' ); ?>
348
								</span>
349
								<div class="actions alignright">
350
									<a href="#" class="delete-condition dashicons dashicons-no"><?php esc_html_e( 'Delete', 'jetpack' ); ?></a><a href="#" class="add-condition dashicons dashicons-plus"><?php esc_html_e( 'Add', 'jetpack' ); ?></a>
351
								</div>
352
							</div>
353
354
						</div><!-- .condition -->
355
						<?php
356
					}
357
358
					?>
359
				</div><!-- .conditions -->
360
				<div class="conditions">
361
					<div class="condition-top">
362
						<label>
363
							<input
364
								type="checkbox"
365
								name="conditions[match_all]"
366
								value="1"
367
								class="conditions-match-all"
368
								<?php checked( $conditions['match_all'], '1' ); ?> />
369
							<?php esc_html_e( 'Match all conditions', 'jetpack' ); ?>
370
						</label>
371
					</div><!-- .condition-top -->
372
				</div><!-- .conditions -->
373
			</div><!-- .widget-conditional-inner -->
374
		</div><!-- .widget-conditional -->
375
		<?php
376
	}
377
378
	/**
379
	 * On an AJAX update of the widget settings, process the display conditions.
380
	 *
381
	 * @param array $new_instance New settings for this instance as input by the user.
382
	 * @param array $old_instance Old settings for this instance.
383
	 * @return array Modified settings.
384
	 */
385
	public static function widget_update( $instance, $new_instance, $old_instance ) {
386
		if ( empty( $_POST['conditions'] ) ) {
387
			return $instance;
388
		}
389
390
		$conditions = array();
391
		$conditions['action'] = $_POST['conditions']['action'];
392
		$conditions['match_all'] = ( isset( $_POST['conditions']['match_all'] ) ? '1' : '0' );
393
		$conditions['rules'] = array();
394
395
		foreach ( $_POST['conditions']['rules_major'] as $index => $major_rule ) {
396
			if ( ! $major_rule )
397
				continue;
398
399
			$defaults = array(
400
				'major' => $major_rule,
401
				'minor' => isset( $_POST['conditions']['rules_minor'][$index] ) ? $_POST['conditions']['rules_minor'][$index] : '',
402
				'has_children' => isset( $_POST['conditions']['page_children'][$index] ) ? true : false,
403
			);
404
405
			/**
406
			 * Filters default widget visibility conditions. This filter will be passed
407
			 * an array of conditions for every major rule with default values set.
408
			 *
409
			 * @since 4.8.0
410
			 *
411
			 * @module widget-visibility
412
			 *
413
			 * @param array $defaults [
414
			 *   'major' => Array,
415
			 *   'minor' => Array,
416
			 *   'has_children' => Boolean
417
			 * ].
418
			 * @param int $index the rule index
419
			 */
420
421
			$conditions['rules'][] = apply_filters(
422
				'jetpack_widget_visibility_conditions_defaults',
423
				$defaults,
424
				$index
425
			);
426
		}
427
428
		if ( ! empty( $conditions['rules'] ) )
429
			$instance['conditions'] = $conditions;
430
		else
431
			unset( $instance['conditions'] );
432
433
		if (
434
				( isset( $instance['conditions'] ) && ! isset( $old_instance['conditions'] ) )
435
				||
436
				(
437
					isset( $instance['conditions'], $old_instance['conditions'] )
438
					&&
439
					serialize( $instance['conditions'] ) != serialize( $old_instance['conditions'] )
440
				)
441
			) {
442
443
			/**
444
			 * Fires after the widget visibility conditions are saved.
445
			 *
446
			 * @module widget-visibility
447
			 *
448
			 * @since 2.4.0
449
			 */
450
			do_action( 'widget_conditions_save' );
451
		}
452
		else if ( ! isset( $instance['conditions'] ) && isset( $old_instance['conditions'] ) ) {
453
454
			/**
455
			 * Fires after the widget visibility conditions are deleted.
456
			 *
457
			 * @module widget-visibility
458
			 *
459
			 * @since 2.4.0
460
			 */
461
			do_action( 'widget_conditions_delete' );
462
		}
463
464
		return $instance;
465
	}
466
467
	/**
468
	 * Filter the list of widgets for a sidebar so that active sidebars work as expected.
469
	 *
470
	 * @param array $widget_areas An array of widget areas and their widgets.
471
	 * @return array The modified $widget_area array.
472
	 */
473
	public static function sidebars_widgets( $widget_areas ) {
474
		$settings = array();
475
476
		foreach ( $widget_areas as $widget_area => $widgets ) {
477
			if ( empty( $widgets ) )
478
				continue;
479
480
			if ( ! is_array( $widgets ) )
481
				continue;
482
483
			if ( 'wp_inactive_widgets' == $widget_area )
484
				continue;
485
486
			foreach ( $widgets as $position => $widget_id ) {
487
				// Find the conditions for this widget.
488
				if ( preg_match( '/^(.+?)-(\d+)$/', $widget_id, $matches ) ) {
489
					$id_base = $matches[1];
490
					$widget_number = intval( $matches[2] );
491
				}
492
				else {
493
					$id_base = $widget_id;
494
					$widget_number = null;
495
				}
496
497
				if ( ! isset( $settings[$id_base] ) ) {
498
					$settings[$id_base] = get_option( 'widget_' . $id_base );
499
				}
500
501
				// New multi widget (WP_Widget)
502
				if ( ! is_null( $widget_number ) ) {
503
					if ( isset( $settings[$id_base][$widget_number] ) && false === self::filter_widget( $settings[$id_base][$widget_number] ) ) {
504
						unset( $widget_areas[$widget_area][$position] );
505
					}
506
				}
507
508
				// Old single widget
509
				else if ( ! empty( $settings[ $id_base ] ) && false === self::filter_widget( $settings[$id_base] ) ) {
510
					unset( $widget_areas[$widget_area][$position] );
511
				}
512
			}
513
		}
514
515
		return $widget_areas;
516
	}
517
518
	public static function template_redirect() {
519
		self::$passed_template_redirect = true;
520
	}
521
522
	/**
523
	 * Generates a condition key based on the rule array
524
	 *
525
	 * @param array $rule
526
	 * @return string key used to retrieve the condition.
527
	 */
528
	static function generate_condition_key( $rule ) {
529
		if ( isset( $rule['has_children'] ) ) {
530
			return $rule['major'] . ":" . $rule['minor'] . ":" . $rule['has_children'];
531
		}
532
		return $rule['major'] . ":" . $rule['minor'];
533
	}
534
535
	/**
536
	 * Determine whether the widget should be displayed based on conditions set by the user.
537
	 *
538
	 * @param array $instance The widget settings.
539
	 * @return array Settings to display or bool false to hide.
540
	 */
541
	public static function filter_widget( $instance ) {
542
		global $wp_query;
543
544
		if ( empty( $instance['conditions'] ) || empty( $instance['conditions']['rules'] ) )
545
			return $instance;
546
547
		// Store the results of all in-page condition lookups so that multiple widgets with
548
		// the same visibility conditions don't result in duplicate DB queries.
549
		static $condition_result_cache = array();
550
551
		$condition_result = false;
552
553
		foreach ( $instance['conditions']['rules'] as $rule ) {
554
			$condition_result = false;
555
			$condition_key = self::generate_condition_key( $rule );
556
557
			if ( isset( $condition_result_cache[ $condition_key ] ) ) {
558
				$condition_result = $condition_result_cache[ $condition_key ];
559
			}
560
			else {
561
				switch ( $rule['major'] ) {
562
					case 'date':
563
						switch ( $rule['minor'] ) {
564
							case '':
565
								$condition_result = is_date();
566
							break;
567
							case 'month':
568
								$condition_result = is_month();
569
							break;
570
							case 'day':
571
								$condition_result = is_day();
572
							break;
573
							case 'year':
574
								$condition_result = is_year();
575
							break;
576
						}
577
					break;
578
					case 'page':
579
						// Previously hardcoded post type options.
580
						if ( 'post' == $rule['minor'] )
581
							$rule['minor'] = 'post_type-post';
582
						else if ( ! $rule['minor'] )
583
							$rule['minor'] = 'post_type-page';
584
585
						switch ( $rule['minor'] ) {
586
							case '404':
587
								$condition_result = is_404();
588
							break;
589
							case 'search':
590
								$condition_result = is_search();
591
							break;
592
							case 'archive':
593
								$condition_result = is_archive();
594
							break;
595
							case 'posts':
596
								$condition_result = $wp_query->is_posts_page;
597
							break;
598
							case 'home':
599
								$condition_result = is_home();
600
							break;
601
							case 'front':
602
								if ( current_theme_supports( 'infinite-scroll' ) )
603
									$condition_result = is_front_page();
604
								else {
605
									$condition_result = is_front_page() && !is_paged();
606
								}
607
							break;
608
							default:
609
								if ( substr( $rule['minor'], 0, 10 ) == 'post_type-' ) {
610
									$condition_result = is_singular( substr( $rule['minor'], 10 ) );
611 View Code Duplication
								} elseif ( substr( $rule['minor'], 0, 18 ) == 'post_type_archive-' ) {
612
									$condition_result = is_post_type_archive( substr( $rule['minor'], 18 ) );
613
								} elseif ( $rule['minor'] == get_option( 'page_for_posts' ) ) {
614
									// If $rule['minor'] is a page ID which is also the posts page
615
									$condition_result = $wp_query->is_posts_page;
616
								} else {
617
									// $rule['minor'] is a page ID
618
									$condition_result = is_page() && ( $rule['minor'] == get_the_ID() );
619
620
									// Check if $rule['minor'] is parent of page ID
621
									if ( ! $condition_result && isset( $rule['has_children'] ) && $rule['has_children'] )
622
										$condition_result = wp_get_post_parent_id( get_the_ID() ) == $rule['minor'];
623
								}
624
							break;
625
						}
626
					break;
627 View Code Duplication
					case 'tag':
628
						// All tag pages.
629
						if( ! $rule['minor'] ) {
630
							if ( is_tag() ) {
631
								$condition_result = true;
632
							} else if ( is_singular() ) {
633
								if( in_array( 'post_tag', get_post_taxonomies() ) ) {
634
									$condition_result = true;
635
								}
636
							}
637
							break;
638
						}
639
640
						// All pages with the specified tag term.
641
						if ( is_tag( $rule['minor'] ) ) {
642
							$condition_result = true;
643
						}
644
						else if ( is_singular() && has_term( $rule['minor'], 'post_tag' ) ) {
645
							$condition_result = true;
646
						}
647
					break;
648 View Code Duplication
					case 'category':
649
						// All category pages.
650
						if( ! $rule['minor'] ) {
651
							if ( is_category() ) {
652
								$condition_result = true;
653
							}
654
							else if ( is_singular() ) {
655
								if( in_array( 'category', get_post_taxonomies() ) ) {
656
									$condition_result = true;
657
								}
658
							}
659
							break;
660
						}
661
662
						// All pages with the specified category term.
663
						if ( is_category( $rule['minor'] ) ) {
664
							$condition_result = true;
665
						}
666
						else if ( is_singular() && has_term( $rule['minor'], 'category' ) ) {
667
							$condition_result = true;
668
						}
669
					break;
670
					case 'loggedin':
671
						$condition_result = is_user_logged_in();
672
						if ( 'loggedin' !== $rule['minor'] ) {
673
							$condition_result = ! $condition_result;
674
						}
675
					break;
676
					case 'author':
677
						$post = get_post();
678
						if ( ! $rule['minor'] && is_author() )
679
							$condition_result = true;
680
						else if ( $rule['minor'] && is_author( $rule['minor'] ) )
681
							$condition_result = true;
682
						else if ( is_singular() && $rule['minor'] && $rule['minor'] == $post->post_author )
683
							$condition_result = true;
684
					break;
685
					case 'role':
686
						if( is_user_logged_in() ) {
687
							$current_user = wp_get_current_user();
688
689
							$user_roles = $current_user->roles;
690
691
							if( in_array( $rule['minor'], $user_roles ) ) {
692
								$condition_result = true;
693
							} else {
694
								$condition_result = false;
695
							}
696
697
						} else {
698
							$condition_result = false;
699
						}
700
					break;
701
					case 'post_type':
702
						if ( substr( $rule['minor'], 0, 10 ) == 'post_type-' ) {
703
							$condition_result = is_singular( substr( $rule['minor'], 10 ) );
704 View Code Duplication
						} elseif ( substr( $rule['minor'], 0, 18 ) == 'post_type_archive-' ) {
705
							$condition_result = is_post_type_archive( substr( $rule['minor'], 18 ) );
706
						}
707
					break;
708
					case 'taxonomy':
709
						// All taxonomy pages.
710
						if( ! $rule['minor'] ) {
711
							if ( is_archive() ) {
712
								if ( is_tag() || is_category() || is_tax() ) {
713
									$condition_result = true;
714
								}
715
							}
716
							else if ( is_singular() ) {
717
								$post_taxonomies = get_post_taxonomies();
718
								$condition_result = ! empty( $post_taxonomies );
719
							}
720
							break;
721
						}
722
723
						// Specified taxonomy page.
724
						$term = explode( '_tax_', $rule['minor'] ); // $term[0] = taxonomy name; $term[1] = term id
725
						if ( isset( $term[0] ) && isset( $term[1] ) ) {
726
							$term[1] = self::maybe_get_split_term( $term[1], $term[0] );
727
						}
728
729
						// All pages of the specified taxonomy.
730
						if ( ! isset( $term[1] ) || ! $term[1] ) {
731
							if ( is_tax( $term[0] ) ) {
732
								$condition_result = true;
733
							}
734
							else if ( is_singular() ) {
735
								if( in_array( $term[0], get_post_taxonomies() ) ) {
736
									$condition_result = true;
737
								}
738
							}
739
							break;
740
						}
741
742
						// All pages with the specified taxonomy term.
743
						if ( is_tax( $term[0], $term[1] ) ) {
744
							$condition_result = true;
745
						}
746
						else if ( is_singular() && has_term( $term[1], $term[0] ) ) {
747
							$condition_result = true;
748
						}
749
					break;
750
				}
751
752
				if ( $condition_result || self::$passed_template_redirect ) {
753
					// Some of the conditions will return false when checked before the template_redirect
754
					// action has been called, like is_page(). Only store positive lookup results, which
755
					// won't be false positives, before template_redirect, and everything after.
756
					$condition_result_cache[ $condition_key ] = $condition_result;
757
				}
758
			}
759
760
			/**
761
			 * Filters the condition result based on the current rule.
762
			 *
763
			 * @since 4.8.0
764
			 *
765
			 * @module widget-visibility
766
			 *
767
			 * @param boolean $result The current rule result.
768
			 * @param array $rule (
769
			 *   'major' => Array,
770
			 *   'minor' => Array,
771
			 *   'has_children' => Boolean
772
			 * ).
773
			 */
774
			$condition_result = apply_filters(
775
				'jetpack_widget_visibility_condition_result',
776
				$condition_result,
777
				$rule
778
			);
779
780
			if (
781
				isset( $instance['conditions']['match_all'] )
782
				&& $instance['conditions']['match_all'] == '1'
783
				&& ! $condition_result
784
			) {
785
786
				// In case the match_all flag was set we quit on first failed condition
787
				break;
788
			} elseif (
789
				(
790
					empty( $instance['conditions']['match_all'] )
791
					|| $instance['conditions']['match_all'] !== '1'
792
				)
793
				&& $condition_result
794
			) {
795
796
				// Only quit on first condition if the match_all flag was not set
797
				break;
798
			}
799
		}
800
801
		if (
802
			(
803
				'show' == $instance['conditions']['action']
804
				&& ! $condition_result
805
			) || (
806
				'hide' == $instance['conditions']['action']
807
				&& $condition_result
808
			)
809
		) {
810
			return false;
811
		}
812
813
		return $instance;
814
	}
815
816
	public static function strcasecmp_name( $a, $b ) {
817
		return strcasecmp( $a->name, $b->name );
818
	}
819
820
	public static function maybe_get_split_term( $old_term_id = '', $taxonomy = '' ) {
821
		$term_id = $old_term_id;
822
823
		if ( 'tag' == $taxonomy ) {
824
			$taxonomy = 'post_tag';
825
		}
826
827
		if ( function_exists( 'wp_get_split_term' ) && $new_term_id = wp_get_split_term( $old_term_id, $taxonomy ) ) {
828
			$term_id = $new_term_id;
829
		}
830
831
		return $term_id;
832
	}
833
834
	/**
835
	 * Upgrade routine to go through all widgets and move the Post Type
836
	 * setting to its newer location.
837
	 *
838
	 * @since 4.7.1
839
	 *
840
	 */
841
	static function migrate_post_type_rules() {
842
		global $wp_registered_widgets;
843
844
		$sidebars_widgets = get_option( 'sidebars_widgets' );
845
846
		// Going through all sidebars and through inactive and orphaned widgets
847
		foreach ( $sidebars_widgets as $s => $sidebar ) {
848
			if ( ! is_array( $sidebar ) ) {
849
				continue;
850
			}
851
852
			foreach ( $sidebar as $w => $widget ) {
853
				// $widget is the id of the widget
854
				if ( empty( $wp_registered_widgets[ $widget ] ) ) {
855
					continue;
856
				}
857
858
				$opts = $wp_registered_widgets[ $widget ];
859
				$instances = get_option( $opts['callback'][0]->option_name );
860
861
				// Going through each instance of the widget
862
				foreach( $instances as $number => $instance ) {
863
					if (
864
						! is_array( $instance ) ||
865
						empty( $instance['conditions'] ) ||
866
						empty( $instance['conditions']['rules'] )
867
					) {
868
						continue;
869
					}
870
871
					// Going through all visibility rules
872
					foreach( $instance['conditions']['rules'] as $index => $rule ) {
873
874
						// We only need Post Type rules
875
						if ( 'post_type' !== $rule['major'] ) {
876
							continue;
877
						}
878
879
						$rule_type = false;
880
881
						// Post type or type archive rule
882
						if ( 0 === strpos( $rule['minor'], 'post_type_archive' ) ) {
883
							$rule_type = 'post_type_archive';
884
						} else if ( 0 === strpos( $rule['minor'], 'post_type' ) ) {
885
							$rule_type = 'post_type';
886
						}
887
888
						if ( $rule_type ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $rule_type of type string|false is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== false instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
889
							$post_type = substr( $rule['minor'], strlen( $rule_type ) + 1 );
890
							$rule['minor'] = $rule_type . '-' . $post_type;
891
							$rule['major'] = 'page';
892
893
							$instances[ $number ]['conditions']['rules'][ $index ] = $rule;
894
						}
895
					}
896
				}
897
898
				update_option( $opts['callback'][0]->option_name, $instances );
899
			}
900
		}
901
	}
902
903
}
904
905
add_action( 'init', array( 'Jetpack_Widget_Conditions', 'init' ) );
906