Version Description
Download this release
Release Info
Developer | paultgoodchild |
Plugin | Shield Security for WordPress |
Version | 10.2.0 |
Comparing to | |
See all releases |
Code changes from version 10.1.6 to 10.2.0
- cl.json +91 -0
- icwp-wpsf.php +2 -2
- init.php +4 -5
- plugin-spec.php +140 -19
- readme.txt +1 -1
- resources/css/bootstrap4.min.css +0 -7
- resources/css/{mainwp.css → mainwp-extension.css} +0 -0
- resources/css/plugin.css +1 -1
- resources/images/shield/background-blob.svg +18 -0
- resources/images/shield/dash-background.jpg +0 -0
- resources/js/bootstrap4.bundle.min.js +0 -7
- resources/js/bootstrap4.min.js +0 -7
- resources/js/global-plugin.js +21 -3
- resources/js/plugin.js +64 -61
- resources/js/{shield-scans.js → shield/scans.js} +0 -0
- resources/js/{shield-tables.js → shield/tables.js} +0 -0
- resources/js/{shield-u2f-admin.js → shield/u2f-admin.js} +1 -4
- resources/js/{shield-userprofile.js → shield/userprofile.js} +1 -1
- src/config/feature-hack_protect.php +1 -1
- src/config/feature-headers.php +1 -69
- src/config/feature-login_protect.php +1 -4
- src/config/feature-user_management.php +1 -1
- src/features/admin_access_restriction.php +0 -396
- src/features/audit_trail.php +0 -111
- src/features/autoupdates.php +0 -16
- src/features/base.php +0 -1599
- src/features/base_wpsf.php +0 -262
- src/features/comments_filter.php +0 -112
- src/features/comms.php +0 -19
- src/features/email.php +0 -11
- src/features/events.php +0 -34
- src/features/firewall.php +0 -68
- src/features/hack_protect.php +0 -326
- src/features/headers.php +0 -98
- src/features/insights.php +0 -226
- src/features/ips.php +0 -155
- src/features/license.php +0 -87
- src/features/lockdown.php +0 -39
- src/features/login_protect.php +0 -295
- src/features/plugin.php +0 -553
- src/features/reporting.php +0 -46
- src/features/sessions.php +0 -48
- src/features/traffic.php +0 -51
- src/features/user_management.php +0 -138
- src/lib/src/Controller/Admin/AdminBarMenu.php +50 -0
- src/lib/src/Controller/Admin/DashboardWidget.php +35 -0
- src/lib/src/Controller/Admin/MainAdminMenu.php +87 -0
- src/lib/src/Controller/Ajax/Init.php +55 -0
- src/lib/src/Controller/Ajax/Response.php +28 -0
- src/lib/src/Controller/Assets/Enqueue.php +174 -0
- src/lib/src/Controller/Assets/Urls.php +64 -0
- src/lib/src/Controller/Config/Ops/LoadConfig.php +1 -1
- src/lib/src/Controller/Controller.php +196 -505
- src/lib/src/Controller/Utilities/Upgrade.php +29 -10
- src/lib/src/Crons/StandardCron.php +1 -1
- src/lib/src/Databases/Base/EntryVoConsumer.php +3 -3
- src/lib/src/Databases/Base/Handler.php +7 -11
- src/lib/src/Databases/Base/Insert.php +8 -8
- src/lib/src/Databases/IPs/CommonFilters.php +8 -4
- src/lib/src/Modules/AuditTrail/Options.php +0 -71
- src/lib/src/Modules/Autoupdates/Insights/OverviewCards.php +3 -3
- src/lib/src/Modules/Autoupdates/Processor.php +1 -1
- src/lib/src/Modules/Base/AdminNotices.php +1 -1
- src/lib/src/Modules/Base/AjaxHandlerBase.php +0 -110
- src/lib/src/Modules/Base/AjaxHandlerShield.php +0 -82
- src/lib/src/Modules/Base/BaseProcessor.php +1 -8
- src/lib/src/Modules/Base/BaseReporting.php +0 -54
- src/lib/src/Modules/Base/ModCon.php +53 -99
- src/lib/src/Modules/Base/Options.php +53 -68
- src/lib/src/Modules/Base/Processor.php +1 -28
- src/lib/src/Modules/Base/ShieldOptions.php +0 -34
- src/lib/src/Modules/Base/ShieldUI.php +0 -70
- src/lib/src/Modules/Base/UI.php +17 -16
- src/lib/src/Modules/BaseShield/ModCon.php +1 -19
- src/lib/src/Modules/BaseShield/ShieldProcessor.php +1 -20
- src/lib/src/Modules/CommentsFilter/Scan/Scanner.php +2 -2
- src/lib/src/Modules/Events/Lib/EventsService.php +0 -8
- src/lib/src/Modules/Events/Lib/Reports/KeyStats.php +1 -1
- src/lib/src/Modules/Events/Lib/Reports/ScanRepairs.php +1 -1
- src/lib/src/Modules/GeoIp/Lookup.php +12 -12
- src/lib/src/Modules/HackGuard/AjaxHandler.php +50 -50
- src/lib/src/Modules/HackGuard/Debug.php +19 -0
- src/lib/src/Modules/HackGuard/Lib/FileLocker/FileLockerController.php +2 -2
- src/lib/src/Modules/HackGuard/Lib/FileLocker/Ops/AssessLocks.php +1 -1
- src/lib/src/Modules/HackGuard/Lib/FileLocker/Ops/ReadOriginalFileContent.php +6 -6
- src/lib/src/Modules/HackGuard/Lib/FileLocker/Ops/Verify.php +4 -4
- src/lib/src/Modules/HackGuard/Lib/Snapshots/Build/BuildHashesFromDir.php +1 -1
- src/lib/src/Modules/HackGuard/Lib/Snapshots/Store.php +2 -2
- src/lib/src/Modules/HackGuard/Lib/Snapshots/StoreAction/Build.php +19 -20
- src/lib/src/Modules/HackGuard/Lib/Snapshots/StoreAction/CleanAll.php +1 -1
- src/lib/src/Modules/HackGuard/Lib/Snapshots/StoreAction/Delete.php +4 -4
- src/lib/src/Modules/HackGuard/Lib/Snapshots/StoreAction/ScheduleBuildAll.php +11 -14
- src/lib/src/Modules/HackGuard/Lib/Snapshots/StoreAction/TouchAll.php +5 -5
- src/lib/src/Modules/HackGuard/ModCon.php +1 -23
- src/lib/src/Modules/HackGuard/Options.php +1 -1
- src/lib/src/Modules/HackGuard/Scan/Controller/Base.php +17 -17
- src/lib/src/Modules/HackGuard/Scan/Controller/BaseForAssets.php +6 -6
- src/lib/src/Modules/HackGuard/Scan/Controller/Mal.php +9 -9
- src/lib/src/Modules/HackGuard/Scan/Controller/Ptg.php +5 -7
- src/lib/src/Modules/HackGuard/Scan/Controller/Ufc.php +1 -1
- src/lib/src/Modules/HackGuard/Scan/Controller/Wcf.php +3 -4
- src/lib/src/Modules/HackGuard/Scan/Queue/Build/QueueBuilder.php +5 -5
- src/lib/src/Modules/HackGuard/Scan/Queue/QueueProcessor.php +2 -2
- src/lib/src/Modules/HackGuard/Scan/ScansController.php +2 -4
- src/lib/src/Modules/HackGuard/Scan/Utilities/PtgAddReinstallLinks.php +25 -29
- src/lib/src/Modules/HackGuard/Scan/Utilities/WpvAddPluginRows.php +19 -7
- src/lib/src/Modules/HackGuard/UI.php +1 -4
- src/lib/src/Modules/HackGuard/Upgrade.php +12 -1
- src/lib/src/Modules/Headers/Insights/OverviewCards.php +1 -1
- src/lib/src/Modules/Headers/Options.php +16 -29
- src/lib/src/Modules/Headers/Processor.php +5 -28
- src/lib/src/Modules/Headers/Strings.php +7 -2
- src/lib/src/Modules/IPs/AjaxHandler.php +2 -2
- src/lib/src/Modules/IPs/BotTrack/Base.php +1 -4
- src/lib/src/Modules/IPs/BotTrack/TrackFakeWebCrawler.php +1 -1
- src/lib/src/Modules/IPs/BotTrack/TrackLoginFailed.php +10 -13
- src/lib/src/Modules/IPs/BotTrack/TrackLoginInvalid.php +16 -11
- src/lib/src/Modules/IPs/Components/QueryIpBlock.php +7 -15
- src/lib/src/Modules/IPs/Components/UnblockIpByFlag.php +4 -4
- src/lib/src/Modules/IPs/Lib/BlockRequest.php +3 -10
- src/lib/src/Modules/IPs/Lib/Ops/LookupIpOnList.php +28 -28
- src/lib/src/Modules/IPs/WpCli/Add.php +2 -2
- src/lib/src/Modules/Insights/ModCon.php +89 -178
- src/lib/src/Modules/Integrations/Lib/MainWP/Client/Actions/Sync.php +1 -1
- src/lib/src/Modules/Integrations/Lib/MainWP/Server/Data/ClientPluginStatus.php +1 -1
- src/lib/src/Modules/Integrations/Lib/MainWP/Server/Init.php +1 -1
- src/lib/src/Modules/Integrations/Lib/MainWP/Server/UI/ExtensionSettingsPage.php +10 -23
- src/lib/src/Modules/License/AjaxHandler.php +13 -13
- src/lib/src/Modules/License/Lib/LicenseHandler.php +1 -1
- src/lib/src/Modules/License/Lib/PluginNameSuffix.php +29 -0
- src/lib/src/Modules/License/Lib/WpHashes/ApiTokenManager.php +1 -1
- src/lib/src/Modules/License/Processor.php +6 -0
- src/lib/src/Modules/License/WpCli/License.php +2 -2
- src/lib/src/Modules/LoginGuard/AjaxHandler.php +14 -12
- src/lib/src/Modules/LoginGuard/Lib/AntiBot/FormProviders/BaseFormProvider.php +7 -7
- src/lib/src/Modules/LoginGuard/Lib/AntiBot/FormProviders/EasyDigitalDownloads.php +2 -2
- src/lib/src/Modules/LoginGuard/Lib/AntiBot/FormProviders/LearnPress.php +4 -4
- src/lib/src/Modules/LoginGuard/Lib/AntiBot/FormProviders/MemberPress.php +6 -6
- src/lib/src/Modules/LoginGuard/Lib/AntiBot/FormProviders/PaidMemberSubscriptions.php +2 -2
- src/lib/src/Modules/LoginGuard/Lib/AntiBot/FormProviders/ProfileBuilder.php +5 -5
- src/lib/src/Modules/LoginGuard/Lib/AntiBot/FormProviders/UltimateMember.php +6 -6
- src/lib/src/Modules/LoginGuard/Lib/AntiBot/FormProviders/UserRegistration.php +5 -5
- src/lib/src/Modules/LoginGuard/Lib/AntiBot/FormProviders/WPMembers.php +3 -3
- src/lib/src/Modules/LoginGuard/Lib/AntiBot/FormProviders/WooCommerce.php +6 -6
- src/lib/src/Modules/LoginGuard/Lib/AntiBot/FormProviders/WordPress.php +6 -6
- src/lib/src/Modules/LoginGuard/Lib/AntiBot/ProtectionProviders/GaspJs.php +6 -6
- src/lib/src/Modules/LoginGuard/Lib/AntiBot/ProtectionProviders/GoogleRecaptcha.php +2 -2
- src/lib/src/Modules/LoginGuard/Lib/TwoFactor/LoginIntentPage.php +7 -7
- src/lib/src/Modules/LoginGuard/Lib/TwoFactor/MfaController.php +0 -8
- src/lib/src/Modules/LoginGuard/Lib/TwoFactor/Provider/Backup.php +0 -6
- src/lib/src/Modules/LoginGuard/Lib/TwoFactor/Provider/BaseProvider.php +4 -21
- src/lib/src/Modules/LoginGuard/Lib/TwoFactor/Provider/Email.php +3 -20
- src/lib/src/Modules/LoginGuard/Lib/TwoFactor/Provider/GoogleAuth.php +5 -14
- src/lib/src/Modules/LoginGuard/Lib/TwoFactor/Provider/U2F.php +55 -86
- src/lib/src/Modules/LoginGuard/Lib/TwoFactor/Provider/Yubikey.php +16 -27
- src/lib/src/Modules/LoginGuard/ModCon.php +20 -8
- src/lib/src/Modules/LoginGuard/UI.php +16 -4
- src/lib/src/Modules/ModConsumer.php +8 -8
- src/lib/src/Modules/Plugin/AdminNotices.php +5 -5
- src/lib/src/Modules/Plugin/AjaxHandler.php +9 -9
- src/lib/src/Modules/Plugin/Components/PluginBadge.php +2 -2
- src/lib/src/Modules/Plugin/Components/SiteGroundPluginCompatibility.php +11 -14
- src/lib/src/Modules/Plugin/Lib/Debug/Collate.php +9 -0
- src/lib/src/Modules/Plugin/Lib/ImportExport/Export.php +1 -1
- src/lib/src/Modules/Plugin/Lib/ImportExport/Import.php +4 -4
- src/lib/src/Modules/Plugin/Lib/PluginTelemetry.php +7 -5
- src/lib/src/Modules/Plugin/Lib/TourManager.php +5 -5
- src/lib/src/Modules/Plugin/ModCon.php +39 -24
- src/lib/src/Modules/Plugin/WpCli/Import.php +3 -3
- src/lib/src/Modules/SecurityAdmin/Lib/WhiteLabel/ApplyLabels.php +13 -13
- src/lib/src/Modules/SecurityAdmin/ModCon.php +11 -11
- src/lib/src/Modules/SecurityAdmin/WpCli/Pin.php +2 -2
- src/lib/src/Modules/Sessions/Processor.php +0 -64
- src/lib/src/Modules/Traffic/Lib/Limit/Limiter.php +5 -5
- src/lib/src/Modules/UserManagement/Lib/Password/UserPasswordHandler.php +2 -16
- src/lib/src/Modules/UserManagement/ModCon.php +0 -8
- src/lib/src/Modules/UserManagement/Options.php +0 -8
- src/lib/src/Scans/Base/BaseScanActionVO.php +4 -4
- src/lib/src/Scans/Base/Files/BaseFileMapScan.php +6 -6
- src/lib/src/Scans/Base/Files/BaseFileScanner.php +2 -2
- src/lib/src/Scans/Base/Files/BaseScanFromFileMap.php +9 -10
- src/lib/src/Scans/Base/Table/BaseFileEntryFormatter.php +3 -3
- src/lib/src/Scans/Base/Utilities/ItemActionHandler.php +7 -7
- src/lib/src/Scans/Base/Utilities/ItemActionHandlerAssets.php +4 -4
- src/lib/src/Scans/Helpers/BuildHashesFromDir.php +1 -1
- src/lib/src/Scans/Helpers/WpCoreFile.php +1 -1
- src/lib/src/Scans/Mal/BuildFileMap.php +27 -31
- src/lib/src/Scans/Mal/FileScanner.php +115 -98
- src/lib/src/Scans/Mal/Scan.php +9 -8
- src/lib/src/Scans/Mal/ScanActionVO.php +2 -1
- src/lib/src/Scans/Mal/Table/EntryFormatter.php +8 -8
- src/lib/src/Scans/Mal/Utilities/FalsePositiveQuery.php +27 -31
- src/lib/src/Scans/Mal/Utilities/FalsePositiveReporter.php +1 -1
- src/lib/src/Scans/Mal/Utilities/Patterns.php +21 -21
- src/lib/src/Scans/Mal/Utilities/Repair.php +37 -37
- src/lib/src/Scans/Ptg/BuildFileMap.php +3 -3
- src/lib/src/Scans/Ptg/FileScanner.php +22 -22
- src/lib/src/Scans/Ptg/Utilities/ItemActionHandler.php +21 -21
- src/lib/src/Scans/Ptg/Utilities/Repair.php +10 -10
- src/lib/src/Scans/Ufc/BuildFileMap.php +2 -2
- src/lib/src/Scans/Ufc/FileScanner.php +10 -10
- src/lib/src/Scans/Wcf/FileScanner.php +12 -12
- src/lib/src/Scans/Wpv/WpVulnDb/WpVulnVO.php +28 -4
- src/lib/src/Tables/Build/Traffic.php +107 -70
- src/lib/src/Tables/Render/WpListTable/Base.php +4 -4
- src/lib/src/Tables/Render/WpListTable/ScanWpv.php +9 -9
- src/lib/src/Utilities/ReCaptcha/TestRequest.php +2 -2
- src/lib/src/Utilities/Time/WorldTimeApi.php +33 -0
- src/lib/vendor/autoload.php +1 -1
- src/lib/vendor/composer/ClassLoader.php +34 -2
- src/lib/vendor/composer/autoload_classmap.php +15 -59
- src/lib/vendor/composer/autoload_real.php +11 -9
- src/lib/vendor/composer/autoload_static.php +22 -66
- src/lib/vendor/composer/platform_check.php +26 -0
- src/lib/vendor/fernleafsystems/utilities/src/Data/Adapter/DynamicProperties.php +74 -0
- src/lib/vendor/fernleafsystems/utilities/src/Data/Adapter/DynamicPropertiesClass.php +13 -0
- src/lib/vendor/fernleafsystems/utilities/src/Data/Adapter/StdClassAdapter.php +8 -7
- src/lib/vendor/fernleafsystems/utilities/src/Data/CaptureOutput.php +13 -0
- src/lib/vendor/fernleafsystems/utilities/src/Logic/ExecOnce.php +31 -0
- src/lib/vendor/fernleafsystems/wordpress-services/src/Core/AdminNotices.php +3 -3
- src/lib/vendor/fernleafsystems/wordpress-services/src/Core/CoreFileHashes.php +31 -34
- src/lib/vendor/fernleafsystems/wordpress-services/src/Core/Fs.php +22 -37
- src/lib/vendor/fernleafsystems/wordpress-services/src/Core/General.php +3 -3
- src/lib/vendor/fernleafsystems/wordpress-services/src/Core/Nonce.php +18 -35
- src/lib/vendor/fernleafsystems/wordpress-services/src/Core/Request.php +116 -134
- src/lib/vendor/fernleafsystems/wordpress-services/src/Core/Response.php +32 -32
- src/lib/vendor/fernleafsystems/wordpress-services/src/Core/Themes.php +72 -96
- src/lib/vendor/fernleafsystems/wordpress-services/src/Core/Users.php +2 -2
- src/lib/vendor/fernleafsystems/wordpress-services/src/Core/VOs/WpBaseVo.php +3 -6
- src/lib/vendor/fernleafsystems/wordpress-services/src/Core/VOs/WpPluginVo.php +12 -15
- src/lib/vendor/fernleafsystems/wordpress-services/src/Core/VOs/WpThemeVo.php +16 -22
- src/lib/vendor/fernleafsystems/wordpress-services/src/Utilities/Data.php +19 -23
- src/lib/vendor/fernleafsystems/wordpress-services/src/Utilities/File/GetFileAsArray.php +8 -8
- src/lib/vendor/fernleafsystems/wordpress-services/src/Utilities/File/LocateStrInFile.php +59 -70
- src/lib/vendor/fernleafsystems/wordpress-services/src/Utilities/Net/IpIdentify.php +4 -4
- src/lib/vendor/fernleafsystems/wordpress-services/src/Utilities/Obfuscate.php +6 -6
- src/lib/vendor/fernleafsystems/wordpress-services/src/Utilities/Options/Transient.php +24 -24
- src/lib/vendor/fernleafsystems/wordpress-services/src/Utilities/Render.php +6 -6
- src/processors/admin_access_restriction.php +0 -450
- src/processors/audit_trail.php +0 -74
- src/processors/autoupdates.php +0 -456
- src/processors/comments_filter.php +0 -75
- src/processors/commentsfilter_botspam.php +0 -116
- src/processors/commentsfilter_googlerecaptcha.php +0 -35
- src/processors/email.php +0 -218
- src/processors/events.php +0 -94
- src/processors/firewall.php +0 -430
- src/processors/hack_protect.php +0 -26
- src/processors/hackprotect_integrity.php +0 -136
- src/processors/hackprotect_scan_apc.php +0 -11
- src/processors/hackprotect_scan_base.php +0 -46
- src/processors/hackprotect_scan_mal.php +0 -11
- src/processors/hackprotect_scan_ptg.php +0 -25
- src/processors/hackprotect_scan_ufc.php +0 -11
- src/processors/hackprotect_scan_wcf.php +0 -11
- src/processors/hackprotect_scan_wpv.php +0 -74
- src/processors/hackprotect_scanner.php +0 -89
- src/processors/headers.php +0 -230
- src/processors/lockdown.php +0 -136
- src/processors/login_protect.php +0 -52
- src/processors/loginprotect_wplogin.php +0 -236
- src/processors/plugin.php +0 -68
- src/processors/plugin_tracking.php +0 -29
- src/processors/sessions.php +0 -191
- src/processors/traffic.php +0 -24
- src/processors/user_management.php +0 -265
- src/processors/usermanagement_passwords.php +0 -333
- src/processors/usermanagement_sessions.php +0 -186
- src/wizards/base.php +16 -16
- src/wizards/plugin.php +15 -15
- templates/php/snippets/module-help-firewall.php +1 -1
- templates/php/snippets/plugin-vulnerability.php +0 -25
- templates/twig/components/options_form/main.twig +2 -2
- templates/twig/snippets/js/freshdesk_chatbot.twig +22 -0
- templates/twig/snippets/plugin_vulnerability.twig +21 -0
- templates/twig/wpadmin_pages/insights/base.twig +1 -1
- templates/twig/wpadmin_pages/insights/docs/index.twig +4 -2
- templates/twig/wpadmin_pages/insights/importexport/index.twig +5 -2
- templates/twig/wpadmin_pages/insights/ips/index.twig +7 -4
- templates/twig/wpadmin_pages/insights/ips/ip_analyse/ip_info.twig +8 -4
- templates/twig/wpadmin_pages/security_admin/index.twig +3 -2
cl.json
CHANGED
@@ -1,4 +1,95 @@
|
|
1 |
{
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2 |
"10.1": {
|
3 |
"version": "10.1",
|
4 |
"released_at": 1605606920,
|
1 |
{
|
2 |
+
"10.2": {
|
3 |
+
"version": "10.2",
|
4 |
+
"released_at": 1613037000,
|
5 |
+
"hrefs": {
|
6 |
+
"release": "https://shsec.io/shieldrelease102",
|
7 |
+
"upgrade": "https://shsec.io/shieldupgradeguide102"
|
8 |
+
},
|
9 |
+
"href": "https://shsec.io/",
|
10 |
+
"title": "Removal of simple Content Security Policy settings and bugfixes",
|
11 |
+
"description": [
|
12 |
+
"We've decided to remove our simple Content Security Policy options as this feature is too complex.",
|
13 |
+
"We've also fixed a number of bugs and optimised how Shield loads and stores options and configurations."
|
14 |
+
],
|
15 |
+
"items": [
|
16 |
+
{
|
17 |
+
"type": "new",
|
18 |
+
"pro_only": false,
|
19 |
+
"title": "Removed Content Security Policy Settings",
|
20 |
+
"description": [
|
21 |
+
"Due to the complexity of CSP and the superficial nature of our CSP implementation, we've decided to remove these options.",
|
22 |
+
"We explore the issue in full detail in our blog post on this topic."
|
23 |
+
],
|
24 |
+
"href": "https://shsec.io/jb"
|
25 |
+
},
|
26 |
+
{
|
27 |
+
"type": "new",
|
28 |
+
"title": "Invalid user login tracking covers empty usernames.",
|
29 |
+
"description": [
|
30 |
+
"When tracking for bots logging in user invalid usernames (i.e. that don't exist) it'll also trigger an offense on empty usernames."
|
31 |
+
]
|
32 |
+
},
|
33 |
+
{
|
34 |
+
"type": "improved",
|
35 |
+
"title": "Deleting Malware files doesn't initiate a new scan.",
|
36 |
+
"description": [
|
37 |
+
"This addresses a reported UX issue where bulk malware deletion isn't yet available and so instead of a full re-scan, the page just reloads."
|
38 |
+
]
|
39 |
+
},
|
40 |
+
{
|
41 |
+
"type": "improved",
|
42 |
+
"title": "Malware scanners are more efficient.",
|
43 |
+
"description": [
|
44 |
+
"Malware scanning is involved - every PHP file has to be read and then searched using a large set of patterns.",
|
45 |
+
"So it takes time. Hopefully these tweaks will optimise this process a little and lead to faster scans."
|
46 |
+
]
|
47 |
+
},
|
48 |
+
{
|
49 |
+
"type": "improved",
|
50 |
+
"title": "Add IP status to information in the traffic viewer.",
|
51 |
+
"description": [
|
52 |
+
"The traffic table will now display many offenses or whether the IP address is blocked."
|
53 |
+
]
|
54 |
+
},
|
55 |
+
{
|
56 |
+
"type": "improved",
|
57 |
+
"title": "Upgrade Bootstrap Library to latest 4.6.0",
|
58 |
+
"description": [
|
59 |
+
"Asset enqueuing has been refactored and optimised and also now loading Bootstrap assets from CDNJS."
|
60 |
+
]
|
61 |
+
},
|
62 |
+
{
|
63 |
+
"type": "improved",
|
64 |
+
"title": "Significant code cleanup.",
|
65 |
+
"description": []
|
66 |
+
},
|
67 |
+
{
|
68 |
+
"type": "improved",
|
69 |
+
"title": "Added cleanup code to remove stale entries in the WP Options table.",
|
70 |
+
"description": []
|
71 |
+
},
|
72 |
+
{
|
73 |
+
"type": "improved",
|
74 |
+
"title": "Added detection of server clock inconsistencies which break Google Authenticator.",
|
75 |
+
"description": []
|
76 |
+
},
|
77 |
+
{
|
78 |
+
"type": "fixed",
|
79 |
+
"title": "U2F/Yubikey Removal Bug",
|
80 |
+
"description": [
|
81 |
+
"A javascript issue prevented removal of U2F keys from user profiles."
|
82 |
+
]
|
83 |
+
},
|
84 |
+
{
|
85 |
+
"type": "fixed",
|
86 |
+
"title": "FileLocker would fail to load file contents if it exceeded 64KB.",
|
87 |
+
"description": [
|
88 |
+
"We upgraded the database table definition to allow for much larger files."
|
89 |
+
]
|
90 |
+
}
|
91 |
+
]
|
92 |
+
},
|
93 |
"10.1": {
|
94 |
"version": "10.1",
|
95 |
"released_at": 1605606920,
|
icwp-wpsf.php
CHANGED
@@ -3,7 +3,7 @@
|
|
3 |
* Plugin Name: Shield Security
|
4 |
* Plugin URI: https://shsec.io/2f
|
5 |
* Description: Powerful, Easy-To-Use #1 Rated WordPress Security System
|
6 |
-
* Version: 10.
|
7 |
* Text Domain: wp-simple-firewall
|
8 |
* Domain Path: /languages
|
9 |
* Author: Shield Security
|
@@ -39,7 +39,7 @@ elseif ( @is_file( dirname( __FILE__ ).'/src/lib/vendor/autoload.php' ) ) {
|
|
39 |
|
40 |
add_action( 'plugins_loaded', 'icwp_wpsf_init', 1 ); // use 0 for extensions to ensure hooks have been added.
|
41 |
function icwp_wpsf_init() {
|
42 |
-
$
|
43 |
require_once( dirname( __FILE__ ).'/init.php' );
|
44 |
}
|
45 |
|
3 |
* Plugin Name: Shield Security
|
4 |
* Plugin URI: https://shsec.io/2f
|
5 |
* Description: Powerful, Easy-To-Use #1 Rated WordPress Security System
|
6 |
+
* Version: 10.2.0
|
7 |
* Text Domain: wp-simple-firewall
|
8 |
* Domain Path: /languages
|
9 |
* Author: Shield Security
|
39 |
|
40 |
add_action( 'plugins_loaded', 'icwp_wpsf_init', 1 ); // use 0 for extensions to ensure hooks have been added.
|
41 |
function icwp_wpsf_init() {
|
42 |
+
$rootFile = __FILE__;
|
43 |
require_once( dirname( __FILE__ ).'/init.php' );
|
44 |
}
|
45 |
|
init.php
CHANGED
@@ -2,9 +2,8 @@
|
|
2 |
|
3 |
use FernleafSystems\Wordpress\Plugin\Shield;
|
4 |
|
5 |
-
/** @var string $
|
6 |
global $oICWP_Wpsf;
|
7 |
-
|
8 |
if ( isset( $oICWP_Wpsf ) ) {
|
9 |
error_log( 'Attempting to load the Shield Plugin twice?' );
|
10 |
return;
|
@@ -49,13 +48,13 @@ class ICWP_WPSF_Shield_Security {
|
|
49 |
}
|
50 |
|
51 |
try {
|
52 |
-
$oICWP_Wpsf_Controller = Shield\Controller\Controller::GetInstance( $
|
53 |
$oICWP_Wpsf = ICWP_WPSF_Shield_Security::GetInstance( $oICWP_Wpsf_Controller );
|
54 |
}
|
55 |
-
catch ( \Exception $
|
56 |
if ( is_admin() ) {
|
57 |
error_log( 'Perhaps due to a failed upgrade, the Shield plugin failed to load certain component(s) - you should remove the plugin and reinstall.' );
|
58 |
-
error_log( $
|
59 |
}
|
60 |
}
|
61 |
|
2 |
|
3 |
use FernleafSystems\Wordpress\Plugin\Shield;
|
4 |
|
5 |
+
/** @var string $rootFile */
|
6 |
global $oICWP_Wpsf;
|
|
|
7 |
if ( isset( $oICWP_Wpsf ) ) {
|
8 |
error_log( 'Attempting to load the Shield Plugin twice?' );
|
9 |
return;
|
48 |
}
|
49 |
|
50 |
try {
|
51 |
+
$oICWP_Wpsf_Controller = Shield\Controller\Controller::GetInstance( $rootFile );
|
52 |
$oICWP_Wpsf = ICWP_WPSF_Shield_Security::GetInstance( $oICWP_Wpsf_Controller );
|
53 |
}
|
54 |
+
catch ( \Exception $e ) {
|
55 |
if ( is_admin() ) {
|
56 |
error_log( 'Perhaps due to a failed upgrade, the Shield plugin failed to load certain component(s) - you should remove the plugin and reinstall.' );
|
57 |
+
error_log( $e->getMessage() );
|
58 |
}
|
59 |
}
|
60 |
|
plugin-spec.php
CHANGED
@@ -1,8 +1,8 @@
|
|
1 |
{
|
2 |
"properties": {
|
3 |
-
"version": "10.
|
4 |
-
"release_timestamp":
|
5 |
-
"build": "
|
6 |
"slug_parent": "icwp",
|
7 |
"slug_plugin": "wpsf",
|
8 |
"human_name": "Shield Security",
|
@@ -23,10 +23,6 @@
|
|
23 |
"wordpress": "3.5.2"
|
24 |
},
|
25 |
"upgrade_reqs": {
|
26 |
-
"7.0": {
|
27 |
-
"php": "5.4",
|
28 |
-
"wp": "3.5.2"
|
29 |
-
},
|
30 |
"10.0": {
|
31 |
"php": "7.0",
|
32 |
"wp": "3.5.2"
|
@@ -48,23 +44,17 @@
|
|
48 |
"global-plugin"
|
49 |
],
|
50 |
"js": [
|
51 |
-
"jquery",
|
52 |
"global-plugin"
|
53 |
]
|
54 |
},
|
55 |
"plugin_admin": {
|
56 |
"css": [
|
57 |
-
"bootstrap4.min",
|
58 |
"bootstrap-select.min",
|
59 |
-
"global-plugin",
|
60 |
"plugin",
|
61 |
"featherlight"
|
62 |
],
|
63 |
"js": [
|
64 |
-
"bootstrap4.bundle.min",
|
65 |
"bootstrap-select.min",
|
66 |
-
"jquery",
|
67 |
-
"global-plugin",
|
68 |
"plugin",
|
69 |
"base64.min",
|
70 |
"lz-string.min",
|
@@ -73,7 +63,141 @@
|
|
73 |
]
|
74 |
},
|
75 |
"frontend": {
|
76 |
-
"css":
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
77 |
}
|
78 |
},
|
79 |
"menu": {
|
@@ -81,7 +205,6 @@
|
|
81 |
"title": "Shield Security",
|
82 |
"top_level": true,
|
83 |
"do_submenu_fix": true,
|
84 |
-
"callback": "onDisplayTopMenu",
|
85 |
"icon_image": "pluginlogo_16x16.png",
|
86 |
"has_submenu": true
|
87 |
},
|
@@ -109,13 +232,11 @@
|
|
109 |
}
|
110 |
],
|
111 |
"version_upgrades": [
|
112 |
-
"9.0.0",
|
113 |
-
"9.0.3",
|
114 |
-
"9.0.5",
|
115 |
"9.1.1",
|
116 |
"9.2.0",
|
117 |
"9.2.2",
|
118 |
-
"10.1.0"
|
|
|
119 |
],
|
120 |
"action_links": {
|
121 |
"remove": null,
|
1 |
{
|
2 |
"properties": {
|
3 |
+
"version": "10.2.0",
|
4 |
+
"release_timestamp": 1613037000,
|
5 |
+
"build": "202102.1107",
|
6 |
"slug_parent": "icwp",
|
7 |
"slug_plugin": "wpsf",
|
8 |
"human_name": "Shield Security",
|
23 |
"wordpress": "3.5.2"
|
24 |
},
|
25 |
"upgrade_reqs": {
|
|
|
|
|
|
|
|
|
26 |
"10.0": {
|
27 |
"php": "7.0",
|
28 |
"wp": "3.5.2"
|
44 |
"global-plugin"
|
45 |
],
|
46 |
"js": [
|
|
|
47 |
"global-plugin"
|
48 |
]
|
49 |
},
|
50 |
"plugin_admin": {
|
51 |
"css": [
|
|
|
52 |
"bootstrap-select.min",
|
|
|
53 |
"plugin",
|
54 |
"featherlight"
|
55 |
],
|
56 |
"js": [
|
|
|
57 |
"bootstrap-select.min",
|
|
|
|
|
58 |
"plugin",
|
59 |
"base64.min",
|
60 |
"lz-string.min",
|
63 |
]
|
64 |
},
|
65 |
"frontend": {
|
66 |
+
"css": [],
|
67 |
+
"js": []
|
68 |
+
},
|
69 |
+
"register": {
|
70 |
+
"css": {
|
71 |
+
"bootstrap4.min": {
|
72 |
+
"url": "https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.6.0/css/bootstrap.min.css"
|
73 |
+
},
|
74 |
+
"bootstrap-select.min": {
|
75 |
+
"deps": [
|
76 |
+
"bootstrap4.min"
|
77 |
+
]
|
78 |
+
},
|
79 |
+
"bootstrap-datepicker": {
|
80 |
+
"url": "https://cdnjs.cloudflare.com/ajax/libs/bootstrap-datepicker/1.8.0/css/bootstrap-datepicker.min.css",
|
81 |
+
"deps": [
|
82 |
+
"bootstrap4.min"
|
83 |
+
]
|
84 |
+
},
|
85 |
+
"global-plugin": {},
|
86 |
+
"plugin": {
|
87 |
+
"deps": [
|
88 |
+
"bootstrap4.min",
|
89 |
+
"global-plugin"
|
90 |
+
]
|
91 |
+
},
|
92 |
+
"wizard": {
|
93 |
+
"deps": [
|
94 |
+
"bootstrap4.min",
|
95 |
+
"global-plugin"
|
96 |
+
]
|
97 |
+
},
|
98 |
+
"featherlight": {},
|
99 |
+
"chartist.min": {},
|
100 |
+
"chartist-plugin-legend": {
|
101 |
+
"deps": [
|
102 |
+
"chartist.min"
|
103 |
+
]
|
104 |
+
},
|
105 |
+
"introjs.min": {}
|
106 |
+
},
|
107 |
+
"js": {
|
108 |
+
"bootstrap4.bundle.min": {
|
109 |
+
"url": "https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.6.0/js/bootstrap.bundle.min.js",
|
110 |
+
"deps": [
|
111 |
+
"wp-jquery"
|
112 |
+
]
|
113 |
+
},
|
114 |
+
"bootstrap-select.min": {
|
115 |
+
"deps": [
|
116 |
+
"bootstrap4.bundle.min"
|
117 |
+
]
|
118 |
+
},
|
119 |
+
"bootstrap-datepicker": {
|
120 |
+
"url": "https://cdnjs.cloudflare.com/ajax/libs/bootstrap-datepicker/1.8.0/js/bootstrap-datepicker.min.js",
|
121 |
+
"deps": [
|
122 |
+
"bootstrap4.bundle.min"
|
123 |
+
]
|
124 |
+
},
|
125 |
+
"global-plugin": {
|
126 |
+
"deps": [
|
127 |
+
"wp-jquery"
|
128 |
+
]
|
129 |
+
},
|
130 |
+
"plugin": {
|
131 |
+
"deps": [
|
132 |
+
"bootstrap4.bundle.min",
|
133 |
+
"global-plugin"
|
134 |
+
]
|
135 |
+
},
|
136 |
+
"base64.min": {},
|
137 |
+
"lz-string.min": {},
|
138 |
+
"jquery.fileDownload": {},
|
139 |
+
"wizard": {},
|
140 |
+
"featherlight": {
|
141 |
+
"deps": [
|
142 |
+
"wp-jquery"
|
143 |
+
]
|
144 |
+
},
|
145 |
+
"chartist.min": {},
|
146 |
+
"chartist-plugin-legend": {
|
147 |
+
"deps": [
|
148 |
+
"chartist.min"
|
149 |
+
]
|
150 |
+
},
|
151 |
+
"charts": {
|
152 |
+
"deps": [
|
153 |
+
"chartist-plugin-legend"
|
154 |
+
]
|
155 |
+
},
|
156 |
+
"shuffle": {},
|
157 |
+
"shield-card-shuffle": {
|
158 |
+
"deps": [
|
159 |
+
"shuffle"
|
160 |
+
]
|
161 |
+
},
|
162 |
+
"introjs.min": {},
|
163 |
+
"shield/tables": {
|
164 |
+
"deps": [
|
165 |
+
"plugin"
|
166 |
+
]
|
167 |
+
},
|
168 |
+
"shield/scans": {
|
169 |
+
"deps": [
|
170 |
+
"shield/tables"
|
171 |
+
]
|
172 |
+
},
|
173 |
+
"shield/import": {
|
174 |
+
"deps": [
|
175 |
+
"plugin"
|
176 |
+
]
|
177 |
+
},
|
178 |
+
"shield/ipanalyse": {
|
179 |
+
"deps": [
|
180 |
+
"plugin"
|
181 |
+
]
|
182 |
+
},
|
183 |
+
"shield/mainwp-extension": {
|
184 |
+
"deps": [
|
185 |
+
"jquery"
|
186 |
+
]
|
187 |
+
},
|
188 |
+
"shield/userprofile": {
|
189 |
+
"deps": [
|
190 |
+
"global-plugin"
|
191 |
+
]
|
192 |
+
},
|
193 |
+
"u2f-bundle": {},
|
194 |
+
"shield/u2f-admin": {
|
195 |
+
"deps": [
|
196 |
+
"u2f-bundle",
|
197 |
+
"wp-jquery"
|
198 |
+
]
|
199 |
+
}
|
200 |
+
}
|
201 |
}
|
202 |
},
|
203 |
"menu": {
|
205 |
"title": "Shield Security",
|
206 |
"top_level": true,
|
207 |
"do_submenu_fix": true,
|
|
|
208 |
"icon_image": "pluginlogo_16x16.png",
|
209 |
"has_submenu": true
|
210 |
},
|
232 |
}
|
233 |
],
|
234 |
"version_upgrades": [
|
|
|
|
|
|
|
235 |
"9.1.1",
|
236 |
"9.2.0",
|
237 |
"9.2.2",
|
238 |
+
"10.1.0",
|
239 |
+
"10.2.0"
|
240 |
],
|
241 |
"action_links": {
|
242 |
"remove": null,
|
readme.txt
CHANGED
@@ -8,7 +8,7 @@ Requires at least: 3.5.2
|
|
8 |
Requires PHP: 7.0
|
9 |
Recommended PHP: 7.4
|
10 |
Tested up to: 5.6
|
11 |
-
Stable tag: 10.
|
12 |
|
13 |
The highest rated WordPress Security plugin, delivering unparalleled, all-in-one protection for you and your customers.
|
14 |
|
8 |
Requires PHP: 7.0
|
9 |
Recommended PHP: 7.4
|
10 |
Tested up to: 5.6
|
11 |
+
Stable tag: 10.2.0
|
12 |
|
13 |
The highest rated WordPress Security plugin, delivering unparalleled, all-in-one protection for you and your customers.
|
14 |
|
resources/css/bootstrap4.min.css
DELETED
@@ -1,7 +0,0 @@
|
|
1 |
-
/*!
|
2 |
-
* Bootstrap v4.5.3 (https://getbootstrap.com/)
|
3 |
-
* Copyright 2011-2020 The Bootstrap Authors
|
4 |
-
* Copyright 2011-2020 Twitter, Inc.
|
5 |
-
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
|
6 |
-
*/:root{--blue:#007bff;--indigo:#6610f2;--purple:#6f42c1;--pink:#e83e8c;--red:#dc3545;--orange:#fd7e14;--yellow:#ffc107;--green:#28a745;--teal:#20c997;--cyan:#17a2b8;--white:#fff;--gray:#6c757d;--gray-dark:#343a40;--primary:#007bff;--secondary:#6c757d;--success:#28a745;--info:#17a2b8;--warning:#ffc107;--danger:#dc3545;--light:#f8f9fa;--dark:#343a40;--breakpoint-xs:0;--breakpoint-sm:576px;--breakpoint-md:768px;--breakpoint-lg:992px;--breakpoint-xl:1200px;--font-family-sans-serif:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";--font-family-monospace:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace}*,::after,::before{box-sizing:border-box}html{font-family:sans-serif;line-height:1.15;-webkit-text-size-adjust:100%;-webkit-tap-highlight-color:transparent}article,aside,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}body{margin:0;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";font-size:1rem;font-weight:400;line-height:1.5;color:#212529;text-align:left;background-color:#fff}[tabindex="-1"]:focus:not(:focus-visible){outline:0!important}hr{box-sizing:content-box;height:0;overflow:visible}h1,h2,h3,h4,h5,h6{margin-top:0;margin-bottom:.5rem}p{margin-top:0;margin-bottom:1rem}abbr[data-original-title],abbr[title]{text-decoration:underline;-webkit-text-decoration:underline dotted;text-decoration:underline dotted;cursor:help;border-bottom:0;-webkit-text-decoration-skip-ink:none;text-decoration-skip-ink:none}address{margin-bottom:1rem;font-style:normal;line-height:inherit}dl,ol,ul{margin-top:0;margin-bottom:1rem}ol ol,ol ul,ul ol,ul ul{margin-bottom:0}dt{font-weight:700}dd{margin-bottom:.5rem;margin-left:0}blockquote{margin:0 0 1rem}b,strong{font-weight:bolder}small{font-size:80%}sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}a{color:#007bff;text-decoration:none;background-color:transparent}a:hover{color:#0056b3;text-decoration:underline}a:not([href]):not([class]){color:inherit;text-decoration:none}a:not([href]):not([class]):hover{color:inherit;text-decoration:none}code,kbd,pre,samp{font-family:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;font-size:1em}pre{margin-top:0;margin-bottom:1rem;overflow:auto;-ms-overflow-style:scrollbar}figure{margin:0 0 1rem}img{vertical-align:middle;border-style:none}svg{overflow:hidden;vertical-align:middle}table{border-collapse:collapse}caption{padding-top:.75rem;padding-bottom:.75rem;color:#6c757d;text-align:left;caption-side:bottom}th{text-align:inherit;text-align:-webkit-match-parent}label{display:inline-block;margin-bottom:.5rem}button{border-radius:0}button:focus{outline:1px dotted;outline:5px auto -webkit-focus-ring-color}button,input,optgroup,select,textarea{margin:0;font-family:inherit;font-size:inherit;line-height:inherit}button,input{overflow:visible}button,select{text-transform:none}[role=button]{cursor:pointer}select{word-wrap:normal}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button}[type=button]:not(:disabled),[type=reset]:not(:disabled),[type=submit]:not(:disabled),button:not(:disabled){cursor:pointer}[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner,button::-moz-focus-inner{padding:0;border-style:none}input[type=checkbox],input[type=radio]{box-sizing:border-box;padding:0}textarea{overflow:auto;resize:vertical}fieldset{min-width:0;padding:0;margin:0;border:0}legend{display:block;width:100%;max-width:100%;padding:0;margin-bottom:.5rem;font-size:1.5rem;line-height:inherit;color:inherit;white-space:normal}progress{vertical-align:baseline}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{outline-offset:-2px;-webkit-appearance:none}[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{font:inherit;-webkit-appearance:button}output{display:inline-block}summary{display:list-item;cursor:pointer}template{display:none}[hidden]{display:none!important}.h1,.h2,.h3,.h4,.h5,.h6,h1,h2,h3,h4,h5,h6{margin-bottom:.5rem;font-weight:500;line-height:1.2}.h1,h1{font-size:2.5rem}.h2,h2{font-size:2rem}.h3,h3{font-size:1.75rem}.h4,h4{font-size:1.5rem}.h5,h5{font-size:1.25rem}.h6,h6{font-size:1rem}.lead{font-size:1.25rem;font-weight:300}.display-1{font-size:6rem;font-weight:300;line-height:1.2}.display-2{font-size:5.5rem;font-weight:300;line-height:1.2}.display-3{font-size:4.5rem;font-weight:300;line-height:1.2}.display-4{font-size:3.5rem;font-weight:300;line-height:1.2}hr{margin-top:1rem;margin-bottom:1rem;border:0;border-top:1px solid rgba(0,0,0,.1)}.small,small{font-size:80%;font-weight:400}.mark,mark{padding:.2em;background-color:#fcf8e3}.list-unstyled{padding-left:0;list-style:none}.list-inline{padding-left:0;list-style:none}.list-inline-item{display:inline-block}.list-inline-item:not(:last-child){margin-right:.5rem}.initialism{font-size:90%;text-transform:uppercase}.blockquote{margin-bottom:1rem;font-size:1.25rem}.blockquote-footer{display:block;font-size:80%;color:#6c757d}.blockquote-footer::before{content:"\2014\00A0"}.img-fluid{max-width:100%;height:auto}.img-thumbnail{padding:.25rem;background-color:#fff;border:1px solid #dee2e6;border-radius:.25rem;max-width:100%;height:auto}.figure{display:inline-block}.figure-img{margin-bottom:.5rem;line-height:1}.figure-caption{font-size:90%;color:#6c757d}code{font-size:87.5%;color:#e83e8c;word-wrap:break-word}a>code{color:inherit}kbd{padding:.2rem .4rem;font-size:87.5%;color:#fff;background-color:#212529;border-radius:.2rem}kbd kbd{padding:0;font-size:100%;font-weight:700}pre{display:block;font-size:87.5%;color:#212529}pre code{font-size:inherit;color:inherit;word-break:normal}.pre-scrollable{max-height:340px;overflow-y:scroll}.container,.container-fluid,.container-lg,.container-md,.container-sm,.container-xl{width:100%;padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}@media (min-width:576px){.container,.container-sm{max-width:540px}}@media (min-width:768px){.container,.container-md,.container-sm{max-width:720px}}@media (min-width:992px){.container,.container-lg,.container-md,.container-sm{max-width:960px}}@media (min-width:1200px){.container,.container-lg,.container-md,.container-sm,.container-xl{max-width:1140px}}.row{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;margin-right:-15px;margin-left:-15px}.no-gutters{margin-right:0;margin-left:0}.no-gutters>.col,.no-gutters>[class*=col-]{padding-right:0;padding-left:0}.col,.col-1,.col-10,.col-11,.col-12,.col-2,.col-3,.col-4,.col-5,.col-6,.col-7,.col-8,.col-9,.col-auto,.col-lg,.col-lg-1,.col-lg-10,.col-lg-11,.col-lg-12,.col-lg-2,.col-lg-3,.col-lg-4,.col-lg-5,.col-lg-6,.col-lg-7,.col-lg-8,.col-lg-9,.col-lg-auto,.col-md,.col-md-1,.col-md-10,.col-md-11,.col-md-12,.col-md-2,.col-md-3,.col-md-4,.col-md-5,.col-md-6,.col-md-7,.col-md-8,.col-md-9,.col-md-auto,.col-sm,.col-sm-1,.col-sm-10,.col-sm-11,.col-sm-12,.col-sm-2,.col-sm-3,.col-sm-4,.col-sm-5,.col-sm-6,.col-sm-7,.col-sm-8,.col-sm-9,.col-sm-auto,.col-xl,.col-xl-1,.col-xl-10,.col-xl-11,.col-xl-12,.col-xl-2,.col-xl-3,.col-xl-4,.col-xl-5,.col-xl-6,.col-xl-7,.col-xl-8,.col-xl-9,.col-xl-auto{position:relative;width:100%;padding-right:15px;padding-left:15px}.col{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;max-width:100%}.row-cols-1>*{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.row-cols-2>*{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.row-cols-3>*{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.row-cols-4>*{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.row-cols-5>*{-ms-flex:0 0 20%;flex:0 0 20%;max-width:20%}.row-cols-6>*{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-auto{-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:100%}.col-1{-ms-flex:0 0 8.333333%;flex:0 0 8.333333%;max-width:8.333333%}.col-2{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-3{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-4{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.col-5{-ms-flex:0 0 41.666667%;flex:0 0 41.666667%;max-width:41.666667%}.col-6{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-7{-ms-flex:0 0 58.333333%;flex:0 0 58.333333%;max-width:58.333333%}.col-8{-ms-flex:0 0 66.666667%;flex:0 0 66.666667%;max-width:66.666667%}.col-9{-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-10{-ms-flex:0 0 83.333333%;flex:0 0 83.333333%;max-width:83.333333%}.col-11{-ms-flex:0 0 91.666667%;flex:0 0 91.666667%;max-width:91.666667%}.col-12{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-first{-ms-flex-order:-1;order:-1}.order-last{-ms-flex-order:13;order:13}.order-0{-ms-flex-order:0;order:0}.order-1{-ms-flex-order:1;order:1}.order-2{-ms-flex-order:2;order:2}.order-3{-ms-flex-order:3;order:3}.order-4{-ms-flex-order:4;order:4}.order-5{-ms-flex-order:5;order:5}.order-6{-ms-flex-order:6;order:6}.order-7{-ms-flex-order:7;order:7}.order-8{-ms-flex-order:8;order:8}.order-9{-ms-flex-order:9;order:9}.order-10{-ms-flex-order:10;order:10}.order-11{-ms-flex-order:11;order:11}.order-12{-ms-flex-order:12;order:12}.offset-1{margin-left:8.333333%}.offset-2{margin-left:16.666667%}.offset-3{margin-left:25%}.offset-4{margin-left:33.333333%}.offset-5{margin-left:41.666667%}.offset-6{margin-left:50%}.offset-7{margin-left:58.333333%}.offset-8{margin-left:66.666667%}.offset-9{margin-left:75%}.offset-10{margin-left:83.333333%}.offset-11{margin-left:91.666667%}@media (min-width:576px){.col-sm{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;max-width:100%}.row-cols-sm-1>*{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.row-cols-sm-2>*{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.row-cols-sm-3>*{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.row-cols-sm-4>*{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.row-cols-sm-5>*{-ms-flex:0 0 20%;flex:0 0 20%;max-width:20%}.row-cols-sm-6>*{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-sm-auto{-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:100%}.col-sm-1{-ms-flex:0 0 8.333333%;flex:0 0 8.333333%;max-width:8.333333%}.col-sm-2{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-sm-3{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-sm-4{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.col-sm-5{-ms-flex:0 0 41.666667%;flex:0 0 41.666667%;max-width:41.666667%}.col-sm-6{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-sm-7{-ms-flex:0 0 58.333333%;flex:0 0 58.333333%;max-width:58.333333%}.col-sm-8{-ms-flex:0 0 66.666667%;flex:0 0 66.666667%;max-width:66.666667%}.col-sm-9{-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-sm-10{-ms-flex:0 0 83.333333%;flex:0 0 83.333333%;max-width:83.333333%}.col-sm-11{-ms-flex:0 0 91.666667%;flex:0 0 91.666667%;max-width:91.666667%}.col-sm-12{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-sm-first{-ms-flex-order:-1;order:-1}.order-sm-last{-ms-flex-order:13;order:13}.order-sm-0{-ms-flex-order:0;order:0}.order-sm-1{-ms-flex-order:1;order:1}.order-sm-2{-ms-flex-order:2;order:2}.order-sm-3{-ms-flex-order:3;order:3}.order-sm-4{-ms-flex-order:4;order:4}.order-sm-5{-ms-flex-order:5;order:5}.order-sm-6{-ms-flex-order:6;order:6}.order-sm-7{-ms-flex-order:7;order:7}.order-sm-8{-ms-flex-order:8;order:8}.order-sm-9{-ms-flex-order:9;order:9}.order-sm-10{-ms-flex-order:10;order:10}.order-sm-11{-ms-flex-order:11;order:11}.order-sm-12{-ms-flex-order:12;order:12}.offset-sm-0{margin-left:0}.offset-sm-1{margin-left:8.333333%}.offset-sm-2{margin-left:16.666667%}.offset-sm-3{margin-left:25%}.offset-sm-4{margin-left:33.333333%}.offset-sm-5{margin-left:41.666667%}.offset-sm-6{margin-left:50%}.offset-sm-7{margin-left:58.333333%}.offset-sm-8{margin-left:66.666667%}.offset-sm-9{margin-left:75%}.offset-sm-10{margin-left:83.333333%}.offset-sm-11{margin-left:91.666667%}}@media (min-width:768px){.col-md{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;max-width:100%}.row-cols-md-1>*{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.row-cols-md-2>*{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.row-cols-md-3>*{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.row-cols-md-4>*{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.row-cols-md-5>*{-ms-flex:0 0 20%;flex:0 0 20%;max-width:20%}.row-cols-md-6>*{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-md-auto{-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:100%}.col-md-1{-ms-flex:0 0 8.333333%;flex:0 0 8.333333%;max-width:8.333333%}.col-md-2{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-md-3{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-md-4{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.col-md-5{-ms-flex:0 0 41.666667%;flex:0 0 41.666667%;max-width:41.666667%}.col-md-6{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-md-7{-ms-flex:0 0 58.333333%;flex:0 0 58.333333%;max-width:58.333333%}.col-md-8{-ms-flex:0 0 66.666667%;flex:0 0 66.666667%;max-width:66.666667%}.col-md-9{-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-md-10{-ms-flex:0 0 83.333333%;flex:0 0 83.333333%;max-width:83.333333%}.col-md-11{-ms-flex:0 0 91.666667%;flex:0 0 91.666667%;max-width:91.666667%}.col-md-12{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-md-first{-ms-flex-order:-1;order:-1}.order-md-last{-ms-flex-order:13;order:13}.order-md-0{-ms-flex-order:0;order:0}.order-md-1{-ms-flex-order:1;order:1}.order-md-2{-ms-flex-order:2;order:2}.order-md-3{-ms-flex-order:3;order:3}.order-md-4{-ms-flex-order:4;order:4}.order-md-5{-ms-flex-order:5;order:5}.order-md-6{-ms-flex-order:6;order:6}.order-md-7{-ms-flex-order:7;order:7}.order-md-8{-ms-flex-order:8;order:8}.order-md-9{-ms-flex-order:9;order:9}.order-md-10{-ms-flex-order:10;order:10}.order-md-11{-ms-flex-order:11;order:11}.order-md-12{-ms-flex-order:12;order:12}.offset-md-0{margin-left:0}.offset-md-1{margin-left:8.333333%}.offset-md-2{margin-left:16.666667%}.offset-md-3{margin-left:25%}.offset-md-4{margin-left:33.333333%}.offset-md-5{margin-left:41.666667%}.offset-md-6{margin-left:50%}.offset-md-7{margin-left:58.333333%}.offset-md-8{margin-left:66.666667%}.offset-md-9{margin-left:75%}.offset-md-10{margin-left:83.333333%}.offset-md-11{margin-left:91.666667%}}@media (min-width:992px){.col-lg{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;max-width:100%}.row-cols-lg-1>*{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.row-cols-lg-2>*{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.row-cols-lg-3>*{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.row-cols-lg-4>*{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.row-cols-lg-5>*{-ms-flex:0 0 20%;flex:0 0 20%;max-width:20%}.row-cols-lg-6>*{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-lg-auto{-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:100%}.col-lg-1{-ms-flex:0 0 8.333333%;flex:0 0 8.333333%;max-width:8.333333%}.col-lg-2{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-lg-3{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-lg-4{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.col-lg-5{-ms-flex:0 0 41.666667%;flex:0 0 41.666667%;max-width:41.666667%}.col-lg-6{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-lg-7{-ms-flex:0 0 58.333333%;flex:0 0 58.333333%;max-width:58.333333%}.col-lg-8{-ms-flex:0 0 66.666667%;flex:0 0 66.666667%;max-width:66.666667%}.col-lg-9{-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-lg-10{-ms-flex:0 0 83.333333%;flex:0 0 83.333333%;max-width:83.333333%}.col-lg-11{-ms-flex:0 0 91.666667%;flex:0 0 91.666667%;max-width:91.666667%}.col-lg-12{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-lg-first{-ms-flex-order:-1;order:-1}.order-lg-last{-ms-flex-order:13;order:13}.order-lg-0{-ms-flex-order:0;order:0}.order-lg-1{-ms-flex-order:1;order:1}.order-lg-2{-ms-flex-order:2;order:2}.order-lg-3{-ms-flex-order:3;order:3}.order-lg-4{-ms-flex-order:4;order:4}.order-lg-5{-ms-flex-order:5;order:5}.order-lg-6{-ms-flex-order:6;order:6}.order-lg-7{-ms-flex-order:7;order:7}.order-lg-8{-ms-flex-order:8;order:8}.order-lg-9{-ms-flex-order:9;order:9}.order-lg-10{-ms-flex-order:10;order:10}.order-lg-11{-ms-flex-order:11;order:11}.order-lg-12{-ms-flex-order:12;order:12}.offset-lg-0{margin-left:0}.offset-lg-1{margin-left:8.333333%}.offset-lg-2{margin-left:16.666667%}.offset-lg-3{margin-left:25%}.offset-lg-4{margin-left:33.333333%}.offset-lg-5{margin-left:41.666667%}.offset-lg-6{margin-left:50%}.offset-lg-7{margin-left:58.333333%}.offset-lg-8{margin-left:66.666667%}.offset-lg-9{margin-left:75%}.offset-lg-10{margin-left:83.333333%}.offset-lg-11{margin-left:91.666667%}}@media (min-width:1200px){.col-xl{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;max-width:100%}.row-cols-xl-1>*{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.row-cols-xl-2>*{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.row-cols-xl-3>*{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.row-cols-xl-4>*{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.row-cols-xl-5>*{-ms-flex:0 0 20%;flex:0 0 20%;max-width:20%}.row-cols-xl-6>*{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-xl-auto{-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:100%}.col-xl-1{-ms-flex:0 0 8.333333%;flex:0 0 8.333333%;max-width:8.333333%}.col-xl-2{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-xl-3{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-xl-4{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.col-xl-5{-ms-flex:0 0 41.666667%;flex:0 0 41.666667%;max-width:41.666667%}.col-xl-6{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-xl-7{-ms-flex:0 0 58.333333%;flex:0 0 58.333333%;max-width:58.333333%}.col-xl-8{-ms-flex:0 0 66.666667%;flex:0 0 66.666667%;max-width:66.666667%}.col-xl-9{-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-xl-10{-ms-flex:0 0 83.333333%;flex:0 0 83.333333%;max-width:83.333333%}.col-xl-11{-ms-flex:0 0 91.666667%;flex:0 0 91.666667%;max-width:91.666667%}.col-xl-12{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-xl-first{-ms-flex-order:-1;order:-1}.order-xl-last{-ms-flex-order:13;order:13}.order-xl-0{-ms-flex-order:0;order:0}.order-xl-1{-ms-flex-order:1;order:1}.order-xl-2{-ms-flex-order:2;order:2}.order-xl-3{-ms-flex-order:3;order:3}.order-xl-4{-ms-flex-order:4;order:4}.order-xl-5{-ms-flex-order:5;order:5}.order-xl-6{-ms-flex-order:6;order:6}.order-xl-7{-ms-flex-order:7;order:7}.order-xl-8{-ms-flex-order:8;order:8}.order-xl-9{-ms-flex-order:9;order:9}.order-xl-10{-ms-flex-order:10;order:10}.order-xl-11{-ms-flex-order:11;order:11}.order-xl-12{-ms-flex-order:12;order:12}.offset-xl-0{margin-left:0}.offset-xl-1{margin-left:8.333333%}.offset-xl-2{margin-left:16.666667%}.offset-xl-3{margin-left:25%}.offset-xl-4{margin-left:33.333333%}.offset-xl-5{margin-left:41.666667%}.offset-xl-6{margin-left:50%}.offset-xl-7{margin-left:58.333333%}.offset-xl-8{margin-left:66.666667%}.offset-xl-9{margin-left:75%}.offset-xl-10{margin-left:83.333333%}.offset-xl-11{margin-left:91.666667%}}.table{width:100%;margin-bottom:1rem;color:#212529}.table td,.table th{padding:.75rem;vertical-align:top;border-top:1px solid #dee2e6}.table thead th{vertical-align:bottom;border-bottom:2px solid #dee2e6}.table tbody+tbody{border-top:2px solid #dee2e6}.table-sm td,.table-sm th{padding:.3rem}.table-bordered{border:1px solid #dee2e6}.table-bordered td,.table-bordered th{border:1px solid #dee2e6}.table-bordered thead td,.table-bordered thead th{border-bottom-width:2px}.table-borderless tbody+tbody,.table-borderless td,.table-borderless th,.table-borderless thead th{border:0}.table-striped tbody tr:nth-of-type(odd){background-color:rgba(0,0,0,.05)}.table-hover tbody tr:hover{color:#212529;background-color:rgba(0,0,0,.075)}.table-primary,.table-primary>td,.table-primary>th{background-color:#b8daff}.table-primary tbody+tbody,.table-primary td,.table-primary th,.table-primary thead th{border-color:#7abaff}.table-hover .table-primary:hover{background-color:#9fcdff}.table-hover .table-primary:hover>td,.table-hover .table-primary:hover>th{background-color:#9fcdff}.table-secondary,.table-secondary>td,.table-secondary>th{background-color:#d6d8db}.table-secondary tbody+tbody,.table-secondary td,.table-secondary th,.table-secondary thead th{border-color:#b3b7bb}.table-hover .table-secondary:hover{background-color:#c8cbcf}.table-hover .table-secondary:hover>td,.table-hover .table-secondary:hover>th{background-color:#c8cbcf}.table-success,.table-success>td,.table-success>th{background-color:#c3e6cb}.table-success tbody+tbody,.table-success td,.table-success th,.table-success thead th{border-color:#8fd19e}.table-hover .table-success:hover{background-color:#b1dfbb}.table-hover .table-success:hover>td,.table-hover .table-success:hover>th{background-color:#b1dfbb}.table-info,.table-info>td,.table-info>th{background-color:#bee5eb}.table-info tbody+tbody,.table-info td,.table-info th,.table-info thead th{border-color:#86cfda}.table-hover .table-info:hover{background-color:#abdde5}.table-hover .table-info:hover>td,.table-hover .table-info:hover>th{background-color:#abdde5}.table-warning,.table-warning>td,.table-warning>th{background-color:#ffeeba}.table-warning tbody+tbody,.table-warning td,.table-warning th,.table-warning thead th{border-color:#ffdf7e}.table-hover .table-warning:hover{background-color:#ffe8a1}.table-hover .table-warning:hover>td,.table-hover .table-warning:hover>th{background-color:#ffe8a1}.table-danger,.table-danger>td,.table-danger>th{background-color:#f5c6cb}.table-danger tbody+tbody,.table-danger td,.table-danger th,.table-danger thead th{border-color:#ed969e}.table-hover .table-danger:hover{background-color:#f1b0b7}.table-hover .table-danger:hover>td,.table-hover .table-danger:hover>th{background-color:#f1b0b7}.table-light,.table-light>td,.table-light>th{background-color:#fdfdfe}.table-light tbody+tbody,.table-light td,.table-light th,.table-light thead th{border-color:#fbfcfc}.table-hover .table-light:hover{background-color:#ececf6}.table-hover .table-light:hover>td,.table-hover .table-light:hover>th{background-color:#ececf6}.table-dark,.table-dark>td,.table-dark>th{background-color:#c6c8ca}.table-dark tbody+tbody,.table-dark td,.table-dark th,.table-dark thead th{border-color:#95999c}.table-hover .table-dark:hover{background-color:#b9bbbe}.table-hover .table-dark:hover>td,.table-hover .table-dark:hover>th{background-color:#b9bbbe}.table-active,.table-active>td,.table-active>th{background-color:rgba(0,0,0,.075)}.table-hover .table-active:hover{background-color:rgba(0,0,0,.075)}.table-hover .table-active:hover>td,.table-hover .table-active:hover>th{background-color:rgba(0,0,0,.075)}.table .thead-dark th{color:#fff;background-color:#343a40;border-color:#454d55}.table .thead-light th{color:#495057;background-color:#e9ecef;border-color:#dee2e6}.table-dark{color:#fff;background-color:#343a40}.table-dark td,.table-dark th,.table-dark thead th{border-color:#454d55}.table-dark.table-bordered{border:0}.table-dark.table-striped tbody tr:nth-of-type(odd){background-color:rgba(255,255,255,.05)}.table-dark.table-hover tbody tr:hover{color:#fff;background-color:rgba(255,255,255,.075)}@media (max-width:575.98px){.table-responsive-sm{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch}.table-responsive-sm>.table-bordered{border:0}}@media (max-width:767.98px){.table-responsive-md{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch}.table-responsive-md>.table-bordered{border:0}}@media (max-width:991.98px){.table-responsive-lg{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch}.table-responsive-lg>.table-bordered{border:0}}@media (max-width:1199.98px){.table-responsive-xl{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch}.table-responsive-xl>.table-bordered{border:0}}.table-responsive{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch}.table-responsive>.table-bordered{border:0}.form-control{display:block;width:100%;height:calc(1.5em + .75rem + 2px);padding:.375rem .75rem;font-size:1rem;font-weight:400;line-height:1.5;color:#495057;background-color:#fff;background-clip:padding-box;border:1px solid #ced4da;border-radius:.25rem;transition:border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.form-control{transition:none}}.form-control::-ms-expand{background-color:transparent;border:0}.form-control:-moz-focusring{color:transparent;text-shadow:0 0 0 #495057}.form-control:focus{color:#495057;background-color:#fff;border-color:#80bdff;outline:0;box-shadow:0 0 0 .2rem rgba(0,123,255,.25)}.form-control::-webkit-input-placeholder{color:#6c757d;opacity:1}.form-control::-moz-placeholder{color:#6c757d;opacity:1}.form-control:-ms-input-placeholder{color:#6c757d;opacity:1}.form-control::-ms-input-placeholder{color:#6c757d;opacity:1}.form-control::placeholder{color:#6c757d;opacity:1}.form-control:disabled,.form-control[readonly]{background-color:#e9ecef;opacity:1}input[type=date].form-control,input[type=datetime-local].form-control,input[type=month].form-control,input[type=time].form-control{-webkit-appearance:none;-moz-appearance:none;appearance:none}select.form-control:focus::-ms-value{color:#495057;background-color:#fff}.form-control-file,.form-control-range{display:block;width:100%}.col-form-label{padding-top:calc(.375rem + 1px);padding-bottom:calc(.375rem + 1px);margin-bottom:0;font-size:inherit;line-height:1.5}.col-form-label-lg{padding-top:calc(.5rem + 1px);padding-bottom:calc(.5rem + 1px);font-size:1.25rem;line-height:1.5}.col-form-label-sm{padding-top:calc(.25rem + 1px);padding-bottom:calc(.25rem + 1px);font-size:.875rem;line-height:1.5}.form-control-plaintext{display:block;width:100%;padding:.375rem 0;margin-bottom:0;font-size:1rem;line-height:1.5;color:#212529;background-color:transparent;border:solid transparent;border-width:1px 0}.form-control-plaintext.form-control-lg,.form-control-plaintext.form-control-sm{padding-right:0;padding-left:0}.form-control-sm{height:calc(1.5em + .5rem + 2px);padding:.25rem .5rem;font-size:.875rem;line-height:1.5;border-radius:.2rem}.form-control-lg{height:calc(1.5em + 1rem + 2px);padding:.5rem 1rem;font-size:1.25rem;line-height:1.5;border-radius:.3rem}select.form-control[multiple],select.form-control[size]{height:auto}textarea.form-control{height:auto}.form-group{margin-bottom:1rem}.form-text{display:block;margin-top:.25rem}.form-row{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;margin-right:-5px;margin-left:-5px}.form-row>.col,.form-row>[class*=col-]{padding-right:5px;padding-left:5px}.form-check{position:relative;display:block;padding-left:1.25rem}.form-check-input{position:absolute;margin-top:.3rem;margin-left:-1.25rem}.form-check-input:disabled~.form-check-label,.form-check-input[disabled]~.form-check-label{color:#6c757d}.form-check-label{margin-bottom:0}.form-check-inline{display:-ms-inline-flexbox;display:inline-flex;-ms-flex-align:center;align-items:center;padding-left:0;margin-right:.75rem}.form-check-inline .form-check-input{position:static;margin-top:0;margin-right:.3125rem;margin-left:0}.valid-feedback{display:none;width:100%;margin-top:.25rem;font-size:80%;color:#28a745}.valid-tooltip{position:absolute;top:100%;left:0;z-index:5;display:none;max-width:100%;padding:.25rem .5rem;margin-top:.1rem;font-size:.875rem;line-height:1.5;color:#fff;background-color:rgba(40,167,69,.9);border-radius:.25rem}.is-valid~.valid-feedback,.is-valid~.valid-tooltip,.was-validated :valid~.valid-feedback,.was-validated :valid~.valid-tooltip{display:block}.form-control.is-valid,.was-validated .form-control:valid{border-color:#28a745;padding-right:calc(1.5em + .75rem);background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='8' height='8' viewBox='0 0 8 8'%3e%3cpath fill='%2328a745' d='M2.3 6.73L.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3e%3c/svg%3e");background-repeat:no-repeat;background-position:right calc(.375em + .1875rem) center;background-size:calc(.75em + .375rem) calc(.75em + .375rem)}.form-control.is-valid:focus,.was-validated .form-control:valid:focus{border-color:#28a745;box-shadow:0 0 0 .2rem rgba(40,167,69,.25)}.was-validated textarea.form-control:valid,textarea.form-control.is-valid{padding-right:calc(1.5em + .75rem);background-position:top calc(.375em + .1875rem) right calc(.375em + .1875rem)}.custom-select.is-valid,.was-validated .custom-select:valid{border-color:#28a745;padding-right:calc(.75em + 2.3125rem);background:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='4' height='5' viewBox='0 0 4 5'%3e%3cpath fill='%23343a40' d='M2 0L0 2h4zm0 5L0 3h4z'/%3e%3c/svg%3e") no-repeat right .75rem center/8px 10px,url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='8' height='8' viewBox='0 0 8 8'%3e%3cpath fill='%2328a745' d='M2.3 6.73L.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3e%3c/svg%3e") #fff no-repeat center right 1.75rem/calc(.75em + .375rem) calc(.75em + .375rem)}.custom-select.is-valid:focus,.was-validated .custom-select:valid:focus{border-color:#28a745;box-shadow:0 0 0 .2rem rgba(40,167,69,.25)}.form-check-input.is-valid~.form-check-label,.was-validated .form-check-input:valid~.form-check-label{color:#28a745}.form-check-input.is-valid~.valid-feedback,.form-check-input.is-valid~.valid-tooltip,.was-validated .form-check-input:valid~.valid-feedback,.was-validated .form-check-input:valid~.valid-tooltip{display:block}.custom-control-input.is-valid~.custom-control-label,.was-validated .custom-control-input:valid~.custom-control-label{color:#28a745}.custom-control-input.is-valid~.custom-control-label::before,.was-validated .custom-control-input:valid~.custom-control-label::before{border-color:#28a745}.custom-control-input.is-valid:checked~.custom-control-label::before,.was-validated .custom-control-input:valid:checked~.custom-control-label::before{border-color:#34ce57;background-color:#34ce57}.custom-control-input.is-valid:focus~.custom-control-label::before,.was-validated .custom-control-input:valid:focus~.custom-control-label::before{box-shadow:0 0 0 .2rem rgba(40,167,69,.25)}.custom-control-input.is-valid:focus:not(:checked)~.custom-control-label::before,.was-validated .custom-control-input:valid:focus:not(:checked)~.custom-control-label::before{border-color:#28a745}.custom-file-input.is-valid~.custom-file-label,.was-validated .custom-file-input:valid~.custom-file-label{border-color:#28a745}.custom-file-input.is-valid:focus~.custom-file-label,.was-validated .custom-file-input:valid:focus~.custom-file-label{border-color:#28a745;box-shadow:0 0 0 .2rem rgba(40,167,69,.25)}.invalid-feedback{display:none;width:100%;margin-top:.25rem;font-size:80%;color:#dc3545}.invalid-tooltip{position:absolute;top:100%;left:0;z-index:5;display:none;max-width:100%;padding:.25rem .5rem;margin-top:.1rem;font-size:.875rem;line-height:1.5;color:#fff;background-color:rgba(220,53,69,.9);border-radius:.25rem}.is-invalid~.invalid-feedback,.is-invalid~.invalid-tooltip,.was-validated :invalid~.invalid-feedback,.was-validated :invalid~.invalid-tooltip{display:block}.form-control.is-invalid,.was-validated .form-control:invalid{border-color:#dc3545;padding-right:calc(1.5em + .75rem);background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' fill='none' stroke='%23dc3545' viewBox='0 0 12 12'%3e%3ccircle cx='6' cy='6' r='4.5'/%3e%3cpath stroke-linejoin='round' d='M5.8 3.6h.4L6 6.5z'/%3e%3ccircle cx='6' cy='8.2' r='.6' fill='%23dc3545' stroke='none'/%3e%3c/svg%3e");background-repeat:no-repeat;background-position:right calc(.375em + .1875rem) center;background-size:calc(.75em + .375rem) calc(.75em + .375rem)}.form-control.is-invalid:focus,.was-validated .form-control:invalid:focus{border-color:#dc3545;box-shadow:0 0 0 .2rem rgba(220,53,69,.25)}.was-validated textarea.form-control:invalid,textarea.form-control.is-invalid{padding-right:calc(1.5em + .75rem);background-position:top calc(.375em + .1875rem) right calc(.375em + .1875rem)}.custom-select.is-invalid,.was-validated .custom-select:invalid{border-color:#dc3545;padding-right:calc(.75em + 2.3125rem);background:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='4' height='5' viewBox='0 0 4 5'%3e%3cpath fill='%23343a40' d='M2 0L0 2h4zm0 5L0 3h4z'/%3e%3c/svg%3e") no-repeat right .75rem center/8px 10px,url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' fill='none' stroke='%23dc3545' viewBox='0 0 12 12'%3e%3ccircle cx='6' cy='6' r='4.5'/%3e%3cpath stroke-linejoin='round' d='M5.8 3.6h.4L6 6.5z'/%3e%3ccircle cx='6' cy='8.2' r='.6' fill='%23dc3545' stroke='none'/%3e%3c/svg%3e") #fff no-repeat center right 1.75rem/calc(.75em + .375rem) calc(.75em + .375rem)}.custom-select.is-invalid:focus,.was-validated .custom-select:invalid:focus{border-color:#dc3545;box-shadow:0 0 0 .2rem rgba(220,53,69,.25)}.form-check-input.is-invalid~.form-check-label,.was-validated .form-check-input:invalid~.form-check-label{color:#dc3545}.form-check-input.is-invalid~.invalid-feedback,.form-check-input.is-invalid~.invalid-tooltip,.was-validated .form-check-input:invalid~.invalid-feedback,.was-validated .form-check-input:invalid~.invalid-tooltip{display:block}.custom-control-input.is-invalid~.custom-control-label,.was-validated .custom-control-input:invalid~.custom-control-label{color:#dc3545}.custom-control-input.is-invalid~.custom-control-label::before,.was-validated .custom-control-input:invalid~.custom-control-label::before{border-color:#dc3545}.custom-control-input.is-invalid:checked~.custom-control-label::before,.was-validated .custom-control-input:invalid:checked~.custom-control-label::before{border-color:#e4606d;background-color:#e4606d}.custom-control-input.is-invalid:focus~.custom-control-label::before,.was-validated .custom-control-input:invalid:focus~.custom-control-label::before{box-shadow:0 0 0 .2rem rgba(220,53,69,.25)}.custom-control-input.is-invalid:focus:not(:checked)~.custom-control-label::before,.was-validated .custom-control-input:invalid:focus:not(:checked)~.custom-control-label::before{border-color:#dc3545}.custom-file-input.is-invalid~.custom-file-label,.was-validated .custom-file-input:invalid~.custom-file-label{border-color:#dc3545}.custom-file-input.is-invalid:focus~.custom-file-label,.was-validated .custom-file-input:invalid:focus~.custom-file-label{border-color:#dc3545;box-shadow:0 0 0 .2rem rgba(220,53,69,.25)}.form-inline{display:-ms-flexbox;display:flex;-ms-flex-flow:row wrap;flex-flow:row wrap;-ms-flex-align:center;align-items:center}.form-inline .form-check{width:100%}@media (min-width:576px){.form-inline label{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;-ms-flex-pack:center;justify-content:center;margin-bottom:0}.form-inline .form-group{display:-ms-flexbox;display:flex;-ms-flex:0 0 auto;flex:0 0 auto;-ms-flex-flow:row wrap;flex-flow:row wrap;-ms-flex-align:center;align-items:center;margin-bottom:0}.form-inline .form-control{display:inline-block;width:auto;vertical-align:middle}.form-inline .form-control-plaintext{display:inline-block}.form-inline .custom-select,.form-inline .input-group{width:auto}.form-inline .form-check{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;-ms-flex-pack:center;justify-content:center;width:auto;padding-left:0}.form-inline .form-check-input{position:relative;-ms-flex-negative:0;flex-shrink:0;margin-top:0;margin-right:.25rem;margin-left:0}.form-inline .custom-control{-ms-flex-align:center;align-items:center;-ms-flex-pack:center;justify-content:center}.form-inline .custom-control-label{margin-bottom:0}}.btn{display:inline-block;font-weight:400;color:#212529;text-align:center;vertical-align:middle;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;background-color:transparent;border:1px solid transparent;padding:.375rem .75rem;font-size:1rem;line-height:1.5;border-radius:.25rem;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.btn{transition:none}}.btn:hover{color:#212529;text-decoration:none}.btn.focus,.btn:focus{outline:0;box-shadow:0 0 0 .2rem rgba(0,123,255,.25)}.btn.disabled,.btn:disabled{opacity:.65}.btn:not(:disabled):not(.disabled){cursor:pointer}a.btn.disabled,fieldset:disabled a.btn{pointer-events:none}.btn-primary{color:#fff;background-color:#007bff;border-color:#007bff}.btn-primary:hover{color:#fff;background-color:#0069d9;border-color:#0062cc}.btn-primary.focus,.btn-primary:focus{color:#fff;background-color:#0069d9;border-color:#0062cc;box-shadow:0 0 0 .2rem rgba(38,143,255,.5)}.btn-primary.disabled,.btn-primary:disabled{color:#fff;background-color:#007bff;border-color:#007bff}.btn-primary:not(:disabled):not(.disabled).active,.btn-primary:not(:disabled):not(.disabled):active,.show>.btn-primary.dropdown-toggle{color:#fff;background-color:#0062cc;border-color:#005cbf}.btn-primary:not(:disabled):not(.disabled).active:focus,.btn-primary:not(:disabled):not(.disabled):active:focus,.show>.btn-primary.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(38,143,255,.5)}.btn-secondary{color:#fff;background-color:#6c757d;border-color:#6c757d}.btn-secondary:hover{color:#fff;background-color:#5a6268;border-color:#545b62}.btn-secondary.focus,.btn-secondary:focus{color:#fff;background-color:#5a6268;border-color:#545b62;box-shadow:0 0 0 .2rem rgba(130,138,145,.5)}.btn-secondary.disabled,.btn-secondary:disabled{color:#fff;background-color:#6c757d;border-color:#6c757d}.btn-secondary:not(:disabled):not(.disabled).active,.btn-secondary:not(:disabled):not(.disabled):active,.show>.btn-secondary.dropdown-toggle{color:#fff;background-color:#545b62;border-color:#4e555b}.btn-secondary:not(:disabled):not(.disabled).active:focus,.btn-secondary:not(:disabled):not(.disabled):active:focus,.show>.btn-secondary.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(130,138,145,.5)}.btn-success{color:#fff;background-color:#28a745;border-color:#28a745}.btn-success:hover{color:#fff;background-color:#218838;border-color:#1e7e34}.btn-success.focus,.btn-success:focus{color:#fff;background-color:#218838;border-color:#1e7e34;box-shadow:0 0 0 .2rem rgba(72,180,97,.5)}.btn-success.disabled,.btn-success:disabled{color:#fff;background-color:#28a745;border-color:#28a745}.btn-success:not(:disabled):not(.disabled).active,.btn-success:not(:disabled):not(.disabled):active,.show>.btn-success.dropdown-toggle{color:#fff;background-color:#1e7e34;border-color:#1c7430}.btn-success:not(:disabled):not(.disabled).active:focus,.btn-success:not(:disabled):not(.disabled):active:focus,.show>.btn-success.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(72,180,97,.5)}.btn-info{color:#fff;background-color:#17a2b8;border-color:#17a2b8}.btn-info:hover{color:#fff;background-color:#138496;border-color:#117a8b}.btn-info.focus,.btn-info:focus{color:#fff;background-color:#138496;border-color:#117a8b;box-shadow:0 0 0 .2rem rgba(58,176,195,.5)}.btn-info.disabled,.btn-info:disabled{color:#fff;background-color:#17a2b8;border-color:#17a2b8}.btn-info:not(:disabled):not(.disabled).active,.btn-info:not(:disabled):not(.disabled):active,.show>.btn-info.dropdown-toggle{color:#fff;background-color:#117a8b;border-color:#10707f}.btn-info:not(:disabled):not(.disabled).active:focus,.btn-info:not(:disabled):not(.disabled):active:focus,.show>.btn-info.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(58,176,195,.5)}.btn-warning{color:#212529;background-color:#ffc107;border-color:#ffc107}.btn-warning:hover{color:#212529;background-color:#e0a800;border-color:#d39e00}.btn-warning.focus,.btn-warning:focus{color:#212529;background-color:#e0a800;border-color:#d39e00;box-shadow:0 0 0 .2rem rgba(222,170,12,.5)}.btn-warning.disabled,.btn-warning:disabled{color:#212529;background-color:#ffc107;border-color:#ffc107}.btn-warning:not(:disabled):not(.disabled).active,.btn-warning:not(:disabled):not(.disabled):active,.show>.btn-warning.dropdown-toggle{color:#212529;background-color:#d39e00;border-color:#c69500}.btn-warning:not(:disabled):not(.disabled).active:focus,.btn-warning:not(:disabled):not(.disabled):active:focus,.show>.btn-warning.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(222,170,12,.5)}.btn-danger{color:#fff;background-color:#dc3545;border-color:#dc3545}.btn-danger:hover{color:#fff;background-color:#c82333;border-color:#bd2130}.btn-danger.focus,.btn-danger:focus{color:#fff;background-color:#c82333;border-color:#bd2130;box-shadow:0 0 0 .2rem rgba(225,83,97,.5)}.btn-danger.disabled,.btn-danger:disabled{color:#fff;background-color:#dc3545;border-color:#dc3545}.btn-danger:not(:disabled):not(.disabled).active,.btn-danger:not(:disabled):not(.disabled):active,.show>.btn-danger.dropdown-toggle{color:#fff;background-color:#bd2130;border-color:#b21f2d}.btn-danger:not(:disabled):not(.disabled).active:focus,.btn-danger:not(:disabled):not(.disabled):active:focus,.show>.btn-danger.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(225,83,97,.5)}.btn-light{color:#212529;background-color:#f8f9fa;border-color:#f8f9fa}.btn-light:hover{color:#212529;background-color:#e2e6ea;border-color:#dae0e5}.btn-light.focus,.btn-light:focus{color:#212529;background-color:#e2e6ea;border-color:#dae0e5;box-shadow:0 0 0 .2rem rgba(216,217,219,.5)}.btn-light.disabled,.btn-light:disabled{color:#212529;background-color:#f8f9fa;border-color:#f8f9fa}.btn-light:not(:disabled):not(.disabled).active,.btn-light:not(:disabled):not(.disabled):active,.show>.btn-light.dropdown-toggle{color:#212529;background-color:#dae0e5;border-color:#d3d9df}.btn-light:not(:disabled):not(.disabled).active:focus,.btn-light:not(:disabled):not(.disabled):active:focus,.show>.btn-light.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(216,217,219,.5)}.btn-dark{color:#fff;background-color:#343a40;border-color:#343a40}.btn-dark:hover{color:#fff;background-color:#23272b;border-color:#1d2124}.btn-dark.focus,.btn-dark:focus{color:#fff;background-color:#23272b;border-color:#1d2124;box-shadow:0 0 0 .2rem rgba(82,88,93,.5)}.btn-dark.disabled,.btn-dark:disabled{color:#fff;background-color:#343a40;border-color:#343a40}.btn-dark:not(:disabled):not(.disabled).active,.btn-dark:not(:disabled):not(.disabled):active,.show>.btn-dark.dropdown-toggle{color:#fff;background-color:#1d2124;border-color:#171a1d}.btn-dark:not(:disabled):not(.disabled).active:focus,.btn-dark:not(:disabled):not(.disabled):active:focus,.show>.btn-dark.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(82,88,93,.5)}.btn-outline-primary{color:#007bff;border-color:#007bff}.btn-outline-primary:hover{color:#fff;background-color:#007bff;border-color:#007bff}.btn-outline-primary.focus,.btn-outline-primary:focus{box-shadow:0 0 0 .2rem rgba(0,123,255,.5)}.btn-outline-primary.disabled,.btn-outline-primary:disabled{color:#007bff;background-color:transparent}.btn-outline-primary:not(:disabled):not(.disabled).active,.btn-outline-primary:not(:disabled):not(.disabled):active,.show>.btn-outline-primary.dropdown-toggle{color:#fff;background-color:#007bff;border-color:#007bff}.btn-outline-primary:not(:disabled):not(.disabled).active:focus,.btn-outline-primary:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-primary.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(0,123,255,.5)}.btn-outline-secondary{color:#6c757d;border-color:#6c757d}.btn-outline-secondary:hover{color:#fff;background-color:#6c757d;border-color:#6c757d}.btn-outline-secondary.focus,.btn-outline-secondary:focus{box-shadow:0 0 0 .2rem rgba(108,117,125,.5)}.btn-outline-secondary.disabled,.btn-outline-secondary:disabled{color:#6c757d;background-color:transparent}.btn-outline-secondary:not(:disabled):not(.disabled).active,.btn-outline-secondary:not(:disabled):not(.disabled):active,.show>.btn-outline-secondary.dropdown-toggle{color:#fff;background-color:#6c757d;border-color:#6c757d}.btn-outline-secondary:not(:disabled):not(.disabled).active:focus,.btn-outline-secondary:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-secondary.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(108,117,125,.5)}.btn-outline-success{color:#28a745;border-color:#28a745}.btn-outline-success:hover{color:#fff;background-color:#28a745;border-color:#28a745}.btn-outline-success.focus,.btn-outline-success:focus{box-shadow:0 0 0 .2rem rgba(40,167,69,.5)}.btn-outline-success.disabled,.btn-outline-success:disabled{color:#28a745;background-color:transparent}.btn-outline-success:not(:disabled):not(.disabled).active,.btn-outline-success:not(:disabled):not(.disabled):active,.show>.btn-outline-success.dropdown-toggle{color:#fff;background-color:#28a745;border-color:#28a745}.btn-outline-success:not(:disabled):not(.disabled).active:focus,.btn-outline-success:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-success.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(40,167,69,.5)}.btn-outline-info{color:#17a2b8;border-color:#17a2b8}.btn-outline-info:hover{color:#fff;background-color:#17a2b8;border-color:#17a2b8}.btn-outline-info.focus,.btn-outline-info:focus{box-shadow:0 0 0 .2rem rgba(23,162,184,.5)}.btn-outline-info.disabled,.btn-outline-info:disabled{color:#17a2b8;background-color:transparent}.btn-outline-info:not(:disabled):not(.disabled).active,.btn-outline-info:not(:disabled):not(.disabled):active,.show>.btn-outline-info.dropdown-toggle{color:#fff;background-color:#17a2b8;border-color:#17a2b8}.btn-outline-info:not(:disabled):not(.disabled).active:focus,.btn-outline-info:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-info.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(23,162,184,.5)}.btn-outline-warning{color:#ffc107;border-color:#ffc107}.btn-outline-warning:hover{color:#212529;background-color:#ffc107;border-color:#ffc107}.btn-outline-warning.focus,.btn-outline-warning:focus{box-shadow:0 0 0 .2rem rgba(255,193,7,.5)}.btn-outline-warning.disabled,.btn-outline-warning:disabled{color:#ffc107;background-color:transparent}.btn-outline-warning:not(:disabled):not(.disabled).active,.btn-outline-warning:not(:disabled):not(.disabled):active,.show>.btn-outline-warning.dropdown-toggle{color:#212529;background-color:#ffc107;border-color:#ffc107}.btn-outline-warning:not(:disabled):not(.disabled).active:focus,.btn-outline-warning:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-warning.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(255,193,7,.5)}.btn-outline-danger{color:#dc3545;border-color:#dc3545}.btn-outline-danger:hover{color:#fff;background-color:#dc3545;border-color:#dc3545}.btn-outline-danger.focus,.btn-outline-danger:focus{box-shadow:0 0 0 .2rem rgba(220,53,69,.5)}.btn-outline-danger.disabled,.btn-outline-danger:disabled{color:#dc3545;background-color:transparent}.btn-outline-danger:not(:disabled):not(.disabled).active,.btn-outline-danger:not(:disabled):not(.disabled):active,.show>.btn-outline-danger.dropdown-toggle{color:#fff;background-color:#dc3545;border-color:#dc3545}.btn-outline-danger:not(:disabled):not(.disabled).active:focus,.btn-outline-danger:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-danger.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(220,53,69,.5)}.btn-outline-light{color:#f8f9fa;border-color:#f8f9fa}.btn-outline-light:hover{color:#212529;background-color:#f8f9fa;border-color:#f8f9fa}.btn-outline-light.focus,.btn-outline-light:focus{box-shadow:0 0 0 .2rem rgba(248,249,250,.5)}.btn-outline-light.disabled,.btn-outline-light:disabled{color:#f8f9fa;background-color:transparent}.btn-outline-light:not(:disabled):not(.disabled).active,.btn-outline-light:not(:disabled):not(.disabled):active,.show>.btn-outline-light.dropdown-toggle{color:#212529;background-color:#f8f9fa;border-color:#f8f9fa}.btn-outline-light:not(:disabled):not(.disabled).active:focus,.btn-outline-light:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-light.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(248,249,250,.5)}.btn-outline-dark{color:#343a40;border-color:#343a40}.btn-outline-dark:hover{color:#fff;background-color:#343a40;border-color:#343a40}.btn-outline-dark.focus,.btn-outline-dark:focus{box-shadow:0 0 0 .2rem rgba(52,58,64,.5)}.btn-outline-dark.disabled,.btn-outline-dark:disabled{color:#343a40;background-color:transparent}.btn-outline-dark:not(:disabled):not(.disabled).active,.btn-outline-dark:not(:disabled):not(.disabled):active,.show>.btn-outline-dark.dropdown-toggle{color:#fff;background-color:#343a40;border-color:#343a40}.btn-outline-dark:not(:disabled):not(.disabled).active:focus,.btn-outline-dark:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-dark.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(52,58,64,.5)}.btn-link{font-weight:400;color:#007bff;text-decoration:none}.btn-link:hover{color:#0056b3;text-decoration:underline}.btn-link.focus,.btn-link:focus{text-decoration:underline}.btn-link.disabled,.btn-link:disabled{color:#6c757d;pointer-events:none}.btn-group-lg>.btn,.btn-lg{padding:.5rem 1rem;font-size:1.25rem;line-height:1.5;border-radius:.3rem}.btn-group-sm>.btn,.btn-sm{padding:.25rem .5rem;font-size:.875rem;line-height:1.5;border-radius:.2rem}.btn-block{display:block;width:100%}.btn-block+.btn-block{margin-top:.5rem}input[type=button].btn-block,input[type=reset].btn-block,input[type=submit].btn-block{width:100%}.fade{transition:opacity .15s linear}@media (prefers-reduced-motion:reduce){.fade{transition:none}}.fade:not(.show){opacity:0}.collapse:not(.show){display:none}.collapsing{position:relative;height:0;overflow:hidden;transition:height .35s ease}@media (prefers-reduced-motion:reduce){.collapsing{transition:none}}.dropdown,.dropleft,.dropright,.dropup{position:relative}.dropdown-toggle{white-space:nowrap}.dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:"";border-top:.3em solid;border-right:.3em solid transparent;border-bottom:0;border-left:.3em solid transparent}.dropdown-toggle:empty::after{margin-left:0}.dropdown-menu{position:absolute;top:100%;left:0;z-index:1000;display:none;float:left;min-width:10rem;padding:.5rem 0;margin:.125rem 0 0;font-size:1rem;color:#212529;text-align:left;list-style:none;background-color:#fff;background-clip:padding-box;border:1px solid rgba(0,0,0,.15);border-radius:.25rem}.dropdown-menu-left{right:auto;left:0}.dropdown-menu-right{right:0;left:auto}@media (min-width:576px){.dropdown-menu-sm-left{right:auto;left:0}.dropdown-menu-sm-right{right:0;left:auto}}@media (min-width:768px){.dropdown-menu-md-left{right:auto;left:0}.dropdown-menu-md-right{right:0;left:auto}}@media (min-width:992px){.dropdown-menu-lg-left{right:auto;left:0}.dropdown-menu-lg-right{right:0;left:auto}}@media (min-width:1200px){.dropdown-menu-xl-left{right:auto;left:0}.dropdown-menu-xl-right{right:0;left:auto}}.dropup .dropdown-menu{top:auto;bottom:100%;margin-top:0;margin-bottom:.125rem}.dropup .dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:"";border-top:0;border-right:.3em solid transparent;border-bottom:.3em solid;border-left:.3em solid transparent}.dropup .dropdown-toggle:empty::after{margin-left:0}.dropright .dropdown-menu{top:0;right:auto;left:100%;margin-top:0;margin-left:.125rem}.dropright .dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:"";border-top:.3em solid transparent;border-right:0;border-bottom:.3em solid transparent;border-left:.3em solid}.dropright .dropdown-toggle:empty::after{margin-left:0}.dropright .dropdown-toggle::after{vertical-align:0}.dropleft .dropdown-menu{top:0;right:100%;left:auto;margin-top:0;margin-right:.125rem}.dropleft .dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:""}.dropleft .dropdown-toggle::after{display:none}.dropleft .dropdown-toggle::before{display:inline-block;margin-right:.255em;vertical-align:.255em;content:"";border-top:.3em solid transparent;border-right:.3em solid;border-bottom:.3em solid transparent}.dropleft .dropdown-toggle:empty::after{margin-left:0}.dropleft .dropdown-toggle::before{vertical-align:0}.dropdown-menu[x-placement^=bottom],.dropdown-menu[x-placement^=left],.dropdown-menu[x-placement^=right],.dropdown-menu[x-placement^=top]{right:auto;bottom:auto}.dropdown-divider{height:0;margin:.5rem 0;overflow:hidden;border-top:1px solid #e9ecef}.dropdown-item{display:block;width:100%;padding:.25rem 1.5rem;clear:both;font-weight:400;color:#212529;text-align:inherit;white-space:nowrap;background-color:transparent;border:0}.dropdown-item:focus,.dropdown-item:hover{color:#16181b;text-decoration:none;background-color:#f8f9fa}.dropdown-item.active,.dropdown-item:active{color:#fff;text-decoration:none;background-color:#007bff}.dropdown-item.disabled,.dropdown-item:disabled{color:#6c757d;pointer-events:none;background-color:transparent}.dropdown-menu.show{display:block}.dropdown-header{display:block;padding:.5rem 1.5rem;margin-bottom:0;font-size:.875rem;color:#6c757d;white-space:nowrap}.dropdown-item-text{display:block;padding:.25rem 1.5rem;color:#212529}.btn-group,.btn-group-vertical{position:relative;display:-ms-inline-flexbox;display:inline-flex;vertical-align:middle}.btn-group-vertical>.btn,.btn-group>.btn{position:relative;-ms-flex:1 1 auto;flex:1 1 auto}.btn-group-vertical>.btn:hover,.btn-group>.btn:hover{z-index:1}.btn-group-vertical>.btn.active,.btn-group-vertical>.btn:active,.btn-group-vertical>.btn:focus,.btn-group>.btn.active,.btn-group>.btn:active,.btn-group>.btn:focus{z-index:1}.btn-toolbar{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;-ms-flex-pack:start;justify-content:flex-start}.btn-toolbar .input-group{width:auto}.btn-group>.btn-group:not(:first-child),.btn-group>.btn:not(:first-child){margin-left:-1px}.btn-group>.btn-group:not(:last-child)>.btn,.btn-group>.btn:not(:last-child):not(.dropdown-toggle){border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn-group:not(:first-child)>.btn,.btn-group>.btn:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.dropdown-toggle-split{padding-right:.5625rem;padding-left:.5625rem}.dropdown-toggle-split::after,.dropright .dropdown-toggle-split::after,.dropup .dropdown-toggle-split::after{margin-left:0}.dropleft .dropdown-toggle-split::before{margin-right:0}.btn-group-sm>.btn+.dropdown-toggle-split,.btn-sm+.dropdown-toggle-split{padding-right:.375rem;padding-left:.375rem}.btn-group-lg>.btn+.dropdown-toggle-split,.btn-lg+.dropdown-toggle-split{padding-right:.75rem;padding-left:.75rem}.btn-group-vertical{-ms-flex-direction:column;flex-direction:column;-ms-flex-align:start;align-items:flex-start;-ms-flex-pack:center;justify-content:center}.btn-group-vertical>.btn,.btn-group-vertical>.btn-group{width:100%}.btn-group-vertical>.btn-group:not(:first-child),.btn-group-vertical>.btn:not(:first-child){margin-top:-1px}.btn-group-vertical>.btn-group:not(:last-child)>.btn,.btn-group-vertical>.btn:not(:last-child):not(.dropdown-toggle){border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn-group:not(:first-child)>.btn,.btn-group-vertical>.btn:not(:first-child){border-top-left-radius:0;border-top-right-radius:0}.btn-group-toggle>.btn,.btn-group-toggle>.btn-group>.btn{margin-bottom:0}.btn-group-toggle>.btn input[type=checkbox],.btn-group-toggle>.btn input[type=radio],.btn-group-toggle>.btn-group>.btn input[type=checkbox],.btn-group-toggle>.btn-group>.btn input[type=radio]{position:absolute;clip:rect(0,0,0,0);pointer-events:none}.input-group{position:relative;display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;-ms-flex-align:stretch;align-items:stretch;width:100%}.input-group>.custom-file,.input-group>.custom-select,.input-group>.form-control,.input-group>.form-control-plaintext{position:relative;-ms-flex:1 1 auto;flex:1 1 auto;width:1%;min-width:0;margin-bottom:0}.input-group>.custom-file+.custom-file,.input-group>.custom-file+.custom-select,.input-group>.custom-file+.form-control,.input-group>.custom-select+.custom-file,.input-group>.custom-select+.custom-select,.input-group>.custom-select+.form-control,.input-group>.form-control+.custom-file,.input-group>.form-control+.custom-select,.input-group>.form-control+.form-control,.input-group>.form-control-plaintext+.custom-file,.input-group>.form-control-plaintext+.custom-select,.input-group>.form-control-plaintext+.form-control{margin-left:-1px}.input-group>.custom-file .custom-file-input:focus~.custom-file-label,.input-group>.custom-select:focus,.input-group>.form-control:focus{z-index:3}.input-group>.custom-file .custom-file-input:focus{z-index:4}.input-group>.custom-select:not(:last-child),.input-group>.form-control:not(:last-child){border-top-right-radius:0;border-bottom-right-radius:0}.input-group>.custom-select:not(:first-child),.input-group>.form-control:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.input-group>.custom-file{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center}.input-group>.custom-file:not(:last-child) .custom-file-label,.input-group>.custom-file:not(:last-child) .custom-file-label::after{border-top-right-radius:0;border-bottom-right-radius:0}.input-group>.custom-file:not(:first-child) .custom-file-label{border-top-left-radius:0;border-bottom-left-radius:0}.input-group-append,.input-group-prepend{display:-ms-flexbox;display:flex}.input-group-append .btn,.input-group-prepend .btn{position:relative;z-index:2}.input-group-append .btn:focus,.input-group-prepend .btn:focus{z-index:3}.input-group-append .btn+.btn,.input-group-append .btn+.input-group-text,.input-group-append .input-group-text+.btn,.input-group-append .input-group-text+.input-group-text,.input-group-prepend .btn+.btn,.input-group-prepend .btn+.input-group-text,.input-group-prepend .input-group-text+.btn,.input-group-prepend .input-group-text+.input-group-text{margin-left:-1px}.input-group-prepend{margin-right:-1px}.input-group-append{margin-left:-1px}.input-group-text{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;padding:.375rem .75rem;margin-bottom:0;font-size:1rem;font-weight:400;line-height:1.5;color:#495057;text-align:center;white-space:nowrap;background-color:#e9ecef;border:1px solid #ced4da;border-radius:.25rem}.input-group-text input[type=checkbox],.input-group-text input[type=radio]{margin-top:0}.input-group-lg>.custom-select,.input-group-lg>.form-control:not(textarea){height:calc(1.5em + 1rem + 2px)}.input-group-lg>.custom-select,.input-group-lg>.form-control,.input-group-lg>.input-group-append>.btn,.input-group-lg>.input-group-append>.input-group-text,.input-group-lg>.input-group-prepend>.btn,.input-group-lg>.input-group-prepend>.input-group-text{padding:.5rem 1rem;font-size:1.25rem;line-height:1.5;border-radius:.3rem}.input-group-sm>.custom-select,.input-group-sm>.form-control:not(textarea){height:calc(1.5em + .5rem + 2px)}.input-group-sm>.custom-select,.input-group-sm>.form-control,.input-group-sm>.input-group-append>.btn,.input-group-sm>.input-group-append>.input-group-text,.input-group-sm>.input-group-prepend>.btn,.input-group-sm>.input-group-prepend>.input-group-text{padding:.25rem .5rem;font-size:.875rem;line-height:1.5;border-radius:.2rem}.input-group-lg>.custom-select,.input-group-sm>.custom-select{padding-right:1.75rem}.input-group>.input-group-append:last-child>.btn:not(:last-child):not(.dropdown-toggle),.input-group>.input-group-append:last-child>.input-group-text:not(:last-child),.input-group>.input-group-append:not(:last-child)>.btn,.input-group>.input-group-append:not(:last-child)>.input-group-text,.input-group>.input-group-prepend>.btn,.input-group>.input-group-prepend>.input-group-text{border-top-right-radius:0;border-bottom-right-radius:0}.input-group>.input-group-append>.btn,.input-group>.input-group-append>.input-group-text,.input-group>.input-group-prepend:first-child>.btn:not(:first-child),.input-group>.input-group-prepend:first-child>.input-group-text:not(:first-child),.input-group>.input-group-prepend:not(:first-child)>.btn,.input-group>.input-group-prepend:not(:first-child)>.input-group-text{border-top-left-radius:0;border-bottom-left-radius:0}.custom-control{position:relative;z-index:1;display:block;min-height:1.5rem;padding-left:1.5rem;-webkit-print-color-adjust:exact;color-adjust:exact}.custom-control-inline{display:-ms-inline-flexbox;display:inline-flex;margin-right:1rem}.custom-control-input{position:absolute;left:0;z-index:-1;width:1rem;height:1.25rem;opacity:0}.custom-control-input:checked~.custom-control-label::before{color:#fff;border-color:#007bff;background-color:#007bff}.custom-control-input:focus~.custom-control-label::before{box-shadow:0 0 0 .2rem rgba(0,123,255,.25)}.custom-control-input:focus:not(:checked)~.custom-control-label::before{border-color:#80bdff}.custom-control-input:not(:disabled):active~.custom-control-label::before{color:#fff;background-color:#b3d7ff;border-color:#b3d7ff}.custom-control-input:disabled~.custom-control-label,.custom-control-input[disabled]~.custom-control-label{color:#6c757d}.custom-control-input:disabled~.custom-control-label::before,.custom-control-input[disabled]~.custom-control-label::before{background-color:#e9ecef}.custom-control-label{position:relative;margin-bottom:0;vertical-align:top}.custom-control-label::before{position:absolute;top:.25rem;left:-1.5rem;display:block;width:1rem;height:1rem;pointer-events:none;content:"";background-color:#fff;border:#adb5bd solid 1px}.custom-control-label::after{position:absolute;top:.25rem;left:-1.5rem;display:block;width:1rem;height:1rem;content:"";background:no-repeat 50%/50% 50%}.custom-checkbox .custom-control-label::before{border-radius:.25rem}.custom-checkbox .custom-control-input:checked~.custom-control-label::after{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='8' height='8' viewBox='0 0 8 8'%3e%3cpath fill='%23fff' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26l2.974 2.99L8 2.193z'/%3e%3c/svg%3e")}.custom-checkbox .custom-control-input:indeterminate~.custom-control-label::before{border-color:#007bff;background-color:#007bff}.custom-checkbox .custom-control-input:indeterminate~.custom-control-label::after{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='4' height='4' viewBox='0 0 4 4'%3e%3cpath stroke='%23fff' d='M0 2h4'/%3e%3c/svg%3e")}.custom-checkbox .custom-control-input:disabled:checked~.custom-control-label::before{background-color:rgba(0,123,255,.5)}.custom-checkbox .custom-control-input:disabled:indeterminate~.custom-control-label::before{background-color:rgba(0,123,255,.5)}.custom-radio .custom-control-label::before{border-radius:50%}.custom-radio .custom-control-input:checked~.custom-control-label::after{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='%23fff'/%3e%3c/svg%3e")}.custom-radio .custom-control-input:disabled:checked~.custom-control-label::before{background-color:rgba(0,123,255,.5)}.custom-switch{padding-left:2.25rem}.custom-switch .custom-control-label::before{left:-2.25rem;width:1.75rem;pointer-events:all;border-radius:.5rem}.custom-switch .custom-control-label::after{top:calc(.25rem + 2px);left:calc(-2.25rem + 2px);width:calc(1rem - 4px);height:calc(1rem - 4px);background-color:#adb5bd;border-radius:.5rem;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out,-webkit-transform .15s ease-in-out;transition:transform .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;transition:transform .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out,-webkit-transform .15s ease-in-out}@media (prefers-reduced-motion:reduce){.custom-switch .custom-control-label::after{transition:none}}.custom-switch .custom-control-input:checked~.custom-control-label::after{background-color:#fff;-webkit-transform:translateX(.75rem);transform:translateX(.75rem)}.custom-switch .custom-control-input:disabled:checked~.custom-control-label::before{background-color:rgba(0,123,255,.5)}.custom-select{display:inline-block;width:100%;height:calc(1.5em + .75rem + 2px);padding:.375rem 1.75rem .375rem .75rem;font-size:1rem;font-weight:400;line-height:1.5;color:#495057;vertical-align:middle;background:#fff url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='4' height='5' viewBox='0 0 4 5'%3e%3cpath fill='%23343a40' d='M2 0L0 2h4zm0 5L0 3h4z'/%3e%3c/svg%3e") no-repeat right .75rem center/8px 10px;border:1px solid #ced4da;border-radius:.25rem;-webkit-appearance:none;-moz-appearance:none;appearance:none}.custom-select:focus{border-color:#80bdff;outline:0;box-shadow:0 0 0 .2rem rgba(0,123,255,.25)}.custom-select:focus::-ms-value{color:#495057;background-color:#fff}.custom-select[multiple],.custom-select[size]:not([size="1"]){height:auto;padding-right:.75rem;background-image:none}.custom-select:disabled{color:#6c757d;background-color:#e9ecef}.custom-select::-ms-expand{display:none}.custom-select:-moz-focusring{color:transparent;text-shadow:0 0 0 #495057}.custom-select-sm{height:calc(1.5em + .5rem + 2px);padding-top:.25rem;padding-bottom:.25rem;padding-left:.5rem;font-size:.875rem}.custom-select-lg{height:calc(1.5em + 1rem + 2px);padding-top:.5rem;padding-bottom:.5rem;padding-left:1rem;font-size:1.25rem}.custom-file{position:relative;display:inline-block;width:100%;height:calc(1.5em + .75rem + 2px);margin-bottom:0}.custom-file-input{position:relative;z-index:2;width:100%;height:calc(1.5em + .75rem + 2px);margin:0;opacity:0}.custom-file-input:focus~.custom-file-label{border-color:#80bdff;box-shadow:0 0 0 .2rem rgba(0,123,255,.25)}.custom-file-input:disabled~.custom-file-label,.custom-file-input[disabled]~.custom-file-label{background-color:#e9ecef}.custom-file-input:lang(en)~.custom-file-label::after{content:"Browse"}.custom-file-input~.custom-file-label[data-browse]::after{content:attr(data-browse)}.custom-file-label{position:absolute;top:0;right:0;left:0;z-index:1;height:calc(1.5em + .75rem + 2px);padding:.375rem .75rem;font-weight:400;line-height:1.5;color:#495057;background-color:#fff;border:1px solid #ced4da;border-radius:.25rem}.custom-file-label::after{position:absolute;top:0;right:0;bottom:0;z-index:3;display:block;height:calc(1.5em + .75rem);padding:.375rem .75rem;line-height:1.5;color:#495057;content:"Browse";background-color:#e9ecef;border-left:inherit;border-radius:0 .25rem .25rem 0}.custom-range{width:100%;height:1.4rem;padding:0;background-color:transparent;-webkit-appearance:none;-moz-appearance:none;appearance:none}.custom-range:focus{outline:0}.custom-range:focus::-webkit-slider-thumb{box-shadow:0 0 0 1px #fff,0 0 0 .2rem rgba(0,123,255,.25)}.custom-range:focus::-moz-range-thumb{box-shadow:0 0 0 1px #fff,0 0 0 .2rem rgba(0,123,255,.25)}.custom-range:focus::-ms-thumb{box-shadow:0 0 0 1px #fff,0 0 0 .2rem rgba(0,123,255,.25)}.custom-range::-moz-focus-outer{border:0}.custom-range::-webkit-slider-thumb{width:1rem;height:1rem;margin-top:-.25rem;background-color:#007bff;border:0;border-radius:1rem;-webkit-transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;-webkit-appearance:none;appearance:none}@media (prefers-reduced-motion:reduce){.custom-range::-webkit-slider-thumb{-webkit-transition:none;transition:none}}.custom-range::-webkit-slider-thumb:active{background-color:#b3d7ff}.custom-range::-webkit-slider-runnable-track{width:100%;height:.5rem;color:transparent;cursor:pointer;background-color:#dee2e6;border-color:transparent;border-radius:1rem}.custom-range::-moz-range-thumb{width:1rem;height:1rem;background-color:#007bff;border:0;border-radius:1rem;-moz-transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;-moz-appearance:none;appearance:none}@media (prefers-reduced-motion:reduce){.custom-range::-moz-range-thumb{-moz-transition:none;transition:none}}.custom-range::-moz-range-thumb:active{background-color:#b3d7ff}.custom-range::-moz-range-track{width:100%;height:.5rem;color:transparent;cursor:pointer;background-color:#dee2e6;border-color:transparent;border-radius:1rem}.custom-range::-ms-thumb{width:1rem;height:1rem;margin-top:0;margin-right:.2rem;margin-left:.2rem;background-color:#007bff;border:0;border-radius:1rem;-ms-transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;appearance:none}@media (prefers-reduced-motion:reduce){.custom-range::-ms-thumb{-ms-transition:none;transition:none}}.custom-range::-ms-thumb:active{background-color:#b3d7ff}.custom-range::-ms-track{width:100%;height:.5rem;color:transparent;cursor:pointer;background-color:transparent;border-color:transparent;border-width:.5rem}.custom-range::-ms-fill-lower{background-color:#dee2e6;border-radius:1rem}.custom-range::-ms-fill-upper{margin-right:15px;background-color:#dee2e6;border-radius:1rem}.custom-range:disabled::-webkit-slider-thumb{background-color:#adb5bd}.custom-range:disabled::-webkit-slider-runnable-track{cursor:default}.custom-range:disabled::-moz-range-thumb{background-color:#adb5bd}.custom-range:disabled::-moz-range-track{cursor:default}.custom-range:disabled::-ms-thumb{background-color:#adb5bd}.custom-control-label::before,.custom-file-label,.custom-select{transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.custom-control-label::before,.custom-file-label,.custom-select{transition:none}}.nav{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;padding-left:0;margin-bottom:0;list-style:none}.nav-link{display:block;padding:.5rem 1rem}.nav-link:focus,.nav-link:hover{text-decoration:none}.nav-link.disabled{color:#6c757d;pointer-events:none;cursor:default}.nav-tabs{border-bottom:1px solid #dee2e6}.nav-tabs .nav-item{margin-bottom:-1px}.nav-tabs .nav-link{border:1px solid transparent;border-top-left-radius:.25rem;border-top-right-radius:.25rem}.nav-tabs .nav-link:focus,.nav-tabs .nav-link:hover{border-color:#e9ecef #e9ecef #dee2e6}.nav-tabs .nav-link.disabled{color:#6c757d;background-color:transparent;border-color:transparent}.nav-tabs .nav-item.show .nav-link,.nav-tabs .nav-link.active{color:#495057;background-color:#fff;border-color:#dee2e6 #dee2e6 #fff}.nav-tabs .dropdown-menu{margin-top:-1px;border-top-left-radius:0;border-top-right-radius:0}.nav-pills .nav-link{border-radius:.25rem}.nav-pills .nav-link.active,.nav-pills .show>.nav-link{color:#fff;background-color:#007bff}.nav-fill .nav-item,.nav-fill>.nav-link{-ms-flex:1 1 auto;flex:1 1 auto;text-align:center}.nav-justified .nav-item,.nav-justified>.nav-link{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;text-align:center}.tab-content>.tab-pane{display:none}.tab-content>.active{display:block}.navbar{position:relative;display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;-ms-flex-align:center;align-items:center;-ms-flex-pack:justify;justify-content:space-between;padding:.5rem 1rem}.navbar .container,.navbar .container-fluid,.navbar .container-lg,.navbar .container-md,.navbar .container-sm,.navbar .container-xl{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;-ms-flex-align:center;align-items:center;-ms-flex-pack:justify;justify-content:space-between}.navbar-brand{display:inline-block;padding-top:.3125rem;padding-bottom:.3125rem;margin-right:1rem;font-size:1.25rem;line-height:inherit;white-space:nowrap}.navbar-brand:focus,.navbar-brand:hover{text-decoration:none}.navbar-nav{display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column;padding-left:0;margin-bottom:0;list-style:none}.navbar-nav .nav-link{padding-right:0;padding-left:0}.navbar-nav .dropdown-menu{position:static;float:none}.navbar-text{display:inline-block;padding-top:.5rem;padding-bottom:.5rem}.navbar-collapse{-ms-flex-preferred-size:100%;flex-basis:100%;-ms-flex-positive:1;flex-grow:1;-ms-flex-align:center;align-items:center}.navbar-toggler{padding:.25rem .75rem;font-size:1.25rem;line-height:1;background-color:transparent;border:1px solid transparent;border-radius:.25rem}.navbar-toggler:focus,.navbar-toggler:hover{text-decoration:none}.navbar-toggler-icon{display:inline-block;width:1.5em;height:1.5em;vertical-align:middle;content:"";background:no-repeat center center;background-size:100% 100%}@media (max-width:575.98px){.navbar-expand-sm>.container,.navbar-expand-sm>.container-fluid,.navbar-expand-sm>.container-lg,.navbar-expand-sm>.container-md,.navbar-expand-sm>.container-sm,.navbar-expand-sm>.container-xl{padding-right:0;padding-left:0}}@media (min-width:576px){.navbar-expand-sm{-ms-flex-flow:row nowrap;flex-flow:row nowrap;-ms-flex-pack:start;justify-content:flex-start}.navbar-expand-sm .navbar-nav{-ms-flex-direction:row;flex-direction:row}.navbar-expand-sm .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-sm .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-sm>.container,.navbar-expand-sm>.container-fluid,.navbar-expand-sm>.container-lg,.navbar-expand-sm>.container-md,.navbar-expand-sm>.container-sm,.navbar-expand-sm>.container-xl{-ms-flex-wrap:nowrap;flex-wrap:nowrap}.navbar-expand-sm .navbar-collapse{display:-ms-flexbox!important;display:flex!important;-ms-flex-preferred-size:auto;flex-basis:auto}.navbar-expand-sm .navbar-toggler{display:none}}@media (max-width:767.98px){.navbar-expand-md>.container,.navbar-expand-md>.container-fluid,.navbar-expand-md>.container-lg,.navbar-expand-md>.container-md,.navbar-expand-md>.container-sm,.navbar-expand-md>.container-xl{padding-right:0;padding-left:0}}@media (min-width:768px){.navbar-expand-md{-ms-flex-flow:row nowrap;flex-flow:row nowrap;-ms-flex-pack:start;justify-content:flex-start}.navbar-expand-md .navbar-nav{-ms-flex-direction:row;flex-direction:row}.navbar-expand-md .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-md .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-md>.container,.navbar-expand-md>.container-fluid,.navbar-expand-md>.container-lg,.navbar-expand-md>.container-md,.navbar-expand-md>.container-sm,.navbar-expand-md>.container-xl{-ms-flex-wrap:nowrap;flex-wrap:nowrap}.navbar-expand-md .navbar-collapse{display:-ms-flexbox!important;display:flex!important;-ms-flex-preferred-size:auto;flex-basis:auto}.navbar-expand-md .navbar-toggler{display:none}}@media (max-width:991.98px){.navbar-expand-lg>.container,.navbar-expand-lg>.container-fluid,.navbar-expand-lg>.container-lg,.navbar-expand-lg>.container-md,.navbar-expand-lg>.container-sm,.navbar-expand-lg>.container-xl{padding-right:0;padding-left:0}}@media (min-width:992px){.navbar-expand-lg{-ms-flex-flow:row nowrap;flex-flow:row nowrap;-ms-flex-pack:start;justify-content:flex-start}.navbar-expand-lg .navbar-nav{-ms-flex-direction:row;flex-direction:row}.navbar-expand-lg .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-lg .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-lg>.container,.navbar-expand-lg>.container-fluid,.navbar-expand-lg>.container-lg,.navbar-expand-lg>.container-md,.navbar-expand-lg>.container-sm,.navbar-expand-lg>.container-xl{-ms-flex-wrap:nowrap;flex-wrap:nowrap}.navbar-expand-lg .navbar-collapse{display:-ms-flexbox!important;display:flex!important;-ms-flex-preferred-size:auto;flex-basis:auto}.navbar-expand-lg .navbar-toggler{display:none}}@media (max-width:1199.98px){.navbar-expand-xl>.container,.navbar-expand-xl>.container-fluid,.navbar-expand-xl>.container-lg,.navbar-expand-xl>.container-md,.navbar-expand-xl>.container-sm,.navbar-expand-xl>.container-xl{padding-right:0;padding-left:0}}@media (min-width:1200px){.navbar-expand-xl{-ms-flex-flow:row nowrap;flex-flow:row nowrap;-ms-flex-pack:start;justify-content:flex-start}.navbar-expand-xl .navbar-nav{-ms-flex-direction:row;flex-direction:row}.navbar-expand-xl .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-xl .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-xl>.container,.navbar-expand-xl>.container-fluid,.navbar-expand-xl>.container-lg,.navbar-expand-xl>.container-md,.navbar-expand-xl>.container-sm,.navbar-expand-xl>.container-xl{-ms-flex-wrap:nowrap;flex-wrap:nowrap}.navbar-expand-xl .navbar-collapse{display:-ms-flexbox!important;display:flex!important;-ms-flex-preferred-size:auto;flex-basis:auto}.navbar-expand-xl .navbar-toggler{display:none}}.navbar-expand{-ms-flex-flow:row nowrap;flex-flow:row nowrap;-ms-flex-pack:start;justify-content:flex-start}.navbar-expand>.container,.navbar-expand>.container-fluid,.navbar-expand>.container-lg,.navbar-expand>.container-md,.navbar-expand>.container-sm,.navbar-expand>.container-xl{padding-right:0;padding-left:0}.navbar-expand .navbar-nav{-ms-flex-direction:row;flex-direction:row}.navbar-expand .navbar-nav .dropdown-menu{position:absolute}.navbar-expand .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand>.container,.navbar-expand>.container-fluid,.navbar-expand>.container-lg,.navbar-expand>.container-md,.navbar-expand>.container-sm,.navbar-expand>.container-xl{-ms-flex-wrap:nowrap;flex-wrap:nowrap}.navbar-expand .navbar-collapse{display:-ms-flexbox!important;display:flex!important;-ms-flex-preferred-size:auto;flex-basis:auto}.navbar-expand .navbar-toggler{display:none}.navbar-light .navbar-brand{color:rgba(0,0,0,.9)}.navbar-light .navbar-brand:focus,.navbar-light .navbar-brand:hover{color:rgba(0,0,0,.9)}.navbar-light .navbar-nav .nav-link{color:rgba(0,0,0,.5)}.navbar-light .navbar-nav .nav-link:focus,.navbar-light .navbar-nav .nav-link:hover{color:rgba(0,0,0,.7)}.navbar-light .navbar-nav .nav-link.disabled{color:rgba(0,0,0,.3)}.navbar-light .navbar-nav .active>.nav-link,.navbar-light .navbar-nav .nav-link.active,.navbar-light .navbar-nav .nav-link.show,.navbar-light .navbar-nav .show>.nav-link{color:rgba(0,0,0,.9)}.navbar-light .navbar-toggler{color:rgba(0,0,0,.5);border-color:rgba(0,0,0,.1)}.navbar-light .navbar-toggler-icon{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='30' height='30' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%280, 0, 0, 0.5%29' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e")}.navbar-light .navbar-text{color:rgba(0,0,0,.5)}.navbar-light .navbar-text a{color:rgba(0,0,0,.9)}.navbar-light .navbar-text a:focus,.navbar-light .navbar-text a:hover{color:rgba(0,0,0,.9)}.navbar-dark .navbar-brand{color:#fff}.navbar-dark .navbar-brand:focus,.navbar-dark .navbar-brand:hover{color:#fff}.navbar-dark .navbar-nav .nav-link{color:rgba(255,255,255,.5)}.navbar-dark .navbar-nav .nav-link:focus,.navbar-dark .navbar-nav .nav-link:hover{color:rgba(255,255,255,.75)}.navbar-dark .navbar-nav .nav-link.disabled{color:rgba(255,255,255,.25)}.navbar-dark .navbar-nav .active>.nav-link,.navbar-dark .navbar-nav .nav-link.active,.navbar-dark .navbar-nav .nav-link.show,.navbar-dark .navbar-nav .show>.nav-link{color:#fff}.navbar-dark .navbar-toggler{color:rgba(255,255,255,.5);border-color:rgba(255,255,255,.1)}.navbar-dark .navbar-toggler-icon{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='30' height='30' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%28255, 255, 255, 0.5%29' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e")}.navbar-dark .navbar-text{color:rgba(255,255,255,.5)}.navbar-dark .navbar-text a{color:#fff}.navbar-dark .navbar-text a:focus,.navbar-dark .navbar-text a:hover{color:#fff}.card{position:relative;display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column;min-width:0;word-wrap:break-word;background-color:#fff;background-clip:border-box;border:1px solid rgba(0,0,0,.125);border-radius:.25rem}.card>hr{margin-right:0;margin-left:0}.card>.list-group{border-top:inherit;border-bottom:inherit}.card>.list-group:first-child{border-top-width:0;border-top-left-radius:calc(.25rem - 1px);border-top-right-radius:calc(.25rem - 1px)}.card>.list-group:last-child{border-bottom-width:0;border-bottom-right-radius:calc(.25rem - 1px);border-bottom-left-radius:calc(.25rem - 1px)}.card>.card-header+.list-group,.card>.list-group+.card-footer{border-top:0}.card-body{-ms-flex:1 1 auto;flex:1 1 auto;min-height:1px;padding:1.25rem}.card-title{margin-bottom:.75rem}.card-subtitle{margin-top:-.375rem;margin-bottom:0}.card-text:last-child{margin-bottom:0}.card-link:hover{text-decoration:none}.card-link+.card-link{margin-left:1.25rem}.card-header{padding:.75rem 1.25rem;margin-bottom:0;background-color:rgba(0,0,0,.03);border-bottom:1px solid rgba(0,0,0,.125)}.card-header:first-child{border-radius:calc(.25rem - 1px) calc(.25rem - 1px) 0 0}.card-footer{padding:.75rem 1.25rem;background-color:rgba(0,0,0,.03);border-top:1px solid rgba(0,0,0,.125)}.card-footer:last-child{border-radius:0 0 calc(.25rem - 1px) calc(.25rem - 1px)}.card-header-tabs{margin-right:-.625rem;margin-bottom:-.75rem;margin-left:-.625rem;border-bottom:0}.card-header-pills{margin-right:-.625rem;margin-left:-.625rem}.card-img-overlay{position:absolute;top:0;right:0;bottom:0;left:0;padding:1.25rem;border-radius:calc(.25rem - 1px)}.card-img,.card-img-bottom,.card-img-top{-ms-flex-negative:0;flex-shrink:0;width:100%}.card-img,.card-img-top{border-top-left-radius:calc(.25rem - 1px);border-top-right-radius:calc(.25rem - 1px)}.card-img,.card-img-bottom{border-bottom-right-radius:calc(.25rem - 1px);border-bottom-left-radius:calc(.25rem - 1px)}.card-deck .card{margin-bottom:15px}@media (min-width:576px){.card-deck{display:-ms-flexbox;display:flex;-ms-flex-flow:row wrap;flex-flow:row wrap;margin-right:-15px;margin-left:-15px}.card-deck .card{-ms-flex:1 0 0%;flex:1 0 0%;margin-right:15px;margin-bottom:0;margin-left:15px}}.card-group>.card{margin-bottom:15px}@media (min-width:576px){.card-group{display:-ms-flexbox;display:flex;-ms-flex-flow:row wrap;flex-flow:row wrap}.card-group>.card{-ms-flex:1 0 0%;flex:1 0 0%;margin-bottom:0}.card-group>.card+.card{margin-left:0;border-left:0}.card-group>.card:not(:last-child){border-top-right-radius:0;border-bottom-right-radius:0}.card-group>.card:not(:last-child) .card-header,.card-group>.card:not(:last-child) .card-img-top{border-top-right-radius:0}.card-group>.card:not(:last-child) .card-footer,.card-group>.card:not(:last-child) .card-img-bottom{border-bottom-right-radius:0}.card-group>.card:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.card-group>.card:not(:first-child) .card-header,.card-group>.card:not(:first-child) .card-img-top{border-top-left-radius:0}.card-group>.card:not(:first-child) .card-footer,.card-group>.card:not(:first-child) .card-img-bottom{border-bottom-left-radius:0}}.card-columns .card{margin-bottom:.75rem}@media (min-width:576px){.card-columns{-webkit-column-count:3;-moz-column-count:3;column-count:3;-webkit-column-gap:1.25rem;-moz-column-gap:1.25rem;column-gap:1.25rem;orphans:1;widows:1}.card-columns .card{display:inline-block;width:100%}}.accordion{overflow-anchor:none}.accordion>.card{overflow:hidden}.accordion>.card:not(:last-of-type){border-bottom:0;border-bottom-right-radius:0;border-bottom-left-radius:0}.accordion>.card:not(:first-of-type){border-top-left-radius:0;border-top-right-radius:0}.accordion>.card>.card-header{border-radius:0;margin-bottom:-1px}.breadcrumb{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;padding:.75rem 1rem;margin-bottom:1rem;list-style:none;background-color:#e9ecef;border-radius:.25rem}.breadcrumb-item{display:-ms-flexbox;display:flex}.breadcrumb-item+.breadcrumb-item{padding-left:.5rem}.breadcrumb-item+.breadcrumb-item::before{display:inline-block;padding-right:.5rem;color:#6c757d;content:"/"}.breadcrumb-item+.breadcrumb-item:hover::before{text-decoration:underline}.breadcrumb-item+.breadcrumb-item:hover::before{text-decoration:none}.breadcrumb-item.active{color:#6c757d}.pagination{display:-ms-flexbox;display:flex;padding-left:0;list-style:none;border-radius:.25rem}.page-link{position:relative;display:block;padding:.5rem .75rem;margin-left:-1px;line-height:1.25;color:#007bff;background-color:#fff;border:1px solid #dee2e6}.page-link:hover{z-index:2;color:#0056b3;text-decoration:none;background-color:#e9ecef;border-color:#dee2e6}.page-link:focus{z-index:3;outline:0;box-shadow:0 0 0 .2rem rgba(0,123,255,.25)}.page-item:first-child .page-link{margin-left:0;border-top-left-radius:.25rem;border-bottom-left-radius:.25rem}.page-item:last-child .page-link{border-top-right-radius:.25rem;border-bottom-right-radius:.25rem}.page-item.active .page-link{z-index:3;color:#fff;background-color:#007bff;border-color:#007bff}.page-item.disabled .page-link{color:#6c757d;pointer-events:none;cursor:auto;background-color:#fff;border-color:#dee2e6}.pagination-lg .page-link{padding:.75rem 1.5rem;font-size:1.25rem;line-height:1.5}.pagination-lg .page-item:first-child .page-link{border-top-left-radius:.3rem;border-bottom-left-radius:.3rem}.pagination-lg .page-item:last-child .page-link{border-top-right-radius:.3rem;border-bottom-right-radius:.3rem}.pagination-sm .page-link{padding:.25rem .5rem;font-size:.875rem;line-height:1.5}.pagination-sm .page-item:first-child .page-link{border-top-left-radius:.2rem;border-bottom-left-radius:.2rem}.pagination-sm .page-item:last-child .page-link{border-top-right-radius:.2rem;border-bottom-right-radius:.2rem}.badge{display:inline-block;padding:.25em .4em;font-size:75%;font-weight:700;line-height:1;text-align:center;white-space:nowrap;vertical-align:baseline;border-radius:.25rem;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.badge{transition:none}}a.badge:focus,a.badge:hover{text-decoration:none}.badge:empty{display:none}.btn .badge{position:relative;top:-1px}.badge-pill{padding-right:.6em;padding-left:.6em;border-radius:10rem}.badge-primary{color:#fff;background-color:#007bff}a.badge-primary:focus,a.badge-primary:hover{color:#fff;background-color:#0062cc}a.badge-primary.focus,a.badge-primary:focus{outline:0;box-shadow:0 0 0 .2rem rgba(0,123,255,.5)}.badge-secondary{color:#fff;background-color:#6c757d}a.badge-secondary:focus,a.badge-secondary:hover{color:#fff;background-color:#545b62}a.badge-secondary.focus,a.badge-secondary:focus{outline:0;box-shadow:0 0 0 .2rem rgba(108,117,125,.5)}.badge-success{color:#fff;background-color:#28a745}a.badge-success:focus,a.badge-success:hover{color:#fff;background-color:#1e7e34}a.badge-success.focus,a.badge-success:focus{outline:0;box-shadow:0 0 0 .2rem rgba(40,167,69,.5)}.badge-info{color:#fff;background-color:#17a2b8}a.badge-info:focus,a.badge-info:hover{color:#fff;background-color:#117a8b}a.badge-info.focus,a.badge-info:focus{outline:0;box-shadow:0 0 0 .2rem rgba(23,162,184,.5)}.badge-warning{color:#212529;background-color:#ffc107}a.badge-warning:focus,a.badge-warning:hover{color:#212529;background-color:#d39e00}a.badge-warning.focus,a.badge-warning:focus{outline:0;box-shadow:0 0 0 .2rem rgba(255,193,7,.5)}.badge-danger{color:#fff;background-color:#dc3545}a.badge-danger:focus,a.badge-danger:hover{color:#fff;background-color:#bd2130}a.badge-danger.focus,a.badge-danger:focus{outline:0;box-shadow:0 0 0 .2rem rgba(220,53,69,.5)}.badge-light{color:#212529;background-color:#f8f9fa}a.badge-light:focus,a.badge-light:hover{color:#212529;background-color:#dae0e5}a.badge-light.focus,a.badge-light:focus{outline:0;box-shadow:0 0 0 .2rem rgba(248,249,250,.5)}.badge-dark{color:#fff;background-color:#343a40}a.badge-dark:focus,a.badge-dark:hover{color:#fff;background-color:#1d2124}a.badge-dark.focus,a.badge-dark:focus{outline:0;box-shadow:0 0 0 .2rem rgba(52,58,64,.5)}.jumbotron{padding:2rem 1rem;margin-bottom:2rem;background-color:#e9ecef;border-radius:.3rem}@media (min-width:576px){.jumbotron{padding:4rem 2rem}}.jumbotron-fluid{padding-right:0;padding-left:0;border-radius:0}.alert{position:relative;padding:.75rem 1.25rem;margin-bottom:1rem;border:1px solid transparent;border-radius:.25rem}.alert-heading{color:inherit}.alert-link{font-weight:700}.alert-dismissible{padding-right:4rem}.alert-dismissible .close{position:absolute;top:0;right:0;z-index:2;padding:.75rem 1.25rem;color:inherit}.alert-primary{color:#004085;background-color:#cce5ff;border-color:#b8daff}.alert-primary hr{border-top-color:#9fcdff}.alert-primary .alert-link{color:#002752}.alert-secondary{color:#383d41;background-color:#e2e3e5;border-color:#d6d8db}.alert-secondary hr{border-top-color:#c8cbcf}.alert-secondary .alert-link{color:#202326}.alert-success{color:#155724;background-color:#d4edda;border-color:#c3e6cb}.alert-success hr{border-top-color:#b1dfbb}.alert-success .alert-link{color:#0b2e13}.alert-info{color:#0c5460;background-color:#d1ecf1;border-color:#bee5eb}.alert-info hr{border-top-color:#abdde5}.alert-info .alert-link{color:#062c33}.alert-warning{color:#856404;background-color:#fff3cd;border-color:#ffeeba}.alert-warning hr{border-top-color:#ffe8a1}.alert-warning .alert-link{color:#533f03}.alert-danger{color:#721c24;background-color:#f8d7da;border-color:#f5c6cb}.alert-danger hr{border-top-color:#f1b0b7}.alert-danger .alert-link{color:#491217}.alert-light{color:#818182;background-color:#fefefe;border-color:#fdfdfe}.alert-light hr{border-top-color:#ececf6}.alert-light .alert-link{color:#686868}.alert-dark{color:#1b1e21;background-color:#d6d8d9;border-color:#c6c8ca}.alert-dark hr{border-top-color:#b9bbbe}.alert-dark .alert-link{color:#040505}@-webkit-keyframes progress-bar-stripes{from{background-position:1rem 0}to{background-position:0 0}}@keyframes progress-bar-stripes{from{background-position:1rem 0}to{background-position:0 0}}.progress{display:-ms-flexbox;display:flex;height:1rem;overflow:hidden;line-height:0;font-size:.75rem;background-color:#e9ecef;border-radius:.25rem}.progress-bar{display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column;-ms-flex-pack:center;justify-content:center;overflow:hidden;color:#fff;text-align:center;white-space:nowrap;background-color:#007bff;transition:width .6s ease}@media (prefers-reduced-motion:reduce){.progress-bar{transition:none}}.progress-bar-striped{background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-size:1rem 1rem}.progress-bar-animated{-webkit-animation:progress-bar-stripes 1s linear infinite;animation:progress-bar-stripes 1s linear infinite}@media (prefers-reduced-motion:reduce){.progress-bar-animated{-webkit-animation:none;animation:none}}.media{display:-ms-flexbox;display:flex;-ms-flex-align:start;align-items:flex-start}.media-body{-ms-flex:1;flex:1}.list-group{display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column;padding-left:0;margin-bottom:0;border-radius:.25rem}.list-group-item-action{width:100%;color:#495057;text-align:inherit}.list-group-item-action:focus,.list-group-item-action:hover{z-index:1;color:#495057;text-decoration:none;background-color:#f8f9fa}.list-group-item-action:active{color:#212529;background-color:#e9ecef}.list-group-item{position:relative;display:block;padding:.75rem 1.25rem;background-color:#fff;border:1px solid rgba(0,0,0,.125)}.list-group-item:first-child{border-top-left-radius:inherit;border-top-right-radius:inherit}.list-group-item:last-child{border-bottom-right-radius:inherit;border-bottom-left-radius:inherit}.list-group-item.disabled,.list-group-item:disabled{color:#6c757d;pointer-events:none;background-color:#fff}.list-group-item.active{z-index:2;color:#fff;background-color:#007bff;border-color:#007bff}.list-group-item+.list-group-item{border-top-width:0}.list-group-item+.list-group-item.active{margin-top:-1px;border-top-width:1px}.list-group-horizontal{-ms-flex-direction:row;flex-direction:row}.list-group-horizontal>.list-group-item:first-child{border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal>.list-group-item:last-child{border-top-right-radius:.25rem;border-bottom-left-radius:0}.list-group-horizontal>.list-group-item.active{margin-top:0}.list-group-horizontal>.list-group-item+.list-group-item{border-top-width:1px;border-left-width:0}.list-group-horizontal>.list-group-item+.list-group-item.active{margin-left:-1px;border-left-width:1px}@media (min-width:576px){.list-group-horizontal-sm{-ms-flex-direction:row;flex-direction:row}.list-group-horizontal-sm>.list-group-item:first-child{border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal-sm>.list-group-item:last-child{border-top-right-radius:.25rem;border-bottom-left-radius:0}.list-group-horizontal-sm>.list-group-item.active{margin-top:0}.list-group-horizontal-sm>.list-group-item+.list-group-item{border-top-width:1px;border-left-width:0}.list-group-horizontal-sm>.list-group-item+.list-group-item.active{margin-left:-1px;border-left-width:1px}}@media (min-width:768px){.list-group-horizontal-md{-ms-flex-direction:row;flex-direction:row}.list-group-horizontal-md>.list-group-item:first-child{border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal-md>.list-group-item:last-child{border-top-right-radius:.25rem;border-bottom-left-radius:0}.list-group-horizontal-md>.list-group-item.active{margin-top:0}.list-group-horizontal-md>.list-group-item+.list-group-item{border-top-width:1px;border-left-width:0}.list-group-horizontal-md>.list-group-item+.list-group-item.active{margin-left:-1px;border-left-width:1px}}@media (min-width:992px){.list-group-horizontal-lg{-ms-flex-direction:row;flex-direction:row}.list-group-horizontal-lg>.list-group-item:first-child{border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal-lg>.list-group-item:last-child{border-top-right-radius:.25rem;border-bottom-left-radius:0}.list-group-horizontal-lg>.list-group-item.active{margin-top:0}.list-group-horizontal-lg>.list-group-item+.list-group-item{border-top-width:1px;border-left-width:0}.list-group-horizontal-lg>.list-group-item+.list-group-item.active{margin-left:-1px;border-left-width:1px}}@media (min-width:1200px){.list-group-horizontal-xl{-ms-flex-direction:row;flex-direction:row}.list-group-horizontal-xl>.list-group-item:first-child{border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal-xl>.list-group-item:last-child{border-top-right-radius:.25rem;border-bottom-left-radius:0}.list-group-horizontal-xl>.list-group-item.active{margin-top:0}.list-group-horizontal-xl>.list-group-item+.list-group-item{border-top-width:1px;border-left-width:0}.list-group-horizontal-xl>.list-group-item+.list-group-item.active{margin-left:-1px;border-left-width:1px}}.list-group-flush{border-radius:0}.list-group-flush>.list-group-item{border-width:0 0 1px}.list-group-flush>.list-group-item:last-child{border-bottom-width:0}.list-group-item-primary{color:#004085;background-color:#b8daff}.list-group-item-primary.list-group-item-action:focus,.list-group-item-primary.list-group-item-action:hover{color:#004085;background-color:#9fcdff}.list-group-item-primary.list-group-item-action.active{color:#fff;background-color:#004085;border-color:#004085}.list-group-item-secondary{color:#383d41;background-color:#d6d8db}.list-group-item-secondary.list-group-item-action:focus,.list-group-item-secondary.list-group-item-action:hover{color:#383d41;background-color:#c8cbcf}.list-group-item-secondary.list-group-item-action.active{color:#fff;background-color:#383d41;border-color:#383d41}.list-group-item-success{color:#155724;background-color:#c3e6cb}.list-group-item-success.list-group-item-action:focus,.list-group-item-success.list-group-item-action:hover{color:#155724;background-color:#b1dfbb}.list-group-item-success.list-group-item-action.active{color:#fff;background-color:#155724;border-color:#155724}.list-group-item-info{color:#0c5460;background-color:#bee5eb}.list-group-item-info.list-group-item-action:focus,.list-group-item-info.list-group-item-action:hover{color:#0c5460;background-color:#abdde5}.list-group-item-info.list-group-item-action.active{color:#fff;background-color:#0c5460;border-color:#0c5460}.list-group-item-warning{color:#856404;background-color:#ffeeba}.list-group-item-warning.list-group-item-action:focus,.list-group-item-warning.list-group-item-action:hover{color:#856404;background-color:#ffe8a1}.list-group-item-warning.list-group-item-action.active{color:#fff;background-color:#856404;border-color:#856404}.list-group-item-danger{color:#721c24;background-color:#f5c6cb}.list-group-item-danger.list-group-item-action:focus,.list-group-item-danger.list-group-item-action:hover{color:#721c24;background-color:#f1b0b7}.list-group-item-danger.list-group-item-action.active{color:#fff;background-color:#721c24;border-color:#721c24}.list-group-item-light{color:#818182;background-color:#fdfdfe}.list-group-item-light.list-group-item-action:focus,.list-group-item-light.list-group-item-action:hover{color:#818182;background-color:#ececf6}.list-group-item-light.list-group-item-action.active{color:#fff;background-color:#818182;border-color:#818182}.list-group-item-dark{color:#1b1e21;background-color:#c6c8ca}.list-group-item-dark.list-group-item-action:focus,.list-group-item-dark.list-group-item-action:hover{color:#1b1e21;background-color:#b9bbbe}.list-group-item-dark.list-group-item-action.active{color:#fff;background-color:#1b1e21;border-color:#1b1e21}.close{float:right;font-size:1.5rem;font-weight:700;line-height:1;color:#000;text-shadow:0 1px 0 #fff;opacity:.5}.close:hover{color:#000;text-decoration:none}.close:not(:disabled):not(.disabled):focus,.close:not(:disabled):not(.disabled):hover{opacity:.75}button.close{padding:0;background-color:transparent;border:0}a.close.disabled{pointer-events:none}.toast{-ms-flex-preferred-size:350px;flex-basis:350px;max-width:350px;font-size:.875rem;background-color:rgba(255,255,255,.85);background-clip:padding-box;border:1px solid rgba(0,0,0,.1);box-shadow:0 .25rem .75rem rgba(0,0,0,.1);opacity:0;border-radius:.25rem}.toast:not(:last-child){margin-bottom:.75rem}.toast.showing{opacity:1}.toast.show{display:block;opacity:1}.toast.hide{display:none}.toast-header{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;padding:.25rem .75rem;color:#6c757d;background-color:rgba(255,255,255,.85);background-clip:padding-box;border-bottom:1px solid rgba(0,0,0,.05);border-top-left-radius:calc(.25rem - 1px);border-top-right-radius:calc(.25rem - 1px)}.toast-body{padding:.75rem}.modal-open{overflow:hidden}.modal-open .modal{overflow-x:hidden;overflow-y:auto}.modal{position:fixed;top:0;left:0;z-index:1050;display:none;width:100%;height:100%;overflow:hidden;outline:0}.modal-dialog{position:relative;width:auto;margin:.5rem;pointer-events:none}.modal.fade .modal-dialog{transition:-webkit-transform .3s ease-out;transition:transform .3s ease-out;transition:transform .3s ease-out,-webkit-transform .3s ease-out;-webkit-transform:translate(0,-50px);transform:translate(0,-50px)}@media (prefers-reduced-motion:reduce){.modal.fade .modal-dialog{transition:none}}.modal.show .modal-dialog{-webkit-transform:none;transform:none}.modal.modal-static .modal-dialog{-webkit-transform:scale(1.02);transform:scale(1.02)}.modal-dialog-scrollable{display:-ms-flexbox;display:flex;max-height:calc(100% - 1rem)}.modal-dialog-scrollable .modal-content{max-height:calc(100vh - 1rem);overflow:hidden}.modal-dialog-scrollable .modal-footer,.modal-dialog-scrollable .modal-header{-ms-flex-negative:0;flex-shrink:0}.modal-dialog-scrollable .modal-body{overflow-y:auto}.modal-dialog-centered{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;min-height:calc(100% - 1rem)}.modal-dialog-centered::before{display:block;height:calc(100vh - 1rem);height:-webkit-min-content;height:-moz-min-content;height:min-content;content:""}.modal-dialog-centered.modal-dialog-scrollable{-ms-flex-direction:column;flex-direction:column;-ms-flex-pack:center;justify-content:center;height:100%}.modal-dialog-centered.modal-dialog-scrollable .modal-content{max-height:none}.modal-dialog-centered.modal-dialog-scrollable::before{content:none}.modal-content{position:relative;display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column;width:100%;pointer-events:auto;background-color:#fff;background-clip:padding-box;border:1px solid rgba(0,0,0,.2);border-radius:.3rem;outline:0}.modal-backdrop{position:fixed;top:0;left:0;z-index:1040;width:100vw;height:100vh;background-color:#000}.modal-backdrop.fade{opacity:0}.modal-backdrop.show{opacity:.5}.modal-header{display:-ms-flexbox;display:flex;-ms-flex-align:start;align-items:flex-start;-ms-flex-pack:justify;justify-content:space-between;padding:1rem 1rem;border-bottom:1px solid #dee2e6;border-top-left-radius:calc(.3rem - 1px);border-top-right-radius:calc(.3rem - 1px)}.modal-header .close{padding:1rem 1rem;margin:-1rem -1rem -1rem auto}.modal-title{margin-bottom:0;line-height:1.5}.modal-body{position:relative;-ms-flex:1 1 auto;flex:1 1 auto;padding:1rem}.modal-footer{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;-ms-flex-align:center;align-items:center;-ms-flex-pack:end;justify-content:flex-end;padding:.75rem;border-top:1px solid #dee2e6;border-bottom-right-radius:calc(.3rem - 1px);border-bottom-left-radius:calc(.3rem - 1px)}.modal-footer>*{margin:.25rem}.modal-scrollbar-measure{position:absolute;top:-9999px;width:50px;height:50px;overflow:scroll}@media (min-width:576px){.modal-dialog{max-width:500px;margin:1.75rem auto}.modal-dialog-scrollable{max-height:calc(100% - 3.5rem)}.modal-dialog-scrollable .modal-content{max-height:calc(100vh - 3.5rem)}.modal-dialog-centered{min-height:calc(100% - 3.5rem)}.modal-dialog-centered::before{height:calc(100vh - 3.5rem);height:-webkit-min-content;height:-moz-min-content;height:min-content}.modal-sm{max-width:300px}}@media (min-width:992px){.modal-lg,.modal-xl{max-width:800px}}@media (min-width:1200px){.modal-xl{max-width:1140px}}.tooltip{position:absolute;z-index:1070;display:block;margin:0;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";font-style:normal;font-weight:400;line-height:1.5;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;white-space:normal;line-break:auto;font-size:.875rem;word-wrap:break-word;opacity:0}.tooltip.show{opacity:.9}.tooltip .arrow{position:absolute;display:block;width:.8rem;height:.4rem}.tooltip .arrow::before{position:absolute;content:"";border-color:transparent;border-style:solid}.bs-tooltip-auto[x-placement^=top],.bs-tooltip-top{padding:.4rem 0}.bs-tooltip-auto[x-placement^=top] .arrow,.bs-tooltip-top .arrow{bottom:0}.bs-tooltip-auto[x-placement^=top] .arrow::before,.bs-tooltip-top .arrow::before{top:0;border-width:.4rem .4rem 0;border-top-color:#000}.bs-tooltip-auto[x-placement^=right],.bs-tooltip-right{padding:0 .4rem}.bs-tooltip-auto[x-placement^=right] .arrow,.bs-tooltip-right .arrow{left:0;width:.4rem;height:.8rem}.bs-tooltip-auto[x-placement^=right] .arrow::before,.bs-tooltip-right .arrow::before{right:0;border-width:.4rem .4rem .4rem 0;border-right-color:#000}.bs-tooltip-auto[x-placement^=bottom],.bs-tooltip-bottom{padding:.4rem 0}.bs-tooltip-auto[x-placement^=bottom] .arrow,.bs-tooltip-bottom .arrow{top:0}.bs-tooltip-auto[x-placement^=bottom] .arrow::before,.bs-tooltip-bottom .arrow::before{bottom:0;border-width:0 .4rem .4rem;border-bottom-color:#000}.bs-tooltip-auto[x-placement^=left],.bs-tooltip-left{padding:0 .4rem}.bs-tooltip-auto[x-placement^=left] .arrow,.bs-tooltip-left .arrow{right:0;width:.4rem;height:.8rem}.bs-tooltip-auto[x-placement^=left] .arrow::before,.bs-tooltip-left .arrow::before{left:0;border-width:.4rem 0 .4rem .4rem;border-left-color:#000}.tooltip-inner{max-width:200px;padding:.25rem .5rem;color:#fff;text-align:center;background-color:#000;border-radius:.25rem}.popover{position:absolute;top:0;left:0;z-index:1060;display:block;max-width:276px;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";font-style:normal;font-weight:400;line-height:1.5;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;white-space:normal;line-break:auto;font-size:.875rem;word-wrap:break-word;background-color:#fff;background-clip:padding-box;border:1px solid rgba(0,0,0,.2);border-radius:.3rem}.popover .arrow{position:absolute;display:block;width:1rem;height:.5rem;margin:0 .3rem}.popover .arrow::after,.popover .arrow::before{position:absolute;display:block;content:"";border-color:transparent;border-style:solid}.bs-popover-auto[x-placement^=top],.bs-popover-top{margin-bottom:.5rem}.bs-popover-auto[x-placement^=top]>.arrow,.bs-popover-top>.arrow{bottom:calc(-.5rem - 1px)}.bs-popover-auto[x-placement^=top]>.arrow::before,.bs-popover-top>.arrow::before{bottom:0;border-width:.5rem .5rem 0;border-top-color:rgba(0,0,0,.25)}.bs-popover-auto[x-placement^=top]>.arrow::after,.bs-popover-top>.arrow::after{bottom:1px;border-width:.5rem .5rem 0;border-top-color:#fff}.bs-popover-auto[x-placement^=right],.bs-popover-right{margin-left:.5rem}.bs-popover-auto[x-placement^=right]>.arrow,.bs-popover-right>.arrow{left:calc(-.5rem - 1px);width:.5rem;height:1rem;margin:.3rem 0}.bs-popover-auto[x-placement^=right]>.arrow::before,.bs-popover-right>.arrow::before{left:0;border-width:.5rem .5rem .5rem 0;border-right-color:rgba(0,0,0,.25)}.bs-popover-auto[x-placement^=right]>.arrow::after,.bs-popover-right>.arrow::after{left:1px;border-width:.5rem .5rem .5rem 0;border-right-color:#fff}.bs-popover-auto[x-placement^=bottom],.bs-popover-bottom{margin-top:.5rem}.bs-popover-auto[x-placement^=bottom]>.arrow,.bs-popover-bottom>.arrow{top:calc(-.5rem - 1px)}.bs-popover-auto[x-placement^=bottom]>.arrow::before,.bs-popover-bottom>.arrow::before{top:0;border-width:0 .5rem .5rem .5rem;border-bottom-color:rgba(0,0,0,.25)}.bs-popover-auto[x-placement^=bottom]>.arrow::after,.bs-popover-bottom>.arrow::after{top:1px;border-width:0 .5rem .5rem .5rem;border-bottom-color:#fff}.bs-popover-auto[x-placement^=bottom] .popover-header::before,.bs-popover-bottom .popover-header::before{position:absolute;top:0;left:50%;display:block;width:1rem;margin-left:-.5rem;content:"";border-bottom:1px solid #f7f7f7}.bs-popover-auto[x-placement^=left],.bs-popover-left{margin-right:.5rem}.bs-popover-auto[x-placement^=left]>.arrow,.bs-popover-left>.arrow{right:calc(-.5rem - 1px);width:.5rem;height:1rem;margin:.3rem 0}.bs-popover-auto[x-placement^=left]>.arrow::before,.bs-popover-left>.arrow::before{right:0;border-width:.5rem 0 .5rem .5rem;border-left-color:rgba(0,0,0,.25)}.bs-popover-auto[x-placement^=left]>.arrow::after,.bs-popover-left>.arrow::after{right:1px;border-width:.5rem 0 .5rem .5rem;border-left-color:#fff}.popover-header{padding:.5rem .75rem;margin-bottom:0;font-size:1rem;background-color:#f7f7f7;border-bottom:1px solid #ebebeb;border-top-left-radius:calc(.3rem - 1px);border-top-right-radius:calc(.3rem - 1px)}.popover-header:empty{display:none}.popover-body{padding:.5rem .75rem;color:#212529}.carousel{position:relative}.carousel.pointer-event{-ms-touch-action:pan-y;touch-action:pan-y}.carousel-inner{position:relative;width:100%;overflow:hidden}.carousel-inner::after{display:block;clear:both;content:""}.carousel-item{position:relative;display:none;float:left;width:100%;margin-right:-100%;-webkit-backface-visibility:hidden;backface-visibility:hidden;transition:-webkit-transform .6s ease-in-out;transition:transform .6s ease-in-out;transition:transform .6s ease-in-out,-webkit-transform .6s ease-in-out}@media (prefers-reduced-motion:reduce){.carousel-item{transition:none}}.carousel-item-next,.carousel-item-prev,.carousel-item.active{display:block}.active.carousel-item-right,.carousel-item-next:not(.carousel-item-left){-webkit-transform:translateX(100%);transform:translateX(100%)}.active.carousel-item-left,.carousel-item-prev:not(.carousel-item-right){-webkit-transform:translateX(-100%);transform:translateX(-100%)}.carousel-fade .carousel-item{opacity:0;transition-property:opacity;-webkit-transform:none;transform:none}.carousel-fade .carousel-item-next.carousel-item-left,.carousel-fade .carousel-item-prev.carousel-item-right,.carousel-fade .carousel-item.active{z-index:1;opacity:1}.carousel-fade .active.carousel-item-left,.carousel-fade .active.carousel-item-right{z-index:0;opacity:0;transition:opacity 0s .6s}@media (prefers-reduced-motion:reduce){.carousel-fade .active.carousel-item-left,.carousel-fade .active.carousel-item-right{transition:none}}.carousel-control-next,.carousel-control-prev{position:absolute;top:0;bottom:0;z-index:1;display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;-ms-flex-pack:center;justify-content:center;width:15%;color:#fff;text-align:center;opacity:.5;transition:opacity .15s ease}@media (prefers-reduced-motion:reduce){.carousel-control-next,.carousel-control-prev{transition:none}}.carousel-control-next:focus,.carousel-control-next:hover,.carousel-control-prev:focus,.carousel-control-prev:hover{color:#fff;text-decoration:none;outline:0;opacity:.9}.carousel-control-prev{left:0}.carousel-control-next{right:0}.carousel-control-next-icon,.carousel-control-prev-icon{display:inline-block;width:20px;height:20px;background:no-repeat 50%/100% 100%}.carousel-control-prev-icon{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='%23fff' width='8' height='8' viewBox='0 0 8 8'%3e%3cpath d='M5.25 0l-4 4 4 4 1.5-1.5L4.25 4l2.5-2.5L5.25 0z'/%3e%3c/svg%3e")}.carousel-control-next-icon{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='%23fff' width='8' height='8' viewBox='0 0 8 8'%3e%3cpath d='M2.75 0l-1.5 1.5L3.75 4l-2.5 2.5L2.75 8l4-4-4-4z'/%3e%3c/svg%3e")}.carousel-indicators{position:absolute;right:0;bottom:0;left:0;z-index:15;display:-ms-flexbox;display:flex;-ms-flex-pack:center;justify-content:center;padding-left:0;margin-right:15%;margin-left:15%;list-style:none}.carousel-indicators li{box-sizing:content-box;-ms-flex:0 1 auto;flex:0 1 auto;width:30px;height:3px;margin-right:3px;margin-left:3px;text-indent:-999px;cursor:pointer;background-color:#fff;background-clip:padding-box;border-top:10px solid transparent;border-bottom:10px solid transparent;opacity:.5;transition:opacity .6s ease}@media (prefers-reduced-motion:reduce){.carousel-indicators li{transition:none}}.carousel-indicators .active{opacity:1}.carousel-caption{position:absolute;right:15%;bottom:20px;left:15%;z-index:10;padding-top:20px;padding-bottom:20px;color:#fff;text-align:center}@-webkit-keyframes spinner-border{to{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}@keyframes spinner-border{to{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}.spinner-border{display:inline-block;width:2rem;height:2rem;vertical-align:text-bottom;border:.25em solid currentColor;border-right-color:transparent;border-radius:50%;-webkit-animation:spinner-border .75s linear infinite;animation:spinner-border .75s linear infinite}.spinner-border-sm{width:1rem;height:1rem;border-width:.2em}@-webkit-keyframes spinner-grow{0%{-webkit-transform:scale(0);transform:scale(0)}50%{opacity:1;-webkit-transform:none;transform:none}}@keyframes spinner-grow{0%{-webkit-transform:scale(0);transform:scale(0)}50%{opacity:1;-webkit-transform:none;transform:none}}.spinner-grow{display:inline-block;width:2rem;height:2rem;vertical-align:text-bottom;background-color:currentColor;border-radius:50%;opacity:0;-webkit-animation:spinner-grow .75s linear infinite;animation:spinner-grow .75s linear infinite}.spinner-grow-sm{width:1rem;height:1rem}.align-baseline{vertical-align:baseline!important}.align-top{vertical-align:top!important}.align-middle{vertical-align:middle!important}.align-bottom{vertical-align:bottom!important}.align-text-bottom{vertical-align:text-bottom!important}.align-text-top{vertical-align:text-top!important}.bg-primary{background-color:#007bff!important}a.bg-primary:focus,a.bg-primary:hover,button.bg-primary:focus,button.bg-primary:hover{background-color:#0062cc!important}.bg-secondary{background-color:#6c757d!important}a.bg-secondary:focus,a.bg-secondary:hover,button.bg-secondary:focus,button.bg-secondary:hover{background-color:#545b62!important}.bg-success{background-color:#28a745!important}a.bg-success:focus,a.bg-success:hover,button.bg-success:focus,button.bg-success:hover{background-color:#1e7e34!important}.bg-info{background-color:#17a2b8!important}a.bg-info:focus,a.bg-info:hover,button.bg-info:focus,button.bg-info:hover{background-color:#117a8b!important}.bg-warning{background-color:#ffc107!important}a.bg-warning:focus,a.bg-warning:hover,button.bg-warning:focus,button.bg-warning:hover{background-color:#d39e00!important}.bg-danger{background-color:#dc3545!important}a.bg-danger:focus,a.bg-danger:hover,button.bg-danger:focus,button.bg-danger:hover{background-color:#bd2130!important}.bg-light{background-color:#f8f9fa!important}a.bg-light:focus,a.bg-light:hover,button.bg-light:focus,button.bg-light:hover{background-color:#dae0e5!important}.bg-dark{background-color:#343a40!important}a.bg-dark:focus,a.bg-dark:hover,button.bg-dark:focus,button.bg-dark:hover{background-color:#1d2124!important}.bg-white{background-color:#fff!important}.bg-transparent{background-color:transparent!important}.border{border:1px solid #dee2e6!important}.border-top{border-top:1px solid #dee2e6!important}.border-right{border-right:1px solid #dee2e6!important}.border-bottom{border-bottom:1px solid #dee2e6!important}.border-left{border-left:1px solid #dee2e6!important}.border-0{border:0!important}.border-top-0{border-top:0!important}.border-right-0{border-right:0!important}.border-bottom-0{border-bottom:0!important}.border-left-0{border-left:0!important}.border-primary{border-color:#007bff!important}.border-secondary{border-color:#6c757d!important}.border-success{border-color:#28a745!important}.border-info{border-color:#17a2b8!important}.border-warning{border-color:#ffc107!important}.border-danger{border-color:#dc3545!important}.border-light{border-color:#f8f9fa!important}.border-dark{border-color:#343a40!important}.border-white{border-color:#fff!important}.rounded-sm{border-radius:.2rem!important}.rounded{border-radius:.25rem!important}.rounded-top{border-top-left-radius:.25rem!important;border-top-right-radius:.25rem!important}.rounded-right{border-top-right-radius:.25rem!important;border-bottom-right-radius:.25rem!important}.rounded-bottom{border-bottom-right-radius:.25rem!important;border-bottom-left-radius:.25rem!important}.rounded-left{border-top-left-radius:.25rem!important;border-bottom-left-radius:.25rem!important}.rounded-lg{border-radius:.3rem!important}.rounded-circle{border-radius:50%!important}.rounded-pill{border-radius:50rem!important}.rounded-0{border-radius:0!important}.clearfix::after{display:block;clear:both;content:""}.d-none{display:none!important}.d-inline{display:inline!important}.d-inline-block{display:inline-block!important}.d-block{display:block!important}.d-table{display:table!important}.d-table-row{display:table-row!important}.d-table-cell{display:table-cell!important}.d-flex{display:-ms-flexbox!important;display:flex!important}.d-inline-flex{display:-ms-inline-flexbox!important;display:inline-flex!important}@media (min-width:576px){.d-sm-none{display:none!important}.d-sm-inline{display:inline!important}.d-sm-inline-block{display:inline-block!important}.d-sm-block{display:block!important}.d-sm-table{display:table!important}.d-sm-table-row{display:table-row!important}.d-sm-table-cell{display:table-cell!important}.d-sm-flex{display:-ms-flexbox!important;display:flex!important}.d-sm-inline-flex{display:-ms-inline-flexbox!important;display:inline-flex!important}}@media (min-width:768px){.d-md-none{display:none!important}.d-md-inline{display:inline!important}.d-md-inline-block{display:inline-block!important}.d-md-block{display:block!important}.d-md-table{display:table!important}.d-md-table-row{display:table-row!important}.d-md-table-cell{display:table-cell!important}.d-md-flex{display:-ms-flexbox!important;display:flex!important}.d-md-inline-flex{display:-ms-inline-flexbox!important;display:inline-flex!important}}@media (min-width:992px){.d-lg-none{display:none!important}.d-lg-inline{display:inline!important}.d-lg-inline-block{display:inline-block!important}.d-lg-block{display:block!important}.d-lg-table{display:table!important}.d-lg-table-row{display:table-row!important}.d-lg-table-cell{display:table-cell!important}.d-lg-flex{display:-ms-flexbox!important;display:flex!important}.d-lg-inline-flex{display:-ms-inline-flexbox!important;display:inline-flex!important}}@media (min-width:1200px){.d-xl-none{display:none!important}.d-xl-inline{display:inline!important}.d-xl-inline-block{display:inline-block!important}.d-xl-block{display:block!important}.d-xl-table{display:table!important}.d-xl-table-row{display:table-row!important}.d-xl-table-cell{display:table-cell!important}.d-xl-flex{display:-ms-flexbox!important;display:flex!important}.d-xl-inline-flex{display:-ms-inline-flexbox!important;display:inline-flex!important}}@media print{.d-print-none{display:none!important}.d-print-inline{display:inline!important}.d-print-inline-block{display:inline-block!important}.d-print-block{display:block!important}.d-print-table{display:table!important}.d-print-table-row{display:table-row!important}.d-print-table-cell{display:table-cell!important}.d-print-flex{display:-ms-flexbox!important;display:flex!important}.d-print-inline-flex{display:-ms-inline-flexbox!important;display:inline-flex!important}}.embed-responsive{position:relative;display:block;width:100%;padding:0;overflow:hidden}.embed-responsive::before{display:block;content:""}.embed-responsive .embed-responsive-item,.embed-responsive embed,.embed-responsive iframe,.embed-responsive object,.embed-responsive video{position:absolute;top:0;bottom:0;left:0;width:100%;height:100%;border:0}.embed-responsive-21by9::before{padding-top:42.857143%}.embed-responsive-16by9::before{padding-top:56.25%}.embed-responsive-4by3::before{padding-top:75%}.embed-responsive-1by1::before{padding-top:100%}.flex-row{-ms-flex-direction:row!important;flex-direction:row!important}.flex-column{-ms-flex-direction:column!important;flex-direction:column!important}.flex-row-reverse{-ms-flex-direction:row-reverse!important;flex-direction:row-reverse!important}.flex-column-reverse{-ms-flex-direction:column-reverse!important;flex-direction:column-reverse!important}.flex-wrap{-ms-flex-wrap:wrap!important;flex-wrap:wrap!important}.flex-nowrap{-ms-flex-wrap:nowrap!important;flex-wrap:nowrap!important}.flex-wrap-reverse{-ms-flex-wrap:wrap-reverse!important;flex-wrap:wrap-reverse!important}.flex-fill{-ms-flex:1 1 auto!important;flex:1 1 auto!important}.flex-grow-0{-ms-flex-positive:0!important;flex-grow:0!important}.flex-grow-1{-ms-flex-positive:1!important;flex-grow:1!important}.flex-shrink-0{-ms-flex-negative:0!important;flex-shrink:0!important}.flex-shrink-1{-ms-flex-negative:1!important;flex-shrink:1!important}.justify-content-start{-ms-flex-pack:start!important;justify-content:flex-start!important}.justify-content-end{-ms-flex-pack:end!important;justify-content:flex-end!important}.justify-content-center{-ms-flex-pack:center!important;justify-content:center!important}.justify-content-between{-ms-flex-pack:justify!important;justify-content:space-between!important}.justify-content-around{-ms-flex-pack:distribute!important;justify-content:space-around!important}.align-items-start{-ms-flex-align:start!important;align-items:flex-start!important}.align-items-end{-ms-flex-align:end!important;align-items:flex-end!important}.align-items-center{-ms-flex-align:center!important;align-items:center!important}.align-items-baseline{-ms-flex-align:baseline!important;align-items:baseline!important}.align-items-stretch{-ms-flex-align:stretch!important;align-items:stretch!important}.align-content-start{-ms-flex-line-pack:start!important;align-content:flex-start!important}.align-content-end{-ms-flex-line-pack:end!important;align-content:flex-end!important}.align-content-center{-ms-flex-line-pack:center!important;align-content:center!important}.align-content-between{-ms-flex-line-pack:justify!important;align-content:space-between!important}.align-content-around{-ms-flex-line-pack:distribute!important;align-content:space-around!important}.align-content-stretch{-ms-flex-line-pack:stretch!important;align-content:stretch!important}.align-self-auto{-ms-flex-item-align:auto!important;align-self:auto!important}.align-self-start{-ms-flex-item-align:start!important;align-self:flex-start!important}.align-self-end{-ms-flex-item-align:end!important;align-self:flex-end!important}.align-self-center{-ms-flex-item-align:center!important;align-self:center!important}.align-self-baseline{-ms-flex-item-align:baseline!important;align-self:baseline!important}.align-self-stretch{-ms-flex-item-align:stretch!important;align-self:stretch!important}@media (min-width:576px){.flex-sm-row{-ms-flex-direction:row!important;flex-direction:row!important}.flex-sm-column{-ms-flex-direction:column!important;flex-direction:column!important}.flex-sm-row-reverse{-ms-flex-direction:row-reverse!important;flex-direction:row-reverse!important}.flex-sm-column-reverse{-ms-flex-direction:column-reverse!important;flex-direction:column-reverse!important}.flex-sm-wrap{-ms-flex-wrap:wrap!important;flex-wrap:wrap!important}.flex-sm-nowrap{-ms-flex-wrap:nowrap!important;flex-wrap:nowrap!important}.flex-sm-wrap-reverse{-ms-flex-wrap:wrap-reverse!important;flex-wrap:wrap-reverse!important}.flex-sm-fill{-ms-flex:1 1 auto!important;flex:1 1 auto!important}.flex-sm-grow-0{-ms-flex-positive:0!important;flex-grow:0!important}.flex-sm-grow-1{-ms-flex-positive:1!important;flex-grow:1!important}.flex-sm-shrink-0{-ms-flex-negative:0!important;flex-shrink:0!important}.flex-sm-shrink-1{-ms-flex-negative:1!important;flex-shrink:1!important}.justify-content-sm-start{-ms-flex-pack:start!important;justify-content:flex-start!important}.justify-content-sm-end{-ms-flex-pack:end!important;justify-content:flex-end!important}.justify-content-sm-center{-ms-flex-pack:center!important;justify-content:center!important}.justify-content-sm-between{-ms-flex-pack:justify!important;justify-content:space-between!important}.justify-content-sm-around{-ms-flex-pack:distribute!important;justify-content:space-around!important}.align-items-sm-start{-ms-flex-align:start!important;align-items:flex-start!important}.align-items-sm-end{-ms-flex-align:end!important;align-items:flex-end!important}.align-items-sm-center{-ms-flex-align:center!important;align-items:center!important}.align-items-sm-baseline{-ms-flex-align:baseline!important;align-items:baseline!important}.align-items-sm-stretch{-ms-flex-align:stretch!important;align-items:stretch!important}.align-content-sm-start{-ms-flex-line-pack:start!important;align-content:flex-start!important}.align-content-sm-end{-ms-flex-line-pack:end!important;align-content:flex-end!important}.align-content-sm-center{-ms-flex-line-pack:center!important;align-content:center!important}.align-content-sm-between{-ms-flex-line-pack:justify!important;align-content:space-between!important}.align-content-sm-around{-ms-flex-line-pack:distribute!important;align-content:space-around!important}.align-content-sm-stretch{-ms-flex-line-pack:stretch!important;align-content:stretch!important}.align-self-sm-auto{-ms-flex-item-align:auto!important;align-self:auto!important}.align-self-sm-start{-ms-flex-item-align:start!important;align-self:flex-start!important}.align-self-sm-end{-ms-flex-item-align:end!important;align-self:flex-end!important}.align-self-sm-center{-ms-flex-item-align:center!important;align-self:center!important}.align-self-sm-baseline{-ms-flex-item-align:baseline!important;align-self:baseline!important}.align-self-sm-stretch{-ms-flex-item-align:stretch!important;align-self:stretch!important}}@media (min-width:768px){.flex-md-row{-ms-flex-direction:row!important;flex-direction:row!important}.flex-md-column{-ms-flex-direction:column!important;flex-direction:column!important}.flex-md-row-reverse{-ms-flex-direction:row-reverse!important;flex-direction:row-reverse!important}.flex-md-column-reverse{-ms-flex-direction:column-reverse!important;flex-direction:column-reverse!important}.flex-md-wrap{-ms-flex-wrap:wrap!important;flex-wrap:wrap!important}.flex-md-nowrap{-ms-flex-wrap:nowrap!important;flex-wrap:nowrap!important}.flex-md-wrap-reverse{-ms-flex-wrap:wrap-reverse!important;flex-wrap:wrap-reverse!important}.flex-md-fill{-ms-flex:1 1 auto!important;flex:1 1 auto!important}.flex-md-grow-0{-ms-flex-positive:0!important;flex-grow:0!important}.flex-md-grow-1{-ms-flex-positive:1!important;flex-grow:1!important}.flex-md-shrink-0{-ms-flex-negative:0!important;flex-shrink:0!important}.flex-md-shrink-1{-ms-flex-negative:1!important;flex-shrink:1!important}.justify-content-md-start{-ms-flex-pack:start!important;justify-content:flex-start!important}.justify-content-md-end{-ms-flex-pack:end!important;justify-content:flex-end!important}.justify-content-md-center{-ms-flex-pack:center!important;justify-content:center!important}.justify-content-md-between{-ms-flex-pack:justify!important;justify-content:space-between!important}.justify-content-md-around{-ms-flex-pack:distribute!important;justify-content:space-around!important}.align-items-md-start{-ms-flex-align:start!important;align-items:flex-start!important}.align-items-md-end{-ms-flex-align:end!important;align-items:flex-end!important}.align-items-md-center{-ms-flex-align:center!important;align-items:center!important}.align-items-md-baseline{-ms-flex-align:baseline!important;align-items:baseline!important}.align-items-md-stretch{-ms-flex-align:stretch!important;align-items:stretch!important}.align-content-md-start{-ms-flex-line-pack:start!important;align-content:flex-start!important}.align-content-md-end{-ms-flex-line-pack:end!important;align-content:flex-end!important}.align-content-md-center{-ms-flex-line-pack:center!important;align-content:center!important}.align-content-md-between{-ms-flex-line-pack:justify!important;align-content:space-between!important}.align-content-md-around{-ms-flex-line-pack:distribute!important;align-content:space-around!important}.align-content-md-stretch{-ms-flex-line-pack:stretch!important;align-content:stretch!important}.align-self-md-auto{-ms-flex-item-align:auto!important;align-self:auto!important}.align-self-md-start{-ms-flex-item-align:start!important;align-self:flex-start!important}.align-self-md-end{-ms-flex-item-align:end!important;align-self:flex-end!important}.align-self-md-center{-ms-flex-item-align:center!important;align-self:center!important}.align-self-md-baseline{-ms-flex-item-align:baseline!important;align-self:baseline!important}.align-self-md-stretch{-ms-flex-item-align:stretch!important;align-self:stretch!important}}@media (min-width:992px){.flex-lg-row{-ms-flex-direction:row!important;flex-direction:row!important}.flex-lg-column{-ms-flex-direction:column!important;flex-direction:column!important}.flex-lg-row-reverse{-ms-flex-direction:row-reverse!important;flex-direction:row-reverse!important}.flex-lg-column-reverse{-ms-flex-direction:column-reverse!important;flex-direction:column-reverse!important}.flex-lg-wrap{-ms-flex-wrap:wrap!important;flex-wrap:wrap!important}.flex-lg-nowrap{-ms-flex-wrap:nowrap!important;flex-wrap:nowrap!important}.flex-lg-wrap-reverse{-ms-flex-wrap:wrap-reverse!important;flex-wrap:wrap-reverse!important}.flex-lg-fill{-ms-flex:1 1 auto!important;flex:1 1 auto!important}.flex-lg-grow-0{-ms-flex-positive:0!important;flex-grow:0!important}.flex-lg-grow-1{-ms-flex-positive:1!important;flex-grow:1!important}.flex-lg-shrink-0{-ms-flex-negative:0!important;flex-shrink:0!important}.flex-lg-shrink-1{-ms-flex-negative:1!important;flex-shrink:1!important}.justify-content-lg-start{-ms-flex-pack:start!important;justify-content:flex-start!important}.justify-content-lg-end{-ms-flex-pack:end!important;justify-content:flex-end!important}.justify-content-lg-center{-ms-flex-pack:center!important;justify-content:center!important}.justify-content-lg-between{-ms-flex-pack:justify!important;justify-content:space-between!important}.justify-content-lg-around{-ms-flex-pack:distribute!important;justify-content:space-around!important}.align-items-lg-start{-ms-flex-align:start!important;align-items:flex-start!important}.align-items-lg-end{-ms-flex-align:end!important;align-items:flex-end!important}.align-items-lg-center{-ms-flex-align:center!important;align-items:center!important}.align-items-lg-baseline{-ms-flex-align:baseline!important;align-items:baseline!important}.align-items-lg-stretch{-ms-flex-align:stretch!important;align-items:stretch!important}.align-content-lg-start{-ms-flex-line-pack:start!important;align-content:flex-start!important}.align-content-lg-end{-ms-flex-line-pack:end!important;align-content:flex-end!important}.align-content-lg-center{-ms-flex-line-pack:center!important;align-content:center!important}.align-content-lg-between{-ms-flex-line-pack:justify!important;align-content:space-between!important}.align-content-lg-around{-ms-flex-line-pack:distribute!important;align-content:space-around!important}.align-content-lg-stretch{-ms-flex-line-pack:stretch!important;align-content:stretch!important}.align-self-lg-auto{-ms-flex-item-align:auto!important;align-self:auto!important}.align-self-lg-start{-ms-flex-item-align:start!important;align-self:flex-start!important}.align-self-lg-end{-ms-flex-item-align:end!important;align-self:flex-end!important}.align-self-lg-center{-ms-flex-item-align:center!important;align-self:center!important}.align-self-lg-baseline{-ms-flex-item-align:baseline!important;align-self:baseline!important}.align-self-lg-stretch{-ms-flex-item-align:stretch!important;align-self:stretch!important}}@media (min-width:1200px){.flex-xl-row{-ms-flex-direction:row!important;flex-direction:row!important}.flex-xl-column{-ms-flex-direction:column!important;flex-direction:column!important}.flex-xl-row-reverse{-ms-flex-direction:row-reverse!important;flex-direction:row-reverse!important}.flex-xl-column-reverse{-ms-flex-direction:column-reverse!important;flex-direction:column-reverse!important}.flex-xl-wrap{-ms-flex-wrap:wrap!important;flex-wrap:wrap!important}.flex-xl-nowrap{-ms-flex-wrap:nowrap!important;flex-wrap:nowrap!important}.flex-xl-wrap-reverse{-ms-flex-wrap:wrap-reverse!important;flex-wrap:wrap-reverse!important}.flex-xl-fill{-ms-flex:1 1 auto!important;flex:1 1 auto!important}.flex-xl-grow-0{-ms-flex-positive:0!important;flex-grow:0!important}.flex-xl-grow-1{-ms-flex-positive:1!important;flex-grow:1!important}.flex-xl-shrink-0{-ms-flex-negative:0!important;flex-shrink:0!important}.flex-xl-shrink-1{-ms-flex-negative:1!important;flex-shrink:1!important}.justify-content-xl-start{-ms-flex-pack:start!important;justify-content:flex-start!important}.justify-content-xl-end{-ms-flex-pack:end!important;justify-content:flex-end!important}.justify-content-xl-center{-ms-flex-pack:center!important;justify-content:center!important}.justify-content-xl-between{-ms-flex-pack:justify!important;justify-content:space-between!important}.justify-content-xl-around{-ms-flex-pack:distribute!important;justify-content:space-around!important}.align-items-xl-start{-ms-flex-align:start!important;align-items:flex-start!important}.align-items-xl-end{-ms-flex-align:end!important;align-items:flex-end!important}.align-items-xl-center{-ms-flex-align:center!important;align-items:center!important}.align-items-xl-baseline{-ms-flex-align:baseline!important;align-items:baseline!important}.align-items-xl-stretch{-ms-flex-align:stretch!important;align-items:stretch!important}.align-content-xl-start{-ms-flex-line-pack:start!important;align-content:flex-start!important}.align-content-xl-end{-ms-flex-line-pack:end!important;align-content:flex-end!important}.align-content-xl-center{-ms-flex-line-pack:center!important;align-content:center!important}.align-content-xl-between{-ms-flex-line-pack:justify!important;align-content:space-between!important}.align-content-xl-around{-ms-flex-line-pack:distribute!important;align-content:space-around!important}.align-content-xl-stretch{-ms-flex-line-pack:stretch!important;align-content:stretch!important}.align-self-xl-auto{-ms-flex-item-align:auto!important;align-self:auto!important}.align-self-xl-start{-ms-flex-item-align:start!important;align-self:flex-start!important}.align-self-xl-end{-ms-flex-item-align:end!important;align-self:flex-end!important}.align-self-xl-center{-ms-flex-item-align:center!important;align-self:center!important}.align-self-xl-baseline{-ms-flex-item-align:baseline!important;align-self:baseline!important}.align-self-xl-stretch{-ms-flex-item-align:stretch!important;align-self:stretch!important}}.float-left{float:left!important}.float-right{float:right!important}.float-none{float:none!important}@media (min-width:576px){.float-sm-left{float:left!important}.float-sm-right{float:right!important}.float-sm-none{float:none!important}}@media (min-width:768px){.float-md-left{float:left!important}.float-md-right{float:right!important}.float-md-none{float:none!important}}@media (min-width:992px){.float-lg-left{float:left!important}.float-lg-right{float:right!important}.float-lg-none{float:none!important}}@media (min-width:1200px){.float-xl-left{float:left!important}.float-xl-right{float:right!important}.float-xl-none{float:none!important}}.user-select-all{-webkit-user-select:all!important;-moz-user-select:all!important;-ms-user-select:all!important;user-select:all!important}.user-select-auto{-webkit-user-select:auto!important;-moz-user-select:auto!important;-ms-user-select:auto!important;user-select:auto!important}.user-select-none{-webkit-user-select:none!important;-moz-user-select:none!important;-ms-user-select:none!important;user-select:none!important}.overflow-auto{overflow:auto!important}.overflow-hidden{overflow:hidden!important}.position-static{position:static!important}.position-relative{position:relative!important}.position-absolute{position:absolute!important}.position-fixed{position:fixed!important}.position-sticky{position:-webkit-sticky!important;position:sticky!important}.fixed-top{position:fixed;top:0;right:0;left:0;z-index:1030}.fixed-bottom{position:fixed;right:0;bottom:0;left:0;z-index:1030}@supports ((position:-webkit-sticky) or (position:sticky)){.sticky-top{position:-webkit-sticky;position:sticky;top:0;z-index:1020}}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;overflow:visible;clip:auto;white-space:normal}.shadow-sm{box-shadow:0 .125rem .25rem rgba(0,0,0,.075)!important}.shadow{box-shadow:0 .5rem 1rem rgba(0,0,0,.15)!important}.shadow-lg{box-shadow:0 1rem 3rem rgba(0,0,0,.175)!important}.shadow-none{box-shadow:none!important}.w-25{width:25%!important}.w-50{width:50%!important}.w-75{width:75%!important}.w-100{width:100%!important}.w-auto{width:auto!important}.h-25{height:25%!important}.h-50{height:50%!important}.h-75{height:75%!important}.h-100{height:100%!important}.h-auto{height:auto!important}.mw-100{max-width:100%!important}.mh-100{max-height:100%!important}.min-vw-100{min-width:100vw!important}.min-vh-100{min-height:100vh!important}.vw-100{width:100vw!important}.vh-100{height:100vh!important}.m-0{margin:0!important}.mt-0,.my-0{margin-top:0!important}.mr-0,.mx-0{margin-right:0!important}.mb-0,.my-0{margin-bottom:0!important}.ml-0,.mx-0{margin-left:0!important}.m-1{margin:.25rem!important}.mt-1,.my-1{margin-top:.25rem!important}.mr-1,.mx-1{margin-right:.25rem!important}.mb-1,.my-1{margin-bottom:.25rem!important}.ml-1,.mx-1{margin-left:.25rem!important}.m-2{margin:.5rem!important}.mt-2,.my-2{margin-top:.5rem!important}.mr-2,.mx-2{margin-right:.5rem!important}.mb-2,.my-2{margin-bottom:.5rem!important}.ml-2,.mx-2{margin-left:.5rem!important}.m-3{margin:1rem!important}.mt-3,.my-3{margin-top:1rem!important}.mr-3,.mx-3{margin-right:1rem!important}.mb-3,.my-3{margin-bottom:1rem!important}.ml-3,.mx-3{margin-left:1rem!important}.m-4{margin:1.5rem!important}.mt-4,.my-4{margin-top:1.5rem!important}.mr-4,.mx-4{margin-right:1.5rem!important}.mb-4,.my-4{margin-bottom:1.5rem!important}.ml-4,.mx-4{margin-left:1.5rem!important}.m-5{margin:3rem!important}.mt-5,.my-5{margin-top:3rem!important}.mr-5,.mx-5{margin-right:3rem!important}.mb-5,.my-5{margin-bottom:3rem!important}.ml-5,.mx-5{margin-left:3rem!important}.p-0{padding:0!important}.pt-0,.py-0{padding-top:0!important}.pr-0,.px-0{padding-right:0!important}.pb-0,.py-0{padding-bottom:0!important}.pl-0,.px-0{padding-left:0!important}.p-1{padding:.25rem!important}.pt-1,.py-1{padding-top:.25rem!important}.pr-1,.px-1{padding-right:.25rem!important}.pb-1,.py-1{padding-bottom:.25rem!important}.pl-1,.px-1{padding-left:.25rem!important}.p-2{padding:.5rem!important}.pt-2,.py-2{padding-top:.5rem!important}.pr-2,.px-2{padding-right:.5rem!important}.pb-2,.py-2{padding-bottom:.5rem!important}.pl-2,.px-2{padding-left:.5rem!important}.p-3{padding:1rem!important}.pt-3,.py-3{padding-top:1rem!important}.pr-3,.px-3{padding-right:1rem!important}.pb-3,.py-3{padding-bottom:1rem!important}.pl-3,.px-3{padding-left:1rem!important}.p-4{padding:1.5rem!important}.pt-4,.py-4{padding-top:1.5rem!important}.pr-4,.px-4{padding-right:1.5rem!important}.pb-4,.py-4{padding-bottom:1.5rem!important}.pl-4,.px-4{padding-left:1.5rem!important}.p-5{padding:3rem!important}.pt-5,.py-5{padding-top:3rem!important}.pr-5,.px-5{padding-right:3rem!important}.pb-5,.py-5{padding-bottom:3rem!important}.pl-5,.px-5{padding-left:3rem!important}.m-n1{margin:-.25rem!important}.mt-n1,.my-n1{margin-top:-.25rem!important}.mr-n1,.mx-n1{margin-right:-.25rem!important}.mb-n1,.my-n1{margin-bottom:-.25rem!important}.ml-n1,.mx-n1{margin-left:-.25rem!important}.m-n2{margin:-.5rem!important}.mt-n2,.my-n2{margin-top:-.5rem!important}.mr-n2,.mx-n2{margin-right:-.5rem!important}.mb-n2,.my-n2{margin-bottom:-.5rem!important}.ml-n2,.mx-n2{margin-left:-.5rem!important}.m-n3{margin:-1rem!important}.mt-n3,.my-n3{margin-top:-1rem!important}.mr-n3,.mx-n3{margin-right:-1rem!important}.mb-n3,.my-n3{margin-bottom:-1rem!important}.ml-n3,.mx-n3{margin-left:-1rem!important}.m-n4{margin:-1.5rem!important}.mt-n4,.my-n4{margin-top:-1.5rem!important}.mr-n4,.mx-n4{margin-right:-1.5rem!important}.mb-n4,.my-n4{margin-bottom:-1.5rem!important}.ml-n4,.mx-n4{margin-left:-1.5rem!important}.m-n5{margin:-3rem!important}.mt-n5,.my-n5{margin-top:-3rem!important}.mr-n5,.mx-n5{margin-right:-3rem!important}.mb-n5,.my-n5{margin-bottom:-3rem!important}.ml-n5,.mx-n5{margin-left:-3rem!important}.m-auto{margin:auto!important}.mt-auto,.my-auto{margin-top:auto!important}.mr-auto,.mx-auto{margin-right:auto!important}.mb-auto,.my-auto{margin-bottom:auto!important}.ml-auto,.mx-auto{margin-left:auto!important}@media (min-width:576px){.m-sm-0{margin:0!important}.mt-sm-0,.my-sm-0{margin-top:0!important}.mr-sm-0,.mx-sm-0{margin-right:0!important}.mb-sm-0,.my-sm-0{margin-bottom:0!important}.ml-sm-0,.mx-sm-0{margin-left:0!important}.m-sm-1{margin:.25rem!important}.mt-sm-1,.my-sm-1{margin-top:.25rem!important}.mr-sm-1,.mx-sm-1{margin-right:.25rem!important}.mb-sm-1,.my-sm-1{margin-bottom:.25rem!important}.ml-sm-1,.mx-sm-1{margin-left:.25rem!important}.m-sm-2{margin:.5rem!important}.mt-sm-2,.my-sm-2{margin-top:.5rem!important}.mr-sm-2,.mx-sm-2{margin-right:.5rem!important}.mb-sm-2,.my-sm-2{margin-bottom:.5rem!important}.ml-sm-2,.mx-sm-2{margin-left:.5rem!important}.m-sm-3{margin:1rem!important}.mt-sm-3,.my-sm-3{margin-top:1rem!important}.mr-sm-3,.mx-sm-3{margin-right:1rem!important}.mb-sm-3,.my-sm-3{margin-bottom:1rem!important}.ml-sm-3,.mx-sm-3{margin-left:1rem!important}.m-sm-4{margin:1.5rem!important}.mt-sm-4,.my-sm-4{margin-top:1.5rem!important}.mr-sm-4,.mx-sm-4{margin-right:1.5rem!important}.mb-sm-4,.my-sm-4{margin-bottom:1.5rem!important}.ml-sm-4,.mx-sm-4{margin-left:1.5rem!important}.m-sm-5{margin:3rem!important}.mt-sm-5,.my-sm-5{margin-top:3rem!important}.mr-sm-5,.mx-sm-5{margin-right:3rem!important}.mb-sm-5,.my-sm-5{margin-bottom:3rem!important}.ml-sm-5,.mx-sm-5{margin-left:3rem!important}.p-sm-0{padding:0!important}.pt-sm-0,.py-sm-0{padding-top:0!important}.pr-sm-0,.px-sm-0{padding-right:0!important}.pb-sm-0,.py-sm-0{padding-bottom:0!important}.pl-sm-0,.px-sm-0{padding-left:0!important}.p-sm-1{padding:.25rem!important}.pt-sm-1,.py-sm-1{padding-top:.25rem!important}.pr-sm-1,.px-sm-1{padding-right:.25rem!important}.pb-sm-1,.py-sm-1{padding-bottom:.25rem!important}.pl-sm-1,.px-sm-1{padding-left:.25rem!important}.p-sm-2{padding:.5rem!important}.pt-sm-2,.py-sm-2{padding-top:.5rem!important}.pr-sm-2,.px-sm-2{padding-right:.5rem!important}.pb-sm-2,.py-sm-2{padding-bottom:.5rem!important}.pl-sm-2,.px-sm-2{padding-left:.5rem!important}.p-sm-3{padding:1rem!important}.pt-sm-3,.py-sm-3{padding-top:1rem!important}.pr-sm-3,.px-sm-3{padding-right:1rem!important}.pb-sm-3,.py-sm-3{padding-bottom:1rem!important}.pl-sm-3,.px-sm-3{padding-left:1rem!important}.p-sm-4{padding:1.5rem!important}.pt-sm-4,.py-sm-4{padding-top:1.5rem!important}.pr-sm-4,.px-sm-4{padding-right:1.5rem!important}.pb-sm-4,.py-sm-4{padding-bottom:1.5rem!important}.pl-sm-4,.px-sm-4{padding-left:1.5rem!important}.p-sm-5{padding:3rem!important}.pt-sm-5,.py-sm-5{padding-top:3rem!important}.pr-sm-5,.px-sm-5{padding-right:3rem!important}.pb-sm-5,.py-sm-5{padding-bottom:3rem!important}.pl-sm-5,.px-sm-5{padding-left:3rem!important}.m-sm-n1{margin:-.25rem!important}.mt-sm-n1,.my-sm-n1{margin-top:-.25rem!important}.mr-sm-n1,.mx-sm-n1{margin-right:-.25rem!important}.mb-sm-n1,.my-sm-n1{margin-bottom:-.25rem!important}.ml-sm-n1,.mx-sm-n1{margin-left:-.25rem!important}.m-sm-n2{margin:-.5rem!important}.mt-sm-n2,.my-sm-n2{margin-top:-.5rem!important}.mr-sm-n2,.mx-sm-n2{margin-right:-.5rem!important}.mb-sm-n2,.my-sm-n2{margin-bottom:-.5rem!important}.ml-sm-n2,.mx-sm-n2{margin-left:-.5rem!important}.m-sm-n3{margin:-1rem!important}.mt-sm-n3,.my-sm-n3{margin-top:-1rem!important}.mr-sm-n3,.mx-sm-n3{margin-right:-1rem!important}.mb-sm-n3,.my-sm-n3{margin-bottom:-1rem!important}.ml-sm-n3,.mx-sm-n3{margin-left:-1rem!important}.m-sm-n4{margin:-1.5rem!important}.mt-sm-n4,.my-sm-n4{margin-top:-1.5rem!important}.mr-sm-n4,.mx-sm-n4{margin-right:-1.5rem!important}.mb-sm-n4,.my-sm-n4{margin-bottom:-1.5rem!important}.ml-sm-n4,.mx-sm-n4{margin-left:-1.5rem!important}.m-sm-n5{margin:-3rem!important}.mt-sm-n5,.my-sm-n5{margin-top:-3rem!important}.mr-sm-n5,.mx-sm-n5{margin-right:-3rem!important}.mb-sm-n5,.my-sm-n5{margin-bottom:-3rem!important}.ml-sm-n5,.mx-sm-n5{margin-left:-3rem!important}.m-sm-auto{margin:auto!important}.mt-sm-auto,.my-sm-auto{margin-top:auto!important}.mr-sm-auto,.mx-sm-auto{margin-right:auto!important}.mb-sm-auto,.my-sm-auto{margin-bottom:auto!important}.ml-sm-auto,.mx-sm-auto{margin-left:auto!important}}@media (min-width:768px){.m-md-0{margin:0!important}.mt-md-0,.my-md-0{margin-top:0!important}.mr-md-0,.mx-md-0{margin-right:0!important}.mb-md-0,.my-md-0{margin-bottom:0!important}.ml-md-0,.mx-md-0{margin-left:0!important}.m-md-1{margin:.25rem!important}.mt-md-1,.my-md-1{margin-top:.25rem!important}.mr-md-1,.mx-md-1{margin-right:.25rem!important}.mb-md-1,.my-md-1{margin-bottom:.25rem!important}.ml-md-1,.mx-md-1{margin-left:.25rem!important}.m-md-2{margin:.5rem!important}.mt-md-2,.my-md-2{margin-top:.5rem!important}.mr-md-2,.mx-md-2{margin-right:.5rem!important}.mb-md-2,.my-md-2{margin-bottom:.5rem!important}.ml-md-2,.mx-md-2{margin-left:.5rem!important}.m-md-3{margin:1rem!important}.mt-md-3,.my-md-3{margin-top:1rem!important}.mr-md-3,.mx-md-3{margin-right:1rem!important}.mb-md-3,.my-md-3{margin-bottom:1rem!important}.ml-md-3,.mx-md-3{margin-left:1rem!important}.m-md-4{margin:1.5rem!important}.mt-md-4,.my-md-4{margin-top:1.5rem!important}.mr-md-4,.mx-md-4{margin-right:1.5rem!important}.mb-md-4,.my-md-4{margin-bottom:1.5rem!important}.ml-md-4,.mx-md-4{margin-left:1.5rem!important}.m-md-5{margin:3rem!important}.mt-md-5,.my-md-5{margin-top:3rem!important}.mr-md-5,.mx-md-5{margin-right:3rem!important}.mb-md-5,.my-md-5{margin-bottom:3rem!important}.ml-md-5,.mx-md-5{margin-left:3rem!important}.p-md-0{padding:0!important}.pt-md-0,.py-md-0{padding-top:0!important}.pr-md-0,.px-md-0{padding-right:0!important}.pb-md-0,.py-md-0{padding-bottom:0!important}.pl-md-0,.px-md-0{padding-left:0!important}.p-md-1{padding:.25rem!important}.pt-md-1,.py-md-1{padding-top:.25rem!important}.pr-md-1,.px-md-1{padding-right:.25rem!important}.pb-md-1,.py-md-1{padding-bottom:.25rem!important}.pl-md-1,.px-md-1{padding-left:.25rem!important}.p-md-2{padding:.5rem!important}.pt-md-2,.py-md-2{padding-top:.5rem!important}.pr-md-2,.px-md-2{padding-right:.5rem!important}.pb-md-2,.py-md-2{padding-bottom:.5rem!important}.pl-md-2,.px-md-2{padding-left:.5rem!important}.p-md-3{padding:1rem!important}.pt-md-3,.py-md-3{padding-top:1rem!important}.pr-md-3,.px-md-3{padding-right:1rem!important}.pb-md-3,.py-md-3{padding-bottom:1rem!important}.pl-md-3,.px-md-3{padding-left:1rem!important}.p-md-4{padding:1.5rem!important}.pt-md-4,.py-md-4{padding-top:1.5rem!important}.pr-md-4,.px-md-4{padding-right:1.5rem!important}.pb-md-4,.py-md-4{padding-bottom:1.5rem!important}.pl-md-4,.px-md-4{padding-left:1.5rem!important}.p-md-5{padding:3rem!important}.pt-md-5,.py-md-5{padding-top:3rem!important}.pr-md-5,.px-md-5{padding-right:3rem!important}.pb-md-5,.py-md-5{padding-bottom:3rem!important}.pl-md-5,.px-md-5{padding-left:3rem!important}.m-md-n1{margin:-.25rem!important}.mt-md-n1,.my-md-n1{margin-top:-.25rem!important}.mr-md-n1,.mx-md-n1{margin-right:-.25rem!important}.mb-md-n1,.my-md-n1{margin-bottom:-.25rem!important}.ml-md-n1,.mx-md-n1{margin-left:-.25rem!important}.m-md-n2{margin:-.5rem!important}.mt-md-n2,.my-md-n2{margin-top:-.5rem!important}.mr-md-n2,.mx-md-n2{margin-right:-.5rem!important}.mb-md-n2,.my-md-n2{margin-bottom:-.5rem!important}.ml-md-n2,.mx-md-n2{margin-left:-.5rem!important}.m-md-n3{margin:-1rem!important}.mt-md-n3,.my-md-n3{margin-top:-1rem!important}.mr-md-n3,.mx-md-n3{margin-right:-1rem!important}.mb-md-n3,.my-md-n3{margin-bottom:-1rem!important}.ml-md-n3,.mx-md-n3{margin-left:-1rem!important}.m-md-n4{margin:-1.5rem!important}.mt-md-n4,.my-md-n4{margin-top:-1.5rem!important}.mr-md-n4,.mx-md-n4{margin-right:-1.5rem!important}.mb-md-n4,.my-md-n4{margin-bottom:-1.5rem!important}.ml-md-n4,.mx-md-n4{margin-left:-1.5rem!important}.m-md-n5{margin:-3rem!important}.mt-md-n5,.my-md-n5{margin-top:-3rem!important}.mr-md-n5,.mx-md-n5{margin-right:-3rem!important}.mb-md-n5,.my-md-n5{margin-bottom:-3rem!important}.ml-md-n5,.mx-md-n5{margin-left:-3rem!important}.m-md-auto{margin:auto!important}.mt-md-auto,.my-md-auto{margin-top:auto!important}.mr-md-auto,.mx-md-auto{margin-right:auto!important}.mb-md-auto,.my-md-auto{margin-bottom:auto!important}.ml-md-auto,.mx-md-auto{margin-left:auto!important}}@media (min-width:992px){.m-lg-0{margin:0!important}.mt-lg-0,.my-lg-0{margin-top:0!important}.mr-lg-0,.mx-lg-0{margin-right:0!important}.mb-lg-0,.my-lg-0{margin-bottom:0!important}.ml-lg-0,.mx-lg-0{margin-left:0!important}.m-lg-1{margin:.25rem!important}.mt-lg-1,.my-lg-1{margin-top:.25rem!important}.mr-lg-1,.mx-lg-1{margin-right:.25rem!important}.mb-lg-1,.my-lg-1{margin-bottom:.25rem!important}.ml-lg-1,.mx-lg-1{margin-left:.25rem!important}.m-lg-2{margin:.5rem!important}.mt-lg-2,.my-lg-2{margin-top:.5rem!important}.mr-lg-2,.mx-lg-2{margin-right:.5rem!important}.mb-lg-2,.my-lg-2{margin-bottom:.5rem!important}.ml-lg-2,.mx-lg-2{margin-left:.5rem!important}.m-lg-3{margin:1rem!important}.mt-lg-3,.my-lg-3{margin-top:1rem!important}.mr-lg-3,.mx-lg-3{margin-right:1rem!important}.mb-lg-3,.my-lg-3{margin-bottom:1rem!important}.ml-lg-3,.mx-lg-3{margin-left:1rem!important}.m-lg-4{margin:1.5rem!important}.mt-lg-4,.my-lg-4{margin-top:1.5rem!important}.mr-lg-4,.mx-lg-4{margin-right:1.5rem!important}.mb-lg-4,.my-lg-4{margin-bottom:1.5rem!important}.ml-lg-4,.mx-lg-4{margin-left:1.5rem!important}.m-lg-5{margin:3rem!important}.mt-lg-5,.my-lg-5{margin-top:3rem!important}.mr-lg-5,.mx-lg-5{margin-right:3rem!important}.mb-lg-5,.my-lg-5{margin-bottom:3rem!important}.ml-lg-5,.mx-lg-5{margin-left:3rem!important}.p-lg-0{padding:0!important}.pt-lg-0,.py-lg-0{padding-top:0!important}.pr-lg-0,.px-lg-0{padding-right:0!important}.pb-lg-0,.py-lg-0{padding-bottom:0!important}.pl-lg-0,.px-lg-0{padding-left:0!important}.p-lg-1{padding:.25rem!important}.pt-lg-1,.py-lg-1{padding-top:.25rem!important}.pr-lg-1,.px-lg-1{padding-right:.25rem!important}.pb-lg-1,.py-lg-1{padding-bottom:.25rem!important}.pl-lg-1,.px-lg-1{padding-left:.25rem!important}.p-lg-2{padding:.5rem!important}.pt-lg-2,.py-lg-2{padding-top:.5rem!important}.pr-lg-2,.px-lg-2{padding-right:.5rem!important}.pb-lg-2,.py-lg-2{padding-bottom:.5rem!important}.pl-lg-2,.px-lg-2{padding-left:.5rem!important}.p-lg-3{padding:1rem!important}.pt-lg-3,.py-lg-3{padding-top:1rem!important}.pr-lg-3,.px-lg-3{padding-right:1rem!important}.pb-lg-3,.py-lg-3{padding-bottom:1rem!important}.pl-lg-3,.px-lg-3{padding-left:1rem!important}.p-lg-4{padding:1.5rem!important}.pt-lg-4,.py-lg-4{padding-top:1.5rem!important}.pr-lg-4,.px-lg-4{padding-right:1.5rem!important}.pb-lg-4,.py-lg-4{padding-bottom:1.5rem!important}.pl-lg-4,.px-lg-4{padding-left:1.5rem!important}.p-lg-5{padding:3rem!important}.pt-lg-5,.py-lg-5{padding-top:3rem!important}.pr-lg-5,.px-lg-5{padding-right:3rem!important}.pb-lg-5,.py-lg-5{padding-bottom:3rem!important}.pl-lg-5,.px-lg-5{padding-left:3rem!important}.m-lg-n1{margin:-.25rem!important}.mt-lg-n1,.my-lg-n1{margin-top:-.25rem!important}.mr-lg-n1,.mx-lg-n1{margin-right:-.25rem!important}.mb-lg-n1,.my-lg-n1{margin-bottom:-.25rem!important}.ml-lg-n1,.mx-lg-n1{margin-left:-.25rem!important}.m-lg-n2{margin:-.5rem!important}.mt-lg-n2,.my-lg-n2{margin-top:-.5rem!important}.mr-lg-n2,.mx-lg-n2{margin-right:-.5rem!important}.mb-lg-n2,.my-lg-n2{margin-bottom:-.5rem!important}.ml-lg-n2,.mx-lg-n2{margin-left:-.5rem!important}.m-lg-n3{margin:-1rem!important}.mt-lg-n3,.my-lg-n3{margin-top:-1rem!important}.mr-lg-n3,.mx-lg-n3{margin-right:-1rem!important}.mb-lg-n3,.my-lg-n3{margin-bottom:-1rem!important}.ml-lg-n3,.mx-lg-n3{margin-left:-1rem!important}.m-lg-n4{margin:-1.5rem!important}.mt-lg-n4,.my-lg-n4{margin-top:-1.5rem!important}.mr-lg-n4,.mx-lg-n4{margin-right:-1.5rem!important}.mb-lg-n4,.my-lg-n4{margin-bottom:-1.5rem!important}.ml-lg-n4,.mx-lg-n4{margin-left:-1.5rem!important}.m-lg-n5{margin:-3rem!important}.mt-lg-n5,.my-lg-n5{margin-top:-3rem!important}.mr-lg-n5,.mx-lg-n5{margin-right:-3rem!important}.mb-lg-n5,.my-lg-n5{margin-bottom:-3rem!important}.ml-lg-n5,.mx-lg-n5{margin-left:-3rem!important}.m-lg-auto{margin:auto!important}.mt-lg-auto,.my-lg-auto{margin-top:auto!important}.mr-lg-auto,.mx-lg-auto{margin-right:auto!important}.mb-lg-auto,.my-lg-auto{margin-bottom:auto!important}.ml-lg-auto,.mx-lg-auto{margin-left:auto!important}}@media (min-width:1200px){.m-xl-0{margin:0!important}.mt-xl-0,.my-xl-0{margin-top:0!important}.mr-xl-0,.mx-xl-0{margin-right:0!important}.mb-xl-0,.my-xl-0{margin-bottom:0!important}.ml-xl-0,.mx-xl-0{margin-left:0!important}.m-xl-1{margin:.25rem!important}.mt-xl-1,.my-xl-1{margin-top:.25rem!important}.mr-xl-1,.mx-xl-1{margin-right:.25rem!important}.mb-xl-1,.my-xl-1{margin-bottom:.25rem!important}.ml-xl-1,.mx-xl-1{margin-left:.25rem!important}.m-xl-2{margin:.5rem!important}.mt-xl-2,.my-xl-2{margin-top:.5rem!important}.mr-xl-2,.mx-xl-2{margin-right:.5rem!important}.mb-xl-2,.my-xl-2{margin-bottom:.5rem!important}.ml-xl-2,.mx-xl-2{margin-left:.5rem!important}.m-xl-3{margin:1rem!important}.mt-xl-3,.my-xl-3{margin-top:1rem!important}.mr-xl-3,.mx-xl-3{margin-right:1rem!important}.mb-xl-3,.my-xl-3{margin-bottom:1rem!important}.ml-xl-3,.mx-xl-3{margin-left:1rem!important}.m-xl-4{margin:1.5rem!important}.mt-xl-4,.my-xl-4{margin-top:1.5rem!important}.mr-xl-4,.mx-xl-4{margin-right:1.5rem!important}.mb-xl-4,.my-xl-4{margin-bottom:1.5rem!important}.ml-xl-4,.mx-xl-4{margin-left:1.5rem!important}.m-xl-5{margin:3rem!important}.mt-xl-5,.my-xl-5{margin-top:3rem!important}.mr-xl-5,.mx-xl-5{margin-right:3rem!important}.mb-xl-5,.my-xl-5{margin-bottom:3rem!important}.ml-xl-5,.mx-xl-5{margin-left:3rem!important}.p-xl-0{padding:0!important}.pt-xl-0,.py-xl-0{padding-top:0!important}.pr-xl-0,.px-xl-0{padding-right:0!important}.pb-xl-0,.py-xl-0{padding-bottom:0!important}.pl-xl-0,.px-xl-0{padding-left:0!important}.p-xl-1{padding:.25rem!important}.pt-xl-1,.py-xl-1{padding-top:.25rem!important}.pr-xl-1,.px-xl-1{padding-right:.25rem!important}.pb-xl-1,.py-xl-1{padding-bottom:.25rem!important}.pl-xl-1,.px-xl-1{padding-left:.25rem!important}.p-xl-2{padding:.5rem!important}.pt-xl-2,.py-xl-2{padding-top:.5rem!important}.pr-xl-2,.px-xl-2{padding-right:.5rem!important}.pb-xl-2,.py-xl-2{padding-bottom:.5rem!important}.pl-xl-2,.px-xl-2{padding-left:.5rem!important}.p-xl-3{padding:1rem!important}.pt-xl-3,.py-xl-3{padding-top:1rem!important}.pr-xl-3,.px-xl-3{padding-right:1rem!important}.pb-xl-3,.py-xl-3{padding-bottom:1rem!important}.pl-xl-3,.px-xl-3{padding-left:1rem!important}.p-xl-4{padding:1.5rem!important}.pt-xl-4,.py-xl-4{padding-top:1.5rem!important}.pr-xl-4,.px-xl-4{padding-right:1.5rem!important}.pb-xl-4,.py-xl-4{padding-bottom:1.5rem!important}.pl-xl-4,.px-xl-4{padding-left:1.5rem!important}.p-xl-5{padding:3rem!important}.pt-xl-5,.py-xl-5{padding-top:3rem!important}.pr-xl-5,.px-xl-5{padding-right:3rem!important}.pb-xl-5,.py-xl-5{padding-bottom:3rem!important}.pl-xl-5,.px-xl-5{padding-left:3rem!important}.m-xl-n1{margin:-.25rem!important}.mt-xl-n1,.my-xl-n1{margin-top:-.25rem!important}.mr-xl-n1,.mx-xl-n1{margin-right:-.25rem!important}.mb-xl-n1,.my-xl-n1{margin-bottom:-.25rem!important}.ml-xl-n1,.mx-xl-n1{margin-left:-.25rem!important}.m-xl-n2{margin:-.5rem!important}.mt-xl-n2,.my-xl-n2{margin-top:-.5rem!important}.mr-xl-n2,.mx-xl-n2{margin-right:-.5rem!important}.mb-xl-n2,.my-xl-n2{margin-bottom:-.5rem!important}.ml-xl-n2,.mx-xl-n2{margin-left:-.5rem!important}.m-xl-n3{margin:-1rem!important}.mt-xl-n3,.my-xl-n3{margin-top:-1rem!important}.mr-xl-n3,.mx-xl-n3{margin-right:-1rem!important}.mb-xl-n3,.my-xl-n3{margin-bottom:-1rem!important}.ml-xl-n3,.mx-xl-n3{margin-left:-1rem!important}.m-xl-n4{margin:-1.5rem!important}.mt-xl-n4,.my-xl-n4{margin-top:-1.5rem!important}.mr-xl-n4,.mx-xl-n4{margin-right:-1.5rem!important}.mb-xl-n4,.my-xl-n4{margin-bottom:-1.5rem!important}.ml-xl-n4,.mx-xl-n4{margin-left:-1.5rem!important}.m-xl-n5{margin:-3rem!important}.mt-xl-n5,.my-xl-n5{margin-top:-3rem!important}.mr-xl-n5,.mx-xl-n5{margin-right:-3rem!important}.mb-xl-n5,.my-xl-n5{margin-bottom:-3rem!important}.ml-xl-n5,.mx-xl-n5{margin-left:-3rem!important}.m-xl-auto{margin:auto!important}.mt-xl-auto,.my-xl-auto{margin-top:auto!important}.mr-xl-auto,.mx-xl-auto{margin-right:auto!important}.mb-xl-auto,.my-xl-auto{margin-bottom:auto!important}.ml-xl-auto,.mx-xl-auto{margin-left:auto!important}}.stretched-link::after{position:absolute;top:0;right:0;bottom:0;left:0;z-index:1;pointer-events:auto;content:"";background-color:rgba(0,0,0,0)}.text-monospace{font-family:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace!important}.text-justify{text-align:justify!important}.text-wrap{white-space:normal!important}.text-nowrap{white-space:nowrap!important}.text-truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.text-left{text-align:left!important}.text-right{text-align:right!important}.text-center{text-align:center!important}@media (min-width:576px){.text-sm-left{text-align:left!important}.text-sm-right{text-align:right!important}.text-sm-center{text-align:center!important}}@media (min-width:768px){.text-md-left{text-align:left!important}.text-md-right{text-align:right!important}.text-md-center{text-align:center!important}}@media (min-width:992px){.text-lg-left{text-align:left!important}.text-lg-right{text-align:right!important}.text-lg-center{text-align:center!important}}@media (min-width:1200px){.text-xl-left{text-align:left!important}.text-xl-right{text-align:right!important}.text-xl-center{text-align:center!important}}.text-lowercase{text-transform:lowercase!important}.text-uppercase{text-transform:uppercase!important}.text-capitalize{text-transform:capitalize!important}.font-weight-light{font-weight:300!important}.font-weight-lighter{font-weight:lighter!important}.font-weight-normal{font-weight:400!important}.font-weight-bold{font-weight:700!important}.font-weight-bolder{font-weight:bolder!important}.font-italic{font-style:italic!important}.text-white{color:#fff!important}.text-primary{color:#007bff!important}a.text-primary:focus,a.text-primary:hover{color:#0056b3!important}.text-secondary{color:#6c757d!important}a.text-secondary:focus,a.text-secondary:hover{color:#494f54!important}.text-success{color:#28a745!important}a.text-success:focus,a.text-success:hover{color:#19692c!important}.text-info{color:#17a2b8!important}a.text-info:focus,a.text-info:hover{color:#0f6674!important}.text-warning{color:#ffc107!important}a.text-warning:focus,a.text-warning:hover{color:#ba8b00!important}.text-danger{color:#dc3545!important}a.text-danger:focus,a.text-danger:hover{color:#a71d2a!important}.text-light{color:#f8f9fa!important}a.text-light:focus,a.text-light:hover{color:#cbd3da!important}.text-dark{color:#343a40!important}a.text-dark:focus,a.text-dark:hover{color:#121416!important}.text-body{color:#212529!important}.text-muted{color:#6c757d!important}.text-black-50{color:rgba(0,0,0,.5)!important}.text-white-50{color:rgba(255,255,255,.5)!important}.text-hide{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.text-decoration-none{text-decoration:none!important}.text-break{word-break:break-word!important;word-wrap:break-word!important}.text-reset{color:inherit!important}.visible{visibility:visible!important}.invisible{visibility:hidden!important}@media print{*,::after,::before{text-shadow:none!important;box-shadow:none!important}a:not(.btn){text-decoration:underline}abbr[title]::after{content:" (" attr(title) ")"}pre{white-space:pre-wrap!important}blockquote,pre{border:1px solid #adb5bd;page-break-inside:avoid}thead{display:table-header-group}img,tr{page-break-inside:avoid}h2,h3,p{orphans:3;widows:3}h2,h3{page-break-after:avoid}@page{size:a3}body{min-width:992px!important}.container{min-width:992px!important}.navbar{display:none}.badge{border:1px solid #000}.table{border-collapse:collapse!important}.table td,.table th{background-color:#fff!important}.table-bordered td,.table-bordered th{border:1px solid #dee2e6!important}.table-dark{color:inherit}.table-dark tbody+tbody,.table-dark td,.table-dark th,.table-dark thead th{border-color:#dee2e6}.table .thead-dark th{color:inherit;border-color:#dee2e6}}
|
7 |
-
/*# sourceMappingURL=bootstrap.min.css.map */
|
|
|
|
|
|
|
|
|
|
|
|
|
|
resources/css/{mainwp.css → mainwp-extension.css}
RENAMED
File without changes
|
resources/css/plugin.css
CHANGED
@@ -762,7 +762,7 @@ th.column-trans {
|
|
762 |
width: 114px;
|
763 |
}
|
764 |
th.column-request_info {
|
765 |
-
width:
|
766 |
}
|
767 |
/** INSIGHTS **/
|
768 |
/* TABLES */
|
762 |
width: 114px;
|
763 |
}
|
764 |
th.column-request_info {
|
765 |
+
width: 120px;
|
766 |
}
|
767 |
/** INSIGHTS **/
|
768 |
/* TABLES */
|
resources/images/shield/background-blob.svg
ADDED
@@ -0,0 +1,18 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<svg xmlns="http://www.w3.org/2000/svg"
|
2 |
+
width="943.431" height="890.376" viewBox="0 0 943.431 890.376"
|
3 |
+
style="transform: scale(0.9) translateX(10%);"
|
4 |
+
class="w-full h-full md:h-64 lg:h-full lg:mt-0 mt-12">
|
5 |
+
<g transform="translate(-643.895 11)">
|
6 |
+
<path style="fill:#5c9d04;opacity:.35;"
|
7 |
+
d="M555.3,327.441c-43.408,77.248-113.273,9.045-242.451,24.411S94.4,458.988,38.594,388.9-22.334,158.029,89.639,71.518,370.069-21.118,486.485,42.862,598.712,250.194,555.3,327.441Z"
|
8 |
+
transform="matrix(-0.966, 0.259, -0.259, -0.966, 1494.893, 663.611)" />
|
9 |
+
<path style="opacity:0.178;fill:#5c9d04;"
|
10 |
+
d="M33,467.787C84.03,578.144,166.168,480.709,318.039,502.66s256.832,153.056,322.439,52.933,71.631-329.832-60.013-453.422S250.772-30.171,113.905,61.233-18.037,357.43,33,467.787Z"
|
11 |
+
transform="translate(1587.326 285.412) rotate(120)" />
|
12 |
+
<path style="opacity:0.351;fill:#5c9d04;"
|
13 |
+
d="M555.3,116.48C511.9,13.675,442.031,104.442,312.853,83.992S94.4-58.588,38.594,34.682-22.334,341.94,89.639,457.073s280.429,123.284,396.846,38.137S598.712,219.284,555.3,116.48Z"
|
14 |
+
transform="matrix(0.966, 0.259, -0.259, 0.966, 786.828, 94.786)" />
|
15 |
+
<g transform="translate(844 262.148)" class="hidden lg:block">
|
16 |
+
</g>
|
17 |
+
</g>
|
18 |
+
</svg>
|
resources/images/shield/dash-background.jpg
DELETED
Binary file
|
resources/js/bootstrap4.bundle.min.js
DELETED
@@ -1,7 +0,0 @@
|
|
1 |
-
/*!
|
2 |
-
* Bootstrap v4.5.3 (https://getbootstrap.com/)
|
3 |
-
* Copyright 2011-2020 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)
|
4 |
-
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
|
5 |
-
*/
|
6 |
-
!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?e(exports,require("jquery")):"function"==typeof define&&define.amd?define(["exports","jquery"],e):e((t="undefined"!=typeof globalThis?globalThis:t||self).bootstrap={},t.jQuery)}(this,(function(t,e){"use strict";function n(t){return t&&"object"==typeof t&&"default"in t?t:{default:t}}var i=n(e);function o(t,e){for(var n=0;n<e.length;n++){var i=e[n];i.enumerable=i.enumerable||!1,i.configurable=!0,"value"in i&&(i.writable=!0),Object.defineProperty(t,i.key,i)}}function r(t,e,n){return e&&o(t.prototype,e),n&&o(t,n),t}function a(){return(a=Object.assign||function(t){for(var e=1;e<arguments.length;e++){var n=arguments[e];for(var i in n)Object.prototype.hasOwnProperty.call(n,i)&&(t[i]=n[i])}return t}).apply(this,arguments)}function s(t){var e=this,n=!1;return i.default(this).one(l.TRANSITION_END,(function(){n=!0})),setTimeout((function(){n||l.triggerTransitionEnd(e)}),t),this}var l={TRANSITION_END:"bsTransitionEnd",getUID:function(t){do{t+=~~(1e6*Math.random())}while(document.getElementById(t));return t},getSelectorFromElement:function(t){var e=t.getAttribute("data-target");if(!e||"#"===e){var n=t.getAttribute("href");e=n&&"#"!==n?n.trim():""}try{return document.querySelector(e)?e:null}catch(t){return null}},getTransitionDurationFromElement:function(t){if(!t)return 0;var e=i.default(t).css("transition-duration"),n=i.default(t).css("transition-delay"),o=parseFloat(e),r=parseFloat(n);return o||r?(e=e.split(",")[0],n=n.split(",")[0],1e3*(parseFloat(e)+parseFloat(n))):0},reflow:function(t){return t.offsetHeight},triggerTransitionEnd:function(t){i.default(t).trigger("transitionend")},supportsTransitionEnd:function(){return Boolean("transitionend")},isElement:function(t){return(t[0]||t).nodeType},typeCheckConfig:function(t,e,n){for(var i in n)if(Object.prototype.hasOwnProperty.call(n,i)){var o=n[i],r=e[i],a=r&&l.isElement(r)?"element":null===(s=r)||"undefined"==typeof s?""+s:{}.toString.call(s).match(/\s([a-z]+)/i)[1].toLowerCase();if(!new RegExp(o).test(a))throw new Error(t.toUpperCase()+': Option "'+i+'" provided type "'+a+'" but expected type "'+o+'".')}var s},findShadowRoot:function(t){if(!document.documentElement.attachShadow)return null;if("function"==typeof t.getRootNode){var e=t.getRootNode();return e instanceof ShadowRoot?e:null}return t instanceof ShadowRoot?t:t.parentNode?l.findShadowRoot(t.parentNode):null},jQueryDetection:function(){if("undefined"==typeof i.default)throw new TypeError("Bootstrap's JavaScript requires jQuery. jQuery must be included before Bootstrap's JavaScript.");var t=i.default.fn.jquery.split(" ")[0].split(".");if(t[0]<2&&t[1]<9||1===t[0]&&9===t[1]&&t[2]<1||t[0]>=4)throw new Error("Bootstrap's JavaScript requires at least jQuery v1.9.1 but less than v4.0.0")}};l.jQueryDetection(),i.default.fn.emulateTransitionEnd=s,i.default.event.special[l.TRANSITION_END]={bindType:"transitionend",delegateType:"transitionend",handle:function(t){if(i.default(t.target).is(this))return t.handleObj.handler.apply(this,arguments)}};var u="alert",f=i.default.fn[u],d=function(){function t(t){this._element=t}var e=t.prototype;return e.close=function(t){var e=this._element;t&&(e=this._getRootElement(t)),this._triggerCloseEvent(e).isDefaultPrevented()||this._removeElement(e)},e.dispose=function(){i.default.removeData(this._element,"bs.alert"),this._element=null},e._getRootElement=function(t){var e=l.getSelectorFromElement(t),n=!1;return e&&(n=document.querySelector(e)),n||(n=i.default(t).closest(".alert")[0]),n},e._triggerCloseEvent=function(t){var e=i.default.Event("close.bs.alert");return i.default(t).trigger(e),e},e._removeElement=function(t){var e=this;if(i.default(t).removeClass("show"),i.default(t).hasClass("fade")){var n=l.getTransitionDurationFromElement(t);i.default(t).one(l.TRANSITION_END,(function(n){return e._destroyElement(t,n)})).emulateTransitionEnd(n)}else this._destroyElement(t)},e._destroyElement=function(t){i.default(t).detach().trigger("closed.bs.alert").remove()},t._jQueryInterface=function(e){return this.each((function(){var n=i.default(this),o=n.data("bs.alert");o||(o=new t(this),n.data("bs.alert",o)),"close"===e&&o[e](this)}))},t._handleDismiss=function(t){return function(e){e&&e.preventDefault(),t.close(this)}},r(t,null,[{key:"VERSION",get:function(){return"4.5.3"}}]),t}();i.default(document).on("click.bs.alert.data-api",'[data-dismiss="alert"]',d._handleDismiss(new d)),i.default.fn[u]=d._jQueryInterface,i.default.fn[u].Constructor=d,i.default.fn[u].noConflict=function(){return i.default.fn[u]=f,d._jQueryInterface};var c=i.default.fn.button,h=function(){function t(t){this._element=t,this.shouldAvoidTriggerChange=!1}var e=t.prototype;return e.toggle=function(){var t=!0,e=!0,n=i.default(this._element).closest('[data-toggle="buttons"]')[0];if(n){var o=this._element.querySelector('input:not([type="hidden"])');if(o){if("radio"===o.type)if(o.checked&&this._element.classList.contains("active"))t=!1;else{var r=n.querySelector(".active");r&&i.default(r).removeClass("active")}t&&("checkbox"!==o.type&&"radio"!==o.type||(o.checked=!this._element.classList.contains("active")),this.shouldAvoidTriggerChange||i.default(o).trigger("change")),o.focus(),e=!1}}this._element.hasAttribute("disabled")||this._element.classList.contains("disabled")||(e&&this._element.setAttribute("aria-pressed",!this._element.classList.contains("active")),t&&i.default(this._element).toggleClass("active"))},e.dispose=function(){i.default.removeData(this._element,"bs.button"),this._element=null},t._jQueryInterface=function(e,n){return this.each((function(){var o=i.default(this),r=o.data("bs.button");r||(r=new t(this),o.data("bs.button",r)),r.shouldAvoidTriggerChange=n,"toggle"===e&&r[e]()}))},r(t,null,[{key:"VERSION",get:function(){return"4.5.3"}}]),t}();i.default(document).on("click.bs.button.data-api",'[data-toggle^="button"]',(function(t){var e=t.target,n=e;if(i.default(e).hasClass("btn")||(e=i.default(e).closest(".btn")[0]),!e||e.hasAttribute("disabled")||e.classList.contains("disabled"))t.preventDefault();else{var o=e.querySelector('input:not([type="hidden"])');if(o&&(o.hasAttribute("disabled")||o.classList.contains("disabled")))return void t.preventDefault();"INPUT"!==n.tagName&&"LABEL"===e.tagName||h._jQueryInterface.call(i.default(e),"toggle","INPUT"===n.tagName)}})).on("focus.bs.button.data-api blur.bs.button.data-api",'[data-toggle^="button"]',(function(t){var e=i.default(t.target).closest(".btn")[0];i.default(e).toggleClass("focus",/^focus(in)?$/.test(t.type))})),i.default(window).on("load.bs.button.data-api",(function(){for(var t=[].slice.call(document.querySelectorAll('[data-toggle="buttons"] .btn')),e=0,n=t.length;e<n;e++){var i=t[e],o=i.querySelector('input:not([type="hidden"])');o.checked||o.hasAttribute("checked")?i.classList.add("active"):i.classList.remove("active")}for(var r=0,a=(t=[].slice.call(document.querySelectorAll('[data-toggle="button"]'))).length;r<a;r++){var s=t[r];"true"===s.getAttribute("aria-pressed")?s.classList.add("active"):s.classList.remove("active")}})),i.default.fn.button=h._jQueryInterface,i.default.fn.button.Constructor=h,i.default.fn.button.noConflict=function(){return i.default.fn.button=c,h._jQueryInterface};var p="carousel",m=".bs.carousel",g=i.default.fn[p],v={interval:5e3,keyboard:!0,slide:!1,pause:"hover",wrap:!0,touch:!0},_={interval:"(number|boolean)",keyboard:"boolean",slide:"(boolean|string)",pause:"(string|boolean)",wrap:"boolean",touch:"boolean"},b={TOUCH:"touch",PEN:"pen"},y=function(){function t(t,e){this._items=null,this._interval=null,this._activeElement=null,this._isPaused=!1,this._isSliding=!1,this.touchTimeout=null,this.touchStartX=0,this.touchDeltaX=0,this._config=this._getConfig(e),this._element=t,this._indicatorsElement=this._element.querySelector(".carousel-indicators"),this._touchSupported="ontouchstart"in document.documentElement||navigator.maxTouchPoints>0,this._pointerEvent=Boolean(window.PointerEvent||window.MSPointerEvent),this._addEventListeners()}var e=t.prototype;return e.next=function(){this._isSliding||this._slide("next")},e.nextWhenVisible=function(){var t=i.default(this._element);!document.hidden&&t.is(":visible")&&"hidden"!==t.css("visibility")&&this.next()},e.prev=function(){this._isSliding||this._slide("prev")},e.pause=function(t){t||(this._isPaused=!0),this._element.querySelector(".carousel-item-next, .carousel-item-prev")&&(l.triggerTransitionEnd(this._element),this.cycle(!0)),clearInterval(this._interval),this._interval=null},e.cycle=function(t){t||(this._isPaused=!1),this._interval&&(clearInterval(this._interval),this._interval=null),this._config.interval&&!this._isPaused&&(this._interval=setInterval((document.visibilityState?this.nextWhenVisible:this.next).bind(this),this._config.interval))},e.to=function(t){var e=this;this._activeElement=this._element.querySelector(".active.carousel-item");var n=this._getItemIndex(this._activeElement);if(!(t>this._items.length-1||t<0))if(this._isSliding)i.default(this._element).one("slid.bs.carousel",(function(){return e.to(t)}));else{if(n===t)return this.pause(),void this.cycle();var o=t>n?"next":"prev";this._slide(o,this._items[t])}},e.dispose=function(){i.default(this._element).off(m),i.default.removeData(this._element,"bs.carousel"),this._items=null,this._config=null,this._element=null,this._interval=null,this._isPaused=null,this._isSliding=null,this._activeElement=null,this._indicatorsElement=null},e._getConfig=function(t){return t=a({},v,t),l.typeCheckConfig(p,t,_),t},e._handleSwipe=function(){var t=Math.abs(this.touchDeltaX);if(!(t<=40)){var e=t/this.touchDeltaX;this.touchDeltaX=0,e>0&&this.prev(),e<0&&this.next()}},e._addEventListeners=function(){var t=this;this._config.keyboard&&i.default(this._element).on("keydown.bs.carousel",(function(e){return t._keydown(e)})),"hover"===this._config.pause&&i.default(this._element).on("mouseenter.bs.carousel",(function(e){return t.pause(e)})).on("mouseleave.bs.carousel",(function(e){return t.cycle(e)})),this._config.touch&&this._addTouchEventListeners()},e._addTouchEventListeners=function(){var t=this;if(this._touchSupported){var e=function(e){t._pointerEvent&&b[e.originalEvent.pointerType.toUpperCase()]?t.touchStartX=e.originalEvent.clientX:t._pointerEvent||(t.touchStartX=e.originalEvent.touches[0].clientX)},n=function(e){t._pointerEvent&&b[e.originalEvent.pointerType.toUpperCase()]&&(t.touchDeltaX=e.originalEvent.clientX-t.touchStartX),t._handleSwipe(),"hover"===t._config.pause&&(t.pause(),t.touchTimeout&&clearTimeout(t.touchTimeout),t.touchTimeout=setTimeout((function(e){return t.cycle(e)}),500+t._config.interval))};i.default(this._element.querySelectorAll(".carousel-item img")).on("dragstart.bs.carousel",(function(t){return t.preventDefault()})),this._pointerEvent?(i.default(this._element).on("pointerdown.bs.carousel",(function(t){return e(t)})),i.default(this._element).on("pointerup.bs.carousel",(function(t){return n(t)})),this._element.classList.add("pointer-event")):(i.default(this._element).on("touchstart.bs.carousel",(function(t){return e(t)})),i.default(this._element).on("touchmove.bs.carousel",(function(e){return function(e){e.originalEvent.touches&&e.originalEvent.touches.length>1?t.touchDeltaX=0:t.touchDeltaX=e.originalEvent.touches[0].clientX-t.touchStartX}(e)})),i.default(this._element).on("touchend.bs.carousel",(function(t){return n(t)})))}},e._keydown=function(t){if(!/input|textarea/i.test(t.target.tagName))switch(t.which){case 37:t.preventDefault(),this.prev();break;case 39:t.preventDefault(),this.next()}},e._getItemIndex=function(t){return this._items=t&&t.parentNode?[].slice.call(t.parentNode.querySelectorAll(".carousel-item")):[],this._items.indexOf(t)},e._getItemByDirection=function(t,e){var n="next"===t,i="prev"===t,o=this._getItemIndex(e),r=this._items.length-1;if((i&&0===o||n&&o===r)&&!this._config.wrap)return e;var a=(o+("prev"===t?-1:1))%this._items.length;return-1===a?this._items[this._items.length-1]:this._items[a]},e._triggerSlideEvent=function(t,e){var n=this._getItemIndex(t),o=this._getItemIndex(this._element.querySelector(".active.carousel-item")),r=i.default.Event("slide.bs.carousel",{relatedTarget:t,direction:e,from:o,to:n});return i.default(this._element).trigger(r),r},e._setActiveIndicatorElement=function(t){if(this._indicatorsElement){var e=[].slice.call(this._indicatorsElement.querySelectorAll(".active"));i.default(e).removeClass("active");var n=this._indicatorsElement.children[this._getItemIndex(t)];n&&i.default(n).addClass("active")}},e._slide=function(t,e){var n,o,r,a=this,s=this._element.querySelector(".active.carousel-item"),u=this._getItemIndex(s),f=e||s&&this._getItemByDirection(t,s),d=this._getItemIndex(f),c=Boolean(this._interval);if("next"===t?(n="carousel-item-left",o="carousel-item-next",r="left"):(n="carousel-item-right",o="carousel-item-prev",r="right"),f&&i.default(f).hasClass("active"))this._isSliding=!1;else if(!this._triggerSlideEvent(f,r).isDefaultPrevented()&&s&&f){this._isSliding=!0,c&&this.pause(),this._setActiveIndicatorElement(f);var h=i.default.Event("slid.bs.carousel",{relatedTarget:f,direction:r,from:u,to:d});if(i.default(this._element).hasClass("slide")){i.default(f).addClass(o),l.reflow(f),i.default(s).addClass(n),i.default(f).addClass(n);var p=parseInt(f.getAttribute("data-interval"),10);p?(this._config.defaultInterval=this._config.defaultInterval||this._config.interval,this._config.interval=p):this._config.interval=this._config.defaultInterval||this._config.interval;var m=l.getTransitionDurationFromElement(s);i.default(s).one(l.TRANSITION_END,(function(){i.default(f).removeClass(n+" "+o).addClass("active"),i.default(s).removeClass("active "+o+" "+n),a._isSliding=!1,setTimeout((function(){return i.default(a._element).trigger(h)}),0)})).emulateTransitionEnd(m)}else i.default(s).removeClass("active"),i.default(f).addClass("active"),this._isSliding=!1,i.default(this._element).trigger(h);c&&this.cycle()}},t._jQueryInterface=function(e){return this.each((function(){var n=i.default(this).data("bs.carousel"),o=a({},v,i.default(this).data());"object"==typeof e&&(o=a({},o,e));var r="string"==typeof e?e:o.slide;if(n||(n=new t(this,o),i.default(this).data("bs.carousel",n)),"number"==typeof e)n.to(e);else if("string"==typeof r){if("undefined"==typeof n[r])throw new TypeError('No method named "'+r+'"');n[r]()}else o.interval&&o.ride&&(n.pause(),n.cycle())}))},t._dataApiClickHandler=function(e){var n=l.getSelectorFromElement(this);if(n){var o=i.default(n)[0];if(o&&i.default(o).hasClass("carousel")){var r=a({},i.default(o).data(),i.default(this).data()),s=this.getAttribute("data-slide-to");s&&(r.interval=!1),t._jQueryInterface.call(i.default(o),r),s&&i.default(o).data("bs.carousel").to(s),e.preventDefault()}}},r(t,null,[{key:"VERSION",get:function(){return"4.5.3"}},{key:"Default",get:function(){return v}}]),t}();i.default(document).on("click.bs.carousel.data-api","[data-slide], [data-slide-to]",y._dataApiClickHandler),i.default(window).on("load.bs.carousel.data-api",(function(){for(var t=[].slice.call(document.querySelectorAll('[data-ride="carousel"]')),e=0,n=t.length;e<n;e++){var o=i.default(t[e]);y._jQueryInterface.call(o,o.data())}})),i.default.fn[p]=y._jQueryInterface,i.default.fn[p].Constructor=y,i.default.fn[p].noConflict=function(){return i.default.fn[p]=g,y._jQueryInterface};var w="collapse",E=i.default.fn[w],T={toggle:!0,parent:""},C={toggle:"boolean",parent:"(string|element)"},S=function(){function t(t,e){this._isTransitioning=!1,this._element=t,this._config=this._getConfig(e),this._triggerArray=[].slice.call(document.querySelectorAll('[data-toggle="collapse"][href="#'+t.id+'"],[data-toggle="collapse"][data-target="#'+t.id+'"]'));for(var n=[].slice.call(document.querySelectorAll('[data-toggle="collapse"]')),i=0,o=n.length;i<o;i++){var r=n[i],a=l.getSelectorFromElement(r),s=[].slice.call(document.querySelectorAll(a)).filter((function(e){return e===t}));null!==a&&s.length>0&&(this._selector=a,this._triggerArray.push(r))}this._parent=this._config.parent?this._getParent():null,this._config.parent||this._addAriaAndCollapsedClass(this._element,this._triggerArray),this._config.toggle&&this.toggle()}var e=t.prototype;return e.toggle=function(){i.default(this._element).hasClass("show")?this.hide():this.show()},e.show=function(){var e,n,o=this;if(!this._isTransitioning&&!i.default(this._element).hasClass("show")&&(this._parent&&0===(e=[].slice.call(this._parent.querySelectorAll(".show, .collapsing")).filter((function(t){return"string"==typeof o._config.parent?t.getAttribute("data-parent")===o._config.parent:t.classList.contains("collapse")}))).length&&(e=null),!(e&&(n=i.default(e).not(this._selector).data("bs.collapse"))&&n._isTransitioning))){var r=i.default.Event("show.bs.collapse");if(i.default(this._element).trigger(r),!r.isDefaultPrevented()){e&&(t._jQueryInterface.call(i.default(e).not(this._selector),"hide"),n||i.default(e).data("bs.collapse",null));var a=this._getDimension();i.default(this._element).removeClass("collapse").addClass("collapsing"),this._element.style[a]=0,this._triggerArray.length&&i.default(this._triggerArray).removeClass("collapsed").attr("aria-expanded",!0),this.setTransitioning(!0);var s="scroll"+(a[0].toUpperCase()+a.slice(1)),u=l.getTransitionDurationFromElement(this._element);i.default(this._element).one(l.TRANSITION_END,(function(){i.default(o._element).removeClass("collapsing").addClass("collapse show"),o._element.style[a]="",o.setTransitioning(!1),i.default(o._element).trigger("shown.bs.collapse")})).emulateTransitionEnd(u),this._element.style[a]=this._element[s]+"px"}}},e.hide=function(){var t=this;if(!this._isTransitioning&&i.default(this._element).hasClass("show")){var e=i.default.Event("hide.bs.collapse");if(i.default(this._element).trigger(e),!e.isDefaultPrevented()){var n=this._getDimension();this._element.style[n]=this._element.getBoundingClientRect()[n]+"px",l.reflow(this._element),i.default(this._element).addClass("collapsing").removeClass("collapse show");var o=this._triggerArray.length;if(o>0)for(var r=0;r<o;r++){var a=this._triggerArray[r],s=l.getSelectorFromElement(a);if(null!==s)i.default([].slice.call(document.querySelectorAll(s))).hasClass("show")||i.default(a).addClass("collapsed").attr("aria-expanded",!1)}this.setTransitioning(!0);this._element.style[n]="";var u=l.getTransitionDurationFromElement(this._element);i.default(this._element).one(l.TRANSITION_END,(function(){t.setTransitioning(!1),i.default(t._element).removeClass("collapsing").addClass("collapse").trigger("hidden.bs.collapse")})).emulateTransitionEnd(u)}}},e.setTransitioning=function(t){this._isTransitioning=t},e.dispose=function(){i.default.removeData(this._element,"bs.collapse"),this._config=null,this._parent=null,this._element=null,this._triggerArray=null,this._isTransitioning=null},e._getConfig=function(t){return(t=a({},T,t)).toggle=Boolean(t.toggle),l.typeCheckConfig(w,t,C),t},e._getDimension=function(){return i.default(this._element).hasClass("width")?"width":"height"},e._getParent=function(){var e,n=this;l.isElement(this._config.parent)?(e=this._config.parent,"undefined"!=typeof this._config.parent.jquery&&(e=this._config.parent[0])):e=document.querySelector(this._config.parent);var o='[data-toggle="collapse"][data-parent="'+this._config.parent+'"]',r=[].slice.call(e.querySelectorAll(o));return i.default(r).each((function(e,i){n._addAriaAndCollapsedClass(t._getTargetFromElement(i),[i])})),e},e._addAriaAndCollapsedClass=function(t,e){var n=i.default(t).hasClass("show");e.length&&i.default(e).toggleClass("collapsed",!n).attr("aria-expanded",n)},t._getTargetFromElement=function(t){var e=l.getSelectorFromElement(t);return e?document.querySelector(e):null},t._jQueryInterface=function(e){return this.each((function(){var n=i.default(this),o=n.data("bs.collapse"),r=a({},T,n.data(),"object"==typeof e&&e?e:{});if(!o&&r.toggle&&"string"==typeof e&&/show|hide/.test(e)&&(r.toggle=!1),o||(o=new t(this,r),n.data("bs.collapse",o)),"string"==typeof e){if("undefined"==typeof o[e])throw new TypeError('No method named "'+e+'"');o[e]()}}))},r(t,null,[{key:"VERSION",get:function(){return"4.5.3"}},{key:"Default",get:function(){return T}}]),t}();i.default(document).on("click.bs.collapse.data-api",'[data-toggle="collapse"]',(function(t){"A"===t.currentTarget.tagName&&t.preventDefault();var e=i.default(this),n=l.getSelectorFromElement(this),o=[].slice.call(document.querySelectorAll(n));i.default(o).each((function(){var t=i.default(this),n=t.data("bs.collapse")?"toggle":e.data();S._jQueryInterface.call(t,n)}))})),i.default.fn[w]=S._jQueryInterface,i.default.fn[w].Constructor=S,i.default.fn[w].noConflict=function(){return i.default.fn[w]=E,S._jQueryInterface};var D="undefined"!=typeof window&&"undefined"!=typeof document&&"undefined"!=typeof navigator,N=function(){for(var t=["Edge","Trident","Firefox"],e=0;e<t.length;e+=1)if(D&&navigator.userAgent.indexOf(t[e])>=0)return 1;return 0}();var k=D&&window.Promise?function(t){var e=!1;return function(){e||(e=!0,window.Promise.resolve().then((function(){e=!1,t()})))}}:function(t){var e=!1;return function(){e||(e=!0,setTimeout((function(){e=!1,t()}),N))}};function A(t){return t&&"[object Function]"==={}.toString.call(t)}function I(t,e){if(1!==t.nodeType)return[];var n=t.ownerDocument.defaultView.getComputedStyle(t,null);return e?n[e]:n}function O(t){return"HTML"===t.nodeName?t:t.parentNode||t.host}function x(t){if(!t)return document.body;switch(t.nodeName){case"HTML":case"BODY":return t.ownerDocument.body;case"#document":return t.body}var e=I(t),n=e.overflow,i=e.overflowX,o=e.overflowY;return/(auto|scroll|overlay)/.test(n+o+i)?t:x(O(t))}function j(t){return t&&t.referenceNode?t.referenceNode:t}var L=D&&!(!window.MSInputMethodContext||!document.documentMode),P=D&&/MSIE 10/.test(navigator.userAgent);function F(t){return 11===t?L:10===t?P:L||P}function R(t){if(!t)return document.documentElement;for(var e=F(10)?document.body:null,n=t.offsetParent||null;n===e&&t.nextElementSibling;)n=(t=t.nextElementSibling).offsetParent;var i=n&&n.nodeName;return i&&"BODY"!==i&&"HTML"!==i?-1!==["TH","TD","TABLE"].indexOf(n.nodeName)&&"static"===I(n,"position")?R(n):n:t?t.ownerDocument.documentElement:document.documentElement}function H(t){return null!==t.parentNode?H(t.parentNode):t}function M(t,e){if(!(t&&t.nodeType&&e&&e.nodeType))return document.documentElement;var n=t.compareDocumentPosition(e)&Node.DOCUMENT_POSITION_FOLLOWING,i=n?t:e,o=n?e:t,r=document.createRange();r.setStart(i,0),r.setEnd(o,0);var a,s,l=r.commonAncestorContainer;if(t!==l&&e!==l||i.contains(o))return"BODY"===(s=(a=l).nodeName)||"HTML"!==s&&R(a.firstElementChild)!==a?R(l):l;var u=H(t);return u.host?M(u.host,e):M(t,H(e).host)}function B(t){var e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"top",n="top"===e?"scrollTop":"scrollLeft",i=t.nodeName;if("BODY"===i||"HTML"===i){var o=t.ownerDocument.documentElement,r=t.ownerDocument.scrollingElement||o;return r[n]}return t[n]}function q(t,e){var n=arguments.length>2&&void 0!==arguments[2]&&arguments[2],i=B(e,"top"),o=B(e,"left"),r=n?-1:1;return t.top+=i*r,t.bottom+=i*r,t.left+=o*r,t.right+=o*r,t}function Q(t,e){var n="x"===e?"Left":"Top",i="Left"===n?"Right":"Bottom";return parseFloat(t["border"+n+"Width"])+parseFloat(t["border"+i+"Width"])}function W(t,e,n,i){return Math.max(e["offset"+t],e["scroll"+t],n["client"+t],n["offset"+t],n["scroll"+t],F(10)?parseInt(n["offset"+t])+parseInt(i["margin"+("Height"===t?"Top":"Left")])+parseInt(i["margin"+("Height"===t?"Bottom":"Right")]):0)}function U(t){var e=t.body,n=t.documentElement,i=F(10)&&getComputedStyle(n);return{height:W("Height",e,n,i),width:W("Width",e,n,i)}}var V=function(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")},Y=function(){function t(t,e){for(var n=0;n<e.length;n++){var i=e[n];i.enumerable=i.enumerable||!1,i.configurable=!0,"value"in i&&(i.writable=!0),Object.defineProperty(t,i.key,i)}}return function(e,n,i){return n&&t(e.prototype,n),i&&t(e,i),e}}(),z=function(t,e,n){return e in t?Object.defineProperty(t,e,{value:n,enumerable:!0,configurable:!0,writable:!0}):t[e]=n,t},X=Object.assign||function(t){for(var e=1;e<arguments.length;e++){var n=arguments[e];for(var i in n)Object.prototype.hasOwnProperty.call(n,i)&&(t[i]=n[i])}return t};function K(t){return X({},t,{right:t.left+t.width,bottom:t.top+t.height})}function G(t){var e={};try{if(F(10)){e=t.getBoundingClientRect();var n=B(t,"top"),i=B(t,"left");e.top+=n,e.left+=i,e.bottom+=n,e.right+=i}else e=t.getBoundingClientRect()}catch(t){}var o={left:e.left,top:e.top,width:e.right-e.left,height:e.bottom-e.top},r="HTML"===t.nodeName?U(t.ownerDocument):{},a=r.width||t.clientWidth||o.width,s=r.height||t.clientHeight||o.height,l=t.offsetWidth-a,u=t.offsetHeight-s;if(l||u){var f=I(t);l-=Q(f,"x"),u-=Q(f,"y"),o.width-=l,o.height-=u}return K(o)}function $(t,e){var n=arguments.length>2&&void 0!==arguments[2]&&arguments[2],i=F(10),o="HTML"===e.nodeName,r=G(t),a=G(e),s=x(t),l=I(e),u=parseFloat(l.borderTopWidth),f=parseFloat(l.borderLeftWidth);n&&o&&(a.top=Math.max(a.top,0),a.left=Math.max(a.left,0));var d=K({top:r.top-a.top-u,left:r.left-a.left-f,width:r.width,height:r.height});if(d.marginTop=0,d.marginLeft=0,!i&&o){var c=parseFloat(l.marginTop),h=parseFloat(l.marginLeft);d.top-=u-c,d.bottom-=u-c,d.left-=f-h,d.right-=f-h,d.marginTop=c,d.marginLeft=h}return(i&&!n?e.contains(s):e===s&&"BODY"!==s.nodeName)&&(d=q(d,e)),d}function J(t){var e=arguments.length>1&&void 0!==arguments[1]&&arguments[1],n=t.ownerDocument.documentElement,i=$(t,n),o=Math.max(n.clientWidth,window.innerWidth||0),r=Math.max(n.clientHeight,window.innerHeight||0),a=e?0:B(n),s=e?0:B(n,"left"),l={top:a-i.top+i.marginTop,left:s-i.left+i.marginLeft,width:o,height:r};return K(l)}function Z(t){var e=t.nodeName;if("BODY"===e||"HTML"===e)return!1;if("fixed"===I(t,"position"))return!0;var n=O(t);return!!n&&Z(n)}function tt(t){if(!t||!t.parentElement||F())return document.documentElement;for(var e=t.parentElement;e&&"none"===I(e,"transform");)e=e.parentElement;return e||document.documentElement}function et(t,e,n,i){var o=arguments.length>4&&void 0!==arguments[4]&&arguments[4],r={top:0,left:0},a=o?tt(t):M(t,j(e));if("viewport"===i)r=J(a,o);else{var s=void 0;"scrollParent"===i?"BODY"===(s=x(O(e))).nodeName&&(s=t.ownerDocument.documentElement):s="window"===i?t.ownerDocument.documentElement:i;var l=$(s,a,o);if("HTML"!==s.nodeName||Z(a))r=l;else{var u=U(t.ownerDocument),f=u.height,d=u.width;r.top+=l.top-l.marginTop,r.bottom=f+l.top,r.left+=l.left-l.marginLeft,r.right=d+l.left}}var c="number"==typeof(n=n||0);return r.left+=c?n:n.left||0,r.top+=c?n:n.top||0,r.right-=c?n:n.right||0,r.bottom-=c?n:n.bottom||0,r}function nt(t){return t.width*t.height}function it(t,e,n,i,o){var r=arguments.length>5&&void 0!==arguments[5]?arguments[5]:0;if(-1===t.indexOf("auto"))return t;var a=et(n,i,r,o),s={top:{width:a.width,height:e.top-a.top},right:{width:a.right-e.right,height:a.height},bottom:{width:a.width,height:a.bottom-e.bottom},left:{width:e.left-a.left,height:a.height}},l=Object.keys(s).map((function(t){return X({key:t},s[t],{area:nt(s[t])})})).sort((function(t,e){return e.area-t.area})),u=l.filter((function(t){var e=t.width,i=t.height;return e>=n.clientWidth&&i>=n.clientHeight})),f=u.length>0?u[0].key:l[0].key,d=t.split("-")[1];return f+(d?"-"+d:"")}function ot(t,e,n){var i=arguments.length>3&&void 0!==arguments[3]?arguments[3]:null,o=i?tt(e):M(e,j(n));return $(n,o,i)}function rt(t){var e=t.ownerDocument.defaultView.getComputedStyle(t),n=parseFloat(e.marginTop||0)+parseFloat(e.marginBottom||0),i=parseFloat(e.marginLeft||0)+parseFloat(e.marginRight||0);return{width:t.offsetWidth+i,height:t.offsetHeight+n}}function at(t){var e={left:"right",right:"left",bottom:"top",top:"bottom"};return t.replace(/left|right|bottom|top/g,(function(t){return e[t]}))}function st(t,e,n){n=n.split("-")[0];var i=rt(t),o={width:i.width,height:i.height},r=-1!==["right","left"].indexOf(n),a=r?"top":"left",s=r?"left":"top",l=r?"height":"width",u=r?"width":"height";return o[a]=e[a]+e[l]/2-i[l]/2,o[s]=n===s?e[s]-i[u]:e[at(s)],o}function lt(t,e){return Array.prototype.find?t.find(e):t.filter(e)[0]}function ut(t,e,n){return(void 0===n?t:t.slice(0,function(t,e,n){if(Array.prototype.findIndex)return t.findIndex((function(t){return t[e]===n}));var i=lt(t,(function(t){return t[e]===n}));return t.indexOf(i)}(t,"name",n))).forEach((function(t){t.function&&console.warn("`modifier.function` is deprecated, use `modifier.fn`!");var n=t.function||t.fn;t.enabled&&A(n)&&(e.offsets.popper=K(e.offsets.popper),e.offsets.reference=K(e.offsets.reference),e=n(e,t))})),e}function ft(){if(!this.state.isDestroyed){var t={instance:this,styles:{},arrowStyles:{},attributes:{},flipped:!1,offsets:{}};t.offsets.reference=ot(this.state,this.popper,this.reference,this.options.positionFixed),t.placement=it(this.options.placement,t.offsets.reference,this.popper,this.reference,this.options.modifiers.flip.boundariesElement,this.options.modifiers.flip.padding),t.originalPlacement=t.placement,t.positionFixed=this.options.positionFixed,t.offsets.popper=st(this.popper,t.offsets.reference,t.placement),t.offsets.popper.position=this.options.positionFixed?"fixed":"absolute",t=ut(this.modifiers,t),this.state.isCreated?this.options.onUpdate(t):(this.state.isCreated=!0,this.options.onCreate(t))}}function dt(t,e){return t.some((function(t){var n=t.name;return t.enabled&&n===e}))}function ct(t){for(var e=[!1,"ms","Webkit","Moz","O"],n=t.charAt(0).toUpperCase()+t.slice(1),i=0;i<e.length;i++){var o=e[i],r=o?""+o+n:t;if("undefined"!=typeof document.body.style[r])return r}return null}function ht(){return this.state.isDestroyed=!0,dt(this.modifiers,"applyStyle")&&(this.popper.removeAttribute("x-placement"),this.popper.style.position="",this.popper.style.top="",this.popper.style.left="",this.popper.style.right="",this.popper.style.bottom="",this.popper.style.willChange="",this.popper.style[ct("transform")]=""),this.disableEventListeners(),this.options.removeOnDestroy&&this.popper.parentNode.removeChild(this.popper),this}function pt(t){var e=t.ownerDocument;return e?e.defaultView:window}function mt(t,e,n,i){n.updateBound=i,pt(t).addEventListener("resize",n.updateBound,{passive:!0});var o=x(t);return function t(e,n,i,o){var r="BODY"===e.nodeName,a=r?e.ownerDocument.defaultView:e;a.addEventListener(n,i,{passive:!0}),r||t(x(a.parentNode),n,i,o),o.push(a)}(o,"scroll",n.updateBound,n.scrollParents),n.scrollElement=o,n.eventsEnabled=!0,n}function gt(){this.state.eventsEnabled||(this.state=mt(this.reference,this.options,this.state,this.scheduleUpdate))}function vt(){var t,e;this.state.eventsEnabled&&(cancelAnimationFrame(this.scheduleUpdate),this.state=(t=this.reference,e=this.state,pt(t).removeEventListener("resize",e.updateBound),e.scrollParents.forEach((function(t){t.removeEventListener("scroll",e.updateBound)})),e.updateBound=null,e.scrollParents=[],e.scrollElement=null,e.eventsEnabled=!1,e))}function _t(t){return""!==t&&!isNaN(parseFloat(t))&&isFinite(t)}function bt(t,e){Object.keys(e).forEach((function(n){var i="";-1!==["width","height","top","right","bottom","left"].indexOf(n)&&_t(e[n])&&(i="px"),t.style[n]=e[n]+i}))}var yt=D&&/Firefox/i.test(navigator.userAgent);function wt(t,e,n){var i=lt(t,(function(t){return t.name===e})),o=!!i&&t.some((function(t){return t.name===n&&t.enabled&&t.order<i.order}));if(!o){var r="`"+e+"`",a="`"+n+"`";console.warn(a+" modifier is required by "+r+" modifier in order to work, be sure to include it before "+r+"!")}return o}var Et=["auto-start","auto","auto-end","top-start","top","top-end","right-start","right","right-end","bottom-end","bottom","bottom-start","left-end","left","left-start"],Tt=Et.slice(3);function Ct(t){var e=arguments.length>1&&void 0!==arguments[1]&&arguments[1],n=Tt.indexOf(t),i=Tt.slice(n+1).concat(Tt.slice(0,n));return e?i.reverse():i}var St="flip",Dt="clockwise",Nt="counterclockwise";function kt(t,e,n,i){var o=[0,0],r=-1!==["right","left"].indexOf(i),a=t.split(/(\+|\-)/).map((function(t){return t.trim()})),s=a.indexOf(lt(a,(function(t){return-1!==t.search(/,|\s/)})));a[s]&&-1===a[s].indexOf(",")&&console.warn("Offsets separated by white space(s) are deprecated, use a comma (,) instead.");var l=/\s*,\s*|\s+/,u=-1!==s?[a.slice(0,s).concat([a[s].split(l)[0]]),[a[s].split(l)[1]].concat(a.slice(s+1))]:[a];return(u=u.map((function(t,i){var o=(1===i?!r:r)?"height":"width",a=!1;return t.reduce((function(t,e){return""===t[t.length-1]&&-1!==["+","-"].indexOf(e)?(t[t.length-1]=e,a=!0,t):a?(t[t.length-1]+=e,a=!1,t):t.concat(e)}),[]).map((function(t){return function(t,e,n,i){var o=t.match(/((?:\-|\+)?\d*\.?\d*)(.*)/),r=+o[1],a=o[2];if(!r)return t;if(0===a.indexOf("%")){var s=void 0;switch(a){case"%p":s=n;break;case"%":case"%r":default:s=i}return K(s)[e]/100*r}if("vh"===a||"vw"===a){return("vh"===a?Math.max(document.documentElement.clientHeight,window.innerHeight||0):Math.max(document.documentElement.clientWidth,window.innerWidth||0))/100*r}return r}(t,o,e,n)}))}))).forEach((function(t,e){t.forEach((function(n,i){_t(n)&&(o[e]+=n*("-"===t[i-1]?-1:1))}))})),o}var At={placement:"bottom",positionFixed:!1,eventsEnabled:!0,removeOnDestroy:!1,onCreate:function(){},onUpdate:function(){},modifiers:{shift:{order:100,enabled:!0,fn:function(t){var e=t.placement,n=e.split("-")[0],i=e.split("-")[1];if(i){var o=t.offsets,r=o.reference,a=o.popper,s=-1!==["bottom","top"].indexOf(n),l=s?"left":"top",u=s?"width":"height",f={start:z({},l,r[l]),end:z({},l,r[l]+r[u]-a[u])};t.offsets.popper=X({},a,f[i])}return t}},offset:{order:200,enabled:!0,fn:function(t,e){var n=e.offset,i=t.placement,o=t.offsets,r=o.popper,a=o.reference,s=i.split("-")[0],l=void 0;return l=_t(+n)?[+n,0]:kt(n,r,a,s),"left"===s?(r.top+=l[0],r.left-=l[1]):"right"===s?(r.top+=l[0],r.left+=l[1]):"top"===s?(r.left+=l[0],r.top-=l[1]):"bottom"===s&&(r.left+=l[0],r.top+=l[1]),t.popper=r,t},offset:0},preventOverflow:{order:300,enabled:!0,fn:function(t,e){var n=e.boundariesElement||R(t.instance.popper);t.instance.reference===n&&(n=R(n));var i=ct("transform"),o=t.instance.popper.style,r=o.top,a=o.left,s=o[i];o.top="",o.left="",o[i]="";var l=et(t.instance.popper,t.instance.reference,e.padding,n,t.positionFixed);o.top=r,o.left=a,o[i]=s,e.boundaries=l;var u=e.priority,f=t.offsets.popper,d={primary:function(t){var n=f[t];return f[t]<l[t]&&!e.escapeWithReference&&(n=Math.max(f[t],l[t])),z({},t,n)},secondary:function(t){var n="right"===t?"left":"top",i=f[n];return f[t]>l[t]&&!e.escapeWithReference&&(i=Math.min(f[n],l[t]-("right"===t?f.width:f.height))),z({},n,i)}};return u.forEach((function(t){var e=-1!==["left","top"].indexOf(t)?"primary":"secondary";f=X({},f,d[e](t))})),t.offsets.popper=f,t},priority:["left","right","top","bottom"],padding:5,boundariesElement:"scrollParent"},keepTogether:{order:400,enabled:!0,fn:function(t){var e=t.offsets,n=e.popper,i=e.reference,o=t.placement.split("-")[0],r=Math.floor,a=-1!==["top","bottom"].indexOf(o),s=a?"right":"bottom",l=a?"left":"top",u=a?"width":"height";return n[s]<r(i[l])&&(t.offsets.popper[l]=r(i[l])-n[u]),n[l]>r(i[s])&&(t.offsets.popper[l]=r(i[s])),t}},arrow:{order:500,enabled:!0,fn:function(t,e){var n;if(!wt(t.instance.modifiers,"arrow","keepTogether"))return t;var i=e.element;if("string"==typeof i){if(!(i=t.instance.popper.querySelector(i)))return t}else if(!t.instance.popper.contains(i))return console.warn("WARNING: `arrow.element` must be child of its popper element!"),t;var o=t.placement.split("-")[0],r=t.offsets,a=r.popper,s=r.reference,l=-1!==["left","right"].indexOf(o),u=l?"height":"width",f=l?"Top":"Left",d=f.toLowerCase(),c=l?"left":"top",h=l?"bottom":"right",p=rt(i)[u];s[h]-p<a[d]&&(t.offsets.popper[d]-=a[d]-(s[h]-p)),s[d]+p>a[h]&&(t.offsets.popper[d]+=s[d]+p-a[h]),t.offsets.popper=K(t.offsets.popper);var m=s[d]+s[u]/2-p/2,g=I(t.instance.popper),v=parseFloat(g["margin"+f]),_=parseFloat(g["border"+f+"Width"]),b=m-t.offsets.popper[d]-v-_;return b=Math.max(Math.min(a[u]-p,b),0),t.arrowElement=i,t.offsets.arrow=(z(n={},d,Math.round(b)),z(n,c,""),n),t},element:"[x-arrow]"},flip:{order:600,enabled:!0,fn:function(t,e){if(dt(t.instance.modifiers,"inner"))return t;if(t.flipped&&t.placement===t.originalPlacement)return t;var n=et(t.instance.popper,t.instance.reference,e.padding,e.boundariesElement,t.positionFixed),i=t.placement.split("-")[0],o=at(i),r=t.placement.split("-")[1]||"",a=[];switch(e.behavior){case St:a=[i,o];break;case Dt:a=Ct(i);break;case Nt:a=Ct(i,!0);break;default:a=e.behavior}return a.forEach((function(s,l){if(i!==s||a.length===l+1)return t;i=t.placement.split("-")[0],o=at(i);var u=t.offsets.popper,f=t.offsets.reference,d=Math.floor,c="left"===i&&d(u.right)>d(f.left)||"right"===i&&d(u.left)<d(f.right)||"top"===i&&d(u.bottom)>d(f.top)||"bottom"===i&&d(u.top)<d(f.bottom),h=d(u.left)<d(n.left),p=d(u.right)>d(n.right),m=d(u.top)<d(n.top),g=d(u.bottom)>d(n.bottom),v="left"===i&&h||"right"===i&&p||"top"===i&&m||"bottom"===i&&g,_=-1!==["top","bottom"].indexOf(i),b=!!e.flipVariations&&(_&&"start"===r&&h||_&&"end"===r&&p||!_&&"start"===r&&m||!_&&"end"===r&&g),y=!!e.flipVariationsByContent&&(_&&"start"===r&&p||_&&"end"===r&&h||!_&&"start"===r&&g||!_&&"end"===r&&m),w=b||y;(c||v||w)&&(t.flipped=!0,(c||v)&&(i=a[l+1]),w&&(r=function(t){return"end"===t?"start":"start"===t?"end":t}(r)),t.placement=i+(r?"-"+r:""),t.offsets.popper=X({},t.offsets.popper,st(t.instance.popper,t.offsets.reference,t.placement)),t=ut(t.instance.modifiers,t,"flip"))})),t},behavior:"flip",padding:5,boundariesElement:"viewport",flipVariations:!1,flipVariationsByContent:!1},inner:{order:700,enabled:!1,fn:function(t){var e=t.placement,n=e.split("-")[0],i=t.offsets,o=i.popper,r=i.reference,a=-1!==["left","right"].indexOf(n),s=-1===["top","left"].indexOf(n);return o[a?"left":"top"]=r[n]-(s?o[a?"width":"height"]:0),t.placement=at(e),t.offsets.popper=K(o),t}},hide:{order:800,enabled:!0,fn:function(t){if(!wt(t.instance.modifiers,"hide","preventOverflow"))return t;var e=t.offsets.reference,n=lt(t.instance.modifiers,(function(t){return"preventOverflow"===t.name})).boundaries;if(e.bottom<n.top||e.left>n.right||e.top>n.bottom||e.right<n.left){if(!0===t.hide)return t;t.hide=!0,t.attributes["x-out-of-boundaries"]=""}else{if(!1===t.hide)return t;t.hide=!1,t.attributes["x-out-of-boundaries"]=!1}return t}},computeStyle:{order:850,enabled:!0,fn:function(t,e){var n=e.x,i=e.y,o=t.offsets.popper,r=lt(t.instance.modifiers,(function(t){return"applyStyle"===t.name})).gpuAcceleration;void 0!==r&&console.warn("WARNING: `gpuAcceleration` option moved to `computeStyle` modifier and will not be supported in future versions of Popper.js!");var a=void 0!==r?r:e.gpuAcceleration,s=R(t.instance.popper),l=G(s),u={position:o.position},f=function(t,e){var n=t.offsets,i=n.popper,o=n.reference,r=Math.round,a=Math.floor,s=function(t){return t},l=r(o.width),u=r(i.width),f=-1!==["left","right"].indexOf(t.placement),d=-1!==t.placement.indexOf("-"),c=e?f||d||l%2==u%2?r:a:s,h=e?r:s;return{left:c(l%2==1&&u%2==1&&!d&&e?i.left-1:i.left),top:h(i.top),bottom:h(i.bottom),right:c(i.right)}}(t,window.devicePixelRatio<2||!yt),d="bottom"===n?"top":"bottom",c="right"===i?"left":"right",h=ct("transform"),p=void 0,m=void 0;if(m="bottom"===d?"HTML"===s.nodeName?-s.clientHeight+f.bottom:-l.height+f.bottom:f.top,p="right"===c?"HTML"===s.nodeName?-s.clientWidth+f.right:-l.width+f.right:f.left,a&&h)u[h]="translate3d("+p+"px, "+m+"px, 0)",u[d]=0,u[c]=0,u.willChange="transform";else{var g="bottom"===d?-1:1,v="right"===c?-1:1;u[d]=m*g,u[c]=p*v,u.willChange=d+", "+c}var _={"x-placement":t.placement};return t.attributes=X({},_,t.attributes),t.styles=X({},u,t.styles),t.arrowStyles=X({},t.offsets.arrow,t.arrowStyles),t},gpuAcceleration:!0,x:"bottom",y:"right"},applyStyle:{order:900,enabled:!0,fn:function(t){var e,n;return bt(t.instance.popper,t.styles),e=t.instance.popper,n=t.attributes,Object.keys(n).forEach((function(t){!1!==n[t]?e.setAttribute(t,n[t]):e.removeAttribute(t)})),t.arrowElement&&Object.keys(t.arrowStyles).length&&bt(t.arrowElement,t.arrowStyles),t},onLoad:function(t,e,n,i,o){var r=ot(o,e,t,n.positionFixed),a=it(n.placement,r,e,t,n.modifiers.flip.boundariesElement,n.modifiers.flip.padding);return e.setAttribute("x-placement",a),bt(e,{position:n.positionFixed?"fixed":"absolute"}),n},gpuAcceleration:void 0}}},It=function(){function t(e,n){var i=this,o=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{};V(this,t),this.scheduleUpdate=function(){return requestAnimationFrame(i.update)},this.update=k(this.update.bind(this)),this.options=X({},t.Defaults,o),this.state={isDestroyed:!1,isCreated:!1,scrollParents:[]},this.reference=e&&e.jquery?e[0]:e,this.popper=n&&n.jquery?n[0]:n,this.options.modifiers={},Object.keys(X({},t.Defaults.modifiers,o.modifiers)).forEach((function(e){i.options.modifiers[e]=X({},t.Defaults.modifiers[e]||{},o.modifiers?o.modifiers[e]:{})})),this.modifiers=Object.keys(this.options.modifiers).map((function(t){return X({name:t},i.options.modifiers[t])})).sort((function(t,e){return t.order-e.order})),this.modifiers.forEach((function(t){t.enabled&&A(t.onLoad)&&t.onLoad(i.reference,i.popper,i.options,t,i.state)})),this.update();var r=this.options.eventsEnabled;r&&this.enableEventListeners(),this.state.eventsEnabled=r}return Y(t,[{key:"update",value:function(){return ft.call(this)}},{key:"destroy",value:function(){return ht.call(this)}},{key:"enableEventListeners",value:function(){return gt.call(this)}},{key:"disableEventListeners",value:function(){return vt.call(this)}}]),t}();It.Utils=("undefined"!=typeof window?window:global).PopperUtils,It.placements=Et,It.Defaults=At;var Ot="dropdown",xt=i.default.fn[Ot],jt=new RegExp("38|40|27"),Lt={offset:0,flip:!0,boundary:"scrollParent",reference:"toggle",display:"dynamic",popperConfig:null},Pt={offset:"(number|string|function)",flip:"boolean",boundary:"(string|element)",reference:"(string|element)",display:"string",popperConfig:"(null|object)"},Ft=function(){function t(t,e){this._element=t,this._popper=null,this._config=this._getConfig(e),this._menu=this._getMenuElement(),this._inNavbar=this._detectNavbar(),this._addEventListeners()}var e=t.prototype;return e.toggle=function(){if(!this._element.disabled&&!i.default(this._element).hasClass("disabled")){var e=i.default(this._menu).hasClass("show");t._clearMenus(),e||this.show(!0)}},e.show=function(e){if(void 0===e&&(e=!1),!(this._element.disabled||i.default(this._element).hasClass("disabled")||i.default(this._menu).hasClass("show"))){var n={relatedTarget:this._element},o=i.default.Event("show.bs.dropdown",n),r=t._getParentFromElement(this._element);if(i.default(r).trigger(o),!o.isDefaultPrevented()){if(!this._inNavbar&&e){if("undefined"==typeof It)throw new TypeError("Bootstrap's dropdowns require Popper.js (https://popper.js.org/)");var a=this._element;"parent"===this._config.reference?a=r:l.isElement(this._config.reference)&&(a=this._config.reference,"undefined"!=typeof this._config.reference.jquery&&(a=this._config.reference[0])),"scrollParent"!==this._config.boundary&&i.default(r).addClass("position-static"),this._popper=new It(a,this._menu,this._getPopperConfig())}"ontouchstart"in document.documentElement&&0===i.default(r).closest(".navbar-nav").length&&i.default(document.body).children().on("mouseover",null,i.default.noop),this._element.focus(),this._element.setAttribute("aria-expanded",!0),i.default(this._menu).toggleClass("show"),i.default(r).toggleClass("show").trigger(i.default.Event("shown.bs.dropdown",n))}}},e.hide=function(){if(!this._element.disabled&&!i.default(this._element).hasClass("disabled")&&i.default(this._menu).hasClass("show")){var e={relatedTarget:this._element},n=i.default.Event("hide.bs.dropdown",e),o=t._getParentFromElement(this._element);i.default(o).trigger(n),n.isDefaultPrevented()||(this._popper&&this._popper.destroy(),i.default(this._menu).toggleClass("show"),i.default(o).toggleClass("show").trigger(i.default.Event("hidden.bs.dropdown",e)))}},e.dispose=function(){i.default.removeData(this._element,"bs.dropdown"),i.default(this._element).off(".bs.dropdown"),this._element=null,this._menu=null,null!==this._popper&&(this._popper.destroy(),this._popper=null)},e.update=function(){this._inNavbar=this._detectNavbar(),null!==this._popper&&this._popper.scheduleUpdate()},e._addEventListeners=function(){var t=this;i.default(this._element).on("click.bs.dropdown",(function(e){e.preventDefault(),e.stopPropagation(),t.toggle()}))},e._getConfig=function(t){return t=a({},this.constructor.Default,i.default(this._element).data(),t),l.typeCheckConfig(Ot,t,this.constructor.DefaultType),t},e._getMenuElement=function(){if(!this._menu){var e=t._getParentFromElement(this._element);e&&(this._menu=e.querySelector(".dropdown-menu"))}return this._menu},e._getPlacement=function(){var t=i.default(this._element.parentNode),e="bottom-start";return t.hasClass("dropup")?e=i.default(this._menu).hasClass("dropdown-menu-right")?"top-end":"top-start":t.hasClass("dropright")?e="right-start":t.hasClass("dropleft")?e="left-start":i.default(this._menu).hasClass("dropdown-menu-right")&&(e="bottom-end"),e},e._detectNavbar=function(){return i.default(this._element).closest(".navbar").length>0},e._getOffset=function(){var t=this,e={};return"function"==typeof this._config.offset?e.fn=function(e){return e.offsets=a({},e.offsets,t._config.offset(e.offsets,t._element)||{}),e}:e.offset=this._config.offset,e},e._getPopperConfig=function(){var t={placement:this._getPlacement(),modifiers:{offset:this._getOffset(),flip:{enabled:this._config.flip},preventOverflow:{boundariesElement:this._config.boundary}}};return"static"===this._config.display&&(t.modifiers.applyStyle={enabled:!1}),a({},t,this._config.popperConfig)},t._jQueryInterface=function(e){return this.each((function(){var n=i.default(this).data("bs.dropdown");if(n||(n=new t(this,"object"==typeof e?e:null),i.default(this).data("bs.dropdown",n)),"string"==typeof e){if("undefined"==typeof n[e])throw new TypeError('No method named "'+e+'"');n[e]()}}))},t._clearMenus=function(e){if(!e||3!==e.which&&("keyup"!==e.type||9===e.which))for(var n=[].slice.call(document.querySelectorAll('[data-toggle="dropdown"]')),o=0,r=n.length;o<r;o++){var a=t._getParentFromElement(n[o]),s=i.default(n[o]).data("bs.dropdown"),l={relatedTarget:n[o]};if(e&&"click"===e.type&&(l.clickEvent=e),s){var u=s._menu;if(i.default(a).hasClass("show")&&!(e&&("click"===e.type&&/input|textarea/i.test(e.target.tagName)||"keyup"===e.type&&9===e.which)&&i.default.contains(a,e.target))){var f=i.default.Event("hide.bs.dropdown",l);i.default(a).trigger(f),f.isDefaultPrevented()||("ontouchstart"in document.documentElement&&i.default(document.body).children().off("mouseover",null,i.default.noop),n[o].setAttribute("aria-expanded","false"),s._popper&&s._popper.destroy(),i.default(u).removeClass("show"),i.default(a).removeClass("show").trigger(i.default.Event("hidden.bs.dropdown",l)))}}}},t._getParentFromElement=function(t){var e,n=l.getSelectorFromElement(t);return n&&(e=document.querySelector(n)),e||t.parentNode},t._dataApiKeydownHandler=function(e){if(!(/input|textarea/i.test(e.target.tagName)?32===e.which||27!==e.which&&(40!==e.which&&38!==e.which||i.default(e.target).closest(".dropdown-menu").length):!jt.test(e.which))&&!this.disabled&&!i.default(this).hasClass("disabled")){var n=t._getParentFromElement(this),o=i.default(n).hasClass("show");if(o||27!==e.which){if(e.preventDefault(),e.stopPropagation(),!o||27===e.which||32===e.which)return 27===e.which&&i.default(n.querySelector('[data-toggle="dropdown"]')).trigger("focus"),void i.default(this).trigger("click");var r=[].slice.call(n.querySelectorAll(".dropdown-menu .dropdown-item:not(.disabled):not(:disabled)")).filter((function(t){return i.default(t).is(":visible")}));if(0!==r.length){var a=r.indexOf(e.target);38===e.which&&a>0&&a--,40===e.which&&a<r.length-1&&a++,a<0&&(a=0),r[a].focus()}}}},r(t,null,[{key:"VERSION",get:function(){return"4.5.3"}},{key:"Default",get:function(){return Lt}},{key:"DefaultType",get:function(){return Pt}}]),t}();i.default(document).on("keydown.bs.dropdown.data-api",'[data-toggle="dropdown"]',Ft._dataApiKeydownHandler).on("keydown.bs.dropdown.data-api",".dropdown-menu",Ft._dataApiKeydownHandler).on("click.bs.dropdown.data-api keyup.bs.dropdown.data-api",Ft._clearMenus).on("click.bs.dropdown.data-api",'[data-toggle="dropdown"]',(function(t){t.preventDefault(),t.stopPropagation(),Ft._jQueryInterface.call(i.default(this),"toggle")})).on("click.bs.dropdown.data-api",".dropdown form",(function(t){t.stopPropagation()})),i.default.fn[Ot]=Ft._jQueryInterface,i.default.fn[Ot].Constructor=Ft,i.default.fn[Ot].noConflict=function(){return i.default.fn[Ot]=xt,Ft._jQueryInterface};var Rt=i.default.fn.modal,Ht={backdrop:!0,keyboard:!0,focus:!0,show:!0},Mt={backdrop:"(boolean|string)",keyboard:"boolean",focus:"boolean",show:"boolean"},Bt=function(){function t(t,e){this._config=this._getConfig(e),this._element=t,this._dialog=t.querySelector(".modal-dialog"),this._backdrop=null,this._isShown=!1,this._isBodyOverflowing=!1,this._ignoreBackdropClick=!1,this._isTransitioning=!1,this._scrollbarWidth=0}var e=t.prototype;return e.toggle=function(t){return this._isShown?this.hide():this.show(t)},e.show=function(t){var e=this;if(!this._isShown&&!this._isTransitioning){i.default(this._element).hasClass("fade")&&(this._isTransitioning=!0);var n=i.default.Event("show.bs.modal",{relatedTarget:t});i.default(this._element).trigger(n),this._isShown||n.isDefaultPrevented()||(this._isShown=!0,this._checkScrollbar(),this._setScrollbar(),this._adjustDialog(),this._setEscapeEvent(),this._setResizeEvent(),i.default(this._element).on("click.dismiss.bs.modal",'[data-dismiss="modal"]',(function(t){return e.hide(t)})),i.default(this._dialog).on("mousedown.dismiss.bs.modal",(function(){i.default(e._element).one("mouseup.dismiss.bs.modal",(function(t){i.default(t.target).is(e._element)&&(e._ignoreBackdropClick=!0)}))})),this._showBackdrop((function(){return e._showElement(t)})))}},e.hide=function(t){var e=this;if(t&&t.preventDefault(),this._isShown&&!this._isTransitioning){var n=i.default.Event("hide.bs.modal");if(i.default(this._element).trigger(n),this._isShown&&!n.isDefaultPrevented()){this._isShown=!1;var o=i.default(this._element).hasClass("fade");if(o&&(this._isTransitioning=!0),this._setEscapeEvent(),this._setResizeEvent(),i.default(document).off("focusin.bs.modal"),i.default(this._element).removeClass("show"),i.default(this._element).off("click.dismiss.bs.modal"),i.default(this._dialog).off("mousedown.dismiss.bs.modal"),o){var r=l.getTransitionDurationFromElement(this._element);i.default(this._element).one(l.TRANSITION_END,(function(t){return e._hideModal(t)})).emulateTransitionEnd(r)}else this._hideModal()}}},e.dispose=function(){[window,this._element,this._dialog].forEach((function(t){return i.default(t).off(".bs.modal")})),i.default(document).off("focusin.bs.modal"),i.default.removeData(this._element,"bs.modal"),this._config=null,this._element=null,this._dialog=null,this._backdrop=null,this._isShown=null,this._isBodyOverflowing=null,this._ignoreBackdropClick=null,this._isTransitioning=null,this._scrollbarWidth=null},e.handleUpdate=function(){this._adjustDialog()},e._getConfig=function(t){return t=a({},Ht,t),l.typeCheckConfig("modal",t,Mt),t},e._triggerBackdropTransition=function(){var t=this;if("static"===this._config.backdrop){var e=i.default.Event("hidePrevented.bs.modal");if(i.default(this._element).trigger(e),e.isDefaultPrevented())return;var n=this._element.scrollHeight>document.documentElement.clientHeight;n||(this._element.style.overflowY="hidden"),this._element.classList.add("modal-static");var o=l.getTransitionDurationFromElement(this._dialog);i.default(this._element).off(l.TRANSITION_END),i.default(this._element).one(l.TRANSITION_END,(function(){t._element.classList.remove("modal-static"),n||i.default(t._element).one(l.TRANSITION_END,(function(){t._element.style.overflowY=""})).emulateTransitionEnd(t._element,o)})).emulateTransitionEnd(o),this._element.focus()}else this.hide()},e._showElement=function(t){var e=this,n=i.default(this._element).hasClass("fade"),o=this._dialog?this._dialog.querySelector(".modal-body"):null;this._element.parentNode&&this._element.parentNode.nodeType===Node.ELEMENT_NODE||document.body.appendChild(this._element),this._element.style.display="block",this._element.removeAttribute("aria-hidden"),this._element.setAttribute("aria-modal",!0),this._element.setAttribute("role","dialog"),i.default(this._dialog).hasClass("modal-dialog-scrollable")&&o?o.scrollTop=0:this._element.scrollTop=0,n&&l.reflow(this._element),i.default(this._element).addClass("show"),this._config.focus&&this._enforceFocus();var r=i.default.Event("shown.bs.modal",{relatedTarget:t}),a=function(){e._config.focus&&e._element.focus(),e._isTransitioning=!1,i.default(e._element).trigger(r)};if(n){var s=l.getTransitionDurationFromElement(this._dialog);i.default(this._dialog).one(l.TRANSITION_END,a).emulateTransitionEnd(s)}else a()},e._enforceFocus=function(){var t=this;i.default(document).off("focusin.bs.modal").on("focusin.bs.modal",(function(e){document!==e.target&&t._element!==e.target&&0===i.default(t._element).has(e.target).length&&t._element.focus()}))},e._setEscapeEvent=function(){var t=this;this._isShown?i.default(this._element).on("keydown.dismiss.bs.modal",(function(e){t._config.keyboard&&27===e.which?(e.preventDefault(),t.hide()):t._config.keyboard||27!==e.which||t._triggerBackdropTransition()})):this._isShown||i.default(this._element).off("keydown.dismiss.bs.modal")},e._setResizeEvent=function(){var t=this;this._isShown?i.default(window).on("resize.bs.modal",(function(e){return t.handleUpdate(e)})):i.default(window).off("resize.bs.modal")},e._hideModal=function(){var t=this;this._element.style.display="none",this._element.setAttribute("aria-hidden",!0),this._element.removeAttribute("aria-modal"),this._element.removeAttribute("role"),this._isTransitioning=!1,this._showBackdrop((function(){i.default(document.body).removeClass("modal-open"),t._resetAdjustments(),t._resetScrollbar(),i.default(t._element).trigger("hidden.bs.modal")}))},e._removeBackdrop=function(){this._backdrop&&(i.default(this._backdrop).remove(),this._backdrop=null)},e._showBackdrop=function(t){var e=this,n=i.default(this._element).hasClass("fade")?"fade":"";if(this._isShown&&this._config.backdrop){if(this._backdrop=document.createElement("div"),this._backdrop.className="modal-backdrop",n&&this._backdrop.classList.add(n),i.default(this._backdrop).appendTo(document.body),i.default(this._element).on("click.dismiss.bs.modal",(function(t){e._ignoreBackdropClick?e._ignoreBackdropClick=!1:t.target===t.currentTarget&&e._triggerBackdropTransition()})),n&&l.reflow(this._backdrop),i.default(this._backdrop).addClass("show"),!t)return;if(!n)return void t();var o=l.getTransitionDurationFromElement(this._backdrop);i.default(this._backdrop).one(l.TRANSITION_END,t).emulateTransitionEnd(o)}else if(!this._isShown&&this._backdrop){i.default(this._backdrop).removeClass("show");var r=function(){e._removeBackdrop(),t&&t()};if(i.default(this._element).hasClass("fade")){var a=l.getTransitionDurationFromElement(this._backdrop);i.default(this._backdrop).one(l.TRANSITION_END,r).emulateTransitionEnd(a)}else r()}else t&&t()},e._adjustDialog=function(){var t=this._element.scrollHeight>document.documentElement.clientHeight;!this._isBodyOverflowing&&t&&(this._element.style.paddingLeft=this._scrollbarWidth+"px"),this._isBodyOverflowing&&!t&&(this._element.style.paddingRight=this._scrollbarWidth+"px")},e._resetAdjustments=function(){this._element.style.paddingLeft="",this._element.style.paddingRight=""},e._checkScrollbar=function(){var t=document.body.getBoundingClientRect();this._isBodyOverflowing=Math.round(t.left+t.right)<window.innerWidth,this._scrollbarWidth=this._getScrollbarWidth()},e._setScrollbar=function(){var t=this;if(this._isBodyOverflowing){var e=[].slice.call(document.querySelectorAll(".fixed-top, .fixed-bottom, .is-fixed, .sticky-top")),n=[].slice.call(document.querySelectorAll(".sticky-top"));i.default(e).each((function(e,n){var o=n.style.paddingRight,r=i.default(n).css("padding-right");i.default(n).data("padding-right",o).css("padding-right",parseFloat(r)+t._scrollbarWidth+"px")})),i.default(n).each((function(e,n){var o=n.style.marginRight,r=i.default(n).css("margin-right");i.default(n).data("margin-right",o).css("margin-right",parseFloat(r)-t._scrollbarWidth+"px")}));var o=document.body.style.paddingRight,r=i.default(document.body).css("padding-right");i.default(document.body).data("padding-right",o).css("padding-right",parseFloat(r)+this._scrollbarWidth+"px")}i.default(document.body).addClass("modal-open")},e._resetScrollbar=function(){var t=[].slice.call(document.querySelectorAll(".fixed-top, .fixed-bottom, .is-fixed, .sticky-top"));i.default(t).each((function(t,e){var n=i.default(e).data("padding-right");i.default(e).removeData("padding-right"),e.style.paddingRight=n||""}));var e=[].slice.call(document.querySelectorAll(".sticky-top"));i.default(e).each((function(t,e){var n=i.default(e).data("margin-right");"undefined"!=typeof n&&i.default(e).css("margin-right",n).removeData("margin-right")}));var n=i.default(document.body).data("padding-right");i.default(document.body).removeData("padding-right"),document.body.style.paddingRight=n||""},e._getScrollbarWidth=function(){var t=document.createElement("div");t.className="modal-scrollbar-measure",document.body.appendChild(t);var e=t.getBoundingClientRect().width-t.clientWidth;return document.body.removeChild(t),e},t._jQueryInterface=function(e,n){return this.each((function(){var o=i.default(this).data("bs.modal"),r=a({},Ht,i.default(this).data(),"object"==typeof e&&e?e:{});if(o||(o=new t(this,r),i.default(this).data("bs.modal",o)),"string"==typeof e){if("undefined"==typeof o[e])throw new TypeError('No method named "'+e+'"');o[e](n)}else r.show&&o.show(n)}))},r(t,null,[{key:"VERSION",get:function(){return"4.5.3"}},{key:"Default",get:function(){return Ht}}]),t}();i.default(document).on("click.bs.modal.data-api",'[data-toggle="modal"]',(function(t){var e,n=this,o=l.getSelectorFromElement(this);o&&(e=document.querySelector(o));var r=i.default(e).data("bs.modal")?"toggle":a({},i.default(e).data(),i.default(this).data());"A"!==this.tagName&&"AREA"!==this.tagName||t.preventDefault();var s=i.default(e).one("show.bs.modal",(function(t){t.isDefaultPrevented()||s.one("hidden.bs.modal",(function(){i.default(n).is(":visible")&&n.focus()}))}));Bt._jQueryInterface.call(i.default(e),r,this)})),i.default.fn.modal=Bt._jQueryInterface,i.default.fn.modal.Constructor=Bt,i.default.fn.modal.noConflict=function(){return i.default.fn.modal=Rt,Bt._jQueryInterface};var qt=["background","cite","href","itemtype","longdesc","poster","src","xlink:href"],Qt={"*":["class","dir","id","lang","role",/^aria-[\w-]*$/i],a:["target","href","title","rel"],area:[],b:[],br:[],col:[],code:[],div:[],em:[],hr:[],h1:[],h2:[],h3:[],h4:[],h5:[],h6:[],i:[],img:["src","srcset","alt","title","width","height"],li:[],ol:[],p:[],pre:[],s:[],small:[],span:[],sub:[],sup:[],strong:[],u:[],ul:[]},Wt=/^(?:(?:https?|mailto|ftp|tel|file):|[^#&/:?]*(?:[#/?]|$))/gi,Ut=/^data:(?:image\/(?:bmp|gif|jpeg|jpg|png|tiff|webp)|video\/(?:mpeg|mp4|ogg|webm)|audio\/(?:mp3|oga|ogg|opus));base64,[\d+/a-z]+=*$/i;function Vt(t,e,n){if(0===t.length)return t;if(n&&"function"==typeof n)return n(t);for(var i=(new window.DOMParser).parseFromString(t,"text/html"),o=Object.keys(e),r=[].slice.call(i.body.querySelectorAll("*")),a=function(t,n){var i=r[t],a=i.nodeName.toLowerCase();if(-1===o.indexOf(i.nodeName.toLowerCase()))return i.parentNode.removeChild(i),"continue";var s=[].slice.call(i.attributes),l=[].concat(e["*"]||[],e[a]||[]);s.forEach((function(t){(function(t,e){var n=t.nodeName.toLowerCase();if(-1!==e.indexOf(n))return-1===qt.indexOf(n)||Boolean(t.nodeValue.match(Wt)||t.nodeValue.match(Ut));for(var i=e.filter((function(t){return t instanceof RegExp})),o=0,r=i.length;o<r;o++)if(n.match(i[o]))return!0;return!1})(t,l)||i.removeAttribute(t.nodeName)}))},s=0,l=r.length;s<l;s++)a(s);return i.body.innerHTML}var Yt="tooltip",zt=i.default.fn[Yt],Xt=new RegExp("(^|\\s)bs-tooltip\\S+","g"),Kt=["sanitize","whiteList","sanitizeFn"],Gt={animation:"boolean",template:"string",title:"(string|element|function)",trigger:"string",delay:"(number|object)",html:"boolean",selector:"(string|boolean)",placement:"(string|function)",offset:"(number|string|function)",container:"(string|element|boolean)",fallbackPlacement:"(string|array)",boundary:"(string|element)",sanitize:"boolean",sanitizeFn:"(null|function)",whiteList:"object",popperConfig:"(null|object)"},$t={AUTO:"auto",TOP:"top",RIGHT:"right",BOTTOM:"bottom",LEFT:"left"},Jt={animation:!0,template:'<div class="tooltip" role="tooltip"><div class="arrow"></div><div class="tooltip-inner"></div></div>',trigger:"hover focus",title:"",delay:0,html:!1,selector:!1,placement:"top",offset:0,container:!1,fallbackPlacement:"flip",boundary:"scrollParent",sanitize:!0,sanitizeFn:null,whiteList:Qt,popperConfig:null},Zt={HIDE:"hide.bs.tooltip",HIDDEN:"hidden.bs.tooltip",SHOW:"show.bs.tooltip",SHOWN:"shown.bs.tooltip",INSERTED:"inserted.bs.tooltip",CLICK:"click.bs.tooltip",FOCUSIN:"focusin.bs.tooltip",FOCUSOUT:"focusout.bs.tooltip",MOUSEENTER:"mouseenter.bs.tooltip",MOUSELEAVE:"mouseleave.bs.tooltip"},te=function(){function t(t,e){if("undefined"==typeof It)throw new TypeError("Bootstrap's tooltips require Popper.js (https://popper.js.org/)");this._isEnabled=!0,this._timeout=0,this._hoverState="",this._activeTrigger={},this._popper=null,this.element=t,this.config=this._getConfig(e),this.tip=null,this._setListeners()}var e=t.prototype;return e.enable=function(){this._isEnabled=!0},e.disable=function(){this._isEnabled=!1},e.toggleEnabled=function(){this._isEnabled=!this._isEnabled},e.toggle=function(t){if(this._isEnabled)if(t){var e=this.constructor.DATA_KEY,n=i.default(t.currentTarget).data(e);n||(n=new this.constructor(t.currentTarget,this._getDelegateConfig()),i.default(t.currentTarget).data(e,n)),n._activeTrigger.click=!n._activeTrigger.click,n._isWithActiveTrigger()?n._enter(null,n):n._leave(null,n)}else{if(i.default(this.getTipElement()).hasClass("show"))return void this._leave(null,this);this._enter(null,this)}},e.dispose=function(){clearTimeout(this._timeout),i.default.removeData(this.element,this.constructor.DATA_KEY),i.default(this.element).off(this.constructor.EVENT_KEY),i.default(this.element).closest(".modal").off("hide.bs.modal",this._hideModalHandler),this.tip&&i.default(this.tip).remove(),this._isEnabled=null,this._timeout=null,this._hoverState=null,this._activeTrigger=null,this._popper&&this._popper.destroy(),this._popper=null,this.element=null,this.config=null,this.tip=null},e.show=function(){var t=this;if("none"===i.default(this.element).css("display"))throw new Error("Please use show on visible elements");var e=i.default.Event(this.constructor.Event.SHOW);if(this.isWithContent()&&this._isEnabled){i.default(this.element).trigger(e);var n=l.findShadowRoot(this.element),o=i.default.contains(null!==n?n:this.element.ownerDocument.documentElement,this.element);if(e.isDefaultPrevented()||!o)return;var r=this.getTipElement(),a=l.getUID(this.constructor.NAME);r.setAttribute("id",a),this.element.setAttribute("aria-describedby",a),this.setContent(),this.config.animation&&i.default(r).addClass("fade");var s="function"==typeof this.config.placement?this.config.placement.call(this,r,this.element):this.config.placement,u=this._getAttachment(s);this.addAttachmentClass(u);var f=this._getContainer();i.default(r).data(this.constructor.DATA_KEY,this),i.default.contains(this.element.ownerDocument.documentElement,this.tip)||i.default(r).appendTo(f),i.default(this.element).trigger(this.constructor.Event.INSERTED),this._popper=new It(this.element,r,this._getPopperConfig(u)),i.default(r).addClass("show"),"ontouchstart"in document.documentElement&&i.default(document.body).children().on("mouseover",null,i.default.noop);var d=function(){t.config.animation&&t._fixTransition();var e=t._hoverState;t._hoverState=null,i.default(t.element).trigger(t.constructor.Event.SHOWN),"out"===e&&t._leave(null,t)};if(i.default(this.tip).hasClass("fade")){var c=l.getTransitionDurationFromElement(this.tip);i.default(this.tip).one(l.TRANSITION_END,d).emulateTransitionEnd(c)}else d()}},e.hide=function(t){var e=this,n=this.getTipElement(),o=i.default.Event(this.constructor.Event.HIDE),r=function(){"show"!==e._hoverState&&n.parentNode&&n.parentNode.removeChild(n),e._cleanTipClass(),e.element.removeAttribute("aria-describedby"),i.default(e.element).trigger(e.constructor.Event.HIDDEN),null!==e._popper&&e._popper.destroy(),t&&t()};if(i.default(this.element).trigger(o),!o.isDefaultPrevented()){if(i.default(n).removeClass("show"),"ontouchstart"in document.documentElement&&i.default(document.body).children().off("mouseover",null,i.default.noop),this._activeTrigger.click=!1,this._activeTrigger.focus=!1,this._activeTrigger.hover=!1,i.default(this.tip).hasClass("fade")){var a=l.getTransitionDurationFromElement(n);i.default(n).one(l.TRANSITION_END,r).emulateTransitionEnd(a)}else r();this._hoverState=""}},e.update=function(){null!==this._popper&&this._popper.scheduleUpdate()},e.isWithContent=function(){return Boolean(this.getTitle())},e.addAttachmentClass=function(t){i.default(this.getTipElement()).addClass("bs-tooltip-"+t)},e.getTipElement=function(){return this.tip=this.tip||i.default(this.config.template)[0],this.tip},e.setContent=function(){var t=this.getTipElement();this.setElementContent(i.default(t.querySelectorAll(".tooltip-inner")),this.getTitle()),i.default(t).removeClass("fade show")},e.setElementContent=function(t,e){"object"!=typeof e||!e.nodeType&&!e.jquery?this.config.html?(this.config.sanitize&&(e=Vt(e,this.config.whiteList,this.config.sanitizeFn)),t.html(e)):t.text(e):this.config.html?i.default(e).parent().is(t)||t.empty().append(e):t.text(i.default(e).text())},e.getTitle=function(){var t=this.element.getAttribute("data-original-title");return t||(t="function"==typeof this.config.title?this.config.title.call(this.element):this.config.title),t},e._getPopperConfig=function(t){var e=this;return a({},{placement:t,modifiers:{offset:this._getOffset(),flip:{behavior:this.config.fallbackPlacement},arrow:{element:".arrow"},preventOverflow:{boundariesElement:this.config.boundary}},onCreate:function(t){t.originalPlacement!==t.placement&&e._handlePopperPlacementChange(t)},onUpdate:function(t){return e._handlePopperPlacementChange(t)}},this.config.popperConfig)},e._getOffset=function(){var t=this,e={};return"function"==typeof this.config.offset?e.fn=function(e){return e.offsets=a({},e.offsets,t.config.offset(e.offsets,t.element)||{}),e}:e.offset=this.config.offset,e},e._getContainer=function(){return!1===this.config.container?document.body:l.isElement(this.config.container)?i.default(this.config.container):i.default(document).find(this.config.container)},e._getAttachment=function(t){return $t[t.toUpperCase()]},e._setListeners=function(){var t=this;this.config.trigger.split(" ").forEach((function(e){if("click"===e)i.default(t.element).on(t.constructor.Event.CLICK,t.config.selector,(function(e){return t.toggle(e)}));else if("manual"!==e){var n="hover"===e?t.constructor.Event.MOUSEENTER:t.constructor.Event.FOCUSIN,o="hover"===e?t.constructor.Event.MOUSELEAVE:t.constructor.Event.FOCUSOUT;i.default(t.element).on(n,t.config.selector,(function(e){return t._enter(e)})).on(o,t.config.selector,(function(e){return t._leave(e)}))}})),this._hideModalHandler=function(){t.element&&t.hide()},i.default(this.element).closest(".modal").on("hide.bs.modal",this._hideModalHandler),this.config.selector?this.config=a({},this.config,{trigger:"manual",selector:""}):this._fixTitle()},e._fixTitle=function(){var t=typeof this.element.getAttribute("data-original-title");(this.element.getAttribute("title")||"string"!==t)&&(this.element.setAttribute("data-original-title",this.element.getAttribute("title")||""),this.element.setAttribute("title",""))},e._enter=function(t,e){var n=this.constructor.DATA_KEY;(e=e||i.default(t.currentTarget).data(n))||(e=new this.constructor(t.currentTarget,this._getDelegateConfig()),i.default(t.currentTarget).data(n,e)),t&&(e._activeTrigger["focusin"===t.type?"focus":"hover"]=!0),i.default(e.getTipElement()).hasClass("show")||"show"===e._hoverState?e._hoverState="show":(clearTimeout(e._timeout),e._hoverState="show",e.config.delay&&e.config.delay.show?e._timeout=setTimeout((function(){"show"===e._hoverState&&e.show()}),e.config.delay.show):e.show())},e._leave=function(t,e){var n=this.constructor.DATA_KEY;(e=e||i.default(t.currentTarget).data(n))||(e=new this.constructor(t.currentTarget,this._getDelegateConfig()),i.default(t.currentTarget).data(n,e)),t&&(e._activeTrigger["focusout"===t.type?"focus":"hover"]=!1),e._isWithActiveTrigger()||(clearTimeout(e._timeout),e._hoverState="out",e.config.delay&&e.config.delay.hide?e._timeout=setTimeout((function(){"out"===e._hoverState&&e.hide()}),e.config.delay.hide):e.hide())},e._isWithActiveTrigger=function(){for(var t in this._activeTrigger)if(this._activeTrigger[t])return!0;return!1},e._getConfig=function(t){var e=i.default(this.element).data();return Object.keys(e).forEach((function(t){-1!==Kt.indexOf(t)&&delete e[t]})),"number"==typeof(t=a({},this.constructor.Default,e,"object"==typeof t&&t?t:{})).delay&&(t.delay={show:t.delay,hide:t.delay}),"number"==typeof t.title&&(t.title=t.title.toString()),"number"==typeof t.content&&(t.content=t.content.toString()),l.typeCheckConfig(Yt,t,this.constructor.DefaultType),t.sanitize&&(t.template=Vt(t.template,t.whiteList,t.sanitizeFn)),t},e._getDelegateConfig=function(){var t={};if(this.config)for(var e in this.config)this.constructor.Default[e]!==this.config[e]&&(t[e]=this.config[e]);return t},e._cleanTipClass=function(){var t=i.default(this.getTipElement()),e=t.attr("class").match(Xt);null!==e&&e.length&&t.removeClass(e.join(""))},e._handlePopperPlacementChange=function(t){this.tip=t.instance.popper,this._cleanTipClass(),this.addAttachmentClass(this._getAttachment(t.placement))},e._fixTransition=function(){var t=this.getTipElement(),e=this.config.animation;null===t.getAttribute("x-placement")&&(i.default(t).removeClass("fade"),this.config.animation=!1,this.hide(),this.show(),this.config.animation=e)},t._jQueryInterface=function(e){return this.each((function(){var n=i.default(this),o=n.data("bs.tooltip"),r="object"==typeof e&&e;if((o||!/dispose|hide/.test(e))&&(o||(o=new t(this,r),n.data("bs.tooltip",o)),"string"==typeof e)){if("undefined"==typeof o[e])throw new TypeError('No method named "'+e+'"');o[e]()}}))},r(t,null,[{key:"VERSION",get:function(){return"4.5.3"}},{key:"Default",get:function(){return Jt}},{key:"NAME",get:function(){return Yt}},{key:"DATA_KEY",get:function(){return"bs.tooltip"}},{key:"Event",get:function(){return Zt}},{key:"EVENT_KEY",get:function(){return".bs.tooltip"}},{key:"DefaultType",get:function(){return Gt}}]),t}();i.default.fn[Yt]=te._jQueryInterface,i.default.fn[Yt].Constructor=te,i.default.fn[Yt].noConflict=function(){return i.default.fn[Yt]=zt,te._jQueryInterface};var ee="popover",ne=i.default.fn[ee],ie=new RegExp("(^|\\s)bs-popover\\S+","g"),oe=a({},te.Default,{placement:"right",trigger:"click",content:"",template:'<div class="popover" role="tooltip"><div class="arrow"></div><h3 class="popover-header"></h3><div class="popover-body"></div></div>'}),re=a({},te.DefaultType,{content:"(string|element|function)"}),ae={HIDE:"hide.bs.popover",HIDDEN:"hidden.bs.popover",SHOW:"show.bs.popover",SHOWN:"shown.bs.popover",INSERTED:"inserted.bs.popover",CLICK:"click.bs.popover",FOCUSIN:"focusin.bs.popover",FOCUSOUT:"focusout.bs.popover",MOUSEENTER:"mouseenter.bs.popover",MOUSELEAVE:"mouseleave.bs.popover"},se=function(t){var e,n;function o(){return t.apply(this,arguments)||this}n=t,(e=o).prototype=Object.create(n.prototype),e.prototype.constructor=e,e.__proto__=n;var a=o.prototype;return a.isWithContent=function(){return this.getTitle()||this._getContent()},a.addAttachmentClass=function(t){i.default(this.getTipElement()).addClass("bs-popover-"+t)},a.getTipElement=function(){return this.tip=this.tip||i.default(this.config.template)[0],this.tip},a.setContent=function(){var t=i.default(this.getTipElement());this.setElementContent(t.find(".popover-header"),this.getTitle());var e=this._getContent();"function"==typeof e&&(e=e.call(this.element)),this.setElementContent(t.find(".popover-body"),e),t.removeClass("fade show")},a._getContent=function(){return this.element.getAttribute("data-content")||this.config.content},a._cleanTipClass=function(){var t=i.default(this.getTipElement()),e=t.attr("class").match(ie);null!==e&&e.length>0&&t.removeClass(e.join(""))},o._jQueryInterface=function(t){return this.each((function(){var e=i.default(this).data("bs.popover"),n="object"==typeof t?t:null;if((e||!/dispose|hide/.test(t))&&(e||(e=new o(this,n),i.default(this).data("bs.popover",e)),"string"==typeof t)){if("undefined"==typeof e[t])throw new TypeError('No method named "'+t+'"');e[t]()}}))},r(o,null,[{key:"VERSION",get:function(){return"4.5.3"}},{key:"Default",get:function(){return oe}},{key:"NAME",get:function(){return ee}},{key:"DATA_KEY",get:function(){return"bs.popover"}},{key:"Event",get:function(){return ae}},{key:"EVENT_KEY",get:function(){return".bs.popover"}},{key:"DefaultType",get:function(){return re}}]),o}(te);i.default.fn[ee]=se._jQueryInterface,i.default.fn[ee].Constructor=se,i.default.fn[ee].noConflict=function(){return i.default.fn[ee]=ne,se._jQueryInterface};var le="scrollspy",ue=i.default.fn[le],fe={offset:10,method:"auto",target:""},de={offset:"number",method:"string",target:"(string|element)"},ce=function(){function t(t,e){var n=this;this._element=t,this._scrollElement="BODY"===t.tagName?window:t,this._config=this._getConfig(e),this._selector=this._config.target+" .nav-link,"+this._config.target+" .list-group-item,"+this._config.target+" .dropdown-item",this._offsets=[],this._targets=[],this._activeTarget=null,this._scrollHeight=0,i.default(this._scrollElement).on("scroll.bs.scrollspy",(function(t){return n._process(t)})),this.refresh(),this._process()}var e=t.prototype;return e.refresh=function(){var t=this,e=this._scrollElement===this._scrollElement.window?"offset":"position",n="auto"===this._config.method?e:this._config.method,o="position"===n?this._getScrollTop():0;this._offsets=[],this._targets=[],this._scrollHeight=this._getScrollHeight(),[].slice.call(document.querySelectorAll(this._selector)).map((function(t){var e,r=l.getSelectorFromElement(t);if(r&&(e=document.querySelector(r)),e){var a=e.getBoundingClientRect();if(a.width||a.height)return[i.default(e)[n]().top+o,r]}return null})).filter((function(t){return t})).sort((function(t,e){return t[0]-e[0]})).forEach((function(e){t._offsets.push(e[0]),t._targets.push(e[1])}))},e.dispose=function(){i.default.removeData(this._element,"bs.scrollspy"),i.default(this._scrollElement).off(".bs.scrollspy"),this._element=null,this._scrollElement=null,this._config=null,this._selector=null,this._offsets=null,this._targets=null,this._activeTarget=null,this._scrollHeight=null},e._getConfig=function(t){if("string"!=typeof(t=a({},fe,"object"==typeof t&&t?t:{})).target&&l.isElement(t.target)){var e=i.default(t.target).attr("id");e||(e=l.getUID(le),i.default(t.target).attr("id",e)),t.target="#"+e}return l.typeCheckConfig(le,t,de),t},e._getScrollTop=function(){return this._scrollElement===window?this._scrollElement.pageYOffset:this._scrollElement.scrollTop},e._getScrollHeight=function(){return this._scrollElement.scrollHeight||Math.max(document.body.scrollHeight,document.documentElement.scrollHeight)},e._getOffsetHeight=function(){return this._scrollElement===window?window.innerHeight:this._scrollElement.getBoundingClientRect().height},e._process=function(){var t=this._getScrollTop()+this._config.offset,e=this._getScrollHeight(),n=this._config.offset+e-this._getOffsetHeight();if(this._scrollHeight!==e&&this.refresh(),t>=n){var i=this._targets[this._targets.length-1];this._activeTarget!==i&&this._activate(i)}else{if(this._activeTarget&&t<this._offsets[0]&&this._offsets[0]>0)return this._activeTarget=null,void this._clear();for(var o=this._offsets.length;o--;){this._activeTarget!==this._targets[o]&&t>=this._offsets[o]&&("undefined"==typeof this._offsets[o+1]||t<this._offsets[o+1])&&this._activate(this._targets[o])}}},e._activate=function(t){this._activeTarget=t,this._clear();var e=this._selector.split(",").map((function(e){return e+'[data-target="'+t+'"],'+e+'[href="'+t+'"]'})),n=i.default([].slice.call(document.querySelectorAll(e.join(","))));n.hasClass("dropdown-item")?(n.closest(".dropdown").find(".dropdown-toggle").addClass("active"),n.addClass("active")):(n.addClass("active"),n.parents(".nav, .list-group").prev(".nav-link, .list-group-item").addClass("active"),n.parents(".nav, .list-group").prev(".nav-item").children(".nav-link").addClass("active")),i.default(this._scrollElement).trigger("activate.bs.scrollspy",{relatedTarget:t})},e._clear=function(){[].slice.call(document.querySelectorAll(this._selector)).filter((function(t){return t.classList.contains("active")})).forEach((function(t){return t.classList.remove("active")}))},t._jQueryInterface=function(e){return this.each((function(){var n=i.default(this).data("bs.scrollspy");if(n||(n=new t(this,"object"==typeof e&&e),i.default(this).data("bs.scrollspy",n)),"string"==typeof e){if("undefined"==typeof n[e])throw new TypeError('No method named "'+e+'"');n[e]()}}))},r(t,null,[{key:"VERSION",get:function(){return"4.5.3"}},{key:"Default",get:function(){return fe}}]),t}();i.default(window).on("load.bs.scrollspy.data-api",(function(){for(var t=[].slice.call(document.querySelectorAll('[data-spy="scroll"]')),e=t.length;e--;){var n=i.default(t[e]);ce._jQueryInterface.call(n,n.data())}})),i.default.fn[le]=ce._jQueryInterface,i.default.fn[le].Constructor=ce,i.default.fn[le].noConflict=function(){return i.default.fn[le]=ue,ce._jQueryInterface};var he=i.default.fn.tab,pe=function(){function t(t){this._element=t}var e=t.prototype;return e.show=function(){var t=this;if(!(this._element.parentNode&&this._element.parentNode.nodeType===Node.ELEMENT_NODE&&i.default(this._element).hasClass("active")||i.default(this._element).hasClass("disabled"))){var e,n,o=i.default(this._element).closest(".nav, .list-group")[0],r=l.getSelectorFromElement(this._element);if(o){var a="UL"===o.nodeName||"OL"===o.nodeName?"> li > .active":".active";n=(n=i.default.makeArray(i.default(o).find(a)))[n.length-1]}var s=i.default.Event("hide.bs.tab",{relatedTarget:this._element}),u=i.default.Event("show.bs.tab",{relatedTarget:n});if(n&&i.default(n).trigger(s),i.default(this._element).trigger(u),!u.isDefaultPrevented()&&!s.isDefaultPrevented()){r&&(e=document.querySelector(r)),this._activate(this._element,o);var f=function(){var e=i.default.Event("hidden.bs.tab",{relatedTarget:t._element}),o=i.default.Event("shown.bs.tab",{relatedTarget:n});i.default(n).trigger(e),i.default(t._element).trigger(o)};e?this._activate(e,e.parentNode,f):f()}}},e.dispose=function(){i.default.removeData(this._element,"bs.tab"),this._element=null},e._activate=function(t,e,n){var o=this,r=(!e||"UL"!==e.nodeName&&"OL"!==e.nodeName?i.default(e).children(".active"):i.default(e).find("> li > .active"))[0],a=n&&r&&i.default(r).hasClass("fade"),s=function(){return o._transitionComplete(t,r,n)};if(r&&a){var u=l.getTransitionDurationFromElement(r);i.default(r).removeClass("show").one(l.TRANSITION_END,s).emulateTransitionEnd(u)}else s()},e._transitionComplete=function(t,e,n){if(e){i.default(e).removeClass("active");var o=i.default(e.parentNode).find("> .dropdown-menu .active")[0];o&&i.default(o).removeClass("active"),"tab"===e.getAttribute("role")&&e.setAttribute("aria-selected",!1)}if(i.default(t).addClass("active"),"tab"===t.getAttribute("role")&&t.setAttribute("aria-selected",!0),l.reflow(t),t.classList.contains("fade")&&t.classList.add("show"),t.parentNode&&i.default(t.parentNode).hasClass("dropdown-menu")){var r=i.default(t).closest(".dropdown")[0];if(r){var a=[].slice.call(r.querySelectorAll(".dropdown-toggle"));i.default(a).addClass("active")}t.setAttribute("aria-expanded",!0)}n&&n()},t._jQueryInterface=function(e){return this.each((function(){var n=i.default(this),o=n.data("bs.tab");if(o||(o=new t(this),n.data("bs.tab",o)),"string"==typeof e){if("undefined"==typeof o[e])throw new TypeError('No method named "'+e+'"');o[e]()}}))},r(t,null,[{key:"VERSION",get:function(){return"4.5.3"}}]),t}();i.default(document).on("click.bs.tab.data-api",'[data-toggle="tab"], [data-toggle="pill"], [data-toggle="list"]',(function(t){t.preventDefault(),pe._jQueryInterface.call(i.default(this),"show")})),i.default.fn.tab=pe._jQueryInterface,i.default.fn.tab.Constructor=pe,i.default.fn.tab.noConflict=function(){return i.default.fn.tab=he,pe._jQueryInterface};var me=i.default.fn.toast,ge={animation:"boolean",autohide:"boolean",delay:"number"},ve={animation:!0,autohide:!0,delay:500},_e=function(){function t(t,e){this._element=t,this._config=this._getConfig(e),this._timeout=null,this._setListeners()}var e=t.prototype;return e.show=function(){var t=this,e=i.default.Event("show.bs.toast");if(i.default(this._element).trigger(e),!e.isDefaultPrevented()){this._clearTimeout(),this._config.animation&&this._element.classList.add("fade");var n=function(){t._element.classList.remove("showing"),t._element.classList.add("show"),i.default(t._element).trigger("shown.bs.toast"),t._config.autohide&&(t._timeout=setTimeout((function(){t.hide()}),t._config.delay))};if(this._element.classList.remove("hide"),l.reflow(this._element),this._element.classList.add("showing"),this._config.animation){var o=l.getTransitionDurationFromElement(this._element);i.default(this._element).one(l.TRANSITION_END,n).emulateTransitionEnd(o)}else n()}},e.hide=function(){if(this._element.classList.contains("show")){var t=i.default.Event("hide.bs.toast");i.default(this._element).trigger(t),t.isDefaultPrevented()||this._close()}},e.dispose=function(){this._clearTimeout(),this._element.classList.contains("show")&&this._element.classList.remove("show"),i.default(this._element).off("click.dismiss.bs.toast"),i.default.removeData(this._element,"bs.toast"),this._element=null,this._config=null},e._getConfig=function(t){return t=a({},ve,i.default(this._element).data(),"object"==typeof t&&t?t:{}),l.typeCheckConfig("toast",t,this.constructor.DefaultType),t},e._setListeners=function(){var t=this;i.default(this._element).on("click.dismiss.bs.toast",'[data-dismiss="toast"]',(function(){return t.hide()}))},e._close=function(){var t=this,e=function(){t._element.classList.add("hide"),i.default(t._element).trigger("hidden.bs.toast")};if(this._element.classList.remove("show"),this._config.animation){var n=l.getTransitionDurationFromElement(this._element);i.default(this._element).one(l.TRANSITION_END,e).emulateTransitionEnd(n)}else e()},e._clearTimeout=function(){clearTimeout(this._timeout),this._timeout=null},t._jQueryInterface=function(e){return this.each((function(){var n=i.default(this),o=n.data("bs.toast");if(o||(o=new t(this,"object"==typeof e&&e),n.data("bs.toast",o)),"string"==typeof e){if("undefined"==typeof o[e])throw new TypeError('No method named "'+e+'"');o[e](this)}}))},r(t,null,[{key:"VERSION",get:function(){return"4.5.3"}},{key:"DefaultType",get:function(){return ge}},{key:"Default",get:function(){return ve}}]),t}();i.default.fn.toast=_e._jQueryInterface,i.default.fn.toast.Constructor=_e,i.default.fn.toast.noConflict=function(){return i.default.fn.toast=me,_e._jQueryInterface},t.Alert=d,t.Button=h,t.Carousel=y,t.Collapse=S,t.Dropdown=Ft,t.Modal=Bt,t.Popover=se,t.Scrollspy=ce,t.Tab=pe,t.Toast=_e,t.Tooltip=te,t.Util=l,Object.defineProperty(t,"__esModule",{value:!0})}));
|
7 |
-
//# sourceMappingURL=bootstrap.bundle.min.js.map
|
|
|
|
|
|
|
|
|
|
|
|
|
|
resources/js/bootstrap4.min.js
DELETED
@@ -1,7 +0,0 @@
|
|
1 |
-
/*!
|
2 |
-
* Bootstrap v4.5.3 (https://getbootstrap.com/)
|
3 |
-
* Copyright 2011-2020 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)
|
4 |
-
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
|
5 |
-
*/
|
6 |
-
!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?e(exports,require("jquery"),require("popper.js")):"function"==typeof define&&define.amd?define(["exports","jquery","popper.js"],e):e((t="undefined"!=typeof globalThis?globalThis:t||self).bootstrap={},t.jQuery,t.Popper)}(this,(function(t,e,n){"use strict";function i(t){return t&&"object"==typeof t&&"default"in t?t:{default:t}}var o=i(e),a=i(n);function s(t,e){for(var n=0;n<e.length;n++){var i=e[n];i.enumerable=i.enumerable||!1,i.configurable=!0,"value"in i&&(i.writable=!0),Object.defineProperty(t,i.key,i)}}function l(t,e,n){return e&&s(t.prototype,e),n&&s(t,n),t}function r(){return(r=Object.assign||function(t){for(var e=1;e<arguments.length;e++){var n=arguments[e];for(var i in n)Object.prototype.hasOwnProperty.call(n,i)&&(t[i]=n[i])}return t}).apply(this,arguments)}function u(t){var e=this,n=!1;return o.default(this).one(d.TRANSITION_END,(function(){n=!0})),setTimeout((function(){n||d.triggerTransitionEnd(e)}),t),this}var d={TRANSITION_END:"bsTransitionEnd",getUID:function(t){do{t+=~~(1e6*Math.random())}while(document.getElementById(t));return t},getSelectorFromElement:function(t){var e=t.getAttribute("data-target");if(!e||"#"===e){var n=t.getAttribute("href");e=n&&"#"!==n?n.trim():""}try{return document.querySelector(e)?e:null}catch(t){return null}},getTransitionDurationFromElement:function(t){if(!t)return 0;var e=o.default(t).css("transition-duration"),n=o.default(t).css("transition-delay"),i=parseFloat(e),a=parseFloat(n);return i||a?(e=e.split(",")[0],n=n.split(",")[0],1e3*(parseFloat(e)+parseFloat(n))):0},reflow:function(t){return t.offsetHeight},triggerTransitionEnd:function(t){o.default(t).trigger("transitionend")},supportsTransitionEnd:function(){return Boolean("transitionend")},isElement:function(t){return(t[0]||t).nodeType},typeCheckConfig:function(t,e,n){for(var i in n)if(Object.prototype.hasOwnProperty.call(n,i)){var o=n[i],a=e[i],s=a&&d.isElement(a)?"element":null===(l=a)||"undefined"==typeof l?""+l:{}.toString.call(l).match(/\s([a-z]+)/i)[1].toLowerCase();if(!new RegExp(o).test(s))throw new Error(t.toUpperCase()+': Option "'+i+'" provided type "'+s+'" but expected type "'+o+'".')}var l},findShadowRoot:function(t){if(!document.documentElement.attachShadow)return null;if("function"==typeof t.getRootNode){var e=t.getRootNode();return e instanceof ShadowRoot?e:null}return t instanceof ShadowRoot?t:t.parentNode?d.findShadowRoot(t.parentNode):null},jQueryDetection:function(){if("undefined"==typeof o.default)throw new TypeError("Bootstrap's JavaScript requires jQuery. jQuery must be included before Bootstrap's JavaScript.");var t=o.default.fn.jquery.split(" ")[0].split(".");if(t[0]<2&&t[1]<9||1===t[0]&&9===t[1]&&t[2]<1||t[0]>=4)throw new Error("Bootstrap's JavaScript requires at least jQuery v1.9.1 but less than v4.0.0")}};d.jQueryDetection(),o.default.fn.emulateTransitionEnd=u,o.default.event.special[d.TRANSITION_END]={bindType:"transitionend",delegateType:"transitionend",handle:function(t){if(o.default(t.target).is(this))return t.handleObj.handler.apply(this,arguments)}};var f="alert",c=o.default.fn[f],h=function(){function t(t){this._element=t}var e=t.prototype;return e.close=function(t){var e=this._element;t&&(e=this._getRootElement(t)),this._triggerCloseEvent(e).isDefaultPrevented()||this._removeElement(e)},e.dispose=function(){o.default.removeData(this._element,"bs.alert"),this._element=null},e._getRootElement=function(t){var e=d.getSelectorFromElement(t),n=!1;return e&&(n=document.querySelector(e)),n||(n=o.default(t).closest(".alert")[0]),n},e._triggerCloseEvent=function(t){var e=o.default.Event("close.bs.alert");return o.default(t).trigger(e),e},e._removeElement=function(t){var e=this;if(o.default(t).removeClass("show"),o.default(t).hasClass("fade")){var n=d.getTransitionDurationFromElement(t);o.default(t).one(d.TRANSITION_END,(function(n){return e._destroyElement(t,n)})).emulateTransitionEnd(n)}else this._destroyElement(t)},e._destroyElement=function(t){o.default(t).detach().trigger("closed.bs.alert").remove()},t._jQueryInterface=function(e){return this.each((function(){var n=o.default(this),i=n.data("bs.alert");i||(i=new t(this),n.data("bs.alert",i)),"close"===e&&i[e](this)}))},t._handleDismiss=function(t){return function(e){e&&e.preventDefault(),t.close(this)}},l(t,null,[{key:"VERSION",get:function(){return"4.5.3"}}]),t}();o.default(document).on("click.bs.alert.data-api",'[data-dismiss="alert"]',h._handleDismiss(new h)),o.default.fn[f]=h._jQueryInterface,o.default.fn[f].Constructor=h,o.default.fn[f].noConflict=function(){return o.default.fn[f]=c,h._jQueryInterface};var g=o.default.fn.button,m=function(){function t(t){this._element=t,this.shouldAvoidTriggerChange=!1}var e=t.prototype;return e.toggle=function(){var t=!0,e=!0,n=o.default(this._element).closest('[data-toggle="buttons"]')[0];if(n){var i=this._element.querySelector('input:not([type="hidden"])');if(i){if("radio"===i.type)if(i.checked&&this._element.classList.contains("active"))t=!1;else{var a=n.querySelector(".active");a&&o.default(a).removeClass("active")}t&&("checkbox"!==i.type&&"radio"!==i.type||(i.checked=!this._element.classList.contains("active")),this.shouldAvoidTriggerChange||o.default(i).trigger("change")),i.focus(),e=!1}}this._element.hasAttribute("disabled")||this._element.classList.contains("disabled")||(e&&this._element.setAttribute("aria-pressed",!this._element.classList.contains("active")),t&&o.default(this._element).toggleClass("active"))},e.dispose=function(){o.default.removeData(this._element,"bs.button"),this._element=null},t._jQueryInterface=function(e,n){return this.each((function(){var i=o.default(this),a=i.data("bs.button");a||(a=new t(this),i.data("bs.button",a)),a.shouldAvoidTriggerChange=n,"toggle"===e&&a[e]()}))},l(t,null,[{key:"VERSION",get:function(){return"4.5.3"}}]),t}();o.default(document).on("click.bs.button.data-api",'[data-toggle^="button"]',(function(t){var e=t.target,n=e;if(o.default(e).hasClass("btn")||(e=o.default(e).closest(".btn")[0]),!e||e.hasAttribute("disabled")||e.classList.contains("disabled"))t.preventDefault();else{var i=e.querySelector('input:not([type="hidden"])');if(i&&(i.hasAttribute("disabled")||i.classList.contains("disabled")))return void t.preventDefault();"INPUT"!==n.tagName&&"LABEL"===e.tagName||m._jQueryInterface.call(o.default(e),"toggle","INPUT"===n.tagName)}})).on("focus.bs.button.data-api blur.bs.button.data-api",'[data-toggle^="button"]',(function(t){var e=o.default(t.target).closest(".btn")[0];o.default(e).toggleClass("focus",/^focus(in)?$/.test(t.type))})),o.default(window).on("load.bs.button.data-api",(function(){for(var t=[].slice.call(document.querySelectorAll('[data-toggle="buttons"] .btn')),e=0,n=t.length;e<n;e++){var i=t[e],o=i.querySelector('input:not([type="hidden"])');o.checked||o.hasAttribute("checked")?i.classList.add("active"):i.classList.remove("active")}for(var a=0,s=(t=[].slice.call(document.querySelectorAll('[data-toggle="button"]'))).length;a<s;a++){var l=t[a];"true"===l.getAttribute("aria-pressed")?l.classList.add("active"):l.classList.remove("active")}})),o.default.fn.button=m._jQueryInterface,o.default.fn.button.Constructor=m,o.default.fn.button.noConflict=function(){return o.default.fn.button=g,m._jQueryInterface};var p="carousel",_=".bs.carousel",v=o.default.fn[p],b={interval:5e3,keyboard:!0,slide:!1,pause:"hover",wrap:!0,touch:!0},y={interval:"(number|boolean)",keyboard:"boolean",slide:"(boolean|string)",pause:"(string|boolean)",wrap:"boolean",touch:"boolean"},E={TOUCH:"touch",PEN:"pen"},w=function(){function t(t,e){this._items=null,this._interval=null,this._activeElement=null,this._isPaused=!1,this._isSliding=!1,this.touchTimeout=null,this.touchStartX=0,this.touchDeltaX=0,this._config=this._getConfig(e),this._element=t,this._indicatorsElement=this._element.querySelector(".carousel-indicators"),this._touchSupported="ontouchstart"in document.documentElement||navigator.maxTouchPoints>0,this._pointerEvent=Boolean(window.PointerEvent||window.MSPointerEvent),this._addEventListeners()}var e=t.prototype;return e.next=function(){this._isSliding||this._slide("next")},e.nextWhenVisible=function(){var t=o.default(this._element);!document.hidden&&t.is(":visible")&&"hidden"!==t.css("visibility")&&this.next()},e.prev=function(){this._isSliding||this._slide("prev")},e.pause=function(t){t||(this._isPaused=!0),this._element.querySelector(".carousel-item-next, .carousel-item-prev")&&(d.triggerTransitionEnd(this._element),this.cycle(!0)),clearInterval(this._interval),this._interval=null},e.cycle=function(t){t||(this._isPaused=!1),this._interval&&(clearInterval(this._interval),this._interval=null),this._config.interval&&!this._isPaused&&(this._interval=setInterval((document.visibilityState?this.nextWhenVisible:this.next).bind(this),this._config.interval))},e.to=function(t){var e=this;this._activeElement=this._element.querySelector(".active.carousel-item");var n=this._getItemIndex(this._activeElement);if(!(t>this._items.length-1||t<0))if(this._isSliding)o.default(this._element).one("slid.bs.carousel",(function(){return e.to(t)}));else{if(n===t)return this.pause(),void this.cycle();var i=t>n?"next":"prev";this._slide(i,this._items[t])}},e.dispose=function(){o.default(this._element).off(_),o.default.removeData(this._element,"bs.carousel"),this._items=null,this._config=null,this._element=null,this._interval=null,this._isPaused=null,this._isSliding=null,this._activeElement=null,this._indicatorsElement=null},e._getConfig=function(t){return t=r({},b,t),d.typeCheckConfig(p,t,y),t},e._handleSwipe=function(){var t=Math.abs(this.touchDeltaX);if(!(t<=40)){var e=t/this.touchDeltaX;this.touchDeltaX=0,e>0&&this.prev(),e<0&&this.next()}},e._addEventListeners=function(){var t=this;this._config.keyboard&&o.default(this._element).on("keydown.bs.carousel",(function(e){return t._keydown(e)})),"hover"===this._config.pause&&o.default(this._element).on("mouseenter.bs.carousel",(function(e){return t.pause(e)})).on("mouseleave.bs.carousel",(function(e){return t.cycle(e)})),this._config.touch&&this._addTouchEventListeners()},e._addTouchEventListeners=function(){var t=this;if(this._touchSupported){var e=function(e){t._pointerEvent&&E[e.originalEvent.pointerType.toUpperCase()]?t.touchStartX=e.originalEvent.clientX:t._pointerEvent||(t.touchStartX=e.originalEvent.touches[0].clientX)},n=function(e){t._pointerEvent&&E[e.originalEvent.pointerType.toUpperCase()]&&(t.touchDeltaX=e.originalEvent.clientX-t.touchStartX),t._handleSwipe(),"hover"===t._config.pause&&(t.pause(),t.touchTimeout&&clearTimeout(t.touchTimeout),t.touchTimeout=setTimeout((function(e){return t.cycle(e)}),500+t._config.interval))};o.default(this._element.querySelectorAll(".carousel-item img")).on("dragstart.bs.carousel",(function(t){return t.preventDefault()})),this._pointerEvent?(o.default(this._element).on("pointerdown.bs.carousel",(function(t){return e(t)})),o.default(this._element).on("pointerup.bs.carousel",(function(t){return n(t)})),this._element.classList.add("pointer-event")):(o.default(this._element).on("touchstart.bs.carousel",(function(t){return e(t)})),o.default(this._element).on("touchmove.bs.carousel",(function(e){return function(e){e.originalEvent.touches&&e.originalEvent.touches.length>1?t.touchDeltaX=0:t.touchDeltaX=e.originalEvent.touches[0].clientX-t.touchStartX}(e)})),o.default(this._element).on("touchend.bs.carousel",(function(t){return n(t)})))}},e._keydown=function(t){if(!/input|textarea/i.test(t.target.tagName))switch(t.which){case 37:t.preventDefault(),this.prev();break;case 39:t.preventDefault(),this.next()}},e._getItemIndex=function(t){return this._items=t&&t.parentNode?[].slice.call(t.parentNode.querySelectorAll(".carousel-item")):[],this._items.indexOf(t)},e._getItemByDirection=function(t,e){var n="next"===t,i="prev"===t,o=this._getItemIndex(e),a=this._items.length-1;if((i&&0===o||n&&o===a)&&!this._config.wrap)return e;var s=(o+("prev"===t?-1:1))%this._items.length;return-1===s?this._items[this._items.length-1]:this._items[s]},e._triggerSlideEvent=function(t,e){var n=this._getItemIndex(t),i=this._getItemIndex(this._element.querySelector(".active.carousel-item")),a=o.default.Event("slide.bs.carousel",{relatedTarget:t,direction:e,from:i,to:n});return o.default(this._element).trigger(a),a},e._setActiveIndicatorElement=function(t){if(this._indicatorsElement){var e=[].slice.call(this._indicatorsElement.querySelectorAll(".active"));o.default(e).removeClass("active");var n=this._indicatorsElement.children[this._getItemIndex(t)];n&&o.default(n).addClass("active")}},e._slide=function(t,e){var n,i,a,s=this,l=this._element.querySelector(".active.carousel-item"),r=this._getItemIndex(l),u=e||l&&this._getItemByDirection(t,l),f=this._getItemIndex(u),c=Boolean(this._interval);if("next"===t?(n="carousel-item-left",i="carousel-item-next",a="left"):(n="carousel-item-right",i="carousel-item-prev",a="right"),u&&o.default(u).hasClass("active"))this._isSliding=!1;else if(!this._triggerSlideEvent(u,a).isDefaultPrevented()&&l&&u){this._isSliding=!0,c&&this.pause(),this._setActiveIndicatorElement(u);var h=o.default.Event("slid.bs.carousel",{relatedTarget:u,direction:a,from:r,to:f});if(o.default(this._element).hasClass("slide")){o.default(u).addClass(i),d.reflow(u),o.default(l).addClass(n),o.default(u).addClass(n);var g=parseInt(u.getAttribute("data-interval"),10);g?(this._config.defaultInterval=this._config.defaultInterval||this._config.interval,this._config.interval=g):this._config.interval=this._config.defaultInterval||this._config.interval;var m=d.getTransitionDurationFromElement(l);o.default(l).one(d.TRANSITION_END,(function(){o.default(u).removeClass(n+" "+i).addClass("active"),o.default(l).removeClass("active "+i+" "+n),s._isSliding=!1,setTimeout((function(){return o.default(s._element).trigger(h)}),0)})).emulateTransitionEnd(m)}else o.default(l).removeClass("active"),o.default(u).addClass("active"),this._isSliding=!1,o.default(this._element).trigger(h);c&&this.cycle()}},t._jQueryInterface=function(e){return this.each((function(){var n=o.default(this).data("bs.carousel"),i=r({},b,o.default(this).data());"object"==typeof e&&(i=r({},i,e));var a="string"==typeof e?e:i.slide;if(n||(n=new t(this,i),o.default(this).data("bs.carousel",n)),"number"==typeof e)n.to(e);else if("string"==typeof a){if("undefined"==typeof n[a])throw new TypeError('No method named "'+a+'"');n[a]()}else i.interval&&i.ride&&(n.pause(),n.cycle())}))},t._dataApiClickHandler=function(e){var n=d.getSelectorFromElement(this);if(n){var i=o.default(n)[0];if(i&&o.default(i).hasClass("carousel")){var a=r({},o.default(i).data(),o.default(this).data()),s=this.getAttribute("data-slide-to");s&&(a.interval=!1),t._jQueryInterface.call(o.default(i),a),s&&o.default(i).data("bs.carousel").to(s),e.preventDefault()}}},l(t,null,[{key:"VERSION",get:function(){return"4.5.3"}},{key:"Default",get:function(){return b}}]),t}();o.default(document).on("click.bs.carousel.data-api","[data-slide], [data-slide-to]",w._dataApiClickHandler),o.default(window).on("load.bs.carousel.data-api",(function(){for(var t=[].slice.call(document.querySelectorAll('[data-ride="carousel"]')),e=0,n=t.length;e<n;e++){var i=o.default(t[e]);w._jQueryInterface.call(i,i.data())}})),o.default.fn[p]=w._jQueryInterface,o.default.fn[p].Constructor=w,o.default.fn[p].noConflict=function(){return o.default.fn[p]=v,w._jQueryInterface};var T="collapse",C=o.default.fn[T],S={toggle:!0,parent:""},N={toggle:"boolean",parent:"(string|element)"},D=function(){function t(t,e){this._isTransitioning=!1,this._element=t,this._config=this._getConfig(e),this._triggerArray=[].slice.call(document.querySelectorAll('[data-toggle="collapse"][href="#'+t.id+'"],[data-toggle="collapse"][data-target="#'+t.id+'"]'));for(var n=[].slice.call(document.querySelectorAll('[data-toggle="collapse"]')),i=0,o=n.length;i<o;i++){var a=n[i],s=d.getSelectorFromElement(a),l=[].slice.call(document.querySelectorAll(s)).filter((function(e){return e===t}));null!==s&&l.length>0&&(this._selector=s,this._triggerArray.push(a))}this._parent=this._config.parent?this._getParent():null,this._config.parent||this._addAriaAndCollapsedClass(this._element,this._triggerArray),this._config.toggle&&this.toggle()}var e=t.prototype;return e.toggle=function(){o.default(this._element).hasClass("show")?this.hide():this.show()},e.show=function(){var e,n,i=this;if(!this._isTransitioning&&!o.default(this._element).hasClass("show")&&(this._parent&&0===(e=[].slice.call(this._parent.querySelectorAll(".show, .collapsing")).filter((function(t){return"string"==typeof i._config.parent?t.getAttribute("data-parent")===i._config.parent:t.classList.contains("collapse")}))).length&&(e=null),!(e&&(n=o.default(e).not(this._selector).data("bs.collapse"))&&n._isTransitioning))){var a=o.default.Event("show.bs.collapse");if(o.default(this._element).trigger(a),!a.isDefaultPrevented()){e&&(t._jQueryInterface.call(o.default(e).not(this._selector),"hide"),n||o.default(e).data("bs.collapse",null));var s=this._getDimension();o.default(this._element).removeClass("collapse").addClass("collapsing"),this._element.style[s]=0,this._triggerArray.length&&o.default(this._triggerArray).removeClass("collapsed").attr("aria-expanded",!0),this.setTransitioning(!0);var l="scroll"+(s[0].toUpperCase()+s.slice(1)),r=d.getTransitionDurationFromElement(this._element);o.default(this._element).one(d.TRANSITION_END,(function(){o.default(i._element).removeClass("collapsing").addClass("collapse show"),i._element.style[s]="",i.setTransitioning(!1),o.default(i._element).trigger("shown.bs.collapse")})).emulateTransitionEnd(r),this._element.style[s]=this._element[l]+"px"}}},e.hide=function(){var t=this;if(!this._isTransitioning&&o.default(this._element).hasClass("show")){var e=o.default.Event("hide.bs.collapse");if(o.default(this._element).trigger(e),!e.isDefaultPrevented()){var n=this._getDimension();this._element.style[n]=this._element.getBoundingClientRect()[n]+"px",d.reflow(this._element),o.default(this._element).addClass("collapsing").removeClass("collapse show");var i=this._triggerArray.length;if(i>0)for(var a=0;a<i;a++){var s=this._triggerArray[a],l=d.getSelectorFromElement(s);if(null!==l)o.default([].slice.call(document.querySelectorAll(l))).hasClass("show")||o.default(s).addClass("collapsed").attr("aria-expanded",!1)}this.setTransitioning(!0);this._element.style[n]="";var r=d.getTransitionDurationFromElement(this._element);o.default(this._element).one(d.TRANSITION_END,(function(){t.setTransitioning(!1),o.default(t._element).removeClass("collapsing").addClass("collapse").trigger("hidden.bs.collapse")})).emulateTransitionEnd(r)}}},e.setTransitioning=function(t){this._isTransitioning=t},e.dispose=function(){o.default.removeData(this._element,"bs.collapse"),this._config=null,this._parent=null,this._element=null,this._triggerArray=null,this._isTransitioning=null},e._getConfig=function(t){return(t=r({},S,t)).toggle=Boolean(t.toggle),d.typeCheckConfig(T,t,N),t},e._getDimension=function(){return o.default(this._element).hasClass("width")?"width":"height"},e._getParent=function(){var e,n=this;d.isElement(this._config.parent)?(e=this._config.parent,"undefined"!=typeof this._config.parent.jquery&&(e=this._config.parent[0])):e=document.querySelector(this._config.parent);var i='[data-toggle="collapse"][data-parent="'+this._config.parent+'"]',a=[].slice.call(e.querySelectorAll(i));return o.default(a).each((function(e,i){n._addAriaAndCollapsedClass(t._getTargetFromElement(i),[i])})),e},e._addAriaAndCollapsedClass=function(t,e){var n=o.default(t).hasClass("show");e.length&&o.default(e).toggleClass("collapsed",!n).attr("aria-expanded",n)},t._getTargetFromElement=function(t){var e=d.getSelectorFromElement(t);return e?document.querySelector(e):null},t._jQueryInterface=function(e){return this.each((function(){var n=o.default(this),i=n.data("bs.collapse"),a=r({},S,n.data(),"object"==typeof e&&e?e:{});if(!i&&a.toggle&&"string"==typeof e&&/show|hide/.test(e)&&(a.toggle=!1),i||(i=new t(this,a),n.data("bs.collapse",i)),"string"==typeof e){if("undefined"==typeof i[e])throw new TypeError('No method named "'+e+'"');i[e]()}}))},l(t,null,[{key:"VERSION",get:function(){return"4.5.3"}},{key:"Default",get:function(){return S}}]),t}();o.default(document).on("click.bs.collapse.data-api",'[data-toggle="collapse"]',(function(t){"A"===t.currentTarget.tagName&&t.preventDefault();var e=o.default(this),n=d.getSelectorFromElement(this),i=[].slice.call(document.querySelectorAll(n));o.default(i).each((function(){var t=o.default(this),n=t.data("bs.collapse")?"toggle":e.data();D._jQueryInterface.call(t,n)}))})),o.default.fn[T]=D._jQueryInterface,o.default.fn[T].Constructor=D,o.default.fn[T].noConflict=function(){return o.default.fn[T]=C,D._jQueryInterface};var k="dropdown",A=o.default.fn[k],I=new RegExp("38|40|27"),j={offset:0,flip:!0,boundary:"scrollParent",reference:"toggle",display:"dynamic",popperConfig:null},O={offset:"(number|string|function)",flip:"boolean",boundary:"(string|element)",reference:"(string|element)",display:"string",popperConfig:"(null|object)"},x=function(){function t(t,e){this._element=t,this._popper=null,this._config=this._getConfig(e),this._menu=this._getMenuElement(),this._inNavbar=this._detectNavbar(),this._addEventListeners()}var e=t.prototype;return e.toggle=function(){if(!this._element.disabled&&!o.default(this._element).hasClass("disabled")){var e=o.default(this._menu).hasClass("show");t._clearMenus(),e||this.show(!0)}},e.show=function(e){if(void 0===e&&(e=!1),!(this._element.disabled||o.default(this._element).hasClass("disabled")||o.default(this._menu).hasClass("show"))){var n={relatedTarget:this._element},i=o.default.Event("show.bs.dropdown",n),s=t._getParentFromElement(this._element);if(o.default(s).trigger(i),!i.isDefaultPrevented()){if(!this._inNavbar&&e){if("undefined"==typeof a.default)throw new TypeError("Bootstrap's dropdowns require Popper.js (https://popper.js.org/)");var l=this._element;"parent"===this._config.reference?l=s:d.isElement(this._config.reference)&&(l=this._config.reference,"undefined"!=typeof this._config.reference.jquery&&(l=this._config.reference[0])),"scrollParent"!==this._config.boundary&&o.default(s).addClass("position-static"),this._popper=new a.default(l,this._menu,this._getPopperConfig())}"ontouchstart"in document.documentElement&&0===o.default(s).closest(".navbar-nav").length&&o.default(document.body).children().on("mouseover",null,o.default.noop),this._element.focus(),this._element.setAttribute("aria-expanded",!0),o.default(this._menu).toggleClass("show"),o.default(s).toggleClass("show").trigger(o.default.Event("shown.bs.dropdown",n))}}},e.hide=function(){if(!this._element.disabled&&!o.default(this._element).hasClass("disabled")&&o.default(this._menu).hasClass("show")){var e={relatedTarget:this._element},n=o.default.Event("hide.bs.dropdown",e),i=t._getParentFromElement(this._element);o.default(i).trigger(n),n.isDefaultPrevented()||(this._popper&&this._popper.destroy(),o.default(this._menu).toggleClass("show"),o.default(i).toggleClass("show").trigger(o.default.Event("hidden.bs.dropdown",e)))}},e.dispose=function(){o.default.removeData(this._element,"bs.dropdown"),o.default(this._element).off(".bs.dropdown"),this._element=null,this._menu=null,null!==this._popper&&(this._popper.destroy(),this._popper=null)},e.update=function(){this._inNavbar=this._detectNavbar(),null!==this._popper&&this._popper.scheduleUpdate()},e._addEventListeners=function(){var t=this;o.default(this._element).on("click.bs.dropdown",(function(e){e.preventDefault(),e.stopPropagation(),t.toggle()}))},e._getConfig=function(t){return t=r({},this.constructor.Default,o.default(this._element).data(),t),d.typeCheckConfig(k,t,this.constructor.DefaultType),t},e._getMenuElement=function(){if(!this._menu){var e=t._getParentFromElement(this._element);e&&(this._menu=e.querySelector(".dropdown-menu"))}return this._menu},e._getPlacement=function(){var t=o.default(this._element.parentNode),e="bottom-start";return t.hasClass("dropup")?e=o.default(this._menu).hasClass("dropdown-menu-right")?"top-end":"top-start":t.hasClass("dropright")?e="right-start":t.hasClass("dropleft")?e="left-start":o.default(this._menu).hasClass("dropdown-menu-right")&&(e="bottom-end"),e},e._detectNavbar=function(){return o.default(this._element).closest(".navbar").length>0},e._getOffset=function(){var t=this,e={};return"function"==typeof this._config.offset?e.fn=function(e){return e.offsets=r({},e.offsets,t._config.offset(e.offsets,t._element)||{}),e}:e.offset=this._config.offset,e},e._getPopperConfig=function(){var t={placement:this._getPlacement(),modifiers:{offset:this._getOffset(),flip:{enabled:this._config.flip},preventOverflow:{boundariesElement:this._config.boundary}}};return"static"===this._config.display&&(t.modifiers.applyStyle={enabled:!1}),r({},t,this._config.popperConfig)},t._jQueryInterface=function(e){return this.each((function(){var n=o.default(this).data("bs.dropdown");if(n||(n=new t(this,"object"==typeof e?e:null),o.default(this).data("bs.dropdown",n)),"string"==typeof e){if("undefined"==typeof n[e])throw new TypeError('No method named "'+e+'"');n[e]()}}))},t._clearMenus=function(e){if(!e||3!==e.which&&("keyup"!==e.type||9===e.which))for(var n=[].slice.call(document.querySelectorAll('[data-toggle="dropdown"]')),i=0,a=n.length;i<a;i++){var s=t._getParentFromElement(n[i]),l=o.default(n[i]).data("bs.dropdown"),r={relatedTarget:n[i]};if(e&&"click"===e.type&&(r.clickEvent=e),l){var u=l._menu;if(o.default(s).hasClass("show")&&!(e&&("click"===e.type&&/input|textarea/i.test(e.target.tagName)||"keyup"===e.type&&9===e.which)&&o.default.contains(s,e.target))){var d=o.default.Event("hide.bs.dropdown",r);o.default(s).trigger(d),d.isDefaultPrevented()||("ontouchstart"in document.documentElement&&o.default(document.body).children().off("mouseover",null,o.default.noop),n[i].setAttribute("aria-expanded","false"),l._popper&&l._popper.destroy(),o.default(u).removeClass("show"),o.default(s).removeClass("show").trigger(o.default.Event("hidden.bs.dropdown",r)))}}}},t._getParentFromElement=function(t){var e,n=d.getSelectorFromElement(t);return n&&(e=document.querySelector(n)),e||t.parentNode},t._dataApiKeydownHandler=function(e){if(!(/input|textarea/i.test(e.target.tagName)?32===e.which||27!==e.which&&(40!==e.which&&38!==e.which||o.default(e.target).closest(".dropdown-menu").length):!I.test(e.which))&&!this.disabled&&!o.default(this).hasClass("disabled")){var n=t._getParentFromElement(this),i=o.default(n).hasClass("show");if(i||27!==e.which){if(e.preventDefault(),e.stopPropagation(),!i||27===e.which||32===e.which)return 27===e.which&&o.default(n.querySelector('[data-toggle="dropdown"]')).trigger("focus"),void o.default(this).trigger("click");var a=[].slice.call(n.querySelectorAll(".dropdown-menu .dropdown-item:not(.disabled):not(:disabled)")).filter((function(t){return o.default(t).is(":visible")}));if(0!==a.length){var s=a.indexOf(e.target);38===e.which&&s>0&&s--,40===e.which&&s<a.length-1&&s++,s<0&&(s=0),a[s].focus()}}}},l(t,null,[{key:"VERSION",get:function(){return"4.5.3"}},{key:"Default",get:function(){return j}},{key:"DefaultType",get:function(){return O}}]),t}();o.default(document).on("keydown.bs.dropdown.data-api",'[data-toggle="dropdown"]',x._dataApiKeydownHandler).on("keydown.bs.dropdown.data-api",".dropdown-menu",x._dataApiKeydownHandler).on("click.bs.dropdown.data-api keyup.bs.dropdown.data-api",x._clearMenus).on("click.bs.dropdown.data-api",'[data-toggle="dropdown"]',(function(t){t.preventDefault(),t.stopPropagation(),x._jQueryInterface.call(o.default(this),"toggle")})).on("click.bs.dropdown.data-api",".dropdown form",(function(t){t.stopPropagation()})),o.default.fn[k]=x._jQueryInterface,o.default.fn[k].Constructor=x,o.default.fn[k].noConflict=function(){return o.default.fn[k]=A,x._jQueryInterface};var P=o.default.fn.modal,R={backdrop:!0,keyboard:!0,focus:!0,show:!0},L={backdrop:"(boolean|string)",keyboard:"boolean",focus:"boolean",show:"boolean"},q=function(){function t(t,e){this._config=this._getConfig(e),this._element=t,this._dialog=t.querySelector(".modal-dialog"),this._backdrop=null,this._isShown=!1,this._isBodyOverflowing=!1,this._ignoreBackdropClick=!1,this._isTransitioning=!1,this._scrollbarWidth=0}var e=t.prototype;return e.toggle=function(t){return this._isShown?this.hide():this.show(t)},e.show=function(t){var e=this;if(!this._isShown&&!this._isTransitioning){o.default(this._element).hasClass("fade")&&(this._isTransitioning=!0);var n=o.default.Event("show.bs.modal",{relatedTarget:t});o.default(this._element).trigger(n),this._isShown||n.isDefaultPrevented()||(this._isShown=!0,this._checkScrollbar(),this._setScrollbar(),this._adjustDialog(),this._setEscapeEvent(),this._setResizeEvent(),o.default(this._element).on("click.dismiss.bs.modal",'[data-dismiss="modal"]',(function(t){return e.hide(t)})),o.default(this._dialog).on("mousedown.dismiss.bs.modal",(function(){o.default(e._element).one("mouseup.dismiss.bs.modal",(function(t){o.default(t.target).is(e._element)&&(e._ignoreBackdropClick=!0)}))})),this._showBackdrop((function(){return e._showElement(t)})))}},e.hide=function(t){var e=this;if(t&&t.preventDefault(),this._isShown&&!this._isTransitioning){var n=o.default.Event("hide.bs.modal");if(o.default(this._element).trigger(n),this._isShown&&!n.isDefaultPrevented()){this._isShown=!1;var i=o.default(this._element).hasClass("fade");if(i&&(this._isTransitioning=!0),this._setEscapeEvent(),this._setResizeEvent(),o.default(document).off("focusin.bs.modal"),o.default(this._element).removeClass("show"),o.default(this._element).off("click.dismiss.bs.modal"),o.default(this._dialog).off("mousedown.dismiss.bs.modal"),i){var a=d.getTransitionDurationFromElement(this._element);o.default(this._element).one(d.TRANSITION_END,(function(t){return e._hideModal(t)})).emulateTransitionEnd(a)}else this._hideModal()}}},e.dispose=function(){[window,this._element,this._dialog].forEach((function(t){return o.default(t).off(".bs.modal")})),o.default(document).off("focusin.bs.modal"),o.default.removeData(this._element,"bs.modal"),this._config=null,this._element=null,this._dialog=null,this._backdrop=null,this._isShown=null,this._isBodyOverflowing=null,this._ignoreBackdropClick=null,this._isTransitioning=null,this._scrollbarWidth=null},e.handleUpdate=function(){this._adjustDialog()},e._getConfig=function(t){return t=r({},R,t),d.typeCheckConfig("modal",t,L),t},e._triggerBackdropTransition=function(){var t=this;if("static"===this._config.backdrop){var e=o.default.Event("hidePrevented.bs.modal");if(o.default(this._element).trigger(e),e.isDefaultPrevented())return;var n=this._element.scrollHeight>document.documentElement.clientHeight;n||(this._element.style.overflowY="hidden"),this._element.classList.add("modal-static");var i=d.getTransitionDurationFromElement(this._dialog);o.default(this._element).off(d.TRANSITION_END),o.default(this._element).one(d.TRANSITION_END,(function(){t._element.classList.remove("modal-static"),n||o.default(t._element).one(d.TRANSITION_END,(function(){t._element.style.overflowY=""})).emulateTransitionEnd(t._element,i)})).emulateTransitionEnd(i),this._element.focus()}else this.hide()},e._showElement=function(t){var e=this,n=o.default(this._element).hasClass("fade"),i=this._dialog?this._dialog.querySelector(".modal-body"):null;this._element.parentNode&&this._element.parentNode.nodeType===Node.ELEMENT_NODE||document.body.appendChild(this._element),this._element.style.display="block",this._element.removeAttribute("aria-hidden"),this._element.setAttribute("aria-modal",!0),this._element.setAttribute("role","dialog"),o.default(this._dialog).hasClass("modal-dialog-scrollable")&&i?i.scrollTop=0:this._element.scrollTop=0,n&&d.reflow(this._element),o.default(this._element).addClass("show"),this._config.focus&&this._enforceFocus();var a=o.default.Event("shown.bs.modal",{relatedTarget:t}),s=function(){e._config.focus&&e._element.focus(),e._isTransitioning=!1,o.default(e._element).trigger(a)};if(n){var l=d.getTransitionDurationFromElement(this._dialog);o.default(this._dialog).one(d.TRANSITION_END,s).emulateTransitionEnd(l)}else s()},e._enforceFocus=function(){var t=this;o.default(document).off("focusin.bs.modal").on("focusin.bs.modal",(function(e){document!==e.target&&t._element!==e.target&&0===o.default(t._element).has(e.target).length&&t._element.focus()}))},e._setEscapeEvent=function(){var t=this;this._isShown?o.default(this._element).on("keydown.dismiss.bs.modal",(function(e){t._config.keyboard&&27===e.which?(e.preventDefault(),t.hide()):t._config.keyboard||27!==e.which||t._triggerBackdropTransition()})):this._isShown||o.default(this._element).off("keydown.dismiss.bs.modal")},e._setResizeEvent=function(){var t=this;this._isShown?o.default(window).on("resize.bs.modal",(function(e){return t.handleUpdate(e)})):o.default(window).off("resize.bs.modal")},e._hideModal=function(){var t=this;this._element.style.display="none",this._element.setAttribute("aria-hidden",!0),this._element.removeAttribute("aria-modal"),this._element.removeAttribute("role"),this._isTransitioning=!1,this._showBackdrop((function(){o.default(document.body).removeClass("modal-open"),t._resetAdjustments(),t._resetScrollbar(),o.default(t._element).trigger("hidden.bs.modal")}))},e._removeBackdrop=function(){this._backdrop&&(o.default(this._backdrop).remove(),this._backdrop=null)},e._showBackdrop=function(t){var e=this,n=o.default(this._element).hasClass("fade")?"fade":"";if(this._isShown&&this._config.backdrop){if(this._backdrop=document.createElement("div"),this._backdrop.className="modal-backdrop",n&&this._backdrop.classList.add(n),o.default(this._backdrop).appendTo(document.body),o.default(this._element).on("click.dismiss.bs.modal",(function(t){e._ignoreBackdropClick?e._ignoreBackdropClick=!1:t.target===t.currentTarget&&e._triggerBackdropTransition()})),n&&d.reflow(this._backdrop),o.default(this._backdrop).addClass("show"),!t)return;if(!n)return void t();var i=d.getTransitionDurationFromElement(this._backdrop);o.default(this._backdrop).one(d.TRANSITION_END,t).emulateTransitionEnd(i)}else if(!this._isShown&&this._backdrop){o.default(this._backdrop).removeClass("show");var a=function(){e._removeBackdrop(),t&&t()};if(o.default(this._element).hasClass("fade")){var s=d.getTransitionDurationFromElement(this._backdrop);o.default(this._backdrop).one(d.TRANSITION_END,a).emulateTransitionEnd(s)}else a()}else t&&t()},e._adjustDialog=function(){var t=this._element.scrollHeight>document.documentElement.clientHeight;!this._isBodyOverflowing&&t&&(this._element.style.paddingLeft=this._scrollbarWidth+"px"),this._isBodyOverflowing&&!t&&(this._element.style.paddingRight=this._scrollbarWidth+"px")},e._resetAdjustments=function(){this._element.style.paddingLeft="",this._element.style.paddingRight=""},e._checkScrollbar=function(){var t=document.body.getBoundingClientRect();this._isBodyOverflowing=Math.round(t.left+t.right)<window.innerWidth,this._scrollbarWidth=this._getScrollbarWidth()},e._setScrollbar=function(){var t=this;if(this._isBodyOverflowing){var e=[].slice.call(document.querySelectorAll(".fixed-top, .fixed-bottom, .is-fixed, .sticky-top")),n=[].slice.call(document.querySelectorAll(".sticky-top"));o.default(e).each((function(e,n){var i=n.style.paddingRight,a=o.default(n).css("padding-right");o.default(n).data("padding-right",i).css("padding-right",parseFloat(a)+t._scrollbarWidth+"px")})),o.default(n).each((function(e,n){var i=n.style.marginRight,a=o.default(n).css("margin-right");o.default(n).data("margin-right",i).css("margin-right",parseFloat(a)-t._scrollbarWidth+"px")}));var i=document.body.style.paddingRight,a=o.default(document.body).css("padding-right");o.default(document.body).data("padding-right",i).css("padding-right",parseFloat(a)+this._scrollbarWidth+"px")}o.default(document.body).addClass("modal-open")},e._resetScrollbar=function(){var t=[].slice.call(document.querySelectorAll(".fixed-top, .fixed-bottom, .is-fixed, .sticky-top"));o.default(t).each((function(t,e){var n=o.default(e).data("padding-right");o.default(e).removeData("padding-right"),e.style.paddingRight=n||""}));var e=[].slice.call(document.querySelectorAll(".sticky-top"));o.default(e).each((function(t,e){var n=o.default(e).data("margin-right");"undefined"!=typeof n&&o.default(e).css("margin-right",n).removeData("margin-right")}));var n=o.default(document.body).data("padding-right");o.default(document.body).removeData("padding-right"),document.body.style.paddingRight=n||""},e._getScrollbarWidth=function(){var t=document.createElement("div");t.className="modal-scrollbar-measure",document.body.appendChild(t);var e=t.getBoundingClientRect().width-t.clientWidth;return document.body.removeChild(t),e},t._jQueryInterface=function(e,n){return this.each((function(){var i=o.default(this).data("bs.modal"),a=r({},R,o.default(this).data(),"object"==typeof e&&e?e:{});if(i||(i=new t(this,a),o.default(this).data("bs.modal",i)),"string"==typeof e){if("undefined"==typeof i[e])throw new TypeError('No method named "'+e+'"');i[e](n)}else a.show&&i.show(n)}))},l(t,null,[{key:"VERSION",get:function(){return"4.5.3"}},{key:"Default",get:function(){return R}}]),t}();o.default(document).on("click.bs.modal.data-api",'[data-toggle="modal"]',(function(t){var e,n=this,i=d.getSelectorFromElement(this);i&&(e=document.querySelector(i));var a=o.default(e).data("bs.modal")?"toggle":r({},o.default(e).data(),o.default(this).data());"A"!==this.tagName&&"AREA"!==this.tagName||t.preventDefault();var s=o.default(e).one("show.bs.modal",(function(t){t.isDefaultPrevented()||s.one("hidden.bs.modal",(function(){o.default(n).is(":visible")&&n.focus()}))}));q._jQueryInterface.call(o.default(e),a,this)})),o.default.fn.modal=q._jQueryInterface,o.default.fn.modal.Constructor=q,o.default.fn.modal.noConflict=function(){return o.default.fn.modal=P,q._jQueryInterface};var F=["background","cite","href","itemtype","longdesc","poster","src","xlink:href"],Q={"*":["class","dir","id","lang","role",/^aria-[\w-]*$/i],a:["target","href","title","rel"],area:[],b:[],br:[],col:[],code:[],div:[],em:[],hr:[],h1:[],h2:[],h3:[],h4:[],h5:[],h6:[],i:[],img:["src","srcset","alt","title","width","height"],li:[],ol:[],p:[],pre:[],s:[],small:[],span:[],sub:[],sup:[],strong:[],u:[],ul:[]},B=/^(?:(?:https?|mailto|ftp|tel|file):|[^#&/:?]*(?:[#/?]|$))/gi,H=/^data:(?:image\/(?:bmp|gif|jpeg|jpg|png|tiff|webp)|video\/(?:mpeg|mp4|ogg|webm)|audio\/(?:mp3|oga|ogg|opus));base64,[\d+/a-z]+=*$/i;function U(t,e,n){if(0===t.length)return t;if(n&&"function"==typeof n)return n(t);for(var i=(new window.DOMParser).parseFromString(t,"text/html"),o=Object.keys(e),a=[].slice.call(i.body.querySelectorAll("*")),s=function(t,n){var i=a[t],s=i.nodeName.toLowerCase();if(-1===o.indexOf(i.nodeName.toLowerCase()))return i.parentNode.removeChild(i),"continue";var l=[].slice.call(i.attributes),r=[].concat(e["*"]||[],e[s]||[]);l.forEach((function(t){(function(t,e){var n=t.nodeName.toLowerCase();if(-1!==e.indexOf(n))return-1===F.indexOf(n)||Boolean(t.nodeValue.match(B)||t.nodeValue.match(H));for(var i=e.filter((function(t){return t instanceof RegExp})),o=0,a=i.length;o<a;o++)if(n.match(i[o]))return!0;return!1})(t,r)||i.removeAttribute(t.nodeName)}))},l=0,r=a.length;l<r;l++)s(l);return i.body.innerHTML}var M="tooltip",W=o.default.fn[M],V=new RegExp("(^|\\s)bs-tooltip\\S+","g"),z=["sanitize","whiteList","sanitizeFn"],K={animation:"boolean",template:"string",title:"(string|element|function)",trigger:"string",delay:"(number|object)",html:"boolean",selector:"(string|boolean)",placement:"(string|function)",offset:"(number|string|function)",container:"(string|element|boolean)",fallbackPlacement:"(string|array)",boundary:"(string|element)",sanitize:"boolean",sanitizeFn:"(null|function)",whiteList:"object",popperConfig:"(null|object)"},X={AUTO:"auto",TOP:"top",RIGHT:"right",BOTTOM:"bottom",LEFT:"left"},Y={animation:!0,template:'<div class="tooltip" role="tooltip"><div class="arrow"></div><div class="tooltip-inner"></div></div>',trigger:"hover focus",title:"",delay:0,html:!1,selector:!1,placement:"top",offset:0,container:!1,fallbackPlacement:"flip",boundary:"scrollParent",sanitize:!0,sanitizeFn:null,whiteList:Q,popperConfig:null},$={HIDE:"hide.bs.tooltip",HIDDEN:"hidden.bs.tooltip",SHOW:"show.bs.tooltip",SHOWN:"shown.bs.tooltip",INSERTED:"inserted.bs.tooltip",CLICK:"click.bs.tooltip",FOCUSIN:"focusin.bs.tooltip",FOCUSOUT:"focusout.bs.tooltip",MOUSEENTER:"mouseenter.bs.tooltip",MOUSELEAVE:"mouseleave.bs.tooltip"},J=function(){function t(t,e){if("undefined"==typeof a.default)throw new TypeError("Bootstrap's tooltips require Popper.js (https://popper.js.org/)");this._isEnabled=!0,this._timeout=0,this._hoverState="",this._activeTrigger={},this._popper=null,this.element=t,this.config=this._getConfig(e),this.tip=null,this._setListeners()}var e=t.prototype;return e.enable=function(){this._isEnabled=!0},e.disable=function(){this._isEnabled=!1},e.toggleEnabled=function(){this._isEnabled=!this._isEnabled},e.toggle=function(t){if(this._isEnabled)if(t){var e=this.constructor.DATA_KEY,n=o.default(t.currentTarget).data(e);n||(n=new this.constructor(t.currentTarget,this._getDelegateConfig()),o.default(t.currentTarget).data(e,n)),n._activeTrigger.click=!n._activeTrigger.click,n._isWithActiveTrigger()?n._enter(null,n):n._leave(null,n)}else{if(o.default(this.getTipElement()).hasClass("show"))return void this._leave(null,this);this._enter(null,this)}},e.dispose=function(){clearTimeout(this._timeout),o.default.removeData(this.element,this.constructor.DATA_KEY),o.default(this.element).off(this.constructor.EVENT_KEY),o.default(this.element).closest(".modal").off("hide.bs.modal",this._hideModalHandler),this.tip&&o.default(this.tip).remove(),this._isEnabled=null,this._timeout=null,this._hoverState=null,this._activeTrigger=null,this._popper&&this._popper.destroy(),this._popper=null,this.element=null,this.config=null,this.tip=null},e.show=function(){var t=this;if("none"===o.default(this.element).css("display"))throw new Error("Please use show on visible elements");var e=o.default.Event(this.constructor.Event.SHOW);if(this.isWithContent()&&this._isEnabled){o.default(this.element).trigger(e);var n=d.findShadowRoot(this.element),i=o.default.contains(null!==n?n:this.element.ownerDocument.documentElement,this.element);if(e.isDefaultPrevented()||!i)return;var s=this.getTipElement(),l=d.getUID(this.constructor.NAME);s.setAttribute("id",l),this.element.setAttribute("aria-describedby",l),this.setContent(),this.config.animation&&o.default(s).addClass("fade");var r="function"==typeof this.config.placement?this.config.placement.call(this,s,this.element):this.config.placement,u=this._getAttachment(r);this.addAttachmentClass(u);var f=this._getContainer();o.default(s).data(this.constructor.DATA_KEY,this),o.default.contains(this.element.ownerDocument.documentElement,this.tip)||o.default(s).appendTo(f),o.default(this.element).trigger(this.constructor.Event.INSERTED),this._popper=new a.default(this.element,s,this._getPopperConfig(u)),o.default(s).addClass("show"),"ontouchstart"in document.documentElement&&o.default(document.body).children().on("mouseover",null,o.default.noop);var c=function(){t.config.animation&&t._fixTransition();var e=t._hoverState;t._hoverState=null,o.default(t.element).trigger(t.constructor.Event.SHOWN),"out"===e&&t._leave(null,t)};if(o.default(this.tip).hasClass("fade")){var h=d.getTransitionDurationFromElement(this.tip);o.default(this.tip).one(d.TRANSITION_END,c).emulateTransitionEnd(h)}else c()}},e.hide=function(t){var e=this,n=this.getTipElement(),i=o.default.Event(this.constructor.Event.HIDE),a=function(){"show"!==e._hoverState&&n.parentNode&&n.parentNode.removeChild(n),e._cleanTipClass(),e.element.removeAttribute("aria-describedby"),o.default(e.element).trigger(e.constructor.Event.HIDDEN),null!==e._popper&&e._popper.destroy(),t&&t()};if(o.default(this.element).trigger(i),!i.isDefaultPrevented()){if(o.default(n).removeClass("show"),"ontouchstart"in document.documentElement&&o.default(document.body).children().off("mouseover",null,o.default.noop),this._activeTrigger.click=!1,this._activeTrigger.focus=!1,this._activeTrigger.hover=!1,o.default(this.tip).hasClass("fade")){var s=d.getTransitionDurationFromElement(n);o.default(n).one(d.TRANSITION_END,a).emulateTransitionEnd(s)}else a();this._hoverState=""}},e.update=function(){null!==this._popper&&this._popper.scheduleUpdate()},e.isWithContent=function(){return Boolean(this.getTitle())},e.addAttachmentClass=function(t){o.default(this.getTipElement()).addClass("bs-tooltip-"+t)},e.getTipElement=function(){return this.tip=this.tip||o.default(this.config.template)[0],this.tip},e.setContent=function(){var t=this.getTipElement();this.setElementContent(o.default(t.querySelectorAll(".tooltip-inner")),this.getTitle()),o.default(t).removeClass("fade show")},e.setElementContent=function(t,e){"object"!=typeof e||!e.nodeType&&!e.jquery?this.config.html?(this.config.sanitize&&(e=U(e,this.config.whiteList,this.config.sanitizeFn)),t.html(e)):t.text(e):this.config.html?o.default(e).parent().is(t)||t.empty().append(e):t.text(o.default(e).text())},e.getTitle=function(){var t=this.element.getAttribute("data-original-title");return t||(t="function"==typeof this.config.title?this.config.title.call(this.element):this.config.title),t},e._getPopperConfig=function(t){var e=this;return r({},{placement:t,modifiers:{offset:this._getOffset(),flip:{behavior:this.config.fallbackPlacement},arrow:{element:".arrow"},preventOverflow:{boundariesElement:this.config.boundary}},onCreate:function(t){t.originalPlacement!==t.placement&&e._handlePopperPlacementChange(t)},onUpdate:function(t){return e._handlePopperPlacementChange(t)}},this.config.popperConfig)},e._getOffset=function(){var t=this,e={};return"function"==typeof this.config.offset?e.fn=function(e){return e.offsets=r({},e.offsets,t.config.offset(e.offsets,t.element)||{}),e}:e.offset=this.config.offset,e},e._getContainer=function(){return!1===this.config.container?document.body:d.isElement(this.config.container)?o.default(this.config.container):o.default(document).find(this.config.container)},e._getAttachment=function(t){return X[t.toUpperCase()]},e._setListeners=function(){var t=this;this.config.trigger.split(" ").forEach((function(e){if("click"===e)o.default(t.element).on(t.constructor.Event.CLICK,t.config.selector,(function(e){return t.toggle(e)}));else if("manual"!==e){var n="hover"===e?t.constructor.Event.MOUSEENTER:t.constructor.Event.FOCUSIN,i="hover"===e?t.constructor.Event.MOUSELEAVE:t.constructor.Event.FOCUSOUT;o.default(t.element).on(n,t.config.selector,(function(e){return t._enter(e)})).on(i,t.config.selector,(function(e){return t._leave(e)}))}})),this._hideModalHandler=function(){t.element&&t.hide()},o.default(this.element).closest(".modal").on("hide.bs.modal",this._hideModalHandler),this.config.selector?this.config=r({},this.config,{trigger:"manual",selector:""}):this._fixTitle()},e._fixTitle=function(){var t=typeof this.element.getAttribute("data-original-title");(this.element.getAttribute("title")||"string"!==t)&&(this.element.setAttribute("data-original-title",this.element.getAttribute("title")||""),this.element.setAttribute("title",""))},e._enter=function(t,e){var n=this.constructor.DATA_KEY;(e=e||o.default(t.currentTarget).data(n))||(e=new this.constructor(t.currentTarget,this._getDelegateConfig()),o.default(t.currentTarget).data(n,e)),t&&(e._activeTrigger["focusin"===t.type?"focus":"hover"]=!0),o.default(e.getTipElement()).hasClass("show")||"show"===e._hoverState?e._hoverState="show":(clearTimeout(e._timeout),e._hoverState="show",e.config.delay&&e.config.delay.show?e._timeout=setTimeout((function(){"show"===e._hoverState&&e.show()}),e.config.delay.show):e.show())},e._leave=function(t,e){var n=this.constructor.DATA_KEY;(e=e||o.default(t.currentTarget).data(n))||(e=new this.constructor(t.currentTarget,this._getDelegateConfig()),o.default(t.currentTarget).data(n,e)),t&&(e._activeTrigger["focusout"===t.type?"focus":"hover"]=!1),e._isWithActiveTrigger()||(clearTimeout(e._timeout),e._hoverState="out",e.config.delay&&e.config.delay.hide?e._timeout=setTimeout((function(){"out"===e._hoverState&&e.hide()}),e.config.delay.hide):e.hide())},e._isWithActiveTrigger=function(){for(var t in this._activeTrigger)if(this._activeTrigger[t])return!0;return!1},e._getConfig=function(t){var e=o.default(this.element).data();return Object.keys(e).forEach((function(t){-1!==z.indexOf(t)&&delete e[t]})),"number"==typeof(t=r({},this.constructor.Default,e,"object"==typeof t&&t?t:{})).delay&&(t.delay={show:t.delay,hide:t.delay}),"number"==typeof t.title&&(t.title=t.title.toString()),"number"==typeof t.content&&(t.content=t.content.toString()),d.typeCheckConfig(M,t,this.constructor.DefaultType),t.sanitize&&(t.template=U(t.template,t.whiteList,t.sanitizeFn)),t},e._getDelegateConfig=function(){var t={};if(this.config)for(var e in this.config)this.constructor.Default[e]!==this.config[e]&&(t[e]=this.config[e]);return t},e._cleanTipClass=function(){var t=o.default(this.getTipElement()),e=t.attr("class").match(V);null!==e&&e.length&&t.removeClass(e.join(""))},e._handlePopperPlacementChange=function(t){this.tip=t.instance.popper,this._cleanTipClass(),this.addAttachmentClass(this._getAttachment(t.placement))},e._fixTransition=function(){var t=this.getTipElement(),e=this.config.animation;null===t.getAttribute("x-placement")&&(o.default(t).removeClass("fade"),this.config.animation=!1,this.hide(),this.show(),this.config.animation=e)},t._jQueryInterface=function(e){return this.each((function(){var n=o.default(this),i=n.data("bs.tooltip"),a="object"==typeof e&&e;if((i||!/dispose|hide/.test(e))&&(i||(i=new t(this,a),n.data("bs.tooltip",i)),"string"==typeof e)){if("undefined"==typeof i[e])throw new TypeError('No method named "'+e+'"');i[e]()}}))},l(t,null,[{key:"VERSION",get:function(){return"4.5.3"}},{key:"Default",get:function(){return Y}},{key:"NAME",get:function(){return M}},{key:"DATA_KEY",get:function(){return"bs.tooltip"}},{key:"Event",get:function(){return $}},{key:"EVENT_KEY",get:function(){return".bs.tooltip"}},{key:"DefaultType",get:function(){return K}}]),t}();o.default.fn[M]=J._jQueryInterface,o.default.fn[M].Constructor=J,o.default.fn[M].noConflict=function(){return o.default.fn[M]=W,J._jQueryInterface};var G="popover",Z=o.default.fn[G],tt=new RegExp("(^|\\s)bs-popover\\S+","g"),et=r({},J.Default,{placement:"right",trigger:"click",content:"",template:'<div class="popover" role="tooltip"><div class="arrow"></div><h3 class="popover-header"></h3><div class="popover-body"></div></div>'}),nt=r({},J.DefaultType,{content:"(string|element|function)"}),it={HIDE:"hide.bs.popover",HIDDEN:"hidden.bs.popover",SHOW:"show.bs.popover",SHOWN:"shown.bs.popover",INSERTED:"inserted.bs.popover",CLICK:"click.bs.popover",FOCUSIN:"focusin.bs.popover",FOCUSOUT:"focusout.bs.popover",MOUSEENTER:"mouseenter.bs.popover",MOUSELEAVE:"mouseleave.bs.popover"},ot=function(t){var e,n;function i(){return t.apply(this,arguments)||this}n=t,(e=i).prototype=Object.create(n.prototype),e.prototype.constructor=e,e.__proto__=n;var a=i.prototype;return a.isWithContent=function(){return this.getTitle()||this._getContent()},a.addAttachmentClass=function(t){o.default(this.getTipElement()).addClass("bs-popover-"+t)},a.getTipElement=function(){return this.tip=this.tip||o.default(this.config.template)[0],this.tip},a.setContent=function(){var t=o.default(this.getTipElement());this.setElementContent(t.find(".popover-header"),this.getTitle());var e=this._getContent();"function"==typeof e&&(e=e.call(this.element)),this.setElementContent(t.find(".popover-body"),e),t.removeClass("fade show")},a._getContent=function(){return this.element.getAttribute("data-content")||this.config.content},a._cleanTipClass=function(){var t=o.default(this.getTipElement()),e=t.attr("class").match(tt);null!==e&&e.length>0&&t.removeClass(e.join(""))},i._jQueryInterface=function(t){return this.each((function(){var e=o.default(this).data("bs.popover"),n="object"==typeof t?t:null;if((e||!/dispose|hide/.test(t))&&(e||(e=new i(this,n),o.default(this).data("bs.popover",e)),"string"==typeof t)){if("undefined"==typeof e[t])throw new TypeError('No method named "'+t+'"');e[t]()}}))},l(i,null,[{key:"VERSION",get:function(){return"4.5.3"}},{key:"Default",get:function(){return et}},{key:"NAME",get:function(){return G}},{key:"DATA_KEY",get:function(){return"bs.popover"}},{key:"Event",get:function(){return it}},{key:"EVENT_KEY",get:function(){return".bs.popover"}},{key:"DefaultType",get:function(){return nt}}]),i}(J);o.default.fn[G]=ot._jQueryInterface,o.default.fn[G].Constructor=ot,o.default.fn[G].noConflict=function(){return o.default.fn[G]=Z,ot._jQueryInterface};var at="scrollspy",st=o.default.fn[at],lt={offset:10,method:"auto",target:""},rt={offset:"number",method:"string",target:"(string|element)"},ut=function(){function t(t,e){var n=this;this._element=t,this._scrollElement="BODY"===t.tagName?window:t,this._config=this._getConfig(e),this._selector=this._config.target+" .nav-link,"+this._config.target+" .list-group-item,"+this._config.target+" .dropdown-item",this._offsets=[],this._targets=[],this._activeTarget=null,this._scrollHeight=0,o.default(this._scrollElement).on("scroll.bs.scrollspy",(function(t){return n._process(t)})),this.refresh(),this._process()}var e=t.prototype;return e.refresh=function(){var t=this,e=this._scrollElement===this._scrollElement.window?"offset":"position",n="auto"===this._config.method?e:this._config.method,i="position"===n?this._getScrollTop():0;this._offsets=[],this._targets=[],this._scrollHeight=this._getScrollHeight(),[].slice.call(document.querySelectorAll(this._selector)).map((function(t){var e,a=d.getSelectorFromElement(t);if(a&&(e=document.querySelector(a)),e){var s=e.getBoundingClientRect();if(s.width||s.height)return[o.default(e)[n]().top+i,a]}return null})).filter((function(t){return t})).sort((function(t,e){return t[0]-e[0]})).forEach((function(e){t._offsets.push(e[0]),t._targets.push(e[1])}))},e.dispose=function(){o.default.removeData(this._element,"bs.scrollspy"),o.default(this._scrollElement).off(".bs.scrollspy"),this._element=null,this._scrollElement=null,this._config=null,this._selector=null,this._offsets=null,this._targets=null,this._activeTarget=null,this._scrollHeight=null},e._getConfig=function(t){if("string"!=typeof(t=r({},lt,"object"==typeof t&&t?t:{})).target&&d.isElement(t.target)){var e=o.default(t.target).attr("id");e||(e=d.getUID(at),o.default(t.target).attr("id",e)),t.target="#"+e}return d.typeCheckConfig(at,t,rt),t},e._getScrollTop=function(){return this._scrollElement===window?this._scrollElement.pageYOffset:this._scrollElement.scrollTop},e._getScrollHeight=function(){return this._scrollElement.scrollHeight||Math.max(document.body.scrollHeight,document.documentElement.scrollHeight)},e._getOffsetHeight=function(){return this._scrollElement===window?window.innerHeight:this._scrollElement.getBoundingClientRect().height},e._process=function(){var t=this._getScrollTop()+this._config.offset,e=this._getScrollHeight(),n=this._config.offset+e-this._getOffsetHeight();if(this._scrollHeight!==e&&this.refresh(),t>=n){var i=this._targets[this._targets.length-1];this._activeTarget!==i&&this._activate(i)}else{if(this._activeTarget&&t<this._offsets[0]&&this._offsets[0]>0)return this._activeTarget=null,void this._clear();for(var o=this._offsets.length;o--;){this._activeTarget!==this._targets[o]&&t>=this._offsets[o]&&("undefined"==typeof this._offsets[o+1]||t<this._offsets[o+1])&&this._activate(this._targets[o])}}},e._activate=function(t){this._activeTarget=t,this._clear();var e=this._selector.split(",").map((function(e){return e+'[data-target="'+t+'"],'+e+'[href="'+t+'"]'})),n=o.default([].slice.call(document.querySelectorAll(e.join(","))));n.hasClass("dropdown-item")?(n.closest(".dropdown").find(".dropdown-toggle").addClass("active"),n.addClass("active")):(n.addClass("active"),n.parents(".nav, .list-group").prev(".nav-link, .list-group-item").addClass("active"),n.parents(".nav, .list-group").prev(".nav-item").children(".nav-link").addClass("active")),o.default(this._scrollElement).trigger("activate.bs.scrollspy",{relatedTarget:t})},e._clear=function(){[].slice.call(document.querySelectorAll(this._selector)).filter((function(t){return t.classList.contains("active")})).forEach((function(t){return t.classList.remove("active")}))},t._jQueryInterface=function(e){return this.each((function(){var n=o.default(this).data("bs.scrollspy");if(n||(n=new t(this,"object"==typeof e&&e),o.default(this).data("bs.scrollspy",n)),"string"==typeof e){if("undefined"==typeof n[e])throw new TypeError('No method named "'+e+'"');n[e]()}}))},l(t,null,[{key:"VERSION",get:function(){return"4.5.3"}},{key:"Default",get:function(){return lt}}]),t}();o.default(window).on("load.bs.scrollspy.data-api",(function(){for(var t=[].slice.call(document.querySelectorAll('[data-spy="scroll"]')),e=t.length;e--;){var n=o.default(t[e]);ut._jQueryInterface.call(n,n.data())}})),o.default.fn[at]=ut._jQueryInterface,o.default.fn[at].Constructor=ut,o.default.fn[at].noConflict=function(){return o.default.fn[at]=st,ut._jQueryInterface};var dt=o.default.fn.tab,ft=function(){function t(t){this._element=t}var e=t.prototype;return e.show=function(){var t=this;if(!(this._element.parentNode&&this._element.parentNode.nodeType===Node.ELEMENT_NODE&&o.default(this._element).hasClass("active")||o.default(this._element).hasClass("disabled"))){var e,n,i=o.default(this._element).closest(".nav, .list-group")[0],a=d.getSelectorFromElement(this._element);if(i){var s="UL"===i.nodeName||"OL"===i.nodeName?"> li > .active":".active";n=(n=o.default.makeArray(o.default(i).find(s)))[n.length-1]}var l=o.default.Event("hide.bs.tab",{relatedTarget:this._element}),r=o.default.Event("show.bs.tab",{relatedTarget:n});if(n&&o.default(n).trigger(l),o.default(this._element).trigger(r),!r.isDefaultPrevented()&&!l.isDefaultPrevented()){a&&(e=document.querySelector(a)),this._activate(this._element,i);var u=function(){var e=o.default.Event("hidden.bs.tab",{relatedTarget:t._element}),i=o.default.Event("shown.bs.tab",{relatedTarget:n});o.default(n).trigger(e),o.default(t._element).trigger(i)};e?this._activate(e,e.parentNode,u):u()}}},e.dispose=function(){o.default.removeData(this._element,"bs.tab"),this._element=null},e._activate=function(t,e,n){var i=this,a=(!e||"UL"!==e.nodeName&&"OL"!==e.nodeName?o.default(e).children(".active"):o.default(e).find("> li > .active"))[0],s=n&&a&&o.default(a).hasClass("fade"),l=function(){return i._transitionComplete(t,a,n)};if(a&&s){var r=d.getTransitionDurationFromElement(a);o.default(a).removeClass("show").one(d.TRANSITION_END,l).emulateTransitionEnd(r)}else l()},e._transitionComplete=function(t,e,n){if(e){o.default(e).removeClass("active");var i=o.default(e.parentNode).find("> .dropdown-menu .active")[0];i&&o.default(i).removeClass("active"),"tab"===e.getAttribute("role")&&e.setAttribute("aria-selected",!1)}if(o.default(t).addClass("active"),"tab"===t.getAttribute("role")&&t.setAttribute("aria-selected",!0),d.reflow(t),t.classList.contains("fade")&&t.classList.add("show"),t.parentNode&&o.default(t.parentNode).hasClass("dropdown-menu")){var a=o.default(t).closest(".dropdown")[0];if(a){var s=[].slice.call(a.querySelectorAll(".dropdown-toggle"));o.default(s).addClass("active")}t.setAttribute("aria-expanded",!0)}n&&n()},t._jQueryInterface=function(e){return this.each((function(){var n=o.default(this),i=n.data("bs.tab");if(i||(i=new t(this),n.data("bs.tab",i)),"string"==typeof e){if("undefined"==typeof i[e])throw new TypeError('No method named "'+e+'"');i[e]()}}))},l(t,null,[{key:"VERSION",get:function(){return"4.5.3"}}]),t}();o.default(document).on("click.bs.tab.data-api",'[data-toggle="tab"], [data-toggle="pill"], [data-toggle="list"]',(function(t){t.preventDefault(),ft._jQueryInterface.call(o.default(this),"show")})),o.default.fn.tab=ft._jQueryInterface,o.default.fn.tab.Constructor=ft,o.default.fn.tab.noConflict=function(){return o.default.fn.tab=dt,ft._jQueryInterface};var ct=o.default.fn.toast,ht={animation:"boolean",autohide:"boolean",delay:"number"},gt={animation:!0,autohide:!0,delay:500},mt=function(){function t(t,e){this._element=t,this._config=this._getConfig(e),this._timeout=null,this._setListeners()}var e=t.prototype;return e.show=function(){var t=this,e=o.default.Event("show.bs.toast");if(o.default(this._element).trigger(e),!e.isDefaultPrevented()){this._clearTimeout(),this._config.animation&&this._element.classList.add("fade");var n=function(){t._element.classList.remove("showing"),t._element.classList.add("show"),o.default(t._element).trigger("shown.bs.toast"),t._config.autohide&&(t._timeout=setTimeout((function(){t.hide()}),t._config.delay))};if(this._element.classList.remove("hide"),d.reflow(this._element),this._element.classList.add("showing"),this._config.animation){var i=d.getTransitionDurationFromElement(this._element);o.default(this._element).one(d.TRANSITION_END,n).emulateTransitionEnd(i)}else n()}},e.hide=function(){if(this._element.classList.contains("show")){var t=o.default.Event("hide.bs.toast");o.default(this._element).trigger(t),t.isDefaultPrevented()||this._close()}},e.dispose=function(){this._clearTimeout(),this._element.classList.contains("show")&&this._element.classList.remove("show"),o.default(this._element).off("click.dismiss.bs.toast"),o.default.removeData(this._element,"bs.toast"),this._element=null,this._config=null},e._getConfig=function(t){return t=r({},gt,o.default(this._element).data(),"object"==typeof t&&t?t:{}),d.typeCheckConfig("toast",t,this.constructor.DefaultType),t},e._setListeners=function(){var t=this;o.default(this._element).on("click.dismiss.bs.toast",'[data-dismiss="toast"]',(function(){return t.hide()}))},e._close=function(){var t=this,e=function(){t._element.classList.add("hide"),o.default(t._element).trigger("hidden.bs.toast")};if(this._element.classList.remove("show"),this._config.animation){var n=d.getTransitionDurationFromElement(this._element);o.default(this._element).one(d.TRANSITION_END,e).emulateTransitionEnd(n)}else e()},e._clearTimeout=function(){clearTimeout(this._timeout),this._timeout=null},t._jQueryInterface=function(e){return this.each((function(){var n=o.default(this),i=n.data("bs.toast");if(i||(i=new t(this,"object"==typeof e&&e),n.data("bs.toast",i)),"string"==typeof e){if("undefined"==typeof i[e])throw new TypeError('No method named "'+e+'"');i[e](this)}}))},l(t,null,[{key:"VERSION",get:function(){return"4.5.3"}},{key:"DefaultType",get:function(){return ht}},{key:"Default",get:function(){return gt}}]),t}();o.default.fn.toast=mt._jQueryInterface,o.default.fn.toast.Constructor=mt,o.default.fn.toast.noConflict=function(){return o.default.fn.toast=ct,mt._jQueryInterface},t.Alert=h,t.Button=m,t.Carousel=w,t.Collapse=D,t.Dropdown=x,t.Modal=q,t.Popover=ot,t.Scrollspy=ut,t.Tab=ft,t.Toast=mt,t.Tooltip=J,t.Util=d,Object.defineProperty(t,"__esModule",{value:!0})}));
|
7 |
-
//# sourceMappingURL=bootstrap.min.js.map
|
|
|
|
|
|
|
|
|
|
|
|
|
|
resources/js/global-plugin.js
CHANGED
@@ -45,9 +45,23 @@ var iCWP_WPSF_ParseAjaxResponse = new function () {
|
|
45 |
parsed = JSON.parse( raw );
|
46 |
}
|
47 |
catch ( e ) {
|
48 |
-
|
49 |
-
|
50 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
51 |
}
|
52 |
return parsed;
|
53 |
};
|
@@ -57,6 +71,8 @@ var iCWP_WPSF_StandardAjax = new function () {
|
|
57 |
this.send_ajax_req = function ( reqData ) {
|
58 |
iCWP_WPSF_BodyOverlay.show();
|
59 |
|
|
|
|
|
60 |
jQuery.ajax( {
|
61 |
type: "POST",
|
62 |
url: ajaxurl,
|
@@ -81,6 +97,8 @@ var iCWP_WPSF_StandardAjax = new function () {
|
|
81 |
iCWP_WPSF_BodyOverlay.hide();
|
82 |
}
|
83 |
}
|
|
|
|
|
84 |
} )
|
85 |
};
|
86 |
}();
|
45 |
parsed = JSON.parse( raw );
|
46 |
}
|
47 |
catch ( e ) {
|
48 |
+
var openJsonTag = '##APTO_OPEN##';
|
49 |
+
var closeJsonTag = '##APTO_CLOSE##';
|
50 |
+
var start = 0;
|
51 |
+
var end = 0;
|
52 |
+
|
53 |
+
if ( raw.indexOf( openJsonTag ) >= 0 ) {
|
54 |
+
start = raw.indexOf( openJsonTag ) + openJsonTag.length;
|
55 |
+
end = raw.indexOf( closeJsonTag );
|
56 |
+
try {
|
57 |
+
parsed = JSON.parse( raw.substring( start, end ) );
|
58 |
+
}
|
59 |
+
catch ( e ) {
|
60 |
+
start = raw.indexOf( '{' );
|
61 |
+
end = raw.lastIndexOf( '}' ) + 1;
|
62 |
+
parsed = JSON.parse( raw.substring( start, end ) );
|
63 |
+
}
|
64 |
+
}
|
65 |
}
|
66 |
return parsed;
|
67 |
};
|
71 |
this.send_ajax_req = function ( reqData ) {
|
72 |
iCWP_WPSF_BodyOverlay.show();
|
73 |
|
74 |
+
reqData.apto_wrap_response = 1;
|
75 |
+
|
76 |
jQuery.ajax( {
|
77 |
type: "POST",
|
78 |
url: ajaxurl,
|
97 |
iCWP_WPSF_BodyOverlay.hide();
|
98 |
}
|
99 |
}
|
100 |
+
} ).fail( function () {
|
101 |
+
alert( 'Something went wrong with the request - it was either blocked or there was an error.' );
|
102 |
} )
|
103 |
};
|
104 |
}();
|
resources/js/plugin.js
CHANGED
@@ -126,6 +126,69 @@ var iCWP_WPSF_OptionsFormSubmit = new function () {
|
|
126 |
aAjaxReqParams = aParams;
|
127 |
};
|
128 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
129 |
var submitOptionsForm = function ( event ) {
|
130 |
iCWP_WPSF_BodyOverlay.show();
|
131 |
|
@@ -151,67 +214,7 @@ var iCWP_WPSF_OptionsFormSubmit = new function () {
|
|
151 |
} );
|
152 |
|
153 |
if ( $bPasswordsReady ) {
|
154 |
-
|
155 |
-
* First try with base64 and failover to lz-string upon abject failure.
|
156 |
-
* This works around mod_security rules that even unpack b64 encoded params and look
|
157 |
-
* for patterns within them.
|
158 |
-
*/
|
159 |
-
var aReq = jQuery.extend(
|
160 |
-
aAjaxReqParams,
|
161 |
-
{
|
162 |
-
'form_params': Base64.encode( $oForm.serialize() ),
|
163 |
-
'enc_params': 'b64'
|
164 |
-
}
|
165 |
-
);
|
166 |
-
|
167 |
-
jQuery.post( ajaxurl, aReq,
|
168 |
-
function ( oResponse ) {
|
169 |
-
var sMessage;
|
170 |
-
if ( oResponse === null || typeof oResponse.data === 'undefined'
|
171 |
-
|| typeof oResponse.data.message === 'undefined' ) {
|
172 |
-
sMessage = oResponse.success ? 'Success' : 'Failure';
|
173 |
-
}
|
174 |
-
else {
|
175 |
-
sMessage = oResponse.data.message;
|
176 |
-
}
|
177 |
-
iCWP_WPSF_Toaster.showMessage( sMessage, oResponse.success );
|
178 |
-
// iCWP_WPSF_Growl.showMessage( sMessage, oResponse.success );
|
179 |
-
}
|
180 |
-
).fail(
|
181 |
-
function () {
|
182 |
-
iCWP_WPSF_Toaster.showMessage( 'The request was blocked. Retrying an alternative...', false );
|
183 |
-
aReq = jQuery.extend(
|
184 |
-
aAjaxReqParams,
|
185 |
-
{
|
186 |
-
'form_params': Base64.encode( LZString.compress( $oForm.serialize() ) ),
|
187 |
-
'enc_params': 'lz-string'
|
188 |
-
}
|
189 |
-
);
|
190 |
-
jQuery.post( ajaxurl, aReq,
|
191 |
-
function ( oResponse ) {
|
192 |
-
var sMessage;
|
193 |
-
if ( oResponse === null || typeof oResponse.data === 'undefined'
|
194 |
-
|| typeof oResponse.data.message === 'undefined' ) {
|
195 |
-
sMessage = oResponse.success ? 'Success' : 'Failure';
|
196 |
-
}
|
197 |
-
else {
|
198 |
-
sMessage = oResponse.data.message;
|
199 |
-
}
|
200 |
-
iCWP_WPSF_Toaster.showMessage( sMessage, oResponse.success );
|
201 |
-
}
|
202 |
-
)
|
203 |
-
}
|
204 |
-
).always( function () {
|
205 |
-
bRequestCurrentlyRunning = false;
|
206 |
-
setTimeout( function () {
|
207 |
-
location.reload();
|
208 |
-
}, 1000 );
|
209 |
-
}
|
210 |
-
);
|
211 |
-
}
|
212 |
-
else {
|
213 |
-
bRequestCurrentlyRunning = false;
|
214 |
-
iCWP_WPSF_BodyOverlay.hide();
|
215 |
}
|
216 |
};
|
217 |
|
126 |
aAjaxReqParams = aParams;
|
127 |
};
|
128 |
|
129 |
+
/**
|
130 |
+
* First try with base64 and failover to lz-string upon abject failure.
|
131 |
+
* This works around mod_security rules that even unpack b64 encoded params and look
|
132 |
+
* for patterns within them.
|
133 |
+
*/
|
134 |
+
var sendForm = function ( $oForm, useCompression = false ) {
|
135 |
+
|
136 |
+
let formData = $oForm.serialize();
|
137 |
+
if ( useCompression ) {
|
138 |
+
formData = LZString.compress( formData );
|
139 |
+
}
|
140 |
+
|
141 |
+
let reqs = jQuery.extend(
|
142 |
+
aAjaxReqParams,
|
143 |
+
{
|
144 |
+
'form_params': Base64.encode( formData ),
|
145 |
+
'enc_params': useCompression ? 'lz-string' : 'b64',
|
146 |
+
'apto_wrap_response': 1
|
147 |
+
}
|
148 |
+
);
|
149 |
+
|
150 |
+
jQuery.ajax(
|
151 |
+
{
|
152 |
+
type: "POST",
|
153 |
+
url: ajaxurl,
|
154 |
+
data: reqs,
|
155 |
+
dataType: "text",
|
156 |
+
success: function ( raw ) {
|
157 |
+
handleResponse( raw );
|
158 |
+
},
|
159 |
+
}
|
160 |
+
).fail( function () {
|
161 |
+
alert( 'fail()' );
|
162 |
+
if ( useCompression ) {
|
163 |
+
handleResponse( raw );
|
164 |
+
}
|
165 |
+
else {
|
166 |
+
iCWP_WPSF_Toaster.showMessage( 'The request was blocked. Retrying an alternative...', false );
|
167 |
+
sendForm( $oForm, true );
|
168 |
+
}
|
169 |
+
|
170 |
+
} ).always( function () {
|
171 |
+
bRequestCurrentlyRunning = false;
|
172 |
+
setTimeout( function () {
|
173 |
+
location.reload();
|
174 |
+
}, 1000 );
|
175 |
+
} );
|
176 |
+
|
177 |
+
};
|
178 |
+
|
179 |
+
var handleResponse = function ( raw ) {
|
180 |
+
let response = iCWP_WPSF_ParseAjaxResponse.parseIt( raw );
|
181 |
+
let msg;
|
182 |
+
if ( response === null || typeof response.data === 'undefined'
|
183 |
+
|| typeof response.data.message === 'undefined' ) {
|
184 |
+
msg = response.success ? 'Success' : 'Failure';
|
185 |
+
}
|
186 |
+
else {
|
187 |
+
msg = response.data.message;
|
188 |
+
}
|
189 |
+
iCWP_WPSF_Toaster.showMessage( msg, response.success );
|
190 |
+
};
|
191 |
+
|
192 |
var submitOptionsForm = function ( event ) {
|
193 |
iCWP_WPSF_BodyOverlay.show();
|
194 |
|
214 |
} );
|
215 |
|
216 |
if ( $bPasswordsReady ) {
|
217 |
+
sendForm( $oForm, false );
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
218 |
}
|
219 |
};
|
220 |
|
resources/js/{shield-scans.js → shield/scans.js}
RENAMED
File without changes
|
resources/js/{shield-tables.js → shield/tables.js}
RENAMED
File without changes
|
resources/js/{shield-u2f-admin.js → shield/u2f-admin.js}
RENAMED
@@ -27,7 +27,6 @@ if ( typeof icwp_wpsf_vars_u2f !== 'undefined' ) {
|
|
27 |
.css( 'color', 'green' );
|
28 |
} )
|
29 |
.catch( function ( response ) {
|
30 |
-
console.log( response );
|
31 |
$oU2fStatus.text( icwp_wpsf_vars_u2f.strings.failed );
|
32 |
$oU2fStatus.css( 'font-weight', 'bolder' )
|
33 |
.css( 'color', 'red' );
|
@@ -44,15 +43,13 @@ if ( typeof icwp_wpsf_vars_u2f !== 'undefined' ) {
|
|
44 |
|
45 |
} );
|
46 |
|
47 |
-
|
48 |
jQuery.fn.icwpWpsfProfileU2f = function () {
|
49 |
|
50 |
var initialise = function () {
|
51 |
jQuery( document ).ready( function () {
|
52 |
jQuery( 'a.icwpWpsf-U2FRemove' ).on( 'click', function ( evt ) {
|
53 |
evt.preventDefault();
|
54 |
-
icwp_wpsf_vars_u2f.ajax.u2f_remove.u2fid =
|
55 |
-
jQuery( evt.target ).data( 'u2fid' );
|
56 |
iCWP_WPSF_StandardAjax.send_ajax_req( icwp_wpsf_vars_u2f.ajax.u2f_remove );
|
57 |
return false;
|
58 |
} )
|
27 |
.css( 'color', 'green' );
|
28 |
} )
|
29 |
.catch( function ( response ) {
|
|
|
30 |
$oU2fStatus.text( icwp_wpsf_vars_u2f.strings.failed );
|
31 |
$oU2fStatus.css( 'font-weight', 'bolder' )
|
32 |
.css( 'color', 'red' );
|
43 |
|
44 |
} );
|
45 |
|
|
|
46 |
jQuery.fn.icwpWpsfProfileU2f = function () {
|
47 |
|
48 |
var initialise = function () {
|
49 |
jQuery( document ).ready( function () {
|
50 |
jQuery( 'a.icwpWpsf-U2FRemove' ).on( 'click', function ( evt ) {
|
51 |
evt.preventDefault();
|
52 |
+
icwp_wpsf_vars_u2f.ajax.u2f_remove.u2fid = jQuery( evt.currentTarget ).data( 'u2fid' );
|
|
|
53 |
iCWP_WPSF_StandardAjax.send_ajax_req( icwp_wpsf_vars_u2f.ajax.u2f_remove );
|
54 |
return false;
|
55 |
} )
|
resources/js/{shield-userprofile.js → shield/userprofile.js}
RENAMED
@@ -6,7 +6,7 @@ if ( typeof icwp_wpsf_vars_profileyubikey !== 'undefined' ) {
|
|
6 |
jQuery( 'a.icwpWpsf-YubikeyRemove' ).on( 'click', function ( evt ) {
|
7 |
evt.preventDefault();
|
8 |
icwp_wpsf_vars_profileyubikey.yubikey_remove.yubikeyid =
|
9 |
-
jQuery( evt.
|
10 |
iCWP_WPSF_StandardAjax.send_ajax_req( icwp_wpsf_vars_profileyubikey.yubikey_remove );
|
11 |
return false;
|
12 |
} )
|
6 |
jQuery( 'a.icwpWpsf-YubikeyRemove' ).on( 'click', function ( evt ) {
|
7 |
evt.preventDefault();
|
8 |
icwp_wpsf_vars_profileyubikey.yubikey_remove.yubikeyid =
|
9 |
+
jQuery( evt.currentTarget ).data( 'yubikeyid' );
|
10 |
iCWP_WPSF_StandardAjax.send_ajax_req( icwp_wpsf_vars_profileyubikey.yubikey_remove );
|
11 |
return false;
|
12 |
} )
|
src/config/feature-hack_protect.php
CHANGED
@@ -417,7 +417,7 @@
|
|
417 |
"file": "varchar(256) NOT NULL COMMENT 'File Path relative to ABSPATH'",
|
418 |
"hash_original": "varchar(40) NOT NULL COMMENT 'SHA1 File Hash Original'",
|
419 |
"hash_current": "varchar(40) NOT NULL COMMENT 'SHA1 File Hash Current'",
|
420 |
-
"content": "
|
421 |
"public_key_id": "TINYINT(2) UNSIGNED NOT NULL COMMENT 'Public Key ID'",
|
422 |
"detected_at": "int(15) UNSIGNED NOT NULL DEFAULT 0 COMMENT 'TS Change Last Detected'",
|
423 |
"reverted_at": "int(15) UNSIGNED NOT NULL DEFAULT 0 COMMENT 'TS Reverted To Backup'",
|
417 |
"file": "varchar(256) NOT NULL COMMENT 'File Path relative to ABSPATH'",
|
418 |
"hash_original": "varchar(40) NOT NULL COMMENT 'SHA1 File Hash Original'",
|
419 |
"hash_current": "varchar(40) NOT NULL COMMENT 'SHA1 File Hash Current'",
|
420 |
+
"content": "MEDIUMBLOB COMMENT 'Content'",
|
421 |
"public_key_id": "TINYINT(2) UNSIGNED NOT NULL COMMENT 'Public Key ID'",
|
422 |
"detected_at": "int(15) UNSIGNED NOT NULL DEFAULT 0 COMMENT 'TS Change Last Detected'",
|
423 |
"reverted_at": "int(15) UNSIGNED NOT NULL DEFAULT 0 COMMENT 'TS Reverted To Backup'",
|
src/config/feature-headers.php
CHANGED
@@ -167,6 +167,7 @@
|
|
167 |
{
|
168 |
"key": "enable_x_content_security_policy",
|
169 |
"section": "section_content_security_policy",
|
|
|
170 |
"default": "N",
|
171 |
"type": "checkbox",
|
172 |
"link_info": "https://shsec.io/7d",
|
@@ -175,75 +176,6 @@
|
|
175 |
"summary": "Enable (or Disable) The Content Security Policy module",
|
176 |
"description": "Allows for permission and restriction of all resources loaded on your site."
|
177 |
},
|
178 |
-
{
|
179 |
-
"key": "xcsp_self",
|
180 |
-
"section": "section_content_security_policy",
|
181 |
-
"default": "Y",
|
182 |
-
"type": "checkbox",
|
183 |
-
"link_info": "",
|
184 |
-
"link_blog": "",
|
185 |
-
"name": "Self",
|
186 |
-
"summary": "Allow 'self' Directive",
|
187 |
-
"description": "Using 'self' is generally recommended. It essentially means that resources from your own host:protocol are permitted."
|
188 |
-
},
|
189 |
-
{
|
190 |
-
"key": "xcsp_inline",
|
191 |
-
"section": "section_content_security_policy",
|
192 |
-
"default": "Y",
|
193 |
-
"type": "checkbox",
|
194 |
-
"link_info": "",
|
195 |
-
"link_blog": "",
|
196 |
-
"name": "Inline Entities",
|
197 |
-
"summary": "Allow Inline Scripts and CSS",
|
198 |
-
"description": "Allows parsing of Javascript and CSS declared in-line in your html document."
|
199 |
-
},
|
200 |
-
{
|
201 |
-
"key": "xcsp_data",
|
202 |
-
"section": "section_content_security_policy",
|
203 |
-
"default": "Y",
|
204 |
-
"type": "checkbox",
|
205 |
-
"link_info": "",
|
206 |
-
"link_blog": "",
|
207 |
-
"name": "Embedded Data",
|
208 |
-
"summary": "Allow 'data:' Directives",
|
209 |
-
"description": "Allows use of embedded data directives, most commonly used for images and fonts."
|
210 |
-
},
|
211 |
-
{
|
212 |
-
"key": "xcsp_eval",
|
213 |
-
"section": "section_content_security_policy",
|
214 |
-
"default": "Y",
|
215 |
-
"type": "checkbox",
|
216 |
-
"link_info": "",
|
217 |
-
"link_blog": "",
|
218 |
-
"name": "Allow eval()",
|
219 |
-
"summary": "Allow Javascript eval()",
|
220 |
-
"description": "Permits the use of Javascript the eval() function."
|
221 |
-
},
|
222 |
-
{
|
223 |
-
"key": "xcsp_https",
|
224 |
-
"section": "section_content_security_policy",
|
225 |
-
"default": "N",
|
226 |
-
"type": "checkbox",
|
227 |
-
"link_info": "",
|
228 |
-
"link_blog": "",
|
229 |
-
"name": "HTTPS",
|
230 |
-
"summary": "HTTPS Resource Loading",
|
231 |
-
"description": "Allows loading of any content provided over HTTPS."
|
232 |
-
},
|
233 |
-
{
|
234 |
-
"key": "xcsp_hosts",
|
235 |
-
"section": "section_content_security_policy",
|
236 |
-
"sensitive": true,
|
237 |
-
"default": [
|
238 |
-
"*"
|
239 |
-
],
|
240 |
-
"type": "array",
|
241 |
-
"link_info": "https://shsec.io/ga",
|
242 |
-
"link_blog": "",
|
243 |
-
"name": "Permitted Hosts",
|
244 |
-
"summary": "Permitted Hosts and Domains",
|
245 |
-
"description": "You can explicitly state which hosts/domain from which content may be loaded. Take great care and test your site as you may block legitimate resources. If in-doubt, leave blank or use '*' only. Note: You can force only HTTPS for a given domain by prefixing it with 'https://'."
|
246 |
-
},
|
247 |
{
|
248 |
"key": "xcsp_custom",
|
249 |
"section": "section_content_security_policy",
|
167 |
{
|
168 |
"key": "enable_x_content_security_policy",
|
169 |
"section": "section_content_security_policy",
|
170 |
+
"premium": true,
|
171 |
"default": "N",
|
172 |
"type": "checkbox",
|
173 |
"link_info": "https://shsec.io/7d",
|
176 |
"summary": "Enable (or Disable) The Content Security Policy module",
|
177 |
"description": "Allows for permission and restriction of all resources loaded on your site."
|
178 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
179 |
{
|
180 |
"key": "xcsp_custom",
|
181 |
"section": "section_content_security_policy",
|
src/config/feature-login_protect.php
CHANGED
@@ -358,10 +358,7 @@
|
|
358 |
"advanced": true,
|
359 |
"premium": true,
|
360 |
"type": "array",
|
361 |
-
"default": [
|
362 |
-
"form#ihc_login_form",
|
363 |
-
"form#createuser"
|
364 |
-
],
|
365 |
"link_info": "https://shsec.io/hg",
|
366 |
"link_blog": "",
|
367 |
"name": "AntiBot Forms",
|
358 |
"advanced": true,
|
359 |
"premium": true,
|
360 |
"type": "array",
|
361 |
+
"default": [],
|
|
|
|
|
|
|
362 |
"link_info": "https://shsec.io/hg",
|
363 |
"link_blog": "",
|
364 |
"name": "AntiBot Forms",
|
src/config/feature-user_management.php
CHANGED
@@ -136,7 +136,7 @@
|
|
136 |
"default": 48,
|
137 |
"min": 0,
|
138 |
"type": "integer",
|
139 |
-
"link_info": "https://
|
140 |
"link_blog": "",
|
141 |
"name": "Idle Timeout",
|
142 |
"summary": "Specify How Many Hours After Inactivity To Automatically Logout User",
|
136 |
"default": 48,
|
137 |
"min": 0,
|
138 |
"type": "integer",
|
139 |
+
"link_info": "https://support.getshieldsecurity.com/support/solutions/articles/3000070590",
|
140 |
"link_blog": "",
|
141 |
"name": "Idle Timeout",
|
142 |
"summary": "Specify How Many Hours After Inactivity To Automatically Logout User",
|
src/features/admin_access_restriction.php
DELETED
@@ -1,396 +0,0 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
use FernleafSystems\Wordpress\Plugin\Shield;
|
4 |
-
use FernleafSystems\Wordpress\Plugin\Shield\Modules\SecurityAdmin;
|
5 |
-
use FernleafSystems\Wordpress\Services\Services;
|
6 |
-
|
7 |
-
/**
|
8 |
-
* @deprecated 10.1
|
9 |
-
*/
|
10 |
-
class ICWP_WPSF_FeatureHandler_AdminAccessRestriction extends ICWP_WPSF_FeatureHandler_BaseWpsf {
|
11 |
-
|
12 |
-
const HASH_DELETE = '32f68a60cef40faedbc6af20298c1a1e';
|
13 |
-
|
14 |
-
/**
|
15 |
-
* @var bool
|
16 |
-
*/
|
17 |
-
private $bValidSecAdminRequest;
|
18 |
-
|
19 |
-
/**
|
20 |
-
* @var SecurityAdmin\Lib\WhiteLabel\ApplyLabels
|
21 |
-
*/
|
22 |
-
private $oWhiteLabelController;
|
23 |
-
|
24 |
-
protected function setupCustomHooks() {
|
25 |
-
parent::setupCustomHooks();
|
26 |
-
add_action( $this->prefix( 'pre_deactivate_plugin' ), [ $this, 'preDeactivatePlugin' ] );
|
27 |
-
}
|
28 |
-
|
29 |
-
public function getWhiteLabelController() :SecurityAdmin\Lib\WhiteLabel\ApplyLabels {
|
30 |
-
if ( !$this->oWhiteLabelController instanceof SecurityAdmin\Lib\WhiteLabel\ApplyLabels ) {
|
31 |
-
$this->oWhiteLabelController = ( new SecurityAdmin\Lib\WhiteLabel\ApplyLabels() )
|
32 |
-
->setMod( $this );
|
33 |
-
}
|
34 |
-
return $this->oWhiteLabelController;
|
35 |
-
}
|
36 |
-
|
37 |
-
/**
|
38 |
-
* @return bool
|
39 |
-
* @throws \Exception
|
40 |
-
*/
|
41 |
-
protected function isReadyToExecute() {
|
42 |
-
return $this->isEnabledSecurityAdmin() && parent::isReadyToExecute();
|
43 |
-
}
|
44 |
-
|
45 |
-
/**
|
46 |
-
* No checking of admin capabilities in-case of infinite loop with
|
47 |
-
* admin access caps check
|
48 |
-
* @return bool
|
49 |
-
*/
|
50 |
-
public function isRegisteredSecAdminUser() {
|
51 |
-
/** @var SecurityAdmin\Options $opts */
|
52 |
-
$opts = $this->getOptions();
|
53 |
-
$sUser = Services::WpUsers()->getCurrentWpUsername();
|
54 |
-
return !empty( $sUser ) && in_array( $sUser, $opts->getSecurityAdminUsers() );
|
55 |
-
}
|
56 |
-
|
57 |
-
protected function preProcessOptions() {
|
58 |
-
/** @var SecurityAdmin\Options $opts */
|
59 |
-
$opts = $this->getOptions();
|
60 |
-
|
61 |
-
if ( $this->isValidSecAdminRequest() ) {
|
62 |
-
$this->setSecurityAdminStatusOnOff( true );
|
63 |
-
}
|
64 |
-
|
65 |
-
// Verify whitelabel images
|
66 |
-
if ( $this->isWlEnabled() ) {
|
67 |
-
$aImages = [
|
68 |
-
'wl_menuiconurl',
|
69 |
-
'wl_dashboardlogourl',
|
70 |
-
'wl_login2fa_logourl',
|
71 |
-
];
|
72 |
-
$oOpts = $this->getOptions();
|
73 |
-
foreach ( $aImages as $sKey ) {
|
74 |
-
if ( !Services::Data()->isValidWebUrl( $this->buildWlImageUrl( $sKey ) ) ) {
|
75 |
-
$oOpts->resetOptToDefault( $sKey );
|
76 |
-
}
|
77 |
-
}
|
78 |
-
}
|
79 |
-
|
80 |
-
$opts->setOpt( 'sec_admin_users', $this->verifySecAdminUsers( $opts->getSecurityAdminUsers() ) );
|
81 |
-
}
|
82 |
-
|
83 |
-
/**
|
84 |
-
* Ensures that all entries are valid users.
|
85 |
-
* @param string[] $aSecUsers
|
86 |
-
* @return string[]
|
87 |
-
*/
|
88 |
-
private function verifySecAdminUsers( $aSecUsers ) {
|
89 |
-
$oDP = Services::Data();
|
90 |
-
$oWpUsers = Services::WpUsers();
|
91 |
-
/** @var SecurityAdmin\Options $opts */
|
92 |
-
$opts = $this->getOptions();
|
93 |
-
|
94 |
-
$aFiltered = [];
|
95 |
-
foreach ( $aSecUsers as $nCurrentKey => $sUsernameOrEmail ) {
|
96 |
-
if ( $oDP->validEmail( $sUsernameOrEmail ) ) {
|
97 |
-
$oUser = $oWpUsers->getUserByEmail( $sUsernameOrEmail );
|
98 |
-
}
|
99 |
-
else {
|
100 |
-
$oUser = $oWpUsers->getUserByUsername( $sUsernameOrEmail );
|
101 |
-
if ( is_null( $oUser ) && is_numeric( $sUsernameOrEmail ) ) {
|
102 |
-
$oUser = $oWpUsers->getUserById( $sUsernameOrEmail );
|
103 |
-
}
|
104 |
-
}
|
105 |
-
|
106 |
-
if ( $oUser instanceof WP_User && $oUser->ID > 0 && $oWpUsers->isUserAdmin( $oUser ) ) {
|
107 |
-
$aFiltered[] = $oUser->user_login;
|
108 |
-
}
|
109 |
-
}
|
110 |
-
|
111 |
-
// We now run a bit of a sanity check to ensure that the current user is
|
112 |
-
// not adding users here that aren't themselves without a key to still gain access
|
113 |
-
$oCurrent = $oWpUsers->getCurrentWpUser();
|
114 |
-
if ( !empty( $aFiltered ) && !$opts->hasSecurityPIN() && !in_array( $oCurrent->user_login, $aFiltered ) ) {
|
115 |
-
$aFiltered[] = $oCurrent->user_login;
|
116 |
-
}
|
117 |
-
|
118 |
-
natsort( $aFiltered );
|
119 |
-
return array_unique( $aFiltered );
|
120 |
-
}
|
121 |
-
|
122 |
-
public function getSecAdminTimeout() :int {
|
123 |
-
return (int)$this->getOptions()->getOpt( 'admin_access_timeout' )*MINUTE_IN_SECONDS;
|
124 |
-
}
|
125 |
-
|
126 |
-
/**
|
127 |
-
* Only returns greater than 0 if you have a valid Sec admin session
|
128 |
-
*/
|
129 |
-
public function getSecAdminTimeLeft() :int {
|
130 |
-
$nLeft = 0;
|
131 |
-
if ( $this->hasSession() ) {
|
132 |
-
|
133 |
-
$nSecAdminAt = $this->getSession()->getSecAdminAt();
|
134 |
-
if ( $this->isRegisteredSecAdminUser() ) {
|
135 |
-
$nLeft = 0;
|
136 |
-
}
|
137 |
-
elseif ( $nSecAdminAt > 0 ) {
|
138 |
-
$nLeft = $this->getSecAdminTimeout() - ( Services::Request()->ts() - $nSecAdminAt );
|
139 |
-
}
|
140 |
-
}
|
141 |
-
return (int)max( 0, $nLeft );
|
142 |
-
}
|
143 |
-
|
144 |
-
protected function handleModAction( string $sAction ) {
|
145 |
-
switch ( $sAction ) {
|
146 |
-
case 'remove_secadmin_confirm':
|
147 |
-
( new SecurityAdmin\Lib\Actions\RemoveSecAdmin() )
|
148 |
-
->setMod( $this )
|
149 |
-
->remove();
|
150 |
-
break;
|
151 |
-
default:
|
152 |
-
break;
|
153 |
-
}
|
154 |
-
}
|
155 |
-
|
156 |
-
public function isSecAdminSessionValid() :bool {
|
157 |
-
return $this->getSecAdminTimeLeft() > 0;
|
158 |
-
}
|
159 |
-
|
160 |
-
public function isEnabledSecurityAdmin() :bool {
|
161 |
-
/** @var SecurityAdmin\Options $opts */
|
162 |
-
$opts = $this->getOptions();
|
163 |
-
return $this->isModOptEnabled() &&
|
164 |
-
( count( $opts->getSecurityAdminUsers() ) > 0 ||
|
165 |
-
( $opts->hasSecurityPIN() && $this->getSecAdminTimeout() > 0 )
|
166 |
-
);
|
167 |
-
}
|
168 |
-
|
169 |
-
/**
|
170 |
-
* @param bool $bSetOn
|
171 |
-
* @return bool
|
172 |
-
*/
|
173 |
-
public function setSecurityAdminStatusOnOff( $bSetOn = false ) {
|
174 |
-
/** @var Shield\Databases\Session\Update $oUpdater */
|
175 |
-
$oUpdater = $this->getDbHandler_Sessions()->getQueryUpdater();
|
176 |
-
return $bSetOn ?
|
177 |
-
$oUpdater->startSecurityAdmin( $this->getSession() )
|
178 |
-
: $oUpdater->terminateSecurityAdmin( $this->getSession() );
|
179 |
-
}
|
180 |
-
|
181 |
-
public function isValidSecAdminRequest() :bool {
|
182 |
-
return $this->isAccessKeyRequest() && $this->testSecAccessKeyRequest();
|
183 |
-
}
|
184 |
-
|
185 |
-
public function testSecAccessKeyRequest() :bool {
|
186 |
-
if ( !isset( $this->bValidSecAdminRequest ) ) {
|
187 |
-
$bValid = false;
|
188 |
-
$sReqKey = Services::Request()->post( 'sec_admin_key' );
|
189 |
-
if ( !empty( $sReqKey ) ) {
|
190 |
-
/** @var SecurityAdmin\Options $opts */
|
191 |
-
$opts = $this->getOptions();
|
192 |
-
$bValid = hash_equals( $opts->getSecurityPIN(), md5( $sReqKey ) );
|
193 |
-
if ( !$bValid ) {
|
194 |
-
$sEscaped = isset( $_POST[ 'sec_admin_key' ] ) ? $_POST[ 'sec_admin_key' ] : '';
|
195 |
-
if ( !empty( $sEscaped ) ) {
|
196 |
-
// Workaround for escaping of passwords
|
197 |
-
$bValid = hash_equals( $opts->getSecurityPIN(), md5( $sEscaped ) );
|
198 |
-
if ( $bValid ) {
|
199 |
-
$opts->setOpt( 'admin_access_key', md5( $sReqKey ) );
|
200 |
-
}
|
201 |
-
}
|
202 |
-
}
|
203 |
-
|
204 |
-
$this->getCon()->fireEvent( $bValid ? 'key_success' : 'key_fail' );
|
205 |
-
}
|
206 |
-
|
207 |
-
$this->bValidSecAdminRequest = $bValid;
|
208 |
-
}
|
209 |
-
return $this->bValidSecAdminRequest;
|
210 |
-
}
|
211 |
-
|
212 |
-
private function isAccessKeyRequest() :bool {
|
213 |
-
return strlen( Services::Request()->post( 'sec_admin_key', '' ) ) > 0;
|
214 |
-
}
|
215 |
-
|
216 |
-
public function verifyAccessKey( string $key ) :bool {
|
217 |
-
/** @var SecurityAdmin\Options $opts */
|
218 |
-
$opts = $this->getOptions();
|
219 |
-
return !empty( $key ) && hash_equals( $opts->getSecurityPIN(), md5( $key ) );
|
220 |
-
}
|
221 |
-
|
222 |
-
public function getWhitelabelOptions() :array {
|
223 |
-
$opts = $this->getOptions();
|
224 |
-
$sMain = $opts->getOpt( 'wl_pluginnamemain' );
|
225 |
-
$sMenu = $opts->getOpt( 'wl_namemenu' );
|
226 |
-
if ( empty( $sMenu ) ) {
|
227 |
-
$sMenu = $sMain;
|
228 |
-
}
|
229 |
-
|
230 |
-
return [
|
231 |
-
'name_main' => $sMain,
|
232 |
-
'name_menu' => $sMenu,
|
233 |
-
'name_company' => $opts->getOpt( 'wl_companyname' ),
|
234 |
-
'description' => $opts->getOpt( 'wl_description' ),
|
235 |
-
'url_home' => $opts->getOpt( 'wl_homeurl' ),
|
236 |
-
'url_icon' => $this->buildWlImageUrl( 'wl_menuiconurl' ),
|
237 |
-
'url_dashboardlogourl' => $this->buildWlImageUrl( 'wl_dashboardlogourl' ),
|
238 |
-
'url_login2fa_logourl' => $this->buildWlImageUrl( 'wl_login2fa_logourl' ),
|
239 |
-
];
|
240 |
-
}
|
241 |
-
|
242 |
-
/**
|
243 |
-
* We cater for 3 options:
|
244 |
-
* Full URL
|
245 |
-
* Relative path URL: i.e. starts with /
|
246 |
-
* Or Plugin image URL i.e. doesn't start with HTTP or /
|
247 |
-
* @param string $key
|
248 |
-
* @return string
|
249 |
-
*/
|
250 |
-
private function buildWlImageUrl( $key ) {
|
251 |
-
$opts = $this->getOptions();
|
252 |
-
|
253 |
-
$sLogoUrl = $opts->getOpt( $key );
|
254 |
-
if ( empty( $sLogoUrl ) ) {
|
255 |
-
$opts->resetOptToDefault( $key );
|
256 |
-
$sLogoUrl = $opts->getOpt( $key );
|
257 |
-
}
|
258 |
-
if ( !empty( $sLogoUrl ) && !Services::Data()->isValidWebUrl( $sLogoUrl ) && strpos( $sLogoUrl, '/' ) !== 0 ) {
|
259 |
-
$sLogoUrl = $this->getCon()->getPluginUrl_Image( $sLogoUrl );
|
260 |
-
if ( empty( $sLogoUrl ) ) {
|
261 |
-
$opts->resetOptToDefault( $key );
|
262 |
-
$sLogoUrl = $this->getCon()->getPluginUrl_Image( $opts->getOpt( $key ) );
|
263 |
-
}
|
264 |
-
}
|
265 |
-
|
266 |
-
return $sLogoUrl;
|
267 |
-
}
|
268 |
-
|
269 |
-
public function isWlEnabled() :bool {
|
270 |
-
/** @var SecurityAdmin\Options $opts */
|
271 |
-
$opts = $this->getOptions();
|
272 |
-
return $opts->isEnabledWhitelabel() && $this->isPremium();
|
273 |
-
}
|
274 |
-
|
275 |
-
public function isWlHideUpdates() :bool {
|
276 |
-
return $this->isWlEnabled() && $this->getOptions()->isOpt( 'wl_hide_updates', 'Y' );
|
277 |
-
}
|
278 |
-
|
279 |
-
/**
|
280 |
-
* @param string $pin
|
281 |
-
* @return $this
|
282 |
-
* @throws \Exception
|
283 |
-
*/
|
284 |
-
public function setNewPinManually( string $pin ) {
|
285 |
-
if ( empty( $pin ) ) {
|
286 |
-
throw new \Exception( 'Attempting to set an empty Security PIN.' );
|
287 |
-
}
|
288 |
-
if ( !$this->getCon()->isPluginAdmin() ) {
|
289 |
-
throw new \Exception( 'User does not have permission to update the Security PIN.' );
|
290 |
-
}
|
291 |
-
|
292 |
-
$this->setIsMainFeatureEnabled( true );
|
293 |
-
$this->getOptions()->setOpt( 'admin_access_key', md5( $pin ) );
|
294 |
-
return $this->saveModOptions();
|
295 |
-
}
|
296 |
-
|
297 |
-
public function insertCustomJsVars_Admin() {
|
298 |
-
parent::insertCustomJsVars_Admin();
|
299 |
-
|
300 |
-
if ( $this->getSecAdminTimeLeft() > 0 ) {
|
301 |
-
$aInsertData = [
|
302 |
-
'ajax' => [
|
303 |
-
'check' => $this->getSecAdminCheckAjaxData(),
|
304 |
-
],
|
305 |
-
'is_sec_admin' => true, // if $nSecTimeLeft > 0
|
306 |
-
'timeleft' => $this->getSecAdminTimeLeft(), // JS uses milliseconds
|
307 |
-
'strings' => [
|
308 |
-
'confirm' => __( 'Security Admin session has timed-out.', 'wp-simple-firewall' ).' '.__( 'Reload now?', 'wp-simple-firewall' ),
|
309 |
-
'nearly' => __( 'Security Admin session has nearly timed-out.', 'wp-simple-firewall' ),
|
310 |
-
'expired' => __( 'Security Admin session has timed-out.', 'wp-simple-firewall' )
|
311 |
-
]
|
312 |
-
];
|
313 |
-
}
|
314 |
-
else {
|
315 |
-
$aInsertData = [
|
316 |
-
'ajax' => [
|
317 |
-
'req_email_remove' => $this->getAjaxActionData( 'req_email_remove' ),
|
318 |
-
],
|
319 |
-
'strings' => [
|
320 |
-
'are_you_sure' => __( 'Are you sure?', 'wp-simple-firewall' )
|
321 |
-
]
|
322 |
-
];
|
323 |
-
}
|
324 |
-
|
325 |
-
if ( !empty( $aInsertData ) ) {
|
326 |
-
wp_localize_script(
|
327 |
-
$this->prefix( 'plugin' ),
|
328 |
-
'icwp_wpsf_vars_secadmin',
|
329 |
-
$aInsertData
|
330 |
-
);
|
331 |
-
}
|
332 |
-
}
|
333 |
-
|
334 |
-
/**
|
335 |
-
* This is the point where you would want to do any options verification
|
336 |
-
*/
|
337 |
-
protected function doPrePluginOptionsSave() {
|
338 |
-
/** @var SecurityAdmin\Options $opts */
|
339 |
-
$opts = $this->getOptions();
|
340 |
-
|
341 |
-
if ( hash_equals( $opts->getSecurityPIN(), self::HASH_DELETE ) ) {
|
342 |
-
$opts->clearSecurityAdminKey();
|
343 |
-
$this->setSecurityAdminStatusOnOff( false );
|
344 |
-
}
|
345 |
-
|
346 |
-
// Restricting Activate Plugins also means restricting the rest.
|
347 |
-
$aPluginsRestrictions = $opts->getAdminAccessArea_Plugins();
|
348 |
-
if ( in_array( 'activate_plugins', $aPluginsRestrictions ) ) {
|
349 |
-
$opts->setOpt(
|
350 |
-
'admin_access_restrict_plugins',
|
351 |
-
array_unique( array_merge( $aPluginsRestrictions, [
|
352 |
-
'install_plugins',
|
353 |
-
'update_plugins',
|
354 |
-
'delete_plugins'
|
355 |
-
] ) )
|
356 |
-
);
|
357 |
-
}
|
358 |
-
|
359 |
-
// Restricting Switch (Activate) Themes also means restricting the rest.
|
360 |
-
$aThemesRestrictions = $opts->getAdminAccessArea_Themes();
|
361 |
-
if ( in_array( 'switch_themes', $aThemesRestrictions ) && in_array( 'edit_theme_options', $aThemesRestrictions ) ) {
|
362 |
-
$opts->setOpt(
|
363 |
-
'admin_access_restrict_themes',
|
364 |
-
array_unique( array_merge( $aThemesRestrictions, [
|
365 |
-
'install_themes',
|
366 |
-
'update_themes',
|
367 |
-
'delete_themes'
|
368 |
-
] ) )
|
369 |
-
);
|
370 |
-
}
|
371 |
-
|
372 |
-
$aPostRestrictions = $opts->getAdminAccessArea_Posts();
|
373 |
-
if ( in_array( 'edit', $aPostRestrictions ) ) {
|
374 |
-
$opts->setOpt(
|
375 |
-
'admin_access_restrict_posts',
|
376 |
-
array_unique( array_merge( $aPostRestrictions, [ 'create', 'publish', 'delete' ] ) )
|
377 |
-
);
|
378 |
-
}
|
379 |
-
}
|
380 |
-
|
381 |
-
public function preDeactivatePlugin() {
|
382 |
-
if ( !$this->getCon()->isPluginAdmin() ) {
|
383 |
-
Services::WpGeneral()->wpDie(
|
384 |
-
__( "Sorry, this plugin is protected against unauthorised attempts to disable it.", 'wp-simple-firewall' )
|
385 |
-
.'<br />'.sprintf( '<a href="%s">%s</a>',
|
386 |
-
$this->getUrl_AdminPage(),
|
387 |
-
__( "You'll just need to authenticate first and try again.", 'wp-simple-firewall' )
|
388 |
-
)
|
389 |
-
);
|
390 |
-
}
|
391 |
-
}
|
392 |
-
|
393 |
-
protected function getNamespaceBase() :string {
|
394 |
-
return 'SecurityAdmin';
|
395 |
-
}
|
396 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
src/features/audit_trail.php
DELETED
@@ -1,111 +0,0 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
use FernleafSystems\Wordpress\Plugin\Shield;
|
4 |
-
use FernleafSystems\Wordpress\Services\Services;
|
5 |
-
|
6 |
-
/**
|
7 |
-
* @deprecated 10.1
|
8 |
-
*/
|
9 |
-
class ICWP_WPSF_FeatureHandler_AuditTrail extends ICWP_WPSF_FeatureHandler_BaseWpsf {
|
10 |
-
|
11 |
-
public function getDbHandler_AuditTrail() :Shield\Databases\AuditTrail\Handler {
|
12 |
-
return $this->getDbH( 'audit' );
|
13 |
-
}
|
14 |
-
|
15 |
-
/**
|
16 |
-
* @return bool
|
17 |
-
* @throws \Exception
|
18 |
-
*/
|
19 |
-
protected function isReadyToExecute() {
|
20 |
-
return $this->getDbHandler_AuditTrail()->isReady()
|
21 |
-
&& parent::isReadyToExecute();
|
22 |
-
}
|
23 |
-
|
24 |
-
/**
|
25 |
-
* @return array
|
26 |
-
*/
|
27 |
-
public function getAllContexts() {
|
28 |
-
return [
|
29 |
-
'all' => 'All', //special
|
30 |
-
'wpsf' => $this->getCon()->getHumanName(),
|
31 |
-
'wordpress' => 'WordPress',
|
32 |
-
'users' => 'Users',
|
33 |
-
'posts' => 'Posts',
|
34 |
-
'plugins' => 'Plugins',
|
35 |
-
'themes' => 'Themes',
|
36 |
-
'emails' => 'Emails',
|
37 |
-
];
|
38 |
-
}
|
39 |
-
|
40 |
-
/**
|
41 |
-
* See plugin controller for the nature of $aData wpPrivacyExport()
|
42 |
-
*
|
43 |
-
* @param array $aExportItems
|
44 |
-
* @param string $sEmail
|
45 |
-
* @param int $nPage
|
46 |
-
* @return array
|
47 |
-
*/
|
48 |
-
public function onWpPrivacyExport( $aExportItems, $sEmail, $nPage = 1 ) {
|
49 |
-
|
50 |
-
$oUser = Services::WpUsers()->getUserByEmail( $sEmail );
|
51 |
-
|
52 |
-
$aExportItem = [
|
53 |
-
'group_id' => $this->prefix(),
|
54 |
-
'group_label' => sprintf( __( '[%s] Audit Trail Entries', 'wp-simple-firewall' ), $this->getCon()
|
55 |
-
->getHumanName() ),
|
56 |
-
'item_id' => $this->prefix( 'audit-trail' ),
|
57 |
-
'data' => [],
|
58 |
-
];
|
59 |
-
|
60 |
-
try {
|
61 |
-
/** @var Shield\Databases\AuditTrail\Select $oFinder */
|
62 |
-
$oFinder = $this->getDbHandler_AuditTrail()
|
63 |
-
->getQuerySelector();
|
64 |
-
$oFinder->filterByUsername( $oUser->user_login );
|
65 |
-
|
66 |
-
$oWp = Services::WpGeneral();
|
67 |
-
/** @var Shield\Databases\AuditTrail\EntryVO $oEntry */
|
68 |
-
foreach ( $oFinder->query() as $oEntry ) {
|
69 |
-
$aExportItem[ 'data' ][] = [
|
70 |
-
$sTimeStamp = $oWp->getTimeStringForDisplay( $oEntry->getCreatedAt() ),
|
71 |
-
'name' => sprintf( '[%s] Audit Trail Entry', $sTimeStamp ),
|
72 |
-
'value' => sprintf( '[IP:%s] %s', $oEntry->ip, $oEntry->message )
|
73 |
-
];
|
74 |
-
}
|
75 |
-
|
76 |
-
if ( !empty( $aExportItem[ 'data' ] ) ) {
|
77 |
-
$aExportItems[] = $aExportItem;
|
78 |
-
}
|
79 |
-
}
|
80 |
-
catch ( \Exception $oE ) {
|
81 |
-
}
|
82 |
-
|
83 |
-
return $aExportItems;
|
84 |
-
}
|
85 |
-
|
86 |
-
/**
|
87 |
-
* See plugin controller for the nature of $aData wpPrivacyErase()
|
88 |
-
*
|
89 |
-
* @param array $aData
|
90 |
-
* @param string $sEmail
|
91 |
-
* @param int $nPage
|
92 |
-
* @return array
|
93 |
-
*/
|
94 |
-
public function onWpPrivacyErase( $aData, $sEmail, $nPage = 1 ) {
|
95 |
-
try {
|
96 |
-
$oThisUsername = Services::WpUsers()->getUserByEmail( $sEmail )->user_login;
|
97 |
-
$this->getDbHandler_AuditTrail()
|
98 |
-
->getQueryDeleter()
|
99 |
-
->addWhereSearch( 'wp_username', $oThisUsername )
|
100 |
-
->all();
|
101 |
-
$aData[ 'messages' ][] = sprintf( '%s Audit Entries deleted', $this->getCon()->getHumanName() );
|
102 |
-
}
|
103 |
-
catch ( \Exception $oE ) {
|
104 |
-
}
|
105 |
-
return $aData;
|
106 |
-
}
|
107 |
-
|
108 |
-
protected function getNamespaceBase() :string {
|
109 |
-
return 'AuditTrail';
|
110 |
-
}
|
111 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
src/features/autoupdates.php
DELETED
@@ -1,16 +0,0 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
use FernleafSystems\Wordpress\Plugin\Shield;
|
4 |
-
|
5 |
-
/**
|
6 |
-
* @deprecated 10.1
|
7 |
-
*/
|
8 |
-
class ICWP_WPSF_FeatureHandler_Autoupdates extends ICWP_WPSF_FeatureHandler_BaseWpsf {
|
9 |
-
|
10 |
-
/**
|
11 |
-
* @return string
|
12 |
-
*/
|
13 |
-
protected function getNamespaceBase() :string {
|
14 |
-
return 'Autoupdates';
|
15 |
-
}
|
16 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
src/features/base.php
DELETED
@@ -1,1599 +0,0 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
use FernleafSystems\Wordpress\Plugin\Shield;
|
4 |
-
use FernleafSystems\Wordpress\Services\Services;
|
5 |
-
|
6 |
-
/**
|
7 |
-
* Class ICWP_WPSF_FeatureHandler_Base
|
8 |
-
*/
|
9 |
-
abstract class ICWP_WPSF_FeatureHandler_Base {
|
10 |
-
|
11 |
-
use Shield\Modules\PluginControllerConsumer;
|
12 |
-
|
13 |
-
/**
|
14 |
-
* @var string
|
15 |
-
*/
|
16 |
-
private $sOptionsStoreKey;
|
17 |
-
|
18 |
-
/**
|
19 |
-
* @var string
|
20 |
-
*/
|
21 |
-
protected $sModSlug;
|
22 |
-
|
23 |
-
/**
|
24 |
-
* @var bool
|
25 |
-
*/
|
26 |
-
protected $bImportExportWhitelistNotify = false;
|
27 |
-
|
28 |
-
/**
|
29 |
-
* @var ICWP_WPSF_FeatureHandler_Email
|
30 |
-
* @deprecated 10.1
|
31 |
-
*/
|
32 |
-
private static $oEmailHandler;
|
33 |
-
|
34 |
-
/**
|
35 |
-
* @var Shield\Modules\Base\BaseProcessor
|
36 |
-
*/
|
37 |
-
private $oProcessor;
|
38 |
-
|
39 |
-
/**
|
40 |
-
* @var ICWP_WPSF_Wizard_Base
|
41 |
-
*/
|
42 |
-
private $oWizard;
|
43 |
-
|
44 |
-
/**
|
45 |
-
* @var Shield\Modules\Base\BaseReporting
|
46 |
-
*/
|
47 |
-
private $oReporting;
|
48 |
-
|
49 |
-
/**
|
50 |
-
* @var Shield\Modules\Base\UI
|
51 |
-
*/
|
52 |
-
private $oUI;
|
53 |
-
|
54 |
-
/**
|
55 |
-
* @var Shield\Modules\Base\Options
|
56 |
-
*/
|
57 |
-
private $oOpts;
|
58 |
-
|
59 |
-
/**
|
60 |
-
* @var Shield\Modules\Base\WpCli
|
61 |
-
*/
|
62 |
-
private $oWpCli;
|
63 |
-
|
64 |
-
/**
|
65 |
-
* @var Shield\Databases\Base\Handler[]
|
66 |
-
*/
|
67 |
-
private $aDbHandlers;
|
68 |
-
|
69 |
-
/**
|
70 |
-
* @param Shield\Controller\Controller $oPluginController
|
71 |
-
* @param array $aMod
|
72 |
-
* @throws \Exception
|
73 |
-
*/
|
74 |
-
public function __construct( $oPluginController, $aMod = [] ) {
|
75 |
-
if ( !$oPluginController instanceof Shield\Controller\Controller ) {
|
76 |
-
throw new \Exception( 'Plugin controller not supplied to Module' );
|
77 |
-
}
|
78 |
-
$this->setCon( $oPluginController );
|
79 |
-
|
80 |
-
if ( empty( $aMod[ 'storage_key' ] ) && empty( $aMod[ 'slug' ] ) ) {
|
81 |
-
throw new \Exception( 'Module storage key AND slug are undefined' );
|
82 |
-
}
|
83 |
-
|
84 |
-
$this->sOptionsStoreKey = empty( $aMod[ 'storage_key' ] ) ? $aMod[ 'slug' ] : $aMod[ 'storage_key' ];
|
85 |
-
if ( isset( $aMod[ 'slug' ] ) ) {
|
86 |
-
$this->sModSlug = $aMod[ 'slug' ];
|
87 |
-
}
|
88 |
-
|
89 |
-
if ( $this->verifyModuleMeetRequirements() ) {
|
90 |
-
$this->handleAutoPageRedirects();
|
91 |
-
$this->setupHooks( $aMod );
|
92 |
-
$this->doPostConstruction();
|
93 |
-
}
|
94 |
-
}
|
95 |
-
|
96 |
-
protected function setupHooks( array $aModProps ) {
|
97 |
-
$con = $this->getCon();
|
98 |
-
$nRunPriority = isset( $aModProps[ 'load_priority' ] ) ? $aModProps[ 'load_priority' ] : 100;
|
99 |
-
|
100 |
-
add_action( $con->prefix( 'modules_loaded' ), function () {
|
101 |
-
$this->onModulesLoaded();
|
102 |
-
}, $nRunPriority );
|
103 |
-
add_action( $con->prefix( 'run_processors' ), [ $this, 'onRunProcessors' ], $nRunPriority );
|
104 |
-
add_action( 'init', [ $this, 'onWpInit' ], 1 );
|
105 |
-
|
106 |
-
$nMenuPri = isset( $aModProps[ 'menu_priority' ] ) ? $aModProps[ 'menu_priority' ] : 100;
|
107 |
-
add_filter( $con->prefix( 'submenu_items' ), [ $this, 'supplySubMenuItem' ], $nMenuPri );
|
108 |
-
add_action( $con->prefix( 'plugin_shutdown' ), [ $this, 'onPluginShutdown' ] );
|
109 |
-
add_action( $con->prefix( 'deactivate_plugin' ), [ $this, 'onPluginDeactivate' ] );
|
110 |
-
add_action( $con->prefix( 'delete_plugin' ), [ $this, 'onPluginDelete' ] );
|
111 |
-
add_filter( $con->prefix( 'aggregate_all_plugin_options' ), [ $this, 'aggregateOptionsValues' ] );
|
112 |
-
|
113 |
-
add_filter( $con->prefix( 'register_admin_notices' ), [ $this, 'fRegisterAdminNotices' ] );
|
114 |
-
|
115 |
-
add_action( $con->prefix( 'daily_cron' ), [ $this, 'runDailyCron' ] );
|
116 |
-
add_action( $con->prefix( 'hourly_cron' ), [ $this, 'runHourlyCron' ] );
|
117 |
-
|
118 |
-
add_action( 'admin_enqueue_scripts', [ $this, 'onWpEnqueueAdminJs' ], 100 );
|
119 |
-
|
120 |
-
if ( is_admin() || is_network_admin() ) {
|
121 |
-
$this->loadAdminNotices();
|
122 |
-
}
|
123 |
-
|
124 |
-
// if ( $this->isAdminOptionsPage() ) {
|
125 |
-
// add_action( 'current_screen', array( $this, 'onSetCurrentScreen' ) );
|
126 |
-
// }
|
127 |
-
|
128 |
-
$this->setupCustomHooks();
|
129 |
-
}
|
130 |
-
|
131 |
-
protected function setupCustomHooks() {
|
132 |
-
}
|
133 |
-
|
134 |
-
protected function doPostConstruction() {
|
135 |
-
}
|
136 |
-
|
137 |
-
public function runDailyCron() {
|
138 |
-
$this->cleanupDatabases();
|
139 |
-
}
|
140 |
-
|
141 |
-
public function runHourlyCron() {
|
142 |
-
}
|
143 |
-
|
144 |
-
protected function cleanupDatabases() {
|
145 |
-
foreach ( $this->getDbHandlers( true ) as $oDbh ) {
|
146 |
-
if ( $oDbh instanceof Shield\Databases\Base\Handler && $oDbh->isReady() ) {
|
147 |
-
$oDbh->autoCleanDb();
|
148 |
-
}
|
149 |
-
}
|
150 |
-
}
|
151 |
-
|
152 |
-
/**
|
153 |
-
* @param bool $bInitAll
|
154 |
-
* @return Shield\Databases\Base\Handler[]
|
155 |
-
*/
|
156 |
-
protected function getDbHandlers( $bInitAll = false ) {
|
157 |
-
if ( $bInitAll ) {
|
158 |
-
foreach ( $this->getAllDbClasses() as $sDbSlug => $sDbClass ) {
|
159 |
-
$this->getDbH( $sDbSlug );
|
160 |
-
}
|
161 |
-
}
|
162 |
-
return is_array( $this->aDbHandlers ) ? $this->aDbHandlers : [];
|
163 |
-
}
|
164 |
-
|
165 |
-
/**
|
166 |
-
* @param string $sDbhKey
|
167 |
-
* @return Shield\Databases\Base\Handler|mixed|false
|
168 |
-
*/
|
169 |
-
protected function getDbH( $sDbhKey ) {
|
170 |
-
$dbh = false;
|
171 |
-
|
172 |
-
if ( !is_array( $this->aDbHandlers ) ) {
|
173 |
-
$this->aDbHandlers = [];
|
174 |
-
}
|
175 |
-
|
176 |
-
if ( !empty( $this->aDbHandlers[ $sDbhKey ] ) ) {
|
177 |
-
$dbh = $this->aDbHandlers[ $sDbhKey ];
|
178 |
-
}
|
179 |
-
else {
|
180 |
-
$aDbClasses = $this->getAllDbClasses();
|
181 |
-
if ( isset( $aDbClasses[ $sDbhKey ] ) ) {
|
182 |
-
/** @var Shield\Databases\Base\Handler $dbh */
|
183 |
-
$dbh = new $aDbClasses[ $sDbhKey ]();
|
184 |
-
try {
|
185 |
-
$dbh->setMod( $this )->tableInit();
|
186 |
-
}
|
187 |
-
catch ( \Exception $e ) {
|
188 |
-
}
|
189 |
-
}
|
190 |
-
$this->aDbHandlers[ $sDbhKey ] = $dbh;
|
191 |
-
}
|
192 |
-
|
193 |
-
return $dbh;
|
194 |
-
}
|
195 |
-
|
196 |
-
/**
|
197 |
-
* @return string[]
|
198 |
-
*/
|
199 |
-
private function getAllDbClasses() {
|
200 |
-
$classes = $this->getOptions()->getDef( 'db_classes' );
|
201 |
-
return is_array( $classes ) ? $classes : [];
|
202 |
-
}
|
203 |
-
|
204 |
-
/**
|
205 |
-
* @return false|Shield\Modules\Base\Upgrade|mixed
|
206 |
-
*/
|
207 |
-
public function getUpgradeHandler() {
|
208 |
-
return $this->loadModElement( 'Upgrade' );
|
209 |
-
}
|
210 |
-
|
211 |
-
/**
|
212 |
-
* @param string $sEncoding
|
213 |
-
* @return array
|
214 |
-
*/
|
215 |
-
public function getAjaxFormParams( $sEncoding = 'none' ) {
|
216 |
-
$oReq = Services::Request();
|
217 |
-
$aFormParams = [];
|
218 |
-
$sRaw = $oReq->post( 'form_params', '' );
|
219 |
-
|
220 |
-
if ( !empty( $sRaw ) ) {
|
221 |
-
|
222 |
-
$sMaybeEncoding = $oReq->post( 'enc_params' );
|
223 |
-
if ( in_array( $sMaybeEncoding, [ 'none', 'lz-string', 'b64' ] ) ) {
|
224 |
-
$sEncoding = $sMaybeEncoding;
|
225 |
-
}
|
226 |
-
|
227 |
-
switch ( $sEncoding ) {
|
228 |
-
case 'lz-string':
|
229 |
-
$sRaw = \LZCompressor\LZString::decompress( base64_decode( $sRaw ) );
|
230 |
-
break;
|
231 |
-
|
232 |
-
case 'b64':
|
233 |
-
$sRaw = base64_decode( $sRaw );
|
234 |
-
break;
|
235 |
-
|
236 |
-
case 'none':
|
237 |
-
default:
|
238 |
-
break;
|
239 |
-
}
|
240 |
-
|
241 |
-
parse_str( $sRaw, $aFormParams );
|
242 |
-
}
|
243 |
-
return $aFormParams;
|
244 |
-
}
|
245 |
-
|
246 |
-
/**
|
247 |
-
* @param array $aAdminNotices
|
248 |
-
* @return array
|
249 |
-
*/
|
250 |
-
public function fRegisterAdminNotices( $aAdminNotices ) {
|
251 |
-
if ( !is_array( $aAdminNotices ) ) {
|
252 |
-
$aAdminNotices = [];
|
253 |
-
}
|
254 |
-
return array_merge( $aAdminNotices, $this->getOptions()->getAdminNotices() );
|
255 |
-
}
|
256 |
-
|
257 |
-
private function verifyModuleMeetRequirements() :bool {
|
258 |
-
$bMeetsReqs = true;
|
259 |
-
|
260 |
-
$aPhpReqs = $this->getOptions()->getFeatureRequirement( 'php' );
|
261 |
-
if ( !empty( $aPhpReqs ) ) {
|
262 |
-
|
263 |
-
if ( !empty( $aPhpReqs[ 'version' ] ) ) {
|
264 |
-
$bMeetsReqs = $bMeetsReqs && Services::Data()->getPhpVersionIsAtLeast( $aPhpReqs[ 'version' ] );
|
265 |
-
}
|
266 |
-
if ( !empty( $aPhpReqs[ 'functions' ] ) && is_array( $aPhpReqs[ 'functions' ] ) ) {
|
267 |
-
foreach ( $aPhpReqs[ 'functions' ] as $sFunction ) {
|
268 |
-
$bMeetsReqs = $bMeetsReqs && function_exists( $sFunction );
|
269 |
-
}
|
270 |
-
}
|
271 |
-
if ( !empty( $aPhpReqs[ 'constants' ] ) && is_array( $aPhpReqs[ 'constants' ] ) ) {
|
272 |
-
foreach ( $aPhpReqs[ 'constants' ] as $sConstant ) {
|
273 |
-
$bMeetsReqs = $bMeetsReqs && defined( $sConstant );
|
274 |
-
}
|
275 |
-
}
|
276 |
-
}
|
277 |
-
|
278 |
-
return $bMeetsReqs;
|
279 |
-
}
|
280 |
-
|
281 |
-
protected function onModulesLoaded() {
|
282 |
-
}
|
283 |
-
|
284 |
-
public function onRunProcessors() {
|
285 |
-
$oOpts = $this->getOptions();
|
286 |
-
if ( $oOpts->getFeatureProperty( 'auto_load_processor' ) ) {
|
287 |
-
$this->loadProcessor();
|
288 |
-
}
|
289 |
-
try {
|
290 |
-
$bSkip = (bool)$oOpts->getFeatureProperty( 'skip_processor' );
|
291 |
-
if ( !$bSkip && !$this->isUpgrading() && $this->isModuleEnabled() && $this->isReadyToExecute() ) {
|
292 |
-
$this->doExecuteProcessor();
|
293 |
-
}
|
294 |
-
}
|
295 |
-
catch ( \Exception $e ) {
|
296 |
-
}
|
297 |
-
}
|
298 |
-
|
299 |
-
/**
|
300 |
-
* @return bool
|
301 |
-
* @throws \Exception
|
302 |
-
*/
|
303 |
-
protected function isReadyToExecute() {
|
304 |
-
return !is_null( $this->getProcessor() );
|
305 |
-
}
|
306 |
-
|
307 |
-
protected function doExecuteProcessor() {
|
308 |
-
$this->getProcessor()->execute();
|
309 |
-
}
|
310 |
-
|
311 |
-
public function onWpInit() {
|
312 |
-
}
|
313 |
-
|
314 |
-
/**
|
315 |
-
* We have to do it this way as the "page hook" is built upon the top-level plugin
|
316 |
-
* menu name. But what if we white label? So we need to dynamically grab the page hook
|
317 |
-
*/
|
318 |
-
public function onSetCurrentScreen() {
|
319 |
-
global $page_hook;
|
320 |
-
add_action( 'load-'.$page_hook, [ $this, 'onLoadOptionsScreen' ] );
|
321 |
-
}
|
322 |
-
|
323 |
-
public function onLoadOptionsScreen() {
|
324 |
-
if ( $this->getCon()->isValidAdminArea() ) {
|
325 |
-
$this->buildContextualHelp();
|
326 |
-
}
|
327 |
-
}
|
328 |
-
|
329 |
-
/**
|
330 |
-
* Override this and adapt per feature
|
331 |
-
* @return Shield\Modules\Base\BaseProcessor|mixed
|
332 |
-
*/
|
333 |
-
protected function loadProcessor() {
|
334 |
-
if ( !isset( $this->oProcessor ) ) {
|
335 |
-
try {
|
336 |
-
// TODO: Remove 'abstract' from base processor after transition to new processors is complete
|
337 |
-
$class = $this->findElementClass( 'Processor', true );
|
338 |
-
}
|
339 |
-
catch ( Exception $e ) {
|
340 |
-
$class = $this->getProcessorClassName();
|
341 |
-
}
|
342 |
-
if ( !@class_exists( $class ) ) {
|
343 |
-
return null;
|
344 |
-
}
|
345 |
-
$this->oProcessor = new $class( $this );
|
346 |
-
}
|
347 |
-
return $this->oProcessor;
|
348 |
-
}
|
349 |
-
|
350 |
-
/**
|
351 |
-
* This is the old method
|
352 |
-
* @deprecated 10.1
|
353 |
-
*/
|
354 |
-
protected function getProcessorClassName() :string {
|
355 |
-
return implode( '_',
|
356 |
-
[
|
357 |
-
strtoupper( $this->getCon()->getPluginPrefix( '_' ) ),
|
358 |
-
'Processor',
|
359 |
-
str_replace( ' ', '', ucwords( str_replace( '_', ' ', $this->getSlug() ) ) )
|
360 |
-
]
|
361 |
-
);
|
362 |
-
}
|
363 |
-
|
364 |
-
/**
|
365 |
-
* Override this and adapt per feature
|
366 |
-
* @return string
|
367 |
-
*/
|
368 |
-
protected function getWizardClassName() {
|
369 |
-
return implode( '_',
|
370 |
-
[
|
371 |
-
strtoupper( $this->getCon()->getPluginPrefix( '_' ) ),
|
372 |
-
'Wizard',
|
373 |
-
str_replace( ' ', '', ucwords( str_replace( '_', ' ', $this->getSlug() ) ) )
|
374 |
-
]
|
375 |
-
);
|
376 |
-
}
|
377 |
-
|
378 |
-
public function isUpgrading() :bool {
|
379 |
-
return $this->getCon()->getIsRebuildOptionsFromFile() || $this->getOptions()->getRebuildFromFile();
|
380 |
-
}
|
381 |
-
|
382 |
-
/**
|
383 |
-
* Hooked to the plugin's main plugin_shutdown action
|
384 |
-
*/
|
385 |
-
public function onPluginShutdown() {
|
386 |
-
if ( !$this->getCon()->plugin_deleting ) {
|
387 |
-
if ( rand( 1, 40 ) === 2 ) {
|
388 |
-
// cleanup databases randomly just in-case cron doesn't run.
|
389 |
-
$this->cleanupDatabases();
|
390 |
-
}
|
391 |
-
$this->saveModOptions();
|
392 |
-
}
|
393 |
-
}
|
394 |
-
|
395 |
-
public function getOptionsStorageKey() :string {
|
396 |
-
return $this->getCon()->prefixOption( $this->sOptionsStoreKey ).'_options';
|
397 |
-
}
|
398 |
-
|
399 |
-
/**
|
400 |
-
* @return Shield\Modules\Base\BaseProcessor|\FernleafSystems\Utilities\Logic\OneTimeExecute|mixed
|
401 |
-
*/
|
402 |
-
public function getProcessor() {
|
403 |
-
return $this->loadProcessor();
|
404 |
-
}
|
405 |
-
|
406 |
-
public function getUrl_AdminPage() :string {
|
407 |
-
return Services::WpGeneral()
|
408 |
-
->getUrl_AdminPage(
|
409 |
-
$this->getModSlug(),
|
410 |
-
$this->getCon()->getIsWpmsNetworkAdminOnly()
|
411 |
-
);
|
412 |
-
}
|
413 |
-
|
414 |
-
/**
|
415 |
-
* @param string $sAction
|
416 |
-
* @return string
|
417 |
-
*/
|
418 |
-
public function buildAdminActionNonceUrl( $sAction ) {
|
419 |
-
$aActionNonce = $this->getNonceActionData( $sAction );
|
420 |
-
$aActionNonce[ 'ts' ] = Services::Request()->ts();
|
421 |
-
return add_query_arg( $aActionNonce, $this->getUrl_AdminPage() );
|
422 |
-
}
|
423 |
-
|
424 |
-
protected function getModActionParams( string $action ) :array {
|
425 |
-
$con = $this->getCon();
|
426 |
-
return [
|
427 |
-
'action' => $con->prefix(),
|
428 |
-
'exec' => $action,
|
429 |
-
'mod_slug' => $this->getModSlug(),
|
430 |
-
'ts' => Services::Request()->ts(),
|
431 |
-
'exec_nonce' => substr(
|
432 |
-
hash_hmac( 'md5', $action.Services::Request()->ts(), $con->getSiteInstallationId() )
|
433 |
-
, 0, 6 )
|
434 |
-
];
|
435 |
-
}
|
436 |
-
|
437 |
-
/**
|
438 |
-
* @return bool
|
439 |
-
* @throws \Exception
|
440 |
-
*/
|
441 |
-
protected function verifyModActionRequest() :bool {
|
442 |
-
$bValid = false;
|
443 |
-
|
444 |
-
$con = $this->getCon();
|
445 |
-
$req = Services::Request();
|
446 |
-
|
447 |
-
$sExec = $req->request( 'exec' );
|
448 |
-
if ( !empty( $sExec ) && $req->request( 'action' ) == $con->prefix() ) {
|
449 |
-
|
450 |
-
|
451 |
-
if ( wp_verify_nonce( $req->request( 'exec_nonce' ), $sExec ) && $con->getMeetsBasePermissions() ) {
|
452 |
-
$bValid = true;
|
453 |
-
}
|
454 |
-
else {
|
455 |
-
$bValid = $req->request( 'exec_nonce' ) ===
|
456 |
-
substr( hash_hmac( 'md5', $sExec.$req->request( 'ts' ), $con->getSiteInstallationId() ), 0, 6 );
|
457 |
-
}
|
458 |
-
if ( !$bValid ) {
|
459 |
-
throw new Exception( 'Invalid request' );
|
460 |
-
}
|
461 |
-
}
|
462 |
-
|
463 |
-
return $bValid;
|
464 |
-
}
|
465 |
-
|
466 |
-
public function getUrl_DirectLinkToOption( string $key ) :string {
|
467 |
-
$url = $this->getUrl_AdminPage();
|
468 |
-
$def = $this->getOptions()->getOptDefinition( $key );
|
469 |
-
if ( !empty( $def[ 'section' ] ) ) {
|
470 |
-
$url = $this->getUrl_DirectLinkToSection( $def[ 'section' ] );
|
471 |
-
}
|
472 |
-
return $url;
|
473 |
-
}
|
474 |
-
|
475 |
-
public function getUrl_DirectLinkToSection( string $section ) :string {
|
476 |
-
if ( $section == 'primary' ) {
|
477 |
-
$section = $this->getOptions()->getPrimarySection()[ 'slug' ];
|
478 |
-
}
|
479 |
-
return $this->getUrl_AdminPage().'#tab-'.$section;
|
480 |
-
}
|
481 |
-
|
482 |
-
/**
|
483 |
-
* TODO: Get rid of this crap and/or handle the \Exception thrown in loadFeatureHandler()
|
484 |
-
* @return ICWP_WPSF_FeatureHandler_Email
|
485 |
-
* @throws \Exception
|
486 |
-
* @deprecated 10.1
|
487 |
-
*/
|
488 |
-
public function getEmailHandler() {
|
489 |
-
return $this->getCon()->getModule( 'email' );
|
490 |
-
}
|
491 |
-
|
492 |
-
/**
|
493 |
-
* @return ICWP_WPSF_Processor_Email
|
494 |
-
*/
|
495 |
-
public function getEmailProcessor() {
|
496 |
-
return $this->getEmailHandler()->getProcessor();
|
497 |
-
}
|
498 |
-
|
499 |
-
/**
|
500 |
-
* @param bool $enable
|
501 |
-
* @return $this
|
502 |
-
*/
|
503 |
-
public function setIsMainFeatureEnabled( bool $enable ) {
|
504 |
-
$this->getOptions()->setOpt( 'enable_'.$this->getSlug(), $enable ? 'Y' : 'N' );
|
505 |
-
return $this;
|
506 |
-
}
|
507 |
-
|
508 |
-
public function isModuleEnabled() :bool {
|
509 |
-
/** @var Shield\Modules\Plugin\Options $oPluginOpts */
|
510 |
-
$oPluginOpts = $this->getCon()->getModule_Plugin()->getOptions();
|
511 |
-
|
512 |
-
if ( $this->getOptions()->getFeatureProperty( 'auto_enabled' ) === true ) {
|
513 |
-
// Auto enabled modules always run regardless
|
514 |
-
$enabled = true;
|
515 |
-
}
|
516 |
-
elseif ( $oPluginOpts->isPluginGloballyDisabled() ) {
|
517 |
-
$enabled = false;
|
518 |
-
}
|
519 |
-
elseif ( $this->getCon()->getIfForceOffActive() ) {
|
520 |
-
$enabled = false;
|
521 |
-
}
|
522 |
-
elseif ( $this->getOptions()->getFeatureProperty( 'premium' ) === true
|
523 |
-
&& !$this->isPremium() ) {
|
524 |
-
$enabled = false;
|
525 |
-
}
|
526 |
-
else {
|
527 |
-
$enabled = $this->isModOptEnabled();
|
528 |
-
}
|
529 |
-
|
530 |
-
return $enabled;
|
531 |
-
}
|
532 |
-
|
533 |
-
public function isModOptEnabled() :bool {
|
534 |
-
$opts = $this->getOptions();
|
535 |
-
return $opts->isOpt( $this->getEnableModOptKey(), 'Y' )
|
536 |
-
|| $opts->isOpt( $this->getEnableModOptKey(), true, true );
|
537 |
-
}
|
538 |
-
|
539 |
-
public function getEnableModOptKey() :string {
|
540 |
-
return 'enable_'.$this->getSlug();
|
541 |
-
}
|
542 |
-
|
543 |
-
public function getMainFeatureName() :string {
|
544 |
-
return __( $this->getOptions()->getFeatureProperty( 'name' ), 'wp-simple-firewall' );
|
545 |
-
}
|
546 |
-
|
547 |
-
public function getModSlug( bool $prefix = true ) :string {
|
548 |
-
return $prefix ? $this->prefix( $this->getSlug() ) : $this->getSlug();
|
549 |
-
}
|
550 |
-
|
551 |
-
/**
|
552 |
-
* @return string
|
553 |
-
*/
|
554 |
-
public function getSlug() {
|
555 |
-
if ( !isset( $this->sModSlug ) ) {
|
556 |
-
$this->sModSlug = $this->getOptions()->getFeatureProperty( 'slug' );
|
557 |
-
}
|
558 |
-
return $this->sModSlug;
|
559 |
-
}
|
560 |
-
|
561 |
-
/**
|
562 |
-
* @param array $aItems
|
563 |
-
* @return array
|
564 |
-
*/
|
565 |
-
public function supplySubMenuItem( $aItems ) {
|
566 |
-
|
567 |
-
$sTitle = $this->getOptions()->getFeatureProperty( 'menu_title' );
|
568 |
-
$sTitle = empty( $sTitle ) ? $this->getMainFeatureName() : __( $sTitle, 'wp-simple-firewall' );
|
569 |
-
|
570 |
-
if ( !empty( $sTitle ) ) {
|
571 |
-
|
572 |
-
$sHumanName = $this->getCon()->getHumanName();
|
573 |
-
|
574 |
-
$bMenuHighlighted = $this->getOptions()->getFeatureProperty( 'highlight_menu_item' );
|
575 |
-
if ( $bMenuHighlighted ) {
|
576 |
-
$sTitle = sprintf( '<span class="icwp_highlighted">%s</span>', $sTitle );
|
577 |
-
}
|
578 |
-
|
579 |
-
$sMenuPageTitle = $sTitle.' - '.$sHumanName;
|
580 |
-
$aItems[ $sMenuPageTitle ] = [
|
581 |
-
$sTitle,
|
582 |
-
$this->getModSlug(),
|
583 |
-
[ $this, 'displayModuleAdminPage' ],
|
584 |
-
$this->getIfShowModuleMenuItem()
|
585 |
-
];
|
586 |
-
|
587 |
-
$aAdditionalItems = $this->getOptions()->getAdditionalMenuItems();
|
588 |
-
if ( !empty( $aAdditionalItems ) && is_array( $aAdditionalItems ) ) {
|
589 |
-
|
590 |
-
foreach ( $aAdditionalItems as $aMenuItem ) {
|
591 |
-
$sMenuPageTitle = $sHumanName.' - '.$aMenuItem[ 'title' ];
|
592 |
-
$aItems[ $sMenuPageTitle ] = [
|
593 |
-
__( $aMenuItem[ 'title' ], 'wp-simple-firewall' ),
|
594 |
-
$this->prefix( $aMenuItem[ 'slug' ] ),
|
595 |
-
[ $this, $aMenuItem[ 'callback' ] ],
|
596 |
-
true
|
597 |
-
];
|
598 |
-
}
|
599 |
-
}
|
600 |
-
}
|
601 |
-
return $aItems;
|
602 |
-
}
|
603 |
-
|
604 |
-
/**
|
605 |
-
* Handles the case where we want to redirect certain menu requests to other pages
|
606 |
-
* of the plugin automatically. It lets us create custom menu items.
|
607 |
-
* This can of course be extended for any other types of redirect.
|
608 |
-
*/
|
609 |
-
public function handleAutoPageRedirects() {
|
610 |
-
$aConf = $this->getOptions()->getRawData_FullFeatureConfig();
|
611 |
-
if ( !empty( $aConf[ 'custom_redirects' ] ) && $this->getCon()->isValidAdminArea() ) {
|
612 |
-
foreach ( $aConf[ 'custom_redirects' ] as $aRedirect ) {
|
613 |
-
if ( Services::Request()->query( 'page' ) == $this->prefix( $aRedirect[ 'source_mod_page' ] ) ) {
|
614 |
-
Services::Response()->redirect(
|
615 |
-
$this->getCon()->getModule( $aRedirect[ 'target_mod_page' ] )->getUrl_AdminPage(),
|
616 |
-
$aRedirect[ 'query_args' ],
|
617 |
-
true,
|
618 |
-
false
|
619 |
-
);
|
620 |
-
}
|
621 |
-
}
|
622 |
-
}
|
623 |
-
}
|
624 |
-
|
625 |
-
/**
|
626 |
-
* @return array
|
627 |
-
*/
|
628 |
-
protected function getAdditionalMenuItem() {
|
629 |
-
return [];
|
630 |
-
}
|
631 |
-
|
632 |
-
/**
|
633 |
-
* TODO: not the place for this method.
|
634 |
-
* @return array[]
|
635 |
-
*/
|
636 |
-
public function getModulesSummaryData() {
|
637 |
-
return array_map(
|
638 |
-
function ( $mod ) {
|
639 |
-
return $mod->buildSummaryData();
|
640 |
-
},
|
641 |
-
$this->getCon()->modules
|
642 |
-
);
|
643 |
-
}
|
644 |
-
|
645 |
-
/**
|
646 |
-
* @return array
|
647 |
-
*/
|
648 |
-
public function buildSummaryData() {
|
649 |
-
$opts = $this->getOptions();
|
650 |
-
$sMenuTitle = $opts->getFeatureProperty( 'menu_title' );
|
651 |
-
|
652 |
-
$aSections = $opts->getSections();
|
653 |
-
foreach ( $aSections as $sSlug => $aSection ) {
|
654 |
-
try {
|
655 |
-
$aStrings = $this->getStrings()->getSectionStrings( $aSection[ 'slug' ] );
|
656 |
-
foreach ( $aStrings as $sKey => $sVal ) {
|
657 |
-
unset( $aSection[ $sKey ] );
|
658 |
-
$aSection[ $sKey ] = $sVal;
|
659 |
-
}
|
660 |
-
}
|
661 |
-
catch ( \Exception $e ) {
|
662 |
-
}
|
663 |
-
}
|
664 |
-
|
665 |
-
$aSum = [
|
666 |
-
'slug' => $this->getSlug(),
|
667 |
-
'enabled' => $this->getUIHandler()->isEnabledForUiSummary(),
|
668 |
-
'active' => $this->isThisModulePage() || $this->isPage_InsightsThisModule(),
|
669 |
-
'name' => $this->getMainFeatureName(),
|
670 |
-
'sidebar_name' => $opts->getFeatureProperty( 'sidebar_name' ),
|
671 |
-
'menu_title' => empty( $sMenuTitle ) ? $this->getMainFeatureName() : __( $sMenuTitle, 'wp-simple-firewall' ),
|
672 |
-
'href' => network_admin_url( 'admin.php?page='.$this->getModSlug() ),
|
673 |
-
'sections' => $aSections,
|
674 |
-
'options' => [],
|
675 |
-
'show_mod_opts' => $this->getIfShowModuleOpts(),
|
676 |
-
];
|
677 |
-
|
678 |
-
foreach ( $opts->getVisibleOptionsKeys() as $sOptKey ) {
|
679 |
-
try {
|
680 |
-
$aOptData = $this->getStrings()->getOptionStrings( $sOptKey );
|
681 |
-
$aOptData[ 'href' ] = $this->getUrl_DirectLinkToOption( $sOptKey );
|
682 |
-
$aSum[ 'options' ][ $sOptKey ] = $aOptData;
|
683 |
-
}
|
684 |
-
catch ( \Exception $e ) {
|
685 |
-
}
|
686 |
-
}
|
687 |
-
|
688 |
-
$aSum[ 'tooltip' ] = sprintf(
|
689 |
-
'%s',
|
690 |
-
empty( $aSum[ 'sidebar_name' ] ) ? $aSum[ 'name' ] : __( $aSum[ 'sidebar_name' ], 'wp-simple-firewall' )
|
691 |
-
);
|
692 |
-
return $aSum;
|
693 |
-
}
|
694 |
-
|
695 |
-
/**
|
696 |
-
* @return bool
|
697 |
-
*/
|
698 |
-
public function getIfShowModuleMenuItem() {
|
699 |
-
return (bool)$this->getOptions()->getFeatureProperty( 'show_module_menu_item' );
|
700 |
-
}
|
701 |
-
|
702 |
-
/**
|
703 |
-
* @return bool
|
704 |
-
*/
|
705 |
-
public function getIfShowModuleOpts() {
|
706 |
-
return (bool)$this->getOptions()->getFeatureProperty( 'show_module_options' );
|
707 |
-
}
|
708 |
-
|
709 |
-
/**
|
710 |
-
* Get config 'definition'.
|
711 |
-
* @param string $key
|
712 |
-
* @return mixed|null
|
713 |
-
*/
|
714 |
-
public function getDef( string $key ) {
|
715 |
-
return $this->getOptions()->getDef( $key );
|
716 |
-
}
|
717 |
-
|
718 |
-
/**
|
719 |
-
* @return $this
|
720 |
-
*/
|
721 |
-
public function clearLastErrors() {
|
722 |
-
return $this->setLastErrors( [] );
|
723 |
-
}
|
724 |
-
|
725 |
-
/**
|
726 |
-
* @param bool $bAsString
|
727 |
-
* @param string $sGlue
|
728 |
-
* @return string|array
|
729 |
-
*/
|
730 |
-
public function getLastErrors( $bAsString = false, $sGlue = " " ) {
|
731 |
-
$errors = $this->getOptions()->getOpt( 'last_errors' );
|
732 |
-
if ( !is_array( $errors ) ) {
|
733 |
-
$errors = [];
|
734 |
-
}
|
735 |
-
return $bAsString ? implode( $sGlue, $errors ) : $errors;
|
736 |
-
}
|
737 |
-
|
738 |
-
public function hasLastErrors() :bool {
|
739 |
-
return count( $this->getLastErrors( false ) ) > 0;
|
740 |
-
}
|
741 |
-
|
742 |
-
public function getTextOpt( string $key ) :string {
|
743 |
-
$sValue = $this->getOptions()->getOpt( $key, 'default' );
|
744 |
-
if ( $sValue == 'default' ) {
|
745 |
-
$sValue = $this->getTextOptDefault( $key );
|
746 |
-
}
|
747 |
-
return __( $sValue, 'wp-simple-firewall' );
|
748 |
-
}
|
749 |
-
|
750 |
-
/**
|
751 |
-
* Override this on each feature that has Text field options to supply the text field defaults
|
752 |
-
* @param string $sOptKey
|
753 |
-
* @return string
|
754 |
-
*/
|
755 |
-
public function getTextOptDefault( $sOptKey ) {
|
756 |
-
return 'Undefined Text Opt Default';
|
757 |
-
}
|
758 |
-
|
759 |
-
/**
|
760 |
-
* @param array|string $mErrors
|
761 |
-
* @return $this
|
762 |
-
*/
|
763 |
-
public function setLastErrors( $mErrors = [] ) {
|
764 |
-
if ( !is_array( $mErrors ) ) {
|
765 |
-
if ( is_string( $mErrors ) ) {
|
766 |
-
$mErrors = [ $mErrors ];
|
767 |
-
}
|
768 |
-
else {
|
769 |
-
$mErrors = [];
|
770 |
-
}
|
771 |
-
}
|
772 |
-
$this->getOptions()->setOpt( 'last_errors', $mErrors );
|
773 |
-
return $this;
|
774 |
-
}
|
775 |
-
|
776 |
-
public function setOptions( array $options ) {
|
777 |
-
$opts = $this->getOptions();
|
778 |
-
foreach ( $options as $key => $value ) {
|
779 |
-
$opts->setOpt( $key, $value );
|
780 |
-
}
|
781 |
-
}
|
782 |
-
|
783 |
-
public function isModuleRequest() :bool {
|
784 |
-
return $this->getModSlug() === Services::Request()->request( 'mod_slug' );
|
785 |
-
}
|
786 |
-
|
787 |
-
/**
|
788 |
-
* @param string $sAction
|
789 |
-
* @param bool $bAsJsonEncodedObject
|
790 |
-
* @return array|string
|
791 |
-
*/
|
792 |
-
public function getAjaxActionData( $sAction = '', $bAsJsonEncodedObject = false ) {
|
793 |
-
$aData = $this->getNonceActionData( $sAction );
|
794 |
-
$aData[ 'ajaxurl' ] = admin_url( 'admin-ajax.php' );
|
795 |
-
return $bAsJsonEncodedObject ? json_encode( (object)$aData ) : $aData;
|
796 |
-
}
|
797 |
-
|
798 |
-
/**
|
799 |
-
* @param string $sAction
|
800 |
-
* @return array
|
801 |
-
*/
|
802 |
-
public function getNonceActionData( $sAction = '' ) {
|
803 |
-
$aData = $this->getCon()->getNonceActionData( $sAction );
|
804 |
-
$aData[ 'mod_slug' ] = $this->getModSlug();
|
805 |
-
return $aData;
|
806 |
-
}
|
807 |
-
|
808 |
-
/**
|
809 |
-
* @return string[]
|
810 |
-
*/
|
811 |
-
public function getDismissedNotices() :array {
|
812 |
-
$notices = $this->getOptions()->getOpt( 'dismissed_notices' );
|
813 |
-
return is_array( $notices ) ? $notices : [];
|
814 |
-
}
|
815 |
-
|
816 |
-
/**
|
817 |
-
* @return string[]
|
818 |
-
*/
|
819 |
-
public function getUiTrack() :array {
|
820 |
-
$a = $this->getOptions()->getOpt( 'ui_track' );
|
821 |
-
return is_array( $a ) ? $a : [];
|
822 |
-
}
|
823 |
-
|
824 |
-
public function setDismissedNotices( array $dis ) {
|
825 |
-
$this->getOptions()->setOpt( 'dismissed_notices', $dis );
|
826 |
-
}
|
827 |
-
|
828 |
-
public function setUiTrack( array $UI ) {
|
829 |
-
$this->getOptions()->setOpt( 'ui_track', $UI );
|
830 |
-
}
|
831 |
-
|
832 |
-
/**
|
833 |
-
* @return \ICWP_WPSF_Wizard_Base|null
|
834 |
-
*/
|
835 |
-
public function getWizardHandler() {
|
836 |
-
if ( !isset( $this->oWizard ) ) {
|
837 |
-
$sClassName = $this->getWizardClassName();
|
838 |
-
if ( !class_exists( $sClassName ) ) {
|
839 |
-
return null;
|
840 |
-
}
|
841 |
-
$this->oWizard = new $sClassName();
|
842 |
-
$this->oWizard->setMod( $this );
|
843 |
-
}
|
844 |
-
return $this->oWizard;
|
845 |
-
}
|
846 |
-
|
847 |
-
/**
|
848 |
-
* @param bool $bPreProcessOptions
|
849 |
-
* @return $this
|
850 |
-
*/
|
851 |
-
public function saveModOptions( $bPreProcessOptions = false ) {
|
852 |
-
|
853 |
-
if ( $bPreProcessOptions ) {
|
854 |
-
$this->preProcessOptions();
|
855 |
-
}
|
856 |
-
|
857 |
-
$this->doPrePluginOptionsSave();
|
858 |
-
if ( apply_filters( $this->prefix( 'force_options_resave' ), false ) ) {
|
859 |
-
$this->getOptions()
|
860 |
-
->setNeedSave( true );
|
861 |
-
}
|
862 |
-
|
863 |
-
// we set the flag that options have been updated. (only use this flag if it's a MANUAL options update)
|
864 |
-
$this->bImportExportWhitelistNotify = $this->getOptions()->getNeedSave();
|
865 |
-
$this->store();
|
866 |
-
return $this;
|
867 |
-
}
|
868 |
-
|
869 |
-
protected function preProcessOptions() {
|
870 |
-
}
|
871 |
-
|
872 |
-
private function store() {
|
873 |
-
add_filter( $this->prefix( 'bypass_is_plugin_admin' ), '__return_true', 1000 );
|
874 |
-
$this->getOptions()
|
875 |
-
->doOptionsSave( $this->getCon()->getIsResetPlugin(), $this->isPremium() );
|
876 |
-
remove_filter( $this->prefix( 'bypass_is_plugin_admin' ), '__return_true', 1000 );
|
877 |
-
}
|
878 |
-
|
879 |
-
/**
|
880 |
-
* @param array $aAggregatedOptions
|
881 |
-
* @return array
|
882 |
-
*/
|
883 |
-
public function aggregateOptionsValues( $aAggregatedOptions ) {
|
884 |
-
return array_merge( $aAggregatedOptions, $this->getOptions()->getAllOptionsValues() );
|
885 |
-
}
|
886 |
-
|
887 |
-
/**
|
888 |
-
* This is the point where you would want to do any options verification
|
889 |
-
*/
|
890 |
-
protected function doPrePluginOptionsSave() {
|
891 |
-
}
|
892 |
-
|
893 |
-
public function onPluginDeactivate() {
|
894 |
-
}
|
895 |
-
|
896 |
-
public function onPluginDelete() {
|
897 |
-
foreach ( $this->getDbHandlers( true ) as $oDbh ) {
|
898 |
-
if ( !empty( $oDbh ) ) {
|
899 |
-
$oDbh->tableDelete();
|
900 |
-
}
|
901 |
-
}
|
902 |
-
$this->getOptions()->deleteStorage();
|
903 |
-
}
|
904 |
-
|
905 |
-
/**
|
906 |
-
* @return array - map of each option to its option type
|
907 |
-
*/
|
908 |
-
protected function getAllFormOptionsAndTypes() {
|
909 |
-
$aOpts = [];
|
910 |
-
|
911 |
-
foreach ( $this->getUIHandler()->buildOptions() as $aOptionsSection ) {
|
912 |
-
if ( !empty( $aOptionsSection ) ) {
|
913 |
-
foreach ( $aOptionsSection[ 'options' ] as $aOption ) {
|
914 |
-
$aOpts[ $aOption[ 'key' ] ] = $aOption[ 'type' ];
|
915 |
-
}
|
916 |
-
}
|
917 |
-
}
|
918 |
-
|
919 |
-
return $aOpts;
|
920 |
-
}
|
921 |
-
|
922 |
-
protected function handleModAction( string $sAction ) {
|
923 |
-
}
|
924 |
-
|
925 |
-
/**
|
926 |
-
* @throws \Exception
|
927 |
-
*/
|
928 |
-
public function saveOptionsSubmit() {
|
929 |
-
if ( !$this->getCon()->isPluginAdmin() ) {
|
930 |
-
throw new \Exception( __( "You don't currently have permission to save settings.", 'wp-simple-firewall' ) );
|
931 |
-
}
|
932 |
-
|
933 |
-
$this->doSaveStandardOptions();
|
934 |
-
|
935 |
-
$this->saveModOptions( true );
|
936 |
-
|
937 |
-
// only use this flag when the options are being updated with a MANUAL save.
|
938 |
-
if ( isset( $this->bImportExportWhitelistNotify ) && $this->bImportExportWhitelistNotify ) {
|
939 |
-
if ( !wp_next_scheduled( $this->prefix( 'importexport_notify' ) ) ) {
|
940 |
-
wp_schedule_single_event( Services::Request()->ts() + 15, $this->prefix( 'importexport_notify' ) );
|
941 |
-
}
|
942 |
-
}
|
943 |
-
}
|
944 |
-
|
945 |
-
/**
|
946 |
-
* @param string $sMsg
|
947 |
-
* @param bool $bError
|
948 |
-
* @param bool $bShowOnLogin
|
949 |
-
* @return $this
|
950 |
-
*/
|
951 |
-
public function setFlashAdminNotice( $sMsg, $bError = false, $bShowOnLogin = false ) {
|
952 |
-
$this->getCon()
|
953 |
-
->getAdminNotices()
|
954 |
-
->addFlash(
|
955 |
-
sprintf( '[%s] %s', $this->getCon()->getHumanName(), $sMsg ),
|
956 |
-
$bError,
|
957 |
-
$bShowOnLogin
|
958 |
-
);
|
959 |
-
return $this;
|
960 |
-
}
|
961 |
-
|
962 |
-
protected function isAdminOptionsPage() :bool {
|
963 |
-
return is_admin() && !Services::WpGeneral()->isAjax() && $this->isThisModulePage();
|
964 |
-
}
|
965 |
-
|
966 |
-
/**
|
967 |
-
* @return bool
|
968 |
-
*/
|
969 |
-
public function isPremium() {
|
970 |
-
return $this->getCon()->isPremiumActive();
|
971 |
-
}
|
972 |
-
|
973 |
-
/**
|
974 |
-
* @throws \Exception
|
975 |
-
*/
|
976 |
-
private function doSaveStandardOptions() {
|
977 |
-
$aForm = $this->getAjaxFormParams( 'b64' ); // standard options use b64 and failover to lz-string
|
978 |
-
|
979 |
-
foreach ( $this->getAllFormOptionsAndTypes() as $sKey => $sOptType ) {
|
980 |
-
|
981 |
-
$sOptionValue = isset( $aForm[ $sKey ] ) ? $aForm[ $sKey ] : null;
|
982 |
-
if ( is_null( $sOptionValue ) ) {
|
983 |
-
|
984 |
-
if ( in_array( $sOptType, [ 'text', 'email' ] ) ) { //text box, and it's null, don't update
|
985 |
-
continue;
|
986 |
-
}
|
987 |
-
elseif ( $sOptType == 'checkbox' ) { //if it was a checkbox, and it's null, it means 'N'
|
988 |
-
$sOptionValue = 'N';
|
989 |
-
}
|
990 |
-
elseif ( $sOptType == 'integer' ) { //if it was a integer, and it's null, it means '0'
|
991 |
-
$sOptionValue = 0;
|
992 |
-
}
|
993 |
-
elseif ( $sOptType == 'multiple_select' ) {
|
994 |
-
$sOptionValue = [];
|
995 |
-
}
|
996 |
-
}
|
997 |
-
else { //handle any pre-processing we need to.
|
998 |
-
|
999 |
-
if ( $sOptType == 'text' || $sOptType == 'email' ) {
|
1000 |
-
$sOptionValue = trim( $sOptionValue );
|
1001 |
-
}
|
1002 |
-
if ( $sOptType == 'integer' ) {
|
1003 |
-
$sOptionValue = intval( $sOptionValue );
|
1004 |
-
}
|
1005 |
-
elseif ( $sOptType == 'password' ) {
|
1006 |
-
$sTempValue = trim( $sOptionValue );
|
1007 |
-
if ( empty( $sTempValue ) ) {
|
1008 |
-
continue;
|
1009 |
-
}
|
1010 |
-
|
1011 |
-
$sConfirm = isset( $aForm[ $sKey.'_confirm' ] ) ? $aForm[ $sKey.'_confirm' ] : null;
|
1012 |
-
if ( $sTempValue !== $sConfirm ) {
|
1013 |
-
throw new \Exception( __( 'Password values do not match.', 'wp-simple-firewall' ) );
|
1014 |
-
}
|
1015 |
-
|
1016 |
-
$sOptionValue = md5( $sTempValue );
|
1017 |
-
}
|
1018 |
-
elseif ( $sOptType == 'array' ) { //arrays are textareas, where each is separated by newline
|
1019 |
-
$sOptionValue = array_filter( explode( "\n", esc_textarea( $sOptionValue ) ), 'trim' );
|
1020 |
-
}
|
1021 |
-
elseif ( $sOptType == 'comma_separated_lists' ) {
|
1022 |
-
$sOptionValue = Services::Data()->extractCommaSeparatedList( $sOptionValue );
|
1023 |
-
}
|
1024 |
-
/* elseif ( $sOptType == 'multiple_select' ) { } */
|
1025 |
-
}
|
1026 |
-
|
1027 |
-
// Prevent overwriting of non-editable fields
|
1028 |
-
if ( !in_array( $sOptType, [ 'noneditable_text' ] ) ) {
|
1029 |
-
$this->getOptions()->setOpt( $sKey, $sOptionValue );
|
1030 |
-
}
|
1031 |
-
}
|
1032 |
-
|
1033 |
-
// Handle Import/Export exclusions
|
1034 |
-
if ( $this->isPremium() ) {
|
1035 |
-
( new Shield\Modules\Plugin\Lib\ImportExport\Options\SaveExcludedOptions() )
|
1036 |
-
->setMod( $this )
|
1037 |
-
->save( $aForm );
|
1038 |
-
}
|
1039 |
-
}
|
1040 |
-
|
1041 |
-
protected function runWizards() {
|
1042 |
-
if ( $this->isWizardPage() && $this->hasWizard() ) {
|
1043 |
-
$oWiz = $this->getWizardHandler();
|
1044 |
-
if ( $oWiz instanceof ICWP_WPSF_Wizard_Base ) {
|
1045 |
-
$oWiz->init();
|
1046 |
-
}
|
1047 |
-
}
|
1048 |
-
}
|
1049 |
-
|
1050 |
-
/**
|
1051 |
-
* @return bool
|
1052 |
-
*/
|
1053 |
-
public function isThisModulePage() {
|
1054 |
-
return $this->getCon()->isModulePage()
|
1055 |
-
&& Services::Request()->query( 'page' ) == $this->getModSlug();
|
1056 |
-
}
|
1057 |
-
|
1058 |
-
/**
|
1059 |
-
* @return bool
|
1060 |
-
*/
|
1061 |
-
public function isPage_Insights() {
|
1062 |
-
return Services::Request()->query( 'page' ) == $this->getCon()->getModule_Insights()->getModSlug();
|
1063 |
-
}
|
1064 |
-
|
1065 |
-
/**
|
1066 |
-
* @return bool
|
1067 |
-
*/
|
1068 |
-
public function isPage_InsightsThisModule() {
|
1069 |
-
return $this->isPage_Insights()
|
1070 |
-
&& Services::Request()->query( 'subnav' ) == $this->getSlug();
|
1071 |
-
}
|
1072 |
-
|
1073 |
-
/**
|
1074 |
-
* @return bool
|
1075 |
-
*/
|
1076 |
-
protected function isModuleOptionsRequest() {
|
1077 |
-
return Services::Request()->post( 'mod_slug' ) === $this->getModSlug();
|
1078 |
-
}
|
1079 |
-
|
1080 |
-
/**
|
1081 |
-
* @return bool
|
1082 |
-
*/
|
1083 |
-
protected function isWizardPage() {
|
1084 |
-
return ( $this->getCon()->getShieldAction() == 'wizard' && $this->isThisModulePage() );
|
1085 |
-
}
|
1086 |
-
|
1087 |
-
/**
|
1088 |
-
* Will prefix and return any string with the unique plugin prefix.
|
1089 |
-
* @param string $sSuffix
|
1090 |
-
* @param string $sGlue
|
1091 |
-
* @return string
|
1092 |
-
*/
|
1093 |
-
public function prefix( $sSuffix = '', $sGlue = '-' ) {
|
1094 |
-
return $this->getCon()->prefix( $sSuffix, $sGlue );
|
1095 |
-
}
|
1096 |
-
|
1097 |
-
/**
|
1098 |
-
* @uses echo()
|
1099 |
-
*/
|
1100 |
-
public function displayModuleAdminPage() {
|
1101 |
-
echo $this->renderModulePage();
|
1102 |
-
}
|
1103 |
-
|
1104 |
-
/**
|
1105 |
-
* Override this to customize anything with the display of the page
|
1106 |
-
* @param array $aData
|
1107 |
-
* @return string
|
1108 |
-
*/
|
1109 |
-
protected function renderModulePage( array $aData = [] ) :string {
|
1110 |
-
return $this->renderTemplate(
|
1111 |
-
'index.php',
|
1112 |
-
Services::DataManipulation()->mergeArraysRecursive( $this->getUIHandler()->getBaseDisplayData(), $aData )
|
1113 |
-
);
|
1114 |
-
}
|
1115 |
-
|
1116 |
-
/**
|
1117 |
-
* @return string
|
1118 |
-
*/
|
1119 |
-
protected function getContentWizardLanding() {
|
1120 |
-
$aData = $this->getUIHandler()->getBaseDisplayData();
|
1121 |
-
if ( $this->hasWizard() ) {
|
1122 |
-
$aData[ 'content' ][ 'wizard_landing' ] = $this->getWizardHandler()->renderWizardLandingSnippet();
|
1123 |
-
}
|
1124 |
-
return $this->renderTemplate( 'snippets/module-wizard-template.php', $aData );
|
1125 |
-
}
|
1126 |
-
|
1127 |
-
protected function buildContextualHelp() {
|
1128 |
-
if ( !function_exists( 'get_current_screen' ) ) {
|
1129 |
-
require_once( ABSPATH.'wp-admin/includes/screen.php' );
|
1130 |
-
}
|
1131 |
-
$screen = get_current_screen();
|
1132 |
-
//$screen->remove_help_tabs();
|
1133 |
-
$screen->add_help_tab( [
|
1134 |
-
'id' => 'my-plugin-default',
|
1135 |
-
'title' => __( 'Default' ),
|
1136 |
-
'content' => 'This is where I would provide tabbed help to the user on how everything in my admin panel works. Formatted HTML works fine in here too'
|
1137 |
-
] );
|
1138 |
-
//add more help tabs as needed with unique id's
|
1139 |
-
|
1140 |
-
// Help sidebars are optional
|
1141 |
-
$screen->set_help_sidebar(
|
1142 |
-
'<p><strong>'.__( 'For more information:' ).'</strong></p>'.
|
1143 |
-
'<p><a href="http://wordpress.org/support/" target="_blank">'._( 'Support Forums' ).'</a></p>'
|
1144 |
-
);
|
1145 |
-
}
|
1146 |
-
|
1147 |
-
/**
|
1148 |
-
* @param string $sWizardSlug
|
1149 |
-
* @return string
|
1150 |
-
* @uses nonce
|
1151 |
-
*/
|
1152 |
-
public function getUrl_Wizard( $sWizardSlug ) {
|
1153 |
-
$aDef = $this->getWizardDefinition( $sWizardSlug );
|
1154 |
-
if ( empty( $aDef[ 'min_user_permissions' ] ) ) { // i.e. no login/minimum perms
|
1155 |
-
$sUrl = Services::WpGeneral()->getHomeUrl();
|
1156 |
-
}
|
1157 |
-
else {
|
1158 |
-
$sUrl = Services::WpGeneral()->getAdminUrl( 'admin.php' );
|
1159 |
-
}
|
1160 |
-
|
1161 |
-
return add_query_arg(
|
1162 |
-
[
|
1163 |
-
'page' => $this->getModSlug(),
|
1164 |
-
'shield_action' => 'wizard',
|
1165 |
-
'wizard' => $sWizardSlug,
|
1166 |
-
'nonwizard' => wp_create_nonce( 'wizard'.$sWizardSlug )
|
1167 |
-
],
|
1168 |
-
$sUrl
|
1169 |
-
);
|
1170 |
-
}
|
1171 |
-
|
1172 |
-
/**
|
1173 |
-
* @return string
|
1174 |
-
*/
|
1175 |
-
public function getUrl_WizardLanding() {
|
1176 |
-
return $this->getUrl_Wizard( 'landing' );
|
1177 |
-
}
|
1178 |
-
|
1179 |
-
/**
|
1180 |
-
* @param string $sWizardSlug
|
1181 |
-
* @return array
|
1182 |
-
*/
|
1183 |
-
public function getWizardDefinition( $sWizardSlug ) {
|
1184 |
-
$aDef = null;
|
1185 |
-
if ( $this->hasWizardDefinition( $sWizardSlug ) ) {
|
1186 |
-
$aW = $this->getWizardDefinitions();
|
1187 |
-
$aDef = $aW[ $sWizardSlug ];
|
1188 |
-
}
|
1189 |
-
return $aDef;
|
1190 |
-
}
|
1191 |
-
|
1192 |
-
/**
|
1193 |
-
* @return array
|
1194 |
-
*/
|
1195 |
-
public function getWizardDefinitions() {
|
1196 |
-
$aW = $this->getDef( 'wizards' );
|
1197 |
-
return is_array( $aW ) ? $aW : [];
|
1198 |
-
}
|
1199 |
-
|
1200 |
-
public function hasWizard() :bool {
|
1201 |
-
return count( $this->getWizardDefinitions() ) > 0;
|
1202 |
-
}
|
1203 |
-
|
1204 |
-
/**
|
1205 |
-
* @param string $sWizardSlug
|
1206 |
-
* @return bool
|
1207 |
-
*/
|
1208 |
-
public function hasWizardDefinition( $sWizardSlug ) {
|
1209 |
-
$aW = $this->getWizardDefinitions();
|
1210 |
-
return !empty( $aW[ $sWizardSlug ] );
|
1211 |
-
}
|
1212 |
-
|
1213 |
-
/**
|
1214 |
-
* @return bool
|
1215 |
-
*/
|
1216 |
-
public function getIsShowMarketing() {
|
1217 |
-
return apply_filters( $this->prefix( 'show_marketing' ), !$this->isPremium() );
|
1218 |
-
}
|
1219 |
-
|
1220 |
-
/**
|
1221 |
-
* @return string
|
1222 |
-
*/
|
1223 |
-
public function renderOptionsForm() {
|
1224 |
-
|
1225 |
-
if ( $this->canDisplayOptionsForm() ) {
|
1226 |
-
$sTemplate = 'components/options_form/main.twig';
|
1227 |
-
}
|
1228 |
-
else {
|
1229 |
-
$sTemplate = 'subfeature-access_restricted';
|
1230 |
-
}
|
1231 |
-
|
1232 |
-
try {
|
1233 |
-
return $this->getCon()
|
1234 |
-
->getRenderer()
|
1235 |
-
->setTemplate( $sTemplate )
|
1236 |
-
->setRenderVars( $this->getUIHandler()->getBaseDisplayData() )
|
1237 |
-
->setTemplateEngineTwig()
|
1238 |
-
->render();
|
1239 |
-
}
|
1240 |
-
catch ( \Exception $oE ) {
|
1241 |
-
return 'Error rendering options form: '.$oE->getMessage();
|
1242 |
-
}
|
1243 |
-
}
|
1244 |
-
|
1245 |
-
/**
|
1246 |
-
* @return bool
|
1247 |
-
*/
|
1248 |
-
public function canDisplayOptionsForm() {
|
1249 |
-
return $this->getOptions()->isAccessRestricted() ? $this->getCon()->isPluginAdmin() : true;
|
1250 |
-
}
|
1251 |
-
|
1252 |
-
public function onWpEnqueueAdminJs() {
|
1253 |
-
$this->insertCustomJsVars_Admin();
|
1254 |
-
}
|
1255 |
-
|
1256 |
-
/**
|
1257 |
-
* Override this with custom JS vars for your particular module.
|
1258 |
-
*/
|
1259 |
-
public function insertCustomJsVars_Admin() {
|
1260 |
-
|
1261 |
-
if ( $this->isThisModulePage() ) {
|
1262 |
-
wp_localize_script(
|
1263 |
-
$this->prefix( 'plugin' ),
|
1264 |
-
'icwp_wpsf_vars_base',
|
1265 |
-
[
|
1266 |
-
'ajax' => [
|
1267 |
-
'mod_options' => $this->getAjaxActionData( 'mod_options' ),
|
1268 |
-
'mod_opts_form_render' => $this->getAjaxActionData( 'mod_opts_form_render' ),
|
1269 |
-
]
|
1270 |
-
]
|
1271 |
-
);
|
1272 |
-
}
|
1273 |
-
}
|
1274 |
-
|
1275 |
-
/**
|
1276 |
-
* @param array $aData
|
1277 |
-
* @param string $sSubView
|
1278 |
-
*/
|
1279 |
-
protected function display( $aData = [], $sSubView = '' ) {
|
1280 |
-
}
|
1281 |
-
|
1282 |
-
/**
|
1283 |
-
* @param array $aData
|
1284 |
-
* @return string
|
1285 |
-
* @throws \Exception
|
1286 |
-
*/
|
1287 |
-
public function renderAdminNotice( $aData ) {
|
1288 |
-
if ( empty( $aData[ 'notice_attributes' ] ) ) {
|
1289 |
-
throw new \Exception( 'notice_attributes is empty' );
|
1290 |
-
}
|
1291 |
-
|
1292 |
-
if ( !isset( $aData[ 'icwp_admin_notice_template' ] ) ) {
|
1293 |
-
$aData[ 'icwp_admin_notice_template' ] = $aData[ 'notice_attributes' ][ 'notice_id' ];
|
1294 |
-
}
|
1295 |
-
|
1296 |
-
if ( !isset( $aData[ 'notice_classes' ] ) ) {
|
1297 |
-
$aData[ 'notice_classes' ] = [];
|
1298 |
-
}
|
1299 |
-
if ( is_array( $aData[ 'notice_classes' ] ) ) {
|
1300 |
-
$aData[ 'notice_classes' ][] = $aData[ 'notice_attributes' ][ 'type' ];
|
1301 |
-
if ( empty( $aData[ 'notice_classes' ] )
|
1302 |
-
|| ( !in_array( 'error', $aData[ 'notice_classes' ] ) && !in_array( 'updated', $aData[ 'notice_classes' ] ) ) ) {
|
1303 |
-
$aData[ 'notice_classes' ][] = 'updated';
|
1304 |
-
}
|
1305 |
-
}
|
1306 |
-
$aData[ 'notice_classes' ] = implode( ' ', $aData[ 'notice_classes' ] );
|
1307 |
-
|
1308 |
-
$aAjaxData = $this->getAjaxActionData( 'dismiss_admin_notice' );
|
1309 |
-
$aAjaxData[ 'hide' ] = 1;
|
1310 |
-
$aAjaxData[ 'notice_id' ] = $aData[ 'notice_attributes' ][ 'notice_id' ];
|
1311 |
-
$aData[ 'ajax' ][ 'dismiss_admin_notice' ] = json_encode( $aAjaxData );
|
1312 |
-
|
1313 |
-
$bTwig = $aData[ 'notice_attributes' ][ 'twig' ];
|
1314 |
-
$sTemplate = $bTwig ? '/notices/'.$aAjaxData[ 'notice_id' ] : 'notices/admin-notice-template';
|
1315 |
-
return $this->renderTemplate( $sTemplate, $aData, $bTwig );
|
1316 |
-
}
|
1317 |
-
|
1318 |
-
public function renderTemplate( string $template, array $data = [], bool $isTwig = false ) :string {
|
1319 |
-
if ( empty( $data[ 'unique_render_id' ] ) ) {
|
1320 |
-
$data[ 'unique_render_id' ] = 'noticeid-'.substr( md5( mt_rand() ), 0, 5 );
|
1321 |
-
}
|
1322 |
-
try {
|
1323 |
-
$oRndr = $this->getCon()->getRenderer();
|
1324 |
-
if ( $isTwig || preg_match( '#^.*\.twig$#i', $template ) ) {
|
1325 |
-
$oRndr->setTemplateEngineTwig();
|
1326 |
-
}
|
1327 |
-
|
1328 |
-
$data[ 'strings' ] = Services::DataManipulation()
|
1329 |
-
->mergeArraysRecursive(
|
1330 |
-
$this->getStrings()->getDisplayStrings(),
|
1331 |
-
$data[ 'strings' ] ?? []
|
1332 |
-
);
|
1333 |
-
|
1334 |
-
$render = $oRndr->setTemplate( $template )
|
1335 |
-
->setRenderVars( $data )
|
1336 |
-
->render();
|
1337 |
-
}
|
1338 |
-
catch ( \Exception $oE ) {
|
1339 |
-
$render = $oE->getMessage();
|
1340 |
-
error_log( $oE->getMessage() );
|
1341 |
-
}
|
1342 |
-
|
1343 |
-
return (string)$render;
|
1344 |
-
}
|
1345 |
-
|
1346 |
-
/**
|
1347 |
-
* @param array $aTransferableOptions
|
1348 |
-
* @return array
|
1349 |
-
*/
|
1350 |
-
public function exportTransferableOptions( $aTransferableOptions ) {
|
1351 |
-
if ( !is_array( $aTransferableOptions ) ) {
|
1352 |
-
$aTransferableOptions = [];
|
1353 |
-
}
|
1354 |
-
$aTransferableOptions[ $this->getOptionsStorageKey() ] = $this->getOptions()->getTransferableOptions();
|
1355 |
-
return $aTransferableOptions;
|
1356 |
-
}
|
1357 |
-
|
1358 |
-
public function collectOptionsForTracking() :array {
|
1359 |
-
$opts = $this->getOptions();
|
1360 |
-
$aOptionsData = $this->getOptions()->getOptionsForTracking();
|
1361 |
-
foreach ( $aOptionsData as $opt => $mValue ) {
|
1362 |
-
unset( $aOptionsData[ $opt ] );
|
1363 |
-
// some cleaning to ensure we don't have disallowed characters
|
1364 |
-
$opt = preg_replace( '#[^_a-z]#', '', strtolower( $opt ) );
|
1365 |
-
$sType = $opts->getOptionType( $opt );
|
1366 |
-
if ( $sType == 'checkbox' ) { // only want a boolean 1 or 0
|
1367 |
-
$aOptionsData[ $opt ] = (int)( $mValue == 'Y' );
|
1368 |
-
}
|
1369 |
-
else {
|
1370 |
-
$aOptionsData[ $opt ] = $mValue;
|
1371 |
-
}
|
1372 |
-
}
|
1373 |
-
return $aOptionsData;
|
1374 |
-
}
|
1375 |
-
|
1376 |
-
public function getMainWpData() :array {
|
1377 |
-
return [
|
1378 |
-
'options' => $this->getOptions()->getTransferableOptions()
|
1379 |
-
];
|
1380 |
-
}
|
1381 |
-
|
1382 |
-
/**
|
1383 |
-
* See plugin controller for the nature of $aData wpPrivacyExport()
|
1384 |
-
* @param array $aExportItems
|
1385 |
-
* @param string $sEmail
|
1386 |
-
* @param int $nPage
|
1387 |
-
* @return array
|
1388 |
-
*/
|
1389 |
-
public function onWpPrivacyExport( $aExportItems, $sEmail, $nPage = 1 ) {
|
1390 |
-
return $aExportItems;
|
1391 |
-
}
|
1392 |
-
|
1393 |
-
/**
|
1394 |
-
* See plugin controller for the nature of $aData wpPrivacyErase()
|
1395 |
-
* @param array $aData
|
1396 |
-
* @param string $sEmail
|
1397 |
-
* @param int $nPage
|
1398 |
-
* @return array
|
1399 |
-
*/
|
1400 |
-
public function onWpPrivacyErase( $aData, $sEmail, $nPage = 1 ) {
|
1401 |
-
return $aData;
|
1402 |
-
}
|
1403 |
-
|
1404 |
-
/**
|
1405 |
-
* @return null|Shield\Modules\Base\ShieldOptions|mixed
|
1406 |
-
*/
|
1407 |
-
public function getOptions() {
|
1408 |
-
if ( !isset( $this->oOpts ) ) {
|
1409 |
-
$oCon = $this->getCon();
|
1410 |
-
$this->oOpts = $this->loadModElement( 'Options' );
|
1411 |
-
$this->oOpts->setPathToConfig( $oCon->getPath_ConfigFile( $this->getSlug() ) )
|
1412 |
-
->setRebuildFromFile( $oCon->getIsRebuildOptionsFromFile() )
|
1413 |
-
->setOptionsStorageKey( $this->getOptionsStorageKey() )
|
1414 |
-
->setIfLoadOptionsFromStorage( !$oCon->getIsResetPlugin() );
|
1415 |
-
}
|
1416 |
-
return $this->oOpts;
|
1417 |
-
}
|
1418 |
-
|
1419 |
-
/**
|
1420 |
-
* @return Shield\Modules\Base\WpCli
|
1421 |
-
* @throws \Exception
|
1422 |
-
*/
|
1423 |
-
public function getWpCli() {
|
1424 |
-
if ( !isset( $this->oWpCli ) ) {
|
1425 |
-
$this->oWpCli = $this->loadModElement( 'WpCli' );
|
1426 |
-
if ( !$this->oWpCli instanceof Shield\Modules\Base\WpCli ) {
|
1427 |
-
throw new \Exception( 'WP-CLI not supported' );
|
1428 |
-
}
|
1429 |
-
}
|
1430 |
-
return $this->oWpCli;
|
1431 |
-
}
|
1432 |
-
|
1433 |
-
/**
|
1434 |
-
* @return null|Shield\Modules\Base\Strings
|
1435 |
-
*/
|
1436 |
-
public function getStrings() {
|
1437 |
-
return $this->loadStrings()->setMod( $this );
|
1438 |
-
}
|
1439 |
-
|
1440 |
-
/**
|
1441 |
-
* @return Shield\Modules\Base\UI
|
1442 |
-
*/
|
1443 |
-
public function getUIHandler() {
|
1444 |
-
if ( !isset( $this->oUI ) ) {
|
1445 |
-
$this->oUI = $this->loadModElement( 'UI' );
|
1446 |
-
if ( !$this->oUI instanceof Shield\Modules\Base\UI ) {
|
1447 |
-
// TODO: autoloader for base classes
|
1448 |
-
$this->oUI = $this->loadModElement( 'ShieldUI' );
|
1449 |
-
}
|
1450 |
-
}
|
1451 |
-
return $this->oUI;
|
1452 |
-
}
|
1453 |
-
|
1454 |
-
/**
|
1455 |
-
* @return Shield\Modules\Base\BaseReporting|mixed|false
|
1456 |
-
*/
|
1457 |
-
public function getReportingHandler() {
|
1458 |
-
if ( !isset( $this->oReporting ) ) {
|
1459 |
-
$this->oReporting = $this->loadModElement( 'Reporting' );
|
1460 |
-
}
|
1461 |
-
return $this->oReporting;
|
1462 |
-
}
|
1463 |
-
|
1464 |
-
protected function loadAdminNotices() {
|
1465 |
-
$N = $this->loadModElement( 'AdminNotices' );
|
1466 |
-
if ( $N instanceof Shield\Modules\Base\AdminNotices ) {
|
1467 |
-
$N->run();
|
1468 |
-
}
|
1469 |
-
}
|
1470 |
-
|
1471 |
-
protected function loadAjaxHandler() {
|
1472 |
-
$oAj = $this->loadModElement( 'AjaxHandler' );
|
1473 |
-
if ( !$oAj instanceof Shield\Modules\Base\AjaxHandlerBase ) {
|
1474 |
-
$this->loadModElement( 'AjaxHandlerShield' );
|
1475 |
-
}
|
1476 |
-
}
|
1477 |
-
|
1478 |
-
/**
|
1479 |
-
* @return Shield\Modules\Base\ShieldOptions|mixed
|
1480 |
-
* @deprecated 10.1
|
1481 |
-
*/
|
1482 |
-
protected function loadOptions() {
|
1483 |
-
return $this->loadModElement( 'Options' );
|
1484 |
-
}
|
1485 |
-
|
1486 |
-
protected function loadDebug() {
|
1487 |
-
$req = Services::Request();
|
1488 |
-
if ( $req->query( 'debug' ) && $req->query( 'mod' ) == $this->getModSlug()
|
1489 |
-
&& $this->getCon()->isPluginAdmin() ) {
|
1490 |
-
/** @var Shield\Modules\Base\Debug $debug */
|
1491 |
-
$debug = $this->loadModElement( 'Debug', true );
|
1492 |
-
$debug->run();
|
1493 |
-
}
|
1494 |
-
}
|
1495 |
-
|
1496 |
-
/**
|
1497 |
-
* @return Shield\Modules\Base\Strings|mixed
|
1498 |
-
*/
|
1499 |
-
protected function loadStrings() {
|
1500 |
-
return $this->loadModElement( 'Strings', true );
|
1501 |
-
}
|
1502 |
-
|
1503 |
-
/**
|
1504 |
-
* @param $sClass
|
1505 |
-
* @return \stdClass|mixed|false
|
1506 |
-
* @deprecated 10.1
|
1507 |
-
*/
|
1508 |
-
private function loadClass( $sClass ) {
|
1509 |
-
$sC = $this->getNamespace().$sClass;
|
1510 |
-
return @class_exists( $sC ) ? new $sC() : false;
|
1511 |
-
}
|
1512 |
-
|
1513 |
-
/**
|
1514 |
-
* @param string $class
|
1515 |
-
* @param false $injectMod
|
1516 |
-
* @return false|Shield\Modules\ModConsumer
|
1517 |
-
*/
|
1518 |
-
private function loadModElement( string $class, $injectMod = true ) {
|
1519 |
-
$element = false;
|
1520 |
-
try {
|
1521 |
-
$C = $this->findElementClass( $class, true );
|
1522 |
-
/** @var Shield\Modules\ModConsumer $element */
|
1523 |
-
$element = @class_exists( $C ) ? new $C() : false;
|
1524 |
-
if ( $injectMod && method_exists( $element, 'setMod' ) ) {
|
1525 |
-
$element->setMod( $this );
|
1526 |
-
}
|
1527 |
-
}
|
1528 |
-
catch ( \Exception $e ) {
|
1529 |
-
}
|
1530 |
-
return $element;
|
1531 |
-
}
|
1532 |
-
|
1533 |
-
/**
|
1534 |
-
* @param $sClass
|
1535 |
-
* @return \stdClass|mixed|false
|
1536 |
-
* @deprecated 10.1
|
1537 |
-
*/
|
1538 |
-
private function loadClassFromBase( $sClass ) {
|
1539 |
-
$sC = $this->getBaseNamespace().$sClass;
|
1540 |
-
return @class_exists( $sC ) ? new $sC() : false;
|
1541 |
-
}
|
1542 |
-
|
1543 |
-
/**
|
1544 |
-
* @param string $element
|
1545 |
-
* @param bool $bThrowException
|
1546 |
-
* @return string|null
|
1547 |
-
* @throws \Exception
|
1548 |
-
*/
|
1549 |
-
protected function findElementClass( string $element, $bThrowException = true ) {
|
1550 |
-
$theClass = null;
|
1551 |
-
|
1552 |
-
foreach ( $this->getNamespaceRoots() as $NS ) {
|
1553 |
-
$maybe = $NS.$element;
|
1554 |
-
if ( @class_exists( $maybe ) ) {
|
1555 |
-
if ( ( new ReflectionClass( $maybe ) )->isInstantiable() ) {
|
1556 |
-
$theClass = $maybe;
|
1557 |
-
break;
|
1558 |
-
}
|
1559 |
-
}
|
1560 |
-
}
|
1561 |
-
|
1562 |
-
if ( $bThrowException && is_null( $theClass ) ) {
|
1563 |
-
throw new \Exception( sprintf( 'Could not find class for element "%s".', $element ) );
|
1564 |
-
}
|
1565 |
-
return $theClass;
|
1566 |
-
}
|
1567 |
-
|
1568 |
-
private function getBaseNamespace() {
|
1569 |
-
return $this->getModulesNamespace().'Base\\';
|
1570 |
-
}
|
1571 |
-
|
1572 |
-
protected function getModulesNamespace() :string {
|
1573 |
-
return '\FernleafSystems\Wordpress\Plugin\Shield\Modules\\';
|
1574 |
-
}
|
1575 |
-
|
1576 |
-
protected function getNamespace() :string {
|
1577 |
-
return $this->getModulesNamespace().$this->getNamespaceBase().'\\';
|
1578 |
-
}
|
1579 |
-
|
1580 |
-
protected function getNamespaceRoots() :array {
|
1581 |
-
return [
|
1582 |
-
$this->getNamespace(),
|
1583 |
-
$this->getBaseNamespace()
|
1584 |
-
];
|
1585 |
-
}
|
1586 |
-
|
1587 |
-
protected function getNamespaceBase() :string {
|
1588 |
-
return 'Base';
|
1589 |
-
}
|
1590 |
-
|
1591 |
-
/**
|
1592 |
-
* Saves the options to the WordPress Options store.
|
1593 |
-
* @return void
|
1594 |
-
* @deprecated 8.4
|
1595 |
-
*/
|
1596 |
-
public function savePluginOptions() {
|
1597 |
-
$this->saveModOptions();
|
1598 |
-
}
|
1599 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
src/features/base_wpsf.php
DELETED
@@ -1,262 +0,0 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
use FernleafSystems\Wordpress\Plugin\Shield;
|
4 |
-
use FernleafSystems\Wordpress\Plugin\Shield\Modules\Plugin;
|
5 |
-
use FernleafSystems\Wordpress\Services\Services;
|
6 |
-
use FernleafSystems\Wordpress\Services\Utilities;
|
7 |
-
use FernleafSystems\Wordpress\Services\Utilities\Net\IpIdentify;
|
8 |
-
|
9 |
-
class ICWP_WPSF_FeatureHandler_BaseWpsf extends ICWP_WPSF_FeatureHandler_Base {
|
10 |
-
|
11 |
-
/**
|
12 |
-
* @var bool
|
13 |
-
*/
|
14 |
-
protected static $bIsVerifiedBot;
|
15 |
-
|
16 |
-
/**
|
17 |
-
* @var bool
|
18 |
-
*/
|
19 |
-
private static $bVisitorIsWhitelisted;
|
20 |
-
|
21 |
-
/**
|
22 |
-
* @return bool
|
23 |
-
*/
|
24 |
-
public function canCacheDirWrite() {
|
25 |
-
return ( new Shield\Modules\Plugin\Lib\TestCacheDirWrite() )
|
26 |
-
->setMod( $this->getCon()->getModule_Plugin() )
|
27 |
-
->canWrite();
|
28 |
-
}
|
29 |
-
|
30 |
-
/**
|
31 |
-
* @return \ICWP_WPSF_Processor_Sessions
|
32 |
-
*/
|
33 |
-
public function getSessionsProcessor() {
|
34 |
-
return $this->getCon()
|
35 |
-
->getModule_Sessions()
|
36 |
-
->getProcessor();
|
37 |
-
}
|
38 |
-
|
39 |
-
/**
|
40 |
-
* @return Shield\Databases\Session\Handler
|
41 |
-
*/
|
42 |
-
public function getDbHandler_Sessions() {
|
43 |
-
return $this->getCon()
|
44 |
-
->getModule_Sessions()
|
45 |
-
->getDbHandler_Sessions();
|
46 |
-
}
|
47 |
-
|
48 |
-
/**
|
49 |
-
* @return Shield\Databases\Session\EntryVO|null
|
50 |
-
*/
|
51 |
-
public function getSession() {
|
52 |
-
$oP = $this->getSessionsProcessor();
|
53 |
-
return is_null( $oP ) ? null : $oP->getCurrentSession();
|
54 |
-
}
|
55 |
-
|
56 |
-
/**
|
57 |
-
* @return bool
|
58 |
-
*/
|
59 |
-
public function hasSession() {
|
60 |
-
return $this->getSession() instanceof Shield\Databases\Session\EntryVO;
|
61 |
-
}
|
62 |
-
|
63 |
-
/**
|
64 |
-
* @return bool
|
65 |
-
*/
|
66 |
-
public function hasValidRequestIP() {
|
67 |
-
return Services::IP()->isValidIp( Services::IP()->getRequestIp() );
|
68 |
-
}
|
69 |
-
|
70 |
-
public function onWpInit() {
|
71 |
-
parent::onWpInit();
|
72 |
-
if ( $this->isThisModulePage() && !$this->isWizardPage() && ( $this->getSlug() != 'insights' ) ) {
|
73 |
-
$this->redirectToInsightsSubPage();
|
74 |
-
}
|
75 |
-
}
|
76 |
-
|
77 |
-
protected function redirectToInsightsSubPage() {
|
78 |
-
Services::Response()->redirect(
|
79 |
-
$this->getCon()->getModule_Insights()->getUrl_AdminPage(),
|
80 |
-
[
|
81 |
-
'inav' => 'settings',
|
82 |
-
'subnav' => $this->getSlug()
|
83 |
-
],
|
84 |
-
true, false
|
85 |
-
);
|
86 |
-
}
|
87 |
-
|
88 |
-
/**
|
89 |
-
* @return Plugin\Lib\Captcha\CaptchaConfigVO
|
90 |
-
*/
|
91 |
-
public function getCaptchaCfg() {
|
92 |
-
$oPlugMod = $this->getCon()->getModule_Plugin();
|
93 |
-
/** @var Shield\Modules\Plugin\Options $oOpts */
|
94 |
-
$oOpts = $oPlugMod->getOptions();
|
95 |
-
$oCfg = ( new Plugin\Lib\Captcha\CaptchaConfigVO() )->applyFromArray( $oOpts->getCaptchaConfig() );
|
96 |
-
$oCfg->invisible = $oCfg->theme === 'invisible';
|
97 |
-
|
98 |
-
if ( $oCfg->provider === Plugin\Lib\Captcha\CaptchaConfigVO::PROV_GOOGLE_RECAP2 ) {
|
99 |
-
$oCfg->url_api = 'https://www.google.com/recaptcha/api.js';
|
100 |
-
}
|
101 |
-
elseif ( $oCfg->provider === Plugin\Lib\Captcha\CaptchaConfigVO::PROV_HCAPTCHA ) {
|
102 |
-
$oCfg->url_api = 'https://hcaptcha.com/1/api.js';
|
103 |
-
}
|
104 |
-
else {
|
105 |
-
error_log( 'CAPTCHA Provider not supported: '.$oCfg->provider );
|
106 |
-
}
|
107 |
-
|
108 |
-
$oCfg->js_handle = $this->getCon()->prefix( $oCfg->provider );
|
109 |
-
|
110 |
-
return $oCfg;
|
111 |
-
}
|
112 |
-
|
113 |
-
/**
|
114 |
-
* @return array
|
115 |
-
*/
|
116 |
-
public function getSecAdminLoginAjaxData() {
|
117 |
-
// We set a custom mod_slug so that this module handles the ajax request
|
118 |
-
$aAjaxData = $this->getAjaxActionData( 'sec_admin_login' );
|
119 |
-
$aAjaxData[ 'mod_slug' ] = $this->prefix( 'admin_access_restriction' );
|
120 |
-
return $aAjaxData;
|
121 |
-
}
|
122 |
-
|
123 |
-
/**
|
124 |
-
* @return array
|
125 |
-
*/
|
126 |
-
protected function getSecAdminCheckAjaxData() {
|
127 |
-
// We set a custom mod_slug so that this module handles the ajax request
|
128 |
-
$aAjaxData = $this->getAjaxActionData( 'sec_admin_check' );
|
129 |
-
$aAjaxData[ 'mod_slug' ] = $this->prefix( 'admin_access_restriction' );
|
130 |
-
return $aAjaxData;
|
131 |
-
}
|
132 |
-
|
133 |
-
public function getPluginReportEmail() :string {
|
134 |
-
return $this->getCon()
|
135 |
-
->getModule_Plugin()
|
136 |
-
->getPluginReportEmail();
|
137 |
-
}
|
138 |
-
|
139 |
-
/**
|
140 |
-
* @uses echo()
|
141 |
-
*/
|
142 |
-
public function displayModuleAdminPage() {
|
143 |
-
if ( $this->canDisplayOptionsForm() ) {
|
144 |
-
parent::displayModuleAdminPage();
|
145 |
-
}
|
146 |
-
else {
|
147 |
-
echo $this->renderRestrictedPage();
|
148 |
-
}
|
149 |
-
}
|
150 |
-
|
151 |
-
/**
|
152 |
-
* @return string
|
153 |
-
*/
|
154 |
-
protected function renderRestrictedPage() {
|
155 |
-
/** @var Shield\Modules\SecurityAdmin\Options $oSecOpts */
|
156 |
-
$oSecOpts = $this->getCon()
|
157 |
-
->getModule_SecAdmin()
|
158 |
-
->getOptions();
|
159 |
-
$aData = Services::DataManipulation()
|
160 |
-
->mergeArraysRecursive(
|
161 |
-
$this->getUIHandler()->getBaseDisplayData(),
|
162 |
-
[
|
163 |
-
'ajax' => [
|
164 |
-
'restricted_access' => $this->getAjaxActionData( 'restricted_access' ),
|
165 |
-
],
|
166 |
-
'strings' => [
|
167 |
-
'force_remove_email' => __( "If you've forgotten your PIN, a link can be sent to the plugin administrator email address to remove this restriction.", 'wp-simple-firewall' ),
|
168 |
-
'click_email' => __( "Click here to send the verification email.", 'wp-simple-firewall' ),
|
169 |
-
'send_to_email' => sprintf( __( "Email will be sent to %s", 'wp-simple-firewall' ),
|
170 |
-
Utilities\Obfuscate::Email( $this->getPluginReportEmail() ) ),
|
171 |
-
'no_email_override' => __( "The Security Administrator has restricted the use of the email override feature.", 'wp-simple-firewall' ),
|
172 |
-
],
|
173 |
-
'flags' => [
|
174 |
-
'allow_email_override' => $oSecOpts->isEmailOverridePermitted()
|
175 |
-
]
|
176 |
-
]
|
177 |
-
);
|
178 |
-
return $this->renderTemplate( '/wpadmin_pages/security_admin/index.twig', $aData, true );
|
179 |
-
}
|
180 |
-
|
181 |
-
/**
|
182 |
-
* @return bool
|
183 |
-
*/
|
184 |
-
public function getIfSupport3rdParty() {
|
185 |
-
return $this->isPremium();
|
186 |
-
}
|
187 |
-
|
188 |
-
/**
|
189 |
-
* @return bool
|
190 |
-
* @throws \Exception
|
191 |
-
*/
|
192 |
-
protected function isReadyToExecute() {
|
193 |
-
$opts = $this->getOptions();
|
194 |
-
return ( $opts->isModuleRunIfWhitelisted() || !$this->isVisitorWhitelisted() )
|
195 |
-
&& ( $opts->isModuleRunIfVerifiedBot() || !$this->isVerifiedBot() )
|
196 |
-
&& ( $opts->isModuleRunUnderWpCli() || !Services::WpGeneral()->isWpCli() )
|
197 |
-
&& parent::isReadyToExecute();
|
198 |
-
}
|
199 |
-
|
200 |
-
public function isVisitorWhitelisted() :bool {
|
201 |
-
if ( !isset( self::$bVisitorIsWhitelisted ) ) {
|
202 |
-
self::$bVisitorIsWhitelisted =
|
203 |
-
( new Shield\Modules\IPs\Lib\Ops\LookupIpOnList() )
|
204 |
-
->setDbHandler( $this->getCon()->getModule_IPs()->getDbHandler_IPs() )
|
205 |
-
->setIP( Services::IP()->getRequestIp() )
|
206 |
-
->setListTypeWhite()
|
207 |
-
->lookup()
|
208 |
-
instanceof Shield\Databases\IPs\EntryVO;
|
209 |
-
}
|
210 |
-
return self::$bVisitorIsWhitelisted;
|
211 |
-
}
|
212 |
-
|
213 |
-
public function isVerifiedBot() :bool {
|
214 |
-
if ( !isset( self::$bIsVerifiedBot ) ) {
|
215 |
-
$srvIP = Services::IP();
|
216 |
-
self::$bIsVerifiedBot = !$srvIP->isLoopback() &&
|
217 |
-
!in_array( $srvIP->getIpDetector()->getIPIdentity(), [
|
218 |
-
IpIdentify::UNKNOWN,
|
219 |
-
IpIdentify::THIS_SERVER,
|
220 |
-
IpIdentify::VISITOR,
|
221 |
-
] );
|
222 |
-
}
|
223 |
-
return self::$bIsVerifiedBot;
|
224 |
-
}
|
225 |
-
|
226 |
-
public function isXmlrpcBypass() :bool {
|
227 |
-
return $this->getCon()
|
228 |
-
->getModule_Plugin()
|
229 |
-
->isXmlrpcBypass();
|
230 |
-
}
|
231 |
-
|
232 |
-
/**
|
233 |
-
* @param string[] $aArray
|
234 |
-
* @param string $sPregReplacePattern
|
235 |
-
* @return string[]
|
236 |
-
*/
|
237 |
-
protected function cleanStringArray( $aArray, $sPregReplacePattern ) {
|
238 |
-
$aCleaned = [];
|
239 |
-
if ( !is_array( $aArray ) ) {
|
240 |
-
return $aCleaned;
|
241 |
-
}
|
242 |
-
|
243 |
-
foreach ( $aArray as $nKey => $sVal ) {
|
244 |
-
$sVal = preg_replace( $sPregReplacePattern, '', $sVal );
|
245 |
-
if ( !empty( $sVal ) ) {
|
246 |
-
$aCleaned[] = $sVal;
|
247 |
-
}
|
248 |
-
}
|
249 |
-
return array_unique( array_filter( $aCleaned ) );
|
250 |
-
}
|
251 |
-
|
252 |
-
protected function getNamespaceRoots() :array {
|
253 |
-
// Ensure order of namespaces is 'Module', 'Shield', then 'Base'
|
254 |
-
return array_unique( array_merge(
|
255 |
-
[
|
256 |
-
$this->getNamespace(),
|
257 |
-
$this->getModulesNamespace().'BaseShield\\',
|
258 |
-
],
|
259 |
-
parent::getNamespaceRoots()
|
260 |
-
) );
|
261 |
-
}
|
262 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
src/features/comments_filter.php
DELETED
@@ -1,112 +0,0 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
use FernleafSystems\Wordpress\Plugin\Shield;
|
4 |
-
use FernleafSystems\Wordpress\Plugin\Shield\Modules\CommentsFilter;
|
5 |
-
|
6 |
-
/**
|
7 |
-
* @deprecated 10.1
|
8 |
-
*/
|
9 |
-
class ICWP_WPSF_FeatureHandler_CommentsFilter extends ICWP_WPSF_FeatureHandler_BaseWpsf {
|
10 |
-
|
11 |
-
/**
|
12 |
-
* @return Shield\Modules\Plugin\Lib\Captcha\CaptchaConfigVO
|
13 |
-
*/
|
14 |
-
public function getCaptchaCfg() {
|
15 |
-
$oCfg = parent::getCaptchaCfg();
|
16 |
-
$sStyle = $this->getOptions()->getOpt( 'google_recaptcha_style_comments' );
|
17 |
-
if ( $sStyle !== 'default' && $this->isPremium() ) {
|
18 |
-
$oCfg->theme = $sStyle;
|
19 |
-
$oCfg->invisible = $oCfg->theme == 'invisible';
|
20 |
-
}
|
21 |
-
return $oCfg;
|
22 |
-
}
|
23 |
-
|
24 |
-
public function ensureCorrectCaptchaConfig() {
|
25 |
-
/** @var CommentsFilter\Options $opts */
|
26 |
-
$opts = $this->getOptions();
|
27 |
-
|
28 |
-
$sStyle = $opts->getOpt( 'google_recaptcha_style_comments' );
|
29 |
-
if ( $this->isPremium() ) {
|
30 |
-
$oCfg = $this->getCaptchaCfg();
|
31 |
-
if ( $oCfg->provider == $oCfg::PROV_GOOGLE_RECAP2 ) {
|
32 |
-
if ( !$oCfg->invisible && $sStyle == 'invisible' ) {
|
33 |
-
$opts->setOpt( 'google_recaptcha_style_comments', 'default' );
|
34 |
-
}
|
35 |
-
}
|
36 |
-
}
|
37 |
-
elseif ( !in_array( $sStyle, [ 'disabled', 'default' ] ) ) {
|
38 |
-
$opts->setOpt( 'google_recaptcha_style_comments', 'default' );
|
39 |
-
}
|
40 |
-
}
|
41 |
-
|
42 |
-
/**
|
43 |
-
* @param string $sOptKey
|
44 |
-
* @return string
|
45 |
-
*/
|
46 |
-
public function getTextOptDefault( $sOptKey ) {
|
47 |
-
|
48 |
-
switch ( $sOptKey ) {
|
49 |
-
case 'custom_message_checkbox':
|
50 |
-
$sText = __( "I'm not a spammer.", 'wp-simple-firewall' );
|
51 |
-
break;
|
52 |
-
case 'custom_message_alert':
|
53 |
-
$sText = __( "Please check the box to confirm you're not a spammer.", 'wp-simple-firewall' );
|
54 |
-
break;
|
55 |
-
case 'custom_message_comment_wait':
|
56 |
-
$sText = __( "Please wait %s seconds before posting your comment.", 'wp-simple-firewall' );
|
57 |
-
break;
|
58 |
-
case 'custom_message_comment_reload':
|
59 |
-
$sText = __( "Please reload this page to post a comment.", 'wp-simple-firewall' );
|
60 |
-
break;
|
61 |
-
default:
|
62 |
-
$sText = parent::getTextOptDefault( $sOptKey );
|
63 |
-
break;
|
64 |
-
}
|
65 |
-
return $sText;
|
66 |
-
}
|
67 |
-
|
68 |
-
protected function preProcessOptions() {
|
69 |
-
/** @var CommentsFilter\Options $opts */
|
70 |
-
$opts = $this->getOptions();
|
71 |
-
|
72 |
-
// clean roles
|
73 |
-
$opts->setOpt( 'trusted_user_roles',
|
74 |
-
array_unique( array_filter( array_map(
|
75 |
-
function ( $sRole ) {
|
76 |
-
return sanitize_key( strtolower( $sRole ) );
|
77 |
-
},
|
78 |
-
$opts->getTrustedRoles()
|
79 |
-
) ) )
|
80 |
-
);
|
81 |
-
|
82 |
-
$this->ensureCorrectCaptchaConfig();
|
83 |
-
}
|
84 |
-
|
85 |
-
/**
|
86 |
-
* @return bool
|
87 |
-
*/
|
88 |
-
public function isEnabledCaptcha() {
|
89 |
-
/** @var CommentsFilter\Options $opts */
|
90 |
-
$opts = $this->getOptions();
|
91 |
-
return $this->isModOptEnabled() && !$opts->isEnabledCaptcha()
|
92 |
-
&& $this->getCaptchaCfg()->ready;
|
93 |
-
}
|
94 |
-
|
95 |
-
public function setEnabledGasp( bool $enabled = true ) {
|
96 |
-
$this->getOptions()->setOpt( 'enable_comments_gasp_protection', $enabled ? 'Y' : 'N' );
|
97 |
-
}
|
98 |
-
|
99 |
-
/**
|
100 |
-
* @return string
|
101 |
-
*/
|
102 |
-
protected function getNamespaceBase() :string {
|
103 |
-
return 'CommentsFilter';
|
104 |
-
}
|
105 |
-
|
106 |
-
/**
|
107 |
-
* @return string
|
108 |
-
*/
|
109 |
-
public function getSpamBlacklistFile() {
|
110 |
-
return $this->getCon()->getPluginCachePath( 'spamblacklist.txt' );
|
111 |
-
}
|
112 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
src/features/comms.php
DELETED
@@ -1,19 +0,0 @@
|
|
1 |
-
<?php declare( strict_types=1 );
|
2 |
-
|
3 |
-
use FernleafSystems\Wordpress\Plugin\Shield\Modules\Comms;
|
4 |
-
|
5 |
-
/**
|
6 |
-
* Class ICWP_WPSF_FeatureHandler_Comms
|
7 |
-
* @deprecated 10.1
|
8 |
-
*/
|
9 |
-
class ICWP_WPSF_FeatureHandler_Comms extends ICWP_WPSF_FeatureHandler_BaseWpsf {
|
10 |
-
|
11 |
-
public function getSureSendController() :Comms\Lib\SureSend\SureSendController {
|
12 |
-
return ( new Comms\Lib\SureSend\SureSendController() )
|
13 |
-
->setMod( $this );
|
14 |
-
}
|
15 |
-
|
16 |
-
protected function getNamespaceBase() :string {
|
17 |
-
return 'Comms';
|
18 |
-
}
|
19 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
src/features/email.php
DELETED
@@ -1,11 +0,0 @@
|
|
1 |
-
<?php declare( strict_types=1 );
|
2 |
-
|
3 |
-
/**
|
4 |
-
* @deprecated 10.1
|
5 |
-
*/
|
6 |
-
class ICWP_WPSF_FeatureHandler_Email extends ICWP_WPSF_FeatureHandler_BaseWpsf {
|
7 |
-
|
8 |
-
protected function getNamespaceBase() :string {
|
9 |
-
return 'Email';
|
10 |
-
}
|
11 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
src/features/events.php
DELETED
@@ -1,34 +0,0 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
use FernleafSystems\Wordpress\Plugin\Shield;
|
4 |
-
|
5 |
-
/**
|
6 |
-
* Class ICWP_WPSF_FeatureHandler_Events
|
7 |
-
* @deprecated 10.1
|
8 |
-
*/
|
9 |
-
class ICWP_WPSF_FeatureHandler_Events extends ICWP_WPSF_FeatureHandler_BaseWpsf {
|
10 |
-
|
11 |
-
/**
|
12 |
-
* @return false|Shield\Databases\Events\Handler
|
13 |
-
*/
|
14 |
-
public function getDbHandler_Events() {
|
15 |
-
return $this->getDbH( 'events' );
|
16 |
-
}
|
17 |
-
|
18 |
-
/**
|
19 |
-
* @return bool
|
20 |
-
* @throws \Exception
|
21 |
-
*/
|
22 |
-
protected function isReadyToExecute() {
|
23 |
-
return ( $this->getDbHandler_Events() instanceof Shield\Databases\Events\Handler )
|
24 |
-
&& $this->getDbHandler_Events()->isReady()
|
25 |
-
&& parent::isReadyToExecute();
|
26 |
-
}
|
27 |
-
|
28 |
-
/**
|
29 |
-
* @return string
|
30 |
-
*/
|
31 |
-
protected function getNamespaceBase() :string {
|
32 |
-
return 'Events';
|
33 |
-
}
|
34 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
src/features/firewall.php
DELETED
@@ -1,68 +0,0 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
use FernleafSystems\Wordpress\Plugin\Shield;
|
4 |
-
use FernleafSystems\Wordpress\Plugin\Shield\Modules\Firewall;
|
5 |
-
|
6 |
-
/**
|
7 |
-
* @deprecated 10.1
|
8 |
-
*/
|
9 |
-
class ICWP_WPSF_FeatureHandler_Firewall extends ICWP_WPSF_FeatureHandler_BaseWpsf {
|
10 |
-
|
11 |
-
/**
|
12 |
-
* @param string $sParam
|
13 |
-
* @param string $sPage
|
14 |
-
*/
|
15 |
-
public function addParamToWhitelist( $sParam, $sPage = '*' ) {
|
16 |
-
/** @var Firewall\Options $oOpts */
|
17 |
-
$oOpts = $this->getOptions();
|
18 |
-
|
19 |
-
if ( empty( $sPage ) ) {
|
20 |
-
$sPage = '*';
|
21 |
-
}
|
22 |
-
|
23 |
-
$aW = $oOpts->getCustomWhitelist();
|
24 |
-
$aParams = isset( $aW[ $sPage ] ) ? $aW[ $sPage ] : [];
|
25 |
-
$aParams[] = $sParam;
|
26 |
-
natsort( $aParams );
|
27 |
-
$aW[ $sPage ] = array_unique( $aParams );
|
28 |
-
|
29 |
-
$oOpts->setOpt( 'page_params_whitelist', $aW );
|
30 |
-
}
|
31 |
-
|
32 |
-
/**
|
33 |
-
* @return string
|
34 |
-
*/
|
35 |
-
public function getBlockResponse() {
|
36 |
-
$response = $this->getOptions()->getOpt( 'block_response', '' );
|
37 |
-
return !empty( $response ) ? $response : 'redirect_die_message'; // TODO: use default
|
38 |
-
}
|
39 |
-
|
40 |
-
/**
|
41 |
-
* @param string $sOptKey
|
42 |
-
* @return string
|
43 |
-
*/
|
44 |
-
public function getTextOptDefault( $sOptKey ) {
|
45 |
-
|
46 |
-
switch ( $sOptKey ) {
|
47 |
-
case 'text_firewalldie':
|
48 |
-
$sText = sprintf(
|
49 |
-
__( "You were blocked by the %s.", 'wp-simple-firewall' ),
|
50 |
-
'<a href="https://wordpress.org/plugins/wp-simple-firewall/" target="_blank">'.$this->getCon()
|
51 |
-
->getHumanName().'</a>'
|
52 |
-
);
|
53 |
-
break;
|
54 |
-
|
55 |
-
default:
|
56 |
-
$sText = parent::getTextOptDefault( $sOptKey );
|
57 |
-
break;
|
58 |
-
}
|
59 |
-
return $sText;
|
60 |
-
}
|
61 |
-
|
62 |
-
/**
|
63 |
-
* @return string
|
64 |
-
*/
|
65 |
-
protected function getNamespaceBase() :string {
|
66 |
-
return 'Firewall';
|
67 |
-
}
|
68 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
src/features/hack_protect.php
DELETED
@@ -1,326 +0,0 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
use FernleafSystems\Wordpress\Plugin\Shield;
|
4 |
-
use FernleafSystems\Wordpress\Plugin\Shield\Modules\HackGuard;
|
5 |
-
use FernleafSystems\Wordpress\Services\Services;
|
6 |
-
|
7 |
-
/**
|
8 |
-
* @deprecated 10.1
|
9 |
-
*/
|
10 |
-
class ICWP_WPSF_FeatureHandler_HackProtect extends ICWP_WPSF_FeatureHandler_BaseWpsf {
|
11 |
-
|
12 |
-
/**
|
13 |
-
* @var HackGuard\Scan\Queue\Controller
|
14 |
-
*/
|
15 |
-
private $oScanQueueController;
|
16 |
-
|
17 |
-
/**
|
18 |
-
* @var HackGuard\Scan\Controller\Base[]
|
19 |
-
*/
|
20 |
-
private $aScanCons;
|
21 |
-
|
22 |
-
/**
|
23 |
-
* @var HackGuard\Lib\FileLocker\FileLockerController
|
24 |
-
*/
|
25 |
-
private $oFileLocker;
|
26 |
-
|
27 |
-
protected function doPostConstruction() {
|
28 |
-
$this->setCustomCronSchedules();
|
29 |
-
}
|
30 |
-
|
31 |
-
public function onWpInit() {
|
32 |
-
parent::onWpInit();
|
33 |
-
$this->getScanQueueController();
|
34 |
-
}
|
35 |
-
|
36 |
-
public function getFileLocker() :HackGuard\Lib\FileLocker\FileLockerController {
|
37 |
-
if ( !isset( $this->oFileLocker ) ) {
|
38 |
-
$this->oFileLocker = ( new HackGuard\Lib\FileLocker\FileLockerController() )
|
39 |
-
->setMod( $this );
|
40 |
-
}
|
41 |
-
return $this->oFileLocker;
|
42 |
-
}
|
43 |
-
|
44 |
-
public function getScanQueueController() :HackGuard\Scan\Queue\Controller {
|
45 |
-
if ( !isset( $this->oScanQueueController ) ) {
|
46 |
-
$this->oScanQueueController = ( new HackGuard\Scan\Queue\Controller() )
|
47 |
-
->setMod( $this );
|
48 |
-
}
|
49 |
-
return $this->oScanQueueController;
|
50 |
-
}
|
51 |
-
|
52 |
-
/**
|
53 |
-
* @return HackGuard\Scan\Controller\Base[]
|
54 |
-
*/
|
55 |
-
public function getAllScanCons() :array {
|
56 |
-
/** @var HackGuard\Options $opts */
|
57 |
-
$opts = $this->getOptions();
|
58 |
-
foreach ( $opts->getScanSlugs() as $scanSlug ) {
|
59 |
-
$this->getScanCon( $scanSlug );
|
60 |
-
}
|
61 |
-
return $this->aScanCons;
|
62 |
-
}
|
63 |
-
|
64 |
-
/**
|
65 |
-
* @param string $slug
|
66 |
-
* @return HackGuard\Scan\Controller\Base|mixed
|
67 |
-
*/
|
68 |
-
public function getScanCon( string $slug ) {
|
69 |
-
if ( !is_array( $this->aScanCons ) ) {
|
70 |
-
$this->aScanCons = [];
|
71 |
-
}
|
72 |
-
if ( !isset( $this->aScanCons[ $slug ] ) ) {
|
73 |
-
$class = $this->getNamespace().'Scan\Controller\\'.ucwords( $slug );
|
74 |
-
if ( @class_exists( $class ) ) {
|
75 |
-
/** @var HackGuard\Scan\Controller\Base $oObj */
|
76 |
-
$oObj = new $class();
|
77 |
-
$this->aScanCons[ $slug ] = $oObj->setMod( $this );
|
78 |
-
}
|
79 |
-
else {
|
80 |
-
$this->aScanCons[ $slug ] = false;
|
81 |
-
}
|
82 |
-
}
|
83 |
-
return $this->aScanCons[ $slug ];
|
84 |
-
}
|
85 |
-
|
86 |
-
public function getMainWpData() :array {
|
87 |
-
$issues = ( new HackGuard\Lib\Reports\Query\ScanCounts() )->setMod( $this );
|
88 |
-
$issues->notified = null;
|
89 |
-
return array_merge( parent::getMainWpData(), [
|
90 |
-
'scan_issues' => array_filter( $issues->all() )
|
91 |
-
] );
|
92 |
-
}
|
93 |
-
|
94 |
-
protected function handleModAction( string $sAction ) {
|
95 |
-
switch ( $sAction ) {
|
96 |
-
case 'scan_file_download':
|
97 |
-
( new HackGuard\Lib\Utility\FileDownloadHandler() )
|
98 |
-
->setDbHandler( $this->getDbHandler_ScanResults() )
|
99 |
-
->downloadByItemId( (int)Services::Request()->query( 'rid', 0 ) );
|
100 |
-
break;
|
101 |
-
case 'filelocker_download_original':
|
102 |
-
case 'filelocker_download_current':
|
103 |
-
$this->getFileLocker()->handleFileDownloadRequest();
|
104 |
-
break;
|
105 |
-
default:
|
106 |
-
break;
|
107 |
-
}
|
108 |
-
}
|
109 |
-
|
110 |
-
protected function preProcessOptions() {
|
111 |
-
/** @var HackGuard\Options $opts */
|
112 |
-
$opts = $this->getOptions();
|
113 |
-
|
114 |
-
$this->cleanFileExclusions();
|
115 |
-
|
116 |
-
if ( count( $opts->getFilesToLock() ) === 0 || !$this->getCon()
|
117 |
-
->getModule_Plugin()
|
118 |
-
->getShieldNetApiController()
|
119 |
-
->canHandshake() ) {
|
120 |
-
$opts->setOpt( 'file_locker', [] );
|
121 |
-
$this->getFileLocker()->purge();
|
122 |
-
}
|
123 |
-
|
124 |
-
$lockFiles = $opts->getFilesToLock();
|
125 |
-
if ( in_array( 'root_webconfig', $lockFiles ) && !Services::Data()->isWindows() ) {
|
126 |
-
unset( $lockFiles[ array_search( 'root_webconfig', $lockFiles ) ] );
|
127 |
-
$opts->setOpt( 'file_locker', $lockFiles );
|
128 |
-
}
|
129 |
-
|
130 |
-
foreach ( $this->getAllScanCons() as $con ) {
|
131 |
-
if ( !$con->isEnabled() ) {
|
132 |
-
$con->purge();
|
133 |
-
}
|
134 |
-
}
|
135 |
-
}
|
136 |
-
|
137 |
-
/**
|
138 |
-
* @return int[] - key is scan slug
|
139 |
-
*/
|
140 |
-
public function getLastScansAt() :array {
|
141 |
-
/** @var HackGuard\Options $oOpts */
|
142 |
-
$oOpts = $this->getOptions();
|
143 |
-
/** @var Shield\Databases\Events\Select $oSel */
|
144 |
-
$oSel = $this->getCon()
|
145 |
-
->getModule_Events()
|
146 |
-
->getDbHandler_Events()
|
147 |
-
->getQuerySelector();
|
148 |
-
$aEvents = $oSel->getLatestForAllEvents();
|
149 |
-
|
150 |
-
$aLatest = [];
|
151 |
-
foreach ( $oOpts->getScanSlugs() as $sScan ) {
|
152 |
-
$sEvt = $sScan.'_scan_run';
|
153 |
-
$aLatest[ $sScan ] = isset( $aEvents[ $sEvt ] ) ? $aEvents[ $sEvt ]->created_at : 0;
|
154 |
-
}
|
155 |
-
return $aLatest;
|
156 |
-
}
|
157 |
-
|
158 |
-
/**
|
159 |
-
* @param string $scan ptg, wcf, ufc, wpv
|
160 |
-
* @return int
|
161 |
-
*/
|
162 |
-
public function getLastScanAt( $scan ) {
|
163 |
-
/** @var Shield\Databases\Events\Select $oSel */
|
164 |
-
$oSel = $this->getCon()
|
165 |
-
->getModule_Events()
|
166 |
-
->getDbHandler_Events()
|
167 |
-
->getQuerySelector();
|
168 |
-
$oEntry = $oSel->getLatestForEvent( $scan.'_scan_run' );
|
169 |
-
return ( $oEntry instanceof Shield\Databases\Events\EntryVO ) ? $oEntry->created_at : 0;
|
170 |
-
}
|
171 |
-
|
172 |
-
/**
|
173 |
-
* @return $this
|
174 |
-
*/
|
175 |
-
protected function setCustomCronSchedules() {
|
176 |
-
/** @var HackGuard\Options $opts */
|
177 |
-
$opts = $this->getOptions();
|
178 |
-
$freq = $opts->getScanFrequency();
|
179 |
-
Services::WpCron()
|
180 |
-
->addNewSchedule(
|
181 |
-
$this->prefix( sprintf( 'per-day-%s', $freq ) ),
|
182 |
-
[
|
183 |
-
'interval' => DAY_IN_SECONDS/$freq,
|
184 |
-
'display' => sprintf( __( '%s per day', 'wp-simple-firewall' ), $freq )
|
185 |
-
]
|
186 |
-
);
|
187 |
-
return $this;
|
188 |
-
}
|
189 |
-
|
190 |
-
protected function cleanFileExclusions() {
|
191 |
-
/** @var HackGuard\Options $opts */
|
192 |
-
$opts = $this->getOptions();
|
193 |
-
$aExclusions = [];
|
194 |
-
|
195 |
-
$aToClean = $opts->getOpt( 'ufc_exclusions', [] );
|
196 |
-
if ( is_array( $aToClean ) ) {
|
197 |
-
foreach ( $aToClean as $nKey => $sExclusion ) {
|
198 |
-
$sExclusion = wp_normalize_path( trim( $sExclusion ) );
|
199 |
-
|
200 |
-
if ( preg_match( '/^#(.+)#$/', $sExclusion, $aMatches ) ) { // it's regex
|
201 |
-
// ignore it
|
202 |
-
}
|
203 |
-
elseif ( strpos( $sExclusion, '/' ) === false ) { // filename only
|
204 |
-
$sExclusion = trim( preg_replace( '#[^.0-9a-z_-]#i', '', $sExclusion ) );
|
205 |
-
}
|
206 |
-
|
207 |
-
if ( !empty( $sExclusion ) ) {
|
208 |
-
$aExclusions[] = $sExclusion;
|
209 |
-
}
|
210 |
-
}
|
211 |
-
}
|
212 |
-
|
213 |
-
$opts->setOpt( 'ufc_exclusions', array_unique( $aExclusions ) );
|
214 |
-
}
|
215 |
-
|
216 |
-
/**
|
217 |
-
* @return bool
|
218 |
-
*/
|
219 |
-
public function isWpvulnPluginsHighlightEnabled() {
|
220 |
-
$oWpvCon = $this->getScanCon( 'wpv' );
|
221 |
-
if ( $oWpvCon->isEnabled() ) {
|
222 |
-
$sOpt = apply_filters( 'icwp_shield_wpvuln_scan_display', 'securityadmin' );
|
223 |
-
}
|
224 |
-
else {
|
225 |
-
$sOpt = 'disabled';
|
226 |
-
}
|
227 |
-
return ( $sOpt != 'disabled' ) && Services::WpUsers()->isUserAdmin()
|
228 |
-
&& ( ( $sOpt != 'securityadmin' ) || $this->getCon()->isPluginAdmin() );
|
229 |
-
}
|
230 |
-
|
231 |
-
public function isPtgEnabled() :bool {
|
232 |
-
$opts = $this->getOptions();
|
233 |
-
return $this->isModuleEnabled() && $this->isPremium()
|
234 |
-
&& $opts->isOpt( 'ptg_enable', 'enabled' )
|
235 |
-
&& $opts->isOptReqsMet( 'ptg_enable' )
|
236 |
-
&& $this->canCacheDirWrite();
|
237 |
-
}
|
238 |
-
|
239 |
-
public function insertCustomJsVars_Admin() {
|
240 |
-
parent::insertCustomJsVars_Admin();
|
241 |
-
|
242 |
-
/** @var HackGuard\Options $opts */
|
243 |
-
$opts = $this->getOptions();
|
244 |
-
if ( Services::WpPost()->isCurrentPage( 'plugins.php' )
|
245 |
-
&& $opts->isPtgReinstallLinks() && $this->getScanCon( 'ptg' )->isReady() ) {
|
246 |
-
wp_localize_script(
|
247 |
-
$this->prefix( 'global-plugin' ),
|
248 |
-
'icwp_wpsf_vars_hp',
|
249 |
-
[
|
250 |
-
'ajax_plugin_reinstall' => $this->getAjaxActionData( 'plugin_reinstall' ),
|
251 |
-
'reinstallable' => Services::WpPlugins()->getInstalledWpOrgPluginFiles(),
|
252 |
-
'strings' => [
|
253 |
-
'reinstall_first' => __( 'Re-install First', 'wp-simple-firewall' )
|
254 |
-
.'. '.__( 'Then Activate', 'wp-simple-firewall' ),
|
255 |
-
'okay_reinstall' => sprintf( '%s, %s',
|
256 |
-
__( 'Yes', 'wp-simple-firewall' ), __( 'Re-Install It', 'wp-simple-firewall' ) ),
|
257 |
-
'activate_only' => __( 'Activate Only', 'wp-simple-firewall' ),
|
258 |
-
'cancel' => __( 'Cancel', 'wp-simple-firewall' ),
|
259 |
-
]
|
260 |
-
]
|
261 |
-
);
|
262 |
-
wp_enqueue_script( 'jquery-ui-dialog' ); // jquery and jquery-ui should be dependencies, didn't check though...
|
263 |
-
wp_enqueue_style( 'wp-jquery-ui-dialog' );
|
264 |
-
}
|
265 |
-
}
|
266 |
-
|
267 |
-
/**
|
268 |
-
* @return string|false
|
269 |
-
*/
|
270 |
-
public function getPtgSnapsBaseDir() {
|
271 |
-
return $this->getCon()->getPluginCachePath( 'ptguard/' );
|
272 |
-
}
|
273 |
-
|
274 |
-
public function hasWizard() :bool {
|
275 |
-
return false;
|
276 |
-
}
|
277 |
-
|
278 |
-
/**
|
279 |
-
* @return string
|
280 |
-
*/
|
281 |
-
public function getTempDir() {
|
282 |
-
$sDir = $this->getCon()->getPluginCachePath( 'scans' );
|
283 |
-
return Services::WpFs()->mkdir( $sDir ) ? $sDir : false;
|
284 |
-
}
|
285 |
-
|
286 |
-
public function getDbHandler_FileLocker() :Shield\Databases\FileLocker\Handler {
|
287 |
-
return $this->getDbH( 'file_protect' );
|
288 |
-
}
|
289 |
-
|
290 |
-
public function getDbHandler_ScanQueue() :Shield\Databases\ScanQueue\Handler {
|
291 |
-
return $this->getDbH( 'scanq' );
|
292 |
-
}
|
293 |
-
|
294 |
-
public function getDbHandler_ScanResults() :Shield\Databases\Scanner\Handler {
|
295 |
-
return $this->getDbH( 'scanresults' );
|
296 |
-
}
|
297 |
-
|
298 |
-
/**
|
299 |
-
* @return bool
|
300 |
-
* @throws \Exception
|
301 |
-
*/
|
302 |
-
protected function isReadyToExecute() {
|
303 |
-
return ( $this->getDbHandler_ScanQueue() instanceof Shield\Databases\ScanQueue\Handler )
|
304 |
-
&& $this->getDbHandler_ScanQueue()->isReady()
|
305 |
-
&& ( $this->getDbHandler_ScanResults() instanceof Shield\Databases\Scanner\Handler )
|
306 |
-
&& $this->getDbHandler_ScanQueue()->isReady()
|
307 |
-
&& parent::isReadyToExecute();
|
308 |
-
}
|
309 |
-
|
310 |
-
public function onPluginDeactivate() {
|
311 |
-
// 1. Clean out the scanners
|
312 |
-
/** @var HackGuard\Options $oOpts */
|
313 |
-
$oOpts = $this->getOptions();
|
314 |
-
foreach ( $oOpts->getScanSlugs() as $sSlug ) {
|
315 |
-
$this->getScanCon( $sSlug )->purge();
|
316 |
-
}
|
317 |
-
$this->getDbHandler_ScanQueue()->tableDelete();
|
318 |
-
$this->getDbHandler_ScanResults()->tableDelete();
|
319 |
-
// 2. Clean out the file locker
|
320 |
-
$this->getFileLocker()->purge();
|
321 |
-
}
|
322 |
-
|
323 |
-
protected function getNamespaceBase() :string {
|
324 |
-
return 'HackGuard';
|
325 |
-
}
|
326 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
src/features/headers.php
DELETED
@@ -1,98 +0,0 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
use FernleafSystems\Wordpress\Plugin\Shield\Modules\Headers;
|
4 |
-
use FernleafSystems\Wordpress\Services\Services;
|
5 |
-
|
6 |
-
/**
|
7 |
-
* @deprecated 10.1
|
8 |
-
*/
|
9 |
-
class ICWP_WPSF_FeatureHandler_Headers extends ICWP_WPSF_FeatureHandler_BaseWpsf {
|
10 |
-
|
11 |
-
protected function preProcessOptions() {
|
12 |
-
$this->cleanCspHosts();
|
13 |
-
$this->cleanCustomRules();
|
14 |
-
}
|
15 |
-
|
16 |
-
private function cleanCustomRules() {
|
17 |
-
/** @var Headers\Options $opts */
|
18 |
-
$opts = $this->getOptions();
|
19 |
-
$opts->setOpt( 'xcsp_custom', array_unique( array_filter( array_map(
|
20 |
-
function ( $sRule ) {
|
21 |
-
$sRule = trim( preg_replace( '#;|\s{2,}#', '', html_entity_decode( $sRule, ENT_QUOTES ) ) );
|
22 |
-
if ( !empty( $sRule ) ) {
|
23 |
-
$sRule .= ';';
|
24 |
-
}
|
25 |
-
return $sRule;
|
26 |
-
},
|
27 |
-
$opts->getOpt( 'xcsp_custom', [] )
|
28 |
-
) ) ) );
|
29 |
-
}
|
30 |
-
|
31 |
-
private function cleanCspHosts() {
|
32 |
-
/** @var Headers\Options $oOpts */
|
33 |
-
$oOpts = $this->getOptions();
|
34 |
-
|
35 |
-
$aValidDomains = [];
|
36 |
-
foreach ( $oOpts->getOpt( 'xcsp_hosts', [] ) as $sDomain ) {
|
37 |
-
$bValidDomain = false;
|
38 |
-
$sDomain = trim( $sDomain );
|
39 |
-
|
40 |
-
$bHttps = ( strpos( $sDomain, 'https://' ) === 0 );
|
41 |
-
$bHttp = ( strpos( $sDomain, 'http://' ) === 0 );
|
42 |
-
if ( $bHttp || $bHttps ) {
|
43 |
-
$sDomain = preg_replace( '#^http(s)?://#', '', $sDomain );
|
44 |
-
}
|
45 |
-
|
46 |
-
$sCustomProtocol = '';
|
47 |
-
// Special wildcard case
|
48 |
-
if ( $sDomain == '*' ) {
|
49 |
-
if ( $bHttps ) {
|
50 |
-
$this->getOptions()->setOpt( 'xcsp_https', 'Y' );
|
51 |
-
}
|
52 |
-
else {
|
53 |
-
$bValidDomain = true;
|
54 |
-
}
|
55 |
-
}
|
56 |
-
elseif ( strpos( $sDomain, '://' ) && preg_match( '#^([a-zA-Z]+://)#', $sDomain, $aMatches ) ) {
|
57 |
-
// there's a protocol specified
|
58 |
-
$sCustomProtocol = $aMatches[ 1 ];
|
59 |
-
$sDomain = str_replace( $sCustomProtocol, '', $sDomain );
|
60 |
-
}
|
61 |
-
|
62 |
-
// First we remove the wildcard and test domain, then add it back later.
|
63 |
-
$bWildCard = ( strpos( $sDomain, '*.' ) === 0 );
|
64 |
-
if ( $bWildCard ) {
|
65 |
-
$sDomain = preg_replace( '#^\*\.#', '', $sDomain );
|
66 |
-
}
|
67 |
-
|
68 |
-
if ( !empty ( $sDomain ) && Services::Data()->isValidDomainName( $sDomain ) ) {
|
69 |
-
$bValidDomain = true;
|
70 |
-
}
|
71 |
-
|
72 |
-
if ( $bValidDomain ) {
|
73 |
-
if ( $bWildCard ) {
|
74 |
-
$sDomain = '*.'.$sDomain;
|
75 |
-
}
|
76 |
-
if ( $bHttp ) {
|
77 |
-
// $sDomain = 'http://'.$sDomain; // it seems there's no need to "explicitly" state http://
|
78 |
-
}
|
79 |
-
elseif ( $bHttps ) {
|
80 |
-
$sDomain = 'https://'.$sDomain;
|
81 |
-
}
|
82 |
-
elseif ( !empty( $sCustomProtocol ) ) {
|
83 |
-
$sDomain = $sCustomProtocol.$sDomain;
|
84 |
-
}
|
85 |
-
$aValidDomains[] = $sDomain;
|
86 |
-
}
|
87 |
-
}
|
88 |
-
asort( $aValidDomains );
|
89 |
-
$oOpts->setOpt( 'xcsp_hosts', array_unique( $aValidDomains ) );
|
90 |
-
}
|
91 |
-
|
92 |
-
/**
|
93 |
-
* @return string
|
94 |
-
*/
|
95 |
-
protected function getNamespaceBase() :string {
|
96 |
-
return 'Headers';
|
97 |
-
}
|
98 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
src/features/insights.php
DELETED
@@ -1,226 +0,0 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
use FernleafSystems\Wordpress\Plugin\Shield;
|
4 |
-
use FernleafSystems\Wordpress\Services\Services;
|
5 |
-
|
6 |
-
/**
|
7 |
-
* @deprecated 10.1
|
8 |
-
*/
|
9 |
-
class ICWP_WPSF_FeatureHandler_Insights extends ICWP_WPSF_FeatureHandler_BaseWpsf {
|
10 |
-
|
11 |
-
protected function onModulesLoaded() {
|
12 |
-
$this->maybeRedirectToAdmin();
|
13 |
-
}
|
14 |
-
|
15 |
-
private function maybeRedirectToAdmin() {
|
16 |
-
$con = $this->getCon();
|
17 |
-
$nActiveFor = $con->getModule_Plugin()->getActivateLength();
|
18 |
-
if ( !Services::WpGeneral()->isAjax() && is_admin() && !$con->isModulePage() && $nActiveFor < 4 ) {
|
19 |
-
Services::Response()->redirect( $this->getUrl_AdminPage() );
|
20 |
-
}
|
21 |
-
}
|
22 |
-
|
23 |
-
public function getUrl_IpAnalysis( string $ip ) :string {
|
24 |
-
return add_query_arg( [ 'analyse_ip' => $ip ], $this->getUrl_SubInsightsPage( 'ips' ) );
|
25 |
-
}
|
26 |
-
|
27 |
-
public function getUrl_SubInsightsPage( string $subPage ) :string {
|
28 |
-
return add_query_arg(
|
29 |
-
[ 'inav' => sanitize_key( $subPage ) ],
|
30 |
-
$this->getUrl_AdminPage()
|
31 |
-
);
|
32 |
-
}
|
33 |
-
|
34 |
-
protected function renderModulePage( array $aData = [] ) :string {
|
35 |
-
/** @var Shield\Modules\Insights\UI $UI */
|
36 |
-
$UI = $this->getUIHandler();
|
37 |
-
return $UI->renderPages();
|
38 |
-
}
|
39 |
-
|
40 |
-
public function insertCustomJsVars_Admin() {
|
41 |
-
parent::insertCustomJsVars_Admin();
|
42 |
-
|
43 |
-
if ( $this->isThisModulePage() ) {
|
44 |
-
|
45 |
-
$con = $this->getCon();
|
46 |
-
$aStdDepsJs = [ $con->prefix( 'plugin' ) ];
|
47 |
-
$iNav = Services::Request()->query( 'inav', 'overview' );
|
48 |
-
|
49 |
-
$oModPlugin = $con->getModule_Plugin();
|
50 |
-
$oTourManager = $oModPlugin->getTourManager();
|
51 |
-
switch ( $iNav ) {
|
52 |
-
|
53 |
-
case 'importexport':
|
54 |
-
|
55 |
-
$sAsset = 'shield/import';
|
56 |
-
$sUnique = $con->prefix( $sAsset );
|
57 |
-
wp_register_script(
|
58 |
-
$sUnique,
|
59 |
-
$con->getPluginUrl_Js( $sAsset ),
|
60 |
-
$aStdDepsJs,
|
61 |
-
$con->getVersion(),
|
62 |
-
false
|
63 |
-
);
|
64 |
-
wp_enqueue_script( $sUnique );
|
65 |
-
break;
|
66 |
-
|
67 |
-
case 'overview':
|
68 |
-
case 'reports':
|
69 |
-
|
70 |
-
$aDeps = $aStdDepsJs;
|
71 |
-
|
72 |
-
$aJsAssets = [
|
73 |
-
'chartist.min',
|
74 |
-
'chartist-plugin-legend',
|
75 |
-
'charts',
|
76 |
-
'shuffle',
|
77 |
-
'shield-card-shuffle'
|
78 |
-
];
|
79 |
-
if ( $oTourManager->canShow( 'insights_overview' ) ) {
|
80 |
-
array_unshift( $aJsAssets, 'introjs.min.js' );
|
81 |
-
}
|
82 |
-
foreach ( $aJsAssets as $sAsset ) {
|
83 |
-
$sUnique = $con->prefix( $sAsset );
|
84 |
-
wp_register_script(
|
85 |
-
$sUnique,
|
86 |
-
$con->getPluginUrl_Js( $sAsset ),
|
87 |
-
$aDeps,
|
88 |
-
$con->getVersion(),
|
89 |
-
false
|
90 |
-
);
|
91 |
-
wp_enqueue_script( $sUnique );
|
92 |
-
$aDeps[] = $sUnique;
|
93 |
-
}
|
94 |
-
|
95 |
-
$aDeps = [];
|
96 |
-
$aCssAssets = [ 'chartist.min', 'chartist-plugin-legend' ];
|
97 |
-
if ( $oTourManager->canShow( 'insights_overview' ) ) {
|
98 |
-
array_unshift( $aCssAssets, 'introjs.min.css' );
|
99 |
-
}
|
100 |
-
foreach ( $aCssAssets as $sAsset ) {
|
101 |
-
$sUnique = $con->prefix( $sAsset );
|
102 |
-
wp_register_style(
|
103 |
-
$sUnique,
|
104 |
-
$con->getPluginUrl_Css( $sAsset ),
|
105 |
-
$aDeps,
|
106 |
-
$con->getVersion(),
|
107 |
-
false
|
108 |
-
);
|
109 |
-
wp_enqueue_style( $sUnique );
|
110 |
-
$aDeps[] = $sUnique;
|
111 |
-
}
|
112 |
-
|
113 |
-
$this->includeScriptIpDetect();
|
114 |
-
break;
|
115 |
-
|
116 |
-
case 'notes':
|
117 |
-
case 'scans':
|
118 |
-
case 'audit':
|
119 |
-
case 'traffic':
|
120 |
-
case 'ips':
|
121 |
-
case 'debug':
|
122 |
-
case 'users':
|
123 |
-
|
124 |
-
$sAsset = 'shield-tables';
|
125 |
-
$sUnique = $con->prefix( $sAsset );
|
126 |
-
wp_register_script(
|
127 |
-
$sUnique,
|
128 |
-
$con->getPluginUrl_Js( $sAsset ),
|
129 |
-
$aStdDepsJs,
|
130 |
-
$con->getVersion(),
|
131 |
-
false
|
132 |
-
);
|
133 |
-
wp_enqueue_script( $sUnique );
|
134 |
-
|
135 |
-
$aStdDepsJs[] = $sUnique;
|
136 |
-
if ( $iNav == 'scans' ) {
|
137 |
-
$sAsset = 'shield-scans';
|
138 |
-
$sUnique = $con->prefix( $sAsset );
|
139 |
-
wp_register_script(
|
140 |
-
$sUnique,
|
141 |
-
$con->getPluginUrl_Js( $sAsset ),
|
142 |
-
array_unique( $aStdDepsJs ),
|
143 |
-
$con->getVersion(),
|
144 |
-
false
|
145 |
-
);
|
146 |
-
wp_enqueue_script( $sUnique );
|
147 |
-
}
|
148 |
-
|
149 |
-
if ( $iNav == 'ips' ) {
|
150 |
-
$sAsset = 'shield/ipanalyse';
|
151 |
-
$sUnique = $con->prefix( $sAsset );
|
152 |
-
wp_register_script(
|
153 |
-
$sUnique,
|
154 |
-
$con->getPluginUrl_Js( $sAsset ),
|
155 |
-
array_unique( $aStdDepsJs ),
|
156 |
-
$con->getVersion(),
|
157 |
-
false
|
158 |
-
);
|
159 |
-
wp_enqueue_script( $sUnique );
|
160 |
-
}
|
161 |
-
|
162 |
-
if ( in_array( $iNav, [ 'audit', 'traffic' ] ) ) {
|
163 |
-
$sUnique = $con->prefix( 'datepicker' );
|
164 |
-
wp_register_script(
|
165 |
-
$sUnique, //TODO: use an includes services for CNDJS
|
166 |
-
'https://cdnjs.cloudflare.com/ajax/libs/bootstrap-datepicker/1.8.0/js/bootstrap-datepicker.min.js',
|
167 |
-
array_unique( $aStdDepsJs ),
|
168 |
-
$con->getVersion(),
|
169 |
-
false
|
170 |
-
);
|
171 |
-
wp_enqueue_script( $sUnique );
|
172 |
-
|
173 |
-
wp_register_style(
|
174 |
-
$sUnique,
|
175 |
-
'https://cdnjs.cloudflare.com/ajax/libs/bootstrap-datepicker/1.8.0/css/bootstrap-datepicker.min.css',
|
176 |
-
[],
|
177 |
-
$con->getVersion(),
|
178 |
-
false
|
179 |
-
);
|
180 |
-
wp_enqueue_style( $sUnique );
|
181 |
-
}
|
182 |
-
|
183 |
-
break;
|
184 |
-
}
|
185 |
-
|
186 |
-
wp_localize_script(
|
187 |
-
$con->prefix( 'plugin' ),
|
188 |
-
'icwp_wpsf_vars_insights',
|
189 |
-
[
|
190 |
-
'strings' => [
|
191 |
-
'downloading_file' => __( 'Downloading file, please wait...', 'wp-simple-firewall' ),
|
192 |
-
'downloading_file_problem' => __( 'There was a problem downloading the file.', 'wp-simple-firewall' ),
|
193 |
-
'select_action' => __( 'Please select an action to perform.', 'wp-simple-firewall' ),
|
194 |
-
'are_you_sure' => __( 'Are you sure?', 'wp-simple-firewall' ),
|
195 |
-
],
|
196 |
-
]
|
197 |
-
);
|
198 |
-
}
|
199 |
-
}
|
200 |
-
|
201 |
-
private function includeScriptIpDetect() {
|
202 |
-
$con = $this->getCon();
|
203 |
-
/** @var Shield\Modules\Plugin\Options $opts */
|
204 |
-
$opts = $con->getModule_Plugin()->getOptions();
|
205 |
-
if ( $opts->isIpSourceAutoDetect() ) {
|
206 |
-
wp_register_script(
|
207 |
-
$con->prefix( 'ip_detect' ),
|
208 |
-
$con->getPluginUrl_Js( 'ip_detect.js' ),
|
209 |
-
[],
|
210 |
-
$con->getVersion(),
|
211 |
-
true
|
212 |
-
);
|
213 |
-
wp_enqueue_script( $con->prefix( 'ip_detect' ) );
|
214 |
-
|
215 |
-
wp_localize_script(
|
216 |
-
$con->prefix( 'ip_detect' ),
|
217 |
-
'icwp_wpsf_vars_ipdetect',
|
218 |
-
[ 'ajax' => $con->getModule_Plugin()->getAjaxActionData( 'ipdetect' ) ]
|
219 |
-
);
|
220 |
-
}
|
221 |
-
}
|
222 |
-
|
223 |
-
protected function getNamespaceBase() :string {
|
224 |
-
return 'Insights';
|
225 |
-
}
|
226 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
src/features/ips.php
DELETED
@@ -1,155 +0,0 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
use FernleafSystems\Wordpress\Plugin\Shield;
|
4 |
-
use FernleafSystems\Wordpress\Plugin\Shield\Modules\IPs;
|
5 |
-
use FernleafSystems\Wordpress\Services\Services;
|
6 |
-
|
7 |
-
/**
|
8 |
-
* @deprecated 10.1
|
9 |
-
*/
|
10 |
-
class ICWP_WPSF_FeatureHandler_Ips extends ICWP_WPSF_FeatureHandler_BaseWpsf {
|
11 |
-
|
12 |
-
const LIST_MANUAL_WHITE = 'MW';
|
13 |
-
const LIST_MANUAL_BLACK = 'MB';
|
14 |
-
const LIST_AUTO_BLACK = 'AB';
|
15 |
-
|
16 |
-
/**
|
17 |
-
* @var IPs\Lib\OffenseTracker
|
18 |
-
*/
|
19 |
-
private $oOffenseTracker;
|
20 |
-
|
21 |
-
/**
|
22 |
-
* @var IPs\Lib\BlacklistHandler
|
23 |
-
*/
|
24 |
-
private $oBlacklistHandler;
|
25 |
-
|
26 |
-
/**
|
27 |
-
* @return IPs\Lib\BlacklistHandler
|
28 |
-
*/
|
29 |
-
public function getBlacklistHandler() {
|
30 |
-
if ( !isset( $this->oBlacklistHandler ) ) {
|
31 |
-
$this->oBlacklistHandler = ( new IPs\Lib\BlacklistHandler() )->setMod( $this );
|
32 |
-
}
|
33 |
-
return $this->oBlacklistHandler;
|
34 |
-
}
|
35 |
-
|
36 |
-
/**
|
37 |
-
* @return IPs\Lib\BlacklistHandler
|
38 |
-
*/
|
39 |
-
public function getProcessor() {
|
40 |
-
return $this->getBlacklistHandler();
|
41 |
-
}
|
42 |
-
|
43 |
-
/**
|
44 |
-
* @return false|Shield\Databases\IPs\Handler
|
45 |
-
*/
|
46 |
-
public function getDbHandler_IPs() {
|
47 |
-
return $this->getDbH( 'ips' );
|
48 |
-
}
|
49 |
-
|
50 |
-
/**
|
51 |
-
* @return bool
|
52 |
-
* @throws \Exception
|
53 |
-
*/
|
54 |
-
protected function isReadyToExecute() {
|
55 |
-
$oIp = Services::IP();
|
56 |
-
return $oIp->isValidIp_PublicRange( $oIp->getRequestIp() )
|
57 |
-
&& ( $this->getDbHandler_IPs() instanceof Shield\Databases\IPs\Handler )
|
58 |
-
&& $this->getDbHandler_IPs()->isReady()
|
59 |
-
&& parent::isReadyToExecute();
|
60 |
-
}
|
61 |
-
|
62 |
-
protected function preProcessOptions() {
|
63 |
-
/** @var IPs\Options $oOpts */
|
64 |
-
$oOpts = $this->getOptions();
|
65 |
-
if ( !defined( strtoupper( $oOpts->getOpt( 'auto_expire' ).'_IN_SECONDS' ) ) ) {
|
66 |
-
$oOpts->resetOptToDefault( 'auto_expire' );
|
67 |
-
}
|
68 |
-
|
69 |
-
$nLimit = $oOpts->getOffenseLimit();
|
70 |
-
if ( !is_int( $nLimit ) || $nLimit < 0 ) {
|
71 |
-
$oOpts->resetOptToDefault( 'transgression_limit' );
|
72 |
-
}
|
73 |
-
|
74 |
-
$this->cleanPathWhitelist();
|
75 |
-
}
|
76 |
-
|
77 |
-
private function cleanPathWhitelist() {
|
78 |
-
/** @var IPs\Options $opts */
|
79 |
-
$opts = $this->getOptions();
|
80 |
-
$opts->setOpt( 'request_whitelist', array_unique( array_filter( array_map(
|
81 |
-
function ( $sRule ) {
|
82 |
-
$sRule = strtolower( trim( $sRule ) );
|
83 |
-
if ( !empty( $sRule ) ) {
|
84 |
-
$aToCheck = [
|
85 |
-
parse_url( Services::WpGeneral()->getHomeUrl(), PHP_URL_PATH ),
|
86 |
-
parse_url( Services::WpGeneral()->getWpUrl(), PHP_URL_PATH ),
|
87 |
-
];
|
88 |
-
$sRegEx = sprintf( '#^%s$#i', str_replace( 'STAR', '.*', preg_quote( str_replace( '*', 'STAR', $sRule ), '#' ) ) );
|
89 |
-
foreach ( $aToCheck as $sPath ) {
|
90 |
-
$sSlashPath = rtrim( $sPath, '/' ).'/';
|
91 |
-
if ( preg_match( $sRegEx, $sPath ) || preg_match( $sRegEx, $sSlashPath ) ) {
|
92 |
-
$sRule = false;
|
93 |
-
break;
|
94 |
-
}
|
95 |
-
}
|
96 |
-
}
|
97 |
-
return $sRule;
|
98 |
-
},
|
99 |
-
$opts->getOpt( 'request_whitelist', [] ) // do not use Options getter as it formats into regex
|
100 |
-
) ) ) );
|
101 |
-
}
|
102 |
-
|
103 |
-
/**
|
104 |
-
* @return IPs\Lib\OffenseTracker
|
105 |
-
*/
|
106 |
-
public function loadOffenseTracker() {
|
107 |
-
if ( !isset( $this->oOffenseTracker ) ) {
|
108 |
-
$this->oOffenseTracker = new IPs\Lib\OffenseTracker( $this->getCon() );
|
109 |
-
}
|
110 |
-
return $this->oOffenseTracker;
|
111 |
-
}
|
112 |
-
|
113 |
-
/**
|
114 |
-
* @param string $sOptKey
|
115 |
-
* @return string
|
116 |
-
*/
|
117 |
-
public function getTextOptDefault( $sOptKey ) {
|
118 |
-
|
119 |
-
switch ( $sOptKey ) {
|
120 |
-
|
121 |
-
case 'text_loginfailed':
|
122 |
-
$sText = sprintf( '%s: %s',
|
123 |
-
__( 'Warning', 'wp-simple-firewall' ),
|
124 |
-
__( 'Repeated login attempts that fail will result in a complete ban of your IP Address.', 'wp-simple-firewall' )
|
125 |
-
);
|
126 |
-
break;
|
127 |
-
|
128 |
-
case 'text_remainingtrans':
|
129 |
-
$sText = sprintf( '%s: %s',
|
130 |
-
__( 'Warning', 'wp-simple-firewall' ),
|
131 |
-
__( 'You have %s remaining offenses(s) against this site and then your IP address will be completely blocked.', 'wp-simple-firewall' )
|
132 |
-
.'<br/><strong>'.__( 'Seriously, stop repeating what you are doing or you will be locked out.', 'wp-simple-firewall' ).'</strong>'
|
133 |
-
);
|
134 |
-
break;
|
135 |
-
|
136 |
-
default:
|
137 |
-
$sText = parent::getTextOptDefault( $sOptKey );
|
138 |
-
break;
|
139 |
-
}
|
140 |
-
return $sText;
|
141 |
-
}
|
142 |
-
|
143 |
-
/**
|
144 |
-
* @return string
|
145 |
-
*/
|
146 |
-
protected function getNamespaceBase() :string {
|
147 |
-
return 'IPs';
|
148 |
-
}
|
149 |
-
|
150 |
-
/**
|
151 |
-
* @deprecated 10.1
|
152 |
-
*/
|
153 |
-
protected function addFilterIpsToWhiteList() {
|
154 |
-
}
|
155 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
src/features/license.php
DELETED
@@ -1,87 +0,0 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
use FernleafSystems\Wordpress\Plugin\Shield;
|
4 |
-
use FernleafSystems\Wordpress\Plugin\Shield\Modules\License;
|
5 |
-
use FernleafSystems\Wordpress\Services\Services;
|
6 |
-
|
7 |
-
/**
|
8 |
-
* @deprecated 10.1
|
9 |
-
*/
|
10 |
-
class ICWP_WPSF_FeatureHandler_License extends ICWP_WPSF_FeatureHandler_BaseWpsf {
|
11 |
-
|
12 |
-
/**
|
13 |
-
* @var License\Lib\LicenseHandler
|
14 |
-
*/
|
15 |
-
private $oLicHandler;
|
16 |
-
|
17 |
-
/**
|
18 |
-
* @var License\Lib\WpHashes\ApiTokenManager
|
19 |
-
*/
|
20 |
-
private $oWpHashesTokenManager;
|
21 |
-
|
22 |
-
/**
|
23 |
-
* @return License\Lib\LicenseHandler
|
24 |
-
*/
|
25 |
-
public function getProcessor() {
|
26 |
-
return $this->getLicenseHandler();
|
27 |
-
}
|
28 |
-
|
29 |
-
/**
|
30 |
-
* @return License\Lib\LicenseHandler
|
31 |
-
*/
|
32 |
-
public function getLicenseHandler() {
|
33 |
-
if ( !isset( $this->oLicHandler ) ) {
|
34 |
-
$this->oLicHandler = ( new License\Lib\LicenseHandler() )->setMod( $this );
|
35 |
-
}
|
36 |
-
return $this->oLicHandler;
|
37 |
-
}
|
38 |
-
|
39 |
-
/**
|
40 |
-
* @return License\Lib\WpHashes\ApiTokenManager
|
41 |
-
*/
|
42 |
-
public function getWpHashesTokenManager() {
|
43 |
-
if ( !isset( $this->oWpHashesTokenManager ) ) {
|
44 |
-
$this->oWpHashesTokenManager = ( new License\Lib\WpHashes\ApiTokenManager() )->setMod( $this );
|
45 |
-
}
|
46 |
-
return $this->oWpHashesTokenManager;
|
47 |
-
}
|
48 |
-
|
49 |
-
protected function redirectToInsightsSubPage() {
|
50 |
-
Services::Response()->redirect(
|
51 |
-
$this->getCon()->getModule_Insights()->getUrl_AdminPage(),
|
52 |
-
[ 'inav' => 'license' ]
|
53 |
-
);
|
54 |
-
}
|
55 |
-
|
56 |
-
public function runHourlyCron() {
|
57 |
-
$this->getWpHashesTokenManager()->getToken();
|
58 |
-
}
|
59 |
-
|
60 |
-
public function onWpInit() {
|
61 |
-
parent::onWpInit();
|
62 |
-
$this->getWpHashesTokenManager()->execute();
|
63 |
-
}
|
64 |
-
|
65 |
-
/**
|
66 |
-
* @return bool
|
67 |
-
*/
|
68 |
-
public function getIfShowModuleMenuItem() {
|
69 |
-
return parent::getIfShowModuleMenuItem() && !$this->isPremium();
|
70 |
-
}
|
71 |
-
|
72 |
-
public function onPluginShutdown() {
|
73 |
-
try {
|
74 |
-
$this->getLicenseHandler()->verify( false );
|
75 |
-
}
|
76 |
-
catch ( Exception $oE ) {
|
77 |
-
}
|
78 |
-
parent::onPluginShutdown();
|
79 |
-
}
|
80 |
-
|
81 |
-
/**
|
82 |
-
* @return string
|
83 |
-
*/
|
84 |
-
protected function getNamespaceBase() :string {
|
85 |
-
return 'License';
|
86 |
-
}
|
87 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
src/features/lockdown.php
DELETED
@@ -1,39 +0,0 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
use FernleafSystems\Wordpress\Plugin\Shield;
|
4 |
-
|
5 |
-
/**
|
6 |
-
* @deprecated 10.1
|
7 |
-
*/
|
8 |
-
class ICWP_WPSF_FeatureHandler_Lockdown extends ICWP_WPSF_FeatureHandler_BaseWpsf {
|
9 |
-
|
10 |
-
/**
|
11 |
-
* @param string $namespace
|
12 |
-
* @return bool
|
13 |
-
*/
|
14 |
-
public function isPermittedAnonRestApiNamespace( $namespace ) {
|
15 |
-
/** @var Shield\Modules\Lockdown\Options $opts */
|
16 |
-
$opts = $this->getOptions();
|
17 |
-
return in_array( $namespace, $opts->getRestApiAnonymousExclusions() );
|
18 |
-
}
|
19 |
-
|
20 |
-
protected function preProcessOptions() {
|
21 |
-
$this->cleanApiExclusions();
|
22 |
-
}
|
23 |
-
|
24 |
-
private function cleanApiExclusions() {
|
25 |
-
/** @var Shield\Modules\Lockdown\Options $opts */
|
26 |
-
$opts = $this->getOptions();
|
27 |
-
$opts->setOpt(
|
28 |
-
'api_namespace_exclusions',
|
29 |
-
$this->cleanStringArray( $opts->getRestApiAnonymousExclusions(), '#[^a-z0-9_-]#i' )
|
30 |
-
);
|
31 |
-
}
|
32 |
-
|
33 |
-
/**
|
34 |
-
* @return string
|
35 |
-
*/
|
36 |
-
protected function getNamespaceBase() :string {
|
37 |
-
return 'Lockdown';
|
38 |
-
}
|
39 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
src/features/login_protect.php
DELETED
@@ -1,295 +0,0 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
use FernleafSystems\Wordpress\Plugin\Shield;
|
4 |
-
use FernleafSystems\Wordpress\Plugin\Shield\Modules\LoginGuard;
|
5 |
-
use FernleafSystems\Wordpress\Services\Services;
|
6 |
-
|
7 |
-
/**
|
8 |
-
* @deprecated 10.1
|
9 |
-
*/
|
10 |
-
class ICWP_WPSF_FeatureHandler_LoginProtect extends ICWP_WPSF_FeatureHandler_BaseWpsf {
|
11 |
-
|
12 |
-
/**
|
13 |
-
* @var LoginGuard\Lib\TwoFactor\MfaController
|
14 |
-
*/
|
15 |
-
private $oLoginIntentController;
|
16 |
-
|
17 |
-
protected function preProcessOptions() {
|
18 |
-
/** @var LoginGuard\Options $opts */
|
19 |
-
$opts = $this->getOptions();
|
20 |
-
/**
|
21 |
-
* $oWp = $this->loadWpFunctionsProcessor();
|
22 |
-
* $sCustomLoginPath = $this->cleanLoginUrlPath();
|
23 |
-
* if ( !empty( $sCustomLoginPath ) && $oWp->getIsPermalinksEnabled() ) {
|
24 |
-
* $oWp->resavePermalinks();
|
25 |
-
* }
|
26 |
-
*/
|
27 |
-
if ( $this->isModuleOptionsRequest() && $opts->isEnabledEmailAuth() && !$opts->getIfCanSendEmailVerified() ) {
|
28 |
-
$this->setIfCanSendEmail( false )
|
29 |
-
->sendEmailVerifyCanSend();
|
30 |
-
}
|
31 |
-
|
32 |
-
$aIds = $opts->getOpt( 'antibot_form_ids', [] );
|
33 |
-
foreach ( $aIds as $nKey => $sId ) {
|
34 |
-
$sId = trim( strip_tags( $sId ) );
|
35 |
-
if ( empty( $sId ) ) {
|
36 |
-
unset( $aIds[ $nKey ] );
|
37 |
-
}
|
38 |
-
else {
|
39 |
-
$aIds[ $nKey ] = $sId;
|
40 |
-
}
|
41 |
-
}
|
42 |
-
$opts->setOpt( 'antibot_form_ids', array_values( array_unique( $aIds ) ) );
|
43 |
-
|
44 |
-
$this->cleanLoginUrlPath();
|
45 |
-
$this->ensureCorrectCaptchaConfig();
|
46 |
-
}
|
47 |
-
|
48 |
-
public function ensureCorrectCaptchaConfig() {
|
49 |
-
/** @var LoginGuard\Options $oOpts */
|
50 |
-
$oOpts = $this->getOptions();
|
51 |
-
|
52 |
-
$sStyle = $oOpts->getOpt( 'enable_google_recaptcha_login' );
|
53 |
-
if ( $this->isPremium() ) {
|
54 |
-
$oCfg = $this->getCaptchaCfg();
|
55 |
-
if ( $oCfg->provider == $oCfg::PROV_GOOGLE_RECAP2 ) {
|
56 |
-
if ( !$oCfg->invisible && $sStyle == 'invisible' ) {
|
57 |
-
$oOpts->setOpt( 'enable_google_recaptcha_login', 'default' );
|
58 |
-
}
|
59 |
-
}
|
60 |
-
}
|
61 |
-
elseif ( !in_array( $sStyle, [ 'disabled', 'default' ] ) ) {
|
62 |
-
$oOpts->setOpt( 'enable_google_recaptcha_login', 'default' );
|
63 |
-
}
|
64 |
-
}
|
65 |
-
|
66 |
-
protected function handleModAction( string $sAction ) {
|
67 |
-
switch ( $sAction ) {
|
68 |
-
case 'email_send_verify':
|
69 |
-
$this->processEmailSendVerify();
|
70 |
-
break;
|
71 |
-
default:
|
72 |
-
break;
|
73 |
-
}
|
74 |
-
}
|
75 |
-
|
76 |
-
/**
|
77 |
-
* @uses wp_redirect()
|
78 |
-
*/
|
79 |
-
private function processEmailSendVerify() {
|
80 |
-
/** @var LoginGuard\Options $opts */
|
81 |
-
$opts = $this->getOptions();
|
82 |
-
$this->setIfCanSendEmail( true );
|
83 |
-
$this->saveModOptions();
|
84 |
-
|
85 |
-
if ( $opts->getIfCanSendEmailVerified() ) {
|
86 |
-
$bSuccess = true;
|
87 |
-
$sMessage = __( 'Email verification completed successfully.', 'wp-simple-firewall' );
|
88 |
-
}
|
89 |
-
else {
|
90 |
-
$bSuccess = false;
|
91 |
-
$sMessage = __( 'Email verification could not be completed.', 'wp-simple-firewall' );
|
92 |
-
}
|
93 |
-
$this->setFlashAdminNotice( $sMessage, !$bSuccess );
|
94 |
-
if ( Services::WpUsers()->isUserLoggedIn() ) {
|
95 |
-
Services::Response()->redirect( $this->getUrl_AdminPage() );
|
96 |
-
}
|
97 |
-
}
|
98 |
-
|
99 |
-
/**
|
100 |
-
* @param string $sEmail
|
101 |
-
* @param bool $bSendAsLink
|
102 |
-
* @return bool
|
103 |
-
*/
|
104 |
-
public function sendEmailVerifyCanSend( $sEmail = null, $bSendAsLink = true ) {
|
105 |
-
|
106 |
-
if ( !Services::Data()->validEmail( $sEmail ) ) {
|
107 |
-
$sEmail = get_bloginfo( 'admin_email' );
|
108 |
-
}
|
109 |
-
|
110 |
-
$aMessage = [
|
111 |
-
__( 'Before enabling 2-factor email authentication for your WordPress site, you must verify you can receive this email.', 'wp-simple-firewall' ),
|
112 |
-
__( 'This verifies your website can send email and that your account can receive emails sent from your site.', 'wp-simple-firewall' ),
|
113 |
-
''
|
114 |
-
];
|
115 |
-
|
116 |
-
if ( $bSendAsLink ) {
|
117 |
-
$aMessage[] = sprintf(
|
118 |
-
__( 'Click the verify link: %s', 'wp-simple-firewall' ),
|
119 |
-
add_query_arg( $this->getModActionParams( 'email_send_verify' ), Services::WpGeneral()->getHomeUrl() )
|
120 |
-
);
|
121 |
-
}
|
122 |
-
else {
|
123 |
-
$aMessage[] = sprintf( __( "Here's your code for the guided wizard: %s", 'wp-simple-firewall' ), $this->getCanEmailVerifyCode() );
|
124 |
-
}
|
125 |
-
|
126 |
-
$sEmailSubject = __( 'Email Sending Verification', 'wp-simple-firewall' );
|
127 |
-
return $this->getEmailProcessor()
|
128 |
-
->sendEmailWithWrap( $sEmail, $sEmailSubject, $aMessage );
|
129 |
-
}
|
130 |
-
|
131 |
-
private function cleanLoginUrlPath() {
|
132 |
-
/** @var LoginGuard\Options $opts */
|
133 |
-
$opts = $this->getOptions();
|
134 |
-
$path = $opts->getCustomLoginPath();
|
135 |
-
if ( !empty( $path ) ) {
|
136 |
-
$path = preg_replace( '#[^0-9a-zA-Z-]#', '', trim( $path, '/' ) );
|
137 |
-
$this->getOptions()->setOpt( 'rename_wplogin_path', $path );
|
138 |
-
}
|
139 |
-
}
|
140 |
-
|
141 |
-
/**
|
142 |
-
* @param bool $bAsOptDefaults
|
143 |
-
* @return array
|
144 |
-
*/
|
145 |
-
public function getOptEmailTwoFactorRolesDefaults( $bAsOptDefaults = true ) {
|
146 |
-
$aTwoAuthRoles = [
|
147 |
-
'type' => 'multiple_select',
|
148 |
-
0 => __( 'Subscribers', 'wp-simple-firewall' ),
|
149 |
-
1 => __( 'Contributors', 'wp-simple-firewall' ),
|
150 |
-
2 => __( 'Authors', 'wp-simple-firewall' ),
|
151 |
-
3 => __( 'Editors', 'wp-simple-firewall' ),
|
152 |
-
8 => __( 'Administrators', 'wp-simple-firewall' )
|
153 |
-
];
|
154 |
-
if ( $bAsOptDefaults ) {
|
155 |
-
unset( $aTwoAuthRoles[ 'type' ] );
|
156 |
-
unset( $aTwoAuthRoles[ 0 ] );
|
157 |
-
return array_keys( $aTwoAuthRoles );
|
158 |
-
}
|
159 |
-
return $aTwoAuthRoles;
|
160 |
-
}
|
161 |
-
|
162 |
-
public function getGaspKey() :string {
|
163 |
-
/** @var LoginGuard\Options $opts */
|
164 |
-
$opts = $this->getOptions();
|
165 |
-
$sKey = $opts->getOpt( 'gasp_key' );
|
166 |
-
if ( empty( $sKey ) ) {
|
167 |
-
$sKey = uniqid();
|
168 |
-
$opts->setOpt( 'gasp_key', $sKey );
|
169 |
-
}
|
170 |
-
return $this->prefix( $sKey );
|
171 |
-
}
|
172 |
-
|
173 |
-
/**
|
174 |
-
* @return string
|
175 |
-
*/
|
176 |
-
public function getTextImAHuman() {
|
177 |
-
return stripslashes( $this->getTextOpt( 'text_imahuman' ) );
|
178 |
-
}
|
179 |
-
|
180 |
-
/**
|
181 |
-
* @return string
|
182 |
-
*/
|
183 |
-
public function getTextPleaseCheckBox() {
|
184 |
-
return stripslashes( $this->getTextOpt( 'text_pleasecheckbox' ) );
|
185 |
-
}
|
186 |
-
|
187 |
-
/**
|
188 |
-
* @return string
|
189 |
-
*/
|
190 |
-
public function getCanEmailVerifyCode() {
|
191 |
-
return strtoupper( substr( $this->getCon()->getSiteInstallationId(), 10, 6 ) );
|
192 |
-
}
|
193 |
-
|
194 |
-
public function isEnabledCaptcha() :bool {
|
195 |
-
return !$this->getOptions()->isOpt( 'enable_google_recaptcha_login', 'disabled' )
|
196 |
-
&& $this->getCaptchaCfg()->ready;
|
197 |
-
}
|
198 |
-
|
199 |
-
/**
|
200 |
-
* @return Shield\Modules\Plugin\Lib\Captcha\CaptchaConfigVO
|
201 |
-
*/
|
202 |
-
public function getCaptchaCfg() {
|
203 |
-
$oCfg = parent::getCaptchaCfg();
|
204 |
-
$sStyle = $this->getOptions()->getOpt( 'enable_google_recaptcha_login' );
|
205 |
-
if ( $sStyle !== 'default' && $this->isPremium() ) {
|
206 |
-
$oCfg->theme = $sStyle;
|
207 |
-
$oCfg->invisible = $oCfg->theme == 'invisible';
|
208 |
-
}
|
209 |
-
return $oCfg;
|
210 |
-
}
|
211 |
-
|
212 |
-
/**
|
213 |
-
* @return LoginGuard\Lib\TwoFactor\MfaController
|
214 |
-
*/
|
215 |
-
public function getLoginIntentController() {
|
216 |
-
if ( !isset( $this->oLoginIntentController ) ) {
|
217 |
-
$this->oLoginIntentController = ( new LoginGuard\Lib\TwoFactor\MfaController() )
|
218 |
-
->setMod( $this );
|
219 |
-
}
|
220 |
-
return $this->oLoginIntentController;
|
221 |
-
}
|
222 |
-
|
223 |
-
public function setIsChainedAuth( bool $isChained ) {
|
224 |
-
$this->getOptions()->setOpt( 'enable_chained_authentication', $isChained ? 'Y' : 'N' );
|
225 |
-
}
|
226 |
-
|
227 |
-
/**
|
228 |
-
* @param bool $bCan
|
229 |
-
* @return $this
|
230 |
-
*/
|
231 |
-
public function setIfCanSendEmail( $bCan ) {
|
232 |
-
$this->getOptions()->setOpt( 'email_can_send_verified_at', $bCan ? Services::Request()->ts() : 0 );
|
233 |
-
return $this;
|
234 |
-
}
|
235 |
-
|
236 |
-
public function setEnabled2FaEmail( bool $enable ) {
|
237 |
-
$this->getOptions()->setOpt( 'enable_email_authentication', $enable ? 'Y' : 'N' );
|
238 |
-
}
|
239 |
-
|
240 |
-
public function setEnabled2FaGoogleAuthenticator( bool $enable ) {
|
241 |
-
$this->getOptions()->setOpt( 'enable_google_authenticator', $enable ? 'Y' : 'N' );
|
242 |
-
}
|
243 |
-
|
244 |
-
/**
|
245 |
-
* @return string
|
246 |
-
*/
|
247 |
-
public function getLoginIntentRequestFlag() {
|
248 |
-
return $this->getCon()->prefix( 'login-intent-request' );
|
249 |
-
}
|
250 |
-
|
251 |
-
/**
|
252 |
-
* @param string $sOptKey
|
253 |
-
* @return string
|
254 |
-
*/
|
255 |
-
public function getTextOptDefault( $sOptKey ) {
|
256 |
-
|
257 |
-
switch ( $sOptKey ) {
|
258 |
-
case 'text_imahuman':
|
259 |
-
$sText = __( "I'm a human.", 'wp-simple-firewall' );
|
260 |
-
break;
|
261 |
-
|
262 |
-
case 'text_pleasecheckbox':
|
263 |
-
$sText = __( "Please check the box to show us you're a human.", 'wp-simple-firewall' );
|
264 |
-
break;
|
265 |
-
|
266 |
-
default:
|
267 |
-
$sText = parent::getTextOptDefault( $sOptKey );
|
268 |
-
break;
|
269 |
-
}
|
270 |
-
return $sText;
|
271 |
-
}
|
272 |
-
|
273 |
-
public function setEnabledGaspCheck( bool $enable ) {
|
274 |
-
$this->getOptions()->setOpt( 'enable_login_gasp_check', $enable ? 'Y' : 'N' );
|
275 |
-
}
|
276 |
-
|
277 |
-
public function insertCustomJsVars_Admin() {
|
278 |
-
parent::insertCustomJsVars_Admin();
|
279 |
-
|
280 |
-
wp_localize_script(
|
281 |
-
$this->getCon()->prefix( 'global-plugin' ),
|
282 |
-
'icwp_wpsf_vars_lg',
|
283 |
-
[
|
284 |
-
'ajax_gen_backup_codes' => $this->getAjaxActionData( 'gen_backup_codes' ),
|
285 |
-
'ajax_del_backup_codes' => $this->getAjaxActionData( 'del_backup_codes' ),
|
286 |
-
]
|
287 |
-
);
|
288 |
-
wp_enqueue_script( 'jquery-ui-dialog' );
|
289 |
-
wp_enqueue_style( 'wp-jquery-ui-dialog' );
|
290 |
-
}
|
291 |
-
|
292 |
-
protected function getNamespaceBase() :string {
|
293 |
-
return 'LoginGuard';
|
294 |
-
}
|
295 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
src/features/plugin.php
DELETED
@@ -1,553 +0,0 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
use FernleafSystems\Wordpress\Plugin\Shield;
|
4 |
-
use FernleafSystems\Wordpress\Plugin\Shield\Modules\Plugin;
|
5 |
-
use FernleafSystems\Wordpress\Services\Services;
|
6 |
-
use FernleafSystems\Wordpress\Services\Utilities;
|
7 |
-
|
8 |
-
/**
|
9 |
-
* @deprecated 10.1
|
10 |
-
*/
|
11 |
-
class ICWP_WPSF_FeatureHandler_Plugin extends ICWP_WPSF_FeatureHandler_BaseWpsf {
|
12 |
-
|
13 |
-
/**
|
14 |
-
* @var Plugin\Lib\ImportExport\ImportExportController
|
15 |
-
*/
|
16 |
-
private $oImportExportController;
|
17 |
-
|
18 |
-
/**
|
19 |
-
* @var Plugin\Components\PluginBadge
|
20 |
-
*/
|
21 |
-
private $oPluginBadgeController;
|
22 |
-
|
23 |
-
/**
|
24 |
-
* @var Shield\Utilities\ReCaptcha\Enqueue
|
25 |
-
*/
|
26 |
-
private $oCaptchaEnqueue;
|
27 |
-
|
28 |
-
/**
|
29 |
-
* @var Shield\ShieldNetApi\ShieldNetApiController
|
30 |
-
*/
|
31 |
-
private $oShieldNetApiController;
|
32 |
-
|
33 |
-
public function getImpExpController() :Plugin\Lib\ImportExport\ImportExportController {
|
34 |
-
if ( !isset( $this->oImportExportController ) ) {
|
35 |
-
$this->oImportExportController = ( new Plugin\Lib\ImportExport\ImportExportController() )
|
36 |
-
->setMod( $this );
|
37 |
-
}
|
38 |
-
return $this->oImportExportController;
|
39 |
-
}
|
40 |
-
|
41 |
-
public function getPluginBadgeCon() :Plugin\Components\PluginBadge {
|
42 |
-
if ( !isset( $this->oPluginBadgeController ) ) {
|
43 |
-
$this->oPluginBadgeController = ( new Plugin\Components\PluginBadge() )
|
44 |
-
->setMod( $this );
|
45 |
-
}
|
46 |
-
return $this->oPluginBadgeController;
|
47 |
-
}
|
48 |
-
|
49 |
-
public function getShieldNetApiController() :Shield\ShieldNetApi\ShieldNetApiController {
|
50 |
-
if ( !isset( $this->oShieldNetApiController ) ) {
|
51 |
-
$this->oShieldNetApiController = ( new Shield\ShieldNetApi\ShieldNetApiController() )
|
52 |
-
->setMod( $this );
|
53 |
-
}
|
54 |
-
return $this->oShieldNetApiController;
|
55 |
-
}
|
56 |
-
|
57 |
-
protected function doPostConstruction() {
|
58 |
-
$this->setVisitorIpSource();
|
59 |
-
}
|
60 |
-
|
61 |
-
protected function preProcessOptions() {
|
62 |
-
( new Plugin\Lib\Captcha\CheckCaptchaSettings() )
|
63 |
-
->setMod( $this )
|
64 |
-
->checkAll();
|
65 |
-
}
|
66 |
-
|
67 |
-
public function deleteAllPluginCrons() {
|
68 |
-
$con = $this->getCon();
|
69 |
-
$oWpCron = Services::WpCron();
|
70 |
-
|
71 |
-
foreach ( $oWpCron->getCrons() as $nKey => $aCronArgs ) {
|
72 |
-
foreach ( $aCronArgs as $sHook => $aCron ) {
|
73 |
-
if ( strpos( $sHook, $con->prefix() ) === 0
|
74 |
-
|| strpos( $sHook, $con->prefixOption() ) === 0 ) {
|
75 |
-
$oWpCron->deleteCronJob( $sHook );
|
76 |
-
}
|
77 |
-
}
|
78 |
-
}
|
79 |
-
}
|
80 |
-
|
81 |
-
/**
|
82 |
-
* Hooked to the plugin's main plugin_shutdown action
|
83 |
-
*/
|
84 |
-
public function onPluginShutdown() {
|
85 |
-
$preferred = Services::IP()->getIpDetector()->getLastSuccessfulSource();
|
86 |
-
if ( !empty( $preferred ) ) {
|
87 |
-
$this->getOptions()->setOpt( 'last_ip_detect_source', $preferred );
|
88 |
-
}
|
89 |
-
parent::onPluginShutdown();
|
90 |
-
}
|
91 |
-
|
92 |
-
public function onWpInit() {
|
93 |
-
parent::onWpInit();
|
94 |
-
$this->getImportExportSecretKey();
|
95 |
-
}
|
96 |
-
|
97 |
-
/**
|
98 |
-
* Forcefully sets preferred Visitor IP source in the Data component for use throughout the plugin
|
99 |
-
*/
|
100 |
-
private function setVisitorIpSource() {
|
101 |
-
/** @var Plugin\Options $opts */
|
102 |
-
$opts = $this->getOptions();
|
103 |
-
if ( !$opts->isIpSourceAutoDetect() ) {
|
104 |
-
Services::IP()->setIpDetector(
|
105 |
-
( new Utilities\Net\VisitorIpDetection() )->setPreferredSource( $opts->getIpSource() )
|
106 |
-
);
|
107 |
-
}
|
108 |
-
}
|
109 |
-
|
110 |
-
protected function handleModAction( string $sAction ) {
|
111 |
-
switch ( $sAction ) {
|
112 |
-
|
113 |
-
case 'export_file_download':
|
114 |
-
header( 'Set-Cookie: fileDownload=true; path=/' );
|
115 |
-
( new Plugin\Lib\ImportExport\Export() )
|
116 |
-
->setMod( $this )
|
117 |
-
->toFile();
|
118 |
-
break;
|
119 |
-
|
120 |
-
case 'import_file_upload':
|
121 |
-
try {
|
122 |
-
( new Plugin\Lib\ImportExport\Import() )
|
123 |
-
->setMod( $this )
|
124 |
-
->fromFileUpload();
|
125 |
-
$bSuccess = true;
|
126 |
-
$sMessage = __( 'Options imported successfully', 'wp-simple-firewall' );
|
127 |
-
}
|
128 |
-
catch ( \Exception $oE ) {
|
129 |
-
$bSuccess = false;
|
130 |
-
$sMessage = $oE->getMessage();
|
131 |
-
}
|
132 |
-
$this->setFlashAdminNotice( $sMessage, !$bSuccess );
|
133 |
-
Services::Response()->redirect(
|
134 |
-
$this->getCon()->getModule_Insights()->getUrl_SubInsightsPage( 'importexport' )
|
135 |
-
);
|
136 |
-
break;
|
137 |
-
|
138 |
-
default:
|
139 |
-
break;
|
140 |
-
}
|
141 |
-
}
|
142 |
-
|
143 |
-
public function getCanSiteCallToItself() :bool {
|
144 |
-
$oHttp = Services::HttpRequest();
|
145 |
-
return $oHttp->get( Services::WpGeneral()->getHomeUrl(), [ 'timeout' => 20 ] )
|
146 |
-
&& $oHttp->lastResponse->getCode() < 400;
|
147 |
-
}
|
148 |
-
|
149 |
-
public function getActivePluginFeatures() :array {
|
150 |
-
$aActiveFeatures = $this->getDef( 'active_plugin_features' );
|
151 |
-
|
152 |
-
$aPluginFeatures = [];
|
153 |
-
if ( !empty( $aActiveFeatures ) && is_array( $aActiveFeatures ) ) {
|
154 |
-
|
155 |
-
foreach ( $aActiveFeatures as $nPosition => $aFeature ) {
|
156 |
-
if ( isset( $aFeature[ 'hidden' ] ) && $aFeature[ 'hidden' ] ) {
|
157 |
-
continue;
|
158 |
-
}
|
159 |
-
$aPluginFeatures[ $aFeature[ 'slug' ] ] = $aFeature;
|
160 |
-
}
|
161 |
-
}
|
162 |
-
return $aPluginFeatures;
|
163 |
-
}
|
164 |
-
|
165 |
-
public function getLinkToTrackingDataDump() :string {
|
166 |
-
return add_query_arg(
|
167 |
-
[ 'shield_action' => 'dump_tracking_data' ],
|
168 |
-
Services::WpGeneral()->getAdminUrl()
|
169 |
-
);
|
170 |
-
}
|
171 |
-
|
172 |
-
public function getPluginReportEmail() :string {
|
173 |
-
$e = (string)$this->getOptions()->getOpt( 'block_send_email_address' );
|
174 |
-
if ( $this->isPremium() ) {
|
175 |
-
$e = apply_filters( $this->getCon()->prefix( 'report_email' ), $e );
|
176 |
-
}
|
177 |
-
$e = trim( $e );
|
178 |
-
return Services::Data()->validEmail( $e ) ? $e : Services::WpGeneral()->getSiteAdminEmail();
|
179 |
-
}
|
180 |
-
|
181 |
-
/**
|
182 |
-
* This is the point where you would want to do any options verification
|
183 |
-
*/
|
184 |
-
protected function doPrePluginOptionsSave() {
|
185 |
-
/** @var Plugin\Options $oOpts */
|
186 |
-
$oOpts = $this->getOptions();
|
187 |
-
|
188 |
-
$this->storeRealInstallDate();
|
189 |
-
|
190 |
-
if ( $oOpts->isTrackingEnabled() && !$oOpts->isTrackingPermissionSet() ) {
|
191 |
-
$oOpts->setOpt( 'tracking_permission_set_at', Services::Request()->ts() );
|
192 |
-
}
|
193 |
-
|
194 |
-
$this->cleanRecaptchaKey( 'google_recaptcha_site_key' );
|
195 |
-
$this->cleanRecaptchaKey( 'google_recaptcha_secret_key' );
|
196 |
-
|
197 |
-
$this->cleanImportExportWhitelistUrls();
|
198 |
-
$this->cleanImportExportMasterImportUrl();
|
199 |
-
|
200 |
-
$this->setPluginInstallationId();
|
201 |
-
}
|
202 |
-
|
203 |
-
public function getFirstInstallDate() :int {
|
204 |
-
return (int)Services::WpGeneral()->getOption( $this->getCon()->prefixOption( 'install_date' ) );
|
205 |
-
}
|
206 |
-
|
207 |
-
public function getInstallDate() :int {
|
208 |
-
return (int)$this->getOptions()->getOpt( 'installation_time', 0 );
|
209 |
-
}
|
210 |
-
|
211 |
-
public function isShowAdvanced() :bool {
|
212 |
-
return $this->getOptions()->isOpt( 'show_advanced', 'Y' );
|
213 |
-
}
|
214 |
-
|
215 |
-
/**
|
216 |
-
* @return string
|
217 |
-
*/
|
218 |
-
public function getOpenSslPrivateKey() {
|
219 |
-
$opts = $this->getOptions();
|
220 |
-
$key = null;
|
221 |
-
$oEnc = Services::Encrypt();
|
222 |
-
if ( $oEnc->isSupportedOpenSslDataEncryption() ) {
|
223 |
-
$key = $opts->getOpt( 'openssl_private_key' );
|
224 |
-
if ( empty( $key ) ) {
|
225 |
-
try {
|
226 |
-
$aKeys = $oEnc->createNewPrivatePublicKeyPair();
|
227 |
-
if ( !empty( $aKeys[ 'private' ] ) ) {
|
228 |
-
$key = $aKeys[ 'private' ];
|
229 |
-
$opts->setOpt( 'openssl_private_key', base64_encode( $key ) );
|
230 |
-
$this->saveModOptions();
|
231 |
-
}
|
232 |
-
}
|
233 |
-
catch ( \Exception $e ) {
|
234 |
-
}
|
235 |
-
}
|
236 |
-
else {
|
237 |
-
$key = base64_decode( $key );
|
238 |
-
}
|
239 |
-
}
|
240 |
-
return $key;
|
241 |
-
}
|
242 |
-
|
243 |
-
/**
|
244 |
-
* @return string|null
|
245 |
-
*/
|
246 |
-
public function getOpenSslPublicKey() {
|
247 |
-
$sKey = null;
|
248 |
-
if ( $this->hasOpenSslPrivateKey() ) {
|
249 |
-
try {
|
250 |
-
$sKey = Services::Encrypt()->getPublicKeyFromPrivateKey( $this->getOpenSslPrivateKey() );
|
251 |
-
}
|
252 |
-
catch ( \Exception $e ) {
|
253 |
-
}
|
254 |
-
}
|
255 |
-
return $sKey;
|
256 |
-
}
|
257 |
-
|
258 |
-
public function hasOpenSslPrivateKey() :bool {
|
259 |
-
return !empty( $this->getOpenSslPrivateKey() );
|
260 |
-
}
|
261 |
-
|
262 |
-
/**
|
263 |
-
* @return int - the real install timestamp
|
264 |
-
*/
|
265 |
-
public function storeRealInstallDate() {
|
266 |
-
$oWP = Services::WpGeneral();
|
267 |
-
$nNow = Services::Request()->ts();
|
268 |
-
|
269 |
-
$sOptKey = $this->getCon()->prefixOption( 'install_date' );
|
270 |
-
|
271 |
-
$nWpDate = $oWP->getOption( $sOptKey );
|
272 |
-
if ( empty( $nWpDate ) ) {
|
273 |
-
$nWpDate = $nNow;
|
274 |
-
}
|
275 |
-
|
276 |
-
$nPluginDate = $this->getInstallDate();
|
277 |
-
if ( $nPluginDate == 0 ) {
|
278 |
-
$nPluginDate = $nNow;
|
279 |
-
}
|
280 |
-
|
281 |
-
$nFinal = min( $nPluginDate, $nWpDate );
|
282 |
-
$oWP->updateOption( $sOptKey, $nFinal );
|
283 |
-
$this->getOptions()->setOpt( 'installation_time', $nPluginDate );
|
284 |
-
|
285 |
-
return $nFinal;
|
286 |
-
}
|
287 |
-
|
288 |
-
/**
|
289 |
-
* @param string $optionKey
|
290 |
-
*/
|
291 |
-
protected function cleanRecaptchaKey( $optionKey ) {
|
292 |
-
$opts = $this->getOptions();
|
293 |
-
$sCaptchaKey = trim( (string)$opts->getOpt( $optionKey, '' ) );
|
294 |
-
$nSpacePos = strpos( $sCaptchaKey, ' ' );
|
295 |
-
if ( $nSpacePos !== false ) {
|
296 |
-
$sCaptchaKey = substr( $sCaptchaKey, 0, $nSpacePos + 1 ); // cut off the string if there's spaces
|
297 |
-
}
|
298 |
-
$sCaptchaKey = preg_replace( '#[^0-9a-zA-Z_-]#', '', $sCaptchaKey ); // restrict character set
|
299 |
-
// if ( strlen( $sCaptchaKey ) != 40 ) {
|
300 |
-
// $sCaptchaKey = ''; // need to verify length is 40.
|
301 |
-
// }
|
302 |
-
$opts->setOpt( $optionKey, $sCaptchaKey );
|
303 |
-
}
|
304 |
-
|
305 |
-
/**
|
306 |
-
* Ensure we always a valid installation ID.
|
307 |
-
*
|
308 |
-
* @return string
|
309 |
-
* @deprecated but still used because it aligns with stats collection
|
310 |
-
*/
|
311 |
-
public function getPluginInstallationId() {
|
312 |
-
$ID = $this->getOptions()->getOpt( 'unique_installation_id', '' );
|
313 |
-
|
314 |
-
if ( !$this->isValidInstallId( $ID ) ) {
|
315 |
-
$ID = $this->setPluginInstallationId();
|
316 |
-
}
|
317 |
-
return $ID;
|
318 |
-
}
|
319 |
-
|
320 |
-
public function getActivateLength() :int {
|
321 |
-
return Services::Request()->ts() - (int)$this->getOptions()->getOpt( 'activated_at', 0 );
|
322 |
-
}
|
323 |
-
|
324 |
-
/**
|
325 |
-
* hidden 20200121
|
326 |
-
* @return bool
|
327 |
-
*/
|
328 |
-
public function getIfShowIntroVideo() {
|
329 |
-
return false && ( $this->getActivateLength() < 8 )
|
330 |
-
&& ( Services::Request()->ts() - $this->getInstallDate() < 15 );
|
331 |
-
}
|
332 |
-
|
333 |
-
public function getTourManager() :Plugin\Lib\TourManager {
|
334 |
-
return ( new Plugin\Lib\TourManager() )->setMod( $this );
|
335 |
-
}
|
336 |
-
|
337 |
-
public function setActivatedAt() {
|
338 |
-
$this->getOptions()->setOpt( 'activated_at', Services::Request()->ts() );
|
339 |
-
}
|
340 |
-
|
341 |
-
/**
|
342 |
-
* @param string $newID - leave empty to reset if the current isn't valid
|
343 |
-
* @return string
|
344 |
-
*/
|
345 |
-
protected function setPluginInstallationId( $newID = null ) {
|
346 |
-
// only reset if it's not of the correct type
|
347 |
-
if ( !$this->isValidInstallId( $newID ) ) {
|
348 |
-
$newID = $this->genInstallId();
|
349 |
-
}
|
350 |
-
$this->getOptions()->setOpt( 'unique_installation_id', $newID );
|
351 |
-
return $newID;
|
352 |
-
}
|
353 |
-
|
354 |
-
protected function genInstallId() :string {
|
355 |
-
return sha1(
|
356 |
-
$this->getInstallDate()
|
357 |
-
.Services::WpGeneral()->getWpUrl()
|
358 |
-
.Services::WpDb()->getPrefix()
|
359 |
-
);
|
360 |
-
}
|
361 |
-
|
362 |
-
public function hasImportExportWhitelistSites() :bool {
|
363 |
-
return count( $this->getImportExportWhitelist() ) > 0;
|
364 |
-
}
|
365 |
-
|
366 |
-
/**
|
367 |
-
* @return string[]
|
368 |
-
*/
|
369 |
-
public function getImportExportWhitelist() :array {
|
370 |
-
$list = $this->getOptions()->getOpt( 'importexport_whitelist', [] );
|
371 |
-
return is_array( $list ) ? $list : [];
|
372 |
-
}
|
373 |
-
|
374 |
-
/**
|
375 |
-
* @return string
|
376 |
-
*/
|
377 |
-
protected function getImportExportSecretKey() {
|
378 |
-
$opts = $this->getOptions();
|
379 |
-
$ID = $opts->getOpt( 'importexport_secretkey', '' );
|
380 |
-
if ( empty( $ID ) || $this->isImportExportSecretKeyExpired() ) {
|
381 |
-
$ID = sha1( $this->getCon()->getSiteInstallationId().wp_rand( 0, PHP_INT_MAX ) );
|
382 |
-
$opts->setOpt( 'importexport_secretkey', $ID )
|
383 |
-
->setOpt( 'importexport_secretkey_expires_at', Services::Request()->ts() + HOUR_IN_SECONDS );
|
384 |
-
}
|
385 |
-
return $ID;
|
386 |
-
}
|
387 |
-
|
388 |
-
protected function isImportExportSecretKeyExpired() :bool {
|
389 |
-
return Services::Request()->ts() >
|
390 |
-
$this->getOptions()->getOpt( 'importexport_secretkey_expires_at' );
|
391 |
-
}
|
392 |
-
|
393 |
-
public function isImportExportWhitelistNotify() :bool {
|
394 |
-
return $this->getOptions()->isOpt( 'importexport_whitelist_notify', 'Y' );
|
395 |
-
}
|
396 |
-
|
397 |
-
/**
|
398 |
-
* @param string $sUrl
|
399 |
-
* @return $this
|
400 |
-
*/
|
401 |
-
public function addUrlToImportExportWhitelistUrls( $sUrl ) {
|
402 |
-
$sUrl = Services::Data()->validateSimpleHttpUrl( $sUrl );
|
403 |
-
if ( $sUrl !== false ) {
|
404 |
-
$aWhitelistUrls = $this->getImportExportWhitelist();
|
405 |
-
$aWhitelistUrls[] = $sUrl;
|
406 |
-
$this->getOptions()->setOpt( 'importexport_whitelist', $aWhitelistUrls );
|
407 |
-
$this->saveModOptions();
|
408 |
-
}
|
409 |
-
return $this;
|
410 |
-
}
|
411 |
-
|
412 |
-
/**
|
413 |
-
* @param string $url
|
414 |
-
* @return $this
|
415 |
-
*/
|
416 |
-
public function removeUrlFromImportExportWhitelistUrls( $url ) {
|
417 |
-
$url = Services::Data()->validateSimpleHttpUrl( $url );
|
418 |
-
if ( $url !== false ) {
|
419 |
-
$aWhitelistUrls = $this->getImportExportWhitelist();
|
420 |
-
$sKey = array_search( $url, $aWhitelistUrls );
|
421 |
-
if ( $sKey !== false ) {
|
422 |
-
unset( $aWhitelistUrls[ $sKey ] );
|
423 |
-
}
|
424 |
-
$this->getOptions()->setOpt( 'importexport_whitelist', $aWhitelistUrls );
|
425 |
-
$this->saveModOptions();
|
426 |
-
}
|
427 |
-
return $this;
|
428 |
-
}
|
429 |
-
|
430 |
-
/**
|
431 |
-
* @param string $sKey
|
432 |
-
* @return bool
|
433 |
-
*/
|
434 |
-
public function isImportExportSecretKey( $sKey ) :bool {
|
435 |
-
return !empty( $sKey ) && $this->getImportExportSecretKey() == $sKey;
|
436 |
-
}
|
437 |
-
|
438 |
-
protected function cleanImportExportWhitelistUrls() {
|
439 |
-
$oDP = Services::Data();
|
440 |
-
|
441 |
-
$aCleaned = [];
|
442 |
-
$aWhitelistUrls = $this->getImportExportWhitelist();
|
443 |
-
foreach ( $aWhitelistUrls as $nKey => $sUrl ) {
|
444 |
-
|
445 |
-
$sUrl = $oDP->validateSimpleHttpUrl( $sUrl );
|
446 |
-
if ( $sUrl !== false ) {
|
447 |
-
$aCleaned[] = $sUrl;
|
448 |
-
}
|
449 |
-
}
|
450 |
-
$this->getOptions()->setOpt( 'importexport_whitelist', array_unique( $aCleaned ) );
|
451 |
-
}
|
452 |
-
|
453 |
-
protected function cleanImportExportMasterImportUrl() {
|
454 |
-
/** @var Plugin\Options $oOpts */
|
455 |
-
$oOpts = $this->getOptions();
|
456 |
-
$url = Services::Data()->validateSimpleHttpUrl( $oOpts->getImportExportMasterImportUrl() );
|
457 |
-
if ( $url === false ) {
|
458 |
-
$url = '';
|
459 |
-
}
|
460 |
-
$this->getOptions()->setOpt( 'importexport_masterurl', $url );
|
461 |
-
}
|
462 |
-
|
463 |
-
/**
|
464 |
-
* @param string $url
|
465 |
-
* @return $this
|
466 |
-
*/
|
467 |
-
public function setImportExportMasterImportUrl( $url ) {
|
468 |
-
$this->getOptions()->setOpt( 'importexport_masterurl', $url ); //saving will clean the URL
|
469 |
-
return $this->saveModOptions();
|
470 |
-
}
|
471 |
-
|
472 |
-
/**
|
473 |
-
* @param string $sId
|
474 |
-
* @return bool
|
475 |
-
*/
|
476 |
-
protected function isValidInstallId( $sId ) {
|
477 |
-
return !empty( $sId ) && is_string( $sId ) && strlen( $sId ) == 40;
|
478 |
-
}
|
479 |
-
|
480 |
-
public function isXmlrpcBypass() :bool {
|
481 |
-
return $this->getOptions()->isOpt( 'enable_xmlrpc_compatibility', 'Y' );
|
482 |
-
}
|
483 |
-
|
484 |
-
public function getCanAdminNotes() :bool {
|
485 |
-
return Services::WpUsers()->isUserAdmin();
|
486 |
-
}
|
487 |
-
|
488 |
-
public function insertCustomJsVars_Admin() {
|
489 |
-
parent::insertCustomJsVars_Admin();
|
490 |
-
|
491 |
-
$con = $this->getCon();
|
492 |
-
if ( Services::WpPost()->isCurrentPage( 'plugins.php' ) ) {
|
493 |
-
$sFile = $con->getPluginBaseFile();
|
494 |
-
wp_localize_script(
|
495 |
-
$con->prefix( 'global-plugin' ),
|
496 |
-
'icwp_wpsf_vars_plugin',
|
497 |
-
[
|
498 |
-
'file' => $sFile,
|
499 |
-
'ajax' => [
|
500 |
-
'send_deactivate_survey' => $this->getAjaxActionData( 'send_deactivate_survey' ),
|
501 |
-
],
|
502 |
-
'hrefs' => [
|
503 |
-
'deactivate' => Services::WpPlugins()->getUrl_Deactivate( $sFile ),
|
504 |
-
],
|
505 |
-
]
|
506 |
-
);
|
507 |
-
wp_enqueue_script( 'jquery-ui-dialog' ); // jquery and jquery-ui should be dependencies, didn't check though...
|
508 |
-
wp_enqueue_style( 'wp-jquery-ui-dialog' );
|
509 |
-
}
|
510 |
-
|
511 |
-
wp_localize_script(
|
512 |
-
$con->prefix( 'plugin' ),
|
513 |
-
'icwp_wpsf_vars_tourmanager',
|
514 |
-
[ 'ajax' => $this->getAjaxActionData( 'mark_tour_finished' ) ]
|
515 |
-
);
|
516 |
-
wp_localize_script(
|
517 |
-
$con->prefix( 'plugin' ),
|
518 |
-
'icwp_wpsf_vars_plugin',
|
519 |
-
[
|
520 |
-
'strings' => [
|
521 |
-
'downloading_file' => __( 'Downloading file, please wait...', 'wp-simple-firewall' ),
|
522 |
-
'problem_downloading_file' => __( 'There was a problem downloading the file.', 'wp-simple-firewall' ),
|
523 |
-
],
|
524 |
-
]
|
525 |
-
);
|
526 |
-
}
|
527 |
-
|
528 |
-
public function getDbHandler_GeoIp() :Shield\Databases\GeoIp\Handler {
|
529 |
-
return $this->getDbH( 'geoip' );
|
530 |
-
}
|
531 |
-
|
532 |
-
public function getDbHandler_Notes() :Shield\Databases\AdminNotes\Handler {
|
533 |
-
return $this->getDbH( 'notes' );
|
534 |
-
}
|
535 |
-
|
536 |
-
public function getCaptchaEnqueue() :Shield\Utilities\ReCaptcha\Enqueue {
|
537 |
-
if ( !isset( $this->oCaptchaEnqueue ) ) {
|
538 |
-
$this->oCaptchaEnqueue = ( new Shield\Utilities\ReCaptcha\Enqueue() )->setMod( $this );
|
539 |
-
}
|
540 |
-
return $this->oCaptchaEnqueue;
|
541 |
-
}
|
542 |
-
|
543 |
-
protected function getNamespaceBase() :string {
|
544 |
-
return 'Plugin';
|
545 |
-
}
|
546 |
-
|
547 |
-
/**
|
548 |
-
* @return string
|
549 |
-
*/
|
550 |
-
public function getSurveyEmail() {
|
551 |
-
return base64_decode( $this->getDef( 'survey_email' ) );
|
552 |
-
}
|
553 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
src/features/reporting.php
DELETED
@@ -1,46 +0,0 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
use FernleafSystems\Wordpress\Plugin\Shield;
|
4 |
-
use FernleafSystems\Wordpress\Plugin\Shield\Modules\Reporting;
|
5 |
-
|
6 |
-
/**
|
7 |
-
* @deprecated 10.1
|
8 |
-
*/
|
9 |
-
class ICWP_WPSF_FeatureHandler_Reporting extends ICWP_WPSF_FeatureHandler_BaseWpsf {
|
10 |
-
|
11 |
-
/**
|
12 |
-
* @var Reporting\Lib\ReportingController
|
13 |
-
*/
|
14 |
-
private $oReportsController;
|
15 |
-
|
16 |
-
/**
|
17 |
-
* @return Shield\Databases\Reports\Handler
|
18 |
-
*/
|
19 |
-
public function getDbHandler_Reports() {
|
20 |
-
return $this->getDbH( 'reports' );
|
21 |
-
}
|
22 |
-
|
23 |
-
/**
|
24 |
-
* @return string
|
25 |
-
*/
|
26 |
-
protected function getNamespaceBase() :string {
|
27 |
-
return 'Reporting';
|
28 |
-
}
|
29 |
-
|
30 |
-
/**
|
31 |
-
* @return Reporting\Lib\ReportingController
|
32 |
-
*/
|
33 |
-
public function getReportingController() {
|
34 |
-
if ( !isset( $this->oReportsController ) ) {
|
35 |
-
$this->oReportsController = ( new Reporting\Lib\ReportingController() )->setMod( $this );
|
36 |
-
}
|
37 |
-
return $this->oReportsController;
|
38 |
-
}
|
39 |
-
|
40 |
-
/**
|
41 |
-
* @return Reporting\Lib\ReportingController
|
42 |
-
*/
|
43 |
-
public function getProcessor() {
|
44 |
-
return $this->getReportingController();
|
45 |
-
}
|
46 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
src/features/sessions.php
DELETED
@@ -1,48 +0,0 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
use FernleafSystems\Wordpress\Plugin\Shield;
|
4 |
-
use FernleafSystems\Wordpress\Services\Services;
|
5 |
-
|
6 |
-
/**
|
7 |
-
* @deprecated 10.1
|
8 |
-
*/
|
9 |
-
class ICWP_WPSF_FeatureHandler_Sessions extends ICWP_WPSF_FeatureHandler_BaseWpsf {
|
10 |
-
|
11 |
-
/**
|
12 |
-
* @return bool
|
13 |
-
*/
|
14 |
-
public function isAutoAddSessions() {
|
15 |
-
$opts = $this->getOptions();
|
16 |
-
$oReq = Services::Request();
|
17 |
-
$nStartedAt = $opts->getOpt( 'autoadd_sessions_started_at', 0 );
|
18 |
-
if ( $nStartedAt < 1 ) {
|
19 |
-
$nStartedAt = $oReq->ts();
|
20 |
-
$opts->setOpt( 'autoadd_sessions_started_at', $nStartedAt );
|
21 |
-
}
|
22 |
-
return ( $oReq->ts() - $nStartedAt ) < 20;
|
23 |
-
}
|
24 |
-
|
25 |
-
/**
|
26 |
-
* @return false|Shield\Databases\Session\Handler
|
27 |
-
*/
|
28 |
-
public function getDbHandler_Sessions() {
|
29 |
-
return $this->getDbH( 'session' );
|
30 |
-
}
|
31 |
-
|
32 |
-
/**
|
33 |
-
* @return bool
|
34 |
-
* @throws \Exception
|
35 |
-
*/
|
36 |
-
protected function isReadyToExecute() {
|
37 |
-
return ( $this->getDbHandler_Sessions() instanceof Shield\Databases\Session\Handler )
|
38 |
-
&& $this->getDbHandler_Sessions()->isReady()
|
39 |
-
&& parent::isReadyToExecute();
|
40 |
-
}
|
41 |
-
|
42 |
-
/**
|
43 |
-
* @return string
|
44 |
-
*/
|
45 |
-
protected function getNamespaceBase() :string {
|
46 |
-
return 'Sessions';
|
47 |
-
}
|
48 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
src/features/traffic.php
DELETED
@@ -1,51 +0,0 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
use FernleafSystems\Wordpress\Plugin\Shield;
|
4 |
-
use FernleafSystems\Wordpress\Plugin\Shield\Modules\Traffic;
|
5 |
-
use FernleafSystems\Wordpress\Services\Services;
|
6 |
-
|
7 |
-
/**
|
8 |
-
* @deprecated 10.1
|
9 |
-
*/
|
10 |
-
class ICWP_WPSF_FeatureHandler_Traffic extends ICWP_WPSF_FeatureHandler_BaseWpsf {
|
11 |
-
|
12 |
-
/**
|
13 |
-
* @return false|Shield\Databases\Traffic\Handler
|
14 |
-
*/
|
15 |
-
public function getDbHandler_Traffic() {
|
16 |
-
return $this->getDbH( 'traffic' );
|
17 |
-
}
|
18 |
-
|
19 |
-
/**
|
20 |
-
* We clean the database after saving.
|
21 |
-
*/
|
22 |
-
protected function preProcessOptions() {
|
23 |
-
/** @var Traffic\Options $oOpts */
|
24 |
-
$oOpts = $this->getOptions();
|
25 |
-
|
26 |
-
$aExcls = $oOpts->getCustomExclusions();
|
27 |
-
foreach ( $aExcls as &$sExcl ) {
|
28 |
-
$sExcl = trim( esc_js( $sExcl ) );
|
29 |
-
}
|
30 |
-
$oOpts->setOpt( 'custom_exclusions', array_filter( $aExcls ) );
|
31 |
-
}
|
32 |
-
|
33 |
-
/**
|
34 |
-
* @return bool
|
35 |
-
* @throws \Exception
|
36 |
-
*/
|
37 |
-
protected function isReadyToExecute() {
|
38 |
-
$oIp = Services::IP();
|
39 |
-
return $oIp->isValidIp_PublicRange( $oIp->getRequestIp() )
|
40 |
-
&& ( $this->getDbHandler_Traffic() instanceof Shield\Databases\Traffic\Handler )
|
41 |
-
&& $this->getDbHandler_Traffic()->isReady()
|
42 |
-
&& parent::isReadyToExecute();
|
43 |
-
}
|
44 |
-
|
45 |
-
/**
|
46 |
-
* @return string
|
47 |
-
*/
|
48 |
-
protected function getNamespaceBase() :string {
|
49 |
-
return 'Traffic';
|
50 |
-
}
|
51 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
src/features/user_management.php
DELETED
@@ -1,138 +0,0 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
use FernleafSystems\Wordpress\Plugin\Shield;
|
4 |
-
use FernleafSystems\Wordpress\Plugin\Shield\Modules\UserManagement;
|
5 |
-
use FernleafSystems\Wordpress\Services\Services;
|
6 |
-
|
7 |
-
/**
|
8 |
-
* @deprecated 10.1
|
9 |
-
*/
|
10 |
-
class ICWP_WPSF_FeatureHandler_UserManagement extends \ICWP_WPSF_FeatureHandler_BaseWpsf {
|
11 |
-
|
12 |
-
/**
|
13 |
-
* Should have no default email. If no email is set, no notification is sent.
|
14 |
-
* @return string[]
|
15 |
-
*/
|
16 |
-
public function getAdminLoginNotificationEmails() {
|
17 |
-
$aEmails = [];
|
18 |
-
|
19 |
-
$sEmails = $this->getOptions()->getOpt( 'enable_admin_login_email_notification', '' );
|
20 |
-
if ( !empty( $sEmails ) ) {
|
21 |
-
$aEmails = array_values( array_unique( array_filter(
|
22 |
-
array_map(
|
23 |
-
function ( $sEmail ) {
|
24 |
-
return trim( strtolower( $sEmail ) );
|
25 |
-
},
|
26 |
-
explode( ',', $sEmails )
|
27 |
-
),
|
28 |
-
function ( $sEmail ) {
|
29 |
-
return Services::Data()->validEmail( $sEmail );
|
30 |
-
}
|
31 |
-
) ) );
|
32 |
-
if ( !$this->isPremium() && !empty( $aEmails ) ) {
|
33 |
-
$aEmails = array_slice( $aEmails, 0, 1 );
|
34 |
-
}
|
35 |
-
}
|
36 |
-
|
37 |
-
return $aEmails;
|
38 |
-
}
|
39 |
-
|
40 |
-
protected function preProcessOptions() {
|
41 |
-
/** @var UserManagement\Options $opts */
|
42 |
-
$opts = $this->getOptions();
|
43 |
-
|
44 |
-
$opts->setOpt( 'enable_admin_login_email_notification', implode( ', ', $this->getAdminLoginNotificationEmails() ) );
|
45 |
-
|
46 |
-
if ( $opts->getIdleTimeoutInterval() > $opts->getMaxSessionTime() ) {
|
47 |
-
$opts->setOpt( 'session_idle_timeout_interval', $opts->getOpt( 'session_timeout_interval' )*24 );
|
48 |
-
}
|
49 |
-
|
50 |
-
$opts->setOpt( 'auto_idle_roles',
|
51 |
-
array_unique( array_filter( array_map(
|
52 |
-
function ( $sRole ) {
|
53 |
-
return preg_replace( '#[^\sa-z0-9_-]#i', '', trim( strtolower( $sRole ) ) );
|
54 |
-
},
|
55 |
-
$opts->getSuspendAutoIdleUserRoles()
|
56 |
-
) ) )
|
57 |
-
);
|
58 |
-
|
59 |
-
$aChecks = $opts->getEmailValidationChecks();
|
60 |
-
if ( !empty( $aChecks ) ) {
|
61 |
-
$aChecks[] = 'syntax';
|
62 |
-
}
|
63 |
-
$opts->setOpt( 'email_checks', array_unique( $aChecks ) );
|
64 |
-
}
|
65 |
-
|
66 |
-
public function isUserSessionsManagementEnabled() :bool {
|
67 |
-
return $this->isModOptEnabled() && $this->getDbHandler_Sessions()->isReady();
|
68 |
-
}
|
69 |
-
|
70 |
-
public function isSendUserEmailLoginNotification() :bool {
|
71 |
-
return $this->isPremium() && $this->getOptions()->isOpt( 'enable_user_login_email_notification', 'Y' );
|
72 |
-
}
|
73 |
-
|
74 |
-
/**
|
75 |
-
* @param int $nStrength
|
76 |
-
* @return int
|
77 |
-
*/
|
78 |
-
public function getPassStrengthName( $nStrength ) {
|
79 |
-
$aMap = [
|
80 |
-
__( 'Very Weak', 'wp-simple-firewall' ),
|
81 |
-
__( 'Weak', 'wp-simple-firewall' ),
|
82 |
-
__( 'Medium', 'wp-simple-firewall' ),
|
83 |
-
__( 'Strong', 'wp-simple-firewall' ),
|
84 |
-
__( 'Very Strong', 'wp-simple-firewall' ),
|
85 |
-
];
|
86 |
-
return $aMap[ max( 0, min( 4, $nStrength ) ) ];
|
87 |
-
}
|
88 |
-
|
89 |
-
/**
|
90 |
-
* @param int $nUserId
|
91 |
-
* @param bool $bAdd - set true to add, false to remove
|
92 |
-
*/
|
93 |
-
public function addRemoveHardSuspendUserId( $nUserId, $bAdd = true ) {
|
94 |
-
/** @var UserManagement\Options $opts */
|
95 |
-
$opts = $this->getOptions();
|
96 |
-
|
97 |
-
$aIds = $opts->getSuspendHardUserIds();
|
98 |
-
|
99 |
-
$oMeta = $this->getCon()->getUserMeta( Services::WpUsers()->getUserById( $nUserId ) );
|
100 |
-
$bIdSuspended = isset( $aIds[ $nUserId ] ) || $oMeta->hard_suspended_at > 0;
|
101 |
-
|
102 |
-
if ( $bAdd && !$bIdSuspended ) {
|
103 |
-
$oMeta->hard_suspended_at = Services::Request()->ts();
|
104 |
-
$aIds[ $nUserId ] = $oMeta->hard_suspended_at;
|
105 |
-
$this->getCon()->fireEvent(
|
106 |
-
'user_hard_suspended',
|
107 |
-
[
|
108 |
-
'audit' => [
|
109 |
-
'user_id' => $nUserId,
|
110 |
-
'admin' => Services::WpUsers()->getCurrentWpUsername(),
|
111 |
-
]
|
112 |
-
]
|
113 |
-
);
|
114 |
-
}
|
115 |
-
elseif ( !$bAdd && $bIdSuspended ) {
|
116 |
-
$oMeta->hard_suspended_at = 0;
|
117 |
-
unset( $aIds[ $nUserId ] );
|
118 |
-
$this->getCon()->fireEvent(
|
119 |
-
'user_hard_unsuspended',
|
120 |
-
[
|
121 |
-
'audit' => [
|
122 |
-
'user_id' => $nUserId,
|
123 |
-
'admin' => Services::WpUsers()->getCurrentWpUsername(),
|
124 |
-
]
|
125 |
-
]
|
126 |
-
);
|
127 |
-
}
|
128 |
-
|
129 |
-
$opts->setOpt( 'hard_suspended_userids', $aIds );
|
130 |
-
}
|
131 |
-
|
132 |
-
/**
|
133 |
-
* @return string
|
134 |
-
*/
|
135 |
-
protected function getNamespaceBase() :string {
|
136 |
-
return 'UserManagement';
|
137 |
-
}
|
138 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
src/lib/src/Controller/Admin/AdminBarMenu.php
ADDED
@@ -0,0 +1,50 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php declare( strict_types=1 );
|
2 |
+
|
3 |
+
namespace FernleafSystems\Wordpress\Plugin\Shield\Controller\Admin;
|
4 |
+
|
5 |
+
use FernleafSystems\Utilities\Logic\ExecOnce;
|
6 |
+
use FernleafSystems\Wordpress\Plugin\Shield\Modules\PluginControllerConsumer;
|
7 |
+
|
8 |
+
class AdminBarMenu {
|
9 |
+
|
10 |
+
use PluginControllerConsumer;
|
11 |
+
use ExecOnce;
|
12 |
+
|
13 |
+
protected function canRun() :bool {
|
14 |
+
$con = $this->getCon();
|
15 |
+
return $con->isValidAdminArea( true ) &&
|
16 |
+
apply_filters( $con->prefix( 'shield/show_admin_bar_menu' ), $con->cfg->properties[ 'show_admin_bar_menu' ] );
|
17 |
+
}
|
18 |
+
|
19 |
+
protected function run() {
|
20 |
+
add_action( 'admin_init', function ( $adminBar ) {
|
21 |
+
$this->createAdminBarMenu( $adminBar );
|
22 |
+
} );
|
23 |
+
}
|
24 |
+
|
25 |
+
/**
|
26 |
+
* @param \WP_Admin_Bar $adminBar
|
27 |
+
*/
|
28 |
+
private function createAdminBarMenu( $adminBar ) {
|
29 |
+
$con = $this->getCon();
|
30 |
+
|
31 |
+
$items = apply_filters( $con->prefix( 'admin_bar_menu_items' ), [] );
|
32 |
+
if ( !empty( $items ) && is_array( $items ) ) {
|
33 |
+
$warningCount = 0;
|
34 |
+
foreach ( $items as $item ) {
|
35 |
+
$warningCount += isset( $item[ 'warnings' ] ) ? $item[ 'warnings' ] : 0;
|
36 |
+
}
|
37 |
+
|
38 |
+
$sNodeId = $con->prefix( 'adminbarmenu' );
|
39 |
+
$adminBar->add_node( [
|
40 |
+
'id' => $sNodeId,
|
41 |
+
'title' => $con->getHumanName()
|
42 |
+
.sprintf( '<div class="wp-core-ui wp-ui-notification shield-counter"><span aria-hidden="true">%s</span></div>', $warningCount ),
|
43 |
+
] );
|
44 |
+
foreach ( $items as $item ) {
|
45 |
+
$item[ 'parent' ] = $sNodeId;
|
46 |
+
$adminBar->add_menu( $item );
|
47 |
+
}
|
48 |
+
}
|
49 |
+
}
|
50 |
+
}
|
src/lib/src/Controller/Admin/DashboardWidget.php
ADDED
@@ -0,0 +1,35 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php declare( strict_types=1 );
|
2 |
+
|
3 |
+
namespace FernleafSystems\Wordpress\Plugin\Shield\Controller\Admin;
|
4 |
+
|
5 |
+
use FernleafSystems\Utilities\Logic\ExecOnce;
|
6 |
+
use FernleafSystems\Wordpress\Plugin\Shield\Modules\PluginControllerConsumer;
|
7 |
+
|
8 |
+
class DashboardWidget {
|
9 |
+
|
10 |
+
use PluginControllerConsumer;
|
11 |
+
use ExecOnce;
|
12 |
+
|
13 |
+
protected function canRun() :bool {
|
14 |
+
$con = $this->getCon();
|
15 |
+
return $con->isValidAdminArea() &&
|
16 |
+
apply_filters( 'shield/show_dashboard_widget', $con->cfg->properties[ 'show_dashboard_widget' ] ?? true );
|
17 |
+
}
|
18 |
+
|
19 |
+
protected function run() {
|
20 |
+
add_action( 'wp_dashboard_setup', function () {
|
21 |
+
$this->createWidget();
|
22 |
+
} );
|
23 |
+
}
|
24 |
+
|
25 |
+
private function createWidget() {
|
26 |
+
$con = $this->getCon();
|
27 |
+
wp_add_dashboard_widget(
|
28 |
+
$con->prefix( 'dashboard_widget' ),
|
29 |
+
apply_filters( $con->prefix( 'dashboard_widget_title' ), $con->getHumanName() ),
|
30 |
+
function () {
|
31 |
+
do_action( $this->getCon()->prefix( 'dashboard_widget_content' ) );
|
32 |
+
}
|
33 |
+
);
|
34 |
+
}
|
35 |
+
}
|
src/lib/src/Controller/Admin/MainAdminMenu.php
ADDED
@@ -0,0 +1,87 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php declare( strict_types=1 );
|
2 |
+
|
3 |
+
namespace FernleafSystems\Wordpress\Plugin\Shield\Controller\Admin;
|
4 |
+
|
5 |
+
use FernleafSystems\Utilities\Logic\ExecOnce;
|
6 |
+
use FernleafSystems\Wordpress\Plugin\Shield\Modules\PluginControllerConsumer;
|
7 |
+
|
8 |
+
class MainAdminMenu {
|
9 |
+
|
10 |
+
use PluginControllerConsumer;
|
11 |
+
use ExecOnce;
|
12 |
+
|
13 |
+
protected function canRun() :bool {
|
14 |
+
$con = $this->getCon();
|
15 |
+
return $con->isValidAdminArea()
|
16 |
+
&& apply_filters( 'shield/show_admin_menu', $con->cfg->menu[ 'show' ] ?? true );
|
17 |
+
}
|
18 |
+
|
19 |
+
protected function run() {
|
20 |
+
add_action( 'admin_menu', function () {
|
21 |
+
$this->createAdminMenu();
|
22 |
+
} );
|
23 |
+
add_action( 'network_admin_menu', function () {
|
24 |
+
$this->createAdminMenu();
|
25 |
+
} );
|
26 |
+
}
|
27 |
+
|
28 |
+
private function createAdminMenu() {
|
29 |
+
$con = $this->getCon();
|
30 |
+
|
31 |
+
$menu = $con->cfg->menu;
|
32 |
+
if ( $menu[ 'top_level' ] ) {
|
33 |
+
|
34 |
+
$labels = $con->getLabels();
|
35 |
+
$sMenuTitle = empty( $labels[ 'MenuTitle' ] ) ? $menu[ 'title' ] : $labels[ 'MenuTitle' ];
|
36 |
+
if ( is_null( $sMenuTitle ) ) {
|
37 |
+
$sMenuTitle = $con->getHumanName();
|
38 |
+
}
|
39 |
+
|
40 |
+
$sMenuIcon = $con->urls->forImage( $menu[ 'icon_image' ] );
|
41 |
+
$sIconUrl = empty( $labels[ 'icon_url_16x16' ] ) ? $sMenuIcon : $labels[ 'icon_url_16x16' ];
|
42 |
+
|
43 |
+
$parentMenuID = $con->getPluginPrefix();
|
44 |
+
add_menu_page(
|
45 |
+
$con->getHumanName(),
|
46 |
+
$sMenuTitle,
|
47 |
+
$con->getBasePermissions(),
|
48 |
+
$parentMenuID,
|
49 |
+
[ $this, 'onDisplayTopMenu' ],
|
50 |
+
$sIconUrl
|
51 |
+
);
|
52 |
+
|
53 |
+
if ( $menu[ 'has_submenu' ] ) {
|
54 |
+
|
55 |
+
$menuItems = apply_filters( $con->prefix( 'submenu_items' ), [] );
|
56 |
+
if ( !empty( $menuItems ) ) {
|
57 |
+
foreach ( $menuItems as $sMenuTitle => $aMenu ) {
|
58 |
+
list( $sMenuItemText, $sMenuItemId, $aMenuCallBack, $bShowItem ) = $aMenu;
|
59 |
+
add_submenu_page(
|
60 |
+
$bShowItem ? $parentMenuID : null,
|
61 |
+
$sMenuTitle,
|
62 |
+
$sMenuItemText,
|
63 |
+
$con->getBasePermissions(),
|
64 |
+
$sMenuItemId,
|
65 |
+
$aMenuCallBack
|
66 |
+
);
|
67 |
+
}
|
68 |
+
}
|
69 |
+
}
|
70 |
+
|
71 |
+
if ( $menu[ 'do_submenu_fix' ] ) {
|
72 |
+
$this->fixSubmenu();
|
73 |
+
}
|
74 |
+
}
|
75 |
+
}
|
76 |
+
|
77 |
+
public function onDisplayTopMenu() {
|
78 |
+
}
|
79 |
+
|
80 |
+
private function fixSubmenu() {
|
81 |
+
global $submenu;
|
82 |
+
$menuID = $this->getCon()->getPluginPrefix();
|
83 |
+
if ( isset( $submenu[ $menuID ] ) ) {
|
84 |
+
unset( $submenu[ $menuID ][ 0 ] );
|
85 |
+
}
|
86 |
+
}
|
87 |
+
}
|
src/lib/src/Controller/Ajax/Init.php
ADDED
@@ -0,0 +1,55 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php declare( strict_types=1 );
|
2 |
+
|
3 |
+
namespace FernleafSystems\Wordpress\Plugin\Shield\Controller\Ajax;
|
4 |
+
|
5 |
+
use FernleafSystems\Utilities\Logic\ExecOnce;
|
6 |
+
use FernleafSystems\Wordpress\Plugin\Shield\Modules\PluginControllerConsumer;
|
7 |
+
use FernleafSystems\Wordpress\Services\Services;
|
8 |
+
|
9 |
+
class Init {
|
10 |
+
|
11 |
+
use ExecOnce;
|
12 |
+
use PluginControllerConsumer;
|
13 |
+
|
14 |
+
protected function canRun() :bool {
|
15 |
+
return Services::WpGeneral()->isAjax();
|
16 |
+
}
|
17 |
+
|
18 |
+
protected function run() {
|
19 |
+
add_action( 'wp_ajax_'.$this->getCon()->prefix(), function () {
|
20 |
+
$this->ajaxAction();
|
21 |
+
} );
|
22 |
+
add_action( 'wp_ajax_nopriv_'.$this->getCon()->prefix(), function () {
|
23 |
+
$this->ajaxAction();
|
24 |
+
} );
|
25 |
+
}
|
26 |
+
|
27 |
+
private function ajaxAction() {
|
28 |
+
$nonceAction = Services::Request()->request( 'exec' );
|
29 |
+
check_ajax_referer( $nonceAction, 'exec_nonce' );
|
30 |
+
|
31 |
+
ob_start();
|
32 |
+
$response = apply_filters(
|
33 |
+
$this->getCon()->prefix( Services::WpUsers()->isUserLoggedIn() ? 'ajaxAuthAction' : 'ajaxNonAuthAction' ),
|
34 |
+
[], $nonceAction
|
35 |
+
);
|
36 |
+
$noise = ob_get_clean();
|
37 |
+
|
38 |
+
if ( is_array( $response ) && isset( $response[ 'success' ] ) ) {
|
39 |
+
$success = $response[ 'success' ];
|
40 |
+
}
|
41 |
+
else {
|
42 |
+
$success = false;
|
43 |
+
$response = [];
|
44 |
+
}
|
45 |
+
|
46 |
+
( new Response() )->issue(
|
47 |
+
[
|
48 |
+
'success' => $success,
|
49 |
+
'data' => $response,
|
50 |
+
'noise' => $noise
|
51 |
+
],
|
52 |
+
false
|
53 |
+
);
|
54 |
+
}
|
55 |
+
}
|
src/lib/src/Controller/Ajax/Response.php
ADDED
@@ -0,0 +1,28 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php declare( strict_types=1 );
|
2 |
+
|
3 |
+
namespace FernleafSystems\Wordpress\Plugin\Shield\Controller\Ajax;
|
4 |
+
|
5 |
+
use FernleafSystems\Wordpress\Services\Services;
|
6 |
+
|
7 |
+
class Response {
|
8 |
+
|
9 |
+
public function issue( array $response, $wrap = false ) {
|
10 |
+
$wrap = $wrap || (bool)Services::Request()->request( 'apto_wrap_response' );
|
11 |
+
|
12 |
+
if ( !headers_sent() ) {
|
13 |
+
header( 'Content-Type: application/json; charset='.get_option( 'blog_charset' ) );
|
14 |
+
nocache_headers();
|
15 |
+
if ( isset( $response[ 'status_code' ] ) ) {
|
16 |
+
status_header( $response[ 'status_code' ] );
|
17 |
+
}
|
18 |
+
}
|
19 |
+
if ( $wrap ) {
|
20 |
+
echo '##APTO_OPEN##';
|
21 |
+
}
|
22 |
+
echo wp_json_encode( $response );
|
23 |
+
if ( $wrap ) {
|
24 |
+
echo '##APTO_CLOSE##';
|
25 |
+
}
|
26 |
+
die( '' );
|
27 |
+
}
|
28 |
+
}
|
src/lib/src/Controller/Assets/Enqueue.php
ADDED
@@ -0,0 +1,174 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php declare( strict_types=1 );
|
2 |
+
|
3 |
+
namespace FernleafSystems\Wordpress\Plugin\Shield\Controller\Assets;
|
4 |
+
|
5 |
+
use FernleafSystems\Utilities\Logic\ExecOnce;
|
6 |
+
use FernleafSystems\Wordpress\Plugin\Shield\Modules\PluginControllerConsumer;
|
7 |
+
use FernleafSystems\Wordpress\Services\Services;
|
8 |
+
|
9 |
+
class Enqueue {
|
10 |
+
|
11 |
+
use PluginControllerConsumer;
|
12 |
+
use ExecOnce;
|
13 |
+
|
14 |
+
const CSS = 'css';
|
15 |
+
const JS = 'js';
|
16 |
+
|
17 |
+
private $adminHookSuffix = '';
|
18 |
+
|
19 |
+
protected function canRun() :bool {
|
20 |
+
$WP = Services::WpGeneral();
|
21 |
+
return !$WP->isAjax() && !$WP->isCron()
|
22 |
+
&& !empty( $this->getCon()->cfg->includes[ 'register' ] );
|
23 |
+
}
|
24 |
+
|
25 |
+
protected function run() {
|
26 |
+
add_action( 'wp_enqueue_scripts', function () {
|
27 |
+
$this->enqueue();
|
28 |
+
}, 1000 );
|
29 |
+
add_action( 'admin_enqueue_scripts', function ( $hook_suffix ) {
|
30 |
+
$this->adminHookSuffix = $hook_suffix;
|
31 |
+
$this->enqueue();
|
32 |
+
}, 1000 );
|
33 |
+
}
|
34 |
+
|
35 |
+
protected function enqueue() {
|
36 |
+
|
37 |
+
// Register all plugin assets
|
38 |
+
$this->registerAssets();
|
39 |
+
|
40 |
+
// Get standard enqueues
|
41 |
+
if ( current_action() == 'admin_enqueue_scripts' ) {
|
42 |
+
$assets = $this->getAdminAssetsToEnq();
|
43 |
+
}
|
44 |
+
else {
|
45 |
+
$assets = $this->getFrontendAssetsToEnq();
|
46 |
+
}
|
47 |
+
|
48 |
+
// Get custom enqueues from modules or elsewhere
|
49 |
+
$customAssets = $this->getCustomEnqueues();
|
50 |
+
|
51 |
+
// Combine enqueues and enqueue assets
|
52 |
+
foreach ( [ self::CSS, self::JS ] as $type ) {
|
53 |
+
if ( !empty( $customAssets[ $type ] ) ) {
|
54 |
+
$assets[ $type ] = array_unique( array_merge( $assets[ $type ], $customAssets[ $type ] ) );
|
55 |
+
}
|
56 |
+
$this->runEnqueueOnAssets( $type, $assets[ $type ] );
|
57 |
+
}
|
58 |
+
|
59 |
+
// Get module localisations
|
60 |
+
$this->localise();
|
61 |
+
}
|
62 |
+
|
63 |
+
private function localise() {
|
64 |
+
$localz = [];
|
65 |
+
foreach ( $this->getCon()->modules as $module ) {
|
66 |
+
foreach ( $module->getScriptLocalisations() as $local ) {
|
67 |
+
$localz[] = $local;
|
68 |
+
}
|
69 |
+
}
|
70 |
+
|
71 |
+
$localz = apply_filters( 'shield/custom_localisations', $localz, $this->adminHookSuffix );
|
72 |
+
|
73 |
+
foreach ( $localz as $local ) {
|
74 |
+
if ( is_array( $local ) && count( $local ) === 3 ) { //sanity
|
75 |
+
wp_localize_script( $this->normaliseHandle( $local[ 0 ] ), $local[ 1 ], $local[ 2 ] );
|
76 |
+
}
|
77 |
+
else {
|
78 |
+
error_log( 'Invalid localisation: '.var_export( $local, true ) );
|
79 |
+
}
|
80 |
+
}
|
81 |
+
}
|
82 |
+
|
83 |
+
/**
|
84 |
+
* Registers all assets in the plugin with the global population of assets (if not already included)
|
85 |
+
* This allows us to easily share assets amongst plugins and especially to use the Foundation Classes
|
86 |
+
* plugin to cater for most shared assets.
|
87 |
+
*/
|
88 |
+
private function registerAssets() {
|
89 |
+
$con = $this->getCon();
|
90 |
+
|
91 |
+
$assetKeys = [
|
92 |
+
self::CSS => [],
|
93 |
+
self::JS => [],
|
94 |
+
];
|
95 |
+
|
96 |
+
$incl = $con->cfg->includes[ 'register' ];
|
97 |
+
|
98 |
+
foreach ( array_keys( $assetKeys ) as $type ) {
|
99 |
+
|
100 |
+
foreach ( $incl[ $type ] as $key => $spec ) {
|
101 |
+
if ( !in_array( $key, $assetKeys[ $type ] ) ) {
|
102 |
+
|
103 |
+
$handle = $this->normaliseHandle( $key );
|
104 |
+
if ( $type === self::CSS ) {
|
105 |
+
$url = $spec[ 'url' ] ?? $con->urls->forCss( $key );
|
106 |
+
$reg = wp_register_style(
|
107 |
+
$handle,
|
108 |
+
$url,
|
109 |
+
$this->prefixKeys( $spec[ 'deps' ] ?? [] ),
|
110 |
+
$con->getVersion()
|
111 |
+
);
|
112 |
+
}
|
113 |
+
else {
|
114 |
+
$url = $spec[ 'url' ] ?? $con->urls->forJs( $key );
|
115 |
+
$reg = wp_register_script(
|
116 |
+
$handle,
|
117 |
+
$url,
|
118 |
+
$this->prefixKeys( $spec[ 'deps' ] ?? [] ),
|
119 |
+
$con->getVersion()
|
120 |
+
);
|
121 |
+
}
|
122 |
+
|
123 |
+
if ( $reg ) {
|
124 |
+
$assetKeys[ $type ][] = $handle;
|
125 |
+
}
|
126 |
+
}
|
127 |
+
}
|
128 |
+
}
|
129 |
+
}
|
130 |
+
|
131 |
+
private function getCustomEnqueues() :array {
|
132 |
+
$enqueues = [
|
133 |
+
self::CSS => [],
|
134 |
+
self::JS => [],
|
135 |
+
];
|
136 |
+
foreach ( $this->getCon()->modules as $module ) {
|
137 |
+
$custom = $module->getCustomScriptEnqueues();
|
138 |
+
foreach ( array_keys( $enqueues ) as $type ) {
|
139 |
+
if ( !empty( $custom[ $type ] ) ) {
|
140 |
+
$enqueues[ $type ] = array_merge( $enqueues[ $type ], $custom[ $type ] );
|
141 |
+
}
|
142 |
+
}
|
143 |
+
}
|
144 |
+
return apply_filters( 'shield/custom_enqueues', $enqueues, $this->adminHookSuffix );
|
145 |
+
}
|
146 |
+
|
147 |
+
private function prefixKeys( array $keys ) :array {
|
148 |
+
return array_map( function ( $handle ) {
|
149 |
+
return strpos( $handle, 'wp-' ) === 0 ?
|
150 |
+
preg_replace( '#^wp-#', '', $handle )
|
151 |
+
: $this->normaliseHandle( $handle );
|
152 |
+
}, $keys );
|
153 |
+
}
|
154 |
+
|
155 |
+
private function normaliseHandle( string $handle ) :string {
|
156 |
+
return str_replace( '/', '-', $this->getCon()->prefix( $handle ) );
|
157 |
+
}
|
158 |
+
|
159 |
+
private function getAdminAssetsToEnq() {
|
160 |
+
$con = $this->getCon();
|
161 |
+
return $con->cfg->includes[ $con->getIsPage_PluginAdmin() ? 'plugin_admin' : 'admin' ];
|
162 |
+
}
|
163 |
+
|
164 |
+
private function getFrontendAssetsToEnq() :array {
|
165 |
+
return $this->getCon()->cfg->includes[ 'frontend' ] ?? [];
|
166 |
+
}
|
167 |
+
|
168 |
+
private function runEnqueueOnAssets( string $type, array $asset ) {
|
169 |
+
array_map(
|
170 |
+
$type == self::CSS ? 'wp_enqueue_style' : 'wp_enqueue_script',
|
171 |
+
$this->prefixKeys( $asset )
|
172 |
+
);
|
173 |
+
}
|
174 |
+
}
|
src/lib/src/Controller/Assets/Urls.php
ADDED
@@ -0,0 +1,64 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php declare( strict_types=1 );
|
2 |
+
|
3 |
+
namespace FernleafSystems\Wordpress\Plugin\Shield\Controller\Assets;
|
4 |
+
|
5 |
+
use FernleafSystems\Wordpress\Plugin\Shield\Modules\PluginControllerConsumer;
|
6 |
+
use FernleafSystems\Wordpress\Services\Services;
|
7 |
+
|
8 |
+
class Urls {
|
9 |
+
|
10 |
+
use PluginControllerConsumer;
|
11 |
+
|
12 |
+
public function forCss( string $asset ) :string {
|
13 |
+
$url = $this->lookupAssetUrlInSpec( $asset, 'css' );
|
14 |
+
return empty( $url ) ?
|
15 |
+
$this->forAsset( 'css/'.Services::Data()->addExtensionToFilePath( $asset, 'css' ) )
|
16 |
+
: $url;
|
17 |
+
}
|
18 |
+
|
19 |
+
public function forImage( string $asset ) :string {
|
20 |
+
return $this->forAsset( 'images/'.$asset );
|
21 |
+
}
|
22 |
+
|
23 |
+
public function forJs( string $asset ) :string {
|
24 |
+
$url = $this->lookupAssetUrlInSpec( $asset, 'js' );
|
25 |
+
return empty( $url ) ?
|
26 |
+
$this->forAsset( 'js/'.Services::Data()->addExtensionToFilePath( $asset, 'js' ) )
|
27 |
+
: $url;
|
28 |
+
}
|
29 |
+
|
30 |
+
public function forAsset( string $asset ) :string {
|
31 |
+
$con = $this->getCon();
|
32 |
+
|
33 |
+
$path = $con->getPath_Assets( $asset );
|
34 |
+
if ( Services::WpFs()->exists( $path ) ) {
|
35 |
+
$url = Services::Includes()->addIncludeModifiedParam(
|
36 |
+
$this->forPluginItem( $con->getPluginSpec_Path( 'assets' ).'/'.$asset ),
|
37 |
+
$path
|
38 |
+
);
|
39 |
+
}
|
40 |
+
else {
|
41 |
+
$url = '';
|
42 |
+
}
|
43 |
+
|
44 |
+
return $url;
|
45 |
+
}
|
46 |
+
|
47 |
+
public function forPluginItem( string $path = '' ) :string {
|
48 |
+
$con = $this->getCon();
|
49 |
+
return add_query_arg( [ 'ver' => $con->getVersion() ], plugins_url( $path, $con->getRootFile() ) );
|
50 |
+
}
|
51 |
+
|
52 |
+
/**
|
53 |
+
* @param string $asset
|
54 |
+
* @param string $type
|
55 |
+
* @return mixed|null
|
56 |
+
*/
|
57 |
+
protected function lookupAssetUrlInSpec( string $asset, string $type ) {
|
58 |
+
$registrations = $this->getCon()->cfg->includes[ 'register' ][ $type ];
|
59 |
+
if ( isset( $registrations[ $asset ] ) && !empty( $registrations[ $asset ][ 'url' ] ) ) {
|
60 |
+
return $registrations[ $asset ][ 'url' ];
|
61 |
+
}
|
62 |
+
return null;
|
63 |
+
}
|
64 |
+
}
|
src/lib/src/Controller/Config/Ops/LoadConfig.php
CHANGED
@@ -40,7 +40,7 @@ class LoadConfig {
|
|
40 |
$version = $def[ 'properties' ][ 'version' ] ?? '0';
|
41 |
|
42 |
$rebuild = empty( $def[ 'hash' ] ) || !hash_equals( $def[ 'hash' ], $specHash )
|
43 |
-
|| ( $version !== Services::WpPlugins()->getPluginAsVo( $con->
|
44 |
$def[ 'hash' ] = $specHash;
|
45 |
}
|
46 |
|
40 |
$version = $def[ 'properties' ][ 'version' ] ?? '0';
|
41 |
|
42 |
$rebuild = empty( $def[ 'hash' ] ) || !hash_equals( $def[ 'hash' ], $specHash )
|
43 |
+
|| ( $version !== Services::WpPlugins()->getPluginAsVo( $con->base_file )->Version );
|
44 |
$def[ 'hash' ] = $specHash;
|
45 |
}
|
46 |
|
src/lib/src/Controller/Controller.php
CHANGED
@@ -11,6 +11,7 @@ use FernleafSystems\Wordpress\Services\Utilities\Options\Transient;
|
|
11 |
* Class Controller
|
12 |
* @package FernleafSystems\Wordpress\Plugin\Shield\Controller
|
13 |
* @property Config\ConfigVO $cfg
|
|
|
14 |
* @property bool $is_activating
|
15 |
* @property bool $is_debug
|
16 |
* @property bool $modules_loaded
|
@@ -42,18 +43,6 @@ class Controller {
|
|
42 |
*/
|
43 |
public static $oInstance;
|
44 |
|
45 |
-
/**
|
46 |
-
* @var string
|
47 |
-
* @deprecated 10.1
|
48 |
-
*/
|
49 |
-
private $sRootFile;
|
50 |
-
|
51 |
-
/**
|
52 |
-
* @var string
|
53 |
-
* @deprecated 10.1
|
54 |
-
*/
|
55 |
-
private $sPluginBaseFile;
|
56 |
-
|
57 |
/**
|
58 |
* @var array
|
59 |
*/
|
@@ -138,9 +127,8 @@ class Controller {
|
|
138 |
* @throws \Exception
|
139 |
*/
|
140 |
protected function __construct( string $rootFile ) {
|
141 |
-
$this->sRootFile = $rootFile;
|
142 |
$this->root_file = $rootFile;
|
143 |
-
$this->base_file = $this->
|
144 |
$this->modules = [];
|
145 |
|
146 |
$this->loadServices();
|
@@ -169,6 +157,12 @@ class Controller {
|
|
169 |
}
|
170 |
break;
|
171 |
|
|
|
|
|
|
|
|
|
|
|
|
|
172 |
case 'is_debug':
|
173 |
if ( is_null( $val ) ) {
|
174 |
$val = ( new Shield\Controller\Utilities\DebugMode() )
|
@@ -192,23 +186,6 @@ class Controller {
|
|
192 |
Services::GetInstance();
|
193 |
}
|
194 |
|
195 |
-
/**
|
196 |
-
* @return array
|
197 |
-
* @throws \Exception
|
198 |
-
* @deprecated 10.1.4
|
199 |
-
*/
|
200 |
-
private function readPluginSpecification() :array {
|
201 |
-
$spec = [];
|
202 |
-
$content = Services::Data()->readFileContentsUsingInclude( $this->getPathPluginSpec() );
|
203 |
-
if ( !empty( $content ) ) {
|
204 |
-
$spec = json_decode( $content, true );
|
205 |
-
if ( empty( $spec ) || !is_array( $spec ) ) {
|
206 |
-
throw new \Exception( 'Could not load plugin spec configuration.' );
|
207 |
-
}
|
208 |
-
}
|
209 |
-
return $spec;
|
210 |
-
}
|
211 |
-
|
212 |
/**
|
213 |
* @param bool $bCheckOnlyFrontEnd
|
214 |
* @throws \Exception
|
@@ -315,9 +292,9 @@ class Controller {
|
|
315 |
|
316 |
public function onWpActivatePlugin() {
|
317 |
$this->is_activating = true;
|
318 |
-
$
|
319 |
-
if ( $
|
320 |
-
$
|
321 |
}
|
322 |
}
|
323 |
|
@@ -363,21 +340,12 @@ class Controller {
|
|
363 |
add_action( 'wp_loaded', [ $this, 'onWpLoaded' ] );
|
364 |
add_action( 'admin_init', [ $this, 'onWpAdminInit' ] );
|
365 |
|
366 |
-
add_action( 'admin_menu', [ $this, 'onWpAdminMenu' ] );
|
367 |
-
add_action( 'network_admin_menu', [ $this, 'onWpAdminMenu' ] );
|
368 |
-
|
369 |
-
if ( Services::WpGeneral()->isAjax() ) {
|
370 |
-
add_action( 'wp_ajax_'.$this->prefix(), [ $this, 'ajaxAction' ] );
|
371 |
-
add_action( 'wp_ajax_nopriv_'.$this->prefix(), [ $this, 'ajaxAction' ] );
|
372 |
-
}
|
373 |
-
|
374 |
-
$sBaseFile = $this->getPluginBaseFile();
|
375 |
add_filter( 'all_plugins', [ $this, 'filter_hidePluginFromTableList' ] );
|
376 |
add_filter( 'all_plugins', [ $this, 'doPluginLabels' ] );
|
377 |
-
add_filter( 'plugin_action_links_'.$
|
378 |
add_filter( 'plugin_row_meta', [ $this, 'onPluginRowMeta' ], 50, 2 );
|
379 |
add_filter( 'site_transient_update_plugins', [ $this, 'filter_hidePluginUpdatesFromUI' ] );
|
380 |
-
add_action( 'in_plugin_update_message-'.$
|
381 |
add_filter( 'site_transient_update_plugins', [ $this, 'blockIncompatibleUpdates' ] );
|
382 |
add_filter( 'auto_update_plugin', [ $this, 'onWpAutoUpdate' ], 500, 2 );
|
383 |
add_filter( 'set_site_transient_update_plugins', [ $this, 'setUpdateFirstDetectedAt' ] );
|
@@ -401,10 +369,12 @@ class Controller {
|
|
401 |
}
|
402 |
|
403 |
public function onWpAdminInit() {
|
404 |
-
|
405 |
-
|
406 |
-
|
407 |
-
|
|
|
|
|
408 |
|
409 |
if ( Services::Request()->query( $this->prefix( 'runtests' ) ) && $this->isPluginAdmin() ) {
|
410 |
$this->runTests();
|
@@ -418,25 +388,26 @@ class Controller {
|
|
418 |
|
419 |
/**
|
420 |
* In order to prevent certain errors when the back button is used
|
421 |
-
* @param array $
|
422 |
* @return array
|
423 |
*/
|
424 |
-
public function adjustNocacheHeaders( $
|
425 |
-
if ( is_array( $
|
426 |
-
$
|
427 |
-
$
|
428 |
-
$
|
429 |
}
|
430 |
-
return $
|
431 |
}
|
432 |
|
433 |
public function onWpInit() {
|
434 |
$this->getMeetsBasePermissions();
|
435 |
-
add_action( 'wp_enqueue_scripts', [ $this, 'onWpEnqueueFrontendCss' ], 99 );
|
436 |
-
|
437 |
if ( $this->isModulePage() ) {
|
438 |
add_filter( 'nocache_headers', [ $this, 'adjustNocacheHeaders' ] );
|
439 |
}
|
|
|
|
|
|
|
440 |
}
|
441 |
|
442 |
/**
|
@@ -445,10 +416,10 @@ class Controller {
|
|
445 |
* @return string - the unique, never-changing site install ID.
|
446 |
*/
|
447 |
public function getSiteInstallationId() {
|
448 |
-
$
|
449 |
$sOptKey = $this->prefixOption( 'install_id' );
|
450 |
|
451 |
-
$mStoredID = $
|
452 |
if ( is_array( $mStoredID ) && !empty( $mStoredID[ 'id' ] ) ) {
|
453 |
$sID = $mStoredID[ 'id' ];
|
454 |
$bUpdate = true;
|
@@ -467,13 +438,13 @@ class Controller {
|
|
467 |
$sID = \Ramsey\Uuid\Uuid::uuid4()->toString();
|
468 |
}
|
469 |
catch ( \Exception $e ) {
|
470 |
-
$sID = sha1( uniqid( $
|
471 |
}
|
472 |
$bUpdate = true;
|
473 |
}
|
474 |
|
475 |
if ( $bUpdate ) {
|
476 |
-
$
|
477 |
}
|
478 |
|
479 |
return $sID;
|
@@ -481,17 +452,23 @@ class Controller {
|
|
481 |
|
482 |
/**
|
483 |
* TODO: Use to set ID after license verify where applicable
|
484 |
-
* @param string $
|
485 |
*/
|
486 |
-
public function setSiteInstallID( $
|
487 |
-
if ( !empty( $
|
488 |
-
Services::WpGeneral()->updateOption( $this->prefixOption( 'install_id' ), $
|
489 |
}
|
490 |
}
|
491 |
|
492 |
public function onWpLoaded() {
|
493 |
$this->getAdminNotices();
|
494 |
$this->initCrons();
|
|
|
|
|
|
|
|
|
|
|
|
|
495 |
}
|
496 |
|
497 |
protected function initCrons() {
|
@@ -503,56 +480,6 @@ class Controller {
|
|
503 |
->run();
|
504 |
}
|
505 |
|
506 |
-
public function onWpAdminMenu() {
|
507 |
-
if ( $this->isValidAdminArea() ) {
|
508 |
-
$this->createPluginMenu();
|
509 |
-
}
|
510 |
-
}
|
511 |
-
|
512 |
-
/**
|
513 |
-
* @param \WP_Admin_Bar $oAdminBar
|
514 |
-
*/
|
515 |
-
public function onWpAdminBarMenu( $oAdminBar ) {
|
516 |
-
$bShow = apply_filters( $this->prefix( 'show_admin_bar_menu' ),
|
517 |
-
$this->isValidAdminArea( true ) && $this->cfg->properties[ 'show_admin_bar_menu' ]
|
518 |
-
);
|
519 |
-
if ( $bShow ) {
|
520 |
-
$aMenuItems = apply_filters( $this->prefix( 'admin_bar_menu_items' ), [] );
|
521 |
-
if ( !empty( $aMenuItems ) && is_array( $aMenuItems ) ) {
|
522 |
-
$nCountWarnings = 0;
|
523 |
-
foreach ( $aMenuItems as $aMenuItem ) {
|
524 |
-
$nCountWarnings += isset( $aMenuItem[ 'warnings' ] ) ? $aMenuItem[ 'warnings' ] : 0;
|
525 |
-
}
|
526 |
-
|
527 |
-
$sNodeId = $this->prefix( 'adminbarmenu' );
|
528 |
-
$oAdminBar->add_node( [
|
529 |
-
'id' => $sNodeId,
|
530 |
-
'title' => $this->getHumanName()
|
531 |
-
.sprintf( '<div class="wp-core-ui wp-ui-notification shield-counter"><span aria-hidden="true">%s</span></div>', $nCountWarnings ),
|
532 |
-
] );
|
533 |
-
foreach ( $aMenuItems as $aMenuItem ) {
|
534 |
-
$aMenuItem[ 'parent' ] = $sNodeId;
|
535 |
-
$oAdminBar->add_menu( $aMenuItem );
|
536 |
-
}
|
537 |
-
}
|
538 |
-
}
|
539 |
-
}
|
540 |
-
|
541 |
-
public function onWpDashboardSetup() {
|
542 |
-
$show = apply_filters( $this->prefix( 'show_dashboard_widget' ),
|
543 |
-
$this->isValidAdminArea() && $this->cfg->properties[ 'show_dashboard_widget' ]
|
544 |
-
);
|
545 |
-
if ( $show ) {
|
546 |
-
wp_add_dashboard_widget(
|
547 |
-
$this->prefix( 'dashboard_widget' ),
|
548 |
-
apply_filters( $this->prefix( 'dashboard_widget_title' ), $this->getHumanName() ),
|
549 |
-
function () {
|
550 |
-
do_action( $this->prefix( 'dashboard_widget_content' ) );
|
551 |
-
}
|
552 |
-
);
|
553 |
-
}
|
554 |
-
}
|
555 |
-
|
556 |
/**
|
557 |
* @return Shield\Utilities\AdminNotices\Controller
|
558 |
*/
|
@@ -580,119 +507,20 @@ class Controller {
|
|
580 |
];
|
581 |
}
|
582 |
|
583 |
-
public function ajaxAction() {
|
584 |
-
$nonceAction = Services::Request()->request( 'exec' );
|
585 |
-
check_ajax_referer( $nonceAction, 'exec_nonce' );
|
586 |
-
|
587 |
-
ob_start();
|
588 |
-
$response = apply_filters(
|
589 |
-
$this->prefix( Services::WpUsers()->isUserLoggedIn() ? 'ajaxAuthAction' : 'ajaxNonAuthAction' ),
|
590 |
-
[], $nonceAction
|
591 |
-
);
|
592 |
-
$noise = ob_get_clean();
|
593 |
-
|
594 |
-
if ( is_array( $response ) && isset( $response[ 'success' ] ) ) {
|
595 |
-
$bSuccess = $response[ 'success' ];
|
596 |
-
}
|
597 |
-
else {
|
598 |
-
$bSuccess = false;
|
599 |
-
$response = [];
|
600 |
-
}
|
601 |
-
|
602 |
-
wp_send_json(
|
603 |
-
[
|
604 |
-
'success' => $bSuccess,
|
605 |
-
'data' => $response,
|
606 |
-
'noise' => $noise
|
607 |
-
]
|
608 |
-
);
|
609 |
-
}
|
610 |
-
|
611 |
-
/**
|
612 |
-
* @return bool
|
613 |
-
*/
|
614 |
-
protected function createPluginMenu() {
|
615 |
-
$menu = $this->cfg->menu;
|
616 |
-
|
617 |
-
if ( apply_filters( $this->prefix( 'filter_hidePluginMenu' ), !$menu[ 'show' ] ) ) {
|
618 |
-
return true;
|
619 |
-
}
|
620 |
-
|
621 |
-
if ( $menu[ 'top_level' ] ) {
|
622 |
-
|
623 |
-
$labels = $this->getLabels();
|
624 |
-
$sMenuTitle = empty( $labels[ 'MenuTitle' ] ) ? $menu[ 'title' ] : $labels[ 'MenuTitle' ];
|
625 |
-
if ( is_null( $sMenuTitle ) ) {
|
626 |
-
$sMenuTitle = $this->getHumanName();
|
627 |
-
}
|
628 |
-
|
629 |
-
$sMenuIcon = $this->getPluginUrl_Image( $menu[ 'icon_image' ] );
|
630 |
-
$sIconUrl = empty( $labels[ 'icon_url_16x16' ] ) ? $sMenuIcon : $labels[ 'icon_url_16x16' ];
|
631 |
-
|
632 |
-
$sFullParentMenuId = $this->getPluginPrefix();
|
633 |
-
add_menu_page(
|
634 |
-
$this->getHumanName(),
|
635 |
-
$sMenuTitle,
|
636 |
-
$this->getBasePermissions(),
|
637 |
-
$sFullParentMenuId,
|
638 |
-
[ $this, $menu[ 'callback' ] ],
|
639 |
-
$sIconUrl
|
640 |
-
);
|
641 |
-
|
642 |
-
if ( $menu[ 'has_submenu' ] ) {
|
643 |
-
|
644 |
-
$menuItems = apply_filters( $this->prefix( 'submenu_items' ), [] );
|
645 |
-
if ( !empty( $menuItems ) ) {
|
646 |
-
foreach ( $menuItems as $sMenuTitle => $aMenu ) {
|
647 |
-
list( $sMenuItemText, $sMenuItemId, $aMenuCallBack, $bShowItem ) = $aMenu;
|
648 |
-
add_submenu_page(
|
649 |
-
$bShowItem ? $sFullParentMenuId : null,
|
650 |
-
$sMenuTitle,
|
651 |
-
$sMenuItemText,
|
652 |
-
$this->getBasePermissions(),
|
653 |
-
$sMenuItemId,
|
654 |
-
$aMenuCallBack
|
655 |
-
);
|
656 |
-
}
|
657 |
-
}
|
658 |
-
}
|
659 |
-
|
660 |
-
if ( $menu[ 'do_submenu_fix' ] ) {
|
661 |
-
$this->fixSubmenu();
|
662 |
-
}
|
663 |
-
}
|
664 |
-
return true;
|
665 |
-
}
|
666 |
-
|
667 |
-
protected function fixSubmenu() {
|
668 |
-
global $submenu;
|
669 |
-
$sFullParentMenuId = $this->getPluginPrefix();
|
670 |
-
if ( isset( $submenu[ $sFullParentMenuId ] ) ) {
|
671 |
-
unset( $submenu[ $sFullParentMenuId ][ 0 ] );
|
672 |
-
}
|
673 |
-
}
|
674 |
-
|
675 |
/**
|
676 |
-
*
|
677 |
-
*
|
678 |
-
*/
|
679 |
-
public function onDisplayTopMenu() {
|
680 |
-
}
|
681 |
-
|
682 |
-
/**
|
683 |
-
* @param array $aPluginMeta
|
684 |
-
* @param string $sPluginFile
|
685 |
* @return array
|
686 |
*/
|
687 |
-
public function onPluginRowMeta( $
|
688 |
|
689 |
-
if ( $
|
690 |
$sTemplate = '<strong><a href="%s" target="_blank">%s</a></strong>';
|
691 |
foreach ( $this->cfg->plugin_meta as $aHref ) {
|
692 |
-
array_push( $
|
693 |
}
|
694 |
}
|
695 |
-
return $
|
696 |
}
|
697 |
|
698 |
/**
|
@@ -710,8 +538,8 @@ class Controller {
|
|
710 |
$links = $this->cfg->action_links[ 'add' ];
|
711 |
if ( is_array( $links ) ) {
|
712 |
|
713 |
-
$
|
714 |
-
$
|
715 |
$sLinkTemplate = '<a href="%s" target="%s" title="%s">%s</a>';
|
716 |
foreach ( $links as $aLink ) {
|
717 |
$aLink = array_merge(
|
@@ -726,13 +554,13 @@ class Controller {
|
|
726 |
$aLink
|
727 |
);
|
728 |
|
729 |
-
$
|
730 |
-
$bShow = ( $
|
731 |
-
if ( !$
|
732 |
$aLink[ 'href' ] = $this->{$aLink[ 'href' ]}();
|
733 |
}
|
734 |
|
735 |
-
if ( !$bShow || !$
|
736 |
|| empty( $aLink[ 'name' ] ) || empty( $aLink[ 'href' ] ) ) {
|
737 |
continue;
|
738 |
}
|
@@ -754,133 +582,41 @@ class Controller {
|
|
754 |
return $aActionLinks;
|
755 |
}
|
756 |
|
757 |
-
public function onWpEnqueueFrontendCss() {
|
758 |
-
$includes = $this->cfg->includes[ 'frontend' ];
|
759 |
-
if ( isset( $includes[ 'css' ] ) && !empty( $includes[ 'css' ] ) && is_array( $includes[ 'css' ] ) ) {
|
760 |
-
|
761 |
-
$aDeps = [];
|
762 |
-
foreach ( $includes[ 'css' ] as $sAsset ) {
|
763 |
-
$sUrl = $this->getPluginUrl_Css( $sAsset );
|
764 |
-
if ( !empty( $sUrl ) ) {
|
765 |
-
$sAsset = $this->prefix( $sAsset );
|
766 |
-
wp_register_style( $sAsset, $sUrl, $aDeps, $this->getVersion() );
|
767 |
-
wp_enqueue_style( $sAsset );
|
768 |
-
$aDeps[] = $aDeps;
|
769 |
-
}
|
770 |
-
}
|
771 |
-
}
|
772 |
-
}
|
773 |
-
|
774 |
-
public function onWpEnqueueAdminJs() {
|
775 |
-
|
776 |
-
$aIncludes = [];
|
777 |
-
if ( $this->getIsPage_PluginAdmin() ) {
|
778 |
-
$includes = $this->cfg->includes[ 'plugin_admin' ];
|
779 |
-
if ( !empty( $includes[ 'js' ] ) && is_array( $includes[ 'js' ] ) ) {
|
780 |
-
$aIncludes = $includes[ 'js' ];
|
781 |
-
}
|
782 |
-
}
|
783 |
-
elseif ( $this->isValidAdminArea() ) {
|
784 |
-
$includes = $this->cfg->includes[ 'admin' ];
|
785 |
-
if ( !empty( $includes[ 'js' ] ) && is_array( $includes[ 'js' ] ) ) {
|
786 |
-
$aIncludes = $includes[ 'js' ];
|
787 |
-
}
|
788 |
-
}
|
789 |
-
|
790 |
-
$nativeWP = [ 'jquery' ];
|
791 |
-
|
792 |
-
$aDeps = [];
|
793 |
-
foreach ( $aIncludes as $asset ) {
|
794 |
-
|
795 |
-
// Built-in handles
|
796 |
-
if ( in_array( $asset, $nativeWP ) ) {
|
797 |
-
if ( wp_script_is( $asset, 'registered' ) ) {
|
798 |
-
wp_enqueue_script( $asset );
|
799 |
-
$aDeps[] = $asset;
|
800 |
-
}
|
801 |
-
}
|
802 |
-
else {
|
803 |
-
$sUrl = $this->getPluginUrl_Js( $asset );
|
804 |
-
if ( !empty( $sUrl ) ) {
|
805 |
-
$asset = $this->prefix( $asset );
|
806 |
-
wp_register_script( $asset, $sUrl, $aDeps, $this->getVersion() );
|
807 |
-
wp_enqueue_script( $asset );
|
808 |
-
$aDeps[] = $asset;
|
809 |
-
}
|
810 |
-
}
|
811 |
-
}
|
812 |
-
}
|
813 |
-
|
814 |
-
public function onWpEnqueueAdminCss() {
|
815 |
-
|
816 |
-
$aIncludes = [];
|
817 |
-
if ( $this->getIsPage_PluginAdmin() ) {
|
818 |
-
$includes = $this->cfg->includes[ 'plugin_admin' ];
|
819 |
-
if ( !empty( $includes[ 'css' ] ) && is_array( $includes[ 'css' ] ) ) {
|
820 |
-
$aIncludes = $includes[ 'css' ];
|
821 |
-
}
|
822 |
-
}
|
823 |
-
elseif ( $this->isValidAdminArea() ) {
|
824 |
-
$includes = $this->cfg->includes[ 'admin' ];
|
825 |
-
if ( !empty( $includes[ 'css' ] ) && is_array( $includes[ 'css' ] ) ) {
|
826 |
-
$aIncludes = $includes[ 'css' ];
|
827 |
-
}
|
828 |
-
}
|
829 |
-
|
830 |
-
$aDeps = [];
|
831 |
-
foreach ( $aIncludes as $asset ) {
|
832 |
-
$sUrl = $this->getPluginUrl_Css( $asset );
|
833 |
-
if ( !empty( $sUrl ) ) {
|
834 |
-
$asset = $this->prefix( $asset );
|
835 |
-
wp_register_style( $asset, $sUrl, $aDeps, $this->getVersion() );
|
836 |
-
wp_enqueue_style( $asset );
|
837 |
-
$aDeps[] = $asset;
|
838 |
-
}
|
839 |
-
}
|
840 |
-
}
|
841 |
-
|
842 |
/**
|
843 |
* Displays a message in the plugins listing when a plugin has an update available.
|
844 |
*/
|
845 |
public function onWpPluginUpdateMessage() {
|
846 |
-
|
847 |
-
|
848 |
-
$
|
849 |
-
|
850 |
-
|
851 |
-
$sMessage = sprintf(
|
852 |
-
' <span class="%s plugin_update_message">%s</span>',
|
853 |
-
$this->getPluginPrefix(),
|
854 |
-
$sMessage
|
855 |
-
);
|
856 |
-
}
|
857 |
-
echo $sMessage;
|
858 |
}
|
859 |
|
860 |
/**
|
861 |
* Prevents upgrades to Shield versions when the system PHP version is too old.
|
862 |
-
* @param \stdClass $
|
863 |
* @return \stdClass
|
864 |
*/
|
865 |
-
public function blockIncompatibleUpdates( $
|
866 |
-
$
|
867 |
-
if ( !empty( $
|
868 |
-
$
|
869 |
-
if ( is_array( $
|
870 |
-
foreach ( $
|
871 |
-
$bNeedsHidden = version_compare( $
|
872 |
&& (
|
873 |
!Services::Data()->getPhpVersionIsAtLeast( $aReqs[ 'php' ] )
|
874 |
|| !Services::WpGeneral()->getWordpressIsAtLeastVersion( $aReqs[ 'wp' ] )
|
875 |
);
|
876 |
if ( $bNeedsHidden ) {
|
877 |
-
unset( $
|
878 |
break;
|
879 |
}
|
880 |
}
|
881 |
}
|
882 |
}
|
883 |
-
return $
|
884 |
}
|
885 |
|
886 |
/**
|
@@ -891,10 +627,10 @@ class Controller {
|
|
891 |
*/
|
892 |
public function setUpdateFirstDetectedAt( $data ) {
|
893 |
|
894 |
-
if ( !empty( $data ) && !empty( $data->response ) && isset( $data->response[ $this->
|
895 |
// i.e. update available
|
896 |
|
897 |
-
$new = Services::WpPlugins()->getUpdateNewVersion( $this->
|
898 |
if ( !empty( $new ) && isset( $this->cfg ) ) {
|
899 |
$updates = $this->cfg->update_first_detected;
|
900 |
if ( count( $updates ) > 3 ) {
|
@@ -921,17 +657,17 @@ class Controller {
|
|
921 |
$WP = Services::WpGeneral();
|
922 |
$oWpPlugins = Services::WpPlugins();
|
923 |
|
924 |
-
$
|
925 |
|
926 |
// The item in question is this plugin...
|
927 |
-
if ( $
|
928 |
$autoupdateSelf = $this->cfg->properties[ 'autoupdate' ];
|
929 |
|
930 |
if ( !$WP->isRunningAutomaticUpdates() && $autoupdateSelf == 'confidence' ) {
|
931 |
$autoupdateSelf = 'yes'; // so that we appear to be automatically updating
|
932 |
}
|
933 |
|
934 |
-
$new = $oWpPlugins->getUpdateNewVersion( $
|
935 |
|
936 |
switch ( $autoupdateSelf ) {
|
937 |
|
@@ -966,16 +702,16 @@ class Controller {
|
|
966 |
* @return array
|
967 |
*/
|
968 |
public function doPluginLabels( $aPlugins ) {
|
969 |
-
$
|
970 |
-
if ( empty( $
|
971 |
return $aPlugins;
|
972 |
}
|
973 |
|
974 |
-
$
|
975 |
// For this plugin, overwrite any specified settings
|
976 |
-
if ( array_key_exists( $
|
977 |
-
foreach ( $
|
978 |
-
$aPlugins[ $
|
979 |
}
|
980 |
}
|
981 |
|
@@ -1032,7 +768,7 @@ class Controller {
|
|
1032 |
*/
|
1033 |
public function filter_hidePluginFromTableList( $plugins ) {
|
1034 |
if ( apply_filters( $this->prefix( 'hide_plugin' ), false ) ) {
|
1035 |
-
unset( $plugins[ $this->
|
1036 |
}
|
1037 |
return $plugins;
|
1038 |
}
|
@@ -1047,7 +783,7 @@ class Controller {
|
|
1047 |
*/
|
1048 |
public function filter_hidePluginUpdatesFromUI( $plugins ) {
|
1049 |
if ( !Services::WpGeneral()->isCron() && apply_filters( $this->prefix( 'hide_plugin_updates' ), false ) ) {
|
1050 |
-
unset( $plugins->response[ $this->
|
1051 |
}
|
1052 |
return $plugins;
|
1053 |
}
|
@@ -1058,13 +794,13 @@ class Controller {
|
|
1058 |
* @return string
|
1059 |
*/
|
1060 |
public function prefix( $suffix = '', $glue = '-' ) {
|
1061 |
-
$
|
1062 |
|
1063 |
-
if ( $suffix == $
|
1064 |
return $suffix;
|
1065 |
}
|
1066 |
|
1067 |
-
return sprintf( '%s%s%s', $
|
1068 |
}
|
1069 |
|
1070 |
public function prefixOption( string $suffix = '' ) :string {
|
@@ -1094,52 +830,6 @@ class Controller {
|
|
1094 |
return $this->getPluginControllerOptions()->plugin_spec;
|
1095 |
}
|
1096 |
|
1097 |
-
/**
|
1098 |
-
* @param string $key
|
1099 |
-
* @return array
|
1100 |
-
* @deprecated 10.1.4
|
1101 |
-
*/
|
1102 |
-
protected function getPluginSpec_ActionLinks( string $key ) :array {
|
1103 |
-
$aData = $this->getPluginSpec()[ 'action_links' ];
|
1104 |
-
return $aData[ $key ] ?? [];
|
1105 |
-
}
|
1106 |
-
|
1107 |
-
/**
|
1108 |
-
* @param string $key
|
1109 |
-
* @return mixed|null
|
1110 |
-
* @deprecated 10.1.4
|
1111 |
-
*/
|
1112 |
-
protected function getPluginSpec_Include( string $key ) {
|
1113 |
-
$aData = $this->getPluginSpec()[ 'includes' ];
|
1114 |
-
return $aData[ $key ] ?? null;
|
1115 |
-
}
|
1116 |
-
|
1117 |
-
/**
|
1118 |
-
* @param string $key
|
1119 |
-
* @return array|string
|
1120 |
-
* @deprecated 10.1.4
|
1121 |
-
*/
|
1122 |
-
protected function getPluginSpec_Labels( string $key = '' ) {
|
1123 |
-
$oSpec = $this->getPluginSpec();
|
1124 |
-
$aLabels = isset( $oSpec[ 'labels' ] ) ? $oSpec[ 'labels' ] : [];
|
1125 |
-
|
1126 |
-
if ( empty( $key ) ) {
|
1127 |
-
return $aLabels;
|
1128 |
-
}
|
1129 |
-
|
1130 |
-
return isset( $oSpec[ 'labels' ][ $key ] ) ? $oSpec[ 'labels' ][ $key ] : null;
|
1131 |
-
}
|
1132 |
-
|
1133 |
-
/**
|
1134 |
-
* @param string $key
|
1135 |
-
* @return mixed|null
|
1136 |
-
* @deprecated 10.1.4
|
1137 |
-
*/
|
1138 |
-
protected function getPluginSpec_Menu( string $key ) {
|
1139 |
-
$aData = $this->getPluginSpec()[ 'menu' ];
|
1140 |
-
return $aData[ $key ] ?? null;
|
1141 |
-
}
|
1142 |
-
|
1143 |
/**
|
1144 |
* @param string $key
|
1145 |
* @return string|null
|
@@ -1163,40 +853,17 @@ class Controller {
|
|
1163 |
/**
|
1164 |
* @param string $key
|
1165 |
* @return mixed|null
|
1166 |
-
* @deprecated 10.
|
1167 |
*/
|
1168 |
protected function getPluginSpec_Property( string $key ) {
|
1169 |
-
|
1170 |
-
return $this->cfg->properties[ $key ];
|
1171 |
-
}
|
1172 |
-
$data = $this->getPluginSpec()[ 'properties' ];
|
1173 |
-
return $data[ $key ] ?? null;
|
1174 |
-
}
|
1175 |
-
|
1176 |
-
/**
|
1177 |
-
* @return array
|
1178 |
-
* @deprecated 10.1.4
|
1179 |
-
*/
|
1180 |
-
protected function getPluginSpec_PluginMeta() {
|
1181 |
-
$aSpec = $this->getPluginSpec();
|
1182 |
-
return ( isset( $aSpec[ 'plugin_meta' ] ) && is_array( $aSpec[ 'plugin_meta' ] ) ) ? $aSpec[ 'plugin_meta' ] : [];
|
1183 |
-
}
|
1184 |
-
|
1185 |
-
/**
|
1186 |
-
* @param string $key
|
1187 |
-
* @return mixed|null
|
1188 |
-
* @deprecated 10.1.4
|
1189 |
-
*/
|
1190 |
-
protected function getPluginSpec_Requirement( string $key ) {
|
1191 |
-
$aData = $this->getPluginSpec()[ 'requirements' ];
|
1192 |
-
return $aData[ $key ] ?? null;
|
1193 |
}
|
1194 |
|
1195 |
public function getBasePermissions() :string {
|
1196 |
if ( isset( $this->cfg ) ) {
|
1197 |
return $this->cfg->properties[ 'base_permissions' ];
|
1198 |
}
|
1199 |
-
return $this->
|
1200 |
}
|
1201 |
|
1202 |
public function isValidAdminArea( bool $bCheckUserPerms = false ) :bool {
|
@@ -1204,11 +871,11 @@ class Controller {
|
|
1204 |
return false;
|
1205 |
}
|
1206 |
|
1207 |
-
$
|
1208 |
-
if ( !$
|
1209 |
return true;
|
1210 |
}
|
1211 |
-
elseif ( $
|
1212 |
return true;
|
1213 |
}
|
1214 |
return false;
|
@@ -1244,8 +911,8 @@ class Controller {
|
|
1244 |
return $this->getPluginPrefix( '_' ).'_';
|
1245 |
}
|
1246 |
|
1247 |
-
public function getPluginPrefix( string $
|
1248 |
-
return sprintf( '%s%s%s', $this->getParentSlug(), $
|
1249 |
}
|
1250 |
|
1251 |
/**
|
@@ -1254,21 +921,15 @@ class Controller {
|
|
1254 |
*/
|
1255 |
public function getHumanName() {
|
1256 |
$labels = $this->getLabels();
|
1257 |
-
return empty( $labels[ 'Name' ] ) ? $this->
|
1258 |
}
|
1259 |
|
1260 |
-
|
1261 |
-
|
1262 |
-
*/
|
1263 |
-
public function isLoggingEnabled() {
|
1264 |
-
return $this->getPluginSpec_Property( 'logging_enabled' );
|
1265 |
}
|
1266 |
|
1267 |
-
|
1268 |
-
|
1269 |
-
*/
|
1270 |
-
public function getIsPage_PluginAdmin() {
|
1271 |
-
return ( strpos( Services::WpGeneral()->getCurrentWpAdminPage(), $this->getPluginPrefix() ) === 0 );
|
1272 |
}
|
1273 |
|
1274 |
public function getIsPage_PluginMainDashboard() :bool {
|
@@ -1277,7 +938,7 @@ class Controller {
|
|
1277 |
|
1278 |
/**
|
1279 |
* @return bool
|
1280 |
-
* @deprecated 10.
|
1281 |
*/
|
1282 |
public function getIsRebuildOptionsFromFile() :bool {
|
1283 |
return $this->rebuild_options;
|
@@ -1290,17 +951,18 @@ class Controller {
|
|
1290 |
return (bool)$this->plugin_reset;
|
1291 |
}
|
1292 |
|
1293 |
-
|
1294 |
-
|
1295 |
-
*/
|
1296 |
-
public function getIsWpmsNetworkAdminOnly() {
|
1297 |
-
return $this->getPluginSpec_Property( 'wpms_network_admin_only' );
|
1298 |
}
|
1299 |
|
1300 |
public function getParentSlug() :string {
|
1301 |
-
return $this->
|
1302 |
}
|
1303 |
|
|
|
|
|
|
|
|
|
1304 |
public function getPluginBaseFile() :string {
|
1305 |
if ( !isset( $this->base_file ) ) {
|
1306 |
$this->base_file = plugin_basename( $this->getRootFile() );
|
@@ -1309,13 +971,16 @@ class Controller {
|
|
1309 |
}
|
1310 |
|
1311 |
public function getPluginSlug() :string {
|
1312 |
-
return $this->
|
1313 |
}
|
1314 |
|
1315 |
public function getPluginUrl( string $path = '' ) :string {
|
1316 |
return add_query_arg( [ 'ver' => $this->getVersion() ], plugins_url( $path, $this->getRootFile() ) );
|
1317 |
}
|
1318 |
|
|
|
|
|
|
|
1319 |
public function getPluginUrl_Asset( string $asset ) :string {
|
1320 |
$url = '';
|
1321 |
$sAssetPath = $this->getPath_Assets( $asset );
|
@@ -1326,16 +991,25 @@ class Controller {
|
|
1326 |
return $url;
|
1327 |
}
|
1328 |
|
|
|
|
|
|
|
1329 |
public function getPluginUrl_Css( string $asset ) :string {
|
1330 |
-
return $this->
|
1331 |
}
|
1332 |
|
|
|
|
|
|
|
1333 |
public function getPluginUrl_Image( string $asset ) :string {
|
1334 |
-
return $this->
|
1335 |
}
|
1336 |
|
|
|
|
|
|
|
1337 |
public function getPluginUrl_Js( string $asset ) :string {
|
1338 |
-
return $this->
|
1339 |
}
|
1340 |
|
1341 |
public function getPluginUrl_AdminMainPage() :string {
|
@@ -1433,41 +1107,20 @@ class Controller {
|
|
1433 |
return $this->root_file;
|
1434 |
}
|
1435 |
|
1436 |
-
|
1437 |
-
|
1438 |
-
*/
|
1439 |
-
public function getReleaseTimestamp() {
|
1440 |
-
return $this->getPluginSpec_Property( 'release_timestamp' );
|
1441 |
}
|
1442 |
|
1443 |
public function getTextDomain() :string {
|
1444 |
-
return $this->
|
1445 |
}
|
1446 |
|
1447 |
-
|
1448 |
-
|
1449 |
-
*/
|
1450 |
-
public function getBuild() {
|
1451 |
-
return $this->getPluginSpec_Property( 'build' );
|
1452 |
}
|
1453 |
|
1454 |
-
|
1455 |
-
|
1456 |
-
*/
|
1457 |
-
public function getVersion() {
|
1458 |
-
return $this->getPluginSpec_Property( 'version' );
|
1459 |
-
}
|
1460 |
-
|
1461 |
-
/**
|
1462 |
-
* @return string
|
1463 |
-
* @deprecated 10.1.4
|
1464 |
-
*/
|
1465 |
-
public function getPreviousVersion() :string {
|
1466 |
-
$opts = $this->getPluginControllerOptions();
|
1467 |
-
if ( empty( $opts->previous_version ) ) {
|
1468 |
-
$opts->previous_version = $this->getVersion();
|
1469 |
-
}
|
1470 |
-
return $opts->previous_version;
|
1471 |
}
|
1472 |
|
1473 |
public function getVersionNumeric() :int {
|
@@ -1503,11 +1156,8 @@ class Controller {
|
|
1503 |
}
|
1504 |
}
|
1505 |
|
1506 |
-
|
1507 |
-
|
1508 |
-
*/
|
1509 |
-
public function isPremiumExtensionsEnabled() {
|
1510 |
-
return (bool)$this->getPluginSpec_Property( 'enable_premium' );
|
1511 |
}
|
1512 |
|
1513 |
public function isPremiumActive() :bool {
|
@@ -1519,7 +1169,6 @@ class Controller {
|
|
1519 |
}
|
1520 |
|
1521 |
protected function saveCurrentPluginControllerOptions() {
|
1522 |
-
$WP = Services::WpGeneral();
|
1523 |
add_filter( $this->prefix( 'bypass_is_plugin_admin' ), '__return_true' );
|
1524 |
|
1525 |
if ( $this->plugin_deleting ) {
|
@@ -1528,13 +1177,6 @@ class Controller {
|
|
1528 |
elseif ( isset( $this->cfg ) ) {
|
1529 |
Config\Ops\Save::ToWp( $this->cfg, $this->getConfigStoreKey() );
|
1530 |
}
|
1531 |
-
else {
|
1532 |
-
/* @deprecated 10.1.4 */
|
1533 |
-
$WP->updateOption(
|
1534 |
-
$this->getPluginControllerOptionsKey(),
|
1535 |
-
$this->getPluginControllerOptions()
|
1536 |
-
);
|
1537 |
-
}
|
1538 |
remove_filter( $this->prefix( 'bypass_is_plugin_admin' ), '__return_true' );
|
1539 |
}
|
1540 |
|
@@ -1552,17 +1194,9 @@ class Controller {
|
|
1552 |
return 'aptoweb_controller_'.substr( md5( get_class() ), 0, 6 );
|
1553 |
}
|
1554 |
|
1555 |
-
/**
|
1556 |
-
* @return string
|
1557 |
-
* @deprecated 10.1.4
|
1558 |
-
*/
|
1559 |
-
private function getPluginControllerOptionsKey() {
|
1560 |
-
return strtolower( get_class() );
|
1561 |
-
}
|
1562 |
-
|
1563 |
public function deactivateSelf() {
|
1564 |
if ( $this->isPluginAdmin() && function_exists( 'deactivate_plugins' ) ) {
|
1565 |
-
deactivate_plugins( $this->
|
1566 |
}
|
1567 |
}
|
1568 |
|
@@ -1652,8 +1286,8 @@ class Controller {
|
|
1652 |
* @throws \Exception from loadFeatureHandler()
|
1653 |
*/
|
1654 |
public function loadCorePluginFeatureHandler() {
|
1655 |
-
|
1656 |
-
|
1657 |
$this->loadFeatureHandler(
|
1658 |
[
|
1659 |
'slug' => 'plugin',
|
@@ -1697,11 +1331,11 @@ class Controller {
|
|
1697 |
|
1698 |
/**
|
1699 |
* @param string $slug
|
1700 |
-
* @return \
|
1701 |
*/
|
1702 |
public function getModule( string $slug ) {
|
1703 |
$mod = isset( $this->modules[ $slug ] ) ? $this->modules[ $slug ] : null;
|
1704 |
-
if ( !$mod instanceof \
|
1705 |
try {
|
1706 |
$mods = $this->loadCorePluginFeatureHandler()->getActivePluginFeatures();
|
1707 |
if ( isset( $mods[ $slug ] ) ) {
|
@@ -1788,13 +1422,13 @@ class Controller {
|
|
1788 |
|
1789 |
/**
|
1790 |
* @param array $modProps
|
1791 |
-
* @return \
|
1792 |
* @throws \Exception
|
1793 |
*/
|
1794 |
public function loadFeatureHandler( array $modProps ) {
|
1795 |
$modSlug = $modProps[ 'slug' ];
|
1796 |
$mod = isset( $this->modules[ $modSlug ] ) ? $this->modules[ $modSlug ] : null;
|
1797 |
-
if ( $mod instanceof
|
1798 |
return $mod;
|
1799 |
}
|
1800 |
|
@@ -1992,4 +1626,61 @@ class Controller {
|
|
1992 |
->run();
|
1993 |
}
|
1994 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1995 |
}
|
11 |
* Class Controller
|
12 |
* @package FernleafSystems\Wordpress\Plugin\Shield\Controller
|
13 |
* @property Config\ConfigVO $cfg
|
14 |
+
* @property Shield\Controller\Assets\Urls $urls
|
15 |
* @property bool $is_activating
|
16 |
* @property bool $is_debug
|
17 |
* @property bool $modules_loaded
|
43 |
*/
|
44 |
public static $oInstance;
|
45 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
46 |
/**
|
47 |
* @var array
|
48 |
*/
|
127 |
* @throws \Exception
|
128 |
*/
|
129 |
protected function __construct( string $rootFile ) {
|
|
|
130 |
$this->root_file = $rootFile;
|
131 |
+
$this->base_file = plugin_basename( $this->getRootFile() );
|
132 |
$this->modules = [];
|
133 |
|
134 |
$this->loadServices();
|
157 |
}
|
158 |
break;
|
159 |
|
160 |
+
case 'urls':
|
161 |
+
if ( !$val instanceof Shield\Controller\Assets\Urls ) {
|
162 |
+
$val = ( new Shield\Controller\Assets\Urls() )->setCon( $this );
|
163 |
+
}
|
164 |
+
break;
|
165 |
+
|
166 |
case 'is_debug':
|
167 |
if ( is_null( $val ) ) {
|
168 |
$val = ( new Shield\Controller\Utilities\DebugMode() )
|
186 |
Services::GetInstance();
|
187 |
}
|
188 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
189 |
/**
|
190 |
* @param bool $bCheckOnlyFrontEnd
|
191 |
* @throws \Exception
|
292 |
|
293 |
public function onWpActivatePlugin() {
|
294 |
$this->is_activating = true;
|
295 |
+
$modPlugin = $this->getModule_Plugin();
|
296 |
+
if ( $modPlugin instanceof Shield\Modules\Base\ModCon ) {
|
297 |
+
$modPlugin->setActivatedAt();
|
298 |
}
|
299 |
}
|
300 |
|
340 |
add_action( 'wp_loaded', [ $this, 'onWpLoaded' ] );
|
341 |
add_action( 'admin_init', [ $this, 'onWpAdminInit' ] );
|
342 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
343 |
add_filter( 'all_plugins', [ $this, 'filter_hidePluginFromTableList' ] );
|
344 |
add_filter( 'all_plugins', [ $this, 'doPluginLabels' ] );
|
345 |
+
add_filter( 'plugin_action_links_'.$this->base_file, [ $this, 'onWpPluginActionLinks' ], 50, 1 );
|
346 |
add_filter( 'plugin_row_meta', [ $this, 'onPluginRowMeta' ], 50, 2 );
|
347 |
add_filter( 'site_transient_update_plugins', [ $this, 'filter_hidePluginUpdatesFromUI' ] );
|
348 |
+
add_action( 'in_plugin_update_message-'.$this->base_file, [ $this, 'onWpPluginUpdateMessage' ] );
|
349 |
add_filter( 'site_transient_update_plugins', [ $this, 'blockIncompatibleUpdates' ] );
|
350 |
add_filter( 'auto_update_plugin', [ $this, 'onWpAutoUpdate' ], 500, 2 );
|
351 |
add_filter( 'set_site_transient_update_plugins', [ $this, 'setUpdateFirstDetectedAt' ] );
|
369 |
}
|
370 |
|
371 |
public function onWpAdminInit() {
|
372 |
+
( new Admin\AdminBarMenu() )
|
373 |
+
->setCon( $this )
|
374 |
+
->execute();
|
375 |
+
( new Admin\DashboardWidget() )
|
376 |
+
->setCon( $this )
|
377 |
+
->execute();
|
378 |
|
379 |
if ( Services::Request()->query( $this->prefix( 'runtests' ) ) && $this->isPluginAdmin() ) {
|
380 |
$this->runTests();
|
388 |
|
389 |
/**
|
390 |
* In order to prevent certain errors when the back button is used
|
391 |
+
* @param array $headers
|
392 |
* @return array
|
393 |
*/
|
394 |
+
public function adjustNocacheHeaders( $headers ) {
|
395 |
+
if ( is_array( $headers ) && !empty( $headers[ 'Cache-Control' ] ) ) {
|
396 |
+
$Hs = array_map( 'trim', explode( ',', $headers[ 'Cache-Control' ] ) );
|
397 |
+
$Hs[] = 'no-store';
|
398 |
+
$headers[ 'Cache-Control' ] = implode( ', ', array_unique( $Hs ) );
|
399 |
}
|
400 |
+
return $headers;
|
401 |
}
|
402 |
|
403 |
public function onWpInit() {
|
404 |
$this->getMeetsBasePermissions();
|
|
|
|
|
405 |
if ( $this->isModulePage() ) {
|
406 |
add_filter( 'nocache_headers', [ $this, 'adjustNocacheHeaders' ] );
|
407 |
}
|
408 |
+
( new Ajax\Init() )
|
409 |
+
->setCon( $this )
|
410 |
+
->execute();
|
411 |
}
|
412 |
|
413 |
/**
|
416 |
* @return string - the unique, never-changing site install ID.
|
417 |
*/
|
418 |
public function getSiteInstallationId() {
|
419 |
+
$WP = Services::WpGeneral();
|
420 |
$sOptKey = $this->prefixOption( 'install_id' );
|
421 |
|
422 |
+
$mStoredID = $WP->getOption( $sOptKey );
|
423 |
if ( is_array( $mStoredID ) && !empty( $mStoredID[ 'id' ] ) ) {
|
424 |
$sID = $mStoredID[ 'id' ];
|
425 |
$bUpdate = true;
|
438 |
$sID = \Ramsey\Uuid\Uuid::uuid4()->toString();
|
439 |
}
|
440 |
catch ( \Exception $e ) {
|
441 |
+
$sID = sha1( uniqid( $WP->getHomeUrl( '', true ), true ) );
|
442 |
}
|
443 |
$bUpdate = true;
|
444 |
}
|
445 |
|
446 |
if ( $bUpdate ) {
|
447 |
+
$WP->updateOption( $sOptKey, $sID );
|
448 |
}
|
449 |
|
450 |
return $sID;
|
452 |
|
453 |
/**
|
454 |
* TODO: Use to set ID after license verify where applicable
|
455 |
+
* @param string $ID
|
456 |
*/
|
457 |
+
public function setSiteInstallID( $ID ) {
|
458 |
+
if ( !empty( $ID ) && ( \Ramsey\Uuid\Uuid::isValid( $ID ) ) ) {
|
459 |
+
Services::WpGeneral()->updateOption( $this->prefixOption( 'install_id' ), $ID );
|
460 |
}
|
461 |
}
|
462 |
|
463 |
public function onWpLoaded() {
|
464 |
$this->getAdminNotices();
|
465 |
$this->initCrons();
|
466 |
+
( new Shield\Controller\Assets\Enqueue() )
|
467 |
+
->setCon( $this )
|
468 |
+
->execute();
|
469 |
+
( new Admin\MainAdminMenu() )
|
470 |
+
->setCon( $this )
|
471 |
+
->execute();
|
472 |
}
|
473 |
|
474 |
protected function initCrons() {
|
480 |
->run();
|
481 |
}
|
482 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
483 |
/**
|
484 |
* @return Shield\Utilities\AdminNotices\Controller
|
485 |
*/
|
507 |
];
|
508 |
}
|
509 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
510 |
/**
|
511 |
+
* @param array $pluginMeta
|
512 |
+
* @param string $pluginFile
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
513 |
* @return array
|
514 |
*/
|
515 |
+
public function onPluginRowMeta( $pluginMeta, $pluginFile ) {
|
516 |
|
517 |
+
if ( $pluginFile === $this->base_file ) {
|
518 |
$sTemplate = '<strong><a href="%s" target="_blank">%s</a></strong>';
|
519 |
foreach ( $this->cfg->plugin_meta as $aHref ) {
|
520 |
+
array_push( $pluginMeta, sprintf( $sTemplate, $aHref[ 'href' ], $aHref[ 'name' ] ) );
|
521 |
}
|
522 |
}
|
523 |
+
return $pluginMeta;
|
524 |
}
|
525 |
|
526 |
/**
|
538 |
$links = $this->cfg->action_links[ 'add' ];
|
539 |
if ( is_array( $links ) ) {
|
540 |
|
541 |
+
$isPro = $this->isPremiumActive();
|
542 |
+
$DP = Services::Data();
|
543 |
$sLinkTemplate = '<a href="%s" target="%s" title="%s">%s</a>';
|
544 |
foreach ( $links as $aLink ) {
|
545 |
$aLink = array_merge(
|
554 |
$aLink
|
555 |
);
|
556 |
|
557 |
+
$show = $aLink[ 'show' ];
|
558 |
+
$bShow = ( $show == 'always' ) || ( $isPro && $show == 'pro' ) || ( !$isPro && $show == 'free' );
|
559 |
+
if ( !$DP->isValidWebUrl( $aLink[ 'href' ] ) && method_exists( $this, $aLink[ 'href' ] ) ) {
|
560 |
$aLink[ 'href' ] = $this->{$aLink[ 'href' ]}();
|
561 |
}
|
562 |
|
563 |
+
if ( !$bShow || !$DP->isValidWebUrl( $aLink[ 'href' ] )
|
564 |
|| empty( $aLink[ 'name' ] ) || empty( $aLink[ 'href' ] ) ) {
|
565 |
continue;
|
566 |
}
|
582 |
return $aActionLinks;
|
583 |
}
|
584 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
585 |
/**
|
586 |
* Displays a message in the plugins listing when a plugin has an update available.
|
587 |
*/
|
588 |
public function onWpPluginUpdateMessage() {
|
589 |
+
echo sprintf(
|
590 |
+
' <span class="%s plugin_update_message">%s</span>',
|
591 |
+
$this->getPluginPrefix(),
|
592 |
+
__( 'Update Now To Keep Your Security Current With The Latest Features.', 'wp-simple-firewall' )
|
593 |
+
);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
594 |
}
|
595 |
|
596 |
/**
|
597 |
* Prevents upgrades to Shield versions when the system PHP version is too old.
|
598 |
+
* @param \stdClass $updates
|
599 |
* @return \stdClass
|
600 |
*/
|
601 |
+
public function blockIncompatibleUpdates( $updates ) {
|
602 |
+
$file = $this->base_file;
|
603 |
+
if ( !empty( $updates->response ) && isset( $updates->response[ $file ] ) ) {
|
604 |
+
$reqs = $this->cfg->upgrade_reqs;
|
605 |
+
if ( is_array( $reqs ) ) {
|
606 |
+
foreach ( $reqs as $sShieldVer => $aReqs ) {
|
607 |
+
$bNeedsHidden = version_compare( $updates->response[ $file ]->new_version, $sShieldVer, '>=' )
|
608 |
&& (
|
609 |
!Services::Data()->getPhpVersionIsAtLeast( $aReqs[ 'php' ] )
|
610 |
|| !Services::WpGeneral()->getWordpressIsAtLeastVersion( $aReqs[ 'wp' ] )
|
611 |
);
|
612 |
if ( $bNeedsHidden ) {
|
613 |
+
unset( $updates->response[ $file ] );
|
614 |
break;
|
615 |
}
|
616 |
}
|
617 |
}
|
618 |
}
|
619 |
+
return $updates;
|
620 |
}
|
621 |
|
622 |
/**
|
627 |
*/
|
628 |
public function setUpdateFirstDetectedAt( $data ) {
|
629 |
|
630 |
+
if ( !empty( $data ) && !empty( $data->response ) && isset( $data->response[ $this->base_file ] ) ) {
|
631 |
// i.e. update available
|
632 |
|
633 |
+
$new = Services::WpPlugins()->getUpdateNewVersion( $this->base_file );
|
634 |
if ( !empty( $new ) && isset( $this->cfg ) ) {
|
635 |
$updates = $this->cfg->update_first_detected;
|
636 |
if ( count( $updates ) > 3 ) {
|
657 |
$WP = Services::WpGeneral();
|
658 |
$oWpPlugins = Services::WpPlugins();
|
659 |
|
660 |
+
$file = $WP->getFileFromAutomaticUpdateItem( $mItem );
|
661 |
|
662 |
// The item in question is this plugin...
|
663 |
+
if ( $file === $this->base_file ) {
|
664 |
$autoupdateSelf = $this->cfg->properties[ 'autoupdate' ];
|
665 |
|
666 |
if ( !$WP->isRunningAutomaticUpdates() && $autoupdateSelf == 'confidence' ) {
|
667 |
$autoupdateSelf = 'yes'; // so that we appear to be automatically updating
|
668 |
}
|
669 |
|
670 |
+
$new = $oWpPlugins->getUpdateNewVersion( $file );
|
671 |
|
672 |
switch ( $autoupdateSelf ) {
|
673 |
|
702 |
* @return array
|
703 |
*/
|
704 |
public function doPluginLabels( $aPlugins ) {
|
705 |
+
$labels = $this->getLabels();
|
706 |
+
if ( empty( $labels ) ) {
|
707 |
return $aPlugins;
|
708 |
}
|
709 |
|
710 |
+
$file = $this->base_file;
|
711 |
// For this plugin, overwrite any specified settings
|
712 |
+
if ( array_key_exists( $file, $aPlugins ) ) {
|
713 |
+
foreach ( $labels as $sLabelKey => $sLabel ) {
|
714 |
+
$aPlugins[ $file ][ $sLabelKey ] = $sLabel;
|
715 |
}
|
716 |
}
|
717 |
|
768 |
*/
|
769 |
public function filter_hidePluginFromTableList( $plugins ) {
|
770 |
if ( apply_filters( $this->prefix( 'hide_plugin' ), false ) ) {
|
771 |
+
unset( $plugins[ $this->base_file ] );
|
772 |
}
|
773 |
return $plugins;
|
774 |
}
|
783 |
*/
|
784 |
public function filter_hidePluginUpdatesFromUI( $plugins ) {
|
785 |
if ( !Services::WpGeneral()->isCron() && apply_filters( $this->prefix( 'hide_plugin_updates' ), false ) ) {
|
786 |
+
unset( $plugins->response[ $this->base_file ] );
|
787 |
}
|
788 |
return $plugins;
|
789 |
}
|
794 |
* @return string
|
795 |
*/
|
796 |
public function prefix( $suffix = '', $glue = '-' ) {
|
797 |
+
$prefix = $this->getPluginPrefix( $glue );
|
798 |
|
799 |
+
if ( $suffix == $prefix || strpos( $suffix, $prefix.$glue ) === 0 ) { //it already has the full prefix
|
800 |
return $suffix;
|
801 |
}
|
802 |
|
803 |
+
return sprintf( '%s%s%s', $prefix, empty( $suffix ) ? '' : $glue, empty( $suffix ) ? '' : $suffix );
|
804 |
}
|
805 |
|
806 |
public function prefixOption( string $suffix = '' ) :string {
|
830 |
return $this->getPluginControllerOptions()->plugin_spec;
|
831 |
}
|
832 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
833 |
/**
|
834 |
* @param string $key
|
835 |
* @return string|null
|
853 |
/**
|
854 |
* @param string $key
|
855 |
* @return mixed|null
|
856 |
+
* @deprecated 10.2.0 - getCfgProperty()
|
857 |
*/
|
858 |
protected function getPluginSpec_Property( string $key ) {
|
859 |
+
return $this->cfg->properties[ $key ] ?? null;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
860 |
}
|
861 |
|
862 |
public function getBasePermissions() :string {
|
863 |
if ( isset( $this->cfg ) ) {
|
864 |
return $this->cfg->properties[ 'base_permissions' ];
|
865 |
}
|
866 |
+
return $this->getCfgProperty( 'base_permissions' );
|
867 |
}
|
868 |
|
869 |
public function isValidAdminArea( bool $bCheckUserPerms = false ) :bool {
|
871 |
return false;
|
872 |
}
|
873 |
|
874 |
+
$WP = Services::WpGeneral();
|
875 |
+
if ( !$WP->isMultisite() && is_admin() ) {
|
876 |
return true;
|
877 |
}
|
878 |
+
elseif ( $WP->isMultisite() && $this->getIsWpmsNetworkAdminOnly() && ( is_network_admin() || $WP->isAjax() ) ) {
|
879 |
return true;
|
880 |
}
|
881 |
return false;
|
911 |
return $this->getPluginPrefix( '_' ).'_';
|
912 |
}
|
913 |
|
914 |
+
public function getPluginPrefix( string $glue = '-' ) :string {
|
915 |
+
return sprintf( '%s%s%s', $this->getParentSlug(), $glue, $this->getPluginSlug() );
|
916 |
}
|
917 |
|
918 |
/**
|
921 |
*/
|
922 |
public function getHumanName() {
|
923 |
$labels = $this->getLabels();
|
924 |
+
return empty( $labels[ 'Name' ] ) ? $this->getCfgProperty( 'human_name' ) : $labels[ 'Name' ];
|
925 |
}
|
926 |
|
927 |
+
public function isLoggingEnabled() :bool {
|
928 |
+
return (bool)$this->getCfgProperty( 'logging_enabled' );
|
|
|
|
|
|
|
929 |
}
|
930 |
|
931 |
+
public function getIsPage_PluginAdmin() :bool {
|
932 |
+
return strpos( Services::WpGeneral()->getCurrentWpAdminPage(), $this->getPluginPrefix() ) === 0;
|
|
|
|
|
|
|
933 |
}
|
934 |
|
935 |
public function getIsPage_PluginMainDashboard() :bool {
|
938 |
|
939 |
/**
|
940 |
* @return bool
|
941 |
+
* @deprecated 10.2
|
942 |
*/
|
943 |
public function getIsRebuildOptionsFromFile() :bool {
|
944 |
return $this->rebuild_options;
|
951 |
return (bool)$this->plugin_reset;
|
952 |
}
|
953 |
|
954 |
+
public function getIsWpmsNetworkAdminOnly() :bool {
|
955 |
+
return (bool)$this->getCfgProperty( 'wpms_network_admin_only' );
|
|
|
|
|
|
|
956 |
}
|
957 |
|
958 |
public function getParentSlug() :string {
|
959 |
+
return $this->getCfgProperty( 'slug_parent' );
|
960 |
}
|
961 |
|
962 |
+
/**
|
963 |
+
* @return string
|
964 |
+
* @deprecated 10.2.0
|
965 |
+
*/
|
966 |
public function getPluginBaseFile() :string {
|
967 |
if ( !isset( $this->base_file ) ) {
|
968 |
$this->base_file = plugin_basename( $this->getRootFile() );
|
971 |
}
|
972 |
|
973 |
public function getPluginSlug() :string {
|
974 |
+
return $this->getCfgProperty( 'slug_plugin' );
|
975 |
}
|
976 |
|
977 |
public function getPluginUrl( string $path = '' ) :string {
|
978 |
return add_query_arg( [ 'ver' => $this->getVersion() ], plugins_url( $path, $this->getRootFile() ) );
|
979 |
}
|
980 |
|
981 |
+
/**
|
982 |
+
* @deprecated 10.2
|
983 |
+
*/
|
984 |
public function getPluginUrl_Asset( string $asset ) :string {
|
985 |
$url = '';
|
986 |
$sAssetPath = $this->getPath_Assets( $asset );
|
991 |
return $url;
|
992 |
}
|
993 |
|
994 |
+
/**
|
995 |
+
* @deprecated 10.2
|
996 |
+
*/
|
997 |
public function getPluginUrl_Css( string $asset ) :string {
|
998 |
+
return $this->urls->forCss( $asset );
|
999 |
}
|
1000 |
|
1001 |
+
/**
|
1002 |
+
* @deprecated 10.2
|
1003 |
+
*/
|
1004 |
public function getPluginUrl_Image( string $asset ) :string {
|
1005 |
+
return $this->urls->forImage( $asset );
|
1006 |
}
|
1007 |
|
1008 |
+
/**
|
1009 |
+
* @deprecated 10.2
|
1010 |
+
*/
|
1011 |
public function getPluginUrl_Js( string $asset ) :string {
|
1012 |
+
return $this->urls->forJs( $asset );
|
1013 |
}
|
1014 |
|
1015 |
public function getPluginUrl_AdminMainPage() :string {
|
1107 |
return $this->root_file;
|
1108 |
}
|
1109 |
|
1110 |
+
public function getReleaseTimestamp() :int {
|
1111 |
+
return $this->getCfgProperty( 'release_timestamp' );
|
|
|
|
|
|
|
1112 |
}
|
1113 |
|
1114 |
public function getTextDomain() :string {
|
1115 |
+
return $this->getCfgProperty( 'text_domain' );
|
1116 |
}
|
1117 |
|
1118 |
+
public function getBuild() :string {
|
1119 |
+
return $this->getCfgProperty( 'build' );
|
|
|
|
|
|
|
1120 |
}
|
1121 |
|
1122 |
+
public function getVersion() :string {
|
1123 |
+
return $this->getCfgProperty( 'version' );
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1124 |
}
|
1125 |
|
1126 |
public function getVersionNumeric() :int {
|
1156 |
}
|
1157 |
}
|
1158 |
|
1159 |
+
public function isPremiumExtensionsEnabled() :bool {
|
1160 |
+
return (bool)$this->getCfgProperty( 'enable_premium' );
|
|
|
|
|
|
|
1161 |
}
|
1162 |
|
1163 |
public function isPremiumActive() :bool {
|
1169 |
}
|
1170 |
|
1171 |
protected function saveCurrentPluginControllerOptions() {
|
|
|
1172 |
add_filter( $this->prefix( 'bypass_is_plugin_admin' ), '__return_true' );
|
1173 |
|
1174 |
if ( $this->plugin_deleting ) {
|
1177 |
elseif ( isset( $this->cfg ) ) {
|
1178 |
Config\Ops\Save::ToWp( $this->cfg, $this->getConfigStoreKey() );
|
1179 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1180 |
remove_filter( $this->prefix( 'bypass_is_plugin_admin' ), '__return_true' );
|
1181 |
}
|
1182 |
|
1194 |
return 'aptoweb_controller_'.substr( md5( get_class() ), 0, 6 );
|
1195 |
}
|
1196 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1197 |
public function deactivateSelf() {
|
1198 |
if ( $this->isPluginAdmin() && function_exists( 'deactivate_plugins' ) ) {
|
1199 |
+
deactivate_plugins( [ $this->base_file ] );
|
1200 |
}
|
1201 |
}
|
1202 |
|
1286 |
* @throws \Exception from loadFeatureHandler()
|
1287 |
*/
|
1288 |
public function loadCorePluginFeatureHandler() {
|
1289 |
+
$plugin = $this->modules[ 'plugin' ] ?? null;
|
1290 |
+
if ( !$plugin instanceof Shield\Modules\Plugin\ModCon ) {
|
1291 |
$this->loadFeatureHandler(
|
1292 |
[
|
1293 |
'slug' => 'plugin',
|
1331 |
|
1332 |
/**
|
1333 |
* @param string $slug
|
1334 |
+
* @return Shield\Modules\Base\ModCon|null|mixed
|
1335 |
*/
|
1336 |
public function getModule( string $slug ) {
|
1337 |
$mod = isset( $this->modules[ $slug ] ) ? $this->modules[ $slug ] : null;
|
1338 |
+
if ( !$mod instanceof Shield\Modules\Base\ModCon ) {
|
1339 |
try {
|
1340 |
$mods = $this->loadCorePluginFeatureHandler()->getActivePluginFeatures();
|
1341 |
if ( isset( $mods[ $slug ] ) ) {
|
1422 |
|
1423 |
/**
|
1424 |
* @param array $modProps
|
1425 |
+
* @return Shield\Modules\Base\ModCon|mixed
|
1426 |
* @throws \Exception
|
1427 |
*/
|
1428 |
public function loadFeatureHandler( array $modProps ) {
|
1429 |
$modSlug = $modProps[ 'slug' ];
|
1430 |
$mod = isset( $this->modules[ $modSlug ] ) ? $this->modules[ $modSlug ] : null;
|
1431 |
+
if ( $mod instanceof Shield\Modules\Base\ModCon ) {
|
1432 |
return $mod;
|
1433 |
}
|
1434 |
|
1626 |
->run();
|
1627 |
}
|
1628 |
}
|
1629 |
+
|
1630 |
+
/**
|
1631 |
+
* @deprecated 10.2
|
1632 |
+
*/
|
1633 |
+
public function onWpAdminMenu() {
|
1634 |
+
}
|
1635 |
+
|
1636 |
+
/**
|
1637 |
+
* @param \WP_Admin_Bar $adminBar
|
1638 |
+
* @deprecated 10.2
|
1639 |
+
*/
|
1640 |
+
public function onWpAdminBarMenu( $adminBar ) {
|
1641 |
+
}
|
1642 |
+
|
1643 |
+
/**
|
1644 |
+
* @deprecated 10.2
|
1645 |
+
*/
|
1646 |
+
public function onWpDashboardSetup() {
|
1647 |
+
}
|
1648 |
+
|
1649 |
+
/**
|
1650 |
+
* @deprecated 10.2
|
1651 |
+
*/
|
1652 |
+
protected function createPluginMenu() :bool {
|
1653 |
+
}
|
1654 |
+
|
1655 |
+
/**
|
1656 |
+
* @deprecated 10.2
|
1657 |
+
*/
|
1658 |
+
protected function fixSubmenu() {
|
1659 |
+
}
|
1660 |
+
|
1661 |
+
/**
|
1662 |
+
* Displaying all views now goes through this central function and we work out
|
1663 |
+
* what to display based on the name of current hook/filter being processed.
|
1664 |
+
* @deprecated 10.2
|
1665 |
+
*/
|
1666 |
+
public function onDisplayTopMenu() {
|
1667 |
+
}
|
1668 |
+
|
1669 |
+
/**
|
1670 |
+
* @deprecated 10.2
|
1671 |
+
*/
|
1672 |
+
public function onWpEnqueueFrontendCss() {
|
1673 |
+
}
|
1674 |
+
|
1675 |
+
/**
|
1676 |
+
* @deprecated 10.2
|
1677 |
+
*/
|
1678 |
+
public function onWpEnqueueAdminJs() {
|
1679 |
+
}
|
1680 |
+
|
1681 |
+
/**
|
1682 |
+
* @deprecated 10.2
|
1683 |
+
*/
|
1684 |
+
public function onWpEnqueueAdminCss() {
|
1685 |
+
}
|
1686 |
}
|
src/lib/src/Controller/Utilities/Upgrade.php
CHANGED
@@ -1,26 +1,45 @@
|
|
1 |
-
<?php
|
2 |
|
3 |
namespace FernleafSystems\Wordpress\Plugin\Shield\Controller\Utilities;
|
4 |
|
|
|
5 |
use FernleafSystems\Wordpress\Plugin\Shield;
|
|
|
6 |
|
7 |
class Upgrade {
|
8 |
|
9 |
use Shield\Modules\PluginControllerConsumer;
|
10 |
-
use
|
|
|
|
|
|
|
|
|
|
|
11 |
|
12 |
protected function run() {
|
13 |
$con = $this->getCon();
|
14 |
|
15 |
-
|
16 |
-
|
17 |
-
|
18 |
-
|
19 |
-
$H->execute();
|
20 |
-
}
|
21 |
-
}
|
22 |
-
}
|
23 |
|
24 |
$con->cfg->previous_version = $con->getVersion();
|
25 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
26 |
}
|
1 |
+
<?php declare( strict_types=1 );
|
2 |
|
3 |
namespace FernleafSystems\Wordpress\Plugin\Shield\Controller\Utilities;
|
4 |
|
5 |
+
use FernleafSystems\Utilities\Logic\ExecOnce;
|
6 |
use FernleafSystems\Wordpress\Plugin\Shield;
|
7 |
+
use FernleafSystems\Wordpress\Services\Services;
|
8 |
|
9 |
class Upgrade {
|
10 |
|
11 |
use Shield\Modules\PluginControllerConsumer;
|
12 |
+
use ExecOnce;
|
13 |
+
|
14 |
+
protected function canRun() :bool {
|
15 |
+
$con = $this->getCon();
|
16 |
+
return $con->cfg->previous_version !== $con->getVersion();
|
17 |
+
}
|
18 |
|
19 |
protected function run() {
|
20 |
$con = $this->getCon();
|
21 |
|
22 |
+
$this->upgradeModules();
|
23 |
+
do_action( $con->prefix( 'plugin_shutdown' ), function () {
|
24 |
+
$this->deleteOldModConfigs();
|
25 |
+
} );
|
|
|
|
|
|
|
|
|
26 |
|
27 |
$con->cfg->previous_version = $con->getVersion();
|
28 |
}
|
29 |
+
|
30 |
+
private function deleteOldModConfigs() {
|
31 |
+
$DB = Services::WpDb();
|
32 |
+
$DB->doSql(
|
33 |
+
sprintf( 'DELETE from `%s` where `option_name` LIKE "shield_mod_config_%%"', $DB->getTable_Options() )
|
34 |
+
);
|
35 |
+
}
|
36 |
+
|
37 |
+
private function upgradeModules() {
|
38 |
+
foreach ( $this->getCon()->modules as $mod ) {
|
39 |
+
$H = $mod->getUpgradeHandler();
|
40 |
+
if ( $H instanceof Shield\Modules\Base\Upgrade ) {
|
41 |
+
$H->execute();
|
42 |
+
}
|
43 |
+
}
|
44 |
+
}
|
45 |
}
|
src/lib/src/Crons/StandardCron.php
CHANGED
@@ -18,7 +18,7 @@ trait StandardCron {
|
|
18 |
->setNextRun( $this->getFirstRunTimestamp() )
|
19 |
->createCronJob( $this->getCronName(), [ $this, 'runCron' ] );
|
20 |
}
|
21 |
-
catch ( \Exception $
|
22 |
}
|
23 |
add_action( $this->getCon()->prefix( 'deactivate_plugin' ), [ $this, 'deleteCron' ] );
|
24 |
}
|
18 |
->setNextRun( $this->getFirstRunTimestamp() )
|
19 |
->createCronJob( $this->getCronName(), [ $this, 'runCron' ] );
|
20 |
}
|
21 |
+
catch ( \Exception $e ) {
|
22 |
}
|
23 |
add_action( $this->getCon()->prefix( 'deactivate_plugin' ), [ $this, 'deleteCron' ] );
|
24 |
}
|
src/lib/src/Databases/Base/EntryVoConsumer.php
CHANGED
@@ -21,11 +21,11 @@ trait EntryVoConsumer {
|
|
21 |
}
|
22 |
|
23 |
/**
|
24 |
-
* @param EntryVO $
|
25 |
* @return $this
|
26 |
*/
|
27 |
-
public function setEntryVO( $
|
28 |
-
$this->oEntryVO = $
|
29 |
return $this;
|
30 |
}
|
31 |
}
|
21 |
}
|
22 |
|
23 |
/**
|
24 |
+
* @param EntryVO $entry
|
25 |
* @return $this
|
26 |
*/
|
27 |
+
public function setEntryVO( $entry ) {
|
28 |
+
$this->oEntryVO = $entry;
|
29 |
return $this;
|
30 |
}
|
31 |
}
|
src/lib/src/Databases/Base/Handler.php
CHANGED
@@ -166,10 +166,9 @@ abstract class Handler {
|
|
166 |
|
167 |
public function tableTrimExcess( int $nRowsLimit ) :self {
|
168 |
try {
|
169 |
-
$this->getQueryDeleter()
|
170 |
-
->deleteExcess( $nRowsLimit );
|
171 |
}
|
172 |
-
catch ( \Exception $
|
173 |
}
|
174 |
return $this;
|
175 |
}
|
@@ -228,17 +227,14 @@ abstract class Handler {
|
|
228 |
return $sch;
|
229 |
}
|
230 |
|
231 |
-
|
232 |
-
* @return string
|
233 |
-
*/
|
234 |
-
private function getNamespace() {
|
235 |
try {
|
236 |
-
$
|
237 |
}
|
238 |
-
catch ( \Exception $
|
239 |
-
$
|
240 |
}
|
241 |
-
return rtrim( $
|
242 |
}
|
243 |
|
244 |
/**
|
166 |
|
167 |
public function tableTrimExcess( int $nRowsLimit ) :self {
|
168 |
try {
|
169 |
+
$this->getQueryDeleter()->deleteExcess( $nRowsLimit );
|
|
|
170 |
}
|
171 |
+
catch ( \Exception $e ) {
|
172 |
}
|
173 |
return $this;
|
174 |
}
|
227 |
return $sch;
|
228 |
}
|
229 |
|
230 |
+
private function getNamespace() :string {
|
|
|
|
|
|
|
231 |
try {
|
232 |
+
$namespace = ( new \ReflectionClass( $this ) )->getNamespaceName();
|
233 |
}
|
234 |
+
catch ( \Exception $e ) {
|
235 |
+
$namespace = __NAMESPACE__;
|
236 |
}
|
237 |
+
return rtrim( $namespace, '\\' ).'\\';
|
238 |
}
|
239 |
|
240 |
/**
|
src/lib/src/Databases/Base/Insert.php
CHANGED
@@ -72,16 +72,16 @@ class Insert extends BaseQuery {
|
|
72 |
public function query() {
|
73 |
try {
|
74 |
$this->verifyInsertData();
|
75 |
-
$
|
76 |
-
|
77 |
-
|
78 |
-
|
79 |
-
|
80 |
}
|
81 |
-
catch ( \Exception $
|
82 |
-
$
|
83 |
}
|
84 |
-
return $
|
85 |
}
|
86 |
|
87 |
/**
|
72 |
public function query() {
|
73 |
try {
|
74 |
$this->verifyInsertData();
|
75 |
+
$success = Services::WpDb()
|
76 |
+
->insertDataIntoTable(
|
77 |
+
$this->getDbH()->getTable(),
|
78 |
+
$this->getInsertData()
|
79 |
+
);
|
80 |
}
|
81 |
+
catch ( \Exception $e ) {
|
82 |
+
$success = false;
|
83 |
}
|
84 |
+
return $success;
|
85 |
}
|
86 |
|
87 |
/**
|
src/lib/src/Databases/IPs/CommonFilters.php
CHANGED
@@ -40,14 +40,18 @@ trait CommonFilters {
|
|
40 |
}
|
41 |
|
42 |
/**
|
43 |
-
* @param bool $
|
44 |
* @return $this
|
45 |
*/
|
46 |
-
public function filterByIsRange( $
|
47 |
-
return $this->addWhereEquals( 'is_range', $
|
48 |
}
|
49 |
|
50 |
-
|
|
|
|
|
|
|
|
|
51 |
return $this->addWhereEquals( 'label', $label );
|
52 |
}
|
53 |
|
40 |
}
|
41 |
|
42 |
/**
|
43 |
+
* @param bool $isRange
|
44 |
* @return $this
|
45 |
*/
|
46 |
+
public function filterByIsRange( bool $isRange ) {
|
47 |
+
return $this->addWhereEquals( 'is_range', $isRange ? 1 : 0 );
|
48 |
}
|
49 |
|
50 |
+
/**
|
51 |
+
* @param string $label
|
52 |
+
* @return $this
|
53 |
+
*/
|
54 |
+
public function filterByLabel( string $label ) {
|
55 |
return $this->addWhereEquals( 'label', $label );
|
56 |
}
|
57 |
|
src/lib/src/Modules/AuditTrail/Options.php
CHANGED
@@ -15,75 +15,4 @@ class Options extends BaseShield\Options {
|
|
15 |
(int)$this->getOpt( 'audit_trail_max_entries' ) :
|
16 |
(int)$this->getDef( 'audit_trail_free_max_entries' );
|
17 |
}
|
18 |
-
|
19 |
-
/**
|
20 |
-
* @return bool
|
21 |
-
* @deprecated 10.1
|
22 |
-
*/
|
23 |
-
public function isEnabledAuditing() :bool {
|
24 |
-
return true;
|
25 |
-
}
|
26 |
-
|
27 |
-
/**
|
28 |
-
* @return bool
|
29 |
-
* @deprecated 10.1
|
30 |
-
*/
|
31 |
-
public function isAuditEmails() :bool {
|
32 |
-
return $this->isOpt( 'enable_audit_context_emails', 'Y' );
|
33 |
-
}
|
34 |
-
|
35 |
-
/**
|
36 |
-
* @return bool
|
37 |
-
* @deprecated 10.1
|
38 |
-
*/
|
39 |
-
public function isAuditPlugins() :bool {
|
40 |
-
return $this->isOpt( 'enable_audit_context_plugins', 'Y' );
|
41 |
-
}
|
42 |
-
|
43 |
-
/**
|
44 |
-
* @return bool
|
45 |
-
* @deprecated 10.1
|
46 |
-
*/
|
47 |
-
public function isAuditPosts() :bool {
|
48 |
-
return $this->isOpt( 'enable_audit_context_posts', 'Y' );
|
49 |
-
}
|
50 |
-
|
51 |
-
/**
|
52 |
-
* @return bool
|
53 |
-
* @deprecated 10.1
|
54 |
-
*/
|
55 |
-
public function isAuditShield() :bool {
|
56 |
-
return $this->isOpt( 'enable_audit_context_wpsf', 'Y' );
|
57 |
-
}
|
58 |
-
|
59 |
-
/**
|
60 |
-
* @return bool
|
61 |
-
* @deprecated 10.1
|
62 |
-
*/
|
63 |
-
public function isAuditThemes() :bool {
|
64 |
-
return $this->isOpt( 'enable_audit_context_themes', 'Y' );
|
65 |
-
}
|
66 |
-
|
67 |
-
/**
|
68 |
-
* @return bool
|
69 |
-
* @deprecated 10.1
|
70 |
-
*/
|
71 |
-
public function isAuditUsers() :bool {
|
72 |
-
return $this->isOpt( 'enable_audit_context_users', 'Y' );
|
73 |
-
}
|
74 |
-
|
75 |
-
/**
|
76 |
-
* @return bool
|
77 |
-
* @deprecated 10.1
|
78 |
-
*/
|
79 |
-
public function isAuditWp() :bool {
|
80 |
-
return $this->isOpt( 'enable_audit_context_wordpress', 'Y' );
|
81 |
-
}
|
82 |
-
|
83 |
-
/**
|
84 |
-
* @return $this
|
85 |
-
*/
|
86 |
-
public function updateCTLastSnapshotAt() {
|
87 |
-
return $this->setOptAt( 'ct_last_snapshot_at' );
|
88 |
-
}
|
89 |
}
|
15 |
(int)$this->getOpt( 'audit_trail_max_entries' ) :
|
16 |
(int)$this->getDef( 'audit_trail_free_max_entries' );
|
17 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
18 |
}
|
src/lib/src/Modules/Autoupdates/Insights/OverviewCards.php
CHANGED
@@ -3,15 +3,15 @@
|
|
3 |
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Autoupdates\Insights;
|
4 |
|
5 |
use FernleafSystems\Wordpress\Plugin\Shield;
|
6 |
-
use FernleafSystems\Wordpress\Plugin\Shield\Modules\Autoupdates
|
7 |
use FernleafSystems\Wordpress\Services\Services;
|
8 |
|
9 |
class OverviewCards extends Shield\Modules\Base\Insights\OverviewCards {
|
10 |
|
11 |
public function build() :array {
|
12 |
-
/** @var \
|
13 |
$mod = $this->getMod();
|
14 |
-
/** @var Options $opts */
|
15 |
$opts = $this->getOptions();
|
16 |
$WP = Services::WpGeneral();
|
17 |
|
3 |
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Autoupdates\Insights;
|
4 |
|
5 |
use FernleafSystems\Wordpress\Plugin\Shield;
|
6 |
+
use FernleafSystems\Wordpress\Plugin\Shield\Modules\Autoupdates;
|
7 |
use FernleafSystems\Wordpress\Services\Services;
|
8 |
|
9 |
class OverviewCards extends Shield\Modules\Base\Insights\OverviewCards {
|
10 |
|
11 |
public function build() :array {
|
12 |
+
/** @var Autoupdates\ModCon $mod */
|
13 |
$mod = $this->getMod();
|
14 |
+
/** @var Autoupdates\Options $opts */
|
15 |
$opts = $this->getOptions();
|
16 |
$WP = Services::WpGeneral();
|
17 |
|
src/lib/src/Modules/Autoupdates/Processor.php
CHANGED
@@ -238,7 +238,7 @@ class Processor extends BaseShield\Processor {
|
|
238 |
elseif ( $oOpts->isAutoupdateAllPlugins() ) {
|
239 |
$bDoAutoUpdate = true;
|
240 |
}
|
241 |
-
elseif ( $file === $this->getCon()->
|
242 |
$sAuto = $oOpts->getSelfAutoUpdateOpt();
|
243 |
if ( $sAuto === 'immediate' ) {
|
244 |
$bDoAutoUpdate = true;
|
238 |
elseif ( $oOpts->isAutoupdateAllPlugins() ) {
|
239 |
$bDoAutoUpdate = true;
|
240 |
}
|
241 |
+
elseif ( $file === $this->getCon()->base_file ) {
|
242 |
$sAuto = $oOpts->getSelfAutoUpdateOpt();
|
243 |
if ( $sAuto === 'immediate' ) {
|
244 |
$bDoAutoUpdate = true;
|
src/lib/src/Modules/Base/AdminNotices.php
CHANGED
@@ -69,7 +69,7 @@ class AdminNotices {
|
|
69 |
$aNotices[] = $notice;
|
70 |
}
|
71 |
}
|
72 |
-
catch ( \Exception $
|
73 |
}
|
74 |
}
|
75 |
}
|
69 |
$aNotices[] = $notice;
|
70 |
}
|
71 |
}
|
72 |
+
catch ( \Exception $e ) {
|
73 |
}
|
74 |
}
|
75 |
}
|
src/lib/src/Modules/Base/AjaxHandlerBase.php
DELETED
@@ -1,110 +0,0 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Base;
|
4 |
-
|
5 |
-
use FernleafSystems\Wordpress\Plugin\Shield\Modules\ModConsumer;
|
6 |
-
use FernleafSystems\Wordpress\Services\Services;
|
7 |
-
|
8 |
-
/**
|
9 |
-
* Class AjaxHandlerBase
|
10 |
-
* @package FernleafSystems\Wordpress\Plugin\Shield\Modules\Base
|
11 |
-
* @deprecated 10.1
|
12 |
-
*/
|
13 |
-
abstract class AjaxHandlerBase {
|
14 |
-
|
15 |
-
use ModConsumer;
|
16 |
-
|
17 |
-
public function __construct() {
|
18 |
-
add_action( 'wp_loaded', [ $this, 'init' ] );
|
19 |
-
}
|
20 |
-
|
21 |
-
public function init() {
|
22 |
-
add_filter( $this->getCon()->prefix( 'ajaxAuthAction' ), [ $this, 'handleAjaxAuth' ], 10, 2 );
|
23 |
-
add_filter( $this->getCon()->prefix( 'ajaxNonAuthAction' ), [ $this, 'handleAjaxNonAuth' ], 10, 2 );
|
24 |
-
}
|
25 |
-
|
26 |
-
/**
|
27 |
-
* @param array $aAjaxResponse
|
28 |
-
* @param string $sAjaxAction
|
29 |
-
* @return array
|
30 |
-
*/
|
31 |
-
public function handleAjaxAuth( $aAjaxResponse, $sAjaxAction ) {
|
32 |
-
if ( !empty( $sAjaxAction ) && ( empty( $aAjaxResponse ) || !is_array( $aAjaxResponse ) ) ) {
|
33 |
-
$aAjaxResponse = $this->normaliseAjaxResponse( $this->processAjaxAction( $sAjaxAction ) );
|
34 |
-
}
|
35 |
-
return $aAjaxResponse;
|
36 |
-
}
|
37 |
-
|
38 |
-
/**
|
39 |
-
* @param array $aAjaxResponse
|
40 |
-
* @param string $sAjaxAction
|
41 |
-
* @return array
|
42 |
-
*/
|
43 |
-
public function handleAjaxNonAuth( $aAjaxResponse, $sAjaxAction ) {
|
44 |
-
if ( !empty( $sAjaxAction ) && ( empty( $aAjaxResponse ) || !is_array( $aAjaxResponse ) ) ) {
|
45 |
-
$aAjaxResponse = $this->normaliseAjaxResponse( $this->processAjaxAction( $sAjaxAction ) );
|
46 |
-
}
|
47 |
-
return $aAjaxResponse;
|
48 |
-
}
|
49 |
-
|
50 |
-
/**
|
51 |
-
* @param string $sEncoding
|
52 |
-
* @return array
|
53 |
-
*/
|
54 |
-
protected function getAjaxFormParams( $sEncoding = 'none' ) {
|
55 |
-
$oReq = Services::Request();
|
56 |
-
$aFormParams = [];
|
57 |
-
$sRaw = $oReq->post( 'form_params', '' );
|
58 |
-
|
59 |
-
if ( !empty( $sRaw ) ) {
|
60 |
-
|
61 |
-
$sMaybeEncoding = $oReq->post( 'enc_params' );
|
62 |
-
if ( in_array( $sMaybeEncoding, [ 'none', 'lz-string', 'b64' ] ) ) {
|
63 |
-
$sEncoding = $sMaybeEncoding;
|
64 |
-
}
|
65 |
-
|
66 |
-
switch ( $sEncoding ) {
|
67 |
-
case 'lz-string':
|
68 |
-
$sRaw = \LZCompressor\LZString::decompress( base64_decode( $sRaw ) );
|
69 |
-
break;
|
70 |
-
|
71 |
-
case 'b64':
|
72 |
-
$sRaw = base64_decode( $sRaw );
|
73 |
-
break;
|
74 |
-
|
75 |
-
case 'none':
|
76 |
-
default:
|
77 |
-
break;
|
78 |
-
}
|
79 |
-
|
80 |
-
parse_str( $sRaw, $aFormParams );
|
81 |
-
}
|
82 |
-
return $aFormParams;
|
83 |
-
}
|
84 |
-
|
85 |
-
protected function processAjaxAction( string $action ) :array {
|
86 |
-
return [];
|
87 |
-
}
|
88 |
-
|
89 |
-
/**
|
90 |
-
* We check for empty since if it's empty, there's nothing to normalize. It's a filter,
|
91 |
-
* so if we send something back non-empty, it'll be treated like a "handled" response and
|
92 |
-
* processing will finish
|
93 |
-
* @param array $aAjaxResponse
|
94 |
-
* @return array
|
95 |
-
*/
|
96 |
-
protected function normaliseAjaxResponse( $aAjaxResponse ) {
|
97 |
-
if ( !empty( $aAjaxResponse ) ) {
|
98 |
-
$aAjaxResponse = array_merge(
|
99 |
-
[
|
100 |
-
'success' => false,
|
101 |
-
'page_reload' => false,
|
102 |
-
'message' => 'Unknown',
|
103 |
-
'html' => '',
|
104 |
-
],
|
105 |
-
$aAjaxResponse
|
106 |
-
);
|
107 |
-
}
|
108 |
-
return $aAjaxResponse;
|
109 |
-
}
|
110 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
src/lib/src/Modules/Base/AjaxHandlerShield.php
DELETED
@@ -1,82 +0,0 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Base;
|
4 |
-
|
5 |
-
/**
|
6 |
-
* Class AjaxHandlerShield
|
7 |
-
* @package FernleafSystems\Wordpress\Plugin\Shield\Modules\Base
|
8 |
-
* @deprecated 10.1
|
9 |
-
*/
|
10 |
-
class AjaxHandlerShield extends AjaxHandlerBase {
|
11 |
-
|
12 |
-
protected function processAjaxAction( string $action ) :array {
|
13 |
-
$aResponse = [];
|
14 |
-
$mod = $this->getMod();
|
15 |
-
|
16 |
-
switch ( $action ) {
|
17 |
-
|
18 |
-
case 'mod_opts_form_render':
|
19 |
-
$aResponse = $this->ajaxExec_ModOptionsFormRender();
|
20 |
-
break;
|
21 |
-
|
22 |
-
case 'mod_options':
|
23 |
-
$aResponse = $this->ajaxExec_ModOptions();
|
24 |
-
break;
|
25 |
-
|
26 |
-
case 'wiz_process_step':
|
27 |
-
if ( $mod->hasWizard() ) {
|
28 |
-
$aResponse = $mod->getWizardHandler()
|
29 |
-
->ajaxExec_WizProcessStep();
|
30 |
-
}
|
31 |
-
break;
|
32 |
-
|
33 |
-
case 'wiz_render_step':
|
34 |
-
if ( $mod->hasWizard() ) {
|
35 |
-
$aResponse = $mod->getWizardHandler()
|
36 |
-
->ajaxExec_WizRenderStep();
|
37 |
-
}
|
38 |
-
break;
|
39 |
-
|
40 |
-
default:
|
41 |
-
$aResponse = parent::processAjaxAction( $action );
|
42 |
-
}
|
43 |
-
|
44 |
-
return $aResponse;
|
45 |
-
}
|
46 |
-
|
47 |
-
/**
|
48 |
-
* @return array
|
49 |
-
*/
|
50 |
-
protected function ajaxExec_ModOptions() {
|
51 |
-
|
52 |
-
$sName = $this->getCon()->getHumanName();
|
53 |
-
|
54 |
-
try {
|
55 |
-
$this->getMod()->saveOptionsSubmit();
|
56 |
-
$bSuccess = true;
|
57 |
-
$sMessage = sprintf( __( '%s Plugin options updated successfully.', 'wp-simple-firewall' ), $sName );
|
58 |
-
}
|
59 |
-
catch ( \Exception $oE ) {
|
60 |
-
$bSuccess = false;
|
61 |
-
$sMessage = sprintf( __( 'Failed to update %s plugin options.', 'wp-simple-firewall' ), $sName )
|
62 |
-
.' '.$oE->getMessage();
|
63 |
-
}
|
64 |
-
|
65 |
-
return [
|
66 |
-
'success' => $bSuccess,
|
67 |
-
'html' => '', //we reload the page
|
68 |
-
'message' => $sMessage
|
69 |
-
];
|
70 |
-
}
|
71 |
-
|
72 |
-
/**
|
73 |
-
* @return array
|
74 |
-
*/
|
75 |
-
protected function ajaxExec_ModOptionsFormRender() {
|
76 |
-
return [
|
77 |
-
'success' => true,
|
78 |
-
'html' => $this->getMod()->renderOptionsForm(),
|
79 |
-
'message' => 'loaded'
|
80 |
-
];
|
81 |
-
}
|
82 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
src/lib/src/Modules/Base/BaseProcessor.php
CHANGED
@@ -8,7 +8,7 @@ use FernleafSystems\Wordpress\Services\Services;
|
|
8 |
/**
|
9 |
* Class BaseProcessor
|
10 |
* @package FernleafSystems\Wordpress\Plugin\Shield\Modules\Base
|
11 |
-
* @deprecated 10.
|
12 |
*/
|
13 |
class BaseProcessor {
|
14 |
|
@@ -148,13 +148,6 @@ class BaseProcessor {
|
|
148 |
return Services::WpGeneral()->getLocale( '-' );
|
149 |
}
|
150 |
|
151 |
-
/**
|
152 |
-
* @return \ICWP_WPSF_Processor_Email
|
153 |
-
*/
|
154 |
-
public function getEmailProcessor() {
|
155 |
-
return $this->getMod()->getEmailProcessor();
|
156 |
-
}
|
157 |
-
|
158 |
/**
|
159 |
* @param string $key
|
160 |
* @return BaseProcessor|mixed|null
|
8 |
/**
|
9 |
* Class BaseProcessor
|
10 |
* @package FernleafSystems\Wordpress\Plugin\Shield\Modules\Base
|
11 |
+
* @deprecated 10.2
|
12 |
*/
|
13 |
class BaseProcessor {
|
14 |
|
148 |
return Services::WpGeneral()->getLocale( '-' );
|
149 |
}
|
150 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
151 |
/**
|
152 |
* @param string $key
|
153 |
* @return BaseProcessor|mixed|null
|
src/lib/src/Modules/Base/BaseReporting.php
DELETED
@@ -1,54 +0,0 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Base;
|
4 |
-
|
5 |
-
use FernleafSystems\Wordpress\Plugin\Shield\Modules\ModConsumer;
|
6 |
-
use FernleafSystems\Wordpress\Plugin\Shield\Modules\Reporting\Lib\Reports;
|
7 |
-
|
8 |
-
/**
|
9 |
-
* Class BaseReporting
|
10 |
-
* @package FernleafSystems\Wordpress\Plugin\Shield\Modules\Base
|
11 |
-
* @deprecated 10.1
|
12 |
-
*/
|
13 |
-
abstract class BaseReporting {
|
14 |
-
|
15 |
-
use ModConsumer;
|
16 |
-
|
17 |
-
/**
|
18 |
-
* @return Reports\BaseReporter[]
|
19 |
-
*/
|
20 |
-
public function getAlertReporters() :array {
|
21 |
-
return $this->assignMod( $this->enumAlertReporters() );
|
22 |
-
}
|
23 |
-
|
24 |
-
/**
|
25 |
-
* @return Reports\BaseReporter[]
|
26 |
-
*/
|
27 |
-
public function getInfoReporters() :array {
|
28 |
-
return $this->assignMod( $this->enumInfoReporters() );
|
29 |
-
}
|
30 |
-
|
31 |
-
/**
|
32 |
-
* @return Reports\BaseReporter[]
|
33 |
-
*/
|
34 |
-
protected function enumAlertReporters() :array {
|
35 |
-
return [];
|
36 |
-
}
|
37 |
-
|
38 |
-
/**
|
39 |
-
* @return Reports\BaseReporter[]
|
40 |
-
*/
|
41 |
-
protected function enumInfoReporters() :array {
|
42 |
-
return [];
|
43 |
-
}
|
44 |
-
|
45 |
-
/**
|
46 |
-
* @param Reports\BaseReporter[] $aReporters
|
47 |
-
* @return array
|
48 |
-
*/
|
49 |
-
protected function assignMod( array $aReporters ) :array {
|
50 |
-
return array_map( function ( $oReporter ) {
|
51 |
-
return $oReporter->setMod( $this->getMod() );
|
52 |
-
}, $aReporters );
|
53 |
-
}
|
54 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
src/lib/src/Modules/Base/ModCon.php
CHANGED
@@ -111,8 +111,6 @@ abstract class ModCon {
|
|
111 |
|
112 |
add_filter( $con->prefix( 'register_admin_notices' ), [ $this, 'fRegisterAdminNotices' ] );
|
113 |
|
114 |
-
add_action( 'admin_enqueue_scripts', [ $this, 'onWpEnqueueAdminJs' ], 100 );
|
115 |
-
|
116 |
if ( is_admin() || is_network_admin() ) {
|
117 |
$this->loadAdminNotices();
|
118 |
}
|
@@ -361,18 +359,14 @@ abstract class ModCon {
|
|
361 |
|
362 |
/**
|
363 |
* Override this and adapt per feature
|
364 |
-
* @return Shield\Modules\Base\
|
365 |
*/
|
366 |
protected function loadProcessor() {
|
367 |
if ( !isset( $this->oProcessor ) ) {
|
368 |
try {
|
369 |
-
// TODO: Remove 'abstract' from base processor after transition to new processors is complete
|
370 |
$class = $this->findElementClass( 'Processor', true );
|
371 |
}
|
372 |
catch ( \Exception $e ) {
|
373 |
-
$class = $this->getProcessorClassName();
|
374 |
-
}
|
375 |
-
if ( !@class_exists( $class ) ) {
|
376 |
return null;
|
377 |
}
|
378 |
$this->oProcessor = new $class( $this );
|
@@ -380,20 +374,6 @@ abstract class ModCon {
|
|
380 |
return $this->oProcessor;
|
381 |
}
|
382 |
|
383 |
-
/**
|
384 |
-
* This is the old method
|
385 |
-
* @deprecated 10.1
|
386 |
-
*/
|
387 |
-
protected function getProcessorClassName() :string {
|
388 |
-
return '\\'.implode( '_',
|
389 |
-
[
|
390 |
-
strtoupper( $this->getCon()->getPluginPrefix( '_' ) ),
|
391 |
-
'Processor',
|
392 |
-
str_replace( ' ', '', ucwords( str_replace( '_', ' ', $this->getSlug() ) ) )
|
393 |
-
]
|
394 |
-
);
|
395 |
-
}
|
396 |
-
|
397 |
/**
|
398 |
* Override this and adapt per feature
|
399 |
* @return string
|
@@ -409,7 +389,7 @@ abstract class ModCon {
|
|
409 |
}
|
410 |
|
411 |
public function isUpgrading() :bool {
|
412 |
-
return $this->getCon()->
|
413 |
}
|
414 |
|
415 |
/**
|
@@ -516,7 +496,7 @@ abstract class ModCon {
|
|
516 |
* TODO: Get rid of this crap and/or handle the \Exception thrown in loadFeatureHandler()
|
517 |
* @return Modules\Email\ModCon
|
518 |
* @throws \Exception
|
519 |
-
* @deprecated 10.
|
520 |
*/
|
521 |
public function getEmailHandler() {
|
522 |
return $this->getCon()->getModule_Email();
|
@@ -818,13 +798,13 @@ abstract class ModCon {
|
|
818 |
}
|
819 |
|
820 |
/**
|
821 |
-
* @param string $
|
822 |
* @return array
|
823 |
*/
|
824 |
-
public function getNonceActionData( $
|
825 |
-
$
|
826 |
-
$
|
827 |
-
return $
|
828 |
}
|
829 |
|
830 |
/**
|
@@ -928,17 +908,17 @@ abstract class ModCon {
|
|
928 |
* @return array - map of each option to its option type
|
929 |
*/
|
930 |
protected function getAllFormOptionsAndTypes() {
|
931 |
-
$
|
932 |
|
933 |
foreach ( $this->getUIHandler()->buildOptions() as $aOptionsSection ) {
|
934 |
if ( !empty( $aOptionsSection ) ) {
|
935 |
foreach ( $aOptionsSection[ 'options' ] as $aOption ) {
|
936 |
-
$
|
937 |
}
|
938 |
}
|
939 |
}
|
940 |
|
941 |
-
return $
|
942 |
}
|
943 |
|
944 |
protected function handleModAction( string $action ) {
|
@@ -965,17 +945,17 @@ abstract class ModCon {
|
|
965 |
}
|
966 |
|
967 |
/**
|
968 |
-
* @param string $
|
969 |
-
* @param bool $
|
970 |
* @param bool $bShowOnLogin
|
971 |
* @return $this
|
972 |
*/
|
973 |
-
public function setFlashAdminNotice( $
|
974 |
$this->getCon()
|
975 |
->getAdminNotices()
|
976 |
->addFlash(
|
977 |
-
sprintf( '[%s] %s', $this->getCon()->getHumanName(), $
|
978 |
-
$
|
979 |
$bShowOnLogin
|
980 |
);
|
981 |
return $this;
|
@@ -985,10 +965,7 @@ abstract class ModCon {
|
|
985 |
return is_admin() && !Services::WpGeneral()->isAjax() && $this->isThisModulePage();
|
986 |
}
|
987 |
|
988 |
-
|
989 |
-
* @return bool
|
990 |
-
*/
|
991 |
-
public function isPremium() {
|
992 |
return $this->getCon()->isPremiumActive();
|
993 |
}
|
994 |
|
@@ -1062,47 +1039,32 @@ abstract class ModCon {
|
|
1062 |
|
1063 |
protected function runWizards() {
|
1064 |
if ( $this->isWizardPage() && $this->hasWizard() ) {
|
1065 |
-
$
|
1066 |
-
if ( $
|
1067 |
-
$
|
1068 |
}
|
1069 |
}
|
1070 |
}
|
1071 |
|
1072 |
-
|
1073 |
-
* @return bool
|
1074 |
-
*/
|
1075 |
-
public function isThisModulePage() {
|
1076 |
return $this->getCon()->isModulePage()
|
1077 |
&& Services::Request()->query( 'page' ) == $this->getModSlug();
|
1078 |
}
|
1079 |
|
1080 |
-
|
1081 |
-
* @return bool
|
1082 |
-
*/
|
1083 |
-
public function isPage_Insights() {
|
1084 |
return Services::Request()->query( 'page' ) == $this->getCon()->getModule_Insights()->getModSlug();
|
1085 |
}
|
1086 |
|
1087 |
-
|
1088 |
-
* @return bool
|
1089 |
-
*/
|
1090 |
-
public function isPage_InsightsThisModule() {
|
1091 |
return $this->isPage_Insights()
|
1092 |
&& Services::Request()->query( 'subnav' ) == $this->getSlug();
|
1093 |
}
|
1094 |
|
1095 |
-
|
1096 |
-
* @return bool
|
1097 |
-
*/
|
1098 |
-
protected function isModuleOptionsRequest() {
|
1099 |
return Services::Request()->post( 'mod_slug' ) === $this->getModSlug();
|
1100 |
}
|
1101 |
|
1102 |
-
|
1103 |
-
* @return bool
|
1104 |
-
*/
|
1105 |
-
protected function isWizardPage() {
|
1106 |
return ( $this->getCon()->getShieldAction() == 'wizard' && $this->isThisModulePage() );
|
1107 |
}
|
1108 |
|
@@ -1259,30 +1221,19 @@ abstract class ModCon {
|
|
1259 |
->setTemplateEngineTwig()
|
1260 |
->render();
|
1261 |
}
|
1262 |
-
catch ( \Exception $
|
1263 |
-
return 'Error rendering options form: '.$
|
1264 |
}
|
1265 |
}
|
1266 |
|
1267 |
-
|
1268 |
-
* @return bool
|
1269 |
-
*/
|
1270 |
-
public function canDisplayOptionsForm() {
|
1271 |
return $this->getOptions()->isAccessRestricted() ? $this->getCon()->isPluginAdmin() : true;
|
1272 |
}
|
1273 |
|
1274 |
-
public function
|
1275 |
-
|
1276 |
-
|
1277 |
-
|
1278 |
-
/**
|
1279 |
-
* Override this with custom JS vars for your particular module.
|
1280 |
-
*/
|
1281 |
-
public function insertCustomJsVars_Admin() {
|
1282 |
-
|
1283 |
-
if ( $this->isThisModulePage() ) {
|
1284 |
-
wp_localize_script(
|
1285 |
-
$this->prefix( 'plugin' ),
|
1286 |
'icwp_wpsf_vars_base',
|
1287 |
[
|
1288 |
'ajax' => [
|
@@ -1290,8 +1241,19 @@ abstract class ModCon {
|
|
1290 |
'mod_opts_form_render' => $this->getAjaxActionData( 'mod_opts_form_render' ),
|
1291 |
]
|
1292 |
]
|
1293 |
-
|
1294 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1295 |
}
|
1296 |
|
1297 |
/**
|
@@ -1357,9 +1319,9 @@ abstract class ModCon {
|
|
1357 |
->setRenderVars( $data )
|
1358 |
->render();
|
1359 |
}
|
1360 |
-
catch ( \Exception $
|
1361 |
-
$render = $
|
1362 |
-
error_log( $
|
1363 |
}
|
1364 |
|
1365 |
return (string)$render;
|
@@ -1406,16 +1368,16 @@ abstract class ModCon {
|
|
1406 |
}
|
1407 |
|
1408 |
/**
|
1409 |
-
* @return null|Shield\Modules\Base\
|
1410 |
*/
|
1411 |
public function getOptions() {
|
1412 |
if ( !isset( $this->oOpts ) ) {
|
1413 |
-
$
|
1414 |
$this->oOpts = $this->loadModElement( 'Options' );
|
1415 |
-
$this->oOpts->setPathToConfig( $
|
1416 |
-
->setRebuildFromFile( $
|
1417 |
->setOptionsStorageKey( $this->getOptionsStorageKey() )
|
1418 |
-
->setIfLoadOptionsFromStorage( !$
|
1419 |
}
|
1420 |
return $this->oOpts;
|
1421 |
}
|
@@ -1476,14 +1438,6 @@ abstract class ModCon {
|
|
1476 |
$this->loadModElement( 'AjaxHandler' );
|
1477 |
}
|
1478 |
|
1479 |
-
/**
|
1480 |
-
* @return Shield\Modules\Base\ShieldOptions|mixed
|
1481 |
-
* @deprecated 10.1
|
1482 |
-
*/
|
1483 |
-
protected function loadOptions() {
|
1484 |
-
return $this->loadModElement( 'Options' );
|
1485 |
-
}
|
1486 |
-
|
1487 |
protected function loadDebug() {
|
1488 |
$req = Services::Request();
|
1489 |
if ( $req->query( 'debug' ) && $req->query( 'mod' ) == $this->getModSlug()
|
111 |
|
112 |
add_filter( $con->prefix( 'register_admin_notices' ), [ $this, 'fRegisterAdminNotices' ] );
|
113 |
|
|
|
|
|
114 |
if ( is_admin() || is_network_admin() ) {
|
115 |
$this->loadAdminNotices();
|
116 |
}
|
359 |
|
360 |
/**
|
361 |
* Override this and adapt per feature
|
362 |
+
* @return Shield\Modules\Base\Processor|mixed
|
363 |
*/
|
364 |
protected function loadProcessor() {
|
365 |
if ( !isset( $this->oProcessor ) ) {
|
366 |
try {
|
|
|
367 |
$class = $this->findElementClass( 'Processor', true );
|
368 |
}
|
369 |
catch ( \Exception $e ) {
|
|
|
|
|
|
|
370 |
return null;
|
371 |
}
|
372 |
$this->oProcessor = new $class( $this );
|
374 |
return $this->oProcessor;
|
375 |
}
|
376 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
377 |
/**
|
378 |
* Override this and adapt per feature
|
379 |
* @return string
|
389 |
}
|
390 |
|
391 |
public function isUpgrading() :bool {
|
392 |
+
return $this->getCon()->cfg->rebuilt || $this->getOptions()->getRebuildFromFile();
|
393 |
}
|
394 |
|
395 |
/**
|
496 |
* TODO: Get rid of this crap and/or handle the \Exception thrown in loadFeatureHandler()
|
497 |
* @return Modules\Email\ModCon
|
498 |
* @throws \Exception
|
499 |
+
* @deprecated 10.2
|
500 |
*/
|
501 |
public function getEmailHandler() {
|
502 |
return $this->getCon()->getModule_Email();
|
798 |
}
|
799 |
|
800 |
/**
|
801 |
+
* @param string $action
|
802 |
* @return array
|
803 |
*/
|
804 |
+
public function getNonceActionData( $action = '' ) {
|
805 |
+
$data = $this->getCon()->getNonceActionData( $action );
|
806 |
+
$data[ 'mod_slug' ] = $this->getModSlug();
|
807 |
+
return $data;
|
808 |
}
|
809 |
|
810 |
/**
|
908 |
* @return array - map of each option to its option type
|
909 |
*/
|
910 |
protected function getAllFormOptionsAndTypes() {
|
911 |
+
$opts = [];
|
912 |
|
913 |
foreach ( $this->getUIHandler()->buildOptions() as $aOptionsSection ) {
|
914 |
if ( !empty( $aOptionsSection ) ) {
|
915 |
foreach ( $aOptionsSection[ 'options' ] as $aOption ) {
|
916 |
+
$opts[ $aOption[ 'key' ] ] = $aOption[ 'type' ];
|
917 |
}
|
918 |
}
|
919 |
}
|
920 |
|
921 |
+
return $opts;
|
922 |
}
|
923 |
|
924 |
protected function handleModAction( string $action ) {
|
945 |
}
|
946 |
|
947 |
/**
|
948 |
+
* @param string $msg
|
949 |
+
* @param bool $isError
|
950 |
* @param bool $bShowOnLogin
|
951 |
* @return $this
|
952 |
*/
|
953 |
+
public function setFlashAdminNotice( $msg, $isError = false, $bShowOnLogin = false ) {
|
954 |
$this->getCon()
|
955 |
->getAdminNotices()
|
956 |
->addFlash(
|
957 |
+
sprintf( '[%s] %s', $this->getCon()->getHumanName(), $msg ),
|
958 |
+
$isError,
|
959 |
$bShowOnLogin
|
960 |
);
|
961 |
return $this;
|
965 |
return is_admin() && !Services::WpGeneral()->isAjax() && $this->isThisModulePage();
|
966 |
}
|
967 |
|
968 |
+
public function isPremium() :bool {
|
|
|
|
|
|
|
969 |
return $this->getCon()->isPremiumActive();
|
970 |
}
|
971 |
|
1039 |
|
1040 |
protected function runWizards() {
|
1041 |
if ( $this->isWizardPage() && $this->hasWizard() ) {
|
1042 |
+
$wiz = $this->getWizardHandler();
|
1043 |
+
if ( $wiz instanceof \ICWP_WPSF_Wizard_Base ) {
|
1044 |
+
$wiz->init();
|
1045 |
}
|
1046 |
}
|
1047 |
}
|
1048 |
|
1049 |
+
public function isThisModulePage() :bool {
|
|
|
|
|
|
|
1050 |
return $this->getCon()->isModulePage()
|
1051 |
&& Services::Request()->query( 'page' ) == $this->getModSlug();
|
1052 |
}
|
1053 |
|
1054 |
+
public function isPage_Insights() :bool {
|
|
|
|
|
|
|
1055 |
return Services::Request()->query( 'page' ) == $this->getCon()->getModule_Insights()->getModSlug();
|
1056 |
}
|
1057 |
|
1058 |
+
public function isPage_InsightsThisModule() :bool {
|
|
|
|
|
|
|
1059 |
return $this->isPage_Insights()
|
1060 |
&& Services::Request()->query( 'subnav' ) == $this->getSlug();
|
1061 |
}
|
1062 |
|
1063 |
+
protected function isModuleOptionsRequest() :bool {
|
|
|
|
|
|
|
1064 |
return Services::Request()->post( 'mod_slug' ) === $this->getModSlug();
|
1065 |
}
|
1066 |
|
1067 |
+
protected function isWizardPage() :bool {
|
|
|
|
|
|
|
1068 |
return ( $this->getCon()->getShieldAction() == 'wizard' && $this->isThisModulePage() );
|
1069 |
}
|
1070 |
|
1221 |
->setTemplateEngineTwig()
|
1222 |
->render();
|
1223 |
}
|
1224 |
+
catch ( \Exception $e ) {
|
1225 |
+
return 'Error rendering options form: '.$e->getMessage();
|
1226 |
}
|
1227 |
}
|
1228 |
|
1229 |
+
public function canDisplayOptionsForm() :bool {
|
|
|
|
|
|
|
1230 |
return $this->getOptions()->isAccessRestricted() ? $this->getCon()->isPluginAdmin() : true;
|
1231 |
}
|
1232 |
|
1233 |
+
public function getScriptLocalisations() :array {
|
1234 |
+
return [
|
1235 |
+
[
|
1236 |
+
'plugin',
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1237 |
'icwp_wpsf_vars_base',
|
1238 |
[
|
1239 |
'ajax' => [
|
1241 |
'mod_opts_form_render' => $this->getAjaxActionData( 'mod_opts_form_render' ),
|
1242 |
]
|
1243 |
]
|
1244 |
+
]
|
1245 |
+
];
|
1246 |
+
}
|
1247 |
+
|
1248 |
+
public function getCustomScriptEnqueues() :array {
|
1249 |
+
return [];
|
1250 |
+
}
|
1251 |
+
|
1252 |
+
/**
|
1253 |
+
* Override this with custom JS vars for your particular module.
|
1254 |
+
* @deprecated 10.2
|
1255 |
+
*/
|
1256 |
+
public function insertCustomJsVars_Admin() {
|
1257 |
}
|
1258 |
|
1259 |
/**
|
1319 |
->setRenderVars( $data )
|
1320 |
->render();
|
1321 |
}
|
1322 |
+
catch ( \Exception $e ) {
|
1323 |
+
$render = $e->getMessage();
|
1324 |
+
error_log( $e->getMessage() );
|
1325 |
}
|
1326 |
|
1327 |
return (string)$render;
|
1368 |
}
|
1369 |
|
1370 |
/**
|
1371 |
+
* @return null|Shield\Modules\Base\Options|mixed
|
1372 |
*/
|
1373 |
public function getOptions() {
|
1374 |
if ( !isset( $this->oOpts ) ) {
|
1375 |
+
$con = $this->getCon();
|
1376 |
$this->oOpts = $this->loadModElement( 'Options' );
|
1377 |
+
$this->oOpts->setPathToConfig( $con->getPath_ConfigFile( $this->getSlug() ) )
|
1378 |
+
->setRebuildFromFile( $con->cfg->rebuilt )
|
1379 |
->setOptionsStorageKey( $this->getOptionsStorageKey() )
|
1380 |
+
->setIfLoadOptionsFromStorage( !$con->getIsResetPlugin() );
|
1381 |
}
|
1382 |
return $this->oOpts;
|
1383 |
}
|
1438 |
$this->loadModElement( 'AjaxHandler' );
|
1439 |
}
|
1440 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1441 |
protected function loadDebug() {
|
1442 |
$req = Services::Request();
|
1443 |
if ( $req->query( 'debug' ) && $req->query( 'mod' ) == $this->getModSlug()
|
src/lib/src/Modules/Base/Options.php
CHANGED
@@ -5,6 +5,7 @@ namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Base;
|
|
5 |
use FernleafSystems\Wordpress\Plugin\Shield\Modules\Base\Options\OptValueSanitize;
|
6 |
use FernleafSystems\Wordpress\Plugin\Shield\Modules\ModConsumer;
|
7 |
use FernleafSystems\Wordpress\Services\Services;
|
|
|
8 |
|
9 |
class Options {
|
10 |
|
@@ -88,9 +89,6 @@ class Options {
|
|
88 |
return $oWp->deleteOption( $this->getOptionsStorageKey() );
|
89 |
}
|
90 |
|
91 |
-
/**
|
92 |
-
* @return array
|
93 |
-
*/
|
94 |
public function getAllOptionsValues() :array {
|
95 |
return $this->getStoredOptions();
|
96 |
}
|
@@ -286,26 +284,23 @@ class Options {
|
|
286 |
return $aSections;
|
287 |
}
|
288 |
|
289 |
-
|
290 |
-
|
291 |
-
*/
|
292 |
-
public function getPrimarySection() {
|
293 |
-
$aSec = [];
|
294 |
foreach ( $this->getSections() as $aS ) {
|
295 |
if ( isset( $aS[ 'primary' ] ) && $aS[ 'primary' ] ) {
|
296 |
-
$
|
297 |
break;
|
298 |
}
|
299 |
}
|
300 |
-
return $
|
301 |
}
|
302 |
|
303 |
/**
|
304 |
-
* @param string $
|
305 |
* @return array
|
306 |
*/
|
307 |
-
public function getSection_Requirements( $
|
308 |
-
$aSection = $this->getSection( $
|
309 |
$aReqs = ( is_array( $aSection ) && isset( $aSection[ 'reqs' ] ) ) ? $aSection[ 'reqs' ] : [];
|
310 |
return array_merge(
|
311 |
[
|
@@ -326,27 +321,27 @@ class Options {
|
|
326 |
}
|
327 |
|
328 |
/**
|
329 |
-
* @param string $
|
330 |
* @return bool
|
331 |
*/
|
332 |
-
public function isSectionReqsMet( $
|
333 |
-
$
|
334 |
-
return Services::Data()->getPhpVersionIsAtLeast( $
|
335 |
-
&& Services::WpGeneral()->getWordpressIsAtLeastVersion( $
|
336 |
}
|
337 |
|
338 |
/**
|
339 |
-
* @param string $
|
340 |
* @return bool
|
341 |
*/
|
342 |
-
public function isOptReqsMet( $
|
343 |
-
return $this->isSectionReqsMet( $this->getOptProperty( $
|
344 |
}
|
345 |
|
346 |
/**
|
347 |
* @return string[]
|
348 |
*/
|
349 |
-
public function getVisibleOptionsKeys() {
|
350 |
$aKeys = [];
|
351 |
|
352 |
foreach ( $this->getRawData_AllOptions() as $aOptionDef ) {
|
@@ -392,15 +387,15 @@ class Options {
|
|
392 |
}
|
393 |
|
394 |
/**
|
395 |
-
* @param string $
|
396 |
* @return array[]
|
397 |
*/
|
398 |
-
protected function getOptionsForSection( $
|
399 |
|
400 |
$aAllOptions = [];
|
401 |
foreach ( $this->getRawData_AllOptions() as $aOptionDef ) {
|
402 |
|
403 |
-
if ( ( $aOptionDef[ 'section' ] != $
|
404 |
continue;
|
405 |
}
|
406 |
|
@@ -432,10 +427,7 @@ class Options {
|
|
432 |
return $aAllOptions;
|
433 |
}
|
434 |
|
435 |
-
|
436 |
-
* @return array
|
437 |
-
*/
|
438 |
-
public function getAdditionalMenuItems() {
|
439 |
return $this->getRawData_MenuItems();
|
440 |
}
|
441 |
|
@@ -497,32 +489,29 @@ class Options {
|
|
497 |
}
|
498 |
|
499 |
/**
|
500 |
-
* @param string $
|
501 |
* @param mixed $mValueToTest
|
502 |
-
* @param bool $
|
503 |
* @return bool
|
504 |
*/
|
505 |
-
public function isOpt( $
|
506 |
-
$mOptionValue = $this->getOpt( $
|
507 |
-
return $
|
508 |
}
|
509 |
|
510 |
/**
|
511 |
-
* @param string $
|
512 |
* @return string|null
|
513 |
*/
|
514 |
-
public function getOptionType( $
|
515 |
-
$
|
516 |
-
if ( !empty( $
|
517 |
-
return $
|
518 |
}
|
519 |
return null;
|
520 |
}
|
521 |
|
522 |
-
|
523 |
-
* @return array
|
524 |
-
*/
|
525 |
-
public function getOptionsKeys() {
|
526 |
if ( !isset( $this->aOptionsKeys ) ) {
|
527 |
$this->aOptionsKeys = [];
|
528 |
foreach ( $this->getRawData_AllOptions() as $aOption ) {
|
@@ -733,7 +722,7 @@ class Options {
|
|
733 |
->run( $sOptKey, $mNewValue );
|
734 |
$bVerified = true;
|
735 |
}
|
736 |
-
catch ( \Exception $
|
737 |
$bVerified = false;
|
738 |
}
|
739 |
|
@@ -894,21 +883,21 @@ class Options {
|
|
894 |
}
|
895 |
|
896 |
/**
|
897 |
-
* @param bool $
|
898 |
* @return array
|
899 |
* @throws \Exception
|
900 |
*/
|
901 |
-
private function loadOptionsValuesFromStorage( bool $
|
902 |
|
903 |
-
if ( $
|
904 |
|
905 |
if ( $this->getIfLoadOptionsFromStorage() ) {
|
906 |
|
907 |
-
$
|
908 |
-
if ( empty( $
|
909 |
throw new \Exception( 'Options Storage Key Is Empty' );
|
910 |
}
|
911 |
-
$this->aOptionsValues = Services::WpGeneral()->getOption( $
|
912 |
}
|
913 |
}
|
914 |
if ( !is_array( $this->aOptionsValues ) ) {
|
@@ -919,36 +908,32 @@ class Options {
|
|
919 |
}
|
920 |
|
921 |
private function readConfiguration() :array {
|
922 |
-
$
|
923 |
-
|
924 |
-
$sStorageKey = $this->getConfigStorageKey();
|
925 |
-
$aConfig = $WP->getOption( $sStorageKey );
|
926 |
|
927 |
-
$bRebuild = $this->getRebuildFromFile() || empty( $
|
928 |
-
if ( !$bRebuild
|
929 |
-
|
930 |
-
|
931 |
-
$aConfig[ 'meta_modts' ] = 0;
|
932 |
}
|
933 |
-
$bRebuild = $this->getConfigModTime() > $
|
934 |
}
|
935 |
|
936 |
if ( $bRebuild ) {
|
937 |
try {
|
938 |
-
$
|
939 |
}
|
940 |
-
catch ( \Exception $
|
941 |
if ( Services::WpGeneral()->isDebug() ) {
|
942 |
-
trigger_error( $
|
943 |
}
|
944 |
-
$
|
945 |
}
|
946 |
-
$
|
947 |
-
$
|
948 |
}
|
949 |
|
950 |
$this->setRebuildFromFile( $bRebuild );
|
951 |
-
return $
|
952 |
}
|
953 |
|
954 |
/**
|
@@ -974,7 +959,7 @@ class Options {
|
|
974 |
return Services::Data()->readFileContentsUsingInclude( $this->getPathToConfig() );
|
975 |
}
|
976 |
|
977 |
-
|
978 |
return 'shield_mod_config_'.md5(
|
979 |
str_replace( wp_normalize_path( ABSPATH ), '', wp_normalize_path( $this->getPathToConfig() ) )
|
980 |
);
|
5 |
use FernleafSystems\Wordpress\Plugin\Shield\Modules\Base\Options\OptValueSanitize;
|
6 |
use FernleafSystems\Wordpress\Plugin\Shield\Modules\ModConsumer;
|
7 |
use FernleafSystems\Wordpress\Services\Services;
|
8 |
+
use FernleafSystems\Wordpress\Services\Utilities\Options\Transient;
|
9 |
|
10 |
class Options {
|
11 |
|
89 |
return $oWp->deleteOption( $this->getOptionsStorageKey() );
|
90 |
}
|
91 |
|
|
|
|
|
|
|
92 |
public function getAllOptionsValues() :array {
|
93 |
return $this->getStoredOptions();
|
94 |
}
|
284 |
return $aSections;
|
285 |
}
|
286 |
|
287 |
+
public function getPrimarySection() :array {
|
288 |
+
$section = [];
|
|
|
|
|
|
|
289 |
foreach ( $this->getSections() as $aS ) {
|
290 |
if ( isset( $aS[ 'primary' ] ) && $aS[ 'primary' ] ) {
|
291 |
+
$section = $aS;
|
292 |
break;
|
293 |
}
|
294 |
}
|
295 |
+
return $section;
|
296 |
}
|
297 |
|
298 |
/**
|
299 |
+
* @param string $slug
|
300 |
* @return array
|
301 |
*/
|
302 |
+
public function getSection_Requirements( $slug ) {
|
303 |
+
$aSection = $this->getSection( $slug );
|
304 |
$aReqs = ( is_array( $aSection ) && isset( $aSection[ 'reqs' ] ) ) ? $aSection[ 'reqs' ] : [];
|
305 |
return array_merge(
|
306 |
[
|
321 |
}
|
322 |
|
323 |
/**
|
324 |
+
* @param string $slug
|
325 |
* @return bool
|
326 |
*/
|
327 |
+
public function isSectionReqsMet( $slug ) :bool {
|
328 |
+
$reqs = $this->getSection_Requirements( $slug );
|
329 |
+
return Services::Data()->getPhpVersionIsAtLeast( $reqs[ 'php_min' ] )
|
330 |
+
&& Services::WpGeneral()->getWordpressIsAtLeastVersion( $reqs[ 'wp_min' ] );
|
331 |
}
|
332 |
|
333 |
/**
|
334 |
+
* @param string $optKey
|
335 |
* @return bool
|
336 |
*/
|
337 |
+
public function isOptReqsMet( $optKey ) :bool {
|
338 |
+
return $this->isSectionReqsMet( $this->getOptProperty( $optKey, 'section' ) );
|
339 |
}
|
340 |
|
341 |
/**
|
342 |
* @return string[]
|
343 |
*/
|
344 |
+
public function getVisibleOptionsKeys() :array {
|
345 |
$aKeys = [];
|
346 |
|
347 |
foreach ( $this->getRawData_AllOptions() as $aOptionDef ) {
|
387 |
}
|
388 |
|
389 |
/**
|
390 |
+
* @param string $slug
|
391 |
* @return array[]
|
392 |
*/
|
393 |
+
protected function getOptionsForSection( $slug ) :array {
|
394 |
|
395 |
$aAllOptions = [];
|
396 |
foreach ( $this->getRawData_AllOptions() as $aOptionDef ) {
|
397 |
|
398 |
+
if ( ( $aOptionDef[ 'section' ] != $slug ) || ( isset( $aOptionDef[ 'hidden' ] ) && $aOptionDef[ 'hidden' ] ) ) {
|
399 |
continue;
|
400 |
}
|
401 |
|
427 |
return $aAllOptions;
|
428 |
}
|
429 |
|
430 |
+
public function getAdditionalMenuItems() :array {
|
|
|
|
|
|
|
431 |
return $this->getRawData_MenuItems();
|
432 |
}
|
433 |
|
489 |
}
|
490 |
|
491 |
/**
|
492 |
+
* @param string $key
|
493 |
* @param mixed $mValueToTest
|
494 |
+
* @param bool $strict
|
495 |
* @return bool
|
496 |
*/
|
497 |
+
public function isOpt( string $key, $mValueToTest, $strict = false ) :bool {
|
498 |
+
$mOptionValue = $this->getOpt( $key );
|
499 |
+
return $strict ? $mOptionValue === $mValueToTest : $mOptionValue == $mValueToTest;
|
500 |
}
|
501 |
|
502 |
/**
|
503 |
+
* @param string $key
|
504 |
* @return string|null
|
505 |
*/
|
506 |
+
public function getOptionType( $key ) {
|
507 |
+
$def = $this->getRawData_SingleOption( $key );
|
508 |
+
if ( !empty( $def ) && isset( $def[ 'type' ] ) ) {
|
509 |
+
return $def[ 'type' ];
|
510 |
}
|
511 |
return null;
|
512 |
}
|
513 |
|
514 |
+
public function getOptionsKeys() :array {
|
|
|
|
|
|
|
515 |
if ( !isset( $this->aOptionsKeys ) ) {
|
516 |
$this->aOptionsKeys = [];
|
517 |
foreach ( $this->getRawData_AllOptions() as $aOption ) {
|
722 |
->run( $sOptKey, $mNewValue );
|
723 |
$bVerified = true;
|
724 |
}
|
725 |
+
catch ( \Exception $e ) {
|
726 |
$bVerified = false;
|
727 |
}
|
728 |
|
883 |
}
|
884 |
|
885 |
/**
|
886 |
+
* @param bool $reload
|
887 |
* @return array
|
888 |
* @throws \Exception
|
889 |
*/
|
890 |
+
private function loadOptionsValuesFromStorage( bool $reload = false ) :array {
|
891 |
|
892 |
+
if ( $reload || empty( $this->aOptionsValues ) ) {
|
893 |
|
894 |
if ( $this->getIfLoadOptionsFromStorage() ) {
|
895 |
|
896 |
+
$key = $this->getOptionsStorageKey();
|
897 |
+
if ( empty( $key ) ) {
|
898 |
throw new \Exception( 'Options Storage Key Is Empty' );
|
899 |
}
|
900 |
+
$this->aOptionsValues = Services::WpGeneral()->getOption( $key, [] );
|
901 |
}
|
902 |
}
|
903 |
if ( !is_array( $this->aOptionsValues ) ) {
|
908 |
}
|
909 |
|
910 |
private function readConfiguration() :array {
|
911 |
+
$cfg = Transient::Get( $this->getConfigStorageKey() );
|
|
|
|
|
|
|
912 |
|
913 |
+
$bRebuild = $this->getRebuildFromFile() || empty( $cfg ) || !is_array( $cfg );
|
914 |
+
if ( !$bRebuild ) {
|
915 |
+
if ( !isset( $cfg[ 'meta_modts' ] ) ) {
|
916 |
+
$cfg[ 'meta_modts' ] = 0;
|
|
|
917 |
}
|
918 |
+
$bRebuild = $this->getConfigModTime() > $cfg[ 'meta_modts' ];
|
919 |
}
|
920 |
|
921 |
if ( $bRebuild ) {
|
922 |
try {
|
923 |
+
$cfg = $this->readConfigurationJson();
|
924 |
}
|
925 |
+
catch ( \Exception $e ) {
|
926 |
if ( Services::WpGeneral()->isDebug() ) {
|
927 |
+
trigger_error( $e->getMessage() );
|
928 |
}
|
929 |
+
$cfg = [];
|
930 |
}
|
931 |
+
$cfg[ 'meta_modts' ] = $this->getConfigModTime();
|
932 |
+
Transient::Set( $this->getConfigStorageKey(), $cfg );
|
933 |
}
|
934 |
|
935 |
$this->setRebuildFromFile( $bRebuild );
|
936 |
+
return $cfg;
|
937 |
}
|
938 |
|
939 |
/**
|
959 |
return Services::Data()->readFileContentsUsingInclude( $this->getPathToConfig() );
|
960 |
}
|
961 |
|
962 |
+
public function getConfigStorageKey() :string {
|
963 |
return 'shield_mod_config_'.md5(
|
964 |
str_replace( wp_normalize_path( ABSPATH ), '', wp_normalize_path( $this->getPathToConfig() ) )
|
965 |
);
|
src/lib/src/Modules/Base/Processor.php
CHANGED
@@ -4,12 +4,11 @@ namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Base;
|
|
4 |
|
5 |
use FernleafSystems\Utilities\Logic\OneTimeExecute;
|
6 |
use FernleafSystems\Wordpress\Plugin\Shield;
|
7 |
-
use FernleafSystems\Wordpress\Plugin\Shield\Modules;
|
8 |
|
9 |
abstract class Processor {
|
10 |
|
11 |
-
use Modules\ModConsumer;
|
12 |
use Shield\Crons\PluginCronsConsumer;
|
|
|
13 |
use OneTimeExecute;
|
14 |
|
15 |
/**
|
@@ -31,30 +30,4 @@ abstract class Processor {
|
|
31 |
|
32 |
public function onModuleShutdown() {
|
33 |
}
|
34 |
-
|
35 |
-
/**
|
36 |
-
* @deprecated 10.1
|
37 |
-
*/
|
38 |
-
public function deactivatePlugin() {
|
39 |
-
}
|
40 |
-
|
41 |
-
/**
|
42 |
-
* @var BaseProcessor[]
|
43 |
-
* @deprecated 10.1
|
44 |
-
*/
|
45 |
-
protected $aSubPros;
|
46 |
-
|
47 |
-
/**
|
48 |
-
* @deprecated 10.1
|
49 |
-
*/
|
50 |
-
public function onWpEnqueueJs() {
|
51 |
-
}
|
52 |
-
|
53 |
-
/**
|
54 |
-
* @return Modules\Email\Processor
|
55 |
-
* @deprecated 10.1
|
56 |
-
*/
|
57 |
-
public function getEmailProcessor() {
|
58 |
-
return $this->getMod()->getEmailProcessor();
|
59 |
-
}
|
60 |
}
|
4 |
|
5 |
use FernleafSystems\Utilities\Logic\OneTimeExecute;
|
6 |
use FernleafSystems\Wordpress\Plugin\Shield;
|
|
|
7 |
|
8 |
abstract class Processor {
|
9 |
|
|
|
10 |
use Shield\Crons\PluginCronsConsumer;
|
11 |
+
use Shield\Modules\ModConsumer;
|
12 |
use OneTimeExecute;
|
13 |
|
14 |
/**
|
30 |
|
31 |
public function onModuleShutdown() {
|
32 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
33 |
}
|
src/lib/src/Modules/Base/ShieldOptions.php
DELETED
@@ -1,34 +0,0 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Base;
|
4 |
-
|
5 |
-
use FernleafSystems\Wordpress\Services\Services;
|
6 |
-
|
7 |
-
/**
|
8 |
-
* Class ShieldOptions
|
9 |
-
* @package FernleafSystems\Wordpress\Plugin\Shield\Modules\Base
|
10 |
-
* @deprecated 10.1
|
11 |
-
*/
|
12 |
-
class ShieldOptions extends Options {
|
13 |
-
|
14 |
-
public function getInstallationDays() :int {
|
15 |
-
$nTimeInstalled = $this->getCon()
|
16 |
-
->getModule_Plugin()
|
17 |
-
->getInstallDate();
|
18 |
-
if ( empty( $nTimeInstalled ) ) {
|
19 |
-
return 0;
|
20 |
-
}
|
21 |
-
return (int)round( ( Services::Request()->ts() - $nTimeInstalled )/DAY_IN_SECONDS );
|
22 |
-
}
|
23 |
-
|
24 |
-
public function isPremium() :bool {
|
25 |
-
return $this->getCon()->isPremiumActive();
|
26 |
-
}
|
27 |
-
|
28 |
-
public function isShowPromoAdminNotices() :bool {
|
29 |
-
return $this->getCon()
|
30 |
-
->getModule_Plugin()
|
31 |
-
->getOptions()
|
32 |
-
->isOpt( 'enable_upgrade_admin_notice', 'Y' );
|
33 |
-
}
|
34 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
src/lib/src/Modules/Base/ShieldUI.php
DELETED
@@ -1,70 +0,0 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Base;
|
4 |
-
|
5 |
-
use FernleafSystems\Wordpress\Plugin\Shield;
|
6 |
-
use FernleafSystems\Wordpress\Services\Services;
|
7 |
-
|
8 |
-
/**
|
9 |
-
* Class ShieldUI
|
10 |
-
* @package FernleafSystems\Wordpress\Plugin\Shield\Modules\Base
|
11 |
-
* @deprecated 10.1
|
12 |
-
*/
|
13 |
-
class ShieldUI extends UI {
|
14 |
-
|
15 |
-
public function getBaseDisplayData() :array {
|
16 |
-
$con = $this->getCon();
|
17 |
-
/** @var \ICWP_WPSF_FeatureHandler_BaseWpsf $mod */
|
18 |
-
$mod = $this->getMod();
|
19 |
-
|
20 |
-
return Services::DataManipulation()->mergeArraysRecursive(
|
21 |
-
parent::getBaseDisplayData(),
|
22 |
-
[
|
23 |
-
'head' => [
|
24 |
-
'html' => [
|
25 |
-
'lang' => Services::WpGeneral()->getLocale( '-' ),
|
26 |
-
'dir' => is_rtl() ? 'rtl' : 'ltr',
|
27 |
-
],
|
28 |
-
'meta' => [
|
29 |
-
[
|
30 |
-
'type' => 'http-equiv',
|
31 |
-
'type_type' => 'Cache-Control',
|
32 |
-
'content' => 'no-store, no-cache',
|
33 |
-
],
|
34 |
-
[
|
35 |
-
'type' => 'http-equiv',
|
36 |
-
'type_type' => 'Expires',
|
37 |
-
'content' => '0',
|
38 |
-
],
|
39 |
-
],
|
40 |
-
'scripts' => []
|
41 |
-
],
|
42 |
-
'ajax' => [
|
43 |
-
'sec_admin_login' => $mod->getSecAdminLoginAjaxData(),
|
44 |
-
],
|
45 |
-
'flags' => [
|
46 |
-
'has_session' => $mod->hasSession()
|
47 |
-
],
|
48 |
-
'hrefs' => [
|
49 |
-
'aar_forget_key' => $con->getModule_SecAdmin()->isWlEnabled() ?
|
50 |
-
$this->getCon()->getLabels()[ 'AuthorURI' ] : 'https://shsec.io/gc'
|
51 |
-
],
|
52 |
-
'classes' => [
|
53 |
-
'top_container' => implode( ' ', array_filter( [
|
54 |
-
'odp-outercontainer',
|
55 |
-
$this->getCon()->isPremiumActive() ? 'is-pro' : 'is-not-pro',
|
56 |
-
$mod->getModSlug(),
|
57 |
-
Services::Request()->query( 'inav', '' )
|
58 |
-
] ) )
|
59 |
-
],
|
60 |
-
'vars' => [
|
61 |
-
'related_hrefs' => $this->getSettingsRelatedLinks()
|
62 |
-
]
|
63 |
-
]
|
64 |
-
);
|
65 |
-
}
|
66 |
-
|
67 |
-
protected function getSettingsRelatedLinks() :array {
|
68 |
-
return [];
|
69 |
-
}
|
70 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
src/lib/src/Modules/Base/UI.php
CHANGED
@@ -143,7 +143,7 @@ class UI {
|
|
143 |
}
|
144 |
$aOptParams = Services::DataManipulation()->mergeArraysRecursive( $aOptParams, $aOptStrings );
|
145 |
}
|
146 |
-
catch ( \Exception $
|
147 |
}
|
148 |
return $aOptParams;
|
149 |
}
|
@@ -169,6 +169,7 @@ class UI {
|
|
169 |
public function getBaseDisplayData() :array {
|
170 |
$mod = $this->getMod();
|
171 |
$con = $this->getCon();
|
|
|
172 |
|
173 |
/** @var Shield\Modules\Plugin\Options $oPluginOptions */
|
174 |
$oPluginOptions = $con->getModule_Plugin()->getOptions();
|
@@ -234,23 +235,23 @@ class UI {
|
|
234 |
'wizard_landing' => $mod->getUrl_WizardLanding(),
|
235 |
|
236 |
'form_action' => Services::Request()->getUri(),
|
237 |
-
'css_bootstrap' => $
|
238 |
-
'css_pages' => $
|
239 |
-
'css_steps' => $
|
240 |
-
'css_fancybox' => $
|
241 |
-
'css_globalplugin' => $
|
242 |
-
'css_wizard' => $
|
243 |
'js_jquery' => Services::Includes()->getUrl_Jquery(),
|
244 |
-
'js_bootstrap' => $
|
245 |
-
'js_fancybox' => $
|
246 |
-
'js_globalplugin' => $
|
247 |
-
'js_steps' => $
|
248 |
-
'js_wizard' => $
|
249 |
],
|
250 |
'imgs' => [
|
251 |
-
'favicon'
|
252 |
-
'plugin_banner'
|
253 |
-
'
|
254 |
],
|
255 |
'content' => [
|
256 |
'options_form' => '',
|
@@ -365,7 +366,7 @@ class UI {
|
|
365 |
try {
|
366 |
$NS = ( new \ReflectionClass( $this ) )->getNamespaceName();
|
367 |
}
|
368 |
-
catch ( \Exception $
|
369 |
$NS = __NAMESPACE__;
|
370 |
}
|
371 |
|
143 |
}
|
144 |
$aOptParams = Services::DataManipulation()->mergeArraysRecursive( $aOptParams, $aOptStrings );
|
145 |
}
|
146 |
+
catch ( \Exception $e ) {
|
147 |
}
|
148 |
return $aOptParams;
|
149 |
}
|
169 |
public function getBaseDisplayData() :array {
|
170 |
$mod = $this->getMod();
|
171 |
$con = $this->getCon();
|
172 |
+
$urlBuilder = $con->urls;
|
173 |
|
174 |
/** @var Shield\Modules\Plugin\Options $oPluginOptions */
|
175 |
$oPluginOptions = $con->getModule_Plugin()->getOptions();
|
235 |
'wizard_landing' => $mod->getUrl_WizardLanding(),
|
236 |
|
237 |
'form_action' => Services::Request()->getUri(),
|
238 |
+
'css_bootstrap' => $urlBuilder->forCss( 'bootstrap4.min' ),
|
239 |
+
'css_pages' => $urlBuilder->forCss( 'pages' ),
|
240 |
+
'css_steps' => $urlBuilder->forCss( 'jquery.steps' ),
|
241 |
+
'css_fancybox' => $urlBuilder->forCss( 'jquery.fancybox.min' ),
|
242 |
+
'css_globalplugin' => $urlBuilder->forCss( 'global-plugin' ),
|
243 |
+
'css_wizard' => $urlBuilder->forCss( 'wizard' ),
|
244 |
'js_jquery' => Services::Includes()->getUrl_Jquery(),
|
245 |
+
'js_bootstrap' => $urlBuilder->forJs( 'bootstrap4.bundle.min' ),
|
246 |
+
'js_fancybox' => $urlBuilder->forJs( 'jquery.fancybox.min' ),
|
247 |
+
'js_globalplugin' => $urlBuilder->forJs( 'global-plugin' ),
|
248 |
+
'js_steps' => $urlBuilder->forJs( 'jquery.steps.min' ),
|
249 |
+
'js_wizard' => $urlBuilder->forJs( 'wizard' ),
|
250 |
],
|
251 |
'imgs' => [
|
252 |
+
'favicon' => $urlBuilder->forImage( 'pluginlogo_24x24.png' ),
|
253 |
+
'plugin_banner' => $urlBuilder->forImage( 'banner-1500x500-transparent.png' ),
|
254 |
+
'background_svg' => $urlBuilder->forImage( 'shield/background-blob.svg' )
|
255 |
],
|
256 |
'content' => [
|
257 |
'options_form' => '',
|
366 |
try {
|
367 |
$NS = ( new \ReflectionClass( $this ) )->getNamespaceName();
|
368 |
}
|
369 |
+
catch ( \Exception $e ) {
|
370 |
$NS = __NAMESPACE__;
|
371 |
}
|
372 |
|
src/lib/src/Modules/BaseShield/ModCon.php
CHANGED
@@ -23,7 +23,7 @@ class ModCon extends Base\ModCon {
|
|
23 |
|
24 |
/**
|
25 |
* @return bool
|
26 |
-
* @deprecated 10.
|
27 |
*/
|
28 |
public function canCacheDirWrite() :bool {
|
29 |
return ( new Shield\Modules\Plugin\Lib\TestCacheDirWrite() )
|
@@ -31,16 +31,6 @@ class ModCon extends Base\ModCon {
|
|
31 |
->canWrite();
|
32 |
}
|
33 |
|
34 |
-
/**
|
35 |
-
* @return Shield\Modules\Sessions\Processor
|
36 |
-
* @deprecated 10.1
|
37 |
-
*/
|
38 |
-
public function getSessionsProcessor() :Shield\Modules\Sessions\Processor {
|
39 |
-
return $this->getCon()
|
40 |
-
->getModule_Sessions()
|
41 |
-
->getProcessor();
|
42 |
-
}
|
43 |
-
|
44 |
/**
|
45 |
* @return Shield\Databases\Session\Handler
|
46 |
*/
|
@@ -60,14 +50,6 @@ class ModCon extends Base\ModCon {
|
|
60 |
->getCurrent();
|
61 |
}
|
62 |
|
63 |
-
/**
|
64 |
-
* @return bool
|
65 |
-
* @deprecated 10.1
|
66 |
-
*/
|
67 |
-
public function hasSession() :bool {
|
68 |
-
return $this->getSession() instanceof Shield\Databases\Session\EntryVO;
|
69 |
-
}
|
70 |
-
|
71 |
/**
|
72 |
* @return bool
|
73 |
*/
|
23 |
|
24 |
/**
|
25 |
* @return bool
|
26 |
+
* @deprecated 10.2
|
27 |
*/
|
28 |
public function canCacheDirWrite() :bool {
|
29 |
return ( new Shield\Modules\Plugin\Lib\TestCacheDirWrite() )
|
31 |
->canWrite();
|
32 |
}
|
33 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
34 |
/**
|
35 |
* @return Shield\Databases\Session\Handler
|
36 |
*/
|
50 |
->getCurrent();
|
51 |
}
|
52 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
53 |
/**
|
54 |
* @return bool
|
55 |
*/
|
src/lib/src/Modules/BaseShield/ShieldProcessor.php
CHANGED
@@ -3,31 +3,12 @@
|
|
3 |
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\BaseShield;
|
4 |
|
5 |
use FernleafSystems\Wordpress\Plugin\Shield\Modules\Base;
|
6 |
-
use FernleafSystems\Wordpress\Services\Services;
|
7 |
|
8 |
/**
|
9 |
* Class ShieldProcessor
|
10 |
* @package FernleafSystems\Wordpress\Plugin\Shield\Modules\BaseShield
|
11 |
-
* @deprecated 10.
|
12 |
*/
|
13 |
class ShieldProcessor extends Base\BaseProcessor {
|
14 |
|
15 |
-
const RECAPTCHA_JS_HANDLE = 'icwp-google-recaptcha';
|
16 |
-
|
17 |
-
/**
|
18 |
-
* @param \WP_User $oUser
|
19 |
-
* @return bool
|
20 |
-
*/
|
21 |
-
protected function isUserSubjectToLoginIntent( $oUser = null ) {
|
22 |
-
$bIsSubject = false;
|
23 |
-
|
24 |
-
if ( !$oUser instanceof \WP_User ) {
|
25 |
-
$oUser = Services::WpUsers()->getCurrentWpUser();
|
26 |
-
}
|
27 |
-
if ( $oUser instanceof \WP_User ) {
|
28 |
-
$bIsSubject = apply_filters( $this->getCon()->prefix( 'user_subject_to_login_intent' ), false, $oUser );
|
29 |
-
}
|
30 |
-
|
31 |
-
return $bIsSubject;
|
32 |
-
}
|
33 |
}
|
3 |
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\BaseShield;
|
4 |
|
5 |
use FernleafSystems\Wordpress\Plugin\Shield\Modules\Base;
|
|
|
6 |
|
7 |
/**
|
8 |
* Class ShieldProcessor
|
9 |
* @package FernleafSystems\Wordpress\Plugin\Shield\Modules\BaseShield
|
10 |
+
* @deprecated 10.2
|
11 |
*/
|
12 |
class ShieldProcessor extends Base\BaseProcessor {
|
13 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
14 |
}
|
src/lib/src/Modules/CommentsFilter/Scan/Scanner.php
CHANGED
@@ -149,8 +149,8 @@ class Scanner {
|
|
149 |
->test();
|
150 |
}
|
151 |
}
|
152 |
-
catch ( \Exception $
|
153 |
-
$mResult = new \WP_Error( 'recaptcha', $
|
154 |
}
|
155 |
}
|
156 |
|
149 |
->test();
|
150 |
}
|
151 |
}
|
152 |
+
catch ( \Exception $e ) {
|
153 |
+
$mResult = new \WP_Error( 'recaptcha', $e->getMessage(), [] );
|
154 |
}
|
155 |
}
|
156 |
|
src/lib/src/Modules/Events/Lib/EventsService.php
CHANGED
@@ -56,14 +56,6 @@ class EventsService {
|
|
56 |
return $this->isSupportedEvent( $eventKey ) ? $this->getEvents()[ $eventKey ] : null;
|
57 |
}
|
58 |
|
59 |
-
/**
|
60 |
-
* @return string[]
|
61 |
-
* @deprecated 10.1
|
62 |
-
*/
|
63 |
-
public function getEventKeys() {
|
64 |
-
return array_keys( $this->getEvents() );
|
65 |
-
}
|
66 |
-
|
67 |
public function isSupportedEvent( string $eventKey ) :bool {
|
68 |
return in_array( $eventKey, array_keys( $this->getEvents() ) );
|
69 |
}
|
56 |
return $this->isSupportedEvent( $eventKey ) ? $this->getEvents()[ $eventKey ] : null;
|
57 |
}
|
58 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
59 |
public function isSupportedEvent( string $eventKey ) :bool {
|
60 |
return in_array( $eventKey, array_keys( $this->getEvents() ) );
|
61 |
}
|
src/lib/src/Modules/Events/Lib/Reports/KeyStats.php
CHANGED
@@ -53,7 +53,7 @@ class KeyStats extends BaseReporter {
|
|
53 |
];
|
54 |
}
|
55 |
}
|
56 |
-
catch ( \Exception $
|
57 |
}
|
58 |
}
|
59 |
|
53 |
];
|
54 |
}
|
55 |
}
|
56 |
+
catch ( \Exception $e ) {
|
57 |
}
|
58 |
}
|
59 |
|
src/lib/src/Modules/Events/Lib/Reports/ScanRepairs.php
CHANGED
@@ -42,7 +42,7 @@ class ScanRepairs extends BaseReporter {
|
|
42 |
];
|
43 |
}
|
44 |
}
|
45 |
-
catch ( \Exception $
|
46 |
}
|
47 |
}
|
48 |
|
42 |
];
|
43 |
}
|
44 |
}
|
45 |
+
catch ( \Exception $e ) {
|
46 |
}
|
47 |
}
|
48 |
|
src/lib/src/Modules/GeoIp/Lookup.php
CHANGED
@@ -24,27 +24,27 @@ class Lookup {
|
|
24 |
return $this->aIP[ $ip ];
|
25 |
}
|
26 |
|
27 |
-
/** @var Databases\GeoIp\Handler $
|
28 |
-
$
|
29 |
-
/** @var Databases\GeoIp\Select $
|
30 |
-
$
|
31 |
-
$
|
32 |
|
33 |
/**
|
34 |
* We look up the IP and if the request fails, we store it anyway so that we don't repeatedly
|
35 |
* bombard the API. The address will eventually be expired over time and lookup will process
|
36 |
* again at a later date, as required
|
37 |
*/
|
38 |
-
if ( empty( $
|
39 |
-
$
|
40 |
-
$
|
41 |
-
$
|
42 |
/** @var Databases\GeoIp\Insert $oIsrt */
|
43 |
-
$
|
44 |
}
|
45 |
|
46 |
-
$this->aIP[ $ip ] = $
|
47 |
-
return $
|
48 |
}
|
49 |
|
50 |
private function redirectliIpLookup() :array {
|
24 |
return $this->aIP[ $ip ];
|
25 |
}
|
26 |
|
27 |
+
/** @var Databases\GeoIp\Handler $dbh */
|
28 |
+
$dbh = $this->getDbHandler();
|
29 |
+
/** @var Databases\GeoIp\Select $select */
|
30 |
+
$select = $dbh->getQuerySelector();
|
31 |
+
$IP = $select->byIp( $ip );
|
32 |
|
33 |
/**
|
34 |
* We look up the IP and if the request fails, we store it anyway so that we don't repeatedly
|
35 |
* bombard the API. The address will eventually be expired over time and lookup will process
|
36 |
* again at a later date, as required
|
37 |
*/
|
38 |
+
if ( empty( $IP ) ) {
|
39 |
+
$IP = new Databases\GeoIp\EntryVO();
|
40 |
+
$IP->ip = $ip;
|
41 |
+
$IP->meta = $this->redirectliIpLookup();
|
42 |
/** @var Databases\GeoIp\Insert $oIsrt */
|
43 |
+
$dbh->getQueryInserter()->insert( $IP );
|
44 |
}
|
45 |
|
46 |
+
$this->aIP[ $ip ] = $IP;
|
47 |
+
return $IP;
|
48 |
}
|
49 |
|
50 |
private function redirectliIpLookup() :array {
|
src/lib/src/Modules/HackGuard/AjaxHandler.php
CHANGED
@@ -12,23 +12,23 @@ class AjaxHandler extends Shield\Modules\BaseShield\AjaxHandler {
|
|
12 |
|
13 |
protected function processAjaxAction( string $action ) :array {
|
14 |
|
15 |
-
$
|
16 |
switch ( $action ) {
|
17 |
|
18 |
case 'scans_start':
|
19 |
-
$
|
20 |
break;
|
21 |
|
22 |
case 'scans_check':
|
23 |
-
$
|
24 |
break;
|
25 |
|
26 |
case 'item_action':
|
27 |
-
$
|
28 |
break;
|
29 |
|
30 |
case 'bulk_action':
|
31 |
-
$
|
32 |
break;
|
33 |
|
34 |
case 'item_asset_deactivate':
|
@@ -36,30 +36,30 @@ class AjaxHandler extends Shield\Modules\BaseShield\AjaxHandler {
|
|
36 |
case 'item_delete':
|
37 |
case 'item_ignore':
|
38 |
case 'item_repair':
|
39 |
-
$
|
40 |
break;
|
41 |
|
42 |
case 'render_table_scan':
|
43 |
-
$
|
44 |
break;
|
45 |
|
46 |
case 'plugin_reinstall':
|
47 |
-
$
|
48 |
break;
|
49 |
|
50 |
case 'filelocker_showdiff':
|
51 |
-
$
|
52 |
break;
|
53 |
|
54 |
case 'filelocker_fileaction':
|
55 |
-
$
|
56 |
break;
|
57 |
|
58 |
default:
|
59 |
-
$
|
60 |
}
|
61 |
|
62 |
-
return $
|
63 |
}
|
64 |
|
65 |
private function ajaxExec_BuildTableScan() :array {
|
@@ -190,8 +190,8 @@ class AjaxHandler extends Shield\Modules\BaseShield\AjaxHandler {
|
|
190 |
$aData[ 'vars' ][ 'file_name' ] = basename( $oLock->file );
|
191 |
$aData[ 'success' ] = true;
|
192 |
}
|
193 |
-
catch ( \Exception $
|
194 |
-
$aData[ 'error' ] = $
|
195 |
}
|
196 |
|
197 |
return [
|
@@ -207,29 +207,27 @@ class AjaxHandler extends Shield\Modules\BaseShield\AjaxHandler {
|
|
207 |
}
|
208 |
|
209 |
private function ajaxExec_FileLockerFileAction() :array {
|
210 |
-
$
|
211 |
-
$
|
212 |
|
213 |
-
if ( $
|
214 |
-
$nRID = $oReq->post( 'rid' );
|
215 |
-
$sAction = $oReq->post( 'file_action' );
|
216 |
try {
|
217 |
-
$
|
218 |
->setMod( $this->getMod() )
|
219 |
-
->run( $
|
220 |
-
$
|
221 |
}
|
222 |
-
catch ( \Exception $
|
223 |
-
$
|
224 |
}
|
225 |
}
|
226 |
else {
|
227 |
-
$
|
228 |
}
|
229 |
|
230 |
return [
|
231 |
-
'success' => $
|
232 |
-
'message' => $
|
233 |
];
|
234 |
}
|
235 |
|
@@ -269,58 +267,60 @@ class AjaxHandler extends Shield\Modules\BaseShield\AjaxHandler {
|
|
269 |
if ( $action == 'download' ) {
|
270 |
// A special case since this action is handled using Javascript
|
271 |
$success = true;
|
272 |
-
$
|
273 |
}
|
274 |
else {
|
275 |
if ( $bIsBulkAction ) {
|
276 |
-
$
|
277 |
}
|
278 |
else {
|
279 |
-
$
|
280 |
}
|
281 |
-
/** @var int[] $
|
282 |
-
$
|
283 |
|
284 |
-
if ( empty( $
|
285 |
-
$
|
286 |
}
|
287 |
else {
|
288 |
try {
|
289 |
-
$
|
290 |
$aSuccessfulItems = [];
|
291 |
-
foreach ( $
|
292 |
/** @var Shield\Databases\Scanner\EntryVO $entry */
|
293 |
$entry = $mod->getDbHandler_ScanResults()
|
294 |
->getQuerySelector()
|
295 |
-
->byId( $
|
296 |
if ( $entry instanceof Shield\Databases\Scanner\EntryVO ) {
|
297 |
-
$
|
298 |
-
if ( $mod->getScanCon( $entry->scan )->executeItemAction( $
|
299 |
-
$aSuccessfulItems[] = $
|
300 |
}
|
301 |
}
|
302 |
}
|
303 |
|
304 |
-
if ( count( $aSuccessfulItems ) === count( $
|
305 |
$success = true;
|
306 |
-
$
|
307 |
}
|
308 |
else {
|
309 |
-
$
|
310 |
}
|
311 |
|
312 |
// We don't rescan for ignores.
|
313 |
-
|
314 |
-
|
|
|
|
|
315 |
}
|
316 |
else {
|
317 |
// rescan
|
318 |
-
$mod->getScanQueueController()->startScans( $
|
319 |
-
$
|
320 |
}
|
321 |
}
|
322 |
-
catch ( \Exception $
|
323 |
-
$
|
324 |
}
|
325 |
}
|
326 |
}
|
@@ -328,7 +328,7 @@ class AjaxHandler extends Shield\Modules\BaseShield\AjaxHandler {
|
|
328 |
return [
|
329 |
'success' => $success,
|
330 |
'page_reload' => !in_array( $action, [ 'download' ] ),
|
331 |
-
'message' => $
|
332 |
];
|
333 |
}
|
334 |
|
12 |
|
13 |
protected function processAjaxAction( string $action ) :array {
|
14 |
|
15 |
+
$req = Services::Request();
|
16 |
switch ( $action ) {
|
17 |
|
18 |
case 'scans_start':
|
19 |
+
$response = $this->ajaxExec_StartScans();
|
20 |
break;
|
21 |
|
22 |
case 'scans_check':
|
23 |
+
$response = $this->ajaxExec_CheckScans();
|
24 |
break;
|
25 |
|
26 |
case 'item_action':
|
27 |
+
$response = $this->ajaxExec_ScanItemAction( $req->post( 'item_action' ), false );
|
28 |
break;
|
29 |
|
30 |
case 'bulk_action':
|
31 |
+
$response = $this->ajaxExec_ScanItemAction( $req->post( 'bulk_action' ), true );
|
32 |
break;
|
33 |
|
34 |
case 'item_asset_deactivate':
|
36 |
case 'item_delete':
|
37 |
case 'item_ignore':
|
38 |
case 'item_repair':
|
39 |
+
$response = $this->ajaxExec_ScanItemAction( str_replace( 'item_', '', $action ) );
|
40 |
break;
|
41 |
|
42 |
case 'render_table_scan':
|
43 |
+
$response = $this->ajaxExec_BuildTableScan();
|
44 |
break;
|
45 |
|
46 |
case 'plugin_reinstall':
|
47 |
+
$response = $this->ajaxExec_PluginReinstall();
|
48 |
break;
|
49 |
|
50 |
case 'filelocker_showdiff':
|
51 |
+
$response = $this->ajaxExec_FileLockerShowDiff();
|
52 |
break;
|
53 |
|
54 |
case 'filelocker_fileaction':
|
55 |
+
$response = $this->ajaxExec_FileLockerFileAction();
|
56 |
break;
|
57 |
|
58 |
default:
|
59 |
+
$response = parent::processAjaxAction( $action );
|
60 |
}
|
61 |
|
62 |
+
return $response;
|
63 |
}
|
64 |
|
65 |
private function ajaxExec_BuildTableScan() :array {
|
190 |
$aData[ 'vars' ][ 'file_name' ] = basename( $oLock->file );
|
191 |
$aData[ 'success' ] = true;
|
192 |
}
|
193 |
+
catch ( \Exception $e ) {
|
194 |
+
$aData[ 'error' ] = $e->getMessage();
|
195 |
}
|
196 |
|
197 |
return [
|
207 |
}
|
208 |
|
209 |
private function ajaxExec_FileLockerFileAction() :array {
|
210 |
+
$req = Services::Request();
|
211 |
+
$success = false;
|
212 |
|
213 |
+
if ( $req->post( 'confirmed' ) == '1' ) {
|
|
|
|
|
214 |
try {
|
215 |
+
$success = ( new FileLocker\Ops\PerformAction() )
|
216 |
->setMod( $this->getMod() )
|
217 |
+
->run( $req->post( 'rid' ), $req->post( 'file_action' ) );
|
218 |
+
$msg = __( 'Requested action completed successfully.', 'wp-simple-firewall' );
|
219 |
}
|
220 |
+
catch ( \Exception $e ) {
|
221 |
+
$msg = __( 'Requested action failed.', 'wp-simple-firewall' );
|
222 |
}
|
223 |
}
|
224 |
else {
|
225 |
+
$msg = __( 'Please check the box to confirm this action', 'wp-simple-firewall' );
|
226 |
}
|
227 |
|
228 |
return [
|
229 |
+
'success' => $success,
|
230 |
+
'message' => $msg,
|
231 |
];
|
232 |
}
|
233 |
|
267 |
if ( $action == 'download' ) {
|
268 |
// A special case since this action is handled using Javascript
|
269 |
$success = true;
|
270 |
+
$msg = __( 'File download has started.', 'wp-simple-firewall' );
|
271 |
}
|
272 |
else {
|
273 |
if ( $bIsBulkAction ) {
|
274 |
+
$itemIDs = (array)Services::Request()->post( 'ids', [] );
|
275 |
}
|
276 |
else {
|
277 |
+
$itemIDs = [ Services::Request()->post( 'rid' ) ];
|
278 |
}
|
279 |
+
/** @var int[] $itemIDs */
|
280 |
+
$itemIDs = array_filter( array_map( 'intval', $itemIDs ) );
|
281 |
|
282 |
+
if ( empty( $itemIDs ) ) {
|
283 |
+
$msg = __( 'Unsupported item(s) selected', 'wp-simple-firewall' );
|
284 |
}
|
285 |
else {
|
286 |
try {
|
287 |
+
$scanSlugs = [];
|
288 |
$aSuccessfulItems = [];
|
289 |
+
foreach ( $itemIDs as $ID ) {
|
290 |
/** @var Shield\Databases\Scanner\EntryVO $entry */
|
291 |
$entry = $mod->getDbHandler_ScanResults()
|
292 |
->getQuerySelector()
|
293 |
+
->byId( $ID );
|
294 |
if ( $entry instanceof Shield\Databases\Scanner\EntryVO ) {
|
295 |
+
$scanSlugs[] = $entry->scan;
|
296 |
+
if ( $mod->getScanCon( $entry->scan )->executeItemAction( $ID, $action ) ) {
|
297 |
+
$aSuccessfulItems[] = $ID;
|
298 |
}
|
299 |
}
|
300 |
}
|
301 |
|
302 |
+
if ( count( $aSuccessfulItems ) === count( $itemIDs ) ) {
|
303 |
$success = true;
|
304 |
+
$msg = __( 'Action successful.' );
|
305 |
}
|
306 |
else {
|
307 |
+
$msg = __( 'An error occurred.' ).' '.__( 'Some items may not have been processed.' );
|
308 |
}
|
309 |
|
310 |
// We don't rescan for ignores.
|
311 |
+
$rescanSlugs = array_diff( $scanSlugs, [ Scan\Controller\Mal::SCAN_SLUG ] );
|
312 |
+
|
313 |
+
if ( empty( $rescanSlugs ) || in_array( $action, [ 'ignore' ] ) ) {
|
314 |
+
$msg .= ' '.__( 'Reloading', 'wp-simple-firewall' ).' ...';
|
315 |
}
|
316 |
else {
|
317 |
// rescan
|
318 |
+
$mod->getScanQueueController()->startScans( $rescanSlugs );
|
319 |
+
$msg .= ' '.__( 'Rescanning', 'wp-simple-firewall' ).' ...';
|
320 |
}
|
321 |
}
|
322 |
+
catch ( \Exception $e ) {
|
323 |
+
$msg = $e->getMessage();
|
324 |
}
|
325 |
}
|
326 |
}
|
328 |
return [
|
329 |
'success' => $success,
|
330 |
'page_reload' => !in_array( $action, [ 'download' ] ),
|
331 |
+
'message' => $msg,
|
332 |
];
|
333 |
}
|
334 |
|
src/lib/src/Modules/HackGuard/Debug.php
ADDED
@@ -0,0 +1,19 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php declare( strict_types=1 );
|
2 |
+
|
3 |
+
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\HackGuard;
|
4 |
+
|
5 |
+
use FernleafSystems\Wordpress\Plugin\Shield\Modules;
|
6 |
+
use FernleafSystems\Wordpress\Plugin\Shield\Scans\Mal\Signatures;
|
7 |
+
use FernleafSystems\Wordpress\Services\Utilities\Net\IpIdentify;
|
8 |
+
|
9 |
+
class Debug extends Modules\Base\Debug {
|
10 |
+
|
11 |
+
public function run() {
|
12 |
+
$this->dumpSigs();
|
13 |
+
die();
|
14 |
+
}
|
15 |
+
|
16 |
+
private function dumpSigs() {
|
17 |
+
var_dump(Signatures::getAll());
|
18 |
+
}
|
19 |
+
}
|
src/lib/src/Modules/HackGuard/Lib/FileLocker/FileLockerController.php
CHANGED
@@ -162,8 +162,8 @@ class FileLockerController {
|
|
162 |
->setWorkingFile( $this->getFile( $sFileKey ) )
|
163 |
->create();
|
164 |
}
|
165 |
-
catch ( \Exception $
|
166 |
-
error_log( $
|
167 |
}
|
168 |
}
|
169 |
}
|
162 |
->setWorkingFile( $this->getFile( $sFileKey ) )
|
163 |
->create();
|
164 |
}
|
165 |
+
catch ( \Exception $e ) {
|
166 |
+
error_log( $e->getMessage() );
|
167 |
}
|
168 |
}
|
169 |
}
|
src/lib/src/Modules/HackGuard/Lib/FileLocker/Ops/AssessLocks.php
CHANGED
@@ -35,7 +35,7 @@ class AssessLocks extends BaseOps {
|
|
35 |
}
|
36 |
}
|
37 |
}
|
38 |
-
catch ( \InvalidArgumentException $
|
39 |
$oUpd->markProblem( $oLock );
|
40 |
$aProblemIds[] = $oLock->id;
|
41 |
}
|
35 |
}
|
36 |
}
|
37 |
}
|
38 |
+
catch ( \InvalidArgumentException $e ) {
|
39 |
$oUpd->markProblem( $oLock );
|
40 |
$aProblemIds[] = $oLock->id;
|
41 |
}
|
src/lib/src/Modules/HackGuard/Lib/FileLocker/Ops/ReadOriginalFileContent.php
CHANGED
@@ -14,17 +14,17 @@ use FernleafSystems\Wordpress\Services\Utilities\Encrypt\OpenSslEncryptVo;
|
|
14 |
class ReadOriginalFileContent extends BaseOps {
|
15 |
|
16 |
/**
|
17 |
-
* @param Databases\FileLocker\EntryVO $
|
18 |
* @return string
|
19 |
*/
|
20 |
-
public function run( $
|
21 |
try {
|
22 |
-
$
|
23 |
}
|
24 |
-
catch ( \Exception $
|
25 |
-
$
|
26 |
}
|
27 |
-
return $
|
28 |
}
|
29 |
|
30 |
/**
|
14 |
class ReadOriginalFileContent extends BaseOps {
|
15 |
|
16 |
/**
|
17 |
+
* @param Databases\FileLocker\EntryVO $lock
|
18 |
* @return string
|
19 |
*/
|
20 |
+
public function run( $lock ) {
|
21 |
try {
|
22 |
+
$content = $this->useOriginalFile( $lock );
|
23 |
}
|
24 |
+
catch ( \Exception $e ) {
|
25 |
+
$content = $this->useCacheAndApi( $lock );
|
26 |
}
|
27 |
+
return $content;
|
28 |
}
|
29 |
|
30 |
/**
|
src/lib/src/Modules/HackGuard/Lib/FileLocker/Ops/Verify.php
CHANGED
@@ -12,14 +12,14 @@ use FernleafSystems\Wordpress\Services\Utilities\File\Compare\CompareHash;
|
|
12 |
class Verify {
|
13 |
|
14 |
/**
|
15 |
-
* @param EntryVO $
|
16 |
* @return bool
|
17 |
*/
|
18 |
-
public function run( $
|
19 |
try {
|
20 |
-
return ( new CompareHash() )->isEqualFileSha1( $
|
21 |
}
|
22 |
-
catch ( \InvalidArgumentException $
|
23 |
return false;
|
24 |
}
|
25 |
}
|
12 |
class Verify {
|
13 |
|
14 |
/**
|
15 |
+
* @param EntryVO $record
|
16 |
* @return bool
|
17 |
*/
|
18 |
+
public function run( $record ) {
|
19 |
try {
|
20 |
+
return ( new CompareHash() )->isEqualFileSha1( $record->file, $record->hash );
|
21 |
}
|
22 |
+
catch ( \InvalidArgumentException $e ) {
|
23 |
return false;
|
24 |
}
|
25 |
}
|
src/lib/src/Modules/HackGuard/Lib/Snapshots/Build/BuildHashesFromDir.php
CHANGED
@@ -43,7 +43,7 @@ class BuildHashesFromDir {
|
|
43 |
$aSnaps[ $sKey ] = hash_file( $sAlgo, $sFullPath );
|
44 |
}
|
45 |
}
|
46 |
-
catch ( \Exception $
|
47 |
}
|
48 |
return $aSnaps;
|
49 |
}
|
43 |
$aSnaps[ $sKey ] = hash_file( $sAlgo, $sFullPath );
|
44 |
}
|
45 |
}
|
46 |
+
catch ( \Exception $e ) {
|
47 |
}
|
48 |
return $aSnaps;
|
49 |
}
|
src/lib/src/Modules/HackGuard/Lib/Snapshots/Store.php
CHANGED
@@ -115,7 +115,7 @@ class Store {
|
|
115 |
try {
|
116 |
$this->aSnapData = $this->readSnapData();
|
117 |
}
|
118 |
-
catch ( \Exception $
|
119 |
$this->aSnapData = [];
|
120 |
}
|
121 |
return $this;
|
@@ -128,7 +128,7 @@ class Store {
|
|
128 |
try {
|
129 |
$this->aSnapMeta = $this->readSnapMeta();
|
130 |
}
|
131 |
-
catch ( \Exception $
|
132 |
$this->aSnapMeta = [];
|
133 |
}
|
134 |
return $this;
|
115 |
try {
|
116 |
$this->aSnapData = $this->readSnapData();
|
117 |
}
|
118 |
+
catch ( \Exception $e ) {
|
119 |
$this->aSnapData = [];
|
120 |
}
|
121 |
return $this;
|
128 |
try {
|
129 |
$this->aSnapMeta = $this->readSnapMeta();
|
130 |
}
|
131 |
+
catch ( \Exception $e ) {
|
132 |
$this->aSnapMeta = [];
|
133 |
}
|
134 |
return $this;
|
src/lib/src/Modules/HackGuard/Lib/Snapshots/StoreAction/Build.php
CHANGED
@@ -3,7 +3,6 @@
|
|
3 |
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\HackGuard\Lib\Snapshots\StoreAction;
|
4 |
|
5 |
use FernleafSystems\Wordpress\Plugin\Shield\Modules\HackGuard\Lib\Snapshots;
|
6 |
-
use FernleafSystems\Wordpress\Plugin\Shield\Modules\HackGuard\ModCon;
|
7 |
use FernleafSystems\Wordpress\Services\Core\VOs\WpPluginVo;
|
8 |
use FernleafSystems\Wordpress\Services\Services;
|
9 |
|
@@ -13,30 +12,30 @@ class Build extends BaseAction {
|
|
13 |
* @throws \Exception
|
14 |
*/
|
15 |
public function run() {
|
16 |
-
$
|
17 |
try {
|
18 |
-
$
|
19 |
}
|
20 |
-
catch ( \Exception $
|
21 |
}
|
22 |
|
23 |
$aMeta = $this->generateMeta();
|
24 |
-
if ( empty( $
|
25 |
-
$
|
26 |
->setHashAlgo( 'md5' )
|
27 |
-
->build( $
|
28 |
$aMeta[ 'live_hashes' ] = false;
|
29 |
}
|
30 |
else {
|
31 |
$aMeta[ 'live_hashes' ] = true;
|
32 |
}
|
33 |
|
34 |
-
if ( !empty( $
|
35 |
$oStore = ( new CreateNew() )
|
36 |
->setMod( $this->getMod() )
|
37 |
-
->setAsset( $
|
38 |
->run();
|
39 |
-
$oStore->setSnapData( $
|
40 |
->setSnapMeta( $aMeta )
|
41 |
->save();
|
42 |
}
|
@@ -46,18 +45,18 @@ class Build extends BaseAction {
|
|
46 |
* @return array
|
47 |
*/
|
48 |
private function generateMeta() {
|
49 |
-
$
|
50 |
-
$
|
51 |
'ts' => Services::Request()->ts(),
|
52 |
'snap_version' => $this->getCon()->getVersion(),
|
53 |
];
|
54 |
-
$
|
55 |
-
$
|
56 |
-
: $
|
57 |
-
$
|
58 |
-
$
|
59 |
-
: $
|
60 |
-
$
|
61 |
-
return $
|
62 |
}
|
63 |
}
|
3 |
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\HackGuard\Lib\Snapshots\StoreAction;
|
4 |
|
5 |
use FernleafSystems\Wordpress\Plugin\Shield\Modules\HackGuard\Lib\Snapshots;
|
|
|
6 |
use FernleafSystems\Wordpress\Services\Core\VOs\WpPluginVo;
|
7 |
use FernleafSystems\Wordpress\Services\Services;
|
8 |
|
12 |
* @throws \Exception
|
13 |
*/
|
14 |
public function run() {
|
15 |
+
$asset = $this->getAsset();
|
16 |
try {
|
17 |
+
$hashes = ( new Snapshots\Build\BuildHashesFromApi() )->build( $asset );
|
18 |
}
|
19 |
+
catch ( \Exception $e ) {
|
20 |
}
|
21 |
|
22 |
$aMeta = $this->generateMeta();
|
23 |
+
if ( empty( $hashes ) ) {
|
24 |
+
$hashes = ( new Snapshots\Build\BuildHashesForAsset() )
|
25 |
->setHashAlgo( 'md5' )
|
26 |
+
->build( $asset );
|
27 |
$aMeta[ 'live_hashes' ] = false;
|
28 |
}
|
29 |
else {
|
30 |
$aMeta[ 'live_hashes' ] = true;
|
31 |
}
|
32 |
|
33 |
+
if ( !empty( $hashes ) ) {
|
34 |
$oStore = ( new CreateNew() )
|
35 |
->setMod( $this->getMod() )
|
36 |
+
->setAsset( $asset )
|
37 |
->run();
|
38 |
+
$oStore->setSnapData( $hashes )
|
39 |
->setSnapMeta( $aMeta )
|
40 |
->save();
|
41 |
}
|
45 |
* @return array
|
46 |
*/
|
47 |
private function generateMeta() {
|
48 |
+
$asset = $this->getAsset();
|
49 |
+
$meta = [
|
50 |
'ts' => Services::Request()->ts(),
|
51 |
'snap_version' => $this->getCon()->getVersion(),
|
52 |
];
|
53 |
+
$meta[ 'unique_id' ] = ( $asset instanceof WpPluginVo ) ?
|
54 |
+
$asset->file
|
55 |
+
: $asset->stylesheet;
|
56 |
+
$meta[ 'name' ] = ( $asset instanceof WpPluginVo ) ?
|
57 |
+
$asset->Name
|
58 |
+
: $asset->wp_theme->get( 'Name' );
|
59 |
+
$meta[ 'version' ] = $asset->version;
|
60 |
+
return $meta;
|
61 |
}
|
62 |
}
|
src/lib/src/Modules/HackGuard/Lib/Snapshots/StoreAction/CleanAll.php
CHANGED
@@ -23,7 +23,7 @@ class CleanAll extends BaseBulk {
|
|
23 |
}
|
24 |
}
|
25 |
}
|
26 |
-
catch ( \Exception $
|
27 |
}
|
28 |
}
|
29 |
}
|
23 |
}
|
24 |
}
|
25 |
}
|
26 |
+
catch ( \Exception $e ) {
|
27 |
}
|
28 |
}
|
29 |
}
|
src/lib/src/Modules/HackGuard/Lib/Snapshots/StoreAction/Delete.php
CHANGED
@@ -8,15 +8,15 @@ class Delete extends BaseAction {
|
|
8 |
|
9 |
public function run() {
|
10 |
try {
|
11 |
-
$
|
12 |
->setMod( $this->getMod() )
|
13 |
->setAsset( $this->getAsset() )
|
14 |
->run();
|
15 |
-
foreach ( [ $
|
16 |
-
Services::WpFs()->deleteFile( $
|
17 |
}
|
18 |
}
|
19 |
-
catch ( \Exception $
|
20 |
}
|
21 |
}
|
22 |
}
|
8 |
|
9 |
public function run() {
|
10 |
try {
|
11 |
+
$store = ( new Load() )
|
12 |
->setMod( $this->getMod() )
|
13 |
->setAsset( $this->getAsset() )
|
14 |
->run();
|
15 |
+
foreach ( [ $store->getSnapStorePath(), $store->getSnapStoreMetaPath() ] as $path ) {
|
16 |
+
Services::WpFs()->deleteFile( $path );
|
17 |
}
|
18 |
}
|
19 |
+
catch ( \Exception $e ) {
|
20 |
}
|
21 |
}
|
22 |
}
|
src/lib/src/Modules/HackGuard/Lib/Snapshots/StoreAction/ScheduleBuildAll.php
CHANGED
@@ -9,14 +9,14 @@ use FernleafSystems\Wordpress\Services\Services;
|
|
9 |
class ScheduleBuildAll extends BaseBulk {
|
10 |
|
11 |
public function build() {
|
12 |
-
foreach ( $this->getAssetsThatNeedBuilt() as $
|
13 |
try {
|
14 |
( new Build() )
|
15 |
->setMod( $this->getMod() )
|
16 |
-
->setAsset( $
|
17 |
->run();
|
18 |
}
|
19 |
-
catch ( \Exception $
|
20 |
}
|
21 |
}
|
22 |
}
|
@@ -43,27 +43,24 @@ class ScheduleBuildAll extends BaseBulk {
|
|
43 |
( new FindAssetsToSnap() )
|
44 |
->setMod( $this->getMod() )
|
45 |
->run(),
|
46 |
-
function ( $
|
47 |
-
/** @var VOs\WpPluginVo|VOs\WpThemeVo $
|
48 |
try {
|
49 |
-
$
|
50 |
->setMod( $this->getMod() )
|
51 |
-
->setAsset( $
|
52 |
->run()
|
53 |
->getSnapMeta();
|
54 |
}
|
55 |
-
catch ( \Exception $
|
56 |
-
$
|
57 |
}
|
58 |
-
return ( empty( $
|
59 |
}
|
60 |
);
|
61 |
}
|
62 |
|
63 |
-
|
64 |
-
* @return string
|
65 |
-
*/
|
66 |
-
private function getCronHook() {
|
67 |
return $this->getCon()->prefix( 'ptg_build_snapshots' );
|
68 |
}
|
69 |
}
|
9 |
class ScheduleBuildAll extends BaseBulk {
|
10 |
|
11 |
public function build() {
|
12 |
+
foreach ( $this->getAssetsThatNeedBuilt() as $asset ) {
|
13 |
try {
|
14 |
( new Build() )
|
15 |
->setMod( $this->getMod() )
|
16 |
+
->setAsset( $asset )
|
17 |
->run();
|
18 |
}
|
19 |
+
catch ( \Exception $e ) {
|
20 |
}
|
21 |
}
|
22 |
}
|
43 |
( new FindAssetsToSnap() )
|
44 |
->setMod( $this->getMod() )
|
45 |
->run(),
|
46 |
+
function ( $asset ) {
|
47 |
+
/** @var VOs\WpPluginVo|VOs\WpThemeVo $asset */
|
48 |
try {
|
49 |
+
$meta = ( new Load() )
|
50 |
->setMod( $this->getMod() )
|
51 |
+
->setAsset( $asset )
|
52 |
->run()
|
53 |
->getSnapMeta();
|
54 |
}
|
55 |
+
catch ( \Exception $e ) {
|
56 |
+
$meta = null;
|
57 |
}
|
58 |
+
return ( empty( $meta ) || $asset->version !== $meta[ 'version' ] );
|
59 |
}
|
60 |
);
|
61 |
}
|
62 |
|
63 |
+
private function getCronHook() :string {
|
|
|
|
|
|
|
64 |
return $this->getCon()->prefix( 'ptg_build_snapshots' );
|
65 |
}
|
66 |
}
|
src/lib/src/Modules/HackGuard/Lib/Snapshots/StoreAction/TouchAll.php
CHANGED
@@ -8,17 +8,17 @@ use FernleafSystems\Wordpress\Services\Services;
|
|
8 |
class TouchAll extends BaseBulk {
|
9 |
|
10 |
public function run() {
|
11 |
-
foreach ( ( new FindAssetsToSnap() )->setMod( $this->getMod() )->run() as $
|
12 |
try {
|
13 |
$oStore = ( new Load() )
|
14 |
->setMod( $this->getMod() )
|
15 |
-
->setAsset( $
|
16 |
->run();
|
17 |
-
foreach ( [ $oStore->getSnapStorePath(), $oStore->getSnapStoreMetaPath() ] as $
|
18 |
-
Services::WpFs()->touch( $
|
19 |
}
|
20 |
}
|
21 |
-
catch ( \Exception $
|
22 |
}
|
23 |
}
|
24 |
}
|
8 |
class TouchAll extends BaseBulk {
|
9 |
|
10 |
public function run() {
|
11 |
+
foreach ( ( new FindAssetsToSnap() )->setMod( $this->getMod() )->run() as $asset ) {
|
12 |
try {
|
13 |
$oStore = ( new Load() )
|
14 |
->setMod( $this->getMod() )
|
15 |
+
->setAsset( $asset )
|
16 |
->run();
|
17 |
+
foreach ( [ $oStore->getSnapStorePath(), $oStore->getSnapStoreMetaPath() ] as $path ) {
|
18 |
+
Services::WpFs()->touch( $path );
|
19 |
}
|
20 |
}
|
21 |
+
catch ( \Exception $e ) {
|
22 |
}
|
23 |
}
|
24 |
}
|
src/lib/src/Modules/HackGuard/ModCon.php
CHANGED
@@ -19,11 +19,6 @@ class ModCon extends BaseShield\ModCon {
|
|
19 |
*/
|
20 |
private $scanQueueCon;
|
21 |
|
22 |
-
/**
|
23 |
-
* @var Scan\Controller\Base[]
|
24 |
-
*/
|
25 |
-
private $scansCons;
|
26 |
-
|
27 |
/**
|
28 |
* @var Lib\FileLocker\FileLockerController
|
29 |
*/
|
@@ -62,22 +57,13 @@ class ModCon extends BaseShield\ModCon {
|
|
62 |
return $this->scanQueueCon;
|
63 |
}
|
64 |
|
65 |
-
/**
|
66 |
-
* @return Scan\Controller\Base[]
|
67 |
-
* @deprecated 10.1
|
68 |
-
*/
|
69 |
-
public function getAllScanCons() :array {
|
70 |
-
return $this->scansCons ?? $this->getScansCon()->getAllScanCons();
|
71 |
-
}
|
72 |
-
|
73 |
/**
|
74 |
* @param string $slug
|
75 |
* @return Scan\Controller\Base|mixed
|
76 |
* @throws \Exception
|
77 |
*/
|
78 |
public function getScanCon( string $slug ) {
|
79 |
-
return
|
80 |
-
$this->getScansCon()->getScanCon( $slug ) : $this->scansCons[ $slug ];
|
81 |
}
|
82 |
|
83 |
public function getMainWpData() :array {
|
@@ -242,12 +228,4 @@ class ModCon extends BaseShield\ModCon {
|
|
242 |
// 2. Clean out the file locker
|
243 |
$this->getFileLocker()->purge();
|
244 |
}
|
245 |
-
|
246 |
-
/**
|
247 |
-
* @return bool
|
248 |
-
* @deprecated 10.1
|
249 |
-
*/
|
250 |
-
public function isWpvulnPluginsHighlightEnabled() :bool {
|
251 |
-
return false;
|
252 |
-
}
|
253 |
}
|
19 |
*/
|
20 |
private $scanQueueCon;
|
21 |
|
|
|
|
|
|
|
|
|
|
|
22 |
/**
|
23 |
* @var Lib\FileLocker\FileLockerController
|
24 |
*/
|
57 |
return $this->scanQueueCon;
|
58 |
}
|
59 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
60 |
/**
|
61 |
* @param string $slug
|
62 |
* @return Scan\Controller\Base|mixed
|
63 |
* @throws \Exception
|
64 |
*/
|
65 |
public function getScanCon( string $slug ) {
|
66 |
+
return $this->getScansCon()->getScanCon( $slug );
|
|
|
67 |
}
|
68 |
|
69 |
public function getMainWpData() :array {
|
228 |
// 2. Clean out the file locker
|
229 |
$this->getFileLocker()->purge();
|
230 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
231 |
}
|
src/lib/src/Modules/HackGuard/Options.php
CHANGED
@@ -29,7 +29,7 @@ class Options extends BaseShield\Options {
|
|
29 |
}
|
30 |
|
31 |
public function getMalConfidenceBoundary() :int {
|
32 |
-
return (int)apply_filters( '
|
33 |
}
|
34 |
|
35 |
/**
|
29 |
}
|
30 |
|
31 |
public function getMalConfidenceBoundary() :int {
|
32 |
+
return (int)apply_filters( 'shield/fp_confidence_boundary', 65 );
|
33 |
}
|
34 |
|
35 |
/**
|
src/lib/src/Modules/HackGuard/Scan/Controller/Base.php
CHANGED
@@ -2,7 +2,7 @@
|
|
2 |
|
3 |
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\HackGuard\Scan\Controller;
|
4 |
|
5 |
-
use FernleafSystems\Utilities\Logic\
|
6 |
use FernleafSystems\Wordpress\Plugin\Shield\Databases;
|
7 |
use FernleafSystems\Wordpress\Plugin\Shield\Modules\HackGuard;
|
8 |
use FernleafSystems\Wordpress\Plugin\Shield\Modules\HackGuard\ModCon;
|
@@ -17,7 +17,7 @@ use FernleafSystems\Wordpress\Services\Services;
|
|
17 |
abstract class Base {
|
18 |
|
19 |
use ModConsumer;
|
20 |
-
use
|
21 |
|
22 |
const SCAN_SLUG = '';
|
23 |
|
@@ -90,7 +90,7 @@ abstract class Base {
|
|
90 |
* @param BaseResultItem|mixed $item
|
91 |
* @return bool
|
92 |
*/
|
93 |
-
abstract protected function isResultItemStale( $item );
|
94 |
|
95 |
/**
|
96 |
* @param int|string $itemID
|
@@ -98,28 +98,28 @@ abstract class Base {
|
|
98 |
* @return bool
|
99 |
* @throws \Exception
|
100 |
*/
|
101 |
-
public function executeItemAction( $itemID, $action ) {
|
102 |
-
$
|
103 |
|
104 |
if ( is_numeric( $itemID ) ) {
|
105 |
-
/** @var Databases\Scanner\EntryVO $
|
106 |
-
$
|
107 |
-
|
108 |
-
|
109 |
-
if ( empty( $
|
110 |
throw new \Exception( 'Item could not be found.' );
|
111 |
}
|
112 |
|
113 |
-
$
|
114 |
->setScanController( $this )
|
115 |
-
->convertVoToResultItem( $
|
116 |
|
117 |
-
$
|
118 |
-
|
119 |
-
|
120 |
}
|
121 |
|
122 |
-
return $
|
123 |
}
|
124 |
|
125 |
/**
|
@@ -244,7 +244,7 @@ abstract class Base {
|
|
244 |
->setScanItem( $oItem )
|
245 |
->repair();
|
246 |
}
|
247 |
-
catch ( \Exception $
|
248 |
}
|
249 |
}
|
250 |
$this->cleanStalesResults();
|
2 |
|
3 |
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\HackGuard\Scan\Controller;
|
4 |
|
5 |
+
use FernleafSystems\Utilities\Logic\ExecOnce;
|
6 |
use FernleafSystems\Wordpress\Plugin\Shield\Databases;
|
7 |
use FernleafSystems\Wordpress\Plugin\Shield\Modules\HackGuard;
|
8 |
use FernleafSystems\Wordpress\Plugin\Shield\Modules\HackGuard\ModCon;
|
17 |
abstract class Base {
|
18 |
|
19 |
use ModConsumer;
|
20 |
+
use ExecOnce;
|
21 |
|
22 |
const SCAN_SLUG = '';
|
23 |
|
90 |
* @param BaseResultItem|mixed $item
|
91 |
* @return bool
|
92 |
*/
|
93 |
+
abstract protected function isResultItemStale( $item ) :bool;
|
94 |
|
95 |
/**
|
96 |
* @param int|string $itemID
|
98 |
* @return bool
|
99 |
* @throws \Exception
|
100 |
*/
|
101 |
+
public function executeItemAction( $itemID, string $action ) {
|
102 |
+
$success = false;
|
103 |
|
104 |
if ( is_numeric( $itemID ) ) {
|
105 |
+
/** @var Databases\Scanner\EntryVO $entry */
|
106 |
+
$entry = $this->getScanResultsDbHandler()
|
107 |
+
->getQuerySelector()
|
108 |
+
->byId( $itemID );
|
109 |
+
if ( empty( $entry ) ) {
|
110 |
throw new \Exception( 'Item could not be found.' );
|
111 |
}
|
112 |
|
113 |
+
$entry = ( new HackGuard\Scan\Results\ConvertBetweenTypes() )
|
114 |
->setScanController( $this )
|
115 |
+
->convertVoToResultItem( $entry );
|
116 |
|
117 |
+
$success = $this->getItemActionHandler()
|
118 |
+
->setScanItem( $entry )
|
119 |
+
->process( $action );
|
120 |
}
|
121 |
|
122 |
+
return $success;
|
123 |
}
|
124 |
|
125 |
/**
|
244 |
->setScanItem( $oItem )
|
245 |
->repair();
|
246 |
}
|
247 |
+
catch ( \Exception $e ) {
|
248 |
}
|
249 |
}
|
250 |
$this->cleanStalesResults();
|
src/lib/src/Modules/HackGuard/Scan/Controller/BaseForAssets.php
CHANGED
@@ -11,15 +11,15 @@ abstract class BaseForAssets extends Base {
|
|
11 |
* @param Scans\Ptg\ResultItem|Scans\Wpv\ResultItem|Scans\Apc\ResultItem $item
|
12 |
* @return bool
|
13 |
*/
|
14 |
-
protected function isResultItemStale( $item ) {
|
15 |
if ( $item->context == 'plugins' ) {
|
16 |
-
$
|
17 |
-
$
|
18 |
}
|
19 |
else {
|
20 |
-
$
|
21 |
-
$
|
22 |
}
|
23 |
-
return
|
24 |
}
|
25 |
}
|
11 |
* @param Scans\Ptg\ResultItem|Scans\Wpv\ResultItem|Scans\Apc\ResultItem $item
|
12 |
* @return bool
|
13 |
*/
|
14 |
+
protected function isResultItemStale( $item ) :bool {
|
15 |
if ( $item->context == 'plugins' ) {
|
16 |
+
$asset = Services::WpPlugins()->getPluginAsVo( $item->slug );
|
17 |
+
$stale = empty( $asset ) || !$asset->active;
|
18 |
}
|
19 |
else {
|
20 |
+
$asset = Services::WpThemes()->getThemeAsVo( $item->slug );
|
21 |
+
$stale = empty( $asset ) || ( !$asset->active && !$asset->is_parent );
|
22 |
}
|
23 |
+
return $stale;
|
24 |
}
|
25 |
}
|
src/lib/src/Modules/HackGuard/Scan/Controller/Mal.php
CHANGED
@@ -21,13 +21,13 @@ class Mal extends Base {
|
|
21 |
|
22 |
$oRes = new Scans\Mal\ResultsSet();
|
23 |
|
24 |
-
/** @var Scans\Mal\ResultItem $
|
25 |
-
foreach ( parent::getItemsToAutoRepair()->getAllItems() as $
|
26 |
|
27 |
try {
|
28 |
if ( $opts->isRepairFilePlugin()
|
29 |
-
&& ( new WpOrg\Plugin\Files() )->isValidFileFromPlugin( $
|
30 |
-
$oRes->addItem( $
|
31 |
}
|
32 |
}
|
33 |
catch ( \InvalidArgumentException $e ) {
|
@@ -35,16 +35,16 @@ class Mal extends Base {
|
|
35 |
|
36 |
try {
|
37 |
if ( $opts->isRepairFileTheme()
|
38 |
-
&& ( new WpOrg\Theme\Files() )->isValidFileFromTheme( $
|
39 |
-
$oRes->addItem( $
|
40 |
}
|
41 |
}
|
42 |
catch ( \InvalidArgumentException $e ) {
|
43 |
}
|
44 |
|
45 |
if ( !$opts->isRepairFileWP()
|
46 |
-
&& Services::CoreFileHashes()->isCoreFile( $
|
47 |
-
$oRes->addItem( $
|
48 |
}
|
49 |
}
|
50 |
|
@@ -55,7 +55,7 @@ class Mal extends Base {
|
|
55 |
* @param Scans\Mal\ResultItem $item
|
56 |
* @return bool
|
57 |
*/
|
58 |
-
protected function isResultItemStale( $item ) {
|
59 |
return !Services::WpFs()->exists( $item->path_full );
|
60 |
}
|
61 |
|
21 |
|
22 |
$oRes = new Scans\Mal\ResultsSet();
|
23 |
|
24 |
+
/** @var Scans\Mal\ResultItem $item */
|
25 |
+
foreach ( parent::getItemsToAutoRepair()->getAllItems() as $item ) {
|
26 |
|
27 |
try {
|
28 |
if ( $opts->isRepairFilePlugin()
|
29 |
+
&& ( new WpOrg\Plugin\Files() )->isValidFileFromPlugin( $item->path_full ) ) {
|
30 |
+
$oRes->addItem( $item );
|
31 |
}
|
32 |
}
|
33 |
catch ( \InvalidArgumentException $e ) {
|
35 |
|
36 |
try {
|
37 |
if ( $opts->isRepairFileTheme()
|
38 |
+
&& ( new WpOrg\Theme\Files() )->isValidFileFromTheme( $item->path_full ) ) {
|
39 |
+
$oRes->addItem( $item );
|
40 |
}
|
41 |
}
|
42 |
catch ( \InvalidArgumentException $e ) {
|
43 |
}
|
44 |
|
45 |
if ( !$opts->isRepairFileWP()
|
46 |
+
&& Services::CoreFileHashes()->isCoreFile( $item->path_full ) ) {
|
47 |
+
$oRes->addItem( $item );
|
48 |
}
|
49 |
}
|
50 |
|
55 |
* @param Scans\Mal\ResultItem $item
|
56 |
* @return bool
|
57 |
*/
|
58 |
+
protected function isResultItemStale( $item ) :bool {
|
59 |
return !Services::WpFs()->exists( $item->path_full );
|
60 |
}
|
61 |
|
src/lib/src/Modules/HackGuard/Scan/Controller/Ptg.php
CHANGED
@@ -50,14 +50,12 @@ class Ptg extends BaseForAssets {
|
|
50 |
* @param Scans\Mal\ResultItem $item
|
51 |
* @return bool
|
52 |
*/
|
53 |
-
protected function isResultItemStale( $item ) {
|
54 |
-
$
|
55 |
-
|
56 |
-
|
57 |
-
$oAsset = ( new WpOrg\Theme\Files() )->findThemeFromFile( $item->path_full );
|
58 |
-
$bStale = empty( $oAsset );
|
59 |
}
|
60 |
-
return $
|
61 |
}
|
62 |
|
63 |
/**
|
50 |
* @param Scans\Mal\ResultItem $item
|
51 |
* @return bool
|
52 |
*/
|
53 |
+
protected function isResultItemStale( $item ) :bool {
|
54 |
+
$asset = ( new WpOrg\Plugin\Files() )->findPluginFromFile( $item->path_full );
|
55 |
+
if ( empty( $asset ) ) {
|
56 |
+
$asset = ( new WpOrg\Theme\Files() )->findThemeFromFile( $item->path_full );
|
|
|
|
|
57 |
}
|
58 |
+
return empty( $asset );
|
59 |
}
|
60 |
|
61 |
/**
|
src/lib/src/Modules/HackGuard/Scan/Controller/Ufc.php
CHANGED
@@ -21,7 +21,7 @@ class Ufc extends Base {
|
|
21 |
* @param Scans\Mal\ResultItem $item
|
22 |
* @return bool
|
23 |
*/
|
24 |
-
protected function isResultItemStale( $item ) {
|
25 |
return !Services::WpFs()->exists( $item->path_full );
|
26 |
}
|
27 |
|
21 |
* @param Scans\Mal\ResultItem $item
|
22 |
* @return bool
|
23 |
*/
|
24 |
+
protected function isResultItemStale( $item ) :bool {
|
25 |
return !Services::WpFs()->exists( $item->path_full );
|
26 |
}
|
27 |
|
src/lib/src/Modules/HackGuard/Scan/Controller/Wcf.php
CHANGED
@@ -21,10 +21,9 @@ class Wcf extends Base {
|
|
21 |
* @param Scans\Wcf\ResultItem $item
|
22 |
* @return bool
|
23 |
*/
|
24 |
-
protected function isResultItemStale( $item ) {
|
25 |
-
$
|
26 |
-
return !$
|
27 |
-
|| Services::CoreFileHashes()->isCoreFileHashValid( $item->path_full );
|
28 |
}
|
29 |
|
30 |
public function isCronAutoRepair() :bool {
|
21 |
* @param Scans\Wcf\ResultItem $item
|
22 |
* @return bool
|
23 |
*/
|
24 |
+
protected function isResultItemStale( $item ) :bool {
|
25 |
+
$CFH = Services::CoreFileHashes();
|
26 |
+
return !$CFH->isCoreFile( $item->path_full ) || $CFH->isCoreFileHashValid( $item->path_full );
|
|
|
27 |
}
|
28 |
|
29 |
public function isCronAutoRepair() :bool {
|
src/lib/src/Modules/HackGuard/Scan/Queue/Build/QueueBuilder.php
CHANGED
@@ -49,19 +49,19 @@ class QueueBuilder extends Utilities\BackgroundProcessing\BackgroundProcess {
|
|
49 |
* in the next pass through. Or, return false to remove the
|
50 |
* item from the queue.
|
51 |
*
|
52 |
-
* @param string $
|
53 |
* @return mixed
|
54 |
*/
|
55 |
-
protected function task( $
|
56 |
|
57 |
try {
|
58 |
( new HackGuard\Scan\Queue\ScanInitiate() )
|
59 |
->setMod( $this->getMod() )
|
60 |
->setQueueProcessor( $this->getQueueProcessor() )
|
61 |
-
->init( $
|
62 |
}
|
63 |
-
catch ( \Exception $
|
64 |
-
// error_log( $
|
65 |
}
|
66 |
|
67 |
// deletes the scan from the to-be-built array
|
49 |
* in the next pass through. Or, return false to remove the
|
50 |
* item from the queue.
|
51 |
*
|
52 |
+
* @param string $slug .
|
53 |
* @return mixed
|
54 |
*/
|
55 |
+
protected function task( $slug ) {
|
56 |
|
57 |
try {
|
58 |
( new HackGuard\Scan\Queue\ScanInitiate() )
|
59 |
->setMod( $this->getMod() )
|
60 |
->setQueueProcessor( $this->getQueueProcessor() )
|
61 |
+
->init( $slug );
|
62 |
}
|
63 |
+
catch ( \Exception $e ) {
|
64 |
+
// error_log( $e->getMessage() );
|
65 |
}
|
66 |
|
67 |
// deletes the scan from the to-be-built array
|
src/lib/src/Modules/HackGuard/Scan/Queue/QueueProcessor.php
CHANGED
@@ -54,8 +54,8 @@ class QueueProcessor extends Utilities\BackgroundProcessing\BackgroundProcess {
|
|
54 |
->setMod( $this->getMod() )
|
55 |
->execute( $oEntry );
|
56 |
}
|
57 |
-
catch ( \Exception $
|
58 |
-
// error_log( $
|
59 |
}
|
60 |
|
61 |
$oUpd->setFinished( $oEntry );
|
54 |
->setMod( $this->getMod() )
|
55 |
->execute( $oEntry );
|
56 |
}
|
57 |
+
catch ( \Exception $e ) {
|
58 |
+
// error_log( $e->getMessage() );
|
59 |
}
|
60 |
|
61 |
$oUpd->setFinished( $oEntry );
|
src/lib/src/Modules/HackGuard/Scan/ScansController.php
CHANGED
@@ -2,7 +2,7 @@
|
|
2 |
|
3 |
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\HackGuard\Scan;
|
4 |
|
5 |
-
use FernleafSystems\Utilities\Logic\
|
6 |
use FernleafSystems\Wordpress\Plugin\Shield\Crons\StandardCron;
|
7 |
use FernleafSystems\Wordpress\Plugin\Shield\Databases;
|
8 |
use FernleafSystems\Wordpress\Plugin\Shield\Modules\HackGuard;
|
@@ -13,7 +13,7 @@ use FernleafSystems\Wordpress\Services\Services;
|
|
13 |
class ScansController {
|
14 |
|
15 |
use ModConsumer;
|
16 |
-
use
|
17 |
use StandardCron;
|
18 |
|
19 |
private $scanCons;
|
@@ -23,8 +23,6 @@ class ScansController {
|
|
23 |
}
|
24 |
|
25 |
protected function run() {
|
26 |
-
/** @var HackGuard\ModCon $mod */
|
27 |
-
$mod = $this->getMod();
|
28 |
foreach ( $this->getAllScanCons() as $scanCon ) {
|
29 |
$scanCon->execute();
|
30 |
}
|
2 |
|
3 |
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\HackGuard\Scan;
|
4 |
|
5 |
+
use FernleafSystems\Utilities\Logic\ExecOnce;
|
6 |
use FernleafSystems\Wordpress\Plugin\Shield\Crons\StandardCron;
|
7 |
use FernleafSystems\Wordpress\Plugin\Shield\Databases;
|
8 |
use FernleafSystems\Wordpress\Plugin\Shield\Modules\HackGuard;
|
13 |
class ScansController {
|
14 |
|
15 |
use ModConsumer;
|
16 |
+
use ExecOnce;
|
17 |
use StandardCron;
|
18 |
|
19 |
private $scanCons;
|
23 |
}
|
24 |
|
25 |
protected function run() {
|
|
|
|
|
26 |
foreach ( $this->getAllScanCons() as $scanCon ) {
|
27 |
$scanCon->execute();
|
28 |
}
|
src/lib/src/Modules/HackGuard/Scan/Utilities/PtgAddReinstallLinks.php
CHANGED
@@ -2,7 +2,7 @@
|
|
2 |
|
3 |
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\HackGuard\Scan\Utilities;
|
4 |
|
5 |
-
use FernleafSystems\Utilities\Logic\
|
6 |
use FernleafSystems\Wordpress\Plugin\Shield\Modules\HackGuard;
|
7 |
use FernleafSystems\Wordpress\Plugin\Shield\Modules\HackGuard\Scan\Controller;
|
8 |
use FernleafSystems\Wordpress\Services\Core\VOs\WpPluginVo;
|
@@ -11,7 +11,7 @@ use FernleafSystems\Wordpress\Services\Services;
|
|
11 |
class PtgAddReinstallLinks {
|
12 |
|
13 |
use Controller\ScanControllerConsumer;
|
14 |
-
use
|
15 |
|
16 |
/**
|
17 |
* @var int
|
@@ -33,14 +33,32 @@ class PtgAddReinstallLinks {
|
|
33 |
add_action( 'admin_footer', function () {
|
34 |
$this->printPluginReinstallDialogs();
|
35 |
} );
|
36 |
-
|
37 |
-
|
38 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
39 |
}
|
40 |
-
|
|
|
41 |
}
|
42 |
|
43 |
-
protected function canRun() {
|
44 |
$scanCon = $this->getScanController();
|
45 |
/** @var HackGuard\Options $opts */
|
46 |
$opts = $scanCon->getOptions();
|
@@ -59,28 +77,6 @@ class PtgAddReinstallLinks {
|
|
59 |
return $links;
|
60 |
}
|
61 |
|
62 |
-
private function insertCustomJsVars() {
|
63 |
-
$scanCon = $this->getScanController();
|
64 |
-
wp_localize_script(
|
65 |
-
$scanCon->getCon()->prefix( 'global-plugin' ),
|
66 |
-
'icwp_wpsf_vars_hp',
|
67 |
-
[
|
68 |
-
'ajax_plugin_reinstall' => $scanCon->getMod()->getAjaxActionData( 'plugin_reinstall' ),
|
69 |
-
'reinstallable' => Services::WpPlugins()->getInstalledWpOrgPluginFiles(),
|
70 |
-
'strings' => [
|
71 |
-
'reinstall_first' => __( 'Re-install First', 'wp-simple-firewall' )
|
72 |
-
.'. '.__( 'Then Activate', 'wp-simple-firewall' ),
|
73 |
-
'okay_reinstall' => sprintf( '%s, %s',
|
74 |
-
__( 'Yes', 'wp-simple-firewall' ), __( 'Re-Install It', 'wp-simple-firewall' ) ),
|
75 |
-
'activate_only' => __( 'Activate Only', 'wp-simple-firewall' ),
|
76 |
-
'cancel' => __( 'Cancel', 'wp-simple-firewall' ),
|
77 |
-
]
|
78 |
-
]
|
79 |
-
);
|
80 |
-
wp_enqueue_script( 'jquery-ui-dialog' ); // jquery and jquery-ui should be dependencies, didn't check though...
|
81 |
-
wp_enqueue_style( 'wp-jquery-ui-dialog' );
|
82 |
-
}
|
83 |
-
|
84 |
private function printPluginReinstallDialogs() {
|
85 |
$scanCon = $this->getScanController();
|
86 |
echo $scanCon->getMod()
|
2 |
|
3 |
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\HackGuard\Scan\Utilities;
|
4 |
|
5 |
+
use FernleafSystems\Utilities\Logic\ExecOnce;
|
6 |
use FernleafSystems\Wordpress\Plugin\Shield\Modules\HackGuard;
|
7 |
use FernleafSystems\Wordpress\Plugin\Shield\Modules\HackGuard\Scan\Controller;
|
8 |
use FernleafSystems\Wordpress\Services\Core\VOs\WpPluginVo;
|
11 |
class PtgAddReinstallLinks {
|
12 |
|
13 |
use Controller\ScanControllerConsumer;
|
14 |
+
use ExecOnce;
|
15 |
|
16 |
/**
|
17 |
* @var int
|
33 |
add_action( 'admin_footer', function () {
|
34 |
$this->printPluginReinstallDialogs();
|
35 |
} );
|
36 |
+
|
37 |
+
add_filter( 'shield/custom_localisations', function ( array $localz, $hook ) {
|
38 |
+
if ( in_array( $hook, [ 'plugins.php', ] ) ) {
|
39 |
+
$localz[] = [
|
40 |
+
'global-plugin',
|
41 |
+
'icwp_wpsf_vars_hp',
|
42 |
+
[
|
43 |
+
'ajax_plugin_reinstall' => $this->getScanController()->getMod()
|
44 |
+
->getAjaxActionData( 'plugin_reinstall' ),
|
45 |
+
'reinstallable' => Services::WpPlugins()->getInstalledWpOrgPluginFiles(),
|
46 |
+
'strings' => [
|
47 |
+
'reinstall_first' => __( 'Re-install First', 'wp-simple-firewall' )
|
48 |
+
.'. '.__( 'Then Activate', 'wp-simple-firewall' ),
|
49 |
+
'okay_reinstall' => sprintf( '%s, %s',
|
50 |
+
__( 'Yes', 'wp-simple-firewall' ), __( 'Re-Install It', 'wp-simple-firewall' ) ),
|
51 |
+
'activate_only' => __( 'Activate Only', 'wp-simple-firewall' ),
|
52 |
+
'cancel' => __( 'Cancel', 'wp-simple-firewall' ),
|
53 |
+
]
|
54 |
+
]
|
55 |
+
];
|
56 |
}
|
57 |
+
return $localz;
|
58 |
+
}, 10, 2 );
|
59 |
}
|
60 |
|
61 |
+
protected function canRun() :bool {
|
62 |
$scanCon = $this->getScanController();
|
63 |
/** @var HackGuard\Options $opts */
|
64 |
$opts = $scanCon->getOptions();
|
77 |
return $links;
|
78 |
}
|
79 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
80 |
private function printPluginReinstallDialogs() {
|
81 |
$scanCon = $this->getScanController();
|
82 |
echo $scanCon->getMod()
|
src/lib/src/Modules/HackGuard/Scan/Utilities/WpvAddPluginRows.php
CHANGED
@@ -109,22 +109,34 @@ class WpvAddPluginRows {
|
|
109 |
|
110 |
$vulns = $scanCon->getPluginVulnerabilities( $pluginFile );
|
111 |
if ( count( $vulns ) > 0 ) {
|
112 |
-
$
|
113 |
echo $scanCon->getMod()
|
114 |
->renderTemplate(
|
115 |
-
'snippets/
|
116 |
[
|
117 |
-
'strings'
|
118 |
'known_vuln' => sprintf( __( '%s has discovered that the currently installed version of the %s plugin has known security vulnerabilities.', 'wp-simple-firewall' ),
|
119 |
-
$
|
120 |
'name' => __( 'Vulnerability Name', 'wp-simple-firewall' ),
|
121 |
'type' => __( 'Vulnerability Type', 'wp-simple-firewall' ),
|
122 |
'fixed_versions' => __( 'Fixed Versions', 'wp-simple-firewall' ),
|
123 |
'more_info' => __( 'More Info', 'wp-simple-firewall' ),
|
124 |
],
|
125 |
-
'
|
126 |
-
|
127 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
128 |
);
|
129 |
}
|
130 |
}
|
109 |
|
110 |
$vulns = $scanCon->getPluginVulnerabilities( $pluginFile );
|
111 |
if ( count( $vulns ) > 0 ) {
|
112 |
+
$name = $scanCon->getCon()->getHumanName();
|
113 |
echo $scanCon->getMod()
|
114 |
->renderTemplate(
|
115 |
+
'/snippets/plugin_vulnerability.twig',
|
116 |
[
|
117 |
+
'strings' => [
|
118 |
'known_vuln' => sprintf( __( '%s has discovered that the currently installed version of the %s plugin has known security vulnerabilities.', 'wp-simple-firewall' ),
|
119 |
+
$name, '<strong>'.$pData[ 'Name' ].'</strong>' ),
|
120 |
'name' => __( 'Vulnerability Name', 'wp-simple-firewall' ),
|
121 |
'type' => __( 'Vulnerability Type', 'wp-simple-firewall' ),
|
122 |
'fixed_versions' => __( 'Fixed Versions', 'wp-simple-firewall' ),
|
123 |
'more_info' => __( 'More Info', 'wp-simple-firewall' ),
|
124 |
],
|
125 |
+
'vars' => [
|
126 |
+
'vulns' => array_map(
|
127 |
+
function ( $vuln ) {
|
128 |
+
$data = $vuln->getRawData();
|
129 |
+
if ( empty( $data[ 'url' ] ) ) {
|
130 |
+
$data[ 'url' ] = $vuln->url;
|
131 |
+
}
|
132 |
+
return $data;
|
133 |
+
},
|
134 |
+
$vulns
|
135 |
+
),
|
136 |
+
'colspan' => $this->nColumnsCount
|
137 |
+
],
|
138 |
+
],
|
139 |
+
true
|
140 |
);
|
141 |
}
|
142 |
}
|
src/lib/src/Modules/HackGuard/UI.php
CHANGED
@@ -241,10 +241,7 @@ class UI extends BaseShield\UI {
|
|
241 |
];
|
242 |
}
|
243 |
|
244 |
-
|
245 |
-
* @return array
|
246 |
-
*/
|
247 |
-
private function getInsightVarsScan_Ptg() {
|
248 |
/** @var ModCon $mod */
|
249 |
$mod = $this->getMod();
|
250 |
|
241 |
];
|
242 |
}
|
243 |
|
244 |
+
private function getInsightVarsScan_Ptg() :array {
|
|
|
|
|
|
|
245 |
/** @var ModCon $mod */
|
246 |
$mod = $this->getMod();
|
247 |
|
src/lib/src/Modules/HackGuard/Upgrade.php
CHANGED
@@ -1,11 +1,22 @@
|
|
1 |
-
<?php
|
2 |
|
3 |
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\HackGuard;
|
4 |
|
5 |
use FernleafSystems\Wordpress\Plugin\Shield\Modules\Base;
|
|
|
6 |
|
7 |
class Upgrade extends Base\Upgrade {
|
8 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
9 |
protected function upgrade_900() {
|
10 |
/** @var Options $opts */
|
11 |
$opts = $this->getOptions();
|
1 |
+
<?php declare( strict_types=1 );
|
2 |
|
3 |
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\HackGuard;
|
4 |
|
5 |
use FernleafSystems\Wordpress\Plugin\Shield\Modules\Base;
|
6 |
+
use FernleafSystems\Wordpress\Services\Services;
|
7 |
|
8 |
class Upgrade extends Base\Upgrade {
|
9 |
|
10 |
+
protected function upgrade_1020() {
|
11 |
+
/** @var ModCon $mod */
|
12 |
+
$mod = $this->getMod();
|
13 |
+
$schema = $mod->getDbHandler_FileLocker()->getTableSchema();
|
14 |
+
Services::WpDb()->doSql(
|
15 |
+
sprintf( "ALTER TABLE `%s` MODIFY `%s` %s;",
|
16 |
+
$schema->table, 'content', $schema->enumerateColumns()[ 'content' ] )
|
17 |
+
);
|
18 |
+
}
|
19 |
+
|
20 |
protected function upgrade_900() {
|
21 |
/** @var Options $opts */
|
22 |
$opts = $this->getOptions();
|
src/lib/src/Modules/Headers/Insights/OverviewCards.php
CHANGED
@@ -39,7 +39,7 @@ class OverviewCards extends Shield\Modules\Base\Insights\OverviewCards {
|
|
39 |
$bCsp = $opts->isEnabledContentSecurityPolicy();
|
40 |
$cards[ 'csp' ] = [
|
41 |
'name' => __( 'Content Security Policies', 'wp-simple-firewall' ),
|
42 |
-
'state' => $bCsp ? 1 :
|
43 |
'summary' => $bCsp ?
|
44 |
__( 'Content Security Policy is turned on', 'wp-simple-firewall' )
|
45 |
: __( "Content Security Policies aren't active", 'wp-simple-firewall' ),
|
39 |
$bCsp = $opts->isEnabledContentSecurityPolicy();
|
40 |
$cards[ 'csp' ] = [
|
41 |
'name' => __( 'Content Security Policies', 'wp-simple-firewall' ),
|
42 |
+
'state' => $bCsp ? 1 : 0,
|
43 |
'summary' => $bCsp ?
|
44 |
__( 'Content Security Policy is turned on', 'wp-simple-firewall' )
|
45 |
: __( "Content Security Policies aren't active", 'wp-simple-firewall' ),
|
src/lib/src/Modules/Headers/Options.php
CHANGED
@@ -2,57 +2,44 @@
|
|
2 |
|
3 |
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Headers;
|
4 |
|
5 |
-
|
6 |
-
|
7 |
-
class Options extends BaseShield\Options {
|
8 |
|
9 |
public function getCspCustomRules() :array {
|
10 |
$csp = $this->getOpt( 'xcsp_custom' );
|
11 |
-
|
|
|
|
|
|
|
|
|
12 |
}
|
13 |
|
14 |
/**
|
15 |
* Using this function without first checking isReferrerPolicyEnabled() will result in empty
|
16 |
* referrer policy header in the case of "disabled"
|
17 |
-
* @return string
|
18 |
*/
|
19 |
-
public function getReferrerPolicyValue() {
|
20 |
-
$
|
21 |
-
return in_array( $
|
22 |
}
|
23 |
|
24 |
-
|
25 |
-
|
26 |
-
|
27 |
-
public function isEnabledContentSecurityPolicy() {
|
28 |
-
return $this->isOpt( 'enable_x_content_security_policy', 'Y' );
|
29 |
}
|
30 |
|
31 |
-
|
32 |
-
* @return bool
|
33 |
-
*/
|
34 |
-
public function isEnabledContentTypeHeader() {
|
35 |
return $this->isOpt( 'x_content_type', 'Y' );
|
36 |
}
|
37 |
|
38 |
-
|
39 |
-
* @return bool
|
40 |
-
*/
|
41 |
-
public function isEnabledXssProtection() {
|
42 |
return $this->isOpt( 'x_xss_protect', 'Y' );
|
43 |
}
|
44 |
|
45 |
-
|
46 |
-
* @return bool
|
47 |
-
*/
|
48 |
-
public function isEnabledXFrame() {
|
49 |
return in_array( $this->getOpt( 'x_frame' ), [ 'on_sameorigin', 'on_deny' ] );
|
50 |
}
|
51 |
|
52 |
-
|
53 |
-
* @return bool
|
54 |
-
*/
|
55 |
-
public function isReferrerPolicyEnabled() {
|
56 |
return !$this->isOpt( 'x_referrer_policy', 'disabled' );
|
57 |
}
|
58 |
}
|
2 |
|
3 |
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Headers;
|
4 |
|
5 |
+
class Options extends \FernleafSystems\Wordpress\Plugin\Shield\Modules\BaseShield\Options {
|
|
|
|
|
6 |
|
7 |
public function getCspCustomRules() :array {
|
8 |
$csp = $this->getOpt( 'xcsp_custom' );
|
9 |
+
if ( !is_array( $csp ) ) {
|
10 |
+
$csp = [];
|
11 |
+
$this->setOpt( 'xcsp_custom', $csp );
|
12 |
+
}
|
13 |
+
return $this->isPremium() ? array_filter( array_map( 'trim', $csp ) ) : [];
|
14 |
}
|
15 |
|
16 |
/**
|
17 |
* Using this function without first checking isReferrerPolicyEnabled() will result in empty
|
18 |
* referrer policy header in the case of "disabled"
|
|
|
19 |
*/
|
20 |
+
public function getReferrerPolicyValue() :string {
|
21 |
+
$value = $this->getOpt( 'x_referrer_policy' );
|
22 |
+
return in_array( $value, [ 'empty', 'disabled' ] ) ? '' : $value;
|
23 |
}
|
24 |
|
25 |
+
public function isEnabledContentSecurityPolicy() :bool {
|
26 |
+
return $this->isOpt( 'enable_x_content_security_policy', 'Y' )
|
27 |
+
&& !empty( $this->getCspCustomRules() );
|
|
|
|
|
28 |
}
|
29 |
|
30 |
+
public function isEnabledContentTypeHeader() :bool {
|
|
|
|
|
|
|
31 |
return $this->isOpt( 'x_content_type', 'Y' );
|
32 |
}
|
33 |
|
34 |
+
public function isEnabledXssProtection() :bool {
|
|
|
|
|
|
|
35 |
return $this->isOpt( 'x_xss_protect', 'Y' );
|
36 |
}
|
37 |
|
38 |
+
public function isEnabledXFrame() :bool {
|
|
|
|
|
|
|
39 |
return in_array( $this->getOpt( 'x_frame' ), [ 'on_sameorigin', 'on_deny' ] );
|
40 |
}
|
41 |
|
42 |
+
public function isReferrerPolicyEnabled() :bool {
|
|
|
|
|
|
|
43 |
return !$this->isOpt( 'x_referrer_policy', 'disabled' );
|
44 |
}
|
45 |
}
|
src/lib/src/Modules/Headers/Processor.php
CHANGED
@@ -9,7 +9,7 @@ class Processor extends BaseShield\Processor {
|
|
9 |
/**
|
10 |
* @var bool
|
11 |
*/
|
12 |
-
private $
|
13 |
|
14 |
/**
|
15 |
* @var array
|
@@ -77,7 +77,7 @@ class Processor extends BaseShield\Processor {
|
|
77 |
/**
|
78 |
* @return string[] - array of all previously sent headers. Keys are header names, values are header values.
|
79 |
*/
|
80 |
-
private function getAlreadySentHeaders() {
|
81 |
$headers = [];
|
82 |
|
83 |
if ( function_exists( 'headers_list' ) ) {
|
@@ -127,30 +127,7 @@ class Processor extends BaseShield\Processor {
|
|
127 |
private function setContentSecurityPolicyHeader() :array {
|
128 |
/** @var Options $opts */
|
129 |
$opts = $this->getOptions();
|
130 |
-
|
131 |
-
$aDefaultSrcDirectives = [];
|
132 |
-
|
133 |
-
if ( $opts->isOpt( 'xcsp_self', 'Y' ) ) {
|
134 |
-
$aDefaultSrcDirectives[] = "'self'";
|
135 |
-
}
|
136 |
-
if ( $opts->isOpt( 'xcsp_data', 'Y' ) ) {
|
137 |
-
$aDefaultSrcDirectives[] = "data:";
|
138 |
-
}
|
139 |
-
if ( $opts->isOpt( 'xcsp_inline', 'Y' ) ) {
|
140 |
-
$aDefaultSrcDirectives[] = "'unsafe-inline'";
|
141 |
-
}
|
142 |
-
if ( $opts->isOpt( 'xcsp_eval', 'Y' ) ) {
|
143 |
-
$aDefaultSrcDirectives[] = "'unsafe-eval'";
|
144 |
-
}
|
145 |
-
if ( $opts->isOpt( 'xcsp_https', 'Y' ) ) {
|
146 |
-
$aDefaultSrcDirectives[] = "https:";
|
147 |
-
}
|
148 |
-
|
149 |
-
$aDefaultSrcDirectives[] = implode( " ", $opts->getOpt( 'xcsp_hosts', [] ) );
|
150 |
-
|
151 |
-
$rules = $opts->getCspCustomRules();
|
152 |
-
array_unshift( $rules, sprintf( 'default-src %s;', implode( " ", $aDefaultSrcDirectives ) ) );
|
153 |
-
return [ 'Content-Security-Policy' => implode( ' ', $rules ) ];
|
154 |
}
|
155 |
|
156 |
private function gatherSecurityHeaders() :array {
|
@@ -189,11 +166,11 @@ class Processor extends BaseShield\Processor {
|
|
189 |
}
|
190 |
|
191 |
private function isHeadersPushed() :bool {
|
192 |
-
return (bool)$this->
|
193 |
}
|
194 |
|
195 |
private function setHeadersPushed( bool $pushed ) :self {
|
196 |
-
$this->
|
197 |
return $this;
|
198 |
}
|
199 |
}
|
9 |
/**
|
10 |
* @var bool
|
11 |
*/
|
12 |
+
private $pushed = false;
|
13 |
|
14 |
/**
|
15 |
* @var array
|
77 |
/**
|
78 |
* @return string[] - array of all previously sent headers. Keys are header names, values are header values.
|
79 |
*/
|
80 |
+
private function getAlreadySentHeaders() :array {
|
81 |
$headers = [];
|
82 |
|
83 |
if ( function_exists( 'headers_list' ) ) {
|
127 |
private function setContentSecurityPolicyHeader() :array {
|
128 |
/** @var Options $opts */
|
129 |
$opts = $this->getOptions();
|
130 |
+
return [ 'Content-Security-Policy' => implode( ' ', $opts->getCspCustomRules() ) ];
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
131 |
}
|
132 |
|
133 |
private function gatherSecurityHeaders() :array {
|
166 |
}
|
167 |
|
168 |
private function isHeadersPushed() :bool {
|
169 |
+
return (bool)$this->pushed;
|
170 |
}
|
171 |
|
172 |
private function setHeadersPushed( bool $pushed ) :self {
|
173 |
+
$this->pushed = $pushed;
|
174 |
return $this;
|
175 |
}
|
176 |
}
|
src/lib/src/Modules/Headers/Strings.php
CHANGED
@@ -144,8 +144,13 @@ class Strings extends Base\Strings {
|
|
144 |
case 'xcsp_custom' :
|
145 |
$sName = __( 'Manual Rules', 'wp-simple-firewall' );
|
146 |
$sSummary = __( 'Manual CSP Rules', 'wp-simple-firewall' );
|
147 |
-
$sDescription =
|
148 |
-
|
|
|
|
|
|
|
|
|
|
|
149 |
break;
|
150 |
|
151 |
default:
|
144 |
case 'xcsp_custom' :
|
145 |
$sName = __( 'Manual Rules', 'wp-simple-firewall' );
|
146 |
$sSummary = __( 'Manual CSP Rules', 'wp-simple-firewall' );
|
147 |
+
$sDescription = [
|
148 |
+
__( 'Manual CSP rules which are not covered by the rules above.', 'wp-simple-firewall' ),
|
149 |
+
'- '.__( 'Take a new line per rule.', 'wp-simple-firewall' ),
|
150 |
+
'- '.__( 'We provide this feature as-is: to allow you to add custom CSP rules to your site.', 'wp-simple-firewall' ),
|
151 |
+
'- '.__( "We don't provide support for creating CSP rules and whether they're correct for your site.", 'wp-simple-firewall' ),
|
152 |
+
'- '.__( "Many WordPress caching plugins ignore HTTP Headers - if they're not showing up, disable page caching.", 'wp-simple-firewall' )
|
153 |
+
];
|
154 |
break;
|
155 |
|
156 |
default:
|
src/lib/src/Modules/IPs/AjaxHandler.php
CHANGED
@@ -88,7 +88,7 @@ class AjaxHandler extends Shield\Modules\BaseShield\AjaxHandler {
|
|
88 |
->setIP( $ip )
|
89 |
->toManualWhitelist( (string)$label );
|
90 |
}
|
91 |
-
catch ( \Exception $
|
92 |
}
|
93 |
break;
|
94 |
|
@@ -99,7 +99,7 @@ class AjaxHandler extends Shield\Modules\BaseShield\AjaxHandler {
|
|
99 |
->setIP( $ip )
|
100 |
->toManualBlacklist( (string)$label );
|
101 |
}
|
102 |
-
catch ( \Exception $
|
103 |
}
|
104 |
break;
|
105 |
|
88 |
->setIP( $ip )
|
89 |
->toManualWhitelist( (string)$label );
|
90 |
}
|
91 |
+
catch ( \Exception $e ) {
|
92 |
}
|
93 |
break;
|
94 |
|
99 |
->setIP( $ip )
|
100 |
->toManualBlacklist( (string)$label );
|
101 |
}
|
102 |
+
catch ( \Exception $e ) {
|
103 |
}
|
104 |
break;
|
105 |
|
src/lib/src/Modules/IPs/BotTrack/Base.php
CHANGED
@@ -42,10 +42,7 @@ abstract class Base {
|
|
42 |
);
|
43 |
}
|
44 |
|
45 |
-
|
46 |
-
* @return array
|
47 |
-
*/
|
48 |
-
protected function getAuditData() {
|
49 |
return [
|
50 |
'path' => Services::Request()->getPath()
|
51 |
];
|
42 |
);
|
43 |
}
|
44 |
|
45 |
+
protected function getAuditData() :array {
|
|
|
|
|
|
|
46 |
return [
|
47 |
'path' => Services::Request()->getPath()
|
48 |
];
|
src/lib/src/Modules/IPs/BotTrack/TrackFakeWebCrawler.php
CHANGED
@@ -16,7 +16,7 @@ class TrackFakeWebCrawler extends Base {
|
|
16 |
try {
|
17 |
$this->getIfVisitorIdentifiesAsCrawler(); // TEST this logic
|
18 |
}
|
19 |
-
catch ( \Exception $
|
20 |
$this->doTransgression();
|
21 |
}
|
22 |
}
|
16 |
try {
|
17 |
$this->getIfVisitorIdentifiesAsCrawler(); // TEST this logic
|
18 |
}
|
19 |
+
catch ( \Exception $e ) {
|
20 |
$this->doTransgression();
|
21 |
}
|
22 |
}
|
src/lib/src/Modules/IPs/BotTrack/TrackLoginFailed.php
CHANGED
@@ -16,32 +16,29 @@ class TrackLoginFailed extends Base {
|
|
16 |
protected function process() {
|
17 |
add_filter( 'authenticate',
|
18 |
/**
|
19 |
-
* @param null|\WP_User|\WP_Error $
|
20 |
-
* @param string $
|
21 |
-
* @param string $
|
22 |
* @return null|\WP_User|\WP_Error
|
23 |
*/
|
24 |
-
function ( $
|
25 |
-
if ( is_wp_error( $
|
26 |
-
&& !empty( $
|
27 |
-
$this->user_login = Services::Data()->validEmail( $
|
28 |
$this->doTransgression();
|
29 |
|
30 |
// Adds an extra message to login failed
|
31 |
-
$
|
32 |
$this->getCon()->prefix( 'transgression-warning' ),
|
33 |
$this->getMod()->getTextOpt( 'text_loginfailed' )
|
34 |
);
|
35 |
}
|
36 |
-
return $
|
37 |
},
|
38 |
21, 3 ); //right after username/password check
|
39 |
}
|
40 |
|
41 |
-
|
42 |
-
* @return array
|
43 |
-
*/
|
44 |
-
protected function getAuditData() {
|
45 |
return [
|
46 |
'login' => $this->user_login
|
47 |
];
|
16 |
protected function process() {
|
17 |
add_filter( 'authenticate',
|
18 |
/**
|
19 |
+
* @param null|\WP_User|\WP_Error $user
|
20 |
+
* @param string $login
|
21 |
+
* @param string $pass
|
22 |
* @return null|\WP_User|\WP_Error
|
23 |
*/
|
24 |
+
function ( $user, $login, $pass ) {
|
25 |
+
if ( is_wp_error( $user ) && !empty( $login )
|
26 |
+
&& !empty( $pass ) && Services::WpUsers()->exists( $login ) ) {
|
27 |
+
$this->user_login = Services::Data()->validEmail( $login ) ? $login : sanitize_user( $login );
|
28 |
$this->doTransgression();
|
29 |
|
30 |
// Adds an extra message to login failed
|
31 |
+
$user->add(
|
32 |
$this->getCon()->prefix( 'transgression-warning' ),
|
33 |
$this->getMod()->getTextOpt( 'text_loginfailed' )
|
34 |
);
|
35 |
}
|
36 |
+
return $user;
|
37 |
},
|
38 |
21, 3 ); //right after username/password check
|
39 |
}
|
40 |
|
41 |
+
protected function getAuditData() :array {
|
|
|
|
|
|
|
42 |
return [
|
43 |
'login' => $this->user_login
|
44 |
];
|
src/lib/src/Modules/IPs/BotTrack/TrackLoginInvalid.php
CHANGED
@@ -16,24 +16,29 @@ class TrackLoginInvalid extends Base {
|
|
16 |
protected function process() {
|
17 |
add_filter( 'authenticate',
|
18 |
/**
|
19 |
-
* @param null|\WP_User|\WP_Error $
|
20 |
-
* @param string $
|
|
|
21 |
* @return null|\WP_User|\WP_Error
|
22 |
*/
|
23 |
-
function ( $
|
24 |
-
if (
|
25 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
26 |
$this->doTransgression();
|
27 |
}
|
28 |
-
return $
|
29 |
},
|
30 |
-
|
31 |
}
|
32 |
|
33 |
-
|
34 |
-
* @return array
|
35 |
-
*/
|
36 |
-
protected function getAuditData() {
|
37 |
return [
|
38 |
'login' => $this->user_login
|
39 |
];
|
16 |
protected function process() {
|
17 |
add_filter( 'authenticate',
|
18 |
/**
|
19 |
+
* @param null|\WP_User|\WP_Error $user
|
20 |
+
* @param string $login
|
21 |
+
* @param string $pass
|
22 |
* @return null|\WP_User|\WP_Error
|
23 |
*/
|
24 |
+
function ( $user, $login, $pass ) {
|
25 |
+
if ( Services::Request()->isPost() && is_wp_error( $user ) && !empty( $pass )
|
26 |
+
&& ( empty( $login ) || !Services::WpUsers()->exists( $login ) ) ) {
|
27 |
+
|
28 |
+
if ( empty( $login ) ) {
|
29 |
+
$this->user_login = 'empty username';
|
30 |
+
}
|
31 |
+
else {
|
32 |
+
$this->user_login = Services::Data()->validEmail( $login ) ? $login : sanitize_user( $login );
|
33 |
+
}
|
34 |
$this->doTransgression();
|
35 |
}
|
36 |
+
return $user;
|
37 |
},
|
38 |
+
21, 3 );
|
39 |
}
|
40 |
|
41 |
+
protected function getAuditData() :array {
|
|
|
|
|
|
|
42 |
return [
|
43 |
'login' => $this->user_login
|
44 |
];
|
src/lib/src/Modules/IPs/Components/QueryIpBlock.php
CHANGED
@@ -16,29 +16,21 @@ class QueryIpBlock {
|
|
16 |
use Shield\Modules\ModConsumer;
|
17 |
use IpAddressConsumer;
|
18 |
|
19 |
-
|
20 |
-
|
21 |
-
*/
|
22 |
-
private $sIP;
|
23 |
-
|
24 |
-
/**
|
25 |
-
* @return bool - true if IP is blocked, false otherwise
|
26 |
-
*/
|
27 |
-
public function run() {
|
28 |
-
$bIpBlocked = false;
|
29 |
|
30 |
$oIP = $this->getBlockedIpRecord();
|
31 |
if ( $oIP instanceof Databases\IPs\EntryVO ) {
|
32 |
|
33 |
-
$
|
34 |
|
35 |
/** @var IPs\ModCon $mod */
|
36 |
$mod = $this->getMod();
|
37 |
-
/** @var Databases\IPs\Update $
|
38 |
-
$
|
39 |
-
$
|
40 |
}
|
41 |
-
return $
|
42 |
}
|
43 |
|
44 |
/**
|
16 |
use Shield\Modules\ModConsumer;
|
17 |
use IpAddressConsumer;
|
18 |
|
19 |
+
public function run() :bool {
|
20 |
+
$isBlocked = false;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
21 |
|
22 |
$oIP = $this->getBlockedIpRecord();
|
23 |
if ( $oIP instanceof Databases\IPs\EntryVO ) {
|
24 |
|
25 |
+
$isBlocked = true;
|
26 |
|
27 |
/** @var IPs\ModCon $mod */
|
28 |
$mod = $this->getMod();
|
29 |
+
/** @var Databases\IPs\Update $upd */
|
30 |
+
$upd = $mod->getDbHandler_IPs()->getQueryUpdater();
|
31 |
+
$upd->updateLastAccessAt( $oIP );
|
32 |
}
|
33 |
+
return $isBlocked;
|
34 |
}
|
35 |
|
36 |
/**
|
src/lib/src/Modules/IPs/Components/UnblockIpByFlag.php
CHANGED
@@ -15,9 +15,9 @@ class UnblockIpByFlag {
|
|
15 |
$mod = $this->getMod();
|
16 |
$FS = Services::WpFs();
|
17 |
|
18 |
-
$
|
19 |
-
if ( $FS->isFile( $
|
20 |
-
$sContent = $FS->getFileContent( $
|
21 |
if ( !empty( $sContent ) ) {
|
22 |
|
23 |
$aLines = array_map( 'trim', explode( "\n", $sContent ) );
|
@@ -31,7 +31,7 @@ class UnblockIpByFlag {
|
|
31 |
}
|
32 |
}
|
33 |
}
|
34 |
-
$FS->deleteFile( $
|
35 |
}
|
36 |
}
|
37 |
}
|
15 |
$mod = $this->getMod();
|
16 |
$FS = Services::WpFs();
|
17 |
|
18 |
+
$path = $FS->findFileInDir( 'unblock', $this->getCon()->getPath_Flags() );
|
19 |
+
if ( !empty( $path ) && $FS->isFile( $path ) ) {
|
20 |
+
$sContent = $FS->getFileContent( $path );
|
21 |
if ( !empty( $sContent ) ) {
|
22 |
|
23 |
$aLines = array_map( 'trim', explode( "\n", $sContent ) );
|
31 |
}
|
32 |
}
|
33 |
}
|
34 |
+
$FS->deleteFile( $path );
|
35 |
}
|
36 |
}
|
37 |
}
|
src/lib/src/Modules/IPs/Lib/BlockRequest.php
CHANGED
@@ -15,8 +15,7 @@ class BlockRequest {
|
|
15 |
if ( $this->isBlocked() ) {
|
16 |
|
17 |
if ( $this->isAutoUnBlocked() ) {
|
18 |
-
|
19 |
-
Services::Response()->redirectToAdmin();
|
20 |
}
|
21 |
|
22 |
// don't log killed requests
|
@@ -26,20 +25,14 @@ class BlockRequest {
|
|
26 |
}
|
27 |
}
|
28 |
|
29 |
-
|
30 |
-
* @return bool
|
31 |
-
*/
|
32 |
-
private function isBlocked() {
|
33 |
return ( new IPs\Components\QueryIpBlock() )
|
34 |
->setMod( $this->getMod() )
|
35 |
->setIp( Services::IP()->getRequestIp() )
|
36 |
->run();
|
37 |
}
|
38 |
|
39 |
-
|
40 |
-
* @return bool
|
41 |
-
*/
|
42 |
-
private function isAutoUnBlocked() {
|
43 |
return ( new AutoUnblock() )
|
44 |
->setMod( $this->getMod() )
|
45 |
->run();
|
15 |
if ( $this->isBlocked() ) {
|
16 |
|
17 |
if ( $this->isAutoUnBlocked() ) {
|
18 |
+
Services::Response()->redirectToHome();
|
|
|
19 |
}
|
20 |
|
21 |
// don't log killed requests
|
25 |
}
|
26 |
}
|
27 |
|
28 |
+
private function isBlocked() :bool {
|
|
|
|
|
|
|
29 |
return ( new IPs\Components\QueryIpBlock() )
|
30 |
->setMod( $this->getMod() )
|
31 |
->setIp( Services::IP()->getRequestIp() )
|
32 |
->run();
|
33 |
}
|
34 |
|
35 |
+
private function isAutoUnBlocked() :bool {
|
|
|
|
|
|
|
36 |
return ( new AutoUnblock() )
|
37 |
->setMod( $this->getMod() )
|
38 |
->run();
|
src/lib/src/Modules/IPs/Lib/Ops/LookupIpOnList.php
CHANGED
@@ -14,26 +14,26 @@ class LookupIpOnList {
|
|
14 |
/**
|
15 |
* @var string
|
16 |
*/
|
17 |
-
private $
|
18 |
|
19 |
/**
|
20 |
* @var bool
|
21 |
*/
|
22 |
-
private $
|
23 |
|
24 |
/**
|
25 |
-
* @param bool $
|
26 |
* @return Databases\IPs\EntryVO|null
|
27 |
* @version 8.6.0 - switched to lookup ranges first
|
28 |
*/
|
29 |
-
public function lookup( $
|
30 |
$IP = null;
|
31 |
if ( !empty( $this->getIP() ) ) {
|
32 |
-
if ( $
|
33 |
-
foreach ( $this->lookupRange() as $
|
34 |
try {
|
35 |
-
if ( Services::IP()->checkIp( $this->getIP(), $
|
36 |
-
$IP = $
|
37 |
break;
|
38 |
}
|
39 |
}
|
@@ -52,57 +52,57 @@ class LookupIpOnList {
|
|
52 |
* @return Databases\IPs\EntryVO|null
|
53 |
*/
|
54 |
public function lookupIp() {
|
55 |
-
/** @var Databases\IPs\Select $
|
56 |
-
$
|
57 |
|
58 |
if ( $this->getListType() == 'white' ) {
|
59 |
-
$
|
60 |
}
|
61 |
elseif ( $this->getListType() == 'black' ) {
|
62 |
-
$
|
63 |
if ( !is_null( $this->isIpBlocked() ) ) {
|
64 |
-
$
|
65 |
}
|
66 |
}
|
67 |
|
68 |
-
return $
|
69 |
-
|
70 |
-
|
71 |
}
|
72 |
|
73 |
/**
|
74 |
* @return Databases\IPs\EntryVO[]
|
75 |
*/
|
76 |
public function lookupRange() {
|
77 |
-
/** @var Databases\IPs\Select $
|
78 |
-
$
|
79 |
|
80 |
if ( $this->getListType() == 'white' ) {
|
81 |
-
$
|
82 |
}
|
83 |
elseif ( $this->getListType() == 'black' ) {
|
84 |
-
$
|
85 |
if ( !is_null( $this->isIpBlocked() ) ) {
|
86 |
-
$
|
87 |
}
|
88 |
}
|
89 |
|
90 |
-
$
|
91 |
-
return is_array( $
|
92 |
}
|
93 |
|
94 |
/**
|
95 |
* @return string
|
96 |
*/
|
97 |
public function getListType() {
|
98 |
-
return $this->
|
99 |
}
|
100 |
|
101 |
/**
|
102 |
* @return bool|null
|
103 |
*/
|
104 |
public function isIpBlocked() {
|
105 |
-
return $this->
|
106 |
}
|
107 |
|
108 |
/**
|
@@ -110,7 +110,7 @@ class LookupIpOnList {
|
|
110 |
* @return $this
|
111 |
*/
|
112 |
public function setIsIpBlocked( $bIsBlocked ) {
|
113 |
-
$this->
|
114 |
return $this;
|
115 |
}
|
116 |
|
@@ -118,7 +118,7 @@ class LookupIpOnList {
|
|
118 |
* @return $this
|
119 |
*/
|
120 |
public function setListTypeBlack() {
|
121 |
-
$this->
|
122 |
return $this;
|
123 |
}
|
124 |
|
@@ -126,7 +126,7 @@ class LookupIpOnList {
|
|
126 |
* @return $this
|
127 |
*/
|
128 |
public function setListTypeWhite() {
|
129 |
-
$this->
|
130 |
return $this;
|
131 |
}
|
132 |
}
|
14 |
/**
|
15 |
* @var string
|
16 |
*/
|
17 |
+
private $listType;
|
18 |
|
19 |
/**
|
20 |
* @var bool
|
21 |
*/
|
22 |
+
private $isBlocked;
|
23 |
|
24 |
/**
|
25 |
+
* @param bool $includeRanges
|
26 |
* @return Databases\IPs\EntryVO|null
|
27 |
* @version 8.6.0 - switched to lookup ranges first
|
28 |
*/
|
29 |
+
public function lookup( $includeRanges = true ) {
|
30 |
$IP = null;
|
31 |
if ( !empty( $this->getIP() ) ) {
|
32 |
+
if ( $includeRanges ) {
|
33 |
+
foreach ( $this->lookupRange() as $maybe ) {
|
34 |
try {
|
35 |
+
if ( Services::IP()->checkIp( $this->getIP(), $maybe->ip ) ) {
|
36 |
+
$IP = $maybe;
|
37 |
break;
|
38 |
}
|
39 |
}
|
52 |
* @return Databases\IPs\EntryVO|null
|
53 |
*/
|
54 |
public function lookupIp() {
|
55 |
+
/** @var Databases\IPs\Select $select */
|
56 |
+
$select = $this->getDbHandler()->getQuerySelector();
|
57 |
|
58 |
if ( $this->getListType() == 'white' ) {
|
59 |
+
$select->filterByWhitelist();
|
60 |
}
|
61 |
elseif ( $this->getListType() == 'black' ) {
|
62 |
+
$select->filterByBlacklist();
|
63 |
if ( !is_null( $this->isIpBlocked() ) ) {
|
64 |
+
$select->filterByBlocked( $this->isIpBlocked() );
|
65 |
}
|
66 |
}
|
67 |
|
68 |
+
return $select->filterByIsRange( false )
|
69 |
+
->filterByIp( $this->getIP() )
|
70 |
+
->first();
|
71 |
}
|
72 |
|
73 |
/**
|
74 |
* @return Databases\IPs\EntryVO[]
|
75 |
*/
|
76 |
public function lookupRange() {
|
77 |
+
/** @var Databases\IPs\Select $select */
|
78 |
+
$select = $this->getDbHandler()->getQuerySelector();
|
79 |
|
80 |
if ( $this->getListType() == 'white' ) {
|
81 |
+
$select->filterByWhitelist();
|
82 |
}
|
83 |
elseif ( $this->getListType() == 'black' ) {
|
84 |
+
$select->filterByBlacklist();
|
85 |
if ( !is_null( $this->isIpBlocked() ) ) {
|
86 |
+
$select->filterByBlocked( $this->isIpBlocked() );
|
87 |
}
|
88 |
}
|
89 |
|
90 |
+
$IPs = $select->filterByIsRange( true )->query();
|
91 |
+
return is_array( $IPs ) ? $IPs : [];
|
92 |
}
|
93 |
|
94 |
/**
|
95 |
* @return string
|
96 |
*/
|
97 |
public function getListType() {
|
98 |
+
return $this->listType;
|
99 |
}
|
100 |
|
101 |
/**
|
102 |
* @return bool|null
|
103 |
*/
|
104 |
public function isIpBlocked() {
|
105 |
+
return $this->isBlocked;
|
106 |
}
|
107 |
|
108 |
/**
|
110 |
* @return $this
|
111 |
*/
|
112 |
public function setIsIpBlocked( $bIsBlocked ) {
|
113 |
+
$this->isBlocked = $bIsBlocked;
|
114 |
return $this;
|
115 |
}
|
116 |
|
118 |
* @return $this
|
119 |
*/
|
120 |
public function setListTypeBlack() {
|
121 |
+
$this->listType = 'black';
|
122 |
return $this;
|
123 |
}
|
124 |
|
126 |
* @return $this
|
127 |
*/
|
128 |
public function setListTypeWhite() {
|
129 |
+
$this->listType = 'white';
|
130 |
return $this;
|
131 |
}
|
132 |
}
|
src/lib/src/Modules/IPs/WpCli/Add.php
CHANGED
@@ -47,8 +47,8 @@ class Add extends BaseAddRemove {
|
|
47 |
$oAdder->toManualBlacklist( $label );
|
48 |
}
|
49 |
}
|
50 |
-
catch ( \Exception $
|
51 |
-
WP_CLI::error( $
|
52 |
}
|
53 |
WP_CLI::success( __( 'IP address added successfully.', 'wp-simple-firewall' ) );
|
54 |
}
|
47 |
$oAdder->toManualBlacklist( $label );
|
48 |
}
|
49 |
}
|
50 |
+
catch ( \Exception $e ) {
|
51 |
+
WP_CLI::error( $e->getMessage() );
|
52 |
}
|
53 |
WP_CLI::success( __( 'IP address added successfully.', 'wp-simple-firewall' ) );
|
54 |
}
|
src/lib/src/Modules/Insights/ModCon.php
CHANGED
@@ -2,7 +2,7 @@
|
|
2 |
|
3 |
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Insights;
|
4 |
|
5 |
-
use FernleafSystems\Wordpress\Plugin\Shield\
|
6 |
use FernleafSystems\Wordpress\Plugin\Shield\Modules\BaseShield;
|
7 |
use FernleafSystems\Wordpress\Services\Services;
|
8 |
|
@@ -45,186 +45,97 @@ class ModCon extends BaseShield\ModCon {
|
|
45 |
return $UI->renderPages();
|
46 |
}
|
47 |
|
48 |
-
public function
|
49 |
-
|
50 |
-
|
51 |
-
|
52 |
-
|
53 |
-
|
54 |
-
|
55 |
-
|
56 |
-
|
57 |
-
|
58 |
-
|
59 |
-
|
60 |
-
|
61 |
-
|
62 |
-
|
63 |
-
|
64 |
-
|
65 |
-
|
66 |
-
|
67 |
-
|
68 |
-
|
69 |
-
|
70 |
-
false
|
71 |
-
);
|
72 |
-
wp_enqueue_script( $sUnique );
|
73 |
-
break;
|
74 |
-
|
75 |
-
case 'overview':
|
76 |
-
case 'reports':
|
77 |
-
|
78 |
-
$aDeps = $aStdDepsJs;
|
79 |
-
|
80 |
-
$aJsAssets = [
|
81 |
-
'chartist.min',
|
82 |
-
'chartist-plugin-legend',
|
83 |
-
'charts',
|
84 |
-
'shuffle',
|
85 |
-
'shield-card-shuffle'
|
86 |
-
];
|
87 |
-
if ( $oTourManager->canShow( 'insights_overview' ) ) {
|
88 |
-
array_unshift( $aJsAssets, 'introjs.min.js' );
|
89 |
-
}
|
90 |
-
foreach ( $aJsAssets as $sAsset ) {
|
91 |
-
$sUnique = $con->prefix( $sAsset );
|
92 |
-
wp_register_script(
|
93 |
-
$sUnique,
|
94 |
-
$con->getPluginUrl_Js( $sAsset ),
|
95 |
-
$aDeps,
|
96 |
-
$con->getVersion(),
|
97 |
-
false
|
98 |
-
);
|
99 |
-
wp_enqueue_script( $sUnique );
|
100 |
-
$aDeps[] = $sUnique;
|
101 |
-
}
|
102 |
-
|
103 |
-
$aDeps = [];
|
104 |
-
$aCssAssets = [ 'chartist.min', 'chartist-plugin-legend' ];
|
105 |
-
if ( $oTourManager->canShow( 'insights_overview' ) ) {
|
106 |
-
array_unshift( $aCssAssets, 'introjs.min.css' );
|
107 |
-
}
|
108 |
-
foreach ( $aCssAssets as $sAsset ) {
|
109 |
-
$sUnique = $con->prefix( $sAsset );
|
110 |
-
wp_register_style(
|
111 |
-
$sUnique,
|
112 |
-
$con->getPluginUrl_Css( $sAsset ),
|
113 |
-
$aDeps,
|
114 |
-
$con->getVersion(),
|
115 |
-
false
|
116 |
-
);
|
117 |
-
wp_enqueue_style( $sUnique );
|
118 |
-
$aDeps[] = $sUnique;
|
119 |
-
}
|
120 |
-
|
121 |
-
$this->includeScriptIpDetect();
|
122 |
-
break;
|
123 |
-
|
124 |
-
case 'notes':
|
125 |
-
case 'scans':
|
126 |
-
case 'audit':
|
127 |
-
case 'traffic':
|
128 |
-
case 'ips':
|
129 |
-
case 'debug':
|
130 |
-
case 'users':
|
131 |
-
|
132 |
-
$sAsset = 'shield-tables';
|
133 |
-
$sUnique = $con->prefix( $sAsset );
|
134 |
-
wp_register_script(
|
135 |
-
$sUnique,
|
136 |
-
$con->getPluginUrl_Js( $sAsset ),
|
137 |
-
$aStdDepsJs,
|
138 |
-
$con->getVersion(),
|
139 |
-
false
|
140 |
-
);
|
141 |
-
wp_enqueue_script( $sUnique );
|
142 |
-
|
143 |
-
$aStdDepsJs[] = $sUnique;
|
144 |
-
if ( $iNav == 'scans' ) {
|
145 |
-
$sAsset = 'shield-scans';
|
146 |
-
$sUnique = $con->prefix( $sAsset );
|
147 |
-
wp_register_script(
|
148 |
-
$sUnique,
|
149 |
-
$con->getPluginUrl_Js( $sAsset ),
|
150 |
-
array_unique( $aStdDepsJs ),
|
151 |
-
$con->getVersion(),
|
152 |
-
false
|
153 |
-
);
|
154 |
-
wp_enqueue_script( $sUnique );
|
155 |
-
}
|
156 |
-
|
157 |
-
if ( $iNav == 'ips' ) {
|
158 |
-
$sAsset = 'shield/ipanalyse';
|
159 |
-
$sUnique = $con->prefix( $sAsset );
|
160 |
-
wp_register_script(
|
161 |
-
$sUnique,
|
162 |
-
$con->getPluginUrl_Js( $sAsset ),
|
163 |
-
array_unique( $aStdDepsJs ),
|
164 |
-
$con->getVersion(),
|
165 |
-
false
|
166 |
-
);
|
167 |
-
wp_enqueue_script( $sUnique );
|
168 |
-
}
|
169 |
-
|
170 |
-
if ( in_array( $iNav, [ 'audit', 'traffic' ] ) ) {
|
171 |
-
$sUnique = $con->prefix( 'datepicker' );
|
172 |
-
wp_register_script(
|
173 |
-
$sUnique, //TODO: use an includes services for CNDJS
|
174 |
-
'https://cdnjs.cloudflare.com/ajax/libs/bootstrap-datepicker/1.8.0/js/bootstrap-datepicker.min.js',
|
175 |
-
array_unique( $aStdDepsJs ),
|
176 |
-
$con->getVersion(),
|
177 |
-
false
|
178 |
-
);
|
179 |
-
wp_enqueue_script( $sUnique );
|
180 |
-
|
181 |
-
wp_register_style(
|
182 |
-
$sUnique,
|
183 |
-
'https://cdnjs.cloudflare.com/ajax/libs/bootstrap-datepicker/1.8.0/css/bootstrap-datepicker.min.css',
|
184 |
-
[],
|
185 |
-
$con->getVersion(),
|
186 |
-
false
|
187 |
-
);
|
188 |
-
wp_enqueue_style( $sUnique );
|
189 |
-
}
|
190 |
-
|
191 |
-
break;
|
192 |
-
}
|
193 |
-
|
194 |
-
wp_localize_script(
|
195 |
-
$con->prefix( 'plugin' ),
|
196 |
-
'icwp_wpsf_vars_insights',
|
197 |
-
[
|
198 |
-
'strings' => [
|
199 |
-
'downloading_file' => __( 'Downloading file, please wait...', 'wp-simple-firewall' ),
|
200 |
-
'downloading_file_problem' => __( 'There was a problem downloading the file.', 'wp-simple-firewall' ),
|
201 |
-
'select_action' => __( 'Please select an action to perform.', 'wp-simple-firewall' ),
|
202 |
-
'are_you_sure' => __( 'Are you sure?', 'wp-simple-firewall' ),
|
203 |
-
],
|
204 |
-
]
|
205 |
-
);
|
206 |
-
}
|
207 |
}
|
208 |
|
209 |
-
|
|
|
|
|
|
|
|
|
|
|
210 |
$con = $this->getCon();
|
211 |
-
|
212 |
-
$
|
213 |
-
|
214 |
-
|
215 |
-
|
216 |
-
|
217 |
-
[]
|
218 |
-
|
219 |
-
|
220 |
-
|
221 |
-
|
222 |
-
|
223 |
-
|
224 |
-
|
225 |
-
|
226 |
-
|
227 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
228 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
229 |
}
|
230 |
}
|
2 |
|
3 |
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Insights;
|
4 |
|
5 |
+
use FernleafSystems\Wordpress\Plugin\Shield\Controller\Assets\Enqueue;
|
6 |
use FernleafSystems\Wordpress\Plugin\Shield\Modules\BaseShield;
|
7 |
use FernleafSystems\Wordpress\Services\Services;
|
8 |
|
45 |
return $UI->renderPages();
|
46 |
}
|
47 |
|
48 |
+
public function getScriptLocalisations() :array {
|
49 |
+
$con = $this->getCon();
|
50 |
+
$locals = parent::getScriptLocalisations();
|
51 |
+
$locals[] = [
|
52 |
+
'plugin',
|
53 |
+
'icwp_wpsf_vars_insights',
|
54 |
+
[
|
55 |
+
'strings' => [
|
56 |
+
'downloading_file' => __( 'Downloading file, please wait...', 'wp-simple-firewall' ),
|
57 |
+
'downloading_file_problem' => __( 'There was a problem downloading the file.', 'wp-simple-firewall' ),
|
58 |
+
'select_action' => __( 'Please select an action to perform.', 'wp-simple-firewall' ),
|
59 |
+
'are_you_sure' => __( 'Are you sure?', 'wp-simple-firewall' ),
|
60 |
+
],
|
61 |
+
]
|
62 |
+
];
|
63 |
+
$locals[] = [
|
64 |
+
$con->prefix( 'ip_detect' ),
|
65 |
+
'icwp_wpsf_vars_ipdetect',
|
66 |
+
[ 'ajax' => $con->getModule_Plugin()->getAjaxActionData( 'ipdetect' ) ]
|
67 |
+
];
|
68 |
+
|
69 |
+
return $locals;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
70 |
}
|
71 |
|
72 |
+
public function getCustomScriptEnqueues() :array {
|
73 |
+
$enq = [
|
74 |
+
Enqueue::CSS => [],
|
75 |
+
Enqueue::JS => [],
|
76 |
+
];
|
77 |
+
|
78 |
$con = $this->getCon();
|
79 |
+
$iNav = Services::Request()->query( 'inav', 'overview' );
|
80 |
+
$oTourManager = $con->getModule_Plugin()->getTourManager();
|
81 |
+
|
82 |
+
switch ( $iNav ) {
|
83 |
+
|
84 |
+
case 'importexport':
|
85 |
+
$enq[ Enqueue::JS ][] = 'shield/import';
|
86 |
+
break;
|
87 |
+
|
88 |
+
case 'overview':
|
89 |
+
case 'reports':
|
90 |
+
|
91 |
+
$enq[ Enqueue::JS ] = [
|
92 |
+
'chartist.min',
|
93 |
+
'chartist-plugin-legend',
|
94 |
+
'charts',
|
95 |
+
'shuffle',
|
96 |
+
'shield-card-shuffle',
|
97 |
+
'ip_detect'
|
98 |
+
];
|
99 |
+
$enq[ Enqueue::CSS ] = [
|
100 |
+
'chartist.min',
|
101 |
+
'chartist-plugin-legend'
|
102 |
+
];
|
103 |
+
|
104 |
+
if ( $oTourManager->canShow( 'insights_overview' ) ) {
|
105 |
+
$enq[ Enqueue::JS ][] = 'introjs.min';
|
106 |
+
$enq[ Enqueue::CSS ][] = 'introjs.min';
|
107 |
+
}
|
108 |
+
break;
|
109 |
+
|
110 |
+
case 'notes':
|
111 |
+
case 'scans':
|
112 |
+
case 'audit':
|
113 |
+
case 'traffic':
|
114 |
+
case 'ips':
|
115 |
+
case 'debug':
|
116 |
+
case 'users':
|
117 |
+
|
118 |
+
$enq[ Enqueue::JS ][] = 'shield-tables';
|
119 |
+
if ( $iNav == 'scans' ) {
|
120 |
+
$enq[ Enqueue::JS ][] = 'shield-scans';
|
121 |
+
}
|
122 |
+
elseif ( $iNav == 'ips' ) {
|
123 |
+
$enq[ Enqueue::JS ][] = 'shield/ipanalyse';
|
124 |
+
}
|
125 |
+
|
126 |
+
if ( in_array( $iNav, [ 'audit', 'traffic' ] ) ) {
|
127 |
+
$enq[ Enqueue::JS ][] = 'bootstrap-datepicker';
|
128 |
+
$enq[ Enqueue::CSS ][] = 'bootstrap-datepicker';
|
129 |
+
}
|
130 |
+
break;
|
131 |
}
|
132 |
+
|
133 |
+
return $enq;
|
134 |
+
}
|
135 |
+
|
136 |
+
/**
|
137 |
+
* @deprecated 10.2
|
138 |
+
*/
|
139 |
+
private function includeScriptIpDetect() {
|
140 |
}
|
141 |
}
|
src/lib/src/Modules/Integrations/Lib/MainWP/Client/Actions/Sync.php
CHANGED
@@ -33,7 +33,7 @@ class Sync {
|
|
33 |
'installed_at' => $con->getModule_Plugin()->getInstallDate(),
|
34 |
'sync_at' => Services::Request()->ts(),
|
35 |
'version' => $con->getVersion(),
|
36 |
-
'has_update' => Services::WpPlugins()->isUpdateAvailable( $con->
|
37 |
];
|
38 |
}
|
39 |
|
33 |
'installed_at' => $con->getModule_Plugin()->getInstallDate(),
|
34 |
'sync_at' => Services::Request()->ts(),
|
35 |
'version' => $con->getVersion(),
|
36 |
+
'has_update' => Services::WpPlugins()->isUpdateAvailable( $con->base_file ),
|
37 |
];
|
38 |
}
|
39 |
|
src/lib/src/Modules/Integrations/Lib/MainWP/Server/Data/ClientPluginStatus.php
CHANGED
@@ -71,7 +71,7 @@ class ClientPluginStatus {
|
|
71 |
public function getInstalledPlugin() {
|
72 |
$thePlugin = null;
|
73 |
|
74 |
-
$baseName = basename( $this->getCon()->
|
75 |
foreach ( $this->getMwpSite()->plugins as $plugin ) {
|
76 |
if ( basename( $plugin[ 'slug' ] ) === $baseName ) {
|
77 |
$thePlugin = $plugin;
|
71 |
public function getInstalledPlugin() {
|
72 |
$thePlugin = null;
|
73 |
|
74 |
+
$baseName = basename( $this->getCon()->base_file );
|
75 |
foreach ( $this->getMwpSite()->plugins as $plugin ) {
|
76 |
if ( basename( $plugin[ 'slug' ] ) === $baseName ) {
|
77 |
$thePlugin = $plugin;
|
src/lib/src/Modules/Integrations/Lib/MainWP/Server/Init.php
CHANGED
@@ -35,7 +35,7 @@ class Init {
|
|
35 |
'plugin' => $this->getCon()->getRootFile(),
|
36 |
// while this is a "callback" field, a Closure isn't supported as it's serialized for DB storage. Sigh.
|
37 |
'callback' => [ $extensionsPage, 'render' ],
|
38 |
-
'icon' => $con->
|
39 |
];
|
40 |
return $exts;
|
41 |
}, 10, 1 );
|
35 |
'plugin' => $this->getCon()->getRootFile(),
|
36 |
// while this is a "callback" field, a Closure isn't supported as it's serialized for DB storage. Sigh.
|
37 |
'callback' => [ $extensionsPage, 'render' ],
|
38 |
+
'icon' => $con->urls->forImage( 'pluginlogo_col_32x32.png' ),
|
39 |
];
|
40 |
return $exts;
|
41 |
}, 10, 1 );
|
src/lib/src/Modules/Integrations/Lib/MainWP/Server/UI/ExtensionSettingsPage.php
CHANGED
@@ -3,6 +3,7 @@
|
|
3 |
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Integrations\Lib\MainWP\Server\UI;
|
4 |
|
5 |
use FernleafSystems\Utilities\Logic\OneTimeExecute;
|
|
|
6 |
use FernleafSystems\Wordpress\Plugin\Shield\Modules\Integrations\Lib\MainWP\Controller;
|
7 |
use FernleafSystems\Wordpress\Plugin\Shield\Modules\Integrations\Lib\MainWP\Server\UI\PageRender;
|
8 |
use FernleafSystems\Wordpress\Plugin\Shield\Modules\ModConsumer;
|
@@ -14,26 +15,13 @@ class ExtensionSettingsPage {
|
|
14 |
use OneTimeExecute;
|
15 |
|
16 |
protected function run() {
|
17 |
-
add_action( 'admin_enqueue_scripts', function ( $hook ) {
|
18 |
-
$con = $this->getCon();
|
19 |
-
if ( 'mainwp_page_'.$con->mwpVO->extension->page === $hook ) {
|
20 |
-
$handle = $con->prefix( 'mainwp-extension' );
|
21 |
-
wp_register_script(
|
22 |
-
$handle,
|
23 |
-
$con->getPluginUrl_Js( 'shield/mainwp-extension.js' ),
|
24 |
-
[ 'jquery' ],
|
25 |
-
$con->getVersion(),
|
26 |
-
true
|
27 |
-
);
|
28 |
-
wp_enqueue_script( $handle );
|
29 |
|
30 |
-
|
31 |
-
|
32 |
-
|
33 |
-
|
34 |
-
|
35 |
-
|
36 |
-
wp_enqueue_style( $handle );
|
37 |
|
38 |
// $handle = 'semantic-ui-datatables-select';
|
39 |
// wp_register_script(
|
@@ -52,7 +40,8 @@ class ExtensionSettingsPage {
|
|
52 |
// );
|
53 |
// wp_enqueue_style( 'semantic-ui-datatables-select' );
|
54 |
}
|
55 |
-
|
|
|
56 |
}
|
57 |
|
58 |
/**
|
@@ -124,8 +113,6 @@ class ExtensionSettingsPage {
|
|
124 |
}
|
125 |
|
126 |
private function serverPluginNeedsUpdate() :bool {
|
127 |
-
return Services::WpPlugins()->isUpdateAvailable(
|
128 |
-
$this->getCon()->getPluginBaseFile()
|
129 |
-
);
|
130 |
}
|
131 |
}
|
3 |
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Integrations\Lib\MainWP\Server\UI;
|
4 |
|
5 |
use FernleafSystems\Utilities\Logic\OneTimeExecute;
|
6 |
+
use FernleafSystems\Wordpress\Plugin\Shield\Controller\Assets\Enqueue;
|
7 |
use FernleafSystems\Wordpress\Plugin\Shield\Modules\Integrations\Lib\MainWP\Controller;
|
8 |
use FernleafSystems\Wordpress\Plugin\Shield\Modules\Integrations\Lib\MainWP\Server\UI\PageRender;
|
9 |
use FernleafSystems\Wordpress\Plugin\Shield\Modules\ModConsumer;
|
15 |
use OneTimeExecute;
|
16 |
|
17 |
protected function run() {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
18 |
|
19 |
+
add_filter( 'shield/custom_enqueues', function ( array $enqueues, $hook ) {
|
20 |
+
|
21 |
+
if ( 'mainwp_page_'.$this->getCon()->mwpVO->extension->page === $hook ) {
|
22 |
+
|
23 |
+
$enqueues[ Enqueue::JS ][] = 'shield/mainwp-extension';
|
24 |
+
$enqueues[ Enqueue::CSS ][] = 'mainwp-extension';
|
|
|
25 |
|
26 |
// $handle = 'semantic-ui-datatables-select';
|
27 |
// wp_register_script(
|
40 |
// );
|
41 |
// wp_enqueue_style( 'semantic-ui-datatables-select' );
|
42 |
}
|
43 |
+
return $enqueues;
|
44 |
+
}, 10,2 );
|
45 |
}
|
46 |
|
47 |
/**
|
113 |
}
|
114 |
|
115 |
private function serverPluginNeedsUpdate() :bool {
|
116 |
+
return Services::WpPlugins()->isUpdateAvailable( $this->getCon()->base_file );
|
|
|
|
|
117 |
}
|
118 |
}
|
src/lib/src/Modules/License/AjaxHandler.php
CHANGED
@@ -60,43 +60,43 @@ class AjaxHandler extends Shield\Modules\BaseShield\AjaxHandler {
|
|
60 |
$mod = $this->getMod();
|
61 |
$sHandler = $mod->getLicenseHandler();
|
62 |
|
63 |
-
$
|
64 |
-
$
|
65 |
|
66 |
$sLicenseAction = Services::Request()->post( 'license-action' );
|
67 |
|
68 |
if ( $sLicenseAction == 'clear' ) {
|
69 |
-
$
|
70 |
$sHandler->deactivate( false );
|
71 |
$sHandler->clearLicense();
|
72 |
-
$
|
73 |
-
|
74 |
}
|
75 |
elseif ( $sLicenseAction == 'check' ) {
|
76 |
|
77 |
$nCheckInterval = $sHandler->getLicenseNotCheckedForInterval();
|
78 |
if ( $nCheckInterval < 20 ) {
|
79 |
$nWait = 20 - $nCheckInterval;
|
80 |
-
$
|
81 |
__( 'Please wait %s before attempting another license check.', 'wp-simple-firewall' ),
|
82 |
sprintf( _n( '%s second', '%s seconds', $nWait, 'wp-simple-firewall' ), $nWait )
|
83 |
);
|
84 |
}
|
85 |
else {
|
86 |
try {
|
87 |
-
$
|
88 |
-
|
89 |
-
$
|
90 |
}
|
91 |
-
catch ( \Exception $
|
92 |
-
$
|
93 |
}
|
94 |
}
|
95 |
}
|
96 |
|
97 |
return [
|
98 |
-
'success' => $
|
99 |
-
'message' => $
|
100 |
];
|
101 |
}
|
102 |
}
|
60 |
$mod = $this->getMod();
|
61 |
$sHandler = $mod->getLicenseHandler();
|
62 |
|
63 |
+
$success = false;
|
64 |
+
$msg = 'Unsupported license action';
|
65 |
|
66 |
$sLicenseAction = Services::Request()->post( 'license-action' );
|
67 |
|
68 |
if ( $sLicenseAction == 'clear' ) {
|
69 |
+
$success = true;
|
70 |
$sHandler->deactivate( false );
|
71 |
$sHandler->clearLicense();
|
72 |
+
$msg = __( 'Success', 'wp-simple-firewall' ).'! '
|
73 |
+
.__( 'Reloading page', 'wp-simple-firewall' ).'...';
|
74 |
}
|
75 |
elseif ( $sLicenseAction == 'check' ) {
|
76 |
|
77 |
$nCheckInterval = $sHandler->getLicenseNotCheckedForInterval();
|
78 |
if ( $nCheckInterval < 20 ) {
|
79 |
$nWait = 20 - $nCheckInterval;
|
80 |
+
$msg = sprintf(
|
81 |
__( 'Please wait %s before attempting another license check.', 'wp-simple-firewall' ),
|
82 |
sprintf( _n( '%s second', '%s seconds', $nWait, 'wp-simple-firewall' ), $nWait )
|
83 |
);
|
84 |
}
|
85 |
else {
|
86 |
try {
|
87 |
+
$success = $sHandler->verify( true )
|
88 |
+
->hasValidWorkingLicense();
|
89 |
+
$msg = $success ? __( 'Valid license found.', 'wp-simple-firewall' ) : __( "Valid license couldn't be found.", 'wp-simple-firewall' );
|
90 |
}
|
91 |
+
catch ( \Exception $e ) {
|
92 |
+
$msg = $e->getMessage();
|
93 |
}
|
94 |
}
|
95 |
}
|
96 |
|
97 |
return [
|
98 |
+
'success' => $success,
|
99 |
+
'message' => $msg,
|
100 |
];
|
101 |
}
|
102 |
}
|
src/lib/src/Modules/License/Lib/LicenseHandler.php
CHANGED
@@ -47,7 +47,7 @@ class LicenseHandler {
|
|
47 |
try {
|
48 |
$mod->getLicenseHandler()->verify( true );
|
49 |
}
|
50 |
-
catch ( \Exception $
|
51 |
}
|
52 |
} );
|
53 |
}
|
47 |
try {
|
48 |
$mod->getLicenseHandler()->verify( true );
|
49 |
}
|
50 |
+
catch ( \Exception $e ) {
|
51 |
}
|
52 |
} );
|
53 |
}
|
src/lib/src/Modules/License/Lib/PluginNameSuffix.php
ADDED
@@ -0,0 +1,29 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\License\Lib;
|
4 |
+
|
5 |
+
use FernleafSystems\Utilities\Logic\OneTimeExecute;
|
6 |
+
use FernleafSystems\Wordpress\Plugin\Shield\Modules;
|
7 |
+
use FernleafSystems\Wordpress\Plugin\Shield\Modules\SecurityAdmin;
|
8 |
+
|
9 |
+
class PluginNameSuffix {
|
10 |
+
|
11 |
+
use Modules\ModConsumer;
|
12 |
+
use OneTimeExecute;
|
13 |
+
|
14 |
+
protected function canRun() {
|
15 |
+
$con = $this->getCon();
|
16 |
+
/** @var SecurityAdmin\Options $optsSecAdmin */
|
17 |
+
$optsSecAdmin = $con->getModule_SecAdmin()->getOptions();
|
18 |
+
return (bool)apply_filters( 'shield/add_pro_suffix', $con->isPremiumActive() && !$optsSecAdmin->isEnabledWhitelabel() );
|
19 |
+
}
|
20 |
+
|
21 |
+
protected function run() {
|
22 |
+
add_filter( $this->getCon()->prefix( 'plugin_labels' ), function ( $labels ) {
|
23 |
+
$labels[ 'Name' ] = 'ShieldPRO';
|
24 |
+
$labels[ 'Title' ] = 'ShieldPRO';
|
25 |
+
$labels[ 'MenuTitle' ] = 'ShieldPRO';
|
26 |
+
return $labels;
|
27 |
+
} );
|
28 |
+
}
|
29 |
+
}
|
src/lib/src/Modules/License/Lib/WpHashes/ApiTokenManager.php
CHANGED
@@ -51,7 +51,7 @@ class ApiTokenManager {
|
|
51 |
try {
|
52 |
$aT = array_merge( $aT, $this->solicitApiToken() );
|
53 |
}
|
54 |
-
catch ( \Exception $
|
55 |
}
|
56 |
$aT[ 'attempt_at' ] = Services::Request()->ts();
|
57 |
$aT[ 'next_attempt_from' ] = Services::Request()->ts() + HOUR_IN_SECONDS;
|
51 |
try {
|
52 |
$aT = array_merge( $aT, $this->solicitApiToken() );
|
53 |
}
|
54 |
+
catch ( \Exception $e ) {
|
55 |
}
|
56 |
$aT[ 'attempt_at' ] = Services::Request()->ts();
|
57 |
$aT[ 'next_attempt_from' ] = Services::Request()->ts() + HOUR_IN_SECONDS;
|
src/lib/src/Modules/License/Processor.php
CHANGED
@@ -11,4 +11,10 @@ class Processor extends BaseShield\Processor {
|
|
11 |
$mod = $this->getMod();
|
12 |
$mod->getLicenseHandler()->execute();
|
13 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
14 |
}
|
11 |
$mod = $this->getMod();
|
12 |
$mod->getLicenseHandler()->execute();
|
13 |
}
|
14 |
+
|
15 |
+
public function onWpLoaded() {
|
16 |
+
( new Lib\PluginNameSuffix() )
|
17 |
+
->setMod( $this->getMod() )
|
18 |
+
->execute();
|
19 |
+
}
|
20 |
}
|
src/lib/src/Modules/License/WpCli/License.php
CHANGED
@@ -104,9 +104,9 @@ class License extends Base\WpCli\BaseWpCliCmd {
|
|
104 |
->hasValidWorkingLicense();
|
105 |
$sMessage = $bSuccess ? __( 'Valid license found and installed.', 'wp-simple-firewall' ) : __( "Valid license couldn't be found.", 'wp-simple-firewall' );
|
106 |
}
|
107 |
-
catch ( \Exception $
|
108 |
$bSuccess = false;
|
109 |
-
$sMessage = $
|
110 |
}
|
111 |
|
112 |
$bSuccess ? WP_CLI::success( $sMessage ) : WP_CLI::error( $sMessage );
|
104 |
->hasValidWorkingLicense();
|
105 |
$sMessage = $bSuccess ? __( 'Valid license found and installed.', 'wp-simple-firewall' ) : __( "Valid license couldn't be found.", 'wp-simple-firewall' );
|
106 |
}
|
107 |
+
catch ( \Exception $e ) {
|
108 |
$bSuccess = false;
|
109 |
+
$sMessage = $e->getMessage();
|
110 |
}
|
111 |
|
112 |
$bSuccess ? WP_CLI::success( $sMessage ) : WP_CLI::error( $sMessage );
|
src/lib/src/Modules/LoginGuard/AjaxHandler.php
CHANGED
@@ -12,34 +12,34 @@ class AjaxHandler extends Shield\Modules\BaseShield\AjaxHandler {
|
|
12 |
|
13 |
switch ( $action ) {
|
14 |
case 'gen_backup_codes':
|
15 |
-
$
|
16 |
break;
|
17 |
|
18 |
case 'del_backup_codes':
|
19 |
-
$
|
20 |
break;
|
21 |
|
22 |
case 'disable_2fa_email':
|
23 |
-
$
|
24 |
break;
|
25 |
|
26 |
case 'resend_verification_email':
|
27 |
-
$
|
28 |
break;
|
29 |
|
30 |
case 'u2f_remove':
|
31 |
-
$
|
32 |
break;
|
33 |
|
34 |
case 'yubikey_remove':
|
35 |
-
$
|
36 |
break;
|
37 |
|
38 |
default:
|
39 |
-
$
|
40 |
}
|
41 |
|
42 |
-
return $
|
43 |
}
|
44 |
|
45 |
protected function ajaxExec_GenBackupCodes() :array {
|
@@ -89,11 +89,13 @@ class AjaxHandler extends Shield\Modules\BaseShield\AjaxHandler {
|
|
89 |
$mod = $this->getMod();
|
90 |
|
91 |
$key = Services::Request()->post( 'u2fid' );
|
92 |
-
(
|
93 |
-
|
94 |
-
|
|
|
|
|
95 |
return [
|
96 |
-
'success' =>
|
97 |
'message' => __( 'Registered U2F device removed from profile.', 'wp-simple-firewall' ),
|
98 |
'page_reload' => true
|
99 |
];
|
12 |
|
13 |
switch ( $action ) {
|
14 |
case 'gen_backup_codes':
|
15 |
+
$response = $this->ajaxExec_GenBackupCodes();
|
16 |
break;
|
17 |
|
18 |
case 'del_backup_codes':
|
19 |
+
$response = $this->ajaxExec_DeleteBackupCodes();
|
20 |
break;
|
21 |
|
22 |
case 'disable_2fa_email':
|
23 |
+
$response = $this->ajaxExec_Disable2faEmail();
|
24 |
break;
|
25 |
|
26 |
case 'resend_verification_email':
|
27 |
+
$response = $this->ajaxExec_ResendEmailVerification();
|
28 |
break;
|
29 |
|
30 |
case 'u2f_remove':
|
31 |
+
$response = $this->ajaxExec_ProfileU2fRemove();
|
32 |
break;
|
33 |
|
34 |
case 'yubikey_remove':
|
35 |
+
$response = $this->ajaxExec_ProfileYubikeyRemove();
|
36 |
break;
|
37 |
|
38 |
default:
|
39 |
+
$response = parent::processAjaxAction( $action );
|
40 |
}
|
41 |
|
42 |
+
return $response;
|
43 |
}
|
44 |
|
45 |
protected function ajaxExec_GenBackupCodes() :array {
|
89 |
$mod = $this->getMod();
|
90 |
|
91 |
$key = Services::Request()->post( 'u2fid' );
|
92 |
+
if ( !empty( $key ) ) {
|
93 |
+
( new TwoFactor\Provider\U2F() )
|
94 |
+
->setMod( $mod )
|
95 |
+
->removeRegisteredU2fId( Services::WpUsers()->getCurrentWpUser(), $key );
|
96 |
+
}
|
97 |
return [
|
98 |
+
'success' => !empty( $key ),
|
99 |
'message' => __( 'Registered U2F device removed from profile.', 'wp-simple-firewall' ),
|
100 |
'page_reload' => true
|
101 |
];
|
src/lib/src/Modules/LoginGuard/Lib/AntiBot/FormProviders/BaseFormProvider.php
CHANGED
@@ -46,21 +46,21 @@ abstract class BaseFormProvider {
|
|
46 |
try {
|
47 |
$this->checkProviders();
|
48 |
}
|
49 |
-
catch ( \Exception $
|
50 |
-
Services::WpGeneral()->wpDie( $
|
51 |
}
|
52 |
}
|
53 |
|
54 |
public function run() {
|
55 |
-
/** @var LoginGuard\Options $
|
56 |
-
$
|
57 |
-
if ( $
|
58 |
$this->login();
|
59 |
}
|
60 |
-
if ( $
|
61 |
$this->register();
|
62 |
}
|
63 |
-
if ( $
|
64 |
$this->lostpassword();
|
65 |
}
|
66 |
}
|
46 |
try {
|
47 |
$this->checkProviders();
|
48 |
}
|
49 |
+
catch ( \Exception $e ) {
|
50 |
+
Services::WpGeneral()->wpDie( $e->getMessage() );
|
51 |
}
|
52 |
}
|
53 |
|
54 |
public function run() {
|
55 |
+
/** @var LoginGuard\Options $opts */
|
56 |
+
$opts = $this->getOptions();
|
57 |
+
if ( $opts->isProtectLogin() ) {
|
58 |
$this->login();
|
59 |
}
|
60 |
+
if ( $opts->isProtectRegister() ) {
|
61 |
$this->register();
|
62 |
}
|
63 |
+
if ( $opts->isProtectLostPassword() ) {
|
64 |
$this->lostpassword();
|
65 |
}
|
66 |
}
|
src/lib/src/Modules/LoginGuard/Lib/AntiBot/FormProviders/EasyDigitalDownloads.php
CHANGED
@@ -18,9 +18,9 @@ class EasyDigitalDownloads extends BaseFormProvider {
|
|
18 |
$this->setActionToAudit( 'edd-register' )
|
19 |
->checkProviders();
|
20 |
}
|
21 |
-
catch ( \Exception $
|
22 |
if ( function_exists( 'edd_set_error' ) ) {
|
23 |
-
edd_set_error( $this->getCon()->prefix( rand() ), $
|
24 |
}
|
25 |
}
|
26 |
}
|
18 |
$this->setActionToAudit( 'edd-register' )
|
19 |
->checkProviders();
|
20 |
}
|
21 |
+
catch ( \Exception $e ) {
|
22 |
if ( function_exists( 'edd_set_error' ) ) {
|
23 |
+
edd_set_error( $this->getCon()->prefix( rand() ), $e->getMessage() );
|
24 |
}
|
25 |
}
|
26 |
}
|
src/lib/src/Modules/LoginGuard/Lib/AntiBot/FormProviders/LearnPress.php
CHANGED
@@ -30,8 +30,8 @@ class LearnPress extends BaseFormProvider {
|
|
30 |
$this->setActionToAudit( 'learnpress-login' )
|
31 |
->checkProviders();
|
32 |
}
|
33 |
-
catch ( \Exception $
|
34 |
-
$sFieldNameOrError = new \WP_Error( 'shield-fail-login', $
|
35 |
}
|
36 |
}
|
37 |
return $sFieldNameOrError;
|
@@ -47,8 +47,8 @@ class LearnPress extends BaseFormProvider {
|
|
47 |
$this->setActionToAudit( 'learnpress-register' )
|
48 |
->checkProviders();
|
49 |
}
|
50 |
-
catch ( \Exception $
|
51 |
-
$sFieldNameOrError = new \WP_Error( 'shield-fail-register', $
|
52 |
}
|
53 |
}
|
54 |
return $sFieldNameOrError;
|
30 |
$this->setActionToAudit( 'learnpress-login' )
|
31 |
->checkProviders();
|
32 |
}
|
33 |
+
catch ( \Exception $e ) {
|
34 |
+
$sFieldNameOrError = new \WP_Error( 'shield-fail-login', $e->getMessage() );
|
35 |
}
|
36 |
}
|
37 |
return $sFieldNameOrError;
|
47 |
$this->setActionToAudit( 'learnpress-register' )
|
48 |
->checkProviders();
|
49 |
}
|
50 |
+
catch ( \Exception $e ) {
|
51 |
+
$sFieldNameOrError = new \WP_Error( 'shield-fail-register', $e->getMessage() );
|
52 |
}
|
53 |
}
|
54 |
return $sFieldNameOrError;
|
src/lib/src/Modules/LoginGuard/Lib/AntiBot/FormProviders/MemberPress.php
CHANGED
@@ -35,8 +35,8 @@ class MemberPress extends BaseFormProvider {
|
|
35 |
$this->setActionToAudit( 'memberpress-login' )
|
36 |
->checkProviders();
|
37 |
}
|
38 |
-
catch ( \Exception $
|
39 |
-
$aErrors[] = $
|
40 |
}
|
41 |
}
|
42 |
return $aErrors;
|
@@ -52,8 +52,8 @@ class MemberPress extends BaseFormProvider {
|
|
52 |
$this->setActionToAudit( 'memberpress-lostpassword' )
|
53 |
->checkProviders();
|
54 |
}
|
55 |
-
catch ( \Exception $
|
56 |
-
$aErrors[] = $
|
57 |
}
|
58 |
}
|
59 |
return $aErrors;
|
@@ -70,8 +70,8 @@ class MemberPress extends BaseFormProvider {
|
|
70 |
$this->setActionToAudit( 'memberpress-register' )
|
71 |
->checkProviders();
|
72 |
}
|
73 |
-
catch ( \Exception $
|
74 |
-
$aErrors[] = $
|
75 |
}
|
76 |
}
|
77 |
return $aErrors;
|
35 |
$this->setActionToAudit( 'memberpress-login' )
|
36 |
->checkProviders();
|
37 |
}
|
38 |
+
catch ( \Exception $e ) {
|
39 |
+
$aErrors[] = $e->getMessage();
|
40 |
}
|
41 |
}
|
42 |
return $aErrors;
|
52 |
$this->setActionToAudit( 'memberpress-lostpassword' )
|
53 |
->checkProviders();
|
54 |
}
|
55 |
+
catch ( \Exception $e ) {
|
56 |
+
$aErrors[] = $e->getMessage();
|
57 |
}
|
58 |
}
|
59 |
return $aErrors;
|
70 |
$this->setActionToAudit( 'memberpress-register' )
|
71 |
->checkProviders();
|
72 |
}
|
73 |
+
catch ( \Exception $e ) {
|
74 |
+
$aErrors[] = $e->getMessage();
|
75 |
}
|
76 |
}
|
77 |
return $aErrors;
|
src/lib/src/Modules/LoginGuard/Lib/AntiBot/FormProviders/PaidMemberSubscriptions.php
CHANGED
@@ -19,8 +19,8 @@ class PaidMemberSubscriptions extends BaseFormProvider {
|
|
19 |
$this->setActionToAudit( 'paidmembersubscriptions-register' )
|
20 |
->checkProviders();
|
21 |
}
|
22 |
-
catch ( \Exception $
|
23 |
-
\pms_errors()->add( 'shield-fail-register', $
|
24 |
}
|
25 |
}
|
26 |
}
|
19 |
$this->setActionToAudit( 'paidmembersubscriptions-register' )
|
20 |
->checkProviders();
|
21 |
}
|
22 |
+
catch ( \Exception $e ) {
|
23 |
+
\pms_errors()->add( 'shield-fail-register', $e->getMessage() );
|
24 |
}
|
25 |
}
|
26 |
}
|
src/lib/src/Modules/LoginGuard/Lib/AntiBot/FormProviders/ProfileBuilder.php
CHANGED
@@ -15,18 +15,18 @@ class ProfileBuilder extends BaseFormProvider {
|
|
15 |
}
|
16 |
|
17 |
/**
|
18 |
-
* @param array $
|
19 |
* @return array
|
20 |
*/
|
21 |
-
public function checkRegister( $
|
22 |
try {
|
23 |
$this->setActionToAudit( 'profilebuilder-register' )
|
24 |
->checkProviders();
|
25 |
}
|
26 |
-
catch ( \Exception $
|
27 |
-
$
|
28 |
'<span class="wppb-form-error">Bot</span>';
|
29 |
}
|
30 |
-
return $
|
31 |
}
|
32 |
}
|
15 |
}
|
16 |
|
17 |
/**
|
18 |
+
* @param array $errors
|
19 |
* @return array
|
20 |
*/
|
21 |
+
public function checkRegister( $errors ) {
|
22 |
try {
|
23 |
$this->setActionToAudit( 'profilebuilder-register' )
|
24 |
->checkProviders();
|
25 |
}
|
26 |
+
catch ( \Exception $e ) {
|
27 |
+
$errors[ 'shield-fail-register' ] =
|
28 |
'<span class="wppb-form-error">Bot</span>';
|
29 |
}
|
30 |
+
return $errors;
|
31 |
}
|
32 |
}
|
src/lib/src/Modules/LoginGuard/Lib/AntiBot/FormProviders/UltimateMember.php
CHANGED
@@ -32,8 +32,8 @@ class UltimateMember extends BaseFormProvider {
|
|
32 |
$this->setActionToAudit( 'ultimatemember-login' )
|
33 |
->checkProviders();
|
34 |
}
|
35 |
-
catch ( \Exception $
|
36 |
-
\UM()->form()->add_error( 'shield-fail-login', $
|
37 |
}
|
38 |
}
|
39 |
|
@@ -42,8 +42,8 @@ class UltimateMember extends BaseFormProvider {
|
|
42 |
$this->setActionToAudit( 'ultimatemember-lostpassword' )
|
43 |
->checkProviders();
|
44 |
}
|
45 |
-
catch ( \Exception $
|
46 |
-
\UM()->form()->add_error( 'shield-fail-lostpassword', $
|
47 |
}
|
48 |
}
|
49 |
|
@@ -52,8 +52,8 @@ class UltimateMember extends BaseFormProvider {
|
|
52 |
$this->setActionToAudit( 'ultimatemember-register' )
|
53 |
->checkProviders();
|
54 |
}
|
55 |
-
catch ( \Exception $
|
56 |
-
\UM()->form()->add_error( 'shield-fail-register', $
|
57 |
}
|
58 |
}
|
59 |
}
|
32 |
$this->setActionToAudit( 'ultimatemember-login' )
|
33 |
->checkProviders();
|
34 |
}
|
35 |
+
catch ( \Exception $e ) {
|
36 |
+
\UM()->form()->add_error( 'shield-fail-login', $e->getMessage() );
|
37 |
}
|
38 |
}
|
39 |
|
42 |
$this->setActionToAudit( 'ultimatemember-lostpassword' )
|
43 |
->checkProviders();
|
44 |
}
|
45 |
+
catch ( \Exception $e ) {
|
46 |
+
\UM()->form()->add_error( 'shield-fail-lostpassword', $e->getMessage() );
|
47 |
}
|
48 |
}
|
49 |
|
52 |
$this->setActionToAudit( 'ultimatemember-register' )
|
53 |
->checkProviders();
|
54 |
}
|
55 |
+
catch ( \Exception $e ) {
|
56 |
+
\UM()->form()->add_error( 'shield-fail-register', $e->getMessage() );
|
57 |
}
|
58 |
}
|
59 |
}
|
src/lib/src/Modules/LoginGuard/Lib/AntiBot/FormProviders/UserRegistration.php
CHANGED
@@ -25,19 +25,19 @@ class UserRegistration extends BaseFormProvider {
|
|
25 |
}
|
26 |
|
27 |
/**
|
28 |
-
* @param array $
|
29 |
* @param array $aFormData
|
30 |
* @param int $nFormID
|
31 |
* @return mixed
|
32 |
*/
|
33 |
-
public function checkRegister( $
|
34 |
try {
|
35 |
$this->setActionToAudit( 'userregistration-register' )
|
36 |
->checkProviders();
|
37 |
}
|
38 |
-
catch ( \Exception $
|
39 |
-
$
|
40 |
}
|
41 |
-
return $
|
42 |
}
|
43 |
}
|
25 |
}
|
26 |
|
27 |
/**
|
28 |
+
* @param array $response
|
29 |
* @param array $aFormData
|
30 |
* @param int $nFormID
|
31 |
* @return mixed
|
32 |
*/
|
33 |
+
public function checkRegister( $response, $aFormData, $nFormID ) {
|
34 |
try {
|
35 |
$this->setActionToAudit( 'userregistration-register' )
|
36 |
->checkProviders();
|
37 |
}
|
38 |
+
catch ( \Exception $e ) {
|
39 |
+
$response[] = $e->getMessage();
|
40 |
}
|
41 |
+
return $response;
|
42 |
}
|
43 |
}
|
src/lib/src/Modules/LoginGuard/Lib/AntiBot/FormProviders/WPMembers.php
CHANGED
@@ -28,7 +28,7 @@ class WPMembers extends BaseFormProvider {
|
|
28 |
$this->setActionToAudit( 'wpmembers-lostpassword' )
|
29 |
->checkProviders();
|
30 |
}
|
31 |
-
catch ( \Exception $
|
32 |
$args[ 'user' ] = null;
|
33 |
$args[ 'email' ] = null;
|
34 |
}
|
@@ -43,9 +43,9 @@ class WPMembers extends BaseFormProvider {
|
|
43 |
$this->setActionToAudit( 'wpmembers-register' )
|
44 |
->checkProviders();
|
45 |
}
|
46 |
-
catch ( \Exception $
|
47 |
global $wpmem_themsg;
|
48 |
-
$wpmem_themsg = $
|
49 |
}
|
50 |
}
|
51 |
}
|
28 |
$this->setActionToAudit( 'wpmembers-lostpassword' )
|
29 |
->checkProviders();
|
30 |
}
|
31 |
+
catch ( \Exception $e ) {
|
32 |
$args[ 'user' ] = null;
|
33 |
$args[ 'email' ] = null;
|
34 |
}
|
43 |
$this->setActionToAudit( 'wpmembers-register' )
|
44 |
->checkProviders();
|
45 |
}
|
46 |
+
catch ( \Exception $e ) {
|
47 |
global $wpmem_themsg;
|
48 |
+
$wpmem_themsg = $e->getMessage();
|
49 |
}
|
50 |
}
|
51 |
}
|
src/lib/src/Modules/LoginGuard/Lib/AntiBot/FormProviders/WooCommerce.php
CHANGED
@@ -98,9 +98,9 @@ class WooCommerce extends BaseFormProvider {
|
|
98 |
->checkProviders();
|
99 |
}
|
100 |
}
|
101 |
-
catch ( \Exception $
|
102 |
$oUserOrError = $this->giveMeWpError( $oUserOrError );
|
103 |
-
$oUserOrError->add( $this->getCon()->prefix( rand() ), $
|
104 |
}
|
105 |
return $oUserOrError;
|
106 |
}
|
@@ -116,9 +116,9 @@ class WooCommerce extends BaseFormProvider {
|
|
116 |
->setActionToAudit( 'woo-register' )
|
117 |
->checkProviders();
|
118 |
}
|
119 |
-
catch ( \Exception $
|
120 |
$oWpError = $this->giveMeWpError( $oWpError );
|
121 |
-
$oWpError->add( $this->getCon()->prefix( rand() ), $
|
122 |
}
|
123 |
return $oWpError;
|
124 |
}
|
@@ -134,9 +134,9 @@ class WooCommerce extends BaseFormProvider {
|
|
134 |
$this->setActionToAudit( 'woo-checkout' )
|
135 |
->checkProviders();
|
136 |
}
|
137 |
-
catch ( \Exception $
|
138 |
$oWpError = $this->giveMeWpError( $oWpError );
|
139 |
-
$oWpError->add( $this->getCon()->prefix( rand() ), $
|
140 |
}
|
141 |
return $oWpError;
|
142 |
}
|
98 |
->checkProviders();
|
99 |
}
|
100 |
}
|
101 |
+
catch ( \Exception $e ) {
|
102 |
$oUserOrError = $this->giveMeWpError( $oUserOrError );
|
103 |
+
$oUserOrError->add( $this->getCon()->prefix( rand() ), $e->getMessage() );
|
104 |
}
|
105 |
return $oUserOrError;
|
106 |
}
|
116 |
->setActionToAudit( 'woo-register' )
|
117 |
->checkProviders();
|
118 |
}
|
119 |
+
catch ( \Exception $e ) {
|
120 |
$oWpError = $this->giveMeWpError( $oWpError );
|
121 |
+
$oWpError->add( $this->getCon()->prefix( rand() ), $e->getMessage() );
|
122 |
}
|
123 |
return $oWpError;
|
124 |
}
|
134 |
$this->setActionToAudit( 'woo-checkout' )
|
135 |
->checkProviders();
|
136 |
}
|
137 |
+
catch ( \Exception $e ) {
|
138 |
$oWpError = $this->giveMeWpError( $oWpError );
|
139 |
+
$oWpError->add( $this->getCon()->prefix( rand() ), $e->getMessage() );
|
140 |
}
|
141 |
return $oWpError;
|
142 |
}
|
src/lib/src/Modules/LoginGuard/Lib/AntiBot/FormProviders/WordPress.php
CHANGED
@@ -40,9 +40,9 @@ class WordPress extends BaseFormProvider {
|
|
40 |
->checkProviders();
|
41 |
}
|
42 |
}
|
43 |
-
catch ( \Exception $
|
44 |
$oUserOrError = $this->giveMeWpError( $oUserOrError );
|
45 |
-
$oUserOrError->add( $this->getCon()->prefix( rand() ), $
|
46 |
}
|
47 |
return $oUserOrError;
|
48 |
}
|
@@ -57,9 +57,9 @@ class WordPress extends BaseFormProvider {
|
|
57 |
->setActionToAudit( 'reset-password' )
|
58 |
->checkProviders();
|
59 |
}
|
60 |
-
catch ( \Exception $
|
61 |
$oWpError = $this->giveMeWpError( $oWpError );
|
62 |
-
$oWpError->add( $this->getCon()->prefix( rand() ), $
|
63 |
}
|
64 |
return $oWpError;
|
65 |
}
|
@@ -75,9 +75,9 @@ class WordPress extends BaseFormProvider {
|
|
75 |
->setActionToAudit( 'register' )
|
76 |
->checkProviders();
|
77 |
}
|
78 |
-
catch ( \Exception $
|
79 |
$oWpError = $this->giveMeWpError( $oWpError );
|
80 |
-
$oWpError->add( $this->getCon()->prefix( rand() ), $
|
81 |
}
|
82 |
return $oWpError;
|
83 |
}
|
40 |
->checkProviders();
|
41 |
}
|
42 |
}
|
43 |
+
catch ( \Exception $e ) {
|
44 |
$oUserOrError = $this->giveMeWpError( $oUserOrError );
|
45 |
+
$oUserOrError->add( $this->getCon()->prefix( rand() ), $e->getMessage() );
|
46 |
}
|
47 |
return $oUserOrError;
|
48 |
}
|
57 |
->setActionToAudit( 'reset-password' )
|
58 |
->checkProviders();
|
59 |
}
|
60 |
+
catch ( \Exception $e ) {
|
61 |
$oWpError = $this->giveMeWpError( $oWpError );
|
62 |
+
$oWpError->add( $this->getCon()->prefix( rand() ), $e->getMessage() );
|
63 |
}
|
64 |
return $oWpError;
|
65 |
}
|
75 |
->setActionToAudit( 'register' )
|
76 |
->checkProviders();
|
77 |
}
|
78 |
+
catch ( \Exception $e ) {
|
79 |
$oWpError = $this->giveMeWpError( $oWpError );
|
80 |
+
$oWpError->add( $this->getCon()->prefix( rand() ), $e->getMessage() );
|
81 |
}
|
82 |
return $oWpError;
|
83 |
}
|
src/lib/src/Modules/LoginGuard/Lib/AntiBot/ProtectionProviders/GaspJs.php
CHANGED
@@ -76,18 +76,18 @@ class GaspJs extends BaseProtectionProvider {
|
|
76 |
/** @var LoginGuard\Options $opts */
|
77 |
$opts = $this->getOptions();
|
78 |
|
79 |
-
$
|
80 |
-
$
|
81 |
wp_register_script(
|
82 |
-
$
|
83 |
-
$con->getPluginUrl_Js( $
|
84 |
[ 'jquery' ],
|
85 |
$con->getVersion()
|
86 |
);
|
87 |
-
wp_enqueue_script( $
|
88 |
|
89 |
wp_localize_script(
|
90 |
-
$
|
91 |
'icwp_wpsf_vars_lpantibot',
|
92 |
[
|
93 |
'form_selectors' => implode( ',', $opts->getAntiBotFormSelectors() ),
|
76 |
/** @var LoginGuard\Options $opts */
|
77 |
$opts = $this->getOptions();
|
78 |
|
79 |
+
$asset = 'shield-antibot';
|
80 |
+
$uniq = $con->prefix( $asset );
|
81 |
wp_register_script(
|
82 |
+
$uniq,
|
83 |
+
$con->getPluginUrl_Js( $asset ),
|
84 |
[ 'jquery' ],
|
85 |
$con->getVersion()
|
86 |
);
|
87 |
+
wp_enqueue_script( $uniq );
|
88 |
|
89 |
wp_localize_script(
|
90 |
+
$uniq,
|
91 |
'icwp_wpsf_vars_lpantibot',
|
92 |
[
|
93 |
'form_selectors' => implode( ',', $opts->getAntiBotFormSelectors() ),
|
src/lib/src/Modules/LoginGuard/Lib/AntiBot/ProtectionProviders/GoogleRecaptcha.php
CHANGED
@@ -26,9 +26,9 @@ class GoogleRecaptcha extends BaseProtectionProvider {
|
|
26 |
->setMod( $this->getMod() )
|
27 |
->test();
|
28 |
}
|
29 |
-
catch ( \Exception $
|
30 |
$this->processFailure();
|
31 |
-
throw $
|
32 |
}
|
33 |
}
|
34 |
}
|
26 |
->setMod( $this->getMod() )
|
27 |
->test();
|
28 |
}
|
29 |
+
catch ( \Exception $e ) {
|
30 |
$this->processFailure();
|
31 |
+
throw $e;
|
32 |
}
|
33 |
}
|
34 |
}
|
src/lib/src/Modules/LoginGuard/Lib/TwoFactor/LoginIntentPage.php
CHANGED
@@ -119,7 +119,7 @@ class LoginIntentPage {
|
|
119 |
$req = Services::Request();
|
120 |
|
121 |
$aLabels = $con->getLabels();
|
122 |
-
$sBannerUrl = empty( $aLabels[ 'url_login2fa_logourl' ] ) ? $con->
|
123 |
$nTimeRemaining = $mod->getSession()->login_intent_expires_at - $req->ts();
|
124 |
$aDisplayData = [
|
125 |
'strings' => [
|
@@ -130,14 +130,14 @@ class LoginIntentPage {
|
|
130 |
'time_remaining' => $nTimeRemaining,
|
131 |
],
|
132 |
'hrefs' => [
|
133 |
-
'css_bootstrap' => $con->
|
134 |
-
'js_bootstrap' => $con->
|
135 |
'shield_logo' => 'https://ps.w.org/wp-simple-firewall/assets/banner-772x250.png',
|
136 |
-
'what_is_this' => 'https://
|
137 |
],
|
138 |
'imgs' => [
|
139 |
'banner' => $sBannerUrl,
|
140 |
-
'favicon' => $con->
|
141 |
],
|
142 |
'flags' => [
|
143 |
'show_branded_links' => !$con->getModule_SecAdmin()->isWlEnabled(), // white label mitigation
|
@@ -154,10 +154,10 @@ class LoginIntentPage {
|
|
154 |
$aDisplayData[ 'head' ] = [
|
155 |
'scripts' => [
|
156 |
[
|
157 |
-
'src' => $con->
|
158 |
],
|
159 |
[
|
160 |
-
'src' => $con->
|
161 |
]
|
162 |
]
|
163 |
];
|
119 |
$req = Services::Request();
|
120 |
|
121 |
$aLabels = $con->getLabels();
|
122 |
+
$sBannerUrl = empty( $aLabels[ 'url_login2fa_logourl' ] ) ? $con->urls->forImage( 'pluginlogo_banner-772x250.png' ) : $aLabels[ 'url_login2fa_logourl' ];
|
123 |
$nTimeRemaining = $mod->getSession()->login_intent_expires_at - $req->ts();
|
124 |
$aDisplayData = [
|
125 |
'strings' => [
|
130 |
'time_remaining' => $nTimeRemaining,
|
131 |
],
|
132 |
'hrefs' => [
|
133 |
+
'css_bootstrap' => $con->urls->forCss( 'bootstrap4.min' ),
|
134 |
+
'js_bootstrap' => $con->urls->forJs( 'bootstrap4.bundle.min' ),
|
135 |
'shield_logo' => 'https://ps.w.org/wp-simple-firewall/assets/banner-772x250.png',
|
136 |
+
'what_is_this' => 'https://support.getshieldsecurity.com/support/solutions/articles/3000064840',
|
137 |
],
|
138 |
'imgs' => [
|
139 |
'banner' => $sBannerUrl,
|
140 |
+
'favicon' => $con->urls->forImage( 'pluginlogo_24x24.png' ),
|
141 |
],
|
142 |
'flags' => [
|
143 |
'show_branded_links' => !$con->getModule_SecAdmin()->isWlEnabled(), // white label mitigation
|
154 |
$aDisplayData[ 'head' ] = [
|
155 |
'scripts' => [
|
156 |
[
|
157 |
+
'src' => $con->urls->forJs( 'u2f-bundle.js' ),
|
158 |
],
|
159 |
[
|
160 |
+
'src' => $con->urls->forJs( 'u2f-frontend.js' ),
|
161 |
]
|
162 |
]
|
163 |
];
|
src/lib/src/Modules/LoginGuard/Lib/TwoFactor/MfaController.php
CHANGED
@@ -261,12 +261,4 @@ class MfaController {
|
|
261 |
private function getLoginIntentRequestFlag() :string {
|
262 |
return $this->getCon()->prefix( 'login-intent-request' );
|
263 |
}
|
264 |
-
|
265 |
-
/**
|
266 |
-
* @return bool
|
267 |
-
* @deprecated 10.1
|
268 |
-
*/
|
269 |
-
private function hasLoginIntent() :bool {
|
270 |
-
return $this->getLoginIntentExpiresAt() > 0;
|
271 |
-
}
|
272 |
}
|
261 |
private function getLoginIntentRequestFlag() :string {
|
262 |
return $this->getCon()->prefix( 'login-intent-request' );
|
263 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
264 |
}
|
src/lib/src/Modules/LoginGuard/Lib/TwoFactor/Provider/Backup.php
CHANGED
@@ -80,12 +80,6 @@ class Backup extends BaseProvider {
|
|
80 |
return $this;
|
81 |
}
|
82 |
|
83 |
-
/**
|
84 |
-
* Backup Code are 1-time only and if you have MFA, then we need to remove all the other tracking factors
|
85 |
-
* @param \WP_User $user
|
86 |
-
* @param string $otp
|
87 |
-
* @return bool
|
88 |
-
*/
|
89 |
protected function processOtp( \WP_User $user, string $otp ) :bool {
|
90 |
return $this->validateBackupCode( $user, $otp );
|
91 |
}
|
80 |
return $this;
|
81 |
}
|
82 |
|
|
|
|
|
|
|
|
|
|
|
|
|
83 |
protected function processOtp( \WP_User $user, string $otp ) :bool {
|
84 |
return $this->validateBackupCode( $user, $otp );
|
85 |
}
|
src/lib/src/Modules/LoginGuard/Lib/TwoFactor/Provider/BaseProvider.php
CHANGED
@@ -66,27 +66,15 @@ abstract class BaseProvider {
|
|
66 |
return $this->isSecretValid( $this->getSecret( $user ) );
|
67 |
}
|
68 |
|
69 |
-
|
70 |
-
* @param \WP_User $user
|
71 |
-
* @return bool
|
72 |
-
*/
|
73 |
-
protected function isEnforced( \WP_User $user ) {
|
74 |
return false;
|
75 |
}
|
76 |
|
77 |
-
|
78 |
-
* @param \WP_User $user
|
79 |
-
* @return bool
|
80 |
-
*/
|
81 |
-
public function isProfileActive( \WP_User $user ) {
|
82 |
return $this->hasValidSecret( $user );
|
83 |
}
|
84 |
|
85 |
-
|
86 |
-
* @param \WP_User $user
|
87 |
-
* @return bool
|
88 |
-
*/
|
89 |
-
public function isProviderAvailableToUser( \WP_User $user ) {
|
90 |
return $this->isProviderEnabled();
|
91 |
}
|
92 |
|
@@ -150,11 +138,6 @@ abstract class BaseProvider {
|
|
150 |
return '';
|
151 |
}
|
152 |
|
153 |
-
/**
|
154 |
-
* @param \WP_User $user
|
155 |
-
* @param string $otp
|
156 |
-
* @return bool
|
157 |
-
*/
|
158 |
abstract protected function processOtp( \WP_User $user, string $otp ) :bool;
|
159 |
|
160 |
/**
|
@@ -194,7 +177,7 @@ abstract class BaseProvider {
|
|
194 |
/**
|
195 |
* @param \WP_User $user
|
196 |
*/
|
197 |
-
protected function processRemovalFromAccount( $user ) {
|
198 |
}
|
199 |
|
200 |
/**
|
66 |
return $this->isSecretValid( $this->getSecret( $user ) );
|
67 |
}
|
68 |
|
69 |
+
protected function isEnforced( \WP_User $user ) :bool {
|
|
|
|
|
|
|
|
|
70 |
return false;
|
71 |
}
|
72 |
|
73 |
+
public function isProfileActive( \WP_User $user ) :bool {
|
|
|
|
|
|
|
|
|
74 |
return $this->hasValidSecret( $user );
|
75 |
}
|
76 |
|
77 |
+
public function isProviderAvailableToUser( \WP_User $user ) :bool {
|
|
|
|
|
|
|
|
|
78 |
return $this->isProviderEnabled();
|
79 |
}
|
80 |
|
138 |
return '';
|
139 |
}
|
140 |
|
|
|
|
|
|
|
|
|
|
|
141 |
abstract protected function processOtp( \WP_User $user, string $otp ) :bool;
|
142 |
|
143 |
/**
|
177 |
/**
|
178 |
* @param \WP_User $user
|
179 |
*/
|
180 |
+
protected function processRemovalFromAccount( \WP_User $user ) {
|
181 |
}
|
182 |
|
183 |
/**
|
src/lib/src/Modules/LoginGuard/Lib/TwoFactor/Provider/Email.php
CHANGED
@@ -45,11 +45,6 @@ class Email extends BaseProvider {
|
|
45 |
return $this;
|
46 |
}
|
47 |
|
48 |
-
/**
|
49 |
-
* @param \WP_User $user
|
50 |
-
* @param string $otp
|
51 |
-
* @return bool
|
52 |
-
*/
|
53 |
protected function processOtp( \WP_User $user, string $otp ) :bool {
|
54 |
$valid = false;
|
55 |
foreach ( $this->getAllCodes( $user ) as $secret => $expiresAt ) {
|
@@ -106,11 +101,7 @@ class Email extends BaseProvider {
|
|
106 |
}
|
107 |
}
|
108 |
|
109 |
-
|
110 |
-
* @param \WP_User $user
|
111 |
-
* @return bool
|
112 |
-
*/
|
113 |
-
public function isProfileActive( \WP_User $user ) {
|
114 |
/** @var LoginGuard\Options $opts */
|
115 |
$opts = $this->getOptions();
|
116 |
return parent::isProfileActive( $user ) &&
|
@@ -118,11 +109,7 @@ class Email extends BaseProvider {
|
|
118 |
( $this->hasValidatedProfile( $user ) && $opts->isEnabledEmailAuthAnyUserSet() ) );
|
119 |
}
|
120 |
|
121 |
-
|
122 |
-
* @param \WP_User $user
|
123 |
-
* @return bool
|
124 |
-
*/
|
125 |
-
protected function isEnforced( \WP_User $user ) {
|
126 |
/** @var LoginGuard\Options $opts */
|
127 |
$opts = $this->getOptions();
|
128 |
return count( array_intersect( $opts->getEmail2FaRoles(), $user->roles ) ) > 0;
|
@@ -229,11 +216,7 @@ class Email extends BaseProvider {
|
|
229 |
return $opts->isEmailAuthenticationActive();
|
230 |
}
|
231 |
|
232 |
-
|
233 |
-
* @param \WP_User $user
|
234 |
-
* @return bool
|
235 |
-
*/
|
236 |
-
public function isProviderAvailableToUser( \WP_User $user ) {
|
237 |
/** @var LoginGuard\Options $opts */
|
238 |
$opts = $this->getOptions();
|
239 |
return parent::isProviderAvailableToUser( $user )
|
45 |
return $this;
|
46 |
}
|
47 |
|
|
|
|
|
|
|
|
|
|
|
48 |
protected function processOtp( \WP_User $user, string $otp ) :bool {
|
49 |
$valid = false;
|
50 |
foreach ( $this->getAllCodes( $user ) as $secret => $expiresAt ) {
|
101 |
}
|
102 |
}
|
103 |
|
104 |
+
public function isProfileActive( \WP_User $user ) :bool {
|
|
|
|
|
|
|
|
|
105 |
/** @var LoginGuard\Options $opts */
|
106 |
$opts = $this->getOptions();
|
107 |
return parent::isProfileActive( $user ) &&
|
109 |
( $this->hasValidatedProfile( $user ) && $opts->isEnabledEmailAuthAnyUserSet() ) );
|
110 |
}
|
111 |
|
112 |
+
protected function isEnforced( \WP_User $user ) :bool {
|
|
|
|
|
|
|
|
|
113 |
/** @var LoginGuard\Options $opts */
|
114 |
$opts = $this->getOptions();
|
115 |
return count( array_intersect( $opts->getEmail2FaRoles(), $user->roles ) ) > 0;
|
216 |
return $opts->isEmailAuthenticationActive();
|
217 |
}
|
218 |
|
219 |
+
public function isProviderAvailableToUser( \WP_User $user ) :bool {
|
|
|
|
|
|
|
|
|
220 |
/** @var LoginGuard\Options $opts */
|
221 |
$opts = $this->getOptions();
|
222 |
return parent::isProviderAvailableToUser( $user )
|
src/lib/src/Modules/LoginGuard/Lib/TwoFactor/Provider/GoogleAuth.php
CHANGED
@@ -15,11 +15,7 @@ class GoogleAuth extends BaseProvider {
|
|
15 |
*/
|
16 |
private $oWorkingSecret;
|
17 |
|
18 |
-
|
19 |
-
* @param \WP_User $user
|
20 |
-
* @return bool
|
21 |
-
*/
|
22 |
-
public function isProfileActive( \WP_User $user ) {
|
23 |
return parent::isProfileActive( $user ) && $this->hasValidatedProfile( $user );
|
24 |
}
|
25 |
|
@@ -99,7 +95,7 @@ class GoogleAuth extends BaseProvider {
|
|
99 |
* @param \WP_User $user
|
100 |
* @return $this
|
101 |
*/
|
102 |
-
protected function processRemovalFromAccount( $user ) {
|
103 |
$this->setProfileValidated( $user, false )
|
104 |
->resetSecret( $user );
|
105 |
return $this;
|
@@ -155,11 +151,6 @@ class GoogleAuth extends BaseProvider {
|
|
155 |
];
|
156 |
}
|
157 |
|
158 |
-
/**
|
159 |
-
* @param \WP_User $user
|
160 |
-
* @param string $otp
|
161 |
-
* @return bool
|
162 |
-
*/
|
163 |
protected function processOtp( \WP_User $user, string $otp ) :bool {
|
164 |
return $this->validateGaCode( $user, $otp );
|
165 |
}
|
@@ -176,9 +167,9 @@ class GoogleAuth extends BaseProvider {
|
|
176 |
$valid = (bool)( new GoogleAuthenticator\GoogleAuthenticator() )
|
177 |
->authenticate( $this->getSecret( $user ), $otp );
|
178 |
}
|
179 |
-
catch ( \Exception $
|
180 |
}
|
181 |
-
catch ( \Psr\Cache\CacheException $
|
182 |
}
|
183 |
}
|
184 |
return $valid;
|
@@ -208,7 +199,7 @@ class GoogleAuth extends BaseProvider {
|
|
208 |
try {
|
209 |
return $this->getGaSecret( $user )->getSecretKey();
|
210 |
}
|
211 |
-
catch ( \InvalidArgumentException $
|
212 |
return '';
|
213 |
}
|
214 |
}
|
15 |
*/
|
16 |
private $oWorkingSecret;
|
17 |
|
18 |
+
public function isProfileActive( \WP_User $user ) :bool {
|
|
|
|
|
|
|
|
|
19 |
return parent::isProfileActive( $user ) && $this->hasValidatedProfile( $user );
|
20 |
}
|
21 |
|
95 |
* @param \WP_User $user
|
96 |
* @return $this
|
97 |
*/
|
98 |
+
protected function processRemovalFromAccount( \WP_User $user ) {
|
99 |
$this->setProfileValidated( $user, false )
|
100 |
->resetSecret( $user );
|
101 |
return $this;
|
151 |
];
|
152 |
}
|
153 |
|
|
|
|
|
|
|
|
|
|
|
154 |
protected function processOtp( \WP_User $user, string $otp ) :bool {
|
155 |
return $this->validateGaCode( $user, $otp );
|
156 |
}
|
167 |
$valid = (bool)( new GoogleAuthenticator\GoogleAuthenticator() )
|
168 |
->authenticate( $this->getSecret( $user ), $otp );
|
169 |
}
|
170 |
+
catch ( \Exception $e ) {
|
171 |
}
|
172 |
+
catch ( \Psr\Cache\CacheException $e ) {
|
173 |
}
|
174 |
}
|
175 |
return $valid;
|
199 |
try {
|
200 |
return $this->getGaSecret( $user )->getSecretKey();
|
201 |
}
|
202 |
+
catch ( \InvalidArgumentException $e ) {
|
203 |
return '';
|
204 |
}
|
205 |
}
|
src/lib/src/Modules/LoginGuard/Lib/TwoFactor/Provider/U2F.php
CHANGED
@@ -2,6 +2,7 @@
|
|
2 |
|
3 |
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\LoginGuard\Lib\TwoFactor\Provider;
|
4 |
|
|
|
5 |
use FernleafSystems\Wordpress\Plugin\Shield\Modules\LoginGuard;
|
6 |
use FernleafSystems\Wordpress\Services\Services;
|
7 |
use u2flib_server\RegisterRequest;
|
@@ -12,64 +13,50 @@ class U2F extends BaseProvider {
|
|
12 |
const SLUG = 'u2f';
|
13 |
const DEFAULT_SECRET = '[]';
|
14 |
|
15 |
-
|
16 |
-
* @param \WP_User $user
|
17 |
-
* @return bool
|
18 |
-
*/
|
19 |
-
public function isProfileActive( \WP_User $user ) {
|
20 |
return parent::isProfileActive( $user ) && $this->hasValidatedProfile( $user );
|
21 |
}
|
22 |
|
23 |
public function setupProfile() {
|
24 |
-
|
25 |
-
if ( in_array( $
|
26 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
27 |
}
|
28 |
-
} );
|
29 |
-
}
|
30 |
-
|
31 |
-
private function enqueueAdminU2f() {
|
32 |
-
$aDeps = [];
|
33 |
-
foreach ( [ 'u2f-bundle', 'shield-u2f-admin' ] as $sScript ) {
|
34 |
-
wp_enqueue_script(
|
35 |
-
$this->getCon()->prefix( $sScript ),
|
36 |
-
$this->getCon()->getPluginUrl_Js( $sScript ),
|
37 |
-
$aDeps
|
38 |
-
);
|
39 |
-
$aDeps[] = $this->getCon()->prefix( $sScript );
|
40 |
-
}
|
41 |
|
42 |
-
|
43 |
-
|
44 |
-
list( $oReg, $aSigns ) = $this->createNewU2fRegistrationRequest( $user );
|
45 |
-
wp_localize_script(
|
46 |
-
$this->getCon()->prefix( 'shield-u2f-admin' ),
|
47 |
-
'icwp_wpsf_vars_u2f',
|
48 |
-
[
|
49 |
-
'reg_request' => $oReg,
|
50 |
-
'signs' => $aSigns,
|
51 |
-
'ajax' => [
|
52 |
-
'u2f_remove' => $this->getMod()->getAjaxActionData( 'u2f_remove' )
|
53 |
-
],
|
54 |
-
'flags' => [
|
55 |
-
'has_validated' => $this->hasValidatedProfile( $user )
|
56 |
-
],
|
57 |
-
'strings' => [
|
58 |
-
'not_supported' => __( 'U2F Security Key registration is not supported in this browser', 'wp-simple-firewall' ),
|
59 |
-
'failed' => __( 'Key registration failed.', 'wp-simple-firewall' )
|
60 |
-
.' '.__( "Perhaps the device isn't supported, or you've already registered it.", 'wp-simple-firewall' )
|
61 |
-
.' '.__( 'Please retry or refresh the page.', 'wp-simple-firewall' ),
|
62 |
-
'do_save' => __( 'Key registration was successful.', 'wp-simple-firewall' )
|
63 |
-
.' '.__( 'Please now save your profile settings.', 'wp-simple-firewall' ),
|
64 |
-
'prompt_dialog' => __( 'Please provide a label to identify the new U2F device.', 'wp-simple-firewall' ),
|
65 |
-
'err_no_label' => __( 'Device registration may not proceed without a unique label.', 'wp-simple-firewall' ),
|
66 |
-
'err_invalid_label' => __( 'Device label must contain letters, numbers, underscore, or hypen, and be no more than 16 characters.', 'wp-simple-firewall' ),
|
67 |
-
]
|
68 |
-
]
|
69 |
-
);
|
70 |
-
}
|
71 |
-
catch ( \Exception $oE ) {
|
72 |
-
}
|
73 |
}
|
74 |
|
75 |
/**
|
@@ -102,7 +89,7 @@ class U2F extends BaseProvider {
|
|
102 |
]
|
103 |
];
|
104 |
}
|
105 |
-
catch ( \Exception $
|
106 |
}
|
107 |
|
108 |
return $aFieldData;
|
@@ -196,8 +183,8 @@ class U2F extends BaseProvider {
|
|
196 |
* @inheritDoc
|
197 |
*/
|
198 |
public function handleUserProfileSubmit( \WP_User $user ) {
|
199 |
-
$
|
200 |
-
$
|
201 |
|
202 |
$sU2fResponse = Services::Request()->post( 'icwp_wpsf_new_u2f_response' );
|
203 |
if ( !empty( $sU2fResponse ) ) {
|
@@ -225,42 +212,24 @@ class U2F extends BaseProvider {
|
|
225 |
$this->addRegistration( $user, $aConfirmedReg )
|
226 |
->setProfileValidated( $user );
|
227 |
|
228 |
-
$
|
229 |
}
|
230 |
-
catch ( \Exception $
|
231 |
-
$
|
232 |
-
$
|
233 |
-
$
|
234 |
}
|
235 |
}
|
236 |
-
elseif ( Services::Request()->post( 'wpsf_u2f_key_delete' ) === 'Y' ) {
|
237 |
-
$this->processRemovalFromAccount( $user );
|
238 |
-
$sMsg = __( 'U2F Device was removed from your profile.', 'wp-simple-firewall' );
|
239 |
-
}
|
240 |
|
241 |
-
if ( !empty( $
|
242 |
-
$this->getMod()->setFlashAdminNotice( $
|
243 |
}
|
244 |
}
|
245 |
|
246 |
-
/**
|
247 |
-
* @param \WP_User $user
|
248 |
-
* @param string $otp
|
249 |
-
* @return bool
|
250 |
-
*/
|
251 |
protected function processOtp( \WP_User $user, string $otp ) :bool {
|
252 |
return $this->validateU2F( $user, $otp );
|
253 |
}
|
254 |
|
255 |
-
/**
|
256 |
-
* @param \WP_User $user
|
257 |
-
* @return $this
|
258 |
-
*/
|
259 |
-
protected function processRemovalFromAccount( $user ) {
|
260 |
-
return $this->setProfileValidated( $user, false )
|
261 |
-
->deleteSecret( $user );
|
262 |
-
}
|
263 |
-
|
264 |
/**
|
265 |
* @param \WP_User $user
|
266 |
* @param array $aReg
|
@@ -314,10 +283,10 @@ class U2F extends BaseProvider {
|
|
314 |
* @return $this
|
315 |
*/
|
316 |
public function removeRegisteredU2fId( \WP_User $user, $sU2fID ) {
|
317 |
-
$
|
318 |
-
if ( isset( $
|
319 |
-
unset( $
|
320 |
-
$this->storeRegistrations( $user, $
|
321 |
}
|
322 |
return $this;
|
323 |
}
|
@@ -334,8 +303,8 @@ class U2F extends BaseProvider {
|
|
334 |
$aReg[ 'used_at' ] = Services::Request()->ts();
|
335 |
$this->addRegistration( $user, $aReg );
|
336 |
}
|
337 |
-
catch ( \Exception $
|
338 |
-
error_log( $
|
339 |
}
|
340 |
|
341 |
return !empty( $oRegistration );
|
2 |
|
3 |
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\LoginGuard\Lib\TwoFactor\Provider;
|
4 |
|
5 |
+
use FernleafSystems\Wordpress\Plugin\Shield\Controller\Assets\Enqueue;
|
6 |
use FernleafSystems\Wordpress\Plugin\Shield\Modules\LoginGuard;
|
7 |
use FernleafSystems\Wordpress\Services\Services;
|
8 |
use u2flib_server\RegisterRequest;
|
13 |
const SLUG = 'u2f';
|
14 |
const DEFAULT_SECRET = '[]';
|
15 |
|
16 |
+
public function isProfileActive( \WP_User $user ) :bool {
|
|
|
|
|
|
|
|
|
17 |
return parent::isProfileActive( $user ) && $this->hasValidatedProfile( $user );
|
18 |
}
|
19 |
|
20 |
public function setupProfile() {
|
21 |
+
add_filter( 'shield/custom_enqueues', function ( array $enqueues, $hook ) {
|
22 |
+
if ( in_array( $hook, [ 'profile.php', ] ) ) {
|
23 |
+
|
24 |
+
$enqueues[ Enqueue::JS ][] = 'shield/u2f-admin';
|
25 |
+
|
26 |
+
add_filter( 'shield/custom_localisations', function ( array $localz ) {
|
27 |
+
$user = Services::WpUsers()->getCurrentWpUser();
|
28 |
+
list( $reg, $signs ) = $this->createNewU2fRegistrationRequest( $user );
|
29 |
+
$localz[] = [
|
30 |
+
'shield/u2f-admin',
|
31 |
+
'icwp_wpsf_vars_u2f',
|
32 |
+
[
|
33 |
+
'reg_request' => $reg,
|
34 |
+
'signs' => $signs,
|
35 |
+
'ajax' => [
|
36 |
+
'u2f_remove' => $this->getMod()->getAjaxActionData( 'u2f_remove' )
|
37 |
+
],
|
38 |
+
'flags' => [
|
39 |
+
'has_validated' => $this->hasValidatedProfile( $user )
|
40 |
+
],
|
41 |
+
'strings' => [
|
42 |
+
'not_supported' => __( 'U2F Security Key registration is not supported in this browser', 'wp-simple-firewall' ),
|
43 |
+
'failed' => __( 'Key registration failed.', 'wp-simple-firewall' )
|
44 |
+
.' '.__( "Perhaps the device isn't supported, or you've already registered it.", 'wp-simple-firewall' )
|
45 |
+
.' '.__( 'Please retry or refresh the page.', 'wp-simple-firewall' ),
|
46 |
+
'do_save' => __( 'Key registration was successful.', 'wp-simple-firewall' )
|
47 |
+
.' '.__( 'Please now save your profile settings.', 'wp-simple-firewall' ),
|
48 |
+
'prompt_dialog' => __( 'Please provide a label to identify the new U2F device.', 'wp-simple-firewall' ),
|
49 |
+
'err_no_label' => __( 'Device registration may not proceed without a unique label.', 'wp-simple-firewall' ),
|
50 |
+
'err_invalid_label' => __( 'Device label must contain letters, numbers, underscore, or hypen, and be no more than 16 characters.', 'wp-simple-firewall' ),
|
51 |
+
]
|
52 |
+
]
|
53 |
+
];
|
54 |
+
return $localz;
|
55 |
+
} );
|
56 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
57 |
|
58 |
+
return $enqueues;
|
59 |
+
}, 10, 2 );
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
60 |
}
|
61 |
|
62 |
/**
|
89 |
]
|
90 |
];
|
91 |
}
|
92 |
+
catch ( \Exception $e ) {
|
93 |
}
|
94 |
|
95 |
return $aFieldData;
|
183 |
* @inheritDoc
|
184 |
*/
|
185 |
public function handleUserProfileSubmit( \WP_User $user ) {
|
186 |
+
$isError = false;
|
187 |
+
$msg = null;
|
188 |
|
189 |
$sU2fResponse = Services::Request()->post( 'icwp_wpsf_new_u2f_response' );
|
190 |
if ( !empty( $sU2fResponse ) ) {
|
212 |
$this->addRegistration( $user, $aConfirmedReg )
|
213 |
->setProfileValidated( $user );
|
214 |
|
215 |
+
$msg = __( 'U2F Device was successfully registered on your profile.', 'wp-simple-firewall' );
|
216 |
}
|
217 |
+
catch ( \Exception $e ) {
|
218 |
+
$isError = true;
|
219 |
+
$msg = sprintf( __( 'U2F Device registration failed with the following error: %s', 'wp-simple-firewall' ),
|
220 |
+
$e->getMessage() );
|
221 |
}
|
222 |
}
|
|
|
|
|
|
|
|
|
223 |
|
224 |
+
if ( !empty( $msg ) ) {
|
225 |
+
$this->getMod()->setFlashAdminNotice( $msg, $isError );
|
226 |
}
|
227 |
}
|
228 |
|
|
|
|
|
|
|
|
|
|
|
229 |
protected function processOtp( \WP_User $user, string $otp ) :bool {
|
230 |
return $this->validateU2F( $user, $otp );
|
231 |
}
|
232 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
233 |
/**
|
234 |
* @param \WP_User $user
|
235 |
* @param array $aReg
|
283 |
* @return $this
|
284 |
*/
|
285 |
public function removeRegisteredU2fId( \WP_User $user, $sU2fID ) {
|
286 |
+
$regs = $this->getRegistrations( $user );
|
287 |
+
if ( isset( $regs[ $sU2fID ] ) ) {
|
288 |
+
unset( $regs[ $sU2fID ] );
|
289 |
+
$this->storeRegistrations( $user, $regs );
|
290 |
}
|
291 |
return $this;
|
292 |
}
|
303 |
$aReg[ 'used_at' ] = Services::Request()->ts();
|
304 |
$this->addRegistration( $user, $aReg );
|
305 |
}
|
306 |
+
catch ( \Exception $e ) {
|
307 |
+
error_log( $e->getMessage() );
|
308 |
}
|
309 |
|
310 |
return !empty( $oRegistration );
|
src/lib/src/Modules/LoginGuard/Lib/TwoFactor/Provider/Yubikey.php
CHANGED
@@ -2,6 +2,7 @@
|
|
2 |
|
3 |
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\LoginGuard\Lib\TwoFactor\Provider;
|
4 |
|
|
|
5 |
use FernleafSystems\Wordpress\Plugin\Shield\Modules\LoginGuard;
|
6 |
use FernleafSystems\Wordpress\Services\Services;
|
7 |
|
@@ -12,29 +13,22 @@ class Yubikey extends BaseProvider {
|
|
12 |
const URL_YUBIKEY_VERIFY = 'https://api.yubico.com/wsapi/2.0/verify';
|
13 |
|
14 |
public function setupProfile() {
|
15 |
-
add_action( 'admin_enqueue_scripts', function ( $sHook ) {
|
16 |
-
if ( in_array( $sHook, [ 'profile.php', ] ) ) {
|
17 |
-
$this->enqueueYubikeyJS();
|
18 |
-
}
|
19 |
-
} );
|
20 |
-
}
|
21 |
|
22 |
-
|
23 |
-
|
24 |
-
|
25 |
-
|
26 |
-
|
27 |
-
|
28 |
-
|
29 |
-
|
30 |
-
|
31 |
-
|
32 |
-
|
33 |
-
|
34 |
-
|
35 |
-
|
36 |
-
|
37 |
-
);
|
38 |
}
|
39 |
|
40 |
public function renderUserProfileOptions( \WP_User $user ) :string {
|
@@ -138,11 +132,6 @@ class Yubikey extends BaseProvider {
|
|
138 |
return array_filter( array_map( 'trim', explode( ',', $this->getSecret( $user ) ) ) );
|
139 |
}
|
140 |
|
141 |
-
/**
|
142 |
-
* @param \WP_User $user
|
143 |
-
* @param string $otp
|
144 |
-
* @return bool
|
145 |
-
*/
|
146 |
protected function processOtp( \WP_User $user, string $otp ) :bool {
|
147 |
$valid = false;
|
148 |
|
2 |
|
3 |
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\LoginGuard\Lib\TwoFactor\Provider;
|
4 |
|
5 |
+
use FernleafSystems\Wordpress\Plugin\Shield\Controller\Assets\Enqueue;
|
6 |
use FernleafSystems\Wordpress\Plugin\Shield\Modules\LoginGuard;
|
7 |
use FernleafSystems\Wordpress\Services\Services;
|
8 |
|
13 |
const URL_YUBIKEY_VERIFY = 'https://api.yubico.com/wsapi/2.0/verify';
|
14 |
|
15 |
public function setupProfile() {
|
|
|
|
|
|
|
|
|
|
|
|
|
16 |
|
17 |
+
add_filter( 'shield/custom_enqueues', function ( array $enqueues, $hook ) {
|
18 |
+
if ( in_array( $hook, [ 'profile.php', ] ) ) {
|
19 |
+
$enqueues[ Enqueue::JS ][] = 'shield/userprofile';
|
20 |
+
|
21 |
+
add_filter( 'shield/custom_localisations', function ( array $localz ) {
|
22 |
+
$localz[] = [
|
23 |
+
'shield/userprofile',
|
24 |
+
'icwp_wpsf_vars_profileyubikey',
|
25 |
+
[ 'yubikey_remove' => $this->getMod()->getAjaxActionData( 'yubikey_remove' ) ]
|
26 |
+
];
|
27 |
+
return $localz;
|
28 |
+
} );
|
29 |
+
}
|
30 |
+
return $enqueues;
|
31 |
+
}, 10, 2 );
|
|
|
32 |
}
|
33 |
|
34 |
public function renderUserProfileOptions( \WP_User $user ) :string {
|
132 |
return array_filter( array_map( 'trim', explode( ',', $this->getSecret( $user ) ) ) );
|
133 |
}
|
134 |
|
|
|
|
|
|
|
|
|
|
|
135 |
protected function processOtp( \WP_User $user, string $otp ) :bool {
|
136 |
$valid = false;
|
137 |
|
src/lib/src/Modules/LoginGuard/ModCon.php
CHANGED
@@ -2,6 +2,7 @@
|
|
2 |
|
3 |
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\LoginGuard;
|
4 |
|
|
|
5 |
use FernleafSystems\Wordpress\Plugin\Shield\Modules\BaseShield;
|
6 |
use FernleafSystems\Wordpress\Plugin\Shield\Modules\Plugin\Lib\Captcha\CaptchaConfigVO;
|
7 |
use FernleafSystems\Wordpress\Services\Services;
|
@@ -263,18 +264,29 @@ class ModCon extends BaseShield\ModCon {
|
|
263 |
$this->getOptions()->setOpt( 'enable_login_gasp_check', $enable ? 'Y' : 'N' );
|
264 |
}
|
265 |
|
266 |
-
public function
|
267 |
-
parent::
|
268 |
-
|
269 |
-
|
270 |
-
$this->getCon()->prefix( 'global-plugin' ),
|
271 |
'icwp_wpsf_vars_lg',
|
272 |
[
|
273 |
'ajax_gen_backup_codes' => $this->getAjaxActionData( 'gen_backup_codes' ),
|
274 |
'ajax_del_backup_codes' => $this->getAjaxActionData( 'del_backup_codes' ),
|
275 |
]
|
276 |
-
|
277 |
-
|
278 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
279 |
}
|
280 |
}
|
2 |
|
3 |
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\LoginGuard;
|
4 |
|
5 |
+
use FernleafSystems\Wordpress\Plugin\Shield\Controller\Assets\Enqueue;
|
6 |
use FernleafSystems\Wordpress\Plugin\Shield\Modules\BaseShield;
|
7 |
use FernleafSystems\Wordpress\Plugin\Shield\Modules\Plugin\Lib\Captcha\CaptchaConfigVO;
|
8 |
use FernleafSystems\Wordpress\Services\Services;
|
264 |
$this->getOptions()->setOpt( 'enable_login_gasp_check', $enable ? 'Y' : 'N' );
|
265 |
}
|
266 |
|
267 |
+
public function getScriptLocalisations() :array {
|
268 |
+
$locals = parent::getScriptLocalisations();
|
269 |
+
$locals[] = [
|
270 |
+
'global-plugin',
|
|
|
271 |
'icwp_wpsf_vars_lg',
|
272 |
[
|
273 |
'ajax_gen_backup_codes' => $this->getAjaxActionData( 'gen_backup_codes' ),
|
274 |
'ajax_del_backup_codes' => $this->getAjaxActionData( 'del_backup_codes' ),
|
275 |
]
|
276 |
+
];
|
277 |
+
return $locals;
|
278 |
+
}
|
279 |
+
|
280 |
+
public function getCustomScriptEnqueues() :array {
|
281 |
+
$enqs = [];
|
282 |
+
if ( is_admin() || is_network_admin() ) {
|
283 |
+
$enqs[ Enqueue::CSS ] = [
|
284 |
+
'wp-wp-jquery-ui-dialog'
|
285 |
+
];
|
286 |
+
$enqs[ Enqueue::JS ] = [
|
287 |
+
'wp-jquery-ui-dialog'
|
288 |
+
];
|
289 |
+
}
|
290 |
+
return $enqs;
|
291 |
}
|
292 |
}
|
src/lib/src/Modules/LoginGuard/UI.php
CHANGED
@@ -3,27 +3,39 @@
|
|
3 |
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\LoginGuard;
|
4 |
|
5 |
use FernleafSystems\Wordpress\Plugin\Shield\Modules\BaseShield;
|
|
|
6 |
|
7 |
class UI extends BaseShield\UI {
|
8 |
|
9 |
protected function getSectionWarnings( string $section ) :array {
|
10 |
-
$
|
11 |
|
12 |
if ( $section == 'section_brute_force_login_protection' && !$this->getCon()->isPremiumActive() ) {
|
13 |
$sIntegration = $this->getPremiumOnlyIntegration();
|
14 |
if ( !empty( $sIntegration ) ) {
|
15 |
-
$
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
16 |
}
|
17 |
}
|
18 |
|
19 |
if ( $section == 'section_2fa_email' ) {
|
20 |
-
$
|
21 |
__( '2FA by email demands that your WP site is properly configured to send email.', 'wp-simple-firewall' )
|
22 |
.'<br/>'.__( 'This is a common problem and you may get locked out in the future if you ignore this.', 'wp-simple-firewall' )
|
23 |
.' '.sprintf( '<a href="%s" target="_blank" class="alert-link">%s</a>', 'https://shsec.io/dd', __( 'Learn More.', 'wp-simple-firewall' ) );
|
24 |
}
|
25 |
|
26 |
-
return $
|
27 |
}
|
28 |
|
29 |
/**
|
3 |
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\LoginGuard;
|
4 |
|
5 |
use FernleafSystems\Wordpress\Plugin\Shield\Modules\BaseShield;
|
6 |
+
use FernleafSystems\Wordpress\Plugin\Shield\Utilities\Time\WorldTimeApi;
|
7 |
|
8 |
class UI extends BaseShield\UI {
|
9 |
|
10 |
protected function getSectionWarnings( string $section ) :array {
|
11 |
+
$warnings = [];
|
12 |
|
13 |
if ( $section == 'section_brute_force_login_protection' && !$this->getCon()->isPremiumActive() ) {
|
14 |
$sIntegration = $this->getPremiumOnlyIntegration();
|
15 |
if ( !empty( $sIntegration ) ) {
|
16 |
+
$warnings[] = sprintf( __( 'Support for login protection with %s is a Pro-only feature.', 'wp-simple-firewall' ), $sIntegration );
|
17 |
+
}
|
18 |
+
}
|
19 |
+
|
20 |
+
if ( $section == 'section_2fa_ga' ) {
|
21 |
+
try {
|
22 |
+
$diff = ( new WorldTimeApi() )->diffServerWithReal();
|
23 |
+
if ( $diff > 10 ) {
|
24 |
+
$warnings[] = __( 'It appears that your server time configuration is out of sync - Please contact your server admin, as features like Google Authenticator wont work.', 'wp-simple-firewall' );
|
25 |
+
}
|
26 |
+
}
|
27 |
+
catch ( \Exception $e ) {
|
28 |
}
|
29 |
}
|
30 |
|
31 |
if ( $section == 'section_2fa_email' ) {
|
32 |
+
$warnings[] =
|
33 |
__( '2FA by email demands that your WP site is properly configured to send email.', 'wp-simple-firewall' )
|
34 |
.'<br/>'.__( 'This is a common problem and you may get locked out in the future if you ignore this.', 'wp-simple-firewall' )
|
35 |
.' '.sprintf( '<a href="%s" target="_blank" class="alert-link">%s</a>', 'https://shsec.io/dd', __( 'Learn More.', 'wp-simple-firewall' ) );
|
36 |
}
|
37 |
|
38 |
+
return $warnings;
|
39 |
}
|
40 |
|
41 |
/**
|
src/lib/src/Modules/ModConsumer.php
CHANGED
@@ -12,7 +12,7 @@ use FernleafSystems\Wordpress\Plugin\Shield\Modules;
|
|
12 |
trait ModConsumer {
|
13 |
|
14 |
/**
|
15 |
-
* @var \
|
16 |
*/
|
17 |
private $oMod;
|
18 |
|
@@ -24,7 +24,7 @@ trait ModConsumer {
|
|
24 |
}
|
25 |
|
26 |
/**
|
27 |
-
* @return
|
28 |
*/
|
29 |
public function getMod() {
|
30 |
return $this->oMod;
|
@@ -38,20 +38,20 @@ trait ModConsumer {
|
|
38 |
}
|
39 |
|
40 |
/**
|
41 |
-
* @param Controller $
|
42 |
* @return $this
|
43 |
*/
|
44 |
-
public function setCon( $
|
45 |
-
$this->getMod()->setCon( $
|
46 |
return $this;
|
47 |
}
|
48 |
|
49 |
/**
|
50 |
-
* @param
|
51 |
* @return $this
|
52 |
*/
|
53 |
-
public function setMod( $
|
54 |
-
$this->oMod = $
|
55 |
return $this;
|
56 |
}
|
57 |
}
|
12 |
trait ModConsumer {
|
13 |
|
14 |
/**
|
15 |
+
* @var Modules\Base\ModCon
|
16 |
*/
|
17 |
private $oMod;
|
18 |
|
24 |
}
|
25 |
|
26 |
/**
|
27 |
+
* @return Modules\Base\ModCon
|
28 |
*/
|
29 |
public function getMod() {
|
30 |
return $this->oMod;
|
38 |
}
|
39 |
|
40 |
/**
|
41 |
+
* @param Controller $con
|
42 |
* @return $this
|
43 |
*/
|
44 |
+
public function setCon( $con ) {
|
45 |
+
$this->getMod()->setCon( $con );
|
46 |
return $this;
|
47 |
}
|
48 |
|
49 |
/**
|
50 |
+
* @param Modules\Base\ModCon $mod
|
51 |
* @return $this
|
52 |
*/
|
53 |
+
public function setMod( $mod ) {
|
54 |
+
$this->oMod = $mod;
|
55 |
return $this;
|
56 |
}
|
57 |
}
|
src/lib/src/Modules/Plugin/AdminNotices.php
CHANGED
@@ -242,7 +242,7 @@ class AdminNotices extends Shield\Modules\Base\AdminNotices {
|
|
242 |
'dismiss' => __( 'Dismiss this notice', 'wp-simple-firewall' )
|
243 |
],
|
244 |
'hrefs' => [
|
245 |
-
'upgrade_link' => Services::WpPlugins()->getUrl_Upgrade( $this->getCon()->
|
246 |
]
|
247 |
];
|
248 |
}
|
@@ -310,7 +310,7 @@ class AdminNotices extends Shield\Modules\Base\AdminNotices {
|
|
310 |
}
|
311 |
|
312 |
protected function isDisplayNeeded( NoticeVO $notice ) :bool {
|
313 |
-
$
|
314 |
/** @var Options $oOpts */
|
315 |
$oOpts = $this->getOptions();
|
316 |
|
@@ -321,7 +321,7 @@ class AdminNotices extends Shield\Modules\Base\AdminNotices {
|
|
321 |
break;
|
322 |
|
323 |
case 'override-forceoff':
|
324 |
-
$needed = $
|
325 |
break;
|
326 |
|
327 |
case 'php7':
|
@@ -333,7 +333,7 @@ class AdminNotices extends Shield\Modules\Base\AdminNotices {
|
|
333 |
break;
|
334 |
|
335 |
case 'update-available':
|
336 |
-
$needed = Services::WpPlugins()->isUpdateAvailable( $
|
337 |
break;
|
338 |
|
339 |
case 'compat-sgoptimize':
|
@@ -354,7 +354,7 @@ class AdminNotices extends Shield\Modules\Base\AdminNotices {
|
|
354 |
private function isNeeded_PluginTooOld() :bool {
|
355 |
$needed = false;
|
356 |
$con = $this->getCon();
|
357 |
-
if ( Services::WpPlugins()->isUpdateAvailable( $con->
|
358 |
$versions = Transient::Get( $con->prefix( 'releases' ) );
|
359 |
if ( !is_array( $versions ) ) {
|
360 |
$versions = ( new Shield\Utilities\Github\ListTags() )->run( 'FernleafSystems/Shield-Security-for-WordPress' );
|
242 |
'dismiss' => __( 'Dismiss this notice', 'wp-simple-firewall' )
|
243 |
],
|
244 |
'hrefs' => [
|
245 |
+
'upgrade_link' => Services::WpPlugins()->getUrl_Upgrade( $this->getCon()->base_file )
|
246 |
]
|
247 |
];
|
248 |
}
|
310 |
}
|
311 |
|
312 |
protected function isDisplayNeeded( NoticeVO $notice ) :bool {
|
313 |
+
$con = $this->getCon();
|
314 |
/** @var Options $oOpts */
|
315 |
$oOpts = $this->getOptions();
|
316 |
|
321 |
break;
|
322 |
|
323 |
case 'override-forceoff':
|
324 |
+
$needed = $con->getIfForceOffActive();
|
325 |
break;
|
326 |
|
327 |
case 'php7':
|
333 |
break;
|
334 |
|
335 |
case 'update-available':
|
336 |
+
$needed = Services::WpPlugins()->isUpdateAvailable( $con->base_file );
|
337 |
break;
|
338 |
|
339 |
case 'compat-sgoptimize':
|
354 |
private function isNeeded_PluginTooOld() :bool {
|
355 |
$needed = false;
|
356 |
$con = $this->getCon();
|
357 |
+
if ( Services::WpPlugins()->isUpdateAvailable( $con->base_file ) ) {
|
358 |
$versions = Transient::Get( $con->prefix( 'releases' ) );
|
359 |
if ( !is_array( $versions ) ) {
|
360 |
$versions = ( new Shield\Utilities\Github\ListTags() )->run( 'FernleafSystems/Shield-Security-for-WordPress' );
|
src/lib/src/Modules/Plugin/AjaxHandler.php
CHANGED
@@ -180,8 +180,8 @@ class AjaxHandler extends Shield\Modules\BaseShield\AjaxHandler {
|
|
180 |
$msg = __( "Note couldn't be deleted", 'wp-simple-firewall' );
|
181 |
}
|
182 |
}
|
183 |
-
catch ( \Exception $
|
184 |
-
$msg = $
|
185 |
}
|
186 |
}
|
187 |
|
@@ -202,7 +202,7 @@ class AjaxHandler extends Shield\Modules\BaseShield\AjaxHandler {
|
|
202 |
|
203 |
// TODO: align with wizard AND combine with file upload errors
|
204 |
if ( $aFormParams[ 'confirm' ] !== 'Y' ) {
|
205 |
-
$
|
206 |
}
|
207 |
else {
|
208 |
$sMasterSiteUrl = $aFormParams[ 'MasterSiteUrl' ];
|
@@ -213,20 +213,20 @@ class AjaxHandler extends Shield\Modules\BaseShield\AjaxHandler {
|
|
213 |
|
214 |
/** @var Shield\Databases\AdminNotes\Insert $oInserter */
|
215 |
try {
|
216 |
-
$
|
217 |
->setMod( $this->getMod() )
|
218 |
->fromSite( $sMasterSiteUrl, $sSecretKey, $bNetwork );
|
219 |
}
|
220 |
-
catch ( \Exception $
|
221 |
-
$
|
222 |
}
|
223 |
-
$success = $
|
224 |
-
$
|
225 |
}
|
226 |
|
227 |
return [
|
228 |
'success' => $success,
|
229 |
-
'message' => $
|
230 |
];
|
231 |
}
|
232 |
|
180 |
$msg = __( "Note couldn't be deleted", 'wp-simple-firewall' );
|
181 |
}
|
182 |
}
|
183 |
+
catch ( \Exception $e ) {
|
184 |
+
$msg = $e->getMessage();
|
185 |
}
|
186 |
}
|
187 |
|
202 |
|
203 |
// TODO: align with wizard AND combine with file upload errors
|
204 |
if ( $aFormParams[ 'confirm' ] !== 'Y' ) {
|
205 |
+
$msg = __( 'Please check the box to confirm your intent to overwrite settings', 'wp-simple-firewall' );
|
206 |
}
|
207 |
else {
|
208 |
$sMasterSiteUrl = $aFormParams[ 'MasterSiteUrl' ];
|
213 |
|
214 |
/** @var Shield\Databases\AdminNotes\Insert $oInserter */
|
215 |
try {
|
216 |
+
$code = ( new Plugin\Lib\ImportExport\Import() )
|
217 |
->setMod( $this->getMod() )
|
218 |
->fromSite( $sMasterSiteUrl, $sSecretKey, $bNetwork );
|
219 |
}
|
220 |
+
catch ( \Exception $e ) {
|
221 |
+
$code = $e->getCode();
|
222 |
}
|
223 |
+
$success = $code == 0;
|
224 |
+
$msg = $success ? __( 'Options imported successfully', 'wp-simple-firewall' ) : __( 'Options failed to import', 'wp-simple-firewall' );
|
225 |
}
|
226 |
|
227 |
return [
|
228 |
'success' => $success,
|
229 |
+
'message' => $msg
|
230 |
];
|
231 |
}
|
232 |
|
src/lib/src/Modules/Plugin/Components/PluginBadge.php
CHANGED
@@ -122,8 +122,8 @@ class PluginBadge {
|
|
122 |
try {
|
123 |
$render = $this->getMod()->renderTemplate( 'snippets/plugin_badge_widget', $data, true );
|
124 |
}
|
125 |
-
catch ( \Exception $
|
126 |
-
$render = 'Could not generate badge: '.$
|
127 |
}
|
128 |
return $render;
|
129 |
}
|
122 |
try {
|
123 |
$render = $this->getMod()->renderTemplate( 'snippets/plugin_badge_widget', $data, true );
|
124 |
}
|
125 |
+
catch ( \Exception $e ) {
|
126 |
+
$render = 'Could not generate badge: '.$e->getMessage();
|
127 |
}
|
128 |
return $render;
|
129 |
}
|
src/lib/src/Modules/Plugin/Components/SiteGroundPluginCompatibility.php
CHANGED
@@ -18,43 +18,40 @@ class SiteGroundPluginCompatibility {
|
|
18 |
}
|
19 |
}
|
20 |
}
|
21 |
-
catch ( \Exception $
|
22 |
}
|
23 |
}
|
24 |
return $bIncompatExist;
|
25 |
}
|
26 |
|
27 |
-
|
28 |
-
|
29 |
-
*/
|
30 |
-
public function isSGOptimizerPluginAsExpected() {
|
31 |
-
$bExpected = false;
|
32 |
try {
|
33 |
$oRefl = new \ReflectionClass( '\SiteGround_Optimizer\Options\Options' );
|
34 |
-
$
|
35 |
-
|
36 |
}
|
37 |
-
catch ( \Exception $
|
38 |
}
|
39 |
-
return $
|
40 |
}
|
41 |
|
42 |
/**
|
43 |
* @return bool
|
44 |
*/
|
45 |
public function switchOffOptions() {
|
46 |
-
$
|
47 |
if ( $this->isSGOptimizerPluginAsExpected() ) {
|
48 |
try {
|
49 |
foreach ( $this->getIncompatOptions() as $sOption ) {
|
50 |
\SiteGround_Optimizer\Options\Options::disable_option( $sOption );
|
51 |
}
|
52 |
-
$
|
53 |
}
|
54 |
-
catch ( \Exception $
|
55 |
}
|
56 |
}
|
57 |
-
return $
|
58 |
}
|
59 |
|
60 |
/**
|
18 |
}
|
19 |
}
|
20 |
}
|
21 |
+
catch ( \Exception $e ) {
|
22 |
}
|
23 |
}
|
24 |
return $bIncompatExist;
|
25 |
}
|
26 |
|
27 |
+
public function isSGOptimizerPluginAsExpected() :bool {
|
28 |
+
$isExpected = false;
|
|
|
|
|
|
|
29 |
try {
|
30 |
$oRefl = new \ReflectionClass( '\SiteGround_Optimizer\Options\Options' );
|
31 |
+
$isExpected = $oRefl->getMethod( 'is_enabled' )->isStatic()
|
32 |
+
&& $oRefl->getMethod( 'disable_option' )->isStatic();
|
33 |
}
|
34 |
+
catch ( \Exception $e ) {
|
35 |
}
|
36 |
+
return $isExpected;
|
37 |
}
|
38 |
|
39 |
/**
|
40 |
* @return bool
|
41 |
*/
|
42 |
public function switchOffOptions() {
|
43 |
+
$success = false;
|
44 |
if ( $this->isSGOptimizerPluginAsExpected() ) {
|
45 |
try {
|
46 |
foreach ( $this->getIncompatOptions() as $sOption ) {
|
47 |
\SiteGround_Optimizer\Options\Options::disable_option( $sOption );
|
48 |
}
|
49 |
+
$success = !$this->testIsIncompatible();
|
50 |
}
|
51 |
+
catch ( \Exception $e ) {
|
52 |
}
|
53 |
}
|
54 |
+
return $success;
|
55 |
}
|
56 |
|
57 |
/**
|
src/lib/src/Modules/Plugin/Lib/Debug/Collate.php
CHANGED
@@ -4,6 +4,7 @@ namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Plugin\Lib\Debug;
|
|
4 |
|
5 |
use FernleafSystems\Wordpress\Plugin\Shield\Modules\ModConsumer;
|
6 |
use FernleafSystems\Wordpress\Plugin\Shield\Modules\Plugin\Options;
|
|
|
7 |
use FernleafSystems\Wordpress\Plugin\Shield\Utilities\Tool\FormatBytes;
|
8 |
use FernleafSystems\Wordpress\Services\Services;
|
9 |
use FernleafSystems\Wordpress\Services\Utilities\Integrations\WpHashes\ApiPing;
|
@@ -60,9 +61,17 @@ class Collate {
|
|
60 |
|
61 |
$totalDisk = disk_total_space( ABSPATH );
|
62 |
$freeDisk = disk_free_space( ABSPATH );
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
63 |
return [
|
64 |
'Host OS' => PHP_OS,
|
65 |
'Server Hostname' => gethostname(),
|
|
|
66 |
'Server IPs' => implode( ', ', $aIPs ),
|
67 |
'CloudFlare' => empty( $req->server( 'HTTP_CF_REQUEST_ID' ) ) ? 'No' : 'Yes',
|
68 |
'rDNS' => empty( $rDNS ) ? '-' : $rDNS,
|
4 |
|
5 |
use FernleafSystems\Wordpress\Plugin\Shield\Modules\ModConsumer;
|
6 |
use FernleafSystems\Wordpress\Plugin\Shield\Modules\Plugin\Options;
|
7 |
+
use FernleafSystems\Wordpress\Plugin\Shield\Utilities\Time\WorldTimeApi;
|
8 |
use FernleafSystems\Wordpress\Plugin\Shield\Utilities\Tool\FormatBytes;
|
9 |
use FernleafSystems\Wordpress\Services\Services;
|
10 |
use FernleafSystems\Wordpress\Services\Utilities\Integrations\WpHashes\ApiPing;
|
61 |
|
62 |
$totalDisk = disk_total_space( ABSPATH );
|
63 |
$freeDisk = disk_free_space( ABSPATH );
|
64 |
+
try {
|
65 |
+
$diff = ( new WorldTimeApi() )->diffServerWithReal();
|
66 |
+
}
|
67 |
+
catch ( \Exception $e ) {
|
68 |
+
$diff = 'failed';
|
69 |
+
}
|
70 |
+
|
71 |
return [
|
72 |
'Host OS' => PHP_OS,
|
73 |
'Server Hostname' => gethostname(),
|
74 |
+
'Server Time Difference' => $diff,
|
75 |
'Server IPs' => implode( ', ', $aIPs ),
|
76 |
'CloudFlare' => empty( $req->server( 'HTTP_CF_REQUEST_ID' ) ) ? 'No' : 'Yes',
|
77 |
'rDNS' => empty( $rDNS ) ? '-' : $rDNS,
|
src/lib/src/Modules/Plugin/Lib/ImportExport/Export.php
CHANGED
@@ -26,7 +26,7 @@ class Export {
|
|
26 |
break;
|
27 |
}
|
28 |
}
|
29 |
-
catch ( \Exception $
|
30 |
}
|
31 |
die();
|
32 |
}
|
26 |
break;
|
27 |
}
|
28 |
}
|
29 |
+
catch ( \Exception $e ) {
|
30 |
}
|
31 |
die();
|
32 |
}
|
src/lib/src/Modules/Plugin/Lib/ImportExport/Import.php
CHANGED
@@ -25,21 +25,21 @@ class Import {
|
|
25 |
break;
|
26 |
}
|
27 |
}
|
28 |
-
catch ( \Exception $
|
29 |
}
|
30 |
die();
|
31 |
}
|
32 |
|
33 |
/**
|
34 |
-
* @param string $
|
35 |
* @throws \Exception
|
36 |
*/
|
37 |
-
public function fromFile( $
|
38 |
if ( !$this->getCon()->isPluginAdmin() ) {
|
39 |
throw new \Exception( __( 'Not currently logged-in as security admin', 'wp-simple-firewall' ) );
|
40 |
}
|
41 |
|
42 |
-
$sContent = Services::WpFs()->getFileContent( $
|
43 |
if ( empty( $sContent ) ) {
|
44 |
throw new \Exception( __( 'Uploaded file was empty', 'wp-simple-firewall' ) );
|
45 |
}
|
25 |
break;
|
26 |
}
|
27 |
}
|
28 |
+
catch ( \Exception $e ) {
|
29 |
}
|
30 |
die();
|
31 |
}
|
32 |
|
33 |
/**
|
34 |
+
* @param string $path
|
35 |
* @throws \Exception
|
36 |
*/
|
37 |
+
public function fromFile( $path ) {
|
38 |
if ( !$this->getCon()->isPluginAdmin() ) {
|
39 |
throw new \Exception( __( 'Not currently logged-in as security admin', 'wp-simple-firewall' ) );
|
40 |
}
|
41 |
|
42 |
+
$sContent = Services::WpFs()->getFileContent( $path );
|
43 |
if ( empty( $sContent ) ) {
|
44 |
throw new \Exception( __( 'Uploaded file was empty', 'wp-simple-firewall' ) );
|
45 |
}
|
src/lib/src/Modules/Plugin/Lib/PluginTelemetry.php
CHANGED
@@ -3,6 +3,7 @@
|
|
3 |
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Plugin\Lib;
|
4 |
|
5 |
use FernleafSystems\Utilities\Logic\OneTimeExecute;
|
|
|
6 |
use FernleafSystems\Wordpress\Plugin\Shield\Modules\Base\ModCon;
|
7 |
use FernleafSystems\Wordpress\Plugin\Shield\Modules\ModConsumer;
|
8 |
use FernleafSystems\Wordpress\Plugin\Shield\Modules\Plugin;
|
@@ -86,17 +87,18 @@ class PluginTelemetry {
|
|
86 |
}
|
87 |
|
88 |
if ( !empty( $data[ 'events' ] ) ) {
|
89 |
-
|
90 |
-
|
91 |
-
|
92 |
-
|
|
|
93 |
}
|
94 |
if ( !empty( $data[ 'login_protect' ] ) ) {
|
95 |
$data[ 'login_protect' ][ 'options' ][ 'email_can_send_verified_at' ] =
|
96 |
$data[ 'login_protect' ][ 'options' ][ 'email_can_send_verified_at' ] > 0 ? 1 : 0;
|
97 |
}
|
98 |
if ( !empty( $data[ 'admin_access_restriction' ] ) ) {
|
99 |
-
$keys= [
|
100 |
'admin_access_restrict_plugins',
|
101 |
'admin_access_restrict_themes',
|
102 |
'admin_access_restrict_posts'
|
3 |
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Plugin\Lib;
|
4 |
|
5 |
use FernleafSystems\Utilities\Logic\OneTimeExecute;
|
6 |
+
use FernleafSystems\Wordpress\Plugin\Shield\Databases\Events\Select;
|
7 |
use FernleafSystems\Wordpress\Plugin\Shield\Modules\Base\ModCon;
|
8 |
use FernleafSystems\Wordpress\Plugin\Shield\Modules\ModConsumer;
|
9 |
use FernleafSystems\Wordpress\Plugin\Shield\Modules\Plugin;
|
87 |
}
|
88 |
|
89 |
if ( !empty( $data[ 'events' ] ) ) {
|
90 |
+
/** @var Select $select */
|
91 |
+
$select = $con->getModule_Events()
|
92 |
+
->getDbHandler_Events()
|
93 |
+
->getQuerySelector();
|
94 |
+
$data[ 'events' ][ 'stats' ] = $select->sumAllEvents();
|
95 |
}
|
96 |
if ( !empty( $data[ 'login_protect' ] ) ) {
|
97 |
$data[ 'login_protect' ][ 'options' ][ 'email_can_send_verified_at' ] =
|
98 |
$data[ 'login_protect' ][ 'options' ][ 'email_can_send_verified_at' ] > 0 ? 1 : 0;
|
99 |
}
|
100 |
if ( !empty( $data[ 'admin_access_restriction' ] ) ) {
|
101 |
+
$keys = [
|
102 |
'admin_access_restrict_plugins',
|
103 |
'admin_access_restrict_themes',
|
104 |
'admin_access_restrict_posts'
|
src/lib/src/Modules/Plugin/Lib/TourManager.php
CHANGED
@@ -28,12 +28,12 @@ class TourManager {
|
|
28 |
public function isCompleted( $sTourKey ) {
|
29 |
try {
|
30 |
$aTrs = $this->getTours();
|
31 |
-
$
|
32 |
}
|
33 |
-
catch ( \Exception $
|
34 |
-
$
|
35 |
}
|
36 |
-
return $
|
37 |
}
|
38 |
|
39 |
/**
|
@@ -49,7 +49,7 @@ class TourManager {
|
|
49 |
$this->getCon()
|
50 |
->getCurrentUserMeta()->tours = $aTrs;
|
51 |
}
|
52 |
-
catch ( \Exception $
|
53 |
}
|
54 |
}
|
55 |
return $this;
|
28 |
public function isCompleted( $sTourKey ) {
|
29 |
try {
|
30 |
$aTrs = $this->getTours();
|
31 |
+
$shown = isset( $aTrs[ $sTourKey ] ) && $aTrs[ $sTourKey ] > 0;
|
32 |
}
|
33 |
+
catch ( \Exception $e ) {
|
34 |
+
$shown = true; // in-case there's a meta saving issue.
|
35 |
}
|
36 |
+
return $shown;
|
37 |
}
|
38 |
|
39 |
/**
|
49 |
$this->getCon()
|
50 |
->getCurrentUserMeta()->tours = $aTrs;
|
51 |
}
|
52 |
+
catch ( \Exception $e ) {
|
53 |
}
|
54 |
}
|
55 |
return $this;
|
src/lib/src/Modules/Plugin/ModCon.php
CHANGED
@@ -3,6 +3,7 @@
|
|
3 |
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Plugin;
|
4 |
|
5 |
use FernleafSystems\Wordpress\Plugin\Shield;
|
|
|
6 |
use FernleafSystems\Wordpress\Plugin\Shield\Modules\BaseShield;
|
7 |
use FernleafSystems\Wordpress\Services\Services;
|
8 |
use FernleafSystems\Wordpress\Services\Utilities\Net\VisitorIpDetection;
|
@@ -118,14 +119,14 @@ class ModCon extends BaseShield\ModCon {
|
|
118 |
( new Lib\ImportExport\Import() )
|
119 |
->setMod( $this )
|
120 |
->fromFileUpload();
|
121 |
-
$
|
122 |
-
$
|
123 |
}
|
124 |
-
catch ( \Exception $
|
125 |
-
$
|
126 |
-
$
|
127 |
}
|
128 |
-
$this->setFlashAdminNotice( $
|
129 |
Services::Response()->redirect(
|
130 |
$this->getCon()->getModule_Insights()->getUrl_SubInsightsPage( 'importexport' )
|
131 |
);
|
@@ -321,7 +322,7 @@ class ModCon extends BaseShield\ModCon {
|
|
321 |
* hidden 20200121
|
322 |
* @return bool
|
323 |
*/
|
324 |
-
public function getIfShowIntroVideo() {
|
325 |
return false && ( $this->getActivateLength() < 8 )
|
326 |
&& ( Services::Request()->ts() - $this->getInstallDate() < 15 );
|
327 |
}
|
@@ -481,36 +482,35 @@ class ModCon extends BaseShield\ModCon {
|
|
481 |
return Services::WpUsers()->isUserAdmin();
|
482 |
}
|
483 |
|
484 |
-
public function
|
485 |
-
parent::insertCustomJsVars_Admin();
|
486 |
-
|
487 |
$con = $this->getCon();
|
|
|
|
|
488 |
if ( Services::WpPost()->isCurrentPage( 'plugins.php' ) ) {
|
489 |
-
$
|
490 |
-
|
491 |
-
|
492 |
'icwp_wpsf_vars_plugin',
|
493 |
[
|
494 |
-
'file' => $
|
495 |
'ajax' => [
|
496 |
'send_deactivate_survey' => $this->getAjaxActionData( 'send_deactivate_survey' ),
|
497 |
],
|
498 |
'hrefs' => [
|
499 |
-
'deactivate' => Services::WpPlugins()->getUrl_Deactivate( $
|
500 |
],
|
501 |
]
|
502 |
-
|
503 |
-
wp_enqueue_script( 'jquery-ui-dialog' ); // jquery and jquery-ui should be dependencies, didn't check though...
|
504 |
-
wp_enqueue_style( 'wp-jquery-ui-dialog' );
|
505 |
}
|
506 |
|
507 |
-
|
508 |
-
|
509 |
'icwp_wpsf_vars_tourmanager',
|
510 |
[ 'ajax' => $this->getAjaxActionData( 'mark_tour_finished' ) ]
|
511 |
-
|
512 |
-
|
513 |
-
|
|
|
514 |
'icwp_wpsf_vars_plugin',
|
515 |
[
|
516 |
'strings' => [
|
@@ -518,7 +518,22 @@ class ModCon extends BaseShield\ModCon {
|
|
518 |
'problem_downloading_file' => __( 'There was a problem downloading the file.', 'wp-simple-firewall' ),
|
519 |
],
|
520 |
]
|
521 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
522 |
}
|
523 |
|
524 |
public function getDbHandler_GeoIp() :Shield\Databases\GeoIp\Handler {
|
3 |
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Plugin;
|
4 |
|
5 |
use FernleafSystems\Wordpress\Plugin\Shield;
|
6 |
+
use FernleafSystems\Wordpress\Plugin\Shield\Controller\Assets\Enqueue;
|
7 |
use FernleafSystems\Wordpress\Plugin\Shield\Modules\BaseShield;
|
8 |
use FernleafSystems\Wordpress\Services\Services;
|
9 |
use FernleafSystems\Wordpress\Services\Utilities\Net\VisitorIpDetection;
|
119 |
( new Lib\ImportExport\Import() )
|
120 |
->setMod( $this )
|
121 |
->fromFileUpload();
|
122 |
+
$success = true;
|
123 |
+
$msg = __( 'Options imported successfully', 'wp-simple-firewall' );
|
124 |
}
|
125 |
+
catch ( \Exception $e ) {
|
126 |
+
$success = false;
|
127 |
+
$msg = $e->getMessage();
|
128 |
}
|
129 |
+
$this->setFlashAdminNotice( $msg, !$success );
|
130 |
Services::Response()->redirect(
|
131 |
$this->getCon()->getModule_Insights()->getUrl_SubInsightsPage( 'importexport' )
|
132 |
);
|
322 |
* hidden 20200121
|
323 |
* @return bool
|
324 |
*/
|
325 |
+
public function getIfShowIntroVideo() :bool {
|
326 |
return false && ( $this->getActivateLength() < 8 )
|
327 |
&& ( Services::Request()->ts() - $this->getInstallDate() < 15 );
|
328 |
}
|
482 |
return Services::WpUsers()->isUserAdmin();
|
483 |
}
|
484 |
|
485 |
+
public function getScriptLocalisations() :array {
|
|
|
|
|
486 |
$con = $this->getCon();
|
487 |
+
$locals = parent::getScriptLocalisations();
|
488 |
+
|
489 |
if ( Services::WpPost()->isCurrentPage( 'plugins.php' ) ) {
|
490 |
+
$file = $con->base_file;
|
491 |
+
$locals[] = [
|
492 |
+
'global-plugin',
|
493 |
'icwp_wpsf_vars_plugin',
|
494 |
[
|
495 |
+
'file' => $file,
|
496 |
'ajax' => [
|
497 |
'send_deactivate_survey' => $this->getAjaxActionData( 'send_deactivate_survey' ),
|
498 |
],
|
499 |
'hrefs' => [
|
500 |
+
'deactivate' => Services::WpPlugins()->getUrl_Deactivate( $file ),
|
501 |
],
|
502 |
]
|
503 |
+
];
|
|
|
|
|
504 |
}
|
505 |
|
506 |
+
$locals[] = [
|
507 |
+
'plugin',
|
508 |
'icwp_wpsf_vars_tourmanager',
|
509 |
[ 'ajax' => $this->getAjaxActionData( 'mark_tour_finished' ) ]
|
510 |
+
];
|
511 |
+
|
512 |
+
$locals[] = [
|
513 |
+
'plugin',
|
514 |
'icwp_wpsf_vars_plugin',
|
515 |
[
|
516 |
'strings' => [
|
518 |
'problem_downloading_file' => __( 'There was a problem downloading the file.', 'wp-simple-firewall' ),
|
519 |
],
|
520 |
]
|
521 |
+
];
|
522 |
+
|
523 |
+
return $locals;
|
524 |
+
}
|
525 |
+
|
526 |
+
public function getCustomScriptEnqueues() :array {
|
527 |
+
$enqs = [];
|
528 |
+
if ( Services::WpPost()->isCurrentPage( 'plugins.php' ) ) {
|
529 |
+
$enqs[ Enqueue::CSS ] = [
|
530 |
+
'wp-wp-jquery-ui-dialog'
|
531 |
+
];
|
532 |
+
$enqs[ Enqueue::JS ] = [
|
533 |
+
'wp-jquery-ui-dialog'
|
534 |
+
];
|
535 |
+
}
|
536 |
+
return $enqs;
|
537 |
}
|
538 |
|
539 |
public function getDbHandler_GeoIp() :Shield\Databases\GeoIp\Handler {
|
src/lib/src/Modules/Plugin/WpCli/Import.php
CHANGED
@@ -82,14 +82,14 @@ class Import extends Base\WpCli\BaseWpCliCmd {
|
|
82 |
$this->runImportFromFile( $sSource, WP_CLI\Utils\get_flag_value( $aA, 'delete-file', false ) );
|
83 |
}
|
84 |
}
|
85 |
-
catch ( \Exception $
|
86 |
WP_CLI::error_multi_line(
|
87 |
[
|
88 |
__( 'The import encountered an error.', 'wp-simple-firewall' ),
|
89 |
-
$
|
90 |
]
|
91 |
);
|
92 |
-
WP_CLI::halt( $
|
93 |
}
|
94 |
|
95 |
WP_CLI::success( __( 'Plugin settings imported successfully.', 'wp-simple-firewall' ) );
|
82 |
$this->runImportFromFile( $sSource, WP_CLI\Utils\get_flag_value( $aA, 'delete-file', false ) );
|
83 |
}
|
84 |
}
|
85 |
+
catch ( \Exception $e ) {
|
86 |
WP_CLI::error_multi_line(
|
87 |
[
|
88 |
__( 'The import encountered an error.', 'wp-simple-firewall' ),
|
89 |
+
$e->getMessage(),
|
90 |
]
|
91 |
);
|
92 |
+
WP_CLI::halt( $e->getCode() );
|
93 |
}
|
94 |
|
95 |
WP_CLI::success( __( 'Plugin settings imported successfully.', 'wp-simple-firewall' ) );
|
src/lib/src/Modules/SecurityAdmin/Lib/WhiteLabel/ApplyLabels.php
CHANGED
@@ -55,8 +55,8 @@ class ApplyLabels {
|
|
55 |
*/
|
56 |
public function adjustUpdateDataCount( $aUpdateData ) {
|
57 |
|
58 |
-
$
|
59 |
-
if ( Services::WpPlugins()->isUpdateAvailable( $
|
60 |
$aUpdateData[ 'counts' ][ 'total' ]--;
|
61 |
$aUpdateData[ 'counts' ][ 'plugins' ]--;
|
62 |
}
|
@@ -66,8 +66,8 @@ class ApplyLabels {
|
|
66 |
|
67 |
public function hideFromPluginEditor() {
|
68 |
$con = $this->getCon();
|
69 |
-
$
|
70 |
-
echo sprintf( '<script type="text/javascript">%s</script>', sprintf( $
|
71 |
}
|
72 |
|
73 |
/**
|
@@ -112,11 +112,11 @@ class ApplyLabels {
|
|
112 |
/**
|
113 |
* @filter
|
114 |
* @param array $aPluginMeta
|
115 |
-
* @param string $
|
116 |
* @return array
|
117 |
*/
|
118 |
-
public function removePluginMetaLinks( $aPluginMeta, $
|
119 |
-
if ( $
|
120 |
unset( $aPluginMeta[ 2 ] ); // View details
|
121 |
unset( $aPluginMeta[ 3 ] ); // Rate 5*
|
122 |
}
|
@@ -125,15 +125,15 @@ class ApplyLabels {
|
|
125 |
|
126 |
/**
|
127 |
* Hides the update if the page loaded is the plugins page or the updates page.
|
128 |
-
* @param \stdClass $
|
129 |
* @return \stdClass
|
130 |
*/
|
131 |
-
public function hidePluginUpdatesFromUI( $
|
132 |
-
$
|
133 |
-
if ( isset( $
|
134 |
-
unset( $
|
135 |
}
|
136 |
-
return $
|
137 |
}
|
138 |
|
139 |
private function isNeedToHideUpdates() :bool {
|
55 |
*/
|
56 |
public function adjustUpdateDataCount( $aUpdateData ) {
|
57 |
|
58 |
+
$file = $this->getCon()->base_file;
|
59 |
+
if ( Services::WpPlugins()->isUpdateAvailable( $file ) ) {
|
60 |
$aUpdateData[ 'counts' ][ 'total' ]--;
|
61 |
$aUpdateData[ 'counts' ][ 'plugins' ]--;
|
62 |
}
|
66 |
|
67 |
public function hideFromPluginEditor() {
|
68 |
$con = $this->getCon();
|
69 |
+
$js = Services::Data()->readFileContentsUsingInclude( $con->getPath_AssetJs( 'whitelabel.js' ) );
|
70 |
+
echo sprintf( '<script type="text/javascript">%s</script>', sprintf( $js, $con->base_file ) );
|
71 |
}
|
72 |
|
73 |
/**
|
112 |
/**
|
113 |
* @filter
|
114 |
* @param array $aPluginMeta
|
115 |
+
* @param string $pluginBaseFile
|
116 |
* @return array
|
117 |
*/
|
118 |
+
public function removePluginMetaLinks( $aPluginMeta, $pluginBaseFile ) {
|
119 |
+
if ( $pluginBaseFile == $this->getCon()->base_file ) {
|
120 |
unset( $aPluginMeta[ 2 ] ); // View details
|
121 |
unset( $aPluginMeta[ 3 ] ); // Rate 5*
|
122 |
}
|
125 |
|
126 |
/**
|
127 |
* Hides the update if the page loaded is the plugins page or the updates page.
|
128 |
+
* @param \stdClass $plugins
|
129 |
* @return \stdClass
|
130 |
*/
|
131 |
+
public function hidePluginUpdatesFromUI( $plugins ) {
|
132 |
+
$file = $this->getCon()->base_file;
|
133 |
+
if ( isset( $plugins->response[ $file ] ) ) {
|
134 |
+
unset( $plugins->response[ $file ] );
|
135 |
}
|
136 |
+
return $plugins;
|
137 |
}
|
138 |
|
139 |
private function isNeedToHideUpdates() :bool {
|
src/lib/src/Modules/SecurityAdmin/ModCon.php
CHANGED
@@ -286,11 +286,11 @@ class ModCon extends BaseShield\ModCon {
|
|
286 |
return $this->saveModOptions();
|
287 |
}
|
288 |
|
289 |
-
public function
|
290 |
-
parent::
|
291 |
|
292 |
if ( $this->getSecAdminTimeLeft() > 0 ) {
|
293 |
-
$
|
294 |
'ajax' => [
|
295 |
'check' => $this->getSecAdminCheckAjaxData(),
|
296 |
],
|
@@ -304,7 +304,7 @@ class ModCon extends BaseShield\ModCon {
|
|
304 |
];
|
305 |
}
|
306 |
else {
|
307 |
-
$
|
308 |
'ajax' => [
|
309 |
'req_email_remove' => $this->getAjaxActionData( 'req_email_remove' ),
|
310 |
],
|
@@ -314,13 +314,13 @@ class ModCon extends BaseShield\ModCon {
|
|
314 |
];
|
315 |
}
|
316 |
|
317 |
-
|
318 |
-
|
319 |
-
|
320 |
-
|
321 |
-
|
322 |
-
|
323 |
-
|
324 |
}
|
325 |
|
326 |
/**
|
286 |
return $this->saveModOptions();
|
287 |
}
|
288 |
|
289 |
+
public function getScriptLocalisations() :array {
|
290 |
+
$locals = parent::getScriptLocalisations();
|
291 |
|
292 |
if ( $this->getSecAdminTimeLeft() > 0 ) {
|
293 |
+
$data = [
|
294 |
'ajax' => [
|
295 |
'check' => $this->getSecAdminCheckAjaxData(),
|
296 |
],
|
304 |
];
|
305 |
}
|
306 |
else {
|
307 |
+
$data = [
|
308 |
'ajax' => [
|
309 |
'req_email_remove' => $this->getAjaxActionData( 'req_email_remove' ),
|
310 |
],
|
314 |
];
|
315 |
}
|
316 |
|
317 |
+
$locals[] = [
|
318 |
+
'plugin',
|
319 |
+
'icwp_wpsf_vars_secadmin',
|
320 |
+
$data
|
321 |
+
];
|
322 |
+
|
323 |
+
return $locals;
|
324 |
}
|
325 |
|
326 |
/**
|
src/lib/src/Modules/SecurityAdmin/WpCli/Pin.php
CHANGED
@@ -65,11 +65,11 @@ class Pin extends BaseWpCliCmd {
|
|
65 |
sprintf( __( 'Security admin pin set to: "%s"', 'wp-simple-firewall' ), $newPIN )
|
66 |
);
|
67 |
}
|
68 |
-
catch ( \Exception $
|
69 |
WP_CLI::error_multi_line(
|
70 |
[
|
71 |
__( 'Setting Security admin pin failed.', 'wp-simple-firewall' ),
|
72 |
-
$
|
73 |
]
|
74 |
);
|
75 |
WP_CLI::halt( 1 );
|
65 |
sprintf( __( 'Security admin pin set to: "%s"', 'wp-simple-firewall' ), $newPIN )
|
66 |
);
|
67 |
}
|
68 |
+
catch ( \Exception $e ) {
|
69 |
WP_CLI::error_multi_line(
|
70 |
[
|
71 |
__( 'Setting Security admin pin failed.', 'wp-simple-firewall' ),
|
72 |
+
$e->getMessage()
|
73 |
]
|
74 |
);
|
75 |
WP_CLI::halt( 1 );
|
src/lib/src/Modules/Sessions/Processor.php
CHANGED
@@ -99,68 +99,4 @@ class Processor extends BaseShield\Processor {
|
|
99 |
$mod->getSessionCon()->terminateCurrentSession();
|
100 |
$mod->getSessionCon()->queryCreateSession( $this->getCon()->getSessionId( true ), $user );
|
101 |
}
|
102 |
-
|
103 |
-
/**
|
104 |
-
* @return bool
|
105 |
-
* @deprecated 10.1
|
106 |
-
*/
|
107 |
-
public function terminateCurrentSession() {
|
108 |
-
$success = false;
|
109 |
-
|
110 |
-
$oSes = $this->getCurrentSession();
|
111 |
-
if ( $oSes instanceof Session\EntryVO ) {
|
112 |
-
$success = ( new Lib\Ops\Terminate() )
|
113 |
-
->setMod( $this->getMod() )
|
114 |
-
->byRecordId( $oSes->id );
|
115 |
-
}
|
116 |
-
|
117 |
-
$this->current = null;
|
118 |
-
$this->getCon()->clearSession();
|
119 |
-
|
120 |
-
return $success;
|
121 |
-
}
|
122 |
-
|
123 |
-
/**
|
124 |
-
* @return Session\EntryVO|null
|
125 |
-
* @deprecated 10.1
|
126 |
-
*/
|
127 |
-
public function getCurrentSession() {
|
128 |
-
/** @var ModCon $mod */
|
129 |
-
$mod = $this->getMod();
|
130 |
-
return empty( $this->current ) ? $mod->getSessionCon()->getCurrent() : $this->current;
|
131 |
-
}
|
132 |
-
|
133 |
-
/**
|
134 |
-
* @return Session\EntryVO|null
|
135 |
-
* @deprecated 10.1
|
136 |
-
*/
|
137 |
-
public function loadCurrentSession() {
|
138 |
-
$sess = null;
|
139 |
-
$con = $this->getCon();
|
140 |
-
if ( did_action( 'init' ) && $con->hasSessionId() ) {
|
141 |
-
$sess = $this->queryGetSession( $con->getSessionId() );
|
142 |
-
}
|
143 |
-
return $sess;
|
144 |
-
}
|
145 |
-
|
146 |
-
/**
|
147 |
-
* @param string $sessionID
|
148 |
-
* @param string $username
|
149 |
-
* @return bool
|
150 |
-
* @deprecated 10.1
|
151 |
-
*/
|
152 |
-
protected function queryCreateSession( $sessionID, $username ) {
|
153 |
-
return false;
|
154 |
-
}
|
155 |
-
|
156 |
-
/**
|
157 |
-
* Checks for and gets a user session.
|
158 |
-
* @param string $username
|
159 |
-
* @param string $sessionID
|
160 |
-
* @return Session\EntryVO|null
|
161 |
-
* @deprecated 10.1
|
162 |
-
*/
|
163 |
-
private function queryGetSession( $sessionID, $username = '' ) {
|
164 |
-
return null;
|
165 |
-
}
|
166 |
}
|
99 |
$mod->getSessionCon()->terminateCurrentSession();
|
100 |
$mod->getSessionCon()->queryCreateSession( $this->getCon()->getSessionId( true ), $user );
|
101 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
102 |
}
|
src/lib/src/Modules/Traffic/Lib/Limit/Limiter.php
CHANGED
@@ -28,15 +28,15 @@ class Limiter {
|
|
28 |
->setMod( $this->getMod() )
|
29 |
->runTest( Services::IP()->getRequestIp() );
|
30 |
}
|
31 |
-
catch ( \Exception $
|
32 |
-
/** @var Traffic\Options $
|
33 |
-
$
|
34 |
$this->getCon()->fireEvent(
|
35 |
'request_limit_exceeded',
|
36 |
[
|
37 |
'audit' => [
|
38 |
-
'count' => $
|
39 |
-
'span' => $
|
40 |
]
|
41 |
]
|
42 |
);
|
28 |
->setMod( $this->getMod() )
|
29 |
->runTest( Services::IP()->getRequestIp() );
|
30 |
}
|
31 |
+
catch ( \Exception $e ) {
|
32 |
+
/** @var Traffic\Options $opts */
|
33 |
+
$opts = $this->getOptions();
|
34 |
$this->getCon()->fireEvent(
|
35 |
'request_limit_exceeded',
|
36 |
[
|
37 |
'audit' => [
|
38 |
+
'count' => $opts->getLimitRequestCount(),
|
39 |
+
'span' => $opts->getLimitTimeSpan(),
|
40 |
]
|
41 |
]
|
42 |
);
|
src/lib/src/Modules/UserManagement/Lib/Password/UserPasswordHandler.php
CHANGED
@@ -219,20 +219,6 @@ class UserPasswordHandler {
|
|
219 |
return true;
|
220 |
}
|
221 |
|
222 |
-
/**
|
223 |
-
* Unused
|
224 |
-
* @return bool
|
225 |
-
* private function verifyApiAccess() {
|
226 |
-
* try {
|
227 |
-
* $this->sendRequestToPwnedRange( 'P@ssw0rd' );
|
228 |
-
* }
|
229 |
-
* catch ( \Exception $oE ) {
|
230 |
-
* return false;
|
231 |
-
* }
|
232 |
-
* return true;
|
233 |
-
* }
|
234 |
-
*/
|
235 |
-
|
236 |
/**
|
237 |
* @param string $password
|
238 |
* @return bool
|
@@ -244,7 +230,7 @@ class UserPasswordHandler {
|
|
244 |
$sPassHash = strtoupper( hash( 'sha1', $password ) );
|
245 |
$sSubHash = substr( $sPassHash, 0, 5 );
|
246 |
|
247 |
-
$
|
248 |
sprintf( '%s/%s', $this->getOptions()->getDef( 'pwned_api_url_password_range' ), $sSubHash ),
|
249 |
[
|
250 |
'headers' => [ 'user-agent' => sprintf( '%s WP Plugin-v%s', 'Shield', $this->getCon()->getVersion() ) ]
|
@@ -253,7 +239,7 @@ class UserPasswordHandler {
|
|
253 |
|
254 |
$sError = '';
|
255 |
$nErrorCode = 2; // Default To Error
|
256 |
-
if ( !$
|
257 |
$sError = 'API request failed';
|
258 |
$nErrorCode = 999; // We don't fail PWNED passwords on failed API requests.
|
259 |
}
|
219 |
return true;
|
220 |
}
|
221 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
222 |
/**
|
223 |
* @param string $password
|
224 |
* @return bool
|
230 |
$sPassHash = strtoupper( hash( 'sha1', $password ) );
|
231 |
$sSubHash = substr( $sPassHash, 0, 5 );
|
232 |
|
233 |
+
$success = $oHttpReq->get(
|
234 |
sprintf( '%s/%s', $this->getOptions()->getDef( 'pwned_api_url_password_range' ), $sSubHash ),
|
235 |
[
|
236 |
'headers' => [ 'user-agent' => sprintf( '%s WP Plugin-v%s', 'Shield', $this->getCon()->getVersion() ) ]
|
239 |
|
240 |
$sError = '';
|
241 |
$nErrorCode = 2; // Default To Error
|
242 |
+
if ( !$success ) {
|
243 |
$sError = 'API request failed';
|
244 |
$nErrorCode = 999; // We don't fail PWNED passwords on failed API requests.
|
245 |
}
|
src/lib/src/Modules/UserManagement/ModCon.php
CHANGED
@@ -61,14 +61,6 @@ class ModCon extends BaseShield\ModCon {
|
|
61 |
$opts->setOpt( 'email_checks', array_unique( $aChecks ) );
|
62 |
}
|
63 |
|
64 |
-
/**
|
65 |
-
* @return bool
|
66 |
-
* @deprecated 10.1
|
67 |
-
*/
|
68 |
-
public function isUserSessionsManagementEnabled() :bool {
|
69 |
-
return $this->isModOptEnabled() && $this->getDbHandler_Sessions()->isReady();
|
70 |
-
}
|
71 |
-
|
72 |
public function isSendUserEmailLoginNotification() :bool {
|
73 |
return $this->isPremium() && $this->getOptions()->isOpt( 'enable_user_login_email_notification', 'Y' );
|
74 |
}
|
61 |
$opts->setOpt( 'email_checks', array_unique( $aChecks ) );
|
62 |
}
|
63 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
64 |
public function isSendUserEmailLoginNotification() :bool {
|
65 |
return $this->isPremium() && $this->getOptions()->isOpt( 'enable_user_login_email_notification', 'Y' );
|
66 |
}
|
src/lib/src/Modules/UserManagement/Options.php
CHANGED
@@ -83,14 +83,6 @@ class Options extends BaseShield\Options {
|
|
83 |
return $this->isOpt( 'pass_prevent_pwned', 'Y' );
|
84 |
}
|
85 |
|
86 |
-
/**
|
87 |
-
* @return bool
|
88 |
-
* @deprecated 10.1
|
89 |
-
*/
|
90 |
-
public function isPassForceUpdateExisting() :bool {
|
91 |
-
return $this->isOpt( 'pass_force_existing', 'Y' );
|
92 |
-
}
|
93 |
-
|
94 |
public function isPasswordPoliciesEnabled() :bool {
|
95 |
return $this->isOpt( 'enable_password_policies', 'Y' )
|
96 |
&& $this->isOptReqsMet( 'enable_password_policies' );
|
83 |
return $this->isOpt( 'pass_prevent_pwned', 'Y' );
|
84 |
}
|
85 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
86 |
public function isPasswordPoliciesEnabled() :bool {
|
87 |
return $this->isOpt( 'enable_password_policies', 'Y' )
|
88 |
&& $this->isOptReqsMet( 'enable_password_policies' );
|
src/lib/src/Scans/Base/BaseScanActionVO.php
CHANGED
@@ -56,11 +56,11 @@ abstract class BaseScanActionVO {
|
|
56 |
*/
|
57 |
public function getScanNamespace() {
|
58 |
try {
|
59 |
-
$
|
60 |
}
|
61 |
-
catch ( \Exception $
|
62 |
-
$
|
63 |
}
|
64 |
-
return rtrim( $
|
65 |
}
|
66 |
}
|
56 |
*/
|
57 |
public function getScanNamespace() {
|
58 |
try {
|
59 |
+
$namespace = ( new \ReflectionClass( $this ) )->getNamespaceName();
|
60 |
}
|
61 |
+
catch ( \Exception $e ) {
|
62 |
+
$namespace = __NAMESPACE__;
|
63 |
}
|
64 |
+
return rtrim( $namespace, '\\' ).'\\';
|
65 |
}
|
66 |
}
|
src/lib/src/Scans/Base/Files/BaseFileMapScan.php
CHANGED
@@ -14,20 +14,20 @@ abstract class BaseFileMapScan extends Base\BaseScan {
|
|
14 |
* @return $this
|
15 |
*/
|
16 |
protected function scanSlice() {
|
17 |
-
/** @var Base\BaseScanActionVO $
|
18 |
-
$
|
19 |
|
20 |
$oTempRs = $this->getScanFromFileMap()
|
21 |
-
->setScanActionVO( $
|
22 |
->run();
|
23 |
|
24 |
-
$
|
25 |
if ( $oTempRs->hasItems() ) {
|
26 |
foreach ( $oTempRs->getAllItems() as $oItem ) {
|
27 |
-
$
|
28 |
}
|
29 |
}
|
30 |
-
$
|
31 |
|
32 |
return $this;
|
33 |
}
|
14 |
* @return $this
|
15 |
*/
|
16 |
protected function scanSlice() {
|
17 |
+
/** @var Base\BaseScanActionVO $action */
|
18 |
+
$action = $this->getScanActionVO();
|
19 |
|
20 |
$oTempRs = $this->getScanFromFileMap()
|
21 |
+
->setScanActionVO( $action )
|
22 |
->run();
|
23 |
|
24 |
+
$newItems = [];
|
25 |
if ( $oTempRs->hasItems() ) {
|
26 |
foreach ( $oTempRs->getAllItems() as $oItem ) {
|
27 |
+
$newItems[] = $oItem->getRawDataAsArray();
|
28 |
}
|
29 |
}
|
30 |
+
$action->results = $newItems;
|
31 |
|
32 |
return $this;
|
33 |
}
|
src/lib/src/Scans/Base/Files/BaseFileScanner.php
CHANGED
@@ -14,8 +14,8 @@ abstract class BaseFileScanner {
|
|
14 |
use Shield\Scans\Common\ScanActionConsumer;
|
15 |
|
16 |
/**
|
17 |
-
* @param string $
|
18 |
* @return Shield\Scans\Base\BaseResultItem|null
|
19 |
*/
|
20 |
-
abstract public function scan( $
|
21 |
}
|
14 |
use Shield\Scans\Common\ScanActionConsumer;
|
15 |
|
16 |
/**
|
17 |
+
* @param string $fullPath
|
18 |
* @return Shield\Scans\Base\BaseResultItem|null
|
19 |
*/
|
20 |
+
abstract public function scan( string $fullPath );
|
21 |
}
|
src/lib/src/Scans/Base/Files/BaseScanFromFileMap.php
CHANGED
@@ -18,20 +18,19 @@ abstract class BaseScanFromFileMap {
|
|
18 |
* @return Scans\Base\BaseResultsSet
|
19 |
*/
|
20 |
public function run() {
|
21 |
-
$
|
22 |
-
$
|
23 |
-
|
24 |
-
if ( is_array( $
|
25 |
-
|
26 |
-
|
27 |
-
|
28 |
-
|
29 |
-
$oResultSet->addItem( $oItem );
|
30 |
}
|
31 |
}
|
32 |
}
|
33 |
|
34 |
-
return $
|
35 |
}
|
36 |
|
37 |
/**
|
18 |
* @return Scans\Base\BaseResultsSet
|
19 |
*/
|
20 |
public function run() {
|
21 |
+
$action = $this->getScanActionVO();
|
22 |
+
$results = $action->getNewResultsSet();
|
23 |
+
|
24 |
+
if ( is_array( $action->items ) ) {
|
25 |
+
foreach ( $action->items as $key => $fullPath ) {
|
26 |
+
$item = $this->getFileScanner()->scan( $fullPath );
|
27 |
+
if ( $item instanceof Scans\Base\BaseResultItem ) {
|
28 |
+
$results->addItem( $item );
|
|
|
29 |
}
|
30 |
}
|
31 |
}
|
32 |
|
33 |
+
return $results;
|
34 |
}
|
35 |
|
36 |
/**
|
src/lib/src/Scans/Base/Table/BaseFileEntryFormatter.php
CHANGED
@@ -13,10 +13,10 @@ abstract class BaseFileEntryFormatter extends BaseEntryFormatter {
|
|
13 |
*/
|
14 |
protected function getBaseData() {
|
15 |
$aData = parent::getBaseData();
|
16 |
-
$
|
17 |
$aData[ 'explanation' ] = $this->getExplanation();
|
18 |
-
$aData[ 'path' ] = $
|
19 |
-
$aData[ 'path_relabs' ] = Services::WpFs()->getPathRelativeToAbsPath( $
|
20 |
$aData[ 'path_details' ] = [];
|
21 |
$aData[ 'created_at' ] = $this->formatTimestampField( $this->getEntryVO()->created_at );
|
22 |
$aData[ 'custom_row' ] = false;
|
13 |
*/
|
14 |
protected function getBaseData() {
|
15 |
$aData = parent::getBaseData();
|
16 |
+
$item = $this->getResultItem();
|
17 |
$aData[ 'explanation' ] = $this->getExplanation();
|
18 |
+
$aData[ 'path' ] = $item->path_fragment;
|
19 |
+
$aData[ 'path_relabs' ] = Services::WpFs()->getPathRelativeToAbsPath( $item->path_full );
|
20 |
$aData[ 'path_details' ] = [];
|
21 |
$aData[ 'created_at' ] = $this->formatTimestampField( $this->getEntryVO()->created_at );
|
22 |
$aData[ 'custom_row' ] = false;
|
src/lib/src/Scans/Base/Utilities/ItemActionHandler.php
CHANGED
@@ -14,29 +14,29 @@ abstract class ItemActionHandler {
|
|
14 |
use HackGuard\Scan\Controller\ScanControllerConsumer;
|
15 |
|
16 |
/**
|
17 |
-
* @param string $
|
18 |
* @return bool
|
19 |
* @throws \Exception
|
20 |
*/
|
21 |
-
public function process( $
|
22 |
-
switch ( $
|
23 |
case 'delete':
|
24 |
-
$
|
25 |
break;
|
26 |
|
27 |
case 'ignore':
|
28 |
-
$
|
29 |
break;
|
30 |
|
31 |
case 'repair':
|
32 |
-
$
|
33 |
break;
|
34 |
|
35 |
default:
|
36 |
throw new \Exception( 'Unsupported Scan Item Action' );
|
37 |
break;
|
38 |
}
|
39 |
-
return $
|
40 |
}
|
41 |
|
42 |
/**
|
14 |
use HackGuard\Scan\Controller\ScanControllerConsumer;
|
15 |
|
16 |
/**
|
17 |
+
* @param string $action
|
18 |
* @return bool
|
19 |
* @throws \Exception
|
20 |
*/
|
21 |
+
public function process( $action ) {
|
22 |
+
switch ( $action ) {
|
23 |
case 'delete':
|
24 |
+
$success = $this->delete();
|
25 |
break;
|
26 |
|
27 |
case 'ignore':
|
28 |
+
$success = $this->ignore();
|
29 |
break;
|
30 |
|
31 |
case 'repair':
|
32 |
+
$success = $this->repair();
|
33 |
break;
|
34 |
|
35 |
default:
|
36 |
throw new \Exception( 'Unsupported Scan Item Action' );
|
37 |
break;
|
38 |
}
|
39 |
+
return $success;
|
40 |
}
|
41 |
|
42 |
/**
|
src/lib/src/Scans/Base/Utilities/ItemActionHandlerAssets.php
CHANGED
@@ -10,19 +10,19 @@ use FernleafSystems\Wordpress\Services\Services;
|
|
10 |
abstract class ItemActionHandlerAssets extends ItemActionHandler {
|
11 |
|
12 |
/**
|
13 |
-
* @param string $
|
14 |
* @return bool
|
15 |
* @throws \Exception
|
16 |
*/
|
17 |
-
public function process( $
|
18 |
-
switch ( $
|
19 |
|
20 |
case 'asset_deactivate':
|
21 |
$bSuccess = $this->assetDeactivate();
|
22 |
break;
|
23 |
|
24 |
default:
|
25 |
-
$bSuccess = parent::process( $
|
26 |
break;
|
27 |
}
|
28 |
|
10 |
abstract class ItemActionHandlerAssets extends ItemActionHandler {
|
11 |
|
12 |
/**
|
13 |
+
* @param string $action
|
14 |
* @return bool
|
15 |
* @throws \Exception
|
16 |
*/
|
17 |
+
public function process( $action ) {
|
18 |
+
switch ( $action ) {
|
19 |
|
20 |
case 'asset_deactivate':
|
21 |
$bSuccess = $this->assetDeactivate();
|
22 |
break;
|
23 |
|
24 |
default:
|
25 |
+
$bSuccess = parent::process( $action );
|
26 |
break;
|
27 |
}
|
28 |
|
src/lib/src/Scans/Helpers/BuildHashesFromDir.php
CHANGED
@@ -41,7 +41,7 @@ class BuildHashesFromDir {
|
|
41 |
$aSnaps[ $sKey ] = hash_file( $sAlgo, $sFullPath );
|
42 |
}
|
43 |
}
|
44 |
-
catch ( \Exception $
|
45 |
}
|
46 |
return $aSnaps;
|
47 |
}
|
41 |
$aSnaps[ $sKey ] = hash_file( $sAlgo, $sFullPath );
|
42 |
}
|
43 |
}
|
44 |
+
catch ( \Exception $e ) {
|
45 |
}
|
46 |
return $aSnaps;
|
47 |
}
|
src/lib/src/Scans/Helpers/WpCoreFile.php
CHANGED
@@ -33,7 +33,7 @@ class WpCoreFile {
|
|
33 |
$oFiles->replaceFileFromVcs( $sPath );
|
34 |
$bSuccess = true;
|
35 |
}
|
36 |
-
catch ( \InvalidArgumentException $
|
37 |
}
|
38 |
}
|
39 |
return $bSuccess;
|
33 |
$oFiles->replaceFileFromVcs( $sPath );
|
34 |
$bSuccess = true;
|
35 |
}
|
36 |
+
catch ( \InvalidArgumentException $e ) {
|
37 |
}
|
38 |
}
|
39 |
return $bSuccess;
|
src/lib/src/Scans/Mal/BuildFileMap.php
CHANGED
@@ -16,72 +16,68 @@ class BuildFileMap {
|
|
16 |
/**
|
17 |
* @return string[]
|
18 |
*/
|
19 |
-
public function build() {
|
20 |
-
$
|
21 |
$this->preBuild();
|
22 |
|
23 |
-
/** @var ScanActionVO $
|
24 |
-
$
|
25 |
|
26 |
-
foreach ( $
|
27 |
try {
|
28 |
-
foreach ( StandardDirectoryIterator::create( $
|
29 |
-
/** @var \SplFileInfo $
|
30 |
-
$
|
31 |
try {
|
32 |
-
if ( !$this->isWhitelistedPath( $
|
33 |
-
$
|
34 |
}
|
35 |
}
|
36 |
-
catch ( \Exception $
|
37 |
}
|
38 |
}
|
39 |
}
|
40 |
-
catch ( \Exception $
|
41 |
error_log(
|
42 |
sprintf( 'Shield file scanner (%s) attempted to read directory (%s) but there was error: "%s".',
|
43 |
-
$
|
44 |
);
|
45 |
}
|
46 |
}
|
47 |
-
return $
|
48 |
}
|
49 |
|
50 |
protected function preBuild() {
|
51 |
-
/** @var ScanActionVO $
|
52 |
-
$
|
53 |
|
54 |
-
if ( empty( $
|
55 |
-
$
|
56 |
ABSPATH => 1,
|
57 |
path_join( ABSPATH, WPINC ) => 0,
|
58 |
path_join( ABSPATH, 'wp-admin' ) => 0,
|
59 |
WP_CONTENT_DIR => 0,
|
60 |
];
|
61 |
}
|
62 |
-
if ( empty( $
|
63 |
-
$
|
64 |
}
|
65 |
-
if ( !is_array( $
|
66 |
-
$
|
67 |
}
|
68 |
}
|
69 |
|
70 |
-
|
71 |
-
|
72 |
-
* @return bool
|
73 |
-
*/
|
74 |
-
private function isWhitelistedPath( $sThePath ) {
|
75 |
-
$bWhitelisted = false;
|
76 |
|
77 |
/** @var ScanActionVO $oAction */
|
78 |
$oAction = $this->getScanActionVO();
|
79 |
foreach ( $oAction->paths_whitelisted as $sWlPath ) {
|
80 |
-
if ( stripos( $
|
81 |
-
$
|
82 |
break;
|
83 |
}
|
84 |
}
|
85 |
-
return $
|
86 |
}
|
87 |
}
|
16 |
/**
|
17 |
* @return string[]
|
18 |
*/
|
19 |
+
public function build() :array {
|
20 |
+
$files = [];
|
21 |
$this->preBuild();
|
22 |
|
23 |
+
/** @var ScanActionVO $action */
|
24 |
+
$action = $this->getScanActionVO();
|
25 |
|
26 |
+
foreach ( $action->scan_root_dirs as $scanDir => $depth ) {
|
27 |
try {
|
28 |
+
foreach ( StandardDirectoryIterator::create( $scanDir, (int)$depth, $action->file_exts, false ) as $item ) {
|
29 |
+
/** @var \SplFileInfo $item */
|
30 |
+
$fullPath = wp_normalize_path( $item->getPathname() );
|
31 |
try {
|
32 |
+
if ( !$this->isWhitelistedPath( $fullPath ) && $item->getSize() > 0 ) {
|
33 |
+
$files[] = $fullPath;
|
34 |
}
|
35 |
}
|
36 |
+
catch ( \Exception $e ) {
|
37 |
}
|
38 |
}
|
39 |
}
|
40 |
+
catch ( \Exception $e ) {
|
41 |
error_log(
|
42 |
sprintf( 'Shield file scanner (%s) attempted to read directory (%s) but there was error: "%s".',
|
43 |
+
$action->scan, $scanDir, $e->getMessage() )
|
44 |
);
|
45 |
}
|
46 |
}
|
47 |
+
return $files;
|
48 |
}
|
49 |
|
50 |
protected function preBuild() {
|
51 |
+
/** @var ScanActionVO $action */
|
52 |
+
$action = $this->getScanActionVO();
|
53 |
|
54 |
+
if ( empty( $action->scan_root_dirs ) || !is_array( $action->scan_root_dirs ) ) {
|
55 |
+
$action->scan_root_dirs = [
|
56 |
ABSPATH => 1,
|
57 |
path_join( ABSPATH, WPINC ) => 0,
|
58 |
path_join( ABSPATH, 'wp-admin' ) => 0,
|
59 |
WP_CONTENT_DIR => 0,
|
60 |
];
|
61 |
}
|
62 |
+
if ( empty( $action->file_exts ) ) {
|
63 |
+
$action->file_exts = [ 'php', 'php5' ];
|
64 |
}
|
65 |
+
if ( !is_array( $action->paths_whitelisted ) ) {
|
66 |
+
$action->paths_whitelisted = [];
|
67 |
}
|
68 |
}
|
69 |
|
70 |
+
private function isWhitelistedPath( string $path ) :bool {
|
71 |
+
$whitelisted = false;
|
|
|
|
|
|
|
|
|
72 |
|
73 |
/** @var ScanActionVO $oAction */
|
74 |
$oAction = $this->getScanActionVO();
|
75 |
foreach ( $oAction->paths_whitelisted as $sWlPath ) {
|
76 |
+
if ( stripos( $path, $sWlPath ) === 0 ) {
|
77 |
+
$whitelisted = true;
|
78 |
break;
|
79 |
}
|
80 |
}
|
81 |
+
return $whitelisted;
|
82 |
}
|
83 |
}
|
src/lib/src/Scans/Mal/FileScanner.php
CHANGED
@@ -16,209 +16,226 @@ use FernleafSystems\Wordpress\Services\Utilities;
|
|
16 |
class FileScanner extends Shield\Scans\Base\Files\BaseFileScanner {
|
17 |
|
18 |
/**
|
19 |
-
* @
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
20 |
* @return ResultItem|null
|
21 |
*/
|
22 |
-
public function scan( $
|
23 |
-
$
|
24 |
|
25 |
-
/** @var ScanActionVO $
|
26 |
-
$
|
27 |
|
28 |
try {
|
29 |
-
$
|
30 |
-
|
31 |
{ // Simple Patterns first
|
32 |
-
$
|
33 |
-
foreach ( $
|
34 |
-
$
|
35 |
-
if ( $
|
36 |
-
return $
|
37 |
}
|
38 |
}
|
39 |
}
|
40 |
|
41 |
-
|
42 |
-
|
43 |
-
|
44 |
-
|
45 |
-
|
46 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
47 |
}
|
48 |
}
|
49 |
}
|
50 |
}
|
51 |
-
catch ( \Exception $
|
52 |
}
|
53 |
|
54 |
-
return $
|
55 |
}
|
56 |
|
57 |
/**
|
58 |
-
* @param
|
59 |
-
* @param string $sSig
|
60 |
* @return ResultItem|null
|
61 |
*/
|
62 |
-
private function scanForSig(
|
63 |
-
$
|
|
|
64 |
|
65 |
-
|
66 |
-
->run();
|
67 |
-
$sFullPath = $oLocator->getPath();
|
68 |
-
if ( !empty( $aLines ) ) {
|
69 |
|
70 |
-
|
71 |
-
|
|
|
|
|
72 |
->setMod( $this->getMod() );
|
73 |
-
foreach ( $
|
74 |
-
$
|
75 |
}
|
76 |
-
$
|
77 |
}
|
78 |
else {
|
79 |
-
/** @var ScanActionVO $
|
80 |
-
$
|
81 |
|
82 |
-
if ( $
|
83 |
-
$
|
84 |
// 1. First check whether the FP of the whole file means we can filter it
|
85 |
$nFalsePositiveConfidence = ( new Shield\Scans\Mal\Utilities\FalsePositiveQuery() )
|
86 |
->setMod( $this->getMod() )
|
87 |
-
->queryPath( $
|
88 |
-
if ( $nFalsePositiveConfidence < $
|
89 |
// 2. Check each line and filter out fp confident lines
|
90 |
$aLineScores = ( new Shield\Scans\Mal\Utilities\FalsePositiveQuery() )
|
91 |
->setMod( $this->getMod() )
|
92 |
-
->queryFileLines( $
|
93 |
-
$
|
94 |
$aLineScores,
|
95 |
-
function ( $
|
96 |
-
return $
|
97 |
}
|
98 |
);
|
99 |
|
100 |
-
if ( empty( $
|
101 |
// Now send False Positive report for entire file based on all file lines being FPs.
|
102 |
( new Shield\Scans\Mal\Utilities\FalsePositiveReporter() )
|
103 |
->setMod( $this->getMod() )
|
104 |
-
->reportPath( $
|
105 |
}
|
106 |
else {
|
107 |
-
$
|
108 |
}
|
109 |
}
|
110 |
}
|
111 |
else {
|
112 |
-
$
|
113 |
}
|
114 |
|
115 |
-
if ( $
|
116 |
-
$
|
117 |
}
|
118 |
}
|
119 |
}
|
120 |
-
return $
|
121 |
}
|
122 |
|
123 |
/**
|
124 |
-
* @param string[] $
|
125 |
-
* @param string $
|
126 |
-
* @param string $
|
127 |
* @return ResultItem
|
128 |
*/
|
129 |
-
private function getResultItemFromLines( $
|
130 |
$oResultItem = new ResultItem();
|
131 |
-
$oResultItem->path_full = wp_normalize_path( $
|
132 |
$oResultItem->path_fragment = str_replace( wp_normalize_path( ABSPATH ), '', $oResultItem->path_full );
|
133 |
$oResultItem->is_mal = true;
|
134 |
-
$oResultItem->mal_sig = base64_encode( $
|
135 |
$oResultItem->fp_confidence = 0;
|
136 |
-
$oResultItem->file_lines = $
|
137 |
return $oResultItem;
|
138 |
}
|
139 |
|
140 |
/**
|
141 |
-
* @param string $
|
142 |
* @return bool
|
143 |
*/
|
144 |
-
private function canExcludeFile( $
|
145 |
-
return $this->isValidCoreFile( $
|
146 |
-
|| $this->isPluginFileValid( $
|
147 |
}
|
148 |
|
149 |
/**
|
150 |
-
* @param string $
|
151 |
* @return bool
|
152 |
*/
|
153 |
-
private function isPluginFileValid( $
|
154 |
-
$
|
155 |
try {
|
156 |
$oPluginFiles = new Utilities\WpOrg\Plugin\Files();
|
157 |
-
$
|
158 |
-
if ( $
|
159 |
-
$
|
160 |
-
$oPluginFiles->verifyFileContents( $
|
161 |
-
: $this->verifyPremiumAssetFile( $
|
162 |
}
|
163 |
}
|
164 |
-
catch ( \Exception $
|
165 |
}
|
166 |
|
167 |
-
return $
|
168 |
}
|
169 |
|
170 |
/**
|
171 |
-
* @param string $
|
172 |
* @return bool
|
173 |
*/
|
174 |
-
private function isThemeFileValid( $
|
175 |
-
$
|
176 |
try {
|
177 |
$oThemeFiles = new Utilities\WpOrg\Theme\Files();
|
178 |
-
$
|
179 |
-
if ( $
|
180 |
-
$
|
181 |
-
$oThemeFiles->verifyFileContents( $
|
182 |
-
: $this->verifyPremiumAssetFile( $
|
183 |
}
|
184 |
}
|
185 |
-
catch ( \Exception $
|
186 |
}
|
187 |
|
188 |
-
return $
|
189 |
}
|
190 |
|
191 |
/**
|
192 |
-
* @param string $
|
193 |
* @param WpPluginVo|WpThemeVo $oPluginOrTheme
|
194 |
* @return bool
|
195 |
* @throws \Exception
|
196 |
*/
|
197 |
-
private function verifyPremiumAssetFile( $
|
198 |
-
$
|
199 |
-
$
|
200 |
->build( $oPluginOrTheme );
|
201 |
-
$
|
202 |
-
if ( !empty( $
|
203 |
-
$
|
204 |
-
->isEqualFileMd5( $
|
205 |
}
|
206 |
-
return $
|
207 |
}
|
208 |
|
209 |
/**
|
210 |
-
* @param string $
|
211 |
* @return bool
|
212 |
*/
|
213 |
-
private function isValidCoreFile( $
|
214 |
-
$
|
215 |
try {
|
216 |
-
$
|
217 |
-
|
218 |
}
|
219 |
-
catch ( \Exception $
|
220 |
-
$
|
221 |
}
|
222 |
-
return $
|
223 |
}
|
224 |
}
|
16 |
class FileScanner extends Shield\Scans\Base\Files\BaseFileScanner {
|
17 |
|
18 |
/**
|
19 |
+
* @var Utilities\File\LocateStrInFile
|
20 |
+
*/
|
21 |
+
private $locator;
|
22 |
+
|
23 |
+
public function __construct() {
|
24 |
+
$this->locator = new Utilities\File\LocateStrInFile();
|
25 |
+
}
|
26 |
+
|
27 |
+
/**
|
28 |
+
* @param string $fullPath
|
29 |
* @return ResultItem|null
|
30 |
*/
|
31 |
+
public function scan( string $fullPath ) {
|
32 |
+
$item = null;
|
33 |
|
34 |
+
/** @var ScanActionVO $action */
|
35 |
+
$action = $this->getScanActionVO();
|
36 |
|
37 |
try {
|
38 |
+
$this->locator->setPath( $fullPath );
|
|
|
39 |
{ // Simple Patterns first
|
40 |
+
$this->locator->setIsRegEx( false );
|
41 |
+
foreach ( $action->patterns_simple as $signature ) {
|
42 |
+
$item = $this->scanForSig( $signature );
|
43 |
+
if ( $item instanceof ResultItem ) {
|
44 |
+
return $item;
|
45 |
}
|
46 |
}
|
47 |
}
|
48 |
|
49 |
+
// RegEx Patterns
|
50 |
+
if ( empty( $action->patterns_fullregex ) ) {
|
51 |
+
$this->locator->setIsRegEx( true );
|
52 |
+
foreach ( $action->patterns_regex as $signature ) {
|
53 |
+
$item = $this->scanForSig( $signature );
|
54 |
+
if ( $item instanceof ResultItem ) {
|
55 |
+
return $item;
|
56 |
+
}
|
57 |
+
}
|
58 |
+
}
|
59 |
+
else { // Full regex patterns
|
60 |
+
$this->locator->setIsRegEx( true );
|
61 |
+
foreach ( $action->patterns_fullregex as $signature ) {
|
62 |
+
$item = $this->scanForSig( $signature );
|
63 |
+
if ( $item instanceof ResultItem ) {
|
64 |
+
return $item;
|
65 |
}
|
66 |
}
|
67 |
}
|
68 |
}
|
69 |
+
catch ( \Exception $e ) {
|
70 |
}
|
71 |
|
72 |
+
return $item;
|
73 |
}
|
74 |
|
75 |
/**
|
76 |
+
* @param string $signature
|
|
|
77 |
* @return ResultItem|null
|
78 |
*/
|
79 |
+
private function scanForSig( string $signature ) {
|
80 |
+
$resultItem = null;
|
81 |
+
$lines = $this->locator->setNeedle( $signature )->run();
|
82 |
|
83 |
+
if ( !empty( $lines ) ) {
|
|
|
|
|
|
|
84 |
|
85 |
+
$fullPath = $this->locator->getPath();
|
86 |
+
|
87 |
+
if ( $this->canExcludeFile( $fullPath ) ) { // we report false positives: file and lines
|
88 |
+
$reporter = ( new Shield\Scans\Mal\Utilities\FalsePositiveReporter() )
|
89 |
->setMod( $this->getMod() );
|
90 |
+
foreach ( $lines as $linNum => $line ) {
|
91 |
+
$reporter->reportLine( $fullPath, $line, true );
|
92 |
}
|
93 |
+
$reporter->reportPath( $fullPath, true );
|
94 |
}
|
95 |
else {
|
96 |
+
/** @var ScanActionVO $action */
|
97 |
+
$action = $this->getScanActionVO();
|
98 |
|
99 |
+
if ( $action->confidence_threshold > 0 ) {
|
100 |
+
$reportItem = false;
|
101 |
// 1. First check whether the FP of the whole file means we can filter it
|
102 |
$nFalsePositiveConfidence = ( new Shield\Scans\Mal\Utilities\FalsePositiveQuery() )
|
103 |
->setMod( $this->getMod() )
|
104 |
+
->queryPath( $fullPath );
|
105 |
+
if ( $nFalsePositiveConfidence < $action->confidence_threshold ) {
|
106 |
// 2. Check each line and filter out fp confident lines
|
107 |
$aLineScores = ( new Shield\Scans\Mal\Utilities\FalsePositiveQuery() )
|
108 |
->setMod( $this->getMod() )
|
109 |
+
->queryFileLines( $fullPath, array_keys( $lines ) );
|
110 |
+
$lines = array_filter(
|
111 |
$aLineScores,
|
112 |
+
function ( $score ) use ( $action ) {
|
113 |
+
return $score < $action->confidence_threshold;
|
114 |
}
|
115 |
);
|
116 |
|
117 |
+
if ( empty( $lines ) ) {
|
118 |
// Now send False Positive report for entire file based on all file lines being FPs.
|
119 |
( new Shield\Scans\Mal\Utilities\FalsePositiveReporter() )
|
120 |
->setMod( $this->getMod() )
|
121 |
+
->reportPath( $fullPath, true );
|
122 |
}
|
123 |
else {
|
124 |
+
$reportItem = true;
|
125 |
}
|
126 |
}
|
127 |
}
|
128 |
else {
|
129 |
+
$reportItem = true;
|
130 |
}
|
131 |
|
132 |
+
if ( $reportItem ) {
|
133 |
+
$resultItem = $this->getResultItemFromLines( array_keys( $lines ), $fullPath, $signature );
|
134 |
}
|
135 |
}
|
136 |
}
|
137 |
+
return $resultItem;
|
138 |
}
|
139 |
|
140 |
/**
|
141 |
+
* @param string[] $lines
|
142 |
+
* @param string $fullPath
|
143 |
+
* @param string $signature
|
144 |
* @return ResultItem
|
145 |
*/
|
146 |
+
private function getResultItemFromLines( array $lines, string $fullPath, string $signature ) :ResultItem {
|
147 |
$oResultItem = new ResultItem();
|
148 |
+
$oResultItem->path_full = wp_normalize_path( $fullPath );
|
149 |
$oResultItem->path_fragment = str_replace( wp_normalize_path( ABSPATH ), '', $oResultItem->path_full );
|
150 |
$oResultItem->is_mal = true;
|
151 |
+
$oResultItem->mal_sig = base64_encode( $signature );
|
152 |
$oResultItem->fp_confidence = 0;
|
153 |
+
$oResultItem->file_lines = $lines;
|
154 |
return $oResultItem;
|
155 |
}
|
156 |
|
157 |
/**
|
158 |
+
* @param string $fullPath - normalized
|
159 |
* @return bool
|
160 |
*/
|
161 |
+
private function canExcludeFile( string $fullPath ) :bool {
|
162 |
+
return $this->isValidCoreFile( $fullPath )
|
163 |
+
|| $this->isPluginFileValid( $fullPath ) || $this->isThemeFileValid( $fullPath );
|
164 |
}
|
165 |
|
166 |
/**
|
167 |
+
* @param string $fullPath - normalized
|
168 |
* @return bool
|
169 |
*/
|
170 |
+
private function isPluginFileValid( string $fullPath ) :bool {
|
171 |
+
$valid = false;
|
172 |
try {
|
173 |
$oPluginFiles = new Utilities\WpOrg\Plugin\Files();
|
174 |
+
$plugin = $oPluginFiles->findPluginFromFile( $fullPath );
|
175 |
+
if ( $plugin instanceof WpPluginVo ) {
|
176 |
+
$valid = $plugin->isWpOrg() ?
|
177 |
+
$oPluginFiles->verifyFileContents( $fullPath )
|
178 |
+
: $this->verifyPremiumAssetFile( $fullPath, $plugin );
|
179 |
}
|
180 |
}
|
181 |
+
catch ( \Exception $e ) {
|
182 |
}
|
183 |
|
184 |
+
return $valid;
|
185 |
}
|
186 |
|
187 |
/**
|
188 |
+
* @param string $fullPath - normalized
|
189 |
* @return bool
|
190 |
*/
|
191 |
+
private function isThemeFileValid( string $fullPath ) :bool {
|
192 |
+
$valid = false;
|
193 |
try {
|
194 |
$oThemeFiles = new Utilities\WpOrg\Theme\Files();
|
195 |
+
$theme = $oThemeFiles->findThemeFromFile( $fullPath );
|
196 |
+
if ( $theme instanceof WpThemeVo ) {
|
197 |
+
$valid = $theme->isWpOrg() ?
|
198 |
+
$oThemeFiles->verifyFileContents( $fullPath )
|
199 |
+
: $this->verifyPremiumAssetFile( $fullPath, $theme );
|
200 |
}
|
201 |
}
|
202 |
+
catch ( \Exception $e ) {
|
203 |
}
|
204 |
|
205 |
+
return $valid;
|
206 |
}
|
207 |
|
208 |
/**
|
209 |
+
* @param string $fullPath
|
210 |
* @param WpPluginVo|WpThemeVo $oPluginOrTheme
|
211 |
* @return bool
|
212 |
* @throws \Exception
|
213 |
*/
|
214 |
+
private function verifyPremiumAssetFile( $fullPath, $oPluginOrTheme ) :bool {
|
215 |
+
$valid = false;
|
216 |
+
$hashes = ( new Lib\Snapshots\Build\BuildHashesFromApi() )
|
217 |
->build( $oPluginOrTheme );
|
218 |
+
$fragment = str_replace( $oPluginOrTheme->getInstallDir(), '', $fullPath );
|
219 |
+
if ( !empty( $hashes ) && !empty( $hashes[ $fragment ] ) ) {
|
220 |
+
$valid = ( new Utilities\File\Compare\CompareHash() )
|
221 |
+
->isEqualFileMd5( $fullPath, $hashes[ $fragment ] );
|
222 |
}
|
223 |
+
return $valid;
|
224 |
}
|
225 |
|
226 |
/**
|
227 |
+
* @param string $fullPath
|
228 |
* @return bool
|
229 |
*/
|
230 |
+
private function isValidCoreFile( $fullPath ) :bool {
|
231 |
+
$hash = Services::CoreFileHashes()->getFileHash( $fullPath );
|
232 |
try {
|
233 |
+
$valid = !empty( $hash )
|
234 |
+
&& ( new Utilities\File\Compare\CompareHash() )->isEqualFileMd5( $fullPath, $hash );
|
235 |
}
|
236 |
+
catch ( \Exception $e ) {
|
237 |
+
$valid = false;
|
238 |
}
|
239 |
+
return $valid;
|
240 |
}
|
241 |
}
|
src/lib/src/Scans/Mal/Scan.php
CHANGED
@@ -17,19 +17,20 @@ class Scan extends Shield\Scans\Base\Files\BaseFileMapScan {
|
|
17 |
protected function preScan() {
|
18 |
parent::preScan();
|
19 |
|
20 |
-
/** @var HackGuard\Options $
|
21 |
-
$
|
22 |
|
23 |
-
/** @var ScanActionVO $
|
24 |
-
$
|
25 |
|
26 |
-
$
|
27 |
|
28 |
-
$
|
29 |
->setMod( $this->getMod() )
|
30 |
->retrieve();
|
31 |
-
$
|
32 |
-
$
|
|
|
33 |
}
|
34 |
|
35 |
/**
|
17 |
protected function preScan() {
|
18 |
parent::preScan();
|
19 |
|
20 |
+
/** @var HackGuard\Options $opts */
|
21 |
+
$opts = $this->getOptions();
|
22 |
|
23 |
+
/** @var ScanActionVO $action */
|
24 |
+
$action = $this->getScanActionVO();
|
25 |
|
26 |
+
$action->confidence_threshold = $opts->getMalConfidenceBoundary();
|
27 |
|
28 |
+
$patterns = ( new Utilities\Patterns() )
|
29 |
->setMod( $this->getMod() )
|
30 |
->retrieve();
|
31 |
+
$action->patterns_simple = $patterns[ 'simple' ];
|
32 |
+
$action->patterns_regex = $patterns[ 'regex' ];
|
33 |
+
$action->patterns_fullregex = $patterns[ 'fullregex' ] ?? [];
|
34 |
}
|
35 |
|
36 |
/**
|
src/lib/src/Scans/Mal/ScanActionVO.php
CHANGED
@@ -1,4 +1,4 @@
|
|
1 |
-
<?php
|
2 |
|
3 |
namespace FernleafSystems\Wordpress\Plugin\Shield\Scans\Mal;
|
4 |
|
@@ -10,6 +10,7 @@ use FernleafSystems\Wordpress\Plugin\Shield\Scans\Base\BaseScanActionVO;
|
|
10 |
* @property string[] $file_exts
|
11 |
* @property string[] $scan_root_dirs
|
12 |
* @property string[] $paths_whitelisted
|
|
|
13 |
* @property string[] $patterns_regex
|
14 |
* @property string[] $patterns_simple
|
15 |
* @property int $confidence_threshold
|
1 |
+
<?php declare( strict_types=1 );
|
2 |
|
3 |
namespace FernleafSystems\Wordpress\Plugin\Shield\Scans\Mal;
|
4 |
|
10 |
* @property string[] $file_exts
|
11 |
* @property string[] $scan_root_dirs
|
12 |
* @property string[] $paths_whitelisted
|
13 |
+
* @property string[] $patterns_fullregex
|
14 |
* @property string[] $patterns_regex
|
15 |
* @property string[] $patterns_simple
|
16 |
* @property int $confidence_threshold
|
src/lib/src/Scans/Mal/Table/EntryFormatter.php
CHANGED
@@ -80,24 +80,24 @@ class EntryFormatter extends BaseFileEntryFormatter {
|
|
80 |
* @inheritDoc
|
81 |
*/
|
82 |
protected function getSupportedActions() {
|
83 |
-
$
|
84 |
|
85 |
-
/** @var Mal\ResultItem $
|
86 |
-
$
|
87 |
|
88 |
try {
|
89 |
$bCanRepair = ( new Mal\Utilities\Repair() )
|
90 |
->setMod( $this->getMod() )
|
91 |
-
->setScanItem( $
|
92 |
->canRepair();
|
93 |
}
|
94 |
-
catch ( \Exception $
|
95 |
$bCanRepair = false;
|
96 |
}
|
97 |
|
98 |
-
$
|
99 |
-
$
|
100 |
|
101 |
-
return $
|
102 |
}
|
103 |
}
|
80 |
* @inheritDoc
|
81 |
*/
|
82 |
protected function getSupportedActions() {
|
83 |
+
$actions = parent::getSupportedActions();
|
84 |
|
85 |
+
/** @var Mal\ResultItem $item */
|
86 |
+
$item = $this->getResultItem();
|
87 |
|
88 |
try {
|
89 |
$bCanRepair = ( new Mal\Utilities\Repair() )
|
90 |
->setMod( $this->getMod() )
|
91 |
+
->setScanItem( $item )
|
92 |
->canRepair();
|
93 |
}
|
94 |
+
catch ( \Exception $e ) {
|
95 |
$bCanRepair = false;
|
96 |
}
|
97 |
|
98 |
+
$actions[] = $bCanRepair ? 'repair' : 'delete';
|
99 |
+
$actions[] = 'download';
|
100 |
|
101 |
+
return $actions;
|
102 |
}
|
103 |
}
|
src/lib/src/Scans/Mal/Utilities/FalsePositiveQuery.php
CHANGED
@@ -15,71 +15,67 @@ class FalsePositiveQuery {
|
|
15 |
use Modules\ModConsumer;
|
16 |
|
17 |
/**
|
18 |
-
* @param string $
|
19 |
* @param int[] $aLines
|
20 |
* @return int[] - key is the file line number, value is the false positive confidence score
|
21 |
*/
|
22 |
-
public function queryFileLines( $
|
23 |
-
$
|
24 |
-
/** @var Modules\HackGuard\Options $
|
25 |
-
$
|
26 |
-
if ( $
|
27 |
try {
|
28 |
-
$aFile = ( new ExtractLinesFromFile() )->run( $
|
29 |
foreach ( $aFile as $nLineNum => $sLine ) {
|
30 |
-
$
|
31 |
}
|
32 |
}
|
33 |
-
catch ( \Exception $
|
34 |
}
|
35 |
}
|
36 |
-
return $
|
37 |
}
|
38 |
|
39 |
-
|
40 |
-
* @param string $sFullPath
|
41 |
-
* @return int
|
42 |
-
*/
|
43 |
-
public function queryPath( $sFullPath ) {
|
44 |
$nFpConfidence = 0;
|
45 |
|
46 |
-
/** @var Modules\HackGuard\Options $
|
47 |
-
$
|
48 |
-
if ( $
|
49 |
-
$
|
50 |
-
|
51 |
-
|
52 |
-
|
53 |
-
$
|
54 |
-
if ( isset( $
|
55 |
-
$nFpConfidence = (int)$
|
56 |
}
|
57 |
}
|
58 |
return $nFpConfidence;
|
59 |
}
|
60 |
|
61 |
/**
|
62 |
-
* @param string $
|
63 |
-
* @param string $
|
64 |
* @return int
|
65 |
*/
|
66 |
-
public function queryLine( $
|
67 |
$nFpConfidence = 0;
|
68 |
|
69 |
/** @var Modules\HackGuard\Options $oOpts */
|
70 |
$oOpts = $this->getOptions();
|
71 |
if ( $oOpts->isMalUseNetworkIntelligence() ) {
|
72 |
-
$
|
73 |
->getModule_License()
|
74 |
->getWpHashesTokenManager()
|
75 |
->getToken();
|
76 |
try {
|
77 |
-
$aData = ( new Malware\Confidence\Retrieve( $
|
78 |
if ( isset( $aData[ 'score' ] ) ) {
|
79 |
$nFpConfidence = (int)$aData[ 'score' ];
|
80 |
}
|
81 |
}
|
82 |
-
catch ( \Exception $
|
83 |
}
|
84 |
}
|
85 |
return $nFpConfidence;
|
15 |
use Modules\ModConsumer;
|
16 |
|
17 |
/**
|
18 |
+
* @param string $fullPath
|
19 |
* @param int[] $aLines
|
20 |
* @return int[] - key is the file line number, value is the false positive confidence score
|
21 |
*/
|
22 |
+
public function queryFileLines( $fullPath, $aLines ) {
|
23 |
+
$scores = [];
|
24 |
+
/** @var Modules\HackGuard\Options $opts */
|
25 |
+
$opts = $this->getOptions();
|
26 |
+
if ( $opts->isMalUseNetworkIntelligence() ) {
|
27 |
try {
|
28 |
+
$aFile = ( new ExtractLinesFromFile() )->run( $fullPath, $aLines );
|
29 |
foreach ( $aFile as $nLineNum => $sLine ) {
|
30 |
+
$scores[ $nLineNum ] = $this->queryLine( $fullPath, $sLine );
|
31 |
}
|
32 |
}
|
33 |
+
catch ( \Exception $e ) {
|
34 |
}
|
35 |
}
|
36 |
+
return $scores;
|
37 |
}
|
38 |
|
39 |
+
public function queryPath( string $fullPath ) :int {
|
|
|
|
|
|
|
|
|
40 |
$nFpConfidence = 0;
|
41 |
|
42 |
+
/** @var Modules\HackGuard\Options $opts */
|
43 |
+
$opts = $this->getOptions();
|
44 |
+
if ( $opts->isMalUseNetworkIntelligence() ) {
|
45 |
+
$apiToken = $this->getCon()
|
46 |
+
->getModule_License()
|
47 |
+
->getWpHashesTokenManager()
|
48 |
+
->getToken();
|
49 |
+
$data = ( new Malware\Confidence\Retrieve( $apiToken ) )->retrieveForFile( $fullPath );
|
50 |
+
if ( isset( $data[ 'score' ] ) ) {
|
51 |
+
$nFpConfidence = (int)$data[ 'score' ];
|
52 |
}
|
53 |
}
|
54 |
return $nFpConfidence;
|
55 |
}
|
56 |
|
57 |
/**
|
58 |
+
* @param string $file - path to file containing line
|
59 |
+
* @param string $line
|
60 |
* @return int
|
61 |
*/
|
62 |
+
public function queryLine( $file, $line ) {
|
63 |
$nFpConfidence = 0;
|
64 |
|
65 |
/** @var Modules\HackGuard\Options $oOpts */
|
66 |
$oOpts = $this->getOptions();
|
67 |
if ( $oOpts->isMalUseNetworkIntelligence() ) {
|
68 |
+
$token = $this->getCon()
|
69 |
->getModule_License()
|
70 |
->getWpHashesTokenManager()
|
71 |
->getToken();
|
72 |
try {
|
73 |
+
$aData = ( new Malware\Confidence\Retrieve( $token ) )->retrieveForFileLine( $file, $line );
|
74 |
if ( isset( $aData[ 'score' ] ) ) {
|
75 |
$nFpConfidence = (int)$aData[ 'score' ];
|
76 |
}
|
77 |
}
|
78 |
+
catch ( \Exception $e ) {
|
79 |
}
|
80 |
}
|
81 |
return $nFpConfidence;
|
src/lib/src/Scans/Mal/Utilities/FalsePositiveReporter.php
CHANGED
@@ -106,7 +106,7 @@ class FalsePositiveReporter {
|
|
106 |
->report( $sFile, $sLine, $bIsFalsePositive );
|
107 |
}
|
108 |
}
|
109 |
-
catch ( \Exception $
|
110 |
}
|
111 |
}
|
112 |
$this->updateReportedCache( $sReportHash );
|
106 |
->report( $sFile, $sLine, $bIsFalsePositive );
|
107 |
}
|
108 |
}
|
109 |
+
catch ( \Exception $e ) {
|
110 |
}
|
111 |
}
|
112 |
$this->updateReportedCache( $sReportHash );
|
src/lib/src/Scans/Mal/Utilities/Patterns.php
CHANGED
@@ -21,40 +21,40 @@ class Patterns {
|
|
21 |
/** @var Modules\HackGuard\ModCon $mod */
|
22 |
$mod = $this->getMod();
|
23 |
|
24 |
-
$
|
25 |
-
$
|
26 |
-
if ( !empty( $
|
27 |
-
$
|
28 |
-
$
|
29 |
( new Cache\LoadFromCache() )
|
30 |
-
->setCacheDef( $
|
31 |
->load();
|
32 |
}
|
33 |
|
34 |
-
if ( empty( $
|
35 |
-
$
|
36 |
-
|
37 |
-
|
38 |
-
|
39 |
// First attempt to download from WP Hashes API.
|
40 |
-
$
|
41 |
|
42 |
// Fallback to original method
|
43 |
-
if ( !is_array( $
|
44 |
-
/** @var Modules\HackGuard\Options $
|
45 |
-
$
|
46 |
-
$
|
47 |
-
'simple' => $
|
48 |
-
'regex' => $
|
49 |
];
|
50 |
}
|
51 |
|
52 |
-
$
|
53 |
( new Cache\StoreToCache() )
|
54 |
-
->setCacheDef( $
|
55 |
->store();
|
56 |
}
|
57 |
|
58 |
-
return $
|
59 |
}
|
60 |
}
|
21 |
/** @var Modules\HackGuard\ModCon $mod */
|
22 |
$mod = $this->getMod();
|
23 |
|
24 |
+
$cacher = new Cache\CacheDefVO();
|
25 |
+
$cacher->dir = $mod->getTempDir();
|
26 |
+
if ( !empty( $cacher->dir ) ) {
|
27 |
+
$cacher->file_fragment = 'cache_patterns.txt';
|
28 |
+
$cacher->expiration = HOUR_IN_SECONDS;
|
29 |
( new Cache\LoadFromCache() )
|
30 |
+
->setCacheDef( $cacher )
|
31 |
->load();
|
32 |
}
|
33 |
|
34 |
+
if ( empty( $cacher->data ) ) {
|
35 |
+
$token = $this->getCon()
|
36 |
+
->getModule_License()
|
37 |
+
->getWpHashesTokenManager()
|
38 |
+
->getToken();
|
39 |
// First attempt to download from WP Hashes API.
|
40 |
+
$patterns = ( new Malware\Patterns\Retrieve( $token ) )->getPatterns();
|
41 |
|
42 |
// Fallback to original method
|
43 |
+
if ( !is_array( $patterns ) || empty( $patterns[ 'simple' ] ) || empty( $patterns[ 'regex' ] ) ) {
|
44 |
+
/** @var Modules\HackGuard\Options $opts */
|
45 |
+
$opts = $this->getOptions();
|
46 |
+
$patterns = [
|
47 |
+
'simple' => $opts->getMalSignaturesSimple(),
|
48 |
+
'regex' => $opts->getMalSignaturesRegex(),
|
49 |
];
|
50 |
}
|
51 |
|
52 |
+
$cacher->data = $patterns;
|
53 |
( new Cache\StoreToCache() )
|
54 |
+
->setCacheDef( $cacher )
|
55 |
->store();
|
56 |
}
|
57 |
|
58 |
+
return $cacher->data;
|
59 |
}
|
60 |
}
|
src/lib/src/Scans/Mal/Utilities/Repair.php
CHANGED
@@ -19,62 +19,62 @@ class Repair extends Shield\Scans\Base\Utilities\BaseRepair {
|
|
19 |
* @return bool
|
20 |
*/
|
21 |
public function repairItem() {
|
22 |
-
/** @var ResultItem $
|
23 |
-
$
|
24 |
-
/** @var Shield\Modules\HackGuard\Options $
|
25 |
-
$
|
26 |
-
$
|
27 |
|
28 |
try {
|
29 |
-
$
|
30 |
}
|
31 |
catch ( \Exception $e ) {
|
32 |
-
$
|
33 |
}
|
34 |
|
35 |
-
if ( $
|
36 |
|
37 |
-
if ( Services\Services::CoreFileHashes()->isCoreFile( $
|
38 |
-
$
|
39 |
}
|
40 |
else {
|
41 |
-
$
|
42 |
-
if ( $
|
43 |
|
44 |
-
$
|
45 |
}
|
46 |
else {
|
47 |
-
$
|
48 |
-
if ( $
|
49 |
|
50 |
-
$
|
51 |
}
|
52 |
-
elseif ( $
|
53 |
-
$
|
54 |
}
|
55 |
}
|
56 |
}
|
57 |
}
|
58 |
elseif ( $this->isAllowDelete() ) {
|
59 |
-
$
|
60 |
}
|
61 |
|
62 |
-
if ( $
|
63 |
// 1) Report the file as being malware.
|
64 |
( new Shield\Scans\Mal\Utilities\FalsePositiveReporter() )
|
65 |
->setMod( $this->getMod() )
|
66 |
-
->reportResultItem( $
|
67 |
}
|
68 |
|
69 |
-
return $
|
70 |
}
|
71 |
|
72 |
/**
|
73 |
-
* @param ResultItem $
|
74 |
* @return bool
|
75 |
*/
|
76 |
-
private function repairItemByDelete( $
|
77 |
-
return Services\Services::WpFs()->deleteFile( $
|
78 |
}
|
79 |
|
80 |
/**
|
@@ -151,7 +151,7 @@ class Repair extends Shield\Scans\Base\Utilities\BaseRepair {
|
|
151 |
try {
|
152 |
$bSuccess = $oFiles->replaceFileFromVcs( $oItem->path_fragment );
|
153 |
}
|
154 |
-
catch ( \InvalidArgumentException $
|
155 |
$bSuccess = false;
|
156 |
}
|
157 |
return $bSuccess;
|
@@ -168,7 +168,7 @@ class Repair extends Shield\Scans\Base\Utilities\BaseRepair {
|
|
168 |
( new Services\Utilities\File\RemoveLineFromFile() )->run( $oItem->path_full, $nLine );
|
169 |
$bSuccess = true;
|
170 |
}
|
171 |
-
catch ( \Exception $
|
172 |
$bSuccess = false;
|
173 |
break;
|
174 |
}
|
@@ -181,21 +181,21 @@ class Repair extends Shield\Scans\Base\Utilities\BaseRepair {
|
|
181 |
* @return bool
|
182 |
*/
|
183 |
private function repairItemInPlugin( $oItem ) {
|
184 |
-
$
|
185 |
|
186 |
$oFiles = new WpOrg\Plugin\Files();
|
187 |
try {
|
188 |
if ( $oFiles->isValidFileFromPlugin( $oItem->path_full ) ) {
|
189 |
-
$
|
190 |
}
|
191 |
elseif ( $this->isAllowDelete() ) {
|
192 |
-
$
|
193 |
}
|
194 |
}
|
195 |
-
catch ( \InvalidArgumentException $
|
196 |
}
|
197 |
|
198 |
-
return $
|
199 |
}
|
200 |
|
201 |
/**
|
@@ -203,20 +203,20 @@ class Repair extends Shield\Scans\Base\Utilities\BaseRepair {
|
|
203 |
* @return bool
|
204 |
*/
|
205 |
private function repairItemInTheme( $oItem ) {
|
206 |
-
$
|
207 |
|
208 |
$oFiles = new WpOrg\Theme\Files();
|
209 |
try {
|
210 |
if ( $oFiles->isValidFileFromTheme( $oItem->path_full ) ) {
|
211 |
-
$
|
212 |
}
|
213 |
elseif ( $this->isAllowDelete() ) {
|
214 |
-
$
|
215 |
}
|
216 |
}
|
217 |
-
catch ( \InvalidArgumentException $
|
218 |
}
|
219 |
|
220 |
-
return $
|
221 |
}
|
222 |
}
|
19 |
* @return bool
|
20 |
*/
|
21 |
public function repairItem() {
|
22 |
+
/** @var ResultItem $item */
|
23 |
+
$item = $this->getScanItem();
|
24 |
+
/** @var Shield\Modules\HackGuard\Options $opts */
|
25 |
+
$opts = $this->getOptions();
|
26 |
+
$success = false;
|
27 |
|
28 |
try {
|
29 |
+
$canRepair = $this->canRepair();
|
30 |
}
|
31 |
catch ( \Exception $e ) {
|
32 |
+
$canRepair = false;
|
33 |
}
|
34 |
|
35 |
+
if ( $canRepair ) {
|
36 |
|
37 |
+
if ( Services\Services::CoreFileHashes()->isCoreFile( $item->path_fragment ) ) {
|
38 |
+
$success = $this->repairCoreItem( $item );
|
39 |
}
|
40 |
else {
|
41 |
+
$plugin = ( new WpOrg\Plugin\Files() )->findPluginFromFile( $item->path_full );
|
42 |
+
if ( $plugin instanceof Services\Core\VOs\WpPluginVo && $plugin->isWpOrg() ) {
|
43 |
|
44 |
+
$success = $this->repairItemInPlugin( $item );
|
45 |
}
|
46 |
else {
|
47 |
+
$theme = ( new WpOrg\Theme\Files() )->findThemeFromFile( $item->path_full );
|
48 |
+
if ( $theme instanceof Services\Core\VOs\WpThemeVo && $theme->isWpOrg() ) {
|
49 |
|
50 |
+
$success = $this->repairItemInTheme( $item );
|
51 |
}
|
52 |
+
elseif ( $opts->isMalAutoRepairSurgical() ) {
|
53 |
+
$success = $this->repairSurgicalItem( $item );
|
54 |
}
|
55 |
}
|
56 |
}
|
57 |
}
|
58 |
elseif ( $this->isAllowDelete() ) {
|
59 |
+
$success = $this->repairItemByDelete( $item );
|
60 |
}
|
61 |
|
62 |
+
if ( $success ) {
|
63 |
// 1) Report the file as being malware.
|
64 |
( new Shield\Scans\Mal\Utilities\FalsePositiveReporter() )
|
65 |
->setMod( $this->getMod() )
|
66 |
+
->reportResultItem( $item, false );
|
67 |
}
|
68 |
|
69 |
+
return $success;
|
70 |
}
|
71 |
|
72 |
/**
|
73 |
+
* @param ResultItem $item
|
74 |
* @return bool
|
75 |
*/
|
76 |
+
private function repairItemByDelete( $item ) {
|
77 |
+
return Services\Services::WpFs()->deleteFile( $item->path_full );
|
78 |
}
|
79 |
|
80 |
/**
|
151 |
try {
|
152 |
$bSuccess = $oFiles->replaceFileFromVcs( $oItem->path_fragment );
|
153 |
}
|
154 |
+
catch ( \InvalidArgumentException $e ) {
|
155 |
$bSuccess = false;
|
156 |
}
|
157 |
return $bSuccess;
|
168 |
( new Services\Utilities\File\RemoveLineFromFile() )->run( $oItem->path_full, $nLine );
|
169 |
$bSuccess = true;
|
170 |
}
|
171 |
+
catch ( \Exception $e ) {
|
172 |
$bSuccess = false;
|
173 |
break;
|
174 |
}
|
181 |
* @return bool
|
182 |
*/
|
183 |
private function repairItemInPlugin( $oItem ) {
|
184 |
+
$success = false;
|
185 |
|
186 |
$oFiles = new WpOrg\Plugin\Files();
|
187 |
try {
|
188 |
if ( $oFiles->isValidFileFromPlugin( $oItem->path_full ) ) {
|
189 |
+
$success = $oFiles->replaceFileFromVcs( $oItem->path_full );
|
190 |
}
|
191 |
elseif ( $this->isAllowDelete() ) {
|
192 |
+
$success = (bool)Services\Services::WpFs()->deleteFile( $oItem->path_full );
|
193 |
}
|
194 |
}
|
195 |
+
catch ( \InvalidArgumentException $e ) {
|
196 |
}
|
197 |
|
198 |
+
return $success;
|
199 |
}
|
200 |
|
201 |
/**
|
203 |
* @return bool
|
204 |
*/
|
205 |
private function repairItemInTheme( $oItem ) {
|
206 |
+
$success = false;
|
207 |
|
208 |
$oFiles = new WpOrg\Theme\Files();
|
209 |
try {
|
210 |
if ( $oFiles->isValidFileFromTheme( $oItem->path_full ) ) {
|
211 |
+
$success = $oFiles->replaceFileFromVcs( $oItem->path_full );
|
212 |
}
|
213 |
elseif ( $this->isAllowDelete() ) {
|
214 |
+
$success = (bool)Services\Services::WpFs()->deleteFile( $oItem->path_full );
|
215 |
}
|
216 |
}
|
217 |
+
catch ( \InvalidArgumentException $e ) {
|
218 |
}
|
219 |
|
220 |
+
return $success;
|
221 |
}
|
222 |
}
|
src/lib/src/Scans/Ptg/BuildFileMap.php
CHANGED
@@ -33,14 +33,14 @@ class BuildFileMap {
|
|
33 |
$aFiles[] = str_replace( $sAbsPath, '', wp_normalize_path( $oFsItem->getPathname() ) );
|
34 |
}
|
35 |
}
|
36 |
-
catch ( \Exception $
|
37 |
}
|
38 |
}
|
39 |
}
|
40 |
-
catch ( \Exception $
|
41 |
error_log(
|
42 |
sprintf( 'Shield file scanner (%s) attempted to read directory (%s) but there was error: "%s".',
|
43 |
-
$oAction->scan, $sScanDir, $
|
44 |
);
|
45 |
}
|
46 |
}
|
33 |
$aFiles[] = str_replace( $sAbsPath, '', wp_normalize_path( $oFsItem->getPathname() ) );
|
34 |
}
|
35 |
}
|
36 |
+
catch ( \Exception $e ) {
|
37 |
}
|
38 |
}
|
39 |
}
|
40 |
+
catch ( \Exception $e ) {
|
41 |
error_log(
|
42 |
sprintf( 'Shield file scanner (%s) attempted to read directory (%s) but there was error: "%s".',
|
43 |
+
$oAction->scan, $sScanDir, $e->getMessage() )
|
44 |
);
|
45 |
}
|
46 |
}
|
src/lib/src/Scans/Ptg/FileScanner.php
CHANGED
@@ -21,40 +21,40 @@ class FileScanner extends Shield\Scans\Base\Files\BaseFileScanner {
|
|
21 |
private $oAssetStore;
|
22 |
|
23 |
/**
|
24 |
-
* @param string $
|
25 |
* @return ResultItem|null
|
26 |
*/
|
27 |
-
public function scan( $
|
28 |
-
$
|
29 |
// file paths are stored in the queue relatives to ABSPATH
|
30 |
-
$
|
31 |
try {
|
32 |
-
$
|
33 |
-
if ( empty( $
|
34 |
-
$
|
35 |
}
|
36 |
-
if ( empty( $
|
37 |
-
throw new \Exception( 'Could not load asset' );
|
38 |
}
|
39 |
|
40 |
-
$
|
41 |
-
$
|
42 |
-
if ( empty( $
|
43 |
-
$
|
44 |
-
$
|
45 |
-
$
|
46 |
}
|
47 |
-
elseif ( !( new CompareHash() )->isEqualFileMd5( $
|
48 |
-
$
|
49 |
-
$
|
50 |
-
$
|
51 |
}
|
52 |
}
|
53 |
-
catch ( \Exception $
|
54 |
-
error_log( $
|
55 |
}
|
56 |
|
57 |
-
return $
|
58 |
}
|
59 |
|
60 |
/**
|
21 |
private $oAssetStore;
|
22 |
|
23 |
/**
|
24 |
+
* @param string $fullPath - in this case it's relative to ABSPATH
|
25 |
* @return ResultItem|null
|
26 |
*/
|
27 |
+
public function scan( string $fullPath ) {
|
28 |
+
$item = null;
|
29 |
// file paths are stored in the queue relatives to ABSPATH
|
30 |
+
$fullPath = path_join( wp_normalize_path( ABSPATH ), $fullPath );
|
31 |
try {
|
32 |
+
$asset = ( new Plugin\Files() )->findPluginFromFile( $fullPath );
|
33 |
+
if ( empty( $asset ) ) {
|
34 |
+
$asset = ( new Theme\Files() )->findThemeFromFile( $fullPath );
|
35 |
}
|
36 |
+
if ( empty( $asset ) ) {
|
37 |
+
throw new \Exception( sprintf( 'Could not load asset for: %s', $fullPath ) );
|
38 |
}
|
39 |
|
40 |
+
$assetHashes = $this->getHashes( $asset );
|
41 |
+
$pathFragment = str_replace( $asset->getInstallDir(), '', $fullPath );
|
42 |
+
if ( empty( $assetHashes[ $pathFragment ] ) ) {
|
43 |
+
$item = $this->getNewItem( $asset, $fullPath );
|
44 |
+
$item->path_fragment = $pathFragment;
|
45 |
+
$item->is_unrecognised = true;
|
46 |
}
|
47 |
+
elseif ( !( new CompareHash() )->isEqualFileMd5( $fullPath, $assetHashes[ $pathFragment ] ) ) {
|
48 |
+
$item = $this->getNewItem( $asset, $fullPath );
|
49 |
+
$item->path_fragment = $pathFragment;
|
50 |
+
$item->is_different = true;
|
51 |
}
|
52 |
}
|
53 |
+
catch ( \Exception $e ) {
|
54 |
+
error_log( $e->getMessage() );
|
55 |
}
|
56 |
|
57 |
+
return $item;
|
58 |
}
|
59 |
|
60 |
/**
|
src/lib/src/Scans/Ptg/Utilities/ItemActionHandler.php
CHANGED
@@ -11,12 +11,12 @@ use FernleafSystems\Wordpress\Services\Services;
|
|
11 |
class ItemActionHandler extends Base\Utilities\ItemActionHandlerAssets {
|
12 |
|
13 |
/**
|
14 |
-
* @param string $
|
15 |
* @return bool
|
16 |
* @throws \Exception
|
17 |
*/
|
18 |
-
public function process( $
|
19 |
-
switch ( $
|
20 |
|
21 |
case 'asset_accept':
|
22 |
$bSuccess = $this->assetAccept();
|
@@ -27,7 +27,7 @@ class ItemActionHandler extends Base\Utilities\ItemActionHandlerAssets {
|
|
27 |
break;
|
28 |
|
29 |
default:
|
30 |
-
$bSuccess = parent::process( $
|
31 |
break;
|
32 |
}
|
33 |
|
@@ -64,38 +64,38 @@ class ItemActionHandler extends Base\Utilities\ItemActionHandlerAssets {
|
|
64 |
* @throws \Exception
|
65 |
*/
|
66 |
protected function assetReinstall() {
|
67 |
-
/** @var ResultItem $
|
68 |
-
$
|
69 |
|
70 |
-
$
|
71 |
|
72 |
-
$
|
73 |
-
$
|
74 |
-
if ( $
|
75 |
-
$
|
76 |
-
if ( $
|
77 |
-
$
|
78 |
}
|
79 |
}
|
80 |
-
elseif ( $
|
81 |
-
$
|
82 |
-
if ( $
|
83 |
-
$
|
84 |
}
|
85 |
}
|
86 |
|
87 |
-
if ( $
|
88 |
try {
|
89 |
( new Snapshots\StoreAction\Build() )
|
90 |
->setMod( $this->getMod() )
|
91 |
-
->setAsset( $this->getAssetFromSlug( $
|
92 |
->run();
|
93 |
}
|
94 |
-
catch ( \Exception $
|
95 |
}
|
96 |
}
|
97 |
|
98 |
-
return $
|
99 |
}
|
100 |
|
101 |
/**
|
11 |
class ItemActionHandler extends Base\Utilities\ItemActionHandlerAssets {
|
12 |
|
13 |
/**
|
14 |
+
* @param string $action
|
15 |
* @return bool
|
16 |
* @throws \Exception
|
17 |
*/
|
18 |
+
public function process( $action ) {
|
19 |
+
switch ( $action ) {
|
20 |
|
21 |
case 'asset_accept':
|
22 |
$bSuccess = $this->assetAccept();
|
27 |
break;
|
28 |
|
29 |
default:
|
30 |
+
$bSuccess = parent::process( $action );
|
31 |
break;
|
32 |
}
|
33 |
|
64 |
* @throws \Exception
|
65 |
*/
|
66 |
protected function assetReinstall() {
|
67 |
+
/** @var ResultItem $item */
|
68 |
+
$item = $this->getScanItem();
|
69 |
|
70 |
+
$success = false;
|
71 |
|
72 |
+
$WPP = Services::WpPlugins();
|
73 |
+
$WPT = Services::WpThemes();
|
74 |
+
if ( $WPP->isInstalled( $item->slug ) ) {
|
75 |
+
$asset = $WPP->getPluginAsVo( $item->slug );
|
76 |
+
if ( $asset->isWpOrg() ) {
|
77 |
+
$success = $WPP->reinstall( $item->slug );
|
78 |
}
|
79 |
}
|
80 |
+
elseif ( $WPT->isInstalled( $item->slug ) ) {
|
81 |
+
$asset = $WPT->getThemeAsVo( $item->slug );
|
82 |
+
if ( $asset->isWpOrg() ) {
|
83 |
+
$success = $WPT->reinstall( $item->slug );
|
84 |
}
|
85 |
}
|
86 |
|
87 |
+
if ( $success ) {
|
88 |
try {
|
89 |
( new Snapshots\StoreAction\Build() )
|
90 |
->setMod( $this->getMod() )
|
91 |
+
->setAsset( $this->getAssetFromSlug( $item->slug ) )
|
92 |
->run();
|
93 |
}
|
94 |
+
catch ( \Exception $e ) {
|
95 |
}
|
96 |
}
|
97 |
|
98 |
+
return $success;
|
99 |
}
|
100 |
|
101 |
/**
|
src/lib/src/Scans/Ptg/Utilities/Repair.php
CHANGED
@@ -42,19 +42,19 @@ class Repair extends Scans\Base\Utilities\BaseRepair {
|
|
42 |
* @return bool
|
43 |
*/
|
44 |
private function repairPluginFile( $sPath ) {
|
45 |
-
$
|
46 |
$oFiles = new WpOrg\Plugin\Files();
|
47 |
try {
|
48 |
if ( $oFiles->isValidFileFromPlugin( $sPath ) ) {
|
49 |
-
$
|
50 |
}
|
51 |
elseif ( $this->isAllowDelete() ) {
|
52 |
-
$
|
53 |
}
|
54 |
}
|
55 |
-
catch ( \InvalidArgumentException $
|
56 |
}
|
57 |
-
return (bool)$
|
58 |
}
|
59 |
|
60 |
/**
|
@@ -62,19 +62,19 @@ class Repair extends Scans\Base\Utilities\BaseRepair {
|
|
62 |
* @return bool
|
63 |
*/
|
64 |
private function repairThemeFile( $sPath ) {
|
65 |
-
$
|
66 |
$oFiles = new WpOrg\Theme\Files();
|
67 |
try {
|
68 |
if ( $oFiles->isValidFileFromTheme( $sPath ) ) {
|
69 |
-
$
|
70 |
}
|
71 |
elseif ( $this->isAllowDelete() ) {
|
72 |
-
$
|
73 |
}
|
74 |
}
|
75 |
-
catch ( \InvalidArgumentException $
|
76 |
}
|
77 |
-
return (bool)$
|
78 |
}
|
79 |
|
80 |
/**
|
42 |
* @return bool
|
43 |
*/
|
44 |
private function repairPluginFile( $sPath ) {
|
45 |
+
$success = false;
|
46 |
$oFiles = new WpOrg\Plugin\Files();
|
47 |
try {
|
48 |
if ( $oFiles->isValidFileFromPlugin( $sPath ) ) {
|
49 |
+
$success = $oFiles->replaceFileFromVcs( $sPath );
|
50 |
}
|
51 |
elseif ( $this->isAllowDelete() ) {
|
52 |
+
$success = (bool)Services::WpFs()->deleteFile( $sPath );
|
53 |
}
|
54 |
}
|
55 |
+
catch ( \InvalidArgumentException $e ) {
|
56 |
}
|
57 |
+
return (bool)$success;
|
58 |
}
|
59 |
|
60 |
/**
|
62 |
* @return bool
|
63 |
*/
|
64 |
private function repairThemeFile( $sPath ) {
|
65 |
+
$success = false;
|
66 |
$oFiles = new WpOrg\Theme\Files();
|
67 |
try {
|
68 |
if ( $oFiles->isValidFileFromTheme( $sPath ) ) {
|
69 |
+
$success = $oFiles->replaceFileFromVcs( $sPath );
|
70 |
}
|
71 |
elseif ( $this->isAllowDelete() ) {
|
72 |
+
$success = (bool)Services::WpFs()->deleteFile( $sPath );
|
73 |
}
|
74 |
}
|
75 |
+
catch ( \InvalidArgumentException $e ) {
|
76 |
}
|
77 |
+
return (bool)$success;
|
78 |
}
|
79 |
|
80 |
/**
|
src/lib/src/Scans/Ufc/BuildFileMap.php
CHANGED
@@ -44,10 +44,10 @@ class BuildFileMap {
|
|
44 |
}
|
45 |
}
|
46 |
}
|
47 |
-
catch ( \Exception $
|
48 |
error_log(
|
49 |
sprintf( 'Shield file scanner (%s) attempted to read directory (%s) but there was error: "%s".',
|
50 |
-
$oAction->scan, $sScanDir, $
|
51 |
);
|
52 |
}
|
53 |
}
|
44 |
}
|
45 |
}
|
46 |
}
|
47 |
+
catch ( \Exception $e ) {
|
48 |
error_log(
|
49 |
sprintf( 'Shield file scanner (%s) attempted to read directory (%s) but there was error: "%s".',
|
50 |
+
$oAction->scan, $sScanDir, $e->getMessage() )
|
51 |
);
|
52 |
}
|
53 |
}
|
src/lib/src/Scans/Ufc/FileScanner.php
CHANGED
@@ -12,21 +12,21 @@ use FernleafSystems\Wordpress\Services\Services;
|
|
12 |
class FileScanner extends Shield\Scans\Base\Files\BaseFileScanner {
|
13 |
|
14 |
/**
|
15 |
-
* @param string $
|
16 |
* @return ResultItem|null
|
17 |
*/
|
18 |
-
public function scan( $
|
19 |
-
$
|
20 |
|
21 |
-
$
|
22 |
-
if ( !$this->isExcluded( $
|
23 |
-
/** @var ResultItem $
|
24 |
-
$
|
25 |
-
$
|
26 |
-
$
|
27 |
}
|
28 |
|
29 |
-
return $
|
30 |
}
|
31 |
|
32 |
/**
|
12 |
class FileScanner extends Shield\Scans\Base\Files\BaseFileScanner {
|
13 |
|
14 |
/**
|
15 |
+
* @param string $fullPath
|
16 |
* @return ResultItem|null
|
17 |
*/
|
18 |
+
public function scan( string $fullPath ) {
|
19 |
+
$item = null;
|
20 |
|
21 |
+
$fullPath = wp_normalize_path( $fullPath );
|
22 |
+
if ( !$this->isExcluded( $fullPath ) ) {
|
23 |
+
/** @var ResultItem $item */
|
24 |
+
$item = $this->getScanActionVO()->getNewResultItem();
|
25 |
+
$item->path_full = $fullPath;
|
26 |
+
$item->path_fragment = Services::CoreFileHashes()->getFileFragment( $fullPath );
|
27 |
}
|
28 |
|
29 |
+
return $item;
|
30 |
}
|
31 |
|
32 |
/**
|
src/lib/src/Scans/Wcf/FileScanner.php
CHANGED
@@ -13,17 +13,17 @@ use FernleafSystems\Wordpress\Services\Utilities\File\Compare\CompareHash;
|
|
13 |
class FileScanner extends Shield\Scans\Base\Files\BaseFileScanner {
|
14 |
|
15 |
/**
|
16 |
-
* @param string $
|
17 |
* @return ResultItem|null
|
18 |
*/
|
19 |
-
public function scan( $
|
20 |
$oResult = null;
|
21 |
$oHashes = Services::CoreFileHashes();
|
22 |
|
23 |
/** @var ResultItem $oRes */
|
24 |
$oRes = $this->getScanActionVO()->getNewResultItem();
|
25 |
-
$oRes->path_full = $
|
26 |
-
$oRes->path_fragment = $oHashes->getFileFragment( $
|
27 |
$oRes->md5_file_wp = $oHashes->getFileHash( $oRes->path_fragment );
|
28 |
$oRes->is_missing = !Services::WpFs()->exists( $oRes->path_full );
|
29 |
$oRes->is_checksumfail = !$oRes->is_missing && $this->isChecksumFail( $oRes );
|
@@ -58,19 +58,19 @@ class FileScanner extends Shield\Scans\Base\Files\BaseFileScanner {
|
|
58 |
}
|
59 |
|
60 |
/**
|
61 |
-
* @param ResultItem $
|
62 |
* @return bool
|
63 |
*/
|
64 |
-
private function isChecksumFail( $
|
65 |
-
$
|
66 |
-
if ( !$
|
67 |
try {
|
68 |
-
$
|
69 |
-
&& !( new CompareHash() )->isEqualFileMd5( $
|
70 |
}
|
71 |
-
catch ( \Exception $
|
72 |
}
|
73 |
}
|
74 |
-
return $
|
75 |
}
|
76 |
}
|
13 |
class FileScanner extends Shield\Scans\Base\Files\BaseFileScanner {
|
14 |
|
15 |
/**
|
16 |
+
* @param string $fullPath
|
17 |
* @return ResultItem|null
|
18 |
*/
|
19 |
+
public function scan( string $fullPath ) {
|
20 |
$oResult = null;
|
21 |
$oHashes = Services::CoreFileHashes();
|
22 |
|
23 |
/** @var ResultItem $oRes */
|
24 |
$oRes = $this->getScanActionVO()->getNewResultItem();
|
25 |
+
$oRes->path_full = $fullPath;
|
26 |
+
$oRes->path_fragment = $oHashes->getFileFragment( $fullPath );
|
27 |
$oRes->md5_file_wp = $oHashes->getFileHash( $oRes->path_fragment );
|
28 |
$oRes->is_missing = !Services::WpFs()->exists( $oRes->path_full );
|
29 |
$oRes->is_checksumfail = !$oRes->is_missing && $this->isChecksumFail( $oRes );
|
58 |
}
|
59 |
|
60 |
/**
|
61 |
+
* @param ResultItem $item
|
62 |
* @return bool
|
63 |
*/
|
64 |
+
private function isChecksumFail( $item ) {
|
65 |
+
$fail = false;
|
66 |
+
if ( !$item->is_missing ) {
|
67 |
try {
|
68 |
+
$fail = ( strpos( $item->path_full, '.php' ) > 0 )
|
69 |
+
&& !( new CompareHash() )->isEqualFileMd5( $item->path_full, $item->md5_file_wp );
|
70 |
}
|
71 |
+
catch ( \Exception $e ) {
|
72 |
}
|
73 |
}
|
74 |
+
return $fail;
|
75 |
}
|
76 |
}
|
src/lib/src/Scans/Wpv/WpVulnDb/WpVulnVO.php
CHANGED
@@ -1,10 +1,14 @@
|
|
1 |
-
<?php
|
2 |
|
3 |
namespace FernleafSystems\Wordpress\Plugin\Shield\Scans\Wpv\WpVulnDb;
|
4 |
|
|
|
|
|
5 |
/**
|
6 |
* Class WpVulnVO
|
|
|
7 |
* @property int $id
|
|
|
8 |
* @property string $title
|
9 |
* @property string $vuln_type
|
10 |
* @property string $fixed_in
|
@@ -12,14 +16,34 @@ namespace FernleafSystems\Wordpress\Plugin\Shield\Scans\Wpv\WpVulnDb;
|
|
12 |
* @property int $updated_at
|
13 |
* @property int $created_at
|
14 |
* @property int $published_date
|
15 |
-
* @package FernleafSystems\Wordpress\Plugin\Shield\Scans\Wpv\WpVulnDb
|
16 |
*/
|
17 |
-
class WpVulnVO {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
18 |
|
19 |
-
|
|
|
|
|
|
|
|
|
|
|
20 |
|
21 |
/**
|
22 |
* @return string
|
|
|
23 |
*/
|
24 |
public function getUrl() {
|
25 |
return sprintf( 'https://wpvulndb.com/vulnerabilities/%s', $this->id );
|
1 |
+
<?php declare( strict_types=1 );
|
2 |
|
3 |
namespace FernleafSystems\Wordpress\Plugin\Shield\Scans\Wpv\WpVulnDb;
|
4 |
|
5 |
+
use FernleafSystems\Utilities\Data\Adapter\DynamicPropertiesClass;
|
6 |
+
|
7 |
/**
|
8 |
* Class WpVulnVO
|
9 |
+
* @package FernleafSystems\Wordpress\Plugin\Shield\Scans\Wpv\WpVulnDb
|
10 |
* @property int $id
|
11 |
+
* @property string $url
|
12 |
* @property string $title
|
13 |
* @property string $vuln_type
|
14 |
* @property string $fixed_in
|
16 |
* @property int $updated_at
|
17 |
* @property int $created_at
|
18 |
* @property int $published_date
|
|
|
19 |
*/
|
20 |
+
class WpVulnVO extends DynamicPropertiesClass {
|
21 |
+
|
22 |
+
const URL_BASE = 'https://wpscan.com/vulnerability/%s';
|
23 |
+
|
24 |
+
/**
|
25 |
+
* @inheritDoc
|
26 |
+
*/
|
27 |
+
public function __get( string $key ) {
|
28 |
+
$val = parent::__get( $key );
|
29 |
+
switch ( $key ) {
|
30 |
+
|
31 |
+
case 'url':
|
32 |
+
if ( empty( $val ) ) {
|
33 |
+
$val = sprintf( self::URL_BASE, $this->id );
|
34 |
+
}
|
35 |
+
break;
|
36 |
|
37 |
+
default:
|
38 |
+
break;
|
39 |
+
}
|
40 |
+
|
41 |
+
return $val;
|
42 |
+
}
|
43 |
|
44 |
/**
|
45 |
* @return string
|
46 |
+
* @deprecated 10.2
|
47 |
*/
|
48 |
public function getUrl() {
|
49 |
return sprintf( 'https://wpvulndb.com/vulnerabilities/%s', $this->id );
|
src/lib/src/Tables/Build/Traffic.php
CHANGED
@@ -4,13 +4,11 @@ namespace FernleafSystems\Wordpress\Plugin\Shield\Tables\Build;
|
|
4 |
|
5 |
use FernleafSystems\Wordpress\Plugin\Shield\Databases;
|
6 |
use FernleafSystems\Wordpress\Plugin\Shield\Modules\GeoIp\Lookup;
|
|
|
|
|
7 |
use FernleafSystems\Wordpress\Plugin\Shield\Tables;
|
8 |
use FernleafSystems\Wordpress\Services\Services;
|
9 |
|
10 |
-
/**
|
11 |
-
* Class Traffic
|
12 |
-
* @package FernleafSystems\Wordpress\Plugin\Shield\Tables\Build
|
13 |
-
*/
|
14 |
class Traffic extends BaseBuild {
|
15 |
|
16 |
/**
|
@@ -18,36 +16,36 @@ class Traffic extends BaseBuild {
|
|
18 |
* @return $this
|
19 |
*/
|
20 |
protected function applyCustomQueryFilters() {
|
21 |
-
$
|
22 |
-
/** @var Databases\Traffic\Select $
|
23 |
-
$
|
24 |
|
25 |
$oIp = Services::IP();
|
26 |
// If an IP is specified, it takes priority
|
27 |
-
if ( $oIp->isValidIp( $
|
28 |
-
$
|
29 |
}
|
30 |
-
elseif ( $
|
31 |
-
$
|
32 |
}
|
33 |
|
34 |
// if username is provided, this takes priority over "logged-in" (even if it's invalid)
|
35 |
-
if ( !empty( $
|
36 |
-
$oUser = Services::WpUsers()->getUserByUsername( $
|
37 |
if ( !empty( $oUser ) ) {
|
38 |
-
$
|
39 |
}
|
40 |
}
|
41 |
-
elseif ( $
|
42 |
-
$
|
43 |
}
|
44 |
|
45 |
-
if ( $
|
46 |
-
$
|
47 |
}
|
48 |
|
49 |
-
$
|
50 |
-
$
|
51 |
|
52 |
return $this;
|
53 |
}
|
@@ -73,7 +71,7 @@ class Traffic extends BaseBuild {
|
|
73 |
* @return array[]
|
74 |
*/
|
75 |
public function getEntriesFormatted() :array {
|
76 |
-
$
|
77 |
|
78 |
$oWpUsers = Services::WpUsers();
|
79 |
$oGeoIpLookup = ( new Lookup() )->setDbHandler( $this->getCon()
|
@@ -82,83 +80,122 @@ class Traffic extends BaseBuild {
|
|
82 |
$srvIP = Services::IP();
|
83 |
$you = $srvIP->getRequestIp();
|
84 |
|
85 |
-
$
|
86 |
-
|
87 |
-
|
88 |
-
|
|
|
89 |
|
90 |
-
list( $
|
91 |
-
$
|
92 |
-
$sPath = strtoupper( $
|
93 |
-
.( empty( $
|
94 |
|
95 |
$sCodeType = 'success';
|
96 |
-
if ( $
|
97 |
$sCodeType = 'danger';
|
98 |
}
|
99 |
-
elseif ( $
|
100 |
$sCodeType = 'warning';
|
101 |
}
|
102 |
|
103 |
-
$
|
104 |
-
$
|
105 |
-
$
|
106 |
-
$
|
107 |
'<span class="badge badge-%s">%s</span>',
|
108 |
-
$
|
109 |
-
$
|
110 |
);
|
111 |
-
$
|
112 |
-
$
|
113 |
|
114 |
try {
|
115 |
-
$
|
116 |
}
|
117 |
catch ( \Exception $e ) {
|
118 |
-
$
|
119 |
}
|
120 |
-
$
|
121 |
-
$this->getIpAnalysisLink( $
|
122 |
-
$
|
123 |
);
|
124 |
|
125 |
-
if ( $
|
126 |
-
if ( !isset( $
|
127 |
-
$
|
128 |
-
$
|
129 |
sprintf( '<a href="%s" target="_blank" title="Go To Profile">%s</a>',
|
130 |
-
$oWpUsers->getAdminUrl_ProfileEdit( $
|
131 |
}
|
132 |
}
|
133 |
|
134 |
-
$
|
|
|
|
|
|
|
|
|
|
|
|
|
135 |
->setIP( $ip )
|
136 |
->lookupIp();
|
137 |
-
$
|
138 |
-
if ( empty( $
|
139 |
-
$
|
140 |
}
|
141 |
else {
|
142 |
-
$
|
143 |
-
|
|
|
|
|
|
|
|
|
144 |
}
|
145 |
|
146 |
-
$
|
147 |
-
sprintf( '%s: %s', __( 'IP', 'wp-simple-firewall' ), $
|
148 |
-
sprintf( '%s: %s', __( '
|
149 |
-
sprintf( '%s: %s', __( '
|
150 |
-
|
151 |
-
|
152 |
-
|
153 |
-
|
154 |
-
$
|
155 |
-
sprintf( '%s: %s', __( 'Response', 'wp-simple-firewall' ), $
|
156 |
-
sprintf( '%s: %s', __( 'Offense', 'wp-simple-firewall' ), $
|
157 |
-
];
|
158 |
-
|
159 |
-
$
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
160 |
}
|
161 |
-
return $
|
162 |
}
|
163 |
|
164 |
/**
|
4 |
|
5 |
use FernleafSystems\Wordpress\Plugin\Shield\Databases;
|
6 |
use FernleafSystems\Wordpress\Plugin\Shield\Modules\GeoIp\Lookup;
|
7 |
+
use FernleafSystems\Wordpress\Plugin\Shield\Modules\IPs\Lib\Ops\LookupIpOnList;
|
8 |
+
use FernleafSystems\Wordpress\Plugin\Shield\Modules\IPs\ModCon;
|
9 |
use FernleafSystems\Wordpress\Plugin\Shield\Tables;
|
10 |
use FernleafSystems\Wordpress\Services\Services;
|
11 |
|
|
|
|
|
|
|
|
|
12 |
class Traffic extends BaseBuild {
|
13 |
|
14 |
/**
|
16 |
* @return $this
|
17 |
*/
|
18 |
protected function applyCustomQueryFilters() {
|
19 |
+
$params = $this->getParams();
|
20 |
+
/** @var Databases\Traffic\Select $select */
|
21 |
+
$select = $this->getWorkingSelector();
|
22 |
|
23 |
$oIp = Services::IP();
|
24 |
// If an IP is specified, it takes priority
|
25 |
+
if ( $oIp->isValidIp( $params[ 'fIp' ] ) ) {
|
26 |
+
$select->filterByIp( inet_pton( $params[ 'fIp' ] ) );
|
27 |
}
|
28 |
+
elseif ( $params[ 'fExcludeYou' ] == 'Y' ) {
|
29 |
+
$select->filterByNotIp( inet_pton( $oIp->getRequestIp() ) );
|
30 |
}
|
31 |
|
32 |
// if username is provided, this takes priority over "logged-in" (even if it's invalid)
|
33 |
+
if ( !empty( $params[ 'fUsername' ] ) ) {
|
34 |
+
$oUser = Services::WpUsers()->getUserByUsername( $params[ 'fUsername' ] );
|
35 |
if ( !empty( $oUser ) ) {
|
36 |
+
$select->filterByUserId( $oUser->ID );
|
37 |
}
|
38 |
}
|
39 |
+
elseif ( $params[ 'fLoggedIn' ] >= 0 ) {
|
40 |
+
$select->filterByIsLoggedIn( $params[ 'fLoggedIn' ] );
|
41 |
}
|
42 |
|
43 |
+
if ( $params[ 'fOffense' ] >= 0 ) {
|
44 |
+
$select->filterByIsTransgression( $params[ 'fOffense' ] );
|
45 |
}
|
46 |
|
47 |
+
$select->filterByPathContains( $params[ 'fPath' ] );
|
48 |
+
$select->filterByResponseCode( $params[ 'fResponse' ] );
|
49 |
|
50 |
return $this;
|
51 |
}
|
71 |
* @return array[]
|
72 |
*/
|
73 |
public function getEntriesFormatted() :array {
|
74 |
+
$entries = [];
|
75 |
|
76 |
$oWpUsers = Services::WpUsers();
|
77 |
$oGeoIpLookup = ( new Lookup() )->setDbHandler( $this->getCon()
|
80 |
$srvIP = Services::IP();
|
81 |
$you = $srvIP->getRequestIp();
|
82 |
|
83 |
+
$users = [ 0 => __( 'No', 'wp-simple-firewall' ) ];
|
84 |
+
$ipInfos = [];
|
85 |
+
foreach ( $this->getEntriesRaw() as $key => $record ) {
|
86 |
+
/** @var Databases\Traffic\EntryVO $record */
|
87 |
+
$ip = $record->ip;
|
88 |
|
89 |
+
list( $preQuery, $query ) = explode( '?', $record->path.'?', 2 );
|
90 |
+
$query = trim( $query, '?' );
|
91 |
+
$sPath = strtoupper( $record->verb ).': <code>'.$preQuery
|
92 |
+
.( empty( $query ) ? '' : '?<br/>'.$query ).'</code>';
|
93 |
|
94 |
$sCodeType = 'success';
|
95 |
+
if ( $record->code >= 400 ) {
|
96 |
$sCodeType = 'danger';
|
97 |
}
|
98 |
+
elseif ( $record->code >= 300 ) {
|
99 |
$sCodeType = 'warning';
|
100 |
}
|
101 |
|
102 |
+
$e = $record->getRawDataAsArray();
|
103 |
+
$e[ 'path' ] = $sPath;
|
104 |
+
$e[ 'code' ] = sprintf( '<span class="badge badge-%s">%s</span>', $sCodeType, $record->code );
|
105 |
+
$e[ 'trans' ] = sprintf(
|
106 |
'<span class="badge badge-%s">%s</span>',
|
107 |
+
$record->trans ? 'danger' : 'info',
|
108 |
+
$record->trans ? __( 'Yes', 'wp-simple-firewall' ) : __( 'No', 'wp-simple-firewall' )
|
109 |
);
|
110 |
+
$e[ 'ip' ] = $ip;
|
111 |
+
$e[ 'created_at' ] = $this->formatTimestampField( $record->created_at );
|
112 |
|
113 |
try {
|
114 |
+
$e[ 'is_you' ] = $srvIP->checkIp( $you, $record->ip );
|
115 |
}
|
116 |
catch ( \Exception $e ) {
|
117 |
+
$e[ 'is_you' ] = false;
|
118 |
}
|
119 |
+
$ipLink = sprintf( '%s%s',
|
120 |
+
$this->getIpAnalysisLink( $record->ip ),
|
121 |
+
$e[ 'is_you' ] ? ' <small>('.__( 'This Is You', 'wp-simple-firewall' ).')</small>' : ''
|
122 |
);
|
123 |
|
124 |
+
if ( $record->uid > 0 ) {
|
125 |
+
if ( !isset( $users[ $record->uid ] ) ) {
|
126 |
+
$user = $oWpUsers->getUserById( $record->uid );
|
127 |
+
$users[ $record->uid ] = empty( $user ) ? __( 'Unknown', 'wp-simple-firewall' ) :
|
128 |
sprintf( '<a href="%s" target="_blank" title="Go To Profile">%s</a>',
|
129 |
+
$oWpUsers->getAdminUrl_ProfileEdit( $user ), $user->user_login );
|
130 |
}
|
131 |
}
|
132 |
|
133 |
+
if ( !empty( $record->ip ) ) {
|
134 |
+
if ( !isset( $ipInfos[ $record->ip ] ) ) {
|
135 |
+
$ipInfos[ $record->ip ] = $this->getIpInfo( $record->ip );
|
136 |
+
}
|
137 |
+
}
|
138 |
+
|
139 |
+
$geoIP = $oGeoIpLookup
|
140 |
->setIP( $ip )
|
141 |
->lookupIp();
|
142 |
+
$countryISO = $geoIP->getCountryCode();
|
143 |
+
if ( empty( $countryISO ) ) {
|
144 |
+
$country = __( 'Unknown', 'wp-simple-firewall' );
|
145 |
}
|
146 |
else {
|
147 |
+
$country = sprintf(
|
148 |
+
'<img class="icon-flag" src="%s" alt="%s"/> %s',
|
149 |
+
sprintf( 'https://www.countryflags.io/%s/flat/16.png', strtolower( $countryISO ) ),
|
150 |
+
$countryISO,
|
151 |
+
$geoIP->getCountryName()
|
152 |
+
);
|
153 |
}
|
154 |
|
155 |
+
$e[ 'visitor' ] = sprintf( '<div>%s</div>', implode( '</div><div>', [
|
156 |
+
sprintf( '%s: %s', __( 'IP', 'wp-simple-firewall' ), $ipLink ),
|
157 |
+
sprintf( '%s: %s', __( 'IP Status', 'wp-simple-firewall' ), $ipInfos[ $record->ip ] ?? 'n/a' ),
|
158 |
+
sprintf( '%s: %s', __( 'Logged-In', 'wp-simple-firewall' ), $users[ $record->uid ] ),
|
159 |
+
sprintf( '%s: %s', __( 'Location', 'wp-simple-firewall' ), $country ),
|
160 |
+
esc_html( esc_js( sprintf( '%s - %s', __( 'User Agent', 'wp-simple-firewall' ), $record->ua ) ) ),
|
161 |
+
] ) );
|
162 |
+
|
163 |
+
$e[ 'request_info' ] = sprintf( '<div>%s</div>', implode( '</div><div>', [
|
164 |
+
sprintf( '%s: %s', __( 'Response', 'wp-simple-firewall' ), $e[ 'code' ] ),
|
165 |
+
sprintf( '%s: %s', __( 'Offense', 'wp-simple-firewall' ), $e[ 'trans' ] ),
|
166 |
+
] ) );
|
167 |
+
|
168 |
+
$entries[ $key ] = $e;
|
169 |
+
}
|
170 |
+
return $entries;
|
171 |
+
}
|
172 |
+
|
173 |
+
private function getIpInfo( string $ip ) :string {
|
174 |
+
$record = ( new LookupIpOnList() )
|
175 |
+
->setDbHandler( $this->getCon()->getModule_IPs()->getDbHandler_IPs() )
|
176 |
+
->setIP( $ip )
|
177 |
+
->lookup();
|
178 |
+
|
179 |
+
$badgeTemplate = '<span class="badge badge-%s">%s</span>';
|
180 |
+
if ( $record->blocked_at > 0 || $record->list === ModCon::LIST_MANUAL_BLACK ) {
|
181 |
+
$status = sprintf( $badgeTemplate, 'danger', __( 'Blocked', 'wp-simple-firewall' ) );
|
182 |
+
}
|
183 |
+
elseif ( $record->list === ModCon::LIST_AUTO_BLACK ) {
|
184 |
+
$status = sprintf( $badgeTemplate,
|
185 |
+
'warning',
|
186 |
+
sprintf( _n( '%s offense', '%s offenses', $record->transgressions, 'wp-simple-firewall' ), $record->transgressions )
|
187 |
+
);
|
188 |
+
}
|
189 |
+
elseif ( $record->list === ModCon::LIST_MANUAL_WHITE ) {
|
190 |
+
$status = sprintf( $badgeTemplate,
|
191 |
+
'success',
|
192 |
+
__( 'Bypass', 'wp-simple-firewall' )
|
193 |
+
);
|
194 |
+
}
|
195 |
+
else {
|
196 |
+
$status = __( 'No Record', 'wp-simple-firewall' );
|
197 |
}
|
198 |
+
return $status;
|
199 |
}
|
200 |
|
201 |
/**
|
src/lib/src/Tables/Render/WpListTable/Base.php
CHANGED
@@ -42,12 +42,12 @@ class Base extends \WP_List_Table {
|
|
42 |
}
|
43 |
|
44 |
/**
|
45 |
-
* @param object $
|
46 |
-
* @param string $
|
47 |
* @return string
|
48 |
*/
|
49 |
-
public function column_default( $
|
50 |
-
return $
|
51 |
}
|
52 |
|
53 |
/**
|
42 |
}
|
43 |
|
44 |
/**
|
45 |
+
* @param object $item
|
46 |
+
* @param string $colName
|
47 |
* @return string
|
48 |
*/
|
49 |
+
public function column_default( $item, $colName ) {
|
50 |
+
return $item[ $colName ];
|
51 |
}
|
52 |
|
53 |
/**
|
src/lib/src/Tables/Render/WpListTable/ScanWpv.php
CHANGED
@@ -41,20 +41,20 @@ class ScanWpv extends ScanBase {
|
|
41 |
}
|
42 |
|
43 |
/**
|
44 |
-
* @param array $
|
45 |
* @return string
|
46 |
*/
|
47 |
-
public function column_vulnerability( $
|
48 |
-
/** @var Scans\Wpv\WpVulnDb\WpVulnVO $
|
49 |
-
$
|
50 |
-
$sContent = sprintf( '<span class="vuln-title">%s</span>', $
|
51 |
|
52 |
-
$
|
53 |
-
$this->getActionButton_Ignore( $
|
54 |
sprintf( '<a href="%s" class="btn btn-sm btn-link text-info" target="_blank">%s</a>',
|
55 |
-
$
|
56 |
];
|
57 |
-
return $sContent.$this->buildActions( $
|
58 |
}
|
59 |
|
60 |
/**
|
41 |
}
|
42 |
|
43 |
/**
|
44 |
+
* @param array $item
|
45 |
* @return string
|
46 |
*/
|
47 |
+
public function column_vulnerability( $item ) {
|
48 |
+
/** @var Scans\Wpv\WpVulnDb\WpVulnVO $vuln */
|
49 |
+
$vuln = $item[ 'wpvuln_vo' ];
|
50 |
+
$sContent = sprintf( '<span class="vuln-title">%s</span>', $vuln->title );
|
51 |
|
52 |
+
$buttons = [
|
53 |
+
$this->getActionButton_Ignore( $item[ 'id' ] ),
|
54 |
sprintf( '<a href="%s" class="btn btn-sm btn-link text-info" target="_blank">%s</a>',
|
55 |
+
$vuln->url, __( 'More Info', 'wp-simple-firewall' ) ),
|
56 |
];
|
57 |
+
return $sContent.$this->buildActions( $buttons );
|
58 |
}
|
59 |
|
60 |
/**
|
src/lib/src/Utilities/ReCaptcha/TestRequest.php
CHANGED
@@ -20,9 +20,9 @@ class TestRequest {
|
|
20 |
$this->runTest();
|
21 |
$this->getCon()->fireEvent( 'recaptcha_success' );
|
22 |
}
|
23 |
-
catch ( \Exception $
|
24 |
$this->getCon()->fireEvent( 'recaptcha_fail' );
|
25 |
-
throw $
|
26 |
}
|
27 |
return true;
|
28 |
}
|
20 |
$this->runTest();
|
21 |
$this->getCon()->fireEvent( 'recaptcha_success' );
|
22 |
}
|
23 |
+
catch ( \Exception $e ) {
|
24 |
$this->getCon()->fireEvent( 'recaptcha_fail' );
|
25 |
+
throw $e;
|
26 |
}
|
27 |
return true;
|
28 |
}
|
src/lib/src/Utilities/Time/WorldTimeApi.php
ADDED
@@ -0,0 +1,33 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php declare( strict_types=1 );
|
2 |
+
|
3 |
+
namespace FernleafSystems\Wordpress\Plugin\Shield\Utilities\Time;
|
4 |
+
|
5 |
+
use FernleafSystems\Wordpress\Services\Services;
|
6 |
+
|
7 |
+
class WorldTimeApi {
|
8 |
+
|
9 |
+
/**
|
10 |
+
* @return int
|
11 |
+
* @throws \Exception
|
12 |
+
*/
|
13 |
+
public function current() :int {
|
14 |
+
$raw = Services::HttpRequest()
|
15 |
+
->getContent( 'https://showcase.api.linx.twenty57.net/UnixTime/tounixtimestamp?datetime=now' );
|
16 |
+
if ( empty( $raw ) ) {
|
17 |
+
throw new \Exception( 'Request to World Clock Api Failed' );
|
18 |
+
}
|
19 |
+
$dec = json_decode( $raw, true );
|
20 |
+
if ( empty( $dec ) ) {
|
21 |
+
throw new \Exception( 'Failed to decode World Clock Api response' );
|
22 |
+
}
|
23 |
+
return (int)$dec[ 'UnixTimeStamp' ];
|
24 |
+
}
|
25 |
+
|
26 |
+
/**
|
27 |
+
* @return int
|
28 |
+
* @throws \Exception
|
29 |
+
*/
|
30 |
+
public function diffServerWithReal() :int {
|
31 |
+
return 13;// time() - $this->current();
|
32 |
+
}
|
33 |
+
}
|
src/lib/vendor/autoload.php
CHANGED
@@ -4,4 +4,4 @@
|
|
4 |
|
5 |
require_once __DIR__ . '/composer/autoload_real.php';
|
6 |
|
7 |
-
return
|
4 |
|
5 |
require_once __DIR__ . '/composer/autoload_real.php';
|
6 |
|
7 |
+
return ComposerAutoloaderInit4fc2c6daaffaf40b64b79b6d26830171::getLoader();
|
src/lib/vendor/composer/ClassLoader.php
CHANGED
@@ -37,11 +37,13 @@ namespace Composer\Autoload;
|
|
37 |
*
|
38 |
* @author Fabien Potencier <fabien@symfony.com>
|
39 |
* @author Jordi Boggiano <j.boggiano@seld.be>
|
40 |
-
* @see
|
41 |
-
* @see
|
42 |
*/
|
43 |
class ClassLoader
|
44 |
{
|
|
|
|
|
45 |
// PSR-4
|
46 |
private $prefixLengthsPsr4 = array();
|
47 |
private $prefixDirsPsr4 = array();
|
@@ -57,6 +59,13 @@ class ClassLoader
|
|
57 |
private $missingClasses = array();
|
58 |
private $apcuPrefix;
|
59 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
60 |
public function getPrefixes()
|
61 |
{
|
62 |
if (!empty($this->prefixesPsr0)) {
|
@@ -300,6 +309,15 @@ class ClassLoader
|
|
300 |
public function register($prepend = false)
|
301 |
{
|
302 |
spl_autoload_register(array($this, 'loadClass'), true, $prepend);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
303 |
}
|
304 |
|
305 |
/**
|
@@ -308,6 +326,10 @@ class ClassLoader
|
|
308 |
public function unregister()
|
309 |
{
|
310 |
spl_autoload_unregister(array($this, 'loadClass'));
|
|
|
|
|
|
|
|
|
311 |
}
|
312 |
|
313 |
/**
|
@@ -367,6 +389,16 @@ class ClassLoader
|
|
367 |
return $file;
|
368 |
}
|
369 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
370 |
private function findFileWithExtension($class, $ext)
|
371 |
{
|
372 |
// PSR-4 lookup
|
37 |
*
|
38 |
* @author Fabien Potencier <fabien@symfony.com>
|
39 |
* @author Jordi Boggiano <j.boggiano@seld.be>
|
40 |
+
* @see https://www.php-fig.org/psr/psr-0/
|
41 |
+
* @see https://www.php-fig.org/psr/psr-4/
|
42 |
*/
|
43 |
class ClassLoader
|
44 |
{
|
45 |
+
private $vendorDir;
|
46 |
+
|
47 |
// PSR-4
|
48 |
private $prefixLengthsPsr4 = array();
|
49 |
private $prefixDirsPsr4 = array();
|
59 |
private $missingClasses = array();
|
60 |
private $apcuPrefix;
|
61 |
|
62 |
+
private static $registeredLoaders = array();
|
63 |
+
|
64 |
+
public function __construct($vendorDir = null)
|
65 |
+
{
|
66 |
+
$this->vendorDir = $vendorDir;
|
67 |
+
}
|
68 |
+
|
69 |
public function getPrefixes()
|
70 |
{
|
71 |
if (!empty($this->prefixesPsr0)) {
|
309 |
public function register($prepend = false)
|
310 |
{
|
311 |
spl_autoload_register(array($this, 'loadClass'), true, $prepend);
|
312 |
+
|
313 |
+
if (null === $this->vendorDir) {
|
314 |
+
//no-op
|
315 |
+
} elseif ($prepend) {
|
316 |
+
self::$registeredLoaders = array($this->vendorDir => $this) + self::$registeredLoaders;
|
317 |
+
} else {
|
318 |
+
unset(self::$registeredLoaders[$this->vendorDir]);
|
319 |
+
self::$registeredLoaders[$this->vendorDir] = $this;
|
320 |
+
}
|
321 |
}
|
322 |
|
323 |
/**
|
326 |
public function unregister()
|
327 |
{
|
328 |
spl_autoload_unregister(array($this, 'loadClass'));
|
329 |
+
|
330 |
+
if (null !== $this->vendorDir) {
|
331 |
+
unset(self::$registeredLoaders[$this->vendorDir]);
|
332 |
+
}
|
333 |
}
|
334 |
|
335 |
/**
|
389 |
return $file;
|
390 |
}
|
391 |
|
392 |
+
/**
|
393 |
+
* Returns the currently registered loaders indexed by their corresponding vendor directories.
|
394 |
+
*
|
395 |
+
* @return self[]
|
396 |
+
*/
|
397 |
+
public static function getRegisteredLoaders()
|
398 |
+
{
|
399 |
+
return self::$registeredLoaders;
|
400 |
+
}
|
401 |
+
|
402 |
private function findFileWithExtension($class, $ext)
|
403 |
{
|
404 |
// PSR-4 lookup
|
src/lib/vendor/composer/autoload_classmap.php
CHANGED
@@ -14,6 +14,7 @@ return array(
|
|
14 |
'Carbon\\Laravel\\ServiceProvider' => $vendorDir . '/nesbot/carbon/src/Carbon/Laravel/ServiceProvider.php',
|
15 |
'Carbon\\Translator' => $vendorDir . '/nesbot/carbon/src/Carbon/Translator.php',
|
16 |
'Carbon\\Upgrade' => $vendorDir . '/nesbot/carbon/src/Carbon/Upgrade.php',
|
|
|
17 |
'Dolondro\\GoogleAuthenticator\\GoogleAuthenticator' => $vendorDir . '/dolondro/google-authenticator/src/GoogleAuthenticator.php',
|
18 |
'Dolondro\\GoogleAuthenticator\\QrImageGenerator\\EndroidQrImageGenerator' => $vendorDir . '/dolondro/google-authenticator/src/QrImageGenerator/EndroidQrImageGenerator.php',
|
19 |
'Dolondro\\GoogleAuthenticator\\QrImageGenerator\\GoogleQrImageGenerator' => $vendorDir . '/dolondro/google-authenticator/src/QrImageGenerator/GoogleQrImageGenerator.php',
|
@@ -21,10 +22,12 @@ return array(
|
|
21 |
'Dolondro\\GoogleAuthenticator\\Secret' => $vendorDir . '/dolondro/google-authenticator/src/Secret.php',
|
22 |
'Dolondro\\GoogleAuthenticator\\SecretFactory' => $vendorDir . '/dolondro/google-authenticator/src/SecretFactory.php',
|
23 |
'Elliotchance\\Iterator\\AbstractPagedIterator' => $vendorDir . '/elliotchance/iterator/src/Elliotchance/Iterator/AbstractPagedIterator.php',
|
24 |
-
'Elliotchance\\Iterator\\PagedIterator1' => $vendorDir . '/elliotchance/iterator/tests/Elliotchance/Iterator/PagedIteratorTest.php',
|
25 |
-
'Elliotchance\\Iterator\\PagedIterator2' => $vendorDir . '/elliotchance/iterator/tests/Elliotchance/Iterator/PagedIteratorTest.php',
|
26 |
'Elliotchance\\Iterator\\PagedIteratorTest' => $vendorDir . '/elliotchance/iterator/tests/Elliotchance/Iterator/PagedIteratorTest.php',
|
|
|
|
|
27 |
'FernleafSystems\\Utilities\\Data\\Adapter\\StdClassAdapter' => $vendorDir . '/fernleafsystems/utilities/src/Data/Adapter/StdClassAdapter.php',
|
|
|
|
|
28 |
'FernleafSystems\\Utilities\\Logic\\OneTimeExecute' => $vendorDir . '/fernleafsystems/utilities/src/Logic/OneTimeExecute.php',
|
29 |
'FernleafSystems\\Utilities\\Response' => $vendorDir . '/fernleafsystems/utilities/src/Response.php',
|
30 |
'FernleafSystems\\Wordpress\\Plugin\\Shield\\ChangeTrack\\Diff\\Base' => $baseDir . '/src/ChangeTrack/Diff/Base.php',
|
@@ -52,6 +55,13 @@ return array(
|
|
52 |
'FernleafSystems\\Wordpress\\Plugin\\Shield\\ChangeTrack\\Snapshot\\BuildUsers' => $baseDir . '/src/ChangeTrack/Snapshot/BuildUsers.php',
|
53 |
'FernleafSystems\\Wordpress\\Plugin\\Shield\\ChangeTrack\\Snapshot\\Collate' => $baseDir . '/src/ChangeTrack/Snapshot/Collate.php',
|
54 |
'FernleafSystems\\Wordpress\\Plugin\\Shield\\ChangeTrack\\Snapshot\\SnapshotsConsumer' => $baseDir . '/src/ChangeTrack/Snapshot/SnapshotsConsumer.php',
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
55 |
'FernleafSystems\\Wordpress\\Plugin\\Shield\\Controller\\Config\\ConfigVO' => $baseDir . '/src/Controller/Config/ConfigVO.php',
|
56 |
'FernleafSystems\\Wordpress\\Plugin\\Shield\\Controller\\Config\\Ops\\LoadConfig' => $baseDir . '/src/Controller/Config/Ops/LoadConfig.php',
|
57 |
'FernleafSystems\\Wordpress\\Plugin\\Shield\\Controller\\Config\\Ops\\Read' => $baseDir . '/src/Controller/Config/Ops/Read.php',
|
@@ -196,10 +206,7 @@ return array(
|
|
196 |
'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\BaseShield\\UI' => $baseDir . '/src/Modules/BaseShield/UI.php',
|
197 |
'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Base\\AdminNotices' => $baseDir . '/src/Modules/Base/AdminNotices.php',
|
198 |
'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Base\\AjaxHandler' => $baseDir . '/src/Modules/Base/AjaxHandler.php',
|
199 |
-
'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Base\\AjaxHandlerBase' => $baseDir . '/src/Modules/Base/AjaxHandlerBase.php',
|
200 |
-
'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Base\\AjaxHandlerShield' => $baseDir . '/src/Modules/Base/AjaxHandlerShield.php',
|
201 |
'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Base\\BaseProcessor' => $baseDir . '/src/Modules/Base/BaseProcessor.php',
|
202 |
-
'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Base\\BaseReporting' => $baseDir . '/src/Modules/Base/BaseReporting.php',
|
203 |
'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Base\\Debug' => $baseDir . '/src/Modules/Base/Debug.php',
|
204 |
'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Base\\Insights\\OverviewCards' => $baseDir . '/src/Modules/Base/Insights/OverviewCards.php',
|
205 |
'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Base\\ModCon' => $baseDir . '/src/Modules/Base/ModCon.php',
|
@@ -207,8 +214,6 @@ return array(
|
|
207 |
'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Base\\Options\\OptValueSanitize' => $baseDir . '/src/Modules/Base/Options/OptValueSanitize.php',
|
208 |
'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Base\\Processor' => $baseDir . '/src/Modules/Base/Processor.php',
|
209 |
'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Base\\Reporting' => $baseDir . '/src/Modules/Base/Reporting.php',
|
210 |
-
'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Base\\ShieldOptions' => $baseDir . '/src/Modules/Base/ShieldOptions.php',
|
211 |
-
'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Base\\ShieldUI' => $baseDir . '/src/Modules/Base/ShieldUI.php',
|
212 |
'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Base\\Strings' => $baseDir . '/src/Modules/Base/Strings.php',
|
213 |
'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Base\\UI' => $baseDir . '/src/Modules/Base/UI.php',
|
214 |
'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Base\\Upgrade' => $baseDir . '/src/Modules/Base/Upgrade.php',
|
@@ -262,6 +267,7 @@ return array(
|
|
262 |
'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Firewall\\UI' => $baseDir . '/src/Modules/Firewall/UI.php',
|
263 |
'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\GeoIp\\Lookup' => $baseDir . '/src/Modules/GeoIp/Lookup.php',
|
264 |
'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\HackGuard\\AjaxHandler' => $baseDir . '/src/Modules/HackGuard/AjaxHandler.php',
|
|
|
265 |
'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\HackGuard\\Insights\\OverviewCards' => $baseDir . '/src/Modules/HackGuard/Insights/OverviewCards.php',
|
266 |
'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\HackGuard\\Lib\\FileLocker\\File' => $baseDir . '/src/Modules/HackGuard/Lib/FileLocker/File.php',
|
267 |
'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\HackGuard\\Lib\\FileLocker\\FileLockerController' => $baseDir . '/src/Modules/HackGuard/Lib/FileLocker/FileLockerController.php',
|
@@ -420,6 +426,7 @@ return array(
|
|
420 |
'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\License\\Lib\\LicenseEmails' => $baseDir . '/src/Modules/License/Lib/LicenseEmails.php',
|
421 |
'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\License\\Lib\\LicenseHandler' => $baseDir . '/src/Modules/License/Lib/LicenseHandler.php',
|
422 |
'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\License\\Lib\\LookupRequest' => $baseDir . '/src/Modules/License/Lib/LookupRequest.php',
|
|
|
423 |
'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\License\\Lib\\Verify' => $baseDir . '/src/Modules/License/Lib/Verify.php',
|
424 |
'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\License\\Lib\\WpHashes\\ApiTokenManager' => $baseDir . '/src/Modules/License/Lib/WpHashes/ApiTokenManager.php',
|
425 |
'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\License\\ModCon' => $baseDir . '/src/Modules/License/ModCon.php',
|
@@ -741,6 +748,7 @@ return array(
|
|
741 |
'FernleafSystems\\Wordpress\\Plugin\\Shield\\Utilities\\ReCaptcha\\Enqueue' => $baseDir . '/src/Utilities/ReCaptcha/Enqueue.php',
|
742 |
'FernleafSystems\\Wordpress\\Plugin\\Shield\\Utilities\\ReCaptcha\\TestRequest' => $baseDir . '/src/Utilities/ReCaptcha/TestRequest.php',
|
743 |
'FernleafSystems\\Wordpress\\Plugin\\Shield\\Utilities\\ReCaptcha\\WordpressPost' => $baseDir . '/src/Utilities/ReCaptcha/WordpressPost.php',
|
|
|
744 |
'FernleafSystems\\Wordpress\\Plugin\\Shield\\Utilities\\Tool\\FormatBytes' => $baseDir . '/src/Utilities/Tool/FormatBytes.php',
|
745 |
'FernleafSystems\\Wordpress\\Plugin\\Shield\\Utilities\\Tool\\IpListSort' => $baseDir . '/src/Utilities/Tool/IpListSort.php',
|
746 |
'FernleafSystems\\Wordpress\\Services\\Core\\AdminNotices' => $vendorDir . '/fernleafsystems/wordpress-services/src/Core/AdminNotices.php',
|
@@ -892,59 +900,7 @@ return array(
|
|
892 |
'FernleafSystems\\Wordpress\\Services\\Utilities\\WpOrg\\Wp\\Versions' => $vendorDir . '/fernleafsystems/wordpress-services/src/Utilities/WpOrg/Wp/Versions.php',
|
893 |
'Html2Text\\Html2Text' => $vendorDir . '/soundasleep/html2text/src/Html2Text.php',
|
894 |
'Html2Text\\Html2TextException' => $vendorDir . '/soundasleep/html2text/src/Html2TextException.php',
|
895 |
-
'ICWP_WPSF_FeatureHandler_AdminAccessRestriction' => $baseDir . '/../features/admin_access_restriction.php',
|
896 |
-
'ICWP_WPSF_FeatureHandler_AuditTrail' => $baseDir . '/../features/audit_trail.php',
|
897 |
-
'ICWP_WPSF_FeatureHandler_Autoupdates' => $baseDir . '/../features/autoupdates.php',
|
898 |
-
'ICWP_WPSF_FeatureHandler_Base' => $baseDir . '/../features/base.php',
|
899 |
-
'ICWP_WPSF_FeatureHandler_BaseWpsf' => $baseDir . '/../features/base_wpsf.php',
|
900 |
-
'ICWP_WPSF_FeatureHandler_CommentsFilter' => $baseDir . '/../features/comments_filter.php',
|
901 |
-
'ICWP_WPSF_FeatureHandler_Comms' => $baseDir . '/../features/comms.php',
|
902 |
-
'ICWP_WPSF_FeatureHandler_Email' => $baseDir . '/../features/email.php',
|
903 |
-
'ICWP_WPSF_FeatureHandler_Events' => $baseDir . '/../features/events.php',
|
904 |
-
'ICWP_WPSF_FeatureHandler_Firewall' => $baseDir . '/../features/firewall.php',
|
905 |
-
'ICWP_WPSF_FeatureHandler_HackProtect' => $baseDir . '/../features/hack_protect.php',
|
906 |
-
'ICWP_WPSF_FeatureHandler_Headers' => $baseDir . '/../features/headers.php',
|
907 |
-
'ICWP_WPSF_FeatureHandler_Insights' => $baseDir . '/../features/insights.php',
|
908 |
-
'ICWP_WPSF_FeatureHandler_Ips' => $baseDir . '/../features/ips.php',
|
909 |
-
'ICWP_WPSF_FeatureHandler_License' => $baseDir . '/../features/license.php',
|
910 |
-
'ICWP_WPSF_FeatureHandler_Lockdown' => $baseDir . '/../features/lockdown.php',
|
911 |
-
'ICWP_WPSF_FeatureHandler_LoginProtect' => $baseDir . '/../features/login_protect.php',
|
912 |
-
'ICWP_WPSF_FeatureHandler_Plugin' => $baseDir . '/../features/plugin.php',
|
913 |
-
'ICWP_WPSF_FeatureHandler_Reporting' => $baseDir . '/../features/reporting.php',
|
914 |
-
'ICWP_WPSF_FeatureHandler_Sessions' => $baseDir . '/../features/sessions.php',
|
915 |
-
'ICWP_WPSF_FeatureHandler_Traffic' => $baseDir . '/../features/traffic.php',
|
916 |
-
'ICWP_WPSF_FeatureHandler_UserManagement' => $baseDir . '/../features/user_management.php',
|
917 |
-
'ICWP_WPSF_Processor_AdminAccessRestriction' => $baseDir . '/../processors/admin_access_restriction.php',
|
918 |
-
'ICWP_WPSF_Processor_AuditTrail' => $baseDir . '/../processors/audit_trail.php',
|
919 |
'ICWP_WPSF_Processor_AuditTrail_Auditor' => $baseDir . '/../processors/audit_trail_auditor.php',
|
920 |
-
'ICWP_WPSF_Processor_Autoupdates' => $baseDir . '/../processors/autoupdates.php',
|
921 |
-
'ICWP_WPSF_Processor_CommentsFilter' => $baseDir . '/../processors/comments_filter.php',
|
922 |
-
'ICWP_WPSF_Processor_CommentsFilter_BotSpam' => $baseDir . '/../processors/commentsfilter_botspam.php',
|
923 |
-
'ICWP_WPSF_Processor_CommentsFilter_GoogleRecaptcha' => $baseDir . '/../processors/commentsfilter_googlerecaptcha.php',
|
924 |
-
'ICWP_WPSF_Processor_Email' => $baseDir . '/../processors/email.php',
|
925 |
-
'ICWP_WPSF_Processor_Events' => $baseDir . '/../processors/events.php',
|
926 |
-
'ICWP_WPSF_Processor_Firewall' => $baseDir . '/../processors/firewall.php',
|
927 |
-
'ICWP_WPSF_Processor_HackProtect' => $baseDir . '/../processors/hack_protect.php',
|
928 |
-
'ICWP_WPSF_Processor_HackProtect_Apc' => $baseDir . '/../processors/hackprotect_scan_apc.php',
|
929 |
-
'ICWP_WPSF_Processor_HackProtect_Integrity' => $baseDir . '/../processors/hackprotect_integrity.php',
|
930 |
-
'ICWP_WPSF_Processor_HackProtect_Mal' => $baseDir . '/../processors/hackprotect_scan_mal.php',
|
931 |
-
'ICWP_WPSF_Processor_HackProtect_Ptg' => $baseDir . '/../processors/hackprotect_scan_ptg.php',
|
932 |
-
'ICWP_WPSF_Processor_HackProtect_Scanner' => $baseDir . '/../processors/hackprotect_scanner.php',
|
933 |
-
'ICWP_WPSF_Processor_HackProtect_Ufc' => $baseDir . '/../processors/hackprotect_scan_ufc.php',
|
934 |
-
'ICWP_WPSF_Processor_HackProtect_Wcf' => $baseDir . '/../processors/hackprotect_scan_wcf.php',
|
935 |
-
'ICWP_WPSF_Processor_HackProtect_Wpv' => $baseDir . '/../processors/hackprotect_scan_wpv.php',
|
936 |
-
'ICWP_WPSF_Processor_Headers' => $baseDir . '/../processors/headers.php',
|
937 |
-
'ICWP_WPSF_Processor_Lockdown' => $baseDir . '/../processors/lockdown.php',
|
938 |
-
'ICWP_WPSF_Processor_LoginProtect' => $baseDir . '/../processors/login_protect.php',
|
939 |
-
'ICWP_WPSF_Processor_LoginProtect_WpLogin' => $baseDir . '/../processors/loginprotect_wplogin.php',
|
940 |
-
'ICWP_WPSF_Processor_Plugin' => $baseDir . '/../processors/plugin.php',
|
941 |
-
'ICWP_WPSF_Processor_Plugin_Tracking' => $baseDir . '/../processors/plugin_tracking.php',
|
942 |
-
'ICWP_WPSF_Processor_ScanBase' => $baseDir . '/../processors/hackprotect_scan_base.php',
|
943 |
-
'ICWP_WPSF_Processor_Sessions' => $baseDir . '/../processors/sessions.php',
|
944 |
-
'ICWP_WPSF_Processor_Traffic' => $baseDir . '/../processors/traffic.php',
|
945 |
-
'ICWP_WPSF_Processor_UserManagement' => $baseDir . '/../processors/user_management.php',
|
946 |
-
'ICWP_WPSF_Processor_UserManagement_Passwords' => $baseDir . '/../processors/usermanagement_passwords.php',
|
947 |
-
'ICWP_WPSF_Processor_UserManagement_Sessions' => $baseDir . '/../processors/usermanagement_sessions.php',
|
948 |
'ICWP_WPSF_Wizard_Base' => $baseDir . '/../wizards/base.php',
|
949 |
'ICWP_WPSF_Wizard_BaseWpsf' => $baseDir . '/../wizards/base_wpsf.php',
|
950 |
'ICWP_WPSF_Wizard_LoginProtect' => $baseDir . '/../wizards/login_protect.php',
|
14 |
'Carbon\\Laravel\\ServiceProvider' => $vendorDir . '/nesbot/carbon/src/Carbon/Laravel/ServiceProvider.php',
|
15 |
'Carbon\\Translator' => $vendorDir . '/nesbot/carbon/src/Carbon/Translator.php',
|
16 |
'Carbon\\Upgrade' => $vendorDir . '/nesbot/carbon/src/Carbon/Upgrade.php',
|
17 |
+
'Composer\\InstalledVersions' => $vendorDir . '/composer/InstalledVersions.php',
|
18 |
'Dolondro\\GoogleAuthenticator\\GoogleAuthenticator' => $vendorDir . '/dolondro/google-authenticator/src/GoogleAuthenticator.php',
|
19 |
'Dolondro\\GoogleAuthenticator\\QrImageGenerator\\EndroidQrImageGenerator' => $vendorDir . '/dolondro/google-authenticator/src/QrImageGenerator/EndroidQrImageGenerator.php',
|
20 |
'Dolondro\\GoogleAuthenticator\\QrImageGenerator\\GoogleQrImageGenerator' => $vendorDir . '/dolondro/google-authenticator/src/QrImageGenerator/GoogleQrImageGenerator.php',
|
22 |
'Dolondro\\GoogleAuthenticator\\Secret' => $vendorDir . '/dolondro/google-authenticator/src/Secret.php',
|
23 |
'Dolondro\\GoogleAuthenticator\\SecretFactory' => $vendorDir . '/dolondro/google-authenticator/src/SecretFactory.php',
|
24 |
'Elliotchance\\Iterator\\AbstractPagedIterator' => $vendorDir . '/elliotchance/iterator/src/Elliotchance/Iterator/AbstractPagedIterator.php',
|
|
|
|
|
25 |
'Elliotchance\\Iterator\\PagedIteratorTest' => $vendorDir . '/elliotchance/iterator/tests/Elliotchance/Iterator/PagedIteratorTest.php',
|
26 |
+
'FernleafSystems\\Utilities\\Data\\Adapter\\DynamicProperties' => $vendorDir . '/fernleafsystems/utilities/src/Data/Adapter/DynamicProperties.php',
|
27 |
+
'FernleafSystems\\Utilities\\Data\\Adapter\\DynamicPropertiesClass' => $vendorDir . '/fernleafsystems/utilities/src/Data/Adapter/DynamicPropertiesClass.php',
|
28 |
'FernleafSystems\\Utilities\\Data\\Adapter\\StdClassAdapter' => $vendorDir . '/fernleafsystems/utilities/src/Data/Adapter/StdClassAdapter.php',
|
29 |
+
'FernleafSystems\\Utilities\\Data\\CaptureOutput' => $vendorDir . '/fernleafsystems/utilities/src/Data/CaptureOutput.php',
|
30 |
+
'FernleafSystems\\Utilities\\Logic\\ExecOnce' => $vendorDir . '/fernleafsystems/utilities/src/Logic/ExecOnce.php',
|
31 |
'FernleafSystems\\Utilities\\Logic\\OneTimeExecute' => $vendorDir . '/fernleafsystems/utilities/src/Logic/OneTimeExecute.php',
|
32 |
'FernleafSystems\\Utilities\\Response' => $vendorDir . '/fernleafsystems/utilities/src/Response.php',
|
33 |
'FernleafSystems\\Wordpress\\Plugin\\Shield\\ChangeTrack\\Diff\\Base' => $baseDir . '/src/ChangeTrack/Diff/Base.php',
|
55 |
'FernleafSystems\\Wordpress\\Plugin\\Shield\\ChangeTrack\\Snapshot\\BuildUsers' => $baseDir . '/src/ChangeTrack/Snapshot/BuildUsers.php',
|
56 |
'FernleafSystems\\Wordpress\\Plugin\\Shield\\ChangeTrack\\Snapshot\\Collate' => $baseDir . '/src/ChangeTrack/Snapshot/Collate.php',
|
57 |
'FernleafSystems\\Wordpress\\Plugin\\Shield\\ChangeTrack\\Snapshot\\SnapshotsConsumer' => $baseDir . '/src/ChangeTrack/Snapshot/SnapshotsConsumer.php',
|
58 |
+
'FernleafSystems\\Wordpress\\Plugin\\Shield\\Controller\\Admin\\AdminBarMenu' => $baseDir . '/src/Controller/Admin/AdminBarMenu.php',
|
59 |
+
'FernleafSystems\\Wordpress\\Plugin\\Shield\\Controller\\Admin\\DashboardWidget' => $baseDir . '/src/Controller/Admin/DashboardWidget.php',
|
60 |
+
'FernleafSystems\\Wordpress\\Plugin\\Shield\\Controller\\Admin\\MainAdminMenu' => $baseDir . '/src/Controller/Admin/MainAdminMenu.php',
|
61 |
+
'FernleafSystems\\Wordpress\\Plugin\\Shield\\Controller\\Ajax\\Init' => $baseDir . '/src/Controller/Ajax/Init.php',
|
62 |
+
'FernleafSystems\\Wordpress\\Plugin\\Shield\\Controller\\Ajax\\Response' => $baseDir . '/src/Controller/Ajax/Response.php',
|
63 |
+
'FernleafSystems\\Wordpress\\Plugin\\Shield\\Controller\\Assets\\Enqueue' => $baseDir . '/src/Controller/Assets/Enqueue.php',
|
64 |
+
'FernleafSystems\\Wordpress\\Plugin\\Shield\\Controller\\Assets\\Urls' => $baseDir . '/src/Controller/Assets/Urls.php',
|
65 |
'FernleafSystems\\Wordpress\\Plugin\\Shield\\Controller\\Config\\ConfigVO' => $baseDir . '/src/Controller/Config/ConfigVO.php',
|
66 |
'FernleafSystems\\Wordpress\\Plugin\\Shield\\Controller\\Config\\Ops\\LoadConfig' => $baseDir . '/src/Controller/Config/Ops/LoadConfig.php',
|
67 |
'FernleafSystems\\Wordpress\\Plugin\\Shield\\Controller\\Config\\Ops\\Read' => $baseDir . '/src/Controller/Config/Ops/Read.php',
|
206 |
'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\BaseShield\\UI' => $baseDir . '/src/Modules/BaseShield/UI.php',
|
207 |
'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Base\\AdminNotices' => $baseDir . '/src/Modules/Base/AdminNotices.php',
|
208 |
'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Base\\AjaxHandler' => $baseDir . '/src/Modules/Base/AjaxHandler.php',
|
|
|
|
|
209 |
'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Base\\BaseProcessor' => $baseDir . '/src/Modules/Base/BaseProcessor.php',
|
|
|
210 |
'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Base\\Debug' => $baseDir . '/src/Modules/Base/Debug.php',
|
211 |
'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Base\\Insights\\OverviewCards' => $baseDir . '/src/Modules/Base/Insights/OverviewCards.php',
|
212 |
'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Base\\ModCon' => $baseDir . '/src/Modules/Base/ModCon.php',
|
214 |
'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Base\\Options\\OptValueSanitize' => $baseDir . '/src/Modules/Base/Options/OptValueSanitize.php',
|
215 |
'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Base\\Processor' => $baseDir . '/src/Modules/Base/Processor.php',
|
216 |
'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Base\\Reporting' => $baseDir . '/src/Modules/Base/Reporting.php',
|
|
|
|
|
217 |
'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Base\\Strings' => $baseDir . '/src/Modules/Base/Strings.php',
|
218 |
'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Base\\UI' => $baseDir . '/src/Modules/Base/UI.php',
|
219 |
'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Base\\Upgrade' => $baseDir . '/src/Modules/Base/Upgrade.php',
|
267 |
'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Firewall\\UI' => $baseDir . '/src/Modules/Firewall/UI.php',
|
268 |
'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\GeoIp\\Lookup' => $baseDir . '/src/Modules/GeoIp/Lookup.php',
|
269 |
'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\HackGuard\\AjaxHandler' => $baseDir . '/src/Modules/HackGuard/AjaxHandler.php',
|
270 |
+
'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\HackGuard\\Debug' => $baseDir . '/src/Modules/HackGuard/Debug.php',
|
271 |
'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\HackGuard\\Insights\\OverviewCards' => $baseDir . '/src/Modules/HackGuard/Insights/OverviewCards.php',
|
272 |
'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\HackGuard\\Lib\\FileLocker\\File' => $baseDir . '/src/Modules/HackGuard/Lib/FileLocker/File.php',
|
273 |
'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\HackGuard\\Lib\\FileLocker\\FileLockerController' => $baseDir . '/src/Modules/HackGuard/Lib/FileLocker/FileLockerController.php',
|
426 |
'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\License\\Lib\\LicenseEmails' => $baseDir . '/src/Modules/License/Lib/LicenseEmails.php',
|
427 |
'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\License\\Lib\\LicenseHandler' => $baseDir . '/src/Modules/License/Lib/LicenseHandler.php',
|
428 |
'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\License\\Lib\\LookupRequest' => $baseDir . '/src/Modules/License/Lib/LookupRequest.php',
|
429 |
+
'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\License\\Lib\\PluginNameSuffix' => $baseDir . '/src/Modules/License/Lib/PluginNameSuffix.php',
|
430 |
'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\License\\Lib\\Verify' => $baseDir . '/src/Modules/License/Lib/Verify.php',
|
431 |
'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\License\\Lib\\WpHashes\\ApiTokenManager' => $baseDir . '/src/Modules/License/Lib/WpHashes/ApiTokenManager.php',
|
432 |
'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\License\\ModCon' => $baseDir . '/src/Modules/License/ModCon.php',
|
748 |
'FernleafSystems\\Wordpress\\Plugin\\Shield\\Utilities\\ReCaptcha\\Enqueue' => $baseDir . '/src/Utilities/ReCaptcha/Enqueue.php',
|
749 |
'FernleafSystems\\Wordpress\\Plugin\\Shield\\Utilities\\ReCaptcha\\TestRequest' => $baseDir . '/src/Utilities/ReCaptcha/TestRequest.php',
|
750 |
'FernleafSystems\\Wordpress\\Plugin\\Shield\\Utilities\\ReCaptcha\\WordpressPost' => $baseDir . '/src/Utilities/ReCaptcha/WordpressPost.php',
|
751 |
+
'FernleafSystems\\Wordpress\\Plugin\\Shield\\Utilities\\Time\\WorldTimeApi' => $baseDir . '/src/Utilities/Time/WorldTimeApi.php',
|
752 |
'FernleafSystems\\Wordpress\\Plugin\\Shield\\Utilities\\Tool\\FormatBytes' => $baseDir . '/src/Utilities/Tool/FormatBytes.php',
|
753 |
'FernleafSystems\\Wordpress\\Plugin\\Shield\\Utilities\\Tool\\IpListSort' => $baseDir . '/src/Utilities/Tool/IpListSort.php',
|
754 |
'FernleafSystems\\Wordpress\\Services\\Core\\AdminNotices' => $vendorDir . '/fernleafsystems/wordpress-services/src/Core/AdminNotices.php',
|
900 |
'FernleafSystems\\Wordpress\\Services\\Utilities\\WpOrg\\Wp\\Versions' => $vendorDir . '/fernleafsystems/wordpress-services/src/Utilities/WpOrg/Wp/Versions.php',
|
901 |
'Html2Text\\Html2Text' => $vendorDir . '/soundasleep/html2text/src/Html2Text.php',
|
902 |
'Html2Text\\Html2TextException' => $vendorDir . '/soundasleep/html2text/src/Html2TextException.php',
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
903 |
'ICWP_WPSF_Processor_AuditTrail_Auditor' => $baseDir . '/../processors/audit_trail_auditor.php',
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
904 |
'ICWP_WPSF_Wizard_Base' => $baseDir . '/../wizards/base.php',
|
905 |
'ICWP_WPSF_Wizard_BaseWpsf' => $baseDir . '/../wizards/base_wpsf.php',
|
906 |
'ICWP_WPSF_Wizard_LoginProtect' => $baseDir . '/../wizards/login_protect.php',
|
src/lib/vendor/composer/autoload_real.php
CHANGED
@@ -2,7 +2,7 @@
|
|
2 |
|
3 |
// autoload_real.php @generated by Composer
|
4 |
|
5 |
-
class
|
6 |
{
|
7 |
private static $loader;
|
8 |
|
@@ -22,15 +22,17 @@ class ComposerAutoloaderInit0b573d8879ea3b08a114d68dbb7a4533
|
|
22 |
return self::$loader;
|
23 |
}
|
24 |
|
25 |
-
|
26 |
-
|
27 |
-
|
|
|
|
|
28 |
|
29 |
$useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded());
|
30 |
if ($useStaticLoader) {
|
31 |
-
|
32 |
|
33 |
-
call_user_func(\Composer\Autoload\
|
34 |
} else {
|
35 |
$map = require __DIR__ . '/autoload_namespaces.php';
|
36 |
foreach ($map as $namespace => $path) {
|
@@ -51,19 +53,19 @@ class ComposerAutoloaderInit0b573d8879ea3b08a114d68dbb7a4533
|
|
51 |
$loader->register(true);
|
52 |
|
53 |
if ($useStaticLoader) {
|
54 |
-
$includeFiles = Composer\Autoload\
|
55 |
} else {
|
56 |
$includeFiles = require __DIR__ . '/autoload_files.php';
|
57 |
}
|
58 |
foreach ($includeFiles as $fileIdentifier => $file) {
|
59 |
-
|
60 |
}
|
61 |
|
62 |
return $loader;
|
63 |
}
|
64 |
}
|
65 |
|
66 |
-
function
|
67 |
{
|
68 |
if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) {
|
69 |
require $file;
|
2 |
|
3 |
// autoload_real.php @generated by Composer
|
4 |
|
5 |
+
class ComposerAutoloaderInit4fc2c6daaffaf40b64b79b6d26830171
|
6 |
{
|
7 |
private static $loader;
|
8 |
|
22 |
return self::$loader;
|
23 |
}
|
24 |
|
25 |
+
require __DIR__ . '/platform_check.php';
|
26 |
+
|
27 |
+
spl_autoload_register(array('ComposerAutoloaderInit4fc2c6daaffaf40b64b79b6d26830171', 'loadClassLoader'), true, true);
|
28 |
+
self::$loader = $loader = new \Composer\Autoload\ClassLoader(\dirname(\dirname(__FILE__)));
|
29 |
+
spl_autoload_unregister(array('ComposerAutoloaderInit4fc2c6daaffaf40b64b79b6d26830171', 'loadClassLoader'));
|
30 |
|
31 |
$useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded());
|
32 |
if ($useStaticLoader) {
|
33 |
+
require __DIR__ . '/autoload_static.php';
|
34 |
|
35 |
+
call_user_func(\Composer\Autoload\ComposerStaticInit4fc2c6daaffaf40b64b79b6d26830171::getInitializer($loader));
|
36 |
} else {
|
37 |
$map = require __DIR__ . '/autoload_namespaces.php';
|
38 |
foreach ($map as $namespace => $path) {
|
53 |
$loader->register(true);
|
54 |
|
55 |
if ($useStaticLoader) {
|
56 |
+
$includeFiles = Composer\Autoload\ComposerStaticInit4fc2c6daaffaf40b64b79b6d26830171::$files;
|
57 |
} else {
|
58 |
$includeFiles = require __DIR__ . '/autoload_files.php';
|
59 |
}
|
60 |
foreach ($includeFiles as $fileIdentifier => $file) {
|
61 |
+
composerRequire4fc2c6daaffaf40b64b79b6d26830171($fileIdentifier, $file);
|
62 |
}
|
63 |
|
64 |
return $loader;
|
65 |
}
|
66 |
}
|
67 |
|
68 |
+
function composerRequire4fc2c6daaffaf40b64b79b6d26830171($fileIdentifier, $file)
|
69 |
{
|
70 |
if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) {
|
71 |
require $file;
|
src/lib/vendor/composer/autoload_static.php
CHANGED
@@ -4,7 +4,7 @@
|
|
4 |
|
5 |
namespace Composer\Autoload;
|
6 |
|
7 |
-
class
|
8 |
{
|
9 |
public static $files = array (
|
10 |
'0e6d7bf4a5811bfa5cf40c5ccd6fae6a' => __DIR__ . '/..' . '/symfony/polyfill-mbstring/bootstrap.php',
|
@@ -181,6 +181,7 @@ class ComposerStaticInit0b573d8879ea3b08a114d68dbb7a4533
|
|
181 |
'Carbon\\Laravel\\ServiceProvider' => __DIR__ . '/..' . '/nesbot/carbon/src/Carbon/Laravel/ServiceProvider.php',
|
182 |
'Carbon\\Translator' => __DIR__ . '/..' . '/nesbot/carbon/src/Carbon/Translator.php',
|
183 |
'Carbon\\Upgrade' => __DIR__ . '/..' . '/nesbot/carbon/src/Carbon/Upgrade.php',
|
|
|
184 |
'Dolondro\\GoogleAuthenticator\\GoogleAuthenticator' => __DIR__ . '/..' . '/dolondro/google-authenticator/src/GoogleAuthenticator.php',
|
185 |
'Dolondro\\GoogleAuthenticator\\QrImageGenerator\\EndroidQrImageGenerator' => __DIR__ . '/..' . '/dolondro/google-authenticator/src/QrImageGenerator/EndroidQrImageGenerator.php',
|
186 |
'Dolondro\\GoogleAuthenticator\\QrImageGenerator\\GoogleQrImageGenerator' => __DIR__ . '/..' . '/dolondro/google-authenticator/src/QrImageGenerator/GoogleQrImageGenerator.php',
|
@@ -188,10 +189,12 @@ class ComposerStaticInit0b573d8879ea3b08a114d68dbb7a4533
|
|
188 |
'Dolondro\\GoogleAuthenticator\\Secret' => __DIR__ . '/..' . '/dolondro/google-authenticator/src/Secret.php',
|
189 |
'Dolondro\\GoogleAuthenticator\\SecretFactory' => __DIR__ . '/..' . '/dolondro/google-authenticator/src/SecretFactory.php',
|
190 |
'Elliotchance\\Iterator\\AbstractPagedIterator' => __DIR__ . '/..' . '/elliotchance/iterator/src/Elliotchance/Iterator/AbstractPagedIterator.php',
|
191 |
-
'Elliotchance\\Iterator\\PagedIterator1' => __DIR__ . '/..' . '/elliotchance/iterator/tests/Elliotchance/Iterator/PagedIteratorTest.php',
|
192 |
-
'Elliotchance\\Iterator\\PagedIterator2' => __DIR__ . '/..' . '/elliotchance/iterator/tests/Elliotchance/Iterator/PagedIteratorTest.php',
|
193 |
'Elliotchance\\Iterator\\PagedIteratorTest' => __DIR__ . '/..' . '/elliotchance/iterator/tests/Elliotchance/Iterator/PagedIteratorTest.php',
|
|
|
|
|
194 |
'FernleafSystems\\Utilities\\Data\\Adapter\\StdClassAdapter' => __DIR__ . '/..' . '/fernleafsystems/utilities/src/Data/Adapter/StdClassAdapter.php',
|
|
|
|
|
195 |
'FernleafSystems\\Utilities\\Logic\\OneTimeExecute' => __DIR__ . '/..' . '/fernleafsystems/utilities/src/Logic/OneTimeExecute.php',
|
196 |
'FernleafSystems\\Utilities\\Response' => __DIR__ . '/..' . '/fernleafsystems/utilities/src/Response.php',
|
197 |
'FernleafSystems\\Wordpress\\Plugin\\Shield\\ChangeTrack\\Diff\\Base' => __DIR__ . '/../..' . '/src/ChangeTrack/Diff/Base.php',
|
@@ -219,6 +222,13 @@ class ComposerStaticInit0b573d8879ea3b08a114d68dbb7a4533
|
|
219 |
'FernleafSystems\\Wordpress\\Plugin\\Shield\\ChangeTrack\\Snapshot\\BuildUsers' => __DIR__ . '/../..' . '/src/ChangeTrack/Snapshot/BuildUsers.php',
|
220 |
'FernleafSystems\\Wordpress\\Plugin\\Shield\\ChangeTrack\\Snapshot\\Collate' => __DIR__ . '/../..' . '/src/ChangeTrack/Snapshot/Collate.php',
|
221 |
'FernleafSystems\\Wordpress\\Plugin\\Shield\\ChangeTrack\\Snapshot\\SnapshotsConsumer' => __DIR__ . '/../..' . '/src/ChangeTrack/Snapshot/SnapshotsConsumer.php',
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
222 |
'FernleafSystems\\Wordpress\\Plugin\\Shield\\Controller\\Config\\ConfigVO' => __DIR__ . '/../..' . '/src/Controller/Config/ConfigVO.php',
|
223 |
'FernleafSystems\\Wordpress\\Plugin\\Shield\\Controller\\Config\\Ops\\LoadConfig' => __DIR__ . '/../..' . '/src/Controller/Config/Ops/LoadConfig.php',
|
224 |
'FernleafSystems\\Wordpress\\Plugin\\Shield\\Controller\\Config\\Ops\\Read' => __DIR__ . '/../..' . '/src/Controller/Config/Ops/Read.php',
|
@@ -363,10 +373,7 @@ class ComposerStaticInit0b573d8879ea3b08a114d68dbb7a4533
|
|
363 |
'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\BaseShield\\UI' => __DIR__ . '/../..' . '/src/Modules/BaseShield/UI.php',
|
364 |
'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Base\\AdminNotices' => __DIR__ . '/../..' . '/src/Modules/Base/AdminNotices.php',
|
365 |
'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Base\\AjaxHandler' => __DIR__ . '/../..' . '/src/Modules/Base/AjaxHandler.php',
|
366 |
-
'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Base\\AjaxHandlerBase' => __DIR__ . '/../..' . '/src/Modules/Base/AjaxHandlerBase.php',
|
367 |
-
'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Base\\AjaxHandlerShield' => __DIR__ . '/../..' . '/src/Modules/Base/AjaxHandlerShield.php',
|
368 |
'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Base\\BaseProcessor' => __DIR__ . '/../..' . '/src/Modules/Base/BaseProcessor.php',
|
369 |
-
'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Base\\BaseReporting' => __DIR__ . '/../..' . '/src/Modules/Base/BaseReporting.php',
|
370 |
'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Base\\Debug' => __DIR__ . '/../..' . '/src/Modules/Base/Debug.php',
|
371 |
'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Base\\Insights\\OverviewCards' => __DIR__ . '/../..' . '/src/Modules/Base/Insights/OverviewCards.php',
|
372 |
'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Base\\ModCon' => __DIR__ . '/../..' . '/src/Modules/Base/ModCon.php',
|
@@ -374,8 +381,6 @@ class ComposerStaticInit0b573d8879ea3b08a114d68dbb7a4533
|
|
374 |
'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Base\\Options\\OptValueSanitize' => __DIR__ . '/../..' . '/src/Modules/Base/Options/OptValueSanitize.php',
|
375 |
'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Base\\Processor' => __DIR__ . '/../..' . '/src/Modules/Base/Processor.php',
|
376 |
'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Base\\Reporting' => __DIR__ . '/../..' . '/src/Modules/Base/Reporting.php',
|
377 |
-
'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Base\\ShieldOptions' => __DIR__ . '/../..' . '/src/Modules/Base/ShieldOptions.php',
|
378 |
-
'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Base\\ShieldUI' => __DIR__ . '/../..' . '/src/Modules/Base/ShieldUI.php',
|
379 |
'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Base\\Strings' => __DIR__ . '/../..' . '/src/Modules/Base/Strings.php',
|
380 |
'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Base\\UI' => __DIR__ . '/../..' . '/src/Modules/Base/UI.php',
|
381 |
'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Base\\Upgrade' => __DIR__ . '/../..' . '/src/Modules/Base/Upgrade.php',
|
@@ -429,6 +434,7 @@ class ComposerStaticInit0b573d8879ea3b08a114d68dbb7a4533
|
|
429 |
'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Firewall\\UI' => __DIR__ . '/../..' . '/src/Modules/Firewall/UI.php',
|
430 |
'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\GeoIp\\Lookup' => __DIR__ . '/../..' . '/src/Modules/GeoIp/Lookup.php',
|
431 |
'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\HackGuard\\AjaxHandler' => __DIR__ . '/../..' . '/src/Modules/HackGuard/AjaxHandler.php',
|
|
|
432 |
'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\HackGuard\\Insights\\OverviewCards' => __DIR__ . '/../..' . '/src/Modules/HackGuard/Insights/OverviewCards.php',
|
433 |
'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\HackGuard\\Lib\\FileLocker\\File' => __DIR__ . '/../..' . '/src/Modules/HackGuard/Lib/FileLocker/File.php',
|
434 |
'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\HackGuard\\Lib\\FileLocker\\FileLockerController' => __DIR__ . '/../..' . '/src/Modules/HackGuard/Lib/FileLocker/FileLockerController.php',
|
@@ -587,6 +593,7 @@ class ComposerStaticInit0b573d8879ea3b08a114d68dbb7a4533
|
|
587 |
'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\License\\Lib\\LicenseEmails' => __DIR__ . '/../..' . '/src/Modules/License/Lib/LicenseEmails.php',
|
588 |
'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\License\\Lib\\LicenseHandler' => __DIR__ . '/../..' . '/src/Modules/License/Lib/LicenseHandler.php',
|
589 |
'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\License\\Lib\\LookupRequest' => __DIR__ . '/../..' . '/src/Modules/License/Lib/LookupRequest.php',
|
|
|
590 |
'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\License\\Lib\\Verify' => __DIR__ . '/../..' . '/src/Modules/License/Lib/Verify.php',
|
591 |
'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\License\\Lib\\WpHashes\\ApiTokenManager' => __DIR__ . '/../..' . '/src/Modules/License/Lib/WpHashes/ApiTokenManager.php',
|
592 |
'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\License\\ModCon' => __DIR__ . '/../..' . '/src/Modules/License/ModCon.php',
|
@@ -908,6 +915,7 @@ class ComposerStaticInit0b573d8879ea3b08a114d68dbb7a4533
|
|
908 |
'FernleafSystems\\Wordpress\\Plugin\\Shield\\Utilities\\ReCaptcha\\Enqueue' => __DIR__ . '/../..' . '/src/Utilities/ReCaptcha/Enqueue.php',
|
909 |
'FernleafSystems\\Wordpress\\Plugin\\Shield\\Utilities\\ReCaptcha\\TestRequest' => __DIR__ . '/../..' . '/src/Utilities/ReCaptcha/TestRequest.php',
|
910 |
'FernleafSystems\\Wordpress\\Plugin\\Shield\\Utilities\\ReCaptcha\\WordpressPost' => __DIR__ . '/../..' . '/src/Utilities/ReCaptcha/WordpressPost.php',
|
|
|
911 |
'FernleafSystems\\Wordpress\\Plugin\\Shield\\Utilities\\Tool\\FormatBytes' => __DIR__ . '/../..' . '/src/Utilities/Tool/FormatBytes.php',
|
912 |
'FernleafSystems\\Wordpress\\Plugin\\Shield\\Utilities\\Tool\\IpListSort' => __DIR__ . '/../..' . '/src/Utilities/Tool/IpListSort.php',
|
913 |
'FernleafSystems\\Wordpress\\Services\\Core\\AdminNotices' => __DIR__ . '/..' . '/fernleafsystems/wordpress-services/src/Core/AdminNotices.php',
|
@@ -1059,59 +1067,7 @@ class ComposerStaticInit0b573d8879ea3b08a114d68dbb7a4533
|
|
1059 |
'FernleafSystems\\Wordpress\\Services\\Utilities\\WpOrg\\Wp\\Versions' => __DIR__ . '/..' . '/fernleafsystems/wordpress-services/src/Utilities/WpOrg/Wp/Versions.php',
|
1060 |
'Html2Text\\Html2Text' => __DIR__ . '/..' . '/soundasleep/html2text/src/Html2Text.php',
|
1061 |
'Html2Text\\Html2TextException' => __DIR__ . '/..' . '/soundasleep/html2text/src/Html2TextException.php',
|
1062 |
-
'ICWP_WPSF_FeatureHandler_AdminAccessRestriction' => __DIR__ . '/../..' . '/../features/admin_access_restriction.php',
|
1063 |
-
'ICWP_WPSF_FeatureHandler_AuditTrail' => __DIR__ . '/../..' . '/../features/audit_trail.php',
|
1064 |
-
'ICWP_WPSF_FeatureHandler_Autoupdates' => __DIR__ . '/../..' . '/../features/autoupdates.php',
|
1065 |
-
'ICWP_WPSF_FeatureHandler_Base' => __DIR__ . '/../..' . '/../features/base.php',
|
1066 |
-
'ICWP_WPSF_FeatureHandler_BaseWpsf' => __DIR__ . '/../..' . '/../features/base_wpsf.php',
|
1067 |
-
'ICWP_WPSF_FeatureHandler_CommentsFilter' => __DIR__ . '/../..' . '/../features/comments_filter.php',
|
1068 |
-
'ICWP_WPSF_FeatureHandler_Comms' => __DIR__ . '/../..' . '/../features/comms.php',
|
1069 |
-
'ICWP_WPSF_FeatureHandler_Email' => __DIR__ . '/../..' . '/../features/email.php',
|
1070 |
-
'ICWP_WPSF_FeatureHandler_Events' => __DIR__ . '/../..' . '/../features/events.php',
|
1071 |
-
'ICWP_WPSF_FeatureHandler_Firewall' => __DIR__ . '/../..' . '/../features/firewall.php',
|
1072 |
-
'ICWP_WPSF_FeatureHandler_HackProtect' => __DIR__ . '/../..' . '/../features/hack_protect.php',
|
1073 |
-
'ICWP_WPSF_FeatureHandler_Headers' => __DIR__ . '/../..' . '/../features/headers.php',
|
1074 |
-
'ICWP_WPSF_FeatureHandler_Insights' => __DIR__ . '/../..' . '/../features/insights.php',
|
1075 |
-
'ICWP_WPSF_FeatureHandler_Ips' => __DIR__ . '/../..' . '/../features/ips.php',
|
1076 |
-
'ICWP_WPSF_FeatureHandler_License' => __DIR__ . '/../..' . '/../features/license.php',
|
1077 |
-
'ICWP_WPSF_FeatureHandler_Lockdown' => __DIR__ . '/../..' . '/../features/lockdown.php',
|
1078 |
-
'ICWP_WPSF_FeatureHandler_LoginProtect' => __DIR__ . '/../..' . '/../features/login_protect.php',
|
1079 |
-
'ICWP_WPSF_FeatureHandler_Plugin' => __DIR__ . '/../..' . '/../features/plugin.php',
|
1080 |
-
'ICWP_WPSF_FeatureHandler_Reporting' => __DIR__ . '/../..' . '/../features/reporting.php',
|
1081 |
-
'ICWP_WPSF_FeatureHandler_Sessions' => __DIR__ . '/../..' . '/../features/sessions.php',
|
1082 |
-
'ICWP_WPSF_FeatureHandler_Traffic' => __DIR__ . '/../..' . '/../features/traffic.php',
|
1083 |
-
'ICWP_WPSF_FeatureHandler_UserManagement' => __DIR__ . '/../..' . '/../features/user_management.php',
|
1084 |
-
'ICWP_WPSF_Processor_AdminAccessRestriction' => __DIR__ . '/../..' . '/../processors/admin_access_restriction.php',
|
1085 |
-
'ICWP_WPSF_Processor_AuditTrail' => __DIR__ . '/../..' . '/../processors/audit_trail.php',
|
1086 |
'ICWP_WPSF_Processor_AuditTrail_Auditor' => __DIR__ . '/../..' . '/../processors/audit_trail_auditor.php',
|
1087 |
-
'ICWP_WPSF_Processor_Autoupdates' => __DIR__ . '/../..' . '/../processors/autoupdates.php',
|
1088 |
-
'ICWP_WPSF_Processor_CommentsFilter' => __DIR__ . '/../..' . '/../processors/comments_filter.php',
|
1089 |
-
'ICWP_WPSF_Processor_CommentsFilter_BotSpam' => __DIR__ . '/../..' . '/../processors/commentsfilter_botspam.php',
|
1090 |
-
'ICWP_WPSF_Processor_CommentsFilter_GoogleRecaptcha' => __DIR__ . '/../..' . '/../processors/commentsfilter_googlerecaptcha.php',
|
1091 |
-
'ICWP_WPSF_Processor_Email' => __DIR__ . '/../..' . '/../processors/email.php',
|
1092 |
-
'ICWP_WPSF_Processor_Events' => __DIR__ . '/../..' . '/../processors/events.php',
|
1093 |
-
'ICWP_WPSF_Processor_Firewall' => __DIR__ . '/../..' . '/../processors/firewall.php',
|
1094 |
-
'ICWP_WPSF_Processor_HackProtect' => __DIR__ . '/../..' . '/../processors/hack_protect.php',
|
1095 |
-
'ICWP_WPSF_Processor_HackProtect_Apc' => __DIR__ . '/../..' . '/../processors/hackprotect_scan_apc.php',
|
1096 |
-
'ICWP_WPSF_Processor_HackProtect_Integrity' => __DIR__ . '/../..' . '/../processors/hackprotect_integrity.php',
|
1097 |
-
'ICWP_WPSF_Processor_HackProtect_Mal' => __DIR__ . '/../..' . '/../processors/hackprotect_scan_mal.php',
|
1098 |
-
'ICWP_WPSF_Processor_HackProtect_Ptg' => __DIR__ . '/../..' . '/../processors/hackprotect_scan_ptg.php',
|
1099 |
-
'ICWP_WPSF_Processor_HackProtect_Scanner' => __DIR__ . '/../..' . '/../processors/hackprotect_scanner.php',
|
1100 |
-
'ICWP_WPSF_Processor_HackProtect_Ufc' => __DIR__ . '/../..' . '/../processors/hackprotect_scan_ufc.php',
|
1101 |
-
'ICWP_WPSF_Processor_HackProtect_Wcf' => __DIR__ . '/../..' . '/../processors/hackprotect_scan_wcf.php',
|
1102 |
-
'ICWP_WPSF_Processor_HackProtect_Wpv' => __DIR__ . '/../..' . '/../processors/hackprotect_scan_wpv.php',
|
1103 |
-
'ICWP_WPSF_Processor_Headers' => __DIR__ . '/../..' . '/../processors/headers.php',
|
1104 |
-
'ICWP_WPSF_Processor_Lockdown' => __DIR__ . '/../..' . '/../processors/lockdown.php',
|
1105 |
-
'ICWP_WPSF_Processor_LoginProtect' => __DIR__ . '/../..' . '/../processors/login_protect.php',
|
1106 |
-
'ICWP_WPSF_Processor_LoginProtect_WpLogin' => __DIR__ . '/../..' . '/../processors/loginprotect_wplogin.php',
|
1107 |
-
'ICWP_WPSF_Processor_Plugin' => __DIR__ . '/../..' . '/../processors/plugin.php',
|
1108 |
-
'ICWP_WPSF_Processor_Plugin_Tracking' => __DIR__ . '/../..' . '/../processors/plugin_tracking.php',
|
1109 |
-
'ICWP_WPSF_Processor_ScanBase' => __DIR__ . '/../..' . '/../processors/hackprotect_scan_base.php',
|
1110 |
-
'ICWP_WPSF_Processor_Sessions' => __DIR__ . '/../..' . '/../processors/sessions.php',
|
1111 |
-
'ICWP_WPSF_Processor_Traffic' => __DIR__ . '/../..' . '/../processors/traffic.php',
|
1112 |
-
'ICWP_WPSF_Processor_UserManagement' => __DIR__ . '/../..' . '/../processors/user_management.php',
|
1113 |
-
'ICWP_WPSF_Processor_UserManagement_Passwords' => __DIR__ . '/../..' . '/../processors/usermanagement_passwords.php',
|
1114 |
-
'ICWP_WPSF_Processor_UserManagement_Sessions' => __DIR__ . '/../..' . '/../processors/usermanagement_sessions.php',
|
1115 |
'ICWP_WPSF_Wizard_Base' => __DIR__ . '/../..' . '/../wizards/base.php',
|
1116 |
'ICWP_WPSF_Wizard_BaseWpsf' => __DIR__ . '/../..' . '/../wizards/base_wpsf.php',
|
1117 |
'ICWP_WPSF_Wizard_LoginProtect' => __DIR__ . '/../..' . '/../wizards/login_protect.php',
|
@@ -1682,12 +1638,12 @@ class ComposerStaticInit0b573d8879ea3b08a114d68dbb7a4533
|
|
1682 |
public static function getInitializer(ClassLoader $loader)
|
1683 |
{
|
1684 |
return \Closure::bind(function () use ($loader) {
|
1685 |
-
$loader->prefixLengthsPsr4 =
|
1686 |
-
$loader->prefixDirsPsr4 =
|
1687 |
-
$loader->fallbackDirsPsr4 =
|
1688 |
-
$loader->prefixesPsr0 =
|
1689 |
-
$loader->fallbackDirsPsr0 =
|
1690 |
-
$loader->classMap =
|
1691 |
|
1692 |
}, null, ClassLoader::class);
|
1693 |
}
|
4 |
|
5 |
namespace Composer\Autoload;
|
6 |
|
7 |
+
class ComposerStaticInit4fc2c6daaffaf40b64b79b6d26830171
|
8 |
{
|
9 |
public static $files = array (
|
10 |
'0e6d7bf4a5811bfa5cf40c5ccd6fae6a' => __DIR__ . '/..' . '/symfony/polyfill-mbstring/bootstrap.php',
|
181 |
'Carbon\\Laravel\\ServiceProvider' => __DIR__ . '/..' . '/nesbot/carbon/src/Carbon/Laravel/ServiceProvider.php',
|
182 |
'Carbon\\Translator' => __DIR__ . '/..' . '/nesbot/carbon/src/Carbon/Translator.php',
|
183 |
'Carbon\\Upgrade' => __DIR__ . '/..' . '/nesbot/carbon/src/Carbon/Upgrade.php',
|
184 |
+
'Composer\\InstalledVersions' => __DIR__ . '/..' . '/composer/InstalledVersions.php',
|
185 |
'Dolondro\\GoogleAuthenticator\\GoogleAuthenticator' => __DIR__ . '/..' . '/dolondro/google-authenticator/src/GoogleAuthenticator.php',
|
186 |
'Dolondro\\GoogleAuthenticator\\QrImageGenerator\\EndroidQrImageGenerator' => __DIR__ . '/..' . '/dolondro/google-authenticator/src/QrImageGenerator/EndroidQrImageGenerator.php',
|
187 |
'Dolondro\\GoogleAuthenticator\\QrImageGenerator\\GoogleQrImageGenerator' => __DIR__ . '/..' . '/dolondro/google-authenticator/src/QrImageGenerator/GoogleQrImageGenerator.php',
|
189 |
'Dolondro\\GoogleAuthenticator\\Secret' => __DIR__ . '/..' . '/dolondro/google-authenticator/src/Secret.php',
|
190 |
'Dolondro\\GoogleAuthenticator\\SecretFactory' => __DIR__ . '/..' . '/dolondro/google-authenticator/src/SecretFactory.php',
|
191 |
'Elliotchance\\Iterator\\AbstractPagedIterator' => __DIR__ . '/..' . '/elliotchance/iterator/src/Elliotchance/Iterator/AbstractPagedIterator.php',
|
|
|
|
|
192 |
'Elliotchance\\Iterator\\PagedIteratorTest' => __DIR__ . '/..' . '/elliotchance/iterator/tests/Elliotchance/Iterator/PagedIteratorTest.php',
|
193 |
+
'FernleafSystems\\Utilities\\Data\\Adapter\\DynamicProperties' => __DIR__ . '/..' . '/fernleafsystems/utilities/src/Data/Adapter/DynamicProperties.php',
|
194 |
+
'FernleafSystems\\Utilities\\Data\\Adapter\\DynamicPropertiesClass' => __DIR__ . '/..' . '/fernleafsystems/utilities/src/Data/Adapter/DynamicPropertiesClass.php',
|
195 |
'FernleafSystems\\Utilities\\Data\\Adapter\\StdClassAdapter' => __DIR__ . '/..' . '/fernleafsystems/utilities/src/Data/Adapter/StdClassAdapter.php',
|
196 |
+
'FernleafSystems\\Utilities\\Data\\CaptureOutput' => __DIR__ . '/..' . '/fernleafsystems/utilities/src/Data/CaptureOutput.php',
|
197 |
+
'FernleafSystems\\Utilities\\Logic\\ExecOnce' => __DIR__ . '/..' . '/fernleafsystems/utilities/src/Logic/ExecOnce.php',
|
198 |
'FernleafSystems\\Utilities\\Logic\\OneTimeExecute' => __DIR__ . '/..' . '/fernleafsystems/utilities/src/Logic/OneTimeExecute.php',
|
199 |
'FernleafSystems\\Utilities\\Response' => __DIR__ . '/..' . '/fernleafsystems/utilities/src/Response.php',
|
200 |
'FernleafSystems\\Wordpress\\Plugin\\Shield\\ChangeTrack\\Diff\\Base' => __DIR__ . '/../..' . '/src/ChangeTrack/Diff/Base.php',
|
222 |
'FernleafSystems\\Wordpress\\Plugin\\Shield\\ChangeTrack\\Snapshot\\BuildUsers' => __DIR__ . '/../..' . '/src/ChangeTrack/Snapshot/BuildUsers.php',
|
223 |
'FernleafSystems\\Wordpress\\Plugin\\Shield\\ChangeTrack\\Snapshot\\Collate' => __DIR__ . '/../..' . '/src/ChangeTrack/Snapshot/Collate.php',
|
224 |
'FernleafSystems\\Wordpress\\Plugin\\Shield\\ChangeTrack\\Snapshot\\SnapshotsConsumer' => __DIR__ . '/../..' . '/src/ChangeTrack/Snapshot/SnapshotsConsumer.php',
|
225 |
+
'FernleafSystems\\Wordpress\\Plugin\\Shield\\Controller\\Admin\\AdminBarMenu' => __DIR__ . '/../..' . '/src/Controller/Admin/AdminBarMenu.php',
|
226 |
+
'FernleafSystems\\Wordpress\\Plugin\\Shield\\Controller\\Admin\\DashboardWidget' => __DIR__ . '/../..' . '/src/Controller/Admin/DashboardWidget.php',
|
227 |
+
'FernleafSystems\\Wordpress\\Plugin\\Shield\\Controller\\Admin\\MainAdminMenu' => __DIR__ . '/../..' . '/src/Controller/Admin/MainAdminMenu.php',
|
228 |
+
'FernleafSystems\\Wordpress\\Plugin\\Shield\\Controller\\Ajax\\Init' => __DIR__ . '/../..' . '/src/Controller/Ajax/Init.php',
|
229 |
+
'FernleafSystems\\Wordpress\\Plugin\\Shield\\Controller\\Ajax\\Response' => __DIR__ . '/../..' . '/src/Controller/Ajax/Response.php',
|
230 |
+
'FernleafSystems\\Wordpress\\Plugin\\Shield\\Controller\\Assets\\Enqueue' => __DIR__ . '/../..' . '/src/Controller/Assets/Enqueue.php',
|
231 |
+
'FernleafSystems\\Wordpress\\Plugin\\Shield\\Controller\\Assets\\Urls' => __DIR__ . '/../..' . '/src/Controller/Assets/Urls.php',
|
232 |
'FernleafSystems\\Wordpress\\Plugin\\Shield\\Controller\\Config\\ConfigVO' => __DIR__ . '/../..' . '/src/Controller/Config/ConfigVO.php',
|
233 |
'FernleafSystems\\Wordpress\\Plugin\\Shield\\Controller\\Config\\Ops\\LoadConfig' => __DIR__ . '/../..' . '/src/Controller/Config/Ops/LoadConfig.php',
|
234 |
'FernleafSystems\\Wordpress\\Plugin\\Shield\\Controller\\Config\\Ops\\Read' => __DIR__ . '/../..' . '/src/Controller/Config/Ops/Read.php',
|
373 |
'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\BaseShield\\UI' => __DIR__ . '/../..' . '/src/Modules/BaseShield/UI.php',
|
374 |
'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Base\\AdminNotices' => __DIR__ . '/../..' . '/src/Modules/Base/AdminNotices.php',
|
375 |
'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Base\\AjaxHandler' => __DIR__ . '/../..' . '/src/Modules/Base/AjaxHandler.php',
|
|
|
|
|
376 |
'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Base\\BaseProcessor' => __DIR__ . '/../..' . '/src/Modules/Base/BaseProcessor.php',
|
|
|
377 |
'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Base\\Debug' => __DIR__ . '/../..' . '/src/Modules/Base/Debug.php',
|
378 |
'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Base\\Insights\\OverviewCards' => __DIR__ . '/../..' . '/src/Modules/Base/Insights/OverviewCards.php',
|
379 |
'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Base\\ModCon' => __DIR__ . '/../..' . '/src/Modules/Base/ModCon.php',
|
381 |
'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Base\\Options\\OptValueSanitize' => __DIR__ . '/../..' . '/src/Modules/Base/Options/OptValueSanitize.php',
|
382 |
'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Base\\Processor' => __DIR__ . '/../..' . '/src/Modules/Base/Processor.php',
|
383 |
'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Base\\Reporting' => __DIR__ . '/../..' . '/src/Modules/Base/Reporting.php',
|
|
|
|
|
384 |
'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Base\\Strings' => __DIR__ . '/../..' . '/src/Modules/Base/Strings.php',
|
385 |
'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Base\\UI' => __DIR__ . '/../..' . '/src/Modules/Base/UI.php',
|
386 |
'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Base\\Upgrade' => __DIR__ . '/../..' . '/src/Modules/Base/Upgrade.php',
|
434 |
'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Firewall\\UI' => __DIR__ . '/../..' . '/src/Modules/Firewall/UI.php',
|
435 |
'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\GeoIp\\Lookup' => __DIR__ . '/../..' . '/src/Modules/GeoIp/Lookup.php',
|
436 |
'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\HackGuard\\AjaxHandler' => __DIR__ . '/../..' . '/src/Modules/HackGuard/AjaxHandler.php',
|
437 |
+
'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\HackGuard\\Debug' => __DIR__ . '/../..' . '/src/Modules/HackGuard/Debug.php',
|
438 |
'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\HackGuard\\Insights\\OverviewCards' => __DIR__ . '/../..' . '/src/Modules/HackGuard/Insights/OverviewCards.php',
|
439 |
'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\HackGuard\\Lib\\FileLocker\\File' => __DIR__ . '/../..' . '/src/Modules/HackGuard/Lib/FileLocker/File.php',
|
440 |
'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\HackGuard\\Lib\\FileLocker\\FileLockerController' => __DIR__ . '/../..' . '/src/Modules/HackGuard/Lib/FileLocker/FileLockerController.php',
|
593 |
'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\License\\Lib\\LicenseEmails' => __DIR__ . '/../..' . '/src/Modules/License/Lib/LicenseEmails.php',
|
594 |
'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\License\\Lib\\LicenseHandler' => __DIR__ . '/../..' . '/src/Modules/License/Lib/LicenseHandler.php',
|
595 |
'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\License\\Lib\\LookupRequest' => __DIR__ . '/../..' . '/src/Modules/License/Lib/LookupRequest.php',
|
596 |
+
'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\License\\Lib\\PluginNameSuffix' => __DIR__ . '/../..' . '/src/Modules/License/Lib/PluginNameSuffix.php',
|
597 |
'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\License\\Lib\\Verify' => __DIR__ . '/../..' . '/src/Modules/License/Lib/Verify.php',
|
598 |
'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\License\\Lib\\WpHashes\\ApiTokenManager' => __DIR__ . '/../..' . '/src/Modules/License/Lib/WpHashes/ApiTokenManager.php',
|
599 |
'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\License\\ModCon' => __DIR__ . '/../..' . '/src/Modules/License/ModCon.php',
|
915 |
'FernleafSystems\\Wordpress\\Plugin\\Shield\\Utilities\\ReCaptcha\\Enqueue' => __DIR__ . '/../..' . '/src/Utilities/ReCaptcha/Enqueue.php',
|
916 |
'FernleafSystems\\Wordpress\\Plugin\\Shield\\Utilities\\ReCaptcha\\TestRequest' => __DIR__ . '/../..' . '/src/Utilities/ReCaptcha/TestRequest.php',
|
917 |
'FernleafSystems\\Wordpress\\Plugin\\Shield\\Utilities\\ReCaptcha\\WordpressPost' => __DIR__ . '/../..' . '/src/Utilities/ReCaptcha/WordpressPost.php',
|
918 |
+
'FernleafSystems\\Wordpress\\Plugin\\Shield\\Utilities\\Time\\WorldTimeApi' => __DIR__ . '/../..' . '/src/Utilities/Time/WorldTimeApi.php',
|
919 |
'FernleafSystems\\Wordpress\\Plugin\\Shield\\Utilities\\Tool\\FormatBytes' => __DIR__ . '/../..' . '/src/Utilities/Tool/FormatBytes.php',
|
920 |
'FernleafSystems\\Wordpress\\Plugin\\Shield\\Utilities\\Tool\\IpListSort' => __DIR__ . '/../..' . '/src/Utilities/Tool/IpListSort.php',
|
921 |
'FernleafSystems\\Wordpress\\Services\\Core\\AdminNotices' => __DIR__ . '/..' . '/fernleafsystems/wordpress-services/src/Core/AdminNotices.php',
|
1067 |
'FernleafSystems\\Wordpress\\Services\\Utilities\\WpOrg\\Wp\\Versions' => __DIR__ . '/..' . '/fernleafsystems/wordpress-services/src/Utilities/WpOrg/Wp/Versions.php',
|
1068 |
'Html2Text\\Html2Text' => __DIR__ . '/..' . '/soundasleep/html2text/src/Html2Text.php',
|
1069 |
'Html2Text\\Html2TextException' => __DIR__ . '/..' . '/soundasleep/html2text/src/Html2TextException.php',
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1070 |
'ICWP_WPSF_Processor_AuditTrail_Auditor' => __DIR__ . '/../..' . '/../processors/audit_trail_auditor.php',
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1071 |
'ICWP_WPSF_Wizard_Base' => __DIR__ . '/../..' . '/../wizards/base.php',
|
1072 |
'ICWP_WPSF_Wizard_BaseWpsf' => __DIR__ . '/../..' . '/../wizards/base_wpsf.php',
|
1073 |
'ICWP_WPSF_Wizard_LoginProtect' => __DIR__ . '/../..' . '/../wizards/login_protect.php',
|
1638 |
public static function getInitializer(ClassLoader $loader)
|
1639 |
{
|
1640 |
return \Closure::bind(function () use ($loader) {
|
1641 |
+
$loader->prefixLengthsPsr4 = ComposerStaticInit4fc2c6daaffaf40b64b79b6d26830171::$prefixLengthsPsr4;
|
1642 |
+
$loader->prefixDirsPsr4 = ComposerStaticInit4fc2c6daaffaf40b64b79b6d26830171::$prefixDirsPsr4;
|
1643 |
+
$loader->fallbackDirsPsr4 = ComposerStaticInit4fc2c6daaffaf40b64b79b6d26830171::$fallbackDirsPsr4;
|
1644 |
+
$loader->prefixesPsr0 = ComposerStaticInit4fc2c6daaffaf40b64b79b6d26830171::$prefixesPsr0;
|
1645 |
+
$loader->fallbackDirsPsr0 = ComposerStaticInit4fc2c6daaffaf40b64b79b6d26830171::$fallbackDirsPsr0;
|
1646 |
+
$loader->classMap = ComposerStaticInit4fc2c6daaffaf40b64b79b6d26830171::$classMap;
|
1647 |
|
1648 |
}, null, ClassLoader::class);
|
1649 |
}
|
src/lib/vendor/composer/platform_check.php
ADDED
@@ -0,0 +1,26 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
// platform_check.php @generated by Composer
|
4 |
+
|
5 |
+
$issues = array();
|
6 |
+
|
7 |
+
if (!(PHP_VERSION_ID >= 70000)) {
|
8 |
+
$issues[] = 'Your Composer dependencies require a PHP version ">= 7.0.0". You are running ' . PHP_VERSION . '.';
|
9 |
+
}
|
10 |
+
|
11 |
+
if ($issues) {
|
12 |
+
if (!headers_sent()) {
|
13 |
+
header('HTTP/1.1 500 Internal Server Error');
|
14 |
+
}
|
15 |
+
if (!ini_get('display_errors')) {
|
16 |
+
if (PHP_SAPI === 'cli' || PHP_SAPI === 'phpdbg') {
|
17 |
+
fwrite(STDERR, 'Composer detected issues in your platform:' . PHP_EOL.PHP_EOL . implode(PHP_EOL, $issues) . PHP_EOL.PHP_EOL);
|
18 |
+
} elseif (!headers_sent()) {
|
19 |
+
echo 'Composer detected issues in your platform:' . PHP_EOL.PHP_EOL . str_replace('You are running '.PHP_VERSION.'.', '', implode(PHP_EOL, $issues)) . PHP_EOL.PHP_EOL;
|
20 |
+
}
|
21 |
+
}
|
22 |
+
trigger_error(
|
23 |
+
'Composer detected issues in your platform: ' . implode(' ', $issues),
|
24 |
+
E_USER_ERROR
|
25 |
+
);
|
26 |
+
}
|
src/lib/vendor/fernleafsystems/utilities/src/Data/Adapter/DynamicProperties.php
ADDED
@@ -0,0 +1,74 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
namespace FernleafSystems\Utilities\Data\Adapter;
|
4 |
+
|
5 |
+
/**
|
6 |
+
* Trait DynamicProperties
|
7 |
+
* @package FernleafSystems\Utilities\Data\Adapter
|
8 |
+
*/
|
9 |
+
trait DynamicProperties {
|
10 |
+
|
11 |
+
private $raw = [];
|
12 |
+
|
13 |
+
/**
|
14 |
+
* @param string $key
|
15 |
+
* @return mixed
|
16 |
+
*/
|
17 |
+
public function __get( string $key ) {
|
18 |
+
$data = $this->getRawData();
|
19 |
+
return $data[ $key ] ?? null;
|
20 |
+
}
|
21 |
+
|
22 |
+
public function __isset( string $key ) :bool {
|
23 |
+
return array_key_exists( $key, $this->getRawData() );
|
24 |
+
}
|
25 |
+
|
26 |
+
/**
|
27 |
+
* @param string $key
|
28 |
+
* @param mixed $value
|
29 |
+
* @return $this
|
30 |
+
*/
|
31 |
+
public function __set( string $key, $value ) :self {
|
32 |
+
$data = $this->getRawData();
|
33 |
+
$data[ $key ] = $value;
|
34 |
+
return $this->applyFromArray( $data );
|
35 |
+
}
|
36 |
+
|
37 |
+
/**
|
38 |
+
* @param string $key
|
39 |
+
* @return $this
|
40 |
+
*/
|
41 |
+
public function __unset( string $key ) {
|
42 |
+
$data = $this->getRawData();
|
43 |
+
if ( array_key_exists( $key, $data ) ) {
|
44 |
+
unset( $data[ $key ] );
|
45 |
+
$this->applyFromArray( $data );
|
46 |
+
}
|
47 |
+
return $this;
|
48 |
+
}
|
49 |
+
|
50 |
+
public function applyFromArray( $data, array $restrictedKeys = [] ) :self {
|
51 |
+
if ( !empty( $restrictedKeys ) ) {
|
52 |
+
$data = array_intersect_key( $data, array_flip( $restrictedKeys ) );
|
53 |
+
}
|
54 |
+
$this->raw = $data;
|
55 |
+
return $this;
|
56 |
+
}
|
57 |
+
|
58 |
+
public function reset() :self {
|
59 |
+
$this->raw = [];
|
60 |
+
return $this;
|
61 |
+
}
|
62 |
+
|
63 |
+
public function getRawData() :array {
|
64 |
+
return is_array( $this->raw ) ? $this->raw : [];
|
65 |
+
}
|
66 |
+
|
67 |
+
/**
|
68 |
+
* @return array
|
69 |
+
* @deprecated
|
70 |
+
*/
|
71 |
+
public function getRawDataAsArray() :array {
|
72 |
+
return $this->getRawData();
|
73 |
+
}
|
74 |
+
}
|
src/lib/vendor/fernleafsystems/utilities/src/Data/Adapter/DynamicPropertiesClass.php
ADDED
@@ -0,0 +1,13 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
namespace FernleafSystems\Utilities\Data\Adapter;
|
4 |
+
|
5 |
+
/**
|
6 |
+
* Use this instead of the trait wherever possible for more flexible inheritance
|
7 |
+
* Class DynamicPropertiesBase
|
8 |
+
* @package FernleafSystems\Utilities\Data\Adapter
|
9 |
+
*/
|
10 |
+
class DynamicPropertiesClass {
|
11 |
+
|
12 |
+
use DynamicProperties;
|
13 |
+
}
|
src/lib/vendor/fernleafsystems/utilities/src/Data/Adapter/StdClassAdapter.php
CHANGED
@@ -4,7 +4,8 @@ namespace FernleafSystems\Utilities\Data\Adapter;
|
|
4 |
|
5 |
/**
|
6 |
* Trait StdClassAdapter
|
7 |
-
* @package
|
|
|
8 |
*/
|
9 |
trait StdClassAdapter {
|
10 |
|
@@ -59,7 +60,7 @@ trait StdClassAdapter {
|
|
59 |
* @param array $aRestrictedKeys
|
60 |
* @return $this
|
61 |
*/
|
62 |
-
public function applyFromArray( $aDataValues, $aRestrictedKeys =
|
63 |
if ( !empty( $aRestrictedKeys ) ) {
|
64 |
$aDataValues = array_intersect_key( $aDataValues, array_flip( $aRestrictedKeys ) );
|
65 |
}
|
@@ -71,13 +72,13 @@ trait StdClassAdapter {
|
|
71 |
* @return $this
|
72 |
*/
|
73 |
public function reset() {
|
74 |
-
$this->aRaw =
|
75 |
return $this;
|
76 |
}
|
77 |
|
78 |
/**
|
79 |
-
* @deprecated
|
80 |
* @return \stdClass
|
|
|
81 |
*/
|
82 |
public function getRawData() {
|
83 |
return (object)$this->getRawDataAsArray();
|
@@ -88,7 +89,7 @@ trait StdClassAdapter {
|
|
88 |
*/
|
89 |
public function getRawDataAsArray() {
|
90 |
if ( !is_array( $this->aRaw ) ) {
|
91 |
-
$this->aRaw =
|
92 |
}
|
93 |
return $this->aRaw;
|
94 |
}
|
@@ -107,7 +108,7 @@ trait StdClassAdapter {
|
|
107 |
* @param array $aDefault
|
108 |
* @return array
|
109 |
*/
|
110 |
-
public function getArrayParam( $sKey, $aDefault =
|
111 |
return is_array( $this->{$sKey} ) ? $this->{$sKey} : $aDefault;
|
112 |
}
|
113 |
|
@@ -141,9 +142,9 @@ trait StdClassAdapter {
|
|
141 |
}
|
142 |
|
143 |
/**
|
144 |
-
* @deprecated use applyFromArray()
|
145 |
* @param object $oRaw
|
146 |
* @return $this
|
|
|
147 |
*/
|
148 |
public function setRawData( $oRaw ) {
|
149 |
return $this->applyFromArray( (array)$oRaw );
|
4 |
|
5 |
/**
|
6 |
* Trait StdClassAdapter
|
7 |
+
* @package FernleafSystems\Utilities\Data\Adapter
|
8 |
+
* @deprecated 1.4
|
9 |
*/
|
10 |
trait StdClassAdapter {
|
11 |
|
60 |
* @param array $aRestrictedKeys
|
61 |
* @return $this
|
62 |
*/
|
63 |
+
public function applyFromArray( $aDataValues, $aRestrictedKeys = [] ) {
|
64 |
if ( !empty( $aRestrictedKeys ) ) {
|
65 |
$aDataValues = array_intersect_key( $aDataValues, array_flip( $aRestrictedKeys ) );
|
66 |
}
|
72 |
* @return $this
|
73 |
*/
|
74 |
public function reset() {
|
75 |
+
$this->aRaw = [];
|
76 |
return $this;
|
77 |
}
|
78 |
|
79 |
/**
|
|
|
80 |
* @return \stdClass
|
81 |
+
* @deprecated
|
82 |
*/
|
83 |
public function getRawData() {
|
84 |
return (object)$this->getRawDataAsArray();
|
89 |
*/
|
90 |
public function getRawDataAsArray() {
|
91 |
if ( !is_array( $this->aRaw ) ) {
|
92 |
+
$this->aRaw = [];
|
93 |
}
|
94 |
return $this->aRaw;
|
95 |
}
|
108 |
* @param array $aDefault
|
109 |
* @return array
|
110 |
*/
|
111 |
+
public function getArrayParam( $sKey, $aDefault = [] ) {
|
112 |
return is_array( $this->{$sKey} ) ? $this->{$sKey} : $aDefault;
|
113 |
}
|
114 |
|
142 |
}
|
143 |
|
144 |
/**
|
|
|
145 |
* @param object $oRaw
|
146 |
* @return $this
|
147 |
+
* @deprecated use applyFromArray()
|
148 |
*/
|
149 |
public function setRawData( $oRaw ) {
|
150 |
return $this->applyFromArray( (array)$oRaw );
|
src/lib/vendor/fernleafsystems/utilities/src/Data/CaptureOutput.php
ADDED
@@ -0,0 +1,13 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php declare( strict_types=1 );
|
2 |
+
|
3 |
+
namespace FernleafSystems\Utilities\Data;
|
4 |
+
|
5 |
+
class CaptureOutput {
|
6 |
+
|
7 |
+
public static function Capture( callable $func, array $args = [] ) :string {
|
8 |
+
ob_start();
|
9 |
+
empty( $args ) ? call_user_func( $func ) : call_user_func_array( $func, $args );
|
10 |
+
$out = ob_get_clean();
|
11 |
+
return is_string( $out ) ? $out : 'No output from capture';
|
12 |
+
}
|
13 |
+
}
|
src/lib/vendor/fernleafsystems/utilities/src/Logic/ExecOnce.php
ADDED
@@ -0,0 +1,31 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
namespace FernleafSystems\Utilities\Logic;
|
4 |
+
|
5 |
+
trait ExecOnce {
|
6 |
+
|
7 |
+
private $hasExecuted = false;
|
8 |
+
|
9 |
+
protected function canRun() :bool {
|
10 |
+
return true;
|
11 |
+
}
|
12 |
+
|
13 |
+
public function execute() {
|
14 |
+
if ( !$this->isAlreadyExecuted() && $this->canRun() ) {
|
15 |
+
$this->hasExecuted = true;
|
16 |
+
$this->run();
|
17 |
+
}
|
18 |
+
}
|
19 |
+
|
20 |
+
protected function isAlreadyExecuted() :bool {
|
21 |
+
return (bool)$this->hasExecuted;
|
22 |
+
}
|
23 |
+
|
24 |
+
public function resetExecution() :self {
|
25 |
+
$this->hasExecuted = false;
|
26 |
+
return $this;
|
27 |
+
}
|
28 |
+
|
29 |
+
protected function run() {
|
30 |
+
}
|
31 |
+
}
|
src/lib/vendor/fernleafsystems/wordpress-services/src/Core/AdminNotices.php
CHANGED
@@ -184,10 +184,10 @@ class AdminNotices {
|
|
184 |
}
|
185 |
|
186 |
/**
|
187 |
-
* @param $
|
188 |
*/
|
189 |
-
public function addFlashMessage( $
|
190 |
-
Services::
|
191 |
}
|
192 |
|
193 |
protected function flashNotice() {
|
184 |
}
|
185 |
|
186 |
/**
|
187 |
+
* @param $msg
|
188 |
*/
|
189 |
+
public function addFlashMessage( string $msg ) {
|
190 |
+
Services::Response()->cookieSet( $this->getActionPrefix().'flash', esc_attr( $msg ) );
|
191 |
}
|
192 |
|
193 |
protected function flashNotice() {
|
src/lib/vendor/fernleafsystems/wordpress-services/src/Core/CoreFileHashes.php
CHANGED
@@ -14,81 +14,78 @@ class CoreFileHashes {
|
|
14 |
/**
|
15 |
* @var array
|
16 |
*/
|
17 |
-
private $
|
18 |
|
19 |
/**
|
20 |
* Filters out wp-content plugins/themes data.
|
21 |
-
* @return
|
22 |
*/
|
23 |
-
public function getHashes() {
|
24 |
-
if ( !isset( $this->
|
25 |
-
$
|
26 |
|
27 |
-
$this->
|
28 |
-
$
|
29 |
array_flip( array_filter(
|
30 |
-
array_keys( $
|
31 |
-
function ( $
|
32 |
-
return preg_match( '#wp-content/(plugins|themes)#i', $
|
33 |
}
|
34 |
) )
|
35 |
);
|
36 |
}
|
37 |
-
return $this->
|
38 |
}
|
39 |
|
40 |
/**
|
41 |
-
* @param string $
|
42 |
* @return string|null
|
43 |
*/
|
44 |
-
public function getFileHash( $
|
45 |
-
$sNorm = $this->getFileFragment( $
|
46 |
return $this->isCoreFile( $sNorm ) ? $this->getHashes()[ $sNorm ] : null;
|
47 |
}
|
48 |
|
49 |
/**
|
50 |
-
* @param string $
|
51 |
* @return string
|
52 |
*/
|
53 |
-
public function getFileFragment( $
|
54 |
-
return Services::WpFs()->getPathRelativeToAbsPath( $
|
55 |
}
|
56 |
|
57 |
/**
|
58 |
-
* @param string $
|
59 |
* @return string
|
60 |
*/
|
61 |
-
public function getAbsolutePathFromFragment( $
|
62 |
-
return wp_normalize_path( path_join( ABSPATH, $this->getFileFragment( $
|
63 |
}
|
64 |
|
65 |
/**
|
66 |
-
* @param string $
|
67 |
* @return bool
|
68 |
*/
|
69 |
-
public function isCoreFile( $
|
70 |
-
return array_key_exists( $this->getFileFragment( $
|
71 |
}
|
72 |
|
73 |
/**
|
74 |
-
* @param string $
|
75 |
* @return bool
|
76 |
*/
|
77 |
-
public function isCoreFileHashValid( $
|
78 |
try {
|
79 |
-
$
|
80 |
-
|
81 |
}
|
82 |
catch ( \Exception $oE ) {
|
83 |
-
$
|
84 |
}
|
85 |
-
return $
|
86 |
}
|
87 |
|
88 |
-
|
89 |
-
|
90 |
-
*/
|
91 |
-
public function isReady() {
|
92 |
-
return ( count( $this->getHashes() ) > 0 );
|
93 |
}
|
94 |
}
|
14 |
/**
|
15 |
* @var array
|
16 |
*/
|
17 |
+
private $hashes;
|
18 |
|
19 |
/**
|
20 |
* Filters out wp-content plugins/themes data.
|
21 |
+
* @return string[]
|
22 |
*/
|
23 |
+
public function getHashes() :array {
|
24 |
+
if ( !isset( $this->hashes ) ) {
|
25 |
+
$hashes = Services::WpGeneral()->getCoreChecksums();
|
26 |
|
27 |
+
$this->hashes = array_intersect_key(
|
28 |
+
$hashes,
|
29 |
array_flip( array_filter(
|
30 |
+
array_keys( $hashes ),
|
31 |
+
function ( $file ) {
|
32 |
+
return preg_match( '#wp-content/(plugins|themes)#i', $file ) === 0;
|
33 |
}
|
34 |
) )
|
35 |
);
|
36 |
}
|
37 |
+
return $this->hashes;
|
38 |
}
|
39 |
|
40 |
/**
|
41 |
+
* @param string $file
|
42 |
* @return string|null
|
43 |
*/
|
44 |
+
public function getFileHash( $file ) {
|
45 |
+
$sNorm = $this->getFileFragment( $file );
|
46 |
return $this->isCoreFile( $sNorm ) ? $this->getHashes()[ $sNorm ] : null;
|
47 |
}
|
48 |
|
49 |
/**
|
50 |
+
* @param string $file
|
51 |
* @return string
|
52 |
*/
|
53 |
+
public function getFileFragment( $file ) :string {
|
54 |
+
return Services::WpFs()->getPathRelativeToAbsPath( $file );
|
55 |
}
|
56 |
|
57 |
/**
|
58 |
+
* @param string $file
|
59 |
* @return string
|
60 |
*/
|
61 |
+
public function getAbsolutePathFromFragment( $file ) :string {
|
62 |
+
return wp_normalize_path( path_join( ABSPATH, $this->getFileFragment( $file ) ) );
|
63 |
}
|
64 |
|
65 |
/**
|
66 |
+
* @param string $file
|
67 |
* @return bool
|
68 |
*/
|
69 |
+
public function isCoreFile( $file ) :bool {
|
70 |
+
return array_key_exists( $this->getFileFragment( $file ), $this->getHashes() );
|
71 |
}
|
72 |
|
73 |
/**
|
74 |
+
* @param string $fullPath
|
75 |
* @return bool
|
76 |
*/
|
77 |
+
public function isCoreFileHashValid( $fullPath ) :bool {
|
78 |
try {
|
79 |
+
$valid = $this->isCoreFile( $fullPath )
|
80 |
+
&& ( new CompareHash() )->isEqualFileMd5( $fullPath, $this->getFileHash( $fullPath ) );
|
81 |
}
|
82 |
catch ( \Exception $oE ) {
|
83 |
+
$valid = false;
|
84 |
}
|
85 |
+
return $valid;
|
86 |
}
|
87 |
|
88 |
+
public function isReady() :bool {
|
89 |
+
return count( $this->getHashes() ) > 0;
|
|
|
|
|
|
|
90 |
}
|
91 |
}
|
src/lib/vendor/fernleafsystems/wordpress-services/src/Core/Fs.php
CHANGED
@@ -228,14 +228,14 @@ class Fs {
|
|
228 |
}
|
229 |
|
230 |
/**
|
231 |
-
* @param $
|
232 |
* @return string
|
233 |
*/
|
234 |
-
public function getPathRelativeToAbsPath( $
|
235 |
return preg_replace(
|
236 |
sprintf( '#^%s#i', preg_quote( wp_normalize_path( ABSPATH ), '#' ) ),
|
237 |
'',
|
238 |
-
wp_normalize_path( $
|
239 |
);
|
240 |
}
|
241 |
|
@@ -469,56 +469,39 @@ class Fs {
|
|
469 |
return function_exists( 'rename' ) ? @rename( $sFilePathSource, $sFilePathDestination ) : null;
|
470 |
}
|
471 |
|
472 |
-
|
473 |
-
|
474 |
-
|
475 |
-
*/
|
476 |
-
public function isDir( $sPath ) {
|
477 |
-
$oFs = $this->getWpfs();
|
478 |
-
if ( $oFs && $oFs->is_dir( $sPath ) ) {
|
479 |
-
return true;
|
480 |
-
}
|
481 |
-
return function_exists( 'is_dir' ) ? is_dir( $sPath ) : false;
|
482 |
}
|
483 |
|
484 |
-
|
485 |
-
|
486 |
-
|
487 |
-
*/
|
488 |
-
public function isFile( $sPath ) {
|
489 |
-
$oFs = $this->getWpfs();
|
490 |
-
if ( $oFs && $oFs->is_file( $sPath ) ) {
|
491 |
-
return true;
|
492 |
-
}
|
493 |
-
return function_exists( 'is_file' ) ? is_file( $sPath ) : null;
|
494 |
}
|
495 |
|
496 |
-
|
497 |
-
|
498 |
-
*/
|
499 |
-
public function isFilesystemAccessDirect() {
|
500 |
-
return ( $this->getWpfs() instanceof \WP_Filesystem_Direct );
|
501 |
}
|
502 |
|
503 |
/**
|
504 |
-
* @param string $
|
505 |
* @return bool
|
506 |
*/
|
507 |
-
public function mkdir( $
|
508 |
-
return wp_mkdir_p( $
|
509 |
}
|
510 |
|
511 |
/**
|
512 |
-
* @param string $
|
513 |
* @param int $nTime
|
514 |
* @return bool|mixed
|
515 |
*/
|
516 |
-
public function touch( $
|
517 |
$oFs = $this->getWpfs();
|
518 |
-
if ( $oFs && $oFs->touch( $
|
519 |
return true;
|
520 |
}
|
521 |
-
return function_exists( 'touch' ) ? @touch( $
|
522 |
}
|
523 |
|
524 |
/**
|
@@ -531,8 +514,10 @@ class Fs {
|
|
531 |
return $this->oWpfs;
|
532 |
}
|
533 |
|
534 |
-
|
535 |
-
|
|
|
|
|
536 |
private function initFileSystem() {
|
537 |
if ( is_null( $this->oWpfs ) ) {
|
538 |
$this->oWpfs = false;
|
228 |
}
|
229 |
|
230 |
/**
|
231 |
+
* @param $path
|
232 |
* @return string
|
233 |
*/
|
234 |
+
public function getPathRelativeToAbsPath( $path ) :string {
|
235 |
return preg_replace(
|
236 |
sprintf( '#^%s#i', preg_quote( wp_normalize_path( ABSPATH ), '#' ) ),
|
237 |
'',
|
238 |
+
wp_normalize_path( $path )
|
239 |
);
|
240 |
}
|
241 |
|
469 |
return function_exists( 'rename' ) ? @rename( $sFilePathSource, $sFilePathDestination ) : null;
|
470 |
}
|
471 |
|
472 |
+
public function isDir( string $path ) :bool {
|
473 |
+
return ( $this->hasWpfs() && $this->getWpfs()->is_dir( $path ) )
|
474 |
+
|| ( function_exists( 'is_dir' ) ? is_dir( $path ) : false );
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
475 |
}
|
476 |
|
477 |
+
public function isFile( $path ) :bool {
|
478 |
+
return ( $this->hasWpfs() && $this->getWpfs()->is_file( $path ) )
|
479 |
+
|| ( function_exists( 'is_file' ) ? is_file( $path ) : false );
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
480 |
}
|
481 |
|
482 |
+
public function isFilesystemAccessDirect() :bool {
|
483 |
+
return $this->getWpfs() instanceof \WP_Filesystem_Direct;
|
|
|
|
|
|
|
484 |
}
|
485 |
|
486 |
/**
|
487 |
+
* @param string $path
|
488 |
* @return bool
|
489 |
*/
|
490 |
+
public function mkdir( $path ) {
|
491 |
+
return wp_mkdir_p( $path );
|
492 |
}
|
493 |
|
494 |
/**
|
495 |
+
* @param string $path
|
496 |
* @param int $nTime
|
497 |
* @return bool|mixed
|
498 |
*/
|
499 |
+
public function touch( $path, $nTime = null ) {
|
500 |
$oFs = $this->getWpfs();
|
501 |
+
if ( $oFs && $oFs->touch( $path, $nTime ) ) {
|
502 |
return true;
|
503 |
}
|
504 |
+
return function_exists( 'touch' ) ? @touch( $path, $nTime ) : null;
|
505 |
}
|
506 |
|
507 |
/**
|
514 |
return $this->oWpfs;
|
515 |
}
|
516 |
|
517 |
+
protected function hasWpfs() :bool {
|
518 |
+
return $this->getWpfs() instanceof \WP_Filesystem_Base;
|
519 |
+
}
|
520 |
+
|
521 |
private function initFileSystem() {
|
522 |
if ( is_null( $this->oWpfs ) ) {
|
523 |
$this->oWpfs = false;
|
src/lib/vendor/fernleafsystems/wordpress-services/src/Core/General.php
CHANGED
@@ -976,14 +976,14 @@ class General {
|
|
976 |
}
|
977 |
|
978 |
/**
|
979 |
-
* @param string $
|
980 |
* @param array $aQueryParams
|
981 |
* @param bool $bSafe
|
982 |
* @param bool $bProtectAgainstInfiniteLoops - if false, ignores the redirect loop protection
|
983 |
* @deprecated
|
984 |
*/
|
985 |
-
public function doRedirect( $
|
986 |
-
Services::Response()->redirect( $
|
987 |
}
|
988 |
|
989 |
/**
|
976 |
}
|
977 |
|
978 |
/**
|
979 |
+
* @param string $url
|
980 |
* @param array $aQueryParams
|
981 |
* @param bool $bSafe
|
982 |
* @param bool $bProtectAgainstInfiniteLoops - if false, ignores the redirect loop protection
|
983 |
* @deprecated
|
984 |
*/
|
985 |
+
public function doRedirect( $url, $aQueryParams = [], $bSafe = true, $bProtectAgainstInfiniteLoops = true ) {
|
986 |
+
Services::Response()->redirect( $url, $aQueryParams, $bSafe, $bProtectAgainstInfiniteLoops );
|
987 |
}
|
988 |
|
989 |
/**
|
src/lib/vendor/fernleafsystems/wordpress-services/src/Core/Nonce.php
CHANGED
@@ -16,7 +16,7 @@ class Nonce {
|
|
16 |
/**
|
17 |
* @var string
|
18 |
*/
|
19 |
-
private $
|
20 |
|
21 |
public function create() {
|
22 |
if ( !$this->hasAction() ) {
|
@@ -26,15 +26,15 @@ class Nonce {
|
|
26 |
}
|
27 |
|
28 |
/**
|
29 |
-
* @param string $
|
30 |
* @return false|int
|
31 |
* @throws \Exception
|
32 |
*/
|
33 |
-
public function verify( $
|
34 |
if ( !$this->hasAction() ) {
|
35 |
throw new \Exception( 'No action specified for nonce' );
|
36 |
}
|
37 |
-
return $this->isIncludeUserId() ? wp_verify_nonce( $
|
38 |
}
|
39 |
|
40 |
/**
|
@@ -48,12 +48,12 @@ class Nonce {
|
|
48 |
}
|
49 |
|
50 |
/**
|
51 |
-
* @param $
|
52 |
* @return int
|
53 |
* @throws \Exception
|
54 |
*/
|
55 |
-
private function verifyNonceNoUser( $
|
56 |
-
if ( empty( $
|
57 |
throw new \Exception( 'Nonce is empty' );
|
58 |
}
|
59 |
|
@@ -62,55 +62,38 @@ class Nonce {
|
|
62 |
|
63 |
// Nonce generated 0-12 hours ago.
|
64 |
$expected = substr( wp_hash( $i.'|'.$this->getAction().'|'.$token, 'nonce' ), -12, 10 );
|
65 |
-
if ( hash_equals( $expected, $
|
66 |
return 1;
|
67 |
}
|
68 |
|
69 |
// Nonce generated 12-24 hours ago.
|
70 |
$expected = substr( wp_hash( ( $i - 1 ).'|'.$this->getAction().'|'.$token, 'nonce' ), -12, 10 );
|
71 |
-
if ( hash_equals( $expected, $
|
72 |
return 2;
|
73 |
}
|
74 |
|
75 |
throw new \Exception( 'Nonce verification failed.' );
|
76 |
}
|
77 |
|
78 |
-
|
79 |
-
|
80 |
-
*/
|
81 |
-
public function getAction() {
|
82 |
-
return (string)$this->sAction;
|
83 |
}
|
84 |
|
85 |
-
|
86 |
-
|
87 |
-
*/
|
88 |
-
public function hasAction() {
|
89 |
-
return !empty( $this->sAction );
|
90 |
}
|
91 |
|
92 |
-
|
93 |
-
* @return bool
|
94 |
-
*/
|
95 |
-
public function isIncludeUserId() {
|
96 |
return isset( $this->bIncludeUserId ) ? (bool)$this->bIncludeUserId : true;
|
97 |
}
|
98 |
|
99 |
-
|
100 |
-
|
101 |
-
* @return $this
|
102 |
-
*/
|
103 |
-
public function setAction( $sAction ) {
|
104 |
-
$this->sAction = $sAction;
|
105 |
return $this;
|
106 |
}
|
107 |
|
108 |
-
|
109 |
-
|
110 |
-
* @return $this
|
111 |
-
*/
|
112 |
-
public function setIncludeUserId( $bUse ) {
|
113 |
-
$this->bIncludeUserId = $bUse;
|
114 |
return $this;
|
115 |
}
|
116 |
}
|
16 |
/**
|
17 |
* @var string
|
18 |
*/
|
19 |
+
private $action;
|
20 |
|
21 |
public function create() {
|
22 |
if ( !$this->hasAction() ) {
|
26 |
}
|
27 |
|
28 |
/**
|
29 |
+
* @param string $nonce
|
30 |
* @return false|int
|
31 |
* @throws \Exception
|
32 |
*/
|
33 |
+
public function verify( $nonce ) {
|
34 |
if ( !$this->hasAction() ) {
|
35 |
throw new \Exception( 'No action specified for nonce' );
|
36 |
}
|
37 |
+
return $this->isIncludeUserId() ? wp_verify_nonce( $nonce, $this->getAction() ) : $this->verifyNonceNoUser( $nonce );
|
38 |
}
|
39 |
|
40 |
/**
|
48 |
}
|
49 |
|
50 |
/**
|
51 |
+
* @param $nonce
|
52 |
* @return int
|
53 |
* @throws \Exception
|
54 |
*/
|
55 |
+
private function verifyNonceNoUser( $nonce ) {
|
56 |
+
if ( empty( $nonce ) ) {
|
57 |
throw new \Exception( 'Nonce is empty' );
|
58 |
}
|
59 |
|
62 |
|
63 |
// Nonce generated 0-12 hours ago.
|
64 |
$expected = substr( wp_hash( $i.'|'.$this->getAction().'|'.$token, 'nonce' ), -12, 10 );
|
65 |
+
if ( hash_equals( $expected, $nonce ) ) {
|
66 |
return 1;
|
67 |
}
|
68 |
|
69 |
// Nonce generated 12-24 hours ago.
|
70 |
$expected = substr( wp_hash( ( $i - 1 ).'|'.$this->getAction().'|'.$token, 'nonce' ), -12, 10 );
|
71 |
+
if ( hash_equals( $expected, $nonce ) ) {
|
72 |
return 2;
|
73 |
}
|
74 |
|
75 |
throw new \Exception( 'Nonce verification failed.' );
|
76 |
}
|
77 |
|
78 |
+
public function getAction() :string {
|
79 |
+
return (string)$this->action;
|
|
|
|
|
|
|
80 |
}
|
81 |
|
82 |
+
public function hasAction() :bool {
|
83 |
+
return !empty( $this->action );
|
|
|
|
|
|
|
84 |
}
|
85 |
|
86 |
+
public function isIncludeUserId() :bool {
|
|
|
|
|
|
|
87 |
return isset( $this->bIncludeUserId ) ? (bool)$this->bIncludeUserId : true;
|
88 |
}
|
89 |
|
90 |
+
public function setAction( string $action ) :self {
|
91 |
+
$this->action = $action;
|
|
|
|
|
|
|
|
|
92 |
return $this;
|
93 |
}
|
94 |
|
95 |
+
public function setIncludeUserId( bool $use ) :self {
|
96 |
+
$this->bIncludeUserId = $use;
|
|
|
|
|
|
|
|
|
97 |
return $this;
|
98 |
}
|
99 |
}
|
src/lib/vendor/fernleafsystems/wordpress-services/src/Core/Request.php
CHANGED
@@ -14,27 +14,27 @@ class Request {
|
|
14 |
/**
|
15 |
* @var array
|
16 |
*/
|
17 |
-
private $
|
18 |
|
19 |
/**
|
20 |
* @var array
|
21 |
*/
|
22 |
-
private $
|
23 |
|
24 |
/**
|
25 |
* @var array
|
26 |
*/
|
27 |
-
private $
|
28 |
|
29 |
/**
|
30 |
* @var array
|
31 |
*/
|
32 |
-
private $
|
33 |
|
34 |
/**
|
35 |
* @var array
|
36 |
*/
|
37 |
-
private $
|
38 |
|
39 |
/**
|
40 |
* @var int
|
@@ -49,36 +49,35 @@ class Request {
|
|
49 |
/**
|
50 |
* @var string
|
51 |
*/
|
52 |
-
private $
|
53 |
|
54 |
/**
|
55 |
* Request constructor.
|
56 |
*/
|
57 |
public function __construct() {
|
58 |
-
$this->
|
59 |
-
$this->
|
60 |
-
$
|
61 |
-
|
62 |
-
|
|
|
|
|
|
|
|
|
|
|
63 |
$this->ts();
|
64 |
}
|
65 |
|
66 |
-
|
67 |
-
|
68 |
-
|
69 |
-
public function getContent() {
|
70 |
-
if ( !isset( $this->sContent ) ) {
|
71 |
-
$this->sContent = file_get_contents( 'php://input' );
|
72 |
}
|
73 |
-
return $this->
|
74 |
}
|
75 |
|
76 |
-
|
77 |
-
|
78 |
-
|
79 |
-
public function getMethod() {
|
80 |
-
$sRequestMethod = $this->server( 'REQUEST_METHOD' );
|
81 |
-
return ( empty( $sRequestMethod ) ? '' : strtolower( $sRequestMethod ) );
|
82 |
}
|
83 |
|
84 |
/**
|
@@ -86,20 +85,17 @@ class Request {
|
|
86 |
* @return int
|
87 |
*/
|
88 |
public function mts( $bMsOnly = false ) {
|
89 |
-
$
|
90 |
if ( empty( $this->nMts ) ) {
|
91 |
-
$
|
92 |
}
|
93 |
else {
|
94 |
-
$
|
95 |
}
|
96 |
-
return $
|
97 |
}
|
98 |
|
99 |
-
|
100 |
-
* @return int
|
101 |
-
*/
|
102 |
-
public function ts() {
|
103 |
if ( empty( $this->nTs ) ) {
|
104 |
$this->nTs = time();
|
105 |
$this->nMts = function_exists( 'microtime' ) ? @microtime( true ) : false;
|
@@ -112,172 +108,146 @@ class Request {
|
|
112 |
* @return Carbon
|
113 |
*/
|
114 |
public function carbon( $bSetTimezone = false ) {
|
115 |
-
$
|
116 |
-
$
|
117 |
-
$
|
118 |
-
$
|
119 |
if ( $bSetTimezone ) {
|
120 |
-
$
|
121 |
-
if ( !empty( $
|
122 |
-
$
|
123 |
}
|
124 |
}
|
125 |
-
return $
|
126 |
}
|
127 |
|
128 |
-
|
129 |
-
|
130 |
-
|
131 |
-
*/
|
132 |
-
public function getRawRequestParams( $bIncludeCookie = true ) {
|
133 |
-
$aParams = array_merge( $this->aQuery, $this->aPost );
|
134 |
-
return $bIncludeCookie ? array_merge( $aParams, $this->aCookie ) : $aParams;
|
135 |
}
|
136 |
|
137 |
-
|
138 |
-
|
139 |
-
*/
|
140 |
-
public function getHost() {
|
141 |
-
return $this->server( 'HTTP_HOST' );
|
142 |
}
|
143 |
|
144 |
-
|
145 |
-
* @return string URI Path in lowercase
|
146 |
-
*/
|
147 |
-
public function getPath() {
|
148 |
return $this->getUriParts()[ 'path' ];
|
149 |
}
|
150 |
|
151 |
-
|
152 |
-
|
153 |
-
*/
|
154 |
-
public function getServerAddress() {
|
155 |
-
return $this->server( 'SERVER_ADDR' );
|
156 |
}
|
157 |
|
158 |
-
|
159 |
-
|
160 |
-
*/
|
161 |
-
public function getUri() {
|
162 |
-
return $this->server( 'REQUEST_URI' );
|
163 |
}
|
164 |
|
165 |
-
|
166 |
-
|
167 |
-
|
168 |
-
|
169 |
-
$sPath = $this->getUri();
|
170 |
-
if ( strpos( $sPath, '?' ) !== false ) {
|
171 |
-
list( $sPath, $sQuery ) = explode( '?', $sPath, 2 );
|
172 |
}
|
173 |
else {
|
174 |
-
$
|
175 |
}
|
176 |
return [
|
177 |
-
'path' => $
|
178 |
-
'query' => $
|
179 |
];
|
180 |
}
|
181 |
|
182 |
-
|
183 |
-
|
184 |
-
*/
|
185 |
-
public function getUserAgent() {
|
186 |
-
return $this->server( 'HTTP_USER_AGENT' );
|
187 |
}
|
188 |
|
189 |
-
|
190 |
-
|
191 |
-
*/
|
192 |
-
public function isPost() {
|
193 |
-
return ( $this->getMethod() == 'post' );
|
194 |
}
|
195 |
|
196 |
-
|
197 |
-
|
198 |
-
*/
|
199 |
-
public function countQuery() {
|
200 |
-
return $this->count( str_replace( 'count', '', __FUNCTION__ ) );
|
201 |
}
|
202 |
|
203 |
-
|
204 |
-
|
205 |
-
*/
|
206 |
-
public function countPost() {
|
207 |
-
return $this->count( str_replace( 'count', '', __FUNCTION__ ) );
|
208 |
}
|
209 |
|
210 |
-
|
211 |
-
|
212 |
-
* @return int
|
213 |
-
*/
|
214 |
-
private function count( $sContainer ) {
|
215 |
-
$sArray = 'a'.$sContainer;
|
216 |
-
$aArray = $this->{$sArray};
|
217 |
-
return is_array( $aArray ) ? count( $aArray ) : 0;
|
218 |
}
|
219 |
|
220 |
/**
|
221 |
-
* @param string $
|
222 |
-
* @param null $
|
223 |
* @return mixed|null
|
224 |
*/
|
225 |
-
public function cookie( $
|
226 |
-
return $this->
|
227 |
}
|
228 |
|
229 |
/**
|
230 |
-
* @param string $
|
231 |
-
* @param null $
|
232 |
* @return mixed|null
|
233 |
*/
|
234 |
-
public function env( $
|
235 |
-
return $this->
|
236 |
}
|
237 |
|
238 |
/**
|
239 |
-
* @param string $
|
240 |
-
* @param null $
|
241 |
* @return mixed|null
|
242 |
*/
|
243 |
-
public function post( $
|
244 |
-
return $this->
|
245 |
}
|
246 |
|
247 |
/**
|
248 |
-
* @param string $
|
249 |
-
* @param null $
|
250 |
* @return mixed|null
|
251 |
*/
|
252 |
-
public function query( $
|
253 |
-
return $this->
|
254 |
}
|
255 |
|
256 |
/**
|
257 |
* POST > GET > COOKIE
|
258 |
-
* @param string $
|
259 |
-
* @param bool $
|
260 |
-
* @param null $
|
261 |
* @return mixed|null
|
262 |
*/
|
263 |
-
public function request( $
|
264 |
-
$
|
265 |
-
if ( is_null( $
|
266 |
-
$
|
267 |
-
if ( $
|
268 |
-
$
|
269 |
}
|
270 |
}
|
271 |
-
return is_null( $
|
272 |
}
|
273 |
|
274 |
/**
|
275 |
-
* @param string $
|
276 |
-
* @param null $
|
277 |
* @return mixed|null
|
278 |
*/
|
279 |
-
public function server( $
|
280 |
-
return $this->
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
281 |
}
|
282 |
|
283 |
/**
|
@@ -285,6 +255,7 @@ class Request {
|
|
285 |
* @param string $sKey
|
286 |
* @param mixed $mDefault
|
287 |
* @return mixed|null
|
|
|
288 |
*/
|
289 |
private function arrayFetch( $sContainer, $sKey, $mDefault = null ) {
|
290 |
$sArray = 'a'.ucfirst( $sContainer );
|
@@ -335,4 +306,15 @@ class Request {
|
|
335 |
public function getRequestUriParts() {
|
336 |
return $this->getUriParts();
|
337 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
338 |
}
|
14 |
/**
|
15 |
* @var array
|
16 |
*/
|
17 |
+
private $post;
|
18 |
|
19 |
/**
|
20 |
* @var array
|
21 |
*/
|
22 |
+
private $query;
|
23 |
|
24 |
/**
|
25 |
* @var array
|
26 |
*/
|
27 |
+
private $cookie;
|
28 |
|
29 |
/**
|
30 |
* @var array
|
31 |
*/
|
32 |
+
private $server;
|
33 |
|
34 |
/**
|
35 |
* @var array
|
36 |
*/
|
37 |
+
private $env;
|
38 |
|
39 |
/**
|
40 |
* @var int
|
49 |
/**
|
50 |
* @var string
|
51 |
*/
|
52 |
+
private $content;
|
53 |
|
54 |
/**
|
55 |
* Request constructor.
|
56 |
*/
|
57 |
public function __construct() {
|
58 |
+
$this->post = is_array( $_POST ) ? $_POST : [];
|
59 |
+
$this->query = is_array( $_GET ) ? $_GET : [];
|
60 |
+
if ( is_array( $_COOKIE ) ) {
|
61 |
+
$this->cookie = &$_COOKIE;
|
62 |
+
}
|
63 |
+
else {
|
64 |
+
$this->cookie = [];
|
65 |
+
}
|
66 |
+
$this->server = is_array( $_SERVER ) ? $_SERVER : [];
|
67 |
+
$this->env = is_array( $_ENV ) ? $_ENV : [];
|
68 |
$this->ts();
|
69 |
}
|
70 |
|
71 |
+
public function getContent() :string {
|
72 |
+
if ( !isset( $this->content ) ) {
|
73 |
+
$this->content = file_get_contents( 'php://input' );
|
|
|
|
|
|
|
74 |
}
|
75 |
+
return (string)$this->content;
|
76 |
}
|
77 |
|
78 |
+
public function getMethod() :string {
|
79 |
+
$method = (string)$this->server( 'REQUEST_METHOD' );
|
80 |
+
return empty( $method ) ? '' : strtolower( $method );
|
|
|
|
|
|
|
81 |
}
|
82 |
|
83 |
/**
|
85 |
* @return int
|
86 |
*/
|
87 |
public function mts( $bMsOnly = false ) {
|
88 |
+
$now = $this->ts();
|
89 |
if ( empty( $this->nMts ) ) {
|
90 |
+
$now = $bMsOnly ? 0 : $now;
|
91 |
}
|
92 |
else {
|
93 |
+
$now = $bMsOnly ? preg_replace( '#^[0-9]+\.#', '', $this->nMts ) : $this->nMts;
|
94 |
}
|
95 |
+
return $now;
|
96 |
}
|
97 |
|
98 |
+
public function ts() :int {
|
|
|
|
|
|
|
99 |
if ( empty( $this->nTs ) ) {
|
100 |
$this->nTs = time();
|
101 |
$this->nMts = function_exists( 'microtime' ) ? @microtime( true ) : false;
|
108 |
* @return Carbon
|
109 |
*/
|
110 |
public function carbon( $bSetTimezone = false ) {
|
111 |
+
$WP = Services::WpGeneral();
|
112 |
+
$carbon = new Carbon();
|
113 |
+
$carbon->setTimestamp( $this->ts() );
|
114 |
+
$carbon->setLocale( $WP->getLocaleCountry() );
|
115 |
if ( $bSetTimezone ) {
|
116 |
+
$TZ = $WP->getOption( 'timezone_string' );
|
117 |
+
if ( !empty( $TZ ) ) {
|
118 |
+
$carbon->setTimezone( $TZ );
|
119 |
}
|
120 |
}
|
121 |
+
return $carbon;
|
122 |
}
|
123 |
|
124 |
+
public function getRawRequestParams( bool $includeCookies = true ) :array {
|
125 |
+
$params = array_merge( $this->query, $this->post );
|
126 |
+
return $includeCookies ? array_merge( $params, $this->cookie ) : $params;
|
|
|
|
|
|
|
|
|
127 |
}
|
128 |
|
129 |
+
public function getHost() :string {
|
130 |
+
return (string)$this->server( 'HTTP_HOST' );
|
|
|
|
|
|
|
131 |
}
|
132 |
|
133 |
+
public function getPath() :string {
|
|
|
|
|
|
|
134 |
return $this->getUriParts()[ 'path' ];
|
135 |
}
|
136 |
|
137 |
+
public function getServerAddress() :string {
|
138 |
+
return (string)$this->server( 'SERVER_ADDR' );
|
|
|
|
|
|
|
139 |
}
|
140 |
|
141 |
+
public function getUri() :string {
|
142 |
+
return (string)$this->server( 'REQUEST_URI' );
|
|
|
|
|
|
|
143 |
}
|
144 |
|
145 |
+
public function getUriParts() :array {
|
146 |
+
$path = $this->getUri();
|
147 |
+
if ( strpos( $path, '?' ) !== false ) {
|
148 |
+
list( $path, $query ) = explode( '?', $path, 2 );
|
|
|
|
|
|
|
149 |
}
|
150 |
else {
|
151 |
+
$query = '';
|
152 |
}
|
153 |
return [
|
154 |
+
'path' => $path,
|
155 |
+
'query' => $query,
|
156 |
];
|
157 |
}
|
158 |
|
159 |
+
public function getUserAgent() :string {
|
160 |
+
return (string)$this->server( 'HTTP_USER_AGENT' );
|
|
|
|
|
|
|
161 |
}
|
162 |
|
163 |
+
public function isGet() :bool {
|
164 |
+
return $this->getMethod() == 'get';
|
|
|
|
|
|
|
165 |
}
|
166 |
|
167 |
+
public function isPost() :bool {
|
168 |
+
return $this->getMethod() == 'post';
|
|
|
|
|
|
|
169 |
}
|
170 |
|
171 |
+
public function countQuery() :int {
|
172 |
+
return count( $this->query );
|
|
|
|
|
|
|
173 |
}
|
174 |
|
175 |
+
public function countPost() :int {
|
176 |
+
return count( $this->post );
|
|
|
|
|
|
|
|
|
|
|
|
|
177 |
}
|
178 |
|
179 |
/**
|
180 |
+
* @param string $key
|
181 |
+
* @param null $default
|
182 |
* @return mixed|null
|
183 |
*/
|
184 |
+
public function cookie( $key, $default = null ) {
|
185 |
+
return $this->fetch( $this->cookie, $key, $default );
|
186 |
}
|
187 |
|
188 |
/**
|
189 |
+
* @param string $key
|
190 |
+
* @param null $default
|
191 |
* @return mixed|null
|
192 |
*/
|
193 |
+
public function env( $key, $default = null ) {
|
194 |
+
return $this->fetch( $this->env, $key, $default );
|
195 |
}
|
196 |
|
197 |
/**
|
198 |
+
* @param string $key
|
199 |
+
* @param null $default
|
200 |
* @return mixed|null
|
201 |
*/
|
202 |
+
public function post( $key, $default = null ) {
|
203 |
+
return $this->fetch( $this->post, $key, $default );
|
204 |
}
|
205 |
|
206 |
/**
|
207 |
+
* @param string $key
|
208 |
+
* @param null $default
|
209 |
* @return mixed|null
|
210 |
*/
|
211 |
+
public function query( $key, $default = null ) {
|
212 |
+
return $this->fetch( $this->query, $key, $default );
|
213 |
}
|
214 |
|
215 |
/**
|
216 |
* POST > GET > COOKIE
|
217 |
+
* @param string $key
|
218 |
+
* @param bool $includeCookies
|
219 |
+
* @param null $default
|
220 |
* @return mixed|null
|
221 |
*/
|
222 |
+
public function request( $key, $includeCookies = false, $default = null ) {
|
223 |
+
$value = $this->post( $key );
|
224 |
+
if ( is_null( $value ) ) {
|
225 |
+
$value = $this->query( $key );
|
226 |
+
if ( $includeCookies && is_null( $value ) ) {
|
227 |
+
$value = $this->cookie( $key );
|
228 |
}
|
229 |
}
|
230 |
+
return is_null( $value ) ? $default : $value;
|
231 |
}
|
232 |
|
233 |
/**
|
234 |
+
* @param string $key
|
235 |
+
* @param null $default
|
236 |
* @return mixed|null
|
237 |
*/
|
238 |
+
public function server( $key, $default = null ) {
|
239 |
+
return $this->fetch( $this->server, $key, $default );
|
240 |
+
}
|
241 |
+
|
242 |
+
/**
|
243 |
+
* @param array $container
|
244 |
+
* @param string $key
|
245 |
+
* @param mixed $default
|
246 |
+
* @return mixed|null
|
247 |
+
*/
|
248 |
+
private function fetch( array $container, $key, $default = null ) {
|
249 |
+
$value = $container[ $key ] ?? $default;
|
250 |
+
return is_null( $value ) ? $default : $value;
|
251 |
}
|
252 |
|
253 |
/**
|
255 |
* @param string $sKey
|
256 |
* @param mixed $mDefault
|
257 |
* @return mixed|null
|
258 |
+
* @deprecated
|
259 |
*/
|
260 |
private function arrayFetch( $sContainer, $sKey, $mDefault = null ) {
|
261 |
$sArray = 'a'.ucfirst( $sContainer );
|
306 |
public function getRequestUriParts() {
|
307 |
return $this->getUriParts();
|
308 |
}
|
309 |
+
|
310 |
+
/**
|
311 |
+
* @param string $sContainer
|
312 |
+
* @return int
|
313 |
+
* @deprecated
|
314 |
+
*/
|
315 |
+
private function count( $sContainer ) {
|
316 |
+
$sArray = 'a'.$sContainer;
|
317 |
+
$aArray = $this->{$sArray};
|
318 |
+
return is_array( $aArray ) ? count( $aArray ) : 0;
|
319 |
+
}
|
320 |
}
|
src/lib/vendor/fernleafsystems/wordpress-services/src/Core/Response.php
CHANGED
@@ -17,60 +17,60 @@ class Response {
|
|
17 |
}
|
18 |
|
19 |
/**
|
20 |
-
* @param string $
|
21 |
* @return bool
|
22 |
*/
|
23 |
-
public function cookieDelete( $
|
24 |
-
unset( $_COOKIE[ $
|
25 |
-
return $this->cookieSet( $
|
26 |
}
|
27 |
|
28 |
/**
|
29 |
-
* @param string $
|
30 |
-
* @param string $
|
31 |
-
* @param int $
|
32 |
-
* @param null $
|
33 |
-
* @param null $
|
34 |
-
* @param bool $
|
35 |
* @return bool
|
36 |
*/
|
37 |
-
public function cookieSet( $
|
38 |
if ( function_exists( 'headers_sent' ) && headers_sent() ) {
|
39 |
return false;
|
40 |
}
|
41 |
-
$_COOKIE[ $
|
42 |
return setcookie(
|
43 |
-
$
|
44 |
-
$
|
45 |
-
(int)( Services::Request()->ts() + $
|
46 |
-
( is_null( $
|
47 |
-
( is_null( $
|
48 |
-
is_null( $
|
49 |
);
|
50 |
}
|
51 |
|
52 |
/**
|
53 |
-
* @param string $
|
54 |
-
* @param string $
|
55 |
* @return bool
|
56 |
*/
|
57 |
-
public function downloadStringAsFile( $
|
58 |
header( "Content-type: application/octet-stream" );
|
59 |
-
header( "Content-disposition: attachment; filename=".$
|
60 |
header( "Content-Transfer-Encoding: binary" );
|
61 |
-
header( "Content-Length: ".strlen( $
|
62 |
-
echo $
|
63 |
die();
|
64 |
}
|
65 |
|
66 |
/**
|
67 |
-
* @param string $
|
68 |
-
* @param array $
|
69 |
-
* @param bool $
|
70 |
* @param bool $bProtectAgainstInfiniteLoops - if false, ignores the redirect loop protection
|
71 |
*/
|
72 |
-
public function redirect( $
|
73 |
-
$
|
74 |
|
75 |
// we prevent any repetitive redirect loops
|
76 |
if ( $bProtectAgainstInfiniteLoops ) {
|
@@ -78,15 +78,15 @@ class Response {
|
|
78 |
return;
|
79 |
}
|
80 |
else {
|
81 |
-
|
82 |
}
|
83 |
}
|
84 |
|
85 |
// based on: https://make.wordpress.org/plugins/2015/04/20/fixing-add_query_arg-and-remove_query_arg-usage/
|
86 |
// we now escape the URL to be absolutely sure since we can't guarantee the URL coming through there
|
87 |
-
$
|
88 |
header( 'Cache-Control: no-store, no-cache' );
|
89 |
-
$
|
90 |
exit();
|
91 |
}
|
92 |
|
17 |
}
|
18 |
|
19 |
/**
|
20 |
+
* @param string $key
|
21 |
* @return bool
|
22 |
*/
|
23 |
+
public function cookieDelete( $key ) {
|
24 |
+
unset( $_COOKIE[ $key ] );
|
25 |
+
return $this->cookieSet( $key, '', -1000000 );
|
26 |
}
|
27 |
|
28 |
/**
|
29 |
+
* @param string $key
|
30 |
+
* @param string $value
|
31 |
+
* @param int $duration
|
32 |
+
* @param null $path
|
33 |
+
* @param null $domain
|
34 |
+
* @param bool $ssl
|
35 |
* @return bool
|
36 |
*/
|
37 |
+
public function cookieSet( $key, $value, $duration = 3600, $path = null, $domain = null, $ssl = null ) {
|
38 |
if ( function_exists( 'headers_sent' ) && headers_sent() ) {
|
39 |
return false;
|
40 |
}
|
41 |
+
$_COOKIE[ $key ] = $value;
|
42 |
return setcookie(
|
43 |
+
$key,
|
44 |
+
$value,
|
45 |
+
(int)( Services::Request()->ts() + $duration ),
|
46 |
+
( is_null( $path ) && defined( 'COOKIEPATH' ) ) ? COOKIEPATH : $path,
|
47 |
+
( is_null( $domain ) && defined( 'COOKIE_DOMAIN' ) ) ? COOKIE_DOMAIN : $domain,
|
48 |
+
is_null( $ssl ) ? ( is_ssl() ? true : false ) : $ssl
|
49 |
);
|
50 |
}
|
51 |
|
52 |
/**
|
53 |
+
* @param string $content
|
54 |
+
* @param string $filename
|
55 |
* @return bool
|
56 |
*/
|
57 |
+
public function downloadStringAsFile( $content, $filename ) {
|
58 |
header( "Content-type: application/octet-stream" );
|
59 |
+
header( "Content-disposition: attachment; filename=".$filename );
|
60 |
header( "Content-Transfer-Encoding: binary" );
|
61 |
+
header( "Content-Length: ".strlen( $content ) );
|
62 |
+
echo $content;
|
63 |
die();
|
64 |
}
|
65 |
|
66 |
/**
|
67 |
+
* @param string $url
|
68 |
+
* @param array $queryParams
|
69 |
+
* @param bool $safe
|
70 |
* @param bool $bProtectAgainstInfiniteLoops - if false, ignores the redirect loop protection
|
71 |
*/
|
72 |
+
public function redirect( $url, $queryParams = [], $safe = true, $bProtectAgainstInfiniteLoops = true ) {
|
73 |
+
$url = empty( $queryParams ) ? $url : add_query_arg( $queryParams, $url );
|
74 |
|
75 |
// we prevent any repetitive redirect loops
|
76 |
if ( $bProtectAgainstInfiniteLoops ) {
|
78 |
return;
|
79 |
}
|
80 |
else {
|
81 |
+
$this->cookieSet( 'icwp-isredirect', 'yes', 7 );
|
82 |
}
|
83 |
}
|
84 |
|
85 |
// based on: https://make.wordpress.org/plugins/2015/04/20/fixing-add_query_arg-and-remove_query_arg-usage/
|
86 |
// we now escape the URL to be absolutely sure since we can't guarantee the URL coming through there
|
87 |
+
$url = esc_url_raw( $url );
|
88 |
header( 'Cache-Control: no-store, no-cache' );
|
89 |
+
$safe ? wp_safe_redirect( $url ) : wp_redirect( $url );
|
90 |
exit();
|
91 |
}
|
92 |
|
src/lib/vendor/fernleafsystems/wordpress-services/src/Core/Themes.php
CHANGED
@@ -19,15 +19,15 @@ class Themes {
|
|
19 |
private $aLoadedVOs;
|
20 |
|
21 |
/**
|
22 |
-
* @param string $
|
23 |
* @return bool
|
24 |
*/
|
25 |
-
public function activate( $
|
26 |
-
if ( empty( $
|
27 |
return false;
|
28 |
}
|
29 |
|
30 |
-
$oTheme = $this->getTheme( $
|
31 |
if ( !$oTheme->exists() ) {
|
32 |
return false;
|
33 |
}
|
@@ -37,40 +37,40 @@ class Themes {
|
|
37 |
// Now test currently active theme
|
38 |
$oCurrentTheme = $this->getCurrent();
|
39 |
|
40 |
-
return ( $
|
41 |
}
|
42 |
|
43 |
/**
|
44 |
-
* @param string $
|
45 |
* @return bool|\WP_Error
|
46 |
*/
|
47 |
-
public function delete( $
|
48 |
-
if ( empty( $
|
49 |
return false;
|
50 |
}
|
51 |
if ( !function_exists( 'delete_theme' ) ) {
|
52 |
require_once( ABSPATH.'wp-admin/includes/theme.php' );
|
53 |
}
|
54 |
-
return function_exists( 'delete_theme' ) ? delete_theme( $
|
55 |
}
|
56 |
|
57 |
/**
|
58 |
-
* @param string $
|
59 |
* @return bool
|
60 |
*/
|
61 |
-
public function installFromWpOrg( $
|
62 |
-
$
|
63 |
try {
|
64 |
-
$
|
65 |
-
->setWorkingSlug( $
|
66 |
->getInfo();
|
67 |
-
if ( !empty( $
|
68 |
-
$
|
69 |
}
|
70 |
}
|
71 |
-
catch ( \Exception $
|
72 |
}
|
73 |
-
return $
|
74 |
}
|
75 |
|
76 |
/**
|
@@ -100,33 +100,33 @@ class Themes {
|
|
100 |
}
|
101 |
|
102 |
/**
|
103 |
-
* @param string $
|
104 |
* @param bool $bUseBackup
|
105 |
* @return bool
|
106 |
*/
|
107 |
-
public function reinstall( $
|
108 |
$bSuccess = false;
|
109 |
|
110 |
-
if ( $this->isInstalled( $
|
111 |
-
$
|
112 |
|
113 |
-
$oTheme = $this->getTheme( $
|
114 |
|
115 |
$sDir = $oTheme->get_stylesheet_directory();
|
116 |
-
$sBackupDir = dirname( $sDir ).'/../../'.$
|
117 |
if ( $bUseBackup ) {
|
118 |
rename( $sDir, $sBackupDir );
|
119 |
}
|
120 |
|
121 |
-
$bSuccess = $this->installFromWpOrg( $
|
122 |
if ( $bSuccess ) {
|
123 |
wp_update_themes(); //refreshes our update information
|
124 |
if ( $bUseBackup ) {
|
125 |
-
$
|
126 |
}
|
127 |
}
|
128 |
elseif ( $bUseBackup ) {
|
129 |
-
$
|
130 |
rename( $sBackupDir, $sDir );
|
131 |
}
|
132 |
}
|
@@ -134,15 +134,15 @@ class Themes {
|
|
134 |
}
|
135 |
|
136 |
/**
|
137 |
-
* @param string $
|
138 |
* @return array
|
139 |
*/
|
140 |
-
public function update( $
|
141 |
require_once( ABSPATH.'wp-admin/includes/upgrade.php' );
|
142 |
require_once( ABSPATH.'wp-admin/includes/class-wp-upgrader.php' );
|
143 |
|
144 |
$oSkin = new \Automatic_Upgrader_Skin();
|
145 |
-
$mResult = ( new \Theme_Upgrader( $oSkin ) )->upgrade( $
|
146 |
|
147 |
return [
|
148 |
'successful' => $mResult === true,
|
@@ -165,20 +165,16 @@ class Themes {
|
|
165 |
return $this->getTheme();
|
166 |
}
|
167 |
|
168 |
-
|
169 |
-
|
170 |
-
* @return bool
|
171 |
-
*/
|
172 |
-
public function getExists( $sStylesheet ) {
|
173 |
-
return $this->getTheme( $sStylesheet )->exists();
|
174 |
}
|
175 |
|
176 |
/**
|
177 |
-
* @param string $
|
178 |
* @return string
|
179 |
*/
|
180 |
-
public function getInstallationDir( $
|
181 |
-
return wp_normalize_path( $this->getTheme( $
|
182 |
}
|
183 |
|
184 |
/**
|
@@ -192,24 +188,24 @@ class Themes {
|
|
192 |
}
|
193 |
|
194 |
/**
|
195 |
-
* @param string $
|
196 |
* @param bool $bReload
|
197 |
* @return WpThemeVo|null
|
198 |
*/
|
199 |
-
public function getThemeAsVo( $
|
200 |
try {
|
201 |
if ( !is_array( $this->aLoadedVOs ) ) {
|
202 |
$this->aLoadedVOs = [];
|
203 |
}
|
204 |
-
if ( $bReload || !isset( $this->aLoadedVOs[ $
|
205 |
-
$this->aLoadedVOs[ $
|
206 |
}
|
207 |
-
$
|
208 |
}
|
209 |
-
catch ( \Exception $
|
210 |
-
$
|
211 |
}
|
212 |
-
return $
|
213 |
}
|
214 |
|
215 |
/**
|
@@ -218,8 +214,8 @@ class Themes {
|
|
218 |
public function getThemesAsVo() {
|
219 |
return array_filter(
|
220 |
array_map(
|
221 |
-
function ( $
|
222 |
-
return $this->getThemeAsVo( $
|
223 |
},
|
224 |
$this->getInstalledStylesheets()
|
225 |
)
|
@@ -229,11 +225,10 @@ class Themes {
|
|
229 |
/**
|
230 |
* @return string[]
|
231 |
*/
|
232 |
-
public function getInstalledStylesheets() {
|
233 |
return array_map(
|
234 |
-
function ( $
|
235 |
-
|
236 |
-
return $oTheme->get_stylesheet();
|
237 |
},
|
238 |
$this->getThemes()
|
239 |
);
|
@@ -244,18 +239,18 @@ class Themes {
|
|
244 |
* Abstracts the WordPress wp_get_themes()
|
245 |
* @return \WP_Theme[]
|
246 |
*/
|
247 |
-
public function getThemes() {
|
248 |
require_once( ABSPATH.'wp-admin/includes/theme.php' );
|
249 |
return function_exists( 'wp_get_themes' ) ? wp_get_themes() : [];
|
250 |
}
|
251 |
|
252 |
/**
|
253 |
-
* @param string $
|
254 |
* @return array|null
|
255 |
*/
|
256 |
-
public function getUpdateInfo( $
|
257 |
-
$
|
258 |
-
return
|
259 |
}
|
260 |
|
261 |
/**
|
@@ -282,20 +277,20 @@ class Themes {
|
|
282 |
* @return array[] - keys are theme stylesheets
|
283 |
*/
|
284 |
public function getAllExtendedData() {
|
285 |
-
$
|
286 |
return array_merge(
|
287 |
-
|
288 |
-
|
289 |
);
|
290 |
}
|
291 |
|
292 |
/**
|
293 |
-
* @param string $
|
294 |
* @return array
|
295 |
*/
|
296 |
-
public function getExtendedData( $
|
297 |
-
$
|
298 |
-
return
|
299 |
}
|
300 |
|
301 |
/**
|
@@ -303,49 +298,30 @@ class Themes {
|
|
303 |
* @param bool $bCheckIsActiveParent
|
304 |
* @return bool
|
305 |
*/
|
306 |
-
public function isActive( $sSlug, $bCheckIsActiveParent = false ) {
|
307 |
return ( $this->isInstalled( $sSlug ) && $this->getCurrent()->get_stylesheet() == $sSlug )
|
308 |
|| ( $bCheckIsActiveParent && $this->isActiveParent( $sSlug ) );
|
309 |
}
|
310 |
|
311 |
-
|
312 |
-
|
313 |
-
|
314 |
-
public function isActiveThemeAChild() {
|
315 |
-
$oTheme = $this->getCurrent();
|
316 |
-
return ( $oTheme->get_stylesheet() != $oTheme->get_template() );
|
317 |
}
|
318 |
|
319 |
-
|
320 |
-
|
321 |
-
* @return bool - true if this is the Parent of the currently active theme
|
322 |
-
*/
|
323 |
-
public function isActiveParent( $sSlug ) {
|
324 |
-
return ( $this->isInstalled( $sSlug ) && $this->getCurrent()->get_template() == $sSlug );
|
325 |
}
|
326 |
|
327 |
-
|
328 |
-
|
329 |
-
* @return bool
|
330 |
-
*/
|
331 |
-
public function isInstalled( $sSlug ) {
|
332 |
-
return ( !empty( $sSlug ) && $this->getExists( $sSlug ) );
|
333 |
}
|
334 |
|
335 |
-
|
336 |
-
|
337 |
-
* @return bool
|
338 |
-
*/
|
339 |
-
public function isUpdateAvailable( $sSlug ) {
|
340 |
-
return !is_null( $this->getUpdateInfo( $sSlug ) );
|
341 |
}
|
342 |
|
343 |
-
|
344 |
-
|
345 |
-
* @return bool
|
346 |
-
*/
|
347 |
-
public function isWpOrg( $sBaseName ) {
|
348 |
-
return $this->getThemeAsVo( $sBaseName )->isWpOrg();
|
349 |
}
|
350 |
|
351 |
/**
|
19 |
private $aLoadedVOs;
|
20 |
|
21 |
/**
|
22 |
+
* @param string $stylesheet
|
23 |
* @return bool
|
24 |
*/
|
25 |
+
public function activate( $stylesheet ) {
|
26 |
+
if ( empty( $stylesheet ) ) {
|
27 |
return false;
|
28 |
}
|
29 |
|
30 |
+
$oTheme = $this->getTheme( $stylesheet );
|
31 |
if ( !$oTheme->exists() ) {
|
32 |
return false;
|
33 |
}
|
37 |
// Now test currently active theme
|
38 |
$oCurrentTheme = $this->getCurrent();
|
39 |
|
40 |
+
return ( $stylesheet == $oCurrentTheme->get_stylesheet() );
|
41 |
}
|
42 |
|
43 |
/**
|
44 |
+
* @param string $stylesheet
|
45 |
* @return bool|\WP_Error
|
46 |
*/
|
47 |
+
public function delete( $stylesheet ) {
|
48 |
+
if ( empty( $stylesheet ) ) {
|
49 |
return false;
|
50 |
}
|
51 |
if ( !function_exists( 'delete_theme' ) ) {
|
52 |
require_once( ABSPATH.'wp-admin/includes/theme.php' );
|
53 |
}
|
54 |
+
return function_exists( 'delete_theme' ) ? delete_theme( $stylesheet ) : false;
|
55 |
}
|
56 |
|
57 |
/**
|
58 |
+
* @param string $slug
|
59 |
* @return bool
|
60 |
*/
|
61 |
+
public function installFromWpOrg( $slug ) {
|
62 |
+
$success = false;
|
63 |
try {
|
64 |
+
$theme = ( new Api() )
|
65 |
+
->setWorkingSlug( $slug )
|
66 |
->getInfo();
|
67 |
+
if ( !empty( $theme->download_link ) ) {
|
68 |
+
$success = $this->install( $theme->download_link, true )[ 'successful' ];
|
69 |
}
|
70 |
}
|
71 |
+
catch ( \Exception $e ) {
|
72 |
}
|
73 |
+
return $success;
|
74 |
}
|
75 |
|
76 |
/**
|
100 |
}
|
101 |
|
102 |
/**
|
103 |
+
* @param string $slug
|
104 |
* @param bool $bUseBackup
|
105 |
* @return bool
|
106 |
*/
|
107 |
+
public function reinstall( $slug, $bUseBackup = false ) {
|
108 |
$bSuccess = false;
|
109 |
|
110 |
+
if ( $this->isInstalled( $slug ) ) {
|
111 |
+
$FS = Services::WpFs();
|
112 |
|
113 |
+
$oTheme = $this->getTheme( $slug );
|
114 |
|
115 |
$sDir = $oTheme->get_stylesheet_directory();
|
116 |
+
$sBackupDir = dirname( $sDir ).'/../../'.$slug.'bak'.time();
|
117 |
if ( $bUseBackup ) {
|
118 |
rename( $sDir, $sBackupDir );
|
119 |
}
|
120 |
|
121 |
+
$bSuccess = $this->installFromWpOrg( $slug );
|
122 |
if ( $bSuccess ) {
|
123 |
wp_update_themes(); //refreshes our update information
|
124 |
if ( $bUseBackup ) {
|
125 |
+
$FS->deleteDir( $sBackupDir );
|
126 |
}
|
127 |
}
|
128 |
elseif ( $bUseBackup ) {
|
129 |
+
$FS->deleteDir( $sDir );
|
130 |
rename( $sBackupDir, $sDir );
|
131 |
}
|
132 |
}
|
134 |
}
|
135 |
|
136 |
/**
|
137 |
+
* @param string $file
|
138 |
* @return array
|
139 |
*/
|
140 |
+
public function update( $file ) :array {
|
141 |
require_once( ABSPATH.'wp-admin/includes/upgrade.php' );
|
142 |
require_once( ABSPATH.'wp-admin/includes/class-wp-upgrader.php' );
|
143 |
|
144 |
$oSkin = new \Automatic_Upgrader_Skin();
|
145 |
+
$mResult = ( new \Theme_Upgrader( $oSkin ) )->upgrade( $file );
|
146 |
|
147 |
return [
|
148 |
'successful' => $mResult === true,
|
165 |
return $this->getTheme();
|
166 |
}
|
167 |
|
168 |
+
public function getExists( string $stylesheet ) :bool {
|
169 |
+
return $this->getTheme( $stylesheet )->exists();
|
|
|
|
|
|
|
|
|
170 |
}
|
171 |
|
172 |
/**
|
173 |
+
* @param string $slug - the folder name of the theme
|
174 |
* @return string
|
175 |
*/
|
176 |
+
public function getInstallationDir( $slug ) {
|
177 |
+
return wp_normalize_path( $this->getTheme( $slug )->get_stylesheet_directory() );
|
178 |
}
|
179 |
|
180 |
/**
|
188 |
}
|
189 |
|
190 |
/**
|
191 |
+
* @param string $stylesheet
|
192 |
* @param bool $bReload
|
193 |
* @return WpThemeVo|null
|
194 |
*/
|
195 |
+
public function getThemeAsVo( $stylesheet, $bReload = false ) {
|
196 |
try {
|
197 |
if ( !is_array( $this->aLoadedVOs ) ) {
|
198 |
$this->aLoadedVOs = [];
|
199 |
}
|
200 |
+
if ( $bReload || !isset( $this->aLoadedVOs[ $stylesheet ] ) ) {
|
201 |
+
$this->aLoadedVOs[ $stylesheet ] = new WpThemeVo( $stylesheet );
|
202 |
}
|
203 |
+
$asset = $this->aLoadedVOs[ $stylesheet ];
|
204 |
}
|
205 |
+
catch ( \Exception $e ) {
|
206 |
+
$asset = null;
|
207 |
}
|
208 |
+
return $asset;
|
209 |
}
|
210 |
|
211 |
/**
|
214 |
public function getThemesAsVo() {
|
215 |
return array_filter(
|
216 |
array_map(
|
217 |
+
function ( $stylesheet ) {
|
218 |
+
return $this->getThemeAsVo( $stylesheet );
|
219 |
},
|
220 |
$this->getInstalledStylesheets()
|
221 |
)
|
225 |
/**
|
226 |
* @return string[]
|
227 |
*/
|
228 |
+
public function getInstalledStylesheets() :array {
|
229 |
return array_map(
|
230 |
+
function ( $theme ) {
|
231 |
+
return $theme->get_stylesheet();
|
|
|
232 |
},
|
233 |
$this->getThemes()
|
234 |
);
|
239 |
* Abstracts the WordPress wp_get_themes()
|
240 |
* @return \WP_Theme[]
|
241 |
*/
|
242 |
+
public function getThemes() :array {
|
243 |
require_once( ABSPATH.'wp-admin/includes/theme.php' );
|
244 |
return function_exists( 'wp_get_themes' ) ? wp_get_themes() : [];
|
245 |
}
|
246 |
|
247 |
/**
|
248 |
+
* @param string $slug
|
249 |
* @return array|null
|
250 |
*/
|
251 |
+
public function getUpdateInfo( $slug ) {
|
252 |
+
$updates = $this->getUpdates();
|
253 |
+
return $updates[ $slug ] ?? null;
|
254 |
}
|
255 |
|
256 |
/**
|
277 |
* @return array[] - keys are theme stylesheets
|
278 |
*/
|
279 |
public function getAllExtendedData() {
|
280 |
+
$data = Services::WpGeneral()->getTransient( 'update_themes' );
|
281 |
return array_merge(
|
282 |
+
$data->no_update ?? [],
|
283 |
+
$data->response ?? []
|
284 |
);
|
285 |
}
|
286 |
|
287 |
/**
|
288 |
+
* @param string $slug
|
289 |
* @return array
|
290 |
*/
|
291 |
+
public function getExtendedData( $slug ) {
|
292 |
+
$data = $this->getAllExtendedData();
|
293 |
+
return $data[ $slug ] ?? [];
|
294 |
}
|
295 |
|
296 |
/**
|
298 |
* @param bool $bCheckIsActiveParent
|
299 |
* @return bool
|
300 |
*/
|
301 |
+
public function isActive( $sSlug, $bCheckIsActiveParent = false ) :bool {
|
302 |
return ( $this->isInstalled( $sSlug ) && $this->getCurrent()->get_stylesheet() == $sSlug )
|
303 |
|| ( $bCheckIsActiveParent && $this->isActiveParent( $sSlug ) );
|
304 |
}
|
305 |
|
306 |
+
public function isActiveThemeAChild() :bool {
|
307 |
+
$current = $this->getCurrent();
|
308 |
+
return $current->get_stylesheet() !== $current->get_template();
|
|
|
|
|
|
|
309 |
}
|
310 |
|
311 |
+
public function isActiveParent( string $slug ) :bool {
|
312 |
+
return $this->isInstalled( $slug ) && $this->getCurrent()->get_template() == $slug;
|
|
|
|
|
|
|
|
|
313 |
}
|
314 |
|
315 |
+
public function isInstalled( string $slug ) :bool {
|
316 |
+
return !empty( $slug ) && $this->getExists( $slug );
|
|
|
|
|
|
|
|
|
317 |
}
|
318 |
|
319 |
+
public function isUpdateAvailable( string $slug ) :bool {
|
320 |
+
return !is_null( $this->getUpdateInfo( $slug ) );
|
|
|
|
|
|
|
|
|
321 |
}
|
322 |
|
323 |
+
public function isWpOrg( string $stylesheet ) :bool {
|
324 |
+
return $this->getThemeAsVo( $stylesheet )->isWpOrg();
|
|
|
|
|
|
|
|
|
325 |
}
|
326 |
|
327 |
/**
|
src/lib/vendor/fernleafsystems/wordpress-services/src/Core/Users.php
CHANGED
@@ -170,8 +170,8 @@ class Users {
|
|
170 |
* @return null|\WP_User
|
171 |
*/
|
172 |
public function getUserBy( $byKey, $value ) {
|
173 |
-
$
|
174 |
-
return empty( $
|
175 |
}
|
176 |
|
177 |
/**
|
170 |
* @return null|\WP_User
|
171 |
*/
|
172 |
public function getUserBy( $byKey, $value ) {
|
173 |
+
$user = function_exists( 'get_user_by' ) ? get_user_by( $byKey, $value ) : null;
|
174 |
+
return empty( $user ) ? null : $user;
|
175 |
}
|
176 |
|
177 |
/**
|
src/lib/vendor/fernleafsystems/wordpress-services/src/Core/VOs/WpBaseVo.php
CHANGED
@@ -62,7 +62,7 @@ abstract class WpBaseVo {
|
|
62 |
/**
|
63 |
* @return string
|
64 |
*/
|
65 |
-
abstract public function getInstallDir();
|
66 |
|
67 |
/**
|
68 |
* @return bool
|
@@ -72,10 +72,7 @@ abstract class WpBaseVo {
|
|
72 |
return !empty( $this->new_version ) && version_compare( $this->new_version, $this->Version, '>' );
|
73 |
}
|
74 |
|
75 |
-
|
76 |
-
* @return bool
|
77 |
-
*/
|
78 |
-
abstract public function isWpOrg();
|
79 |
|
80 |
/**
|
81 |
* @return array
|
@@ -85,7 +82,7 @@ abstract class WpBaseVo {
|
|
85 |
/**
|
86 |
* @return string[]
|
87 |
*/
|
88 |
-
protected function getExtendedDataSlugs() {
|
89 |
return [
|
90 |
'new_version',
|
91 |
];
|
62 |
/**
|
63 |
* @return string
|
64 |
*/
|
65 |
+
abstract public function getInstallDir():string;
|
66 |
|
67 |
/**
|
68 |
* @return bool
|
72 |
return !empty( $this->new_version ) && version_compare( $this->new_version, $this->Version, '>' );
|
73 |
}
|
74 |
|
75 |
+
abstract public function isWpOrg() :bool;
|
|
|
|
|
|
|
76 |
|
77 |
/**
|
78 |
* @return array
|
82 |
/**
|
83 |
* @return string[]
|
84 |
*/
|
85 |
+
protected function getExtendedDataSlugs() :array {
|
86 |
return [
|
87 |
'new_version',
|
88 |
];
|
src/lib/vendor/fernleafsystems/wordpress-services/src/Core/VOs/WpPluginVo.php
CHANGED
@@ -28,18 +28,18 @@ class WpPluginVo extends WpBaseVo {
|
|
28 |
|
29 |
/**
|
30 |
* WpPluginVo constructor.
|
31 |
-
* @param string $
|
32 |
* @throws \Exception
|
33 |
*/
|
34 |
-
public function __construct( $
|
35 |
-
$
|
36 |
-
$
|
37 |
-
if ( empty( $
|
38 |
-
throw new \Exception( sprintf( 'Plugin file %s does not exist', $
|
39 |
}
|
40 |
-
$this->applyFromArray( $
|
41 |
-
$this->file = $
|
42 |
-
$this->active = $
|
43 |
}
|
44 |
|
45 |
/**
|
@@ -79,14 +79,11 @@ class WpPluginVo extends WpBaseVo {
|
|
79 |
/**
|
80 |
* @return string
|
81 |
*/
|
82 |
-
public function getInstallDir() {
|
83 |
return wp_normalize_path( trailingslashit( dirname( path_join( WP_PLUGIN_DIR, $this->file ) ) ) );
|
84 |
}
|
85 |
|
86 |
-
|
87 |
-
* @return bool
|
88 |
-
*/
|
89 |
-
public function isWpOrg() {
|
90 |
$this->id; // loads the data
|
91 |
return isset( $this->id ) ? strpos( $this->id, 'w.org/' ) === 0 : false;
|
92 |
}
|
@@ -101,7 +98,7 @@ class WpPluginVo extends WpBaseVo {
|
|
101 |
/**
|
102 |
* @return string[]
|
103 |
*/
|
104 |
-
protected function getExtendedDataSlugs() {
|
105 |
return array_merge( parent::getExtendedDataSlugs(), [
|
106 |
'id',
|
107 |
'slug',
|
28 |
|
29 |
/**
|
30 |
* WpPluginVo constructor.
|
31 |
+
* @param string $baseFile
|
32 |
* @throws \Exception
|
33 |
*/
|
34 |
+
public function __construct( string $baseFile ) {
|
35 |
+
$WPP = Services::WpPlugins();
|
36 |
+
$p = $WPP->getPlugin( $baseFile );
|
37 |
+
if ( empty( $p ) ) {
|
38 |
+
throw new \Exception( sprintf( 'Plugin file %s does not exist', $baseFile ) );
|
39 |
}
|
40 |
+
$this->applyFromArray( $p );
|
41 |
+
$this->file = $baseFile;
|
42 |
+
$this->active = $WPP->isActive( $baseFile );
|
43 |
}
|
44 |
|
45 |
/**
|
79 |
/**
|
80 |
* @return string
|
81 |
*/
|
82 |
+
public function getInstallDir() :string {
|
83 |
return wp_normalize_path( trailingslashit( dirname( path_join( WP_PLUGIN_DIR, $this->file ) ) ) );
|
84 |
}
|
85 |
|
86 |
+
public function isWpOrg() :bool {
|
|
|
|
|
|
|
87 |
$this->id; // loads the data
|
88 |
return isset( $this->id ) ? strpos( $this->id, 'w.org/' ) === 0 : false;
|
89 |
}
|
98 |
/**
|
99 |
* @return string[]
|
100 |
*/
|
101 |
+
protected function getExtendedDataSlugs() :array {
|
102 |
return array_merge( parent::getExtendedDataSlugs(), [
|
103 |
'id',
|
104 |
'slug',
|
src/lib/vendor/fernleafsystems/wordpress-services/src/Core/VOs/WpThemeVo.php
CHANGED
@@ -24,20 +24,20 @@ class WpThemeVo extends WpBaseVo {
|
|
24 |
|
25 |
/**
|
26 |
* WpPluginVo constructor.
|
27 |
-
* @param string $
|
28 |
* @throws \Exception
|
29 |
*/
|
30 |
-
public function __construct( $
|
31 |
-
$
|
32 |
-
$
|
33 |
-
if ( empty( $
|
34 |
-
throw new \Exception( sprintf( 'Theme file %s does not exist', $
|
35 |
}
|
36 |
-
$this->wp_theme = $
|
37 |
-
$this->stylesheet = $
|
38 |
-
$this->active = $
|
39 |
-
$this->is_child = $this->active && $
|
40 |
-
$this->is_parent = !$this->active && $
|
41 |
}
|
42 |
|
43 |
/**
|
@@ -75,7 +75,7 @@ class WpThemeVo extends WpBaseVo {
|
|
75 |
/**
|
76 |
* @return string[]
|
77 |
*/
|
78 |
-
private function getWpThemeKeys() {
|
79 |
return [
|
80 |
'Name',
|
81 |
'ThemeURI',
|
@@ -91,17 +91,11 @@ class WpThemeVo extends WpBaseVo {
|
|
91 |
];
|
92 |
}
|
93 |
|
94 |
-
|
95 |
-
* @return string
|
96 |
-
*/
|
97 |
-
public function getInstallDir() {
|
98 |
return wp_normalize_path( trailingslashit( $this->wp_theme->get_stylesheet_directory() ) );
|
99 |
}
|
100 |
|
101 |
-
|
102 |
-
* @return bool
|
103 |
-
*/
|
104 |
-
public function isWpOrg() {
|
105 |
$this->wp_info;
|
106 |
return !empty( $this->wp_info );
|
107 |
}
|
@@ -116,7 +110,7 @@ class WpThemeVo extends WpBaseVo {
|
|
116 |
/**
|
117 |
* @return string[]
|
118 |
*/
|
119 |
-
protected function getExtendedDataSlugs() {
|
120 |
return array_merge( parent::getExtendedDataSlugs(), [
|
121 |
'theme',
|
122 |
'package',
|
@@ -135,7 +129,7 @@ class WpThemeVo extends WpBaseVo {
|
|
135 |
->setWorkingSlug( $this->stylesheet )
|
136 |
->getInfo();
|
137 |
}
|
138 |
-
catch ( \Exception $
|
139 |
$oInfo = false;
|
140 |
}
|
141 |
return $oInfo;
|
24 |
|
25 |
/**
|
26 |
* WpPluginVo constructor.
|
27 |
+
* @param string $stylesheet - the name of the theme folder.
|
28 |
* @throws \Exception
|
29 |
*/
|
30 |
+
public function __construct( string $stylesheet ) {
|
31 |
+
$WPT = Services::WpThemes();
|
32 |
+
$t = $WPT->getTheme( $stylesheet );
|
33 |
+
if ( empty( $t ) ) {
|
34 |
+
throw new \Exception( sprintf( 'Theme file %s does not exist', $stylesheet ) );
|
35 |
}
|
36 |
+
$this->wp_theme = $t;
|
37 |
+
$this->stylesheet = $stylesheet;
|
38 |
+
$this->active = $WPT->isActive( $stylesheet );
|
39 |
+
$this->is_child = $this->active && $WPT->isActiveThemeAChild();
|
40 |
+
$this->is_parent = !$this->active && $WPT->isActiveParent( $stylesheet );
|
41 |
}
|
42 |
|
43 |
/**
|
75 |
/**
|
76 |
* @return string[]
|
77 |
*/
|
78 |
+
private function getWpThemeKeys() :array {
|
79 |
return [
|
80 |
'Name',
|
81 |
'ThemeURI',
|
91 |
];
|
92 |
}
|
93 |
|
94 |
+
public function getInstallDir() :string {
|
|
|
|
|
|
|
95 |
return wp_normalize_path( trailingslashit( $this->wp_theme->get_stylesheet_directory() ) );
|
96 |
}
|
97 |
|
98 |
+
public function isWpOrg() :bool {
|
|
|
|
|
|
|
99 |
$this->wp_info;
|
100 |
return !empty( $this->wp_info );
|
101 |
}
|
110 |
/**
|
111 |
* @return string[]
|
112 |
*/
|
113 |
+
protected function getExtendedDataSlugs() :array {
|
114 |
return array_merge( parent::getExtendedDataSlugs(), [
|
115 |
'theme',
|
116 |
'package',
|
129 |
->setWorkingSlug( $this->stylesheet )
|
130 |
->getInfo();
|
131 |
}
|
132 |
+
catch ( \Exception $e ) {
|
133 |
$oInfo = false;
|
134 |
}
|
135 |
return $oInfo;
|
src/lib/vendor/fernleafsystems/wordpress-services/src/Utilities/Data.php
CHANGED
@@ -48,21 +48,17 @@ class Data {
|
|
48 |
* PHP Code.
|
49 |
* Why use this? In the name of naive security, silly web hosts can prevent reading the contents of
|
50 |
* non-PHP files so we simply put the content we want to have read into a php file and then "include" it.
|
51 |
-
* @param string $
|
52 |
* @return string
|
53 |
*/
|
54 |
-
public function readFileContentsUsingInclude( $
|
55 |
ob_start();
|
56 |
-
include( $
|
57 |
return ob_get_clean();
|
58 |
}
|
59 |
|
60 |
-
|
61 |
-
|
62 |
-
* @return string
|
63 |
-
*/
|
64 |
-
public function urlStripQueryPart( $sUrl ) {
|
65 |
-
return preg_replace( '#\s?\?.*$#', '', $sUrl );
|
66 |
}
|
67 |
|
68 |
/**
|
@@ -74,35 +70,35 @@ class Data {
|
|
74 |
}
|
75 |
|
76 |
/**
|
77 |
-
* @param string $
|
78 |
* @return bool
|
79 |
*/
|
80 |
-
public function isValidWebUrl( $
|
81 |
-
$
|
82 |
-
return filter_var( $
|
83 |
-
&& in_array( parse_url( $
|
84 |
}
|
85 |
|
86 |
/**
|
87 |
-
* @param string $
|
88 |
* @return bool
|
89 |
*/
|
90 |
-
public function verifyUrl( $
|
91 |
try {
|
92 |
-
$
|
93 |
}
|
94 |
-
catch ( \Exception $
|
95 |
-
$
|
96 |
}
|
97 |
-
return $
|
98 |
}
|
99 |
|
100 |
/**
|
101 |
-
* @param string $
|
102 |
* @return bool
|
103 |
*/
|
104 |
-
public function validEmail( $
|
105 |
-
return
|
106 |
}
|
107 |
|
108 |
/**
|
48 |
* PHP Code.
|
49 |
* Why use this? In the name of naive security, silly web hosts can prevent reading the contents of
|
50 |
* non-PHP files so we simply put the content we want to have read into a php file and then "include" it.
|
51 |
+
* @param string $file
|
52 |
* @return string
|
53 |
*/
|
54 |
+
public function readFileContentsUsingInclude( string $file ) {
|
55 |
ob_start();
|
56 |
+
include( $file );
|
57 |
return ob_get_clean();
|
58 |
}
|
59 |
|
60 |
+
public function urlStripQueryPart( $url ) :string {
|
61 |
+
return preg_replace( '#\s?\?.*$#', '', $url );
|
|
|
|
|
|
|
|
|
62 |
}
|
63 |
|
64 |
/**
|
70 |
}
|
71 |
|
72 |
/**
|
73 |
+
* @param string $url
|
74 |
* @return bool
|
75 |
*/
|
76 |
+
public function isValidWebUrl( $url ) {
|
77 |
+
$url = trim( $this->urlStripQueryPart( $url ) );
|
78 |
+
return filter_var( $url, FILTER_VALIDATE_URL )
|
79 |
+
&& in_array( parse_url( $url, PHP_URL_SCHEME ), [ 'http', 'https' ] );
|
80 |
}
|
81 |
|
82 |
/**
|
83 |
+
* @param string $url
|
84 |
* @return bool
|
85 |
*/
|
86 |
+
public function verifyUrl( $url ) :bool {
|
87 |
try {
|
88 |
+
$valid = $this->isValidWebUrl( $url ) && ( new HttpUtil() )->checkUrl( $url );
|
89 |
}
|
90 |
+
catch ( \Exception $e ) {
|
91 |
+
$valid = false;
|
92 |
}
|
93 |
+
return $valid;
|
94 |
}
|
95 |
|
96 |
/**
|
97 |
+
* @param string $email
|
98 |
* @return bool
|
99 |
*/
|
100 |
+
public function validEmail( $email ) :bool {
|
101 |
+
return !empty( $email ) && is_email( $email );
|
102 |
}
|
103 |
|
104 |
/**
|
src/lib/vendor/fernleafsystems/wordpress-services/src/Utilities/File/GetFileAsArray.php
CHANGED
@@ -14,22 +14,22 @@ use FernleafSystems\Wordpress\Services\Services;
|
|
14 |
class GetFileAsArray {
|
15 |
|
16 |
/**
|
17 |
-
* @param string $
|
18 |
-
* @param string $
|
19 |
* @return string[]
|
20 |
* @throws \Exception
|
21 |
*/
|
22 |
-
public function run( $
|
23 |
-
$
|
24 |
-
if ( !$
|
25 |
throw new \InvalidArgumentException( 'File does not exist' );
|
26 |
}
|
27 |
|
28 |
-
$
|
29 |
-
if ( empty( $
|
30 |
throw new \Exception( 'File is empty' );
|
31 |
}
|
32 |
|
33 |
-
return
|
34 |
}
|
35 |
}
|
14 |
class GetFileAsArray {
|
15 |
|
16 |
/**
|
17 |
+
* @param string $path
|
18 |
+
* @param string $splitOn
|
19 |
* @return string[]
|
20 |
* @throws \Exception
|
21 |
*/
|
22 |
+
public function run( $path, $splitOn = '\r\n|\r|\n' ) :array {
|
23 |
+
$FS = Services::WpFs();
|
24 |
+
if ( !$FS->isFile( $path ) ) {
|
25 |
throw new \InvalidArgumentException( 'File does not exist' );
|
26 |
}
|
27 |
|
28 |
+
$content = $FS->getFileContent( $path );
|
29 |
+
if ( empty( $content ) ) {
|
30 |
throw new \Exception( 'File is empty' );
|
31 |
}
|
32 |
|
33 |
+
return preg_split( '/\r\n|\r|\n/', $content );
|
34 |
}
|
35 |
}
|
src/lib/vendor/fernleafsystems/wordpress-services/src/Utilities/File/LocateStrInFile.php
CHANGED
@@ -13,44 +13,55 @@ class LocateStrInFile {
|
|
13 |
/**
|
14 |
* @var string
|
15 |
*/
|
16 |
-
private $
|
17 |
|
18 |
/**
|
19 |
* @var string
|
20 |
*/
|
21 |
-
private $
|
22 |
|
23 |
/**
|
24 |
* @var string
|
25 |
*/
|
26 |
-
private $
|
27 |
|
28 |
/**
|
29 |
* @var string[]
|
30 |
*/
|
31 |
-
private $
|
32 |
|
33 |
/**
|
34 |
* @var bool
|
35 |
*/
|
36 |
-
private $
|
37 |
|
38 |
/**
|
39 |
* @return string[]
|
40 |
*/
|
41 |
-
public function run() {
|
42 |
return $this->isRegEx() ? $this->runAsRegEx() : $this->runAsSimple();
|
43 |
}
|
44 |
|
45 |
/**
|
46 |
* @return string[] - keys are line numbers
|
47 |
*/
|
48 |
-
protected function runAsRegEx() {
|
49 |
-
$
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
50 |
return array_filter(
|
51 |
$this->getLines(),
|
52 |
-
function ( $
|
53 |
-
return
|
54 |
}
|
55 |
);
|
56 |
}
|
@@ -58,18 +69,12 @@ class LocateStrInFile {
|
|
58 |
/**
|
59 |
* @return string[] - keys are line numbers
|
60 |
*/
|
61 |
-
protected function runAsSimple() {
|
62 |
-
$
|
63 |
-
$sNeedle = $this->getNeedle();
|
64 |
if ( stripos( $this->getContent(), $this->getNeedle() ) !== false ) {
|
65 |
-
$
|
66 |
-
$this->getLines(),
|
67 |
-
function ( $sLine ) use ( $sNeedle ) {
|
68 |
-
return ( strpos( $sLine, $sNeedle ) !== false );
|
69 |
-
}
|
70 |
-
);
|
71 |
}
|
72 |
-
return $
|
73 |
}
|
74 |
|
75 |
/**
|
@@ -78,7 +83,7 @@ class LocateStrInFile {
|
|
78 |
* @throws \InvalidArgumentException
|
79 |
* @deprecated
|
80 |
*/
|
81 |
-
public function inFile( $sPath ) {
|
82 |
return $this->setPath( $sPath )
|
83 |
->run();
|
84 |
}
|
@@ -86,81 +91,65 @@ class LocateStrInFile {
|
|
86 |
/**
|
87 |
* @return string[]
|
88 |
*/
|
89 |
-
protected function getLines() {
|
90 |
-
if ( is_null( $this->
|
91 |
-
$this->
|
|
|
|
|
92 |
}
|
93 |
-
return $this->
|
94 |
}
|
95 |
|
96 |
-
|
97 |
-
|
98 |
-
|
99 |
-
public function getContent() {
|
100 |
-
if ( is_null( $this->sContent ) ) {
|
101 |
-
$this->sContent = Services::WpFs()->getFileContent( $this->getPath() );
|
102 |
}
|
103 |
-
return $this->
|
104 |
}
|
105 |
|
106 |
-
|
107 |
-
|
108 |
-
*/
|
109 |
-
public function getNeedle() {
|
110 |
-
return $this->sNeedle;
|
111 |
}
|
112 |
|
113 |
-
|
114 |
-
|
115 |
-
*/
|
116 |
-
public function getPath() {
|
117 |
-
return $this->sPath;
|
118 |
}
|
119 |
|
120 |
-
|
121 |
-
|
122 |
-
*/
|
123 |
-
public function isRegEx() {
|
124 |
-
return (bool)$this->bIsRegExNeedle;
|
125 |
}
|
126 |
|
127 |
-
|
128 |
-
|
129 |
-
* @return $this
|
130 |
-
*/
|
131 |
-
public function setIsRegEx( $bIsRegEx ) {
|
132 |
-
$this->bIsRegExNeedle = $bIsRegEx;
|
133 |
return $this;
|
134 |
}
|
135 |
|
136 |
-
|
137 |
-
|
138 |
-
* @return $this
|
139 |
-
*/
|
140 |
-
public function setNeedle( $sStr ) {
|
141 |
-
$this->sNeedle = $sStr;
|
142 |
return $this;
|
143 |
}
|
144 |
|
145 |
/**
|
146 |
-
* @param string $
|
147 |
* @return $this
|
148 |
* @throws \InvalidArgumentException
|
|
|
149 |
*/
|
150 |
-
public function setPath( $
|
151 |
-
if ( !Services::WpFs()->isFile( $
|
152 |
-
throw new \InvalidArgumentException(
|
|
|
|
|
|
|
153 |
}
|
154 |
-
$this->
|
|
|
155 |
return $this->reset();
|
156 |
}
|
157 |
|
158 |
-
|
159 |
-
|
160 |
-
|
161 |
-
protected function reset() {
|
162 |
-
$this->sContent = null;
|
163 |
-
$this->aLines = null;
|
164 |
return $this;
|
165 |
}
|
166 |
}
|
13 |
/**
|
14 |
* @var string
|
15 |
*/
|
16 |
+
private $needle;
|
17 |
|
18 |
/**
|
19 |
* @var string
|
20 |
*/
|
21 |
+
private $content;
|
22 |
|
23 |
/**
|
24 |
* @var string
|
25 |
*/
|
26 |
+
private $path;
|
27 |
|
28 |
/**
|
29 |
* @var string[]
|
30 |
*/
|
31 |
+
private $lines;
|
32 |
|
33 |
/**
|
34 |
* @var bool
|
35 |
*/
|
36 |
+
private $isRegExNeedle;
|
37 |
|
38 |
/**
|
39 |
* @return string[]
|
40 |
*/
|
41 |
+
public function run() :array {
|
42 |
return $this->isRegEx() ? $this->runAsRegEx() : $this->runAsSimple();
|
43 |
}
|
44 |
|
45 |
/**
|
46 |
* @return string[] - keys are line numbers
|
47 |
*/
|
48 |
+
protected function runAsRegEx() :array {
|
49 |
+
$lines = [];
|
50 |
+
|
51 |
+
if ( preg_match_all( '/('.$this->getNeedle().')/i', $this->getContent(), $matches, PREG_PATTERN_ORDER ) ) {
|
52 |
+
foreach ( $matches[ 0 ] as $match ) {
|
53 |
+
// use + for numerical index
|
54 |
+
$lines = $lines + $this->findLinesFor( $match );
|
55 |
+
}
|
56 |
+
}
|
57 |
+
return $lines;
|
58 |
+
}
|
59 |
+
|
60 |
+
protected function findLinesFor( string $for ) :array {
|
61 |
return array_filter(
|
62 |
$this->getLines(),
|
63 |
+
function ( $line ) use ( $for ) {
|
64 |
+
return stripos( $line, $for ) !== false;
|
65 |
}
|
66 |
);
|
67 |
}
|
69 |
/**
|
70 |
* @return string[] - keys are line numbers
|
71 |
*/
|
72 |
+
protected function runAsSimple() :array {
|
73 |
+
$lines = [];
|
|
|
74 |
if ( stripos( $this->getContent(), $this->getNeedle() ) !== false ) {
|
75 |
+
$lines = $lines + $this->findLinesFor( $this->getNeedle() );
|
|
|
|
|
|
|
|
|
|
|
76 |
}
|
77 |
+
return $lines;
|
78 |
}
|
79 |
|
80 |
/**
|
83 |
* @throws \InvalidArgumentException
|
84 |
* @deprecated
|
85 |
*/
|
86 |
+
public function inFile( $sPath ) :array {
|
87 |
return $this->setPath( $sPath )
|
88 |
->run();
|
89 |
}
|
91 |
/**
|
92 |
* @return string[]
|
93 |
*/
|
94 |
+
protected function getLines() :array {
|
95 |
+
if ( is_null( $this->lines ) ) {
|
96 |
+
$this->lines = array_filter(
|
97 |
+
array_map( 'trim', preg_split( '/\r\n|\r|\n/', $this->getContent() ) )
|
98 |
+
);
|
99 |
}
|
100 |
+
return $this->lines;
|
101 |
}
|
102 |
|
103 |
+
public function getContent() :string {
|
104 |
+
if ( is_null( $this->content ) ) {
|
105 |
+
$this->content = Services::WpFs()->getFileContent( $this->getPath() );
|
|
|
|
|
|
|
106 |
}
|
107 |
+
return $this->content;
|
108 |
}
|
109 |
|
110 |
+
public function getNeedle() :string {
|
111 |
+
return $this->needle;
|
|
|
|
|
|
|
112 |
}
|
113 |
|
114 |
+
public function getPath() :string {
|
115 |
+
return $this->path;
|
|
|
|
|
|
|
116 |
}
|
117 |
|
118 |
+
public function isRegEx() :bool {
|
119 |
+
return (bool)$this->isRegExNeedle;
|
|
|
|
|
|
|
120 |
}
|
121 |
|
122 |
+
public function setIsRegEx( bool $isRegEx ) :self {
|
123 |
+
$this->isRegExNeedle = $isRegEx;
|
|
|
|
|
|
|
|
|
124 |
return $this;
|
125 |
}
|
126 |
|
127 |
+
public function setNeedle( string $needle ) :self {
|
128 |
+
$this->needle = $needle;
|
|
|
|
|
|
|
|
|
129 |
return $this;
|
130 |
}
|
131 |
|
132 |
/**
|
133 |
+
* @param string $path
|
134 |
* @return $this
|
135 |
* @throws \InvalidArgumentException
|
136 |
+
* @throws \Exception
|
137 |
*/
|
138 |
+
public function setPath( string $path ) :self {
|
139 |
+
if ( !Services::WpFs()->isFile( $path ) ) {
|
140 |
+
throw new \InvalidArgumentException( "File doesn't exist" );
|
141 |
+
}
|
142 |
+
if ( !is_readable( $path ) ) {
|
143 |
+
throw new \Exception( "File isn't readable" );
|
144 |
}
|
145 |
+
$this->path = $path;
|
146 |
+
$this->getContent();
|
147 |
return $this->reset();
|
148 |
}
|
149 |
|
150 |
+
protected function reset() :self {
|
151 |
+
$this->content = null;
|
152 |
+
$this->lines = null;
|
|
|
|
|
|
|
153 |
return $this;
|
154 |
}
|
155 |
}
|
src/lib/vendor/fernleafsystems/wordpress-services/src/Utilities/Net/IpIdentify.php
CHANGED
@@ -56,10 +56,7 @@ class IpIdentify {
|
|
56 |
throw new \Exception( "A valid IP address was not provided." );
|
57 |
}
|
58 |
|
59 |
-
if ( $
|
60 |
-
$is = self::THIS_SERVER;
|
61 |
-
}
|
62 |
-
elseif ( $srvProviders->isIp_AppleBot( $this->ip, $this->agent ) ) {
|
63 |
$is = self::APPLE;
|
64 |
}
|
65 |
elseif ( $srvProviders->isIp_BaiduBot( $this->ip, $this->agent ) ) {
|
@@ -116,6 +113,9 @@ class IpIdentify {
|
|
116 |
elseif ( $srvProviders->isIp_YandexBot( $this->ip, $this->agent ) ) {
|
117 |
$is = self::YANDEX;
|
118 |
}
|
|
|
|
|
|
|
119 |
elseif ( $srvIP->checkIp( $this->ip, $srvIP->getRequestIp() ) ) {
|
120 |
$is = self::VISITOR;
|
121 |
}
|
56 |
throw new \Exception( "A valid IP address was not provided." );
|
57 |
}
|
58 |
|
59 |
+
if ( $srvProviders->isIp_AppleBot( $this->ip, $this->agent ) ) {
|
|
|
|
|
|
|
60 |
$is = self::APPLE;
|
61 |
}
|
62 |
elseif ( $srvProviders->isIp_BaiduBot( $this->ip, $this->agent ) ) {
|
113 |
elseif ( $srvProviders->isIp_YandexBot( $this->ip, $this->agent ) ) {
|
114 |
$is = self::YANDEX;
|
115 |
}
|
116 |
+
elseif ( $srvIP->checkIp( $this->ip, $srvIP->getServerPublicIPs() ) ) {
|
117 |
+
$is = self::THIS_SERVER;
|
118 |
+
}
|
119 |
elseif ( $srvIP->checkIp( $this->ip, $srvIP->getRequestIp() ) ) {
|
120 |
$is = self::VISITOR;
|
121 |
}
|
src/lib/vendor/fernleafsystems/wordpress-services/src/Utilities/Obfuscate.php
CHANGED
@@ -1,17 +1,17 @@
|
|
1 |
-
<?php
|
2 |
|
3 |
namespace FernleafSystems\Wordpress\Services\Utilities;
|
4 |
|
5 |
class Obfuscate {
|
6 |
|
7 |
/**
|
8 |
-
* @param string $
|
9 |
* @return string
|
10 |
*/
|
11 |
-
public static function Email( $
|
12 |
-
list( $
|
13 |
-
return substr( $
|
14 |
.'@'.
|
15 |
-
substr( $
|
16 |
}
|
17 |
}
|
1 |
+
<?php declare( strict_types=1 );
|
2 |
|
3 |
namespace FernleafSystems\Wordpress\Services\Utilities;
|
4 |
|
5 |
class Obfuscate {
|
6 |
|
7 |
/**
|
8 |
+
* @param string $email
|
9 |
* @return string
|
10 |
*/
|
11 |
+
public static function Email( string $email ) :string {
|
12 |
+
list( $left, $right ) = explode( '@', $email, 2 );
|
13 |
+
return substr( $left, 0, 1 ).'****'.substr( $left, -1, 1 )
|
14 |
.'@'.
|
15 |
+
substr( $right, 0, 1 ).'****'.substr( $right, strrpos( $right, '.' ) );
|
16 |
}
|
17 |
}
|
src/lib/vendor/fernleafsystems/wordpress-services/src/Utilities/Options/Transient.php
CHANGED
@@ -13,33 +13,33 @@ use FernleafSystems\Wordpress\Services\Services;
|
|
13 |
class Transient {
|
14 |
|
15 |
/**
|
16 |
-
* @param string $
|
17 |
-
* @param bool $
|
18 |
* @return bool
|
19 |
*/
|
20 |
-
public static function Delete( $
|
21 |
$oWP = Services::WpGeneral();
|
22 |
return $oWP->canUseTransients() ?
|
23 |
-
$oWP->deleteTransient( $
|
24 |
-
: Services::WpGeneral()->deleteOption( System::PREFIX.'trans_'.$
|
25 |
}
|
26 |
|
27 |
/**
|
28 |
-
* @param string $
|
29 |
-
* @param null $
|
30 |
-
* @param bool $
|
31 |
* @return mixed|null
|
32 |
*/
|
33 |
-
public static function Get( $
|
34 |
$mVal = null;
|
35 |
|
36 |
$oWP = Services::WpGeneral();
|
37 |
|
38 |
if ( $oWP->canUseTransients() ) {
|
39 |
-
$mVal = $oWP->getTransient( $
|
40 |
}
|
41 |
else {
|
42 |
-
$aData = $oWP->getOption( System::PREFIX.'trans_'.$
|
43 |
if ( !empty( $aData ) && is_array( $aData ) && isset( $aData[ 'data' ] )
|
44 |
&& isset( $aData[ 'expires_at' ] ) ) {
|
45 |
if ( $aData[ 'expires_at' ] === 0 || Services::Request()->ts() < $aData[ 'expires_at' ] ) {
|
@@ -48,34 +48,34 @@ class Transient {
|
|
48 |
}
|
49 |
}
|
50 |
|
51 |
-
return is_null( $mVal ) ? $
|
52 |
}
|
53 |
|
54 |
/**
|
55 |
-
* @param string $
|
56 |
-
* @param mixed $
|
57 |
-
* @param int $
|
58 |
-
* @param bool $
|
59 |
* @return bool
|
60 |
*/
|
61 |
-
public static function Set( $
|
62 |
-
if ( is_null( $
|
63 |
-
self::Delete( $
|
64 |
}
|
65 |
|
66 |
$oWP = Services::WpGeneral();
|
67 |
|
68 |
if ( $oWP->canUseTransients() ) {
|
69 |
-
return $oWP->setTransient( $
|
70 |
}
|
71 |
else {
|
72 |
return $oWP->updateOption(
|
73 |
-
System::PREFIX.'trans_'.$
|
74 |
[
|
75 |
-
'data' => $
|
76 |
-
'expires_at' => empty( $
|
77 |
],
|
78 |
-
$
|
79 |
);
|
80 |
}
|
81 |
}
|
13 |
class Transient {
|
14 |
|
15 |
/**
|
16 |
+
* @param string $key
|
17 |
+
* @param bool $ignoreWPMS
|
18 |
* @return bool
|
19 |
*/
|
20 |
+
public static function Delete( $key, $ignoreWPMS = true ) {
|
21 |
$oWP = Services::WpGeneral();
|
22 |
return $oWP->canUseTransients() ?
|
23 |
+
$oWP->deleteTransient( $key )
|
24 |
+
: Services::WpGeneral()->deleteOption( System::PREFIX.'trans_'.$key, $ignoreWPMS );
|
25 |
}
|
26 |
|
27 |
/**
|
28 |
+
* @param string $key
|
29 |
+
* @param null $default
|
30 |
+
* @param bool $ignoreWPMS
|
31 |
* @return mixed|null
|
32 |
*/
|
33 |
+
public static function Get( $key, $default = null, $ignoreWPMS = true ) {
|
34 |
$mVal = null;
|
35 |
|
36 |
$oWP = Services::WpGeneral();
|
37 |
|
38 |
if ( $oWP->canUseTransients() ) {
|
39 |
+
$mVal = $oWP->getTransient( $key );
|
40 |
}
|
41 |
else {
|
42 |
+
$aData = $oWP->getOption( System::PREFIX.'trans_'.$key, null, $ignoreWPMS );
|
43 |
if ( !empty( $aData ) && is_array( $aData ) && isset( $aData[ 'data' ] )
|
44 |
&& isset( $aData[ 'expires_at' ] ) ) {
|
45 |
if ( $aData[ 'expires_at' ] === 0 || Services::Request()->ts() < $aData[ 'expires_at' ] ) {
|
48 |
}
|
49 |
}
|
50 |
|
51 |
+
return is_null( $mVal ) ? $default : $mVal;
|
52 |
}
|
53 |
|
54 |
/**
|
55 |
+
* @param string $key
|
56 |
+
* @param mixed $data
|
57 |
+
* @param int $lifeTime
|
58 |
+
* @param bool $ignoreWPMS
|
59 |
* @return bool
|
60 |
*/
|
61 |
+
public static function Set( $key, $data, $lifeTime = 0, $ignoreWPMS = true ) {
|
62 |
+
if ( is_null( $data ) ) {
|
63 |
+
self::Delete( $key );
|
64 |
}
|
65 |
|
66 |
$oWP = Services::WpGeneral();
|
67 |
|
68 |
if ( $oWP->canUseTransients() ) {
|
69 |
+
return $oWP->setTransient( $key, $data, $lifeTime );
|
70 |
}
|
71 |
else {
|
72 |
return $oWP->updateOption(
|
73 |
+
System::PREFIX.'trans_'.$key,
|
74 |
[
|
75 |
+
'data' => $data,
|
76 |
+
'expires_at' => empty( $lifeTime ) ? 0 : Services::Request()->ts() + max( 0, $lifeTime ),
|
77 |
],
|
78 |
+
$ignoreWPMS
|
79 |
);
|
80 |
}
|
81 |
}
|
src/lib/vendor/fernleafsystems/wordpress-services/src/Utilities/Render.php
CHANGED
@@ -74,17 +74,17 @@ class Render {
|
|
74 |
extract( $this->getRenderVars() );
|
75 |
}
|
76 |
|
77 |
-
$
|
78 |
-
if ( Services::WpFs()->isFile( $
|
79 |
ob_start();
|
80 |
-
include( $
|
81 |
-
$
|
82 |
}
|
83 |
else {
|
84 |
-
$
|
85 |
}
|
86 |
|
87 |
-
return $
|
88 |
}
|
89 |
|
90 |
/**
|
74 |
extract( $this->getRenderVars() );
|
75 |
}
|
76 |
|
77 |
+
$template = path_join( $this->getTemplateRoot(), $this->getTemplate() );
|
78 |
+
if ( Services::WpFs()->isFile( $template ) ) {
|
79 |
ob_start();
|
80 |
+
include( $template );
|
81 |
+
$contents = ob_get_clean();
|
82 |
}
|
83 |
else {
|
84 |
+
$contents = 'Error: Template file not found: '.$template;
|
85 |
}
|
86 |
|
87 |
+
return $contents;
|
88 |
}
|
89 |
|
90 |
/**
|
src/processors/admin_access_restriction.php
DELETED
@@ -1,450 +0,0 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
use FernleafSystems\Wordpress\Plugin\Shield\Modules;
|
4 |
-
use FernleafSystems\Wordpress\Plugin\Shield\Modules\SecurityAdmin;
|
5 |
-
use FernleafSystems\Wordpress\Services\Services;
|
6 |
-
|
7 |
-
/**
|
8 |
-
* @deprecated 10.1
|
9 |
-
*/
|
10 |
-
class ICWP_WPSF_Processor_AdminAccessRestriction extends Modules\BaseShield\ShieldProcessor {
|
11 |
-
|
12 |
-
/**
|
13 |
-
* @var string
|
14 |
-
*/
|
15 |
-
protected $sOptionRegexPattern;
|
16 |
-
|
17 |
-
public function run() {
|
18 |
-
add_filter( $this->getCon()->prefix( 'is_plugin_admin' ), [ $this, 'adjustUserAdminPermissions' ] );
|
19 |
-
|
20 |
-
/** @var SecurityAdmin\Options $opts */
|
21 |
-
$opts = $this->getOptions();
|
22 |
-
if ( $opts->isEnabledWhitelabel() ) {
|
23 |
-
/** @var SecurityAdmin\ModCon $mod */
|
24 |
-
$mod = $this->getMod();
|
25 |
-
$mod->getWhiteLabelController()->execute();
|
26 |
-
}
|
27 |
-
}
|
28 |
-
|
29 |
-
/**
|
30 |
-
* @param bool $bHasPermission
|
31 |
-
* @return bool
|
32 |
-
*/
|
33 |
-
public function adjustUserAdminPermissions( $bHasPermission = true ) {
|
34 |
-
/** @var SecurityAdmin\ModCon $mod */
|
35 |
-
$mod = $this->getMod();
|
36 |
-
return $bHasPermission && ( $mod->isRegisteredSecAdminUser() || $mod->isSecAdminSessionValid()
|
37 |
-
|| $mod->testSecAccessKeyRequest() );
|
38 |
-
}
|
39 |
-
|
40 |
-
public function onWpInit() {
|
41 |
-
if ( !$this->getCon()->isPluginAdmin() ) {
|
42 |
-
/** @var SecurityAdmin\ModCon $mod */
|
43 |
-
$mod = $this->getMod();
|
44 |
-
/** @var SecurityAdmin\Options $opts */
|
45 |
-
$opts = $this->getOptions();
|
46 |
-
|
47 |
-
if ( !$mod->isUpgrading() && !Services::WpGeneral()->isLoginRequest() ) {
|
48 |
-
add_filter( 'pre_update_option', [ $this, 'blockOptionsSaves' ], 1, 3 );
|
49 |
-
}
|
50 |
-
|
51 |
-
if ( $opts->isSecAdminRestrictUsersEnabled() ) {
|
52 |
-
add_filter( 'editable_roles', [ $this, 'restrictEditableRoles' ], 100, 1 );
|
53 |
-
add_filter( 'user_has_cap', [ $this, 'restrictAdminUserChanges' ], 100, 3 );
|
54 |
-
add_action( 'delete_user', [ $this, 'restrictAdminUserDelete' ], 100, 1 );
|
55 |
-
add_action( 'add_user_role', [ $this, 'restrictAddUserRole' ], 100, 2 );
|
56 |
-
add_action( 'remove_user_role', [ $this, 'restrictRemoveUserRole' ], 100, 2 );
|
57 |
-
add_action( 'set_user_role', [ $this, 'restrictSetUserRole' ], 100, 3 );
|
58 |
-
}
|
59 |
-
|
60 |
-
$aPluginRestrictions = $opts->getAdminAccessArea_Plugins();
|
61 |
-
if ( !empty( $aPluginRestrictions ) ) {
|
62 |
-
add_filter( 'user_has_cap', [ $this, 'disablePluginManipulation' ], 0, 3 );
|
63 |
-
}
|
64 |
-
|
65 |
-
$aThemeRestrictions = $opts->getAdminAccessArea_Themes();
|
66 |
-
if ( !empty( $aThemeRestrictions ) ) {
|
67 |
-
add_filter( 'user_has_cap', [ $this, 'disableThemeManipulation' ], 0, 3 );
|
68 |
-
}
|
69 |
-
|
70 |
-
$aPostRestrictions = $opts->getAdminAccessArea_Posts();
|
71 |
-
if ( !empty( $aPostRestrictions ) ) {
|
72 |
-
add_filter( 'user_has_cap', [ $this, 'disablePostsManipulation' ], 0, 3 );
|
73 |
-
}
|
74 |
-
|
75 |
-
if ( !$this->getCon()->isThisPluginModuleRequest() ) {
|
76 |
-
add_action( 'admin_footer', [ $this, 'printAdminAccessAjaxForm' ] );
|
77 |
-
}
|
78 |
-
}
|
79 |
-
}
|
80 |
-
|
81 |
-
/**
|
82 |
-
* Override the original collection to then add plugin statistics to the mix
|
83 |
-
* @param $aData
|
84 |
-
* @return array
|
85 |
-
*/
|
86 |
-
public function tracking_DataCollect( $aData ) {
|
87 |
-
return $aData;
|
88 |
-
}
|
89 |
-
|
90 |
-
/**
|
91 |
-
* @param int $nUserId
|
92 |
-
* @param string $sRole
|
93 |
-
*/
|
94 |
-
public function restrictAddUserRole( $nUserId, $sRole ) {
|
95 |
-
$oWpUsers = Services::WpUsers();
|
96 |
-
|
97 |
-
if ( $oWpUsers->getCurrentWpUserId() !== $nUserId && strtolower( $sRole ) === 'administrator' ) {
|
98 |
-
$oModUser = $oWpUsers->getUserById( $nUserId );
|
99 |
-
remove_action( 'remove_user_role', [ $this, 'restrictRemoveUserRole' ], 100 );
|
100 |
-
$oModUser->remove_role( 'administrator' );
|
101 |
-
add_action( 'remove_user_role', [ $this, 'restrictRemoveUserRole' ], 100, 2 );
|
102 |
-
}
|
103 |
-
}
|
104 |
-
|
105 |
-
/**
|
106 |
-
* @param int $nUserId
|
107 |
-
* @param string $sRole
|
108 |
-
* @param array $aOldRoles
|
109 |
-
*/
|
110 |
-
public function restrictSetUserRole( $nUserId, $sRole, $aOldRoles = [] ) {
|
111 |
-
$oWpUsers = Services::WpUsers();
|
112 |
-
|
113 |
-
$sRole = strtolower( $sRole );
|
114 |
-
if ( !is_array( $aOldRoles ) ) {
|
115 |
-
$aOldRoles = [];
|
116 |
-
}
|
117 |
-
|
118 |
-
if ( $oWpUsers->getCurrentWpUserId() !== $nUserId ) {
|
119 |
-
$bNewRoleIsAdmin = $sRole == 'administrator';
|
120 |
-
|
121 |
-
// 1. Setting administrator role where it doesn't previously exist
|
122 |
-
if ( $bNewRoleIsAdmin && !in_array( 'administrator', $aOldRoles ) ) {
|
123 |
-
$bRevert = true;
|
124 |
-
}
|
125 |
-
// 2. Setting non-administrator role when previous roles included administrator
|
126 |
-
elseif ( !$bNewRoleIsAdmin && in_array( 'administrator', $aOldRoles ) ) {
|
127 |
-
$bRevert = true;
|
128 |
-
}
|
129 |
-
else {
|
130 |
-
$bRevert = false;
|
131 |
-
}
|
132 |
-
|
133 |
-
if ( $bRevert ) {
|
134 |
-
$oModUser = $oWpUsers->getUserById( $nUserId );
|
135 |
-
remove_action( 'add_user_role', [ $this, 'restrictAddUserRole' ], 100 );
|
136 |
-
remove_action( 'remove_user_role', [ $this, 'restrictRemoveUserRole' ], 100 );
|
137 |
-
$oModUser->remove_role( $sRole );
|
138 |
-
foreach ( $aOldRoles as $sPreExistingRoles ) {
|
139 |
-
$oModUser->add_role( $sPreExistingRoles );
|
140 |
-
}
|
141 |
-
add_action( 'add_user_role', [ $this, 'restrictAddUserRole' ], 100, 2 );
|
142 |
-
add_action( 'remove_user_role', [ $this, 'restrictRemoveUserRole' ], 100, 2 );
|
143 |
-
}
|
144 |
-
}
|
145 |
-
}
|
146 |
-
|
147 |
-
/**
|
148 |
-
* @param int $nUserId
|
149 |
-
* @param string $sRole
|
150 |
-
*/
|
151 |
-
public function restrictRemoveUserRole( $nUserId, $sRole ) {
|
152 |
-
$oWpUsers = Services::WpUsers();
|
153 |
-
|
154 |
-
if ( $oWpUsers->getCurrentWpUserId() !== $nUserId && strtolower( $sRole ) === 'administrator' ) {
|
155 |
-
$oModUser = $oWpUsers->getUserById( $nUserId );
|
156 |
-
remove_action( 'add_user_role', [ $this, 'restrictAddUserRole' ], 100 );
|
157 |
-
$oModUser->add_role( 'administrator' );
|
158 |
-
add_action( 'add_user_role', [ $this, 'restrictAddUserRole' ], 100, 2 );
|
159 |
-
}
|
160 |
-
}
|
161 |
-
|
162 |
-
/**
|
163 |
-
* @param int $nId
|
164 |
-
*/
|
165 |
-
public function restrictAdminUserDelete( $nId ) {
|
166 |
-
$oWpUsers = Services::WpUsers();
|
167 |
-
$oUserToDelete = $oWpUsers->getUserById( $nId );
|
168 |
-
if ( $oUserToDelete && $oWpUsers->isUserAdmin( $oUserToDelete ) ) {
|
169 |
-
Services::WpGeneral()
|
170 |
-
->wpDie( __( 'Sorry, deleting administrators is currently restricted to your Security Admin', 'wp-simple-firewall' ) );
|
171 |
-
}
|
172 |
-
}
|
173 |
-
|
174 |
-
/**
|
175 |
-
* @param array[] $aAllRoles
|
176 |
-
* @return array[]
|
177 |
-
*/
|
178 |
-
public function restrictEditableRoles( $aAllRoles ) {
|
179 |
-
if ( isset( $aAllRoles[ 'administrator' ] ) ) {
|
180 |
-
unset( $aAllRoles[ 'administrator' ] );
|
181 |
-
}
|
182 |
-
return $aAllRoles;
|
183 |
-
}
|
184 |
-
|
185 |
-
/**
|
186 |
-
* This hooked function captures the attempts to modify the user role using the standard
|
187 |
-
* WordPress profile edit pages. It doesn't sufficiently capture the AJAX request to
|
188 |
-
* modify user roles. (see user role hooks)
|
189 |
-
* @param array $aAllCaps
|
190 |
-
* @param $cap
|
191 |
-
* @param array $aArgs
|
192 |
-
* @return array
|
193 |
-
*/
|
194 |
-
public function restrictAdminUserChanges( $aAllCaps, $cap, $aArgs ) {
|
195 |
-
/** @var string $sUserCap */
|
196 |
-
$sUserCap = $aArgs[ 0 ];
|
197 |
-
|
198 |
-
$aReleventCaps = [ 'edit_users', 'create_users' ];
|
199 |
-
|
200 |
-
// If we're registered with Admin Access we don't modify anything
|
201 |
-
if ( in_array( $sUserCap, $aReleventCaps ) ) {
|
202 |
-
$bBlockCapability = false;
|
203 |
-
|
204 |
-
$oReq = Services::Request();
|
205 |
-
$oWpUsers = Services::WpUsers();
|
206 |
-
|
207 |
-
// Find the WP_User for the POST
|
208 |
-
$oPostUser = false;
|
209 |
-
$sPostUserlogin = $oReq->post( 'user_login' );
|
210 |
-
if ( empty( $sPostUserlogin ) ) {
|
211 |
-
$nPostUserId = $oReq->post( 'user_id' );
|
212 |
-
if ( !empty( $nPostUserId ) ) {
|
213 |
-
$oPostUser = $oWpUsers->getUserById( $nPostUserId );
|
214 |
-
}
|
215 |
-
}
|
216 |
-
else {
|
217 |
-
$oPostUser = $oWpUsers->getUserByUsername( $sPostUserlogin );
|
218 |
-
}
|
219 |
-
|
220 |
-
$sRequestRole = strtolower( $oReq->post( 'role', '' ) );
|
221 |
-
|
222 |
-
if ( $oPostUser instanceof WP_User ) {
|
223 |
-
// editing an existing user other than yourself?
|
224 |
-
if ( $oPostUser->user_login != $oWpUsers->getCurrentWpUsername() ) {
|
225 |
-
|
226 |
-
if ( $oWpUsers->isUserAdmin( $oPostUser ) || ( $sRequestRole == 'administrator' ) ) {
|
227 |
-
$bBlockCapability = true;
|
228 |
-
}
|
229 |
-
}
|
230 |
-
}
|
231 |
-
elseif ( $sRequestRole == 'administrator' ) { //creating a new admin user?
|
232 |
-
$bBlockCapability = true;
|
233 |
-
}
|
234 |
-
|
235 |
-
if ( $bBlockCapability ) {
|
236 |
-
$aAllCaps[ $sUserCap ] = false;
|
237 |
-
}
|
238 |
-
}
|
239 |
-
|
240 |
-
return $aAllCaps;
|
241 |
-
}
|
242 |
-
|
243 |
-
protected function getUserPagesToRestrict() :array {
|
244 |
-
return [
|
245 |
-
/* 'user-new.php', */
|
246 |
-
'user-edit.php',
|
247 |
-
'users.php',
|
248 |
-
];
|
249 |
-
}
|
250 |
-
|
251 |
-
/**
|
252 |
-
* Need to always re-test isPluginAdmin() because there's a dynamic filter in there to
|
253 |
-
* permit saving by the plugin itself.
|
254 |
-
*
|
255 |
-
* Right before a plugin option is due to update it will check that we have permissions to do so
|
256 |
-
* and if not, will * revert the option to save to the previous one.
|
257 |
-
* @param mixed $mNewOptionValue
|
258 |
-
* @param string $key
|
259 |
-
* @param mixed $mOldValue
|
260 |
-
* @return mixed
|
261 |
-
*/
|
262 |
-
public function blockOptionsSaves( $mNewOptionValue, $key, $mOldValue ) {
|
263 |
-
|
264 |
-
if ( !$this->getCon()->isPluginAdmin() && is_string( $key )
|
265 |
-
&& ( $this->isOptionForThisPlugin( $key ) || $this->isOptionRestricted( $key ) ) ) {
|
266 |
-
$mNewOptionValue = $mOldValue;
|
267 |
-
}
|
268 |
-
|
269 |
-
return $mNewOptionValue;
|
270 |
-
}
|
271 |
-
|
272 |
-
private function isOptionForThisPlugin( string $key ) :bool {
|
273 |
-
return preg_match( $this->getOptionRegexPattern(), $key ) > 0;
|
274 |
-
}
|
275 |
-
|
276 |
-
private function isOptionRestricted( string $key ) :bool {
|
277 |
-
/** @var SecurityAdmin\Options $opts */
|
278 |
-
$opts = $this->getOptions();
|
279 |
-
return $opts->getAdminAccessArea_Options()
|
280 |
-
&& in_array( $key, $opts->getOptionsToRestrict() );
|
281 |
-
}
|
282 |
-
|
283 |
-
/**
|
284 |
-
* @param array $aAllCaps
|
285 |
-
* @param $cap
|
286 |
-
* @param array $aArgs
|
287 |
-
* @return array
|
288 |
-
*/
|
289 |
-
public function disablePluginManipulation( $aAllCaps, $cap, $aArgs ) {
|
290 |
-
/** @var SecurityAdmin\Options $opts */
|
291 |
-
$opts = $this->getOptions();
|
292 |
-
$req = Services::Request();
|
293 |
-
|
294 |
-
/** @var string $sRequestedCapability */
|
295 |
-
$sRequestedCapability = $aArgs[ 0 ];
|
296 |
-
|
297 |
-
// special case for plugin info thickbox for changelog
|
298 |
-
$bIsChangelog = defined( 'IFRAME_REQUEST' )
|
299 |
-
&& ( $sRequestedCapability === 'install_plugins' )
|
300 |
-
&& ( $req->query( 'section' ) == 'changelog' )
|
301 |
-
&& $req->query( 'plugin' );
|
302 |
-
if ( $bIsChangelog ) {
|
303 |
-
return $aAllCaps;
|
304 |
-
}
|
305 |
-
|
306 |
-
$aEditCapabilities = [ 'activate_plugins', 'delete_plugins', 'install_plugins', 'update_plugins' ];
|
307 |
-
|
308 |
-
if ( in_array( $sRequestedCapability, $aEditCapabilities ) ) {
|
309 |
-
$aAreaRestrictions = $opts->getAdminAccessArea_Plugins();
|
310 |
-
if ( in_array( $sRequestedCapability, $aAreaRestrictions ) ) {
|
311 |
-
$aAllCaps[ $sRequestedCapability ] = false;
|
312 |
-
}
|
313 |
-
}
|
314 |
-
|
315 |
-
return $aAllCaps;
|
316 |
-
}
|
317 |
-
|
318 |
-
/**
|
319 |
-
* @param array $aAllCaps
|
320 |
-
* @param $cap
|
321 |
-
* @param array $aArgs
|
322 |
-
* @return array
|
323 |
-
*/
|
324 |
-
public function disableThemeManipulation( $aAllCaps, $cap, $aArgs ) {
|
325 |
-
// If we're registered with Admin Access we don't modify anything
|
326 |
-
if ( $this->getCon()->isPluginAdmin() ) {
|
327 |
-
return $aAllCaps;
|
328 |
-
}
|
329 |
-
|
330 |
-
/** @var SecurityAdmin\Options $opts */
|
331 |
-
$opts = $this->getOptions();
|
332 |
-
|
333 |
-
/** @var string $sRequestedCapability */
|
334 |
-
$sRequestedCapability = $aArgs[ 0 ];
|
335 |
-
$aEditCapabilities = [
|
336 |
-
'switch_themes',
|
337 |
-
'edit_theme_options',
|
338 |
-
'install_themes',
|
339 |
-
'update_themes',
|
340 |
-
'delete_themes'
|
341 |
-
];
|
342 |
-
|
343 |
-
if ( in_array( $sRequestedCapability, $aEditCapabilities ) ) {
|
344 |
-
$aAreaRestrictions = $opts->getAdminAccessArea_Themes();
|
345 |
-
if ( in_array( $sRequestedCapability, $aAreaRestrictions ) ) {
|
346 |
-
$aAllCaps[ $sRequestedCapability ] = false;
|
347 |
-
}
|
348 |
-
}
|
349 |
-
|
350 |
-
return $aAllCaps;
|
351 |
-
}
|
352 |
-
|
353 |
-
/**
|
354 |
-
* @param array $aAllCaps
|
355 |
-
* @param $cap
|
356 |
-
* @param array $args
|
357 |
-
* @return array
|
358 |
-
*/
|
359 |
-
public function disablePostsManipulation( $aAllCaps, $cap, $args ) {
|
360 |
-
if ( $this->getCon()->isPluginAdmin() ) {
|
361 |
-
return $aAllCaps;
|
362 |
-
}
|
363 |
-
|
364 |
-
/** @var SecurityAdmin\Options $opts */
|
365 |
-
$opts = $this->getOptions();
|
366 |
-
|
367 |
-
/** @var string $sRequestedCapability */
|
368 |
-
$sRequestedCapability = $args[ 0 ];
|
369 |
-
$aEditCapabilities = [
|
370 |
-
'edit_post',
|
371 |
-
'publish_post',
|
372 |
-
'delete_post',
|
373 |
-
'edit_posts',
|
374 |
-
'publish_posts',
|
375 |
-
'delete_posts',
|
376 |
-
'edit_page',
|
377 |
-
'publish_page',
|
378 |
-
'delete_page',
|
379 |
-
'edit_pages',
|
380 |
-
'publish_pages',
|
381 |
-
'delete_pages'
|
382 |
-
];
|
383 |
-
if ( in_array( $sRequestedCapability, $aEditCapabilities ) ) {
|
384 |
-
$sRequestedCapabilityTrimmed = str_replace( [
|
385 |
-
'_posts',
|
386 |
-
'_pages',
|
387 |
-
'_post',
|
388 |
-
'_page'
|
389 |
-
], '', $sRequestedCapability ); //Order of items in this array is important!
|
390 |
-
$aAreaRestrictions = $opts->getAdminAccessArea_Posts();
|
391 |
-
if ( in_array( $sRequestedCapabilityTrimmed, $aAreaRestrictions ) ) {
|
392 |
-
$aAllCaps[ $sRequestedCapability ] = false;
|
393 |
-
}
|
394 |
-
}
|
395 |
-
return $aAllCaps;
|
396 |
-
}
|
397 |
-
|
398 |
-
/**
|
399 |
-
* @return string
|
400 |
-
*/
|
401 |
-
protected function getOptionRegexPattern() {
|
402 |
-
if ( !isset( $this->sOptionRegexPattern ) ) {
|
403 |
-
$this->sOptionRegexPattern = sprintf( '/^%s.*_options$/',
|
404 |
-
$this->getCon()->getOptionStoragePrefix()
|
405 |
-
);
|
406 |
-
}
|
407 |
-
return $this->sOptionRegexPattern;
|
408 |
-
}
|
409 |
-
|
410 |
-
public function printAdminAccessAjaxForm() {
|
411 |
-
/** @var SecurityAdmin\ModCon $mod */
|
412 |
-
$mod = $this->getMod();
|
413 |
-
/** @var SecurityAdmin\Options $opts */
|
414 |
-
$opts = $this->getOptions();
|
415 |
-
|
416 |
-
$aRenderData = [
|
417 |
-
'flags' => [
|
418 |
-
'restrict_options' => $opts->getAdminAccessArea_Options()
|
419 |
-
],
|
420 |
-
'strings' => [
|
421 |
-
'editing_restricted' => __( 'Editing this option is currently restricted.', 'wp-simple-firewall' ),
|
422 |
-
'unlock_link' => $this->getUnlockLinkHtml(),
|
423 |
-
],
|
424 |
-
'js_snippets' => [
|
425 |
-
'options_to_restrict' => "'".implode( "','", $opts->getOptionsToRestrict() )."'",
|
426 |
-
],
|
427 |
-
'ajax' => [
|
428 |
-
'sec_admin_login_box' => $mod->getAjaxActionData( 'sec_admin_login_box', true )
|
429 |
-
]
|
430 |
-
];
|
431 |
-
add_thickbox();
|
432 |
-
echo $mod->renderTemplate( 'snippets/admin_access_login_box.php', $aRenderData );
|
433 |
-
}
|
434 |
-
|
435 |
-
/**
|
436 |
-
* @param string $sLinkText
|
437 |
-
* @return string
|
438 |
-
*/
|
439 |
-
protected function getUnlockLinkHtml( $sLinkText = '' ) {
|
440 |
-
if ( empty( $sLinkText ) ) {
|
441 |
-
$sLinkText = __( 'Unlock', 'wp-simple-firewall' );
|
442 |
-
}
|
443 |
-
return sprintf(
|
444 |
-
'<a href="%1$s" title="%2$s" class="thickbox">%3$s</a>',
|
445 |
-
'#TB_inline?width=400&height=180&inlineId=WpsfAdminAccessLogin',
|
446 |
-
__( 'Security Admin Login', 'wp-simple-firewall' ),
|
447 |
-
$sLinkText
|
448 |
-
);
|
449 |
-
}
|
450 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
src/processors/audit_trail.php
DELETED
@@ -1,74 +0,0 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
use FernleafSystems\Wordpress\Plugin\Shield\Modules;
|
4 |
-
use FernleafSystems\Wordpress\Plugin\Shield\Modules\AuditTrail\Auditors;
|
5 |
-
use FernleafSystems\Wordpress\Plugin\Shield\Modules\AuditTrail\Lib\AuditWriter;
|
6 |
-
use FernleafSystems\Wordpress\Plugin\Shield\Modules\AuditTrail\ModCon;
|
7 |
-
|
8 |
-
/**
|
9 |
-
* @deprecated 10.1
|
10 |
-
*/
|
11 |
-
class ICWP_WPSF_Processor_AuditTrail extends Modules\BaseShield\ShieldProcessor {
|
12 |
-
|
13 |
-
/**
|
14 |
-
* @var AuditWriter
|
15 |
-
*/
|
16 |
-
private $oAuditor;
|
17 |
-
|
18 |
-
public function run() {
|
19 |
-
$this->initAuditors();
|
20 |
-
$this->getSubProAuditor()->execute();
|
21 |
-
}
|
22 |
-
|
23 |
-
/**
|
24 |
-
* @return AuditWriter
|
25 |
-
*/
|
26 |
-
private function loadAuditorWriter() {
|
27 |
-
if ( !isset( $this->oAuditor ) ) {
|
28 |
-
/** @var ModCon $mod */
|
29 |
-
$mod = $this->getMod();
|
30 |
-
$this->oAuditor = ( new AuditWriter( $this->getCon() ) )
|
31 |
-
->setDbHandler( $mod->getDbHandler_AuditTrail() );
|
32 |
-
}
|
33 |
-
return $this->oAuditor;
|
34 |
-
}
|
35 |
-
|
36 |
-
private function initAuditors() {
|
37 |
-
$this->loadAuditorWriter()->setIfCommit( true );
|
38 |
-
|
39 |
-
( new Auditors\Users() )
|
40 |
-
->setMod( $this->getMod() )
|
41 |
-
->run();
|
42 |
-
( new Auditors\Plugins() )
|
43 |
-
->setMod( $this->getMod() )
|
44 |
-
->run();
|
45 |
-
( new Auditors\Themes() )
|
46 |
-
->setMod( $this->getMod() )
|
47 |
-
->run();
|
48 |
-
( new Auditors\Wordpress() )
|
49 |
-
->setMod( $this->getMod() )
|
50 |
-
->run();
|
51 |
-
( new Auditors\Posts() )
|
52 |
-
->setMod( $this->getMod() )
|
53 |
-
->run();
|
54 |
-
( new Auditors\Emails() )
|
55 |
-
->setMod( $this->getMod() )
|
56 |
-
->run();
|
57 |
-
( new Auditors\Upgrades() )
|
58 |
-
->setMod( $this->getMod() )
|
59 |
-
->run();
|
60 |
-
}
|
61 |
-
|
62 |
-
/**
|
63 |
-
* @return ICWP_WPSF_Processor_AuditTrail_Auditor|mixed
|
64 |
-
*/
|
65 |
-
public function getSubProAuditor() {
|
66 |
-
return $this->getSubPro( 'auditor' );
|
67 |
-
}
|
68 |
-
|
69 |
-
protected function getSubProMap() :array {
|
70 |
-
return [
|
71 |
-
'auditor' => 'ICWP_WPSF_Processor_AuditTrail_Auditor',
|
72 |
-
];
|
73 |
-
}
|
74 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
src/processors/autoupdates.php
DELETED
@@ -1,456 +0,0 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
use FernleafSystems\Wordpress\Plugin\Shield\Modules;
|
4 |
-
use FernleafSystems\Wordpress\Services\Services;
|
5 |
-
|
6 |
-
/**
|
7 |
-
* @deprecated 10.1
|
8 |
-
*/
|
9 |
-
class ICWP_WPSF_Processor_Autoupdates extends Modules\BaseShield\ShieldProcessor {
|
10 |
-
|
11 |
-
/**
|
12 |
-
* @var array
|
13 |
-
*/
|
14 |
-
private $aAssetsVersions = [];
|
15 |
-
|
16 |
-
/**
|
17 |
-
* The allow_* core filters are run first in a "should_update" query. Then comes the "auto_update_core"
|
18 |
-
* filter. What this filter decides will ultimately determine the fate of any core upgrade.
|
19 |
-
*/
|
20 |
-
public function run() {
|
21 |
-
/** @var Modules\Autoupdates\Options $oOpts */
|
22 |
-
$oOpts = $this->getOptions();
|
23 |
-
|
24 |
-
$nPriority = $this->getHookPriority();
|
25 |
-
if ( Services::WpGeneral()->isClassicPress() ) {
|
26 |
-
add_filter( 'allow_patch_auto_core_updates', [ $this, 'autoupdate_core_minor' ], $nPriority );
|
27 |
-
add_filter( 'allow_minor_auto_core_updates', [ $this, 'autoupdate_core_major' ], $nPriority );
|
28 |
-
}
|
29 |
-
else {
|
30 |
-
add_filter( 'allow_minor_auto_core_updates', [ $this, 'autoupdate_core_minor' ], $nPriority );
|
31 |
-
add_filter( 'allow_major_auto_core_updates', [ $this, 'autoupdate_core_major' ], $nPriority );
|
32 |
-
}
|
33 |
-
|
34 |
-
add_filter( 'auto_update_plugin', [ $this, 'autoupdate_plugins' ], $nPriority, 2 );
|
35 |
-
add_filter( 'auto_update_theme', [ $this, 'autoupdate_themes' ], $nPriority, 2 );
|
36 |
-
add_filter( 'auto_update_core', [ $this, 'autoupdate_core' ], $nPriority, 2 );
|
37 |
-
|
38 |
-
if ( !$oOpts->isDisableAllAutoUpdates() ) {
|
39 |
-
//more parameter options here for later
|
40 |
-
add_filter( 'auto_core_update_send_email', [ $this, 'autoupdate_send_email' ], $nPriority, 1 );
|
41 |
-
add_filter( 'auto_core_update_email', [ $this, 'autoupdate_email_override' ], $nPriority, 1 );
|
42 |
-
add_filter( 'auto_plugin_theme_update_email', [ $this, 'autoupdate_email_override' ], $nPriority, 1 );
|
43 |
-
|
44 |
-
add_action( 'set_site_transient_update_core', [ $this, 'trackUpdateTimesCore' ] );
|
45 |
-
add_action( 'set_site_transient_update_plugins', [ $this, 'trackUpdateTimesPlugins' ] );
|
46 |
-
add_action( 'set_site_transient_update_themes', [ $this, 'trackUpdateTimesThemes' ] );
|
47 |
-
|
48 |
-
if ( $oOpts->isSendAutoupdatesNotificationEmail()
|
49 |
-
&& !Services::WpGeneral()->getWordpressIsAtLeastVersion( '5.5' ) ) {
|
50 |
-
$this->trackAssetsVersions();
|
51 |
-
add_action( 'automatic_updates_complete', [ $this, 'sendNotificationEmail' ] );
|
52 |
-
}
|
53 |
-
}
|
54 |
-
}
|
55 |
-
|
56 |
-
public function onWpLoaded() {
|
57 |
-
/** @var Modules\Autoupdates\Options $oOpts */
|
58 |
-
$oOpts = $this->getOptions();
|
59 |
-
if ( $oOpts->isDisableAllAutoUpdates() ) {
|
60 |
-
$this->disableAllAutoUpdates();
|
61 |
-
}
|
62 |
-
}
|
63 |
-
|
64 |
-
private function disableAllAutoUpdates() {
|
65 |
-
remove_all_filters( 'automatic_updater_disabled' );
|
66 |
-
add_filter( 'automatic_updater_disabled', '__return_true', PHP_INT_MAX );
|
67 |
-
if ( !defined( 'WP_AUTO_UPDATE_CORE' ) ) {
|
68 |
-
define( 'WP_AUTO_UPDATE_CORE', false );
|
69 |
-
}
|
70 |
-
}
|
71 |
-
|
72 |
-
private function trackAssetsVersions() {
|
73 |
-
$aAssVers = $this->getTrackedAssetsVersions();
|
74 |
-
|
75 |
-
$oWpPlugins = Services::WpPlugins();
|
76 |
-
foreach ( array_keys( $oWpPlugins->getUpdates() ) as $sFile ) {
|
77 |
-
$aAssVers[ 'plugins' ][ $sFile ] = $oWpPlugins->getPluginAsVo( $sFile )->Version;
|
78 |
-
}
|
79 |
-
$oWpThemes = Services::WpThemes();
|
80 |
-
foreach ( array_keys( $oWpThemes->getUpdates() ) as $sFile ) {
|
81 |
-
$aAssVers[ 'themes' ][ $sFile ] = $oWpThemes->getTheme( $sFile )->get( 'Version' );
|
82 |
-
}
|
83 |
-
$this->aAssetsVersions = $aAssVers;
|
84 |
-
}
|
85 |
-
|
86 |
-
/**
|
87 |
-
* @return array
|
88 |
-
*/
|
89 |
-
protected function getTrackedAssetsVersions() {
|
90 |
-
if ( empty( $this->aAssetsVersions ) || !is_array( $this->aAssetsVersions ) ) {
|
91 |
-
$this->aAssetsVersions = [
|
92 |
-
'plugins' => [],
|
93 |
-
'themes' => [],
|
94 |
-
];
|
95 |
-
}
|
96 |
-
return $this->aAssetsVersions;
|
97 |
-
}
|
98 |
-
|
99 |
-
/**
|
100 |
-
* @param stdClass $oUpdates
|
101 |
-
*/
|
102 |
-
public function trackUpdateTimesCore( $oUpdates ) {
|
103 |
-
|
104 |
-
if ( !empty( $oUpdates ) && isset( $oUpdates->updates ) && is_array( $oUpdates->updates ) ) {
|
105 |
-
/** @var Modules\Autoupdates\Options $oOpts */
|
106 |
-
$oOpts = $this->getOptions();
|
107 |
-
|
108 |
-
$aTk = $oOpts->getDelayTracking();
|
109 |
-
$aItemTk = isset( $aTk[ 'core' ][ 'wp' ] ) ? $aTk[ 'core' ][ 'wp' ] : [];
|
110 |
-
foreach ( $oUpdates->updates as $oUpdate ) {
|
111 |
-
if ( 'autoupdate' == $oUpdate->response ) {
|
112 |
-
$sVersion = $oUpdate->current;
|
113 |
-
if ( !isset( $aItemTk[ $sVersion ] ) ) {
|
114 |
-
$aItemTk[ $sVersion ] = Services::Request()->ts();
|
115 |
-
}
|
116 |
-
}
|
117 |
-
}
|
118 |
-
$aTk[ 'core' ][ 'wp' ] = array_slice( $aItemTk, -5 );
|
119 |
-
$oOpts->setDelayTracking( $aTk );
|
120 |
-
}
|
121 |
-
}
|
122 |
-
|
123 |
-
/**
|
124 |
-
* @param stdClass $oUpdates
|
125 |
-
*/
|
126 |
-
public function trackUpdateTimesPlugins( $oUpdates ) {
|
127 |
-
$this->trackUpdateTimeCommon( $oUpdates, 'plugins' );
|
128 |
-
}
|
129 |
-
|
130 |
-
/**
|
131 |
-
* @param stdClass $oUpdates
|
132 |
-
*/
|
133 |
-
public function trackUpdateTimesThemes( $oUpdates ) {
|
134 |
-
$this->trackUpdateTimeCommon( $oUpdates, 'themes' );
|
135 |
-
}
|
136 |
-
|
137 |
-
/**
|
138 |
-
* @param stdClass $oUpdates
|
139 |
-
* @param string $sContext - plugins/themes
|
140 |
-
*/
|
141 |
-
protected function trackUpdateTimeCommon( $oUpdates, $sContext ) {
|
142 |
-
/** @var Modules\Autoupdates\Options $oOpts */
|
143 |
-
$oOpts = $this->getOptions();
|
144 |
-
|
145 |
-
if ( !empty( $oUpdates ) && isset( $oUpdates->response ) && is_array( $oUpdates->response ) ) {
|
146 |
-
|
147 |
-
$aTk = $oOpts->getDelayTracking();
|
148 |
-
foreach ( $oUpdates->response as $sSlug => $oUpdate ) {
|
149 |
-
$aItemTk = isset( $aTk[ $sContext ][ $sSlug ] ) ? $aTk[ $sContext ][ $sSlug ] : [];
|
150 |
-
if ( is_array( $oUpdate ) ) {
|
151 |
-
$oUpdate = (object)$oUpdate;
|
152 |
-
}
|
153 |
-
|
154 |
-
$sNewVersion = isset( $oUpdate->new_version ) ? $oUpdate->new_version : '';
|
155 |
-
if ( !empty( $sNewVersion ) ) {
|
156 |
-
if ( !isset( $aItemTk[ $sNewVersion ] ) ) {
|
157 |
-
$aItemTk[ $sNewVersion ] = Services::Request()->ts();
|
158 |
-
}
|
159 |
-
$aTk[ $sContext ][ $sSlug ] = array_slice( $aItemTk, -3 );
|
160 |
-
}
|
161 |
-
}
|
162 |
-
$oOpts->setDelayTracking( $aTk );
|
163 |
-
}
|
164 |
-
}
|
165 |
-
|
166 |
-
/**
|
167 |
-
* This is a filter method designed to say whether a major core WordPress upgrade should be permitted,
|
168 |
-
* based on the plugin settings.
|
169 |
-
* @param bool $bUpdate
|
170 |
-
* @return bool
|
171 |
-
*/
|
172 |
-
public function autoupdate_core_major( $bUpdate ) {
|
173 |
-
/** @var Modules\Autoupdates\Options $oOpts */
|
174 |
-
$oOpts = $this->getOptions();
|
175 |
-
|
176 |
-
if ( $oOpts->isDisableAllAutoUpdates() || $oOpts->isAutoUpdateCoreNever() ) {
|
177 |
-
$bUpdate = false;
|
178 |
-
}
|
179 |
-
elseif ( !$oOpts->isDelayUpdates() ) { // delay handled elsewhere
|
180 |
-
$bUpdate = $oOpts->isAutoUpdateCoreMajor();
|
181 |
-
}
|
182 |
-
|
183 |
-
return $bUpdate;
|
184 |
-
}
|
185 |
-
|
186 |
-
/**
|
187 |
-
* This is a filter method designed to say whether a minor core WordPress upgrade should be permitted,
|
188 |
-
* based on the plugin settings.
|
189 |
-
* @param bool $bUpdate
|
190 |
-
* @return bool
|
191 |
-
*/
|
192 |
-
public function autoupdate_core_minor( $bUpdate ) {
|
193 |
-
/** @var Modules\Autoupdates\Options $oOpts */
|
194 |
-
$oOpts = $this->getOptions();
|
195 |
-
|
196 |
-
if ( $oOpts->isDisableAllAutoUpdates() || $oOpts->isAutoUpdateCoreNever() ) {
|
197 |
-
$bUpdate = false;
|
198 |
-
}
|
199 |
-
elseif ( !$oOpts->isDelayUpdates() ) {
|
200 |
-
$bUpdate = !$oOpts->isAutoUpdateCoreNever();
|
201 |
-
}
|
202 |
-
return $bUpdate;
|
203 |
-
}
|
204 |
-
|
205 |
-
/**
|
206 |
-
* @param bool $bDoAutoUpdate
|
207 |
-
* @param \stdClass $oCoreUpdate
|
208 |
-
* @return bool
|
209 |
-
*/
|
210 |
-
public function autoupdate_core( $bDoAutoUpdate, $oCoreUpdate ) {
|
211 |
-
/** @var Modules\Autoupdates\Options $oOpts */
|
212 |
-
$oOpts = $this->getOptions();
|
213 |
-
|
214 |
-
if ( $oOpts->isDisableAllAutoUpdates() ) {
|
215 |
-
$bDoAutoUpdate = false;
|
216 |
-
}
|
217 |
-
elseif ( $this->isDelayed( $oCoreUpdate, 'core' ) ) {
|
218 |
-
$bDoAutoUpdate = false;
|
219 |
-
}
|
220 |
-
|
221 |
-
return $bDoAutoUpdate;
|
222 |
-
}
|
223 |
-
|
224 |
-
/**
|
225 |
-
* @param bool $bDoAutoUpdate
|
226 |
-
* @param \stdClass|string $mItem
|
227 |
-
* @return bool
|
228 |
-
*/
|
229 |
-
public function autoupdate_plugins( $bDoAutoUpdate, $mItem ) {
|
230 |
-
/** @var Modules\Autoupdates\Options $oOpts */
|
231 |
-
$oOpts = $this->getOptions();
|
232 |
-
|
233 |
-
if ( $oOpts->isDisableAllAutoUpdates() ) {
|
234 |
-
$bDoAutoUpdate = false;
|
235 |
-
}
|
236 |
-
else {
|
237 |
-
$file = Services::WpGeneral()->getFileFromAutomaticUpdateItem( $mItem );
|
238 |
-
|
239 |
-
if ( $this->isDelayed( $file, 'plugins' ) ) {
|
240 |
-
$bDoAutoUpdate = false;
|
241 |
-
}
|
242 |
-
elseif ( $oOpts->isAutoupdateAllPlugins() ) {
|
243 |
-
$bDoAutoUpdate = true;
|
244 |
-
}
|
245 |
-
elseif ( $file === $this->getCon()->getPluginBaseFile() ) {
|
246 |
-
$sAuto = $oOpts->getSelfAutoUpdateOpt();
|
247 |
-
if ( $sAuto === 'immediate' ) {
|
248 |
-
$bDoAutoUpdate = true;
|
249 |
-
}
|
250 |
-
elseif ( $sAuto === 'disabled' ) {
|
251 |
-
$bDoAutoUpdate = false;
|
252 |
-
}
|
253 |
-
}
|
254 |
-
}
|
255 |
-
|
256 |
-
return $bDoAutoUpdate;
|
257 |
-
}
|
258 |
-
|
259 |
-
/**
|
260 |
-
* @param bool $bDoAutoUpdate
|
261 |
-
* @param stdClass|string $mItem
|
262 |
-
* @return bool
|
263 |
-
*/
|
264 |
-
public function autoupdate_themes( $bDoAutoUpdate, $mItem ) {
|
265 |
-
/** @var Modules\Autoupdates\Options $opts */
|
266 |
-
$opts = $this->getOptions();
|
267 |
-
|
268 |
-
if ( $opts->isDisableAllAutoUpdates() ) {
|
269 |
-
$bDoAutoUpdate = false;
|
270 |
-
}
|
271 |
-
else {
|
272 |
-
$file = Services::WpGeneral()->getFileFromAutomaticUpdateItem( $mItem, 'theme' );
|
273 |
-
|
274 |
-
if ( $this->isDelayed( $file, 'themes' ) ) {
|
275 |
-
$bDoAutoUpdate = false;
|
276 |
-
}
|
277 |
-
elseif ( $opts->isOpt( 'enable_autoupdate_themes', 'Y' ) ) {
|
278 |
-
$bDoAutoUpdate = true;
|
279 |
-
}
|
280 |
-
}
|
281 |
-
|
282 |
-
return $bDoAutoUpdate;
|
283 |
-
}
|
284 |
-
|
285 |
-
/**
|
286 |
-
* @param string|stdClass $sSlug
|
287 |
-
* @param string $sContext
|
288 |
-
* @return bool
|
289 |
-
*/
|
290 |
-
private function isDelayed( $sSlug, $sContext = 'plugins' ) {
|
291 |
-
/** @var Modules\Autoupdates\Options $oOpts */
|
292 |
-
$oOpts = $this->getOptions();
|
293 |
-
|
294 |
-
$bDelayed = false;
|
295 |
-
|
296 |
-
if ( $oOpts->isDelayUpdates() ) {
|
297 |
-
|
298 |
-
$aTk = $oOpts->getDelayTracking();
|
299 |
-
|
300 |
-
$sVersion = '';
|
301 |
-
if ( $sContext == 'core' ) {
|
302 |
-
$sVersion = $sSlug->current; // stdClass from transient update_core
|
303 |
-
$sSlug = 'wp';
|
304 |
-
}
|
305 |
-
|
306 |
-
$aItemTk = isset( $aTk[ $sContext ][ $sSlug ] ) ? $aTk[ $sContext ][ $sSlug ] : [];
|
307 |
-
|
308 |
-
if ( $sContext == 'plugins' ) {
|
309 |
-
$oPlugin = Services::WpPlugins()->getUpdateInfo( $sSlug );
|
310 |
-
$sVersion = isset( $oPlugin->new_version ) ? $oPlugin->new_version : '';
|
311 |
-
}
|
312 |
-
elseif ( $sContext == 'themes' ) {
|
313 |
-
$aThemeInfo = Services::WpThemes()->getUpdateInfo( $sSlug );
|
314 |
-
$sVersion = isset( $aThemeInfo[ 'new_version' ] ) ? $aThemeInfo[ 'new_version' ] : '';
|
315 |
-
}
|
316 |
-
|
317 |
-
if ( !empty( $sVersion ) && isset( $aItemTk[ $sVersion ] ) ) {
|
318 |
-
$bDelayed = ( Services::Request()->ts() - $aItemTk[ $sVersion ] < $oOpts->getDelayUpdatesPeriod() );
|
319 |
-
}
|
320 |
-
}
|
321 |
-
|
322 |
-
return $bDelayed;
|
323 |
-
}
|
324 |
-
|
325 |
-
/**
|
326 |
-
* A filter on whether or not a notification email is sent after core upgrades are attempted.
|
327 |
-
* @param bool $bSendEmail
|
328 |
-
* @return bool
|
329 |
-
*/
|
330 |
-
public function autoupdate_send_email( $bSendEmail ) {
|
331 |
-
/** @var Modules\Autoupdates\Options $oOpts */
|
332 |
-
$oOpts = $this->getOptions();
|
333 |
-
return $oOpts->isSendAutoupdatesNotificationEmail();
|
334 |
-
}
|
335 |
-
|
336 |
-
/**
|
337 |
-
* A filter on the target email address to which to send upgrade notification emails.
|
338 |
-
* @param array $aEmailParams
|
339 |
-
* @return array
|
340 |
-
*/
|
341 |
-
public function autoupdate_email_override( $aEmailParams ) {
|
342 |
-
$sOverride = $this->getOptions()->getOpt( 'override_email_address', '' );
|
343 |
-
if ( Services::Data()->validEmail( $sOverride ) ) {
|
344 |
-
$aEmailParams[ 'to' ] = $sOverride;
|
345 |
-
}
|
346 |
-
return $aEmailParams;
|
347 |
-
}
|
348 |
-
|
349 |
-
/**
|
350 |
-
* @param array $aUpdateResults
|
351 |
-
*/
|
352 |
-
public function sendNotificationEmail( $aUpdateResults ) {
|
353 |
-
if ( empty( $aUpdateResults ) || !is_array( $aUpdateResults ) ) {
|
354 |
-
return;
|
355 |
-
}
|
356 |
-
|
357 |
-
// Are there really updates?
|
358 |
-
$bReallyUpdates = false;
|
359 |
-
|
360 |
-
$aBody = [
|
361 |
-
sprintf(
|
362 |
-
__( 'This is a quick notification from the %s that WordPress Automatic Updates just completed on your site with the following results.', 'wp-simple-firewall' ),
|
363 |
-
$this->getCon()->getHumanName()
|
364 |
-
),
|
365 |
-
''
|
366 |
-
];
|
367 |
-
|
368 |
-
$aTrkd = $this->getTrackedAssetsVersions();
|
369 |
-
|
370 |
-
$oWpPlugins = Services::WpPlugins();
|
371 |
-
if ( !empty( $aUpdateResults[ 'plugin' ] ) && is_array( $aUpdateResults[ 'plugin' ] ) ) {
|
372 |
-
$bHasPluginUpdates = false;
|
373 |
-
$aTrkdPlugs = $aTrkd[ 'plugins' ];
|
374 |
-
|
375 |
-
$aTempContent[] = __( 'Plugins Updated:', 'wp-simple-firewall' );
|
376 |
-
foreach ( $aUpdateResults[ 'plugin' ] as $oUpdate ) {
|
377 |
-
$oP = $oWpPlugins->getPluginAsVo( $oUpdate->item->plugin, true );
|
378 |
-
$bValidUpdate = !empty( $oUpdate->result ) && !empty( $oUpdate->name )
|
379 |
-
&& isset( $aTrkdPlugs[ $oP->file ] )
|
380 |
-
&& version_compare( $aTrkdPlugs[ $oP->file ], $oP->Version, '<' );
|
381 |
-
if ( $bValidUpdate ) {
|
382 |
-
$aTempContent[] = ' - '.sprintf(
|
383 |
-
__( 'Plugin "%s" auto-updated from "%s" to version "%s"', 'wp-simple-firewall' ),
|
384 |
-
$oUpdate->name, $aTrkdPlugs[ $oP->file ], $oP->Version );
|
385 |
-
$bHasPluginUpdates = true;
|
386 |
-
}
|
387 |
-
}
|
388 |
-
$aTempContent[] = '';
|
389 |
-
|
390 |
-
if ( $bHasPluginUpdates ) {
|
391 |
-
$bReallyUpdates = true;
|
392 |
-
$aBody = array_merge( $aBody, $aTempContent );
|
393 |
-
}
|
394 |
-
}
|
395 |
-
|
396 |
-
if ( !empty( $aUpdateResults[ 'theme' ] ) && is_array( $aUpdateResults[ 'theme' ] ) ) {
|
397 |
-
$bHasThemesUpdates = false;
|
398 |
-
$aTrkdThemes = $aTrkd[ 'themes' ];
|
399 |
-
|
400 |
-
$aTempContent = [ __( 'Themes Updated:', 'wp-simple-firewall' ) ];
|
401 |
-
foreach ( $aUpdateResults[ 'theme' ] as $oUpdate ) {
|
402 |
-
$oItem = $oUpdate->item;
|
403 |
-
$bValidUpdate = isset( $oUpdate->result ) && $oUpdate->result && !empty( $oUpdate->name )
|
404 |
-
&& isset( $aTrkdThemes[ $oItem->theme ] )
|
405 |
-
&& version_compare( $aTrkdThemes[ $oItem->theme ], $oItem->new_version, '<' );
|
406 |
-
if ( $bValidUpdate ) {
|
407 |
-
$aTempContent[] = ' - '.sprintf(
|
408 |
-
__( 'Theme "%s" auto-updated from "%s" to version "%s"', 'wp-simple-firewall' ),
|
409 |
-
$oUpdate->name, $aTrkdThemes[ $oItem->theme ], $oItem->new_version );
|
410 |
-
$bHasThemesUpdates = true;
|
411 |
-
}
|
412 |
-
}
|
413 |
-
$aTempContent[] = '';
|
414 |
-
|
415 |
-
if ( $bHasThemesUpdates ) {
|
416 |
-
$bReallyUpdates = true;
|
417 |
-
$aBody = array_merge( $aBody, $aTempContent );
|
418 |
-
}
|
419 |
-
}
|
420 |
-
|
421 |
-
if ( !empty( $aUpdateResults[ 'core' ] ) && is_array( $aUpdateResults[ 'core' ] ) ) {
|
422 |
-
$bHasCoreUpdates = false;
|
423 |
-
$aTempContent = [ __( 'WordPress Core Updated:', 'wp-simple-firewall' ) ];
|
424 |
-
foreach ( $aUpdateResults[ 'core' ] as $oUpdate ) {
|
425 |
-
if ( isset( $oUpdate->result ) && !is_wp_error( $oUpdate->result ) ) {
|
426 |
-
$aTempContent[] = ' - '.sprintf( 'WordPress was automatically updated to "%s"', $oUpdate->name );
|
427 |
-
$bHasCoreUpdates = true;
|
428 |
-
}
|
429 |
-
}
|
430 |
-
$aTempContent[] = '';
|
431 |
-
|
432 |
-
if ( $bHasCoreUpdates ) {
|
433 |
-
$bReallyUpdates = true;
|
434 |
-
$aBody = array_merge( $aBody, $aTempContent );
|
435 |
-
}
|
436 |
-
}
|
437 |
-
|
438 |
-
if ( !$bReallyUpdates ) {
|
439 |
-
return;
|
440 |
-
}
|
441 |
-
|
442 |
-
$aBody[] = __( 'Thank you.', 'wp-simple-firewall' );
|
443 |
-
|
444 |
-
$sTitle = sprintf( __( "Notice: %s", 'wp-simple-firewall' ), __( "Automatic Updates Completed", 'wp-simple-firewall' ) );
|
445 |
-
$this->getEmailProcessor()
|
446 |
-
->sendEmailWithWrap( $this->getOptions()->getOpt( 'override_email_address' ), $sTitle, $aBody );
|
447 |
-
die();
|
448 |
-
}
|
449 |
-
|
450 |
-
/**
|
451 |
-
* @return int
|
452 |
-
*/
|
453 |
-
private function getHookPriority() {
|
454 |
-
return $this->getOptions()->getDef( 'action_hook_priority' );
|
455 |
-
}
|
456 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
src/processors/comments_filter.php
DELETED
@@ -1,75 +0,0 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
use FernleafSystems\Wordpress\Plugin\Shield\Modules;
|
4 |
-
use FernleafSystems\Wordpress\Plugin\Shield\Modules\CommentsFilter;
|
5 |
-
use FernleafSystems\Wordpress\Services\Services;
|
6 |
-
|
7 |
-
/**
|
8 |
-
* @deprecated 10.1
|
9 |
-
*/
|
10 |
-
class ICWP_WPSF_Processor_CommentsFilter extends Modules\BaseShield\ShieldProcessor {
|
11 |
-
|
12 |
-
public function run() {
|
13 |
-
}
|
14 |
-
|
15 |
-
public function onWpInit() {
|
16 |
-
/** @var CommentsFilter\ModCon $mod */
|
17 |
-
$mod = $this->getMod();
|
18 |
-
/** @var CommentsFilter\Options $opts */
|
19 |
-
$opts = $this->getOptions();
|
20 |
-
$oWpUsers = Services::WpUsers();
|
21 |
-
|
22 |
-
$bLoadComProc = !$oWpUsers->isUserLoggedIn() ||
|
23 |
-
!( new CommentsFilter\Scan\IsEmailTrusted() )->trusted(
|
24 |
-
$oWpUsers->getCurrentWpUser()->user_email,
|
25 |
-
$opts->getApprovedMinimum(),
|
26 |
-
$opts->getTrustedRoles()
|
27 |
-
);
|
28 |
-
|
29 |
-
if ( $bLoadComProc ) {
|
30 |
-
|
31 |
-
if ( $opts->isEnabledCaptcha() && $mod->getCaptchaCfg()->ready ) {
|
32 |
-
$this->getSubPro( 'recaptcha' )->execute();
|
33 |
-
}
|
34 |
-
|
35 |
-
if ( Services::Request()->isPost() ) {
|
36 |
-
add_filter( 'comment_notification_recipients', [ $this, 'clearCommentNotificationEmail' ], 100, 1 );
|
37 |
-
}
|
38 |
-
elseif ( $opts->isEnabledGaspCheck() ) {
|
39 |
-
$this->getSubPro( 'bot' )->execute();
|
40 |
-
}
|
41 |
-
}
|
42 |
-
}
|
43 |
-
|
44 |
-
public function runHourlyCron() {
|
45 |
-
/** @var CommentsFilter\Options $opts */
|
46 |
-
$opts = $this->getOptions();
|
47 |
-
if ( $opts->isEnabledGaspCheck() && function_exists( 'delete_expired_transients' ) ) {
|
48 |
-
delete_expired_transients(); // cleanup unused comment tokens
|
49 |
-
}
|
50 |
-
}
|
51 |
-
|
52 |
-
/**
|
53 |
-
* @return array
|
54 |
-
*/
|
55 |
-
protected function getSubProMap() :array {
|
56 |
-
return [
|
57 |
-
'bot' => 'ICWP_WPSF_Processor_CommentsFilter_BotSpam',
|
58 |
-
'recaptcha' => 'ICWP_WPSF_Processor_CommentsFilter_GoogleRecaptcha',
|
59 |
-
];
|
60 |
-
}
|
61 |
-
|
62 |
-
/**
|
63 |
-
* When you set a new comment as anything but 'spam' a notification email is sent to the post author.
|
64 |
-
* We suppress this for when we mark as trash by emptying the email notifications list.
|
65 |
-
* @param array $aEmails
|
66 |
-
* @return array
|
67 |
-
*/
|
68 |
-
public function clearCommentNotificationEmail( $aEmails ) {
|
69 |
-
$sStatus = apply_filters( $this->getCon()->prefix( 'cf_status' ), '' );
|
70 |
-
if ( in_array( $sStatus, [ 'reject', 'trash' ] ) ) {
|
71 |
-
$aEmails = [];
|
72 |
-
}
|
73 |
-
return $aEmails;
|
74 |
-
}
|
75 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
src/processors/commentsfilter_botspam.php
DELETED
@@ -1,116 +0,0 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
use FernleafSystems\Wordpress\Plugin\Shield\Modules;
|
4 |
-
use FernleafSystems\Wordpress\Plugin\Shield\Modules\CommentsFilter;
|
5 |
-
use FernleafSystems\Wordpress\Services\Services;
|
6 |
-
|
7 |
-
/**
|
8 |
-
* @deprecated 10.1
|
9 |
-
*/
|
10 |
-
class ICWP_WPSF_Processor_CommentsFilter_BotSpam extends Modules\BaseShield\ShieldProcessor {
|
11 |
-
|
12 |
-
/**
|
13 |
-
* The unique comment token assigned to this page
|
14 |
-
* @var string
|
15 |
-
*/
|
16 |
-
private $sFormId;
|
17 |
-
|
18 |
-
/**
|
19 |
-
* @var bool
|
20 |
-
*/
|
21 |
-
private $bFormItemPrinted = false;
|
22 |
-
|
23 |
-
public function run() {
|
24 |
-
add_action( 'wp', [ $this, 'onWp' ] );
|
25 |
-
add_action( 'wp_footer', [ $this, 'maybeDequeueScript' ] );
|
26 |
-
}
|
27 |
-
|
28 |
-
public function onWp() {
|
29 |
-
add_action( 'comment_form', [ $this, 'printGaspFormItems' ], 1 );
|
30 |
-
}
|
31 |
-
|
32 |
-
public function onWpEnqueueJs() {
|
33 |
-
/** @var CommentsFilter\ModCon $mod */
|
34 |
-
$mod = $this->getMod();
|
35 |
-
/** @var CommentsFilter\Options $opts */
|
36 |
-
$opts = $this->getOptions();
|
37 |
-
$con = $this->getCon();
|
38 |
-
|
39 |
-
$sAsset = 'shield-comments';
|
40 |
-
$sUnique = $con->prefix( 'shield-comments' );
|
41 |
-
wp_register_script(
|
42 |
-
$sUnique,
|
43 |
-
$con->getPluginUrl_Js( $sAsset ),
|
44 |
-
[ 'jquery' ],
|
45 |
-
$con->getVersion(),
|
46 |
-
true
|
47 |
-
);
|
48 |
-
wp_enqueue_script( $sUnique );
|
49 |
-
|
50 |
-
$nTs = Services::Request()->ts();
|
51 |
-
$aNonce = $mod->getAjaxActionData( 'comment_token'.Services::IP()->getRequestIp() );
|
52 |
-
$aNonce[ 'ts' ] = $nTs;
|
53 |
-
$aNonce[ 'post_id' ] = Services::WpPost()->getCurrentPostId();
|
54 |
-
|
55 |
-
wp_localize_script(
|
56 |
-
$sUnique,
|
57 |
-
'shield_comments',
|
58 |
-
[
|
59 |
-
'ajax' => [
|
60 |
-
'comment_token' => $aNonce,
|
61 |
-
],
|
62 |
-
'vars' => [
|
63 |
-
'cbname' => 'cb_nombre'.rand(),
|
64 |
-
'botts' => $nTs,
|
65 |
-
'token' => 'not created',
|
66 |
-
'uniq' => $this->getUniqueFormId(),
|
67 |
-
'cooldown' => $opts->getTokenCooldown(),
|
68 |
-
'expires' => $opts->getTokenExpireInterval(),
|
69 |
-
],
|
70 |
-
'strings' => [
|
71 |
-
'label' => $mod->getTextOpt( 'custom_message_checkbox' ),
|
72 |
-
'alert' => $mod->getTextOpt( 'custom_message_alert' ),
|
73 |
-
'comment_reload' => $mod->getTextOpt( 'custom_message_comment_reload' ),
|
74 |
-
'js_comment_wait' => $mod->getTextOpt( 'custom_message_comment_wait' ),
|
75 |
-
],
|
76 |
-
'flags' => [
|
77 |
-
'gasp' => true,
|
78 |
-
'recap' => $opts->isEnabledCaptcha() && $mod->getCaptchaCfg()->ready,
|
79 |
-
]
|
80 |
-
]
|
81 |
-
);
|
82 |
-
}
|
83 |
-
|
84 |
-
/**
|
85 |
-
* If the comment form component hasn't been printed, there's no comment form to protect.
|
86 |
-
*/
|
87 |
-
public function maybeDequeueScript() {
|
88 |
-
if ( empty( $this->bFormItemPrinted ) ) {
|
89 |
-
wp_dequeue_script( $this->getCon()->prefix( 'shield-comments' ) );
|
90 |
-
}
|
91 |
-
}
|
92 |
-
|
93 |
-
public function printGaspFormItems() {
|
94 |
-
$this->bFormItemPrinted = true;
|
95 |
-
echo $this->getMod()
|
96 |
-
->renderTemplate(
|
97 |
-
'snippets/comment_form_botbox.twig',
|
98 |
-
[ 'uniq' => $this->getUniqueFormId() ],
|
99 |
-
true
|
100 |
-
);
|
101 |
-
}
|
102 |
-
|
103 |
-
/**
|
104 |
-
* @return string
|
105 |
-
*/
|
106 |
-
private function getUniqueFormId() {
|
107 |
-
if ( !isset( $this->sFormId ) ) {
|
108 |
-
$oDp = Services::Data();
|
109 |
-
$sId = $oDp->generateRandomLetter().$oDp->generateRandomString( rand( 7, 23 ), 7 );
|
110 |
-
$this->sFormId = preg_replace(
|
111 |
-
'#[^a-zA-Z0-9]#', '',
|
112 |
-
apply_filters( 'icwp_shield_cf_gasp_uniqid', $sId ) );
|
113 |
-
}
|
114 |
-
return $this->sFormId;
|
115 |
-
}
|
116 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
src/processors/commentsfilter_googlerecaptcha.php
DELETED
@@ -1,35 +0,0 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
use FernleafSystems\Wordpress\Plugin\Shield\Modules;
|
4 |
-
use FernleafSystems\Wordpress\Services\Services;
|
5 |
-
|
6 |
-
/**
|
7 |
-
* @deprecated 10.1
|
8 |
-
*/
|
9 |
-
class ICWP_WPSF_Processor_CommentsFilter_GoogleRecaptcha extends Modules\BaseShield\ShieldProcessor {
|
10 |
-
|
11 |
-
public function run() {
|
12 |
-
add_action( 'wp', [ $this, 'setup' ] );
|
13 |
-
}
|
14 |
-
|
15 |
-
/**
|
16 |
-
* The WP Query is alive and well at this stage so we can assume certain data is available.
|
17 |
-
*/
|
18 |
-
public function setup() {
|
19 |
-
if ( Services::WpComments()->isCommentsOpen() ) {
|
20 |
-
$this->getCon()
|
21 |
-
->getModule_Plugin()
|
22 |
-
->getCaptchaEnqueue()
|
23 |
-
->setMod( $this->getMod() )
|
24 |
-
->setToEnqueue();
|
25 |
-
add_action( 'comment_form_after_fields', [ $this, 'printGoogleRecaptchaCheck' ] );
|
26 |
-
}
|
27 |
-
}
|
28 |
-
|
29 |
-
public function printGoogleRecaptchaCheck() {
|
30 |
-
echo $this->getCon()
|
31 |
-
->getModule_Plugin()
|
32 |
-
->getCaptchaEnqueue()
|
33 |
-
->getCaptchaHtml();
|
34 |
-
}
|
35 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
src/processors/email.php
DELETED
@@ -1,218 +0,0 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
use FernleafSystems\Wordpress\Plugin\Shield;
|
4 |
-
use FernleafSystems\Wordpress\Plugin\Shield\Modules;
|
5 |
-
use FernleafSystems\Wordpress\Services\Services;
|
6 |
-
|
7 |
-
/**
|
8 |
-
* @deprecated 10.1
|
9 |
-
*/
|
10 |
-
class ICWP_WPSF_Processor_Email extends Modules\BaseShield\ShieldProcessor {
|
11 |
-
|
12 |
-
const Slug = 'email';
|
13 |
-
|
14 |
-
/**
|
15 |
-
* @return array
|
16 |
-
*/
|
17 |
-
protected function getEmailHeader() {
|
18 |
-
return [
|
19 |
-
__( 'Hi !', 'wp-simple-firewall' ),
|
20 |
-
'',
|
21 |
-
];
|
22 |
-
}
|
23 |
-
|
24 |
-
/**
|
25 |
-
* @return array
|
26 |
-
*/
|
27 |
-
protected function getEmailFooter() {
|
28 |
-
$con = $this->getCon();
|
29 |
-
$oWp = Services::WpGeneral();
|
30 |
-
|
31 |
-
{
|
32 |
-
$aGoProPhrases = [
|
33 |
-
'Go PRO For The Equivalent Of 1 Cappuccino Per Month ☕',
|
34 |
-
'Go PRO For The Equivalent Of 1 Beer Per Month 🍺',
|
35 |
-
'Go PRO For The Equivalent Of 1 Glass Of Wine Per Month 🍷',
|
36 |
-
];
|
37 |
-
$aBenefits = [
|
38 |
-
'The Easiest, Frustration-Free Pro-Upgrade Available Anywhere',
|
39 |
-
'Powerful, Auto-Learning Malware Scanner',
|
40 |
-
'Plugin and Theme File Guard',
|
41 |
-
'Vulnerability Scanner',
|
42 |
-
'Traffic Rate Limiting',
|
43 |
-
'WooCommerce Support',
|
44 |
-
'Automatic Import/Export Sync Of Options Across Your WP Portfolio',
|
45 |
-
'Powerful User Password Policies',
|
46 |
-
'Exclusive Customer Support',
|
47 |
-
'That Warm And Fuzzy Feeling That Comes From Supporting Future Development',
|
48 |
-
];
|
49 |
-
shuffle( $aBenefits );
|
50 |
-
}
|
51 |
-
|
52 |
-
$aFooter = [
|
53 |
-
$this->getMod()
|
54 |
-
->renderTemplate( '/email/footer.twig', [
|
55 |
-
'strings' => [
|
56 |
-
'benefits' => $aBenefits,
|
57 |
-
'much_more' => 'And So Much More',
|
58 |
-
'upgrade' => $aGoProPhrases[ array_rand( $aGoProPhrases ) ],
|
59 |
-
'sent_from' => sprintf( __( 'Email sent from the %s Plugin v%s, on %s.', 'wp-simple-firewall' ),
|
60 |
-
$this->getCon()->getHumanName(),
|
61 |
-
$this->getCon()->getVersion(),
|
62 |
-
$oWp->getHomeUrl()
|
63 |
-
),
|
64 |
-
'delays' => __( 'Note: Email delays are caused by website hosting and email providers.', 'wp-simple-firewall' ),
|
65 |
-
'time_sent' => sprintf( __( 'Time Sent: %s', 'wp-simple-firewall' ), $oWp->getTimeStampForDisplay() ),
|
66 |
-
],
|
67 |
-
'hrefs' => [
|
68 |
-
'upgrade' => 'https://shsec.io/buyshieldproemailfooter',
|
69 |
-
'much_more' => 'https://shsec.io/gp'
|
70 |
-
],
|
71 |
-
'flags' => [
|
72 |
-
'is_pro' => $con->isPremiumActive(),
|
73 |
-
'is_whitelabelled' => $con->getModule_SecAdmin()->isWlEnabled()
|
74 |
-
]
|
75 |
-
] ),
|
76 |
-
];
|
77 |
-
|
78 |
-
return apply_filters( 'icwp_shield_email_footer', $aFooter );
|
79 |
-
}
|
80 |
-
|
81 |
-
/**
|
82 |
-
* Wraps up a message with header and footer
|
83 |
-
* @param string $sAddress
|
84 |
-
* @param string $sSubject
|
85 |
-
* @param array $aMessage
|
86 |
-
* @return bool
|
87 |
-
*/
|
88 |
-
public function sendEmailWithWrap( $sAddress = '', $sSubject = '', $aMessage = [] ) :bool {
|
89 |
-
$oWP = Services::WpGeneral();
|
90 |
-
return $this->send(
|
91 |
-
$sAddress,
|
92 |
-
sprintf( '[%s] %s', html_entity_decode( $oWP->getSiteName(), ENT_QUOTES ), $sSubject ),
|
93 |
-
sprintf( '<html lang="%s">%s</html>',
|
94 |
-
$oWP->getLocale( '-' ),
|
95 |
-
implode( "<br />", array_merge( $this->getEmailHeader(), $aMessage, $this->getEmailFooter() ) )
|
96 |
-
)
|
97 |
-
);
|
98 |
-
}
|
99 |
-
|
100 |
-
public function sendEmailWithTemplate( string $templ, string $to, string $subject, array $body ) :bool {
|
101 |
-
return $this->send(
|
102 |
-
$to,
|
103 |
-
$subject,
|
104 |
-
$this->getMod()->renderTemplate(
|
105 |
-
$templ,
|
106 |
-
[
|
107 |
-
'header' => $this->getEmailHeader(),
|
108 |
-
'body' => $body,
|
109 |
-
'footer' => $this->getEmailFooter(),
|
110 |
-
'vars' => [
|
111 |
-
'lang' => Services::WpGeneral()->getLocale( '-' )
|
112 |
-
]
|
113 |
-
],
|
114 |
-
true
|
115 |
-
)
|
116 |
-
);
|
117 |
-
}
|
118 |
-
|
119 |
-
/**
|
120 |
-
* @param string $sAddress
|
121 |
-
* @param string $sSubject
|
122 |
-
* @param string $sMessageBody
|
123 |
-
* @return bool
|
124 |
-
* @uses wp_mail
|
125 |
-
*/
|
126 |
-
public function send( $sAddress = '', $sSubject = '', $sMessageBody = '' ) :bool {
|
127 |
-
|
128 |
-
$this->emailFilters( true );
|
129 |
-
$bSuccess = wp_mail(
|
130 |
-
$this->verifyEmailAddress( $sAddress ),
|
131 |
-
$sSubject,
|
132 |
-
$sMessageBody
|
133 |
-
);
|
134 |
-
$this->emailFilters( false );
|
135 |
-
|
136 |
-
return (bool)$bSuccess;
|
137 |
-
}
|
138 |
-
|
139 |
-
/**
|
140 |
-
* @param $bAdd - true to add, false to remove
|
141 |
-
*/
|
142 |
-
protected function emailFilters( $bAdd ) {
|
143 |
-
if ( $bAdd ) {
|
144 |
-
add_filter( 'wp_mail_from', [ $this, 'setMailFrom' ], 100 );
|
145 |
-
add_filter( 'wp_mail_from_name', [ $this, 'setMailFromName' ], 100 );
|
146 |
-
add_filter( 'wp_mail_content_type', [ $this, 'setMailContentType' ], 100, 0 );
|
147 |
-
}
|
148 |
-
else {
|
149 |
-
remove_filter( 'wp_mail_from', [ $this, 'setMailFrom' ], 100 );
|
150 |
-
remove_filter( 'wp_mail_from_name', [ $this, 'setMailFromName' ], 100 );
|
151 |
-
remove_filter( 'wp_mail_content_type', [ $this, 'setMailContentType' ], 100 );
|
152 |
-
}
|
153 |
-
}
|
154 |
-
|
155 |
-
/**
|
156 |
-
* @return string
|
157 |
-
*/
|
158 |
-
public function setMailContentType() {
|
159 |
-
return 'text/html';
|
160 |
-
}
|
161 |
-
|
162 |
-
/**
|
163 |
-
* @param string $sFrom
|
164 |
-
* @return string
|
165 |
-
*/
|
166 |
-
public function setMailFrom( $sFrom ) {
|
167 |
-
$oDP = Services::Data();
|
168 |
-
$sProposedFrom = apply_filters( 'icwp_shield_from_email', '' );
|
169 |
-
if ( $oDP->validEmail( $sProposedFrom ) ) {
|
170 |
-
$sFrom = $sProposedFrom;
|
171 |
-
}
|
172 |
-
// We help out by trying to correct any funky "from" addresses
|
173 |
-
// So, at the very least, we don't fail on this for our emails.
|
174 |
-
if ( !$oDP->validEmail( $sFrom ) ) {
|
175 |
-
$aUrlParts = @parse_url( Services::WpGeneral()->getWpUrl() );
|
176 |
-
if ( !empty( $aUrlParts[ 'host' ] ) ) {
|
177 |
-
$sProposedFrom = 'wordpress@'.$aUrlParts[ 'host' ];
|
178 |
-
if ( $oDP->validEmail( $sProposedFrom ) ) {
|
179 |
-
$sFrom = $sProposedFrom;
|
180 |
-
}
|
181 |
-
}
|
182 |
-
}
|
183 |
-
return $sFrom;
|
184 |
-
}
|
185 |
-
|
186 |
-
/**
|
187 |
-
* @param string $sFromName
|
188 |
-
* @return string
|
189 |
-
*/
|
190 |
-
public function setMailFromName( $sFromName ) {
|
191 |
-
$sProposedFromName = apply_filters( 'icwp_shield_from_email_name', '' );
|
192 |
-
if ( !empty( $sProposedFromName ) ) {
|
193 |
-
$sFromName = $sProposedFromName;
|
194 |
-
}
|
195 |
-
else {
|
196 |
-
$sFromName = sprintf( '%s - %s', $sFromName, $this->getCon()->getHumanName() );
|
197 |
-
}
|
198 |
-
return $sFromName;
|
199 |
-
}
|
200 |
-
|
201 |
-
/**
|
202 |
-
* Will send email to the default recipient setup in the object.
|
203 |
-
* @param string $sEmailSubject
|
204 |
-
* @param array $aMessage
|
205 |
-
* @return bool
|
206 |
-
*/
|
207 |
-
public function sendEmail( $sEmailSubject, $aMessage ) {
|
208 |
-
return $this->sendEmailWithWrap( null, $sEmailSubject, $aMessage );
|
209 |
-
}
|
210 |
-
|
211 |
-
/**
|
212 |
-
* @param string $sEmail
|
213 |
-
* @return string
|
214 |
-
*/
|
215 |
-
public function verifyEmailAddress( $sEmail = '' ) {
|
216 |
-
return Services::Data()->validEmail( $sEmail ) ? $sEmail : Services::WpGeneral()->getSiteAdminEmail();
|
217 |
-
}
|
218 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
src/processors/events.php
DELETED
@@ -1,94 +0,0 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
use FernleafSystems\Wordpress\Plugin\Shield;
|
4 |
-
use FernleafSystems\Wordpress\Plugin\Shield\Databases;
|
5 |
-
use FernleafSystems\Wordpress\Plugin\Shield\Modules\Events;
|
6 |
-
use FernleafSystems\Wordpress\Plugin\Shield\Modules\Events\ModCon;
|
7 |
-
|
8 |
-
/**
|
9 |
-
* @deprecated 10.1
|
10 |
-
*/
|
11 |
-
class ICWP_WPSF_Processor_Events extends Shield\Modules\BaseShield\ShieldProcessor {
|
12 |
-
|
13 |
-
/**
|
14 |
-
* @var Events\Lib\StatsWriter
|
15 |
-
*/
|
16 |
-
private $oStatsWriter;
|
17 |
-
|
18 |
-
public function run() {
|
19 |
-
$this->loadStatsWriter()->setIfCommit( true );
|
20 |
-
add_action( $this->getCon()->prefix( 'dashboard_widget_content' ), [ $this, 'statsWidget' ], 10 );
|
21 |
-
}
|
22 |
-
|
23 |
-
/**
|
24 |
-
* @return Events\Lib\StatsWriter
|
25 |
-
*/
|
26 |
-
public function loadStatsWriter() {
|
27 |
-
if ( !isset( $this->oStatsWriter ) ) {
|
28 |
-
/** @var ModCon $mod */
|
29 |
-
$mod = $this->getMod();
|
30 |
-
$this->oStatsWriter = ( new Events\Lib\StatsWriter( $this->getCon() ) )
|
31 |
-
->setDbHandler( $mod->getDbHandler_Events() );
|
32 |
-
}
|
33 |
-
return $this->oStatsWriter;
|
34 |
-
}
|
35 |
-
|
36 |
-
public function statsWidget() {
|
37 |
-
/** @var Databases\Events\Select $oSelEvents */
|
38 |
-
$oSelEvents = $this->getCon()
|
39 |
-
->getModule_Events()
|
40 |
-
->getDbHandler_Events()
|
41 |
-
->getQuerySelector();
|
42 |
-
|
43 |
-
$aKeyStats = [
|
44 |
-
'comments' => [
|
45 |
-
__( 'Comment Blocks', 'wp-simple-firewall' ),
|
46 |
-
$oSelEvents->clearWheres()->sumEvents( [
|
47 |
-
'spam_block_bot',
|
48 |
-
'spam_block_human',
|
49 |
-
'spam_block_recaptcha'
|
50 |
-
] )
|
51 |
-
],
|
52 |
-
'firewall' => [
|
53 |
-
__( 'Firewall Blocks', 'wp-simple-firewall' ),
|
54 |
-
$oSelEvents->clearWheres()->sumEvent( 'firewall_block' )
|
55 |
-
],
|
56 |
-
'login_fail' => [
|
57 |
-
__( 'Login Blocks', 'wp-simple-firewall' ),
|
58 |
-
$oSelEvents->clearWheres()->sumEvent( 'login_block' )
|
59 |
-
],
|
60 |
-
'login_verified' => [
|
61 |
-
__( 'Login Verified', 'wp-simple-firewall' ),
|
62 |
-
$oSelEvents->clearWheres()->sumEvent( '2fa_success' )
|
63 |
-
],
|
64 |
-
'session_start' => [
|
65 |
-
__( 'User Sessions', 'wp-simple-firewall' ),
|
66 |
-
$oSelEvents->clearWheres()->sumEvent( 'session_start' )
|
67 |
-
],
|
68 |
-
'ip_killed' => [
|
69 |
-
__( 'IP Blocks', 'wp-simple-firewall' ),
|
70 |
-
$oSelEvents->clearWheres()->sumEvent( 'conn_kill' )
|
71 |
-
],
|
72 |
-
'ip_transgressions' => [
|
73 |
-
__( 'Total Offenses', 'wp-simple-firewall' ),
|
74 |
-
$oSelEvents->clearWheres()->sumEvent( 'ip_offense' )
|
75 |
-
],
|
76 |
-
];
|
77 |
-
|
78 |
-
$aDisplayData = [
|
79 |
-
'sHeading' => sprintf( __( '%s Statistics', 'wp-simple-firewall' ), $this->getCon()->getHumanName() ),
|
80 |
-
'aKeyStats' => $aKeyStats,
|
81 |
-
];
|
82 |
-
|
83 |
-
echo $this->getMod()->renderTemplate(
|
84 |
-
'snippets/widget_dashboard_statistics.php',
|
85 |
-
$aDisplayData
|
86 |
-
);
|
87 |
-
}
|
88 |
-
|
89 |
-
public function runDailyCron() {
|
90 |
-
( new Events\Consolidate\ConsolidateAllEvents() )
|
91 |
-
->setMod( $this->getMod() )
|
92 |
-
->run();
|
93 |
-
}
|
94 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
src/processors/firewall.php
DELETED
@@ -1,430 +0,0 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
use FernleafSystems\Wordpress\Plugin\Shield\Modules;
|
4 |
-
use FernleafSystems\Wordpress\Plugin\Shield\Modules\Firewall;
|
5 |
-
use FernleafSystems\Wordpress\Services\Services;
|
6 |
-
|
7 |
-
/**
|
8 |
-
* @deprecated 10.1
|
9 |
-
*/
|
10 |
-
class ICWP_WPSF_Processor_Firewall extends Modules\BaseShield\ShieldProcessor {
|
11 |
-
|
12 |
-
/**
|
13 |
-
* @var array
|
14 |
-
*/
|
15 |
-
private $aDieMessage;
|
16 |
-
|
17 |
-
/**
|
18 |
-
* @var array
|
19 |
-
*/
|
20 |
-
protected $aPatterns;
|
21 |
-
|
22 |
-
/**
|
23 |
-
* @var array
|
24 |
-
*/
|
25 |
-
private $aAuditBlockMessage;
|
26 |
-
|
27 |
-
/**
|
28 |
-
* After any parameter whitelisting has been accounted for
|
29 |
-
*
|
30 |
-
* @var array
|
31 |
-
*/
|
32 |
-
private $aPageParams;
|
33 |
-
|
34 |
-
public function run() {
|
35 |
-
if ( $this->getIfPerformFirewallScan() && $this->getIfDoFirewallBlock() ) {
|
36 |
-
// Hooked here to ensure "plugins_loaded" has completely finished as some mailers aren't init'd.
|
37 |
-
add_action( 'init', function () {
|
38 |
-
$this->doPreFirewallBlock();
|
39 |
-
$this->doFirewallBlock();
|
40 |
-
}, 0 );
|
41 |
-
}
|
42 |
-
}
|
43 |
-
|
44 |
-
private function getIfDoFirewallBlock() :bool {
|
45 |
-
return !$this->isVisitorRequestPermitted();
|
46 |
-
}
|
47 |
-
|
48 |
-
private function getIfPerformFirewallScan() :bool {
|
49 |
-
$bPerformScan = true;
|
50 |
-
/** @var Modules\Firewall\Options $opts */
|
51 |
-
$opts = $this->getOptions();
|
52 |
-
|
53 |
-
$sPath = Services::Request()->getPath();
|
54 |
-
|
55 |
-
if ( count( $this->getRawRequestParams() ) == 0 ) {
|
56 |
-
$bPerformScan = false;
|
57 |
-
}
|
58 |
-
elseif ( empty( $sPath ) ) {
|
59 |
-
$this->getCon()->fireEvent( 'firewall_skip' );
|
60 |
-
$bPerformScan = false;
|
61 |
-
}
|
62 |
-
elseif ( count( $this->getParamsToCheck() ) == 0 ) {
|
63 |
-
$bPerformScan = false;
|
64 |
-
}
|
65 |
-
// TODO: are we calling is_super_admin() too early?
|
66 |
-
elseif ( $opts->isIgnoreAdmin() && is_super_admin() ) {
|
67 |
-
$bPerformScan = false;
|
68 |
-
}
|
69 |
-
|
70 |
-
return $bPerformScan;
|
71 |
-
}
|
72 |
-
|
73 |
-
private function isVisitorRequestPermitted() :bool {
|
74 |
-
$opts = $this->getOptions();
|
75 |
-
|
76 |
-
$bRequestIsPermitted = true;
|
77 |
-
if ( $bRequestIsPermitted && $opts->isOpt( 'block_dir_traversal', 'Y' ) ) {
|
78 |
-
$bRequestIsPermitted = $this->doPassCheck( 'dirtraversal' );
|
79 |
-
}
|
80 |
-
if ( $bRequestIsPermitted && $opts->isOpt( 'block_sql_queries', 'Y' ) ) {
|
81 |
-
$bRequestIsPermitted = $this->doPassCheck( 'sqlqueries' );
|
82 |
-
}
|
83 |
-
if ( $bRequestIsPermitted && $opts->isOpt( 'block_wordpress_terms', 'Y' ) ) {
|
84 |
-
$bRequestIsPermitted = $this->doPassCheck( 'wpterms' );
|
85 |
-
}
|
86 |
-
if ( $bRequestIsPermitted && $opts->isOpt( 'block_field_truncation', 'Y' ) ) {
|
87 |
-
$bRequestIsPermitted = $this->doPassCheck( 'fieldtruncation' );
|
88 |
-
}
|
89 |
-
if ( $bRequestIsPermitted && $opts->isOpt( 'block_php_code', 'Y' ) ) {
|
90 |
-
$bRequestIsPermitted = $this->doPassCheck( 'phpcode' );
|
91 |
-
}
|
92 |
-
if ( $bRequestIsPermitted && $opts->isOpt( 'block_leading_schema', 'Y' ) ) {
|
93 |
-
$bRequestIsPermitted = $this->doPassCheck( 'schema' );
|
94 |
-
}
|
95 |
-
if ( $bRequestIsPermitted && $opts->isOpt( 'block_aggressive', 'Y' ) ) {
|
96 |
-
$bRequestIsPermitted = $this->doPassCheck( 'aggressive' );
|
97 |
-
}
|
98 |
-
if ( $bRequestIsPermitted && $opts->isOpt( 'block_exe_file_uploads', 'Y' ) ) {
|
99 |
-
$bRequestIsPermitted = $this->doPassCheckBlockExeFileUploads();
|
100 |
-
}
|
101 |
-
return $bRequestIsPermitted;
|
102 |
-
}
|
103 |
-
|
104 |
-
protected function doPassCheckBlockExeFileUploads() :bool {
|
105 |
-
/** @var Firewall\ModCon $mod */
|
106 |
-
$mod = $this->getMod();
|
107 |
-
|
108 |
-
$sKey = 'exefile';
|
109 |
-
$bFAIL = false;
|
110 |
-
if ( isset( $_FILES ) && !empty( $_FILES ) ) {
|
111 |
-
$aFileNames = [];
|
112 |
-
foreach ( $_FILES as $aFile ) {
|
113 |
-
if ( !empty( $aFile[ 'name' ] ) ) {
|
114 |
-
$aFileNames[] = $aFile[ 'name' ];
|
115 |
-
}
|
116 |
-
}
|
117 |
-
$aMatchTerms = $this->getFirewallPatterns( 'exefile' );
|
118 |
-
if ( isset( $aMatchTerms[ 'regex' ] ) && is_array( $aMatchTerms[ 'regex' ] ) ) {
|
119 |
-
|
120 |
-
$aMatchTerms[ 'regex' ] = array_map( [ $this, 'prepRegexTerms' ], $aMatchTerms[ 'regex' ] );
|
121 |
-
foreach ( $aMatchTerms[ 'regex' ] as $sTerm ) {
|
122 |
-
foreach ( $aFileNames as $sParam => $mValue ) {
|
123 |
-
if ( is_scalar( $mValue ) && preg_match( $sTerm, (string)$mValue ) ) {
|
124 |
-
$bFAIL = true;
|
125 |
-
break( 2 );
|
126 |
-
}
|
127 |
-
}
|
128 |
-
}
|
129 |
-
}
|
130 |
-
if ( $bFAIL ) {
|
131 |
-
$this->getCon()
|
132 |
-
->fireEvent(
|
133 |
-
'block_exefile',
|
134 |
-
[
|
135 |
-
'audit' => [
|
136 |
-
'blockresponse' => $mod->getBlockResponse(),
|
137 |
-
'blockkey' => $sKey,
|
138 |
-
]
|
139 |
-
]
|
140 |
-
|
141 |
-
);
|
142 |
-
}
|
143 |
-
}
|
144 |
-
return !$bFAIL;
|
145 |
-
}
|
146 |
-
|
147 |
-
/**
|
148 |
-
* Returns false when check fails - that is, it should be blocked by the firewall.
|
149 |
-
*
|
150 |
-
* @param string $sBlockKey
|
151 |
-
* @return bool
|
152 |
-
*/
|
153 |
-
private function doPassCheck( string $sBlockKey ) :bool {
|
154 |
-
/** @var Firewall\ModCon $mod */
|
155 |
-
$mod = $this->getMod();
|
156 |
-
|
157 |
-
$aMatchTerms = $this->getFirewallPatterns( $sBlockKey );
|
158 |
-
$aParamValues = $this->getParamsToCheck();
|
159 |
-
if ( empty( $aMatchTerms ) || empty( $aParamValues ) ) {
|
160 |
-
return true;
|
161 |
-
}
|
162 |
-
|
163 |
-
$sParam = '';
|
164 |
-
$mValue = '';
|
165 |
-
|
166 |
-
$bFAIL = false;
|
167 |
-
if ( isset( $aMatchTerms[ 'simple' ] ) && is_array( $aMatchTerms[ 'simple' ] ) ) {
|
168 |
-
|
169 |
-
foreach ( $aMatchTerms[ 'simple' ] as $sTerm ) {
|
170 |
-
foreach ( $aParamValues as $sParam => $mValue ) {
|
171 |
-
if ( is_scalar( $mValue ) && ( stripos( (string)$mValue, $sTerm ) !== false ) ) {
|
172 |
-
$bFAIL = true;
|
173 |
-
break( 2 );
|
174 |
-
}
|
175 |
-
}
|
176 |
-
}
|
177 |
-
}
|
178 |
-
|
179 |
-
if ( !$bFAIL && isset( $aMatchTerms[ 'regex' ] ) && is_array( $aMatchTerms[ 'regex' ] ) ) {
|
180 |
-
$aMatchTerms[ 'regex' ] = array_map( [ $this, 'prepRegexTerms' ], $aMatchTerms[ 'regex' ] );
|
181 |
-
foreach ( $aMatchTerms[ 'regex' ] as $sTerm ) {
|
182 |
-
foreach ( $aParamValues as $sParam => $mValue ) {
|
183 |
-
if ( is_scalar( $mValue ) && preg_match( $sTerm, (string)$mValue ) ) {
|
184 |
-
$sParam = sanitize_text_field( $sParam );
|
185 |
-
$mValue = sanitize_text_field( $mValue );
|
186 |
-
$bFAIL = true;
|
187 |
-
break( 2 );
|
188 |
-
}
|
189 |
-
}
|
190 |
-
}
|
191 |
-
}
|
192 |
-
|
193 |
-
if ( $bFAIL ) {
|
194 |
-
$this->addToFirewallDieMessage( __( "Something in the URL, Form or Cookie data wasn't appropriate.", 'wp-simple-firewall' ) );
|
195 |
-
|
196 |
-
$this->aAuditBlockMessage = [
|
197 |
-
sprintf( __( 'Firewall Trigger: %s.', 'wp-simple-firewall' ), $this->getFirewallBlockKeyName( $sBlockKey ) ),
|
198 |
-
__( 'Page parameter failed firewall check.', 'wp-simple-firewall' ),
|
199 |
-
sprintf( __( 'The offending parameter was "%s" with a value of "%s".', 'wp-simple-firewall' ), $sParam, $mValue )
|
200 |
-
];
|
201 |
-
|
202 |
-
$this->getCon()
|
203 |
-
->fireEvent(
|
204 |
-
'blockparam_'.$sBlockKey,
|
205 |
-
[
|
206 |
-
'audit' => [
|
207 |
-
'param' => $sParam,
|
208 |
-
'val' => $mValue,
|
209 |
-
'blockresponse' => $mod->getBlockResponse(),
|
210 |
-
'blockkey' => $sBlockKey,
|
211 |
-
]
|
212 |
-
]
|
213 |
-
);
|
214 |
-
}
|
215 |
-
|
216 |
-
return !$bFAIL;
|
217 |
-
}
|
218 |
-
|
219 |
-
/**
|
220 |
-
* @param string $sKey
|
221 |
-
* @return array|null
|
222 |
-
*/
|
223 |
-
protected function getFirewallPatterns( $sKey = null ) {
|
224 |
-
if ( !isset( $this->aPatterns ) ) {
|
225 |
-
$this->aPatterns = $this->getOptions()->getDef( 'firewall_patterns' );
|
226 |
-
}
|
227 |
-
if ( !empty( $sKey ) ) {
|
228 |
-
return isset( $this->aPatterns[ $sKey ] ) ? $this->aPatterns[ $sKey ] : null;
|
229 |
-
}
|
230 |
-
return $this->aPatterns;
|
231 |
-
}
|
232 |
-
|
233 |
-
/**
|
234 |
-
* @param string $sTerm
|
235 |
-
* @return string
|
236 |
-
*/
|
237 |
-
private function prepRegexTerms( $sTerm ) {
|
238 |
-
return '/'.$sTerm.'/i';
|
239 |
-
}
|
240 |
-
|
241 |
-
private function doPreFirewallBlock() {
|
242 |
-
/** @var Modules\Firewall\Options $opts */
|
243 |
-
$opts = $this->getOptions();
|
244 |
-
if ( $opts->isSendBlockEmail() ) {
|
245 |
-
$recipient = $this->getMod()->getPluginReportEmail();
|
246 |
-
$this->getCon()->fireEvent(
|
247 |
-
$this->sendBlockEmail( $recipient ) ? 'fw_email_success' : 'fw_email_fail',
|
248 |
-
[ 'audit' => [ 'recipient' => $recipient ] ]
|
249 |
-
);
|
250 |
-
}
|
251 |
-
$this->getCon()->fireEvent( 'firewall_block' );
|
252 |
-
}
|
253 |
-
|
254 |
-
private function doFirewallBlock() {
|
255 |
-
/** @var Firewall\ModCon $mod */
|
256 |
-
$mod = $this->getMod();
|
257 |
-
|
258 |
-
switch ( $mod->getBlockResponse() ) {
|
259 |
-
case 'redirect_die':
|
260 |
-
Services::WpGeneral()->wpDie( 'Firewall Triggered' );
|
261 |
-
break;
|
262 |
-
case 'redirect_die_message':
|
263 |
-
Services::WpGeneral()->wpDie( $this->getFirewallDieMessageForDisplay() );
|
264 |
-
break;
|
265 |
-
case 'redirect_home':
|
266 |
-
Services::Response()->redirectToHome();
|
267 |
-
break;
|
268 |
-
case 'redirect_404':
|
269 |
-
header( 'Cache-Control: no-store, no-cache' );
|
270 |
-
Services::WpGeneral()->turnOffCache();
|
271 |
-
Services::Response()->sendApache404();
|
272 |
-
break;
|
273 |
-
default:
|
274 |
-
break;
|
275 |
-
}
|
276 |
-
die();
|
277 |
-
}
|
278 |
-
|
279 |
-
protected function getFirewallDieMessage() :array {
|
280 |
-
if ( !isset( $this->aDieMessage ) || !is_array( $this->aDieMessage ) ) {
|
281 |
-
$this->aDieMessage = [ $this->getMod()->getTextOpt( 'text_firewalldie' ) ];
|
282 |
-
}
|
283 |
-
return $this->aDieMessage;
|
284 |
-
}
|
285 |
-
|
286 |
-
protected function getFirewallDieMessageForDisplay() :string {
|
287 |
-
$messages = apply_filters(
|
288 |
-
$this->getCon()->prefix( 'firewall_die_message' ),
|
289 |
-
$this->getFirewallDieMessage()
|
290 |
-
);
|
291 |
-
return implode( ' ', is_array( $messages ) ? $messages : [] );
|
292 |
-
}
|
293 |
-
|
294 |
-
/**
|
295 |
-
* @param string $sMessagePart
|
296 |
-
* @return $this
|
297 |
-
*/
|
298 |
-
protected function addToFirewallDieMessage( $sMessagePart ) {
|
299 |
-
$aMessages = $this->getFirewallDieMessage();
|
300 |
-
$aMessages[] = $sMessagePart;
|
301 |
-
$this->aDieMessage = $aMessages;
|
302 |
-
return $this;
|
303 |
-
}
|
304 |
-
|
305 |
-
private function getParamsToCheck() :array {
|
306 |
-
if ( isset( $this->aPageParams ) ) {
|
307 |
-
return $this->aPageParams;
|
308 |
-
}
|
309 |
-
|
310 |
-
/** @var Modules\Firewall\Options $opts */
|
311 |
-
$opts = $this->getOptions();
|
312 |
-
|
313 |
-
$this->aPageParams = $this->getRawRequestParams();
|
314 |
-
$aWhitelist = Services::DataManipulation()
|
315 |
-
->mergeArraysRecursive( $opts->getDef( 'default_whitelist' ), $opts->getCustomWhitelist() );
|
316 |
-
|
317 |
-
// first we remove globally whitelisted request parameters
|
318 |
-
if ( !empty( $aWhitelist[ '*' ] ) && is_array( $aWhitelist[ '*' ] ) ) {
|
319 |
-
foreach ( $aWhitelist[ '*' ] as $sWhitelistParam ) {
|
320 |
-
|
321 |
-
if ( preg_match( '#^/.+/$#', $sWhitelistParam ) ) {
|
322 |
-
foreach ( array_keys( $this->aPageParams ) as $sParamKey ) {
|
323 |
-
if ( preg_match( $sWhitelistParam, $sParamKey ) ) {
|
324 |
-
unset( $this->aPageParams[ $sParamKey ] );
|
325 |
-
}
|
326 |
-
}
|
327 |
-
}
|
328 |
-
elseif ( isset( $this->aPageParams[ $sWhitelistParam ] ) ) {
|
329 |
-
unset( $this->aPageParams[ $sWhitelistParam ] );
|
330 |
-
}
|
331 |
-
}
|
332 |
-
}
|
333 |
-
|
334 |
-
// If the parameters to check is already empty, we return it to save any further processing.
|
335 |
-
if ( empty( $this->aPageParams ) ) {
|
336 |
-
return $this->aPageParams;
|
337 |
-
}
|
338 |
-
|
339 |
-
// Now we run through the list of whitelist pages
|
340 |
-
$sRequestPage = Services::Request()->getPath();
|
341 |
-
foreach ( $aWhitelist as $sWhitelistPageName => $aWhitelistPageParams ) {
|
342 |
-
|
343 |
-
// if the page is white listed
|
344 |
-
if ( strpos( $sRequestPage, $sWhitelistPageName ) !== false ) {
|
345 |
-
|
346 |
-
// if the page has no particular parameters specified there is nothing to check since the whole page is white listed.
|
347 |
-
if ( empty( $aWhitelistPageParams ) ) {
|
348 |
-
$this->aPageParams = [];
|
349 |
-
}
|
350 |
-
else {
|
351 |
-
// Otherwise we run through any whitelisted parameters and remove them.
|
352 |
-
foreach ( $aWhitelistPageParams as $sWhitelistParam ) {
|
353 |
-
if ( array_key_exists( $sWhitelistParam, $this->aPageParams ) ) {
|
354 |
-
unset( $this->aPageParams[ $sWhitelistParam ] );
|
355 |
-
}
|
356 |
-
}
|
357 |
-
}
|
358 |
-
break;
|
359 |
-
}
|
360 |
-
}
|
361 |
-
|
362 |
-
return $this->aPageParams;
|
363 |
-
}
|
364 |
-
|
365 |
-
private function getRawRequestParams() :array {
|
366 |
-
return Services::Request()->getRawRequestParams( $this->getOptions()->isOpt( 'include_cookie_checks', 'Y' ) );
|
367 |
-
}
|
368 |
-
|
369 |
-
private function sendBlockEmail( string $recipient ) :bool {
|
370 |
-
$bSuccess = false;
|
371 |
-
if ( !empty( $this->aAuditBlockMessage ) ) {
|
372 |
-
$sIp = Services::IP()->getRequestIp();
|
373 |
-
$aMessage = array_merge(
|
374 |
-
[
|
375 |
-
sprintf( __( '%s has blocked a page visit to your site.', 'wp-simple-firewall' ), $this->getCon()
|
376 |
-
->getHumanName() ),
|
377 |
-
__( 'Log details for this visitor are below:', 'wp-simple-firewall' ),
|
378 |
-
'- '.sprintf( '%s: %s', __( 'IP Address', 'wp-simple-firewall' ), $sIp ),
|
379 |
-
],
|
380 |
-
array_map(
|
381 |
-
function ( $sLine ) {
|
382 |
-
return '- '.$sLine;
|
383 |
-
},
|
384 |
-
$this->aAuditBlockMessage
|
385 |
-
),
|
386 |
-
[
|
387 |
-
'',
|
388 |
-
sprintf( __( 'You can look up the offending IP Address here: %s', 'wp-simple-firewall' ), 'http://ip-lookup.net/?ip='.$sIp )
|
389 |
-
]
|
390 |
-
);
|
391 |
-
|
392 |
-
$bSuccess = $this->getEmailProcessor()
|
393 |
-
->sendEmailWithWrap( $recipient, __( 'Firewall Block Alert', 'wp-simple-firewall' ), $aMessage );
|
394 |
-
}
|
395 |
-
return $bSuccess;
|
396 |
-
}
|
397 |
-
|
398 |
-
private function getFirewallBlockKeyName( string $blockKey ) :string {
|
399 |
-
switch ( $blockKey ) {
|
400 |
-
case 'dirtraversal':
|
401 |
-
$name = __( 'Directory Traversal', 'wp-simple-firewall' );
|
402 |
-
break;
|
403 |
-
case 'wpterms':
|
404 |
-
$name = __( 'WordPress Terms', 'wp-simple-firewall' );
|
405 |
-
break;
|
406 |
-
case 'fieldtruncation':
|
407 |
-
$name = __( 'Field Truncation', 'wp-simple-firewall' );
|
408 |
-
break;
|
409 |
-
case 'sqlqueries':
|
410 |
-
$name = __( 'SQL Queries', 'wp-simple-firewall' );
|
411 |
-
break;
|
412 |
-
case 'exefile':
|
413 |
-
$name = __( 'EXE File Uploads', 'wp-simple-firewall' );
|
414 |
-
break;
|
415 |
-
case 'schema':
|
416 |
-
$name = __( 'Leading Schema', 'wp-simple-firewall' );
|
417 |
-
break;
|
418 |
-
case 'phpcode':
|
419 |
-
$name = __( 'PHP Code', 'wp-simple-firewall' );
|
420 |
-
break;
|
421 |
-
case 'aggressive':
|
422 |
-
$name = __( 'Aggressive Rules', 'wp-simple-firewall' );
|
423 |
-
break;
|
424 |
-
default:
|
425 |
-
$name = __( 'Unknown Rules', 'wp-simple-firewall' );
|
426 |
-
break;
|
427 |
-
}
|
428 |
-
return $name;
|
429 |
-
}
|
430 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
src/processors/hack_protect.php
DELETED
@@ -1,26 +0,0 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
use FernleafSystems\Wordpress\Plugin\Shield;
|
4 |
-
use FernleafSystems\Wordpress\Plugin\Shield\Databases;
|
5 |
-
use FernleafSystems\Wordpress\Plugin\Shield\Modules;
|
6 |
-
use FernleafSystems\Wordpress\Plugin\Shield\Modules\HackGuard;
|
7 |
-
|
8 |
-
/**
|
9 |
-
* @deprecated 10.1
|
10 |
-
*/
|
11 |
-
class ICWP_WPSF_Processor_HackProtect extends Modules\BaseShield\ShieldProcessor {
|
12 |
-
|
13 |
-
public function run() {
|
14 |
-
die('hasdf');
|
15 |
-
}
|
16 |
-
|
17 |
-
public function getSubProScanner() :\ICWP_WPSF_Processor_HackProtect_Scanner {
|
18 |
-
return $this->getSubPro( 'scanner' );
|
19 |
-
}
|
20 |
-
|
21 |
-
protected function getSubProMap() :array {
|
22 |
-
return [
|
23 |
-
'scanner' => 'ICWP_WPSF_Processor_HackProtect_Scanner',
|
24 |
-
];
|
25 |
-
}
|
26 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
src/processors/hackprotect_integrity.php
DELETED
@@ -1,136 +0,0 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
use FernleafSystems\Wordpress\Plugin\Shield\Modules\BaseShield\ShieldProcessor;
|
4 |
-
use FernleafSystems\Wordpress\Plugin\Shield\Modules\HackGuard;
|
5 |
-
use FernleafSystems\Wordpress\Services\Services;
|
6 |
-
|
7 |
-
class ICWP_WPSF_Processor_HackProtect_Integrity extends ShieldProcessor {
|
8 |
-
|
9 |
-
public function run() {
|
10 |
-
$this->setupSnapshots();
|
11 |
-
add_action( 'user_register', [ $this, 'snapshotUsers' ] );
|
12 |
-
add_action( 'profile_update', [ $this, 'snapshotUsers' ] );
|
13 |
-
add_action( 'after_password_reset', [ $this, 'snapshotUsers' ] );
|
14 |
-
}
|
15 |
-
|
16 |
-
/**
|
17 |
-
* @return array[] - associative arrays where keys are $this->getStandardUserFields()
|
18 |
-
*/
|
19 |
-
public function getSnapshotUsers() {
|
20 |
-
$aUs = $this->getOptions()->getOpt( 'snapshot_users' );
|
21 |
-
return is_array( $aUs ) ? $aUs : [];
|
22 |
-
}
|
23 |
-
|
24 |
-
/**
|
25 |
-
* @return array
|
26 |
-
*/
|
27 |
-
public function getStandardUserFields() {
|
28 |
-
return [ 'user_login', 'user_email', 'user_pass' ];
|
29 |
-
}
|
30 |
-
|
31 |
-
/**
|
32 |
-
* @return bool
|
33 |
-
*/
|
34 |
-
public function hasSnapshotUsers() {
|
35 |
-
return ( count( $this->getSnapshotUsers() ) > 0 );
|
36 |
-
}
|
37 |
-
|
38 |
-
protected function setupSnapshots() {
|
39 |
-
$this->snapshotUsers();
|
40 |
-
}
|
41 |
-
|
42 |
-
protected function verifyUsers() {
|
43 |
-
$aSnapshot = $this->getSnapshotUsers();
|
44 |
-
$aFieldsToCheck = $this->getStandardUserFields();
|
45 |
-
|
46 |
-
foreach ( Services::WpUsers()->getAllUsers() as $oUser ) {
|
47 |
-
|
48 |
-
if ( !array_key_exists( $oUser->ID, $aSnapshot ) ) {
|
49 |
-
// Unrecognised user ID exists.
|
50 |
-
$this->deleteUserById( $oUser->ID );
|
51 |
-
}
|
52 |
-
else {
|
53 |
-
$aSnapUser = $aSnapshot[ $oUser->ID ];
|
54 |
-
$bAltered = false;
|
55 |
-
foreach ( $aFieldsToCheck as $sField ) {
|
56 |
-
if ( $aSnapUser[ $sField ] != $oUser->get( $sField ) ) { //Field has been altered
|
57 |
-
$bAltered = true;
|
58 |
-
}
|
59 |
-
}
|
60 |
-
|
61 |
-
if ( $bAltered ) {
|
62 |
-
$this->resetUserToSnapshot( $oUser->ID );
|
63 |
-
}
|
64 |
-
}
|
65 |
-
}
|
66 |
-
}
|
67 |
-
|
68 |
-
/**
|
69 |
-
* @param int $nId
|
70 |
-
* @return bool
|
71 |
-
*/
|
72 |
-
public function deleteUserById( $nId ) {
|
73 |
-
$oDb = Services::WpDb();
|
74 |
-
return $oDb->deleteRowsFromTableWhere(
|
75 |
-
$oDb->getTable_Users(),
|
76 |
-
[ 'ID' => $nId ]
|
77 |
-
) > 0;
|
78 |
-
}
|
79 |
-
|
80 |
-
/**
|
81 |
-
* @param int $nId
|
82 |
-
* @return bool
|
83 |
-
*/
|
84 |
-
public function resetUserToSnapshot( $nId ) {
|
85 |
-
$aSnapshot = $this->getSnapshotUsers();
|
86 |
-
$aUser = $aSnapshot[ $nId ];
|
87 |
-
|
88 |
-
$oDb = Services::WpDb();
|
89 |
-
return $oDb->updateRowsFromTableWhere(
|
90 |
-
$oDb->getTable_Users(),
|
91 |
-
$aUser,
|
92 |
-
[ 'ID' => $nId ]
|
93 |
-
) > 0;
|
94 |
-
}
|
95 |
-
|
96 |
-
/**
|
97 |
-
* Guarded: Only ever snapshots when option is enabled.
|
98 |
-
*
|
99 |
-
* @param bool $bUpdate
|
100 |
-
* @return $this
|
101 |
-
*/
|
102 |
-
public function snapshotUsers( $bUpdate = false ) {
|
103 |
-
|
104 |
-
if ( $bUpdate || !$this->hasSnapshotUsers() ) {
|
105 |
-
|
106 |
-
$aUsersToStore = [];
|
107 |
-
$aFields = $this->getStandardUserFields();
|
108 |
-
foreach ( Services::WpUsers()->getAllUsers() as $oUser ) {
|
109 |
-
|
110 |
-
$aUserData = [];
|
111 |
-
foreach ( $aFields as $sField ) {
|
112 |
-
$aUserData[ $sField ] = $oUser->get( $sField );
|
113 |
-
}
|
114 |
-
$aUsersToStore[ $oUser->ID ] = $aUserData;
|
115 |
-
}
|
116 |
-
// store snapshot users
|
117 |
-
}
|
118 |
-
return $this;
|
119 |
-
}
|
120 |
-
|
121 |
-
/**
|
122 |
-
* Cron callback
|
123 |
-
*/
|
124 |
-
public function runCron() {
|
125 |
-
$this->verifyUsers();
|
126 |
-
}
|
127 |
-
|
128 |
-
/**
|
129 |
-
* @return int
|
130 |
-
*/
|
131 |
-
protected function getCronFrequency() {
|
132 |
-
/** @var HackGuard\Options $oOpts */
|
133 |
-
$oOpts = $this->getOptions();
|
134 |
-
return $oOpts->getScanFrequency();
|
135 |
-
}
|
136 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
src/processors/hackprotect_scan_apc.php
DELETED
@@ -1,11 +0,0 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
use FernleafSystems\Wordpress\Plugin\Shield;
|
4 |
-
|
5 |
-
/**
|
6 |
-
* @deprecated 10.1
|
7 |
-
*/
|
8 |
-
class ICWP_WPSF_Processor_HackProtect_Apc extends ICWP_WPSF_Processor_ScanBase {
|
9 |
-
|
10 |
-
const SCAN_SLUG = 'apc';
|
11 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
src/processors/hackprotect_scan_base.php
DELETED
@@ -1,46 +0,0 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
use FernleafSystems\Wordpress\Plugin\Shield;
|
4 |
-
use FernleafSystems\Wordpress\Plugin\Shield\Modules\HackGuard;
|
5 |
-
use FernleafSystems\Wordpress\Services\Services;
|
6 |
-
|
7 |
-
/**
|
8 |
-
* @deprecated 10.1
|
9 |
-
*/
|
10 |
-
abstract class ICWP_WPSF_Processor_ScanBase extends Shield\Modules\BaseShield\ShieldProcessor {
|
11 |
-
|
12 |
-
use Shield\Scans\Common\ScanActionConsumer;
|
13 |
-
|
14 |
-
const SCAN_SLUG = 'base';
|
15 |
-
|
16 |
-
/**
|
17 |
-
* @param int $nDelay
|
18 |
-
* @deprecated 10.1
|
19 |
-
*/
|
20 |
-
public function scheduleOnDemandScan( $nDelay = 3 ) {
|
21 |
-
}
|
22 |
-
|
23 |
-
/**
|
24 |
-
* @return Shield\Scans\Base\BaseScanActionVO|mixed
|
25 |
-
* @deprecated 10.1
|
26 |
-
*/
|
27 |
-
public function getScanActionVO() {
|
28 |
-
return $this->getThisScanCon()->getScanActionVO();
|
29 |
-
}
|
30 |
-
|
31 |
-
/**
|
32 |
-
* @return HackGuard\Scan\Controller\Base|mixed
|
33 |
-
* @deprecated 10.1
|
34 |
-
*/
|
35 |
-
protected function getThisScanCon() {
|
36 |
-
/** @var HackGuard\ModCon $mod */
|
37 |
-
$mod = $this->getMod();
|
38 |
-
return $mod->getScanCon( static::SCAN_SLUG );
|
39 |
-
}
|
40 |
-
|
41 |
-
/**
|
42 |
-
* @deprecated 10.1
|
43 |
-
*/
|
44 |
-
public function hookOnDemandScan() {
|
45 |
-
}
|
46 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
src/processors/hackprotect_scan_mal.php
DELETED
@@ -1,11 +0,0 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
use FernleafSystems\Wordpress\Plugin\Shield;
|
4 |
-
|
5 |
-
/**
|
6 |
-
* @deprecated 10.1
|
7 |
-
*/
|
8 |
-
class ICWP_WPSF_Processor_HackProtect_Mal extends ICWP_WPSF_Processor_ScanBase {
|
9 |
-
|
10 |
-
const SCAN_SLUG = 'mal';
|
11 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
src/processors/hackprotect_scan_ptg.php
DELETED
@@ -1,25 +0,0 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
use FernleafSystems\Wordpress\Plugin\Shield;
|
4 |
-
use FernleafSystems\Wordpress\Plugin\Shield\Modules\HackGuard;
|
5 |
-
use FernleafSystems\Wordpress\Services;
|
6 |
-
|
7 |
-
/**
|
8 |
-
* @deprecated 10.1
|
9 |
-
*/
|
10 |
-
class ICWP_WPSF_Processor_HackProtect_Ptg extends ICWP_WPSF_Processor_ScanBase {
|
11 |
-
|
12 |
-
const SCAN_SLUG = 'ptg';
|
13 |
-
|
14 |
-
/**
|
15 |
-
* @param array $aLinks
|
16 |
-
* @param string $sPluginFile
|
17 |
-
* @return string[]
|
18 |
-
*/
|
19 |
-
public function addActionLinkRefresh( $aLinks, $sPluginFile ) {
|
20 |
-
return $aLinks;
|
21 |
-
}
|
22 |
-
|
23 |
-
public function printPluginReinstallDialogs() {
|
24 |
-
}
|
25 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
src/processors/hackprotect_scan_ufc.php
DELETED
@@ -1,11 +0,0 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
use FernleafSystems\Wordpress\Plugin\Shield;
|
4 |
-
|
5 |
-
/**
|
6 |
-
* @deprecated 10.1
|
7 |
-
*/
|
8 |
-
class ICWP_WPSF_Processor_HackProtect_Ufc extends ICWP_WPSF_Processor_ScanBase {
|
9 |
-
|
10 |
-
const SCAN_SLUG = 'ufc';
|
11 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
src/processors/hackprotect_scan_wcf.php
DELETED
@@ -1,11 +0,0 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
use FernleafSystems\Wordpress\Plugin\Shield;
|
4 |
-
|
5 |
-
/**
|
6 |
-
* @deprecated 10.1
|
7 |
-
*/
|
8 |
-
class ICWP_WPSF_Processor_HackProtect_Wcf extends ICWP_WPSF_Processor_ScanBase {
|
9 |
-
|
10 |
-
const SCAN_SLUG = 'wcf';
|
11 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
src/processors/hackprotect_scan_wpv.php
DELETED
@@ -1,74 +0,0 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
use FernleafSystems\Wordpress\Plugin\Shield;
|
4 |
-
|
5 |
-
/**
|
6 |
-
* @deprecated 10.1
|
7 |
-
*/
|
8 |
-
class ICWP_WPSF_Processor_HackProtect_Wpv extends ICWP_WPSF_Processor_ScanBase {
|
9 |
-
|
10 |
-
const SCAN_SLUG = 'wpv';
|
11 |
-
|
12 |
-
/**
|
13 |
-
* @param bool $bDoAutoUpdate
|
14 |
-
* @param \stdClass|string $mItem
|
15 |
-
* @return bool
|
16 |
-
*/
|
17 |
-
public function autoupdateVulnerablePlugins( $bDoAutoUpdate, $mItem ) {
|
18 |
-
return $bDoAutoUpdate;
|
19 |
-
}
|
20 |
-
|
21 |
-
/**
|
22 |
-
* @param array $aColumns
|
23 |
-
* @return array
|
24 |
-
*/
|
25 |
-
public function fCountColumns( $aColumns ) {
|
26 |
-
return $aColumns;
|
27 |
-
}
|
28 |
-
|
29 |
-
public function addPluginVulnerabilityRows() {
|
30 |
-
}
|
31 |
-
|
32 |
-
public function addVulnerablePluginStatusView() {
|
33 |
-
}
|
34 |
-
|
35 |
-
/**
|
36 |
-
* FILTER
|
37 |
-
* @param array $aViews
|
38 |
-
* @return array
|
39 |
-
*/
|
40 |
-
public function addPluginsStatusViewLink( $aViews ) {
|
41 |
-
return $aViews;
|
42 |
-
}
|
43 |
-
|
44 |
-
/**
|
45 |
-
* FILTER
|
46 |
-
* @param array $aPlugins
|
47 |
-
* @return array
|
48 |
-
*/
|
49 |
-
public function filterPluginsToView( $aPlugins ) {
|
50 |
-
return $aPlugins;
|
51 |
-
}
|
52 |
-
|
53 |
-
/**
|
54 |
-
* @param string $sPluginFile
|
55 |
-
* @param array $aPluginData
|
56 |
-
*/
|
57 |
-
public function attachVulnerabilityWarning( $sPluginFile, $aPluginData ) {
|
58 |
-
}
|
59 |
-
|
60 |
-
/**
|
61 |
-
* @param string $sFile
|
62 |
-
* @return Shield\Scans\Wpv\WpVulnDb\WpVulnVO[]
|
63 |
-
*/
|
64 |
-
private function getPluginVulnerabilities( $sFile ) {
|
65 |
-
return [];
|
66 |
-
}
|
67 |
-
|
68 |
-
/**
|
69 |
-
* @return bool
|
70 |
-
*/
|
71 |
-
private function countVulnerablePlugins() {
|
72 |
-
return 0;
|
73 |
-
}
|
74 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
src/processors/hackprotect_scanner.php
DELETED
@@ -1,89 +0,0 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
use FernleafSystems\Wordpress\Plugin\Shield;
|
4 |
-
use FernleafSystems\Wordpress\Plugin\Shield\Modules\BaseShield;
|
5 |
-
use FernleafSystems\Wordpress\Plugin\Shield\Modules\HackGuard;
|
6 |
-
use FernleafSystems\Wordpress\Services\Services;
|
7 |
-
|
8 |
-
/**
|
9 |
-
* @deprecated 10.1
|
10 |
-
*/
|
11 |
-
class ICWP_WPSF_Processor_HackProtect_Scanner extends BaseShield\ShieldProcessor {
|
12 |
-
|
13 |
-
use Shield\Crons\StandardCron;
|
14 |
-
|
15 |
-
public function run() {
|
16 |
-
}
|
17 |
-
|
18 |
-
public function getSubProcessorPtg() :\ICWP_WPSF_Processor_HackProtect_Ptg {
|
19 |
-
return $this->getSubPro( 'ptg' );
|
20 |
-
}
|
21 |
-
|
22 |
-
protected function getSubProMap() :array {
|
23 |
-
return [];
|
24 |
-
}
|
25 |
-
|
26 |
-
private function handlePostScanCron() {
|
27 |
-
}
|
28 |
-
|
29 |
-
private function runAutoRepair() {
|
30 |
-
}
|
31 |
-
|
32 |
-
public function runHourlyCron() {
|
33 |
-
}
|
34 |
-
|
35 |
-
public function runDailyCron() {
|
36 |
-
}
|
37 |
-
|
38 |
-
public function onWpLoaded() {
|
39 |
-
}
|
40 |
-
|
41 |
-
public function onModuleShutdown() {
|
42 |
-
}
|
43 |
-
|
44 |
-
/**
|
45 |
-
* Cron callback
|
46 |
-
*/
|
47 |
-
public function runCron() {
|
48 |
-
}
|
49 |
-
|
50 |
-
private function cronScan() {
|
51 |
-
}
|
52 |
-
|
53 |
-
public function getReasonsScansCantExecute() :array {
|
54 |
-
return [];
|
55 |
-
}
|
56 |
-
|
57 |
-
public function getCanScansExecute() :bool {
|
58 |
-
}
|
59 |
-
|
60 |
-
protected function getCronFrequency() {
|
61 |
-
/** @var Shield\Modules\HackGuard\Options $opts */
|
62 |
-
$opts = $this->getOptions();
|
63 |
-
return $opts->getScanFrequency();
|
64 |
-
}
|
65 |
-
|
66 |
-
public function getFirstRunTimestamp() :int {
|
67 |
-
$c = Services::Request()->carbon( true );
|
68 |
-
$c->addHours( $c->minute < 40 ? 0 : 1 )
|
69 |
-
->minute( $c->minute < 40 ? 45 : 15 )
|
70 |
-
->second( 0 );
|
71 |
-
|
72 |
-
if ( $this->getCronFrequency() === 1 ) { // If it's a daily scan only, set to 3am by default
|
73 |
-
$hour = (int)apply_filters( $this->getCon()->prefix( 'daily_scan_cron_hour' ), 3 );
|
74 |
-
if ( $hour < 0 || $hour > 23 ) {
|
75 |
-
$hour = 3;
|
76 |
-
}
|
77 |
-
if ( $c->hour >= $hour ) {
|
78 |
-
$c->addDays( 1 );
|
79 |
-
}
|
80 |
-
$c->hour( $hour );
|
81 |
-
}
|
82 |
-
|
83 |
-
return $c->timestamp;
|
84 |
-
}
|
85 |
-
|
86 |
-
protected function getCronName() :string {
|
87 |
-
return $this->getCon()->prefix( $this->getOptions()->getDef( 'cron_all_scans' ) );
|
88 |
-
}
|
89 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
src/processors/headers.php
DELETED
@@ -1,230 +0,0 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
use FernleafSystems\Wordpress\Plugin\Shield\Modules;
|
4 |
-
use FernleafSystems\Wordpress\Plugin\Shield\Modules\Headers;
|
5 |
-
|
6 |
-
/**
|
7 |
-
* Class ICWP_WPSF_Processor_Lockdown
|
8 |
-
* @deprecated 10.1
|
9 |
-
*/
|
10 |
-
class ICWP_WPSF_Processor_Headers extends Modules\BaseShield\ShieldProcessor {
|
11 |
-
|
12 |
-
/**
|
13 |
-
* @var bool
|
14 |
-
*/
|
15 |
-
private $bHeadersPushed;
|
16 |
-
|
17 |
-
/**
|
18 |
-
* @var array
|
19 |
-
*/
|
20 |
-
private $aHeaders;
|
21 |
-
|
22 |
-
public function run() {
|
23 |
-
if ( $this->getPushHeadersEarly() ) {
|
24 |
-
$this->sendHeaders();
|
25 |
-
}
|
26 |
-
else {
|
27 |
-
add_filter( 'wp_headers', [ $this, 'addToHeaders' ], PHP_INT_MAX );
|
28 |
-
add_action( 'send_headers', [ $this, 'sendHeaders' ], PHP_INT_MAX, 0 );
|
29 |
-
}
|
30 |
-
}
|
31 |
-
|
32 |
-
/**
|
33 |
-
* @return bool
|
34 |
-
*/
|
35 |
-
protected function getPushHeadersEarly() {
|
36 |
-
return defined( 'WPCACHEHOME' ); //WP Super Cache
|
37 |
-
}
|
38 |
-
|
39 |
-
/**
|
40 |
-
* Tries to ensure duplicate headers are not sent. Previously sent/supplied headers take priority.
|
41 |
-
* @param array $aCurrentWpHeaders
|
42 |
-
* @return array
|
43 |
-
*/
|
44 |
-
public function addToHeaders( $aCurrentWpHeaders ) {
|
45 |
-
if ( !$this->isHeadersPushed() ) {
|
46 |
-
$aAlreadySentHeaders = array_map(
|
47 |
-
function ( $sHeader ) {
|
48 |
-
return strtolower( trim( $sHeader ) );
|
49 |
-
},
|
50 |
-
( is_array( $aCurrentWpHeaders ) ? array_keys( $aCurrentWpHeaders ) : [] )
|
51 |
-
);
|
52 |
-
foreach ( $this->gatherSecurityHeaders() as $sHeader => $sValue ) {
|
53 |
-
if ( !in_array( strtolower( $sHeader ), $aAlreadySentHeaders ) ) {
|
54 |
-
$aCurrentWpHeaders[ $sHeader ] = $sValue;
|
55 |
-
}
|
56 |
-
}
|
57 |
-
$this->setHeadersPushed( true );
|
58 |
-
}
|
59 |
-
return $aCurrentWpHeaders;
|
60 |
-
}
|
61 |
-
|
62 |
-
/**
|
63 |
-
* Tries to ensure duplicate headers are not sent.
|
64 |
-
*/
|
65 |
-
public function sendHeaders() {
|
66 |
-
if ( !$this->isHeadersPushed() ) {
|
67 |
-
$aAlreadySent = array_map( 'strtolower', array_keys( $this->getAlreadySentHeaders() ) );
|
68 |
-
foreach ( $this->gatherSecurityHeaders() as $sName => $sValue ) {
|
69 |
-
if ( !in_array( strtolower( $sName ), $aAlreadySent ) ) {
|
70 |
-
@header( sprintf( '%s: %s', $sName, $sValue ) );
|
71 |
-
}
|
72 |
-
}
|
73 |
-
$this->setHeadersPushed( true );
|
74 |
-
}
|
75 |
-
}
|
76 |
-
|
77 |
-
/**
|
78 |
-
* @return string[] - array of all previously sent headers. Keys are header names, values are header values.
|
79 |
-
*/
|
80 |
-
private function getAlreadySentHeaders() {
|
81 |
-
$aHeaders = [];
|
82 |
-
|
83 |
-
if ( function_exists( 'headers_list' ) ) {
|
84 |
-
$aSent = headers_list();
|
85 |
-
if ( is_array( $aSent ) ) {
|
86 |
-
foreach ( $aSent as $sHeader ) {
|
87 |
-
if ( strpos( $sHeader, ':' ) ) {
|
88 |
-
list( $sKey, $sValue ) = array_map( 'trim', explode( ':', $sHeader, 2 ) );
|
89 |
-
$aHeaders[ $sKey ] = $sValue;
|
90 |
-
}
|
91 |
-
}
|
92 |
-
}
|
93 |
-
}
|
94 |
-
|
95 |
-
return $aHeaders;
|
96 |
-
}
|
97 |
-
|
98 |
-
/**
|
99 |
-
* @return array|null
|
100 |
-
*/
|
101 |
-
private function getXFrameHeader() {
|
102 |
-
switch ( $this->getOptions()->getOpt( 'x_frame' ) ) {
|
103 |
-
case 'on_sameorigin':
|
104 |
-
$sXFrameOption = 'SAMEORIGIN';
|
105 |
-
break;
|
106 |
-
case 'on_deny':
|
107 |
-
$sXFrameOption = 'DENY';
|
108 |
-
break;
|
109 |
-
default:
|
110 |
-
$sXFrameOption = '';
|
111 |
-
break;
|
112 |
-
}
|
113 |
-
return !empty( $sXFrameOption ) ? [ 'x-frame-options' => $sXFrameOption ] : null;
|
114 |
-
}
|
115 |
-
|
116 |
-
/**
|
117 |
-
* @return array
|
118 |
-
*/
|
119 |
-
private function getXssProtectionHeader() {
|
120 |
-
return [ 'X-XSS-Protection' => '1; mode=block' ];
|
121 |
-
}
|
122 |
-
|
123 |
-
/**
|
124 |
-
* @return array
|
125 |
-
*/
|
126 |
-
private function getContentTypeOptionHeader() {
|
127 |
-
return [ 'X-Content-Type-Options' => 'nosniff' ];
|
128 |
-
}
|
129 |
-
|
130 |
-
/**
|
131 |
-
* @return array|null
|
132 |
-
*/
|
133 |
-
private function getReferrerPolicyHeader() {
|
134 |
-
/** @var Headers\Options $oOpts */
|
135 |
-
$oOpts = $this->getOptions();
|
136 |
-
return [ 'Referrer-Policy' => $oOpts->getReferrerPolicyValue() ];
|
137 |
-
}
|
138 |
-
|
139 |
-
/**
|
140 |
-
* @return array|null
|
141 |
-
*/
|
142 |
-
private function setContentSecurityPolicyHeader() {
|
143 |
-
/** @var Headers\Options $oOpts */
|
144 |
-
$oOpts = $this->getOptions();
|
145 |
-
|
146 |
-
$aDefaultSrcDirectives = [];
|
147 |
-
|
148 |
-
if ( $oOpts->isOpt( 'xcsp_self', 'Y' ) ) {
|
149 |
-
$aDefaultSrcDirectives[] = "'self'";
|
150 |
-
}
|
151 |
-
if ( $oOpts->isOpt( 'xcsp_data', 'Y' ) ) {
|
152 |
-
$aDefaultSrcDirectives[] = "data:";
|
153 |
-
}
|
154 |
-
if ( $oOpts->isOpt( 'xcsp_inline', 'Y' ) ) {
|
155 |
-
$aDefaultSrcDirectives[] = "'unsafe-inline'";
|
156 |
-
}
|
157 |
-
if ( $oOpts->isOpt( 'xcsp_eval', 'Y' ) ) {
|
158 |
-
$aDefaultSrcDirectives[] = "'unsafe-eval'";
|
159 |
-
}
|
160 |
-
if ( $oOpts->isOpt( 'xcsp_https', 'Y' ) ) {
|
161 |
-
$aDefaultSrcDirectives[] = "https:";
|
162 |
-
}
|
163 |
-
|
164 |
-
$aDefaultSrcDirectives[] = implode( " ", $oOpts->getOpt( 'xcsp_hosts', [] ) );
|
165 |
-
|
166 |
-
$aRules = $oOpts->getCspCustomRules();
|
167 |
-
array_unshift( $aRules, sprintf( 'default-src %s;', implode( " ", $aDefaultSrcDirectives ) ) );
|
168 |
-
return [ 'Content-Security-Policy' => implode( ' ', $aRules ) ];
|
169 |
-
}
|
170 |
-
|
171 |
-
/**
|
172 |
-
* @return array
|
173 |
-
*/
|
174 |
-
private function gatherSecurityHeaders() {
|
175 |
-
/** @var Headers\Options $oOpts */
|
176 |
-
$oOpts = $this->getOptions();
|
177 |
-
|
178 |
-
if ( $oOpts->isReferrerPolicyEnabled() ) {
|
179 |
-
$this->addHeader( $this->getReferrerPolicyHeader() );
|
180 |
-
}
|
181 |
-
if ( $oOpts->isEnabledXFrame() ) {
|
182 |
-
$this->addHeader( $this->getXFrameHeader() );
|
183 |
-
}
|
184 |
-
if ( $oOpts->isEnabledXssProtection() ) {
|
185 |
-
$this->addHeader( $this->getXssProtectionHeader() );
|
186 |
-
}
|
187 |
-
if ( $oOpts->isEnabledContentTypeHeader() ) {
|
188 |
-
$this->addHeader( $this->getContentTypeOptionHeader() );
|
189 |
-
}
|
190 |
-
if ( $oOpts->isEnabledContentSecurityPolicy() ) {
|
191 |
-
$this->addHeader( $this->setContentSecurityPolicyHeader() );
|
192 |
-
}
|
193 |
-
return $this->getHeaders();
|
194 |
-
}
|
195 |
-
|
196 |
-
/**
|
197 |
-
* @return array
|
198 |
-
*/
|
199 |
-
private function getHeaders() {
|
200 |
-
if ( !isset( $this->aHeaders ) || !is_array( $this->aHeaders ) ) {
|
201 |
-
$this->aHeaders = [];
|
202 |
-
}
|
203 |
-
return array_unique( $this->aHeaders );
|
204 |
-
}
|
205 |
-
|
206 |
-
/**
|
207 |
-
* @param array $aHeader
|
208 |
-
*/
|
209 |
-
private function addHeader( $aHeader ) {
|
210 |
-
if ( !empty( $aHeader ) && is_array( $aHeader ) ) {
|
211 |
-
$this->aHeaders = array_merge( $this->getHeaders(), $aHeader );
|
212 |
-
}
|
213 |
-
}
|
214 |
-
|
215 |
-
/**
|
216 |
-
* @return bool
|
217 |
-
*/
|
218 |
-
private function isHeadersPushed() {
|
219 |
-
return (bool)$this->bHeadersPushed;
|
220 |
-
}
|
221 |
-
|
222 |
-
/**
|
223 |
-
* @param bool $bHeadersPushed
|
224 |
-
* @return $this
|
225 |
-
*/
|
226 |
-
private function setHeadersPushed( $bHeadersPushed ) {
|
227 |
-
$this->bHeadersPushed = $bHeadersPushed;
|
228 |
-
return $this;
|
229 |
-
}
|
230 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
src/processors/lockdown.php
DELETED
@@ -1,136 +0,0 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
use FernleafSystems\Wordpress\Plugin\Shield\Modules;
|
4 |
-
use FernleafSystems\Wordpress\Plugin\Shield\Modules\Lockdown;
|
5 |
-
use FernleafSystems\Wordpress\Services\Services;
|
6 |
-
|
7 |
-
/**
|
8 |
-
* @deprecated 10.1
|
9 |
-
*/
|
10 |
-
class ICWP_WPSF_Processor_Lockdown extends Modules\BaseShield\ShieldProcessor {
|
11 |
-
|
12 |
-
public function run() {
|
13 |
-
/** @var Lockdown\Options $opts */
|
14 |
-
$opts = $this->getOptions();
|
15 |
-
|
16 |
-
if ( $opts->isOptFileEditingDisabled() ) {
|
17 |
-
$this->blockFileEditing();
|
18 |
-
}
|
19 |
-
|
20 |
-
if ( $opts->isOpt( 'force_ssl_admin', 'Y' ) && function_exists( 'force_ssl_admin' ) ) {
|
21 |
-
if ( !defined( 'FORCE_SSL_ADMIN' ) ) {
|
22 |
-
define( 'FORCE_SSL_ADMIN', true );
|
23 |
-
}
|
24 |
-
force_ssl_admin( true );
|
25 |
-
}
|
26 |
-
|
27 |
-
if ( $opts->isOpt( 'hide_wordpress_generator_tag', 'Y' ) ) {
|
28 |
-
remove_action( 'wp_head', 'wp_generator' );
|
29 |
-
}
|
30 |
-
|
31 |
-
if ( $opts->isOpt( 'clean_wp_rubbish', 'Y' ) ) {
|
32 |
-
( new Lockdown\Lib\CleanRubbish() )
|
33 |
-
->setMod( $this->getMod() )
|
34 |
-
->execute();
|
35 |
-
}
|
36 |
-
|
37 |
-
if ( $opts->isXmlrpcDisabled() ) {
|
38 |
-
add_filter( 'xmlrpc_enabled', [ $this, 'disableXmlrpc' ], 1000, 0 );
|
39 |
-
add_filter( 'xmlrpc_methods', [ $this, 'disableXmlrpc' ], 1000, 0 );
|
40 |
-
}
|
41 |
-
}
|
42 |
-
|
43 |
-
private function blockFileEditing() {
|
44 |
-
if ( !defined( 'DISALLOW_FILE_EDIT' ) ) {
|
45 |
-
define( 'DISALLOW_FILE_EDIT', true );
|
46 |
-
}
|
47 |
-
|
48 |
-
add_filter( 'user_has_cap',
|
49 |
-
/**
|
50 |
-
* @param array $aAllCaps
|
51 |
-
* @param array $cap
|
52 |
-
* @param array $aArgs
|
53 |
-
* @return array
|
54 |
-
*/
|
55 |
-
function ( $aAllCaps, $cap, $aArgs ) {
|
56 |
-
$sRequestedCapability = $aArgs[ 0 ];
|
57 |
-
if ( in_array( $sRequestedCapability, [ 'edit_themes', 'edit_plugins', 'edit_files' ] ) ) {
|
58 |
-
$aAllCaps[ $sRequestedCapability ] = false;
|
59 |
-
}
|
60 |
-
return $aAllCaps;
|
61 |
-
},
|
62 |
-
PHP_INT_MAX, 3
|
63 |
-
);
|
64 |
-
}
|
65 |
-
|
66 |
-
public function onWpInit() {
|
67 |
-
/** @var Lockdown\Options $opts */
|
68 |
-
$opts = $this->getOptions();
|
69 |
-
|
70 |
-
if ( !Services::WpUsers()->isUserLoggedIn() ) {
|
71 |
-
$this->interceptCanonicalRedirects();
|
72 |
-
if ( $opts->isRestApiAnonymousAccessDisabled() ) {
|
73 |
-
add_filter( 'rest_authentication_errors', [ $this, 'disableAnonymousRestApi' ], 99 );
|
74 |
-
}
|
75 |
-
}
|
76 |
-
}
|
77 |
-
|
78 |
-
/**
|
79 |
-
* @return array|false
|
80 |
-
*/
|
81 |
-
public function disableXmlrpc() {
|
82 |
-
$this->getCon()->fireEvent( 'block_xml' );
|
83 |
-
return ( current_filter() == 'xmlrpc_enabled' ) ? false : [];
|
84 |
-
}
|
85 |
-
|
86 |
-
/**
|
87 |
-
* @uses wp_die()
|
88 |
-
*/
|
89 |
-
private function interceptCanonicalRedirects() {
|
90 |
-
|
91 |
-
if ( $this->getOptions()->isOpt( 'block_author_discovery', 'Y' ) ) {
|
92 |
-
$sAuthor = Services::Request()->query( 'author', '' );
|
93 |
-
if ( !empty( $sAuthor ) ) {
|
94 |
-
Services::WpGeneral()->wpDie( sprintf(
|
95 |
-
__( 'The "author" query parameter has been blocked by %s to protect against user login name fishing.', 'wp-simple-firewall' )
|
96 |
-
.sprintf( '<br /><a href="%s" target="_blank">%s</a>',
|
97 |
-
'https://shsec.io/7l',
|
98 |
-
__( 'Learn More.', 'wp-simple-firewall' )
|
99 |
-
),
|
100 |
-
$this->getCon()->getHumanName()
|
101 |
-
) );
|
102 |
-
}
|
103 |
-
}
|
104 |
-
}
|
105 |
-
|
106 |
-
/**
|
107 |
-
* Understand that if $mCurrentStatus is null, no check has been made. If true, something has
|
108 |
-
* authenticated the request, and if WP_Error, then an error is already present
|
109 |
-
* @param WP_Error|true|null $mStatus
|
110 |
-
* @return WP_Error
|
111 |
-
*/
|
112 |
-
public function disableAnonymousRestApi( $mStatus ) {
|
113 |
-
/** @var \ICWP_WPSF_FeatureHandler_Lockdown $mod */
|
114 |
-
$mod = $this->getMod();
|
115 |
-
$oWpRest = Services::Rest();
|
116 |
-
|
117 |
-
$sNamespace = $oWpRest->getNamespace();
|
118 |
-
if ( !empty( $sNamespace ) && $mStatus !== true && !is_wp_error( $mStatus )
|
119 |
-
&& !$mod->isPermittedAnonRestApiNamespace( $sNamespace ) ) {
|
120 |
-
|
121 |
-
$mStatus = new \WP_Error(
|
122 |
-
'shield_block_anon_restapi',
|
123 |
-
sprintf( __( 'Anonymous access to the WordPress Rest API has been restricted by %s.', 'wp-simple-firewall' ), $this->getCon()
|
124 |
-
->getHumanName() ),
|
125 |
-
[ 'status' => rest_authorization_required_code() ] );
|
126 |
-
|
127 |
-
$this->getCon()
|
128 |
-
->fireEvent(
|
129 |
-
'block_anonymous_restapi',
|
130 |
-
[ 'audit' => [ 'namespace' => $sNamespace ] ]
|
131 |
-
);
|
132 |
-
}
|
133 |
-
|
134 |
-
return $mStatus;
|
135 |
-
}
|
136 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
src/processors/login_protect.php
DELETED
@@ -1,52 +0,0 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
use FernleafSystems\Wordpress\Plugin\Shield\Modules;
|
4 |
-
use FernleafSystems\Wordpress\Plugin\Shield\Modules\LoginGuard;
|
5 |
-
use FernleafSystems\Wordpress\Plugin\Shield\Modules\LoginGuard\Lib\AntiBot;
|
6 |
-
use FernleafSystems\Wordpress\Services\Services;
|
7 |
-
|
8 |
-
/**
|
9 |
-
* @deprecated 10.1
|
10 |
-
*/
|
11 |
-
class ICWP_WPSF_Processor_LoginProtect extends Modules\BaseShield\ShieldProcessor {
|
12 |
-
|
13 |
-
public function run() {
|
14 |
-
/** @var LoginGuard\ModCon $mod */
|
15 |
-
$mod = $this->getMod();
|
16 |
-
|
17 |
-
// XML-RPC Compatibility
|
18 |
-
if ( Services::WpGeneral()->isXmlrpc() && $mod->isXmlrpcBypass() ) {
|
19 |
-
return;
|
20 |
-
}
|
21 |
-
|
22 |
-
// So we can allow access to the login pages if IP is whitelisted
|
23 |
-
/** @var LoginGuard\Options $opts */
|
24 |
-
$opts = $this->getOptions();
|
25 |
-
if ( !empty( $opts->getCustomLoginPath() ) ) {
|
26 |
-
$this->getSubPro( 'rename' )->execute();
|
27 |
-
}
|
28 |
-
|
29 |
-
if ( !$mod->isVisitorWhitelisted() ) {
|
30 |
-
( new AntiBot\AntibotSetup() )->setMod( $mod );
|
31 |
-
$mod->getLoginIntentController()->run();
|
32 |
-
}
|
33 |
-
}
|
34 |
-
|
35 |
-
/**
|
36 |
-
* Override the original collection to then add plugin statistics to the mix
|
37 |
-
* @param $aData
|
38 |
-
* @return array
|
39 |
-
*/
|
40 |
-
public function tracking_DataCollect( $aData ) {
|
41 |
-
return $aData;
|
42 |
-
}
|
43 |
-
|
44 |
-
/**
|
45 |
-
* @return array
|
46 |
-
*/
|
47 |
-
protected function getSubProMap() :array {
|
48 |
-
return [
|
49 |
-
'rename' => 'ICWP_WPSF_Processor_LoginProtect_WpLogin',
|
50 |
-
];
|
51 |
-
}
|
52 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
src/processors/loginprotect_wplogin.php
DELETED
@@ -1,236 +0,0 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
use FernleafSystems\Wordpress\Plugin\Shield\Modules;
|
4 |
-
use FernleafSystems\Wordpress\Plugin\Shield\Modules\LoginGuard;
|
5 |
-
use FernleafSystems\Wordpress\Services\Services;
|
6 |
-
|
7 |
-
/**
|
8 |
-
* @deprecated 10.1
|
9 |
-
*/
|
10 |
-
class ICWP_WPSF_Processor_LoginProtect_WpLogin extends Modules\BaseShield\ShieldProcessor {
|
11 |
-
|
12 |
-
public function onWpInit() {
|
13 |
-
/** @var LoginGuard\ModCon $mod */
|
14 |
-
$mod = $this->getMod();
|
15 |
-
|
16 |
-
if ( $this->checkForPluginConflict() || $this->checkForUnsupportedConfiguration() ) {
|
17 |
-
return;
|
18 |
-
}
|
19 |
-
if ( Services::WpGeneral()->isLoginUrl() &&
|
20 |
-
( $mod->isVisitorWhitelisted() || Services::WpUsers()->isUserLoggedIn() ) ) {
|
21 |
-
return;
|
22 |
-
}
|
23 |
-
if ( is_admin() && $mod->isVisitorWhitelisted() && !Services::WpUsers()->isUserLoggedIn() ) {
|
24 |
-
return;
|
25 |
-
}
|
26 |
-
|
27 |
-
$this->doBlockPossibleWpLoginLoad();
|
28 |
-
|
29 |
-
// Loads the wp-login.php if the correct URL is loaded
|
30 |
-
add_action( 'wp_loaded', [ $this, 'aLoadWpLogin' ] );
|
31 |
-
|
32 |
-
// Shouldn't be necessary, but in-case something else includes the wp-login.php, we block that too.
|
33 |
-
add_action( 'login_init', [ $this, 'aLoginFormAction' ], 0 );
|
34 |
-
|
35 |
-
// ensure that wp-login.php is never used in site urls or redirects
|
36 |
-
add_filter( 'site_url', [ $this, 'fCheckForLoginPhp' ], 20, 1 );
|
37 |
-
add_filter( 'network_site_url', [ $this, 'fCheckForLoginPhp' ], 20, 1 );
|
38 |
-
add_filter( 'wp_redirect', [ $this, 'fCheckForLoginPhp' ], 20, 1 );
|
39 |
-
if ( !Services::WpUsers()->isUserLoggedIn() ) {
|
40 |
-
add_filter( 'wp_redirect', [ $this, 'fProtectUnauthorizedLoginRedirect' ], 50, 1 );
|
41 |
-
}
|
42 |
-
add_filter( 'register_url', [ $this, 'blockRegisterUrlRedirect' ], 20, 1 );
|
43 |
-
|
44 |
-
add_filter( 'et_anticipate_exceptions', [ $this, 'fAddToEtMaintenanceExceptions' ] );
|
45 |
-
}
|
46 |
-
|
47 |
-
/**
|
48 |
-
* @return bool - true if conflict exists
|
49 |
-
*/
|
50 |
-
protected function checkForPluginConflict() {
|
51 |
-
/** @var LoginGuard\ModCon $mod */
|
52 |
-
$mod = $this->getMod();
|
53 |
-
/** @var LoginGuard\Options $opts */
|
54 |
-
$opts = $this->getOptions();
|
55 |
-
|
56 |
-
$sMessage = '';
|
57 |
-
$bConflicted = false;
|
58 |
-
|
59 |
-
$path = $opts->getCustomLoginPath();
|
60 |
-
|
61 |
-
$WP = Services::WpGeneral();
|
62 |
-
if ( $WP->isMultisite() ) {
|
63 |
-
$sMessage = __( 'Your login URL is unchanged because the Rename WP Login feature is not currently supported on WPMS.', 'wp-simple-firewall' );
|
64 |
-
$bConflicted = true;
|
65 |
-
}
|
66 |
-
elseif ( class_exists( 'Rename_WP_Login' ) ) {
|
67 |
-
$sMessage = sprintf( __( 'Can not use the Rename WP Login feature because you have the "%s" plugin installed and it is active.', 'wp-simple-firewall' ), 'Rename WP Login' );
|
68 |
-
$bConflicted = true;
|
69 |
-
}
|
70 |
-
elseif ( class_exists( 'Theme_My_Login' ) ) {
|
71 |
-
$sMessage = sprintf( __( 'Can not use the Rename WP Login feature because you have the "%s" plugin installed and it is active.', 'wp-simple-firewall' ), 'Theme My Login' );
|
72 |
-
$bConflicted = true;
|
73 |
-
}
|
74 |
-
elseif ( !$WP->isPermalinksEnabled() ) {
|
75 |
-
$sMessage = sprintf( __( 'Can not use the Rename WP Login feature because you have not enabled %s.', 'wp-simple-firewall' ), __( 'Permalinks' ) );
|
76 |
-
$bConflicted = true;
|
77 |
-
}
|
78 |
-
elseif ( $WP->isPermalinksEnabled() && ( $WP->getDoesWpSlugExist( $path ) || in_array( $path, $WP->getAutoRedirectLocations() ) ) ) {
|
79 |
-
$sMessage = sprintf( __( 'Can not use the Rename WP Login feature because you have chosen a path ("%s") that is reserved on your WordPress site.', 'wp-simple-firewall' ), $path );
|
80 |
-
$bConflicted = true;
|
81 |
-
}
|
82 |
-
|
83 |
-
if ( $bConflicted ) {
|
84 |
-
$sNoticeMessage = sprintf( '<strong>%s</strong>: %s',
|
85 |
-
__( 'Warning', 'wp-simple-firewall' ),
|
86 |
-
$sMessage
|
87 |
-
);
|
88 |
-
$mod->setFlashAdminNotice( $sNoticeMessage, true );
|
89 |
-
}
|
90 |
-
|
91 |
-
return $bConflicted;
|
92 |
-
}
|
93 |
-
|
94 |
-
/**
|
95 |
-
* @return bool
|
96 |
-
*/
|
97 |
-
private function checkForUnsupportedConfiguration() {
|
98 |
-
/** @var LoginGuard\ModCon $mod */
|
99 |
-
$mod = $this->getMod();
|
100 |
-
$path = Services::Request()->getPath();
|
101 |
-
if ( empty( $path ) ) {
|
102 |
-
|
103 |
-
$sNoticeMessage = sprintf(
|
104 |
-
'<strong>%s</strong>: %s',
|
105 |
-
__( 'Warning', 'wp-simple-firewall' ),
|
106 |
-
__( 'Your login URL is unchanged because your current hosting/PHP configuration cannot parse the necessary information.', 'wp-simple-firewall' )
|
107 |
-
);
|
108 |
-
$mod->setFlashAdminNotice( $sNoticeMessage, true );
|
109 |
-
return true;
|
110 |
-
}
|
111 |
-
return false;
|
112 |
-
}
|
113 |
-
|
114 |
-
public function doBlockPossibleWpLoginLoad() {
|
115 |
-
|
116 |
-
// To begin, we block if it's an access to the admin area and the user isn't logged in (and it's not ajax)
|
117 |
-
$bDoBlock = is_admin()
|
118 |
-
&& !Services::WpGeneral()->isAjax() && !Services::WpUsers()->isUserLoggedIn();
|
119 |
-
|
120 |
-
// Next block option is where it's a direct attempt to access the old login URL
|
121 |
-
if ( !$bDoBlock ) {
|
122 |
-
$sPath = trim( Services::Request()->getPath(), '/' );
|
123 |
-
$aPossiblePaths = [
|
124 |
-
trim( home_url( 'wp-login.php', 'relative' ), '/' ),
|
125 |
-
trim( home_url( 'wp-signup.php', 'relative' ), '/' ),
|
126 |
-
trim( site_url( 'wp-signup.php', 'relative' ), '/' ),
|
127 |
-
// trim( site_url( 'wp-login.php', 'relative' ), '/' ), our own filters in run() scuttle us here so we have to build it manually
|
128 |
-
trim( rtrim( site_url( '', 'relative' ), '/' ).'/wp-login.php', '/' ),
|
129 |
-
trim( home_url( 'login', 'relative' ), '/' ),
|
130 |
-
trim( site_url( 'login', 'relative' ), '/' )
|
131 |
-
];
|
132 |
-
$bDoBlock = !empty( $sPath )
|
133 |
-
&& ( in_array( $sPath, $aPossiblePaths ) || preg_match( '/wp-login\.php/i', $sPath ) );
|
134 |
-
}
|
135 |
-
|
136 |
-
if ( $bDoBlock ) {
|
137 |
-
$this->doWpLoginFailedRedirect404();
|
138 |
-
}
|
139 |
-
}
|
140 |
-
|
141 |
-
/**
|
142 |
-
* @param string $sLocation
|
143 |
-
* @return string
|
144 |
-
*/
|
145 |
-
public function fCheckForLoginPhp( $sLocation ) {
|
146 |
-
/** @var LoginGuard\Options $opts */
|
147 |
-
$opts = $this->getOptions();
|
148 |
-
|
149 |
-
$sRedirectPath = parse_url( $sLocation, PHP_URL_PATH );
|
150 |
-
if ( strpos( $sRedirectPath, 'wp-login.php' ) !== false ) {
|
151 |
-
|
152 |
-
$sLoginUrl = home_url( $opts->getCustomLoginPath() );
|
153 |
-
$aQueryArgs = explode( '?', $sLocation );
|
154 |
-
if ( !empty( $aQueryArgs[ 1 ] ) ) {
|
155 |
-
parse_str( $aQueryArgs[ 1 ], $aNewQueryArgs );
|
156 |
-
$sLoginUrl = add_query_arg( $aNewQueryArgs, $sLoginUrl );
|
157 |
-
}
|
158 |
-
return $sLoginUrl;
|
159 |
-
}
|
160 |
-
return $sLocation;
|
161 |
-
}
|
162 |
-
|
163 |
-
/**
|
164 |
-
* @param string $sLocation
|
165 |
-
* @return string
|
166 |
-
*/
|
167 |
-
public function fProtectUnauthorizedLoginRedirect( $sLocation ) {
|
168 |
-
/** @var LoginGuard\Options $opts */
|
169 |
-
$opts = $this->getOptions();
|
170 |
-
|
171 |
-
if ( !Services::WpGeneral()->isLoginUrl() ) {
|
172 |
-
$sRedirectPath = trim( parse_url( $sLocation, PHP_URL_PATH ), '/' );
|
173 |
-
$bRedirectIsHiddenUrl = ( $sRedirectPath == $opts->getCustomLoginPath() );
|
174 |
-
if ( $bRedirectIsHiddenUrl && !Services::WpUsers()->isUserLoggedIn() ) {
|
175 |
-
$this->doWpLoginFailedRedirect404();
|
176 |
-
}
|
177 |
-
}
|
178 |
-
return $sLocation;
|
179 |
-
}
|
180 |
-
|
181 |
-
/**
|
182 |
-
* @param string $sUrl
|
183 |
-
* @return string
|
184 |
-
*/
|
185 |
-
public function blockRegisterUrlRedirect( $sUrl ) {
|
186 |
-
$sPath = Services::Request()->getPath();
|
187 |
-
if ( strpos( $sPath, 'wp-register.php' ) ) {
|
188 |
-
$this->doWpLoginFailedRedirect404();
|
189 |
-
die();
|
190 |
-
}
|
191 |
-
return $sUrl;
|
192 |
-
}
|
193 |
-
|
194 |
-
public function aLoadWpLogin() {
|
195 |
-
if ( Services::WpGeneral()->isLoginUrl() ) {
|
196 |
-
@require_once( ABSPATH.'wp-login.php' );
|
197 |
-
die();
|
198 |
-
}
|
199 |
-
}
|
200 |
-
|
201 |
-
public function aLoginFormAction() {
|
202 |
-
if ( !Services::WpGeneral()->isLoginUrl() ) {
|
203 |
-
$this->doWpLoginFailedRedirect404();
|
204 |
-
die();
|
205 |
-
}
|
206 |
-
}
|
207 |
-
|
208 |
-
/**
|
209 |
-
* Add the custom login URL to the Elegant Themes Maintenance Mode plugin URL exceptions list
|
210 |
-
* @param array $aUrlExceptions
|
211 |
-
* @return array
|
212 |
-
*/
|
213 |
-
public function fAddToEtMaintenanceExceptions( $aUrlExceptions ) {
|
214 |
-
/** @var LoginGuard\Options $opts */
|
215 |
-
$opts = $this->getOptions();
|
216 |
-
$aUrlExceptions[] = $opts->getCustomLoginPath();
|
217 |
-
return $aUrlExceptions;
|
218 |
-
}
|
219 |
-
|
220 |
-
/**
|
221 |
-
* Will by default send a 404 response screen. Has a filter to specify redirect URL.
|
222 |
-
*/
|
223 |
-
protected function doWpLoginFailedRedirect404() {
|
224 |
-
$this->getCon()->fireEvent( 'hide_login_url' );
|
225 |
-
|
226 |
-
$sRedirectUrl = apply_filters( 'icwp_shield_renamewplogin_redirect_url', false );
|
227 |
-
if ( !empty( $sRedirectUrl ) ) {
|
228 |
-
$sRedirectUrl = esc_url( $sRedirectUrl );
|
229 |
-
if ( @parse_url( $sRedirectUrl ) !== false ) {
|
230 |
-
Services::Response()->redirect( $sRedirectUrl, [], false );
|
231 |
-
}
|
232 |
-
}
|
233 |
-
|
234 |
-
Services::Response()->sendApache404( '', Services::WpGeneral()->getHomeUrl() );
|
235 |
-
}
|
236 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
src/processors/plugin.php
DELETED
@@ -1,68 +0,0 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
use FernleafSystems\Wordpress\Plugin\Shield;
|
4 |
-
use FernleafSystems\Wordpress\Plugin\Shield\Modules;
|
5 |
-
|
6 |
-
/**
|
7 |
-
* @deprecated 10.1
|
8 |
-
*/
|
9 |
-
class ICWP_WPSF_Processor_Plugin extends Modules\BaseShield\ShieldProcessor {
|
10 |
-
|
11 |
-
public function run() {
|
12 |
-
}
|
13 |
-
|
14 |
-
public function printDashboardWidget() {
|
15 |
-
}
|
16 |
-
|
17 |
-
/**
|
18 |
-
* @return \ICWP_WPSF_Processor_Plugin_Tracking
|
19 |
-
*/
|
20 |
-
protected function getSubProTracking() {
|
21 |
-
return $this->getSubPro( 'tracking' );
|
22 |
-
}
|
23 |
-
|
24 |
-
/**
|
25 |
-
* @return array
|
26 |
-
*/
|
27 |
-
protected function getSubProMap() :array {
|
28 |
-
return [
|
29 |
-
'tracking' => 'ICWP_WPSF_Processor_Plugin_Tracking',
|
30 |
-
];
|
31 |
-
}
|
32 |
-
|
33 |
-
public function printAdminFooterItems() {
|
34 |
-
}
|
35 |
-
|
36 |
-
/**
|
37 |
-
* Sets this plugin to be the first loaded of all the plugins.
|
38 |
-
*/
|
39 |
-
private function printToastTemplate() {
|
40 |
-
}
|
41 |
-
|
42 |
-
private function printPluginDeactivateSurvey() {
|
43 |
-
}
|
44 |
-
|
45 |
-
/**
|
46 |
-
* @deprecated 10.1
|
47 |
-
*/
|
48 |
-
public function dumpTrackingData() {
|
49 |
-
}
|
50 |
-
|
51 |
-
public function runDailyCron() {
|
52 |
-
}
|
53 |
-
|
54 |
-
/**
|
55 |
-
* Lets you remove certain plugin conflicts that might interfere with this plugin
|
56 |
-
*/
|
57 |
-
protected function removePluginConflicts() {
|
58 |
-
}
|
59 |
-
|
60 |
-
/**
|
61 |
-
* Override the original collection to then add plugin statistics to the mix
|
62 |
-
* @param array $aData
|
63 |
-
* @return array
|
64 |
-
*/
|
65 |
-
public function tracking_DataCollect( $aData ) {
|
66 |
-
return parent::tracking_DataCollect( $aData );
|
67 |
-
}
|
68 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
src/processors/plugin_tracking.php
DELETED
@@ -1,29 +0,0 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
use FernleafSystems\Wordpress\Plugin\Shield;
|
4 |
-
|
5 |
-
/**
|
6 |
-
* @deprecated 10.1
|
7 |
-
*/
|
8 |
-
class ICWP_WPSF_Processor_Plugin_Tracking extends Shield\Modules\BaseShield\ShieldProcessor {
|
9 |
-
|
10 |
-
public function runDailyCron() {
|
11 |
-
}
|
12 |
-
|
13 |
-
private function sendTrackingData() {
|
14 |
-
}
|
15 |
-
|
16 |
-
/**
|
17 |
-
* @return array
|
18 |
-
*/
|
19 |
-
public function collectTrackingData() {
|
20 |
-
return [];
|
21 |
-
}
|
22 |
-
|
23 |
-
/**
|
24 |
-
* @return array
|
25 |
-
*/
|
26 |
-
protected function getBaseTrackingData() {
|
27 |
-
return [];
|
28 |
-
}
|
29 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
src/processors/sessions.php
DELETED
@@ -1,191 +0,0 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
use FernleafSystems\Wordpress\Plugin\Shield\Databases\Session;
|
4 |
-
use FernleafSystems\Wordpress\Plugin\Shield\Modules;
|
5 |
-
use FernleafSystems\Wordpress\Plugin\Shield\Modules\Sessions;
|
6 |
-
use FernleafSystems\Wordpress\Services\Services;
|
7 |
-
|
8 |
-
/**
|
9 |
-
* @deprecated 10.1
|
10 |
-
*/
|
11 |
-
class ICWP_WPSF_Processor_Sessions extends Modules\BaseShield\ShieldProcessor {
|
12 |
-
|
13 |
-
/**
|
14 |
-
* @var Session\EntryVO
|
15 |
-
*/
|
16 |
-
private $oCurrent;
|
17 |
-
|
18 |
-
public function run() {
|
19 |
-
if ( !Services::WpUsers()->isProfilePage() ) { // only on logout
|
20 |
-
add_action( 'clear_auth_cookie', function () {
|
21 |
-
$this->terminateCurrentSession();
|
22 |
-
}, 0 );
|
23 |
-
}
|
24 |
-
add_filter( 'login_message', [ $this, 'printLinkToAdmin' ] );
|
25 |
-
}
|
26 |
-
|
27 |
-
/**
|
28 |
-
* @param string $sUsername
|
29 |
-
* @param \WP_User $user
|
30 |
-
*/
|
31 |
-
public function onWpLogin( $sUsername, $user ) {
|
32 |
-
if ( !$user instanceof \WP_User ) {
|
33 |
-
$user = Services::WpUsers()->getUserByUsername( $sUsername );
|
34 |
-
}
|
35 |
-
$this->activateUserSession( $user );
|
36 |
-
}
|
37 |
-
|
38 |
-
/**
|
39 |
-
* @param string $sCookie
|
40 |
-
* @param int $nExpire
|
41 |
-
* @param int $nExpiration
|
42 |
-
* @param int $nUserId
|
43 |
-
*/
|
44 |
-
public function onWpSetLoggedInCookie( $sCookie, $nExpire, $nExpiration, $nUserId ) {
|
45 |
-
$this->activateUserSession( Services::WpUsers()->getUserById( $nUserId ) );
|
46 |
-
}
|
47 |
-
|
48 |
-
public function onWpLoaded() {
|
49 |
-
if ( Services::WpUsers()->isUserLoggedIn() && !Services::Rest()->isRest() ) {
|
50 |
-
$this->autoAddSession();
|
51 |
-
}
|
52 |
-
}
|
53 |
-
|
54 |
-
public function onModuleShutdown() {
|
55 |
-
/** @var Sessions\ModCon $mod */
|
56 |
-
$mod = $this->getMod();
|
57 |
-
|
58 |
-
if ( !Services::Rest()->isRest() && !$this->getCon()->plugin_deleting ) {
|
59 |
-
$oSession = $this->getCurrentSession();
|
60 |
-
if ( $oSession instanceof Session\EntryVO ) {
|
61 |
-
/** @var Session\Update $oUpd */
|
62 |
-
$oUpd = $mod->getDbHandler_Sessions()->getQueryUpdater();
|
63 |
-
$oUpd->updateLastActivity( $this->getCurrentSession() );
|
64 |
-
}
|
65 |
-
}
|
66 |
-
|
67 |
-
parent::onModuleShutdown();
|
68 |
-
}
|
69 |
-
|
70 |
-
private function autoAddSession() {
|
71 |
-
/** @var Sessions\ModCon $mod */
|
72 |
-
$mod = $this->getMod();
|
73 |
-
if ( !$mod->getSession() && $mod->isAutoAddSessions() ) {
|
74 |
-
$this->queryCreateSession(
|
75 |
-
$this->getCon()->getSessionId( true ),
|
76 |
-
Services::WpUsers()->getCurrentWpUsername()
|
77 |
-
);
|
78 |
-
}
|
79 |
-
}
|
80 |
-
|
81 |
-
/**
|
82 |
-
* Only show Go To Admin link for Authors and above.
|
83 |
-
* @param string $sMessage
|
84 |
-
* @return string
|
85 |
-
* @throws \Exception
|
86 |
-
*/
|
87 |
-
public function printLinkToAdmin( $sMessage = '' ) {
|
88 |
-
/** @var Sessions\ModCon $mod */
|
89 |
-
$mod = $this->getMod();
|
90 |
-
$user = Services::WpUsers()->getCurrentWpUser();
|
91 |
-
|
92 |
-
if ( in_array( Services::Request()->query( 'action' ), [ '', 'login' ] )
|
93 |
-
&& ( $user instanceof \WP_User ) && $mod->getSessionCon()->hasSession() ) {
|
94 |
-
$sMessage .= sprintf( '<p class="message">%s<br />%s</p>',
|
95 |
-
__( "You're already logged-in.", 'wp-simple-firewall' )
|
96 |
-
.sprintf( ' <span style="white-space: nowrap">(%s)</span>', $user->user_login ),
|
97 |
-
( $user->user_level >= 2 ) ? sprintf( '<a href="%s">%s</a>',
|
98 |
-
Services::WpGeneral()->getAdminUrl(),
|
99 |
-
__( "Go To Admin", 'wp-simple-firewall' ).' →' ) : '' );
|
100 |
-
}
|
101 |
-
return $sMessage;
|
102 |
-
}
|
103 |
-
|
104 |
-
/**
|
105 |
-
* @param \WP_User $oUser
|
106 |
-
* @return bool
|
107 |
-
*/
|
108 |
-
private function activateUserSession( $oUser ) {
|
109 |
-
if ( !$this->isLoginCaptured() && $oUser instanceof \WP_User ) {
|
110 |
-
$this->setLoginCaptured();
|
111 |
-
// If they have a currently active session, terminate it (i.e. we replace it)
|
112 |
-
$this->terminateCurrentSession();
|
113 |
-
$this->queryCreateSession( $this->getCon()->getSessionId( true ), $oUser->user_login );
|
114 |
-
}
|
115 |
-
return true;
|
116 |
-
}
|
117 |
-
|
118 |
-
/**
|
119 |
-
* @return bool
|
120 |
-
*/
|
121 |
-
public function terminateCurrentSession() {
|
122 |
-
$bSuccess = false;
|
123 |
-
|
124 |
-
$oSes = $this->getCurrentSession();
|
125 |
-
if ( $oSes instanceof Session\EntryVO ) {
|
126 |
-
$bSuccess = ( new Sessions\Lib\Ops\Terminate() )
|
127 |
-
->setMod( $this->getMod() )
|
128 |
-
->byRecordId( $oSes->id );
|
129 |
-
}
|
130 |
-
|
131 |
-
$this->oCurrent = null;
|
132 |
-
$this->getCon()->clearSession();
|
133 |
-
|
134 |
-
return $bSuccess;
|
135 |
-
}
|
136 |
-
|
137 |
-
/**
|
138 |
-
* @return Session\EntryVO|null
|
139 |
-
*/
|
140 |
-
public function getCurrentSession() {
|
141 |
-
if ( empty( $this->oCurrent ) ) {
|
142 |
-
$this->oCurrent = $this->loadCurrentSession();
|
143 |
-
}
|
144 |
-
return $this->oCurrent;
|
145 |
-
}
|
146 |
-
|
147 |
-
/**
|
148 |
-
* @return Session\EntryVO|null
|
149 |
-
*/
|
150 |
-
public function loadCurrentSession() {
|
151 |
-
$oSession = null;
|
152 |
-
$oCon = $this->getCon();
|
153 |
-
if ( did_action( 'init' ) && $oCon->hasSessionId() ) {
|
154 |
-
$oSession = $this->queryGetSession( $oCon->getSessionId() );
|
155 |
-
}
|
156 |
-
return $oSession;
|
157 |
-
}
|
158 |
-
|
159 |
-
/**
|
160 |
-
* @param string $sSessionId
|
161 |
-
* @param string $sUsername
|
162 |
-
* @return bool
|
163 |
-
*/
|
164 |
-
protected function queryCreateSession( $sSessionId, $sUsername ) {
|
165 |
-
/** @var Sessions\ModCon $mod */
|
166 |
-
$mod = $this->getMod();
|
167 |
-
if ( empty( $sSessionId ) || empty( $sUsername ) ) {
|
168 |
-
return null;
|
169 |
-
}
|
170 |
-
|
171 |
-
$this->getCon()->fireEvent( 'session_start' );
|
172 |
-
|
173 |
-
/** @var Session\Insert $oInsert */
|
174 |
-
$oInsert = $mod->getDbHandler_Sessions()->getQueryInserter();
|
175 |
-
return $oInsert->create( $sSessionId, $sUsername );
|
176 |
-
}
|
177 |
-
|
178 |
-
/**
|
179 |
-
* Checks for and gets a user session.
|
180 |
-
* @param string $sUsername
|
181 |
-
* @param string $sSessionId
|
182 |
-
* @return Session\EntryVO|null
|
183 |
-
*/
|
184 |
-
private function queryGetSession( $sSessionId, $sUsername = '' ) {
|
185 |
-
/** @var Sessions\ModCon $mod */
|
186 |
-
$mod = $this->getMod();
|
187 |
-
/** @var Session\Select $oSel */
|
188 |
-
$oSel = $mod->getDbHandler_Sessions()->getQuerySelector();
|
189 |
-
return $oSel->retrieveUserSession( $sSessionId, $sUsername );
|
190 |
-
}
|
191 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
src/processors/traffic.php
DELETED
@@ -1,24 +0,0 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
use FernleafSystems\Wordpress\Plugin\Shield\Modules;
|
4 |
-
use FernleafSystems\Wordpress\Plugin\Shield\Modules\Traffic\Lib;
|
5 |
-
|
6 |
-
/**
|
7 |
-
* Class ICWP_WPSF_Processor_Traffic
|
8 |
-
* @deprecated 10.1
|
9 |
-
*/
|
10 |
-
class ICWP_WPSF_Processor_Traffic extends Modules\BaseShield\ShieldProcessor {
|
11 |
-
|
12 |
-
public function run() {
|
13 |
-
/** @var Modules\Traffic\Options $opts */
|
14 |
-
$opts = $this->getOptions();
|
15 |
-
if ( $opts->isTrafficLoggerEnabled() ) {
|
16 |
-
( new Lib\Logger() )
|
17 |
-
->setMod( $this->getMod() )
|
18 |
-
->run();
|
19 |
-
( new Lib\Limit\Limiter() )
|
20 |
-
->setMod( $this->getMod() )
|
21 |
-
->run();
|
22 |
-
}
|
23 |
-
}
|
24 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
src/processors/user_management.php
DELETED
@@ -1,265 +0,0 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
use FernleafSystems\Wordpress\Plugin\Shield\Modules;
|
4 |
-
use FernleafSystems\Wordpress\Plugin\Shield\Modules\UserManagement;
|
5 |
-
use FernleafSystems\Wordpress\Services\Services;
|
6 |
-
|
7 |
-
/**
|
8 |
-
* @deprecated 10.1
|
9 |
-
*/
|
10 |
-
class ICWP_WPSF_Processor_UserManagement extends Modules\BaseShield\ShieldProcessor {
|
11 |
-
|
12 |
-
/**
|
13 |
-
* This module is set to "run if whitelisted", so we must ensure any
|
14 |
-
* actions taken by this module respect whether the current visitor is whitelisted.
|
15 |
-
*/
|
16 |
-
public function run() {
|
17 |
-
/** @var UserManagement\ModCon $mod */
|
18 |
-
$mod = $this->getMod();
|
19 |
-
/** @var UserManagement\Options $opts */
|
20 |
-
$opts = $this->getOptions();
|
21 |
-
|
22 |
-
// Adds last login indicator column
|
23 |
-
add_filter( 'manage_users_columns', [ $this, 'addUserStatusLastLogin' ] );
|
24 |
-
add_filter( 'wpmu_users_columns', [ $this, 'addUserStatusLastLogin' ] );
|
25 |
-
|
26 |
-
/** Everything from this point on must consider XMLRPC compatibility **/
|
27 |
-
|
28 |
-
// XML-RPC Compatibility
|
29 |
-
if ( Services::WpGeneral()->isXmlrpc() && $mod->isXmlrpcBypass() ) {
|
30 |
-
return;
|
31 |
-
}
|
32 |
-
|
33 |
-
// This controller will handle visitor whitelisted status internally.
|
34 |
-
( new UserManagement\Lib\Suspend\UserSuspendController() )
|
35 |
-
->setMod( $this->getMod() )
|
36 |
-
->execute();
|
37 |
-
|
38 |
-
if ( !$mod->isVisitorWhitelisted() ) {
|
39 |
-
|
40 |
-
/** Everything from this point on must consider XMLRPC compatibility **/
|
41 |
-
if ( $mod->isUserSessionsManagementEnabled() ) {
|
42 |
-
$this->getSubPro( 'sessions' )->execute();
|
43 |
-
}
|
44 |
-
|
45 |
-
if ( $opts->isPasswordPoliciesEnabled() ) {
|
46 |
-
$this->getSubPro( 'passwords' )->execute();
|
47 |
-
}
|
48 |
-
|
49 |
-
// All newly created users have their first seen and password start date set
|
50 |
-
add_action( 'user_register', function ( $nUserId ) {
|
51 |
-
$this->getCon()->getUserMeta( Services::WpUsers()->getUserById( $nUserId ) );
|
52 |
-
} );
|
53 |
-
|
54 |
-
( new UserManagement\Lib\Registration\EmailValidate() )
|
55 |
-
->setMod( $this->getMod() )
|
56 |
-
->run();
|
57 |
-
}
|
58 |
-
}
|
59 |
-
|
60 |
-
public function onWpInit() {
|
61 |
-
$WPU = Services::WpUsers();
|
62 |
-
if ( $WPU->isUserLoggedIn() ) {
|
63 |
-
$this->setPasswordStartedAt( $WPU->getCurrentWpUser() ); // used by Password Policies
|
64 |
-
}
|
65 |
-
}
|
66 |
-
|
67 |
-
/**
|
68 |
-
* @param string $sUsername
|
69 |
-
* @param \WP_User $user
|
70 |
-
*/
|
71 |
-
public function onWpLogin( $sUsername, $user = null ) {
|
72 |
-
if ( !$user instanceof \WP_User ) {
|
73 |
-
$user = Services::WpUsers()->getUserByUsername( $sUsername );
|
74 |
-
}
|
75 |
-
$this->setPasswordStartedAt( $user )// used by Password Policies
|
76 |
-
->setUserLastLoginTime( $user )
|
77 |
-
->sendLoginNotifications( $user );
|
78 |
-
}
|
79 |
-
|
80 |
-
/**
|
81 |
-
* @param \WP_User $user - not checking that user is valid
|
82 |
-
* @return $this
|
83 |
-
*/
|
84 |
-
private function sendLoginNotifications( \WP_User $user ) {
|
85 |
-
/** @var UserManagement\ModCon $mod */
|
86 |
-
$mod = $this->getMod();
|
87 |
-
$aAdminEmails = $mod->getAdminLoginNotificationEmails();
|
88 |
-
$bAdmin = count( $aAdminEmails ) > 0;
|
89 |
-
$bUser = $mod->isSendUserEmailLoginNotification();
|
90 |
-
|
91 |
-
// do some magic logic so we don't send both to the same person (the assumption being that the admin
|
92 |
-
// email recipient is actually an admin (or they'll maybe not get any).
|
93 |
-
if ( $bAdmin && $bUser && in_array( strtolower( $user->user_email ), $aAdminEmails ) ) {
|
94 |
-
$bUser = false;
|
95 |
-
}
|
96 |
-
|
97 |
-
if ( $bAdmin ) {
|
98 |
-
$this->sendAdminLoginEmailNotification( $user );
|
99 |
-
}
|
100 |
-
if ( $bUser && !$this->isUserSubjectToLoginIntent( $user ) ) {
|
101 |
-
$this->sendUserLoginEmailNotification( $user );
|
102 |
-
}
|
103 |
-
return $this;
|
104 |
-
}
|
105 |
-
|
106 |
-
private function setPasswordStartedAt( \WP_User $user ) :self {
|
107 |
-
$this->getCon()
|
108 |
-
->getUserMeta( $user )
|
109 |
-
->setPasswordStartedAt( $user->user_pass );
|
110 |
-
return $this;
|
111 |
-
}
|
112 |
-
|
113 |
-
protected function setUserLastLoginTime( \WP_User $user ) :self {
|
114 |
-
$meta = $this->getCon()->getUserMeta( $user );
|
115 |
-
$meta->last_login_at = Services::Request()->ts();
|
116 |
-
return $this;
|
117 |
-
}
|
118 |
-
|
119 |
-
/**
|
120 |
-
* Adds the column to the users listing table to indicate
|
121 |
-
* @param array $aColumns
|
122 |
-
* @return array
|
123 |
-
*/
|
124 |
-
public function addUserStatusLastLogin( $aColumns ) {
|
125 |
-
|
126 |
-
$sCustomColumnName = $this->getCon()->prefix( 'col_user_status' );
|
127 |
-
if ( !isset( $aColumns[ $sCustomColumnName ] ) ) {
|
128 |
-
$aColumns[ $sCustomColumnName ] = __( 'User Status', 'wp-simple-firewall' );
|
129 |
-
}
|
130 |
-
|
131 |
-
add_filter( 'manage_users_custom_column',
|
132 |
-
function ( $sContent, $sColumnName, $nUserId ) use ( $sCustomColumnName ) {
|
133 |
-
|
134 |
-
if ( $sColumnName == $sCustomColumnName ) {
|
135 |
-
$sValue = __( 'Not Recorded', 'wp-simple-firewall' );
|
136 |
-
$oUser = Services::WpUsers()->getUserById( $nUserId );
|
137 |
-
if ( $oUser instanceof \WP_User ) {
|
138 |
-
$nLastLoginTime = $this->getCon()->getUserMeta( $oUser )->last_login_at;
|
139 |
-
if ( $nLastLoginTime > 0 ) {
|
140 |
-
$sValue = Services::Request()
|
141 |
-
->carbon()
|
142 |
-
->setTimestamp( $nLastLoginTime )
|
143 |
-
->diffForHumans();
|
144 |
-
}
|
145 |
-
}
|
146 |
-
$sNewContent = sprintf( '%s: %s', __( 'Last Login', 'wp-simple-firewall' ), $sValue );
|
147 |
-
$sContent = empty( $sContent ) ? $sNewContent : $sContent.'<br/>'.$sNewContent;
|
148 |
-
}
|
149 |
-
|
150 |
-
return $sContent;
|
151 |
-
},
|
152 |
-
10, 3
|
153 |
-
);
|
154 |
-
|
155 |
-
return $aColumns;
|
156 |
-
}
|
157 |
-
|
158 |
-
/**
|
159 |
-
* @param \WP_User $oUser
|
160 |
-
* @return bool
|
161 |
-
*/
|
162 |
-
private function sendAdminLoginEmailNotification( $oUser ) {
|
163 |
-
/** @var UserManagement\ModCon $mod */
|
164 |
-
$mod = $this->getMod();
|
165 |
-
$con = $this->getCon();
|
166 |
-
|
167 |
-
$aUserCapToRolesMap = [
|
168 |
-
'network_admin' => 'manage_network',
|
169 |
-
'administrator' => 'manage_options',
|
170 |
-
'editor' => 'edit_pages',
|
171 |
-
'author' => 'publish_posts',
|
172 |
-
'contributor' => 'delete_posts',
|
173 |
-
'subscriber' => 'read',
|
174 |
-
];
|
175 |
-
|
176 |
-
$sRoleToCheck = strtolower( apply_filters(
|
177 |
-
$con->prefix( 'login-notification-email-role' ), 'administrator' ) );
|
178 |
-
if ( !array_key_exists( $sRoleToCheck, $aUserCapToRolesMap ) ) {
|
179 |
-
$sRoleToCheck = 'administrator';
|
180 |
-
}
|
181 |
-
$sHumanName = ucwords( str_replace( '_', ' ', $sRoleToCheck ) ).'+';
|
182 |
-
|
183 |
-
$bIsUserSignificantEnough = false;
|
184 |
-
foreach ( $aUserCapToRolesMap as $sRole => $sCap ) {
|
185 |
-
if ( isset( $oUser->allcaps[ $sCap ] ) && $oUser->allcaps[ $sCap ] ) {
|
186 |
-
$bIsUserSignificantEnough = true;
|
187 |
-
}
|
188 |
-
if ( $sRoleToCheck == $sRole ) {
|
189 |
-
break; // we've hit our role limit.
|
190 |
-
}
|
191 |
-
}
|
192 |
-
if ( !$bIsUserSignificantEnough ) {
|
193 |
-
return false;
|
194 |
-
}
|
195 |
-
|
196 |
-
$sHomeUrl = Services::WpGeneral()->getHomeUrl();
|
197 |
-
|
198 |
-
$aMessage = [
|
199 |
-
sprintf( __( 'As requested, %s is notifying you of a successful %s login to a WordPress site that you manage.', 'wp-simple-firewall' ),
|
200 |
-
$con->getHumanName(),
|
201 |
-
$sHumanName
|
202 |
-
),
|
203 |
-
'',
|
204 |
-
sprintf( __( 'Important: %s', 'wp-simple-firewall' ), __( 'This user may now be subject to additional Two-Factor Authentication before completing their login.', 'wp-simple-firewall' ) ),
|
205 |
-
'',
|
206 |
-
__( 'Details for this user are below:', 'wp-simple-firewall' ),
|
207 |
-
'- '.sprintf( '%s: %s', __( 'Site URL', 'wp-simple-firewall' ), $sHomeUrl ),
|
208 |
-
'- '.sprintf( '%s: %s', __( 'Username', 'wp-simple-firewall' ), $oUser->user_login ),
|
209 |
-
'- '.sprintf( '%s: %s', __( 'Email', 'wp-simple-firewall' ), $oUser->user_email ),
|
210 |
-
'- '.sprintf( '%s: %s', __( 'IP Address', 'wp-simple-firewall' ), Services::IP()->getRequestIp() ),
|
211 |
-
'',
|
212 |
-
__( 'Thanks.', 'wp-simple-firewall' )
|
213 |
-
];
|
214 |
-
|
215 |
-
$oEmailer = $this->getMod()
|
216 |
-
->getEmailProcessor();
|
217 |
-
foreach ( $mod->getAdminLoginNotificationEmails() as $to ) {
|
218 |
-
$oEmailer->sendEmailWithWrap(
|
219 |
-
$to,
|
220 |
-
sprintf( '%s - %s', __( 'Notice', 'wp-simple-firewall' ), sprintf( __( '%s Just Logged Into %s', 'wp-simple-firewall' ), $sHumanName, $sHomeUrl ) ),
|
221 |
-
$aMessage
|
222 |
-
);
|
223 |
-
}
|
224 |
-
|
225 |
-
return true;
|
226 |
-
}
|
227 |
-
|
228 |
-
/**
|
229 |
-
* @param \WP_User $oUser
|
230 |
-
* @return bool
|
231 |
-
*/
|
232 |
-
private function sendUserLoginEmailNotification( $oUser ) {
|
233 |
-
$oWp = Services::WpGeneral();
|
234 |
-
$aMessage = [
|
235 |
-
sprintf( __( '%s is notifying you of a successful login to your WordPress account.', 'wp-simple-firewall' ), $this->getCon()
|
236 |
-
->getHumanName() ),
|
237 |
-
'',
|
238 |
-
__( 'Details for this login are below:', 'wp-simple-firewall' ),
|
239 |
-
'- '.sprintf( '%s: %s', __( 'Site URL', 'wp-simple-firewall' ), $oWp->getHomeUrl() ),
|
240 |
-
'- '.sprintf( '%s: %s', __( 'Username', 'wp-simple-firewall' ), $oUser->user_login ),
|
241 |
-
'- '.sprintf( '%s: %s', __( 'IP Address', 'wp-simple-firewall' ), Services::IP()->getRequestIp() ),
|
242 |
-
'- '.sprintf( '%s: %s', __( 'Time', 'wp-simple-firewall' ), $oWp->getTimeStampForDisplay() ),
|
243 |
-
'',
|
244 |
-
__( 'If this is unexpected or suspicious, please contact your site administrator immediately.', 'wp-simple-firewall' ),
|
245 |
-
'',
|
246 |
-
__( 'Thanks.', 'wp-simple-firewall' )
|
247 |
-
];
|
248 |
-
|
249 |
-
return $this
|
250 |
-
->getMod()
|
251 |
-
->getEmailProcessor()
|
252 |
-
->sendEmailWithWrap(
|
253 |
-
$oUser->user_email,
|
254 |
-
sprintf( '%s - %s', __( 'Notice', 'wp-simple-firewall' ), __( 'A login to your WordPress account just occurred', 'wp-simple-firewall' ) ),
|
255 |
-
$aMessage
|
256 |
-
);
|
257 |
-
}
|
258 |
-
|
259 |
-
protected function getSubProMap() :array {
|
260 |
-
return [
|
261 |
-
'passwords' => 'ICWP_WPSF_Processor_UserManagement_Passwords',
|
262 |
-
'sessions' => 'ICWP_WPSF_Processor_UserManagement_Sessions',
|
263 |
-
];
|
264 |
-
}
|
265 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
src/processors/usermanagement_passwords.php
DELETED
@@ -1,333 +0,0 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
use FernleafSystems\Wordpress\Plugin\Shield\Modules;
|
4 |
-
use FernleafSystems\Wordpress\Plugin\Shield\Modules\UserManagement;
|
5 |
-
use FernleafSystems\Wordpress\Services\Services;
|
6 |
-
|
7 |
-
/**
|
8 |
-
* @deprecated 10.1
|
9 |
-
*/
|
10 |
-
class ICWP_WPSF_Processor_UserManagement_Passwords extends Modules\BaseShield\ShieldProcessor {
|
11 |
-
|
12 |
-
public function run() {
|
13 |
-
add_action( 'password_reset', [ $this, 'onPasswordReset' ], 100, 1 );
|
14 |
-
add_filter( 'registration_errors', [ $this, 'checkPassword' ], 100, 3 );
|
15 |
-
add_action( 'user_profile_update_errors', [ $this, 'checkPassword' ], 100, 3 );
|
16 |
-
add_action( 'validate_password_reset', [ $this, 'checkPassword' ], 100, 3 );
|
17 |
-
}
|
18 |
-
|
19 |
-
/**
|
20 |
-
* @param string $sUsername
|
21 |
-
* @param \WP_User $user
|
22 |
-
*/
|
23 |
-
public function onWpLogin( $sUsername, $user ) {
|
24 |
-
$this->captureLogin( $user );
|
25 |
-
}
|
26 |
-
|
27 |
-
/**
|
28 |
-
* @param string $sCookie
|
29 |
-
* @param int $nExpire
|
30 |
-
* @param int $nExpiration
|
31 |
-
* @param int $nUserId
|
32 |
-
*/
|
33 |
-
public function onWpSetLoggedInCookie( $sCookie, $nExpire, $nExpiration, $nUserId ) {
|
34 |
-
$this->captureLogin( Services::WpUsers()->getUserById( $nUserId ) );
|
35 |
-
}
|
36 |
-
|
37 |
-
/**
|
38 |
-
* @param \WP_User $user
|
39 |
-
*/
|
40 |
-
private function captureLogin( $user ) {
|
41 |
-
$password = $this->getLoginPassword();
|
42 |
-
|
43 |
-
if ( $user instanceof \WP_User
|
44 |
-
&& Services::Request()->isPost()
|
45 |
-
&& !$this->isLoginCaptured() && !empty( $password ) ) {
|
46 |
-
|
47 |
-
$this->setLoginCaptured();
|
48 |
-
try {
|
49 |
-
$this->applyPasswordChecks( $password );
|
50 |
-
$failed = false;
|
51 |
-
}
|
52 |
-
catch ( \Exception $e ) {
|
53 |
-
$failed = ( $e->getCode() != 999 ); // We don't fail when the PWNED API is not available.
|
54 |
-
}
|
55 |
-
$this->setPasswordFailedFlag( $user, $failed );
|
56 |
-
}
|
57 |
-
}
|
58 |
-
|
59 |
-
public function onWpLoaded() {
|
60 |
-
if ( is_admin() && !Services::WpGeneral()->isAjax() && !Services::Request()->isPost()
|
61 |
-
&& Services::WpUsers()->isUserLoggedIn() ) {
|
62 |
-
$this->processExpiredPassword();
|
63 |
-
$this->processFailedCheckPassword();
|
64 |
-
}
|
65 |
-
}
|
66 |
-
|
67 |
-
/**
|
68 |
-
* @param \WP_User $user
|
69 |
-
*/
|
70 |
-
public function onPasswordReset( $user ) {
|
71 |
-
if ( $user instanceof \WP_User && $user->ID > 0 ) {
|
72 |
-
$meta = $this->getCon()->getUserMeta( $user );
|
73 |
-
unset( $meta->pass_hash );
|
74 |
-
$meta->pass_started_at = 0;
|
75 |
-
}
|
76 |
-
}
|
77 |
-
|
78 |
-
private function processExpiredPassword() {
|
79 |
-
/** @var UserManagement\Options $oOpts */
|
80 |
-
$oOpts = $this->getOptions();
|
81 |
-
if ( $oOpts->isPassExpirationEnabled() ) {
|
82 |
-
$nPassStartedAt = (int)$this->getCon()->getCurrentUserMeta()->pass_started_at;
|
83 |
-
if ( $nPassStartedAt > 0 ) {
|
84 |
-
if ( Services::Request()->ts() - $nPassStartedAt > $oOpts->getPassExpireTimeout() ) {
|
85 |
-
$this->getCon()->fireEvent( 'pass_expired' );
|
86 |
-
$this->redirectToResetPassword(
|
87 |
-
sprintf( __( 'Your password has expired (after %s days).', 'wp-simple-firewall' ), $oOpts->getPassExpireDays() )
|
88 |
-
);
|
89 |
-
}
|
90 |
-
}
|
91 |
-
}
|
92 |
-
}
|
93 |
-
|
94 |
-
private function processFailedCheckPassword() {
|
95 |
-
/** @var UserManagement\Options $opts */
|
96 |
-
$opts = $this->getOptions();
|
97 |
-
$oMeta = $this->getCon()->getCurrentUserMeta();
|
98 |
-
|
99 |
-
$checkFailed = $opts->isPassForceUpdateExisting()
|
100 |
-
&& isset( $oMeta->pass_check_failed_at ) && $oMeta->pass_check_failed_at > 0;
|
101 |
-
|
102 |
-
if ( $checkFailed ) {
|
103 |
-
$this->redirectToResetPassword(
|
104 |
-
__( "Your password doesn't meet requirements set by your security administrator.", 'wp-simple-firewall' )
|
105 |
-
);
|
106 |
-
}
|
107 |
-
}
|
108 |
-
|
109 |
-
/**
|
110 |
-
* IMPORTANT: User must be logged-in for this to work correctly
|
111 |
-
* We have a 2 minute delay between redirects because some custom user forms redirect to custom
|
112 |
-
* password reset pages. This prevents users following this flow.
|
113 |
-
* @param string $sMessage
|
114 |
-
* @uses wp_redirect()
|
115 |
-
*/
|
116 |
-
private function redirectToResetPassword( $sMessage ) {
|
117 |
-
$nNow = Services::Request()->ts();
|
118 |
-
|
119 |
-
$oMeta = $this->getCon()->getCurrentUserMeta();
|
120 |
-
$nLastRedirect = (int)$oMeta->pass_reset_last_redirect_at;
|
121 |
-
if ( $nNow - $nLastRedirect > MINUTE_IN_SECONDS*2 ) {
|
122 |
-
|
123 |
-
$oMeta->pass_reset_last_redirect_at = $nNow;
|
124 |
-
|
125 |
-
$oWpUsers = Services::WpUsers();
|
126 |
-
$sAction = Services::Request()->query( 'action' );
|
127 |
-
$oUser = $oWpUsers->getCurrentWpUser();
|
128 |
-
if ( $oUser && ( !Services::WpGeneral()->isLoginUrl() || !in_array( $sAction, [ 'rp', 'resetpass' ] ) ) ) {
|
129 |
-
|
130 |
-
$sMessage .= ' '.__( 'For your security, please use the password section below to update your password.', 'wp-simple-firewall' );
|
131 |
-
$this->getMod()
|
132 |
-
->setFlashAdminNotice( $sMessage, true, true );
|
133 |
-
$this->getCon()->fireEvent( 'password_policy_force_change' );
|
134 |
-
Services::Response()->redirect( $oWpUsers->getPasswordResetUrl( $oUser ) );
|
135 |
-
}
|
136 |
-
}
|
137 |
-
}
|
138 |
-
|
139 |
-
/**
|
140 |
-
* @param \WP_Error $oErrors
|
141 |
-
* @return \WP_Error
|
142 |
-
*/
|
143 |
-
public function checkPassword( $oErrors ) {
|
144 |
-
$aExistingCodes = $oErrors->get_error_code();
|
145 |
-
if ( empty( $aExistingCodes ) ) {
|
146 |
-
$password = $this->getLoginPassword();
|
147 |
-
if ( !empty( $password ) ) {
|
148 |
-
$aFailureMsg = '';
|
149 |
-
try {
|
150 |
-
$this->applyPasswordChecks( $password );
|
151 |
-
$bChecksPassed = true;
|
152 |
-
}
|
153 |
-
catch ( \Exception $oE ) {
|
154 |
-
$bChecksPassed = ( $oE->getCode() === 999 );
|
155 |
-
$aFailureMsg = $oE->getMessage();
|
156 |
-
}
|
157 |
-
|
158 |
-
if ( $bChecksPassed ) {
|
159 |
-
if ( Services::WpUsers()->isUserLoggedIn() ) {
|
160 |
-
$this->getCon()->getCurrentUserMeta()->pass_check_failed_at = 0;
|
161 |
-
}
|
162 |
-
}
|
163 |
-
else {
|
164 |
-
$sMessage = __( 'Your security administrator has imposed requirements for password quality.', 'wp-simple-firewall' );
|
165 |
-
if ( !empty( $aFailureMsg ) ) {
|
166 |
-
$sMessage .= '<br/>'.sprintf( __( 'Reason', 'wp-simple-firewall' ).': '.$aFailureMsg );
|
167 |
-
}
|
168 |
-
$oErrors->add( 'shield_password_policy', $sMessage );
|
169 |
-
$this->getCon()->fireEvent( 'password_policy_block' );
|
170 |
-
}
|
171 |
-
}
|
172 |
-
}
|
173 |
-
|
174 |
-
return $oErrors;
|
175 |
-
}
|
176 |
-
|
177 |
-
/**
|
178 |
-
* @param string $password
|
179 |
-
* @throws \Exception
|
180 |
-
*/
|
181 |
-
protected function applyPasswordChecks( string $password ) {
|
182 |
-
/** @var UserManagement\Options $opts */
|
183 |
-
$opts = $this->getOptions();
|
184 |
-
|
185 |
-
if ( $opts->getPassMinLength() > 0 ) {
|
186 |
-
$this->testPasswordMeetsMinimumLength( $password, $opts->getPassMinLength() );
|
187 |
-
}
|
188 |
-
if ( $opts->getPassMinStrength() > 0 ) {
|
189 |
-
$this->testPasswordMeetsMinimumStrength( $password, $opts->getPassMinStrength() );
|
190 |
-
}
|
191 |
-
if ( $opts->isPassPreventPwned() ) {
|
192 |
-
$this->sendRequestToPwnedRange( $password );
|
193 |
-
}
|
194 |
-
}
|
195 |
-
|
196 |
-
/**
|
197 |
-
* @param string $password
|
198 |
-
* @param int $min
|
199 |
-
* @return bool
|
200 |
-
* @throws \Exception
|
201 |
-
*/
|
202 |
-
private function testPasswordMeetsMinimumLength( string $password, int $min ) {
|
203 |
-
$length = strlen( $password );
|
204 |
-
if ( $length < $min ) {
|
205 |
-
throw new \Exception( sprintf( __( 'Password length (%s) too short (min: %s characters)', 'wp-simple-firewall' ), $length, $min ) );
|
206 |
-
}
|
207 |
-
return true;
|
208 |
-
}
|
209 |
-
|
210 |
-
/**
|
211 |
-
* @param string $password
|
212 |
-
* @param int $min
|
213 |
-
* @return bool
|
214 |
-
* @throws \Exception
|
215 |
-
*/
|
216 |
-
private function testPasswordMeetsMinimumStrength( string $password, int $min ) {
|
217 |
-
$aResults = ( new \ZxcvbnPhp\Zxcvbn() )->passwordStrength( $password );
|
218 |
-
|
219 |
-
$nScore = $aResults[ 'score' ];
|
220 |
-
|
221 |
-
if ( $nScore < $min ) {
|
222 |
-
/** @var UserManagement\ModCon $mod */
|
223 |
-
$mod = $this->getMod();
|
224 |
-
throw new \Exception( sprintf( "Password strength (%s) doesn't meet the minimum required strength (%s).",
|
225 |
-
$mod->getPassStrengthName( $nScore ), $mod->getPassStrengthName( $min ) ) );
|
226 |
-
}
|
227 |
-
return true;
|
228 |
-
}
|
229 |
-
|
230 |
-
/**
|
231 |
-
* Unused
|
232 |
-
* @return bool
|
233 |
-
* private function verifyApiAccess() {
|
234 |
-
* try {
|
235 |
-
* $this->sendRequestToPwnedRange( 'P@ssw0rd' );
|
236 |
-
* }
|
237 |
-
* catch ( \Exception $oE ) {
|
238 |
-
* return false;
|
239 |
-
* }
|
240 |
-
* return true;
|
241 |
-
* }
|
242 |
-
*/
|
243 |
-
|
244 |
-
/**
|
245 |
-
* @param string $password
|
246 |
-
* @return bool
|
247 |
-
* @throws \Exception
|
248 |
-
*/
|
249 |
-
private function sendRequestToPwnedRange( string $password ) {
|
250 |
-
$oHttpReq = Services::HttpRequest();
|
251 |
-
|
252 |
-
$sPassHash = strtoupper( hash( 'sha1', $password ) );
|
253 |
-
$sSubHash = substr( $sPassHash, 0, 5 );
|
254 |
-
|
255 |
-
$bSuccess = $oHttpReq->get(
|
256 |
-
sprintf( '%s/%s', $this->getOptions()->getDef( 'pwned_api_url_password_range' ), $sSubHash ),
|
257 |
-
[
|
258 |
-
'headers' => [ 'user-agent' => sprintf( '%s WP Plugin-v%s', 'Shield', $this->getCon()->getVersion() ) ]
|
259 |
-
]
|
260 |
-
);
|
261 |
-
|
262 |
-
$sError = '';
|
263 |
-
$nErrorCode = 2; // Default To Error
|
264 |
-
if ( !$bSuccess ) {
|
265 |
-
$sError = 'API request failed';
|
266 |
-
$nErrorCode = 999; // We don't fail PWNED passwords on failed API requests.
|
267 |
-
}
|
268 |
-
else {
|
269 |
-
$nHttpCode = $oHttpReq->lastResponse->getCode();
|
270 |
-
if ( empty( $nHttpCode ) ) {
|
271 |
-
$sError = 'Unexpected Error: No response code available from the Pwned API';
|
272 |
-
}
|
273 |
-
elseif ( $nHttpCode != 200 ) {
|
274 |
-
$sError = 'Unexpected Error: The response from the Pwned API was unexpected';
|
275 |
-
}
|
276 |
-
elseif ( empty( $oHttpReq->lastResponse->body ) ) {
|
277 |
-
$sError = 'Unexpected Error: The response from the Pwned API was empty';
|
278 |
-
}
|
279 |
-
else {
|
280 |
-
$nPwnedCount = 0;
|
281 |
-
foreach ( array_map( 'trim', explode( "\n", trim( $oHttpReq->lastResponse->body ) ) ) as $sRow ) {
|
282 |
-
if ( $sSubHash.substr( strtoupper( $sRow ), 0, 35 ) == $sPassHash ) {
|
283 |
-
$nPwnedCount = substr( $sRow, 36 );
|
284 |
-
break;
|
285 |
-
}
|
286 |
-
}
|
287 |
-
if ( $nPwnedCount > 0 ) {
|
288 |
-
$sError = __( 'Please use a different password.', 'wp-simple-firewall' )
|
289 |
-
.'<br/>'.__( 'This password has been pwned.', 'wp-simple-firewall' )
|
290 |
-
.' '.sprintf(
|
291 |
-
'(<a href="%s" target="_blank">%s</a>)',
|
292 |
-
'https://www.troyhunt.com/ive-just-launched-pwned-passwords-version-2/',
|
293 |
-
sprintf( __( '%s times', 'wp-simple-firewall' ), $nPwnedCount )
|
294 |
-
);
|
295 |
-
}
|
296 |
-
else {
|
297 |
-
// Success: Password is not pwned
|
298 |
-
$nErrorCode = 0;
|
299 |
-
}
|
300 |
-
}
|
301 |
-
}
|
302 |
-
|
303 |
-
if ( $nErrorCode != 0 ) {
|
304 |
-
throw new \Exception( '[Pwned Request] '.$sError, $nErrorCode );
|
305 |
-
}
|
306 |
-
|
307 |
-
return true;
|
308 |
-
}
|
309 |
-
|
310 |
-
private function getLoginPassword() :string {
|
311 |
-
$sPass = '';
|
312 |
-
|
313 |
-
// Edd: edd_user_pass; Woo: password;
|
314 |
-
foreach ( [ 'pwd', 'pass1' ] as $key ) {
|
315 |
-
$sP = Services::Request()->post( $key );
|
316 |
-
if ( !empty( $sP ) ) {
|
317 |
-
$sPass = $sP;
|
318 |
-
break;
|
319 |
-
}
|
320 |
-
}
|
321 |
-
return $sPass;
|
322 |
-
}
|
323 |
-
|
324 |
-
/**
|
325 |
-
* @param \WP_User $user
|
326 |
-
* @param bool $failed
|
327 |
-
*/
|
328 |
-
private function setPasswordFailedFlag( \WP_User $user, bool $failed = false ) {
|
329 |
-
$this->getCon()
|
330 |
-
->getUserMeta( $user )
|
331 |
-
->pass_check_failed_at = $failed ? Services::Request()->ts() : 0;
|
332 |
-
}
|
333 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
src/processors/usermanagement_sessions.php
DELETED
@@ -1,186 +0,0 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
use FernleafSystems\Wordpress\Plugin\Shield\Modules;
|
4 |
-
use FernleafSystems\Wordpress\Plugin\Shield\Modules\UserManagement;
|
5 |
-
use FernleafSystems\Wordpress\Services\Services;
|
6 |
-
|
7 |
-
/**
|
8 |
-
* @deprecated 10.1
|
9 |
-
*/
|
10 |
-
class ICWP_WPSF_Processor_UserManagement_Sessions extends Modules\BaseShield\ShieldProcessor {
|
11 |
-
|
12 |
-
public function run() {
|
13 |
-
add_filter( 'wp_login_errors', [ $this, 'addLoginMessage' ] );
|
14 |
-
add_filter( 'auth_cookie_expiration', [ $this, 'setMaxAuthCookieExpiration' ], 100, 1 );
|
15 |
-
}
|
16 |
-
|
17 |
-
/**
|
18 |
-
* Cron callback
|
19 |
-
*/
|
20 |
-
public function runDailyCron() {
|
21 |
-
( new UserManagement\Lib\CleanExpired() )
|
22 |
-
->setMod( $this->getMod() )
|
23 |
-
->run();
|
24 |
-
}
|
25 |
-
|
26 |
-
/**
|
27 |
-
* @param string $username
|
28 |
-
* @param \WP_User $user
|
29 |
-
*/
|
30 |
-
public function onWpLogin( $username, $user ) {
|
31 |
-
if ( !$user instanceof \WP_User ) {
|
32 |
-
$user = Services::WpUsers()->getUserByUsername( $username );
|
33 |
-
}
|
34 |
-
$this->enforceSessionLimits( $user );
|
35 |
-
}
|
36 |
-
|
37 |
-
/**
|
38 |
-
* @param string $sCookie
|
39 |
-
* @param int $nExpire
|
40 |
-
* @param int $nExpiration
|
41 |
-
* @param int $nUserId
|
42 |
-
*/
|
43 |
-
public function onWpSetLoggedInCookie( $sCookie, $nExpire, $nExpiration, $nUserId ) {
|
44 |
-
$this->enforceSessionLimits( Services::WpUsers()->getUserById( $nUserId ) );
|
45 |
-
}
|
46 |
-
|
47 |
-
public function onWpLoaded() {
|
48 |
-
if ( Services::WpUsers()->isUserLoggedIn() && !Services::Rest()->isRest() ) {
|
49 |
-
$this->checkCurrentSession();
|
50 |
-
}
|
51 |
-
}
|
52 |
-
|
53 |
-
private function checkCurrentSession() {
|
54 |
-
$con = $this->getCon();
|
55 |
-
/** @var UserManagement\ModCon $mod */
|
56 |
-
$mod = $this->getMod();
|
57 |
-
try {
|
58 |
-
if ( $mod->hasValidRequestIP() ) {
|
59 |
-
$this->assessSession();
|
60 |
-
}
|
61 |
-
}
|
62 |
-
catch ( \Exception $e ) {
|
63 |
-
$event = $e->getMessage();
|
64 |
-
$con->fireEvent( $event );
|
65 |
-
$con->getModule_Sessions()
|
66 |
-
->getSessionCon()
|
67 |
-
->terminateCurrentSession();
|
68 |
-
$WPU = Services::WpUsers();
|
69 |
-
is_admin() ? $WPU->forceUserRelogin( [ 'shield-forcelogout' => $event ] ) : $WPU->logoutUser( true );
|
70 |
-
}
|
71 |
-
}
|
72 |
-
|
73 |
-
/**
|
74 |
-
* @throws \Exception
|
75 |
-
*/
|
76 |
-
private function assessSession() {
|
77 |
-
/** @var UserManagement\Options $oOpts */
|
78 |
-
$oOpts = $this->getOptions();
|
79 |
-
|
80 |
-
$sess = $this->getMod()->getSession();
|
81 |
-
if ( empty( $sess ) ) {
|
82 |
-
throw new \Exception( 'session_notfound' );
|
83 |
-
}
|
84 |
-
|
85 |
-
$nTime = Services::Request()->ts();
|
86 |
-
|
87 |
-
if ( $oOpts->hasMaxSessionTimeout() && ( $nTime - $sess->logged_in_at > $oOpts->getMaxSessionTime() ) ) {
|
88 |
-
throw new \Exception( 'session_expired' );
|
89 |
-
}
|
90 |
-
|
91 |
-
if ( $oOpts->hasSessionIdleTimeout() && ( $nTime - $sess->last_activity_at > $oOpts->getIdleTimeoutInterval() ) ) {
|
92 |
-
throw new \Exception( 'session_idle' );
|
93 |
-
}
|
94 |
-
|
95 |
-
$oIP = Services::IP();
|
96 |
-
if ( $oOpts->isLockToIp() && $oIP->getRequestIp() != $sess->ip ) {
|
97 |
-
// We force-refresh the server IPs just to be sure.
|
98 |
-
Services::IP()->getServerPublicIPs( true );
|
99 |
-
if ( !$oIP->isLoopback() ) {
|
100 |
-
throw new \Exception( 'session_iplock' );
|
101 |
-
}
|
102 |
-
}
|
103 |
-
// TODO: 'session_browserlock';
|
104 |
-
}
|
105 |
-
|
106 |
-
/**
|
107 |
-
* @param int $nTimeout
|
108 |
-
* @return int
|
109 |
-
*/
|
110 |
-
public function setMaxAuthCookieExpiration( $nTimeout ) {
|
111 |
-
/** @var UserManagement\Options $oOpts */
|
112 |
-
$oOpts = $this->getOptions();
|
113 |
-
return $oOpts->hasMaxSessionTimeout() ? min( $nTimeout, $oOpts->getMaxSessionTime() ) : $nTimeout;
|
114 |
-
}
|
115 |
-
|
116 |
-
/**
|
117 |
-
* @param \WP_User $user
|
118 |
-
*/
|
119 |
-
protected function enforceSessionLimits( $user ) {
|
120 |
-
/** @var UserManagement\Options $opts */
|
121 |
-
$opts = $this->getOptions();
|
122 |
-
|
123 |
-
$nSessionLimit = $opts->getOpt( 'session_username_concurrent_limit', 1 );
|
124 |
-
if ( !$this->isLoginCaptured() && $nSessionLimit > 0 && $user instanceof WP_User ) {
|
125 |
-
$this->setLoginCaptured();
|
126 |
-
try {
|
127 |
-
$this->getMod()
|
128 |
-
->getDbHandler_Sessions()
|
129 |
-
->getQueryDeleter()
|
130 |
-
->addWhere( 'wp_username', $user->user_login )
|
131 |
-
->deleteExcess( $nSessionLimit, 'last_activity_at', true );
|
132 |
-
}
|
133 |
-
catch ( \Exception $e ) {
|
134 |
-
}
|
135 |
-
}
|
136 |
-
}
|
137 |
-
|
138 |
-
/**
|
139 |
-
* @param \WP_Error $error
|
140 |
-
* @return \WP_Error
|
141 |
-
*/
|
142 |
-
public function addLoginMessage( $error ) {
|
143 |
-
|
144 |
-
if ( !$error instanceof \WP_Error ) {
|
145 |
-
$error = new \WP_Error();
|
146 |
-
}
|
147 |
-
|
148 |
-
$sForceLogout = Services::Request()->query( 'shield-forcelogout' );
|
149 |
-
if ( $sForceLogout ) {
|
150 |
-
|
151 |
-
switch ( $sForceLogout ) {
|
152 |
-
case 'session_expired':
|
153 |
-
$sMessage = __( 'Your session has expired.', 'wp-simple-firewall' );
|
154 |
-
break;
|
155 |
-
|
156 |
-
case 'session_idle':
|
157 |
-
$sMessage = __( 'Your session was idle for too long.', 'wp-simple-firewall' );
|
158 |
-
break;
|
159 |
-
|
160 |
-
case 'session_iplock':
|
161 |
-
$sMessage = __( 'Your session was locked to another IP Address.', 'wp-simple-firewall' );
|
162 |
-
break;
|
163 |
-
|
164 |
-
case 'session_notfound':
|
165 |
-
$sMessage = sprintf(
|
166 |
-
__( 'You do not currently have a %s user session.', 'wp-simple-firewall' ),
|
167 |
-
$this->getCon()->getHumanName()
|
168 |
-
);
|
169 |
-
break;
|
170 |
-
|
171 |
-
case 'session_browserlock':
|
172 |
-
$sMessage = __( 'Your browser appears to have changed for this session.', 'wp-simple-firewall' );
|
173 |
-
break;
|
174 |
-
|
175 |
-
case 'session_unverified':
|
176 |
-
default:
|
177 |
-
$sMessage = __( 'Your session was terminated.', 'wp-simple-firewall' );
|
178 |
-
break;
|
179 |
-
}
|
180 |
-
|
181 |
-
$sMessage .= '<br />'.__( 'Please login again.', 'wp-simple-firewall' );
|
182 |
-
$error->add( 'shield-forcelogout', $sMessage );
|
183 |
-
}
|
184 |
-
return $error;
|
185 |
-
}
|
186 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
src/wizards/base.php
CHANGED
@@ -46,7 +46,7 @@ abstract class ICWP_WPSF_Wizard_Base {
|
|
46 |
$aResponse[ 'message' ] = 'Please login to run this wizard.';
|
47 |
}
|
48 |
}
|
49 |
-
catch ( Exception $
|
50 |
}
|
51 |
|
52 |
return $aResponse;
|
@@ -70,7 +70,7 @@ abstract class ICWP_WPSF_Wizard_Base {
|
|
70 |
|
71 |
Services::WpGeneral()->wpDie( $sDieMessage );
|
72 |
}
|
73 |
-
catch ( Exception $
|
74 |
if ( $sWizard == 'landing' ) {
|
75 |
$this->loadWizardLanding();
|
76 |
}
|
@@ -82,12 +82,12 @@ abstract class ICWP_WPSF_Wizard_Base {
|
|
82 |
*/
|
83 |
protected function loadWizard() {
|
84 |
try {
|
85 |
-
$
|
86 |
}
|
87 |
-
catch ( Exception $
|
88 |
-
$
|
89 |
}
|
90 |
-
echo $
|
91 |
die();
|
92 |
}
|
93 |
|
@@ -96,17 +96,17 @@ abstract class ICWP_WPSF_Wizard_Base {
|
|
96 |
*/
|
97 |
public function renderWizardLandingPage() {
|
98 |
try {
|
99 |
-
$
|
100 |
->renderTemplate(
|
101 |
'wizard/pages/landing.twig',
|
102 |
$this->getRenderData_PageWizardLanding(),
|
103 |
true
|
104 |
);
|
105 |
}
|
106 |
-
catch ( Exception $
|
107 |
-
$
|
108 |
}
|
109 |
-
return $
|
110 |
}
|
111 |
|
112 |
/**
|
@@ -114,17 +114,17 @@ abstract class ICWP_WPSF_Wizard_Base {
|
|
114 |
*/
|
115 |
public function renderWizardLandingSnippet() {
|
116 |
try {
|
117 |
-
$
|
118 |
->renderTemplate(
|
119 |
'wizard/snippets/wizard_landing.twig',
|
120 |
$this->getRenderData_PageWizardLanding(),
|
121 |
true
|
122 |
);
|
123 |
}
|
124 |
-
catch ( Exception $
|
125 |
-
$
|
126 |
}
|
127 |
-
return $
|
128 |
}
|
129 |
|
130 |
/**
|
@@ -366,8 +366,8 @@ abstract class ICWP_WPSF_Wizard_Base {
|
|
366 |
try {
|
367 |
$aNextStepDef[ 'content' ] = $this->renderWizardStep( $aNextStepDef[ 'slug' ] );
|
368 |
}
|
369 |
-
catch ( Exception $
|
370 |
-
$aNextStepDef[ 'content' ] = 'Content could not be displayed due to error: '.$
|
371 |
}
|
372 |
|
373 |
return $aNextStepDef;
|
46 |
$aResponse[ 'message' ] = 'Please login to run this wizard.';
|
47 |
}
|
48 |
}
|
49 |
+
catch ( Exception $e ) {
|
50 |
}
|
51 |
|
52 |
return $aResponse;
|
70 |
|
71 |
Services::WpGeneral()->wpDie( $sDieMessage );
|
72 |
}
|
73 |
+
catch ( \Exception $e ) {
|
74 |
if ( $sWizard == 'landing' ) {
|
75 |
$this->loadWizardLanding();
|
76 |
}
|
82 |
*/
|
83 |
protected function loadWizard() {
|
84 |
try {
|
85 |
+
$content = $this->renderWizard();
|
86 |
}
|
87 |
+
catch ( \Exception $e ) {
|
88 |
+
$content = $e->getMessage();
|
89 |
}
|
90 |
+
echo $content;
|
91 |
die();
|
92 |
}
|
93 |
|
96 |
*/
|
97 |
public function renderWizardLandingPage() {
|
98 |
try {
|
99 |
+
$content = $this->getMod()
|
100 |
->renderTemplate(
|
101 |
'wizard/pages/landing.twig',
|
102 |
$this->getRenderData_PageWizardLanding(),
|
103 |
true
|
104 |
);
|
105 |
}
|
106 |
+
catch ( \Exception $e ) {
|
107 |
+
$content = $e->getMessage();
|
108 |
}
|
109 |
+
return $content;
|
110 |
}
|
111 |
|
112 |
/**
|
114 |
*/
|
115 |
public function renderWizardLandingSnippet() {
|
116 |
try {
|
117 |
+
$content = $this->getMod()
|
118 |
->renderTemplate(
|
119 |
'wizard/snippets/wizard_landing.twig',
|
120 |
$this->getRenderData_PageWizardLanding(),
|
121 |
true
|
122 |
);
|
123 |
}
|
124 |
+
catch ( \Exception $e ) {
|
125 |
+
$content = $e->getMessage();
|
126 |
}
|
127 |
+
return $content;
|
128 |
}
|
129 |
|
130 |
/**
|
366 |
try {
|
367 |
$aNextStepDef[ 'content' ] = $this->renderWizardStep( $aNextStepDef[ 'slug' ] );
|
368 |
}
|
369 |
+
catch ( \Exception $e ) {
|
370 |
+
$aNextStepDef[ 'content' ] = 'Content could not be displayed due to error: '.$e->getMessage();
|
371 |
}
|
372 |
|
373 |
return $aNextStepDef;
|
src/wizards/plugin.php
CHANGED
@@ -365,27 +365,27 @@ class ICWP_WPSF_Wizard_Plugin extends ICWP_WPSF_Wizard_BaseWpsf {
|
|
365 |
*/
|
366 |
private function wizardLicense() {
|
367 |
|
368 |
-
$
|
369 |
|
370 |
$mod = $this->getCon()->getModule_License();
|
371 |
try {
|
372 |
-
$
|
373 |
->verify( true )
|
374 |
->hasValidWorkingLicense();
|
375 |
-
if ( $
|
376 |
-
$
|
377 |
}
|
378 |
else {
|
379 |
-
$
|
380 |
}
|
381 |
}
|
382 |
-
catch ( Exception $
|
383 |
-
$
|
384 |
}
|
385 |
|
386 |
return ( new \FernleafSystems\Utilities\Response() )
|
387 |
-
->setSuccessful( $
|
388 |
-
->setMessageText( $
|
389 |
}
|
390 |
|
391 |
/**
|
@@ -403,9 +403,9 @@ class ICWP_WPSF_Wizard_Plugin extends ICWP_WPSF_Wizard_BaseWpsf {
|
|
403 |
->setMod( $this->getMod() )
|
404 |
->fromSite( $sMasterSiteUrl, $sSecretKey, $bEnabledNetwork );
|
405 |
}
|
406 |
-
catch ( Exception $
|
407 |
-
$sSiteResponse = $
|
408 |
-
$nCode = $
|
409 |
}
|
410 |
|
411 |
$aErrors = [
|
@@ -451,8 +451,8 @@ class ICWP_WPSF_Wizard_Plugin extends ICWP_WPSF_Wizard_BaseWpsf {
|
|
451 |
$bSuccess = true;
|
452 |
$sMessage = __( 'Security Admin PIN setup was successful.', 'wp-simple-firewall' );
|
453 |
}
|
454 |
-
catch ( Exception $
|
455 |
-
$sMessage = __( $
|
456 |
}
|
457 |
}
|
458 |
|
@@ -762,7 +762,7 @@ class ICWP_WPSF_Wizard_Plugin extends ICWP_WPSF_Wizard_BaseWpsf {
|
|
762 |
->addWhereSearch( 'message', $sItem )
|
763 |
->query();
|
764 |
}
|
765 |
-
catch ( \Exception $
|
766 |
$aResults = [];
|
767 |
}
|
768 |
// $aResults = array_intersect_key( $aResults, array_flip( [ 'wp_username', 'message' ] ) );
|
365 |
*/
|
366 |
private function wizardLicense() {
|
367 |
|
368 |
+
$success = false;
|
369 |
|
370 |
$mod = $this->getCon()->getModule_License();
|
371 |
try {
|
372 |
+
$success = $mod->getLicenseHandler()
|
373 |
->verify( true )
|
374 |
->hasValidWorkingLicense();
|
375 |
+
if ( $success ) {
|
376 |
+
$msg = __( 'License was found and successfully installed.', 'wp-simple-firewall' );
|
377 |
}
|
378 |
else {
|
379 |
+
$msg = __( 'License could not be found.', 'wp-simple-firewall' );
|
380 |
}
|
381 |
}
|
382 |
+
catch ( Exception $e ) {
|
383 |
+
$msg = __( $e->getMessage(), 'wp-simple-firewall' );
|
384 |
}
|
385 |
|
386 |
return ( new \FernleafSystems\Utilities\Response() )
|
387 |
+
->setSuccessful( $success )
|
388 |
+
->setMessageText( $msg );
|
389 |
}
|
390 |
|
391 |
/**
|
403 |
->setMod( $this->getMod() )
|
404 |
->fromSite( $sMasterSiteUrl, $sSecretKey, $bEnabledNetwork );
|
405 |
}
|
406 |
+
catch ( Exception $e ) {
|
407 |
+
$sSiteResponse = $e->getMessage();
|
408 |
+
$nCode = $e->getCode();
|
409 |
}
|
410 |
|
411 |
$aErrors = [
|
451 |
$bSuccess = true;
|
452 |
$sMessage = __( 'Security Admin PIN setup was successful.', 'wp-simple-firewall' );
|
453 |
}
|
454 |
+
catch ( \Exception $e ) {
|
455 |
+
$sMessage = __( $e->getMessage(), 'wp-simple-firewall' );
|
456 |
}
|
457 |
}
|
458 |
|
762 |
->addWhereSearch( 'message', $sItem )
|
763 |
->query();
|
764 |
}
|
765 |
+
catch ( \Exception $e ) {
|
766 |
$aResults = [];
|
767 |
}
|
768 |
// $aResults = array_intersect_key( $aResults, array_flip( [ 'wp_username', 'message' ] ) );
|
templates/php/snippets/module-help-firewall.php
CHANGED
@@ -1,5 +1,5 @@
|
|
1 |
<h5>What is the Firewall?</h5>
|
2 |
<p>This is a big topic and is best covered in-detail in our helpdesk section.</p>
|
3 |
<p><a href="https://shsec.io/ak" target="_blank">
|
4 |
-
https://
|
5 |
</p>
|
1 |
<h5>What is the Firewall?</h5>
|
2 |
<p>This is a big topic and is best covered in-detail in our helpdesk section.</p>
|
3 |
<p><a href="https://shsec.io/ak" target="_blank">
|
4 |
+
https://support.getshieldsecurity.com/support/solutions/folders/3000000263</a> .
|
5 |
</p>
|
templates/php/snippets/plugin-vulnerability.php
DELETED
@@ -1,25 +0,0 @@
|
|
1 |
-
<tr class="icwp-plugin-vulnerability plugin-update-tr">
|
2 |
-
<td colspan="<?php echo $nColspan; ?>" class="colspanchange">
|
3 |
-
<p class="vuln-title"><?php echo $strings[ 'known_vuln' ]; ?></p>
|
4 |
-
<table>
|
5 |
-
<tr>
|
6 |
-
<th><?php echo $strings['name'];?></th>
|
7 |
-
<th><?php echo $strings['type'];?></th>
|
8 |
-
<th><?php echo $strings['fixed_versions'];?></th>
|
9 |
-
<th><?php echo $strings['more_info'];?></th>
|
10 |
-
</tr>
|
11 |
-
<?php foreach ( $vulns as $oVuln ) :
|
12 |
-
/** @var \FernleafSystems\Wordpress\Plugin\Shield\Scans\Wpv\WpVulnDb\WpVulnVO $oVuln */ ?>
|
13 |
-
<tr>
|
14 |
-
<td><?php echo $oVuln->title; ?></td>
|
15 |
-
<td><?php echo $oVuln->vuln_type; ?></td>
|
16 |
-
<td><?php echo $oVuln->fixed_in; ?></td>
|
17 |
-
<td><a href="<?php echo $oVuln->getUrl(); ?>" target="_blank">
|
18 |
-
<?php echo $strings[ 'more_info' ]; ?>
|
19 |
-
</a>
|
20 |
-
</td>
|
21 |
-
</tr>
|
22 |
-
<?php endforeach; ?>
|
23 |
-
</table>
|
24 |
-
</td>
|
25 |
-
</tr>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
templates/twig/components/options_form/main.twig
CHANGED
@@ -1,8 +1,8 @@
|
|
1 |
<form action="{{ form_action }}" method="post" class="icwpOptionsForm form" novalidate="novalidate"
|
2 |
autocomplete="off">
|
3 |
|
4 |
-
<div id="ModuleOptionsNav" class="insights-sub-nav"
|
5 |
-
<ul class="nav nav-tabs">
|
6 |
{% for opt_sect_key,opt_section in data.all_options %}
|
7 |
<li class="nav-item">
|
8 |
|
1 |
<form action="{{ form_action }}" method="post" class="icwpOptionsForm form" novalidate="novalidate"
|
2 |
autocomplete="off">
|
3 |
|
4 |
+
<div id="ModuleOptionsNav" class="insights-sub-nav" aria-orientation="horizontal">
|
5 |
+
<ul class="nav nav-tabs" role="tablist">
|
6 |
{% for opt_sect_key,opt_section in data.all_options %}
|
7 |
<li class="nav-item">
|
8 |
|
templates/twig/snippets/js/freshdesk_chatbot.twig
ADDED
@@ -0,0 +1,22 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<script> (function ( d, w, c ) {
|
2 |
+
if ( !d.getElementById( "spd-busns-spt" ) ) {
|
3 |
+
var n = d.getElementsByTagName( 'script' )[ 0 ],
|
4 |
+
s = d.createElement( 'script' );
|
5 |
+
var loaded = false;
|
6 |
+
s.id = "spd-busns-spt";
|
7 |
+
s.async = "async";
|
8 |
+
s.src = "https://cdn.freshbots.ai/assets/share/js/fbotsChat.min.js";
|
9 |
+
s.setAttribute( "data-prdct-hash", "72739a6abc259fc06bd3aae6d959178cf1ee37c7" );
|
10 |
+
s.setAttribute( "data-region", "us" );
|
11 |
+
s.setAttribute( "data-ext-client-id", "72d53bdc450411eb99f74a925608a346" );
|
12 |
+
if ( c ) {
|
13 |
+
s.onreadystatechange = s.onload = function () {
|
14 |
+
if ( !loaded ) {
|
15 |
+
c();
|
16 |
+
}
|
17 |
+
loaded = true;
|
18 |
+
};
|
19 |
+
}
|
20 |
+
n.parentNode.insertBefore( s, n );
|
21 |
+
}
|
22 |
+
})( document, window ); </script>
|
templates/twig/snippets/plugin_vulnerability.twig
ADDED
@@ -0,0 +1,21 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<tr class="icwp-plugin-vulnerability plugin-update-tr">
|
2 |
+
<td colspan="{{ vars.colspan }}" class="colspanchange">
|
3 |
+
<p class="vuln-title">{{ strings.known_vuln|raw }}</p>
|
4 |
+
<table>
|
5 |
+
<tr>
|
6 |
+
<th>{{ strings.name }}</th>
|
7 |
+
<th>{{ strings.type }}</th>
|
8 |
+
<th>{{ strings.fixed_versions }}</th>
|
9 |
+
<th>{{ strings.more_info }}</th>
|
10 |
+
</tr>
|
11 |
+
{% for vuln in vars.vulns %}
|
12 |
+
<tr>
|
13 |
+
<td>{{ vuln.title }}</td>
|
14 |
+
<td>{{ vuln.vuln_type }}</td>
|
15 |
+
<td>{{ vuln.fixed_in }}</td>
|
16 |
+
<td><a href="{{ vuln.url }}" target="_blank">{{ strings.more_info }}</a></td>
|
17 |
+
</tr>
|
18 |
+
{% endfor %}
|
19 |
+
</table>
|
20 |
+
</td>
|
21 |
+
</tr>
|
templates/twig/wpadmin_pages/insights/base.twig
CHANGED
@@ -46,7 +46,7 @@
|
|
46 |
} );
|
47 |
</script>
|
48 |
<script async src="https://cdn.announcekit.app/widget.js"></script>
|
49 |
-
{% include '/snippets/js/
|
50 |
{% endblock %}
|
51 |
|
52 |
{% block page_main %}
|
46 |
} );
|
47 |
</script>
|
48 |
<script async src="https://cdn.announcekit.app/widget.js"></script>
|
49 |
+
{% include '/snippets/js/freshdesk_chatbot.twig' %}
|
50 |
{% endblock %}
|
51 |
|
52 |
{% block page_main %}
|
templates/twig/wpadmin_pages/insights/docs/index.twig
CHANGED
@@ -7,14 +7,16 @@
|
|
7 |
<ul class="nav nav-tabs">
|
8 |
|
9 |
<li class="nav-item">
|
10 |
-
<a class="nav-link active" data-toggle="tab" role="tab" href="#tabUpdates"
|
|
|
11 |
{{ strings.tab_updates }}
|
12 |
</a>
|
13 |
</li>
|
14 |
|
15 |
{% if not flags.is_pro %}
|
16 |
<li class="nav-item">
|
17 |
-
<a class="nav-link" data-toggle="tab" role="tab" href="#tabFreeTrial"
|
|
|
18 |
{{ strings.tab_freetrial }}
|
19 |
</a>
|
20 |
</li>
|
7 |
<ul class="nav nav-tabs">
|
8 |
|
9 |
<li class="nav-item">
|
10 |
+
<a class="nav-link active" data-toggle="tab" role="tab" href="#tabUpdates"
|
11 |
+
data-bs-toggle="tab">
|
12 |
{{ strings.tab_updates }}
|
13 |
</a>
|
14 |
</li>
|
15 |
|
16 |
{% if not flags.is_pro %}
|
17 |
<li class="nav-item">
|
18 |
+
<a class="nav-link" data-toggle="tab" role="tab" href="#tabFreeTrial"
|
19 |
+
data-bs-toggle="tab">
|
20 |
{{ strings.tab_freetrial }}
|
21 |
</a>
|
22 |
</li>
|
templates/twig/wpadmin_pages/insights/importexport/index.twig
CHANGED
@@ -6,18 +6,21 @@
|
|
6 |
<div class="col insights-sub-nav insights_section">
|
7 |
<ul class="nav nav-tabs">
|
8 |
<li class="nav-item">
|
9 |
-
<a class="nav-link active" data-toggle="tab" role="tab"
|
|
|
10 |
{{ strings.tab_by_file }}
|
11 |
</a>
|
12 |
</li>
|
13 |
<li class="nav-item">
|
14 |
-
<a class="nav-link" data-toggle="tab" role="tab"
|
|
|
15 |
{{ strings.tab_by_site }}
|
16 |
</a>
|
17 |
</li>
|
18 |
</ul>
|
19 |
</div>
|
20 |
</div>
|
|
|
21 |
<div class="tab-content">
|
22 |
<div class="tab-pane fade show active" id="byFile" role="tabpanel" aria-labelledby="byFile">
|
23 |
<div class="row">
|
6 |
<div class="col insights-sub-nav insights_section">
|
7 |
<ul class="nav nav-tabs">
|
8 |
<li class="nav-item">
|
9 |
+
<a class="nav-link active" data-toggle="tab" role="tab" href="#byFile" aria-selected="true"
|
10 |
+
data-bs-toggle="tab">
|
11 |
{{ strings.tab_by_file }}
|
12 |
</a>
|
13 |
</li>
|
14 |
<li class="nav-item">
|
15 |
+
<a class="nav-link" data-toggle="tab" role="tab" href="#bySite"
|
16 |
+
data-bs-toggle="tab">
|
17 |
{{ strings.tab_by_site }}
|
18 |
</a>
|
19 |
</li>
|
20 |
</ul>
|
21 |
</div>
|
22 |
</div>
|
23 |
+
|
24 |
<div class="tab-content">
|
25 |
<div class="tab-pane fade show active" id="byFile" role="tabpanel" aria-labelledby="byFile">
|
26 |
<div class="row">
|
templates/twig/wpadmin_pages/insights/ips/index.twig
CHANGED
@@ -3,19 +3,22 @@
|
|
3 |
{% block page_main %}
|
4 |
<div class="row">
|
5 |
<div class="col insights-sub-nav insights_section">
|
6 |
-
<ul class="nav nav-tabs" id="TabsIps">
|
7 |
<li class="nav-item">
|
8 |
-
<a class="nav-link active" data-toggle="tab" role="tab" href="#ipanalysis"
|
|
|
9 |
{{ strings.tab_ip_analysis }}
|
10 |
</a>
|
11 |
</li>
|
12 |
<li class="nav-item">
|
13 |
-
<a class="nav-link" data-toggle="tab" role="tab" href="#blocklist"
|
|
|
14 |
{{ strings.tab_manage_block }}
|
15 |
</a>
|
16 |
</li>
|
17 |
<li class="nav-item">
|
18 |
-
<a class="nav-link" data-toggle="tab" role="tab" href="#allowedlist"
|
|
|
19 |
{{ strings.tab_manage_bypass }}
|
20 |
</a>
|
21 |
</li>
|
3 |
{% block page_main %}
|
4 |
<div class="row">
|
5 |
<div class="col insights-sub-nav insights_section">
|
6 |
+
<ul class="nav nav-tabs" id="TabsIps" role="tablist">
|
7 |
<li class="nav-item">
|
8 |
+
<a class="nav-link active" data-toggle="tab" role="tab" href="#ipanalysis"
|
9 |
+
data-bs-toggle="tab">
|
10 |
{{ strings.tab_ip_analysis }}
|
11 |
</a>
|
12 |
</li>
|
13 |
<li class="nav-item">
|
14 |
+
<a class="nav-link" data-toggle="tab" role="tab" href="#blocklist"
|
15 |
+
data-bs-toggle="tab">
|
16 |
{{ strings.tab_manage_block }}
|
17 |
</a>
|
18 |
</li>
|
19 |
<li class="nav-item">
|
20 |
+
<a class="nav-link" data-toggle="tab" role="tab" href="#allowedlist"
|
21 |
+
data-bs-toggle="tab">
|
22 |
{{ strings.tab_manage_bypass }}
|
23 |
</a>
|
24 |
</li>
|
templates/twig/wpadmin_pages/insights/ips/ip_analyse/ip_info.twig
CHANGED
@@ -5,22 +5,26 @@
|
|
5 |
|
6 |
<ul class="nav nav-pills card-header-pills mr-auto">
|
7 |
<li class="nav-item">
|
8 |
-
<a class="nav-link active" data-toggle="tab" role="tab" href="#tabIpInfoGeneral"
|
|
|
9 |
{{ strings.nav_general }}
|
10 |
</a>
|
11 |
</li>
|
12 |
<li class="nav-item">
|
13 |
-
<a class="nav-link" data-toggle="tab" role="tab" href="#tabIpInfoSessions"
|
|
|
14 |
{{ strings.nav_sessions }}
|
15 |
</a>
|
16 |
</li>
|
17 |
<li class="nav-item">
|
18 |
-
<a class="nav-link" data-toggle="tab" role="tab" href="#tabIpInfoAudit"
|
|
|
19 |
{{ strings.nav_audit }}
|
20 |
</a>
|
21 |
</li>
|
22 |
<li class="nav-item">
|
23 |
-
<a class="nav-link" data-toggle="tab" role="tab" href="#tabIpInfoTraffic"
|
|
|
24 |
{{ strings.nav_traffic }}
|
25 |
</a>
|
26 |
</li>
|
5 |
|
6 |
<ul class="nav nav-pills card-header-pills mr-auto">
|
7 |
<li class="nav-item">
|
8 |
+
<a class="nav-link active" data-toggle="tab" role="tab" href="#tabIpInfoGeneral"
|
9 |
+
data-bs-toggle="pill">
|
10 |
{{ strings.nav_general }}
|
11 |
</a>
|
12 |
</li>
|
13 |
<li class="nav-item">
|
14 |
+
<a class="nav-link" data-toggle="tab" role="tab" href="#tabIpInfoSessions"
|
15 |
+
data-bs-toggle="pill">
|
16 |
{{ strings.nav_sessions }}
|
17 |
</a>
|
18 |
</li>
|
19 |
<li class="nav-item">
|
20 |
+
<a class="nav-link" data-toggle="tab" role="tab" href="#tabIpInfoAudit"
|
21 |
+
data-bs-toggle="pill">
|
22 |
{{ strings.nav_audit }}
|
23 |
</a>
|
24 |
</li>
|
25 |
<li class="nav-item">
|
26 |
+
<a class="nav-link" data-toggle="tab" role="tab" href="#tabIpInfoTraffic"
|
27 |
+
data-bs-toggle="pill">
|
28 |
{{ strings.nav_traffic }}
|
29 |
</a>
|
30 |
</li>
|
templates/twig/wpadmin_pages/security_admin/index.twig
CHANGED
@@ -77,8 +77,9 @@
|
|
77 |
{% block inline_styles %}
|
78 |
<style>
|
79 |
#wpwrap {
|
80 |
-
background-image: url({{ imgs.
|
81 |
background-size:100%;
|
|
|
82 |
}
|
83 |
#wpcontent,
|
84 |
#wpbody-content {
|
@@ -88,5 +89,5 @@
|
|
88 |
{% endblock %}
|
89 |
|
90 |
{% block inline_scripts %}
|
91 |
-
{% include '/snippets/js/
|
92 |
{% endblock %}
|
77 |
{% block inline_styles %}
|
78 |
<style>
|
79 |
#wpwrap {
|
80 |
+
background-image: url({{ imgs.background_svg }});
|
81 |
background-size:100%;
|
82 |
+
background-repeat:no-repeat;
|
83 |
}
|
84 |
#wpcontent,
|
85 |
#wpbody-content {
|
89 |
{% endblock %}
|
90 |
|
91 |
{% block inline_scripts %}
|
92 |
+
{% include '/snippets/js/freshdesk_chatbot.twig' %}
|
93 |
{% endblock %}
|