PluginProbe
Content Control – The Ultimate Content Restriction Plugin! Restrict Content, Create Conditional Blocks & More / 2.6.3
Content Control – The Ultimate Content Restriction Plugin! Restrict Content, Create Conditional Blocks & More v2.6.3
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.6.3, at classes/Controllers/Admin/Reviews.php

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