PluginProbe
Content Control – The Ultimate Content Restriction Plugin! Restrict Content, Create Conditional Blocks & More / 2.0.4
Content Control – The Ultimate Content Restriction Plugin! Restrict Content, Create Conditional Blocks & More v2.0.4
trunk 1.0.0 1.0.1 1.0.2 1.0.3 1.1.0 1.1.1 1.1.10 1.1.2 1.1.3 1.1.4 1.1.5 1.1.6 1.1.7 1.1.8 2.0.0 2.0.1 2.0.10 2.0.11 2.0.12 2.0.2 2.0.3 2.0.4 2.0.5 2.0.6 All 47 releases
content-control / classes / Controllers / Admin / Reviews.php

Reviews.php in Content Control – The Ultimate Content Restriction Plugin! Restrict Content, Create Conditional Blocks & More 2.0.4, at classes/Controllers/Admin/Reviews.php

571 lines 13.5 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Plugin Review Controller Class.
4 *
5 * @package ContentControl
6 */
7
8 namespace ContentControl\Controllers\Admin;
9
10 use ContentControl\Base\Controller;
11
12 use function ContentControl\plugin;
13
14 defined( 'ABSPATH' ) || exit;
15
16 /**
17 * Class ContentControl\Admin\Reviews
18 *
19 * This class adds a review request system for your plugin or theme to the WP dashboard.
20 *
21 * @since 1.1.0
22 */
23 class Reviews extends Controller {
24
25 /**
26 * Enable debug mode.
27 *
28 * @var boolean|array
29 */
30 private $debug = false;
31
32 /**
33 * Debug trigger.
34 *
35 * @var array
36 */
37 private $debug_trigger = [
38 'group' => 'time_installed',
39 'code' => 'one_week',
40 ];
41
42 /**
43 * Tracking API Endpoint.
44 *
45 * @var string
46 */
47 public static $api_url;
48
49 /**
50 * Initialize review requests.
51 */
52 public function init() {
53 add_action( 'init', [ $this, 'hooks' ] );
54 add_action( 'wp_ajax_content_control_review_action', [ $this, 'ajax_handler' ] );
55 }
56
57 /**
58 * Hook into relevant WP actions.
59 */
60 public function hooks() {
61 if ( is_admin() && current_user_can( 'manage_options' ) ) {
62 $this->installed_on();
63 add_action( 'admin_notices', [ $this, 'admin_notices' ] );
64 add_action( 'network_admin_notices', [ $this, 'admin_notices' ] );
65 add_action( 'user_admin_notices', [ $this, 'admin_notices' ] );
66 }
67 }
68
69 /**
70 * Get the install date for comparisons. Sets the date to now if none is found.
71 *
72 * @return false|string
73 */
74 public function installed_on() {
75 $installed_on = \get_option( 'content_control_installed_on', false );
76
77 if ( ! $installed_on ) {
78 $installed_on = current_time( 'mysql' );
79 \update_option( 'content_control_installed_on', $installed_on );
80 }
81
82 return $installed_on;
83 }
84
85 /**
86 * AJAX Handler
87 */
88 public function ajax_handler() {
89 // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
90 if ( ! isset( $_REQUEST['nonce'] ) || ! wp_verify_nonce( wp_unslash( $_REQUEST['nonce'] ), 'content_control_review_action' ) ) {
91 wp_send_json_error();
92 }
93
94 $args = wp_parse_args( $_REQUEST, [
95 'group' => $this->get_trigger_group(),
96 'code' => $this->get_trigger_code(),
97 'pri' => $this->get_current_trigger( 'pri' ),
98 'reason' => 'maybe_later',
99 ] );
100
101 try {
102 $user_id = get_current_user_id();
103
104 $dismissed_triggers = $this->dismissed_triggers();
105 $dismissed_triggers[ $args['group'] ] = $args['pri'];
106 update_user_meta( $user_id, 'content_control_reviews_dismissed_triggers', $dismissed_triggers );
107 update_user_meta( $user_id, 'content_control_reviews_last_dismissed', current_time( 'mysql' ) );
108
109 switch ( $args['reason'] ) {
110 case 'maybe_later':
111 update_user_meta( $user_id, 'content_control_reviews_last_dismissed', current_time( 'mysql' ) );
112 break;
113 case 'am_now':
114 case 'already_did':
115 $this->already_did( true );
116 break;
117 default:
118 // Do nothing if the reason value does not match one of ours.
119 }
120
121 wp_send_json_success();
122 } catch ( \Exception $e ) {
123 wp_send_json_error( $e );
124 }
125 }
126
127 /**
128 * Get the current trigger group and code.
129 *
130 * @return int|string
131 */
132 public function get_trigger_group() {
133 static $selected;
134
135 if ( $this->debug ) {
136 return isset( $this->debug_trigger['group'] ) ? $this->debug_trigger['group'] : 'time_installed';
137 }
138
139 if ( ! isset( $selected ) ) {
140 $dismissed_triggers = $this->dismissed_triggers();
141
142 $triggers = $this->triggers();
143
144 foreach ( $triggers as $g => $group ) {
145 foreach ( $group['triggers'] as $t => $trigger ) {
146 if ( ! in_array( false, $trigger['conditions'], true ) && ( empty( $dismissed_triggers[ $g ] ) || $dismissed_triggers[ $g ] < $trigger['pri'] ) ) {
147 $selected = $g;
148 break;
149 }
150 }
151
152 if ( isset( $selected ) ) {
153 break;
154 }
155 }
156 }
157
158 return $selected;
159 }
160
161 /**
162 * Get the current trigger group and code.
163 *
164 * @return int|string
165 */
166 public function get_trigger_code() {
167 static $selected;
168
169 if ( $this->debug ) {
170 return isset( $this->debug_trigger['code'] ) ? $this->debug_trigger['code'] : 'one_week';
171 }
172
173 if ( ! isset( $selected ) ) {
174 $dismissed_triggers = $this->dismissed_triggers();
175
176 foreach ( $this->triggers() as $g => $group ) {
177 foreach ( $group['triggers'] as $t => $trigger ) {
178 if ( ! in_array( false, $trigger['conditions'], true ) && ( empty( $dismissed_triggers[ $g ] ) || $dismissed_triggers[ $g ] < $trigger['pri'] ) ) {
179 $selected = $t;
180 break;
181 }
182 }
183
184 if ( isset( $selected ) ) {
185 break;
186 }
187 }
188 }
189
190 return $selected;
191 }
192
193 /**
194 * Get the current trigger.
195 *
196 * @param string $key Optional. Key to return from the trigger array.
197 *
198 * @return bool|mixed
199 */
200 public function get_current_trigger( $key = null ) {
201 $group = $this->get_trigger_group();
202 $code = $this->get_trigger_code();
203
204 if ( ! $group || ! $code ) {
205 return false;
206 }
207
208 $trigger = $this->triggers( $group, $code );
209
210 if ( empty( $key ) ) {
211 return $trigger;
212 } else {
213 return isset( $trigger[ $key ] ) ? $trigger[ $key ] : false;
214 }
215 }
216
217 /**
218 * Returns an array of dismissed trigger groups.
219 *
220 * Array contains the group key and highest priority trigger that has been shown previously for each group.
221 *
222 * $return = array(
223 * 'group1' => 20
224 * );
225 *
226 * @return array|mixed
227 */
228 public function dismissed_triggers() {
229 $user_id = get_current_user_id();
230
231 $dismissed_triggers = get_user_meta( $user_id, 'content_control_reviews_dismissed_triggers', true );
232
233 if ( ! $dismissed_triggers ) {
234 $dismissed_triggers = [];
235 }
236
237 return $dismissed_triggers;
238 }
239
240 /**
241 * Returns true if the user has opted to never see this again. Or sets the option.
242 *
243 * @param bool $set If set this will mark the user as having opted to never see this again.
244 *
245 * @return bool
246 */
247 public function already_did( $set = false ) {
248 $user_id = get_current_user_id();
249
250 if ( $set ) {
251 update_user_meta( $user_id, '_content_control_reviews_already_did', true );
252
253 return true;
254 }
255
256 return (bool) get_user_meta( $user_id, '_content_control_reviews_already_did', true );
257 }
258
259 /**
260 * Gets a list of triggers.
261 *
262 * @param string $group Trigger group.
263 * @param string $code Trigger code.
264 *
265 * @return bool|mixed
266 */
267 public function triggers( $group = null, $code = null ) {
268 static $triggers;
269
270 if ( ! isset( $triggers ) ) {
271 $link = 'https://wordpress.org/support/plugin/content-control/reviews/?rate=5#rate-response';
272
273 // Translators: 1. emoji, 2. html tag, 3. html tag, 4. the number of days, 5. html tag, 6. html tag.
274 $time_message = __( 'Hi there! %1$s You\'ve been using the %2$sContent Control%3$s plugin on your site for %4$s now - We hope it\'s been helpful. If you\'re enjoying the plugin, would you mind rating it %5$s5-stars%6$s to help spread the word?', 'content-control' );
275 $triggers = [
276 'time_installed' => [
277 'triggers' => [
278 'one_week' => [
279 'message' => sprintf(
280 $time_message,
281 '👋',
282 '<strong>',
283 '</strong>',
284 __( '1 week', 'content-control' ),
285 '<span class="five-stars" title="',
286 '"></span>'
287 ),
288 'conditions' => [
289 strtotime( $this->installed_on() . ' +1 week' ) < time(),
290 ],
291 'link' => $link,
292 'pri' => 10,
293 ],
294 'one_month' => [
295 'message' => sprintf(
296 $time_message,
297 '👋',
298 '<strong>',
299 '</strong>',
300 __( '1 month', 'content-control' ),
301 '<span class="five-stars" title="',
302 '"></span>'
303 ),
304 'conditions' => [
305 strtotime( $this->installed_on() . ' +1 month' ) < time(),
306 ],
307 'link' => $link,
308 'pri' => 20,
309 ],
310 'three_months' => [
311 'message' => sprintf(
312 $time_message,
313 '👋',
314 '<strong>',
315 '</strong>',
316 __( '3 months', 'content-control' ),
317 '<span class="five-stars" title="',
318 '"></span>'
319 ),
320 'conditions' => [
321 strtotime( $this->installed_on() . ' +3 months' ) < time(),
322 ],
323 'link' => $link,
324 'pri' => 30,
325 ],
326
327 ],
328 'pri' => 10,
329 ],
330 ];
331
332 $triggers = apply_filters( 'content_control_reviews_triggers', $triggers );
333
334 // Sort Groups.
335 uasort( $triggers, [ $this, 'rsort_by_priority' ] );
336
337 // Sort each groups triggers.
338 foreach ( $triggers as $k => $v ) {
339 uasort( $triggers[ $k ]['triggers'], [ $this, 'rsort_by_priority' ] );
340 }
341 }
342
343 if ( isset( $group ) ) {
344 if ( ! isset( $triggers[ $group ] ) ) {
345 return false;
346 }
347
348 if ( ! isset( $code ) ) {
349 return $triggers[ $group ];
350 } else {
351 return isset( $triggers[ $group ]['triggers'][ $code ] ) ? $triggers[ $group ]['triggers'][ $code ] : false;
352 }
353 }
354
355 return $triggers;
356 }
357
358 /**
359 * Render admin notices if available.
360 */
361 public function admin_notices() {
362 if ( $this->hide_notices() ) {
363 return;
364 }
365
366 $group = $this->get_trigger_group();
367 $code = $this->get_trigger_code();
368 $pri = $this->get_current_trigger( 'pri' );
369 $trigger = $this->get_current_trigger();
370
371 // Used to anonymously distinguish unique site+user combinations in terms of effectiveness of each trigger.
372 $uuid = wp_hash( home_url() . '-' . get_current_user_id() );
373
374 ?>
375
376 <script type="text/javascript">
377 (function ($) {
378 var trigger = {
379 group: '<?php echo esc_js( $group ); ?>',
380 code: '<?php echo esc_js( $code ); ?>',
381 pri: '<?php echo esc_js( $pri ); ?>'
382 };
383
384 function dismiss(reason) {
385 $.ajax({
386 method: "POST",
387 dataType: "json",
388 url: ajaxurl,
389 data: {
390 action: 'content_control_review_action',
391 nonce: '<?php echo esc_js( wp_create_nonce( 'content_control_review_action' ) ); ?>',
392 group: trigger.group,
393 code: trigger.code,
394 pri: trigger.pri,
395 reason: reason
396 }
397 });
398
399 <?php if ( ! empty( $this->api_url ) ) : ?>
400 $.ajax({
401 method: "POST",
402 dataType: "json",
403 url: '<?php echo esc_js( $this->api_url ); ?>',
404 data: {
405 trigger_group: trigger.group,
406 trigger_code: trigger.code,
407 reason: reason,
408 uuid: '<?php echo esc_js( $uuid ); ?>'
409 }
410 });
411 <?php endif; ?>
412 }
413
414 $(document)
415 .on('click', '.content-control-notice .content-control-dismiss', function (event) {
416 var $this = $(this),
417 reason = $this.data('reason'),
418 notice = $this.parents('.content-control-notice');
419
420 notice.fadeTo(100, 0, function () {
421 notice.slideUp(100, function () {
422 notice.remove();
423 });
424 });
425
426 dismiss(reason);
427 })
428 .ready(function () {
429 setTimeout(function () {
430 $('.content-control-notice button.notice-dismiss').click(function (event) {
431 dismiss('maybe_later');
432 });
433 }, 1000);
434 });
435 }(jQuery));
436 </script>
437
438 <style>
439 .content-control-notice {
440 display: flex;
441 align-items: center;
442 gap: 16px;
443 padding: 8px;
444 margin-top: 16px;
445 margin-bottom: 16px;
446 }
447
448 .content-control-notice .notice-logo {
449 flex: 0 0 110px;
450 max-width: 110px;
451 }
452
453 .content-control-notice .notice-content {
454 flex-grow: 1;
455 }
456
457 .content-control-notice p {
458 margin-bottom: 0;
459 max-width: 800px;
460 }
461
462 .content-control-notice .review-actions {
463 margin-top: 10px;
464 margin-bottom: 0;
465 padding-left: 0;
466 list-style: none;
467
468 display: flex;
469 gap: 16px;
470 align-items: center;
471 }
472 .content-control-notice .five-stars::before {
473 content: "�
474
475
476
477
478 ";
479 color: #f0ad4e;
480 }
481 </style>
482
483 <div class="notice notice-success is-dismissible content-control-notice">
484
485 <div class="notice-logo">
486 <img class="logo" width="110" src="<?php echo esc_attr( plugin()->get_url( 'assets/images/illustration-check.svg' ) ); ?>" />
487 </div>
488
489 <div class="notice-content">
490 <p>
491 <?php
492 // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
493 echo $trigger['message'];
494 ?>
495 ~ <a target="_blank" href="https://twitter.com/danieliser" title="Follow Daniel on Twitter">@danieliser</a>
496 </p>
497 <ul class="review-actions">
498 <li>😁
499 <a class="content-control-dismiss" target="_blank" href="<?php echo esc_attr( $trigger['link'] ); ?>" data-reason="am_now">
500 <strong><?php esc_html_e( 'Ok, you deserve it', 'content-control' ); ?></strong>
501 </a>
502 </li>
503 <li>
504 🤔
505 <a href="#" class="content-control-dismiss" data-reason="maybe_later">
506 <?php esc_html_e( 'Maybe later, okay?', 'content-control' ); ?>
507 </a>
508 </li>
509 <li>🙌
510 <a href="#" class="content-control-dismiss" data-reason="already_did">
511 <?php esc_html_e( 'I already did', 'content-control' ); ?>
512 </a>
513 </li>
514 </ul>
515
516 </div>
517
518 </div>
519
520 <?php
521 }
522
523 /**
524 * Checks if notices should be shown.
525 *
526 * @return bool
527 */
528 public function hide_notices() {
529 if ( $this->debug ) {
530 return false;
531 }
532
533 $code = $this->get_trigger_code();
534
535 $conditions = [
536 $this->already_did(),
537 $this->last_dismissed() && strtotime( $this->last_dismissed() . ' +2 weeks' ) > time(),
538 empty( $code ),
539 ];
540
541 return in_array( true, $conditions, true );
542 }
543
544 /**
545 * Gets the last dismissed date.
546 *
547 * @return false|string
548 */
549 public function last_dismissed() {
550 $user_id = get_current_user_id();
551
552 return get_user_meta( $user_id, 'content_control_reviews_last_dismissed', true );
553 }
554
555 /**
556 * Sort array in reverse by priority value
557 *
558 * @param array $a First array to compare.
559 * @param array $b Second array to compare.
560 *
561 * @return int
562 */
563 public function rsort_by_priority( $a, $b ) {
564 if ( ! isset( $a['pri'] ) || ! isset( $b['pri'] ) || $a['pri'] === $b['pri'] ) {
565 return 0;
566 }
567
568 return ( $a['pri'] < $b['pri'] ) ? 1 : - 1;
569 }
570 }
571