PluginProbe ʕ •ᴥ•ʔ
Web Accessibility Toolkit – Accessibility Checker & ARIA for WCAG, Section 508 & ADA Compliance / 1.7.0
Web Accessibility Toolkit – Accessibility Checker & ARIA for WCAG, Section 508 & ADA Compliance v1.7.0
1.7.0 trunk 1.3.0 1.3.1 1.4.0 1.4.1 1.4.2 1.5.0 1.5.1 1.5.10 1.5.11 1.5.12 1.5.13 1.5.2 1.5.3 1.5.4 1.5.5 1.5.6 1.5.7 1.5.8 1.5.9 1.6 1.6.1 1.6.2 1.6.3 1.6.4 1.6.5 1.6.6
aria-accessibility-toolkit / includes / class-review-notice.php
aria-accessibility-toolkit / includes Last commit date
class-admin.php 1 month ago class-menu-aria-labels.php 1 month ago class-review-notice.php 1 month ago index.php 1 year ago
class-review-notice.php
191 lines
1 <?php
2 if ( ! defined( 'ABSPATH' ) ) exit;
3
4 /**
5 * Dismissible review notice (per-user) for ARIAAT.
6 */
7 class ARIAAT_Review_Notice {
8 const META_KEY = 'ariaat_review_notice_state';
9 const NONCE = 'ariaat_review_notice_nonce';
10 const VERSION = '1.0.0';
11
12 // Tweak these thresholds:
13 protected $days_until_prompt = 3; // days after first use
14 protected $views_until_prompt = 3; // plugin-screen views before showing
15 protected $remind_after_days = 5; // snooze duration
16
17 // Your wp.org plugin slug + optional UTM
18 protected $wporg_slug = 'aria-accessibility-toolkit';
19 protected $utm = [
20 'utm_source' => 'plugin_admin',
21 'utm_medium' => 'review_notice',
22 'utm_campaign' => 'review_request',
23 ];
24
25 public function __construct() {
26 add_action( 'admin_init', [ $this, 'maybe_bootstrap_user_state' ] );
27 add_action( 'current_screen', [ $this, 'track_view_if_plugin_screen' ] );
28 add_action( 'admin_notices', [ $this, 'maybe_render_notice' ] );
29 add_action( 'network_admin_notices', [ $this, 'maybe_render_notice' ] );
30 add_action( 'admin_init', [ $this, 'handle_actions' ] );
31 }
32
33 /** Limit to your plugin’s screens (update IDs if needed). */
34 protected function is_plugin_screen( $screen = null ) {
35 if ( ! $screen ) $screen = function_exists('get_current_screen') ? get_current_screen() : null;
36 if ( ! $screen ) return false;
37
38 $ids = [
39 'settings_page_ariaat', // settings subpage
40 // add more as needed; discover with pp($screen->id)
41 ];
42
43 return in_array( $screen->id, $ids, true );
44 }
45
46 protected function get_user_state( $user_id = 0 ) {
47 $user_id = $user_id ?: get_current_user_id();
48 $state = get_user_meta( $user_id, self::META_KEY, true );
49 return is_array( $state ) ? $state : [];
50 }
51
52 protected function update_user_state( array $state, $user_id = 0 ) {
53 $user_id = $user_id ?: get_current_user_id();
54 update_user_meta( $user_id, self::META_KEY, $state );
55 }
56
57 public function maybe_bootstrap_user_state() {
58 $user_id = get_current_user_id();
59 if ( ! $user_id ) return;
60
61 $state = $this->get_user_state( $user_id );
62 if ( empty( $state ) ) {
63 $state = [
64 'installed' => time(),
65 'views' => 0,
66 'last_prompt' => 0,
67 'snooze_until' => 0,
68 'dismissed' => false,
69 ];
70 $this->update_user_state( $state, $user_id );
71 }
72 }
73
74 public function track_view_if_plugin_screen( $screen ) {
75 if ( ! $this->is_plugin_screen( $screen ) ) return;
76
77 if ( defined( 'ARIAAT_REVIEW_NOTICE_VIEW_TRACKED' ) ) return;
78 define( 'ARIAAT_REVIEW_NOTICE_VIEW_TRACKED', true );
79
80 $state = $this->get_user_state();
81 if ( empty( $state ) ) return;
82
83 $state['views'] = isset( $state['views'] ) ? (int) $state['views'] + 1 : 1;
84 $this->update_user_state( $state );
85 }
86
87 protected function should_show_notice() {
88 if ( ! is_admin() ) return false;
89
90 $screen = function_exists( 'get_current_screen' ) ? get_current_screen() : null;
91 if ( ! $this->is_plugin_screen( $screen ) ) return false;
92
93 $state = $this->get_user_state();
94 if ( empty( $state ) ) return false;
95 if ( ! empty( $state['dismissed'] ) ) return false;
96
97 $installed = isset( $state['installed'] ) ? (int) $state['installed'] : time();
98 $views = isset( $state['views'] ) ? (int) $state['views'] : 0;
99 $snooze_until = isset( $state['snooze_until'] ) ? (int) $state['snooze_until'] : 0;
100 $last_prompt = isset( $state['last_prompt'] ) ? (int) $state['last_prompt'] : 0;
101
102 $days_active = floor( ( time() - $installed ) / DAY_IN_SECONDS );
103 if ( $days_active < $this->days_until_prompt ) return false;
104 if ( $views < $this->views_until_prompt ) return false;
105 if ( $snooze_until && time() < $snooze_until ) return false;
106 if ( ( time() - $last_prompt ) < HOUR_IN_SECONDS ) return false;
107
108 return true;
109 }
110
111 protected function url_with_action( $choice ) {
112 $args = [
113 'ariaat_review' => $choice, // now|later|never
114 '_wpnonce' => wp_create_nonce( self::NONCE ),
115 ];
116 return add_query_arg( $args, remove_query_arg( [ 'ariaat_review', '_wpnonce' ] ) );
117 }
118
119 public function handle_actions() {
120 if ( empty( $_GET['ariaat_review'] ) ) return;
121 if ( ! wp_verify_nonce( $_GET['_wpnonce'] ?? '', self::NONCE ) ) return;
122
123 $choice = sanitize_text_field( wp_unslash( $_GET['ariaat_review'] ) );
124 $state = $this->get_user_state();
125 if ( empty( $state ) ) return;
126
127 if ( $choice === 'now' ) {
128 $state['snooze_until'] = time() + ( 90 * DAY_IN_SECONDS );
129 $state['last_prompt'] = time();
130 $this->update_user_state( $state );
131 } elseif ( $choice === 'later' ) {
132 $state['snooze_until'] = time() + ( $this->remind_after_days * DAY_IN_SECONDS );
133 $state['last_prompt'] = time();
134 $this->update_user_state( $state );
135 } elseif ( $choice === 'never' ) {
136 $state['dismissed'] = true;
137 $this->update_user_state( $state );
138 }
139
140 // Clean URL to avoid repeat on refresh
141 wp_safe_redirect( remove_query_arg( [ 'ariaat_review', '_wpnonce' ] ) );
142 exit;
143 }
144
145 public function maybe_render_notice() {
146 if ( ! $this->should_show_notice() ) return;
147
148 // Record that we prompted now
149 $state = $this->get_user_state();
150 $state['last_prompt'] = time();
151 $this->update_user_state( $state );
152
153 $review_now = 'https://wordpress.org/support/plugin/aria-accessibility-toolkit/reviews/#new-post';
154 $later_url = $this->url_with_action( 'later' );
155 $never_url = $this->url_with_action( 'never' );
156 $now_url = $this->url_with_action( 'now' ); // persist choice before opening wp.org
157 ?>
158 <div class="notice notice-info is-dismissible" style="display:flex;align-items:center;justify-content:space-between;gap:16px;padding: 16px 38px 16px 16px;border-left-color: #4f36aa;border-radius: 10px;">
159 <div>
160 <strong><?php esc_html_e( 'Enjoying Web Accessibility Toolkit?', 'aria-accessibility-toolkit' ); ?></strong>
161 <p style="margin:.3em 0 0;">
162 <?php esc_html_e( 'If the plugin’s helping, could you spare 30 seconds for a quick review? It really helps.', 'aria-accessibility-toolkit' ); ?>
163 </p>
164 </div>
165 <div style="display:flex;gap:8px;flex-wrap:wrap;align-items:center;">
166 <a class="button button-primary small" href="<?php echo esc_url( $now_url ); ?>" target="_blank" rel="noopener noreferrer">
167 <?php esc_html_e( 'Review now', 'aria-accessibility-toolkit' ); ?>
168 </a>
169 <a class="button-link" href="<?php echo esc_url( $later_url ); ?>">
170 <?php esc_html_e( 'Remind me later', 'aria-accessibility-toolkit' ); ?>
171 </a>
172 <a class="button-link" href="<?php echo esc_url( $never_url ); ?>">
173 <?php esc_html_e( 'Don’t show again', 'aria-accessibility-toolkit' ); ?>
174 </a>
175 </div>
176 <script>
177 // When "Review now" clicked, also open wp.org (PHP cannot open a new tab on redirect).
178 (function(){
179 var btn = document.currentScript.previousElementSibling.querySelector('.button-primary');
180 if(!btn) return;
181 btn.addEventListener('click', function(e){
182 window.open('<?php echo esc_js( $review_now ); ?>','_blank','noopener,noreferrer');
183 });
184 })();
185 </script>
186 </div>
187 <?php
188 }
189 }
190
191 new ARIAAT_Review_Notice();