Version Description
- Removed https notice.
Download this release
Release Info
Developer | alexkovalevv |
Plugin | Anti-spam |
Version | 7.0.1 |
Comparing to | |
See all releases |
Code changes from version 6.5.4 to 7.0.1
- admin/ajax/logs.php +14 -14
- admin/assets/css/about-premium.css +69 -69
- admin/assets/css/about-premium.css.map +0 -1
- admin/assets/css/dashboard-dashboard.css +302 -0
- admin/assets/css/firewall/firewall-attacks-log.css +68 -0
- admin/assets/css/firewall/firewall-attacks-log.less +86 -0
- admin/assets/css/firewall/firewall-dashboard.css +83 -0
- admin/assets/css/firewall/firewall-dashboard.less +91 -0
- admin/assets/css/firewall/firewall-ips-blocking.css +98 -0
- admin/assets/css/firewall/firewall-ips-blocking.less +121 -0
- admin/assets/css/firewall/firewall-settings.css +205 -0
- admin/assets/css/firewall/firewall-settings.less +211 -0
- admin/assets/css/libs/jquery.datetimepicker.min.css +1 -0
- admin/assets/css/quick-dashboard.css +266 -0
- admin/assets/css/settings.css.map +0 -1
- admin/assets/css/sweetalert-custom.css +152 -78
- admin/assets/css/sweetalert-custom.css.map +1 -1
- admin/assets/css/sweetalert-custom.less +85 -4
- admin/assets/css/titan-security.css +213 -0
- admin/assets/img/check.png +0 -0
- admin/assets/img/clock.png +0 -0
- admin/assets/img/firewall-modal-preloader.gif +0 -0
- admin/assets/img/icon.png +0 -0
- admin/assets/img/loader.gif +0 -0
- admin/assets/img/titan-icon.png +0 -0
- admin/assets/img/x.png +0 -0
- admin/assets/js/dashboard.js +215 -0
- admin/assets/js/firewall/firewall-block-ip.js +126 -0
- admin/assets/js/firewall/firewall-dashboard.js +289 -0
- admin/assets/js/firewall/firewall-settings.js +20 -0
- admin/assets/js/import.js +103 -0
- admin/assets/js/libs/circular-progress.js +140 -0
- admin/assets/js/libs/jquery.datetimepicker.full.min.js +1 -0
- admin/assets/js/libs/popover.min.js +1 -0
- admin/assets/js/quickstart.js +16 -0
- admin/assets/js/settings.js +127 -18
- admin/assets/js/trial-popup.js +30 -4
- admin/boot.php +51 -106
- admin/class-activation.php +21 -25
- admin/class-page-titan-basic.php +119 -0
- admin/index.php +0 -0
- admin/pages/class-pages-about.php +0 -205
- admin/pages/{class-pages-settings.php → class-pages-antispam.php} +71 -52
- admin/pages/class-pages-check.php +386 -0
- admin/pages/class-pages-dashboard.php +380 -0
- admin/pages/class-pages-license.php +199 -121
- admin/pages/class-pages-logs.php +63 -44
- admin/pages/class-pages-plugin-settings.php +511 -0
- admin/pages/class-pages-scanner.php +114 -0
- admin/pages/class-pages-sitechecker.php +129 -0
- admin/pages/class-pages-tweaks.php +297 -0
- admin/pages/firewall/class-pages-bruteforce.php +309 -0
- admin/pages/firewall/class-pages-firewall-attacks-log.php +138 -0
- admin/pages/firewall/class-pages-firewall-blocking.php +152 -0
- admin/pages/firewall/class-pages-firewall-login-attempts.php +162 -0
- admin/pages/firewall/class-pages-firewall-settings.php +729 -0
- admin/pages/firewall/class-pages-firewall.php +107 -0
- anti-spam.php +92 -73
- assets/css/admin-bar.css +55 -0
- assets/img/titan-icon.png +0 -0
- {admin → includes/antispam}/assets/css/settings.css +56 -56
- {admin → includes/antispam}/assets/css/settings.less +0 -0
- includes/antispam/assets/js/settings.js +24 -0
- includes/antispam/boot.php +12 -0
- includes/antispam/classes/class-antispam.php +143 -0
- includes/{class-protector.php → antispam/classes/class-protector.php} +9 -8
- includes/antispam/functions.php +261 -0
- includes/audit/assets/css/audit-dashboard.css +16 -0
- includes/audit/assets/js/audit.js +3 -0
- includes/audit/assets/js/audit_ajax.js +40 -0
- includes/audit/boot.php +15 -0
- includes/audit/classes/class.audit.php +504 -0
- includes/audit/classes/class.auditresult.php +75 -0
- includes/audit/classes/class.cert.php +187 -0
- includes/audit/views/all-audit.php +73 -0
- includes/bruteforce/class-helpers.php +65 -0
- includes/bruteforce/class-limit-login-attempts.php +1086 -0
- includes/bruteforce/const.php +6 -0
- includes/bruteforce/do_activate.php +17 -0
- includes/check/assets/css/check-dashboard.css +148 -0
- includes/check/assets/img/ajax-loader-big.gif +0 -0
- includes/check/assets/img/close.svg +5 -0
- includes/check/assets/img/error.png +0 -0
- includes/check/assets/img/loader.gif +0 -0
- includes/check/assets/img/none.png +0 -0
- includes/check/assets/img/off.png +0 -0
- includes/check/assets/img/ok.png +0 -0
- includes/check/assets/img/warning.png +0 -0
- includes/check/assets/js/check.js +51 -0
- includes/check/boot.php +19 -0
- includes/check/classes/class.check.php +207 -0
- includes/check/views/check.php +25 -0
- includes/check/views/hided.php +70 -0
- includes/check/views/main.php +50 -0
- includes/class-anti-spam-plugin.php +0 -143
- includes/class-titan-security-plugin.php +315 -0
- includes/class-views.php +112 -0
- includes/class.module-base.php +100 -0
- includes/functions.php +406 -115
- includes/helpers.php +237 -0
- includes/index.php +2 -0
- includes/logger/assets/css/base.css +19 -19
- includes/logger/assets/js/base.js +5 -6
- includes/logger/class-logger-export.php +78 -72
- includes/logger/class-logger-reader.php +9 -9
- includes/logger/class-logger-writter.php +311 -292
- includes/scanner/assets/css/base-statistic.css +473 -0
- includes/scanner/assets/css/scanner-dashboard.css +47 -0
- includes/scanner/assets/js/Chart.min.js +10 -0
- includes/scanner/assets/js/scanner.js +107 -0
- includes/scanner/assets/js/statistic.js +34 -0
- includes/scanner/boot.php +12 -0
- includes/scanner/classes/class.scanner.php +166 -0
- includes/scanner/classes/scanner/File.php +118 -0
- includes/scanner/classes/scanner/HashListPool.php +61 -0
- includes/scanner/classes/scanner/Match.php +82 -0
- includes/scanner/classes/scanner/Scanner.php +207 -0
- includes/scanner/classes/scanner/Signature.php +177 -0
- includes/scanner/classes/scanner/SignaturePool.php +190 -0
- includes/scanner/classes/scanner/boot.php +13 -0
- includes/scanner/test.php +55 -0
- includes/scanner/views/results.php +87 -0
- includes/scanner/views/scanner.php +76 -0
- includes/sitechecker/assets/css/sitechecker-dashboard.css +180 -0
- includes/sitechecker/assets/img/delete.png +0 -0
- includes/sitechecker/assets/img/loader.gif +0 -0
- includes/sitechecker/assets/js/app.js +234 -0
- includes/sitechecker/assets/js/firebase-messaging-sw.js +46 -0
- includes/sitechecker/assets/js/firebase.min.js +5 -0
- includes/sitechecker/assets/js/sitechecker.js +83 -0
- includes/sitechecker/boot.php +14 -0
- includes/sitechecker/classes/class.sitechecker.php +238 -0
- includes/sitechecker/views/sitechecker.php +87 -0
- includes/tweaks/class-security-tweaks.php +361 -0
- includes/tweaks/password-requirements/assets/js/login-interstitial-util.js +131 -0
- includes/tweaks/password-requirements/assets/js/script.js +3 -0
- includes/tweaks/password-requirements/boot.php +13 -0
- includes/tweaks/password-requirements/class-canonical-roles.php +423 -0
- includes/tweaks/password-requirements/class-password-requirements-base.php +371 -0
- includes/tweaks/password-requirements/class-password-requirements.php +509 -0
- includes/tweaks/password-requirements/class-strong-passwords.php +260 -0
- includes/tweaks/password-requirements/libs/zxcvbn-php/index.php +1 -0
- includes/tweaks/password-requirements/libs/zxcvbn-php/matcher.php +103 -0
- includes/tweaks/password-requirements/libs/zxcvbn-php/matchers/adjacency_graphs.json +1 -0
- includes/tweaks/password-requirements/libs/zxcvbn-php/matchers/bruteforce.php +72 -0
- includes/tweaks/password-requirements/libs/zxcvbn-php/matchers/date.php +319 -0
- includes/tweaks/password-requirements/libs/zxcvbn-php/matchers/dictionary-l33t.php +114 -0
- includes/tweaks/password-requirements/libs/zxcvbn-php/matchers/dictionary-reverse.php +44 -0
- includes/tweaks/password-requirements/libs/zxcvbn-php/matchers/dictionary.php +211 -0
- includes/tweaks/password-requirements/libs/zxcvbn-php/matchers/index.php +1 -0
- 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(
|
7 |
-
check_admin_referer(
|
8 |
|
9 |
-
if
|
10 |
-
wp_die(
|
11 |
}
|
12 |
|
13 |
-
if
|
14 |
-
wp_send_json_error(
|
15 |
-
'message' => esc_html__(
|
16 |
-
'type'
|
17 |
-
]
|
18 |
}
|
19 |
|
20 |
-
wp_send_json(
|
21 |
-
'message' => esc_html__(
|
22 |
-
'type'
|
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-
|
3 |
-
background: rgba(16, 17, 21, 0.9);
|
4 |
-
z-index: 100000;
|
5 |
-
}
|
6 |
-
.
|
7 |
-
padding: 0 !important;
|
8 |
-
}
|
9 |
-
.
|
10 |
-
color: rgba(0, 0, 0, 0.8);
|
11 |
-
}
|
12 |
-
.
|
13 |
-
border-radius: 2px;
|
14 |
-
}
|
15 |
-
.
|
16 |
-
margin-bottom: 25px;
|
17 |
-
}
|
18 |
-
.
|
19 |
-
|
20 |
-
|
21 |
-
|
22 |
-
|
23 |
-
|
24 |
-
|
25 |
-
|
26 |
-
|
27 |
-
|
28 |
-
|
29 |
-
|
30 |
-
|
31 |
-
|
32 |
-
|
33 |
-
|
34 |
-
|
35 |
-
|
36 |
-
|
37 |
-
|
38 |
-
|
39 |
-
|
40 |
-
|
41 |
-
|
42 |
-
|
43 |
-
|
44 |
-
|
45 |
-
|
46 |
-
|
47 |
-
|
48 |
-
|
49 |
-
|
50 |
-
|
51 |
-
|
52 |
-
|
53 |
-
|
54 |
-
|
55 |
-
|
56 |
-
|
57 |
-
|
58 |
-
|
59 |
-
|
60 |
-
|
61 |
-
|
62 |
-
|
63 |
-
|
64 |
-
|
65 |
-
|
66 |
-
|
67 |
-
|
68 |
-
|
69 |
-
|
70 |
-
|
71 |
-
|
72 |
-
|
73 |
-
|
74 |
-
|
75 |
-
|
76 |
-
|
77 |
-
|
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,
|
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-
|
3 |
background: rgba(16, 17, 21, 0.9);
|
4 |
z-index: 100000;
|
5 |
}
|
6 |
|
7 |
-
.
|
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 |
-
|
3 |
-
|
4 |
-
|
5 |
-
|
6 |
-
|
7 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
8 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
9 |
|
10 |
-
|
11 |
-
|
|
|
|
|
12 |
|
13 |
-
|
|
|
|
|
|
|
14 |
|
15 |
-
|
16 |
-
|
17 |
-
|
|
|
18 |
|
19 |
-
|
20 |
-
e.stopPropagation();
|
21 |
-
window.location.href = 'https://anti-spam.space/pricing/';
|
22 |
-
});
|
23 |
|
24 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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 |
-
|
|
|
3 |
e.preventDefault();
|
4 |
-
var infosModal = $('#
|
|
|
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: '
|
14 |
width: 500,
|
15 |
showCancelButton: true,
|
16 |
showCloseButton: true,
|
17 |
confirmButtonText: 'Agree',
|
18 |
}).then(function(result) {
|
19 |
if( result.value ) {
|
20 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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 |
-
*
|
|
|
4 |
*
|
5 |
-
*
|
6 |
-
*
|
|
|
|
|
|
|
7 |
*/
|
8 |
|
9 |
// Exit if accessed directly
|
10 |
-
if
|
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 |
-
|
29 |
-
|
30 |
-
|
|
|
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(
|
43 |
-
if
|
|
|
|
|
|
|
|
|
44 |
return $notices;
|
45 |
}
|
46 |
-
|
47 |
-
$
|
|
|
|
|
|
|
48 |
|
49 |
$notices[] = [
|
50 |
-
'id'
|
51 |
-
'type'
|
52 |
-
'where'
|
53 |
'edit-comments',
|
54 |
'plugins',
|
55 |
'themes',
|
56 |
'dashboard',
|
57 |
'edit',
|
58 |
'settings'
|
59 |
-
]
|
60 |
-
'dismissible'
|
61 |
'dismiss_expires' => 0,
|
62 |
-
'text'
|
63 |
];
|
64 |
|
65 |
return $notices;
|
66 |
-
}, 10, 2
|
67 |
-
|
68 |
-
|
69 |
-
|
70 |
-
|
71 |
-
|
72 |
-
|
73 |
-
|
74 |
-
|
75 |
-
|
76 |
-
|
77 |
-
|
78 |
-
|
79 |
-
|
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\
|
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
|
11 |
* @version 1.0
|
12 |
*/
|
13 |
|
14 |
// Exit if accessed directly
|
15 |
-
if
|
16 |
exit;
|
17 |
}
|
18 |
|
19 |
-
class Activation extends \
|
20 |
|
21 |
/**
|
22 |
* Runs activation actions.
|
23 |
*
|
24 |
* @since 6.0
|
25 |
*/
|
26 |
-
public function activate()
|
27 |
-
|
28 |
-
$plugin_version_in_db
|
29 |
$current_plugin_version = $this->plugin->getPluginVersion();
|
30 |
|
31 |
-
$tab
|
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 |
-
|
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(
|
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 |
-
|
56 |
-
|
|
|
57 |
}
|
58 |
|
59 |
-
return get_option(
|
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 |
-
|
71 |
-
\WBCR\Logger\Writter::info(
|
|
|
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'> </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\
|
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 |
-
* @
|
|
|
17 |
* @version 1.0
|
18 |
*/
|
19 |
-
class
|
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 = "
|
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 $
|
52 |
|
53 |
/**
|
54 |
-
*
|
55 |
*
|
56 |
-
* @since
|
57 |
-
* @var
|
58 |
*/
|
59 |
-
public $
|
60 |
|
61 |
/**
|
62 |
-
*
|
63 |
*
|
64 |
-
* @since
|
65 |
-
* @var
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
66 |
*/
|
67 |
-
public $
|
|
|
68 |
|
69 |
/**
|
70 |
-
*
|
71 |
*
|
72 |
* @author Alexander Kovalev <alex.kovalevv@gmail.com>
|
73 |
*
|
74 |
-
* @param \
|
75 |
*
|
76 |
*/
|
77 |
-
public function __construct( \
|
78 |
-
$this->menu_title = __( 'Anti-spam', '
|
79 |
-
$this->page_menu_short_description = __( '
|
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
|
97 |
*
|
98 |
*/
|
99 |
public function assets( $scripts, $styles ) {
|
100 |
parent::assets( $scripts, $styles );
|
101 |
|
102 |
-
$this->styles->add(
|
103 |
-
$this->scripts->add(
|
104 |
'jquery',
|
105 |
-
'wbcr-factory-clearfy-
|
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 =
|
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.', '
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
129 |
];
|
130 |
|
131 |
$options[] = [
|
132 |
'type' => 'checkbox',
|
133 |
'way' => 'buttons',
|
134 |
'name' => 'save_spam_comments',
|
135 |
-
'title' => __( 'Save spam comments', '
|
136 |
'layout' => [ 'hint-type' => 'icon', 'hint-icon-color' => 'green' ],
|
137 |
-
'hint' => __( 'Save spam comments into spam section. Useful for testing how the plugin works.', '
|
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.', '
|
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.', '
|
149 |
'default' => false
|
150 |
];
|
151 |
}
|
152 |
|
153 |
$options[] = [
|
154 |
'type' => 'html',
|
155 |
-
'html' => '<div class="wbcr-factory-page-group-header">' . '<strong>' . __( 'Modules.', '
|
156 |
];
|
157 |
|
158 |
$options[] = [
|
159 |
'type' => 'checkbox',
|
160 |
'way' => 'buttons',
|
161 |
'name' => 'protect_register_form',
|
162 |
-
'title' => __( 'Protect Register Form', '
|
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.', '
|
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', '
|
173 |
'layout' => [ 'hint-type' => 'icon', 'hint-icon-color' => 'green' ],
|
174 |
-
'hint' =>
|
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', '
|
184 |
'layout' => [ 'hint-type' => 'icon', 'hint-icon-color' => 'green' ],
|
185 |
-
'hint' => __( 'Job Spam-Free for WordPress Contact Forms.', '
|
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', '
|
196 |
'layout' => [ 'hint-type' => 'icon', 'hint-icon-color' => 'green' ],
|
197 |
-
'hint' => __( 'Protects contact forms of the Ninja Forms plugin from 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', '
|
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.', '
|
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.', '
|
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\
|
4 |
|
5 |
// Exit if accessed directly
|
6 |
-
if
|
7 |
exit;
|
8 |
}
|
9 |
|
@@ -16,7 +16,7 @@ if ( ! defined( 'ABSPATH' ) ) {
|
|
16 |
*
|
17 |
* @copyright (c) 2018 Webraftic Ltd
|
18 |
*/
|
19 |
-
class License extends \
|
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(
|
47 |
-
|
48 |
-
$this->
|
49 |
-
$this->
|
|
|
|
|
50 |
|
51 |
-
|
52 |
|
53 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
54 |
}
|
55 |
|
56 |
/**
|
57 |
* {@inheritDoc}
|
58 |
-
* @since 6.5.2
|
59 |
-
*
|
60 |
* @param $notices
|
61 |
-
* @param \
|
62 |
*
|
63 |
* @return array
|
64 |
-
* @
|
|
|
|
|
65 |
*/
|
66 |
-
public function getActionNotices(
|
|
|
67 |
|
68 |
$notices[] = [
|
69 |
'conditions' => [
|
70 |
-
'
|
71 |
],
|
72 |
-
'type'
|
73 |
-
'message'
|
|
|
74 |
];
|
75 |
|
76 |
$notices[] = [
|
77 |
'conditions' => [
|
78 |
-
'
|
79 |
-
'
|
80 |
],
|
81 |
-
'type'
|
82 |
-
'message'
|
|
|
83 |
];
|
84 |
|
85 |
$notices[] = [
|
86 |
'conditions' => [
|
87 |
-
'
|
88 |
-
'
|
89 |
],
|
90 |
-
'type'
|
91 |
-
'message'
|
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(
|
104 |
-
|
105 |
-
|
106 |
-
|
107 |
-
|
108 |
-
|
109 |
-
|
110 |
-
$this->
|
111 |
-
|
|
|
|
|
|
|
|
|
|
|
112 |
}
|
113 |
}
|
114 |
|
115 |
/**
|
116 |
* {@inheritdoc}
|
117 |
*
|
118 |
-
* @since 6.5.2
|
119 |
* @return void
|
|
|
120 |
*/
|
121 |
-
|
122 |
-
|
123 |
-
|
124 |
-
|
|
|
|
|
125 |
|
126 |
?>
|
127 |
-
|
128 |
-
|
129 |
-
<?php _e(
|
130 |
-
|
131 |
-
|
132 |
-
|
133 |
-
|
134 |
-
<?php _e(
|
135 |
-
|
136 |
-
|
137 |
-
<?php printf(
|
138 |
-
<a href="%s" target="_blank" rel="noreferrer noopener">Privacy Policy (GDPR compilant)</a>', '
|
139 |
-
|
140 |
-
|
141 |
-
|
142 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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 |
-
|
157 |
-
|
158 |
-
|
159 |
-
$description
|
160 |
-
|
161 |
-
|
|
|
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 .= '<
|
|
|
165 |
}
|
166 |
|
167 |
return $description;
|
168 |
}
|
169 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
170 |
/**
|
171 |
-
* @author
|
172 |
-
* @since
|
173 |
*/
|
174 |
-
|
175 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
176 |
return;
|
177 |
}
|
178 |
|
179 |
-
|
180 |
|
181 |
-
\WBCR\Logger\
|
182 |
|
183 |
-
$admin_email =
|
184 |
-
$domain
|
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'
|
192 |
'domain' => site_url(),
|
193 |
]
|
194 |
];
|
195 |
|
196 |
// Get license key from remote server
|
197 |
-
|
198 |
-
|
199 |
-
|
200 |
-
|
201 |
-
\WBCR\Logger\
|
202 |
-
|
203 |
-
|
204 |
-
|
205 |
-
|
|
|
206 |
}
|
207 |
|
208 |
-
$data = json_decode(
|
209 |
|
210 |
-
if
|
211 |
-
if
|
212 |
$message = $data['error']['message'];
|
213 |
-
|
214 |
-
\WBCR\Logger\
|
215 |
-
|
216 |
-
|
217 |
-
|
218 |
-
|
219 |
-
'
|
220 |
-
]
|
221 |
} else {
|
222 |
-
|
223 |
-
'
|
224 |
-
|
225 |
-
] );
|
226 |
}
|
227 |
}
|
228 |
}
|
229 |
|
230 |
$license_key = $data['response']['license_key'];
|
231 |
|
232 |
-
if
|
233 |
-
\WBCR\Logger\
|
234 |
-
\WBCR\Logger\
|
235 |
-
|
236 |
-
'
|
237 |
-
|
238 |
-
] );
|
239 |
}
|
240 |
|
241 |
try {
|
242 |
-
$this->plugin->premium->activate(
|
|
|
|
|
|
|
243 |
|
244 |
-
|
245 |
-
|
246 |
-
|
247 |
-
'wantispam_trial_activated' => 1
|
248 |
-
] );
|
249 |
} catch( \Exception $e ) {
|
250 |
-
\WBCR\Logger\
|
251 |
-
\WBCR\Logger\
|
252 |
|
253 |
-
|
254 |
-
'
|
255 |
-
|
256 |
-
] );
|
257 |
}
|
258 |
|
|
|
|
|
259 |
// Redirect to index
|
260 |
-
|
261 |
-
|
|
|
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'> </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\
|
4 |
|
5 |
// Exit if accessed directly
|
6 |
-
if
|
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
|
20 |
|
21 |
/**
|
22 |
* {@inheritdoc}
|
@@ -26,66 +26,83 @@ class Logs extends \Wbcr_FactoryClearfy217_PageBase {
|
|
26 |
/**
|
27 |
* {@inheritdoc}
|
28 |
*/
|
29 |
-
public $page_menu_dashicon = 'dashicons-
|
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(
|
|
|
44 |
|
45 |
-
$this->menu_title
|
46 |
-
$this->page_menu_short_description = __(
|
47 |
|
48 |
-
parent::__construct(
|
49 |
}
|
50 |
|
51 |
/**
|
52 |
* {@inheritdoc}
|
53 |
*
|
54 |
-
* @since 1.0.0
|
55 |
* @return void
|
|
|
56 |
*/
|
57 |
-
public function assets(
|
58 |
-
|
|
|
59 |
|
60 |
-
$this->styles->add(
|
61 |
-
$this->scripts->add(
|
62 |
}
|
63 |
|
64 |
/**
|
65 |
* {@inheritdoc}
|
66 |
*/
|
67 |
-
public function showPageContent()
|
68 |
-
|
|
|
69 |
?>
|
70 |
-
|
71 |
-
|
72 |
-
|
73 |
-
<?php _e(
|
74 |
-
|
75 |
-
|
76 |
-
|
77 |
-
|
78 |
-
|
79 |
-
|
80 |
-
|
81 |
-
|
82 |
-
|
83 |
-
|
84 |
-
|
85 |
-
|
86 |
-
<?php echo \WBCR\Logger\Reader::prettify() ?>
|
87 |
-
|
88 |
-
|
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 |
-
|
99 |
-
|
|
|
100 |
|
101 |
-
if
|
102 |
-
$export->download(
|
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(
|
116 |
} catch( \Exception $exception ) {
|
117 |
-
\WBCR\Logger\Writter::error(
|
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'), '<head>'),
|
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>
|
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 <= 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 <= 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 <= 5.0 - PHP Object Injection via Meta Data & 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> <a href="#" id="whitelist-bulk-enable" class="disabled btn btn-default btn-small">Enable</a> <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>
|
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> 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> 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> 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> 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> 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-
|
4 |
Plugin URI: http://wordpress.org/plugins/anti-spam/
|
5 |
-
Description:
|
6 |
-
Version:
|
7 |
Author: CreativeMotion
|
8 |
-
Text Domain:
|
9 |
Author URI: https://cm-wp.com/
|
10 |
License: GPLv3
|
11 |
*/
|
12 |
|
13 |
// Exit if accessed directly
|
14 |
-
if
|
15 |
exit;
|
16 |
}
|
17 |
|
@@ -20,12 +20,20 @@ if ( ! defined( 'ABSPATH' ) ) {
|
|
20 |
*
|
21 |
* Alexander Kovalev
|
22 |
* ---------------------------------------------------------------------------------
|
23 |
-
*
|
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(
|
40 |
|
41 |
// @formatter:off
|
42 |
-
$
|
43 |
-
'prefix'
|
44 |
-
'plugin_name'
|
45 |
-
'plugin_title'
|
46 |
|
47 |
// PLUGIN SUPPORT
|
48 |
-
'support_details'
|
49 |
-
'url'
|
50 |
-
'pages_map' =>
|
51 |
'support' => 'support', // {site}/support
|
52 |
-
'docs'
|
53 |
-
|
54 |
-
|
|
|
55 |
|
56 |
// PLUGIN PREMIUM SETTINGS
|
57 |
-
'has_premium'
|
58 |
-
'license_settings'
|
59 |
-
'provider'
|
60 |
-
'slug'
|
61 |
-
'plugin_id'
|
62 |
-
'public_key'
|
63 |
-
|
64 |
-
|
65 |
-
'
|
66 |
-
|
67 |
-
|
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'
|
77 |
-
'adverts_settings'
|
78 |
'dashboard_widget' => true, // show dashboard widget (default: false)
|
79 |
-
'right_sidebar'
|
80 |
-
'notice'
|
81 |
-
|
82 |
|
83 |
// FRAMEWORK MODULES
|
84 |
-
'load_factory_modules' =>
|
85 |
-
|
86 |
-
|
87 |
-
|
88 |
-
|
89 |
-
|
90 |
-
|
91 |
-
|
92 |
-
|
93 |
-
|
94 |
-
|
95 |
-
|
96 |
-
|
97 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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
|
106 |
return;
|
107 |
}
|
108 |
|
@@ -115,11 +129,11 @@ if ( ! $cm_antspam_compatibility->check() ) {
|
|
115 |
*/
|
116 |
|
117 |
// This plugin is activated
|
118 |
-
define(
|
119 |
-
define(
|
120 |
-
define(
|
121 |
-
define(
|
122 |
-
define(
|
123 |
|
124 |
|
125 |
|
@@ -128,26 +142,31 @@ define( 'WANTISPAM_PLUGIN_URL', plugins_url( null, __FILE__ ) );
|
|
128 |
* PLUGIN INIT
|
129 |
* -----------------------------------------------------------------------------
|
130 |
*/
|
131 |
-
|
132 |
-
require_once(
|
133 |
-
require_once(
|
134 |
-
require_once( WANTISPAM_PLUGIN_DIR . '/includes/class-anti-spam-plugin.php' );
|
135 |
|
136 |
try {
|
137 |
-
new \WBCR\
|
138 |
-
'plugin_version'
|
139 |
-
'plugin_text_domain' => $
|
140 |
-
)
|
|
|
|
|
|
|
|
|
|
|
|
|
141 |
} catch( Exception $e ) {
|
142 |
// Plugin wasn't initialized due to an error
|
143 |
-
define(
|
144 |
|
145 |
-
$
|
146 |
-
$error = sprintf(
|
147 |
echo '<div class="notice notice-error"><p>' . $error . '</p></div>';
|
148 |
};
|
149 |
|
150 |
-
add_action(
|
151 |
-
add_action(
|
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: #
|
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 (
|
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',
|
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 =
|
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 =
|
95 |
-
$antspm_d =
|
96 |
-
$antspm_e =
|
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
|
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 = ' ';
|
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 = ' ';
|
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
|
6 |
-
* @copyright (c) 12.12.2019, Webcraftic
|
7 |
* @version 1.0
|
|
|
8 |
*/
|
9 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
10 |
/**
|
11 |
-
*
|
12 |
-
*
|
13 |
-
* @author Alexander Kovalev <alex.kovalevv@gmail.com>
|
14 |
-
* @since 6.5.3
|
15 |
*/
|
16 |
-
function
|
17 |
-
|
18 |
-
$
|
19 |
-
|
20 |
-
|
21 |
-
|
22 |
-
|
23 |
-
|
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 |
-
*
|
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
|
44 |
-
|
45 |
-
|
46 |
-
|
47 |
-
|
48 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
49 |
}
|
50 |
-
$html .= '</div>';
|
51 |
-
$html .= '<!--\End Anti-spam plugin -->';
|
52 |
|
53 |
-
|
|
|
|
|
|
|
|
|
|
|
54 |
}
|
55 |
|
56 |
/**
|
57 |
-
*
|
58 |
-
*
|
59 |
-
* @author Alexander Kovalev <alex.kovalevv@gmail.com>
|
60 |
-
* @since 6.5.3
|
61 |
*/
|
62 |
-
function
|
63 |
-
|
64 |
-
|
|
|
|
|
|
|
|
|
|
|
65 |
}
|
66 |
|
67 |
-
$
|
|
|
|
|
|
|
|
|
|
|
|
|
68 |
|
69 |
-
if (
|
70 |
-
return
|
71 |
}
|
72 |
|
73 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
74 |
}
|
75 |
|
76 |
/**
|
77 |
-
*
|
78 |
-
*
|
79 |
-
* @return string
|
80 |
-
* @since 6.5.3
|
81 |
-
* @author Alexander Kovalev <alex.kovalevv@gmail.com>
|
82 |
*/
|
83 |
-
function
|
84 |
-
|
85 |
-
|
86 |
-
|
87 |
-
|
88 |
-
|
89 |
-
|
90 |
-
|
91 |
-
|
92 |
-
|
93 |
-
|
94 |
-
|
95 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
96 |
}
|
97 |
|
98 |
/**
|
99 |
-
*
|
100 |
-
*
|
101 |
-
* @return bool
|
102 |
-
* @since 6.5.3
|
103 |
*/
|
104 |
-
function
|
105 |
-
|
106 |
-
|
107 |
-
|
108 |
-
$
|
|
|
|
|
|
|
|
|
109 |
}
|
110 |
|
111 |
-
|
112 |
-
|
113 |
-
$is_rest = wantispam_doing_rest_api();
|
114 |
|
115 |
-
|
116 |
-
|
117 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
118 |
|
119 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
120 |
}
|
121 |
|
122 |
/**
|
123 |
-
*
|
124 |
*
|
125 |
-
*
|
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 |
-
* @
|
131 |
-
* @since 2.1.0
|
132 |
-
* @return boolean
|
133 |
*/
|
134 |
-
function
|
135 |
-
|
136 |
-
$
|
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 |
-
|
144 |
-
|
145 |
-
|
|
|
146 |
|
147 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
148 |
}
|
149 |
|
150 |
/**
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
151 |
* @return bool
|
152 |
-
* @since 6.5.3
|
153 |
*/
|
154 |
-
function
|
155 |
-
if
|
156 |
-
return
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
157 |
}
|
158 |
|
159 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
160 |
}
|
161 |
|
162 |
/**
|
163 |
-
* @return
|
164 |
-
* @since 6.5.3
|
165 |
*/
|
166 |
-
function
|
167 |
-
|
168 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
169 |
}
|
170 |
|
171 |
-
return
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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: '
|
24 |
nonce: btn.data('nonce')
|
25 |
},
|
26 |
success: function(data) {
|
27 |
btn.html(currentBtnText);
|
28 |
|
29 |
-
|
30 |
-
|
31 |
-
|
32 |
},
|
33 |
error: function(jqXHR, textStatus, errorThrown) {
|
34 |
-
|
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 = '
|
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(
|
41 |
-
|
|
|
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
|
54 |
-
\WBCR\Logger\Writter::error(
|
55 |
|
56 |
return false;
|
57 |
}
|
58 |
|
59 |
$zip = new \ZipArchive();
|
60 |
|
61 |
-
$log_base_dir = \WBCR\Logger\Writter::get_base_dir();
|
62 |
|
63 |
-
if
|
64 |
-
\WBCR\Logger\Writter::error(
|
65 |
|
66 |
return false;
|
67 |
}
|
68 |
|
69 |
$uploads = wp_get_upload_dir();
|
70 |
|
71 |
-
if
|
72 |
-
\WBCR\Logger\Writter::error(
|
73 |
|
74 |
return false;
|
75 |
}
|
76 |
|
77 |
-
$save_base_path
|
78 |
-
$zip_archive_name = sprintf(
|
79 |
-
$zip_save_path
|
80 |
|
81 |
-
if
|
82 |
-
\WBCR\Logger\Writter::error(
|
83 |
|
84 |
return false;
|
85 |
}
|
86 |
|
87 |
// Add all logs to ZIP archive
|
88 |
$glob_path = $log_base_dir . '*.log';
|
89 |
-
$log_files = glob(
|
90 |
|
91 |
-
if
|
92 |
-
foreach
|
93 |
-
if
|
94 |
-
\WBCR\Logger\Writter::error(
|
95 |
|
96 |
return false;
|
97 |
}
|
@@ -100,25 +102,25 @@ class Export {
|
|
100 |
|
101 |
$system_info = $this->prepare_system_info();
|
102 |
|
103 |
-
if
|
104 |
-
$system_info_file_name = '
|
105 |
-
$system_info_path
|
106 |
-
if
|
107 |
-
if
|
108 |
-
\WBCR\Logger\Writter::error(
|
109 |
}
|
110 |
} else {
|
111 |
-
\WBCR\Logger\Writter::error(
|
112 |
}
|
113 |
}
|
114 |
|
115 |
-
if
|
116 |
-
\WBCR\Logger\Writter::error(
|
117 |
}
|
118 |
|
119 |
-
if
|
120 |
// Clean-up as this is just temp file
|
121 |
-
@unlink(
|
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
|
138 |
|
139 |
-
$report = 'Plugin version: ' . \WBCR\
|
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: ' . (
|
147 |
-
$report .= 'HTTP User Agent: ' . (
|
148 |
-
$report .= 'Server software: ' . (
|
149 |
|
150 |
$report .= $space;
|
151 |
|
152 |
-
$active_plugins = get_option(
|
153 |
|
154 |
-
if
|
155 |
|
156 |
$prepared_plugins = [];
|
157 |
|
158 |
$all_plugins = get_plugins();
|
159 |
|
160 |
-
foreach
|
161 |
-
if
|
162 |
-
$advanced_info
|
163 |
-
$name
|
164 |
-
$version
|
165 |
-
$prepared_plugins[] = sprintf(
|
166 |
}
|
167 |
}
|
168 |
|
169 |
$report .= 'Active plugins:' . PHP_EOL;
|
170 |
-
$report .= implode(
|
171 |
}
|
172 |
|
173 |
-
if
|
174 |
|
175 |
$report .= PHP_EOL . PHP_EOL;
|
176 |
$report .= 'Active extensions: ' . $nl;
|
177 |
-
$report .= implode(
|
178 |
}
|
179 |
|
180 |
$report .= $space;
|
181 |
|
182 |
-
$report .= 'Generated at: ' . date(
|
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
|
195 |
*
|
196 |
* @return bool
|
197 |
*/
|
198 |
-
public function download(
|
|
|
199 |
|
200 |
$zip_save_path = $this->_archive_save_path;
|
201 |
|
202 |
-
if
|
203 |
return false;
|
204 |
}
|
205 |
|
206 |
-
$zip_content = @file_get_contents(
|
207 |
|
208 |
-
if
|
209 |
-
\WBCR\Logger\Writter::error(
|
210 |
|
211 |
return false;
|
212 |
}
|
213 |
|
214 |
-
if
|
215 |
// Delete as ZIP is just for temporary usage
|
216 |
-
@unlink(
|
217 |
}
|
218 |
|
219 |
-
$archive_name = str_replace(
|
220 |
|
221 |
// Set-up headers to download export file
|
222 |
-
header(
|
223 |
-
header(
|
224 |
-
header(
|
225 |
-
header(
|
226 |
-
header(
|
227 |
-
header(
|
228 |
-
header(
|
229 |
-
header(
|
230 |
-
header(
|
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 |
-
|
|
|
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
|
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 |
-
|
45 |
-
|
46 |
-
|
47 |
-
|
48 |
-
|
49 |
-
|
50 |
-
|
51 |
-
|
52 |
-
|
53 |
-
|
54 |
-
|
55 |
-
|
56 |
-
|
57 |
-
|
58 |
-
|
59 |
-
|
60 |
-
|
61 |
-
|
62 |
-
|
63 |
-
|
64 |
-
|
65 |
-
|
66 |
-
|
67 |
-
|
68 |
-
|
69 |
-
|
70 |
-
|
71 |
-
|
72 |
-
|
73 |
-
|
74 |
-
|
75 |
-
|
76 |
-
|
77 |
-
|
78 |
-
|
79 |
-
|
80 |
-
|
81 |
-
|
82 |
-
|
83 |
-
|
84 |
-
|
85 |
-
|
86 |
-
|
87 |
-
|
88 |
-
|
89 |
-
|
90 |
-
|
91 |
-
|
92 |
-
|
93 |
-
|
|
|
|
|
|
|
94 |
|
95 |
-
|
96 |
-
|
97 |
-
|
98 |
-
|
99 |
-
|
|
|
100 |
|
101 |
-
|
102 |
-
|
103 |
|
104 |
-
|
105 |
-
|
106 |
-
|
107 |
-
|
108 |
-
|
109 |
-
|
110 |
-
|
111 |
-
|
|
|
112 |
|
113 |
-
|
114 |
|
115 |
-
|
116 |
-
|
117 |
-
|
118 |
|
119 |
-
|
120 |
|
121 |
-
|
122 |
-
|
123 |
-
|
124 |
|
125 |
-
|
126 |
-
|
127 |
|
128 |
-
|
129 |
|
130 |
-
|
131 |
-
|
132 |
|
133 |
-
|
134 |
|
135 |
-
|
136 |
-
|
|
|
137 |
}
|
138 |
}
|
|
|
|
|
|
|
139 |
}
|
140 |
|
141 |
-
|
142 |
-
@file_put_contents( $root_file, '' );
|
143 |
}
|
144 |
|
145 |
-
|
146 |
-
|
147 |
-
|
148 |
-
|
149 |
-
|
150 |
-
|
151 |
-
|
152 |
-
|
153 |
-
|
154 |
-
|
155 |
-
|
156 |
-
|
157 |
-
return null;
|
158 |
-
}
|
159 |
|
160 |
-
|
161 |
|
162 |
-
|
163 |
|
164 |
-
|
165 |
-
|
166 |
-
|
167 |
-
} else {
|
168 |
-
if ( function_exists( 'wp_salt' ) ) {
|
169 |
-
$hash = md5( wp_salt() );
|
170 |
} else {
|
171 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
172 |
}
|
173 |
|
174 |
-
$
|
175 |
-
}
|
176 |
|
177 |
-
|
|
|
|
|
178 |
|
179 |
-
|
180 |
-
|
181 |
-
}
|
182 |
|
183 |
-
|
184 |
-
|
|
|
|
|
185 |
|
186 |
-
|
187 |
-
$
|
188 |
-
@file_put_contents( $htaccess_path, $htaccess_content );
|
189 |
-
}
|
190 |
|
191 |
-
|
192 |
-
|
|
|
193 |
|
194 |
-
|
195 |
-
@file_put_contents( $index_path, '' );
|
196 |
}
|
197 |
|
198 |
-
|
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 |
-
|
210 |
-
return false;
|
211 |
}
|
212 |
|
213 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
214 |
|
215 |
-
|
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 |
-
|
233 |
-
|
234 |
-
|
235 |
-
|
236 |
-
|
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 |
-
|
255 |
|
256 |
-
|
257 |
-
|
258 |
-
|
259 |
|
260 |
-
|
261 |
-
return true;
|
262 |
-
}
|
263 |
|
264 |
-
|
265 |
|
266 |
-
|
267 |
-
|
268 |
-
$unlinked_count ++;
|
269 |
}
|
270 |
-
}
|
271 |
-
|
272 |
-
return count( $files ) === $unlinked_count;
|
273 |
-
}
|
274 |
|
275 |
-
|
276 |
-
|
277 |
-
|
278 |
-
* @return bool
|
279 |
-
*/
|
280 |
-
public static function flush() {
|
281 |
|
282 |
-
|
283 |
|
284 |
-
|
|
|
|
|
|
|
|
|
285 |
|
286 |
-
|
287 |
-
return false;
|
288 |
}
|
289 |
|
290 |
-
|
291 |
-
|
292 |
-
|
293 |
-
|
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 |
-
|
306 |
-
$template = '%s [%s][%s][%s] %s';
|
307 |
|
308 |
-
|
|
|
|
|
309 |
|
310 |
-
|
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 |
-
|
324 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
325 |
|
326 |
-
|
327 |
-
|
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 |
-
|
337 |
-
//$log_debug = defined( 'WP_DEBUG' ) && WP_DEBUG;
|
338 |
|
339 |
-
|
340 |
-
|
341 |
-
//}
|
342 |
-
//}
|
343 |
|
344 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
345 |
|
346 |
-
|
347 |
-
static::flush();
|
348 |
}
|
349 |
|
350 |
-
|
351 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
352 |
|
353 |
-
|
354 |
-
|
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 |
-
|
364 |
-
|
365 |
-
|
366 |
-
|
367 |
-
|
368 |
-
|
369 |
-
|
|
|
370 |
|
371 |
-
|
372 |
-
|
373 |
-
|
374 |
-
|
375 |
-
|
376 |
-
|
377 |
-
|
378 |
-
|
|
|
379 |
|
380 |
-
|
381 |
-
|
382 |
-
|
383 |
-
|
384 |
-
|
385 |
-
|
386 |
-
|
387 |
-
|
|
|
388 |
|
389 |
-
|
390 |
-
|
391 |
-
|
392 |
-
|
393 |
-
|
394 |
-
|
395 |
-
|
396 |
-
|
397 |
-
|
398 |
-
|
399 |
-
|
400 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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 |
+
"(?:Ρ|P)ay(?:Ρ|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; ?>"> </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
|