PluginProbe
Cookie Consent – GDPR & CCPA Cookie Banner & Consent Manager / 0.0.3
Cookie Consent – GDPR & CCPA Cookie Banner & Consent Manager v0.0.3
0.0.11 0.0.10 0.0.9 0.0.8 0.0.7 0.0.6 trunk 0.0.1 0.0.2 0.0.3 0.0.4 0.0.5
cookiez / modules / reviews / module.php

module.php in Cookie Consent – GDPR & CCPA Cookie Banner & Consent Manager 0.0.3, at modules/reviews/module.php

287 lines 6.6 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace Cookiez\Modules\Reviews;
4
5 use Cookiez\Classes\Module_Base;
6 use Cookiez\Classes\Utils;
7 use Cookiez\Modules\Connect\Module as Connect_Module;
8 use Cookiez\Modules\Scanner\Database\Scan_Entry;
9 use Cookiez\Modules\Settings\Classes\Settings;
10
11 if ( ! defined( 'ABSPATH' ) ) {
12 exit; // Exit if accessed directly.
13 }
14
15 /**
16 * Class Module
17 */
18 class Module extends Module_Base {
19 private const MAX_POPUP_DISMISSALS = 3;
20 private const HIGH_RATING_THRESHOLD = 4;
21 private const PLUGIN_ROW_STYLE_HANDLE = 'cookiez-reviews-plugin-row';
22
23 /**
24 * @var bool
25 */
26 private bool $should_print_reviews_mount = false;
27
28 /**
29 * Get module name.
30 * Retrieve the module name.
31 *
32 * @access public
33 * @return string Module name.
34 */
35 public function get_name(): string {
36 return 'Reviews';
37 }
38
39 public static function routes_list(): array {
40 return [
41 'Feedback',
42 ];
43 }
44
45 public function enqueue_plugin_row_styles( string $hook_suffix ): void {
46 if ( 'plugins.php' !== $hook_suffix ) {
47 return;
48 }
49
50 wp_register_style( self::PLUGIN_ROW_STYLE_HANDLE, false );
51 wp_enqueue_style( self::PLUGIN_ROW_STYLE_HANDLE );
52
53 $css = '
54 .wp-cookiez-review__stars {
55 display: inline-flex;
56 flex-direction: row-reverse;
57 }
58 .wp-cookiez-review__stars > span {
59 color: #888;
60 }
61 .wp-cookiez-review__stars > span:hover {
62 color: #ffa400;
63 }
64 .wp-cookiez-review__stars > span:hover ~ span {
65 color: #ffa400;
66 }
67 ';
68
69 wp_add_inline_style( self::PLUGIN_ROW_STYLE_HANDLE, $css );
70 }
71
72 /**
73 * Enqueue Scripts and Styles
74 */
75 public function enqueue_scripts(): void {
76 $this->should_print_reviews_mount = false;
77
78 if ( ! Utils::is_plugin_page() ) {
79 return;
80 }
81
82 if ( ! Connect_Module::is_connected() ) {
83 return;
84 }
85
86 if ( ! $this->maybe_show_review_popup() ) {
87 return;
88 }
89
90 $this->should_print_reviews_mount = true;
91
92 Utils\Assets::enqueue_app_assets( 'reviews', false );
93
94 wp_localize_script(
95 'reviews',
96 'cookiezReviewData',
97 [
98 'wpRestNonce' => wp_create_nonce( 'wp_rest' ),
99 'reviewData' => $this->get_review_data(),
100 'isRTL' => is_rtl(),
101 'isDevelopment' => defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG,
102 ]
103 );
104 }
105
106 /**
107 * Print reviews app mount node in admin footer (after enqueued scripts).
108 */
109 public function print_reviews_mount(): void {
110 if ( ! $this->should_print_reviews_mount ) {
111 return;
112 }
113
114 echo '<div id="reviews-app"></div>';
115 }
116
117 public function register_base_data(): void {
118
119 if ( get_option( Settings::REVIEW_DATA ) ) {
120 return;
121 }
122
123 $data = [
124 'dismissals' => 0,
125 'hide_for_days' => 0,
126 'last_dismiss' => null,
127 'rating' => null,
128 'feedback' => null,
129 'added_on' => gmdate( 'Y-m-d H:i:s' ),
130 'submitted' => false,
131 'repo_review_clicked' => false,
132 ];
133
134 update_option( Settings::REVIEW_DATA, $data, false );
135 }
136
137 /**
138 * Register settings.
139 *
140 * Register settings for the plugin.
141 *
142 * @return void
143 */
144 public function register_settings(): void {
145 register_setting(
146 'options',
147 Settings::REVIEW_DATA,
148 [
149 'type' => 'object',
150 'show_in_rest' => [
151 'schema' => [
152 'type' => 'object',
153 'additionalProperties' => true,
154 ],
155 ],
156 ]
157 );
158 }
159
160 /**
161 * @return array<string, mixed>
162 */
163 public function get_review_data(): array {
164 $data = get_option( Settings::REVIEW_DATA, [] );
165
166 return is_array( $data ) ? $data : [];
167 }
168
169 /**
170 * Whether the review prompt may appear based on scan activity: at least one completed scan,
171 * and at least one full day has passed since that scan finished (updated_at).
172 *
173 * @return bool
174 */
175 public function show_review_popup(): bool {
176 $first_completed = Scan_Entry::get_first_completed_scan();
177
178 if ( ! $first_completed || empty( $first_completed->updated_at ) ) {
179 return false;
180 }
181
182 $completed_at = strtotime( $first_completed->updated_at );
183
184 if ( false === $completed_at ) {
185 return false;
186 }
187
188 return ( time() - $completed_at ) >= DAY_IN_SECONDS;
189 }
190
191 /**
192 * Maybe show the review popup.
193 * Check if the review popup should be shown based on various conditions.
194 * @return bool
195 */
196 public function maybe_show_review_popup() {
197 if ( ! $this->show_review_popup() ) {
198 return false;
199 }
200
201 $review_data = $this->get_review_data();
202
203 // Don't show if user has already submitted feedback when rating is less than 4.
204 if ( isset( $review_data['rating'] ) && (int) $review_data['rating'] < self::HIGH_RATING_THRESHOLD ) {
205 return false;
206 }
207
208 // Hide if rating is submitted but repo review is not clicked.
209 if (
210 isset( $review_data['rating'] ) &&
211 (int) $review_data['rating'] >= self::HIGH_RATING_THRESHOLD &&
212 ! empty( $review_data['repo_review_clicked'] )
213 ) {
214 return false;
215 }
216
217 // Don't show if user has dismissed the popup 3+ times.
218 if ( (int) ( $review_data['dismissals'] ?? 0 ) >= self::MAX_POPUP_DISMISSALS ) {
219 return false;
220 }
221
222 if ( ! empty( $review_data['hide_for_days'] ) && ! empty( $review_data['last_dismiss'] ) ) {
223 $hide_for_days = (int) $review_data['hide_for_days'];
224 $last_dismiss = strtotime( $review_data['last_dismiss'] );
225
226 if ( false === $last_dismiss ) {
227 return true;
228 }
229
230 $days_since_dismiss = (int) floor( ( time() - $last_dismiss ) / DAY_IN_SECONDS );
231
232 if ( $days_since_dismiss < $hide_for_days ) {
233 return false;
234 }
235 }
236
237 return true;
238 }
239
240 /**
241 * Add review link to plugin row meta
242 *
243 * @param array $links Plugin row meta links.
244 * @param string $file Plugin file path.
245 * @return array
246 */
247 public function add_plugin_row_meta( $links, $file ) {
248
249 if ( ! defined( 'COOKIEZ_PLUGIN_BASE' ) || COOKIEZ_PLUGIN_BASE !== $file ) {
250 return $links;
251 }
252
253 $review_url = 'https://wordpress.org/support/plugin/cookiez/reviews/#new-post';
254
255 $links[] = '<a class="wp-cookiez-review"
256 href="' . esc_url( $review_url ) . '"
257 target="_blank" rel="noopener noreferrer"
258 title="' . esc_attr__( 'Review our plugin', 'cookiez' ) . '"
259 aria-label="' . esc_attr__(
260 'Rate Cookiez on WordPress.org (opens in a new tab)',
261 'cookiez'
262 ) . '">
263 <span class="wp-cookiez-review__stars" aria-hidden="true">
264 <span>�
265 </span><span>�
266 </span><span>�
267 </span><span>�
268 </span><span>�
269 </span>
270 </span>
271 </a>';
272
273 return $links;
274 }
275
276 public function __construct() {
277 add_action( 'admin_enqueue_scripts', [ $this, 'enqueue_plugin_row_styles' ] );
278 add_action( 'admin_enqueue_scripts', [ $this, 'enqueue_scripts' ] );
279 add_action( 'admin_footer', [ $this, 'print_reviews_mount' ] );
280 add_action( 'admin_init', [ $this, 'register_base_data' ] );
281 add_action( 'rest_api_init', [ $this, 'register_settings' ] );
282 add_filter( 'plugin_row_meta', [ $this, 'add_plugin_row_meta' ], 10, 2 );
283
284 $this->register_routes();
285 }
286 }
287