MonsterInsights_Notifications::verify()   C
last analyzed

Complexity

Conditions 17
Paths 25

Size

Total Lines 53
Code Lines 24

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
cc 17
eloc 24
c 2
b 0
f 0
nc 25
nop 1
dl 0
loc 53
rs 5.2166

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
/**
4
 * Notifications.
5
 *
6
 * @since 7.10.5
7
 */
8
class MonsterInsights_Notifications {
9
10
	/**
11
	 * Source of notifications content.
12
	 *
13
	 * @since {VERSION}
14
	 *
15
	 * @var string
16
	 */
17
	const SOURCE_URL = 'https://plugin-cdn.monsterinsights.com/notifications.json';
18
19
	/**
20
	 * Option value.
21
	 *
22
	 * @since {VERSION}
23
	 *
24
	 * @var bool|array
25
	 */
26
	public $option = false;
27
28
	/**
29
	 * The name of the option used to store the data.
30
	 *
31
	 * @var string
32
	 */
33
	public $option_name = 'monsterinsights_notifications';
34
35
	/**
36
	 * MonsterInsights_Notifications constructor.
37
	 */
38
	public function __construct() {
39
		$this->init();
40
	}
41
42
	/**
43
	 * Initialize class.
44
	 *
45
	 * @since {VERSION}
46
	 */
47
	public function init() {
48
49
		$this->hooks();
50
	}
51
52
	/**
53
	 * Register hooks.
54
	 *
55
	 * @since {VERSION}
56
	 */
57
	public function hooks() {
58
		add_action( 'wp_ajax_monsterinsights_notification_dismiss', array( $this, 'dismiss' ) );
59
60
		add_action( 'wp_ajax_monsterinsights_vue_get_notifications', array( $this, 'ajax_get_notifications' ) );
61
62
		add_action( 'monsterinsights_admin_notifications_update', array( $this, 'update' ) );
63
64
	}
65
66
	/**
67
	 * Check if user has access and is enabled.
68
	 *
69
	 * @return bool
70
	 * @since {VERSION}
71
	 *
72
	 */
73
	public function has_access() {
74
75
		$access = false;
76
77
		if ( current_user_can( 'monsterinsights_view_dashboard' ) && ! monsterinsights_get_option( 'hide_am_notices', false ) ) {
78
			$access = true;
79
		}
80
81
		return apply_filters( 'monsterinsights_admin_notifications_has_access', $access );
82
	}
83
84
	/**
85
	 * Get option value.
86
	 *
87
	 * @param bool $cache Reference property cache if available.
88
	 *
89
	 * @return array
90
	 * @since {VERSION}
91
	 *
92
	 */
93
	public function get_option( $cache = true ) {
94
95
		if ( $this->option && $cache ) {
96
			return $this->option;
97
		}
98
99
		$option = get_option( $this->option_name, array() );
100
101
		$this->option = array(
102
			'update'    => ! empty( $option['update'] ) ? $option['update'] : 0,
103
			'events'    => ! empty( $option['events'] ) ? $option['events'] : array(),
104
			'feed'      => ! empty( $option['feed'] ) ? $option['feed'] : array(),
105
			'dismissed' => ! empty( $option['dismissed'] ) ? $option['dismissed'] : array(),
106
			'feed_fetched' => ! empty( $option['feed_fetched'] ),
107
		);
108
109
		return $this->option;
110
	}
111
112
	/**
113
	 * Fetch notifications from feed.
114
	 *
115
	 * @return array
116
	 * @since {VERSION}
117
	 *
118
	 */
119
	public function fetch_feed() {
120
121
		$res = wp_remote_get( self::SOURCE_URL );
122
123
		if ( is_wp_error( $res ) ) {
124
			return array();
125
		}
126
127
		$body = wp_remote_retrieve_body( $res );
128
129
		if ( empty( $body ) ) {
130
			return array();
131
		}
132
133
		return $this->verify( json_decode( $body, true ) );
134
	}
135
136
	/**
137
	 * Verify notification data before it is saved.
138
	 *
139
	 * @param array $notifications Array of notifications items to verify.
140
	 *
141
	 * @return array
142
	 * @since {VERSION}
143
	 *
144
	 */
145
	public function verify( $notifications ) { // phpcs:ignore Generic.Metrics.CyclomaticComplexity.TooHigh
146
147
		$data = array();
148
149
		if ( ! is_array( $notifications ) || empty( $notifications ) ) {
0 ignored issues
show
introduced by
The condition is_array($notifications) is always true.
Loading history...
150
			return $data;
151
		}
152
153
		$option = $this->get_option();
154
155
		foreach ( $notifications as $notification ) {
156
157
			// The message and license should never be empty, if they are, ignore.
158
			if ( empty( $notification['content'] ) || empty( $notification['type'] ) ) {
159
				continue;
160
			}
161
162
			// Ignore if license type does not match.
163
			$license_type = MonsterInsights()->license->get_license_type() ? MonsterInsights()->license->get_license_type() : 'lite';
0 ignored issues
show
Bug Best Practice introduced by
The property $license is declared protected in MonsterInsights_Lite. Since you implement __get, consider adding a @property or @property-read.
Loading history...
164
165
			if ( ! in_array( $license_type, $notification['type'] ) ) {
166
				continue;
167
			}
168
169
			// Ignore if notification is not ready to display(based on start time).
170
			if ( ! empty( $notification['start'] ) && time() < strtotime( $notification['start'] ) ) {
171
				continue;
172
			}
173
174
			// Ignore if expired.
175
			if ( ! empty( $notification['end'] ) && time() > strtotime( $notification['end'] ) ) {
176
				continue;
177
			}
178
179
			// Ignore if notification has already been dismissed.
180
			$notification_already_dismissed = false;
181
			if ( is_array( $option['dismissed'] ) && ! empty( $option['dismissed'] ) ) {
182
				foreach ( $option['dismissed'] as $dismiss_notification ) {
183
					if ( $notification['id'] === $dismiss_notification['id'] ) {
184
						$notification_already_dismissed = true;
185
						break;
186
					}
187
				}
188
			}
189
190
			if ( true === $notification_already_dismissed ) {
191
				continue;
192
			}
193
194
			$data[] = $notification;
195
		}
196
197
		return $data;
198
	}
199
200
	/**
201
	 * Verify saved notification data for active notifications.
202
	 *
203
	 * @param array $notifications Array of notifications items to verify.
204
	 *
205
	 * @return array
206
	 * @since {VERSION}
207
	 *
208
	 */
209
	public function verify_active( $notifications ) {
210
211
		if ( ! is_array( $notifications ) || empty( $notifications ) ) {
0 ignored issues
show
introduced by
The condition is_array($notifications) is always true.
Loading history...
212
			return array();
213
		}
214
215
		$license_type = MonsterInsights()->license->get_license_type() ? MonsterInsights()->license->get_license_type() : 'lite';
0 ignored issues
show
Bug Best Practice introduced by
The property $license is declared protected in MonsterInsights_Lite. Since you implement __get, consider adding a @property or @property-read.
Loading history...
216
217
		// Remove notifications that are not active, or if the license type not exists
218
		foreach ( $notifications as $key => $notification ) {
219
			if (
220
				( ! empty( $notification['start'] ) && time() < strtotime( $notification['start'] ) ) ||
221
				( ! empty( $notification['end'] ) && time() > strtotime( $notification['end'] ) ) ||
222
				( ! empty( $notification['type'] ) && ! in_array( $license_type, $notification['type'] ) )
223
			) {
224
				unset( $notifications[ $key ] );
225
			}
226
		}
227
228
		return $notifications;
229
	}
230
231
	/**
232
	 * Get notification data.
233
	 *
234
	 * @return array
235
	 * @since {VERSION}
236
	 *
237
	 */
238
	public function get() {
239
240
		if ( ! $this->has_access() ) {
241
			return array();
242
		}
243
244
		$option = $this->get_option();
245
246
		// Update notifications using async task.
247
		if ( empty( $option['update'] ) || time() > $option['update'] + DAY_IN_SECONDS ) {
248
			if ( false === wp_next_scheduled( 'monsterinsights_admin_notifications_update' ) ) {
249
				wp_schedule_single_event( time(), 'monsterinsights_admin_notifications_update' );
250
			}
251
		}
252
253
		// If cron has not finish running then display no notifications.
254
		if ( empty( $option['feed_fetched'] ) ) {
255
			return array();
256
		}
257
258
		$events = ! empty( $option['events'] ) ? $this->verify_active( $option['events'] ) : array();
259
		$feed   = ! empty( $option['feed'] ) ? $this->verify_active( $option['feed'] ) : array();
260
261
		$notifications              = array();
262
		$notifications['events']    = $events;
263
		$notifications['events']    = $this->get_notifications_with_human_readeable_start_time( $notifications['events'] );
264
		$notifications['events']    = $this->get_notifications_with_formatted_content( $notifications['events'] );
265
		$notifications['feed']      = $feed;
266
		$notifications['feed']      = $this->get_notifications_with_human_readeable_start_time( $notifications['feed'] );
267
		$notifications['feed']      = $this->get_notifications_with_formatted_content( $notifications['feed'] );
268
		$notifications['dismissed'] = ! empty( $option['dismissed'] ) ? $option['dismissed'] : array();
269
		$notifications['dismissed'] = $this->get_notifications_with_human_readeable_start_time( $notifications['dismissed'] );
270
		$notifications['dismissed'] = $this->get_notifications_with_formatted_content( $notifications['dismissed'] );
271
272
		return $notifications;
273
	}
274
275
	/**
276
	 * Improve format of the content of notifications before display. By default just runs wpautop.
277
	 *
278
	 * @param array $notifications The notifications to be parsed.
279
	 *
280
	 * @return mixed
281
	 */
282
	public function get_notifications_with_formatted_content( $notifications ) {
283
		if ( ! is_array( $notifications ) || empty( $notifications ) ) {
0 ignored issues
show
introduced by
The condition is_array($notifications) is always true.
Loading history...
284
			return $notifications;
285
		}
286
287
		foreach ( $notifications as $key => $notification ) {
288
			if ( ! empty( $notification['content'] ) ) {
289
				$notifications[ $key ]['content'] = wpautop( $notification['content'] );
290
				$notifications[ $key ]['content'] = apply_filters( 'monsterinsights_notification_content_display', $notifications[ $key ]['content'] );
291
			}
292
		}
293
294
		return $notifications;
295
	}
296
297
	/**
298
	 * Get notifications start time with human time difference
299
	 *
300
	 * @return array $notifications
301
	 *
302
	 * @since 7.12.3
303
	 */
304
	public function get_notifications_with_human_readeable_start_time( $notifications ) {
305
		if ( ! is_array( $notifications ) || empty( $notifications ) ) {
306
			return;
307
		}
308
309
		foreach ( $notifications as $key => $notification ) {
310
			if ( ! isset( $notification['start'] ) || empty( $notification['start'] ) ) {
311
				continue;
312
			}
313
314
			// Translators: Readable time to display
315
			$modified_start_time            = sprintf( __( '%1$s ago', 'google-analytics-for-wordpress' ), human_time_diff( strtotime( $notification['start'] ), current_time( 'timestamp' ) ) );
316
			$notifications[ $key ]['start'] = $modified_start_time;
317
		}
318
319
		return $notifications;
320
	}
321
322
	/**
323
	 * Get active notifications.
324
	 *
325
	 * @return array $notifications['active'] active notifications
326
	 *
327
	 * @since 7.12.3
328
	 */
329
	public function get_active_notifications() {
330
		$notifications = $this->get();
331
332
		// Show only 5 active notifications plus any that has a priority of 1
333
		$all_active = isset( $notifications['events'] ) ? $notifications['events'] : array();
334
		$all_feeds  = isset( $notifications['feed'] ) ? $notifications['feed'] : array();
335
		$displayed  = array();
336
337
		// Set 3 feeds.
338
		foreach ( $all_feeds as $notification ) {
339
			if ( count( $displayed ) < 3 ) {
340
				$displayed[] = $notification;
341
			}
342
		}
343
344
		foreach ( $all_active as $notification ) {
345
			if ( ( isset( $notification['priority'] ) && $notification['priority'] === 1 ) || count( $displayed ) < 5 ) {
346
				$displayed[] = $notification;
347
			}
348
		}
349
350
		return $displayed;
351
	}
352
353
	/**
354
	 * Get dismissed notifications.
355
	 *
356
	 * @return array $notifications['dismissed'] dismissed notifications
357
	 *
358
	 * @since 7.12.3
359
	 */
360
	public function get_dismissed_notifications() {
361
		$notifications = $this->get();
362
363
		return isset( $notifications['dismissed'] ) ? $notifications['dismissed'] : array();
364
	}
365
366
	/**
367
	 * Get notification count.
368
	 *
369
	 * @return int
370
	 * @since {VERSION}
371
	 *
372
	 */
373
	public function get_count() {
374
375
		return count( $this->get_active_notifications() );
376
	}
377
378
	/**
379
	 * Check if a notification has been dismissed before
380
	 *
381
	 * @param $notification
382
	 *
383
	 * @return bool
384
	 */
385
	public function is_dismissed( $notification ) {
386
		if ( empty( $notification['id'] ) ) {
387
			return true;
388
		}
389
390
		$option = $this->get_option();
391
392
		foreach ( $option['dismissed'] as $item ) {
393
			if ( $item['id'] === $notification['id'] ) {
394
				return true;
395
			}
396
		}
397
398
		return false;
399
	}
400
401
	/**
402
	 * Add a manual notification event.
403
	 *
404
	 * @param array $notification Notification data.
405
	 *
406
	 * @since {VERSION}
407
	 *
408
	 */
409
	public function add( $notification ) {
410
411
		if ( empty( $notification['id'] ) || $this->is_dismissed( $notification ) ) {
412
			return false;
413
		}
414
415
		$option = $this->get_option( false );
416
417
		$current_notifications = $option['events'];
418
419
		foreach ( $current_notifications as $item ) {
420
			if ( $item['id'] === $notification['id'] ) {
421
				return false;
422
			}
423
		}
424
425
		$notification = $this->verify( array( $notification ) );
426
427
		$notifications = array_merge( $notification, $current_notifications );
428
429
		//  Sort notifications by priority
430
		usort( $notifications, function ( $a, $b ) {
431
			if ( ! isset( $a['priority'] ) || ! isset( $b['priority'] ) ) {
432
				return 0;
433
			}
434
435
			if ( $a['priority'] == $b['priority'] ) {
436
				return 0;
437
			}
438
439
			return $a['priority'] < $b['priority'] ? - 1 : 1;
440
		} );
441
442
		update_option(
443
			$this->option_name,
444
			array(
445
				'update'    => $option['update'],
446
				'feed'      => $option['feed'],
447
				'events'    => $notifications,
448
				'dismissed' => $option['dismissed'],
449
				'feed_fetched' => $option['feed_fetched'],
450
			),
451
			false
452
		);
453
454
		return true;
455
	}
456
457
	/**
458
	 * Update notification data from feed.
459
	 *
460
	 * @param array $option (Optional) Added @since 7.13.2
461
	 *
462
	 * @since {VERSION}
463
	 */
464
	public function update() {
465
466
		$feed   = $this->fetch_feed();
467
		$option = $this->get_option();
468
469
		update_option(
470
			$this->option_name,
471
			array(
472
				'update'    => time(),
473
				'feed'      => $feed,
474
				'events'    => $option['events'],
475
				'dismissed' => array_slice( $option['dismissed'], 0, 30 ), // Limit dismissed notifications to last 30.
476
				'feed_fetched' => true,
477
			),
478
			false
479
		);
480
	}
481
482
	/**
483
	 * Dismiss notification via AJAX.
484
	 *
485
	 * @since {VERSION}
486
	 */
487
	public function dismiss() {
488
		// Run a security check.
489
		check_ajax_referer( 'mi-admin-nonce', 'nonce' );
490
491
		// Check for access and required param.
492
		if ( ! $this->has_access() || empty( $_POST['id'] ) ) {
493
			wp_send_json_error();
494
		}
495
496
		$id     = sanitize_text_field( wp_unslash( $_POST['id'] ) );
497
		$option = $this->get_option();
498
499
		// Dismiss all notifications and add them to dissmiss array.
500
		if ( 'all' === $id ) {
501
			if ( is_array( $option['feed'] ) && ! empty( $option['feed'] ) ) {
502
				foreach ( $option['feed'] as $key => $notification ) {
503
					array_unshift( $option['dismissed'], $notification );
504
					unset( $option['feed'][ $key ] );
505
				}
506
			}
507
			if ( is_array( $option['events'] ) && ! empty( $option['events'] ) ) {
508
				foreach ( $option['events'] as $key => $notification ) {
509
					array_unshift( $option['dismissed'], $notification );
510
					unset( $option['events'][ $key ] );
511
				}
512
			}
513
		}
514
515
		$type = is_numeric( $id ) ? 'feed' : 'events';
516
517
		// Remove notification and add in dismissed array.
518
		if ( is_array( $option[ $type ] ) && ! empty( $option[ $type ] ) ) {
519
			foreach ( $option[ $type ] as $key => $notification ) {
520
				if ( $notification['id'] == $id ) { // phpcs:ignore WordPress.PHP.StrictComparisons
521
					// Add notification to dismissed array.
522
					array_unshift( $option['dismissed'], $notification );
523
					// Remove notification from feed or events.
524
					unset( $option[ $type ][ $key ] );
525
					break;
526
				}
527
			}
528
		}
529
530
		update_option( $this->option_name, $option, false );
531
532
		wp_send_json_success();
533
	}
534
535
	/**
536
	 * This generates the markup for the notifications indicator if needed.
537
	 *
538
	 * @return string
539
	 */
540
	public function get_menu_count() {
541
542
		if ( $this->get_count() > 0 ) {
543
			return '<span class="monsterinsights-menu-notification-indicator update-plugins">' . $this->get_count() . '</span>';
544
		}
545
546
		return '';
547
548
	}
549
550
	/**
551
	 * Retrieve the notifications via an ajax call.
552
	 */
553
	public function ajax_get_notifications() {
554
555
		// Run a security check.
556
		check_ajax_referer( 'mi-admin-nonce', 'nonce' );
557
558
		$notifications_data = array(
559
			'notifications' => $this->get_active_notifications(),
560
			'dismissed'     => $this->get_dismissed_notifications(),
561
			'view_url'      => $this->get_view_url( 'monsterinsights-report-overview', 'monsterinsights_reports' ),
562
			'sidebar_url'   => $this->get_sidebar_url(),
563
		);
564
565
		wp_send_json_success( $notifications_data );
566
	}
567
568
	/**
569
	 * Get the URL for the page where users can see/read notifications.
570
	 *
571
	 * @return string
572
	 */
573
	public function get_view_url( $scroll_to, $page, $tab = '' ) {
574
		$disabled = monsterinsights_get_option( 'dashboards_disabled', false );
575
576
		$url = add_query_arg( array(
577
			'page'                      => $page,
578
			'monsterinsights-scroll'    => $scroll_to,
579
			'monsterinsights-highlight' => $scroll_to,
580
		), admin_url( 'admin.php' ) );
581
582
		if ( ! empty( $tab ) ) {
583
			$url .= '#/' . $tab;
584
		}
585
586
		if ( false !== $disabled ) {
0 ignored issues
show
introduced by
The condition false !== $disabled is always true.
Loading history...
587
			$url = is_multisite() ? network_admin_url( 'admin.php?page=monsterinsights_network' ) : admin_url( 'admin.php?page=monsterinsights_settings' );
588
		}
589
590
		return $url;
591
592
	}
593
594
	/**
595
	 * Get the notification sidebar URL for the page where users can see/read notifications.
596
	 *
597
	 * @return string
598
	 */
599
	public function get_sidebar_url() {
600
601
		$disabled = monsterinsights_get_option( 'dashboards_disabled', false );
602
603
		$url = add_query_arg(
604
			array(
605
				'page' => 'monsterinsights_reports',
606
				'open' => 'monsterinsights_notification_sidebar',
607
			),
608
			admin_url( 'admin.php' )
609
		);
610
611
		if ( false !== $disabled ) {
0 ignored issues
show
introduced by
The condition false !== $disabled is always true.
Loading history...
612
			$url = is_multisite() ? network_admin_url( 'admin.php?page=monsterinsights_network' ) : admin_url( 'admin.php?page=monsterinsights_settings' );
613
		}
614
615
		return $url;
616
	}
617
618
	/**
619
	 * Delete the notification options.
620
	 */
621
	public function delete_notifications_data() {
622
623
		delete_option( $this->option_name );
624
625
		// Delete old notices option.
626
		delete_option( 'monsterinsights_notices' );
627
628
		monsterinsights_notification_event_runner()->delete_data();
629
630
	}
631
632
	/**
633
	 * This generates the markup for the notifications indicator for expired license.
634
	 *
635
	 * @return string
636
	 */
637
	public function get_license_expired_indicator() {
638
			return '<span class="monsterinsights-menu-notification-indicator expired-license">!</span>';
639
	}
640
}
641