PluginProbe ʕ •ᴥ•ʔ
Custom Twitter Feeds – A Tweets Widget or X Feed Widget / 2.7.0
Custom Twitter Feeds – A Tweets Widget or X Feed Widget v2.7.0
2.8.0 2.7.0 2.6.1 2.6.0 1.0 1.0.1 1.1 1.1.1 1.1.2 1.1.3 1.1.4 1.1.5 1.1.6 1.1.7 1.1.8 1.2 1.2.1 1.2.10 1.2.11 1.2.2 1.2.3 1.2.4 1.2.5 1.2.6 1.2.7 1.2.8 1.2.9 1.3 1.4 1.4.1 1.5 1.5.1 1.6 1.6.1 1.7 1.8 1.8.1 1.8.2 1.8.3 1.8.4 2.0 2.0.1 2.0.2 2.0.3 2.0.4 2.0.5 2.0.6 2.0.7 2.1 2.1.1 2.1.2 2.2 2.2.1 2.2.2 2.2.3 2.2.4 2.2.5 2.3.0 2.3.1 2.5.4 2.5.5 trunk
custom-twitter-feeds / inc / Admin / CTF_Notifications.php
custom-twitter-feeds / inc / Admin Last commit date
Traits 1 month ago CTF_About_Us.php 1 month ago CTF_Admin_Notices.php 1 month ago CTF_Cache_Handler.php 1 month ago CTF_Global_Settings.php 1 month ago CTF_HTTP_Request.php 1 month ago CTF_New_User.php 1 month ago CTF_Notifications.php 1 month ago CTF_Response.php 1 month ago CTF_Support.php 1 month ago CTF_Upgrader.php 1 month ago CTF_View.php 1 month ago PluginSilentUpgrader.php 1 month ago PluginSilentUpgraderSkin.php 1 month ago SB_Twitter_Cache.php 1 month ago addon-functions.php 1 month ago class-install-skin.php 1 month ago
CTF_Notifications.php
771 lines
1 <?php
2 /**
3 * CTF_Notifications.
4 *
5 * @since 2.0
6 */
7 namespace TwitterFeed\Admin;
8
9 // Exit if accessed directly
10 if ( ! defined( 'ABSPATH' ) ) {
11 exit;
12 }
13
14 class CTF_Notifications {
15
16 /**
17 * Source of notifications content.
18 *
19 * @var string
20 */
21 const SOURCE_URL = 'https://plugin.smashballoon.com/notifications.json';
22
23 /**
24 * @var string
25 */
26 const OPTION_NAME = 'ctf_notifications';
27
28 /**
29 * JSON data contains notices for all plugins. This is used
30 * to select messages only meant for this plugin
31 *
32 * @var string
33 */
34 const PLUGIN = 'twitter';
35
36 /**
37 * Option value.
38 *
39 * @since 2.0
40 *
41 * @var bool|array
42 */
43 public $option = false;
44
45 /**
46 * Initialize class.
47 *
48 * @since 2.0
49 */
50 public function init() {
51 $this->hooks();
52 }
53
54 /**
55 * Use this function to get the option name to allow
56 * inheritance for the New_User class
57 *
58 * @return string
59 */
60 public function option_name() {
61 return self::OPTION_NAME;
62 }
63
64 /**
65 * Use this function to get the source URL to allow
66 * inheritance for the New_User class
67 *
68 * @return string
69 */
70 public function source_url() {
71 return self::SOURCE_URL;
72 }
73
74 /**
75 * Register hooks.
76 *
77 * @since 2.0
78 */
79 public function hooks() {
80 add_action( 'admin_enqueue_scripts', array( $this, 'enqueues' ) );
81
82 add_action( 'ctf_admin_notices', array( $this, 'output' ) );
83
84 // on cron. Once a week?
85 add_action( 'ctf_notification_update', array( $this, 'update' ) );
86
87 add_action( 'wp_ajax_ctf_dashboard_notification_dismiss', array( $this, 'dismiss' ) );
88 }
89
90
91 /**
92 * Check if user has access and is enabled.
93 *
94 * @since 2.0
95 *
96 * @return bool
97 */
98 public function has_access() {
99 $access = false;
100
101 if ( current_user_can( 'manage_twitter_feed_options' ) || current_user_can( 'manage_options' ) ) {
102 $access = true;
103 }
104
105 return apply_filters( 'ctf_admin_notifications_has_access', $access );
106 }
107
108 /**
109 * Get option value.
110 *
111 * @since 2.0
112 *
113 * @param bool $cache Reference property cache if available.
114 *
115 * @return array
116 */
117 public function get_option( $cache = true ) {
118 if ( $this->option && $cache ) {
119 return $this->option;
120 }
121
122 $option = get_option( $this->option_name(), array() );
123
124 $this->option = array(
125 'update' => ! empty( $option['update'] ) ? $option['update'] : 0,
126 'events' => ! empty( $option['events'] ) ? $option['events'] : array(),
127 'feed' => ! empty( $option['feed'] ) ? $option['feed'] : array(),
128 'dismissed' => ! empty( $option['dismissed'] ) ? $option['dismissed'] : array(),
129 );
130
131 return $this->option;
132 }
133
134 /**
135 * Fetch notifications from feed.
136 *
137 * @since 2.0
138 *
139 * @return array
140 */
141 public function fetch_feed() {
142 $res = wp_remote_get( $this->source_url() );
143
144 if ( is_wp_error( $res ) ) {
145 return array();
146 }
147
148 $body = wp_remote_retrieve_body( $res );
149
150 if ( empty( $body ) ) {
151 return array();
152 }
153
154 return $this->verify( json_decode( $body, true ) );
155 }
156
157 /**
158 * Verify notification data before it is saved.
159 *
160 * @since 2.0
161 *
162 * @param array $notifications Array of notifications items to verify.
163 *
164 * @return array
165 */
166 public function verify( $notifications ) { // phpcs:ignore Generic.Metrics.CyclomaticComplexity.TooHigh
167 $data = array();
168
169 if ( ! is_array( $notifications ) || empty( $notifications ) ) {
170 return $data;
171 }
172
173 $option = $this->get_option();
174
175 foreach ( $notifications as $notification ) {
176 // Ignore if not a targeted plugin
177 if ( ! empty( $notification['plugin'] ) && is_array( $notification['plugin'] ) && ! in_array( self::PLUGIN, $notification['plugin'], true ) ) {
178 continue;
179 }
180
181 // Ignore if max wp version detected
182 if ( ! empty( $notification['maxwpver'] ) && version_compare( get_bloginfo( 'version' ), $notification['maxwpver'], '>' ) ) {
183 continue;
184 }
185
186 // Ignore if max version has been reached
187 if ( ! empty( $notification['maxver'] ) && version_compare( $notification['maxver'], CTF_VERSION ) < 0 ) {
188 continue;
189 }
190
191 // Ignore if min version has not been reached
192 if ( ! empty( $notification['minver'] ) && version_compare( $notification['minver'], CTF_VERSION ) > 0 ) {
193 continue;
194 }
195
196 // Ignore if a specific ctf_status is empty or false
197 if ( ! empty( $notification['statuscheck'] ) ) {
198 $status_key = sanitize_key( $notification['statuscheck'] );
199 $ctf_statuses_option = get_option( 'ctf_statuses', array() );
200
201 if ( empty( $ctf_statuses_option[ $status_key ] ) ) {
202 continue;
203 }
204 }
205
206 // The message and license should never be empty, if they are, ignore.
207 if ( empty( $notification['content'] ) || empty( $notification['type'] ) ) {
208 continue;
209 }
210
211 // Ignore if license type does not match.
212 $license = ctf_is_pro_version() ? 'pro' : 'free';
213
214 if ( ! in_array( $license, $notification['type'], true ) ) {
215 continue;
216 }
217
218 // Ignore if expired.
219 if ( ! empty( $notification['end'] ) && ctf_get_current_time() > strtotime( $notification['end'] ) ) {
220 continue;
221 }
222
223 // Ignore if notification has already been dismissed.
224 if ( ! empty( $option['dismissed'] ) && in_array( $notification['id'], $option['dismissed'] ) ) { // phpcs:ignore WordPress.PHP.StrictInArray.MissingTrueStrict
225 continue;
226 }
227
228 // Ignore message "9".
229 if ( in_array( (int)$notification['id'], array( 9 ) ) ) { // phpcs:ignore WordPress.PHP.StrictInArray.MissingTrueStrict
230 continue;
231 }
232
233 // TODO: Ignore if notification existed before installing CTF.
234 // Prevents bombarding the user with notifications after activation.
235 $activated = false;
236 if ( ! empty( $activated )
237 && ! empty( $notification['start'] )
238 && $activated > strtotime( $notification['start'] ) ) {
239 continue;
240 }
241
242 $data[] = $notification;
243 }
244
245 return $data;
246 }
247
248 /**
249 * Verify saved notification data for active notifications.
250 *
251 * @since 2.0
252 *
253 * @param array $notifications Array of notifications items to verify.
254 *
255 * @return array
256 */
257 public function verify_active( $notifications ) {
258 if ( ! is_array( $notifications ) || empty( $notifications ) ) {
259 return array();
260 }
261
262 // Remove notfications that are not active.
263 foreach ( $notifications as $key => $notification ) {
264 if ( ( ! empty( $notification['start'] ) && ctf_get_current_time() < strtotime( $notification['start'] ) )
265 || ( ! empty( $notification['end'] ) && ctf_get_current_time() > strtotime( $notification['end'] ) ) ) {
266 unset( $notifications[ $key ] );
267 }
268
269 if ( empty( $notification['recent_install_override'] ) && $this->recently_installed() ) {
270 unset( $notifications[ $key ] );
271 }
272
273 // Ignore if max version has been reached
274 if ( ! empty( $notification['maxver'] ) && version_compare( $notification['maxver'], CTF_VERSION ) < 0 ) {
275 unset( $notifications[ $key ] );
276 }
277
278 // Ignore if max wp version detected
279 if ( ! empty( $notification['maxwpver'] ) && version_compare( get_bloginfo( 'version' ), $notification['maxwpver'], '>' ) ) {
280 unset( $notifications[ $key ] );
281 }
282
283 // Ignore if min version has not been reached
284 if ( ! empty( $notification['minver'] ) && version_compare( $notification['minver'], CTF_VERSION ) > 0 ) {
285 unset( $notifications[ $key ] );
286 }
287
288 // Ignore if a specific ctf_status is empty or false
289 if ( ! empty( $notification['statuscheck'] ) ) {
290 $status_key = sanitize_key( $notification['statuscheck'] );
291 $ctf_statuses_option = get_option( 'ctf_statuses', array() );
292
293 if ( empty( $ctf_statuses_option[ $status_key ] ) ) {
294 unset( $notifications[ $key ] );
295 }
296 }
297 }
298
299 return $notifications;
300 }
301
302 /**
303 * @return bool
304 *
305 * @since 1.4.5/1.4.2
306 */
307 public function recently_installed() {
308 $ctf_statuses_option = get_option( 'ctf_statuses', array() );
309
310 if ( ! isset( $ctf_statuses_option['first_install'] ) ) {
311 return false;
312 }
313
314 // Plugin was installed less than a week ago
315 if ( (int) $ctf_statuses_option['first_install'] > time() - WEEK_IN_SECONDS ) {
316 return true;
317 }
318
319 return false;
320 }
321
322 /**
323 * Get notification data.
324 *
325 * @since 2.0
326 *
327 * @return array
328 */
329 public function get() {
330 if ( ! $this->has_access() ) {
331 return array();
332 }
333
334 $option = $this->get_option();
335
336 // Update notifications using async task.
337 if ( empty( $option['update'] ) || ctf_get_current_time() > $option['update'] + DAY_IN_SECONDS ) {
338 $this->update();
339 }
340
341 $events = ! empty( $option['events'] ) ? $this->verify_active( $option['events'] ) : array();
342 $feed = ! empty( $option['feed'] ) ? $this->verify_active( $option['feed'] ) : array();
343
344 // If there is a new user notification, add it to the beginning of the notification list
345 $ctf_newuser = new CTF_New_User();
346 $newuser_notifications = $ctf_newuser->get();
347
348 if ( ! empty( $newuser_notifications ) ) {
349 $events = array_merge( $newuser_notifications, $events );
350 }
351
352 return array_merge( $events, $feed );
353 }
354
355 /**
356 * Get notification count.
357 *
358 * @since 2.0
359 *
360 * @return int
361 */
362 public function get_count() {
363 return count( $this->get() );
364 }
365
366 /**
367 * Add a manual notification event.
368 *
369 * @since 2.0
370 *
371 * @param array $notification Notification data.
372 */
373 public function add( $notification ) {
374 if ( empty( $notification['id'] ) ) {
375 return;
376 }
377
378 $option = $this->get_option();
379
380 if ( in_array( $notification['id'], $option['dismissed'] ) ) { // phpcs:ignore WordPress.PHP.StrictInArray.MissingTrueStrict
381 return;
382 }
383
384 foreach ( $option['events'] as $item ) {
385 if ( $item['id'] === $notification['id'] ) {
386 return;
387 }
388 }
389
390 $notification = $this->verify( array( $notification ) );
391
392 update_option(
393 'ctf_notifications',
394 array(
395 'update' => $option['update'],
396 'feed' => $option['feed'],
397 'events' => array_merge( $notification, $option['events'] ),
398 'dismissed' => $option['dismissed'],
399 )
400 );
401 }
402
403 /**
404 * Update notification data from feed.
405 *
406 * @since 2.0
407 */
408 public function update() {
409 $feed = $this->fetch_feed();
410 $option = $this->get_option();
411
412 update_option(
413 'ctf_notifications',
414 array(
415 'update' => ctf_get_current_time(),
416 'feed' => $feed,
417 'events' => $option['events'],
418 'dismissed' => $option['dismissed'],
419 )
420 );
421 }
422
423 /**
424 * Admin area Form Overview enqueues.
425 *
426 * @since 2.0
427 */
428 public function enqueues() {
429 if ( ! $this->has_access() ) {
430 return;
431 }
432
433 $notifications = $this->get();
434
435 if ( empty( $notifications ) ) {
436 return;
437 }
438
439 $min = '';
440
441 wp_enqueue_style(
442 'ctf-admin-notifications',
443 CTF_PLUGIN_URL . "css/admin-notifications{$min}.css",
444 array(),
445 CTF_VERSION
446 );
447
448 wp_enqueue_script(
449 'ctf-admin-notifications',
450 CTF_PLUGIN_URL . "js/admin-notifications{$min}.js",
451 array( 'jquery' ),
452 CTF_VERSION,
453 true
454 );
455 }
456
457 /**
458 * Fields from the remote source contain placeholders to allow
459 * some messages to be used for multiple plugins.
460 *
461 * @param $content string
462 * @param $notification array
463 *
464 * @return string
465 *
466 * @since 2.0
467 */
468 public function replace_merge_fields( $content, $notification ) {
469 $merge_fields = array(
470 '{plugin}' => 'Twitter Feed',
471 '{amount}' => isset( $notification['amount'] ) ? $notification['amount'] : '',
472 '{platform}' => 'Twitter',
473 '{lowerplatform}' => 'twitter',
474 '{review-url}' => 'https://wordpress.org/support/plugin/custom-twitter-feeds/reviews/',
475 '{slug}' => 'custom-twitter-feeds',
476 '{campaign}' => 'twitter-free'
477 );
478
479 if ( ctf_is_pro_version() ) {
480 $merge_fields['{campaign}'] = 'twitter-pro';
481 $merge_fields['{plugin}'] = 'Twitter Feed Pro';
482 }
483
484 foreach ( $merge_fields as $find => $replace ) {
485 $content = str_replace( $find, $replace, $content );
486 }
487
488 return $content;
489 }
490
491 /**
492 * Output notifications on Twitter Feed admin area.
493 *
494 * @since 2.0
495 */
496 public function output() {
497 // if we are one single feed page then return
498 if ( isset( $_GET['feed_id'] ) ) {
499 return;
500 }
501
502 $notifications = $this->get();
503
504 if ( empty( $notifications ) ) {
505 return;
506 }
507
508 $is_review_notice = ! empty( $notifications[0] ) && ! empty( $notifications[0]['id'] ) && $notifications[0]['id'] === 'review';
509
510 if ( ! $is_review_notice && ! empty( $_GET['feed_id'] ) ) {
511 return;
512 }
513
514 $notifications_html = '';
515 $current_class = ' current';
516 $content_allowed_tags = array(
517 'em' => array(),
518 'strong' => array(),
519 'br' => array(),
520 'span' => array(
521 'style' => array(),
522 ),
523 'a' => array(
524 'href' => array(),
525 'target' => array(),
526 'rel' => array(),
527 ),
528 );
529
530 foreach ( $notifications as $notification ) {
531 $type = $notification['id'];
532 // Buttons HTML.
533 $buttons_html = '';
534 if ( ! empty( $notification['btns'] ) && is_array( $notification['btns'] ) ) {
535 foreach ( $notification['btns'] as $btn_type => $btn ) {
536 if ( $type == 'review' || $type == 'discount' ) {
537 $btn_class = $btn_type === 'primary' ? 'ctf-btn-blue' : 'ctf-btn-grey';
538 } else {
539 $btn_class = $btn_type === 'primary' ? 'ctf-btn-orange' : 'ctf-btn-grey';
540 }
541 if ( is_array( $btn['url'] ) ) {
542 $args = array();
543 foreach ( $btn['url'] as $key => $value ) {
544 $args[ sanitize_key( $key ) ] = sanitize_key( $value );
545 }
546 $btn['url'] = add_query_arg( $args );
547 }
548 if ( ! empty( $btn['attr'] ) ) {
549 $btn['target'] = '_blank';
550 }
551 if ( empty( $btn['class'] ) ) {
552 $btn['class'] = '';
553 }
554 $buttons_html .= sprintf(
555 '<a href="%1$s" class="ctf-btn %2$s %3$s"%4$s>%5$s</a>',
556 ! empty( $btn['url'] ) ? esc_url( $this->replace_merge_fields( str_replace( 'sbi_', 'ctf_', $btn['url'] ), $notification ) ) : '',
557 esc_attr( $btn['class'] ),
558 esc_attr( $btn_class ),
559 ! empty( $btn['target'] ) && $btn['target'] === '_blank' ? ' target="_blank" rel="noopener noreferrer"' : '',
560 ! empty( $btn['text'] ) ? sanitize_text_field( $btn['text'] ) : ''
561 );
562 }
563 $buttons_html = ! empty( $buttons_html ) ? '<div class="buttons">' . $buttons_html . '</div>' : '';
564 }
565
566 if ( empty( $notification['image'] ) ) {
567 $image_html = '<div class="bell">';
568
569 $image_html .= '<svg xmlns="http://www.w3.org/2000/svg" width="42" height="48" viewBox="0 0 42 48"><defs><style>.a{fill:#777;}.b{fill:#ca4a1f;}</style></defs><path class="a" d="M23-79a6.005,6.005,0,0,1-6-6h10.06a12.066,12.066,0,0,0,1.791,1.308,6.021,6.021,0,0,1-2.077,3.352A6.008,6.008,0,0,1,23-79Zm1.605-9H5.009a2.955,2.955,0,0,1-2.173-.923A3.088,3.088,0,0,1,2-91a2.919,2.919,0,0,1,.807-2.036c.111-.12.229-.243.351-.371a14.936,14.936,0,0,0,3.126-4.409A23.283,23.283,0,0,0,8.007-107.5a14.846,14.846,0,0,1,.906-5.145,14.5,14.5,0,0,1,2.509-4.324A15.279,15.279,0,0,1,20-122.046V-124a3,3,0,0,1,3-3,3,3,0,0,1,3,3v1.954a15.28,15.28,0,0,1,8.58,5.078,14.5,14.5,0,0,1,2.509,4.324,14.846,14.846,0,0,1,.906,5.145c0,.645.016,1.281.047,1.888A12.036,12.036,0,0,0,35-106a11.921,11.921,0,0,0-8.485,3.515A11.923,11.923,0,0,0,23-94a12,12,0,0,0,1.6,6Z" transform="translate(-2 127)"/><circle class="b" cx="9" cy="9" r="9" transform="translate(24 24)"/></svg>';
570 $image_html .= '</div>';
571 } else {
572 if ( $notification['image'] === 'balloon' ) {
573 $image_html = sprintf(
574 '<div class="bell"><img src="%s" alt="notice">',
575 CTF_PLUGIN_URL . 'admin/assets/img/balloon.svg' );
576 } else if ( $notification['id'] === 'review' || $notification['id'] === 'discount' ) {
577 $image_html = sprintf(
578 '<div class="bell"><img src="%s" alt="notice">',
579 CTF_PLUGIN_URL . 'admin/assets/img/' . sanitize_text_field( str_replace( array( 'sbi', '.png' ), array( 'ctf', '.svg' ), $notification['image'] ) )
580 );
581 } else {
582 $image_html = '<div class="thumb">';
583 $img_src = CTF_PLUGIN_URL . 'admin/assets/img/' . sanitize_text_field( $notification['image'] );
584 $image_html .= '<img src="'.esc_url( $img_src ).'" alt="notice">';
585
586 if ( isset( $notification['image_overlay'] ) ) {
587 $image_html .= '<div class="img-overlay">'. esc_html( str_replace( '%', '%%', $notification['image_overlay'] ) ).'</div>';
588 }
589 }
590 $image_html .= '</div>';
591
592 }
593
594 // Check if it's review notice then show step #1
595 if ( $type == 'review' ) {
596 $step1_img = CTF_PLUGIN_URL . 'admin/assets/img/' . sanitize_text_field( str_replace( array( 'sbi', 'png' ), array( 'ctf', 'svg' ), $notification['image'] ) );
597 $step1_img_html = sprintf('<div class="bell"><img src="%s" alt="notice"></div>', $step1_img);
598
599 $review_consent = get_option( 'ctf_review_consent' );
600 $ctf_open_feedback_url = 'https://smashballoon.com/feedback/?plugin=twitter-free&utm_campaign=twitter-free&utm_source=notifications&utm_medium=feedback';
601 // step #1 for the review notice
602 if ( ! $review_consent ) {
603 $step1_btns = sprintf(
604 '<button class="ctf-btn-link" id="ctf_review_consent_yes">%s</button>',
605 __( 'Yes', 'custom-twitter-feeds' )
606 );
607 $step1_btns .= sprintf(
608 '<a href="%s" target="_blank" rel="nofollow noopener" class="ctf-btn-link" id="ctf_review_consent_no">%s</a>',
609 $ctf_open_feedback_url,
610 __( 'No', 'custom-twitter-feeds' )
611 );
612 $notifications_html .= sprintf(
613 '<div class="ctf_review_step1_notice" data-message-id="%3$s">' . $step1_img_html . '
614 <h3 class="title">%1$s</h3>
615 <div class="review-step-1-btns">%2$s</div>
616 </div>',
617 __( 'Are you enjoying the Custom Twitter Feeds Plugin?', 'custom-twitter-feeds' ),
618 $step1_btns,
619 ! empty( $notification['id'] ) ? esc_attr( sanitize_text_field( $notification['id'] ) ) : 0
620 );
621 }
622 }
623
624 $review_consent = get_option( 'ctf_review_consent' );
625 $review_step2_style = '';
626 if ( $type == 'review' && ! $review_consent ) {
627 $review_step2_style = 'style="display: none;"';
628 }
629
630 // Build the notification HTML for review notice
631 if ( $type == 'review' ) {
632 $notifications_html .= sprintf(
633 '<div class="message%5$s %7$s" data-message-id="%4$s" %6$s>' . $image_html . '
634 <h3 class="title">%1$s</h3>
635 <p class="content">%2$s</p>
636 %3$s
637 </div>',
638 __( 'Glad to hear you are enjoying it. Would you consider leaving a positive review?', 'custom-twitter-feeds' ),
639 __( 'It really helps to support the plugin and help others to discover it too!', 'custom-twitter-feeds' ),
640 $buttons_html,
641 ! empty( $notification['id'] ) ? esc_attr( sanitize_text_field( $notification['id'] ) ) : 0,
642 $current_class,
643 ( $notification['id'] == 'review' && ! empty( $review_step2_style ) ) ? $review_step2_style : '',
644 ( $type == 'review' ) ? 'rn_step_2' : ''
645 );
646 } else if ( $type == 'discount' ) {
647 // Build the notification HTML for discount notice
648 $notifications_html .= sprintf(
649 '<div class="message%5$s %7$s" data-message-id="%4$s" %6$s>' . $image_html . '
650 <h3 class="title">%1$s</h3>
651 <p class="content">%2$s</p>
652 %3$s
653 </div>',
654 __( 'Exclusive offer - 60% off!', 'custom-twitter-feeds' ),
655 __( 'We don’t run promotions very often, but for a limited time we’re offering 60% Off our Pro version to all users of our free Custom Twitter Feeds.', 'custom-twitter-feeds' ),
656 $buttons_html,
657 ! empty( $notification['id'] ) ? esc_attr( sanitize_text_field( $notification['id'] ) ) : 0,
658 $current_class,
659 ( $notification['id'] == 'review' && ! empty( $review_step2_style ) ) ? $review_step2_style : '',
660 ( $type == 'review' && ! $review_consent ) ? 'rn_step_2' : ''
661 );
662 } else {
663 // Notification HTML for other notices
664 $notifications_html .= sprintf(
665 '<div class="message%5$s" data-message-id="%4$s" %6$s>' . $image_html . '
666 <h3 class="title">%1$s</h3>
667 <p class="content">%2$s</p>
668 %3$s
669 </div>',
670 ! empty( $notification['title'] ) ? $this->replace_merge_fields( sanitize_text_field( $notification['title'] ), $notification ) : '',
671 ! empty( $notification['content'] ) ? wp_kses( $this->replace_merge_fields( $notification['content'], $notification ), $content_allowed_tags ) : '',
672 $buttons_html,
673 ! empty( $notification['id'] ) ? esc_attr( sanitize_text_field( $notification['id'] ) ) : 0,
674 $current_class,
675 ( $notification['id'] == 'review' && ! empty( $review_step2_style ) ) ? $review_step2_style : ''
676 );
677 }
678
679 // Only first notification is current.
680 $current_class = '';
681 }
682
683 $close_href = add_query_arg( array( 'ctf_dismiss' => $type ) );
684 $type_class = '';
685 if ( $type === 'review' || $type == 'discount' ) {
686 $type_class = $type === 'review' ? 'ctf_review_notice' : 'ctf_discount_notice';
687 }
688
689 ?>
690
691 <div id="ctf-notifications" class="<?php echo esc_attr( $type_class ); ?>">
692 <a
693 class="dismiss"
694 title="<?php echo esc_attr__( 'Dismiss this message', 'custom-twitter-feeds' ); ?>"
695 <?php echo ( $type == 'review' || $type == 'discount' ) ? 'href="'. esc_attr( $close_href ) .'"' : '' ?>
696 >
697 <svg width="10" height="10" viewBox="0 0 10 10" fill="none" xmlns="http://www.w3.org/2000/svg">
698 <path d="M9.66683 1.27325L8.72683 0.333252L5.00016 4.05992L1.2735 0.333252L0.333496 1.27325L4.06016 4.99992L0.333496 8.72659L1.2735 9.66659L5.00016 5.93992L8.72683 9.66659L9.66683 8.72659L5.94016 4.99992L9.66683 1.27325Z" fill="white"/>
699 </svg>
700 </a>
701
702 <?php if ( count( $notifications ) > 1 ) : ?>
703 <div class="navigation">
704 <a class="prev disabled" title="<?php echo esc_attr__( 'Previous message', 'custom-twitter-feeds' ); ?>"><svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="chevron-left" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 320 512" class="svg-inline--fa fa-chevron-left fa-w-10"><path fill="currentColor" d="M34.52 239.03L228.87 44.69c9.37-9.37 24.57-9.37 33.94 0l22.67 22.67c9.36 9.36 9.37 24.52.04 33.9L131.49 256l154.02 154.75c9.34 9.38 9.32 24.54-.04 33.9l-22.67 22.67c-9.37 9.37-24.57 9.37-33.94 0L34.52 272.97c-9.37-9.37-9.37-24.57 0-33.94z" class=""></path></svg></a>
705 <a class="next disabled" title="<?php echo esc_attr__( 'Next message', 'custom-twitter-feeds' ); ?>"><svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="chevron-right" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 320 512" class="svg-inline--fa fa-chevron-right fa-w-10"><path fill="currentColor" d="M285.476 272.971L91.132 467.314c-9.373 9.373-24.569 9.373-33.941 0l-22.667-22.667c-9.357-9.357-9.375-24.522-.04-33.901L188.505 256 34.484 101.255c-9.335-9.379-9.317-24.544.04-33.901l22.667-22.667c9.373-9.373 24.569-9.373 33.941 0L285.475 239.03c9.373 9.372 9.373 24.568.001 33.941z" class=""></path></svg></a>
706 </div>
707 <?php endif; ?>
708
709 <div class="messages">
710 <?php echo $notifications_html; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
711 </div>
712 </div>
713 <?php
714 }
715
716 /**
717 * Dismiss notification via AJAX. If it's a new user message, also dismiss it
718 * on all admin pages.
719 *
720 * @since 2.0
721 */
722 public function dismiss() {
723 // Run a security check.
724 check_ajax_referer( 'ctf-admin', 'nonce' );
725
726 // Check for access and required param.
727 if ( ! $this->has_access() || empty( $_POST['id'] ) ) {
728 wp_send_json_error();
729 }
730
731 $id = sanitize_text_field( wp_unslash( $_POST['id'] ) );
732
733 if ( $id === 'review' ) {
734 $ctf_statuses_option = get_option( 'ctf_statuses', array() );
735
736 update_option( 'ctf_rating_notice', 'dismissed', false );
737 $ctf_statuses_option['rating_notice_dismissed'] = ctf_get_current_time();
738 update_option( 'ctf_statuses', $ctf_statuses_option, false );
739 } elseif ( $id === 'discount' ) {
740 update_user_meta( get_current_user_id(), 'ctf_ignore_new_user_sale_notice', 'always' );
741
742 $current_month_number = (int)date('n', ctf_get_current_time() );
743 $not_early_in_the_year = ($current_month_number > 5);
744
745 if ( $not_early_in_the_year ) {
746 update_user_meta( get_current_user_id(), 'ctf_ignore_bfcm_sale_notice', date( 'Y', ctf_get_current_time() ) );
747 }
748 }
749
750 $option = $this->get_option();
751 $type = is_numeric( $id ) ? 'feed' : 'events';
752
753 $option['dismissed'][] = $id;
754 $option['dismissed'] = array_unique( $option['dismissed'] );
755
756 // Remove notification.
757 if ( is_array( $option[ $type ] ) && ! empty( $option[ $type ] ) ) {
758 foreach ( $option[ $type ] as $key => $notification ) {
759 if ( $notification['id'] == $id ) { // phpcs:ignore WordPress.PHP.StrictComparisons
760 unset( $option[ $type ][ $key ] );
761 break;
762 }
763 }
764 }
765
766 update_option( 'ctf_notifications', $option );
767
768 wp_send_json_success();
769 }
770 }
771