Anti-spam - Version 7.0.1

Version Description

  • Removed https notice.
Download this release

Release Info

Developer alexkovalevv
Plugin Icon 128x128 Anti-spam
Version 7.0.1
Comparing to
See all releases

Code changes from version 6.5.4 to 7.0.1

Files changed (151) hide show
  1. admin/ajax/logs.php +14 -14
  2. admin/assets/css/about-premium.css +69 -69
  3. admin/assets/css/about-premium.css.map +0 -1
  4. admin/assets/css/dashboard-dashboard.css +302 -0
  5. admin/assets/css/firewall/firewall-attacks-log.css +68 -0
  6. admin/assets/css/firewall/firewall-attacks-log.less +86 -0
  7. admin/assets/css/firewall/firewall-dashboard.css +83 -0
  8. admin/assets/css/firewall/firewall-dashboard.less +91 -0
  9. admin/assets/css/firewall/firewall-ips-blocking.css +98 -0
  10. admin/assets/css/firewall/firewall-ips-blocking.less +121 -0
  11. admin/assets/css/firewall/firewall-settings.css +205 -0
  12. admin/assets/css/firewall/firewall-settings.less +211 -0
  13. admin/assets/css/libs/jquery.datetimepicker.min.css +1 -0
  14. admin/assets/css/quick-dashboard.css +266 -0
  15. admin/assets/css/settings.css.map +0 -1
  16. admin/assets/css/sweetalert-custom.css +152 -78
  17. admin/assets/css/sweetalert-custom.css.map +1 -1
  18. admin/assets/css/sweetalert-custom.less +85 -4
  19. admin/assets/css/titan-security.css +213 -0
  20. admin/assets/img/check.png +0 -0
  21. admin/assets/img/clock.png +0 -0
  22. admin/assets/img/firewall-modal-preloader.gif +0 -0
  23. admin/assets/img/icon.png +0 -0
  24. admin/assets/img/loader.gif +0 -0
  25. admin/assets/img/titan-icon.png +0 -0
  26. admin/assets/img/x.png +0 -0
  27. admin/assets/js/dashboard.js +215 -0
  28. admin/assets/js/firewall/firewall-block-ip.js +126 -0
  29. admin/assets/js/firewall/firewall-dashboard.js +289 -0
  30. admin/assets/js/firewall/firewall-settings.js +20 -0
  31. admin/assets/js/import.js +103 -0
  32. admin/assets/js/libs/circular-progress.js +140 -0
  33. admin/assets/js/libs/jquery.datetimepicker.full.min.js +1 -0
  34. admin/assets/js/libs/popover.min.js +1 -0
  35. admin/assets/js/quickstart.js +16 -0
  36. admin/assets/js/settings.js +127 -18
  37. admin/assets/js/trial-popup.js +30 -4
  38. admin/boot.php +51 -106
  39. admin/class-activation.php +21 -25
  40. admin/class-page-titan-basic.php +119 -0
  41. admin/index.php +0 -0
  42. admin/pages/class-pages-about.php +0 -205
  43. admin/pages/{class-pages-settings.php → class-pages-antispam.php} +71 -52
  44. admin/pages/class-pages-check.php +386 -0
  45. admin/pages/class-pages-dashboard.php +380 -0
  46. admin/pages/class-pages-license.php +199 -121
  47. admin/pages/class-pages-logs.php +63 -44
  48. admin/pages/class-pages-plugin-settings.php +511 -0
  49. admin/pages/class-pages-scanner.php +114 -0
  50. admin/pages/class-pages-sitechecker.php +129 -0
  51. admin/pages/class-pages-tweaks.php +297 -0
  52. admin/pages/firewall/class-pages-bruteforce.php +309 -0
  53. admin/pages/firewall/class-pages-firewall-attacks-log.php +138 -0
  54. admin/pages/firewall/class-pages-firewall-blocking.php +152 -0
  55. admin/pages/firewall/class-pages-firewall-login-attempts.php +162 -0
  56. admin/pages/firewall/class-pages-firewall-settings.php +729 -0
  57. admin/pages/firewall/class-pages-firewall.php +107 -0
  58. anti-spam.php +92 -73
  59. assets/css/admin-bar.css +55 -0
  60. assets/img/titan-icon.png +0 -0
  61. {admin → includes/antispam}/assets/css/settings.css +56 -56
  62. {admin → includes/antispam}/assets/css/settings.less +0 -0
  63. includes/antispam/assets/js/settings.js +24 -0
  64. includes/antispam/boot.php +12 -0
  65. includes/antispam/classes/class-antispam.php +143 -0
  66. includes/{class-protector.php → antispam/classes/class-protector.php} +9 -8
  67. includes/antispam/functions.php +261 -0
  68. includes/audit/assets/css/audit-dashboard.css +16 -0
  69. includes/audit/assets/js/audit.js +3 -0
  70. includes/audit/assets/js/audit_ajax.js +40 -0
  71. includes/audit/boot.php +15 -0
  72. includes/audit/classes/class.audit.php +504 -0
  73. includes/audit/classes/class.auditresult.php +75 -0
  74. includes/audit/classes/class.cert.php +187 -0
  75. includes/audit/views/all-audit.php +73 -0
  76. includes/bruteforce/class-helpers.php +65 -0
  77. includes/bruteforce/class-limit-login-attempts.php +1086 -0
  78. includes/bruteforce/const.php +6 -0
  79. includes/bruteforce/do_activate.php +17 -0
  80. includes/check/assets/css/check-dashboard.css +148 -0
  81. includes/check/assets/img/ajax-loader-big.gif +0 -0
  82. includes/check/assets/img/close.svg +5 -0
  83. includes/check/assets/img/error.png +0 -0
  84. includes/check/assets/img/loader.gif +0 -0
  85. includes/check/assets/img/none.png +0 -0
  86. includes/check/assets/img/off.png +0 -0
  87. includes/check/assets/img/ok.png +0 -0
  88. includes/check/assets/img/warning.png +0 -0
  89. includes/check/assets/js/check.js +51 -0
  90. includes/check/boot.php +19 -0
  91. includes/check/classes/class.check.php +207 -0
  92. includes/check/views/check.php +25 -0
  93. includes/check/views/hided.php +70 -0
  94. includes/check/views/main.php +50 -0
  95. includes/class-anti-spam-plugin.php +0 -143
  96. includes/class-titan-security-plugin.php +315 -0
  97. includes/class-views.php +112 -0
  98. includes/class.module-base.php +100 -0
  99. includes/functions.php +406 -115
  100. includes/helpers.php +237 -0
  101. includes/index.php +2 -0
  102. includes/logger/assets/css/base.css +19 -19
  103. includes/logger/assets/js/base.js +5 -6
  104. includes/logger/class-logger-export.php +78 -72
  105. includes/logger/class-logger-reader.php +9 -9
  106. includes/logger/class-logger-writter.php +311 -292
  107. includes/scanner/assets/css/base-statistic.css +473 -0
  108. includes/scanner/assets/css/scanner-dashboard.css +47 -0
  109. includes/scanner/assets/js/Chart.min.js +10 -0
  110. includes/scanner/assets/js/scanner.js +107 -0
  111. includes/scanner/assets/js/statistic.js +34 -0
  112. includes/scanner/boot.php +12 -0
  113. includes/scanner/classes/class.scanner.php +166 -0
  114. includes/scanner/classes/scanner/File.php +118 -0
  115. includes/scanner/classes/scanner/HashListPool.php +61 -0
  116. includes/scanner/classes/scanner/Match.php +82 -0
  117. includes/scanner/classes/scanner/Scanner.php +207 -0
  118. includes/scanner/classes/scanner/Signature.php +177 -0
  119. includes/scanner/classes/scanner/SignaturePool.php +190 -0
  120. includes/scanner/classes/scanner/boot.php +13 -0
  121. includes/scanner/test.php +55 -0
  122. includes/scanner/views/results.php +87 -0
  123. includes/scanner/views/scanner.php +76 -0
  124. includes/sitechecker/assets/css/sitechecker-dashboard.css +180 -0
  125. includes/sitechecker/assets/img/delete.png +0 -0
  126. includes/sitechecker/assets/img/loader.gif +0 -0
  127. includes/sitechecker/assets/js/app.js +234 -0
  128. includes/sitechecker/assets/js/firebase-messaging-sw.js +46 -0
  129. includes/sitechecker/assets/js/firebase.min.js +5 -0
  130. includes/sitechecker/assets/js/sitechecker.js +83 -0
  131. includes/sitechecker/boot.php +14 -0
  132. includes/sitechecker/classes/class.sitechecker.php +238 -0
  133. includes/sitechecker/views/sitechecker.php +87 -0
  134. includes/tweaks/class-security-tweaks.php +361 -0
  135. includes/tweaks/password-requirements/assets/js/login-interstitial-util.js +131 -0
  136. includes/tweaks/password-requirements/assets/js/script.js +3 -0
  137. includes/tweaks/password-requirements/boot.php +13 -0
  138. includes/tweaks/password-requirements/class-canonical-roles.php +423 -0
  139. includes/tweaks/password-requirements/class-password-requirements-base.php +371 -0
  140. includes/tweaks/password-requirements/class-password-requirements.php +509 -0
  141. includes/tweaks/password-requirements/class-strong-passwords.php +260 -0
  142. includes/tweaks/password-requirements/libs/zxcvbn-php/index.php +1 -0
  143. includes/tweaks/password-requirements/libs/zxcvbn-php/matcher.php +103 -0
  144. includes/tweaks/password-requirements/libs/zxcvbn-php/matchers/adjacency_graphs.json +1 -0
  145. includes/tweaks/password-requirements/libs/zxcvbn-php/matchers/bruteforce.php +72 -0
  146. includes/tweaks/password-requirements/libs/zxcvbn-php/matchers/date.php +319 -0
  147. includes/tweaks/password-requirements/libs/zxcvbn-php/matchers/dictionary-l33t.php +114 -0
  148. includes/tweaks/password-requirements/libs/zxcvbn-php/matchers/dictionary-reverse.php +44 -0
  149. includes/tweaks/password-requirements/libs/zxcvbn-php/matchers/dictionary.php +211 -0
  150. includes/tweaks/password-requirements/libs/zxcvbn-php/matchers/index.php +1 -0
  151. includes/tweaks/password-requirements/libs/zxcvbn-php/matchers/ranked_frequency_list-english_wikipedia.json +1 -0
admin/ajax/logs.php CHANGED
@@ -3,22 +3,22 @@
3
  /**
4
  * Used to clean-up logs.
5
  */
6
- add_action( 'wp_ajax_wlogger-logs-cleanup', function () {
7
- check_admin_referer( 'wlogger_clean_logs', 'nonce' );
8
 
9
- if ( ! current_user_can( 'manage_options' ) ) {
10
- wp_die( - 1 );
11
  }
12
 
13
- if ( ! \WBCR\Logger\Writter::clean_up() ) {
14
- wp_send_json_error( [
15
- 'message' => esc_html__( 'Failed to clean-up logs. Please try again later.', 'robin-image-optimizer' ),
16
- 'type' => 'danger',
17
- ] );
18
  }
19
 
20
- wp_send_json( [
21
- 'message' => esc_html__( 'Logs clean-up successfully', 'robin-image-optimizer' ),
22
- 'type' => 'success',
23
- ] );
24
- } );
3
  /**
4
  * Used to clean-up logs.
5
  */
6
+ add_action('wp_ajax_wtitan-logger-logs-cleanup', function () {
7
+ check_admin_referer('wlogger_clean_logs', 'nonce');
8
 
9
+ if( !current_user_can('manage_options') ) {
10
+ wp_die(-1);
11
  }
12
 
13
+ if( !\WBCR\Titan\Logger\Writter::clean_up() ) {
14
+ wp_send_json_error([
15
+ 'message' => esc_html__('Failed to clean-up logs. Please try again later.', 'robin-image-optimizer'),
16
+ 'type' => 'danger',
17
+ ]);
18
  }
19
 
20
+ wp_send_json([
21
+ 'message' => esc_html__('Logs clean-up successfully', 'robin-image-optimizer'),
22
+ 'type' => 'success',
23
+ ]);
24
+ });
admin/assets/css/about-premium.css CHANGED
@@ -1,70 +1,70 @@
1
- #WBCR .wantispam-about-premium h1 {
2
- font-size: 35px;
3
- }
4
- #WBCR .wantispam-about-premium__title {
5
- text-align: left;
6
- margin-bottom: 20px;
7
- }
8
- #WBCR .wantispam-about-premium__title--h2 {
9
- margin-top: 60px;
10
- }
11
- #WBCR .wantispam-about-premium__headline-title {
12
- font-size: 15px;
13
- background: #fff;
14
- padding: 20px;
15
- text-align: left;
16
- }
17
- #WBCR .wantispam-about-premium__columns {
18
- margin: 40px 0;
19
- }
20
- #WBCR .wantispam-about-premium__column {
21
- text-align: center;
22
- background: #fff;
23
- margin-left: 5px;
24
- padding: 20px 0;
25
- }
26
- #WBCR .wantispam-about-premium__column .dashicons {
27
- color: #ffc107;
28
- font-size: 106px;
29
- width: 106px;
30
- height: 106px;
31
- }
32
- #WBCR .wantispam-about-premium__column p {
33
- font-size: 13px;
34
- color: #909090;
35
- padding: 6px;
36
- }
37
- #WBCR .wantispam-about-premium__activate-trial {
38
- text-align: center;
39
- padding: 30px;
40
- background: #fff;
41
- border: 1px dashed #d0a295;
42
- margin-top: 60px;
43
- }
44
- #WBCR .wantispam-about-premium__activate-trial p {
45
- font-size: 13px;
46
- color: #909090;
47
- margin-top: 20px;
48
- }
49
- #WBCR .wantispam-about-premium__activate-trial-button {
50
- -webkit-box-shadow: 0 1px 1px rgba(0, 0, 0, 0.1), inset 0 1px 1px rgba(255, 255, 255, 0.7);
51
- color: #ffffff;
52
- border: 1px solid #fe5421;
53
- background: #fe5421;
54
- text-transform: uppercase;
55
- }
56
- #WBCR .wantispam-about-premium ul {
57
- padding-left: 60px;
58
- list-style: square;
59
- }
60
- #WBCR .wantispam-about-premium ul li {
61
- font-size: 17px;
62
- margin-bottom: 10px;
63
- }
64
- #WBCR .wantispam-about-premium ul li p {
65
- font-size: 13px;
66
- color: #909090;
67
- background: #fff;
68
- padding: 6px;
69
- }
70
  /*# sourceMappingURL=about-premium.css.map */
1
+ #WBCR .wantispam-about-premium h1 {
2
+ font-size: 35px;
3
+ }
4
+ #WBCR .wantispam-about-premium__title {
5
+ text-align: left;
6
+ margin-bottom: 20px;
7
+ }
8
+ #WBCR .wantispam-about-premium__title--h2 {
9
+ margin-top: 60px;
10
+ }
11
+ #WBCR .wantispam-about-premium__headline-title {
12
+ font-size: 15px;
13
+ background: #fff;
14
+ padding: 20px;
15
+ text-align: left;
16
+ }
17
+ #WBCR .wantispam-about-premium__columns {
18
+ margin: 40px 0;
19
+ }
20
+ #WBCR .wantispam-about-premium__column {
21
+ text-align: center;
22
+ background: #fff;
23
+ margin-left: 5px;
24
+ padding: 20px 0;
25
+ }
26
+ #WBCR .wantispam-about-premium__column .dashicons {
27
+ color: #ffc107;
28
+ font-size: 106px;
29
+ width: 106px;
30
+ height: 106px;
31
+ }
32
+ #WBCR .wantispam-about-premium__column p {
33
+ font-size: 13px;
34
+ color: #909090;
35
+ padding: 6px;
36
+ }
37
+ #WBCR .wantispam-about-premium__activate-trial {
38
+ text-align: center;
39
+ padding: 30px;
40
+ background: #fff;
41
+ border: 1px dashed #d0a295;
42
+ margin-top: 60px;
43
+ }
44
+ #WBCR .wantispam-about-premium__activate-trial p {
45
+ font-size: 13px;
46
+ color: #909090;
47
+ margin-top: 20px;
48
+ }
49
+ #WBCR .wantispam-about-premium__activate-trial-button {
50
+ -webkit-box-shadow: 0 1px 1px rgba(0, 0, 0, 0.1), inset 0 1px 1px rgba(255, 255, 255, 0.7);
51
+ color: #ffffff;
52
+ border: 1px solid #fe5421;
53
+ background: #fe5421;
54
+ text-transform: uppercase;
55
+ }
56
+ #WBCR .wantispam-about-premium ul {
57
+ padding-left: 60px;
58
+ list-style: square;
59
+ }
60
+ #WBCR .wantispam-about-premium ul li {
61
+ font-size: 17px;
62
+ margin-bottom: 10px;
63
+ }
64
+ #WBCR .wantispam-about-premium ul li p {
65
+ font-size: 13px;
66
+ color: #909090;
67
+ background: #fff;
68
+ padding: 6px;
69
+ }
70
  /*# sourceMappingURL=about-premium.css.map */
admin/assets/css/about-premium.css.map DELETED
@@ -1 +0,0 @@
1
- {"version":3,"sources":["about-premium.less"],"names":[],"mappings":"AAAA,KACE,yBACE;EACE,eAAA;;AAGF,KALF,yBAKG;EACC,gBAAA;EACA,mBAAA;;AAEA,KATJ,yBAKG,OAIE;EACC,gBAAA;;AAIJ,KAdF,yBAcG;EACC,eAAA;EACA,gBAAA;EACA,aAAA;EACA,gBAAA;;AAGF,KArBF,yBAqBG;EACC,cAAA;;AAGF,KAzBF,yBAyBG;EACC,kBAAA;EACA,gBAAA;EACA,gBAAA;EACA,eAAA;;AAJF,KAzBF,yBAyBG,QAMC;EACE,cAAA;EACA,gBAAA;EACA,YAAA;EACA,aAAA;;AAVJ,KAzBF,yBAyBG,QAaC;EACE,eAAA;EACA,cAAA;EACA,YAAA;;AAKJ,KA9CF,yBA8CG;EACC,kBAAA;EACA,aAAA;EACA,gBAAA;EACA,0BAAA;EACA,gBAAA;;AALF,KA9CF,yBA8CG,gBAOC;EACE,eAAA;EACA,cAAA;EACA,gBAAA;;AAGF,KA3DJ,yBA8CG,gBAaE;EACC,0FAAA;EACA,cAAA;EACA,yBAAA;EACA,mBAAA;EACA,yBAAA;;AAjER,KACE,yBAqEE;EACE,kBAAA;EACA,kBAAA;;AAxEN,KACE,yBAqEE,GAIE;EACE,eAAA;EACA,mBAAA;;AA5ER,KACE,yBAqEE,GAIE,GAIE;EACE,eAAA;EACA,cAAA;EACA,gBAAA;EACA,YAAA","file":"about-premium.css"}
 
admin/assets/css/dashboard-dashboard.css ADDED
@@ -0,0 +1,302 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #wpfooter {
2
+ position: initial !important;
3
+ /*display: inline;*/
4
+ }
5
+
6
+ .wt-dashboard-container {
7
+ margin: 10px 5px 10px 5px;
8
+ /*display: inline-block;*/
9
+ }
10
+
11
+ .wt-dashboard-block {
12
+ /*width: 100%;*/
13
+ border: 1px solid #d6d6d6;
14
+ background: #fff;
15
+ text-align: center;
16
+ border-radius: 5px;
17
+ min-height: 16em !important;
18
+ }
19
+
20
+ .wt-row {
21
+ width: 100%;
22
+ display: inline-block;
23
+ }
24
+
25
+ .wt-dashboard-block div.row {
26
+ padding: 0 15px 0 15px;
27
+ }
28
+
29
+ .wt-block-gutter {
30
+ padding: 0 10px 0 5px !important;
31
+ }
32
+
33
+ .wt-dashboard-block-header {
34
+ text-align: left;
35
+ padding: 0 !important;
36
+ }
37
+
38
+ .wt-dashboard-block-header h4 {
39
+ font-family: Roboto, "Helvetica Neue", Helvetica, Arial, sans-serif;
40
+ font-size: 12px;
41
+ font-weight: 600;
42
+ margin: 10px;
43
+ color: #868686;
44
+ text-transform: uppercase;
45
+ }
46
+
47
+ .wt-dashboard-block-header-right {
48
+ text-align: right;
49
+ }
50
+
51
+ .wt-dashboard-block-content {
52
+ margin: 10px -20px 10px 20px;
53
+ text-align: left;
54
+ }
55
+
56
+ .wt-dashboard-block-content-right {
57
+ margin: 10px 20px 10px -20px;
58
+ text-align: right;
59
+ }
60
+
61
+ .wt-block-description {
62
+ font-family: Roboto, "Helvetica Neue", Helvetica, Arial, sans-serif;
63
+ font-size: 12px;
64
+ margin-top: 10px;
65
+ color: #868686;
66
+ }
67
+
68
+ .wt-firewall-icon-ok, .wt-firewall-icon-clock, .wt-firewall-icon-dissmiss, .wt-dashboard-icon-loader {
69
+ width: 50px;
70
+ height: 60px;
71
+ display: inline-block;
72
+ }
73
+
74
+ .wt-firewall-icon-ok {
75
+ background: url('../img/check.png') no-repeat center bottom;
76
+ background-size: contain;
77
+ }
78
+
79
+ .wt-firewall-icon-clock {
80
+ background: url('../img/clock.png') no-repeat center bottom;
81
+ background-size: contain;
82
+ }
83
+
84
+ .wt-firewall-icon-dissmiss {
85
+ background: url('../img/x.png') no-repeat center bottom;
86
+ background-size: contain;
87
+ }
88
+
89
+ .wt-dashboard-icon-loader {
90
+ background: transparent url("../img/loader.gif") no-repeat center bottom;
91
+ background-size: contain;
92
+ }
93
+
94
+ .wt-block-loading {
95
+ opacity: 0.5;
96
+ }
97
+
98
+ .wt-checkbox {
99
+ background-color: #5d05b7 !important;
100
+ border: 2px solid #5d05b7 !important;
101
+ border-radius: 5px !important;
102
+ }
103
+
104
+ .factory-bootstrap-427 .factory-buttons-way .factory-on.active {
105
+ background-color: #5d05b7 !important;
106
+ box-shadow: none !important;
107
+ border: none !important;
108
+ }
109
+
110
+ .factory-bootstrap-427 .factory-buttons-way .factory-off.active {
111
+ background-color: #5d05b7 !important;
112
+ box-shadow: none !important;
113
+ border: none !important;
114
+ }
115
+
116
+ .wt-scanner-tabs-container {
117
+ background: #fff !important;
118
+ }
119
+
120
+ .factory-bootstrap-427 .nav-tabs > li.active > a, .factory-bootstrap-427 .nav-tabs > li.active > a:focus, .factory-bootstrap-427 .nav-tabs > li.active > a:hover {
121
+ background-color: #efefef !important;
122
+ }
123
+
124
+ .wtitan-tab-table-container {
125
+ background-color: #efefef !important;
126
+ }
127
+
128
+ .wtitan-scanner-vulner-table-container table thead td {
129
+ background-color: #efefef !important;
130
+ }
131
+ .wtitan-scanner-results table td
132
+ {
133
+ word-break: break-word;
134
+ }
135
+ .wtitan-scanner-results table td code
136
+ {
137
+ word-break: break-word;
138
+ white-space: normal;
139
+ }
140
+ /*----------------*/
141
+ li.wt-tabs-pro a:after {
142
+ display: inline-block;
143
+ position: relative;
144
+ content: 'PRO';
145
+ background: #f6065b;
146
+ border-radius: 4px;
147
+ color: #fff;
148
+ font-size: 10px;
149
+ line-height: 1;
150
+ font-style: normal;
151
+ padding: 4px 6px;
152
+ margin-left: 4px;
153
+ vertical-align: top;
154
+ top: 0;
155
+ left: 5px;
156
+ right: 0;
157
+ z-index: 11;
158
+ cursor: pointer;
159
+ }
160
+
161
+ .wt-dashboard-pro-span {
162
+ background: #f6065b;
163
+ border-radius: 4px;
164
+ color: #fff;
165
+ font-size: 10px;
166
+ line-height: 1;
167
+ font-style: normal;
168
+ padding: 4px 6px;
169
+ margin-left: 4px;
170
+ }
171
+
172
+ .wt-scan-icon-loader {
173
+ height: 55px;
174
+ background: transparent url("../img/loader.gif");
175
+ background-repeat: no-repeat;
176
+ background-position: center center;
177
+ background-size: 60px;
178
+ }
179
+
180
+ .wt-magenta-text {
181
+ color: #5d05b7;
182
+ font-weight: 500;
183
+ }
184
+
185
+ .wt-dashboard-audit-button, .wt-dashboard-scan-button {
186
+ font-size: 16px !important;
187
+ }
188
+
189
+ .wt-dashboard-audit-button {
190
+ margin: 20px 10px 0 0 !important;
191
+ }
192
+
193
+ /*--- Scanner -------*/
194
+ .wt-dashboard-scan-button-loader, .wt-scan-icon-loader {
195
+ display: inline-block;
196
+ height: 34px;
197
+ width: 34px;
198
+ background: transparent url("../img/loader.gif") no-repeat center bottom;
199
+ background-size: contain;
200
+ vertical-align: bottom;
201
+
202
+ }
203
+
204
+ .wt-scanner-chart, .wt-scanner-legend {
205
+ border-radius: 5px;
206
+ margin: 0 10px 10px 10px;
207
+ }
208
+
209
+ .wt-scanner-chart-clean[style*="width: 0%"],
210
+ .wt-scanner-chart-suspicious[style*="width: 0%"],
211
+ .wt-scanner-chart-notverified[style*="width: 0%"] {
212
+ display: none !important;
213
+ }
214
+
215
+ .wt-scanner-chart-clean, .wt-scanner-chart-suspicious, .wt-scanner-chart-notverified {
216
+ border: 1px solid #c1c1c1;
217
+ border-radius: 5px;
218
+ display: inline-block !important;
219
+ content: "";
220
+ height: 30px;
221
+ margin: 0 -5px;
222
+ position: relative;
223
+ }
224
+
225
+ .wt-scanner-chart-clean {
226
+ background-color: #5d05b7;
227
+ z-index: 10;
228
+ border-right: 0;
229
+ }
230
+
231
+ .wt-scanner-chart-suspicious {
232
+ background-color: #f6065b;
233
+ z-index: 9;
234
+ border-right: 0;
235
+ }
236
+
237
+ .wt-scanner-chart-notverified {
238
+ background-color: #dadada;
239
+ z-index: 8;
240
+ }
241
+
242
+ .wt-scanner-legend td {
243
+ padding: 0 0 0 10px;
244
+ font-size: 12px;
245
+ -webkit-transition: background-color 200ms ease-in-out;
246
+ -moz-transition: background-color 200ms ease-in-out;
247
+ -o-transition: background-color 200ms ease-in-out;
248
+ transition: background-color 200ms ease-in-out;
249
+ }
250
+
251
+ span.wt-legend-item {
252
+ display: inline-block;
253
+ width: 30px;
254
+ height: 30px;
255
+ border-radius: 50%;
256
+ padding-top: 6px;
257
+ }
258
+
259
+ .wt-scanner-legend td:nth-child(odd) {
260
+ padding: 0 0 0 20px !important;
261
+ }
262
+
263
+ .wbcr-titan-content {
264
+ overflow: auto;
265
+ height: 500px;
266
+ }
267
+
268
+ .wbcr-titan-content table thead th {
269
+ text-align: center;
270
+ }
271
+
272
+ .wbcr-titan-content table tbody td {
273
+ word-break: break-word;
274
+ text-align: left;
275
+ padding: 10px 20px;
276
+ }
277
+
278
+ .wbcr-titan-content table tbody td:first-child {
279
+ width: 50%;
280
+ }
281
+
282
+ .wt-nobutton {
283
+ cursor: inherit !important;
284
+ margin: 0 5px;
285
+ }
286
+
287
+ .wt-dashboard-form-label {
288
+ width: unset;
289
+ float: unset;
290
+ display: block;
291
+ vertical-align: middle;
292
+ font-size: 16px;
293
+ margin-right: 10px;
294
+ }
295
+
296
+ .wt-dashboard-block-content .factory-buttons-group .btn-default {
297
+ font-size: 16px;
298
+ }
299
+
300
+ .factory-hints {
301
+ text-align: left !important;
302
+ }
admin/assets/css/firewall/firewall-attacks-log.css ADDED
@@ -0,0 +1,68 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ .wtitan-attacks-log {
2
+ padding: 0 20px;
3
+ }
4
+ .wtitan-attacks-log.wtitan-section-disabled {
5
+ opacity: 0.4;
6
+ }
7
+ .wtitan-attacks-log.wtitan-section-disabled select,
8
+ .wtitan-attacks-log.wtitan-section-disabled button,
9
+ .wtitan-attacks-log.wtitan-section-disabled a {
10
+ pointer-events: none;
11
+ cursor: not-allowed;
12
+ opacity: 0.65;
13
+ filter: alpha(opacity=65);
14
+ -webkit-box-shadow: none;
15
+ box-shadow: none;
16
+ }
17
+ .wtitan-attacks-log__nav {
18
+ padding: 15px;
19
+ text-align: right;
20
+ }
21
+ .wtitan-attacks-log__pages {
22
+ display: inline-block;
23
+ }
24
+ .wtitan-attacks-log__pages .page-numbers {
25
+ display: inline-block;
26
+ vertical-align: baseline;
27
+ text-decoration: none;
28
+ min-width: 30px;
29
+ min-height: 30px;
30
+ margin: 0;
31
+ padding: 0 4px;
32
+ font-size: 13px;
33
+ line-height: 2.1;
34
+ text-align: center;
35
+ box-shadow: 0 0 0 transparent;
36
+ border-radius: 4px;
37
+ border: 1px solid #e6e6e6;
38
+ background-color: #efefef;
39
+ color: #424242;
40
+ }
41
+ .wtitan-attacks-log__pages .page-numbers.current {
42
+ background-color: #fff;
43
+ }
44
+ .wtitan-attacks-log__table td,
45
+ .wtitan-attacks-log__table th {
46
+ padding: 10px 20px !important;
47
+ }
48
+ .wtitan-attacks-log__table-column-event ul {
49
+ margin: 0;
50
+ padding: 0;
51
+ }
52
+ .wtitan-attacks-log__table-column-event ul li {
53
+ display: block;
54
+ margin: 0;
55
+ padding: 3px 0;
56
+ }
57
+ .wtitan-attacks-log__table-label--red {
58
+ background: #dc7a7a;
59
+ color: #fff;
60
+ padding: 3px;
61
+ }
62
+ .wtitan-ip-blocking__form {
63
+ margin: 0 auto;
64
+ }
65
+ .wtitan-ip-blocking__form__form-label {
66
+ display: block;
67
+ }
68
+ /*# sourceMappingURL=firewall-attacks-log.css.map */
admin/assets/css/firewall/firewall-attacks-log.less ADDED
@@ -0,0 +1,86 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ .wtitan-attacks-log {
2
+ padding: 0 20px;
3
+
4
+ &.wtitan-section-disabled {
5
+ opacity: 0.4;
6
+
7
+ select, button, a {
8
+ pointer-events: none;
9
+ cursor: not-allowed;
10
+ opacity: 0.65;
11
+ filter: alpha(opacity=65);
12
+ -webkit-box-shadow: none;
13
+ box-shadow: none;
14
+ }
15
+ }
16
+
17
+ &__nav {
18
+ padding: 15px;
19
+ text-align: right;
20
+ }
21
+
22
+ &__pages {
23
+ display: inline-block;
24
+
25
+ .page-numbers {
26
+ display: inline-block;
27
+ vertical-align: baseline;
28
+ text-decoration: none;
29
+ min-width: 30px;
30
+ min-height: 30px;
31
+ margin: 0;
32
+ padding: 0 4px;
33
+ font-size: 13px;
34
+ line-height: 2.1;
35
+ text-align: center;
36
+ box-shadow: 0 0 0 transparent;
37
+ border-radius: 4px;
38
+ border: 1px solid #e6e6e6;
39
+ background-color: #efefef;
40
+ color: #424242;
41
+
42
+ &.current {
43
+ background-color: #fff;
44
+ }
45
+ }
46
+ }
47
+
48
+ &__form {
49
+
50
+ }
51
+
52
+ &__table {
53
+ td, th {
54
+ padding: 10px 20px !important;
55
+ }
56
+
57
+ &-column-event {
58
+ ul {
59
+ margin: 0;
60
+ padding: 0;
61
+
62
+ li {
63
+ display: block;
64
+ margin: 0;
65
+ padding: 3px 0;
66
+ }
67
+ }
68
+ }
69
+
70
+ &-label--red {
71
+ background: #dc7a7a;
72
+ color: #fff;
73
+ padding: 3px;
74
+ }
75
+ }
76
+ }
77
+
78
+ .wtitan-ip-blocking {
79
+ &__form {
80
+ margin: 0 auto;
81
+
82
+ &__form-label {
83
+ display: block;
84
+ }
85
+ }
86
+ }
admin/assets/css/firewall/firewall-dashboard.css ADDED
@@ -0,0 +1,83 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #wtitan-firewall-dashboard-top-section table {
2
+ width: 100%;
3
+ table-layout: fixed;
4
+ }
5
+ #wtitan-firewall-dashboard-top-section table td {
6
+ border: 1px solid #efefef;
7
+ background: #fff;
8
+ text-align: center;
9
+ padding: 20px;
10
+ }
11
+ #wtitan-firewall-dashboard-top-section table td h4 {
12
+ font-family: Roboto, "Helvetica Neue", Helvetica, Arial, sans-serif;
13
+ font-size: 16px;
14
+ }
15
+ .wtitan-status-circular {
16
+ position: relative;
17
+ margin: 0 auto;
18
+ }
19
+ .wtitan-status-circular-text {
20
+ position: absolute;
21
+ left: 50%;
22
+ top: 50%;
23
+ padding: 0;
24
+ margin: 0;
25
+ transform: translate(-50%, -50%);
26
+ color: #aaa;
27
+ font-family: Roboto, "Helvetica Neue", Helvetica, Arial, sans-serif;
28
+ font-size: 1.3125rem;
29
+ font-weight: 300;
30
+ line-height: 1.5;
31
+ }
32
+ .wtitan-status-circular .wf-status-overlay-text {
33
+ position: absolute;
34
+ left: 50%;
35
+ top: 50%;
36
+ padding: 0;
37
+ margin: 0;
38
+ width: 200%;
39
+ text-align: center;
40
+ transform: translate(-50%, -50%);
41
+ font-family: Roboto, "Helvetica Neue", Helvetica, Arial, sans-serif;
42
+ font-size: 0.875rem;
43
+ font-weight: normal;
44
+ line-height: 1.3125;
45
+ opacity: 0;
46
+ color: #777;
47
+ }
48
+ .wf-status-warning,
49
+ .wf-status-critical,
50
+ .wf-status-payment-expiring,
51
+ .wf-status-renewing {
52
+ width: 100px;
53
+ height: 100px;
54
+ margin-bottom: 1rem;
55
+ }
56
+ .wf-status-warning svg path {
57
+ fill: #fcb214;
58
+ }
59
+ .wf-status-critical svg path {
60
+ fill: #930000;
61
+ }
62
+ .wf-status-payment-expiring svg rect,
63
+ .wf-status-payment-expiring svg path {
64
+ fill: #930000;
65
+ }
66
+ .wf-status-renewing svg rect,
67
+ .wf-status-renewing svg path {
68
+ fill: #11967a;
69
+ }
70
+ .wtitan-section-disabled {
71
+ opacity: 0.4;
72
+ }
73
+ .wtitan-section-disabled select,
74
+ .wtitan-section-disabled button,
75
+ .wtitan-section-disabled a {
76
+ pointer-events: none;
77
+ cursor: not-allowed;
78
+ opacity: 0.65;
79
+ filter: alpha(opacity=65);
80
+ -webkit-box-shadow: none;
81
+ box-shadow: none;
82
+ }
83
+ /*# sourceMappingURL=firewall-dashboard.css.map */
admin/assets/css/firewall/firewall-dashboard.less ADDED
@@ -0,0 +1,91 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ #wtitan-firewall-dashboard-top-section {
3
+ table {
4
+ width: 100%;
5
+ table-layout: fixed;
6
+
7
+
8
+ td {
9
+ border: 1px solid #efefef;
10
+ background: #fff;
11
+ text-align: center;
12
+ padding: 20px;
13
+
14
+ h4 {
15
+ font-family: Roboto, "Helvetica Neue", Helvetica, Arial, sans-serif;
16
+ font-size: 16px;
17
+ }
18
+ }
19
+ }
20
+ }
21
+
22
+ .wtitan-status-circular {
23
+ position: relative;
24
+ margin: 0 auto;
25
+ }
26
+
27
+ .wtitan-status-circular-text {
28
+ position: absolute;
29
+ left: 50%;
30
+ top: 50%;
31
+ padding: 0;
32
+ margin: 0;
33
+ transform: translate(-50%, -50%);
34
+ color: #aaa;
35
+ font-family: Roboto, "Helvetica Neue", Helvetica, Arial, sans-serif;
36
+ font-size: 1.3125rem;
37
+ font-weight: 300;
38
+ line-height: 1.5
39
+ }
40
+
41
+ .wtitan-status-circular .wf-status-overlay-text {
42
+ position: absolute;
43
+ left: 50%;
44
+ top: 50%;
45
+ padding: 0;
46
+ margin: 0;
47
+ width: 200%;
48
+ text-align: center;
49
+ transform: translate(-50%, -50%);
50
+ font-family: Roboto, "Helvetica Neue", Helvetica, Arial, sans-serif;
51
+ font-size: .875rem;
52
+ font-weight: normal;
53
+ line-height: 1.3125;
54
+ opacity: 0.0;
55
+ color: #777
56
+ }
57
+
58
+ .wf-status-warning, .wf-status-critical, .wf-status-payment-expiring, .wf-status-renewing {
59
+ width: 100px;
60
+ height: 100px;
61
+ margin-bottom: 1rem
62
+ }
63
+
64
+ .wf-status-warning svg path {
65
+ fill: #fcb214
66
+ }
67
+
68
+ .wf-status-critical svg path {
69
+ fill: #930000
70
+ }
71
+
72
+ .wf-status-payment-expiring svg rect, .wf-status-payment-expiring svg path {
73
+ fill: #930000
74
+ }
75
+
76
+ .wf-status-renewing svg rect, .wf-status-renewing svg path {
77
+ fill: #11967a
78
+ }
79
+
80
+ .wtitan-section-disabled {
81
+ opacity: 0.4;
82
+
83
+ select, button, a {
84
+ pointer-events: none;
85
+ cursor: not-allowed;
86
+ opacity: 0.65;
87
+ filter: alpha(opacity=65);
88
+ -webkit-box-shadow: none;
89
+ box-shadow: none;
90
+ }
91
+ }
admin/assets/css/firewall/firewall-ips-blocking.css ADDED
@@ -0,0 +1,98 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ .wtitan-ips-blocking {
2
+ padding: 5px 20px;
3
+ }
4
+ .wtitan-ips-blocking.wtitan-section-disabled {
5
+ opacity: 0.4;
6
+ }
7
+ .wtitan-ips-blocking.wtitan-section-disabled select,
8
+ .wtitan-ips-blocking.wtitan-section-disabled button,
9
+ .wtitan-ips-blocking.wtitan-section-disabled a {
10
+ pointer-events: none;
11
+ cursor: not-allowed;
12
+ opacity: 0.65;
13
+ filter: alpha(opacity=65);
14
+ -webkit-box-shadow: none;
15
+ box-shadow: none;
16
+ }
17
+ .wtitan-ips-blocking__controls {
18
+ display: flex;
19
+ flex-direction: row;
20
+ }
21
+ .wtitan-ips-blocking__controls-left {
22
+ flex-grow: 0 !important;
23
+ }
24
+ .wtitan-ips-blocking__controls-right {
25
+ flex-grow: 1 !important;
26
+ text-align: right;
27
+ }
28
+ .wtitan-ips-blocking__controls li {
29
+ display: block;
30
+ }
31
+ .wtitan-ips-blocking__table th,
32
+ .wtitan-ips-blocking__table td {
33
+ padding: 20px;
34
+ }
35
+ .wtitan-ips-blocking__table th {
36
+ background-color: #ebebeb;
37
+ color: #777;
38
+ font-weight: bold;
39
+ text-align: left;
40
+ border-left: 1px solid #bdbdbd;
41
+ }
42
+ .wtitan-ips-blocking__table th:first-child {
43
+ border: 0;
44
+ }
45
+ .wtitan-ips-blocking-modal__tabs {
46
+ border-bottom: 1px solid #d0d0d0;
47
+ }
48
+ .wtitan-ips-blocking-modal__tab {
49
+ display: inline-block;
50
+ border: 1px solid #ccc;
51
+ border-bottom: none;
52
+ margin: 0;
53
+ padding: 5px 10px;
54
+ font-size: 14px;
55
+ line-height: 1.71428571;
56
+ font-weight: 600;
57
+ background: #e5e5e5;
58
+ color: #555;
59
+ text-decoration: none;
60
+ white-space: nowrap;
61
+ }
62
+ .wtitan-ips-blocking-modal__tab a {
63
+ color: #555;
64
+ text-decoration: none;
65
+ font-size: 13px;
66
+ }
67
+ .wtitan-ips-blocking-modal__tab a:active,
68
+ .wtitan-ips-blocking-modal__tab a:focus {
69
+ outline: none;
70
+ box-shadow: none;
71
+ }
72
+ .wtitan-ips-blocking-modal__tab--active {
73
+ background: #ffffff;
74
+ box-shadow: 0 2px 0 #efefef;
75
+ }
76
+ .wtitan-ips-blocking-modal__tab-content {
77
+ display: none;
78
+ }
79
+ .wtitan-ips-blocking-modal__tab-content--active {
80
+ display: block;
81
+ }
82
+ .wtitan-ips-blocking-modal__form {
83
+ margin: 0 auto;
84
+ }
85
+ .wtitan-ips-blocking-modal__form-label {
86
+ display: block;
87
+ font-size: 14px;
88
+ font-weight: 600;
89
+ }
90
+ .wtitan-ips-blocking-modal__form input[type="text"] {
91
+ width: 100%;
92
+ height: 40px;
93
+ }
94
+ .wtitan-ips-blocking-modal__form textarea {
95
+ width: 100%;
96
+ height: 100px;
97
+ }
98
+ /*# sourceMappingURL=firewall-ips-blocking.css.map */
admin/assets/css/firewall/firewall-ips-blocking.less ADDED
@@ -0,0 +1,121 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ .wtitan-ips-blocking {
2
+ padding: 5px 20px;
3
+
4
+ &.wtitan-section-disabled {
5
+ opacity: 0.4;
6
+
7
+ select, button, a {
8
+ pointer-events: none;
9
+ cursor: not-allowed;
10
+ opacity: 0.65;
11
+ filter: alpha(opacity=65);
12
+ -webkit-box-shadow: none;
13
+ box-shadow: none;
14
+ }
15
+ }
16
+
17
+
18
+ &__controls {
19
+ display: flex;
20
+ flex-direction: row;
21
+
22
+ &-left {
23
+ flex-grow: 0 !important;
24
+ }
25
+
26
+ &-right {
27
+ flex-grow: 1 !important;
28
+ text-align: right;
29
+ }
30
+
31
+ li {
32
+ display: block;
33
+ }
34
+ }
35
+
36
+ &__table {
37
+ th, td {
38
+ padding: 20px;
39
+ }
40
+
41
+ th {
42
+ background-color: #ebebeb;
43
+ color: #777;
44
+ font-weight: bold;
45
+ text-align: left;
46
+ border-left: 1px solid #bdbdbd;
47
+
48
+ &:first-child {
49
+ border: 0;
50
+ }
51
+ }
52
+ }
53
+ }
54
+
55
+ .wtitan-ips-blocking-modal {
56
+ &__tabs {
57
+ border-bottom: 1px solid #d0d0d0;
58
+ }
59
+
60
+ &__tab {
61
+ display: inline-block;
62
+ border: 1px solid #ccc;
63
+ border-bottom: none;
64
+ margin: 0;
65
+ padding: 5px 10px;
66
+ font-size: 14px;
67
+ line-height: 1.71428571;
68
+ font-weight: 600;
69
+ background: #e5e5e5;
70
+ color: #555;
71
+ text-decoration: none;
72
+ white-space: nowrap;
73
+
74
+ a {
75
+ color: #555;
76
+ text-decoration: none;
77
+ font-size: 13px;
78
+
79
+ &:active, &:focus {
80
+ outline: none;
81
+ box-shadow: none;
82
+ }
83
+ }
84
+
85
+ &--active {
86
+ background: #ffffff;
87
+ box-shadow: 0 2px 0 #efefef;
88
+ }
89
+
90
+ &-content {
91
+ display: none;
92
+
93
+ &--active {
94
+ display: block;
95
+ }
96
+ }
97
+ }
98
+
99
+
100
+ &__form {
101
+ margin: 0 auto;
102
+
103
+ &-label {
104
+ display: block;
105
+ font-size: 14px;
106
+ font-weight: 600;
107
+ }
108
+
109
+ input[type="text"] {
110
+ width: 100%;
111
+ height: 40px;
112
+ }
113
+
114
+ textarea {
115
+ width: 100%;
116
+ height: 100px;
117
+ }
118
+ }
119
+ }
120
+
121
+
admin/assets/css/firewall/firewall-settings.css ADDED
@@ -0,0 +1,205 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #WBCR .factory-textarea.factory-control-premium-label:before {
2
+ border: 1px solid red;
3
+ display: inline-block;
4
+ position: absolute;
5
+ content: 'PRO';
6
+ background: #f6065b;
7
+ border-radius: 4px;
8
+ color: #fff;
9
+ font-size: 10px;
10
+ line-height: 1;
11
+ font-style: normal;
12
+ padding: 4px 6px;
13
+ margin-left: 4px;
14
+ vertical-align: top;
15
+ top: 0px;
16
+ left: -5px;
17
+ right: auto;
18
+ bottom: auto;
19
+ z-index: 11;
20
+ cursor: pointer;
21
+ }
22
+ #WBCR .factory-list.factory-control-premium-label {
23
+ box-shadow: 20px 20px 20px #222;
24
+ }
25
+ #WBCR .factory-list.factory-control-premium-label:before {
26
+ display: inline-block;
27
+ position: absolute;
28
+ content: 'PRO';
29
+ background: #f6065b;
30
+ border-radius: 4px;
31
+ color: #fff;
32
+ font-size: 10px;
33
+ line-height: 1;
34
+ font-style: normal;
35
+ padding: 4px 6px;
36
+ margin-left: 4px;
37
+ vertical-align: top;
38
+ top: 0px;
39
+ left: 70px;
40
+ right: auto;
41
+ bottom: auto;
42
+ z-index: 11;
43
+ cursor: pointer;
44
+ }
45
+ #WBCR .factory-checkbox.factory-control-premium-label:after {
46
+ display: inline-block;
47
+ position: absolute;
48
+ content: 'PRO';
49
+ background: #f6065b;
50
+ border-radius: 4px;
51
+ color: #fff;
52
+ font-size: 10px;
53
+ line-height: 1;
54
+ font-style: normal;
55
+ padding: 4px 6px;
56
+ margin-left: 4px;
57
+ vertical-align: top;
58
+ top: -10px;
59
+ left: auto;
60
+ right: -15px;
61
+ bottom: auto;
62
+ z-index: 11;
63
+ cursor: pointer;
64
+ }
65
+ #WBCR .factory-dropdown.factory-control-premium-label .factory-buttons-group:after {
66
+ display: inline-block;
67
+ position: absolute;
68
+ content: 'PRO';
69
+ background: #f6065b;
70
+ border-radius: 4px;
71
+ color: #fff;
72
+ font-size: 10px;
73
+ line-height: 1;
74
+ font-style: normal;
75
+ padding: 4px 6px;
76
+ margin-left: 4px;
77
+ vertical-align: top;
78
+ top: -10px;
79
+ left: auto;
80
+ right: -15px;
81
+ bottom: auto;
82
+ z-index: 11;
83
+ cursor: pointer;
84
+ }
85
+ #WBCR .factory-checkbox.factory-control--disabled,
86
+ #WBCR .factory-dropdown.factory-control--disabled,
87
+ #WBCR .factory-textarea.factory-control--disabled,
88
+ #WBCR .factory-list.factory-control--disabled,
89
+ #WBCR .factory-checkbox.factory-control--disabled input,
90
+ #WBCR .factory-dropdown.factory-control--disabled input,
91
+ #WBCR .factory-textarea.factory-control--disabled input,
92
+ #WBCR .factory-list.factory-control--disabled input,
93
+ #WBCR .factory-checkbox.factory-control--disabled button,
94
+ #WBCR .factory-dropdown.factory-control--disabled button,
95
+ #WBCR .factory-textarea.factory-control--disabled button,
96
+ #WBCR .factory-list.factory-control--disabled button {
97
+ pointer-events: none;
98
+ cursor: not-allowed;
99
+ opacity: 0.65;
100
+ filter: alpha(opacity=65);
101
+ -webkit-box-shadow: none;
102
+ box-shadow: none;
103
+ }
104
+ #WBCR .wtitan-section-disabled {
105
+ opacity: 0.4;
106
+ }
107
+ #WBCR .wtitan-section-disabled select,
108
+ #WBCR .wtitan-section-disabled button,
109
+ #WBCR .wtitan-section-disabled a {
110
+ pointer-events: none;
111
+ cursor: not-allowed;
112
+ opacity: 0.65;
113
+ filter: alpha(opacity=65);
114
+ -webkit-box-shadow: none;
115
+ box-shadow: none;
116
+ }
117
+ .wtitan-excluded-rules {
118
+ height: 150px;
119
+ width: 100%;
120
+ padding: 10px 10px 0;
121
+ background: #fff;
122
+ border: 1px solid #ccc;
123
+ overflow-y: scroll;
124
+ overflow-x: hidden;
125
+ }
126
+ .wtitan-rate-limit-settings__control li {
127
+ display: inline-block;
128
+ width: 49%;
129
+ }
130
+ .wtitan-rate-limit-settings__control-title {
131
+ padding: 0 40px;
132
+ font-size: 13px;
133
+ }
134
+ .wtitan-rate-limit-settings__control-subtitle {
135
+ display: block;
136
+ font-size: 12px;
137
+ color: #afafaf;
138
+ }
139
+ .wtitan-rate-limit-settings__control-block-time-select {
140
+ width: 200px;
141
+ }
142
+ .wtitan-rate-limit-settings__control-action-select {
143
+ width: 168px;
144
+ }
145
+ .wtitan-rate-limit-settings__control-allowed404s-textarea {
146
+ width: 100%;
147
+ height: 100px;
148
+ }
149
+ .wtitan-whitelist {
150
+ padding: 0 20px;
151
+ }
152
+ .wtitan-whitelist__hint {
153
+ display: block;
154
+ font-size: 12px;
155
+ color: #afafaf;
156
+ }
157
+ .wtitan-whitelist__form-group {
158
+ display: inline-block;
159
+ vertical-align: middle;
160
+ }
161
+ .wtitan-whitelist__table-controls {
162
+ display: flex;
163
+ flex-direction: row;
164
+ }
165
+ .wtitan-whitelist__table-controls-left {
166
+ flex-grow: 0 !important;
167
+ }
168
+ .wtitan-whitelist__table-controls-right {
169
+ text-align: right;
170
+ flex-grow: 1 !important;
171
+ }
172
+ .wtitan-whitelist__table-controls-right select,
173
+ .wtitan-whitelist__table-controls-right input,
174
+ .wtitan-whitelist__table-controls-right a {
175
+ display: inline-block;
176
+ vertical-align: middle;
177
+ }
178
+ .wtitan-whitelist__table th,
179
+ .wtitan-whitelist__table td {
180
+ padding: 20px;
181
+ }
182
+ .wtitan-whitelist__table th {
183
+ background-color: #ebebeb;
184
+ color: #777;
185
+ font-weight: bold;
186
+ text-align: left;
187
+ border-left: 1px solid #bdbdbd;
188
+ }
189
+ .wtitan-whitelist__table th:first-child {
190
+ border: 0;
191
+ }
192
+ .wtitan-whitelist__table td {
193
+ background-color: #fafafa;
194
+ }
195
+ .wtitan-whitelist__bg-requests-controls ul > li {
196
+ display: inline-block;
197
+ margin-right: 20px;
198
+ }
199
+ .wtitan-whitelist__bg-requests-controls ul > li label {
200
+ font-weight: normal;
201
+ }
202
+ .wtitan-whitelist__bg-requests-controls ul > li input[type="checkbox"] {
203
+ margin: 0;
204
+ }
205
+ /*# sourceMappingURL=firewall-settings.css.map */
admin/assets/css/firewall/firewall-settings.less ADDED
@@ -0,0 +1,211 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #WBCR {
2
+ .premium-label(@labelPositionTop:-8px, @labelPositionRight:auto, @labelPositionBottom:auto, @labelPositionLeft:-10px) {
3
+ display: inline-block;
4
+ position: absolute;
5
+ content: 'PRO';
6
+ background: #f6065b;
7
+ border-radius: 4px;
8
+ color: #fff;
9
+ font-size: 10px;
10
+ line-height: 1;
11
+ font-style: normal;
12
+ padding: 4px 6px;
13
+ margin-left: 4px;
14
+ vertical-align: top;
15
+ top: @labelPositionTop;
16
+ left: @labelPositionLeft;
17
+ right: @labelPositionRight;
18
+ bottom: @labelPositionBottom;
19
+ z-index: 11;
20
+ cursor: pointer;
21
+ }
22
+ .premium-disabled() {
23
+ pointer-events: none;
24
+ cursor: not-allowed;
25
+ opacity: 0.65;
26
+ filter: alpha(opacity=65);
27
+ -webkit-box-shadow: none;
28
+ box-shadow: none;
29
+ }
30
+
31
+ .factory-textarea {
32
+ &.factory-control-premium-label:before {
33
+ border: 1px solid red;
34
+ .premium-label(0px, auto, auto, -5px);
35
+ }
36
+ }
37
+
38
+ .factory-list {
39
+ &.factory-control-premium-label {
40
+ box-shadow: 20px 20px 20px #222;
41
+ }
42
+
43
+ &.factory-control-premium-label:before {
44
+ .premium-label(0px, auto, auto, 70px);
45
+ }
46
+ }
47
+
48
+ .factory-checkbox {
49
+ &.factory-control-premium-label:after {
50
+ .premium-label(-10px, -15px, auto, auto);
51
+ }
52
+ }
53
+
54
+ .factory-dropdown {
55
+ &.factory-control-premium-label .factory-buttons-group:after {
56
+ .premium-label(-10px, -15px, auto, auto);
57
+ }
58
+ }
59
+
60
+ .factory-checkbox, .factory-dropdown, .factory-textarea, .factory-list {
61
+ &.factory-control--disabled,
62
+ &.factory-control--disabled input,
63
+ &.factory-control--disabled button {
64
+ .premium-disabled();
65
+ }
66
+ }
67
+
68
+ .wtitan-section-disabled {
69
+ opacity: 0.4;
70
+
71
+ select, button, a {
72
+ .premium-disabled();
73
+ }
74
+ }
75
+ }
76
+
77
+ .wtitan-excluded-rules {
78
+ height: 150px;
79
+ width: 100%;
80
+ padding: 10px 10px 0;
81
+ background: #fff;
82
+ border: 1px solid #ccc;
83
+ overflow-y: scroll;
84
+ overflow-x: hidden;
85
+ }
86
+
87
+ .wtitan-rate-limit-settings {
88
+ @elementClass: ~"wtitan-rate-limit-settings";
89
+
90
+ //@{elementClass}
91
+
92
+ &__control {
93
+ li {
94
+ display: inline-block;
95
+ width: 49%;
96
+ }
97
+
98
+ &-title {
99
+ padding: 0 40px;
100
+ font-size: 13px;
101
+ }
102
+
103
+ &-subtitle {
104
+ display: block;
105
+ font-size: 12px;
106
+ color: #afafaf;
107
+ }
108
+
109
+ &-fields {
110
+
111
+ }
112
+
113
+ &-block-time-select {
114
+ width: 200px;
115
+ }
116
+
117
+ &-action-select {
118
+ width: 168px;
119
+ }
120
+
121
+ &-allowed404s-textarea {
122
+ width: 100%;
123
+ height: 100px;
124
+ }
125
+ }
126
+ }
127
+
128
+ .wtitan-whitelist {
129
+ padding: 0 20px;
130
+
131
+ &__top-section {
132
+ ul > li {
133
+ //display: inline-block;
134
+ }
135
+ }
136
+
137
+ &__bottom-section {
138
+
139
+ }
140
+
141
+
142
+ &__hint {
143
+ display: block;
144
+ font-size: 12px;
145
+ color: #afafaf;
146
+ }
147
+
148
+ &__form-group {
149
+ display: inline-block;
150
+ vertical-align: middle;
151
+ }
152
+
153
+ &__table-controls {
154
+ display: flex;
155
+ flex-direction: row;
156
+ }
157
+
158
+ &__table-controls-left {
159
+ flex-grow: 0 !important;
160
+
161
+ }
162
+
163
+ &__table-controls-right {
164
+ text-align: right;
165
+ flex-grow: 1 !important;
166
+
167
+
168
+ select, input, a {
169
+ display: inline-block;
170
+ vertical-align: middle;
171
+ }
172
+ }
173
+
174
+ &__table {
175
+ th, td {
176
+ padding: 20px;
177
+ }
178
+
179
+ th {
180
+ background-color: #ebebeb;
181
+ color: #777;
182
+ font-weight: bold;
183
+ text-align: left;
184
+ border-left: 1px solid #bdbdbd;
185
+
186
+ &:first-child {
187
+ border: 0;
188
+ }
189
+ }
190
+
191
+ td {
192
+ background-color: #fafafa;
193
+ }
194
+ }
195
+
196
+ &__bg-requests-controls {
197
+ ul > li {
198
+ display: inline-block;
199
+ margin-right: 20px;
200
+
201
+ label {
202
+ font-weight: normal;
203
+ }
204
+
205
+ input[type="checkbox"] {
206
+ margin: 0;
207
+ }
208
+ }
209
+
210
+ }
211
+ }
admin/assets/css/libs/jquery.datetimepicker.min.css ADDED
@@ -0,0 +1 @@
 
1
+ .xdsoft_datetimepicker{box-shadow:0 5px 15px -5px rgba(0,0,0,0.506);background:#fff;border-bottom:1px solid #bbb;border-left:1px solid #ccc;border-right:1px solid #ccc;border-top:1px solid #ccc;color:#333;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;padding:8px;padding-left:0;padding-top:2px;position:absolute;z-index:9999;-moz-box-sizing:border-box;box-sizing:border-box;display:none}.xdsoft_datetimepicker.xdsoft_rtl{padding:8px 0 8px 8px}.xdsoft_datetimepicker iframe{position:absolute;left:0;top:0;width:75px;height:210px;background:transparent;border:0}.xdsoft_datetimepicker button{border:none !important}.xdsoft_noselect{-webkit-touch-callout:none;-webkit-user-select:none;-khtml-user-select:none;-moz-user-select:none;-ms-user-select:none;-o-user-select:none;user-select:none}.xdsoft_noselect::selection{background:transparent}.xdsoft_noselect::-moz-selection{background:transparent}.xdsoft_datetimepicker.xdsoft_inline{display:inline-block;position:static;box-shadow:none}.xdsoft_datetimepicker *{-moz-box-sizing:border-box;box-sizing:border-box;padding:0;margin:0}.xdsoft_datetimepicker .xdsoft_datepicker,.xdsoft_datetimepicker .xdsoft_timepicker{display:none}.xdsoft_datetimepicker .xdsoft_datepicker.active,.xdsoft_datetimepicker .xdsoft_timepicker.active{display:block}.xdsoft_datetimepicker .xdsoft_datepicker{width:224px;float:left;margin-left:8px}.xdsoft_datetimepicker.xdsoft_rtl .xdsoft_datepicker{float:right;margin-right:8px;margin-left:0}.xdsoft_datetimepicker.xdsoft_showweeks .xdsoft_datepicker{width:256px}.xdsoft_datetimepicker .xdsoft_timepicker{width:58px;float:left;text-align:center;margin-left:8px;margin-top:0}.xdsoft_datetimepicker.xdsoft_rtl .xdsoft_timepicker{float:right;margin-right:8px;margin-left:0}.xdsoft_datetimepicker .xdsoft_datepicker.active+.xdsoft_timepicker{margin-top:8px;margin-bottom:3px}.xdsoft_datetimepicker .xdsoft_monthpicker{position:relative;text-align:center}.xdsoft_datetimepicker .xdsoft_label i,.xdsoft_datetimepicker .xdsoft_prev,.xdsoft_datetimepicker .xdsoft_next,.xdsoft_datetimepicker .xdsoft_today_button{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGQAAAAeCAYAAADaW7vzAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyJpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNiAoV2luZG93cykiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6Q0NBRjI1NjM0M0UwMTFFNDk4NkFGMzJFQkQzQjEwRUIiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6Q0NBRjI1NjQ0M0UwMTFFNDk4NkFGMzJFQkQzQjEwRUIiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDpDQ0FGMjU2MTQzRTAxMUU0OTg2QUYzMkVCRDNCMTBFQiIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDpDQ0FGMjU2MjQzRTAxMUU0OTg2QUYzMkVCRDNCMTBFQiIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/PoNEP54AAAIOSURBVHja7Jq9TsMwEMcxrZD4WpBYeKUCe+kTMCACHZh4BFfHO/AAIHZGFhYkBBsSEqxsLCAgXKhbXYOTxh9pfJVP+qutnZ5s/5Lz2Y5I03QhWji2GIcgAokWgfCxNvcOCCGKqiSqhUp0laHOne05vdEyGMfkdxJDVjgwDlEQgYQBgx+ULJaWSXXS6r/ER5FBVR8VfGftTKcITNs+a1XpcFoExREIDF14AVIFxgQUS+h520cdud6wNkC0UBw6BCO/HoCYwBhD8QCkQ/x1mwDyD4plh4D6DDV0TAGyo4HcawLIBBSLDkHeH0Mg2yVP3l4TQMZQDDsEOl/MgHQqhMNuE0D+oBh0CIr8MAKyazBH9WyBuKxDWgbXfjNf32TZ1KWm/Ap1oSk/R53UtQ5xTh3LUlMmT8gt6g51Q9p+SobxgJQ/qmsfZhWywGFSl0yBjCLJCMgXail3b7+rumdVJ2YRss4cN+r6qAHDkPWjPjdJCF4n9RmAD/V9A/Wp4NQassDjwlB6XBiCxcJQWmZZb8THFilfy/lfrTvLghq2TqTHrRMTKNJ0sIhdo15RT+RpyWwFdY96UZ/LdQKBGjcXpcc1AlSFEfLmouD+1knuxBDUVrvOBmoOC/rEcN7OQxKVeJTCiAdUzUJhA2Oez9QTkp72OTVcxDcXY8iKNkxGAJXmJCOQwOa6dhyXsOa6XwEGAKdeb5ET3rQdAAAAAElFTkSuQmCC)}.xdsoft_datetimepicker .xdsoft_label i{opacity:.5;background-position:-92px -19px;display:inline-block;width:9px;height:20px;vertical-align:middle}.xdsoft_datetimepicker .xdsoft_prev{float:left;background-position:-20px 0}.xdsoft_datetimepicker .xdsoft_today_button{float:left;background-position:-70px 0;margin-left:5px}.xdsoft_datetimepicker .xdsoft_next{float:right;background-position:0 0}.xdsoft_datetimepicker .xdsoft_next,.xdsoft_datetimepicker .xdsoft_prev,.xdsoft_datetimepicker .xdsoft_today_button{background-color:transparent;background-repeat:no-repeat;border:0 none;cursor:pointer;display:block;height:30px;opacity:.5;-ms-filter:"alpha(opacity=50)";outline:medium none;overflow:hidden;padding:0;position:relative;text-indent:100%;white-space:nowrap;width:20px;min-width:0}.xdsoft_datetimepicker .xdsoft_timepicker .xdsoft_prev,.xdsoft_datetimepicker .xdsoft_timepicker .xdsoft_next{float:none;background-position:-40px -15px;height:15px;width:30px;display:block;margin-left:14px;margin-top:7px}.xdsoft_datetimepicker.xdsoft_rtl .xdsoft_timepicker .xdsoft_prev,.xdsoft_datetimepicker.xdsoft_rtl .xdsoft_timepicker .xdsoft_next{float:none;margin-left:0;margin-right:14px}.xdsoft_datetimepicker .xdsoft_timepicker .xdsoft_prev{background-position:-40px 0;margin-bottom:7px;margin-top:0}.xdsoft_datetimepicker .xdsoft_timepicker .xdsoft_time_box{height:151px;overflow:hidden;border-bottom:1px solid #ddd}.xdsoft_datetimepicker .xdsoft_timepicker .xdsoft_time_box>div>div{background:#f5f5f5;border-top:1px solid #ddd;color:#666;font-size:12px;text-align:center;border-collapse:collapse;cursor:pointer;border-bottom-width:0;height:25px;line-height:25px}.xdsoft_datetimepicker .xdsoft_timepicker .xdsoft_time_box>div>div:first-child{border-top-width:0}.xdsoft_datetimepicker .xdsoft_today_button:hover,.xdsoft_datetimepicker .xdsoft_next:hover,.xdsoft_datetimepicker .xdsoft_prev:hover{opacity:1;-ms-filter:"alpha(opacity=100)"}.xdsoft_datetimepicker .xdsoft_label{display:inline;position:relative;z-index:9999;margin:0;padding:5px 3px;font-size:14px;line-height:20px;font-weight:bold;background-color:#fff;float:left;width:182px;text-align:center;cursor:pointer}.xdsoft_datetimepicker .xdsoft_label:hover>span{text-decoration:underline}.xdsoft_datetimepicker .xdsoft_label:hover i{opacity:1.0}.xdsoft_datetimepicker .xdsoft_label>.xdsoft_select{border:1px solid #ccc;position:absolute;right:0;top:30px;z-index:101;display:none;background:#fff;max-height:160px;overflow-y:hidden}.xdsoft_datetimepicker .xdsoft_label>.xdsoft_select.xdsoft_monthselect{right:-7px}.xdsoft_datetimepicker .xdsoft_label>.xdsoft_select.xdsoft_yearselect{right:2px}.xdsoft_datetimepicker .xdsoft_label>.xdsoft_select>div>.xdsoft_option:hover{color:#fff;background:#ff8000}.xdsoft_datetimepicker .xdsoft_label>.xdsoft_select>div>.xdsoft_option{padding:2px 10px 2px 5px;text-decoration:none !important}.xdsoft_datetimepicker .xdsoft_label>.xdsoft_select>div>.xdsoft_option.xdsoft_current{background:#3af;box-shadow:#178fe5 0 1px 3px 0 inset;color:#fff;font-weight:700}.xdsoft_datetimepicker .xdsoft_month{width:100px;text-align:right}.xdsoft_datetimepicker .xdsoft_calendar{clear:both}.xdsoft_datetimepicker .xdsoft_year{width:48px;margin-left:5px}.xdsoft_datetimepicker .xdsoft_calendar table{border-collapse:collapse;width:100%}.xdsoft_datetimepicker .xdsoft_calendar td>div{padding-right:5px}.xdsoft_datetimepicker .xdsoft_calendar th{height:25px}.xdsoft_datetimepicker .xdsoft_calendar td,.xdsoft_datetimepicker .xdsoft_calendar th{width:14.2857142%;background:#f5f5f5;border:1px solid #ddd;color:#666;font-size:12px;text-align:right;vertical-align:middle;padding:0;border-collapse:collapse;cursor:pointer;height:25px}.xdsoft_datetimepicker.xdsoft_showweeks .xdsoft_calendar td,.xdsoft_datetimepicker.xdsoft_showweeks .xdsoft_calendar th{width:12.5%}.xdsoft_datetimepicker .xdsoft_calendar th{background:#f1f1f1}.xdsoft_datetimepicker .xdsoft_calendar td.xdsoft_today{color:#3af}.xdsoft_datetimepicker .xdsoft_calendar td.xdsoft_highlighted_default{background:#ffe9d2;box-shadow:#ffb871 0 1px 4px 0 inset;color:#000}.xdsoft_datetimepicker .xdsoft_calendar td.xdsoft_highlighted_mint{background:#c1ffc9;box-shadow:#00dd1c 0 1px 4px 0 inset;color:#000}.xdsoft_datetimepicker .xdsoft_calendar td.xdsoft_default,.xdsoft_datetimepicker .xdsoft_calendar td.xdsoft_current,.xdsoft_datetimepicker .xdsoft_timepicker .xdsoft_time_box>div>div.xdsoft_current{background:#3af;box-shadow:#178fe5 0 1px 3px 0 inset;color:#fff;font-weight:700}.xdsoft_datetimepicker .xdsoft_calendar td.xdsoft_other_month,.xdsoft_datetimepicker .xdsoft_calendar td.xdsoft_disabled,.xdsoft_datetimepicker .xdsoft_time_box>div>div.xdsoft_disabled{opacity:.5;-ms-filter:"alpha(opacity=50)";cursor:default}.xdsoft_datetimepicker .xdsoft_calendar td.xdsoft_other_month.xdsoft_disabled{opacity:.2;-ms-filter:"alpha(opacity=20)"}.xdsoft_datetimepicker .xdsoft_calendar td:hover,.xdsoft_datetimepicker .xdsoft_timepicker .xdsoft_time_box>div>div:hover{color:#fff !important;background:#ff8000 !important;box-shadow:none !important}.xdsoft_datetimepicker .xdsoft_calendar td.xdsoft_current.xdsoft_disabled:hover,.xdsoft_datetimepicker .xdsoft_timepicker .xdsoft_time_box>div>div.xdsoft_current.xdsoft_disabled:hover{background:#3af !important;box-shadow:#178fe5 0 1px 3px 0 inset !important;color:#fff !important}.xdsoft_datetimepicker .xdsoft_calendar td.xdsoft_disabled:hover,.xdsoft_datetimepicker .xdsoft_timepicker .xdsoft_time_box>div>div.xdsoft_disabled:hover{color:inherit !important;background:inherit !important;box-shadow:inherit !important}.xdsoft_datetimepicker .xdsoft_calendar th{font-weight:700;text-align:center;color:#999;cursor:default}.xdsoft_datetimepicker .xdsoft_copyright{color:#ccc !important;font-size:10px;clear:both;float:none;margin-left:8px}.xdsoft_datetimepicker .xdsoft_copyright a{color:#eee !important}.xdsoft_datetimepicker .xdsoft_copyright a:hover{color:#aaa !important}.xdsoft_time_box{position:relative;border:1px solid #ccc}.xdsoft_scrollbar>.xdsoft_scroller{background:#ccc !important;height:20px;border-radius:3px}.xdsoft_scrollbar{position:absolute;width:7px;right:0;top:0;bottom:0;cursor:pointer}.xdsoft_datetimepicker.xdsoft_rtl .xdsoft_scrollbar{left:0;right:auto}.xdsoft_scroller_box{position:relative}.xdsoft_datetimepicker.xdsoft_dark{box-shadow:0 5px 15px -5px rgba(255,255,255,0.506);background:#000;border-bottom:1px solid #444;border-left:1px solid #333;border-right:1px solid #333;border-top:1px solid #333;color:#ccc}.xdsoft_datetimepicker.xdsoft_dark .xdsoft_timepicker .xdsoft_time_box{border-bottom:1px solid #222}.xdsoft_datetimepicker.xdsoft_dark .xdsoft_timepicker .xdsoft_time_box>div>div{background:#0a0a0a;border-top:1px solid #222;color:#999}.xdsoft_datetimepicker.xdsoft_dark .xdsoft_label{background-color:#000}.xdsoft_datetimepicker.xdsoft_dark .xdsoft_label>.xdsoft_select{border:1px solid #333;background:#000}.xdsoft_datetimepicker.xdsoft_dark .xdsoft_label>.xdsoft_select>div>.xdsoft_option:hover{color:#000;background:#007fff}.xdsoft_datetimepicker.xdsoft_dark .xdsoft_label>.xdsoft_select>div>.xdsoft_option.xdsoft_current{background:#c50;box-shadow:#b03e00 0 1px 3px 0 inset;color:#000}.xdsoft_datetimepicker.xdsoft_dark .xdsoft_label i,.xdsoft_datetimepicker.xdsoft_dark .xdsoft_prev,.xdsoft_datetimepicker.xdsoft_dark .xdsoft_next,.xdsoft_datetimepicker.xdsoft_dark .xdsoft_today_button{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGQAAAAeCAYAAADaW7vzAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyJpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNiAoV2luZG93cykiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6QUExQUUzOTA0M0UyMTFFNDlBM0FFQTJENTExRDVBODYiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6QUExQUUzOTE0M0UyMTFFNDlBM0FFQTJENTExRDVBODYiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDpBQTFBRTM4RTQzRTIxMUU0OUEzQUVBMkQ1MTFENUE4NiIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDpBQTFBRTM4RjQzRTIxMUU0OUEzQUVBMkQ1MTFENUE4NiIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/Pp0VxGEAAAIASURBVHja7JrNSgMxEMebtgh+3MSLr1T1Xn2CHoSKB08+QmR8Bx9A8e7RixdB9CKCoNdexIugxFlJa7rNZneTbLIpM/CnNLsdMvNjM8l0mRCiQ9Ye61IKCAgZAUnH+mU3MMZaHYChBnJUDzWOFZdVfc5+ZFLbrWDeXPwbxIqrLLfaeS0hEBVGIRQCEiZoHQwtlGSByCCdYBl8g8egTTAWoKQMRBRBcZxYlhzhKegqMOageErsCHVkk3hXIFooDgHB1KkHIHVgzKB4ADJQ/A1jAFmAYhkQqA5TOBtocrKrgXwQA8gcFIuAIO8sQSA7hidvPwaQGZSaAYHOUWJABhWWw2EMIH9QagQERU4SArJXo0ZZL18uvaxejXt/Em8xjVBXmvFr1KVm/AJ10tRe2XnraNqaJvKE3KHuUbfK1E+VHB0q40/y3sdQSxY4FHWeKJCunP8UyDdqJZenT3ntVV5jIYCAh20vT7ioP8tpf6E2lfEMwERe+whV1MHjwZB7PBiCxcGQWwKZKD62lfGNnP/1poFAA60T7rF1UgcKd2id3KDeUS+oLWV8DfWAepOfq00CgQabi9zjcgJVYVD7PVzQUAUGAQkbNJTBICDhgwYTjDYD6XeW08ZKh+A4pYkzenOxXUbvZcWz7E8ykRMnIHGX1XPl+1m2vPYpL+2qdb8CDAARlKFEz/ZVkAAAAABJRU5ErkJggg==)}.xdsoft_datetimepicker.xdsoft_dark .xdsoft_calendar td,.xdsoft_datetimepicker.xdsoft_dark .xdsoft_calendar th{background:#0a0a0a;border:1px solid #222;color:#999}.xdsoft_datetimepicker.xdsoft_dark .xdsoft_calendar th{background:#0e0e0e}.xdsoft_datetimepicker.xdsoft_dark .xdsoft_calendar td.xdsoft_today{color:#c50}.xdsoft_datetimepicker.xdsoft_dark .xdsoft_calendar td.xdsoft_highlighted_default{background:#ffe9d2;box-shadow:#ffb871 0 1px 4px 0 inset;color:#000}.xdsoft_datetimepicker.xdsoft_dark .xdsoft_calendar td.xdsoft_highlighted_mint{background:#c1ffc9;box-shadow:#00dd1c 0 1px 4px 0 inset;color:#000}.xdsoft_datetimepicker.xdsoft_dark .xdsoft_calendar td.xdsoft_default,.xdsoft_datetimepicker.xdsoft_dark .xdsoft_calendar td.xdsoft_current,.xdsoft_datetimepicker.xdsoft_dark .xdsoft_timepicker .xdsoft_time_box>div>div.xdsoft_current{background:#c50;box-shadow:#b03e00 0 1px 3px 0 inset;color:#000}.xdsoft_datetimepicker.xdsoft_dark .xdsoft_calendar td:hover,.xdsoft_datetimepicker.xdsoft_dark .xdsoft_timepicker .xdsoft_time_box>div>div:hover{color:#000 !important;background:#007fff !important}.xdsoft_datetimepicker.xdsoft_dark .xdsoft_calendar th{color:#666}.xdsoft_datetimepicker.xdsoft_dark .xdsoft_copyright{color:#333 !important}.xdsoft_datetimepicker.xdsoft_dark .xdsoft_copyright a{color:#111 !important}.xdsoft_datetimepicker.xdsoft_dark .xdsoft_copyright a:hover{color:#555 !important}.xdsoft_dark .xdsoft_time_box{border:1px solid #333}.xdsoft_dark .xdsoft_scrollbar>.xdsoft_scroller{background:#333 !important}.xdsoft_datetimepicker .xdsoft_save_selected{display:block;border:1px solid #ddd !important;margin-top:5px;width:100%;color:#454551;font-size:13px}.xdsoft_datetimepicker .blue-gradient-button{font-family:"museo-sans","Book Antiqua",sans-serif;font-size:12px;font-weight:300;color:#82878c;height:28px;position:relative;padding:4px 17px 4px 33px;border:1px solid #d7d8da;background:-moz-linear-gradient(top,#fff 0,#f4f8fa 73%);background:-webkit-gradient(linear,left top,left bottom,color-stop(0,#fff),color-stop(73%,#f4f8fa));background:-webkit-linear-gradient(top,#fff 0,#f4f8fa 73%);background:-o-linear-gradient(top,#fff 0,#f4f8fa 73%);background:-ms-linear-gradient(top,#fff 0,#f4f8fa 73%);background:linear-gradient(to bottom,#fff 0,#f4f8fa 73%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff',endColorstr='#f4f8fa',GradientType=0)}.xdsoft_datetimepicker .blue-gradient-button:hover,.xdsoft_datetimepicker .blue-gradient-button:focus,.xdsoft_datetimepicker .blue-gradient-button:hover span,.xdsoft_datetimepicker .blue-gradient-button:focus span{color:#454551;background:-moz-linear-gradient(top,#f4f8fa 0,#FFF 73%);background:-webkit-gradient(linear,left top,left bottom,color-stop(0,#f4f8fa),color-stop(73%,#FFF));background:-webkit-linear-gradient(top,#f4f8fa 0,#FFF 73%);background:-o-linear-gradient(top,#f4f8fa 0,#FFF 73%);background:-ms-linear-gradient(top,#f4f8fa 0,#FFF 73%);background:linear-gradient(to bottom,#f4f8fa 0,#FFF 73%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#f4f8fa',endColorstr='#FFF',GradientType=0)}
admin/assets/css/quick-dashboard.css ADDED
@@ -0,0 +1,266 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ .wt-scanner-container
2
+ {
3
+ margin: 15px 15px 15px 15px;
4
+ }
5
+ .wt-dashboard-container
6
+ {
7
+ margin: 15px 10px 15px 10px;
8
+ }
9
+ /*----------------*/
10
+ #wt-quickstart-scan
11
+ {
12
+ width: 100%;
13
+ }
14
+ .wt-scanner-block-scan
15
+ {
16
+ text-align: center;
17
+ }
18
+ .wt-scanner-block-scan table {
19
+ width: 100%;
20
+ }
21
+ .wt-scanner-block-scan table td:first-child {
22
+ width: 20%;
23
+ }
24
+ .wt-scanner-block-scan table td {
25
+ border: 1px solid #d6d6d6;
26
+ background: #fff;
27
+ text-align: center;
28
+ padding: 20px;
29
+ border-radius: 10px;
30
+ }
31
+ .wt-scanner-block-scan table td h4 {
32
+ font-family: Roboto, "Helvetica Neue", Helvetica, Arial, sans-serif;
33
+ font-size: 16px;
34
+ }
35
+ .wt-scan-icon-loader
36
+ {
37
+ height: 55px;
38
+ background: transparent url("../img/loader.gif");
39
+ background-repeat: no-repeat;
40
+ background-position: center center;
41
+ background-size: 60px;
42
+ }
43
+
44
+ #wt-dashboard-section table.wt-dashboard-table {
45
+ width: 100%;
46
+ table-layout: fixed;
47
+ border-spacing: 5px 0;
48
+ border-collapse: separate;
49
+ margin-bottom: 5px;
50
+ }
51
+ #wt-dashboard-section table.wt-dashboard-table > thead > tr > td {
52
+ border: 1px solid #d6d6d6;
53
+ border-bottom: 0;
54
+ background: #efefef;
55
+ text-align: left;
56
+ padding: 10px;
57
+ border-radius: 10px 10px 0 0;
58
+ }
59
+ #wt-dashboard-section table.wt-dashboard-table > thead > tr > td > h4 {
60
+ font-family: Roboto, "Helvetica Neue", Helvetica, Arial, sans-serif;
61
+ font-size: 14px;
62
+ font-weight: 700;
63
+ margin: 5px;
64
+ color: #868686;
65
+ text-transform: uppercase;
66
+ }
67
+ #wt-dashboard-section table.wt-dashboard-table > tbody > tr > td {
68
+ border: 1px solid #d6d6d6;
69
+ border-top: 0;
70
+ background: #fff;
71
+ text-align: center;
72
+ padding: 15px;
73
+ border-radius: 0 0 10px 10px;
74
+ }
75
+ #wt-dashboard-section table.wt-dashboard-table > tbody > tr > td > h4 {
76
+ font-family: Roboto, "Helvetica Neue", Helvetica, Arial, sans-serif;
77
+ font-size: 14px;
78
+ }
79
+ #wt-dashboard-section table.wt-dashboard-table > tbody > tr > td h4 {
80
+ font-family: Roboto, "Helvetica Neue", Helvetica, Arial, sans-serif;
81
+ font-size: 14px;
82
+ font-weight: bold;
83
+ }
84
+ .wt-full-block
85
+ {
86
+ width: 100%;
87
+ display: block;
88
+ height: 50%;
89
+ }
90
+ .wt-left-block, .wt-right-block
91
+ {
92
+ width: 50%;
93
+ display: inline-block;
94
+
95
+ }
96
+ div.wt-block-span-count
97
+ {
98
+ width: 100%;
99
+ font-family: Roboto, "Helvetica Neue", Helvetica, Arial, sans-serif;
100
+ font-size: 12px;
101
+ font-weight: bold;
102
+ }
103
+ .wt-block-span-count div
104
+ {
105
+ padding: 1px 4px;
106
+ border: 1px solid #b1b1b1;
107
+ display: inline-block;
108
+ margin: 1px;
109
+ border-radius: 5px;
110
+ }
111
+ .wt-left-block
112
+ {
113
+ float: left;
114
+ }
115
+ .wt-right-block
116
+ {
117
+ float: right;
118
+ }
119
+
120
+ .wtitan-status-circular
121
+ {
122
+ /*height: 100px !important;*/
123
+ width: auto !important;
124
+ }
125
+ .wt-caption-block
126
+ {
127
+ width: auto;
128
+ float: left;
129
+ display: inline-block;
130
+ }
131
+ .wt-manage-link-block
132
+ {
133
+ width: auto;
134
+ float: right;
135
+ display: inline-block;
136
+ padding: 4px;
137
+ }
138
+
139
+ .wt-manage-link-block a
140
+ {
141
+ color: #5d05b7;
142
+ font-weight: bold;
143
+ text-decoration: underline;
144
+ }
145
+ table.wt-sitechecker-block-table
146
+ {
147
+ width: 100%;
148
+ border: 0;
149
+ table-layout: fixed;
150
+ }
151
+ table.wt-sitechecker-block-table tbody td
152
+ {
153
+ border: 0 !important;
154
+ font-size: 40px;
155
+ color: #5d05b7;
156
+ font-weight: bold;
157
+ padding: 15px 0;
158
+ text-align: center;
159
+ }
160
+ table.wt-sitechecker-block-table tbody td span
161
+ {
162
+ font-size: 20px;
163
+ }
164
+
165
+ table.wt-sitechecker-block-table thead td
166
+ {
167
+ border: 0 !important;
168
+ }
169
+ table.wt-sitechecker-block-table thead td h4
170
+ {
171
+ font-family: Roboto, "Helvetica Neue", Helvetica, Arial, sans-serif;
172
+ font-size: 14px;
173
+ font-weight: 700;
174
+ margin: 5px;
175
+ color: #868686;
176
+ text-transform: uppercase;
177
+ }
178
+ .wt-push-status
179
+ {
180
+ font-size: inherit !important;
181
+ }
182
+ .wtitan-status-circular-text {
183
+ left: 110px;
184
+ position: relative;
185
+ bottom: 53px;
186
+ padding: 0;
187
+ margin: 0;
188
+ transform: translate(-50%, -50%);
189
+ color: #aaa;
190
+ font-family: Roboto, "Helvetica Neue", Helvetica, Arial, sans-serif;
191
+ font-size: 1.3125rem;
192
+ font-weight: 300;
193
+ line-height: 1.5;
194
+ }
195
+
196
+ /*--- Scanner -------*/
197
+ #WBCR .wio-chart-container canvas {
198
+ display: inline-block !important;
199
+ }
200
+ #WBCR .wio-chart-container {
201
+ position: relative;
202
+ display: inline-block;
203
+ margin-right: 5px;
204
+ }
205
+ #WBCR .wio-chart-percent {
206
+ position: absolute;
207
+ left: 3px;
208
+ right: 0;
209
+ top: 35%;
210
+ margin-top: -0.5em;
211
+ line-height: 0.8;
212
+ text-align: center;
213
+ font-size: 32px;
214
+ font-weight: bold;
215
+ color: #afafaf;
216
+ }
217
+ #WBCR .wio-chart-percent span {
218
+ font-size: 15px;
219
+ vertical-align: super;
220
+ }
221
+ #WBCR .wio-page-statistic .wio-chart-percent {
222
+ margin-top: -1.1em;
223
+ }
224
+ #WBCR .wio-widget .wio-chart-percent {
225
+ font-size: 44px;
226
+ line-height: 1;
227
+ }
228
+ #WBCR .wio-doughnut-legend li {
229
+ display: table;
230
+ position: relative;
231
+ border-radius: 5px;
232
+ padding: 3px 8px 2px 31px;
233
+ font-size: 12px;
234
+ cursor: default;
235
+ -webkit-transition: background-color 200ms ease-in-out;
236
+ -moz-transition: background-color 200ms ease-in-out;
237
+ -o-transition: background-color 200ms ease-in-out;
238
+ transition: background-color 200ms ease-in-out;
239
+ }
240
+ #WBCR .wio-doughnut-legend li span {
241
+ display: inline-block;
242
+ width: 30px;
243
+ height: 30px;
244
+ border-radius: 50%;
245
+ padding-top: 6px;
246
+ }
247
+ /*-------------------*/
248
+ .wt-pink
249
+ {
250
+ color: #f1b1b6 !important;
251
+ }
252
+ .wt-firewall-icon-ok, .wt-firewall-icon-clock, .wt-firewall-icon-dissmiss
253
+ {
254
+ width: 97px;
255
+ height: 97px;
256
+ display: inline-block;
257
+ }
258
+ .wt-firewall-icon-ok{
259
+ background: url('../img/check.png');
260
+ }
261
+ .wt-firewall-icon-clock{
262
+ background: url('../img/check.png');
263
+ }
264
+ .wt-firewall-icon-dissmiss{
265
+ background: url('../img/check.png');
266
+ }
admin/assets/css/settings.css.map DELETED
@@ -1 +0,0 @@
1
- {"version":3,"sources":["settings.less"],"names":[],"mappings":"AAEI,KADF,kBACG,iCAAiC;EAChC,qBAAA;EACA,kBAAA;EACA,SAAS,KAAT;EACA,mBAAA;EACA,kBAAA;EACA,WAAA;EACA,eAAA;EACA,cAAA;EACA,kBAAA;EACA,gBAAA;EACA,gBAAA;EACA,mBAAA;EACA,SAAA;EACA,WAAA;EACA,WAAA;EACA,WAAA;EACA,eAAA;;AAGF,KArBF,kBAqBG,UAAW;AACZ,KAtBF,kBAsBG,UAAW;EACV,oBAAA;EACA,mBAAA;EACA,aAAA;EACA,yBAAA;EACA,wBAAA;EACA,gBAAA;;AA7BN,KAiCE;EACE,kBAAA;EACA,aAAA;EACA,mBAAA;EACA,cAAA;EACA,kBAAA;;AAEA,KAPF,oCAOG;AAAQ,KAPX,oCAOY;EACR,YAAA;EACA,UAAA;EACA,yBAAA;EACA,SAAS,GAAT;EACA,SAAA;EACA,QAAA;EACA,kBAAA;EACA,oBAAA;;AAGF,KAlBF,oCAkBG;EACC,oCAAA;EACA,4BAAA;EACA,kBAAA;;AAGF,KAxBF,oCAwBG;EACC,oCAAA;EACA,4BAAA;EACA,kBAAA","file":"settings.css"}
 
admin/assets/css/sweetalert-custom.css CHANGED
@@ -1,79 +1,153 @@
1
- /* Sub Layerr */
2
- body[class*="_license-wantispam"] .swal2-container.swal2-shown {
3
- background: rgba(16, 17, 21, 0.9);
4
- z-index: 100000;
5
- }
6
- .wantispam-modal {
7
- padding: 0 !important;
8
- }
9
- .wantispam-modal .swal2-close {
10
- color: rgba(0, 0, 0, 0.8);
11
- }
12
- .wantispam-modal .swal2-modal {
13
- border-radius: 2px;
14
- }
15
- .wantispam-modal .swal2-icon {
16
- margin-bottom: 25px;
17
- }
18
- .wantispam-modal .swal2-title {
19
- margin: 0;
20
- padding: 15px 32px;
21
- font-size: 20px;
22
- text-align: left;
23
- color: #fff !important;
24
- background: #3e3e3e !important;
25
- }
26
- .wantispam-modal .swal2-content {
27
- font-size: 16px;
28
- padding: 0;
29
- background: #efefef;
30
- }
31
- .wantispam-modal .swal2-actions {
32
- margin-top: 0;
33
- padding: 10px;
34
- background: #F4F7F9;
35
- }
36
- .wantispam-modal .swal2-actions a.button svg {
37
- margin-right: 12px;
38
- vertical-align: -2px;
39
- }
40
- .wantispam-modal .swal2-actions button.loading {
41
- border-radius: 100% !important;
42
- height: 40px !important;
43
- padding: 0 !important;
44
- box-shadow: none !important;
45
- }
46
- .wantispam-modal .swal2-actions button.swal2-styled {
47
- height: auto;
48
- padding: 12px 32px;
49
- margin: 10px;
50
- font-size: 14px;
51
- letter-spacing: 1px;
52
- text-transform: uppercase;
53
- border-radius: 3px;
54
- font-weight: bold;
55
- outline: none;
56
- }
57
- .wantispam-modal .swal2-actions button.swal2-styled.swal2-confirm {
58
- background-color: #fdd599 !important;
59
- text-shadow: none !important;
60
- box-shadow: 0 3px 0 #ceac7a !important;
61
- color: #a57b3c !important;
62
- }
63
- .wantispam-modal .swal2-actions button.swal2-styled.swal2-cancel {
64
- background-color: #d2d2d2 !important;
65
- color: #656464 !important;
66
- text-shadow: none !important;
67
- box-shadow: 0 3px 0 #a9a9a9;
68
- /*background-color: #c9deb2 !important;
69
- color: #606956 !important;
70
- text-shadow: none !important;
71
- box-shadow: 0 3px 0 #a7b994;*/
72
- }
73
- .wantispam-modal .swal2-actions button.swal2-styled:focus,
74
- .wantispam-modal .swal2-actions button.swal2-styled:hover {
75
- outline: none;
76
- text-shadow: none;
77
- color: #FFF;
78
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
79
  /*# sourceMappingURL=sweetalert-custom.css.map */
1
+ /* Sub Layerr */
2
+ body[class*="_license-wtitan"] .swal2-container.swal2-shown {
3
+ background: rgba(16, 17, 21, 0.9);
4
+ z-index: 100000;
5
+ }
6
+ .wtitan-modal {
7
+ padding: 0 !important;
8
+ }
9
+ .wtitan-modal .swal2-close {
10
+ color: rgba(0, 0, 0, 0.8);
11
+ }
12
+ .wtitan-modal .swal2-modal {
13
+ border-radius: 2px;
14
+ }
15
+ .wtitan-modal .swal2-icon {
16
+ margin-bottom: 25px;
17
+ }
18
+ .wtitan-modal .swal2-title,
19
+ .wtitan-modal .wtitan-modal__title {
20
+ margin: 0;
21
+ padding: 15px 32px;
22
+ font-size: 20px;
23
+ text-align: left;
24
+ color: #fff !important;
25
+ background: #3e3e3e !important;
26
+ }
27
+ .wtitan-modal .swal2-content,
28
+ .wtitan-modal .wtitan-modal__content {
29
+ font-size: 16px;
30
+ padding: 0;
31
+ background: #efefef;
32
+ }
33
+ .wtitan-modal .swal2-actions {
34
+ margin-top: 0;
35
+ padding: 10px;
36
+ background: #F4F7F9;
37
+ }
38
+ .wtitan-modal .swal2-actions a.button svg {
39
+ margin-right: 12px;
40
+ vertical-align: -2px;
41
+ }
42
+ .wtitan-modal .swal2-actions button.loading {
43
+ border-radius: 100% !important;
44
+ height: 40px !important;
45
+ padding: 0 !important;
46
+ box-shadow: none !important;
47
+ }
48
+ .wtitan-modal .swal2-actions button.swal2-styled {
49
+ height: auto;
50
+ padding: 12px 32px;
51
+ margin: 10px;
52
+ font-size: 14px;
53
+ letter-spacing: 1px;
54
+ text-transform: uppercase;
55
+ border-radius: 3px;
56
+ font-weight: bold;
57
+ outline: none;
58
+ }
59
+ .wtitan-modal .swal2-actions button.swal2-styled.swal2-confirm {
60
+ background-color: #fdd599 !important;
61
+ text-shadow: none !important;
62
+ box-shadow: 0 3px 0 #ceac7a !important;
63
+ color: #a57b3c !important;
64
+ }
65
+ .wtitan-modal .swal2-actions button.swal2-styled.swal2-cancel {
66
+ background-color: #d2d2d2 !important;
67
+ color: #656464 !important;
68
+ text-shadow: none !important;
69
+ box-shadow: 0 3px 0 #a9a9a9;
70
+ /*background-color: #c9deb2 !important;
71
+ color: #606956 !important;
72
+ text-shadow: none !important;
73
+ box-shadow: 0 3px 0 #a7b994;*/
74
+ }
75
+ .wtitan-modal .swal2-actions button.swal2-styled:focus,
76
+ .wtitan-modal .swal2-actions button.swal2-styled:hover {
77
+ outline: none;
78
+ text-shadow: none;
79
+ color: #FFF;
80
+ }
81
+ .wtitan-modal__content {
82
+ padding: 20px !important;
83
+ }
84
+ .wtitan-modal__content,
85
+ .wtitan-modal__content p {
86
+ text-align: left;
87
+ font-size: 13px;
88
+ line-height: 1.5;
89
+ }
90
+ .wtitan-modal__content p {
91
+ margin: 1em 0;
92
+ }
93
+ .wtitan-modal__content .wtitan-notice {
94
+ margin: 12px 0;
95
+ padding: 8px;
96
+ background-color: #ffffe0;
97
+ border: 1px solid #ffd975;
98
+ border-width: 1px 1px 1px 10px;
99
+ font-size: 13px;
100
+ }
101
+ .wtitan-modal__content .wtitan-pre {
102
+ margin: 8px 0 20px;
103
+ padding: 12px;
104
+ background: #ffffff;
105
+ border: 1px solid #999999;
106
+ font-size: 13px;
107
+ overflow: auto;
108
+ }
109
+ .wtitan-modal__content .wtitan-switch {
110
+ display: -webkit-flex !important;
111
+ display: flex !important;
112
+ -webkit-align-items: stretch !important;
113
+ align-items: stretch !important;
114
+ -webkit-justify-content: flex-start !important;
115
+ justify-content: flex-start !important;
116
+ -webkit-flex-direction: row !important;
117
+ flex-direction: row !important;
118
+ margin: 0;
119
+ padding: 0;
120
+ }
121
+ .wtitan-modal__content .wtitan-switch > li {
122
+ margin: 0 !important;
123
+ padding: 0.5rem 0.7rem !important;
124
+ text-transform: uppercase;
125
+ cursor: pointer;
126
+ color: #aaa;
127
+ font-weight: 400;
128
+ border-top: 1px solid #bfbfbf;
129
+ border-bottom: 1px solid #bfbfbf;
130
+ border-right: 1px solid #bfbfbf;
131
+ }
132
+ .wtitan-modal__content .wtitan-switch > li:first-of-type {
133
+ border-left: 1px solid #bfbfbf;
134
+ -moz-border-radius-topleft: 6px;
135
+ -webkit-border-top-left-radius: 6px;
136
+ border-top-left-radius: 6px;
137
+ -moz-border-radius-bottomleft: 6px;
138
+ -webkit-border-bottom-left-radius: 6px;
139
+ border-bottom-left-radius: 6px;
140
+ }
141
+ .wtitan-modal__content .wtitan-switch > li:last-of-type {
142
+ -moz-border-radius-topright: 6px;
143
+ -webkit-border-top-right-radius: 6px;
144
+ border-top-right-radius: 6px;
145
+ -moz-border-radius-bottomright: 6px;
146
+ -webkit-border-bottom-right-radius: 6px;
147
+ border-bottom-right-radius: 6px;
148
+ }
149
+ .wtitan-modal__content .wtitan-switch > li.wtitan-active {
150
+ color: #ffffff;
151
+ background-color: #00709e;
152
+ }
153
  /*# sourceMappingURL=sweetalert-custom.css.map */
admin/assets/css/sweetalert-custom.css.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["sweetalert-custom.less"],"names":[],"mappings":";AACA,IAAI,6BAA8B,iBAAgB;EAChD,iCAAA;EACA,eAAA;;AAGF;EACE,qBAAA;;AADF,gBAGE;EACE,yBAAA;;AAJJ,gBAOE;EACE,kBAAA;;AARJ,gBAWE;EACE,mBAAA;;AAZJ,gBAeE;EACE,SAAA;EACA,kBAAA;EACA,eAAA;EACA,gBAAA;EACA,WAAA;EACA,mBAAA;;AArBJ,gBAwBE;EACE,eAAA;EACA,UAAA;EACA,mBAAA;;AA3BJ,gBA8BE;EACE,aAAA;EACA,aAAA;EACA,mBAAA;;AAjCJ,gBA8BE,eAKE,EAAC,OAAQ;EACP,kBAAA;EACA,oBAAA;;AArCN,gBA8BE,eAUE,OAAM;EACJ,8BAAA;EACA,uBAAA;EACA,qBAAA;EACA,2BAAA;;AA5CN,gBA8BE,eAiBE,OAAM;EACJ,YAAA;EACA,kBAAA;EACA,YAAA;EACA,eAAA;EACA,mBAAA;EACA,yBAAA;EACA,kBAAA;EACA,iBAAA;EACA,aAAA;;AAEA,gBA5BJ,eAiBE,OAAM,aAWH;EACC,yBAAA;EACA,4BAAA;EACA,2BAAA;EACA,cAAA;;AAGF,gBAnCJ,eAiBE,OAAM,aAkBH;EACC,yBAAA;EACA,cAAA;EACA,4BAAA;EACA,2BAAA;;;;;;AAOF,gBA9CJ,eAiBE,OAAM,aA6BH;AAAQ,gBA9Cb,eAiBE,OAAM,aA6BM;EACR,aAAA;EACA,iBAAA;EACA,WAAA","file":"sweetalert-custom.css"}
1
+ {"version":3,"sources":["sweetalert-custom.less"],"names":[],"mappings":";AACA,IAAI,0BAA2B,iBAAgB;EAC7C,iCAAA;EACA,eAAA;;AAGF;EACE,qBAAA;;AADF,aAGE;EACE,yBAAA;;AAJJ,aAOE;EACE,kBAAA;;AARJ,aAWE;EACE,mBAAA;;AAZJ,aAeE;AAfF,aAegB;EACZ,SAAA;EACA,kBAAA;EACA,eAAA;EACA,gBAAA;EACA,WAAA;EACA,mBAAA;;AArBJ,aAwBE;AAxBF,aAwBkB;EACd,eAAA;EACA,UAAA;EACA,mBAAA;;AA3BJ,aA8BE;EACE,aAAA;EACA,aAAA;EACA,mBAAA;;AAjCJ,aA8BE,eAKE,EAAC,OAAQ;EACP,kBAAA;EACA,oBAAA;;AArCN,aA8BE,eAUE,OAAM;EACJ,8BAAA;EACA,uBAAA;EACA,qBAAA;EACA,2BAAA;;AA5CN,aA8BE,eAiBE,OAAM;EACJ,YAAA;EACA,kBAAA;EACA,YAAA;EACA,eAAA;EACA,mBAAA;EACA,yBAAA;EACA,kBAAA;EACA,iBAAA;EACA,aAAA;;AAEA,aA5BJ,eAiBE,OAAM,aAWH;EACC,yBAAA;EACA,4BAAA;EACA,2BAAA;EACA,cAAA;;AAGF,aAnCJ,eAiBE,OAAM,aAkBH;EACC,yBAAA;EACA,cAAA;EACA,4BAAA;EACA,2BAAA;;;;;;AAOF,aA9CJ,eAiBE,OAAM,aA6BH;AAAQ,aA9Cb,eAiBE,OAAM,aA6BM;EACR,aAAA;EACA,iBAAA;EACA,WAAA;;AAMR;EACE,wBAAA;;AAEA;AAHF,sBAGK;EACD,gBAAA;EACA,eAAA;EACA,gBAAA;;AANJ,sBASE;EACE,aAAA;;AAVJ,sBAaE;EACE,cAAA;EACA,YAAA;EACA,yBAAA;EACA,yBAAA;EACA,8BAAA;EACA,eAAA;;AAnBJ,sBAsBE;EACE,kBAAA;EACA,aAAA;EACA,mBAAA;EACA,yBAAA;EACA,eAAA;EACA,cAAA;;AA5BJ,sBA+BE;EACE,qBAAA;EACA,wBAAA;EACA,uCAAA;EACA,+BAAA;EACA,mCAAA;EACA,2BAAA;EACA,sCAAA;EACA,8BAAA;EACA,SAAA;EACA,UAAA;;AAzCJ,sBA4CE,eAAe;EACb,oBAAA;EACA,sBAAA;EACA,yBAAA;EACA,eAAA;EACA,WAAA;EACA,gBAAA;EACA,6BAAA;EACA,gCAAA;EACA,+BAAA;;AArDJ,sBAwDE,eAAe,KAAI;EACjB,8BAAA;EACA,+BAAA;EACA,mCAAA;EACA,2BAAA;EACA,kCAAA;EACA,sCAAA;EACA,8BAAA;;AA/DJ,sBAkEE,eAAe,KAAI;EACjB,gCAAA;EACA,oCAAA;EACA,4BAAA;EACA,mCAAA;EACA,uCAAA;EACA,+BAAA;;AAxEJ,sBA2EE,eAAe,KAAI;EACjB,cAAA;EACA,yBAAA","file":"sweetalert-custom.css"}
admin/assets/css/sweetalert-custom.less CHANGED
@@ -1,10 +1,10 @@
1
  /* Sub Layerr */
2
- body[class*="_license-wantispam"] .swal2-container.swal2-shown {
3
  background: rgba(16, 17, 21, 0.9);
4
  z-index: 100000;
5
  }
6
 
7
- .wantispam-modal {
8
  padding: 0 !important;
9
 
10
  .swal2-close {
@@ -19,7 +19,7 @@ body[class*="_license-wantispam"] .swal2-container.swal2-shown {
19
  margin-bottom: 25px;
20
  }
21
 
22
- .swal2-title {
23
  margin: 0;
24
  padding: 15px 32px;
25
  font-size: 20px;
@@ -28,7 +28,7 @@ body[class*="_license-wantispam"] .swal2-container.swal2-shown {
28
  background: #3e3e3e !important;
29
  }
30
 
31
- .swal2-content {
32
  font-size: 16px;
33
  padding: 0;
34
  background: #efefef;
@@ -87,4 +87,85 @@ body[class*="_license-wantispam"] .swal2-container.swal2-shown {
87
  }
88
  }
89
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
90
  }
1
  /* Sub Layerr */
2
+ body[class*="_license-wtitan"] .swal2-container.swal2-shown {
3
  background: rgba(16, 17, 21, 0.9);
4
  z-index: 100000;
5
  }
6
 
7
+ .wtitan-modal {
8
  padding: 0 !important;
9
 
10
  .swal2-close {
19
  margin-bottom: 25px;
20
  }
21
 
22
+ .swal2-title, .wtitan-modal__title {
23
  margin: 0;
24
  padding: 15px 32px;
25
  font-size: 20px;
28
  background: #3e3e3e !important;
29
  }
30
 
31
+ .swal2-content, .wtitan-modal__content {
32
  font-size: 16px;
33
  padding: 0;
34
  background: #efefef;
87
  }
88
  }
89
  }
90
+ }
91
+
92
+ .wtitan-modal__content {
93
+ padding: 20px !important;
94
+
95
+ &, p {
96
+ text-align: left;
97
+ font-size: 13px;
98
+ line-height: 1.5;
99
+ }
100
+
101
+ p {
102
+ margin: 1em 0;
103
+ }
104
+
105
+ .wtitan-notice {
106
+ margin: 12px 0;
107
+ padding: 8px;
108
+ background-color: #ffffe0;
109
+ border: 1px solid #ffd975;
110
+ border-width: 1px 1px 1px 10px;
111
+ font-size: 13px;
112
+ }
113
+
114
+ .wtitan-pre {
115
+ margin: 8px 0 20px;
116
+ padding: 12px;
117
+ background: #ffffff;
118
+ border: 1px solid #999999;
119
+ font-size: 13px;
120
+ overflow: auto;
121
+ }
122
+
123
+ .wtitan-switch {
124
+ display: -webkit-flex !important;
125
+ display: flex !important;
126
+ -webkit-align-items: stretch !important;
127
+ align-items: stretch !important;
128
+ -webkit-justify-content: flex-start !important;
129
+ justify-content: flex-start !important;
130
+ -webkit-flex-direction: row !important;
131
+ flex-direction: row !important;
132
+ margin: 0;
133
+ padding: 0
134
+ }
135
+
136
+ .wtitan-switch > li {
137
+ margin: 0 !important;
138
+ padding: 0.5rem 0.7rem !important;
139
+ text-transform: uppercase;
140
+ cursor: pointer;
141
+ color: #aaa;
142
+ font-weight: 400;
143
+ border-top: 1px solid #bfbfbf;
144
+ border-bottom: 1px solid #bfbfbf;
145
+ border-right: 1px solid #bfbfbf
146
+ }
147
+
148
+ .wtitan-switch > li:first-of-type {
149
+ border-left: 1px solid #bfbfbf;
150
+ -moz-border-radius-topleft: 6px;
151
+ -webkit-border-top-left-radius: 6px;
152
+ border-top-left-radius: 6px;
153
+ -moz-border-radius-bottomleft: 6px;
154
+ -webkit-border-bottom-left-radius: 6px;
155
+ border-bottom-left-radius: 6px
156
+ }
157
+
158
+ .wtitan-switch > li:last-of-type {
159
+ -moz-border-radius-topright: 6px;
160
+ -webkit-border-top-right-radius: 6px;
161
+ border-top-right-radius: 6px;
162
+ -moz-border-radius-bottomright: 6px;
163
+ -webkit-border-bottom-right-radius: 6px;
164
+ border-bottom-right-radius: 6px
165
+ }
166
+
167
+ .wtitan-switch > li.wtitan-active {
168
+ color: #ffffff;
169
+ background-color: #00709e
170
+ }
171
  }
admin/assets/css/titan-security.css ADDED
@@ -0,0 +1,213 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ h2.nav-tab-wrapper {
2
+ margin-left: 10px !important;
3
+ }
4
+
5
+ /*#WBCR .wbcr-factory-pages-426-impressive-page-template .alert
6
+ {
7
+ padding: 10px !important;
8
+ /*position: absolute !important;
9
+ width: 100%;
10
+
11
+ opacity: 0.8;
12
+ }*/
13
+ #WBCR .wbcr-factory-pages-426-impressive-page-template .wbcr-factory-page-inner-wrap .nav-tab {
14
+
15
+ /*opacity: 0.8;*/
16
+ }
17
+
18
+ #WBCR .wbcr-factory-pages-426-impressive-page-template .wbcr-factory-page-inner-wrap .nav-tab {
19
+ border-radius: 5px 5px 0 0;
20
+ }
21
+
22
+ #WBCR .wbcr-factory-pages-426-impressive-page-template .wbcr-factory-page-header {
23
+ background-color: #5d05b7 !important;
24
+ }
25
+
26
+ #WBCR .wbcr-factory-pages-426-impressive-page-template .wbcr-factory-left-navigation-bar ul .wbcr-factory-nav-tab a {
27
+ border-left: 5px solid #efefef !important;
28
+ }
29
+
30
+ #WBCR .wbcr-factory-pages-426-impressive-page-template .wbcr-factory-left-navigation-bar ul .wbcr-factory-nav-tab.wbcr-factory-active-tab a,
31
+ #WBCR .wbcr-factory-pages-426-impressive-page-template .wbcr-factory-left-navigation-bar ul .wbcr-factory-nav-tab a:hover {
32
+ color: #5d05b7 !important;
33
+ border-left: 5px solid #5d05b7 !important;
34
+ }
35
+
36
+ #WBCR .wbcr-factory-pages-426-impressive-page-template .wbcr-factory-left-navigation-bar ul .wbcr-factory-nav-tab.wbcr-factory-active-tab a .dashicons,
37
+ #WBCR .wbcr-factory-pages-426-impressive-page-template .wbcr-factory-left-navigation-bar ul .wbcr-factory-nav-tab a:hover .dashicons {
38
+ color: #5d05b7 !important;
39
+ }
40
+
41
+ #WBCR .wbcr-factory-pages-426-impressive-page-template .wbcr-factory-page-header .wbcr-factory-button.wbcr-factory-type-save {
42
+ background-color: #5d05b7 !important;
43
+ color: #ffffff !important;
44
+ display: block;
45
+ position: fixed !important;
46
+ right: 40px;
47
+ top: 50px;
48
+ border-radius: 15px;
49
+ border: 2px solid #ffffff;
50
+ }
51
+ #WBCR .wbcr-factory-pages-426-impressive-page-template .wbcr-factory-page-header .wbcr-factory-button.wbcr-factory-type-save:hover {
52
+ background-color: #ffffff !important;
53
+ color: #5d05b7 !important;
54
+ border-color: #5d05b7 !important;
55
+ }
56
+
57
+ #WBCR .wbcr-factory-pages-426-impressive-page-template input[type="checkbox"]:checked::before {
58
+ content: url(data:image/svg+xml;utf8,%3Csvg%20xmlns%3D%27http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%27%20viewBox%3D%270%200%2020%2020%27%3E%3Cpath%20d%3D%27M14.83%204.89l1.34.94-5.81%208.38H9.02L5.78%209.67l1.34-1.25%202.57%202.4z%27%20fill%3D%27%235d05b7%27%2F%3E%3C%2Fsvg%3E) !important;
59
+ }
60
+
61
+ /*--------------------------*/
62
+ .factory-bootstrap-427 select.form-control {
63
+ display: inline;
64
+ background: #fff url(data:image/svg+xml;charset=US-ASCII,%3Csvg%20width%3D%2220%22%20height%3D%2220%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%3Cpath%20d%3D%22M5%206l5%205%205-5%202%201-7%207-7-7%202-1z%22%20fill%3D%22%23555%22%2F%3E%3C%2Fsvg%3E) no-repeat right 5px top 55%;
65
+ background-size: 16px 16px !important;
66
+ }
67
+
68
+ .factory-bootstrap-427 .btn-default.active, .factory-bootstrap-427 .btn-default:active {
69
+ background: #b083de !important;
70
+ color: #ffffff !important;
71
+ }
72
+
73
+ .factory-bootstrap-427 .btn-primary {
74
+ background: #5d05b7 !important;
75
+ border-color: #27004e !important;
76
+ box-shadow: none !important;
77
+ -webkit-box-shadow: none !important;
78
+ }
79
+
80
+ .factory-bootstrap-427 .factory-buttons-way .factory-on.active {
81
+ background-color: #b083de !important;
82
+ -webkit-box-shadow: inset 0 1px 1px #8651bd !important;
83
+ box-shadow: inset 0 1px 3px #8651bd !important;
84
+ border-top: 1px solid #8651bd !important;
85
+ border-bottom: 1px solid #8651bd !important;
86
+ border-left: 1px solid #8651bd !important;
87
+ }
88
+
89
+ .factory-bootstrap-427 .factory-buttons-way .factory-off.active {
90
+ background-color: #b083de !important;
91
+ -webkit-box-shadow: inset 0 1px 1px #8651bd !important;
92
+ box-shadow: inset 0 1px 3px #8651bd !important;
93
+ border-top: 1px solid #8651bd !important;
94
+ border-bottom: 1px solid #8651bd !important;
95
+ border-right: 1px solid #8651bd !important;
96
+ color: #ffffff !important;
97
+
98
+ }
99
+
100
+ .wt-plugin-header-logo {
101
+ background: transparent url(../img/titan-icon.png) no-repeat left center !important;
102
+ background-size: 30px !important;
103
+ display: block;
104
+ float: left;
105
+ height: 30px;
106
+ width: 35px;
107
+ }
108
+
109
+ .wbcr-factory-header-logo {
110
+ font-weight: 700 !important;
111
+ text-shadow: 0px 0px 3px #000000;
112
+ color: #ffffff !important;
113
+ }
114
+
115
+ .wbcr-titan-export-import
116
+ {
117
+ padding: 0 50px;
118
+ text-align: right;
119
+ }
120
+ .wbcr-titan-export-import label
121
+ {
122
+ display: block;
123
+ width: 100%;
124
+ text-align: left;
125
+ padding: 7px 0;
126
+ }
127
+ .wbcr-titan-export-import #wbcr-titan-export-textarea
128
+ {
129
+ width: 100%;
130
+ box-sizing: border-box;
131
+ height: 200px;
132
+ line-height: inherit;
133
+ margin: 0;
134
+ padding: 7px 14px;
135
+ box-shadow: none;
136
+ border: 1px solid #dedede;
137
+ border-radius: 3px;
138
+ background-color: #f1f1f1;
139
+ color: #444;
140
+ transition: border-color 0.3s;
141
+ -webkit-appearance: none;
142
+ }
143
+ .wtitan-import-options-button {
144
+ margin: 15px !important;
145
+ }
146
+ .wbcr-factory-nav-tab .dashicons-yes-alt
147
+ {
148
+ color: green !important;
149
+ }
150
+ input[id*="scanner_schedule"], select[id*="scanner_schedule"]
151
+ {
152
+ width: 200px !important;
153
+ }
154
+ .wt-schedule-controls-daily, .wt-schedule-controls-weekly, .wt-schedule-controls-custom
155
+ {
156
+ display: none;
157
+ }
158
+ .wtitan-audit-empty-container
159
+ {
160
+ text-align: left;
161
+ font-size: 16px;
162
+ margin: 20px 0 20px 55px;
163
+ }
164
+ .wt-element-pro:after
165
+ {
166
+ display: inline-block;
167
+ position: relative;
168
+ content: 'PRO';
169
+ background: #f6065b;
170
+ border-radius: 4px;
171
+ color: #fff;
172
+ font-size: 10px;
173
+ line-height: 1;
174
+ font-style: normal;
175
+ padding: 4px 6px;
176
+ margin-left: 4px;
177
+ vertical-align: top;
178
+ top: -10px;
179
+ left: -25px;
180
+ right: 0;
181
+ z-index: 11;
182
+ }
183
+ .wtitan-control-premium-label .factory-buttons-group:after {
184
+ display: inline-block;
185
+ position: relative;
186
+ content: 'PRO';
187
+ background: #f6065b;
188
+ border-radius: 4px;
189
+ color: #fff;
190
+ font-size: 10px;
191
+ line-height: 1;
192
+ font-style: normal;
193
+ padding: 4px 6px;
194
+ margin-left: 4px;
195
+ vertical-align: top;
196
+ top: -8px;
197
+ left: -10px;
198
+ right: auto;
199
+ z-index: 11;
200
+ cursor: pointer;
201
+ }
202
+ .factory-checkbox--disabled button {
203
+ pointer-events: none;
204
+ cursor: not-allowed;
205
+ opacity: 0.65;
206
+ filter: alpha(opacity=65);
207
+ -webkit-box-shadow: none;
208
+ box-shadow: none;
209
+ }
210
+ #wp-admin-bar-titan-premium a span
211
+ {
212
+ color: #ffbe00;
213
+ }
admin/assets/img/check.png ADDED
Binary file
admin/assets/img/clock.png ADDED
Binary file
admin/assets/img/firewall-modal-preloader.gif ADDED
Binary file
admin/assets/img/icon.png ADDED
Binary file
admin/assets/img/loader.gif ADDED
Binary file
admin/assets/img/titan-icon.png ADDED
Binary file
admin/assets/img/x.png ADDED
Binary file
admin/assets/js/dashboard.js ADDED
@@ -0,0 +1,215 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ jQuery(document).ready(function($) {
2
+ $('#js-wtitan-firewall-mode').change(function() {
3
+ var selectmode = $(this);
4
+ console.log(selectmode.val());
5
+
6
+ $('.wtitan-status-block').hide();
7
+ $('.wtitan-status-block.wtitan-status--loading').show();
8
+
9
+ $.ajax(ajaxurl, {
10
+ type: 'post',
11
+ dataType: 'json',
12
+ data: {
13
+ action: 'wtitan-change-firewall-mode',
14
+ mode: $(this).val(),
15
+ _wpnonce: $(this).data('nonce')
16
+ },
17
+ success: function(data, textStatus, jqXHR) {
18
+ var noticeId;
19
+
20
+ console.log(data);
21
+
22
+ if( !data || data.error ) {
23
+ $('.wtitan-status-block.wtitan-status--loading').hide();
24
+ console.log(data);
25
+
26
+ if( data ) {
27
+ noticeId = $.wbcr_factory_clearfy_218.app.showNotice(data.error_message, 'danger');
28
+ }
29
+
30
+ setTimeout(function() {
31
+ $.wbcr_factory_clearfy_218.app.hideNotice(noticeId);
32
+ }, 5000);
33
+ return;
34
+ }
35
+ else {
36
+ $('.wtitan-status-block.wtitan-status--loading').hide();
37
+ $('.wtitan-status-block.wtitan-status--' + selectmode.val()).show();
38
+ }
39
+
40
+ },
41
+ error: function(xhr, ajaxOptions, thrownError) {
42
+ $('.wtitan-status-block.wtitan-status--loading').hide();
43
+ console.log(xhr.status);
44
+ console.log(xhr.responseText);
45
+ console.log(thrownError);
46
+
47
+ var noticeId = $.wbcr_factory_clearfy_218.app.showNotice('Error: [' + thrownError + '] Status: [' + xhr.status + '] Error massage: [' + xhr.responseText + ']', 'danger');
48
+ }
49
+ });
50
+ });
51
+
52
+ $('#wt-antispam-status').change(function() {
53
+ var selectmode = $(this);
54
+ var block = $('#wt-antispam-status-block');
55
+
56
+ block.addClass('wt-block-loading');
57
+
58
+ $.ajax(ajaxurl, {
59
+ type: 'post',
60
+ dataType: 'json',
61
+ data: {
62
+ action: 'wtitan-change-antispam-mode',
63
+ mode: selectmode.val(),
64
+ _wpnonce: $(this).data('nonce')
65
+ },
66
+ success: function(data, textStatus, jqXHR) {
67
+ var noticeId;
68
+ console.log(data);
69
+ block.removeClass('wt-block-loading');
70
+
71
+ if( data.error_message ) {
72
+ noticeId = $.wbcr_factory_clearfy_218.app.showNotice(data.error_message, 'danger');
73
+ }
74
+ else {
75
+ noticeId = $.wbcr_factory_clearfy_218.app.showNotice(data.message, 'success');
76
+ }
77
+
78
+ setTimeout(function() {
79
+ $.wbcr_factory_clearfy_218.app.hideNotice(noticeId);
80
+ }, 5000);
81
+ return;
82
+
83
+ },
84
+ error: function(xhr, ajaxOptions, thrownError) {
85
+ block.removeClass('wt-block-loading');
86
+ console.log(xhr.status);
87
+ console.log(xhr.responseText);
88
+ console.log(thrownError);
89
+
90
+ var noticeId = $.wbcr_factory_clearfy_218.app.showNotice('Error: [' + thrownError + '] Status: [' + xhr.status + '] Error massage: [' + xhr.responseText + ']', 'danger');
91
+ }
92
+ });
93
+ });
94
+
95
+ $('.wt-scanner-speed-button').click(function() {
96
+ var select_speed = $(this);
97
+ console.log(select_speed.val());
98
+
99
+ $('.wt-scanner-speed-button').addClass('disabled');
100
+
101
+ $.ajax(ajaxurl, {
102
+ type: 'post',
103
+ dataType: 'json',
104
+ data: {
105
+ action: 'wtitan_change_scanner_speed',
106
+ speed: select_speed.data('value'),
107
+ _wpnonce: wtdashboard.nonce
108
+ },
109
+ success: function(data, textStatus, jqXHR) {
110
+ var noticeId;
111
+
112
+ console.log(data);
113
+
114
+ if( !data || data.error ) {
115
+
116
+ if( data ) {
117
+ noticeId = $.wbcr_factory_clearfy_218.app.showNotice(data.error_message, 'danger');
118
+ }
119
+
120
+ setTimeout(function() {
121
+ $.wbcr_factory_clearfy_218.app.hideNotice(noticeId);
122
+ }, 5000);
123
+ return;
124
+ }
125
+ else {
126
+ if( data ) {
127
+ noticeId = $.wbcr_factory_clearfy_218.app.showNotice(data.message, 'success');
128
+ }
129
+ setTimeout(function() {
130
+ $.wbcr_factory_clearfy_218.app.hideNotice(noticeId);
131
+ }, 5000);
132
+ }
133
+ $('.wt-scanner-speed-button').removeClass('disabled');
134
+
135
+ },
136
+ error: function(xhr, ajaxOptions, thrownError) {
137
+ $('.wt-scanner-speed-button').removeClass('disabled');
138
+ console.log(xhr.status);
139
+ console.log(xhr.responseText);
140
+ console.log(thrownError);
141
+
142
+ var noticeId = $.wbcr_factory_clearfy_218.app.showNotice('Error: [' + thrownError + '] Status: [' + xhr.status + '] Error massage: [' + xhr.responseText + ']', 'danger');
143
+ }
144
+ });
145
+
146
+ });
147
+
148
+ $('.wt-scanner-schedule-button').click(function() {
149
+ var select = $(this);
150
+ console.log(select.val());
151
+
152
+ $('.wt-scanner-schedule-button').addClass('disabled');
153
+
154
+ $.ajax(ajaxurl, {
155
+ type: 'post',
156
+ dataType: 'json',
157
+ data: {
158
+ action: 'wtitan_change_scanner_schedule',
159
+ schedule: select.data('value'),
160
+ _wpnonce: wtdashboard.nonce
161
+ },
162
+ success: function(data, textStatus, jqXHR) {
163
+ var noticeId;
164
+
165
+ console.log(data);
166
+
167
+ if( !data || data.error ) {
168
+
169
+ if( data ) {
170
+ noticeId = $.wbcr_factory_clearfy_218.app.showNotice(data.error_message, 'danger');
171
+ }
172
+
173
+ setTimeout(function() {
174
+ $.wbcr_factory_clearfy_218.app.hideNotice(noticeId);
175
+ }, 5000);
176
+ return;
177
+ }
178
+ else {
179
+ if( data ) {
180
+ noticeId = $.wbcr_factory_clearfy_218.app.showNotice(data.message, 'success');
181
+ }
182
+ setTimeout(function() {
183
+ $.wbcr_factory_clearfy_218.app.hideNotice(noticeId);
184
+ }, 5000);
185
+ }
186
+ $('.wt-scanner-schedule-button').removeClass('disabled');
187
+
188
+ },
189
+ error: function(xhr, ajaxOptions, thrownError) {
190
+ $('.wt-scanner-schedule-button').removeClass('disabled');
191
+ console.log(xhr.status);
192
+ console.log(xhr.responseText);
193
+ console.log(thrownError);
194
+
195
+ var noticeId = $.wbcr_factory_clearfy_218.app.showNotice('Error: [' + thrownError + '] Status: [' + xhr.status + '] Error massage: [' + xhr.responseText + ']', 'danger');
196
+ }
197
+ });
198
+
199
+ });
200
+
201
+ jQuery('#wt-quickstart-scan').on('click', function(e) {
202
+ e.preventDefault();
203
+ var btn = jQuery(this);
204
+ btn.hide();
205
+ jQuery('.wt-scan-icon-loader').show();
206
+ //jQuery('#scan').trigger('click');
207
+ vulnerability_ajax(false);
208
+ audit_ajax(false);
209
+ });
210
+
211
+ $('.factory-checkbox--disabled.wtitan-control-premium-label .factory-buttons-group').click(function(e) {
212
+ e.stopPropagation();
213
+ window.location.href = 'https://titansitescanner.com/pricing/';
214
+ });
215
+ });
admin/assets/js/firewall/firewall-block-ip.js ADDED
@@ -0,0 +1,126 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ (function($) {
2
+ 'use strict';
3
+
4
+ class FirewallBlockIps {
5
+ constructor() {
6
+ this.initEvents();
7
+ this.createTableRow('IP Block', '191.168.200.201', '22.03.2020 22:42', 'Yahoo crawler', 'Permanent', '0', 'Never');
8
+ this.createTableRow('IP Block', '192.168.200.145', '22.03.2020 00:42', 'Bad bot', 'Permanent', '0', 'Never');
9
+ }
10
+
11
+ initEvents() {
12
+ var self = this;
13
+
14
+ $('#wtitan-blocks-ips').click(function(e) {
15
+ e.preventDefault();
16
+
17
+ let $self = $(this),
18
+ infosModal = $('#wtitan-tmpl-block-ips-modal'),
19
+ btn = jQuery(this);
20
+
21
+ if( !infosModal.length ) {
22
+ console.log('[Error]: Html template for modal not found.');
23
+ return;
24
+ }
25
+
26
+ Swal.fire({
27
+ html: infosModal.html(),
28
+ customClass: 'wtitan-modal wtitan-ips-blocking-modal',
29
+ width: 500,
30
+ showCancelButton: true,
31
+ showCloseButton: true,
32
+ confirmButtonText: 'Block',
33
+ preConfirm: function() {
34
+ return self.preConfirmModal();
35
+ },
36
+ onOpen: function() {
37
+ $('.wtitan-ips-blocking-modal__tab').find('a').click(function() {
38
+ $('.wtitan-ips-blocking-modal__tab').removeClass('wtitan-ips-blocking-modal__tab--active');
39
+ $('.wtitan-ips-blocking-modal__tab-content').removeClass('wtitan-ips-blocking-modal__tab-content--active');
40
+
41
+ $(this).parent().addClass('wtitan-ips-blocking-modal__tab--active');
42
+
43
+ let tabID = $(this).attr('href').replace('#', '');
44
+ $('#wtitan-ips-blocking-modal__' + tabID + '-tab-content').addClass('wtitan-ips-blocking-modal__tab-content--active')
45
+ });
46
+ }
47
+ }).then(function(result) {
48
+ if( result.value ) {
49
+ console.log(result);
50
+ }
51
+ });
52
+ });
53
+ }
54
+
55
+ preConfirmModal() {
56
+ return new Promise((resolve, reject) => {
57
+ $.ajax(ajaxurl, {
58
+ type: 'post',
59
+ dataType: 'json',
60
+ data: {
61
+ action: 'wtitan-block-ips',
62
+ payload: {
63
+ type: $('.wtitan-ips-blocking-modal__tab--active').find('a').attr('href').replace('#', ''),
64
+ duration: 0,
65
+ reason: $('#wtitan-ips-blocking-modal__form-reason-field').val(),
66
+ ip: $('#wtitan-ips-blocking-modal__form-ip-field').val(),
67
+ ipRange: $('#wtitan-ips-blocking-modal__form-range-ip-field').val(),
68
+ hostname: $('#wtitan-ips-blocking-modal__form-hostname-field').val(),
69
+ userAgent: $('#wtitan-ips-blocking-modal__form-user-agent-field').val(),
70
+ referrer: $('#wtitan-ips-blocking-modal__form-referrer-field').val(),
71
+ },
72
+ _wpnonce: $('#wtitan-blocks-ips').data('nonce')
73
+ },
74
+ success: function(data, textStatus, jqXHR) {
75
+ var noticeId;
76
+
77
+ console.log(data);
78
+
79
+ if( !data || data.error ) {
80
+ console.log(data);
81
+
82
+ if( data ) {
83
+ noticeId = $.wbcr_factory_clearfy_218.app.showNotice(data.error_message, 'danger');
84
+ }
85
+
86
+ setTimeout(function() {
87
+ $.wbcr_factory_clearfy_218.app.hideNotice(noticeId);
88
+ }, 5000);
89
+ return data;
90
+ }
91
+
92
+ resolve(data)
93
+ },
94
+ error: function(xhr, ajaxOptions, thrownError) {
95
+ console.log(xhr.status);
96
+ console.log(xhr.responseText);
97
+ console.log(thrownError);
98
+
99
+ var noticeId = $.wbcr_factory_clearfy_218.app.showNotice('Error: [' + thrownError + '] Status: [' + xhr.status + '] Error massage: [' + xhr.responseText + ']', 'danger');
100
+ reject(thrownError);
101
+ Swal.close();
102
+ }
103
+ });
104
+ })
105
+ }
106
+
107
+ createTableRow(blockType, detail, ruleAdded, reason, expiration, blockCount, lastAttempt) {
108
+ let tableBodyElement = $('.wtitan-ips-blocking__table').find('tbody'),
109
+ row = $('<tr>').clone();
110
+
111
+ row.appendTo(tableBodyElement);
112
+ row.append($('<td>').html('<input type="checkbox">').clone());
113
+ row.append($('<td>').html(blockType).clone());
114
+ row.append($('<td>').html(detail).clone());
115
+ row.append($('<td>').html(ruleAdded).clone());
116
+ row.append($('<td>').html(reason).clone());
117
+ row.append($('<td>').html(expiration).clone());
118
+ row.append($('<td>').html(blockCount).clone());
119
+ row.append($('<td>').html(lastAttempt).clone());
120
+ }
121
+ }
122
+
123
+ jQuery(document).ready(function($) {
124
+ new FirewallBlockIps();
125
+ });
126
+ })(jQuery);
admin/assets/js/firewall/firewall-dashboard.js ADDED
@@ -0,0 +1,289 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ jQuery(document).ready(function($) {
2
+ $('.js-wtitan-excluded-rules__checkbox').click(function() {
3
+ let excludedRulesFieldElem = $('#js-wtitan-excluded-rules__field'),
4
+ excludedRules = split(',', excludedRulesFieldElem.val());
5
+
6
+ console.log(excludedRules);
7
+
8
+ return false;
9
+ });
10
+
11
+ $("#wtitan-circle-firewall-coverage").fu_popover({
12
+ content: $('#wtitan-status-tooltip').html(),
13
+ dismissable: true,
14
+ placement: 'left',
15
+ trigger: 'hover',
16
+ width: '350px',
17
+ autoHide: false
18
+ });
19
+
20
+ $('#js-wtitan-firewall-mode').change(function() {
21
+ console.log($(this).val());
22
+
23
+ $('.wtitan-status-block').hide();
24
+ $('.wtitan-status-block.wtitan-status--' + $(this).val()).show();
25
+
26
+ $.ajax(ajaxurl, {
27
+ type: 'post',
28
+ dataType: 'json',
29
+ data: {
30
+ action: 'wtitan-change-firewall-mode',
31
+ mode: $(this).val(),
32
+ _wpnonce: $(this).data('nonce')
33
+ },
34
+ success: function(data, textStatus, jqXHR) {
35
+ var noticeId;
36
+
37
+ console.log(data);
38
+
39
+ if( !data || data.error ) {
40
+ console.log(data);
41
+
42
+ if( data ) {
43
+ noticeId = $.wbcr_factory_clearfy_218.app.showNotice(data.error_message, 'danger');
44
+ }
45
+
46
+ setTimeout(function() {
47
+ $.wbcr_factory_clearfy_218.app.hideNotice(noticeId);
48
+ }, 5000);
49
+ return;
50
+ }
51
+
52
+ },
53
+ error: function(xhr, ajaxOptions, thrownError) {
54
+ console.log(xhr.status);
55
+ console.log(xhr.responseText);
56
+ console.log(thrownError);
57
+
58
+ var noticeId = $.wbcr_factory_clearfy_218.app.showNotice('Error: [' + thrownError + '] Status: [' + xhr.status + '] Error massage: [' + xhr.responseText + ']', 'danger');
59
+ }
60
+ });
61
+ });
62
+
63
+ $('#js-wtitan-optimize-firewall-protection,#js-wtitan-firewall-uninstall').click(function(e) {
64
+ e.preventDefault();
65
+ var infosModal, action;
66
+ infosModal = $('#wtitan-tmpl-default-modal');
67
+ action = 'install';
68
+
69
+ if( "js-wtitan-optimize-firewall-protection" !== $(this).attr('id') ) {
70
+ action = 'uninstall';
71
+ }
72
+
73
+ if( !infosModal.length ) {
74
+ console.log('[Error]: Html template for modal not found.');
75
+ return;
76
+ }
77
+
78
+ Swal.fire({
79
+ html: infosModal.html(),
80
+ customClass: 'wtitan-modal wtitan-modal-confirm',
81
+ width: 800,
82
+ showCancelButton: true,
83
+ showCloseButton: true,
84
+ confirmButtonText: 'Continue',
85
+ preConfirm: function() {
86
+ return new Promise((resolve, reject) => {
87
+ $.ajax(ajaxurl, {
88
+ type: 'post',
89
+ dataType: 'json',
90
+ data: {
91
+ action: "install" === action
92
+ ? 'wtitan-install-auto-prepend'
93
+ : 'wtitan-uninstall-auto-prepend',
94
+ server_configuration: $('#wtitan-server-config').val(),
95
+ //iniModified: true,
96
+ //_wpnonce: $(this).data('nonce')
97
+ },
98
+ success: function(data, textStatus, jqXHR) {
99
+ var noticeId;
100
+
101
+ console.log(data);
102
+
103
+ if( !data || data.error ) {
104
+ console.log(data);
105
+
106
+ if( data ) {
107
+ noticeId = $.wbcr_factory_clearfy_218.app.showNotice(data.error_message, 'danger');
108
+ }
109
+
110
+ setTimeout(function() {
111
+ $.wbcr_factory_clearfy_218.app.hideNotice(noticeId);
112
+ }, 5000);
113
+ return data;
114
+ }
115
+
116
+ resolve(data)
117
+ },
118
+ error: function(xhr, ajaxOptions, thrownError) {
119
+ console.log(xhr.status);
120
+ console.log(xhr.responseText);
121
+ console.log(thrownError);
122
+
123
+ var noticeId = $.wbcr_factory_clearfy_218.app.showNotice('Error: [' + thrownError + '] Status: [' + xhr.status + '] Error massage: [' + xhr.responseText + ']', 'danger');
124
+ reject(thrownError)
125
+ }
126
+ });
127
+ })
128
+
129
+ /*(login) => {
130
+ return fetch(`//api.github.com/users/${login}`)
131
+ .then(response => {
132
+ if( !response.ok ) {
133
+ throw new Error(response.statusText)
134
+ }
135
+ return response.json()
136
+ })
137
+ .catch(error => {
138
+ Swal.showValidationMessage(
139
+ `Request failed: ${error}`
140
+ )
141
+ })*/
142
+ },
143
+ onOpen: function() {
144
+
145
+ if( 'install' === action ) {
146
+ $('.wtitan-modal').find('.wtitan-install-auto-prepend-modal-content').css('display', 'block');
147
+ } else {
148
+ $('.wtitan-modal').find('.wtitan-uninstall-auto-prepend-modal-content').css('display', 'block');
149
+ }
150
+
151
+ $('#wtitan-include-prepend > li').each(function(index, element) {
152
+ console.log('1111');
153
+ $(element).on('click', function(e) {
154
+ console.log('fsdf');
155
+ e.preventDefault();
156
+ e.stopPropagation();
157
+
158
+ var control = $(this).closest('.wtitan-switch');
159
+ var value = $(this).data('optionValue');
160
+
161
+ control.find('li').each(function() {
162
+ $(this).toggleClass('wtitan-active', value === $(this).data('optionValue'));
163
+ });
164
+ });
165
+ });
166
+
167
+ //var nginxNotice = $('.wtitan-nginx-config');
168
+ //var manualNotice = $('.wtitan-manual-config');
169
+ $('#wtitan-server-config').on('change', function() {
170
+ var el = $(this);
171
+ /*if( manualNotice.length ) {
172
+ if( el.val() == 'manual' ) {
173
+ manualNotice.fadeIn(400, function() {
174
+ $.wfcolorbox.resize();
175
+ });
176
+ } else {
177
+ manualNotice.fadeOut(400, function() {
178
+ $.wfcolorbox.resize();
179
+ });
180
+ }
181
+ }*/
182
+
183
+ var identifier = '.wtitan-backups-' + el.val().replace(/[^a-z0-9\-]/i, '');
184
+ $('.wtitan-backups').hide();
185
+ $(identifier).show();
186
+ if( $(identifier).find('.wtitan-backup-file-list').children().length > 0 ) {
187
+ $('.wtitan-download-instructions').show();
188
+ } else {
189
+ $('.wtitan-download-instructions').hide();
190
+ }
191
+
192
+ /*if( nginxNotice.length ) { //Install only
193
+ if( el.val() == 'nginx' ) {
194
+ nginxNotice.fadeIn(400, function() {
195
+ $.wfcolorbox.resize();
196
+ });
197
+ } else {
198
+ nginxNotice.fadeOut(400, function() {
199
+ $.wfcolorbox.resize();
200
+ });
201
+ }
202
+
203
+ validateContinue();
204
+ return;
205
+ }*/
206
+
207
+ //$.wfcolorbox.resize();
208
+ //validateContinue();
209
+ }).triggerHandler('change');
210
+ }
211
+ }).then(function(result) {
212
+ console.log(result);
213
+ console.log(action);
214
+
215
+ if( result.value && result.value.html ) {
216
+
217
+ let swalOptions = {
218
+ html: result.value.html,
219
+ customClass: 'wtitan-modal wtitan-modal-confirm',
220
+ showConfirmButton: false,
221
+ showCancelButton: false,
222
+ showCloseButton: true
223
+ };
224
+
225
+ // Uninstall action
226
+ if( 'uninstall' === action && result.value.uninstallation_waiting ) {
227
+ let timeout = 0;
228
+
229
+ if( result.value.timeout ) {
230
+ timeout = parseInt(result.value.timeout) * 1000;
231
+ }
232
+
233
+ setTimeout(function() {
234
+
235
+ let data = {
236
+ action: 'wtitan-uninstall-auto-prepend',
237
+ server_configuration: result.value.server_configuration,
238
+ ini_modified: true,
239
+ //_wpnonce: $(this).data('nonce')};
240
+ };
241
+
242
+ if( result.value.credentials ) {
243
+ data['credentials'] = result.value.credentials;
244
+ }
245
+
246
+ if( result.value.credentials_signature ) {
247
+ data['credentials_signature'] = result.value.credentials_signature;
248
+ }
249
+
250
+ $.ajax(ajaxurl, {
251
+ type: 'post',
252
+ dataType: 'json',
253
+ data: data,
254
+ success: function(data, textStatus, jqXHR) {
255
+ if( data.uninstallation_success ) {
256
+ swalOptions.html = data.html;
257
+
258
+ Swal.fire(swalOptions).then(function(r) {
259
+ window.location.reload();
260
+ });
261
+ }
262
+ },
263
+ error: function(xhr, ajaxOptions, thrownError) {
264
+ console.log(xhr.status);
265
+ console.log(xhr.responseText);
266
+ console.log(thrownError);
267
+
268
+ var noticeId = $.wbcr_factory_clearfy_218.app.showNotice('Error: [' + thrownError + '] Status: [' + xhr.status + '] Error massage: [' + xhr.responseText + ']', 'danger');
269
+
270
+ }
271
+ });
272
+ }, timeout);
273
+ }
274
+
275
+ Swal.fire(swalOptions).then(function(r) {
276
+ console.log(result);
277
+
278
+ // Install action
279
+ if( 'install' === action ) {
280
+ window.location.reload();
281
+ return false;
282
+ }
283
+ });
284
+ }
285
+
286
+ });
287
+ });
288
+ })
289
+ ;
admin/assets/js/firewall/firewall-settings.js ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ (function($) {
2
+ 'use strict';
3
+
4
+ jQuery(document).ready(function($) {
5
+ let excludedRulesFieldElem = $('#js-wtitan-excluded-rules__field'),
6
+ excludedRules = ("" !== excludedRulesFieldElem.val()) ? excludedRulesFieldElem.val().split(',') : [];
7
+
8
+ $('.js-wtitan-excluded-rules__checkbox').click(function() {
9
+ let ruleID = parseInt($(this).val());
10
+
11
+ if( $(this).is(":checked") ) {
12
+ excludedRules.splice($.inArray(parseInt(ruleID), excludedRules), 1);
13
+ } else {
14
+ excludedRules.push(ruleID);
15
+ }
16
+
17
+ excludedRulesFieldElem.val(excludedRules.join(','));
18
+ });
19
+ });
20
+ })(jQuery);
admin/assets/js/import.js ADDED
@@ -0,0 +1,103 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ jQuery(document).ready(function($) {
2
+ $('.wtitan-import-options-button').click(function() {
3
+ var settings = $('#wbcr-clearfy-import-export').val(),
4
+ $this = $(this);
5
+
6
+ if( !settings ) {
7
+ $.wbcr_factory_clearfy_217.app.showNotice('Import options is empty!', 'danger');
8
+ return false;
9
+ }
10
+
11
+ if( void 0 == wtitan_ajax || !wtitan_ajax.import_options_nonce ) {
12
+ $.wbcr_factory_clearfy_217.app.showNotice('Unknown Javascript error, most likely the wtitan_ajax variable does not exist!', 'danger');
13
+ return false;
14
+ }
15
+
16
+ $(this).prop('disabled', true);
17
+
18
+ sendRequest({
19
+ action: 'wtitan_import_settings',
20
+ _wpnonce: wtitan_ajax.import_options_nonce,
21
+ settings: settings
22
+ }, function(response) {
23
+ $this.prop('disabled', false);
24
+
25
+ if( response.data.update_notice ) {
26
+ if( !$('.wbcr-clr-update-package').length ) {
27
+ $.wbcr_factory_clearfy_217.app.showNotice(response.data.update_notice);
28
+ }
29
+ } else {
30
+ if( $('.wbcr-clr-update-package').length ) {
31
+ $('.wbcr-clr-update-package').closest('.wbcr-factory-warning-notice').remove();
32
+ }
33
+ }
34
+ });
35
+
36
+ return false;
37
+ });
38
+
39
+ function sendRequest(request_data, beforeValidateCallback, successCallback) {
40
+
41
+ if( wtitan_ajax === undefined ) {
42
+ console.log('Undefinded wtitan_ajax object.');
43
+ return;
44
+ }
45
+
46
+ if( typeof request_data === 'object' ) {
47
+ request_data.security = wtitan_ajax.ajax_nonce;
48
+ }
49
+
50
+ $.ajax(ajaxurl, {
51
+ type: 'post',
52
+ dataType: 'json',
53
+ data: request_data,
54
+ success: function(data, textStatus, jqXHR) {
55
+ var noticeId;
56
+
57
+ beforeValidateCallback && beforeValidateCallback(data);
58
+
59
+ if( !data || data.error ) {
60
+ console.log(data);
61
+
62
+ if( data ) {
63
+ noticeId = $.wbcr_factory_clearfy_217.app.showNotice(data.error_message, 'danger');
64
+ } else {
65
+ if( void 0 != wtitan_ajax ) {
66
+ noticeId = $.wbcr_factory_clearfy_217.app.showNotice(wtitan_ajax.i18n.unknown_error, 'danger');
67
+ }
68
+ }
69
+
70
+ setTimeout(function() {
71
+ $.wbcr_factory_clearfy_217.app.hideNotice(noticeId);
72
+ }, 5000);
73
+ return;
74
+ }
75
+
76
+ successCallback && successCallback(data);
77
+
78
+ if( !request_data.flush_redirect ) {
79
+ if( void 0 != wtitan_ajax ) {
80
+ noticeId = $.wbcr_factory_clearfy_217.app.showNotice(wtitan_ajax.i18n.success_update_settings, 'success');
81
+
82
+ setTimeout(function() {
83
+ $.wbcr_factory_clearfy_217.app.hideNotice(noticeId);
84
+ }, 5000);
85
+ }
86
+ return;
87
+ }
88
+
89
+ window.location.href = wtitan_ajax.flush_cache_url;
90
+ // открыть уведомление
91
+
92
+ },
93
+ error: function(xhr, ajaxOptions, thrownError) {
94
+ console.log(xhr.status);
95
+ console.log(xhr.responseText);
96
+ console.log(thrownError);
97
+
98
+ var noticeId = $.wbcr_factory_clearfy_217.app.showNotice('Error: [' + thrownError + '] Status: [' + xhr.status + '] Error massage: [' + xhr.responseText + ']', 'danger');
99
+ }
100
+ });
101
+ }
102
+
103
+ });
admin/assets/js/libs/circular-progress.js ADDED
@@ -0,0 +1,140 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ *
3
+ * @author Webcraftic <wordpress.webraftic@gmail.com>
4
+ * @copyright (c) 04.02.2020, Webcraftic
5
+ * @version 1.0
6
+ */
7
+
8
+ //wfCircularProgress
9
+ jQuery.fn.wfCircularProgress = function(options) {
10
+ jQuery(this).each(function() {
11
+ var creationOptions;
12
+ try {
13
+ creationOptions = JSON.parse(jQuery(this).data('wfCircularProgressOptions'));
14
+ }
15
+ catch( e ) { /* Ignore */
16
+ }
17
+ if( typeof creationOptions !== 'object' ) {
18
+ creationOptions = {};
19
+ }
20
+ var opts = jQuery.extend({}, jQuery.fn.wfCircularProgress.defaults, creationOptions, options);
21
+
22
+ var center = Math.floor(opts.diameter / 2);
23
+ var insetRadius = center - opts.strokeWidth * 2;
24
+
25
+ var circumference = 2 * insetRadius * Math.PI;
26
+ var finalOffset = -(circumference * (1 - opts.endPercent));
27
+ var initialOffset = -(circumference);
28
+
29
+ var terminatorRadius = Math.floor(opts.strokeWidth * 1.5);
30
+ var terminatorDiameter = 2 * terminatorRadius;
31
+ var finalTerminatorX = center - insetRadius * Math.cos(Math.PI * 2 * (opts.endPercent - 0.25));
32
+ var finalTerminatorY = center + insetRadius * Math.sin(Math.PI * 2 * (opts.endPercent - 0.25));
33
+ var initialTerminatorX = center - insetRadius * Math.cos(Math.PI * 2 * (opts.startPercent - 0.25));
34
+ var initialTerminatorY = center + insetRadius * Math.sin(Math.PI * 2 * (opts.startPercent - 0.25));
35
+
36
+ var terminatorSVG = "m 0,-" + terminatorRadius + " a " + terminatorRadius + "," + terminatorRadius + " 0 1 1 0," + terminatorDiameter + " a " + terminatorRadius + "," + terminatorRadius + " 0 1 1 0,-" + terminatorDiameter;
37
+
38
+ jQuery(this).data('wfCircularProgressOptions', JSON.stringify(opts));
39
+
40
+ jQuery(this).css('width', opts.diameter + 'px');
41
+ jQuery(this).css('height', opts.diameter + 'px');
42
+
43
+ var svg = jQuery(this).find('svg');
44
+ if( svg.length === 0 ) {
45
+ svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
46
+ jQuery(this).append(svg);
47
+ }
48
+ var inactivePath = jQuery(this).find('.wtitan-status-circular-inactive-path');
49
+ if( inactivePath.length === 0 ) {
50
+ inactivePath = document.createElementNS("http://www.w3.org/2000/svg", "path");
51
+ jQuery(inactivePath).addClass('wtitan-status-circular-inactive-path');
52
+ jQuery(svg).append(inactivePath);
53
+ }
54
+ var activePath = jQuery(this).find('.wtitan-status-circular-active-path');
55
+ if( activePath.length === 0 ) {
56
+ activePath = document.createElementNS("http://www.w3.org/2000/svg", "path");
57
+ jQuery(activePath).addClass('wtitan-status-circular-active-path');
58
+ jQuery(svg).append(activePath);
59
+ }
60
+ var terminator = jQuery(this).find('.wtitan-status-circular-terminator');
61
+ if( terminator.length === 0 ) {
62
+ terminator = document.createElementNS("http://www.w3.org/2000/svg", "path");
63
+ jQuery(terminator).addClass('wtitan-status-circular-terminator');
64
+ jQuery(svg).append(terminator);
65
+ }
66
+ var text = jQuery(this).find('.wtitan-status-circular-text');
67
+ if( text.length === 0 ) {
68
+ text = jQuery('<div class="wtitan-status-circular-text"></div>');
69
+ jQuery(this).append(text);
70
+ }
71
+ var pendingOverlay = jQuery(this).find('.wf-status-overlay-text');
72
+ if( pendingOverlay.length === 0 && opts.pendingMessage.length !== 0 ) {
73
+ pendingOverlay = jQuery('<div class="wf-status-overlay-text"></div>');
74
+ jQuery(this).append(pendingOverlay);
75
+ }
76
+
77
+ jQuery(svg).attr('viewBox', '0 0 ' + opts.diameter + ' ' + opts.diameter);
78
+ jQuery(svg).css('display', opts.css_display);
79
+ jQuery(svg).css('width', opts.diameter + 'px');
80
+ jQuery(svg).css('height', opts.diameter + 'px');
81
+ jQuery(inactivePath).attr('d', 'M ' + center + ',' + center + ' m 0,-' + insetRadius + ' a ' + insetRadius + ',' + insetRadius + ' 0 1 1 0,' + (2 * insetRadius) + ' a ' + insetRadius + ',' + insetRadius + ' 0 1 1 0,-' + (2 * insetRadius));
82
+ jQuery(inactivePath).attr('stroke', opts.inactiveColor);
83
+ jQuery(inactivePath).attr('stroke-width', opts.strokeWidth);
84
+ jQuery(inactivePath).attr('fill-opacity', 0);
85
+ jQuery(activePath).attr('d', 'M ' + center + ',' + center + ' m 0,-' + insetRadius + ' a ' + insetRadius + ',' + insetRadius + ' 0 1 1 0,' + (2 * insetRadius) + ' a ' + insetRadius + ',' + insetRadius + ' 0 1 1 0,-' + (2 * insetRadius));
86
+ jQuery(activePath).attr('stroke', opts.color);
87
+ jQuery(activePath).attr('stroke-width', opts.strokeWidth);
88
+ jQuery(activePath).attr('stroke-dasharray', circumference + ',' + circumference);
89
+ jQuery(activePath).attr('stroke-dashoffset', initialOffset);
90
+ jQuery(activePath).attr('fill-opacity', 0);
91
+ jQuery(terminator).attr('d', 'M ' + initialTerminatorX + ',' + initialTerminatorY + ' ' + terminatorSVG);
92
+ jQuery(terminator).attr('stroke', opts.color);
93
+ jQuery(terminator).attr('stroke-width', opts.strokeWidth);
94
+ jQuery(terminator).attr('fill', '#ffffff');
95
+ jQuery(pendingOverlay).html(opts.pendingMessage);
96
+
97
+ jQuery(pendingOverlay).animate({
98
+ opacity: opts.pendingOverlay ? 1.0 : 0.0,
99
+ }, {
100
+ duration: 500,
101
+ step: function(value) {
102
+ var opacity = 1.0 - (value * 0.8);
103
+ jQuery(svg).css('opacity', opacity);
104
+ jQuery(text).css('opacity', opacity);
105
+ },
106
+ complete: function() {
107
+ jQuery(svg).css('opacity', opts.pendingOverlay ? 0.2 : 1.0);
108
+ jQuery(text).css('opacity', opts.pendingOverlay ? 0.2 : 1.0);
109
+ }
110
+ });
111
+
112
+ jQuery(activePath).animate({
113
+ "stroke-dashoffset": finalOffset + 'px'
114
+ }, {
115
+ duration: 500,
116
+ step: function(value) {
117
+ var percentage = 1 + value / circumference;
118
+ var x = center - insetRadius * Math.cos(Math.PI * 2 * (percentage - 0.25));
119
+ var y = center + insetRadius * Math.sin(Math.PI * 2 * (percentage - 0.25));
120
+ jQuery(terminator).attr('d', 'M ' + x + ',' + y + ' ' + terminatorSVG);
121
+ text.html(Math.round(percentage * 100) + '%');
122
+ },
123
+ complete: function() {
124
+ text.html(Math.round(opts.endPercent * 100) + '%');
125
+ }
126
+ });
127
+ });
128
+ };
129
+
130
+ jQuery.fn.wfCircularProgress.defaults = {
131
+ startPercent: 0,
132
+ endPercent: 1,
133
+ color: '#16bc9b',
134
+ inactiveColor: '#ececec',
135
+ strokeWidth: 3,
136
+ diameter: 100,
137
+ pendingOverlay: false,
138
+ pendingMessage: 'Note: Status will update when changes are saved',
139
+ css_display: 'block',
140
+ };
admin/assets/js/libs/jquery.datetimepicker.full.min.js ADDED
@@ -0,0 +1 @@
 
1
+ var DateFormatter;!function(){"use strict";var D,s,r,a,n;D=function(e,t){return"string"==typeof e&&"string"==typeof t&&e.toLowerCase()===t.toLowerCase()},s=function(e,t,a){var n=a||"0",r=e.toString();return r.length<t?s(n+r,t):r},r=function(e){var t,a;for(e=e||{},t=1;t<arguments.length;t++)if(a=arguments[t])for(var n in a)a.hasOwnProperty(n)&&("object"==typeof a[n]?r(e[n],a[n]):e[n]=a[n]);return e},a=function(e,t){for(var a=0;a<t.length;a++)if(t[a].toLowerCase()===e.toLowerCase())return a;return-1},n={dateSettings:{days:["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],daysShort:["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],months:["January","February","March","April","May","June","July","August","September","October","November","December"],monthsShort:["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],meridiem:["AM","PM"],ordinal:function(e){var t=e%10,a={1:"st",2:"nd",3:"rd"};return 1!==Math.floor(e%100/10)&&a[t]?a[t]:"th"}},separators:/[ \-+\/\.T:@]/g,validParts:/[dDjlNSwzWFmMntLoYyaABgGhHisueTIOPZcrU]/g,intParts:/[djwNzmnyYhHgGis]/g,tzParts:/\b(?:[PMCEA][SDP]T|(?:Pacific|Mountain|Central|Eastern|Atlantic) (?:Standard|Daylight|Prevailing) Time|(?:GMT|UTC)(?:[-+]\d{4})?)\b/g,tzClip:/[^-+\dA-Z]/g},(DateFormatter=function(e){var t=this,a=r(n,e);t.dateSettings=a.dateSettings,t.separators=a.separators,t.validParts=a.validParts,t.intParts=a.intParts,t.tzParts=a.tzParts,t.tzClip=a.tzClip}).prototype={constructor:DateFormatter,getMonth:function(e){var t;return 0===(t=a(e,this.dateSettings.monthsShort)+1)&&(t=a(e,this.dateSettings.months)+1),t},parseDate:function(e,t){var a,n,r,o,i,s,d,u,l,f,c=this,m=!1,h=!1,g=c.dateSettings,p={date:null,year:null,month:null,day:null,hour:0,min:0,sec:0};if(!e)return null;if(e instanceof Date)return e;if("U"===t)return(r=parseInt(e))?new Date(1e3*r):e;switch(typeof e){case"number":return new Date(e);case"string":break;default:return null}if(!(a=t.match(c.validParts))||0===a.length)throw new Error("Invalid date format definition.");for(n=e.replace(c.separators,"\0").split("\0"),r=0;r<n.length;r++)switch(o=n[r],i=parseInt(o),a[r]){case"y":case"Y":if(!i)return null;l=o.length,p.year=2===l?parseInt((i<70?"20":"19")+o):i,m=!0;break;case"m":case"n":case"M":case"F":if(isNaN(i)){if(!(0<(s=c.getMonth(o))))return null;p.month=s}else{if(!(1<=i&&i<=12))return null;p.month=i}m=!0;break;case"d":case"j":if(!(1<=i&&i<=31))return null;p.day=i,m=!0;break;case"g":case"h":if(f=n[d=-1<a.indexOf("a")?a.indexOf("a"):-1<a.indexOf("A")?a.indexOf("A"):-1],-1<d)u=D(f,g.meridiem[0])?0:D(f,g.meridiem[1])?12:-1,1<=i&&i<=12&&-1<u?p.hour=i+u-1:0<=i&&i<=23&&(p.hour=i);else{if(!(0<=i&&i<=23))return null;p.hour=i}h=!0;break;case"G":case"H":if(!(0<=i&&i<=23))return null;p.hour=i,h=!0;break;case"i":if(!(0<=i&&i<=59))return null;p.min=i,h=!0;break;case"s":if(!(0<=i&&i<=59))return null;p.sec=i,h=!0}if(!0===m&&p.year&&p.month&&p.day)p.date=new Date(p.year,p.month-1,p.day,p.hour,p.min,p.sec,0);else{if(!0!==h)return null;p.date=new Date(0,0,0,p.hour,p.min,p.sec,0)}return p.date},guessDate:function(e,t){if("string"!=typeof e)return e;var a,n,r,o,i,s,d=e.replace(this.separators,"\0").split("\0"),u=t.match(this.validParts),l=new Date,f=0;if(!/^[djmn]/g.test(u[0]))return e;for(r=0;r<d.length;r++){if(f=2,i=d[r],s=parseInt(i.substr(0,2)),isNaN(s))return null;switch(r){case 0:"m"===u[0]||"n"===u[0]?l.setMonth(s-1):l.setDate(s);break;case 1:"m"===u[0]||"n"===u[0]?l.setDate(s):l.setMonth(s-1);break;case 2:if(n=l.getFullYear(),f=(a=i.length)<4?a:4,!(n=parseInt(a<4?n.toString().substr(0,4-a)+i:i.substr(0,4))))return null;l.setFullYear(n);break;case 3:l.setHours(s);break;case 4:l.setMinutes(s);break;case 5:l.setSeconds(s)}0<(o=i.substr(f)).length&&d.splice(r+1,0,o)}return l},parseFormat:function(e,n){var a,t=this,r=t.dateSettings,o=/\\?(.?)/gi,i=function(e,t){return a[e]?a[e]():t};return a={d:function(){return s(a.j(),2)},D:function(){return r.daysShort[a.w()]},j:function(){return n.getDate()},l:function(){return r.days[a.w()]},N:function(){return a.w()||7},w:function(){return n.getDay()},z:function(){var e=new Date(a.Y(),a.n()-1,a.j()),t=new Date(a.Y(),0,1);return Math.round((e-t)/864e5)},W:function(){var e=new Date(a.Y(),a.n()-1,a.j()-a.N()+3),t=new Date(e.getFullYear(),0,4);return s(1+Math.round((e-t)/864e5/7),2)},F:function(){return r.months[n.getMonth()]},m:function(){return s(a.n(),2)},M:function(){return r.monthsShort[n.getMonth()]},n:function(){return n.getMonth()+1},t:function(){return new Date(a.Y(),a.n(),0).getDate()},L:function(){var e=a.Y();return e%4==0&&e%100!=0||e%400==0?1:0},o:function(){var e=a.n(),t=a.W();return a.Y()+(12===e&&t<9?1:1===e&&9<t?-1:0)},Y:function(){return n.getFullYear()},y:function(){return a.Y().toString().slice(-2)},a:function(){return a.A().toLowerCase()},A:function(){var e=a.G()<12?0:1;return r.meridiem[e]},B:function(){var e=3600*n.getUTCHours(),t=60*n.getUTCMinutes(),a=n.getUTCSeconds();return s(Math.floor((e+t+a+3600)/86.4)%1e3,3)},g:function(){return a.G()%12||12},G:function(){return n.getHours()},h:function(){return s(a.g(),2)},H:function(){return s(a.G(),2)},i:function(){return s(n.getMinutes(),2)},s:function(){return s(n.getSeconds(),2)},u:function(){return s(1e3*n.getMilliseconds(),6)},e:function(){return/\((.*)\)/.exec(String(n))[1]||"Coordinated Universal Time"},I:function(){return new Date(a.Y(),0)-Date.UTC(a.Y(),0)!=new Date(a.Y(),6)-Date.UTC(a.Y(),6)?1:0},O:function(){var e=n.getTimezoneOffset(),t=Math.abs(e);return(0<e?"-":"+")+s(100*Math.floor(t/60)+t%60,4)},P:function(){var e=a.O();return e.substr(0,3)+":"+e.substr(3,2)},T:function(){return(String(n).match(t.tzParts)||[""]).pop().replace(t.tzClip,"")||"UTC"},Z:function(){return 60*-n.getTimezoneOffset()},c:function(){return"Y-m-d\\TH:i:sP".replace(o,i)},r:function(){return"D, d M Y H:i:s O".replace(o,i)},U:function(){return n.getTime()/1e3||0}},i(e,e)},formatDate:function(e,t){var a,n,r,o,i,s="";if("string"==typeof e&&!(e=this.parseDate(e,t)))return null;if(e instanceof Date){for(r=t.length,a=0;a<r;a++)"S"!==(i=t.charAt(a))&&"\\"!==i&&(0<a&&"\\"===t.charAt(a-1)?s+=i:(o=this.parseFormat(i,e),a!==r-1&&this.intParts.test(i)&&"S"===t.charAt(a+1)&&(n=parseInt(o)||0,o+=this.dateSettings.ordinal(n)),s+=o));return s}return""}}}();var datetimepickerFactory=function(L){"use strict";var s={i18n:{ar:{months:["كانون الثاني","شباط","آذار","نيسان","مايو","حزيران","تموز","آب","أيلول","تشرين الأول","تشرين الثاني","كانون الأول"],dayOfWeekShort:["ن","ث","ع","خ","ج","س","ح"],dayOfWeek:["الأحد","الاثنين","الثلاثاء","الأربعاء","الخميس","الجمعة","السبت","الأحد"]},ro:{months:["Ianuarie","Februarie","Martie","Aprilie","Mai","Iunie","Iulie","August","Septembrie","Octombrie","Noiembrie","Decembrie"],dayOfWeekShort:["Du","Lu","Ma","Mi","Jo","Vi","Sâ"],dayOfWeek:["Duminică","Luni","Marţi","Miercuri","Joi","Vineri","Sâmbătă"]},id:{months:["Januari","Februari","Maret","April","Mei","Juni","Juli","Agustus","September","Oktober","November","Desember"],dayOfWeekShort:["Min","Sen","Sel","Rab","Kam","Jum","Sab"],dayOfWeek:["Minggu","Senin","Selasa","Rabu","Kamis","Jumat","Sabtu"]},is:{months:["Janúar","Febrúar","Mars","Apríl","Maí","Júní","Júlí","Ágúst","September","Október","Nóvember","Desember"],dayOfWeekShort:["Sun","Mán","Þrið","Mið","Fim","Fös","Lau"],dayOfWeek:["Sunnudagur","Mánudagur","Þriðjudagur","Miðvikudagur","Fimmtudagur","Föstudagur","Laugardagur"]},bg:{months:["Януари","Февруари","Март","Април","Май","Юни","Юли","Август","Септември","Октомври","Ноември","Декември"],dayOfWeekShort:["Нд","Пн","Вт","Ср","Чт","Пт","Сб"],dayOfWeek:["Неделя","Понеделник","Вторник","Сряда","Четвъртък","Петък","Събота"]},fa:{months:["فروردین","اردیبهشت","خرداد","تیر","مرداد","شهریور","مهر","آبان","آذر","دی","بهمن","اسفند"],dayOfWeekShort:["یکشنبه","دوشنبه","سه شنبه","چهارشنبه","پنجشنبه","جمعه","شنبه"],dayOfWeek:["یک‌شنبه","دوشنبه","سه‌شنبه","چهارشنبه","پنج‌شنبه","جمعه","شنبه","یک‌شنبه"]},ru:{months:["Январь","Февраль","Март","Апрель","Май","Июнь","Июль","Август","Сентябрь","Октябрь","Ноябрь","Декабрь"],dayOfWeekShort:["Вс","Пн","Вт","Ср","Чт","Пт","Сб"],dayOfWeek:["Воскресенье","Понедельник","Вторник","Среда","Четверг","Пятница","Суббота"]},uk:{months:["Січень","Лютий","Березень","Квітень","Травень","Червень","Липень","Серпень","Вересень","Жовтень","Листопад","Грудень"],dayOfWeekShort:["Ндл","Пнд","Втр","Срд","Чтв","Птн","Сбт"],dayOfWeek:["Неділя","Понеділок","Вівторок","Середа","Четвер","П'ятниця","Субота"]},en:{months:["January","February","March","April","May","June","July","August","September","October","November","December"],dayOfWeekShort:["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],dayOfWeek:["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"]},el:{months:["Ιανουάριος","Φεβρουάριος","Μάρτιος","Απρίλιος","Μάιος","Ιούνιος","Ιούλιος","Αύγουστος","Σεπτέμβριος","Οκτώβριος","Νοέμβριος","Δεκέμβριος"],dayOfWeekShort:["Κυρ","Δευ","Τρι","Τετ","Πεμ","Παρ","Σαβ"],dayOfWeek:["Κυριακή","Δευτέρα","Τρίτη","Τετάρτη","Πέμπτη","Παρασκευή","Σάββατο"]},de:{months:["Januar","Februar","März","April","Mai","Juni","Juli","August","September","Oktober","November","Dezember"],dayOfWeekShort:["So","Mo","Di","Mi","Do","Fr","Sa"],dayOfWeek:["Sonntag","Montag","Dienstag","Mittwoch","Donnerstag","Freitag","Samstag"]},nl:{months:["januari","februari","maart","april","mei","juni","juli","augustus","september","oktober","november","december"],dayOfWeekShort:["zo","ma","di","wo","do","vr","za"],dayOfWeek:["zondag","maandag","dinsdag","woensdag","donderdag","vrijdag","zaterdag"]},tr:{months:["Ocak","Şubat","Mart","Nisan","Mayıs","Haziran","Temmuz","Ağustos","Eylül","Ekim","Kasım","Aralık"],dayOfWeekShort:["Paz","Pts","Sal","Çar","Per","Cum","Cts"],dayOfWeek:["Pazar","Pazartesi","Salı","Çarşamba","Perşembe","Cuma","Cumartesi"]},fr:{months:["Janvier","Février","Mars","Avril","Mai","Juin","Juillet","Août","Septembre","Octobre","Novembre","Décembre"],dayOfWeekShort:["Dim","Lun","Mar","Mer","Jeu","Ven","Sam"],dayOfWeek:["dimanche","lundi","mardi","mercredi","jeudi","vendredi","samedi"]},es:{months:["Enero","Febrero","Marzo","Abril","Mayo","Junio","Julio","Agosto","Septiembre","Octubre","Noviembre","Diciembre"],dayOfWeekShort:["Dom","Lun","Mar","Mié","Jue","Vie","Sáb"],dayOfWeek:["Domingo","Lunes","Martes","Miércoles","Jueves","Viernes","Sábado"]},th:{months:["มกราคม","กุมภาพันธ์","มีนาคม","เมษายน","พฤษภาคม","มิถุนายน","กรกฎาคม","สิงหาคม","กันยายน","ตุลาคม","พฤศจิกายน","ธันวาคม"],dayOfWeekShort:["อา.","จ.","อ.","พ.","พฤ.","ศ.","ส."],dayOfWeek:["อาทิตย์","จันทร์","อังคาร","พุธ","พฤหัส","ศุกร์","เสาร์","อาทิตย์"]},pl:{months:["styczeń","luty","marzec","kwiecień","maj","czerwiec","lipiec","sierpień","wrzesień","październik","listopad","grudzień"],dayOfWeekShort:["nd","pn","wt","śr","cz","pt","sb"],dayOfWeek:["niedziela","poniedziałek","wtorek","środa","czwartek","piątek","sobota"]},pt:{months:["Janeiro","Fevereiro","Março","Abril","Maio","Junho","Julho","Agosto","Setembro","Outubro","Novembro","Dezembro"],dayOfWeekShort:["Dom","Seg","Ter","Qua","Qui","Sex","Sab"],dayOfWeek:["Domingo","Segunda","Terça","Quarta","Quinta","Sexta","Sábado"]},ch:{months:["一月","二月","三月","四月","五月","六月","七月","八月","九月","十月","十一月","十二月"],dayOfWeekShort:["日","一","二","三","四","五","六"]},se:{months:["Januari","Februari","Mars","April","Maj","Juni","Juli","Augusti","September","Oktober","November","December"],dayOfWeekShort:["Sön","Mån","Tis","Ons","Tor","Fre","Lör"]},km:{months:["មករា​","កុម្ភៈ","មិនា​","មេសា​","ឧសភា​","មិថុនា​","កក្កដា​","សីហា​","កញ្ញា​","តុលា​","វិច្ឆិកា","ធ្នូ​"],dayOfWeekShort:["អាទិ​","ច័ន្ទ​","អង្គារ​","ពុធ​","ព្រហ​​","សុក្រ​","សៅរ៍"],dayOfWeek:["អាទិត្យ​","ច័ន្ទ​","អង្គារ​","ពុធ​","ព្រហស្បតិ៍​","សុក្រ​","សៅរ៍"]},kr:{months:["1월","2월","3월","4월","5월","6월","7월","8월","9월","10월","11월","12월"],dayOfWeekShort:["일","월","화","수","목","금","토"],dayOfWeek:["일요일","월요일","화요일","수요일","목요일","금요일","토요일"]},it:{months:["Gennaio","Febbraio","Marzo","Aprile","Maggio","Giugno","Luglio","Agosto","Settembre","Ottobre","Novembre","Dicembre"],dayOfWeekShort:["Dom","Lun","Mar","Mer","Gio","Ven","Sab"],dayOfWeek:["Domenica","Lunedì","Martedì","Mercoledì","Giovedì","Venerdì","Sabato"]},da:{months:["Januar","Februar","Marts","April","Maj","Juni","Juli","August","September","Oktober","November","December"],dayOfWeekShort:["Søn","Man","Tir","Ons","Tor","Fre","Lør"],dayOfWeek:["søndag","mandag","tirsdag","onsdag","torsdag","fredag","lørdag"]},no:{months:["Januar","Februar","Mars","April","Mai","Juni","Juli","August","September","Oktober","November","Desember"],dayOfWeekShort:["Søn","Man","Tir","Ons","Tor","Fre","Lør"],dayOfWeek:["Søndag","Mandag","Tirsdag","Onsdag","Torsdag","Fredag","Lørdag"]},ja:{months:["1月","2月","3月","4月","5月","6月","7月","8月","9月","10月","11月","12月"],dayOfWeekShort:["日","月","火","水","木","金","土"],dayOfWeek:["日曜","月曜","火曜","水曜","木曜","金曜","土曜"]},vi:{months:["Tháng 1","Tháng 2","Tháng 3","Tháng 4","Tháng 5","Tháng 6","Tháng 7","Tháng 8","Tháng 9","Tháng 10","Tháng 11","Tháng 12"],dayOfWeekShort:["CN","T2","T3","T4","T5","T6","T7"],dayOfWeek:["Chủ nhật","Thứ hai","Thứ ba","Thứ tư","Thứ năm","Thứ sáu","Thứ bảy"]},sl:{months:["Januar","Februar","Marec","April","Maj","Junij","Julij","Avgust","September","Oktober","November","December"],dayOfWeekShort:["Ned","Pon","Tor","Sre","Čet","Pet","Sob"],dayOfWeek:["Nedelja","Ponedeljek","Torek","Sreda","Četrtek","Petek","Sobota"]},cs:{months:["Leden","Únor","Březen","Duben","Květen","Červen","Červenec","Srpen","Září","Říjen","Listopad","Prosinec"],dayOfWeekShort:["Ne","Po","Út","St","Čt","Pá","So"]},hu:{months:["Január","Február","Március","Április","Május","Június","Július","Augusztus","Szeptember","Október","November","December"],dayOfWeekShort:["Va","Hé","Ke","Sze","Cs","Pé","Szo"],dayOfWeek:["vasárnap","hétfő","kedd","szerda","csütörtök","péntek","szombat"]},az:{months:["Yanvar","Fevral","Mart","Aprel","May","Iyun","Iyul","Avqust","Sentyabr","Oktyabr","Noyabr","Dekabr"],dayOfWeekShort:["B","Be","Ça","Ç","Ca","C","Ş"],dayOfWeek:["Bazar","Bazar ertəsi","Çərşənbə axşamı","Çərşənbə","Cümə axşamı","Cümə","Şənbə"]},bs:{months:["Januar","Februar","Mart","April","Maj","Jun","Jul","Avgust","Septembar","Oktobar","Novembar","Decembar"],dayOfWeekShort:["Ned","Pon","Uto","Sri","Čet","Pet","Sub"],dayOfWeek:["Nedjelja","Ponedjeljak","Utorak","Srijeda","Četvrtak","Petak","Subota"]},ca:{months:["Gener","Febrer","Març","Abril","Maig","Juny","Juliol","Agost","Setembre","Octubre","Novembre","Desembre"],dayOfWeekShort:["Dg","Dl","Dt","Dc","Dj","Dv","Ds"],dayOfWeek:["Diumenge","Dilluns","Dimarts","Dimecres","Dijous","Divendres","Dissabte"]},"en-GB":{months:["January","February","March","April","May","June","July","August","September","October","November","December"],dayOfWeekShort:["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],dayOfWeek:["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"]},et:{months:["Jaanuar","Veebruar","Märts","Aprill","Mai","Juuni","Juuli","August","September","Oktoober","November","Detsember"],dayOfWeekShort:["P","E","T","K","N","R","L"],dayOfWeek:["Pühapäev","Esmaspäev","Teisipäev","Kolmapäev","Neljapäev","Reede","Laupäev"]},eu:{months:["Urtarrila","Otsaila","Martxoa","Apirila","Maiatza","Ekaina","Uztaila","Abuztua","Iraila","Urria","Azaroa","Abendua"],dayOfWeekShort:["Ig.","Al.","Ar.","Az.","Og.","Or.","La."],dayOfWeek:["Igandea","Astelehena","Asteartea","Asteazkena","Osteguna","Ostirala","Larunbata"]},fi:{months:["Tammikuu","Helmikuu","Maaliskuu","Huhtikuu","Toukokuu","Kesäkuu","Heinäkuu","Elokuu","Syyskuu","Lokakuu","Marraskuu","Joulukuu"],dayOfWeekShort:["Su","Ma","Ti","Ke","To","Pe","La"],dayOfWeek:["sunnuntai","maanantai","tiistai","keskiviikko","torstai","perjantai","lauantai"]},gl:{months:["Xan","Feb","Maz","Abr","Mai","Xun","Xul","Ago","Set","Out","Nov","Dec"],dayOfWeekShort:["Dom","Lun","Mar","Mer","Xov","Ven","Sab"],dayOfWeek:["Domingo","Luns","Martes","Mércores","Xoves","Venres","Sábado"]},hr:{months:["Siječanj","Veljača","Ožujak","Travanj","Svibanj","Lipanj","Srpanj","Kolovoz","Rujan","Listopad","Studeni","Prosinac"],dayOfWeekShort:["Ned","Pon","Uto","Sri","Čet","Pet","Sub"],dayOfWeek:["Nedjelja","Ponedjeljak","Utorak","Srijeda","Četvrtak","Petak","Subota"]},ko:{months:["1월","2월","3월","4월","5월","6월","7월","8월","9월","10월","11월","12월"],dayOfWeekShort:["일","월","화","수","목","금","토"],dayOfWeek:["일요일","월요일","화요일","수요일","목요일","금요일","토요일"]},lt:{months:["Sausio","Vasario","Kovo","Balandžio","Gegužės","Birželio","Liepos","Rugpjūčio","Rugsėjo","Spalio","Lapkričio","Gruodžio"],dayOfWeekShort:["Sek","Pir","Ant","Tre","Ket","Pen","Šeš"],dayOfWeek:["Sekmadienis","Pirmadienis","Antradienis","Trečiadienis","Ketvirtadienis","Penktadienis","Šeštadienis"]},lv:{months:["Janvāris","Februāris","Marts","Aprīlis ","Maijs","Jūnijs","Jūlijs","Augusts","Septembris","Oktobris","Novembris","Decembris"],dayOfWeekShort:["Sv","Pr","Ot","Tr","Ct","Pk","St"],dayOfWeek:["Svētdiena","Pirmdiena","Otrdiena","Trešdiena","Ceturtdiena","Piektdiena","Sestdiena"]},mk:{months:["јануари","февруари","март","април","мај","јуни","јули","август","септември","октомври","ноември","декември"],dayOfWeekShort:["нед","пон","вто","сре","чет","пет","саб"],dayOfWeek:["Недела","Понеделник","Вторник","Среда","Четврток","Петок","Сабота"]},mn:{months:["1-р сар","2-р сар","3-р сар","4-р сар","5-р сар","6-р сар","7-р сар","8-р сар","9-р сар","10-р сар","11-р сар","12-р сар"],dayOfWeekShort:["Дав","Мяг","Лха","Пүр","Бсн","Бям","Ням"],dayOfWeek:["Даваа","Мягмар","Лхагва","Пүрэв","Баасан","Бямба","Ням"]},"pt-BR":{months:["Janeiro","Fevereiro","Março","Abril","Maio","Junho","Julho","Agosto","Setembro","Outubro","Novembro","Dezembro"],dayOfWeekShort:["Dom","Seg","Ter","Qua","Qui","Sex","Sáb"],dayOfWeek:["Domingo","Segunda","Terça","Quarta","Quinta","Sexta","Sábado"]},sk:{months:["Január","Február","Marec","Apríl","Máj","Jún","Júl","August","September","Október","November","December"],dayOfWeekShort:["Ne","Po","Ut","St","Št","Pi","So"],dayOfWeek:["Nedeľa","Pondelok","Utorok","Streda","Štvrtok","Piatok","Sobota"]},sq:{months:["Janar","Shkurt","Mars","Prill","Maj","Qershor","Korrik","Gusht","Shtator","Tetor","Nëntor","Dhjetor"],dayOfWeekShort:["Die","Hën","Mar","Mër","Enj","Pre","Shtu"],dayOfWeek:["E Diel","E Hënë","E Martē","E Mërkurë","E Enjte","E Premte","E Shtunë"]},"sr-YU":{months:["Januar","Februar","Mart","April","Maj","Jun","Jul","Avgust","Septembar","Oktobar","Novembar","Decembar"],dayOfWeekShort:["Ned","Pon","Uto","Sre","čet","Pet","Sub"],dayOfWeek:["Nedelja","Ponedeljak","Utorak","Sreda","Četvrtak","Petak","Subota"]},sr:{months:["јануар","фебруар","март","април","мај","јун","јул","август","септембар","октобар","новембар","децембар"],dayOfWeekShort:["нед","пон","уто","сре","чет","пет","суб"],dayOfWeek:["Недеља","Понедељак","Уторак","Среда","Четвртак","Петак","Субота"]},sv:{months:["Januari","Februari","Mars","April","Maj","Juni","Juli","Augusti","September","Oktober","November","December"],dayOfWeekShort:["Sön","Mån","Tis","Ons","Tor","Fre","Lör"],dayOfWeek:["Söndag","Måndag","Tisdag","Onsdag","Torsdag","Fredag","Lördag"]},"zh-TW":{months:["一月","二月","三月","四月","五月","六月","七月","八月","九月","十月","十一月","十二月"],dayOfWeekShort:["日","一","二","三","四","五","六"],dayOfWeek:["星期日","星期一","星期二","星期三","星期四","星期五","星期六"]},zh:{months:["一月","二月","三月","四月","五月","六月","七月","八月","九月","十月","十一月","十二月"],dayOfWeekShort:["日","一","二","三","四","五","六"],dayOfWeek:["星期日","星期一","星期二","星期三","星期四","星期五","星期六"]},ug:{months:["1-ئاي","2-ئاي","3-ئاي","4-ئاي","5-ئاي","6-ئاي","7-ئاي","8-ئاي","9-ئاي","10-ئاي","11-ئاي","12-ئاي"],dayOfWeek:["يەكشەنبە","دۈشەنبە","سەيشەنبە","چارشەنبە","پەيشەنبە","جۈمە","شەنبە"]},he:{months:["ינואר","פברואר","מרץ","אפריל","מאי","יוני","יולי","אוגוסט","ספטמבר","אוקטובר","נובמבר","דצמבר"],dayOfWeekShort:["א'","ב'","ג'","ד'","ה'","ו'","שבת"],dayOfWeek:["ראשון","שני","שלישי","רביעי","חמישי","שישי","שבת","ראשון"]},hy:{months:["Հունվար","Փետրվար","Մարտ","Ապրիլ","Մայիս","Հունիս","Հուլիս","Օգոստոս","Սեպտեմբեր","Հոկտեմբեր","Նոյեմբեր","Դեկտեմբեր"],dayOfWeekShort:["Կի","Երկ","Երք","Չոր","Հնգ","Ուրբ","Շբթ"],dayOfWeek:["Կիրակի","Երկուշաբթի","Երեքշաբթի","Չորեքշաբթի","Հինգշաբթի","Ուրբաթ","Շաբաթ"]},kg:{months:["Үчтүн айы","Бирдин айы","Жалган Куран","Чын Куран","Бугу","Кулжа","Теке","Баш Оона","Аяк Оона","Тогуздун айы","Жетинин айы","Бештин айы"],dayOfWeekShort:["Жек","Дүй","Шей","Шар","Бей","Жум","Ише"],dayOfWeek:["Жекшемб","Дүйшөмб","Шейшемб","Шаршемб","Бейшемби","Жума","Ишенб"]},rm:{months:["Schaner","Favrer","Mars","Avrigl","Matg","Zercladur","Fanadur","Avust","Settember","October","November","December"],dayOfWeekShort:["Du","Gli","Ma","Me","Gie","Ve","So"],dayOfWeek:["Dumengia","Glindesdi","Mardi","Mesemna","Gievgia","Venderdi","Sonda"]},ka:{months:["იანვარი","თებერვალი","მარტი","აპრილი","მაისი","ივნისი","ივლისი","აგვისტო","სექტემბერი","ოქტომბერი","ნოემბერი","დეკემბერი"],dayOfWeekShort:["კვ","ორშ","სამშ","ოთხ","ხუთ","პარ","შაბ"],dayOfWeek:["კვირა","ორშაბათი","სამშაბათი","ოთხშაბათი","ხუთშაბათი","პარასკევი","შაბათი"]}},ownerDocument:document,contentWindow:window,value:"",rtl:!1,format:"Y/m/d H:i",formatTime:"H:i",formatDate:"Y/m/d",startDate:!1,step:60,monthChangeSpinner:!0,closeOnDateSelect:!1,closeOnTimeSelect:!0,closeOnWithoutClick:!0,closeOnInputClick:!0,openOnFocus:!0,timepicker:!0,datepicker:!0,weeks:!1,defaultTime:!1,defaultDate:!1,minDate:!1,maxDate:!1,minTime:!1,maxTime:!1,minDateTime:!1,maxDateTime:!1,allowTimes:[],opened:!1,initTime:!0,inline:!1,theme:"",touchMovedThreshold:5,onSelectDate:function(){},onSelectTime:function(){},onChangeMonth:function(){},onGetWeekOfYear:function(){},onChangeYear:function(){},onChangeDateTime:function(){},onShow:function(){},onClose:function(){},onGenerate:function(){},withoutCopyright:!0,inverseButton:!1,hours12:!1,next:"xdsoft_next",prev:"xdsoft_prev",dayOfWeekStart:0,parentID:"body",timeHeightInTimePicker:25,timepickerScrollbar:!0,todayButton:!0,prevButton:!0,nextButton:!0,defaultSelect:!0,scrollMonth:!0,scrollTime:!0,scrollInput:!0,lazyInit:!1,mask:!1,validateOnBlur:!0,allowBlank:!0,yearStart:1950,yearEnd:2050,monthStart:0,monthEnd:11,style:"",id:"",fixed:!1,roundTime:"round",className:"",weekends:[],highlightedDates:[],highlightedPeriods:[],allowDates:[],allowDateRe:null,disabledDates:[],disabledWeekDays:[],yearOffset:0,beforeShowDay:null,enterLikeTab:!0,showApplyButton:!1,insideParent:!1},E=null,n=null,R="en",a={meridiem:["AM","PM"]},r=function(){var e=s.i18n[R],t={days:e.dayOfWeek,daysShort:e.dayOfWeekShort,months:e.months,monthsShort:L.map(e.months,function(e){return e.substring(0,3)})};"function"==typeof DateFormatter&&(E=n=new DateFormatter({dateSettings:L.extend({},a,t)}))},o={moment:{default_options:{format:"YYYY/MM/DD HH:mm",formatDate:"YYYY/MM/DD",formatTime:"HH:mm"},formatter:{parseDate:function(e,t){if(i(t))return n.parseDate(e,t);var a=moment(e,t);return!!a.isValid()&&a.toDate()},formatDate:function(e,t){return i(t)?n.formatDate(e,t):moment(e).format(t)},formatMask:function(e){return e.replace(/Y{4}/g,"9999").replace(/Y{2}/g,"99").replace(/M{2}/g,"19").replace(/D{2}/g,"39").replace(/H{2}/g,"29").replace(/m{2}/g,"59").replace(/s{2}/g,"59")}}}};L.datetimepicker={setLocale:function(e){var t=s.i18n[e]?e:"en";R!==t&&(R=t,r())},setDateFormatter:function(e){if("string"==typeof e&&o.hasOwnProperty(e)){var t=o[e];L.extend(s,t.default_options),E=t.formatter}else E=e}};var t={RFC_2822:"D, d M Y H:i:s O",ATOM:"Y-m-dTH:i:sP",ISO_8601:"Y-m-dTH:i:sO",RFC_822:"D, d M y H:i:s O",RFC_850:"l, d-M-y H:i:s T",RFC_1036:"D, d M y H:i:s O",RFC_1123:"D, d M Y H:i:s O",RSS:"D, d M Y H:i:s O",W3C:"Y-m-dTH:i:sP"},i=function(e){return-1!==Object.values(t).indexOf(e)};function m(e,t,a){this.date=e,this.desc=t,this.style=a}L.extend(L.datetimepicker,t),r(),window.getComputedStyle||(window.getComputedStyle=function(a){return this.el=a,this.getPropertyValue=function(e){var t=/(-([a-z]))/g;return"float"===e&&(e="styleFloat"),t.test(e)&&(e=e.replace(t,function(e,t,a){return a.toUpperCase()})),a.currentStyle[e]||null},this}),Array.prototype.indexOf||(Array.prototype.indexOf=function(e,t){var a,n;for(a=t||0,n=this.length;a<n;a+=1)if(this[a]===e)return a;return-1}),Date.prototype.countDaysInMonth=function(){return new Date(this.getFullYear(),this.getMonth()+1,0).getDate()},L.fn.xdsoftScroller=function(p,D){return this.each(function(){var o,i,s,d,u,l=L(this),a=function(e){var t,a={x:0,y:0};return"touchstart"===e.type||"touchmove"===e.type||"touchend"===e.type||"touchcancel"===e.type?(t=e.originalEvent.touches[0]||e.originalEvent.changedTouches[0],a.x=t.clientX,a.y=t.clientY):"mousedown"!==e.type&&"mouseup"!==e.type&&"mousemove"!==e.type&&"mouseover"!==e.type&&"mouseout"!==e.type&&"mouseenter"!==e.type&&"mouseleave"!==e.type||(a.x=e.clientX,a.y=e.clientY),a},f=100,n=!1,r=0,c=0,m=0,t=!1,h=0,g=function(){};"hide"!==D?(L(this).hasClass("xdsoft_scroller_box")||(o=l.children().eq(0),i=l[0].clientHeight,s=o[0].offsetHeight,d=L('<div class="xdsoft_scrollbar"></div>'),u=L('<div class="xdsoft_scroller"></div>'),d.append(u),l.addClass("xdsoft_scroller_box").append(d),g=function(e){var t=a(e).y-r+h;t<0&&(t=0),t+u[0].offsetHeight>m&&(t=m-u[0].offsetHeight),l.trigger("scroll_element.xdsoft_scroller",[f?t/f:0])},u.on("touchstart.xdsoft_scroller mousedown.xdsoft_scroller",function(e){i||l.trigger("resize_scroll.xdsoft_scroller",[D]),r=a(e).y,h=parseInt(u.css("margin-top"),10),m=d[0].offsetHeight,"mousedown"===e.type||"touchstart"===e.type?(p.ownerDocument&&L(p.ownerDocument.body).addClass("xdsoft_noselect"),L([p.ownerDocument.body,p.contentWindow]).on("touchend mouseup.xdsoft_scroller",function e(){L([p.ownerDocument.body,p.contentWindow]).off("touchend mouseup.xdsoft_scroller",e).off("mousemove.xdsoft_scroller",g).removeClass("xdsoft_noselect")}),L(p.ownerDocument.body).on("mousemove.xdsoft_scroller",g)):(t=!0,e.stopPropagation(),e.preventDefault())}).on("touchmove",function(e){t&&(e.preventDefault(),g(e))}).on("touchend touchcancel",function(){t=!1,h=0}),l.on("scroll_element.xdsoft_scroller",function(e,t){i||l.trigger("resize_scroll.xdsoft_scroller",[t,!0]),t=1<t?1:t<0||isNaN(t)?0:t,u.css("margin-top",f*t),setTimeout(function(){o.css("marginTop",-parseInt((o[0].offsetHeight-i)*t,10))},10)}).on("resize_scroll.xdsoft_scroller",function(e,t,a){var n,r;i=l[0].clientHeight,s=o[0].offsetHeight,r=(n=i/s)*d[0].offsetHeight,1<n?u.hide():(u.show(),u.css("height",parseInt(10<r?r:10,10)),f=d[0].offsetHeight-u[0].offsetHeight,!0!==a&&l.trigger("scroll_element.xdsoft_scroller",[t||Math.abs(parseInt(o.css("marginTop"),10))/(s-i)]))}),l.on("mousewheel",function(e){var t=Math.abs(parseInt(o.css("marginTop"),10));return(t-=20*e.deltaY)<0&&(t=0),l.trigger("scroll_element.xdsoft_scroller",[t/(s-i)]),e.stopPropagation(),!1}),l.on("touchstart",function(e){n=a(e),c=Math.abs(parseInt(o.css("marginTop"),10))}),l.on("touchmove",function(e){if(n){e.preventDefault();var t=a(e);l.trigger("scroll_element.xdsoft_scroller",[(c-(t.y-n.y))/(s-i)])}}),l.on("touchend touchcancel",function(){n=!1,c=0})),l.trigger("resize_scroll.xdsoft_scroller",[D])):l.find(".xdsoft_scrollbar").hide()})},L.fn.datetimepicker=function(H,a){var n,r,o=this,p=17,D=13,y=27,v=37,b=38,k=39,x=40,T=9,S=116,M=65,w=67,j=86,J=90,z=89,I=!1,N=L.isPlainObject(H)||!H?L.extend(!0,{},s,H):L.extend(!0,{},s),i=0;return n=function(O){var t,n,a,r,W,h,_=L('<div class="xdsoft_datetimepicker xdsoft_noselect"></div>'),e=L('<div class="xdsoft_copyright"><a target="_blank" href="http://xdsoft.net/jqplugins/datetimepicker/">xdsoft.net</a></div>'),g=L('<div class="xdsoft_datepicker active"></div>'),F=L('<div class="xdsoft_monthpicker"><button type="button" class="xdsoft_prev"></button><button type="button" class="xdsoft_today_button"></button><div class="xdsoft_label xdsoft_month"><span></span><i></i></div><div class="xdsoft_label xdsoft_year"><span></span><i></i></div><button type="button" class="xdsoft_next"></button></div>'),C=L('<div class="xdsoft_calendar"></div>'),o=L('<div class="xdsoft_timepicker active"><button type="button" class="xdsoft_prev"></button><div class="xdsoft_time_box"></div><button type="button" class="xdsoft_next"></button></div>'),u=o.find(".xdsoft_time_box").eq(0),P=L('<div class="xdsoft_time_variant"></div>'),i=L('<button type="button" class="xdsoft_save_selected blue-gradient-button">Save Selected</button>'),Y=L('<div class="xdsoft_select xdsoft_monthselect"><div></div></div>'),A=L('<div class="xdsoft_select xdsoft_yearselect"><div></div></div>'),s=!1,d=0;N.id&&_.attr("id",N.id),N.style&&_.attr("style",N.style),N.weeks&&_.addClass("xdsoft_showweeks"),N.rtl&&_.addClass("xdsoft_rtl"),_.addClass("xdsoft_"+N.theme),_.addClass(N.className),F.find(".xdsoft_month span").after(Y),F.find(".xdsoft_year span").after(A),F.find(".xdsoft_month,.xdsoft_year").on("touchstart mousedown.xdsoft",function(e){var t,a,n=L(this).find(".xdsoft_select").eq(0),r=0,o=0,i=n.is(":visible");for(F.find(".xdsoft_select").hide(),W.currentTime&&(r=W.currentTime[L(this).hasClass("xdsoft_month")?"getMonth":"getFullYear"]()),n[i?"hide":"show"](),t=n.find("div.xdsoft_option"),a=0;a<t.length&&t.eq(a).data("value")!==r;a+=1)o+=t[0].offsetHeight;return n.xdsoftScroller(N,o/(n.children()[0].offsetHeight-n[0].clientHeight)),e.stopPropagation(),!1});var l=function(e){var t=e.originalEvent,a=t.touches?t.touches[0]:t;this.touchStartPosition=this.touchStartPosition||a;var n=Math.abs(this.touchStartPosition.clientX-a.clientX),r=Math.abs(this.touchStartPosition.clientY-a.clientY);Math.sqrt(n*n+r*r)>N.touchMovedThreshold&&(this.touchMoved=!0)};function f(){var e,t=!1;return N.startDate?t=W.strToDate(N.startDate):(t=N.value||(O&&O.val&&O.val()?O.val():""))?(t=W.strToDateTime(t),N.yearOffset&&(t=new Date(t.getFullYear()-N.yearOffset,t.getMonth(),t.getDate(),t.getHours(),t.getMinutes(),t.getSeconds(),t.getMilliseconds()))):N.defaultDate&&(t=W.strToDateTime(N.defaultDate),N.defaultTime&&(e=W.strtotime(N.defaultTime),t.setHours(e.getHours()),t.setMinutes(e.getMinutes()))),t&&W.isValidDate(t)?_.data("changed",!0):t="",t||0}function c(m){var h=function(e,t){var a=e.replace(/([\[\]\/\{\}\(\)\-\.\+]{1})/g,"\\$1").replace(/_/g,"{digit+}").replace(/([0-9]{1})/g,"{digit$1}").replace(/\{digit([0-9]{1})\}/g,"[0-$1_]{1}").replace(/\{digit[\+]\}/g,"[0-9_]{1}");return new RegExp(a).test(t)},g=function(e,t){if(!(e="string"==typeof e||e instanceof String?m.ownerDocument.getElementById(e):e))return!1;if(e.createTextRange){var a=e.createTextRange();return a.collapse(!0),a.moveEnd("character",t),a.moveStart("character",t),a.select(),!0}return!!e.setSelectionRange&&(e.setSelectionRange(t,t),!0)};m.mask&&O.off("keydown.xdsoft"),!0===m.mask&&(E.formatMask?m.mask=E.formatMask(m.format):m.mask=m.format.replace(/Y/g,"9999").replace(/F/g,"9999").replace(/m/g,"19").replace(/d/g,"39").replace(/H/g,"29").replace(/i/g,"59").replace(/s/g,"59")),"string"===L.type(m.mask)&&(h(m.mask,O.val())||(O.val(m.mask.replace(/[0-9]/g,"_")),g(O[0],0)),O.on("paste.xdsoft",function(e){var t=(e.clipboardData||e.originalEvent.clipboardData||window.clipboardData).getData("text"),a=this.value,n=this.selectionStart;return a=a.substr(0,n)+t+a.substr(n+t.length),n+=t.length,h(m.mask,a)?(this.value=a,g(this,n)):""===L.trim(a)?this.value=m.mask.replace(/[0-9]/g,"_"):O.trigger("error_input.xdsoft"),e.preventDefault(),!1}),O.on("keydown.xdsoft",function(e){var t,a=this.value,n=e.which,r=this.selectionStart,o=this.selectionEnd,i=r!==o;if(48<=n&&n<=57||96<=n&&n<=105||8===n||46===n){for(t=8===n||46===n?"_":String.fromCharCode(96<=n&&n<=105?n-48:n),8===n&&r&&!i&&(r-=1);;){var s=m.mask.substr(r,1),d=r<m.mask.length,u=0<r;if(!(/[^0-9_]/.test(s)&&d&&u))break;r+=8!==n||i?1:-1}if(e.metaKey&&(i=!(r=0)),i){var l=o-r,f=m.mask.replace(/[0-9]/g,"_"),c=f.substr(r,l).substr(1);a=a.substr(0,r)+(t+c)+a.substr(r+l)}else{a=a.substr(0,r)+t+a.substr(r+1)}if(""===L.trim(a))a=f;else if(r===m.mask.length)return e.preventDefault(),!1;for(r+=8===n?0:1;/[^0-9_]/.test(m.mask.substr(r,1))&&r<m.mask.length&&0<r;)r+=8===n?0:1;h(m.mask,a)?(this.value=a,g(this,r)):""===L.trim(a)?this.value=m.mask.replace(/[0-9]/g,"_"):O.trigger("error_input.xdsoft")}else if(-1!==[M,w,j,J,z].indexOf(n)&&I||-1!==[y,b,x,v,k,S,p,T,D].indexOf(n))return!0;return e.preventDefault(),!1}))}F.find(".xdsoft_select").xdsoftScroller(N).on("touchstart mousedown.xdsoft",function(e){var t=e.originalEvent;this.touchMoved=!1,this.touchStartPosition=t.touches?t.touches[0]:t,e.stopPropagation(),e.preventDefault()}).on("touchmove",".xdsoft_option",l).on("touchend mousedown.xdsoft",".xdsoft_option",function(){if(!this.touchMoved){void 0!==W.currentTime&&null!==W.currentTime||(W.currentTime=W.now());var e=W.currentTime.getFullYear();W&&W.currentTime&&W.currentTime[L(this).parent().parent().hasClass("xdsoft_monthselect")?"setMonth":"setFullYear"](L(this).data("value")),L(this).parent().parent().hide(),_.trigger("xchange.xdsoft"),N.onChangeMonth&&L.isFunction(N.onChangeMonth)&&N.onChangeMonth.call(_,W.currentTime,_.data("input")),e!==W.currentTime.getFullYear()&&L.isFunction(N.onChangeYear)&&N.onChangeYear.call(_,W.currentTime,_.data("input"))}}),_.getValue=function(){return W.getCurrentTime()},_.setOptions=function(e){var l={};N=L.extend(!0,{},N,e),e.allowTimes&&L.isArray(e.allowTimes)&&e.allowTimes.length&&(N.allowTimes=L.extend(!0,[],e.allowTimes)),e.weekends&&L.isArray(e.weekends)&&e.weekends.length&&(N.weekends=L.extend(!0,[],e.weekends)),e.allowDates&&L.isArray(e.allowDates)&&e.allowDates.length&&(N.allowDates=L.extend(!0,[],e.allowDates)),e.allowDateRe&&"[object String]"===Object.prototype.toString.call(e.allowDateRe)&&(N.allowDateRe=new RegExp(e.allowDateRe)),e.highlightedDates&&L.isArray(e.highlightedDates)&&e.highlightedDates.length&&(L.each(e.highlightedDates,function(e,t){var a,n=L.map(t.split(","),L.trim),r=new m(E.parseDate(n[0],N.formatDate),n[1],n[2]),o=E.formatDate(r.date,N.formatDate);void 0!==l[o]?(a=l[o].desc)&&a.length&&r.desc&&r.desc.length&&(l[o].desc=a+"\n"+r.desc):l[o]=r}),N.highlightedDates=L.extend(!0,[],l)),e.highlightedPeriods&&L.isArray(e.highlightedPeriods)&&e.highlightedPeriods.length&&(l=L.extend(!0,[],N.highlightedDates),L.each(e.highlightedPeriods,function(e,t){var a,n,r,o,i,s,d;if(L.isArray(t))a=t[0],n=t[1],r=t[2],d=t[3];else{var u=L.map(t.split(","),L.trim);a=E.parseDate(u[0],N.formatDate),n=E.parseDate(u[1],N.formatDate),r=u[2],d=u[3]}for(;a<=n;)o=new m(a,r,d),i=E.formatDate(a,N.formatDate),a.setDate(a.getDate()+1),void 0!==l[i]?(s=l[i].desc)&&s.length&&o.desc&&o.desc.length&&(l[i].desc=s+"\n"+o.desc):l[i]=o}),N.highlightedDates=L.extend(!0,[],l)),e.disabledDates&&L.isArray(e.disabledDates)&&e.disabledDates.length&&(N.disabledDates=L.extend(!0,[],e.disabledDates)),e.disabledWeekDays&&L.isArray(e.disabledWeekDays)&&e.disabledWeekDays.length&&(N.disabledWeekDays=L.extend(!0,[],e.disabledWeekDays)),!N.open&&!N.opened||N.inline||O.trigger("open.xdsoft"),N.inline&&(s=!0,_.addClass("xdsoft_inline"),O.after(_).hide()),N.inverseButton&&(N.next="xdsoft_prev",N.prev="xdsoft_next"),N.datepicker?g.addClass("active"):g.removeClass("active"),N.timepicker?o.addClass("active"):o.removeClass("active"),N.value&&(W.setCurrentTime(N.value),O&&O.val&&O.val(W.str)),isNaN(N.dayOfWeekStart)?N.dayOfWeekStart=0:N.dayOfWeekStart=parseInt(N.dayOfWeekStart,10)%7,N.timepickerScrollbar||u.xdsoftScroller(N,"hide"),N.minDate&&/^[\+\-](.*)$/.test(N.minDate)&&(N.minDate=E.formatDate(W.strToDateTime(N.minDate),N.formatDate)),N.maxDate&&/^[\+\-](.*)$/.test(N.maxDate)&&(N.maxDate=E.formatDate(W.strToDateTime(N.maxDate),N.formatDate)),N.minDateTime&&/^\+(.*)$/.test(N.minDateTime)&&(N.minDateTime=W.strToDateTime(N.minDateTime).dateFormat(N.formatDate)),N.maxDateTime&&/^\+(.*)$/.test(N.maxDateTime)&&(N.maxDateTime=W.strToDateTime(N.maxDateTime).dateFormat(N.formatDate)),i.toggle(N.showApplyButton),F.find(".xdsoft_today_button").css("visibility",N.todayButton?"visible":"hidden"),F.find("."+N.prev).css("visibility",N.prevButton?"visible":"hidden"),F.find("."+N.next).css("visibility",N.nextButton?"visible":"hidden"),c(N),N.validateOnBlur&&O.off("blur.xdsoft").on("blur.xdsoft",function(){if(N.allowBlank&&(!L.trim(L(this).val()).length||"string"==typeof N.mask&&L.trim(L(this).val())===N.mask.replace(/[0-9]/g,"_")))L(this).val(null),_.data("xdsoft_datetime").empty();else{var e=E.parseDate(L(this).val(),N.format);if(e)L(this).val(E.formatDate(e,N.format));else{var t=+[L(this).val()[0],L(this).val()[1]].join(""),a=+[L(this).val()[2],L(this).val()[3]].join("");!N.datepicker&&N.timepicker&&0<=t&&t<24&&0<=a&&a<60?L(this).val([t,a].map(function(e){return 9<e?e:"0"+e}).join(":")):L(this).val(E.formatDate(W.now(),N.format))}_.data("xdsoft_datetime").setCurrentTime(L(this).val())}_.trigger("changedatetime.xdsoft"),_.trigger("close.xdsoft")}),N.dayOfWeekStartPrev=0===N.dayOfWeekStart?6:N.dayOfWeekStart-1,_.trigger("xchange.xdsoft").trigger("afterOpen.xdsoft")},_.data("options",N).on("touchstart mousedown.xdsoft",function(e){return e.stopPropagation(),e.preventDefault(),A.hide(),Y.hide(),!1}),u.append(P),u.xdsoftScroller(N),_.on("afterOpen.xdsoft",function(){u.xdsoftScroller(N)}),_.append(g).append(o),!0!==N.withoutCopyright&&_.append(e),g.append(F).append(C).append(i),N.insideParent?L(O).parent().append(_):L(N.parentID).append(_),W=new function(){var r=this;r.now=function(e){var t,a,n=new Date;return!e&&N.defaultDate&&(t=r.strToDateTime(N.defaultDate),n.setFullYear(t.getFullYear()),n.setMonth(t.getMonth()),n.setDate(t.getDate())),n.setFullYear(n.getFullYear()),!e&&N.defaultTime&&(a=r.strtotime(N.defaultTime),n.setHours(a.getHours()),n.setMinutes(a.getMinutes()),n.setSeconds(a.getSeconds()),n.setMilliseconds(a.getMilliseconds())),n},r.isValidDate=function(e){return"[object Date]"===Object.prototype.toString.call(e)&&!isNaN(e.getTime())},r.setCurrentTime=function(e,t){"string"==typeof e?r.currentTime=r.strToDateTime(e):r.isValidDate(e)?r.currentTime=e:e||t||!N.allowBlank||N.inline?r.currentTime=r.now():r.currentTime=null,_.trigger("xchange.xdsoft")},r.empty=function(){r.currentTime=null},r.getCurrentTime=function(){return r.currentTime},r.nextMonth=function(){void 0!==r.currentTime&&null!==r.currentTime||(r.currentTime=r.now());var e,t=r.currentTime.getMonth()+1;return 12===t&&(r.currentTime.setFullYear(r.currentTime.getFullYear()+1),t=0),e=r.currentTime.getFullYear(),r.currentTime.setDate(Math.min(new Date(r.currentTime.getFullYear(),t+1,0).getDate(),r.currentTime.getDate())),r.currentTime.setMonth(t),N.onChangeMonth&&L.isFunction(N.onChangeMonth)&&N.onChangeMonth.call(_,W.currentTime,_.data("input")),e!==r.currentTime.getFullYear()&&L.isFunction(N.onChangeYear)&&N.onChangeYear.call(_,W.currentTime,_.data("input")),_.trigger("xchange.xdsoft"),t},r.prevMonth=function(){void 0!==r.currentTime&&null!==r.currentTime||(r.currentTime=r.now());var e=r.currentTime.getMonth()-1;return-1===e&&(r.currentTime.setFullYear(r.currentTime.getFullYear()-1),e=11),r.currentTime.setDate(Math.min(new Date(r.currentTime.getFullYear(),e+1,0).getDate(),r.currentTime.getDate())),r.currentTime.setMonth(e),N.onChangeMonth&&L.isFunction(N.onChangeMonth)&&N.onChangeMonth.call(_,W.currentTime,_.data("input")),_.trigger("xchange.xdsoft"),e},r.getWeekOfYear=function(e){if(N.onGetWeekOfYear&&L.isFunction(N.onGetWeekOfYear)){var t=N.onGetWeekOfYear.call(_,e);if(void 0!==t)return t}var a=new Date(e.getFullYear(),0,1);return 4!==a.getDay()&&a.setMonth(0,1+(4-a.getDay()+7)%7),Math.ceil(((e-a)/864e5+a.getDay()+1)/7)},r.strToDateTime=function(e){var t,a,n=[];return e&&e instanceof Date&&r.isValidDate(e)?e:((n=/^([+-]{1})(.*)$/.exec(e))&&(n[2]=E.parseDate(n[2],N.formatDate)),a=n&&n[2]?(t=n[2].getTime()-6e4*n[2].getTimezoneOffset(),new Date(r.now(!0).getTime()+parseInt(n[1]+"1",10)*t)):e?E.parseDate(e,N.format):r.now(),r.isValidDate(a)||(a=r.now()),a)},r.strToDate=function(e){if(e&&e instanceof Date&&r.isValidDate(e))return e;var t=e?E.parseDate(e,N.formatDate):r.now(!0);return r.isValidDate(t)||(t=r.now(!0)),t},r.strtotime=function(e){if(e&&e instanceof Date&&r.isValidDate(e))return e;var t=e?E.parseDate(e,N.formatTime):r.now(!0);return r.isValidDate(t)||(t=r.now(!0)),t},r.str=function(){var e=N.format;return N.yearOffset&&(e=(e=e.replace("Y",r.currentTime.getFullYear()+N.yearOffset)).replace("y",String(r.currentTime.getFullYear()+N.yearOffset).substring(2,4))),E.formatDate(r.currentTime,e)},r.currentTime=this.now()},i.on("touchend click",function(e){e.preventDefault(),_.data("changed",!0),W.setCurrentTime(f()),O.val(W.str()),_.trigger("close.xdsoft")}),F.find(".xdsoft_today_button").on("touchend mousedown.xdsoft",function(){_.data("changed",!0),W.setCurrentTime(0,!0),_.trigger("afterOpen.xdsoft")}).on("dblclick.xdsoft",function(){var e,t,a=W.getCurrentTime();a=new Date(a.getFullYear(),a.getMonth(),a.getDate()),e=W.strToDate(N.minDate),a<(e=new Date(e.getFullYear(),e.getMonth(),e.getDate()))||(t=W.strToDate(N.maxDate),(t=new Date(t.getFullYear(),t.getMonth(),t.getDate()))<a||(O.val(W.str()),O.trigger("change"),_.trigger("close.xdsoft")))}),F.find(".xdsoft_prev,.xdsoft_next").on("touchend mousedown.xdsoft",function(){var a=L(this),n=0,r=!1;!function e(t){a.hasClass(N.next)?W.nextMonth():a.hasClass(N.prev)&&W.prevMonth(),N.monthChangeSpinner&&(r||(n=setTimeout(e,t||100)))}(500),L([N.ownerDocument.body,N.contentWindow]).on("touchend mouseup.xdsoft",function e(){clearTimeout(n),r=!0,L([N.ownerDocument.body,N.contentWindow]).off("touchend mouseup.xdsoft",e)})}),o.find(".xdsoft_prev,.xdsoft_next").on("touchend mousedown.xdsoft",function(){var o=L(this),i=0,s=!1,d=110;!function e(t){var a=u[0].clientHeight,n=P[0].offsetHeight,r=Math.abs(parseInt(P.css("marginTop"),10));o.hasClass(N.next)&&n-a-N.timeHeightInTimePicker>=r?P.css("marginTop","-"+(r+N.timeHeightInTimePicker)+"px"):o.hasClass(N.prev)&&0<=r-N.timeHeightInTimePicker&&P.css("marginTop","-"+(r-N.timeHeightInTimePicker)+"px"),u.trigger("scroll_element.xdsoft_scroller",[Math.abs(parseInt(P[0].style.marginTop,10)/(n-a))]),d=10<d?10:d-10,s||(i=setTimeout(e,t||d))}(500),L([N.ownerDocument.body,N.contentWindow]).on("touchend mouseup.xdsoft",function e(){clearTimeout(i),s=!0,L([N.ownerDocument.body,N.contentWindow]).off("touchend mouseup.xdsoft",e)})}),t=0,_.on("xchange.xdsoft",function(e){clearTimeout(t),t=setTimeout(function(){void 0!==W.currentTime&&null!==W.currentTime||(W.currentTime=W.now());for(var e,t,a,n,r,o,i,s,d,u,l="",f=new Date(W.currentTime.getFullYear(),W.currentTime.getMonth(),1,12,0,0),c=0,m=W.now(),h=!1,g=!1,p=!1,D=!1,y=[],v=!0,b="";f.getDay()!==N.dayOfWeekStart;)f.setDate(f.getDate()-1);for(l+="<table><thead><tr>",N.weeks&&(l+="<th></th>"),e=0;e<7;e+=1)l+="<th>"+N.i18n[R].dayOfWeekShort[(e+N.dayOfWeekStart)%7]+"</th>";for(l+="</tr></thead>",l+="<tbody>",!1!==N.maxDate&&(h=W.strToDate(N.maxDate),h=new Date(h.getFullYear(),h.getMonth(),h.getDate(),23,59,59,999)),!1!==N.minDate&&(g=W.strToDate(N.minDate),g=new Date(g.getFullYear(),g.getMonth(),g.getDate())),!1!==N.minDateTime&&(p=W.strToDate(N.minDateTime),p=new Date(p.getFullYear(),p.getMonth(),p.getDate(),p.getHours(),p.getMinutes(),p.getSeconds())),!1!==N.maxDateTime&&(D=W.strToDate(N.maxDateTime),D=new Date(D.getFullYear(),D.getMonth(),D.getDate(),D.getHours(),D.getMinutes(),D.getSeconds())),!1!==D&&(u=31*(12*D.getFullYear()+D.getMonth())+D.getDate());c<W.currentTime.countDaysInMonth()||f.getDay()!==N.dayOfWeekStart||W.currentTime.getMonth()===f.getMonth();){y=[],c+=1,a=f.getDay(),n=f.getDate(),r=f.getFullYear(),M=f.getMonth(),o=W.getWeekOfYear(f),d="",y.push("xdsoft_date"),i=N.beforeShowDay&&L.isFunction(N.beforeShowDay.call)?N.beforeShowDay.call(_,f):null,N.allowDateRe&&"[object RegExp]"===Object.prototype.toString.call(N.allowDateRe)&&(N.allowDateRe.test(E.formatDate(f,N.formatDate))||y.push("xdsoft_disabled")),N.allowDates&&0<N.allowDates.length&&-1===N.allowDates.indexOf(E.formatDate(f,N.formatDate))&&y.push("xdsoft_disabled");var k=31*(12*f.getFullYear()+f.getMonth())+f.getDate();(!1!==h&&h<f||!1!==p&&f<p||!1!==g&&f<g||!1!==D&&u<k||i&&!1===i[0])&&y.push("xdsoft_disabled"),-1!==N.disabledDates.indexOf(E.formatDate(f,N.formatDate))&&y.push("xdsoft_disabled"),-1!==N.disabledWeekDays.indexOf(a)&&y.push("xdsoft_disabled"),O.is("[disabled]")&&y.push("xdsoft_disabled"),i&&""!==i[1]&&y.push(i[1]),W.currentTime.getMonth()!==M&&y.push("xdsoft_other_month"),(N.defaultSelect||_.data("changed"))&&E.formatDate(W.currentTime,N.formatDate)===E.formatDate(f,N.formatDate)&&y.push("xdsoft_current"),E.formatDate(m,N.formatDate)===E.formatDate(f,N.formatDate)&&y.push("xdsoft_today"),0!==f.getDay()&&6!==f.getDay()&&-1===N.weekends.indexOf(E.formatDate(f,N.formatDate))||y.push("xdsoft_weekend"),void 0!==N.highlightedDates[E.formatDate(f,N.formatDate)]&&(t=N.highlightedDates[E.formatDate(f,N.formatDate)],y.push(void 0===t.style?"xdsoft_highlighted_default":t.style),d=void 0===t.desc?"":t.desc),N.beforeShowDay&&L.isFunction(N.beforeShowDay)&&y.push(N.beforeShowDay(f)),v&&(l+="<tr>",v=!1,N.weeks&&(l+="<th>"+o+"</th>")),l+='<td data-date="'+n+'" data-month="'+M+'" data-year="'+r+'" class="xdsoft_date xdsoft_day_of_week'+f.getDay()+" "+y.join(" ")+'" title="'+d+'"><div>'+n+"</div></td>",f.getDay()===N.dayOfWeekStartPrev&&(l+="</tr>",v=!0),f.setDate(n+1)}l+="</tbody></table>",C.html(l),F.find(".xdsoft_label span").eq(0).text(N.i18n[R].months[W.currentTime.getMonth()]),F.find(".xdsoft_label span").eq(1).text(W.currentTime.getFullYear()+N.yearOffset),M=b="";var x=0;if(!1!==N.minTime){var T=W.strtotime(N.minTime);x=60*T.getHours()+T.getMinutes()}var S=1440;if(!1!==N.maxTime){T=W.strtotime(N.maxTime);S=60*T.getHours()+T.getMinutes()}if(!1!==N.minDateTime){T=W.strToDateTime(N.minDateTime);if(E.formatDate(W.currentTime,N.formatDate)===E.formatDate(T,N.formatDate)){var M=60*T.getHours()+T.getMinutes();x<M&&(x=M)}}if(!1!==N.maxDateTime){T=W.strToDateTime(N.maxDateTime);if(E.formatDate(W.currentTime,N.formatDate)===E.formatDate(T,N.formatDate))(M=60*T.getHours()+T.getMinutes())<S&&(S=M)}if(s=function(e,t){var a,n=W.now(),r=N.allowTimes&&L.isArray(N.allowTimes)&&N.allowTimes.length;n.setHours(e),e=parseInt(n.getHours(),10),n.setMinutes(t),t=parseInt(n.getMinutes(),10),y=[];var o=60*e+t;(O.is("[disabled]")||S<=o||o<x)&&y.push("xdsoft_disabled"),(a=new Date(W.currentTime)).setHours(parseInt(W.currentTime.getHours(),10)),r||a.setMinutes(Math[N.roundTime](W.currentTime.getMinutes()/N.step)*N.step),(N.initTime||N.defaultSelect||_.data("changed"))&&a.getHours()===parseInt(e,10)&&(!r&&59<N.step||a.getMinutes()===parseInt(t,10))&&(N.defaultSelect||_.data("changed")?y.push("xdsoft_current"):N.initTime&&y.push("xdsoft_init_time")),parseInt(m.getHours(),10)===parseInt(e,10)&&parseInt(m.getMinutes(),10)===parseInt(t,10)&&y.push("xdsoft_today"),b+='<div class="xdsoft_time '+y.join(" ")+'" data-hour="'+e+'" data-minute="'+t+'">'+E.formatDate(n,N.formatTime)+"</div>"},N.allowTimes&&L.isArray(N.allowTimes)&&N.allowTimes.length)for(c=0;c<N.allowTimes.length;c+=1)s(W.strtotime(N.allowTimes[c]).getHours(),M=W.strtotime(N.allowTimes[c]).getMinutes());else for(e=c=0;c<(N.hours12?12:24);c+=1)for(e=0;e<60;e+=N.step){var w=60*c+e;w<x||(S<=w||s((c<10?"0":"")+c,M=(e<10?"0":"")+e))}for(P.html(b),H="",c=parseInt(N.yearStart,10);c<=parseInt(N.yearEnd,10);c+=1)H+='<div class="xdsoft_option '+(W.currentTime.getFullYear()===c?"xdsoft_current":"")+'" data-value="'+c+'">'+(c+N.yearOffset)+"</div>";for(A.children().eq(0).html(H),c=parseInt(N.monthStart,10),H="";c<=parseInt(N.monthEnd,10);c+=1)H+='<div class="xdsoft_option '+(W.currentTime.getMonth()===c?"xdsoft_current":"")+'" data-value="'+c+'">'+N.i18n[R].months[c]+"</div>";Y.children().eq(0).html(H),L(_).trigger("generate.xdsoft")},10),e.stopPropagation()}).on("afterOpen.xdsoft",function(){var e,t,a,n;N.timepicker&&(P.find(".xdsoft_current").length?e=".xdsoft_current":P.find(".xdsoft_init_time").length&&(e=".xdsoft_init_time"),e?(t=u[0].clientHeight,(a=P[0].offsetHeight)-t<(n=P.find(e).index()*N.timeHeightInTimePicker+1)&&(n=a-t),u.trigger("scroll_element.xdsoft_scroller",[parseInt(n,10)/(a-t)])):u.trigger("scroll_element.xdsoft_scroller",[0]))}),n=0,C.on("touchend click.xdsoft","td",function(e){e.stopPropagation(),n+=1;var t=L(this),a=W.currentTime;if(null==a&&(W.currentTime=W.now(),a=W.currentTime),t.hasClass("xdsoft_disabled"))return!1;a.setDate(1),a.setFullYear(t.data("year")),a.setMonth(t.data("month")),a.setDate(t.data("date")),_.trigger("select.xdsoft",[a]),O.val(W.str()),N.onSelectDate&&L.isFunction(N.onSelectDate)&&N.onSelectDate.call(_,W.currentTime,_.data("input"),e),_.data("changed",!0),_.trigger("xchange.xdsoft"),_.trigger("changedatetime.xdsoft"),(1<n||!0===N.closeOnDateSelect||!1===N.closeOnDateSelect&&!N.timepicker)&&!N.inline&&_.trigger("close.xdsoft"),setTimeout(function(){n=0},200)}),P.on("touchstart","div",function(e){this.touchMoved=!1}).on("touchmove","div",l).on("touchend click.xdsoft","div",function(e){if(!this.touchMoved){e.stopPropagation();var t=L(this),a=W.currentTime;if(null==a&&(W.currentTime=W.now(),a=W.currentTime),t.hasClass("xdsoft_disabled"))return!1;a.setHours(t.data("hour")),a.setMinutes(t.data("minute")),_.trigger("select.xdsoft",[a]),_.data("input").val(W.str()),N.onSelectTime&&L.isFunction(N.onSelectTime)&&N.onSelectTime.call(_,W.currentTime,_.data("input"),e),_.data("changed",!0),_.trigger("xchange.xdsoft"),_.trigger("changedatetime.xdsoft"),!0!==N.inline&&!0===N.closeOnTimeSelect&&_.trigger("close.xdsoft")}}),g.on("mousewheel.xdsoft",function(e){return!N.scrollMonth||(e.deltaY<0?W.nextMonth():W.prevMonth(),!1)}),O.on("mousewheel.xdsoft",function(e){return!N.scrollInput||(!N.datepicker&&N.timepicker?(0<=(a=P.find(".xdsoft_current").length?P.find(".xdsoft_current").eq(0).index():0)+e.deltaY&&a+e.deltaY<P.children().length&&(a+=e.deltaY),P.children().eq(a).length&&P.children().eq(a).trigger("mousedown"),!1):N.datepicker&&!N.timepicker?(g.trigger(e,[e.deltaY,e.deltaX,e.deltaY]),O.val&&O.val(W.str()),_.trigger("changedatetime.xdsoft"),!1):void 0)}),_.on("changedatetime.xdsoft",function(e){if(N.onChangeDateTime&&L.isFunction(N.onChangeDateTime)){var t=_.data("input");N.onChangeDateTime.call(_,W.currentTime,t,e),delete N.value,t.trigger("change")}}).on("generate.xdsoft",function(){N.onGenerate&&L.isFunction(N.onGenerate)&&N.onGenerate.call(_,W.currentTime,_.data("input")),s&&(_.trigger("afterOpen.xdsoft"),s=!1)}).on("click.xdsoft",function(e){e.stopPropagation()}),a=0,h=function(e,t){do{if(!(e=e.parentNode)||!1===t(e))break}while("HTML"!==e.nodeName)},r=function(){var e,t,a,n,r,o,i,s,d,u,l,f,c;if(e=(s=_.data("input")).offset(),t=s[0],u="top",a=e.top+t.offsetHeight-1,n=e.left,r="absolute",d=L(N.contentWindow).width(),f=L(N.contentWindow).height(),c=L(N.contentWindow).scrollTop(),N.ownerDocument.documentElement.clientWidth-e.left<g.parent().outerWidth(!0)){var m=g.parent().outerWidth(!0)-t.offsetWidth;n-=m}"rtl"===s.parent().css("direction")&&(n-=_.outerWidth()-s.outerWidth()),N.fixed?(a-=c,n-=L(N.contentWindow).scrollLeft(),r="fixed"):(i=!1,h(t,function(e){return null!==e&&("fixed"===N.contentWindow.getComputedStyle(e).getPropertyValue("position")?!(i=!0):void 0)}),i&&!N.insideParent?(r="fixed",a+_.outerHeight()>f+c?(u="bottom",a=f+c-e.top):a-=c):a+_[0].offsetHeight>f+c&&(a=e.top-_[0].offsetHeight+1),a<0&&(a=0),n+t.offsetWidth>d&&(n=d-t.offsetWidth)),o=_[0],h(o,function(e){if("relative"===N.contentWindow.getComputedStyle(e).getPropertyValue("position")&&d>=e.offsetWidth)return n-=(d-e.offsetWidth)/2,!1}),l={position:r,left:N.insideParent?t.offsetLeft:n,top:"",bottom:""},N.insideParent?l[u]=t.offsetTop+t.offsetHeight:l[u]=a,_.css(l)},_.on("open.xdsoft",function(e){var t=!0;N.onShow&&L.isFunction(N.onShow)&&(t=N.onShow.call(_,W.currentTime,_.data("input"),e)),!1!==t&&(_.show(),r(),L(N.contentWindow).off("resize.xdsoft",r).on("resize.xdsoft",r),N.closeOnWithoutClick&&L([N.ownerDocument.body,N.contentWindow]).on("touchstart mousedown.xdsoft",function e(){_.trigger("close.xdsoft"),L([N.ownerDocument.body,N.contentWindow]).off("touchstart mousedown.xdsoft",e)}))}).on("close.xdsoft",function(e){var t=!0;F.find(".xdsoft_month,.xdsoft_year").find(".xdsoft_select").hide(),N.onClose&&L.isFunction(N.onClose)&&(t=N.onClose.call(_,W.currentTime,_.data("input"),e)),!1===t||N.opened||N.inline||_.hide(),e.stopPropagation()}).on("toggle.xdsoft",function(){_.is(":visible")?_.trigger("close.xdsoft"):_.trigger("open.xdsoft")}).data("input",O),d=0,_.data("xdsoft_datetime",W),_.setOptions(N),W.setCurrentTime(f()),O.data("xdsoft_datetimepicker",_).on("open.xdsoft focusin.xdsoft mousedown.xdsoft touchstart",function(){O.is(":disabled")||O.data("xdsoft_datetimepicker").is(":visible")&&N.closeOnInputClick||N.openOnFocus&&(clearTimeout(d),d=setTimeout(function(){O.is(":disabled")||(s=!0,W.setCurrentTime(f(),!0),N.mask&&c(N),_.trigger("open.xdsoft"))},100))}).on("keydown.xdsoft",function(e){var t,a=e.which;return-1!==[D].indexOf(a)&&N.enterLikeTab?(t=L("input:visible,textarea:visible,button:visible,a:visible"),_.trigger("close.xdsoft"),t.eq(t.index(this)+1).focus(),!1):-1!==[T].indexOf(a)?(_.trigger("close.xdsoft"),!0):void 0}).on("blur.xdsoft",function(){_.trigger("close.xdsoft")})},r=function(e){var t=e.data("xdsoft_datetimepicker");t&&(t.data("xdsoft_datetime",null),t.remove(),e.data("xdsoft_datetimepicker",null).off(".xdsoft"),L(N.contentWindow).off("resize.xdsoft"),L([N.contentWindow,N.ownerDocument.body]).off("mousedown.xdsoft touchstart"),e.unmousewheel&&e.unmousewheel())},L(N.ownerDocument).off("keydown.xdsoftctrl keyup.xdsoftctrl").off("keydown.xdsoftcmd keyup.xdsoftcmd").on("keydown.xdsoftctrl",function(e){e.keyCode===p&&(I=!0)}).on("keyup.xdsoftctrl",function(e){e.keyCode===p&&(I=!1)}).on("keydown.xdsoftcmd",function(e){91===e.keyCode&&!0}).on("keyup.xdsoftcmd",function(e){91===e.keyCode&&!1}),this.each(function(){var t,e=L(this).data("xdsoft_datetimepicker");if(e){if("string"===L.type(H))switch(H){case"show":L(this).select().focus(),e.trigger("open.xdsoft");break;case"hide":e.trigger("close.xdsoft");break;case"toggle":e.trigger("toggle.xdsoft");break;case"destroy":r(L(this));break;case"reset":this.value=this.defaultValue,this.value&&e.data("xdsoft_datetime").isValidDate(E.parseDate(this.value,N.format))||e.data("changed",!1),e.data("xdsoft_datetime").setCurrentTime(this.value);break;case"validate":e.data("input").trigger("blur.xdsoft");break;default:e[H]&&L.isFunction(e[H])&&(o=e[H](a))}else e.setOptions(H);return 0}"string"!==L.type(H)&&(!N.lazyInit||N.open||N.inline?n(L(this)):(t=L(this)).on("open.xdsoft focusin.xdsoft mousedown.xdsoft touchstart",function e(){t.is(":disabled")||t.data("xdsoft_datetimepicker")||(clearTimeout(i),i=setTimeout(function(){t.data("xdsoft_datetimepicker")||n(t),t.off("open.xdsoft focusin.xdsoft mousedown.xdsoft touchstart",e).trigger("open.xdsoft")},100))}))}),o},L.fn.datetimepicker.defaults=s};!function(e){"function"==typeof define&&define.amd?define(["jquery","jquery-mousewheel"],e):"object"==typeof exports?module.exports=e(require("jquery")):e(jQuery)}(datetimepickerFactory),function(e){"function"==typeof define&&define.amd?define(["jquery"],e):"object"==typeof exports?module.exports=e:e(jQuery)}(function(c){var m,h,e=["wheel","mousewheel","DOMMouseScroll","MozMousePixelScroll"],t="onwheel"in document||9<=document.documentMode?["wheel"]:["mousewheel","DomMouseScroll","MozMousePixelScroll"],g=Array.prototype.slice;if(c.event.fixHooks)for(var a=e.length;a;)c.event.fixHooks[e[--a]]=c.event.mouseHooks;var p=c.event.special.mousewheel={version:"3.1.12",setup:function(){if(this.addEventListener)for(var e=t.length;e;)this.addEventListener(t[--e],n,!1);else this.onmousewheel=n;c.data(this,"mousewheel-line-height",p.getLineHeight(this)),c.data(this,"mousewheel-page-height",p.getPageHeight(this))},teardown:function(){if(this.removeEventListener)for(var e=t.length;e;)this.removeEventListener(t[--e],n,!1);else this.onmousewheel=null;c.removeData(this,"mousewheel-line-height"),c.removeData(this,"mousewheel-page-height")},getLineHeight:function(e){var t=c(e),a=t["offsetParent"in c.fn?"offsetParent":"parent"]();return a.length||(a=c("body")),parseInt(a.css("fontSize"),10)||parseInt(t.css("fontSize"),10)||16},getPageHeight:function(e){return c(e).height()},settings:{adjustOldDeltas:!0,normalizeOffset:!0}};function n(e){var t,a=e||window.event,n=g.call(arguments,1),r=0,o=0,i=0,s=0,d=0;if((e=c.event.fix(a)).type="mousewheel","detail"in a&&(i=-1*a.detail),"wheelDelta"in a&&(i=a.wheelDelta),"wheelDeltaY"in a&&(i=a.wheelDeltaY),"wheelDeltaX"in a&&(o=-1*a.wheelDeltaX),"axis"in a&&a.axis===a.HORIZONTAL_AXIS&&(o=-1*i,i=0),r=0===i?o:i,"deltaY"in a&&(r=i=-1*a.deltaY),"deltaX"in a&&(o=a.deltaX,0===i&&(r=-1*o)),0!==i||0!==o){if(1===a.deltaMode){var u=c.data(this,"mousewheel-line-height");r*=u,i*=u,o*=u}else if(2===a.deltaMode){var l=c.data(this,"mousewheel-page-height");r*=l,i*=l,o*=l}if(t=Math.max(Math.abs(i),Math.abs(o)),(!h||t<h)&&y(a,h=t)&&(h/=40),y(a,t)&&(r/=40,o/=40,i/=40),r=Math[1<=r?"floor":"ceil"](r/h),o=Math[1<=o?"floor":"ceil"](o/h),i=Math[1<=i?"floor":"ceil"](i/h),p.settings.normalizeOffset&&this.getBoundingClientRect){var f=this.getBoundingClientRect();s=e.clientX-f.left,d=e.clientY-f.top}return e.deltaX=o,e.deltaY=i,e.deltaFactor=h,e.offsetX=s,e.offsetY=d,e.deltaMode=0,n.unshift(e,r,o,i),m&&clearTimeout(m),m=setTimeout(D,200),(c.event.dispatch||c.event.handle).apply(this,n)}}function D(){h=null}function y(e,t){return p.settings.adjustOldDeltas&&"mousewheel"===e.type&&t%120==0}c.fn.extend({mousewheel:function(e){return e?this.bind("mousewheel",e):this.trigger("mousewheel")},unmousewheel:function(e){return this.unbind("mousewheel",e)}})});
admin/assets/js/libs/popover.min.js ADDED
@@ -0,0 +1 @@
 
1
+ !function(a){var b="fu_popover",c=7,d={arrowShow:!0,autoHide:!1,autoHideDelay:2500,content:"",delay:{show:0,hide:0},dismissable:!1,placement:"bottom",themeName:"default",title:"",trigger:"click",width:"150px"},e=function(e,f){return this.element=a(e),this.popoverId=b+"_"+c++,this.options=a.extend({},d,f),this.options.autoHideDelay=void 0===this.options.autoHideDelay?0:this.options.autoHideDelay,this.options.delay.show=void 0===this.options.delay.show?0:this.options.delay.show,this.options.delay.hide=void 0===this.options.delay.hide?0:this.options.delay.hide,this.setStyles(),this.init(),this.initTriggers(),this};e.prototype.setStyles=function(){if("default"===this.options.themeName)if(a("#fu_popover_styles_default").length);else{var b=".fu_popover_"+this.options.themeName+" {position: absolute;background: #fff;border: 1px solid rgba(0, 0, 0, 0.2);border-radius: 6px;z-index: 1060;-webkit-background-clip: padding-box;background-clip: padding-box;border: 1px solid #cccccc;border: 1px solid rgba(0, 0, 0, 0.2);border-radius: 6px;-webkit-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);}";b+=".fu_popover_header_"+this.options.themeName+"{margin: 0;padding: 8px 14px;font-size: 14px;text-align: center;background-color: #f7f7f7;border-bottom: 1px solid #ebebeb;border-radius: 5px 5px 0 0;}.fu_popover_content_"+this.options.themeName+"{padding: 9px 14px;}",b+=".fu_popover_"+this.options.themeName+":after, .fu_popover_"+this.options.themeName+':before {border: solid transparent;content: " ";height: 0;width: 0;position: absolute;pointer-events: none;}.fu_popover_'+this.options.themeName+":after {border-color: rgba(255, 255, 255, 0);border-width: 10px;}.fu_popover_"+this.options.themeName+":before {border-color: rgba(0, 0, 0, 0);border-width: 11px;}",b+=".arrow_top_"+this.options.themeName+":after{left: 50%;bottom: 100%;border-bottom-color: #fff;margin-left: -10px;}.arrow_top_"+this.options.themeName+":before{left: 50%;bottom: 100%;border-bottom-color: rgba(0, 0, 0, 0.2);margin-left: -11px;}",b+=".arrow_bottom_"+this.options.themeName+":after{left: 50%;top: 100%;border-top-color: #fff;margin-left: -10px;}.arrow_bottom_"+this.options.themeName+":before{left: 50%;top: 100%;border-top-color: rgba(0, 0, 0, 0.2);margin-left: -11px;}",b+=".arrow_left_"+this.options.themeName+":after{right: 100%;top: 50%;border-right-color: #fff;margin-top: -10px;}.arrow_left_"+this.options.themeName+":before{right: 100%;top: 50%;border-right-color: rgba(0, 0, 0, 0.2);margin-top: -11px;}",b+=".arrow_right_"+this.options.themeName+":after{left: 100%;top: 50%;border-left-color: #fff;margin-top: -10px;}.arrow_right_"+this.options.themeName+":before{left: 100%;top: 50%;border-left-color: rgba(0, 0, 0, 0.2);margin-top: -11px;}",a("<style type='text/css' id='fu_popover_styles_default'>"+b+"</style>").appendTo("head"),b=".fu_progress{overflow: hidden;height: 20px;margin-bottom: 10px;background-color: #f5f5f5;border-radius: 4px;-webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1);box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1);}",b+=".fu_progress_bar{float: left;width: 0%;height: 100%;font-size: 12px;line-height: 20px;color: #ffffff;text-align: center;background-color: #337ab7;-webkit-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15);box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15);-webkit-transition: width 0.6s ease;-o-transition: width 0.6s ease;transition: width 0.6s ease;}",a("<style type='text/css'>"+b+"</style>").appendTo("head")}},e.prototype.init=function(){var a="";this.options.title.length>0&&(a='<div class="fu_popover_header_'+this.options.themeName+'">'+this.options.title+"</div>");var c='<div class="fu_popover_'+this.options.themeName+" "+this.getArrowClass()+'" id="'+this.popoverId+'" style="display:none;">'+a+'<div class="fu_popover_content_'+this.options.themeName+'">'+this.options.content+"</div></div>";this.options.dismissable===!0?this.initDismissableEvent():"",this.htmlStr=c},e.prototype.getArrowClass=function(){return"top"===this.options.placement?"arrow_bottom_"+this.options.themeName:"bottom"===this.options.placement?"arrow_top_"+this.options.themeName:"left"===this.options.placement?"arrow_right_"+this.options.themeName:"right"===this.options.placement?"arrow_left_"+this.options.themeName:""},e.prototype.initDismissableEvent=function(){var b=this.popoverId,c=this.options.delay.hide,d=this.element;a(document).mouseup(function(e){var f=a(d),g=a("#"+b);f.is(e.target)||0!==f.has(e.target).length||g.is(e.target)||0!==g.has(e.target).length||a("#"+b).hide(c)})},e.prototype.initTriggers=function(){var a=this.options.trigger.split(" ");a=jQuery.unique(a);for(var b=0;b<a.length;b++)"click"===a[b]?this.initClickTrigger():"hover"===a[b]?this.initHoverTrigger():"focus"===a[b]?this.initFocusTrigger():""},e.prototype.initClickTrigger=function(){var b=this.element;a("body").on("click","#"+this.element[0].id,function(){a(b).fu_popover("show")})},e.prototype.initHoverTrigger=function(){var b=this.element;a("body").on("mouseenter","#"+this.element[0].id,function(){a(b).fu_popover("show")})},e.prototype.initFocusTrigger=function(){var b=this.element;a("body").on("focus","#"+this.element[0].id,function(){a(b).fu_popover("show")})},e.prototype.display=function(b){var c=this.popoverId;if("show"===b){var d=this.options.delay.show,e=this.options.arrowShow;if(a("#"+c).length||a("body").append(this.htmlStr),setTimeout(function(){e||(a("#"+c).attr("class",""),a("#"+c).addClass("fu_popover_"+this.options.themeName)),a("#"+c).show()},d),this.setPopupPosition(),this.options.autoHide===!0){var f=this.options.autoHideDelay;f=0==f?2500:f,setTimeout(function(){a("#"+c).hide()},f)}}else"hide"===b?a("#"+c).hide(this.options.delay.hide):"destroy"===b&&(a("#"+c).remove(),a(this.element).removeData("fu_popover"),this.destroyTriggers())},e.prototype.getOffsetSum=function(a){for(var b=0,c=0;a;)b+=parseInt(a.offsetTop),c+=parseInt(a.offsetLeft),a=a.offsetParent;return{top:b,left:c}},e.prototype.getOffsetRect=function(a){var b=a.getBoundingClientRect(),c=document.body,d=document.documentElement,e=window.pageYOffset||d.scrollTop||c.scrollTop,f=window.pageXOffset||d.scrollLeft||c.scrollLeft,g=d.clientTop||c.clientTop||0,h=d.clientLeft||c.clientLeft||0,i=b.top+e-g,j=b.left+f-h;return{top:Math.round(i),left:Math.round(j)}},e.prototype.getOffset=function(a){return a.getBoundingClientRect?this.getOffsetRect(a):this.getOffsetSum(a)},e.prototype.setPopupPosition=function(){a("#"+this.popoverId).css({width:this.options.width});var j,k,b=this.getOffset(this.element[0]),d=(a("#"+this.popoverId).position(),a(this.element).outerWidth()),e=a(this.element).outerHeight(),f=b.left,g=b.top,h=a("#"+this.popoverId).outerWidth(),i=a("#"+this.popoverId).outerHeight();if("bottom"===this.options.placement||"top"===this.options.placement){var l=d/2,m=h/2;k="bottom"===this.options.placement?g+e+14:g-i-10;var n=f+l;j=n-m}else if("left"===this.options.placement||"right"===this.options.placement){var l=e/2,m=i/2;j="left"===this.options.placement?f-h-10:f+d+14;var o=g+l;k=o-m}a("#"+this.popoverId).css({left:j+"px",top:k+"px"})},e.prototype.destroyTriggers=function(){var b=this.options.trigger.split(" ");b=jQuery.unique(b);for(var c=0;c<b.length;c++)a("body").off("click","#"+this.element[0].id,function(){}),a("body").off("mouseenter","#"+this.element[0].id,function(){}),a("body").off("focus","#"+this.element[0].id,function(){})},a.fn.fu_popover=function(c){if("string"==typeof c){var d=a(this).data(b);if(jQuery.isEmptyObject(d))return this;d.display(c)}else if(!a.data(this,b)){var f=new e(this,c);return a("#"+f.element[0].id).data(b,f),a.data(this,b,f),this}}}(jQuery);
admin/assets/js/quickstart.js ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ jQuery(document).ready(function($) {
2
+ if(document.cookie.includes('wt-push-subscribe=1'))
3
+ jQuery('.wt-push-status').html("OK");
4
+ else
5
+ jQuery('.wt-push-status').html("No");
6
+
7
+ jQuery('#wt-quickstart-scan').on('click', function(e) {
8
+ e.preventDefault();
9
+ var btn = jQuery(this);
10
+ btn.hide();
11
+ jQuery('.wt-scan-icon-loader').show();
12
+ //jQuery('#scan').trigger('click');
13
+ vulnerability_ajax(false);
14
+ audit_ajax(false);
15
+ });
16
+ });
admin/assets/js/settings.js CHANGED
@@ -1,24 +1,133 @@
1
- /**
2
- * General scripts
3
- *
4
- * @author Alex Kovalev <alex.kovalevv@gmail.com>, Github: https://github.com/alexkovalevv
5
- * @copyright (c) 03.12.2019, CreativeMotion
6
- * @version 1.0
7
- */
 
 
 
 
 
 
 
 
 
 
8
 
 
 
 
 
 
 
 
9
 
10
- (function($) {
11
- 'use strict';
 
 
12
 
13
- $.wantispam = {};
 
 
 
14
 
15
- if( $.wbcr_factory_clearfy_217 ) {
16
- $.wantispam = $.wbcr_factory_clearfy_217;
17
- }
 
18
 
19
- $('.factory-checkbox--disabled.wantispam-checkbox-premium-label').click(function(e) {
20
- e.stopPropagation();
21
- window.location.href = 'https://anti-spam.space/pricing/';
22
- });
23
 
24
- })(jQuery);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ jQuery(document).ready(function($) {
2
+ jQuery('#titan_scanner_schedule_daily').datetimepicker({
3
+ datepicker:false,
4
+ mask:true,
5
+ format:'H:i'
6
+ });
7
+ jQuery('#titan_scanner_schedule_weekly_time').datetimepicker({
8
+ datepicker:false,
9
+ mask:true,
10
+ format:'H:i'
11
+ });
12
+ jQuery('#titan_scanner_schedule_custom').datetimepicker({
13
+ dayOfWeekStart: 1,
14
+ timepicker:true,
15
+ mask:true,
16
+ format:'Y/m/d H:i'
17
+ });
18
 
19
+ var schedule = jQuery('#titan_scanner_schedule').val();
20
+ jQuery('.wt-schedule-controls-'+schedule).show();
21
+ jQuery('#titan_scanner_schedule').on('change', function ($) {
22
+ var schedule = jQuery(this).val();
23
+ jQuery('.wt-schedule-controls').hide();
24
+ jQuery('.wt-schedule-controls-'+schedule).show();
25
+ });
26
 
27
+ // ------- IMPORT --------
28
+ $('.wtitan-import-options-button').click(function() {
29
+ var settings = $('#wbcr-clearfy-import-export').val(),
30
+ $this = $(this);
31
 
32
+ if( !settings ) {
33
+ $.wbcr_factory_clearfy_217.app.showNotice('Import options is empty!', 'danger');
34
+ return false;
35
+ }
36
 
37
+ if( void 0 == wtitan_ajax || !wtitan_ajax.import_options_nonce ) {
38
+ $.wbcr_factory_clearfy_217.app.showNotice('Unknown Javascript error, most likely the wtitan_ajax variable does not exist!', 'danger');
39
+ return false;
40
+ }
41
 
42
+ $(this).prop('disabled', true);
 
 
 
43
 
44
+ sendRequest({
45
+ action: 'wtitan_import_settings',
46
+ _wpnonce: wtitan_ajax.import_options_nonce,
47
+ settings: settings
48
+ }, function(response) {
49
+ $this.prop('disabled', false);
50
+
51
+ if( response.data.update_notice ) {
52
+ if( !$('.wbcr-clr-update-package').length ) {
53
+ $.wbcr_factory_clearfy_217.app.showNotice(response.data.update_notice);
54
+ }
55
+ } else {
56
+ if( $('.wbcr-clr-update-package').length ) {
57
+ $('.wbcr-clr-update-package').closest('.wbcr-factory-warning-notice').remove();
58
+ }
59
+ }
60
+ });
61
+
62
+ return false;
63
+ });
64
+ function sendRequest(request_data, beforeValidateCallback, successCallback) {
65
+
66
+ if( wtitan_ajax === undefined ) {
67
+ console.log('Undefinded wtitan_ajax object.');
68
+ return;
69
+ }
70
+
71
+ if( typeof request_data === 'object' ) {
72
+ request_data.security = wtitan_ajax.ajax_nonce;
73
+ }
74
+
75
+ $.ajax(ajaxurl, {
76
+ type: 'post',
77
+ dataType: 'json',
78
+ data: request_data,
79
+ success: function(data, textStatus, jqXHR) {
80
+ var noticeId;
81
+
82
+ beforeValidateCallback && beforeValidateCallback(data);
83
+
84
+ if( !data || data.error ) {
85
+ console.log(data);
86
+
87
+ if( data ) {
88
+ noticeId = $.wbcr_factory_clearfy_217.app.showNotice(data.error_message, 'danger');
89
+ } else {
90
+ if( void 0 != wtitan_ajax ) {
91
+ noticeId = $.wbcr_factory_clearfy_217.app.showNotice(wtitan_ajax.i18n.unknown_error, 'danger');
92
+ }
93
+ }
94
+
95
+ setTimeout(function() {
96
+ $.wbcr_factory_clearfy_217.app.hideNotice(noticeId);
97
+ }, 5000);
98
+ return;
99
+ }
100
+
101
+ successCallback && successCallback(data);
102
+
103
+ if( !request_data.flush_redirect ) {
104
+ if( void 0 != wtitan_ajax ) {
105
+ noticeId = $.wbcr_factory_clearfy_217.app.showNotice(wtitan_ajax.i18n.success_update_settings, 'success');
106
+
107
+ setTimeout(function() {
108
+ $.wbcr_factory_clearfy_217.app.hideNotice(noticeId);
109
+ }, 5000);
110
+ }
111
+ return;
112
+ }
113
+
114
+ window.location.href = wtitan_ajax.flush_cache_url;
115
+ // открыть уведомление
116
+
117
+ },
118
+ error: function(xhr, ajaxOptions, thrownError) {
119
+ console.log(xhr.status);
120
+ console.log(xhr.responseText);
121
+ console.log(thrownError);
122
+
123
+ var noticeId = $.wbcr_factory_clearfy_217.app.showNotice('Error: [' + thrownError + '] Status: [' + xhr.status + '] Error massage: [' + xhr.responseText + ']', 'danger');
124
+ }
125
+ });
126
+ }
127
+
128
+ $('.factory-checkbox--disabled.wtitan-control-premium-label').click(function(e) {
129
+ e.stopPropagation();
130
+ window.location.href = 'https://titansitescanner.com/pricing/';
131
+ });
132
+
133
+ });
admin/assets/js/trial-popup.js CHANGED
@@ -1,7 +1,9 @@
1
  jQuery(document).ready(function($) {
2
- jQuery('#js-wantispam-activate-trial-button').click(function(e) {
 
3
  e.preventDefault();
4
- var infosModal = $('#wantispam-tmpl-confirmation-modal');
 
5
 
6
  if( !infosModal.length ) {
7
  console.log('[Error]: Html template for modal not found.');
@@ -10,16 +12,40 @@ jQuery(document).ready(function($) {
10
 
11
  Swal.fire({
12
  html: infosModal.html(),
13
- customClass: 'wantispam-modal wantispam-modal-confirm',
14
  width: 500,
15
  showCancelButton: true,
16
  showCloseButton: true,
17
  confirmButtonText: 'Agree',
18
  }).then(function(result) {
19
  if( result.value ) {
20
- window.location.href = jQuery('#js-wantispam-activate-trial-button').data('url');
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
21
  }
22
  console.log(result);
23
  });
24
  });
 
25
  });
1
  jQuery(document).ready(function($) {
2
+
3
+ jQuery('#wtitan-activate-trial-button').click(function(e) {
4
  e.preventDefault();
5
+ var infosModal = $('#wtitan-tmpl-confirmation-modal');
6
+ var btn = jQuery(this);
7
 
8
  if( !infosModal.length ) {
9
  console.log('[Error]: Html template for modal not found.');
12
 
13
  Swal.fire({
14
  html: infosModal.html(),
15
+ customClass: 'wtitan-modal wtitan-modal-confirm',
16
  width: 500,
17
  showCancelButton: true,
18
  showCloseButton: true,
19
  confirmButtonText: 'Agree',
20
  }).then(function(result) {
21
  if( result.value ) {
22
+
23
+ jQuery.ajax({
24
+ method: 'POST',
25
+ url: ajaxurl,
26
+ data: {
27
+ action: 'wtitan_activate_trial',
28
+ email: jQuery('#wtitan-trial-email').val(),
29
+ _ajax_nonce: wtitan.trial_nonce
30
+ },
31
+ beforeSend: function() {
32
+ btn.attr('disabled', 'disabled');
33
+ var loader = document.createElement('img');
34
+ loader.src = jQuery('#wcl-license-wrapper').data('loader');
35
+ loader.height = '32';
36
+ btn.after(loader);
37
+ },
38
+ success: function(result) {
39
+ console.log(result);
40
+ window.location.href = result.data.url;
41
+ },
42
+ complete: function() {
43
+ }
44
+ });
45
+
46
  }
47
  console.log(result);
48
  });
49
  });
50
+
51
  });
admin/boot.php CHANGED
@@ -1,37 +1,35 @@
1
  <?php
2
  /**
3
- * Admin boot
 
4
  *
5
- * @author Alexander Kovalev <alex.kovalevv@gmail.com>, Github: https://github.com/alexkovalevv
6
- * @copyright Webcraftic 22.10.2019
 
 
 
7
  */
8
 
9
  // Exit if accessed directly
10
- if ( ! defined( 'ABSPATH' ) ) {
11
  exit;
12
  }
13
-
14
- /*add_action('wantispam/factory/clearfy/check_license_success', function($action, $license_key){
15
- if('activate' === $action || 'sync' === $action) {
16
-
17
- }
18
- });*/
19
-
20
  /**
21
- * Виджет отзывов
22
- *
23
- * @param string $page_url
24
- * @param string $plugin_name
25
- *
26
- * @return string
27
  */
28
- add_filter( 'wbcr_factory_pages_425_imppage_rating_widget_url', function ( $page_url, $plugin_name ) {
29
- if ( $plugin_name == \WBCR\Antispam\Plugin::app()->getPluginName() ) {
30
- return 'https://wordpress.org/support/plugin/anti-spam/reviews/';
 
31
  }
 
 
 
 
 
 
 
32
 
33
- return $page_url;
34
- }, 10, 2 );
35
 
36
  /**
37
  * Print admin notice: "Would you like to send them for spam checking?"
@@ -39,102 +37,49 @@ add_filter( 'wbcr_factory_pages_425_imppage_rating_widget_url', function ( $page
39
  * If user clicked button "Yes, do it", plugin will exec action,
40
  * that put all unapproved comments to spam check queue.
41
  */
42
- add_action( 'wbcr/factory/admin_notices', function ( $notices, $plugin_name ) {
43
- if ( $plugin_name != \WBCR\Antispam\Plugin::app()->getPluginName() ) {
 
 
 
 
44
  return $notices;
45
  }
46
- $review_link = "https://wordpress.org/support/plugin/anti-spam/reviews/";
47
- $notice_text = sprintf( __( 'Hey, You\'ve using Anti-spam – that\'s awesome! Could you please do me a BIG favor and give it a 5-star rating on WordPress? Just to help us spread the word and boost our motivation! <a href="%s" target="_blank" rel="noopener">Review</a>', "anti-spam" ), $review_link );
 
 
 
48
 
49
  $notices[] = [
50
- 'id' => 'wantispam_give_me_review',
51
- 'type' => 'success',
52
- 'where' => [
53
  'edit-comments',
54
  'plugins',
55
  'themes',
56
  'dashboard',
57
  'edit',
58
  'settings'
59
- ],
60
- 'dismissible' => true,
61
  'dismiss_expires' => 0,
62
- 'text' => '<p><strong>Anti-spam:</strong><br>' . $notice_text . '</p>'
63
  ];
64
 
65
  return $notices;
66
- }, 10, 2 );
67
-
68
- /**
69
- * Удаляем лишние виджеты из правого сайдбара в интерфейсе плагина
70
- *
71
- * - Виджет с премиум рекламой
72
- * - Виджет с рейтингом
73
- * - Виджет с маркерами информации
74
- */
75
- add_filter( 'wbcr/factory/pages/impressive/widgets', function ( $widgets, $position, $plugin ) {
76
- if ( \WBCR\Antispam\Plugin::app()->getPluginName() == $plugin->getPluginName() && 'right' == $position ) {
77
- unset( $widgets['business_suggetion'] );
78
- unset( $widgets['rating_widget'] );
79
- unset( $widgets['info_widget'] );
80
-
81
- if ( ! \WBCR\Antispam\Plugin::app()->premium->is_activate() ) {
82
- $widgets['premium_suggetion'] = wantispam_get_sidebar_premium_widget();
83
- }
84
- }
85
-
86
- return $widgets;
87
- }, 20, 3 );
88
-
89
- /**
90
- * Changes plugin title in plugin interface header
91
- */
92
- add_filter( 'wbcr/factory/pages/impressive/plugin_title', function ( $title, $plugin_name ) {
93
- if ( \WBCR\Antispam\Plugin::app()->getPluginName() == $plugin_name ) {
94
- return __( 'Anti-spam', 'realforce' );
95
- }
96
-
97
- return $title;
98
- }, 20, 2 );
99
-
100
- /**
101
- * Инициализации метабоксов и страницы "о плагине".
102
- *
103
- * Этот хук реализует условную логику, при которой пользователь переодически будет
104
- * видет страницу "О плагине", а конкретно при активации и обновлении плагина.
105
- */
106
- /*add_action( 'admin_init', function () {
107
- if ( ! current_user_can( 'manage_option' ) ) {
108
- return;
109
- }
110
-
111
- $plugin = \WBCR\Antispam\Plugin::app();
112
-
113
- // If the user has updated the plugin or activated it for the first time,
114
- // you need to show the page "What's new?"
115
- //-------------------------
116
- $about_page_viewed = $plugin->request->get( 'wantispam_about_page_viewed', null );
117
-
118
- if ( is_null( $about_page_viewed ) ) {
119
- if ( wantispam_is_need_show_about_page() ) {
120
- try {
121
- $redirect_url = $plugin->getPluginPageUrl( 'about', [ 'wantispam_about_page_viewed' => 1 ] );
122
-
123
- if ( $redirect_url ) {
124
- wp_safe_redirect( $redirect_url );
125
- die();
126
- }
127
- } catch( Exception $e ) {
128
- }
129
- }
130
- } else {
131
- if ( wantispam_is_need_show_about_page() ) {
132
- if ( $plugin->isNetworkAdmin() ) {
133
- delete_site_option( $plugin->getOptionName( 'what_is_new_64' ) );
134
- } else {
135
- delete_option( $plugin->getOptionName( 'what_is_new_64' ) );
136
- }
137
- }
138
- }
139
- } );*/
140
 
1
  <?php
2
  /**
3
+ * Usually in this file places the code that is responsible for the notification, compatibility with other plugins,
4
+ * minor functions that must be performed on all pages of the admin panel.
5
  *
6
+ * This file should contain code that applies only to the administration area.
7
+ *
8
+ * @author Webcraftic <wordpress.webraftic@gmail.com>
9
+ * @copyright Webcraftic 20.11.2019
10
+ * @version 1.0
11
  */
12
 
13
  // Exit if accessed directly
14
+ if( !defined('ABSPATH') ) {
15
  exit;
16
  }
 
 
 
 
 
 
 
17
  /**
18
+ * Выводит кнопку настроек в шапке интерфейса плагина
 
 
 
 
 
19
  */
20
+ /*
21
+ add_action( 'wbcr/factory/pages/impressive/header', function ( $plugin_name ) {
22
+ if ( $plugin_name != WBCR\Titan\Plugin::app()->getPluginName() ) {
23
+ return;
24
  }
25
+ ?>
26
+ <a href="<?php echo WBCR\Titan\Plugin::app()->getPluginPageUrl( 'plugin_settings' ) ?>" class="wbcr-factory-button wbcr-factory-type-settings">
27
+ <?php echo apply_filters( 'wbcr/clearfy/settings_button_title', __( 'Titan settings', 'titan-security' ) ); ?>
28
+ </a>
29
+ <?php
30
+ } );
31
+ */
32
 
 
 
33
 
34
  /**
35
  * Print admin notice: "Would you like to send them for spam checking?"
37
  * If user clicked button "Yes, do it", plugin will exec action,
38
  * that put all unapproved comments to spam check queue.
39
  */
40
+ add_action('wbcr/factory/admin_notices', function ($notices, $plugin_name) {
41
+ if( $plugin_name != \WBCR\Titan\Plugin::app()->getPluginName() || defined('WTITAN_PLUGIN_ACTIVE') ) {
42
+ return $notices;
43
+ }
44
+
45
+ if( !\WBCR\Titan\Plugin::app()->is_premium() ) {
46
  return $notices;
47
  }
48
+
49
+ $about_plugin_url = "https://anti-spam.space";
50
+ $install_plugin_url = admin_url('update.php?action=install-plugin&plugin=anti-spam&_wpnonce=' . wp_create_nonce('activate-plugin_titan-security'));
51
+
52
+ $notice_text = sprintf(__('Thanks for activating the premium Titan security plugin. You got a bonus, premium <a href="%s" target="_blank" rel="noopener">Anti-spam</a> plugin. Want to <a href="%s" target="_blank" rel="noopener">install it now</a>?', "titan-security"), $about_plugin_url, $install_plugin_url);
53
 
54
  $notices[] = [
55
+ 'id' => 'wtitan_bonus_suggestion',
56
+ 'type' => 'success',
57
+ /*'where' => [
58
  'edit-comments',
59
  'plugins',
60
  'themes',
61
  'dashboard',
62
  'edit',
63
  'settings'
64
+ ],*/
65
+ 'dismissible' => true,
66
  'dismiss_expires' => 0,
67
+ 'text' => '<p><strong>Titan:</strong><br>' . $notice_text . '</p>'
68
  ];
69
 
70
  return $notices;
71
+ }, 10, 2);
72
+
73
+ // Vulner class
74
+ require_once WTITAN_PLUGIN_DIR . "/includes/vulnerabilities/boot.php";
75
+ // Audit class
76
+ require_once WTITAN_PLUGIN_DIR . "/includes/audit/boot.php";
77
+ // SiteChecker class
78
+ require_once WTITAN_PLUGIN_DIR . "/includes/sitechecker/boot.php";
79
+ // Scanner class
80
+ require_once WTITAN_PLUGIN_DIR . "/includes/scanner/boot.php";
81
+ // Anti-spam class
82
+ require_once WTITAN_PLUGIN_DIR . "/includes/antispam/boot.php";
83
+ // Audit class
84
+ require_once WTITAN_PLUGIN_DIR . "/includes/check/boot.php";
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
85
 
admin/class-activation.php CHANGED
@@ -1,62 +1,57 @@
1
  <?php
2
 
3
- namespace WBCR\Antispam;
4
 
5
  /**
6
  * Activator for the Antispam
7
  *
8
  * @author Alexander Kovalev <alex.kovalevv@gmail.com>, Github: https://github.com/alexkovalevv
9
  * @copyright (c) 26.10.2019, Webcraftic
10
- * @see Wbcr_Factory425_Activator
11
  * @version 1.0
12
  */
13
 
14
  // Exit if accessed directly
15
- if ( ! defined( 'ABSPATH' ) ) {
16
  exit;
17
  }
18
 
19
- class Activation extends \Wbcr_Factory425_Activator {
20
 
21
  /**
22
  * Runs activation actions.
23
  *
24
  * @since 6.0
25
  */
26
- public function activate() {
27
-
28
- $plugin_version_in_db = $this->get_plugin_version_in_db();
29
  $current_plugin_version = $this->plugin->getPluginVersion();
30
 
31
- $tab = "\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t";
32
  $log_message = "Plugin starts activation [START].\r\n";
33
  $log_message .= "{$tab}-Plugin Version in DB: {$plugin_version_in_db}\r\n";
34
  $log_message .= "{$tab}-Current Plugin Version: {$current_plugin_version}";
35
 
36
- \WBCR\Logger\Writter::info( $log_message );
37
-
38
- if ( $this->plugin->isNetworkAdmin() ) {
39
- update_site_option( $this->plugin->getOptionName( 'what_is_new_64' ), 1 );
40
- } else {
41
- update_option( $this->plugin->getOptionName( 'what_is_new_64' ), 1 );
42
- }
43
 
44
- \WBCR\Logger\Writter::info( "Plugin has been activated [END]!" );
45
  }
46
 
47
  /**
48
  * Get previous plugin version
49
  *
50
- * @author Alexander Kovalev <alex.kovalevv@gmail.com>
51
- * @since 6.0
52
  * @return number
 
 
53
  */
54
- public function get_plugin_version_in_db() {
55
- if ( \WBCR\Antispam\Plugin::app()->isNetworkActive() ) {
56
- return get_site_option( \WBCR\Antispam\Plugin::app()->getOptionName( 'plugin_version' ), 0 );
 
57
  }
58
 
59
- return get_option( \WBCR\Antispam\Plugin::app()->getOptionName( 'plugin_version' ), 0 );
60
  }
61
 
62
 
@@ -66,8 +61,9 @@ class Activation extends \Wbcr_Factory425_Activator {
66
  * @author Alexander Kovalev <alex.kovalevv@gmail.com>
67
  * @since 6.0
68
  */
69
- public function deactivate() {
70
- \WBCR\Logger\Writter::info( "Plugin starts deactivate [START]." );
71
- \WBCR\Logger\Writter::info( "Plugin has been deactivated [END]!" );
 
72
  }
73
  }
1
  <?php
2
 
3
+ namespace WBCR\Titan;
4
 
5
  /**
6
  * Activator for the Antispam
7
  *
8
  * @author Alexander Kovalev <alex.kovalevv@gmail.com>, Github: https://github.com/alexkovalevv
9
  * @copyright (c) 26.10.2019, Webcraftic
10
+ * @see Wbcr_Factory426_Activator
11
  * @version 1.0
12
  */
13
 
14
  // Exit if accessed directly
15
+ if( !defined('ABSPATH') ) {
16
  exit;
17
  }
18
 
19
+ class Activation extends \Wbcr_Factory426_Activator {
20
 
21
  /**
22
  * Runs activation actions.
23
  *
24
  * @since 6.0
25
  */
26
+ public function activate()
27
+ {
28
+ $plugin_version_in_db = $this->get_plugin_version_in_db();
29
  $current_plugin_version = $this->plugin->getPluginVersion();
30
 
31
+ $tab = "\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t";
32
  $log_message = "Plugin starts activation [START].\r\n";
33
  $log_message .= "{$tab}-Plugin Version in DB: {$plugin_version_in_db}\r\n";
34
  $log_message .= "{$tab}-Current Plugin Version: {$current_plugin_version}";
35
 
36
+ require_once WTITAN_PLUGIN_DIR . '/includes/bruteforce/do_activate.php';
 
 
 
 
 
 
37
 
38
+ \WBCR\Titan\Logger\Writter::info($log_message);
39
  }
40
 
41
  /**
42
  * Get previous plugin version
43
  *
 
 
44
  * @return number
45
+ * @since 6.0
46
+ * @author Alexander Kovalev <alex.kovalevv@gmail.com>
47
  */
48
+ public function get_plugin_version_in_db()
49
+ {
50
+ if( \WBCR\Titan\Plugin::app()->isNetworkActive() ) {
51
+ return get_site_option(\WBCR\Titan\Plugin::app()->getOptionName('plugin_version'), 0);
52
  }
53
 
54
+ return get_option(\WBCR\Titan\Plugin::app()->getOptionName('plugin_version'), 0);
55
  }
56
 
57
 
61
  * @author Alexander Kovalev <alex.kovalevv@gmail.com>
62
  * @since 6.0
63
  */
64
+ public function deactivate()
65
+ {
66
+ \WBCR\Titan\Logger\Writter::info("Plugin starts deactivate [START].");
67
+ \WBCR\Titan\Logger\Writter::info("Plugin has been deactivated [END]!");
68
  }
69
  }
admin/class-page-titan-basic.php ADDED
@@ -0,0 +1,119 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace WBCR\Titan\Page;
4
+
5
+ // Exit if accessed directly
6
+ if( !defined('ABSPATH') ) {
7
+ exit;
8
+ }
9
+
10
+ /**
11
+ * Base class for Titan pages
12
+ *
13
+ * @author Artem Prihodko <webtemyk@ya.ru>
14
+ * @copyright (c) 2020 Creative Motion
15
+ * @version 1.0
16
+ */
17
+ class Base extends \Wbcr_FactoryClearfy218_PageBase {
18
+
19
+ /**
20
+ * Scanner page constructor.
21
+ *
22
+ * @param \Wbcr_Factory426_Plugin $plugin
23
+ *
24
+ * @author Alexander Kovalev <alex.kovalevv@gmail.com>
25
+ *
26
+ */
27
+ public function __construct(\Wbcr_Factory426_Plugin $plugin)
28
+ {
29
+ parent::__construct($plugin);
30
+ $this->menuIcon = WTITAN_PLUGIN_URL . '/admin/assets/img/titan-icon.png';
31
+ }
32
+
33
+ /**
34
+ * Add assets
35
+ *
36
+ * @return void
37
+ * @since 1.0.0
38
+ */
39
+ public function assets($scripts, $styles)
40
+ {
41
+ $this->styles->add( WTITAN_PLUGIN_URL . '/admin/assets/css/titan-security.css');
42
+
43
+ parent::assets($scripts, $styles);
44
+ }
45
+
46
+ public function getPluginTitle() {
47
+ return "<span class='wt-plugin-header-logo'>&nbsp;</span>".__( 'Titan Anti-spam & Security', 'titan-security' );
48
+ }
49
+ /**
50
+ * Создает html разметку виджета поддержки
51
+ *
52
+ * @since 7.0.6
53
+ */
54
+ public function showSupportWidget() {
55
+ $free_support_url = $this->plugin->get_support()->get_contacts_url();
56
+ $hot_support_url = $this->plugin->get_support()->get_site_url() . '/support';
57
+
58
+ ?>
59
+ <div id="wbcr-clr-support-widget" class="wbcr-factory-sidebar-widget">
60
+ <p><strong><?php _e( 'Having Issues?', 'titan-security' ); ?></strong></p>
61
+ <div class="wbcr-clr-support-widget-body">
62
+ <p>
63
+ <?php _e( 'We provide free support for this plugin. If you are pushed with a problem, just create a new ticket. We will definitely help you!', 'titan-security' ); ?>
64
+ </p>
65
+ <ul>
66
+ <li><span class="dashicons dashicons-sos"></span>
67
+ <a href="<?= $free_support_url ?>" target="_blank"
68
+ rel="noopener"><?php _e( 'Get starting free support', 'titan-security' ); ?></a>
69
+ </li>
70
+ <li style="margin-top: 15px;background: #fff4f1;padding: 10px;color: #a58074;">
71
+ <span class="dashicons dashicons-warning"></span>
72
+ <?php printf( __( 'If you find a php error or a vulnerability in plugin, you can <a href="%s" target="_blank" rel="noopener">create ticket</a> in hot support that we responded instantly.', 'titan-security' ), $hot_support_url ); ?>
73
+ </li>
74
+ </ul>
75
+ </div>
76
+ </div>
77
+ <?php
78
+ }
79
+
80
+ /**
81
+ * Создает html разметку виджета рейтинга
82
+ *
83
+ * @param array $args
84
+ *
85
+ * @since 7.0.6
86
+ */
87
+ public function showRatingWidget( array $args ) {
88
+ if ( ! isset( $args[0] ) || empty( $args[0] ) ) {
89
+ $page_url = "https://wordpress.org/support/plugin/anti-spam/reviews/#new-post";
90
+ } else {
91
+ $page_url = $args[0];
92
+ }
93
+
94
+ $page_url = apply_filters( 'wbcr_factory_pages_426_imppage_rating_widget_url', $page_url, $this->plugin->getPluginName(), $this->getResultId() );
95
+
96
+ ?>
97
+ <div class="wbcr-factory-sidebar-widget">
98
+ <p>
99
+ <strong><?php _e( 'Do you want the plugin to improved and update?', 'titan-security' ); ?></strong>
100
+ </p>
101
+ <p><?php _e( 'Help the author, leave a review on wordpress.org. Thanks to feedback, we will know that the plugin is really useful to you and is needed.', 'titan-security' ); ?></p>
102
+ <p><?php _e( 'And also write your ideas on how to extend or improve the plugin.', 'titan-security' ); ?></p>
103
+ <p>
104
+ <span class="wporg-ratings" title="5 out of 5 stars" style="color:#ffb900;">
105
+ <span class="dashicons dashicons-star-filled"></span>
106
+ <span class="dashicons dashicons-star-filled"></span>
107
+ <span class="dashicons dashicons-star-filled"></span>
108
+ <span class="dashicons dashicons-star-filled"></span>
109
+ <span class="dashicons dashicons-star-filled"></span>
110
+ </span>
111
+ <a href="<?= $page_url ?>" title="Go rate us" target="_blank">
112
+ <strong><?php _e( 'Go rate us and push ideas', 'titan-security' ); ?></strong>
113
+ </a>
114
+ </p>
115
+ </div>
116
+ <?php
117
+ }
118
+
119
+ }
admin/index.php ADDED
File without changes
admin/pages/class-pages-about.php DELETED
@@ -1,205 +0,0 @@
1
- <?php
2
-
3
- namespace WBCR\Antispam\Page;
4
-
5
- // Exit if accessed directly
6
- if ( ! defined( 'ABSPATH' ) ) {
7
- exit;
8
- }
9
-
10
- /**
11
- * The file contains a short help info.
12
- *
13
- * @author Alexander Kovalev <alex.kovalevv@gmail.com>, Github: https://github.com/alexkovalevv
14
- * @copyright (c) 2019 Webraftic Ltd
15
- * @version 1.0
16
- */
17
- class About extends \Wbcr_FactoryClearfy217_PageBase {
18
-
19
- /**
20
- * {@inheritdoc}
21
- */
22
- public $id = 'about';
23
-
24
- /**
25
- * {@inheritdoc}
26
- */
27
- public $page_menu_dashicon = 'dashicons-star-filled';
28
-
29
- /**
30
- * {@inheritdoc}
31
- */
32
- public $type = 'page';
33
-
34
- /**
35
- * {@inheritdoc}
36
- */
37
- public $show_right_sidebar_in_options = false;
38
-
39
- /**
40
- * {@inheritdoc}
41
- */
42
- public $page_menu_position = 0;
43
-
44
-
45
- /**
46
- * Logs constructor.
47
- *
48
- * @author Alexander Kovalev <alex.kovalevv@gmail.com>
49
- *
50
- * @param \Wbcr_Factory425_Plugin $plugin
51
- */
52
- public function __construct( \Wbcr_Factory425_Plugin $plugin ) {
53
- $this->plugin = $plugin;
54
-
55
- $this->menu_title = __( 'Premium', 'anti-spam' );
56
- $this->page_menu_short_description = sprintf( __( 'What is new in %s?', 'anti-spam' ), $this->plugin->getPluginVersion() );
57
-
58
- parent::__construct( $plugin );
59
-
60
- add_action( 'admin_footer', [ $this, 'print_confirmation_modal_tpl' ] );
61
- }
62
-
63
- /**
64
- * {@inheritdoc}
65
- *
66
- * @since 6.5.2
67
- * @return void
68
- */
69
- public function assets( $scripts, $styles ) {
70
- parent::assets( $scripts, $styles );
71
-
72
- $this->styles->add( WANTISPAM_PLUGIN_URL . '/admin/assets/css/about-premium.css' );
73
-
74
- if ( ! $this->plugin->premium->is_activate() ) {
75
- $this->styles->add( WANTISPAM_PLUGIN_URL . '/admin/assets/css/libs/sweetalert2.css' );
76
- $this->styles->add( WANTISPAM_PLUGIN_URL . '/admin/assets/css/sweetalert-custom.css' );
77
-
78
- $this->scripts->add( WANTISPAM_PLUGIN_URL . '/admin/assets/js/libs/sweetalert3.min.js' );
79
- $this->scripts->add( WANTISPAM_PLUGIN_URL . '/admin/assets/js/trial-popup.js' );
80
- }
81
- }
82
-
83
- /**
84
- * {@inheritdoc}
85
- *
86
- * @since 6.5.2
87
- * @return void
88
- */
89
- public function print_confirmation_modal_tpl() {
90
- if ( isset( $_GET['page'] ) && $this->getResultId() === $_GET['page'] ) {
91
- $terms_url = "https://anti-spam.space/terms-of-use/";
92
- $privacy_url = "https://anti-spam.space/privacy/";
93
-
94
- ?>
95
- <script type="text/html" id="wantispam-tmpl-confirmation-modal">
96
- <h2 class="swal2-title">
97
- <?php _e( 'Confirmation', 'anti-spam' ) ?>
98
- </h2>
99
- <div class="wantispam-swal-content">
100
- <ul class="wantispam-list-infos">
101
- <li>
102
- <?php _e( 'We are using some personal data, like admin\'s e-mail', 'anti-spam' ) ?>
103
- </li>
104
- <li>
105
- <?php printf( __( 'By agreeing to the trial, you confirm that you have read <a href="%s" target="_blank" rel="noreferrer noopener">Terms of Service</a> and the
106
- <a href="%s" target="_blank" rel="noreferrer noopener">Privacy Policy (GDPR compilant)</a>', 'anti-spam' ), $terms_url, $privacy_url ) ?>
107
- </li>
108
- </ul>
109
- </div>
110
- </script>
111
- <?php
112
- }
113
- }
114
-
115
- /**
116
- * {@inheritdoc}
117
- */
118
- public function showPageContent() {
119
- $activate_trial_url = wp_nonce_url( $this->plugin->getPluginPageUrl( 'license', [
120
- 'action' => 'activate-trial'
121
- ] ), 'activate_trial' );
122
-
123
- ?>
124
- <div class="wrap about-wrap full-width-layout wantispam-about-premium">
125
- <!-- News Title !-->
126
- <h1 class="wantispam-about-premium__title">Meet with <?php echo $this->plugin->getPluginTitle() ?>
127
- Pro in <?php echo $this->plugin->getPluginVersion() ?></h1>
128
- <!-- News Subtext !-->
129
- <div class="about-text">
130
- Thanks for upgrading! Many new features and improvements are available that you will enjoy.
131
- </div>
132
- <!-- Latest News !-->
133
- <div class="headline">
134
- <div class="is-fullwidth has-3-columns wantispam-about-premium__columns">
135
- <div class="col wantispam-about-premium__column">
136
- <span class="dashicons dashicons-chart-line"></span>
137
- <h3>Statistic widget</h3>
138
- <p>The statistics widget on the dashboard page will provide you with statistics on the number of
139
- blocked spam.</p>
140
- </div>
141
- <div class="col wantispam-about-premium__column">
142
- <span class="dashicons dashicons-shield"></span>
143
- <h3>Protect forms</h3>
144
- <p>Allows you to protect contact and comment forms. At the moment, we support the Contact form 7
145
- plugin and Wordpress native comments.</p>
146
- </div>
147
- <div class="col wantispam-about-premium__column">
148
- <span class="dashicons dashicons-sos"></span>
149
- <h3>Perfect support</h3>
150
- <p>We provide the best support for premium users.</p>
151
- </div>
152
- </div>
153
- <p class="introduction">
154
- A new way of checking comments and registrations for spam. Once you install the plugin, all messages
155
- pass a three-step verification:
156
- </p>
157
- <ul>
158
- <li>match with the constantly updated spam base;</li>
159
- <li>check by a neural network;</li>
160
- <li>filter comments posted on a website before the plugin installation.</li>
161
- </ul>
162
- <p>Besides, now you have a handy control panel with various settings and analytics section. The result of
163
- our work is a great plugin that protects your site from spam much better! Check how it works. If you
164
- like it, don’t forget to post a review – that motivates us the best!</p>
165
- <div class="wantispam-about-premium__activate-trial">
166
- <a href="" data-url="<?php echo esc_url( $activate_trial_url ) ?>" id="js-wantispam-activate-trial-button" class="button button-default button-hero wantispam-about-premium__activate-trial-button"><?php _e( 'Try all premium features now, activate 30 days trial', 'anti-spam' ); ?></a>
167
- <p>The free trial edition (no credit card) contains all of the features included in the paid-for
168
- version of the product.</p>
169
- </div>
170
- </div>
171
- <div class="feature-section one-col">
172
- <div class="col">
173
- <h2 class="wantispam-about-premium__title wantispam-about-premium__title--h2">Useful features scheduled
174
- for future releases</h2>
175
- </div>
176
- </div>
177
- <div class="feature-section one-col">
178
- <div class="col">
179
- <ul>
180
- <li>An additional level of checking comments on the base of stop words;</li>
181
- <li>Additional integrations:
182
- <p>popular plugins for generating forms; membership plugins, plugins
183
- that add registration forms; elementor builders, beaver, composer; woocommerce; bbPress; the
184
- subscription forms protection from popular services (for example, Mailchimp).
185
- </p>
186
- </li>
187
- <li>Block or allow comments from specific countries.</li>
188
- <li>Allow comments in a certain language only.</li>
189
- <li>
190
- Manual sorting of comments mistakenly marked as spam.
191
- <p>(If a user clicked Spam (that it is not spam), display a pop-up window offering to remove the
192
- user from the blacklist. In that case, the messages from this user won’t be considered as
193
- spam anymore. It’s a sort of a training model helping the user to avoid manual operations
194
- when his client mistakenly ended up being in the blacklist.)</p>
195
- </li>
196
- <li>Remove all links from comments.</li>
197
- <li>Admin notifications to control the correct plugin performance.</li>
198
- <li>The spam list auto clean after a certain period.</li>
199
- </ul>
200
- </div>
201
- </div>
202
- <?php
203
- }
204
-
205
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
admin/pages/{class-pages-settings.php → class-pages-antispam.php} RENAMED
@@ -1,6 +1,6 @@
1
  <?php
2
 
3
- namespace WBCR\Antispam\Page;
4
 
5
  // Exit if accessed directly
6
  if ( ! defined( 'ABSPATH' ) ) {
@@ -10,13 +10,12 @@ if ( ! defined( 'ABSPATH' ) ) {
10
  /**
11
  * Страница общих настроек для этого плагина.
12
  *
13
- * Не поддерживает режим работы с мультисаймами.
14
- *
15
  * @author Alexander Kovalev <alex.kovalevv@gmail.com>, Github: https://github.com/alexkovalevv
16
- * @copyright (c) 2019 Webraftic Ltd
 
17
  * @version 1.0
18
  */
19
- class Settings extends \Wbcr_FactoryClearfy217_PageBase {
20
 
21
  /**
22
  * {@inheritDoc}
@@ -24,7 +23,7 @@ class Settings extends \Wbcr_FactoryClearfy217_PageBase {
24
  * @since 6.0
25
  * @var string
26
  */
27
- public $id = "settings";
28
 
29
  /**
30
  * {@inheritDoc}
@@ -38,54 +37,64 @@ class Settings extends \Wbcr_FactoryClearfy217_PageBase {
38
  * {@inheritDoc}
39
  *
40
  * @since 6.0
 
 
 
 
 
 
 
 
 
 
41
  * @var string
42
  */
43
  public $menu_target = 'options-general.php';
44
 
45
  /**
46
- * {@inheritDoc}
47
- *
48
- * @since 6.0
49
  * @var bool
50
  */
51
- public $show_right_sidebar_in_options = true;
52
 
53
  /**
54
- * {@inheritDoc}
55
  *
56
- * @since 6.0
57
- * @var bool
58
  */
59
- public $internal = false;
60
 
61
  /**
62
- * {@inheritDoc}
63
  *
64
- * @since 6.0
65
- * @var bool
 
 
 
 
 
 
 
 
66
  */
67
- public $add_link_to_plugin_actions = true;
 
68
 
69
  /**
70
- * WBCR\Page\Settings constructor.
71
  *
72
  * @author Alexander Kovalev <alex.kovalevv@gmail.com>
73
  *
74
- * @param \Wbcr_Factory425_Plugin $plugin
75
  *
76
  */
77
- public function __construct( \Wbcr_Factory425_Plugin $plugin ) {
78
- $this->menu_title = __( 'Anti-spam', 'anti-spam' );
79
- $this->page_menu_short_description = __( 'All settings', 'anti-spam' );
80
-
81
- parent::__construct( $plugin );
82
 
83
  $this->plugin = $plugin;
84
- }
85
-
86
-
87
- public function getPageTitle() {
88
- return __( 'General', 'anti-spam' );
89
  }
90
 
91
  /**
@@ -93,16 +102,16 @@ class Settings extends \Wbcr_FactoryClearfy217_PageBase {
93
  *
94
  * @since 6.2
95
  * @return void
96
- * @see Wbcr_FactoryPages425_AdminPage
97
  *
98
  */
99
  public function assets( $scripts, $styles ) {
100
  parent::assets( $scripts, $styles );
101
 
102
- $this->styles->add( WANTISPAM_PLUGIN_URL . '/admin/assets/css/settings.css' );
103
- $this->scripts->add( WANTISPAM_PLUGIN_URL . '/admin/assets/js/settings.js', [
104
  'jquery',
105
- 'wbcr-factory-clearfy-217-global'
106
  ], 'wantispam-settings' );
107
  }
108
 
@@ -113,7 +122,7 @@ class Settings extends \Wbcr_FactoryClearfy217_PageBase {
113
  * @return mixed[]
114
  */
115
  public function getPageOptions() {
116
- $is_premium = $this->plugin->premium->is_activate();
117
  //$upgrade_premium_url = $this->plugin->get_support()->get_pricing_url();
118
 
119
  $blocked_total = 0; // show 0 by default
@@ -125,16 +134,26 @@ class Settings extends \Wbcr_FactoryClearfy217_PageBase {
125
 
126
  $options[] = [
127
  'type' => 'html',
128
- 'html' => '<div class="wbcr-factory-page-group-header">' . '<strong>' . __( 'Base options.', 'anti-spam' ) . '</strong>' . '<p>' . sprintf( __( '%s spam comments were blocked by Anti-spam plugin so far.', 'anti-spam' ), $blocked_total ) . '</p>' . '</div>'
 
 
 
 
 
 
 
 
 
 
129
  ];
130
 
131
  $options[] = [
132
  'type' => 'checkbox',
133
  'way' => 'buttons',
134
  'name' => 'save_spam_comments',
135
- 'title' => __( 'Save spam comments', 'anti-spam' ),
136
  'layout' => [ 'hint-type' => 'icon', 'hint-icon-color' => 'green' ],
137
- 'hint' => __( 'Save spam comments into spam section. Useful for testing how the plugin works.', 'anti-spam' ),
138
  'default' => true
139
  ];
140
 
@@ -143,25 +162,25 @@ class Settings extends \Wbcr_FactoryClearfy217_PageBase {
143
  'type' => 'checkbox',
144
  'way' => 'buttons',
145
  'name' => 'comment_form_privacy_notice',
146
- 'title' => __( 'Display a privacy notice under your comment forms.', 'anti-spam' ),
147
  'layout' => [ 'hint-type' => 'icon', 'hint-icon-color' => 'green' ],
148
- 'hint' => __( 'To help your site with transparency under privacy laws like the GDPR, Antispam can display a notice to your users under your comment forms. This feature is disabled by default, however, you can turn it on above.', 'anti-spam' ),
149
  'default' => false
150
  ];
151
  }
152
 
153
  $options[] = [
154
  'type' => 'html',
155
- 'html' => '<div class="wbcr-factory-page-group-header">' . '<strong>' . __( 'Modules.', 'anti-spam' ) . '</strong>' . '<p>' . __( 'Additional modules to spam protect.', 'anti-spam' ) . '</p>' . '</div>'
156
  ];
157
 
158
  $options[] = [
159
  'type' => 'checkbox',
160
  'way' => 'buttons',
161
  'name' => 'protect_register_form',
162
- 'title' => __( 'Protect Register Form', 'anti-spam' ),
163
  'layout' => [ 'hint-type' => 'icon', 'hint-icon-color' => 'green' ],
164
- 'hint' => __( 'Registration form can be protected in a matter of minutes with a few new fields and limits imposed.', 'anti-spam' ),
165
  'default' => false,
166
  'cssClass' => ! $is_premium ? [ 'factory-checkbox--disabled wantispam-checkbox-premium-label' ] : [],
167
  ];
@@ -169,9 +188,9 @@ class Settings extends \Wbcr_FactoryClearfy217_PageBase {
169
  'type' => 'checkbox',
170
  'way' => 'buttons',
171
  'name' => 'protect_comments_form',
172
- 'title' => __( 'Advanced protection of comment forms', 'anti-spam' ),
173
  'layout' => [ 'hint-type' => 'icon', 'hint-icon-color' => 'green' ],
174
- 'hint' => sprintf( __( 'In order to protect your cooment forms, you need to make it difficult or impossible for an automated tool to fill in or submit the form while keeping it as easy as possible for your customers to fill out the form.', 'anti-spam' ), \WBCR\Antispam\Plugin::app()->getPluginTitle() ),
175
  'default' => false,
176
  'cssClass' => ! $is_premium ? [ 'factory-checkbox--disabled wantispam-checkbox-premium-label' ] : [],
177
  ];
@@ -180,9 +199,9 @@ class Settings extends \Wbcr_FactoryClearfy217_PageBase {
180
  'type' => 'checkbox',
181
  'way' => 'buttons',
182
  'name' => 'protect_contacts_form7',
183
- 'title' => __( 'Protect Contact Forms 7', 'anti-spam' ),
184
  'layout' => [ 'hint-type' => 'icon', 'hint-icon-color' => 'green' ],
185
- 'hint' => __( 'Job Spam-Free for WordPress Contact Forms.', 'anti-spam' ),
186
  'default' => false,
187
  'cssClass' => ! $is_premium ? [ 'factory-checkbox--disabled wantispam-checkbox-premium-label' ] : [],
188
  ];
@@ -192,9 +211,9 @@ class Settings extends \Wbcr_FactoryClearfy217_PageBase {
192
  'type' => 'checkbox',
193
  'way' => 'buttons',
194
  'name' => 'protect_ninja_forms',
195
- 'title' => __( 'Protect Ninja Forms', 'anti-spam' ),
196
  'layout' => [ 'hint-type' => 'icon', 'hint-icon-color' => 'green' ],
197
- 'hint' => __( 'Protects contact forms of the Ninja Forms plugin from spam.', 'anti-spam' ),
198
  'default' => false,
199
  'cssClass' => ! $is_premium ? [ 'factory-checkbox--disabled wantispam-checkbox-premium-label' ] : [],
200
  ];
@@ -204,9 +223,9 @@ class Settings extends \Wbcr_FactoryClearfy217_PageBase {
204
  'type' => 'checkbox',
205
  'way' => 'buttons',
206
  'name' => 'protect_caldera_forms',
207
- 'title' => __( 'Protect Caldera Forms', 'anti-spam' ),
208
  'layout' => [ 'hint-type' => 'icon', 'hint-icon-color' => 'green' ],
209
- 'hint' => __( 'Caldera Forms has powerful anti-spam by default. The Anti-spam plugin provides additional anti-spam protection for your Caldera Forms.', 'anti-spam' ),
210
  'default' => false,
211
  'cssClass' => ! $is_premium ? [ 'factory-checkbox--disabled wantispam-checkbox-premium-label' ] : [],
212
  'eventsOn' => [
@@ -243,7 +262,7 @@ class Settings extends \Wbcr_FactoryClearfy217_PageBase {
243
  <label class="col-sm-4 control-label"></label>
244
  <div class="control-group col-sm-8">
245
  <div id="wantispam-protect-caldera-forms-message" class="wantispam-checkbox-warning-message">
246
- <?php printf( __( '<b>You have to make additional settings in the Caldera Forms plugin!</b><br> Please create an Anti-spam processor for each of your forms that you want to protect. You can read this <a href="%s" target="_blank" rel="noopener">manual</a> to learn more about how to create an Anti-spam processor in the Caldera Forms plugin.', 'clearfy' ), 'https://anti-spam.space/docs/anti-spam-processor-for-caldera-forms/' ) ?>
247
  </div>
248
  </div>
249
  </div>
1
  <?php
2
 
3
+ namespace WBCR\Titan\Page;
4
 
5
  // Exit if accessed directly
6
  if ( ! defined( 'ABSPATH' ) ) {
10
  /**
11
  * Страница общих настроек для этого плагина.
12
  *
 
 
13
  * @author Alexander Kovalev <alex.kovalevv@gmail.com>, Github: https://github.com/alexkovalevv
14
+ * @author Artem Prihodko <webtemyk@yandex.ru>
15
+ * @copyright (c) 2020 Creative Motion
16
  * @version 1.0
17
  */
18
+ class Antispam extends Base {
19
 
20
  /**
21
  * {@inheritDoc}
23
  * @since 6.0
24
  * @var string
25
  */
26
+ public $id = "antispam";
27
 
28
  /**
29
  * {@inheritDoc}
37
  * {@inheritDoc}
38
  *
39
  * @since 6.0
40
+ * @var bool
41
+ */
42
+ public $show_right_sidebar_in_options = false;
43
+
44
+ /**
45
+ *
46
+ */
47
+ public $page_menu_position = 100;
48
+
49
+ /**
50
  * @var string
51
  */
52
  public $menu_target = 'options-general.php';
53
 
54
  /**
 
 
 
55
  * @var bool
56
  */
57
+ public $internal = false;
58
 
59
  /**
60
+ * Module URL
61
  *
62
+ * @since 7.0
63
+ * @var string
64
  */
65
+ public $MODULE_URL = WTITAN_PLUGIN_URL . "/includes/antispam";
66
 
67
  /**
68
+ * Module path
69
  *
70
+ * @since 7.0
71
+ * @var string
72
+ */
73
+ public $MODULE_PATH = WTITAN_PLUGIN_DIR . "/includes/antispam";
74
+
75
+ /**
76
+ * Module object
77
+ *
78
+ * @since 7.0
79
+ * @var object
80
  */
81
+ public $module;
82
+
83
 
84
  /**
85
+ * Antispam page constructor.
86
  *
87
  * @author Alexander Kovalev <alex.kovalevv@gmail.com>
88
  *
89
+ * @param \Wbcr_Factory426_Plugin $plugin
90
  *
91
  */
92
+ public function __construct( \Wbcr_Factory426_Plugin $plugin ) {
93
+ $this->menu_title = __( 'Anti-spam', 'titan-security' );
94
+ $this->page_menu_short_description = __( 'Detected and stoped spam', 'titan-security' );
 
 
95
 
96
  $this->plugin = $plugin;
97
+ parent::__construct( $plugin );
 
 
 
 
98
  }
99
 
100
  /**
102
  *
103
  * @since 6.2
104
  * @return void
105
+ * @see Wbcr_FactoryPages426_AdminPage
106
  *
107
  */
108
  public function assets( $scripts, $styles ) {
109
  parent::assets( $scripts, $styles );
110
 
111
+ $this->styles->add( $this->MODULE_URL . '/assets/css/settings.css' );
112
+ $this->scripts->add( $this->MODULE_URL . '/assets/js/settings.js', [
113
  'jquery',
114
+ 'wbcr-factory-clearfy-218-global'
115
  ], 'wantispam-settings' );
116
  }
117
 
122
  * @return mixed[]
123
  */
124
  public function getPageOptions() {
125
+ $is_premium = wantispam_is_license_activate();
126
  //$upgrade_premium_url = $this->plugin->get_support()->get_pricing_url();
127
 
128
  $blocked_total = 0; // show 0 by default
134
 
135
  $options[] = [
136
  'type' => 'html',
137
+ 'html' => '<div class="wbcr-factory-page-group-header">' . '<strong>' . __( 'Base options.', 'titan-security' ) . '</strong>' . '<p>' . sprintf( __( '%s spam comments were blocked by Anti-spam plugin so far.', 'titan-security' ), $blocked_total ) . '</p>' . '</div>'
138
+ ];
139
+
140
+ $options[] = [
141
+ 'type' => 'checkbox',
142
+ 'way' => 'buttons',
143
+ 'name' => 'antispam_mode',
144
+ 'title' => __( 'Anti-spam mode', 'titan-security' ),
145
+ 'layout' => [ 'hint-type' => 'icon', 'hint-icon-color' => 'green' ],
146
+ 'hint' => __( 'Enable or disable anti-spam for all site.', 'titan-security' ),
147
+ 'default' => true,
148
  ];
149
 
150
  $options[] = [
151
  'type' => 'checkbox',
152
  'way' => 'buttons',
153
  'name' => 'save_spam_comments',
154
+ 'title' => __( 'Save spam comments', 'titan-security' ),
155
  'layout' => [ 'hint-type' => 'icon', 'hint-icon-color' => 'green' ],
156
+ 'hint' => __( 'Save spam comments into spam section. Useful for testing how the plugin works.', 'titan-security' ),
157
  'default' => true
158
  ];
159
 
162
  'type' => 'checkbox',
163
  'way' => 'buttons',
164
  'name' => 'comment_form_privacy_notice',
165
+ 'title' => __( 'Display a privacy notice under your comment forms.', 'titan-security' ),
166
  'layout' => [ 'hint-type' => 'icon', 'hint-icon-color' => 'green' ],
167
+ 'hint' => __( 'To help your site with transparency under privacy laws like the GDPR, Antispam can display a notice to your users under your comment forms. This feature is disabled by default, however, you can turn it on above.', 'titan-security' ),
168
  'default' => false
169
  ];
170
  }
171
 
172
  $options[] = [
173
  'type' => 'html',
174
+ 'html' => '<div class="wbcr-factory-page-group-header">' . '<strong>' . __( 'Modules.', 'titan-security' ) . '</strong>' . '<p>' . __( 'Additional modules to spam protect.', 'titan-security' ) . '</p>' . '</div>'
175
  ];
176
 
177
  $options[] = [
178
  'type' => 'checkbox',
179
  'way' => 'buttons',
180
  'name' => 'protect_register_form',
181
+ 'title' => __( 'Protect Register Form', 'titan-security' ),
182
  'layout' => [ 'hint-type' => 'icon', 'hint-icon-color' => 'green' ],
183
+ 'hint' => __( 'Registration form can be protected in a matter of minutes with a few new fields and limits imposed.', 'titan-security' ),
184
  'default' => false,
185
  'cssClass' => ! $is_premium ? [ 'factory-checkbox--disabled wantispam-checkbox-premium-label' ] : [],
186
  ];
188
  'type' => 'checkbox',
189
  'way' => 'buttons',
190
  'name' => 'protect_comments_form',
191
+ 'title' => __( 'Advanced protection of comment forms', 'titan-security' ),
192
  'layout' => [ 'hint-type' => 'icon', 'hint-icon-color' => 'green' ],
193
+ 'hint' => __( 'In order to protect your comment forms, you need to make it difficult or impossible for an automated tool to fill in or submit the form while keeping it as easy as possible for your customers to fill out the form.', 'titan-security' ),
194
  'default' => false,
195
  'cssClass' => ! $is_premium ? [ 'factory-checkbox--disabled wantispam-checkbox-premium-label' ] : [],
196
  ];
199
  'type' => 'checkbox',
200
  'way' => 'buttons',
201
  'name' => 'protect_contacts_form7',
202
+ 'title' => __( 'Protect Contact Forms 7', 'titan-security' ),
203
  'layout' => [ 'hint-type' => 'icon', 'hint-icon-color' => 'green' ],
204
+ 'hint' => __( 'Job Spam-Free for WordPress Contact Forms.', 'titan-security' ),
205
  'default' => false,
206
  'cssClass' => ! $is_premium ? [ 'factory-checkbox--disabled wantispam-checkbox-premium-label' ] : [],
207
  ];
211
  'type' => 'checkbox',
212
  'way' => 'buttons',
213
  'name' => 'protect_ninja_forms',
214
+ 'title' => __( 'Protect Ninja Forms', 'titan-security' ),
215
  'layout' => [ 'hint-type' => 'icon', 'hint-icon-color' => 'green' ],
216
+ 'hint' => __( 'Protects contact forms of the Ninja Forms plugin from spam.', 'titan-security' ),
217
  'default' => false,
218
  'cssClass' => ! $is_premium ? [ 'factory-checkbox--disabled wantispam-checkbox-premium-label' ] : [],
219
  ];
223
  'type' => 'checkbox',
224
  'way' => 'buttons',
225
  'name' => 'protect_caldera_forms',
226
+ 'title' => __( 'Protect Caldera Forms', 'titan-security' ),
227
  'layout' => [ 'hint-type' => 'icon', 'hint-icon-color' => 'green' ],
228
+ 'hint' => __( 'Caldera Forms has powerful anti-spam by default. The Anti-spam plugin provides additional anti-spam protection for your Caldera Forms.', 'titan-security' ),
229
  'default' => false,
230
  'cssClass' => ! $is_premium ? [ 'factory-checkbox--disabled wantispam-checkbox-premium-label' ] : [],
231
  'eventsOn' => [
262
  <label class="col-sm-4 control-label"></label>
263
  <div class="control-group col-sm-8">
264
  <div id="wantispam-protect-caldera-forms-message" class="wantispam-checkbox-warning-message">
265
+ <?php printf( __( '<b>You have to make additional settings in the Caldera Forms plugin!</b><br> Please create an Anti-spam processor for each of your forms that you want to protect. You can read this <a href="%s" target="_blank" rel="noopener">manual</a> to learn more about how to create an Anti-spam processor in the Caldera Forms plugin.', 'titan-security' ), 'https://anti-spam.space/docs/anti-spam-processor-for-caldera-forms/' ) ?>
266
  </div>
267
  </div>
268
  </div>
admin/pages/class-pages-check.php ADDED
@@ -0,0 +1,386 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace WBCR\Titan\Page;
4
+
5
+ use WBCR\Titan;
6
+
7
+ // Exit if accessed directly
8
+ if( !defined('ABSPATH') ) {
9
+ exit;
10
+ }
11
+
12
+ /**
13
+ * Scanner page class
14
+ *
15
+ * @author Artem Prihodko <webtemyk@ya.ru>
16
+ * @copyright (c) 2020 Creative Motion
17
+ * @version 1.0
18
+ */
19
+ class Check extends Base {
20
+
21
+ /**
22
+ * {@inheritdoc}
23
+ */
24
+ public $id = 'check';
25
+
26
+ /**
27
+ * {@inheritdoc}
28
+ */
29
+ public $page_parent_page = 'none';
30
+
31
+ /**
32
+ * {@inheritdoc}
33
+ */
34
+ public $page_menu_dashicon = 'dashicons-plugins-checked';
35
+
36
+ /**
37
+ * {@inheritdoc}
38
+ */
39
+ public $type = 'page';
40
+
41
+ /**
42
+ * {@inheritdoc}
43
+ */
44
+ public $show_right_sidebar_in_options = false;
45
+
46
+
47
+ /**
48
+ * Module URL
49
+ *
50
+ * @since 7.0
51
+ * @var string
52
+ */
53
+ public $MODULE_URL = WTITAN_PLUGIN_URL . "/includes/check";
54
+
55
+ /**
56
+ * Module path
57
+ *
58
+ * @since 7.0
59
+ * @var string
60
+ */
61
+ public $MODULE_PATH = WTITAN_PLUGIN_DIR . "/includes/check";
62
+
63
+ /**
64
+ * Module object
65
+ *
66
+ * @since 7.0
67
+ * @var object
68
+ */
69
+ public $module;
70
+
71
+ /**
72
+ * Scanner page constructor.
73
+ *
74
+ * @param \Wbcr_Factory426_Plugin $plugin
75
+ *
76
+ * @author Alexander Kovalev <alex.kovalevv@gmail.com>
77
+ *
78
+ */
79
+ public function __construct(\Wbcr_Factory426_Plugin $plugin)
80
+ {
81
+ $this->plugin = $plugin;
82
+
83
+ $this->menu_title = __('Audit', 'titan-security');
84
+ $this->page_menu_short_description = __('Security audit and vulnerability detection', 'titan-security');
85
+
86
+ if( $this->plugin->is_premium() ) {
87
+ require_once $this->MODULE_PATH . "/boot.php";
88
+ $this->module = new Titan\Check();
89
+ }
90
+
91
+ parent::__construct($plugin);
92
+ }
93
+
94
+ /**
95
+ * {@inheritDoc}
96
+ * @param $notices
97
+ * @param \Wbcr_Factory426_Plugin $plugin
98
+ *
99
+ * @return array
100
+ * @since 6.5.2
101
+ *
102
+ * @see \FactoryPages426_ImpressiveThemplate
103
+ */
104
+ public function getActionNotices($notices)
105
+ {
106
+
107
+ $notices[] = [
108
+ 'conditions' => [
109
+ 'wtitan_prefix_changed' => 1
110
+ ],
111
+ 'type' => 'success',
112
+ 'message' => __('Database prefix has been changed!', 'titan-security')
113
+
114
+ ];
115
+
116
+ $notices[] = [
117
+ 'conditions' => [
118
+ 'wtitan_prefix_save_error' => 1,
119
+ 'wtitan_error_code' => 'empty_prefix'
120
+ ],
121
+ 'type' => 'danger',
122
+ 'message' => __('Prefix cannot be empty!', 'titan-security')
123
+
124
+ ];
125
+
126
+ $notices[] = [
127
+ 'conditions' => [
128
+ 'wtitan_prefix_save_error' => 1,
129
+ 'wtitan_error_code' => 'permission_error'
130
+ ],
131
+ 'type' => 'danger',
132
+ 'message' => sprintf(__('The database prefix cannot be changed because the wp-config.php file is not writable.', 'titan-security'), 'https://users.freemius.com/login', 'https://users.freemius.com/login')
133
+ ];
134
+
135
+ return $notices;
136
+ }
137
+
138
+ /**
139
+ * Add assets
140
+ *
141
+ * @return void
142
+ * @since 1.0.0
143
+ */
144
+ public function assets($scripts, $styles)
145
+ {
146
+ parent::assets($scripts, $styles);
147
+
148
+ $this->scripts->request([
149
+ 'bootstrap.tab',
150
+ ], 'bootstrap');
151
+
152
+ $this->styles->request([
153
+ 'bootstrap.tab',
154
+ ], 'bootstrap');
155
+
156
+ $this->styles->add($this->MODULE_URL . '/assets/css/check-dashboard.css');
157
+ $this->scripts->add($this->MODULE_URL . '/assets/js/check.js', ['jquery']);
158
+ $this->scripts->localize('update_nonce', wp_create_nonce("updates"));
159
+ $this->scripts->localize('wtscanner', [
160
+ 'update_nonce' => wp_create_nonce("updates"),
161
+ 'hide_nonce' => wp_create_nonce("hide"),
162
+ ]);
163
+
164
+ $this->styles->add(WTITAN_PLUGIN_URL . '/includes/vulnerabilities/assets/css/vulnerabilities-dashboard.css');
165
+ $this->styles->add(WTITAN_PLUGIN_URL . '/includes/audit/assets/css/audit-dashboard.css');
166
+ }
167
+
168
+ /**
169
+ * Show page content
170
+ */
171
+ public function showPageContent()
172
+ {
173
+ if( !$this->plugin->is_premium() ) {
174
+ $this->plugin->view->print_template('require-license-activate');
175
+
176
+ return;
177
+ }
178
+
179
+ $this->module->showPageContent();
180
+ }
181
+
182
+ public function fixDatabasePrefixAction()
183
+ {
184
+ if( !current_user_can('manage_options') ) {
185
+ wp_die(__('You are not have permission for this action!', 'titan-security'));
186
+ }
187
+
188
+ if( isset($_POST['wtitan_save_prefix']) ) {
189
+ $prefix = $this->request->post('wtitan_new_prefix', '', true);
190
+ $fixing_issue_id = $this->request->post('wtitan_fixing_issue_id', '', 'intval');
191
+
192
+ $this->confirmPageTemplate([
193
+ 'title' => __('Are you sure you want to change the database prefix?', 'titan-security'),
194
+ 'description' => __('Attention! The prefix for the names of all tables in your database will be changed. Please backup the database and wp-config.php file. If an error occurs when changing the database prefix, you can restore data from backup by replacing the wp-config.php file and restoring the database backup manually.', 'titan-security'),
195
+ 'actions' => [
196
+ [
197
+ 'url' => wp_nonce_url($this->getActionUrl('fix-database-prefix-confirm', [
198
+ 'wtitan_prefix' => base64_encode($prefix),
199
+ 'wtitan_fixing_issue_id' => $fixing_issue_id
200
+ ]), 'wtitan_save_prefix_confirm'),
201
+ 'title' => __('Yes, confirm', 'titan-security'),
202
+ 'class' => 'button button-primary'
203
+ ],
204
+ [
205
+ 'url' => $this->getActionUrl('fix-database-prefix-cancel'),
206
+ 'title' => __('Cancel'),
207
+ 'class' => 'button button-default'
208
+ ]
209
+
210
+ ]
211
+ ]);
212
+
213
+ return;
214
+ }
215
+
216
+ global $table_prefix;
217
+
218
+ $template = \WBCR\Titan\Plugin::app()->view()->get_template('audit/fix-database-prefix', [
219
+ 'current_prefix' => $table_prefix,
220
+ 'random_prefix' => strtolower(wp_generate_password('4', false)) . '_',
221
+ 'fixing_issue_id' => $this->request->get('wtitan_fixing_issue_id', null, 'intval')
222
+ ]);
223
+ $this->showPage($template);
224
+ }
225
+
226
+ public function fixDatabasePrefixConfirmAction()
227
+ {
228
+ if( !current_user_can('manage_options') ) {
229
+ wp_die(__('You are not have permission for this action!', 'titan-security'));
230
+ }
231
+
232
+ check_admin_referer('wtitan_save_prefix_confirm');
233
+
234
+ global $table_prefix;
235
+ $fixing_issue_id = $this->request->request('wtitan_fixing_issue_id', '', 'intval');
236
+ $table_old_prefix = $table_prefix;
237
+ $table_new_prefix = base64_decode($this->request->get('wtitan_prefix', '', true));
238
+
239
+ if( empty($table_new_prefix) ) {
240
+ $this->redirectToAction('fix-database-prefix', [
241
+ 'wtitan_prefix_save_error' => 1,
242
+ 'wtitan_error_code' => 'empty_prefix',
243
+ 'wtitan_fixing_issue_id' => $fixing_issue_id
244
+ ]);
245
+ }
246
+
247
+ if( !$this->edit_wp_config($table_new_prefix) ) {
248
+ $this->redirectToAction('fix-database-prefix', [
249
+ 'wtitan_prefix_save_error' => 1,
250
+ 'wtitan_error_code' => 'permission_error',
251
+ 'wtitan_fixing_issue_id' => $fixing_issue_id
252
+ ]);
253
+ }
254
+ $this->rename_table_names($table_old_prefix, $table_new_prefix);
255
+
256
+ $table_name = $table_new_prefix . 'options';
257
+ $this->update_table_values($table_name, 'option_name', $table_old_prefix, $table_new_prefix);
258
+
259
+ $table_name = $table_new_prefix . 'usermeta';
260
+ $this->update_table_values($table_name, 'meta_key', $table_old_prefix, $table_new_prefix);
261
+
262
+ if( !empty($_GET['wtitan_fixing_issue_id']) && is_numeric($_GET['wtitan_fixing_issue_id']) ) {
263
+ $fixing_issue_id = $this->request->get('wtitan_fixing_issue_id', '', 'intval');
264
+ $issues = get_option($this->plugin->getPrefix() . "audit_results", []);
265
+
266
+ if( isset($issues[$fixing_issue_id]) ) {
267
+ unset($issues[$fixing_issue_id]);
268
+ update_option($this->plugin->getPrefix() . "audit_results", $issues, 'no');
269
+ }
270
+ }
271
+
272
+ $url = $this->getBaseUrl('dashboard', [
273
+ 'wtitan_prefix_changed' => 1
274
+ ]);
275
+
276
+ $url = add_query_arg('action', 'index', $url);
277
+
278
+ wp_safe_redirect($url);
279
+
280
+ die();
281
+ }
282
+
283
+ public function fixDatabasePrefixCancelAction()
284
+ {
285
+ if( !current_user_can('manage_options') ) {
286
+ wp_die(__('You are not have permission for this action!', 'titan-security'));
287
+ }
288
+
289
+ $this->redirectToAction('index');
290
+ }
291
+
292
+ /**
293
+ * Edit the wp-config.php file
294
+ * @param String $table_new_prefix The name of the new prefix
295
+ * @return bool
296
+ */
297
+ private function edit_wp_config($table_new_prefix)
298
+ {
299
+ $path = ABSPATH . 'wp-config.php';
300
+
301
+ if( !file_exists($path) || !is_writeable($path) ) {
302
+ return false;
303
+ }
304
+
305
+ @chmod($path, 0777);
306
+ $content = file_get_contents($path);
307
+
308
+ if( !$content || empty($content) ) {
309
+ return false;
310
+ }
311
+
312
+ $content = preg_replace('/\$table_prefix\s?=\s?\'[A-z0-9_-]+\'[\s\t]?;/i', "\$table_prefix = '{$table_new_prefix}';", $content);
313
+
314
+ if( empty($content) ) {
315
+ return false;
316
+ }
317
+
318
+ $handle = fopen($path, 'w+');
319
+
320
+ fwrite($handle, $content);
321
+ rewind($handle);
322
+
323
+ $changed_file_content = fread($handle, filesize($path));
324
+
325
+ fclose($handle);
326
+ @chmod($path, 0644);
327
+
328
+ if( false === strpos($changed_file_content, "'$table_new_prefix'") ) {
329
+ return false;
330
+ }
331
+
332
+ return true;
333
+ }
334
+
335
+ /**
336
+ * Rename all the table names
337
+ * @param String $table_old_prefix The old prefix of the database table names
338
+ * @param String $table_new_prefix The new database prefix name
339
+ * @return void
340
+ */
341
+ private function rename_table_names($table_old_prefix, $table_new_prefix)
342
+ {
343
+ global $wpdb;
344
+
345
+ $sql = "SHOW TABLES LIKE '%'";
346
+ $results = $wpdb->get_results($sql, ARRAY_N);
347
+ $queries = array();
348
+ foreach($results as $result) {
349
+
350
+ $table_old_name = $result[0];
351
+ $table_new_name = $table_old_name;
352
+
353
+ if( strpos($table_old_name, $table_old_prefix) === 0 ) {
354
+ $table_new_name = $table_new_prefix . substr($table_old_name, strlen($table_old_prefix));
355
+ }
356
+
357
+ $sql = "RENAME TABLE $table_old_name TO $table_new_name";
358
+ $queries[] = false === $wpdb->query($sql);
359
+ }
360
+ }
361
+
362
+ /**
363
+ * Update a table column with the new prefix
364
+ * @param string $table_name the table that is going to be update
365
+ * @param string $field the table column that is going to be updated
366
+ * @param string $table_old_prefix The previous table prefix name
367
+ * @param string $table_new_prefix The new table prefix name
368
+ */
369
+ private function update_table_values($table_name, $field, $table_old_prefix, $table_new_prefix)
370
+ {
371
+ global $wpdb;
372
+
373
+ $sql = "SELECT $field FROM $table_name WHERE $field LIKE '%$table_old_prefix%'";
374
+ $results = $wpdb->get_results($sql, ARRAY_N);
375
+
376
+ foreach($results as $result) {
377
+ $old_value = $result[0];
378
+
379
+ if( strpos($old_value, $table_old_prefix) === 0 ) {
380
+ $new_value = $table_new_prefix . substr($old_value, strlen($table_old_prefix));
381
+ $sql = "UPDATE $table_name SET $field='$new_value' WHERE $field='$old_value'";
382
+ $wpdb->query($sql);
383
+ }
384
+ }
385
+ }
386
+ }
admin/pages/class-pages-dashboard.php ADDED
@@ -0,0 +1,380 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace WBCR\Titan\Page;
4
+
5
+ // Exit if accessed directly
6
+ use WBCR\Titan\Audit;
7
+ use WBCR\Titan\Plugin;
8
+ use WBCR\Titan\Views;
9
+ use WBCR\Titan\Vulnerabilities;
10
+ use WBCR\Titan\SiteChecker;
11
+
12
+ if( !defined('ABSPATH') ) {
13
+ exit;
14
+ }
15
+
16
+ /**
17
+ * The file contains a short help info.
18
+ *
19
+ * @author Artem Prihodko <webtemyk@yandex.ru>
20
+ * @copyright (c) 2020 Creative Motion
21
+ * @version 1.0
22
+ */
23
+ class Dashboard extends Base {
24
+
25
+ /**
26
+ * {@inheritdoc}
27
+ */
28
+ public $id = 'dashboard';
29
+
30
+ /**
31
+ * {@inheritdoc}
32
+ */
33
+ public $page_menu_dashicon = 'dashicons-dashboard';
34
+
35
+ /**
36
+ * Menu icon (only if a page is placed as a main menu).
37
+ * For example: '~/assets/img/menu-icon.png'
38
+ * For example dashicons: '\f321'
39
+ * @var string
40
+ */
41
+ public $menu_icon;
42
+
43
+ /**
44
+ * {@inheritdoc}
45
+ */
46
+ public $type = 'page';
47
+
48
+ /**
49
+ * {@inheritdoc}
50
+ */
51
+ public $show_right_sidebar_in_options = false;
52
+
53
+ /**
54
+ * {@inheritdoc}
55
+ */
56
+ public $page_menu_position = 90;
57
+
58
+ /**
59
+ * {@inheritDoc}
60
+ *
61
+ * @since 6.0
62
+ * @var bool
63
+ */
64
+ public $add_link_to_plugin_actions = true;
65
+
66
+ /**
67
+ * {@inheritDoc}
68
+ *
69
+ * @since 6.0
70
+ * @var bool
71
+ */
72
+ public $internal = false;
73
+
74
+ /**
75
+ * {@inheritDoc}
76
+ *
77
+ * @since 6.0
78
+ * @var bool
79
+ */
80
+ //public $add_link_to_plugin_actions = true;
81
+
82
+ /**
83
+ * Заголовок страницы, также использует в меню, как название закладки
84
+ *
85
+ * @var bool
86
+ */
87
+ public $show_page_title = true;
88
+
89
+ /**
90
+ * @var object|\WBCR\Titan\Views
91
+ */
92
+ public $view;
93
+
94
+ /**
95
+ * @var object|\WBCR\Titan\Model\Firewall
96
+ */
97
+ //public $firewall;
98
+
99
+ /**
100
+ * @var object|\WBCR\Titan\Vulnerabilities
101
+ */
102
+ public $vulnerabilities;
103
+
104
+ /**
105
+ * @var object|\WBCR\Titan\Audit
106
+ */
107
+ public $audit;
108
+
109
+ /**
110
+ * @var \WBCR\Titan\SiteChecker
111
+ */
112
+ public $sites;
113
+
114
+ /**
115
+ * @var \WBCR\Titan\Scanner
116
+ */
117
+ public $scanner;
118
+
119
+ /**
120
+ * @var \WBCR\Titan\Antispam
121
+ */
122
+ public $antispam;
123
+
124
+ /**
125
+ * @var \WBCR\Titan\Check
126
+ */
127
+ public $check;
128
+
129
+ /**
130
+ * Logs constructor.
131
+ *
132
+ * @param \Wbcr_Factory426_Plugin $plugin
133
+ *
134
+ * @author Alexander Kovalev <alex.kovalevv@gmail.com>
135
+ *
136
+ */
137
+ public function __construct(\Wbcr_Factory426_Plugin $plugin)
138
+ {
139
+ $this->plugin = $plugin;
140
+
141
+ $this->menu_title = __('Titan Anti-spam', 'titan-security');
142
+ $this->page_title = __('Dashboard', 'titan-security');
143
+ $this->title_plugin_action_link = $this->page_title;
144
+ $this->menu_sub_title = $this->page_title;
145
+ $this->page_menu_short_description = __('Start scanning and information about problems', 'titan-security');
146
+ $this->menu_icon = '~/admin/assets/img/icon.png';
147
+
148
+ $this->view = $this->plugin->view();
149
+
150
+ $this->vulnerabilities = new Vulnerabilities();
151
+ $this->audit = new Audit();
152
+ $this->check = new \WBCR\Titan\Check();
153
+ $this->antispam = new \WBCR\Titan\Antispam();
154
+
155
+ if($this->plugin->is_premium()) {
156
+ $this->sites = new SiteChecker();
157
+ }
158
+
159
+ add_action('wp_ajax_wtitan_change_scanner_speed', [$this, 'change_scanner_speed']);
160
+ add_action('wp_ajax_wtitan_change_scanner_schedule', [$this, 'change_scanner_schedule']);
161
+
162
+ parent::__construct($plugin);
163
+ }
164
+
165
+ /**
166
+ * @return string
167
+ */
168
+ public function getMenuTitle()
169
+ {
170
+ $this->check = new \WBCR\Titan\Check();
171
+ return apply_filters('wbcr/titan/admin_menu_title', $this->menu_title);
172
+ }
173
+
174
+ /**
175
+ * {@inheritdoc}
176
+ *
177
+ * @return void
178
+ * @since 1.0.0
179
+ */
180
+ public function assets($scripts, $styles)
181
+ {
182
+ parent::assets($scripts, $styles);
183
+
184
+ $this->scripts->request([
185
+ 'bootstrap.core',
186
+ 'bootstrap.tab',
187
+ ], 'bootstrap');
188
+
189
+ $this->styles->request([
190
+ 'bootstrap.core',
191
+ 'bootstrap.tab',
192
+ ], 'bootstrap');
193
+
194
+ $this->styles->add(WTITAN_PLUGIN_URL . '/includes/vulnerabilities/assets/css/vulnerabilities-dashboard.css');
195
+ $this->scripts->add(WTITAN_PLUGIN_URL . '/includes/vulnerabilities/assets/js/vulnerability_ajax.js', ['jquery']);
196
+ $this->scripts->localize('wtvulner', ['nonce' => wp_create_nonce('get_vulners')]);
197
+
198
+ $this->styles->add(WTITAN_PLUGIN_URL . '/includes/audit/assets/css/audit-dashboard.css');
199
+ $this->scripts->add(WTITAN_PLUGIN_URL . '/includes/audit/assets/js/audit_ajax.js', ['jquery']);
200
+ $this->scripts->localize('wtaudit', ['nonce' => wp_create_nonce('get_audits')]);
201
+
202
+ //$this->scripts->add(WTITAN_PLUGIN_URL . '/includes/scanner/assets/js/Chart.min.js', ['jquery']);
203
+ //$this->scripts->add(WTITAN_PLUGIN_URL . '/includes/scanner/assets/js/statistic.js', ['jquery']);
204
+ $this->scripts->add(WTITAN_PLUGIN_URL . '/includes/scanner/assets/js/scanner.js', ['jquery']);
205
+ $this->scripts->localize('wpnonce', [
206
+ 'start' => wp_create_nonce('titan-start-scan'),
207
+ 'stop' => wp_create_nonce('titan-stop-scan'),
208
+ 'status' => wp_create_nonce('titan-status-scan'),
209
+ ]);
210
+
211
+ $this->styles->add(WTITAN_PLUGIN_URL . '/includes/check/assets/css/check-dashboard.css');
212
+ $this->scripts->add(WTITAN_PLUGIN_URL . '/includes/check/assets/js/check.js', ['jquery']);
213
+ $this->scripts->localize('update_nonce', wp_create_nonce("updates"));
214
+ $this->scripts->localize('wtscanner', [
215
+ 'update_nonce' => wp_create_nonce("updates"),
216
+ 'hide_nonce' => wp_create_nonce("hide"),
217
+ ]);
218
+
219
+ $this->styles->add(WTITAN_PLUGIN_URL . '/includes/vulnerabilities/assets/css/vulnerabilities-dashboard.css');
220
+ $this->styles->add(WTITAN_PLUGIN_URL . '/includes/audit/assets/css/audit-dashboard.css');
221
+
222
+ $this->styles->add(WTITAN_PLUGIN_URL . '/admin/assets/css/dashboard-dashboard.css');
223
+ $this->scripts->add(WTITAN_PLUGIN_URL . '/admin/assets/js/dashboard.js');
224
+
225
+ $this->scripts->localize('wtdashboard', [
226
+ 'nonce' => wp_create_nonce("wtitan_change_scanner"),
227
+ ]);
228
+ //$this->scripts->add('https://www.gstatic.com/charts/loader.js', [],'', WANTISPAMP_PLUGIN_VERSION);
229
+ }
230
+
231
+
232
+ /**
233
+ * {@inheritdoc}
234
+ */
235
+ public function showPageContent()
236
+ {
237
+ $this->init();
238
+
239
+ //FIREWALL
240
+ $firewall = array();
241
+
242
+ $firewall['firewall_mode'] = $this->plugin->getPopulateOption('firewall_mode');
243
+ $firewall['firewall_pro_activated'] = defined('WTITANP_PLUGIN_ACTIVE') && WTITANP_PLUGIN_ACTIVE;
244
+ //end FIREWALL
245
+
246
+ //AUDIT
247
+ $check_content = $this->check->getPageContent('check');
248
+ //---
249
+ $scanner_started = $this->plugin->getOption('scanner_status') == 'started';
250
+
251
+ $scanner_speed = $this->plugin->getOption('scanner_speed', 'none');
252
+ if( $scanner_speed == 'none' ) {
253
+ if( $this->plugin->is_premium() ) {
254
+ $scanner_speed = 'slow';
255
+ } else {
256
+ $scanner_speed = 'free';
257
+ }
258
+ }
259
+ $scanner_speeds = [
260
+ [
261
+ \WBCR\Titan\MalwareScanner\Scanner::SPEED_FREE,
262
+ __('Free', 'titan-security'),
263
+ __('Free speed is slow', 'titan-security')
264
+ ],
265
+ [
266
+ \WBCR\Titan\MalwareScanner\Scanner::SPEED_SLOW,
267
+ __('Slow', 'titan-security'),
268
+ __('Suitable for the most budget hosting services', 'titan-security')
269
+ ],
270
+ [
271
+ \WBCR\Titan\MalwareScanner\Scanner::SPEED_MEDIUM,
272
+ __('Medium', 'titan-security'),
273
+ __('The best option for almost any capacity', 'titan-security')
274
+ ],
275
+ [
276
+ \WBCR\Titan\MalwareScanner\Scanner::SPEED_FAST,
277
+ __('Fast', 'titan-security'),
278
+ __('Checks the maximum number of files per minute. We recommend that you have more than 100 MB of RAM', 'titan-security')
279
+ ],
280
+ ];
281
+
282
+ $schedule = $this->plugin->getOption('scanner_schedule', 'none');
283
+ if( $schedule == 'none' ) {
284
+ $schedule = 'disabled';
285
+ }
286
+ $schedules = [
287
+ [
288
+ \WBCR\Titan\MalwareScanner\Scanner::SCHEDULE_DISABLED,
289
+ __('Disabled', 'titan-security'),
290
+ __('Disable scheduled scanning', 'titan-security')
291
+ ],
292
+ [
293
+ \WBCR\Titan\MalwareScanner\Scanner::SCHEDULE_DAILY,
294
+ __('Daily', 'titan-security'),
295
+ __('Scan every day', 'titan-security')
296
+ ],
297
+ [
298
+ \WBCR\Titan\MalwareScanner\Scanner::SCHEDULE_WEEKLY,
299
+ __('Weekly', 'titan-security'),
300
+ __('Scan every week', 'titan-security')
301
+ ],
302
+ [
303
+ \WBCR\Titan\MalwareScanner\Scanner::SCHEDULE_CUSTOM,
304
+ __('Custom', 'titan-security'),
305
+ __('Select the date and time of the next scan', 'titan-security')
306
+ ],
307
+ ];
308
+
309
+ $this->view->print_template('dashboard', [
310
+ 'is_premium' => $this->plugin->is_premium(),
311
+ 'scanner_started' => $scanner_started,
312
+ 'this_plugin' => $this->plugin,
313
+ 'firewall' => $firewall,
314
+ 'vulnerabilities' => $this->vulnerabilities,
315
+ 'audit' => $this->audit,
316
+ 'sites' => $this->sites,
317
+ 'scanner' => $this->check->scanner->get_current_results(),
318
+ 'antispam' => $this->antispam,
319
+ 'check_content' => $check_content,
320
+ 'scanner_speed' => $scanner_speed,
321
+ 'scanner_speeds' => $scanner_speeds,
322
+ 'schedule' => $schedule,
323
+ 'schedules' => $schedules,
324
+ ]);
325
+ }
326
+
327
+ /**
328
+ * AJAX change scanner speed
329
+ */
330
+ public function change_scanner_speed()
331
+ {
332
+ check_ajax_referer('wtitan_change_scanner');
333
+
334
+ if( !current_user_can('manage_options') ) {
335
+ wp_send_json(array('error_message' => __('You don\'t have enough capability to edit this information.', 'titan-security')));
336
+ }
337
+
338
+ if( isset($_POST['speed']) ) {
339
+
340
+ $speed = $_POST['speed'];
341
+
342
+ \WBCR\Titan\Plugin::app()->updatePopulateOption('scanner_speed', $speed);
343
+
344
+ wp_send_json([
345
+ 'message' => __("Scanner speed successfully changed", "titan-security"),
346
+ 'speed' => $speed
347
+ ]);
348
+ } else {
349
+ wp_send_json(array('error_message' => __('Scanner speed is not selected', 'titan-security')));
350
+ }
351
+ }
352
+
353
+ /**
354
+ * AJAX change scanner speed
355
+ */
356
+ public function change_scanner_schedule()
357
+ {
358
+ check_ajax_referer('wtitan_change_scanner');
359
+
360
+ if( !current_user_can('manage_options') ) {
361
+ wp_send_json(array('error_message' => __('You don\'t have enough capability to edit this information.', 'titan-security')));
362
+ }
363
+
364
+ if( isset($_POST['schedule']) ) {
365
+
366
+ $schedule = $_POST['schedule'];
367
+
368
+ \WBCR\Titan\Plugin::app()->updatePopulateOption('scanner_schedule', $schedule);
369
+
370
+ wp_send_json([
371
+ 'message' => __("Scanner schedule successfully changed", "titan-security"),
372
+ 'schedule' => $schedule
373
+ ]);
374
+ } else {
375
+ wp_send_json(array('error_message' => __('Scanner schedule is not selected', 'titan-security')));
376
+ }
377
+ }
378
+
379
+
380
+ }
admin/pages/class-pages-license.php CHANGED
@@ -1,9 +1,9 @@
1
  <?php
2
 
3
- namespace WBCR\Antispam\Page;
4
 
5
  // Exit if accessed directly
6
- if ( ! defined( 'ABSPATH' ) ) {
7
  exit;
8
  }
9
 
@@ -16,7 +16,7 @@ if ( ! defined( 'ABSPATH' ) ) {
16
  *
17
  * @copyright (c) 2018 Webraftic Ltd
18
  */
19
- class License extends \Wbcr_FactoryClearfy217_LicensePage {
20
 
21
  /**
22
  * {@inheritdoc}
@@ -27,6 +27,11 @@ class License extends \Wbcr_FactoryClearfy217_LicensePage {
27
  */
28
  public $id = 'license';
29
 
 
 
 
 
 
30
  /**
31
  * {@inheritdoc}
32
  *
@@ -36,59 +41,86 @@ class License extends \Wbcr_FactoryClearfy217_LicensePage {
36
  */
37
  public $page_parent_page;
38
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
39
  /**
40
  * WCL_LicensePage constructor.
41
  *
 
 
42
  * @author Alexander Kovalev <alex.kovalevv@gmail.com>
43
  *
44
- * @param \Wbcr_Factory425_Plugin $plugin
45
  */
46
- public function __construct( \Wbcr_Factory425_Plugin $plugin ) {
47
- $this->menu_title = __( 'License', 'anti-spam' );
48
- $this->page_menu_short_description = __( 'Product activation', 'anti-spam' );
49
- $this->plan_name = __( 'Anti-spam PRO', 'anti-spam' );
 
 
50
 
51
- parent::__construct( $plugin );
52
 
53
- add_action( 'admin_footer', [ $this, 'print_confirmation_modal_tpl' ] );
 
 
 
 
 
 
 
54
  }
55
 
56
  /**
57
  * {@inheritDoc}
58
- * @since 6.5.2
59
- *
60
  * @param $notices
61
- * @param \Wbcr_Factory425_Plugin $plugin
62
  *
63
  * @return array
64
- * @see \FactoryPages425_ImpressiveThemplate
 
 
65
  */
66
- public function getActionNotices( $notices ) {
 
67
 
68
  $notices[] = [
69
  'conditions' => [
70
- 'wantispam_trial_activated' => 1
71
  ],
72
- 'type' => 'success',
73
- 'message' => __( 'Trial is activated successfully!', 'anti-spam' )
 
74
  ];
75
 
76
  $notices[] = [
77
  'conditions' => [
78
- 'wantispam_trial_activated_error' => 1,
79
- 'wantispam_error_code' => 'interal_error'
80
  ],
81
- 'type' => 'danger',
82
- 'message' => __( 'An unknown error occurred during trial activation. Details of the error are wrote in error log.', 'anti-spam' )
 
83
  ];
84
 
85
  $notices[] = [
86
  'conditions' => [
87
- 'wantispam_trial_activated_error' => 1,
88
- 'wantispam_error_code' => 'trial_already_activated'
89
  ],
90
- 'type' => 'danger',
91
- 'message' => sprintf( __( 'You have already activated the trial earlier, you cannot activate the trial more than once. However, if your key has not expired yet, you can find it in your account (<a href="%s" target="_blank" rel="noopener noreferrer">%s</a>), and then insert the key in the license form to activate the premium plugin. To restore access to your account, use your admin email.', 'anti-spam' ), 'https://users.freemius.com/login', 'https://users.freemius.com/login' )
92
  ];
93
 
94
  return $notices;
@@ -97,49 +129,64 @@ class License extends \Wbcr_FactoryClearfy217_LicensePage {
97
  /**
98
  * {@inheritdoc}
99
  *
100
- * @since 6.5.2
101
  * @return void
 
102
  */
103
- public function assets( $scripts, $styles ) {
104
- parent::assets( $scripts, $styles );
105
-
106
- if ( ! $this->plugin->premium->is_activate() ) {
107
- $this->styles->add( WANTISPAM_PLUGIN_URL . '/admin/assets/css/libs/sweetalert2.css' );
108
- $this->styles->add( WANTISPAM_PLUGIN_URL . '/admin/assets/css/sweetalert-custom.css' );
109
-
110
- $this->scripts->add( WANTISPAM_PLUGIN_URL . '/admin/assets/js/libs/sweetalert3.min.js' );
111
- $this->scripts->add( WANTISPAM_PLUGIN_URL . '/admin/assets/js/trial-popup.js' );
 
 
 
 
 
112
  }
113
  }
114
 
115
  /**
116
  * {@inheritdoc}
117
  *
118
- * @since 6.5.2
119
  * @return void
 
120
  */
121
- public function print_confirmation_modal_tpl() {
122
- if ( isset( $_GET['page'] ) && $this->getResultId() === $_GET['page'] ) {
123
- $terms_url = "https://anti-spam.space/terms-of-use/";
124
- $privacy_url = "https://anti-spam.space/privacy/";
 
 
125
 
126
  ?>
127
- <script type="text/html" id="wantispam-tmpl-confirmation-modal">
128
- <h2 class="swal2-title">
129
- <?php _e( 'Confirmation', 'anti-spam' ) ?>
130
- </h2>
131
- <div class="wantispam-swal-content">
132
- <ul class="wantispam-list-infos">
133
- <li>
134
- <?php _e( 'We are using some personal data, like admin\'s e-mail', 'anti-spam' ) ?>
135
- </li>
136
- <li>
137
- <?php printf( __( 'By agreeing to the trial, you confirm that you have read <a href="%s" target="_blank" rel="noreferrer noopener">Terms of Service</a> and the
138
- <a href="%s" target="_blank" rel="noreferrer noopener">Privacy Policy (GDPR compilant)</a>', 'anti-spam' ), $terms_url, $privacy_url ) ?>
139
- </li>
140
- </ul>
141
- </div>
142
- </script>
 
 
 
 
 
 
 
 
143
  <?php
144
  }
145
  }
@@ -148,116 +195,147 @@ class License extends \Wbcr_FactoryClearfy217_LicensePage {
148
  /**
149
  * {@inheritdoc}
150
  *
151
- * @author Alexander Kovalev <alex.kovalevv@gmail.com>
152
- * @since 6.5
153
  * @return string
 
 
154
  */
155
- public function get_plan_description() {
156
- $activate_trial_url = wp_nonce_url( $this->getActionUrl( 'activate-trial' ), 'activate_trial' );
157
-
158
- $description = '<p style="font-size: 16px;">' . __( '<b>Anti-spam PRO</b> is a paid package of components for the popular free WordPress plugin named Anti-spam PRO. You get access to all paid components at one price.', 'clearfy' ) . '</p>';
159
- $description .= '<p style="font-size: 16px;">' . __( 'Paid license guarantees that you can download and update existing and future paid components of the plugin.', 'clearfy' ) . '</p>';
160
-
161
- if ( ! $this->plugin->premium->is_activate() ) {
 
162
  $description .= '<p>The free trial edition (no credit card) contains all of the features included in the paid-for version
163
  of the product.</p>';
164
- $description .= '<a href="" data-url="' . esc_url( $activate_trial_url ) . '" id="js-wantispam-activate-trial-button" class="button button-default">' . __( 'Activate 30 days trial', 'anti-spam' ) . '</a>';
 
165
  }
166
 
167
  return $description;
168
  }
169
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
170
  /**
171
- * @author Alexander Kovalev <alex.kovalevv@gmail.com>
172
- * @since 6.6
173
  */
174
- public function activateTrialAction() {
175
- if ( ! current_user_can( 'manage_options' ) ) {
 
 
 
 
 
 
176
  return;
177
  }
178
 
179
- check_admin_referer( 'activate_trial' );
180
 
181
- \WBCR\Logger\writter::info( 'Start trial activation [PROCESS START]!' );
182
 
183
- $admin_email = get_option( 'admin_email' );
184
- $domain = site_url();
185
 
186
  //$url = 'https://dev.anti-spam.space/api/v1.0/trial/register';
187
  $url = 'https://api.anti-spam.space/api/v1.0/trial/register';
188
 
189
  $options = [
190
  'body' => [
191
- 'email' => $admin_email,
192
  'domain' => site_url(),
193
  ]
194
  ];
195
 
196
  // Get license key from remote server
197
- $request = wp_remote_post( $url, $options );
198
-
199
- if ( is_wp_error( $request ) ) {
200
- \WBCR\Logger\writter::error( 'Http request error: ' . $request->get_error_message() );
201
- \WBCR\Logger\writter::info( 'End trial activation [PROCESS END]!' );
202
- $this->redirectToAction( 'index', [
203
- 'wantispam_trial_activated_error' => 1,
204
- 'wantispam_error_code' => 'interal_error'
205
- ] );
 
206
  }
207
 
208
- $data = json_decode( $request['body'], true );
209
 
210
- if ( $data['status'] == 'fail' ) {
211
- if ( ! empty( $data['error'] ) ) {
212
  $message = $data['error']['message'];
213
- \WBCR\Logger\writter::error( sprintf( 'Trial activation failed for domain: %s, e-mail: %s with message: %s', $domain, $admin_email, $message ) );
214
- \WBCR\Logger\writter::info( 'End trial activation [PROCESS END]!' );
215
-
216
- if ( ! empty( $data['error']['code'] ) && 1001 === $data['error']['code'] ) {
217
- $this->redirectToAction( 'index', [
218
- 'wantispam_trial_activated_error' => 1,
219
- 'wantispam_error_code' => 'trial_already_activated'
220
- ] );
221
  } else {
222
- $this->redirectToAction( 'index', [
223
- 'wantispam_trial_activated_error' => 1,
224
- 'wantispam_error_code' => 'interal_error'
225
- ] );
226
  }
227
  }
228
  }
229
 
230
  $license_key = $data['response']['license_key'];
231
 
232
- if ( empty( $license_key ) || 32 !== strlen( $license_key ) ) {
233
- \WBCR\Logger\writter::error( 'License key format is not valid' );
234
- \WBCR\Logger\writter::info( 'End trial activation [PROCESS END]!' );
235
- $this->redirectToAction( 'index', [
236
- 'wantispam_trial_activated_error' => 1,
237
- 'wantispam_error_code' => 'interal_error'
238
- ] );
239
  }
240
 
241
  try {
242
- $this->plugin->premium->activate( $license_key );
 
 
 
243
 
244
- \WBCR\Logger\writter::info( sprintf( 'Trial activation success for domain: %s, e-mail: %s', $domain, $admin_email ) );
245
- \WBCR\Logger\writter::info( 'End trial activation [PROCESS END]!' );
246
- $this->redirectToAction( 'index', [
247
- 'wantispam_trial_activated' => 1
248
- ] );
249
  } catch( \Exception $e ) {
250
- \WBCR\Logger\writter::error( $e->getMessage() );
251
- \WBCR\Logger\writter::info( 'End trial activation [PROCESS END]!' );
252
 
253
- $this->redirectToAction( 'index', [
254
- 'wantispam_trial_activated_error' => 1,
255
- 'wantispam_error_code' => 'interal_error'
256
- ] );
257
  }
258
 
 
 
259
  // Redirect to index
260
- $this->redirectToAction( 'index' );
261
- \WBCR\Logger\writter::info( 'End trial activation [PROCESS END]!' );
 
262
  }
263
  }
1
  <?php
2
 
3
+ namespace WBCR\Titan\Page;
4
 
5
  // Exit if accessed directly
6
+ if( !defined('ABSPATH') ) {
7
  exit;
8
  }
9
 
16
  *
17
  * @copyright (c) 2018 Webraftic Ltd
18
  */
19
+ class License extends \Wbcr_FactoryClearfy218_LicensePage {
20
 
21
  /**
22
  * {@inheritdoc}
27
  */
28
  public $id = 'license';
29
 
30
+ /**
31
+ * {@inheritdoc}
32
+ */
33
+ public $type = "page";
34
+
35
  /**
36
  * {@inheritdoc}
37
  *
41
  */
42
  public $page_parent_page;
43
 
44
+ /**
45
+ * {@inheritdoc}
46
+ *
47
+ * @author Alexander Kovalev <alex.kovalevv@gmail.com>
48
+ * @since 2.1.2
49
+ * @var int
50
+ */
51
+ public $page_menu_position = 0;
52
+
53
+ /**
54
+ * {@inheritdoc}
55
+ */
56
+ public $page_menu_dashicon = 'dashicons-admin-network';
57
+
58
  /**
59
  * WCL_LicensePage constructor.
60
  *
61
+ * @param \Wbcr_Factory426_Plugin $plugin
62
+ *
63
  * @author Alexander Kovalev <alex.kovalevv@gmail.com>
64
  *
 
65
  */
66
+ public function __construct(\Wbcr_Factory426_Plugin $plugin)
67
+ {
68
+ $this->menu_title = __('License', 'titan-security');
69
+ $this->page_menu_short_description = __('Product activation', 'titan-security');
70
+ $this->plan_name = __('Titan security Pro', 'titan-security');
71
+ $this->menuIcon = WTITAN_PLUGIN_URL . '/admin/assets/img/titan-icon.png';
72
 
73
+ if($plugin->is_premium()) $this->page_menu_dashicon = 'dashicons-yes-alt';
74
 
75
+ parent::__construct($plugin);
76
+
77
+ add_action('admin_footer', [$this, 'print_confirmation_modal_tpl']);
78
+ add_action('wp_ajax_wtitan_activate_trial', array($this, 'activate_trial'));
79
+ }
80
+
81
+ public function getPluginTitle() {
82
+ return "<span class='wt-plugin-header-logo'>&nbsp;</span>".__( 'Titan Anti-spam & Security', 'titan-security' );
83
  }
84
 
85
  /**
86
  * {@inheritDoc}
 
 
87
  * @param $notices
88
+ * @param \Wbcr_Factory426_Plugin $plugin
89
  *
90
  * @return array
91
+ * @since 6.5.2
92
+ *
93
+ * @see \FactoryPages426_ImpressiveThemplate
94
  */
95
+ public function getActionNotices($notices)
96
+ {
97
 
98
  $notices[] = [
99
  'conditions' => [
100
+ 'wtitan_trial_activated' => 1
101
  ],
102
+ 'type' => 'success',
103
+ 'message' => __('Trial is activated successfully!', 'titan-security')
104
+
105
  ];
106
 
107
  $notices[] = [
108
  'conditions' => [
109
+ 'wtitan_trial_activated_error' => 1,
110
+ 'wtitan_error_code' => 'interal_error'
111
  ],
112
+ 'type' => 'danger',
113
+ 'message' => __('An unknown error occurred during trial activation. Details of the error are wrote in error log.', 'titan-security')
114
+
115
  ];
116
 
117
  $notices[] = [
118
  'conditions' => [
119
+ 'wtitan_trial_activated_error' => 1,
120
+ 'wtitan_error_code' => 'trial_already_activated'
121
  ],
122
+ 'type' => 'danger',
123
+ 'message' => sprintf(__('You have already activated the trial earlier, you cannot activate the trial more than once. However, if your key has not expired yet, you can find it in your account (<a href="%s" target="_blank" rel="noopener noreferrer">%s</a>), and then insert the key in the license form to activate the premium plugin. To restore access to your account, use your admin email.', 'titan-security'), 'https://users.freemius.com/login', 'https://users.freemius.com/login')
124
  ];
125
 
126
  return $notices;
129
  /**
130
  * {@inheritdoc}
131
  *
 
132
  * @return void
133
+ * @since 6.5.2
134
  */
135
+ public function assets($scripts, $styles)
136
+ {
137
+ parent::assets($scripts, $styles);
138
+
139
+ $this->styles->add(WTITAN_PLUGIN_URL . '/admin/assets/css/titan-security.css');
140
+ if( !$this->plugin->premium->is_activate() ) {
141
+ $this->styles->add(WTITAN_PLUGIN_URL . '/admin/assets/css/libs/sweetalert2.css');
142
+ $this->styles->add(WTITAN_PLUGIN_URL . '/admin/assets/css/sweetalert-custom.css');
143
+
144
+ $this->scripts->add(WTITAN_PLUGIN_URL . '/admin/assets/js/libs/sweetalert3.min.js');
145
+ $this->scripts->add(WTITAN_PLUGIN_URL . '/admin/assets/js/trial-popup.js');
146
+ $this->scripts->localize('wtitan', [
147
+ 'trial_nonce' => wp_create_nonce("activate_trial"),
148
+ ]);
149
  }
150
  }
151
 
152
  /**
153
  * {@inheritdoc}
154
  *
 
155
  * @return void
156
+ * @since 6.5.2
157
  */
158
+
159
+ public function print_confirmation_modal_tpl()
160
+ {
161
+ if( isset($_GET['page']) && $this->getResultId() === $_GET['page'] ) {
162
+ $terms_url = "https://titansitescanner.com/terms-of-use/";
163
+ $privacy_url = "https://titansitescanner.com/privacy/";
164
 
165
  ?>
166
+ <script type="text/html" id="wtitan-tmpl-confirmation-modal">
167
+ <h2 class="swal2-title">
168
+ <?php _e('Confirmation', 'titan-security') ?>
169
+ </h2>
170
+ <div class="wtitan-swal-content">
171
+ <ul class="wtitan-list-infos" style="padding: 5px 20px;">
172
+ <li>
173
+ <?php _e('We are using some personal data, like your\'s e-mail.', 'titan-security') ?>
174
+ </li>
175
+ <li>
176
+ <?php printf(__('By agreeing to the trial, you confirm that you have read <a href="%s" target="_blank" rel="noreferrer noopener">Terms of Service</a> and the
177
+ <a href="%s" target="_blank" rel="noreferrer noopener">Privacy Policy (GDPR compilant)</a>', 'titan-security'), $terms_url, $privacy_url) ?>
178
+ </li>
179
+ <li>
180
+ <label for="wtitan-trial-email">Enter your E-mail:</label>
181
+ <input type="text" style="margin-top: 15px; padding: 5px;" value="<?php echo get_option('admin_email'); ?>" id="wtitan-trial-email">
182
+ <?php
183
+ //_e( 'We are using some personal data, like your\'s e-mail.', 'titan-security' );
184
+ ?>
185
+ </li>
186
+ </ul>
187
+ </div>
188
+ </script>
189
+
190
  <?php
191
  }
192
  }
195
  /**
196
  * {@inheritdoc}
197
  *
 
 
198
  * @return string
199
+ * @since 6.5
200
+ * @author Alexander Kovalev <alex.kovalevv@gmail.com>
201
  */
202
+ public function get_plan_description()
203
+ {
204
+ $activate_trial_url = wp_nonce_url($this->getActionUrl('activate-trial'), 'activate_trial');
205
+
206
+ $description = "";
207
+ // $description = '<p style="font-size: 16px;">' . __( '<b>Anti-spam PRO</b> is a paid package of components for the popular free WordPress plugin named Anti-spam PRO. You get access to all paid components at one price.', 'titan-security' ) . '</p>';
208
+ // $description .= '<p style="font-size: 16px;">' . __( 'Paid license guarantees that you can download and update existing and future paid components of the plugin.', 'titan-security' ) . '</p>';
209
+ if( !$this->plugin->premium->is_activate() ) {
210
  $description .= '<p>The free trial edition (no credit card) contains all of the features included in the paid-for version
211
  of the product.</p>';
212
+ $description .= '<button id="wtitan-activate-trial-button" class="btn btn-primary">' . __('Activate 30 days trial', 'titan-security') . '</button>';
213
+ $description .= "<span class='wt-spinner'></span>";
214
  }
215
 
216
  return $description;
217
  }
218
 
219
+ /**
220
+ * @author Egor Semenkov <highskil395472@gmail.ru>
221
+ * @since 7.0.8
222
+ */
223
+ public function render_learnmore_section()
224
+ {
225
+ if( $this->is_premium ):
226
+ ?>
227
+ <p style="margin-top: 10px;">
228
+ <?php printf(__('<a href="%s" target="_blank" rel="noopener">Lean more</a> about the premium version and get the license key to activate it now!', 'wbcr_factory_clearfy_218'), $this->plugin->get_support()->get_pricing_url(true, 'license_page')); ?>
229
+ </p>
230
+ <?php else: ?>
231
+ <p style="margin-top: 10px;">
232
+ <?php printf(__('Can’t find your key? Go to <a href="%s" target="_blank" rel="noopener">this page</a> and login using the e-mail address associated with your purchase.', 'wbcr_factory_clearfy_218'), "https://users.freemius.com/") ?>
233
+ </p>
234
+ <p style="margin-top: 10px;">
235
+ <?php printf(__('We use certain personal data, such as your email address. By activating the license, you confirm that you have read the <a href="https://titansitescanner.com/terms-of-use/">Terms of Service</a> and <a href="https://titansitescanner.com/privacy/">Privacy Policy (in accordance with GDPR)</a>', 'wbcr_factory_clearfy_218'), $this->plugin->get_support()->get_contacts_url(true, 'license_page')) ?>
236
+ </p>
237
+ <?php endif;
238
+ }
239
+
240
  /**
241
+ * @author Artem Prihodko <webtemyk@yandex.ru>
242
+ * @since 7.0
243
  */
244
+
245
+ public function activate_trial()
246
+ {
247
+ if( !current_user_can('manage_options') ) {
248
+ return;
249
+ }
250
+
251
+ if( !isset($_POST['email']) || empty($_POST['email']) ) {
252
  return;
253
  }
254
 
255
+ check_ajax_referer('activate_trial');
256
 
257
+ \WBCR\Titan\Logger\Writter::info('Start trial activation [PROCESS START]!');
258
 
259
+ $admin_email = $_POST['email'];
260
+ $domain = site_url();
261
 
262
  //$url = 'https://dev.anti-spam.space/api/v1.0/trial/register';
263
  $url = 'https://api.anti-spam.space/api/v1.0/trial/register';
264
 
265
  $options = [
266
  'body' => [
267
+ 'email' => $admin_email,
268
  'domain' => site_url(),
269
  ]
270
  ];
271
 
272
  // Get license key from remote server
273
+
274
+ $request = wp_remote_post($url, $options);
275
+
276
+ if( is_wp_error($request) ) {
277
+ \WBCR\Titan\Logger\Writter::error('Http request error: ' . $request->get_error_message());
278
+ \WBCR\Titan\Logger\Writter::info('End trial activation [PROCESS END]!');
279
+
280
+ wp_send_json_error([
281
+ 'url' => $this->getPageUrl() . "&wtitan_trial_activated_error=1&wtitan_error_code=interal_error",
282
+ ]);
283
  }
284
 
285
+ $data = json_decode($request['body'], true);
286
 
287
+ if( $data['status'] == 'fail' ) {
288
+ if( !empty($data['error']) ) {
289
  $message = $data['error']['message'];
290
+
291
+ \WBCR\Titan\Logger\Writter::error(sprintf('Trial activation failed for domain: %s, e-mail: %s with message: %s', $domain, $admin_email, $message));
292
+ \WBCR\Titan\Logger\Writter::info('End trial activation [PROCESS END]!');
293
+
294
+ if( !empty($data['error']['code']) && 1001 === $data['error']['code'] ) {
295
+ wp_send_json_error([
296
+ 'url' => $this->getPageUrl() . "&wtitan_trial_activated_error=1&wtitan_error_code=trial_already_activated",
297
+ ]);
298
  } else {
299
+ wp_send_json_error([
300
+ 'url' => $this->getPageUrl() . "&wtitan_trial_activated_error=1&wtitan_error_code=interal_error",
301
+ ]);
 
302
  }
303
  }
304
  }
305
 
306
  $license_key = $data['response']['license_key'];
307
 
308
+ if( empty($license_key) || 32 !== strlen($license_key) ) {
309
+ \WBCR\Titan\Logger\Writter::error('License key format is not valid');
310
+ \WBCR\Titan\Logger\Writter::info('End trial activation [PROCESS END]!');
311
+ wp_send_json_error([
312
+ 'url' => $this->getPageUrl() . "&wtitan_trial_activated_error=1&wtitan_error_code=interal_error",
313
+ ]);
 
314
  }
315
 
316
  try {
317
+ $this->plugin->premium->activate($license_key);
318
+
319
+ \WBCR\Titan\Logger\Writter::info(sprintf('Trial activation success for domain: %s, e-mail: %s', $domain, $admin_email));
320
+ \WBCR\Titan\Logger\Writter::info('End trial activation [PROCESS END]!');
321
 
322
+ wp_send_json_error([
323
+ 'url' => $this->getPageUrl() . "&wtitan_trial_activated=1",
324
+ ]);
 
 
325
  } catch( \Exception $e ) {
326
+ \WBCR\Titan\Logger\Writter::error($e->getMessage());
327
+ \WBCR\Titan\Logger\Writter::info('End trial activation [PROCESS END]!');
328
 
329
+ wp_send_json_error([
330
+ 'url' => $this->getPageUrl() . "&wtitan_trial_activated_error=1&wtitan_error_code=interal_error",
331
+ ]);
 
332
  }
333
 
334
+ \WBCR\Titan\Logger\Writter::info('End trial activation [PROCESS END]!');
335
+
336
  // Redirect to index
337
+ wp_send_json_error([
338
+ 'url' => $this->getPageUrl(),
339
+ ]);
340
  }
341
  }
admin/pages/class-pages-logs.php CHANGED
@@ -1,9 +1,9 @@
1
  <?php
2
 
3
- namespace WBCR\Antispam\Page;
4
 
5
  // Exit if accessed directly
6
- if ( ! defined( 'ABSPATH' ) ) {
7
  exit;
8
  }
9
 
@@ -16,7 +16,7 @@ if ( ! defined( 'ABSPATH' ) ) {
16
  * @copyright (c) 2019 Webraftic Ltd
17
  * @version 1.0
18
  */
19
- class Logs extends \Wbcr_FactoryClearfy217_PageBase {
20
 
21
  /**
22
  * {@inheritdoc}
@@ -26,66 +26,83 @@ class Logs extends \Wbcr_FactoryClearfy217_PageBase {
26
  /**
27
  * {@inheritdoc}
28
  */
29
- public $page_menu_dashicon = 'dashicons-admin-tools';
30
 
31
  /**
32
  * {@inheritdoc}
33
  */
34
  public $type = 'page';
35
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
36
  /**
37
  * Logs constructor.
38
  *
 
39
  * @author Alexander Kovalev <alex.kovalevv@gmail.com>
40
  *
41
- * @param \Wbcr_Factory425_Plugin $plugin
42
  */
43
- public function __construct( \Wbcr_Factory425_Plugin $plugin ) {
 
44
 
45
- $this->menu_title = __( 'Error Log', 'anti-spam' );
46
- $this->page_menu_short_description = __( 'Plugin debug report', 'anti-spam' );
47
 
48
- parent::__construct( $plugin );
49
  }
50
 
51
  /**
52
  * {@inheritdoc}
53
  *
54
- * @since 1.0.0
55
  * @return void
 
56
  */
57
- public function assets( $scripts, $styles ) {
58
- parent::assets( $scripts, $styles );
 
59
 
60
- $this->styles->add( WANTISPAM_PLUGIN_URL . '/includes/logger/assets/css/base.css' );
61
- $this->scripts->add( WANTISPAM_PLUGIN_URL . '/includes/logger/assets/js/base.js' );
62
  }
63
 
64
  /**
65
  * {@inheritdoc}
66
  */
67
- public function showPageContent() {
68
- require_once( WANTISPAM_PLUGIN_DIR . '/includes/logger/class-logger-reader.php' );
 
69
  ?>
70
- <div class="wbcr-factory-page-group-header">
71
- <strong><?php _e( 'Error Log', 'anti-spam' ) ?></strong>
72
- <p>
73
- <?php _e( 'In this section, you can track image optimization errors. Sending this log to us, will help in solving possible optimization issues.', 'anti-spam' ) ?>
74
- </p>
75
- </div>
76
- <div class="wbcr-factory-page-group-body">
77
- <div class="btn-group">
78
- <a href="<?php echo wp_nonce_url( $this->getPageUrl() . 'action=export' ) ?>"
79
- class="btn btn-default"><?php _e( 'Export Debug Information', 'anti-spam' ) ?></a>
80
- <a href="#"
81
- data-working="<?php echo esc_attr__( 'Working...', 'anti-spam' ) ?>"
82
- data-nonce="<?php echo wp_create_nonce( 'wlogger_clean_logs' ) ?>"
83
- class="btn btn-default js-wlogger-export-debug-report"><?php echo sprintf( __( 'Clean-up Logs (<span id="js-wlogger-size">%s</span>)', 'anti-spam' ), $this->get_log_size_formatted() ) ?></a>
84
- </div>
85
- <div class="wlogger-viewer" id="js-wlogger-viewer">
86
- <?php echo \WBCR\Logger\Reader::prettify() ?>
87
- </div>
88
- </div>
89
  <?php
90
  }
91
 
@@ -94,27 +111,29 @@ class Logs extends \Wbcr_FactoryClearfy217_PageBase {
94
  *
95
  * @since 6.0
96
  */
97
- public function exportAction() {
98
- require_once( WANTISPAM_PLUGIN_DIR . '/includes/logger/class-logger-export.php' );
99
- $export = new \WBCR\Logger\Export();
 
100
 
101
- if ( $export->prepare() ) {
102
- $export->download( true );
103
  }
104
  }
105
 
106
  /**
107
  * Get log size formatted.
108
  *
109
- * @since 6.0
110
  * @return false|string
 
111
  */
112
- private function get_log_size_formatted() {
 
113
 
114
  try {
115
- return size_format( \WBCR\Logger\Writter::get_total_size() );
116
  } catch( \Exception $exception ) {
117
- \WBCR\Logger\Writter::error( sprintf( 'Failed to get total log size as exception was thrown: %s', $exception->getMessage() ) );
118
  }
119
 
120
  return '';
1
  <?php
2
 
3
+ namespace WBCR\Titan\Page;
4
 
5
  // Exit if accessed directly
6
+ if( !defined('ABSPATH') ) {
7
  exit;
8
  }
9
 
16
  * @copyright (c) 2019 Webraftic Ltd
17
  * @version 1.0
18
  */
19
+ class Logs extends Base {
20
 
21
  /**
22
  * {@inheritdoc}
26
  /**
27
  * {@inheritdoc}
28
  */
29
+ public $page_menu_dashicon = 'dashicons-list-view';
30
 
31
  /**
32
  * {@inheritdoc}
33
  */
34
  public $type = 'page';
35
 
36
+ /**
37
+ * {@inheritdoc}
38
+ */
39
+ public $page_menu_position = 2;
40
+
41
+
42
+ /**
43
+ * {@inheritDoc}
44
+ *
45
+ * @since 6.0
46
+ * @var bool
47
+ */
48
+ public $show_right_sidebar_in_options = false;
49
+
50
  /**
51
  * Logs constructor.
52
  *
53
+ * @param \Wbcr_Factory426_Plugin $plugin
54
  * @author Alexander Kovalev <alex.kovalevv@gmail.com>
55
  *
 
56
  */
57
+ public function __construct(\Wbcr_Factory426_Plugin $plugin)
58
+ {
59
 
60
+ $this->menu_title = __('Error Log', 'titan-security');
61
+ $this->page_menu_short_description = __('Plugin debug report', 'titan-security');
62
 
63
+ parent::__construct($plugin);
64
  }
65
 
66
  /**
67
  * {@inheritdoc}
68
  *
 
69
  * @return void
70
+ * @since 1.0.0
71
  */
72
+ public function assets($scripts, $styles)
73
+ {
74
+ parent::assets($scripts, $styles);
75
 
76
+ $this->styles->add(WTITAN_PLUGIN_URL . '/includes/logger/assets/css/base.css');
77
+ $this->scripts->add(WTITAN_PLUGIN_URL . '/includes/logger/assets/js/base.js', ['jquery']);
78
  }
79
 
80
  /**
81
  * {@inheritdoc}
82
  */
83
+ public function showPageContent()
84
+ {
85
+ require_once(WTITAN_PLUGIN_DIR . '/includes/logger/class-logger-reader.php');
86
  ?>
87
+ <div class="wbcr-factory-page-group-header">
88
+ <strong><?php _e('Error Log', 'titan-security') ?></strong>
89
+ <p>
90
+ <?php _e('In this section, you can track errors. Sending this log to us, will help in solving possible issues.', 'titan-security') ?>
91
+ </p>
92
+ </div>
93
+ <div class="wbcr-factory-page-group-body">
94
+ <div class="btn-group">
95
+ <a href="<?php echo wp_nonce_url($this->getPageUrl() . 'action=export') ?>"
96
+ class="btn btn-default"><?php _e('Export Debug Information', 'titan-security') ?></a>
97
+ <a href="#"
98
+ data-working="<?php echo esc_attr__('Working...', 'titan-security') ?>"
99
+ data-nonce="<?php echo wp_create_nonce('wlogger_clean_logs') ?>"
100
+ class="btn btn-default js-wlogger-export-debug-report"><?php echo sprintf(__('Clean-up Logs (<span id="js-wlogger-size">%s</span>)', 'titan-security'), $this->get_log_size_formatted()) ?></a>
101
+ </div>
102
+ <div class="wlogger-viewer" id="js-wlogger-viewer">
103
+ <?php echo \WBCR\Titan\Logger\Reader::prettify() ?>
104
+ </div>
105
+ </div>
106
  <?php
107
  }
108
 
111
  *
112
  * @since 6.0
113
  */
114
+ public function exportAction()
115
+ {
116
+ require_once(WTITAN_PLUGIN_DIR . '/includes/logger/class-logger-export.php');
117
+ $export = new \WBCR\Titan\Logger\Export();
118
 
119
+ if( $export->prepare() ) {
120
+ $export->download(true);
121
  }
122
  }
123
 
124
  /**
125
  * Get log size formatted.
126
  *
 
127
  * @return false|string
128
+ * @since 6.0
129
  */
130
+ private function get_log_size_formatted()
131
+ {
132
 
133
  try {
134
+ return size_format(\WBCR\Titan\Logger\Writter::get_total_size());
135
  } catch( \Exception $exception ) {
136
+ \WBCR\Titan\Logger\Writter::error(sprintf('Failed to get total log size as exception was thrown: %s', $exception->getMessage()));
137
  }
138
 
139
  return '';
admin/pages/class-pages-plugin-settings.php ADDED
@@ -0,0 +1,511 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace WBCR\Titan\Page;
4
+
5
+ use WBCR\Titan\Plugin;
6
+ use WBCR\Titan\Plugin\Helper;
7
+ use Wbcr_Factory426_Plugin;
8
+
9
+ /**
10
+ * The plugin Settings.
11
+ *
12
+ * @since 7.0.0
13
+ */
14
+
15
+ // Exit if accessed directly
16
+ if ( ! defined( 'ABSPATH' ) ) {
17
+ exit;
18
+ }
19
+
20
+ class PluginSettings extends Base {
21
+
22
+ /**
23
+ * The id of the page in the admin menu.
24
+ *
25
+ * Mainly used to navigate between pages.
26
+ *
27
+ * @since 7.0.0
28
+ *
29
+ * @var string
30
+ */
31
+ public $id = "plugin_settings";
32
+
33
+ /**
34
+ * @var string
35
+ */
36
+ public $page_menu_dashicon = 'dashicons-admin-generic';
37
+
38
+ /**
39
+ * {@inheritdoc}
40
+ */
41
+ public $show_right_sidebar_in_options = false;
42
+
43
+ /**
44
+ * {@inheritdoc}
45
+ */
46
+ public $page_menu_position = 1;
47
+
48
+ /**
49
+ * @param Wbcr_Factory426_Plugin $plugin
50
+ */
51
+ public function __construct( Wbcr_Factory426_Plugin $plugin ) {
52
+ $this->menu_title = __( 'Settings', 'titan-security' );
53
+ $this->page_menu_short_description = __( 'Global plugin settings', 'titan-security' );
54
+
55
+ parent::__construct( $plugin );
56
+
57
+ $this->plugin = $plugin;
58
+
59
+ add_action('wp_ajax_wtitan_import_settings', [$this, 'import_settings']);
60
+ }
61
+
62
+
63
+ /**
64
+ * Requests assets (js and css) for the page.
65
+ *
66
+ * @return void
67
+ *
68
+ * @since 7.0.0
69
+ */
70
+ public function assets( $scripts, $styles ) {
71
+ parent::assets( $scripts, $styles );
72
+
73
+ $this->scripts->add( WTITAN_PLUGIN_URL.'/admin/assets/js/libs/jquery.datetimepicker.full.min.js' );
74
+ $this->styles->add( WTITAN_PLUGIN_URL.'/admin/assets/css/libs/jquery.datetimepicker.min.css' );
75
+
76
+ $this->scripts->add( WTITAN_PLUGIN_URL . '/admin/assets/js/import.js' );
77
+ $this->scripts->add( WTITAN_PLUGIN_URL . '/admin/assets/js/settings.js' );
78
+
79
+ //$this->scripts->add( WTITAN_PLUGIN_URL.'/admin/assets/js/bootstrap-datepicker.min.js' );
80
+ //$this->styles->add( WTITAN_PLUGIN_URL.'/admin/assets/css/bootstrap-datepicker.min.css' );
81
+
82
+ $params = [
83
+ 'import_options_nonce' => wp_create_nonce( 'wtitan_import_options' ),
84
+ 'i18n' => [
85
+ 'success_update_settings' => __( 'Settings successfully updated!', 'titan-security' ),
86
+ 'unknown_error' => __( 'During the setup, an unknown error occurred, please try again or contact the plugin support.', 'titan-security' ),
87
+ ]
88
+ ];
89
+ wp_localize_script( 'jquery', 'wtitan_ajax', $params );
90
+
91
+ }
92
+
93
+ /**
94
+ * Permalinks options.
95
+ *
96
+ * @return mixed[]
97
+ * @since 7.0.0
98
+ */
99
+ public function getPageOptions() {
100
+ $is_premium = $this->plugin->is_premium();
101
+ $options = [];
102
+
103
+ $options[] = [
104
+ 'type' => 'html',
105
+ 'html' => '<div class="wbcr-factory-page-group-header">' . '<strong>' . __( 'Advanced settings', 'titan-security' ) . '</strong>' .
106
+ '<p>' . __( 'This group of settings allows you to configure the work of the plugin.', 'titan-security' ) . '</p>' . '</div>'
107
+ ];
108
+
109
+ $options[] = [
110
+ 'type' => 'checkbox',
111
+ 'way' => 'buttons',
112
+ 'name' => 'extra_menu',
113
+ 'title' => __( 'Plugin menu in adminbar', 'titan-security' ),
114
+ 'layout' => [ 'hint-type' => 'icon', 'hint-icon-color' => 'grey' ],
115
+ 'hint' => __( 'This setting allows you to enable/disable the additional menu of the plugin, in the admin bar.', 'titan-security' ),
116
+ 'default' => false
117
+ ];
118
+
119
+ $options[] = [
120
+ 'type' => 'checkbox',
121
+ 'way' => 'buttons',
122
+ 'name' => 'complete_uninstall',
123
+ 'title' => __( 'Complete Uninstall', 'titan-security' ),
124
+ 'layout' => [ 'hint-type' => 'icon', 'hint-icon-color' => 'grey' ],
125
+ 'hint' => __( "When the plugin is deleted from the Plugins menu, also delete all plugin settings.", 'titan-security' ),
126
+ 'default' => false
127
+ ];
128
+
129
+ $options[] = [
130
+ 'type' => 'html',
131
+ 'html' => '<div class="wbcr-factory-page-group-header">' . '<strong>' . __( 'Antivirus settings', 'titan-security' ) . '</strong>' .
132
+ '<p>' . __( 'This group of settings allows you to configure the work of the plugin.', 'titan-security' ) . '</p>' . '</div>'
133
+ ];
134
+
135
+ $data = [
136
+ [ \WBCR\Titan\MalwareScanner\Scanner::SPEED_FREE, __( 'Free', 'titan-security' ) ],
137
+ [ \WBCR\Titan\MalwareScanner\Scanner::SPEED_SLOW, __( 'Slow', 'titan-security' ) ],
138
+ [ \WBCR\Titan\MalwareScanner\Scanner::SPEED_MEDIUM, __( 'Medium', 'titan-security' ) ],
139
+ [ \WBCR\Titan\MalwareScanner\Scanner::SPEED_FAST, __( 'Fast', 'titan-security' )] ,
140
+ ];
141
+
142
+ $options[] = [
143
+ 'type' => 'dropdown',
144
+ 'way' => 'buttons',
145
+ 'name' => 'scanner_type',
146
+ 'title' => __( 'Scanning type', 'titan-security' ),
147
+ 'layout' => [ 'hint-type' => 'icon', 'hint-icon-color' => 'grey' ],
148
+ 'hint' => __( "The basic scan includes fewer signatures", 'titan-security' ) . "<br>" .
149
+ __("Advanced scanning includes a full set of signatures", 'titan-security'),
150
+ 'data' => [
151
+ [ 'basic', __( 'Basic scan', 'titan-security' ) ],
152
+ [ 'advanced', __( 'Advanced scan', 'titan-security' ) ],
153
+ ],
154
+ 'cssClass' => ! $is_premium ? [ 'factory-checkbox--disabled wtitan-control-premium-label' ] : [],
155
+ 'default' => $this->plugin->is_premium() ? 'advanced' : 'basic',
156
+ ];
157
+
158
+ $options[] = [
159
+ 'type' => 'dropdown',
160
+ 'way' => 'buttons',
161
+ 'name' => 'scanner_speed',
162
+ 'title' => __( 'Scanning speed', 'titan-security' ),
163
+ 'layout' => [ 'hint-type' => 'icon', 'hint-icon-color' => 'grey' ],
164
+ 'hint' => __( "The speed of scanning affects the resources consumed", 'titan-security' ) . "<br>" .
165
+ __("Recommended speed: ", 'titan-security') . get_recommended_scanner_speed(),
166
+ 'data' => $data,
167
+ 'cssClass' => ! $is_premium ? [ 'factory-checkbox--disabled wtitan-control-premium-label' ] : [],
168
+ 'default' => $this->plugin->is_premium() ? \WBCR\Titan\MalwareScanner\Scanner::SPEED_SLOW : \WBCR\Titan\MalwareScanner\Scanner::SPEED_FREE,
169
+ ];
170
+
171
+ $data_schedule = [
172
+ [
173
+ \WBCR\Titan\MalwareScanner\Scanner::SCHEDULE_DISABLED,
174
+ __( 'Disabled', 'titan-security' ),
175
+ __( 'Disable scheduled scanning', 'titan-security' )
176
+ ],
177
+ [
178
+ \WBCR\Titan\MalwareScanner\Scanner::SCHEDULE_DAILY,
179
+ __( 'Daily', 'titan-security' ),
180
+ __( 'Scan every day', 'titan-security' )
181
+ ],
182
+ [
183
+ \WBCR\Titan\MalwareScanner\Scanner::SCHEDULE_WEEKLY,
184
+ __( 'Weekly', 'titan-security' ),
185
+ __( 'Scan every week', 'titan-security' )
186
+ ],
187
+ [
188
+ \WBCR\Titan\MalwareScanner\Scanner::SCHEDULE_CUSTOM,
189
+ __( 'Custom', 'titan-security' ),
190
+ __( 'Select the date and time of the next scan', 'titan-security' )
191
+ ],
192
+ ];
193
+
194
+ $options[] = [
195
+ 'type' => 'dropdown',
196
+ 'way' => 'buttons',
197
+ 'name' => 'scanner_schedule',
198
+ 'title' => __( 'Schedule scan', 'titan-security' ),
199
+ 'layout' => [ 'hint-type' => 'icon', 'hint-icon-color' => 'grey' ],
200
+ 'hint' => __( "The speed of scanning affects the resources consumed", 'titan-security' ),
201
+ 'data' => $data_schedule,
202
+ 'cssClass' => ! $is_premium ? [ 'factory-checkbox--disabled wtitan-control-premium-label' ] : [],
203
+ 'default' => \WBCR\Titan\MalwareScanner\Scanner::SCHEDULE_DISABLED,
204
+ ];
205
+
206
+ if( Plugin::app()->is_premium() ) {
207
+ /*
208
+ * Schedule settings
209
+ * */
210
+ $options[] = [
211
+ 'type' => 'html',
212
+ 'html' => '<div class="wt-schedule-controls wt-schedule-controls-daily">'
213
+ ];
214
+ $options[] = [
215
+ 'type' => 'textbox',
216
+ 'name' => 'scanner_schedule_daily',
217
+ 'title' => __( 'Time for the daily scan', 'titan-security' ),
218
+ 'layout' => [ 'hint-type' => 'icon', 'hint-icon-color' => 'grey' ],
219
+ 'hint' => __( "What time should start scanning", 'titan-security' ),
220
+ ];
221
+ $options[] = [ 'type' => 'html', 'html' => '</div>' ];
222
+
223
+ //------------------------------------------------------------------
224
+
225
+ $options[] = [
226
+ 'type' => 'html',
227
+ 'html' => '<div class="wt-schedule-controls wt-schedule-controls-weekly">'
228
+ ];
229
+ $data_schedule_week = [
230
+ [ 1, 'Monday' ],
231
+ [ 2, 'Tuesday' ],
232
+ [ 3, 'Wednesday' ],
233
+ [ 4, 'Thursday' ],
234
+ [ 5, 'Friday' ],
235
+ [ 6, 'Saturday' ],
236
+ [ 7, 'Sunday' ],
237
+ ];
238
+ $options[] = [
239
+ 'type' => 'dropdown',
240
+ 'way' => 'default',
241
+ 'name' => 'scanner_schedule_weekly_day',
242
+ 'title' => __( 'Day for the weekly scan', 'titan-security' ),
243
+ 'layout' => [ 'hint-type' => 'icon', 'hint-icon-color' => 'grey' ],
244
+ 'hint' => __( "Day of the week for the weekly scan", 'titan-security' ),
245
+ 'data' => $data_schedule_week,
246
+ 'default' => '',
247
+ ];
248
+
249
+ $options[] = [
250
+ 'type' => 'textbox',
251
+ 'name' => 'scanner_schedule_weekly_time',
252
+ 'title' => __( 'Time for the weekly scan', 'titan-security' ),
253
+ 'layout' => [ 'hint-type' => 'icon', 'hint-icon-color' => 'grey' ],
254
+ 'hint' => __( "Time for the weekly scan", 'titan-security' ),
255
+ ];
256
+ $options[] = [ 'type' => 'html', 'html' => '</div>' ];
257
+
258
+ //------------------------------------------------------------------
259
+
260
+ $options[] = [
261
+ 'type' => 'html',
262
+ 'html' => '<div class="wt-schedule-controls wt-schedule-controls-custom">'
263
+ ];
264
+ $options[] = [
265
+ 'type' => 'textbox',
266
+ 'name' => 'scanner_schedule_custom',
267
+ 'title' => __( 'Date and time for the Custom schedule', 'titan-security' ),
268
+ 'layout' => [ 'hint-type' => 'icon', 'hint-icon-color' => 'grey' ],
269
+ 'hint' => __( "Date and time of the custom scan schedule", 'titan-security' ),
270
+ ];
271
+ $options[] = [ 'type' => 'html', 'html' => '</div>' ];
272
+ //------------------------------------------------------------------
273
+ }
274
+
275
+ $options[] = [
276
+ 'type' => 'html',
277
+ 'html' => '<div class="wbcr-factory-page-group-header">' . '<strong>' . __( 'Import/Export', 'titan-security' ) . '</strong>' . '<p>' . __( 'This group of settings allows you to configure the work of the plugin.', 'titan-security' ) . '</p>' . '</div>'
278
+ ];
279
+
280
+ $options[] = [
281
+ 'type' => 'html',
282
+ 'html' => [ $this, 'export' ]
283
+ ];
284
+
285
+ $formOptions = [];
286
+
287
+ $formOptions[] = [
288
+ 'type' => 'form-group',
289
+ 'items' => $options,
290
+ //'cssClass' => 'postbox'
291
+ ];
292
+
293
+ return apply_filters( 'wtitan/settings_form_options', $formOptions, $this );
294
+ }
295
+
296
+ /**
297
+ * Export settings
298
+ */
299
+ public function export() {
300
+ ?>
301
+ <div class="wbcr-titan-export-import">
302
+ <p>
303
+ <label for="wbcr-titan-export-textarea">
304
+ <strong><?php _e( 'Import/Export settings', 'titan-security' ) ?></strong>
305
+ </label>
306
+ <textarea id="wbcr-titan-export-textarea"><?php echo $this->getExportOptions(); ?></textarea>
307
+ <button class="button wtitan-import-options-button"><?php _e( 'Import options', 'titan-security' ) ?></button>
308
+ </p>
309
+ </div>
310
+ <?php
311
+ }
312
+
313
+ /**
314
+ * Получает и возвращает все опции разрешенные для экспорта
315
+ *
316
+ * @param string $return
317
+ * @return array|string
318
+ */
319
+ public function getExportOptions($return = 'json')
320
+ {
321
+ $export_options = $this->getAllowOptions();
322
+
323
+ if( $return == 'array' ) {
324
+ return $export_options;
325
+ }
326
+
327
+ return htmlspecialchars(json_encode($export_options), ENT_QUOTES, 'UTF-8');
328
+ }
329
+
330
+ /**
331
+ * Ajax действите, выполняется для получения всех доступных опций для экспорта.
332
+ */
333
+ public function import_settings()
334
+ {
335
+ require_once WTITAN_PLUGIN_DIR."/includes/helpers.php";
336
+
337
+ global $wpdb;
338
+
339
+ check_ajax_referer('wtitan_import_options');
340
+
341
+ if( !$this->plugin->currentUserCan() ) {
342
+ wp_send_json_error(array('error_message' => __('You don\'t have enough capability to edit this information.', 'titan-security')));
343
+ die();
344
+ }
345
+
346
+ $settings = Helper::maybeGetPostJson('settings');
347
+
348
+ /**
349
+ * Используется для фильтрации импортируемых настроек,
350
+ * обычно это может пригодиться для компонентов, которым нужно выполнить дополнительные дествия к опциям,
351
+ * прежде чем продолжить импорт
352
+ *
353
+ * wtitan/filter_import_options
354
+ * @since 1.4.0
355
+ */
356
+ $settings = apply_filters('wtitan/filter_import_options', $settings);
357
+
358
+ $network_id = get_current_network_id();
359
+
360
+ if( empty($settings) || !is_array($settings) ) {
361
+ wp_send_json_error(array('error_message' => __('Settings are not defined or do not exist.', 'titan-security')));
362
+ die();
363
+ }
364
+
365
+ $values = array();
366
+ $place_holders = array();
367
+
368
+ if( $this->plugin->isNetworkActive() ) {
369
+ $query = "INSERT INTO {$wpdb->sitemeta} (site_id, meta_key, meta_value) VALUES ";
370
+ } else {
371
+ $query = "INSERT INTO {$wpdb->options} (option_name, option_value) VALUES ";
372
+ }
373
+
374
+ foreach($settings as $option_name => $option_value) {
375
+ $option_name = sanitize_text_field($option_name);
376
+ $raw_option_value = $option_value;
377
+
378
+ if( is_serialized($option_value) ) {
379
+ $option_value = unserialize($option_value);
380
+ }
381
+
382
+ if( is_array($option_value) || is_object($option_value) ) {
383
+ $option_value = Helper::recursiveSanitizeArray($option_value, 'wp_kses_post');
384
+ $option_value = maybe_serialize($option_value);
385
+ } else {
386
+ $option_value = wp_kses_post($option_value);
387
+ }
388
+
389
+ /**
390
+ * Используется для фильтрации импортируемых значений,
391
+ * обычно это может пригодиться для компонентов, которым нужно подменять домены, пути или какие-то правила
392
+ * при переносе с одного сайта на другой
393
+ *
394
+ * wtitan/filter_import_values
395
+ * @since 1.4.0
396
+ */
397
+ $option_value = apply_filters('wtitan/filter_import_values', $option_value, $option_name, $raw_option_value);
398
+
399
+ if( $this->plugin->isNetworkActive() ) {
400
+ array_push($values, $network_id, $option_name, $option_value);
401
+ $place_holders[] = "('%d', '%s', '%s')";/* In my case, i know they will always be integers */
402
+ } else {
403
+ array_push($values, $option_name, $option_value);
404
+ $place_holders[] = "('%s', '%s')";/* In my case, i know they will always be integers */
405
+ }
406
+ }
407
+
408
+ $query .= implode(', ', $place_holders);
409
+
410
+ // Удаляем все опции
411
+ $all_options = $this->getAllowOptions(false);
412
+
413
+ if( !empty($all_options) ) {
414
+ foreach($all_options as $name => $value) {
415
+ $this->plugin->deletePopulateOption($name);
416
+ }
417
+ }
418
+
419
+ // Сбрасываем кеш опций
420
+ $this->plugin->flushOptionsCache();
421
+
422
+ // Импортируем опции
423
+ $wpdb->query($wpdb->prepare("$query ", $values));
424
+
425
+ $send_data = array('status' => 'success');
426
+
427
+ //$package_plugin = WCL_Package::instance();
428
+ //$send_data['update_notice'] = $package_plugin->getUpdateNotice();
429
+
430
+ // Сбрасываем кеш для кеширующих плагинов
431
+ Helper::flushPageCache();
432
+
433
+ do_action('wtitan_imported_settings');
434
+
435
+ wp_send_json_success($send_data);
436
+ die();
437
+ }
438
+
439
+ /**
440
+ * @param bool
441
+ */
442
+ public function getAllowOptions($with_prefix = true)
443
+ {
444
+ global $wpdb;
445
+
446
+ $result = array();
447
+
448
+ $excluded_options = array(
449
+ 'plugin_activated',
450
+ 'plugin_version',
451
+ 'audit_results_hided',
452
+ 'audit_results',
453
+ 'vulnerabilities_wordpress',
454
+ 'vulnerabilities_plugins',
455
+ 'vulnerabilities_themes',
456
+ 'scanner',
457
+ 'scanner_malware_matched',
458
+ 'scanner_files_count',
459
+ 'scanner_status',
460
+ 'files_hash',
461
+ 'freemius_api_clock_diff',
462
+ 'what_is_new_64',
463
+ 'license',
464
+ 'stats_transient_',
465
+ );
466
+
467
+ foreach($excluded_options as $key => $option) {
468
+ $excluded_options[$key] = $this->plugin->getOptionName($option);
469
+ }
470
+
471
+ if( $this->plugin->isNetworkActive() ) {
472
+ $network_id = get_current_network_id();
473
+
474
+ $request = $wpdb->get_results($wpdb->prepare("
475
+ SELECT meta_key, meta_value
476
+ FROM {$wpdb->sitemeta}
477
+ WHERE site_id = '%d' AND meta_key
478
+ LIKE '%s'", $network_id, $this->plugin->getPrefix() . "%"));
479
+ } else {
480
+ $request = $wpdb->get_results($wpdb->prepare("
481
+ SELECT option_name, option_value
482
+ FROM {$wpdb->options}
483
+ WHERE option_name
484
+ LIKE '%s'", $this->plugin->getPrefix() . "_%"));
485
+ }
486
+
487
+ if( !empty($request) ) {
488
+ foreach($request as $option) {
489
+ if( $this->plugin->isNetworkActive() ) {
490
+ $option_name = $option->meta_key;
491
+ $option_value = $option->meta_value;
492
+ } else {
493
+ $option_name = $option->option_name;
494
+ $option_value = $option->option_value;
495
+ }
496
+ if(!in_array($option_name, $excluded_options)) {
497
+ $result[ $option_name ] = $option_value;
498
+ }
499
+ }
500
+ }
501
+ if(!$with_prefix)
502
+ foreach($result as $key => $option) {
503
+ unset($result[$key]);
504
+ $k = preg_replace( '/^titan_/', '', $key);
505
+ $result[$k] = $option;
506
+ }
507
+
508
+ return $result;
509
+ }
510
+
511
+ }
admin/pages/class-pages-scanner.php ADDED
@@ -0,0 +1,114 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace WBCR\Titan\Page;
4
+
5
+ use WBCR\Titan;
6
+
7
+ use Wbcr_Factory426_Plugin;
8
+ use Wbcr_FactoryClearfy218_PageBase;
9
+
10
+ if( !defined('ABSPATH') ) {
11
+ exit;
12
+ }
13
+
14
+ class Scanner extends Base {
15
+
16
+ /**
17
+ * {@inheritDoc}
18
+ */
19
+ public $id = 'scanner';
20
+
21
+ /**
22
+ * {@inheritDoc}
23
+ */
24
+ public $page_menu_dashicon = 'dashicons-code-standards';
25
+
26
+ /**
27
+ * {@inheritDoc}
28
+ */
29
+ public $type = 'page';
30
+
31
+ /**
32
+ * {@inheritDoc}
33
+ */
34
+ public $show_right_sidebar_in_options = false;
35
+
36
+ /**
37
+ * Module folder URL
38
+ */
39
+ public $MODULE_URL = WTITAN_PLUGIN_URL . "/includes/scanner";
40
+
41
+ /**
42
+ * Path to module files
43
+ *
44
+ * @since 7.0
45
+ * @var bool
46
+ */
47
+ public $MODULE_PATH = WTITAN_PLUGIN_DIR . "/includes/scanner";
48
+
49
+ /**
50
+ * Path to module files
51
+ *
52
+ * @since 7.0
53
+ * @var object
54
+ */
55
+ public $module;
56
+
57
+ /**
58
+ * Scanner constructor.
59
+ *
60
+ * @param Wbcr_Factory426_Plugin $plugin
61
+ *
62
+ */
63
+ public function __construct(Wbcr_Factory426_Plugin $plugin)
64
+ {
65
+ $this->plugin = $plugin;
66
+
67
+ $this->menu_title = __('Scanner', 'titan-security');
68
+ $this->page_menu_short_description = __('Checking site for viruses', 'titan-security');
69
+
70
+ if( $this->plugin->is_premium() ) {
71
+ /** @noinspection PhpIncludeInspection */
72
+ require_once $this->MODULE_PATH . "/boot.php";
73
+ $this->module = new Titan\Scanner();
74
+ }
75
+
76
+ parent::__construct($plugin);
77
+ }
78
+
79
+ /**
80
+ * {@inheritDoc}
81
+ */
82
+ public function assets($scripts, $styles)
83
+ {
84
+ parent::assets($scripts, $styles);
85
+
86
+ if( $this->plugin->is_premium() ) {
87
+ $this->styles->add($this->MODULE_URL . '/assets/css/scanner-dashboard.css');
88
+ $this->styles->add($this->MODULE_URL . '/assets/css/base-statistic.css');
89
+
90
+ $this->scripts->add($this->MODULE_URL . '/assets/js/Chart.min.js');
91
+ $this->scripts->add($this->MODULE_URL . '/assets/js/statistic.js');
92
+ $this->scripts->add($this->MODULE_URL . '/assets/js/scanner.js');
93
+ $this->scripts->localize('wpnonce', [
94
+ 'start' => wp_create_nonce('titan-start-scan'),
95
+ 'stop' => wp_create_nonce('titan-stop-scan'),
96
+ 'status' => wp_create_nonce('titan-status-scan'),
97
+ ]);
98
+ }
99
+ }
100
+
101
+ /**
102
+ * {@inheritDoc}
103
+ */
104
+ public function showPageContent()
105
+ {
106
+ if( !$this->plugin->is_premium() ) {
107
+ $this->plugin->view->print_template('require-license-activate');
108
+
109
+ return;
110
+ }
111
+
112
+ $this->module->showPageContent();
113
+ }
114
+ }
admin/pages/class-pages-sitechecker.php ADDED
@@ -0,0 +1,129 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace WBCR\Titan\Page;
4
+
5
+ use WBCR\Titan;
6
+
7
+ // Exit if accessed directly
8
+ if( !defined('ABSPATH') ) {
9
+ exit;
10
+ }
11
+
12
+ /**
13
+ * Site checker page class
14
+ *
15
+ * @author Artem Prihodko <webtemyk@yandex.ru>
16
+ * @version 1.0
17
+ */
18
+ class SiteChecker extends Base {
19
+
20
+ /**
21
+ * {@inheritdoc}
22
+ */
23
+ public $id = 'sitechecker';
24
+
25
+ /**
26
+ * {@inheritdoc}
27
+ */
28
+ public $page_menu_dashicon = 'dashicons-welcome-view-site';
29
+
30
+ /**
31
+ * {@inheritdoc}
32
+ */
33
+ public $type = 'page';
34
+
35
+ /**
36
+ * {@inheritdoc}
37
+ */
38
+ public $show_right_sidebar_in_options = false;
39
+
40
+
41
+ /**
42
+ * Module folder URL
43
+ *
44
+ * @since 7.0
45
+ * @var bool
46
+ */
47
+ public $MODULE_URL = WTITAN_PLUGIN_URL . "/includes/sitechecker";
48
+
49
+ /**
50
+ * Path to module files
51
+ *
52
+ * @since 7.0
53
+ * @var bool
54
+ */
55
+ public $MODULE_PATH = WTITAN_PLUGIN_DIR . "/includes/sitechecker";
56
+
57
+ /**
58
+ * Path to module files
59
+ *
60
+ * @since 7.0
61
+ * @var object
62
+ */
63
+ public $module;
64
+
65
+ /**
66
+ * Site Checker constructor.
67
+ *
68
+ * @param \Wbcr_Factory426_Plugin $plugin
69
+ *
70
+ */
71
+ public function __construct(\Wbcr_Factory426_Plugin $plugin)
72
+ {
73
+ $this->plugin = $plugin;
74
+
75
+ $this->menu_title = __('Site Checker', 'titan-security');
76
+ $this->page_menu_short_description = __('Checking sites for availability', 'titan-security');
77
+
78
+ parent::__construct($plugin);
79
+ }
80
+
81
+ /**
82
+ * Init class and page data
83
+ */
84
+ public function init()
85
+ {
86
+ require_once $this->MODULE_PATH . "/boot.php";
87
+ $this->module = new Titan\SiteChecker();
88
+ }
89
+
90
+ /**
91
+ * Assets
92
+ *
93
+ * @return void
94
+ */
95
+ public function assets($scripts, $styles)
96
+ {
97
+ parent::assets($scripts, $styles);
98
+
99
+ $this->styles->add($this->MODULE_URL . '/assets/css/sitechecker-dashboard.css');
100
+ $this->scripts->add($this->MODULE_URL . '/assets/js/sitechecker.js', ['jquery']);
101
+
102
+ if( $this->plugin->is_premium() ) {
103
+ $this->scripts->add($this->MODULE_URL . '/assets/js/firebase.min.js');
104
+ $this->scripts->localize('wtitan', [
105
+ 'path' => $this->MODULE_URL . '/assets/js/firebase-messaging-sw.js',
106
+ 'scope' => $this->MODULE_URL . '/assets/js/',
107
+ 'pushTokenNonce' => wp_create_nonce('titan-send-push-token'),
108
+ 'sitechecker_nonce' => wp_create_nonce('titan-sitechecker'),
109
+ ]);
110
+ $this->scripts->add($this->MODULE_URL . '/assets/js/app.js', ['jquery']);
111
+ $this->scripts->localize('wt_app', [
112
+ 'https' => __( 'Your site must work on HTTPS to subscribe to notifications', 'titan-security' ),
113
+ 'notice' => __( 'Your browser does not support notifications', 'titan-security' ),
114
+ 'worker' => __( 'ServiceWorker not supported. Your site must work on HTTPS', 'titan-security' ),
115
+ ]);
116
+ }
117
+ }
118
+
119
+
120
+ /**
121
+ * Show page content
122
+ */
123
+ public function showPageContent()
124
+ {
125
+ $this->init();
126
+ $this->module->showPageContent();
127
+ }
128
+
129
+ }
admin/pages/class-pages-tweaks.php ADDED
@@ -0,0 +1,297 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace WBCR\Titan\Page;
4
+
5
+ // Exit if accessed directly
6
+ if( !defined('ABSPATH') ) {
7
+ exit;
8
+ }
9
+
10
+ /**
11
+ * Страница общих настроек для этого плагина.
12
+ *
13
+ * Не поддерживает режим работы с мультисаймами.
14
+ *
15
+ * @author Alexander Kovalev <alex.kovalevv@gmail.com>, Github: https://github.com/alexkovalevv
16
+ * @copyright (c) 2019 Webraftic Ltd
17
+ * @version 1.0
18
+ */
19
+ class Tweaks extends Base {
20
+
21
+ /**
22
+ * {@inheritDoc}
23
+ *
24
+ * @since 1.0
25
+ * @var string
26
+ */
27
+ public $id = "tweaks";
28
+
29
+ /**
30
+ * {@inheritDoc}
31
+ *
32
+ * @since 1.0
33
+ * @var string
34
+ */
35
+ public $page_menu_dashicon = 'dashicons-admin-tools';
36
+
37
+ /**
38
+ * {@inheritDoc}
39
+ *
40
+ * @since 1.0
41
+ * @var bool
42
+ */
43
+ public $show_right_sidebar_in_options = false;
44
+
45
+ /**
46
+ * WBCR\Page\Settings constructor.
47
+ *
48
+ * @param \Wbcr_Factory426_Plugin $plugin
49
+ *
50
+ * @author Alexander Kovalev <alex.kovalevv@gmail.com>
51
+ *
52
+ */
53
+ public function __construct(\Wbcr_Factory426_Plugin $plugin)
54
+ {
55
+ $this->menu_title = __('Tweaks', 'titan-security');
56
+ $this->page_menu_short_description = __('Security tweaks', 'titan-security');
57
+
58
+ parent::__construct($plugin);
59
+
60
+ $this->plugin = $plugin;
61
+ }
62
+
63
+ public $rules = array();
64
+
65
+ /**
66
+ * Enqueue page assets
67
+ *
68
+ * @return void
69
+ * @since 6.2
70
+ * @see Wbcr_FactoryPages426_AdminPage
71
+ *
72
+ */
73
+ public function assets($scripts, $styles)
74
+ {
75
+ parent::assets($scripts, $styles);
76
+
77
+ if( $this->plugin->is_premium() ) {
78
+ $this->scripts->request([
79
+ 'control.checkbox',
80
+ 'control.dropdown',
81
+ 'bootstrap.tooltip',
82
+ 'holder.more-link'
83
+ ], 'bootstrap');
84
+
85
+ $this->styles->request([
86
+ 'bootstrap.core',
87
+ 'bootstrap.form-group',
88
+ 'holder.more-link',
89
+ 'bootstrap.separator',
90
+ 'control.dropdown',
91
+ 'control.checkbox'
92
+ ], 'bootstrap');
93
+
94
+ add_action('wbcr/factory/update_option', [$this, 'before_save']);
95
+ }
96
+ }
97
+
98
+ /**
99
+ * Permalinks options.
100
+ *
101
+ * @return mixed[]
102
+ * @since 6.2
103
+ */
104
+ public function getPageOptions()
105
+ {
106
+
107
+
108
+ $options[] = array(
109
+ 'type' => 'html',
110
+ 'html' => '<div class="wbcr-factory-page-group-header">' . __('<strong>Base settings</strong>.', 'titan-security') . '<p>' . __('Basic recommended security settings.', 'titan-security') . '</p></div>'
111
+ );
112
+
113
+ $options[] = array(
114
+ 'type' => 'checkbox',
115
+ 'way' => 'buttons',
116
+ 'name' => 'strong_password',
117
+ 'title' => __('Strong Password Requirement', 'titan-security'),
118
+ 'layout' => array('hint-type' => 'icon'),
119
+ 'hint' => __('Force users to use strong passwords as rated by the WordPress password meter.', 'titan-security') . '<br><b>Titan: </b>' . __('Sets the redirect to exclude the possibility of obtaining a login.', 'titan-security'),
120
+ 'default' => false,
121
+ 'eventsOn' => [
122
+ 'show' => '.factory-control-strong_password_min_role'
123
+ ],
124
+ 'eventsOff' => [
125
+ 'hide' => '.factory-control-strong_password_min_role'
126
+ ]
127
+ );
128
+
129
+ $options[] = [
130
+ 'type' => 'dropdown',
131
+ 'name' => 'strong_password_min_role',
132
+ 'title' => __('Strong Password Minimum Role', 'titan-security'),
133
+ 'data' => [
134
+ ['administrator', 'Administrator'],
135
+ ['editor', 'Editor'],
136
+ ['author', 'Author'],
137
+ ['contributor', 'Contributor'],
138
+ ['subscriber', 'Subscriber'],
139
+ ],
140
+ 'layout' => ['hint-type' => 'icon', 'hint-icon-color' => 'green'],
141
+ 'hint' => __('Minimum role at which a user must choose a strong password. For more information on WordPress roles and capabilities please see http://codex.wordpress.org/Roles_and_Capabilities. Warning: If your site invites public registrations setting the role too low may annoy your members.', 'titan-security'),
142
+ 'default' => 'administrator'
143
+ ];
144
+
145
+ $options[] = array(
146
+ 'type' => 'separator'
147
+ );
148
+
149
+ $options[] = array(
150
+ 'type' => 'checkbox',
151
+ 'way' => 'buttons',
152
+ 'name' => 'protect_author_get',
153
+ 'title' => __('Hide author login', 'titan-security'),
154
+ 'layout' => array('hint-type' => 'icon'),
155
+ 'hint' => __('An attacker can find out the author\'s login, using a similar request to get your site. mysite.com/?author=1', 'titan-security') . '<br><b>Titan: </b>' . __('Sets the redirect to exclude the possibility of obtaining a login.', 'titan-security'),
156
+ 'default' => false
157
+ );
158
+
159
+ $options[] = array(
160
+ 'type' => 'checkbox',
161
+ 'way' => 'buttons',
162
+ 'name' => 'change_login_errors',
163
+ 'title' => __('Hide errors when logging into the site', 'titan-security'),
164
+ 'layout' => array('hint-type' => 'icon'),
165
+ 'hint' => __('WP by default shows whether you entered a wrong login or incorrect password, which allows attackers to understand if there is a certain user on the site, and then start searching through the passwords.', 'titan-security') . '<br><b>Titan: </b>' . __('Changes in the text of the error so that attackers could not find the login.', 'titan-security'),
166
+ 'default' => false
167
+ );
168
+
169
+ $options[] = array(
170
+ 'type' => 'checkbox',
171
+ 'way' => 'buttons',
172
+ 'name' => 'remove_x_pingback',
173
+ 'title' => __('Disable XML-RPC', 'titan-security'),
174
+ 'layout' => array('hint-type' => 'icon', 'hint-icon-color' => 'grey'),
175
+ 'hint' => __('A pingback is basically an automated comment that gets created when another blog links to you. A self-pingback is created when you link to an article within your own blog. Pingbacks are essentially nothing more than spam and simply waste resources.', 'titan-security') . '<br><b>Titan: </b>' . __('Removes the server responses a reference to the xmlrpc file.', 'titan-security'),
176
+ 'default' => false,
177
+ 'eventsOn' => array(
178
+ 'show' => '#wbcr-clearfy-xml-rpc-danger-message'
179
+ ),
180
+ 'eventsOff' => array(
181
+ 'hide' => '#wbcr-clearfy-xml-rpc-danger-message'
182
+ )
183
+ );
184
+
185
+ $options[] = array(
186
+ 'type' => 'html',
187
+ 'html' => array($this, 'xmlRpcDangerMessage')
188
+ );
189
+
190
+ //block_xml_rpc
191
+ //disable_xml_rpc_auth
192
+ //remove_xml_rpc_tag
193
+
194
+ $options[] = array(
195
+ 'type' => 'html',
196
+ 'html' => '<div class="wbcr-factory-page-group-header">' . __('<strong>Hide WordPress versions</strong>', 'titan-security') . '<p>' . __('WordPress itself and many plugins shows their version at the public areas of your site. An attacker received this information may be aware of the vulnerabilities found in the version of the WordPress core or plugins.', 'titan-security') . '</p></div>'
197
+ );
198
+
199
+ $options[] = array(
200
+ 'type' => 'checkbox',
201
+ 'way' => 'buttons',
202
+ 'name' => 'remove_html_comments',
203
+ 'title' => __('Remove html comments', 'titan-security'),
204
+ 'layout' => array('hint-type' => 'icon', 'hint-icon-color' => 'grey'),
205
+ 'hint' => __('This function will remove all html comments in the source code, except for special and hidden comments. This is necessary to hide the version of installed plugins.', 'titan-security') . '<br><br><b>Titan: </b>' . __('Remove html comments in source code.', 'titan-security'),
206
+ 'default' => false
207
+ );
208
+
209
+ $options[] = array(
210
+ 'type' => 'checkbox',
211
+ 'way' => 'buttons',
212
+ 'name' => 'remove_meta_generator',
213
+ 'title' => __('Remove meta generator', 'titan-security') . ' <span class="wbcr-clearfy-recomended-text">(' . __('Recommended', 'titan-security') . ')</span>',
214
+ 'layout' => array('hint-type' => 'icon'),
215
+ 'hint' => __('Allows attacker to learn the version of WP installed on the site. This meta tag has no useful function.', 'titan-security') . '<br><b>Titan: </b>' . sprintf(__('Removes the meta tag from the %s section', 'titan-security'), '&lt;head&gt;'),
216
+ 'default' => false
217
+ );
218
+
219
+ /* $options[] = [
220
+ 'type' => 'html',
221
+ 'html' => '<div class="wbcr-factory-page-group-header">' . '<strong>' . __('Remove query strings from static resources', 'titan-security') . '</strong>' . '<p>' . __('This funcitons will remove query strings from static resources like CSS & JS files inside the HTML <head> element to improve your speed scores in services like Pingdom, GTmetrix, PageSpeed and YSlow. <b style="color:#ff5722">Important:</b> This does not work for authorized users. To avoid problems after plugins update!', 'titan-security') . '</p>' . '</div>'
222
+ ];*/
223
+
224
+ $options[] = [
225
+ 'type' => 'checkbox',
226
+ 'way' => 'buttons',
227
+ 'name' => 'remove_js_version',
228
+ 'title' => __('Remove Version from Script', 'titan-security') . ' <span class="wbcr-clearfy-recomended-text">(' . __('Recommended', 'titan-security') . ')</span>',
229
+ 'layout' => ['hint-type' => 'icon'],
230
+ 'hint' => __('To make it more difficult for others to hack your website you can remove the WordPress version number from your site, your css and js. Without that number it\'s not possible to see if you run not the current version to exploit bugs from the older versions. <br><br>
231
+ Additionally it can improve the loading speed of your site, because without query strings in the URL the css and js files can be cached.', 'titan-security') . '<br><br><b>Titan: </b>' . __('Removes wordpress version number from scripts (not logged in user only).', 'titan-security'),
232
+ 'default' => false
233
+ ];
234
+
235
+ $options[] = [
236
+ 'type' => 'checkbox',
237
+ 'way' => 'buttons',
238
+ 'name' => 'remove_style_version',
239
+ 'title' => __('Remove Version from Stylesheet', 'titan-security') . ' <span class="wbcr-clearfy-recomended-text">(' . __('Recommended', 'titan-security') . ')</span>',
240
+ 'layout' => ['hint-type' => 'icon'],
241
+ 'hint' => __('To make it more difficult for others to hack your website you can remove the WordPress version number from your site, your css and js. Without that number it\'s not possible to see if you run not the current version to exploit bugs from the older versions. <br><br>
242
+ Additionally it can improve the loading speed of your site, because without query strings in the URL the css and js files can be cached.', 'titan-security') . '<br><br><b>Titan: </b>' . __('Removes the wordpress version number from stylesheets (not logged in user only).', 'titan-security'),
243
+ 'default' => false
244
+ /*'eventsOn' => array(
245
+ 'show' => '.factory-control-disable_remove_style_version_for_auth_users'
246
+ ),
247
+ 'eventsOff' => array(
248
+ 'hide' => '.factory-control-disable_remove_style_version_for_auth_users'
249
+ )*/
250
+ ];
251
+
252
+ /*$options[] = array(
253
+ 'type' => 'checkbox',
254
+ 'way' => 'buttons',
255
+ 'name' => 'disable_remove_style_version_for_auth_users',
256
+ 'title' => __( 'Disable remove versions for auth users', 'titan-security' ) . ' <span class="wbcr-clearfy-recomended-text">(' . __( 'Recommended', 'titan-security' ) . ')</span>',
257
+ 'layout' => array( 'hint-type' => 'icon' ),
258
+ 'default' => false
259
+ );*/
260
+
261
+ $options[] = [
262
+ 'type' => 'textarea',
263
+ 'name' => 'remove_version_exclude',
264
+ 'height' => '120',
265
+ 'title' => __('Exclude stylesheet/script file names', 'titan-security'),
266
+ 'layout' => ['hint-type' => 'icon', 'hint-icon-color' => 'grey'],
267
+ 'hint' => __('Enter Stylesheet/Script file names to exclude from version removal (each exclude file starts with a new line)', 'titan-security') . '<br><br><b>' . __('Example', 'titan-security') . ':</b>' . ' http://testwp.dev/wp-includes/js/jquery/jquery.js',
268
+ ];
269
+
270
+ $form_options = [];
271
+
272
+ $form_options[] = [
273
+ 'type' => 'form-group',
274
+ 'items' => $options,
275
+ //'cssClass' => 'postbox'
276
+ ];
277
+
278
+ return apply_filters('wtitan/tweaks_form/options', $form_options, $this);
279
+ }
280
+
281
+ /**
282
+ * Adds an html warning notification html markup.
283
+ */
284
+ public function xmlRpcDangerMessage()
285
+ {
286
+ ?>
287
+ <div class="form-group">
288
+ <label class="col-sm-4 control-label"></label>
289
+ <div class="control-group col-sm-8">
290
+ <div id="wbcr-clearfy-xml-rpc-danger-message" class="wbcr-clearfy-danger-message">
291
+ <?php _e('<b>Use this option carefully!</b><br> Plugins like jetpack may have problems using this option.', 'titan-security') ?>
292
+ </div>
293
+ </div>
294
+ </div>
295
+ <?php
296
+ }
297
+ }
admin/pages/firewall/class-pages-bruteforce.php ADDED
@@ -0,0 +1,309 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace WBCR\Titan\Page;
4
+
5
+ // Exit if accessed directly
6
+ use WBCR\Titan\Views;
7
+
8
+ if( !defined('ABSPATH') ) {
9
+ exit;
10
+ }
11
+
12
+ /**
13
+ * The file contains a short help info.
14
+ *
15
+ * @author Alexander Kovalev <alex.kovalevv@gmail.com>, Github: https://github.com/alexkovalevv
16
+ * @copyright (c) 2019 Webraftic Ltd
17
+ * @version 1.0
18
+ */
19
+ class Brute_Force extends Base {
20
+
21
+ /**
22
+ * {@inheritdoc}
23
+ */
24
+ public $id = 'bruteforce';
25
+
26
+ /**
27
+ * {@inheritDoc}
28
+ *
29
+ * @var string
30
+ */
31
+ public $page_parent_page = "firewall";
32
+
33
+ /**
34
+ * {@inheritdoc}
35
+ */
36
+ public $page_menu_dashicon = 'dashicons-tagcloud';
37
+
38
+ /**
39
+ * {@inheritdoc}
40
+ */
41
+ public $show_right_sidebar_in_options = false;
42
+
43
+ /**
44
+ * @var object|\WBCR\Titan\Views
45
+ */
46
+ public $view;
47
+
48
+
49
+ /**
50
+ * Logs constructor.
51
+ *
52
+ * @param \Wbcr_Factory426_Plugin $plugin
53
+ *
54
+ * @author Alexander Kovalev <alex.kovalevv@gmail.com>
55
+ *
56
+ */
57
+ public function __construct(\Wbcr_Factory426_Plugin $plugin)
58
+ {
59
+ $this->plugin = $plugin;
60
+
61
+ $this->menu_title = __('Limit Login Attempts', 'titan-security');
62
+ $this->page_menu_short_description = __('Stop login attacks', 'titan-security');
63
+
64
+ $this->view = \WBCR\Titan\Plugin::app()->view();
65
+
66
+ parent::__construct($plugin);
67
+ }
68
+
69
+
70
+ /**
71
+ * Enqueue page assets
72
+ *
73
+ * @return void
74
+ * @since 6.2
75
+ * @see Wbcr_FactoryPages426_AdminPage
76
+ *
77
+ */
78
+ public function assets($scripts, $styles)
79
+ {
80
+ parent::assets($scripts, $styles);
81
+
82
+ if( $this->plugin->is_premium() ) {
83
+ $this->scripts->request([
84
+ 'control.checkbox',
85
+ 'control.dropdown',
86
+ 'bootstrap.tooltip',
87
+ 'holder.more-link'
88
+ ], 'bootstrap');
89
+
90
+ $this->styles->request([
91
+ 'bootstrap.core',
92
+ 'bootstrap.form-group',
93
+ 'holder.more-link',
94
+ 'bootstrap.separator',
95
+ 'control.dropdown',
96
+ 'control.checkbox'
97
+ ], 'bootstrap');
98
+
99
+ add_action('wbcr/factory/update_option', [$this, 'before_save']);
100
+ }
101
+ }
102
+
103
+ /**
104
+ * Permalinks options.
105
+ *
106
+ * @return mixed[]
107
+ * @since 6.2
108
+ */
109
+ public function getPageOptions()
110
+ {
111
+
112
+
113
+ $options[] = array(
114
+ 'type' => 'html',
115
+ 'html' => '<div class="wbcr-factory-page-group-header">' . __('<strong>Lockout</strong>.', 'titan-security') . '<p>' . __('Basic recommended security settings.', 'titan-security') . '</p></div>'
116
+ );
117
+
118
+ $options[] = array(
119
+ 'type' => 'checkbox',
120
+ 'way' => 'buttons',
121
+ 'name' => 'bruteforce_enabled',
122
+ 'title' => __('Bruteforce enabled', 'titan-security'),
123
+ 'layout' => array('hint-type' => 'icon', 'hint-icon-color' => 'grey'),
124
+ 'hint' => __('Click to enable or disable protection brute force attacks.', 'titan-security'),
125
+ 'default' => false
126
+ );
127
+
128
+ $options[] = array(
129
+ 'type' => 'checkbox',
130
+ 'way' => 'buttons',
131
+ 'name' => 'bruteforce_gdpr',
132
+ 'title' => __('GDPR compliance', 'titan-security'),
133
+ 'layout' => array('hint-type' => 'icon', 'hint-icon-color' => 'grey'),
134
+ 'hint' => __('This makes the plugin GDPR compliant', 'titan-security'),
135
+ 'default' => false
136
+ );
137
+
138
+ $options[] = array(
139
+ 'type' => 'textbox',
140
+ 'name' => 'bruteforce_allowed_retries',
141
+ 'title' => __('Allowed retries', 'titan-security'),
142
+ 'default' => 4,
143
+ 'filter_value' => [$this, 'filter_allowed_retries_option']
144
+ );
145
+ $options[] = array(
146
+ 'type' => 'textbox',
147
+ 'name' => 'bruteforce_minutes_lockout_raw',
148
+ 'title' => __('Minutes lockout', 'titan-security'),
149
+ 'default' => 20,
150
+ 'filter_value' => [$this, 'filter_minutes_lockout_option']
151
+ );
152
+ $options[] = array(
153
+ 'type' => 'textbox',
154
+ 'name' => 'bruteforce_valid_duration_raw',
155
+ 'title' => __('Hours until retries are reset', 'titan-security'),
156
+ 'default' => 12,
157
+ 'filter_value' => [$this, 'filter_valid_duration_option']
158
+ );
159
+ /*$options[] = array(
160
+ 'type' => 'textbox',
161
+ 'name' => 'bruteforce_allowed_retries',
162
+ 'title' => __('Allowed retries', 'titan-security'),
163
+ 'default' => 4
164
+ );*/
165
+
166
+ $options[] = array(
167
+ 'type' => 'html',
168
+ 'html' => '<div class="wbcr-factory-page-group-header">' . __('<strong>Whitelist</strong>.', 'titan-security') . '<p>' . __('Basic recommended security settings.', 'titan-security') . '</p></div>'
169
+ );
170
+
171
+ $options[] = array(
172
+ 'type' => 'textarea',
173
+ 'name' => 'bruteforce_whitelist_ips_raw',
174
+ 'title' => __('Whitelist ips', 'titan-security'),
175
+ 'hint' => __('One IP or IP range (1.2.3.4-5.6.7.8) per line', 'titan-security'),
176
+ 'default' => '',
177
+ 'filter_value' => [$this, 'filter_whitelist_ips_option']
178
+ );
179
+ $options[] = array(
180
+ 'type' => 'textarea',
181
+ 'name' => 'bruteforce_whitelist_usernames_raw',
182
+ 'title' => __('Whitelist usernames', 'titan-security'),
183
+ 'hint' => __('One Username per line', 'titan-security'),
184
+ 'default' => '',
185
+ 'filter_value' => [$this, 'filter_whitelist_usernames_option']
186
+ );
187
+
188
+ $options[] = array(
189
+ 'type' => 'html',
190
+ 'html' => '<div class="wbcr-factory-page-group-header">' . __('<strong>Blacklist</strong>.', 'titan-security') . '<p>' . __('Basic recommended security settings.', 'titan-security') . '</p></div>'
191
+ );
192
+
193
+ $options[] = array(
194
+ 'type' => 'textarea',
195
+ 'name' => 'bruteforce_blacklist_ips_raw',
196
+ 'title' => __('Blacklist ips', 'titan-security'),
197
+ 'hint' => __('One IP or IP range (1.2.3.4-5.6.7.8) per line', 'titan-security'),
198
+ 'default' => '',
199
+ 'filter_value' => [$this, 'filter_blacklist_ips_option']
200
+ );
201
+ $options[] = array(
202
+ 'type' => 'textarea',
203
+ 'name' => 'bruteforce_blacklist_usernames_raw',
204
+ 'title' => __('Blacklist usernames', 'titan-security'),
205
+ 'hint' => __('One Username per line', 'titan-security'),
206
+ 'default' => '',
207
+ 'filter_value' => [$this, 'filter_blacklist_usernames_option']
208
+ );
209
+
210
+ $form_options = [];
211
+
212
+ $form_options[] = [
213
+ 'type' => 'form-group',
214
+ 'items' => $options,
215
+ //'cssClass' => 'postbox'
216
+ ];
217
+
218
+ return apply_filters('wtitan/tweaks_form/options', $form_options, $this);
219
+ }
220
+
221
+ public function filter_allowed_retries_option($value)
222
+ {
223
+ return (int)$value;
224
+ }
225
+
226
+ public function filter_minutes_lockout_option($value)
227
+ {
228
+ $this->plugin->updateOption('bruteforce_minutes_lockout', (int)$value * 60);
229
+
230
+ return (int)$value;
231
+ }
232
+
233
+ public function filter_valid_duration_option($value)
234
+ {
235
+ $this->plugin->updateOption('bruteforce_valid_duration', (int)$value * 3600);
236
+
237
+ return (int)$value;
238
+ }
239
+
240
+ public function filter_whitelist_ips_option($value)
241
+ {
242
+ $white_list_ips = (!empty($value)) ? explode("\n", str_replace("\r", "", stripslashes($value))) : array();
243
+
244
+ if( !empty($white_list_ips) ) {
245
+ foreach($white_list_ips as $key => $ip) {
246
+ if( '' == $ip ) {
247
+ unset($white_list_ips[$key]);
248
+ }
249
+ }
250
+ }
251
+ $this->plugin->updateOption('bruteforce_whitelist', $white_list_ips);
252
+
253
+ return $value;
254
+ }
255
+
256
+
257
+ public function filter_whitelist_usernames_option($value)
258
+ {
259
+ $white_list_usernames = (!empty($value)) ? explode("\n", str_replace("\r", "", stripslashes($value))) : array();
260
+
261
+ if( !empty($white_list_usernames) ) {
262
+ foreach($white_list_usernames as $key => $ip) {
263
+ if( '' == $ip ) {
264
+ unset($white_list_usernames[$key]);
265
+ }
266
+ }
267
+ }
268
+ $this->plugin->updateOption('bruteforce_whitelist_usernames', $white_list_usernames);
269
+
270
+ return $value;
271
+ }
272
+
273
+ public function filter_blacklist_ips_option($value)
274
+ {
275
+ $black_list_ips = (!empty($value)) ? explode("\n", str_replace("\r", "", stripslashes($value))) : array();
276
+
277
+ if( !empty($black_list_ips) ) {
278
+ foreach($black_list_ips as $key => $ip) {
279
+ /*$range = array_map('trim', explode('-', $ip));
280
+ if( count($range) > 1 && (float)sprintf("%u", ip2long($range[0])) > (float)sprintf("%u", ip2long($range[1])) ) {
281
+ $this->show_error(__('The "' . $ip . '" IP range is invalid', 'titan-security'));
282
+ }*/
283
+ if( '' == $ip ) {
284
+ unset($black_list_ips[$key]);
285
+ }
286
+ }
287
+ }
288
+
289
+ $this->plugin->updateOption('bruteforce_blacklist_ips', $black_list_ips);
290
+
291
+ return $value;
292
+ }
293
+
294
+ public function filter_blacklist_usernames_option($value)
295
+ {
296
+ $black_list_usernames = (!empty($value)) ? explode("\n", str_replace("\r", "", stripslashes($value))) : array();
297
+
298
+ if( !empty($black_list_usernames) ) {
299
+ foreach($black_list_usernames as $key => $ip) {
300
+ if( '' == $ip ) {
301
+ unset($black_list_usernames[$key]);
302
+ }
303
+ }
304
+ }
305
+ $this->plugin->updateOption('bruteforce_blacklist_usernames', $black_list_usernames);
306
+
307
+ return $value;
308
+ }
309
+ }
admin/pages/firewall/class-pages-firewall-attacks-log.php ADDED
@@ -0,0 +1,138 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace WBCR\Titan\Page;
4
+
5
+ // Exit if accessed directly
6
+ if( !defined('ABSPATH') ) {
7
+ exit;
8
+ }
9
+
10
+ /**
11
+ * The file contains a short help info.
12
+ *
13
+ * @author Alexander Kovalev <alex.kovalevv@gmail.com>, Github: https://github.com/alexkovalevv
14
+ * @copyright (c) 2019 Webraftic Ltd
15
+ * @version 1.0
16
+ */
17
+ class Firewall_Attacks_Log extends Base {
18
+
19
+ /**
20
+ * {@inheritdoc}
21
+ */
22
+ public $id = 'firewall-attack-log';
23
+
24
+ /**
25
+ * {@inheritDoc}
26
+ *
27
+ * @since 6.0
28
+ * @var string
29
+ */
30
+ public $type = 'page';
31
+
32
+ /**
33
+ * {@inheritDoc}
34
+ *
35
+ * @since 6.0
36
+ * @var string
37
+ */
38
+ public $page_menu_dashicon = 'dashicons-testimonial';
39
+
40
+ /**
41
+ * {@inheritDoc}
42
+ *
43
+ * @var string
44
+ */
45
+ public $page_parent_page = "firewall";
46
+
47
+
48
+ /**
49
+ * {@inheritdoc}
50
+ */
51
+ public $show_right_sidebar_in_options = false;
52
+
53
+
54
+ /**
55
+ * @var object|\WBCR\Titan\Views
56
+ */
57
+ public $view;
58
+
59
+ /**
60
+ * @var object|\WBCR\Titan\Model\Firewall
61
+ */
62
+ public $firewall;
63
+
64
+ /**
65
+ * Logs constructor.
66
+ *
67
+ * @param \Wbcr_Factory426_Plugin $plugin
68
+ *
69
+ * @author Alexander Kovalev <alex.kovalevv@gmail.com>
70
+ *
71
+ */
72
+ public function __construct($plugin)
73
+ {
74
+ $this->plugin = $plugin;
75
+
76
+ $this->menu_title = __('Attacks log', 'titan-security');
77
+ $this->page_menu_short_description = __('Attacks log', 'titan-security');
78
+
79
+ $this->view = \WBCR\Titan\Plugin::app()->view();
80
+
81
+ parent::__construct($plugin);
82
+ }
83
+
84
+
85
+ /**
86
+ * {@inheritdoc}
87
+ *
88
+ * @return void
89
+ * @since 1.0.0
90
+ */
91
+ public function assets($scripts, $styles)
92
+ {
93
+ parent::assets($scripts, $styles);
94
+
95
+ $this->styles->add(WTITAN_PLUGIN_URL . '/admin/assets/css/firewall/firewall-attacks-log.css');
96
+ }
97
+
98
+
99
+ /**
100
+ * {@inheritdoc}
101
+ */
102
+ public function showPageContent()
103
+ {
104
+
105
+ ?>
106
+ <div class="wbcr-factory-page-group-header">
107
+ <strong><?php _e('Attack list', 'titan-security') ?></strong>
108
+ <p>
109
+ <?php _e('In this table, you can see the attacks on your site that the Titan firewall repelled.', 'titan-security') ?>
110
+ </p>
111
+ </div>
112
+
113
+ <div class="wtitan-attacks-log wtitan-section-disabled">
114
+ <table class="wtitan-attacks-log__table wp-list-table widefat striped plugins wp-list-table__plugins">
115
+ <thead>
116
+ <tr>
117
+ <th class='wtitan-attacks-log__table-column'>
118
+ <strong><?php _e('IP', 'titan-security'); ?></strong></th>
119
+ <th class='wtitan-attacks-log__table-column'>
120
+ <strong><?php _e('Event', 'titan-security'); ?></strong>
121
+ </th>
122
+ <th class='wtitan-attacks-log__table-column'>
123
+ <strong><?php _e('...', 'titan-security'); ?></strong>
124
+ </th>
125
+
126
+ <th class='wtitan-attacks-log__table-column'>
127
+ <strong><?php _e('Attack time', 'titan-security'); ?></strong>
128
+ </th>
129
+ </tr>
130
+ </thead>
131
+ <tbody id="the-list">
132
+ </tbody>
133
+ </table>
134
+
135
+ </div>
136
+ <?php
137
+ }
138
+ }
admin/pages/firewall/class-pages-firewall-blocking.php ADDED
@@ -0,0 +1,152 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace WBCR\Titan\Page;
4
+
5
+ // Exit if accessed directly
6
+ if( !defined('ABSPATH') ) {
7
+ exit;
8
+ }
9
+
10
+ /**
11
+ * Страница общих настроек для этого плагина.
12
+ *
13
+ * Не поддерживает режим работы с мультисаймами.
14
+ *
15
+ * @author Alexander Kovalev <alex.kovalevv@gmail.com>, Github: https://github.com/alexkovalevv
16
+ * @copyright (c) 2019 Webraftic Ltd
17
+ * @version 1.0
18
+ */
19
+ class Firewall_Blocking extends Base {
20
+
21
+ /**
22
+ * {@inheritDoc}
23
+ *
24
+ * @since 6.0
25
+ * @var string
26
+ */
27
+ public $id = "firewall-blocking";
28
+
29
+ /**
30
+ * {@inheritDoc}
31
+ *
32
+ * @since 6.0
33
+ * @var string
34
+ */
35
+ public $type = 'page';
36
+
37
+ /**
38
+ * {@inheritDoc}
39
+ *
40
+ * @since 6.0
41
+ * @var string
42
+ */
43
+ public $page_menu_dashicon = 'dashicons-testimonial';
44
+
45
+ /**
46
+ * {@inheritDoc}
47
+ *
48
+ * @var string
49
+ */
50
+ public $page_parent_page = "firewall";
51
+
52
+ /**
53
+ * {@inheritDoc}
54
+ *
55
+ * @since 6.0
56
+ * @var bool
57
+ */
58
+ public $show_right_sidebar_in_options = false;
59
+
60
+ /**
61
+ * @var object|\WBCR\Titan\Views
62
+ */
63
+ public $view;
64
+
65
+ /**
66
+ * WBCR\Page\Settings constructor.
67
+ *
68
+ * @param \Wbcr_Factory426_Plugin $plugin
69
+ *
70
+ * @author Alexander Kovalev <alex.kovalevv@gmail.com>
71
+ *
72
+ */
73
+ public function __construct($plugin)
74
+ {
75
+ $this->menu_title = __('Blocking', 'titan-security');
76
+ $this->page_menu_short_description = __('Firewall blocking', 'titan-security');
77
+
78
+ parent::__construct($plugin);
79
+
80
+ $this->plugin = $plugin;
81
+ $this->view = \WBCR\Titan\Plugin::app()->view();
82
+ }
83
+
84
+ /**
85
+ * Enqueue page assets
86
+ *
87
+ * @return void
88
+ * @since 6.2
89
+ * @see Wbcr_FactoryPages426_AdminPage
90
+ *
91
+ */
92
+ public function assets($scripts, $styles)
93
+ {
94
+ parent::assets($scripts, $styles);
95
+
96
+ $this->styles->add(WTITAN_PLUGIN_URL . '/admin/assets/css/firewall/firewall-ips-blocking.css');
97
+ $this->styles->add(WTITAN_PLUGIN_URL . '/admin/assets/css/libs/sweetalert2.css');
98
+ $this->styles->add(WTITAN_PLUGIN_URL . '/admin/assets/css/sweetalert-custom.css');
99
+
100
+ $this->scripts->add(WTITAN_PLUGIN_URL . '/admin/assets/js/libs/sweetalert3.min.js');
101
+ $this->scripts->add(WTITAN_PLUGIN_URL . '/admin/assets/js/firewall/firewall-block-ip.js');
102
+ }
103
+
104
+ public function showPageContent()
105
+ {
106
+ ?>
107
+ <div class="wbcr-factory-page-group-header">
108
+ <strong>Blocking Ip.</strong>
109
+ <p>Blocking Ip.</p>
110
+ </div>
111
+ <div class="wtitan-ips-blocking wtitan-section-disabled">
112
+ <ul class="wtitan-ips-blocking__controls">
113
+ <li class="wtitan-ips-blocking__controls-left">
114
+ <input type="text" placeholder="<?php _e('Filter by Type, Detail, or Reason', 'titan-security') ?>" style="width:200px;">
115
+ <a href="#" id="wf-blocks-apply-filter" class="btn btn-default"><?php _e('Filter', 'titan-security') ?></a>
116
+ </li>
117
+ <li class="wtitan-ips-blocking__controls-right">
118
+ <a href="#" id="wtitan-blocks-ips" data-nonce="<?php echo wp_create_nonce('wtitan_block_ip') ?>" class="btn btn-primary">Block
119
+ <?php _e('Ip Address', 'titan-security') ?>
120
+ </a>
121
+ <a href="#" id="blocks-bulk-unblock" class="btn btn-default disabled">
122
+ <?php _e('Unblock', 'titan-security') ?>
123
+ </a>&nbsp; &nbsp;
124
+ <a href="#" id="blocks-bulk-make-permanent" class="btn btn-default disabled">
125
+ <?php _e('Make Permanent', 'titan-security') ?>
126
+ </a>
127
+ </li>
128
+ </ul>
129
+
130
+ <table class="wtitan-ips-blocking__table">
131
+ <thead>
132
+ <tr class="wf-blocks-columns">
133
+ <th style="width: 2%;text-align: center">
134
+ <input type="checkbox">
135
+ </th>
136
+ <th data-column="type"><?php _e('Block Type', 'titan-security') ?></th>
137
+ <th data-column="detail"><?php _e('Detail', 'titan-security') ?></th>
138
+ <th data-column="ruleAdded"><?php _e('Rule Added', 'titan-security') ?></th>
139
+ <th data-column="reason"><?php _e('Reason', 'titan-security') ?></th>
140
+ <th data-column="expiration"><?php _e('Expiration', 'titan-security') ?></th>
141
+ <th data-column="blockCount"><?php _e('Block Count', 'titan-security') ?></th>
142
+ <th data-column="lastAttempt"><?php _e('Last Attempt', 'titan-security') ?></th>
143
+ </tr>
144
+ </thead>
145
+ <tbody></tbody>
146
+ <tfoot></tfoot>
147
+ </table>
148
+ </div>
149
+ <?php
150
+ }
151
+
152
+ }
admin/pages/firewall/class-pages-firewall-login-attempts.php ADDED
@@ -0,0 +1,162 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace WBCR\Titan\Page;
4
+
5
+ // Exit if accessed directly
6
+ if( !defined('ABSPATH') ) {
7
+ exit;
8
+ }
9
+
10
+ /**
11
+ * The file contains a short help info.
12
+ *
13
+ * @author Alexander Kovalev <alex.kovalevv@gmail.com>, Github: https://github.com/alexkovalevv
14
+ * @copyright (c) 2019 Webraftic Ltd
15
+ * @version 1.0
16
+ */
17
+ class Firewall_Login_Attempts extends Base {
18
+
19
+ /**
20
+ * {@inheritdoc}
21
+ */
22
+ public $id = 'firewall-activity-log';
23
+
24
+ /**
25
+ * {@inheritDoc}
26
+ *
27
+ * @since 6.0
28
+ * @var string
29
+ */
30
+ public $type = 'page';
31
+
32
+ /**
33
+ * {@inheritDoc}
34
+ *
35
+ * @since 6.0
36
+ * @var string
37
+ */
38
+ public $page_menu_dashicon = 'dashicons-testimonial';
39
+
40
+ /**
41
+ * {@inheritDoc}
42
+ *
43
+ * @var string
44
+ */
45
+ public $page_parent_page = "firewall";
46
+
47
+
48
+ /**
49
+ * {@inheritdoc}
50
+ */
51
+ public $show_right_sidebar_in_options = false;
52
+
53
+
54
+ /**
55
+ * @var object|\WBCR\Titan\Views
56
+ */
57
+ public $view;
58
+
59
+ /**
60
+ * @var object|\WBCR\Titan\Model\Firewall
61
+ */
62
+ public $firewall;
63
+
64
+ /**
65
+ * Logs constructor.
66
+ *
67
+ * @param \Wbcr_Factory426_Plugin $plugin
68
+ *
69
+ * @author Alexander Kovalev <alex.kovalevv@gmail.com>
70
+ *
71
+ */
72
+ public function __construct($plugin)
73
+ {
74
+ $this->plugin = $plugin;
75
+
76
+ $this->menu_title = __('Login Attempts Log', 'titan-security');
77
+ $this->page_menu_short_description = __('Login Attempts', 'titan-security');
78
+
79
+ $this->view = \WBCR\Titan\Plugin::app()->view();
80
+
81
+ parent::__construct($plugin);
82
+ }
83
+
84
+
85
+ /**
86
+ * {@inheritdoc}
87
+ *
88
+ * @return void
89
+ * @since 1.0.0
90
+ */
91
+ public function assets($scripts, $styles)
92
+ {
93
+ parent::assets($scripts, $styles);
94
+
95
+ $this->styles->add(WTITAN_PLUGIN_URL . '/admin/assets/css/firewall-attacks-log.css');
96
+ }
97
+
98
+ /**
99
+ * {@inheritdoc}
100
+ */
101
+ public function showPageContent()
102
+ {
103
+ require_once(WTITAN_PLUGIN_DIR . '/includes/bruteforce/class-helpers.php');
104
+
105
+ $lockouts = (array)$this->get_option('bruteforce_lockouts');
106
+ $lockouts_log = $this->plugin->getOption('bruteforce_logged');
107
+ $log = \WBCR\Titan\Bruteforce\Helpers::sorted_log_by_date($lockouts_log);
108
+ ?>
109
+ <div class="wbcr-factory-page-group-header">
110
+ <strong><?php _e('Login Attempts', 'titan-security') ?></strong>
111
+ <p>
112
+ <?php _e('In this table, you can see the attacks on your site that the Titan firewall repelled.', 'titan-security') ?>
113
+ </p>
114
+ </div>
115
+
116
+ <div class="wtitan-attacks-log">
117
+
118
+ <table class="wtitan-attacks-log__table wp-list-table widefat striped plugins wp-list-table__plugins">
119
+ <thead>
120
+ <tr>
121
+ <th class='wtitan-attacks-log__table-column'>
122
+ <strong><?php _e('Date', 'titan-security'); ?></strong>
123
+ </th>
124
+ <th class='wtitan-attacks-log__table-column'>
125
+ <strong><?php _e('IP', 'titan-security'); ?></strong></th>
126
+ <th class='wtitan-attacks-log__table-column'>
127
+ <strong><?php _e('Tried to log in as', 'titan-security'); ?></strong>
128
+ </th>
129
+ <th class='wtitan-attacks-log__table-column'>
130
+ <strong><?php _e('Gateway', 'titan-security'); ?></strong>
131
+ </th>
132
+ </tr>
133
+ </thead>
134
+ <tbody id="the-list">
135
+ <?php if( !empty($log) ): ?>
136
+ <?php foreach($log as $date => $user_info): ?>
137
+ <tr>
138
+ <td class="wtitan-attacks-log__table-column">
139
+ <?php echo date_i18n('F d, Y H:i', $date); ?>
140
+ </td>
141
+ <td class="wtitan-attacks-log__table-column">
142
+ <?php echo esc_html($user_info['ip']); ?>
143
+ </td>
144
+ <td class="wtitan-attacks-log__table-column">
145
+ <?php echo esc_html($user_info['username']) . ' (' . esc_html($user_info['counter']) . ' lockouts)'; ?>
146
+ </td>
147
+ <td class="wtitan-attacks-log__table-column">
148
+ <?php if( !empty($lockouts[$user_info['ip']]) && $lockouts[$user_info['ip']] > time() ) : ?>
149
+ <a href="#" class="button limit-login-unlock" data-ip="<?= esc_attr($user_info['ip']) ?>" data-username="<?= esc_attr($user_info['username']) ?>">Unlock</a>
150
+ <?php elseif( $user_info['unlocked'] ): ?>
151
+ Unlocked
152
+ <?php endif ?>
153
+ </td>
154
+ </tr>
155
+ <?php endforeach; ?>
156
+ <?php endif; ?>
157
+ </tbody>
158
+ </table>
159
+ </div>
160
+ <?php
161
+ }
162
+ }
admin/pages/firewall/class-pages-firewall-settings.php ADDED
@@ -0,0 +1,729 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace WBCR\Titan\Page;
4
+
5
+ // Exit if accessed directly
6
+ if( !defined('ABSPATH') ) {
7
+ exit;
8
+ }
9
+
10
+ /**
11
+ * Страница общих настроек для этого плагина.
12
+ *
13
+ * Не поддерживает режим работы с мультисаймами.
14
+ *
15
+ * @author Alexander Kovalev <alex.kovalevv@gmail.com>, Github: https://github.com/alexkovalevv
16
+ * @copyright (c) 2019 Webraftic Ltd
17
+ * @version 1.0
18
+ */
19
+ class Firewall_Settings extends Base {
20
+
21
+ /**
22
+ * {@inheritDoc}
23
+ *
24
+ * @since 6.0
25
+ * @var string
26
+ */
27
+ public $id = "firewall-settings";
28
+
29
+ /**
30
+ * {@inheritDoc}
31
+ *
32
+ * @var string
33
+ */
34
+ public $page_parent_page = "firewall";
35
+
36
+ /**
37
+ * {@inheritDoc}
38
+ *
39
+ * @since 6.0
40
+ * @var string
41
+ */
42
+ public $page_menu_dashicon = 'dashicons-testimonial';
43
+
44
+ /**
45
+ * {@inheritDoc}
46
+ *
47
+ * @since 6.0
48
+ * @var bool
49
+ */
50
+ public $show_right_sidebar_in_options = false;
51
+
52
+ /**
53
+ * WBCR\Page\Settings constructor.
54
+ *
55
+ * @param \Wbcr_Factory426_Plugin $plugin
56
+ *
57
+ * @author Alexander Kovalev <alex.kovalevv@gmail.com>
58
+ *
59
+ */
60
+ public function __construct($plugin)
61
+ {
62
+ $this->menu_title = __('Settings', 'titan-security');
63
+ $this->page_menu_short_description = __('Firewall settings', 'titan-security');
64
+
65
+ parent::__construct($plugin);
66
+
67
+ $this->plugin = $plugin;
68
+ }
69
+
70
+ public $rules = array();
71
+
72
+ /**
73
+ * Enqueue page assets
74
+ *
75
+ * @return void
76
+ * @since 6.2
77
+ * @see Wbcr_FactoryPages426_AdminPage
78
+ *
79
+ */
80
+ public function assets($scripts, $styles)
81
+ {
82
+ parent::assets($scripts, $styles);
83
+
84
+ $this->scripts->request([
85
+ 'control.checkbox',
86
+ 'control.dropdown',
87
+ 'bootstrap.tooltip',
88
+ 'holder.more-link'
89
+ ], 'bootstrap');
90
+
91
+ $this->styles->request([
92
+ 'bootstrap.core',
93
+ 'bootstrap.form-group',
94
+ 'holder.more-link',
95
+ 'bootstrap.separator',
96
+ 'control.dropdown',
97
+ 'control.checkbox'
98
+ ], 'bootstrap');
99
+
100
+ $this->styles->add(WTITAN_PLUGIN_URL . '/admin/assets/css/firewall/firewall-settings.css');
101
+ $this->scripts->add(WTITAN_PLUGIN_URL . '/admin/assets/js/firewall/firewall-settings.js');
102
+
103
+ add_action('wbcr/factory/update_option', [$this, 'before_save']);
104
+ }
105
+
106
+ /**
107
+ * Permalinks options.
108
+ *
109
+ * @return mixed[]
110
+ * @since 6.2
111
+ */
112
+ public function getPageOptions()
113
+ {
114
+ $is_premium = \WBCR\Titan\Plugin::app()->premium->is_active() && \WBCR\Titan\Plugin::app()->premium->is_install_package();
115
+
116
+ $options[] = [
117
+ 'type' => 'html',
118
+ 'html' => '<div class="wbcr-factory-page-group-header">' . '<strong>' . __('Base Options.', 'titan-security') . '</strong>' . '<p>' . __('Additional modules to spam protect.', 'titan-security') . '</p>' . '</div>'
119
+ ];
120
+ $options[] = [
121
+ 'type' => 'dropdown',
122
+ 'name' => 'howget_ip',
123
+ 'way' => 'buttons',
124
+ 'title' => __('How does Titan get IPs ', 'titan-security'),
125
+ 'data' => [
126
+ [
127
+ '',
128
+ __('Default', 'titan-security'),
129
+ __('Let Titan use the most secure method to get visitor IP addresses. Prevents spoofing and works with most sites. (Recommended)', 'titan-security')
130
+ ],
131
+ [
132
+ 'REMOTE_ADDR',
133
+ __('REMOTE_ADDR', 'titan-security'),
134
+ __('Use PHP\'s built in REMOTE_ADDR and don\'t use anything else. Very secure if this is compatible with your site.', 'titan-security')
135
+ ],
136
+ [
137
+ 'HTTP_X_FORWARDED_FOR',
138
+ __('HTTP_X_FORWARDED_FOR', 'titan-security'),
139
+ __('Use the X-Forwarded-For HTTP header. Only use if you have a front-end proxy or spoofing may result.', 'titan-security')
140
+ ],
141
+ [
142
+ 'HTTP_X_REAL_IP',
143
+ __('HTTP_X_REAL_IP', 'titan-security'),
144
+ __('Use the X-Real-IP HTTP header. Only use if you have a front-end proxy or spoofing may result.', 'titan-security')
145
+ ],
146
+ [
147
+ 'HTTP_CF_CONNECTING_IP',
148
+ __('HTTP_CF_CONNECTING_IP', 'titan-security'),
149
+ __('Use the Cloudflare "CF-Connecting-IP" HTTP header to get a visitor IP. Only use if you\'re using Cloudflare.', 'titan-security')
150
+ ]
151
+ ],
152
+ 'layout' => ['hint-type' => 'icon', 'hint-icon-color' => 'grey'],
153
+ 'hint' => __('Titan needs to determine each visitor’s IP address to provide security functions on your website. The Titan default configuration works just fine for most websites, but it’s important that this configuration is correct. For example, if Titan is not receiving IP addresses correctly and thinks an external visitor originates from a private address, it will whitelist that visitor and bypass security protocols. You can read more about which addresses Titan considers private here.
154
+
155
+ The Titan scanner has an option to “Scan for misconfigured How does Titan get IPs”. This scan feature can help you detect if the wrong option has been selected for “How does Titan get IPs.”
156
+
157
+ Another way of determining if Titan is getting IPs correctly is to check the “IPs” section in the Titan Tools > Diagnostics.
158
+
159
+ Let Titan use the most secure method to get visitor IP addresses. Prevents spoofing and works with most sites.
160
+ This is the default mode of operation for Titan. Titan will try to get a valid IP address from PHP and if that doesn’t work, it will look at data that a firewall or reverse proxy sends in case your website uses this configuration.
161
+
162
+ This option provides a good balance between security and compatibility.
163
+
164
+ Use PHP’s built in REMOTE_ADDR and don’t use anything else. Very secure if this is compatible with your site.
165
+ If you know that you definitely don’t use a reverse proxy, cache, Cloudflare, CDN or anything else in front of your web server that “proxies” traffic to your website, and if you are sure that your website is just a standalone PHP web server, then using this option will work and is the most secure in a non-proxy or load balancer configuration.
166
+
167
+ You may also want to select this option for other reasons – for example to force Titan to use the $_SERVER[‘REMOTE_ADDR’] variable in PHP.
168
+
169
+ Use the X-Forwarded-For HTTP header. Only use if you have a front-end proxy or spoofing may result.
170
+ If you are using Nginx or another load balancer as a front-end-proxy or load balancer in front of your web server, and the front-end server sends IP addresses to the web server that runs WordPress using the HTTP X-Forwarded-For header, then you should enable this option.
171
+
172
+ Be careful about enabling this option if you do not have a front-end-proxy, load balancer, or CDN configuration, because it will then allow visitors to spoof their IP address and you will also miss many hits that should have been logged.
173
+
174
+ Use the X-Real-IP HTTP header. Only use if you have a front-end proxy or spoofing may result.
175
+ As with the X-Forwarded-For option above, only use this option if you are sure that you want Titan to retrieve the visitor IP address from the X-Real-IP HTTP header, and do not enable this if you don’t have a front-end proxy or load balancer that is sending visits to your real web server and adding the X-Real-IP header.
176
+
177
+ Use the Cloudflare “CF-Connecting-IP” HTTP header to get a visitor IP. Only use if you’re using Cloudflare.
178
+ Titan is fully compatible with CloudFlare, and in some configurations Cloudflare will send the real visitor IP address to your web server using the CF-Connecting-IP HTTP header. If the CloudFlare support personnel have advised you that this is the case, then enable this option on Titan to ensure that Titan is able to get your visitor IP address.
179
+
180
+ Note that Cloudflare has several configurations including their own web server module that takes care of detecting the visitor IP address, so be sure to work with their technical support staff and read their documentation to determine which configuration you’re using.
181
+
182
+ Multiple IPs detected
183
+ If your host requires using the X-Forwarded-For header, there may be multiple IP addresses detected. If your own IP address does not appear where it shows “Your IP with this setting,” you may need to add trusted proxies.
184
+
185
+ If you do not know whether your host uses more than one proxy address, contact your host or the reverse-proxy service that you use. If you know there is only one proxy address, it should be the last address in the “Detected IPs” field.
186
+
187
+ Once you know which proxies to trust, click the + Edit trusted proxies link below the detected IPs.
188
+ In the Trusted proxies field that appears, enter the IP addresses of the proxies. You can enter a single IP like 10.0.0.15. You can also enter a “CIDR” range like 10.0.0.0/24. Note that your host’s trusted IPs should not be the same addresses in these examples.
189
+ Click Save Options to save the changes, and check that your IP appears correctly in the “Your IP with this setting” field.', 'titan-security') . '<br><b>Clearfy</b>: ' . __('Disable admin top bar.', 'titan-security'),
190
+ 'default' => '',
191
+ 'filter_value' => [$this, 'filter_howget_ip_option'],
192
+ 'cssClass' => !$is_premium ? ['factory-control--disabled factory-control-premium-label'] : [],
193
+ ];
194
+
195
+ $options[] = [
196
+ 'type' => 'textarea',
197
+ 'name' => 'howget_ips_trusted_proxies',
198
+ 'title' => __('Trusted Proxies', 'titan-security'),
199
+ //'layout' => [ 'hint-type' => 'icon', 'hint-icon-color' => 'green' ],
200
+ 'hint' => __('These IPs (or CIDR ranges) will be ignored when determining the requesting IP via the X-Forwarded-For HTTP header. Enter one IP or CIDR range per line.', 'titan-security'),
201
+ 'default' => '',
202
+ //'filter_value' => [$this, 'filter_howget_ips_trusted_proxies_option'],
203
+ 'cssClass' => !$is_premium ? ['factory-control--disabled factory-control-premium-label'] : [],
204
+
205
+ ];
206
+
207
+ $options[] = [
208
+ 'type' => 'html',
209
+ 'html' => '<div class="wbcr-factory-page-group-header">' . '<strong>' . __('Advanced Firewall Options.', 'titan-security') . '</strong>' . '<p>' . __('Additional modules to spam protect.', 'titan-security') . '</p>' . '</div>'
210
+ ];
211
+
212
+ $options[] = [
213
+ 'type' => 'checkbox',
214
+ 'way' => 'buttons',
215
+ 'name' => 'disable_wafip_blocking',
216
+ 'title' => __('Delay IP and Country blocking until after WordPress and plugins have loaded (only process firewall rules early)', 'titan-security'),
217
+ 'layout' => ['hint-type' => 'icon', 'hint-icon-color' => 'green'],
218
+ 'hint' => __('When the Titan Firewall is optimized, the Firewall loads before the WordPress environment loads. This is desired behavior, as it increases security and gives the Firewall a performance boost. But if your server has a conflict with blocking by IP, country, or other advanced blocking settings before WordPress has loaded, you can turn on this option to allow WordPress to load first. We do not recommend enabling this option except for testing purposes.', 'titan-security'),
219
+ 'default' => true,
220
+ 'eventsOn' => [
221
+ 'show' => '.factory-control-whitelisted, .wtitan-disable-wafip-blocking-separator'
222
+ ],
223
+ 'eventsOff' => [
224
+ 'hide' => '.factory-control-whitelisted, .wtitan-disable-wafip-blocking-separator'
225
+ ],
226
+ 'cssClass' => !$is_premium ? ['factory-control--disabled factory-control-premium-label'] : [],
227
+ ];
228
+
229
+ $options[] = [
230
+ 'type' => 'textarea',
231
+ 'name' => 'whitelisted',
232
+ 'title' => __('Whitelisted IP addresses that bypass all rules', 'titan-security'),
233
+ //'layout' => [ 'hint-type' => 'icon', 'hint-icon-color' => 'green' ],
234
+ 'hint' => __('Whitelisted IPs must be separated by commas or placed on separate lines. You can specify ranges using the following formats: 127.0.0.1/24, 127.0.0.[1-100], or 127.0.0.1-127.0.1.100. Titan automatically whitelists private networks because these are not routable on the public Internet.', 'titan-security'),
235
+ 'default' => '',
236
+ 'filter_value' => [$this, 'filter_whitelisted_option'],
237
+ 'cssClass' => !$is_premium ? ['factory-control--disabled factory-control-premium-label'] : [],
238
+ ];
239
+
240
+ $options[] = [
241
+ 'type' => 'separator',
242
+ 'cssClass' => 'wtitan-disable-wafip-blocking-separator'
243
+ ];
244
+
245
+ $options[] = [
246
+ 'type' => 'list',
247
+ 'way' => 'checklist',
248
+ 'name' => 'whitelisted_services',
249
+ 'title' => __('Whitelisted services', 'comments-plus'),
250
+ 'data' => [
251
+ ['sucuri', 'Sucuri'],
252
+ ['facebook', 'Facebook'],
253
+ ['uptime_robot', 'Uptime Robot'],
254
+ ['status_cake', 'StatusCake'],
255
+ ['managewp', 'ManageWP'],
256
+ ['seznam', 'Seznam Search Engine'],
257
+ ],
258
+ 'layout' => ['hint-type' => 'icon', 'hint-icon-color' => 'grey'],
259
+ 'hint' => __('Select the post types for which comments will be disabled', 'comments-plus'),
260
+ //'filter_value' => [$this, 'filter_whitelisted_services_option'],
261
+ 'default' => 'sucuri,facebook,uptime_robots',
262
+ 'cssClass' => !$is_premium ? ['factory-control--disabled factory-control-premium-label'] : [],
263
+ ];
264
+
265
+ $options[] = [
266
+ 'type' => 'textarea',
267
+ 'name' => 'banned_urls',
268
+ 'title' => __('Immediately block IPs that access these URLs', 'titan-security'),
269
+ //'layout' => [ 'hint-type' => 'icon', 'hint-icon-color' => 'green' ],
270
+ 'hint' => __('Separate multiple URLs with commas or place them on separate lines. Asterisks are wildcards, but use with care. If you see an attacker repeatedly probing your site for a known vulnerability you can use this to immediately block them. All URLs must start with a "/" without quotes and must be relative. e.g. /badURLone/, /bannedPage.html, /dont-access/this/URL/, /starts/with-*', 'titan-security'),
271
+ 'default' => '',
272
+ 'cssClass' => !$is_premium ? ['factory-control--disabled factory-control-premium-label'] : [],
273
+ ];
274
+
275
+ $options[] = [
276
+ 'type' => 'textarea',
277
+ 'name' => 'waf_alert_whitelist',
278
+ 'title' => __('Ignored IP addresses for Titan Web Application Firewall alerting', 'titan-security'),
279
+ //'layout' => [ 'hint-type' => 'icon', 'hint-icon-color' => 'green' ],
280
+ 'hint' => __('Ignored IPs must be separated by commas or placed on separate lines. These addresses will be ignored from any alerts about increased attacks and can be used to ignore things like standalone website security scanners.', 'titan-security'),
281
+ 'default' => '',
282
+ 'cssClass' => !$is_premium ? ['factory-control--disabled factory-control-premium-label'] : [],
283
+ ];
284
+
285
+ $options[] = [
286
+ 'type' => 'html',
287
+ 'html' => [$this, 'get_rules_control']
288
+ ];
289
+
290
+ $options[] = [
291
+ 'type' => 'html',
292
+ 'html' => '<div class="wbcr-factory-page-group-header">' . '<strong>' . __('Whitelisted URLs.', 'titan-security') . '</strong>' . '<p>' . __('Additional modules to spam protect.', 'titan-security') . '</p>' . '</div>'
293
+ ];
294
+
295
+ $options[] = [
296
+ 'type' => 'html',
297
+ 'html' => [$this, 'get_whitelisted_urls_section']
298
+ ];
299
+
300
+ $options[] = [
301
+ 'type' => 'html',
302
+ 'html' => '<div class="wbcr-factory-page-group-header">' . '<strong>' . __('Rate Limiting.', 'titan-security') . '</strong>' . '<p>' . __('Additional modules to spam protect.', 'titan-security') . '</p>' . '</div>'
303
+ ];
304
+
305
+ $options[] = [
306
+ 'type' => 'checkbox',
307
+ 'way' => 'buttons',
308
+ 'name' => 'enable_advanced_blocking',
309
+ 'title' => __("Enable Rate Limiting and Advanced Blocking", 'titan-security'),
310
+ //'layout' => ['hint-type' => 'icon', 'hint-icon-color' => 'green'],
311
+ 'hint' => __('NOTE: This checkbox enables ALL blocking/throttling functions including IP, country and advanced blocking, and the "Rate Limiting Rules" below.', 'titan-security'),
312
+ 'default' => true,
313
+ 'cssClass' => !$is_premium ? ['factory-control--disabled factory-control-premium-label'] : [],
314
+ ];
315
+
316
+ $options[] = [
317
+ 'type' => 'separator',
318
+ //'cssClass' => 'wtitan-brute-force-block-bad-post-separator'
319
+ ];
320
+ $options[] = [
321
+ 'type' => 'checkbox',
322
+ 'way' => 'buttons',
323
+ 'name' => 'immediately_block_fake_google',
324
+ 'title' => __("Enable Rate Limiting and Advanced Blocking", 'titan-security'),
325
+ 'layout' => ['hint-type' => 'icon', 'hint-icon-color' => 'red'],
326
+ 'hint' => __('If you are having a problem with people stealing your content and pretending to be Google as they crawl your site, then you can enable this option which will immediately block anyone pretending to be Google. The way this option works is that we look at the visitor User-Agent HTTP header which indicates which browser the visitor is running. If it appears to be Googlebot, then we do a reverse lookup on the visitor’s IP address to verify that the IP does belong to Google. If the IP is not a Google IP, then we block it if you have this option enabled. Be careful about using this option, because we have had reports of it blocking real site visitors, especially (for some reason) legitimate visitors from Brazil. It’s possible, although we haven’t confirmed this, that some Internet service providers in Brazil use transparent proxies that modify their customers’ user-agent headers to pretend to be Googlebot rather than the real header. Or it may be possible that these providers are engaging in some sort of crawling activity pretending to be Googlebot using the same IP address that is the public IP for their customers. Whatever the cause is, the result is that if you enable this you may block some real visitors.', 'titan-security'),
327
+ 'default' => false,
328
+ 'cssClass' => !$is_premium ? ['factory-control--disabled factory-control-premium-label'] : [],
329
+ ];
330
+
331
+ $options[] = [
332
+ 'type' => 'html',
333
+ 'html' => [$this, 'get_rate_limit_section']
334
+ ];
335
+
336
+ $form_options = [];
337
+
338
+ $form_options[] = [
339
+ 'type' => 'form-group',
340
+ 'items' => $options,
341
+ //'cssClass' => 'postbox'
342
+ ];
343
+
344
+ return apply_filters('wtitan/settings_form/options', $form_options, $this);
345
+ }
346
+
347
+
348
+ /**
349
+ * Adds an html warning notification html markup.
350
+ */
351
+ public function get_rules_control()
352
+ {
353
+ ?>
354
+ <div class="form-group wtitan-section-disabled">
355
+ <label class="col-sm-4 control-label"></label>
356
+ <div class="control-group col-sm-8">
357
+ <strong>Rules</strong>
358
+ <div class="wtitan-excluded-rules">
359
+ <table>
360
+ <thead>
361
+ <tr>
362
+ <th style="width: 10%"></th>
363
+ <th style="width: 30%; text-align:left;">Category</th>
364
+ <th style="text-align: left;">Description</th>
365
+ </tr>
366
+ </thead>
367
+ <tbody>
368
+
369
+ <tr>
370
+ <td style="text-align: center">
371
+ <input type="checkbox" class="js-wtitan-excluded-rules__checkbox" value="119">
372
+ </td>
373
+ <td>rce</td>
374
+ <td>Duplicator Installer wp-config.php Overwrite</td>
375
+ </tr>
376
+ <tr>
377
+ <td style="text-align: center">
378
+ <input type="checkbox" class="js-wtitan-excluded-rules__checkbox" value="18">
379
+ </td>
380
+ <td>priv-esc</td>
381
+ <td>User Roles Manager Privilege Escalation &lt;= 4.24</td>
382
+ </tr>
383
+ <tr>
384
+ <td style="text-align: center">
385
+ <input type="checkbox" class="js-wtitan-excluded-rules__checkbox" value="66">
386
+ </td>
387
+ <td>dos</td>
388
+ <td>WordPress Core &lt;= 4.5.3 - DoS</td>
389
+ </tr>
390
+ <tr>
391
+ <td style="text-align: center">
392
+ <input type="checkbox" class="js-wtitan-excluded-rules__checkbox" value="117">
393
+ </td>
394
+ <td>privesc</td>
395
+ <td>WordPress Core: Arbitrary File Deletion</td>
396
+ </tr>
397
+ <tr>
398
+ <td style="text-align: center">
399
+ <input type="checkbox" class="js-wtitan-excluded-rules__checkbox" value="126" checked="checked">
400
+ </td>
401
+ <td>privesc</td>
402
+ <td>WordPress &lt;= 5.0 - PHP Object Injection via Meta Data &amp; Authenticated File
403
+ Delete
404
+ </td>
405
+ </tr>
406
+ <tr>
407
+ <td style="text-align: center">
408
+ <input type="checkbox" class="js-wtitan-excluded-rules__checkbox" value="1" checked="checked">
409
+ </td>
410
+ <td>whitelist</td>
411
+ <td>Whitelisted URL</td>
412
+ </tr>
413
+ <tr>
414
+ <td style="text-align: center">
415
+ <input type="checkbox" class="js-wtitan-excluded-rules__checkbox" value="2" checked="checked">
416
+ </td>
417
+ <td>lfi</td>
418
+ <td>Slider Revolution: Local File Inclusion</td>
419
+ </tr>
420
+ <tr>
421
+ <td style="text-align: center">
422
+ <input type="checkbox" class="js-wtitan-excluded-rules__checkbox" value="60" checked="checked">
423
+ </td>
424
+ <td>file_upload</td>
425
+ <td>Slider Revolution: Arbitrary File Upload</td>
426
+ </tr>
427
+ </tbody>
428
+ </table>
429
+ <input type="hidden" id="js-wtitan-excluded-rules__field" name="titan_disabled_rules" value="">
430
+ </div>
431
+ </div>
432
+ </div>
433
+ <?php
434
+ }
435
+
436
+ public function get_whitelisted_urls_section()
437
+ {
438
+
439
+
440
+ ?>
441
+ <div class="wtitan-whitelist">
442
+ <ul>
443
+ <li class="wtitan-whitelist__top-section">
444
+ <ul>
445
+ <li>
446
+ <strong class="wtitan-whitelist__label">Add Whitelisted URL/Param</strong>
447
+ <span class="wtitan-whitelist__hint">
448
+ The URL/parameters in this table will not be tested by the firewall. They are typically
449
+ added while the firewall is in Learning Mode or by an admin who identifies a particular
450
+ action/request is a false positive.</span>
451
+ </li>
452
+ <li>
453
+ <div>
454
+ <div class="wtitan-whitelist__form-group">
455
+ <input type="text" name="whitelistURL" id="whitelistURL" placeholder="URL" disabled>
456
+ </div>
457
+ <div class="wtitan-whitelist__form-group">
458
+ <select style="width:200px;" name="whitelistParam" id="whitelistParam" tabindex="-1" aria-hidden="true" disabled>
459
+ <option value="request.body">POST Body</option>
460
+ <option value="request.cookies">Cookie</option>
461
+ <option value="request.fileNames">File Name</option>
462
+ <option value="request.headers">Header</option>
463
+ <option value="request.queryString">Query String</option>
464
+ </select>
465
+ </div>
466
+ <div class="wtitan-whitelist__form-group">
467
+ <input style="display:inline-block;" type="text" name="whitelistParamName" id="whitelistParamName" placeholder="Param Name" disabled>
468
+ </div>
469
+ <a href="#" class="btn btn-default btn-small disabled" id="waf-whitelisted-urls-add">Add</a>
470
+ </div>
471
+ </li>
472
+ <li>
473
+ <hr>
474
+ </li>
475
+ <li class="wtitan-whitelist__table-controls">
476
+ <div class="wtitan-whitelist__table-controls-left">
477
+ <a href="#" id="whitelist-bulk-delete" class="btn btn-default btn-small disabled">Delete</a>&nbsp;&nbsp;<a href="#" id="whitelist-bulk-enable" class="disabled btn btn-default btn-small">Enable</a>&nbsp;&nbsp;<a href="#" id="whitelist-bulk-disable" class="btn btn-default btn-small disabled">Disable</a>
478
+ </div>
479
+ <div class="wtitan-whitelist__table-controls-right">
480
+ <select name="filterColumn" disabled>
481
+ <option value="url">URL</option>
482
+ <option value="param">Param</option>
483
+ <option value="source">Source</option>
484
+ <option value="user">User</option>
485
+ <option value="ip">IP</option>
486
+ </select>&nbsp;
487
+ <input type="text" placeholder="Filter Value" name="filterValue" disabled>
488
+ <a href="#" id="whitelist-apply-filter" class="btn btn-default btn-small disabled">Filter</a>
489
+ </div>
490
+ </li>
491
+ <li>
492
+ <table class="wtitan-whitelist__table">
493
+ <thead>
494
+ <tr>
495
+ <th style="width: 2%;text-align: center">
496
+ <input type="checkbox" disabled>
497
+ </th>
498
+ <th style="width: 5%;">Enabled</th>
499
+ <th>URL</th>
500
+ <th>Param</th>
501
+ <th>Created</th>
502
+ <th>Source</th>
503
+ <th>User</th>
504
+ <th>IP</th>
505
+ </tr>
506
+ </thead>
507
+ <tbody>
508
+ </tbody>
509
+ </table>
510
+ </li>
511
+ </ul>
512
+ </li>
513
+ <li class="wtitan-whitelist__bottom-section">
514
+ <ul>
515
+ <li class="wtitan-whitelist__label">
516
+ <strong>Monitor background requests from an administrator's
517
+ web browser for false positives</strong>
518
+ </li>
519
+ <li class="wtitan-whitelist__bg-requests-controls">
520
+ <ul>
521
+ <li><label><input type="checkbox" disabled> Front-end Website</label></li>
522
+ <li><label><input type="checkbox" disabled> Admin Panel</label></li>
523
+ </ul>
524
+ </li>
525
+ </ul>
526
+ </li>
527
+ </ul>
528
+ </div>
529
+ <?php
530
+ }
531
+
532
+ public function get_rate_limit_section()
533
+ {
534
+ $max_global_requests_options = [
535
+ '1' => '1 per minute',
536
+ '2' => '2 per minute',
537
+ '3' => '3 per minute',
538
+ '4' => '4 per minute',
539
+ '5' => '5 per minute',
540
+ '10' => '15 per minute',
541
+ '15' => '15 per minute',
542
+ '30' => '30 per minute',
543
+ '60' => '60 per minute',
544
+ '120' => '120 per minute',
545
+ '240' => '240 per minute',
546
+ '480' => '960 per minute',
547
+ '960' => '960 per minute',
548
+ '1920' => '1920 per minute',
549
+ ];
550
+ ?>
551
+ <div class="wtitan-rate-limit-settings" style="padding:0 40px 0 0;">
552
+ <ul>
553
+ <li>
554
+ <ul class="wtitan-rate-limit-settings__control">
555
+ <li class="wtitan-rate-limit-settings__control-title"><span>How should we treat Google's crawlers</span>
556
+ </li>
557
+ <li>
558
+ <select style="width:400px;font-weight:normal;" name="titan_never_block_bg" disabled>
559
+ <option value="never_block_verified">
560
+ Verified Google crawlers have unlimited access to this site
561
+ </option>
562
+ <option value="never_block_ua">
563
+ Anyone claiming to
564
+ be Google has unlimited access
565
+ </option>
566
+ <option value="treat_as_other_crawlers">
567
+ Treat
568
+ Google like any other Crawler
569
+ </option>
570
+ </select>
571
+ </li>
572
+ </ul>
573
+ </li>
574
+ <li>
575
+ <ul class="wtitan-rate-limit-settings__control">
576
+ <li class="wtitan-rate-limit-settings__control-title">If anyone's requests exceed</li>
577
+ <li class="wtitan-rate-limit-settings__control-fields">
578
+ <select name="titan_max_global_requests" class="wtitan-rate-limit-settings__control-block-time-select" disabled>
579
+ <option value="disabled">
580
+ Unlimited
581
+ </option>
582
+ <?php foreach($max_global_requests_options as $value => $title): ?>
583
+ <option value="<?php echo esc_attr($value); ?>"><?php echo $title; ?></option>
584
+ <?php endforeach; ?>
585
+ </select>&nbsp;then
586
+ <select name="titan_max_global_requests_action" class="wtitan-rate-limit-settings__control-action-select" disabled>
587
+ <option value="throttle">
588
+ throttle it
589
+ </option>
590
+ <option value="block">block it
591
+ </option>
592
+ </select>
593
+ </li>
594
+ </ul>
595
+
596
+ </li>
597
+ <li>
598
+ <ul class="wtitan-rate-limit-settings__control">
599
+ <li class="wtitan-rate-limit-settings__control-title">If a crawler's page views exceed</li>
600
+ <li class="wtitan-rate-limit-settings__control-fields">
601
+ <select name="titan_max_requests_crawlers" class="wtitan-rate-limit-settings__control-block-time-select" disabled>
602
+ <option value="disabled">
603
+ Unlimited
604
+ </option>
605
+ <?php foreach($max_global_requests_options as $value => $title): ?>
606
+ <option value="<?php echo esc_attr($value); ?>"><?php echo $title; ?></option>
607
+ <?php endforeach; ?>
608
+ </select>&nbsp;then
609
+ <select name="titan_max_requests_crawlers_action" class="wtitan-rate-limit-settings__control-action-select" disabled>
610
+ <option value="throttle">
611
+ throttle it
612
+ </option>
613
+ <option value="block">block
614
+ it
615
+ </option>
616
+ </select>
617
+ </li>
618
+ </ul>
619
+ </li>
620
+ <li>
621
+ <ul class="wtitan-rate-limit-settings__control">
622
+ <li class="wtitan-rate-limit-settings__control-title">If a crawler's pages not found (404s)
623
+ exceed
624
+ </li>
625
+ <li class="wtitan-rate-limit-settings__control-fields">
626
+ <select name="titan_max404_crawlers" class="wtitan-rate-limit-settings__control-block-time-select" disabled>
627
+ <option value="disabled">
628
+ Unlimited
629
+ </option>
630
+ <?php foreach($max_global_requests_options as $value => $title): ?>
631
+ <option value="<?php echo esc_attr($value); ?>"><?php echo $title; ?></option>
632
+ <?php endforeach; ?>
633
+ </select>&nbsp;then
634
+ <select name="titan_max404_crawlers_action" class="wtitan-rate-limit-settings__control-action-select" disabled>
635
+ <option value="throttle">
636
+ throttle it
637
+ </option>
638
+ <option value="block">block it
639
+ </option>
640
+ </select>
641
+ </li>
642
+ </ul>
643
+ </li>
644
+ <li>
645
+ <ul class="wtitan-rate-limit-settings__control">
646
+ <li class="wtitan-rate-limit-settings__control-title">If a human's page views exceed</li>
647
+ <li class="wtitan-rate-limit-settings__control-fields">
648
+ <select name="titan_max_requests_humans" class="wtitan-rate-limit-settings__control-block-time-select" disabled>
649
+ <option value="disabled">
650
+ Unlimited
651
+ </option>
652
+ <?php foreach($max_global_requests_options as $value => $title): ?>
653
+ <option value="<?php echo esc_attr($value); ?>"><?php echo $title; ?></option>
654
+ <?php endforeach; ?>
655
+ </select>&nbsp;then
656
+ <select name="titan_max_requests_humans_action" class="wtitan-rate-limit-settings__control-action-select" disabled>
657
+ <option value="throttle">
658
+ throttle it
659
+ </option>
660
+ <option value="block">block it
661
+ </option>
662
+ </select>
663
+ </li>
664
+ </ul>
665
+ </li>
666
+ <li>
667
+ <ul class="wtitan-rate-limit-settings__control">
668
+ <li class="wtitan-rate-limit-settings__control-title">If a human's pages not found (404s)
669
+ exceed
670
+ </li>
671
+ <li class="wtitan-rate-limit-settings__control-fields">
672
+ <select name="titan_max404_humans" class="wtitan-rate-limit-settings__control-block-time-select" disabled>
673
+ <option value="disabled">
674
+ Unlimited
675
+ </option>
676
+ <?php foreach($max_global_requests_options as $value => $title): ?>
677
+ <option value="<?php echo esc_attr($value); ?>"><?php echo $title; ?></option>
678
+ <?php endforeach; ?>
679
+ </select>&nbsp;then
680
+ <select name="titan_max404_humans_action" class="wtitan-rate-limit-settings__control-action-select" disabled>
681
+ <option value="throttle">
682
+ throttle it
683
+ </option>
684
+ <option value="block">block it</option>
685
+ </select>
686
+ </li>
687
+ </ul>
688
+ </li>
689
+ <li>
690
+ <ul class="wtitan-rate-limit-settings__control">
691
+ <li class="wtitan-rate-limit-settings__control-title">How long is an IP address blocked when it
692
+ breaks a rule</span></li>
693
+ <li class="wtitan-rate-limit-settings__control-fields">
694
+ <select name="titan_blocked_time" disabled>
695
+ <option value="60">1 minute</option>
696
+ <option value="300">5 minutes</option>
697
+ <option value="1800">30 minutes</option>
698
+ <option value="3600">1 hour</option>
699
+ <option value="7200">2 hours</option>
700
+ <option value="21600">6 hours</option>
701
+ <option value="43200">12 hours</option>
702
+ <option value="86400">1 day</option>
703
+ <option value="172800">2 days</option>
704
+ <option value="432000">5 days</option>
705
+ <option value="864000">10 days</option>
706
+ <option value="2592000">1 month</option>
707
+ </select>
708
+ </li>
709
+ </ul>
710
+ </li>
711
+ <li>
712
+ <ul class="wtitan-rate-limit-settings__control">
713
+ <li class="wtitan-rate-limit-settings__control-title">Whitelisted 404 URLs
714
+ <span class="wtitan-rate-limit-settings__control-subtitle">These URL patterns will be excluded from the
715
+ throttling rules used to limit crawlers.</span></li>
716
+
717
+ <li class="wtitan-rate-limit-settings__control-fields">
718
+ <textarea name="titan_allowed404s" class="wtitan-rate-limit-settings__control-allowed404s-textarea" disabled></textarea>
719
+ </li>
720
+ </ul>
721
+ </li>
722
+ </ul>
723
+
724
+ </div>
725
+ <?php
726
+ }
727
+
728
+
729
+ }
admin/pages/firewall/class-pages-firewall.php ADDED
@@ -0,0 +1,107 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace WBCR\Titan\Page;
4
+
5
+ // Exit if accessed directly
6
+ use WBCR\Titan\Views;
7
+
8
+ if( !defined('ABSPATH') ) {
9
+ exit;
10
+ }
11
+
12
+ /**
13
+ * The file contains a short help info.
14
+ *
15
+ * @author Alexander Kovalev <alex.kovalevv@gmail.com>, Github: https://github.com/alexkovalevv
16
+ * @copyright (c) 2019 Webraftic Ltd
17
+ * @version 1.0
18
+ */
19
+ class Firewall extends Base {
20
+
21
+ /**
22
+ * {@inheritdoc}
23
+ */
24
+ public $id = 'firewall';
25
+
26
+ /**
27
+ * {@inheritdoc}
28
+ */
29
+ public $page_menu_dashicon = 'dashicons-tagcloud';
30
+
31
+ /**
32
+ * {@inheritdoc}
33
+ */
34
+ public $type = 'page';
35
+
36
+ /**
37
+ * {@inheritdoc}
38
+ */
39
+ public $show_right_sidebar_in_options = false;
40
+
41
+ /**
42
+ * @var object|\WBCR\Titan\Views
43
+ */
44
+ public $view;
45
+
46
+ /**
47
+ * @var object|\WBCR\Titan\Model\Firewall
48
+ */
49
+ public $firewall;
50
+
51
+ /**
52
+ * Logs constructor.
53
+ *
54
+ * @param \Wbcr_Factory426_Plugin $plugin
55
+ *
56
+ * @author Alexander Kovalev <alex.kovalevv@gmail.com>
57
+ *
58
+ */
59
+ public function __construct($plugin)
60
+ {
61
+ $this->plugin = $plugin;
62
+
63
+ $this->menu_title = __('Firewall', 'titan-security');
64
+ $this->page_menu_short_description = __('Stops Complex Attacks', 'titan-security');
65
+
66
+ $this->view = \WBCR\Titan\Plugin::app()->view();
67
+
68
+ parent::__construct($plugin);
69
+ }
70
+
71
+ /**
72
+ * {@inheritdoc}
73
+ *
74
+ * @return void
75
+ * @since 1.0.0
76
+ */
77
+ public function assets($scripts, $styles)
78
+ {
79
+ parent::assets($scripts, $styles);
80
+
81
+ $this->styles->add(WTITAN_PLUGIN_URL . '/admin/assets/css/firewall/firewall-dashboard.css');
82
+ $this->scripts->add(WTITAN_PLUGIN_URL . '/admin/assets/js/libs/circular-progress.js', ['jquery']);
83
+
84
+ $this->styles->add(WTITAN_PLUGIN_URL . '/admin/assets/css/libs/sweetalert2.css');
85
+ $this->styles->add(WTITAN_PLUGIN_URL . '/admin/assets/css/sweetalert-custom.css');
86
+
87
+ $this->scripts->add(WTITAN_PLUGIN_URL . '/admin/assets/js/libs/sweetalert3.min.js');
88
+ $this->scripts->add(WTITAN_PLUGIN_URL . '/admin/assets/js/libs/popover.min.js');
89
+ $this->scripts->add(WTITAN_PLUGIN_URL . '/admin/assets/js/firewall/firewall-dashboard.js');
90
+ }
91
+
92
+
93
+ /**
94
+ * {@inheritdoc}
95
+ */
96
+ public function showPageContent()
97
+ {
98
+
99
+ $data = array();
100
+
101
+ $data['firewall_mode'] = 'disabled';
102
+ $data['firewall_status_percent'] = 0.0;
103
+ $data['firewall_status_color'] = "#5d05b7";
104
+
105
+ $this->view->print_template('firewall/firewall-dashboard-page', $data);
106
+ }
107
+ }
anti-spam.php CHANGED
@@ -1,17 +1,17 @@
1
  <?php
2
  /*
3
- Plugin Name: Anti-Spam
4
  Plugin URI: http://wordpress.org/plugins/anti-spam/
5
- Description: No spam in comments. No captcha.
6
- Version: 6.5.4
7
  Author: CreativeMotion
8
- Text Domain: anti-spam
9
  Author URI: https://cm-wp.com/
10
  License: GPLv3
11
  */
12
 
13
  // Exit if accessed directly
14
- if ( ! defined( 'ABSPATH' ) ) {
15
  exit;
16
  }
17
 
@@ -20,12 +20,20 @@ if ( ! defined( 'ABSPATH' ) ) {
20
  *
21
  * Alexander Kovalev
22
  * ---------------------------------------------------------------------------------
23
- * Full plugin development.
24
  *
25
  * Email: alex.kovalevv@gmail.com
26
  * Personal card: https://alexkovalevv.github.io
27
  * Personal repo: https://github.com/alexkovalevv
28
  * ---------------------------------------------------------------------------------
 
 
 
 
 
 
 
 
29
  */
30
 
31
  /**
@@ -36,73 +44,79 @@ if ( ! defined( 'ABSPATH' ) ) {
36
  * -----------------------------------------------------------------------------
37
  */
38
 
39
- require_once( dirname( __FILE__ ) . '/libs/factory/core/includes/class-factory-requirements.php' );
40
 
41
  // @formatter:off
42
- $cm_antspam_plugin_info = array(
43
- 'prefix' => 'wantispam_',
44
- 'plugin_name' => 'wantispam',
45
- 'plugin_title' => __( 'Anti-Spam', 'anti-spam' ),
46
 
47
  // PLUGIN SUPPORT
48
- 'support_details' => array(
49
- 'url' => 'https://anti-spam.space',
50
- 'pages_map' => array(
51
  'support' => 'support', // {site}/support
52
- 'docs' => 'docs' // {site}/docs
53
- )
54
- ),
 
55
 
56
  // PLUGIN PREMIUM SETTINGS
57
- 'has_premium' => true,
58
- 'license_settings' => array(
59
- 'provider' => 'freemius',
60
- 'slug' => 'antispam-premium',
61
- 'plugin_id' => '5079',
62
- 'public_key' => 'pk_98a99846a14067246257d4f43c04a',
63
- //'plugin_id' => '4865',
64
- //'public_key' => 'pk_05cbde6c0f9c96814c3b3cbff2259',
65
- 'price' => 15,
66
- 'has_updates' => true,
67
- 'updates_settings' => array(
68
- 'maybe_rollback' => true,
69
- 'rollback_settings' => array(
70
  'prev_stable_version' => '0.0.0'
71
- )
72
- )
73
- ),
74
 
75
  // PLUGIN ADVERTS
76
- 'render_adverts' => true,
77
- 'adverts_settings' => array(
78
  'dashboard_widget' => true, // show dashboard widget (default: false)
79
- 'right_sidebar' => true, // show adverts sidebar (default: false)
80
- 'notice' => true, // show notice message (default: false)
81
- ),
82
 
83
  // FRAMEWORK MODULES
84
- 'load_factory_modules' => array(
85
- array( 'libs/factory/bootstrap', 'factory_bootstrap_426', 'admin' ),
86
- array( 'libs/factory/forms', 'factory_forms_423', 'admin' ),
87
- array( 'libs/factory/pages', 'factory_pages_425', 'admin' ),
88
- array( 'libs/factory/clearfy', 'factory_clearfy_217', 'all' ),
89
- array( 'libs/factory/freemius', 'factory_freemius_113', 'all' ),
90
- array( 'libs/factory/feedback', 'factory_feedback_102', 'admin' )
91
- )
92
- );
93
-
94
- $cm_antspam_compatibility = new Wbcr_Factory425_Requirements( __FILE__, array_merge( $cm_antspam_plugin_info, array(
95
- 'plugin_already_activate' => defined( 'WANTISPAM_PLUGIN_ACTIVE' ),
96
- 'required_php_version' => '5.4',
97
- 'required_wp_version' => '4.2.0',
 
 
 
 
 
 
 
98
  'required_clearfy_check_component' => false
99
- ) ) );
100
 
101
  /**
102
  * If the plugin is compatible, then it will continue its work, otherwise it will be stopped,
103
  * and the user will throw a warning.
104
  */
105
- if ( ! $cm_antspam_compatibility->check() ) {
106
  return;
107
  }
108
 
@@ -115,11 +129,11 @@ if ( ! $cm_antspam_compatibility->check() ) {
115
  */
116
 
117
  // This plugin is activated
118
- define( 'WANTISPAM_PLUGIN_ACTIVE', true );
119
- define( 'WANTISPAM_PLUGIN_VERSION', $cm_antspam_compatibility->get_plugin_version() );
120
- define( 'WANTISPAM_PLUGIN_DIR', dirname( __FILE__ ) );
121
- define( 'WANTISPAM_PLUGIN_BASE', plugin_basename( __FILE__ ) );
122
- define( 'WANTISPAM_PLUGIN_URL', plugins_url( null, __FILE__ ) );
123
 
124
 
125
 
@@ -128,26 +142,31 @@ define( 'WANTISPAM_PLUGIN_URL', plugins_url( null, __FILE__ ) );
128
  * PLUGIN INIT
129
  * -----------------------------------------------------------------------------
130
  */
131
-
132
- require_once( WANTISPAM_PLUGIN_DIR . '/libs/factory/core/boot.php' );
133
- require_once( WANTISPAM_PLUGIN_DIR . '/includes/functions.php' );
134
- require_once( WANTISPAM_PLUGIN_DIR . '/includes/class-anti-spam-plugin.php' );
135
 
136
  try {
137
- new \WBCR\Antispam\Plugin( __FILE__, array_merge( $cm_antspam_plugin_info, array(
138
- 'plugin_version' => WANTISPAM_PLUGIN_VERSION,
139
- 'plugin_text_domain' => $cm_antspam_compatibility->get_text_domain(),
140
- ) ) );
 
 
 
 
 
 
141
  } catch( Exception $e ) {
142
  // Plugin wasn't initialized due to an error
143
- define( 'WANTISPAM_PLUGIN_THROW_ERROR', true );
144
 
145
- $cm_antspam_plugin_error_func = function () use ( $e ) {
146
- $error = sprintf( "The %s plugin has stopped. <b>Error:</b> %s Code: %s", 'CreativeMotion Antispam', $e->getMessage(), $e->getCode() );
147
  echo '<div class="notice notice-error"><p>' . $error . '</p></div>';
148
  };
149
 
150
- add_action( 'admin_notices', $cm_antspam_plugin_error_func );
151
- add_action( 'network_admin_notices', $cm_antspam_plugin_error_func );
152
  }
153
- // @formatter:on
1
  <?php
2
  /*
3
+ Plugin Name: Titan Anti-spam & Security
4
  Plugin URI: http://wordpress.org/plugins/anti-spam/
5
+ Description: Titan Security - Anti-spam, Anti-virus, Firewall and Malware Scan
6
+ Version: 7.0.1
7
  Author: CreativeMotion
8
+ Text Domain: titan-security
9
  Author URI: https://cm-wp.com/
10
  License: GPLv3
11
  */
12
 
13
  // Exit if accessed directly
14
+ if( !defined('ABSPATH') ) {
15
  exit;
16
  }
17
 
20
  *
21
  * Alexander Kovalev
22
  * ---------------------------------------------------------------------------------
23
+ * Plugin development
24
  *
25
  * Email: alex.kovalevv@gmail.com
26
  * Personal card: https://alexkovalevv.github.io
27
  * Personal repo: https://github.com/alexkovalevv
28
  * ---------------------------------------------------------------------------------
29
+ *
30
+ * Artem Prihodko
31
+ * ---------------------------------------------------------------------------------
32
+ * Plugin development.
33
+ *
34
+ * Email: webtemyk@yandex.ru
35
+ * Personal repo: https://github.com/temyk
36
+ * ---------------------------------------------------------------------------------
37
  */
38
 
39
  /**
44
  * -----------------------------------------------------------------------------
45
  */
46
 
47
+ require_once(dirname(__FILE__) . '/libs/factory/core/includes/class-factory-requirements.php');
48
 
49
  // @formatter:off
50
+ $wtitan_plugin_info = [
51
+ 'prefix' => 'titan_',
52
+ 'plugin_name' => 'titan_security',
53
+ 'plugin_title' => __('Titan security', 'titan-security'),
54
 
55
  // PLUGIN SUPPORT
56
+ 'support_details' => [
57
+ 'url' => 'https://titansitescanner.com',
58
+ 'pages_map' => [
59
  'support' => 'support', // {site}/support
60
+ 'docs' => 'docs', // {site}/docs
61
+ 'pricing' => 'pricing', // {site}/prices
62
+ ]
63
+ ],
64
 
65
  // PLUGIN PREMIUM SETTINGS
66
+ 'has_premium' => true,
67
+ 'license_settings' => [
68
+ 'provider' => 'freemius',
69
+ 'slug' => 'antispam-premium',
70
+ 'plugin_id' => '5079',
71
+ 'public_key' => 'pk_98a99846a14067246257d4f43c04a',
72
+ 'price' => 79,
73
+ 'has_updates' => true,
74
+ 'updates_settings' => [
75
+ 'maybe_rollback' => true,
76
+ 'rollback_settings' => [
 
 
77
  'prev_stable_version' => '0.0.0'
78
+ ]
79
+ ]
80
+ ],
81
 
82
  // PLUGIN ADVERTS
83
+ 'render_adverts' => true,
84
+ 'adverts_settings' => [
85
  'dashboard_widget' => true, // show dashboard widget (default: false)
86
+ 'right_sidebar' => true, // show adverts sidebar (default: false)
87
+ 'notice' => true, // show notice message (default: false)
88
+ ],
89
 
90
  // FRAMEWORK MODULES
91
+ 'load_factory_modules' => [
92
+ ['libs/factory/bootstrap', 'factory_bootstrap_427', 'admin'],
93
+ ['libs/factory/forms', 'factory_forms_424', 'admin'],
94
+ ['libs/factory/pages', 'factory_pages_426', 'admin'],
95
+ ['libs/factory/clearfy', 'factory_clearfy_218', 'all'],
96
+ ['libs/factory/freemius', 'factory_freemius_114', 'all'],
97
+ ['libs/factory/feedback', 'factory_feedback_103', 'admin']
98
+ ],
99
+ /*'load_plugin_components' => array(
100
+ 'hide-login-page' => array(
101
+ 'autoload' => 'libs/hide-login-page/titan.php',
102
+ 'plugin_prefix' => 'WHLP_'
103
+ )
104
+ )*/
105
+
106
+ ];
107
+
108
+ $wtitan_compatibility = new Wbcr_Factory426_Requirements(__FILE__, array_merge($wtitan_plugin_info, [
109
+ 'plugin_already_activate' => defined('WTITAN_PLUGIN_ACTIVE'),
110
+ 'required_php_version' => '5.6',
111
+ 'required_wp_version' => '4.9.0',
112
  'required_clearfy_check_component' => false
113
+ ]));
114
 
115
  /**
116
  * If the plugin is compatible, then it will continue its work, otherwise it will be stopped,
117
  * and the user will throw a warning.
118
  */
119
+ if( !$wtitan_compatibility->check() ) {
120
  return;
121
  }
122
 
129
  */
130
 
131
  // This plugin is activated
132
+ define('WTITAN_PLUGIN_ACTIVE', true);
133
+ define('WTITAN_PLUGIN_VERSION', $wtitan_compatibility->get_plugin_version());
134
+ define('WTITAN_PLUGIN_DIR', dirname(__FILE__));
135
+ define('WTITAN_PLUGIN_BASE', plugin_basename(__FILE__));
136
+ define('WTITAN_PLUGIN_URL', plugins_url(null, __FILE__));
137
 
138
 
139
 
142
  * PLUGIN INIT
143
  * -----------------------------------------------------------------------------
144
  */
145
+ require_once(WTITAN_PLUGIN_DIR . '/libs/factory/core/boot.php');
146
+ require_once(WTITAN_PLUGIN_DIR . '/includes/antispam/functions.php');
147
+ require_once(WTITAN_PLUGIN_DIR . '/includes/class-titan-security-plugin.php');
 
148
 
149
  try {
150
+ $plugin = new \WBCR\Titan\Plugin(__FILE__, array_merge($wtitan_plugin_info, [
151
+ 'plugin_version' => WTITAN_PLUGIN_VERSION,
152
+ 'plugin_text_domain' => $wtitan_compatibility->get_text_domain(),
153
+ ]));
154
+
155
+ require_once(WTITAN_PLUGIN_DIR . '/includes/functions.php');
156
+
157
+ if( $plugin->is_premium() ) {
158
+ require_once(WTITAN_PLUGIN_DIR . '/libs/antispam-premium/anti-spam-premium.php');
159
+ }
160
  } catch( Exception $e ) {
161
  // Plugin wasn't initialized due to an error
162
+ define('WTITAN_PLUGIN_THROW_ERROR', true);
163
 
164
+ $wtitan_plugin_error_func = function () use ($e) {
165
+ $error = sprintf("The %s plugin has stopped. <b>Error:</b> %s Code: %s", 'CreativeMotion Titan security', $e->getMessage(), $e->getCode());
166
  echo '<div class="notice notice-error"><p>' . $error . '</p></div>';
167
  };
168
 
169
+ add_action('admin_notices', $wtitan_plugin_error_func);
170
+ add_action('network_admin_notices', $wtitan_plugin_error_func);
171
  }
172
+ // @formatter:on
assets/css/admin-bar.css ADDED
@@ -0,0 +1,55 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * Admin bar menu
3
+ */
4
+ #wp-admin-bar-titan-menu::after {
5
+ clear: both;
6
+ }
7
+ #wp-admin-bar-titan-menu .dashicons {
8
+ font: 400 20px/1 dashicons;
9
+ }
10
+ #wp-admin-bar-titan-menu #wp-admin-bar-titan-menu-default {
11
+ /*background: #565656 !important;*/
12
+ }
13
+ #wp-admin-bar-titan-menu .wtitan-admin-bar-menu-icon {
14
+ display: inline-block !important;
15
+ width: 20px;
16
+ height: 25px;
17
+ margin-top: 6px;
18
+ margin-right: 5px;
19
+ float: left;
20
+ background: url(../img/titan-icon.png) 0 0 no-repeat;
21
+ }
22
+ #wp-admin-bar-titan-menu .wtitan-admin-bar-menu-title {
23
+ display: inline-block !important;
24
+ overflow: hidden;
25
+ }
26
+ #wp-admin-bar-titan-menu .wtitan-admin-bar-menu-title .dashicons {
27
+ font-size: 20px !important;
28
+ padding: 6px 0 !important;
29
+ }
30
+ #wp-admin-bar-titan-menu .ab-item .dashicons {
31
+ font-size: 14px;
32
+ padding: 6px 0;
33
+ }
34
+ #wp-admin-bar-titan-menu .ab-item .dashicons.dashicons-heart {
35
+ color: #fb7976;
36
+ }
37
+ #wp-admin-bar-titan-menu .ab-item .dashicons.dashicons-dashboard {
38
+ color: #fff;
39
+ }
40
+ #wp-admin-bar-titan-menu .wtitan-count-bubble {
41
+ display: inline-block;
42
+ vertical-align: baseline;
43
+ box-sizing: border-box;
44
+ margin: 0px 0 0px 5px;
45
+ padding: 0 5px;
46
+ min-width: 18px;
47
+ height: 18px;
48
+ border-radius: 9px;
49
+ background-color: #ca4a1f;
50
+ color: #fff;
51
+ font-size: 11px;
52
+ line-height: 1.6;
53
+ text-align: center;
54
+ z-index: 26;
55
+ }
assets/img/titan-icon.png ADDED
Binary file
{admin → includes/antispam}/assets/css/settings.css RENAMED
@@ -1,57 +1,57 @@
1
- #WBCR .factory-checkbox.wantispam-checkbox-premium-label:after {
2
- display: inline-block;
3
- position: relative;
4
- content: 'PRO';
5
- background: #ff5722;
6
- border-radius: 4px;
7
- color: #fff;
8
- font-size: 10px;
9
- line-height: 1;
10
- font-style: normal;
11
- padding: 4px 6px;
12
- margin-left: 4px;
13
- vertical-align: top;
14
- top: -8px;
15
- left: -10px;
16
- right: auto;
17
- z-index: 11;
18
- cursor: pointer;
19
- }
20
- #WBCR .factory-checkbox--disabled input,
21
- #WBCR .factory-checkbox--disabled button {
22
- pointer-events: none;
23
- cursor: not-allowed;
24
- opacity: 0.65;
25
- filter: alpha(opacity=65);
26
- -webkit-box-shadow: none;
27
- box-shadow: none;
28
- }
29
- #WBCR .wantispam-checkbox-warning-message {
30
- position: relative;
31
- padding: 15px;
32
- background: #fff5de;
33
- color: #9c6e6e;
34
- border-radius: 3px;
35
- }
36
- #WBCR .wantispam-checkbox-warning-message:after,
37
- #WBCR .wantispam-checkbox-warning-message:before {
38
- bottom: 100%;
39
- left: 20px;
40
- border: solid transparent;
41
- content: " ";
42
- height: 0;
43
- width: 0;
44
- position: absolute;
45
- pointer-events: none;
46
- }
47
- #WBCR .wantispam-checkbox-warning-message:after {
48
- border-color: rgba(136, 183, 213, 0);
49
- border-bottom-color: #fff5de;
50
- border-width: 10px;
51
- }
52
- #WBCR .wantispam-checkbox-warning-message:before {
53
- border-color: rgba(194, 225, 245, 0);
54
- border-bottom-color: #fff5de;
55
- border-width: 10px;
56
- }
57
  /*# sourceMappingURL=settings.css.map */
1
+ #WBCR .factory-checkbox.wantispam-checkbox-premium-label:after {
2
+ display: inline-block;
3
+ position: relative;
4
+ content: 'PRO';
5
+ background: #f6065b;
6
+ border-radius: 4px;
7
+ color: #fff;
8
+ font-size: 10px;
9
+ line-height: 1;
10
+ font-style: normal;
11
+ padding: 4px 6px;
12
+ margin-left: 4px;
13
+ vertical-align: top;
14
+ top: -8px;
15
+ left: -10px;
16
+ right: auto;
17
+ z-index: 11;
18
+ cursor: pointer;
19
+ }
20
+ #WBCR .factory-checkbox--disabled input,
21
+ #WBCR .factory-checkbox--disabled button {
22
+ pointer-events: none;
23
+ cursor: not-allowed;
24
+ opacity: 0.65;
25
+ filter: alpha(opacity=65);
26
+ -webkit-box-shadow: none;
27
+ box-shadow: none;
28
+ }
29
+ #WBCR .wantispam-checkbox-warning-message {
30
+ position: relative;
31
+ padding: 15px;
32
+ background: #fff5de;
33
+ color: #9c6e6e;
34
+ border-radius: 3px;
35
+ }
36
+ #WBCR .wantispam-checkbox-warning-message:after,
37
+ #WBCR .wantispam-checkbox-warning-message:before {
38
+ bottom: 100%;
39
+ left: 20px;
40
+ border: solid transparent;
41
+ content: " ";
42
+ height: 0;
43
+ width: 0;
44
+ position: absolute;
45
+ pointer-events: none;
46
+ }
47
+ #WBCR .wantispam-checkbox-warning-message:after {
48
+ border-color: rgba(136, 183, 213, 0);
49
+ border-bottom-color: #fff5de;
50
+ border-width: 10px;
51
+ }
52
+ #WBCR .wantispam-checkbox-warning-message:before {
53
+ border-color: rgba(194, 225, 245, 0);
54
+ border-bottom-color: #fff5de;
55
+ border-width: 10px;
56
+ }
57
  /*# sourceMappingURL=settings.css.map */
{admin → includes/antispam}/assets/css/settings.less RENAMED
File without changes
includes/antispam/assets/js/settings.js ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * General scripts
3
+ *
4
+ * @author Alex Kovalev <alex.kovalevv@gmail.com>, Github: https://github.com/alexkovalevv
5
+ * @copyright (c) 03.12.2019, CreativeMotion
6
+ * @version 1.0
7
+ */
8
+
9
+
10
+ (function($) {
11
+ 'use strict';
12
+
13
+ $.wantispam = {};
14
+
15
+ if( $.wbcr_factory_clearfy_218 ) {
16
+ $.wantispam = $.wbcr_factory_clearfy_218;
17
+ }
18
+
19
+ $('.factory-checkbox--disabled.wantispam-checkbox-premium-label').click(function(e) {
20
+ e.stopPropagation();
21
+ window.location.href = 'https://anti-spam.space/pricing/';
22
+ });
23
+
24
+ })(jQuery);
includes/antispam/boot.php ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // Exit if accessed directly
4
+ if ( ! defined( 'ABSPATH' ) ) {
5
+ exit;
6
+ }
7
+
8
+ // Base module class
9
+ require_once WTITAN_PLUGIN_DIR."/includes/class.module-base.php";
10
+
11
+ require_once('classes/class-antispam.php');
12
+ require_once('classes/class-protector.php');
includes/antispam/classes/class-antispam.php ADDED
@@ -0,0 +1,143 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace WBCR\Titan;
3
+
4
+ // Exit if accessed directly
5
+ if ( ! defined( 'ABSPATH' ) ) {
6
+ exit;
7
+ }
8
+
9
+ /**
10
+ * The file contains a short help info.
11
+ *
12
+ * @author Artem Prihodko <webtemyk@ya.ru>
13
+ * @copyright (c) 2020 Creative Motion
14
+ * @version 1.0
15
+ */
16
+ class Antispam extends Module_Base {
17
+
18
+ /**
19
+ * @see self::app()
20
+ * @var Antispam
21
+ */
22
+ private static $app;
23
+
24
+ /**
25
+ * Request interval in hours
26
+ *
27
+ * @since 1.1
28
+ */
29
+ const DEFAULT_REQUESTS_INTERVAL = 4;
30
+
31
+ /**
32
+ * Request interval in hours, if server is unavailable
33
+ *
34
+ * @since 1.1
35
+ */
36
+ const SERVER_UNAVAILABLE_INTERVAL = 4;
37
+
38
+ /**
39
+ * @var bool
40
+ */
41
+ public $mode;
42
+
43
+ /**
44
+ * Vulnerabilities constructor.
45
+ *
46
+ */
47
+ public function __construct() {
48
+ parent::__construct();
49
+ self::$app = $this;
50
+
51
+ $this->module_dir = WTITAN_PLUGIN_DIR."/includes/antispam";
52
+ $this->module_url = WTITAN_PLUGIN_URL."/includes/antispam";
53
+
54
+ $this->mode = $this->plugin->getOption( 'antispam_mode', true);
55
+
56
+ add_action('wp_ajax_wtitan-change-antispam-mode', [$this, 'change_antispam_mode']);
57
+ }
58
+
59
+ /**
60
+ * @return Antispam
61
+ * @since 7.0
62
+ */
63
+ public static function app() {
64
+ return self::$app;
65
+ }
66
+
67
+ /**
68
+ * AJAX Enable/Disable anti-spam
69
+ */
70
+ public function change_antispam_mode()
71
+ {
72
+ check_ajax_referer('wtitan_change_antispam_mode');
73
+
74
+ if( !current_user_can('manage_options') ) {
75
+ wp_send_json(array('error_message' => __('You don\'t have enough capability to edit this information.', 'titan-security')));
76
+ }
77
+
78
+ if(isset($_POST['mode'])) {
79
+
80
+ $mode_name = $_POST['mode'];
81
+
82
+ \WBCR\Titan\Plugin::app()->updatePopulateOption( 'antispam_mode', $mode_name );
83
+
84
+ if ( (bool) $mode_name ) {
85
+ wp_send_json( [
86
+ 'message' => __( "Anti-spam successfully enabled", "titan-security" ),
87
+ 'mode' => $mode_name
88
+ ] );
89
+ } else {
90
+ wp_send_json( [ 'message' => __( "Anti-spam successfully disabled", "titan-security" ) ] );
91
+ }
92
+ }
93
+ }
94
+
95
+ /**
96
+ *
97
+ * @since 7.0
98
+ */
99
+ public function showPageContent() {
100
+ }
101
+
102
+ /**
103
+ * Get data from cache.
104
+ *
105
+ * If data in the cache, not empty and not expired, then get data from cache. Or get data from server.
106
+ *
107
+ * @author Alexander Kovalev <alex.kovalevv@gmail.com>
108
+ *
109
+ * @since 1.1
110
+ * @return mixed array
111
+ */
112
+
113
+ public function get_statistic_data() {
114
+ $key = \WBCR\Titan\Plugin::app()->getPrefix() . 'stats_transient_';
115
+
116
+ $cached = get_transient( $key );
117
+
118
+ if ( $cached !== false ) {
119
+ if ( isset( $cached->error_code ) && isset( $cached->error ) ) {
120
+ return new \WP_Error( $cached->error_code, $cached->error );
121
+ }
122
+
123
+ return $cached;
124
+ }
125
+
126
+ $api = new \WBCR\Titan\Premium\Api\Request();
127
+ $data = $api->get_statistic( 7 );
128
+
129
+ if ( is_wp_error( $data ) ) {
130
+ set_transient( $key, (object) [
131
+ 'error' => $data->get_error_message(),
132
+ 'error_code' => $data->get_error_code()
133
+ ], self::SERVER_UNAVAILABLE_INTERVAL * HOUR_IN_SECONDS );
134
+
135
+ return $data;
136
+ }
137
+
138
+ set_transient( $key, $data->response, self::DEFAULT_REQUESTS_INTERVAL * HOUR_IN_SECONDS );
139
+
140
+ return $data->response;
141
+ }
142
+
143
+ }
includes/{class-protector.php → antispam/classes/class-protector.php} RENAMED
@@ -1,7 +1,8 @@
1
  <?php
2
 
3
- namespace WBCR\Antispam;
4
 
 
5
  /**
6
  * The class implement some protections ways against spam
7
  *
@@ -15,7 +16,7 @@ class Protector {
15
  add_action( 'wp_enqueue_scripts', [ $this, 'enqueue_script' ] );
16
  add_action( 'comment_form', [ $this, 'form_part' ] ); // add anti-spam inputs to the comment form
17
 
18
- if ( \WBCR\Antispam\Plugin::app()->premium->is_activate() ) {
19
  add_action( 'comment_form_after', 'wantispam_display_comment_form_privacy_notice' );
20
  }
21
 
@@ -33,7 +34,7 @@ class Protector {
33
  */
34
  public function enqueue_script() {
35
  global $withcomments; // WP flag to show comments on all pages
36
- wp_register_script( 'anti-spam-script', WANTISPAM_PLUGIN_URL . '/assets/js/anti-spam.js', [ 'jquery' ], \WBCR\Antispam\Plugin::app()->getPluginVersion(), true );
37
 
38
  if ( ( is_singular() || $withcomments ) && comments_open() ) { // load script only for pages with comments form
39
  wp_enqueue_script( 'anti-spam-script' );
@@ -53,7 +54,7 @@ class Protector {
53
  }
54
 
55
  public function check_comment( $commentdata ) {
56
- $save_spam_comments = \WBCR\Antispam\Plugin::app()->getPopulateOption( 'save_spam_comments', true );
57
 
58
  $comment_type = isset( $commentdata['comment_type'] ) ? $commentdata['comment_type'] : null;
59
 
@@ -91,9 +92,9 @@ class Protector {
91
  public function check_for_spam() {
92
  $spam_flag = false;
93
 
94
- $antspm_q = \WBCR\Antispam\Plugin::app()->request->post( "wantispam_q", '', 'trim' );
95
- $antspm_d = \WBCR\Antispam\Plugin::app()->request->post( "wantispam_d", '', 'trim' );
96
- $antspm_e = \WBCR\Antispam\Plugin::app()->request->post( "wantispam_e_email_url_website", '', 'trim' );
97
 
98
  if ( $antspm_q != date( 'Y' ) ) { // year-answer is wrong - it is spam
99
  if ( $antspm_d != date( 'Y' ) ) { // extra js-only check: there is no js added input - it is spam
@@ -180,7 +181,7 @@ class Protector {
180
  }
181
  }
182
 
183
- new \WBCR\Antispam\Protector();
184
 
185
 
186
 
1
  <?php
2
 
3
+ namespace WBCR\Titan\Antispam;
4
 
5
+ use \WBCR\Titan\Plugin as Plugin;
6
  /**
7
  * The class implement some protections ways against spam
8
  *
16
  add_action( 'wp_enqueue_scripts', [ $this, 'enqueue_script' ] );
17
  add_action( 'comment_form', [ $this, 'form_part' ] ); // add anti-spam inputs to the comment form
18
 
19
+ if ( wantispam_is_license_activate() ) {
20
  add_action( 'comment_form_after', 'wantispam_display_comment_form_privacy_notice' );
21
  }
22
 
34
  */
35
  public function enqueue_script() {
36
  global $withcomments; // WP flag to show comments on all pages
37
+ wp_register_script( 'anti-spam-script', WTITAN_PLUGIN_URL . '/assets/js/anti-spam.js', [ 'jquery' ], Plugin::app()->getPluginVersion(), true );
38
 
39
  if ( ( is_singular() || $withcomments ) && comments_open() ) { // load script only for pages with comments form
40
  wp_enqueue_script( 'anti-spam-script' );
54
  }
55
 
56
  public function check_comment( $commentdata ) {
57
+ $save_spam_comments = Plugin::app()->getPopulateOption( 'save_spam_comments', true );
58
 
59
  $comment_type = isset( $commentdata['comment_type'] ) ? $commentdata['comment_type'] : null;
60
 
92
  public function check_for_spam() {
93
  $spam_flag = false;
94
 
95
+ $antspm_q = Plugin::app()->request->post( "wantispam_q", '', 'trim' );
96
+ $antspm_d = Plugin::app()->request->post( "wantispam_d", '', 'trim' );
97
+ $antspm_e = Plugin::app()->request->post( "wantispam_e_email_url_website", '', 'trim' );
98
 
99
  if ( $antspm_q != date( 'Y' ) ) { // year-answer is wrong - it is spam
100
  if ( $antspm_d != date( 'Y' ) ) { // extra js-only check: there is no js added input - it is spam
181
  }
182
  }
183
 
184
+ new Protector();
185
 
186
 
187
 
includes/antispam/functions.php ADDED
@@ -0,0 +1,261 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Helper functions
4
+ *
5
+ * @author Alex Kovalev <alex.kovalevv@gmail.com>, Github: https://github.com/alexkovalevv
6
+ * @copyright (c) 12.12.2019, Webcraftic
7
+ * @version 1.0
8
+ */
9
+
10
+ /**
11
+ * Gets honeypot fields.
12
+ *
13
+ * @author Alexander Kovalev <alex.kovalevv@gmail.com>
14
+ * @since 6.5.3
15
+ */
16
+ function wantispam_get_honeypot_fields()
17
+ {
18
+ $rn = "\r\n"; // .chr(13).chr(10)
19
+ $html = '';
20
+
21
+ $html .= '<div class="wantispam-group wantispam-group-q" style="clear: both;">
22
+ <label>Current ye@r <span class="required">*</span></label>
23
+ <input type="hidden" name="wantispam_a" class="wantispam-control wantispam-control-a" value="' . date('Y') . '" />
24
+ <input type="text" name="wantispam_q" class="wantispam-control wantispam-control-q" value="' . \WBCR\Titan\Plugin::app()->getPluginVersion() . '" autocomplete="off" />
25
+ </div>' . $rn; // question (hidden with js)
26
+ $html .= '<div class="wantispam-group wantispam-group-e" style="display: none;">
27
+ <label>Leave this field empty</label>
28
+ <input type="text" name="wantispam_e_email_url_website" class="wantispam-control wantispam-control-e" value="" autocomplete="off" />
29
+ </div>' . $rn; // empty field (hidden with css); trap for spammers because many bots will try to put email or url here
30
+
31
+ return $html;
32
+ }
33
+
34
+ /**
35
+ * Gets required fields into the comment form on the page.
36
+ *
37
+ * @param string $html
38
+ *
39
+ * @return string
40
+ * @author Alexander Kovalev <alex.kovalevv@gmail.com>
41
+ * @since 6.5.3
42
+ *
43
+ */
44
+ function wantispam_get_required_fields($render_honeypot_fields = true)
45
+ {
46
+ $html = '<!-- Anti-spam plugin wordpress.org/plugins/anti-spam/ -->';
47
+ $html .= '<div class="wantispam-required-fields">';
48
+ $html .= '<input type="hidden" name="wantispam_t" class="wantispam-control wantispam-control-t" value="' . time() . '" />'; // Start time of form filling
49
+ if( $render_honeypot_fields ) {
50
+ $html .= wantispam_get_honeypot_fields();
51
+ }
52
+ $html .= '</div>';
53
+ $html .= '<!--\End Anti-spam plugin -->';
54
+
55
+ return $html;
56
+ }
57
+
58
+ /**
59
+ * Controls the display of a privacy related notice underneath the comment form.
60
+ *
61
+ * @author Alexander Kovalev <alex.kovalevv@gmail.com>
62
+ * @since 6.5.3
63
+ */
64
+ function wantispam_display_comment_form_privacy_notice($echo = false)
65
+ {
66
+ if( !\WBCR\Titan\Plugin::app()->getPopulateOption('comment_form_privacy_notice') ) {
67
+ return '';
68
+ }
69
+
70
+ $output = '<p class="wantispam-comment-form-privacy-notice" style="margin-top:10px;">' . sprintf(__('This site uses Antispam to reduce spam. <a href="%s" target="_blank" rel="nofollow noopener">Learn how your comment data is processed</a>.', 'titan-security'), 'https://anti-spam.space/antispam-privacy/') . '</p>';
71
+
72
+ if( $echo === false ) {
73
+ return $output;
74
+ }
75
+
76
+ echo $output;
77
+ }
78
+
79
+ /**
80
+ * Return premium widget markup
81
+ *
82
+ * @return string
83
+ * @since 6.5.3
84
+ * @author Alexander Kovalev <alex.kovalevv@gmail.com>
85
+ */
86
+ function wantispam_get_sidebar_premium_widget()
87
+ {
88
+ ob_start();
89
+ ?>
90
+ <div class="wbcr-factory-sidebar-widget">
91
+ <p>
92
+ <a href="https://anti-spam.space/pricing/" target="_blank" rel="noopener nofollow">
93
+ <img style="width: 100%;"
94
+ src="https://api.cm-wp.com/wp-content/uploads/2019/12/baner_antispam_vertical.jpg" alt="">
95
+ </a>
96
+ </p>
97
+ </div>
98
+ <?php
99
+ return ob_get_clean();
100
+ }
101
+
102
+ /**
103
+ * Should show a page about the plugin or not.
104
+ *
105
+ * @return bool
106
+ * @since 6.5.3
107
+ */
108
+ function wantispam_is_need_show_about_page()
109
+ {
110
+ if( \WBCR\Titan\Plugin::app()->isNetworkActive() ) {
111
+ $need_show_about = (int)get_site_option(\WBCR\Titan\Plugin::app()->getOptionName('what_is_new_64'));
112
+ } else {
113
+ $need_show_about = (int)get_option(\WBCR\Titan\Plugin::app()->getOptionName('what_is_new_64'));
114
+ }
115
+
116
+ $is_ajax = wantispam_doing_ajax();
117
+ $is_cron = wantispam_doing_cron();
118
+ $is_rest = wantispam_doing_rest_api();
119
+
120
+ if( $need_show_about && !$is_ajax && !$is_cron && !$is_rest ) {
121
+ return true;
122
+ }
123
+
124
+ return false;
125
+ }
126
+
127
+ /**
128
+ * Checks if the current request is a WP REST API request.
129
+ *
130
+ * Case #1: After WP_REST_Request initialisation
131
+ * Case #2: Support "plain" permalink settings
132
+ * Case #3: URL Path begins with wp-json/ (your REST prefix)
133
+ * Also supports WP installations in subfolders
134
+ *
135
+ * @author matzeeable https://wordpress.stackexchange.com/questions/221202/does-something-like-is-rest-exist
136
+ * @since 2.1.0
137
+ * @return boolean
138
+ */
139
+ function wantispam_doing_rest_api()
140
+ {
141
+ $prefix = rest_get_url_prefix();
142
+ $rest_route = \WBCR\Titan\Plugin::app()->request->get('rest_route', null);
143
+ if( defined('REST_REQUEST') && REST_REQUEST // (#1)
144
+ || !is_null($rest_route) // (#2)
145
+ && strpos(trim($rest_route, '\\/'), $prefix, 0) === 0 ) {
146
+ return true;
147
+ }
148
+
149
+ // (#3)
150
+ $rest_url = wp_parse_url(site_url($prefix));
151
+ $current_url = wp_parse_url(add_query_arg([]));
152
+
153
+ return strpos($current_url['path'], $rest_url['path'], 0) === 0;
154
+ }
155
+
156
+ /**
157
+ * @return bool
158
+ * @since 6.5.3
159
+ */
160
+ function wantispam_doing_ajax()
161
+ {
162
+ if( function_exists('wp_doing_ajax') ) {
163
+ return wp_doing_ajax();
164
+ }
165
+
166
+ return defined('DOING_AJAX') && DOING_AJAX;
167
+ }
168
+
169
+ /**
170
+ * @return bool
171
+ * @since 6.5.3
172
+ */
173
+ function wantispam_doing_cron()
174
+ {
175
+ if( function_exists('wp_doing_cron') ) {
176
+ return wp_doing_cron();
177
+ }
178
+
179
+ return defined('DOING_CRON') && DOING_CRON;
180
+ }
181
+
182
+ /**
183
+ * Checks whether the license is activated for plugin.
184
+ *
185
+ * @return bool
186
+ * @since 6.5.4
187
+ * @author Alexander Kovalev <alex.kovalevv@gmail.com>
188
+ */
189
+ function wantispam_is_titan_license_activate()
190
+ {
191
+ if( class_exists('\WBCR\Titan\Plugin') ) {
192
+ return \WBCR\Titan\Plugin::app()->premium->is_activate();
193
+ }
194
+
195
+ return false;
196
+ }
197
+
198
+ /**
199
+ * Checks whether the license is activated for the plugin or not. If the plugin is installed
200
+ * in priorities checks its license.
201
+ *
202
+ * @return bool
203
+ * @since 6.5.4
204
+ * @author Alexander Kovalev <alex.kovalevv@gmail.com>
205
+ */
206
+ function wantispam_is_license_activate()
207
+ {
208
+ return wantispam_is_titan_license_activate() || \WBCR\Titan\Plugin::app()->premium->is_activate();
209
+ }
210
+
211
+ /**
212
+ * Checks active (not expired!) License for plugin or not. If the plugin is installed
213
+ * checks its license in priorities.
214
+ *
215
+ * @return bool
216
+ * @since 6.5.4
217
+ * @author Alexander Kovalev <alex.kovalevv@gmail.com>
218
+ */
219
+ function wantispam_is_license_active()
220
+ {
221
+ if( wantispam_is_titan_license_activate() ) {
222
+ return \WBCR\Titan\Plugin::app()->premium->is_active();
223
+ }
224
+
225
+ return \WBCR\Titan\Plugin::app()->premium->is_activate() && \WBCR\Titan\Plugin::app()->premium->is_active();
226
+ }
227
+
228
+ /**
229
+ * Allows you to get a license key. If the Clearfy plugin is installed, it will be prioritized
230
+ * return it key.
231
+ *
232
+ * @return string|null
233
+ * @since 6.5.4
234
+ * @author Alexander Kovalev <alex.kovalevv@gmail.com>
235
+ */
236
+ function wantispam_get_license_key()
237
+ {
238
+ if( !wantispam_is_license_activate() ) {
239
+ return null;
240
+ }
241
+
242
+ if( wantispam_is_titan_license_activate() ) {
243
+ return \WBCR\Titan\Plugin::app()->premium->get_license()->get_key();
244
+ }
245
+
246
+ return \WBCR\Titan\Plugin::app()->premium->get_license()->get_key();
247
+ }
248
+
249
+ /**
250
+ * @return number|null
251
+ * @since 6.5.4
252
+ * @author Alexander Kovalev <alex.kovalevv@gmail.com>
253
+ */
254
+ function wantispam_get_freemius_plugin_id()
255
+ {
256
+ if( wantispam_is_titan_license_activate() ) {
257
+ return \WBCR\Titan\Plugin::app()->premium->get_setting('plugin_id');
258
+ }
259
+
260
+ return \WBCR\Titan\Plugin::app()->premium->get_setting('plugin_id');
261
+ }
includes/audit/assets/css/audit-dashboard.css ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ .wt-severity-low
2
+ {
3
+ box-shadow: inset 20px 0px 0px 0px rgb(128, 128, 128);
4
+ }
5
+ .wt-severity-medium
6
+ {
7
+ box-shadow: inset 20px 0px 0px 0px rgb(255, 177, 0);
8
+ }
9
+ .wt-severity-high
10
+ {
11
+ box-shadow: inset 20px 0px 0px 0px rgb(255, 0, 0);
12
+ }
13
+ .wt-scanner-hide-button
14
+ {
15
+ margin-bottom: 5px !important;
16
+ }
includes/audit/assets/js/audit.js ADDED
@@ -0,0 +1,3 @@
 
 
 
1
+ jQuery(document).ready(function($) {
2
+
3
+ });
includes/audit/assets/js/audit_ajax.js ADDED
@@ -0,0 +1,40 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ function audit_ajax(action_before = true) {
2
+ var wtitan_target = jQuery(".wtitan-tab-table-container#wtitan-audit");
3
+ var wtitan_hide_target = jQuery(".wtitan-tab-table-container#wtitan-hided");
4
+ var loader = jQuery('.wt-scan-icon-loader');
5
+ jQuery.ajax({
6
+ method: 'POST', url: ajaxurl, data: {
7
+ action: 'wtitan_audit_all',
8
+ _ajax_nonce: wtaudit.nonce
9
+ },
10
+ beforeSend: function () {
11
+ if(action_before) {
12
+ wtitan_progress_status(jQuery('#wt-scan-progress-audit .wt-scan-step-icon'), 'loader');
13
+ wtitan_target.html("");
14
+ wtitan_hide_target.html("");
15
+ }
16
+ },
17
+ success: function (result) {
18
+ console.log('audit - ok');
19
+
20
+ var status = loader.attr('data-status');
21
+ if((status === '11') || (action_before && status === '1')) {
22
+ loader.hide();
23
+ jQuery('#wt-checker-check').removeAttr('disabled');
24
+ }
25
+ else loader.attr('data-status', loader.attr('data-status')+1);
26
+
27
+ var noticeId = jQuery.wbcr_factory_clearfy_218.app.showNotice('Security audit success', 'success');
28
+ setTimeout(function() {
29
+ jQuery.wbcr_factory_clearfy_218.app.hideNotice(noticeId);
30
+ }, 5000);
31
+
32
+ if(action_before) {
33
+ wtitan_target.html(result);
34
+ wtitan_progress_status(jQuery('#wt-scan-progress-audit .wt-scan-step-icon'), 'ok');
35
+ }
36
+ },
37
+ complete: function () {
38
+ }
39
+ });
40
+ }
includes/audit/boot.php ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ // Exit if accessed directly
3
+ if ( ! defined( 'ABSPATH' ) ) {
4
+ exit;
5
+ }
6
+
7
+ //API Client
8
+ require_once WTITAN_PLUGIN_DIR . "/libs/api-client/boot.php";
9
+
10
+ // Base module class
11
+ require_once WTITAN_PLUGIN_DIR."/includes/class.module-base.php";
12
+
13
+ require_once "classes/class.auditresult.php";
14
+ require_once "classes/class.audit.php";
15
+ require_once 'classes/class.cert.php';
includes/audit/classes/class.audit.php ADDED
@@ -0,0 +1,504 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace WBCR\Titan;
4
+
5
+ // Exit if accessed directly
6
+ if( !defined('ABSPATH') ) {
7
+ exit;
8
+ }
9
+
10
+ use WBCR\Titan\Cert\Cert;
11
+ use WBCR\Titan\Client\Client;
12
+ use WBCR\Titan\Client\Request\SetNoticeData;
13
+
14
+ /**
15
+ * Security audit class
16
+ *
17
+ * @author Artem Prihodko <webtemyk@ya.ru>
18
+ * @copyright (c) 2020 Creative Motion
19
+ * @version 1.0
20
+ */
21
+ class Audit extends Module_Base {
22
+
23
+ /**
24
+ * @see self::app()
25
+ * @var Audit
26
+ */
27
+ private static $app;
28
+
29
+ /**
30
+ * @var AuditResult[]
31
+ */
32
+ public $results = array();
33
+
34
+ /**
35
+ * @var Client
36
+ */
37
+ public $client;
38
+
39
+ /**
40
+ * Audit constructor.
41
+ *
42
+ */
43
+ public function __construct()
44
+ {
45
+ parent::__construct();
46
+ self::$app = $this;
47
+
48
+ $this->module_dir = WTITAN_PLUGIN_DIR . "/includes/audit";
49
+ $this->module_url = WTITAN_PLUGIN_URL . "/includes/audit";
50
+
51
+ add_action('wp_ajax_wtitan_audit_all', array($this, 'show_audit_all'));
52
+
53
+ //AUDIT
54
+ $this->get_audit();
55
+ }
56
+
57
+ /**
58
+ * @return Audit
59
+ * @since 7.0
60
+ */
61
+ public static function app()
62
+ {
63
+ return self::$app;
64
+ }
65
+
66
+ /**
67
+ * Get audit
68
+ *
69
+ * @return AuditResult[]|bool Results
70
+ */
71
+ public function get_audit()
72
+ {
73
+ $this->results = get_option($this->plugin->getPrefix() . "audit_results", false);
74
+ if($this->results === false) return false;
75
+
76
+ if( !is_array($this->results) )
77
+ $this->results = array();
78
+
79
+ return $this->results;
80
+ }
81
+
82
+ /**
83
+ * Get hided
84
+ *
85
+ * @return AuditResult[] Results
86
+ */
87
+ public function get_hided()
88
+ {
89
+ $results = get_option($this->plugin->getPrefix() . "audit_results_hided", array());
90
+ if( !is_array($results) )
91
+ $results = array();
92
+
93
+ return $results;
94
+ }
95
+
96
+ /**
97
+ * Do audit
98
+ *
99
+ * @return AuditResult[] Results
100
+ */
101
+ public function do_audit()
102
+ {
103
+ $this->results = array();
104
+
105
+ $this->check_versions();
106
+ $this->check_wpconfig();
107
+ $this->check_php_variables();
108
+ $this->check_https();
109
+ $this->check_users();
110
+ $this->check_updates();
111
+ $this->check_files();
112
+ $this->check_fileEditor();
113
+ $this->check_folders_access();
114
+ $this->check_self();
115
+
116
+ update_option($this->plugin->getPrefix() . "audit_results_hided", array(), 'no');
117
+ update_option($this->plugin->getPrefix() . "audit_results", $this->results, 'no');
118
+
119
+ return $this->results;
120
+ }
121
+
122
+ /**
123
+ * Add result
124
+ *
125
+ * @param string $title
126
+ * @param string $description
127
+ * @param string $severity
128
+ * @param bool $hided
129
+ */
130
+ public function add($title, $description, $severity, $fix = '', $hided = false)
131
+ {
132
+ $this->results[] = new AuditResult($title, $description, $severity, $fix, $hided);
133
+ }
134
+
135
+ /**
136
+ * Versions audit
137
+ *
138
+ * @return AuditResult[] Results
139
+ */
140
+ public function check_versions()
141
+ {
142
+ //PHP
143
+ $title = sprintf(__('Your PHP version %1s is less than the recommended %2s', 'titan-security'), PHP_VERSION, '7.2.0');
144
+ $description = __('Older versions of PHP are slow and vulnerable', 'titan-security');
145
+ if( version_compare(PHP_VERSION, '7.2.0') < 0 ) {
146
+ $this->add($title, $description, 'medium');
147
+ }
148
+
149
+ //MySQL
150
+ global $wpdb;
151
+ $title = sprintf(__('Your MySQL version %1s is less than the recommended %2s', 'titan-security'), $wpdb->db_version(), '4.0.0');
152
+ $description = __('Older versions of MySQL are very slow and vulnerable', 'titan-security');
153
+ if( version_compare($wpdb->db_version(), '4.0.0') < 0 ) {
154
+ $this->add($title, $description, 'medium');
155
+ }
156
+
157
+ //Wordpress
158
+ global $wp_version;
159
+ $title = sprintf(__('Your Wordpress version %1s is less than the recommended %2s', 'titan-security'), $wp_version, '5.2.0');
160
+ $description = __('Older versions of Wordpress may be vulnerable', 'titan-security');
161
+ if( version_compare($wp_version, '5.2.0') < 0 ) {
162
+ $this->add($title, $description, 'medium', admin_url('update-core.php'));
163
+ }
164
+
165
+ return $this->results;
166
+ }
167
+
168
+ /**
169
+ * Debug audit
170
+ *
171
+ * @return AuditResult[] Results
172
+ */
173
+ public function check_wpconfig()
174
+ {
175
+ //WP_DEBUG
176
+ $title = __('Wordpress Debug mode is enabled on your site', 'titan-security');
177
+ $description = __('Every good developer should enable debugging before starting work on a new plugin or theme. In fact, WordPress Codex "strongly recommends" that developers use WP_DEBUG. Unfortunately, many developers forget to disable debugging mode even when the site is running. Displaying debug logs in the web interface will allow hackers to learn a lot about your WordPress website.', 'titan-security');
178
+ if( (defined('WP_DEBUG') && WP_DEBUG) ) {
179
+ $this->add($title, $description, 'high');
180
+ }
181
+
182
+ //SAVEQUERIES
183
+ $title = __('Wordpress Database Debug mode is enabled on your site', 'titan-security');
184
+ $description = __('When its enabled, all SQL queries will be saved in the $wpdb->queries variable as an array. For security and performance reasons, this constant must be disabled on the production site.', 'titan-security');
185
+ if( (defined('SAVEQUERIES') && SAVEQUERIES) ) {
186
+ $this->add($title, $description, 'low');
187
+ }
188
+
189
+ //SCRIPT_DEBUG
190
+ $title = __('Wordpress Script Debug Mode is enabled on your site', 'titan-security');
191
+ $description = __('When enabled, WordPress will use non-compressed versions (dev versions) of JS and CSS files . The default is to use min versions of the files. For security and performance reasons, this constant must be disabled on the production site.', 'titan-security');
192
+ if( (defined('SCRIPT_DEBUG') && SCRIPT_DEBUG) ) {
193
+ $this->add($title, $description, 'low');
194
+ }
195
+
196
+ //$table_prefix
197
+ $title = __("Database table prefix is empty or has a default value: 'wp_'", 'titan-security');
198
+ $description = __("This allows hackers to plan a massive attack based on the default prefix 'wp_'", 'titan-security');
199
+ global $wpdb;
200
+ if( empty($wpdb->prefix) || 'wp_' == $wpdb->prefix || WTITAN_DEBUG ) {
201
+ $this->add($title, $description, 'medium', \WBCR\Titan\Plugin::app()->getPluginPageUrl('check', ['action' => 'fix-database-prefix']));
202
+ }
203
+
204
+ //SALT/KEYS
205
+ $title = __('Authentication Unique Keys and Salts are not set in wp-config.php file', 'titan-security');
206
+ $description = __("You can generate these using the <a href='https://api.wordpress.org/secret-key/1.1/salt/'>WordPress.org secret-key service</a>", 'titan-security');
207
+ if( (!defined('AUTH_KEY') || empty(AUTH_KEY)) || (!defined('SECURE_AUTH_KEY') || empty(SECURE_AUTH_KEY)) || (!defined('LOGGED_IN_KEY') || empty(LOGGED_IN_KEY)) || (!defined('NONCE_KEY') || empty(NONCE_KEY)) || (!defined('AUTH_SALT') || empty(AUTH_SALT)) || (!defined('SECURE_AUTH_SALT') || empty(SECURE_AUTH_SALT)) || (!defined('LOGGED_IN_SALT') || empty(LOGGED_IN_SALT)) || (!defined('NONCE_SALT') || empty(NONCE_SALT)) ) {
208
+ $this->add($title, $description, 'low');
209
+ }
210
+
211
+ return $this->results;
212
+ }
213
+
214
+ /**
215
+ * PHP variables audit
216
+ *
217
+ * @return AuditResult[] Results
218
+ */
219
+ public function check_php_variables()
220
+ {
221
+ //display_errors
222
+ $title = __("The 'display_errors' PHP directive is enabled", "titan-security");
223
+ $description = __("Displaying any debugging information in the interface can be extremely bad for site security. If any PHP errors occur on your site , they must be registered in a secure location and not displayed to visitors or potential attackers.", "titan-security");
224
+ if( ini_get('display_errors') ) {
225
+ $this->add($title, $description, 'high');
226
+ }
227
+
228
+ //allow_url_include
229
+ $title = __("The 'allow_url_include' PHP directive is enabled", "titan-security");
230
+ $description = __("Enabling 'allow_url_include' PHP Directive will make your site vulnerable to cross-site attacks (XSS).", "titan-security");
231
+ if( ini_get('allow_url_include') ) {
232
+ $this->add($title, $description, 'high');
233
+ }
234
+
235
+ //expose_php
236
+ $title = __("The 'expose_php' PHP directive outputs the PHP version", "titan-security");
237
+ $description = __("Enabling 'expose_php' PHP Directive exposes to the world that PHP is installed on the server, which includes the PHP version within the HTTP header.", "titan-security");
238
+ if( ini_get('expose_php') ) {
239
+ $this->add($title, $description, 'low');
240
+ }
241
+
242
+ return $this->results;
243
+ }
244
+
245
+ /**
246
+ * HTTPS audit
247
+ *
248
+ * @return AuditResult[] Results
249
+ */
250
+ public function check_https()
251
+ {
252
+ $title = __("Problems with the SSL certificate were detected on your site", "titan-security");
253
+ $description = "";
254
+ $securedUrl = get_site_url(null, '', 'https');
255
+ $cert = Cert::get_instance();
256
+ if( $cert->is_available() ) {
257
+ if( !$cert->is_lets_encrypt() ) {
258
+ $description = __("The SSL certificate ends ", "titan-security") . date('d-m-Y H:i:s', $cert->get_expiration_timestamp());
259
+ }
260
+ } else {
261
+ switch( $cert->get_error() ) {
262
+ case Cert::ERROR_UNAVAILABLE:
263
+ $description = __("No openssl extension for php", "titan-security");
264
+ break;
265
+ case Cert::ERROR_ONLY_HTTPS:
266
+ $description = sprintf(__("Available only on <a href='%1s'>%2s</a>", "titan-security"), $securedUrl, $securedUrl);
267
+ break;
268
+ case Cert::ERROR_HTTPS_UNAVAILABLE:
269
+ $description = __("HTTPS is not available on this site", "titan-security");
270
+ break;
271
+ case Cert::ERROR_UNKNOWN_ERROR:
272
+ $description = __("Unknown error", "titan-security");
273
+ break;
274
+ default:
275
+ $description = __("Error", "titan-security");
276
+ break;
277
+ }
278
+ }
279
+
280
+ $this->add($title, $description, 'medium');
281
+
282
+ return $this->results;
283
+ }
284
+
285
+ /**
286
+ * Users audit
287
+ * Check if an user can be found by its ID
288
+ *
289
+ * @return AuditResult[] Results
290
+ */
291
+ public function check_users()
292
+ {
293
+ $users = get_users([
294
+ 'role' => 'administrator',
295
+ ]);
296
+ $admin = false;
297
+ foreach($users as $user) {
298
+ if( "admin" == $user->user_login || "administrator" == $user->user_login )
299
+ $admin = true;
300
+ }
301
+
302
+ $title = __("The standard administrator login 'admin' is used", "titan-security");
303
+ $description = __("Since user names make up half of the login credentials, this made it easier for hackers to launch brute- force attacks. You need to set complex and unique names for your site administrators.", "titan-security");
304
+ if( $admin ) {
305
+ $this->add($title, $description, 'medium');
306
+ }
307
+
308
+ //User ID
309
+ $title = __("Author URL by ID access", "titan-security");
310
+ $description = __("By knowing the username, you are one step closer to logging in using the username to brute-force the password, or to gain access in a similar way.", "titan-security");
311
+
312
+ $users = get_users('number=5');
313
+ $url = home_url() . '/?author=';
314
+ foreach($users as $user) {
315
+ $response = wp_remote_get($url . $user->ID, array('redirection' => 0, 'sslverify' => 0));
316
+ $response_code = wp_remote_retrieve_response_code($response);
317
+ if( $response_code == 301 ) {
318
+ $this->add($title, $description, 'medium');
319
+ break;
320
+ }
321
+ }
322
+
323
+ return $this->results;
324
+ }
325
+
326
+ /**
327
+ * Updates audit
328
+ *
329
+ * @return AuditResult[] Results
330
+ */
331
+ public function check_updates()
332
+ {
333
+ $plugins = get_plugins();
334
+
335
+ //COMPATIBLE
336
+ $no_requirement = array();
337
+ foreach((array)$plugins as $plugin_file => $plugin_data) {
338
+ $requirement = validate_plugin_requirements($plugin_file);
339
+ if( is_wp_error($requirement) ) {
340
+ $no_requirement[] = $plugin_data['Name'];
341
+ }
342
+ }
343
+
344
+ $title = __("Incompatible plugins found", "titan-security");
345
+ $description = "<b>" . __("Some plugins on your site are not compatible with PHP and Wordpress versions: ", "titan-security") . "</b>";
346
+ if( !empty($no_requirement) ) {
347
+ $description .= "<br>- " . implode("<br>- ", $no_requirement);
348
+ $this->add($title, $description, 'medium');
349
+ }
350
+
351
+ //UPDATE Plugins
352
+ $current = get_site_transient('update_plugins');
353
+ foreach((array)$current->response as $plugin_file => $plugin_data) {
354
+ $plugins_update[] = $plugin_data->slug;
355
+ }
356
+ $i = 0;
357
+ foreach((array)$plugins as $plugin_file => $plugin_data) {
358
+ if( isset($current->response[$plugin_file]) ) {
359
+ $plugins[$plugin_file]['update'] = true;
360
+ $i++;
361
+ }
362
+ }
363
+ $title = sprintf(__('You have %1s plugins that need to be updated', 'titan-security'), $i);
364
+ $description = "<b>" . __("Need to update plugins, as previous versions may be vulnerable:", "titan-security") . "</b>";
365
+ if( !empty($plugins_update) )
366
+ $description .= "<br>- " . implode("<br>- ", $plugins_update);
367
+ if( $i ) {
368
+ $this->add($title, $description, 'medium', admin_url('update-core.php'));
369
+ }
370
+
371
+ //UPDATE Themes
372
+ $themes = wp_get_themes();
373
+ $current = get_site_transient('update_themes');
374
+ foreach((array)$current->response as $theme_file => $theme_data) {
375
+ $themes_update[] = $theme_data['theme'];
376
+ }
377
+ $i = 0;
378
+ foreach((array)$themes as $key => $theme) {
379
+ if( isset($current->response[$key]) ) {
380
+ $themes[$key]->update = true;
381
+ $i++;
382
+ }
383
+ }
384
+ $title = sprintf(__('You have %1s themes that need to be updated', 'titan-security'), $i);
385
+ $description = "<b>" . __("Need to update themes, as previous versions may be vulnerable:", "titan-security") . "</b>";
386
+ if( !empty($themes_update) )
387
+ $description .= "<br>- " . implode("<br>- ", $themes_update);
388
+ if( $i ) {
389
+ $this->add($title, $description, 'medium', admin_url('update-core.php'));
390
+ }
391
+
392
+ return $this->results;
393
+ }
394
+
395
+ /**
396
+ * Check files audit
397
+ *
398
+ * @return AuditResult[] Results
399
+ */
400
+ public function check_files()
401
+ {
402
+ //readme.html
403
+ $title = __("Readme.html or readme.txt file is available in the site root", "titan-security");
404
+ $description = __("It is important to hide or delete the readme.html or readme.txt file, because it contains information about the WP version.", "titan-security");
405
+ if( file_exists(ABSPATH . "readme.html") || file_exists(ABSPATH . "readme.txt") ) {
406
+ $this->add($title, $description, 'low');
407
+ }
408
+
409
+ return $this->results;
410
+ }
411
+
412
+ /**
413
+ * Check database password
414
+ *
415
+ * @return AuditResult[] Results
416
+ */
417
+ public function check_fileEditor()
418
+ {
419
+ $title = __("The plugins and themes file editor is enabled on your site", "titan-security");
420
+ $description = __("The plugins and themes file editor is a security issue because it not only shows the PHP source code, it also enables attackers to inject malicious code into your site if they manage to gain access to admin.", "titan-security");
421
+ $description .= sprintf(__("Disable it for live websites in <b>wp_config.php:</b><br>%1\$s", "titan-security"), "<code>define('DISALLOW_FILE_EDIT', true);</code>");
422
+ if( !defined('DISALLOW_FILE_EDIT') || !DISALLOW_FILE_EDIT ) {
423
+ $this->add($title, $description, 'low');
424
+ }
425
+
426
+ return $this->results;
427
+ }
428
+
429
+ /**
430
+ * Check folders access
431
+ *
432
+ * @return AuditResult[] Results
433
+ */
434
+ public function check_folders_access()
435
+ {
436
+ $title = __("The Uploads folder is browsable.", "titan-security");
437
+ $description = __("Allowing anyone to view all files in the Uploads folder with a browser will allow them to easily download all your uploaded files.", "titan-security");
438
+
439
+ $url = wp_upload_dir();
440
+ $url = $url['baseurl'];
441
+ $response = wp_remote_get($url, array('redirection' => 0, 'sslverify' => 0));
442
+ if( !is_wp_error($response) ) {
443
+ $response_code = wp_remote_retrieve_response_code($response);
444
+ if( $response_code == 200 ) {
445
+ $this->add($title, $description, 'medium');
446
+ }
447
+ }
448
+
449
+ return $this->results;
450
+ }
451
+
452
+ /**
453
+ * Check self functions
454
+ *
455
+ * @return AuditResult[] Results
456
+ */
457
+ public function check_self() {
458
+ //FIREWALL
459
+ $title = __("The firewall is disabled.","titan-security");
460
+ $description = __("Firewall protects against password brute force and blocks suspicious activity.","titan-security");
461
+
462
+ $firewall = $this->plugin->getPopulateOption('firewall_mode', '');
463
+ if("disabled" === $firewall || empty($firewall)) {
464
+ $this->add( $title, $description, 'high', admin_url('admin.php?page=firewall-'.$this->plugin->getPluginName()) );
465
+ }
466
+
467
+ return $this->results;
468
+ }
469
+
470
+ /**
471
+ * @return int
472
+ */
473
+ public function get_count()
474
+ {
475
+ return is_array($this->results) ? count($this->results) : 0;
476
+ }
477
+
478
+
479
+ /**
480
+ * Show page content
481
+ */
482
+ public function showPageContent()
483
+ {
484
+ }
485
+
486
+ /**
487
+ * {@inheritdoc}
488
+ */
489
+ public function show_audit_all()
490
+ {
491
+ if( !current_user_can('manage_options') ) {
492
+ wp_die(-2);
493
+ } else {
494
+ check_ajax_referer('get_audits');
495
+
496
+ $args = array(
497
+ 'results' => $this->do_audit(),
498
+ );
499
+ echo $this->render_template('all-audit', $args);
500
+ die();
501
+ }
502
+ }
503
+
504
+ }
includes/audit/classes/class.auditresult.php ADDED
@@ -0,0 +1,75 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace WBCR\Titan;
3
+
4
+ // Exit if accessed directly
5
+ if ( ! defined( 'ABSPATH' ) ) {
6
+ exit;
7
+ }
8
+
9
+ /**
10
+ * Class AuditResult
11
+ *
12
+ * @package WBCR\Titan
13
+ *
14
+ * @author Artem Prihodko <webtemyk@ya.ru>
15
+ * @copyright (c) 2020 Creative Motion
16
+ * @version 1.0
17
+ */
18
+ class AuditResult {
19
+
20
+ /**
21
+ * @var string
22
+ */
23
+ public $type;
24
+
25
+ /**
26
+ * @var string
27
+ */
28
+ public $title;
29
+
30
+ /**
31
+ * @var string
32
+ */
33
+ public $description;
34
+
35
+ /**
36
+ * @var string
37
+ */
38
+ public $timestamp;
39
+
40
+ /**
41
+ * @var string
42
+ */
43
+ public $severity;
44
+
45
+ /**
46
+ * @var string
47
+ * '' - no fix (button not show)
48
+ * 'js' - fix via js (button show)
49
+ * 'http://site.com/wp-admin/?action=do_something' - fix via url (button show)
50
+ */
51
+ public $fix;
52
+
53
+ /**
54
+ * @var bool
55
+ */
56
+ public $hided = false;
57
+
58
+ /**
59
+ * AuditResult constructor.
60
+ *
61
+ * @param string $title
62
+ * @param string $description
63
+ * @param string $severity
64
+ * @param string $fix
65
+ * @param bool $hided
66
+ */
67
+ public function __construct( $title, $description, $severity, $fix, $hided = false ) {
68
+ $this->title = $title;
69
+ $this->description = $description;
70
+ $this->timestamp = time();
71
+ $this->severity = $severity;
72
+ $this->fix = $fix;
73
+ $this->hided = $hided;
74
+ }
75
+ }
includes/audit/classes/class.cert.php ADDED
@@ -0,0 +1,187 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+
4
+ namespace WBCR\Titan\Cert;
5
+
6
+ /**
7
+ * Class Cert
8
+ * @package WBCR\Titan\Cert
9
+ *
10
+ * @author Alexander Gorenkov <g.a.androidjc2@ya.ru>
11
+ * @version 1.0.0
12
+ */
13
+ class Cert {
14
+ const ERROR_NO_ERROR = 0;
15
+ const ERROR_UNAVAILABLE = 1;
16
+ const ERROR_ONLY_HTTPS = 2;
17
+ const ERROR_HTTPS_UNAVAILABLE = 3;
18
+ const ERROR_UNKNOWN_ERROR = -1;
19
+
20
+ /**
21
+ * @var Cert
22
+ */
23
+ private static $cert;
24
+
25
+ /**
26
+ * @var string
27
+ */
28
+ private $url;
29
+
30
+ /**
31
+ * @var array
32
+ */
33
+ private $cert_info = [];
34
+
35
+ /**
36
+ * @var bool
37
+ */
38
+ private $loaded = false;
39
+
40
+ private $error = self::ERROR_NO_ERROR;
41
+
42
+ /**
43
+ * Cert constructor.
44
+ *
45
+ * @param string $url
46
+ */
47
+ public function __construct( $url = null ) {
48
+ if ( is_null( $url ) ) {
49
+ $url = get_site_url( null, '', 'https' );
50
+ }
51
+
52
+ $this->url = $url;
53
+ }
54
+
55
+ public function get_cert_info() {
56
+ if(!$this->loaded) {
57
+ $this->load();
58
+ }
59
+
60
+ return $this->cert_info;
61
+ }
62
+
63
+ /**
64
+ * @return int
65
+ */
66
+ public function get_error() {
67
+ return $this->error;
68
+ }
69
+
70
+ /**
71
+ * @param bool $load
72
+ *
73
+ * @return bool
74
+ */
75
+ public function is_available($load = true) {
76
+ if ( ! function_exists( 'openssl_x509_parse' ) ) {
77
+ $this->error = self::ERROR_UNAVAILABLE;
78
+
79
+ return false;
80
+ }
81
+
82
+ if ( substr( $this->url, 0, 8 ) !== 'https://' ) {
83
+ $this->error = self::ERROR_ONLY_HTTPS;
84
+
85
+ return false;
86
+ }
87
+
88
+ if($load) {
89
+ return $this->load();
90
+ }
91
+
92
+ return true;
93
+ }
94
+
95
+ /**
96
+ * @return array|false|null
97
+ */
98
+ public function load() {
99
+ if ( ! $this->is_available( false ) ) {
100
+ return false;
101
+ }
102
+
103
+ if ( empty( $this->cert_info ) ) {
104
+ $g = stream_context_create( [ 'ssl' => [ 'capture_peer_cert' => true ] ] );
105
+ $r = @fopen( $this->url, 'rb', false, $g );
106
+ if($r === false) {
107
+ $this->error = self::ERROR_HTTPS_UNAVAILABLE;
108
+ return false;
109
+ }
110
+
111
+ $cert = stream_context_get_params( $r );
112
+ fclose( $r );
113
+ $this->cert_info = openssl_x509_parse( $cert['options']['ssl']['peer_certificate'], false );
114
+ }
115
+
116
+ $this->loaded = true;
117
+
118
+ return true;
119
+ }
120
+
121
+ /**
122
+ * @return int
123
+ */
124
+ public function get_expiration_timestamp() {
125
+ if ( ! $this->loaded || ! $this->load() ) {
126
+ return 0;
127
+ }
128
+
129
+ return $this->cert_info['validTo_time_t'];
130
+ }
131
+
132
+ /**
133
+ * @return string|null
134
+ */
135
+ public function get_issuer() {
136
+ if ( ! $this->loaded || ! $this->load() ) {
137
+ return null;
138
+ }
139
+
140
+ return $this->cert_info['issuer']['organizationName'];
141
+ }
142
+
143
+ /**
144
+ * @return bool
145
+ */
146
+ public function is_lets_encrypt() {
147
+ return $this->get_issuer() == 'Let\'s Encrypt';
148
+ }
149
+
150
+ /**
151
+ * @return string
152
+ */
153
+ public function get_error_message() {
154
+ switch($this->error) {
155
+ case self::ERROR_UNKNOWN_ERROR:
156
+ return __('Unknown error', 'titan-security');
157
+ break;
158
+
159
+ case self::ERROR_UNAVAILABLE:
160
+ return __('PHP openssl extension is missing', 'titan-security');
161
+ break;
162
+
163
+ case self::ERROR_ONLY_HTTPS:
164
+ return __('Verification is only available on HTTPS', 'titan-security');
165
+ break;
166
+
167
+ case self::ERROR_HTTPS_UNAVAILABLE:
168
+ return __('HTTPS is not activated on this site.', 'titan-security');
169
+ break;
170
+
171
+ case self::ERROR_NO_ERROR:
172
+ default:
173
+ return '';
174
+ }
175
+ }
176
+
177
+ /**
178
+ * @return Cert
179
+ */
180
+ public static function get_instance() {
181
+ if(is_null(self::$cert)) {
182
+ self::$cert = new Cert();
183
+ }
184
+
185
+ return self::$cert;
186
+ }
187
+ }
includes/audit/views/all-audit.php ADDED
@@ -0,0 +1,73 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /* @var array|string|int|float|bool|object $args data
3
+ * @var string $template_name template
4
+ */
5
+
6
+ if( is_array($args) && !empty($args) ) {
7
+ $audit = $args['results'];
8
+ if( $audit === false ) {
9
+ ?>
10
+ <div class="wtitan-audit-empty-container">
11
+ <?= sprintf(__('Click %1s to perform a security audit','titan-security'), '<span class="btn btn-primary wt-nobutton">'.__('Check now','titan-security').'</span>');?>
12
+ </div>
13
+ <?php
14
+ }
15
+ else if( !empty($audit) ) {
16
+ ?>
17
+ <div class="wtitan-scanner-vulner-table-container">
18
+ <table class="table table-striped table-hover table-responsive" width="100%">
19
+ <thead>
20
+ <tr>
21
+ <td class="wtitan-vulner-table-slim"></td>
22
+ <td class="wtitan-vulner-table-name">Title</td>
23
+ <td class="wtitan-vulner-table-description">Description</td>
24
+ <td class="wtitan-vulner-table-slim">Time</td>
25
+ <td class="wtitan-vulner-table-slim">Actions</td>
26
+ </tr>
27
+
28
+ </thead>
29
+ <tbody>
30
+ <?php
31
+ foreach($audit as $key => $result) {
32
+ /* @var \WBCR\Titan\AuditResult $result */
33
+ if( empty($result->description) ) {
34
+ $result->description = '&nbsp';
35
+ }
36
+ ?>
37
+ <tr>
38
+ <td class="wt-severity-<?php echo $result->severity; ?>"></td>
39
+ <td><?php echo $result->title; ?></td>
40
+ <td class="wtitan-vulner-table-description"><?php echo $result->description; ?></td>
41
+ <td><?php echo date_i18n('d.m.Y H:i', $result->timestamp); ?></td>
42
+ <td>
43
+ <a class="btn btn-default wt-scanner-hide-button"
44
+ data-id="<?php echo $key; ?>"
45
+ data-type="audit">Hide it</a>
46
+ <?php if( empty($result->fix) ): ?>
47
+ <?php elseif( $result->fix == "js" ): ?>
48
+ <a class="btn btn-primary wt-audit-fix-button" data-id="<?php echo esc_attr($key); ?>">Fix
49
+ it</a>
50
+ <?php else: ?>
51
+ <a href="<?php echo esc_url(add_query_arg('wtitan_fixing_issue_id', $key, $result->fix)); ?>" class="btn btn-primary">Fix
52
+ it</a>
53
+ <?php endif; ?>
54
+ </td>
55
+ </tr>
56
+ <?php
57
+ }
58
+ ?>
59
+ </tbody>
60
+ </table>
61
+ </div>
62
+ <?php
63
+ }
64
+ else {
65
+ ?>
66
+ <div class="wtitan-audit-empty-container">
67
+ <?= __('No security issues','titan-security');?>
68
+ </div>
69
+ <?php
70
+
71
+ }
72
+ }
73
+ ?>
includes/bruteforce/class-helpers.php ADDED
@@ -0,0 +1,65 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace WBCR\Titan\Bruteforce;
4
+
5
+ /**
6
+ * Class LLA_Helpers
7
+ */
8
+ class Helpers {
9
+
10
+ /**
11
+ * @param string $msg
12
+ */
13
+ public static function show_error($msg = '')
14
+ {
15
+ if( empty($msg) ) {
16
+ return;
17
+ }
18
+
19
+ echo '<div id="message" class="updated fade"><p>' . $msg . '</p></div>';
20
+ }
21
+
22
+ /**
23
+ * @param $log
24
+ *
25
+ * @return array
26
+ */
27
+ public static function sorted_log_by_date($log)
28
+ {
29
+ $new_log = array();
30
+
31
+ if( !is_array($log) || empty($log) ) {
32
+ return $new_log;
33
+ }
34
+
35
+ foreach($log as $ip => $users) {
36
+
37
+ if( !empty($users) ) {
38
+ foreach($users as $user_name => $info) {
39
+
40
+ if( is_array($info) ) { // For new plugin version
41
+ $new_log[$info['date']] = array(
42
+ 'ip' => $ip,
43
+ 'username' => $user_name,
44
+ 'counter' => $info['counter'],
45
+ 'gateway' => (isset($info['gateway'])) ? $info['gateway'] : '-',
46
+ 'unlocked' => !empty($info['unlocked']),
47
+ );
48
+ } else { // For old plugin version
49
+ $new_log[0] = array(
50
+ 'ip' => $ip,
51
+ 'username' => $user_name,
52
+ 'counter' => $info,
53
+ 'gateway' => '-',
54
+ 'unlocked' => false,
55
+ );
56
+ }
57
+ }
58
+ }
59
+ }
60
+
61
+ krsort($new_log);
62
+
63
+ return $new_log;
64
+ }
65
+ }
includes/bruteforce/class-limit-login-attempts.php ADDED
@@ -0,0 +1,1086 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace WBCR\Titan\Bruteforce;
4
+
5
+ /**
6
+ * Class Limit_Login_Attempts
7
+ */
8
+ class Limit_Login_Attempts {
9
+
10
+ public $default_options = array(
11
+ 'bruteforce_gdpr' => 0,
12
+
13
+ /* Are we behind a proxy? */
14
+ 'client_type' => WTITAN_BRUTEFORCE_DIRECT_ADDR,
15
+
16
+ /* Lock out after this many tries */
17
+ 'allowed_retries' => 4,
18
+
19
+ /* Lock out for this many seconds */
20
+ 'minutes_lockout' => 1200, // 20 minutes
21
+
22
+ /* Long lock out after this many lockouts */
23
+ 'allowed_lockouts' => 4,
24
+
25
+ /* Long lock out for this many seconds */
26
+ 'long_duration' => 86400, // 24 hours,
27
+
28
+ /* Reset failed attempts after this many seconds */
29
+ 'valid_duration' => 43200, // 12 hours
30
+
31
+ /* Also limit malformed/forged cookies? */
32
+ 'cookies' => true,
33
+
34
+ /* Notify on lockout. Values: '', 'log', 'email', 'log,email' */
35
+ 'lockout_notify' => 'log',
36
+
37
+ /* If notify by email, do so after this number of lockouts */
38
+ 'notify_email_after' => 4,
39
+
40
+ 'whitelist_ips' => array(),
41
+ 'whitelist_usernames' => array(),
42
+ 'blacklist_ips' => array(),
43
+ 'blacklist_usernames' => array(),
44
+ );
45
+
46
+ /**
47
+ * Errors messages
48
+ *
49
+ * @var array
50
+ */
51
+ public $_errors = array();
52
+
53
+ public function __construct()
54
+ {
55
+ $this->plugin = \WBCR\Titan\Plugin::app();
56
+ $this->hooks_init();
57
+ }
58
+
59
+ /**
60
+ * Register wp hooks and filters
61
+ */
62
+ public function hooks_init()
63
+ {
64
+ add_action('plugins_loaded', array($this, 'setup'), 9999);
65
+ add_action('after_password_reset', array($this, 'after_password_reset'));
66
+ add_filter('titan_limit_login_whitelist_ip', array($this, 'check_whitelist_ips'), 10, 2);
67
+ add_filter('titan_limit_login_whitelist_usernames', array($this, 'check_whitelist_usernames'), 10, 2);
68
+ add_filter('titan_limit_login_blacklist_ip', array($this, 'check_blacklist_ips'), 10, 2);
69
+ add_filter('titan_limit_login_blacklist_usernames', array($this, 'check_blacklist_usernames'), 10, 2);
70
+ add_filter('illegal_user_logins', array($this, 'register_user_blacklist'), 999);
71
+ }
72
+
73
+ /**
74
+ * Hook 'plugins_loaded'
75
+ */
76
+ public function setup()
77
+ {
78
+
79
+ // Setup default plugin options
80
+ //$this->sanitize_options();
81
+
82
+ add_action('wp_login_failed', array($this, 'limit_login_failed'));
83
+ add_filter('wp_authenticate_user', array($this, 'wp_authenticate_user'), 99999, 2);
84
+
85
+ add_filter('shake_error_codes', array($this, 'failure_shake'));
86
+ add_action('login_head', array($this, 'add_error_message'));
87
+ add_action('login_errors', array($this, 'fixup_error_messages'));
88
+
89
+ // Add notices for XMLRPC request
90
+ add_filter('xmlrpc_login_error', array($this, 'xmlrpc_error_messages'));
91
+
92
+ // Add notices to woocommerce login page
93
+ add_action('wp_head', array($this, 'add_wc_notices'));
94
+
95
+ /*
96
+ * This action should really be changed to the 'authenticate' filter as
97
+ * it will probably be deprecated. That is however only available in
98
+ * later versions of WP.
99
+ */
100
+ add_action('wp_authenticate', array($this, 'track_credentials'), 10, 2);
101
+ add_action('authenticate', array($this, 'authenticate_filter'), 5, 3);
102
+
103
+ if( defined('XMLRPC_REQUEST') && XMLRPC_REQUEST ) {
104
+ add_action('init', array($this, 'check_xmlrpc_lock'));
105
+ }
106
+
107
+ add_action('wp_ajax_limit-login-unlock', array($this, 'ajax_unlock'));
108
+ }
109
+
110
+ /**
111
+ * @param $user \Wp_User
112
+ */
113
+ public function after_password_reset($user)
114
+ {
115
+
116
+ $lockouts = $this->plugin->getOption('bruteforce_lockouts');
117
+ $lockouts_log = $this->plugin->getOption('bruteforce_logged');
118
+
119
+ if( $user->has_cap('administrator') ) {
120
+
121
+ if( $this->is_ip_blacklisted() ) {
122
+
123
+ $black_list_ips = $this->plugin->getOption('bruteforce_blacklist_ips');
124
+
125
+ if( !empty($black_list_ips) ) {
126
+
127
+ foreach($black_list_ips as $key => $ip) {
128
+
129
+ if( $ip === $this->get_address() ) {
130
+
131
+ unset($black_list_ips[$key]);
132
+ }
133
+ }
134
+ }
135
+
136
+ $this->plugin->updateOption('bruteforce_blacklist_ips', $black_list_ips);
137
+ }
138
+
139
+ if( $this->is_username_blacklisted($user->data->user_login) ) {
140
+
141
+ $black_list_usernames = $this->plugin->getOption('bruteforce_blacklist_usernames');
142
+
143
+ if( !empty($black_list_usernames) ) {
144
+
145
+ foreach($black_list_usernames as $key => $login) {
146
+
147
+ if( $login === $user->data->user_login ) {
148
+
149
+ unset($black_list_usernames[$key]);
150
+ }
151
+ }
152
+ }
153
+
154
+ $this->plugin->updateOption('bruteforce_blacklist_usernames', $black_list_usernames);
155
+ }
156
+
157
+ $admin_ip = $this->get_address();
158
+ $admin_ip = ($this->plugin->getOption('bruteforce_gdpr') ? $this->getHash($admin_ip) : $admin_ip);
159
+
160
+ if( is_array($lockouts) && isset($lockouts[$admin_ip]) ) {
161
+
162
+ unset($lockouts[$admin_ip]);
163
+
164
+ $this->plugin->updateOption('bruteforce_lockouts', $lockouts);
165
+
166
+ if( is_array($lockouts_log) && isset($lockouts_log[$admin_ip]) ) {
167
+
168
+ foreach($lockouts_log[$admin_ip] as $user_login => &$data) {
169
+
170
+ $data['unlocked'] = true;
171
+ }
172
+
173
+ $this->plugin->updateOption('bruteforce_logged', $lockouts_log);
174
+ }
175
+ }
176
+
177
+ $valid = $this->plugin->getOption('bruteforce_retries_valid');
178
+
179
+ if( is_array($valid) && isset($valid[$admin_ip]) ) {
180
+
181
+ unset($valid[$admin_ip]);
182
+
183
+ $this->plugin->updateOption('bruteforce_retries_valid', $valid);
184
+ }
185
+
186
+ $retries = $this->plugin->getOption('bruteforce_retries');
187
+
188
+ if( is_array($retries) && isset($retries[$admin_ip]) ) {
189
+
190
+ unset($retries[$admin_ip]);
191
+
192
+ $this->plugin->updateOption('bruteforce_retries', $retries);
193
+ }
194
+ } else {
195
+
196
+ $user_ip = $this->get_address();
197
+ $user_ip = ($this->plugin->getOption('bruteforce_gdpr') ? $this->getHash($user_ip) : $user_ip);
198
+
199
+ if( isset($lockouts_log[$user_ip]) && is_array($lockouts_log[$user_ip]) ) {
200
+
201
+ $last_unlocked_time = 0;
202
+ foreach($lockouts_log[$user_ip] as $user_login => $data) {
203
+
204
+ if( !isset($data['unlocked']) || !$data['unlocked'] ) {
205
+ continue;
206
+ }
207
+
208
+ if( $data['date'] > $last_unlocked_time ) {
209
+ $last_unlocked_time = $data['date'];
210
+ }
211
+ }
212
+
213
+ if( is_array($lockouts) && isset($lockouts[$user_ip]) && ($last_unlocked_time === 0 || ((time() - $last_unlocked_time)) > ($this->plugin->getOption('bruteforce_minutes_lockout'))) ) {
214
+
215
+ unset($lockouts[$user_ip]);
216
+
217
+ if( is_array($lockouts_log) && isset($lockouts_log[$user_ip]) ) {
218
+
219
+ foreach($lockouts_log[$user_ip] as $user_login => &$data) {
220
+
221
+ $data['unlocked'] = true;
222
+ }
223
+
224
+ $this->plugin->updateOption('bruteforce_logged', $lockouts_log);
225
+ }
226
+
227
+ $this->plugin->updateOption('bruteforce_lockouts', $lockouts);
228
+ }
229
+ }
230
+ }
231
+ }
232
+
233
+ public function check_xmlrpc_lock()
234
+ {
235
+ if( is_user_logged_in() || $this->is_ip_whitelisted() ) {
236
+ return;
237
+ }
238
+
239
+ if( $this->is_ip_blacklisted() || !$this->is_limit_login_ok() ) {
240
+ header('HTTP/1.0 403 Forbidden');
241
+ exit;
242
+ }
243
+ }
244
+
245
+ public function check_whitelist_ips($allow, $ip)
246
+ {
247
+ return $this->ip_in_range($ip, (array)$this->plugin->getOption('bruteforce_whitelist_ips'));
248
+ }
249
+
250
+ public function check_whitelist_usernames($allow, $username)
251
+ {
252
+ return in_array($username, (array)$this->plugin->getOption('bruteforce_whitelist_usernames'));
253
+ }
254
+
255
+ public function check_blacklist_ips($allow, $ip)
256
+ {
257
+ return $this->ip_in_range($ip, (array)$this->plugin->getOption('bruteforce_blacklist_ips'));
258
+ }
259
+
260
+ public function check_blacklist_usernames($allow, $username)
261
+ {
262
+ return in_array($username, (array)$this->plugin->getOption('bruteforce_blacklist_usernames'));
263
+ }
264
+
265
+ public function ip_in_range($ip, $list)
266
+ {
267
+ foreach($list as $range) {
268
+ $range = array_map('trim', explode('-', $range));
269
+ if( count($range) == 1 ) {
270
+ if( (string)$ip === (string)$range[0] ) {
271
+ return true;
272
+ }
273
+ } else {
274
+ $low = ip2long($range[0]);
275
+ $high = ip2long($range[1]);
276
+ $needle = ip2long($ip);
277
+
278
+ if( $low === false || $high === false || $needle === false ) {
279
+ continue;
280
+ }
281
+
282
+ $low = (float)sprintf("%u", $low);
283
+ $high = (float)sprintf("%u", $high);
284
+ $needle = (float)sprintf("%u", $needle);
285
+
286
+ if( $needle >= $low && $needle <= $high ) {
287
+ return true;
288
+ }
289
+ }
290
+ }
291
+
292
+ return false;
293
+ }
294
+
295
+ /**
296
+ * @param $blacklist
297
+ * @return array|null
298
+ */
299
+ public function register_user_blacklist($blacklist)
300
+ {
301
+
302
+ $black_list_usernames = $this->plugin->getOption('bruteforce_blacklist_usernames');
303
+
304
+ if( !empty($black_list_usernames) && is_array($black_list_usernames) ) {
305
+ $blacklist += $black_list_usernames;
306
+ }
307
+
308
+ return $blacklist;
309
+ }
310
+
311
+ /**
312
+ * @param $error \IXR_Error
313
+ *
314
+ * @return \IXR_Error
315
+ */
316
+ public function xmlrpc_error_messages($error)
317
+ {
318
+
319
+ if( !class_exists('IXR_Error') ) {
320
+ return $error;
321
+ }
322
+
323
+ if( !$this->is_limit_login_ok() ) {
324
+ return new \IXR_Error(403, $this->error_msg());
325
+ }
326
+
327
+ $ip = $this->get_address();
328
+ $retries = $this->plugin->getOption('bruteforce_retries');
329
+ $valid = $this->plugin->getOption('bruteforce_retries_valid');
330
+
331
+ /* Should we show retries remaining? */
332
+
333
+ if( !is_array($retries) || !is_array($valid) ) {
334
+ /* no retries at all */
335
+ return $error;
336
+ }
337
+ if( (!isset($retries[$ip]) && !isset($retries[$this->getHash($ip)])) || (!isset($valid[$ip]) && !isset($valid[$this->getHash($ip)])) || (time() > $valid[$ip] && time() > $valid[$this->getHash($ip)])
338
+
339
+ ) {
340
+ /* no: no valid retries */
341
+ return $error;
342
+ }
343
+ if( (((isset($retries[$ip]) ? $retries[$ip] : 0) + (isset($retries[$this->getHash($ip)]) ? $retries[$this->getHash($ip)] : 0)) % $this->plugin->getOption('bruteforce_allowed_retries')) == 0 ) {
344
+ //* no: already been locked out for these retries */
345
+ return $error;
346
+ }
347
+
348
+ $remaining = max(($this->plugin->getOption('bruteforce_allowed_retries') - (((isset($retries[$ip]) ? $retries[$ip] : 0) + (isset($retries[$this->getHash($ip)]) ? $retries[$this->getHash($ip)] : 0)) % $this->plugin->getOption('bruteforce_allowed_retries'))), 0);
349
+
350
+ return new \IXR_Error(403, sprintf(_n("<strong>%d</strong> attempt remaining.", "<strong>%d</strong> attempts remaining.", $remaining, 'titan-security'), $remaining));
351
+ }
352
+
353
+ /**
354
+ * Errors on WooCommerce account page
355
+ */
356
+ public function add_wc_notices()
357
+ {
358
+
359
+ global $limit_login_just_lockedout, $limit_login_nonempty_credentials, $limit_login_my_error_shown;
360
+
361
+ if( !function_exists('is_account_page') || !function_exists('wc_add_notice') ) {
362
+ return;
363
+ }
364
+
365
+ /*
366
+ * During lockout we do not want to show any other error messages (like
367
+ * unknown user or empty password).
368
+ */
369
+ if( empty($_POST) && !$this->is_limit_login_ok() && !$limit_login_just_lockedout ) {
370
+ if( is_account_page() ) {
371
+ wc_add_notice($this->error_msg(), 'error');
372
+ }
373
+ }
374
+ }
375
+
376
+ /**
377
+ * @param $user
378
+ * @param $username
379
+ * @param $password
380
+ *
381
+ * @return \WP_Error | \WP_User
382
+ */
383
+ public function authenticate_filter($user, $username, $password)
384
+ {
385
+
386
+ if( !empty($username) && !empty($password) ) {
387
+
388
+ $ip = $this->get_address();
389
+
390
+ // Check if username is blacklisted
391
+ if( !$this->is_username_whitelisted($username) && !$this->is_ip_whitelisted($ip) && ($this->is_username_blacklisted($username) || $this->is_ip_blacklisted($ip)) ) {
392
+
393
+ remove_filter('login_errors', array($this, 'fixup_error_messages'));
394
+ remove_filter('login_head', array($this, 'add_error_message'));
395
+ remove_filter('wp_login_failed', array($this, 'limit_login_failed'));
396
+ remove_filter('wp_authenticate_user', array($this, 'wp_authenticate_user'), 99999);
397
+ remove_filter('login_head', array($this, 'add_error_message'));
398
+ remove_filter('login_errors', array($this, 'fixup_error_messages'));
399
+
400
+ remove_filter('authenticate', 'wp_authenticate_username_password', 20);
401
+ remove_filter('authenticate', 'wp_authenticate_email_password', 20);
402
+
403
+ $user = new \WP_Error();
404
+ $user->add('username_blacklisted', "<strong>ERROR:</strong> Too many failed login attempts.");
405
+ } elseif( $this->is_username_whitelisted($username) || $this->is_ip_whitelisted($ip) ) {
406
+
407
+ remove_filter('wp_login_failed', array($this, 'limit_login_failed'));
408
+ remove_filter('wp_authenticate_user', array($this, 'wp_authenticate_user'), 99999);
409
+ remove_filter('login_head', array($this, 'add_error_message'));
410
+ remove_filter('login_errors', array($this, 'fixup_error_messages'));
411
+ }
412
+ }
413
+
414
+ return $user;
415
+ }
416
+
417
+ /**
418
+ * Check if it is ok to login
419
+ *
420
+ * @return bool
421
+ */
422
+ public function is_limit_login_ok()
423
+ {
424
+
425
+ $ip = $this->get_address();
426
+
427
+ /* Check external whitelist filter */
428
+ if( $this->is_ip_whitelisted($ip) ) {
429
+ return true;
430
+ }
431
+
432
+ /* lockout active? */
433
+ $lockouts = $this->plugin->getOption('bruteforce_lockouts');
434
+
435
+ $a = $this->checkKey($lockouts, $ip);
436
+ $b = $this->checkKey($lockouts, $this->getHash($ip));
437
+
438
+ return (!is_array($lockouts) || (!isset($lockouts[$ip]) && !isset($lockouts[$this->getHash($ip)])) || (time() >= $a && time() >= $b));
439
+ }
440
+
441
+ /**
442
+ * Action when login attempt failed
443
+ *
444
+ * Increase nr of retries (if necessary). Reset valid value. Setup
445
+ * lockout if nr of retries are above threshold. And more!
446
+ *
447
+ * A note on external whitelist: retries and statistics are still counted and
448
+ * notifications done as usual, but no lockout is done.
449
+ *
450
+ * @param $username
451
+ */
452
+ public function limit_login_failed($username)
453
+ {
454
+
455
+ $ip = $this->get_address();
456
+ $ipHash = $this->getHash($this->get_address());
457
+
458
+ /* if currently locked-out, do not add to retries */
459
+ $lockouts = $this->plugin->getOption('bruteforce_lockouts');
460
+
461
+ if( !is_array($lockouts) ) {
462
+ $lockouts = array();
463
+ }
464
+
465
+ if( (isset($lockouts[$ip]) && time() < $lockouts[$ip]) || (isset($lockouts[$ipHash]) && time() < $lockouts[$ipHash]) ) {
466
+ return;
467
+ }
468
+
469
+ /* Get the arrays with retries and retries-valid information */
470
+ $retries = $this->plugin->getOption('bruteforce_retries');
471
+ $valid = $this->plugin->getOption('bruteforce_retries_valid');
472
+
473
+ if( !is_array($retries) ) {
474
+ $retries = array();
475
+ $this->plugin->updateOption('bruteforce_retries', $retries);
476
+ }
477
+
478
+ if( !is_array($valid) ) {
479
+ $valid = array();
480
+ $this->plugin->updateOption('bruteforce_retries_valid', $valid);
481
+ }
482
+
483
+ $gdpr = $this->plugin->getOption('bruteforce_gdpr');
484
+ $ip = ($gdpr ? $ipHash : $ip);
485
+ /* Check validity and add one to retries */
486
+ if( isset($retries[$ip]) && isset($valid[$ip]) && time() < $valid[$ip] ) {
487
+ $retries[$ip]++;
488
+ } else {
489
+ $retries[$ip] = 1;
490
+ }
491
+ $valid[$ip] = time() + $this->plugin->getOption('bruteforce_valid_duration');
492
+
493
+ /* lockout? */
494
+ if( $retries[$ip] % $this->plugin->getOption('bruteforce_allowed_retries') != 0 ) {
495
+ /*
496
+ * Not lockout (yet!)
497
+ * Do housecleaning (which also saves retry/valid values).
498
+ */
499
+ $this->cleanup($retries, null, $valid);
500
+
501
+ return;
502
+ }
503
+
504
+ /* lockout! */
505
+ $whitelisted = $this->is_ip_whitelisted($ip);
506
+ $retries_long = $this->plugin->getOption('bruteforce_allowed_retries') * $this->default_options['allowed_lockouts'];
507
+
508
+ /*
509
+ * Note that retries and statistics are still counted and notifications
510
+ * done as usual for whitelisted ips , but no lockout is done.
511
+ */
512
+ if( $whitelisted ) {
513
+ if( $retries[$ip] >= $retries_long ) {
514
+ unset($retries[$ip]);
515
+ unset($valid[$ip]);
516
+ }
517
+ } else {
518
+ global $limit_login_just_lockedout;
519
+ $limit_login_just_lockedout = true;
520
+ $gdpr = $this->plugin->getOption('bruteforce_gdpr');
521
+ $index = ($gdpr ? $ipHash : $ip);
522
+
523
+ /* setup lockout, reset retries as needed */
524
+ if( (isset($retries[$ip]) ? $retries[$ip] : 0) >= $retries_long || (isset($retries[$ipHash]) ? $retries[$ipHash] : 0) >= $retries_long ) {
525
+ /* long lockout */
526
+ $lockouts[$index] = time() + $this->default_options['long_duration'];
527
+ unset($retries[$index]);
528
+ unset($valid[$index]);
529
+ } else {
530
+ /* normal lockout */
531
+ $lockouts[$index] = time() + $this->plugin->getOption('bruteforce_minutes_lockout');
532
+ }
533
+ }
534
+
535
+ /* do housecleaning and save values */
536
+ $this->cleanup($retries, $lockouts, $valid);
537
+
538
+ /* do any notification */
539
+ $this->notify_log($username);
540
+
541
+ /* increase statistics */
542
+ $total = $this->plugin->getOption('bruteforce_lockouts_total');
543
+ if( $total === false || !is_numeric($total) ) {
544
+ $this->plugin->updateOption('bruteforce_lockouts_total', 1);
545
+ } else {
546
+ $this->plugin->updateOption('bruteforce_lockouts_total', $total + 1);
547
+ }
548
+ }
549
+
550
+ /**
551
+ * Logging of lockout (if configured)
552
+ *
553
+ * @param $user_login
554
+ *
555
+ * @internal param $user
556
+ */
557
+ public function notify_log($user_login)
558
+ {
559
+
560
+ if( !$user_login ) {
561
+ return;
562
+ }
563
+
564
+ $log = $option = $this->plugin->getOption('bruteforce_logged');
565
+ if( !is_array($log) ) {
566
+ $log = array();
567
+ }
568
+ $ip = $this->get_address();
569
+
570
+ $index = ($this->plugin->getOption('bruteforce_gdpr') ? $this->getHash($ip) : $ip);
571
+ /* can be written much simpler, if you do not mind php warnings */
572
+ if( !isset($log[$index]) ) {
573
+ $log[$index] = array();
574
+ }
575
+
576
+ if( !isset($log[$index][$user_login]) ) {
577
+ $log[$index][$user_login] = array('counter' => 0);
578
+ } elseif( !is_array($log[$index][$user_login]) ) {
579
+ $log[$index][$user_login] = array(
580
+ 'counter' => $log[$index][$user_login],
581
+ );
582
+ }
583
+
584
+ $log[$index][$user_login]['counter']++;
585
+ $log[$index][$user_login]['date'] = time();
586
+
587
+ if( isset($_POST['woocommerce-login-nonce']) ) {
588
+ $gateway = 'WooCommerce';
589
+ } elseif( isset($GLOBALS['wp_xmlrpc_server']) && is_object($GLOBALS['wp_xmlrpc_server']) ) {
590
+ $gateway = 'XMLRPC';
591
+ } else {
592
+ $gateway = 'WP Login';
593
+ }
594
+
595
+ $log[$index][$user_login]['gateway'] = $gateway;
596
+
597
+ if( $option === false ) {
598
+ $this->plugin->updateOption('bruteforce_logged', $log);
599
+ } else {
600
+ $this->plugin->updateOption('bruteforce_logged', $log);
601
+ }
602
+ }
603
+
604
+ /**
605
+ * Check if IP is whitelisted.
606
+ *
607
+ * This function allow external ip whitelisting using a filter. Note that it can
608
+ * be called multiple times during the login process.
609
+ *
610
+ * Note that retries and statistics are still counted and notifications
611
+ * done as usual for whitelisted ips , but no lockout is done.
612
+ *
613
+ * Example:
614
+ * function my_ip_whitelist($allow, $ip) {
615
+ * return ($ip == 'my-ip') ? true : $allow;
616
+ * }
617
+ * add_filter('titan_limit_login_whitelist_ip', 'my_ip_whitelist', 10, 2);
618
+ *
619
+ * @param null $ip
620
+ *
621
+ * @return bool
622
+ */
623
+ public function is_ip_whitelisted($ip = null)
624
+ {
625
+
626
+ if( is_null($ip) ) {
627
+ $ip = $this->get_address();
628
+ }
629
+
630
+ $whitelisted = apply_filters('titan_limit_login_whitelist_ip', false, $ip);
631
+
632
+ return ($whitelisted === true);
633
+ }
634
+
635
+ public function is_username_whitelisted($username)
636
+ {
637
+
638
+ if( empty($username) ) {
639
+ return false;
640
+ }
641
+
642
+ $whitelisted = apply_filters('titan_limit_login_whitelist_usernames', false, $username);
643
+
644
+ return ($whitelisted === true);
645
+ }
646
+
647
+ public function is_ip_blacklisted($ip = null)
648
+ {
649
+
650
+ if( is_null($ip) ) {
651
+ $ip = $this->get_address();
652
+ }
653
+
654
+ $whitelisted = apply_filters('titan_limit_login_blacklist_ip', false, $ip);
655
+
656
+ return ($whitelisted === true);
657
+ }
658
+
659
+ public function is_username_blacklisted($username)
660
+ {
661
+
662
+ if( empty($username) ) {
663
+ return false;
664
+ }
665
+
666
+ $whitelisted = apply_filters('titan_limit_login_blacklist_usernames', false, $username);
667
+
668
+ return ($whitelisted === true);
669
+ }
670
+
671
+ /**
672
+ * Filter: allow login attempt? (called from wp_authenticate())
673
+ *
674
+ * @param $user \WP_User
675
+ * @param $password
676
+ *
677
+ * @return \WP_Error
678
+ */
679
+ public function wp_authenticate_user($user, $password)
680
+ {
681
+
682
+ if( is_wp_error($user) || $this->check_whitelist_ips(false, $this->get_address()) || $this->check_whitelist_usernames(false, $user->user_login) || $this->is_limit_login_ok() ) {
683
+
684
+ return $user;
685
+ }
686
+
687
+ $error = new \WP_Error();
688
+
689
+ global $limit_login_my_error_shown;
690
+ $limit_login_my_error_shown = true;
691
+
692
+ if( $this->is_username_blacklisted($user->user_login) || $this->is_ip_blacklisted($this->get_address()) ) {
693
+ $error->add('username_blacklisted', "<strong>ERROR:</strong> Too many failed login attempts.");
694
+ } else {
695
+ // This error should be the same as in "shake it" filter below
696
+ $error->add('too_many_retries', $this->error_msg());
697
+ }
698
+
699
+ return $error;
700
+ }
701
+
702
+ /**
703
+ * Filter: add this failure to login page "Shake it!"
704
+ *
705
+ * @param $error_codes
706
+ *
707
+ * @return array
708
+ */
709
+ public function failure_shake($error_codes)
710
+ {
711
+ $error_codes[] = 'too_many_retries';
712
+ $error_codes[] = 'username_blacklisted';
713
+
714
+ return $error_codes;
715
+ }
716
+
717
+ /**
718
+ * Keep track of if user or password are empty, to filter errors correctly
719
+ *
720
+ * @param $user
721
+ * @param $password
722
+ */
723
+ public function track_credentials($user, $password)
724
+ {
725
+ global $limit_login_nonempty_credentials;
726
+
727
+ $limit_login_nonempty_credentials = (!empty($user) && !empty($password));
728
+ }
729
+
730
+ /**
731
+ * Should we show errors and messages on this page?
732
+ *
733
+ * @return bool
734
+ */
735
+ public function login_show_msg()
736
+ {
737
+ if( isset($_GET['key']) ) {
738
+ /* reset password */
739
+ return false;
740
+ }
741
+
742
+ $action = isset($_REQUEST['action']) ? $_REQUEST['action'] : '';
743
+
744
+ return ($action != 'lostpassword' && $action != 'retrievepassword' && $action != 'resetpass' && $action != 'rp' && $action != 'register');
745
+ }
746
+
747
+ /**
748
+ * Construct informative error message
749
+ *
750
+ * @return string
751
+ */
752
+ public function error_msg()
753
+ {
754
+ $ip = $this->get_address();
755
+ $lockouts = $this->plugin->getOption('bruteforce_lockouts');
756
+ $a = $this->checkKey($lockouts, $ip);
757
+ $b = $this->checkKey($lockouts, $this->getHash($ip));
758
+
759
+ $msg = __('<strong>ERROR</strong>: Too many failed login attempts.', 'titan-security') . ' ';
760
+
761
+ if( !is_array($lockouts) || (!isset($lockouts[$ip]) && !isset($lockouts[$this->getHash($ip)])) || (time() >= $a && time() >= $b) ) {
762
+ /* Huh? No timeout active? */
763
+ $msg .= __('Please try again later.', 'titan-security');
764
+
765
+ return $msg;
766
+ }
767
+
768
+ $when = ceil((($a > $b ? $a : $b) - time()) / 60);
769
+ if( $when > 60 ) {
770
+ $when = ceil($when / 60);
771
+ $msg .= sprintf(_n('Please try again in %d hour.', 'Please try again in %d hours.', $when, 'titan-security'), $when);
772
+ } else {
773
+ $msg .= sprintf(_n('Please try again in %d minute.', 'Please try again in %d minutes.', $when, 'titan-security'), $when);
774
+ }
775
+
776
+ $msg .= '<br><br>' . sprintf(__('You can also try <a href="%s">resetting your password</a> and that should help you to log in.', 'titan-security'), wp_lostpassword_url());
777
+
778
+ return $msg;
779
+ }
780
+
781
+ /**
782
+ * Add a message to login page when necessary
783
+ */
784
+ public function add_error_message()
785
+ {
786
+ global $error, $limit_login_my_error_shown;
787
+
788
+ if( !$this->login_show_msg() || $limit_login_my_error_shown ) {
789
+ return;
790
+ }
791
+
792
+ $msg = $this->get_message();
793
+
794
+ if( $msg != '' ) {
795
+ $limit_login_my_error_shown = true;
796
+ $error .= $msg;
797
+ }
798
+
799
+ return;
800
+ }
801
+
802
+ /**
803
+ * Fix up the error message before showing it
804
+ *
805
+ * @param $content
806
+ *
807
+ * @return string
808
+ */
809
+ public function fixup_error_messages($content)
810
+ {
811
+ global $limit_login_just_lockedout, $limit_login_nonempty_credentials, $limit_login_my_error_shown;
812
+
813
+ if( !$this->login_show_msg() ) {
814
+ return $content;
815
+ }
816
+
817
+ /*
818
+ * During lockout we do not want to show any other error messages (like
819
+ * unknown user or empty password).
820
+ */
821
+ if( !$this->is_limit_login_ok() && !$limit_login_just_lockedout ) {
822
+ return $this->error_msg();
823
+ }
824
+
825
+ /*
826
+ * We want to filter the messages 'Invalid username' and
827
+ * 'Invalid password' as that is an information leak regarding user
828
+ * account names (prior to WP 2.9?).
829
+ *
830
+ * Also, if more than one error message, put an extra <br /> tag between
831
+ * them.
832
+ */
833
+ $msgs = explode("<br />\n", $content);
834
+
835
+ if( strlen(end($msgs)) == 0 ) {
836
+ /* remove last entry empty string */
837
+ array_pop($msgs);
838
+ }
839
+
840
+ $count = count($msgs);
841
+ $my_warn_count = $limit_login_my_error_shown ? 1 : 0;
842
+
843
+ if( $limit_login_nonempty_credentials && $count > $my_warn_count ) {
844
+
845
+ /* Replace error message, including ours if necessary */
846
+ if( !empty($_REQUEST['log']) && is_email($_REQUEST['log']) ) {
847
+ $content = __('<strong>ERROR</strong>: Incorrect email address or password.', 'titan-security') . "<br />\n";
848
+ } else {
849
+ $content = __('<strong>ERROR</strong>: Incorrect username or password.', 'titan-security') . "<br />\n";
850
+ }
851
+
852
+ if( $limit_login_my_error_shown || $this->get_message() ) {
853
+ $content .= "<br />\n" . $this->get_message() . "<br />\n";
854
+ }
855
+
856
+ return $content;
857
+ } elseif( $count <= 1 ) {
858
+ return $content;
859
+ }
860
+
861
+ $new = '';
862
+ while( $count-- > 0 ) {
863
+ $new .= array_shift($msgs) . "<br />\n";
864
+ if( $count > 0 ) {
865
+ $new .= "<br />\n";
866
+ }
867
+ }
868
+
869
+ return $new;
870
+ }
871
+
872
+ public function fixup_error_messages_wc(\WP_Error $error)
873
+ {
874
+ $error->add(1, __('WC Error'));
875
+ }
876
+
877
+ /**
878
+ * Return current (error) message to show, if any
879
+ *
880
+ * @return string
881
+ */
882
+ public function get_message()
883
+ {
884
+ /* Check external whitelist */
885
+ if( $this->is_ip_whitelisted() ) {
886
+ return '';
887
+ }
888
+
889
+ /* Is lockout in effect? */
890
+ if( !$this->is_limit_login_ok() ) {
891
+ return $this->error_msg();
892
+ }
893
+
894
+ return $this->retries_remaining_msg();
895
+ }
896
+
897
+ /**
898
+ * Construct retries remaining message
899
+ *
900
+ * @return string
901
+ */
902
+ public function retries_remaining_msg()
903
+ {
904
+ $ip = $this->get_address();
905
+ $retries = $this->plugin->getOption('bruteforce_retries');
906
+ $valid = $this->plugin->getOption('bruteforce_retries_valid');
907
+ $a = $this->checkKey($retries, $ip);
908
+ $b = $this->checkKey($retries, $this->getHash($ip));
909
+ $c = $this->checkKey($valid, $ip);
910
+ $d = $this->checkKey($valid, $this->getHash($ip));
911
+
912
+ /* Should we show retries remaining? */
913
+ if( !is_array($retries) || !is_array($valid) ) {
914
+ /* no retries at all */
915
+ return '';
916
+ }
917
+ if( (!isset($retries[$ip]) && !isset($retries[$this->getHash($ip)])) || (!isset($valid[$ip]) && !isset($valid[$this->getHash($ip)])) || (time() > $c && time() > $d) ) {
918
+ /* no: no valid retries */
919
+ return '';
920
+ }
921
+ if( ($a % $this->plugin->getOption('bruteforce_allowed_retries')) == 0 && ($b % $this->plugin->getOption('bruteforce_allowed_retries')) == 0 ) {
922
+ /* no: already been locked out for these retries */
923
+ return '';
924
+ }
925
+
926
+ $remaining = max(($this->plugin->getOption('bruteforce_allowed_retries') - (($a + $b) % $this->plugin->getOption('bruteforce_allowed_retries'))), 0);
927
+
928
+ return sprintf(_n("<strong>%d</strong> attempt remaining.", "<strong>%d</strong> attempts remaining.", $remaining, 'titan-security'), $remaining);
929
+ }
930
+
931
+ /**
932
+ * Get correct remote address
933
+ *
934
+ * @return string
935
+ *
936
+ */
937
+ public function get_address()
938
+ {
939
+
940
+ $trusted_ip_origins = $this->default_options['client_type']; //$this->plugin->getOption('trusted_ip_origins');
941
+
942
+ if( empty($trusted_ip_origins) || !is_array($trusted_ip_origins) ) {
943
+
944
+ $trusted_ip_origins = array();
945
+ }
946
+
947
+ if( !in_array('REMOTE_ADDR', $trusted_ip_origins) ) {
948
+
949
+ $trusted_ip_origins[] = 'REMOTE_ADDR';
950
+ }
951
+
952
+ $ip = '';
953
+ foreach($trusted_ip_origins as $origin) {
954
+
955
+ if( isset($_SERVER[$origin]) && !empty($_SERVER[$origin]) ) {
956
+
957
+ $ip = $_SERVER[$origin];
958
+ break;
959
+ }
960
+ }
961
+
962
+ $ip = preg_replace('/^(\d+\.\d+\.\d+\.\d+):\d+$/', '\1', $ip);
963
+
964
+ return $ip;
965
+ }
966
+
967
+ /**
968
+ * Clean up old lockouts and retries, and save supplied arrays
969
+ *
970
+ * @param null $retries
971
+ * @param null $lockouts
972
+ * @param null $valid
973
+ */
974
+ public function cleanup($retries = null, $lockouts = null, $valid = null)
975
+ {
976
+ $now = time();
977
+ $lockouts = !is_null($lockouts) ? $lockouts : $this->plugin->getOption('bruteforce_lockouts');
978
+
979
+ $log = $this->plugin->getOption('bruteforce_logged');
980
+
981
+ /* remove old lockouts */
982
+ if( is_array($lockouts) ) {
983
+ foreach($lockouts as $ip => $lockout) {
984
+ if( $lockout < $now ) {
985
+ unset($lockouts[$ip]);
986
+
987
+ if( is_array($log) && isset($log[$ip]) ) {
988
+ foreach($log[$ip] as $user_login => &$data) {
989
+
990
+ $data['unlocked'] = true;
991
+ }
992
+ }
993
+ }
994
+ }
995
+ $this->plugin->updateOption('bruteforce_lockouts', $lockouts);
996
+ }
997
+
998
+ $this->plugin->updateOption('bruteforce_logged', $log);
999
+
1000
+ /* remove retries that are no longer valid */
1001
+ $valid = !is_null($valid) ? $valid : $this->plugin->getOption('bruteforce_retries_valid');
1002
+ $retries = !is_null($retries) ? $retries : $this->plugin->getOption('bruteforce_retries');
1003
+ if( !is_array($valid) || !is_array($retries) ) {
1004
+ return;
1005
+ }
1006
+
1007
+ foreach($valid as $ip => $lockout) {
1008
+ if( $lockout < $now ) {
1009
+ unset($valid[$ip]);
1010
+ unset($retries[$ip]);
1011
+ }
1012
+ }
1013
+
1014
+ /* go through retries directly, if for some reason they've gone out of sync */
1015
+ foreach($retries as $ip => $retry) {
1016
+ if( !isset($valid[$ip]) ) {
1017
+ unset($retries[$ip]);
1018
+ }
1019
+ }
1020
+
1021
+ $this->plugin->updateOption('bruteforce_retries', $retries);
1022
+ $this->plugin->updateOption('bruteforce_retries_valid', $valid);
1023
+ }
1024
+
1025
+ public function ajax_unlock()
1026
+ {
1027
+ check_ajax_referer('limit-login-unlock', 'sec');
1028
+ $ip = (string)@$_POST['ip'];
1029
+
1030
+ $lockouts = (array)$this->plugin->getOption('bruteforce_lockouts');
1031
+
1032
+ if( isset($lockouts[$ip]) ) {
1033
+ unset($lockouts[$ip]);
1034
+ $this->plugin->updateOption('bruteforce_lockouts', $lockouts);
1035
+ }
1036
+
1037
+ //save to log
1038
+ $user_login = @(string)$_POST['username'];
1039
+ $log = $this->plugin->getOption('bruteforce_logged');
1040
+
1041
+ if( @$log[$ip][$user_login] ) {
1042
+ if( !is_array($log[$ip][$user_login]) ) {
1043
+ $log[$ip][$user_login] = array(
1044
+ 'counter' => $log[$ip][$user_login],
1045
+ );
1046
+ }
1047
+ $log[$ip][$user_login]['unlocked'] = true;
1048
+
1049
+ $this->plugin->updateOption('bruteforce_logged', $log);
1050
+ }
1051
+
1052
+ header('Content-Type: application/json');
1053
+ echo 'true';
1054
+ exit;
1055
+ }
1056
+
1057
+ /**
1058
+ * Show error message
1059
+ *
1060
+ * @param $msg
1061
+ */
1062
+ public function show_error($msg)
1063
+ {
1064
+ Helpers::show_error($msg);
1065
+ }
1066
+
1067
+ /**
1068
+ * returns IP with its md5 value
1069
+ */
1070
+ private function getHash($str)
1071
+ {
1072
+ return md5($str);
1073
+ }
1074
+
1075
+ /**
1076
+ * @param $arr - array
1077
+ * @param $k - key
1078
+ * @return int array value at given index or zero
1079
+ */
1080
+ private function checkKey($arr, $k)
1081
+ {
1082
+ return isset($arr[$k]) ? $arr[$k] : 0;
1083
+ }
1084
+ }
1085
+
1086
+ new Limit_Login_Attempts();
includes/bruteforce/const.php ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
1
+ <?php
2
+ /***************************************************************************************
3
+ * Different ways to get remote address: direct & behind proxy
4
+ **************************************************************************************/
5
+ define('WTITAN_BRUTEFORCE_DIRECT_ADDR', 'REMOTE_ADDR');
6
+ define('WTITAN_BRUTEFORCE_PROXY_ADDR', 'HTTP_X_FORWARDED_FOR');
includes/bruteforce/do_activate.php ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ if( !\WBCR\Titan\Plugin::app()->getOption('bruteforce_set_default_options') ) {
4
+ \WBCR\Titan\Plugin::app()->updateOption('bruteforce_set_default_options', 1);
5
+ \WBCR\Titan\Plugin::app()->updateOption('bruteforce_gdpr', 0);
6
+ \WBCR\Titan\Plugin::app()->updateOption('bruteforce_logged', '');
7
+ \WBCR\Titan\Plugin::app()->updateOption('bruteforce_lockouts_total', 0);
8
+ \WBCR\Titan\Plugin::app()->updateOption('bruteforce_minutes_lockout', 1200);
9
+ \WBCR\Titan\Plugin::app()->updateOption('bruteforce_valid_duration', 43200);
10
+ \WBCR\Titan\Plugin::app()->updateOption('bruteforce_allowed_retries', 4);
11
+ \WBCR\Titan\Plugin::app()->updateOption('bruteforce_lockouts', array());
12
+ \WBCR\Titan\Plugin::app()->updateOption('bruteforce_whitelist_ips', array());
13
+ \WBCR\Titan\Plugin::app()->updateOption('bruteforce_whitelist_usernames', array());
14
+ \WBCR\Titan\Plugin::app()->updateOption('bruteforce_blacklist_ips', array());
15
+ \WBCR\Titan\Plugin::app()->updateOption('bruteforce_blacklist_usernames', array());
16
+ }
17
+
includes/check/assets/css/check-dashboard.css ADDED
@@ -0,0 +1,148 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ .wt-scanner-container
2
+ {
3
+ margin: 15px 15px 15px 15px;
4
+ }
5
+ #wtitan-scanner-tabs li a{
6
+ text-decoration: none;
7
+ color: #0073aa !important;
8
+ }
9
+ /*----------------*/
10
+ .wt-scanner-block-scan
11
+ {
12
+ text-align: center;
13
+ }
14
+ .wt-scanner-block-scan table {
15
+ width: 100%;
16
+ }
17
+ .wt-scanner-block-scan table td:first-child {
18
+ width: 20%;
19
+ }
20
+ .wt-scanner-block-scan table td {
21
+ border: 1px solid #efefef;
22
+ background: #fff;
23
+ text-align: center;
24
+ padding: 20px;
25
+ }
26
+ .wt-scanner-block-scan table td h4 {
27
+ font-family: Roboto, "Helvetica Neue", Helvetica, Arial, sans-serif;
28
+ font-size: 16px;
29
+ }
30
+ /*----------------*/
31
+ .wt-scanner-scanbutton
32
+ {
33
+ font-size: 20px !important;
34
+ /*box-shadow: 0px 0px 10px 3px rgba(0, 0, 0, 0.2);*/
35
+ border: 1px solid #0055808c !important;
36
+ }
37
+ .wt-scan-progress
38
+ {
39
+ padding: 20px 20px 0 20px;
40
+ }
41
+ .wt-scan-progress-ul
42
+ {
43
+ display: -webkit-inline-flex;
44
+ display: inline-flex;
45
+ -webkit-align-items: flex-start;
46
+ align-items: flex-start;
47
+ -webkit-justify-content: center;
48
+ justify-content: center;
49
+ -webkit-flex-direction: row;
50
+ flex-direction: row;
51
+ -webkit-flex-wrap: nowrap;
52
+ flex-wrap: nowrap;
53
+ margin-top: 0.5rem;
54
+ margin-bottom: 0.5rem;
55
+ background: url("data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPCFET0NUWVBFIHN2ZyBQVUJMSUMgIi0vL1czQy8vRFREIFNWRyAxLjEvL0VOIiAiaHR0cDovL3d3dy53My5vcmcvR3JhcGhpY3MvU1ZHLzEuMS9EVEQvc3ZnMTEuZHRkIj4KPHN2ZyB3aWR0aD0iMXB4IiBoZWlnaHQ9IjEwcHgiIHZpZXdCb3g9IjAgMCAxIDYiIHZlcnNpb249IjEuMSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+Cgk8cmVjdCB4PSIwIiB5PSIwIiB3aWR0aD0iMSIgaGVpZ2h0PSIxIiBmaWxsPSIjZDFkMWQxIi8+CjxyZWN0IHg9IjAiIHk9IjciIHdpZHRoPSIxIiBoZWlnaHQ9IjEiIGZpbGw9IiNkMWQxZDEiLz4KPC9zdmc+");
56
+ background-repeat: repeat-x;
57
+ background-position: center 24px;
58
+ overflow-x: auto;
59
+ overflow-y: hidden;
60
+ height: 90px;
61
+ }
62
+ .wt-scan-progress-ul > .wt-scan-progress-li
63
+ {
64
+ display: -webkit-flex;
65
+ display: flex;
66
+ -webkit-align-items: center;
67
+ align-items: center;
68
+ -webkit-justify-content: center;
69
+ justify-content: center;
70
+ -webkit-flex-direction: column;
71
+ flex-direction: column;
72
+ padding-left: 0.5rem;
73
+ padding-right: 0.5rem;
74
+ width: 125px;
75
+ }
76
+ .wt-scan-progress-ul > .wt-scan-progress-li:first-of-type
77
+ {
78
+ padding-left: 0;
79
+ background-image: url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiP…dpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9InVybCgjZ3JhZCkiIC8+PC9zdmc+IA==);
80
+ background-size: 100%;
81
+ background-image: -webkit-gradient(linear, 0% 50%, 100% 50%, color-stop(0%, #f7f7f7),color-stop(50%, #f7f7f7),color-stop(51%, rgba(255,255,255,0)),color-stop(100%, rgba(255,255,255,0)));
82
+ background-image: -moz-linear-gradient(left, #f7f7f7 0%,#f7f7f7 50%,rgba(255,255,255,0) 51%,rgba(255,255,255,0) 100%);
83
+ background-image: -webkit-linear-gradient(left, #f7f7f7 0%,#f7f7f7 50%,rgba(255,255,255,0) 51%,rgba(255,255,255,0) 100%);
84
+ background-image: linear-gradient(to right, #f7f7f7 0%,#f7f7f7 50%,rgba(255,255,255,0) 51%,rgba(255,255,255,0) 100%);
85
+ background-repeat: no-repeat;
86
+ background-position: left center;
87
+ }
88
+ .wt-scan-progress-ul > .wt-scan-progress-li:last-of-type
89
+ {
90
+ padding-right: 0;
91
+ background-image: url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiP…dpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9InVybCgjZ3JhZCkiIC8+PC9zdmc+IA==);
92
+ background-size: 100%;
93
+ background-image: -webkit-gradient(linear, 0% 50%, 100% 50%, color-stop(0%, #f7f7f7),color-stop(50%, #f7f7f7),color-stop(51%, rgba(255,255,255,0)),color-stop(100%, rgba(255,255,255,0)));
94
+ background-image: -moz-linear-gradient(right, #f7f7f7 0%,#f7f7f7 50%,rgba(255,255,255,0) 51%,rgba(255,255,255,0) 100%);
95
+ background-image: -webkit-linear-gradient(right, #f7f7f7 0%,#f7f7f7 50%,rgba(255,255,255,0) 51%,rgba(255,255,255,0) 100%);
96
+ background-image: linear-gradient(to left, #f7f7f7 0%,#f7f7f7 50%,rgba(255,255,255,0) 51%,rgba(255,255,255,0) 100%);
97
+ background-repeat: no-repeat;
98
+ background-position: right center;
99
+ }
100
+ .wt-scan-step-title
101
+ {
102
+ font-size: 0.7rem;
103
+ padding-top: 0.5rem;
104
+ white-space: nowrap;
105
+ }
106
+ .wt-scan-icon-loader
107
+ {
108
+ height: 55px;
109
+ background: transparent url("../img/loader.gif");
110
+ background-repeat: no-repeat;
111
+ background-position: center center;
112
+ background-size: 60px;
113
+ }
114
+ /*----------TABS-----------*/
115
+ .wtitan-page-group-header
116
+ {
117
+ margin:0 !important;
118
+ padding-bottom: 10px !important;
119
+ }
120
+ .wtitan-tab-table-container
121
+ {
122
+ padding: 20px 0px;
123
+ border-bottom: 1px solid #ddd;
124
+ background-color: white;
125
+ }
126
+ .wtitan-scanner-vulner-table-container
127
+ {
128
+ overflow: auto;
129
+ height: 500px;
130
+ }
131
+ .wt-scanner-tabs-container
132
+ {
133
+ margin: 0 0 15px 0;
134
+ background: #efefef;
135
+ }
136
+ .nav-tabs a:active, .nav-tabs a:focus
137
+ {
138
+ outline: none !important;
139
+ box-shadow: none !important;
140
+ }
141
+ #wtitan-scanner-tabs li:first-child
142
+ {
143
+ /*margin-left: 20px;*/
144
+ }
145
+ #wtitan-scanner-tabs li:last-child
146
+ {
147
+ margin-right: 20px;
148
+ }
includes/check/assets/img/ajax-loader-big.gif ADDED
Binary file
includes/check/assets/img/close.svg ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
3
+ <svg width="24px" height="30px" viewBox="0 0 24 30" version="1.1" xmlns="http://www.w3.org/2000/svg">
4
+ <path d="M23.949,14.233c-0.379,-0.39 -0.904,-0.606 -1.448,-0.595l-0.683,0l0,-4.092c0.032,-2.535 -0.986,-4.973 -2.812,-6.731c-1.759,-1.828 -4.198,-2.847 -6.734,-2.815c-2.536,-0.032 -4.976,0.987 -6.734,2.815c-1.826,1.758 -2.844,4.196 -2.812,6.731l0,4.089l-0.68,0c-0.544,-0.011 -1.069,0.205 -1.448,0.595c-0.395,0.382 -0.612,0.912 -0.598,1.461l0,12.266c-0.011,0.544 0.204,1.069 0.595,1.448c0.379,0.391 0.904,0.606 1.448,0.595l20.458,0c0.003,0 0.006,0 0.01,0c1.117,0 2.036,-0.919 2.036,-2.037c0,-0.003 0,-0.006 0,-0.009l0,-12.263c0.011,-0.544 -0.206,-1.069 -0.598,-1.448l0,-0.01Zm-6.211,-0.595l-10.919,0l0,-4.092c-0.022,-1.451 0.557,-2.848 1.599,-3.858c1.009,-1.042 2.407,-1.621 3.857,-1.598c1.451,-0.023 2.848,0.556 3.858,1.598c1.042,1.009 1.621,2.407 1.598,3.858l0.007,4.092Z" fill="#d1d1d1"/>
5
+ </svg>
includes/check/assets/img/error.png ADDED
Binary file
includes/check/assets/img/loader.gif ADDED
Binary file
includes/check/assets/img/none.png ADDED
Binary file
includes/check/assets/img/off.png ADDED
Binary file
includes/check/assets/img/ok.png ADDED
Binary file
includes/check/assets/img/warning.png ADDED
Binary file
includes/check/assets/js/check.js ADDED
@@ -0,0 +1,51 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ function wtitan_progress_status(selector ,status) {
2
+ selector.each(function(index, element){
3
+ jQuery(element).removeAttr('class');
4
+ jQuery(element).addClass('wt-scan-step-icon');
5
+ jQuery(element).addClass('wt-scan-step-icon-'+status);
6
+ });
7
+ }
8
+
9
+ jQuery(document).ready(function($) {
10
+ //TABS
11
+ jQuery('#wtitan-scanner-tabs a').on('click', function(e) {
12
+ e.preventDefault();
13
+ jQuery(this).tab('show');
14
+ });
15
+
16
+ jQuery('#wt-checker-check').on('click', function(e) {
17
+ e.preventDefault();
18
+ jQuery(this).attr('disabled', 'disabled');
19
+ jQuery('.wt-scan-icon-loader').show();
20
+ vulnerability_ajax();
21
+ audit_ajax();
22
+ });
23
+
24
+ //HIDE
25
+ jQuery(document).on('click', '.wt-scanner-hide-button', function(e) {
26
+ e.preventDefault();
27
+ var btn = jQuery(this);
28
+ var wtitan_hide_target = jQuery(".wtitan-tab-table-container#wtitan-hided");
29
+
30
+ jQuery.ajax({
31
+ method: 'POST', url: ajaxurl, data: {
32
+ action: 'wtitan_scanner_hide',
33
+ type: btn.data('type'),
34
+ id: btn.data('id'),
35
+ _ajax_nonce: wtscanner.hide_nonce
36
+ },
37
+ beforeSend: function () {
38
+ btn.parent('td').parent('tr').css('opacity','0.5');
39
+ },
40
+ success: function (result) {
41
+ if(result.success) {
42
+ btn.parent('td').parent('tr').animate({opacity: 'hide' , height: 'hide'}, 200);
43
+ wtitan_hide_target.html(result.data.html);
44
+ console.log('Hided - ' + btn.data('id'));
45
+ }
46
+ },
47
+ complete: function () {
48
+ }
49
+ });
50
+ });
51
+ });
includes/check/boot.php ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ // Exit if accessed directly
3
+ if ( ! defined( 'ABSPATH' ) ) {
4
+ exit;
5
+ }
6
+
7
+ //API Client
8
+ require_once WTITAN_PLUGIN_DIR . "/libs/api-client/boot.php";
9
+
10
+ // Base module class
11
+ require_once WTITAN_PLUGIN_DIR."/includes/class.module-base.php";
12
+
13
+ //Main Class
14
+ require_once "classes/class.check.php";
15
+
16
+ // Vulner class
17
+ require_once WTITAN_PLUGIN_DIR."/includes/vulnerabilities/boot.php";
18
+ // Audit class
19
+ require_once WTITAN_PLUGIN_DIR."/includes/audit/boot.php";
includes/check/classes/class.check.php ADDED
@@ -0,0 +1,207 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace WBCR\Titan;
3
+
4
+ // Exit if accessed directly
5
+ if ( ! defined( 'ABSPATH' ) ) {
6
+ exit;
7
+ }
8
+
9
+ use WBCR\Titan\Client\Client;
10
+ use WBCR\Titan\Client\Request\SetNoticeData;
11
+
12
+ /**
13
+ * The file contains a short help info.
14
+ *
15
+ * @author Artem Prihodko <webtemyk@ya.ru>
16
+ * @copyright (c) 2020 Creative Motion
17
+ * @version 1.0
18
+ */
19
+ class Check extends Module_Base {
20
+
21
+ /**
22
+ * Vulnerabilities object
23
+ *
24
+ * @var Vulnerabilities
25
+ */
26
+ public $vulnerabilities;
27
+
28
+ /**
29
+ * Audit object
30
+ *
31
+ * @var Audit
32
+ */
33
+ public $audit;
34
+
35
+ /**
36
+ * Audit object
37
+ *
38
+ * @var Scanner
39
+ */
40
+ public $scanner;
41
+
42
+ /**
43
+ * Vulnerabilities_API constructor.
44
+ *
45
+ */
46
+ public function __construct() {
47
+ parent::__construct();
48
+
49
+ $this->module_dir = WTITAN_PLUGIN_DIR."/includes/check";
50
+ $this->module_url = WTITAN_PLUGIN_URL."/includes/check";
51
+ $this->vulnerabilities = new Vulnerabilities();
52
+ $this->audit = new Audit();
53
+ $this->scanner = new Scanner();
54
+
55
+ if(!has_action('wp_ajax_wtitan_scanner_hide'))
56
+ add_action( 'wp_ajax_wtitan_scanner_hide', array( $this, 'hide_issue' ) );
57
+
58
+ if(!has_filter('wbcr/titan/adminbar_menu_title'))
59
+ add_filter('wbcr/titan/adminbar_menu_title', function($title){
60
+ $count = $this->get_count();
61
+ if($count)
62
+ return $title."<span class='wtitan-count-bubble'>{$count}</span>";
63
+ else
64
+ return $title;
65
+ });
66
+ if(!has_filter('wbcr/titan/admin_menu_title'))
67
+ add_filter('wbcr/titan/admin_menu_title', function($title){
68
+ $count = $this->get_count();
69
+ if($count)
70
+ return $title."<span class='update-plugins'><span class='plugin-count'>{$count}</span></span>";
71
+ else
72
+ return $title;
73
+ });
74
+ }
75
+
76
+ /**
77
+ * Get count of issues
78
+ *
79
+ * @return int
80
+ */
81
+ public function get_count()
82
+ {
83
+ return (int)$this->vulnerabilities->get_count() + (int)$this->audit->get_count();
84
+ }
85
+
86
+ /**
87
+ * Get page content
88
+ *
89
+ * @param string $template
90
+ *
91
+ * @return string
92
+ */
93
+ public function getPageContent( $template = 'main' ) {
94
+ ob_start();
95
+ $this->showPageContent($template );
96
+ $result = ob_get_contents();
97
+ ob_end_clean();
98
+
99
+ return $result;
100
+ }
101
+ /**
102
+ * Show page content
103
+ *
104
+ * @param string $template
105
+ */
106
+ public function showPageContent( $template = 'main' ) {
107
+ $vuln_args = array(
108
+ 'wordpress' => $this->vulnerabilities->wordpress,
109
+ 'plugins' => $this->vulnerabilities->plugins,
110
+ 'themes' => $this->vulnerabilities->themes,
111
+ );
112
+ $content_vulner = $this->vulnerabilities->render_template( 'all-table', $vuln_args);
113
+
114
+ $audit_args = array(
115
+ 'results' => $this->audit->get_audit(),
116
+ );
117
+ $content_audit = $this->audit->render_template( 'all-audit', $audit_args);
118
+
119
+ $hided_args = array(
120
+ 'results' => $this->audit->get_hided(),
121
+ );
122
+ $content_hided = $this->render_template( 'hided', $hided_args);
123
+
124
+ $content_malware = $this->scanner->render_template( 'results', $this->scanner->get_current_results());
125
+
126
+ $args = array(
127
+ 'modules' => array(
128
+ 'hided' => array(
129
+ 'name' => '',
130
+ 'icon' => 'dashicons-hidden',
131
+ 'content' => $content_hided,
132
+ 'style' => '',
133
+ ),
134
+ 'audit' => array(
135
+ 'name' => __('Security audit', 'titan-security'),
136
+ 'icon' => 'dashicons-plugins-checked',
137
+ 'content' => $content_audit,
138
+ 'count' => $this->audit->get_count(),
139
+ 'style' => '',
140
+ 'active' => 'active',
141
+ ),
142
+ 'vulnerability' => array(
143
+ 'name' => __('Vulnerabilities', 'titan-security'),
144
+ 'icon' => 'dashicons-buddicons-replies',
145
+ 'content' => $content_vulner,
146
+ 'count' => $this->vulnerabilities->get_count(),
147
+ 'style' => $this->plugin->is_premium() ? '' : 'wt-tabs-pro',
148
+ ),
149
+ 'malware' => array(
150
+ 'name' => __('Malware', 'titan-security'),
151
+ 'icon' => 'dashicons-code-standards',
152
+ 'content' => $content_malware,
153
+ 'count' => $this->scanner->get_matched_count(),
154
+ 'style' => '',
155
+ ),
156
+ ),
157
+ 'active_modules' => "audit,vulnerability",
158
+ );
159
+ $script_args = array(
160
+ 'wtvulner' => array(
161
+ 'nonce' => wp_create_nonce('get_vulners'),
162
+ ),
163
+ );
164
+ echo $this->vulnerabilities->render_script('vulnerability_ajax.js', $script_args);
165
+
166
+ $script_args = array(
167
+ 'wtaudit' => array(
168
+ 'nonce' => wp_create_nonce('get_audits'),
169
+ ),
170
+ );
171
+ echo $this->audit->render_script('audit_ajax.js', $script_args);
172
+
173
+ echo $this->render_template( $template, $args);
174
+ }
175
+
176
+ /**
177
+ * {@inheritdoc}
178
+ */
179
+ public function hide_issue() {
180
+ if ( ! current_user_can( 'manage_options' ) ) {
181
+ wp_die( - 2 );
182
+ } else {
183
+ check_ajax_referer( 'hide' );
184
+
185
+ if(isset($_POST['id']) && $_POST['id'] !== '' && isset($_POST['type']) && $_POST['type'] !== '') {
186
+ $audit = $this->audit->get_audit();
187
+ $hided = $this->audit->get_hided();
188
+ $type = $_POST['type'];
189
+
190
+ $hided[$type][] = $audit[$_POST['id']];
191
+ unset($audit[$_POST['id']]);
192
+
193
+ update_option( $this->plugin->getPrefix()."audit_results", $audit, 'no');
194
+ update_option( $this->plugin->getPrefix()."audit_results_hided", $hided, 'no');
195
+ $html = $this->render_template( 'hided', array(
196
+ 'results' => $hided,
197
+ ));
198
+ wp_send_json_success(array(
199
+ 'html' => $html
200
+ ));
201
+ }
202
+
203
+ die();
204
+ }
205
+ }
206
+
207
+ }
includes/check/views/check.php ADDED
@@ -0,0 +1,25 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ // !! Обязательно, чтобы редактор знал что такая переменная тут существует
3
+ /* @var array|string|int|float|bool|object $args data
4
+ * @var string $template_name template username
5
+ */
6
+ $scanner_menu = "";
7
+ $scanner_content = "";
8
+ $count = "";
9
+ foreach ( $args['modules'] as $key => $module ) {
10
+ $active = "";
11
+ if(isset($module['active']) && !empty($module['active'])) $active = $module['active'];
12
+ if(isset($module['count'])) $count = " ({$module['count']})";
13
+ $scanner_menu .= "<li class='{$active}{$module['style']}'><a href='#wtitan-{$key}'><span class='dashicons {$module['icon']}'></span> {$module['name']}{$count}</a></li>\n";
14
+ $scanner_content .= "<div class='wtitan-tab-table-container tab-pane {$active}' id='wtitan-{$key}'>{$module['content']}</div>\n";
15
+ }
16
+ ?>
17
+ <div class="wt-scanner-tabs-container" style="margin-top: 0;">
18
+ <ul class="nav nav-tabs" id="wtitan-scanner-tabs">
19
+ <?php echo $scanner_menu;?>
20
+ </ul>
21
+
22
+ <div class="tab-content">
23
+ <?php echo $scanner_content;?>
24
+ </div>
25
+ </div>
includes/check/views/hided.php ADDED
@@ -0,0 +1,70 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /* @var array|string|int|float|bool|object $args data
3
+ * @var string $template_name template
4
+ */
5
+
6
+ if( is_array($args) && isset($args['results']) ) {
7
+ $hided = $args['results'];
8
+ if( !empty($hided) ) {
9
+ ?>
10
+ <div class="wtitan-scanner-vulner-table-container">
11
+ <table class="table table-striped table-hover table-responsive" width="100%">
12
+ <thead>
13
+ <tr>
14
+ <td class="wtitan-vulner-table-slim"></td>
15
+ <td class="wtitan-vulner-table-name">Title</td>
16
+ <td class="wtitan-vulner-table-description">Description</td>
17
+ <td class="wtitan-vulner-table-slim">Time</td>
18
+ <td class="wtitan-vulner-table-slim">Actions</td>
19
+ </tr>
20
+
21
+ </thead>
22
+ <tbody>
23
+ <?php
24
+ foreach($hided as $type => $hides) {
25
+ ?>
26
+ <tr>
27
+ <td colspan="5" class="wtitan-vulner-table-section">
28
+ <?php
29
+ switch( $type ) {
30
+ case 'audit':
31
+ echo __('Security audit', 'titan-security');
32
+ break;
33
+ case 'malware':
34
+ echo __('Malware', 'titan-security');
35
+ break;
36
+ }
37
+ ?>
38
+ </td>
39
+ </tr><?php
40
+ foreach($hides as $key => $result) {
41
+ /* @var \WBCR\Titan\AuditResult $result */
42
+ if( empty($result->description) ) {
43
+ $result->description = '&nbsp';
44
+ }
45
+ ?>
46
+ <tr>
47
+ <td class="wt-severity-<?php echo $result->severity; ?>"></td>
48
+ <td><?php echo $result->title; ?></td>
49
+ <td class="wtitan-vulner-table-description"><?php echo $result->description; ?></td>
50
+ <td><?php echo date_i18n('d.m.Y H:i', $result->timestamp); ?></td>
51
+ <td>
52
+ <?php if( empty($result->fix) ): ?>
53
+ <?php elseif( $result->fix == "js" ): ?>
54
+ <a class="btn btn-primary wt-audit-fix-button">Fix it</a>
55
+ <?php else: ?>
56
+ <a href="<?php echo $result->fix; ?>" class="btn btn-primary">Fix it</a>
57
+ <?php endif; ?>
58
+ </td>
59
+ </tr>
60
+ <?php
61
+ }
62
+ }
63
+ ?>
64
+ </tbody>
65
+ </table>
66
+ </div>
67
+ <?php
68
+ }
69
+ }
70
+ ?>
includes/check/views/main.php ADDED
@@ -0,0 +1,50 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ // !! Обязательно, чтобы редактор знал что такая переменная тут существует
3
+ /* @var array|string|int|float|bool|object $args data
4
+ * @var string $template_name template username
5
+ */
6
+ ?>
7
+ <div class="wbcr-content-section">
8
+ <div class="wt-scanner-container wt-scanner-block-scan">
9
+ <table>
10
+ <tr>
11
+ <td>
12
+ <h4><?php echo __('Security audit','titan-security'); ?></h4>
13
+ <button class="btn btn-primary wt-scanner-scanbutton" id="wt-checker-check"><?php echo __('Check now','titan-security'); ?></button>
14
+ <div class="wt-scan-icon-loader" data-status="" style="display: none"></div>
15
+ </td>
16
+ <td>
17
+ <h4><?php echo __('Description','titan-security'); ?></h4>
18
+ <p><?php echo __('After launching, the it will check your site for vulnerabilities and doing security audit. After you solve the detected security problems , you need to run the check again.','titan-security'); ?>
19
+ </p>
20
+ </td>
21
+ </tr>
22
+ </table>
23
+ <!--
24
+ <div class="wt-scan-progress">
25
+ <?php if(isset($args['modules'])): ?>
26
+ <ul class="wt-scan-progress-ul">
27
+ <?php foreach ( $args['modules'] as $key => $module ) {
28
+ if("hided" == $key) continue;
29
+ $icon = 'none';
30
+ if(!empty($module['content'])) $icon = 'warning';
31
+ else $icon = 'ok';
32
+ ?>
33
+ <li class="wt-scan-progress-li" id="wt-scan-progress-<?php echo $key; ?>">
34
+ <div class="wt-scan-step-icon wt-scan-step-icon-<?php echo $icon; ?>"></div>
35
+ <div class="wt-scan-step-title"><?php echo $module['name']; ?></div>
36
+ </li>
37
+ <?php } ?>
38
+ </ul>
39
+ <?php endif; ?>
40
+ </div>
41
+ -->
42
+ </div>
43
+ <!-- ############################### -->
44
+ <div class="wbcr-factory-page-group-header wtitan-page-group-header">
45
+ <strong>Results</strong>
46
+ <p>Find malware and viruses</p>
47
+ </div>
48
+ <?php echo $this->render_template( 'check', $args);?>
49
+ <!-- ############################### -->
50
+ </div>
includes/class-anti-spam-plugin.php DELETED
@@ -1,143 +0,0 @@
1
- <?php
2
-
3
- namespace WBCR\Antispam;
4
-
5
- // Exit if accessed directly
6
- if ( ! defined( 'ABSPATH' ) ) {
7
- exit;
8
- }
9
-
10
- /**
11
- * Transliteration core class
12
- *
13
- * @author Alexander Kovalev <alex.kovalevv@gmail.com>, Github: https://github.com/alexkovalevv
14
- * @copyright (c) 20.10.2019, Webcraftic
15
- */
16
- class Plugin extends \Wbcr_Factory425_Plugin {
17
-
18
- /**
19
- * Number of comments that will be sent for verification
20
- *
21
- * @since 6.2
22
- */
23
- const COUNT_TO_CHECK = 30;
24
-
25
- /**
26
- * @see self::app()
27
- * @var \Wbcr_Factory425_Plugin
28
- */
29
- private static $app;
30
-
31
- /**
32
- * @since 6.0
33
- * @var array
34
- */
35
- private $plugin_data;
36
-
37
- /**
38
- * Конструктор
39
- *
40
- * Применяет конструктор родительского класса и записывает экземпляр текущего класса в свойство $app.
41
- * Подробнее о свойстве $app см. self::app()
42
- *
43
- * @since 6.0
44
- *
45
- * @param string $plugin_path
46
- * @param array $data
47
- *
48
- * @throws \Exception
49
- */
50
- public function __construct( $plugin_path, $data ) {
51
- parent::__construct( $plugin_path, $data );
52
-
53
- self::$app = $this;
54
- $this->plugin_data = $data;
55
-
56
- $this->global_scripts();
57
-
58
- if ( is_admin() ) {
59
- $this->admin_scripts();
60
- }
61
- }
62
-
63
- /**
64
- * Статический метод для быстрого доступа к интерфейсу плагина.
65
- *
66
- * Позволяет разработчику глобально получить доступ к экземпляру класса плагина в любом месте
67
- * плагина, но при этом разработчик не может вносить изменения в основной класс плагина.
68
- *
69
- * Используется для получения настроек плагина, информации о плагине, для доступа к вспомогательным
70
- * классам.
71
- *
72
- * @since 6.0
73
- * @return \Wbcr_Factory425_Plugin|\WBCR\Antispam\Plugin
74
- */
75
- public static function app() {
76
- return self::$app;
77
- }
78
-
79
- /**
80
- * @author Alexander Kovalev <alex.kovalevv@gmail.com>
81
- * @since 6.0
82
- */
83
- protected function init_activation() {
84
- include_once( WANTISPAM_PLUGIN_DIR . '/admin/class-activation.php' );
85
- self::app()->registerActivation( "\WBCR\Antispam\Activation" );
86
- }
87
-
88
- /**
89
- * @author Alexander Kovalev <alex.kovalevv@gmail.com>
90
- * @since 6.0
91
- * @throws \Exception
92
- */
93
- private function register_pages() {
94
- if ( ! defined( 'WANTISPAMP_PLUGIN_ACTIVE' ) ) {
95
- self::app()->registerPage( '\WBCR\Antispam\Page\Settings', WANTISPAM_PLUGIN_DIR . '/admin/pages/class-pages-settings.php' );
96
- }
97
-
98
- self::app()->registerPage( '\WBCR\Antispam\Page\License', WANTISPAM_PLUGIN_DIR . '/admin/pages/class-pages-license.php' );
99
- self::app()->registerPage( '\WBCR\Antispam\Page\Logs', WANTISPAM_PLUGIN_DIR . '/admin/pages/class-pages-logs.php' );
100
-
101
- if ( ! $this->premium->is_activate() ) {
102
- self::app()->registerPage( '\WBCR\Antispam\Page\About', WANTISPAM_PLUGIN_DIR . '/admin/pages/class-pages-about.php' );
103
- }
104
- }
105
-
106
- /**
107
- * @author Alexander Kovalev <alex.kovalevv@gmail.com>
108
- * @since 6.0
109
- * @throws \Exception
110
- */
111
- private function admin_scripts() {
112
- require_once( WANTISPAM_PLUGIN_DIR . '/admin/boot.php' );
113
-
114
- $this->init_activation();
115
-
116
- add_action( 'plugins_loaded', function () {
117
- $this->register_pages();
118
- }, 30 );
119
-
120
- if ( ! wp_doing_ajax() || ! isset( $_REQUEST['action'] ) ) {
121
- return;
122
- }
123
-
124
- switch ( $_REQUEST['action'] ) {
125
-
126
- case 'wlogger-logs-cleanup':
127
- require_once( WANTISPAM_PLUGIN_DIR . '/admin/ajax/logs.php' );
128
- break;
129
- }
130
- }
131
-
132
- /**
133
- * @author Alexander Kovalev <alex.kovalevv@gmail.com>
134
- * @since 6.0
135
- */
136
- private function global_scripts() {
137
- require_once( WANTISPAM_PLUGIN_DIR . '/includes/logger/class-logger-writter.php' );
138
- require_once( WANTISPAM_PLUGIN_DIR . '/includes/class-protector.php' );
139
-
140
- new \WBCR\Logger\Writter();
141
- }
142
- }
143
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
includes/class-titan-security-plugin.php ADDED
@@ -0,0 +1,315 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace WBCR\Titan;
4
+
5
+ // Exit if accessed directly
6
+ if( !defined('ABSPATH') ) {
7
+ exit;
8
+ }
9
+
10
+ /**
11
+ *
12
+ *
13
+ * @author Alexander Kovalev <alex.kovalevv@gmail.com>, Github: https://github.com/alexkovalevv
14
+ * @copyright (c) 20.10.2019, Webcraftic
15
+ */
16
+ class Plugin extends \Wbcr_Factory426_Plugin {
17
+
18
+ /**
19
+ * Number of comments that will be sent for verification
20
+ *
21
+ * @since 6.2
22
+ */
23
+ const COUNT_TO_CHECK = 30;
24
+
25
+ /**
26
+ * @see self::app()
27
+ * @var \Wbcr_Factory426_Plugin
28
+ */
29
+ private static $app;
30
+
31
+ /**
32
+ * @since 6.0
33
+ * @var array
34
+ */
35
+ private $plugin_data;
36
+
37
+ /**
38
+ * @var \wfWAFStorageFile
39
+ */
40
+ private $firewall_storage;
41
+
42
+ /**
43
+ * @var \WBCR\Titan\Views
44
+ */
45
+ public $view;
46
+
47
+ /**
48
+ * Конструктор
49
+ *
50
+ * Применяет конструктор родительского класса и записывает экземпляр текущего класса в свойство $app.
51
+ * Подробнее о свойстве $app см. self::app()
52
+ *
53
+ * @param string $plugin_path
54
+ * @param array $data
55
+ *
56
+ * @throws \Exception
57
+ * @since 6.0
58
+ *
59
+ */
60
+ public function __construct($plugin_path, $data)
61
+ {
62
+ parent::__construct($plugin_path, $data);
63
+
64
+ self::$app = $this;
65
+ $this->plugin_data = $data;
66
+
67
+ $this->global_scripts();
68
+
69
+ if( is_admin() ) {
70
+ $this->admin_scripts();
71
+ }
72
+ }
73
+
74
+ /**
75
+ * Статический метод для быстрого доступа к интерфейсу плагина.
76
+ *
77
+ * Позволяет разработчику глобально получить доступ к экземпляру класса плагина в любом месте
78
+ * плагина, но при этом разработчик не может вносить изменения в основной класс плагина.
79
+ *
80
+ * Используется для получения настроек плагина, информации о плагине, для доступа к вспомогательным
81
+ * классам.
82
+ *
83
+ * @return \Wbcr_Factory426_Plugin|\WBCR\Titan\Plugin
84
+ * @since 6.0
85
+ */
86
+ public static function app()
87
+ {
88
+ return self::$app;
89
+ }
90
+
91
+ public function view()
92
+ {
93
+ require_once WTITAN_PLUGIN_DIR . '/includes/class-views.php';
94
+
95
+ if( !empty($this->view) ) {
96
+ return $this->view;
97
+ }
98
+ $this->view = Views::get_instance(WTITAN_PLUGIN_DIR);
99
+
100
+ return $this->view;
101
+ }
102
+
103
+
104
+ /**
105
+ * @throws \Exception
106
+ * @since 6.0
107
+ * @author Alexander Kovalev <alex.kovalevv@gmail.com>
108
+ */
109
+
110
+ private function register_pages()
111
+ {
112
+
113
+ self::app()->registerPage('WBCR\Titan\Page\Antispam', WTITAN_PLUGIN_DIR . '/admin/pages/class-pages-antispam.php');
114
+
115
+ self::app()->registerPage('WBCR\Titan\Page\Dashboard', WTITAN_PLUGIN_DIR . '/admin/pages/class-pages-dashboard.php');
116
+
117
+ //self::app()->registerPage('WBCR\Titan\Page\Check', WTITAN_PLUGIN_DIR . '/admin/pages/class-pages-check.php');
118
+ //self::app()->registerPage('WBCR\Titan\Page\Scanner', WTITAN_PLUGIN_DIR . '/admin/pages/class-pages-scanner.php');
119
+ self::app()->registerPage('WBCR\Titan\Page\SiteChecker', WTITAN_PLUGIN_DIR . '/admin/pages/class-pages-sitechecker.php');
120
+
121
+ self::app()->registerPage('WBCR\Titan\Page\Logs', WTITAN_PLUGIN_DIR . '/admin/pages/class-pages-logs.php');
122
+
123
+ self::app()->registerPage('WBCR\Titan\Page\Tweaks', WTITAN_PLUGIN_DIR . '/admin/pages/class-pages-tweaks.php');
124
+
125
+ self::app()->registerPage('WBCR\Titan\Page\License', WTITAN_PLUGIN_DIR . '/admin/pages/class-pages-license.php');
126
+
127
+ self::app()->registerPage('WBCR\Titan\Page\PluginSettings', WTITAN_PLUGIN_DIR . '/admin/pages/class-pages-plugin-settings.php');
128
+
129
+ // Firewall
130
+ if( !defined('WTITANP_PLUGIN_ACTIVE') ) {
131
+ self::app()->registerPage('WBCR\Titan\Page\Firewall', WTITAN_PLUGIN_DIR . '/admin/pages/firewall/class-pages-firewall.php');
132
+ self::app()->registerPage('WBCR\Titan\Page\Firewall_Settings', WTITAN_PLUGIN_DIR . '/admin/pages/firewall/class-pages-firewall-settings.php');
133
+ self::app()->registerPage('WBCR\Titan\Page\Firewall_Blocking', WTITAN_PLUGIN_DIR . '/admin/pages/firewall/class-pages-firewall-blocking.php');
134
+ self::app()->registerPage('WBCR\Titan\Page\Firewall_Attacks_Log', WTITAN_PLUGIN_DIR . '/admin/pages/firewall/class-pages-firewall-attacks-log.php');
135
+ }
136
+
137
+ self::app()->registerPage('WBCR\Titan\Page\Brute_Force', WTITAN_PLUGIN_DIR . '/admin/pages/firewall/class-pages-bruteforce.php');
138
+
139
+ if( !defined('WTITANP_PLUGIN_ACTIVE') ) {
140
+ self::app()->registerPage('WBCR\Titan\Page\Firewall_Login_Attempts', WTITAN_PLUGIN_DIR . '/admin/pages/firewall/class-pages-firewall-login-attempts.php');
141
+ }
142
+ }
143
+
144
+ /**
145
+ * @author Alexander Kovalev <alex.kovalevv@gmail.com>
146
+ * @since 6.0
147
+ */
148
+ protected function init_activation()
149
+ {
150
+ include_once(WTITAN_PLUGIN_DIR . '/admin/class-activation.php');
151
+ self::app()->registerActivation("\WBCR\Titan\Activation");
152
+ }
153
+
154
+ /**
155
+ * @throws \Exception
156
+ * @since 6.0
157
+ * @author Alexander Kovalev <alex.kovalevv@gmail.com>
158
+ */
159
+ private function admin_scripts()
160
+ {
161
+ $this->init_activation();
162
+
163
+ require_once(WTITAN_PLUGIN_DIR . '/admin/boot.php');
164
+ require_once(WTITAN_PLUGIN_DIR . '/admin/class-page-titan-basic.php');
165
+
166
+ if( defined('DOING_AJAX') && DOING_AJAX ) {
167
+ require(WTITAN_PLUGIN_DIR . '/admin/ajax/logs.php');
168
+ }
169
+
170
+ add_action('admin_bar_menu', [$this, 'admin_bar_menu'], 80);
171
+
172
+ add_action('plugins_loaded', function () {
173
+ $this->register_pages();
174
+ }, 30);
175
+ }
176
+
177
+ /**
178
+ * @author Alexander Kovalev <alex.kovalevv@gmail.com>
179
+ * @since 7.0
180
+ */
181
+ private function global_scripts()
182
+ {
183
+
184
+ // Bruteforce
185
+ if( $this->getPopulateOption('bruteforce_enabled') ) {
186
+ require_once(WTITAN_PLUGIN_DIR . '/includes/bruteforce/const.php');
187
+ require_once(WTITAN_PLUGIN_DIR . '/includes/bruteforce/class-helpers.php');
188
+ require_once(WTITAN_PLUGIN_DIR . '/includes/bruteforce/class-limit-login-attempts.php');
189
+ }
190
+
191
+ // Tweaks
192
+ require_once(WTITAN_PLUGIN_DIR . '/includes/tweaks/class-security-tweaks.php');
193
+
194
+ if( $this->getPopulateOption('strong_password') ) {
195
+ require_once(WTITAN_PLUGIN_DIR . '/includes/tweaks/password-requirements/boot.php');
196
+ }
197
+
198
+ $enable_menu = $this->getPopulateOption('extra_menu', false);
199
+ if( $enable_menu ) {
200
+ add_action('admin_enqueue_scripts', [$this, 'admin_bar_enqueue']);
201
+ add_action('wp_enqueue_scripts', [$this, 'admin_bar_enqueue']);
202
+ }
203
+
204
+ // Logger
205
+ require_once(WTITAN_PLUGIN_DIR . '/includes/logger/class-logger-writter.php');
206
+ new \WBCR\Titan\Logger\Writter();
207
+
208
+ // Antispam
209
+ require_once(WTITAN_PLUGIN_DIR . '/includes/antispam/boot.php');
210
+ }
211
+
212
+ /**
213
+ */
214
+ public function admin_bar_enqueue()
215
+ {
216
+ wp_enqueue_style('titan-adminbar-styles', WTITAN_PLUGIN_URL . '/assets/css/admin-bar.css', [], $this->getPluginVersion());
217
+ }
218
+
219
+ /**
220
+ * @return bool
221
+ */
222
+ public function currentUserCan()
223
+ {
224
+ $permission = $this->isNetworkActive() ? 'manage_network' : 'manage_options';
225
+
226
+ return current_user_can($permission);
227
+ }
228
+
229
+ /**
230
+ * Add menu to admin bar
231
+ *
232
+ * @param \WP_Admin_Bar $wp_admin_bar
233
+ *
234
+ */
235
+ public function admin_bar_menu($wp_admin_bar)
236
+ {
237
+ $enable_menu = $this->getPopulateOption('extra_menu', false);
238
+
239
+ if( !$this->currentUserCan() || !$enable_menu ) {
240
+ return;
241
+ }
242
+
243
+ if( $this->isNetworkActive() ) {
244
+ $settings_url = network_admin_url('settings.php');
245
+ } else {
246
+ $settings_url = admin_url('admin.php');
247
+ }
248
+
249
+ $dashboard_url = $settings_url . '?page=dashboard-' . $this->getPluginName();
250
+ $extra_menu_title = apply_filters('wbcr/titan/adminbar_menu_title', __('Titan Security', 'titan-security'));
251
+
252
+ $menu_items = [];
253
+ $menu_items = apply_filters('wbcr/titan/adminbar_menu_items', $menu_items);
254
+
255
+ $menu_items['titan-dashboard'] = [
256
+ 'id' => 'titan-dashboard',
257
+ 'title' => '<span class="dashicons dashicons-dashboard"></span> ' . __('Dashboard', 'titan-security'),
258
+ 'href' => $dashboard_url
259
+ ];
260
+ $menu_items['titan-settings'] = [
261
+ 'id' => 'titan-settings',
262
+ 'title' => '<span class="dashicons dashicons-admin-generic"></span> ' . __('Settings', 'titan-security'),
263
+ 'href' => $settings_url . '?page=plugin_settings-' . $this->getPluginName()
264
+ ];
265
+ $menu_items['titan-rating'] = [
266
+ 'id' => 'titan-rating',
267
+ 'title' => '<span class="dashicons dashicons-heart"></span> ' . __('Do you like our plugin?', 'titan-security'),
268
+ 'href' => 'https://wordpress.org/support/plugin/anti-spam/reviews/'
269
+ ];
270
+ if(!$this->is_premium())
271
+ {
272
+ $menu_items['titan-premium'] = [
273
+ 'id' => 'titan-premium',
274
+ 'title' => '<span class="dashicons dashicons-star-filled"></span> ' . __( 'Upgrade to premium', 'titan-security' ),
275
+ 'href' => $this->get_support()->get_pricing_url( true, 'adminbar_menu' )
276
+ ];
277
+
278
+ }
279
+
280
+ if( empty($menu_items) ) {
281
+ return;
282
+ }
283
+
284
+ $wp_admin_bar->add_menu([
285
+ 'id' => 'titan-menu',
286
+ 'title' => '<span class="wtitan-admin-bar-menu-icon"></span><span class="wtitan-admin-bar-menu-title">' . $extra_menu_title . ' <span class="dashicons dashicons-arrow-down"></span></span>',
287
+ 'href' => $dashboard_url
288
+ ]);
289
+
290
+ foreach((array)$menu_items as $id => $item) {
291
+ $wp_admin_bar->add_menu([
292
+ 'id' => $id,
293
+ 'parent' => 'titan-menu',
294
+ 'title' => $item['title'],
295
+ 'href' => $item['href'],
296
+ 'meta' => [
297
+ 'class' => isset($item['class']) ? $item['class'] : ''
298
+ ]
299
+ ]);
300
+ }
301
+ }
302
+
303
+ /**
304
+ * @return bool
305
+ */
306
+ public function is_premium()
307
+ {
308
+ if( $this->premium->is_active() && $this->premium->is_activate() ) {
309
+ return true;
310
+ } else {
311
+ return false;
312
+ }
313
+ }
314
+ }
315
+
includes/class-views.php ADDED
@@ -0,0 +1,112 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Class that handles templates.
4
+ *
5
+ * @author Webcraftic <wordpress.webraftic@gmail.com>, Alexander Kovalev <alex.kovalevv@gmail.com>
6
+ * @copyright (c) 05.04.2019, Webcraftic
7
+ * @version 1.0
8
+ */
9
+
10
+ namespace WBCR\Titan;
11
+
12
+ class Views {
13
+
14
+ /**
15
+ * The single instance of the class.
16
+ *
17
+ * @since 1.3.0
18
+ * @access protected
19
+ * @var array
20
+ */
21
+ protected static $_instance = [];
22
+
23
+ /**
24
+ * @author Alexander Kovalev <alex.kovalevv@gmail.com>
25
+ * @since 1.3.0
26
+ * @var string
27
+ */
28
+ protected $plugin_dir;
29
+
30
+ /**
31
+ * \WBCR\Titan\Views constructor.
32
+ *
33
+ * @param string $plugin_dir
34
+ * @author Alexander Kovalev <alex.kovalevv@gmail.com>
35
+ *
36
+ */
37
+ public function __construct($plugin_dir)
38
+ {
39
+ $this->plugin_dir = $plugin_dir;
40
+ }
41
+
42
+ /**
43
+ * @param string $plugin_dir
44
+ *
45
+ * @return object|\WBCR\Titan\Views object Main instance.
46
+ * @since 1.3.0
47
+ *
48
+ * @author Alexander Kovalev <alex.kovalevv@gmail.com>
49
+ * @since 1.3.6 - add instace id
50
+ */
51
+ public static function get_instance($plugin_dir)
52
+ {
53
+ $instance_id = md5($plugin_dir);
54
+
55
+ if( !isset(self::$_instance[$instance_id]) ) {
56
+ self::$_instance[$instance_id] = new self($plugin_dir);
57
+ }
58
+
59
+ return self::$_instance[$instance_id];
60
+ }
61
+
62
+ /**
63
+ * Get a template contents.
64
+ *
65
+ * @param string $template The template name.
66
+ * @param mixed $data Some data to pass to the template.
67
+ * @param \Wbcr_FactoryClearfy218_PageBase $page
68
+ *
69
+ * @return bool|string The page contents. False if the template doesn't exist.
70
+ * @author Alexander Kovalev <alex.kovalevv@gmail.com>
71
+ * @since 1.3.0
72
+ *
73
+ */
74
+ public function get_template($template, $data = [], \Wbcr_FactoryClearfy218_PageBase $page = null)
75
+ {
76
+ $template = str_replace('_', '-', $template);
77
+
78
+ if( false !== strpos($template, '/') ) {
79
+ $path_part_array = explode('/', $template);
80
+ $path = $this->plugin_dir . '/views/' . $path_part_array[0] . '/' . $path_part_array[1] . '.php';
81
+ } else {
82
+ $path = $this->plugin_dir . '/views/' . $template . '.php';
83
+ }
84
+
85
+ if( !file_exists($path) ) {
86
+ return false;
87
+ }
88
+
89
+ ob_start();
90
+ include $path;
91
+ $contents = ob_get_clean();
92
+
93
+ return trim((string)$contents);
94
+ }
95
+
96
+ /**
97
+ * Print a template.
98
+ *
99
+ * @param string $template The template name.
100
+ * @param mixed $data Some data to pass to the template.
101
+ * @param \Wbcr_FactoryClearfy218_PageBase $page
102
+ * @since 1.3.0
103
+ *
104
+ * @author Alexander Kovalev <alex.kovalevv@gmail.com>
105
+ * @access public
106
+ *
107
+ */
108
+ public function print_template($template, $data = [], \Wbcr_FactoryClearfy218_PageBase $page = null)
109
+ {
110
+ echo $this->get_template($template, $data, $page);
111
+ }
112
+ }
includes/class.module-base.php ADDED
@@ -0,0 +1,100 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace WBCR\Titan;
4
+
5
+ use WBCR\Titan\Plugin;
6
+
7
+ // Exit if accessed directly
8
+ if ( ! defined( 'ABSPATH' ) ) {
9
+ exit;
10
+ }
11
+
12
+ /**
13
+ * Base class for Titan module.
14
+ *
15
+ * @author Artem Prihodko <webtemyk@yandex.ru>
16
+ * @version 1.0
17
+ */
18
+ abstract class Module_Base {
19
+
20
+ /**
21
+ * Plugin object
22
+ *
23
+ * @since 7.0
24
+ * @var \Wbcr_Factory426_Plugin
25
+ */
26
+ public $plugin;
27
+
28
+ /**
29
+ * Current license key
30
+ *
31
+ * @since 7.0
32
+ * @var string
33
+ */
34
+ protected $license_key = "";
35
+
36
+ protected $module_dir;
37
+ protected $module_url;
38
+
39
+ /**
40
+ * Titan module constructor.
41
+ *
42
+ */
43
+ public function __construct( ) {
44
+ $this->plugin = Plugin::app();
45
+ if(Plugin::app()->premium->is_activate())
46
+ $this->license_key = Plugin::app()->premium->get_license()->get_key();
47
+ }
48
+
49
+ /**
50
+ * Method renders layout template
51
+ *
52
+ * @param string $template_name Template name without ".php"
53
+ * @param array|string|int|float|bool|object $args Template arguments
54
+ *
55
+ * @return false|string
56
+ */
57
+ protected function render_template( $template_name, $args = array()) {
58
+ $path = $this->module_dir."/views/$template_name.php";
59
+ if( file_exists($path) ) {
60
+ ob_start();
61
+ extract($args);
62
+ include $path;
63
+ unset($path);
64
+ return ob_get_clean();
65
+ } else {
66
+ return __('This template does not exist!', 'titan-security');
67
+ }
68
+ }
69
+
70
+ /**
71
+ * Method renders Java Script
72
+ *
73
+ * @param string $script_name Template name with ".js" "/module/assets/js/$script_name"
74
+ *
75
+ * @param array[] $args Arguments are converted to JS variables similar to the wp_localize_script function
76
+ *
77
+ * @return false|string
78
+ */
79
+ protected function render_script( $script_name, $args = array()) {
80
+ $path = $this->module_dir."/assets/js/$script_name";
81
+ $url = $this->module_url."/assets/js/$script_name";
82
+ if( file_exists($path) ) {
83
+ ob_start();
84
+ echo "<script>";
85
+ if(is_array( $args)) {
86
+ foreach ( $args as $key => $value ) {
87
+ echo "var $key = " . json_encode( $value ) . ";\n";
88
+ }
89
+ }
90
+ echo "</script>";
91
+ echo "<script type='application/javascript' src='{$url}'></script>";
92
+ unset($path);
93
+ return ob_get_clean();
94
+ } else {
95
+ return __('This script file does not exist!', 'titan-security');
96
+ }
97
+ }
98
+
99
+ abstract public function showPageContent();
100
+ }
includes/functions.php CHANGED
@@ -1,172 +1,463 @@
1
  <?php
2
  /**
3
- * Helper functions
4
  *
5
- * @author Alex Kovalev <alex.kovalevv@gmail.com>, Github: https://github.com/alexkovalevv
6
- * @copyright (c) 12.12.2019, Webcraftic
7
  * @version 1.0
 
8
  */
9
 
 
 
 
 
 
 
 
10
  /**
11
- * Gets honeypot fields.
12
- *
13
- * @author Alexander Kovalev <alex.kovalevv@gmail.com>
14
- * @since 6.5.3
15
  */
16
- function wantispam_get_honeypot_fields() {
17
- $rn = "\r\n"; // .chr(13).chr(10)
18
- $html = '';
19
-
20
- $html .= '<div class="wantispam-group wantispam-group-q" style="clear: both;">
21
- <label>Current ye@r <span class="required">*</span></label>
22
- <input type="hidden" name="wantispam_a" class="wantispam-control wantispam-control-a" value="' . date( 'Y' ) . '" />
23
- <input type="text" name="wantispam_q" class="wantispam-control wantispam-control-q" value="' . \WBCR\Antispam\Plugin::app()->getPluginVersion() . '" autocomplete="off" />
24
- </div>' . $rn; // question (hidden with js)
25
- $html .= '<div class="wantispam-group wantispam-group-e" style="display: none;">
26
- <label>Leave this field empty</label>
27
- <input type="text" name="wantispam_e_email_url_website" class="wantispam-control wantispam-control-e" value="" autocomplete="off" />
28
- </div>' . $rn; // empty field (hidden with css); trap for spammers because many bots will try to put email or url here
29
-
30
- return $html;
31
  }
32
 
 
33
  /**
34
- * Gets required fields into the comment form on the page.
35
- *
36
- * @param string $html
37
- *
38
- * @return string
39
- * @author Alexander Kovalev <alex.kovalevv@gmail.com>
40
- * @since 6.5.3
41
- *
42
  */
43
- function wantispam_get_required_fields( $render_honeypot_fields = true ) {
44
- $html = '<!-- Anti-spam plugin wordpress.org/plugins/anti-spam/ -->';
45
- $html .= '<div class="wantispam-required-fields">';
46
- $html .= '<input type="hidden" name="wantispam_t" class="wantispam-control wantispam-control-t" value="' . time() . '" />'; // Start time of form filling
47
- if ( $render_honeypot_fields ) {
48
- $html .= wantispam_get_honeypot_fields();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
49
  }
50
- $html .= '</div>';
51
- $html .= '<!--\End Anti-spam plugin -->';
52
 
53
- return $html;
 
 
 
 
 
54
  }
55
 
56
  /**
57
- * Controls the display of a privacy related notice underneath the comment form.
58
- *
59
- * @author Alexander Kovalev <alex.kovalevv@gmail.com>
60
- * @since 6.5.3
61
  */
62
- function wantispam_display_comment_form_privacy_notice( $echo = false ) {
63
- if ( ! \WBCR\Antispam\Plugin::app()->getPopulateOption( 'comment_form_privacy_notice' ) ) {
64
- return '';
 
 
 
 
 
65
  }
66
 
67
- $output = '<p class="wantispam-comment-form-privacy-notice" style="margin-top:10px;">' . sprintf( __( 'This site uses Antispam to reduce spam. <a href="%s" target="_blank" rel="nofollow noopener">Learn how your comment data is processed</a>.', 'anti-spam' ), 'https://anti-spam.space/antispam-privacy/' ) . '</p>';
 
 
 
 
 
 
68
 
69
- if ( ! $echo ) {
70
- return $output;
71
  }
72
 
73
- echo $output;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
74
  }
75
 
76
  /**
77
- * Return premium widget markup
78
- *
79
- * @return string
80
- * @since 6.5.3
81
- * @author Alexander Kovalev <alex.kovalevv@gmail.com>
82
  */
83
- function wantispam_get_sidebar_premium_widget() {
84
- ob_start();
85
- ?>
86
- <div class="wbcr-factory-sidebar-widget">
87
- <p>
88
- <a href="https://anti-spam.space/pricing/" target="_blank" rel="noopener nofollow">
89
- <img style="width: 100%;"
90
- src="https://api.cm-wp.com/wp-content/uploads/2019/12/baner_antispam_vertical.jpg" alt="">
91
- </a>
92
- </p>
93
- </div>
94
- <?php
95
- return ob_get_clean();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
96
  }
97
 
98
  /**
99
- * Should show a page about the plugin or not.
100
- *
101
- * @return bool
102
- * @since 6.5.3
103
  */
104
- function wantispam_is_need_show_about_page() {
105
- if ( \WBCR\Antispam\Plugin::app()->isNetworkActive() ) {
106
- $need_show_about = (int) get_site_option( \WBCR\Antispam\Plugin::app()->getOptionName( 'what_is_new_64' ) );
107
- } else {
108
- $need_show_about = (int) get_option( \WBCR\Antispam\Plugin::app()->getOptionName( 'what_is_new_64' ) );
 
 
 
 
109
  }
110
 
111
- $is_ajax = wantispam_doing_ajax();
112
- $is_cron = wantispam_doing_cron();
113
- $is_rest = wantispam_doing_rest_api();
114
 
115
- if ( $need_show_about && ! $is_ajax && ! $is_cron && ! $is_rest ) {
116
- return true;
117
- }
 
 
 
 
 
 
 
118
 
119
- return false;
 
 
 
 
 
 
 
 
120
  }
121
 
122
  /**
123
- * Checks if the current request is a WP REST API request.
124
  *
125
- * Case #1: After WP_REST_Request initialisation
126
- * Case #2: Support "plain" permalink settings
127
- * Case #3: URL Path begins with wp-json/ (your REST prefix)
128
- * Also supports WP installations in subfolders
129
  *
130
- * @author matzeeable https://wordpress.stackexchange.com/questions/221202/does-something-like-is-rest-exist
131
- * @since 2.1.0
132
- * @return boolean
133
  */
134
- function wantispam_doing_rest_api() {
135
- $prefix = rest_get_url_prefix();
136
- $rest_route = \WBCR\Antispam\Plugin::app()->request->get( 'rest_route', null );
137
- if ( defined( 'REST_REQUEST' ) && REST_REQUEST // (#1)
138
- || ! is_null( $rest_route ) // (#2)
139
- && strpos( trim( $rest_route, '\\/' ), $prefix, 0 ) === 0 ) {
140
- return true;
141
- }
142
 
143
- // (#3)
144
- $rest_url = wp_parse_url( site_url( $prefix ) );
145
- $current_url = wp_parse_url( add_query_arg( [] ) );
 
146
 
147
- return strpos( $current_url['path'], $rest_url['path'], 0 ) === 0;
 
 
 
 
 
 
 
 
 
148
  }
149
 
150
  /**
 
 
 
 
 
 
 
151
  * @return bool
152
- * @since 6.5.3
153
  */
154
- function wantispam_doing_ajax() {
155
- if ( function_exists( 'wp_doing_ajax' ) ) {
156
- return wp_doing_ajax();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
157
  }
158
 
159
- return defined( 'DOING_AJAX' ) && DOING_AJAX;
 
 
 
 
 
 
 
 
160
  }
161
 
162
  /**
163
- * @return bool
164
- * @since 6.5.3
165
  */
166
- function wantispam_doing_cron() {
167
- if ( function_exists( 'wp_doing_cron' ) ) {
168
- return wp_doing_cron();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
169
  }
170
 
171
- return defined( 'DOING_CRON' ) && DOING_CRON;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
172
  }
1
  <?php
2
  /**
 
3
  *
4
+ * @author Webcraftic <wordpress.webraftic@gmail.com>
 
5
  * @version 1.0
6
+ * @copyright (c) 10.02.2020, Webcraftic
7
  */
8
 
9
+ use WBCR\Titan\Client\Client;
10
+ use WBCR\Titan\Client\Entity\CmsCheckItem;
11
+ use WBCR\Titan\MalwareScanner\HashListPool;
12
+ use WBCR\Titan\MalwareScanner\Scanner;
13
+ use WBCR\Titan\Plugin;
14
+
15
+ add_filter('cron_schedules', 'titan_add_minute_schedule');
16
  /**
17
+ * @param array $schedules
 
 
 
18
  */
19
+ function titan_add_minute_schedule($schedules)
20
+ {
21
+ $schedules['minute'] = [
22
+ 'interval' => 30,
23
+ 'display' => __('Once 30 sec', 'titan-security'),
24
+ ];
25
+
26
+ return $schedules;
 
 
 
 
 
 
 
27
  }
28
 
29
+ add_action('titan_scheduled_scanner', 'titan_scheduled_scanner');
30
  /**
31
+ * @throws Exception
 
 
 
 
 
 
 
32
  */
33
+ function titan_scheduled_scanner()
34
+ {
35
+ require_once WTITAN_PLUGIN_DIR . '/libs/api-client/boot.php';
36
+ require_once WTITAN_PLUGIN_DIR . '/includes/scanner/classes/scanner/boot.php';
37
+
38
+ /** @var Scanner $scanner */
39
+ $scanner = get_option(Plugin::app()->getPrefix() . 'scanner', null);
40
+ if( is_null($scanner) || $scanner === false ) {
41
+ \WBCR\Titan\Logger\Writter::error('Scanner does not exists');
42
+ error_log('Scanner does not exists');
43
+ titan_remove_scheduler_scanner();
44
+
45
+ return;
46
+ }
47
+
48
+ set_time_limit(0);
49
+
50
+ $speed = Plugin::app()->getPopulateOption('scanner_speed', 'slow');
51
+ $files_count = @Scanner::SPEED_FILES[$speed];
52
+ if( is_null($files_count) ) {
53
+ $files_count = Scanner::SPEED_FILES[Scanner::SPEED_MEDIUM];
54
+ }
55
+
56
+ $matched = Plugin::app()->getOption('scanner_malware_matched', []);
57
+
58
+ foreach($scanner->scan($files_count) as $match) {
59
+ /** @var \WBCR\Titan\MalwareScanner\Match $match */
60
+ if( $match->getSignature()->getSever() === \WBCR\Titan\MalwareScanner\Signature::SEVER_CRITICAL ) {
61
+ array_unshift($matched, $match);
62
+ } else {
63
+ array_push($matched, $match);
64
+ }
65
  }
 
 
66
 
67
+ Plugin::app()->updateOption('scanner_malware_matched', $matched);
68
+ Plugin::app()->updateOption('scanner', $scanner);
69
+
70
+ if( $scanner->get_files_count() < 1 ) {
71
+ titan_remove_scheduler_scanner();
72
+ }
73
  }
74
 
75
  /**
76
+ * @return CmsCheckItem[]
 
 
 
77
  */
78
+ function titan_check_cms()
79
+ {
80
+ global $wp_version;
81
+
82
+ if( Plugin::app()->is_premium() ) {
83
+ $license_key = Plugin::app()->premium->get_license()->get_key();
84
+ } else {
85
+ $license_key = null;
86
  }
87
 
88
+ $client = new Client($license_key);
89
+
90
+ if( Plugin::app()->is_premium() ) {
91
+ $result = $client->check_cms_premium($wp_version, collect_wp_hash_sum());
92
+ } else {
93
+ $result = $client->check_cms_free($wp_version, collect_wp_hash_sum());
94
+ }
95
 
96
+ if( is_null($result) ) {
97
+ return [];
98
  }
99
 
100
+ WBCR\Titan\Logger\Writter::info(sprintf("Founded %d corrupted files", count($result->items)));
101
+
102
+ foreach($result->items as $check_item) {
103
+ WBCR\Titan\Logger\Writter::debug(sprintf("File `%s` (action %s)", $check_item->path, $check_item->action));
104
+ $path = dirname(WP_CONTENT_DIR) . '/' . $check_item->path;
105
+ switch( $check_item->action ) {
106
+ case CmsCheckItem::ACTION_REMOVE:
107
+ if( file_exists($path) && is_writable($path) ) {
108
+ unlink($path);
109
+ }
110
+ break;
111
+
112
+ case CmsCheckItem::ACTION_REPAIR:
113
+ if( file_exists($path) && is_writeable($path) ) {
114
+ $data = file_get_contents($check_item->url);
115
+ file_put_contents($path, $data);
116
+ }
117
+ break;
118
+ }
119
+ }
120
+
121
+ return $result->items;
122
  }
123
 
124
  /**
125
+ * Creating cron task
 
 
 
 
126
  */
127
+ function titan_create_scheduler_scanner()
128
+ {
129
+ // todo: реализовать уровень проверки сайта
130
+
131
+ if( Plugin::app()->is_premium() ) {
132
+ $license_key = Plugin::app()->premium->get_license()->get_key();
133
+ } else {
134
+ $license_key = null;
135
+ }
136
+
137
+ $client = new Client($license_key);
138
+
139
+ if( Plugin::app()->is_premium() ) {
140
+ $signatures = $client->get_signatures();
141
+ } else {
142
+ $signatures = $client->get_free_signatures();
143
+ }
144
+
145
+ /** @var array[]|WBCR\Titan\Client\Entity\Signature[] $signatures */
146
+
147
+ foreach($signatures as $key => $signature) {
148
+ $signatures[$key] = $signature->to_array();
149
+ }
150
+ $signature_pool = WBCR\Titan\MalwareScanner\SignaturePool::fromArray($signatures);
151
+
152
+ $file_hash = get_option(Plugin::app()->getPrefix() . 'files_hash');
153
+ if( !$file_hash ) {
154
+ $file_hash_pool = HashListPool::fromArray($file_hash);
155
+ } else {
156
+ $file_hash_pool = null;
157
+ }
158
+
159
+ $scanner = new WBCR\Titan\MalwareScanner\Scanner(ABSPATH, $signature_pool, $file_hash_pool, [
160
+ 'wp-admin',
161
+ 'wp-includes',
162
+ 'wp-activate.php',
163
+ 'wp-blog-header.php',
164
+ 'wp-comments-post.php',
165
+ 'wp-config-sample.php',
166
+ 'wp-cron.php',
167
+ 'wp-links-opml.php',
168
+ 'wp-load.php',
169
+ 'wp-login.php',
170
+ 'wp-mail.php',
171
+ 'wp-settings.php',
172
+ 'wp-signup.php',
173
+ 'wp-trackback.php',
174
+ 'xmlrpc.php',
175
+ 'debug.log',
176
+ 'node_modules',
177
+ 'vendor',
178
+ 'wp-plugin-titan-security',
179
+ 'anti-spam',
180
+ ]);
181
+
182
+ Plugin::app()->updateOption('scanner', $scanner);
183
+ Plugin::app()->updateOption('scanner_malware_matched', []);
184
+ Plugin::app()->updateOption('scanner_files_count', $scanner->get_files_count());
185
+ Plugin::app()->updateOption('scanner_status', 'started');
186
+ wp_schedule_event(time(), 'minute', 'titan_scheduled_scanner');
187
  }
188
 
189
  /**
190
+ * Deleting a cron task
 
 
 
191
  */
192
+ function titan_remove_scheduler_scanner()
193
+ {
194
+ $scanner = get_option(Plugin::app()->getPrefix() . 'scanner');
195
+ if( $scanner ) {
196
+ $file_hash = [];
197
+ foreach($scanner->getFileList() as $file) {
198
+ $file_hash[$file->getPath()] = $file->getFileHash();
199
+ }
200
+ Plugin::app()->updateOption('files_hash', $file_hash);
201
  }
202
 
203
+ wp_unschedule_hook('titan_scheduled_scanner');
204
+ Plugin::app()->updateOption('scanner_status', 'stopped');
 
205
 
206
+ try {
207
+ $matched = Plugin::app()->getOption('scanner_malware_matched');
208
+
209
+ if( count($matched) > 0 ) {
210
+ if( Plugin::app()->is_premium() ) {
211
+ $license_key = Plugin::app()->premium->get_license()->get_key();
212
+ } else {
213
+ $license_key = null;
214
+ }
215
+ $client = new Client($license_key);
216
 
217
+ $client->send_notification('email', 'virusFound', [
218
+ 'subject' => 'VIRUS',
219
+ 'url' => get_site_url(),
220
+ 'files' => $matched
221
+ ]);
222
+ }
223
+ } catch( Exception $e ) {
224
+
225
+ }
226
  }
227
 
228
  /**
229
+ * Collecting hash sums of WP files
230
  *
231
+ * @param string $path
 
 
 
232
  *
233
+ * @return array
 
 
234
  */
235
+ function collect_wp_hash_sum($path = ABSPATH)
236
+ {
237
+ $hash = [];
 
 
 
 
 
238
 
239
+ foreach(scandir($path) as $item) {
240
+ if( $item == '.' || $item == '..' || $item == 'plugins' || $item == 'themes' ) {
241
+ continue;
242
+ }
243
 
244
+ $newPath = $path . $item;
245
+ $relativePath = str_replace(ABSPATH, '', $newPath);
246
+ if( is_dir($newPath) ) {
247
+ $hash = array_merge($hash, collect_wp_hash_sum($newPath . '/'));
248
+ } else {
249
+ $hash[$relativePath] = md5_file($newPath);
250
+ }
251
+ }
252
+
253
+ return $hash;
254
  }
255
 
256
  /**
257
+ * Displays a notification inside the Antispam interface, on all pages of the plugin.
258
+ * This is necessary to remind the user to update the configuration of the plugin components,
259
+ * Otherwise, the newly activated components will not be involved in the work of the plugin.
260
+ *
261
+ * @param Wbcr_Factory426_Plugin $plugin
262
+ * @param Wbcr_FactoryPages426_ImpressiveThemplate $obj
263
+ *
264
  * @return bool
 
265
  */
266
+ add_action('wbcr/factory/pages/impressive/print_all_notices', function ($plugin, $obj) {
267
+ if( $plugin->getPluginName() != \WBCR\Titan\Plugin::app()->getPluginName() ) {
268
+ return;
269
+ }
270
+
271
+ if( !empty($_GET['page']) && "sitechecker-" . \WBCR\Titan\Plugin::app()->getPluginName() === $_GET['page'] ) {
272
+ require_once WTITAN_PLUGIN_DIR . '/includes/audit/classes/class.cert.php';
273
+ $cert = \WBCR\Titan\Cert\Cert::get_instance();
274
+ $output = false;
275
+ $message = '';
276
+ $type = 'warning';
277
+ //$plugin_name = WBCR\Titan\Plugin::app()->getPluginTitle();
278
+
279
+ if( $cert->is_available() ) {
280
+ if( !$cert->is_lets_encrypt() ) {
281
+ $remaining = $cert->get_expiration_timestamp() - time();
282
+ if( $remaining <= 86400 * 90 ) { // 3 month (90 days)
283
+ $message = 'The SSL certificate expires in less than three months';
284
+ $output = true;
285
+ } else if( $remaining <= 86400 * 3 ) { // 3 days
286
+ $type = 'notice-error';
287
+ $message = 'The SSL certificate expires in less than three days';
288
+ $output = true;
289
+ }
290
+ }
291
+ } else {
292
+ $output = true;
293
+ $type = 'error';
294
+ $message = $cert->get_error_message();
295
+ }
296
+
297
+ if( $output ) {
298
+ switch( $type ) {
299
+ case 'error':
300
+ $obj->printErrorNotice($message);
301
+ break;
302
+ case 'warning':
303
+ $obj->printWarningNotice($message);
304
+ break;
305
+ }
306
+ }
307
+ }
308
+ }, 10, 2);
309
+
310
+ add_action('init', 'titan_init_https_redirect');
311
+ function titan_init_https_redirect()
312
+ {
313
+ $strict_https = Plugin::app()->getPopulateOption('strict_https', false);
314
+ if( !is_ssl() && $strict_https ) {
315
+ wp_redirect(home_url(add_query_arg($_GET, $_SERVER['REQUEST_URI']), 'https'));
316
+ die;
317
+ }
318
+ }
319
+
320
+ add_action(Plugin::app()->getPluginName() . "/factory/premium/license_activate", 'titan_set_scanner_speed_active');
321
+ function titan_set_scanner_speed_active()
322
+ {
323
+ $scanner_speed = Plugin::app()->getPopulateOption('scanner_speed', 'free');
324
+ if( $scanner_speed == 'free' ) {
325
+ Plugin::app()->updatePopulateOption('scanner_speed', 'slow');
326
+ }
327
+
328
+ $scanner_schedule = Plugin::app()->getPopulateOption('scanner_schedule', 'disabled');
329
+ if( $scanner_schedule == 'disabled' ) {
330
+ Plugin::app()->updatePopulateOption('scanner_schedule', 'disabled');
331
+ }
332
+
333
+ $scanner_type = Plugin::app()->getPopulateOption('scanner_type', 'basic');
334
+ if( $scanner_type == 'basic' ) {
335
+ Plugin::app()->updatePopulateOption('scanner_type', 'advanced');
336
+ }
337
+ }
338
+
339
+ add_action(Plugin::app()->getPluginName() . "/factory/premium/license_deactivate", 'titan_set_scanner_speed_deactive');
340
+ function titan_set_scanner_speed_deactive()
341
+ {
342
+ $scanner_speed = Plugin::app()->getPopulateOption('scanner_speed', 'free');
343
+ if( $scanner_speed !== 'free' ) {
344
+ Plugin::app()->updatePopulateOption('scanner_speed', 'free');
345
  }
346
 
347
+ $scanner_schedule = Plugin::app()->getPopulateOption('scanner_schedule', 'disabled');
348
+ if( $scanner_schedule !== 'disabled' ) {
349
+ Plugin::app()->updatePopulateOption('scanner_schedule', 'disabled');
350
+ }
351
+
352
+ $scanner_type = Plugin::app()->getPopulateOption('scanner_type', 'basic');
353
+ if( $scanner_type !== 'basic' ) {
354
+ Plugin::app()->updatePopulateOption('scanner_type', 'basic');
355
+ }
356
  }
357
 
358
  /**
359
+ * @return int|float [Memory limit in MB]
 
360
  */
361
+ function get_memory_limit()
362
+ {
363
+ $mem = ini_get('memory_limit');
364
+ $last = $mem[strlen($mem) - 1];
365
+ $mem = (int)$mem;
366
+ do {
367
+ switch( $last ) {
368
+ case 'g':
369
+ case 'G':
370
+ $mem = $mem * 1024;
371
+ $last = 'm';
372
+ break;
373
+
374
+ case 'm':
375
+ case 'M':
376
+ break 2;
377
+
378
+ default:
379
+ $mem = ((int)$mem) / 1024 / 1024; // bytes to mbytes
380
+ break 2;
381
+ }
382
+ } while( true );
383
+
384
+ return $mem;
385
+ }
386
+
387
+ function get_recommended_scanner_speed()
388
+ {
389
+ $mem = get_memory_limit();
390
+ if( $mem > 100 ) {
391
+ $recommendation = \WBCR\Titan\MalwareScanner\Scanner::SPEED_FAST;
392
+ } elseif( $mem > 60 ) {
393
+ $recommendation = \WBCR\Titan\MalwareScanner\Scanner::SPEED_MEDIUM;
394
+ } else {
395
+ $recommendation = \WBCR\Titan\MalwareScanner\Scanner::SPEED_SLOW;
396
  }
397
 
398
+ return $recommendation;
399
+ }
400
+
401
+ /**
402
+ * @param $time
403
+ *
404
+ * @return int
405
+ */
406
+ function correct_timezone($time)
407
+ {
408
+ $localOffset = (new DateTime)->getOffset();
409
+
410
+ return $time + $localOffset;
411
+ }
412
+
413
+ add_action('plugins_loaded', 'titan_init_check_schedule');
414
+ function titan_init_check_schedule()
415
+ {
416
+ $format_date = 'Y/m/d H:i';
417
+ $format_time = 'H:i';
418
+
419
+ $is_schedule = false;
420
+
421
+ $lasttime = Plugin::app()->getPopulateOption('scanner_schedule_last_time', date_i18n($format_date));
422
+ $schedule = Plugin::app()->getPopulateOption('scanner_schedule', 'disabled');
423
+ $last = date_parse_from_format($format_time, $lasttime);
424
+
425
+ switch( $schedule ) {
426
+ case 'daily':
427
+ $daily = Plugin::app()->getPopulateOption('scanner_schedule_daily', '2000/01/01 23:00');
428
+ $daily = date_parse_from_format($format_time, $daily);
429
+ $daily = $daily['hour'] * 60 + $daily['minute'];
430
+ $last = $last['hour'] * 60 + $last['minute'];
431
+ if( $last <= $daily ) {
432
+ titan_create_scheduler_scanner();
433
+ $is_schedule = true;
434
+ }
435
+ break;
436
+ case 'weekly':
437
+ $week = Plugin::app()->getPopulateOption('scanner_schedule_weekly_day', '7');
438
+ $time = Plugin::app()->getPopulateOption('scanner_schedule_weekly_time', '2000/01/01 23:00');
439
+ $time = date_parse_from_format($format_time, $time);
440
+ $time = $time['hour'] * 60 + $time['minute'];
441
+ $last = $last['hour'] * 60 + $last['minute'];
442
+ $this_week = date('N');
443
+ if( $this_week == $week && $last <= $time ) {
444
+ titan_create_scheduler_scanner();
445
+ $is_schedule = true;
446
+ }
447
+ break;
448
+ case 'custom':
449
+ $time = Plugin::app()->getPopulateOption('scanner_schedule_custom', '2000/01/01 23:00');
450
+ $time = strtotime($time);
451
+ $last = strtotime($lasttime);
452
+ if( $last <= $time ) {
453
+ titan_create_scheduler_scanner();
454
+ $is_schedule = true;
455
+ }
456
+ break;
457
+ case 'disabled':
458
+ break;
459
+ }
460
+ if( $is_schedule ) {
461
+ Plugin::app()->updatePopulateOption('scanner_schedule_last_time', date_i18n($format_date));
462
+ }
463
  }
includes/helpers.php ADDED
@@ -0,0 +1,237 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Helpers functions
4
+ * @author Webcraftic <wordpress.webraftic@gmail.com>
5
+ * @copyright (c) 2017 Webraftic Ltd
6
+ * @version 1.0
7
+ */
8
+
9
+ namespace WBCR\Titan\Plugin;
10
+
11
+ // Exit if accessed directly
12
+ if( !defined('ABSPATH') ) {
13
+ exit;
14
+ }
15
+
16
+ class Helper {
17
+
18
+ /**
19
+ * Allows you to get the base path to the plugin in the directory wp-content/plugins/
20
+ *
21
+ * @param $slug - slug for example "clearfy", "hide-login-page"
22
+ * @return int|null|string - "clearfy/clearfy.php"
23
+ */
24
+ public static function getPluginBasePathBySlug($slug)
25
+ {
26
+ // Check if the function get_plugins() is registered. It is necessary for the front-end
27
+ // usually get_plugins() only works in the admin panel.
28
+ if( !function_exists('get_plugins') ) {
29
+ require_once ABSPATH . 'wp-admin/includes/plugin.php';
30
+ }
31
+
32
+ $plugins = get_plugins();
33
+
34
+ foreach($plugins as $base_path => $plugin) {
35
+ if( strpos($base_path, rtrim(trim($slug))) !== false ) {
36
+ return $base_path;
37
+ }
38
+ }
39
+
40
+ return null;
41
+ }
42
+
43
+ /**
44
+ * Static method will check whether the plugin is activated or not. You can check whether the plugin exists
45
+ * by using its slug or the base path.
46
+ *
47
+ * @param string $slug - slug for example "clearfy", "hide-login-page" or base path "clearfy/clearfy.php"
48
+ * @return bool
49
+ */
50
+ public static function isPluginActivated($slug)
51
+ {
52
+ if( strpos(rtrim(trim($slug)), '/') === false ) {
53
+ $plugin_base_path = self::getPluginBasePathBySlug($slug);
54
+
55
+ if( empty($plugin_base_path) ) {
56
+ return false;
57
+ }
58
+ } else {
59
+ $plugin_base_path = $slug;
60
+ }
61
+
62
+ require_once ABSPATH . '/wp-admin/includes/plugin.php';
63
+
64
+ return is_plugin_active($plugin_base_path);
65
+ }
66
+
67
+ /**
68
+ * Static method will check whether the plugin is installed or not. You can check whether the plugin exists
69
+ * by using its slug or the base path.
70
+ *
71
+ * @param string $slug - slug "clearfy" or base_path "clearfy/clearfy.php"
72
+ * @return bool
73
+ */
74
+ public static function isPluginInstalled($slug)
75
+ {
76
+ if( strpos(rtrim(trim($slug)), '/') === false ) {
77
+ $plugin_base_path = self::getPluginBasePathBySlug($slug);
78
+
79
+ if( !empty($plugin_base_path) ) {
80
+ return true;
81
+ }
82
+ } else {
83
+
84
+ // Check if the function get_plugins() is registered. It is necessary for the front-end
85
+ // usually get_plugins() only works in the admin panel.
86
+ if( !function_exists('get_plugins') ) {
87
+ require_once ABSPATH . 'wp-admin/includes/plugin.php';
88
+ }
89
+
90
+ $plugins = get_plugins();
91
+
92
+ if( isset($plugins[$slug]) ) {
93
+ return true;
94
+ }
95
+ }
96
+
97
+ return false;
98
+ }
99
+
100
+ /**
101
+ * Is permalink enabled?
102
+ * @global WP_Rewrite $wp_rewrite
103
+ * @since 1.0.0
104
+ * @return bool
105
+ */
106
+ public static function isPermalink()
107
+ {
108
+ global $wp_rewrite;
109
+
110
+ if( !isset($wp_rewrite) || !is_object($wp_rewrite) || !$wp_rewrite->using_permalinks() ) {
111
+ return false;
112
+ }
113
+
114
+ return true;
115
+ }
116
+
117
+ /**
118
+ * Try to get variable from JSON-encoded post variable
119
+ *
120
+ * Note: we pass some params via json-encoded variables, as via pure post some data (ex empty array) will be absent
121
+ *
122
+ * @param string $name $_POST's variable name
123
+ *
124
+ * @return array
125
+ */
126
+ public static function maybeGetPostJson($name)
127
+ {
128
+ if( isset($_POST[$name]) AND is_string($_POST[$name]) ) {
129
+ $result = json_decode(stripslashes($_POST[$name]), true);
130
+ if( !is_array($result) ) {
131
+ $result = array();
132
+ }
133
+
134
+ return $result;
135
+ } else {
136
+ return array();
137
+ }
138
+ }
139
+
140
+ /**
141
+ * Escape json data
142
+ * @param array $data
143
+ * @return string escaped json string
144
+ */
145
+ public static function getEscapeJson(array $data)
146
+ {
147
+ return htmlspecialchars(json_encode($data), ENT_QUOTES, 'UTF-8');
148
+ }
149
+
150
+ /**
151
+ * Recursive sanitation for an array
152
+ *
153
+ * @since 2.0.5
154
+ *
155
+ * @param $array
156
+ *
157
+ * @return mixed
158
+ */
159
+ public static function recursiveSanitizeArray( $array, $function ) {
160
+ foreach ( $array as $key => &$value ) {
161
+ if ( is_array( $value ) ) {
162
+ $value = self::recursiveSanitizeArray( $value, $function );
163
+ } else {
164
+ if ( function_exists( $function ) ) {
165
+ $value = $function( $value );
166
+ }
167
+ }
168
+ }
169
+
170
+ return $array;
171
+ }
172
+
173
+ /*
174
+ * Flushes as many page cache plugin's caches as possible.
175
+ *
176
+ * @return void
177
+ */
178
+ public static function flushPageCache() {
179
+ if ( function_exists( 'wp_cache_clear_cache' ) ) {
180
+ if ( is_multisite() ) {
181
+ $blog_id = get_current_blog_id();
182
+ wp_cache_clear_cache( $blog_id );
183
+ } else {
184
+ wp_cache_clear_cache();
185
+ }
186
+ } else if ( has_action( 'cachify_flush_cache' ) ) {
187
+ do_action( 'cachify_flush_cache' );
188
+ } else if ( function_exists( 'w3tc_pgcache_flush' ) ) {
189
+ w3tc_pgcache_flush();
190
+ } else if ( function_exists( 'wp_fast_cache_bulk_delete_all' ) ) {
191
+ wp_fast_cache_bulk_delete_all();
192
+ } else if ( class_exists( 'WpFastestCache' ) ) {
193
+ $wpfc = new WpFastestCache();
194
+ $wpfc->deleteCache();
195
+ } else if ( class_exists( 'c_ws_plugin__qcache_purging_routines' ) ) {
196
+ c_ws_plugin__qcache_purging_routines::purge_cache_dir(); // quick cache
197
+ } else if ( class_exists( 'zencache' ) ) {
198
+ zencache::clear();
199
+ } else if ( class_exists( 'comet_cache' ) ) {
200
+ comet_cache::clear();
201
+ } else if ( class_exists( 'WpeCommon' ) ) {
202
+ // WPEngine cache purge/flush methods to call by default
203
+ $wpe_methods = [
204
+ 'purge_varnish_cache',
205
+ ];
206
+
207
+ // More agressive clear/flush/purge behind a filter
208
+ if ( apply_filters( 'wbcr/factory/flush_wpengine_aggressive', false ) ) {
209
+ $wpe_methods = array_merge( $wpe_methods, [ 'purge_memcached', 'clear_maxcdn_cache' ] );
210
+ }
211
+
212
+ // Filtering the entire list of WpeCommon methods to be called (for advanced usage + easier testing)
213
+ $wpe_methods = apply_filters( 'wbcr/factory/wpengine_methods', $wpe_methods );
214
+
215
+ foreach ( $wpe_methods as $wpe_method ) {
216
+ if ( method_exists( 'WpeCommon', $wpe_method ) ) {
217
+ WpeCommon::$wpe_method();
218
+ }
219
+ }
220
+ } else if ( function_exists( 'sg_cachepress_purge_cache' ) ) {
221
+ sg_cachepress_purge_cache();
222
+ } else if ( file_exists( WP_CONTENT_DIR . '/wp-cache-config.php' ) && function_exists( 'prune_super_cache' ) ) {
223
+ // fallback for WP-Super-Cache
224
+ global $cache_path;
225
+ if ( is_multisite() ) {
226
+ $blog_id = get_current_blog_id();
227
+ prune_super_cache( get_supercache_dir( $blog_id ), true );
228
+ prune_super_cache( $cache_path . 'blogs/', true );
229
+ } else {
230
+ prune_super_cache( $cache_path . 'supercache/', true );
231
+ prune_super_cache( $cache_path, true );
232
+ }
233
+ }
234
+ }
235
+
236
+
237
+ }
includes/index.php ADDED
@@ -0,0 +1,2 @@
 
 
1
+ <?php
2
+ // Silence is golden.
includes/logger/assets/css/base.css CHANGED
@@ -1,20 +1,20 @@
1
- .wbcr-factory-page-group-header {
2
- margin-top: 0;
3
- }
4
- .wbcr-factory-page-group-body {
5
- padding: 20px;
6
- }
7
- .wlogger-viewer {
8
- width: 100%;
9
- height: 650px;
10
- font-family: "Menlo", "DejaVu Sans Mono", "Liberation Mono", "Consolas", "Ubuntu Mono", "Courier New", "andale mono", "lucida console", monospace;
11
- font-size: 12px;
12
- word-break: break-all;
13
- word-wrap: break-word;
14
- overflow: auto;
15
- -ms-overflow-style: scrollbar;
16
- background-color: #e9e9e9;
17
- padding: 8px;
18
- border: 1px solid #cfcfcf;
19
- }
20
  /*# sourceMappingURL=base.css.map */
1
+ .wbcr-factory-page-group-header {
2
+ margin-top: 0;
3
+ }
4
+ .wbcr-factory-page-group-body {
5
+ padding: 20px;
6
+ }
7
+ .wlogger-viewer {
8
+ width: 100%;
9
+ height: 650px;
10
+ font-family: "Menlo", "DejaVu Sans Mono", "Liberation Mono", "Consolas", "Ubuntu Mono", "Courier New", "andale mono", "lucida console", monospace;
11
+ font-size: 12px;
12
+ word-break: break-all;
13
+ word-wrap: break-word;
14
+ overflow: auto;
15
+ -ms-overflow-style: scrollbar;
16
+ background-color: #e9e9e9;
17
+ padding: 8px;
18
+ border: 1px solid #cfcfcf;
19
+ }
20
  /*# sourceMappingURL=base.css.map */
includes/logger/assets/js/base.js CHANGED
@@ -6,7 +6,6 @@
6
  * @version 1.0
7
  */
8
 
9
-
10
  (function($) {
11
  'use strict';
12
 
@@ -20,18 +19,18 @@
20
  url: ajaxurl,
21
  method: 'post',
22
  data: {
23
- action: 'wlogger-logs-cleanup',
24
  nonce: btn.data('nonce')
25
  },
26
  success: function(data) {
27
  btn.html(currentBtnText);
28
 
29
- jQuery('#js-wlogger-viewer').html('');
30
- jQuery('#js-wlogger-size').text('0B');
31
- jQuery.wbcr_factory_clearfy_217.app.showNotice(data.message, data.type);
32
  },
33
  error: function(jqXHR, textStatus, errorThrown) {
34
- jQuery.wbcr_factory_clearfy_217.app.showNotice('Error: ' + errorThrown + ', status: ' + textStatus, 'danger');
35
  btn.html(currentBtnText);
36
  }
37
  });
6
  * @version 1.0
7
  */
8
 
 
9
  (function($) {
10
  'use strict';
11
 
19
  url: ajaxurl,
20
  method: 'post',
21
  data: {
22
+ action: 'wtitan-logger-logs-cleanup',
23
  nonce: btn.data('nonce')
24
  },
25
  success: function(data) {
26
  btn.html(currentBtnText);
27
 
28
+ $('#js-wlogger-viewer').html('');
29
+ $('#js-wlogger-size').text('0B');
30
+ $.wbcr_factory_clearfy_218.app.showNotice(data.message, data.type);
31
  },
32
  error: function(jqXHR, textStatus, errorThrown) {
33
+ $.wbcr_factory_clearfy_218.app.showNotice('Error: ' + errorThrown + ', status: ' + textStatus, 'danger');
34
  btn.html(currentBtnText);
35
  }
36
  });
includes/logger/class-logger-export.php CHANGED
@@ -1,6 +1,6 @@
1
  <?php
2
 
3
- namespace WBCR\Logger;
4
 
5
  /**
6
  * Prepares export files, ZIPs them and allows to download the package.
@@ -25,7 +25,7 @@ class Export {
25
  /**
26
  * @var string Default archive name on download. {datetime} will be replaced with current m-d-Y.
27
  */
28
- private $_archive_name = 'wio_export-{datetime}.zip';
29
 
30
  /**
31
  * @var string|null Archive save path.
@@ -37,8 +37,9 @@ class Export {
37
  *
38
  * @param null $archive_name
39
  */
40
- public function __construct( $archive_name = null ) {
41
- if ( $archive_name !== null ) {
 
42
  $this->_archive_name = $archive_name;
43
  }
44
  }
@@ -48,50 +49,51 @@ class Export {
48
  *
49
  * @return bool
50
  */
51
- public function prepare() {
 
52
 
53
- if ( ! class_exists( '\ZipArchive' ) ) {
54
- \WBCR\Logger\Writter::error( 'App does not have \ZipArchive class available. It is not possible to prepare export' );
55
 
56
  return false;
57
  }
58
 
59
  $zip = new \ZipArchive();
60
 
61
- $log_base_dir = \WBCR\Logger\Writter::get_base_dir();
62
 
63
- if ( $log_base_dir === false ) {
64
- \WBCR\Logger\Writter::error( sprintf( 'Failed to get log path %s', $log_base_dir ) );
65
 
66
  return false;
67
  }
68
 
69
  $uploads = wp_get_upload_dir();
70
 
71
- if ( isset( $uploads['error'] ) && $uploads['error'] !== false ) {
72
- \WBCR\Logger\Writter::error( 'Unable to get save path of ZIP archive from wp_get_upload_dir()' );
73
 
74
  return false;
75
  }
76
 
77
- $save_base_path = isset( $uploads['basedir'] ) ? $uploads['basedir'] : null;
78
- $zip_archive_name = sprintf( "antispam_debug_report-%s.zip", date( 'Y-m-d' ) );
79
- $zip_save_path = $save_base_path . DIRECTORY_SEPARATOR . $zip_archive_name;
80
 
81
- if ( ! $zip->open( $zip_save_path, \ZipArchive::CREATE ) ) {
82
- \WBCR\Logger\Writter::error( sprintf( 'Failed to created ZIP archive in path %s. Skipping export...', $zip_save_path ) );
83
 
84
  return false;
85
  }
86
 
87
  // Add all logs to ZIP archive
88
  $glob_path = $log_base_dir . '*.log';
89
- $log_files = glob( $glob_path );
90
 
91
- if ( ! empty( $log_files ) ) {
92
- foreach ( $log_files as $file ) {
93
- if ( ! $zip->addFile( $file, wp_basename( $file ) ) ) {
94
- \WBCR\Logger\Writter::error( sprintf( 'Failed to add %s to %s archive. Skipping it.', $file, $zip_save_path ) );
95
 
96
  return false;
97
  }
@@ -100,25 +102,25 @@ class Export {
100
 
101
  $system_info = $this->prepare_system_info();
102
 
103
- if ( ! empty( $system_info ) ) {
104
- $system_info_file_name = 'wrio-system-info.txt';
105
- $system_info_path = $save_base_path . DIRECTORY_SEPARATOR . $system_info_file_name;
106
- if ( false !== @file_put_contents( $system_info_path, $system_info ) ) {
107
- if ( ! $zip->addFile( $system_info_path, $system_info_file_name ) ) {
108
- \WBCR\Logger\Writter::error( sprintf( 'Failed to add %s to %s archive. Skipping it.', $system_info_file_name, $system_info_path ) );
109
  }
110
  } else {
111
- \WBCR\Logger\Writter::error( sprintf( 'Failed to save %s in %s', $system_info_file_name, $zip_save_path ) );
112
  }
113
  }
114
 
115
- if ( ! $zip->close() ) {
116
- \WBCR\Logger\Writter::error( sprintf( 'Failed to close ZIP archive %s for unknown reason. \ZipArchive::close() failed.' ) );
117
  }
118
 
119
- if ( isset( $system_info_path ) ) {
120
  // Clean-up as this is just temp file
121
- @unlink( $system_info_path );
122
  }
123
 
124
  $this->_archive_save_path = $zip_save_path;
@@ -131,55 +133,56 @@ class Export {
131
  *
132
  * @return string
133
  */
134
- public function prepare_system_info() {
 
135
 
136
  $space = PHP_EOL . PHP_EOL;
137
- $nl = PHP_EOL;
138
 
139
- $report = 'Plugin version: ' . \WBCR\Antispam\Plugin::app()->getPluginVersion() . $nl;
140
 
141
  global $wp_version;
142
 
143
  $report .= 'WordPress Version: ' . $wp_version . $nl;
144
  $report .= 'PHP Version: ' . PHP_VERSION . $nl;
145
  $report .= 'Locale: ' . get_locale() . $nl;
146
- $report .= 'HTTP Accept: ' . ( isset( $_SERVER['HTTP_ACCEPT'] ) ? $_SERVER['HTTP_ACCEPT'] : '*empty*' ) . $nl;
147
- $report .= 'HTTP User Agent: ' . ( isset( $_SERVER['HTTP_USER_AGENT'] ) ? $_SERVER['HTTP_USER_AGENT'] : '*empty*' ) . $nl;
148
- $report .= 'Server software: ' . ( isset( $_SERVER['SERVER_SOFTWARE'] ) ? $_SERVER['SERVER_SOFTWARE'] : '*empty*' ) . $nl;
149
 
150
  $report .= $space;
151
 
152
- $active_plugins = get_option( 'active_plugins', null );
153
 
154
- if ( $active_plugins !== null ) {
155
 
156
  $prepared_plugins = [];
157
 
158
  $all_plugins = get_plugins();
159
 
160
- foreach ( $active_plugins as $active_plugin ) {
161
- if ( isset( $all_plugins[ $active_plugin ] ) ) {
162
- $advanced_info = $all_plugins[ $active_plugin ];
163
- $name = isset( $advanced_info['Name'] ) ? $advanced_info['Name'] : '';
164
- $version = isset( $advanced_info['Version'] ) ? $advanced_info['Version'] : '';
165
- $prepared_plugins[] = sprintf( "%s (%s)", $name, $version );
166
  }
167
  }
168
 
169
  $report .= 'Active plugins:' . PHP_EOL;
170
- $report .= implode( PHP_EOL, $prepared_plugins );
171
  }
172
 
173
- if ( function_exists( 'get_loaded_extensions' ) ) {
174
 
175
  $report .= PHP_EOL . PHP_EOL;
176
  $report .= 'Active extensions: ' . $nl;
177
- $report .= implode( ', ', get_loaded_extensions() );
178
  }
179
 
180
  $report .= $space;
181
 
182
- $report .= 'Generated at: ' . date( 'c' );
183
 
184
  return $report;
185
  }
@@ -191,43 +194,44 @@ class Export {
191
  *
192
  * Additionally it cleans-up by deleting the archive if `$and_delete` set to true.
193
  *
194
- * @param bool $should_clean_up Allows to delete temp ZIP archive if required.
195
  *
196
  * @return bool
197
  */
198
- public function download( $should_clean_up = true ) {
 
199
 
200
  $zip_save_path = $this->_archive_save_path;
201
 
202
- if ( empty( $zip_save_path ) ) {
203
  return false;
204
  }
205
 
206
- $zip_content = @file_get_contents( $zip_save_path );
207
 
208
- if ( $zip_save_path === false ) {
209
- \WBCR\Logger\Writter::error( sprintf( 'Failed to get ZIP %s content as file_get_contents() returned false', $zip_save_path ) );
210
 
211
  return false;
212
  }
213
 
214
- if ( $should_clean_up ) {
215
  // Delete as ZIP is just for temporary usage
216
- @unlink( $zip_save_path );
217
  }
218
 
219
- $archive_name = str_replace( '{datetime}', date( 'c' ), $this->_archive_name );
220
 
221
  // Set-up headers to download export file
222
- header( 'Content-Description: File Transfer' );
223
- header( 'Content-Type: application/zip' );
224
- header( 'Content-Disposition: attachment; filename=' . $archive_name );
225
- header( 'Content-Transfer-Encoding: binary' );
226
- header( 'Connection: Keep-Alive' );
227
- header( 'Expires: 0' );
228
- header( 'Cache-Control: must-revalidate, post-check=0, pre-check=0' );
229
- header( 'Pragma: public' );
230
- header( 'Content-Length: ' . strlen( $zip_content ) );
231
 
232
  echo $zip_content;
233
  exit();
@@ -238,7 +242,8 @@ class Export {
238
  *
239
  * @return string
240
  */
241
- public function get_temp_archive_path() {
 
242
  return $this->_archive_save_path;
243
  }
244
 
@@ -247,7 +252,8 @@ class Export {
247
  *
248
  * @return bool
249
  */
250
- public function delete_temp_archive() {
251
- return @unlink( $this->get_temp_archive_path() );
 
252
  }
253
  }
1
  <?php
2
 
3
+ namespace WBCR\Titan\Logger;
4
 
5
  /**
6
  * Prepares export files, ZIPs them and allows to download the package.
25
  /**
26
  * @var string Default archive name on download. {datetime} will be replaced with current m-d-Y.
27
  */
28
+ private $_archive_name = 'titan_debug-{datetime}.zip';
29
 
30
  /**
31
  * @var string|null Archive save path.
37
  *
38
  * @param null $archive_name
39
  */
40
+ public function __construct($archive_name = null)
41
+ {
42
+ if( $archive_name !== null ) {
43
  $this->_archive_name = $archive_name;
44
  }
45
  }
49
  *
50
  * @return bool
51
  */
52
+ public function prepare()
53
+ {
54
 
55
+ if( !class_exists('\ZipArchive') ) {
56
+ \WBCR\Titan\Logger\Writter::error('App does not have \ZipArchive class available. It is not possible to prepare export');
57
 
58
  return false;
59
  }
60
 
61
  $zip = new \ZipArchive();
62
 
63
+ $log_base_dir = \WBCR\Titan\Logger\Writter::get_base_dir();
64
 
65
+ if( $log_base_dir === false ) {
66
+ \WBCR\Titan\Logger\Writter::error(sprintf('Failed to get log path %s', $log_base_dir));
67
 
68
  return false;
69
  }
70
 
71
  $uploads = wp_get_upload_dir();
72
 
73
+ if( isset($uploads['error']) && $uploads['error'] !== false ) {
74
+ \WBCR\Titan\Logger\Writter::error('Unable to get save path of ZIP archive from wp_get_upload_dir()');
75
 
76
  return false;
77
  }
78
 
79
+ $save_base_path = isset($uploads['basedir']) ? $uploads['basedir'] : null;
80
+ $zip_archive_name = sprintf("titan_debug_report-%s.zip", date('Y-m-d'));
81
+ $zip_save_path = $save_base_path . DIRECTORY_SEPARATOR . $zip_archive_name;
82
 
83
+ if( !$zip->open($zip_save_path, \ZipArchive::CREATE) ) {
84
+ \WBCR\Titan\Logger\Writter::error(sprintf('Failed to created ZIP archive in path %s. Skipping export...', $zip_save_path));
85
 
86
  return false;
87
  }
88
 
89
  // Add all logs to ZIP archive
90
  $glob_path = $log_base_dir . '*.log';
91
+ $log_files = glob($glob_path);
92
 
93
+ if( !empty($log_files) ) {
94
+ foreach($log_files as $file) {
95
+ if( !$zip->addFile($file, wp_basename($file)) ) {
96
+ \WBCR\Titan\Logger\Writter::error(sprintf('Failed to add %s to %s archive. Skipping it.', $file, $zip_save_path));
97
 
98
  return false;
99
  }
102
 
103
  $system_info = $this->prepare_system_info();
104
 
105
+ if( !empty($system_info) ) {
106
+ $system_info_file_name = 'titan-system-info.txt';
107
+ $system_info_path = $save_base_path . DIRECTORY_SEPARATOR . $system_info_file_name;
108
+ if( false !== @file_put_contents($system_info_path, $system_info) ) {
109
+ if( !$zip->addFile($system_info_path, $system_info_file_name) ) {
110
+ \WBCR\Titan\Logger\Writter::error(sprintf('Failed to add %s to %s archive. Skipping it.', $system_info_file_name, $system_info_path));
111
  }
112
  } else {
113
+ \WBCR\Titan\Logger\Writter::error(sprintf('Failed to save %s in %s', $system_info_file_name, $zip_save_path));
114
  }
115
  }
116
 
117
+ if( !$zip->close() ) {
118
+ \WBCR\Titan\Logger\Writter::error(sprintf('Failed to close ZIP archive %s for unknown reason. \ZipArchive::close() failed.'));
119
  }
120
 
121
+ if( isset($system_info_path) ) {
122
  // Clean-up as this is just temp file
123
+ @unlink($system_info_path);
124
  }
125
 
126
  $this->_archive_save_path = $zip_save_path;
133
  *
134
  * @return string
135
  */
136
+ public function prepare_system_info()
137
+ {
138
 
139
  $space = PHP_EOL . PHP_EOL;
140
+ $nl = PHP_EOL;
141
 
142
+ $report = 'Plugin version: ' . \WBCR\Titan\Plugin::app()->getPluginVersion() . $nl;
143
 
144
  global $wp_version;
145
 
146
  $report .= 'WordPress Version: ' . $wp_version . $nl;
147
  $report .= 'PHP Version: ' . PHP_VERSION . $nl;
148
  $report .= 'Locale: ' . get_locale() . $nl;
149
+ $report .= 'HTTP Accept: ' . (isset($_SERVER['HTTP_ACCEPT']) ? $_SERVER['HTTP_ACCEPT'] : '*empty*') . $nl;
150
+ $report .= 'HTTP User Agent: ' . (isset($_SERVER['HTTP_USER_AGENT']) ? $_SERVER['HTTP_USER_AGENT'] : '*empty*') . $nl;
151
+ $report .= 'Server software: ' . (isset($_SERVER['SERVER_SOFTWARE']) ? $_SERVER['SERVER_SOFTWARE'] : '*empty*') . $nl;
152
 
153
  $report .= $space;
154
 
155
+ $active_plugins = get_option('active_plugins', null);
156
 
157
+ if( $active_plugins !== null ) {
158
 
159
  $prepared_plugins = [];
160
 
161
  $all_plugins = get_plugins();
162
 
163
+ foreach($active_plugins as $active_plugin) {
164
+ if( isset($all_plugins[$active_plugin]) ) {
165
+ $advanced_info = $all_plugins[$active_plugin];
166
+ $name = isset($advanced_info['Name']) ? $advanced_info['Name'] : '';
167
+ $version = isset($advanced_info['Version']) ? $advanced_info['Version'] : '';
168
+ $prepared_plugins[] = sprintf("%s (%s)", $name, $version);
169
  }
170
  }
171
 
172
  $report .= 'Active plugins:' . PHP_EOL;
173
+ $report .= implode(PHP_EOL, $prepared_plugins);
174
  }
175
 
176
+ if( function_exists('get_loaded_extensions') ) {
177
 
178
  $report .= PHP_EOL . PHP_EOL;
179
  $report .= 'Active extensions: ' . $nl;
180
+ $report .= implode(', ', get_loaded_extensions());
181
  }
182
 
183
  $report .= $space;
184
 
185
+ $report .= 'Generated at: ' . date('c');
186
 
187
  return $report;
188
  }
194
  *
195
  * Additionally it cleans-up by deleting the archive if `$and_delete` set to true.
196
  *
197
+ * @param bool $should_clean_up Allows to delete temp ZIP archive if required.
198
  *
199
  * @return bool
200
  */
201
+ public function download($should_clean_up = true)
202
+ {
203
 
204
  $zip_save_path = $this->_archive_save_path;
205
 
206
+ if( empty($zip_save_path) ) {
207
  return false;
208
  }
209
 
210
+ $zip_content = @file_get_contents($zip_save_path);
211
 
212
+ if( $zip_save_path === false ) {
213
+ \WBCR\Titan\Logger\Writter::error(sprintf('Failed to get ZIP %s content as file_get_contents() returned false', $zip_save_path));
214
 
215
  return false;
216
  }
217
 
218
+ if( $should_clean_up ) {
219
  // Delete as ZIP is just for temporary usage
220
+ @unlink($zip_save_path);
221
  }
222
 
223
+ $archive_name = str_replace('{datetime}', date('c'), $this->_archive_name);
224
 
225
  // Set-up headers to download export file
226
+ header('Content-Description: File Transfer');
227
+ header('Content-Type: application/zip');
228
+ header('Content-Disposition: attachment; filename=' . $archive_name);
229
+ header('Content-Transfer-Encoding: binary');
230
+ header('Connection: Keep-Alive');
231
+ header('Expires: 0');
232
+ header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
233
+ header('Pragma: public');
234
+ header('Content-Length: ' . strlen($zip_content));
235
 
236
  echo $zip_content;
237
  exit();
242
  *
243
  * @return string
244
  */
245
+ public function get_temp_archive_path()
246
+ {
247
  return $this->_archive_save_path;
248
  }
249
 
252
  *
253
  * @return bool
254
  */
255
+ public function delete_temp_archive()
256
+ {
257
+ return @unlink($this->get_temp_archive_path());
258
  }
259
  }
includes/logger/class-logger-reader.php CHANGED
@@ -1,6 +1,6 @@
1
  <?php
2
 
3
- namespace WBCR\Logger;
4
 
5
  // Exit if accessed directly
6
  if ( ! defined( 'ABSPATH' ) ) {
@@ -18,9 +18,9 @@ if ( ! defined( 'ABSPATH' ) ) {
18
  *
19
  * @author Alexander Teshabaev <sasha.tesh@gmail.com>
20
  * @author Alexander Kovalev <alex.kovalevv@gmail.com>, Github: https://github.com/alexkovalevv
21
- * @see \WBCR\Logger\Writter::get_content() for method which is used to get file content.
22
  *
23
- * @see \WBCR\Logger\Writter for further information about logging.
24
  */
25
  class Reader {
26
 
@@ -28,10 +28,10 @@ class Reader {
28
  * Prettify log content.
29
  *
30
  * @return bool|mixed|string
31
- * @see \WBCR\Logger\Writter::get_content()
32
  */
33
  public static function prettify() {
34
- $content = \WBCR\Logger\Writter::get_content();
35
 
36
  $search = [
37
  "\r\n",
@@ -54,10 +54,10 @@ class Reader {
54
  $content = str_replace( $search, $replacement, $content );
55
 
56
  $color_map = [
57
- \WBCR\Logger\Writter::LEVEL_INFO => [ 'color' => '#fff', 'bg' => '#52d130' ],
58
- \WBCR\Logger\Writter::LEVEL_ERROR => [ 'color' => '#fff', 'bg' => '#ff5e5e' ],
59
- \WBCR\Logger\Writter::LEVEL_WARNING => [ 'color' => '#fff', 'bg' => '#ef910a' ],
60
- \WBCR\Logger\Writter::LEVEL_DEBUG => [ 'color' => '#fff', 'bg' => '#8f8d8b' ],
61
  ];
62
 
63
  /**
1
  <?php
2
 
3
+ namespace WBCR\Titan\Logger;
4
 
5
  // Exit if accessed directly
6
  if ( ! defined( 'ABSPATH' ) ) {
18
  *
19
  * @author Alexander Teshabaev <sasha.tesh@gmail.com>
20
  * @author Alexander Kovalev <alex.kovalevv@gmail.com>, Github: https://github.com/alexkovalevv
21
+ * @see \WBCR\Titan\Logger\Writter::get_content() for method which is used to get file content.
22
  *
23
+ * @see \WBCR\Titan\Logger\Writter for further information about logging.
24
  */
25
  class Reader {
26
 
28
  * Prettify log content.
29
  *
30
  * @return bool|mixed|string
31
+ * @see \WBCR\Titan\Logger\Writter::get_content()
32
  */
33
  public static function prettify() {
34
+ $content = \WBCR\Titan\Logger\Writter::get_content();
35
 
36
  $search = [
37
  "\r\n",
54
  $content = str_replace( $search, $replacement, $content );
55
 
56
  $color_map = [
57
+ \WBCR\Titan\Logger\Writter::LEVEL_INFO => [ 'color' => '#fff', 'bg' => '#52d130' ],
58
+ \WBCR\Titan\Logger\Writter::LEVEL_ERROR => [ 'color' => '#fff', 'bg' => '#ff5e5e' ],
59
+ \WBCR\Titan\Logger\Writter::LEVEL_WARNING => [ 'color' => '#fff', 'bg' => '#ef910a' ],
60
+ \WBCR\Titan\Logger\Writter::LEVEL_DEBUG => [ 'color' => '#fff', 'bg' => '#8f8d8b' ],
61
  ];
62
 
63
  /**
includes/logger/class-logger-writter.php CHANGED
@@ -1,9 +1,9 @@
1
  <?php
2
 
3
- namespace WBCR\Logger;
4
 
5
  // Exit if accessed directly
6
- if ( ! defined( 'ABSPATH' ) ) {
7
  exit;
8
  }
9
 
@@ -24,16 +24,16 @@ if ( ! defined( 'ABSPATH' ) ) {
24
  *
25
  * ```php
26
  * // Info message level
27
- * \WBCR\Logger\Writter::info('Some generic message, good to know');
28
  *
29
  * // Warning message level
30
- * \WBCR\Logger\Writter::warning('Something does not work or unusual');
31
  *
32
  * // Error message level
33
- * \WBCR\Logger\Writter::error('Something critical happened');
34
  *
35
  * // Debug message level
36
- * \WBCR\Logger\Writter::debug('Some message used for debug purposed. Could be stack trace.');
37
  * ```
38
  *
39
  * @author Alexander Teshabaev <sasha.tesh@gmail.com>
@@ -41,362 +41,381 @@ if ( ! defined( 'ABSPATH' ) ) {
41
  * @copyright (c) 2018, Webcraftic
42
  * @version 1.0
43
  */
44
- class Writter {
45
-
46
- const LEVEL_INFO = 'info';
47
- const LEVEL_WARNING = 'warning';
48
- const LEVEL_ERROR = 'error';
49
- const LEVEL_DEBUG = 'debug';
50
-
51
- /**
52
- * @var null|string Request hash.
53
- */
54
- public static $hash = null;
55
-
56
- /**
57
- * @var null|string Directory where log file would be saved.
58
- */
59
- public static $dir = null;
60
-
61
- /**
62
- * @var string File log name where logs would be flushed.
63
- */
64
- public static $file = 'app.log';
65
-
66
- /**
67
- * @var int Flushing interval. When $_logs would reach this number of items they would be flushed to log file.
68
- */
69
- public static $flush_interval = 1000;
70
-
71
- /**
72
- * @var int Rotate size in bytes. Default: 5 Mb.
73
- */
74
- public static $rotate_size = 5000000;
75
-
76
- /**
77
- * @var int Number of rotated files. When size of $rotate_size matches current file, current file would be rotated.
78
- * For example, there are 3 files, current file became size of $rotate_size, third file would be deleted, two first
79
- * shifted and empty one created.
80
- */
81
- public static $rotate_limit = 3;
82
-
83
- /**
84
- * @var array List of logs to be dumped.
85
- */
86
- private static $_logs = [];
87
-
88
- /**
89
- * WRIOP_BackupLogger constructor.
90
- */
91
- public function __construct() {
92
- $this->init();
93
- }
 
 
 
94
 
95
- /**
96
- * Initiate object.
97
- */
98
- public function init() {
99
- static::$hash = substr( uniqid(), 0, 6 );
 
100
 
101
- add_action( 'shutdown', [ '\WBCR\Logger\Writter', 'flush' ], 9999, 0 );
102
- }
103
 
104
- /**
105
- * Get directory to save collected logs.
106
- *
107
- * In addition to that, it manages log rotation so that it does not become too big.
108
- *
109
- * @return string|false false on failure, string on success.
110
- */
111
- public static function get_dir() {
 
112
 
113
- $base_dir = static::get_base_dir();
114
 
115
- if ( $base_dir === null ) {
116
- return false;
117
- }
118
 
119
- $root_file = $base_dir . static::$file;
120
 
121
- // Check whether file exists and it exceeds rotate size, then should rotate it copy
122
- if ( file_exists( $root_file ) && filesize( $root_file ) >= self::$rotate_size ) {
123
- $name_split = explode( '.', self::$file );
124
 
125
- if ( ! empty( $name_split ) && isset( $name_split[0] ) ) {
126
- $name_split[0] = trim( $name_split[0] );
127
 
128
- for ( $i = self::$rotate_limit; $i >= 0; $i -- ) {
129
 
130
- $cur_name = $name_split[0] . $i;
131
- $cur_path = $base_dir . $cur_name . '.log';
132
 
133
- $next_path = $i !== 0 ? $base_dir . $name_split[0] . ( $i - 1 ) . '.log' : $root_file;
134
 
135
- if ( file_exists( $next_path ) ) {
136
- @copy( $next_path, $cur_path );
 
137
  }
138
  }
 
 
 
139
  }
140
 
141
- // Need to empty root file as it was supposed to be copied to next rotation :)
142
- @file_put_contents( $root_file, '' );
143
  }
144
 
145
- return $root_file;
146
- }
147
-
148
- /**
149
- * Get base directory, location of logs.
150
- *
151
- * @return null|string NULL in case of failure, string on success.
152
- */
153
- public static function get_base_dir() {
154
- $wp_upload_dir = wp_upload_dir();
155
-
156
- if ( isset( $wp_upload_dir['error'] ) && $wp_upload_dir['error'] !== false ) {
157
- return null;
158
- }
159
 
160
- $base_path = wp_normalize_path( trailingslashit( $wp_upload_dir['basedir'] ) . 'wantispam-logger/' );
161
 
162
- $folders = glob( $base_path . 'logs-*' );
163
 
164
- if ( ! empty( $folders ) ) {
165
- $exploded_path = explode( '/', trim( $folders[0] ) );
166
- $selected_logs_folder = array_pop( $exploded_path );
167
- } else {
168
- if ( function_exists( 'wp_salt' ) ) {
169
- $hash = md5( wp_salt() );
170
  } else {
171
- $hash = md5( AUTH_KEY );
 
 
 
 
 
 
172
  }
173
 
174
- $selected_logs_folder = 'logs-' . $hash;
175
- }
176
 
177
- $path = $base_path . $selected_logs_folder . '/';
 
 
178
 
179
- if ( ! file_exists( $path ) ) {
180
- @mkdir( $path, 0755, true );
181
- }
182
 
183
- // Create .htaccess file to protect log files
184
- $htaccess_path = $path . '.htaccess';
 
 
185
 
186
- if ( ! file_exists( $htaccess_path ) ) {
187
- $htaccess_content = 'deny from all';
188
- @file_put_contents( $htaccess_path, $htaccess_content );
189
- }
190
 
191
- // Create index.htm file in case .htaccess is not support as a fallback
192
- $index_path = $path . 'index.html';
 
193
 
194
- if ( ! file_exists( $index_path ) ) {
195
- @file_put_contents( $index_path, '' );
196
  }
197
 
198
- return $path;
199
- }
 
 
 
 
 
 
 
 
 
 
200
 
201
- /**
202
- * Get all available log paths.
203
- *
204
- * @return array|bool
205
- */
206
- public static function get_all() {
207
- $base_dir = static::get_base_dir();
208
 
209
- if ( $base_dir === null ) {
210
- return false;
211
  }
212
 
213
- $glob_path = $base_dir . '*.log';
 
 
 
 
 
 
 
 
 
 
 
 
 
214
 
215
- return glob( $glob_path );
216
- }
 
217
 
218
- /**
219
- * Get total log size in bytes.
220
- *
221
- * @return int
222
- * @see size_format() for formatting.
223
- */
224
- public static function get_total_size() {
225
- $logs = static::get_all();
226
- $bytes = 0;
227
-
228
- if ( empty( $logs ) ) {
229
  return $bytes;
230
  }
231
 
232
- foreach ( $logs as $log ) {
233
- $bytes += @filesize( $log );
234
- }
235
-
236
- return $bytes;
237
- }
238
-
239
- /**
240
- * Empty all log files and deleted rotated ones.
241
- *
242
- * @return bool
243
- */
244
- public static function clean_up() {
245
-
246
- $base_dir = static::get_base_dir();
247
-
248
- if ( $base_dir === null ) {
249
- return false;
250
- }
251
-
252
- $glob_path = $base_dir . '*.log';
253
 
254
- $files = glob( $glob_path );
255
 
256
- if ( $files === false ) {
257
- return false;
258
- }
259
 
260
- if ( empty( $files ) ) {
261
- return true;
262
- }
263
 
264
- $unlinked_count = 0;
265
 
266
- foreach ( $files as $file ) {
267
- if ( @unlink( $file ) ) {
268
- $unlinked_count ++;
269
  }
270
- }
271
-
272
- return count( $files ) === $unlinked_count;
273
- }
274
 
275
- /**
276
- * Flush all messages.
277
- *
278
- * @return bool
279
- */
280
- public static function flush() {
281
 
282
- $messages = self::$_logs;
283
 
284
- self::$_logs = [];
 
 
 
 
285
 
286
- if ( empty( $messages ) ) {
287
- return false;
288
  }
289
 
290
- $file_content = PHP_EOL . implode( PHP_EOL, $messages );
291
- $is_put = @file_put_contents( self::get_dir(), $file_content, FILE_APPEND );
292
-
293
- return $is_put !== false;
294
- }
 
 
295
 
296
- /**
297
- *
298
- * @param $level
299
- * @param $message
300
- *
301
- * @return string
302
- */
303
- public static function get_format( $level, $message ) {
304
 
305
- // Example: 2019-01-14 12:03:29.0593 [127.0.0.1][ee6a12][info] {message}
306
- $template = '%s [%s][%s][%s] %s';
307
 
308
- $ip = isset( $_SERVER['REMOTE_ADDR'] ) ? $_SERVER['REMOTE_ADDR'] : '';
 
 
309
 
310
- return sprintf( $template, date( 'd-m-Y H:i:s' ) . '.' . microtime( true ), $ip, static::$hash, $level, $message );
311
- }
312
 
313
- /**
314
- * Get latest file content.
315
- *
316
- * @return bool|string
317
- */
318
- public static function get_content() {
319
- if ( ! file_exists( static::get_dir() ) ) {
320
- return null;
321
  }
322
 
323
- return @file_get_contents( static::get_dir() );
324
- }
 
 
 
 
 
 
 
325
 
326
- /**
327
- * Add new log message.
328
- *
329
- * @param string $level Log level.
330
- * @param string $message Message to log.
331
- *
332
- * @return bool
333
- */
334
- public static function add( $level, $message ) {
335
 
336
- //if ( $level === self::LEVEL_DEBUG ) {
337
- //$log_debug = defined( 'WP_DEBUG' ) && WP_DEBUG;
338
 
339
- //if ( ! $log_debug ) {
340
- //return false;
341
- //}
342
- //}
343
 
344
- static::$_logs[] = static::get_format( $level, $message );
 
 
 
 
 
 
 
 
 
345
 
346
- if ( count( static::$_logs ) >= static::$flush_interval ) {
347
- static::flush();
348
  }
349
 
350
- return true;
351
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
352
 
353
- /**
354
- * Add info level log.
355
- *
356
- * @param string $message Message to log.
357
- */
358
- public static function info( $message ) {
359
- static::add( self::LEVEL_INFO, $message );
360
- }
361
 
362
- /**
363
- * Add error level log.
364
- *
365
- * @param string $message Message to log.
366
- */
367
- public static function error( $message ) {
368
- static::add( self::LEVEL_ERROR, $message );
369
- }
 
370
 
371
- /**
372
- * Add debug level log.
373
- *
374
- * @param $message
375
- */
376
- public static function debug( $message ) {
377
- static::add( self::LEVEL_DEBUG, $message );
378
- }
 
379
 
380
- /**
381
- * Add warning level log.
382
- *
383
- * @param string $message Message to log.
384
- */
385
- public static function warning( $message ) {
386
- static::add( self::LEVEL_WARNING, $message );
387
- }
 
388
 
389
- /**
390
- * Writes information to log about memory.
391
- *
392
- * @author Alexander Kovalev <alex.kovalevv@gmail.com>
393
- * @since 1.3.6
394
- */
395
- public static function memory_usage() {
396
- $memory_avail = ini_get( 'memory_limit' );
397
- $memory_used = number_format( memory_get_usage( true ) / ( 1024 * 1024 ), 2 );
398
- $memory_peak = number_format( memory_get_peak_usage( true ) / ( 1024 * 1024 ), 2 );
399
-
400
- static::info( sprintf( "Memory: %s (avail) / %sM (used) / %sM (peak)", $memory_avail, $memory_used, $memory_peak ) );
 
 
 
 
 
 
 
 
 
 
 
 
401
  }
402
- }
1
  <?php
2
 
3
+ namespace WBCR\Titan\Logger;
4
 
5
  // Exit if accessed directly
6
+ if( !defined('ABSPATH') && !defined('WFWAF_AUTO_PREPEND') ) {
7
  exit;
8
  }
9
 
24
  *
25
  * ```php
26
  * // Info message level
27
+ * \WBCR\Titan\Logger\Writter::info('Some generic message, good to know');
28
  *
29
  * // Warning message level
30
+ * \WBCR\Titan\Logger\Writter::warning('Something does not work or unusual');
31
  *
32
  * // Error message level
33
+ * \WBCR\Titan\Logger\Writter::error('Something critical happened');
34
  *
35
  * // Debug message level
36
+ * \WBCR\Titan\Logger\Writter::debug('Some message used for debug purposed. Could be stack trace.');
37
  * ```
38
  *
39
  * @author Alexander Teshabaev <sasha.tesh@gmail.com>
41
  * @copyright (c) 2018, Webcraftic
42
  * @version 1.0
43
  */
44
+ if( !class_exists('\WBCR\Titan\Logger\Writter') ) {
45
+
46
+ class Writter {
47
+
48
+ const LEVEL_INFO = 'info';
49
+ const LEVEL_WARNING = 'warning';
50
+ const LEVEL_ERROR = 'error';
51
+ const LEVEL_DEBUG = 'debug';
52
+
53
+ /**
54
+ * @var null|string Request hash.
55
+ */
56
+ public static $hash = null;
57
+
58
+ /**
59
+ * @var null|string Directory where log file would be saved.
60
+ */
61
+ public static $dir = null;
62
+
63
+ /**
64
+ * @var string File log name where logs would be flushed.
65
+ */
66
+ public static $file = 'app.log';
67
+
68
+ /**
69
+ * @var int Flushing interval. When $_logs would reach this number of items they would be flushed to log file.
70
+ */
71
+ public static $flush_interval = 1000;
72
+
73
+ /**
74
+ * @var int Rotate size in bytes. Default: 5 Mb.
75
+ */
76
+ public static $rotate_size = 5000000;
77
+
78
+ /**
79
+ * @var int Number of rotated files. When size of $rotate_size matches current file, current file would be rotated.
80
+ * For example, there are 3 files, current file became size of $rotate_size, third file would be deleted, two first
81
+ * shifted and empty one created.
82
+ */
83
+ public static $rotate_limit = 3;
84
+
85
+ /**
86
+ * @var array List of logs to be dumped.
87
+ */
88
+ private static $_logs = [];
89
+
90
+ /**
91
+ * WRIOP_BackupLogger constructor.
92
+ */
93
+ public function __construct()
94
+ {
95
+ $this->init();
96
+ }
97
 
98
+ /**
99
+ * Initiate object.
100
+ */
101
+ public function init()
102
+ {
103
+ static::$hash = substr(uniqid(), 0, 6);
104
 
105
+ add_action('shutdown', ['\WBCR\Titan\Logger\Writter', 'flush'], 9999, 0);
106
+ }
107
 
108
+ /**
109
+ * Get directory to save collected logs.
110
+ *
111
+ * In addition to that, it manages log rotation so that it does not become too big.
112
+ *
113
+ * @return string|false false on failure, string on success.
114
+ */
115
+ public static function get_dir()
116
+ {
117
 
118
+ $base_dir = static::get_base_dir();
119
 
120
+ if( $base_dir === null ) {
121
+ return false;
122
+ }
123
 
124
+ $root_file = $base_dir . static::$file;
125
 
126
+ // Check whether file exists and it exceeds rotate size, then should rotate it copy
127
+ if( file_exists($root_file) && filesize($root_file) >= self::$rotate_size ) {
128
+ $name_split = explode('.', self::$file);
129
 
130
+ if( !empty($name_split) && isset($name_split[0]) ) {
131
+ $name_split[0] = trim($name_split[0]);
132
 
133
+ for($i = self::$rotate_limit; $i >= 0; $i--) {
134
 
135
+ $cur_name = $name_split[0] . $i;
136
+ $cur_path = $base_dir . $cur_name . '.log';
137
 
138
+ $next_path = $i !== 0 ? $base_dir . $name_split[0] . ($i - 1) . '.log' : $root_file;
139
 
140
+ if( file_exists($next_path) ) {
141
+ @copy($next_path, $cur_path);
142
+ }
143
  }
144
  }
145
+
146
+ // Need to empty root file as it was supposed to be copied to next rotation :)
147
+ @file_put_contents($root_file, '');
148
  }
149
 
150
+ return $root_file;
 
151
  }
152
 
153
+ /**
154
+ * Get base directory, location of logs.
155
+ *
156
+ * @return null|string NULL in case of failure, string on success.
157
+ */
158
+ public static function get_base_dir()
159
+ {
160
+ $wp_upload_dir = wp_upload_dir();
161
+
162
+ if( isset($wp_upload_dir['error']) && $wp_upload_dir['error'] !== false ) {
163
+ return null;
164
+ }
 
 
165
 
166
+ $base_path = wp_normalize_path(trailingslashit($wp_upload_dir['basedir']) . 'wtitan-logger/');
167
 
168
+ $folders = glob($base_path . 'logs-*');
169
 
170
+ if( !empty($folders) ) {
171
+ $exploded_path = explode('/', trim($folders[0]));
172
+ $selected_logs_folder = array_pop($exploded_path);
 
 
 
173
  } else {
174
+ if( function_exists('wp_salt') ) {
175
+ $hash = md5(wp_salt());
176
+ } else {
177
+ $hash = md5(AUTH_KEY);
178
+ }
179
+
180
+ $selected_logs_folder = 'logs-' . $hash;
181
  }
182
 
183
+ $path = $base_path . $selected_logs_folder . '/';
 
184
 
185
+ if( !file_exists($path) ) {
186
+ @mkdir($path, 0755, true);
187
+ }
188
 
189
+ // Create .htaccess file to protect log files
190
+ $htaccess_path = $path . '.htaccess';
 
191
 
192
+ if( !file_exists($htaccess_path) ) {
193
+ $htaccess_content = 'deny from all';
194
+ @file_put_contents($htaccess_path, $htaccess_content);
195
+ }
196
 
197
+ // Create index.htm file in case .htaccess is not support as a fallback
198
+ $index_path = $path . 'index.html';
 
 
199
 
200
+ if( !file_exists($index_path) ) {
201
+ @file_put_contents($index_path, '');
202
+ }
203
 
204
+ return $path;
 
205
  }
206
 
207
+ /**
208
+ * Get all available log paths.
209
+ *
210
+ * @return array|bool
211
+ */
212
+ public static function get_all()
213
+ {
214
+ $base_dir = static::get_base_dir();
215
+
216
+ if( $base_dir === null ) {
217
+ return false;
218
+ }
219
 
220
+ $glob_path = $base_dir . '*.log';
 
 
 
 
 
 
221
 
222
+ return glob($glob_path);
 
223
  }
224
 
225
+ /**
226
+ * Get total log size in bytes.
227
+ *
228
+ * @return int
229
+ * @see size_format() for formatting.
230
+ */
231
+ public static function get_total_size()
232
+ {
233
+ $logs = static::get_all();
234
+ $bytes = 0;
235
+
236
+ if( empty($logs) ) {
237
+ return $bytes;
238
+ }
239
 
240
+ foreach($logs as $log) {
241
+ $bytes += @filesize($log);
242
+ }
243
 
 
 
 
 
 
 
 
 
 
 
 
244
  return $bytes;
245
  }
246
 
247
+ /**
248
+ * Empty all log files and deleted rotated ones.
249
+ *
250
+ * @return bool
251
+ */
252
+ public static function clean_up()
253
+ {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
254
 
255
+ $base_dir = static::get_base_dir();
256
 
257
+ if( $base_dir === null ) {
258
+ return false;
259
+ }
260
 
261
+ $glob_path = $base_dir . '*.log';
 
 
262
 
263
+ $files = glob($glob_path);
264
 
265
+ if( $files === false ) {
266
+ return false;
 
267
  }
 
 
 
 
268
 
269
+ if( empty($files) ) {
270
+ return true;
271
+ }
 
 
 
272
 
273
+ $unlinked_count = 0;
274
 
275
+ foreach($files as $file) {
276
+ if( @unlink($file) ) {
277
+ $unlinked_count++;
278
+ }
279
+ }
280
 
281
+ return count($files) === $unlinked_count;
 
282
  }
283
 
284
+ /**
285
+ * Flush all messages.
286
+ *
287
+ * @return bool
288
+ */
289
+ public static function flush()
290
+ {
291
 
292
+ $messages = self::$_logs;
 
 
 
 
 
 
 
293
 
294
+ self::$_logs = [];
 
295
 
296
+ if( empty($messages) ) {
297
+ return false;
298
+ }
299
 
300
+ $file_content = PHP_EOL . implode(PHP_EOL, $messages);
301
+ $is_put = @file_put_contents(self::get_dir(), $file_content, FILE_APPEND);
302
 
303
+ return $is_put !== false;
 
 
 
 
 
 
 
304
  }
305
 
306
+ /**
307
+ *
308
+ * @param $level
309
+ * @param $message
310
+ *
311
+ * @return string
312
+ */
313
+ public static function get_format($level, $message)
314
+ {
315
 
316
+ // Example: 2019-01-14 12:03:29.0593 [127.0.0.1][ee6a12][info] {message}
317
+ $template = '%s [%s][%s][%s] %s';
 
 
 
 
 
 
 
318
 
319
+ $ip = isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : '';
 
320
 
321
+ return sprintf($template, date('d-m-Y H:i:s') . '.' . microtime(true), $ip, static::$hash, $level, $message);
322
+ }
 
 
323
 
324
+ /**
325
+ * Get latest file content.
326
+ *
327
+ * @return bool|string
328
+ */
329
+ public static function get_content()
330
+ {
331
+ if( !file_exists(static::get_dir()) ) {
332
+ return null;
333
+ }
334
 
335
+ return @file_get_contents(static::get_dir());
 
336
  }
337
 
338
+ /**
339
+ * Add new log message.
340
+ *
341
+ * @param string $level Log level.
342
+ * @param string $message Message to log.
343
+ *
344
+ * @return bool
345
+ */
346
+ public static function add($level, $message)
347
+ {
348
+
349
+ //if ( $level === self::LEVEL_DEBUG ) {
350
+ //$log_debug = defined( 'WP_DEBUG' ) && WP_DEBUG;
351
+
352
+ //if ( ! $log_debug ) {
353
+ //return false;
354
+ //}
355
+ //}
356
+
357
+ static::$_logs[] = static::get_format($level, $message);
358
+
359
+ if( count(static::$_logs) >= static::$flush_interval ) {
360
+ static::flush();
361
+ }
362
 
363
+ return true;
364
+ }
 
 
 
 
 
 
365
 
366
+ /**
367
+ * Add info level log.
368
+ *
369
+ * @param string $message Message to log.
370
+ */
371
+ public static function info($message)
372
+ {
373
+ static::add(self::LEVEL_INFO, $message);
374
+ }
375
 
376
+ /**
377
+ * Add error level log.
378
+ *
379
+ * @param string $message Message to log.
380
+ */
381
+ public static function error($message)
382
+ {
383
+ static::add(self::LEVEL_ERROR, $message);
384
+ }
385
 
386
+ /**
387
+ * Add debug level log.
388
+ *
389
+ * @param $message
390
+ */
391
+ public static function debug($message)
392
+ {
393
+ static::add(self::LEVEL_DEBUG, $message);
394
+ }
395
 
396
+ /**
397
+ * Add warning level log.
398
+ *
399
+ * @param string $message Message to log.
400
+ */
401
+ public static function warning($message)
402
+ {
403
+ static::add(self::LEVEL_WARNING, $message);
404
+ }
405
+
406
+ /**
407
+ * Writes information to log about memory.
408
+ *
409
+ * @author Alexander Kovalev <alex.kovalevv@gmail.com>
410
+ * @since 1.3.6
411
+ */
412
+ public static function memory_usage()
413
+ {
414
+ $memory_avail = ini_get('memory_limit');
415
+ $memory_used = number_format(memory_get_usage(true) / (1024 * 1024), 2);
416
+ $memory_peak = number_format(memory_get_peak_usage(true) / (1024 * 1024), 2);
417
+
418
+ static::info(sprintf("Memory: %s (avail) / %sM (used) / %sM (peak)", $memory_avail, $memory_used, $memory_peak));
419
+ }
420
  }
421
+ }
includes/scanner/assets/css/base-statistic.css ADDED
@@ -0,0 +1,473 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #WBCR .wio-clear {
2
+ clear: both;
3
+ }
4
+ #WBCR .wrio-statistic-nav {
5
+ margin: 0;
6
+ background: #efefef;
7
+ }
8
+ #WBCR .wrio-statistic-nav ul {
9
+ margin: 0;
10
+ }
11
+ #WBCR .wrio-statistic-nav ul li {
12
+ position: relative;
13
+ display: inline-block;
14
+ margin: 0 0 0 0;
15
+ background: #ffffff;
16
+ box-shadow: 0 -2px 0 #eaeaea;
17
+ }
18
+ #WBCR .wrio-statistic-nav ul li:hover {
19
+ background: #f7f7f7;
20
+ }
21
+ #WBCR .wrio-statistic-nav ul li.active {
22
+ background: #f7f7f7;
23
+ border-top: 1px solid #d4d4d4;
24
+ border-left: 1px solid #d4d4d4;
25
+ border-right: 1px solid #d4d4d4;
26
+ border-bottom: 1px solid #f7f7f7;
27
+ margin-bottom: -1px;
28
+ }
29
+ #WBCR .wrio-statistic-nav ul li.active a {
30
+ color: #222;
31
+ }
32
+ #WBCR .wrio-statistic-nav ul li.active a .wrio-statistic-tab-percent {
33
+ border: 2px dashed #8bc34a;
34
+ color: #5e8237;
35
+ }
36
+ #WBCR .wrio-statistic-nav ul li.active .dashicons,
37
+ #WBCR .wrio-statistic-nav ul li.active .dashicons-before:before {
38
+ color: #ff8b66;
39
+ }
40
+ #WBCR .wrio-statistic-nav ul li .wrio-statistic-tab {
41
+ display: block;
42
+ padding: 10px 20px 10px 20px;
43
+ text-decoration: none;
44
+ color: #d4d4d4;
45
+ font-size: 22px;
46
+ line-height: 2;
47
+ }
48
+ #WBCR .wrio-statistic-nav ul li .wrio-statistic-tab:active,
49
+ #WBCR .wrio-statistic-nav ul li .wrio-statistic-tab:focus {
50
+ background: 0;
51
+ box-shadow: none;
52
+ outline: none;
53
+ }
54
+ #WBCR .wrio-statistic-nav ul li .wrio-statistic-tab .dashicons,
55
+ #WBCR .wrio-statistic-nav ul li .wrio-statistic-tab .dashicons-before:before {
56
+ display: inline-block;
57
+ width: 30px;
58
+ height: 30px;
59
+ font-size: 30px;
60
+ line-height: 1.5;
61
+ margin-right: 15px;
62
+ color: #d4d4d4;
63
+ }
64
+ #WBCR .wrio-statistic-nav ul li .wrio-statistic-tab .wrio-statistic-tab-percent {
65
+ display: inline-block;
66
+ width: 42px;
67
+ height: 42px;
68
+ border-radius: 100px;
69
+ border: 2px dashed #e4e4e4;
70
+ padding: 5px;
71
+ margin-left: 30px;
72
+ font-size: 14px;
73
+ font-weight: 600;
74
+ text-align: center;
75
+ color: #bdbdbd;
76
+ }
77
+ #WBCR .wrio-statistic-nav ul li .wrio-statistic-tab-premium-label:after {
78
+ display: inline-block;
79
+ position: absolute;
80
+ content: 'PRO';
81
+ background: #ff5722;
82
+ border-radius: 4px;
83
+ color: #fff;
84
+ font-size: 10px;
85
+ line-height: 1;
86
+ font-style: normal;
87
+ padding: 4px 6px;
88
+ margin-left: 4px;
89
+ vertical-align: top;
90
+ top: 10px;
91
+ left: auto;
92
+ right: 10px;
93
+ z-index: 11;
94
+ }
95
+ #WBCR .wrio-table {
96
+ width: 100%;
97
+ table-layout: fixed;
98
+ box-sizing: border-box;
99
+ border-spacing: 3px;
100
+ background: #fff;
101
+ border-top: 2px dashed #cac9c9;
102
+ }
103
+ #WBCR .wrio-table th,
104
+ #WBCR .wrio-table td {
105
+ padding: 16px 10px;
106
+ text-align: center;
107
+ }
108
+ #WBCR .wrio-table th {
109
+ background: #f3f3f3;
110
+ color: #777777;
111
+ box-shadow: 0 1px 0 #d8d8d8;
112
+ }
113
+ #WBCR .wrio-table th:nth-child(2n+1) {
114
+ background: #f9f9f9;
115
+ }
116
+ #WBCR .wrio-table tr.wrio-error {
117
+ background-color: #ffe9e9 !important;
118
+ }
119
+ #WBCR .wrio-table .wrio-table-spinner {
120
+ background: url("../img/quick-start-loader.gif") center center no-repeat;
121
+ }
122
+ #WBCR .wrio-table .wrio-table-highlighter {
123
+ display: inline-block;
124
+ padding: 3px 7px;
125
+ background: #f3f3f3;
126
+ }
127
+ #WBCR .wrio-table.wbcr-rio-folders-table td:nth-child(3) {
128
+ text-align: left;
129
+ }
130
+ #WBCR .wrio-servers {
131
+ padding: 40px 20px;
132
+ }
133
+ #WBCR .wrio-servers label span {
134
+ display: block;
135
+ font-weight: normal;
136
+ font-size: 12px;
137
+ color: #b7b2b2;
138
+ }
139
+ #WBCR .wio-columns {
140
+ overflow: hidden;
141
+ padding: 15px 0;
142
+ counter-reset: cols;
143
+ }
144
+ #WBCR .wio-columns [class^="col-"] {
145
+ float: left;
146
+ -webkit-box-sizing: border-box;
147
+ -moz-box-sizing: border-box;
148
+ box-sizing: border-box;
149
+ }
150
+ #WBCR .wio-columns .col-1-3 {
151
+ width: 33.333%;
152
+ padding-left: 28px;
153
+ }
154
+ #WBCR .wio-columns .col-2-3 {
155
+ width: 66.666%;
156
+ padding-left: 28px;
157
+ }
158
+ #WBCR .wio-columns .col-1-2 {
159
+ width: 50%;
160
+ padding: 0 20px;
161
+ }
162
+ #WBCR .wio-columns .col-statistics.col-statistics {
163
+ width: 60%;
164
+ }
165
+ #WBCR .wio-columns .col-chart.col-chart {
166
+ width: 40%;
167
+ position: relative;
168
+ padding: 20px;
169
+ font-size: 12px;
170
+ text-transform: uppercase;
171
+ background: #f1f1f1b3;
172
+ color: #abacaf;
173
+ font-weight: bold;
174
+ border-radius: 5px;
175
+ margin-top: 10px;
176
+ text-align: left;
177
+ box-shadow: 0 1px 1px rgba(0, 0, 0, 0.1);
178
+ }
179
+ #WBCR .wio-col {
180
+ float: left;
181
+ width: 50%;
182
+ box-sizing: border-box;
183
+ -webkit-flex-basis: 50%;
184
+ -ms-flex-preferred-size: 50%;
185
+ flex-basis: 50%;
186
+ }
187
+ #WBCR .wio-col {
188
+ padding-right: 20px;
189
+ }
190
+ #WBCR .wio-col + .wio-col {
191
+ padding-right: 0;
192
+ padding-left: 50px;
193
+ }
194
+ #WBCR .wio-col:target {
195
+ animation: wiohello 1s 3 linear backwards;
196
+ }
197
+ #WBCR .wio-number-you-optimized {
198
+ margin-bottom: 1.35em;
199
+ overflow: hidden;
200
+ }
201
+ #WBCR .wio-number-you-optimized #wio-total-optimized-attachments-pct {
202
+ color: #828282;
203
+ }
204
+ #WBCR .wio-number-you-optimized .wio-number {
205
+ display: table-cell;
206
+ padding-right: 15px;
207
+ font-size: 48px;
208
+ font-weight: bold;
209
+ line-height: 1;
210
+ vertical-align: middle;
211
+ white-space: nowrap;
212
+ color: #828282;
213
+ }
214
+ #WBCR .wio-number-you-optimized .wio-text {
215
+ display: table-cell;
216
+ vertical-align: middle;
217
+ overflow: hidden;
218
+ font-size: 12px;
219
+ color: #828282;
220
+ }
221
+ #WBCR .wio-number-you-optimized > p {
222
+ display: table;
223
+ }
224
+ #WBCR .wio-bars {
225
+ padding-right: 15px;
226
+ }
227
+ #WBCR .wio-bars p {
228
+ font-size: 12px;
229
+ margin-bottom: 5px;
230
+ }
231
+ #WBCR .wio-bars + .wio-number-you-optimized {
232
+ border-bottom: 0;
233
+ padding-top: 0.85em;
234
+ }
235
+ #WBCR .wio-bars + .wio-number-you-optimized p {
236
+ color: #72a53b;
237
+ }
238
+ #WBCR .wio-bar-negative .wio-progress {
239
+ background: #D2D3D6;
240
+ }
241
+ #WBCR .wio-bar-negative .wio-barnb {
242
+ color: #9d9fa5;
243
+ }
244
+ #WBCR .wio-progress {
245
+ height: 8px;
246
+ transition: width 0.3s;
247
+ /*.wio-bar-negative {
248
+ width: 92% !important;
249
+ }*/
250
+ }
251
+ #WBCR .wio-bar-positive .wio-progress {
252
+ background: #8CC152;
253
+ }
254
+ #WBCR .wio-bar-positive .wio-barnb {
255
+ color: #72a53b;
256
+ }
257
+ #WBCR .wio-bar-primary .wio-progress {
258
+ background: #8CC152;
259
+ }
260
+ #WBCR .wio-bar-primary .wio-barnb {
261
+ color: #72a53b;
262
+ }
263
+ #WBCR .wio-right-outside-number .wio-barnb {
264
+ display: block;
265
+ margin-right: -5.25em;
266
+ text-align: right;
267
+ font-weight: bold;
268
+ line-height: 0.8;
269
+ }
270
+ #WBCR .wio-chart {
271
+ position: relative;
272
+ top: 1px;
273
+ display: inline-block;
274
+ vertical-align: middle;
275
+ }
276
+ #WBCR .wio-chart-container {
277
+ position: relative;
278
+ display: inline-block;
279
+ margin-right: 5px;
280
+ }
281
+ #WBCR .wio-chart-container canvas {
282
+ display: block;
283
+ }
284
+ #WBCR .wio-overview-chart-container {
285
+ float: left;
286
+ margin-right: 20px;
287
+ }
288
+ #WBCR .wio-chart-percent {
289
+ position: absolute;
290
+ left: 0;
291
+ right: 0;
292
+ top: 50%;
293
+ margin-top: -0.5em;
294
+ line-height: 0.8;
295
+ text-align: center;
296
+ font-size: 54px;
297
+ font-weight: bold;
298
+ color: #afafaf;
299
+ }
300
+ #WBCR .wio-chart-percent span {
301
+ font-size: 20px;
302
+ vertical-align: super;
303
+ }
304
+ #WBCR #wio-overview-chart-legend {
305
+ overflow: hidden;
306
+ }
307
+ #WBCR .wio-doughnut-legend li {
308
+ display: inline-block;
309
+ position: relative;
310
+ margin-bottom: 15px;
311
+ border-radius: 5px;
312
+ padding: 3px 8px 2px 31px;
313
+ font-size: 9px;
314
+ cursor: default;
315
+ -webkit-transition: background-color 200ms ease-in-out;
316
+ -moz-transition: background-color 200ms ease-in-out;
317
+ -o-transition: background-color 200ms ease-in-out;
318
+ transition: background-color 200ms ease-in-out;
319
+ }
320
+ #WBCR .wio-doughnut-legend li span {
321
+ display: block;
322
+ position: absolute;
323
+ left: 0;
324
+ top: 0;
325
+ width: 25px;
326
+ height: 25px;
327
+ border-radius: 50%;
328
+ }
329
+ #WBCR .wt-malware-scan-button {
330
+ min-width: 180px;
331
+ padding: 12px 30px;
332
+ background: #5d05b7;
333
+ color: #ffffff;
334
+ border: 0;
335
+ box-shadow: none;
336
+ font-size: 14px;
337
+ text-transform: uppercase !important;
338
+ font-weight: bold;
339
+ border-radius: 4px;
340
+ outline: none;
341
+ }
342
+ #WBCR .wt-malware-scan-button:active {
343
+ box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.5);
344
+ }
345
+ #WBCR .wt-malware-scan-button:disabled {
346
+ background: #b083de;
347
+ color: #ffffff;
348
+ }
349
+ #WBCR .wt-malware-scan-button.wio-running {
350
+ color: #a57b3c;
351
+ background: #fdd599 url("../img/Spinner-1s-33px.gif") 10px center no-repeat;
352
+ padding-left: 50px;
353
+ }
354
+ #WBCR .wio-global-optim-phrase {
355
+ width: 180px;
356
+ padding-top: 20px;
357
+ font-size: 14px;
358
+ text-align: center;
359
+ }
360
+ #WBCR .wio-total-percent {
361
+ color: #b083de;
362
+ }
363
+ #WBCR #wio-start-msg-top,
364
+ #WBCR #wio-start-msg-right,
365
+ #WBCR #wio-start-msg-complete {
366
+ display: none;
367
+ }
368
+ #WBCR .wio-text-left {
369
+ text-align: left;
370
+ }
371
+ #WBCR span.wio-num {
372
+ display: inline !important;
373
+ position: inherit !important;
374
+ }
375
+ #WBCR .wio-image-optimize-board {
376
+ padding-bottom: 0 !important;
377
+ }
378
+ #WBCR .wio-page-statistic {
379
+ padding-left: 40px;
380
+ }
381
+ #WBCR .wio-page-statistic .wio-chart-percent {
382
+ margin-top: -1.1em;
383
+ }
384
+ #WBCR .wrio-optimization-progress {
385
+ background: none;
386
+ padding: 0;
387
+ }
388
+ #WBCR .wrio-optimization-progress h4 {
389
+ font-size: 15px;
390
+ font-weight: 700;
391
+ }
392
+ #WBCR .wrio-optimization-progress .wbcr-rio-warning-message {
393
+ padding: 20px;
394
+ background: #efefef;
395
+ font-size: 15px;
396
+ color: #b7b7b7;
397
+ font-style: italic;
398
+ }
399
+ #WBCR .wio-widget {
400
+ padding: 0 !important;
401
+ }
402
+ #WBCR .wio-widget .wio-chart-percent {
403
+ font-size: 44px;
404
+ line-height: 1;
405
+ }
406
+ #WBCR .wio-widget .wio-bars {
407
+ width: 60%;
408
+ margin-left: 155px;
409
+ }
410
+ #WBCR .wio-widget .col-chart.col-chart {
411
+ width: 100%;
412
+ }
413
+ #WBCR .wio-widget .col-controls {
414
+ width: 45%;
415
+ padding-left: 5px;
416
+ padding-top: 110px;
417
+ }
418
+ #WBCR .wio-widget .wio-doughnut-legend {
419
+ /*padding-top:30px;*/
420
+ text-align: left;
421
+ }
422
+ #WBCR .wio-widget .wio-widget-bottom {
423
+ display: table;
424
+ padding-top: 20px !important;
425
+ width: 100%;
426
+ text-align: right;
427
+ }
428
+ #WBCR .wio-widget .wio-widget-bottom li {
429
+ display: table-cell;
430
+ }
431
+ #WBCR .wio-widget .wio-widget-bottom li:first-child {
432
+ text-align: left;
433
+ }
434
+ @media (max-width: 830px) {
435
+ #WBCR .wio [class^="col-"] {
436
+ float: none;
437
+ margin-bottom: 1.5em;
438
+ }
439
+ #WBCR .wio .col-1-3,
440
+ #WBCR .wio .col-1-2 {
441
+ width: auto;
442
+ padding: 0 28px;
443
+ clear: both;
444
+ padding-top: 1em;
445
+ }
446
+ }
447
+ @keyframes wiohello {
448
+ 0%,
449
+ 100% {
450
+ background: #FFF;
451
+ }
452
+ 50% {
453
+ background: #F4F7F9;
454
+ }
455
+ }
456
+ @media (max-width: 1520px) and (min-width: 1381px), (max-width: 1086px) {
457
+ #WBCR .wio-columns .col-statistics.col-statistics,
458
+ #WBCR .wio-columns .col-chart.col-chart {
459
+ width: 50%;
460
+ }
461
+ }
462
+ @media (max-width: 808px) {
463
+ #WBCR .wio-columns .col-statistics.col-statistics,
464
+ #WBCR .wio-columns .col-chart.col-chart {
465
+ width: auto;
466
+ float: none;
467
+ padding: 0;
468
+ }
469
+ #WBCR .wio-columns .col-chart.col-chart {
470
+ margin-top: 3em;
471
+ }
472
+ }
473
+ /*# sourceMappingURL=base-statistic.css.map */
includes/scanner/assets/css/scanner-dashboard.css ADDED
@@ -0,0 +1,47 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ .wbcr-titan-content {
2
+ margin: 15px;
3
+ }
4
+
5
+ #scan {
6
+ position: relative;
7
+ }
8
+ #scan > span.text {
9
+ position: relative;
10
+ z-index: 1;
11
+ }
12
+
13
+ #scan > span.progress {
14
+ display: block;
15
+ position: absolute;
16
+ left: 0;
17
+ top: 0;
18
+ width: 0;
19
+ height: 100%;
20
+ z-index: 0;
21
+ }
22
+ /*----------------*/
23
+ .wt-scanner-container
24
+ {
25
+ margin: 15px 15px 15px 15px;
26
+ }
27
+ .wt-scanner-block-scan
28
+ {
29
+ text-align: center;
30
+ }
31
+ .wt-scanner-block-scan table {
32
+ width: 100%;
33
+ }
34
+ .wt-scanner-block-scan table td:first-child {
35
+ width: 20%;
36
+ }
37
+ .wt-scanner-block-scan table td {
38
+ border: 1px solid #efefef;
39
+ background: #fff;
40
+ text-align: center;
41
+ padding: 20px;
42
+ }
43
+ .wt-scanner-block-scan table td h4 {
44
+ font-family: Roboto, "Helvetica Neue", Helvetica, Arial, sans-serif;
45
+ font-size: 16px;
46
+ }
47
+ /*----------------*/
includes/scanner/assets/js/Chart.min.js ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
1
+ /*!
2
+ * Chart.js
3
+ * http://chartjs.org/
4
+ * Version: 2.7.2
5
+ *
6
+ * Copyright 2018 Chart.js Contributors
7
+ * Released under the MIT license
8
+ * https://github.com/chartjs/Chart.js/blob/master/LICENSE.md
9
+ */
10
+ !function(t){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=t();else if("function"==typeof define&&define.amd)define([],t);else{("undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:this).Chart=t()}}(function(){return function t(e,i,n){function a(r,s){if(!i[r]){if(!e[r]){var l="function"==typeof require&&require;if(!s&&l)return l(r,!0);if(o)return o(r,!0);var u=new Error("Cannot find module '"+r+"'");throw u.code="MODULE_NOT_FOUND",u}var d=i[r]={exports:{}};e[r][0].call(d.exports,function(t){var i=e[r][1][t];return a(i||t)},d,d.exports,t,e,i,n)}return i[r].exports}for(var o="function"==typeof require&&require,r=0;r<n.length;r++)a(n[r]);return a}({1:[function(t,e,i){},{}],2:[function(t,e,i){var n=t(6);function a(t){if(t){var e=[0,0,0],i=1,a=t.match(/^#([a-fA-F0-9]{3})$/i);if(a){a=a[1];for(var o=0;o<e.length;o++)e[o]=parseInt(a[o]+a[o],16)}else if(a=t.match(/^#([a-fA-F0-9]{6})$/i)){a=a[1];for(o=0;o<e.length;o++)e[o]=parseInt(a.slice(2*o,2*o+2),16)}else if(a=t.match(/^rgba?\(\s*([+-]?\d+)\s*,\s*([+-]?\d+)\s*,\s*([+-]?\d+)\s*(?:,\s*([+-]?[\d\.]+)\s*)?\)$/i)){for(o=0;o<e.length;o++)e[o]=parseInt(a[o+1]);i=parseFloat(a[4])}else if(a=t.match(/^rgba?\(\s*([+-]?[\d\.]+)\%\s*,\s*([+-]?[\d\.]+)\%\s*,\s*([+-]?[\d\.]+)\%\s*(?:,\s*([+-]?[\d\.]+)\s*)?\)$/i)){for(o=0;o<e.length;o++)e[o]=Math.round(2.55*parseFloat(a[o+1]));i=parseFloat(a[4])}else if(a=t.match(/(\w+)/)){if("transparent"==a[1])return[0,0,0,0];if(!(e=n[a[1]]))return}for(o=0;o<e.length;o++)e[o]=d(e[o],0,255);return i=i||0==i?d(i,0,1):1,e[3]=i,e}}function o(t){if(t){var e=t.match(/^hsla?\(\s*([+-]?\d+)(?:deg)?\s*,\s*([+-]?[\d\.]+)%\s*,\s*([+-]?[\d\.]+)%\s*(?:,\s*([+-]?[\d\.]+)\s*)?\)/);if(e){var i=parseFloat(e[4]);return[d(parseInt(e[1]),0,360),d(parseFloat(e[2]),0,100),d(parseFloat(e[3]),0,100),d(isNaN(i)?1:i,0,1)]}}}function r(t){if(t){var e=t.match(/^hwb\(\s*([+-]?\d+)(?:deg)?\s*,\s*([+-]?[\d\.]+)%\s*,\s*([+-]?[\d\.]+)%\s*(?:,\s*([+-]?[\d\.]+)\s*)?\)/);if(e){var i=parseFloat(e[4]);return[d(parseInt(e[1]),0,360),d(parseFloat(e[2]),0,100),d(parseFloat(e[3]),0,100),d(isNaN(i)?1:i,0,1)]}}}function s(t,e){return void 0===e&&(e=void 0!==t[3]?t[3]:1),"rgba("+t[0]+", "+t[1]+", "+t[2]+", "+e+")"}function l(t,e){return"rgba("+Math.round(t[0]/255*100)+"%, "+Math.round(t[1]/255*100)+"%, "+Math.round(t[2]/255*100)+"%, "+(e||t[3]||1)+")"}function u(t,e){return void 0===e&&(e=void 0!==t[3]?t[3]:1),"hsla("+t[0]+", "+t[1]+"%, "+t[2]+"%, "+e+")"}function d(t,e,i){return Math.min(Math.max(e,t),i)}function c(t){var e=t.toString(16).toUpperCase();return e.length<2?"0"+e:e}e.exports={getRgba:a,getHsla:o,getRgb:function(t){var e=a(t);return e&&e.slice(0,3)},getHsl:function(t){var e=o(t);return e&&e.slice(0,3)},getHwb:r,getAlpha:function(t){var e=a(t);{if(e)return e[3];if(e=o(t))return e[3];if(e=r(t))return e[3]}},hexString:function(t){return"#"+c(t[0])+c(t[1])+c(t[2])},rgbString:function(t,e){if(e<1||t[3]&&t[3]<1)return s(t,e);return"rgb("+t[0]+", "+t[1]+", "+t[2]+")"},rgbaString:s,percentString:function(t,e){if(e<1||t[3]&&t[3]<1)return l(t,e);var i=Math.round(t[0]/255*100),n=Math.round(t[1]/255*100),a=Math.round(t[2]/255*100);return"rgb("+i+"%, "+n+"%, "+a+"%)"},percentaString:l,hslString:function(t,e){if(e<1||t[3]&&t[3]<1)return u(t,e);return"hsl("+t[0]+", "+t[1]+"%, "+t[2]+"%)"},hslaString:u,hwbString:function(t,e){void 0===e&&(e=void 0!==t[3]?t[3]:1);return"hwb("+t[0]+", "+t[1]+"%, "+t[2]+"%"+(void 0!==e&&1!==e?", "+e:"")+")"},keyword:function(t){return h[t.slice(0,3)]}};var h={};for(var f in n)h[n[f]]=f},{6:6}],3:[function(t,e,i){var n=t(5),a=t(2),o=function(t){return t instanceof o?t:this instanceof o?(this.valid=!1,this.values={rgb:[0,0,0],hsl:[0,0,0],hsv:[0,0,0],hwb:[0,0,0],cmyk:[0,0,0,0],alpha:1},void("string"==typeof t?(e=a.getRgba(t))?this.setValues("rgb",e):(e=a.getHsla(t))?this.setValues("hsl",e):(e=a.getHwb(t))&&this.setValues("hwb",e):"object"==typeof t&&(void 0!==(e=t).r||void 0!==e.red?this.setValues("rgb",e):void 0!==e.l||void 0!==e.lightness?this.setValues("hsl",e):void 0!==e.v||void 0!==e.value?this.setValues("hsv",e):void 0!==e.w||void 0!==e.whiteness?this.setValues("hwb",e):void 0===e.c&&void 0===e.cyan||this.setValues("cmyk",e)))):new o(t);var e};o.prototype={isValid:function(){return this.valid},rgb:function(){return this.setSpace("rgb",arguments)},hsl:function(){return this.setSpace("hsl",arguments)},hsv:function(){return this.setSpace("hsv",arguments)},hwb:function(){return this.setSpace("hwb",arguments)},cmyk:function(){return this.setSpace("cmyk",arguments)},rgbArray:function(){return this.values.rgb},hslArray:function(){return this.values.hsl},hsvArray:function(){return this.values.hsv},hwbArray:function(){var t=this.values;return 1!==t.alpha?t.hwb.concat([t.alpha]):t.hwb},cmykArray:function(){return this.values.cmyk},rgbaArray:function(){var t=this.values;return t.rgb.concat([t.alpha])},hslaArray:function(){var t=this.values;return t.hsl.concat([t.alpha])},alpha:function(t){return void 0===t?this.values.alpha:(this.setValues("alpha",t),this)},red:function(t){return this.setChannel("rgb",0,t)},green:function(t){return this.setChannel("rgb",1,t)},blue:function(t){return this.setChannel("rgb",2,t)},hue:function(t){return t&&(t=(t%=360)<0?360+t:t),this.setChannel("hsl",0,t)},saturation:function(t){return this.setChannel("hsl",1,t)},lightness:function(t){return this.setChannel("hsl",2,t)},saturationv:function(t){return this.setChannel("hsv",1,t)},whiteness:function(t){return this.setChannel("hwb",1,t)},blackness:function(t){return this.setChannel("hwb",2,t)},value:function(t){return this.setChannel("hsv",2,t)},cyan:function(t){return this.setChannel("cmyk",0,t)},magenta:function(t){return this.setChannel("cmyk",1,t)},yellow:function(t){return this.setChannel("cmyk",2,t)},black:function(t){return this.setChannel("cmyk",3,t)},hexString:function(){return a.hexString(this.values.rgb)},rgbString:function(){return a.rgbString(this.values.rgb,this.values.alpha)},rgbaString:function(){return a.rgbaString(this.values.rgb,this.values.alpha)},percentString:function(){return a.percentString(this.values.rgb,this.values.alpha)},hslString:function(){return a.hslString(this.values.hsl,this.values.alpha)},hslaString:function(){return a.hslaString(this.values.hsl,this.values.alpha)},hwbString:function(){return a.hwbString(this.values.hwb,this.values.alpha)},keyword:function(){return a.keyword(this.values.rgb,this.values.alpha)},rgbNumber:function(){var t=this.values.rgb;return t[0]<<16|t[1]<<8|t[2]},luminosity:function(){for(var t=this.values.rgb,e=[],i=0;i<t.length;i++){var n=t[i]/255;e[i]=n<=.03928?n/12.92:Math.pow((n+.055)/1.055,2.4)}return.2126*e[0]+.7152*e[1]+.0722*e[2]},contrast:function(t){var e=this.luminosity(),i=t.luminosity();return e>i?(e+.05)/(i+.05):(i+.05)/(e+.05)},level:function(t){var e=this.contrast(t);return e>=7.1?"AAA":e>=4.5?"AA":""},dark:function(){var t=this.values.rgb;return(299*t[0]+587*t[1]+114*t[2])/1e3<128},light:function(){return!this.dark()},negate:function(){for(var t=[],e=0;e<3;e++)t[e]=255-this.values.rgb[e];return this.setValues("rgb",t),this},lighten:function(t){var e=this.values.hsl;return e[2]+=e[2]*t,this.setValues("hsl",e),this},darken:function(t){var e=this.values.hsl;return e[2]-=e[2]*t,this.setValues("hsl",e),this},saturate:function(t){var e=this.values.hsl;return e[1]+=e[1]*t,this.setValues("hsl",e),this},desaturate:function(t){var e=this.values.hsl;return e[1]-=e[1]*t,this.setValues("hsl",e),this},whiten:function(t){var e=this.values.hwb;return e[1]+=e[1]*t,this.setValues("hwb",e),this},blacken:function(t){var e=this.values.hwb;return e[2]+=e[2]*t,this.setValues("hwb",e),this},greyscale:function(){var t=this.values.rgb,e=.3*t[0]+.59*t[1]+.11*t[2];return this.setValues("rgb",[e,e,e]),this},clearer:function(t){var e=this.values.alpha;return this.setValues("alpha",e-e*t),this},opaquer:function(t){var e=this.values.alpha;return this.setValues("alpha",e+e*t),this},rotate:function(t){var e=this.values.hsl,i=(e[0]+t)%360;return e[0]=i<0?360+i:i,this.setValues("hsl",e),this},mix:function(t,e){var i=this,n=t,a=void 0===e?.5:e,o=2*a-1,r=i.alpha()-n.alpha(),s=((o*r==-1?o:(o+r)/(1+o*r))+1)/2,l=1-s;return this.rgb(s*i.red()+l*n.red(),s*i.green()+l*n.green(),s*i.blue()+l*n.blue()).alpha(i.alpha()*a+n.alpha()*(1-a))},toJSON:function(){return this.rgb()},clone:function(){var t,e,i=new o,n=this.values,a=i.values;for(var r in n)n.hasOwnProperty(r)&&(t=n[r],"[object Array]"===(e={}.toString.call(t))?a[r]=t.slice(0):"[object Number]"===e?a[r]=t:console.error("unexpected color value:",t));return i}},o.prototype.spaces={rgb:["red","green","blue"],hsl:["hue","saturation","lightness"],hsv:["hue","saturation","value"],hwb:["hue","whiteness","blackness"],cmyk:["cyan","magenta","yellow","black"]},o.prototype.maxes={rgb:[255,255,255],hsl:[360,100,100],hsv:[360,100,100],hwb:[360,100,100],cmyk:[100,100,100,100]},o.prototype.getValues=function(t){for(var e=this.values,i={},n=0;n<t.length;n++)i[t.charAt(n)]=e[t][n];return 1!==e.alpha&&(i.a=e.alpha),i},o.prototype.setValues=function(t,e){var i,a,o=this.values,r=this.spaces,s=this.maxes,l=1;if(this.valid=!0,"alpha"===t)l=e;else if(e.length)o[t]=e.slice(0,t.length),l=e[t.length];else if(void 0!==e[t.charAt(0)]){for(i=0;i<t.length;i++)o[t][i]=e[t.charAt(i)];l=e.a}else if(void 0!==e[r[t][0]]){var u=r[t];for(i=0;i<t.length;i++)o[t][i]=e[u[i]];l=e.alpha}if(o.alpha=Math.max(0,Math.min(1,void 0===l?o.alpha:l)),"alpha"===t)return!1;for(i=0;i<t.length;i++)a=Math.max(0,Math.min(s[t][i],o[t][i])),o[t][i]=Math.round(a);for(var d in r)d!==t&&(o[d]=n[t][d](o[t]));return!0},o.prototype.setSpace=function(t,e){var i=e[0];return void 0===i?this.getValues(t):("number"==typeof i&&(i=Array.prototype.slice.call(e)),this.setValues(t,i),this)},o.prototype.setChannel=function(t,e,i){var n=this.values[t];return void 0===i?n[e]:i===n[e]?this:(n[e]=i,this.setValues(t,n),this)},"undefined"!=typeof window&&(window.Color=o),e.exports=o},{2:2,5:5}],4:[function(t,e,i){function n(t){var e,i,n=t[0]/255,a=t[1]/255,o=t[2]/255,r=Math.min(n,a,o),s=Math.max(n,a,o),l=s-r;return s==r?e=0:n==s?e=(a-o)/l:a==s?e=2+(o-n)/l:o==s&&(e=4+(n-a)/l),(e=Math.min(60*e,360))<0&&(e+=360),i=(r+s)/2,[e,100*(s==r?0:i<=.5?l/(s+r):l/(2-s-r)),100*i]}function a(t){var e,i,n=t[0],a=t[1],o=t[2],r=Math.min(n,a,o),s=Math.max(n,a,o),l=s-r;return i=0==s?0:l/s*1e3/10,s==r?e=0:n==s?e=(a-o)/l:a==s?e=2+(o-n)/l:o==s&&(e=4+(n-a)/l),(e=Math.min(60*e,360))<0&&(e+=360),[e,i,s/255*1e3/10]}function o(t){var e=t[0],i=t[1],a=t[2];return[n(t)[0],100*(1/255*Math.min(e,Math.min(i,a))),100*(a=1-1/255*Math.max(e,Math.max(i,a)))]}function s(t){var e,i=t[0]/255,n=t[1]/255,a=t[2]/255;return[100*((1-i-(e=Math.min(1-i,1-n,1-a)))/(1-e)||0),100*((1-n-e)/(1-e)||0),100*((1-a-e)/(1-e)||0),100*e]}function l(t){return C[JSON.stringify(t)]}function u(t){var e=t[0]/255,i=t[1]/255,n=t[2]/255;return[100*(.4124*(e=e>.04045?Math.pow((e+.055)/1.055,2.4):e/12.92)+.3576*(i=i>.04045?Math.pow((i+.055)/1.055,2.4):i/12.92)+.1805*(n=n>.04045?Math.pow((n+.055)/1.055,2.4):n/12.92)),100*(.2126*e+.7152*i+.0722*n),100*(.0193*e+.1192*i+.9505*n)]}function d(t){var e=u(t),i=e[0],n=e[1],a=e[2];return n/=100,a/=108.883,i=(i/=95.047)>.008856?Math.pow(i,1/3):7.787*i+16/116,[116*(n=n>.008856?Math.pow(n,1/3):7.787*n+16/116)-16,500*(i-n),200*(n-(a=a>.008856?Math.pow(a,1/3):7.787*a+16/116))]}function c(t){var e,i,n,a,o,r=t[0]/360,s=t[1]/100,l=t[2]/100;if(0==s)return[o=255*l,o,o];e=2*l-(i=l<.5?l*(1+s):l+s-l*s),a=[0,0,0];for(var u=0;u<3;u++)(n=r+1/3*-(u-1))<0&&n++,n>1&&n--,o=6*n<1?e+6*(i-e)*n:2*n<1?i:3*n<2?e+(i-e)*(2/3-n)*6:e,a[u]=255*o;return a}function h(t){var e=t[0]/60,i=t[1]/100,n=t[2]/100,a=Math.floor(e)%6,o=e-Math.floor(e),r=255*n*(1-i),s=255*n*(1-i*o),l=255*n*(1-i*(1-o));n*=255;switch(a){case 0:return[n,l,r];case 1:return[s,n,r];case 2:return[r,n,l];case 3:return[r,s,n];case 4:return[l,r,n];case 5:return[n,r,s]}}function f(t){var e,i,n,a,o=t[0]/360,s=t[1]/100,l=t[2]/100,u=s+l;switch(u>1&&(s/=u,l/=u),n=6*o-(e=Math.floor(6*o)),0!=(1&e)&&(n=1-n),a=s+n*((i=1-l)-s),e){default:case 6:case 0:r=i,g=a,b=s;break;case 1:r=a,g=i,b=s;break;case 2:r=s,g=i,b=a;break;case 3:r=s,g=a,b=i;break;case 4:r=a,g=s,b=i;break;case 5:r=i,g=s,b=a}return[255*r,255*g,255*b]}function p(t){var e=t[0]/100,i=t[1]/100,n=t[2]/100,a=t[3]/100;return[255*(1-Math.min(1,e*(1-a)+a)),255*(1-Math.min(1,i*(1-a)+a)),255*(1-Math.min(1,n*(1-a)+a))]}function m(t){var e,i,n,a=t[0]/100,o=t[1]/100,r=t[2]/100;return i=-.9689*a+1.8758*o+.0415*r,n=.0557*a+-.204*o+1.057*r,e=(e=3.2406*a+-1.5372*o+-.4986*r)>.0031308?1.055*Math.pow(e,1/2.4)-.055:e*=12.92,i=i>.0031308?1.055*Math.pow(i,1/2.4)-.055:i*=12.92,n=n>.0031308?1.055*Math.pow(n,1/2.4)-.055:n*=12.92,[255*(e=Math.min(Math.max(0,e),1)),255*(i=Math.min(Math.max(0,i),1)),255*(n=Math.min(Math.max(0,n),1))]}function v(t){var e=t[0],i=t[1],n=t[2];return i/=100,n/=108.883,e=(e/=95.047)>.008856?Math.pow(e,1/3):7.787*e+16/116,[116*(i=i>.008856?Math.pow(i,1/3):7.787*i+16/116)-16,500*(e-i),200*(i-(n=n>.008856?Math.pow(n,1/3):7.787*n+16/116))]}function x(t){var e,i,n,a,o=t[0],r=t[1],s=t[2];return o<=8?a=(i=100*o/903.3)/100*7.787+16/116:(i=100*Math.pow((o+16)/116,3),a=Math.pow(i/100,1/3)),[e=e/95.047<=.008856?e=95.047*(r/500+a-16/116)/7.787:95.047*Math.pow(r/500+a,3),i,n=n/108.883<=.008859?n=108.883*(a-s/200-16/116)/7.787:108.883*Math.pow(a-s/200,3)]}function y(t){var e,i=t[0],n=t[1],a=t[2];return(e=360*Math.atan2(a,n)/2/Math.PI)<0&&(e+=360),[i,Math.sqrt(n*n+a*a),e]}function k(t){return m(x(t))}function M(t){var e,i=t[0],n=t[1];return e=t[2]/360*2*Math.PI,[i,n*Math.cos(e),n*Math.sin(e)]}function w(t){return S[t]}e.exports={rgb2hsl:n,rgb2hsv:a,rgb2hwb:o,rgb2cmyk:s,rgb2keyword:l,rgb2xyz:u,rgb2lab:d,rgb2lch:function(t){return y(d(t))},hsl2rgb:c,hsl2hsv:function(t){var e=t[0],i=t[1]/100,n=t[2]/100;if(0===n)return[0,0,0];return[e,100*(2*(i*=(n*=2)<=1?n:2-n)/(n+i)),100*((n+i)/2)]},hsl2hwb:function(t){return o(c(t))},hsl2cmyk:function(t){return s(c(t))},hsl2keyword:function(t){return l(c(t))},hsv2rgb:h,hsv2hsl:function(t){var e,i,n=t[0],a=t[1]/100,o=t[2]/100;return e=a*o,[n,100*(e=(e/=(i=(2-a)*o)<=1?i:2-i)||0),100*(i/=2)]},hsv2hwb:function(t){return o(h(t))},hsv2cmyk:function(t){return s(h(t))},hsv2keyword:function(t){return l(h(t))},hwb2rgb:f,hwb2hsl:function(t){return n(f(t))},hwb2hsv:function(t){return a(f(t))},hwb2cmyk:function(t){return s(f(t))},hwb2keyword:function(t){return l(f(t))},cmyk2rgb:p,cmyk2hsl:function(t){return n(p(t))},cmyk2hsv:function(t){return a(p(t))},cmyk2hwb:function(t){return o(p(t))},cmyk2keyword:function(t){return l(p(t))},keyword2rgb:w,keyword2hsl:function(t){return n(w(t))},keyword2hsv:function(t){return a(w(t))},keyword2hwb:function(t){return o(w(t))},keyword2cmyk:function(t){return s(w(t))},keyword2lab:function(t){return d(w(t))},keyword2xyz:function(t){return u(w(t))},xyz2rgb:m,xyz2lab:v,xyz2lch:function(t){return y(v(t))},lab2xyz:x,lab2rgb:k,lab2lch:y,lch2lab:M,lch2xyz:function(t){return x(M(t))},lch2rgb:function(t){return k(M(t))}};var S={aliceblue:[240,248,255],antiquewhite:[250,235,215],aqua:[0,255,255],aquamarine:[127,255,212],azure:[240,255,255],beige:[245,245,220],bisque:[255,228,196],black:[0,0,0],blanchedalmond:[255,235,205],blue:[0,0,255],blueviolet:[138,43,226],brown:[165,42,42],burlywood:[222,184,135],cadetblue:[95,158,160],chartreuse:[127,255,0],chocolate:[210,105,30],coral:[255,127,80],cornflowerblue:[100,149,237],cornsilk:[255,248,220],crimson:[220,20,60],cyan:[0,255,255],darkblue:[0,0,139],darkcyan:[0,139,139],darkgoldenrod:[184,134,11],darkgray:[169,169,169],darkgreen:[0,100,0],darkgrey:[169,169,169],darkkhaki:[189,183,107],darkmagenta:[139,0,139],darkolivegreen:[85,107,47],darkorange:[255,140,0],darkorchid:[153,50,204],darkred:[139,0,0],darksalmon:[233,150,122],darkseagreen:[143,188,143],darkslateblue:[72,61,139],darkslategray:[47,79,79],darkslategrey:[47,79,79],darkturquoise:[0,206,209],darkviolet:[148,0,211],deeppink:[255,20,147],deepskyblue:[0,191,255],dimgray:[105,105,105],dimgrey:[105,105,105],dodgerblue:[30,144,255],firebrick:[178,34,34],floralwhite:[255,250,240],forestgreen:[34,139,34],fuchsia:[255,0,255],gainsboro:[220,220,220],ghostwhite:[248,248,255],gold:[255,215,0],goldenrod:[218,165,32],gray:[128,128,128],green:[0,128,0],greenyellow:[173,255,47],grey:[128,128,128],honeydew:[240,255,240],hotpink:[255,105,180],indianred:[205,92,92],indigo:[75,0,130],ivory:[255,255,240],khaki:[240,230,140],lavender:[230,230,250],lavenderblush:[255,240,245],lawngreen:[124,252,0],lemonchiffon:[255,250,205],lightblue:[173,216,230],lightcoral:[240,128,128],lightcyan:[224,255,255],lightgoldenrodyellow:[250,250,210],lightgray:[211,211,211],lightgreen:[144,238,144],lightgrey:[211,211,211],lightpink:[255,182,193],lightsalmon:[255,160,122],lightseagreen:[32,178,170],lightskyblue:[135,206,250],lightslategray:[119,136,153],lightslategrey:[119,136,153],lightsteelblue:[176,196,222],lightyellow:[255,255,224],lime:[0,255,0],limegreen:[50,205,50],linen:[250,240,230],magenta:[255,0,255],maroon:[128,0,0],mediumaquamarine:[102,205,170],mediumblue:[0,0,205],mediumorchid:[186,85,211],mediumpurple:[147,112,219],mediumseagreen:[60,179,113],mediumslateblue:[123,104,238],mediumspringgreen:[0,250,154],mediumturquoise:[72,209,204],mediumvioletred:[199,21,133],midnightblue:[25,25,112],mintcream:[245,255,250],mistyrose:[255,228,225],moccasin:[255,228,181],navajowhite:[255,222,173],navy:[0,0,128],oldlace:[253,245,230],olive:[128,128,0],olivedrab:[107,142,35],orange:[255,165,0],orangered:[255,69,0],orchid:[218,112,214],palegoldenrod:[238,232,170],palegreen:[152,251,152],paleturquoise:[175,238,238],palevioletred:[219,112,147],papayawhip:[255,239,213],peachpuff:[255,218,185],peru:[205,133,63],pink:[255,192,203],plum:[221,160,221],powderblue:[176,224,230],purple:[128,0,128],rebeccapurple:[102,51,153],red:[255,0,0],rosybrown:[188,143,143],royalblue:[65,105,225],saddlebrown:[139,69,19],salmon:[250,128,114],sandybrown:[244,164,96],seagreen:[46,139,87],seashell:[255,245,238],sienna:[160,82,45],silver:[192,192,192],skyblue:[135,206,235],slateblue:[106,90,205],slategray:[112,128,144],slategrey:[112,128,144],snow:[255,250,250],springgreen:[0,255,127],steelblue:[70,130,180],tan:[210,180,140],teal:[0,128,128],thistle:[216,191,216],tomato:[255,99,71],turquoise:[64,224,208],violet:[238,130,238],wheat:[245,222,179],white:[255,255,255],whitesmoke:[245,245,245],yellow:[255,255,0],yellowgreen:[154,205,50]},C={};for(var _ in S)C[JSON.stringify(S[_])]=_},{}],5:[function(t,e,i){var n=t(4),a=function(){return new u};for(var o in n){a[o+"Raw"]=function(t){return function(e){return"number"==typeof e&&(e=Array.prototype.slice.call(arguments)),n[t](e)}}(o);var r=/(\w+)2(\w+)/.exec(o),s=r[1],l=r[2];(a[s]=a[s]||{})[l]=a[o]=function(t){return function(e){"number"==typeof e&&(e=Array.prototype.slice.call(arguments));var i=n[t](e);if("string"==typeof i||void 0===i)return i;for(var a=0;a<i.length;a++)i[a]=Math.round(i[a]);return i}}(o)}var u=function(){this.convs={}};u.prototype.routeSpace=function(t,e){var i=e[0];return void 0===i?this.getValues(t):("number"==typeof i&&(i=Array.prototype.slice.call(e)),this.setValues(t,i))},u.prototype.setValues=function(t,e){return this.space=t,this.convs={},this.convs[t]=e,this},u.prototype.getValues=function(t){var e=this.convs[t];if(!e){var i=this.space,n=this.convs[i];e=a[i][t](n),this.convs[t]=e}return e},["rgb","hsl","hsv","cmyk","keyword"].forEach(function(t){u.prototype[t]=function(e){return this.routeSpace(t,arguments)}}),e.exports=a},{4:4}],6:[function(t,e,i){"use strict";e.exports={aliceblue:[240,248,255],antiquewhite:[250,235,215],aqua:[0,255,255],aquamarine:[127,255,212],azure:[240,255,255],beige:[245,245,220],bisque:[255,228,196],black:[0,0,0],blanchedalmond:[255,235,205],blue:[0,0,255],blueviolet:[138,43,226],brown:[165,42,42],burlywood:[222,184,135],cadetblue:[95,158,160],chartreuse:[127,255,0],chocolate:[210,105,30],coral:[255,127,80],cornflowerblue:[100,149,237],cornsilk:[255,248,220],crimson:[220,20,60],cyan:[0,255,255],darkblue:[0,0,139],darkcyan:[0,139,139],darkgoldenrod:[184,134,11],darkgray:[169,169,169],darkgreen:[0,100,0],darkgrey:[169,169,169],darkkhaki:[189,183,107],darkmagenta:[139,0,139],darkolivegreen:[85,107,47],darkorange:[255,140,0],darkorchid:[153,50,204],darkred:[139,0,0],darksalmon:[233,150,122],darkseagreen:[143,188,143],darkslateblue:[72,61,139],darkslategray:[47,79,79],darkslategrey:[47,79,79],darkturquoise:[0,206,209],darkviolet:[148,0,211],deeppink:[255,20,147],deepskyblue:[0,191,255],dimgray:[105,105,105],dimgrey:[105,105,105],dodgerblue:[30,144,255],firebrick:[178,34,34],floralwhite:[255,250,240],forestgreen:[34,139,34],fuchsia:[255,0,255],gainsboro:[220,220,220],ghostwhite:[248,248,255],gold:[255,215,0],goldenrod:[218,165,32],gray:[128,128,128],green:[0,128,0],greenyellow:[173,255,47],grey:[128,128,128],honeydew:[240,255,240],hotpink:[255,105,180],indianred:[205,92,92],indigo:[75,0,130],ivory:[255,255,240],khaki:[240,230,140],lavender:[230,230,250],lavenderblush:[255,240,245],lawngreen:[124,252,0],lemonchiffon:[255,250,205],lightblue:[173,216,230],lightcoral:[240,128,128],lightcyan:[224,255,255],lightgoldenrodyellow:[250,250,210],lightgray:[211,211,211],lightgreen:[144,238,144],lightgrey:[211,211,211],lightpink:[255,182,193],lightsalmon:[255,160,122],lightseagreen:[32,178,170],lightskyblue:[135,206,250],lightslategray:[119,136,153],lightslategrey:[119,136,153],lightsteelblue:[176,196,222],lightyellow:[255,255,224],lime:[0,255,0],limegreen:[50,205,50],linen:[250,240,230],magenta:[255,0,255],maroon:[128,0,0],mediumaquamarine:[102,205,170],mediumblue:[0,0,205],mediumorchid:[186,85,211],mediumpurple:[147,112,219],mediumseagreen:[60,179,113],mediumslateblue:[123,104,238],mediumspringgreen:[0,250,154],mediumturquoise:[72,209,204],mediumvioletred:[199,21,133],midnightblue:[25,25,112],mintcream:[245,255,250],mistyrose:[255,228,225],moccasin:[255,228,181],navajowhite:[255,222,173],navy:[0,0,128],oldlace:[253,245,230],olive:[128,128,0],olivedrab:[107,142,35],orange:[255,165,0],orangered:[255,69,0],orchid:[218,112,214],palegoldenrod:[238,232,170],palegreen:[152,251,152],paleturquoise:[175,238,238],palevioletred:[219,112,147],papayawhip:[255,239,213],peachpuff:[255,218,185],peru:[205,133,63],pink:[255,192,203],plum:[221,160,221],powderblue:[176,224,230],purple:[128,0,128],rebeccapurple:[102,51,153],red:[255,0,0],rosybrown:[188,143,143],royalblue:[65,105,225],saddlebrown:[139,69,19],salmon:[250,128,114],sandybrown:[244,164,96],seagreen:[46,139,87],seashell:[255,245,238],sienna:[160,82,45],silver:[192,192,192],skyblue:[135,206,235],slateblue:[106,90,205],slategray:[112,128,144],slategrey:[112,128,144],snow:[255,250,250],springgreen:[0,255,127],steelblue:[70,130,180],tan:[210,180,140],teal:[0,128,128],thistle:[216,191,216],tomato:[255,99,71],turquoise:[64,224,208],violet:[238,130,238],wheat:[245,222,179],white:[255,255,255],whitesmoke:[245,245,245],yellow:[255,255,0],yellowgreen:[154,205,50]}},{}],7:[function(t,e,i){var n=t(29)();n.helpers=t(45),t(27)(n),n.defaults=t(25),n.Element=t(26),n.elements=t(40),n.Interaction=t(28),n.layouts=t(30),n.platform=t(48),n.plugins=t(31),n.Ticks=t(34),t(22)(n),t(23)(n),t(24)(n),t(33)(n),t(32)(n),t(35)(n),t(55)(n),t(53)(n),t(54)(n),t(56)(n),t(57)(n),t(58)(n),t(15)(n),t(16)(n),t(17)(n),t(18)(n),t(19)(n),t(20)(n),t(21)(n),t(8)(n),t(9)(n),t(10)(n),t(11)(n),t(12)(n),t(13)(n),t(14)(n);var a=t(49);for(var o in a)a.hasOwnProperty(o)&&n.plugins.register(a[o]);n.platform.initialize(),e.exports=n,"undefined"!=typeof window&&(window.Chart=n),n.Legend=a.legend._element,n.Title=a.title._element,n.pluginService=n.plugins,n.PluginBase=n.Element.extend({}),n.canvasHelpers=n.helpers.canvas,n.layoutService=n.layouts},{10:10,11:11,12:12,13:13,14:14,15:15,16:16,17:17,18:18,19:19,20:20,21:21,22:22,23:23,24:24,25:25,26:26,27:27,28:28,29:29,30:30,31:31,32:32,33:33,34:34,35:35,40:40,45:45,48:48,49:49,53:53,54:54,55:55,56:56,57:57,58:58,8:8,9:9}],8:[function(t,e,i){"use strict";e.exports=function(t){t.Bar=function(e,i){return i.type="bar",new t(e,i)}}},{}],9:[function(t,e,i){"use strict";e.exports=function(t){t.Bubble=function(e,i){return i.type="bubble",new t(e,i)}}},{}],10:[function(t,e,i){"use strict";e.exports=function(t){t.Doughnut=function(e,i){return i.type="doughnut",new t(e,i)}}},{}],11:[function(t,e,i){"use strict";e.exports=function(t){t.Line=function(e,i){return i.type="line",new t(e,i)}}},{}],12:[function(t,e,i){"use strict";e.exports=function(t){t.PolarArea=function(e,i){return i.type="polarArea",new t(e,i)}}},{}],13:[function(t,e,i){"use strict";e.exports=function(t){t.Radar=function(e,i){return i.type="radar",new t(e,i)}}},{}],14:[function(t,e,i){"use strict";e.exports=function(t){t.Scatter=function(e,i){return i.type="scatter",new t(e,i)}}},{}],15:[function(t,e,i){"use strict";var n=t(25),a=t(40),o=t(45);n._set("bar",{hover:{mode:"label"},scales:{xAxes:[{type:"category",categoryPercentage:.8,barPercentage:.9,offset:!0,gridLines:{offsetGridLines:!0}}],yAxes:[{type:"linear"}]}}),n._set("horizontalBar",{hover:{mode:"index",axis:"y"},scales:{xAxes:[{type:"linear",position:"bottom"}],yAxes:[{position:"left",type:"category",categoryPercentage:.8,barPercentage:.9,offset:!0,gridLines:{offsetGridLines:!0}}]},elements:{rectangle:{borderSkipped:"left"}},tooltips:{callbacks:{title:function(t,e){var i="";return t.length>0&&(t[0].yLabel?i=t[0].yLabel:e.labels.length>0&&t[0].index<e.labels.length&&(i=e.labels[t[0].index])),i},label:function(t,e){return(e.datasets[t.datasetIndex].label||"")+": "+t.xLabel}},mode:"index",axis:"y"}}),e.exports=function(t){t.controllers.bar=t.DatasetController.extend({dataElementType:a.Rectangle,initialize:function(){var e;t.DatasetController.prototype.initialize.apply(this,arguments),(e=this.getMeta()).stack=this.getDataset().stack,e.bar=!0},update:function(t){var e,i,n=this.getMeta().data;for(this._ruler=this.getRuler(),e=0,i=n.length;e<i;++e)this.updateElement(n[e],e,t)},updateElement:function(t,e,i){var n=this,a=n.chart,r=n.getMeta(),s=n.getDataset(),l=t.custom||{},u=a.options.elements.rectangle;t._xScale=n.getScaleForId(r.xAxisID),t._yScale=n.getScaleForId(r.yAxisID),t._datasetIndex=n.index,t._index=e,t._model={datasetLabel:s.label,label:a.data.labels[e],borderSkipped:l.borderSkipped?l.borderSkipped:u.borderSkipped,backgroundColor:l.backgroundColor?l.backgroundColor:o.valueAtIndexOrDefault(s.backgroundColor,e,u.backgroundColor),borderColor:l.borderColor?l.borderColor:o.valueAtIndexOrDefault(s.borderColor,e,u.borderColor),borderWidth:l.borderWidth?l.borderWidth:o.valueAtIndexOrDefault(s.borderWidth,e,u.borderWidth)},n.updateElementGeometry(t,e,i),t.pivot()},updateElementGeometry:function(t,e,i){var n=this,a=t._model,o=n.getValueScale(),r=o.getBasePixel(),s=o.isHorizontal(),l=n._ruler||n.getRuler(),u=n.calculateBarValuePixels(n.index,e),d=n.calculateBarIndexPixels(n.index,e,l);a.horizontal=s,a.base=i?r:u.base,a.x=s?i?r:u.head:d.center,a.y=s?d.center:i?r:u.head,a.height=s?d.size:void 0,a.width=s?void 0:d.size},getValueScaleId:function(){return this.getMeta().yAxisID},getIndexScaleId:function(){return this.getMeta().xAxisID},getValueScale:function(){return this.getScaleForId(this.getValueScaleId())},getIndexScale:function(){return this.getScaleForId(this.getIndexScaleId())},_getStacks:function(t){var e,i,n=this.chart,a=this.getIndexScale().options.stacked,o=void 0===t?n.data.datasets.length:t+1,r=[];for(e=0;e<o;++e)(i=n.getDatasetMeta(e)).bar&&n.isDatasetVisible(e)&&(!1===a||!0===a&&-1===r.indexOf(i.stack)||void 0===a&&(void 0===i.stack||-1===r.indexOf(i.stack)))&&r.push(i.stack);return r},getStackCount:function(){return this._getStacks().length},getStackIndex:function(t,e){var i=this._getStacks(t),n=void 0!==e?i.indexOf(e):-1;return-1===n?i.length-1:n},getRuler:function(){var t,e,i=this.getIndexScale(),n=this.getStackCount(),a=this.index,r=i.isHorizontal(),s=r?i.left:i.top,l=s+(r?i.width:i.height),u=[];for(t=0,e=this.getMeta().data.length;t<e;++t)u.push(i.getPixelForValue(null,t,a));return{min:o.isNullOrUndef(i.options.barThickness)?function(t,e){var i,n,a,o,r=t.isHorizontal()?t.width:t.height,s=t.getTicks();for(a=1,o=e.length;a<o;++a)r=Math.min(r,e[a]-e[a-1]);for(a=0,o=s.length;a<o;++a)n=t.getPixelForTick(a),r=a>0?Math.min(r,n-i):r,i=n;return r}(i,u):-1,pixels:u,start:s,end:l,stackCount:n,scale:i}},calculateBarValuePixels:function(t,e){var i,n,a,o,r,s,l=this.chart,u=this.getMeta(),d=this.getValueScale(),c=l.data.datasets,h=d.getRightValue(c[t].data[e]),f=d.options.stacked,g=u.stack,p=0;if(f||void 0===f&&void 0!==g)for(i=0;i<t;++i)(n=l.getDatasetMeta(i)).bar&&n.stack===g&&n.controller.getValueScaleId()===d.id&&l.isDatasetVisible(i)&&(a=d.getRightValue(c[i].data[e]),(h<0&&a<0||h>=0&&a>0)&&(p+=a));return o=d.getPixelForValue(p),{size:s=((r=d.getPixelForValue(p+h))-o)/2,base:o,head:r,center:r+s/2}},calculateBarIndexPixels:function(t,e,i){var n,a,r,s,l,u,d,c,h,f,g,p,m,v,b,x,y,k=i.scale.options,M="flex"===k.barThickness?(h=e,g=k,m=(f=i).pixels,v=m[h],b=h>0?m[h-1]:null,x=h<m.length-1?m[h+1]:null,y=g.categoryPercentage,null===b&&(b=v-(null===x?f.end-v:x-v)),null===x&&(x=v+v-b),p=v-(v-b)/2*y,{chunk:(x-b)/2*y/f.stackCount,ratio:g.barPercentage,start:p}):(n=e,a=i,u=(r=k).barThickness,d=a.stackCount,c=a.pixels[n],o.isNullOrUndef(u)?(s=a.min*r.categoryPercentage,l=r.barPercentage):(s=u*d,l=1),{chunk:s/d,ratio:l,start:c-s/2}),w=this.getStackIndex(t,this.getMeta().stack),S=M.start+M.chunk*w+M.chunk/2,C=Math.min(o.valueOrDefault(k.maxBarThickness,1/0),M.chunk*M.ratio);return{base:S-C/2,head:S+C/2,center:S,size:C}},draw:function(){var t=this.chart,e=this.getValueScale(),i=this.getMeta().data,n=this.getDataset(),a=i.length,r=0;for(o.canvas.clipArea(t.ctx,t.chartArea);r<a;++r)isNaN(e.getRightValue(n.data[r]))||i[r].draw();o.canvas.unclipArea(t.ctx)},setHoverStyle:function(t){var e=this.chart.data.datasets[t._datasetIndex],i=t._index,n=t.custom||{},a=t._model;a.backgroundColor=n.hoverBackgroundColor?n.hoverBackgroundColor:o.valueAtIndexOrDefault(e.hoverBackgroundColor,i,o.getHoverColor(a.backgroundColor)),a.borderColor=n.hoverBorderColor?n.hoverBorderColor:o.valueAtIndexOrDefault(e.hoverBorderColor,i,o.getHoverColor(a.borderColor)),a.borderWidth=n.hoverBorderWidth?n.hoverBorderWidth:o.valueAtIndexOrDefault(e.hoverBorderWidth,i,a.borderWidth)},removeHoverStyle:function(t){var e=this.chart.data.datasets[t._datasetIndex],i=t._index,n=t.custom||{},a=t._model,r=this.chart.options.elements.rectangle;a.backgroundColor=n.backgroundColor?n.backgroundColor:o.valueAtIndexOrDefault(e.backgroundColor,i,r.backgroundColor),a.borderColor=n.borderColor?n.borderColor:o.valueAtIndexOrDefault(e.borderColor,i,r.borderColor),a.borderWidth=n.borderWidth?n.borderWidth:o.valueAtIndexOrDefault(e.borderWidth,i,r.borderWidth)}}),t.controllers.horizontalBar=t.controllers.bar.extend({getValueScaleId:function(){return this.getMeta().xAxisID},getIndexScaleId:function(){return this.getMeta().yAxisID}})}},{25:25,40:40,45:45}],16:[function(t,e,i){"use strict";var n=t(25),a=t(40),o=t(45);n._set("bubble",{hover:{mode:"single"},scales:{xAxes:[{type:"linear",position:"bottom",id:"x-axis-0"}],yAxes:[{type:"linear",position:"left",id:"y-axis-0"}]},tooltips:{callbacks:{title:function(){return""},label:function(t,e){var i=e.datasets[t.datasetIndex].label||"",n=e.datasets[t.datasetIndex].data[t.index];return i+": ("+t.xLabel+", "+t.yLabel+", "+n.r+")"}}}}),e.exports=function(t){t.controllers.bubble=t.DatasetController.extend({dataElementType:a.Point,update:function(t){var e=this,i=e.getMeta().data;o.each(i,function(i,n){e.updateElement(i,n,t)})},updateElement:function(t,e,i){var n=this,a=n.getMeta(),o=t.custom||{},r=n.getScaleForId(a.xAxisID),s=n.getScaleForId(a.yAxisID),l=n._resolveElementOptions(t,e),u=n.getDataset().data[e],d=n.index,c=i?r.getPixelForDecimal(.5):r.getPixelForValue("object"==typeof u?u:NaN,e,d),h=i?s.getBasePixel():s.getPixelForValue(u,e,d);t._xScale=r,t._yScale=s,t._options=l,t._datasetIndex=d,t._index=e,t._model={backgroundColor:l.backgroundColor,borderColor:l.borderColor,borderWidth:l.borderWidth,hitRadius:l.hitRadius,pointStyle:l.pointStyle,radius:i?0:l.radius,skip:o.skip||isNaN(c)||isNaN(h),x:c,y:h},t.pivot()},setHoverStyle:function(t){var e=t._model,i=t._options;e.backgroundColor=o.valueOrDefault(i.hoverBackgroundColor,o.getHoverColor(i.backgroundColor)),e.borderColor=o.valueOrDefault(i.hoverBorderColor,o.getHoverColor(i.borderColor)),e.borderWidth=o.valueOrDefault(i.hoverBorderWidth,i.borderWidth),e.radius=i.radius+i.hoverRadius},removeHoverStyle:function(t){var e=t._model,i=t._options;e.backgroundColor=i.backgroundColor,e.borderColor=i.borderColor,e.borderWidth=i.borderWidth,e.radius=i.radius},_resolveElementOptions:function(t,e){var i,n,a,r=this.chart,s=r.data.datasets[this.index],l=t.custom||{},u=r.options.elements.point,d=o.options.resolve,c=s.data[e],h={},f={chart:r,dataIndex:e,dataset:s,datasetIndex:this.index},g=["backgroundColor","borderColor","borderWidth","hoverBackgroundColor","hoverBorderColor","hoverBorderWidth","hoverRadius","hitRadius","pointStyle"];for(i=0,n=g.length;i<n;++i)h[a=g[i]]=d([l[a],s[a],u[a]],f,e);return h.radius=d([l.radius,c?c.r:void 0,s.radius,u.radius],f,e),h}})}},{25:25,40:40,45:45}],17:[function(t,e,i){"use strict";var n=t(25),a=t(40),o=t(45);n._set("doughnut",{animation:{animateRotate:!0,animateScale:!1},hover:{mode:"single"},legendCallback:function(t){var e=[];e.push('<ul class="'+t.id+'-legend">');var i=t.data,n=i.datasets,a=i.labels;if(n.length)for(var o=0;o<n[0].data.length;++o)e.push('<li><span style="background-color:'+n[0].backgroundColor[o]+'"></span>'),a[o]&&e.push(a[o]),e.push("</li>");return e.push("</ul>"),e.join("")},legend:{labels:{generateLabels:function(t){var e=t.data;return e.labels.length&&e.datasets.length?e.labels.map(function(i,n){var a=t.getDatasetMeta(0),r=e.datasets[0],s=a.data[n],l=s&&s.custom||{},u=o.valueAtIndexOrDefault,d=t.options.elements.arc;return{text:i,fillStyle:l.backgroundColor?l.backgroundColor:u(r.backgroundColor,n,d.backgroundColor),strokeStyle:l.borderColor?l.borderColor:u(r.borderColor,n,d.borderColor),lineWidth:l.borderWidth?l.borderWidth:u(r.borderWidth,n,d.borderWidth),hidden:isNaN(r.data[n])||a.data[n].hidden,index:n}}):[]}},onClick:function(t,e){var i,n,a,o=e.index,r=this.chart;for(i=0,n=(r.data.datasets||[]).length;i<n;++i)(a=r.getDatasetMeta(i)).data[o]&&(a.data[o].hidden=!a.data[o].hidden);r.update()}},cutoutPercentage:50,rotation:-.5*Math.PI,circumference:2*Math.PI,tooltips:{callbacks:{title:function(){return""},label:function(t,e){var i=e.labels[t.index],n=": "+e.datasets[t.datasetIndex].data[t.index];return o.isArray(i)?(i=i.slice())[0]+=n:i+=n,i}}}}),n._set("pie",o.clone(n.doughnut)),n._set("pie",{cutoutPercentage:0}),e.exports=function(t){t.controllers.doughnut=t.controllers.pie=t.DatasetController.extend({dataElementType:a.Arc,linkScales:o.noop,getRingIndex:function(t){for(var e=0,i=0;i<t;++i)this.chart.isDatasetVisible(i)&&++e;return e},update:function(t){var e=this,i=e.chart,n=i.chartArea,a=i.options,r=a.elements.arc,s=n.right-n.left-r.borderWidth,l=n.bottom-n.top-r.borderWidth,u=Math.min(s,l),d={x:0,y:0},c=e.getMeta(),h=a.cutoutPercentage,f=a.circumference;if(f<2*Math.PI){var g=a.rotation%(2*Math.PI),p=(g+=2*Math.PI*(g>=Math.PI?-1:g<-Math.PI?1:0))+f,m=Math.cos(g),v=Math.sin(g),b=Math.cos(p),x=Math.sin(p),y=g<=0&&p>=0||g<=2*Math.PI&&2*Math.PI<=p,k=g<=.5*Math.PI&&.5*Math.PI<=p||g<=2.5*Math.PI&&2.5*Math.PI<=p,M=g<=-Math.PI&&-Math.PI<=p||g<=Math.PI&&Math.PI<=p,w=g<=.5*-Math.PI&&.5*-Math.PI<=p||g<=1.5*Math.PI&&1.5*Math.PI<=p,S=h/100,C=M?-1:Math.min(m*(m<0?1:S),b*(b<0?1:S)),_=w?-1:Math.min(v*(v<0?1:S),x*(x<0?1:S)),D=y?1:Math.max(m*(m>0?1:S),b*(b>0?1:S)),I=k?1:Math.max(v*(v>0?1:S),x*(x>0?1:S)),P=.5*(D-C),A=.5*(I-_);u=Math.min(s/P,l/A),d={x:-.5*(D+C),y:-.5*(I+_)}}i.borderWidth=e.getMaxBorderWidth(c.data),i.outerRadius=Math.max((u-i.borderWidth)/2,0),i.innerRadius=Math.max(h?i.outerRadius/100*h:0,0),i.radiusLength=(i.outerRadius-i.innerRadius)/i.getVisibleDatasetCount(),i.offsetX=d.x*i.outerRadius,i.offsetY=d.y*i.outerRadius,c.total=e.calculateTotal(),e.outerRadius=i.outerRadius-i.radiusLength*e.getRingIndex(e.index),e.innerRadius=Math.max(e.outerRadius-i.radiusLength,0),o.each(c.data,function(i,n){e.updateElement(i,n,t)})},updateElement:function(t,e,i){var n=this,a=n.chart,r=a.chartArea,s=a.options,l=s.animation,u=(r.left+r.right)/2,d=(r.top+r.bottom)/2,c=s.rotation,h=s.rotation,f=n.getDataset(),g=i&&l.animateRotate?0:t.hidden?0:n.calculateCircumference(f.data[e])*(s.circumference/(2*Math.PI)),p=i&&l.animateScale?0:n.innerRadius,m=i&&l.animateScale?0:n.outerRadius,v=o.valueAtIndexOrDefault;o.extend(t,{_datasetIndex:n.index,_index:e,_model:{x:u+a.offsetX,y:d+a.offsetY,startAngle:c,endAngle:h,circumference:g,outerRadius:m,innerRadius:p,label:v(f.label,e,a.data.labels[e])}});var b=t._model;this.removeHoverStyle(t),i&&l.animateRotate||(b.startAngle=0===e?s.rotation:n.getMeta().data[e-1]._model.endAngle,b.endAngle=b.startAngle+b.circumference),t.pivot()},removeHoverStyle:function(e){t.DatasetController.prototype.removeHoverStyle.call(this,e,this.chart.options.elements.arc)},calculateTotal:function(){var t,e=this.getDataset(),i=this.getMeta(),n=0;return o.each(i.data,function(i,a){t=e.data[a],isNaN(t)||i.hidden||(n+=Math.abs(t))}),n},calculateCircumference:function(t){var e=this.getMeta().total;return e>0&&!isNaN(t)?2*Math.PI*(Math.abs(t)/e):0},getMaxBorderWidth:function(t){for(var e,i,n=0,a=this.index,o=t.length,r=0;r<o;r++)e=t[r]._model?t[r]._model.borderWidth:0,n=(i=t[r]._chart?t[r]._chart.config.data.datasets[a].hoverBorderWidth:0)>(n=e>n?e:n)?i:n;return n}})}},{25:25,40:40,45:45}],18:[function(t,e,i){"use strict";var n=t(25),a=t(40),o=t(45);n._set("line",{showLines:!0,spanGaps:!1,hover:{mode:"label"},scales:{xAxes:[{type:"category",id:"x-axis-0"}],yAxes:[{type:"linear",id:"y-axis-0"}]}}),e.exports=function(t){function e(t,e){return o.valueOrDefault(t.showLine,e.showLines)}t.controllers.line=t.DatasetController.extend({datasetElementType:a.Line,dataElementType:a.Point,update:function(t){var i,n,a,r=this,s=r.getMeta(),l=s.dataset,u=s.data||[],d=r.chart.options,c=d.elements.line,h=r.getScaleForId(s.yAxisID),f=r.getDataset(),g=e(f,d);for(g&&(a=l.custom||{},void 0!==f.tension&&void 0===f.lineTension&&(f.lineTension=f.tension),l._scale=h,l._datasetIndex=r.index,l._children=u,l._model={spanGaps:f.spanGaps?f.spanGaps:d.spanGaps,tension:a.tension?a.tension:o.valueOrDefault(f.lineTension,c.tension),backgroundColor:a.backgroundColor?a.backgroundColor:f.backgroundColor||c.backgroundColor,borderWidth:a.borderWidth?a.borderWidth:f.borderWidth||c.borderWidth,borderColor:a.borderColor?a.borderColor:f.borderColor||c.borderColor,borderCapStyle:a.borderCapStyle?a.borderCapStyle:f.borderCapStyle||c.borderCapStyle,borderDash:a.borderDash?a.borderDash:f.borderDash||c.borderDash,borderDashOffset:a.borderDashOffset?a.borderDashOffset:f.borderDashOffset||c.borderDashOffset,borderJoinStyle:a.borderJoinStyle?a.borderJoinStyle:f.borderJoinStyle||c.borderJoinStyle,fill:a.fill?a.fill:void 0!==f.fill?f.fill:c.fill,steppedLine:a.steppedLine?a.steppedLine:o.valueOrDefault(f.steppedLine,c.stepped),cubicInterpolationMode:a.cubicInterpolationMode?a.cubicInterpolationMode:o.valueOrDefault(f.cubicInterpolationMode,c.cubicInterpolationMode)},l.pivot()),i=0,n=u.length;i<n;++i)r.updateElement(u[i],i,t);for(g&&0!==l._model.tension&&r.updateBezierControlPoints(),i=0,n=u.length;i<n;++i)u[i].pivot()},getPointBackgroundColor:function(t,e){var i=this.chart.options.elements.point.backgroundColor,n=this.getDataset(),a=t.custom||{};return a.backgroundColor?i=a.backgroundColor:n.pointBackgroundColor?i=o.valueAtIndexOrDefault(n.pointBackgroundColor,e,i):n.backgroundColor&&(i=n.backgroundColor),i},getPointBorderColor:function(t,e){var i=this.chart.options.elements.point.borderColor,n=this.getDataset(),a=t.custom||{};return a.borderColor?i=a.borderColor:n.pointBorderColor?i=o.valueAtIndexOrDefault(n.pointBorderColor,e,i):n.borderColor&&(i=n.borderColor),i},getPointBorderWidth:function(t,e){var i=this.chart.options.elements.point.borderWidth,n=this.getDataset(),a=t.custom||{};return isNaN(a.borderWidth)?!isNaN(n.pointBorderWidth)||o.isArray(n.pointBorderWidth)?i=o.valueAtIndexOrDefault(n.pointBorderWidth,e,i):isNaN(n.borderWidth)||(i=n.borderWidth):i=a.borderWidth,i},updateElement:function(t,e,i){var n,a,r=this,s=r.getMeta(),l=t.custom||{},u=r.getDataset(),d=r.index,c=u.data[e],h=r.getScaleForId(s.yAxisID),f=r.getScaleForId(s.xAxisID),g=r.chart.options.elements.point;void 0!==u.radius&&void 0===u.pointRadius&&(u.pointRadius=u.radius),void 0!==u.hitRadius&&void 0===u.pointHitRadius&&(u.pointHitRadius=u.hitRadius),n=f.getPixelForValue("object"==typeof c?c:NaN,e,d),a=i?h.getBasePixel():r.calculatePointY(c,e,d),t._xScale=f,t._yScale=h,t._datasetIndex=d,t._index=e,t._model={x:n,y:a,skip:l.skip||isNaN(n)||isNaN(a),radius:l.radius||o.valueAtIndexOrDefault(u.pointRadius,e,g.radius),pointStyle:l.pointStyle||o.valueAtIndexOrDefault(u.pointStyle,e,g.pointStyle),backgroundColor:r.getPointBackgroundColor(t,e),borderColor:r.getPointBorderColor(t,e),borderWidth:r.getPointBorderWidth(t,e),tension:s.dataset._model?s.dataset._model.tension:0,steppedLine:!!s.dataset._model&&s.dataset._model.steppedLine,hitRadius:l.hitRadius||o.valueAtIndexOrDefault(u.pointHitRadius,e,g.hitRadius)}},calculatePointY:function(t,e,i){var n,a,o,r=this.chart,s=this.getMeta(),l=this.getScaleForId(s.yAxisID),u=0,d=0;if(l.options.stacked){for(n=0;n<i;n++)if(a=r.data.datasets[n],"line"===(o=r.getDatasetMeta(n)).type&&o.yAxisID===l.id&&r.isDatasetVisible(n)){var c=Number(l.getRightValue(a.data[e]));c<0?d+=c||0:u+=c||0}var h=Number(l.getRightValue(t));return h<0?l.getPixelForValue(d+h):l.getPixelForValue(u+h)}return l.getPixelForValue(t)},updateBezierControlPoints:function(){var t,e,i,n,a=this.getMeta(),r=this.chart.chartArea,s=a.data||[];function l(t,e,i){return Math.max(Math.min(t,i),e)}if(a.dataset._model.spanGaps&&(s=s.filter(function(t){return!t._model.skip})),"monotone"===a.dataset._model.cubicInterpolationMode)o.splineCurveMonotone(s);else for(t=0,e=s.length;t<e;++t)i=s[t]._model,n=o.splineCurve(o.previousItem(s,t)._model,i,o.nextItem(s,t)._model,a.dataset._model.tension),i.controlPointPreviousX=n.previous.x,i.controlPointPreviousY=n.previous.y,i.controlPointNextX=n.next.x,i.controlPointNextY=n.next.y;if(this.chart.options.elements.line.capBezierPoints)for(t=0,e=s.length;t<e;++t)(i=s[t]._model).controlPointPreviousX=l(i.controlPointPreviousX,r.left,r.right),i.controlPointPreviousY=l(i.controlPointPreviousY,r.top,r.bottom),i.controlPointNextX=l(i.controlPointNextX,r.left,r.right),i.controlPointNextY=l(i.controlPointNextY,r.top,r.bottom)},draw:function(){var t=this.chart,i=this.getMeta(),n=i.data||[],a=t.chartArea,r=n.length,s=0;for(o.canvas.clipArea(t.ctx,a),e(this.getDataset(),t.options)&&i.dataset.draw(),o.canvas.unclipArea(t.ctx);s<r;++s)n[s].draw(a)},setHoverStyle:function(t){var e=this.chart.data.datasets[t._datasetIndex],i=t._index,n=t.custom||{},a=t._model;a.radius=n.hoverRadius||o.valueAtIndexOrDefault(e.pointHoverRadius,i,this.chart.options.elements.point.hoverRadius),a.backgroundColor=n.hoverBackgroundColor||o.valueAtIndexOrDefault(e.pointHoverBackgroundColor,i,o.getHoverColor(a.backgroundColor)),a.borderColor=n.hoverBorderColor||o.valueAtIndexOrDefault(e.pointHoverBorderColor,i,o.getHoverColor(a.borderColor)),a.borderWidth=n.hoverBorderWidth||o.valueAtIndexOrDefault(e.pointHoverBorderWidth,i,a.borderWidth)},removeHoverStyle:function(t){var e=this,i=e.chart.data.datasets[t._datasetIndex],n=t._index,a=t.custom||{},r=t._model;void 0!==i.radius&&void 0===i.pointRadius&&(i.pointRadius=i.radius),r.radius=a.radius||o.valueAtIndexOrDefault(i.pointRadius,n,e.chart.options.elements.point.radius),r.backgroundColor=e.getPointBackgroundColor(t,n),r.borderColor=e.getPointBorderColor(t,n),r.borderWidth=e.getPointBorderWidth(t,n)}})}},{25:25,40:40,45:45}],19:[function(t,e,i){"use strict";var n=t(25),a=t(40),o=t(45);n._set("polarArea",{scale:{type:"radialLinear",angleLines:{display:!1},gridLines:{circular:!0},pointLabels:{display:!1},ticks:{beginAtZero:!0}},animation:{animateRotate:!0,animateScale:!0},startAngle:-.5*Math.PI,legendCallback:function(t){var e=[];e.push('<ul class="'+t.id+'-legend">');var i=t.data,n=i.datasets,a=i.labels;if(n.length)for(var o=0;o<n[0].data.length;++o)e.push('<li><span style="background-color:'+n[0].backgroundColor[o]+'"></span>'),a[o]&&e.push(a[o]),e.push("</li>");return e.push("</ul>"),e.join("")},legend:{labels:{generateLabels:function(t){var e=t.data;return e.labels.length&&e.datasets.length?e.labels.map(function(i,n){var a=t.getDatasetMeta(0),r=e.datasets[0],s=a.data[n].custom||{},l=o.valueAtIndexOrDefault,u=t.options.elements.arc;return{text:i,fillStyle:s.backgroundColor?s.backgroundColor:l(r.backgroundColor,n,u.backgroundColor),strokeStyle:s.borderColor?s.borderColor:l(r.borderColor,n,u.borderColor),lineWidth:s.borderWidth?s.borderWidth:l(r.borderWidth,n,u.borderWidth),hidden:isNaN(r.data[n])||a.data[n].hidden,index:n}}):[]}},onClick:function(t,e){var i,n,a,o=e.index,r=this.chart;for(i=0,n=(r.data.datasets||[]).length;i<n;++i)(a=r.getDatasetMeta(i)).data[o].hidden=!a.data[o].hidden;r.update()}},tooltips:{callbacks:{title:function(){return""},label:function(t,e){return e.labels[t.index]+": "+t.yLabel}}}}),e.exports=function(t){t.controllers.polarArea=t.DatasetController.extend({dataElementType:a.Arc,linkScales:o.noop,update:function(t){var e=this,i=e.chart,n=i.chartArea,a=e.getMeta(),r=i.options,s=r.elements.arc,l=Math.min(n.right-n.left,n.bottom-n.top);i.outerRadius=Math.max((l-s.borderWidth/2)/2,0),i.innerRadius=Math.max(r.cutoutPercentage?i.outerRadius/100*r.cutoutPercentage:1,0),i.radiusLength=(i.outerRadius-i.innerRadius)/i.getVisibleDatasetCount(),e.outerRadius=i.outerRadius-i.radiusLength*e.index,e.innerRadius=e.outerRadius-i.radiusLength,a.count=e.countVisibleElements(),o.each(a.data,function(i,n){e.updateElement(i,n,t)})},updateElement:function(t,e,i){for(var n=this,a=n.chart,r=n.getDataset(),s=a.options,l=s.animation,u=a.scale,d=a.data.labels,c=n.calculateCircumference(r.data[e]),h=u.xCenter,f=u.yCenter,g=0,p=n.getMeta(),m=0;m<e;++m)isNaN(r.data[m])||p.data[m].hidden||++g;var v=s.startAngle,b=t.hidden?0:u.getDistanceFromCenterForValue(r.data[e]),x=v+c*g,y=x+(t.hidden?0:c),k=l.animateScale?0:u.getDistanceFromCenterForValue(r.data[e]);o.extend(t,{_datasetIndex:n.index,_index:e,_scale:u,_model:{x:h,y:f,innerRadius:0,outerRadius:i?k:b,startAngle:i&&l.animateRotate?v:x,endAngle:i&&l.animateRotate?v:y,label:o.valueAtIndexOrDefault(d,e,d[e])}}),n.removeHoverStyle(t),t.pivot()},removeHoverStyle:function(e){t.DatasetController.prototype.removeHoverStyle.call(this,e,this.chart.options.elements.arc)},countVisibleElements:function(){var t=this.getDataset(),e=this.getMeta(),i=0;return o.each(e.data,function(e,n){isNaN(t.data[n])||e.hidden||i++}),i},calculateCircumference:function(t){var e=this.getMeta().count;return e>0&&!isNaN(t)?2*Math.PI/e:0}})}},{25:25,40:40,45:45}],20:[function(t,e,i){"use strict";var n=t(25),a=t(40),o=t(45);n._set("radar",{scale:{type:"radialLinear"},elements:{line:{tension:0}}}),e.exports=function(t){t.controllers.radar=t.DatasetController.extend({datasetElementType:a.Line,dataElementType:a.Point,linkScales:o.noop,update:function(t){var e=this,i=e.getMeta(),n=i.dataset,a=i.data,r=n.custom||{},s=e.getDataset(),l=e.chart.options.elements.line,u=e.chart.scale;void 0!==s.tension&&void 0===s.lineTension&&(s.lineTension=s.tension),o.extend(i.dataset,{_datasetIndex:e.index,_scale:u,_children:a,_loop:!0,_model:{tension:r.tension?r.tension:o.valueOrDefault(s.lineTension,l.tension),backgroundColor:r.backgroundColor?r.backgroundColor:s.backgroundColor||l.backgroundColor,borderWidth:r.borderWidth?r.borderWidth:s.borderWidth||l.borderWidth,borderColor:r.borderColor?r.borderColor:s.borderColor||l.borderColor,fill:r.fill?r.fill:void 0!==s.fill?s.fill:l.fill,borderCapStyle:r.borderCapStyle?r.borderCapStyle:s.borderCapStyle||l.borderCapStyle,borderDash:r.borderDash?r.borderDash:s.borderDash||l.borderDash,borderDashOffset:r.borderDashOffset?r.borderDashOffset:s.borderDashOffset||l.borderDashOffset,borderJoinStyle:r.borderJoinStyle?r.borderJoinStyle:s.borderJoinStyle||l.borderJoinStyle}}),i.dataset.pivot(),o.each(a,function(i,n){e.updateElement(i,n,t)},e),e.updateBezierControlPoints()},updateElement:function(t,e,i){var n=this,a=t.custom||{},r=n.getDataset(),s=n.chart.scale,l=n.chart.options.elements.point,u=s.getPointPositionForValue(e,r.data[e]);void 0!==r.radius&&void 0===r.pointRadius&&(r.pointRadius=r.radius),void 0!==r.hitRadius&&void 0===r.pointHitRadius&&(r.pointHitRadius=r.hitRadius),o.extend(t,{_datasetIndex:n.index,_index:e,_scale:s,_model:{x:i?s.xCenter:u.x,y:i?s.yCenter:u.y,tension:a.tension?a.tension:o.valueOrDefault(r.lineTension,n.chart.options.elements.line.tension),radius:a.radius?a.radius:o.valueAtIndexOrDefault(r.pointRadius,e,l.radius),backgroundColor:a.backgroundColor?a.backgroundColor:o.valueAtIndexOrDefault(r.pointBackgroundColor,e,l.backgroundColor),borderColor:a.borderColor?a.borderColor:o.valueAtIndexOrDefault(r.pointBorderColor,e,l.borderColor),borderWidth:a.borderWidth?a.borderWidth:o.valueAtIndexOrDefault(r.pointBorderWidth,e,l.borderWidth),pointStyle:a.pointStyle?a.pointStyle:o.valueAtIndexOrDefault(r.pointStyle,e,l.pointStyle),hitRadius:a.hitRadius?a.hitRadius:o.valueAtIndexOrDefault(r.pointHitRadius,e,l.hitRadius)}}),t._model.skip=a.skip?a.skip:isNaN(t._model.x)||isNaN(t._model.y)},updateBezierControlPoints:function(){var t=this.chart.chartArea,e=this.getMeta();o.each(e.data,function(i,n){var a=i._model,r=o.splineCurve(o.previousItem(e.data,n,!0)._model,a,o.nextItem(e.data,n,!0)._model,a.tension);a.controlPointPreviousX=Math.max(Math.min(r.previous.x,t.right),t.left),a.controlPointPreviousY=Math.max(Math.min(r.previous.y,t.bottom),t.top),a.controlPointNextX=Math.max(Math.min(r.next.x,t.right),t.left),a.controlPointNextY=Math.max(Math.min(r.next.y,t.bottom),t.top),i.pivot()})},setHoverStyle:function(t){var e=this.chart.data.datasets[t._datasetIndex],i=t.custom||{},n=t._index,a=t._model;a.radius=i.hoverRadius?i.hoverRadius:o.valueAtIndexOrDefault(e.pointHoverRadius,n,this.chart.options.elements.point.hoverRadius),a.backgroundColor=i.hoverBackgroundColor?i.hoverBackgroundColor:o.valueAtIndexOrDefault(e.pointHoverBackgroundColor,n,o.getHoverColor(a.backgroundColor)),a.borderColor=i.hoverBorderColor?i.hoverBorderColor:o.valueAtIndexOrDefault(e.pointHoverBorderColor,n,o.getHoverColor(a.borderColor)),a.borderWidth=i.hoverBorderWidth?i.hoverBorderWidth:o.valueAtIndexOrDefault(e.pointHoverBorderWidth,n,a.borderWidth)},removeHoverStyle:function(t){var e=this.chart.data.datasets[t._datasetIndex],i=t.custom||{},n=t._index,a=t._model,r=this.chart.options.elements.point;a.radius=i.radius?i.radius:o.valueAtIndexOrDefault(e.pointRadius,n,r.radius),a.backgroundColor=i.backgroundColor?i.backgroundColor:o.valueAtIndexOrDefault(e.pointBackgroundColor,n,r.backgroundColor),a.borderColor=i.borderColor?i.borderColor:o.valueAtIndexOrDefault(e.pointBorderColor,n,r.borderColor),a.borderWidth=i.borderWidth?i.borderWidth:o.valueAtIndexOrDefault(e.pointBorderWidth,n,r.borderWidth)}})}},{25:25,40:40,45:45}],21:[function(t,e,i){"use strict";t(25)._set("scatter",{hover:{mode:"single"},scales:{xAxes:[{id:"x-axis-1",type:"linear",position:"bottom"}],yAxes:[{id:"y-axis-1",type:"linear",position:"left"}]},showLines:!1,tooltips:{callbacks:{title:function(){return""},label:function(t){return"("+t.xLabel+", "+t.yLabel+")"}}}}),e.exports=function(t){t.controllers.scatter=t.controllers.line}},{25:25}],22:[function(t,e,i){"use strict";var n=t(25),a=t(26),o=t(45);n._set("global",{animation:{duration:1e3,easing:"easeOutQuart",onProgress:o.noop,onComplete:o.noop}}),e.exports=function(t){t.Animation=a.extend({chart:null,currentStep:0,numSteps:60,easing:"",render:null,onAnimationProgress:null,onAnimationComplete:null}),t.animationService={frameDuration:17,animations:[],dropFrames:0,request:null,addAnimation:function(t,e,i,n){var a,o,r=this.animations;for(e.chart=t,n||(t.animating=!0),a=0,o=r.length;a<o;++a)if(r[a].chart===t)return void(r[a]=e);r.push(e),1===r.length&&this.requestAnimationFrame()},cancelAnimation:function(t){var e=o.findIndex(this.animations,function(e){return e.chart===t});-1!==e&&(this.animations.splice(e,1),t.animating=!1)},requestAnimationFrame:function(){var t=this;null===t.request&&(t.request=o.requestAnimFrame.call(window,function(){t.request=null,t.startDigest()}))},startDigest:function(){var t=this,e=Date.now(),i=0;t.dropFrames>1&&(i=Math.floor(t.dropFrames),t.dropFrames=t.dropFrames%1),t.advance(1+i);var n=Date.now();t.dropFrames+=(n-e)/t.frameDuration,t.animations.length>0&&t.requestAnimationFrame()},advance:function(t){for(var e,i,n=this.animations,a=0;a<n.length;)i=(e=n[a]).chart,e.currentStep=(e.currentStep||0)+t,e.currentStep=Math.min(e.currentStep,e.numSteps),o.callback(e.render,[i,e],i),o.callback(e.onAnimationProgress,[e],i),e.currentStep>=e.numSteps?(o.callback(e.onAnimationComplete,[e],i),i.animating=!1,n.splice(a,1)):++a}},Object.defineProperty(t.Animation.prototype,"animationObject",{get:function(){return this}}),Object.defineProperty(t.Animation.prototype,"chartInstance",{get:function(){return this.chart},set:function(t){this.chart=t}})}},{25:25,26:26,45:45}],23:[function(t,e,i){"use strict";var n=t(25),a=t(45),o=t(28),r=t(30),s=t(48),l=t(31);e.exports=function(t){function e(t){return"top"===t||"bottom"===t}t.types={},t.instances={},t.controllers={},a.extend(t.prototype,{construct:function(e,i){var o,r,l=this;(r=(o=(o=i)||{}).data=o.data||{}).datasets=r.datasets||[],r.labels=r.labels||[],o.options=a.configMerge(n.global,n[o.type],o.options||{}),i=o;var u=s.acquireContext(e,i),d=u&&u.canvas,c=d&&d.height,h=d&&d.width;l.id=a.uid(),l.ctx=u,l.canvas=d,l.config=i,l.width=h,l.height=c,l.aspectRatio=c?h/c:null,l.options=i.options,l._bufferedRender=!1,l.chart=l,l.controller=l,t.instances[l.id]=l,Object.defineProperty(l,"data",{get:function(){return l.config.data},set:function(t){l.config.data=t}}),u&&d?(l.initialize(),l.update()):console.error("Failed to create chart: can't acquire context from the given item")},initialize:function(){var t=this;return l.notify(t,"beforeInit"),a.retinaScale(t,t.options.devicePixelRatio),t.bindEvents(),t.options.responsive&&t.resize(!0),t.ensureScalesHaveIDs(),t.buildOrUpdateScales(),t.initToolTip(),l.notify(t,"afterInit"),t},clear:function(){return a.canvas.clear(this),this},stop:function(){return t.animationService.cancelAnimation(this),this},resize:function(t){var e=this,i=e.options,n=e.canvas,o=i.maintainAspectRatio&&e.aspectRatio||null,r=Math.max(0,Math.floor(a.getMaximumWidth(n))),s=Math.max(0,Math.floor(o?r/o:a.getMaximumHeight(n)));if((e.width!==r||e.height!==s)&&(n.width=e.width=r,n.height=e.height=s,n.style.width=r+"px",n.style.height=s+"px",a.retinaScale(e,i.devicePixelRatio),!t)){var u={width:r,height:s};l.notify(e,"resize",[u]),e.options.onResize&&e.options.onResize(e,u),e.stop(),e.update(e.options.responsiveAnimationDuration)}},ensureScalesHaveIDs:function(){var t=this.options,e=t.scales||{},i=t.scale;a.each(e.xAxes,function(t,e){t.id=t.id||"x-axis-"+e}),a.each(e.yAxes,function(t,e){t.id=t.id||"y-axis-"+e}),i&&(i.id=i.id||"scale")},buildOrUpdateScales:function(){var i=this,n=i.options,o=i.scales||{},r=[],s=Object.keys(o).reduce(function(t,e){return t[e]=!1,t},{});n.scales&&(r=r.concat((n.scales.xAxes||[]).map(function(t){return{options:t,dtype:"category",dposition:"bottom"}}),(n.scales.yAxes||[]).map(function(t){return{options:t,dtype:"linear",dposition:"left"}}))),n.scale&&r.push({options:n.scale,dtype:"radialLinear",isDefault:!0,dposition:"chartArea"}),a.each(r,function(n){var r=n.options,l=r.id,u=a.valueOrDefault(r.type,n.dtype);e(r.position)!==e(n.dposition)&&(r.position=n.dposition),s[l]=!0;var d=null;if(l in o&&o[l].type===u)(d=o[l]).options=r,d.ctx=i.ctx,d.chart=i;else{var c=t.scaleService.getScaleConstructor(u);if(!c)return;d=new c({id:l,type:u,options:r,ctx:i.ctx,chart:i}),o[d.id]=d}d.mergeTicksOptions(),n.isDefault&&(i.scale=d)}),a.each(s,function(t,e){t||delete o[e]}),i.scales=o,t.scaleService.addScalesToLayout(this)},buildOrUpdateControllers:function(){var e=this,i=[],n=[];return a.each(e.data.datasets,function(a,o){var r=e.getDatasetMeta(o),s=a.type||e.config.type;if(r.type&&r.type!==s&&(e.destroyDatasetMeta(o),r=e.getDatasetMeta(o)),r.type=s,i.push(r.type),r.controller)r.controller.updateIndex(o),r.controller.linkScales();else{var l=t.controllers[r.type];if(void 0===l)throw new Error('"'+r.type+'" is not a chart type.');r.controller=new l(e,o),n.push(r.controller)}},e),n},resetElements:function(){var t=this;a.each(t.data.datasets,function(e,i){t.getDatasetMeta(i).controller.reset()},t)},reset:function(){this.resetElements(),this.tooltip.initialize()},update:function(e){var i,n,o=this;if(e&&"object"==typeof e||(e={duration:e,lazy:arguments[1]}),n=(i=o).options,a.each(i.scales,function(t){r.removeBox(i,t)}),n=a.configMerge(t.defaults.global,t.defaults[i.config.type],n),i.options=i.config.options=n,i.ensureScalesHaveIDs(),i.buildOrUpdateScales(),i.tooltip._options=n.tooltips,i.tooltip.initialize(),l._invalidate(o),!1!==l.notify(o,"beforeUpdate")){o.tooltip._data=o.data;var s=o.buildOrUpdateControllers();a.each(o.data.datasets,function(t,e){o.getDatasetMeta(e).controller.buildOrUpdateElements()},o),o.updateLayout(),o.options.animation&&o.options.animation.duration&&a.each(s,function(t){t.reset()}),o.updateDatasets(),o.tooltip.initialize(),o.lastActive=[],l.notify(o,"afterUpdate"),o._bufferedRender?o._bufferedRequest={duration:e.duration,easing:e.easing,lazy:e.lazy}:o.render(e)}},updateLayout:function(){!1!==l.notify(this,"beforeLayout")&&(r.update(this,this.width,this.height),l.notify(this,"afterScaleUpdate"),l.notify(this,"afterLayout"))},updateDatasets:function(){if(!1!==l.notify(this,"beforeDatasetsUpdate")){for(var t=0,e=this.data.datasets.length;t<e;++t)this.updateDataset(t);l.notify(this,"afterDatasetsUpdate")}},updateDataset:function(t){var e=this.getDatasetMeta(t),i={meta:e,index:t};!1!==l.notify(this,"beforeDatasetUpdate",[i])&&(e.controller.update(),l.notify(this,"afterDatasetUpdate",[i]))},render:function(e){var i=this;e&&"object"==typeof e||(e={duration:e,lazy:arguments[1]});var n=e.duration,o=e.lazy;if(!1!==l.notify(i,"beforeRender")){var r=i.options.animation,s=function(t){l.notify(i,"afterRender"),a.callback(r&&r.onComplete,[t],i)};if(r&&(void 0!==n&&0!==n||void 0===n&&0!==r.duration)){var u=new t.Animation({numSteps:(n||r.duration)/16.66,easing:e.easing||r.easing,render:function(t,e){var i=a.easing.effects[e.easing],n=e.currentStep,o=n/e.numSteps;t.draw(i(o),o,n)},onAnimationProgress:r.onProgress,onAnimationComplete:s});t.animationService.addAnimation(i,u,n,o)}else i.draw(),s(new t.Animation({numSteps:0,chart:i}));return i}},draw:function(t){var e=this;e.clear(),a.isNullOrUndef(t)&&(t=1),e.transition(t),!1!==l.notify(e,"beforeDraw",[t])&&(a.each(e.boxes,function(t){t.draw(e.chartArea)},e),e.scale&&e.scale.draw(),e.drawDatasets(t),e._drawTooltip(t),l.notify(e,"afterDraw",[t]))},transition:function(t){for(var e=0,i=(this.data.datasets||[]).length;e<i;++e)this.isDatasetVisible(e)&&this.getDatasetMeta(e).controller.transition(t);this.tooltip.transition(t)},drawDatasets:function(t){var e=this;if(!1!==l.notify(e,"beforeDatasetsDraw",[t])){for(var i=(e.data.datasets||[]).length-1;i>=0;--i)e.isDatasetVisible(i)&&e.drawDataset(i,t);l.notify(e,"afterDatasetsDraw",[t])}},drawDataset:function(t,e){var i=this.getDatasetMeta(t),n={meta:i,index:t,easingValue:e};!1!==l.notify(this,"beforeDatasetDraw",[n])&&(i.controller.draw(e),l.notify(this,"afterDatasetDraw",[n]))},_drawTooltip:function(t){var e=this.tooltip,i={tooltip:e,easingValue:t};!1!==l.notify(this,"beforeTooltipDraw",[i])&&(e.draw(),l.notify(this,"afterTooltipDraw",[i]))},getElementAtEvent:function(t){return o.modes.single(this,t)},getElementsAtEvent:function(t){return o.modes.label(this,t,{intersect:!0})},getElementsAtXAxis:function(t){return o.modes["x-axis"](this,t,{intersect:!0})},getElementsAtEventForMode:function(t,e,i){var n=o.modes[e];return"function"==typeof n?n(this,t,i):[]},getDatasetAtEvent:function(t){return o.modes.dataset(this,t,{intersect:!0})},getDatasetMeta:function(t){var e=this.data.datasets[t];e._meta||(e._meta={});var i=e._meta[this.id];return i||(i=e._meta[this.id]={type:null,data:[],dataset:null,controller:null,hidden:null,xAxisID:null,yAxisID:null}),i},getVisibleDatasetCount:function(){for(var t=0,e=0,i=this.data.datasets.length;e<i;++e)this.isDatasetVisible(e)&&t++;return t},isDatasetVisible:function(t){var e=this.getDatasetMeta(t);return"boolean"==typeof e.hidden?!e.hidden:!this.data.datasets[t].hidden},generateLegend:function(){return this.options.legendCallback(this)},destroyDatasetMeta:function(t){var e=this.id,i=this.data.datasets[t],n=i._meta&&i._meta[e];n&&(n.controller.destroy(),delete i._meta[e])},destroy:function(){var e,i,n=this,o=n.canvas;for(n.stop(),e=0,i=n.data.datasets.length;e<i;++e)n.destroyDatasetMeta(e);o&&(n.unbindEvents(),a.canvas.clear(n),s.releaseContext(n.ctx),n.canvas=null,n.ctx=null),l.notify(n,"destroy"),delete t.instances[n.id]},toBase64Image:function(){return this.canvas.toDataURL.apply(this.canvas,arguments)},initToolTip:function(){var e=this;e.tooltip=new t.Tooltip({_chart:e,_chartInstance:e,_data:e.data,_options:e.options.tooltips},e)},bindEvents:function(){var t=this,e=t._listeners={},i=function(){t.eventHandler.apply(t,arguments)};a.each(t.options.events,function(n){s.addEventListener(t,n,i),e[n]=i}),t.options.responsive&&(i=function(){t.resize()},s.addEventListener(t,"resize",i),e.resize=i)},unbindEvents:function(){var t=this,e=t._listeners;e&&(delete t._listeners,a.each(e,function(e,i){s.removeEventListener(t,i,e)}))},updateHoverStyle:function(t,e,i){var n,a,o,r=i?"setHoverStyle":"removeHoverStyle";for(a=0,o=t.length;a<o;++a)(n=t[a])&&this.getDatasetMeta(n._datasetIndex).controller[r](n)},eventHandler:function(t){var e=this,i=e.tooltip;if(!1!==l.notify(e,"beforeEvent",[t])){e._bufferedRender=!0,e._bufferedRequest=null;var n=e.handleEvent(t);i&&(n=i._start?i.handleEvent(t):n|i.handleEvent(t)),l.notify(e,"afterEvent",[t]);var a=e._bufferedRequest;return a?e.render(a):n&&!e.animating&&(e.stop(),e.render(e.options.hover.animationDuration,!0)),e._bufferedRender=!1,e._bufferedRequest=null,e}},handleEvent:function(t){var e,i=this,n=i.options||{},o=n.hover;return i.lastActive=i.lastActive||[],"mouseout"===t.type?i.active=[]:i.active=i.getElementsAtEventForMode(t,o.mode,o),a.callback(n.onHover||n.hover.onHover,[t.native,i.active],i),"mouseup"!==t.type&&"click"!==t.type||n.onClick&&n.onClick.call(i,t.native,i.active),i.lastActive.length&&i.updateHoverStyle(i.lastActive,o.mode,!1),i.active.length&&o.mode&&i.updateHoverStyle(i.active,o.mode,!0),e=!a.arrayEquals(i.active,i.lastActive),i.lastActive=i.active,e}}),t.Controller=t}},{25:25,28:28,30:30,31:31,45:45,48:48}],24:[function(t,e,i){"use strict";var n=t(45);e.exports=function(t){var e=["push","pop","shift","splice","unshift"];function i(t,i){var n=t._chartjs;if(n){var a=n.listeners,o=a.indexOf(i);-1!==o&&a.splice(o,1),a.length>0||(e.forEach(function(e){delete t[e]}),delete t._chartjs)}}t.DatasetController=function(t,e){this.initialize(t,e)},n.extend(t.DatasetController.prototype,{datasetElementType:null,dataElementType:null,initialize:function(t,e){this.chart=t,this.index=e,this.linkScales(),this.addElements()},updateIndex:function(t){this.index=t},linkScales:function(){var t=this,e=t.getMeta(),i=t.getDataset();null!==e.xAxisID&&e.xAxisID in t.chart.scales||(e.xAxisID=i.xAxisID||t.chart.options.scales.xAxes[0].id),null!==e.yAxisID&&e.yAxisID in t.chart.scales||(e.yAxisID=i.yAxisID||t.chart.options.scales.yAxes[0].id)},getDataset:function(){return this.chart.data.datasets[this.index]},getMeta:function(){return this.chart.getDatasetMeta(this.index)},getScaleForId:function(t){return this.chart.scales[t]},reset:function(){this.update(!0)},destroy:function(){this._data&&i(this._data,this)},createMetaDataset:function(){var t=this.datasetElementType;return t&&new t({_chart:this.chart,_datasetIndex:this.index})},createMetaData:function(t){var e=this.dataElementType;return e&&new e({_chart:this.chart,_datasetIndex:this.index,_index:t})},addElements:function(){var t,e,i=this.getMeta(),n=this.getDataset().data||[],a=i.data;for(t=0,e=n.length;t<e;++t)a[t]=a[t]||this.createMetaData(t);i.dataset=i.dataset||this.createMetaDataset()},addElementAndReset:function(t){var e=this.createMetaData(t);this.getMeta().data.splice(t,0,e),this.updateElement(e,t,!0)},buildOrUpdateElements:function(){var t,a,o=this,r=o.getDataset(),s=r.data||(r.data=[]);o._data!==s&&(o._data&&i(o._data,o),a=o,(t=s)._chartjs?t._chartjs.listeners.push(a):(Object.defineProperty(t,"_chartjs",{configurable:!0,enumerable:!1,value:{listeners:[a]}}),e.forEach(function(e){var i="onData"+e.charAt(0).toUpperCase()+e.slice(1),a=t[e];Object.defineProperty(t,e,{configurable:!0,enumerable:!1,value:function(){var e=Array.prototype.slice.call(arguments),o=a.apply(this,e);return n.each(t._chartjs.listeners,function(t){"function"==typeof t[i]&&t[i].apply(t,e)}),o}})})),o._data=s),o.resyncElements()},update:n.noop,transition:function(t){for(var e=this.getMeta(),i=e.data||[],n=i.length,a=0;a<n;++a)i[a].transition(t);e.dataset&&e.dataset.transition(t)},draw:function(){var t=this.getMeta(),e=t.data||[],i=e.length,n=0;for(t.dataset&&t.dataset.draw();n<i;++n)e[n].draw()},removeHoverStyle:function(t,e){var i=this.chart.data.datasets[t._datasetIndex],a=t._index,o=t.custom||{},r=n.valueAtIndexOrDefault,s=t._model;s.backgroundColor=o.backgroundColor?o.backgroundColor:r(i.backgroundColor,a,e.backgroundColor),s.borderColor=o.borderColor?o.borderColor:r(i.borderColor,a,e.borderColor),s.borderWidth=o.borderWidth?o.borderWidth:r(i.borderWidth,a,e.borderWidth)},setHoverStyle:function(t){var e=this.chart.data.datasets[t._datasetIndex],i=t._index,a=t.custom||{},o=n.valueAtIndexOrDefault,r=n.getHoverColor,s=t._model;s.backgroundColor=a.hoverBackgroundColor?a.hoverBackgroundColor:o(e.hoverBackgroundColor,i,r(s.backgroundColor)),s.borderColor=a.hoverBorderColor?a.hoverBorderColor:o(e.hoverBorderColor,i,r(s.borderColor)),s.borderWidth=a.hoverBorderWidth?a.hoverBorderWidth:o(e.hoverBorderWidth,i,s.borderWidth)},resyncElements:function(){var t=this.getMeta(),e=this.getDataset().data,i=t.data.length,n=e.length;n<i?t.data.splice(n,i-n):n>i&&this.insertElements(i,n-i)},insertElements:function(t,e){for(var i=0;i<e;++i)this.addElementAndReset(t+i)},onDataPush:function(){this.insertElements(this.getDataset().data.length-1,arguments.length)},onDataPop:function(){this.getMeta().data.pop()},onDataShift:function(){this.getMeta().data.shift()},onDataSplice:function(t,e){this.getMeta().data.splice(t,e),this.insertElements(t,arguments.length-2)},onDataUnshift:function(){this.insertElements(0,arguments.length)}}),t.DatasetController.extend=n.inherits}},{45:45}],25:[function(t,e,i){"use strict";var n=t(45);e.exports={_set:function(t,e){return n.merge(this[t]||(this[t]={}),e)}}},{45:45}],26:[function(t,e,i){"use strict";var n=t(3),a=t(45);var o=function(t){a.extend(this,t),this.initialize.apply(this,arguments)};a.extend(o.prototype,{initialize:function(){this.hidden=!1},pivot:function(){var t=this;return t._view||(t._view=a.clone(t._model)),t._start={},t},transition:function(t){var e=this,i=e._model,a=e._start,o=e._view;return i&&1!==t?(o||(o=e._view={}),a||(a=e._start={}),function(t,e,i,a){var o,r,s,l,u,d,c,h,f,g=Object.keys(i);for(o=0,r=g.length;o<r;++o)if(d=i[s=g[o]],e.hasOwnProperty(s)||(e[s]=d),(l=e[s])!==d&&"_"!==s[0]){if(t.hasOwnProperty(s)||(t[s]=l),(c=typeof d)==typeof(u=t[s]))if("string"===c){if((h=n(u)).valid&&(f=n(d)).valid){e[s]=f.mix(h,a).rgbString();continue}}else if("number"===c&&isFinite(u)&&isFinite(d)){e[s]=u+(d-u)*a;continue}e[s]=d}}(a,o,i,t),e):(e._view=i,e._start=null,e)},tooltipPosition:function(){return{x:this._model.x,y:this._model.y}},hasValue:function(){return a.isNumber(this._model.x)&&a.isNumber(this._model.y)}}),o.extend=a.inherits,e.exports=o},{3:3,45:45}],27:[function(t,e,i){"use strict";var n=t(3),a=t(25),o=t(45);e.exports=function(t){function e(t,e,i){var n;return"string"==typeof t?(n=parseInt(t,10),-1!==t.indexOf("%")&&(n=n/100*e.parentNode[i])):n=t,n}function i(t){return null!=t&&"none"!==t}function r(t,n,a){var o=document.defaultView,r=t.parentNode,s=o.getComputedStyle(t)[n],l=o.getComputedStyle(r)[n],u=i(s),d=i(l),c=Number.POSITIVE_INFINITY;return u||d?Math.min(u?e(s,t,a):c,d?e(l,r,a):c):"none"}o.configMerge=function(){return o.merge(o.clone(arguments[0]),[].slice.call(arguments,1),{merger:function(e,i,n,a){var r=i[e]||{},s=n[e];"scales"===e?i[e]=o.scaleMerge(r,s):"scale"===e?i[e]=o.merge(r,[t.scaleService.getScaleDefaults(s.type),s]):o._merger(e,i,n,a)}})},o.scaleMerge=function(){return o.merge(o.clone(arguments[0]),[].slice.call(arguments,1),{merger:function(e,i,n,a){if("xAxes"===e||"yAxes"===e){var r,s,l,u=n[e].length;for(i[e]||(i[e]=[]),r=0;r<u;++r)l=n[e][r],s=o.valueOrDefault(l.type,"xAxes"===e?"category":"linear"),r>=i[e].length&&i[e].push({}),!i[e][r].type||l.type&&l.type!==i[e][r].type?o.merge(i[e][r],[t.scaleService.getScaleDefaults(s),l]):o.merge(i[e][r],l)}else o._merger(e,i,n,a)}})},o.where=function(t,e){if(o.isArray(t)&&Array.prototype.filter)return t.filter(e);var i=[];return o.each(t,function(t){e(t)&&i.push(t)}),i},o.findIndex=Array.prototype.findIndex?function(t,e,i){return t.findIndex(e,i)}:function(t,e,i){i=void 0===i?t:i;for(var n=0,a=t.length;n<a;++n)if(e.call(i,t[n],n,t))return n;return-1},o.findNextWhere=function(t,e,i){o.isNullOrUndef(i)&&(i=-1);for(var n=i+1;n<t.length;n++){var a=t[n];if(e(a))return a}},o.findPreviousWhere=function(t,e,i){o.isNullOrUndef(i)&&(i=t.length);for(var n=i-1;n>=0;n--){var a=t[n];if(e(a))return a}},o.isNumber=function(t){return!isNaN(parseFloat(t))&&isFinite(t)},o.almostEquals=function(t,e,i){return Math.abs(t-e)<i},o.almostWhole=function(t,e){var i=Math.round(t);return i-e<t&&i+e>t},o.max=function(t){return t.reduce(function(t,e){return isNaN(e)?t:Math.max(t,e)},Number.NEGATIVE_INFINITY)},o.min=function(t){return t.reduce(function(t,e){return isNaN(e)?t:Math.min(t,e)},Number.POSITIVE_INFINITY)},o.sign=Math.sign?function(t){return Math.sign(t)}:function(t){return 0===(t=+t)||isNaN(t)?t:t>0?1:-1},o.log10=Math.log10?function(t){return Math.log10(t)}:function(t){var e=Math.log(t)*Math.LOG10E,i=Math.round(e);return t===Math.pow(10,i)?i:e},o.toRadians=function(t){return t*(Math.PI/180)},o.toDegrees=function(t){return t*(180/Math.PI)},o.getAngleFromPoint=function(t,e){var i=e.x-t.x,n=e.y-t.y,a=Math.sqrt(i*i+n*n),o=Math.atan2(n,i);return o<-.5*Math.PI&&(o+=2*Math.PI),{angle:o,distance:a}},o.distanceBetweenPoints=function(t,e){return Math.sqrt(Math.pow(e.x-t.x,2)+Math.pow(e.y-t.y,2))},o.aliasPixel=function(t){return t%2==0?0:.5},o.splineCurve=function(t,e,i,n){var a=t.skip?e:t,o=e,r=i.skip?e:i,s=Math.sqrt(Math.pow(o.x-a.x,2)+Math.pow(o.y-a.y,2)),l=Math.sqrt(Math.pow(r.x-o.x,2)+Math.pow(r.y-o.y,2)),u=s/(s+l),d=l/(s+l),c=n*(u=isNaN(u)?0:u),h=n*(d=isNaN(d)?0:d);return{previous:{x:o.x-c*(r.x-a.x),y:o.y-c*(r.y-a.y)},next:{x:o.x+h*(r.x-a.x),y:o.y+h*(r.y-a.y)}}},o.EPSILON=Number.EPSILON||1e-14,o.splineCurveMonotone=function(t){var e,i,n,a,r,s,l,u,d,c=(t||[]).map(function(t){return{model:t._model,deltaK:0,mK:0}}),h=c.length;for(e=0;e<h;++e)if(!(n=c[e]).model.skip){if(i=e>0?c[e-1]:null,(a=e<h-1?c[e+1]:null)&&!a.model.skip){var f=a.model.x-n.model.x;n.deltaK=0!==f?(a.model.y-n.model.y)/f:0}!i||i.model.skip?n.mK=n.deltaK:!a||a.model.skip?n.mK=i.deltaK:this.sign(i.deltaK)!==this.sign(n.deltaK)?n.mK=0:n.mK=(i.deltaK+n.deltaK)/2}for(e=0;e<h-1;++e)n=c[e],a=c[e+1],n.model.skip||a.model.skip||(o.almostEquals(n.deltaK,0,this.EPSILON)?n.mK=a.mK=0:(r=n.mK/n.deltaK,s=a.mK/n.deltaK,(u=Math.pow(r,2)+Math.pow(s,2))<=9||(l=3/Math.sqrt(u),n.mK=r*l*n.deltaK,a.mK=s*l*n.deltaK)));for(e=0;e<h;++e)(n=c[e]).model.skip||(i=e>0?c[e-1]:null,a=e<h-1?c[e+1]:null,i&&!i.model.skip&&(d=(n.model.x-i.model.x)/3,n.model.controlPointPreviousX=n.model.x-d,n.model.controlPointPreviousY=n.model.y-d*n.mK),a&&!a.model.skip&&(d=(a.model.x-n.model.x)/3,n.model.controlPointNextX=n.model.x+d,n.model.controlPointNextY=n.model.y+d*n.mK))},o.nextItem=function(t,e,i){return i?e>=t.length-1?t[0]:t[e+1]:e>=t.length-1?t[t.length-1]:t[e+1]},o.previousItem=function(t,e,i){return i?e<=0?t[t.length-1]:t[e-1]:e<=0?t[0]:t[e-1]},o.niceNum=function(t,e){var i=Math.floor(o.log10(t)),n=t/Math.pow(10,i);return(e?n<1.5?1:n<3?2:n<7?5:10:n<=1?1:n<=2?2:n<=5?5:10)*Math.pow(10,i)},o.requestAnimFrame="undefined"==typeof window?function(t){t()}:window.requestAnimationFrame||window.webkitRequestAnimationFrame||window.mozRequestAnimationFrame||window.oRequestAnimationFrame||window.msRequestAnimationFrame||function(t){return window.setTimeout(t,1e3/60)},o.getRelativePosition=function(t,e){var i,n,a=t.originalEvent||t,r=t.currentTarget||t.srcElement,s=r.getBoundingClientRect(),l=a.touches;l&&l.length>0?(i=l[0].clientX,n=l[0].clientY):(i=a.clientX,n=a.clientY);var u=parseFloat(o.getStyle(r,"padding-left")),d=parseFloat(o.getStyle(r,"padding-top")),c=parseFloat(o.getStyle(r,"padding-right")),h=parseFloat(o.getStyle(r,"padding-bottom")),f=s.right-s.left-u-c,g=s.bottom-s.top-d-h;return{x:i=Math.round((i-s.left-u)/f*r.width/e.currentDevicePixelRatio),y:n=Math.round((n-s.top-d)/g*r.height/e.currentDevicePixelRatio)}},o.getConstraintWidth=function(t){return r(t,"max-width","clientWidth")},o.getConstraintHeight=function(t){return r(t,"max-height","clientHeight")},o.getMaximumWidth=function(t){var e=t.parentNode;if(!e)return t.clientWidth;var i=parseInt(o.getStyle(e,"padding-left"),10),n=parseInt(o.getStyle(e,"padding-right"),10),a=e.clientWidth-i-n,r=o.getConstraintWidth(t);return isNaN(r)?a:Math.min(a,r)},o.getMaximumHeight=function(t){var e=t.parentNode;if(!e)return t.clientHeight;var i=parseInt(o.getStyle(e,"padding-top"),10),n=parseInt(o.getStyle(e,"padding-bottom"),10),a=e.clientHeight-i-n,r=o.getConstraintHeight(t);return isNaN(r)?a:Math.min(a,r)},o.getStyle=function(t,e){return t.currentStyle?t.currentStyle[e]:document.defaultView.getComputedStyle(t,null).getPropertyValue(e)},o.retinaScale=function(t,e){var i=t.currentDevicePixelRatio=e||window.devicePixelRatio||1;if(1!==i){var n=t.canvas,a=t.height,o=t.width;n.height=a*i,n.width=o*i,t.ctx.scale(i,i),n.style.height||n.style.width||(n.style.height=a+"px",n.style.width=o+"px")}},o.fontString=function(t,e,i){return e+" "+t+"px "+i},o.longestText=function(t,e,i,n){var a=(n=n||{}).data=n.data||{},r=n.garbageCollect=n.garbageCollect||[];n.font!==e&&(a=n.data={},r=n.garbageCollect=[],n.font=e),t.font=e;var s=0;o.each(i,function(e){null!=e&&!0!==o.isArray(e)?s=o.measureText(t,a,r,s,e):o.isArray(e)&&o.each(e,function(e){null==e||o.isArray(e)||(s=o.measureText(t,a,r,s,e))})});var l=r.length/2;if(l>i.length){for(var u=0;u<l;u++)delete a[r[u]];r.splice(0,l)}return s},o.measureText=function(t,e,i,n,a){var o=e[a];return o||(o=e[a]=t.measureText(a).width,i.push(a)),o>n&&(n=o),n},o.numberOfLabelLines=function(t){var e=1;return o.each(t,function(t){o.isArray(t)&&t.length>e&&(e=t.length)}),e},o.color=n?function(t){return t instanceof CanvasGradient&&(t=a.global.defaultColor),n(t)}:function(t){return console.error("Color.js not found!"),t},o.getHoverColor=function(t){return t instanceof CanvasPattern?t:o.color(t).saturate(.5).darken(.1).rgbString()}}},{25:25,3:3,45:45}],28:[function(t,e,i){"use strict";var n=t(45);function a(t,e){return t.native?{x:t.x,y:t.y}:n.getRelativePosition(t,e)}function o(t,e){var i,n,a,o,r;for(n=0,o=t.data.datasets.length;n<o;++n)if(t.isDatasetVisible(n))for(a=0,r=(i=t.getDatasetMeta(n)).data.length;a<r;++a){var s=i.data[a];s._view.skip||e(s)}}function r(t,e){var i=[];return o(t,function(t){t.inRange(e.x,e.y)&&i.push(t)}),i}function s(t,e,i,n){var a=Number.POSITIVE_INFINITY,r=[];return o(t,function(t){if(!i||t.inRange(e.x,e.y)){var o=t.getCenterPoint(),s=n(e,o);s<a?(r=[t],a=s):s===a&&r.push(t)}}),r}function l(t){var e=-1!==t.indexOf("x"),i=-1!==t.indexOf("y");return function(t,n){var a=e?Math.abs(t.x-n.x):0,o=i?Math.abs(t.y-n.y):0;return Math.sqrt(Math.pow(a,2)+Math.pow(o,2))}}function u(t,e,i){var n=a(e,t);i.axis=i.axis||"x";var o=l(i.axis),u=i.intersect?r(t,n):s(t,n,!1,o),d=[];return u.length?(t.data.datasets.forEach(function(e,i){if(t.isDatasetVisible(i)){var n=t.getDatasetMeta(i).data[u[0]._index];n&&!n._view.skip&&d.push(n)}}),d):[]}e.exports={modes:{single:function(t,e){var i=a(e,t),n=[];return o(t,function(t){if(t.inRange(i.x,i.y))return n.push(t),n}),n.slice(0,1)},label:u,index:u,dataset:function(t,e,i){var n=a(e,t);i.axis=i.axis||"xy";var o=l(i.axis),u=i.intersect?r(t,n):s(t,n,!1,o);return u.length>0&&(u=t.getDatasetMeta(u[0]._datasetIndex).data),u},"x-axis":function(t,e){return u(t,e,{intersect:!1})},point:function(t,e){return r(t,a(e,t))},nearest:function(t,e,i){var n=a(e,t);i.axis=i.axis||"xy";var o=l(i.axis),r=s(t,n,i.intersect,o);return r.length>1&&r.sort(function(t,e){var i=t.getArea()-e.getArea();return 0===i&&(i=t._datasetIndex-e._datasetIndex),i}),r.slice(0,1)},x:function(t,e,i){var n=a(e,t),r=[],s=!1;return o(t,function(t){t.inXRange(n.x)&&r.push(t),t.inRange(n.x,n.y)&&(s=!0)}),i.intersect&&!s&&(r=[]),r},y:function(t,e,i){var n=a(e,t),r=[],s=!1;return o(t,function(t){t.inYRange(n.y)&&r.push(t),t.inRange(n.x,n.y)&&(s=!0)}),i.intersect&&!s&&(r=[]),r}}}},{45:45}],29:[function(t,e,i){"use strict";t(25)._set("global",{responsive:!0,responsiveAnimationDuration:0,maintainAspectRatio:!0,events:["mousemove","mouseout","click","touchstart","touchmove"],hover:{onHover:null,mode:"nearest",intersect:!0,animationDuration:400},onClick:null,defaultColor:"rgba(0,0,0,0.1)",defaultFontColor:"#666",defaultFontFamily:"'Helvetica Neue', 'Helvetica', 'Arial', sans-serif",defaultFontSize:12,defaultFontStyle:"normal",showLines:!0,elements:{},layout:{padding:{top:0,right:0,bottom:0,left:0}}}),e.exports=function(){var t=function(t,e){return this.construct(t,e),this};return t.Chart=t,t}},{25:25}],30:[function(t,e,i){"use strict";var n=t(45);function a(t,e){return n.where(t,function(t){return t.position===e})}function o(t,e){t.forEach(function(t,e){return t._tmpIndex_=e,t}),t.sort(function(t,i){var n=e?i:t,a=e?t:i;return n.weight===a.weight?n._tmpIndex_-a._tmpIndex_:n.weight-a.weight}),t.forEach(function(t){delete t._tmpIndex_})}e.exports={defaults:{},addBox:function(t,e){t.boxes||(t.boxes=[]),e.fullWidth=e.fullWidth||!1,e.position=e.position||"top",e.weight=e.weight||0,t.boxes.push(e)},removeBox:function(t,e){var i=t.boxes?t.boxes.indexOf(e):-1;-1!==i&&t.boxes.splice(i,1)},configure:function(t,e,i){for(var n,a=["fullWidth","position","weight"],o=a.length,r=0;r<o;++r)n=a[r],i.hasOwnProperty(n)&&(e[n]=i[n])},update:function(t,e,i){if(t){var r=t.options.layout||{},s=n.options.toPadding(r.padding),l=s.left,u=s.right,d=s.top,c=s.bottom,h=a(t.boxes,"left"),f=a(t.boxes,"right"),g=a(t.boxes,"top"),p=a(t.boxes,"bottom"),m=a(t.boxes,"chartArea");o(h,!0),o(f,!1),o(g,!0),o(p,!1);var v=e-l-u,b=i-d-c,x=b/2,y=(e-v/2)/(h.length+f.length),k=(i-x)/(g.length+p.length),M=v,w=b,S=[];n.each(h.concat(f,g,p),function(t){var e,i=t.isHorizontal();i?(e=t.update(t.fullWidth?v:M,k),w-=e.height):(e=t.update(y,w),M-=e.width),S.push({horizontal:i,minSize:e,box:t})});var C=0,_=0,D=0,I=0;n.each(g.concat(p),function(t){if(t.getPadding){var e=t.getPadding();C=Math.max(C,e.left),_=Math.max(_,e.right)}}),n.each(h.concat(f),function(t){if(t.getPadding){var e=t.getPadding();D=Math.max(D,e.top),I=Math.max(I,e.bottom)}});var P=l,A=u,T=d,F=c;n.each(h.concat(f),N),n.each(h,function(t){P+=t.width}),n.each(f,function(t){A+=t.width}),n.each(g.concat(p),N),n.each(g,function(t){T+=t.height}),n.each(p,function(t){F+=t.height}),n.each(h.concat(f),function(t){var e=n.findNextWhere(S,function(e){return e.box===t}),i={left:0,right:0,top:T,bottom:F};e&&t.update(e.minSize.width,w,i)}),P=l,A=u,T=d,F=c,n.each(h,function(t){P+=t.width}),n.each(f,function(t){A+=t.width}),n.each(g,function(t){T+=t.height}),n.each(p,function(t){F+=t.height});var O=Math.max(C-P,0);P+=O,A+=Math.max(_-A,0);var R=Math.max(D-T,0);T+=R,F+=Math.max(I-F,0);var L=i-T-F,z=e-P-A;z===M&&L===w||(n.each(h,function(t){t.height=L}),n.each(f,function(t){t.height=L}),n.each(g,function(t){t.fullWidth||(t.width=z)}),n.each(p,function(t){t.fullWidth||(t.width=z)}),w=L,M=z);var B=l+O,W=d+R;n.each(h.concat(g),V),B+=M,W+=w,n.each(f,V),n.each(p,V),t.chartArea={left:P,top:T,right:P+M,bottom:T+w},n.each(m,function(e){e.left=t.chartArea.left,e.top=t.chartArea.top,e.right=t.chartArea.right,e.bottom=t.chartArea.bottom,e.update(M,w)})}function N(t){var e=n.findNextWhere(S,function(e){return e.box===t});if(e)if(t.isHorizontal()){var i={left:Math.max(P,C),right:Math.max(A,_),top:0,bottom:0};t.update(t.fullWidth?v:M,b/2,i)}else t.update(e.minSize.width,w)}function V(t){t.isHorizontal()?(t.left=t.fullWidth?l:P,t.right=t.fullWidth?e-u:P+M,t.top=W,t.bottom=W+t.height,W=t.bottom):(t.left=B,t.right=B+t.width,t.top=T,t.bottom=T+w,B=t.right)}}}},{45:45}],31:[function(t,e,i){"use strict";var n=t(25),a=t(45);n._set("global",{plugins:{}}),e.exports={_plugins:[],_cacheId:0,register:function(t){var e=this._plugins;[].concat(t).forEach(function(t){-1===e.indexOf(t)&&e.push(t)}),this._cacheId++},unregister:function(t){var e=this._plugins;[].concat(t).forEach(function(t){var i=e.indexOf(t);-1!==i&&e.splice(i,1)}),this._cacheId++},clear:function(){this._plugins=[],this._cacheId++},count:function(){return this._plugins.length},getAll:function(){return this._plugins},notify:function(t,e,i){var n,a,o,r,s,l=this.descriptors(t),u=l.length;for(n=0;n<u;++n)if("function"==typeof(s=(o=(a=l[n]).plugin)[e])&&((r=[t].concat(i||[])).push(a.options),!1===s.apply(o,r)))return!1;return!0},descriptors:function(t){var e=t.$plugins||(t.$plugins={});if(e.id===this._cacheId)return e.descriptors;var i=[],o=[],r=t&&t.config||{},s=r.options&&r.options.plugins||{};return this._plugins.concat(r.plugins||[]).forEach(function(t){if(-1===i.indexOf(t)){var e=t.id,r=s[e];!1!==r&&(!0===r&&(r=a.clone(n.global.plugins[e])),i.push(t),o.push({plugin:t,options:r||{}}))}}),e.descriptors=o,e.id=this._cacheId,o},_invalidate:function(t){delete t.$plugins}}},{25:25,45:45}],32:[function(t,e,i){"use strict";var n=t(25),a=t(26),o=t(45),r=t(34);function s(t){var e,i,n=[];for(e=0,i=t.length;e<i;++e)n.push(t[e].label);return n}function l(t,e,i){var n=t.getPixelForTick(e);return i&&(n-=0===e?(t.getPixelForTick(1)-n)/2:(n-t.getPixelForTick(e-1))/2),n}n._set("scale",{display:!0,position:"left",offset:!1,gridLines:{display:!0,color:"rgba(0, 0, 0, 0.1)",lineWidth:1,drawBorder:!0,drawOnChartArea:!0,drawTicks:!0,tickMarkLength:10,zeroLineWidth:1,zeroLineColor:"rgba(0,0,0,0.25)",zeroLineBorderDash:[],zeroLineBorderDashOffset:0,offsetGridLines:!1,borderDash:[],borderDashOffset:0},scaleLabel:{display:!1,labelString:"",lineHeight:1.2,padding:{top:4,bottom:4}},ticks:{beginAtZero:!1,minRotation:0,maxRotation:50,mirror:!1,padding:0,reverse:!1,display:!0,autoSkip:!0,autoSkipPadding:0,labelOffset:0,callback:r.formatters.values,minor:{},major:{}}}),e.exports=function(t){function e(t,e,i){return o.isArray(e)?o.longestText(t,i,e):t.measureText(e).width}function i(t){var e=o.valueOrDefault,i=n.global,a=e(t.fontSize,i.defaultFontSize),r=e(t.fontStyle,i.defaultFontStyle),s=e(t.fontFamily,i.defaultFontFamily);return{size:a,style:r,family:s,font:o.fontString(a,r,s)}}function r(t){return o.options.toLineHeight(o.valueOrDefault(t.lineHeight,1.2),o.valueOrDefault(t.fontSize,n.global.defaultFontSize))}t.Scale=a.extend({getPadding:function(){return{left:this.paddingLeft||0,top:this.paddingTop||0,right:this.paddingRight||0,bottom:this.paddingBottom||0}},getTicks:function(){return this._ticks},mergeTicksOptions:function(){var t=this.options.ticks;for(var e in!1===t.minor&&(t.minor={display:!1}),!1===t.major&&(t.major={display:!1}),t)"major"!==e&&"minor"!==e&&(void 0===t.minor[e]&&(t.minor[e]=t[e]),void 0===t.major[e]&&(t.major[e]=t[e]))},beforeUpdate:function(){o.callback(this.options.beforeUpdate,[this])},update:function(t,e,i){var n,a,r,s,l,u,d=this;for(d.beforeUpdate(),d.maxWidth=t,d.maxHeight=e,d.margins=o.extend({left:0,right:0,top:0,bottom:0},i),d.longestTextCache=d.longestTextCache||{},d.beforeSetDimensions(),d.setDimensions(),d.afterSetDimensions(),d.beforeDataLimits(),d.determineDataLimits(),d.afterDataLimits(),d.beforeBuildTicks(),l=d.buildTicks()||[],d.afterBuildTicks(),d.beforeTickToLabelConversion(),r=d.convertTicksToLabels(l)||d.ticks,d.afterTickToLabelConversion(),d.ticks=r,n=0,a=r.length;n<a;++n)s=r[n],(u=l[n])?u.label=s:l.push(u={label:s,major:!1});return d._ticks=l,d.beforeCalculateTickRotation(),d.calculateTickRotation(),d.afterCalculateTickRotation(),d.beforeFit(),d.fit(),d.afterFit(),d.afterUpdate(),d.minSize},afterUpdate:function(){o.callback(this.options.afterUpdate,[this])},beforeSetDimensions:function(){o.callback(this.options.beforeSetDimensions,[this])},setDimensions:function(){var t=this;t.isHorizontal()?(t.width=t.maxWidth,t.left=0,t.right=t.width):(t.height=t.maxHeight,t.top=0,t.bottom=t.height),t.paddingLeft=0,t.paddingTop=0,t.paddingRight=0,t.paddingBottom=0},afterSetDimensions:function(){o.callback(this.options.afterSetDimensions,[this])},beforeDataLimits:function(){o.callback(this.options.beforeDataLimits,[this])},determineDataLimits:o.noop,afterDataLimits:function(){o.callback(this.options.afterDataLimits,[this])},beforeBuildTicks:function(){o.callback(this.options.beforeBuildTicks,[this])},buildTicks:o.noop,afterBuildTicks:function(){o.callback(this.options.afterBuildTicks,[this])},beforeTickToLabelConversion:function(){o.callback(this.options.beforeTickToLabelConversion,[this])},convertTicksToLabels:function(){var t=this.options.ticks;this.ticks=this.ticks.map(t.userCallback||t.callback,this)},afterTickToLabelConversion:function(){o.callback(this.options.afterTickToLabelConversion,[this])},beforeCalculateTickRotation:function(){o.callback(this.options.beforeCalculateTickRotation,[this])},calculateTickRotation:function(){var t=this,e=t.ctx,n=t.options.ticks,a=s(t._ticks),r=i(n);e.font=r.font;var l=n.minRotation||0;if(a.length&&t.options.display&&t.isHorizontal())for(var u,d=o.longestText(e,r.font,a,t.longestTextCache),c=d,h=t.getPixelForTick(1)-t.getPixelForTick(0)-6;c>h&&l<n.maxRotation;){var f=o.toRadians(l);if(u=Math.cos(f),Math.sin(f)*d>t.maxHeight){l--;break}l++,c=u*d}t.labelRotation=l},afterCalculateTickRotation:function(){o.callback(this.options.afterCalculateTickRotation,[this])},beforeFit:function(){o.callback(this.options.beforeFit,[this])},fit:function(){var t=this,n=t.minSize={width:0,height:0},a=s(t._ticks),l=t.options,u=l.ticks,d=l.scaleLabel,c=l.gridLines,h=l.display,f=t.isHorizontal(),g=i(u),p=l.gridLines.tickMarkLength;if(n.width=f?t.isFullWidth()?t.maxWidth-t.margins.left-t.margins.right:t.maxWidth:h&&c.drawTicks?p:0,n.height=f?h&&c.drawTicks?p:0:t.maxHeight,d.display&&h){var m=r(d)+o.options.toPadding(d.padding).height;f?n.height+=m:n.width+=m}if(u.display&&h){var v=o.longestText(t.ctx,g.font,a,t.longestTextCache),b=o.numberOfLabelLines(a),x=.5*g.size,y=t.options.ticks.padding;if(f){t.longestLabelWidth=v;var k=o.toRadians(t.labelRotation),M=Math.cos(k),w=Math.sin(k)*v+g.size*b+x*(b-1)+x;n.height=Math.min(t.maxHeight,n.height+w+y),t.ctx.font=g.font;var S=e(t.ctx,a[0],g.font),C=e(t.ctx,a[a.length-1],g.font);0!==t.labelRotation?(t.paddingLeft="bottom"===l.position?M*S+3:M*x+3,t.paddingRight="bottom"===l.position?M*x+3:M*C+3):(t.paddingLeft=S/2+3,t.paddingRight=C/2+3)}else u.mirror?v=0:v+=y+x,n.width=Math.min(t.maxWidth,n.width+v),t.paddingTop=g.size/2,t.paddingBottom=g.size/2}t.handleMargins(),t.width=n.width,t.height=n.height},handleMargins:function(){var t=this;t.margins&&(t.paddingLeft=Math.max(t.paddingLeft-t.margins.left,0),t.paddingTop=Math.max(t.paddingTop-t.margins.top,0),t.paddingRight=Math.max(t.paddingRight-t.margins.right,0),t.paddingBottom=Math.max(t.paddingBottom-t.margins.bottom,0))},afterFit:function(){o.callback(this.options.afterFit,[this])},isHorizontal:function(){return"top"===this.options.position||"bottom"===this.options.position},isFullWidth:function(){return this.options.fullWidth},getRightValue:function(t){if(o.isNullOrUndef(t))return NaN;if("number"==typeof t&&!isFinite(t))return NaN;if(t)if(this.isHorizontal()){if(void 0!==t.x)return this.getRightValue(t.x)}else if(void 0!==t.y)return this.getRightValue(t.y);return t},getLabelForIndex:o.noop,getPixelForValue:o.noop,getValueForPixel:o.noop,getPixelForTick:function(t){var e=this,i=e.options.offset;if(e.isHorizontal()){var n=(e.width-(e.paddingLeft+e.paddingRight))/Math.max(e._ticks.length-(i?0:1),1),a=n*t+e.paddingLeft;i&&(a+=n/2);var o=e.left+Math.round(a);return o+=e.isFullWidth()?e.margins.left:0}var r=e.height-(e.paddingTop+e.paddingBottom);return e.top+t*(r/(e._ticks.length-1))},getPixelForDecimal:function(t){var e=this;if(e.isHorizontal()){var i=(e.width-(e.paddingLeft+e.paddingRight))*t+e.paddingLeft,n=e.left+Math.round(i);return n+=e.isFullWidth()?e.margins.left:0}return e.top+t*e.height},getBasePixel:function(){return this.getPixelForValue(this.getBaseValue())},getBaseValue:function(){var t=this.min,e=this.max;return this.beginAtZero?0:t<0&&e<0?e:t>0&&e>0?t:0},_autoSkip:function(t){var e,i,n,a,r=this,s=r.isHorizontal(),l=r.options.ticks.minor,u=t.length,d=o.toRadians(r.labelRotation),c=Math.cos(d),h=r.longestLabelWidth*c,f=[];for(l.maxTicksLimit&&(a=l.maxTicksLimit),s&&(e=!1,(h+l.autoSkipPadding)*u>r.width-(r.paddingLeft+r.paddingRight)&&(e=1+Math.floor((h+l.autoSkipPadding)*u/(r.width-(r.paddingLeft+r.paddingRight)))),a&&u>a&&(e=Math.max(e,Math.floor(u/a)))),i=0;i<u;i++)n=t[i],(e>1&&i%e>0||i%e==0&&i+e>=u)&&i!==u-1&&delete n.label,f.push(n);return f},draw:function(t){var e=this,a=e.options;if(a.display){var s=e.ctx,u=n.global,d=a.ticks.minor,c=a.ticks.major||d,h=a.gridLines,f=a.scaleLabel,g=0!==e.labelRotation,p=e.isHorizontal(),m=d.autoSkip?e._autoSkip(e.getTicks()):e.getTicks(),v=o.valueOrDefault(d.fontColor,u.defaultFontColor),b=i(d),x=o.valueOrDefault(c.fontColor,u.defaultFontColor),y=i(c),k=h.drawTicks?h.tickMarkLength:0,M=o.valueOrDefault(f.fontColor,u.defaultFontColor),w=i(f),S=o.options.toPadding(f.padding),C=o.toRadians(e.labelRotation),_=[],D=e.options.gridLines.lineWidth,I="right"===a.position?e.right:e.right-D-k,P="right"===a.position?e.right+k:e.right,A="bottom"===a.position?e.top+D:e.bottom-k-D,T="bottom"===a.position?e.top+D+k:e.bottom+D;if(o.each(m,function(i,n){if(!o.isNullOrUndef(i.label)){var r,s,c,f,v,b,x,y,M,w,S,F,O,R,L=i.label;n===e.zeroLineIndex&&a.offset===h.offsetGridLines?(r=h.zeroLineWidth,s=h.zeroLineColor,c=h.zeroLineBorderDash,f=h.zeroLineBorderDashOffset):(r=o.valueAtIndexOrDefault(h.lineWidth,n),s=o.valueAtIndexOrDefault(h.color,n),c=o.valueOrDefault(h.borderDash,u.borderDash),f=o.valueOrDefault(h.borderDashOffset,u.borderDashOffset));var z="middle",B="middle",W=d.padding;if(p){var N=k+W;"bottom"===a.position?(B=g?"middle":"top",z=g?"right":"center",R=e.top+N):(B=g?"middle":"bottom",z=g?"left":"center",R=e.bottom-N);var V=l(e,n,h.offsetGridLines&&m.length>1);V<e.left&&(s="rgba(0,0,0,0)"),V+=o.aliasPixel(r),O=e.getPixelForTick(n)+d.labelOffset,v=x=M=S=V,b=A,y=T,w=t.top,F=t.bottom+D}else{var E,H="left"===a.position;d.mirror?(z=H?"left":"right",E=W):(z=H?"right":"left",E=k+W),O=H?e.right-E:e.left+E;var j=l(e,n,h.offsetGridLines&&m.length>1);j<e.top&&(s="rgba(0,0,0,0)"),j+=o.aliasPixel(r),R=e.getPixelForTick(n)+d.labelOffset,v=I,x=P,M=t.left,S=t.right+D,b=y=w=F=j}_.push({tx1:v,ty1:b,tx2:x,ty2:y,x1:M,y1:w,x2:S,y2:F,labelX:O,labelY:R,glWidth:r,glColor:s,glBorderDash:c,glBorderDashOffset:f,rotation:-1*C,label:L,major:i.major,textBaseline:B,textAlign:z})}}),o.each(_,function(t){if(h.display&&(s.save(),s.lineWidth=t.glWidth,s.strokeStyle=t.glColor,s.setLineDash&&(s.setLineDash(t.glBorderDash),s.lineDashOffset=t.glBorderDashOffset),s.beginPath(),h.drawTicks&&(s.moveTo(t.tx1,t.ty1),s.lineTo(t.tx2,t.ty2)),h.drawOnChartArea&&(s.moveTo(t.x1,t.y1),s.lineTo(t.x2,t.y2)),s.stroke(),s.restore()),d.display){s.save(),s.translate(t.labelX,t.labelY),s.rotate(t.rotation),s.font=t.major?y.font:b.font,s.fillStyle=t.major?x:v,s.textBaseline=t.textBaseline,s.textAlign=t.textAlign;var i=t.label;if(o.isArray(i))for(var n=i.length,a=1.5*b.size,r=e.isHorizontal()?0:-a*(n-1)/2,l=0;l<n;++l)s.fillText(""+i[l],0,r),r+=a;else s.fillText(i,0,0);s.restore()}}),f.display){var F,O,R=0,L=r(f)/2;if(p)F=e.left+(e.right-e.left)/2,O="bottom"===a.position?e.bottom-L-S.bottom:e.top+L+S.top;else{var z="left"===a.position;F=z?e.left+L+S.top:e.right-L-S.top,O=e.top+(e.bottom-e.top)/2,R=z?-.5*Math.PI:.5*Math.PI}s.save(),s.translate(F,O),s.rotate(R),s.textAlign="center",s.textBaseline="middle",s.fillStyle=M,s.font=w.font,s.fillText(f.labelString,0,0),s.restore()}if(h.drawBorder){s.lineWidth=o.valueAtIndexOrDefault(h.lineWidth,0),s.strokeStyle=o.valueAtIndexOrDefault(h.color,0);var B=e.left,W=e.right+D,N=e.top,V=e.bottom+D,E=o.aliasPixel(s.lineWidth);p?(N=V="top"===a.position?e.bottom:e.top,N+=E,V+=E):(B=W="left"===a.position?e.right:e.left,B+=E,W+=E),s.beginPath(),s.moveTo(B,N),s.lineTo(W,V),s.stroke()}}}})}},{25:25,26:26,34:34,45:45}],33:[function(t,e,i){"use strict";var n=t(25),a=t(45),o=t(30);e.exports=function(t){t.scaleService={constructors:{},defaults:{},registerScaleType:function(t,e,i){this.constructors[t]=e,this.defaults[t]=a.clone(i)},getScaleConstructor:function(t){return this.constructors.hasOwnProperty(t)?this.constructors[t]:void 0},getScaleDefaults:function(t){return this.defaults.hasOwnProperty(t)?a.merge({},[n.scale,this.defaults[t]]):{}},updateScaleDefaults:function(t,e){this.defaults.hasOwnProperty(t)&&(this.defaults[t]=a.extend(this.defaults[t],e))},addScalesToLayout:function(t){a.each(t.scales,function(e){e.fullWidth=e.options.fullWidth,e.position=e.options.position,e.weight=e.options.weight,o.addBox(t,e)})}}}},{25:25,30:30,45:45}],34:[function(t,e,i){"use strict";var n=t(45);e.exports={formatters:{values:function(t){return n.isArray(t)?t:""+t},linear:function(t,e,i){var a=i.length>3?i[2]-i[1]:i[1]-i[0];Math.abs(a)>1&&t!==Math.floor(t)&&(a=t-Math.floor(t));var o=n.log10(Math.abs(a)),r="";if(0!==t){var s=-1*Math.floor(o);s=Math.max(Math.min(s,20),0),r=t.toFixed(s)}else r="0";return r},logarithmic:function(t,e,i){var a=t/Math.pow(10,Math.floor(n.log10(t)));return 0===t?"0":1===a||2===a||5===a||0===e||e===i.length-1?t.toExponential():""}}}},{45:45}],35:[function(t,e,i){"use strict";var n=t(25),a=t(26),o=t(45);n._set("global",{tooltips:{enabled:!0,custom:null,mode:"nearest",position:"average",intersect:!0,backgroundColor:"rgba(0,0,0,0.8)",titleFontStyle:"bold",titleSpacing:2,titleMarginBottom:6,titleFontColor:"#fff",titleAlign:"left",bodySpacing:2,bodyFontColor:"#fff",bodyAlign:"left",footerFontStyle:"bold",footerSpacing:2,footerMarginTop:6,footerFontColor:"#fff",footerAlign:"left",yPadding:6,xPadding:6,caretPadding:2,caretSize:5,cornerRadius:6,multiKeyBackground:"#fff",displayColors:!0,borderColor:"rgba(0,0,0,0)",borderWidth:0,callbacks:{beforeTitle:o.noop,title:function(t,e){var i="",n=e.labels,a=n?n.length:0;if(t.length>0){var o=t[0];o.xLabel?i=o.xLabel:a>0&&o.index<a&&(i=n[o.index])}return i},afterTitle:o.noop,beforeBody:o.noop,beforeLabel:o.noop,label:function(t,e){var i=e.datasets[t.datasetIndex].label||"";return i&&(i+=": "),i+=t.yLabel},labelColor:function(t,e){var i=e.getDatasetMeta(t.datasetIndex).data[t.index]._view;return{borderColor:i.borderColor,backgroundColor:i.backgroundColor}},labelTextColor:function(){return this._options.bodyFontColor},afterLabel:o.noop,afterBody:o.noop,beforeFooter:o.noop,footer:o.noop,afterFooter:o.noop}}}),e.exports=function(t){function e(t,e){var i=o.color(t);return i.alpha(e*i.alpha()).rgbaString()}function i(t,e){return e&&(o.isArray(e)?Array.prototype.push.apply(t,e):t.push(e)),t}function r(t){var e=n.global,i=o.valueOrDefault;return{xPadding:t.xPadding,yPadding:t.yPadding,xAlign:t.xAlign,yAlign:t.yAlign,bodyFontColor:t.bodyFontColor,_bodyFontFamily:i(t.bodyFontFamily,e.defaultFontFamily),_bodyFontStyle:i(t.bodyFontStyle,e.defaultFontStyle),_bodyAlign:t.bodyAlign,bodyFontSize:i(t.bodyFontSize,e.defaultFontSize),bodySpacing:t.bodySpacing,titleFontColor:t.titleFontColor,_titleFontFamily:i(t.titleFontFamily,e.defaultFontFamily),_titleFontStyle:i(t.titleFontStyle,e.defaultFontStyle),titleFontSize:i(t.titleFontSize,e.defaultFontSize),_titleAlign:t.titleAlign,titleSpacing:t.titleSpacing,titleMarginBottom:t.titleMarginBottom,footerFontColor:t.footerFontColor,_footerFontFamily:i(t.footerFontFamily,e.defaultFontFamily),_footerFontStyle:i(t.footerFontStyle,e.defaultFontStyle),footerFontSize:i(t.footerFontSize,e.defaultFontSize),_footerAlign:t.footerAlign,footerSpacing:t.footerSpacing,footerMarginTop:t.footerMarginTop,caretSize:t.caretSize,cornerRadius:t.cornerRadius,backgroundColor:t.backgroundColor,opacity:0,legendColorBackground:t.multiKeyBackground,displayColors:t.displayColors,borderColor:t.borderColor,borderWidth:t.borderWidth}}t.Tooltip=a.extend({initialize:function(){this._model=r(this._options),this._lastActive=[]},getTitle:function(){var t=this._options.callbacks,e=t.beforeTitle.apply(this,arguments),n=t.title.apply(this,arguments),a=t.afterTitle.apply(this,arguments),o=[];return o=i(o=i(o=i(o,e),n),a)},getBeforeBody:function(){var t=this._options.callbacks.beforeBody.apply(this,arguments);return o.isArray(t)?t:void 0!==t?[t]:[]},getBody:function(t,e){var n=this,a=n._options.callbacks,r=[];return o.each(t,function(t){var o={before:[],lines:[],after:[]};i(o.before,a.beforeLabel.call(n,t,e)),i(o.lines,a.label.call(n,t,e)),i(o.after,a.afterLabel.call(n,t,e)),r.push(o)}),r},getAfterBody:function(){var t=this._options.callbacks.afterBody.apply(this,arguments);return o.isArray(t)?t:void 0!==t?[t]:[]},getFooter:function(){var t=this._options.callbacks,e=t.beforeFooter.apply(this,arguments),n=t.footer.apply(this,arguments),a=t.afterFooter.apply(this,arguments),o=[];return o=i(o=i(o=i(o,e),n),a)},update:function(e){var i,n,a,s,l,u,d,c,h,f,g,p,m,v,b,x,y,k,M,w,S=this,C=S._options,_=S._model,D=S._model=r(C),I=S._active,P=S._data,A={xAlign:_.xAlign,yAlign:_.yAlign},T={x:_.x,y:_.y},F={width:_.width,height:_.height},O={x:_.caretX,y:_.caretY};if(I.length){D.opacity=1;var R=[],L=[];O=t.Tooltip.positioners[C.position].call(S,I,S._eventPosition);var z=[];for(i=0,n=I.length;i<n;++i)z.push((x=I[i],y=void 0,k=void 0,void 0,void 0,y=x._xScale,k=x._yScale||x._scale,M=x._index,w=x._datasetIndex,{xLabel:y?y.getLabelForIndex(M,w):"",yLabel:k?k.getLabelForIndex(M,w):"",index:M,datasetIndex:w,x:x._model.x,y:x._model.y}));C.filter&&(z=z.filter(function(t){return C.filter(t,P)})),C.itemSort&&(z=z.sort(function(t,e){return C.itemSort(t,e,P)})),o.each(z,function(t){R.push(C.callbacks.labelColor.call(S,t,S._chart)),L.push(C.callbacks.labelTextColor.call(S,t,S._chart))}),D.title=S.getTitle(z,P),D.beforeBody=S.getBeforeBody(z,P),D.body=S.getBody(z,P),D.afterBody=S.getAfterBody(z,P),D.footer=S.getFooter(z,P),D.x=Math.round(O.x),D.y=Math.round(O.y),D.caretPadding=C.caretPadding,D.labelColors=R,D.labelTextColors=L,D.dataPoints=z,A=function(t,e){var i,n,a,o,r,s=t._model,l=t._chart,u=t._chart.chartArea,d="center",c="center";s.y<e.height?c="top":s.y>l.height-e.height&&(c="bottom");var h=(u.left+u.right)/2,f=(u.top+u.bottom)/2;"center"===c?(i=function(t){return t<=h},n=function(t){return t>h}):(i=function(t){return t<=e.width/2},n=function(t){return t>=l.width-e.width/2}),a=function(t){return t+e.width+s.caretSize+s.caretPadding>l.width},o=function(t){return t-e.width-s.caretSize-s.caretPadding<0},r=function(t){return t<=f?"top":"bottom"},i(s.x)?(d="left",a(s.x)&&(d="center",c=r(s.y))):n(s.x)&&(d="right",o(s.x)&&(d="center",c=r(s.y)));var g=t._options;return{xAlign:g.xAlign?g.xAlign:d,yAlign:g.yAlign?g.yAlign:c}}(this,F=function(t,e){var i=t._chart.ctx,n=2*e.yPadding,a=0,r=e.body,s=r.reduce(function(t,e){return t+e.before.length+e.lines.length+e.after.length},0);s+=e.beforeBody.length+e.afterBody.length;var l=e.title.length,u=e.footer.length,d=e.titleFontSize,c=e.bodyFontSize,h=e.footerFontSize;n+=l*d,n+=l?(l-1)*e.titleSpacing:0,n+=l?e.titleMarginBottom:0,n+=s*c,n+=s?(s-1)*e.bodySpacing:0,n+=u?e.footerMarginTop:0,n+=u*h,n+=u?(u-1)*e.footerSpacing:0;var f=0,g=function(t){a=Math.max(a,i.measureText(t).width+f)};return i.font=o.fontString(d,e._titleFontStyle,e._titleFontFamily),o.each(e.title,g),i.font=o.fontString(c,e._bodyFontStyle,e._bodyFontFamily),o.each(e.beforeBody.concat(e.afterBody),g),f=e.displayColors?c+2:0,o.each(r,function(t){o.each(t.before,g),o.each(t.lines,g),o.each(t.after,g)}),f=0,i.font=o.fontString(h,e._footerFontStyle,e._footerFontFamily),o.each(e.footer,g),{width:a+=2*e.xPadding,height:n}}(this,D)),a=D,s=F,l=A,u=S._chart,d=a.x,c=a.y,h=a.caretSize,f=a.caretPadding,g=a.cornerRadius,p=l.xAlign,m=l.yAlign,v=h+f,b=g+f,"right"===p?d-=s.width:"center"===p&&((d-=s.width/2)+s.width>u.width&&(d=u.width-s.width),d<0&&(d=0)),"top"===m?c+=v:c-="bottom"===m?s.height+v:s.height/2,"center"===m?"left"===p?d+=v:"right"===p&&(d-=v):"left"===p?d-=b:"right"===p&&(d+=b),T={x:d,y:c}}else D.opacity=0;return D.xAlign=A.xAlign,D.yAlign=A.yAlign,D.x=T.x,D.y=T.y,D.width=F.width,D.height=F.height,D.caretX=O.x,D.caretY=O.y,S._model=D,e&&C.custom&&C.custom.call(S,D),S},drawCaret:function(t,e){var i=this._chart.ctx,n=this._view,a=this.getCaretPosition(t,e,n);i.lineTo(a.x1,a.y1),i.lineTo(a.x2,a.y2),i.lineTo(a.x3,a.y3)},getCaretPosition:function(t,e,i){var n,a,o,r,s,l,u=i.caretSize,d=i.cornerRadius,c=i.xAlign,h=i.yAlign,f=t.x,g=t.y,p=e.width,m=e.height;if("center"===h)s=g+m/2,"left"===c?(a=(n=f)-u,o=n,r=s+u,l=s-u):(a=(n=f+p)+u,o=n,r=s-u,l=s+u);else if("left"===c?(n=(a=f+d+u)-u,o=a+u):"right"===c?(n=(a=f+p-d-u)-u,o=a+u):(n=(a=i.caretX)-u,o=a+u),"top"===h)s=(r=g)-u,l=r;else{s=(r=g+m)+u,l=r;var v=o;o=n,n=v}return{x1:n,x2:a,x3:o,y1:r,y2:s,y3:l}},drawTitle:function(t,i,n,a){var r=i.title;if(r.length){n.textAlign=i._titleAlign,n.textBaseline="top";var s,l,u=i.titleFontSize,d=i.titleSpacing;for(n.fillStyle=e(i.titleFontColor,a),n.font=o.fontString(u,i._titleFontStyle,i._titleFontFamily),s=0,l=r.length;s<l;++s)n.fillText(r[s],t.x,t.y),t.y+=u+d,s+1===r.length&&(t.y+=i.titleMarginBottom-d)}},drawBody:function(t,i,n,a){var r=i.bodyFontSize,s=i.bodySpacing,l=i.body;n.textAlign=i._bodyAlign,n.textBaseline="top",n.font=o.fontString(r,i._bodyFontStyle,i._bodyFontFamily);var u=0,d=function(e){n.fillText(e,t.x+u,t.y),t.y+=r+s};n.fillStyle=e(i.bodyFontColor,a),o.each(i.beforeBody,d);var c=i.displayColors;u=c?r+2:0,o.each(l,function(s,l){var u=e(i.labelTextColors[l],a);n.fillStyle=u,o.each(s.before,d),o.each(s.lines,function(o){c&&(n.fillStyle=e(i.legendColorBackground,a),n.fillRect(t.x,t.y,r,r),n.lineWidth=1,n.strokeStyle=e(i.labelColors[l].borderColor,a),n.strokeRect(t.x,t.y,r,r),n.fillStyle=e(i.labelColors[l].backgroundColor,a),n.fillRect(t.x+1,t.y+1,r-2,r-2),n.fillStyle=u),d(o)}),o.each(s.after,d)}),u=0,o.each(i.afterBody,d),t.y-=s},drawFooter:function(t,i,n,a){var r=i.footer;r.length&&(t.y+=i.footerMarginTop,n.textAlign=i._footerAlign,n.textBaseline="top",n.fillStyle=e(i.footerFontColor,a),n.font=o.fontString(i.footerFontSize,i._footerFontStyle,i._footerFontFamily),o.each(r,function(e){n.fillText(e,t.x,t.y),t.y+=i.footerFontSize+i.footerSpacing}))},drawBackground:function(t,i,n,a,o){n.fillStyle=e(i.backgroundColor,o),n.strokeStyle=e(i.borderColor,o),n.lineWidth=i.borderWidth;var r=i.xAlign,s=i.yAlign,l=t.x,u=t.y,d=a.width,c=a.height,h=i.cornerRadius;n.beginPath(),n.moveTo(l+h,u),"top"===s&&this.drawCaret(t,a),n.lineTo(l+d-h,u),n.quadraticCurveTo(l+d,u,l+d,u+h),"center"===s&&"right"===r&&this.drawCaret(t,a),n.lineTo(l+d,u+c-h),n.quadraticCurveTo(l+d,u+c,l+d-h,u+c),"bottom"===s&&this.drawCaret(t,a),n.lineTo(l+h,u+c),n.quadraticCurveTo(l,u+c,l,u+c-h),"center"===s&&"left"===r&&this.drawCaret(t,a),n.lineTo(l,u+h),n.quadraticCurveTo(l,u,l+h,u),n.closePath(),n.fill(),i.borderWidth>0&&n.stroke()},draw:function(){var t=this._chart.ctx,e=this._view;if(0!==e.opacity){var i={width:e.width,height:e.height},n={x:e.x,y:e.y},a=Math.abs(e.opacity<.001)?0:e.opacity,o=e.title.length||e.beforeBody.length||e.body.length||e.afterBody.length||e.footer.length;this._options.enabled&&o&&(this.drawBackground(n,e,t,i,a),n.x+=e.xPadding,n.y+=e.yPadding,this.drawTitle(n,e,t,a),this.drawBody(n,e,t,a),this.drawFooter(n,e,t,a))}},handleEvent:function(t){var e,i=this,n=i._options;return i._lastActive=i._lastActive||[],"mouseout"===t.type?i._active=[]:i._active=i._chart.getElementsAtEventForMode(t,n.mode,n),(e=!o.arrayEquals(i._active,i._lastActive))&&(i._lastActive=i._active,(n.enabled||n.custom)&&(i._eventPosition={x:t.x,y:t.y},i.update(!0),i.pivot())),e}}),t.Tooltip.positioners={average:function(t){if(!t.length)return!1;var e,i,n=0,a=0,o=0;for(e=0,i=t.length;e<i;++e){var r=t[e];if(r&&r.hasValue()){var s=r.tooltipPosition();n+=s.x,a+=s.y,++o}}return{x:Math.round(n/o),y:Math.round(a/o)}},nearest:function(t,e){var i,n,a,r=e.x,s=e.y,l=Number.POSITIVE_INFINITY;for(i=0,n=t.length;i<n;++i){var u=t[i];if(u&&u.hasValue()){var d=u.getCenterPoint(),c=o.distanceBetweenPoints(e,d);c<l&&(l=c,a=u)}}if(a){var h=a.tooltipPosition();r=h.x,s=h.y}return{x:r,y:s}}}}},{25:25,26:26,45:45}],36:[function(t,e,i){"use strict";var n=t(25),a=t(26),o=t(45);n._set("global",{elements:{arc:{backgroundColor:n.global.defaultColor,borderColor:"#fff",borderWidth:2}}}),e.exports=a.extend({inLabelRange:function(t){var e=this._view;return!!e&&Math.pow(t-e.x,2)<Math.pow(e.radius+e.hoverRadius,2)},inRange:function(t,e){var i=this._view;if(i){for(var n=o.getAngleFromPoint(i,{x:t,y:e}),a=n.angle,r=n.distance,s=i.startAngle,l=i.endAngle;l<s;)l+=2*Math.PI;for(;a>l;)a-=2*Math.PI;for(;a<s;)a+=2*Math.PI;var u=a>=s&&a<=l,d=r>=i.innerRadius&&r<=i.outerRadius;return u&&d}return!1},getCenterPoint:function(){var t=this._view,e=(t.startAngle+t.endAngle)/2,i=(t.innerRadius+t.outerRadius)/2;return{x:t.x+Math.cos(e)*i,y:t.y+Math.sin(e)*i}},getArea:function(){var t=this._view;return Math.PI*((t.endAngle-t.startAngle)/(2*Math.PI))*(Math.pow(t.outerRadius,2)-Math.pow(t.innerRadius,2))},tooltipPosition:function(){var t=this._view,e=t.startAngle+(t.endAngle-t.startAngle)/2,i=(t.outerRadius-t.innerRadius)/2+t.innerRadius;return{x:t.x+Math.cos(e)*i,y:t.y+Math.sin(e)*i}},draw:function(){var t=this._chart.ctx,e=this._view,i=e.startAngle,n=e.endAngle;t.beginPath(),t.arc(e.x,e.y,e.outerRadius,i,n),t.arc(e.x,e.y,e.innerRadius,n,i,!0),t.closePath(),t.strokeStyle=e.borderColor,t.lineWidth=e.borderWidth,t.fillStyle=e.backgroundColor,t.fill(),t.lineJoin="bevel",e.borderWidth&&t.stroke()}})},{25:25,26:26,45:45}],37:[function(t,e,i){"use strict";var n=t(25),a=t(26),o=t(45),r=n.global;n._set("global",{elements:{line:{tension:.4,backgroundColor:r.defaultColor,borderWidth:3,borderColor:r.defaultColor,borderCapStyle:"butt",borderDash:[],borderDashOffset:0,borderJoinStyle:"miter",capBezierPoints:!0,fill:!0}}}),e.exports=a.extend({draw:function(){var t,e,i,n,a=this._view,s=this._chart.ctx,l=a.spanGaps,u=this._children.slice(),d=r.elements.line,c=-1;for(this._loop&&u.length&&u.push(u[0]),s.save(),s.lineCap=a.borderCapStyle||d.borderCapStyle,s.setLineDash&&s.setLineDash(a.borderDash||d.borderDash),s.lineDashOffset=a.borderDashOffset||d.borderDashOffset,s.lineJoin=a.borderJoinStyle||d.borderJoinStyle,s.lineWidth=a.borderWidth||d.borderWidth,s.strokeStyle=a.borderColor||r.defaultColor,s.beginPath(),c=-1,t=0;t<u.length;++t)e=u[t],i=o.previousItem(u,t),n=e._view,0===t?n.skip||(s.moveTo(n.x,n.y),c=t):(i=-1===c?i:u[c],n.skip||(c!==t-1&&!l||-1===c?s.moveTo(n.x,n.y):o.canvas.lineTo(s,i._view,e._view),c=t));s.stroke(),s.restore()}})},{25:25,26:26,45:45}],38:[function(t,e,i){"use strict";var n=t(25),a=t(26),o=t(45),r=n.global.defaultColor;function s(t){var e=this._view;return!!e&&Math.abs(t-e.x)<e.radius+e.hitRadius}n._set("global",{elements:{point:{radius:3,pointStyle:"circle",backgroundColor:r,borderColor:r,borderWidth:1,hitRadius:1,hoverRadius:4,hoverBorderWidth:1}}}),e.exports=a.extend({inRange:function(t,e){var i=this._view;return!!i&&Math.pow(t-i.x,2)+Math.pow(e-i.y,2)<Math.pow(i.hitRadius+i.radius,2)},inLabelRange:s,inXRange:s,inYRange:function(t){var e=this._view;return!!e&&Math.abs(t-e.y)<e.radius+e.hitRadius},getCenterPoint:function(){var t=this._view;return{x:t.x,y:t.y}},getArea:function(){return Math.PI*Math.pow(this._view.radius,2)},tooltipPosition:function(){var t=this._view;return{x:t.x,y:t.y,padding:t.radius+t.borderWidth}},draw:function(t){var e=this._view,i=this._model,a=this._chart.ctx,s=e.pointStyle,l=e.radius,u=e.x,d=e.y,c=o.color,h=0;e.skip||(a.strokeStyle=e.borderColor||r,a.lineWidth=o.valueOrDefault(e.borderWidth,n.global.elements.point.borderWidth),a.fillStyle=e.backgroundColor||r,void 0!==t&&(i.x<t.left||1.01*t.right<i.x||i.y<t.top||1.01*t.bottom<i.y)&&(i.x<t.left?h=(u-i.x)/(t.left-i.x):1.01*t.right<i.x?h=(i.x-u)/(i.x-t.right):i.y<t.top?h=(d-i.y)/(t.top-i.y):1.01*t.bottom<i.y&&(h=(i.y-d)/(i.y-t.bottom)),h=Math.round(100*h)/100,a.strokeStyle=c(a.strokeStyle).alpha(h).rgbString(),a.fillStyle=c(a.fillStyle).alpha(h).rgbString()),o.canvas.drawPoint(a,s,l,u,d))}})},{25:25,26:26,45:45}],39:[function(t,e,i){"use strict";var n=t(25),a=t(26);function o(t){return void 0!==t._view.width}function r(t){var e,i,n,a,r=t._view;if(o(t)){var s=r.width/2;e=r.x-s,i=r.x+s,n=Math.min(r.y,r.base),a=Math.max(r.y,r.base)}else{var l=r.height/2;e=Math.min(r.x,r.base),i=Math.max(r.x,r.base),n=r.y-l,a=r.y+l}return{left:e,top:n,right:i,bottom:a}}n._set("global",{elements:{rectangle:{backgroundColor:n.global.defaultColor,borderColor:n.global.defaultColor,borderSkipped:"bottom",borderWidth:0}}}),e.exports=a.extend({draw:function(){var t,e,i,n,a,o,r,s=this._chart.ctx,l=this._view,u=l.borderWidth;if(l.horizontal?(t=l.base,e=l.x,i=l.y-l.height/2,n=l.y+l.height/2,a=e>t?1:-1,o=1,r=l.borderSkipped||"left"):(t=l.x-l.width/2,e=l.x+l.width/2,i=l.y,a=1,o=(n=l.base)>i?1:-1,r=l.borderSkipped||"bottom"),u){var d=Math.min(Math.abs(t-e),Math.abs(i-n)),c=(u=u>d?d:u)/2,h=t+("left"!==r?c*a:0),f=e+("right"!==r?-c*a:0),g=i+("top"!==r?c*o:0),p=n+("bottom"!==r?-c*o:0);h!==f&&(i=g,n=p),g!==p&&(t=h,e=f)}s.beginPath(),s.fillStyle=l.backgroundColor,s.strokeStyle=l.borderColor,s.lineWidth=u;var m=[[t,n],[t,i],[e,i],[e,n]],v=["bottom","left","top","right"].indexOf(r,0);function b(t){return m[(v+t)%4]}-1===v&&(v=0);var x=b(0);s.moveTo(x[0],x[1]);for(var y=1;y<4;y++)x=b(y),s.lineTo(x[0],x[1]);s.fill(),u&&s.stroke()},height:function(){var t=this._view;return t.base-t.y},inRange:function(t,e){var i=!1;if(this._view){var n=r(this);i=t>=n.left&&t<=n.right&&e>=n.top&&e<=n.bottom}return i},inLabelRange:function(t,e){if(!this._view)return!1;var i=r(this);return o(this)?t>=i.left&&t<=i.right:e>=i.top&&e<=i.bottom},inXRange:function(t){var e=r(this);return t>=e.left&&t<=e.right},inYRange:function(t){var e=r(this);return t>=e.top&&t<=e.bottom},getCenterPoint:function(){var t,e,i=this._view;return o(this)?(t=i.x,e=(i.y+i.base)/2):(t=(i.x+i.base)/2,e=i.y),{x:t,y:e}},getArea:function(){var t=this._view;return t.width*Math.abs(t.y-t.base)},tooltipPosition:function(){var t=this._view;return{x:t.x,y:t.y}}})},{25:25,26:26}],40:[function(t,e,i){"use strict";e.exports={},e.exports.Arc=t(36),e.exports.Line=t(37),e.exports.Point=t(38),e.exports.Rectangle=t(39)},{36:36,37:37,38:38,39:39}],41:[function(t,e,i){"use strict";var n=t(42);i=e.exports={clear:function(t){t.ctx.clearRect(0,0,t.width,t.height)},roundedRect:function(t,e,i,n,a,o){if(o){var r=Math.min(o,n/2),s=Math.min(o,a/2);t.moveTo(e+r,i),t.lineTo(e+n-r,i),t.quadraticCurveTo(e+n,i,e+n,i+s),t.lineTo(e+n,i+a-s),t.quadraticCurveTo(e+n,i+a,e+n-r,i+a),t.lineTo(e+r,i+a),t.quadraticCurveTo(e,i+a,e,i+a-s),t.lineTo(e,i+s),t.quadraticCurveTo(e,i,e+r,i)}else t.rect(e,i,n,a)},drawPoint:function(t,e,i,n,a){var o,r,s,l,u,d;if(!e||"object"!=typeof e||"[object HTMLImageElement]"!==(o=e.toString())&&"[object HTMLCanvasElement]"!==o){if(!(isNaN(i)||i<=0)){switch(e){default:t.beginPath(),t.arc(n,a,i,0,2*Math.PI),t.closePath(),t.fill();break;case"triangle":t.beginPath(),u=(r=3*i/Math.sqrt(3))*Math.sqrt(3)/2,t.moveTo(n-r/2,a+u/3),t.lineTo(n+r/2,a+u/3),t.lineTo(n,a-2*u/3),t.closePath(),t.fill();break;case"rect":d=1/Math.SQRT2*i,t.beginPath(),t.fillRect(n-d,a-d,2*d,2*d),t.strokeRect(n-d,a-d,2*d,2*d);break;case"rectRounded":var c=i/Math.SQRT2,h=n-c,f=a-c,g=Math.SQRT2*i;t.beginPath(),this.roundedRect(t,h,f,g,g,i/2),t.closePath(),t.fill();break;case"rectRot":d=1/Math.SQRT2*i,t.beginPath(),t.moveTo(n-d,a),t.lineTo(n,a+d),t.lineTo(n+d,a),t.lineTo(n,a-d),t.closePath(),t.fill();break;case"cross":t.beginPath(),t.moveTo(n,a+i),t.lineTo(n,a-i),t.moveTo(n-i,a),t.lineTo(n+i,a),t.closePath();break;case"crossRot":t.beginPath(),s=Math.cos(Math.PI/4)*i,l=Math.sin(Math.PI/4)*i,t.moveTo(n-s,a-l),t.lineTo(n+s,a+l),t.moveTo(n-s,a+l),t.lineTo(n+s,a-l),t.closePath();break;case"star":t.beginPath(),t.moveTo(n,a+i),t.lineTo(n,a-i),t.moveTo(n-i,a),t.lineTo(n+i,a),s=Math.cos(Math.PI/4)*i,l=Math.sin(Math.PI/4)*i,t.moveTo(n-s,a-l),t.lineTo(n+s,a+l),t.moveTo(n-s,a+l),t.lineTo(n+s,a-l),t.closePath();break;case"line":t.beginPath(),t.moveTo(n-i,a),t.lineTo(n+i,a),t.closePath();break;case"dash":t.beginPath(),t.moveTo(n,a),t.lineTo(n+i,a),t.closePath()}t.stroke()}}else t.drawImage(e,n-e.width/2,a-e.height/2,e.width,e.height)},clipArea:function(t,e){t.save(),t.beginPath(),t.rect(e.left,e.top,e.right-e.left,e.bottom-e.top),t.clip()},unclipArea:function(t){t.restore()},lineTo:function(t,e,i,n){if(i.steppedLine)return"after"===i.steppedLine&&!n||"after"!==i.steppedLine&&n?t.lineTo(e.x,i.y):t.lineTo(i.x,e.y),void t.lineTo(i.x,i.y);i.tension?t.bezierCurveTo(n?e.controlPointPreviousX:e.controlPointNextX,n?e.controlPointPreviousY:e.controlPointNextY,n?i.controlPointNextX:i.controlPointPreviousX,n?i.controlPointNextY:i.controlPointPreviousY,i.x,i.y):t.lineTo(i.x,i.y)}};n.clear=i.clear,n.drawRoundedRectangle=function(t){t.beginPath(),i.roundedRect.apply(i,arguments),t.closePath()}},{42:42}],42:[function(t,e,i){"use strict";var n,a={noop:function(){},uid:(n=0,function(){return n++}),isNullOrUndef:function(t){return null==t},isArray:Array.isArray?Array.isArray:function(t){return"[object Array]"===Object.prototype.toString.call(t)},isObject:function(t){return null!==t&&"[object Object]"===Object.prototype.toString.call(t)},valueOrDefault:function(t,e){return void 0===t?e:t},valueAtIndexOrDefault:function(t,e,i){return a.valueOrDefault(a.isArray(t)?t[e]:t,i)},callback:function(t,e,i){if(t&&"function"==typeof t.call)return t.apply(i,e)},each:function(t,e,i,n){var o,r,s;if(a.isArray(t))if(r=t.length,n)for(o=r-1;o>=0;o--)e.call(i,t[o],o);else for(o=0;o<r;o++)e.call(i,t[o],o);else if(a.isObject(t))for(r=(s=Object.keys(t)).length,o=0;o<r;o++)e.call(i,t[s[o]],s[o])},arrayEquals:function(t,e){var i,n,o,r;if(!t||!e||t.length!==e.length)return!1;for(i=0,n=t.length;i<n;++i)if(o=t[i],r=e[i],o instanceof Array&&r instanceof Array){if(!a.arrayEquals(o,r))return!1}else if(o!==r)return!1;return!0},clone:function(t){if(a.isArray(t))return t.map(a.clone);if(a.isObject(t)){for(var e={},i=Object.keys(t),n=i.length,o=0;o<n;++o)e[i[o]]=a.clone(t[i[o]]);return e}return t},_merger:function(t,e,i,n){var o=e[t],r=i[t];a.isObject(o)&&a.isObject(r)?a.merge(o,r,n):e[t]=a.clone(r)},_mergerIf:function(t,e,i){var n=e[t],o=i[t];a.isObject(n)&&a.isObject(o)?a.mergeIf(n,o):e.hasOwnProperty(t)||(e[t]=a.clone(o))},merge:function(t,e,i){var n,o,r,s,l,u=a.isArray(e)?e:[e],d=u.length;if(!a.isObject(t))return t;for(n=(i=i||{}).merger||a._merger,o=0;o<d;++o)if(e=u[o],a.isObject(e))for(l=0,s=(r=Object.keys(e)).length;l<s;++l)n(r[l],t,e,i);return t},mergeIf:function(t,e){return a.merge(t,e,{merger:a._mergerIf})},extend:function(t){for(var e=function(e,i){t[i]=e},i=1,n=arguments.length;i<n;++i)a.each(arguments[i],e);return t},inherits:function(t){var e=this,i=t&&t.hasOwnProperty("constructor")?t.constructor:function(){return e.apply(this,arguments)},n=function(){this.constructor=i};return n.prototype=e.prototype,i.prototype=new n,i.extend=a.inherits,t&&a.extend(i.prototype,t),i.__super__=e.prototype,i}};e.exports=a,a.callCallback=a.callback,a.indexOf=function(t,e,i){return Array.prototype.indexOf.call(t,e,i)},a.getValueOrDefault=a.valueOrDefault,a.getValueAtIndexOrDefault=a.valueAtIndexOrDefault},{}],43:[function(t,e,i){"use strict";var n=t(42),a={linear:function(t){return t},easeInQuad:function(t){return t*t},easeOutQuad:function(t){return-t*(t-2)},easeInOutQuad:function(t){return(t/=.5)<1?.5*t*t:-.5*(--t*(t-2)-1)},easeInCubic:function(t){return t*t*t},easeOutCubic:function(t){return(t-=1)*t*t+1},easeInOutCubic:function(t){return(t/=.5)<1?.5*t*t*t:.5*((t-=2)*t*t+2)},easeInQuart:function(t){return t*t*t*t},easeOutQuart:function(t){return-((t-=1)*t*t*t-1)},easeInOutQuart:function(t){return(t/=.5)<1?.5*t*t*t*t:-.5*((t-=2)*t*t*t-2)},easeInQuint:function(t){return t*t*t*t*t},easeOutQuint:function(t){return(t-=1)*t*t*t*t+1},easeInOutQuint:function(t){return(t/=.5)<1?.5*t*t*t*t*t:.5*((t-=2)*t*t*t*t+2)},easeInSine:function(t){return 1-Math.cos(t*(Math.PI/2))},easeOutSine:function(t){return Math.sin(t*(Math.PI/2))},easeInOutSine:function(t){return-.5*(Math.cos(Math.PI*t)-1)},easeInExpo:function(t){return 0===t?0:Math.pow(2,10*(t-1))},easeOutExpo:function(t){return 1===t?1:1-Math.pow(2,-10*t)},easeInOutExpo:function(t){return 0===t?0:1===t?1:(t/=.5)<1?.5*Math.pow(2,10*(t-1)):.5*(2-Math.pow(2,-10*--t))},easeInCirc:function(t){return t>=1?t:-(Math.sqrt(1-t*t)-1)},easeOutCirc:function(t){return Math.sqrt(1-(t-=1)*t)},easeInOutCirc:function(t){return(t/=.5)<1?-.5*(Math.sqrt(1-t*t)-1):.5*(Math.sqrt(1-(t-=2)*t)+1)},easeInElastic:function(t){var e=1.70158,i=0,n=1;return 0===t?0:1===t?1:(i||(i=.3),n<1?(n=1,e=i/4):e=i/(2*Math.PI)*Math.asin(1/n),-n*Math.pow(2,10*(t-=1))*Math.sin((t-e)*(2*Math.PI)/i))},easeOutElastic:function(t){var e=1.70158,i=0,n=1;return 0===t?0:1===t?1:(i||(i=.3),n<1?(n=1,e=i/4):e=i/(2*Math.PI)*Math.asin(1/n),n*Math.pow(2,-10*t)*Math.sin((t-e)*(2*Math.PI)/i)+1)},easeInOutElastic:function(t){var e=1.70158,i=0,n=1;return 0===t?0:2==(t/=.5)?1:(i||(i=.45),n<1?(n=1,e=i/4):e=i/(2*Math.PI)*Math.asin(1/n),t<1?n*Math.pow(2,10*(t-=1))*Math.sin((t-e)*(2*Math.PI)/i)*-.5:n*Math.pow(2,-10*(t-=1))*Math.sin((t-e)*(2*Math.PI)/i)*.5+1)},easeInBack:function(t){return t*t*(2.70158*t-1.70158)},easeOutBack:function(t){return(t-=1)*t*(2.70158*t+1.70158)+1},easeInOutBack:function(t){var e=1.70158;return(t/=.5)<1?t*t*((1+(e*=1.525))*t-e)*.5:.5*((t-=2)*t*((1+(e*=1.525))*t+e)+2)},easeInBounce:function(t){return 1-a.easeOutBounce(1-t)},easeOutBounce:function(t){return t<1/2.75?7.5625*t*t:t<2/2.75?7.5625*(t-=1.5/2.75)*t+.75:t<2.5/2.75?7.5625*(t-=2.25/2.75)*t+.9375:7.5625*(t-=2.625/2.75)*t+.984375},easeInOutBounce:function(t){return t<.5?.5*a.easeInBounce(2*t):.5*a.easeOutBounce(2*t-1)+.5}};e.exports={effects:a},n.easingEffects=a},{42:42}],44:[function(t,e,i){"use strict";var n=t(42);e.exports={toLineHeight:function(t,e){var i=(""+t).match(/^(normal|(\d+(?:\.\d+)?)(px|em|%)?)$/);if(!i||"normal"===i[1])return 1.2*e;switch(t=+i[2],i[3]){case"px":return t;case"%":t/=100}return e*t},toPadding:function(t){var e,i,a,o;return n.isObject(t)?(e=+t.top||0,i=+t.right||0,a=+t.bottom||0,o=+t.left||0):e=i=a=o=+t||0,{top:e,right:i,bottom:a,left:o,height:e+a,width:o+i}},resolve:function(t,e,i){var a,o,r;for(a=0,o=t.length;a<o;++a)if(void 0!==(r=t[a])&&(void 0!==e&&"function"==typeof r&&(r=r(e)),void 0!==i&&n.isArray(r)&&(r=r[i]),void 0!==r))return r}}},{42:42}],45:[function(t,e,i){"use strict";e.exports=t(42),e.exports.easing=t(43),e.exports.canvas=t(41),e.exports.options=t(44)},{41:41,42:42,43:43,44:44}],46:[function(t,e,i){e.exports={acquireContext:function(t){return t&&t.canvas&&(t=t.canvas),t&&t.getContext("2d")||null}}},{}],47:[function(t,e,i){"use strict";var n=t(45),a="$chartjs",o="chartjs-",r=o+"render-monitor",s=o+"render-animation",l=["animationstart","webkitAnimationStart"],u={touchstart:"mousedown",touchmove:"mousemove",touchend:"mouseup",pointerenter:"mouseenter",pointerdown:"mousedown",pointermove:"mousemove",pointerup:"mouseup",pointerleave:"mouseout",pointerout:"mouseout"};function d(t,e){var i=n.getStyle(t,e),a=i&&i.match(/^(\d+)(\.\d+)?px$/);return a?Number(a[1]):void 0}var c=!!function(){var t=!1;try{var e=Object.defineProperty({},"passive",{get:function(){t=!0}});window.addEventListener("e",null,e)}catch(t){}return t}()&&{passive:!0};function h(t,e,i){t.addEventListener(e,i,c)}function f(t,e,i){t.removeEventListener(e,i,c)}function g(t,e,i,n,a){return{type:t,chart:e,native:a||null,x:void 0!==i?i:null,y:void 0!==n?n:null}}function p(t,e,i){var u,d,c,f,p,m,v,b,x=t[a]||(t[a]={}),y=x.resizer=function(t){var e=document.createElement("div"),i=o+"size-monitor",n="position:absolute;left:0;top:0;right:0;bottom:0;overflow:hidden;pointer-events:none;visibility:hidden;z-index:-1;";e.style.cssText=n,e.className=i,e.innerHTML='<div class="'+i+'-expand" style="'+n+'"><div style="position:absolute;width:1000000px;height:1000000px;left:0;top:0"></div></div><div class="'+i+'-shrink" style="'+n+'"><div style="position:absolute;width:200%;height:200%;left:0; top:0"></div></div>';var a=e.childNodes[0],r=e.childNodes[1];e._reset=function(){a.scrollLeft=1e6,a.scrollTop=1e6,r.scrollLeft=1e6,r.scrollTop=1e6};var s=function(){e._reset(),t()};return h(a,"scroll",s.bind(a,"expand")),h(r,"scroll",s.bind(r,"shrink")),e}((u=function(){if(x.resizer)return e(g("resize",i))},c=!1,f=[],function(){f=Array.prototype.slice.call(arguments),d=d||this,c||(c=!0,n.requestAnimFrame.call(window,function(){c=!1,u.apply(d,f)}))}));m=function(){if(x.resizer){var e=t.parentNode;e&&e!==y.parentNode&&e.insertBefore(y,e.firstChild),y._reset()}},v=(p=t)[a]||(p[a]={}),b=v.renderProxy=function(t){t.animationName===s&&m()},n.each(l,function(t){h(p,t,b)}),v.reflow=!!p.offsetParent,p.classList.add(r)}function m(t){var e,i,o,s=t[a]||{},u=s.resizer;delete s.resizer,i=(e=t)[a]||{},(o=i.renderProxy)&&(n.each(l,function(t){f(e,t,o)}),delete i.renderProxy),e.classList.remove(r),u&&u.parentNode&&u.parentNode.removeChild(u)}e.exports={_enabled:"undefined"!=typeof window&&"undefined"!=typeof document,initialize:function(){var t,e,i,n="from{opacity:0.99}to{opacity:1}";e="@-webkit-keyframes "+s+"{"+n+"}@keyframes "+s+"{"+n+"}."+r+"{-webkit-animation:"+s+" 0.001s;animation:"+s+" 0.001s;}",i=(t=this)._style||document.createElement("style"),t._style||(t._style=i,e="/* Chart.js */\n"+e,i.setAttribute("type","text/css"),document.getElementsByTagName("head")[0].appendChild(i)),i.appendChild(document.createTextNode(e))},acquireContext:function(t,e){"string"==typeof t?t=document.getElementById(t):t.length&&(t=t[0]),t&&t.canvas&&(t=t.canvas);var i=t&&t.getContext&&t.getContext("2d");return i&&i.canvas===t?(function(t,e){var i=t.style,n=t.getAttribute("height"),o=t.getAttribute("width");if(t[a]={initial:{height:n,width:o,style:{display:i.display,height:i.height,width:i.width}}},i.display=i.display||"block",null===o||""===o){var r=d(t,"width");void 0!==r&&(t.width=r)}if(null===n||""===n)if(""===t.style.height)t.height=t.width/(e.options.aspectRatio||2);else{var s=d(t,"height");void 0!==r&&(t.height=s)}}(t,e),i):null},releaseContext:function(t){var e=t.canvas;if(e[a]){var i=e[a].initial;["height","width"].forEach(function(t){var a=i[t];n.isNullOrUndef(a)?e.removeAttribute(t):e.setAttribute(t,a)}),n.each(i.style||{},function(t,i){e.style[i]=t}),e.width=e.width,delete e[a]}},addEventListener:function(t,e,i){var o=t.canvas;if("resize"!==e){var r=i[a]||(i[a]={});h(o,e,(r.proxies||(r.proxies={}))[t.id+"_"+e]=function(e){var a,o,r,s;i((o=t,r=u[(a=e).type]||a.type,s=n.getRelativePosition(a,o),g(r,o,s.x,s.y,a)))})}else p(o,i,t)},removeEventListener:function(t,e,i){var n=t.canvas;if("resize"!==e){var o=((i[a]||{}).proxies||{})[t.id+"_"+e];o&&f(n,e,o)}else m(n)}},n.addEvent=h,n.removeEvent=f},{45:45}],48:[function(t,e,i){"use strict";var n=t(45),a=t(46),o=t(47),r=o._enabled?o:a;e.exports=n.extend({initialize:function(){},acquireContext:function(){},releaseContext:function(){},addEventListener:function(){},removeEventListener:function(){}},r)},{45:45,46:46,47:47}],49:[function(t,e,i){"use strict";e.exports={},e.exports.filler=t(50),e.exports.legend=t(51),e.exports.title=t(52)},{50:50,51:51,52:52}],50:[function(t,e,i){"use strict";var n=t(25),a=t(40),o=t(45);n._set("global",{plugins:{filler:{propagate:!0}}});var r={dataset:function(t){var e=t.fill,i=t.chart,n=i.getDatasetMeta(e),a=n&&i.isDatasetVisible(e)&&n.dataset._children||[],o=a.length||0;return o?function(t,e){return e<o&&a[e]._view||null}:null},boundary:function(t){var e=t.boundary,i=e?e.x:null,n=e?e.y:null;return function(t){return{x:null===i?t.x:i,y:null===n?t.y:n}}}};function s(t,e,i){var n,a=t._model||{},o=a.fill;if(void 0===o&&(o=!!a.backgroundColor),!1===o||null===o)return!1;if(!0===o)return"origin";if(n=parseFloat(o,10),isFinite(n)&&Math.floor(n)===n)return"-"!==o[0]&&"+"!==o[0]||(n=e+n),!(n===e||n<0||n>=i)&&n;switch(o){case"bottom":return"start";case"top":return"end";case"zero":return"origin";case"origin":case"start":case"end":return o;default:return!1}}function l(t){var e,i=t.el._model||{},n=t.el._scale||{},a=t.fill,o=null;if(isFinite(a))return null;if("start"===a?o=void 0===i.scaleBottom?n.bottom:i.scaleBottom:"end"===a?o=void 0===i.scaleTop?n.top:i.scaleTop:void 0!==i.scaleZero?o=i.scaleZero:n.getBasePosition?o=n.getBasePosition():n.getBasePixel&&(o=n.getBasePixel()),null!=o){if(void 0!==o.x&&void 0!==o.y)return o;if("number"==typeof o&&isFinite(o))return{x:(e=n.isHorizontal())?o:null,y:e?null:o}}return null}function u(t,e,i){var n,a=t[e].fill,o=[e];if(!i)return a;for(;!1!==a&&-1===o.indexOf(a);){if(!isFinite(a))return a;if(!(n=t[a]))return!1;if(n.visible)return a;o.push(a),a=n.fill}return!1}function d(t){return t&&!t.skip}function c(t,e,i,n,a){var r;if(n&&a){for(t.moveTo(e[0].x,e[0].y),r=1;r<n;++r)o.canvas.lineTo(t,e[r-1],e[r]);for(t.lineTo(i[a-1].x,i[a-1].y),r=a-1;r>0;--r)o.canvas.lineTo(t,i[r],i[r-1],!0)}}e.exports={id:"filler",afterDatasetsUpdate:function(t,e){var i,n,o,d,c,h,f,g=(t.data.datasets||[]).length,p=e.propagate,m=[];for(n=0;n<g;++n)d=null,(o=(i=t.getDatasetMeta(n)).dataset)&&o._model&&o instanceof a.Line&&(d={visible:t.isDatasetVisible(n),fill:s(o,n,g),chart:t,el:o}),i.$filler=d,m.push(d);for(n=0;n<g;++n)(d=m[n])&&(d.fill=u(m,n,p),d.boundary=l(d),d.mapper=(void 0,f=void 0,h=(c=d).fill,f="dataset",!1===h?null:(isFinite(h)||(f="boundary"),r[f](c))))},beforeDatasetDraw:function(t,e){var i=e.meta.$filler;if(i){var a=t.ctx,r=i.el,s=r._view,l=r._children||[],u=i.mapper,h=s.backgroundColor||n.global.defaultColor;u&&h&&l.length&&(o.canvas.clipArea(a,t.chartArea),function(t,e,i,n,a,o){var r,s,l,u,h,f,g,p=e.length,m=n.spanGaps,v=[],b=[],x=0,y=0;for(t.beginPath(),r=0,s=p+!!o;r<s;++r)h=i(u=e[l=r%p]._view,l,n),f=d(u),g=d(h),f&&g?(x=v.push(u),y=b.push(h)):x&&y&&(m?(f&&v.push(u),g&&b.push(h)):(c(t,v,b,x,y),x=y=0,v=[],b=[]));c(t,v,b,x,y),t.closePath(),t.fillStyle=a,t.fill()}(a,l,u,s,h,r._loop),o.canvas.unclipArea(a))}}}},{25:25,40:40,45:45}],51:[function(t,e,i){"use strict";var n=t(25),a=t(26),o=t(45),r=t(30),s=o.noop;function l(t,e){return t.usePointStyle?e*Math.SQRT2:t.boxWidth}n._set("global",{legend:{display:!0,position:"top",fullWidth:!0,reverse:!1,weight:1e3,onClick:function(t,e){var i=e.datasetIndex,n=this.chart,a=n.getDatasetMeta(i);a.hidden=null===a.hidden?!n.data.datasets[i].hidden:null,n.update()},onHover:null,labels:{boxWidth:40,padding:10,generateLabels:function(t){var e=t.data;return o.isArray(e.datasets)?e.datasets.map(function(e,i){return{text:e.label,fillStyle:o.isArray(e.backgroundColor)?e.backgroundColor[0]:e.backgroundColor,hidden:!t.isDatasetVisible(i),lineCap:e.borderCapStyle,lineDash:e.borderDash,lineDashOffset:e.borderDashOffset,lineJoin:e.borderJoinStyle,lineWidth:e.borderWidth,strokeStyle:e.borderColor,pointStyle:e.pointStyle,datasetIndex:i}},this):[]}}},legendCallback:function(t){var e=[];e.push('<ul class="'+t.id+'-legend">');for(var i=0;i<t.data.datasets.length;i++)e.push('<li><span style="background-color:'+t.data.datasets[i].backgroundColor+'"></span>'),t.data.datasets[i].label&&e.push(t.data.datasets[i].label),e.push("</li>");return e.push("</ul>"),e.join("")}});var u=a.extend({initialize:function(t){o.extend(this,t),this.legendHitBoxes=[],this.doughnutMode=!1},beforeUpdate:s,update:function(t,e,i){var n=this;return n.beforeUpdate(),n.maxWidth=t,n.maxHeight=e,n.margins=i,n.beforeSetDimensions(),n.setDimensions(),n.afterSetDimensions(),n.beforeBuildLabels(),n.buildLabels(),n.afterBuildLabels(),n.beforeFit(),n.fit(),n.afterFit(),n.afterUpdate(),n.minSize},afterUpdate:s,beforeSetDimensions:s,setDimensions:function(){var t=this;t.isHorizontal()?(t.width=t.maxWidth,t.left=0,t.right=t.width):(t.height=t.maxHeight,t.top=0,t.bottom=t.height),t.paddingLeft=0,t.paddingTop=0,t.paddingRight=0,t.paddingBottom=0,t.minSize={width:0,height:0}},afterSetDimensions:s,beforeBuildLabels:s,buildLabels:function(){var t=this,e=t.options.labels||{},i=o.callback(e.generateLabels,[t.chart],t)||[];e.filter&&(i=i.filter(function(i){return e.filter(i,t.chart.data)})),t.options.reverse&&i.reverse(),t.legendItems=i},afterBuildLabels:s,beforeFit:s,fit:function(){var t=this,e=t.options,i=e.labels,a=e.display,r=t.ctx,s=n.global,u=o.valueOrDefault,d=u(i.fontSize,s.defaultFontSize),c=u(i.fontStyle,s.defaultFontStyle),h=u(i.fontFamily,s.defaultFontFamily),f=o.fontString(d,c,h),g=t.legendHitBoxes=[],p=t.minSize,m=t.isHorizontal();if(m?(p.width=t.maxWidth,p.height=a?10:0):(p.width=a?10:0,p.height=t.maxHeight),a)if(r.font=f,m){var v=t.lineWidths=[0],b=t.legendItems.length?d+i.padding:0;r.textAlign="left",r.textBaseline="top",o.each(t.legendItems,function(e,n){var a=l(i,d)+d/2+r.measureText(e.text).width;v[v.length-1]+a+i.padding>=t.width&&(b+=d+i.padding,v[v.length]=t.left),g[n]={left:0,top:0,width:a,height:d},v[v.length-1]+=a+i.padding}),p.height+=b}else{var x=i.padding,y=t.columnWidths=[],k=i.padding,M=0,w=0,S=d+x;o.each(t.legendItems,function(t,e){var n=l(i,d)+d/2+r.measureText(t.text).width;w+S>p.height&&(k+=M+i.padding,y.push(M),M=0,w=0),M=Math.max(M,n),w+=S,g[e]={left:0,top:0,width:n,height:d}}),k+=M,y.push(M),p.width+=k}t.width=p.width,t.height=p.height},afterFit:s,isHorizontal:function(){return"top"===this.options.position||"bottom"===this.options.position},draw:function(){var t=this,e=t.options,i=e.labels,a=n.global,r=a.elements.line,s=t.width,u=t.lineWidths;if(e.display){var d,c=t.ctx,h=o.valueOrDefault,f=h(i.fontColor,a.defaultFontColor),g=h(i.fontSize,a.defaultFontSize),p=h(i.fontStyle,a.defaultFontStyle),m=h(i.fontFamily,a.defaultFontFamily),v=o.fontString(g,p,m);c.textAlign="left",c.textBaseline="middle",c.lineWidth=.5,c.strokeStyle=f,c.fillStyle=f,c.font=v;var b=l(i,g),x=t.legendHitBoxes,y=t.isHorizontal();d=y?{x:t.left+(s-u[0])/2,y:t.top+i.padding,line:0}:{x:t.left+i.padding,y:t.top+i.padding,line:0};var k=g+i.padding;o.each(t.legendItems,function(n,l){var f,p,m,v,M,w=c.measureText(n.text).width,S=b+g/2+w,C=d.x,_=d.y;y?C+S>=s&&(_=d.y+=k,d.line++,C=d.x=t.left+(s-u[d.line])/2):_+k>t.bottom&&(C=d.x=C+t.columnWidths[d.line]+i.padding,_=d.y=t.top+i.padding,d.line++),function(t,i,n){if(!(isNaN(b)||b<=0)){c.save(),c.fillStyle=h(n.fillStyle,a.defaultColor),c.lineCap=h(n.lineCap,r.borderCapStyle),c.lineDashOffset=h(n.lineDashOffset,r.borderDashOffset),c.lineJoin=h(n.lineJoin,r.borderJoinStyle),c.lineWidth=h(n.lineWidth,r.borderWidth),c.strokeStyle=h(n.strokeStyle,a.defaultColor);var s=0===h(n.lineWidth,r.borderWidth);if(c.setLineDash&&c.setLineDash(h(n.lineDash,r.borderDash)),e.labels&&e.labels.usePointStyle){var l=g*Math.SQRT2/2,u=l/Math.SQRT2,d=t+u,f=i+u;o.canvas.drawPoint(c,n.pointStyle,l,d,f)}else s||c.strokeRect(t,i,b,g),c.fillRect(t,i,b,g);c.restore()}}(C,_,n),x[l].left=C,x[l].top=_,f=n,p=w,v=b+(m=g/2)+C,M=_+m,c.fillText(f.text,v,M),f.hidden&&(c.beginPath(),c.lineWidth=2,c.moveTo(v,M),c.lineTo(v+p,M),c.stroke()),y?d.x+=S+i.padding:d.y+=k})}},handleEvent:function(t){var e=this,i=e.options,n="mouseup"===t.type?"click":t.type,a=!1;if("mousemove"===n){if(!i.onHover)return}else{if("click"!==n)return;if(!i.onClick)return}var o=t.x,r=t.y;if(o>=e.left&&o<=e.right&&r>=e.top&&r<=e.bottom)for(var s=e.legendHitBoxes,l=0;l<s.length;++l){var u=s[l];if(o>=u.left&&o<=u.left+u.width&&r>=u.top&&r<=u.top+u.height){if("click"===n){i.onClick.call(e,t.native,e.legendItems[l]),a=!0;break}if("mousemove"===n){i.onHover.call(e,t.native,e.legendItems[l]),a=!0;break}}}return a}});function d(t,e){var i=new u({ctx:t.ctx,options:e,chart:t});r.configure(t,i,e),r.addBox(t,i),t.legend=i}e.exports={id:"legend",_element:u,beforeInit:function(t){var e=t.options.legend;e&&d(t,e)},beforeUpdate:function(t){var e=t.options.legend,i=t.legend;e?(o.mergeIf(e,n.global.legend),i?(r.configure(t,i,e),i.options=e):d(t,e)):i&&(r.removeBox(t,i),delete t.legend)},afterEvent:function(t,e){var i=t.legend;i&&i.handleEvent(e)}}},{25:25,26:26,30:30,45:45}],52:[function(t,e,i){"use strict";var n=t(25),a=t(26),o=t(45),r=t(30),s=o.noop;n._set("global",{title:{display:!1,fontStyle:"bold",fullWidth:!0,lineHeight:1.2,padding:10,position:"top",text:"",weight:2e3}});var l=a.extend({initialize:function(t){o.extend(this,t),this.legendHitBoxes=[]},beforeUpdate:s,update:function(t,e,i){var n=this;return n.beforeUpdate(),n.maxWidth=t,n.maxHeight=e,n.margins=i,n.beforeSetDimensions(),n.setDimensions(),n.afterSetDimensions(),n.beforeBuildLabels(),n.buildLabels(),n.afterBuildLabels(),n.beforeFit(),n.fit(),n.afterFit(),n.afterUpdate(),n.minSize},afterUpdate:s,beforeSetDimensions:s,setDimensions:function(){var t=this;t.isHorizontal()?(t.width=t.maxWidth,t.left=0,t.right=t.width):(t.height=t.maxHeight,t.top=0,t.bottom=t.height),t.paddingLeft=0,t.paddingTop=0,t.paddingRight=0,t.paddingBottom=0,t.minSize={width:0,height:0}},afterSetDimensions:s,beforeBuildLabels:s,buildLabels:s,afterBuildLabels:s,beforeFit:s,fit:function(){var t=this,e=o.valueOrDefault,i=t.options,a=i.display,r=e(i.fontSize,n.global.defaultFontSize),s=t.minSize,l=o.isArray(i.text)?i.text.length:1,u=o.options.toLineHeight(i.lineHeight,r),d=a?l*u+2*i.padding:0;t.isHorizontal()?(s.width=t.maxWidth,s.height=d):(s.width=d,s.height=t.maxHeight),t.width=s.width,t.height=s.height},afterFit:s,isHorizontal:function(){var t=this.options.position;return"top"===t||"bottom"===t},draw:function(){var t=this,e=t.ctx,i=o.valueOrDefault,a=t.options,r=n.global;if(a.display){var s,l,u,d=i(a.fontSize,r.defaultFontSize),c=i(a.fontStyle,r.defaultFontStyle),h=i(a.fontFamily,r.defaultFontFamily),f=o.fontString(d,c,h),g=o.options.toLineHeight(a.lineHeight,d),p=g/2+a.padding,m=0,v=t.top,b=t.left,x=t.bottom,y=t.right;e.fillStyle=i(a.fontColor,r.defaultFontColor),e.font=f,t.isHorizontal()?(l=b+(y-b)/2,u=v+p,s=y-b):(l="left"===a.position?b+p:y-p,u=v+(x-v)/2,s=x-v,m=Math.PI*("left"===a.position?-.5:.5)),e.save(),e.translate(l,u),e.rotate(m),e.textAlign="center",e.textBaseline="middle";var k=a.text;if(o.isArray(k))for(var M=0,w=0;w<k.length;++w)e.fillText(k[w],0,M,s),M+=g;else e.fillText(k,0,0,s);e.restore()}}});function u(t,e){var i=new l({ctx:t.ctx,options:e,chart:t});r.configure(t,i,e),r.addBox(t,i),t.titleBlock=i}e.exports={id:"title",_element:l,beforeInit:function(t){var e=t.options.title;e&&u(t,e)},beforeUpdate:function(t){var e=t.options.title,i=t.titleBlock;e?(o.mergeIf(e,n.global.title),i?(r.configure(t,i,e),i.options=e):u(t,e)):i&&(r.removeBox(t,i),delete t.titleBlock)}}},{25:25,26:26,30:30,45:45}],53:[function(t,e,i){"use strict";e.exports=function(t){var e=t.Scale.extend({getLabels:function(){var t=this.chart.data;return this.options.labels||(this.isHorizontal()?t.xLabels:t.yLabels)||t.labels},determineDataLimits:function(){var t,e=this,i=e.getLabels();e.minIndex=0,e.maxIndex=i.length-1,void 0!==e.options.ticks.min&&(t=i.indexOf(e.options.ticks.min),e.minIndex=-1!==t?t:e.minIndex),void 0!==e.options.ticks.max&&(t=i.indexOf(e.options.ticks.max),e.maxIndex=-1!==t?t:e.maxIndex),e.min=i[e.minIndex],e.max=i[e.maxIndex]},buildTicks:function(){var t=this,e=t.getLabels();t.ticks=0===t.minIndex&&t.maxIndex===e.length-1?e:e.slice(t.minIndex,t.maxIndex+1)},getLabelForIndex:function(t,e){var i=this,n=i.chart.data,a=i.isHorizontal();return n.yLabels&&!a?i.getRightValue(n.datasets[e].data[t]):i.ticks[t-i.minIndex]},getPixelForValue:function(t,e){var i,n=this,a=n.options.offset,o=Math.max(n.maxIndex+1-n.minIndex-(a?0:1),1);if(null!=t&&(i=n.isHorizontal()?t.x:t.y),void 0!==i||void 0!==t&&isNaN(e)){t=i||t;var r=n.getLabels().indexOf(t);e=-1!==r?r:e}if(n.isHorizontal()){var s=n.width/o,l=s*(e-n.minIndex);return a&&(l+=s/2),n.left+Math.round(l)}var u=n.height/o,d=u*(e-n.minIndex);return a&&(d+=u/2),n.top+Math.round(d)},getPixelForTick:function(t){return this.getPixelForValue(this.ticks[t],t+this.minIndex,null)},getValueForPixel:function(t){var e=this,i=e.options.offset,n=Math.max(e._ticks.length-(i?0:1),1),a=e.isHorizontal(),o=(a?e.width:e.height)/n;return t-=a?e.left:e.top,i&&(t-=o/2),(t<=0?0:Math.round(t/o))+e.minIndex},getBasePixel:function(){return this.bottom}});t.scaleService.registerScaleType("category",e,{position:"bottom"})}},{}],54:[function(t,e,i){"use strict";var n=t(25),a=t(45),o=t(34);e.exports=function(t){var e={position:"left",ticks:{callback:o.formatters.linear}},i=t.LinearScaleBase.extend({determineDataLimits:function(){var t=this,e=t.options,i=t.chart,n=i.data.datasets,o=t.isHorizontal();function r(e){return o?e.xAxisID===t.id:e.yAxisID===t.id}t.min=null,t.max=null;var s=e.stacked;if(void 0===s&&a.each(n,function(t,e){if(!s){var n=i.getDatasetMeta(e);i.isDatasetVisible(e)&&r(n)&&void 0!==n.stack&&(s=!0)}}),e.stacked||s){var l={};a.each(n,function(n,o){var s=i.getDatasetMeta(o),u=[s.type,void 0===e.stacked&&void 0===s.stack?o:"",s.stack].join(".");void 0===l[u]&&(l[u]={positiveValues:[],negativeValues:[]});var d=l[u].positiveValues,c=l[u].negativeValues;i.isDatasetVisible(o)&&r(s)&&a.each(n.data,function(i,n){var a=+t.getRightValue(i);isNaN(a)||s.data[n].hidden||(d[n]=d[n]||0,c[n]=c[n]||0,e.relativePoints?d[n]=100:a<0?c[n]+=a:d[n]+=a)})}),a.each(l,function(e){var i=e.positiveValues.concat(e.negativeValues),n=a.min(i),o=a.max(i);t.min=null===t.min?n:Math.min(t.min,n),t.max=null===t.max?o:Math.max(t.max,o)})}else a.each(n,function(e,n){var o=i.getDatasetMeta(n);i.isDatasetVisible(n)&&r(o)&&a.each(e.data,function(e,i){var n=+t.getRightValue(e);isNaN(n)||o.data[i].hidden||(null===t.min?t.min=n:n<t.min&&(t.min=n),null===t.max?t.max=n:n>t.max&&(t.max=n))})});t.min=isFinite(t.min)&&!isNaN(t.min)?t.min:0,t.max=isFinite(t.max)&&!isNaN(t.max)?t.max:1,this.handleTickRangeOptions()},getTickLimit:function(){var t,e=this.options.ticks;if(this.isHorizontal())t=Math.min(e.maxTicksLimit?e.maxTicksLimit:11,Math.ceil(this.width/50));else{var i=a.valueOrDefault(e.fontSize,n.global.defaultFontSize);t=Math.min(e.maxTicksLimit?e.maxTicksLimit:11,Math.ceil(this.height/(2*i)))}return t},handleDirectionalChanges:function(){this.isHorizontal()||this.ticks.reverse()},getLabelForIndex:function(t,e){return+this.getRightValue(this.chart.data.datasets[e].data[t])},getPixelForValue:function(t){var e=this,i=e.start,n=+e.getRightValue(t),a=e.end-i;return e.isHorizontal()?e.left+e.width/a*(n-i):e.bottom-e.height/a*(n-i)},getValueForPixel:function(t){var e=this,i=e.isHorizontal(),n=i?e.width:e.height,a=(i?t-e.left:e.bottom-t)/n;return e.start+(e.end-e.start)*a},getPixelForTick:function(t){return this.getPixelForValue(this.ticksAsNumbers[t])}});t.scaleService.registerScaleType("linear",i,e)}},{25:25,34:34,45:45}],55:[function(t,e,i){"use strict";var n=t(45);e.exports=function(t){var e=n.noop;t.LinearScaleBase=t.Scale.extend({getRightValue:function(e){return"string"==typeof e?+e:t.Scale.prototype.getRightValue.call(this,e)},handleTickRangeOptions:function(){var t=this,e=t.options.ticks;if(e.beginAtZero){var i=n.sign(t.min),a=n.sign(t.max);i<0&&a<0?t.max=0:i>0&&a>0&&(t.min=0)}var o=void 0!==e.min||void 0!==e.suggestedMin,r=void 0!==e.max||void 0!==e.suggestedMax;void 0!==e.min?t.min=e.min:void 0!==e.suggestedMin&&(null===t.min?t.min=e.suggestedMin:t.min=Math.min(t.min,e.suggestedMin)),void 0!==e.max?t.max=e.max:void 0!==e.suggestedMax&&(null===t.max?t.max=e.suggestedMax:t.max=Math.max(t.max,e.suggestedMax)),o!==r&&t.min>=t.max&&(o?t.max=t.min+1:t.min=t.max-1),t.min===t.max&&(t.max++,e.beginAtZero||t.min--)},getTickLimit:e,handleDirectionalChanges:e,buildTicks:function(){var t=this,e=t.options.ticks,i=t.getTickLimit(),a={maxTicks:i=Math.max(2,i),min:e.min,max:e.max,stepSize:n.valueOrDefault(e.fixedStepSize,e.stepSize)},o=t.ticks=function(t,e){var i,a=[];if(t.stepSize&&t.stepSize>0)i=t.stepSize;else{var o=n.niceNum(e.max-e.min,!1);i=n.niceNum(o/(t.maxTicks-1),!0)}var r=Math.floor(e.min/i)*i,s=Math.ceil(e.max/i)*i;t.min&&t.max&&t.stepSize&&n.almostWhole((t.max-t.min)/t.stepSize,i/1e3)&&(r=t.min,s=t.max);var l=(s-r)/i;l=n.almostEquals(l,Math.round(l),i/1e3)?Math.round(l):Math.ceil(l);var u=1;i<1&&(u=Math.pow(10,i.toString().length-2),r=Math.round(r*u)/u,s=Math.round(s*u)/u),a.push(void 0!==t.min?t.min:r);for(var d=1;d<l;++d)a.push(Math.round((r+d*i)*u)/u);return a.push(void 0!==t.max?t.max:s),a}(a,t);t.handleDirectionalChanges(),t.max=n.max(o),t.min=n.min(o),e.reverse?(o.reverse(),t.start=t.max,t.end=t.min):(t.start=t.min,t.end=t.max)},convertTicksToLabels:function(){var e=this;e.ticksAsNumbers=e.ticks.slice(),e.zeroLineIndex=e.ticks.indexOf(0),t.Scale.prototype.convertTicksToLabels.call(e)}})}},{45:45}],56:[function(t,e,i){"use strict";var n=t(45),a=t(34);e.exports=function(t){var e={position:"left",ticks:{callback:a.formatters.logarithmic}},i=t.Scale.extend({determineDataLimits:function(){var t=this,e=t.options,i=t.chart,a=i.data.datasets,o=t.isHorizontal();function r(e){return o?e.xAxisID===t.id:e.yAxisID===t.id}t.min=null,t.max=null,t.minNotZero=null;var s=e.stacked;if(void 0===s&&n.each(a,function(t,e){if(!s){var n=i.getDatasetMeta(e);i.isDatasetVisible(e)&&r(n)&&void 0!==n.stack&&(s=!0)}}),e.stacked||s){var l={};n.each(a,function(a,o){var s=i.getDatasetMeta(o),u=[s.type,void 0===e.stacked&&void 0===s.stack?o:"",s.stack].join(".");i.isDatasetVisible(o)&&r(s)&&(void 0===l[u]&&(l[u]=[]),n.each(a.data,function(e,i){var n=l[u],a=+t.getRightValue(e);isNaN(a)||s.data[i].hidden||a<0||(n[i]=n[i]||0,n[i]+=a)}))}),n.each(l,function(e){if(e.length>0){var i=n.min(e),a=n.max(e);t.min=null===t.min?i:Math.min(t.min,i),t.max=null===t.max?a:Math.max(t.max,a)}})}else n.each(a,function(e,a){var o=i.getDatasetMeta(a);i.isDatasetVisible(a)&&r(o)&&n.each(e.data,function(e,i){var n=+t.getRightValue(e);isNaN(n)||o.data[i].hidden||n<0||(null===t.min?t.min=n:n<t.min&&(t.min=n),null===t.max?t.max=n:n>t.max&&(t.max=n),0!==n&&(null===t.minNotZero||n<t.minNotZero)&&(t.minNotZero=n))})});this.handleTickRangeOptions()},handleTickRangeOptions:function(){var t=this,e=t.options.ticks,i=n.valueOrDefault;t.min=i(e.min,t.min),t.max=i(e.max,t.max),t.min===t.max&&(0!==t.min&&null!==t.min?(t.min=Math.pow(10,Math.floor(n.log10(t.min))-1),t.max=Math.pow(10,Math.floor(n.log10(t.max))+1)):(t.min=1,t.max=10)),null===t.min&&(t.min=Math.pow(10,Math.floor(n.log10(t.max))-1)),null===t.max&&(t.max=0!==t.min?Math.pow(10,Math.floor(n.log10(t.min))+1):10),null===t.minNotZero&&(t.min>0?t.minNotZero=t.min:t.max<1?t.minNotZero=Math.pow(10,Math.floor(n.log10(t.max))):t.minNotZero=1)},buildTicks:function(){var t=this,e=t.options.ticks,i=!t.isHorizontal(),a={min:e.min,max:e.max},o=t.ticks=function(t,e){var i,a,o=[],r=n.valueOrDefault,s=r(t.min,Math.pow(10,Math.floor(n.log10(e.min)))),l=Math.floor(n.log10(e.max)),u=Math.ceil(e.max/Math.pow(10,l));0===s?(i=Math.floor(n.log10(e.minNotZero)),a=Math.floor(e.minNotZero/Math.pow(10,i)),o.push(s),s=a*Math.pow(10,i)):(i=Math.floor(n.log10(s)),a=Math.floor(s/Math.pow(10,i)));for(var d=i<0?Math.pow(10,Math.abs(i)):1;o.push(s),10==++a&&(a=1,d=++i>=0?1:d),s=Math.round(a*Math.pow(10,i)*d)/d,i<l||i===l&&a<u;);var c=r(t.max,s);return o.push(c),o}(a,t);t.max=n.max(o),t.min=n.min(o),e.reverse?(i=!i,t.start=t.max,t.end=t.min):(t.start=t.min,t.end=t.max),i&&o.reverse()},convertTicksToLabels:function(){this.tickValues=this.ticks.slice(),t.Scale.prototype.convertTicksToLabels.call(this)},getLabelForIndex:function(t,e){return+this.getRightValue(this.chart.data.datasets[e].data[t])},getPixelForTick:function(t){return this.getPixelForValue(this.tickValues[t])},_getFirstTickValue:function(t){var e=Math.floor(n.log10(t));return Math.floor(t/Math.pow(10,e))*Math.pow(10,e)},getPixelForValue:function(e){var i,a,o,r,s,l=this,u=l.options.ticks.reverse,d=n.log10,c=l._getFirstTickValue(l.minNotZero),h=0;return e=+l.getRightValue(e),u?(o=l.end,r=l.start,s=-1):(o=l.start,r=l.end,s=1),l.isHorizontal()?(i=l.width,a=u?l.right:l.left):(i=l.height,s*=-1,a=u?l.top:l.bottom),e!==o&&(0===o&&(i-=h=n.getValueOrDefault(l.options.ticks.fontSize,t.defaults.global.defaultFontSize),o=c),0!==e&&(h+=i/(d(r)-d(o))*(d(e)-d(o))),a+=s*h),a},getValueForPixel:function(e){var i,a,o,r,s=this,l=s.options.ticks.reverse,u=n.log10,d=s._getFirstTickValue(s.minNotZero);if(l?(a=s.end,o=s.start):(a=s.start,o=s.end),s.isHorizontal()?(i=s.width,r=l?s.right-e:e-s.left):(i=s.height,r=l?e-s.top:s.bottom-e),r!==a){if(0===a){var c=n.getValueOrDefault(s.options.ticks.fontSize,t.defaults.global.defaultFontSize);r-=c,i-=c,a=d}r*=u(o)-u(a),r/=i,r=Math.pow(10,u(a)+r)}return r}});t.scaleService.registerScaleType("logarithmic",i,e)}},{34:34,45:45}],57:[function(t,e,i){"use strict";var n=t(25),a=t(45),o=t(34);e.exports=function(t){var e=n.global,i={display:!0,animate:!0,position:"chartArea",angleLines:{display:!0,color:"rgba(0, 0, 0, 0.1)",lineWidth:1},gridLines:{circular:!1},ticks:{showLabelBackdrop:!0,backdropColor:"rgba(255,255,255,0.75)",backdropPaddingY:2,backdropPaddingX:2,callback:o.formatters.linear},pointLabels:{display:!0,fontSize:10,callback:function(t){return t}}};function r(t){var e=t.options;return e.angleLines.display||e.pointLabels.display?t.chart.data.labels.length:0}function s(t){var i=t.options.pointLabels,n=a.valueOrDefault(i.fontSize,e.defaultFontSize),o=a.valueOrDefault(i.fontStyle,e.defaultFontStyle),r=a.valueOrDefault(i.fontFamily,e.defaultFontFamily);return{size:n,style:o,family:r,font:a.fontString(n,o,r)}}function l(t,e,i,n,a){return t===n||t===a?{start:e-i/2,end:e+i/2}:t<n||t>a?{start:e-i-5,end:e}:{start:e,end:e+i+5}}function u(t,e,i,n){if(a.isArray(e))for(var o=i.y,r=1.5*n,s=0;s<e.length;++s)t.fillText(e[s],i.x,o),o+=r;else t.fillText(e,i.x,i.y)}function d(t){return a.isNumber(t)?t:0}var c=t.LinearScaleBase.extend({setDimensions:function(){var t=this,i=t.options,n=i.ticks;t.width=t.maxWidth,t.height=t.maxHeight,t.xCenter=Math.round(t.width/2),t.yCenter=Math.round(t.height/2);var o=a.min([t.height,t.width]),r=a.valueOrDefault(n.fontSize,e.defaultFontSize);t.drawingArea=i.display?o/2-(r/2+n.backdropPaddingY):o/2},determineDataLimits:function(){var t=this,e=t.chart,i=Number.POSITIVE_INFINITY,n=Number.NEGATIVE_INFINITY;a.each(e.data.datasets,function(o,r){if(e.isDatasetVisible(r)){var s=e.getDatasetMeta(r);a.each(o.data,function(e,a){var o=+t.getRightValue(e);isNaN(o)||s.data[a].hidden||(i=Math.min(o,i),n=Math.max(o,n))})}}),t.min=i===Number.POSITIVE_INFINITY?0:i,t.max=n===Number.NEGATIVE_INFINITY?0:n,t.handleTickRangeOptions()},getTickLimit:function(){var t=this.options.ticks,i=a.valueOrDefault(t.fontSize,e.defaultFontSize);return Math.min(t.maxTicksLimit?t.maxTicksLimit:11,Math.ceil(this.drawingArea/(1.5*i)))},convertTicksToLabels:function(){var e=this;t.LinearScaleBase.prototype.convertTicksToLabels.call(e),e.pointLabels=e.chart.data.labels.map(e.options.pointLabels.callback,e)},getLabelForIndex:function(t,e){return+this.getRightValue(this.chart.data.datasets[e].data[t])},fit:function(){var t,e;this.options.pointLabels.display?function(t){var e,i,n,o=s(t),u=Math.min(t.height/2,t.width/2),d={r:t.width,l:0,t:t.height,b:0},c={};t.ctx.font=o.font,t._pointLabelSizes=[];var h,f,g,p=r(t);for(e=0;e<p;e++){n=t.getPointPosition(e,u),h=t.ctx,f=o.size,g=t.pointLabels[e]||"",i=a.isArray(g)?{w:a.longestText(h,h.font,g),h:g.length*f+1.5*(g.length-1)*f}:{w:h.measureText(g).width,h:f},t._pointLabelSizes[e]=i;var m=t.getIndexAngle(e),v=a.toDegrees(m)%360,b=l(v,n.x,i.w,0,180),x=l(v,n.y,i.h,90,270);b.start<d.l&&(d.l=b.start,c.l=m),b.end>d.r&&(d.r=b.end,c.r=m),x.start<d.t&&(d.t=x.start,c.t=m),x.end>d.b&&(d.b=x.end,c.b=m)}t.setReductions(u,d,c)}(this):(t=this,e=Math.min(t.height/2,t.width/2),t.drawingArea=Math.round(e),t.setCenterPoint(0,0,0,0))},setReductions:function(t,e,i){var n=e.l/Math.sin(i.l),a=Math.max(e.r-this.width,0)/Math.sin(i.r),o=-e.t/Math.cos(i.t),r=-Math.max(e.b-this.height,0)/Math.cos(i.b);n=d(n),a=d(a),o=d(o),r=d(r),this.drawingArea=Math.min(Math.round(t-(n+a)/2),Math.round(t-(o+r)/2)),this.setCenterPoint(n,a,o,r)},setCenterPoint:function(t,e,i,n){var a=this,o=a.width-e-a.drawingArea,r=t+a.drawingArea,s=i+a.drawingArea,l=a.height-n-a.drawingArea;a.xCenter=Math.round((r+o)/2+a.left),a.yCenter=Math.round((s+l)/2+a.top)},getIndexAngle:function(t){return t*(2*Math.PI/r(this))+(this.chart.options&&this.chart.options.startAngle?this.chart.options.startAngle:0)*Math.PI*2/360},getDistanceFromCenterForValue:function(t){var e=this;if(null===t)return 0;var i=e.drawingArea/(e.max-e.min);return e.options.ticks.reverse?(e.max-t)*i:(t-e.min)*i},getPointPosition:function(t,e){var i=this.getIndexAngle(t)-Math.PI/2;return{x:Math.round(Math.cos(i)*e)+this.xCenter,y:Math.round(Math.sin(i)*e)+this.yCenter}},getPointPositionForValue:function(t,e){return this.getPointPosition(t,this.getDistanceFromCenterForValue(e))},getBasePosition:function(){var t=this.min,e=this.max;return this.getPointPositionForValue(0,this.beginAtZero?0:t<0&&e<0?e:t>0&&e>0?t:0)},draw:function(){var t=this,i=t.options,n=i.gridLines,o=i.ticks,l=a.valueOrDefault;if(i.display){var d=t.ctx,c=this.getIndexAngle(0),h=l(o.fontSize,e.defaultFontSize),f=l(o.fontStyle,e.defaultFontStyle),g=l(o.fontFamily,e.defaultFontFamily),p=a.fontString(h,f,g);a.each(t.ticks,function(i,s){if(s>0||o.reverse){var u=t.getDistanceFromCenterForValue(t.ticksAsNumbers[s]);if(n.display&&0!==s&&function(t,e,i,n){var o=t.ctx;if(o.strokeStyle=a.valueAtIndexOrDefault(e.color,n-1),o.lineWidth=a.valueAtIndexOrDefault(e.lineWidth,n-1),t.options.gridLines.circular)o.beginPath(),o.arc(t.xCenter,t.yCenter,i,0,2*Math.PI),o.closePath(),o.stroke();else{var s=r(t);if(0===s)return;o.beginPath();var l=t.getPointPosition(0,i);o.moveTo(l.x,l.y);for(var u=1;u<s;u++)l=t.getPointPosition(u,i),o.lineTo(l.x,l.y);o.closePath(),o.stroke()}}(t,n,u,s),o.display){var f=l(o.fontColor,e.defaultFontColor);if(d.font=p,d.save(),d.translate(t.xCenter,t.yCenter),d.rotate(c),o.showLabelBackdrop){var g=d.measureText(i).width;d.fillStyle=o.backdropColor,d.fillRect(-g/2-o.backdropPaddingX,-u-h/2-o.backdropPaddingY,g+2*o.backdropPaddingX,h+2*o.backdropPaddingY)}d.textAlign="center",d.textBaseline="middle",d.fillStyle=f,d.fillText(i,0,-u),d.restore()}}}),(i.angleLines.display||i.pointLabels.display)&&function(t){var i=t.ctx,n=t.options,o=n.angleLines,l=n.pointLabels;i.lineWidth=o.lineWidth,i.strokeStyle=o.color;var d,c,h,f,g=t.getDistanceFromCenterForValue(n.ticks.reverse?t.min:t.max),p=s(t);i.textBaseline="top";for(var m=r(t)-1;m>=0;m--){if(o.display){var v=t.getPointPosition(m,g);i.beginPath(),i.moveTo(t.xCenter,t.yCenter),i.lineTo(v.x,v.y),i.stroke(),i.closePath()}if(l.display){var b=t.getPointPosition(m,g+5),x=a.valueAtIndexOrDefault(l.fontColor,m,e.defaultFontColor);i.font=p.font,i.fillStyle=x;var y=t.getIndexAngle(m),k=a.toDegrees(y);i.textAlign=0===(f=k)||180===f?"center":f<180?"left":"right",d=k,c=t._pointLabelSizes[m],h=b,90===d||270===d?h.y-=c.h/2:(d>270||d<90)&&(h.y-=c.h),u(i,t.pointLabels[m]||"",b,p.size)}}}(t)}}});t.scaleService.registerScaleType("radialLinear",c,i)}},{25:25,34:34,45:45}],58:[function(t,e,i){"use strict";var n=t(1);n="function"==typeof n?n:window.moment;var a=t(25),o=t(45),r=Number.MIN_SAFE_INTEGER||-9007199254740991,s=Number.MAX_SAFE_INTEGER||9007199254740991,l={millisecond:{common:!0,size:1,steps:[1,2,5,10,20,50,100,250,500]},second:{common:!0,size:1e3,steps:[1,2,5,10,30]},minute:{common:!0,size:6e4,steps:[1,2,5,10,30]},hour:{common:!0,size:36e5,steps:[1,2,3,6,12]},day:{common:!0,size:864e5,steps:[1,2,5]},week:{common:!1,size:6048e5,steps:[1,2,3,4]},month:{common:!0,size:2628e6,steps:[1,2,3]},quarter:{common:!1,size:7884e6,steps:[1,2,3,4]},year:{common:!0,size:3154e7}},u=Object.keys(l);function d(t,e){return t-e}function c(t){var e,i,n,a={},o=[];for(e=0,i=t.length;e<i;++e)a[n=t[e]]||(a[n]=!0,o.push(n));return o}function h(t,e,i,n){var a=function(t,e,i){for(var n,a,o,r=0,s=t.length-1;r>=0&&r<=s;){if(a=t[(n=r+s>>1)-1]||null,o=t[n],!a)return{lo:null,hi:o};if(o[e]<i)r=n+1;else{if(!(a[e]>i))return{lo:a,hi:o};s=n-1}}return{lo:o,hi:null}}(t,e,i),o=a.lo?a.hi?a.lo:t[t.length-2]:t[0],r=a.lo?a.hi?a.hi:t[t.length-1]:t[1],s=r[e]-o[e],l=s?(i-o[e])/s:0,u=(r[n]-o[n])*l;return o[n]+u}function f(t,e){var i=e.parser,a=e.parser||e.format;return"function"==typeof i?i(t):"string"==typeof t&&"string"==typeof a?n(t,a):(t instanceof n||(t=n(t)),t.isValid()?t:"function"==typeof a?a(t):t)}function g(t,e){if(o.isNullOrUndef(t))return null;var i=e.options.time,n=f(e.getRightValue(t),i);return n.isValid()?(i.round&&n.startOf(i.round),n.valueOf()):null}function p(t){for(var e=u.indexOf(t)+1,i=u.length;e<i;++e)if(l[u[e]].common)return u[e]}function m(t,e,i,a){var r,d=a.time,c=d.unit||function(t,e,i,n){var a,o,r,d=u.length;for(a=u.indexOf(t);a<d-1;++a)if(r=(o=l[u[a]]).steps?o.steps[o.steps.length-1]:s,o.common&&Math.ceil((i-e)/(r*o.size))<=n)return u[a];return u[d-1]}(d.minUnit,t,e,i),h=p(c),f=o.valueOrDefault(d.stepSize,d.unitStepSize),g="week"===c&&d.isoWeekday,m=a.ticks.major.enabled,v=l[c],b=n(t),x=n(e),y=[];for(f||(f=function(t,e,i,n){var a,o,r,s=e-t,u=l[i],d=u.size,c=u.steps;if(!c)return Math.ceil(s/(n*d));for(a=0,o=c.length;a<o&&(r=c[a],!(Math.ceil(s/(d*r))<=n));++a);return r}(t,e,c,i)),g&&(b=b.isoWeekday(g),x=x.isoWeekday(g)),b=b.startOf(g?"day":c),(x=x.startOf(g?"day":c))<e&&x.add(1,c),r=n(b),m&&h&&!g&&!d.round&&(r.startOf(h),r.add(~~((b-r)/(v.size*f))*f,c));r<x;r.add(f,c))y.push(+r);return y.push(+r),y}e.exports=function(t){var e=t.Scale.extend({initialize:function(){if(!n)throw new Error("Chart.js - Moment.js could not be found! You must include it before Chart.js to use the time scale. Download at https://momentjs.com");this.mergeTicksOptions(),t.Scale.prototype.initialize.call(this)},update:function(){var e=this.options;return e.time&&e.time.format&&console.warn("options.time.format is deprecated and replaced by options.time.parser."),t.Scale.prototype.update.apply(this,arguments)},getRightValue:function(e){return e&&void 0!==e.t&&(e=e.t),t.Scale.prototype.getRightValue.call(this,e)},determineDataLimits:function(){var t,e,i,a,l,u,h=this,f=h.chart,p=h.options.time,m=p.unit||"day",v=s,b=r,x=[],y=[],k=[];for(t=0,i=f.data.labels.length;t<i;++t)k.push(g(f.data.labels[t],h));for(t=0,i=(f.data.datasets||[]).length;t<i;++t)if(f.isDatasetVisible(t))if(l=f.data.datasets[t].data,o.isObject(l[0]))for(y[t]=[],e=0,a=l.length;e<a;++e)u=g(l[e],h),x.push(u),y[t][e]=u;else x.push.apply(x,k),y[t]=k.slice(0);else y[t]=[];k.length&&(k=c(k).sort(d),v=Math.min(v,k[0]),b=Math.max(b,k[k.length-1])),x.length&&(x=c(x).sort(d),v=Math.min(v,x[0]),b=Math.max(b,x[x.length-1])),v=g(p.min,h)||v,b=g(p.max,h)||b,v=v===s?+n().startOf(m):v,b=b===r?+n().endOf(m)+1:b,h.min=Math.min(v,b),h.max=Math.max(v+1,b),h._horizontal=h.isHorizontal(),h._table=[],h._timestamps={data:x,datasets:y,labels:k}},buildTicks:function(){var t,e,i,a,o,r,s,d,c,v,b,x,y=this,k=y.min,M=y.max,w=y.options,S=w.time,C=[],_=[];switch(w.ticks.source){case"data":C=y._timestamps.data;break;case"labels":C=y._timestamps.labels;break;case"auto":default:C=m(k,M,y.getLabelCapacity(k),w)}for("ticks"===w.bounds&&C.length&&(k=C[0],M=C[C.length-1]),k=g(S.min,y)||k,M=g(S.max,y)||M,t=0,e=C.length;t<e;++t)(i=C[t])>=k&&i<=M&&_.push(i);return y.min=k,y.max=M,y._unit=S.unit||function(t,e,i,a){var o,r,s=n.duration(n(a).diff(n(i)));for(o=u.length-1;o>=u.indexOf(e);o--)if(r=u[o],l[r].common&&s.as(r)>=t.length)return r;return u[e?u.indexOf(e):0]}(_,S.minUnit,y.min,y.max),y._majorUnit=p(y._unit),y._table=function(t,e,i,n){if("linear"===n||!t.length)return[{time:e,pos:0},{time:i,pos:1}];var a,o,r,s,l,u=[],d=[e];for(a=0,o=t.length;a<o;++a)(s=t[a])>e&&s<i&&d.push(s);for(d.push(i),a=0,o=d.length;a<o;++a)l=d[a+1],r=d[a-1],s=d[a],void 0!==r&&void 0!==l&&Math.round((l+r)/2)===s||u.push({time:s,pos:a/(o-1)});return u}(y._timestamps.data,k,M,w.distribution),y._offsets=(a=y._table,o=_,r=k,s=M,b=0,x=0,(d=w).offset&&o.length&&(d.time.min||(c=o.length>1?o[1]:s,v=o[0],b=(h(a,"time",c,"pos")-h(a,"time",v,"pos"))/2),d.time.max||(c=o[o.length-1],v=o.length>1?o[o.length-2]:r,x=(h(a,"time",c,"pos")-h(a,"time",v,"pos"))/2)),{left:b,right:x}),y._labelFormat=function(t,e){var i,n,a,o=t.length;for(i=0;i<o;i++){if(0!==(n=f(t[i],e)).millisecond())return"MMM D, YYYY h:mm:ss.SSS a";0===n.second()&&0===n.minute()&&0===n.hour()||(a=!0)}return a?"MMM D, YYYY h:mm:ss a":"MMM D, YYYY"}(y._timestamps.data,S),function(t,e){var i,a,o,r,s=[];for(i=0,a=t.length;i<a;++i)o=t[i],r=!!e&&o===+n(o).startOf(e),s.push({value:o,major:r});return s}(_,y._majorUnit)},getLabelForIndex:function(t,e){var i=this.chart.data,n=this.options.time,a=i.labels&&t<i.labels.length?i.labels[t]:"",r=i.datasets[e].data[t];return o.isObject(r)&&(a=this.getRightValue(r)),n.tooltipFormat?f(a,n).format(n.tooltipFormat):"string"==typeof a?a:f(a,n).format(this._labelFormat)},tickFormatFunction:function(t,e,i,n){var a=this.options,r=t.valueOf(),s=a.time.displayFormats,l=s[this._unit],u=this._majorUnit,d=s[u],c=t.clone().startOf(u).valueOf(),h=a.ticks.major,f=h.enabled&&u&&d&&r===c,g=t.format(n||(f?d:l)),p=f?h:a.ticks.minor,m=o.valueOrDefault(p.callback,p.userCallback);return m?m(g,e,i):g},convertTicksToLabels:function(t){var e,i,a=[];for(e=0,i=t.length;e<i;++e)a.push(this.tickFormatFunction(n(t[e].value),e,t));return a},getPixelForOffset:function(t){var e=this,i=e._horizontal?e.width:e.height,n=e._horizontal?e.left:e.top,a=h(e._table,"time",t,"pos");return n+i*(e._offsets.left+a)/(e._offsets.left+1+e._offsets.right)},getPixelForValue:function(t,e,i){var n=null;if(void 0!==e&&void 0!==i&&(n=this._timestamps.datasets[i][e]),null===n&&(n=g(t,this)),null!==n)return this.getPixelForOffset(n)},getPixelForTick:function(t){var e=this.getTicks();return t>=0&&t<e.length?this.getPixelForOffset(e[t].value):null},getValueForPixel:function(t){var e=this,i=e._horizontal?e.width:e.height,a=e._horizontal?e.left:e.top,o=(i?(t-a)/i:0)*(e._offsets.left+1+e._offsets.left)-e._offsets.right,r=h(e._table,"pos",o,"time");return n(r)},getLabelWidth:function(t){var e=this.options.ticks,i=this.ctx.measureText(t).width,n=o.toRadians(e.maxRotation),r=Math.cos(n),s=Math.sin(n);return i*r+o.valueOrDefault(e.fontSize,a.global.defaultFontSize)*s},getLabelCapacity:function(t){var e=this,i=e.options.time.displayFormats.millisecond,a=e.tickFormatFunction(n(t),0,[],i),o=e.getLabelWidth(a),r=e.isHorizontal()?e.width:e.height,s=Math.floor(r/o);return s>0?s:1}});t.scaleService.registerScaleType("time",e,{position:"bottom",distribution:"linear",bounds:"data",time:{parser:!1,format:!1,unit:!1,round:!1,displayFormat:!1,isoWeekday:!1,minUnit:"millisecond",displayFormats:{millisecond:"h:mm:ss.SSS a",second:"h:mm:ss a",minute:"h:mm a",hour:"hA",day:"MMM D",week:"ll",month:"MMM YYYY",quarter:"[Q]Q - YYYY",year:"YYYY"}},ticks:{autoSkip:!1,source:"auto",major:{enabled:!1}}})}},{1:1,25:25,45:45}]},{},[7])(7)});
includes/scanner/assets/js/scanner.js ADDED
@@ -0,0 +1,107 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * @var {Object} wpnonce
3
+ */
4
+
5
+ (function($) {
6
+ var intervalId;
7
+ var loader = jQuery('.wt-scan-icon-loader');
8
+
9
+
10
+ jQuery(document).ready(function($) {
11
+ if($('#scan').attr('data-action') === 'stop_scan')
12
+ intervalId = setInterval(status_scan, 15000);
13
+ });
14
+
15
+ $('#scan').on('click', function(event) {
16
+ event.preventDefault();
17
+
18
+ var btn = $(this);
19
+ var btn_loader = $('.wt-dashboard-scan-button-loader');
20
+
21
+ btn.attr('disabled', 'disabled');
22
+ btn_loader.show();
23
+
24
+ var action = btn.attr('data-action');
25
+ var nonce;
26
+
27
+ if(action === 'start_scan') {
28
+ btn.html('Starting...');
29
+ nonce = wpnonce.start;
30
+ $("div.wt-scanner-chart-clean").css('width', '0%');
31
+ $("div.wt-scanner-chart-suspicious").css('width', '0%');
32
+ $("div.wt-scanner-chart-notverified").css('width', '100%');
33
+
34
+ $("#wtitan-files-num").html('0');
35
+ $("#wtitan-cleaned-num").html('0');
36
+ $("#wtitan-suspicious-num").html('0');
37
+ $("#wtitan-notverified-num").html('0');
38
+ } else {
39
+ btn.html('Stopping...');
40
+ nonce = wpnonce.stop;
41
+ }
42
+
43
+ $.post(ajaxurl, {
44
+ action: action,
45
+ _wpnonce: nonce
46
+ }, function(response) {
47
+ btn.removeAttr('disabled');
48
+ btn_loader.hide();
49
+ switch (action) {
50
+ case 'start_scan':
51
+ btn.attr('data-action', 'stop_scan');
52
+ btn.html('Stop scanning');
53
+ intervalId = setInterval(status_scan, 15000);
54
+ break;
55
+
56
+ case 'stop_scan':
57
+ btn.attr('data-action', 'start_scan');
58
+ btn.html('Start scan');
59
+ break;
60
+
61
+ default:
62
+ console.error('???');
63
+ return;
64
+ }
65
+
66
+ var type;
67
+ if(response.success) {
68
+ type = 'success';
69
+ } else {
70
+ type = 'warning';
71
+ }
72
+
73
+ var noticeId = $.wbcr_factory_clearfy_218.app.showNotice(response.data.message, type);
74
+ setTimeout(function() {
75
+ $.wbcr_factory_clearfy_218.app.hideNotice(noticeId);
76
+ }, 5000);
77
+ });
78
+ });
79
+
80
+ function status_scan() {
81
+ if($('#scan').attr('data-action') === 'start_scan') clearInterval(intervalId);
82
+ $.post(ajaxurl, {
83
+ action: 'status_scan',
84
+ _wpnonce: wpnonce.status
85
+ }, function(response) {
86
+ if(typeof response.data === 'undefined' || response.data === false) {
87
+ return;
88
+ }
89
+
90
+ $("div.wt-scanner-chart-clean").css('width',response.data.progress[0] + '%');
91
+ $("div.wt-scanner-chart-suspicious").css('width',response.data.progress[1] + '%');
92
+ $("div.wt-scanner-chart-notverified").css('width',response.data.progress[2] + '%');
93
+
94
+ $("#wtitan-files-num").html(response.data.scanned);
95
+ $("#wtitan-cleaned-num").html(response.data.cleaned);
96
+ $("#wtitan-suspicious-num").html(response.data.suspicious);
97
+ $("#wtitan-notverified-num").html(response.data.notfiltered);
98
+
99
+ if(response.data.notfiltered === 0)
100
+ {
101
+ $('#scan').attr('data-action', 'start_scan');
102
+ $('#scan').html('Start scan');
103
+ }
104
+ });
105
+ }
106
+
107
+ })(jQuery);
includes/scanner/assets/js/statistic.js ADDED
@@ -0,0 +1,34 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ jQuery(function($) {
2
+
3
+ var chat_html_id = 'wtitan-scan-chart';
4
+ var ctx = document.getElementById( chat_html_id );
5
+
6
+ window.wtitan_chart = new Chart( ctx, {
7
+ type: 'doughnut',
8
+ data: {
9
+ datasets: [{
10
+ data: [
11
+ $( '#' + chat_html_id ).attr( 'data-cleaned' ),
12
+ $( '#' + chat_html_id ).attr( 'data-suspicious' ),
13
+ ],
14
+ backgroundColor: [
15
+ '#5d05b7',
16
+ '#f1b1b6',
17
+ ],
18
+ borderWidth: 0,
19
+ label: 'Dataset 1'
20
+ }]
21
+ },
22
+ options: {
23
+ legend: {
24
+ display: false
25
+ },
26
+ events: [],
27
+ animation: {
28
+ easing: 'easeOutBounce'
29
+ },
30
+ responsive: false,
31
+ cutoutPercentage: 80
32
+ }
33
+ } );
34
+ });
includes/scanner/boot.php ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // Exit if accessed directly
4
+ if ( ! defined( 'ABSPATH' ) ) {
5
+ exit;
6
+ }
7
+
8
+ // Base module class
9
+ require_once WTITAN_PLUGIN_DIR . "/includes/class.module-base.php";
10
+
11
+ require_once 'classes/scanner/boot.php';
12
+ require_once 'classes/class.scanner.php';
includes/scanner/classes/class.scanner.php ADDED
@@ -0,0 +1,166 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace WBCR\Titan;
4
+
5
+ use WBCR\Titan\Client\Client;
6
+ use WBCR\Titan\Client\Entity\Signature;
7
+ use WBCR\Titan\MalwareScanner\SignaturePool;
8
+
9
+ /**
10
+ * The file contains a short help info.
11
+ *
12
+ * @author Alexander Gorenkov <g.a.androidjc2@ya.ru>
13
+ * @version 1.0
14
+ * @copyright (c) 2020 Creative Motion
15
+ */
16
+ class Scanner extends Module_Base {
17
+
18
+ /**
19
+ * Scanner constructor.
20
+ */
21
+ public function __construct()
22
+ {
23
+ parent::__construct();
24
+
25
+ $this->module_dir = WTITAN_PLUGIN_DIR . "/includes/scanner";
26
+
27
+ add_action('wp_ajax_start_scan', [$this, 'ajax_start_scan']);
28
+ add_action('wp_ajax_stop_scan', [$this, 'ajax_stop_scan']);
29
+ add_action('wp_ajax_status_scan', [$this, 'ajax_status_scan']);
30
+ }
31
+
32
+ public function ajax_status_scan()
33
+ {
34
+ check_ajax_referer('titan-status-scan');
35
+
36
+ if( !Plugin::app()->current_user_can() ) {
37
+ wp_send_json_error([
38
+ 'error_message' => __("You don't have enough capability to edit this information", "titan-security"),
39
+ ]);
40
+ }
41
+
42
+ $status = Plugin::app()->getOption('scanner_status', 'stopped');
43
+ if( $status === 'stopped' ) {
44
+ //wp_send_json_success(false);
45
+ }
46
+
47
+ $cleaned = $suspicious = $progress = 0;
48
+ $scanner = get_option(Plugin::app()->getPrefix() . 'scanner');
49
+ if($scanner) {
50
+ $matchedCount = get_option( Plugin::app()->getPrefix() . 'scanner_malware_matched', false );
51
+ $files_count = Plugin::app()->getOption( 'scanner_files_count', 0 );
52
+
53
+ $cleaned = $scanner->cleaned_count;
54
+ $suspicious = $scanner->suspicious_count;
55
+ $notfiltered = $scanner->files_count;
56
+ $scanned = $scanner->cleaned_count + $scanner->suspicious_count . ' / ' . $files_count;
57
+ $progress = [
58
+ $files_count > 0 ? round( $scanner->cleaned_count / $files_count * 100, 1 ) : 0,
59
+ $files_count > 0 ? round( $scanner->suspicious_count / $files_count * 100, 1 ) : 0,
60
+ $files_count > 0 ? round( $scanner->files_count / $files_count * 100, 1 ) : 0
61
+ ];
62
+
63
+ wp_send_json_success(compact('cleaned', 'suspicious', 'progress', 'notfiltered', 'scanned', 'files_count'));
64
+ }
65
+ }
66
+
67
+ public function ajax_start_scan()
68
+ {
69
+ check_ajax_referer('titan-start-scan');
70
+
71
+ if( !Plugin::app()->current_user_can() ) {
72
+ \WBCR\Titan\Logger\Writter::error('Scanner start action: ' . __("You don't have enough capability to edit this information", "titan-security"));
73
+ wp_send_json_error([
74
+ 'message' => __("You don't have enough capability to edit this information", "titan-security"),
75
+ ]);
76
+ }
77
+
78
+ titan_create_scheduler_scanner();
79
+
80
+ \WBCR\Titan\Logger\Writter::warning('Scanner start action: ' . __('Started', "titan-security"));
81
+ wp_send_json_success([
82
+ 'message' => __('Scanning started', 'titan-security'),
83
+ ]);
84
+ }
85
+
86
+ public function ajax_stop_scan()
87
+ {
88
+ check_ajax_referer('titan-stop-scan');
89
+
90
+ if( !Plugin::app()->current_user_can() ) {
91
+ \WBCR\Titan\Logger\Writter::info('Scanner stop action:' . __("You don't have enough capability to edit this information", "titan-security"));
92
+ wp_send_json_error([
93
+ 'message' => __("You don't have enough capability to edit this information", "titan-security"),
94
+ ]);
95
+ }
96
+
97
+ titan_remove_scheduler_scanner();
98
+
99
+ \WBCR\Titan\Logger\Writter::warning('Scanner stop action:' . __('Cancelled', 'titan-security'));
100
+ wp_send_json_success([
101
+ 'message' => __('Scanning cancelled', 'titan-security'),
102
+ ]);
103
+ }
104
+
105
+
106
+ /**
107
+ * Get count matched
108
+ *
109
+ * @return int
110
+ */
111
+ public function get_matched_count() {
112
+ $matched = get_option(Plugin::app()->getPrefix() . 'scanner_malware_matched', []);
113
+
114
+ return count($matched);
115
+ }
116
+ /**
117
+ * Current state of scanner
118
+ */
119
+ public function get_current_results() {
120
+
121
+ /** @var MalwareScanner\Scanner $scanner */
122
+ $scanner = get_option(Plugin::app()->getPrefix() . 'scanner');
123
+ $matched = get_option(Plugin::app()->getPrefix() . 'scanner_malware_matched', false);
124
+ $scanner_started = Plugin::app()->getOption('scanner_status') == 'started';
125
+ $files_count = Plugin::app()->getOption('scanner_files_count', 0);
126
+ $progress = [
127
+ $files_count > 0 ? floor($scanner->cleaned_count/$files_count*100) : 0,
128
+ $files_count > 0 ? ceil($scanner->suspicious_count/$files_count*100) : 0,
129
+ $files_count > 0 ? floor($scanner->files_count/$files_count*100) : 0
130
+ ];
131
+
132
+ if(!$scanner) {
133
+ return [
134
+ 'scanner_started' => 0,
135
+ 'matched' => false,
136
+ 'progress' => [0,0,100],
137
+ 'cleaned' => 0,
138
+ 'suspicious' => 0,
139
+ 'scanned' => 0,
140
+ 'notverified' => 0,
141
+ 'files_count' => 0,
142
+ ];
143
+ }
144
+
145
+ return [
146
+ 'scanner_started' => $scanner_started,
147
+ 'matched' => $matched,
148
+ 'cleaned' => $scanner->cleaned_count,
149
+ 'suspicious' => $scanner->suspicious_count,
150
+ 'scanned' => $scanner->cleaned_count + $scanner->suspicious_count,
151
+ 'notverified' => $scanner->files_count,
152
+ 'files_count' => $files_count,
153
+ 'progress' => $progress,
154
+ ];
155
+ }
156
+ /**
157
+ * Show page content
158
+ */
159
+ public function showPageContent() {
160
+ require WTITAN_PLUGIN_DIR . '/includes/scanner/classes/scanner/boot.php';
161
+
162
+
163
+ echo $this->render_template( 'scanner', $this->get_current_results() );
164
+
165
+ }
166
+ }
includes/scanner/classes/scanner/File.php ADDED
@@ -0,0 +1,118 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace WBCR\Titan\MalwareScanner;
4
+
5
+
6
+ use WBCR\Titan\Logger\Writter;
7
+
8
+ /**
9
+ * Class File
10
+ *
11
+ * @author Alexander Gorenkov <g.a.androidjc2@ya.ru>
12
+ */
13
+ class File {
14
+ /**
15
+ * @var string
16
+ */
17
+ protected $path;
18
+
19
+ /**
20
+ * @var string|null
21
+ */
22
+ protected $hashFile;
23
+
24
+ /**
25
+ * @var string[]|null
26
+ */
27
+ protected $content;
28
+
29
+ /**
30
+ * File constructor.
31
+ *
32
+ * @param string $path
33
+ * @param null $hashFile
34
+ * @param bool $loadData
35
+ */
36
+ public function __construct( $path, $hashFile = null, $loadData = false ) {
37
+ $this->path = $path;
38
+ $this->hashFile = $hashFile;
39
+ if ( $loadData ) {
40
+ $this->loadData();
41
+ }
42
+ }
43
+
44
+ /**
45
+ * @return \Generator
46
+ * @see File::loadData()
47
+ *
48
+ */
49
+ public function read() {
50
+ $resource = fopen( $this->path, 'r' );
51
+ if ( $resource === false ) {
52
+ Writter::error( sprintf( "Failed to open the file: %s", $this->path ) );
53
+
54
+ return;
55
+ }
56
+
57
+ while ( ! feof( $resource ) ) {
58
+ yield trim( fgets( $resource ) );
59
+ }
60
+
61
+ fclose( $resource );
62
+ }
63
+
64
+
65
+ /**
66
+ * This approach works faster than the usual `file_get_contents`
67
+ *
68
+ * @return string[]
69
+ */
70
+ public function loadData() {
71
+ if ( is_null( $this->content ) ) {
72
+ $this->content = '';
73
+ foreach ( $this->read() as $line ) {
74
+ $this->content .= "\n" . $line;
75
+ }
76
+ }
77
+
78
+ return $this->content;
79
+ }
80
+
81
+ public function clearLoadedData() {
82
+ unset( $this->content );
83
+ $this->content = null;
84
+ }
85
+
86
+ public function toArray()
87
+ {
88
+ return [
89
+ 'path'=>$this->path,
90
+ 'hash_file'=>$this->hashFile,
91
+ 'content' => $this->content
92
+ ];
93
+ }
94
+
95
+
96
+ /**
97
+ * @return string
98
+ *
99
+ * @param bool $short
100
+ */
101
+ public function getPath($short = false) {
102
+ return $short ? str_replace( ABSPATH, '', $this->path) : $this->path;
103
+ }
104
+
105
+ /**
106
+ * @return string|null
107
+ */
108
+ public function getFileHash() {
109
+ return $this->hashFile;
110
+ }
111
+
112
+ /**
113
+ * @return string|null
114
+ */
115
+ public function getContent() {
116
+ return $this->loadData();
117
+ }
118
+ }
includes/scanner/classes/scanner/HashListPool.php ADDED
@@ -0,0 +1,61 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace WBCR\Titan\MalwareScanner;
4
+
5
+ /**
6
+ * Class HashListPool
7
+ * @package WBCR\Titan\MalwareScanner
8
+ *
9
+ * @author Alexander Gorenkov <g.a.androidjc2@ya.ru>
10
+ */
11
+ class HashListPool {
12
+ /**
13
+ * @var string[]
14
+ */
15
+ private $hashList;
16
+
17
+ /**
18
+ * HashListPool constructor.
19
+ *
20
+ * @param string[] $hashList
21
+ */
22
+ public function __construct( $hashList ) {
23
+ $this->hashList = $hashList;
24
+ }
25
+
26
+ /**
27
+ * @return mixed
28
+ */
29
+ public function getHashList() {
30
+ return $this->hashList;
31
+ }
32
+
33
+ /**
34
+ * @param $filePath
35
+ *
36
+ * @return string|null
37
+ */
38
+ public function getFileHash( $filePath ) {
39
+ if ( ! isset( $this->hashList[ $filePath ] ) ) {
40
+ return null;
41
+ }
42
+
43
+ return $this->hashList[ $filePath ];
44
+ }
45
+
46
+ /**
47
+ * @return string[]
48
+ */
49
+ public function toArray() {
50
+ return $this->hashList;
51
+ }
52
+
53
+ /**
54
+ * @param $array
55
+ *
56
+ * @return $this
57
+ */
58
+ public static function fromArray( $array ) {
59
+ return new static( $array );
60
+ }
61
+ }
includes/scanner/classes/scanner/Match.php ADDED
@@ -0,0 +1,82 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace WBCR\Titan\MalwareScanner;
4
+
5
+
6
+ /**
7
+ * Class Match
8
+ *
9
+ * @author Alexander Gorenkov <g.a.androidjc2@ya.ru>
10
+ */
11
+ class Match implements \JsonSerializable {
12
+ /**
13
+ * @var Signature
14
+ */
15
+ private $signature;
16
+
17
+ /**
18
+ * @var File
19
+ */
20
+ private $file;
21
+
22
+ /**
23
+ * @var int
24
+ */
25
+ private $line;
26
+
27
+ /**
28
+ * @var string
29
+ */
30
+ private $match;
31
+
32
+ /**
33
+ * Match constructor.
34
+ *
35
+ * @param Signature $signature
36
+ * @param File $file
37
+ * @param int $line
38
+ * @param string $match
39
+ */
40
+ public function __construct( $signature, $file, $line, $match ) {
41
+ $file->clearLoadedData();
42
+ $this->signature = $signature;
43
+ $this->file = $file;
44
+ $this->line = $line;
45
+ $this->match = $match;
46
+ }
47
+
48
+ /**
49
+ * @return Signature
50
+ */
51
+ public function getSignature() {
52
+ return $this->signature;
53
+ }
54
+
55
+ /**
56
+ * @return File
57
+ */
58
+ public function getFile() {
59
+ return $this->file;
60
+ }
61
+
62
+ /**
63
+ * @return int
64
+ */
65
+ public function getLine() {
66
+ return $this->line;
67
+ }
68
+
69
+ /**
70
+ * @return string
71
+ */
72
+ public function getMatch() {
73
+ return $this->match;
74
+ }
75
+
76
+ public function jsonSerialize() {
77
+ return [
78
+ 'file' => $this->file,
79
+ 'match' => $this->match,
80
+ ];
81
+ }
82
+ }
includes/scanner/classes/scanner/Scanner.php ADDED
@@ -0,0 +1,207 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace WBCR\Titan\MalwareScanner;
4
+
5
+
6
+ use InvalidArgumentException;
7
+
8
+ /**
9
+ * Class Scanner
10
+ *
11
+ * @author Alexander Gorenkov <g.a.androidjc2@ya.ru>
12
+ */
13
+ class Scanner {
14
+ const SPEED_FREE = 'free';
15
+ const SPEED_SLOW = 'slow';
16
+ const SPEED_MEDIUM = 'medium';
17
+ const SPEED_FAST = 'fast';
18
+
19
+ const SCHEDULE_DAILY = 'daily';
20
+ const SCHEDULE_WEEKLY = 'weekly';
21
+ const SCHEDULE_CUSTOM = 'custom';
22
+ const SCHEDULE_DISABLED = 'disabled';
23
+
24
+ const SPEED_LIST = [
25
+ self::SPEED_FREE,
26
+ self::SPEED_SLOW,
27
+ self::SPEED_MEDIUM,
28
+ self::SPEED_FAST,
29
+ ];
30
+
31
+ const SPEED_FILES = [
32
+ self::SPEED_FREE => 25,
33
+ self::SPEED_SLOW => 60,
34
+ self::SPEED_MEDIUM => 120,
35
+ self::SPEED_FAST => 250,
36
+ ];
37
+
38
+ /** @var File[] */
39
+ protected $fileList = [];
40
+
41
+ public $files_count = 0;
42
+
43
+ public $cleaned_count = 0;
44
+
45
+ public $suspicious_count = 0;
46
+
47
+ public $peak_memory_usage = 0;
48
+
49
+ /**
50
+ * @var HashListPool
51
+ */
52
+ protected $hashList = [];
53
+
54
+ /**
55
+ * @var string[]
56
+ */
57
+ protected $ignoreFiles;
58
+
59
+ /**
60
+ * @var SignaturePool
61
+ */
62
+ protected $signaturePool = [];
63
+
64
+ /**
65
+ * Scanner constructor.
66
+ *
67
+ * @param string $path
68
+ * @param SignaturePool $signaturePool
69
+ * @param HashListPool $hashList
70
+ * @param array $ignoreFiles
71
+ */
72
+ public function __construct( $path, $signaturePool, $hashList = null, $ignoreFiles = [] ) {
73
+ if ( is_null( $hashList ) ) {
74
+ $hashList = HashListPool::fromArray( [] );
75
+ }
76
+
77
+ $this->hashList = $hashList;
78
+ $this->ignoreFiles = $ignoreFiles;
79
+ $this->signaturePool = $signaturePool;
80
+
81
+ $this->loadFilesFromPath( $path );
82
+ }
83
+
84
+ /**
85
+ * @return File[]
86
+ */
87
+ public function getFileList() {
88
+ return $this->fileList;
89
+ }
90
+
91
+ /**
92
+ * @return HashListPool
93
+ */
94
+ public function getHashList() {
95
+ return $this->hashList;
96
+ }
97
+
98
+ /**
99
+ * @return string[]
100
+ */
101
+ public function getIgnoreFiles() {
102
+ return $this->ignoreFiles;
103
+ }
104
+
105
+ /**
106
+ * @return SignaturePool
107
+ */
108
+ public function getSignaturePool() {
109
+ return $this->signaturePool;
110
+ }
111
+
112
+ /**
113
+ * @return int
114
+ */
115
+ public function get_files_count() {
116
+ return $this->files_count;
117
+ }
118
+
119
+ /**
120
+ * @param string $path
121
+ */
122
+ protected function loadFilesFromPath( $path ) {
123
+ foreach ( scandir( $path ) as $newPath ) {
124
+ if ( $newPath == '.' || $newPath == '..' ) {
125
+ continue;
126
+ }
127
+
128
+ if ( in_array( $newPath, $this->ignoreFiles ) ) {
129
+ continue;
130
+ }
131
+
132
+ if ( $path[ strlen( $path ) - 1 ] != '/' ) {
133
+ $path .= '/';
134
+ }
135
+ $newPath = $path . $newPath;
136
+
137
+ if ( is_dir( $newPath ) ) {
138
+ $this->loadFilesFromPath( $newPath );
139
+ } else {
140
+ $this->fileList[] = $this->loadFile( $newPath );
141
+ $this->files_count ++;
142
+ }
143
+ }
144
+ }
145
+
146
+ /**
147
+ * @param string $filePath
148
+ *
149
+ * @param null $fileHash
150
+ *
151
+ * @return File
152
+ */
153
+ protected function loadFile( $filePath, $fileHash = null ) {
154
+ if ( ! file_exists( $filePath ) ) {
155
+ throw new InvalidArgumentException( "File `$filePath` not found" );
156
+ }
157
+
158
+ if ( is_null( $fileHash ) ) {
159
+ $fileHash = md5_file( $filePath );
160
+ } else if ( $fileHash === false ) {
161
+ $fileHash = null;
162
+ }
163
+
164
+ return new File( $filePath, $fileHash );
165
+ }
166
+
167
+ /**
168
+ * @param int $count
169
+ *
170
+ * @return \Generator
171
+ */
172
+ public function scan( $count = 100 ) {
173
+ $i = 0;
174
+ foreach ( $this->fileList as $file ) {
175
+ $i ++;
176
+
177
+ $fileMatch = $this->signaturePool->scanFile( $file );
178
+ if ( ! empty( $fileMatch ) ) {
179
+ $this->updateData( true );
180
+ yield $fileMatch;
181
+ } else {
182
+ $this->updateData( false );
183
+ }
184
+
185
+ if ( $i === $count ) {
186
+ break;
187
+ }
188
+ }
189
+
190
+ $this->fileList = array_slice( $this->fileList, $count );
191
+ }
192
+
193
+ /**
194
+ * @param bool $isSuspicious
195
+ */
196
+ protected function updateData( $isSuspicious ) {
197
+ if ( $this->files_count > 0 ) {
198
+ $this->files_count --;
199
+
200
+ if ( $isSuspicious ) {
201
+ $this->suspicious_count ++;
202
+ } else {
203
+ $this->cleaned_count ++;
204
+ }
205
+ }
206
+ }
207
+ }
includes/scanner/classes/scanner/Signature.php ADDED
@@ -0,0 +1,177 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace WBCR\Titan\MalwareScanner;
4
+
5
+ /**
6
+ * Class Signature
7
+ *
8
+ * @author Alexander Gorenkov <g.a.androidjc2@ya.ru>
9
+ */
10
+ class Signature {
11
+ const TYPE_SERVER = 'server';
12
+ const TYPE_BROWSER = 'browser';
13
+ const TYPE_BOTH = 'both';
14
+
15
+ const SEVER_CRITICAL = 'c';
16
+ const SEVERITY_SUSPICIOUS = 's';
17
+ const SEVER_WARNING = 'w';
18
+ const SEVER_INTO = 'i';
19
+
20
+ /**
21
+ * @var int
22
+ */
23
+ protected $id;
24
+
25
+ /**
26
+ * @var string
27
+ */
28
+ protected $format;
29
+
30
+ /**
31
+ * @var int
32
+ */
33
+ protected $childId;
34
+
35
+ /**
36
+ * @var string
37
+ */
38
+ protected $sever;
39
+
40
+ /**
41
+ * @var string
42
+ */
43
+ protected $title;
44
+
45
+ /**
46
+ * @var string
47
+ */
48
+ protected $type;
49
+
50
+ /**
51
+ * @var int[]
52
+ */
53
+ protected $common_indexes;
54
+
55
+ /**
56
+ * @var string
57
+ */
58
+ protected $signature;
59
+
60
+ /**
61
+ * Signature constructor.
62
+ *
63
+ * @param int $id
64
+ * @param string $format
65
+ * @param int|null $childId
66
+ * @param string $sever
67
+ * @param string $title
68
+ * @param string $type
69
+ * @param int[] $common_indexes
70
+ * @param string $signature
71
+ */
72
+ public function __construct( $id, $format, $childId, $sever, $title, $type, $common_indexes, $signature ) {
73
+ $this->id = (int) $id;
74
+ $this->format = $format;
75
+ $this->childId = (int) $childId;
76
+ $this->sever = $sever;
77
+ $this->title = $title;
78
+ $this->type = $type;
79
+ $this->common_indexes = $common_indexes;
80
+ $this->signature = $signature;
81
+ }
82
+
83
+ /**
84
+ * @return int
85
+ */
86
+ public function getId() {
87
+ return $this->id;
88
+ }
89
+
90
+ /**
91
+ * @return string
92
+ */
93
+ public function getFormat() {
94
+ return $this->format;
95
+ }
96
+
97
+ /**
98
+ * @return int|null
99
+ */
100
+ public function getChildId() {
101
+ return $this->id;
102
+ }
103
+
104
+ /**
105
+ * @return string
106
+ */
107
+ public function getSever() {
108
+ return $this->sever;
109
+ }
110
+
111
+ /**
112
+ * @return string
113
+ */
114
+ public function getTitle() {
115
+ return $this->title;
116
+ }
117
+
118
+ /**
119
+ * @return string
120
+ */
121
+ public function getType() {
122
+ return $this->type;
123
+ }
124
+
125
+ /**
126
+ * @return int[]
127
+ */
128
+ public function getCommonIndexes() {
129
+ return $this->common_indexes;
130
+ }
131
+
132
+ /**
133
+ * @return string
134
+ */
135
+ public function getSignature() {
136
+ return $this->signature;
137
+ }
138
+
139
+ /**
140
+ * @return string
141
+ */
142
+ public function __toString() {
143
+ return sprintf(
144
+ "[ID%s][child=%s sever=%s title=%s format=%s]: %s",
145
+ $this->getId(),
146
+ $this->getChildId(),
147
+ $this->getSever(),
148
+ $this->getTitle(),
149
+ $this->getFormat(),
150
+ $this->getSignature()
151
+ );
152
+ }
153
+
154
+ /**
155
+ * @param array $params
156
+ *
157
+ * @return Signature|null
158
+ */
159
+ public static function fromArray( $params ) {
160
+ if ( empty( $params['id'] ) || empty( $params['format'] ) || empty( $params['severity'] )
161
+ || empty( $params['title'] ) || empty( $params['type'] ) || empty( $params['content'] ) ) {
162
+ return null;
163
+ }
164
+
165
+ if ( ! isset( $params['child_id'] ) ) {
166
+ $params['child_id'] = null;
167
+ }
168
+
169
+ if ( ! isset( $params['common_indexes'] ) ) {
170
+ $params['common_indexes'] = [];
171
+ }
172
+
173
+ return new Signature(
174
+ $params['id'], $params['format'], $params['child_id'], $params['severity'],
175
+ $params['title'], $params['type'], $params['common_indexes'], $params['content'] );
176
+ }
177
+ }
includes/scanner/classes/scanner/SignaturePool.php ADDED
@@ -0,0 +1,190 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace WBCR\Titan\MalwareScanner;
4
+
5
+ use WBCR\Titan\Logger\Writter;
6
+
7
+ /**
8
+ * Class SignaturePool
9
+ *
10
+ * @author Alexander Gorenkov <g.a.androidjc2@ya.ru>
11
+ */
12
+ class SignaturePool {
13
+ /**
14
+ * @var Signature[]
15
+ */
16
+ private $signatures;
17
+
18
+ private static $common_strings = [
19
+ "<\\?php",
20
+ "code",
21
+ "eval",
22
+ "include",
23
+ "content",
24
+ "\\.(?:com|net|org|ru|su)",
25
+ "echo",
26
+ "https?(\\?)?:",
27
+ "<(?:\\?(?:\\s|php|=)|%(?:\\s|=)|script)",
28
+ "<(?:\\s[\\*|\\+](?:\\?)?)?script",
29
+ "hack",
30
+ "error_reporting",
31
+ "<(?:\\s[\\*|\\+](?:\\?)?)?title",
32
+ "\\.html",
33
+ "preg_",
34
+ "\\.php",
35
+ "xml:lang",
36
+ "href",
37
+ "title(?:\\s[\\*|\\+](?:\\?)?)?>",
38
+ "Tornido",
39
+ "<a",
40
+ "function",
41
+ "cialis|viagra",
42
+ "center",
43
+ "copy",
44
+ "new",
45
+ "require",
46
+ "false",
47
+ "document",
48
+ "(?:&Rho;|P)ay(?:&Rho;|P)al",
49
+ "email",
50
+ "window.location",
51
+ "<(?:\\\\?(?:\\\\s|php|=)|%(?:\\\\s|=)|script)"
52
+ ];
53
+
54
+ /**
55
+ * SignaturePool constructor.
56
+ *
57
+ * @param Signature[] $signatures
58
+ */
59
+ public function __construct( $signatures ) {
60
+ $this->signatures = $signatures;
61
+ }
62
+
63
+ /**
64
+ * @return Signature[]
65
+ */
66
+ public function getSignatures() {
67
+ return $this->signatures;
68
+ }
69
+
70
+ /**
71
+ * @param File $file
72
+ *
73
+ * @return Match|null
74
+ */
75
+ public function scanFile( $file ) {
76
+ $fData = fopen($file->getPath(), 'r');
77
+ if($fData === false) {
78
+ $e = sprintf("Open error: %s", $file->getPath());
79
+ Writter::error($e);
80
+ error_log($e);
81
+ return null;
82
+ }
83
+
84
+ $ext = explode('.', $file->getPath());
85
+ $ext = $ext[count($ext) - 1];
86
+
87
+ $isPHP = in_array($ext, ['php', 'phtml']);
88
+ $isHTML = !$isPHP && in_array($ext, ['html']);
89
+ $isJS = !$isPHP && !$isHTML && in_array($ext, ['js', 'svg']);
90
+ $isArchive = !$isPHP && !$isHTML && !$isJS && in_array($ext, ['zip', 'tar', 'gz']);
91
+ $isImage = !$isPHP && !$isHTML && !$isJS && !$isArchive && in_array($ext, [
92
+ 'jpg', 'jpeg', 'png', 'webp', 'gif'
93
+ ]);
94
+ $isMedia = !$isPHP && !$isHTML && !$isJS && !$isArchive && in_array($ext, [
95
+ 'pm3', 'm4v', 'avi', 'mov', 'mp4'
96
+ ]);
97
+ $isBinary = !$isPHP && !$isHTML && !$isJS && !$isArchive && !$isImage && !$isMedia && in_array($ext, [
98
+ 'tiff', 'svg', 'sql', 'tbz2', 'bz2', 'xz', 'zip', 'tgz', 'gz', 'log', 'err'
99
+ ]);
100
+ $isUnknown = !$isPHP && !$isHTML && !$isJS && !$isArchive && !$isImage && !$isMedia && !$isBinary;
101
+
102
+ if ( $isImage && function_exists( 'exif_imagetype' ) ) {
103
+ $type = @exif_imagetype( $file->getPath() );
104
+ if ( $type !== false ) {
105
+ return null;
106
+ }
107
+ }
108
+
109
+ if ( $isMedia ) {
110
+ return null;
111
+ }
112
+
113
+ $chunk = 0;
114
+ while(!feof($fData)) {
115
+ if($isUnknown || $isArchive) {
116
+ fclose($fData);
117
+ return null;
118
+ }
119
+ $chunk++;
120
+ $data = fread($fData, 1024 * 1024 * 1); // 1MB
121
+
122
+ foreach($this->signatures as $signature) {
123
+ $type = $signature->getType();
124
+ if(empty($type)) {
125
+ $type = 'server';
126
+ }
127
+
128
+ if($type == 'ignore') {
129
+ continue;
130
+ }
131
+
132
+ if($type == Signature::TYPE_SERVER && !($isPHP || $isHTML)) {
133
+ continue;
134
+ }
135
+
136
+ if(in_array($type, [Signature::TYPE_BOTH, Signature::TYPE_BROWSER]) && ($isPHP || $isHTML)) {
137
+ continue;
138
+ }
139
+
140
+ if(substr($signature->getSignature(), 0, 1) == '^') {
141
+ // Skipping malware signature ({$rule[0]}) because it only applies to the file beginning
142
+ continue;
143
+ }
144
+
145
+ foreach($signature->getCommonIndexes() as $index) {
146
+ if(!isset(self::$common_strings[$index])) {
147
+ continue;
148
+ }
149
+ $s = self::$common_strings[$index];
150
+ if(preg_match('/' . $s . '/i', $data)) {
151
+ continue 2;
152
+ }
153
+ }
154
+
155
+ $found = preg_match("/(" . $signature->getSignature() . ")/iS", $data, $matched,
156
+ PREG_OFFSET_CAPTURE);
157
+ if($found) {
158
+ $match = $matched[0];
159
+ return new Match($signature, $file, $match[1], $match[0]);
160
+ }
161
+ }
162
+ }
163
+
164
+ fclose($fData);
165
+
166
+ $file->clearLoadedData();
167
+ gc_collect_cycles();
168
+
169
+ return null;
170
+ }
171
+
172
+ /**
173
+ * @param array[] $params
174
+ *
175
+ * @return SignaturePool
176
+ */
177
+ public static function fromArray( $params ) {
178
+ $signatures = [];
179
+ foreach ( $params as $signature ) {
180
+ $signature = Signature::fromArray( $signature );
181
+ if ( is_null( $signature ) ) {
182
+ continue;
183
+ }
184
+
185
+ $signatures[ $signature->getId() ] = $signature;
186
+ }
187
+
188
+ return new SignaturePool( $signatures );
189
+ }
190
+ }
includes/scanner/classes/scanner/boot.php ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // Exit if accessed directly
4
+ //if ( ! defined( 'ABSPATH' ) ) {
5
+ // exit;
6
+ //}
7
+
8
+ require_once 'File.php';
9
+ require_once 'Match.php';
10
+ require_once 'Scanner.php';
11
+ require_once 'Signature.php';
12
+ require_once 'SignaturePool.php';
13
+ require_once 'HashListPool.php';
includes/scanner/test.php ADDED
@@ -0,0 +1,55 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ define('WTITAN_PLUGIN_DIR', dirname(dirname(dirname(__FILE__))));
4
+
5
+ function loader($class) {
6
+ $classMap = [
7
+ 'Page' => WTITAN_PLUGIN_DIR . '/admin/pages',
8
+ 'Client' => WTITAN_PLUGIN_DIR . '/libs/api-client',
9
+ 'Entity' => WTITAN_PLUGIN_DIR . '/libs/api-client/entity',
10
+ 'Request' => WTITAN_PLUGIN_DIR . '/libs/api-client/request',
11
+ 'Response' => WTITAN_PLUGIN_DIR . '/libs/api-client/response',
12
+ 'Method' => WTITAN_PLUGIN_DIR . '/libs/api-client/response/method',
13
+ 'MalwareScanner' => WTITAN_PLUGIN_DIR . '/includes/scanner/classes/scanner',
14
+ ];
15
+
16
+ $className = substr($class, strrpos($class, '\\') + 1);
17
+ $lastNamespace = explode('\\', $class);
18
+ $lastNamespace = $lastNamespace[count($lastNamespace) - 2];
19
+
20
+ $path = $classMap[$lastNamespace] . '/class.' . strtolower($className) . '.php';
21
+ if(!file_exists($path)) {
22
+ $path = $classMap[$lastNamespace] . '/class-' . strtolower($className) . '.php';
23
+ }
24
+
25
+ if(!file_exists($path)) {
26
+ $path = $classMap[$lastNamespace] . '/' . $className . '.php';
27
+ }
28
+
29
+ if(!file_exists($path)) {
30
+ die($path);
31
+ }
32
+
33
+ /** @noinspection PhpIncludeInspection */
34
+ include $path;
35
+ }
36
+ spl_autoload_register('loader');
37
+
38
+
39
+
40
+
41
+
42
+
43
+ $client = new \WBCR\Titan\Client\Client('sk_uR~5IArjgfpf-2giAK^hMg5kfC3-7');
44
+
45
+ /** @var array $signatures */
46
+ $signatures = $client->get_signatures();
47
+ foreach($signatures as $key => $signature) {
48
+ $signatures[$key] = $signature->to_array();
49
+ }
50
+ $signature_pool = \WBCR\Titan\MalwareScanner\SignaturePool::fromArray($signatures);
51
+
52
+ $scanner = new \WBCR\Titan\MalwareScanner\Scanner(dirname(dirname(WTITAN_PLUGIN_DIR)), $signature_pool);
53
+ $matches = $scanner->scan();
54
+
55
+ var_dump($matches);
includes/scanner/views/results.php ADDED
@@ -0,0 +1,87 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @var bool $scanner_started
4
+ * @var Match[] $matched
5
+ * @var float $progress
6
+ * @var int $cleaned
7
+ * @var int $suspicious
8
+ */
9
+
10
+ use WBCR\Titan\MalwareScanner\Match;
11
+
12
+ if($matched === false ) {
13
+ ?>
14
+ <div class="wtitan-audit-empty-container">
15
+ <?= sprintf(__('Click %1s to start scanning for malware','titan-security'), '<span class="btn btn-primary wt-nobutton">'.__('Start scan','titan-security').'</span>');?>
16
+ </div>
17
+ <?php
18
+
19
+ }
20
+ else if(!empty($matched)) {
21
+ ?>
22
+ <div class="wtitan-scanner-vulner-table-container wtitan-scanner-results">
23
+ <table class="table table-striped table-hover table-responsive" width="100%">
24
+ <thead>
25
+ <tr>
26
+ <td class="wtitan-vulner-table-severity"></td>
27
+ <td class="wtitan-vulner-table-name" style="text-align: left;">Path</td>
28
+ <td class="wtitan-vulner-table-slim">Type</td>
29
+ <td class="wtitan-vulner-table-slim">Match</td>
30
+ </tr>
31
+
32
+ </thead>
33
+ <tbody>
34
+ <?php foreach ( $matched as $file_path => $match ): ?>
35
+ <?php if ( $match instanceof Match ): ?>
36
+ <tr>
37
+ <?php switch($match->getSignature()->getSever()) {
38
+ case \WBCR\Titan\MalwareScanner\Signature::SEVER_CRITICAL: ?>
39
+ <td class="wt-severity-high"></td>
40
+ <?php break;
41
+
42
+ case \WBCR\Titan\MalwareScanner\Signature::SEVERITY_SUSPICIOUS: ?>
43
+ <td class="wt-severity-medium"></td>
44
+ <?php break;
45
+
46
+ case \WBCR\Titan\MalwareScanner\Signature::SEVER_WARNING: ?>
47
+ <td class="wt-severity-medium"></td>
48
+ <?php break;
49
+
50
+ case \WBCR\Titan\MalwareScanner\Signature::SEVER_INTO: ?>
51
+ <td class="wt-severity-low"></td>
52
+ <?php break;
53
+ } ?>
54
+ <td style="text-align: left;"><?php echo $match->getFile()->getPath( true ) ?></td>
55
+ <td><?php echo $match->getSignature()->getTitle() ?></td>
56
+ <td><code><?php echo htmlspecialchars( $match->getMatch() ); ?></code></td>
57
+ </tr>
58
+ <?php elseif ( $match instanceof \WBCR\Titan\Client\Entity\CmsCheckItem ): ?>
59
+ <tr>
60
+ <td><?php echo $match->path ?></td>
61
+ <td>Corrupted file</td>
62
+ <td><?php echo $match->action ?></td>
63
+ <td></td>
64
+ </tr>
65
+ <?php else: ?>
66
+ <tr>
67
+ <td><?php echo $file_path ?></td>
68
+ <td><?php var_dump( $match ) ?></td>
69
+ <td>null</td>
70
+ <td></td>
71
+ </tr>
72
+ <?php endif; ?>
73
+ <?php endforeach; ?>
74
+ </tbody>
75
+ </table>
76
+ </div>
77
+ <?php
78
+ }
79
+ else {
80
+ ?>
81
+ <div class="wtitan-audit-empty-container">
82
+ <?= __('No malware found','titan-security');?>
83
+ </div>
84
+ <?php
85
+
86
+ }
87
+ ?>
includes/scanner/views/scanner.php ADDED
@@ -0,0 +1,76 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @var bool $scanner_started
4
+ * @var Match[] $matched
5
+ * @var float $progress
6
+ * @var int $cleaned
7
+ * @var int $suspicious
8
+ */
9
+
10
+ use WBCR\Titan\MalwareScanner\Match;
11
+ ?>
12
+ <div class="wbcr-content-section">
13
+ <div class="wt-scanner-container wt-scanner-block-scan">
14
+ <table>
15
+ <tr>
16
+ <td>
17
+ <h4><?php echo __('Malware scan','titan-security'); ?></h4>
18
+ <div class="wrio-statistic-buttons-wrap">
19
+ <?php if ( $scanner_started ): ?>
20
+ <button type="button" id="scan" data-action="stop_scan" class="wt-malware-scan-button">
21
+ <span class="text"><?php echo __( 'Stop scanning', 'titan-security' ) ?></span>
22
+ </button>
23
+ <?php else: ?>
24
+ <button type="button" id="scan" data-action="start_scan" class="wt-malware-scan-button">
25
+ <?php echo __( 'Scan', 'titan-security' ) ?>
26
+ </button>
27
+ <?php endif; ?>
28
+ <div class="wt-scan-icon-loader" data-status="" style="display: none"></div>
29
+ </div>
30
+ </td>
31
+ <td>
32
+ <h4><?php echo __('Description','titan-security'); ?></h4>
33
+ <p><?php echo __('Scanning all files of your site for malware. At each launch, site scanning starts from the beginning','titan-security'); ?>
34
+ </p>
35
+ </td>
36
+ </tr>
37
+ </table>
38
+
39
+ <div class="wio-columns wio-page-statistic">
40
+ <div>
41
+ <div class="wio-chart-container wio-overview-chart-container">
42
+ <canvas id="wtitan-scan-chart" width="180" height="180"
43
+ data-cleaned="<?php echo $cleaned ?>" data-suspicious="<?php echo $suspicious ?>"
44
+ style="display: block;">
45
+ </canvas>
46
+ <div id="wt-total-percent-chart" class="wio-chart-percent">
47
+ <?php echo round( $progress, 1 ) ?><span>%</span>
48
+ </div>
49
+ <p class="wio-global-optim-phrase wio-clear">
50
+ Scanned <span class="wio-total-percent" id="wt-total-percent">
51
+ <?php echo round( $progress, 1 ) ?>%
52
+ </span>
53
+ of your website's files
54
+ </p>
55
+ </div>
56
+ <div style="margin-left:200px;">
57
+ <div id="wio-overview-chart-legend">
58
+ <ul class="wio-doughnut-legend">
59
+ <li>
60
+ <span style="background-color:#5d05b7"></span>
61
+ Cleaned -
62
+ <span class="wio-num" id="wtitan-cleaned-num"><?php echo $cleaned ?></span>
63
+ </li>
64
+ <li>
65
+ <span style="background-color:#f1b1b6"></span>
66
+ Suspicious -
67
+ <span class="wio-num" id="wtitan-suspicious-num"><?php echo $suspicious ?></span>
68
+ </li>
69
+ </ul>
70
+ </div>
71
+ <?php echo $this->render_template( 'results', $args);?>
72
+ </div>
73
+ </div>
74
+ </div>
75
+ </div>
76
+ </div>
includes/sitechecker/assets/css/sitechecker-dashboard.css ADDED
@@ -0,0 +1,180 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ .wtitan-sitechecker-container
2
+ {
3
+ margin: 15px 15px 15px 15px;
4
+ }
5
+ /*----------------*/
6
+ .wt-sitechecker-block
7
+ {
8
+ text-align: center;
9
+ }
10
+ .wt-sitechecker-block table {
11
+ width: 100%;
12
+ }
13
+ .wt-sitechecker-block table td:first-child {
14
+ width: 20%;
15
+ }
16
+ .wt-sitechecker-block table td {
17
+ border: 1px solid #efefef;
18
+ background: #fff;
19
+ text-align: center;
20
+ padding: 20px;
21
+ }
22
+ .wt-sitechecker-block table td h4 {
23
+ font-family: Roboto, "Helvetica Neue", Helvetica, Arial, sans-serif;
24
+ font-size: 16px;
25
+ }
26
+ /*-------------------*/
27
+ .wtitan-sitechecker-table-container
28
+ {
29
+ /*max-height: 30em;
30
+ margin: 0px 0px 15px 0px;
31
+ overflow: visible;*/
32
+ }
33
+ .wtitan-sitechecker-container
34
+ {
35
+ margin: 15px 15px 15px 15px;
36
+ }
37
+
38
+ .wtitan-sitechecker-table-container table
39
+ {
40
+ font-family: Roboto, "Helvetica Neue", Helvetica, Arial, sans-serif;
41
+ width: 100%;
42
+ min-width: 100%;
43
+ display: flex;
44
+ flex-flow: column;
45
+ height: 100%;
46
+
47
+ table-layout: fixed;
48
+ box-sizing: border-box;
49
+ border-spacing: 2px;
50
+ border-top: 2px dashed #cac9c9;
51
+ background-color: #efefef;
52
+ }
53
+ .wtitan-sitechecker-table-container table tbody
54
+ {
55
+ flex: 1 1 auto;
56
+ display: block;
57
+ overflow-y: scroll;
58
+ }
59
+
60
+ .wtitan-sitechecker-table-container table td {
61
+ border-top: 1px !important;
62
+ /*border-bottom: 1px !important;*/
63
+ border-left: 0px !important;
64
+ border-right: 0px !important;
65
+
66
+ border-color: #efefef;
67
+ border-style: solid;
68
+ border-collapse: collapse;
69
+
70
+ box-sizing:border-box;
71
+ background: #fff;
72
+ text-align: center;
73
+ padding: 10px 20px !important;
74
+ vertical-align: middle !important;
75
+ }
76
+ .wtitan-sitechecker-table-container table td.wtitan-sitechecker-table-url
77
+ {
78
+ text-align: left !important;
79
+ width: 100%;
80
+ }
81
+ .wtitan-sitechecker-table-url a
82
+ {
83
+ text-decoration: none;
84
+ }
85
+ .wtitan-sitechecker-table-url a:hover
86
+ {
87
+ text-decoration: underline;
88
+ }
89
+
90
+ .wtitan-sitechecker-table-container table tr.wtitan-sitechecker-table-first-tr td {
91
+ border-top: 0;
92
+ /*border-bottom: 1px !important;*/
93
+ border-left: 0;
94
+ border-right: 0;
95
+ border-color: rgba(0,0,0,0.1);
96
+ border-style: solid;
97
+ border-collapse: collapse;
98
+ background-color: #efefef !important;
99
+ vertical-align: middle !important;
100
+ text-align: center;
101
+ font-weight: bold;
102
+ padding: 10px !important;
103
+ line-height: 1.1 !important;
104
+ }
105
+ .wtitan-sitechecker-table-first-tr td
106
+ {
107
+ position: sticky;
108
+ position: -webkit-sticky; /* for safari */
109
+ top: 0px;
110
+ box-shadow: 0px 2px 6px -3px rgba(0, 0, 0, 0.5);
111
+ }
112
+
113
+ .wtitan-sitechecker-table-slim
114
+ {
115
+ width: 5%;
116
+ }
117
+ .wtitan-sitechecker-table-name
118
+ {
119
+ width: 25%;
120
+ }
121
+ /*-------------------*/
122
+ .wt-sitechecker-button-subscribe
123
+ {
124
+ font-size: 16px !important;
125
+ /*box-shadow: 0px 0px 10px 3px rgba(0, 0, 0, 0.2);*/
126
+ border: 1px solid #0055808c !important;
127
+ }
128
+ .wt-sitechecker-button-delete
129
+ {
130
+ background: transparent url("../img/delete.png") no-repeat center center !important;
131
+ background-size: contain !important;
132
+ padding: 12px;
133
+ outline: none;
134
+ }
135
+ /*----------------------*/
136
+ .wt-sitechecker-form-add
137
+ {
138
+ text-align: center;
139
+ width: 100%;
140
+ margin: 0 -20px;
141
+ }
142
+ .wt-sitechecker-form-add input[type='text']
143
+ {
144
+ width: 60%;
145
+ display: inline-block !important;
146
+ }
147
+ .wt-sitechecker-form-add label
148
+ {
149
+ font-weight: 500;
150
+ }
151
+ .wt-sitechecker-button-add
152
+ {
153
+ height: 34px;
154
+ }
155
+ .wt-spinner
156
+ {
157
+ background: url('../img/loader.gif') no-repeat center center;
158
+ background-size: contain;
159
+ display: inline-block;
160
+ float: right;
161
+ vertical-align: middle;
162
+ opacity: 0.7;
163
+ filter: alpha(opacity=70);
164
+ width: 30px;
165
+ height: 30px;
166
+ }
167
+ .wt-checker-error
168
+ {
169
+ display: block;
170
+ color: #f6065b;
171
+ margin-top: 5px;
172
+ }
173
+ .wtitan-sitechecker-pro-container
174
+ {
175
+ background-color: #efefef;
176
+ padding: 20px 0;
177
+ border-bottom: 1px solid #ddd;
178
+ border-top: 1px solid #ddd;
179
+ margin-bottom: 20px;
180
+ }
includes/sitechecker/assets/img/delete.png ADDED
Binary file
includes/sitechecker/assets/img/loader.gif ADDED
Binary file
includes/sitechecker/assets/js/app.js ADDED
@@ -0,0 +1,234 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * @var {String} wtitan
3
+ */
4
+
5
+ (function ($) {
6
+ firebase.initializeApp({
7
+ apiKey: "AIzaSyAAkQhmoqd-emHHsK5CP79eVUcci7umGMs",
8
+ authDomain: "titan-security.firebaseapp.com",
9
+ databaseURL: "https://titan-security.firebaseio.com",
10
+ projectId: "titan-security",
11
+ storageBucket: "titan-security.appspot.com",
12
+ messagingSenderId: "313719964045",
13
+ appId: "1:313719964045:web:7bd9fa554204728914bdc4",
14
+ measurementId: "G-G5LGKBYFE5"
15
+ });
16
+
17
+ var storageTokenKey = 'firebase-messaging-token';
18
+ var subscribe_bt = $('#subscribe');
19
+ var unsubscribe_bt = $('#unsubscribe');
20
+
21
+ if (
22
+ 'Notification' in window &&
23
+ 'serviceWorker' in navigator &&
24
+ 'localStorage' in window &&
25
+ 'fetch' in window &&
26
+ 'postMessage' in window
27
+ ) {
28
+ var messaging = firebase.messaging();
29
+
30
+ // already granted
31
+ if (Notification.permission === 'granted') {
32
+ getToken();
33
+ }
34
+
35
+ subscribe_bt.on('click', function() {
36
+ showNotice('Subscribing...', 'info', 1500);
37
+ getToken();
38
+ });
39
+
40
+ unsubscribe_bt.on('click', function() {
41
+ messaging.getToken()
42
+ .then(function(currentToken) {
43
+ messaging.deleteToken(currentToken)
44
+ .then(function() {
45
+ console.log('Token deleted');
46
+ sendTokenToServer(undefined);
47
+ resetUI();
48
+ })
49
+ });
50
+ });
51
+
52
+ navigator.serviceWorker
53
+ .register(wtitan.path, {scope:wtitan.scope})
54
+ .then(function() {
55
+ console.log("ServiceWorker was registered");
56
+ });
57
+
58
+ messaging.onMessage(function(payload) {
59
+ console.log('Message received', payload);
60
+
61
+ Notification.requestPermission()
62
+ .then(function(permission) {
63
+ if (permission === 'granted') {
64
+ navigator.serviceWorker.ready.then(function(registration) {
65
+ // Copy data object to get parameters in the click handler
66
+ payload.data.data = JSON.parse(JSON.stringify(payload.data));
67
+
68
+ registration.showNotification(payload.data.title, payload.data);
69
+ }).catch(function(error) {
70
+ // registration failed :(
71
+ showError('ServiceWorker registration failed', error);
72
+ });
73
+ }
74
+ })
75
+ });
76
+
77
+ // Callback fired if Instance ID token is updated.
78
+ messaging.onTokenRefresh(function() {
79
+ messaging.getToken()
80
+ .then(function(refreshedToken) {
81
+ console.log('Token refreshed');
82
+ // Send Instance ID token to app server.
83
+ sendTokenToServer(refreshedToken);
84
+ })
85
+ .catch(function(error) {
86
+ showError('Unable to retrieve refreshed token', error);
87
+ });
88
+ });
89
+
90
+ } else {
91
+ if (!('Notification' in window)) {
92
+ showErrorNearButton(wt_app.notice);
93
+ } else if (!('serviceWorker' in navigator)) {
94
+ console.warn(wt_app.worker);
95
+ } else if (!('localStorage' in window)) {
96
+ console.warn('LocalStorage not supported');
97
+ } else if (!('fetch' in window)) {
98
+ console.warn('fetch not supported');
99
+ } else if (!('postMessage' in window)) {
100
+ console.warn('postMessage not supported');
101
+ }
102
+
103
+ if(!window.location.protocol.startsWith('https')) {
104
+ showErrorNearButton(wt_app.https);
105
+ }
106
+
107
+ console.warn('This browser does not support desktop notification.');
108
+ console.log('Is HTTPS', window.location.protocol.startsWith('https'));
109
+ console.log('Support Notification', 'Notification' in window);
110
+ console.log('Support ServiceWorker', 'serviceWorker' in navigator);
111
+ console.log('Support LocalStorage', 'localStorage' in window);
112
+ console.log('Support fetch', 'fetch' in window);
113
+ console.log('Support postMessage', 'postMessage' in window);
114
+
115
+ subscribe_bt.attr('disabled', 'disabled');
116
+ }
117
+
118
+ /**
119
+ * @param {String} currentToken
120
+ */
121
+ function sendTokenToServer (currentToken) {
122
+ if(typeof currentToken === 'undefined') {
123
+ window.localStorage.removeItem(storageTokenKey);
124
+ }
125
+
126
+ if(!isTokenSentToServer(currentToken)) {
127
+ $.post(ajaxurl, {
128
+ action: 'push_token',
129
+ _wpnonce: wtitan.pushTokenNonce,
130
+ token: currentToken
131
+ }, function(response) {
132
+ if(response.success) {
133
+ showNotice(response.data.message, 'success', 5000);
134
+ } else {
135
+ showNotice(response.data.error_message, 'danger', 5000);
136
+ }
137
+ });
138
+ setSentTokenToServer(currentToken);
139
+ }
140
+ }
141
+
142
+ /**
143
+ * @param {String} currentToken
144
+ * @returns {boolean}
145
+ */
146
+ function isTokenSentToServer(currentToken) {
147
+ return window.localStorage.getItem(storageTokenKey) === currentToken;
148
+ }
149
+
150
+ /**
151
+ * @param {String} currentToken
152
+ */
153
+ function setSentTokenToServer(currentToken) {
154
+ if (currentToken) {
155
+ window.localStorage.setItem(storageTokenKey, currentToken);
156
+ } else {
157
+ window.localStorage.removeItem(storageTokenKey);
158
+ }
159
+ }
160
+
161
+ function showError (error, error_data) {
162
+ if (typeof error_data !== "undefined") {
163
+ console.error(error, error_data);
164
+ } else {
165
+ console.error(error);
166
+ }
167
+
168
+ showNotice(error, 'danger', 0);
169
+ }
170
+
171
+ function showErrorNearButton (error, error_data) {
172
+ if (typeof error_data !== "undefined") {
173
+ console.error(error, error_data);
174
+ } else {
175
+ console.error(error);
176
+ }
177
+
178
+ $('.wt-sitechecker-button-subscribe#subscribe').after('<div class="wt-checker-error">'+error+'</div>');
179
+ //showNotice(error, 'danger', 0);
180
+ }
181
+
182
+ function getToken() {
183
+ messaging.requestPermission()
184
+ .then(function() {
185
+ messaging.getToken()
186
+ .then(function(currentToken) {
187
+ if (currentToken) {
188
+ console.log("Token received: ", currentToken);
189
+ sendTokenToServer(currentToken);
190
+ subscribe_bt.hide();
191
+ unsubscribe_bt.show();
192
+ document.cookie = "wt-push-subscribe=1; expires=Tue, 19 Jan 2040 03:14:07 GMT";
193
+ } else {
194
+ showError('No Instance ID token available. Request permission to generate one');
195
+ setSentTokenToServer(undefined);
196
+ }
197
+ })
198
+ .catch(function(error) {
199
+ showError('An error occurred while retrieving token', error);
200
+ setSentTokenToServer(undefined);
201
+ });
202
+ })
203
+ .catch(function(error) {
204
+ showError('Unable to get permission to notify', error);
205
+ });
206
+ }
207
+
208
+ function showNotice(message, type, timeout) {
209
+ if(typeof type === 'undefined') {
210
+ type = 'success';
211
+ }
212
+
213
+ if(typeof timeout === 'undefined') {
214
+ timeout = 5000;
215
+ }
216
+
217
+ if(typeof $ === 'undefined' || typeof $.wbcr_factory_clearfy_218 === 'undefined') {
218
+ return;
219
+ }
220
+
221
+ var noticeId = $.wbcr_factory_clearfy_218.app.showNotice(message, type);
222
+ if(timeout > 0) {
223
+ setTimeout(function() {
224
+ $.wbcr_factory_clearfy_218.app.hideNotice(noticeId);
225
+ }, timeout);
226
+ }
227
+ }
228
+
229
+ function resetUI() {
230
+ subscribe_bt.show();
231
+ unsubscribe_bt.hide();
232
+ document.cookie = "wt-push-subscribe=0; max-age=0";
233
+ }
234
+ })(jQuery);
includes/sitechecker/assets/js/firebase-messaging-sw.js ADDED
@@ -0,0 +1,46 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ importScripts('https://www.gstatic.com/firebasejs/7.9.1/firebase-app.js');
2
+ importScripts('https://www.gstatic.com/firebasejs/7.9.1/firebase-messaging.js');
3
+
4
+ firebase.initializeApp({
5
+ apiKey: "AIzaSyAAkQhmoqd-emHHsK5CP79eVUcci7umGMs",
6
+ authDomain: "titan-security.firebaseapp.com",
7
+ databaseURL: "https://titan-security.firebaseio.com",
8
+ projectId: "titan-security",
9
+ storageBucket: "titan-security.appspot.com",
10
+ messagingSenderId: "313719964045",
11
+ appId: "1:313719964045:web:7bd9fa554204728914bdc4",
12
+ measurementId: "G-G5LGKBYFE5"
13
+ });
14
+
15
+ const messaging = firebase.messaging();
16
+
17
+ // Customize notification handler
18
+ messaging.setBackgroundMessageHandler(function(payload) {
19
+ console.log('Handling background message', payload);
20
+
21
+ // Copy data object to get parameters in the click handler
22
+ payload.data.data = JSON.parse(JSON.stringify(payload.data));
23
+
24
+ return self.registration.showNotification(payload.data.title, payload.data);
25
+ });
26
+
27
+ self.addEventListener('notificationclick', function(event) {
28
+ const target = event.notification.data.click_action || '/';
29
+ event.notification.close();
30
+
31
+ // This looks to see if the current is already open and focuses if it is
32
+ event.waitUntil(clients.matchAll({
33
+ type: 'window',
34
+ includeUncontrolled: true
35
+ }).then(function(clientList) {
36
+ // clientList always is empty?!
37
+ for (var i = 0; i < clientList.length; i++) {
38
+ var client = clientList[i];
39
+ if (client.url === target && 'focus' in client) {
40
+ return client.focus();
41
+ }
42
+ }
43
+
44
+ return clients.openWindow(target);
45
+ }));
46
+ });
includes/sitechecker/assets/js/firebase.min.js ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
1
+ /**
2
+ * @var {Object} wtitan
3
+ */
4
+ !function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):(e=e||self).firebase=t()}(this,function(){"use strict";var r=function(e,t){return(r=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(e,t){e.__proto__=t}||function(e,t){for(var r in t)t.hasOwnProperty(r)&&(e[r]=t[r])})(e,t)};var a=function(){return(a=Object.assign||function(e){for(var t,r=1,n=arguments.length;r<n;r++)for(var i in t=arguments[r])Object.prototype.hasOwnProperty.call(t,i)&&(e[i]=t[i]);return e}).apply(this,arguments)};function e(r,n){var i,o,a,e,s={label:0,sent:function(){if(1&a[0])throw a[1];return a[1]},trys:[],ops:[]};return e={next:t(0),throw:t(1),return:t(2)},"function"==typeof Symbol&&(e[Symbol.iterator]=function(){return this}),e;function t(t){return function(e){return function(t){if(i)throw new TypeError("Generator is already executing.");for(;s;)try{if(i=1,o&&(a=2&t[0]?o.return:t[0]?o.throw||((a=o.return)&&a.call(o),0):o.next)&&!(a=a.call(o,t[1])).done)return a;switch(o=0,a&&(t=[2&t[0],a.value]),t[0]){case 0:case 1:a=t;break;case 4:return s.label++,{value:t[1],done:!1};case 5:s.label++,o=t[1],t=[0];continue;case 7:t=s.ops.pop(),s.trys.pop();continue;default:if(!(a=0<(a=s.trys).length&&a[a.length-1])&&(6===t[0]||2===t[0])){s=0;continue}if(3===t[0]&&(!a||t[1]>a[0]&&t[1]<a[3])){s.label=t[1];break}if(6===t[0]&&s.label<a[1]){s.label=a[1],a=t;break}if(a&&s.label<a[2]){s.label=a[2],s.ops.push(t);break}a[2]&&s.ops.pop(),s.trys.pop();continue}t=n.call(r,s)}catch(e){t=[6,e],o=0}finally{i=a=0}if(5&t[0])throw t[1];return{value:t[0]?t[1]:void 0,done:!0}}([t,e])}}}function d(e){var t="function"==typeof Symbol&&e[Symbol.iterator],r=0;return t?t.call(e):{next:function(){return e&&r>=e.length&&(e=void 0),{value:e&&e[r++],done:!e}}}}function p(e,t){var r="function"==typeof Symbol&&e[Symbol.iterator];if(!r)return e;var n,i,o=r.call(e),a=[];try{for(;(void 0===t||0<t--)&&!(n=o.next()).done;)a.push(n.value)}catch(e){i={error:e}}finally{try{n&&!n.done&&(r=o.return)&&r.call(o)}finally{if(i)throw i.error}}return a}function v(e,t){if(!(t instanceof Object))return t;switch(t.constructor){case Date:return new Date(t.getTime());case Object:void 0===e&&(e={});break;case Array:e=[];break;default:return t}for(var r in t)t.hasOwnProperty(r)&&(e[r]=v(e[r],t[r]));return e}var i=(t.prototype.wrapCallback=function(r){var n=this;return function(e,t){e?n.reject(e):n.resolve(t),"function"==typeof r&&(n.promise.catch(function(){}),1===r.length?r(e):r(e,t))}},t);function t(){var r=this;this.reject=function(){},this.resolve=function(){},this.promise=new Promise(function(e,t){r.resolve=e,r.reject=t})}var n,o,s,f=(s=Error,r(n=l,o=s),void(n.prototype=null===o?Object.create(o):(c.prototype=o.prototype,new c)),l);function c(){this.constructor=n}function l(e,t){var r=s.call(this,t)||this;return r.code=e,r.name="FirebaseError",Object.setPrototypeOf(r,l.prototype),Error.captureStackTrace&&Error.captureStackTrace(r,u.prototype.create),r}var u=(h.prototype.create=function(e){for(var t=[],r=1;r<arguments.length;r++)t[r-1]=arguments[r];for(var n=t[0]||{},i=this.service+"/"+e,o=this.errors[e],a=o?function(e,n){return e.replace(m,function(e,t){var r=n[t];return null!=r?r.toString():"<"+t+"?>"})}(o,n):"Error",s=this.serviceName+": "+a+" ("+i+").",c=new f(i,s),l=0,p=Object.keys(n);l<p.length;l++){var u=p[l];"_"!==u.slice(-1)&&(u in c&&console.warn('Overwriting FirebaseError base field "'+u+'" can cause unexpected behavior.'),c[u]=n[u])}return c},h);function h(e,t,r){this.service=e,this.serviceName=t,this.errors=r}var m=/\{\$([^}]+)}/g;function y(e,t){return Object.prototype.hasOwnProperty.call(e,t)}function b(e,t){var r=new g(e,t);return r.subscribe.bind(r)}var g=(w.prototype.next=function(t){this.forEachObserver(function(e){e.next(t)})},w.prototype.error=function(t){this.forEachObserver(function(e){e.error(t)}),this.close(t)},w.prototype.complete=function(){this.forEachObserver(function(e){e.complete()}),this.close()},w.prototype.subscribe=function(e,t,r){var n,i=this;if(void 0===e&&void 0===t&&void 0===r)throw new Error("Missing Observer.");void 0===(n=function(e,t){if("object"!=typeof e||null===e)return!1;for(var r=0,n=t;r<n.length;r++){var i=n[r];if(i in e&&"function"==typeof e[i])return!0}return!1}(e,["next","error","complete"])?e:{next:e,error:t,complete:r}).next&&(n.next=O),void 0===n.error&&(n.error=O),void 0===n.complete&&(n.complete=O);var o=this.unsubscribeOne.bind(this,this.observers.length);return this.finalized&&this.task.then(function(){try{i.finalError?n.error(i.finalError):n.complete()}catch(e){}}),this.observers.push(n),o},w.prototype.unsubscribeOne=function(e){void 0!==this.observers&&void 0!==this.observers[e]&&(delete this.observers[e],this.observerCount-=1,0===this.observerCount&&void 0!==this.onNoObservers&&this.onNoObservers(this))},w.prototype.forEachObserver=function(e){if(!this.finalized)for(var t=0;t<this.observers.length;t++)this.sendOne(t,e)},w.prototype.sendOne=function(e,t){var r=this;this.task.then(function(){if(void 0!==r.observers&&void 0!==r.observers[e])try{t(r.observers[e])}catch(e){"undefined"!=typeof console&&console.error&&console.error(e)}})},w.prototype.close=function(e){var t=this;this.finalized||(this.finalized=!0,void 0!==e&&(this.finalError=e),this.task.then(function(){t.observers=void 0,t.onNoObservers=void 0}))},w);function w(e,t){var r=this;this.observers=[],this.unsubscribes=[],this.observerCount=0,this.task=Promise.resolve(),this.finalized=!1,this.onNoObservers=t,this.task.then(function(){e(r)}).catch(function(e){r.error(e)})}function O(){}var E=(I.prototype.setInstantiationMode=function(e){return this.instantiationMode=e,this},I.prototype.setMultipleInstances=function(e){return this.multipleInstances=e,this},I.prototype.setServiceProps=function(e){return this.serviceProps=e,this},I);function I(e,t,r){this.name=e,this.instanceFactory=t,this.type=r,this.multipleInstances=!1,this.serviceProps={},this.instantiationMode="LAZY"}var _="[DEFAULT]",N=(P.prototype.get=function(e){void 0===e&&(e=_);var t=this.normalizeInstanceIdentifier(e);if(!this.instancesDeferred.has(t)){var r=new i;this.instancesDeferred.set(t,r);try{var n=this.getOrInitializeService(t);n&&r.resolve(n)}catch(e){}}return this.instancesDeferred.get(t).promise},P.prototype.getImmediate=function(e){var t=a({identifier:_,optional:!1},e),r=t.identifier,n=t.optional,i=this.normalizeInstanceIdentifier(r);try{var o=this.getOrInitializeService(i);if(o)return o;if(n)return null;throw Error("Service "+this.name+" is not available")}catch(e){if(n)return null;throw e}},P.prototype.getComponent=function(){return this.component},P.prototype.setComponent=function(e){var t,r;if(e.name!==this.name)throw Error("Mismatching Component "+e.name+" for Provider "+this.name+".");if(this.component)throw Error("Component for "+this.name+" has already been provided");if(function(e){return"EAGER"===e.instantiationMode}(this.component=e))try{this.getOrInitializeService(_)}catch(e){}try{for(var n=d(this.instancesDeferred.entries()),i=n.next();!i.done;i=n.next()){var o=p(i.value,2),a=o[0],s=o[1],c=this.normalizeInstanceIdentifier(a);try{var l=this.getOrInitializeService(c);s.resolve(l)}catch(e){}}}catch(e){t={error:e}}finally{try{i&&!i.done&&(r=n.return)&&r.call(n)}finally{if(t)throw t.error}}},P.prototype.clearInstance=function(e){void 0===e&&(e=_),this.instancesDeferred.delete(e),this.instances.delete(e)},P.prototype.delete=function(){return function(o,a,s,c){return new(s=s||Promise)(function(e,t){function r(e){try{i(c.next(e))}catch(e){t(e)}}function n(e){try{i(c.throw(e))}catch(e){t(e)}}function i(t){t.done?e(t.value):new s(function(e){e(t.value)}).then(r,n)}i((c=c.apply(o,a||[])).next())})}(this,void 0,void 0,function(){var t;return e(this,function(e){switch(e.label){case 0:return t=Array.from(this.instances.values()),[4,Promise.all(t.filter(function(e){return"INTERNAL"in e}).map(function(e){return e.INTERNAL.delete()}))];case 1:return e.sent(),[2]}})})},P.prototype.isComponentSet=function(){return null!=this.component},P.prototype.getOrInitializeService=function(e){var t=this.instances.get(e);return!t&&this.component&&(t=this.component.instanceFactory(this.container,function(e){return e===_?void 0:e}(e)),this.instances.set(e,t)),t||null},P.prototype.normalizeInstanceIdentifier=function(e){return this.component?this.component.multipleInstances?e:_:e},P);function P(e,t){this.name=e,this.container=t,this.component=null,this.instances=new Map,this.instancesDeferred=new Map}var S,A,C=(R.prototype.addComponent=function(e){var t=this.getProvider(e.name);if(t.isComponentSet())throw new Error("Component "+e.name+" has already been registered with "+this.name);t.setComponent(e)},R.prototype.addOrOverwriteComponent=function(e){this.getProvider(e.name).isComponentSet()&&this.providers.delete(e.name),this.addComponent(e)},R.prototype.getProvider=function(e){if(this.providers.has(e))return this.providers.get(e);var t=new N(e,this);return this.providers.set(e,t),t},R.prototype.getProviders=function(){return Array.from(this.providers.values())},R);function R(e){this.name=e,this.providers=new Map}function j(){for(var e=0,t=0,r=arguments.length;t<r;t++)e+=arguments[t].length;var n=Array(e),i=0;for(t=0;t<r;t++)for(var o=arguments[t],a=0,s=o.length;a<s;a++,i++)n[i]=o[a];return n}(A=S=S||{})[A.DEBUG=0]="DEBUG",A[A.VERBOSE=1]="VERBOSE",A[A.INFO=2]="INFO",A[A.WARN=3]="WARN",A[A.ERROR=4]="ERROR",A[A.SILENT=5]="SILENT";function k(e,t){for(var r=[],n=2;n<arguments.length;n++)r[n-2]=arguments[n];if(!(t<e.logLevel)){var i=(new Date).toISOString();switch(t){case S.DEBUG:case S.VERBOSE:console.log.apply(console,j(["["+i+"] "+e.name+":"],r));break;case S.INFO:console.info.apply(console,j(["["+i+"] "+e.name+":"],r));break;case S.WARN:console.warn.apply(console,j(["["+i+"] "+e.name+":"],r));break;case S.ERROR:console.error.apply(console,j(["["+i+"] "+e.name+":"],r));break;default:throw new Error("Attempted to log a message with an invalid logType (value: "+t+")")}}}var D,F=S.INFO,x=(Object.defineProperty(L.prototype,"logLevel",{get:function(){return this._logLevel},set:function(e){if(!(e in S))throw new TypeError("Invalid value assigned to `logLevel`");this._logLevel=e},enumerable:!0,configurable:!0}),Object.defineProperty(L.prototype,"logHandler",{get:function(){return this._logHandler},set:function(e){if("function"!=typeof e)throw new TypeError("Value assigned to `logHandler` must be a function");this._logHandler=e},enumerable:!0,configurable:!0}),L.prototype.debug=function(){for(var e=[],t=0;t<arguments.length;t++)e[t]=arguments[t];this._logHandler.apply(this,j([this,S.DEBUG],e))},L.prototype.log=function(){for(var e=[],t=0;t<arguments.length;t++)e[t]=arguments[t];this._logHandler.apply(this,j([this,S.VERBOSE],e))},L.prototype.info=function(){for(var e=[],t=0;t<arguments.length;t++)e[t]=arguments[t];this._logHandler.apply(this,j([this,S.INFO],e))},L.prototype.warn=function(){for(var e=[],t=0;t<arguments.length;t++)e[t]=arguments[t];this._logHandler.apply(this,j([this,S.WARN],e))},L.prototype.error=function(){for(var e=[],t=0;t<arguments.length;t++)e[t]=arguments[t];this._logHandler.apply(this,j([this,S.ERROR],e))},L);function L(e){this.name=e,this._logLevel=F,this._logHandler=k}var T,z=((D={})["no-app"]="No Firebase App '{$appName}' has been created - call Firebase App.initializeApp()",D["bad-app-name"]="Illegal App name: '{$appName}",D["duplicate-app"]="Firebase App named '{$appName}' already exists",D["app-deleted"]="Firebase App named '{$appName}' already deleted",D["invalid-app-argument"]="firebase.{$appName}() takes either no argument or a Firebase App instance.",D),V=new u("app","Firebase",z),B="@firebase/app",M="[DEFAULT]",U=((T={})[B]="fire-core",T["@firebase/analytics"]="fire-analytics",T["@firebase/auth"]="fire-auth",T["@firebase/database"]="fire-rtdb",T["@firebase/functions"]="fire-fn",T["@firebase/installations"]="fire-iid",T["@firebase/messaging"]="fire-fcm",T["@firebase/performance"]="fire-perf",T["@firebase/remote-config"]="fire-rc",T["@firebase/storage"]="fire-gcs",T["@firebase/firestore"]="fire-fst",T["fire-js"]="fire-js",T["firebase-wrapper"]="fire-js-all",T),H=new x("@firebase/app"),W=(Object.defineProperty(G.prototype,"automaticDataCollectionEnabled",{get:function(){return this.checkDestroyed_(),this.automaticDataCollectionEnabled_},set:function(e){this.checkDestroyed_(),this.automaticDataCollectionEnabled_=e},enumerable:!0,configurable:!0}),Object.defineProperty(G.prototype,"name",{get:function(){return this.checkDestroyed_(),this.name_},enumerable:!0,configurable:!0}),Object.defineProperty(G.prototype,"options",{get:function(){return this.checkDestroyed_(),this.options_},enumerable:!0,configurable:!0}),G.prototype.delete=function(){var t=this;return new Promise(function(e){t.checkDestroyed_(),e()}).then(function(){return t.firebase_.INTERNAL.removeApp(t.name_),Promise.all(t.container.getProviders().map(function(e){return e.delete()}))}).then(function(){t.isDeleted_=!0})},G.prototype._getService=function(e,t){return void 0===t&&(t=M),this.checkDestroyed_(),this.container.getProvider(e).getImmediate({identifier:t})},G.prototype._removeServiceInstance=function(e,t){void 0===t&&(t=M),this.container.getProvider(e).clearInstance(t)},G.prototype._addComponent=function(t){try{this.container.addComponent(t)}catch(e){H.debug("Component "+t.name+" failed to register with FirebaseApp "+this.name,e)}},G.prototype._addOrOverwriteComponent=function(e){this.container.addOrOverwriteComponent(e)},G.prototype.checkDestroyed_=function(){if(this.isDeleted_)throw V.create("app-deleted",{appName:this.name_})},G);function G(e,t,r){var n,i,o=this;this.firebase_=r,this.isDeleted_=!1,this.name_=t.name,this.automaticDataCollectionEnabled_=t.automaticDataCollectionEnabled||!1,this.options_=function(e){return v(void 0,e)}(e),this.container=new C(t.name),this._addComponent(new E("app",function(){return o},"PUBLIC"));try{for(var a=d(this.firebase_.INTERNAL.components.values()),s=a.next();!s.done;s=a.next()){var c=s.value;this._addComponent(c)}}catch(e){n={error:e}}finally{try{s&&!s.done&&(i=a.return)&&i.call(a)}finally{if(n)throw n.error}}}W.prototype.name&&W.prototype.options||W.prototype.delete||console.log("dc");var $="7.9.1";function Y(c){var l={},p=new Map,u={__esModule:!0,initializeApp:function(e,t){void 0===t&&(t={});if("object"!=typeof t||null===t){t={name:t}}var r=t;void 0===r.name&&(r.name=M);var n=r.name;if("string"!=typeof n||!n)throw V.create("bad-app-name",{appName:String(n)});if(y(l,n))throw V.create("duplicate-app",{appName:n});var i=new c(e,r,u);return l[n]=i},app:f,registerVersion:function(e,t,r){var n,i=null!==(n=U[e])&&void 0!==n?n:e;r&&(i+="-"+r);var o=i.match(/\s|\//),a=t.match(/\s|\//);if(o||a){var s=['Unable to register library "'+i+'" with version "'+t+'":'];return o&&s.push('library name "'+i+'" contains illegal characters (whitespace or "/")'),o&&a&&s.push("and"),a&&s.push('version name "'+t+'" contains illegal characters (whitespace or "/")'),void H.warn(s.join(" "))}h(new E(i+"-version",function(){return{library:i,version:t}},"VERSION"))},apps:null,SDK_VERSION:$,INTERNAL:{registerComponent:h,removeApp:function(e){delete l[e]},components:p,useAsService:function(e,t){return"serverAuth"!==t?t:null}}};function f(e){if(!y(l,e=e||M))throw V.create("no-app",{appName:e});return l[e]}function h(r){var t,e,n=r.name;if(p.has(n))return H.debug("There were multiple attempts to register component "+n+"."),"PUBLIC"===r.type?u[n]:null;if(p.set(n,r),"PUBLIC"===r.type){var i=function(e){if(void 0===e&&(e=f()),"function"!=typeof e[n])throw V.create("invalid-app-argument",{appName:n});return e[n]()};void 0!==r.serviceProps&&v(i,r.serviceProps),u[n]=i,c.prototype[n]=function(){for(var e=[],t=0;t<arguments.length;t++)e[t]=arguments[t];return this._getService.bind(this,n).apply(this,r.multipleInstances?e:[])}}try{for(var o=d(Object.keys(l)),a=o.next();!a.done;a=o.next()){var s=a.value;l[s]._addComponent(r)}}catch(e){t={error:e}}finally{try{a&&!a.done&&(e=o.return)&&e.call(o)}finally{if(t)throw t.error}}return"PUBLIC"===r.type?u[n]:null}return u.default=u,Object.defineProperty(u,"apps",{get:function(){return Object.keys(l).map(function(e){return l[e]})}}),f.App=c,u}var K=function e(){var t=Y(W);return t.INTERNAL=a(a({},t.INTERNAL),{createFirebaseNamespace:e,extendNamespace:function(e){v(t,e)},createSubscribe:b,ErrorFactory:u,deepExtend:v}),t}(),Z=(q.prototype.getPlatformInfoString=function(){return this.container.getProviders().map(function(e){if(function(e){var t,r=e.getComponent();return"VERSION"===(null===(t=r)||void 0===t?void 0:t.type)}(e)){var t=e.getImmediate();return t.library+"/"+t.version}return null}).filter(function(e){return e}).join(" ")},q);function q(e){this.container=e}if("object"==typeof self&&self.self===self&&void 0!==self.firebase){H.warn("\n Warning: Firebase is already defined in the global scope. Please make sure\n Firebase library is only loaded once.\n ");var J=self.firebase.SDK_VERSION;J&&0<=J.indexOf("LITE")&&H.warn("\n Warning: You are trying to load Firebase while using Firebase Performance standalone script.\n You should load Firebase Performance with this instance of Firebase to avoid loading duplicate code.\n ")}var Q=K.initializeApp;K.initializeApp=function(){for(var e=[],t=0;t<arguments.length;t++)e[t]=arguments[t];return function(){try{return"[object process]"===Object.prototype.toString.call(global.process)}catch(e){return!1}}()&&H.warn('\n Warning: This is a browser-targeted Firebase bundle but it appears it is being\n run in a Node environment. If running in a Node environment, make sure you\n are using the bundle specified by the "main" field in package.json.\n \n If you are using Webpack, you can specify "main" as the first item in\n "resolve.mainFields":\n https://webpack.js.org/configuration/resolve/#resolvemainfields\n \n If using Rollup, use the rollup-plugin-node-resolve plugin and specify "main"\n as the first item in "mainFields", e.g. [\'main\', \'module\'].\n https://github.com/rollup/rollup-plugin-node-resolve\n '),Q.apply(void 0,e)};var X,ee,te=K;(X=te).INTERNAL.registerComponent(new E("platform-logger",function(e){return new Z(e)},"PRIVATE")),X.registerVersion(B,"0.5.4",ee),X.registerVersion("fire-js","");return te.registerVersion("firebase","7.9.1","app"),te});
5
+ !function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(require("@firebase/app")):"function"==typeof define&&define.amd?define(["@firebase/app"],t):t((e=e||self).firebase)}(this,function(ft){"use strict";try{(function(){ft=ft&&ft.hasOwnProperty("default")?ft.default:ft;var n=function(e,t){return(n=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(e,t){e.__proto__=t}||function(e,t){for(var n in t)t.hasOwnProperty(n)&&(e[n]=t[n])})(e,t)};var u=function(){return(u=Object.assign||function(e){for(var t,n=1,r=arguments.length;n<r;n++)for(var i in t=arguments[n])Object.prototype.hasOwnProperty.call(t,i)&&(e[i]=t[i]);return e}).apply(this,arguments)};function l(o,s,a,u){return new(a=a||Promise)(function(e,t){function n(e){try{i(u.next(e))}catch(e){t(e)}}function r(e){try{i(u.throw(e))}catch(e){t(e)}}function i(t){t.done?e(t.value):new a(function(e){e(t.value)}).then(n,r)}i((u=u.apply(o,s||[])).next())})}function p(n,r){var i,o,s,e,a={label:0,sent:function(){if(1&s[0])throw s[1];return s[1]},trys:[],ops:[]};return e={next:t(0),throw:t(1),return:t(2)},"function"==typeof Symbol&&(e[Symbol.iterator]=function(){return this}),e;function t(t){return function(e){return function(t){if(i)throw new TypeError("Generator is already executing.");for(;a;)try{if(i=1,o&&(s=2&t[0]?o.return:t[0]?o.throw||((s=o.return)&&s.call(o),0):o.next)&&!(s=s.call(o,t[1])).done)return s;switch(o=0,s&&(t=[2&t[0],s.value]),t[0]){case 0:case 1:s=t;break;case 4:return a.label++,{value:t[1],done:!1};case 5:a.label++,o=t[1],t=[0];continue;case 7:t=a.ops.pop(),a.trys.pop();continue;default:if(!(s=0<(s=a.trys).length&&s[s.length-1])&&(6===t[0]||2===t[0])){a=0;continue}if(3===t[0]&&(!s||t[1]>s[0]&&t[1]<s[3])){a.label=t[1];break}if(6===t[0]&&a.label<s[1]){a.label=s[1],s=t;break}if(s&&a.label<s[2]){a.label=s[2],a.ops.push(t);break}s[2]&&a.ops.pop(),a.trys.pop();continue}t=r.call(n,a)}catch(e){t=[6,e],o=0}finally{i=s=0}if(5&t[0])throw t[1];return{value:t[0]?t[1]:void 0,done:!0}}([t,e])}}}function c(e){var t="function"==typeof Symbol&&e[Symbol.iterator],n=0;return t?t.call(e):{next:function(){return e&&n>=e.length&&(e=void 0),{value:e&&e[n++],done:!e}}}}function r(e,t){var n="function"==typeof Symbol&&e[Symbol.iterator];if(!n)return e;var r,i,o=n.call(e),s=[];try{for(;(void 0===t||0<t--)&&!(r=o.next()).done;)s.push(r.value)}catch(e){i={error:e}}finally{try{r&&!r.done&&(n=o.return)&&n.call(o)}finally{if(i)throw i.error}}return s}function i(){for(var e=[],t=0;t<arguments.length;t++)e=e.concat(r(arguments[t]));return e}var e,t,o,d=(o=Error,n(e=a,t=o),void(e.prototype=null===t?Object.create(t):(s.prototype=t.prototype,new s)),a);function s(){this.constructor=e}function a(e,t){var n=o.call(this,t)||this;return n.code=e,n.name="FirebaseError",Object.setPrototypeOf(n,a.prototype),Error.captureStackTrace&&Error.captureStackTrace(n,f.prototype.create),n}var f=(h.prototype.create=function(e){for(var t=[],n=1;n<arguments.length;n++)t[n-1]=arguments[n];for(var r=t[0]||{},i=this.service+"/"+e,o=this.errors[e],s=o?function(e,r){return e.replace(v,function(e,t){var n=r[t];return null!=n?n.toString():"<"+t+"?>"})}(o,r):"Error",a=this.serviceName+": "+s+" ("+i+").",u=new d(i,a),c=0,f=Object.keys(r);c<f.length;c++){var l=f[c];"_"!==l.slice(-1)&&(l in u&&console.warn('Overwriting FirebaseError base field "'+l+'" can cause unexpected behavior.'),u[l]=r[l])}return u},h);function h(e,t,n){this.service=e,this.serviceName=t,this.errors=n}var v=/\{\$([^}]+)}/g,g=(b.prototype.setInstantiationMode=function(e){return this.instantiationMode=e,this},b.prototype.setMultipleInstances=function(e){return this.multipleInstances=e,this},b.prototype.setServiceProps=function(e){return this.serviceProps=e,this},b);function b(e,t,n){this.name=e,this.instanceFactory=t,this.type=n,this.multipleInstances=!1,this.serviceProps={},this.instantiationMode="LAZY"}function y(n){return new Promise(function(e,t){n.onsuccess=function(){e(n.result)},n.onerror=function(){t(n.error)}})}function w(n,r,i){var o,e=new Promise(function(e,t){y(o=n[r].apply(n,i)).then(e,t)});return e.request=o,e}function m(e,n,t){t.forEach(function(t){Object.defineProperty(e.prototype,t,{get:function(){return this[n][t]},set:function(e){this[n][t]=e}})})}function k(t,n,r,e){e.forEach(function(e){e in r.prototype&&(t.prototype[e]=function(){return w(this[n],e,arguments)})})}function S(t,n,r,e){e.forEach(function(e){e in r.prototype&&(t.prototype[e]=function(){return this[n][e].apply(this[n],arguments)})})}function I(t,n,r,e){e.forEach(function(e){e in r.prototype&&(t.prototype[e]=function(){return function(e,t,n){var r=w(e,t,n);return r.then(function(e){if(e)return new C(e,r.request)})}(this[n],e,arguments)})})}function T(e){this._index=e}function C(e,t){this._cursor=e,this._request=t}function _(e){this._store=e}function P(n){this._tx=n,this.complete=new Promise(function(e,t){n.oncomplete=function(){e()},n.onerror=function(){t(n.error)},n.onabort=function(){t(n.error)}})}function j(e,t,n){this._db=e,this.oldVersion=t,this.transaction=new P(n)}function D(e){this._db=e}function E(e,t,n){var r=w(indexedDB,"open",[e,t]),i=r.request;return i&&(i.onupgradeneeded=function(e){n&&n(new j(i.result,e.oldVersion,i.transaction))}),r.then(function(e){return new D(e)})}function x(e){return w(indexedDB,"deleteDatabase",[e])}m(T,"_index",["name","keyPath","multiEntry","unique"]),k(T,"_index",IDBIndex,["get","getKey","getAll","getAllKeys","count"]),I(T,"_index",IDBIndex,["openCursor","openKeyCursor"]),m(C,"_cursor",["direction","key","primaryKey","value"]),k(C,"_cursor",IDBCursor,["update","delete"]),["advance","continue","continuePrimaryKey"].forEach(function(n){n in IDBCursor.prototype&&(C.prototype[n]=function(){var t=this,e=arguments;return Promise.resolve().then(function(){return t._cursor[n].apply(t._cursor,e),y(t._request).then(function(e){if(e)return new C(e,t._request)})})})}),_.prototype.createIndex=function(){return new T(this._store.createIndex.apply(this._store,arguments))},_.prototype.index=function(){return new T(this._store.index.apply(this._store,arguments))},m(_,"_store",["name","keyPath","indexNames","autoIncrement"]),k(_,"_store",IDBObjectStore,["put","add","delete","clear","get","getAll","getKey","getAllKeys","count"]),I(_,"_store",IDBObjectStore,["openCursor","openKeyCursor"]),S(_,"_store",IDBObjectStore,["deleteIndex"]),P.prototype.objectStore=function(){return new _(this._tx.objectStore.apply(this._tx,arguments))},m(P,"_tx",["objectStoreNames","mode"]),S(P,"_tx",IDBTransaction,["abort"]),j.prototype.createObjectStore=function(){return new _(this._db.createObjectStore.apply(this._db,arguments))},m(j,"_db",["name","version","objectStoreNames"]),S(j,"_db",IDBDatabase,["deleteObjectStore","close"]),D.prototype.transaction=function(){return new P(this._db.transaction.apply(this._db,arguments))},m(D,"_db",["name","version","objectStoreNames"]),S(D,"_db",IDBDatabase,["close"]),["openCursor","openKeyCursor"].forEach(function(i){[_,T].forEach(function(e){i in e.prototype&&(e.prototype[i.replace("open","iterate")]=function(){var e=function(e){return Array.prototype.slice.call(e)}(arguments),t=e[e.length-1],n=this._store||this._index,r=n[i].apply(n,e.slice(0,-1));r.onsuccess=function(){t(r.result)}})})}),[T,_].forEach(function(e){e.prototype.getAll||(e.prototype.getAll=function(e,n){var r=this,i=[];return new Promise(function(t){r.iterateCursor(e,function(e){e?(i.push(e.value),void 0===n||i.length!=n?e.continue():t(i)):t(i)})})})});var K,O="0.4.2",N=1e4,M="w:"+O,A="FIS_v2",q="https://firebaseinstallations.googleapis.com/v1",B=36e5,R=((K={})["missing-app-config-values"]='Missing App configuration value: "{$valueName}"',K["not-registered"]="Firebase Installation is not registered.",K["installation-not-found"]="Firebase Installation not found.",K["request-failed"]='{$requestName} request failed with error "{$serverCode} {$serverStatus}: {$serverMessage}"',K["app-offline"]="Could not process request. Application offline.",K["delete-pending-registration"]="Can't delete installation while there is a pending registration request.",K),L=new f("installations","Installations",R);function V(e){return e instanceof d&&e.code.includes("request-failed")}function W(e){var t=e.projectId;return q+"/projects/"+t+"/installations"}function F(e){return{token:e.token,requestStatus:2,expiresIn:function(e){return Number(e.replace("s","000"))}(e.expiresIn),creationTime:Date.now()}}function H(r,i){return l(this,void 0,void 0,function(){var t,n;return p(this,function(e){switch(e.label){case 0:return[4,i.json()];case 1:return t=e.sent(),n=t.error,[2,L.create("request-failed",{requestName:r,serverCode:n.code,serverMessage:n.message,serverStatus:n.status})]}})})}function U(e){var t=e.apiKey;return new Headers({"Content-Type":"application/json",Accept:"application/json","x-goog-api-key":t})}function $(e,t){var n=t.refreshToken,r=U(e);return r.append("Authorization",function(e){return A+" "+e}(n)),r}function G(n){return l(this,void 0,void 0,function(){var t;return p(this,function(e){switch(e.label){case 0:return[4,n()];case 1:return 500<=(t=e.sent()).status&&t.status<600?[2,n()]:[2,t]}})})}function z(t){return new Promise(function(e){setTimeout(e,t)})}var J=/^[cdef][\w-]{21}$/,Y="";function Z(){try{var e=new Uint8Array(17);(self.crypto||self.msCrypto).getRandomValues(e),e[0]=112+e[0]%16;var t=function(e){return function(e){return btoa(String.fromCharCode.apply(String,i(e))).replace(/\+/g,"-").replace(/\//g,"_")}(e).substr(0,22)}(e);return J.test(t)?t:Y}catch(e){return Y}}function Q(e){return e.appName+"!"+e.appId}var X=new Map;function ee(e,t){var n=Q(e);te(n,t),function(e,t){var n=re();n&&n.postMessage({key:e,fid:t});ie()}(n,t)}function te(e,t){var n,r,i=X.get(e);if(i)try{for(var o=c(i),s=o.next();!s.done;s=o.next()){(0,s.value)(t)}}catch(e){n={error:e}}finally{try{s&&!s.done&&(r=o.return)&&r.call(o)}finally{if(n)throw n.error}}}var ne=null;function re(){return!ne&&"BroadcastChannel"in self&&((ne=new BroadcastChannel("[Firebase] FID Change")).onmessage=function(e){te(e.data.key,e.data.fid)}),ne}function ie(){0===X.size&&ne&&(ne.close(),ne=null)}var oe,se,ae="firebase-installations-database",ue=1,ce="firebase-installations-store",fe=null;function le(){return fe=fe||E(ae,ue,function(e){switch(e.oldVersion){case 0:e.createObjectStore(ce)}})}function pe(s,a){return l(this,void 0,void 0,function(){var t,n,r,i,o;return p(this,function(e){switch(e.label){case 0:return t=Q(s),[4,le()];case 1:return n=e.sent(),r=n.transaction(ce,"readwrite"),[4,(i=r.objectStore(ce)).get(t)];case 2:return o=e.sent(),[4,i.put(a,t)];case 3:return e.sent(),[4,r.complete];case 4:return e.sent(),o&&o.fid===a.fid||ee(s,a.fid),[2,a]}})})}function de(i){return l(this,void 0,void 0,function(){var t,n,r;return p(this,function(e){switch(e.label){case 0:return t=Q(i),[4,le()];case 1:return n=e.sent(),[4,(r=n.transaction(ce,"readwrite")).objectStore(ce).delete(t)];case 2:return e.sent(),[4,r.complete];case 3:return e.sent(),[2]}})})}function he(a,u){return l(this,void 0,void 0,function(){var t,n,r,i,o,s;return p(this,function(e){switch(e.label){case 0:return t=Q(a),[4,le()];case 1:return n=e.sent(),r=n.transaction(ce,"readwrite"),[4,(i=r.objectStore(ce)).get(t)];case 2:return o=e.sent(),void 0!==(s=u(o))?[3,4]:[4,i.delete(t)];case 3:return e.sent(),[3,6];case 4:return[4,i.put(s,t)];case 5:e.sent(),e.label=6;case 6:return[4,r.complete];case 7:return e.sent(),!s||o&&o.fid===s.fid||ee(a,s.fid),[2,s]}})})}function ve(i){return l(this,void 0,void 0,function(){var r,t,n;return p(this,function(e){switch(e.label){case 0:return[4,he(i,function(e){var t=function(e){return be(e||{fid:Z(),registrationStatus:0})}(e),n=function(e,t){{if(0!==t.registrationStatus)return 1===t.registrationStatus?{installationEntry:t,registrationPromise:function(o){return l(this,void 0,void 0,function(){var t,n,r,i;return p(this,function(e){switch(e.label){case 0:return[4,ge(o)];case 1:t=e.sent(),e.label=2;case 2:return 1!==t.registrationStatus?[3,5]:[4,z(100)];case 3:return e.sent(),[4,ge(o)];case 4:return t=e.sent(),[3,2];case 5:return 0!==t.registrationStatus?[3,7]:[4,ve(o)];case 6:return n=e.sent(),r=n.installationEntry,(i=n.registrationPromise)?[2,i]:[2,r];case 7:return[2,t]}})})}(e)}:{installationEntry:t};if(!navigator.onLine){var n=Promise.reject(L.create("app-offline"));return{installationEntry:t,registrationPromise:n}}var r={fid:t.fid,registrationStatus:1,registrationTime:Date.now()},i=function(r,i){return l(this,void 0,void 0,function(){var t,n;return p(this,function(e){switch(e.label){case 0:return e.trys.push([0,2,,7]),[4,function(a,e){var u=e.fid;return l(this,void 0,void 0,function(){var t,n,r,i,o,s;return p(this,function(e){switch(e.label){case 0:return t=W(a),n=U(a),r={fid:u,authVersion:A,appId:a.appId,sdkVersion:M},i={method:"POST",headers:n,body:JSON.stringify(r)},[4,G(function(){return fetch(t,i)})];case 1:return(o=e.sent()).ok?[4,o.json()]:[3,3];case 2:return s=e.sent(),[2,{fid:s.fid||u,registrationStatus:2,refreshToken:s.refreshToken,authToken:F(s.authToken)}];case 3:return[4,H("Create Installation",o)];case 4:throw e.sent()}})})}(r,i)];case 1:return t=e.sent(),[2,pe(r,t)];case 2:return V(n=e.sent())&&409===n.serverCode?[4,de(r)]:[3,4];case 3:return e.sent(),[3,6];case 4:return[4,pe(r,{fid:i.fid,registrationStatus:0})];case 5:e.sent(),e.label=6;case 6:throw n;case 7:return[2]}})})}(e,r);return{installationEntry:r,registrationPromise:i}}}(i,t);return r=n.registrationPromise,n.installationEntry})];case 1:return(t=e.sent()).fid!==Y?[3,3]:(n={},[4,r]);case 2:return[2,(n.installationEntry=e.sent(),n)];case 3:return[2,{installationEntry:t,registrationPromise:r}]}})})}function ge(e){return he(e,function(e){if(!e)throw L.create("installation-not-found");return be(e)})}function be(e){return function(e){return 1===e.registrationStatus&&e.registrationTime+N<Date.now()}(e)?{fid:e.fid,registrationStatus:0}:e}function ye(e,u){var c=e.appConfig,f=e.platformLoggerProvider;return l(this,void 0,void 0,function(){var t,n,r,i,o,s,a;return p(this,function(e){switch(e.label){case 0:return t=function(e,t){var n=t.fid;return W(e)+"/"+n+"/authTokens:generate"}(c,u),n=$(c,u),(r=f.getImmediate({optional:!0}))&&n.append("x-firebase-client",r.getPlatformInfoString()),i={installation:{sdkVersion:M}},o={method:"POST",headers:n,body:JSON.stringify(i)},[4,G(function(){return fetch(t,o)})];case 1:return(s=e.sent()).ok?[4,s.json()]:[3,3];case 2:return a=e.sent(),[2,F(a)];case 3:return[4,H("Generate Auth Token",s)];case 4:throw e.sent()}})})}function we(i,o){return void 0===o&&(o=!1),l(this,void 0,void 0,function(){var r,t,n;return p(this,function(e){switch(e.label){case 0:return[4,he(i.appConfig,function(e){if(!ke(e))throw L.create("not-registered");var t=e.authToken;if(!o&&function(e){return 2===e.requestStatus&&!function(e){var t=Date.now();return t<e.creationTime||e.creationTime+e.expiresIn<t+B}(e)}(t))return e;if(1===t.requestStatus)return r=function(r,i){return l(this,void 0,void 0,function(){var t,n;return p(this,function(e){switch(e.label){case 0:return[4,me(r.appConfig)];case 1:t=e.sent(),e.label=2;case 2:return 1!==t.authToken.requestStatus?[3,5]:[4,z(100)];case 3:return e.sent(),[4,me(r.appConfig)];case 4:return t=e.sent(),[3,2];case 5:return 0===(n=t.authToken).requestStatus?[2,we(r,i)]:[2,n]}})})}(i,o),e;if(!navigator.onLine)throw L.create("app-offline");var n=function(e){var t={requestStatus:1,requestTime:Date.now()};return u(u({},e),{authToken:t})}(e);return r=function(i,o){return l(this,void 0,void 0,function(){var t,n,r;return p(this,function(e){switch(e.label){case 0:return e.trys.push([0,3,,8]),[4,ye(i,o)];case 1:return t=e.sent(),r=u(u({},o),{authToken:t}),[4,pe(i.appConfig,r)];case 2:return e.sent(),[2,t];case 3:return!V(n=e.sent())||401!==n.serverCode&&404!==n.serverCode?[3,5]:[4,de(i.appConfig)];case 4:return e.sent(),[3,7];case 5:return r=u(u({},o),{authToken:{requestStatus:0}}),[4,pe(i.appConfig,r)];case 6:e.sent(),e.label=7;case 7:throw n;case 8:return[2]}})})}(i,n),n})];case 1:return t=e.sent(),r?[4,r]:[3,3];case 2:return n=e.sent(),[3,4];case 3:n=t.authToken,e.label=4;case 4:return[2,n]}})})}function me(e){return he(e,function(e){if(!ke(e))throw L.create("not-registered");return function(e){return 1===e.requestStatus&&e.requestTime+N<Date.now()}(e.authToken)?u(u({},e),{authToken:{requestStatus:0}}):e})}function ke(e){return void 0!==e&&2===e.registrationStatus}function Se(t,n){return void 0===n&&(n=!1),l(this,void 0,void 0,function(){return p(this,function(e){switch(e.label){case 0:return[4,function(n){return l(this,void 0,void 0,function(){var t;return p(this,function(e){switch(e.label){case 0:return[4,ve(n)];case 1:return(t=e.sent().registrationPromise)?[4,t]:[3,3];case 2:e.sent(),e.label=3;case 3:return[2]}})})}(t.appConfig)];case 1:return e.sent(),[4,we(t,n)];case 2:return[2,e.sent().token]}})})}function Ie(o,s){return l(this,void 0,void 0,function(){var t,n,r,i;return p(this,function(e){switch(e.label){case 0:return t=function(e,t){var n=t.fid;return W(e)+"/"+n}(o,s),n=$(o,s),r={method:"DELETE",headers:n},[4,G(function(){return fetch(t,r)})];case 1:return(i=e.sent()).ok?[3,3]:[4,H("Delete Installation",i)];case 2:throw e.sent();case 3:return[2]}})})}function Te(e,t){var n=e.appConfig;return function(e,t){re();var n=Q(e),r=X.get(n);r||(r=new Set,X.set(n,r)),r.add(t)}(n,t),function(){!function(e,t){var n=Q(e),r=X.get(n);r&&(r.delete(t),0===r.size&&X.delete(n),ie())}(n,t)}}function Ce(e){return L.create("missing-app-config-values",{valueName:e})}(oe=ft).INTERNAL.registerComponent(new g("installations",function(e){var t=e.getProvider("app").getImmediate(),n={appConfig:function(e){var t,n;if(!e||!e.options)throw Ce("App Configuration");if(!e.name)throw Ce("App Name");try{for(var r=c(["projectId","apiKey","appId"]),i=r.next();!i.done;i=r.next()){var o=i.value;if(!e.options[o])throw Ce(o)}}catch(e){t={error:e}}finally{try{i&&!i.done&&(n=r.return)&&n.call(r)}finally{if(t)throw t.error}}return{appName:e.name,projectId:e.options.projectId,apiKey:e.options.apiKey,appId:e.options.appId}}(t),platformLoggerProvider:e.getProvider("platform-logger")};return{app:t,getId:function(){return function(i){return l(this,void 0,void 0,function(){var t,n,r;return p(this,function(e){switch(e.label){case 0:return[4,ve(i.appConfig)];case 1:return t=e.sent(),n=t.installationEntry,(r=t.registrationPromise)?r.catch(console.error):we(i).catch(console.error),[2,n.fid]}})})}(n)},getToken:function(e){return Se(n,e)},delete:function(){return function(r){return l(this,void 0,void 0,function(){var t,n;return p(this,function(e){switch(e.label){case 0:return[4,he(t=r.appConfig,function(e){if(!e||0!==e.registrationStatus)return e})];case 1:if(!(n=e.sent()))return[3,6];if(1!==n.registrationStatus)return[3,2];throw L.create("delete-pending-registration");case 2:if(2!==n.registrationStatus)return[3,6];if(navigator.onLine)return[3,3];throw L.create("app-offline");case 3:return[4,Ie(t,n)];case 4:return e.sent(),[4,de(t)];case 5:e.sent(),e.label=6;case 6:return[2]}})})}(n)},onIdChange:function(e){return Te(n,e)}}},"PUBLIC")),oe.registerVersion("@firebase/installations",O);var _e=((se={})["missing-app-config-values"]='Missing App configuration value: "{$valueName}"',se["only-available-in-window"]="This method is available in a Window context.",se["only-available-in-sw"]="This method is available in a service worker context.",se["permission-default"]="The notification permission was not granted and dismissed instead.",se["permission-blocked"]="The notification permission was not granted and blocked instead.",se["unsupported-browser"]="This browser doesn't support the API's required to use the firebase SDK.",se["failed-service-worker-registration"]="We are unable to register the default service worker. {$browserErrorMessage}",se["token-subscribe-failed"]="A problem occured while subscribing the user to FCM: {$errorInfo}",se["token-subscribe-no-token"]="FCM returned no token when subscribing the user to push.",se["token-unsubscribe-failed"]="A problem occured while unsubscribing the user from FCM: {$errorInfo}",se["token-update-failed"]="A problem occured while updating the user from FCM: {$errorInfo}",se["token-update-no-token"]="FCM returned no token when updating the user to push.",se["use-sw-after-get-token"]="The useServiceWorker() method may only be called once and must be called before calling getToken() to ensure your service worker is used.",se["invalid-sw-registration"]="The input to useServiceWorker() must be a ServiceWorkerRegistration.",se["invalid-bg-handler"]="The input to setBackgroundMessageHandler() must be a function.",se["invalid-vapid-key"]="The public VAPID key must be a string.",se["use-vapid-key-after-get-token"]="The usePublicVapidKey() method may only be called once and must be called before calling getToken() to ensure your VAPID key is used.",se),Pe=new f("messaging","Messaging",_e);function je(e){return Pe.create("missing-app-config-values",{valueName:e})}function De(e){var t=new Uint8Array(e);return btoa(String.fromCharCode.apply(String,i(t))).replace(/=/g,"").replace(/\+/g,"-").replace(/\//g,"_")}var Ee="fcm_token_details_db",xe=5,Ke="fcm_token_object_Store";function Oe(a){return l(this,void 0,void 0,function(){var t,s,n=this;return p(this,function(e){switch(e.label){case 0:return"databases"in indexedDB?[4,indexedDB.databases()]:[3,2];case 1:if(t=e.sent(),!t.map(function(e){return e.name}).includes(Ee))return[2,null];e.label=2;case 2:return s=null,[4,E(Ee,xe,function(o){return l(n,void 0,void 0,function(){var t,n,r,i;return p(this,function(e){switch(e.label){case 0:return o.oldVersion<2?[2]:o.objectStoreNames.contains(Ke)?[4,(t=o.transaction.objectStore(Ke)).index("fcmSenderId").get(a)]:[2];case 1:return n=e.sent(),[4,t.clear()];case 2:if(e.sent(),!n)return[2];if(2===o.oldVersion){if(!(r=n).auth||!r.p256dh||!r.endpoint)return[2];s={token:r.fcmToken,createTime:(i=r.createTime,null!=i?i:Date.now()),subscriptionOptions:{auth:r.auth,p256dh:r.p256dh,endpoint:r.endpoint,swScope:r.swScope,vapidKey:"string"==typeof r.vapidKey?r.vapidKey:De(r.vapidKey)}}}else 3===o.oldVersion?s={token:(r=n).fcmToken,createTime:r.createTime,subscriptionOptions:{auth:De(r.auth),p256dh:De(r.p256dh),endpoint:r.endpoint,swScope:r.swScope,vapidKey:De(r.vapidKey)}}:4===o.oldVersion&&(s={token:(r=n).fcmToken,createTime:r.createTime,subscriptionOptions:{auth:De(r.auth),p256dh:De(r.p256dh),endpoint:r.endpoint,swScope:r.swScope,vapidKey:De(r.vapidKey)}});return[2]}})})})];case 3:return e.sent().close(),[4,x(Ee)];case 4:return e.sent(),[4,x("fcm_vapid_details_db")];case 5:return e.sent(),[4,x("undefined")];case 6:return e.sent(),[2,function(e){if(!e||!e.subscriptionOptions)return!1;var t=e.subscriptionOptions;return"number"==typeof e.createTime&&0<e.createTime&&"string"==typeof e.token&&0<e.token.length&&"string"==typeof t.auth&&0<t.auth.length&&"string"==typeof t.p256dh&&0<t.p256dh.length&&"string"==typeof t.endpoint&&0<t.endpoint.length&&"string"==typeof t.swScope&&0<t.swScope.length&&"string"==typeof t.vapidKey&&0<t.vapidKey.length}(s)?s:null]}})})}var Ne="firebase-messaging-database",Me=1,Ae="firebase-messaging-store",qe=null;function Be(){return qe=qe||E(Ne,Me,function(e){switch(e.oldVersion){case 0:e.createObjectStore(Ae)}})}function Re(i){return l(this,void 0,void 0,function(){var t,n,r;return p(this,function(e){switch(e.label){case 0:return t=Ve(i),[4,Be()];case 1:return[4,e.sent().transaction(Ae).objectStore(Ae).get(t)];case 2:return(n=e.sent())?[2,n]:[3,3];case 3:return[4,Oe(i.appConfig.senderId)];case 4:return(r=e.sent())?[4,Le(i,r)]:[3,6];case 5:return e.sent(),[2,r];case 6:return[2]}})})}function Le(i,o){return l(this,void 0,void 0,function(){var t,n,r;return p(this,function(e){switch(e.label){case 0:return t=Ve(i),[4,Be()];case 1:return n=e.sent(),[4,(r=n.transaction(Ae,"readwrite")).objectStore(Ae).put(o,t)];case 2:return e.sent(),[4,r.complete];case 3:return e.sent(),[2,o]}})})}function Ve(e){return e.appConfig.appId}var We="BDOU99-h67HcA6JeFXHbSNMu7e2yNNu3RzoMj8TM4W88jITfq7ZmPvIM1Iv-4_l2LxQcYwhqby2xGpWwzjfAnG4",Fe="https://fcmregistrations.googleapis.com/v1",He="FCM_MSG",Ue="google.c.a.c_id";function $e(s,a){return l(this,void 0,void 0,function(){var t,n,r,i,o;return p(this,function(e){switch(e.label){case 0:return[4,ze(s)];case 1:t=e.sent(),n={method:"DELETE",headers:t},e.label=2;case 2:return e.trys.push([2,5,,6]),[4,fetch(Ge(s.appConfig)+"/"+a,n)];case 3:return[4,e.sent().json()];case 4:if((r=e.sent()).error)throw i=r.error.message,Pe.create("token-unsubscribe-failed",{errorInfo:i});return[3,6];case 5:throw o=e.sent(),Pe.create("token-unsubscribe-failed",{errorInfo:o});case 6:return[2]}})})}function Ge(e){var t=e.projectId;return Fe+"/projects/"+t+"/registrations"}function ze(e){var n=e.appConfig,r=e.installations;return l(this,void 0,void 0,function(){var t;return p(this,function(e){switch(e.label){case 0:return[4,r.getToken()];case 1:return t=e.sent(),[2,new Headers({"Content-Type":"application/json",Accept:"application/json","x-goog-api-key":n.apiKey,"x-goog-firebase-installations-auth":"FIS "+t})]}})})}function Je(e){var t=e.p256dh,n=e.auth,r=e.endpoint,i=e.vapidKey,o={web:{endpoint:r,auth:n,p256dh:t}};return i!==We&&(o.web.applicationPubKey=i),o}var Ye,Ze;function Qe(o,s,a){return l(this,void 0,void 0,function(){var t,n,r,i;return p(this,function(e){switch(e.label){case 0:if("granted"!==Notification.permission)throw Pe.create("permission-blocked");return[4,function(n,r){return l(this,void 0,void 0,function(){var t;return p(this,function(e){switch(e.label){case 0:return[4,n.pushManager.getSubscription()];case 1:return(t=e.sent())?[2,t]:[2,n.pushManager.subscribe({userVisibleOnly:!0,applicationServerKey:r})]}})})}(s,a)];case 1:return t=e.sent(),[4,Re(o)];case 2:return n=e.sent(),r={vapidKey:a,swScope:s.scope,endpoint:t.endpoint,auth:De(t.getKey("auth")),p256dh:De(t.getKey("p256dh"))},n?[3,3]:[2,et(o,r)];case 3:if(function(e,t){var n=t.vapidKey===e.vapidKey,r=t.endpoint===e.endpoint,i=t.auth===e.auth,o=t.p256dh===e.p256dh;return n&&r&&i&&o}(n.subscriptionOptions,r))return[3,8];e.label=4;case 4:return e.trys.push([4,6,,7]),[4,$e(o,n.token)];case 5:return e.sent(),[3,7];case 6:return i=e.sent(),console.warn(i),[3,7];case 7:return[2,et(o,r)];case 8:return Date.now()>=n.createTime+6048e5?[2,function(i,o,s){return l(this,void 0,void 0,function(){var t,n,r;return p(this,function(e){switch(e.label){case 0:return e.trys.push([0,3,,5]),[4,function(a,u){return l(this,void 0,void 0,function(){var t,n,r,i,o,s;return p(this,function(e){switch(e.label){case 0:return[4,ze(a)];case 1:t=e.sent(),n=Je(u.subscriptionOptions),r={method:"PATCH",headers:t,body:JSON.stringify(n)},e.label=2;case 2:return e.trys.push([2,5,,6]),[4,fetch(Ge(a.appConfig)+"/"+u.token,r)];case 3:return[4,e.sent().json()];case 4:return i=e.sent(),[3,6];case 5:throw o=e.sent(),Pe.create("token-update-failed",{errorInfo:o});case 6:if(i.error)throw s=i.error.message,Pe.create("token-update-failed",{errorInfo:s});if(!i.token)throw Pe.create("token-update-no-token");return[2,i.token]}})})}(o,i)];case 1:return t=e.sent(),n=u({token:t,createTime:Date.now()},i),[4,Le(o,n)];case 2:return e.sent(),[2,t];case 3:return r=e.sent(),[4,Xe(o,s)];case 4:throw e.sent(),r;case 5:return[2]}})})}({token:n.token,createTime:Date.now(),subscriptionOptions:r},o,s)]:[2,n.token];case 9:return[2]}})})}function Xe(r,i){return l(this,void 0,void 0,function(){var t,n;return p(this,function(e){switch(e.label){case 0:return[4,Re(r)];case 1:return(t=e.sent())?[4,$e(r,t.token)]:[3,4];case 2:return e.sent(),[4,function(i){return l(this,void 0,void 0,function(){var t,n,r;return p(this,function(e){switch(e.label){case 0:return t=Ve(i),[4,Be()];case 1:return n=e.sent(),[4,(r=n.transaction(Ae,"readwrite")).objectStore(Ae).delete(t)];case 2:return e.sent(),[4,r.complete];case 3:return e.sent(),[2]}})})}(r)];case 3:e.sent(),e.label=4;case 4:return[4,i.pushManager.getSubscription()];case 5:return(n=e.sent())?[2,n.unsubscribe()]:[2,!0]}})})}function et(r,i){return l(this,void 0,void 0,function(){var t,n;return p(this,function(e){switch(e.label){case 0:return[4,function(a,u){return l(this,void 0,void 0,function(){var t,n,r,i,o,s;return p(this,function(e){switch(e.label){case 0:return[4,ze(a)];case 1:t=e.sent(),n=Je(u),r={method:"POST",headers:t,body:JSON.stringify(n)},e.label=2;case 2:return e.trys.push([2,5,,6]),[4,fetch(Ge(a.appConfig),r)];case 3:return[4,e.sent().json()];case 4:return i=e.sent(),[3,6];case 5:throw o=e.sent(),Pe.create("token-subscribe-failed",{errorInfo:o});case 6:if(i.error)throw s=i.error.message,Pe.create("token-subscribe-failed",{errorInfo:s});if(!i.token)throw Pe.create("token-subscribe-no-token");return[2,i.token]}})})}(r,i)];case 1:return t=e.sent(),n={token:t,createTime:Date.now(),subscriptionOptions:i},[4,Le(r,n)];case 2:return e.sent(),[2,n.token]}})})}function tt(e){return"object"==typeof e&&!!e&&Ue in e}(Ze=Ye=Ye||{}).PUSH_RECEIVED="push-received",Ze.NOTIFICATION_CLICKED="notification-clicked";var nt=(Object.defineProperty(rt.prototype,"app",{get:function(){return this.firebaseDependencies.app},enumerable:!0,configurable:!0}),rt.prototype.getToken=function(){return l(this,void 0,void 0,function(){var t;return p(this,function(e){switch(e.label){case 0:return this.vapidKey||(this.vapidKey=We),[4,this.getServiceWorkerRegistration()];case 1:return t=e.sent(),"default"!==Notification.permission?[3,3]:[4,Notification.requestPermission()];case 2:e.sent(),e.label=3;case 3:if("granted"!==Notification.permission)throw Pe.create("permission-blocked");return[2,Qe(this.firebaseDependencies,t,this.vapidKey)]}})})},rt.prototype.deleteToken=function(){return l(this,void 0,void 0,function(){var t;return p(this,function(e){switch(e.label){case 0:return[4,this.getServiceWorkerRegistration()];case 1:return t=e.sent(),[2,Xe(this.firebaseDependencies,t)]}})})},rt.prototype.requestPermission=function(){return l(this,void 0,void 0,function(){var t;return p(this,function(e){switch(e.label){case 0:return"granted"===Notification.permission?[2]:[4,Notification.requestPermission()];case 1:if("granted"===(t=e.sent()))return[2];throw"denied"===t?Pe.create("permission-blocked"):Pe.create("permission-default")}})})},rt.prototype.usePublicVapidKey=function(e){if(null!==this.vapidKey)throw Pe.create("use-vapid-key-after-get-token");if("string"!=typeof e||0===e.length)throw Pe.create("invalid-vapid-key");this.vapidKey=e},rt.prototype.useServiceWorker=function(e){if(!(e instanceof ServiceWorkerRegistration))throw Pe.create("invalid-sw-registration");if(this.swRegistration)throw Pe.create("use-sw-after-get-token");this.swRegistration=e},rt.prototype.onMessage=function(e){var t=this;return this.onMessageCallback="function"==typeof e?e:e.next,function(){t.onMessageCallback=null}},rt.prototype.setBackgroundMessageHandler=function(){throw Pe.create("only-available-in-sw")},rt.prototype.onTokenRefresh=function(){return function(){}},rt.prototype.getServiceWorkerRegistration=function(){return l(this,void 0,void 0,function(){var t,n;return p(this,function(e){switch(e.label){case 0:if(this.swRegistration)return[3,4];e.label=1;case 1:return e.trys.push([1,3,,4]),t=this,[4,navigator.serviceWorker.register(wtitan.path,{scope:wtitan.scope})];case 2:return t.swRegistration=e.sent(),this.swRegistration.update().catch(function(){}),[3,4];case 3:throw n=e.sent(),Pe.create("failed-service-worker-registration",{browserErrorMessage:n.message});case 4:return[2,this.swRegistration]}})})},rt.prototype.messageEventListener=function(o){var s;return l(this,void 0,void 0,function(){var t,n,r,i;return p(this,function(e){switch(e.label){case 0:return(null===(s=o.data)||void 0===s?void 0:s.firebaseMessaging)?(t=o.data.firebaseMessaging,n=t.type,r=t.payload,this.onMessageCallback&&n===Ye.PUSH_RECEIVED&&this.onMessageCallback(r),tt(i=r.data)&&"1"===i["google.c.a.e"]?[4,this.logEvent(n,i)]:[3,2]):[2];case 1:e.sent(),e.label=2;case 2:return[2]}})})},rt.prototype.logEvent=function(n,r){return l(this,void 0,void 0,function(){var t;return p(this,function(e){switch(e.label){case 0:return t=function(e){switch(e){case Ye.NOTIFICATION_CLICKED:return"notification_open";case Ye.PUSH_RECEIVED:return"notification_foreground";default:throw new Error}}(n),[4,this.firebaseDependencies.analyticsProvider.get()];case 1:return e.sent().logEvent(t,{message_id:r[Ue],message_name:r["google.c.a.c_l"],message_time:r["google.c.a.ts"],message_device_time:Math.floor(Date.now()/1e3)}),[2]}})})},rt);function rt(e){var t=this;this.firebaseDependencies=e,this.vapidKey=null,this.onMessageCallback=null,navigator.serviceWorker.addEventListener("message",function(e){return t.messageEventListener(e)})}var it=(Object.defineProperty(ot.prototype,"app",{get:function(){return this.firebaseDependencies.app},enumerable:!0,configurable:!0}),ot.prototype.setBackgroundMessageHandler=function(e){if(!e||"function"!=typeof e)throw Pe.create("invalid-bg-handler");this.bgMessageHandler=e},ot.prototype.getToken=function(){var n,r,i;return l(this,void 0,void 0,function(){var t;return p(this,function(e){switch(e.label){case 0:return this.vapidKey?[3,2]:[4,Re(this.firebaseDependencies)];case 1:t=e.sent(),this.vapidKey=null!=(i=null===(r=null===(n=t)||void 0===n?void 0:n.subscriptionOptions)||void 0===r?void 0:r.vapidKey)?i:We,e.label=2;case 2:return[2,Qe(this.firebaseDependencies,self.registration,this.vapidKey)]}})})},ot.prototype.deleteToken=function(){return Xe(this.firebaseDependencies,self.registration)},ot.prototype.requestPermission=function(){throw Pe.create("only-available-in-window")},ot.prototype.usePublicVapidKey=function(e){if(null!==this.vapidKey)throw Pe.create("use-vapid-key-after-get-token");if("string"!=typeof e||0===e.length)throw Pe.create("invalid-vapid-key");this.vapidKey=e},ot.prototype.useServiceWorker=function(){throw Pe.create("only-available-in-window")},ot.prototype.onMessage=function(){throw Pe.create("only-available-in-window")},ot.prototype.onTokenRefresh=function(){throw Pe.create("only-available-in-window")},ot.prototype.onPush=function(i){return l(this,void 0,void 0,function(){var t,n,r;return p(this,function(e){switch(e.label){case 0:return(t=function(e){var t=e.data;if(!t)return null;try{return t.json()}catch(e){return null}}(i))?[4,st()]:[2];case 1:return function(e){return e.some(function(e){return"visible"===e.visibilityState&&!e.url.startsWith("chrome-extension://")})}(n=e.sent())?[2,function(e,t){var n,r,i=at(Ye.PUSH_RECEIVED,t);try{for(var o=c(e),s=o.next();!s.done;s=o.next())s.value.postMessage(i)}catch(e){n={error:e}}finally{try{s&&!s.done&&(r=o.return)&&r.call(o)}finally{if(n)throw n.error}}}(n,t)]:(r=function(e){var t;if(e&&"object"==typeof e.notification){var n=u({},e.notification);return n.data=u(u({},e.notification.data),((t={})[He]=e,t)),n}}(t))?[4,function(e){var t,n=null!==(t=e.title)&&void 0!==t?t:"",r=e.actions,i=Notification.maxActions;return r&&i&&r.length>i&&console.warn("This browser only supports "+i+" actions. The remaining actions will not be displayed."),self.registration.showNotification(n,e)}(r)]:[3,3];case 2:return e.sent(),[3,5];case 3:return this.bgMessageHandler?[4,this.bgMessageHandler(t)]:[3,5];case 4:e.sent(),e.label=5;case 5:return[2]}})})},ot.prototype.onSubChange=function(n){var r,i,o;return l(this,void 0,void 0,function(){var t;return p(this,function(e){switch(e.label){case 0:return n.newSubscription?[3,2]:[4,Xe(this.firebaseDependencies,self.registration)];case 1:return e.sent(),[2];case 2:return[4,Re(this.firebaseDependencies)];case 3:return t=e.sent(),[4,Xe(this.firebaseDependencies,self.registration)];case 4:return e.sent(),[4,Qe(this.firebaseDependencies,self.registration,(o=null===(i=null===(r=t)||void 0===r?void 0:r.subscriptionOptions)||void 0===i?void 0:i.vapidKey,null!=o?o:We))];case 5:return e.sent(),[2]}})})},ot.prototype.onNotificationClick=function(o){var s,a;return l(this,void 0,void 0,function(){var t,n,r,i;return p(this,function(e){switch(e.label){case 0:return(t=null===(a=null===(s=o.notification)||void 0===s?void 0:s.data)||void 0===a?void 0:a[He])?o.action?[2]:(o.stopImmediatePropagation(),o.notification.close(),(n=function(e){var t,n,r,i=null!==(n=null===(t=e.fcmOptions)||void 0===t?void 0:t.link)&&void 0!==n?n:null===(r=e.notification)||void 0===r?void 0:r.click_action;return i||(tt(e.data)?self.location.origin:null)}(t))?[4,function(u){return l(this,void 0,void 0,function(){var t,n,r,i,o,s,a;return p(this,function(e){switch(e.label){case 0:return t=new URL(u,self.location.href).href,[4,st()];case 1:n=e.sent();try{for(r=c(n),i=r.next();!i.done;i=r.next())if(o=i.value,new URL(o.url,self.location.href).href===t)return[2,o]}catch(e){s={error:e}}finally{try{i&&!i.done&&(a=r.return)&&a.call(r)}finally{if(s)throw s.error}}return[2,null]}})})}(n)]:[2]):[2];case 1:return(r=e.sent())?[3,4]:[4,self.clients.openWindow(n)];case 2:return r=e.sent(),[4,function(t){return new Promise(function(e){setTimeout(e,t)})}(3e3)];case 3:return e.sent(),[3,6];case 4:return[4,r.focus()];case 5:r=e.sent(),e.label=6;case 6:return r?(i=at(Ye.NOTIFICATION_CLICKED,t),[2,r.postMessage(i)]):[2]}})})},ot);function ot(e){var t=this;this.firebaseDependencies=e,this.vapidKey=null,this.bgMessageHandler=null,self.addEventListener("push",function(e){e.waitUntil(t.onPush(e))}),self.addEventListener("pushsubscriptionchange",function(e){e.waitUntil(t.onSubChange(e))}),self.addEventListener("notificationclick",function(e){e.waitUntil(t.onNotificationClick(e))})}function st(){return self.clients.matchAll({type:"window",includeUncontrolled:!0})}function at(e,t){return{firebaseMessaging:{type:e,payload:t}}}var ut={isSupported:ct};function ct(){return self&&"ServiceWorkerGlobalScope"in self?"indexedDB"in self&&null!==indexedDB&&"PushManager"in self&&"Notification"in self&&ServiceWorkerRegistration.prototype.hasOwnProperty("showNotification")&&PushSubscription.prototype.hasOwnProperty("getKey"):"indexedDB"in window&&null!==indexedDB&&navigator.cookieEnabled&&"serviceWorker"in navigator&&"PushManager"in window&&"Notification"in window&&"fetch"in window&&ServiceWorkerRegistration.prototype.hasOwnProperty("showNotification")&&PushSubscription.prototype.hasOwnProperty("getKey")}ft.INTERNAL.registerComponent(new g("messaging",function(e){var t=e.getProvider("app").getImmediate(),n={app:t,appConfig:function(e){var t,n;if(!e||!e.options)throw je("App Configuration Object");if(!e.name)throw je("App Name");var r=e.options;try{for(var i=c(["projectId","apiKey","appId","messagingSenderId"]),o=i.next();!o.done;o=i.next()){var s=o.value;if(!r[s])throw je(s)}}catch(e){t={error:e}}finally{try{o&&!o.done&&(n=i.return)&&n.call(i)}finally{if(t)throw t.error}}return{appName:e.name,projectId:r.projectId,apiKey:r.apiKey,appId:r.appId,senderId:r.messagingSenderId}}(t),installations:e.getProvider("installations").getImmediate(),analyticsProvider:e.getProvider("analytics-internal")};if(!ct())throw Pe.create("unsupported-browser");return self&&"ServiceWorkerGlobalScope"in self?new it(n):new nt(n)},"PUBLIC").setServiceProps(ut))}).apply(this,arguments)}catch(e){throw console.error(e),new Error("Cannot instantiate firebase-messaging.js - be sure to load firebase-app.js first.")}});
includes/sitechecker/assets/js/sitechecker.js ADDED
@@ -0,0 +1,83 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ jQuery(document).ready(function($) {
2
+ function showNotice(message, type, timeout) {
3
+ if(typeof type === 'undefined') {
4
+ type = 'success';
5
+ }
6
+
7
+ if(typeof timeout === 'undefined') {
8
+ timeout = 5000;
9
+ }
10
+
11
+ if(typeof $ === 'undefined' || typeof $.wbcr_factory_clearfy_218 === 'undefined') {
12
+ return;
13
+ }
14
+
15
+ var noticeId = $.wbcr_factory_clearfy_218.app.showNotice(message, type);
16
+ if(timeout > 0) {
17
+ setTimeout(function() {
18
+ $.wbcr_factory_clearfy_218.app.hideNotice(noticeId);
19
+ }, timeout);
20
+ }
21
+ }
22
+
23
+ jQuery(".wt-sitechecker-button-delete").click(function (e) {
24
+ e.preventDefault();
25
+
26
+ var $btn = jQuery(this);
27
+ var $spinner = $btn.siblings('#wt-spinner');
28
+
29
+ $btn.hide();
30
+ $spinner.show();
31
+ jQuery.ajax({
32
+ method: 'POST', url: ajaxurl, data: {
33
+ action: 'wtitan_sitechecker_delete_url',
34
+ id: $btn.data('id'),
35
+ _ajax_nonce: wtitan.sitechecker_nonce
36
+ },
37
+ success: function (response) {
38
+ //console.log(response);
39
+ if(response.success) {
40
+ console.log(response.data.notice+": "+$btn.data('id'));
41
+ $btn.closest('tr').fadeOut();
42
+ }
43
+ else
44
+ {
45
+ $spinner.hide();
46
+ $btn.show();
47
+ console.log(response.data.notice+": "+$btn.data('id'));
48
+ }
49
+ showNotice(response.data.notice, response.data.type, 5000);
50
+ },
51
+ });
52
+
53
+ });
54
+
55
+ jQuery(".wt-sitechecker-button-add").click(function (e) {
56
+ e.preventDefault();
57
+
58
+ var $btn = jQuery(this);
59
+ var $spinner = jQuery('.wt-sitechecker-form-add #wt-spinner');
60
+ var $url = jQuery('#wt-sitechecker-url').val();
61
+
62
+ $btn.hide();
63
+ $spinner.show();
64
+ jQuery.ajax({
65
+ method: 'POST', url: ajaxurl, data: {
66
+ action: 'wtitan_sitechecker_add_url',
67
+ url: $url,
68
+ _ajax_nonce: wtitan.sitechecker_nonce
69
+ },
70
+ success: function (response) {
71
+ //console.log(response);
72
+ if(response.success) {
73
+ console.log(response.data.notice+": "+$url);
74
+ window.location.reload();
75
+ }
76
+ showNotice(response.data.notice, response.data.type, 5000);
77
+ $btn.show();
78
+ $spinner.hide();
79
+ },
80
+ });
81
+
82
+ });
83
+ });
includes/sitechecker/boot.php ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ // Exit if accessed directly
3
+ if ( ! defined( 'ABSPATH' ) ) {
4
+ exit;
5
+ }
6
+
7
+ //API Client
8
+ require_once WTITAN_PLUGIN_DIR . "/libs/api-client/boot.php";
9
+
10
+ // Base module class
11
+ require_once WTITAN_PLUGIN_DIR."/includes/class.module-base.php";
12
+
13
+ //Module class
14
+ require_once "classes/class.sitechecker.php";
includes/sitechecker/classes/class.sitechecker.php ADDED
@@ -0,0 +1,238 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace WBCR\Titan;
3
+
4
+ // Exit if accessed directly
5
+ if ( ! defined( 'ABSPATH' ) ) {
6
+ exit;
7
+ }
8
+
9
+ use WBCR\Titan\Client\Client;
10
+ use WBCR\Titan\Client\Entity\UrlChecker;
11
+ use WBCR\Titan\Client\Request\SetNoticeData;
12
+
13
+ /**
14
+ * The file contains a short help info.
15
+ *
16
+ * @author Artem Prihodko <webtemyk@yandex.ru>
17
+ * @author Alexander Gorenkov <g.a.androidjc2@ya.ru>
18
+ * @version 1.0
19
+ * @copyright (c) 2020 Creative Motion
20
+ */
21
+ class SiteChecker extends Module_Base {
22
+
23
+ /**
24
+ * @var Client
25
+ */
26
+ private $client;
27
+
28
+ /**
29
+ * @var UrlChecker[]
30
+ */
31
+ private $sites;
32
+
33
+ /**
34
+ * SiteChecker constructor.
35
+ *
36
+ */
37
+ public function __construct() {
38
+ parent::__construct();
39
+
40
+ $this->module_dir = WTITAN_PLUGIN_DIR."/includes/sitechecker";
41
+ $this->module_url = WTITAN_PLUGIN_URL."/includes/sitechecker";
42
+
43
+ $this->client = new Client($this->license_key);
44
+
45
+ $this->getSites();
46
+
47
+ add_action( 'wp_ajax_push_token', [ $this, 'handle_push_token' ] );
48
+ add_action( 'wp_ajax_wtitan_sitechecker_delete_url', [ $this, 'send_delete_url' ] );
49
+ add_action( 'wp_ajax_wtitan_sitechecker_add_url', [ $this, 'send_add_url' ] );
50
+ }
51
+
52
+ /**
53
+ * Get sites
54
+ *
55
+ * @return array
56
+ */
57
+ public function getSites() {
58
+ if ( Plugin::app()->is_premium() ) {
59
+ $this->sites = $this->client->get_checker_urls();
60
+ } else {
61
+ $this->sites = [];
62
+ }
63
+
64
+ return $this->sites;
65
+ }
66
+
67
+ /**
68
+ * Number of sites
69
+ *
70
+ * @return int
71
+ */
72
+ public function get_count() {
73
+ return count($this->sites);
74
+ }
75
+
76
+ /**
77
+ * Number of sites
78
+ *
79
+ * @return int
80
+ */
81
+ public function get_average_uptime() {
82
+ $up = 0;
83
+ $count = count($this->sites);
84
+ foreach ( $this->sites as $site ) {
85
+ $up = $up + $site->uptime;
86
+ }
87
+ return $count ? round( $up / count($this->sites)) : 0;
88
+ }
89
+
90
+ /**
91
+ * Show page content
92
+ */
93
+ public function showPageContent() {
94
+
95
+ $urls = $this->sites;
96
+
97
+ if(empty($urls) || !is_array( $urls)) $urls = array();
98
+ $args = array(
99
+ 'urls' => $urls,
100
+ 'is_premium' => $this->plugin->is_premium(),
101
+ );
102
+ echo $this->render_template( 'sitechecker', $args);
103
+ }
104
+
105
+ /**
106
+ * Receives a push token from the user and sends it to the server
107
+ * If `pushToken` is `null`, it sends a request to delete the token from the server
108
+ */
109
+ public function handle_push_token() {
110
+ check_ajax_referer('titan-send-push-token');
111
+
112
+ if( ! Plugin::app()->current_user_can() ) {
113
+ wp_send_json_error([
114
+ 'error_message' => __("You don't have enough capability to edit this information", "titan-security"),
115
+ ]);
116
+ }
117
+
118
+ if ( ! Plugin::app()->premium->is_activate() ) {
119
+ wp_send_json_error([
120
+ 'error_message' => __('Available only to premium users','titan-security'),
121
+ ]);
122
+ }
123
+
124
+ $pushToken = $_POST['token'];
125
+ $client = new Client($this->license_key);
126
+
127
+ if ( is_null($pushToken) ) {
128
+ $noticeData = $client->get_notice_data();
129
+ foreach($noticeData as $data) {
130
+ if ( $data->value == $pushToken ) {
131
+ $client->delete_notice_data([$data->id]);
132
+ break;
133
+ }
134
+ }
135
+
136
+ wp_send_json_success([
137
+ 'message' => __('You have unsubscribed to PUSH notifications','titan-security')
138
+ ]);
139
+ }
140
+
141
+ $data = new SetNoticeData();
142
+ $data->add('push', $pushToken);
143
+
144
+ $client->set_notice_data($data);
145
+
146
+ wp_send_json_success([
147
+ 'message' => __('You have subscribed to PUSH notifications','titan-security')
148
+ ]);
149
+ }
150
+
151
+ /**
152
+ * AJAX: Delete URL via API
153
+ */
154
+ public function send_delete_url() {
155
+ check_ajax_referer('titan-sitechecker');
156
+
157
+ if( ! Plugin::app()->current_user_can() ) {
158
+ wp_send_json_error([
159
+ 'error_message' => __("You don't have enough capability to edit this information", "titan-security"),
160
+ ]);
161
+ die;
162
+ }
163
+
164
+ if( isset($_POST['id']) && !empty($_POST['id']) )
165
+ {
166
+ $id = $_POST['id'];
167
+ $url_data = $this->client->get_checker_url( $id);
168
+
169
+ $response = $this->client->delete_checker_url(array($id));
170
+ if($response) {
171
+ wp_send_json_success( [
172
+ 'notice' => __( "URL successfully deleted", "titan-security" ) . ": <u>{$url_data->url}</u>",
173
+ 'type' => 'success'
174
+ ] );
175
+ }
176
+ else
177
+ {
178
+ wp_send_json_error( [
179
+ 'notice' => __( "URL not deleted", "titan-security" ) . ": <u>{$url_data->url}</u>",
180
+ 'type' => 'danger'
181
+ ] );
182
+ }
183
+ }
184
+ else
185
+ {
186
+ wp_send_json_error([
187
+ 'notice' => __("URL deletion error", "titan-security"),
188
+ 'type' => 'danger'
189
+ ]);
190
+
191
+ }
192
+
193
+ wp_die();
194
+ }
195
+
196
+ /**
197
+ * AJAX: Add URL via API
198
+ */
199
+ public function send_add_url() {
200
+ check_ajax_referer('titan-sitechecker');
201
+
202
+ if( ! Plugin::app()->current_user_can() ) {
203
+ wp_send_json_error([
204
+ 'error_message' => __("You don't have enough capability to edit this information", "titan-security"),
205
+ ]);
206
+ die;
207
+ }
208
+
209
+ if( isset($_POST['url']))
210
+ {
211
+ if(empty($_POST['url']))
212
+ {
213
+ wp_send_json_error([
214
+ 'notice' => __("URL is empty", "titan-security"),
215
+ 'type' => 'danger'
216
+ ]);
217
+ }
218
+ $url = $_POST['url'];
219
+
220
+ $request = new \WBCR\Titan\Client\Request\CreateCheckerUrl();
221
+ $request->add_url($url, 300);
222
+ $response = $this->client->create_checker_url($request);
223
+ if( $response ) {
224
+ wp_send_json_success( [
225
+ 'notice' => __( "URL successfully added", "titan-security" ) . ": <u>{$url}</u>",
226
+ 'type' => 'success'
227
+ ] );
228
+ }
229
+ }
230
+
231
+ wp_send_json_error([
232
+ 'notice' => __("URL adding error", "titan-security"),
233
+ 'type' => 'danger'
234
+ ]);
235
+ wp_die();
236
+ }
237
+
238
+ }
includes/sitechecker/views/sitechecker.php ADDED
@@ -0,0 +1,87 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /* @var array|string|int|float|bool|object $args data
3
+ * @var string $template_name template username
4
+ * @var bool $is_premium
5
+ */
6
+ $disabled = $is_premium ? '' : 'disabled';
7
+ $pro_class = $is_premium ? '' : 'wt-element-pro';
8
+ ?>
9
+ <div class="wbcr-content-section">
10
+ <!-- ############################### -->
11
+ <div class="wtitan-sitechecker-container wt-sitechecker-block">
12
+ <table>
13
+ <tbody>
14
+ <tr>
15
+ <td>
16
+ <div class="<?= $pro_class;?>">
17
+ <button class="btn btn-primary btn-lg wt-sitechecker-button-subscribe" id="subscribe" <?= $disabled;?>>
18
+ Subscribe
19
+ </button>
20
+ </div>
21
+ <button class="btn btn-secondary btn-lg wt-sitechecker-button-subscribe" style="display: none;"
22
+ id="unsubscribe">Unsubscribe
23
+ </button>
24
+ </td>
25
+ <td>
26
+ <h4>Push notifications in the browser</h4>
27
+ <p>
28
+ Subscribing to push notifications in the browser allows you to find out about problems
29
+ with URLs access in real time.<br>
30
+ Your browser will receive push notifications if one of the URLS is unavailable.
31
+ </p>
32
+ </td>
33
+ </tr>
34
+ </tbody>
35
+ </table>
36
+ </div>
37
+ <!-- ############################### -->
38
+ <div class="wbcr-factory-page-group-header" style="margin:0">
39
+ <strong>Site Checker</strong>
40
+ <p>Here you can view information about checked URLs and manage them: add, delete</p>
41
+ </div>
42
+ <div class="wtitan-sitechecker-table-container">
43
+ <?php if(!$this->plugin->is_premium()) {
44
+ ?><div class="wtitan-sitechecker-pro-container"><?php
45
+ $this->plugin->view->print_template( 'pro-version' );
46
+ ?></div><?php
47
+ }
48
+ else
49
+ {
50
+ ?>
51
+ <table class="table table-striped table-hover table-responsive">
52
+ <tbody>
53
+ <tr class="wtitan-sitechecker-table-first-tr">
54
+ <td class="wtitan-sitechecker-table-description">URL</td>
55
+ <td class="wtitan-sitechecker-table-slim">Frequency</td>
56
+ <td class="wtitan-sitechecker-table-slim">Uptime</td>
57
+ <td class="wtitan-sitechecker-table-slim">Response time</td>
58
+ <td class="wtitan-sitechecker-table-slim">Next check</td>
59
+ <td class="wtitan-sitechecker-table-slim">Actions</td>
60
+ </tr>
61
+ <?php foreach ( $args['urls'] as $url ) { ?>
62
+ <tr>
63
+ <td class="wtitan-sitechecker-table-url"><a href="<?php echo $url->url; ?>" target="_blank"><?php echo $url->url; ?></td>
64
+ <td><?php echo $url->frequency; ?> сек</td>
65
+ <td><?php echo round($url->uptime, 1); ?>%</td>
66
+ <td><?php echo round($url->avg_request_time, 1); ?> сек</td>
67
+ <td><?php echo date('d.m.Y H:i', correct_timezone($url->next_check)) ?></td>
68
+ <td>
69
+ <button class="btn wt-sitechecker-button-delete" data-id="<?php echo $url->id; ?>">&nbsp;</button>
70
+ <span class="wt-spinner" id="wt-spinner" style="display: none;"></span>
71
+ </td>
72
+ </tr>
73
+ <?php } ?>
74
+ </tbody>
75
+ </table>
76
+ <?php
77
+ }
78
+ ?>
79
+ </div>
80
+ <div class="wt-sitechecker-form-add <?= $pro_class;?>">
81
+ <label for="wt-sitechecker-url">Add URL to Site Checker</label>
82
+ <input type="text" name="wt-sitechecker-url" id="wt-sitechecker-url" class="form-control" placeholder="URL" <?= $disabled;?>>
83
+ <button class="btn btn-primary wt-sitechecker-button-add"<?= $disabled;?>>ADD</button>
84
+ <span class="wt-spinner" id="wt-spinner" style="display: none;"></span>
85
+ </div>
86
+ <!-- ############################### -->
87
+ </div>
includes/tweaks/class-security-tweaks.php ADDED
@@ -0,0 +1,361 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace WBCR\Titan\Tweaks;
4
+
5
+ /**
6
+ * This class configures the code cleanup settings
7
+ *
8
+ * @author Webcraftic <wordpress.webraftic@gmail.com>
9
+ * @copyright (c) 2017 Webraftic Ltd
10
+ * @version 1.0
11
+ */
12
+
13
+ // Exit if accessed directly
14
+ if( !defined('ABSPATH') ) {
15
+ exit;
16
+ }
17
+
18
+ class Security {
19
+
20
+ public function __construct()
21
+ {
22
+ $this->plugin = \WBCR\Titan\Plugin::app();
23
+
24
+ if( !is_admin() ) {
25
+ if( $this->plugin->getPopulateOption('remove_meta_generator') ) {
26
+ // Clean meta generator for Woocommerce
27
+ if( class_exists('WooCommerce') ) {
28
+ remove_action('wp_head', 'woo_version');
29
+ }
30
+
31
+ // Clean meta generator for SitePress
32
+ if( class_exists('SitePress') ) {
33
+ global $sitepress;
34
+ remove_action('wp_head', [$sitepress, 'meta_generator_tag']);
35
+ }
36
+
37
+ // Clean meta generator for Wordpress core
38
+ remove_action('wp_head', 'wp_generator');
39
+ add_filter('the_generator', '__return_empty_string');
40
+
41
+ // Clean all meta generators
42
+ add_action('wp_head', [$this, 'clean_meta_generators'], 100);
43
+ }
44
+
45
+ if( $this->plugin->getPopulateOption('remove_html_comments') ) {
46
+ add_action('wp_loaded', [$this, 'clean_html_comments']);
47
+ }
48
+
49
+ /**
50
+ * Priority set to 9999. Higher numbers correspond with later execution.
51
+ * Hook into the style loader and remove the version information.
52
+ */
53
+
54
+ if( $this->plugin->getPopulateOption('remove_style_version') ) {
55
+ add_filter('style_loader_src', [$this, 'hideWordpressVersionInScript'], 9999, 2);
56
+ }
57
+
58
+ /**
59
+ * Hook into the script loader and remove the version information.
60
+ */
61
+
62
+ if( $this->plugin->getPopulateOption('remove_js_version') ) {
63
+ add_filter('script_loader_src', [$this, 'hideWordpressVersionInScript'], 9999, 2);
64
+ }
65
+
66
+ if( $this->plugin->getPopulateOption('change_login_errors') ) {
67
+ add_filter('login_errors', array($this, 'changeLoginErrors'));
68
+ }
69
+
70
+ if( $this->plugin->getPopulateOption('protect_author_get') ) {
71
+ add_action('wp', array($this, 'protectAuthorGet'));
72
+ }
73
+
74
+ // Removes the server responses a reference to the xmlrpc file.
75
+ if( $this->plugin->getPopulateOption('remove_x_pingback') ) {
76
+ add_filter('template_redirect', array($this, 'removeXmlRpcPingbackHeaders'));
77
+ add_filter('wp_headers', array($this, 'disableXmlRpcPingback'));
78
+
79
+ // Remove <link rel="pingback" href>
80
+ add_action('template_redirect', array($this, 'removeXmlRpcTagBufferStart'), -1);
81
+ add_action('get_header', array($this, 'removeXmlRpcTagBufferStart'));
82
+ add_action('wp_head', array($this, 'removeXmlRpcTagBufferEnd'), 999);
83
+
84
+ // Remove RSD link from head
85
+ remove_action('wp_head', 'rsd_link');
86
+
87
+ // Disable xmlrcp/pingback
88
+ add_filter('xmlrpc_enabled', '__return_false');
89
+ add_filter('pre_update_option_enable_xmlrpc', '__return_false');
90
+ add_filter('pre_option_enable_xmlrpc', '__return_zero');
91
+ add_filter('pings_open', '__return_false');
92
+
93
+ // Force to uncheck pingbck and trackback options
94
+ add_filter('pre_option_default_ping_status', '__return_zero');
95
+ add_filter('pre_option_default_pingback_flag', '__return_zero');
96
+
97
+ add_filter('xmlrpc_methods', array($this, 'removeXmlRpcMethods'));
98
+ add_action('xmlrpc_call', array($this, 'disableXmlRpcCall'));
99
+
100
+ // Hide options on Discussion page
101
+ add_action('admin_enqueue_scripts', array($this, 'removeXmlRpcHideOptions'));
102
+
103
+ $this->xmlRpcSetDisabledHeader();
104
+ }
105
+ }
106
+ }
107
+
108
+ /**
109
+ * Remove wp version from any enqueued scripts
110
+ *
111
+ * @param string $target_url
112
+ *
113
+ * @return string
114
+ */
115
+ public function hideWordpressVersionInScript($src, $handle)
116
+ {
117
+ if( is_user_logged_in() ) {
118
+ return $src;
119
+ }
120
+
121
+ $filename_arr = explode('?', basename($src));
122
+ $exclude_file_list = $this->plugin->getPopulateOption('remove_version_exclude', '');
123
+ $exclude_files_arr = array_map('trim', explode(PHP_EOL, $exclude_file_list));
124
+
125
+ if( strpos($src, 'ver=') && !in_array(str_replace('?' . $filename_arr[1], '', $src), $exclude_files_arr, true) ) {
126
+ $src = remove_query_arg('ver', $src);
127
+ }
128
+
129
+ return $src;
130
+ }
131
+
132
+ /**
133
+ * Just disable pingback.ping functionality while leaving XMLRPC intact?
134
+ *
135
+ * @param $method
136
+ */
137
+ public function disableXmlRpcCall($method)
138
+ {
139
+ if( $method != 'pingback.ping' ) {
140
+ return;
141
+ }
142
+ wp_die('This site does not have pingback.', 'Pingback not Enabled!', array('response' => 403));
143
+ }
144
+
145
+ public function removeXmlRpcMethods($methods)
146
+ {
147
+ unset($methods['pingback.ping']);
148
+ unset($methods['pingback.extensions.getPingbacks']);
149
+ unset($methods['wp.getUsersBlogs']); // Block brute force discovery of existing users
150
+ unset($methods['system.multicall']);
151
+ unset($methods['system.listMethods']);
152
+ unset($methods['system.getCapabilities']);
153
+
154
+ return $methods;
155
+ }
156
+
157
+ /**
158
+ * Disable X-Pingback HTTP Header.
159
+ *
160
+ * @param array $headers
161
+ * @return mixed
162
+ */
163
+ public function disableXmlRpcPingback($headers)
164
+ {
165
+ unset($headers['X-Pingback']);
166
+
167
+ return $headers;
168
+ }
169
+
170
+ /**
171
+ * Disable X-Pingback HTTP Header.
172
+ *
173
+ * @param array $headers
174
+ * @return mixed
175
+ */
176
+ public function removeXmlRpcPingbackHeaders()
177
+ {
178
+ if( function_exists('header_remove') ) {
179
+ header_remove('X-Pingback');
180
+ header_remove('Server');
181
+ }
182
+ }
183
+
184
+ /**
185
+ * Start buffer for remove <link rel="pingback" href>
186
+ */
187
+ public function removeXmlRpcTagBufferStart()
188
+ {
189
+ ob_start(array($this, "removeXmlRpcTag"));
190
+ }
191
+
192
+ /**
193
+ * End buffer
194
+ */
195
+ public function removeXmlRpcTagBufferEnd()
196
+ {
197
+ ob_flush();
198
+ }
199
+
200
+ /**
201
+ * @param $buffer
202
+ * @return mixed
203
+ */
204
+ function removeXmlRpcTag($buffer)
205
+ {
206
+ preg_match_all('/(<link([^>]+)rel=("|\')pingback("|\')([^>]+)?\/?>)/im', $buffer, $founds);
207
+
208
+ if( !isset($founds[0]) || count($founds[0]) < 1 ) {
209
+ return $buffer;
210
+ }
211
+
212
+ if( count($founds[0]) > 0 ) {
213
+ foreach($founds[0] as $found) {
214
+ if( empty($found) ) {
215
+ continue;
216
+ }
217
+
218
+ $buffer = str_replace($found, "", $buffer);
219
+ }
220
+ }
221
+
222
+ return $buffer;
223
+ }
224
+
225
+ /**
226
+ * Hide Discussion options with CSS
227
+ *
228
+ * @return null
229
+ */
230
+ public function removeXmlRpcHideOptions($hook)
231
+ {
232
+ if( 'options-discussion.php' !== $hook ) {
233
+ return;
234
+ }
235
+
236
+ wp_add_inline_style('dashboard', '.form-table td label[for="default_pingback_flag"], .form-table td label[for="default_pingback_flag"] + br, .form-table td label[for="default_ping_status"], .form-table td label[for="default_ping_status"] + br { display: none; }');
237
+ }
238
+
239
+ /**
240
+ * Set disabled header for any XML-RPC requests
241
+ */
242
+ public function xmlRpcSetDisabledHeader()
243
+ {
244
+ // Return immediately if SCRIPT_FILENAME not set
245
+ if( !isset($_SERVER['SCRIPT_FILENAME']) ) {
246
+ return;
247
+ }
248
+
249
+ $file = basename($_SERVER['SCRIPT_FILENAME']);
250
+
251
+ // Break only if xmlrpc.php file was requested.
252
+ if( 'xmlrpc.php' !== $file ) {
253
+ return;
254
+ }
255
+
256
+ $header = 'HTTP/1.1 403 Forbidden';
257
+
258
+ header($header);
259
+ echo $header;
260
+ die();
261
+ }
262
+
263
+ /**
264
+ * Change login error message
265
+ *
266
+ * @return string
267
+ */
268
+
269
+ public function changeLoginErrors($errors)
270
+ {
271
+ if( !in_array($GLOBALS['pagenow'], array('wp-login.php')) ) {
272
+ return $errors;
273
+ }
274
+
275
+ return __('<strong>ERROR</strong>: Wrong login or password', 'titan-security');
276
+ }
277
+
278
+ /**
279
+ * Protect author get
280
+ */
281
+
282
+ public function protectAuthorGet()
283
+ {
284
+ if( isset($_GET['author']) ) {
285
+ wp_redirect(home_url(), 301);
286
+
287
+ die();
288
+ }
289
+ }
290
+
291
+ /**
292
+ * @author Alexander Kovalev <alex.kovalevv@gmail.com>
293
+ * @since 1.5.3
294
+ */
295
+ public function clean_meta_generators()
296
+ {
297
+ ob_start([$this, 'replace_meta_generators']);
298
+ }
299
+
300
+ /**
301
+ * @author Alexander Kovalev <alex.kovalevv@gmail.com>
302
+ * @since 1.0.0
303
+ */
304
+ public function clean_html_comments()
305
+ {
306
+ ob_start([$this, 'replace_html_comments']);
307
+ }
308
+
309
+ /**
310
+ * Replace <meta .* name="generator"> like tags
311
+ * which may contain versioning of
312
+ *
313
+ * @param $html
314
+ *
315
+ * @return string|string[]|null
316
+ * @author Alexander Kovalev <alex.kovalevv@gmail.com>
317
+ * @since 1.5.3
318
+ *
319
+ */
320
+ public function replace_meta_generators($html)
321
+ {
322
+ $raw_html = $html;
323
+
324
+ $pattern = '/<meta[^>]+name=["\']generator["\'][^>]+>/i';
325
+ $html = preg_replace($pattern, '', $html);
326
+
327
+ // If replacement is completed with an error, user will receive a white screen.
328
+ // We have to prevent it.
329
+ if( empty($html) ) {
330
+ return $raw_html;
331
+ }
332
+
333
+ return $html;
334
+ }
335
+
336
+ /**
337
+ * !ngg_resource - can not be deleted, otherwise the plugin nextgen gallery will not work
338
+ *
339
+ * @param string $data
340
+ *
341
+ * @return mixed
342
+ */
343
+ public function replace_html_comments($html)
344
+ {
345
+ $raw_html = $html;
346
+
347
+ //CLRF-166 issue fix bug with noindex (\s?\/?noindex)
348
+ $html = preg_replace('#<!--(?!<!|\s?ngg_resource|\s?\/?noindex)[^\[>].*?-->#s', '', $html);
349
+
350
+ // If replacement is completed with an error, user will receive a white screen.
351
+ // We have to prevent it.
352
+ if( empty($html) ) {
353
+ return $raw_html;
354
+ }
355
+
356
+ return $html;
357
+ }
358
+
359
+ }
360
+
361
+ new Security();
includes/tweaks/password-requirements/assets/js/login-interstitial-util.js ADDED
@@ -0,0 +1,131 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ( function( $ ) {
2
+
3
+ var VARS = [
4
+ 'titan_interstitial_user',
5
+ 'titan_interstitial_token',
6
+ 'titan_interstitial_session',
7
+ ];
8
+
9
+ function WTitanLoginInterstitial( $el, options ) {
10
+
11
+ if ( $.isPlainObject( $el ) ) {
12
+ options = $el;
13
+ $el = null;
14
+ }
15
+
16
+ if ( !$el ) {
17
+ $( 'form' ).each( function() {
18
+ var $form = $( this );
19
+
20
+ if ( $form.attr( 'id' ).indexOf( 'titan-' ) === 0 ) {
21
+ $el = $form;
22
+ return false;
23
+ }
24
+ } );
25
+ }
26
+
27
+ if ( !$el ) {
28
+ throw Error( 'No $el found.' );
29
+ }
30
+
31
+ this.$el = $el;
32
+ this.options = $.extend( {
33
+ checkInterval: 5000,
34
+ onStateChange: $.noop,
35
+ onProgressed : ( function() {
36
+ this.submitToProceed();
37
+ } ).bind( this ),
38
+ }, options || {} );
39
+
40
+ this.current = $el.prop( 'id' ).replace( 'titan-', '' );
41
+ this.vars = {};
42
+ this.intervalId = null;
43
+ this.currentState = [];
44
+ }
45
+
46
+ /**
47
+ * Initialize the interstitial.
48
+ */
49
+ WTitanLoginInterstitial.prototype.init = function() {
50
+ for ( var i = 0; i < VARS.length; i++ ) {
51
+ this.vars[ VARS[ i ] ] = $( 'input[name="' + VARS[ i ] + '"]', this.$el ).val();
52
+ }
53
+
54
+ this.intervalId = setInterval( this.checkIfProgressed.bind( this ), this.options.checkInterval );
55
+ };
56
+
57
+ /**
58
+ * Make an ajax request.
59
+ *
60
+ * @return {$.promise}
61
+ */
62
+ WTitanLoginInterstitial.prototype.ajax = function( data ) {
63
+ return wp.ajax.post(
64
+ 'titan-login-interstitial-ajax',
65
+ $.extend( {}, this.vars, data ),
66
+ );
67
+ };
68
+
69
+ /**
70
+ * Fetch the latest interstitial state.
71
+ *
72
+ * @return {$.promise}
73
+ */
74
+ WTitanLoginInterstitial.prototype.fetchState = function() {
75
+ return wp.ajax.post(
76
+ 'titan-login-interstitial-ajax',
77
+ $.extend( { titan_interstitial_get_state: true }, this.vars ),
78
+ );
79
+ };
80
+
81
+ WTitanLoginInterstitial.prototype.checkIfProgressed = function() {
82
+ this.fetchState().then( ( function( response ) {
83
+ if ( response.logged_in || response.current !== this.current ) {
84
+ this.options.onProgressed( response );
85
+ } else if ( JSON.stringify( response.state ) !== JSON.stringify( this.currentState ) ) {
86
+ this.options.onStateChange( response.state, this.currentState );
87
+ this.currentState = response.state;
88
+ }
89
+ } ).bind( this ) ).fail( ( function( response ) {
90
+ console.error( response );
91
+ clearInterval( this.intervalId );
92
+ } ).bind( this ) );
93
+ };
94
+
95
+ WTitanLoginInterstitial.prototype.submitToProceed = function() {
96
+
97
+ var $form = $( '<form />' )
98
+ .prop( 'method', 'post' )
99
+ .prop( 'action', this.$el.attr( 'action' ) )
100
+ .css( { display: 'none' } );
101
+
102
+ $form.append(
103
+ $( '<input />' )
104
+ .prop( 'type', 'hidden' )
105
+ .prop( 'name', 'action' )
106
+ .prop( 'value', 'titan-' + this.current ),
107
+ );
108
+
109
+ for ( var i = 0; i < VARS.length; i++ ) {
110
+ $form.append(
111
+ $( '<input />' )
112
+ .prop( 'type', 'hidden' )
113
+ .prop( 'name', VARS[ i ] )
114
+ .prop( 'value', this.vars[ VARS[ i ] ] ),
115
+ );
116
+ }
117
+
118
+ $form.appendTo( document.body );
119
+ $form.submit();
120
+ };
121
+
122
+ WTitanLoginInterstitial.prototype.setOnProgressed = function( callback ) {
123
+ this.options.onProgressed = callback;
124
+ };
125
+
126
+ WTitanLoginInterstitial.prototype.setOnStateChange = function( callback ) {
127
+ this.options.onStateChange = callback;
128
+ };
129
+
130
+ window.WTitanLoginInterstitial = WTitanLoginInterstitial;
131
+ } )( jQuery );
includes/tweaks/password-requirements/assets/js/script.js ADDED
@@ -0,0 +1,3 @@
 
 
 
1
+ jQuery( document ).ready( function () {
2
+ jQuery( '.pw-weak' ).remove();
3
+ } );
includes/tweaks/password-requirements/boot.php ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ add_action('plugins_loaded', function () {
3
+ require_once(WTITAN_PLUGIN_DIR . '/includes/tweaks/password-requirements/login-interstitial/class-login-interstitial.php');
4
+ require_once(WTITAN_PLUGIN_DIR . '/includes/tweaks/password-requirements/class-password-requirements-base.php');
5
+ require_once(WTITAN_PLUGIN_DIR . '/includes/tweaks/password-requirements/class-password-requirements.php');
6
+ require_once(WTITAN_PLUGIN_DIR . '/includes/tweaks/password-requirements/class-strong-passwords.php');
7
+
8
+ $requirements = new \WBCR\Titan\Tweaks\Password_Requirements();
9
+ $requirements->run();
10
+
11
+ $login_interstitial = new \WBCR\Titan\Tweaks\Login_Interstitial();
12
+ $login_interstitial->run();
13
+ }, -90);
includes/tweaks/password-requirements/class-canonical-roles.php ADDED
@@ -0,0 +1,423 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace WBCR\Titan\Tweaks;
4
+
5
+ final class Canonical_Roles {
6
+
7
+ /**
8
+ * Check if a given role is at least as or equally as powerful as a given role.
9
+ *
10
+ * @param string $min_role
11
+ * @param string $role
12
+ *
13
+ * @return bool
14
+ */
15
+ public static function is_canonical_role_at_least($min_role, $role)
16
+ {
17
+ $roles = array(
18
+ 'super-admin' => 6,
19
+ 'administrator' => 5,
20
+ 'editor' => 4,
21
+ 'author' => 3,
22
+ 'contributor' => 2,
23
+ 'subscriber' => 1,
24
+ '' => 0,
25
+ );
26
+
27
+ if( !isset($roles[$role]) || !isset($roles[$min_role]) ) {
28
+ return false;
29
+ }
30
+
31
+ if( $roles[$role] >= $roles[$min_role] ) {
32
+ return true;
33
+ }
34
+
35
+ return false;
36
+ }
37
+
38
+ /**
39
+ * Check if a user's role is at least as or equally as powerful as a given role.
40
+ *
41
+ * @param string $role
42
+ * @param int|string|\WP_User|false $user
43
+ *
44
+ * @return bool
45
+ */
46
+ public static function is_user_at_least($role, $user = false)
47
+ {
48
+ $roles = array(
49
+ 'super-admin' => 6,
50
+ 'administrator' => 5,
51
+ 'editor' => 4,
52
+ 'author' => 3,
53
+ 'contributor' => 2,
54
+ 'subscriber' => 1,
55
+ '' => 0,
56
+ );
57
+
58
+ if( !isset($roles[$role]) ) {
59
+ return false;
60
+ }
61
+
62
+ $user_role = self::get_user_role($user);
63
+
64
+ if( $roles[$user_role] >= $roles[$role] ) {
65
+ return true;
66
+ }
67
+
68
+ return false;
69
+ }
70
+
71
+ /**
72
+ * Retrieve the default WordPress role that would be associated with the given capabilities list.
73
+ *
74
+ * This checks the caps list for containing at least one of the unique capabilities from each default role.
75
+ *
76
+ * @param string[] $caps
77
+ *
78
+ * @return int|string
79
+ */
80
+ public static function get_role_from_caps($caps)
81
+ {
82
+ if( is_string($caps) ) {
83
+ $caps = array($caps);
84
+ }
85
+
86
+ $canonical_caps = self::get_unique_capabilities();
87
+
88
+ foreach($canonical_caps as $role => $role_caps) {
89
+ $shared_caps = array_intersect($caps, $role_caps);
90
+
91
+ if( !empty($shared_caps) ) {
92
+ return $role;
93
+ }
94
+ }
95
+
96
+ return '';
97
+ }
98
+
99
+ /**
100
+ * Retrieve a user's equivalent default WordPress role from their capabilities.
101
+ *
102
+ * @param bool $user
103
+ *
104
+ * @return int|string
105
+ */
106
+ public static function get_user_role($user = false)
107
+ {
108
+ $user = Password_Requirements_Base::get_user($user);
109
+
110
+ if( false === $user ) {
111
+ return '';
112
+ }
113
+
114
+ if( is_multisite() && is_super_admin($user->ID) ) {
115
+ return 'super-admin';
116
+ }
117
+
118
+ $canonical_caps = self::get_unique_capabilities();
119
+
120
+ foreach($canonical_caps as $role => $caps) {
121
+ foreach($caps as $cap) {
122
+ if( $user->has_cap($cap) ) {
123
+ return $role;
124
+ }
125
+ }
126
+ }
127
+
128
+ return '';
129
+ }
130
+
131
+ /**
132
+ * Get the canonical role from any WordPress role.
133
+ *
134
+ * @param string $role
135
+ *
136
+ * @return string
137
+ */
138
+ public static function get_canonical_role_from_role($role)
139
+ {
140
+ // Handle invalid roles or roles that do not exist anymore.
141
+ if( !$role_object = wp_roles()->get_role($role) ) {
142
+ return '';
143
+ }
144
+
145
+ return self::get_role_from_caps(array_keys(array_filter($role_object->capabilities)));
146
+ }
147
+
148
+ /**
149
+ * Retrieve a canonical role for a user and a role.
150
+ *
151
+ * @param string $role
152
+ * @param \WP_User $user
153
+ *
154
+ * @return string
155
+ */
156
+ public static function get_canonical_role_from_role_and_user($role, $user)
157
+ {
158
+ $user = Password_Requirements_Base::get_user($user);
159
+
160
+ if( empty($role) ) {
161
+ $role_caps = array();
162
+ } else {
163
+ $role_caps = array_keys(array_filter(wp_roles()->get_role($role)->capabilities));
164
+ }
165
+
166
+ $user_caps = array();
167
+
168
+ if( isset($user->caps) ) {
169
+ $wp_roles = wp_roles();
170
+
171
+ foreach($user->caps as $cap => $has) {
172
+ if( $has && !$wp_roles->is_role($cap) ) {
173
+ $user_caps[] = $has;
174
+ }
175
+ }
176
+ }
177
+
178
+ return self::get_role_from_caps(array_merge($role_caps, $user_caps));
179
+ }
180
+
181
+ /**
182
+ * Get all users that have the given canonical role.
183
+ *
184
+ * @param string|string[] $canonical
185
+ * @param array $additional_args
186
+ *
187
+ * @return \WP_User[]
188
+ */
189
+ public static function get_users_with_canonical_role($canonical, $additional_args = array())
190
+ {
191
+
192
+ $canonical = (array)$canonical;
193
+
194
+ $roles = array();
195
+
196
+ foreach(wp_roles()->roles as $role => $_) {
197
+ if( in_array(self::get_canonical_role_from_role($role), $canonical, true) ) {
198
+ $roles[] = $role;
199
+ }
200
+ }
201
+
202
+ if( empty($roles) ) {
203
+ return array();
204
+ }
205
+
206
+ return get_users(array_merge($additional_args, array('role__in' => $roles)));
207
+ }
208
+
209
+ /**
210
+ * Get a list of all of the capabilities that are unique to each role.
211
+ *
212
+ * @return array
213
+ */
214
+ public static function get_unique_capabilities()
215
+ {
216
+ return array(
217
+ 'administrator' => array(
218
+ 'activate_plugins',
219
+ 'create_users',
220
+ 'delete_plugins',
221
+ 'delete_themes',
222
+ 'delete_users',
223
+ 'edit_dashboard',
224
+ 'edit_files',
225
+ 'edit_plugins',
226
+ 'edit_theme_options',
227
+ 'edit_themes',
228
+ 'edit_users',
229
+ 'export',
230
+ 'import',
231
+ 'install_plugins',
232
+ 'install_themes',
233
+ 'level_8',
234
+ 'level_9',
235
+ 'level_10',
236
+ 'list_users',
237
+ 'manage_options',
238
+ 'promote_users',
239
+ 'remove_users',
240
+ 'switch_themes',
241
+ 'unfiltered_upload',
242
+ 'update_core',
243
+ 'update_plugins',
244
+ 'update_themes',
245
+ ),
246
+ 'editor' => array(
247
+ 'delete_others_pages',
248
+ 'delete_others_posts',
249
+ 'delete_pages',
250
+ 'delete_private_pages',
251
+ 'delete_private_posts',
252
+ 'delete_published_pages',
253
+ 'edit_others_pages',
254
+ 'edit_others_posts',
255
+ 'edit_pages',
256
+ 'edit_private_pages',
257
+ 'edit_private_posts',
258
+ 'edit_published_pages',
259
+ 'level_3',
260
+ 'level_4',
261
+ 'level_5',
262
+ 'level_6',
263
+ 'level_7',
264
+ 'manage_categories',
265
+ 'manage_links',
266
+ 'moderate_comments',
267
+ 'publish_pages',
268
+ 'read_private_pages',
269
+ 'read_private_posts',
270
+ 'unfiltered_html',
271
+ ),
272
+ 'author' => array(
273
+ 'delete_published_posts',
274
+ 'edit_published_posts',
275
+ 'level_2',
276
+ 'publish_posts',
277
+ 'upload_files',
278
+ ),
279
+ 'contributor' => array(
280
+ 'delete_posts',
281
+ 'edit_posts',
282
+ 'level_1',
283
+ ),
284
+ 'subscriber' => array(
285
+ 'level_0',
286
+ 'read',
287
+ ),
288
+ );
289
+ }
290
+
291
+ /**
292
+ * Get a list of all of the capabilities each default WordPress role has.
293
+ *
294
+ * @return array
295
+ */
296
+ public static function get_capabilities()
297
+ {
298
+ return array(
299
+ 'administrator' => array(
300
+ 'activate_plugins',
301
+ 'create_users',
302
+ 'delete_others_pages',
303
+ 'delete_others_posts',
304
+ 'delete_pages',
305
+ 'delete_plugins',
306
+ 'delete_posts',
307
+ 'delete_private_pages',
308
+ 'delete_private_posts',
309
+ 'delete_published_pages',
310
+ 'delete_published_posts',
311
+ 'delete_themes',
312
+ 'delete_users',
313
+ 'edit_dashboard',
314
+ 'edit_files',
315
+ 'edit_others_pages',
316
+ 'edit_others_posts',
317
+ 'edit_pages',
318
+ 'edit_plugins',
319
+ 'edit_posts',
320
+ 'edit_private_pages',
321
+ 'edit_private_posts',
322
+ 'edit_published_pages',
323
+ 'edit_published_posts',
324
+ 'edit_theme_options',
325
+ 'edit_themes',
326
+ 'edit_users',
327
+ 'export',
328
+ 'import',
329
+ 'install_plugins',
330
+ 'install_themes',
331
+ 'level_0',
332
+ 'level_1',
333
+ 'level_2',
334
+ 'level_3',
335
+ 'level_4',
336
+ 'level_5',
337
+ 'level_6',
338
+ 'level_7',
339
+ 'level_8',
340
+ 'level_9',
341
+ 'level_10',
342
+ 'list_users',
343
+ 'manage_categories',
344
+ 'manage_links',
345
+ 'manage_options',
346
+ 'moderate_comments',
347
+ 'promote_users',
348
+ 'publish_pages',
349
+ 'publish_posts',
350
+ 'read',
351
+ 'read_private_pages',
352
+ 'read_private_posts',
353
+ 'remove_users',
354
+ 'switch_themes',
355
+ 'unfiltered_html',
356
+ 'unfiltered_upload',
357
+ 'update_core',
358
+ 'update_plugins',
359
+ 'update_themes',
360
+ 'upload_files',
361
+ ),
362
+ 'editor' => array(
363
+ 'delete_others_pages',
364
+ 'delete_others_posts',
365
+ 'delete_pages',
366
+ 'delete_posts',
367
+ 'delete_private_pages',
368
+ 'delete_private_posts',
369
+ 'delete_published_pages',
370
+ 'delete_published_posts',
371
+ 'edit_others_pages',
372
+ 'edit_others_posts',
373
+ 'edit_pages',
374
+ 'edit_posts',
375
+ 'edit_private_pages',
376
+ 'edit_private_posts',
377
+ 'edit_published_pages',
378
+ 'edit_published_posts',
379
+ 'level_0',
380
+ 'level_1',
381
+ 'level_2',
382
+ 'level_3',
383
+ 'level_4',
384
+ 'level_5',
385
+ 'level_6',
386
+ 'level_7',
387
+ 'manage_categories',
388
+ 'manage_links',
389
+ 'moderate_comments',
390
+ 'publish_pages',
391
+ 'publish_posts',
392
+ 'read',
393
+ 'read_private_pages',
394
+ 'read_private_posts',
395
+ 'unfiltered_html',
396
+ 'upload_files',
397
+ ),
398
+ 'author' => array(
399
+ 'delete_posts',
400
+ 'delete_published_posts',
401
+ 'edit_posts',
402
+ 'edit_published_posts',
403
+ 'level_0',
404
+ 'level_1',
405
+ 'level_2',
406
+ 'publish_posts',
407
+ 'read',
408
+ 'upload_files',
409
+ ),
410
+ 'contributor' => array(
411
+ 'delete_posts',
412
+ 'edit_posts',
413
+ 'level_0',
414
+ 'level_1',
415
+ 'read',
416
+ ),
417
+ 'subscriber' => array(
418
+ 'level_0',
419
+ 'read',
420
+ ),
421
+ );
422
+ }
423
+ }
includes/tweaks/password-requirements/class-password-requirements-base.php ADDED
@@ -0,0 +1,371 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace WBCR\Titan\Tweaks;
4
+
5
+ /**
6
+ * Class \WBCR\Titan\Tweaks\Password_Requirements_Base
7
+ */
8
+ class Password_Requirements_Base {
9
+
10
+ /** @var array[] */
11
+ private static $requirements;
12
+
13
+ /**
14
+ * Get all registered password requirements.
15
+ *
16
+ * @return array
17
+ */
18
+ public static function get_registered()
19
+ {
20
+ if( null === self::$requirements ) {
21
+ self::$requirements = array();
22
+
23
+ /**
24
+ * Fires when password requirements should be registered.
25
+ */
26
+ do_action('titan_register_password_requirements');
27
+ }
28
+
29
+ return self::$requirements;
30
+ }
31
+
32
+ /**
33
+ * Register a password requirement.
34
+ *
35
+ * @param string $reason_code
36
+ * @param array $opts
37
+ */
38
+ public static function register($reason_code, $opts)
39
+ {
40
+ $merged = wp_parse_args($opts, array(
41
+ 'evaluate' => null,
42
+ 'validate' => null,
43
+ 'flag_check' => null,
44
+ 'reason' => null,
45
+ 'defaults' => null,
46
+ 'settings_config' => null, // Callable returning label, description, render & sanitize callbacks.
47
+ 'meta' => "_titan_password_evaluation_{$reason_code}",
48
+ 'evaluate_if_not_enabled' => false,
49
+ ));
50
+
51
+ if( (array_key_exists('validate', $opts) || array_key_exists('evaluate', $opts)) && (!is_callable($merged['validate']) || !is_callable($merged['evaluate'])) ) {
52
+ return;
53
+ }
54
+
55
+ if( array_key_exists('flag_check', $opts) && !is_callable($merged['flag_check']) ) {
56
+ return;
57
+ }
58
+
59
+ if( array_key_exists('defaults', $opts) ) {
60
+ if( !is_array($merged['defaults']) ) {
61
+ return;
62
+ }
63
+
64
+ if( !array_key_exists('settings_config', $opts) ) {
65
+ return;
66
+ }
67
+ }
68
+
69
+ if( array_key_exists('settings_config', $opts) && !is_callable($merged['settings_config']) ) {
70
+ return;
71
+ }
72
+
73
+ self::$requirements[$reason_code] = $merged;
74
+ }
75
+
76
+ /**
77
+ * Get a message indicating to the user why a password change is required.
78
+ *
79
+ * @param \WP_User $user
80
+ *
81
+ * @return string
82
+ */
83
+ public static function get_message_for_password_change_reason($user)
84
+ {
85
+
86
+ if( !$reason = self::password_change_required($user) ) {
87
+ return '';
88
+ }
89
+
90
+ $message = '';
91
+
92
+ $registered = self::get_registered();
93
+
94
+ if( isset($registered[$reason]) ) {
95
+ $settings = self::get_requirement_settings($reason);
96
+ $message = call_user_func($registered[$reason]['reason'], get_user_meta($user->ID, $registered[$reason]['meta'], true), $settings);
97
+ }
98
+
99
+ /**
100
+ * Retrieve a human readable description as to why a password change has been required for the current user.
101
+ *
102
+ * Modules MUST HTML escape their reason strings before returning them with this filter.
103
+ *
104
+ * @param string $message
105
+ * @param \WP_User $user
106
+ */
107
+ $message = apply_filters("titan_password_change_requirement_description_for_{$reason}", $message, $user);
108
+
109
+ if( $message ) {
110
+ return $message;
111
+ }
112
+
113
+ return esc_html__('A password change is required for your account.', 'titan-security');
114
+ }
115
+
116
+ /**
117
+ * Validate a user's password.
118
+ *
119
+ * @param \WP_User|\stdClass|int $user
120
+ * @param string $new_password
121
+ * @param array $args
122
+ *
123
+ * @return \WP_Error Error object with new errors.
124
+ */
125
+ public static function validate_password($user, $new_password, $args = array())
126
+ {
127
+
128
+ $args = wp_parse_args($args, array(
129
+ 'error' => new \WP_Error(),
130
+ 'context' => '',
131
+ ));
132
+
133
+ /** @var \WP_Error $error */
134
+ $error = $args['error'];
135
+ $user = $user instanceof \stdClass ? $user : self::get_user($user);
136
+
137
+ if( !$user ) {
138
+ $error->add('invalid_user', esc_html__('Invalid User', 'titan-security'));
139
+
140
+ return $error;
141
+ }
142
+
143
+ if( !empty($user->ID) && wp_check_password($new_password, get_userdata($user->ID)->user_pass, $user->ID) ) {
144
+ $message = wp_kses(__('<strong>ERROR</strong>: The password you have chosen appears to have been used before. You must choose a new password.', 'titan-security'), array('strong' => array()));
145
+ $error->add('pass', $message);
146
+
147
+ return $error;
148
+ }
149
+
150
+ require_once(WTITAN_PLUGIN_DIR . '/includes/tweaks/password-requirements/class-canonical-roles.php');
151
+
152
+ if( isset($args['role']) && $user instanceof \WP_User ) {
153
+ $canonical = \WBCR\Titan\Tweaks\Canonical_Roles::get_canonical_role_from_role_and_user($args['role'], $user);
154
+ } elseif( isset($args['role']) ) {
155
+ $canonical = \WBCR\Titan\Tweaks\Canonical_Roles::get_canonical_role_from_role($args['role']);
156
+ } elseif( empty($user->ID) || !is_numeric($user->ID) ) {
157
+ $canonical = \WBCR\Titan\Tweaks\Canonical_Roles::get_canonical_role_from_role(get_option('default_role', 'subscriber'));
158
+ } else {
159
+ $canonical = \WBCR\Titan\Tweaks\Canonical_Roles::get_user_role($user);
160
+ }
161
+
162
+ $args['canonical'] = $canonical;
163
+
164
+ /**
165
+ * Fires when modules should validate a password according to their rules.
166
+ *
167
+ * @param \WP_Error $error
168
+ * @param \WP_User|\stdClass $user
169
+ * @param string $new_password
170
+ * @param array $args
171
+ * @since 3.9.0
172
+ *
173
+ */
174
+ do_action('titan_validate_password', $error, $user, $new_password, $args);
175
+
176
+ return $error;
177
+ }
178
+
179
+ /**
180
+ * Flag that a password change is required for a user.
181
+ *
182
+ * @param \WP_User|int $user
183
+ * @param string $reason
184
+ */
185
+ public static function flag_password_change_required($user, $reason)
186
+ {
187
+ $user = self::get_user($user);
188
+
189
+ if( $user ) {
190
+ update_user_meta($user->ID, 'titan_password_change_required', $reason);
191
+ }
192
+ }
193
+
194
+ /**
195
+ * Check if a password change is required for the given user.
196
+ *
197
+ * @param \WP_User|int $user
198
+ *
199
+ * @return string|false Either the reason code a change is required, or false.
200
+ */
201
+ public static function password_change_required($user)
202
+ {
203
+ $user = self::get_user($user);
204
+
205
+ if( !$user ) {
206
+ return false;
207
+ }
208
+
209
+ $reason = get_user_meta($user->ID, 'titan_password_change_required', true);
210
+
211
+ if( !$reason ) {
212
+ return false;
213
+ }
214
+
215
+ $registered = self::get_registered();
216
+
217
+ if( isset($registered[$reason]) ) {
218
+ return self::is_requirement_enabled($reason) ? $reason : false;
219
+ }
220
+
221
+ if( !has_filter("titan_password_change_requirement_description_for_{$reason}") ) {
222
+ return false;
223
+ }
224
+
225
+ return $reason;
226
+ }
227
+
228
+ /**
229
+ * Globally clear all required password changes with a particular reason code.
230
+ *
231
+ * @param string $reason
232
+ */
233
+ public static function global_clear_required_password_change($reason)
234
+ {
235
+ delete_metadata('user', 0, 'titan_password_change_required', $reason, true);
236
+ }
237
+
238
+ /**
239
+ * Get the GMT time the user's password has last been changed.
240
+ *
241
+ * @param \WP_User|int $user
242
+ *
243
+ * @return int
244
+ */
245
+ public static function password_last_changed($user)
246
+ {
247
+
248
+ $user = self::get_user($user);
249
+
250
+ if( !$user ) {
251
+ return 0;
252
+ }
253
+
254
+ $changed = (int)get_user_meta($user->ID, 'titan_last_password_change', true);
255
+ $deprecated = (int)get_user_meta($user->ID, 'titan-password-updated', true);
256
+
257
+ if( $deprecated > $changed ) {
258
+ return $deprecated;
259
+ }
260
+
261
+ if( !$changed ) {
262
+ return strtotime($user->user_registered);
263
+ }
264
+
265
+ return $changed;
266
+ }
267
+
268
+ /**
269
+ * Is a password requirement enabled.
270
+ *
271
+ * @param string $requirement
272
+ *
273
+ * @return bool
274
+ */
275
+ public static function is_requirement_enabled($requirement)
276
+ {
277
+
278
+ $requirements = self::get_registered();
279
+
280
+ if( !isset($requirements[$requirement]) ) {
281
+ return false;
282
+ }
283
+
284
+ // If the requirement does not have any settings, than it is always enabled.
285
+ if( null === $requirements[$requirement]['settings_config'] ) {
286
+ return true;
287
+ }
288
+
289
+ //$enabled = ITSEC_Modules::get_setting('password-requirements', 'enabled_requirements');
290
+
291
+ //if( !empty($enabled[$requirement]) ) {
292
+ //return true;
293
+ //}
294
+
295
+ //return false;
296
+
297
+ return true;
298
+ }
299
+
300
+ /**
301
+ * Get requirement settings.
302
+ *
303
+ * @param string $requirement
304
+ *
305
+ * @return array|false
306
+ */
307
+ public static function get_requirement_settings($requirement)
308
+ {
309
+
310
+ $requirements = self::get_registered();
311
+
312
+ if( !isset($requirements[$requirement]) ) {
313
+ return false;
314
+ }
315
+
316
+ if( null === $requirements[$requirement]['settings_config'] ) {
317
+ return false;
318
+ }
319
+
320
+ //$all_settings = ITSEC_Modules::get_setting('password-requirements', 'requirement_settings');
321
+ $all_settings = array(
322
+ 'strength' => array(
323
+ 'role' => 'administrator',
324
+ ),
325
+ );;
326
+ $settings = isset($all_settings[$requirement]) ? $all_settings[$requirement] : array();
327
+
328
+ return wp_parse_args($settings, $requirements[$requirement]['defaults']);
329
+ }
330
+
331
+ /**
332
+ * Get a WordPress user object.
333
+ *
334
+ * @param int|string|\WP_User|bool $user Either the user ID ( must be an int ), the username, a WP_User object,
335
+ * or false to retrieve the currently logged-in user.
336
+ *
337
+ * @return \WP_User|false
338
+ */
339
+ public static function get_user($user = false)
340
+ {
341
+ if( $user instanceof \WP_User ) {
342
+ return $user;
343
+ }
344
+
345
+ if( false === $user ) {
346
+ $user = wp_get_current_user();
347
+ } elseif( is_int($user) ) {
348
+ $user = get_user_by('id', $user);
349
+ } elseif( is_string($user) ) {
350
+ $user = get_user_by('login', $user);
351
+ } elseif( is_object($user) && isset($user->ID) ) {
352
+ $user = get_user_by('id', $user->ID);
353
+ } else {
354
+ if( is_object($user) ) {
355
+ $type = 'object(' . get_class($user) . ')';
356
+ } else {
357
+ $type = gettype($user);
358
+ }
359
+
360
+ error_log('self::get_user() called with an invalid $user argument. Received $user variable of type: ' . $type);
361
+
362
+ wp_die('Internal Server Error');
363
+ }
364
+
365
+ if( $user instanceof \WP_User ) {
366
+ return $user;
367
+ }
368
+
369
+ return false;
370
+ }
371
+ }
includes/tweaks/password-requirements/class-password-requirements.php ADDED
@@ -0,0 +1,509 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace WBCR\Titan\Tweaks;
4
+
5
+ /**
6
+ * Class \WBCR\Titan\Tweaks\Password_Requirements
7
+ */
8
+ class Password_Requirements {
9
+
10
+ const META_KEY = '_titan_password_requirements';
11
+
12
+ public function run()
13
+ {
14
+
15
+ add_action('user_profile_update_errors', array($this, 'forward_profile_pass_update'), 0, 3);
16
+ add_action('validate_password_reset', array($this, 'forward_reset_pass'), 10, 2);
17
+
18
+ add_action('profile_update', array($this, 'handle_update_user'), 10, 2);
19
+ add_action('password_reset', array($this, 'handle_password_reset'), 10, 2);
20
+ add_filter('wp_authenticate_user', array($this, 'check_password_on_login'), 999, 2);
21
+
22
+ add_action('add_user_role', array($this, 'handle_role_change'));
23
+ add_action('set_user_role', array($this, 'handle_role_change'));
24
+ add_action('remove_user_role', array($this, 'handle_role_change'));
25
+
26
+ add_action('titan_validate_password', array($this, 'validate_password'), 10, 4);
27
+
28
+ add_action('wp_login', array($this, 'flag_check'), 9, 2);
29
+
30
+ add_action('titan_login_interstitial_init', array($this, 'register_interstitial'));
31
+ }
32
+
33
+ /**
34
+ * When a user's password is updated, or a new user created, verify that the new password is valid.
35
+ *
36
+ * @param \WP_Error $errors
37
+ * @param bool $update
38
+ * @param \WP_User|\stdClass $user
39
+ */
40
+ public function forward_profile_pass_update($errors, $update, $user)
41
+ {
42
+
43
+ if( $errors->get_error_message('pass') ) {
44
+ return;
45
+ }
46
+
47
+ if( isset($user->user_pass) ) {
48
+ $this->handle_profile_update_password($errors, $update, $user);
49
+ } elseif( $update && isset($user->role) ) {
50
+ $this->handle_profile_update_role($errors, $user);
51
+ }
52
+ }
53
+
54
+ /**
55
+ * Handle the password being updated for a user.
56
+ *
57
+ * @param \WP_Error $errors
58
+ * @param bool $update
59
+ * @param \WP_User|\stdClass $user
60
+ */
61
+ private function handle_profile_update_password($errors, $update, $user)
62
+ {
63
+ if( !$update ) {
64
+ $context = 'admin-user-create';
65
+ } elseif( isset($user->ID) && $user->ID === get_current_user_id() ) {
66
+ $context = 'profile-update';
67
+ } else {
68
+ $context = 'admin-profile-update';
69
+ }
70
+
71
+ $args = array(
72
+ 'error' => $errors,
73
+ 'context' => $context
74
+ );
75
+
76
+ if( isset($user->role) ) {
77
+ $args['role'] = $user->role;
78
+ }
79
+
80
+ \WBCR\Titan\Tweaks\Password_Requirements_Base::validate_password($user, $user->user_pass, $args);
81
+ }
82
+
83
+ /**
84
+ * Handle the user's role being updated.
85
+ *
86
+ * @param \WP_Error $errors
87
+ * @param \WP_User|\stdClass $user
88
+ */
89
+ private function handle_profile_update_role($errors, $user)
90
+ {
91
+ $settings = array(
92
+ 'strength' => array(
93
+ 'role' => \WBCR\Titan\Plugin::app()->getPopulateOption('strong_password_min_role', 'administrator'),
94
+ ),
95
+ );
96
+
97
+ foreach(\WBCR\Titan\Tweaks\Password_Requirements_Base::get_registered() as $code => $requirement) {
98
+
99
+ if( !$requirement['validate'] || !\WBCR\Titan\Tweaks\Password_Requirements_Base::is_requirement_enabled($code) ) {
100
+ continue;
101
+ }
102
+
103
+ $evaluation = get_user_meta($user->ID, $requirement['meta'], true);
104
+
105
+ if( '' === $evaluation ) {
106
+ continue;
107
+ }
108
+
109
+ require_once(WTITAN_PLUGIN_DIR . '/includes/tweaks/password-requirements/class-canonical-roles.php');
110
+
111
+ $args = array(
112
+ 'role' => $user->role,
113
+ 'canonical' => \WBCR\Titan\Tweaks\Canonical_Roles::get_canonical_role_from_role_and_user($user->role, $user),
114
+ );
115
+
116
+ $validated = call_user_func($requirement['validate'], $evaluation, $user, $settings[$code], $args);
117
+
118
+ if( true === $validated ) {
119
+ continue;
120
+ }
121
+
122
+ $message = $validated ? $validated : esc_html__("The provided password does not meet this site's requirements.", 'titan-security');
123
+ $errors->add('pass', $message);
124
+ }
125
+ }
126
+
127
+ /**
128
+ * When a user attempts to reset their password, verify that the new password is valid.
129
+ *
130
+ * @param \WP_Error $errors
131
+ * @param \WP_User $user
132
+ */
133
+ public function forward_reset_pass($errors, $user)
134
+ {
135
+
136
+ if( !isset($_POST['pass1']) || is_wp_error($user) ) {
137
+ // The validate_password_reset action fires when first rendering the reset page and when handling the form
138
+ // submissions. Since the pass1 data is missing, this must be the initial page render. So, we don't need to
139
+ // do anything yet.
140
+ return;
141
+ }
142
+
143
+ \WBCR\Titan\Tweaks\Password_Requirements_Base::validate_password($user, $_POST['pass1'], array(
144
+ 'error' => $errors,
145
+ 'context' => 'reset-password',
146
+ ));
147
+ }
148
+
149
+ /**
150
+ * Whenever a user object is updated, set when their password was last updated.
151
+ *
152
+ * @param int $user_id
153
+ * @param object $old_user_data
154
+ */
155
+ public function handle_update_user($user_id, $old_user_data)
156
+ {
157
+
158
+ $user = get_userdata($user_id);
159
+
160
+ if( $user->user_pass === $old_user_data->user_pass ) {
161
+ return;
162
+ }
163
+
164
+ $this->handle_password_updated($user);
165
+ }
166
+
167
+ /**
168
+ * When a user resets their password, update the last change time.
169
+ *
170
+ * For some unknown reason, the password reset routine uses {@see wp_set_password()} instead of {@see wp_update_user()}.
171
+ *
172
+ * @param \WP_User $user
173
+ * @param string $new_password
174
+ */
175
+ public function handle_password_reset($user, $new_password)
176
+ {
177
+ $this->handle_password_updated($user);
178
+ $this->handle_plain_text_password_available($user, $new_password);
179
+ }
180
+
181
+ /**
182
+ * When a user logs in, if their password hasn't been validated yet,
183
+ * validate it.
184
+ *
185
+ * @param \WP_User $user
186
+ * @param string $password
187
+ *
188
+ * @return \WP_User
189
+ */
190
+ public function check_password_on_login($user, $password)
191
+ {
192
+
193
+ if( !$user instanceof \WP_User ) {
194
+ return $user;
195
+ }
196
+
197
+ if( !wp_check_password($password, $user->user_pass, $user->ID) ) {
198
+ return $user;
199
+ }
200
+
201
+ $this->handle_plain_text_password_available($user, $password);
202
+
203
+ return $user;
204
+ }
205
+
206
+ /**
207
+ * When a password is updated, set the last updated time and delete any pending required change.
208
+ *
209
+ * @param \WP_User $user
210
+ */
211
+ protected function handle_password_updated($user)
212
+ {
213
+ delete_user_meta($user->ID, 'titan_password_change_required');
214
+ update_user_meta($user->ID, 'titan_last_password_change', current_time('timestamp', true));
215
+ }
216
+
217
+ /**
218
+ * When a plain text password is available, we perform any evaluations that have not yet been performed for this password.
219
+ *
220
+ * @param \WP_User $user
221
+ * @param string $password
222
+ */
223
+ protected function handle_plain_text_password_available($user, $password)
224
+ {
225
+
226
+ $config = wp_parse_args(get_user_meta($user->ID, self::META_KEY, true), array(
227
+ 'evaluation_times' => array(),
228
+ ));
229
+
230
+ $last_updated = \WBCR\Titan\Tweaks\Password_Requirements_Base::password_last_changed($user);
231
+
232
+ $settings = array(
233
+ 'strength' => array(
234
+ 'role' => \WBCR\Titan\Plugin::app()->getPopulateOption('strong_password_min_role', 'administrator'),
235
+ ),
236
+ );
237
+
238
+ foreach(\WBCR\Titan\Tweaks\Password_Requirements_Base::get_registered() as $code => $requirement) {
239
+
240
+ if( !$requirement['evaluate'] ) {
241
+ continue;
242
+ }
243
+
244
+ if( !$requirement['evaluate_if_not_enabled'] && !\WBCR\Titan\Tweaks\Password_Requirements_Base::is_requirement_enabled($code) ) {
245
+ continue;
246
+ }
247
+
248
+ if( isset($config['evaluation_times'][$code]) && $config['evaluation_times'][$code] >= $last_updated ) {
249
+ continue;
250
+ }
251
+
252
+ $evaluation = call_user_func($requirement['evaluate'], $password, $user);
253
+
254
+ if( is_wp_error($evaluation) ) {
255
+ continue;
256
+ }
257
+
258
+ $config['evaluation_times'][$code] = current_time('timestamp', true);
259
+ update_user_meta($user->ID, $requirement['meta'], $evaluation);
260
+
261
+ if( !\WBCR\Titan\Tweaks\Password_Requirements_Base::is_requirement_enabled($code) ) {
262
+ continue;
263
+ }
264
+
265
+ $validated = call_user_func($requirement['validate'], $evaluation, $user, $settings[$code], array());
266
+
267
+ if( true === $validated ) {
268
+ continue;
269
+ }
270
+
271
+ \WBCR\Titan\Tweaks\Password_Requirements_Base::flag_password_change_required($user, $code);
272
+ }
273
+
274
+ update_user_meta($user->ID, self::META_KEY, $config);
275
+ }
276
+
277
+ /**
278
+ * Validate password.
279
+ *
280
+ * @param \WP_Error $error
281
+ * @param \WP_User|\stdClass $user
282
+ * @param string $new_password
283
+ * @param array $args
284
+ */
285
+ public function validate_password($error, $user, $new_password, $args)
286
+ {
287
+ $settings = array(
288
+ 'strength' => array(
289
+ 'role' => \WBCR\Titan\Plugin::app()->getPopulateOption('strong_password_min_role', 'administrator'),
290
+ ),
291
+ );
292
+
293
+ foreach(\WBCR\Titan\Tweaks\Password_Requirements_Base::get_registered() as $code => $requirement) {
294
+
295
+ if( !$requirement['evaluate'] || !\WBCR\Titan\Tweaks\Password_Requirements_Base::is_requirement_enabled($code) ) {
296
+ continue;
297
+ }
298
+
299
+ $evaluation = call_user_func($requirement['evaluate'], $new_password, $user);
300
+
301
+ if( is_wp_error($evaluation) ) {
302
+ continue;
303
+ }
304
+
305
+ $validated = call_user_func($requirement['validate'], $evaluation, $user, $settings[$code], $args);
306
+
307
+ if( true === $validated ) {
308
+ continue;
309
+ }
310
+
311
+ // The default error message is a safeguard that should never occur.
312
+ $message = $validated ? $validated : esc_html__("The provided password does not meet this site's requirements.", 'titan-security');
313
+
314
+ switch( $args['context'] ) {
315
+ case 'admin-user-create':
316
+ $message .= ' ' . __('The user has not been created.', 'titan-security');
317
+ break;
318
+ case 'admin-profile-update':
319
+ $message .= ' ' . __('The user changes have not been saved.', 'titan-security');
320
+ break;
321
+ case 'profile-update':
322
+ $message .= ' ' . __('Your profile has not been updated.', 'titan-security');
323
+ break;
324
+ case 'reset-password':
325
+ $message .= ' ' . __('The password has not been updated.', 'titan-security');
326
+ break;
327
+ }
328
+
329
+ $error->add('pass', $message);
330
+ }
331
+ }
332
+
333
+ /**
334
+ * When a user logs in, run any flag checks to see if a password change should be forced.
335
+ *
336
+ * @param string $username
337
+ * @param \WP_User|null $user
338
+ */
339
+ public function flag_check($username, $user = null)
340
+ {
341
+
342
+ if( !$user && is_user_logged_in() ) {
343
+ $user = wp_get_current_user();
344
+ }
345
+
346
+ if( !$user instanceof \WP_User || !$user->exists() ) {
347
+ return;
348
+ }
349
+
350
+ foreach(\WBCR\Titan\Tweaks\Password_Requirements_Base::get_registered() as $code => $requirement) {
351
+ if( !\WBCR\Titan\Tweaks\Password_Requirements_Base::is_requirement_enabled($code) ) {
352
+ continue;
353
+ }
354
+
355
+ $settings = \WBCR\Titan\Tweaks\Password_Requirements_Base::get_requirement_settings($code);
356
+
357
+ if( $requirement['flag_check'] && call_user_func($requirement['flag_check'], $user, $settings) ) {
358
+ \WBCR\Titan\Tweaks\Password_Requirements_Base::flag_password_change_required($user, $code);
359
+
360
+ return;
361
+ }
362
+ }
363
+ }
364
+
365
+ /**
366
+ * Is a given requirement enabled.
367
+ *
368
+ * @param string $requirement
369
+ *
370
+ * @return bool
371
+ */
372
+ protected function is_requirement_enabled($requirement)
373
+ {
374
+
375
+ $requirements = \WBCR\Titan\Tweaks\Password_Requirements_Base::get_registered();
376
+
377
+ if( !isset($requirements[$requirement]) ) {
378
+ return false;
379
+ }
380
+
381
+ // If the requirement does not have any settings, than it is always enabled.
382
+ if( null === $requirements[$requirement]['settings_config'] ) {
383
+ return true;
384
+ }
385
+
386
+ if( !\WBCR\Titan\Plugin::app()->getPopulateOption('strong_password') ) {
387
+ return false;
388
+ }
389
+
390
+ return true;
391
+ }
392
+
393
+ /**
394
+ * When a user's role changes, clear all the evaluation times as evaluat
395
+ *
396
+ * @param int $user_id
397
+ */
398
+ public function handle_role_change($user_id)
399
+ {
400
+
401
+ $config = get_user_meta($user_id, self::META_KEY, true);
402
+
403
+ if( !$config || !is_array($config) ) {
404
+ return;
405
+ }
406
+
407
+ $config['evaluation_times'] = array();
408
+
409
+ update_user_meta($user_id, self::META_KEY, $config);
410
+ }
411
+
412
+ /**
413
+ * Register the password change interstitial.
414
+ *
415
+ * @param \WBCR\Titan\Tweaks\Login_Interstitial $lib
416
+ */
417
+ public function register_interstitial($lib)
418
+ {
419
+ $lib->register('update-password', array($this, 'render_interstitial'), array(
420
+ 'show_to_user' => array('\WBCR\Titan\Tweaks\Password_Requirements_Base', 'password_change_required'),
421
+ 'info_message' => array(
422
+ '\WBCR\Titan\Tweaks\Password_Requirements_Base',
423
+ 'get_message_for_password_change_reason'
424
+ ),
425
+ 'submit' => array($this, 'submit'),
426
+ ));
427
+ }
428
+
429
+ /**
430
+ * Render the interstitial.
431
+ *
432
+ * @param \WP_User $user
433
+ */
434
+ public function render_interstitial($user)
435
+ {
436
+ wp_enqueue_script('user-profile');
437
+
438
+ do_action('titan_password_requirements_change_form', $user);
439
+ ?>
440
+
441
+ <div class="user-pass1-wrap">
442
+ <p><label for="pass1"><?php _e('New Password', 'titan-security'); ?></label></p>
443
+ </div>
444
+
445
+ <div class="wp-pwd">
446
+ <span class="password-input-wrapper">
447
+ <input type="password" data-reveal="1"
448
+ data-pw="<?php echo esc_attr(wp_generate_password(16)); ?>" name="pass1" id="pass1"
449
+ class="input" size="20" value="" autocomplete="off" aria-describedby="pass-strength-result"/>
450
+ </span>
451
+ <div id="pass-strength-result" class="hide-if-no-js" aria-live="polite"><?php _e('Strength indicator', 'titan-security'); ?></div>
452
+ <div class="pw-weak">
453
+ <label>
454
+ <input type="checkbox" name="pw_weak" class="pw-checkbox"/>
455
+ <?php _e('Confirm use of weak password'); ?>
456
+ </label>
457
+ </div>
458
+ </div>
459
+
460
+ <p class="user-pass2-wrap">
461
+ <label for="pass2"><?php _e('Confirm new password') ?></label><br/>
462
+ <input type="password" name="pass2" id="pass2" class="input" size="20" value="" autocomplete="off"/>
463
+ </p>
464
+
465
+ <p class="description indicator-hint"><?php echo wp_get_password_hint(); ?></p>
466
+ <br class="clear"/>
467
+
468
+ <p class="submit">
469
+ <input type="submit" name="wp-submit" id="wp-submit" class="button button-primary button-large" value="<?php esc_attr_e('Update Password', 'titan-security'); ?>"/>
470
+ </p>
471
+
472
+ <?php
473
+ }
474
+
475
+ /**
476
+ * Handle the request to update the user's password.
477
+ *
478
+ * @param \WP_User $user
479
+ * @param array $data POSTed data.
480
+ *
481
+ * @return \WP_Error|null
482
+ */
483
+ public function submit($user, $data)
484
+ {
485
+
486
+ if( empty($data['pass1']) ) {
487
+ return new \WP_Error('titan-password-requirements-empty-password', __('Please enter your new password.', 'titan-security'));
488
+ }
489
+
490
+ $error = \WBCR\Titan\Tweaks\Password_Requirements_Base::validate_password($user, $data['pass1'], array(
491
+ 'context' => 'interstitial',
492
+ ));
493
+
494
+ if( $error->get_error_message() ) {
495
+ return $error;
496
+ }
497
+
498
+ $error = wp_update_user(array(
499
+ 'ID' => $user->ID,
500
+ 'user_pass' => $data['pass1']
501
+ ));
502
+
503
+ if( is_wp_error($error) ) {
504
+ return $error;
505
+ }
506
+
507
+ return null;
508
+ }
509
+ }
includes/tweaks/password-requirements/class-strong-passwords.php ADDED
@@ -0,0 +1,260 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace WBCR\Titan\Tweaks;
4
+
5
+ final class Strong_Passwords {
6
+
7
+ const STRENGTH_KEY = 'titan-password-strength';
8
+
9
+ public function __construct()
10
+ {
11
+
12
+ add_action('titan_register_password_requirements', array($this, 'register_requirements'));
13
+
14
+ add_action('admin_enqueue_scripts', array($this, 'add_scripts'));
15
+ add_action('resetpass_form', array($this, 'add_scripts_to_wp_login'));
16
+ add_action('titan_password_requirements_change_form', array($this, 'add_scripts_to_wp_login'));
17
+ }
18
+
19
+ /**
20
+ * Register the Strong Passwords requirement.
21
+ */
22
+ public function register_requirements()
23
+ {
24
+ Password_Requirements_Base::register('strength', array(
25
+ 'evaluate' => array($this, 'evaluate'),
26
+ 'validate' => array($this, 'validate'),
27
+ 'reason' => array($this, 'reason'),
28
+ 'meta' => self::STRENGTH_KEY,
29
+ 'evaluate_if_not_enabled' => true,
30
+ 'defaults' => array('role' => 'administrator'),
31
+ 'settings_config' => array($this, 'get_settings_config'),
32
+ ));
33
+ }
34
+
35
+ /**
36
+ * Enqueue script to hide the acknowledge weak password checkbox.
37
+ *
38
+ * @return void
39
+ */
40
+ public function add_scripts()
41
+ {
42
+
43
+ global $pagenow;
44
+
45
+ if( 'profile.php' !== $pagenow ) {
46
+ return;
47
+ }
48
+
49
+ if( !Password_Requirements_Base::is_requirement_enabled('strength') ) {
50
+ return;
51
+ }
52
+
53
+ $settings = Password_Requirements_Base::get_requirement_settings('strength');
54
+ $role = isset($settings['role']) ? $settings['role'] : 'administrator';
55
+
56
+ require_once(WTITAN_PLUGIN_DIR . '/includes/tweaks/password-requirements/class-canonical-roles.php');
57
+
58
+ if( Canonical_Roles::is_user_at_least($role) ) {
59
+ wp_enqueue_script('titan_strong_passwords', WTITAN_PLUGIN_URL . '/includes/tweaks/password-requirements/assets/js/script.js', array('jquery'), \WBCR\Titan\Plugin::app()->getPluginVersion());
60
+ }
61
+ }
62
+
63
+ /**
64
+ * On the reset password and login interstitial form, render the Strong Passwords JS to hide the acknowledge weak password checkbox.
65
+ *
66
+ * We have to do this in these late actions so we have access to the correct user data.
67
+ *
68
+ * @param \WP_User $user
69
+ */
70
+ public function add_scripts_to_wp_login($user)
71
+ {
72
+
73
+ if( !Password_Requirements_Base::is_requirement_enabled('strength') ) {
74
+ return;
75
+ }
76
+
77
+ $settings = Password_Requirements_Base::get_requirement_settings('strength');
78
+ $role = isset($settings['role']) ? $settings['role'] : 'administrator';
79
+
80
+ require_once(WTITAN_PLUGIN_DIR . '/includes/tweaks/password-requirements/class-canonical-roles.php');
81
+
82
+ if( Canonical_Roles::is_user_at_least($role, $user) ) {
83
+ wp_enqueue_script('titan_strong_passwords', WTITAN_PLUGIN_URL . '/includes/tweaks/password-requirements/assets/js/script.js', array('jquery'), \WBCR\Titan\Plugin::app()->getPluginVersion());
84
+ }
85
+ }
86
+
87
+ /**
88
+ * Provide the reason string displayed to users on the change password form.
89
+ *
90
+ * @param $evaluation
91
+ *
92
+ * @return string
93
+ */
94
+ public function reason($evaluation)
95
+ {
96
+ return esc_html__('Due to site rules, a strong password is required for your account. Please choose a new password that rates as strong on the meter.', 'titan-security');
97
+ }
98
+
99
+ /**
100
+ * Evaluate the strength of a password.
101
+ *
102
+ * @param string $password
103
+ * @param \WP_User $user
104
+ *
105
+ * @return int
106
+ */
107
+ public function evaluate($password, $user)
108
+ {
109
+ return $this->get_password_strength($user, $password);
110
+ }
111
+
112
+ /**
113
+ * Validate whether a password strength is acceptable for a given user.
114
+ *
115
+ * @param int $strength
116
+ * @param \WP_User|\stdClass $user
117
+ * @param array $settings
118
+ * @param array $args
119
+ *
120
+ * @return bool
121
+ */
122
+ public function validate($strength, $user, $settings, $args)
123
+ {
124
+
125
+ if( (int)$strength === 4 ) {
126
+ return true;
127
+ }
128
+
129
+ require_once(WTITAN_PLUGIN_DIR . '/includes/tweaks/password-requirements/class-canonical-roles.php');
130
+
131
+ $role = isset($args['canonical']) ? $args['canonical'] : Canonical_Roles::get_user_role($user);
132
+
133
+ if( !Canonical_Roles::is_canonical_role_at_least($settings['role'], $role) ) {
134
+ return true;
135
+ }
136
+
137
+ return $this->make_error_message();
138
+ }
139
+
140
+ public function get_settings_config()
141
+ {
142
+ return array(
143
+ 'label' => esc_html__('Strong Passwords', 'titan-security'),
144
+ 'description' => esc_html__('Force users to use strong passwords as rated by the WordPress password meter.', 'titan-security'),
145
+ 'render' => array($this, 'render_settings'),
146
+ 'sanitize' => array($this, 'sanitize_settings'),
147
+ );
148
+ }
149
+
150
+ /**
151
+ * Render the Settings Page.
152
+ *
153
+ * @param \ITSEC_Form $form
154
+ */
155
+ public function render_settings($form)
156
+ {
157
+
158
+ $href = 'http://codex.wordpress.org/Roles_and_Capabilities';
159
+ $link = '<a href="' . $href . '" target="_blank" rel="noopener noreferrer">' . $href . '</a>';
160
+ ?>
161
+ <tr>
162
+ <th scope="row">
163
+ <label for="titan-password-requirements-requirement_settings-strength-role">
164
+ <?php esc_html_e('Minimum Role', 'titan-security'); ?>
165
+ </label>
166
+ </th>
167
+ <td>
168
+ <?php $form->add_canonical_roles('role'); ?>
169
+ <br/>
170
+ <label for="titan-password-requirements-requirement_settings-strength-role"><?php _e('Minimum role at which a user must choose a strong password.', 'titan-security'); ?></label>
171
+ <p class="description"><?php printf(__('For more information on WordPress roles and capabilities please see %s.', 'titan-security'), $link); ?></p>
172
+ <p class="warningtext description"><?php _e('Warning: If your site invites public registrations setting the role too low may annoy your members.', 'titan-security'); ?></p>
173
+ </td>
174
+ </tr>
175
+ <?php
176
+ }
177
+
178
+ /**
179
+ * Get a list of the sanitizer rules to apply.
180
+ *
181
+ * @param array $settings
182
+ *
183
+ * @return array
184
+ */
185
+ public function sanitize_settings($settings)
186
+ {
187
+ return array(
188
+ array('string', 'role', esc_html__('Minimum Role for Strong Passwords', 'titan-security')),
189
+ array('canonical-roles', 'role', esc_html__('Minimum Role for Strong Passwords', 'titan-security')),
190
+ );
191
+ }
192
+
193
+ /**
194
+ * Get the strong password error message according to the given context.
195
+ *
196
+ * @return string
197
+ */
198
+ private function make_error_message()
199
+ {
200
+ $message = __('<strong>Error</strong>: Due to site rules, a strong password is required. Please choose a new password that rates as <strong>Strong</strong> on the meter.', 'titan-security');
201
+
202
+ return wp_kses($message, array('strong' => array()));
203
+ }
204
+
205
+ /**
206
+ * Calculate the strength of a password.
207
+ *
208
+ * @param \WP_User $user
209
+ * @param string $password
210
+ *
211
+ * @return int
212
+ */
213
+ private function get_password_strength($user, $password)
214
+ {
215
+
216
+ $penalty_strings = array(
217
+ get_site_option('admin_email')
218
+ );
219
+ $user_properties = array(
220
+ 'user_login',
221
+ 'first_name',
222
+ 'last_name',
223
+ 'nickname',
224
+ 'display_name',
225
+ 'user_email',
226
+ 'user_url',
227
+ 'description'
228
+ );
229
+
230
+ foreach($user_properties as $user_property) {
231
+ if( isset($user->$user_property) ) {
232
+ $penalty_strings[] = $user->$user_property;
233
+ }
234
+ }
235
+
236
+ $results = self::get_password_strength_results($password, $penalty_strings);
237
+
238
+ return $results->score;
239
+ }
240
+
241
+ /**
242
+ * Evaluate a password's strength.
243
+ *
244
+ * @param string $password
245
+ * @param array $penalty_strings Additional strings that if found within the password, will decrease the strength.
246
+ *
247
+ * @return \ITSEC_Zxcvbn_Results
248
+ */
249
+ public static function get_password_strength_results($password, $penalty_strings = array())
250
+ {
251
+ if( !isset($GLOBALS['titan_zxcvbn']) ) {
252
+ require_once(WTITAN_PLUGIN_DIR . '/includes/tweaks/password-requirements/libs/zxcvbn-php/zxcvbn.php');
253
+ $GLOBALS['titan_zxcvbn'] = new \ITSEC_Zxcvbn();
254
+ }
255
+
256
+ return $GLOBALS['titan_zxcvbn']->test_password($password, $penalty_strings);
257
+ }
258
+ }
259
+
260
+ new Strong_Passwords();
includes/tweaks/password-requirements/libs/zxcvbn-php/index.php ADDED
@@ -0,0 +1 @@
 
1
+ <?php // Silence is golden.
includes/tweaks/password-requirements/libs/zxcvbn-php/matcher.php ADDED
@@ -0,0 +1,103 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ class ITSEC_Zxcvbn_Matcher {
3
+
4
+ private $penalty_strings = array();
5
+
6
+ /**
7
+ * @var array Registered Matchers, Key is class name, value is file to require_once
8
+ */
9
+ private $matchers;
10
+
11
+ public function __construct() {
12
+ $this->matchers = array(
13
+ 'ITSEC_Zxcvbn_Dictionary_Match' => dirname( __FILE__ ) . '/matchers/dictionary.php',
14
+ 'ITSEC_Zxcvbn_Dictionary_Reverse_Match' => dirname( __FILE__ ) . '/matchers/dictionary-reverse.php',
15
+ 'ITSEC_Zxcvbn_Dictionary_L33t_Match' => dirname( __FILE__ ) . '/matchers/dictionary-l33t.php',
16
+ 'ITSEC_Zxcvbn_Spatial_Match' => dirname( __FILE__ ) . '/matchers/spatial.php',
17
+ 'ITSEC_Zxcvbn_Repeat_Match' => dirname( __FILE__ ) . '/matchers/repeat.php',
18
+ 'ITSEC_Zxcvbn_Sequence_Match' => dirname( __FILE__ ) . '/matchers/sequence.php',
19
+ 'ITSEC_Zxcvbn_Regex_Match' => dirname( __FILE__ ) . '/matchers/regex.php',
20
+ 'ITSEC_Zxcvbn_Date_Match' => dirname( __FILE__ ) . '/matchers/date.php',
21
+ 'ITSEC_Zxcvbn_Bruteforce_Match' => dirname( __FILE__ ) . '/matchers/bruteforce.php',
22
+ );
23
+ foreach ( $this->matchers as $file ) {
24
+ require_once( $file );
25
+ }
26
+ }
27
+
28
+ public function omnimatch( $password ) {
29
+ $matches = array();
30
+ foreach ( $this->matchers as $class => $file ) {
31
+ $matched = call_user_func( array( $class, 'match'), $password, $this->penalty_strings );
32
+ if ( ! empty( $matched ) && is_array( $matched ) ) {
33
+ $matches = array_merge( $matches, $matched );
34
+ }
35
+ }
36
+ return $matches;
37
+ }
38
+
39
+ public function set_user_input_dictionary( $list ) {
40
+ // @todo process this properly
41
+ $this->penalty_strings = $list;
42
+ }
43
+
44
+ }
45
+
46
+ abstract class ITSEC_Zxcvbn_Match {
47
+ protected $guesses;
48
+
49
+ protected static $min_year_space = 20;
50
+ /**
51
+ * @todo make this dynamic? Why isn't it?
52
+ */
53
+ protected static $reference_year = 2016;
54
+
55
+ /**
56
+ * Finds matches in the password.
57
+ * @param string $password Password to check for match
58
+ * @param array $penalty_strings Strings that should be penalized if in the password. This should be things like the username, first and last name, etc.
59
+ *
60
+ * @return ITSEC_Zxcvbn_Match[] Array of Match objects
61
+ */
62
+ public static function match($password, array $penalty_strings = array()) {}
63
+
64
+ protected function is_digit( $ord ) {
65
+ return $ord >= 0x30 && $ord <= 0x39;
66
+ }
67
+
68
+ protected function is_upper( $ord ) {
69
+ return $ord >= 0x41 && $ord <= 0x5a;
70
+ }
71
+
72
+ protected function is_lower( $ord ) {
73
+ return $ord >= 0x61 && $ord <= 0x7a;
74
+ }
75
+
76
+ protected function is_symbol( $ord ) {
77
+ return $ord <= 0x7f;
78
+ }
79
+
80
+ /**
81
+ * http://blog.plover.com/math/choose.html
82
+ *
83
+ * @param $n int
84
+ * @param $k int
85
+ *
86
+ * @return int
87
+ */
88
+ protected function binomial_coefficient( $n, $k ) {
89
+ if ( $k > $n ) {
90
+ return 0;
91
+ }
92
+ if ( 0 === $k ) {
93
+ return 1;
94
+ }
95
+ $r = 1;
96
+ for ( $d = 1; $d <= $k; $d++ ) {
97
+ $r *= $n--;
98
+ $r /= $d;
99
+ }
100
+ return $r;
101
+ }
102
+
103
+ }
includes/tweaks/password-requirements/libs/zxcvbn-php/matchers/adjacency_graphs.json ADDED
@@ -0,0 +1 @@
 
1
+ {"qwerty":{"0":["9(",null,null,"-_","pP","oO"],"1":["`~",null,null,"2@","qQ",null],"2":["1!",null,null,"3#","wW","qQ"],"3":["2@",null,null,"4$","eE","wW"],"4":["3#",null,null,"5%","rR","eE"],"5":["4$",null,null,"6^","tT","rR"],"6":["5%",null,null,"7&","yY","tT"],"7":["6^",null,null,"8*","uU","yY"],"8":["7&",null,null,"9(","iI","uU"],"9":["8*",null,null,"0)","oO","iI"],"!":["`~",null,null,"2@","qQ",null],"\"":[";:","[{","]}",null,null,"/?"],"#":["2@",null,null,"4$","eE","wW"],"$":["3#",null,null,"5%","rR","eE"],"%":["4$",null,null,"6^","tT","rR"],"&":["6^",null,null,"8*","uU","yY"],"'":[";:","[{","]}",null,null,"/?"],"(":["8*",null,null,"0)","oO","iI"],")":["9(",null,null,"-_","pP","oO"],"*":["7&",null,null,"9(","iI","uU"],"+":["-_",null,null,null,"]}","[{"],",":["mM","kK","lL",".>",null,null],"-":["0)",null,null,"=+","[{","pP"],".":[",<","lL",";:","/?",null,null],"/":[".>",";:","'\"",null,null,null],":":["lL","pP","[{","'\"","/?",".>"],";":["lL","pP","[{","'\"","/?",".>"],"<":["mM","kK","lL",".>",null,null],"=":["-_",null,null,null,"]}","[{"],">":[",<","lL",";:","/?",null,null],"?":[".>",";:","'\"",null,null,null],"@":["1!",null,null,"3#","wW","qQ"],"A":[null,"qQ","wW","sS","zZ",null],"B":["vV","gG","hH","nN",null,null],"C":["xX","dD","fF","vV",null,null],"D":["sS","eE","rR","fF","cC","xX"],"E":["wW","3#","4$","rR","dD","sS"],"F":["dD","rR","tT","gG","vV","cC"],"G":["fF","tT","yY","hH","bB","vV"],"H":["gG","yY","uU","jJ","nN","bB"],"I":["uU","8*","9(","oO","kK","jJ"],"J":["hH","uU","iI","kK","mM","nN"],"K":["jJ","iI","oO","lL",",<","mM"],"L":["kK","oO","pP",";:",".>",",<"],"M":["nN","jJ","kK",",<",null,null],"N":["bB","hH","jJ","mM",null,null],"O":["iI","9(","0)","pP","lL","kK"],"P":["oO","0)","-_","[{",";:","lL"],"Q":[null,"1!","2@","wW","aA",null],"R":["eE","4$","5%","tT","fF","dD"],"S":["aA","wW","eE","dD","xX","zZ"],"T":["rR","5%","6^","yY","gG","fF"],"U":["yY","7&","8*","iI","jJ","hH"],"V":["cC","fF","gG","bB",null,null],"W":["qQ","2@","3#","eE","sS","aA"],"X":["zZ","sS","dD","cC",null,null],"Y":["tT","6^","7&","uU","hH","gG"],"Z":[null,"aA","sS","xX",null,null],"[":["pP","-_","=+","]}","'\"",";:"],"\\":["]}",null,null,null,null,null],"]":["[{","=+",null,"\\|",null,"'\""],"^":["5%",null,null,"7&","yY","tT"],"_":["0)",null,null,"=+","[{","pP"],"`":[null,null,null,"1!",null,null],"a":[null,"qQ","wW","sS","zZ",null],"b":["vV","gG","hH","nN",null,null],"c":["xX","dD","fF","vV",null,null],"d":["sS","eE","rR","fF","cC","xX"],"e":["wW","3#","4$","rR","dD","sS"],"f":["dD","rR","tT","gG","vV","cC"],"g":["fF","tT","yY","hH","bB","vV"],"h":["gG","yY","uU","jJ","nN","bB"],"i":["uU","8*","9(","oO","kK","jJ"],"j":["hH","uU","iI","kK","mM","nN"],"k":["jJ","iI","oO","lL",",<","mM"],"l":["kK","oO","pP",";:",".>",",<"],"m":["nN","jJ","kK",",<",null,null],"n":["bB","hH","jJ","mM",null,null],"o":["iI","9(","0)","pP","lL","kK"],"p":["oO","0)","-_","[{",";:","lL"],"q":[null,"1!","2@","wW","aA",null],"r":["eE","4$","5%","tT","fF","dD"],"s":["aA","wW","eE","dD","xX","zZ"],"t":["rR","5%","6^","yY","gG","fF"],"u":["yY","7&","8*","iI","jJ","hH"],"v":["cC","fF","gG","bB",null,null],"w":["qQ","2@","3#","eE","sS","aA"],"x":["zZ","sS","dD","cC",null,null],"y":["tT","6^","7&","uU","hH","gG"],"z":[null,"aA","sS","xX",null,null],"{":["pP","-_","=+","]}","'\"",";:"],"|":["]}",null,null,null,null,null],"}":["[{","=+",null,"\\|",null,"'\""],"~":[null,null,null,"1!",null,null]},"dvorak":{"0":["9(",null,null,"[{","lL","rR"],"1":["`~",null,null,"2@","'\"",null],"2":["1!",null,null,"3#",",<","'\""],"3":["2@",null,null,"4$",".>",",<"],"4":["3#",null,null,"5%","pP",".>"],"5":["4$",null,null,"6^","yY","pP"],"6":["5%",null,null,"7&","fF","yY"],"7":["6^",null,null,"8*","gG","fF"],"8":["7&",null,null,"9(","cC","gG"],"9":["8*",null,null,"0)","rR","cC"],"!":["`~",null,null,"2@","'\"",null],"\"":[null,"1!","2@",",<","aA",null],"#":["2@",null,null,"4$",".>",",<"],"$":["3#",null,null,"5%","pP",".>"],"%":["4$",null,null,"6^","yY","pP"],"&":["6^",null,null,"8*","gG","fF"],"'":[null,"1!","2@",",<","aA",null],"(":["8*",null,null,"0)","rR","cC"],")":["9(",null,null,"[{","lL","rR"],"*":["7&",null,null,"9(","cC","gG"],"+":["/?","]}",null,"\\|",null,"-_"],",":["'\"","2@","3#",".>","oO","aA"],"-":["sS","/?","=+",null,null,"zZ"],".":[",<","3#","4$","pP","eE","oO"],"/":["lL","[{","]}","=+","-_","sS"],":":[null,"aA","oO","qQ",null,null],";":[null,"aA","oO","qQ",null,null],"<":["'\"","2@","3#",".>","oO","aA"],"=":["/?","]}",null,"\\|",null,"-_"],">":[",<","3#","4$","pP","eE","oO"],"?":["lL","[{","]}","=+","-_","sS"],"@":["1!",null,null,"3#",",<","'\""],"A":[null,"'\"",",<","oO",";:",null],"B":["xX","dD","hH","mM",null,null],"C":["gG","8*","9(","rR","tT","hH"],"D":["iI","fF","gG","hH","bB","xX"],"E":["oO",".>","pP","uU","jJ","qQ"],"F":["yY","6^","7&","gG","dD","iI"],"G":["fF","7&","8*","cC","hH","dD"],"H":["dD","gG","cC","tT","mM","bB"],"I":["uU","yY","fF","dD","xX","kK"],"J":["qQ","eE","uU","kK",null,null],"K":["jJ","uU","iI","xX",null,null],"L":["rR","0)","[{","/?","sS","nN"],"M":["bB","hH","tT","wW",null,null],"N":["tT","rR","lL","sS","vV","wW"],"O":["aA",",<",".>","eE","qQ",";:"],"P":[".>","4$","5%","yY","uU","eE"],"Q":[";:","oO","eE","jJ",null,null],"R":["cC","9(","0)","lL","nN","tT"],"S":["nN","lL","/?","-_","zZ","vV"],"T":["hH","cC","rR","nN","wW","mM"],"U":["eE","pP","yY","iI","kK","jJ"],"V":["wW","nN","sS","zZ",null,null],"W":["mM","tT","nN","vV",null,null],"X":["kK","iI","dD","bB",null,null],"Y":["pP","5%","6^","fF","iI","uU"],"Z":["vV","sS","-_",null,null,null],"[":["0)",null,null,"]}","/?","lL"],"\\":["=+",null,null,null,null,null],"]":["[{",null,null,null,"=+","/?"],"^":["5%",null,null,"7&","fF","yY"],"_":["sS","/?","=+",null,null,"zZ"],"`":[null,null,null,"1!",null,null],"a":[null,"'\"",",<","oO",";:",null],"b":["xX","dD","hH","mM",null,null],"c":["gG","8*","9(","rR","tT","hH"],"d":["iI","fF","gG","hH","bB","xX"],"e":["oO",".>","pP","uU","jJ","qQ"],"f":["yY","6^","7&","gG","dD","iI"],"g":["fF","7&","8*","cC","hH","dD"],"h":["dD","gG","cC","tT","mM","bB"],"i":["uU","yY","fF","dD","xX","kK"],"j":["qQ","eE","uU","kK",null,null],"k":["jJ","uU","iI","xX",null,null],"l":["rR","0)","[{","/?","sS","nN"],"m":["bB","hH","tT","wW",null,null],"n":["tT","rR","lL","sS","vV","wW"],"o":["aA",",<",".>","eE","qQ",";:"],"p":[".>","4$","5%","yY","uU","eE"],"q":[";:","oO","eE","jJ",null,null],"r":["cC","9(","0)","lL","nN","tT"],"s":["nN","lL","/?","-_","zZ","vV"],"t":["hH","cC","rR","nN","wW","mM"],"u":["eE","pP","yY","iI","kK","jJ"],"v":["wW","nN","sS","zZ",null,null],"w":["mM","tT","nN","vV",null,null],"x":["kK","iI","dD","bB",null,null],"y":["pP","5%","6^","fF","iI","uU"],"z":["vV","sS","-_",null,null,null],"{":["0)",null,null,"]}","/?","lL"],"|":["=+",null,null,null,null,null],"}":["[{",null,null,null,"=+","/?"],"~":[null,null,null,"1!",null,null]},"keypad":{"0":[null,"1","2","3",".",null,null,null],"1":[null,null,"4","5","2","0",null,null],"2":["1","4","5","6","3",".","0",null],"3":["2","5","6",null,null,null,".","0"],"4":[null,null,"7","8","5","2","1",null],"5":["4","7","8","9","6","3","2","1"],"6":["5","8","9","+",null,null,"3","2"],"7":[null,null,null,"/","8","5","4",null],"8":["7",null,"/","*","9","6","5","4"],"9":["8","/","*","-","+",null,"6","5"],"*":["/",null,null,null,"-","+","9","8"],"+":["9","*","-",null,null,null,null,"6"],"-":["*",null,null,null,null,null,"+","9"],".":["0","2","3",null,null,null,null,null],"/":[null,null,null,null,"*","9","8","7"]},"mac_keypad":{"0":[null,"1","2","3",".",null,null,null],"1":[null,null,"4","5","2","0",null,null],"2":["1","4","5","6","3",".","0",null],"3":["2","5","6","+",null,null,".","0"],"4":[null,null,"7","8","5","2","1",null],"5":["4","7","8","9","6","3","2","1"],"6":["5","8","9","-","+",null,"3","2"],"7":[null,null,null,"=","8","5","4",null],"8":["7",null,"=","/","9","6","5","4"],"9":["8","=","/","*","-","+","6","5"],"*":["/",null,null,null,null,null,"-","9"],"+":["6","9","-",null,null,null,null,"3"],"-":["9","/","*",null,null,null,"+","6"],".":["0","2","3",null,null,null,null,null],"/":["=",null,null,null,"*","-","9","8"],"=":[null,null,null,null,"/","9","8","7"]}}
includes/tweaks/password-requirements/libs/zxcvbn-php/matchers/bruteforce.php ADDED
@@ -0,0 +1,72 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ class ITSEC_Zxcvbn_Bruteforce_Match extends ITSEC_Zxcvbn_Match {
3
+ private static $cardinality = 10;
4
+ private static $min_submatch_guesses_single_char = 10;
5
+ private static $min_submatch_guesses_multi_char = 50;
6
+
7
+ private $entropy;
8
+
9
+ public function __construct( $password, $result ) {
10
+ $this->password = $password;
11
+ foreach ( $result as $key => $value ) {
12
+ $this->{$key} = $value;
13
+ }
14
+ }
15
+
16
+ /**
17
+ * Finds matches in the password.
18
+ * @param string $password Password to check for match
19
+ * @param array $penalty_strings Strings that should be penalized if in the password. This should be things like the username, first and last name, etc.
20
+ *
21
+ * @return ITSEC_Zxcvbn_Match[] Array of Match objects
22
+ */
23
+ public static function match( $password, array $penalty_strings = array() ) {
24
+ // Matches entire string.
25
+ $match = new self( $password, array( 'begin' => 0, 'end' => strlen( $password ) - 1, 'token' => $password ) );
26
+ return array($match);
27
+ }
28
+
29
+ public function estimate_guesses() {
30
+ $guesses = pow( self::$cardinality, strlen( $this->token ) );
31
+ $min_guesses = ( 1 === strlen( $this->token ) )? self::$min_submatch_guesses_single_char : self::$min_submatch_guesses_multi_char;
32
+ $this->guesses = max( $guesses, $min_guesses );
33
+ return $this->guesses;
34
+ }
35
+
36
+ public function get_feedback( $is_sole_match = true ) {
37
+ $feedback = new stdClass();
38
+ $feedback->warning = '';
39
+ $feedback->suggestions = array();
40
+
41
+ return $feedback;
42
+ }
43
+ /**
44
+ * @return int
45
+ */
46
+ /*
47
+ public function get_cardinality( $token ) {
48
+ if ( ! is_null( $this->cardinality ) ) {
49
+ return $this->cardinality;
50
+ }
51
+ $lower = $upper = $digits = $symbols = $unicode = 0;
52
+
53
+ foreach ( str_split( $token ) as $char ) {
54
+ $ord = ord( $char );
55
+
56
+ if ($this->is_digit($ord)) {
57
+ $digits = 10;
58
+ } elseif ($this->is_upper($ord)) {
59
+ $upper = 26;
60
+ } elseif ($this->is_lower($ord)) {
61
+ $lower = 26;
62
+ } elseif ($this->is_symbol($ord)) {
63
+ $symbols = 33;
64
+ } else {
65
+ $unicode = 100;
66
+ }
67
+ }
68
+ $this->cardinality = $lower + $digits + $upper + $symbols + $unicode;
69
+ return $this->cardinality;
70
+ }
71
+ /**/
72
+ }
includes/tweaks/password-requirements/libs/zxcvbn-php/matchers/date.php ADDED
@@ -0,0 +1,319 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * a "date" is recognized as:
4
+ * any 3-tuple that starts or ends with a 2- or 4-digit year,
5
+ * with 2 or 0 separator chars (1.1.91 or 1191),
6
+ * maybe zero-padded (01-01-91 vs 1-1-91),
7
+ * a month between 1 and 12,
8
+ * a day between 1 and 31.
9
+ *
10
+ * note: this isn't true date parsing in that "feb 31st" is allowed,
11
+ * this doesn't check for leap years, etc.
12
+ *
13
+ * recipe:
14
+ * start with regex to find maybe-dates, then attempt to map the integers
15
+ * onto month-day-year to filter the maybe-dates into dates.
16
+ * finally, remove matches that are substrings of other matches to reduce noise.
17
+ *
18
+ * note: instead of using a lazy or greedy regex to find many dates over the full string,
19
+ * this uses a ^...$ regex against every substring of the password -- less performant but leads
20
+ * to every possible date match.
21
+ */
22
+ class ITSEC_Zxcvbn_Date_Match extends ITSEC_Zxcvbn_Match {
23
+
24
+ protected $has_full_year = false;
25
+
26
+ protected static $date_max_year = 2050;
27
+ protected static $date_min_year = 1000;
28
+
29
+ protected static $date_splits = array(
30
+ '4' => array(
31
+ array( 1, 2 ),
32
+ array( 2, 3 )
33
+ ),
34
+ '5' => array(
35
+ array( 1, 3 ),
36
+ array( 2, 3 )
37
+ ),
38
+ '6' => array(
39
+ array( 1, 2 ),
40
+ array( 2, 4 ),
41
+ array( 4, 5 )
42
+ ),
43
+ '7' => array(
44
+
45
+ array( 1, 3 ),
46
+ array( 2, 3 ),
47
+ array( 4, 5 ),
48
+ array( 4, 6 )
49
+ ),
50
+ '8' => array(
51
+ array( 2, 4 ),
52
+ array( 4, 6 )
53
+ ),
54
+ );
55
+
56
+
57
+ public function __construct( $password, $result ) {
58
+ $this->password = $password;
59
+ foreach ( $result as $key => $value ) {
60
+ $this->{$key} = $value;
61
+ }
62
+ }
63
+
64
+ /**
65
+ * Finds matches in the password.
66
+ * @param string $password Password to check for match
67
+ * @param array $penalty_strings Strings that should be penalized if in the password. This should be things like the username, first and last name, etc.
68
+ *
69
+ * @return ITSEC_Zxcvbn_Match[] Array of Match objects
70
+ */
71
+ public static function match( $password, array $penalty_strings = array() ) {
72
+ $matches = array();
73
+
74
+ $maybe_date_no_separator = '/^\d{4,8}$/';
75
+ $maybe_date_with_separator =
76
+ '#^' // Anchor to start
77
+ . '(\d{1,4})' // day, month, year
78
+ . '([\s/\_.-])' // separator
79
+ . '(\d{1,2})' // day, month
80
+ . '\2' // same separator
81
+ . '(\d{1,4})' // day, month, year
82
+ . '$#'; // Anchor to end
83
+
84
+ // dates without separators are between length 4 '1191' and 8 '11111991'
85
+ for ( $begin = 0; $begin <= strlen( $password ) - 4; $begin++ ) {
86
+ for ( $end = $begin + 3; $end <= $begin + 7; $end++ ) {
87
+ if ( $end >= strlen( $password ) ) {
88
+ break;
89
+ }
90
+
91
+ $token = substr( $password, $begin, $end - $begin + 1 );
92
+
93
+ if ( ! preg_match( $maybe_date_no_separator, $token ) ) {
94
+ continue;
95
+ }
96
+ $candidates = array();
97
+ for ( $q = 0; $q < count( self::$date_splits[ strlen( $token ) ] ); $q++ ) {
98
+ $k = self::$date_splits[ strlen( $token ) ][ $q ][0];
99
+ $l = self::$date_splits[ strlen( $token ) ][ $q ][1];
100
+ $dmy = self::map_ints_to_dmy( array( substr( $token, 0, $k ), substr( $token, $k, $l - $k ), substr( $token, $l ) ) );
101
+ if ( ! empty( $dmy ) ) {
102
+ $candidates[] = $dmy;
103
+ }
104
+ }
105
+ if ( empty( $candidates ) ) {
106
+ continue;
107
+ }
108
+ // at this point: different possible dmy mappings for the same i,j substring.
109
+ // match the candidate date that likely takes the fewest guesses: a year closest to REFERENCE_YEAR.
110
+ //
111
+ // ie, considering '111504', prefer 11-15-04 to 1-1-1504
112
+ // (interpreting '04' as 2004)
113
+
114
+ $best_candidate = array_reduce( $candidates, array( __CLASS__, 'metric' ) );
115
+
116
+ $result = array(
117
+ 'token' => $token,
118
+ 'begin' => $begin,
119
+ 'end' => $end,
120
+ 'separator' => '',
121
+ 'year' => $best_candidate['year'],
122
+ 'month' => $best_candidate['month'],
123
+ 'day' => $best_candidate['day'],
124
+ );
125
+
126
+ $matches[] = new self( $password, $result );
127
+ }
128
+ }
129
+
130
+ // dates with separators are between length 6 '1/1/91' and 10 '11/11/1991'
131
+ for ( $begin = 0; $begin <= strlen( $password ) - 6; $begin++ ) {
132
+ for ( $end = $begin + 5; $end <= $begin + 9; $end ++ ) {
133
+ if ( $end >= strlen( $password ) ) {
134
+ break;
135
+ }
136
+
137
+ $token = substr( $password, $begin, $end - $begin + 1 );
138
+ if ( ! preg_match( $maybe_date_with_separator, $token, $maybe_match ) ) {
139
+ continue;
140
+ }
141
+ $dmy = self::map_ints_to_dmy( array( $maybe_match[1], $maybe_match[3], $maybe_match[4] ) );
142
+ if ( empty( $dmy ) ) {
143
+ continue;
144
+ }
145
+
146
+ $result = array(
147
+ 'token' => $token,
148
+ 'begin' => $begin,
149
+ 'end' => $end,
150
+ 'separator' => $maybe_match[2],
151
+ 'year' => $dmy['year'],
152
+ 'month' => $dmy['month'],
153
+ 'day' => $dmy['day'],
154
+ );
155
+
156
+ $matches[] = new self( $password, $result );
157
+ }
158
+ }
159
+ // matches now contains all valid date strings in a way that is tricky to capture
160
+ // with regexes only. while thorough, it will contain some unintuitive noise:
161
+ //
162
+ // '2015_06_04', in addition to matching 2015_06_04, will also contain
163
+ // 5(!) other date matches: 15_06_04, 5_06_04, ..., even 2015 (matched as 5/1/2020)
164
+ //
165
+ // to reduce noise, remove date matches that are strict substrings of others
166
+
167
+ foreach ( $matches as $key => $match ) {
168
+ foreach ( $matches as $m ) {
169
+ // Don't compare to self
170
+ if ( $m === $match ) {
171
+ continue;
172
+ }
173
+ if ( $m->begin <= $match->begin && $m->end >= $match->end ) {
174
+ unset( $matches[ $key ] );
175
+ break;
176
+ }
177
+ }
178
+ }
179
+
180
+ return $matches;
181
+ }
182
+
183
+ private static function metric( $a, $b ) {
184
+ if ( empty( $a['year'] ) ) {
185
+ return $b;
186
+ }
187
+ if ( empty( $b['year'] ) ) {
188
+ return $a;
189
+ }
190
+ return ( abs( $a['year'] - self::$reference_year ) <= abs( $b['year'] - self::$reference_year ) )? $a : $b;
191
+ }
192
+
193
+ private static function map_ints_to_dmy( $ints ) {
194
+ // given a 3-tuple, discard if:
195
+ // middle int is over 31 (for all dmy formats, years are never allowed in the middle)
196
+ // middle int is zero
197
+ // any int is over the max allowable year
198
+ // any int is over two digits but under the min allowable year
199
+ // 2 ints are over 31, the max allowable day
200
+ // 2 ints are zero
201
+ // all ints are over 12, the max allowable month
202
+ if ( $ints[1] > 31 || $ints[1] <= 0 ) {
203
+ return;
204
+ }
205
+ $over_12 = 0;
206
+ $over_31 = 0;
207
+ $under_1 = 0;
208
+ foreach ( $ints as $int ) {
209
+ // If this is the year and it's not valid, return nothing
210
+ if ( ( 99 < $int && $int < self::$date_min_year ) || $int > self::$date_max_year ) {
211
+ return;
212
+ }
213
+ if ( $int > 31 ) {
214
+ $over_31++;
215
+ }
216
+ if ( $int > 12 ) {
217
+ $over_12++;
218
+ }
219
+ if ( $int < 1 ) {
220
+ $under_1 ++;
221
+ }
222
+ }
223
+ if ( $over_31 >= 2 || $over_12 === 3 || $under_1 >= 2 ) {
224
+ return;
225
+ }
226
+
227
+ // first look for a four digit year: yyyy + daymonth or daymonth + yyyy
228
+ $possible_year_splits = array(
229
+ array( 'year' => $ints[2], 'rest' => array( $ints[0], $ints[1] ) ), // year last
230
+ array( 'year' => $ints[0], 'rest' => array( $ints[1], $ints[2] ) ), // year first
231
+ );
232
+ foreach ( $possible_year_splits as $split ) {
233
+ if ( self::$date_min_year <= $split['year'] && $split['year'] <= self::$date_max_year ) {
234
+ $dm = self::map_ints_to_dm( $split['rest'] );
235
+ if ( $dm ) {
236
+ return array(
237
+ 'year' => $split['year'],
238
+ 'month' => $dm['month'],
239
+ 'day' => $dm['day'],
240
+ );
241
+ } else {
242
+ // for a candidate that includes a four-digit year,
243
+ // when the remaining ints don't match to a day and month,
244
+ // it is not a date.
245
+ return;
246
+ }
247
+ }
248
+ }
249
+
250
+ # given no four-digit year, two digit years are the most flexible int to match, so
251
+ # try to parse a day-month out of ints[0..1] or ints[1..0]
252
+ foreach ( $possible_year_splits as $split ) {
253
+ $dm = self::map_ints_to_dm( $split['rest'] );
254
+ if ( $dm ) {
255
+ $split['year'] = self::two_to_four_digit_year( $split['year'] );
256
+ return array(
257
+ 'year' => $split['year'],
258
+ 'month' => $dm['month'],
259
+ 'day' => $dm['day'],
260
+ );
261
+ }
262
+ }
263
+ }
264
+
265
+ private static function map_ints_to_dm( $ints ) {
266
+ list( $d, $m ) = $ints;
267
+
268
+ for ( $i = 0; $i < 2; $i++ ) {
269
+ if ( ( 1 <= $d && $d <= 31 ) && ( 1 <= $m && $m <= 12 ) ) {
270
+ return array(
271
+ 'day' => $d,
272
+ 'month' => $m
273
+ );
274
+ }
275
+ // Swap day and month
276
+ $temp = $d;
277
+ $d = $m;
278
+ $m = $temp;
279
+ }
280
+ return;
281
+ }
282
+
283
+ private static function two_to_four_digit_year( $year ) {
284
+ if ( $year > 99 ) {
285
+ return $year;
286
+ } elseif ( $year > 50 ) {
287
+ # 87 -> 1987
288
+ return $year + 1900;
289
+ } else {
290
+ # 15 -> 2015
291
+ return $year + 2000;
292
+ }
293
+ }
294
+
295
+ public function estimate_guesses() {
296
+ // base guesses: (year distance from REFERENCE_YEAR) * num_days * num_years
297
+ $this->guesses = max( abs( $this->year - self::$reference_year ), self::$min_year_space ) * 365;
298
+
299
+ // double for four-digit years
300
+ if ( $this->has_full_year ) {
301
+ $this->guesses *= 2;
302
+ }
303
+
304
+ // add factor of 4 for separator selection (one of ~4 choices)
305
+ if ( $this->separator ) {
306
+ $this->guesses *= 4;
307
+ }
308
+
309
+ return $this->guesses;
310
+ }
311
+
312
+ public function get_feedback( $is_sole_match = true ) {
313
+ $feedback = new stdClass();
314
+ $feedback->warning = 'Dates are often easy to guess';
315
+ $feedback->suggestions = array( 'Avoid dates and years that are associated with you' );
316
+
317
+ return $feedback;
318
+ }
319
+ }
includes/tweaks/password-requirements/libs/zxcvbn-php/matchers/dictionary-l33t.php ADDED
@@ -0,0 +1,114 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ class ITSEC_Zxcvbn_Dictionary_L33t_Match extends ITSEC_Zxcvbn_Dictionary_Match {
3
+
4
+ private static $l33t_table = array(
5
+ 'a' => array( '4', '@' ),
6
+ 'b' => array( '8' ),
7
+ 'c' => array( '(', '{', '[', '<' ),
8
+ 'e' => array( '3' ),
9
+ 'g' => array( '6', '9' ),
10
+ 'i' => array( '1', '!', '|' ),
11
+ 'l' => array( '1', '|', '7' ),
12
+ 'o' => array( '0' ),
13
+ 's' => array( '$', '5' ),
14
+ 't' => array( '+', '7' ),
15
+ 'x' => array( '%' ),
16
+ 'z' => array( '2' )
17
+ );
18
+
19
+ private static $l33t_table_reverse_single = array(
20
+ '!' => 'i',
21
+ '@' => 'a',
22
+ '$' => 's',
23
+ '%' => 'x',
24
+ '(' => 'c',
25
+ '+' => 't',
26
+ '{' => 'c',
27
+ '[' => 'c',
28
+ '<' => 'c',
29
+ '0' => 'o',
30
+ '2' => 'z',
31
+ '3' => 'e',
32
+ '4' => 'a',
33
+ '5' => 's',
34
+ '6' => 'g',
35
+ '8' => 'b',
36
+ '9' => 'g',
37
+ );
38
+
39
+ private static $l33t_table_reverse_multi = array(
40
+ '|' => array( 'i', 'l' ),
41
+ '1' => array( 'i', 'l' ),
42
+ '7' => array( 'l', 't' ),
43
+ );
44
+
45
+ /**
46
+ * Finds matches in the password.
47
+ * @param string $password Password to check for match
48
+ * @param array $penalty_strings Strings that should be penalized if in the password. This should be things like the username, first and last name, etc.
49
+ *
50
+ * @return ITSEC_Zxcvbn_Match[] Array of Match objects
51
+ */
52
+ public static function match( $password, array $penalty_strings = array(), $class = null ) {
53
+ $plain_passes = self::l33t_sub( $password );
54
+ $matches = array();
55
+ foreach ( $plain_passes as $plain_pass ) {
56
+ $match_set = parent::match( $plain_pass, $penalty_strings, __CLASS__ );
57
+ foreach ( $match_set as &$match ) {
58
+ $match->l33t = true;
59
+ $match->token = substr( $password, $match->begin, strlen( $match->token ) );
60
+ $match->password = $password;
61
+ }
62
+ $matches = array_merge( $matches, $match_set );
63
+ }
64
+ return $matches;
65
+ }
66
+
67
+ protected function get_l33t_variations() {
68
+ return 2;
69
+ }
70
+
71
+ public function get_feedback( $is_sole_match = true ) {
72
+ $feedback = parent::get_feedback( $is_sole_match );
73
+ $feedback->suggestions[] = "Predictable substitutions like '@' instead of 'a' don't help very much";
74
+
75
+ if ( 'passwords' == $this->dictionary_name ) {
76
+ if ( log10( $this->estimate_guesses() ) <= 4 ) {
77
+ $feedback->warning = 'This is similar to a commonly used password';
78
+ } else {
79
+ $feedback->warning = '';
80
+ }
81
+ }
82
+
83
+ return $feedback;
84
+ }
85
+
86
+ protected static function l33t_sub( $password ) {
87
+ // Handle all single character replacements
88
+ $password = str_replace( array_keys( self::$l33t_table_reverse_single ), self::$l33t_table_reverse_single, $password );
89
+ $passwords = array( $password );
90
+ //Loop through the more complicated replacements (multiple opssible replacements per character, such as | being l or i)
91
+ foreach ( self::$l33t_table_reverse_multi as $char => $replace_array ) {
92
+ // If the character doesn't exist, don't worry about it
93
+ if ( false !== $pos = strpos( $password, (string) $char ) ) {
94
+ $new_passwords = array();
95
+ // Loop through each current password and merge all returns into $new_passwords
96
+ foreach( $passwords as $password ) {
97
+ $new_passwords = array_merge( $new_passwords, self::l33t_sub_char( $password, $char, $replace_array ) );
98
+ }
99
+ // Replace the old passwords with the newer, bigger set
100
+ $passwords = $new_passwords;
101
+ }
102
+ }
103
+ return $passwords;
104
+ }
105
+
106
+ protected static function l33t_sub_char( $password, $char, $replace_array ) {
107
+ $passwords = array();
108
+ foreach( $replace_array as $replace_char ) {
109
+ $pos = strpos( $password, (string) $char );
110
+ $passwords[] = substr_replace( $password, $replace_char, $pos, 1 );
111
+ }
112
+ return $passwords;
113
+ }
114
+ }
includes/tweaks/password-requirements/libs/zxcvbn-php/matchers/dictionary-reverse.php ADDED
@@ -0,0 +1,44 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ class ITSEC_Zxcvbn_Dictionary_Reverse_Match extends ITSEC_Zxcvbn_Dictionary_Match {
3
+
4
+ /**
5
+ * Finds matches in the password.
6
+ * @param string $password Password to check for match
7
+ * @param array $penalty_strings Strings that should be penalized if in the password. This should be things like the username, first and last name, etc.
8
+ *
9
+ * @return ITSEC_Zxcvbn_Match[] Array of Match objects
10
+ */
11
+ public static function match( $password, array $penalty_strings = array(), $class = null ) {
12
+ $rev_pass = strrev( $password );
13
+ $matches = parent::match( $rev_pass, $penalty_strings, __CLASS__ );
14
+ foreach ( $matches as &$match ) {
15
+ $match->reversed = true;
16
+ $match->token = strrev( $match->token );
17
+ $match->password = strrev( $match->password );
18
+ $match->length = strlen( $match->password );
19
+ list( $match->begin, $match->end ) = array( strlen( $match->password ) - 1 - $match->end, strlen( $match->password ) - 1 - $match->begin );
20
+ }
21
+ return $matches;
22
+ }
23
+
24
+ protected function get_reversed_variations() {
25
+ return 2;
26
+ }
27
+
28
+ public function get_feedback( $is_sole_match = true ) {
29
+ $feedback = parent::get_feedback( $is_sole_match );
30
+ if ( strlen( $this->token ) >= 4 ) {
31
+ $feedback->suggestions[] = "Reversed words aren't much harder to guess";
32
+ }
33
+
34
+ if ( 'passwords' == $this->dictionary_name ) {
35
+ if ( log10( $this->estimate_guesses() ) <= 4 ) {
36
+ $feedback->warning = 'This is similar to a commonly used password';
37
+ } else {
38
+ $feedback->warning = '';
39
+ }
40
+ }
41
+
42
+ return $feedback;
43
+ }
44
+ }
includes/tweaks/password-requirements/libs/zxcvbn-php/matchers/dictionary.php ADDED
@@ -0,0 +1,211 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ class ITSEC_Zxcvbn_Dictionary_Match extends ITSEC_Zxcvbn_Match {
3
+
4
+ /**
5
+ * @var array|mixed|object
6
+ */
7
+ private static $frequency_lists;
8
+
9
+ protected static $start_upper_regex = '/^[A-Z][^A-Z]+$/';
10
+ protected static $end_upper_regex = '/^[^A-Z]+[A-Z]$/';
11
+ protected static $all_upper_regex = '/^[A-Z]+$/';
12
+
13
+ public function __construct( $password, $result ) {
14
+ $this->password = $password;
15
+ foreach ( $result as $key => $value ) {
16
+ $this->{$key} = $value;
17
+ }
18
+ }
19
+
20
+ /**
21
+ * Finds matches in the password.
22
+ * @param string $password Password to check for match
23
+ * @param array $penalty_strings Strings that should be penalized if in the password. This should be things like the username, first and last name, etc.
24
+ *
25
+ * @return ITSEC_Zxcvbn_Match[] Array of Match objects
26
+ */
27
+ public static function match( $password, array $penalty_strings = array(), $class = null ) {
28
+ if ( ! isset( $class ) ) {
29
+ $class = __CLASS__;
30
+ }
31
+ $matches = array();
32
+ $dictionary_names = array(
33
+ 'english_wikipedia',
34
+ 'female_names',
35
+ 'male_names',
36
+ 'passwords',
37
+ 'surnames',
38
+ 'us_tv_and_film',
39
+ );
40
+ foreach ( $dictionary_names as $dictionary_name ) {
41
+ $dictionaries = call_user_func( array( $class, 'get_ranked_dictionary' ), $dictionary_name );
42
+ foreach ( $dictionaries as $name => $dictionary ) {
43
+ $matches = array_merge( $matches, call_user_func( array( $class, 'get_dictionary_matches' ), $dictionary, $name, $password, $class ) );
44
+ }
45
+ if ( ! empty( $penalty_strings ) ) {
46
+ $dictionary = array();
47
+ foreach ( $penalty_strings as $rank => $input ) {
48
+ if ( empty( $input ) ) {
49
+ continue;
50
+ }
51
+
52
+ $input_lower = strtolower( $input );
53
+ $dictionary[ $input_lower ] = $rank;
54
+ }
55
+ $matches = array_merge( $matches, call_user_func( array( $class, 'get_dictionary_matches' ), $dictionary, 'user_inputs', $password, $class ) );
56
+ }
57
+ }
58
+ return $matches;
59
+ }
60
+
61
+ public static function get_dictionary_matches( $dictionary, $name, $password, $class ) {
62
+ $matches = array();
63
+ $results = call_user_func( array( $class, 'dictionary_match' ), $password, $dictionary );
64
+ foreach ( $results as $result ) {
65
+ $result['dictionary_name'] = $name;
66
+ $result['reversed'] = false;
67
+ $matches[] = new $class( $password, $result );
68
+ }
69
+ return $matches;
70
+
71
+ }
72
+
73
+
74
+ /**
75
+ * Match password in a single dictionary.
76
+ *
77
+ * @param string $password
78
+ * @param array $dictionary
79
+ *
80
+ * @return array
81
+ */
82
+ protected static function dictionary_match( $password, $dictionary ) {
83
+ $result = array();
84
+ $length = strlen($password);
85
+
86
+ $pw_lower = strtolower( $password );
87
+
88
+ for ( $i = 0; $i < $length; $i++ ) {
89
+ for ( $j = $i; $j < $length; $j++ ) {
90
+ $word = substr( $pw_lower, $i, $j - $i + 1 );
91
+
92
+ if ( isset( $dictionary[ $word ] ) ) {
93
+ $result[] = array(
94
+ 'begin' => $i,
95
+ 'end' => $j,
96
+ 'token' => substr( $password, $i, $j - $i + 1 ),
97
+ 'matched_word' => $word,
98
+ 'rank' => $dictionary[ $word ],
99
+ );
100
+ }
101
+ }
102
+ }
103
+
104
+ return $result;
105
+ }
106
+
107
+ /**
108
+ * Load ranked frequency dictionaries.
109
+ *
110
+ * @return object
111
+ */
112
+ protected static function get_ranked_dictionary( $dictionary_name ) {
113
+ return json_decode( file_get_contents( dirname( __FILE__ ) . sprintf( '/ranked_frequency_list-%s.json', $dictionary_name ) ), true );
114
+ }
115
+
116
+ public function estimate_guesses() {
117
+ $this->guesses = $this->rank * $this->get_uppercase_variations() * $this->get_l33t_variations() * $this->get_reversed_variations();
118
+ return $this->guesses;
119
+ }
120
+
121
+ /**
122
+ * @return float
123
+ */
124
+ protected function get_uppercase_variations() {
125
+ $token = $this->token;
126
+ // Return if token is all lowercase.
127
+ if ( $token === strtolower( $token ) ) {
128
+ return 1;
129
+ }
130
+
131
+ // a capitalized word is the most common capitalization scheme,
132
+ // so it only doubles the search space (uncapitalized + capitalized).
133
+ // allcaps and end-capitalized are common enough too, underestimate as 2x factor to be safe.
134
+ foreach ( array( self::$start_upper_regex, self::$end_upper_regex, self::$all_upper_regex ) as $regex ) {
135
+ if ( preg_match( $regex, $token ) ) {
136
+ return 2;
137
+ }
138
+ }
139
+ $variations = $upper = $lower = 0;
140
+
141
+ foreach ( str_split( $token ) as $c ) {
142
+ $ord = ord( $c );
143
+
144
+ if ( $this->is_upper( $ord ) ) {
145
+ ++$upper;
146
+ } elseif ( $this->is_lower( $ord ) ) {
147
+ ++$lower;
148
+ }
149
+ }
150
+
151
+ // otherwise calculate the number of ways to capitalize U+L uppercase+lowercase letters
152
+ // with U uppercase letters or less. or, if there's more uppercase than lower (for eg. PASSwORD),
153
+ // the number of ways to lowercase U+L letters with L lowercase letters or less.
154
+ for ( $i = 1; $i <= min( $upper, $lower ); $i++ ) {
155
+ $variations += $this->binomial_coefficient( $upper + $lower, $i );
156
+ }
157
+ return $variations;
158
+ }
159
+
160
+ protected function get_l33t_variations() {
161
+ return 1;
162
+ }
163
+
164
+ protected function get_reversed_variations() {
165
+ return 1;
166
+ }
167
+
168
+ public function get_feedback( $is_sole_match = true ) {
169
+ $feedback = new stdClass();
170
+ $feedback->warning = '';
171
+ $feedback->suggestions = array();
172
+
173
+ switch ( $this->dictionary_name ) {
174
+ case 'passwords':
175
+ if ( $is_sole_match ) {
176
+ if ( $this->rank < 10 ) {
177
+ $feedback->warning = 'This is a top-10 common password';
178
+ } elseif ( $this->rank < 100 ) {
179
+ $feedback->warning = 'This is a top-100 common password';
180
+ } else {
181
+ $feedback->warning = 'This is a very common password';
182
+ }
183
+ } elseif ( log10( $this->estimate_guesses() ) <= 4 ) {
184
+ $feedback->warning = 'This is similar to a commonly used password';
185
+ }
186
+ break;
187
+ case 'english':
188
+ if ( $is_sole_match ) {
189
+ $feedback->warning = 'A word by itself is easy to guess';
190
+ }
191
+ break;
192
+ case 'surnames':
193
+ case 'male_names':
194
+ case 'female_names':
195
+ if ( $is_sole_match ) {
196
+ $feedback->warning = 'Names and surnames by themselves are easy to guess';
197
+ } else {
198
+ $feedback->warning = 'Common names and surnames are easy to guess';
199
+ }
200
+ break;
201
+ }
202
+
203
+ if ( preg_match( self::$start_upper_regex, $this->token ) ) {
204
+ $feedback->suggestions[] = "Capitalization doesn't help very much";
205
+ } elseif ( preg_match( self::$all_upper_regex, $this->token ) ) {
206
+ $feedback->suggestions[] = "All-uppercase is almost as easy to guess as all-lowercase";
207
+ }
208
+
209
+ return $feedback;
210
+ }
211
+ }
includes/tweaks/password-requirements/libs/zxcvbn-php/matchers/index.php ADDED
@@ -0,0 +1 @@
 
1
+ <?php // Silence is golden.
includes/tweaks/password-requirements/libs/zxcvbn-php/matchers/ranked_frequency_list-english_wikipedia.json ADDED
@@ -0,0 +1 @@
 
1
+ {"english_wikipedia":{"the":1,"of":2,"and":3,"in":4,"was":5,"is":6,"for":7,"as":8,"on":9,"with":10,"by":11,"he":12,"at":13,"from":14,"his":15,"an":16,"were":17,"are":18,"which":19,"doc":20,"https":21,"also":22,"or":23,"has":24,"had":25,"first":26,"one":27,"their":28,"its":29,"after":30,"new":31,"who":32,"they":33,"two":34,"her":35,"she":36,"been":37,"other":38,"when":39,"time":40,"during":41,"there":42,"into":43,"school":44,"more":45,"may":46,"years":47,"over":48,"only":49,"year":50,"most":51,"would":52,"world":53,"city":54,"some":55,"where":56,"between":57,"later":58,"three":59,"state":60,"such":61,"then":62,"national":63,"used":64,"made":65,"known":66,"under":67,"many":68,"university":69,"united":70,"while":71,"part":72,"season":73,"team":74,"these":75,"american":76,"than":77,"film":78,"second":79,"born":80,"south":81,"became":82,"states":83,"war":84,"through":85,"being":86,"including":87,"both":88,"before":89,"north":90,"high":91,"however":92,"people":93,"family":94,"early":95,"history":96,"album":97,"area":98,"them":99,"series":100,"against":101,"until":102,"since":103,"district":104,"county":105,"name":106,"work":107,"life":108,"group":109,"music":110,"following":111,"number":112,"company":113,"several":114,"four":115,"called":116,"played":117,"released":118,"career":119,"league":120,"game":121,"government":122,"house":123,"each":124,"based":125,"day":126,"same":127,"won":128,"use":129,"station":130,"club":131,"international":132,"town":133,"located":134,"population":135,"general":136,"college":137,"east":138,"found":139,"age":140,"march":141,"end":142,"september":143,"began":144,"home":145,"public":146,"church":147,"line":148,"june":149,"river":150,"member":151,"system":152,"place":153,"century":154,"band":155,"july":156,"york":157,"january":158,"october":159,"song":160,"august":161,"best":162,"former":163,"british":164,"party":165,"named":166,"held":167,"village":168,"show":169,"local":170,"november":171,"took":172,"service":173,"december":174,"built":175,"another":176,"major":177,"within":178,"along":179,"members":180,"five":181,"single":182,"due":183,"although":184,"small":185,"old":186,"left":187,"final":188,"large":189,"include":190,"building":191,"served":192,"president":193,"received":194,"games":195,"death":196,"february":197,"main":198,"third":199,"set":200,"children":201,"own":202,"order":203,"species":204,"park":205,"law":206,"air":207,"published":208,"road":209,"died":210,"book":211,"men":212,"women":213,"army":214,"often":215,"according":216,"education":217,"central":218,"country":219,"division":220,"english":221,"top":222,"included":223,"development":224,"french":225,"community":226,"among":227,"water":228,"play":229,"side":230,"list":231,"times":232,"near":233,"late":234,"form":235,"original":236,"different":237,"center":238,"power":239,"led":240,"students":241,"german":242,"moved":243,"court":244,"six":245,"land":246,"council":247,"island":248,"u.s.":249,"record":250,"million":251,"research":252,"art":253,"established":254,"award":255,"street":256,"military":257,"television":258,"given":259,"region":260,"support":261,"western":262,"production":263,"non":264,"political":265,"point":266,"cup":267,"period":268,"business":269,"title":270,"started":271,"various":272,"election":273,"using":274,"england":275,"role":276,"produced":277,"become":278,"program":279,"works":280,"field":281,"total":282,"office":283,"class":284,"written":285,"association":286,"radio":287,"union":288,"level":289,"championship":290,"director":291,"few":292,"force":293,"created":294,"department":295,"founded":296,"services":297,"married":298,"though":299,"per":300,"n't":301,"site":302,"open":303,"act":304,"short":305,"society":306,"version":307,"royal":308,"present":309,"northern":310,"worked":311,"professional":312,"full":313,"returned":314,"joined":315,"story":316,"france":317,"european":318,"currently":319,"language":320,"social":321,"california":322,"india":323,"days":324,"design":325,"st.":326,"further":327,"round":328,"australia":329,"wrote":330,"san":331,"project":332,"control":333,"southern":334,"railway":335,"board":336,"popular":337,"continued":338,"free":339,"battle":340,"considered":341,"video":342,"common":343,"position":344,"living":345,"half":346,"playing":347,"recorded":348,"red":349,"post":350,"described":351,"average":352,"records":353,"special":354,"modern":355,"appeared":356,"announced":357,"areas":358,"rock":359,"release":360,"elected":361,"others":362,"example":363,"term":364,"opened":365,"similar":366,"formed":367,"route":368,"census":369,"current":370,"schools":371,"originally":372,"lake":373,"developed":374,"race":375,"himself":376,"forces":377,"addition":378,"information":379,"upon":380,"province":381,"match":382,"event":383,"songs":384,"result":385,"events":386,"win":387,"eastern":388,"track":389,"lead":390,"teams":391,"science":392,"human":393,"construction":394,"minister":395,"germany":396,"awards":397,"available":398,"throughout":399,"training":400,"style":401,"body":402,"museum":403,"australian":404,"health":405,"seven":406,"signed":407,"chief":408,"eventually":409,"appointed":410,"sea":411,"centre":412,"debut":413,"tour":414,"points":415,"media":416,"light":417,"range":418,"character":419,"across":420,"features":421,"families":422,"largest":423,"indian":424,"network":425,"less":426,"performance":427,"players":428,"refer":429,"europe":430,"sold":431,"festival":432,"usually":433,"taken":434,"despite":435,"designed":436,"committee":437,"process":438,"return":439,"official":440,"episode":441,"institute":442,"stage":443,"followed":444,"performed":445,"japanese":446,"personal":447,"thus":448,"arts":449,"space":450,"low":451,"months":452,"includes":453,"china":454,"study":455,"middle":456,"magazine":457,"leading":458,"japan":459,"groups":460,"aircraft":461,"featured":462,"federal":463,"civil":464,"rights":465,"model":466,"coach":467,"canadian":468,"books":469,"remained":470,"eight":471,"type":472,"independent":473,"completed":474,"capital":475,"academy":476,"instead":477,"kingdom":478,"organization":479,"countries":480,"studies":481,"competition":482,"sports":483,"size":484,"above":485,"section":486,"finished":487,"gold":488,"involved":489,"reported":490,"management":491,"systems":492,"industry":493,"directed":494,"market":495,"fourth":496,"movement":497,"technology":498,"bank":499,"ground":500,"campaign":501,"base":502,"lower":503,"sent":504,"rather":505,"added":506,"provided":507,"coast":508,"grand":509,"historic":510,"valley":511,"conference":512,"bridge":513,"winning":514,"approximately":515,"films":516,"chinese":517,"awarded":518,"degree":519,"russian":520,"shows":521,"native":522,"female":523,"replaced":524,"municipality":525,"square":526,"studio":527,"medical":528,"data":529,"african":530,"successful":531,"mid":532,"bay":533,"attack":534,"previous":535,"operations":536,"spanish":537,"theatre":538,"student":539,"republic":540,"beginning":541,"provide":542,"ship":543,"primary":544,"owned":545,"writing":546,"tournament":547,"culture":548,"introduced":549,"texas":550,"related":551,"natural":552,"parts":553,"governor":554,"reached":555,"ireland":556,"units":557,"senior":558,"decided":559,"italian":560,"whose":561,"higher":562,"africa":563,"standard":564,"income":565,"professor":566,"placed":567,"regional":568,"los":569,"buildings":570,"championships":571,"active":572,"novel":573,"energy":574,"generally":575,"interest":576,"via":577,"economic":578,"previously":579,"stated":580,"itself":581,"channel":582,"below":583,"operation":584,"leader":585,"traditional":586,"trade":587,"structure":588,"limited":589,"runs":590,"prior":591,"regular":592,"famous":593,"saint":594,"navy":595,"foreign":596,"listed":597,"artist":598,"catholic":599,"airport":600,"results":601,"parliament":602,"collection":603,"unit":604,"officer":605,"goal":606,"attended":607,"command":608,"staff":609,"commission":610,"lived":611,"location":612,"plays":613,"commercial":614,"places":615,"foundation":616,"significant":617,"older":618,"medal":619,"self":620,"scored":621,"companies":622,"highway":623,"activities":624,"programs":625,"wide":626,"musical":627,"notable":628,"library":629,"numerous":630,"paris":631,"towards":632,"individual":633,"allowed":634,"plant":635,"property":636,"annual":637,"contract":638,"whom":639,"highest":640,"initially":641,"required":642,"earlier":643,"assembly":644,"artists":645,"rural":646,"seat":647,"practice":648,"defeated":649,"ended":650,"soviet":651,"length":652,"spent":653,"manager":654,"press":655,"associated":656,"author":657,"issues":658,"additional":659,"characters":660,"lord":661,"zealand":662,"policy":663,"engine":664,"township":665,"noted":666,"historical":667,"complete":668,"financial":669,"religious":670,"mission":671,"contains":672,"nine":673,"recent":674,"represented":675,"pennsylvania":676,"administration":677,"opening":678,"secretary":679,"lines":680,"report":681,"executive":682,"youth":683,"closed":684,"theory":685,"writer":686,"italy":687,"angeles":688,"appearance":689,"feature":690,"queen":691,"launched":692,"legal":693,"terms":694,"entered":695,"issue":696,"edition":697,"singer":698,"greek":699,"majority":700,"background":701,"source":702,"anti":703,"cultural":704,"complex":705,"changes":706,"recording":707,"stadium":708,"islands":709,"operated":710,"particularly":711,"basketball":712,"month":713,"uses":714,"port":715,"castle":716,"mostly":717,"names":718,"fort":719,"selected":720,"increased":721,"status":722,"earth":723,"subsequently":724,"pacific":725,"cover":726,"variety":727,"certain":728,"goals":729,"remains":730,"upper":731,"congress":732,"becoming":733,"studied":734,"irish":735,"nature":736,"particular":737,"loss":738,"caused":739,"chart":740,"dr.":741,"forced":742,"create":743,"era":744,"retired":745,"material":746,"review":747,"rate":748,"singles":749,"referred":750,"larger":751,"individuals":752,"shown":753,"provides":754,"products":755,"speed":756,"democratic":757,"poland":758,"parish":759,"olympics":760,"cities":761,"themselves":762,"temple":763,"wing":764,"genus":765,"households":766,"serving":767,"cost":768,"wales":769,"stations":770,"passed":771,"supported":772,"view":773,"cases":774,"forms":775,"actor":776,"male":777,"matches":778,"males":779,"stars":780,"tracks":781,"females":782,"administrative":783,"median":784,"effect":785,"biography":786,"train":787,"engineering":788,"camp":789,"offered":790,"chairman":791,"houses":792,"mainly":793,"19th":794,"surface":795,"therefore":796,"nearly":797,"score":798,"ancient":799,"subject":800,"prime":801,"seasons":802,"claimed":803,"experience":804,"specific":805,"jewish":806,"failed":807,"overall":808,"believed":809,"plot":810,"troops":811,"greater":812,"spain":813,"consists":814,"broadcast":815,"heavy":816,"increase":817,"raised":818,"separate":819,"campus":820,"1980s":821,"appears":822,"presented":823,"lies":824,"composed":825,"recently":826,"influence":827,"fifth":828,"nations":829,"creek":830,"references":831,"elections":832,"britain":833,"double":834,"cast":835,"meaning":836,"earned":837,"carried":838,"producer":839,"latter":840,"housing":841,"brothers":842,"attempt":843,"article":844,"response":845,"border":846,"remaining":847,"nearby":848,"direct":849,"ships":850,"value":851,"workers":852,"politician":853,"academic":854,"label":855,"1970s":856,"commander":857,"rule":858,"fellow":859,"residents":860,"authority":861,"editor":862,"transport":863,"dutch":864,"projects":865,"responsible":866,"covered":867,"territory":868,"flight":869,"races":870,"defense":871,"tower":872,"emperor":873,"albums":874,"facilities":875,"daily":876,"stories":877,"assistant":878,"managed":879,"primarily":880,"quality":881,"function":882,"proposed":883,"distribution":884,"conditions":885,"prize":886,"journal":887,"code":888,"vice":889,"newspaper":890,"corps":891,"highly":892,"constructed":893,"mayor":894,"critical":895,"secondary":896,"corporation":897,"rugby":898,"regiment":899,"ohio":900,"appearances":901,"serve":902,"allow":903,"nation":904,"multiple":905,"discovered":906,"directly":907,"scene":908,"levels":909,"growth":910,"elements":911,"acquired":912,"1990s":913,"officers":914,"physical":915,"20th":916,"latin":917,"host":918,"jersey":919,"graduated":920,"arrived":921,"issued":922,"literature":923,"metal":924,"estate":925,"vote":926,"immediately":927,"quickly":928,"asian":929,"competed":930,"extended":931,"produce":932,"urban":933,"1960s":934,"promoted":935,"contemporary":936,"global":937,"formerly":938,"appear":939,"industrial":940,"types":941,"opera":942,"ministry":943,"soldiers":944,"commonly":945,"mass":946,"formation":947,"smaller":948,"typically":949,"drama":950,"shortly":951,"density":952,"senate":953,"effects":954,"iran":955,"polish":956,"prominent":957,"naval":958,"settlement":959,"divided":960,"basis":961,"republican":962,"languages":963,"distance":964,"treatment":965,"continue":966,"product":967,"mile":968,"sources":969,"footballer":970,"format":971,"clubs":972,"leadership":973,"initial":974,"offers":975,"operating":976,"avenue":977,"officially":978,"columbia":979,"grade":980,"squadron":981,"fleet":982,"percent":983,"farm":984,"leaders":985,"agreement":986,"likely":987,"equipment":988,"website":989,"mount":990,"grew":991,"method":992,"transferred":993,"intended":994,"renamed":995,"iron":996,"asia":997,"reserve":998,"capacity":999,"politics":1000,"widely":1001,"activity":1002,"advanced":1003,"relations":1004,"scottish":1005,"dedicated":1006,"crew":1007,"founder":1008,"episodes":1009,"lack":1010,"amount":1011,"build":1012,"efforts":1013,"concept":1014,"follows":1015,"ordered":1016,"leaves":1017,"positive":1018,"economy":1019,"entertainment":1020,"affairs":1021,"memorial":1022,"ability":1023,"illinois":1024,"communities":1025,"color":1026,"text":1027,"railroad":1028,"scientific":1029,"focus":1030,"comedy":1031,"serves":1032,"exchange":1033,"environment":1034,"cars":1035,"direction":1036,"organized":1037,"firm":1038,"description":1039,"agency":1040,"analysis":1041,"purpose":1042,"destroyed":1043,"reception":1044,"planned":1045,"revealed":1046,"infantry":1047,"architecture":1048,"growing":1049,"featuring":1050,"household":1051,"candidate":1052,"removed":1053,"situated":1054,"models":1055,"knowledge":1056,"solo":1057,"technical":1058,"organizations":1059,"assigned":1060,"conducted":1061,"participated":1062,"largely":1063,"purchased":1064,"register":1065,"gained":1066,"combined":1067,"headquarters":1068,"adopted":1069,"potential":1070,"protection":1071,"scale":1072,"approach":1073,"spread":1074,"independence":1075,"mountains":1076,"titled":1077,"geography":1078,"applied":1079,"safety":1080,"mixed":1081,"accepted":1082,"continues":1083,"captured":1084,"rail":1085,"defeat":1086,"principal":1087,"recognized":1088,"lieutenant":1089,"mentioned":1090,"semi":1091,"owner":1092,"joint":1093,"liberal":1094,"actress":1095,"traffic":1096,"creation":1097,"basic":1098,"notes":1099,"unique":1100,"supreme":1101,"declared":1102,"simply":1103,"plants":1104,"sales":1105,"massachusetts":1106,"designated":1107,"parties":1108,"jazz":1109,"compared":1110,"becomes":1111,"resources":1112,"titles":1113,"concert":1114,"learning":1115,"remain":1116,"teaching":1117,"versions":1118,"content":1119,"alongside":1120,"revolution":1121,"sons":1122,"block":1123,"premier":1124,"impact":1125,"champions":1126,"districts":1127,"generation":1128,"estimated":1129,"volume":1130,"image":1131,"sites":1132,"account":1133,"roles":1134,"sport":1135,"quarter":1136,"providing":1137,"zone":1138,"yard":1139,"scoring":1140,"classes":1141,"presence":1142,"performances":1143,"representatives":1144,"hosted":1145,"split":1146,"taught":1147,"origin":1148,"olympic":1149,"claims":1150,"critics":1151,"facility":1152,"occurred":1153,"suffered":1154,"municipal":1155,"damage":1156,"defined":1157,"resulted":1158,"respectively":1159,"expanded":1160,"platform":1161,"draft":1162,"opposition":1163,"expected":1164,"educational":1165,"ontario":1166,"climate":1167,"reports":1168,"atlantic":1169,"surrounding":1170,"performing":1171,"reduced":1172,"ranked":1173,"allows":1174,"birth":1175,"nominated":1176,"younger":1177,"newly":1178,"kong":1179,"positions":1180,"theater":1181,"philadelphia":1182,"heritage":1183,"finals":1184,"disease":1185,"sixth":1186,"laws":1187,"reviews":1188,"constitution":1189,"tradition":1190,"swedish":1191,"theme":1192,"fiction":1193,"rome":1194,"medicine":1195,"trains":1196,"resulting":1197,"existing":1198,"deputy":1199,"environmental":1200,"labour":1201,"classical":1202,"develop":1203,"fans":1204,"granted":1205,"receive":1206,"alternative":1207,"begins":1208,"nuclear":1209,"fame":1210,"buried":1211,"connected":1212,"identified":1213,"palace":1214,"falls":1215,"letters":1216,"combat":1217,"sciences":1218,"effort":1219,"villages":1220,"inspired":1221,"regions":1222,"towns":1223,"conservative":1224,"chosen":1225,"animals":1226,"labor":1227,"attacks":1228,"materials":1229,"yards":1230,"steel":1231,"representative":1232,"orchestra":1233,"peak":1234,"entitled":1235,"officials":1236,"returning":1237,"reference":1238,"northwest":1239,"imperial":1240,"convention":1241,"examples":1242,"ocean":1243,"publication":1244,"painting":1245,"subsequent":1246,"frequently":1247,"religion":1248,"brigade":1249,"fully":1250,"sides":1251,"acts":1252,"cemetery":1253,"relatively":1254,"oldest":1255,"suggested":1256,"succeeded":1257,"achieved":1258,"application":1259,"programme":1260,"cells":1261,"votes":1262,"promotion":1263,"graduate":1264,"armed":1265,"supply":1266,"flying":1267,"communist":1268,"figures":1269,"literary":1270,"netherlands":1271,"korea":1272,"worldwide":1273,"citizens":1274,"1950s":1275,"faculty":1276,"draw":1277,"stock":1278,"seats":1279,"occupied":1280,"methods":1281,"unknown":1282,"articles":1283,"claim":1284,"holds":1285,"authorities":1286,"audience":1287,"sweden":1288,"interview":1289,"obtained":1290,"covers":1291,"settled":1292,"transfer":1293,"marked":1294,"allowing":1295,"funding":1296,"challenge":1297,"southeast":1298,"unlike":1299,"crown":1300,"rise":1301,"portion":1302,"transportation":1303,"sector":1304,"phase":1305,"properties":1306,"edge":1307,"tropical":1308,"standards":1309,"institutions":1310,"philosophy":1311,"legislative":1312,"hills":1313,"brand":1314,"fund":1315,"conflict":1316,"unable":1317,"founding":1318,"refused":1319,"attempts":1320,"metres":1321,"permanent":1322,"starring":1323,"applications":1324,"creating":1325,"effective":1326,"aired":1327,"extensive":1328,"employed":1329,"enemy":1330,"expansion":1331,"billboard":1332,"rank":1333,"battalion":1334,"multi":1335,"vehicle":1336,"fought":1337,"alliance":1338,"category":1339,"perform":1340,"federation":1341,"poetry":1342,"bronze":1343,"bands":1344,"entry":1345,"vehicles":1346,"bureau":1347,"maximum":1348,"billion":1349,"trees":1350,"intelligence":1351,"greatest":1352,"screen":1353,"refers":1354,"commissioned":1355,"gallery":1356,"injury":1357,"confirmed":1358,"setting":1359,"treaty":1360,"adult":1361,"americans":1362,"broadcasting":1363,"supporting":1364,"pilot":1365,"mobile":1366,"writers":1367,"programming":1368,"existence":1369,"squad":1370,"minnesota":1371,"copies":1372,"korean":1373,"provincial":1374,"sets":1375,"defence":1376,"offices":1377,"agricultural":1378,"internal":1379,"core":1380,"northeast":1381,"retirement":1382,"factory":1383,"actions":1384,"prevent":1385,"communications":1386,"ending":1387,"weekly":1388,"containing":1389,"functions":1390,"attempted":1391,"interior":1392,"weight":1393,"bowl":1394,"recognition":1395,"incorporated":1396,"increasing":1397,"ultimately":1398,"documentary":1399,"derived":1400,"attacked":1401,"lyrics":1402,"mexican":1403,"external":1404,"churches":1405,"centuries":1406,"metropolitan":1407,"selling":1408,"opposed":1409,"personnel":1410,"mill":1411,"visited":1412,"presidential":1413,"roads":1414,"pieces":1415,"norwegian":1416,"controlled":1417,"18th":1418,"rear":1419,"influenced":1420,"wrestling":1421,"weapons":1422,"launch":1423,"composer":1424,"locations":1425,"developing":1426,"circuit":1427,"specifically":1428,"studios":1429,"shared":1430,"canal":1431,"wisconsin":1432,"publishing":1433,"approved":1434,"domestic":1435,"consisted":1436,"determined":1437,"comic":1438,"establishment":1439,"exhibition":1440,"southwest":1441,"fuel":1442,"electronic":1443,"cape":1444,"converted":1445,"educated":1446,"melbourne":1447,"hits":1448,"wins":1449,"producing":1450,"norway":1451,"slightly":1452,"occur":1453,"surname":1454,"identity":1455,"represent":1456,"constituency":1457,"funds":1458,"proved":1459,"links":1460,"structures":1461,"athletic":1462,"birds":1463,"contest":1464,"users":1465,"poet":1466,"institution":1467,"display":1468,"receiving":1469,"rare":1470,"contained":1471,"guns":1472,"motion":1473,"piano":1474,"temperature":1475,"publications":1476,"passenger":1477,"contributed":1478,"toward":1479,"cathedral":1480,"inhabitants":1481,"architect":1482,"exist":1483,"athletics":1484,"muslim":1485,"courses":1486,"abandoned":1487,"signal":1488,"successfully":1489,"disambiguation":1490,"tennessee":1491,"dynasty":1492,"heavily":1493,"maryland":1494,"jews":1495,"representing":1496,"budget":1497,"weather":1498,"missouri":1499,"introduction":1500,"faced":1501,"pair":1502,"chapel":1503,"reform":1504,"height":1505,"vietnam":1506,"occurs":1507,"motor":1508,"cambridge":1509,"lands":1510,"focused":1511,"sought":1512,"patients":1513,"shape":1514,"invasion":1515,"chemical":1516,"importance":1517,"communication":1518,"selection":1519,"regarding":1520,"homes":1521,"voivodeship":1522,"maintained":1523,"borough":1524,"failure":1525,"aged":1526,"passing":1527,"agriculture":1528,"oregon":1529,"teachers":1530,"flow":1531,"philippines":1532,"trail":1533,"seventh":1534,"portuguese":1535,"resistance":1536,"reaching":1537,"negative":1538,"fashion":1539,"scheduled":1540,"downtown":1541,"universities":1542,"trained":1543,"skills":1544,"scenes":1545,"views":1546,"notably":1547,"typical":1548,"incident":1549,"candidates":1550,"engines":1551,"decades":1552,"composition":1553,"commune":1554,"chain":1555,"inc.":1556,"austria":1557,"sale":1558,"values":1559,"employees":1560,"chamber":1561,"regarded":1562,"winners":1563,"registered":1564,"task":1565,"investment":1566,"colonial":1567,"swiss":1568,"user":1569,"entirely":1570,"flag":1571,"stores":1572,"closely":1573,"entrance":1574,"laid":1575,"journalist":1576,"coal":1577,"equal":1578,"causes":1579,"turkish":1580,"quebec":1581,"techniques":1582,"promote":1583,"junction":1584,"easily":1585,"dates":1586,"kentucky":1587,"singapore":1588,"residence":1589,"violence":1590,"advance":1591,"survey":1592,"humans":1593,"expressed":1594,"passes":1595,"streets":1596,"distinguished":1597,"qualified":1598,"folk":1599,"establish":1600,"egypt":1601,"artillery":1602,"visual":1603,"improved":1604,"actual":1605,"finishing":1606,"medium":1607,"protein":1608,"switzerland":1609,"productions":1610,"operate":1611,"poverty":1612,"neighborhood":1613,"organisation":1614,"consisting":1615,"consecutive":1616,"sections":1617,"partnership":1618,"extension":1619,"reaction":1620,"factor":1621,"costs":1622,"bodies":1623,"device":1624,"ethnic":1625,"racial":1626,"flat":1627,"objects":1628,"chapter":1629,"improve":1630,"musicians":1631,"courts":1632,"controversy":1633,"membership":1634,"merged":1635,"wars":1636,"expedition":1637,"interests":1638,"arab":1639,"comics":1640,"gain":1641,"describes":1642,"mining":1643,"bachelor":1644,"crisis":1645,"joining":1646,"decade":1647,"1930s":1648,"distributed":1649,"habitat":1650,"routes":1651,"arena":1652,"cycle":1653,"divisions":1654,"briefly":1655,"vocals":1656,"directors":1657,"degrees":1658,"object":1659,"recordings":1660,"installed":1661,"adjacent":1662,"demand":1663,"voted":1664,"causing":1665,"businesses":1666,"ruled":1667,"grounds":1668,"starred":1669,"drawn":1670,"opposite":1671,"stands":1672,"formal":1673,"operates":1674,"persons":1675,"counties":1676,"compete":1677,"wave":1678,"israeli":1679,"ncaa":1680,"resigned":1681,"brief":1682,"greece":1683,"combination":1684,"demographics":1685,"historian":1686,"contain":1687,"commonwealth":1688,"musician":1689,"collected":1690,"argued":1691,"louisiana":1692,"session":1693,"cabinet":1694,"parliamentary":1695,"electoral":1696,"loan":1697,"profit":1698,"regularly":1699,"conservation":1700,"islamic":1701,"purchase":1702,"17th":1703,"charts":1704,"residential":1705,"earliest":1706,"designs":1707,"paintings":1708,"survived":1709,"moth":1710,"items":1711,"goods":1712,"grey":1713,"anniversary":1714,"criticism":1715,"images":1716,"discovery":1717,"observed":1718,"underground":1719,"progress":1720,"additionally":1721,"participate":1722,"thousands":1723,"reduce":1724,"elementary":1725,"owners":1726,"stating":1727,"iraq":1728,"resolution":1729,"capture":1730,"tank":1731,"rooms":1732,"hollywood":1733,"finance":1734,"queensland":1735,"reign":1736,"maintain":1737,"iowa":1738,"landing":1739,"broad":1740,"outstanding":1741,"circle":1742,"path":1743,"manufacturing":1744,"assistance":1745,"sequence":1746,"gmina":1747,"crossing":1748,"leads":1749,"universal":1750,"shaped":1751,"kings":1752,"attached":1753,"medieval":1754,"ages":1755,"metro":1756,"colony":1757,"affected":1758,"scholars":1759,"oklahoma":1760,"coastal":1761,"soundtrack":1762,"painted":1763,"attend":1764,"definition":1765,"meanwhile":1766,"purposes":1767,"trophy":1768,"require":1769,"marketing":1770,"popularity":1771,"cable":1772,"mathematics":1773,"mississippi":1774,"represents":1775,"scheme":1776,"appeal":1777,"distinct":1778,"factors":1779,"acid":1780,"subjects":1781,"roughly":1782,"terminal":1783,"economics":1784,"senator":1785,"diocese":1786,"prix":1787,"contrast":1788,"argentina":1789,"czech":1790,"wings":1791,"relief":1792,"stages":1793,"duties":1794,"16th":1795,"novels":1796,"accused":1797,"whilst":1798,"equivalent":1799,"charged":1800,"measure":1801,"documents":1802,"couples":1803,"request":1804,"danish":1805,"defensive":1806,"guide":1807,"devices":1808,"statistics":1809,"credited":1810,"tries":1811,"passengers":1812,"allied":1813,"frame":1814,"puerto":1815,"peninsula":1816,"concluded":1817,"instruments":1818,"wounded":1819,"differences":1820,"associate":1821,"forests":1822,"afterwards":1823,"replace":1824,"requirements":1825,"aviation":1826,"solution":1827,"offensive":1828,"ownership":1829,"inner":1830,"legislation":1831,"hungarian":1832,"contributions":1833,"actors":1834,"translated":1835,"denmark":1836,"steam":1837,"depending":1838,"aspects":1839,"assumed":1840,"injured":1841,"severe":1842,"admitted":1843,"determine":1844,"shore":1845,"technique":1846,"arrival":1847,"measures":1848,"translation":1849,"debuted":1850,"delivered":1851,"returns":1852,"rejected":1853,"separated":1854,"visitors":1855,"damaged":1856,"storage":1857,"accompanied":1858,"markets":1859,"industries":1860,"losses":1861,"gulf":1862,"charter":1863,"strategy":1864,"corporate":1865,"socialist":1866,"somewhat":1867,"significantly":1868,"physics":1869,"mounted":1870,"satellite":1871,"experienced":1872,"constant":1873,"relative":1874,"pattern":1875,"restored":1876,"belgium":1877,"connecticut":1878,"partners":1879,"harvard":1880,"retained":1881,"networks":1882,"protected":1883,"mode":1884,"artistic":1885,"parallel":1886,"collaboration":1887,"debate":1888,"involving":1889,"journey":1890,"linked":1891,"salt":1892,"authors":1893,"components":1894,"context":1895,"occupation":1896,"requires":1897,"occasionally":1898,"policies":1899,"tamil":1900,"ottoman":1901,"revolutionary":1902,"hungary":1903,"poem":1904,"versus":1905,"gardens":1906,"amongst":1907,"audio":1908,"makeup":1909,"frequency":1910,"meters":1911,"orthodox":1912,"continuing":1913,"suggests":1914,"legislature":1915,"coalition":1916,"guitarist":1917,"eighth":1918,"classification":1919,"practices":1920,"soil":1921,"tokyo":1922,"instance":1923,"limit":1924,"coverage":1925,"considerable":1926,"ranking":1927,"colleges":1928,"cavalry":1929,"centers":1930,"daughters":1931,"twin":1932,"equipped":1933,"broadway":1934,"narrow":1935,"hosts":1936,"rates":1937,"domain":1938,"boundary":1939,"arranged":1940,"12th":1941,"whereas":1942,"brazilian":1943,"forming":1944,"rating":1945,"strategic":1946,"competitions":1947,"trading":1948,"covering":1949,"baltimore":1950,"commissioner":1951,"infrastructure":1952,"origins":1953,"replacement":1954,"praised":1955,"disc":1956,"collections":1957,"expression":1958,"ukraine":1959,"driven":1960,"edited":1961,"austrian":1962,"solar":1963,"ensure":1964,"premiered":1965,"successor":1966,"wooden":1967,"operational":1968,"hispanic":1969,"concerns":1970,"rapid":1971,"prisoners":1972,"childhood":1973,"meets":1974,"influential":1975,"tunnel":1976,"employment":1977,"tribe":1978,"qualifying":1979,"adapted":1980,"temporary":1981,"celebrated":1982,"appearing":1983,"increasingly":1984,"depression":1985,"adults":1986,"cinema":1987,"entering":1988,"laboratory":1989,"script":1990,"flows":1991,"romania":1992,"accounts":1993,"fictional":1994,"pittsburgh":1995,"achieve":1996,"monastery":1997,"franchise":1998,"formally":1999,"tools":2000,"newspapers":2001,"revival":2002,"sponsored":2003,"processes":2004,"vienna":2005,"springs":2006,"missions":2007,"classified":2008,"13th":2009,"annually":2010,"branches":2011,"lakes":2012,"gender":2013,"manner":2014,"advertising":2015,"normally":2016,"maintenance":2017,"adding":2018,"characteristics":2019,"integrated":2020,"decline":2021,"modified":2022,"strongly":2023,"critic":2024,"victims":2025,"malaysia":2026,"arkansas":2027,"nazi":2028,"restoration":2029,"powered":2030,"monument":2031,"hundreds":2032,"depth":2033,"15th":2034,"controversial":2035,"admiral":2036,"criticized":2037,"brick":2038,"honorary":2039,"initiative":2040,"output":2041,"visiting":2042,"birmingham":2043,"progressive":2044,"existed":2045,"carbon":2046,"1920s":2047,"credits":2048,"colour":2049,"rising":2050,"hence":2051,"defeating":2052,"superior":2053,"filmed":2054,"listing":2055,"column":2056,"surrounded":2057,"orleans":2058,"principles":2059,"territories":2060,"struck":2061,"participation":2062,"indonesia":2063,"movements":2064,"index":2065,"commerce":2066,"conduct":2067,"constitutional":2068,"spiritual":2069,"ambassador":2070,"vocal":2071,"completion":2072,"edinburgh":2073,"residing":2074,"tourism":2075,"finland":2076,"bears":2077,"medals":2078,"resident":2079,"themes":2080,"visible":2081,"indigenous":2082,"involvement":2083,"basin":2084,"electrical":2085,"ukrainian":2086,"concerts":2087,"boats":2088,"styles":2089,"processing":2090,"rival":2091,"drawing":2092,"vessels":2093,"experimental":2094,"declined":2095,"touring":2096,"supporters":2097,"compilation":2098,"coaching":2099,"cited":2100,"dated":2101,"roots":2102,"string":2103,"explained":2104,"transit":2105,"traditionally":2106,"poems":2107,"minimum":2108,"representation":2109,"14th":2110,"releases":2111,"effectively":2112,"architectural":2113,"triple":2114,"indicated":2115,"greatly":2116,"elevation":2117,"clinical":2118,"printed":2119,"10th":2120,"proposal":2121,"peaked":2122,"producers":2123,"romanized":2124,"rapidly":2125,"stream":2126,"innings":2127,"meetings":2128,"counter":2129,"householder":2130,"honour":2131,"lasted":2132,"agencies":2133,"document":2134,"exists":2135,"surviving":2136,"experiences":2137,"honors":2138,"landscape":2139,"hurricane":2140,"harbor":2141,"panel":2142,"competing":2143,"profile":2144,"vessel":2145,"farmers":2146,"lists":2147,"revenue":2148,"exception":2149,"customers":2150,"11th":2151,"participants":2152,"wildlife":2153,"utah":2154,"bible":2155,"gradually":2156,"preserved":2157,"replacing":2158,"symphony":2159,"begun":2160,"longest":2161,"siege":2162,"provinces":2163,"mechanical":2164,"genre":2165,"transmission":2166,"agents":2167,"executed":2168,"videos":2169,"benefits":2170,"funded":2171,"rated":2172,"instrumental":2173,"ninth":2174,"similarly":2175,"dominated":2176,"destruction":2177,"passage":2178,"technologies":2179,"thereafter":2180,"outer":2181,"facing":2182,"affiliated":2183,"opportunities":2184,"instrument":2185,"governments":2186,"scholar":2187,"evolution":2188,"channels":2189,"shares":2190,"sessions":2191,"widespread":2192,"occasions":2193,"engineers":2194,"scientists":2195,"signing":2196,"battery":2197,"competitive":2198,"alleged":2199,"eliminated":2200,"supplies":2201,"judges":2202,"hampshire":2203,"regime":2204,"portrayed":2205,"penalty":2206,"taiwan":2207,"denied":2208,"submarine":2209,"scholarship":2210,"substantial":2211,"transition":2212,"victorian":2213,"http":2214,"nevertheless":2215,"filed":2216,"supports":2217,"continental":2218,"tribes":2219,"ratio":2220,"doubles":2221,"useful":2222,"honours":2223,"blocks":2224,"principle":2225,"retail":2226,"departure":2227,"ranks":2228,"patrol":2229,"yorkshire":2230,"vancouver":2231,"inter":2232,"extent":2233,"afghanistan":2234,"strip":2235,"railways":2236,"component":2237,"organ":2238,"symbol":2239,"categories":2240,"encouraged":2241,"abroad":2242,"civilian":2243,"periods":2244,"traveled":2245,"writes":2246,"struggle":2247,"immediate":2248,"recommended":2249,"adaptation":2250,"egyptian":2251,"graduating":2252,"assault":2253,"drums":2254,"nomination":2255,"historically":2256,"voting":2257,"allies":2258,"detailed":2259,"achievement":2260,"percentage":2261,"arabic":2262,"assist":2263,"frequent":2264,"toured":2265,"apply":2266,"and\/or":2267,"intersection":2268,"maine":2269,"touchdown":2270,"throne":2271,"produces":2272,"contribution":2273,"emerged":2274,"obtain":2275,"archbishop":2276,"seek":2277,"researchers":2278,"remainder":2279,"populations":2280,"clan":2281,"finnish":2282,"overseas":2283,"fifa":2284,"licensed":2285,"chemistry":2286,"festivals":2287,"mediterranean":2288,"injuries":2289,"animated":2290,"seeking":2291,"publisher":2292,"volumes":2293,"limits":2294,"venue":2295,"jerusalem":2296,"generated":2297,"trials":2298,"islam":2299,"youngest":2300,"ruling":2301,"glasgow":2302,"germans":2303,"songwriter":2304,"persian":2305,"municipalities":2306,"donated":2307,"viewed":2308,"belgian":2309,"cooperation":2310,"posted":2311,"tech":2312,"dual":2313,"volunteer":2314,"settlers":2315,"commanded":2316,"claiming":2317,"approval":2318,"delhi":2319,"usage":2320,"terminus":2321,"partly":2322,"electricity":2323,"locally":2324,"editions":2325,"premiere":2326,"absence":2327,"belief":2328,"traditions":2329,"statue":2330,"indicate":2331,"manor":2332,"stable":2333,"attributed":2334,"possession":2335,"managing":2336,"viewers":2337,"chile":2338,"overview":2339,"seed":2340,"regulations":2341,"essential":2342,"minority":2343,"cargo":2344,"segment":2345,"endemic":2346,"forum":2347,"deaths":2348,"monthly":2349,"playoffs":2350,"erected":2351,"practical":2352,"machines":2353,"suburb":2354,"relation":2355,"mrs.":2356,"descent":2357,"indoor":2358,"continuous":2359,"characterized":2360,"solutions":2361,"caribbean":2362,"rebuilt":2363,"serbian":2364,"summary":2365,"contested":2366,"psychology":2367,"pitch":2368,"attending":2369,"muhammad":2370,"tenure":2371,"drivers":2372,"diameter":2373,"assets":2374,"venture":2375,"punk":2376,"airlines":2377,"concentration":2378,"athletes":2379,"volunteers":2380,"pages":2381,"mines":2382,"influences":2383,"sculpture":2384,"protest":2385,"ferry":2386,"behalf":2387,"drafted":2388,"apparent":2389,"furthermore":2390,"ranging":2391,"romanian":2392,"democracy":2393,"lanka":2394,"significance":2395,"linear":2396,"d.c.":2397,"certified":2398,"voters":2399,"recovered":2400,"tours":2401,"demolished":2402,"boundaries":2403,"assisted":2404,"identify":2405,"grades":2406,"elsewhere":2407,"mechanism":2408,"1940s":2409,"reportedly":2410,"aimed":2411,"conversion":2412,"suspended":2413,"photography":2414,"departments":2415,"beijing":2416,"locomotives":2417,"publicly":2418,"dispute":2419,"magazines":2420,"resort":2421,"conventional":2422,"platforms":2423,"internationally":2424,"capita":2425,"settlements":2426,"dramatic":2427,"derby":2428,"establishing":2429,"involves":2430,"statistical":2431,"implementation":2432,"immigrants":2433,"exposed":2434,"diverse":2435,"layer":2436,"vast":2437,"ceased":2438,"connections":2439,"belonged":2440,"interstate":2441,"uefa":2442,"organised":2443,"abuse":2444,"deployed":2445,"cattle":2446,"partially":2447,"filming":2448,"mainstream":2449,"reduction":2450,"automatic":2451,"rarely":2452,"subsidiary":2453,"decides":2454,"merger":2455,"comprehensive":2456,"displayed":2457,"amendment":2458,"guinea":2459,"exclusively":2460,"manhattan":2461,"concerning":2462,"commons":2463,"radical":2464,"serbia":2465,"baptist":2466,"buses":2467,"initiated":2468,"portrait":2469,"harbour":2470,"choir":2471,"citizen":2472,"sole":2473,"unsuccessful":2474,"manufactured":2475,"enforcement":2476,"connecting":2477,"increases":2478,"patterns":2479,"sacred":2480,"muslims":2481,"clothing":2482,"hindu":2483,"unincorporated":2484,"sentenced":2485,"advisory":2486,"tanks":2487,"campaigns":2488,"fled":2489,"repeated":2490,"remote":2491,"rebellion":2492,"implemented":2493,"texts":2494,"fitted":2495,"tribute":2496,"writings":2497,"sufficient":2498,"ministers":2499,"21st":2500,"devoted":2501,"jurisdiction":2502,"coaches":2503,"interpretation":2504,"pole":2505,"businessman":2506,"peru":2507,"sporting":2508,"prices":2509,"cuba":2510,"relocated":2511,"opponent":2512,"arrangement":2513,"elite":2514,"manufacturer":2515,"responded":2516,"suitable":2517,"distinction":2518,"calendar":2519,"dominant":2520,"tourist":2521,"earning":2522,"prefecture":2523,"ties":2524,"preparation":2525,"anglo":2526,"pursue":2527,"worship":2528,"archaeological":2529,"chancellor":2530,"bangladesh":2531,"scores":2532,"traded":2533,"lowest":2534,"horror":2535,"outdoor":2536,"biology":2537,"commented":2538,"specialized":2539,"loop":2540,"arriving":2541,"farming":2542,"housed":2543,"historians":2544,"'the":2545,"patent":2546,"pupils":2547,"christianity":2548,"opponents":2549,"athens":2550,"northwestern":2551,"maps":2552,"promoting":2553,"reveals":2554,"flights":2555,"exclusive":2556,"lions":2557,"norfolk":2558,"hebrew":2559,"extensively":2560,"eldest":2561,"shops":2562,"acquisition":2563,"virtual":2564,"renowned":2565,"margin":2566,"ongoing":2567,"essentially":2568,"iranian":2569,"alternate":2570,"sailed":2571,"reporting":2572,"conclusion":2573,"originated":2574,"temperatures":2575,"exposure":2576,"secured":2577,"landed":2578,"rifle":2579,"framework":2580,"identical":2581,"martial":2582,"focuses":2583,"topics":2584,"ballet":2585,"fighters":2586,"belonging":2587,"wealthy":2588,"negotiations":2589,"evolved":2590,"bases":2591,"oriented":2592,"acres":2593,"democrat":2594,"heights":2595,"restricted":2596,"vary":2597,"graduation":2598,"aftermath":2599,"chess":2600,"illness":2601,"participating":2602,"vertical":2603,"collective":2604,"immigration":2605,"demonstrated":2606,"leaf":2607,"completing":2608,"organic":2609,"missile":2610,"leeds":2611,"eligible":2612,"grammar":2613,"confederate":2614,"improvement":2615,"congressional":2616,"wealth":2617,"cincinnati":2618,"spaces":2619,"indicates":2620,"corresponding":2621,"reaches":2622,"repair":2623,"isolated":2624,"taxes":2625,"congregation":2626,"ratings":2627,"leagues":2628,"diplomatic":2629,"submitted":2630,"winds":2631,"awareness":2632,"photographs":2633,"maritime":2634,"nigeria":2635,"accessible":2636,"animation":2637,"restaurants":2638,"philippine":2639,"inaugural":2640,"dismissed":2641,"armenian":2642,"illustrated":2643,"reservoir":2644,"speakers":2645,"programmes":2646,"resource":2647,"genetic":2648,"interviews":2649,"camps":2650,"regulation":2651,"computers":2652,"preferred":2653,"travelled":2654,"comparison":2655,"distinctive":2656,"recreation":2657,"requested":2658,"southeastern":2659,"dependent":2660,"brisbane":2661,"breeding":2662,"playoff":2663,"expand":2664,"bonus":2665,"gauge":2666,"departed":2667,"qualification":2668,"inspiration":2669,"shipping":2670,"slaves":2671,"variations":2672,"shield":2673,"theories":2674,"munich":2675,"recognised":2676,"emphasis":2677,"favour":2678,"variable":2679,"seeds":2680,"undergraduate":2681,"territorial":2682,"intellectual":2683,"qualify":2684,"mini":2685,"banned":2686,"pointed":2687,"democrats":2688,"assessment":2689,"judicial":2690,"examination":2691,"attempting":2692,"objective":2693,"partial":2694,"characteristic":2695,"hardware":2696,"pradesh":2697,"execution":2698,"ottawa":2699,"metre":2700,"drum":2701,"exhibitions":2702,"withdrew":2703,"attendance":2704,"phrase":2705,"journalism":2706,"logo":2707,"measured":2708,"error":2709,"christians":2710,"trio":2711,"protestant":2712,"theology":2713,"respective":2714,"atmosphere":2715,"buddhist":2716,"substitute":2717,"curriculum":2718,"fundamental":2719,"outbreak":2720,"rabbi":2721,"intermediate":2722,"designation":2723,"globe":2724,"liberation":2725,"simultaneously":2726,"diseases":2727,"experiments":2728,"locomotive":2729,"difficulties":2730,"mainland":2731,"nepal":2732,"relegated":2733,"contributing":2734,"database":2735,"developments":2736,"veteran":2737,"carries":2738,"ranges":2739,"instruction":2740,"lodge":2741,"protests":2742,"obama":2743,"newcastle":2744,"experiment":2745,"physician":2746,"describing":2747,"challenges":2748,"corruption":2749,"delaware":2750,"adventures":2751,"ensemble":2752,"succession":2753,"renaissance":2754,"tenth":2755,"altitude":2756,"receives":2757,"approached":2758,"crosses":2759,"syria":2760,"croatia":2761,"warsaw":2762,"professionals":2763,"improvements":2764,"worn":2765,"airline":2766,"compound":2767,"permitted":2768,"preservation":2769,"reducing":2770,"printing":2771,"scientist":2772,"activist":2773,"comprises":2774,"sized":2775,"societies":2776,"enters":2777,"ruler":2778,"gospel":2779,"earthquake":2780,"extend":2781,"autonomous":2782,"croatian":2783,"serial":2784,"decorated":2785,"relevant":2786,"ideal":2787,"grows":2788,"grass":2789,"tier":2790,"towers":2791,"wider":2792,"welfare":2793,"columns":2794,"alumni":2795,"descendants":2796,"interface":2797,"reserves":2798,"banking":2799,"colonies":2800,"manufacturers":2801,"magnetic":2802,"closure":2803,"pitched":2804,"vocalist":2805,"preserve":2806,"enrolled":2807,"cancelled":2808,"equation":2809,"2000s":2810,"nickname":2811,"bulgaria":2812,"heroes":2813,"exile":2814,"mathematical":2815,"demands":2816,"input":2817,"structural":2818,"tube":2819,"stem":2820,"approaches":2821,"argentine":2822,"axis":2823,"manuscript":2824,"inherited":2825,"depicted":2826,"targets":2827,"visits":2828,"veterans":2829,"regard":2830,"removal":2831,"efficiency":2832,"organisations":2833,"concepts":2834,"lebanon":2835,"manga":2836,"petersburg":2837,"rally":2838,"supplied":2839,"amounts":2840,"yale":2841,"tournaments":2842,"broadcasts":2843,"signals":2844,"pilots":2845,"azerbaijan":2846,"architects":2847,"enzyme":2848,"literacy":2849,"declaration":2850,"placing":2851,"batting":2852,"incumbent":2853,"bulgarian":2854,"consistent":2855,"poll":2856,"defended":2857,"landmark":2858,"southwestern":2859,"raid":2860,"resignation":2861,"travels":2862,"casualties":2863,"prestigious":2864,"namely":2865,"aims":2866,"recipient":2867,"warfare":2868,"readers":2869,"collapse":2870,"coached":2871,"controls":2872,"volleyball":2873,"coup":2874,"lesser":2875,"verse":2876,"pairs":2877,"exhibited":2878,"proteins":2879,"molecular":2880,"abilities":2881,"integration":2882,"consist":2883,"aspect":2884,"advocate":2885,"administered":2886,"governing":2887,"hospitals":2888,"commenced":2889,"coins":2890,"lords":2891,"variation":2892,"resumed":2893,"canton":2894,"artificial":2895,"elevated":2896,"palm":2897,"difficulty":2898,"civic":2899,"efficient":2900,"northeastern":2901,"inducted":2902,"radiation":2903,"affiliate":2904,"boards":2905,"stakes":2906,"byzantine":2907,"consumption":2908,"freight":2909,"interaction":2910,"oblast":2911,"numbered":2912,"seminary":2913,"contracts":2914,"extinct":2915,"predecessor":2916,"bearing":2917,"cultures":2918,"functional":2919,"neighboring":2920,"revised":2921,"cylinder":2922,"grants":2923,"narrative":2924,"reforms":2925,"athlete":2926,"tales":2927,"reflect":2928,"presidency":2929,"compositions":2930,"specialist":2931,"cricketer":2932,"founders":2933,"sequel":2934,"widow":2935,"disbanded":2936,"associations":2937,"backed":2938,"thereby":2939,"pitcher":2940,"commanding":2941,"boulevard":2942,"singers":2943,"crops":2944,"militia":2945,"reviewed":2946,"centres":2947,"waves":2948,"consequently":2949,"fortress":2950,"tributary":2951,"portions":2952,"bombing":2953,"excellence":2954,"nest":2955,"payment":2956,"mars":2957,"plaza":2958,"unity":2959,"victories":2960,"scotia":2961,"farms":2962,"nominations":2963,"variant":2964,"attacking":2965,"suspension":2966,"installation":2967,"graphics":2968,"estates":2969,"comments":2970,"acoustic":2971,"destination":2972,"venues":2973,"surrender":2974,"retreat":2975,"libraries":2976,"quarterback":2977,"customs":2978,"berkeley":2979,"collaborated":2980,"gathered":2981,"syndrome":2982,"dialogue":2983,"recruited":2984,"shanghai":2985,"neighbouring":2986,"psychological":2987,"saudi":2988,"moderate":2989,"exhibit":2990,"innovation":2991,"depot":2992,"binding":2993,"brunswick":2994,"situations":2995,"certificate":2996,"actively":2997,"shakespeare":2998,"editorial":2999,"presentation":3000,"ports":3001,"relay":3002,"nationalist":3003,"methodist":3004,"archives":3005,"experts":3006,"maintains":3007,"collegiate":3008,"bishops":3009,"maintaining":3010,"temporarily":3011,"embassy":3012,"essex":3013,"wellington":3014,"connects":3015,"reformed":3016,"bengal":3017,"recalled":3018,"inches":3019,"doctrine":3020,"deemed":3021,"legendary":3022,"reconstruction":3023,"statements":3024,"palestinian":3025,"meter":3026,"achievements":3027,"riders":3028,"interchange":3029,"spots":3030,"auto":3031,"accurate":3032,"chorus":3033,"dissolved":3034,"missionary":3035,"thai":3036,"operators":3037,"e.g.":3038,"generations":3039,"failing":3040,"delayed":3041,"cork":3042,"nashville":3043,"perceived":3044,"venezuela":3045,"cult":3046,"emerging":3047,"tomb":3048,"abolished":3049,"documented":3050,"gaining":3051,"canyon":3052,"episcopal":3053,"stored":3054,"assists":3055,"compiled":3056,"kerala":3057,"kilometers":3058,"mosque":3059,"grammy":3060,"theorem":3061,"unions":3062,"segments":3063,"glacier":3064,"arrives":3065,"theatrical":3066,"circulation":3067,"conferences":3068,"chapters":3069,"displays":3070,"circular":3071,"authored":3072,"conductor":3073,"fewer":3074,"dimensional":3075,"nationwide":3076,"liga":3077,"yugoslavia":3078,"peer":3079,"vietnamese":3080,"fellowship":3081,"armies":3082,"regardless":3083,"relating":3084,"dynamic":3085,"politicians":3086,"mixture":3087,"serie":3088,"somerset":3089,"imprisoned":3090,"posts":3091,"beliefs":3092,"beta":3093,"layout":3094,"independently":3095,"electronics":3096,"provisions":3097,"fastest":3098,"logic":3099,"headquartered":3100,"creates":3101,"challenged":3102,"beaten":3103,"appeals":3104,"plains":3105,"protocol":3106,"graphic":3107,"accommodate":3108,"iraqi":3109,"midfielder":3110,"span":3111,"commentary":3112,"freestyle":3113,"reflected":3114,"palestine":3115,"lighting":3116,"burial":3117,"virtually":3118,"backing":3119,"prague":3120,"tribal":3121,"heir":3122,"identification":3123,"prototype":3124,"criteria":3125,"dame":3126,"arch":3127,"tissue":3128,"footage":3129,"extending":3130,"procedures":3131,"predominantly":3132,"updated":3133,"rhythm":3134,"preliminary":3135,"cafe":3136,"disorder":3137,"prevented":3138,"suburbs":3139,"discontinued":3140,"retiring":3141,"oral":3142,"followers":3143,"extends":3144,"massacre":3145,"journalists":3146,"conquest":3147,"larvae":3148,"pronounced":3149,"behaviour":3150,"diversity":3151,"sustained":3152,"addressed":3153,"geographic":3154,"restrictions":3155,"voiced":3156,"milwaukee":3157,"dialect":3158,"quoted":3159,"grid":3160,"nationally":3161,"nearest":3162,"roster":3163,"twentieth":3164,"separation":3165,"indies":3166,"manages":3167,"citing":3168,"intervention":3169,"guidance":3170,"severely":3171,"migration":3172,"artwork":3173,"focusing":3174,"rivals":3175,"trustees":3176,"varied":3177,"enabled":3178,"committees":3179,"centered":3180,"skating":3181,"slavery":3182,"cardinals":3183,"forcing":3184,"tasks":3185,"auckland":3186,"youtube":3187,"argues":3188,"colored":3189,"advisor":3190,"mumbai":3191,"requiring":3192,"theological":3193,"registration":3194,"refugees":3195,"nineteenth":3196,"survivors":3197,"runners":3198,"colleagues":3199,"priests":3200,"contribute":3201,"variants":3202,"workshop":3203,"concentrated":3204,"creator":3205,"lectures":3206,"temples":3207,"exploration":3208,"requirement":3209,"interactive":3210,"navigation":3211,"companion":3212,"perth":3213,"allegedly":3214,"releasing":3215,"citizenship":3216,"observation":3217,"stationed":3218,"ph.d.":3219,"sheep":3220,"breed":3221,"discovers":3222,"encourage":3223,"kilometres":3224,"journals":3225,"performers":3226,"isle":3227,"saskatchewan":3228,"hybrid":3229,"hotels":3230,"lancashire":3231,"dubbed":3232,"airfield":3233,"anchor":3234,"suburban":3235,"theoretical":3236,"sussex":3237,"anglican":3238,"stockholm":3239,"permanently":3240,"upcoming":3241,"privately":3242,"receiver":3243,"optical":3244,"highways":3245,"congo":3246,"colours":3247,"aggregate":3248,"authorized":3249,"repeatedly":3250,"varies":3251,"fluid":3252,"innovative":3253,"transformed":3254,"praise":3255,"convoy":3256,"demanded":3257,"discography":3258,"attraction":3259,"export":3260,"audiences":3261,"ordained":3262,"enlisted":3263,"occasional":3264,"westminster":3265,"syrian":3266,"heavyweight":3267,"bosnia":3268,"consultant":3269,"eventual":3270,"improving":3271,"aires":3272,"wickets":3273,"epic":3274,"reactions":3275,"scandal":3276,"i.e.":3277,"discrimination":3278,"buenos":3279,"patron":3280,"investors":3281,"conjunction":3282,"testament":3283,"construct":3284,"encountered":3285,"celebrity":3286,"expanding":3287,"georgian":3288,"brands":3289,"retain":3290,"underwent":3291,"algorithm":3292,"foods":3293,"provision":3294,"orbit":3295,"transformation":3296,"associates":3297,"tactical":3298,"compact":3299,"varieties":3300,"stability":3301,"refuge":3302,"gathering":3303,"moreover":3304,"manila":3305,"configuration":3306,"gameplay":3307,"discipline":3308,"entity":3309,"comprising":3310,"composers":3311,"skill":3312,"monitoring":3313,"ruins":3314,"museums":3315,"sustainable":3316,"aerial":3317,"altered":3318,"codes":3319,"voyage":3320,"friedrich":3321,"conflicts":3322,"storyline":3323,"travelling":3324,"conducting":3325,"merit":3326,"indicating":3327,"referendum":3328,"currency":3329,"encounter":3330,"particles":3331,"automobile":3332,"workshops":3333,"acclaimed":3334,"inhabited":3335,"doctorate":3336,"cuban":3337,"phenomenon":3338,"dome":3339,"enrollment":3340,"tobacco":3341,"governance":3342,"trend":3343,"equally":3344,"manufacture":3345,"hydrogen":3346,"grande":3347,"compensation":3348,"download":3349,"pianist":3350,"grain":3351,"shifted":3352,"neutral":3353,"evaluation":3354,"define":3355,"cycling":3356,"seized":3357,"array":3358,"relatives":3359,"motors":3360,"firms":3361,"varying":3362,"automatically":3363,"restore":3364,"nicknamed":3365,"findings":3366,"governed":3367,"investigate":3368,"manitoba":3369,"administrator":3370,"vital":3371,"integral":3372,"indonesian":3373,"confusion":3374,"publishers":3375,"enable":3376,"geographical":3377,"inland":3378,"naming":3379,"civilians":3380,"reconnaissance":3381,"indianapolis":3382,"lecturer":3383,"deer":3384,"tourists":3385,"exterior":3386,"rhode":3387,"bassist":3388,"symbols":3389,"scope":3390,"ammunition":3391,"yuan":3392,"poets":3393,"punjab":3394,"nursing":3395,"cent":3396,"developers":3397,"estimates":3398,"presbyterian":3399,"nasa":3400,"holdings":3401,"generate":3402,"renewed":3403,"computing":3404,"cyprus":3405,"arabia":3406,"duration":3407,"compounds":3408,"gastropod":3409,"permit":3410,"valid":3411,"touchdowns":3412,"facade":3413,"interactions":3414,"mineral":3415,"practiced":3416,"allegations":3417,"consequence":3418,"goalkeeper":3419,"baronet":3420,"copyright":3421,"uprising":3422,"carved":3423,"targeted":3424,"competitors":3425,"mentions":3426,"sanctuary":3427,"fees":3428,"pursued":3429,"tampa":3430,"chronicle":3431,"capabilities":3432,"specified":3433,"specimens":3434,"toll":3435,"accounting":3436,"limestone":3437,"staged":3438,"upgraded":3439,"philosophical":3440,"streams":3441,"guild":3442,"revolt":3443,"rainfall":3444,"supporter":3445,"princeton":3446,"terrain":3447,"hometown":3448,"probability":3449,"assembled":3450,"paulo":3451,"surrey":3452,"voltage":3453,"developer":3454,"destroyer":3455,"floors":3456,"lineup":3457,"curve":3458,"prevention":3459,"potentially":3460,"onwards":3461,"trips":3462,"imposed":3463,"hosting":3464,"striking":3465,"strict":3466,"admission":3467,"apartments":3468,"solely":3469,"utility":3470,"proceeded":3471,"observations":3472,"euro":3473,"incidents":3474,"vinyl":3475,"profession":3476,"haven":3477,"distant":3478,"expelled":3479,"rivalry":3480,"runway":3481,"torpedo":3482,"zones":3483,"shrine":3484,"dimensions":3485,"investigations":3486,"lithuania":3487,"idaho":3488,"pursuit":3489,"copenhagen":3490,"considerably":3491,"locality":3492,"wireless":3493,"decrease":3494,"genes":3495,"thermal":3496,"deposits":3497,"hindi":3498,"habitats":3499,"withdrawn":3500,"biblical":3501,"monuments":3502,"casting":3503,"plateau":3504,"thesis":3505,"managers":3506,"flooding":3507,"assassination":3508,"acknowledged":3509,"interim":3510,"inscription":3511,"guided":3512,"pastor":3513,"finale":3514,"insects":3515,"transported":3516,"activists":3517,"marshal":3518,"intensity":3519,"airing":3520,"cardiff":3521,"proposals":3522,"lifestyle":3523,"prey":3524,"herald":3525,"capitol":3526,"aboriginal":3527,"measuring":3528,"lasting":3529,"interpreted":3530,"occurring":3531,"desired":3532,"drawings":3533,"healthcare":3534,"panels":3535,"elimination":3536,"oslo":3537,"ghana":3538,"blog":3539,"sabha":3540,"intent":3541,"superintendent":3542,"governors":3543,"bankruptcy":3544,"p.m.":3545,"equity":3546,"disk":3547,"layers":3548,"slovenia":3549,"prussia":3550,"quartet":3551,"mechanics":3552,"graduates":3553,"politically":3554,"monks":3555,"screenplay":3556,"nato":3557,"absorbed":3558,"topped":3559,"petition":3560,"bold":3561,"morocco":3562,"exhibits":3563,"canterbury":3564,"publish":3565,"rankings":3566,"crater":3567,"dominican":3568,"enhanced":3569,"planes":3570,"lutheran":3571,"governmental":3572,"joins":3573,"collecting":3574,"brussels":3575,"unified":3576,"streak":3577,"strategies":3578,"flagship":3579,"surfaces":3580,"oval":3581,"archive":3582,"etymology":3583,"imprisonment":3584,"instructor":3585,"noting":3586,"remix":3587,"opposing":3588,"servant":3589,"rotation":3590,"width":3591,"trans":3592,"maker":3593,"synthesis":3594,"excess":3595,"tactics":3596,"snail":3597,"ltd.":3598,"lighthouse":3599,"sequences":3600,"cornwall":3601,"plantation":3602,"mythology":3603,"performs":3604,"foundations":3605,"populated":3606,"horizontal":3607,"speedway":3608,"activated":3609,"performer":3610,"diving":3611,"conceived":3612,"edmonton":3613,"subtropical":3614,"environments":3615,"prompted":3616,"semifinals":3617,"caps":3618,"bulk":3619,"treasury":3620,"recreational":3621,"telegraph":3622,"continent":3623,"portraits":3624,"relegation":3625,"catholics":3626,"graph":3627,"velocity":3628,"rulers":3629,"endangered":3630,"secular":3631,"observer":3632,"learns":3633,"inquiry":3634,"idol":3635,"dictionary":3636,"certification":3637,"estimate":3638,"cluster":3639,"armenia":3640,"observatory":3641,"revived":3642,"nadu":3643,"consumers":3644,"hypothesis":3645,"manuscripts":3646,"contents":3647,"arguments":3648,"editing":3649,"trails":3650,"arctic":3651,"essays":3652,"belfast":3653,"acquire":3654,"promotional":3655,"undertaken":3656,"corridor":3657,"proceedings":3658,"antarctic":3659,"millennium":3660,"labels":3661,"delegates":3662,"vegetation":3663,"acclaim":3664,"directing":3665,"substance":3666,"outcome":3667,"diploma":3668,"philosopher":3669,"malta":3670,"albanian":3671,"vicinity":3672,"degc":3673,"legends":3674,"regiments":3675,"consent":3676,"terrorist":3677,"scattered":3678,"presidents":3679,"gravity":3680,"orientation":3681,"deployment":3682,"duchy":3683,"refuses":3684,"estonia":3685,"crowned":3686,"separately":3687,"renovation":3688,"rises":3689,"wilderness":3690,"objectives":3691,"agreements":3692,"empress":3693,"slopes":3694,"inclusion":3695,"equality":3696,"decree":3697,"ballot":3698,"criticised":3699,"rochester":3700,"recurring":3701,"struggled":3702,"disabled":3703,"henri":3704,"poles":3705,"prussian":3706,"convert":3707,"bacteria":3708,"poorly":3709,"sudan":3710,"geological":3711,"wyoming":3712,"consistently":3713,"minimal":3714,"withdrawal":3715,"interviewed":3716,"proximity":3717,"repairs":3718,"initiatives":3719,"pakistani":3720,"republicans":3721,"propaganda":3722,"viii":3723,"abstract":3724,"commercially":3725,"availability":3726,"mechanisms":3727,"naples":3728,"discussions":3729,"underlying":3730,"lens":3731,"proclaimed":3732,"advised":3733,"spelling":3734,"auxiliary":3735,"attract":3736,"lithuanian":3737,"editors":3738,"o'brien":3739,"accordance":3740,"measurement":3741,"novelist":3742,"ussr":3743,"formats":3744,"councils":3745,"contestants":3746,"indie":3747,"facebook":3748,"parishes":3749,"barrier":3750,"battalions":3751,"sponsor":3752,"consulting":3753,"terrorism":3754,"implement":3755,"uganda":3756,"crucial":3757,"unclear":3758,"notion":3759,"distinguish":3760,"collector":3761,"attractions":3762,"filipino":3763,"ecology":3764,"investments":3765,"capability":3766,"renovated":3767,"iceland":3768,"albania":3769,"accredited":3770,"scouts":3771,"armor":3772,"sculptor":3773,"cognitive":3774,"errors":3775,"gaming":3776,"condemned":3777,"successive":3778,"consolidated":3779,"baroque":3780,"entries":3781,"regulatory":3782,"reserved":3783,"treasurer":3784,"variables":3785,"arose":3786,"technological":3787,"rounded":3788,"provider":3789,"rhine":3790,"agrees":3791,"accuracy":3792,"genera":3793,"decreased":3794,"frankfurt":3795,"ecuador":3796,"edges":3797,"particle":3798,"rendered":3799,"calculated":3800,"careers":3801,"faction":3802,"rifles":3803,"americas":3804,"gaelic":3805,"portsmouth":3806,"resides":3807,"merchants":3808,"fiscal":3809,"premises":3810,"coin":3811,"draws":3812,"presenter":3813,"acceptance":3814,"ceremonies":3815,"pollution":3816,"consensus":3817,"membrane":3818,"brigadier":3819,"nonetheless":3820,"genres":3821,"supervision":3822,"predicted":3823,"magnitude":3824,"finite":3825,"differ":3826,"ancestry":3827,"vale":3828,"delegation":3829,"removing":3830,"proceeds":3831,"placement":3832,"emigrated":3833,"siblings":3834,"molecules":3835,"payments":3836,"considers":3837,"demonstration":3838,"proportion":3839,"newer":3840,"valve":3841,"achieving":3842,"confederation":3843,"continuously":3844,"luxury":3845,"notre":3846,"introducing":3847,"coordinates":3848,"charitable":3849,"squadrons":3850,"disorders":3851,"geometry":3852,"winnipeg":3853,"ulster":3854,"loans":3855,"longtime":3856,"receptor":3857,"preceding":3858,"belgrade":3859,"mandate":3860,"wrestler":3861,"neighbourhood":3862,"factories":3863,"buddhism":3864,"imported":3865,"sectors":3866,"protagonist":3867,"steep":3868,"elaborate":3869,"prohibited":3870,"artifacts":3871,"prizes":3872,"pupil":3873,"cooperative":3874,"sovereign":3875,"subspecies":3876,"carriers":3877,"allmusic":3878,"nationals":3879,"settings":3880,"autobiography":3881,"neighborhoods":3882,"analog":3883,"facilitate":3884,"voluntary":3885,"jointly":3886,"newfoundland":3887,"organizing":3888,"raids":3889,"exercises":3890,"nobel":3891,"machinery":3892,"baltic":3893,"crop":3894,"granite":3895,"dense":3896,"websites":3897,"mandatory":3898,"seeks":3899,"surrendered":3900,"anthology":3901,"comedian":3902,"bombs":3903,"slot":3904,"synopsis":3905,"critically":3906,"arcade":3907,"marking":3908,"equations":3909,"halls":3910,"indo":3911,"inaugurated":3912,"embarked":3913,"speeds":3914,"clause":3915,"invention":3916,"premiership":3917,"likewise":3918,"presenting":3919,"demonstrate":3920,"designers":3921,"organize":3922,"examined":3923,"km\/h":3924,"bavaria":3925,"troop":3926,"referee":3927,"detection":3928,"zurich":3929,"prairie":3930,"rapper":3931,"wingspan":3932,"eurovision":3933,"luxembourg":3934,"slovakia":3935,"inception":3936,"disputed":3937,"mammals":3938,"entrepreneur":3939,"makers":3940,"evangelical":3941,"yield":3942,"clergy":3943,"trademark":3944,"defunct":3945,"allocated":3946,"depicting":3947,"volcanic":3948,"batted":3949,"conquered":3950,"sculptures":3951,"providers":3952,"reflects":3953,"armoured":3954,"locals":3955,"walt":3956,"herzegovina":3957,"contracted":3958,"entities":3959,"sponsorship":3960,"prominence":3961,"flowing":3962,"ethiopia":3963,"marketed":3964,"corporations":3965,"withdraw":3966,"carnegie":3967,"induced":3968,"investigated":3969,"portfolio":3970,"flowering":3971,"opinions":3972,"viewing":3973,"classroom":3974,"donations":3975,"bounded":3976,"perception":3977,"leicester":3978,"fruits":3979,"charleston":3980,"academics":3981,"statute":3982,"complaints":3983,"smallest":3984,"deceased":3985,"petroleum":3986,"resolved":3987,"commanders":3988,"algebra":3989,"southampton":3990,"modes":3991,"cultivation":3992,"transmitter":3993,"spelled":3994,"obtaining":3995,"sizes":3996,"acre":3997,"pageant":3998,"bats":3999,"abbreviated":4000,"correspondence":4001,"barracks":4002,"feast":4003,"tackles":4004,"raja":4005,"derives":4006,"geology":4007,"disputes":4008,"translations":4009,"counted":4010,"constantinople":4011,"seating":4012,"macedonia":4013,"preventing":4014,"accommodation":4015,"homeland":4016,"explored":4017,"invaded":4018,"provisional":4019,"transform":4020,"sphere":4021,"unsuccessfully":4022,"missionaries":4023,"conservatives":4024,"highlights":4025,"traces":4026,"organisms":4027,"openly":4028,"dancers":4029,"fossils":4030,"absent":4031,"monarchy":4032,"combining":4033,"lanes":4034,"stint":4035,"dynamics":4036,"chains":4037,"missiles":4038,"screening":4039,"module":4040,"tribune":4041,"generating":4042,"miners":4043,"nottingham":4044,"seoul":4045,"unofficial":4046,"owing":4047,"linking":4048,"rehabilitation":4049,"citation":4050,"louisville":4051,"mollusk":4052,"depicts":4053,"differential":4054,"zimbabwe":4055,"kosovo":4056,"recommendations":4057,"responses":4058,"pottery":4059,"scorer":4060,"aided":4061,"exceptions":4062,"dialects":4063,"telecommunications":4064,"defines":4065,"elderly":4066,"lunar":4067,"coupled":4068,"flown":4069,"25th":4070,"espn":4071,"formula_1":4072,"bordered":4073,"fragments":4074,"guidelines":4075,"gymnasium":4076,"valued":4077,"complexity":4078,"papal":4079,"presumably":4080,"maternal":4081,"challenging":4082,"reunited":4083,"advancing":4084,"comprised":4085,"uncertain":4086,"favorable":4087,"twelfth":4088,"correspondent":4089,"nobility":4090,"livestock":4091,"expressway":4092,"chilean":4093,"tide":4094,"researcher":4095,"emissions":4096,"profits":4097,"lengths":4098,"accompanying":4099,"witnessed":4100,"itunes":4101,"drainage":4102,"slope":4103,"reinforced":4104,"feminist":4105,"sanskrit":4106,"develops":4107,"physicians":4108,"outlets":4109,"isbn":4110,"coordinator":4111,"averaged":4112,"termed":4113,"occupy":4114,"diagnosed":4115,"yearly":4116,"humanitarian":4117,"prospect":4118,"spacecraft":4119,"stems":4120,"enacted":4121,"linux":4122,"ancestors":4123,"karnataka":4124,"constitute":4125,"immigrant":4126,"thriller":4127,"ecclesiastical":4128,"generals":4129,"celebrations":4130,"enhance":4131,"heating":4132,"advocated":4133,"evident":4134,"advances":4135,"bombardment":4136,"watershed":4137,"shuttle":4138,"wicket":4139,"twitter":4140,"adds":4141,"branded":4142,"teaches":4143,"schemes":4144,"pension":4145,"advocacy":4146,"conservatory":4147,"cairo":4148,"varsity":4149,"freshwater":4150,"providence":4151,"seemingly":4152,"shells":4153,"cuisine":4154,"specially":4155,"peaks":4156,"intensive":4157,"publishes":4158,"trilogy":4159,"skilled":4160,"nacional":4161,"unemployment":4162,"destinations":4163,"parameters":4164,"verses":4165,"trafficking":4166,"determination":4167,"infinite":4168,"savings":4169,"alignment":4170,"linguistic":4171,"countryside":4172,"dissolution":4173,"measurements":4174,"advantages":4175,"licence":4176,"subfamily":4177,"highlands":4178,"modest":4179,"regent":4180,"algeria":4181,"crest":4182,"teachings":4183,"knockout":4184,"brewery":4185,"combine":4186,"conventions":4187,"descended":4188,"chassis":4189,"primitive":4190,"fiji":4191,"explicitly":4192,"cumberland":4193,"uruguay":4194,"laboratories":4195,"bypass":4196,"elect":4197,"informal":4198,"preceded":4199,"holocaust":4200,"tackle":4201,"minneapolis":4202,"quantity":4203,"securities":4204,"console":4205,"doctoral":4206,"religions":4207,"commissioners":4208,"expertise":4209,"unveiled":4210,"precise":4211,"diplomat":4212,"standings":4213,"infant":4214,"disciplines":4215,"sicily":4216,"endorsed":4217,"systematic":4218,"charted":4219,"armored":4220,"mild":4221,"lateral":4222,"townships":4223,"hurling":4224,"prolific":4225,"invested":4226,"wartime":4227,"compatible":4228,"galleries":4229,"moist":4230,"battlefield":4231,"decoration":4232,"convent":4233,"tubes":4234,"terrestrial":4235,"nominee":4236,"requests":4237,"delegate":4238,"leased":4239,"dubai":4240,"polar":4241,"applying":4242,"addresses":4243,"munster":4244,"sings":4245,"commercials":4246,"teamed":4247,"dances":4248,"eleventh":4249,"midland":4250,"cedar":4251,"flee":4252,"sandstone":4253,"snails":4254,"inspection":4255,"divide":4256,"asset":4257,"themed":4258,"comparable":4259,"paramount":4260,"dairy":4261,"archaeology":4262,"intact":4263,"institutes":4264,"rectangular":4265,"instances":4266,"phases":4267,"reflecting":4268,"substantially":4269,"applies":4270,"vacant":4271,"lacked":4272,"copa":4273,"coloured":4274,"encounters":4275,"sponsors":4276,"encoded":4277,"possess":4278,"revenues":4279,"ucla":4280,"chaired":4281,"a.m.":4282,"enabling":4283,"playwright":4284,"stoke":4285,"sociology":4286,"tibetan":4287,"frames":4288,"motto":4289,"financing":4290,"illustrations":4291,"gibraltar":4292,"chateau":4293,"bolivia":4294,"transmitted":4295,"enclosed":4296,"persuaded":4297,"urged":4298,"folded":4299,"suffolk":4300,"regulated":4301,"bros.":4302,"submarines":4303,"myth":4304,"oriental":4305,"malaysian":4306,"effectiveness":4307,"narrowly":4308,"acute":4309,"sunk":4310,"replied":4311,"utilized":4312,"tasmania":4313,"consortium":4314,"quantities":4315,"gains":4316,"parkway":4317,"enlarged":4318,"sided":4319,"employers":4320,"adequate":4321,"accordingly":4322,"assumption":4323,"ballad":4324,"mascot":4325,"distances":4326,"peaking":4327,"saxony":4328,"projected":4329,"affiliation":4330,"limitations":4331,"metals":4332,"guatemala":4333,"scots":4334,"theaters":4335,"kindergarten":4336,"verb":4337,"employer":4338,"differs":4339,"discharge":4340,"controller":4341,"seasonal":4342,"marching":4343,"guru":4344,"campuses":4345,"avoided":4346,"vatican":4347,"maori":4348,"excessive":4349,"chartered":4350,"modifications":4351,"caves":4352,"monetary":4353,"sacramento":4354,"mixing":4355,"institutional":4356,"celebrities":4357,"irrigation":4358,"shapes":4359,"broadcaster":4360,"anthem":4361,"attributes":4362,"demolition":4363,"offshore":4364,"specification":4365,"surveys":4366,"yugoslav":4367,"contributor":4368,"auditorium":4369,"lebanese":4370,"capturing":4371,"airports":4372,"classrooms":4373,"chennai":4374,"paths":4375,"tendency":4376,"determining":4377,"lacking":4378,"upgrade":4379,"sailors":4380,"detected":4381,"kingdoms":4382,"sovereignty":4383,"freely":4384,"decorative":4385,"momentum":4386,"scholarly":4387,"georges":4388,"gandhi":4389,"speculation":4390,"transactions":4391,"undertook":4392,"interact":4393,"similarities":4394,"cove":4395,"teammate":4396,"constituted":4397,"painters":4398,"tends":4399,"madagascar":4400,"partnerships":4401,"afghan":4402,"personalities":4403,"attained":4404,"rebounds":4405,"masses":4406,"synagogue":4407,"reopened":4408,"asylum":4409,"embedded":4410,"imaging":4411,"catalogue":4412,"defenders":4413,"taxonomy":4414,"fiber":4415,"afterward":4416,"appealed":4417,"communists":4418,"lisbon":4419,"rica":4420,"judaism":4421,"adviser":4422,"batsman":4423,"ecological":4424,"commands":4425,"lgbt":4426,"cooling":4427,"accessed":4428,"wards":4429,"shiva":4430,"employs":4431,"thirds":4432,"scenic":4433,"worcester":4434,"tallest":4435,"contestant":4436,"humanities":4437,"economist":4438,"textile":4439,"constituencies":4440,"motorway":4441,"tram":4442,"percussion":4443,"cloth":4444,"leisure":4445,"1880s":4446,"baden":4447,"flags":4448,"resemble":4449,"riots":4450,"coined":4451,"sitcom":4452,"composite":4453,"implies":4454,"daytime":4455,"tanzania":4456,"penalties":4457,"optional":4458,"competitor":4459,"excluded":4460,"steering":4461,"reversed":4462,"autonomy":4463,"reviewer":4464,"breakthrough":4465,"professionally":4466,"damages":4467,"pomeranian":4468,"deputies":4469,"valleys":4470,"ventures":4471,"highlighted":4472,"electorate":4473,"mapping":4474,"shortened":4475,"executives":4476,"tertiary":4477,"specimen":4478,"launching":4479,"bibliography":4480,"sank":4481,"pursuing":4482,"binary":4483,"descendant":4484,"marched":4485,"natives":4486,"ideology":4487,"turks":4488,"adolf":4489,"archdiocese":4490,"tribunal":4491,"exceptional":4492,"nigerian":4493,"preference":4494,"fails":4495,"loading":4496,"comeback":4497,"vacuum":4498,"favored":4499,"alter":4500,"remnants":4501,"consecrated":4502,"spectators":4503,"trends":4504,"patriarch":4505,"feedback":4506,"paved":4507,"sentences":4508,"councillor":4509,"astronomy":4510,"advocates":4511,"broader":4512,"commentator":4513,"commissions":4514,"identifying":4515,"revealing":4516,"theatres":4517,"incomplete":4518,"enables":4519,"constituent":4520,"reformation":4521,"tract":4522,"haiti":4523,"atmospheric":4524,"screened":4525,"explosive":4526,"czechoslovakia":4527,"acids":4528,"symbolic":4529,"subdivision":4530,"liberals":4531,"incorporate":4532,"challenger":4533,"erie":4534,"filmmaker":4535,"laps":4536,"kazakhstan":4537,"organizational":4538,"evolutionary":4539,"chemicals":4540,"dedication":4541,"riverside":4542,"fauna":4543,"moths":4544,"maharashtra":4545,"annexed":4546,"gen.":4547,"resembles":4548,"underwater":4549,"garnered":4550,"timeline":4551,"remake":4552,"suited":4553,"educator":4554,"hectares":4555,"automotive":4556,"feared":4557,"latvia":4558,"finalist":4559,"narrator":4560,"portable":4561,"airways":4562,"plaque":4563,"designing":4564,"villagers":4565,"licensing":4566,"flank":4567,"statues":4568,"struggles":4569,"deutsche":4570,"migrated":4571,"cellular":4572,"jacksonville":4573,"wimbledon":4574,"defining":4575,"highlight":4576,"preparatory":4577,"planets":4578,"cologne":4579,"employ":4580,"frequencies":4581,"detachment":4582,"readily":4583,"libya":4584,"resign":4585,"halt":4586,"helicopters":4587,"reef":4588,"landmarks":4589,"collaborative":4590,"irregular":4591,"retaining":4592,"helsinki":4593,"folklore":4594,"weakened":4595,"viscount":4596,"interred":4597,"professors":4598,"memorable":4599,"mega":4600,"repertoire":4601,"rowing":4602,"dorsal":4603,"albeit":4604,"progressed":4605,"operative":4606,"coronation":4607,"liner":4608,"telugu":4609,"domains":4610,"philharmonic":4611,"detect":4612,"bengali":4613,"synthetic":4614,"tensions":4615,"atlas":4616,"dramatically":4617,"paralympics":4618,"xbox":4619,"shire":4620,"kiev":4621,"lengthy":4622,"sued":4623,"notorious":4624,"seas":4625,"screenwriter":4626,"transfers":4627,"aquatic":4628,"pioneers":4629,"unesco":4630,"radius":4631,"abundant":4632,"tunnels":4633,"syndicated":4634,"inventor":4635,"accreditation":4636,"janeiro":4637,"exeter":4638,"ceremonial":4639,"omaha":4640,"cadet":4641,"predators":4642,"resided":4643,"prose":4644,"slavic":4645,"precision":4646,"abbot":4647,"deity":4648,"engaging":4649,"cambodia":4650,"estonian":4651,"compliance":4652,"demonstrations":4653,"protesters":4654,"reactor":4655,"commodore":4656,"successes":4657,"chronicles":4658,"mare":4659,"extant":4660,"listings":4661,"minerals":4662,"tonnes":4663,"parody":4664,"cultivated":4665,"traders":4666,"pioneering":4667,"supplement":4668,"slovak":4669,"preparations":4670,"collision":4671,"partnered":4672,"vocational":4673,"atoms":4674,"malayalam":4675,"welcomed":4676,"documentation":4677,"curved":4678,"functioning":4679,"presently":4680,"formations":4681,"incorporates":4682,"nazis":4683,"botanical":4684,"nucleus":4685,"ethical":4686,"greeks":4687,"metric":4688,"automated":4689,"whereby":4690,"stance":4691,"europeans":4692,"duet":4693,"disability":4694,"purchasing":4695,"email":4696,"telescope":4697,"displaced":4698,"sodium":4699,"comparative":4700,"processor":4701,"inning":4702,"precipitation":4703,"aesthetic":4704,"import":4705,"coordination":4706,"feud":4707,"alternatively":4708,"mobility":4709,"tibet":4710,"regained":4711,"succeeding":4712,"hierarchy":4713,"apostolic":4714,"catalog":4715,"reproduction":4716,"inscriptions":4717,"vicar":4718,"clusters":4719,"posthumously":4720,"rican":4721,"loosely":4722,"additions":4723,"photographic":4724,"nowadays":4725,"selective":4726,"derivative":4727,"keyboards":4728,"guides":4729,"collectively":4730,"affecting":4731,"combines":4732,"operas":4733,"networking":4734,"decisive":4735,"terminated":4736,"continuity":4737,"finishes":4738,"ancestor":4739,"consul":4740,"heated":4741,"simulation":4742,"leipzig":4743,"incorporating":4744,"georgetown":4745,"formula_2":4746,"circa":4747,"forestry":4748,"portrayal":4749,"councillors":4750,"advancement":4751,"complained":4752,"forewings":4753,"confined":4754,"transaction":4755,"definitions":4756,"reduces":4757,"televised":4758,"1890s":4759,"rapids":4760,"phenomena":4761,"belarus":4762,"alps":4763,"landscapes":4764,"quarterly":4765,"specifications":4766,"commemorate":4767,"continuation":4768,"isolation":4769,"antenna":4770,"downstream":4771,"patents":4772,"ensuing":4773,"tended":4774,"saga":4775,"lifelong":4776,"columnist":4777,"labeled":4778,"gymnastics":4779,"papua":4780,"anticipated":4781,"demise":4782,"encompasses":4783,"madras":4784,"antarctica":4785,"interval":4786,"icon":4787,"rams":4788,"midlands":4789,"ingredients":4790,"priory":4791,"strengthen":4792,"rouge":4793,"explicit":4794,"gaza":4795,"aging":4796,"securing":4797,"anthropology":4798,"listeners":4799,"adaptations":4800,"underway":4801,"vista":4802,"malay":4803,"fortified":4804,"lightweight":4805,"violations":4806,"concerto":4807,"financed":4808,"jesuit":4809,"observers":4810,"trustee":4811,"descriptions":4812,"nordic":4813,"resistant":4814,"opted":4815,"accepts":4816,"prohibition":4817,"andhra":4818,"inflation":4819,"negro":4820,"wholly":4821,"imagery":4822,"spur":4823,"instructed":4824,"gloucester":4825,"cycles":4826,"middlesex":4827,"destroyers":4828,"statewide":4829,"evacuated":4830,"hyderabad":4831,"peasants":4832,"mice":4833,"shipyard":4834,"coordinate":4835,"pitching":4836,"colombian":4837,"exploring":4838,"numbering":4839,"compression":4840,"countess":4841,"hiatus":4842,"exceed":4843,"raced":4844,"archipelago":4845,"traits":4846,"soils":4847,"o'connor":4848,"vowel":4849,"android":4850,"facto":4851,"angola":4852,"amino":4853,"holders":4854,"logistics":4855,"circuits":4856,"emergence":4857,"kuwait":4858,"partition":4859,"emeritus":4860,"outcomes":4861,"submission":4862,"promotes":4863,"barack":4864,"negotiated":4865,"loaned":4866,"stripped":4867,"50th":4868,"excavations":4869,"treatments":4870,"fierce":4871,"participant":4872,"exports":4873,"decommissioned":4874,"cameo":4875,"remarked":4876,"residences":4877,"fuselage":4878,"mound":4879,"undergo":4880,"quarry":4881,"node":4882,"midwest":4883,"specializing":4884,"occupies":4885,"etc.":4886,"showcase":4887,"molecule":4888,"offs":4889,"modules":4890,"salon":4891,"exposition":4892,"revision":4893,"peers":4894,"positioned":4895,"hunters":4896,"competes":4897,"algorithms":4898,"reside":4899,"zagreb":4900,"calcium":4901,"uranium":4902,"silicon":4903,"airs":4904,"counterpart":4905,"outlet":4906,"collectors":4907,"sufficiently":4908,"canberra":4909,"inmates":4910,"anatomy":4911,"ensuring":4912,"curves":4913,"aviv":4914,"firearms":4915,"basque":4916,"volcano":4917,"thrust":4918,"sheikh":4919,"extensions":4920,"installations":4921,"aluminum":4922,"darker":4923,"sacked":4924,"emphasized":4925,"aligned":4926,"asserted":4927,"pseudonym":4928,"spanning":4929,"decorations":4930,"eighteenth":4931,"orbital":4932,"spatial":4933,"subdivided":4934,"notation":4935,"decay":4936,"macedonian":4937,"amended":4938,"declining":4939,"cyclist":4940,"feat":4941,"unusually":4942,"commuter":4943,"birthplace":4944,"latitude":4945,"activation":4946,"overhead":4947,"30th":4948,"finalists":4949,"whites":4950,"encyclopedia":4951,"tenor":4952,"qatar":4953,"survives":4954,"complement":4955,"concentrations":4956,"uncommon":4957,"astronomical":4958,"bangalore":4959,"pius":4960,"genome":4961,"memoir":4962,"recruit":4963,"prosecutor":4964,"modification":4965,"paired":4966,"container":4967,"basilica":4968,"arlington":4969,"displacement":4970,"germanic":4971,"mongolia":4972,"proportional":4973,"debates":4974,"matched":4975,"calcutta":4976,"rows":4977,"tehran":4978,"aerospace":4979,"prevalent":4980,"arise":4981,"lowland":4982,"24th":4983,"spokesman":4984,"supervised":4985,"advertisements":4986,"clash":4987,"tunes":4988,"revelation":4989,"wanderers":4990,"quarterfinals":4991,"fisheries":4992,"steadily":4993,"memoirs":4994,"pastoral":4995,"renewable":4996,"confluence":4997,"acquiring":4998,"strips":4999,"slogan":5000,"upstream":5001,"scouting":5002,"analyst":5003,"practitioners":5004,"turbine":5005,"strengthened":5006,"heavier":5007,"prehistoric":5008,"plural":5009,"excluding":5010,"isles":5011,"persecution":5012,"turin":5013,"rotating":5014,"villain":5015,"hemisphere":5016,"unaware":5017,"arabs":5018,"corpus":5019,"relied":5020,"singular":5021,"unanimous":5022,"schooling":5023,"passive":5024,"angles":5025,"dominance":5026,"instituted":5027,"aria":5028,"outskirts":5029,"balanced":5030,"beginnings":5031,"financially":5032,"structured":5033,"parachute":5034,"viewer":5035,"attitudes":5036,"subjected":5037,"escapes":5038,"derbyshire":5039,"erosion":5040,"addressing":5041,"styled":5042,"declaring":5043,"originating":5044,"colts":5045,"adjusted":5046,"stained":5047,"occurrence":5048,"fortifications":5049,"baghdad":5050,"nitrogen":5051,"localities":5052,"yemen":5053,"galway":5054,"debris":5055,"lodz":5056,"victorious":5057,"pharmaceutical":5058,"substances":5059,"unnamed":5060,"dwelling":5061,"atop":5062,"developmental":5063,"activism":5064,"voter":5065,"refugee":5066,"forested":5067,"relates":5068,"overlooking":5069,"genocide":5070,"kannada":5071,"insufficient":5072,"oversaw":5073,"partisan":5074,"dioxide":5075,"recipients":5076,"factions":5077,"mortality":5078,"capped":5079,"expeditions":5080,"receptors":5081,"reorganized":5082,"prominently":5083,"atom":5084,"flooded":5085,"flute":5086,"orchestral":5087,"scripts":5088,"mathematician":5089,"airplay":5090,"detached":5091,"rebuilding":5092,"dwarf":5093,"brotherhood":5094,"salvation":5095,"expressions":5096,"arabian":5097,"cameroon":5098,"poetic":5099,"recruiting":5100,"bundesliga":5101,"inserted":5102,"scrapped":5103,"disabilities":5104,"evacuation":5105,"pasha":5106,"undefeated":5107,"crafts":5108,"rituals":5109,"aluminium":5110,"norm":5111,"pools":5112,"submerged":5113,"occupying":5114,"pathway":5115,"exams":5116,"prosperity":5117,"wrestlers":5118,"promotions":5119,"basal":5120,"permits":5121,"nationalism":5122,"trim":5123,"merge":5124,"gazette":5125,"tributaries":5126,"transcription":5127,"caste":5128,"porto":5129,"emerge":5130,"modeled":5131,"adjoining":5132,"counterparts":5133,"paraguay":5134,"redevelopment":5135,"renewal":5136,"unreleased":5137,"equilibrium":5138,"similarity":5139,"minorities":5140,"soviets":5141,"comprise":5142,"nodes":5143,"tasked":5144,"unrelated":5145,"expired":5146,"johan":5147,"precursor":5148,"examinations":5149,"electrons":5150,"socialism":5151,"exiled":5152,"admiralty":5153,"floods":5154,"wigan":5155,"nonprofit":5156,"lacks":5157,"brigades":5158,"screens":5159,"repaired":5160,"hanover":5161,"fascist":5162,"labs":5163,"osaka":5164,"delays":5165,"judged":5166,"statutory":5167,"colt":5168,"col.":5169,"offspring":5170,"solving":5171,"bred":5172,"assisting":5173,"retains":5174,"somalia":5175,"grouped":5176,"corresponds":5177,"tunisia":5178,"chaplain":5179,"eminent":5180,"chord":5181,"22nd":5182,"spans":5183,"viral":5184,"innovations":5185,"possessions":5186,"mikhail":5187,"kolkata":5188,"icelandic":5189,"implications":5190,"introduces":5191,"racism":5192,"workforce":5193,"alto":5194,"compulsory":5195,"admits":5196,"censorship":5197,"onset":5198,"reluctant":5199,"inferior":5200,"iconic":5201,"progression":5202,"liability":5203,"turnout":5204,"satellites":5205,"behavioral":5206,"coordinated":5207,"exploitation":5208,"posterior":5209,"averaging":5210,"fringe":5211,"krakow":5212,"mountainous":5213,"greenwich":5214,"para":5215,"plantations":5216,"reinforcements":5217,"offerings":5218,"famed":5219,"intervals":5220,"constraints":5221,"individually":5222,"nutrition":5223,"1870s":5224,"taxation":5225,"threshold":5226,"tomatoes":5227,"fungi":5228,"contractor":5229,"ethiopian":5230,"apprentice":5231,"diabetes":5232,"wool":5233,"gujarat":5234,"honduras":5235,"norse":5236,"bucharest":5237,"23rd":5238,"arguably":5239,"accompany":5240,"prone":5241,"teammates":5242,"perennial":5243,"vacancy":5244,"polytechnic":5245,"deficit":5246,"okinawa":5247,"functionality":5248,"reminiscent":5249,"tolerance":5250,"transferring":5251,"myanmar":5252,"concludes":5253,"neighbours":5254,"hydraulic":5255,"economically":5256,"slower":5257,"plots":5258,"charities":5259,"synod":5260,"investor":5261,"catholicism":5262,"identifies":5263,"bronx":5264,"interpretations":5265,"adverse":5266,"judiciary":5267,"hereditary":5268,"nominal":5269,"sensor":5270,"symmetry":5271,"cubic":5272,"triangular":5273,"tenants":5274,"divisional":5275,"outreach":5276,"representations":5277,"passages":5278,"undergoing":5279,"cartridge":5280,"testified":5281,"exceeded":5282,"impacts":5283,"limiting":5284,"railroads":5285,"defeats":5286,"regain":5287,"rendering":5288,"humid":5289,"retreated":5290,"reliability":5291,"governorate":5292,"antwerp":5293,"infamous":5294,"implied":5295,"packaging":5296,"lahore":5297,"trades":5298,"billed":5299,"extinction":5300,"ecole":5301,"rejoined":5302,"recognizes":5303,"projection":5304,"qualifications":5305,"stripes":5306,"forts":5307,"socially":5308,"lexington":5309,"accurately":5310,"sexuality":5311,"westward":5312,"wikipedia":5313,"pilgrimage":5314,"abolition":5315,"choral":5316,"stuttgart":5317,"nests":5318,"expressing":5319,"strikeouts":5320,"assessed":5321,"monasteries":5322,"reconstructed":5323,"humorous":5324,"marxist":5325,"fertile":5326,"consort":5327,"urdu":5328,"patronage":5329,"peruvian":5330,"devised":5331,"lyric":5332,"baba":5333,"nassau":5334,"communism":5335,"extraction":5336,"popularly":5337,"markings":5338,"inability":5339,"litigation":5340,"accounted":5341,"processed":5342,"emirates":5343,"tempo":5344,"cadets":5345,"eponymous":5346,"contests":5347,"broadly":5348,"oxide":5349,"courtyard":5350,"frigate":5351,"directory":5352,"apex":5353,"outline":5354,"regency":5355,"chiefly":5356,"patrols":5357,"secretariat":5358,"cliffs":5359,"residency":5360,"privy":5361,"armament":5362,"australians":5363,"dorset":5364,"geometric":5365,"genetics":5366,"scholarships":5367,"fundraising":5368,"flats":5369,"demographic":5370,"multimedia":5371,"captained":5372,"documentaries":5373,"updates":5374,"canvas":5375,"blockade":5376,"guerrilla":5377,"songwriting":5378,"administrators":5379,"intake":5380,"drought":5381,"implementing":5382,"fraction":5383,"cannes":5384,"refusal":5385,"inscribed":5386,"meditation":5387,"announcing":5388,"exported":5389,"ballots":5390,"formula_3":5391,"curator":5392,"basel":5393,"arches":5394,"flour":5395,"subordinate":5396,"confrontation":5397,"gravel":5398,"simplified":5399,"berkshire":5400,"patriotic":5401,"tuition":5402,"employing":5403,"servers":5404,"castile":5405,"posting":5406,"combinations":5407,"discharged":5408,"miniature":5409,"mutations":5410,"constellation":5411,"incarnation":5412,"ideals":5413,"necessity":5414,"granting":5415,"ancestral":5416,"crowds":5417,"pioneered":5418,"mormon":5419,"methodology":5420,"rama":5421,"indirect":5422,"complexes":5423,"bavarian":5424,"patrons":5425,"uttar":5426,"skeleton":5427,"bollywood":5428,"flemish":5429,"viable":5430,"bloc":5431,"breeds":5432,"triggered":5433,"sustainability":5434,"tailed":5435,"referenced":5436,"comply":5437,"takeover":5438,"latvian":5439,"homestead":5440,"platoon":5441,"communal":5442,"nationality":5443,"excavated":5444,"targeting":5445,"sundays":5446,"posed":5447,"physicist":5448,"turret":5449,"endowment":5450,"marginal":5451,"dispatched":5452,"commentators":5453,"renovations":5454,"attachment":5455,"collaborations":5456,"ridges":5457,"barriers":5458,"obligations":5459,"shareholders":5460,"prof.":5461,"defenses":5462,"presided":5463,"rite":5464,"backgrounds":5465,"arbitrary":5466,"affordable":5467,"gloucestershire":5468,"thirteenth":5469,"inlet":5470,"miniseries":5471,"possesses":5472,"detained":5473,"pressures":5474,"subscription":5475,"realism":5476,"solidarity":5477,"proto":5478,"postgraduate":5479,"noun":5480,"burmese":5481,"abundance":5482,"homage":5483,"reasoning":5484,"anterior":5485,"robust":5486,"fencing":5487,"shifting":5488,"vowels":5489,"garde":5490,"profitable":5491,"loch":5492,"anchored":5493,"coastline":5494,"samoa":5495,"terminology":5496,"prostitution":5497,"magistrate":5498,"venezuelan":5499,"speculated":5500,"regulate":5501,"fixture":5502,"colonists":5503,"digit":5504,"induction":5505,"manned":5506,"expeditionary":5507,"computational":5508,"centennial":5509,"principally":5510,"vein":5511,"preserving":5512,"engineered":5513,"numerical":5514,"cancellation":5515,"conferred":5516,"continually":5517,"borne":5518,"seeded":5519,"advertisement":5520,"unanimously":5521,"treaties":5522,"infections":5523,"ions":5524,"sensors":5525,"lowered":5526,"amphibious":5527,"lava":5528,"fourteenth":5529,"bahrain":5530,"niagara":5531,"nicaragua":5532,"squares":5533,"congregations":5534,"26th":5535,"periodic":5536,"proprietary":5537,"1860s":5538,"contributors":5539,"seller":5540,"overs":5541,"emission":5542,"procession":5543,"presumed":5544,"illustrator":5545,"zinc":5546,"gases":5547,"tens":5548,"applicable":5549,"stretches":5550,"reproductive":5551,"sixteenth":5552,"apparatus":5553,"accomplishments":5554,"canoe":5555,"guam":5556,"oppose":5557,"recruitment":5558,"accumulated":5559,"limerick":5560,"namibia":5561,"staging":5562,"remixes":5563,"ordnance":5564,"uncertainty":5565,"pedestrian":5566,"temperate":5567,"treason":5568,"deposited":5569,"registry":5570,"cerambycidae":5571,"attracting":5572,"lankan":5573,"reprinted":5574,"shipbuilding":5575,"homosexuality":5576,"neurons":5577,"eliminating":5578,"1900s":5579,"resume":5580,"ministries":5581,"beneficial":5582,"blackpool":5583,"surplus":5584,"northampton":5585,"licenses":5586,"constructing":5587,"announcer":5588,"standardized":5589,"alternatives":5590,"taipei":5591,"inadequate":5592,"failures":5593,"yields":5594,"medalist":5595,"titular":5596,"obsolete":5597,"torah":5598,"burlington":5599,"predecessors":5600,"lublin":5601,"retailers":5602,"castles":5603,"depiction":5604,"issuing":5605,"gubernatorial":5606,"propulsion":5607,"tiles":5608,"damascus":5609,"discs":5610,"alternating":5611,"pomerania":5612,"peasant":5613,"tavern":5614,"redesignated":5615,"27th":5616,"illustration":5617,"focal":5618,"mans":5619,"codex":5620,"specialists":5621,"productivity":5622,"antiquity":5623,"controversies":5624,"promoter":5625,"pits":5626,"companions":5627,"behaviors":5628,"lyrical":5629,"prestige":5630,"creativity":5631,"swansea":5632,"dramas":5633,"approximate":5634,"feudal":5635,"tissues":5636,"crude":5637,"campaigned":5638,"unprecedented":5639,"chancel":5640,"amendments":5641,"surroundings":5642,"allegiance":5643,"exchanges":5644,"align":5645,"firmly":5646,"optimal":5647,"commenting":5648,"reigning":5649,"landings":5650,"obscure":5651,"1850s":5652,"contemporaries":5653,"paternal":5654,"devi":5655,"endurance":5656,"communes":5657,"incorporation":5658,"denominations":5659,"exchanged":5660,"routing":5661,"resorts":5662,"amnesty":5663,"slender":5664,"explores":5665,"suppression":5666,"heats":5667,"pronunciation":5668,"centred":5669,"coupe":5670,"stirling":5671,"freelance":5672,"treatise":5673,"linguistics":5674,"laos":5675,"informs":5676,"discovering":5677,"pillars":5678,"encourages":5679,"halted":5680,"robots":5681,"definitive":5682,"maturity":5683,"tuberculosis":5684,"venetian":5685,"silesian":5686,"unchanged":5687,"originates":5688,"mali":5689,"lincolnshire":5690,"quotes":5691,"seniors":5692,"premise":5693,"contingent":5694,"distribute":5695,"danube":5696,"gorge":5697,"logging":5698,"dams":5699,"curling":5700,"seventeenth":5701,"specializes":5702,"wetlands":5703,"deities":5704,"assess":5705,"thickness":5706,"rigid":5707,"culminated":5708,"utilities":5709,"substrate":5710,"insignia":5711,"nile":5712,"assam":5713,"shri":5714,"currents":5715,"suffrage":5716,"canadians":5717,"mortar":5718,"asteroid":5719,"bosnian":5720,"discoveries":5721,"enzymes":5722,"sanctioned":5723,"replica":5724,"hymn":5725,"investigators":5726,"tidal":5727,"dominate":5728,"derivatives":5729,"converting":5730,"leinster":5731,"verbs":5732,"honoured":5733,"criticisms":5734,"dismissal":5735,"discrete":5736,"masculine":5737,"reorganization":5738,"unlimited":5739,"wurttemberg":5740,"sacks":5741,"allocation":5742,"bahn":5743,"jurisdictions":5744,"participates":5745,"lagoon":5746,"famine":5747,"communion":5748,"culminating":5749,"surveyed":5750,"shortage":5751,"cables":5752,"intersects":5753,"cassette":5754,"foremost":5755,"adopting":5756,"solicitor":5757,"outright":5758,"bihar":5759,"reissued":5760,"farmland":5761,"dissertation":5762,"turnpike":5763,"baton":5764,"photographed":5765,"christchurch":5766,"kyoto":5767,"finances":5768,"rails":5769,"histories":5770,"linebacker":5771,"kilkenny":5772,"accelerated":5773,"dispersed":5774,"handicap":5775,"absorption":5776,"rancho":5777,"ceramic":5778,"captivity":5779,"cites":5780,"font":5781,"weighed":5782,"mater":5783,"utilize":5784,"bravery":5785,"extract":5786,"validity":5787,"slovenian":5788,"seminars":5789,"discourse":5790,"ranged":5791,"duel":5792,"ironically":5793,"warships":5794,"sega":5795,"temporal":5796,"surpassed":5797,"prolonged":5798,"recruits":5799,"northumberland":5800,"greenland":5801,"contributes":5802,"patented":5803,"eligibility":5804,"unification":5805,"discusses":5806,"reply":5807,"translates":5808,"beirut":5809,"relies":5810,"torque":5811,"northward":5812,"reviewers":5813,"monastic":5814,"accession":5815,"neural":5816,"tramway":5817,"heirs":5818,"sikh":5819,"subscribers":5820,"amenities":5821,"taliban":5822,"audit":5823,"rotterdam":5824,"wagons":5825,"kurdish":5826,"favoured":5827,"combustion":5828,"meanings":5829,"persia":5830,"browser":5831,"diagnostic":5832,"niger":5833,"formula_4":5834,"denomination":5835,"dividing":5836,"parameter":5837,"branding":5838,"badminton":5839,"leningrad":5840,"sparked":5841,"hurricanes":5842,"beetles":5843,"propeller":5844,"mozambique":5845,"refined":5846,"diagram":5847,"exhaust":5848,"vacated":5849,"readings":5850,"markers":5851,"reconciliation":5852,"determines":5853,"concurrent":5854,"imprint":5855,"primera":5856,"organism":5857,"demonstrating":5858,"filmmakers":5859,"vanderbilt":5860,"affiliates":5861,"traction":5862,"evaluated":5863,"defendants":5864,"megachile":5865,"investigative":5866,"zambia":5867,"assassinated":5868,"rewarded":5869,"probable":5870,"staffordshire":5871,"foreigners":5872,"directorate":5873,"nominees":5874,"consolidation":5875,"commandant":5876,"reddish":5877,"differing":5878,"unrest":5879,"drilling":5880,"bohemia":5881,"resembling":5882,"instrumentation":5883,"considerations":5884,"haute":5885,"promptly":5886,"variously":5887,"dwellings":5888,"clans":5889,"tablet":5890,"enforced":5891,"cockpit":5892,"semifinal":5893,"hussein":5894,"prisons":5895,"ceylon":5896,"emblem":5897,"monumental":5898,"phrases":5899,"correspond":5900,"crossover":5901,"outlined":5902,"characterised":5903,"acceleration":5904,"caucus":5905,"crusade":5906,"protested":5907,"composing":5908,"rajasthan":5909,"habsburg":5910,"rhythmic":5911,"interception":5912,"inherent":5913,"cooled":5914,"ponds":5915,"spokesperson":5916,"gradual":5917,"consultation":5918,"kuala":5919,"globally":5920,"suppressed":5921,"builders":5922,"avengers":5923,"suffix":5924,"integer":5925,"enforce":5926,"fibers":5927,"unionist":5928,"proclamation":5929,"uncovered":5930,"infrared":5931,"adapt":5932,"eisenhower":5933,"utilizing":5934,"captains":5935,"stretched":5936,"observing":5937,"assumes":5938,"prevents":5939,"analyses":5940,"saxophone":5941,"caucasus":5942,"notices":5943,"villains":5944,"dartmouth":5945,"mongol":5946,"hostilities":5947,"stretching":5948,"veterinary":5949,"lenses":5950,"texture":5951,"prompting":5952,"overthrow":5953,"excavation":5954,"islanders":5955,"masovian":5956,"battleship":5957,"biographer":5958,"replay":5959,"degradation":5960,"departing":5961,"luftwaffe":5962,"fleeing":5963,"oversight":5964,"immigrated":5965,"serbs":5966,"fishermen":5967,"strengthening":5968,"respiratory":5969,"italians":5970,"denotes":5971,"radial":5972,"escorted":5973,"motif":5974,"wiltshire":5975,"expresses":5976,"accessories":5977,"reverted":5978,"establishments":5979,"inequality":5980,"protocols":5981,"charting":5982,"famously":5983,"satirical":5984,"entirety":5985,"trench":5986,"friction":5987,"atletico":5988,"sampling":5989,"subset":5990,"weekday":5991,"upheld":5992,"sharply":5993,"correlation":5994,"incorrect":5995,"mughal":5996,"travelers":5997,"hasan":5998,"earnings":5999,"offset":6000,"evaluate":6001,"specialised":6002,"recognizing":6003,"flexibility":6004,"nagar":6005,"postseason":6006,"algebraic":6007,"capitalism":6008,"crystals":6009,"melodies":6010,"polynomial":6011,"racecourse":6012,"defences":6013,"austro":6014,"wembley":6015,"attracts":6016,"anarchist":6017,"resurrection":6018,"reviewing":6019,"decreasing":6020,"prefix":6021,"ratified":6022,"mutation":6023,"displaying":6024,"separating":6025,"restoring":6026,"assemblies":6027,"ordinance":6028,"priesthood":6029,"cruisers":6030,"appoint":6031,"moldova":6032,"imports":6033,"directive":6034,"epidemic":6035,"militant":6036,"senegal":6037,"signaling":6038,"restriction":6039,"critique":6040,"retrospective":6041,"nationalists":6042,"undertake":6043,"sioux":6044,"canals":6045,"algerian":6046,"redesigned":6047,"philanthropist":6048,"depict":6049,"conceptual":6050,"turbines":6051,"intellectuals":6052,"eastward":6053,"applicants":6054,"contractors":6055,"vendors":6056,"undergone":6057,"namesake":6058,"ensured":6059,"tones":6060,"substituted":6061,"hindwings":6062,"arrests":6063,"tombs":6064,"transitional":6065,"principality":6066,"reelection":6067,"taiwanese":6068,"cavity":6069,"manifesto":6070,"broadcasters":6071,"spawned":6072,"thoroughbred":6073,"identities":6074,"generators":6075,"proposes":6076,"hydroelectric":6077,"johannesburg":6078,"cortex":6079,"scandinavian":6080,"killings":6081,"aggression":6082,"boycott":6083,"catalyst":6084,"physiology":6085,"fifteenth":6086,"waterfront":6087,"chromosome":6088,"organist":6089,"costly":6090,"calculation":6091,"cemeteries":6092,"flourished":6093,"recognise":6094,"juniors":6095,"merging":6096,"disciples":6097,"ashore":6098,"workplace":6099,"enlightenment":6100,"diminished":6101,"debated":6102,"hailed":6103,"podium":6104,"educate":6105,"mandated":6106,"distributor":6107,"litre":6108,"electromagnetic":6109,"flotilla":6110,"estuary":6111,"peterborough":6112,"staircase":6113,"selections":6114,"melodic":6115,"confronts":6116,"wholesale":6117,"integrate":6118,"intercepted":6119,"catalonia":6120,"unite":6121,"immense":6122,"palatinate":6123,"switches":6124,"earthquakes":6125,"occupational":6126,"successors":6127,"praising":6128,"concluding":6129,"faculties":6130,"firstly":6131,"overhaul":6132,"empirical":6133,"metacritic":6134,"inauguration":6135,"evergreen":6136,"laden":6137,"winged":6138,"philosophers":6139,"amalgamated":6140,"geoff":6141,"centimeters":6142,"napoleonic":6143,"upright":6144,"planting":6145,"brewing":6146,"fined":6147,"sensory":6148,"migrants":6149,"wherein":6150,"inactive":6151,"headmaster":6152,"warwickshire":6153,"siberia":6154,"terminals":6155,"denounced":6156,"academia":6157,"divinity":6158,"bilateral":6159,"clive":6160,"omitted":6161,"peerage":6162,"relics":6163,"apartheid":6164,"syndicate":6165,"fearing":6166,"fixtures":6167,"desirable":6168,"dismantled":6169,"ethnicity":6170,"valves":6171,"biodiversity":6172,"aquarium":6173,"ideological":6174,"visibility":6175,"creators":6176,"analyzed":6177,"tenant":6178,"balkan":6179,"postwar":6180,"supplier":6181,"smithsonian":6182,"risen":6183,"morphology":6184,"digits":6185,"bohemian":6186,"wilmington":6187,"vishnu":6188,"demonstrates":6189,"aforementioned":6190,"biographical":6191,"mapped":6192,"khorasan":6193,"phosphate":6194,"presentations":6195,"ecosystem":6196,"processors":6197,"calculations":6198,"mosaic":6199,"clashes":6200,"penned":6201,"recalls":6202,"coding":6203,"angular":6204,"lattice":6205,"macau":6206,"accountability":6207,"extracted":6208,"pollen":6209,"therapeutic":6210,"overlap":6211,"violinist":6212,"deposed":6213,"candidacy":6214,"infants":6215,"covenant":6216,"bacterial":6217,"restructuring":6218,"dungeons":6219,"ordination":6220,"conducts":6221,"builds":6222,"invasive":6223,"customary":6224,"concurrently":6225,"relocation":6226,"cello":6227,"statutes":6228,"borneo":6229,"entrepreneurs":6230,"sanctions":6231,"packet":6232,"rockefeller":6233,"piedmont":6234,"comparisons":6235,"waterfall":6236,"receptions":6237,"glacial":6238,"surge":6239,"signatures":6240,"alterations":6241,"advertised":6242,"enduring":6243,"somali":6244,"botanist":6245,"100th":6246,"canonical":6247,"motifs":6248,"longitude":6249,"circulated":6250,"alloy":6251,"indirectly":6252,"margins":6253,"preserves":6254,"internally":6255,"besieged":6256,"shale":6257,"peripheral":6258,"drained":6259,"baseman":6260,"reassigned":6261,"tobago":6262,"soloist":6263,"socio":6264,"grazing":6265,"contexts":6266,"roofs":6267,"portraying":6268,"ottomans":6269,"shrewsbury":6270,"noteworthy":6271,"lamps":6272,"supplying":6273,"beams":6274,"qualifier":6275,"portray":6276,"greenhouse":6277,"stronghold":6278,"hitter":6279,"rites":6280,"cretaceous":6281,"urging":6282,"derive":6283,"nautical":6284,"aiming":6285,"fortunes":6286,"verde":6287,"donors":6288,"reliance":6289,"exceeding":6290,"exclusion":6291,"exercised":6292,"simultaneous":6293,"continents":6294,"guiding":6295,"pillar":6296,"gradient":6297,"poznan":6298,"eruption":6299,"clinics":6300,"moroccan":6301,"indicator":6302,"trams":6303,"piers":6304,"parallels":6305,"fragment":6306,"teatro":6307,"potassium":6308,"satire":6309,"compressed":6310,"businessmen":6311,"influx":6312,"seine":6313,"perspectives":6314,"shelters":6315,"decreases":6316,"mounting":6317,"formula_5":6318,"confederacy":6319,"equestrian":6320,"expulsion":6321,"mayors":6322,"liberia":6323,"resisted":6324,"affinity":6325,"shrub":6326,"unexpectedly":6327,"stimulus":6328,"amtrak":6329,"deported":6330,"perpendicular":6331,"statesman":6332,"wharf":6333,"storylines":6334,"romanesque":6335,"weights":6336,"surfaced":6337,"interceptions":6338,"dhaka":6339,"crambidae":6340,"orchestras":6341,"rwanda":6342,"conclude":6343,"constitutes":6344,"subsidiaries":6345,"admissions":6346,"prospective":6347,"shear":6348,"bilingual":6349,"campaigning":6350,"presiding":6351,"domination":6352,"commemorative":6353,"trailing":6354,"confiscated":6355,"petrol":6356,"acquisitions":6357,"polymer":6358,"onlyinclude":6359,"chloride":6360,"elevations":6361,"resolutions":6362,"hurdles":6363,"pledged":6364,"likelihood":6365,"objected":6366,"erect":6367,"encoding":6368,"databases":6369,"aristotle":6370,"hindus":6371,"marshes":6372,"bowled":6373,"ministerial":6374,"grange":6375,"acronym":6376,"annexation":6377,"squads":6378,"ambient":6379,"pilgrims":6380,"botany":6381,"sofla":6382,"astronomer":6383,"planetary":6384,"descending":6385,"bestowed":6386,"ceramics":6387,"diplomacy":6388,"metabolism":6389,"colonization":6390,"potomac":6391,"africans":6392,"engraved":6393,"recycling":6394,"commitments":6395,"resonance":6396,"disciplinary":6397,"jamaican":6398,"narrated":6399,"spectral":6400,"tipperary":6401,"waterford":6402,"stationary":6403,"arbitration":6404,"transparency":6405,"threatens":6406,"crossroads":6407,"slalom":6408,"oversee":6409,"centenary":6410,"incidence":6411,"economies":6412,"livery":6413,"moisture":6414,"newsletter":6415,"autobiographical":6416,"bhutan":6417,"propelled":6418,"dependence":6419,"moderately":6420,"adobe":6421,"barrels":6422,"subdivisions":6423,"outlook":6424,"labelled":6425,"stratford":6426,"arising":6427,"diaspora":6428,"barony":6429,"automobiles":6430,"ornamental":6431,"slated":6432,"norms":6433,"primetime":6434,"generalized":6435,"analysts":6436,"vectors":6437,"libyan":6438,"yielded":6439,"certificates":6440,"rooted":6441,"vernacular":6442,"belarusian":6443,"marketplace":6444,"prediction":6445,"fairfax":6446,"malawi":6447,"viruses":6448,"wooded":6449,"demos":6450,"mauritius":6451,"prosperous":6452,"coincided":6453,"liberties":6454,"huddersfield":6455,"ascent":6456,"warnings":6457,"hinduism":6458,"glucose":6459,"pulitzer":6460,"unused":6461,"filters":6462,"illegitimate":6463,"acquitted":6464,"protestants":6465,"canopy":6466,"staple":6467,"psychedelic":6468,"winding":6469,"abbas":6470,"pathways":6471,"cheltenham":6472,"lagos":6473,"niche":6474,"invaders":6475,"proponents":6476,"barred":6477,"conversely":6478,"doncaster":6479,"recession":6480,"embraced":6481,"rematch":6482,"concession":6483,"emigration":6484,"upgrades":6485,"bowls":6486,"tablets":6487,"remixed":6488,"loops":6489,"kensington":6490,"shootout":6491,"monarchs":6492,"organizers":6493,"harmful":6494,"punjabi":6495,"broadband":6496,"exempt":6497,"neolithic":6498,"profiles":6499,"portrays":6500,"parma":6501,"cyrillic":6502,"quasi":6503,"attested":6504,"regimental":6505,"revive":6506,"torpedoes":6507,"heidelberg":6508,"rhythms":6509,"spherical":6510,"denote":6511,"hymns":6512,"icons":6513,"theologian":6514,"qaeda":6515,"exceptionally":6516,"reinstated":6517,"comune":6518,"playhouse":6519,"lobbying":6520,"grossing":6521,"viceroy":6522,"delivers":6523,"visually":6524,"armistice":6525,"utrecht":6526,"syllable":6527,"vertices":6528,"analogous":6529,"annex":6530,"refurbished":6531,"entrants":6532,"knighted":6533,"disciple":6534,"rhetoric":6535,"detailing":6536,"inactivated":6537,"ballads":6538,"algae":6539,"intensified":6540,"favourable":6541,"sanitation":6542,"receivers":6543,"pornography":6544,"commemorated":6545,"cannons":6546,"entrusted":6547,"manifold":6548,"photographers":6549,"pueblo":6550,"textiles":6551,"steamer":6552,"myths":6553,"marquess":6554,"onward":6555,"liturgical":6556,"romney":6557,"uzbekistan":6558,"consistency":6559,"denoted":6560,"hertfordshire":6561,"convex":6562,"hearings":6563,"sulfur":6564,"universidad":6565,"podcast":6566,"selecting":6567,"emperors":6568,"arises":6569,"justices":6570,"1840s":6571,"mongolian":6572,"exploited":6573,"termination":6574,"digitally":6575,"infectious":6576,"sedan":6577,"symmetric":6578,"penal":6579,"illustrate":6580,"formulation":6581,"attribute":6582,"problematic":6583,"modular":6584,"inverse":6585,"berth":6586,"searches":6587,"rutgers":6588,"leicestershire":6589,"enthusiasts":6590,"lockheed":6591,"upwards":6592,"transverse":6593,"accolades":6594,"backward":6595,"archaeologists":6596,"crusaders":6597,"nuremberg":6598,"defects":6599,"ferries":6600,"vogue":6601,"containers":6602,"openings":6603,"transporting":6604,"separates":6605,"lumpur":6606,"purchases":6607,"attain":6608,"wichita":6609,"topology":6610,"woodlands":6611,"deleted":6612,"periodically":6613,"syntax":6614,"overturned":6615,"musicals":6616,"corp.":6617,"strasbourg":6618,"instability":6619,"nationale":6620,"prevailing":6621,"cache":6622,"marathi":6623,"versailles":6624,"unmarried":6625,"grains":6626,"straits":6627,"antagonist":6628,"segregation":6629,"assistants":6630,"d'etat":6631,"contention":6632,"dictatorship":6633,"unpopular":6634,"motorcycles":6635,"criterion":6636,"analytical":6637,"salzburg":6638,"militants":6639,"hanged":6640,"worcestershire":6641,"emphasize":6642,"paralympic":6643,"erupted":6644,"convinces":6645,"offences":6646,"oxidation":6647,"nouns":6648,"populace":6649,"atari":6650,"spanned":6651,"hazardous":6652,"educators":6653,"playable":6654,"births":6655,"baha'i":6656,"preseason":6657,"generates":6658,"invites":6659,"meteorological":6660,"handbook":6661,"foothills":6662,"enclosure":6663,"diffusion":6664,"mirza":6665,"convergence":6666,"geelong":6667,"coefficient":6668,"connector":6669,"formula_6":6670,"cylindrical":6671,"disasters":6672,"pleaded":6673,"knoxville":6674,"contamination":6675,"compose":6676,"libertarian":6677,"arrondissement":6678,"franciscan":6679,"intercontinental":6680,"susceptible":6681,"initiation":6682,"malaria":6683,"unbeaten":6684,"consonants":6685,"waived":6686,"saloon":6687,"popularized":6688,"estadio":6689,"pseudo":6690,"interdisciplinary":6691,"transports":6692,"transformers":6693,"carriages":6694,"bombings":6695,"revolves":6696,"ceded":6697,"collaborator":6698,"celestial":6699,"exemption":6700,"colchester":6701,"maltese":6702,"oceanic":6703,"ligue":6704,"crete":6705,"shareholder":6706,"routed":6707,"depictions":6708,"ridden":6709,"advisors":6710,"calculate":6711,"lending":6712,"guangzhou":6713,"simplicity":6714,"newscast":6715,"scheduling":6716,"snout":6717,"eliot":6718,"undertaking":6719,"armenians":6720,"nottinghamshire":6721,"whitish":6722,"consulted":6723,"deficiency":6724,"salle":6725,"cinemas":6726,"superseded":6727,"rigorous":6728,"kerman":6729,"convened":6730,"landowners":6731,"modernization":6732,"evenings":6733,"pitches":6734,"conditional":6735,"scandinavia":6736,"differed":6737,"formulated":6738,"cyclists":6739,"swami":6740,"guyana":6741,"dunes":6742,"electrified":6743,"appalachian":6744,"abdomen":6745,"scenarios":6746,"prototypes":6747,"sindh":6748,"consonant":6749,"adaptive":6750,"boroughs":6751,"wolverhampton":6752,"modelling":6753,"cylinders":6754,"amounted":6755,"minimize":6756,"ambassadors":6757,"lenin":6758,"settler":6759,"coincide":6760,"approximation":6761,"grouping":6762,"murals":6763,"bullying":6764,"registers":6765,"rumours":6766,"engagements":6767,"energetic":6768,"vertex":6769,"annals":6770,"bordering":6771,"geologic":6772,"yellowish":6773,"runoff":6774,"converts":6775,"allegheny":6776,"facilitated":6777,"saturdays":6778,"colliery":6779,"monitored":6780,"rainforest":6781,"interfaces":6782,"geographically":6783,"impaired":6784,"prevalence":6785,"joachim":6786,"paperback":6787,"slowed":6788,"shankar":6789,"distinguishing":6790,"seminal":6791,"categorized":6792,"authorised":6793,"auspices":6794,"bandwidth":6795,"asserts":6796,"rebranded":6797,"balkans":6798,"supplemented":6799,"seldom":6800,"weaving":6801,"capsule":6802,"apostles":6803,"populous":6804,"monmouth":6805,"payload":6806,"symphonic":6807,"densely":6808,"shoreline":6809,"managerial":6810,"masonry":6811,"antioch":6812,"averages":6813,"textbooks":6814,"royalist":6815,"coliseum":6816,"tandem":6817,"brewers":6818,"diocesan":6819,"posthumous":6820,"walled":6821,"incorrectly":6822,"distributions":6823,"ensued":6824,"reasonably":6825,"graffiti":6826,"propagation":6827,"automation":6828,"harmonic":6829,"augmented":6830,"middleweight":6831,"limbs":6832,"elongated":6833,"landfall":6834,"comparatively":6835,"literal":6836,"grossed":6837,"koppen":6838,"wavelength":6839,"1830s":6840,"cerebral":6841,"boasts":6842,"congestion":6843,"physiological":6844,"practitioner":6845,"coasts":6846,"cartoonist":6847,"undisclosed":6848,"frontal":6849,"launches":6850,"burgundy":6851,"qualifiers":6852,"imposing":6853,"stade":6854,"flanked":6855,"assyrian":6856,"raided":6857,"multiplayer":6858,"montane":6859,"chesapeake":6860,"pathology":6861,"drains":6862,"vineyards":6863,"intercollegiate":6864,"semiconductor":6865,"grassland":6866,"convey":6867,"citations":6868,"predominant":6869,"rejects":6870,"benefited":6871,"yahoo":6872,"graphs":6873,"busiest":6874,"encompassing":6875,"hamlets":6876,"explorers":6877,"suppress":6878,"minors":6879,"graphical":6880,"calculus":6881,"sediment":6882,"intends":6883,"diverted":6884,"mainline":6885,"unopposed":6886,"cottages":6887,"initiate":6888,"alumnus":6889,"towed":6890,"autism":6891,"forums":6892,"darlington":6893,"modernist":6894,"oxfordshire":6895,"lectured":6896,"capitalist":6897,"suppliers":6898,"panchayat":6899,"actresses":6900,"foundry":6901,"southbound":6902,"commodity":6903,"wesleyan":6904,"divides":6905,"palestinians":6906,"luton":6907,"caretaker":6908,"nobleman":6909,"mutiny":6910,"organizer":6911,"preferences":6912,"nomenclature":6913,"splits":6914,"unwilling":6915,"offenders":6916,"timor":6917,"relying":6918,"halftime":6919,"semitic":6920,"arithmetic":6921,"milestone":6922,"jesuits":6923,"arctiidae":6924,"retrieved":6925,"consuming":6926,"contender":6927,"edged":6928,"plagued":6929,"inclusive":6930,"transforming":6931,"khmer":6932,"federally":6933,"insurgents":6934,"distributing":6935,"amherst":6936,"rendition":6937,"prosecutors":6938,"viaduct":6939,"disqualified":6940,"kabul":6941,"liturgy":6942,"prevailed":6943,"reelected":6944,"instructors":6945,"swimmers":6946,"aperture":6947,"churchyard":6948,"interventions":6949,"totals":6950,"darts":6951,"metropolis":6952,"fuels":6953,"fluent":6954,"northbound":6955,"correctional":6956,"inflicted":6957,"barrister":6958,"realms":6959,"culturally":6960,"aristocratic":6961,"collaborating":6962,"emphasizes":6963,"choreographer":6964,"inputs":6965,"ensembles":6966,"humboldt":6967,"practised":6968,"endowed":6969,"strains":6970,"infringement":6971,"archaeologist":6972,"congregational":6973,"magna":6974,"relativity":6975,"efficiently":6976,"proliferation":6977,"mixtape":6978,"abruptly":6979,"regeneration":6980,"commissioning":6981,"yukon":6982,"archaic":6983,"reluctantly":6984,"retailer":6985,"northamptonshire":6986,"universally":6987,"crossings":6988,"boilers":6989,"nickelodeon":6990,"revue":6991,"abbreviation":6992,"retaliation":6993,"scripture":6994,"routinely":6995,"medicinal":6996,"benedictine":6997,"kenyan":6998,"retention":6999,"deteriorated":7000,"glaciers":7001,"apprenticeship":7002,"coupling":7003,"researched":7004,"topography":7005,"entrances":7006,"anaheim":7007,"pivotal":7008,"compensate":7009,"arched":7010,"modify":7011,"reinforce":7012,"dusseldorf":7013,"journeys":7014,"motorsport":7015,"conceded":7016,"sumatra":7017,"spaniards":7018,"quantitative":7019,"loire":7020,"cinematography":7021,"discarded":7022,"botswana":7023,"morale":7024,"engined":7025,"zionist":7026,"philanthropy":7027,"sainte":7028,"fatalities":7029,"cypriot":7030,"motorsports":7031,"indicators":7032,"pricing":7033,"institut":7034,"bethlehem":7035,"implicated":7036,"gravitational":7037,"differentiation":7038,"rotor":7039,"thriving":7040,"precedent":7041,"ambiguous":7042,"concessions":7043,"forecast":7044,"conserved":7045,"fremantle":7046,"asphalt":7047,"landslide":7048,"middlesbrough":7049,"formula_7":7050,"humidity":7051,"overseeing":7052,"chronological":7053,"diaries":7054,"multinational":7055,"crimean":7056,"turnover":7057,"improvised":7058,"youths":7059,"declares":7060,"tasmanian":7061,"canadiens":7062,"fumble":7063,"refinery":7064,"weekdays":7065,"unconstitutional":7066,"upward":7067,"guardians":7068,"brownish":7069,"imminent":7070,"hamas":7071,"endorsement":7072,"naturalist":7073,"martyrs":7074,"caledonia":7075,"chords":7076,"yeshiva":7077,"reptiles":7078,"severity":7079,"mitsubishi":7080,"fairs":7081,"installment":7082,"substitution":7083,"repertory":7084,"keyboardist":7085,"interpreter":7086,"silesia":7087,"noticeable":7088,"rhineland":7089,"transmit":7090,"inconsistent":7091,"booklet":7092,"academies":7093,"epithet":7094,"pertaining":7095,"progressively":7096,"aquatics":7097,"scrutiny":7098,"prefect":7099,"toxicity":7100,"rugged":7101,"consume":7102,"o'donnell":7103,"evolve":7104,"uniquely":7105,"cabaret":7106,"mediated":7107,"landowner":7108,"transgender":7109,"palazzo":7110,"compilations":7111,"albuquerque":7112,"induce":7113,"sinai":7114,"remastered":7115,"efficacy":7116,"und