PluginProbe
Defender Security – Malware Scanner, Login Security & Firewall / 6.3.0
Defender Security – Malware Scanner, Login Security & Firewall v6.3.0
6.3.0 6.2.3 6.2.4 6.2.0 6.2.1 6.2.2 6.1.0 5.3.1 5.4.0 5.4.1 5.5.0 5.5.1 5.6.0 5.6.1 5.6.2 5.7.0 5.7.1 5.7.2 5.8.0 5.8.1 5.9.0 6.0.0 6.0.1 3.0.1 3.1.0 All 141 releases
defender-security / src / component / class-rate.php

class-rate.php in Defender Security – Malware Scanner, Login Security & Firewall 6.3.0, at src/component/class-rate.php

519 lines 14.1 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Handles the functionality related to user ratings and notifications.
4 *
5 * @package WP_Defender\Component
6 */
7
8 namespace WP_Defender\Component;
9
10 use Calotes\Base\Component;
11 use WP_Defender\Model\Setting\Security_Tweaks;
12 use WP_Defender\Traits\Defender_Bootstrap;
13
14 /**
15 * Handles the functionality related to user ratings and notifications.
16 *
17 * @since 4.4.0
18 */
19 class Rate extends Component {
20
21 use Defender_Bootstrap;
22
23 /**
24 * URL for leaving a new review on WordPress.org.
25 *
26 * @var string
27 */
28 public const URL_PLUGIN_NEW_REVIEW_VCS = 'https://wordpress.org/support/plugin/defender-security/reviews/#new-post';
29
30 /**
31 * Number of completed scans required to trigger a rating prompt.
32 *
33 * @var int
34 */
35 public const NUMBER_COMPLETED_SCANS = 3;
36
37 /**
38 * Number of fixed scan issues required to trigger a rating prompt.
39 *
40 * @var int
41 */
42 public const NUMBER_FIXED_SCANS = 5;
43
44 /**
45 * Number of UA lockouts required to trigger a rating prompt.
46 *
47 * @var int
48 */
49 public const NUMBER_UA_LOCKOUTS = 25;
50
51 /**
52 * Number of IP lockouts required to trigger a rating prompt.
53 *
54 * @var int
55 */
56 public const NUMBER_IP_LOCKOUTS = 25;
57
58 /**
59 * Option name for tracking the number of completed scans.
60 *
61 * @var string
62 */
63 public const SLUG_COMPLETED_SCANS = 'defender_counter_completed_scans';
64
65 /**
66 * Option name for tracking the number of fixed scan issues.
67 *
68 * @var string
69 */
70 public const SLUG_FIXED_SCAN_ISSUES = 'defender_counter_fixed_scan_issues';
71
72 /**
73 * Option name for tracking if the rate button was clicked.
74 *
75 * @var string
76 */
77 public const SLUG_FOR_BUTTON_RATE = 'defender_rating_success';
78
79 /**
80 * Option name for tracking if the later button was clicked.
81 *
82 * @var string
83 */
84 public const SLUG_FOR_BUTTON_THANKS = 'defender_days_rating_later_dismiss';
85
86 /**
87 * Option name for storing the installation date of the plugin.
88 *
89 * @var string
90 */
91 public const SLUG_FREE_INSTALL_DATE = 'defender_free_install_date';
92
93 /**
94 * Option name for storing the date of the postponed notice. This is global for all rating notices.
95 *
96 * @var string
97 */
98 public const SLUG_POSTPONED_NOTICE_DATE = 'defender_postponed_notice_date';
99
100 /**
101 * Option name for tracking the number of UA lockouts.
102 *
103 * @var string
104 */
105 public const SLUG_UA_LOCKOUTS = 'defender_counter_ua_lockouts';
106
107 /**
108 * Option name for tracking the number of IP lockouts.
109 *
110 * @var string
111 */
112 public const SLUG_IP_LOCKOUTS = 'defender_counter_ip_lockouts';
113
114 /**
115 * Retrieves the count of scans from the database.
116 *
117 * @param string $slug The option name to retrieve the count from.
118 *
119 * @return int The count of scans.
120 */
121 protected static function get_count_scans( $slug ): int {
122 return (int) get_site_option( $slug, 0 );
123 }
124
125 /**
126 * Get a button label to rate.
127 *
128 * @return string
129 */
130 public static function get_rate_button_title(): string {
131 return esc_html__( 'Rate Defender', 'defender-security' );
132 }
133
134 /**
135 * Get a button label to postpone.
136 *
137 * @return string
138 */
139 public static function get_postpone_button_title(): string {
140 return esc_html__( 'Remind me later', 'defender-security' );
141 }
142
143 /**
144 * Get a button label to dismiss.
145 *
146 * @return string
147 */
148 public static function get_dismiss_button_title(): string {
149 return esc_html__( 'I already did', 'defender-security' );
150 }
151
152 /**
153 * Count completed scans.
154 */
155 public static function run_counter_of_completed_scans(): void {
156 $scan_count = self::get_count_scans( self::SLUG_COMPLETED_SCANS );
157 if ( $scan_count < self::NUMBER_COMPLETED_SCANS ) {
158 update_site_option( self::SLUG_COMPLETED_SCANS, ++$scan_count );
159 }
160 }
161
162 /**
163 * Count fixed scans.
164 */
165 public static function run_counter_of_fixed_scans(): void {
166 $scan_count = self::get_count_scans( self::SLUG_FIXED_SCAN_ISSUES );
167 if ( $scan_count < self::NUMBER_FIXED_SCANS ) {
168 update_site_option( self::SLUG_FIXED_SCAN_ISSUES, ++$scan_count );
169 }
170 }
171
172 /**
173 * Count UA lockouts.
174 */
175 public static function run_counter_of_ua_lockouts(): void {
176 $lockout_count = self::get_count_scans( self::SLUG_UA_LOCKOUTS );
177 if ( $lockout_count < self::NUMBER_UA_LOCKOUTS ) {
178 update_site_option( self::SLUG_UA_LOCKOUTS, ++$lockout_count );
179 }
180 }
181
182 /**
183 * Count IP lockouts.
184 */
185 public static function run_counter_of_ip_lockouts(): void {
186 $lockout_count = self::get_count_scans( self::SLUG_IP_LOCKOUTS );
187 if ( $lockout_count < self::NUMBER_IP_LOCKOUTS ) {
188 update_site_option( self::SLUG_IP_LOCKOUTS, ++$lockout_count );
189 }
190 }
191
192 /**
193 * Get notice by given slug.
194 *
195 * @param string $notice_slug Notice slug.
196 *
197 * @return string
198 */
199 public static function get_notice_by_slug( string $notice_slug ): string {
200 switch ( $notice_slug ) {
201 case 'completed_scans':
202 default:
203 $text = '<p class="notice-header">' . sprintf(
204 /* translators: %d - Number of completed scans. */
205 esc_html__( 'Nice work — %d scans completed!', 'defender-security' ),
206 self::NUMBER_COMPLETED_SCANS
207 );
208 $text .= '</p><p class="notice-body">';
209 $text .= esc_html__(
210 'Your site’s files have been thoroughly scanned for file modifications and outdated plugins. Defender’s here to keep your site safe — could you share your experience with others on WordPress.org?',
211 'defender-security'
212 );
213 $text .= '</p>';
214 break;
215
216 case 'fixed_scans':
217 $text = '<p class="notice-body">' . sprintf(
218 /* translators: %d - Number of fixed scans. */
219 esc_html__(
220 'You`ve successfully resolved %d malware scan issues! We are happy to be a part of helping you secure your site, and we would appreciate it if you dropped us a rating on wp.org to help us spread the word and boost our motivation.',
221 'defender-security'
222 ),
223 self::NUMBER_FIXED_SCANS
224 );
225 $text .= '</p>';
226 break;
227
228 case 'resolved_tweaks':
229 $tweak_arr = wd_di()->get( Security_Tweaks::class )->get_tweak_types();
230 $total_tweaks = $tweak_arr['count_fixed'] + $tweak_arr['count_ignored'] + $tweak_arr['count_issues'];
231 $text = '<p class="notice-body">' . sprintf(
232 /* translators: %d - Total number. */
233 esc_html__(
234 'You`ve resolved all %d security recommendations - that`s impressive! We are happy to be a part of helping you secure your site, and we would appreciate it if you dropped us a rating on wp.org to help us spread the word and boost our motivation.',
235 'defender-security'
236 ),
237 $total_tweaks
238 );
239 $text .= '</p>';
240 break;
241
242 case 'ua_lockouts':
243 $text = '<p class="notice-header">' . sprintf(
244 /* translators: %d - Number of UA lockouts. */
245 esc_html__( 'Defender has blocked %d malicious bots!', 'defender-security' ),
246 self::NUMBER_UA_LOCKOUTS
247 );
248 $text .= '</p><p class="notice-body">';
249 $text .= sprintf(
250 /* translators: %d - Number of UA lockouts. */
251 esc_html__(
252 'That’s %1$d fake crawlers and bad bots stopped from accessing your site. Defender’s User Agent lockouts keep unwanted traffic away so your site stays safe. If Defender’s helped protect your site, please share your experience with a review on WordPress.org.',
253 'defender-security'
254 ),
255 self::NUMBER_UA_LOCKOUTS
256 );
257 $text .= '</p>';
258 break;
259
260 case 'ip_lockouts':
261 $text = '<p class="notice-header">' . sprintf(
262 /* translators: %d - Number of IP lockouts. */
263 esc_html__( 'Defender has blocked %d suspicious IPs!', 'defender-security' ),
264 self::NUMBER_IP_LOCKOUTS
265 );
266 $text .= '</p><p class="notice-body">';
267 $text .= sprintf(
268 /* translators: %d - Number of IP lockouts. */
269 esc_html__(
270 'That’s %1$d potential threats stopped before they could harm your site. Defender’s Firewall is keeping the bad guys and bots out so you can stay focused on running your site. If you’ve found Defender helpful, please share your experience with a review on WordPress.org.',
271 'defender-security'
272 ),
273 self::NUMBER_IP_LOCKOUTS
274 );
275 $text .= '</p>';
276 break;
277
278 case '7_days_installed':
279 $text = '<p class="notice-header">' . esc_html__(
280 'Enjoying Defender? We’d love to hear your feedback!',
281 'defender-security'
282 );
283 $text .= '</p><p class="notice-body">';
284 $text .= esc_html__(
285 'You’ve been using Defender for over a week now, and we’d love to hear about your experience! We’ve spent countless hours developing it for you, and your feedback is important to us. We’d really appreciate your rating.',
286 'defender-security'
287 );
288 $text .= '</p>';
289 break;
290 }
291
292 return $text;
293 }
294
295 /**
296 * Get label by given slug. No need to translate a returned text.
297 *
298 * @param string $notice_slug Notice slug.
299 *
300 * @return string
301 */
302 public static function get_label_by_slug( string $notice_slug ): string {
303 switch ( $notice_slug ) {
304 case 'completed_scans':
305 $text = '3 Malware Scan Completed';
306 break;
307 case 'fixed_scans':
308 $text = '5 Malware Issues Fixed';
309 break;
310 case 'resolved_tweaks':
311 $text = 'Recommendation Resolved';
312 break;
313 case 'ua_lockouts':
314 $text = '25 User Agent Lockout';
315 break;
316 case 'ip_lockouts':
317 $text = '25 IP Lockout';
318 break;
319 case '7_days_installed':
320 $text = '7 Days installed';
321 break;
322 default:
323 $text = '';
324 break;
325 }
326 return $text;
327 }
328
329 /**
330 * Get label of the current page.
331 *
332 * @return string
333 */
334 public static function get_current_page_label(): string {
335 switch ( defender_get_current_page() ) {
336 case 'wp-defender':
337 $text = 'Dashboard';
338 break;
339 case 'wdf-hardener':
340 $text = 'Recommendations';
341 break;
342 case 'wdf-scan':
343 $text = 'Malware Scan';
344 break;
345 case 'wdf-logging':
346 $text = 'Audit Log';
347 break;
348 case 'wdf-ip-lockout':
349 $text = 'Firewall';
350 break;
351 case 'wdf-2fa':
352 $text = '2FA';
353 break;
354 case 'wdf-advanced-tools':
355 $text = 'Tools';
356 break;
357 case 'wdf-notification':
358 $text = 'Notifications';
359 break;
360 case 'wdf-setting':
361 $text = 'Settings';
362 break;
363 default:
364 $text = '';
365 break;
366 }
367
368 return $text;
369 }
370
371 /**
372 * Reset counter of all prompts.
373 */
374 public static function reset_counters(): void {
375 update_site_option( self::SLUG_COMPLETED_SCANS, 0 );
376 update_site_option( self::SLUG_FIXED_SCAN_ISSUES, 0 );
377 update_site_option( self::SLUG_UA_LOCKOUTS, 0 );
378 update_site_option( self::SLUG_IP_LOCKOUTS, 0 );
379 }
380
381 /**
382 * Is the rating notice displayed? The priority list of prompts:
383 * 1) completed malware scans,
384 * 2) fixed scans,
385 * 3) resolved tweaks,
386 * 4) UA lockouts,
387 * 5) IP lockouts,
388 * 6) default one after 7 days installed.
389 * Note: items #1-5 are without waiting for 7 days.
390 * No uses in v6.0 but it'll will be implemented in the future.
391 *
392 * @return array
393 */
394 public static function is_achievement_displayed(): array {
395 $res = array(
396 'is_displayed' => true,
397 'slug' => '',
398 );
399 if ( self::was_rate_request() ) {
400 $res['is_displayed'] = false;
401
402 return $res;
403 }
404 if ( ! self::is_required_page() ) {
405 $res['is_displayed'] = false;
406
407 return $res;
408 }
409
410 // No needed to translate slugs.
411 if ( self::get_count_scans( self::SLUG_COMPLETED_SCANS ) >= self::NUMBER_COMPLETED_SCANS ) {
412 $res['slug'] = 'completed_scans';
413 } elseif ( self::get_count_scans( self::SLUG_FIXED_SCAN_ISSUES ) >= self::NUMBER_FIXED_SCANS ) {
414 $res['slug'] = 'fixed_scans';
415 } else {
416 $tweak_arr = wd_di()->get( Security_Tweaks::class )->get_tweak_types();
417 $total_tweaks = $tweak_arr['count_fixed'] + $tweak_arr['count_ignored'] + $tweak_arr['count_issues'];
418 if ( $tweak_arr['count_fixed'] === $total_tweaks && $total_tweaks > 0 ) {
419 $res['slug'] = 'resolved_tweaks';
420 } elseif ( self::get_count_scans( self::SLUG_UA_LOCKOUTS ) >= self::NUMBER_UA_LOCKOUTS ) {
421 $res['slug'] = 'ua_lockouts';
422 } elseif ( self::get_count_scans( self::SLUG_IP_LOCKOUTS ) >= self::NUMBER_IP_LOCKOUTS ) {
423 $res['slug'] = 'ip_lockouts';
424 } elseif ( self::was_postponed_request() ) {
425 $res['is_displayed'] = false;
426
427 return $res;
428 }
429 }
430 // If the conditions are met, display the notice about plugin achievements.
431 if ( $res['is_displayed'] && '' !== $res['slug'] ) {
432 return $res;
433 }
434 // If no, display the default one.
435 $res['slug'] = '7_days_installed';
436
437 return $res;
438 }
439
440 /**
441 * Display the rating notice on the Issues page if all malware scans are resolved.
442 *
443 * @param int $count_scan_issues Count of Scan issues.
444 *
445 * @return bool
446 * @since 6.0
447 */
448 public static function is_displayed_in_redesigned_version( int $count_scan_issues ): bool {
449 if ( self::was_rate_request() ) {
450 return false;
451 }
452 // Check by required page.
453 if ( 'wdf-scan' !== defender_get_current_page() ) {
454 return false;
455 }
456 if ( self::was_postponed_request() ) {
457 return false;
458 }
459
460 return 0 === $count_scan_issues;
461 }
462
463 /**
464 * Have there already been clicks on the Rate or Dismiss buttons?
465 *
466 * @return bool
467 */
468 private static function was_rate_request(): bool {
469 $display_rating = apply_filters( 'wd_display_rating', false );
470 $display_rating = is_bool( $display_rating ) ? $display_rating : (bool) $display_rating;
471 // Avoid case with '1'.
472 if ( true === (bool) get_site_option( self::SLUG_FOR_BUTTON_RATE, $display_rating ) ) {
473 return true;
474 }
475
476 $dismiss_rating = apply_filters( 'wd_dismiss_rating', false );
477 $dismiss_rating = is_bool( $dismiss_rating ) ? $dismiss_rating : (bool) $dismiss_rating;
478 // Avoid case with '1'.
479 if ( true === (bool) get_site_option( self::SLUG_FOR_BUTTON_THANKS, $dismiss_rating ) ) {
480 return true;
481 }
482
483 return false;
484 }
485
486 /**
487 * Have there already been click to postpone the notice?
488 *
489 * @return bool
490 */
491 private static function was_postponed_request(): bool {
492 $postponed_date = (int) get_site_option( self::SLUG_POSTPONED_NOTICE_DATE, 0 );
493
494 return time() <= strtotime( '+7 days', $postponed_date );
495 }
496
497 /**
498 * Is this required page? Excluded pages: WAF, Free Plugins, Onboard.
499 *
500 * @return bool
501 */
502 protected static function is_required_page(): bool {
503 $arr = array(
504 'wdf-hardener',
505 'wdf-scan',
506 'wdf-logging',
507 'wdf-ip-lockout',
508 'wdf-2fa',
509 'wdf-advanced-tools',
510 'wdf-notification',
511 'wdf-setting',
512 // Dashboard and Setup Wizard have the same page slug.
513 'wp-defender',
514 );
515
516 return in_array( defender_get_current_page(), $arr, true );
517 }
518 }
519