Version Description
Download this release
Release Info
Developer | paultgoodchild |
Plugin | Shield Security for WordPress |
Version | 10.1.0 |
Comparing to | |
See all releases |
Code changes from version 10.0.3 to 10.1.0
- .htaccess +1 -1
- cl.json +71 -0
- filesnotfound.php +0 -24
- icwp-wpsf.php +1 -1
- plugin-spec.php +5 -4
- readme.txt +1 -1
- resources/css/bootstrap-select.min.css +2 -2
- resources/css/mainwp.css +12 -0
- resources/css/plugin.css +163 -37
- resources/images/bootstrap/arrow-down-up.svg +3 -0
- resources/images/bootstrap/award.svg +4 -0
- resources/images/bootstrap/binoculars.svg +3 -0
- resources/images/bootstrap/book-half.svg +3 -0
- resources/images/bootstrap/bug.svg +3 -0
- resources/images/bootstrap/chat-right-dots-fill.svg +3 -0
- resources/images/bootstrap/diagram-3.svg +3 -0
- resources/images/bootstrap/emoji-smile.svg +5 -0
- resources/images/bootstrap/link-45deg.svg +4 -0
- resources/images/bootstrap/pencil-square.svg +4 -0
- resources/images/bootstrap/people.svg +3 -0
- resources/images/bootstrap/person-badge.svg +4 -0
- resources/images/bootstrap/person-lines-fill.svg +3 -0
- resources/images/bootstrap/shield-shaded.svg +4 -0
- resources/images/bootstrap/sliders.svg +3 -0
- resources/images/bootstrap/stickies.svg +5 -0
- resources/images/bootstrap/sticky.svg +4 -0
- resources/images/bootstrap/stoplights.svg +5 -0
- resources/images/shield/dash-background.jpg +0 -0
- resources/js/bootstrap-select.min.js +2 -2
- resources/js/global-plugin.js +3 -3
- resources/js/plugin.js +3 -3
- resources/js/shield/ipanalyse.js +20 -7
- resources/js/shield/mainwp-extension.js +229 -0
- src/config/feature-admin_access_restriction.php +23 -15
- src/config/feature-audit_trail.php +31 -94
- src/config/feature-autoupdates.php +5 -1
- src/config/feature-comments_filter.php +1 -0
- src/config/feature-firewall.php +7 -0
- src/config/feature-hack_protect.php +3 -0
- src/config/feature-headers.php +1 -0
- src/config/feature-insights.php +0 -5
- src/config/feature-integrations.php +52 -0
- src/config/feature-ips.php +20 -0
- src/config/feature-lockdown.php +2 -0
- src/config/feature-login_protect.php +4 -0
- src/config/feature-plugin.php +38 -15
- src/config/feature-reporting.php +1 -0
- src/config/feature-traffic.php +28 -12
- src/config/feature-user_management.php +2 -0
- src/features/admin_access_restriction.php +3 -0
- src/features/audit_trail.php +4 -4
- src/features/autoupdates.php +3 -0
- src/features/base.php +97 -223
- src/features/base_wpsf.php +11 -7
- src/features/comments_filter.php +3 -0
- src/features/comms.php +4 -4
- src/features/email.php +3 -0
- src/features/events.php +4 -0
- src/features/firewall.php +3 -0
- src/features/hack_protect.php +14 -9
- src/features/headers.php +3 -0
- src/features/insights.php +14 -179
- src/features/ips.php +8 -46
- src/features/license.php +3 -0
- src/features/lockdown.php +3 -0
- src/features/login_protect.php +3 -11
- src/features/plugin.php +7 -0
- src/features/reporting.php +3 -0
- src/features/sessions.php +3 -0
- src/features/traffic.php +3 -0
- src/features/user_management.php +3 -0
- src/lib/src/Controller/Controller.php +175 -229
- src/lib/src/Controller/Utilities/Upgrade.php +1 -1
- src/lib/src/Crons/PluginCronsConsumer.php +17 -0
- src/lib/src/Databases/AdminNotes/Handler.php +1 -1
- src/lib/src/Databases/Base/BaseQuery.php +5 -5
- src/lib/src/Databases/Base/Handler.php +0 -58
- src/lib/src/Databases/Base/Insert.php +5 -10
- src/lib/src/Databases/IPs/CommonFilters.php +12 -6
- src/lib/src/Databases/IPs/Handler.php +2 -1
- src/lib/src/Databases/Reports/Common.php +6 -14
- src/lib/src/Databases/Reports/Handler.php +0 -1
- src/lib/src/Modules/AuditTrail/AjaxHandler.php +13 -19
- src/lib/src/Modules/AuditTrail/Auditors/Upgrades.php +88 -0
- src/lib/src/Modules/AuditTrail/Auditors/Users.php +27 -28
- src/lib/src/Modules/AuditTrail/Insights/OverviewCards.php +9 -36
- src/lib/src/Modules/AuditTrail/Lib/AuditWriter.php +5 -5
- src/lib/src/Modules/AuditTrail/ModCon.php +105 -0
- src/lib/src/Modules/AuditTrail/Options.php +20 -79
- src/lib/src/Modules/AuditTrail/Processor.php +65 -0
- src/lib/src/Modules/AuditTrail/Strings.php +6 -0
- src/lib/src/Modules/AuditTrail/UI.php +41 -30
- src/lib/src/Modules/AuditTrail/WpCli.php +1 -1
- src/lib/src/Modules/AuditTrail/WpCli/Display.php +5 -4
- src/lib/src/Modules/Autoupdates/AjaxHandler.php +2 -2
- src/lib/src/Modules/Autoupdates/ModCon.php +9 -0
- src/lib/src/Modules/Autoupdates/Options.php +2 -2
- src/lib/src/Modules/Autoupdates/Processor.php +453 -0
- src/lib/src/Modules/Autoupdates/UI.php +2 -3
- src/lib/src/Modules/Base/AdminNotices.php +39 -45
- src/lib/src/Modules/Base/AjaxHandler.php +95 -0
- src/lib/src/Modules/Base/AjaxHandlerBase.php +6 -1
- src/lib/src/Modules/Base/AjaxHandlerShield.php +5 -0
- src/lib/src/Modules/Base/BaseProcessor.php +12 -7
- src/lib/src/Modules/Base/BaseReporting.php +5 -0
- src/lib/src/Modules/Base/{BaseModCon.php → ModCon.php} +505 -590
- src/lib/src/Modules/Base/OneTimeExecute.php +0 -37
- src/lib/src/Modules/Base/Options.php +32 -51
- src/lib/src/Modules/Base/Processor.php +60 -0
- src/lib/src/Modules/Base/Reporting.php +49 -0
- src/lib/src/Modules/Base/ShieldOptions.php +5 -0
- src/lib/src/Modules/Base/ShieldUI.php +15 -6
- src/lib/src/Modules/Base/Strings.php +30 -4
- src/lib/src/Modules/Base/UI.php +38 -16
- src/lib/src/Modules/Base/Upgrade.php +6 -7
- src/lib/src/Modules/Base/WpCli.php +3 -3
- src/lib/src/Modules/Base/WpCli/BaseWpCliCmd.php +1 -1
- src/lib/src/Modules/BaseShield/AjaxHandler.php +71 -0
- src/lib/src/Modules/BaseShield/ModCon.php +256 -0
- src/lib/src/Modules/BaseShield/Options.php +30 -0
- src/lib/src/Modules/BaseShield/{ShieldModCon.php → Processor.php} +2 -2
- src/lib/src/Modules/BaseShield/ShieldProcessor.php +5 -35
- src/lib/src/Modules/BaseShield/UI.php +66 -0
- src/lib/src/Modules/CommentsFilter/AdminNotices.php +17 -20
- src/lib/src/Modules/CommentsFilter/AjaxHandler.php +1 -1
- src/lib/src/Modules/CommentsFilter/Forms/Gasp.php +123 -0
- src/lib/src/Modules/CommentsFilter/Forms/GoogleRecaptcha.php +44 -0
- src/lib/src/Modules/CommentsFilter/Insights/OverviewCards.php +3 -3
- src/lib/src/Modules/CommentsFilter/ModCon.php +95 -0
- src/lib/src/Modules/CommentsFilter/Options.php +9 -30
- src/lib/src/Modules/CommentsFilter/Processor.php +63 -0
- src/lib/src/Modules/CommentsFilter/Scan/Human.php +12 -12
- src/lib/src/Modules/CommentsFilter/Scan/Scanner.php +11 -5
- src/lib/src/Modules/CommentsFilter/Strings.php +5 -5
- src/lib/src/Modules/CommentsFilter/UI.php +3 -2
- src/lib/src/Modules/Comms/ModCon.php +13 -0
- src/lib/src/Modules/Comms/Options.php +2 -2
- src/lib/src/Modules/Comms/Processor.php +9 -0
- src/lib/src/Modules/Email/ModCon.php +9 -0
- src/lib/src/Modules/Email/Options.php +3 -3
- src/lib/src/Modules/Email/Processor.php +205 -0
- src/lib/src/Modules/Email/Strings.php +1 -1
- src/lib/src/Modules/Events/AjaxHandler.php +8 -8
- src/lib/src/Modules/Events/Charts/BuildData.php +11 -10
- src/lib/src/Modules/Events/Consolidate/ConsolidateAllEvents.php +36 -35
- src/lib/src/Modules/Events/Lib/EventsListener.php +5 -5
- src/lib/src/Modules/Events/Lib/EventsService.php +31 -26
- src/lib/src/Modules/Events/Lib/Reports/KeyStats.php +4 -4
- src/lib/src/Modules/Events/Lib/Reports/ScanRepairs.php +4 -4
- src/lib/src/Modules/Events/Lib/StatsWriter.php +3 -3
- src/lib/src/Modules/Events/ModCon.php +23 -0
- src/lib/src/Modules/Events/Options.php +3 -10
- src/lib/src/Modules/Events/Processor.php +92 -0
- src/lib/src/Modules/Events/Reporting.php +2 -2
- src/lib/src/Modules/Events/Strings.php +7 -5
- src/lib/src/Modules/Firewall/Insights/OverviewCards.php +2 -1
- src/lib/src/Modules/Firewall/ModCon.php +53 -0
- src/lib/src/Modules/Firewall/Options.php +2 -2
- src/lib/src/Modules/Firewall/Processor.php +435 -0
- src/lib/src/Modules/Firewall/Strings.php +1 -1
- src/lib/src/Modules/Firewall/UI.php +2 -2
- src/lib/src/Modules/HackGuard/AjaxHandler.php +85 -97
- src/lib/src/Modules/HackGuard/Insights/OverviewCards.php +7 -7
- src/lib/src/Modules/HackGuard/Lib/FileLocker/FileLockerController.php +15 -25
- src/lib/src/Modules/HackGuard/Lib/FileLocker/Ops/Accept.php +5 -4
- src/lib/src/Modules/HackGuard/Lib/FileLocker/Ops/AssessLocks.php +9 -8
- src/lib/src/Modules/HackGuard/Lib/FileLocker/Ops/CreateFileLocks.php +11 -10
- src/lib/src/Modules/HackGuard/Lib/FileLocker/Ops/DeleteFileLock.php +4 -3
- src/lib/src/Modules/HackGuard/Lib/FileLocker/Ops/LoadFileLocks.php +5 -5
- src/lib/src/Modules/HackGuard/Lib/FileLocker/Ops/PerformAction.php +4 -3
- src/lib/src/Modules/HackGuard/Lib/FileLocker/Ops/Restore.php +4 -3
- src/lib/src/Modules/HackGuard/Lib/Reports/FileLockerAlerts.php +6 -6
- src/lib/src/Modules/HackGuard/Lib/Reports/Query/ScanCounts.php +88 -0
- src/lib/src/Modules/HackGuard/Lib/Reports/ScanAlerts.php +21 -47
- src/lib/src/Modules/HackGuard/Lib/Snapshots/StoreAction/BaseAction.php +4 -3
- src/lib/src/Modules/HackGuard/Lib/Snapshots/StoreAction/Build.php +2 -3
- src/lib/src/Modules/HackGuard/Lib/Snapshots/StoreAction/CleanAll.php +4 -3
- src/lib/src/Modules/HackGuard/Lib/Snapshots/StoreAction/DeleteAll.php +4 -3
- src/lib/src/Modules/HackGuard/ModCon.php +253 -0
- src/lib/src/Modules/HackGuard/Options.php +33 -94
- src/lib/src/Modules/HackGuard/Processor.php +45 -0
- src/lib/src/Modules/HackGuard/Reporting.php +2 -2
- src/lib/src/Modules/HackGuard/Scan/Controller/Base.php +103 -96
- src/lib/src/Modules/HackGuard/Scan/Controller/BaseForAssets.php +5 -5
- src/lib/src/Modules/HackGuard/Scan/Controller/Mal.php +3 -3
- src/lib/src/Modules/HackGuard/Scan/Controller/Ptg.php +25 -28
- src/lib/src/Modules/HackGuard/Scan/Controller/Ufc.php +3 -3
- src/lib/src/Modules/HackGuard/Scan/Controller/Wcf.php +4 -4
- src/lib/src/Modules/HackGuard/Scan/Controller/Wpv.php +46 -0
- src/lib/src/Modules/HackGuard/Scan/Queue/BuildScanAction.php +7 -6
- src/lib/src/Modules/HackGuard/Scan/Queue/CompleteQueue.php +2 -1
- src/lib/src/Modules/HackGuard/Scan/Queue/Controller.php +22 -23
- src/lib/src/Modules/HackGuard/Scan/Queue/QueueProcessor.php +4 -3
- src/lib/src/Modules/HackGuard/Scan/Queue/ScanExecute.php +4 -4
- src/lib/src/Modules/HackGuard/Scan/Queue/ScanInitiate.php +10 -9
- src/lib/src/Modules/HackGuard/Scan/ScanActionFromSlug.php +20 -27
- src/lib/src/Modules/HackGuard/Scan/ScansController.php +185 -0
- src/lib/src/Modules/HackGuard/Scan/Utilities/PtgAddReinstallLinks.php +112 -0
- src/lib/src/Modules/HackGuard/Scan/Utilities/WpvAddPluginRows.php +151 -0
- src/lib/src/Modules/HackGuard/Strings.php +94 -99
- src/lib/src/Modules/HackGuard/UI.php +58 -149
- src/lib/src/Modules/Headers/Insights/OverviewCards.php +1 -1
- src/lib/src/Modules/Headers/ModCon.php +90 -0
- src/lib/src/Modules/Headers/Options.php +5 -7
- src/lib/src/Modules/Headers/Processor.php +199 -0
- src/lib/src/Modules/Headers/UI.php +2 -2
- src/lib/src/Modules/IPs/AdminNotices.php +15 -22
- src/lib/src/Modules/IPs/AjaxHandler.php +118 -27
- src/lib/src/Modules/IPs/Components/ImportIpsFromFile.php +12 -15
- src/lib/src/Modules/IPs/Components/ProcessOffense.php +3 -3
- src/lib/src/Modules/IPs/Components/QueryIpBlock.php +8 -8
- src/lib/src/Modules/IPs/Components/QueryRemainingOffenses.php +8 -9
- src/lib/src/Modules/IPs/Components/UnblockIpByFlag.php +8 -8
- src/lib/src/Modules/IPs/Lib/AutoUnblock.php +4 -3
- src/lib/src/Modules/IPs/Lib/BlacklistHandler.php +2 -2
- src/lib/src/Modules/IPs/Lib/BlockRequest.php +12 -5
- src/lib/src/Modules/IPs/Lib/IpAnalyse/BuildDisplay.php +31 -4
- src/lib/src/Modules/IPs/Lib/OffenseTracker.php +3 -3
- src/lib/src/Modules/IPs/Lib/Ops/AddIp.php +29 -28
- src/lib/src/Modules/IPs/Lib/Ops/DeleteIp.php +9 -18
- src/lib/src/Modules/IPs/Lib/ProcessOffenses.php +2 -2
- src/lib/src/Modules/IPs/ModCon.php +121 -0
- src/lib/src/Modules/IPs/Options.php +2 -10
- src/lib/src/Modules/IPs/Processor.php +14 -0
- src/lib/src/Modules/IPs/Strings.php +4 -4
- src/lib/src/Modules/IPs/UI.php +21 -4
- src/lib/src/Modules/IPs/Upgrade.php +15 -6
- src/lib/src/Modules/IPs/WpCli.php +1 -1
- src/lib/src/Modules/IPs/WpCli/Add.php +3 -3
- src/lib/src/Modules/IPs/WpCli/Enumerate.php +4 -3
- src/lib/src/Modules/IPs/WpCli/Remove.php +5 -5
- src/lib/src/Modules/Insights/ModCon.php +230 -0
- src/lib/src/Modules/Insights/Options.php +3 -3
- src/lib/src/Modules/Insights/UI.php +300 -34
- src/lib/src/Modules/Integrations/Lib/MainWP/Client/Actions/ApiActionInit.php +50 -0
- src/lib/src/Modules/Integrations/Lib/MainWP/Client/Actions/Init.php +61 -0
- src/lib/src/Modules/Integrations/Lib/MainWP/Client/Actions/Sync.php +51 -0
- src/lib/src/Modules/Integrations/Lib/MainWP/Client/Auth/ReproduceClientAuthByKey.php +32 -0
- src/lib/src/Modules/Integrations/Lib/MainWP/Common/Consumers/MWPSiteConsumer.php +26 -0
- src/lib/src/Modules/Integrations/Lib/MainWP/Common/MWPExtensionVO.php +48 -0
- src/lib/src/Modules/Integrations/Lib/MainWP/Common/MWPSiteVO.php +59 -0
- src/lib/src/Modules/Integrations/Lib/MainWP/Common/MainWPVO.php +56 -0
- src/lib/src/Modules/Integrations/Lib/MainWP/Common/SyncMetaVO.php +20 -0
- src/lib/src/Modules/Integrations/Lib/MainWP/Common/SyncVO.php +37 -0
- src/lib/src/Modules/Integrations/Lib/MainWP/Controller.php +88 -0
- src/lib/src/Modules/Integrations/Lib/MainWP/Server/Actions/Action.php +42 -0
- src/lib/src/Modules/Integrations/Lib/MainWP/Server/Actions/ShieldApiAction.php +29 -0
- src/lib/src/Modules/Integrations/Lib/MainWP/Server/Actions/ShieldPluginAction.php +128 -0
- src/lib/src/Modules/Integrations/Lib/MainWP/Server/Ajax/AjaxHandlerMainwp.php +65 -0
- src/lib/src/Modules/Integrations/Lib/MainWP/Server/Ajax/PerformSiteAction.php +119 -0
- src/lib/src/Modules/Integrations/Lib/MainWP/Server/Data/ClientPluginStatus.php +105 -0
- src/lib/src/Modules/Integrations/Lib/MainWP/Server/Data/LoadShieldSyncData.php +23 -0
- src/lib/src/Modules/Integrations/Lib/MainWP/Server/Data/SyncHandler.php +36 -0
- src/lib/src/Modules/Integrations/Lib/MainWP/Server/Init.php +65 -0
- src/lib/src/Modules/Integrations/Lib/MainWP/Server/UI/BaseRender.php +37 -0
- src/lib/src/Modules/Integrations/Lib/MainWP/Server/UI/ExtensionSettingsPage.php +131 -0
- src/lib/src/Modules/Integrations/Lib/MainWP/Server/UI/ManageSites/SitesListTableHandler.php +95 -0
- src/lib/src/Modules/Integrations/Lib/MainWP/Server/UI/PageRender/MwpOutOfDate.php +30 -0
- src/lib/src/Modules/Integrations/Lib/MainWP/Server/UI/PageRender/NotShieldPro.php +24 -0
- src/lib/src/Modules/Integrations/Lib/MainWP/Server/UI/PageRender/PluginOutOfDate.php +25 -0
- src/lib/src/Modules/Integrations/Lib/MainWP/Server/UI/PageRender/SitesList.php +138 -0
- src/lib/src/Modules/Integrations/ModCon.php +22 -0
- src/lib/src/Modules/Integrations/Options.php +12 -0
- src/lib/src/Modules/Integrations/Processor.php +15 -0
- src/lib/src/Modules/Integrations/Strings.php +70 -0
- src/lib/src/Modules/License/AdminNotices.php +16 -23
- src/lib/src/Modules/License/AjaxHandler.php +4 -4
- src/lib/src/Modules/License/Lib/LicenseEmails.php +27 -26
- src/lib/src/Modules/License/Lib/LicenseHandler.php +11 -10
- src/lib/src/Modules/License/Lib/Verify.php +14 -14
- src/lib/src/Modules/License/ModCon.php +66 -0
- src/lib/src/Modules/License/Options.php +2 -2
- src/lib/src/Modules/License/Processor.php +14 -0
- src/lib/src/Modules/License/UI.php +32 -13
- src/lib/src/Modules/License/WpCli.php +1 -1
- src/lib/src/Modules/License/WpCli/License.php +10 -9
- src/lib/src/Modules/Lockdown/Insights/OverviewCards.php +4 -4
- src/lib/src/Modules/Lockdown/ModCon.php +31 -0
- src/lib/src/Modules/Lockdown/Options.php +2 -2
- src/lib/src/Modules/Lockdown/Processor.php +134 -0
- src/lib/src/Modules/Lockdown/UI.php +2 -2
- src/lib/src/Modules/LoginGuard/AdminNotices.php +20 -29
- src/lib/src/Modules/LoginGuard/AjaxHandler.php +35 -47
- src/lib/src/Modules/LoginGuard/Insights/OverviewCards.php +3 -3
- src/lib/src/Modules/LoginGuard/Lib/AntiBot/AntibotSetup.php +4 -4
- src/lib/src/Modules/LoginGuard/Lib/AntiBot/FormProviders/WooCommerce.php +10 -10
- src/lib/src/Modules/LoginGuard/Lib/AntiBot/ProtectionProviders/GaspJs.php +10 -10
- src/lib/src/Modules/LoginGuard/Lib/AntiBot/ProtectionProviders/GoogleRecaptcha.php +4 -3
- src/lib/src/Modules/LoginGuard/Lib/CooldownRedirect.php +5 -4
- src/lib/src/Modules/LoginGuard/Lib/Rename/RenameLogin.php +244 -0
- src/lib/src/Modules/LoginGuard/Lib/TwoFactor/LoginIntentPage.php +19 -19
- src/lib/src/Modules/LoginGuard/Lib/TwoFactor/MfaController.php +95 -140
- src/lib/src/Modules/LoginGuard/Lib/TwoFactor/Profiles/CustomForms.php +2 -2
- src/lib/src/Modules/LoginGuard/Lib/TwoFactor/Provider/Email.php +1 -2
- src/lib/src/Modules/LoginGuard/Lib/TwoFactor/ValidateLoginIntentRequest.php +22 -22
- src/lib/src/Modules/LoginGuard/ModCon.php +280 -0
- src/lib/src/Modules/LoginGuard/Options.php +40 -108
- src/lib/src/Modules/LoginGuard/Processor.php +28 -0
- src/lib/src/Modules/LoginGuard/Strings.php +93 -97
- src/lib/src/Modules/LoginGuard/UI.php +2 -2
- src/lib/src/Modules/ModConsumer.php +2 -2
- src/lib/src/Modules/Plugin/AdminNotices.php +61 -124
- src/lib/src/Modules/Plugin/AjaxHandler.php +78 -114
- src/lib/src/Modules/Plugin/Components/BadgeWidget.php +6 -4
- src/lib/src/Modules/Plugin/Components/PluginBadge.php +58 -50
- src/lib/src/Modules/Plugin/Components/TestForCloudflareAPO.php +1 -1
- src/lib/src/Modules/Plugin/Debug.php +10 -1
- src/lib/src/Modules/Plugin/Insights/AdminNotes.php +3 -6
- src/lib/src/Modules/Plugin/Insights/DashboardCards.php +410 -0
- src/lib/src/Modules/Plugin/Insights/OverviewCards.php +3 -2
- src/lib/src/Modules/Plugin/Lib/Captcha/CheckCaptchaSettings.php +14 -14
- src/lib/src/Modules/Plugin/Lib/ImportExport/Export.php +9 -9
- src/lib/src/Modules/Plugin/Lib/ImportExport/Import.php +10 -10
- src/lib/src/Modules/Plugin/Lib/ImportExport/ImportExportController.php +14 -11
- src/lib/src/Modules/Plugin/Lib/ImportExport/NotifyWhitelist.php +5 -4
- src/lib/src/Modules/Plugin/Lib/PluginTelemetry.php +166 -0
- src/lib/src/Modules/Plugin/ModCon.php +549 -0
- src/lib/src/Modules/Plugin/Options.php +4 -34
- src/lib/src/Modules/Plugin/Processor.php +90 -0
- src/lib/src/Modules/Plugin/Strings.php +23 -9
- src/lib/src/Modules/Plugin/UI.php +26 -10
- src/lib/src/Modules/Plugin/Upgrade.php +4 -4
- src/lib/src/Modules/Plugin/WpCli.php +1 -1
- src/lib/src/Modules/Reporting/Debug.php +3 -0
- src/lib/src/Modules/Reporting/Lib/ReportingController.php +4 -2
- src/lib/src/Modules/Reporting/Lib/Reports/BaseReporter.php +3 -3
- src/lib/src/Modules/Reporting/Lib/Reports/Build/BaseBuilder.php +7 -7
- src/lib/src/Modules/Reporting/Lib/Reports/Build/BuilderAlerts.php +10 -11
- src/lib/src/Modules/Reporting/Lib/Reports/Build/BuilderInfo.php +10 -11
- src/lib/src/Modules/Reporting/Lib/Reports/CreateReportVO.php +41 -41
- src/lib/src/Modules/Reporting/ModCon.php +25 -0
- src/lib/src/Modules/Reporting/Options.php +5 -13
- src/lib/src/Modules/Reporting/Processor.php +14 -0
- src/lib/src/Modules/Reporting/UI.php +2 -2
- src/lib/src/Modules/SecurityAdmin/AdminNotices.php +17 -27
- src/lib/src/Modules/SecurityAdmin/AjaxHandler.php +15 -20
- src/lib/src/Modules/SecurityAdmin/Insights/OverviewCards.php +1 -1
- src/lib/src/Modules/SecurityAdmin/Lib/Actions/RemoveSecAdmin.php +37 -46
- src/lib/src/Modules/SecurityAdmin/Lib/Actions/SetSecAdminPin.php +4 -4
- src/lib/src/Modules/SecurityAdmin/Lib/WhiteLabel/ApplyLabels.php +1 -1
- src/lib/src/Modules/SecurityAdmin/ModCon.php +384 -0
- src/lib/src/Modules/SecurityAdmin/Options.php +3 -3
- src/lib/src/Modules/SecurityAdmin/Processor.php +111 -0
.htaccess
CHANGED
@@ -1,4 +1,4 @@
|
|
1 |
Order Allow,Deny
|
2 |
-
<FilesMatch "^.*\.(css|js|png)$">
|
3 |
Allow from all
|
4 |
</FilesMatch>
|
1 |
Order Allow,Deny
|
2 |
+
<FilesMatch "^.*\.(css|js|png|jpg|svg)$">
|
3 |
Allow from all
|
4 |
</FilesMatch>
|
cl.json
CHANGED
@@ -1,4 +1,75 @@
|
|
1 |
{
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2 |
"10.0": {
|
3 |
"version": "10.0",
|
4 |
"released_at": 1603281600,
|
1 |
{
|
2 |
+
"10.1": {
|
3 |
+
"version": "10.1",
|
4 |
+
"released_at": 1605606920,
|
5 |
+
"hrefs": {
|
6 |
+
"release": "https://shsec.io/shieldrelease101",
|
7 |
+
"upgrade": "https://shsec.io/shieldupgradeguide101"
|
8 |
+
},
|
9 |
+
"href": "https://shsec.io/",
|
10 |
+
"title": "Enhanced Dashboard + MainWP Integration",
|
11 |
+
"description": [
|
12 |
+
"We're continuing our improvements to the Shield UI with a brand new Dashboard.",
|
13 |
+
"The Dashboard is your primary launchpad for all things WordPress Security and Shield.",
|
14 |
+
"We're also delighted to bring our first major 3rd party integration - MainWP."
|
15 |
+
],
|
16 |
+
"items": [
|
17 |
+
{
|
18 |
+
"type": "new",
|
19 |
+
"pro_only": false,
|
20 |
+
"title": "Brand New Shield Dashboard",
|
21 |
+
"description": [
|
22 |
+
"With the help of some feedback from clients, we've made significant enhancements to the Shield UI.",
|
23 |
+
"A brand-new Shield dashboard centralises everything related to Shield giving you a consistent, clean launchpad to perform security tasks."
|
24 |
+
]
|
25 |
+
},
|
26 |
+
{
|
27 |
+
"type": "new",
|
28 |
+
"pro_only": true,
|
29 |
+
"title": "MainWP Integration/Extension",
|
30 |
+
"description": [
|
31 |
+
"You can now manage your Shield Security plugin directly from within your MainWP WordPress management control panel.",
|
32 |
+
"The Shield Security Extension page will highlight all sites with any scan issues that need your attention.",
|
33 |
+
"For now, the functionality is limited to installing, activating and deactivating the Shield plugin."
|
34 |
+
],
|
35 |
+
"href": "https://shsec.io/ir"
|
36 |
+
},
|
37 |
+
{
|
38 |
+
"type": "new",
|
39 |
+
"pro_only": false,
|
40 |
+
"title": "IP Analyse Tool Enhancements",
|
41 |
+
"description": [
|
42 |
+
"Based on customer feedback we've added links to the IP Analyse tool to let you quickly perform blocks or bypass on an IP.",
|
43 |
+
"The identification of a 'known' IP address now also draws information from the IP Bypass labels."
|
44 |
+
]
|
45 |
+
},
|
46 |
+
{
|
47 |
+
"type": "improved",
|
48 |
+
"pro_only": false,
|
49 |
+
"title": "Enhanced Plugin Badge",
|
50 |
+
"description": [
|
51 |
+
"Based on customer feedback we've added the ability to customize the plugin badge based on Whitelabel settings.",
|
52 |
+
"You'll may also use a WordPress filter to make fine adjustments to settings and styles of the badge."
|
53 |
+
],
|
54 |
+
"href": "https://shsec.io/is"
|
55 |
+
},
|
56 |
+
{
|
57 |
+
"type": "improved",
|
58 |
+
"pro_only": false,
|
59 |
+
"title": "Huge Codebase Refactor",
|
60 |
+
"description": [
|
61 |
+
"With our earlier move to PHP 7.0, we're continuing with our codebase cleanup and optimisations."
|
62 |
+
]
|
63 |
+
},
|
64 |
+
{
|
65 |
+
"type": "improved",
|
66 |
+
"title": "Shield Overview Styles",
|
67 |
+
"description": [
|
68 |
+
"With some feedback and suggestions provided by clients, we've improved our Shield Overview design."
|
69 |
+
]
|
70 |
+
}
|
71 |
+
]
|
72 |
+
},
|
73 |
"10.0": {
|
74 |
"version": "10.0",
|
75 |
"released_at": 1603281600,
|
filesnotfound.php
DELETED
@@ -1,24 +0,0 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
foreach (
|
4 |
-
[ 'ICWP_WPSF_FeatureHandler_Base', 'ICWP_WPSF_FeatureHandler_BaseWpsf', ] as $sClass
|
5 |
-
) {
|
6 |
-
if ( !@class_exists( $sClass ) ) {
|
7 |
-
add_action( 'admin_notices', 'icwp_wpsf_checkfilesnotfound' );
|
8 |
-
add_action( 'network_admin_notices', 'icwp_wpsf_checkfilesnotfound' );
|
9 |
-
return false;
|
10 |
-
}
|
11 |
-
}
|
12 |
-
|
13 |
-
function icwp_wpsf_checkfilesnotfound() {
|
14 |
-
echo sprintf( '<div class="error"><h4>%s</h4><p>%s</p></div>',
|
15 |
-
'Shield Security Plugin - Broken Installation',
|
16 |
-
implode( '<br/>', [
|
17 |
-
'It appears the Shield Security plugin was not upgraded/installed correctly.',
|
18 |
-
"We run a quick check to make sure certain important files are present in-case a faulty installation breaks your site.",
|
19 |
-
'Try refreshing this page, and if you continue to see this notice, we recommend that you reinstall the Shield Security plugin.'
|
20 |
-
] )
|
21 |
-
);
|
22 |
-
}
|
23 |
-
|
24 |
-
return true;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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.0
|
7 |
* Text Domain: wp-simple-firewall
|
8 |
* Domain Path: /languages
|
9 |
* Author: Shield Security
|
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.1.0
|
7 |
* Text Domain: wp-simple-firewall
|
8 |
* Domain Path: /languages
|
9 |
* Author: Shield Security
|
plugin-spec.php
CHANGED
@@ -1,8 +1,8 @@
|
|
1 |
{
|
2 |
"properties": {
|
3 |
-
"version": "10.0
|
4 |
-
"release_timestamp":
|
5 |
-
"build": "
|
6 |
"slug_parent": "icwp",
|
7 |
"slug_plugin": "wpsf",
|
8 |
"human_name": "Shield",
|
@@ -114,7 +114,8 @@
|
|
114 |
"9.0.5",
|
115 |
"9.1.1",
|
116 |
"9.2.0",
|
117 |
-
"9.2.2"
|
|
|
118 |
],
|
119 |
"action_links": {
|
120 |
"remove": null,
|
1 |
{
|
2 |
"properties": {
|
3 |
+
"version": "10.1.0",
|
4 |
+
"release_timestamp": 1605606920,
|
5 |
+
"build": "202011.1701",
|
6 |
"slug_parent": "icwp",
|
7 |
"slug_plugin": "wpsf",
|
8 |
"human_name": "Shield",
|
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,
|
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.5
|
11 |
-
Stable tag: 10.0
|
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.5
|
11 |
+
Stable tag: 10.1.0
|
12 |
|
13 |
The highest rated WordPress Security plugin, delivering unparalleled, all-in-one protection for you and your customers.
|
14 |
|
resources/css/bootstrap-select.min.css
CHANGED
@@ -1,6 +1,6 @@
|
|
1 |
/*!
|
2 |
-
* Bootstrap-select v1.13.
|
3 |
*
|
4 |
* Copyright 2012-2020 SnapAppointments, LLC
|
5 |
* Licensed under MIT (https://github.com/snapappointments/bootstrap-select/blob/master/LICENSE)
|
6 |
-
*/@-webkit-keyframes bs-notify-fadeOut{0%{opacity:.9}100%{opacity:0}}@-o-keyframes bs-notify-fadeOut{0%{opacity:.9}100%{opacity:0}}@keyframes bs-notify-fadeOut{0%{opacity:.9}100%{opacity:0}}.bootstrap-select>select.bs-select-hidden,select.bs-select-hidden,select.selectpicker{display:none!important}.bootstrap-select{width:220px\0;vertical-align:middle}.bootstrap-select>.dropdown-toggle{position:relative;width:100%;text-align:right;white-space:nowrap;display:-webkit-inline-box;display:-webkit-inline-flex;display:-ms-inline-flexbox;display:inline-flex;-webkit-box-align:center;-webkit-align-items:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:justify;-webkit-justify-content:space-between;-ms-flex-pack:justify;justify-content:space-between}.bootstrap-select>.dropdown-toggle:after{margin-top:-1px}.bootstrap-select>.dropdown-toggle.bs-placeholder,.bootstrap-select>.dropdown-toggle.bs-placeholder:active,.bootstrap-select>.dropdown-toggle.bs-placeholder:focus,.bootstrap-select>.dropdown-toggle.bs-placeholder:hover{color:#999}.bootstrap-select>.dropdown-toggle.bs-placeholder.btn-danger,.bootstrap-select>.dropdown-toggle.bs-placeholder.btn-danger:active,.bootstrap-select>.dropdown-toggle.bs-placeholder.btn-danger:focus,.bootstrap-select>.dropdown-toggle.bs-placeholder.btn-danger:hover,.bootstrap-select>.dropdown-toggle.bs-placeholder.btn-dark,.bootstrap-select>.dropdown-toggle.bs-placeholder.btn-dark:active,.bootstrap-select>.dropdown-toggle.bs-placeholder.btn-dark:focus,.bootstrap-select>.dropdown-toggle.bs-placeholder.btn-dark:hover,.bootstrap-select>.dropdown-toggle.bs-placeholder.btn-info,.bootstrap-select>.dropdown-toggle.bs-placeholder.btn-info:active,.bootstrap-select>.dropdown-toggle.bs-placeholder.btn-info:focus,.bootstrap-select>.dropdown-toggle.bs-placeholder.btn-info:hover,.bootstrap-select>.dropdown-toggle.bs-placeholder.btn-primary,.bootstrap-select>.dropdown-toggle.bs-placeholder.btn-primary:active,.bootstrap-select>.dropdown-toggle.bs-placeholder.btn-primary:focus,.bootstrap-select>.dropdown-toggle.bs-placeholder.btn-primary:hover,.bootstrap-select>.dropdown-toggle.bs-placeholder.btn-secondary,.bootstrap-select>.dropdown-toggle.bs-placeholder.btn-secondary:active,.bootstrap-select>.dropdown-toggle.bs-placeholder.btn-secondary:focus,.bootstrap-select>.dropdown-toggle.bs-placeholder.btn-secondary:hover,.bootstrap-select>.dropdown-toggle.bs-placeholder.btn-success,.bootstrap-select>.dropdown-toggle.bs-placeholder.btn-success:active,.bootstrap-select>.dropdown-toggle.bs-placeholder.btn-success:focus,.bootstrap-select>.dropdown-toggle.bs-placeholder.btn-success:hover{color:rgba(255,255,255,.5)}.bootstrap-select>select{position:absolute!important;bottom:0;left:50%;display:block!important;width:.5px!important;height:100%!important;padding:0!important;opacity:0!important;border:none;z-index:0!important}.bootstrap-select>select.mobile-device{top:0;left:0;display:block!important;width:100%!important;z-index:2!important}.bootstrap-select.is-invalid .dropdown-toggle,.error .bootstrap-select .dropdown-toggle,.has-error .bootstrap-select .dropdown-toggle,.was-validated .bootstrap-select select:invalid+.dropdown-toggle{border-color:#b94a48}.bootstrap-select.is-valid .dropdown-toggle,.was-validated .bootstrap-select select:valid+.dropdown-toggle{border-color:#28a745}.bootstrap-select.fit-width{width:auto!important}.bootstrap-select:not([class*=col-]):not([class*=form-control]):not(.input-group-btn){width:220px}.bootstrap-select .dropdown-toggle:focus,.bootstrap-select>select.mobile-device:focus+.dropdown-toggle{outline:thin dotted #333!important;outline:5px auto -webkit-focus-ring-color!important;outline-offset:-2px}.bootstrap-select.form-control{margin-bottom:0;padding:0;border:none;height:auto}:not(.input-group)>.bootstrap-select.form-control:not([class*=col-]){width:100%}.bootstrap-select.form-control.input-group-btn{float:none;z-index:auto}.form-inline .bootstrap-select,.form-inline .bootstrap-select.form-control:not([class*=col-]){width:auto}.bootstrap-select:not(.input-group-btn),.bootstrap-select[class*=col-]{float:none;display:inline-block;margin-left:0}.bootstrap-select.dropdown-menu-right,.bootstrap-select[class*=col-].dropdown-menu-right,.row .bootstrap-select[class*=col-].dropdown-menu-right{float:right}.form-group .bootstrap-select,.form-horizontal .bootstrap-select,.form-inline .bootstrap-select{margin-bottom:0}.form-group-lg .bootstrap-select.form-control,.form-group-sm .bootstrap-select.form-control{padding:0}.form-group-lg .bootstrap-select.form-control .dropdown-toggle,.form-group-sm .bootstrap-select.form-control .dropdown-toggle{height:100%;font-size:inherit;line-height:inherit;border-radius:inherit}.bootstrap-select.form-control-lg .dropdown-toggle,.bootstrap-select.form-control-sm .dropdown-toggle{font-size:inherit;line-height:inherit;border-radius:inherit}.bootstrap-select.form-control-sm .dropdown-toggle{padding:.25rem .5rem}.bootstrap-select.form-control-lg .dropdown-toggle{padding:.5rem 1rem}.form-inline .bootstrap-select .form-control{width:100%}.bootstrap-select.disabled,.bootstrap-select>.disabled{cursor:not-allowed}.bootstrap-select.disabled:focus,.bootstrap-select>.disabled:focus{outline:0!important}.bootstrap-select.bs-container{position:absolute;top:0;left:0;height:0!important;padding:0!important}.bootstrap-select.bs-container .dropdown-menu{z-index:1060}.bootstrap-select .dropdown-toggle .filter-option{position:static;top:0;left:0;float:left;height:100%;width:100%;text-align:left;overflow:hidden;-webkit-box-flex:0;-webkit-flex:0 1 auto;-ms-flex:0 1 auto;flex:0 1 auto}.bs3.bootstrap-select .dropdown-toggle .filter-option{padding-right:inherit}.input-group .bs3-has-addon.bootstrap-select .dropdown-toggle .filter-option{position:absolute;padding-top:inherit;padding-bottom:inherit;padding-left:inherit;float:none}.input-group .bs3-has-addon.bootstrap-select .dropdown-toggle .filter-option .filter-option-inner{padding-right:inherit}.bootstrap-select .dropdown-toggle .filter-option-inner-inner{overflow:hidden}.bootstrap-select .dropdown-toggle .filter-expand{width:0!important;float:left;opacity:0!important;overflow:hidden}.bootstrap-select .dropdown-toggle .caret{position:absolute;top:50%;right:12px;margin-top:-2px;vertical-align:middle}.input-group .bootstrap-select.form-control .dropdown-toggle{border-radius:inherit}.bootstrap-select[class*=col-] .dropdown-toggle{width:100%}.bootstrap-select .dropdown-menu{min-width:100%;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.bootstrap-select .dropdown-menu>.inner:focus{outline:0!important}.bootstrap-select .dropdown-menu.inner{position:static;float:none;border:0;padding:0;margin:0;border-radius:0;-webkit-box-shadow:none;box-shadow:none}.bootstrap-select .dropdown-menu li{position:relative}.bootstrap-select .dropdown-menu li.active small{color:rgba(255,255,255,.5)!important}.bootstrap-select .dropdown-menu li.disabled a{cursor:not-allowed}.bootstrap-select .dropdown-menu li a{cursor:pointer;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.bootstrap-select .dropdown-menu li a.opt{position:relative;padding-left:2.25em}.bootstrap-select .dropdown-menu li a span.check-mark{display:none}.bootstrap-select .dropdown-menu li a span.text{display:inline-block}.bootstrap-select .dropdown-menu li small{padding-left:.5em}.bootstrap-select .dropdown-menu .notify{position:absolute;bottom:5px;width:96%;margin:0 2%;min-height:26px;padding:3px 5px;background:#f5f5f5;border:1px solid #e3e3e3;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.05);box-shadow:inset 0 1px 1px rgba(0,0,0,.05);pointer-events:none;opacity:.9;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.bootstrap-select .dropdown-menu .notify.fadeOut{-webkit-animation:.3s linear 750ms forwards bs-notify-fadeOut;-o-animation:.3s linear 750ms forwards bs-notify-fadeOut;animation:.3s linear 750ms forwards bs-notify-fadeOut}.bootstrap-select .no-results{padding:3px;background:#f5f5f5;margin:0 5px;white-space:nowrap}.bootstrap-select.fit-width .dropdown-toggle .filter-option{position:static;display:inline;padding:0}.bootstrap-select.fit-width .dropdown-toggle .filter-option-inner,.bootstrap-select.fit-width .dropdown-toggle .filter-option-inner-inner{display:inline}.bootstrap-select.fit-width .dropdown-toggle .bs-caret:before{content:'\00a0'}.bootstrap-select.fit-width .dropdown-toggle .caret{position:static;top:auto;margin-top:-1px}.bootstrap-select.show-tick .dropdown-menu .selected span.check-mark{position:absolute;display:inline-block;right:15px;top:5px}.bootstrap-select.show-tick .dropdown-menu li a span.text{margin-right:34px}.bootstrap-select .bs-ok-default:after{content:'';display:block;width:.5em;height:1em;border-style:solid;border-width:0 .26em .26em 0;-webkit-transform:rotate(45deg);-ms-transform:rotate(45deg);-o-transform:rotate(45deg);transform:rotate(45deg)}.bootstrap-select.show-menu-arrow.open>.dropdown-toggle,.bootstrap-select.show-menu-arrow.show>.dropdown-toggle{z-index:1061}.bootstrap-select.show-menu-arrow .dropdown-toggle .filter-option:before{content:'';border-left:7px solid transparent;border-right:7px solid transparent;border-bottom:7px solid rgba(204,204,204,.2);position:absolute;bottom:-4px;left:9px;display:none}.bootstrap-select.show-menu-arrow .dropdown-toggle .filter-option:after{content:'';border-left:6px solid transparent;border-right:6px solid transparent;border-bottom:6px solid #fff;position:absolute;bottom:-4px;left:10px;display:none}.bootstrap-select.show-menu-arrow.dropup .dropdown-toggle .filter-option:before{bottom:auto;top:-4px;border-top:7px solid rgba(204,204,204,.2);border-bottom:0}.bootstrap-select.show-menu-arrow.dropup .dropdown-toggle .filter-option:after{bottom:auto;top:-4px;border-top:6px solid #fff;border-bottom:0}.bootstrap-select.show-menu-arrow.pull-right .dropdown-toggle .filter-option:before{right:12px;left:auto}.bootstrap-select.show-menu-arrow.pull-right .dropdown-toggle .filter-option:after{right:13px;left:auto}.bootstrap-select.show-menu-arrow.open>.dropdown-toggle .filter-option:after,.bootstrap-select.show-menu-arrow.open>.dropdown-toggle .filter-option:before,.bootstrap-select.show-menu-arrow.show>.dropdown-toggle .filter-option:after,.bootstrap-select.show-menu-arrow.show>.dropdown-toggle .filter-option:before{display:block}.bs-actionsbox,.bs-donebutton,.bs-searchbox{padding:4px 8px}.bs-actionsbox{width:100%;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.bs-actionsbox .btn-group button{width:50%}.bs-donebutton{float:left;width:100%;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.bs-donebutton .btn-group button{width:100%}.bs-searchbox+.bs-actionsbox{padding:0 8px 4px}.bs-searchbox .form-control{margin-bottom:0;width:100%;float:none}
|
1 |
/*!
|
2 |
+
* Bootstrap-select v1.13.18 (https://developer.snapappointments.com/bootstrap-select)
|
3 |
*
|
4 |
* Copyright 2012-2020 SnapAppointments, LLC
|
5 |
* Licensed under MIT (https://github.com/snapappointments/bootstrap-select/blob/master/LICENSE)
|
6 |
+
*/@-webkit-keyframes bs-notify-fadeOut{0%{opacity:.9}100%{opacity:0}}@-o-keyframes bs-notify-fadeOut{0%{opacity:.9}100%{opacity:0}}@keyframes bs-notify-fadeOut{0%{opacity:.9}100%{opacity:0}}.bootstrap-select>select.bs-select-hidden,select.bs-select-hidden,select.selectpicker{display:none!important}.bootstrap-select{width:220px\0;vertical-align:middle}.bootstrap-select>.dropdown-toggle{position:relative;width:100%;text-align:right;white-space:nowrap;display:-webkit-inline-box;display:-webkit-inline-flex;display:-ms-inline-flexbox;display:inline-flex;-webkit-box-align:center;-webkit-align-items:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:justify;-webkit-justify-content:space-between;-ms-flex-pack:justify;justify-content:space-between}.bootstrap-select>.dropdown-toggle:after{margin-top:-1px}.bootstrap-select>.dropdown-toggle.bs-placeholder,.bootstrap-select>.dropdown-toggle.bs-placeholder:active,.bootstrap-select>.dropdown-toggle.bs-placeholder:focus,.bootstrap-select>.dropdown-toggle.bs-placeholder:hover{color:#999}.bootstrap-select>.dropdown-toggle.bs-placeholder.btn-danger,.bootstrap-select>.dropdown-toggle.bs-placeholder.btn-danger:active,.bootstrap-select>.dropdown-toggle.bs-placeholder.btn-danger:focus,.bootstrap-select>.dropdown-toggle.bs-placeholder.btn-danger:hover,.bootstrap-select>.dropdown-toggle.bs-placeholder.btn-dark,.bootstrap-select>.dropdown-toggle.bs-placeholder.btn-dark:active,.bootstrap-select>.dropdown-toggle.bs-placeholder.btn-dark:focus,.bootstrap-select>.dropdown-toggle.bs-placeholder.btn-dark:hover,.bootstrap-select>.dropdown-toggle.bs-placeholder.btn-info,.bootstrap-select>.dropdown-toggle.bs-placeholder.btn-info:active,.bootstrap-select>.dropdown-toggle.bs-placeholder.btn-info:focus,.bootstrap-select>.dropdown-toggle.bs-placeholder.btn-info:hover,.bootstrap-select>.dropdown-toggle.bs-placeholder.btn-primary,.bootstrap-select>.dropdown-toggle.bs-placeholder.btn-primary:active,.bootstrap-select>.dropdown-toggle.bs-placeholder.btn-primary:focus,.bootstrap-select>.dropdown-toggle.bs-placeholder.btn-primary:hover,.bootstrap-select>.dropdown-toggle.bs-placeholder.btn-secondary,.bootstrap-select>.dropdown-toggle.bs-placeholder.btn-secondary:active,.bootstrap-select>.dropdown-toggle.bs-placeholder.btn-secondary:focus,.bootstrap-select>.dropdown-toggle.bs-placeholder.btn-secondary:hover,.bootstrap-select>.dropdown-toggle.bs-placeholder.btn-success,.bootstrap-select>.dropdown-toggle.bs-placeholder.btn-success:active,.bootstrap-select>.dropdown-toggle.bs-placeholder.btn-success:focus,.bootstrap-select>.dropdown-toggle.bs-placeholder.btn-success:hover{color:rgba(255,255,255,.5)}.bootstrap-select>select{position:absolute!important;bottom:0;left:50%;display:block!important;width:.5px!important;height:100%!important;padding:0!important;opacity:0!important;border:none;z-index:0!important}.bootstrap-select>select.mobile-device{top:0;left:0;display:block!important;width:100%!important;z-index:2!important}.bootstrap-select.is-invalid .dropdown-toggle,.error .bootstrap-select .dropdown-toggle,.has-error .bootstrap-select .dropdown-toggle,.was-validated .bootstrap-select select:invalid+.dropdown-toggle{border-color:#b94a48}.bootstrap-select.is-valid .dropdown-toggle,.was-validated .bootstrap-select select:valid+.dropdown-toggle{border-color:#28a745}.bootstrap-select.fit-width{width:auto!important}.bootstrap-select:not([class*=col-]):not([class*=form-control]):not(.input-group-btn){width:220px}.bootstrap-select .dropdown-toggle:focus,.bootstrap-select>select.mobile-device:focus+.dropdown-toggle{outline:thin dotted #333!important;outline:5px auto -webkit-focus-ring-color!important;outline-offset:-2px}.bootstrap-select.form-control{margin-bottom:0;padding:0;border:none;height:auto}:not(.input-group)>.bootstrap-select.form-control:not([class*=col-]){width:100%}.bootstrap-select.form-control.input-group-btn{float:none;z-index:auto}.form-inline .bootstrap-select,.form-inline .bootstrap-select.form-control:not([class*=col-]){width:auto}.bootstrap-select:not(.input-group-btn),.bootstrap-select[class*=col-]{float:none;display:inline-block;margin-left:0}.bootstrap-select.dropdown-menu-right,.bootstrap-select[class*=col-].dropdown-menu-right,.row .bootstrap-select[class*=col-].dropdown-menu-right{float:right}.form-group .bootstrap-select,.form-horizontal .bootstrap-select,.form-inline .bootstrap-select{margin-bottom:0}.form-group-lg .bootstrap-select.form-control,.form-group-sm .bootstrap-select.form-control{padding:0}.form-group-lg .bootstrap-select.form-control .dropdown-toggle,.form-group-sm .bootstrap-select.form-control .dropdown-toggle{height:100%;font-size:inherit;line-height:inherit;border-radius:inherit}.bootstrap-select.form-control-lg .dropdown-toggle,.bootstrap-select.form-control-sm .dropdown-toggle{font-size:inherit;line-height:inherit;border-radius:inherit}.bootstrap-select.form-control-sm .dropdown-toggle{padding:.25rem .5rem}.bootstrap-select.form-control-lg .dropdown-toggle{padding:.5rem 1rem}.form-inline .bootstrap-select .form-control{width:100%}.bootstrap-select.disabled,.bootstrap-select>.disabled{cursor:not-allowed}.bootstrap-select.disabled:focus,.bootstrap-select>.disabled:focus{outline:0!important}.bootstrap-select.bs-container{position:absolute;top:0;left:0;height:0!important;padding:0!important}.bootstrap-select.bs-container .dropdown-menu{z-index:1060}.bootstrap-select .dropdown-toggle .filter-option{position:static;top:0;left:0;float:left;height:100%;width:100%;text-align:left;overflow:hidden;-webkit-box-flex:0;-webkit-flex:0 1 auto;-ms-flex:0 1 auto;flex:0 1 auto}.bs3.bootstrap-select .dropdown-toggle .filter-option{padding-right:inherit}.input-group .bs3-has-addon.bootstrap-select .dropdown-toggle .filter-option{position:absolute;padding-top:inherit;padding-bottom:inherit;padding-left:inherit;float:none}.input-group .bs3-has-addon.bootstrap-select .dropdown-toggle .filter-option .filter-option-inner{padding-right:inherit}.bootstrap-select .dropdown-toggle .filter-option-inner-inner{overflow:hidden}.bootstrap-select .dropdown-toggle .filter-expand{width:0!important;float:left;opacity:0!important;overflow:hidden}.bootstrap-select .dropdown-toggle .caret{position:absolute;top:50%;right:12px;margin-top:-2px;vertical-align:middle}.input-group .bootstrap-select.form-control .dropdown-toggle{border-radius:inherit}.bootstrap-select[class*=col-] .dropdown-toggle{width:100%}.bootstrap-select .dropdown-menu{min-width:100%;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.bootstrap-select .dropdown-menu>.inner:focus{outline:0!important}.bootstrap-select .dropdown-menu.inner{position:static;float:none;border:0;padding:0;margin:0;border-radius:0;-webkit-box-shadow:none;box-shadow:none}.bootstrap-select .dropdown-menu li{position:relative}.bootstrap-select .dropdown-menu li.active small{color:rgba(255,255,255,.5)!important}.bootstrap-select .dropdown-menu li.disabled a{cursor:not-allowed}.bootstrap-select .dropdown-menu li a{cursor:pointer;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.bootstrap-select .dropdown-menu li a.opt{position:relative;padding-left:2.25em}.bootstrap-select .dropdown-menu li a span.check-mark{display:none}.bootstrap-select .dropdown-menu li a span.text{display:inline-block}.bootstrap-select .dropdown-menu li small{padding-left:.5em}.bootstrap-select .dropdown-menu .notify{position:absolute;bottom:5px;width:96%;margin:0 2%;min-height:26px;padding:3px 5px;background:#f5f5f5;border:1px solid #e3e3e3;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.05);box-shadow:inset 0 1px 1px rgba(0,0,0,.05);pointer-events:none;opacity:.9;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.bootstrap-select .dropdown-menu .notify.fadeOut{-webkit-animation:.3s linear 750ms forwards bs-notify-fadeOut;-o-animation:.3s linear 750ms forwards bs-notify-fadeOut;animation:.3s linear 750ms forwards bs-notify-fadeOut}.bootstrap-select .no-results{padding:3px;background:#f5f5f5;margin:0 5px;white-space:nowrap}.bootstrap-select.fit-width .dropdown-toggle .filter-option{position:static;display:inline;padding:0}.bootstrap-select.fit-width .dropdown-toggle .filter-option-inner,.bootstrap-select.fit-width .dropdown-toggle .filter-option-inner-inner{display:inline}.bootstrap-select.fit-width .dropdown-toggle .bs-caret:before{content:'\00a0'}.bootstrap-select.fit-width .dropdown-toggle .caret{position:static;top:auto;margin-top:-1px}.bootstrap-select.show-tick .dropdown-menu .selected span.check-mark{position:absolute;display:inline-block;right:15px;top:5px}.bootstrap-select.show-tick .dropdown-menu li a span.text{margin-right:34px}.bootstrap-select .bs-ok-default:after{content:'';display:block;width:.5em;height:1em;border-style:solid;border-width:0 .26em .26em 0;-webkit-transform-style:preserve-3d;transform-style:preserve-3d;-webkit-transform:rotate(45deg);-ms-transform:rotate(45deg);-o-transform:rotate(45deg);transform:rotate(45deg)}.bootstrap-select.show-menu-arrow.open>.dropdown-toggle,.bootstrap-select.show-menu-arrow.show>.dropdown-toggle{z-index:1061}.bootstrap-select.show-menu-arrow .dropdown-toggle .filter-option:before{content:'';border-left:7px solid transparent;border-right:7px solid transparent;border-bottom:7px solid rgba(204,204,204,.2);position:absolute;bottom:-4px;left:9px;display:none}.bootstrap-select.show-menu-arrow .dropdown-toggle .filter-option:after{content:'';border-left:6px solid transparent;border-right:6px solid transparent;border-bottom:6px solid #fff;position:absolute;bottom:-4px;left:10px;display:none}.bootstrap-select.show-menu-arrow.dropup .dropdown-toggle .filter-option:before{bottom:auto;top:-4px;border-top:7px solid rgba(204,204,204,.2);border-bottom:0}.bootstrap-select.show-menu-arrow.dropup .dropdown-toggle .filter-option:after{bottom:auto;top:-4px;border-top:6px solid #fff;border-bottom:0}.bootstrap-select.show-menu-arrow.pull-right .dropdown-toggle .filter-option:before{right:12px;left:auto}.bootstrap-select.show-menu-arrow.pull-right .dropdown-toggle .filter-option:after{right:13px;left:auto}.bootstrap-select.show-menu-arrow.open>.dropdown-toggle .filter-option:after,.bootstrap-select.show-menu-arrow.open>.dropdown-toggle .filter-option:before,.bootstrap-select.show-menu-arrow.show>.dropdown-toggle .filter-option:after,.bootstrap-select.show-menu-arrow.show>.dropdown-toggle .filter-option:before{display:block}.bs-actionsbox,.bs-donebutton,.bs-searchbox{padding:4px 8px}.bs-actionsbox{width:100%;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.bs-actionsbox .btn-group button{width:50%}.bs-donebutton{float:left;width:100%;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.bs-donebutton .btn-group button{width:100%}.bs-searchbox+.bs-actionsbox{padding:0 8px 4px}.bs-searchbox .form-control{margin-bottom:0;width:100%;float:none}
|
resources/css/mainwp.css
ADDED
@@ -0,0 +1,12 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
|
2 |
+
#mainwp-shield-extension-table-sites_wrapper {
|
3 |
+
padding: 0 1rem;
|
4 |
+
}
|
5 |
+
|
6 |
+
table.mainwp-shield-ext-table {
|
7 |
+
}
|
8 |
+
table.mainwp-shield-ext-table td.cell-span-issue {
|
9 |
+
background-color: rgba(0, 0, 0, 0.1);
|
10 |
+
font-size: smaller;
|
11 |
+
text-align: center;
|
12 |
+
}
|
resources/css/plugin.css
CHANGED
@@ -364,11 +364,23 @@ label input[type=checkbox] {
|
|
364 |
.module-icon-support:before {
|
365 |
content: "\f525";
|
366 |
}
|
367 |
-
#
|
368 |
-
|
|
|
|
|
|
|
369 |
content: "\f179";
|
370 |
font-size: 24px;
|
371 |
color: #008000 !important;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
372 |
}
|
373 |
.dropdown-item .module-icon:before {
|
374 |
font-size: 1.6rem;
|
@@ -659,7 +671,6 @@ input:checked + .icwp-slider:before {
|
|
659 |
}
|
660 |
.carousel-item {
|
661 |
transition: -webkit-transform 0.3s ease;
|
662 |
-
transition: transform 0.2s ease;
|
663 |
transition: transform 0.2s ease, -webkit-transform 0.2s ease;
|
664 |
}
|
665 |
#AuditTrailTabs .tab-content {
|
@@ -699,13 +710,6 @@ input:checked + .icwp-slider:before {
|
|
699 |
left: -17px;
|
700 |
}
|
701 |
}
|
702 |
-
#FooterWizardBanner {
|
703 |
-
position: fixed;
|
704 |
-
bottom: 0;
|
705 |
-
height: 66px;
|
706 |
-
width: 100%;
|
707 |
-
z-index: 5;
|
708 |
-
}
|
709 |
#FooterWizardBanner .text-left {
|
710 |
margin-bottom: 0;
|
711 |
}
|
@@ -717,21 +721,6 @@ input:checked + .icwp-slider:before {
|
|
717 |
background-color: #fafafa;
|
718 |
background: linear-gradient(to top, rgba(250, 250, 250, 0.95), rgba(250, 250, 250, 0.7));
|
719 |
}
|
720 |
-
#WizardBanner {
|
721 |
-
height: 66px;
|
722 |
-
background-color: #eaffea;
|
723 |
-
border-top: 1px solid #bbbbbb;
|
724 |
-
padding-top: 15px;
|
725 |
-
text-align: center;
|
726 |
-
word-break: keep-all;
|
727 |
-
white-space: nowrap;
|
728 |
-
box-shadow: 0 -2px 8px rgba(0, 0, 0, 0.2);
|
729 |
-
}
|
730 |
-
#WizardBanner p {
|
731 |
-
margin: 0;
|
732 |
-
line-height: 20px;
|
733 |
-
text-align: left;
|
734 |
-
}
|
735 |
.icon-flag {
|
736 |
vertical-align: text-bottom;
|
737 |
}
|
@@ -800,8 +789,6 @@ th.column-request_info {
|
|
800 |
}
|
801 |
#odp-PageHead .nav-link.active {
|
802 |
font-weight: bold;
|
803 |
-
border: 1px solid rgba(0, 0, 0, 0.1);
|
804 |
-
border-radius: 3px;
|
805 |
color: #008000;
|
806 |
}
|
807 |
.nav-link.module.notenabled span.module-name {
|
@@ -824,7 +811,7 @@ th.column-request_info {
|
|
824 |
.odp-outercontainer.icwp-wpsf-insights.settings #odp-PageMain {
|
825 |
}
|
826 |
.insights_section {
|
827 |
-
margin-top: 20px
|
828 |
}
|
829 |
.insights_widget .card-body {
|
830 |
padding: 0 0 0 0;
|
@@ -957,7 +944,7 @@ table.scan-table.wp-list-table button.toggle-row {
|
|
957 |
}
|
958 |
.insights-sub-nav ul.nav.nav-tabs a.nav-link {
|
959 |
color: #666666;
|
960 |
-
font-size:
|
961 |
margin-right: 1rem;
|
962 |
background-color: rgba(0, 0, 0, 0.05);
|
963 |
border-color: rgba(0, 0, 0, 0.1);
|
@@ -1195,9 +1182,6 @@ a:hover {
|
|
1195 |
a:focus .gravatar, a:focus, a:focus .media-icon img {
|
1196 |
box-shadow: none !important;
|
1197 |
}
|
1198 |
-
.wp-core-ui select {
|
1199 |
-
min-height: 38px;
|
1200 |
-
}
|
1201 |
.col-form-label {
|
1202 |
font-weight: bold;
|
1203 |
padding-top: 0;
|
@@ -1298,6 +1282,11 @@ dl.pro-features dd {
|
|
1298 |
background-color: rgba(69, 119, 0, 0.3);
|
1299 |
border-color: rgba(69, 119, 0, 0.3);
|
1300 |
}
|
|
|
|
|
|
|
|
|
|
|
1301 |
.importexport-checkbox.custom-switch .custom-control-input:checked ~ .custom-control-label::before {
|
1302 |
background-color: rgba(119, 107, 12, 1);
|
1303 |
border-color: rgba(119, 107, 12, 1);
|
@@ -1312,7 +1301,15 @@ dl.pro-features dd {
|
|
1312 |
#FileLockerDiffContents .card-body {
|
1313 |
padding: 1.25rem;
|
1314 |
}
|
1315 |
-
.overview.card {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1316 |
}
|
1317 |
.overview.card > .card-footer {
|
1318 |
padding: 10px 15px;
|
@@ -1324,25 +1321,57 @@ dl.pro-features dd {
|
|
1324 |
.overview.card > .card-footer > a .dashicons {
|
1325 |
font-size: 24px;
|
1326 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1327 |
.overview.card.state-danger,
|
1328 |
.overview.card.state-danger > .card-header,
|
1329 |
.overview.card.state-danger > .card-footer {
|
1330 |
-
border-color: #cf0000
|
1331 |
}
|
1332 |
.overview.card.state-warning,
|
1333 |
.overview.card.state-warning > .card-header,
|
1334 |
.overview.card.state-warning > .card-footer {
|
1335 |
-
border-color: #d26605
|
1336 |
}
|
1337 |
.overview.card.state-ok,
|
1338 |
.overview.card.state-ok > .card-header,
|
1339 |
.overview.card.state-ok > .card-footer {
|
1340 |
-
border-color: #0579d2
|
1341 |
}
|
1342 |
.overview.card.state-good,
|
1343 |
.overview.card.state-good > .card-header,
|
1344 |
.overview.card.state-good > .card-footer {
|
1345 |
-
border-color: #008000
|
1346 |
}
|
1347 |
.overview.card.state-good > .card-header {
|
1348 |
background-color: rgba(98, 214, 83, 0.2);
|
@@ -1438,4 +1467,101 @@ a.card_help {
|
|
1438 |
}
|
1439 |
#DynamicChangelog dd p {
|
1440 |
margin-bottom: 5px;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1441 |
}
|
364 |
.module-icon-support:before {
|
365 |
content: "\f525";
|
366 |
}
|
367 |
+
#SearchOptionsLaunch > a {
|
368 |
+
text-decoration: none !important;
|
369 |
+
}
|
370 |
+
#SearchOptionsLaunch .dashicons {
|
371 |
+
width: 24px;
|
372 |
content: "\f179";
|
373 |
font-size: 24px;
|
374 |
color: #008000 !important;
|
375 |
+
line-height: 40px;
|
376 |
+
}
|
377 |
+
.bootstrap-select > button.dropdown-toggle,
|
378 |
+
.bootstrap-select.show > button.dropdown-toggle {
|
379 |
+
background-color: transparent;
|
380 |
+
color: black !important;
|
381 |
+
}
|
382 |
+
.dropdown-item {
|
383 |
+
font-size: 1.05rem;
|
384 |
}
|
385 |
.dropdown-item .module-icon:before {
|
386 |
font-size: 1.6rem;
|
671 |
}
|
672 |
.carousel-item {
|
673 |
transition: -webkit-transform 0.3s ease;
|
|
|
674 |
transition: transform 0.2s ease, -webkit-transform 0.2s ease;
|
675 |
}
|
676 |
#AuditTrailTabs .tab-content {
|
710 |
left: -17px;
|
711 |
}
|
712 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
713 |
#FooterWizardBanner .text-left {
|
714 |
margin-bottom: 0;
|
715 |
}
|
721 |
background-color: #fafafa;
|
722 |
background: linear-gradient(to top, rgba(250, 250, 250, 0.95), rgba(250, 250, 250, 0.7));
|
723 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
724 |
.icon-flag {
|
725 |
vertical-align: text-bottom;
|
726 |
}
|
789 |
}
|
790 |
#odp-PageHead .nav-link.active {
|
791 |
font-weight: bold;
|
|
|
|
|
792 |
color: #008000;
|
793 |
}
|
794 |
.nav-link.module.notenabled span.module-name {
|
811 |
.odp-outercontainer.icwp-wpsf-insights.settings #odp-PageMain {
|
812 |
}
|
813 |
.insights_section {
|
814 |
+
/*margin-top: 20px;*/
|
815 |
}
|
816 |
.insights_widget .card-body {
|
817 |
padding: 0 0 0 0;
|
944 |
}
|
945 |
.insights-sub-nav ul.nav.nav-tabs a.nav-link {
|
946 |
color: #666666;
|
947 |
+
font-size: 0.9rem;
|
948 |
margin-right: 1rem;
|
949 |
background-color: rgba(0, 0, 0, 0.05);
|
950 |
border-color: rgba(0, 0, 0, 0.1);
|
1182 |
a:focus .gravatar, a:focus, a:focus .media-icon img {
|
1183 |
box-shadow: none !important;
|
1184 |
}
|
|
|
|
|
|
|
1185 |
.col-form-label {
|
1186 |
font-weight: bold;
|
1187 |
padding-top: 0;
|
1282 |
background-color: rgba(69, 119, 0, 0.3);
|
1283 |
border-color: rgba(69, 119, 0, 0.3);
|
1284 |
}
|
1285 |
+
.custom-control.custom-checkbox .custom-control-input:disabled,
|
1286 |
+
.custom-control.custom-radio .custom-control-input:disabled,
|
1287 |
+
.option-checkbox.custom-switch .custom-control-input:disabled {
|
1288 |
+
opacity: 0;
|
1289 |
+
}
|
1290 |
.importexport-checkbox.custom-switch .custom-control-input:checked ~ .custom-control-label::before {
|
1291 |
background-color: rgba(119, 107, 12, 1);
|
1292 |
border-color: rgba(119, 107, 12, 1);
|
1301 |
#FileLockerDiffContents .card-body {
|
1302 |
padding: 1.25rem;
|
1303 |
}
|
1304 |
+
#odp-PageContainer .overview.card {
|
1305 |
+
box-shadow: 2px 2px 2px rgba(0, 0, 0, 0.1);
|
1306 |
+
}
|
1307 |
+
.overview.card,
|
1308 |
+
.overview.card .card-header,
|
1309 |
+
.overview.card .card-body {
|
1310 |
+
border: 2px solid white !important;
|
1311 |
+
}
|
1312 |
+
.overview.card > .card-header {
|
1313 |
}
|
1314 |
.overview.card > .card-footer {
|
1315 |
padding: 10px 15px;
|
1321 |
.overview.card > .card-footer > a .dashicons {
|
1322 |
font-size: 24px;
|
1323 |
}
|
1324 |
+
.overview.card.state-good .title-icon:before,
|
1325 |
+
.overview.card.state-good .card-title,
|
1326 |
+
.overview.card.state-good .card-header .card-link .dashicons {
|
1327 |
+
color: #015b01;
|
1328 |
+
}
|
1329 |
+
.overview.card.state-ok .title-icon:before,
|
1330 |
+
.overview.card.state-ok .card-title,
|
1331 |
+
.overview.card.state-ok .card-header .card-link .dashicons {
|
1332 |
+
color: #345c83;
|
1333 |
+
}
|
1334 |
+
.overview.card.state-warning .title-icon:before,
|
1335 |
+
.overview.card.state-warning .card-title,
|
1336 |
+
.overview.card.state-warning .card-header .card-link .dashicons {
|
1337 |
+
color: #ae5403;
|
1338 |
+
}
|
1339 |
+
.overview.card.state-danger .title-icon:before,
|
1340 |
+
.overview.card.state-danger .card-title,
|
1341 |
+
.overview.card.state-danger .card-header .card-link .dashicons {
|
1342 |
+
color: #811313;
|
1343 |
+
}
|
1344 |
+
.overview.card.state-danger .title-icon:before {
|
1345 |
+
content: "\f153";
|
1346 |
+
}
|
1347 |
+
.overview.card.state-warning .title-icon:before {
|
1348 |
+
content: "\f534";
|
1349 |
+
}
|
1350 |
+
.overview.card.state-ok .title-icon:before {
|
1351 |
+
content: "\f348";
|
1352 |
+
}
|
1353 |
+
.overview.card.state-good .title-icon:before {
|
1354 |
+
content: "\f12a";
|
1355 |
+
}
|
1356 |
.overview.card.state-danger,
|
1357 |
.overview.card.state-danger > .card-header,
|
1358 |
.overview.card.state-danger > .card-footer {
|
1359 |
+
/*border-color: #cf0000;*/
|
1360 |
}
|
1361 |
.overview.card.state-warning,
|
1362 |
.overview.card.state-warning > .card-header,
|
1363 |
.overview.card.state-warning > .card-footer {
|
1364 |
+
/*border-color: #d26605;*/
|
1365 |
}
|
1366 |
.overview.card.state-ok,
|
1367 |
.overview.card.state-ok > .card-header,
|
1368 |
.overview.card.state-ok > .card-footer {
|
1369 |
+
/*border-color: #0579d2;*/
|
1370 |
}
|
1371 |
.overview.card.state-good,
|
1372 |
.overview.card.state-good > .card-header,
|
1373 |
.overview.card.state-good > .card-footer {
|
1374 |
+
/*border-color: #008000;*/
|
1375 |
}
|
1376 |
.overview.card.state-good > .card-header {
|
1377 |
background-color: rgba(98, 214, 83, 0.2);
|
1467 |
}
|
1468 |
#DynamicChangelog dd p {
|
1469 |
margin-bottom: 5px;
|
1470 |
+
}
|
1471 |
+
#DashboardFeatureCards .card-footer {
|
1472 |
+
background-color: transparent;
|
1473 |
+
}
|
1474 |
+
#DashboardFeatureCards .list-group-item {
|
1475 |
+
border: 0 none;
|
1476 |
+
padding: .3rem 0.2rem;
|
1477 |
+
}
|
1478 |
+
#DashboardFeatureCards .card.highlighted img {
|
1479 |
+
filter: invert(100%);
|
1480 |
+
}
|
1481 |
+
#DashboardFeatureCards .card.highlighted .list-group-item {
|
1482 |
+
background-color: transparent;
|
1483 |
+
}
|
1484 |
+
#DashboardFeatureCards .card.highlighted .list-group-item a {
|
1485 |
+
color: white;
|
1486 |
+
}
|
1487 |
+
#DashboardFeatureCards .img-holder {
|
1488 |
+
text-align: center;
|
1489 |
+
}
|
1490 |
+
#DashboardFeatureCards .img-holder .card-img-top {
|
1491 |
+
width: 16%;
|
1492 |
+
}
|
1493 |
+
#DashboardFeatureCards .card-title img {
|
1494 |
+
vertical-align: text-bottom;
|
1495 |
+
}
|
1496 |
+
#Dashboard_ModSettingsSelect .bootstrap-select,
|
1497 |
+
#Dashboard_ModSettingsSelect > select {
|
1498 |
+
width: 100%;
|
1499 |
+
}
|
1500 |
+
#ModuleSettingsJump .bootstrap-select button {
|
1501 |
+
padding: 0;
|
1502 |
+
line-height: 15px;
|
1503 |
+
background: transparent !important;
|
1504 |
+
border: 0 none !important;
|
1505 |
+
outline: 0 none !important;
|
1506 |
+
box-shadow: 0 0 transparent !important;
|
1507 |
+
}
|
1508 |
+
#ModuleSettingsJump .bootstrap-select button::after {
|
1509 |
+
content: none !important;
|
1510 |
+
}
|
1511 |
+
#ModuleSettingsJump .bootstrap-select button .filter-option-inner-inner {
|
1512 |
+
font-size: 13px;
|
1513 |
+
line-height: 20px;
|
1514 |
+
color: #008000 !important;
|
1515 |
+
font-weight: 500;
|
1516 |
+
}
|
1517 |
+
#ModuleSettingsJump .bootstrap-select,
|
1518 |
+
#ModuleSettingsJump select,
|
1519 |
+
#ModuleSettingsJump select:focus,
|
1520 |
+
#ModuleSettingsJump select:active {
|
1521 |
+
background: transparent;
|
1522 |
+
border: 0 none !important;
|
1523 |
+
color: #008000;
|
1524 |
+
vertical-align: inherit;
|
1525 |
+
}
|
1526 |
+
|
1527 |
+
#wpfooter {
|
1528 |
+
display: none;
|
1529 |
+
}
|
1530 |
+
#FooterBannerGoPro {
|
1531 |
+
width: 100%;
|
1532 |
+
background-color: white;
|
1533 |
+
border-color: transparent;
|
1534 |
+
border-width: 1px 1px 0 1px;
|
1535 |
+
margin: auto;
|
1536 |
+
padding-top: 15px;
|
1537 |
+
text-align: center;
|
1538 |
+
word-break: keep-all;
|
1539 |
+
white-space: nowrap;
|
1540 |
+
box-shadow: 0 -2px 8px rgba(0, 0, 0, 0.2);
|
1541 |
+
|
1542 |
+
position: absolute;
|
1543 |
+
bottom: 0;
|
1544 |
+
padding-left: 160px;
|
1545 |
+
transition: 0.5s ease;
|
1546 |
+
}
|
1547 |
+
body.folded #FooterBannerGoPro {
|
1548 |
+
padding-left: 40px;
|
1549 |
+
}
|
1550 |
+
@media (max-width: 1200px) {
|
1551 |
+
#FooterBannerGoPro h6{
|
1552 |
+
font-size: 14px;
|
1553 |
+
letter-spacing: -1px;
|
1554 |
+
}
|
1555 |
+
#FooterBannerGoPro p{
|
1556 |
+
font-size: 12px;
|
1557 |
+
letter-spacing: -1px;
|
1558 |
+
}
|
1559 |
+
}
|
1560 |
+
@media (max-width: 960px) {
|
1561 |
+
#FooterBannerGoPro{
|
1562 |
+
padding-left: 40px;
|
1563 |
+
}
|
1564 |
+
}
|
1565 |
+
#odp-PageFoot {
|
1566 |
+
height: 100px;
|
1567 |
}
|
resources/images/bootstrap/arrow-down-up.svg
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
1 |
+
<svg width="1em" height="1em" viewBox="0 0 16 16" class="bi bi-arrow-down-up" fill="currentColor" xmlns="http://www.w3.org/2000/svg">
|
2 |
+
<path fill-rule="evenodd" d="M11.5 15a.5.5 0 0 0 .5-.5V2.707l3.146 3.147a.5.5 0 0 0 .708-.708l-4-4a.5.5 0 0 0-.708 0l-4 4a.5.5 0 1 0 .708.708L11 2.707V14.5a.5.5 0 0 0 .5.5zm-7-14a.5.5 0 0 1 .5.5v11.793l3.146-3.147a.5.5 0 0 1 .708.708l-4 4a.5.5 0 0 1-.708 0l-4-4a.5.5 0 0 1 .708-.708L4 13.293V1.5a.5.5 0 0 1 .5-.5z"/>
|
3 |
+
</svg>
|
resources/images/bootstrap/award.svg
ADDED
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
1 |
+
<svg width="1em" height="1em" viewBox="0 0 16 16" class="bi bi-award" fill="currentColor" xmlns="http://www.w3.org/2000/svg">
|
2 |
+
<path fill-rule="evenodd" d="M9.669.864L8 0 6.331.864l-1.858.282-.842 1.68-1.337 1.32L2.6 6l-.306 1.854 1.337 1.32.842 1.68 1.858.282L8 12l1.669-.864 1.858-.282.842-1.68 1.337-1.32L13.4 6l.306-1.854-1.337-1.32-.842-1.68L9.669.864zm1.196 1.193l-1.51-.229L8 1.126l-1.355.702-1.51.229-.684 1.365-1.086 1.072L3.614 6l-.25 1.506 1.087 1.072.684 1.365 1.51.229L8 10.874l1.356-.702 1.509-.229.684-1.365 1.086-1.072L12.387 6l.248-1.506-1.086-1.072-.684-1.365z"/>
|
3 |
+
<path d="M4 11.794V16l4-1 4 1v-4.206l-2.018.306L8 13.126 6.018 12.1 4 11.794z"/>
|
4 |
+
</svg>
|
resources/images/bootstrap/binoculars.svg
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
1 |
+
<svg width="1em" height="1em" viewBox="0 0 16 16" class="bi bi-binoculars" fill="currentColor" xmlns="http://www.w3.org/2000/svg">
|
2 |
+
<path fill-rule="evenodd" d="M3 2.5A1.5 1.5 0 0 1 4.5 1h1A1.5 1.5 0 0 1 7 2.5V5h2V2.5A1.5 1.5 0 0 1 10.5 1h1A1.5 1.5 0 0 1 13 2.5v2.382a.5.5 0 0 0 .276.447l.895.447A1.5 1.5 0 0 1 15 7.118V14.5a1.5 1.5 0 0 1-1.5 1.5h-3A1.5 1.5 0 0 1 9 14.5v-3a.5.5 0 0 1 .146-.354l.854-.853V9.5a.5.5 0 0 0-.5-.5h-3a.5.5 0 0 0-.5.5v.793l.854.853A.5.5 0 0 1 7 11.5v3A1.5 1.5 0 0 1 5.5 16h-3A1.5 1.5 0 0 1 1 14.5V7.118a1.5 1.5 0 0 1 .83-1.342l.894-.447A.5.5 0 0 0 3 4.882V2.5zM4.5 2a.5.5 0 0 0-.5.5V3h2v-.5a.5.5 0 0 0-.5-.5h-1zM6 4H4v.882a1.5 1.5 0 0 1-.83 1.342l-.894.447A.5.5 0 0 0 2 7.118V13h4v-1.293l-.854-.853A.5.5 0 0 1 5 10.5v-1A1.5 1.5 0 0 1 6.5 8h3A1.5 1.5 0 0 1 11 9.5v1a.5.5 0 0 1-.146.354l-.854.853V13h4V7.118a.5.5 0 0 0-.276-.447l-.895-.447A1.5 1.5 0 0 1 12 4.882V4h-2v1.5a.5.5 0 0 1-.5.5h-3a.5.5 0 0 1-.5-.5V4zm4-1h2v-.5a.5.5 0 0 0-.5-.5h-1a.5.5 0 0 0-.5.5V3zm4 11h-4v.5a.5.5 0 0 0 .5.5h3a.5.5 0 0 0 .5-.5V14zm-8 0H2v.5a.5.5 0 0 0 .5.5h3a.5.5 0 0 0 .5-.5V14z"/>
|
3 |
+
</svg>
|
resources/images/bootstrap/book-half.svg
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
1 |
+
<svg width="1em" height="1em" viewBox="0 0 16 16" class="bi bi-book-half" fill="currentColor" xmlns="http://www.w3.org/2000/svg">
|
2 |
+
<path fill-rule="evenodd" d="M8.5 2.687v9.746c.935-.53 2.12-.603 3.213-.493 1.18.12 2.37.461 3.287.811V2.828c-.885-.37-2.154-.769-3.388-.893-1.33-.134-2.458.063-3.112.752zM8 1.783C7.015.936 5.587.81 4.287.94c-1.514.153-3.042.672-3.994 1.105A.5.5 0 0 0 0 2.5v11a.5.5 0 0 0 .707.455c.882-.4 2.303-.881 3.68-1.02 1.409-.142 2.59.087 3.223.877a.5.5 0 0 0 .78 0c.633-.79 1.814-1.019 3.222-.877 1.378.139 2.8.62 3.681 1.02A.5.5 0 0 0 16 13.5v-11a.5.5 0 0 0-.293-.455c-.952-.433-2.48-.952-3.994-1.105C10.413.809 8.985.936 8 1.783z"/>
|
3 |
+
</svg>
|
resources/images/bootstrap/bug.svg
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
1 |
+
<svg width="1em" height="1em" viewBox="0 0 16 16" class="bi bi-bug" fill="currentColor" xmlns="http://www.w3.org/2000/svg">
|
2 |
+
<path fill-rule="evenodd" d="M4.355.522a.5.5 0 0 1 .623.333l.291.956A4.979 4.979 0 0 1 8 1c1.007 0 1.946.298 2.731.811l.29-.956a.5.5 0 1 1 .957.29l-.41 1.352A4.985 4.985 0 0 1 13 6h.5a.5.5 0 0 0 .5-.5V5a.5.5 0 0 1 1 0v.5A1.5 1.5 0 0 1 13.5 7H13v1h1.5a.5.5 0 0 1 0 1H13v1h.5a1.5 1.5 0 0 1 1.5 1.5v.5a.5.5 0 1 1-1 0v-.5a.5.5 0 0 0-.5-.5H13a5 5 0 0 1-10 0h-.5a.5.5 0 0 0-.5.5v.5a.5.5 0 1 1-1 0v-.5A1.5 1.5 0 0 1 2.5 10H3V9H1.5a.5.5 0 0 1 0-1H3V7h-.5A1.5 1.5 0 0 1 1 5.5V5a.5.5 0 0 1 1 0v.5a.5.5 0 0 0 .5.5H3c0-1.364.547-2.601 1.432-3.503l-.41-1.352a.5.5 0 0 1 .333-.623zM4 7v4a4 4 0 0 0 3.5 3.97V7H4zm4.5 0v7.97A4 4 0 0 0 12 11V7H8.5zM12 6H4a3.99 3.99 0 0 1 1.333-2.982A3.983 3.983 0 0 1 8 2c1.025 0 1.959.385 2.666 1.018A3.989 3.989 0 0 1 12 6z"/>
|
3 |
+
</svg>
|
resources/images/bootstrap/chat-right-dots-fill.svg
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
1 |
+
<svg width="1em" height="1em" viewBox="0 0 16 16" class="bi bi-chat-right-dots-fill" fill="currentColor" xmlns="http://www.w3.org/2000/svg">
|
2 |
+
<path fill-rule="evenodd" d="M16 2a2 2 0 0 0-2-2H2a2 2 0 0 0-2 2v8a2 2 0 0 0 2 2h9.586a1 1 0 0 1 .707.293l2.853 2.853a.5.5 0 0 0 .854-.353V2zM5 6a1 1 0 1 1-2 0 1 1 0 0 1 2 0zm4 0a1 1 0 1 1-2 0 1 1 0 0 1 2 0zm3 1a1 1 0 1 0 0-2 1 1 0 0 0 0 2z"/>
|
3 |
+
</svg>
|
resources/images/bootstrap/diagram-3.svg
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
1 |
+
<svg width="1em" height="1em" viewBox="0 0 16 16" class="bi bi-diagram-3" fill="currentColor" xmlns="http://www.w3.org/2000/svg">
|
2 |
+
<path fill-rule="evenodd" d="M6 3.5A1.5 1.5 0 0 1 7.5 2h1A1.5 1.5 0 0 1 10 3.5v1A1.5 1.5 0 0 1 8.5 6v1H14a.5.5 0 0 1 .5.5v1a.5.5 0 0 1-1 0V8h-5v.5a.5.5 0 0 1-1 0V8h-5v.5a.5.5 0 0 1-1 0v-1A.5.5 0 0 1 2 7h5.5V6A1.5 1.5 0 0 1 6 4.5v-1zM8.5 5a.5.5 0 0 0 .5-.5v-1a.5.5 0 0 0-.5-.5h-1a.5.5 0 0 0-.5.5v1a.5.5 0 0 0 .5.5h1zM0 11.5A1.5 1.5 0 0 1 1.5 10h1A1.5 1.5 0 0 1 4 11.5v1A1.5 1.5 0 0 1 2.5 14h-1A1.5 1.5 0 0 1 0 12.5v-1zm1.5-.5a.5.5 0 0 0-.5.5v1a.5.5 0 0 0 .5.5h1a.5.5 0 0 0 .5-.5v-1a.5.5 0 0 0-.5-.5h-1zm4.5.5A1.5 1.5 0 0 1 7.5 10h1a1.5 1.5 0 0 1 1.5 1.5v1A1.5 1.5 0 0 1 8.5 14h-1A1.5 1.5 0 0 1 6 12.5v-1zm1.5-.5a.5.5 0 0 0-.5.5v1a.5.5 0 0 0 .5.5h1a.5.5 0 0 0 .5-.5v-1a.5.5 0 0 0-.5-.5h-1zm4.5.5a1.5 1.5 0 0 1 1.5-1.5h1a1.5 1.5 0 0 1 1.5 1.5v1a1.5 1.5 0 0 1-1.5 1.5h-1a1.5 1.5 0 0 1-1.5-1.5v-1zm1.5-.5a.5.5 0 0 0-.5.5v1a.5.5 0 0 0 .5.5h1a.5.5 0 0 0 .5-.5v-1a.5.5 0 0 0-.5-.5h-1z"/>
|
3 |
+
</svg>
|
resources/images/bootstrap/emoji-smile.svg
ADDED
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<svg width="1em" height="1em" viewBox="0 0 16 16" class="bi bi-emoji-smile" fill="currentColor" xmlns="http://www.w3.org/2000/svg">
|
2 |
+
<path fill-rule="evenodd" d="M8 15A7 7 0 1 0 8 1a7 7 0 0 0 0 14zm0 1A8 8 0 1 0 8 0a8 8 0 0 0 0 16z"/>
|
3 |
+
<path fill-rule="evenodd" d="M4.285 9.567a.5.5 0 0 1 .683.183A3.498 3.498 0 0 0 8 11.5a3.498 3.498 0 0 0 3.032-1.75.5.5 0 1 1 .866.5A4.498 4.498 0 0 1 8 12.5a4.498 4.498 0 0 1-3.898-2.25.5.5 0 0 1 .183-.683z"/>
|
4 |
+
<path d="M7 6.5C7 7.328 6.552 8 6 8s-1-.672-1-1.5S5.448 5 6 5s1 .672 1 1.5zm4 0c0 .828-.448 1.5-1 1.5s-1-.672-1-1.5S9.448 5 10 5s1 .672 1 1.5z"/>
|
5 |
+
</svg>
|
resources/images/bootstrap/link-45deg.svg
ADDED
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
1 |
+
<svg width="1em" height="1em" viewBox="0 0 16 16" class="bi bi-link-45deg" fill="currentColor" xmlns="http://www.w3.org/2000/svg">
|
2 |
+
<path d="M4.715 6.542L3.343 7.914a3 3 0 1 0 4.243 4.243l1.828-1.829A3 3 0 0 0 8.586 5.5L8 6.086a1.001 1.001 0 0 0-.154.199 2 2 0 0 1 .861 3.337L6.88 11.45a2 2 0 1 1-2.83-2.83l.793-.792a4.018 4.018 0 0 1-.128-1.287z"/>
|
3 |
+
<path d="M6.586 4.672A3 3 0 0 0 7.414 9.5l.775-.776a2 2 0 0 1-.896-3.346L9.12 3.55a2 2 0 0 1 2.83 2.83l-.793.792c.112.42.155.855.128 1.287l1.372-1.372a3 3 0 0 0-4.243-4.243L6.586 4.672z"/>
|
4 |
+
</svg>
|
resources/images/bootstrap/pencil-square.svg
ADDED
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
1 |
+
<svg width="1em" height="1em" viewBox="0 0 16 16" class="bi bi-pencil-square" fill="currentColor" xmlns="http://www.w3.org/2000/svg">
|
2 |
+
<path d="M15.502 1.94a.5.5 0 0 1 0 .706L14.459 3.69l-2-2L13.502.646a.5.5 0 0 1 .707 0l1.293 1.293zm-1.75 2.456l-2-2L4.939 9.21a.5.5 0 0 0-.121.196l-.805 2.414a.25.25 0 0 0 .316.316l2.414-.805a.5.5 0 0 0 .196-.12l6.813-6.814z"/>
|
3 |
+
<path fill-rule="evenodd" d="M1 13.5A1.5 1.5 0 0 0 2.5 15h11a1.5 1.5 0 0 0 1.5-1.5v-6a.5.5 0 0 0-1 0v6a.5.5 0 0 1-.5.5h-11a.5.5 0 0 1-.5-.5v-11a.5.5 0 0 1 .5-.5H9a.5.5 0 0 0 0-1H2.5A1.5 1.5 0 0 0 1 2.5v11z"/>
|
4 |
+
</svg>
|
resources/images/bootstrap/people.svg
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
1 |
+
<svg width="1em" height="1em" viewBox="0 0 16 16" class="bi bi-people" fill="currentColor" xmlns="http://www.w3.org/2000/svg">
|
2 |
+
<path fill-rule="evenodd" d="M15 14s1 0 1-1-1-4-5-4-5 3-5 4 1 1 1 1h8zm-7.978-1h7.956a.274.274 0 0 0 .014-.002l.008-.002c-.002-.264-.167-1.03-.76-1.72C13.688 10.629 12.718 10 11 10c-1.717 0-2.687.63-3.24 1.276-.593.69-.759 1.457-.76 1.72a1.05 1.05 0 0 0 .022.004zM11 7a2 2 0 1 0 0-4 2 2 0 0 0 0 4zm3-2a3 3 0 1 1-6 0 3 3 0 0 1 6 0zM6.936 9.28a5.88 5.88 0 0 0-1.23-.247A7.35 7.35 0 0 0 5 9c-4 0-5 3-5 4 0 .667.333 1 1 1h4.216A2.238 2.238 0 0 1 5 13c0-1.01.377-2.042 1.09-2.904.243-.294.526-.569.846-.816zM4.92 10c-1.668.02-2.615.64-3.16 1.276C1.163 11.97 1 12.739 1 13h3c0-1.045.323-2.086.92-3zM1.5 5.5a3 3 0 1 1 6 0 3 3 0 0 1-6 0zm3-2a2 2 0 1 0 0 4 2 2 0 0 0 0-4z"/>
|
3 |
+
</svg>
|
resources/images/bootstrap/person-badge.svg
ADDED
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
1 |
+
<svg width="1em" height="1em" viewBox="0 0 16 16" class="bi bi-person-badge" fill="currentColor" xmlns="http://www.w3.org/2000/svg">
|
2 |
+
<path fill-rule="evenodd" d="M2 2.5A2.5 2.5 0 0 1 4.5 0h7A2.5 2.5 0 0 1 14 2.5V14a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V2.5zM4.5 1A1.5 1.5 0 0 0 3 2.5v10.795a4.2 4.2 0 0 1 .776-.492C4.608 12.387 5.937 12 8 12s3.392.387 4.224.803a4.2 4.2 0 0 1 .776.492V2.5A1.5 1.5 0 0 0 11.5 1h-7z"/>
|
3 |
+
<path fill-rule="evenodd" d="M8 11a3 3 0 1 0 0-6 3 3 0 0 0 0 6zM6 2.5a.5.5 0 0 1 .5-.5h3a.5.5 0 0 1 0 1h-3a.5.5 0 0 1-.5-.5z"/>
|
4 |
+
</svg>
|
resources/images/bootstrap/person-lines-fill.svg
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
1 |
+
<svg width="1em" height="1em" viewBox="0 0 16 16" class="bi bi-person-lines-fill" fill="currentColor" xmlns="http://www.w3.org/2000/svg">
|
2 |
+
<path fill-rule="evenodd" d="M1 14s-1 0-1-1 1-4 6-4 6 3 6 4-1 1-1 1H1zm5-6a3 3 0 1 0 0-6 3 3 0 0 0 0 6zm7 1.5a.5.5 0 0 1 .5-.5h2a.5.5 0 0 1 0 1h-2a.5.5 0 0 1-.5-.5zm-2-3a.5.5 0 0 1 .5-.5h4a.5.5 0 0 1 0 1h-4a.5.5 0 0 1-.5-.5zm0-3a.5.5 0 0 1 .5-.5h4a.5.5 0 0 1 0 1h-4a.5.5 0 0 1-.5-.5zm2 9a.5.5 0 0 1 .5-.5h2a.5.5 0 0 1 0 1h-2a.5.5 0 0 1-.5-.5z"/>
|
3 |
+
</svg>
|
resources/images/bootstrap/shield-shaded.svg
ADDED
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
1 |
+
<svg width="1em" height="1em" viewBox="0 0 16 16" class="bi bi-shield-shaded" fill="currentColor" xmlns="http://www.w3.org/2000/svg">
|
2 |
+
<path fill-rule="evenodd" d="M5.443 1.991a60.17 60.17 0 0 0-2.725.802.454.454 0 0 0-.315.366C1.87 7.056 3.1 9.9 4.567 11.773c.736.94 1.533 1.636 2.197 2.093.333.228.626.394.857.5.116.053.21.089.282.11A.73.73 0 0 0 8 14.5c.007-.001.038-.005.097-.023.072-.022.166-.058.282-.111.23-.106.525-.272.857-.5a10.197 10.197 0 0 0 2.197-2.093C12.9 9.9 14.13 7.056 13.597 3.159a.454.454 0 0 0-.315-.366c-.626-.2-1.682-.526-2.725-.802C9.491 1.71 8.51 1.5 8 1.5c-.51 0-1.49.21-2.557.491zm-.256-.966C6.23.749 7.337.5 8 .5c.662 0 1.77.249 2.813.525a61.09 61.09 0 0 1 2.772.815c.528.168.926.623 1.003 1.184.573 4.197-.756 7.307-2.367 9.365a11.191 11.191 0 0 1-2.418 2.3 6.942 6.942 0 0 1-1.007.586c-.27.124-.558.225-.796.225s-.526-.101-.796-.225a6.908 6.908 0 0 1-1.007-.586 11.192 11.192 0 0 1-2.417-2.3C2.167 10.331.839 7.221 1.412 3.024A1.454 1.454 0 0 1 2.415 1.84a61.11 61.11 0 0 1 2.772-.815z"/>
|
3 |
+
<path d="M8 2.25c.909 0 3.188.685 4.254 1.022a.94.94 0 0 1 .656.773c.814 6.424-4.13 9.452-4.91 9.452V2.25z"/>
|
4 |
+
</svg>
|
resources/images/bootstrap/sliders.svg
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
1 |
+
<svg width="1em" height="1em" viewBox="0 0 16 16" class="bi bi-sliders" fill="currentColor" xmlns="http://www.w3.org/2000/svg">
|
2 |
+
<path fill-rule="evenodd" d="M11.5 2a1.5 1.5 0 1 0 0 3 1.5 1.5 0 0 0 0-3zM9.05 3a2.5 2.5 0 0 1 4.9 0H16v1h-2.05a2.5 2.5 0 0 1-4.9 0H0V3h9.05zM4.5 7a1.5 1.5 0 1 0 0 3 1.5 1.5 0 0 0 0-3zM2.05 8a2.5 2.5 0 0 1 4.9 0H16v1H6.95a2.5 2.5 0 0 1-4.9 0H0V8h2.05zm9.45 4a1.5 1.5 0 1 0 0 3 1.5 1.5 0 0 0 0-3zm-2.45 1a2.5 2.5 0 0 1 4.9 0H16v1h-2.05a2.5 2.5 0 0 1-4.9 0H0v-1h9.05z"/>
|
3 |
+
</svg>
|
resources/images/bootstrap/stickies.svg
ADDED
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<svg width="1em" height="1em" viewBox="0 0 16 16" class="bi bi-stickies" fill="currentColor" xmlns="http://www.w3.org/2000/svg">
|
2 |
+
<path fill-rule="evenodd" d="M0 1.5A1.5 1.5 0 0 1 1.5 0H13a1 1 0 0 1 1 1H1.5a.5.5 0 0 0-.5.5V14a1 1 0 0 1-1-1V1.5z"/>
|
3 |
+
<path fill-rule="evenodd" d="M2 3.5A1.5 1.5 0 0 1 3.5 2h11A1.5 1.5 0 0 1 16 3.5v6.086a1.5 1.5 0 0 1-.44 1.06l-4.914 4.915a1.5 1.5 0 0 1-1.06.439H3.5A1.5 1.5 0 0 1 2 14.5v-11zM3.5 3a.5.5 0 0 0-.5.5v11a.5.5 0 0 0 .5.5h6.086a.5.5 0 0 0 .353-.146l4.915-4.915A.5.5 0 0 0 15 9.586V3.5a.5.5 0 0 0-.5-.5h-11z"/>
|
4 |
+
<path fill-rule="evenodd" d="M10.5 10a.5.5 0 0 0-.5.5v5H9v-5A1.5 1.5 0 0 1 10.5 9h5v1h-5z"/>
|
5 |
+
</svg>
|
resources/images/bootstrap/sticky.svg
ADDED
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
1 |
+
<svg width="1em" height="1em" viewBox="0 0 16 16" class="bi bi-sticky" fill="currentColor" xmlns="http://www.w3.org/2000/svg">
|
2 |
+
<path fill-rule="evenodd" d="M1 2.5A1.5 1.5 0 0 1 2.5 1h11A1.5 1.5 0 0 1 15 2.5v6.086a1.5 1.5 0 0 1-.44 1.06l-4.914 4.915a1.5 1.5 0 0 1-1.06.439H2.5A1.5 1.5 0 0 1 1 13.5v-11zM2.5 2a.5.5 0 0 0-.5.5v11a.5.5 0 0 0 .5.5h6.086a.5.5 0 0 0 .353-.146l4.915-4.915A.5.5 0 0 0 14 8.586V2.5a.5.5 0 0 0-.5-.5h-11z"/>
|
3 |
+
<path fill-rule="evenodd" d="M9.5 9a.5.5 0 0 0-.5.5v5H8v-5A1.5 1.5 0 0 1 9.5 8h5v1h-5z"/>
|
4 |
+
</svg>
|
resources/images/bootstrap/stoplights.svg
ADDED
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<svg width="1em" height="1em" viewBox="0 0 16 16" class="bi bi-stoplights" fill="currentColor" xmlns="http://www.w3.org/2000/svg">
|
2 |
+
<path d="M9.5 3.5a1.5 1.5 0 1 1-3 0 1.5 1.5 0 0 1 3 0zm0 4a1.5 1.5 0 1 1-3 0 1.5 1.5 0 0 1 3 0zm0 4a1.5 1.5 0 1 1-3 0 1.5 1.5 0 0 1 3 0z"/>
|
3 |
+
<path fill-rule="evenodd" d="M10 1H6a1 1 0 0 0-1 1v11a1 1 0 0 0 1 1h4a1 1 0 0 0 1-1V2a1 1 0 0 0-1-1zM6 0a2 2 0 0 0-2 2v11a2 2 0 0 0 2 2h4a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2H6z"/>
|
4 |
+
<path d="M14 2h-2v2c1.2-.4 1.833-1.5 2-2zM2 2h2v2c-1.2-.4-1.833-1.5-2-2zm12 4h-2v2c1.2-.4 1.833-1.5 2-2zM2 6h2v2c-1.2-.4-1.833-1.5-2-2zm12 4h-2v2c1.2-.4 1.833-1.5 2-2zM2 10h2v2c-1.2-.4-1.833-1.5-2-2z"/>
|
5 |
+
</svg>
|
resources/images/shield/dash-background.jpg
CHANGED
Binary file
|
resources/js/bootstrap-select.min.js
CHANGED
@@ -1,9 +1,9 @@
|
|
1 |
/*!
|
2 |
-
* Bootstrap-select v1.13.
|
3 |
*
|
4 |
* Copyright 2012-2020 SnapAppointments, LLC
|
5 |
* Licensed under MIT (https://github.com/snapappointments/bootstrap-select/blob/master/LICENSE)
|
6 |
*/
|
7 |
|
8 |
-
!function(e,t){void 0===e&&void 0!==window&&(e=window),"function"==typeof define&&define.amd?define(["jquery"],function(e){return t(e)}):"object"==typeof module&&module.exports?module.exports=t(require("jquery")):t(e.jQuery)}(this,function(e){!function(z){"use strict";var d=["sanitize","whiteList","sanitizeFn"],r=["background","cite","href","itemtype","longdesc","poster","src","xlink:href"],e={"*":["class","dir","id","lang","role","tabindex","style",/^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","alt","title","width","height"],li:[],ol:[],p:[],pre:[],s:[],small:[],span:[],sub:[],sup:[],strong:[],u:[],ul:[]},l=/^(?:(?:https?|mailto|ftp|tel|file):|[^&:/?#]*(?:[/?#]|$))/gi,a=/^data:(?:image\/(?:bmp|gif|jpeg|jpg|png|tiff|webp)|video\/(?:mpeg|mp4|ogg|webm)|audio\/(?:mp3|oga|ogg|opus));base64,[a-z0-9+/]+=*$/i;function v(e,t){var i=e.nodeName.toLowerCase();if(-1!==z.inArray(i,t))return-1===z.inArray(i,r)||Boolean(e.nodeValue.match(l)||e.nodeValue.match(a));for(var s=z(t).filter(function(e,t){return t instanceof RegExp}),n=0,o=s.length;n<o;n++)if(i.match(s[n]))return!0;return!1}function P(e,t,i){if(i&&"function"==typeof i)return i(e);for(var s=Object.keys(t),n=0,o=e.length;n<o;n++)for(var r=e[n].querySelectorAll("*"),l=0,a=r.length;l<a;l++){var c=r[l],d=c.nodeName.toLowerCase();if(-1!==s.indexOf(d))for(var h=[].slice.call(c.attributes),p=[].concat(t["*"]||[],t[d]||[]),u=0,f=h.length;u<f;u++){var m=h[u];v(m,p)||c.removeAttribute(m.nodeName)}else c.parentNode.removeChild(c)}}"classList"in document.createElement("_")||function(e){if("Element"in e){var t="classList",i="prototype",s=e.Element[i],n=Object,o=function(){var i=z(this);return{add:function(e){return e=Array.prototype.slice.call(arguments).join(" "),i.addClass(e)},remove:function(e){return e=Array.prototype.slice.call(arguments).join(" "),i.removeClass(e)},toggle:function(e,t){return i.toggleClass(e,t)},contains:function(e){return i.hasClass(e)}}};if(n.defineProperty){var r={get:o,enumerable:!0,configurable:!0};try{n.defineProperty(s,t,r)}catch(e){void 0!==e.number&&-2146823252!==e.number||(r.enumerable=!1,n.defineProperty(s,t,r))}}else n[i].__defineGetter__&&s.__defineGetter__(t,o)}}(window);var t,c,i=document.createElement("_");if(i.classList.add("c1","c2"),!i.classList.contains("c2")){var s=DOMTokenList.prototype.add,n=DOMTokenList.prototype.remove;DOMTokenList.prototype.add=function(){Array.prototype.forEach.call(arguments,s.bind(this))},DOMTokenList.prototype.remove=function(){Array.prototype.forEach.call(arguments,n.bind(this))}}if(i.classList.toggle("c3",!1),i.classList.contains("c3")){var o=DOMTokenList.prototype.toggle;DOMTokenList.prototype.toggle=function(e,t){return 1 in arguments&&!this.contains(e)==!t?t:o.call(this,e)}}function h(e){if(null==this)throw new TypeError;var t=String(this);if(e&&"[object RegExp]"==c.call(e))throw new TypeError;var i=t.length,s=String(e),n=s.length,o=1<arguments.length?arguments[1]:void 0,r=o?Number(o):0;r!=r&&(r=0);var l=Math.min(Math.max(r,0),i);if(i<n+l)return!1;for(var a=-1;++a<n;)if(t.charCodeAt(l+a)!=s.charCodeAt(a))return!1;return!0}function O(e,t){var i,s=e.selectedOptions,n=[];if(t){for(var o=0,r=s.length;o<r;o++)(i=s[o]).disabled||"OPTGROUP"===i.parentNode.tagName&&i.parentNode.disabled||n.push(i);return n}return s}function T(e,t){for(var i,s=[],n=t||e.selectedOptions,o=0,r=n.length;o<r;o++)(i=n[o]).disabled||"OPTGROUP"===i.parentNode.tagName&&i.parentNode.disabled||s.push(i.value);return e.multiple?s:s.length?s[0]:null}i=null,String.prototype.startsWith||(t=function(){try{var e={},t=Object.defineProperty,i=t(e,e,e)&&t}catch(e){}return i}(),c={}.toString,t?t(String.prototype,"startsWith",{value:h,configurable:!0,writable:!0}):String.prototype.startsWith=h),Object.keys||(Object.keys=function(e,t,i){for(t in i=[],e)i.hasOwnProperty.call(e,t)&&i.push(t);return i}),HTMLSelectElement&&!HTMLSelectElement.prototype.hasOwnProperty("selectedOptions")&&Object.defineProperty(HTMLSelectElement.prototype,"selectedOptions",{get:function(){return this.querySelectorAll(":checked")}});var p={useDefault:!1,_set:z.valHooks.select.set};z.valHooks.select.set=function(e,t){return t&&!p.useDefault&&z(e).data("selected",!0),p._set.apply(this,arguments)};var A=null,u=function(){try{return new Event("change"),!0}catch(e){return!1}}();function k(e,t,i,s){for(var n=["display","subtext","tokens"],o=!1,r=0;r<n.length;r++){var l=n[r],a=e[l];if(a&&(a=a.toString(),"display"===l&&(a=a.replace(/<[^>]+>/g,"")),s&&(a=w(a)),a=a.toUpperCase(),o="contains"===i?0<=a.indexOf(t):a.startsWith(t)))break}return o}function L(e){return parseInt(e,10)||0}z.fn.triggerNative=function(e){var t,i=this[0];i.dispatchEvent?(u?t=new Event(e,{bubbles:!0}):(t=document.createEvent("Event")).initEvent(e,!0,!1),i.dispatchEvent(t)):i.fireEvent?((t=document.createEventObject()).eventType=e,i.fireEvent("on"+e,t)):this.trigger(e)};var f={"\xc0":"A","\xc1":"A","\xc2":"A","\xc3":"A","\xc4":"A","\xc5":"A","\xe0":"a","\xe1":"a","\xe2":"a","\xe3":"a","\xe4":"a","\xe5":"a","\xc7":"C","\xe7":"c","\xd0":"D","\xf0":"d","\xc8":"E","\xc9":"E","\xca":"E","\xcb":"E","\xe8":"e","\xe9":"e","\xea":"e","\xeb":"e","\xcc":"I","\xcd":"I","\xce":"I","\xcf":"I","\xec":"i","\xed":"i","\xee":"i","\xef":"i","\xd1":"N","\xf1":"n","\xd2":"O","\xd3":"O","\xd4":"O","\xd5":"O","\xd6":"O","\xd8":"O","\xf2":"o","\xf3":"o","\xf4":"o","\xf5":"o","\xf6":"o","\xf8":"o","\xd9":"U","\xda":"U","\xdb":"U","\xdc":"U","\xf9":"u","\xfa":"u","\xfb":"u","\xfc":"u","\xdd":"Y","\xfd":"y","\xff":"y","\xc6":"Ae","\xe6":"ae","\xde":"Th","\xfe":"th","\xdf":"ss","\u0100":"A","\u0102":"A","\u0104":"A","\u0101":"a","\u0103":"a","\u0105":"a","\u0106":"C","\u0108":"C","\u010a":"C","\u010c":"C","\u0107":"c","\u0109":"c","\u010b":"c","\u010d":"c","\u010e":"D","\u0110":"D","\u010f":"d","\u0111":"d","\u0112":"E","\u0114":"E","\u0116":"E","\u0118":"E","\u011a":"E","\u0113":"e","\u0115":"e","\u0117":"e","\u0119":"e","\u011b":"e","\u011c":"G","\u011e":"G","\u0120":"G","\u0122":"G","\u011d":"g","\u011f":"g","\u0121":"g","\u0123":"g","\u0124":"H","\u0126":"H","\u0125":"h","\u0127":"h","\u0128":"I","\u012a":"I","\u012c":"I","\u012e":"I","\u0130":"I","\u0129":"i","\u012b":"i","\u012d":"i","\u012f":"i","\u0131":"i","\u0134":"J","\u0135":"j","\u0136":"K","\u0137":"k","\u0138":"k","\u0139":"L","\u013b":"L","\u013d":"L","\u013f":"L","\u0141":"L","\u013a":"l","\u013c":"l","\u013e":"l","\u0140":"l","\u0142":"l","\u0143":"N","\u0145":"N","\u0147":"N","\u014a":"N","\u0144":"n","\u0146":"n","\u0148":"n","\u014b":"n","\u014c":"O","\u014e":"O","\u0150":"O","\u014d":"o","\u014f":"o","\u0151":"o","\u0154":"R","\u0156":"R","\u0158":"R","\u0155":"r","\u0157":"r","\u0159":"r","\u015a":"S","\u015c":"S","\u015e":"S","\u0160":"S","\u015b":"s","\u015d":"s","\u015f":"s","\u0161":"s","\u0162":"T","\u0164":"T","\u0166":"T","\u0163":"t","\u0165":"t","\u0167":"t","\u0168":"U","\u016a":"U","\u016c":"U","\u016e":"U","\u0170":"U","\u0172":"U","\u0169":"u","\u016b":"u","\u016d":"u","\u016f":"u","\u0171":"u","\u0173":"u","\u0174":"W","\u0175":"w","\u0176":"Y","\u0177":"y","\u0178":"Y","\u0179":"Z","\u017b":"Z","\u017d":"Z","\u017a":"z","\u017c":"z","\u017e":"z","\u0132":"IJ","\u0133":"ij","\u0152":"Oe","\u0153":"oe","\u0149":"'n","\u017f":"s"},m=/[\xc0-\xd6\xd8-\xf6\xf8-\xff\u0100-\u017f]/g,g=RegExp("[\\u0300-\\u036f\\ufe20-\\ufe2f\\u20d0-\\u20ff\\u1ab0-\\u1aff\\u1dc0-\\u1dff]","g");function b(e){return f[e]}function w(e){return(e=e.toString())&&e.replace(m,b).replace(g,"")}var I,x,y,$,S=(I={"&":"&","<":"<",">":">",'"':""","'":"'","`":"`"},x="(?:"+Object.keys(I).join("|")+")",y=RegExp(x),$=RegExp(x,"g"),function(e){return e=null==e?"":""+e,y.test(e)?e.replace($,E):e});function E(e){return I[e]}var C={32:" ",48:"0",49:"1",50:"2",51:"3",52:"4",53:"5",54:"6",55:"7",56:"8",57:"9",59:";",65:"A",66:"B",67:"C",68:"D",69:"E",70:"F",71:"G",72:"H",73:"I",74:"J",75:"K",76:"L",77:"M",78:"N",79:"O",80:"P",81:"Q",82:"R",83:"S",84:"T",85:"U",86:"V",87:"W",88:"X",89:"Y",90:"Z",96:"0",97:"1",98:"2",99:"3",100:"4",101:"5",102:"6",103:"7",104:"8",105:"9"},N=27,D=13,H=32,W=9,B=38,M=40,R={success:!1,major:"3"};try{R.full=(z.fn.dropdown.Constructor.VERSION||"").split(" ")[0].split("."),R.major=R.full[0],R.success=!0}catch(e){}var U=0,j=".bs.select",V={DISABLED:"disabled",DIVIDER:"divider",SHOW:"open",DROPUP:"dropup",MENU:"dropdown-menu",MENURIGHT:"dropdown-menu-right",MENULEFT:"dropdown-menu-left",BUTTONCLASS:"btn-default",POPOVERHEADER:"popover-title",ICONBASE:"glyphicon",TICKICON:"glyphicon-ok"},F={MENU:"."+V.MENU},_={span:document.createElement("span"),i:document.createElement("i"),subtext:document.createElement("small"),a:document.createElement("a"),li:document.createElement("li"),whitespace:document.createTextNode("\xa0"),fragment:document.createDocumentFragment()};_.a.setAttribute("role","option"),"4"===R.major&&(_.a.className="dropdown-item"),_.subtext.className="text-muted",_.text=_.span.cloneNode(!1),_.text.className="text",_.checkMark=_.span.cloneNode(!1);var G=new RegExp(B+"|"+M),q=new RegExp("^"+W+"$|"+N),K={li:function(e,t,i){var s=_.li.cloneNode(!1);return e&&(1===e.nodeType||11===e.nodeType?s.appendChild(e):s.innerHTML=e),void 0!==t&&""!==t&&(s.className=t),null!=i&&s.classList.add("optgroup-"+i),s},a:function(e,t,i){var s=_.a.cloneNode(!0);return e&&(11===e.nodeType?s.appendChild(e):s.insertAdjacentHTML("beforeend",e)),void 0!==t&&""!==t&&s.classList.add.apply(s.classList,t.split(" ")),i&&s.setAttribute("style",i),s},text:function(e,t){var i,s,n=_.text.cloneNode(!1);if(e.content)n.innerHTML=e.content;else{if(n.textContent=e.text,e.icon){var o=_.whitespace.cloneNode(!1);(s=(!0===t?_.i:_.span).cloneNode(!1)).className=this.options.iconBase+" "+e.icon,_.fragment.appendChild(s),_.fragment.appendChild(o)}e.subtext&&((i=_.subtext.cloneNode(!1)).textContent=e.subtext,n.appendChild(i))}if(!0===t)for(;0<n.childNodes.length;)_.fragment.appendChild(n.childNodes[0]);else _.fragment.appendChild(n);return _.fragment},label:function(e){var t,i,s=_.text.cloneNode(!1);if(s.innerHTML=e.display,e.icon){var n=_.whitespace.cloneNode(!1);(i=_.span.cloneNode(!1)).className=this.options.iconBase+" "+e.icon,_.fragment.appendChild(i),_.fragment.appendChild(n)}return e.subtext&&((t=_.subtext.cloneNode(!1)).textContent=e.subtext,s.appendChild(t)),_.fragment.appendChild(s),_.fragment}},Y=function(e,t){var i=this;p.useDefault||(z.valHooks.select.set=p._set,p.useDefault=!0),this.$element=z(e),this.$newElement=null,this.$button=null,this.$menu=null,this.options=t,this.selectpicker={main:{},search:{},current:{},view:{},isSearching:!1,keydown:{keyHistory:"",resetKeyHistory:{start:function(){return setTimeout(function(){i.selectpicker.keydown.keyHistory=""},800)}}}},this.sizeInfo={},null===this.options.title&&(this.options.title=this.$element.attr("title"));var s=this.options.windowPadding;"number"==typeof s&&(this.options.windowPadding=[s,s,s,s]),this.val=Y.prototype.val,this.render=Y.prototype.render,this.refresh=Y.prototype.refresh,this.setStyle=Y.prototype.setStyle,this.selectAll=Y.prototype.selectAll,this.deselectAll=Y.prototype.deselectAll,this.destroy=Y.prototype.destroy,this.remove=Y.prototype.remove,this.show=Y.prototype.show,this.hide=Y.prototype.hide,this.init()};function Z(e){var l,a=arguments,c=e;if([].shift.apply(a),!R.success){try{R.full=(z.fn.dropdown.Constructor.VERSION||"").split(" ")[0].split(".")}catch(e){Y.BootstrapVersion?R.full=Y.BootstrapVersion.split(" ")[0].split("."):(R.full=[R.major,"0","0"],console.warn("There was an issue retrieving Bootstrap's version. Ensure Bootstrap is being loaded before bootstrap-select and there is no namespace collision. If loading Bootstrap asynchronously, the version may need to be manually specified via $.fn.selectpicker.Constructor.BootstrapVersion.",e))}R.major=R.full[0],R.success=!0}if("4"===R.major){var t=[];Y.DEFAULTS.style===V.BUTTONCLASS&&t.push({name:"style",className:"BUTTONCLASS"}),Y.DEFAULTS.iconBase===V.ICONBASE&&t.push({name:"iconBase",className:"ICONBASE"}),Y.DEFAULTS.tickIcon===V.TICKICON&&t.push({name:"tickIcon",className:"TICKICON"}),V.DIVIDER="dropdown-divider",V.SHOW="show",V.BUTTONCLASS="btn-light",V.POPOVERHEADER="popover-header",V.ICONBASE="",V.TICKICON="bs-ok-default";for(var i=0;i<t.length;i++){e=t[i];Y.DEFAULTS[e.name]=V[e.className]}}var s=this.each(function(){var e=z(this);if(e.is("select")){var t=e.data("selectpicker"),i="object"==typeof c&&c;if(t){if(i)for(var s in i)i.hasOwnProperty(s)&&(t.options[s]=i[s])}else{var n=e.data();for(var o in n)n.hasOwnProperty(o)&&-1!==z.inArray(o,d)&&delete n[o];var r=z.extend({},Y.DEFAULTS,z.fn.selectpicker.defaults||{},n,i);r.template=z.extend({},Y.DEFAULTS.template,z.fn.selectpicker.defaults?z.fn.selectpicker.defaults.template:{},n.template,i.template),e.data("selectpicker",t=new Y(this,r))}"string"==typeof c&&(l=t[c]instanceof Function?t[c].apply(t,a):t.options[c])}});return void 0!==l?l:s}Y.VERSION="1.13.14",Y.DEFAULTS={noneSelectedText:"Nothing selected",noneResultsText:"No results matched {0}",countSelectedText:function(e,t){return 1==e?"{0} item selected":"{0} items selected"},maxOptionsText:function(e,t){return[1==e?"Limit reached ({n} item max)":"Limit reached ({n} items max)",1==t?"Group limit reached ({n} item max)":"Group limit reached ({n} items max)"]},selectAllText:"Select All",deselectAllText:"Deselect All",doneButton:!1,doneButtonText:"Close",multipleSeparator:", ",styleBase:"btn",style:V.BUTTONCLASS,size:"auto",title:null,selectedTextFormat:"values",width:!1,container:!1,hideDisabled:!1,showSubtext:!1,showIcon:!0,showContent:!0,dropupAuto:!0,header:!1,liveSearch:!1,liveSearchPlaceholder:null,liveSearchNormalize:!1,liveSearchStyle:"contains",actionsBox:!1,iconBase:V.ICONBASE,tickIcon:V.TICKICON,showTick:!1,template:{caret:'<span class="caret"></span>'},maxOptions:!1,mobile:!1,selectOnTab:!1,dropdownAlignRight:!1,windowPadding:0,virtualScroll:600,display:!1,sanitize:!0,sanitizeFn:null,whiteList:e},Y.prototype={constructor:Y,init:function(){var i=this,e=this.$element.attr("id");U++,this.selectId="bs-select-"+U,this.$element[0].classList.add("bs-select-hidden"),this.multiple=this.$element.prop("multiple"),this.autofocus=this.$element.prop("autofocus"),this.$element[0].classList.contains("show-tick")&&(this.options.showTick=!0),this.$newElement=this.createDropdown(),this.buildData(),this.$element.after(this.$newElement).prependTo(this.$newElement),this.$button=this.$newElement.children("button"),this.$menu=this.$newElement.children(F.MENU),this.$menuInner=this.$menu.children(".inner"),this.$searchbox=this.$menu.find("input"),this.$element[0].classList.remove("bs-select-hidden"),!0===this.options.dropdownAlignRight&&this.$menu[0].classList.add(V.MENURIGHT),void 0!==e&&this.$button.attr("data-id",e),this.checkDisabled(),this.clickListener(),this.options.liveSearch?(this.liveSearchListener(),this.focusedParent=this.$searchbox[0]):this.focusedParent=this.$menuInner[0],this.setStyle(),this.render(),this.setWidth(),this.options.container?this.selectPosition():this.$element.on("hide"+j,function(){if(i.isVirtual()){var e=i.$menuInner[0],t=e.firstChild.cloneNode(!1);e.replaceChild(t,e.firstChild),e.scrollTop=0}}),this.$menu.data("this",this),this.$newElement.data("this",this),this.options.mobile&&this.mobile(),this.$newElement.on({"hide.bs.dropdown":function(e){i.$element.trigger("hide"+j,e)},"hidden.bs.dropdown":function(e){i.$element.trigger("hidden"+j,e)},"show.bs.dropdown":function(e){i.$element.trigger("show"+j,e)},"shown.bs.dropdown":function(e){i.$element.trigger("shown"+j,e)}}),i.$element[0].hasAttribute("required")&&this.$element.on("invalid"+j,function(){i.$button[0].classList.add("bs-invalid"),i.$element.on("shown"+j+".invalid",function(){i.$element.val(i.$element.val()).off("shown"+j+".invalid")}).on("rendered"+j,function(){this.validity.valid&&i.$button[0].classList.remove("bs-invalid"),i.$element.off("rendered"+j)}),i.$button.on("blur"+j,function(){i.$element.trigger("focus").trigger("blur"),i.$button.off("blur"+j)})}),setTimeout(function(){i.buildList(),i.$element.trigger("loaded"+j)})},createDropdown:function(){var e=this.multiple||this.options.showTick?" show-tick":"",t=this.multiple?' aria-multiselectable="true"':"",i="",s=this.autofocus?" autofocus":"";R.major<4&&this.$element.parent().hasClass("input-group")&&(i=" input-group-btn");var n,o="",r="",l="",a="";return this.options.header&&(o='<div class="'+V.POPOVERHEADER+'"><button type="button" class="close" aria-hidden="true">×</button>'+this.options.header+"</div>"),this.options.liveSearch&&(r='<div class="bs-searchbox"><input type="search" class="form-control" autocomplete="off"'+(null===this.options.liveSearchPlaceholder?"":' placeholder="'+S(this.options.liveSearchPlaceholder)+'"')+' role="combobox" aria-label="Search" aria-controls="'+this.selectId+'" aria-autocomplete="list"></div>'),this.multiple&&this.options.actionsBox&&(l='<div class="bs-actionsbox"><div class="btn-group btn-group-sm btn-block"><button type="button" class="actions-btn bs-select-all btn '+V.BUTTONCLASS+'">'+this.options.selectAllText+'</button><button type="button" class="actions-btn bs-deselect-all btn '+V.BUTTONCLASS+'">'+this.options.deselectAllText+"</button></div></div>"),this.multiple&&this.options.doneButton&&(a='<div class="bs-donebutton"><div class="btn-group btn-block"><button type="button" class="btn btn-sm '+V.BUTTONCLASS+'">'+this.options.doneButtonText+"</button></div></div>"),n='<div class="dropdown bootstrap-select'+e+i+'"><button type="button" class="'+this.options.styleBase+' dropdown-toggle" '+("static"===this.options.display?'data-display="static"':"")+'data-toggle="dropdown"'+s+' role="combobox" aria-owns="'+this.selectId+'" aria-haspopup="listbox" aria-expanded="false"><div class="filter-option"><div class="filter-option-inner"><div class="filter-option-inner-inner"></div></div> </div>'+("4"===R.major?"":'<span class="bs-caret">'+this.options.template.caret+"</span>")+'</button><div class="'+V.MENU+" "+("4"===R.major?"":V.SHOW)+'">'+o+r+l+'<div class="inner '+V.SHOW+'" role="listbox" id="'+this.selectId+'" tabindex="-1" '+t+'><ul class="'+V.MENU+" inner "+("4"===R.major?V.SHOW:"")+'" role="presentation"></ul></div>'+a+"</div></div>",z(n)},setPositionData:function(){this.selectpicker.view.canHighlight=[];for(var e=this.selectpicker.view.size=0;e<this.selectpicker.current.data.length;e++){var t=this.selectpicker.current.data[e],i=!0;"divider"===t.type?(i=!1,t.height=this.sizeInfo.dividerHeight):"optgroup-label"===t.type?(i=!1,t.height=this.sizeInfo.dropdownHeaderHeight):t.height=this.sizeInfo.liHeight,t.disabled&&(i=!1),this.selectpicker.view.canHighlight.push(i),i&&(this.selectpicker.view.size++,t.posinset=this.selectpicker.view.size),t.position=(0===e?0:this.selectpicker.current.data[e-1].position)+t.height}},isVirtual:function(){return!1!==this.options.virtualScroll&&this.selectpicker.main.elements.length>=this.options.virtualScroll||!0===this.options.virtualScroll},createView:function(A,e,t){var L,N,D=this,i=0,H=[];if(this.selectpicker.isSearching=A,this.selectpicker.current=A?this.selectpicker.search:this.selectpicker.main,this.setPositionData(),e)if(t)i=this.$menuInner[0].scrollTop;else if(!D.multiple){var s=D.$element[0],n=(s.options[s.selectedIndex]||{}).liIndex;if("number"==typeof n&&!1!==D.options.size){var o=D.selectpicker.main.data[n],r=o&&o.position;r&&(i=r-(D.sizeInfo.menuInnerHeight+D.sizeInfo.liHeight)/2)}}function l(e,t){var i,s,n,o,r,l,a,c,d=D.selectpicker.current.elements.length,h=[],p=!0,u=D.isVirtual();D.selectpicker.view.scrollTop=e,i=Math.ceil(D.sizeInfo.menuInnerHeight/D.sizeInfo.liHeight*1.5),s=Math.round(d/i)||1;for(var f=0;f<s;f++){var m=(f+1)*i;if(f===s-1&&(m=d),h[f]=[f*i+(f?1:0),m],!d)break;void 0===r&&e-1<=D.selectpicker.current.data[m-1].position-D.sizeInfo.menuInnerHeight&&(r=f)}if(void 0===r&&(r=0),l=[D.selectpicker.view.position0,D.selectpicker.view.position1],n=Math.max(0,r-1),o=Math.min(s-1,r+1),D.selectpicker.view.position0=!1===u?0:Math.max(0,h[n][0])||0,D.selectpicker.view.position1=!1===u?d:Math.min(d,h[o][1])||0,a=l[0]!==D.selectpicker.view.position0||l[1]!==D.selectpicker.view.position1,void 0!==D.activeIndex&&(N=D.selectpicker.main.elements[D.prevActiveIndex],H=D.selectpicker.main.elements[D.activeIndex],L=D.selectpicker.main.elements[D.selectedIndex],t&&(D.activeIndex!==D.selectedIndex&&D.defocusItem(H),D.activeIndex=void 0),D.activeIndex&&D.activeIndex!==D.selectedIndex&&D.defocusItem(L)),void 0!==D.prevActiveIndex&&D.prevActiveIndex!==D.activeIndex&&D.prevActiveIndex!==D.selectedIndex&&D.defocusItem(N),(t||a)&&(c=D.selectpicker.view.visibleElements?D.selectpicker.view.visibleElements.slice():[],D.selectpicker.view.visibleElements=!1===u?D.selectpicker.current.elements:D.selectpicker.current.elements.slice(D.selectpicker.view.position0,D.selectpicker.view.position1),D.setOptionStatus(),(A||!1===u&&t)&&(p=!function(e,i){return e.length===i.length&&e.every(function(e,t){return e===i[t]})}(c,D.selectpicker.view.visibleElements)),(t||!0===u)&&p)){var v,g,b=D.$menuInner[0],w=document.createDocumentFragment(),I=b.firstChild.cloneNode(!1),x=D.selectpicker.view.visibleElements,k=[];b.replaceChild(I,b.firstChild);f=0;for(var y=x.length;f<y;f++){var $,S,E=x[f];D.options.sanitize&&($=E.lastChild)&&(S=D.selectpicker.current.data[f+D.selectpicker.view.position0])&&S.content&&!S.sanitized&&(k.push($),S.sanitized=!0),w.appendChild(E)}if(D.options.sanitize&&k.length&&P(k,D.options.whiteList,D.options.sanitizeFn),!0===u?(v=0===D.selectpicker.view.position0?0:D.selectpicker.current.data[D.selectpicker.view.position0-1].position,g=D.selectpicker.view.position1>d-1?0:D.selectpicker.current.data[d-1].position-D.selectpicker.current.data[D.selectpicker.view.position1-1].position,b.firstChild.style.marginTop=v+"px",b.firstChild.style.marginBottom=g+"px"):(b.firstChild.style.marginTop=0,b.firstChild.style.marginBottom=0),b.firstChild.appendChild(w),!0===u&&D.sizeInfo.hasScrollBar){var C=b.firstChild.offsetWidth;if(t&&C<D.sizeInfo.menuInnerInnerWidth&&D.sizeInfo.totalMenuWidth>D.sizeInfo.selectWidth)b.firstChild.style.minWidth=D.sizeInfo.menuInnerInnerWidth+"px";else if(C>D.sizeInfo.menuInnerInnerWidth){D.$menu[0].style.minWidth=0;var O=b.firstChild.offsetWidth;O>D.sizeInfo.menuInnerInnerWidth&&(D.sizeInfo.menuInnerInnerWidth=O,b.firstChild.style.minWidth=D.sizeInfo.menuInnerInnerWidth+"px"),D.$menu[0].style.minWidth=""}}}if(D.prevActiveIndex=D.activeIndex,D.options.liveSearch){if(A&&t){var z,T=0;D.selectpicker.view.canHighlight[T]||(T=1+D.selectpicker.view.canHighlight.slice(1).indexOf(!0)),z=D.selectpicker.view.visibleElements[T],D.defocusItem(D.selectpicker.view.currentActive),D.activeIndex=(D.selectpicker.current.data[T]||{}).index,D.focusItem(z)}}else D.$menuInner.trigger("focus")}l(i,!0),this.$menuInner.off("scroll.createView").on("scroll.createView",function(e,t){D.noScroll||l(this.scrollTop,t),D.noScroll=!1}),z(window).off("resize"+j+"."+this.selectId+".createView").on("resize"+j+"."+this.selectId+".createView",function(){D.$newElement.hasClass(V.SHOW)&&l(D.$menuInner[0].scrollTop)})},focusItem:function(e,t,i){if(e){t=t||this.selectpicker.main.data[this.activeIndex];var s=e.firstChild;s&&(s.setAttribute("aria-setsize",this.selectpicker.view.size),s.setAttribute("aria-posinset",t.posinset),!0!==i&&(this.focusedParent.setAttribute("aria-activedescendant",s.id),e.classList.add("active"),s.classList.add("active")))}},defocusItem:function(e){e&&(e.classList.remove("active"),e.firstChild&&e.firstChild.classList.remove("active"))},setPlaceholder:function(){var e=!1;if(this.options.title&&!this.multiple){this.selectpicker.view.titleOption||(this.selectpicker.view.titleOption=document.createElement("option")),e=!0;var t=this.$element[0],i=!1,s=!this.selectpicker.view.titleOption.parentNode;if(s)this.selectpicker.view.titleOption.className="bs-title-option",this.selectpicker.view.titleOption.value="",i=void 0===z(t.options[t.selectedIndex]).attr("selected")&&void 0===this.$element.data("selected");!s&&0===this.selectpicker.view.titleOption.index||t.insertBefore(this.selectpicker.view.titleOption,t.firstChild),i&&(t.selectedIndex=0)}return e},buildData:function(){var p=':not([hidden]):not([data-hidden="true"])',u=[],f=0,e=this.setPlaceholder()?1:0;this.options.hideDisabled&&(p+=":not(:disabled)");var t=this.$element[0].querySelectorAll("select > *"+p);function m(e){var t=u[u.length-1];t&&"divider"===t.type&&(t.optID||e.optID)||((e=e||{}).type="divider",u.push(e))}function v(e,t){if((t=t||{}).divider="true"===e.getAttribute("data-divider"),t.divider)m({optID:t.optID});else{var i=u.length,s=e.style.cssText,n=s?S(s):"",o=(e.className||"")+(t.optgroupClass||"");t.optID&&(o="opt "+o),t.optionClass=o.trim(),t.inlineStyle=n,t.text=e.textContent,t.content=e.getAttribute("data-content"),t.tokens=e.getAttribute("data-tokens"),t.subtext=e.getAttribute("data-subtext"),t.icon=e.getAttribute("data-icon"),e.liIndex=i,t.display=t.content||t.text,t.type="option",t.index=i,t.option=e,t.selected=!!e.selected,t.disabled=t.disabled||!!e.disabled,u.push(t)}}function i(e,t){var i=t[e],s=t[e-1],n=t[e+1],o=i.querySelectorAll("option"+p);if(o.length){var r,l,a={display:S(i.label),subtext:i.getAttribute("data-subtext"),icon:i.getAttribute("data-icon"),type:"optgroup-label",optgroupClass:" "+(i.className||"")};f++,s&&m({optID:f}),a.optID=f,u.push(a);for(var c=0,d=o.length;c<d;c++){var h=o[c];0===c&&(l=(r=u.length-1)+d),v(h,{headerIndex:r,lastIndex:l,optID:a.optID,optgroupClass:a.optgroupClass,disabled:i.disabled})}n&&m({optID:f})}}for(var s=t.length;e<s;e++){var n=t[e];"OPTGROUP"!==n.tagName?v(n,{}):i(e,t)}this.selectpicker.main.data=this.selectpicker.current.data=u},buildList:function(){var s=this,e=this.selectpicker.main.data,n=[],o=0;function t(e){var t,i=0;switch(e.type){case"divider":t=K.li(!1,V.DIVIDER,e.optID?e.optID+"div":void 0);break;case"option":(t=K.li(K.a(K.text.call(s,e),e.optionClass,e.inlineStyle),"",e.optID)).firstChild&&(t.firstChild.id=s.selectId+"-"+e.index);break;case"optgroup-label":t=K.li(K.label.call(s,e),"dropdown-header"+e.optgroupClass,e.optID)}n.push(t),e.display&&(i+=e.display.length),e.subtext&&(i+=e.subtext.length),e.icon&&(i+=1),o<i&&(o=i,s.selectpicker.view.widestOption=n[n.length-1])}!s.options.showTick&&!s.multiple||_.checkMark.parentNode||(_.checkMark.className=this.options.iconBase+" "+s.options.tickIcon+" check-mark",_.a.appendChild(_.checkMark));for(var i=e.length,r=0;r<i;r++){t(e[r])}this.selectpicker.main.elements=this.selectpicker.current.elements=n},findLis:function(){return this.$menuInner.find(".inner > li")},render:function(){var e,t=this,i=this.$element[0],s=this.setPlaceholder()&&0===i.selectedIndex,n=O(i,this.options.hideDisabled),o=n.length,r=this.$button[0],l=r.querySelector(".filter-option-inner-inner"),a=document.createTextNode(this.options.multipleSeparator),c=_.fragment.cloneNode(!1),d=!1;if(r.classList.toggle("bs-placeholder",t.multiple?!o:!T(i,n)),this.tabIndex(),"static"===this.options.selectedTextFormat)c=K.text.call(this,{text:this.options.title},!0);else if(!1===(this.multiple&&-1!==this.options.selectedTextFormat.indexOf("count")&&1<o&&(1<(e=this.options.selectedTextFormat.split(">")).length&&o>e[1]||1===e.length&&2<=o))){if(!s){for(var h=0;h<o&&h<50;h++){var p=n[h],u=this.selectpicker.main.data[p.liIndex],f={};this.multiple&&0<h&&c.appendChild(a.cloneNode(!1)),p.title?f.text=p.title:u&&(u.content&&t.options.showContent?(f.content=u.content.toString(),d=!0):(t.options.showIcon&&(f.icon=u.icon),t.options.showSubtext&&!t.multiple&&u.subtext&&(f.subtext=" "+u.subtext),f.text=p.textContent.trim())),c.appendChild(K.text.call(this,f,!0))}49<o&&c.appendChild(document.createTextNode("..."))}}else{var m=':not([hidden]):not([data-hidden="true"]):not([data-divider="true"])';this.options.hideDisabled&&(m+=":not(:disabled)");var v=this.$element[0].querySelectorAll("select > option"+m+", optgroup"+m+" option"+m).length,g="function"==typeof this.options.countSelectedText?this.options.countSelectedText(o,v):this.options.countSelectedText;c=K.text.call(this,{text:g.replace("{0}",o.toString()).replace("{1}",v.toString())},!0)}if(null==this.options.title&&(this.options.title=this.$element.attr("title")),c.childNodes.length||(c=K.text.call(this,{text:void 0!==this.options.title?this.options.title:this.options.noneSelectedText},!0)),r.title=c.textContent.replace(/<[^>]*>?/g,"").trim(),this.options.sanitize&&d&&P([c],t.options.whiteList,t.options.sanitizeFn),l.innerHTML="",l.appendChild(c),R.major<4&&this.$newElement[0].classList.contains("bs3-has-addon")){var b=r.querySelector(".filter-expand"),w=l.cloneNode(!0);w.className="filter-expand",b?r.replaceChild(w,b):r.appendChild(w)}this.$element.trigger("rendered"+j)},setStyle:function(e,t){var i,s=this.$button[0],n=this.$newElement[0],o=this.options.style.trim();this.$element.attr("class")&&this.$newElement.addClass(this.$element.attr("class").replace(/selectpicker|mobile-device|bs-select-hidden|validate\[.*\]/gi,"")),R.major<4&&(n.classList.add("bs3"),n.parentNode.classList.contains("input-group")&&(n.previousElementSibling||n.nextElementSibling)&&(n.previousElementSibling||n.nextElementSibling).classList.contains("input-group-addon")&&n.classList.add("bs3-has-addon")),i=e?e.trim():o,"add"==t?i&&s.classList.add.apply(s.classList,i.split(" ")):"remove"==t?i&&s.classList.remove.apply(s.classList,i.split(" ")):(o&&s.classList.remove.apply(s.classList,o.split(" ")),i&&s.classList.add.apply(s.classList,i.split(" ")))},liHeight:function(e){if(e||!1!==this.options.size&&!Object.keys(this.sizeInfo).length){var t=document.createElement("div"),i=document.createElement("div"),s=document.createElement("div"),n=document.createElement("ul"),o=document.createElement("li"),r=document.createElement("li"),l=document.createElement("li"),a=document.createElement("a"),c=document.createElement("span"),d=this.options.header&&0<this.$menu.find("."+V.POPOVERHEADER).length?this.$menu.find("."+V.POPOVERHEADER)[0].cloneNode(!0):null,h=this.options.liveSearch?document.createElement("div"):null,p=this.options.actionsBox&&this.multiple&&0<this.$menu.find(".bs-actionsbox").length?this.$menu.find(".bs-actionsbox")[0].cloneNode(!0):null,u=this.options.doneButton&&this.multiple&&0<this.$menu.find(".bs-donebutton").length?this.$menu.find(".bs-donebutton")[0].cloneNode(!0):null,f=this.$element.find("option")[0];if(this.sizeInfo.selectWidth=this.$newElement[0].offsetWidth,c.className="text",a.className="dropdown-item "+(f?f.className:""),t.className=this.$menu[0].parentNode.className+" "+V.SHOW,t.style.width=0,"auto"===this.options.width&&(i.style.minWidth=0),i.className=V.MENU+" "+V.SHOW,s.className="inner "+V.SHOW,n.className=V.MENU+" inner "+("4"===R.major?V.SHOW:""),o.className=V.DIVIDER,r.className="dropdown-header",c.appendChild(document.createTextNode("\u200b")),a.appendChild(c),l.appendChild(a),r.appendChild(c.cloneNode(!0)),this.selectpicker.view.widestOption&&n.appendChild(this.selectpicker.view.widestOption.cloneNode(!0)),n.appendChild(l),n.appendChild(o),n.appendChild(r),d&&i.appendChild(d),h){var m=document.createElement("input");h.className="bs-searchbox",m.className="form-control",h.appendChild(m),i.appendChild(h)}p&&i.appendChild(p),s.appendChild(n),i.appendChild(s),u&&i.appendChild(u),t.appendChild(i),document.body.appendChild(t);var v,g=l.offsetHeight,b=r?r.offsetHeight:0,w=d?d.offsetHeight:0,I=h?h.offsetHeight:0,x=p?p.offsetHeight:0,k=u?u.offsetHeight:0,y=z(o).outerHeight(!0),$=!!window.getComputedStyle&&window.getComputedStyle(i),S=i.offsetWidth,E=$?null:z(i),C={vert:L($?$.paddingTop:E.css("paddingTop"))+L($?$.paddingBottom:E.css("paddingBottom"))+L($?$.borderTopWidth:E.css("borderTopWidth"))+L($?$.borderBottomWidth:E.css("borderBottomWidth")),horiz:L($?$.paddingLeft:E.css("paddingLeft"))+L($?$.paddingRight:E.css("paddingRight"))+L($?$.borderLeftWidth:E.css("borderLeftWidth"))+L($?$.borderRightWidth:E.css("borderRightWidth"))},O={vert:C.vert+L($?$.marginTop:E.css("marginTop"))+L($?$.marginBottom:E.css("marginBottom"))+2,horiz:C.horiz+L($?$.marginLeft:E.css("marginLeft"))+L($?$.marginRight:E.css("marginRight"))+2};s.style.overflowY="scroll",v=i.offsetWidth-S,document.body.removeChild(t),this.sizeInfo.liHeight=g,this.sizeInfo.dropdownHeaderHeight=b,this.sizeInfo.headerHeight=w,this.sizeInfo.searchHeight=I,this.sizeInfo.actionsHeight=x,this.sizeInfo.doneButtonHeight=k,this.sizeInfo.dividerHeight=y,this.sizeInfo.menuPadding=C,this.sizeInfo.menuExtras=O,this.sizeInfo.menuWidth=S,this.sizeInfo.menuInnerInnerWidth=S-C.horiz,this.sizeInfo.totalMenuWidth=this.sizeInfo.menuWidth,this.sizeInfo.scrollBarWidth=v,this.sizeInfo.selectHeight=this.$newElement[0].offsetHeight,this.setPositionData()}},getSelectPosition:function(){var e,t=z(window),i=this.$newElement.offset(),s=z(this.options.container);this.options.container&&s.length&&!s.is("body")?((e=s.offset()).top+=parseInt(s.css("borderTopWidth")),e.left+=parseInt(s.css("borderLeftWidth"))):e={top:0,left:0};var n=this.options.windowPadding;this.sizeInfo.selectOffsetTop=i.top-e.top-t.scrollTop(),this.sizeInfo.selectOffsetBot=t.height()-this.sizeInfo.selectOffsetTop-this.sizeInfo.selectHeight-e.top-n[2],this.sizeInfo.selectOffsetLeft=i.left-e.left-t.scrollLeft(),this.sizeInfo.selectOffsetRight=t.width()-this.sizeInfo.selectOffsetLeft-this.sizeInfo.selectWidth-e.left-n[1],this.sizeInfo.selectOffsetTop-=n[0],this.sizeInfo.selectOffsetLeft-=n[3]},setMenuSize:function(e){this.getSelectPosition();var t,i,s,n,o,r,l,a,c=this.sizeInfo.selectWidth,d=this.sizeInfo.liHeight,h=this.sizeInfo.headerHeight,p=this.sizeInfo.searchHeight,u=this.sizeInfo.actionsHeight,f=this.sizeInfo.doneButtonHeight,m=this.sizeInfo.dividerHeight,v=this.sizeInfo.menuPadding,g=0;if(this.options.dropupAuto&&(l=d*this.selectpicker.current.elements.length+v.vert,a=this.sizeInfo.selectOffsetTop-this.sizeInfo.selectOffsetBot>this.sizeInfo.menuExtras.vert&&l+this.sizeInfo.menuExtras.vert+50>this.sizeInfo.selectOffsetBot,!0===this.selectpicker.isSearching&&(a=this.selectpicker.dropup),this.$newElement.toggleClass(V.DROPUP,a),this.selectpicker.dropup=a),"auto"===this.options.size)n=3<this.selectpicker.current.elements.length?3*this.sizeInfo.liHeight+this.sizeInfo.menuExtras.vert-2:0,i=this.sizeInfo.selectOffsetBot-this.sizeInfo.menuExtras.vert,s=n+h+p+u+f,r=Math.max(n-v.vert,0),this.$newElement.hasClass(V.DROPUP)&&(i=this.sizeInfo.selectOffsetTop-this.sizeInfo.menuExtras.vert),t=(o=i)-h-p-u-f-v.vert;else if(this.options.size&&"auto"!=this.options.size&&this.selectpicker.current.elements.length>this.options.size){for(var b=0;b<this.options.size;b++)"divider"===this.selectpicker.current.data[b].type&&g++;t=(i=d*this.options.size+g*m+v.vert)-v.vert,o=i+h+p+u+f,s=r=""}this.$menu.css({"max-height":o+"px",overflow:"hidden","min-height":s+"px"}),this.$menuInner.css({"max-height":t+"px","overflow-y":"auto","min-height":r+"px"}),this.sizeInfo.menuInnerHeight=Math.max(t,1),this.selectpicker.current.data.length&&this.selectpicker.current.data[this.selectpicker.current.data.length-1].position>this.sizeInfo.menuInnerHeight&&(this.sizeInfo.hasScrollBar=!0,this.sizeInfo.totalMenuWidth=this.sizeInfo.menuWidth+this.sizeInfo.scrollBarWidth),"auto"===this.options.dropdownAlignRight&&this.$menu.toggleClass(V.MENURIGHT,this.sizeInfo.selectOffsetLeft>this.sizeInfo.selectOffsetRight&&this.sizeInfo.selectOffsetRight<this.sizeInfo.totalMenuWidth-c),this.dropdown&&this.dropdown._popper&&this.dropdown._popper.update()},setSize:function(e){if(this.liHeight(e),this.options.header&&this.$menu.css("padding-top",0),!1!==this.options.size){var t=this,i=z(window);this.setMenuSize(),this.options.liveSearch&&this.$searchbox.off("input.setMenuSize propertychange.setMenuSize").on("input.setMenuSize propertychange.setMenuSize",function(){return t.setMenuSize()}),"auto"===this.options.size?i.off("resize"+j+"."+this.selectId+".setMenuSize scroll"+j+"."+this.selectId+".setMenuSize").on("resize"+j+"."+this.selectId+".setMenuSize scroll"+j+"."+this.selectId+".setMenuSize",function(){return t.setMenuSize()}):this.options.size&&"auto"!=this.options.size&&this.selectpicker.current.elements.length>this.options.size&&i.off("resize"+j+"."+this.selectId+".setMenuSize scroll"+j+"."+this.selectId+".setMenuSize")}this.createView(!1,!0,e)},setWidth:function(){var i=this;"auto"===this.options.width?requestAnimationFrame(function(){i.$menu.css("min-width","0"),i.$element.on("loaded"+j,function(){i.liHeight(),i.setMenuSize();var e=i.$newElement.clone().appendTo("body"),t=e.css("width","auto").children("button").outerWidth();e.remove(),i.sizeInfo.selectWidth=Math.max(i.sizeInfo.totalMenuWidth,t),i.$newElement.css("width",i.sizeInfo.selectWidth+"px")})}):"fit"===this.options.width?(this.$menu.css("min-width",""),this.$newElement.css("width","").addClass("fit-width")):this.options.width?(this.$menu.css("min-width",""),this.$newElement.css("width",this.options.width)):(this.$menu.css("min-width",""),this.$newElement.css("width","")),this.$newElement.hasClass("fit-width")&&"fit"!==this.options.width&&this.$newElement[0].classList.remove("fit-width")},selectPosition:function(){this.$bsContainer=z('<div class="bs-container" />');function e(e){var t={},i=r.options.display||!!z.fn.dropdown.Constructor.Default&&z.fn.dropdown.Constructor.Default.display;r.$bsContainer.addClass(e.attr("class").replace(/form-control|fit-width/gi,"")).toggleClass(V.DROPUP,e.hasClass(V.DROPUP)),s=e.offset(),l.is("body")?n={top:0,left:0}:((n=l.offset()).top+=parseInt(l.css("borderTopWidth"))-l.scrollTop(),n.left+=parseInt(l.css("borderLeftWidth"))-l.scrollLeft()),o=e.hasClass(V.DROPUP)?0:e[0].offsetHeight,(R.major<4||"static"===i)&&(t.top=s.top-n.top+o,t.left=s.left-n.left),t.width=e[0].offsetWidth,r.$bsContainer.css(t)}var s,n,o,r=this,l=z(this.options.container);this.$button.on("click.bs.dropdown.data-api",function(){r.isDisabled()||(e(r.$newElement),r.$bsContainer.appendTo(r.options.container).toggleClass(V.SHOW,!r.$button.hasClass(V.SHOW)).append(r.$menu))}),z(window).off("resize"+j+"."+this.selectId+" scroll"+j+"."+this.selectId).on("resize"+j+"."+this.selectId+" scroll"+j+"."+this.selectId,function(){r.$newElement.hasClass(V.SHOW)&&e(r.$newElement)}),this.$element.on("hide"+j,function(){r.$menu.data("height",r.$menu.height()),r.$bsContainer.detach()})},setOptionStatus:function(e){var t=this;if(t.noScroll=!1,t.selectpicker.view.visibleElements&&t.selectpicker.view.visibleElements.length)for(var i=0;i<t.selectpicker.view.visibleElements.length;i++){var s=t.selectpicker.current.data[i+t.selectpicker.view.position0],n=s.option;n&&(!0!==e&&t.setDisabled(s.index,s.disabled),t.setSelected(s.index,n.selected))}},setSelected:function(e,t){var i,s,n=this.selectpicker.main.elements[e],o=this.selectpicker.main.data[e],r=void 0!==this.activeIndex,l=this.activeIndex===e||t&&!this.multiple&&!r;o.selected=t,s=n.firstChild,t&&(this.selectedIndex=e),n.classList.toggle("selected",t),l?(this.focusItem(n,o),this.selectpicker.view.currentActive=n,this.activeIndex=e):this.defocusItem(n),s&&(s.classList.toggle("selected",t),t?s.setAttribute("aria-selected",!0):this.multiple?s.setAttribute("aria-selected",!1):s.removeAttribute("aria-selected")),l||r||!t||void 0===this.prevActiveIndex||(i=this.selectpicker.main.elements[this.prevActiveIndex],this.defocusItem(i))},setDisabled:function(e,t){var i,s=this.selectpicker.main.elements[e];this.selectpicker.main.data[e].disabled=t,i=s.firstChild,s.classList.toggle(V.DISABLED,t),i&&("4"===R.major&&i.classList.toggle(V.DISABLED,t),t?(i.setAttribute("aria-disabled",t),i.setAttribute("tabindex",-1)):(i.removeAttribute("aria-disabled"),i.setAttribute("tabindex",0)))},isDisabled:function(){return this.$element[0].disabled},checkDisabled:function(){this.isDisabled()?(this.$newElement[0].classList.add(V.DISABLED),this.$button.addClass(V.DISABLED).attr("tabindex",-1).attr("aria-disabled",!0)):(this.$button[0].classList.contains(V.DISABLED)&&(this.$newElement[0].classList.remove(V.DISABLED),this.$button.removeClass(V.DISABLED).attr("aria-disabled",!1)),-1!=this.$button.attr("tabindex")||this.$element.data("tabindex")||this.$button.removeAttr("tabindex"))},tabIndex:function(){this.$element.data("tabindex")!==this.$element.attr("tabindex")&&-98!==this.$element.attr("tabindex")&&"-98"!==this.$element.attr("tabindex")&&(this.$element.data("tabindex",this.$element.attr("tabindex")),this.$button.attr("tabindex",this.$element.data("tabindex"))),this.$element.attr("tabindex",-98)},clickListener:function(){var C=this,t=z(document);function e(){C.options.liveSearch?C.$searchbox.trigger("focus"):C.$menuInner.trigger("focus")}function i(){C.dropdown&&C.dropdown._popper&&C.dropdown._popper.state.isCreated?e():requestAnimationFrame(i)}t.data("spaceSelect",!1),this.$button.on("keyup",function(e){/(32)/.test(e.keyCode.toString(10))&&t.data("spaceSelect")&&(e.preventDefault(),t.data("spaceSelect",!1))}),this.$newElement.on("show.bs.dropdown",function(){3<R.major&&!C.dropdown&&(C.dropdown=C.$button.data("bs.dropdown"),C.dropdown._menu=C.$menu[0])}),this.$button.on("click.bs.dropdown.data-api",function(){C.$newElement.hasClass(V.SHOW)||C.setSize()}),this.$element.on("shown"+j,function(){C.$menuInner[0].scrollTop!==C.selectpicker.view.scrollTop&&(C.$menuInner[0].scrollTop=C.selectpicker.view.scrollTop),3<R.major?requestAnimationFrame(i):e()}),this.$menuInner.on("mouseenter","li a",function(e){var t=this.parentElement,i=C.isVirtual()?C.selectpicker.view.position0:0,s=Array.prototype.indexOf.call(t.parentElement.children,t),n=C.selectpicker.current.data[s+i];C.focusItem(t,n,!0)}),this.$menuInner.on("click","li a",function(e,t){var i=z(this),s=C.$element[0],n=C.isVirtual()?C.selectpicker.view.position0:0,o=C.selectpicker.current.data[i.parent().index()+n],r=o.index,l=T(s),a=s.selectedIndex,c=s.options[a],d=!0;if(C.multiple&&1!==C.options.maxOptions&&e.stopPropagation(),e.preventDefault(),!C.isDisabled()&&!i.parent().hasClass(V.DISABLED)){var h=o.option,p=z(h),u=h.selected,f=p.parent("optgroup"),m=f.find("option"),v=C.options.maxOptions,g=f.data("maxOptions")||!1;if(r===C.activeIndex&&(t=!0),t||(C.prevActiveIndex=C.activeIndex,C.activeIndex=void 0),C.multiple){if(h.selected=!u,C.setSelected(r,!u),i.trigger("blur"),!1!==v||!1!==g){var b=v<O(s).length,w=g<f.find("option:selected").length;if(v&&b||g&&w)if(v&&1==v)s.selectedIndex=-1,h.selected=!0,C.setOptionStatus(!0);else if(g&&1==g){for(var I=0;I<m.length;I++){var x=m[I];x.selected=!1,C.setSelected(x.liIndex,!1)}h.selected=!0,C.setSelected(r,!0)}else{var k="string"==typeof C.options.maxOptionsText?[C.options.maxOptionsText,C.options.maxOptionsText]:C.options.maxOptionsText,y="function"==typeof k?k(v,g):k,$=y[0].replace("{n}",v),S=y[1].replace("{n}",g),E=z('<div class="notify"></div>');y[2]&&($=$.replace("{var}",y[2][1<v?0:1]),S=S.replace("{var}",y[2][1<g?0:1])),h.selected=!1,C.$menu.append(E),v&&b&&(E.append(z("<div>"+$+"</div>")),d=!1,C.$element.trigger("maxReached"+j)),g&&w&&(E.append(z("<div>"+S+"</div>")),d=!1,C.$element.trigger("maxReachedGrp"+j)),setTimeout(function(){C.setSelected(r,!1)},10),E[0].classList.add("fadeOut"),setTimeout(function(){E.remove()},1050)}}}else c&&(c.selected=!1),h.selected=!0,C.setSelected(r,!0);!C.multiple||C.multiple&&1===C.options.maxOptions?C.$button.trigger("focus"):C.options.liveSearch&&C.$searchbox.trigger("focus"),d&&(!C.multiple&&a===s.selectedIndex||(A=[h.index,p.prop("selected"),l],C.$element.triggerNative("change")))}}),this.$menu.on("click","li."+V.DISABLED+" a, ."+V.POPOVERHEADER+", ."+V.POPOVERHEADER+" :not(.close)",function(e){e.currentTarget==this&&(e.preventDefault(),e.stopPropagation(),C.options.liveSearch&&!z(e.target).hasClass("close")?C.$searchbox.trigger("focus"):C.$button.trigger("focus"))}),this.$menuInner.on("click",".divider, .dropdown-header",function(e){e.preventDefault(),e.stopPropagation(),C.options.liveSearch?C.$searchbox.trigger("focus"):C.$button.trigger("focus")}),this.$menu.on("click","."+V.POPOVERHEADER+" .close",function(){C.$button.trigger("click")}),this.$searchbox.on("click",function(e){e.stopPropagation()}),this.$menu.on("click",".actions-btn",function(e){C.options.liveSearch?C.$searchbox.trigger("focus"):C.$button.trigger("focus"),e.preventDefault(),e.stopPropagation(),z(this).hasClass("bs-select-all")?C.selectAll():C.deselectAll()}),this.$element.on("change"+j,function(){C.render(),C.$element.trigger("changed"+j,A),A=null}).on("focus"+j,function(){C.options.mobile||C.$button.trigger("focus")})},liveSearchListener:function(){var u=this,f=document.createElement("li");this.$button.on("click.bs.dropdown.data-api",function(){u.$searchbox.val()&&u.$searchbox.val("")}),this.$searchbox.on("click.bs.dropdown.data-api focus.bs.dropdown.data-api touchend.bs.dropdown.data-api",function(e){e.stopPropagation()}),this.$searchbox.on("input propertychange",function(){var e=u.$searchbox.val();if(u.selectpicker.search.elements=[],u.selectpicker.search.data=[],e){var t=[],i=e.toUpperCase(),s={},n=[],o=u._searchStyle(),r=u.options.liveSearchNormalize;r&&(i=w(i));for(var l=0;l<u.selectpicker.main.data.length;l++){var a=u.selectpicker.main.data[l];s[l]||(s[l]=k(a,i,o,r)),s[l]&&void 0!==a.headerIndex&&-1===n.indexOf(a.headerIndex)&&(0<a.headerIndex&&(s[a.headerIndex-1]=!0,n.push(a.headerIndex-1)),s[a.headerIndex]=!0,n.push(a.headerIndex),s[a.lastIndex+1]=!0),s[l]&&"optgroup-label"!==a.type&&n.push(l)}l=0;for(var c=n.length;l<c;l++){var d=n[l],h=n[l-1],p=(a=u.selectpicker.main.data[d],u.selectpicker.main.data[h]);("divider"!==a.type||"divider"===a.type&&p&&"divider"!==p.type&&c-1!==l)&&(u.selectpicker.search.data.push(a),t.push(u.selectpicker.main.elements[d]))}u.activeIndex=void 0,u.noScroll=!0,u.$menuInner.scrollTop(0),u.selectpicker.search.elements=t,u.createView(!0),t.length||(f.className="no-results",f.innerHTML=u.options.noneResultsText.replace("{0}",'"'+S(e)+'"'),u.$menuInner[0].firstChild.appendChild(f))}else u.$menuInner.scrollTop(0),u.createView(!1)})},_searchStyle:function(){return this.options.liveSearchStyle||"contains"},val:function(e){var t=this.$element[0];if(void 0===e)return this.$element.val();var i=T(t);if(A=[null,null,i],this.$element.val(e).trigger("changed"+j,A),this.$newElement.hasClass(V.SHOW))if(this.multiple)this.setOptionStatus(!0);else{var s=(t.options[t.selectedIndex]||{}).liIndex;"number"==typeof s&&(this.setSelected(this.selectedIndex,!1),this.setSelected(s,!0))}return this.render(),A=null,this.$element},changeAll:function(e){if(this.multiple){void 0===e&&(e=!0);var t=this.$element[0],i=0,s=0,n=T(t);t.classList.add("bs-select-hidden");for(var o=0,r=this.selectpicker.current.data,l=r.length;o<l;o++){var a=r[o],c=a.option;c&&!a.disabled&&"divider"!==a.type&&(a.selected&&i++,!0===(c.selected=e)&&s++)}t.classList.remove("bs-select-hidden"),i!==s&&(this.setOptionStatus(),A=[null,null,n],this.$element.triggerNative("change"))}},selectAll:function(){return this.changeAll(!0)},deselectAll:function(){return this.changeAll(!1)},toggle:function(e){(e=e||window.event)&&e.stopPropagation(),this.$button.trigger("click.bs.dropdown.data-api")},keydown:function(e){var t,i,s,n,o,r=z(this),l=r.hasClass("dropdown-toggle"),a=(l?r.closest(".dropdown"):r.closest(F.MENU)).data("this"),c=a.findLis(),d=!1,h=e.which===W&&!l&&!a.options.selectOnTab,p=G.test(e.which)||h,u=a.$menuInner[0].scrollTop,f=!0===a.isVirtual()?a.selectpicker.view.position0:0;if(!(112<=e.which&&e.which<=123))if(!(i=a.$newElement.hasClass(V.SHOW))&&(p||48<=e.which&&e.which<=57||96<=e.which&&e.which<=105||65<=e.which&&e.which<=90)&&(a.$button.trigger("click.bs.dropdown.data-api"),a.options.liveSearch))a.$searchbox.trigger("focus");else{if(e.which===N&&i&&(e.preventDefault(),a.$button.trigger("click.bs.dropdown.data-api").trigger("focus")),p){if(!c.length)return;-1!==(t=(s=a.selectpicker.main.elements[a.activeIndex])?Array.prototype.indexOf.call(s.parentElement.children,s):-1)&&a.defocusItem(s),e.which===B?(-1!==t&&t--,t+f<0&&(t+=c.length),a.selectpicker.view.canHighlight[t+f]||-1===(t=a.selectpicker.view.canHighlight.slice(0,t+f).lastIndexOf(!0)-f)&&(t=c.length-1)):e.which!==M&&!h||(++t+f>=a.selectpicker.view.canHighlight.length&&(t=0),a.selectpicker.view.canHighlight[t+f]||(t=t+1+a.selectpicker.view.canHighlight.slice(t+f+1).indexOf(!0))),e.preventDefault();var m=f+t;e.which===B?0===f&&t===c.length-1?(a.$menuInner[0].scrollTop=a.$menuInner[0].scrollHeight,m=a.selectpicker.current.elements.length-1):d=(o=(n=a.selectpicker.current.data[m]).position-n.height)<u:e.which!==M&&!h||(0===t?m=a.$menuInner[0].scrollTop=0:d=u<(o=(n=a.selectpicker.current.data[m]).position-a.sizeInfo.menuInnerHeight)),s=a.selectpicker.current.elements[m],a.activeIndex=a.selectpicker.current.data[m].index,a.focusItem(s),a.selectpicker.view.currentActive=s,d&&(a.$menuInner[0].scrollTop=o),a.options.liveSearch?a.$searchbox.trigger("focus"):r.trigger("focus")}else if(!r.is("input")&&!q.test(e.which)||e.which===H&&a.selectpicker.keydown.keyHistory){var v,g,b=[];e.preventDefault(),a.selectpicker.keydown.keyHistory+=C[e.which],a.selectpicker.keydown.resetKeyHistory.cancel&&clearTimeout(a.selectpicker.keydown.resetKeyHistory.cancel),a.selectpicker.keydown.resetKeyHistory.cancel=a.selectpicker.keydown.resetKeyHistory.start(),g=a.selectpicker.keydown.keyHistory,/^(.)\1+$/.test(g)&&(g=g.charAt(0));for(var w=0;w<a.selectpicker.current.data.length;w++){var I=a.selectpicker.current.data[w];k(I,g,"startsWith",!0)&&a.selectpicker.view.canHighlight[w]&&b.push(I.index)}if(b.length){var x=0;c.removeClass("active").find("a").removeClass("active"),1===g.length&&(-1===(x=b.indexOf(a.activeIndex))||x===b.length-1?x=0:x++),v=b[x],d=0<u-(n=a.selectpicker.main.data[v]).position?(o=n.position-n.height,!0):(o=n.position-a.sizeInfo.menuInnerHeight,n.position>u+a.sizeInfo.menuInnerHeight),s=a.selectpicker.main.elements[v],a.activeIndex=b[x],a.focusItem(s),s&&s.firstChild.focus(),d&&(a.$menuInner[0].scrollTop=o),r.trigger("focus")}}i&&(e.which===H&&!a.selectpicker.keydown.keyHistory||e.which===D||e.which===W&&a.options.selectOnTab)&&(e.which!==H&&e.preventDefault(),a.options.liveSearch&&e.which===H||(a.$menuInner.find(".active a").trigger("click",!0),r.trigger("focus"),a.options.liveSearch||(e.preventDefault(),z(document).data("spaceSelect",!0))))}},mobile:function(){this.$element[0].classList.add("mobile-device")},refresh:function(){var e=z.extend({},this.options,this.$element.data());this.options=e,this.checkDisabled(),this.setStyle(),this.render(),this.buildData(),this.buildList(),this.setWidth(),this.setSize(!0),this.$element.trigger("refreshed"+j)},hide:function(){this.$newElement.hide()},show:function(){this.$newElement.show()},remove:function(){this.$newElement.remove(),this.$element.remove()},destroy:function(){this.$newElement.before(this.$element).remove(),this.$bsContainer?this.$bsContainer.remove():this.$menu.remove(),this.$element.off(j).removeData("selectpicker").removeClass("bs-select-hidden selectpicker"),z(window).off(j+"."+this.selectId)}};var J=z.fn.selectpicker;z.fn.selectpicker=Z,z.fn.selectpicker.Constructor=Y,z.fn.selectpicker.noConflict=function(){return z.fn.selectpicker=J,this};var Q=z.fn.dropdown.Constructor._dataApiKeydownHandler||z.fn.dropdown.Constructor.prototype.keydown;z(document).off("keydown.bs.dropdown.data-api").on("keydown.bs.dropdown.data-api",':not(.bootstrap-select) > [data-toggle="dropdown"]',Q).on("keydown.bs.dropdown.data-api",":not(.bootstrap-select) > .dropdown-menu",Q).on("keydown"+j,'.bootstrap-select [data-toggle="dropdown"], .bootstrap-select [role="listbox"], .bootstrap-select .bs-searchbox input',Y.prototype.keydown).on("focusin.modal",'.bootstrap-select [data-toggle="dropdown"], .bootstrap-select [role="listbox"], .bootstrap-select .bs-searchbox input',function(e){e.stopPropagation()}),z(window).on("load"+j+".data-api",function(){z(".selectpicker").each(function(){var e=z(this);Z.call(e,e.data())})})}(e)});
|
9 |
//# sourceMappingURL=bootstrap-select.min.js.map
|
1 |
/*!
|
2 |
+
* Bootstrap-select v1.13.18 (https://developer.snapappointments.com/bootstrap-select)
|
3 |
*
|
4 |
* Copyright 2012-2020 SnapAppointments, LLC
|
5 |
* Licensed under MIT (https://github.com/snapappointments/bootstrap-select/blob/master/LICENSE)
|
6 |
*/
|
7 |
|
8 |
+
!function(e,t){void 0===e&&void 0!==window&&(e=window),"function"==typeof define&&define.amd?define(["jquery"],function(e){return t(e)}):"object"==typeof module&&module.exports?module.exports=t(require("jquery")):t(e.jQuery)}(this,function(e){!function(P){"use strict";var d=["sanitize","whiteList","sanitizeFn"],r=["background","cite","href","itemtype","longdesc","poster","src","xlink:href"],e={"*":["class","dir","id","lang","role","tabindex","style",/^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","alt","title","width","height"],li:[],ol:[],p:[],pre:[],s:[],small:[],span:[],sub:[],sup:[],strong:[],u:[],ul:[]},l=/^(?:(?:https?|mailto|ftp|tel|file):|[^&:/?#]*(?:[/?#]|$))/gi,a=/^data:(?:image\/(?:bmp|gif|jpeg|jpg|png|tiff|webp)|video\/(?:mpeg|mp4|ogg|webm)|audio\/(?:mp3|oga|ogg|opus));base64,[a-z0-9+/]+=*$/i;function v(e,t){var i=e.nodeName.toLowerCase();if(-1!==P.inArray(i,t))return-1===P.inArray(i,r)||Boolean(e.nodeValue.match(l)||e.nodeValue.match(a));for(var s=P(t).filter(function(e,t){return t instanceof RegExp}),n=0,o=s.length;n<o;n++)if(i.match(s[n]))return!0;return!1}function W(e,t,i){if(i&&"function"==typeof i)return i(e);for(var s=Object.keys(t),n=0,o=e.length;n<o;n++)for(var r=e[n].querySelectorAll("*"),l=0,a=r.length;l<a;l++){var c=r[l],d=c.nodeName.toLowerCase();if(-1!==s.indexOf(d))for(var h=[].slice.call(c.attributes),p=[].concat(t["*"]||[],t[d]||[]),u=0,f=h.length;u<f;u++){var m=h[u];v(m,p)||c.removeAttribute(m.nodeName)}else c.parentNode.removeChild(c)}}"classList"in document.createElement("_")||function(e){if("Element"in e){var t="classList",i="prototype",s=e.Element[i],n=Object,o=function(){var i=P(this);return{add:function(e){return e=Array.prototype.slice.call(arguments).join(" "),i.addClass(e)},remove:function(e){return e=Array.prototype.slice.call(arguments).join(" "),i.removeClass(e)},toggle:function(e,t){return i.toggleClass(e,t)},contains:function(e){return i.hasClass(e)}}};if(n.defineProperty){var r={get:o,enumerable:!0,configurable:!0};try{n.defineProperty(s,t,r)}catch(e){void 0!==e.number&&-2146823252!==e.number||(r.enumerable=!1,n.defineProperty(s,t,r))}}else n[i].__defineGetter__&&s.__defineGetter__(t,o)}}(window);var t,c,i=document.createElement("_");if(i.classList.add("c1","c2"),!i.classList.contains("c2")){var s=DOMTokenList.prototype.add,n=DOMTokenList.prototype.remove;DOMTokenList.prototype.add=function(){Array.prototype.forEach.call(arguments,s.bind(this))},DOMTokenList.prototype.remove=function(){Array.prototype.forEach.call(arguments,n.bind(this))}}if(i.classList.toggle("c3",!1),i.classList.contains("c3")){var o=DOMTokenList.prototype.toggle;DOMTokenList.prototype.toggle=function(e,t){return 1 in arguments&&!this.contains(e)==!t?t:o.call(this,e)}}function h(e){if(null==this)throw new TypeError;var t=String(this);if(e&&"[object RegExp]"==c.call(e))throw new TypeError;var i=t.length,s=String(e),n=s.length,o=1<arguments.length?arguments[1]:void 0,r=o?Number(o):0;r!=r&&(r=0);var l=Math.min(Math.max(r,0),i);if(i<n+l)return!1;for(var a=-1;++a<n;)if(t.charCodeAt(l+a)!=s.charCodeAt(a))return!1;return!0}function O(e,t){var i,s=e.selectedOptions,n=[];if(t){for(var o=0,r=s.length;o<r;o++)(i=s[o]).disabled||"OPTGROUP"===i.parentNode.tagName&&i.parentNode.disabled||n.push(i);return n}return s}function z(e,t){for(var i,s=[],n=t||e.selectedOptions,o=0,r=n.length;o<r;o++)(i=n[o]).disabled||"OPTGROUP"===i.parentNode.tagName&&i.parentNode.disabled||s.push(i.value);return e.multiple?s:s.length?s[0]:null}i=null,String.prototype.startsWith||(t=function(){try{var e={},t=Object.defineProperty,i=t(e,e,e)&&t}catch(e){}return i}(),c={}.toString,t?t(String.prototype,"startsWith",{value:h,configurable:!0,writable:!0}):String.prototype.startsWith=h),Object.keys||(Object.keys=function(e,t,i){for(t in i=[],e)i.hasOwnProperty.call(e,t)&&i.push(t);return i}),HTMLSelectElement&&!HTMLSelectElement.prototype.hasOwnProperty("selectedOptions")&&Object.defineProperty(HTMLSelectElement.prototype,"selectedOptions",{get:function(){return this.querySelectorAll(":checked")}});var p={useDefault:!1,_set:P.valHooks.select.set};P.valHooks.select.set=function(e,t){return t&&!p.useDefault&&P(e).data("selected",!0),p._set.apply(this,arguments)};var T=null,u=function(){try{return new Event("change"),!0}catch(e){return!1}}();function k(e,t,i,s){for(var n=["display","subtext","tokens"],o=!1,r=0;r<n.length;r++){var l=n[r],a=e[l];if(a&&(a=a.toString(),"display"===l&&(a=a.replace(/<[^>]+>/g,"")),s&&(a=w(a)),a=a.toUpperCase(),o="contains"===i?0<=a.indexOf(t):a.startsWith(t)))break}return o}function N(e){return parseInt(e,10)||0}P.fn.triggerNative=function(e){var t,i=this[0];i.dispatchEvent?(u?t=new Event(e,{bubbles:!0}):(t=document.createEvent("Event")).initEvent(e,!0,!1),i.dispatchEvent(t)):i.fireEvent?((t=document.createEventObject()).eventType=e,i.fireEvent("on"+e,t)):this.trigger(e)};var f={"\xc0":"A","\xc1":"A","\xc2":"A","\xc3":"A","\xc4":"A","\xc5":"A","\xe0":"a","\xe1":"a","\xe2":"a","\xe3":"a","\xe4":"a","\xe5":"a","\xc7":"C","\xe7":"c","\xd0":"D","\xf0":"d","\xc8":"E","\xc9":"E","\xca":"E","\xcb":"E","\xe8":"e","\xe9":"e","\xea":"e","\xeb":"e","\xcc":"I","\xcd":"I","\xce":"I","\xcf":"I","\xec":"i","\xed":"i","\xee":"i","\xef":"i","\xd1":"N","\xf1":"n","\xd2":"O","\xd3":"O","\xd4":"O","\xd5":"O","\xd6":"O","\xd8":"O","\xf2":"o","\xf3":"o","\xf4":"o","\xf5":"o","\xf6":"o","\xf8":"o","\xd9":"U","\xda":"U","\xdb":"U","\xdc":"U","\xf9":"u","\xfa":"u","\xfb":"u","\xfc":"u","\xdd":"Y","\xfd":"y","\xff":"y","\xc6":"Ae","\xe6":"ae","\xde":"Th","\xfe":"th","\xdf":"ss","\u0100":"A","\u0102":"A","\u0104":"A","\u0101":"a","\u0103":"a","\u0105":"a","\u0106":"C","\u0108":"C","\u010a":"C","\u010c":"C","\u0107":"c","\u0109":"c","\u010b":"c","\u010d":"c","\u010e":"D","\u0110":"D","\u010f":"d","\u0111":"d","\u0112":"E","\u0114":"E","\u0116":"E","\u0118":"E","\u011a":"E","\u0113":"e","\u0115":"e","\u0117":"e","\u0119":"e","\u011b":"e","\u011c":"G","\u011e":"G","\u0120":"G","\u0122":"G","\u011d":"g","\u011f":"g","\u0121":"g","\u0123":"g","\u0124":"H","\u0126":"H","\u0125":"h","\u0127":"h","\u0128":"I","\u012a":"I","\u012c":"I","\u012e":"I","\u0130":"I","\u0129":"i","\u012b":"i","\u012d":"i","\u012f":"i","\u0131":"i","\u0134":"J","\u0135":"j","\u0136":"K","\u0137":"k","\u0138":"k","\u0139":"L","\u013b":"L","\u013d":"L","\u013f":"L","\u0141":"L","\u013a":"l","\u013c":"l","\u013e":"l","\u0140":"l","\u0142":"l","\u0143":"N","\u0145":"N","\u0147":"N","\u014a":"N","\u0144":"n","\u0146":"n","\u0148":"n","\u014b":"n","\u014c":"O","\u014e":"O","\u0150":"O","\u014d":"o","\u014f":"o","\u0151":"o","\u0154":"R","\u0156":"R","\u0158":"R","\u0155":"r","\u0157":"r","\u0159":"r","\u015a":"S","\u015c":"S","\u015e":"S","\u0160":"S","\u015b":"s","\u015d":"s","\u015f":"s","\u0161":"s","\u0162":"T","\u0164":"T","\u0166":"T","\u0163":"t","\u0165":"t","\u0167":"t","\u0168":"U","\u016a":"U","\u016c":"U","\u016e":"U","\u0170":"U","\u0172":"U","\u0169":"u","\u016b":"u","\u016d":"u","\u016f":"u","\u0171":"u","\u0173":"u","\u0174":"W","\u0175":"w","\u0176":"Y","\u0177":"y","\u0178":"Y","\u0179":"Z","\u017b":"Z","\u017d":"Z","\u017a":"z","\u017c":"z","\u017e":"z","\u0132":"IJ","\u0133":"ij","\u0152":"Oe","\u0153":"oe","\u0149":"'n","\u017f":"s"},m=/[\xc0-\xd6\xd8-\xf6\xf8-\xff\u0100-\u017f]/g,g=RegExp("[\\u0300-\\u036f\\ufe20-\\ufe2f\\u20d0-\\u20ff\\u1ab0-\\u1aff\\u1dc0-\\u1dff]","g");function b(e){return f[e]}function w(e){return(e=e.toString())&&e.replace(m,b).replace(g,"")}var I,x,y,$,S=(I={"&":"&","<":"<",">":">",'"':""","'":"'","`":"`"},x="(?:"+Object.keys(I).join("|")+")",y=RegExp(x),$=RegExp(x,"g"),function(e){return e=null==e?"":""+e,y.test(e)?e.replace($,E):e});function E(e){return I[e]}var C={32:" ",48:"0",49:"1",50:"2",51:"3",52:"4",53:"5",54:"6",55:"7",56:"8",57:"9",59:";",65:"A",66:"B",67:"C",68:"D",69:"E",70:"F",71:"G",72:"H",73:"I",74:"J",75:"K",76:"L",77:"M",78:"N",79:"O",80:"P",81:"Q",82:"R",83:"S",84:"T",85:"U",86:"V",87:"W",88:"X",89:"Y",90:"Z",96:"0",97:"1",98:"2",99:"3",100:"4",101:"5",102:"6",103:"7",104:"8",105:"9"},A=27,L=13,D=32,H=9,B=38,R=40,M={success:!1,major:"3"};try{M.full=(P.fn.dropdown.Constructor.VERSION||"").split(" ")[0].split("."),M.major=M.full[0],M.success=!0}catch(e){}var U=0,j=".bs.select",V={DISABLED:"disabled",DIVIDER:"divider",SHOW:"open",DROPUP:"dropup",MENU:"dropdown-menu",MENURIGHT:"dropdown-menu-right",MENULEFT:"dropdown-menu-left",BUTTONCLASS:"btn-default",POPOVERHEADER:"popover-title",ICONBASE:"glyphicon",TICKICON:"glyphicon-ok"},F={MENU:"."+V.MENU},_={div:document.createElement("div"),span:document.createElement("span"),i:document.createElement("i"),subtext:document.createElement("small"),a:document.createElement("a"),li:document.createElement("li"),whitespace:document.createTextNode("\xa0"),fragment:document.createDocumentFragment()};_.noResults=_.li.cloneNode(!1),_.noResults.className="no-results",_.a.setAttribute("role","option"),_.a.className="dropdown-item",_.subtext.className="text-muted",_.text=_.span.cloneNode(!1),_.text.className="text",_.checkMark=_.span.cloneNode(!1);var G=new RegExp(B+"|"+R),q=new RegExp("^"+H+"$|"+A),K={li:function(e,t,i){var s=_.li.cloneNode(!1);return e&&(1===e.nodeType||11===e.nodeType?s.appendChild(e):s.innerHTML=e),void 0!==t&&""!==t&&(s.className=t),null!=i&&s.classList.add("optgroup-"+i),s},a:function(e,t,i){var s=_.a.cloneNode(!0);return e&&(11===e.nodeType?s.appendChild(e):s.insertAdjacentHTML("beforeend",e)),void 0!==t&&""!==t&&s.classList.add.apply(s.classList,t.split(/\s+/)),i&&s.setAttribute("style",i),s},text:function(e,t){var i,s,n=_.text.cloneNode(!1);if(e.content)n.innerHTML=e.content;else{if(n.textContent=e.text,e.icon){var o=_.whitespace.cloneNode(!1);(s=(!0===t?_.i:_.span).cloneNode(!1)).className=this.options.iconBase+" "+e.icon,_.fragment.appendChild(s),_.fragment.appendChild(o)}e.subtext&&((i=_.subtext.cloneNode(!1)).textContent=e.subtext,n.appendChild(i))}if(!0===t)for(;0<n.childNodes.length;)_.fragment.appendChild(n.childNodes[0]);else _.fragment.appendChild(n);return _.fragment},label:function(e){var t,i,s=_.text.cloneNode(!1);if(s.innerHTML=e.display,e.icon){var n=_.whitespace.cloneNode(!1);(i=_.span.cloneNode(!1)).className=this.options.iconBase+" "+e.icon,_.fragment.appendChild(i),_.fragment.appendChild(n)}return e.subtext&&((t=_.subtext.cloneNode(!1)).textContent=e.subtext,s.appendChild(t)),_.fragment.appendChild(s),_.fragment}};var Y=function(e,t){var i=this;p.useDefault||(P.valHooks.select.set=p._set,p.useDefault=!0),this.$element=P(e),this.$newElement=null,this.$button=null,this.$menu=null,this.options=t,this.selectpicker={main:{},search:{},current:{},view:{},isSearching:!1,keydown:{keyHistory:"",resetKeyHistory:{start:function(){return setTimeout(function(){i.selectpicker.keydown.keyHistory=""},800)}}}},this.sizeInfo={},null===this.options.title&&(this.options.title=this.$element.attr("title"));var s=this.options.windowPadding;"number"==typeof s&&(this.options.windowPadding=[s,s,s,s]),this.val=Y.prototype.val,this.render=Y.prototype.render,this.refresh=Y.prototype.refresh,this.setStyle=Y.prototype.setStyle,this.selectAll=Y.prototype.selectAll,this.deselectAll=Y.prototype.deselectAll,this.destroy=Y.prototype.destroy,this.remove=Y.prototype.remove,this.show=Y.prototype.show,this.hide=Y.prototype.hide,this.init()};function Z(e){var l,a=arguments,c=e;if([].shift.apply(a),!M.success){try{M.full=(P.fn.dropdown.Constructor.VERSION||"").split(" ")[0].split(".")}catch(e){Y.BootstrapVersion?M.full=Y.BootstrapVersion.split(" ")[0].split("."):(M.full=[M.major,"0","0"],console.warn("There was an issue retrieving Bootstrap's version. Ensure Bootstrap is being loaded before bootstrap-select and there is no namespace collision. If loading Bootstrap asynchronously, the version may need to be manually specified via $.fn.selectpicker.Constructor.BootstrapVersion.",e))}M.major=M.full[0],M.success=!0}if("4"===M.major){var t=[];Y.DEFAULTS.style===V.BUTTONCLASS&&t.push({name:"style",className:"BUTTONCLASS"}),Y.DEFAULTS.iconBase===V.ICONBASE&&t.push({name:"iconBase",className:"ICONBASE"}),Y.DEFAULTS.tickIcon===V.TICKICON&&t.push({name:"tickIcon",className:"TICKICON"}),V.DIVIDER="dropdown-divider",V.SHOW="show",V.BUTTONCLASS="btn-light",V.POPOVERHEADER="popover-header",V.ICONBASE="",V.TICKICON="bs-ok-default";for(var i=0;i<t.length;i++){e=t[i];Y.DEFAULTS[e.name]=V[e.className]}}var s=this.each(function(){var e=P(this);if(e.is("select")){var t=e.data("selectpicker"),i="object"==typeof c&&c;if(t){if(i)for(var s in i)Object.prototype.hasOwnProperty.call(i,s)&&(t.options[s]=i[s])}else{var n=e.data();for(var o in n)Object.prototype.hasOwnProperty.call(n,o)&&-1!==P.inArray(o,d)&&delete n[o];var r=P.extend({},Y.DEFAULTS,P.fn.selectpicker.defaults||{},n,i);r.template=P.extend({},Y.DEFAULTS.template,P.fn.selectpicker.defaults?P.fn.selectpicker.defaults.template:{},n.template,i.template),e.data("selectpicker",t=new Y(this,r))}"string"==typeof c&&(l=t[c]instanceof Function?t[c].apply(t,a):t.options[c])}});return void 0!==l?l:s}Y.VERSION="1.13.18",Y.DEFAULTS={noneSelectedText:"Nothing selected",noneResultsText:"No results matched {0}",countSelectedText:function(e,t){return 1==e?"{0} item selected":"{0} items selected"},maxOptionsText:function(e,t){return[1==e?"Limit reached ({n} item max)":"Limit reached ({n} items max)",1==t?"Group limit reached ({n} item max)":"Group limit reached ({n} items max)"]},selectAllText:"Select All",deselectAllText:"Deselect All",doneButton:!1,doneButtonText:"Close",multipleSeparator:", ",styleBase:"btn",style:V.BUTTONCLASS,size:"auto",title:null,selectedTextFormat:"values",width:!1,container:!1,hideDisabled:!1,showSubtext:!1,showIcon:!0,showContent:!0,dropupAuto:!0,header:!1,liveSearch:!1,liveSearchPlaceholder:null,liveSearchNormalize:!1,liveSearchStyle:"contains",actionsBox:!1,iconBase:V.ICONBASE,tickIcon:V.TICKICON,showTick:!1,template:{caret:'<span class="caret"></span>'},maxOptions:!1,mobile:!1,selectOnTab:!1,dropdownAlignRight:!1,windowPadding:0,virtualScroll:600,display:!1,sanitize:!0,sanitizeFn:null,whiteList:e},Y.prototype={constructor:Y,init:function(){var i=this,e=this.$element.attr("id"),t=this.$element[0],s=t.form;U++,this.selectId="bs-select-"+U,t.classList.add("bs-select-hidden"),this.multiple=this.$element.prop("multiple"),this.autofocus=this.$element.prop("autofocus"),t.classList.contains("show-tick")&&(this.options.showTick=!0),this.$newElement=this.createDropdown(),this.buildData(),this.$element.after(this.$newElement).prependTo(this.$newElement),s&&null===t.form&&(s.id||(s.id="form-"+this.selectId),t.setAttribute("form",s.id)),this.$button=this.$newElement.children("button"),this.$menu=this.$newElement.children(F.MENU),this.$menuInner=this.$menu.children(".inner"),this.$searchbox=this.$menu.find("input"),t.classList.remove("bs-select-hidden"),!0===this.options.dropdownAlignRight&&this.$menu[0].classList.add(V.MENURIGHT),void 0!==e&&this.$button.attr("data-id",e),this.checkDisabled(),this.clickListener(),this.options.liveSearch?(this.liveSearchListener(),this.focusedParent=this.$searchbox[0]):this.focusedParent=this.$menuInner[0],this.setStyle(),this.render(),this.setWidth(),this.options.container?this.selectPosition():this.$element.on("hide"+j,function(){if(i.isVirtual()){var e=i.$menuInner[0],t=e.firstChild.cloneNode(!1);e.replaceChild(t,e.firstChild),e.scrollTop=0}}),this.$menu.data("this",this),this.$newElement.data("this",this),this.options.mobile&&this.mobile(),this.$newElement.on({"hide.bs.dropdown":function(e){i.$element.trigger("hide"+j,e)},"hidden.bs.dropdown":function(e){i.$element.trigger("hidden"+j,e)},"show.bs.dropdown":function(e){i.$element.trigger("show"+j,e)},"shown.bs.dropdown":function(e){i.$element.trigger("shown"+j,e)}}),t.hasAttribute("required")&&this.$element.on("invalid"+j,function(){i.$button[0].classList.add("bs-invalid"),i.$element.on("shown"+j+".invalid",function(){i.$element.val(i.$element.val()).off("shown"+j+".invalid")}).on("rendered"+j,function(){this.validity.valid&&i.$button[0].classList.remove("bs-invalid"),i.$element.off("rendered"+j)}),i.$button.on("blur"+j,function(){i.$element.trigger("focus").trigger("blur"),i.$button.off("blur"+j)})}),setTimeout(function(){i.buildList(),i.$element.trigger("loaded"+j)})},createDropdown:function(){var e=this.multiple||this.options.showTick?" show-tick":"",t=this.multiple?' aria-multiselectable="true"':"",i="",s=this.autofocus?" autofocus":"";M.major<4&&this.$element.parent().hasClass("input-group")&&(i=" input-group-btn");var n,o="",r="",l="",a="";return this.options.header&&(o='<div class="'+V.POPOVERHEADER+'"><button type="button" class="close" aria-hidden="true">×</button>'+this.options.header+"</div>"),this.options.liveSearch&&(r='<div class="bs-searchbox"><input type="search" class="form-control" autocomplete="off"'+(null===this.options.liveSearchPlaceholder?"":' placeholder="'+S(this.options.liveSearchPlaceholder)+'"')+' role="combobox" aria-label="Search" aria-controls="'+this.selectId+'" aria-autocomplete="list"></div>'),this.multiple&&this.options.actionsBox&&(l='<div class="bs-actionsbox"><div class="btn-group btn-group-sm btn-block"><button type="button" class="actions-btn bs-select-all btn '+V.BUTTONCLASS+'">'+this.options.selectAllText+'</button><button type="button" class="actions-btn bs-deselect-all btn '+V.BUTTONCLASS+'">'+this.options.deselectAllText+"</button></div></div>"),this.multiple&&this.options.doneButton&&(a='<div class="bs-donebutton"><div class="btn-group btn-block"><button type="button" class="btn btn-sm '+V.BUTTONCLASS+'">'+this.options.doneButtonText+"</button></div></div>"),n='<div class="dropdown bootstrap-select'+e+i+'"><button type="button" tabindex="-1" class="'+this.options.styleBase+' dropdown-toggle" '+("static"===this.options.display?'data-display="static"':"")+'data-toggle="dropdown"'+s+' role="combobox" aria-owns="'+this.selectId+'" aria-haspopup="listbox" aria-expanded="false"><div class="filter-option"><div class="filter-option-inner"><div class="filter-option-inner-inner"></div></div> </div>'+("4"===M.major?"":'<span class="bs-caret">'+this.options.template.caret+"</span>")+'</button><div class="'+V.MENU+" "+("4"===M.major?"":V.SHOW)+'">'+o+r+l+'<div class="inner '+V.SHOW+'" role="listbox" id="'+this.selectId+'" tabindex="-1" '+t+'><ul class="'+V.MENU+" inner "+("4"===M.major?V.SHOW:"")+'" role="presentation"></ul></div>'+a+"</div></div>",P(n)},setPositionData:function(){this.selectpicker.view.canHighlight=[],this.selectpicker.view.size=0,this.selectpicker.view.firstHighlightIndex=!1;for(var e=0;e<this.selectpicker.current.data.length;e++){var t=this.selectpicker.current.data[e],i=!0;"divider"===t.type?(i=!1,t.height=this.sizeInfo.dividerHeight):"optgroup-label"===t.type?(i=!1,t.height=this.sizeInfo.dropdownHeaderHeight):t.height=this.sizeInfo.liHeight,t.disabled&&(i=!1),this.selectpicker.view.canHighlight.push(i),i&&(this.selectpicker.view.size++,t.posinset=this.selectpicker.view.size,!1===this.selectpicker.view.firstHighlightIndex&&(this.selectpicker.view.firstHighlightIndex=e)),t.position=(0===e?0:this.selectpicker.current.data[e-1].position)+t.height}},isVirtual:function(){return!1!==this.options.virtualScroll&&this.selectpicker.main.elements.length>=this.options.virtualScroll||!0===this.options.virtualScroll},createView:function(N,e,t){var A,L,D=this,i=0,H=[];if(this.selectpicker.isSearching=N,this.selectpicker.current=N?this.selectpicker.search:this.selectpicker.main,this.setPositionData(),e)if(t)i=this.$menuInner[0].scrollTop;else if(!D.multiple){var s=D.$element[0],n=(s.options[s.selectedIndex]||{}).liIndex;if("number"==typeof n&&!1!==D.options.size){var o=D.selectpicker.main.data[n],r=o&&o.position;r&&(i=r-(D.sizeInfo.menuInnerHeight+D.sizeInfo.liHeight)/2)}}function l(e,t){var i,s,n,o,r,l,a,c,d=D.selectpicker.current.elements.length,h=[],p=!0,u=D.isVirtual();D.selectpicker.view.scrollTop=e,i=Math.ceil(D.sizeInfo.menuInnerHeight/D.sizeInfo.liHeight*1.5),s=Math.round(d/i)||1;for(var f=0;f<s;f++){var m=(f+1)*i;if(f===s-1&&(m=d),h[f]=[f*i+(f?1:0),m],!d)break;void 0===r&&e-1<=D.selectpicker.current.data[m-1].position-D.sizeInfo.menuInnerHeight&&(r=f)}if(void 0===r&&(r=0),l=[D.selectpicker.view.position0,D.selectpicker.view.position1],n=Math.max(0,r-1),o=Math.min(s-1,r+1),D.selectpicker.view.position0=!1===u?0:Math.max(0,h[n][0])||0,D.selectpicker.view.position1=!1===u?d:Math.min(d,h[o][1])||0,a=l[0]!==D.selectpicker.view.position0||l[1]!==D.selectpicker.view.position1,void 0!==D.activeIndex&&(L=D.selectpicker.main.elements[D.prevActiveIndex],H=D.selectpicker.main.elements[D.activeIndex],A=D.selectpicker.main.elements[D.selectedIndex],t&&(D.activeIndex!==D.selectedIndex&&D.defocusItem(H),D.activeIndex=void 0),D.activeIndex&&D.activeIndex!==D.selectedIndex&&D.defocusItem(A)),void 0!==D.prevActiveIndex&&D.prevActiveIndex!==D.activeIndex&&D.prevActiveIndex!==D.selectedIndex&&D.defocusItem(L),(t||a)&&(c=D.selectpicker.view.visibleElements?D.selectpicker.view.visibleElements.slice():[],D.selectpicker.view.visibleElements=!1===u?D.selectpicker.current.elements:D.selectpicker.current.elements.slice(D.selectpicker.view.position0,D.selectpicker.view.position1),D.setOptionStatus(),(N||!1===u&&t)&&(p=!function(e,i){return e.length===i.length&&e.every(function(e,t){return e===i[t]})}(c,D.selectpicker.view.visibleElements)),(t||!0===u)&&p)){var v,g,b=D.$menuInner[0],w=document.createDocumentFragment(),I=b.firstChild.cloneNode(!1),x=D.selectpicker.view.visibleElements,k=[];b.replaceChild(I,b.firstChild);f=0;for(var y=x.length;f<y;f++){var $,S,E=x[f];D.options.sanitize&&($=E.lastChild)&&(S=D.selectpicker.current.data[f+D.selectpicker.view.position0])&&S.content&&!S.sanitized&&(k.push($),S.sanitized=!0),w.appendChild(E)}if(D.options.sanitize&&k.length&&W(k,D.options.whiteList,D.options.sanitizeFn),!0===u?(v=0===D.selectpicker.view.position0?0:D.selectpicker.current.data[D.selectpicker.view.position0-1].position,g=D.selectpicker.view.position1>d-1?0:D.selectpicker.current.data[d-1].position-D.selectpicker.current.data[D.selectpicker.view.position1-1].position,b.firstChild.style.marginTop=v+"px",b.firstChild.style.marginBottom=g+"px"):(b.firstChild.style.marginTop=0,b.firstChild.style.marginBottom=0),b.firstChild.appendChild(w),!0===u&&D.sizeInfo.hasScrollBar){var C=b.firstChild.offsetWidth;if(t&&C<D.sizeInfo.menuInnerInnerWidth&&D.sizeInfo.totalMenuWidth>D.sizeInfo.selectWidth)b.firstChild.style.minWidth=D.sizeInfo.menuInnerInnerWidth+"px";else if(C>D.sizeInfo.menuInnerInnerWidth){D.$menu[0].style.minWidth=0;var O=b.firstChild.offsetWidth;O>D.sizeInfo.menuInnerInnerWidth&&(D.sizeInfo.menuInnerInnerWidth=O,b.firstChild.style.minWidth=D.sizeInfo.menuInnerInnerWidth+"px"),D.$menu[0].style.minWidth=""}}}if(D.prevActiveIndex=D.activeIndex,D.options.liveSearch){if(N&&t){var z,T=0;D.selectpicker.view.canHighlight[T]||(T=1+D.selectpicker.view.canHighlight.slice(1).indexOf(!0)),z=D.selectpicker.view.visibleElements[T],D.defocusItem(D.selectpicker.view.currentActive),D.activeIndex=(D.selectpicker.current.data[T]||{}).index,D.focusItem(z)}}else D.$menuInner.trigger("focus")}l(i,!0),this.$menuInner.off("scroll.createView").on("scroll.createView",function(e,t){D.noScroll||l(this.scrollTop,t),D.noScroll=!1}),P(window).off("resize"+j+"."+this.selectId+".createView").on("resize"+j+"."+this.selectId+".createView",function(){D.$newElement.hasClass(V.SHOW)&&l(D.$menuInner[0].scrollTop)})},focusItem:function(e,t,i){if(e){t=t||this.selectpicker.main.data[this.activeIndex];var s=e.firstChild;s&&(s.setAttribute("aria-setsize",this.selectpicker.view.size),s.setAttribute("aria-posinset",t.posinset),!0!==i&&(this.focusedParent.setAttribute("aria-activedescendant",s.id),e.classList.add("active"),s.classList.add("active")))}},defocusItem:function(e){e&&(e.classList.remove("active"),e.firstChild&&e.firstChild.classList.remove("active"))},setPlaceholder:function(){var e=this,t=!1;if(this.options.title&&!this.multiple){this.selectpicker.view.titleOption||(this.selectpicker.view.titleOption=document.createElement("option")),t=!0;var i=this.$element[0],s=!1,n=!this.selectpicker.view.titleOption.parentNode,o=i.selectedIndex,r=i.options[o],l=window.performance&&window.performance.getEntriesByType("navigation"),a=l&&l.length?"back_forward"!==l[0].type:2!==window.performance.navigation.type;n&&(this.selectpicker.view.titleOption.className="bs-title-option",this.selectpicker.view.titleOption.value="",s=!r||0===o&&!1===r.defaultSelected&&void 0===this.$element.data("selected")),!n&&0===this.selectpicker.view.titleOption.index||i.insertBefore(this.selectpicker.view.titleOption,i.firstChild),s&&a?i.selectedIndex=0:"complete"!==document.readyState&&window.addEventListener("pageshow",function(){e.selectpicker.view.displayedValue!==i.value&&e.render()})}return t},buildData:function(){var p=':not([hidden]):not([data-hidden="true"])',u=[],f=0,m=this.setPlaceholder()?1:0;this.options.hideDisabled&&(p+=":not(:disabled)");var e=this.$element[0].querySelectorAll("select > *"+p);function v(e){var t=u[u.length-1];t&&"divider"===t.type&&(t.optID||e.optID)||((e=e||{}).type="divider",u.push(e))}function g(e,t){if((t=t||{}).divider="true"===e.getAttribute("data-divider"),t.divider)v({optID:t.optID});else{var i=u.length,s=e.style.cssText,n=s?S(s):"",o=(e.className||"")+(t.optgroupClass||"");t.optID&&(o="opt "+o),t.optionClass=o.trim(),t.inlineStyle=n,t.text=e.textContent,t.content=e.getAttribute("data-content"),t.tokens=e.getAttribute("data-tokens"),t.subtext=e.getAttribute("data-subtext"),t.icon=e.getAttribute("data-icon"),e.liIndex=i,t.display=t.content||t.text,t.type="option",t.index=i,t.option=e,t.selected=!!e.selected,t.disabled=t.disabled||!!e.disabled,u.push(t)}}function t(e,t){var i=t[e],s=!(e-1<m)&&t[e-1],n=t[e+1],o=i.querySelectorAll("option"+p);if(o.length){var r,l,a={display:S(i.label),subtext:i.getAttribute("data-subtext"),icon:i.getAttribute("data-icon"),type:"optgroup-label",optgroupClass:" "+(i.className||"")};f++,s&&v({optID:f}),a.optID=f,u.push(a);for(var c=0,d=o.length;c<d;c++){var h=o[c];0===c&&(l=(r=u.length-1)+d),g(h,{headerIndex:r,lastIndex:l,optID:a.optID,optgroupClass:a.optgroupClass,disabled:i.disabled})}n&&v({optID:f})}}for(var i=e.length,s=m;s<i;s++){var n=e[s];"OPTGROUP"!==n.tagName?g(n,{}):t(s,e)}this.selectpicker.main.data=this.selectpicker.current.data=u},buildList:function(){var s=this,e=this.selectpicker.main.data,n=[],o=0;function t(e){var t,i=0;switch(e.type){case"divider":t=K.li(!1,V.DIVIDER,e.optID?e.optID+"div":void 0);break;case"option":(t=K.li(K.a(K.text.call(s,e),e.optionClass,e.inlineStyle),"",e.optID)).firstChild&&(t.firstChild.id=s.selectId+"-"+e.index);break;case"optgroup-label":t=K.li(K.label.call(s,e),"dropdown-header"+e.optgroupClass,e.optID)}e.element=t,n.push(t),e.display&&(i+=e.display.length),e.subtext&&(i+=e.subtext.length),e.icon&&(i+=1),o<i&&(o=i,s.selectpicker.view.widestOption=n[n.length-1])}!s.options.showTick&&!s.multiple||_.checkMark.parentNode||(_.checkMark.className=this.options.iconBase+" "+s.options.tickIcon+" check-mark",_.a.appendChild(_.checkMark));for(var i=e.length,r=0;r<i;r++){t(e[r])}this.selectpicker.main.elements=this.selectpicker.current.elements=n},findLis:function(){return this.$menuInner.find(".inner > li")},render:function(){var e,t=this,i=this.$element[0],s=this.setPlaceholder()&&0===i.selectedIndex,n=O(i,this.options.hideDisabled),o=n.length,r=this.$button[0],l=r.querySelector(".filter-option-inner-inner"),a=document.createTextNode(this.options.multipleSeparator),c=_.fragment.cloneNode(!1),d=!1;if(r.classList.toggle("bs-placeholder",t.multiple?!o:!z(i,n)),t.multiple||1!==n.length||(t.selectpicker.view.displayedValue=z(i,n)),"static"===this.options.selectedTextFormat)c=K.text.call(this,{text:this.options.title},!0);else if(!1===(this.multiple&&-1!==this.options.selectedTextFormat.indexOf("count")&&1<o&&(1<(e=this.options.selectedTextFormat.split(">")).length&&o>e[1]||1===e.length&&2<=o))){if(!s){for(var h=0;h<o&&h<50;h++){var p=n[h],u=this.selectpicker.main.data[p.liIndex],f={};this.multiple&&0<h&&c.appendChild(a.cloneNode(!1)),p.title?f.text=p.title:u&&(u.content&&t.options.showContent?(f.content=u.content.toString(),d=!0):(t.options.showIcon&&(f.icon=u.icon),t.options.showSubtext&&!t.multiple&&u.subtext&&(f.subtext=" "+u.subtext),f.text=p.textContent.trim())),c.appendChild(K.text.call(this,f,!0))}49<o&&c.appendChild(document.createTextNode("..."))}}else{var m=':not([hidden]):not([data-hidden="true"]):not([data-divider="true"])';this.options.hideDisabled&&(m+=":not(:disabled)");var v=this.$element[0].querySelectorAll("select > option"+m+", optgroup"+m+" option"+m).length,g="function"==typeof this.options.countSelectedText?this.options.countSelectedText(o,v):this.options.countSelectedText;c=K.text.call(this,{text:g.replace("{0}",o.toString()).replace("{1}",v.toString())},!0)}if(null==this.options.title&&(this.options.title=this.$element.attr("title")),c.childNodes.length||(c=K.text.call(this,{text:void 0!==this.options.title?this.options.title:this.options.noneSelectedText},!0)),r.title=c.textContent.replace(/<[^>]*>?/g,"").trim(),this.options.sanitize&&d&&W([c],t.options.whiteList,t.options.sanitizeFn),l.innerHTML="",l.appendChild(c),M.major<4&&this.$newElement[0].classList.contains("bs3-has-addon")){var b=r.querySelector(".filter-expand"),w=l.cloneNode(!0);w.className="filter-expand",b?r.replaceChild(w,b):r.appendChild(w)}this.$element.trigger("rendered"+j)},setStyle:function(e,t){var i,s=this.$button[0],n=this.$newElement[0],o=this.options.style.trim();this.$element.attr("class")&&this.$newElement.addClass(this.$element.attr("class").replace(/selectpicker|mobile-device|bs-select-hidden|validate\[.*\]/gi,"")),M.major<4&&(n.classList.add("bs3"),n.parentNode.classList&&n.parentNode.classList.contains("input-group")&&(n.previousElementSibling||n.nextElementSibling)&&(n.previousElementSibling||n.nextElementSibling).classList.contains("input-group-addon")&&n.classList.add("bs3-has-addon")),i=e?e.trim():o,"add"==t?i&&s.classList.add.apply(s.classList,i.split(" ")):"remove"==t?i&&s.classList.remove.apply(s.classList,i.split(" ")):(o&&s.classList.remove.apply(s.classList,o.split(" ")),i&&s.classList.add.apply(s.classList,i.split(" ")))},liHeight:function(e){if(e||!1!==this.options.size&&!Object.keys(this.sizeInfo).length){var t,i=_.div.cloneNode(!1),s=_.div.cloneNode(!1),n=_.div.cloneNode(!1),o=document.createElement("ul"),r=_.li.cloneNode(!1),l=_.li.cloneNode(!1),a=_.a.cloneNode(!1),c=_.span.cloneNode(!1),d=this.options.header&&0<this.$menu.find("."+V.POPOVERHEADER).length?this.$menu.find("."+V.POPOVERHEADER)[0].cloneNode(!0):null,h=this.options.liveSearch?_.div.cloneNode(!1):null,p=this.options.actionsBox&&this.multiple&&0<this.$menu.find(".bs-actionsbox").length?this.$menu.find(".bs-actionsbox")[0].cloneNode(!0):null,u=this.options.doneButton&&this.multiple&&0<this.$menu.find(".bs-donebutton").length?this.$menu.find(".bs-donebutton")[0].cloneNode(!0):null,f=this.$element.find("option")[0];if(this.sizeInfo.selectWidth=this.$newElement[0].offsetWidth,c.className="text",a.className="dropdown-item "+(f?f.className:""),i.className=this.$menu[0].parentNode.className+" "+V.SHOW,i.style.width=0,"auto"===this.options.width&&(s.style.minWidth=0),s.className=V.MENU+" "+V.SHOW,n.className="inner "+V.SHOW,o.className=V.MENU+" inner "+("4"===M.major?V.SHOW:""),r.className=V.DIVIDER,l.className="dropdown-header",c.appendChild(document.createTextNode("\u200b")),this.selectpicker.current.data.length)for(var m=0;m<this.selectpicker.current.data.length;m++){var v=this.selectpicker.current.data[m];if("option"===v.type){t=v.element;break}}else t=_.li.cloneNode(!1),a.appendChild(c),t.appendChild(a);if(l.appendChild(c.cloneNode(!0)),this.selectpicker.view.widestOption&&o.appendChild(this.selectpicker.view.widestOption.cloneNode(!0)),o.appendChild(t),o.appendChild(r),o.appendChild(l),d&&s.appendChild(d),h){var g=document.createElement("input");h.className="bs-searchbox",g.className="form-control",h.appendChild(g),s.appendChild(h)}p&&s.appendChild(p),n.appendChild(o),s.appendChild(n),u&&s.appendChild(u),i.appendChild(s),document.body.appendChild(i);var b,w=t.offsetHeight,I=l?l.offsetHeight:0,x=d?d.offsetHeight:0,k=h?h.offsetHeight:0,y=p?p.offsetHeight:0,$=u?u.offsetHeight:0,S=P(r).outerHeight(!0),E=!!window.getComputedStyle&&window.getComputedStyle(s),C=s.offsetWidth,O=E?null:P(s),z={vert:N(E?E.paddingTop:O.css("paddingTop"))+N(E?E.paddingBottom:O.css("paddingBottom"))+N(E?E.borderTopWidth:O.css("borderTopWidth"))+N(E?E.borderBottomWidth:O.css("borderBottomWidth")),horiz:N(E?E.paddingLeft:O.css("paddingLeft"))+N(E?E.paddingRight:O.css("paddingRight"))+N(E?E.borderLeftWidth:O.css("borderLeftWidth"))+N(E?E.borderRightWidth:O.css("borderRightWidth"))},T={vert:z.vert+N(E?E.marginTop:O.css("marginTop"))+N(E?E.marginBottom:O.css("marginBottom"))+2,horiz:z.horiz+N(E?E.marginLeft:O.css("marginLeft"))+N(E?E.marginRight:O.css("marginRight"))+2};n.style.overflowY="scroll",b=s.offsetWidth-C,document.body.removeChild(i),this.sizeInfo.liHeight=w,this.sizeInfo.dropdownHeaderHeight=I,this.sizeInfo.headerHeight=x,this.sizeInfo.searchHeight=k,this.sizeInfo.actionsHeight=y,this.sizeInfo.doneButtonHeight=$,this.sizeInfo.dividerHeight=S,this.sizeInfo.menuPadding=z,this.sizeInfo.menuExtras=T,this.sizeInfo.menuWidth=C,this.sizeInfo.menuInnerInnerWidth=C-z.horiz,this.sizeInfo.totalMenuWidth=this.sizeInfo.menuWidth,this.sizeInfo.scrollBarWidth=b,this.sizeInfo.selectHeight=this.$newElement[0].offsetHeight,this.setPositionData()}},getSelectPosition:function(){var e,t=P(window),i=this.$newElement.offset(),s=P(this.options.container);this.options.container&&s.length&&!s.is("body")?((e=s.offset()).top+=parseInt(s.css("borderTopWidth")),e.left+=parseInt(s.css("borderLeftWidth"))):e={top:0,left:0};var n=this.options.windowPadding;this.sizeInfo.selectOffsetTop=i.top-e.top-t.scrollTop(),this.sizeInfo.selectOffsetBot=t.height()-this.sizeInfo.selectOffsetTop-this.sizeInfo.selectHeight-e.top-n[2],this.sizeInfo.selectOffsetLeft=i.left-e.left-t.scrollLeft(),this.sizeInfo.selectOffsetRight=t.width()-this.sizeInfo.selectOffsetLeft-this.sizeInfo.selectWidth-e.left-n[1],this.sizeInfo.selectOffsetTop-=n[0],this.sizeInfo.selectOffsetLeft-=n[3]},setMenuSize:function(e){this.getSelectPosition();var t,i,s,n,o,r,l,a,c=this.sizeInfo.selectWidth,d=this.sizeInfo.liHeight,h=this.sizeInfo.headerHeight,p=this.sizeInfo.searchHeight,u=this.sizeInfo.actionsHeight,f=this.sizeInfo.doneButtonHeight,m=this.sizeInfo.dividerHeight,v=this.sizeInfo.menuPadding,g=0;if(this.options.dropupAuto&&(l=d*this.selectpicker.current.elements.length+v.vert,a=this.sizeInfo.selectOffsetTop-this.sizeInfo.selectOffsetBot>this.sizeInfo.menuExtras.vert&&l+this.sizeInfo.menuExtras.vert+50>this.sizeInfo.selectOffsetBot,!0===this.selectpicker.isSearching&&(a=this.selectpicker.dropup),this.$newElement.toggleClass(V.DROPUP,a),this.selectpicker.dropup=a),"auto"===this.options.size)n=3<this.selectpicker.current.elements.length?3*this.sizeInfo.liHeight+this.sizeInfo.menuExtras.vert-2:0,i=this.sizeInfo.selectOffsetBot-this.sizeInfo.menuExtras.vert,s=n+h+p+u+f,r=Math.max(n-v.vert,0),this.$newElement.hasClass(V.DROPUP)&&(i=this.sizeInfo.selectOffsetTop-this.sizeInfo.menuExtras.vert),t=(o=i)-h-p-u-f-v.vert;else if(this.options.size&&"auto"!=this.options.size&&this.selectpicker.current.elements.length>this.options.size){for(var b=0;b<this.options.size;b++)"divider"===this.selectpicker.current.data[b].type&&g++;t=(i=d*this.options.size+g*m+v.vert)-v.vert,o=i+h+p+u+f,s=r=""}this.$menu.css({"max-height":o+"px",overflow:"hidden","min-height":s+"px"}),this.$menuInner.css({"max-height":t+"px","overflow-y":"auto","min-height":r+"px"}),this.sizeInfo.menuInnerHeight=Math.max(t,1),this.selectpicker.current.data.length&&this.selectpicker.current.data[this.selectpicker.current.data.length-1].position>this.sizeInfo.menuInnerHeight&&(this.sizeInfo.hasScrollBar=!0,this.sizeInfo.totalMenuWidth=this.sizeInfo.menuWidth+this.sizeInfo.scrollBarWidth),"auto"===this.options.dropdownAlignRight&&this.$menu.toggleClass(V.MENURIGHT,this.sizeInfo.selectOffsetLeft>this.sizeInfo.selectOffsetRight&&this.sizeInfo.selectOffsetRight<this.sizeInfo.totalMenuWidth-c),this.dropdown&&this.dropdown._popper&&this.dropdown._popper.update()},setSize:function(e){if(this.liHeight(e),this.options.header&&this.$menu.css("padding-top",0),!1!==this.options.size){var t=this,i=P(window);this.setMenuSize(),this.options.liveSearch&&this.$searchbox.off("input.setMenuSize propertychange.setMenuSize").on("input.setMenuSize propertychange.setMenuSize",function(){return t.setMenuSize()}),"auto"===this.options.size?i.off("resize"+j+"."+this.selectId+".setMenuSize scroll"+j+"."+this.selectId+".setMenuSize").on("resize"+j+"."+this.selectId+".setMenuSize scroll"+j+"."+this.selectId+".setMenuSize",function(){return t.setMenuSize()}):this.options.size&&"auto"!=this.options.size&&this.selectpicker.current.elements.length>this.options.size&&i.off("resize"+j+"."+this.selectId+".setMenuSize scroll"+j+"."+this.selectId+".setMenuSize")}this.createView(!1,!0,e)},setWidth:function(){var i=this;"auto"===this.options.width?requestAnimationFrame(function(){i.$menu.css("min-width","0"),i.$element.on("loaded"+j,function(){i.liHeight(),i.setMenuSize();var e=i.$newElement.clone().appendTo("body"),t=e.css("width","auto").children("button").outerWidth();e.remove(),i.sizeInfo.selectWidth=Math.max(i.sizeInfo.totalMenuWidth,t),i.$newElement.css("width",i.sizeInfo.selectWidth+"px")})}):"fit"===this.options.width?(this.$menu.css("min-width",""),this.$newElement.css("width","").addClass("fit-width")):this.options.width?(this.$menu.css("min-width",""),this.$newElement.css("width",this.options.width)):(this.$menu.css("min-width",""),this.$newElement.css("width","")),this.$newElement.hasClass("fit-width")&&"fit"!==this.options.width&&this.$newElement[0].classList.remove("fit-width")},selectPosition:function(){this.$bsContainer=P('<div class="bs-container" />');function e(e){var t={},i=r.options.display||!!P.fn.dropdown.Constructor.Default&&P.fn.dropdown.Constructor.Default.display;r.$bsContainer.addClass(e.attr("class").replace(/form-control|fit-width/gi,"")).toggleClass(V.DROPUP,e.hasClass(V.DROPUP)),s=e.offset(),l.is("body")?n={top:0,left:0}:((n=l.offset()).top+=parseInt(l.css("borderTopWidth"))-l.scrollTop(),n.left+=parseInt(l.css("borderLeftWidth"))-l.scrollLeft()),o=e.hasClass(V.DROPUP)?0:e[0].offsetHeight,(M.major<4||"static"===i)&&(t.top=s.top-n.top+o,t.left=s.left-n.left),t.width=e[0].offsetWidth,r.$bsContainer.css(t)}var s,n,o,r=this,l=P(this.options.container);this.$button.on("click.bs.dropdown.data-api",function(){r.isDisabled()||(e(r.$newElement),r.$bsContainer.appendTo(r.options.container).toggleClass(V.SHOW,!r.$button.hasClass(V.SHOW)).append(r.$menu))}),P(window).off("resize"+j+"."+this.selectId+" scroll"+j+"."+this.selectId).on("resize"+j+"."+this.selectId+" scroll"+j+"."+this.selectId,function(){r.$newElement.hasClass(V.SHOW)&&e(r.$newElement)}),this.$element.on("hide"+j,function(){r.$menu.data("height",r.$menu.height()),r.$bsContainer.detach()})},setOptionStatus:function(e){var t=this;if(t.noScroll=!1,t.selectpicker.view.visibleElements&&t.selectpicker.view.visibleElements.length)for(var i=0;i<t.selectpicker.view.visibleElements.length;i++){var s=t.selectpicker.current.data[i+t.selectpicker.view.position0],n=s.option;n&&(!0!==e&&t.setDisabled(s.index,s.disabled),t.setSelected(s.index,n.selected))}},setSelected:function(e,t){var i,s,n=this.selectpicker.main.elements[e],o=this.selectpicker.main.data[e],r=void 0!==this.activeIndex,l=this.activeIndex===e||t&&!this.multiple&&!r;o.selected=t,s=n.firstChild,t&&(this.selectedIndex=e),n.classList.toggle("selected",t),l?(this.focusItem(n,o),this.selectpicker.view.currentActive=n,this.activeIndex=e):this.defocusItem(n),s&&(s.classList.toggle("selected",t),t?s.setAttribute("aria-selected",!0):this.multiple?s.setAttribute("aria-selected",!1):s.removeAttribute("aria-selected")),l||r||!t||void 0===this.prevActiveIndex||(i=this.selectpicker.main.elements[this.prevActiveIndex],this.defocusItem(i))},setDisabled:function(e,t){var i,s=this.selectpicker.main.elements[e];this.selectpicker.main.data[e].disabled=t,i=s.firstChild,s.classList.toggle(V.DISABLED,t),i&&("4"===M.major&&i.classList.toggle(V.DISABLED,t),t?(i.setAttribute("aria-disabled",t),i.setAttribute("tabindex",-1)):(i.removeAttribute("aria-disabled"),i.setAttribute("tabindex",0)))},isDisabled:function(){return this.$element[0].disabled},checkDisabled:function(){this.isDisabled()?(this.$newElement[0].classList.add(V.DISABLED),this.$button.addClass(V.DISABLED).attr("aria-disabled",!0)):this.$button[0].classList.contains(V.DISABLED)&&(this.$newElement[0].classList.remove(V.DISABLED),this.$button.removeClass(V.DISABLED).attr("aria-disabled",!1))},clickListener:function(){var C=this,t=P(document);function e(){C.options.liveSearch?C.$searchbox.trigger("focus"):C.$menuInner.trigger("focus")}function i(){C.dropdown&&C.dropdown._popper&&C.dropdown._popper.state.isCreated?e():requestAnimationFrame(i)}t.data("spaceSelect",!1),this.$button.on("keyup",function(e){/(32)/.test(e.keyCode.toString(10))&&t.data("spaceSelect")&&(e.preventDefault(),t.data("spaceSelect",!1))}),this.$newElement.on("show.bs.dropdown",function(){3<M.major&&!C.dropdown&&(C.dropdown=C.$button.data("bs.dropdown"),C.dropdown._menu=C.$menu[0])}),this.$button.on("click.bs.dropdown.data-api",function(){C.$newElement.hasClass(V.SHOW)||C.setSize()}),this.$element.on("shown"+j,function(){C.$menuInner[0].scrollTop!==C.selectpicker.view.scrollTop&&(C.$menuInner[0].scrollTop=C.selectpicker.view.scrollTop),3<M.major?requestAnimationFrame(i):e()}),this.$menuInner.on("mouseenter","li a",function(e){var t=this.parentElement,i=C.isVirtual()?C.selectpicker.view.position0:0,s=Array.prototype.indexOf.call(t.parentElement.children,t),n=C.selectpicker.current.data[s+i];C.focusItem(t,n,!0)}),this.$menuInner.on("click","li a",function(e,t){var i=P(this),s=C.$element[0],n=C.isVirtual()?C.selectpicker.view.position0:0,o=C.selectpicker.current.data[i.parent().index()+n],r=o.index,l=z(s),a=s.selectedIndex,c=s.options[a],d=!0;if(C.multiple&&1!==C.options.maxOptions&&e.stopPropagation(),e.preventDefault(),!C.isDisabled()&&!i.parent().hasClass(V.DISABLED)){var h=o.option,p=P(h),u=h.selected,f=p.parent("optgroup"),m=f.find("option"),v=C.options.maxOptions,g=f.data("maxOptions")||!1;if(r===C.activeIndex&&(t=!0),t||(C.prevActiveIndex=C.activeIndex,C.activeIndex=void 0),C.multiple){if(h.selected=!u,C.setSelected(r,!u),C.focusedParent.focus(),!1!==v||!1!==g){var b=v<O(s).length,w=g<f.find("option:selected").length;if(v&&b||g&&w)if(v&&1==v)s.selectedIndex=-1,h.selected=!0,C.setOptionStatus(!0);else if(g&&1==g){for(var I=0;I<m.length;I++){var x=m[I];x.selected=!1,C.setSelected(x.liIndex,!1)}h.selected=!0,C.setSelected(r,!0)}else{var k="string"==typeof C.options.maxOptionsText?[C.options.maxOptionsText,C.options.maxOptionsText]:C.options.maxOptionsText,y="function"==typeof k?k(v,g):k,$=y[0].replace("{n}",v),S=y[1].replace("{n}",g),E=P('<div class="notify"></div>');y[2]&&($=$.replace("{var}",y[2][1<v?0:1]),S=S.replace("{var}",y[2][1<g?0:1])),h.selected=!1,C.$menu.append(E),v&&b&&(E.append(P("<div>"+$+"</div>")),d=!1,C.$element.trigger("maxReached"+j)),g&&w&&(E.append(P("<div>"+S+"</div>")),d=!1,C.$element.trigger("maxReachedGrp"+j)),setTimeout(function(){C.setSelected(r,!1)},10),E[0].classList.add("fadeOut"),setTimeout(function(){E.remove()},1050)}}}else c&&(c.selected=!1),h.selected=!0,C.setSelected(r,!0);!C.multiple||C.multiple&&1===C.options.maxOptions?C.$button.trigger("focus"):C.options.liveSearch&&C.$searchbox.trigger("focus"),d&&(!C.multiple&&a===s.selectedIndex||(T=[h.index,p.prop("selected"),l],C.$element.triggerNative("change")))}}),this.$menu.on("click","li."+V.DISABLED+" a, ."+V.POPOVERHEADER+", ."+V.POPOVERHEADER+" :not(.close)",function(e){e.currentTarget==this&&(e.preventDefault(),e.stopPropagation(),C.options.liveSearch&&!P(e.target).hasClass("close")?C.$searchbox.trigger("focus"):C.$button.trigger("focus"))}),this.$menuInner.on("click",".divider, .dropdown-header",function(e){e.preventDefault(),e.stopPropagation(),C.options.liveSearch?C.$searchbox.trigger("focus"):C.$button.trigger("focus")}),this.$menu.on("click","."+V.POPOVERHEADER+" .close",function(){C.$button.trigger("click")}),this.$searchbox.on("click",function(e){e.stopPropagation()}),this.$menu.on("click",".actions-btn",function(e){C.options.liveSearch?C.$searchbox.trigger("focus"):C.$button.trigger("focus"),e.preventDefault(),e.stopPropagation(),P(this).hasClass("bs-select-all")?C.selectAll():C.deselectAll()}),this.$button.on("focus"+j,function(e){var t=C.$element[0].getAttribute("tabindex");void 0!==t&&e.originalEvent&&e.originalEvent.isTrusted&&(this.setAttribute("tabindex",t),C.$element[0].setAttribute("tabindex",-1),C.selectpicker.view.tabindex=t)}).on("blur"+j,function(e){void 0!==C.selectpicker.view.tabindex&&e.originalEvent&&e.originalEvent.isTrusted&&(C.$element[0].setAttribute("tabindex",C.selectpicker.view.tabindex),this.setAttribute("tabindex",-1),C.selectpicker.view.tabindex=void 0)}),this.$element.on("change"+j,function(){C.render(),C.$element.trigger("changed"+j,T),T=null}).on("focus"+j,function(){C.options.mobile||C.$button[0].focus()})},liveSearchListener:function(){var u=this;this.$button.on("click.bs.dropdown.data-api",function(){u.$searchbox.val()&&(u.$searchbox.val(""),u.selectpicker.search.previousValue=void 0)}),this.$searchbox.on("click.bs.dropdown.data-api focus.bs.dropdown.data-api touchend.bs.dropdown.data-api",function(e){e.stopPropagation()}),this.$searchbox.on("input propertychange",function(){var e=u.$searchbox[0].value;if(u.selectpicker.search.elements=[],u.selectpicker.search.data=[],e){var t=[],i=e.toUpperCase(),s={},n=[],o=u._searchStyle(),r=u.options.liveSearchNormalize;r&&(i=w(i));for(var l=0;l<u.selectpicker.main.data.length;l++){var a=u.selectpicker.main.data[l];s[l]||(s[l]=k(a,i,o,r)),s[l]&&void 0!==a.headerIndex&&-1===n.indexOf(a.headerIndex)&&(0<a.headerIndex&&(s[a.headerIndex-1]=!0,n.push(a.headerIndex-1)),s[a.headerIndex]=!0,n.push(a.headerIndex),s[a.lastIndex+1]=!0),s[l]&&"optgroup-label"!==a.type&&n.push(l)}l=0;for(var c=n.length;l<c;l++){var d=n[l],h=n[l-1],p=(a=u.selectpicker.main.data[d],u.selectpicker.main.data[h]);("divider"!==a.type||"divider"===a.type&&p&&"divider"!==p.type&&c-1!==l)&&(u.selectpicker.search.data.push(a),t.push(u.selectpicker.main.elements[d]))}u.activeIndex=void 0,u.noScroll=!0,u.$menuInner.scrollTop(0),u.selectpicker.search.elements=t,u.createView(!0),function(e,t){e.length||(_.noResults.innerHTML=this.options.noneResultsText.replace("{0}",'"'+S(t)+'"'),this.$menuInner[0].firstChild.appendChild(_.noResults))}.call(u,t,e)}else u.selectpicker.search.previousValue&&(u.$menuInner.scrollTop(0),u.createView(!1));u.selectpicker.search.previousValue=e})},_searchStyle:function(){return this.options.liveSearchStyle||"contains"},val:function(e){var t=this.$element[0];if(void 0===e)return this.$element.val();var i=z(t);if(T=[null,null,i],this.$element.val(e).trigger("changed"+j,T),this.$newElement.hasClass(V.SHOW))if(this.multiple)this.setOptionStatus(!0);else{var s=(t.options[t.selectedIndex]||{}).liIndex;"number"==typeof s&&(this.setSelected(this.selectedIndex,!1),this.setSelected(s,!0))}return this.render(),T=null,this.$element},changeAll:function(e){if(this.multiple){void 0===e&&(e=!0);var t=this.$element[0],i=0,s=0,n=z(t);t.classList.add("bs-select-hidden");for(var o=0,r=this.selectpicker.current.data,l=r.length;o<l;o++){var a=r[o],c=a.option;c&&!a.disabled&&"divider"!==a.type&&(a.selected&&i++,!0===(c.selected=e)&&s++)}t.classList.remove("bs-select-hidden"),i!==s&&(this.setOptionStatus(),T=[null,null,n],this.$element.triggerNative("change"))}},selectAll:function(){return this.changeAll(!0)},deselectAll:function(){return this.changeAll(!1)},toggle:function(e){(e=e||window.event)&&e.stopPropagation(),this.$button.trigger("click.bs.dropdown.data-api")},keydown:function(e){var t,i,s,n,o,r=P(this),l=r.hasClass("dropdown-toggle"),a=(l?r.closest(".dropdown"):r.closest(F.MENU)).data("this"),c=a.findLis(),d=!1,h=e.which===H&&!l&&!a.options.selectOnTab,p=G.test(e.which)||h,u=a.$menuInner[0].scrollTop,f=!0===a.isVirtual()?a.selectpicker.view.position0:0;if(!(112<=e.which&&e.which<=123))if(!(i=a.$newElement.hasClass(V.SHOW))&&(p||48<=e.which&&e.which<=57||96<=e.which&&e.which<=105||65<=e.which&&e.which<=90)&&(a.$button.trigger("click.bs.dropdown.data-api"),a.options.liveSearch))a.$searchbox.trigger("focus");else{if(e.which===A&&i&&(e.preventDefault(),a.$button.trigger("click.bs.dropdown.data-api").trigger("focus")),p){if(!c.length)return;-1!==(t=(s=a.selectpicker.main.elements[a.activeIndex])?Array.prototype.indexOf.call(s.parentElement.children,s):-1)&&a.defocusItem(s),e.which===B?(-1!==t&&t--,t+f<0&&(t+=c.length),a.selectpicker.view.canHighlight[t+f]||-1===(t=a.selectpicker.view.canHighlight.slice(0,t+f).lastIndexOf(!0)-f)&&(t=c.length-1)):e.which!==R&&!h||(++t+f>=a.selectpicker.view.canHighlight.length&&(t=a.selectpicker.view.firstHighlightIndex),a.selectpicker.view.canHighlight[t+f]||(t=t+1+a.selectpicker.view.canHighlight.slice(t+f+1).indexOf(!0))),e.preventDefault();var m=f+t;e.which===B?0===f&&t===c.length-1?(a.$menuInner[0].scrollTop=a.$menuInner[0].scrollHeight,m=a.selectpicker.current.elements.length-1):d=(o=(n=a.selectpicker.current.data[m]).position-n.height)<u:e.which!==R&&!h||(t===a.selectpicker.view.firstHighlightIndex?(a.$menuInner[0].scrollTop=0,m=a.selectpicker.view.firstHighlightIndex):d=u<(o=(n=a.selectpicker.current.data[m]).position-a.sizeInfo.menuInnerHeight)),s=a.selectpicker.current.elements[m],a.activeIndex=a.selectpicker.current.data[m].index,a.focusItem(s),a.selectpicker.view.currentActive=s,d&&(a.$menuInner[0].scrollTop=o),a.options.liveSearch?a.$searchbox.trigger("focus"):r.trigger("focus")}else if(!r.is("input")&&!q.test(e.which)||e.which===D&&a.selectpicker.keydown.keyHistory){var v,g,b=[];e.preventDefault(),a.selectpicker.keydown.keyHistory+=C[e.which],a.selectpicker.keydown.resetKeyHistory.cancel&&clearTimeout(a.selectpicker.keydown.resetKeyHistory.cancel),a.selectpicker.keydown.resetKeyHistory.cancel=a.selectpicker.keydown.resetKeyHistory.start(),g=a.selectpicker.keydown.keyHistory,/^(.)\1+$/.test(g)&&(g=g.charAt(0));for(var w=0;w<a.selectpicker.current.data.length;w++){var I=a.selectpicker.current.data[w];k(I,g,"startsWith",!0)&&a.selectpicker.view.canHighlight[w]&&b.push(I.index)}if(b.length){var x=0;c.removeClass("active").find("a").removeClass("active"),1===g.length&&(-1===(x=b.indexOf(a.activeIndex))||x===b.length-1?x=0:x++),v=b[x],d=0<u-(n=a.selectpicker.main.data[v]).position?(o=n.position-n.height,!0):(o=n.position-a.sizeInfo.menuInnerHeight,n.position>u+a.sizeInfo.menuInnerHeight),s=a.selectpicker.main.elements[v],a.activeIndex=b[x],a.focusItem(s),s&&s.firstChild.focus(),d&&(a.$menuInner[0].scrollTop=o),r.trigger("focus")}}i&&(e.which===D&&!a.selectpicker.keydown.keyHistory||e.which===L||e.which===H&&a.options.selectOnTab)&&(e.which!==D&&e.preventDefault(),a.options.liveSearch&&e.which===D||(a.$menuInner.find(".active a").trigger("click",!0),r.trigger("focus"),a.options.liveSearch||(e.preventDefault(),P(document).data("spaceSelect",!0))))}},mobile:function(){this.options.mobile=!0,this.$element[0].classList.add("mobile-device")},refresh:function(){var e=P.extend({},this.options,this.$element.data());this.options=e,this.checkDisabled(),this.buildData(),this.setStyle(),this.render(),this.buildList(),this.setWidth(),this.setSize(!0),this.$element.trigger("refreshed"+j)},hide:function(){this.$newElement.hide()},show:function(){this.$newElement.show()},remove:function(){this.$newElement.remove(),this.$element.remove()},destroy:function(){this.$newElement.before(this.$element).remove(),this.$bsContainer?this.$bsContainer.remove():this.$menu.remove(),this.selectpicker.view.titleOption&&this.selectpicker.view.titleOption.parentNode&&this.selectpicker.view.titleOption.parentNode.removeChild(this.selectpicker.view.titleOption),this.$element.off(j).removeData("selectpicker").removeClass("bs-select-hidden selectpicker"),P(window).off(j+"."+this.selectId)}};var J=P.fn.selectpicker;function Q(){if(P.fn.dropdown)return(P.fn.dropdown.Constructor._dataApiKeydownHandler||P.fn.dropdown.Constructor.prototype.keydown).apply(this,arguments)}P.fn.selectpicker=Z,P.fn.selectpicker.Constructor=Y,P.fn.selectpicker.noConflict=function(){return P.fn.selectpicker=J,this},P(document).off("keydown.bs.dropdown.data-api").on("keydown.bs.dropdown.data-api",':not(.bootstrap-select) > [data-toggle="dropdown"]',Q).on("keydown.bs.dropdown.data-api",":not(.bootstrap-select) > .dropdown-menu",Q).on("keydown"+j,'.bootstrap-select [data-toggle="dropdown"], .bootstrap-select [role="listbox"], .bootstrap-select .bs-searchbox input',Y.prototype.keydown).on("focusin.modal",'.bootstrap-select [data-toggle="dropdown"], .bootstrap-select [role="listbox"], .bootstrap-select .bs-searchbox input',function(e){e.stopPropagation()}),P(window).on("load"+j+".data-api",function(){P(".selectpicker").each(function(){var e=P(this);Z.call(e,e.data())})})}(e)});
|
9 |
//# sourceMappingURL=bootstrap-select.min.js.map
|
resources/js/global-plugin.js
CHANGED
@@ -40,7 +40,7 @@ var iCWP_WPSF_SecurityAdmin = new function () {
|
|
40 |
|
41 |
var iCWP_WPSF_ParseAjaxResponse = new function () {
|
42 |
this.parseIt = function ( raw ) {
|
43 |
-
|
44 |
try {
|
45 |
parsed = JSON.parse( raw );
|
46 |
}
|
@@ -63,7 +63,7 @@ var iCWP_WPSF_StandardAjax = new function () {
|
|
63 |
data: reqData,
|
64 |
dataType: "text",
|
65 |
success: function ( raw ) {
|
66 |
-
|
67 |
|
68 |
if ( typeof iCWP_WPSF_Toaster !== 'undefined' ) {
|
69 |
iCWP_WPSF_Toaster.showMessage( resp.data.message, resp.success );
|
@@ -334,7 +334,7 @@ var iCWP_WPSF_Growl = new function () {
|
|
334 |
|
335 |
var iCWP_WPSF_BodyOverlay = new function () {
|
336 |
|
337 |
-
|
338 |
|
339 |
this.show = function () {
|
340 |
nOverlays++;
|
40 |
|
41 |
var iCWP_WPSF_ParseAjaxResponse = new function () {
|
42 |
this.parseIt = function ( raw ) {
|
43 |
+
var parsed = {};
|
44 |
try {
|
45 |
parsed = JSON.parse( raw );
|
46 |
}
|
63 |
data: reqData,
|
64 |
dataType: "text",
|
65 |
success: function ( raw ) {
|
66 |
+
var resp = iCWP_WPSF_ParseAjaxResponse.parseIt( raw );
|
67 |
|
68 |
if ( typeof iCWP_WPSF_Toaster !== 'undefined' ) {
|
69 |
iCWP_WPSF_Toaster.showMessage( resp.data.message, resp.success );
|
334 |
|
335 |
var iCWP_WPSF_BodyOverlay = new function () {
|
336 |
|
337 |
+
var nOverlays = 0;
|
338 |
|
339 |
this.show = function () {
|
340 |
nOverlays++;
|
resources/js/plugin.js
CHANGED
@@ -81,13 +81,13 @@ if ( typeof icwp_wpsf_vars_tourmanager !== 'undefined' ) {
|
|
81 |
|
82 |
var iCWP_WPSF_Toaster = new function () {
|
83 |
|
84 |
-
this.showMessage = function (
|
85 |
let $oNewToast = jQuery( '#icwpWpsfOptionsToast' );
|
86 |
let $oToastBody = jQuery( '.toast-body', $oNewToast );
|
87 |
$oToastBody.html( '' );
|
88 |
|
89 |
-
jQuery( '<span></span>' ).html(
|
90 |
-
.addClass(
|
91 |
.appendTo( $oToastBody );
|
92 |
|
93 |
$oNewToast.css( 'z-index', 1000 );
|
81 |
|
82 |
var iCWP_WPSF_Toaster = new function () {
|
83 |
|
84 |
+
this.showMessage = function ( msg, success ) {
|
85 |
let $oNewToast = jQuery( '#icwpWpsfOptionsToast' );
|
86 |
let $oToastBody = jQuery( '.toast-body', $oNewToast );
|
87 |
$oToastBody.html( '' );
|
88 |
|
89 |
+
jQuery( '<span></span>' ).html( msg )
|
90 |
+
.addClass( success ? 'text-dark' : 'text-danger' )
|
91 |
.appendTo( $oToastBody );
|
92 |
|
93 |
$oNewToast.css( 'z-index', 1000 );
|
resources/js/shield/ipanalyse.js
CHANGED
@@ -2,8 +2,8 @@ jQuery.fn.icwpWpsfIpAnalyse = function ( options ) {
|
|
2 |
|
3 |
var runAnalysis = function () {
|
4 |
let newUrl = window.location.href.replace( /&analyse_ip=(\d{1,3}\.){3}\d{1,3}/i, "" );
|
5 |
-
if ( $
|
6 |
-
newUrl += "&analyse_ip=" + $
|
7 |
}
|
8 |
window.history.replaceState(
|
9 |
{},
|
@@ -11,7 +11,7 @@ jQuery.fn.icwpWpsfIpAnalyse = function ( options ) {
|
|
11 |
newUrl
|
12 |
);
|
13 |
|
14 |
-
sendReq( { 'fIp': $
|
15 |
};
|
16 |
|
17 |
var clearAnalyseIpParam = function () {
|
@@ -27,7 +27,7 @@ jQuery.fn.icwpWpsfIpAnalyse = function ( options ) {
|
|
27 |
|
28 |
jQuery( '#IpReviewContent' ).html( 'loading IP info ...' );
|
29 |
|
30 |
-
var aReqData = aOpts[ '
|
31 |
jQuery.post( ajaxurl, jQuery.extend( aReqData, params ),
|
32 |
function ( oResponse ) {
|
33 |
|
@@ -61,12 +61,25 @@ jQuery.fn.icwpWpsfIpAnalyse = function ( options ) {
|
|
61 |
} );
|
62 |
|
63 |
jQuery( document ).ready( function () {
|
64 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
65 |
|
66 |
let urlParams = new URLSearchParams( window.location.search );
|
67 |
let theIP = urlParams.get( 'analyse_ip' );
|
68 |
if ( theIP ) {
|
69 |
-
$
|
70 |
runAnalysis();
|
71 |
}
|
72 |
else {
|
@@ -79,7 +92,7 @@ jQuery.fn.icwpWpsfIpAnalyse = function ( options ) {
|
|
79 |
} );
|
80 |
};
|
81 |
|
82 |
-
var $
|
83 |
var aOpts = jQuery.extend( {}, options );
|
84 |
initialise();
|
85 |
|
2 |
|
3 |
var runAnalysis = function () {
|
4 |
let newUrl = window.location.href.replace( /&analyse_ip=(\d{1,3}\.){3}\d{1,3}/i, "" );
|
5 |
+
if ( $oIpSelect.val().length > 0 ) {
|
6 |
+
newUrl += "&analyse_ip=" + $oIpSelect.val();
|
7 |
}
|
8 |
window.history.replaceState(
|
9 |
{},
|
11 |
newUrl
|
12 |
);
|
13 |
|
14 |
+
sendReq( { 'fIp': $oIpSelect.val() } );
|
15 |
};
|
16 |
|
17 |
var clearAnalyseIpParam = function () {
|
27 |
|
28 |
jQuery( '#IpReviewContent' ).html( 'loading IP info ...' );
|
29 |
|
30 |
+
var aReqData = aOpts[ 'ajax_build_ip_analyse' ];
|
31 |
jQuery.post( ajaxurl, jQuery.extend( aReqData, params ),
|
32 |
function ( oResponse ) {
|
33 |
|
61 |
} );
|
62 |
|
63 |
jQuery( document ).ready( function () {
|
64 |
+
|
65 |
+
var $aIpActions = jQuery( document ).on( 'click', 'a.ip_analyse_action', function ( evt ) {
|
66 |
+
evt.preventDefault();
|
67 |
+
if ( confirm( 'Are you sure?' ) ) {
|
68 |
+
let $oThis = jQuery( this );
|
69 |
+
let params = aOpts[ 'ajax_ip_analyse_action' ];
|
70 |
+
params.ip = $oThis.data( 'ip' );
|
71 |
+
params.ip_action = $oThis.data( 'ip_action' );
|
72 |
+
iCWP_WPSF_StandardAjax.send_ajax_req( params );
|
73 |
+
}
|
74 |
+
return false;
|
75 |
+
} );
|
76 |
+
|
77 |
+
$oIpSelect.on( 'change', runAnalysis );
|
78 |
|
79 |
let urlParams = new URLSearchParams( window.location.search );
|
80 |
let theIP = urlParams.get( 'analyse_ip' );
|
81 |
if ( theIP ) {
|
82 |
+
$oIpSelect.selectpicker( 'val', theIP );
|
83 |
runAnalysis();
|
84 |
}
|
85 |
else {
|
92 |
} );
|
93 |
};
|
94 |
|
95 |
+
var $oIpSelect = jQuery( '#IpReviewSelect' );
|
96 |
var aOpts = jQuery.extend( {}, options );
|
97 |
initialise();
|
98 |
|
resources/js/shield/mainwp-extension.js
ADDED
@@ -0,0 +1,229 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
(function ( $, window, document, undefined ) {
|
2 |
+
|
3 |
+
var pluginName = 'icwpWpsfMainwpExtension';
|
4 |
+
|
5 |
+
function Ob_TableActions( element, options ) {
|
6 |
+
this.element = element;
|
7 |
+
this._name = pluginName;
|
8 |
+
this._defaults = $.fn.icwpWpsfMainwpExt.defaults;
|
9 |
+
this.options = $.extend(
|
10 |
+
{
|
11 |
+
'forms': {
|
12 |
+
'insert': ''
|
13 |
+
}
|
14 |
+
},
|
15 |
+
this._defaults,
|
16 |
+
options
|
17 |
+
);
|
18 |
+
this.init();
|
19 |
+
}
|
20 |
+
|
21 |
+
$.extend(
|
22 |
+
Ob_TableActions.prototype,
|
23 |
+
{
|
24 |
+
init: function () {
|
25 |
+
this.buildCache();
|
26 |
+
this.bindEvents();
|
27 |
+
},
|
28 |
+
destroy: function () {
|
29 |
+
this.unbindEvents();
|
30 |
+
this.$element.removeData();
|
31 |
+
},
|
32 |
+
buildCache: function () {
|
33 |
+
this.$element = $( this.element );
|
34 |
+
this.$oFormInsert = this.options[ 'forms' ][ 'insert' ];
|
35 |
+
},
|
36 |
+
bindEvents: function () {
|
37 |
+
var plugin = this;
|
38 |
+
|
39 |
+
plugin.$element.on(
|
40 |
+
'click' + '.' + plugin._name,
|
41 |
+
'.site-dropdown a.site_action',
|
42 |
+
function ( evt ) {
|
43 |
+
evt.preventDefault();
|
44 |
+
|
45 |
+
plugin.options[ 'req_params' ] = $.extend(
|
46 |
+
plugin.options[ 'ajax_sh_site_action' ],
|
47 |
+
{
|
48 |
+
'sid': $( this ).parent().data( 'sid' ),
|
49 |
+
'saction': $( this ).data( 'saction' )
|
50 |
+
}
|
51 |
+
);
|
52 |
+
plugin.site_action.call( plugin );
|
53 |
+
}
|
54 |
+
);
|
55 |
+
|
56 |
+
plugin.$element.on(
|
57 |
+
'click' + '.' + plugin._name,
|
58 |
+
'button.action.item_action',
|
59 |
+
function ( evt ) {
|
60 |
+
evt.preventDefault();
|
61 |
+
plugin.options[ 'working_rid' ] = $( this ).data( 'rid' );
|
62 |
+
plugin.options[ 'working_item_action' ] = $( this ).data( 'item_action' );
|
63 |
+
plugin.itemAction.call( plugin );
|
64 |
+
}
|
65 |
+
);
|
66 |
+
|
67 |
+
plugin.$element.on(
|
68 |
+
'click' + '.' + plugin._name,
|
69 |
+
'.tablenav.top input[type=submit].button.action',
|
70 |
+
function ( evt ) {
|
71 |
+
evt.preventDefault();
|
72 |
+
var sAction = $( '#bulk-action-selector-top', plugin.$element ).find( ":selected" ).val();
|
73 |
+
|
74 |
+
if ( sAction === "-1" ) {
|
75 |
+
alert( icwp_wpsf_vars_insights.strings.select_action );
|
76 |
+
}
|
77 |
+
else {
|
78 |
+
var aCheckedIds = $( "input:checkbox[name=ids]:checked", plugin.$element ).map(
|
79 |
+
function () {
|
80 |
+
return $( this ).val()
|
81 |
+
} ).get();
|
82 |
+
|
83 |
+
if ( aCheckedIds.length < 1 ) {
|
84 |
+
alert( 'No rows currently selected' );
|
85 |
+
}
|
86 |
+
else {
|
87 |
+
plugin.options[ 'req_params' ][ 'bulk_action' ] = sAction;
|
88 |
+
plugin.options[ 'req_params' ][ 'ids' ] = aCheckedIds;
|
89 |
+
plugin.bulkAction.call( plugin );
|
90 |
+
}
|
91 |
+
}
|
92 |
+
return false;
|
93 |
+
}
|
94 |
+
);
|
95 |
+
|
96 |
+
plugin.$element.on(
|
97 |
+
'click' + '.' + plugin._name,
|
98 |
+
'button.action.custom-action',
|
99 |
+
function ( evt ) {
|
100 |
+
evt.preventDefault();
|
101 |
+
var $oButt = $( this );
|
102 |
+
var sCustomAction = $oButt.data( 'custom-action' );
|
103 |
+
if ( sCustomAction in plugin.options[ 'custom_actions_ajax' ] ) {
|
104 |
+
plugin.options[ 'working_custom_action' ] = plugin.options[ 'custom_actions_ajax' ][ sCustomAction ];
|
105 |
+
plugin.options[ 'working_custom_action' ][ 'rid' ] = $oButt.data( 'rid' );
|
106 |
+
plugin.customAction.call( plugin );
|
107 |
+
}
|
108 |
+
else {
|
109 |
+
/** This should never be reached live: **/
|
110 |
+
alert( 'custom action not supported: ' + sCustomAction );
|
111 |
+
}
|
112 |
+
}
|
113 |
+
);
|
114 |
+
|
115 |
+
plugin.$element.on(
|
116 |
+
'click' + '.' + plugin._name,
|
117 |
+
'button.action.href-download',
|
118 |
+
function ( evt ) {
|
119 |
+
evt.preventDefault();
|
120 |
+
var $oButt = $( this );
|
121 |
+
var sHref = $oButt.data( 'href-download' );
|
122 |
+
if ( sHref !== undefined ) {
|
123 |
+
plugin.options[ 'working_href_download' ] = sHref;
|
124 |
+
plugin.hrefDownload.call( plugin );
|
125 |
+
}
|
126 |
+
}
|
127 |
+
);
|
128 |
+
|
129 |
+
},
|
130 |
+
unbindEvents: function () {
|
131 |
+
/*
|
132 |
+
Unbind all events in our plugin's namespace that are attached
|
133 |
+
to "this.$element".
|
134 |
+
*/
|
135 |
+
this.$element.off( '.' + this._name );
|
136 |
+
},
|
137 |
+
|
138 |
+
bulkAction: function () {
|
139 |
+
let reqData = this.options[ 'ajax_bulk_action' ];
|
140 |
+
this.sendReq( reqData );
|
141 |
+
},
|
142 |
+
|
143 |
+
site_action: function () {
|
144 |
+
this.sendReq();
|
145 |
+
},
|
146 |
+
|
147 |
+
customAction: function () {
|
148 |
+
this.sendReq( this.options[ 'working_custom_action' ] );
|
149 |
+
},
|
150 |
+
|
151 |
+
hrefDownload: function () {
|
152 |
+
$.fileDownload( this.options[ 'working_href_download' ], {
|
153 |
+
preparingMessageHtml: icwp_wpsf_vars_insights.strings.downloading_file,
|
154 |
+
failMessageHtml: icwp_wpsf_vars_insights.strings.downloading_file_problem
|
155 |
+
} );
|
156 |
+
return false;
|
157 |
+
},
|
158 |
+
|
159 |
+
sendReq: function ( reqData = {} ) {
|
160 |
+
iCWP_WPSF_BodyOverlay.show();
|
161 |
+
|
162 |
+
var plugin = this;
|
163 |
+
|
164 |
+
$.post( ajaxurl, $.extend( reqData, plugin.options[ 'req_params' ] ),
|
165 |
+
function ( oR ) {
|
166 |
+
if ( oR.success ) {
|
167 |
+
iCWP_WPSF_Growl.showMessage( oR.data.message, oR.success );
|
168 |
+
if ( oR.data.page_reload ) {
|
169 |
+
setTimeout( function () {
|
170 |
+
location.reload();
|
171 |
+
}, 1500 );
|
172 |
+
}
|
173 |
+
}
|
174 |
+
else {
|
175 |
+
let msg = 'Communications error with site.';
|
176 |
+
if ( oR.data.message !== undefined ) {
|
177 |
+
msg = oR.data.message;
|
178 |
+
}
|
179 |
+
alert( msg );
|
180 |
+
iCWP_WPSF_BodyOverlay.hide();
|
181 |
+
}
|
182 |
+
}
|
183 |
+
).always( function () {
|
184 |
+
iCWP_WPSF_BodyOverlay.hide();
|
185 |
+
}
|
186 |
+
);
|
187 |
+
},
|
188 |
+
callback: function () {
|
189 |
+
}
|
190 |
+
}
|
191 |
+
);
|
192 |
+
|
193 |
+
$.fn.icwpWpsfMainwpExt = function ( options ) {
|
194 |
+
/*
|
195 |
+
jQuery( '#mainwp-shield-extension-table-sites' ).DataTable( {
|
196 |
+
serverSide: true,
|
197 |
+
"ajax": {
|
198 |
+
"url": ajaxurl,
|
199 |
+
"type": "POST",
|
200 |
+
"data": function ( d ) {
|
201 |
+
return $.extend( {}, d, this.options[ 'ajax_sh_site_action' ] );
|
202 |
+
},
|
203 |
+
"dataSrc": function ( json ) {
|
204 |
+
for ( var i = 0, ien = json.data.length; i < ien; i++ ) {
|
205 |
+
json.data[ i ].syncError = json.rowsInfo[ i ].syncError ? json.rowsInfo[ i ].syncError : false;
|
206 |
+
json.data[ i ].rowClass = json.rowsInfo[ i ].rowClass;
|
207 |
+
json.data[ i ].siteID = json.rowsInfo[ i ].siteID;
|
208 |
+
json.data[ i ].siteUrl = json.rowsInfo[ i ].siteUrl;
|
209 |
+
}
|
210 |
+
return json.data;
|
211 |
+
}
|
212 |
+
},
|
213 |
+
} );
|
214 |
+
*/
|
215 |
+
return this.each(
|
216 |
+
function () {
|
217 |
+
if ( !$.data( this, "plugin_" + pluginName ) ) {
|
218 |
+
$.data( this, "plugin_" + pluginName, new Ob_TableActions( this, options ) );
|
219 |
+
}
|
220 |
+
}
|
221 |
+
);
|
222 |
+
};
|
223 |
+
|
224 |
+
$.fn.icwpWpsfMainwpExt.defaults = {
|
225 |
+
'custom_actions_ajax': {},
|
226 |
+
'req_params': {}
|
227 |
+
};
|
228 |
+
|
229 |
+
})( jQuery );
|
src/config/feature-admin_access_restriction.php
CHANGED
@@ -16,23 +16,23 @@
|
|
16 |
"run_if_wpcli": false,
|
17 |
"order": 20
|
18 |
},
|
19 |
-
"wpcli":
|
20 |
-
"root":
|
21 |
},
|
22 |
"admin_notices": {
|
23 |
"certain-options-restricted": {
|
24 |
-
"id":
|
25 |
-
"schedule":
|
26 |
-
"plugin_admin":
|
27 |
-
"per_user":
|
28 |
-
"type":
|
29 |
},
|
30 |
"admin-users-restricted": {
|
31 |
-
"id":
|
32 |
-
"schedule":
|
33 |
-
"plugin_admin":
|
34 |
-
"type":
|
35 |
-
"per_user":
|
36 |
}
|
37 |
},
|
38 |
"sections": [
|
@@ -84,6 +84,7 @@
|
|
84 |
{
|
85 |
"key": "enable_admin_access_restriction",
|
86 |
"section": "section_enable_plugin_feature_admin_access_restriction",
|
|
|
87 |
"default": "Y",
|
88 |
"type": "checkbox",
|
89 |
"link_info": "https://shsec.io/40",
|
@@ -107,6 +108,7 @@
|
|
107 |
{
|
108 |
"key": "sec_admin_users",
|
109 |
"section": "section_admin_access_restriction_settings",
|
|
|
110 |
"sensitive": true,
|
111 |
"premium": true,
|
112 |
"default": [],
|
@@ -120,6 +122,7 @@
|
|
120 |
{
|
121 |
"key": "admin_access_timeout",
|
122 |
"section": "section_admin_access_restriction_settings",
|
|
|
123 |
"default": 30,
|
124 |
"type": "integer",
|
125 |
"min": 1,
|
@@ -132,6 +135,7 @@
|
|
132 |
{
|
133 |
"key": "allow_email_override",
|
134 |
"section": "section_admin_access_restriction_settings",
|
|
|
135 |
"default": "Y",
|
136 |
"type": "checkbox",
|
137 |
"link_info": "https://shsec.io/gf",
|
@@ -154,6 +158,7 @@
|
|
154 |
{
|
155 |
"key": "admin_access_restrict_admin_users",
|
156 |
"section": "section_admin_access_restriction_areas",
|
|
|
157 |
"default": "N",
|
158 |
"type": "checkbox",
|
159 |
"link_info": "https://shsec.io/a0",
|
@@ -165,6 +170,7 @@
|
|
165 |
{
|
166 |
"key": "admin_access_restrict_plugins",
|
167 |
"section": "section_admin_access_restriction_areas",
|
|
|
168 |
"type": "multiple_select",
|
169 |
"default": [],
|
170 |
"value_options": [
|
@@ -193,6 +199,7 @@
|
|
193 |
{
|
194 |
"key": "admin_access_restrict_themes",
|
195 |
"section": "section_admin_access_restriction_areas",
|
|
|
196 |
"type": "multiple_select",
|
197 |
"default": [],
|
198 |
"value_options": [
|
@@ -225,6 +232,7 @@
|
|
225 |
{
|
226 |
"key": "admin_access_restrict_posts",
|
227 |
"section": "section_admin_access_restriction_areas",
|
|
|
228 |
"type": "multiple_select",
|
229 |
"default": [],
|
230 |
"value_options": [
|
@@ -276,9 +284,9 @@
|
|
276 |
"type": "checkbox",
|
277 |
"link_info": "",
|
278 |
"link_blog": "",
|
279 |
-
"name": "Replace Badge
|
280 |
-
"summary": "Replace Plugin Badge URL",
|
281 |
-
"description": "When using the plugin badge, replace the link with your Whitelabel
|
282 |
},
|
283 |
{
|
284 |
"key": "wl_pluginnamemain",
|
16 |
"run_if_wpcli": false,
|
17 |
"order": 20
|
18 |
},
|
19 |
+
"wpcli": {
|
20 |
+
"root": "secadmin"
|
21 |
},
|
22 |
"admin_notices": {
|
23 |
"certain-options-restricted": {
|
24 |
+
"id": "certain-options-restricted",
|
25 |
+
"schedule": "conditions",
|
26 |
+
"plugin_admin": "no",
|
27 |
+
"per_user": true,
|
28 |
+
"type": "warning"
|
29 |
},
|
30 |
"admin-users-restricted": {
|
31 |
+
"id": "admin-users-restricted",
|
32 |
+
"schedule": "conditions",
|
33 |
+
"plugin_admin": "no",
|
34 |
+
"type": "warning",
|
35 |
+
"per_user": true
|
36 |
}
|
37 |
},
|
38 |
"sections": [
|
84 |
{
|
85 |
"key": "enable_admin_access_restriction",
|
86 |
"section": "section_enable_plugin_feature_admin_access_restriction",
|
87 |
+
"advanced": true,
|
88 |
"default": "Y",
|
89 |
"type": "checkbox",
|
90 |
"link_info": "https://shsec.io/40",
|
108 |
{
|
109 |
"key": "sec_admin_users",
|
110 |
"section": "section_admin_access_restriction_settings",
|
111 |
+
"advanced": true,
|
112 |
"sensitive": true,
|
113 |
"premium": true,
|
114 |
"default": [],
|
122 |
{
|
123 |
"key": "admin_access_timeout",
|
124 |
"section": "section_admin_access_restriction_settings",
|
125 |
+
"advanced": true,
|
126 |
"default": 30,
|
127 |
"type": "integer",
|
128 |
"min": 1,
|
135 |
{
|
136 |
"key": "allow_email_override",
|
137 |
"section": "section_admin_access_restriction_settings",
|
138 |
+
"advanced": true,
|
139 |
"default": "Y",
|
140 |
"type": "checkbox",
|
141 |
"link_info": "https://shsec.io/gf",
|
158 |
{
|
159 |
"key": "admin_access_restrict_admin_users",
|
160 |
"section": "section_admin_access_restriction_areas",
|
161 |
+
"advanced": true,
|
162 |
"default": "N",
|
163 |
"type": "checkbox",
|
164 |
"link_info": "https://shsec.io/a0",
|
170 |
{
|
171 |
"key": "admin_access_restrict_plugins",
|
172 |
"section": "section_admin_access_restriction_areas",
|
173 |
+
"advanced": true,
|
174 |
"type": "multiple_select",
|
175 |
"default": [],
|
176 |
"value_options": [
|
199 |
{
|
200 |
"key": "admin_access_restrict_themes",
|
201 |
"section": "section_admin_access_restriction_areas",
|
202 |
+
"advanced": true,
|
203 |
"type": "multiple_select",
|
204 |
"default": [],
|
205 |
"value_options": [
|
232 |
{
|
233 |
"key": "admin_access_restrict_posts",
|
234 |
"section": "section_admin_access_restriction_areas",
|
235 |
+
"advanced": true,
|
236 |
"type": "multiple_select",
|
237 |
"default": [],
|
238 |
"value_options": [
|
284 |
"type": "checkbox",
|
285 |
"link_info": "",
|
286 |
"link_blog": "",
|
287 |
+
"name": "Replace Plugin Badge",
|
288 |
+
"summary": "Replace Plugin Badge URL and Images",
|
289 |
+
"description": "When using the plugin badge, replace the URL and link with your Whitelabel settings."
|
290 |
},
|
291 |
{
|
292 |
"key": "wl_pluginnamemain",
|
src/config/feature-audit_trail.php
CHANGED
@@ -16,19 +16,26 @@
|
|
16 |
"run_if_wpcli": true,
|
17 |
"order": 110
|
18 |
},
|
19 |
-
"
|
20 |
{
|
21 |
-
"
|
22 |
-
"
|
23 |
-
"
|
24 |
-
|
25 |
-
|
26 |
-
|
27 |
-
|
28 |
-
|
29 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
30 |
{
|
31 |
"slug": "section_audit_trail_options",
|
|
|
32 |
"title": "Audit Trail Options",
|
33 |
"title_short": "Options",
|
34 |
"summary": [
|
@@ -64,6 +71,7 @@
|
|
64 |
{
|
65 |
"key": "enable_audit_trail",
|
66 |
"section": "section_enable_plugin_feature_audit_trail",
|
|
|
67 |
"default": "Y",
|
68 |
"type": "checkbox",
|
69 |
"link_info": "https://shsec.io/5p",
|
@@ -97,83 +105,6 @@
|
|
97 |
"summary": "Maximum Audit Trail Length To Keep",
|
98 |
"description": "Automatically remove any audit trail entries when this limit is exceeded."
|
99 |
},
|
100 |
-
{
|
101 |
-
"key": "enable_audit_context_users",
|
102 |
-
"section": "section_enable_audit_contexts",
|
103 |
-
"default": "Y",
|
104 |
-
"type": "checkbox",
|
105 |
-
"link_info": "https://shsec.io/a3",
|
106 |
-
"link_blog": "https://shsec.io/a1",
|
107 |
-
"name": "Users And Logins",
|
108 |
-
"summary": "Enable Audit Context - Users And Logins",
|
109 |
-
"description": "When this context is enabled, the audit trail will track activity relating to: Users And Logins"
|
110 |
-
},
|
111 |
-
{
|
112 |
-
"key": "enable_audit_context_plugins",
|
113 |
-
"section": "section_enable_audit_contexts",
|
114 |
-
"default": "Y",
|
115 |
-
"type": "checkbox",
|
116 |
-
"link_info": "https://shsec.io/a3",
|
117 |
-
"link_blog": "https://shsec.io/a1",
|
118 |
-
"name": "Plugins",
|
119 |
-
"summary": "Enable Audit Context - Plugins",
|
120 |
-
"description": "When this context is enabled, the audit trail will track activity relating to: WordPress Plugins"
|
121 |
-
},
|
122 |
-
{
|
123 |
-
"key": "enable_audit_context_themes",
|
124 |
-
"section": "section_enable_audit_contexts",
|
125 |
-
"default": "Y",
|
126 |
-
"type": "checkbox",
|
127 |
-
"link_info": "https://shsec.io/a3",
|
128 |
-
"link_blog": "https://shsec.io/a1",
|
129 |
-
"name": "Themes",
|
130 |
-
"summary": "Enable Audit Context - Themes",
|
131 |
-
"description": "When this context is enabled, the audit trail will track activity relating to: WordPress Themes"
|
132 |
-
},
|
133 |
-
{
|
134 |
-
"key": "enable_audit_context_posts",
|
135 |
-
"section": "section_enable_audit_contexts",
|
136 |
-
"default": "Y",
|
137 |
-
"type": "checkbox",
|
138 |
-
"link_info": "https://shsec.io/a3",
|
139 |
-
"link_blog": "https://shsec.io/a1",
|
140 |
-
"name": "Posts And Pages",
|
141 |
-
"summary": "Enable Audit Context - Posts And Pages",
|
142 |
-
"description": "When this context is enabled, the audit trail will track activity relating to: Editing and publishing of posts and pages"
|
143 |
-
},
|
144 |
-
{
|
145 |
-
"key": "enable_audit_context_wordpress",
|
146 |
-
"section": "section_enable_audit_contexts",
|
147 |
-
"default": "Y",
|
148 |
-
"type": "checkbox",
|
149 |
-
"link_info": "https://shsec.io/a3",
|
150 |
-
"link_blog": "https://shsec.io/a1",
|
151 |
-
"name": "WordPress And Settings",
|
152 |
-
"summary": "Enable Audit Context - WordPress And Settings",
|
153 |
-
"description": "When this context is enabled, the audit trail will track activity relating to: WordPress upgrades and changes to particular WordPress settings"
|
154 |
-
},
|
155 |
-
{
|
156 |
-
"key": "enable_audit_context_emails",
|
157 |
-
"section": "section_enable_audit_contexts",
|
158 |
-
"default": "Y",
|
159 |
-
"type": "checkbox",
|
160 |
-
"link_info": "https://shsec.io/a3",
|
161 |
-
"link_blog": "https://shsec.io/a1",
|
162 |
-
"name": "Emails",
|
163 |
-
"summary": "Enable Audit Context - Emails",
|
164 |
-
"description": "When this context is enabled, the audit trail will track activity relating to: Email Sending"
|
165 |
-
},
|
166 |
-
{
|
167 |
-
"key": "enable_audit_context_wpsf",
|
168 |
-
"section": "section_enable_audit_contexts",
|
169 |
-
"default": "Y",
|
170 |
-
"type": "checkbox",
|
171 |
-
"link_info": "https://shsec.io/a4",
|
172 |
-
"link_blog": "https://shsec.io/a1",
|
173 |
-
"name": "Shield",
|
174 |
-
"summary": "Enable Audit Context - Shield",
|
175 |
-
"description": "When this context is enabled, the audit trail will track activity relating to: Shield"
|
176 |
-
},
|
177 |
{
|
178 |
"key": "enable_change_tracking",
|
179 |
"section": "section_change_tracking",
|
@@ -232,12 +163,12 @@
|
|
232 |
}
|
233 |
],
|
234 |
"definitions": {
|
235 |
-
"db_classes":
|
236 |
"audit": "\\FernleafSystems\\Wordpress\\Plugin\\Shield\\Databases\\AuditTrail\\Handler"
|
237 |
},
|
238 |
-
"audit_trail_free_max_entries":
|
239 |
-
"audit_trail_table_name":
|
240 |
-
"audit_trail_table_columns":
|
241 |
"rid": "varchar(10) NOT NULL DEFAULT '' COMMENT 'Request ID'",
|
242 |
"ip": "varchar(40) NOT NULL DEFAULT 0 COMMENT 'Visitor IP Address'",
|
243 |
"wp_username": "varchar(255) NOT NULL DEFAULT '-' COMMENT 'WP User'",
|
@@ -252,15 +183,15 @@
|
|
252 |
"audittrail_table_timestamp_columns": {
|
253 |
"updated_at": "Updated"
|
254 |
},
|
255 |
-
"table_name_changetracking":
|
256 |
-
"table_columns_changetracking":
|
257 |
"id",
|
258 |
"data",
|
259 |
"meta",
|
260 |
"created_at",
|
261 |
"deleted_at"
|
262 |
],
|
263 |
-
"events":
|
264 |
"plugin_activated": {
|
265 |
"context": "plugins",
|
266 |
"audit_multiple": true
|
@@ -272,12 +203,18 @@
|
|
272 |
"plugin_file_edited": {
|
273 |
"context": "plugins"
|
274 |
},
|
|
|
|
|
|
|
275 |
"theme_activated": {
|
276 |
"context": "themes"
|
277 |
},
|
278 |
"theme_file_edited": {
|
279 |
"context": "themes"
|
280 |
},
|
|
|
|
|
|
|
281 |
"core_updated": {
|
282 |
"context": "wordpress"
|
283 |
},
|
16 |
"run_if_wpcli": true,
|
17 |
"order": 110
|
18 |
},
|
19 |
+
"menu_items": [
|
20 |
{
|
21 |
+
"title": "Audit Trail",
|
22 |
+
"slug": "audit-redirect",
|
23 |
+
"callback": ""
|
24 |
+
}
|
25 |
+
],
|
26 |
+
"custom_redirects": [
|
27 |
+
{
|
28 |
+
"source_mod_page": "audit-redirect",
|
29 |
+
"target_mod_page": "insights",
|
30 |
+
"query_args": {
|
31 |
+
"inav": "audit"
|
32 |
+
}
|
33 |
+
}
|
34 |
+
],
|
35 |
+
"sections": [
|
36 |
{
|
37 |
"slug": "section_audit_trail_options",
|
38 |
+
"primary": true,
|
39 |
"title": "Audit Trail Options",
|
40 |
"title_short": "Options",
|
41 |
"summary": [
|
71 |
{
|
72 |
"key": "enable_audit_trail",
|
73 |
"section": "section_enable_plugin_feature_audit_trail",
|
74 |
+
"advanced": true,
|
75 |
"default": "Y",
|
76 |
"type": "checkbox",
|
77 |
"link_info": "https://shsec.io/5p",
|
105 |
"summary": "Maximum Audit Trail Length To Keep",
|
106 |
"description": "Automatically remove any audit trail entries when this limit is exceeded."
|
107 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
108 |
{
|
109 |
"key": "enable_change_tracking",
|
110 |
"section": "section_change_tracking",
|
163 |
}
|
164 |
],
|
165 |
"definitions": {
|
166 |
+
"db_classes": {
|
167 |
"audit": "\\FernleafSystems\\Wordpress\\Plugin\\Shield\\Databases\\AuditTrail\\Handler"
|
168 |
},
|
169 |
+
"audit_trail_free_max_entries": 100,
|
170 |
+
"audit_trail_table_name": "audit_trail",
|
171 |
+
"audit_trail_table_columns": {
|
172 |
"rid": "varchar(10) NOT NULL DEFAULT '' COMMENT 'Request ID'",
|
173 |
"ip": "varchar(40) NOT NULL DEFAULT 0 COMMENT 'Visitor IP Address'",
|
174 |
"wp_username": "varchar(255) NOT NULL DEFAULT '-' COMMENT 'WP User'",
|
183 |
"audittrail_table_timestamp_columns": {
|
184 |
"updated_at": "Updated"
|
185 |
},
|
186 |
+
"table_name_changetracking": "changetracking",
|
187 |
+
"table_columns_changetracking": [
|
188 |
"id",
|
189 |
"data",
|
190 |
"meta",
|
191 |
"created_at",
|
192 |
"deleted_at"
|
193 |
],
|
194 |
+
"events": {
|
195 |
"plugin_activated": {
|
196 |
"context": "plugins",
|
197 |
"audit_multiple": true
|
203 |
"plugin_file_edited": {
|
204 |
"context": "plugins"
|
205 |
},
|
206 |
+
"plugin_upgraded": {
|
207 |
+
"context": "plugins"
|
208 |
+
},
|
209 |
"theme_activated": {
|
210 |
"context": "themes"
|
211 |
},
|
212 |
"theme_file_edited": {
|
213 |
"context": "themes"
|
214 |
},
|
215 |
+
"theme_upgraded": {
|
216 |
+
"context": "themes"
|
217 |
+
},
|
218 |
"core_updated": {
|
219 |
"context": "wordpress"
|
220 |
},
|
src/config/feature-autoupdates.php
CHANGED
@@ -51,6 +51,7 @@
|
|
51 |
{
|
52 |
"key": "enable_autoupdates",
|
53 |
"section": "section_enable_plugin_feature_automatic_updates_control",
|
|
|
54 |
"default": "Y",
|
55 |
"type": "checkbox",
|
56 |
"link_info": "https://shsec.io/3w",
|
@@ -62,6 +63,7 @@
|
|
62 |
{
|
63 |
"key": "enable_autoupdate_disable_all",
|
64 |
"section": "section_automatic_updates_for_wordpress_components",
|
|
|
65 |
"default": "N",
|
66 |
"type": "checkbox",
|
67 |
"link_info": "https://shsec.io/3v",
|
@@ -109,6 +111,7 @@
|
|
109 |
{
|
110 |
"key": "enable_autoupdate_themes",
|
111 |
"section": "section_automatic_updates_for_wordpress_components",
|
|
|
112 |
"default": "N",
|
113 |
"type": "checkbox",
|
114 |
"link_info": "",
|
@@ -132,6 +135,7 @@
|
|
132 |
{
|
133 |
"key": "autoupdate_plugin_self",
|
134 |
"section": "section_options",
|
|
|
135 |
"default": "auto",
|
136 |
"type": "select",
|
137 |
"value_options": [
|
@@ -157,7 +161,7 @@
|
|
157 |
{
|
158 |
"key": "enable_upgrade_notification_email",
|
159 |
"section": "section_options",
|
160 |
-
"default": "
|
161 |
"type": "checkbox",
|
162 |
"link_info": "",
|
163 |
"link_blog": "",
|
51 |
{
|
52 |
"key": "enable_autoupdates",
|
53 |
"section": "section_enable_plugin_feature_automatic_updates_control",
|
54 |
+
"advanced": true,
|
55 |
"default": "Y",
|
56 |
"type": "checkbox",
|
57 |
"link_info": "https://shsec.io/3w",
|
63 |
{
|
64 |
"key": "enable_autoupdate_disable_all",
|
65 |
"section": "section_automatic_updates_for_wordpress_components",
|
66 |
+
"advanced": true,
|
67 |
"default": "N",
|
68 |
"type": "checkbox",
|
69 |
"link_info": "https://shsec.io/3v",
|
111 |
{
|
112 |
"key": "enable_autoupdate_themes",
|
113 |
"section": "section_automatic_updates_for_wordpress_components",
|
114 |
+
"advanced": true,
|
115 |
"default": "N",
|
116 |
"type": "checkbox",
|
117 |
"link_info": "",
|
135 |
{
|
136 |
"key": "autoupdate_plugin_self",
|
137 |
"section": "section_options",
|
138 |
+
"advanced": true,
|
139 |
"default": "auto",
|
140 |
"type": "select",
|
141 |
"value_options": [
|
161 |
{
|
162 |
"key": "enable_upgrade_notification_email",
|
163 |
"section": "section_options",
|
164 |
+
"default": "N",
|
165 |
"type": "checkbox",
|
166 |
"link_info": "",
|
167 |
"link_blog": "",
|
src/config/feature-comments_filter.php
CHANGED
@@ -80,6 +80,7 @@
|
|
80 |
{
|
81 |
"key": "enable_comments_filter",
|
82 |
"section": "section_enable_plugin_feature_spam_comments_protection_filter",
|
|
|
83 |
"default": "Y",
|
84 |
"type": "checkbox",
|
85 |
"link_info": "https://shsec.io/3z",
|
80 |
{
|
81 |
"key": "enable_comments_filter",
|
82 |
"section": "section_enable_plugin_feature_spam_comments_protection_filter",
|
83 |
+
"advanced": true,
|
84 |
"default": "Y",
|
85 |
"type": "checkbox",
|
86 |
"link_info": "https://shsec.io/3z",
|
src/config/feature-firewall.php
CHANGED
@@ -73,6 +73,7 @@
|
|
73 |
{
|
74 |
"key": "enable_firewall",
|
75 |
"section": "section_enable_plugin_feature_wordpress_firewall",
|
|
|
76 |
"default": "Y",
|
77 |
"type": "checkbox",
|
78 |
"link_info": "https://shsec.io/43",
|
@@ -84,6 +85,7 @@
|
|
84 |
{
|
85 |
"key": "include_cookie_checks",
|
86 |
"section": "section_firewall_blocking_options",
|
|
|
87 |
"default": "N",
|
88 |
"type": "checkbox",
|
89 |
"link_info": "",
|
@@ -161,6 +163,7 @@
|
|
161 |
{
|
162 |
"key": "block_leading_schema",
|
163 |
"section": "section_firewall_blocking_options",
|
|
|
164 |
"default": "N",
|
165 |
"type": "checkbox",
|
166 |
"link_info": "",
|
@@ -172,6 +175,7 @@
|
|
172 |
{
|
173 |
"key": "block_aggressive",
|
174 |
"section": "section_firewall_blocking_options",
|
|
|
175 |
"default": "N",
|
176 |
"type": "checkbox",
|
177 |
"link_info": "",
|
@@ -183,6 +187,7 @@
|
|
183 |
{
|
184 |
"key": "block_response",
|
185 |
"section": "section_choose_firewall_block_response",
|
|
|
186 |
"default": "redirect_die_message",
|
187 |
"type": "select",
|
188 |
"value_options": [
|
@@ -223,6 +228,7 @@
|
|
223 |
{
|
224 |
"key": "page_params_whitelist",
|
225 |
"section": "section_whitelist",
|
|
|
226 |
"default": "",
|
227 |
"type": "comma_separated_lists",
|
228 |
"link_info": "https://shsec.io/2a",
|
@@ -234,6 +240,7 @@
|
|
234 |
{
|
235 |
"key": "whitelist_admins",
|
236 |
"section": "section_whitelist",
|
|
|
237 |
"default": "N",
|
238 |
"type": "checkbox",
|
239 |
"link_info": "",
|
73 |
{
|
74 |
"key": "enable_firewall",
|
75 |
"section": "section_enable_plugin_feature_wordpress_firewall",
|
76 |
+
"advanced": true,
|
77 |
"default": "Y",
|
78 |
"type": "checkbox",
|
79 |
"link_info": "https://shsec.io/43",
|
85 |
{
|
86 |
"key": "include_cookie_checks",
|
87 |
"section": "section_firewall_blocking_options",
|
88 |
+
"advanced": true,
|
89 |
"default": "N",
|
90 |
"type": "checkbox",
|
91 |
"link_info": "",
|
163 |
{
|
164 |
"key": "block_leading_schema",
|
165 |
"section": "section_firewall_blocking_options",
|
166 |
+
"advanced": true,
|
167 |
"default": "N",
|
168 |
"type": "checkbox",
|
169 |
"link_info": "",
|
175 |
{
|
176 |
"key": "block_aggressive",
|
177 |
"section": "section_firewall_blocking_options",
|
178 |
+
"advanced": true,
|
179 |
"default": "N",
|
180 |
"type": "checkbox",
|
181 |
"link_info": "",
|
187 |
{
|
188 |
"key": "block_response",
|
189 |
"section": "section_choose_firewall_block_response",
|
190 |
+
"advanced": true,
|
191 |
"default": "redirect_die_message",
|
192 |
"type": "select",
|
193 |
"value_options": [
|
228 |
{
|
229 |
"key": "page_params_whitelist",
|
230 |
"section": "section_whitelist",
|
231 |
+
"advanced": true,
|
232 |
"default": "",
|
233 |
"type": "comma_separated_lists",
|
234 |
"link_info": "https://shsec.io/2a",
|
240 |
{
|
241 |
"key": "whitelist_admins",
|
242 |
"section": "section_whitelist",
|
243 |
+
"advanced": true,
|
244 |
"default": "N",
|
245 |
"type": "checkbox",
|
246 |
"link_info": "",
|
src/config/feature-hack_protect.php
CHANGED
@@ -99,6 +99,7 @@
|
|
99 |
{
|
100 |
"key": "enable_hack_protect",
|
101 |
"section": "section_enable_plugin_feature_hack_protection_tools",
|
|
|
102 |
"default": "Y",
|
103 |
"type": "checkbox",
|
104 |
"link_info": "https://shsec.io/wpsf38",
|
@@ -309,6 +310,7 @@
|
|
309 |
{
|
310 |
"key": "ufc_scan_uploads",
|
311 |
"section": "section_scan_ufc",
|
|
|
312 |
"default": "N",
|
313 |
"type": "checkbox",
|
314 |
"link_info": "https://shsec.io/he",
|
@@ -320,6 +322,7 @@
|
|
320 |
{
|
321 |
"key": "ufc_exclusions",
|
322 |
"section": "section_scan_ufc",
|
|
|
323 |
"default": [
|
324 |
"error_log",
|
325 |
"php_error_log",
|
99 |
{
|
100 |
"key": "enable_hack_protect",
|
101 |
"section": "section_enable_plugin_feature_hack_protection_tools",
|
102 |
+
"advanced": true,
|
103 |
"default": "Y",
|
104 |
"type": "checkbox",
|
105 |
"link_info": "https://shsec.io/wpsf38",
|
310 |
{
|
311 |
"key": "ufc_scan_uploads",
|
312 |
"section": "section_scan_ufc",
|
313 |
+
"advanced": true,
|
314 |
"default": "N",
|
315 |
"type": "checkbox",
|
316 |
"link_info": "https://shsec.io/he",
|
322 |
{
|
323 |
"key": "ufc_exclusions",
|
324 |
"section": "section_scan_ufc",
|
325 |
+
"advanced": true,
|
326 |
"default": [
|
327 |
"error_log",
|
328 |
"php_error_log",
|
src/config/feature-headers.php
CHANGED
@@ -54,6 +54,7 @@
|
|
54 |
{
|
55 |
"key": "enable_headers",
|
56 |
"section": "section_enable_plugin_feature_headers",
|
|
|
57 |
"default": "Y",
|
58 |
"type": "checkbox",
|
59 |
"link_info": "https://shsec.io/aj",
|
54 |
{
|
55 |
"key": "enable_headers",
|
56 |
"section": "section_enable_plugin_feature_headers",
|
57 |
+
"advanced": true,
|
58 |
"default": "Y",
|
59 |
"type": "checkbox",
|
60 |
"link_info": "https://shsec.io/aj",
|
src/config/feature-insights.php
CHANGED
@@ -21,11 +21,6 @@
|
|
21 |
"wpcli": {
|
22 |
"enabled": false
|
23 |
},
|
24 |
-
"requirements": {
|
25 |
-
"php": {
|
26 |
-
"version": "5.4"
|
27 |
-
}
|
28 |
-
},
|
29 |
"sections": [
|
30 |
{
|
31 |
"slug": "section_non_ui",
|
21 |
"wpcli": {
|
22 |
"enabled": false
|
23 |
},
|
|
|
|
|
|
|
|
|
|
|
24 |
"sections": [
|
25 |
{
|
26 |
"slug": "section_non_ui",
|
src/config/feature-integrations.php
ADDED
@@ -0,0 +1,52 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
{
|
2 |
+
"slug": "integrations",
|
3 |
+
"properties": {
|
4 |
+
"slug": "integrations",
|
5 |
+
"storage_key": "integrations",
|
6 |
+
"name": "Integrations",
|
7 |
+
"menu_title": "Integrations",
|
8 |
+
"show_module_options": true,
|
9 |
+
"show_module_menu_item": false,
|
10 |
+
"auto_enabled": true,
|
11 |
+
"show_central": true,
|
12 |
+
"premium": false,
|
13 |
+
"access_restricted": true,
|
14 |
+
"run_if_whitelisted": true,
|
15 |
+
"run_if_wpcli": true,
|
16 |
+
"run_if_verified_bot": true,
|
17 |
+
"skip_processor": false,
|
18 |
+
"tracking_exclude": false
|
19 |
+
},
|
20 |
+
"wpcli": {
|
21 |
+
"enabled": true
|
22 |
+
},
|
23 |
+
"sections": [
|
24 |
+
{
|
25 |
+
"slug": "section_integrations",
|
26 |
+
"primary": true,
|
27 |
+
"title": "Integrations",
|
28 |
+
"title_short": "Integrations"
|
29 |
+
},
|
30 |
+
{
|
31 |
+
"slug": "section_non_ui",
|
32 |
+
"hidden": true
|
33 |
+
}
|
34 |
+
],
|
35 |
+
"options": [
|
36 |
+
{
|
37 |
+
"key": "enable_mainwp",
|
38 |
+
"section": "section_integrations",
|
39 |
+
"default": "Y",
|
40 |
+
"type": "checkbox",
|
41 |
+
"link_info": "https://shsec.io/ir",
|
42 |
+
"link_blog": "",
|
43 |
+
"name": "Enable MainWP",
|
44 |
+
"summary": "Enable The Built-In MainWP Extension",
|
45 |
+
"description": "This option will enable Shield's built-in MainWP extension for both server and client."
|
46 |
+
}
|
47 |
+
],
|
48 |
+
"definitions": {
|
49 |
+
"events": {
|
50 |
+
}
|
51 |
+
}
|
52 |
+
}
|
src/config/feature-ips.php
CHANGED
@@ -16,6 +16,22 @@
|
|
16 |
"run_if_wpcli": false,
|
17 |
"order": 100
|
18 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
19 |
"admin_notices": {
|
20 |
"visitor-whitelisted": {
|
21 |
"id": "visitor-whitelisted",
|
@@ -111,6 +127,7 @@
|
|
111 |
{
|
112 |
"key": "enable_ips",
|
113 |
"section": "section_enable_plugin_feature_ips",
|
|
|
114 |
"default": "Y",
|
115 |
"type": "checkbox",
|
116 |
"link_info": "https://shsec.io/ea",
|
@@ -133,6 +150,7 @@
|
|
133 |
{
|
134 |
"key": "auto_expire",
|
135 |
"section": "section_auto_black_list",
|
|
|
136 |
"default": "day",
|
137 |
"type": "select",
|
138 |
"value_options": [
|
@@ -166,6 +184,7 @@
|
|
166 |
{
|
167 |
"key": "user_auto_recover",
|
168 |
"section": "section_auto_black_list",
|
|
|
169 |
"premium": true,
|
170 |
"default": [],
|
171 |
"type": "multiple_select",
|
@@ -188,6 +207,7 @@
|
|
188 |
{
|
189 |
"key": "request_whitelist",
|
190 |
"section": "section_auto_black_list",
|
|
|
191 |
"premium": true,
|
192 |
"default": [],
|
193 |
"type": "array",
|
16 |
"run_if_wpcli": false,
|
17 |
"order": 100
|
18 |
},
|
19 |
+
"menu_items": [
|
20 |
+
{
|
21 |
+
"title": "IP Lists",
|
22 |
+
"slug": "ips-redirect",
|
23 |
+
"callback": ""
|
24 |
+
}
|
25 |
+
],
|
26 |
+
"custom_redirects": [
|
27 |
+
{
|
28 |
+
"source_mod_page": "ips-redirect",
|
29 |
+
"target_mod_page": "insights",
|
30 |
+
"query_args": {
|
31 |
+
"inav": "ips"
|
32 |
+
}
|
33 |
+
}
|
34 |
+
],
|
35 |
"admin_notices": {
|
36 |
"visitor-whitelisted": {
|
37 |
"id": "visitor-whitelisted",
|
127 |
{
|
128 |
"key": "enable_ips",
|
129 |
"section": "section_enable_plugin_feature_ips",
|
130 |
+
"advanced": true,
|
131 |
"default": "Y",
|
132 |
"type": "checkbox",
|
133 |
"link_info": "https://shsec.io/ea",
|
150 |
{
|
151 |
"key": "auto_expire",
|
152 |
"section": "section_auto_black_list",
|
153 |
+
"advanced": true,
|
154 |
"default": "day",
|
155 |
"type": "select",
|
156 |
"value_options": [
|
184 |
{
|
185 |
"key": "user_auto_recover",
|
186 |
"section": "section_auto_black_list",
|
187 |
+
"advanced": true,
|
188 |
"premium": true,
|
189 |
"default": [],
|
190 |
"type": "multiple_select",
|
207 |
{
|
208 |
"key": "request_whitelist",
|
209 |
"section": "section_auto_black_list",
|
210 |
+
"advanced": true,
|
211 |
"premium": true,
|
212 |
"default": [],
|
213 |
"type": "array",
|
src/config/feature-lockdown.php
CHANGED
@@ -63,6 +63,7 @@
|
|
63 |
{
|
64 |
"key": "enable_lockdown",
|
65 |
"section": "section_enable_plugin_feature_wordpress_lockdown",
|
|
|
66 |
"default": "Y",
|
67 |
"type": "checkbox",
|
68 |
"link_info": "https://shsec.io/4r",
|
@@ -85,6 +86,7 @@
|
|
85 |
{
|
86 |
"key": "disable_anonymous_restapi",
|
87 |
"section": "section_apixml",
|
|
|
88 |
"default": "N",
|
89 |
"type": "checkbox",
|
90 |
"link_info": "",
|
63 |
{
|
64 |
"key": "enable_lockdown",
|
65 |
"section": "section_enable_plugin_feature_wordpress_lockdown",
|
66 |
+
"advanced": true,
|
67 |
"default": "Y",
|
68 |
"type": "checkbox",
|
69 |
"link_info": "https://shsec.io/4r",
|
86 |
{
|
87 |
"key": "disable_anonymous_restapi",
|
88 |
"section": "section_apixml",
|
89 |
+
"advanced": true,
|
90 |
"default": "N",
|
91 |
"type": "checkbox",
|
92 |
"link_info": "",
|
src/config/feature-login_protect.php
CHANGED
@@ -114,6 +114,7 @@
|
|
114 |
{
|
115 |
"key": "enable_login_protect",
|
116 |
"section": "section_enable_plugin_feature_login_protection",
|
|
|
117 |
"default": "Y",
|
118 |
"type": "checkbox",
|
119 |
"link_info": "https://shsec.io/51",
|
@@ -125,6 +126,7 @@
|
|
125 |
{
|
126 |
"key": "rename_wplogin_path",
|
127 |
"section": "section_rename_wplogin",
|
|
|
128 |
"sensitive": true,
|
129 |
"default": "",
|
130 |
"type": "text",
|
@@ -195,6 +197,7 @@
|
|
195 |
{
|
196 |
"key": "two_factor_auth_user_roles",
|
197 |
"section": "section_2fa_email",
|
|
|
198 |
"type": "multiple_select",
|
199 |
"default": [
|
200 |
"contributor",
|
@@ -352,6 +355,7 @@
|
|
352 |
{
|
353 |
"key": "antibot_form_ids",
|
354 |
"section": "section_brute_force_login_protection",
|
|
|
355 |
"premium": true,
|
356 |
"type": "array",
|
357 |
"default": [
|
114 |
{
|
115 |
"key": "enable_login_protect",
|
116 |
"section": "section_enable_plugin_feature_login_protection",
|
117 |
+
"advanced": true,
|
118 |
"default": "Y",
|
119 |
"type": "checkbox",
|
120 |
"link_info": "https://shsec.io/51",
|
126 |
{
|
127 |
"key": "rename_wplogin_path",
|
128 |
"section": "section_rename_wplogin",
|
129 |
+
"advanced": true,
|
130 |
"sensitive": true,
|
131 |
"default": "",
|
132 |
"type": "text",
|
197 |
{
|
198 |
"key": "two_factor_auth_user_roles",
|
199 |
"section": "section_2fa_email",
|
200 |
+
"advanced": true,
|
201 |
"type": "multiple_select",
|
202 |
"default": [
|
203 |
"contributor",
|
355 |
{
|
356 |
"key": "antibot_form_ids",
|
357 |
"section": "section_brute_force_login_protection",
|
358 |
+
"advanced": true,
|
359 |
"premium": true,
|
360 |
"type": "array",
|
361 |
"default": [
|
src/config/feature-plugin.php
CHANGED
@@ -59,14 +59,6 @@
|
|
59 |
"can_dismiss": false,
|
60 |
"type": "warning"
|
61 |
},
|
62 |
-
"cloudflare-apo": {
|
63 |
-
"id": "cloudflare-apo",
|
64 |
-
"schedule": "conditions",
|
65 |
-
"valid_admin": true,
|
66 |
-
"plugin_page_only": true,
|
67 |
-
"can_dismiss": false,
|
68 |
-
"type": "error"
|
69 |
-
},
|
70 |
"wizard_welcome": {
|
71 |
"id": "wizard_welcome",
|
72 |
"per_user": false,
|
@@ -115,6 +107,11 @@
|
|
115 |
"title": "Import / Export",
|
116 |
"title_short": "Import / Export"
|
117 |
},
|
|
|
|
|
|
|
|
|
|
|
118 |
{
|
119 |
"slug": "section_global_security_options",
|
120 |
"title": "Global Plugin Security Options",
|
@@ -148,9 +145,21 @@
|
|
148 |
"summary": "Permit Anonymous Usage Information Gathering",
|
149 |
"description": "Allows us to gather information on statistics and features in-use across our client installations. This information is strictly anonymous and contains no personally, or otherwise, identifiable data."
|
150 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
151 |
{
|
152 |
"key": "visitor_address_source",
|
153 |
"section": "section_defaults",
|
|
|
154 |
"sensitive": false,
|
155 |
"type": "select",
|
156 |
"default": "AUTO_DETECT_IP",
|
@@ -243,6 +252,7 @@
|
|
243 |
{
|
244 |
"key": "enable_wpcli",
|
245 |
"section": "section_general_plugin_options",
|
|
|
246 |
"premium": true,
|
247 |
"default": "Y",
|
248 |
"type": "checkbox",
|
@@ -266,6 +276,7 @@
|
|
266 |
{
|
267 |
"key": "importexport_enable",
|
268 |
"section": "section_importexport",
|
|
|
269 |
"premium": true,
|
270 |
"default": "Y",
|
271 |
"type": "checkbox",
|
@@ -278,6 +289,7 @@
|
|
278 |
{
|
279 |
"key": "importexport_masterurl",
|
280 |
"section": "section_importexport",
|
|
|
281 |
"default": "",
|
282 |
"type": "text",
|
283 |
"link_info": "",
|
@@ -289,6 +301,7 @@
|
|
289 |
{
|
290 |
"key": "importexport_whitelist",
|
291 |
"section": "section_importexport",
|
|
|
292 |
"transferable": false,
|
293 |
"sensitive": true,
|
294 |
"default": [],
|
@@ -302,6 +315,7 @@
|
|
302 |
{
|
303 |
"key": "importexport_whitelist_notify",
|
304 |
"section": "section_importexport",
|
|
|
305 |
"sensitive": true,
|
306 |
"default": "N",
|
307 |
"type": "checkbox",
|
@@ -314,6 +328,7 @@
|
|
314 |
{
|
315 |
"key": "importexport_secretkey",
|
316 |
"section": "section_importexport",
|
|
|
317 |
"transferable": false,
|
318 |
"sensitive": true,
|
319 |
"default": "",
|
@@ -338,6 +353,7 @@
|
|
338 |
{
|
339 |
"key": "locale_override",
|
340 |
"section": "section_general_plugin_options",
|
|
|
341 |
"default": "",
|
342 |
"type": "text",
|
343 |
"link_info": "https://icwp.io/il",
|
@@ -520,11 +536,11 @@
|
|
520 |
"db_notes_name": "notes",
|
521 |
"db_notes_table_columns": {
|
522 |
"wp_username": "varchar(255) NOT NULL DEFAULT 'unknown'",
|
523 |
-
"note":
|
524 |
},
|
525 |
"geoip_table_name": "geoip",
|
526 |
"geoip_table_columns": {
|
527 |
-
"ip":
|
528 |
"meta": "TEXT"
|
529 |
},
|
530 |
"active_plugin_features": [
|
@@ -535,11 +551,13 @@
|
|
535 |
},
|
536 |
{
|
537 |
"slug": "admin_access_restriction",
|
|
|
538 |
"load_priority": 11
|
539 |
},
|
540 |
{
|
541 |
"slug": "ips",
|
542 |
-
"load_priority": 15
|
|
|
543 |
},
|
544 |
{
|
545 |
"slug": "audit_trail",
|
@@ -547,12 +565,12 @@
|
|
547 |
"hidden": false
|
548 |
},
|
549 |
{
|
550 |
-
"slug":
|
|
|
551 |
},
|
552 |
{
|
553 |
"slug": "traffic",
|
554 |
-
"load_priority": 12
|
555 |
-
"min_php": "5.4"
|
556 |
},
|
557 |
{
|
558 |
"slug": "firewall",
|
@@ -560,7 +578,8 @@
|
|
560 |
},
|
561 |
{
|
562 |
"slug": "login_protect",
|
563 |
-
"storage_key": "loginprotect"
|
|
|
564 |
},
|
565 |
{
|
566 |
"slug": "user_management"
|
@@ -581,6 +600,10 @@
|
|
581 |
"slug": "sessions",
|
582 |
"load_priority": 5
|
583 |
},
|
|
|
|
|
|
|
|
|
584 |
{
|
585 |
"slug": "license",
|
586 |
"load_priority": 10
|
59 |
"can_dismiss": false,
|
60 |
"type": "warning"
|
61 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
62 |
"wizard_welcome": {
|
63 |
"id": "wizard_welcome",
|
64 |
"per_user": false,
|
107 |
"title": "Import / Export",
|
108 |
"title_short": "Import / Export"
|
109 |
},
|
110 |
+
{
|
111 |
+
"slug": "section_integrations",
|
112 |
+
"title": "Integrations",
|
113 |
+
"title_short": "Integrations"
|
114 |
+
},
|
115 |
{
|
116 |
"slug": "section_global_security_options",
|
117 |
"title": "Global Plugin Security Options",
|
145 |
"summary": "Permit Anonymous Usage Information Gathering",
|
146 |
"description": "Allows us to gather information on statistics and features in-use across our client installations. This information is strictly anonymous and contains no personally, or otherwise, identifiable data."
|
147 |
},
|
148 |
+
{
|
149 |
+
"key": "show_advanced",
|
150 |
+
"section": "section_non_ui",
|
151 |
+
"default": "Y",
|
152 |
+
"type": "checkbox",
|
153 |
+
"link_info": "",
|
154 |
+
"link_blog": "",
|
155 |
+
"name": "Show All Options",
|
156 |
+
"summary": "Show All Options Including Those Marked As Advanced",
|
157 |
+
"description": "Shield hides advanced options from view to simplify display. Turn this option on to display advanced options at all times."
|
158 |
+
},
|
159 |
{
|
160 |
"key": "visitor_address_source",
|
161 |
"section": "section_defaults",
|
162 |
+
"advanced": true,
|
163 |
"sensitive": false,
|
164 |
"type": "select",
|
165 |
"default": "AUTO_DETECT_IP",
|
252 |
{
|
253 |
"key": "enable_wpcli",
|
254 |
"section": "section_general_plugin_options",
|
255 |
+
"advanced": true,
|
256 |
"premium": true,
|
257 |
"default": "Y",
|
258 |
"type": "checkbox",
|
276 |
{
|
277 |
"key": "importexport_enable",
|
278 |
"section": "section_importexport",
|
279 |
+
"advanced": true,
|
280 |
"premium": true,
|
281 |
"default": "Y",
|
282 |
"type": "checkbox",
|
289 |
{
|
290 |
"key": "importexport_masterurl",
|
291 |
"section": "section_importexport",
|
292 |
+
"advanced": true,
|
293 |
"default": "",
|
294 |
"type": "text",
|
295 |
"link_info": "",
|
301 |
{
|
302 |
"key": "importexport_whitelist",
|
303 |
"section": "section_importexport",
|
304 |
+
"advanced": true,
|
305 |
"transferable": false,
|
306 |
"sensitive": true,
|
307 |
"default": [],
|
315 |
{
|
316 |
"key": "importexport_whitelist_notify",
|
317 |
"section": "section_importexport",
|
318 |
+
"advanced": true,
|
319 |
"sensitive": true,
|
320 |
"default": "N",
|
321 |
"type": "checkbox",
|
328 |
{
|
329 |
"key": "importexport_secretkey",
|
330 |
"section": "section_importexport",
|
331 |
+
"advanced": true,
|
332 |
"transferable": false,
|
333 |
"sensitive": true,
|
334 |
"default": "",
|
353 |
{
|
354 |
"key": "locale_override",
|
355 |
"section": "section_general_plugin_options",
|
356 |
+
"advanced": true,
|
357 |
"default": "",
|
358 |
"type": "text",
|
359 |
"link_info": "https://icwp.io/il",
|
536 |
"db_notes_name": "notes",
|
537 |
"db_notes_table_columns": {
|
538 |
"wp_username": "varchar(255) NOT NULL DEFAULT 'unknown'",
|
539 |
+
"note": "TEXT"
|
540 |
},
|
541 |
"geoip_table_name": "geoip",
|
542 |
"geoip_table_columns": {
|
543 |
+
"ip": "varbinary(16) DEFAULT NULL COMMENT 'IP Address'",
|
544 |
"meta": "TEXT"
|
545 |
},
|
546 |
"active_plugin_features": [
|
551 |
},
|
552 |
{
|
553 |
"slug": "admin_access_restriction",
|
554 |
+
"namespace": "SecurityAdmin",
|
555 |
"load_priority": 11
|
556 |
},
|
557 |
{
|
558 |
"slug": "ips",
|
559 |
+
"load_priority": 15,
|
560 |
+
"namespace": "IPs"
|
561 |
},
|
562 |
{
|
563 |
"slug": "audit_trail",
|
565 |
"hidden": false
|
566 |
},
|
567 |
{
|
568 |
+
"slug": "hack_protect",
|
569 |
+
"namespace": "HackGuard"
|
570 |
},
|
571 |
{
|
572 |
"slug": "traffic",
|
573 |
+
"load_priority": 12
|
|
|
574 |
},
|
575 |
{
|
576 |
"slug": "firewall",
|
578 |
},
|
579 |
{
|
580 |
"slug": "login_protect",
|
581 |
+
"storage_key": "loginprotect",
|
582 |
+
"namespace": "LoginGuard"
|
583 |
},
|
584 |
{
|
585 |
"slug": "user_management"
|
600 |
"slug": "sessions",
|
601 |
"load_priority": 5
|
602 |
},
|
603 |
+
{
|
604 |
+
"slug": "integrations",
|
605 |
+
"load_priority": 20
|
606 |
+
},
|
607 |
{
|
608 |
"slug": "license",
|
609 |
"load_priority": 10
|
src/config/feature-reporting.php
CHANGED
@@ -42,6 +42,7 @@
|
|
42 |
{
|
43 |
"key": "enable_reporting",
|
44 |
"section": "section_enable_mod_reporting",
|
|
|
45 |
"default": "Y",
|
46 |
"type": "checkbox",
|
47 |
"link_info": "https://shsec.io/hb",
|
42 |
{
|
43 |
"key": "enable_reporting",
|
44 |
"section": "section_enable_mod_reporting",
|
45 |
+
"advanced": true,
|
46 |
"default": "Y",
|
47 |
"type": "checkbox",
|
48 |
"link_info": "https://shsec.io/hb",
|
src/config/feature-traffic.php
CHANGED
@@ -16,11 +16,22 @@
|
|
16 |
"run_if_wpcli": false,
|
17 |
"order": 110
|
18 |
},
|
19 |
-
"
|
20 |
-
|
21 |
-
"
|
|
|
|
|
22 |
}
|
23 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
24 |
"sections": [
|
25 |
{
|
26 |
"slug": "section_traffic_options",
|
@@ -59,6 +70,7 @@
|
|
59 |
{
|
60 |
"key": "enable_traffic",
|
61 |
"section": "section_enable_plugin_feature_traffic",
|
|
|
62 |
"default": "Y",
|
63 |
"type": "checkbox",
|
64 |
"link_info": "https://shsec.io/ed",
|
@@ -82,6 +94,7 @@
|
|
82 |
"key": "type_exclusions",
|
83 |
"section": "section_traffic_options",
|
84 |
"type": "multiple_select",
|
|
|
85 |
"default": [
|
86 |
"logged_in",
|
87 |
"cron",
|
@@ -127,6 +140,7 @@
|
|
127 |
{
|
128 |
"key": "custom_exclusions",
|
129 |
"section": "section_traffic_options",
|
|
|
130 |
"premium": true,
|
131 |
"default": [],
|
132 |
"type": "array",
|
@@ -139,6 +153,7 @@
|
|
139 |
{
|
140 |
"key": "auto_clean",
|
141 |
"section": "section_traffic_options",
|
|
|
142 |
"default": 3,
|
143 |
"min": 1,
|
144 |
"type": "integer",
|
@@ -151,6 +166,7 @@
|
|
151 |
{
|
152 |
"key": "max_entries",
|
153 |
"section": "section_traffic_options",
|
|
|
154 |
"premium": true,
|
155 |
"default": 1000,
|
156 |
"min": 0,
|
@@ -199,18 +215,18 @@
|
|
199 |
}
|
200 |
],
|
201 |
"definitions": {
|
202 |
-
"db_classes":
|
203 |
"traffic": "\\FernleafSystems\\Wordpress\\Plugin\\Shield\\Databases\\Traffic\\Handler"
|
204 |
},
|
205 |
"traffic_table_name": "traffic",
|
206 |
"traffic_table_columns": {
|
207 |
-
"rid":
|
208 |
-
"uid":
|
209 |
-
"ip":
|
210 |
-
"path":
|
211 |
-
"code":
|
212 |
-
"verb":
|
213 |
-
"ua":
|
214 |
"trans": "tinyint(1) UNSIGNED NOT NULL DEFAULT 0 COMMENT 'Trangression'"
|
215 |
},
|
216 |
"events": {
|
16 |
"run_if_wpcli": false,
|
17 |
"order": 110
|
18 |
},
|
19 |
+
"menu_items": [
|
20 |
+
{
|
21 |
+
"title": "Traffic Log",
|
22 |
+
"slug": "traffic-redirect",
|
23 |
+
"callback": ""
|
24 |
}
|
25 |
+
],
|
26 |
+
"custom_redirects": [
|
27 |
+
{
|
28 |
+
"source_mod_page": "traffic-redirect",
|
29 |
+
"target_mod_page": "insights",
|
30 |
+
"query_args": {
|
31 |
+
"inav": "traffic"
|
32 |
+
}
|
33 |
+
}
|
34 |
+
],
|
35 |
"sections": [
|
36 |
{
|
37 |
"slug": "section_traffic_options",
|
70 |
{
|
71 |
"key": "enable_traffic",
|
72 |
"section": "section_enable_plugin_feature_traffic",
|
73 |
+
"advanced": true,
|
74 |
"default": "Y",
|
75 |
"type": "checkbox",
|
76 |
"link_info": "https://shsec.io/ed",
|
94 |
"key": "type_exclusions",
|
95 |
"section": "section_traffic_options",
|
96 |
"type": "multiple_select",
|
97 |
+
"advanced": true,
|
98 |
"default": [
|
99 |
"logged_in",
|
100 |
"cron",
|
140 |
{
|
141 |
"key": "custom_exclusions",
|
142 |
"section": "section_traffic_options",
|
143 |
+
"advanced": true,
|
144 |
"premium": true,
|
145 |
"default": [],
|
146 |
"type": "array",
|
153 |
{
|
154 |
"key": "auto_clean",
|
155 |
"section": "section_traffic_options",
|
156 |
+
"advanced": true,
|
157 |
"default": 3,
|
158 |
"min": 1,
|
159 |
"type": "integer",
|
166 |
{
|
167 |
"key": "max_entries",
|
168 |
"section": "section_traffic_options",
|
169 |
+
"advanced": true,
|
170 |
"premium": true,
|
171 |
"default": 1000,
|
172 |
"min": 0,
|
215 |
}
|
216 |
],
|
217 |
"definitions": {
|
218 |
+
"db_classes": {
|
219 |
"traffic": "\\FernleafSystems\\Wordpress\\Plugin\\Shield\\Databases\\Traffic\\Handler"
|
220 |
},
|
221 |
"traffic_table_name": "traffic",
|
222 |
"traffic_table_columns": {
|
223 |
+
"rid": "varchar(10) NOT NULL DEFAULT '' COMMENT 'Request ID'",
|
224 |
+
"uid": "int(11) UNSIGNED NOT NULL DEFAULT 0 COMMENT 'User ID'",
|
225 |
+
"ip": "varbinary(16) DEFAULT NULL COMMENT 'Visitor IP Address'",
|
226 |
+
"path": "text NOT NULL DEFAULT '' COMMENT 'Request Path or URI'",
|
227 |
+
"code": "int(5) NOT NULL DEFAULT '200' COMMENT 'HTTP Response Code'",
|
228 |
+
"verb": "varchar(10) NOT NULL DEFAULT 'get' COMMENT 'HTTP Method'",
|
229 |
+
"ua": "text COMMENT 'Browser User Agent String'",
|
230 |
"trans": "tinyint(1) UNSIGNED NOT NULL DEFAULT 0 COMMENT 'Trangression'"
|
231 |
},
|
232 |
"events": {
|
src/config/feature-user_management.php
CHANGED
@@ -84,6 +84,7 @@
|
|
84 |
{
|
85 |
"key": "enable_user_management",
|
86 |
"section": "section_enable_plugin_feature_user_accounts_management",
|
|
|
87 |
"default": "Y",
|
88 |
"type": "checkbox",
|
89 |
"link_info": "https://shsec.io/e3",
|
@@ -144,6 +145,7 @@
|
|
144 |
{
|
145 |
"key": "session_lock_location",
|
146 |
"section": "section_user_session_management",
|
|
|
147 |
"default": "N",
|
148 |
"type": "checkbox",
|
149 |
"link_info": "",
|
84 |
{
|
85 |
"key": "enable_user_management",
|
86 |
"section": "section_enable_plugin_feature_user_accounts_management",
|
87 |
+
"advanced": true,
|
88 |
"default": "Y",
|
89 |
"type": "checkbox",
|
90 |
"link_info": "https://shsec.io/e3",
|
145 |
{
|
146 |
"key": "session_lock_location",
|
147 |
"section": "section_user_session_management",
|
148 |
+
"advanced": true,
|
149 |
"default": "N",
|
150 |
"type": "checkbox",
|
151 |
"link_info": "",
|
src/features/admin_access_restriction.php
CHANGED
@@ -4,6 +4,9 @@ use FernleafSystems\Wordpress\Plugin\Shield;
|
|
4 |
use FernleafSystems\Wordpress\Plugin\Shield\Modules\SecurityAdmin;
|
5 |
use FernleafSystems\Wordpress\Services\Services;
|
6 |
|
|
|
|
|
|
|
7 |
class ICWP_WPSF_FeatureHandler_AdminAccessRestriction extends ICWP_WPSF_FeatureHandler_BaseWpsf {
|
8 |
|
9 |
const HASH_DELETE = '32f68a60cef40faedbc6af20298c1a1e';
|
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';
|
src/features/audit_trail.php
CHANGED
@@ -3,12 +3,12 @@
|
|
3 |
use FernleafSystems\Wordpress\Plugin\Shield;
|
4 |
use FernleafSystems\Wordpress\Services\Services;
|
5 |
|
|
|
|
|
|
|
6 |
class ICWP_WPSF_FeatureHandler_AuditTrail extends ICWP_WPSF_FeatureHandler_BaseWpsf {
|
7 |
|
8 |
-
|
9 |
-
* @return Shield\Databases\AuditTrail\Handler
|
10 |
-
*/
|
11 |
-
public function getDbHandler_AuditTrail() {
|
12 |
return $this->getDbH( 'audit' );
|
13 |
}
|
14 |
|
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 |
|
src/features/autoupdates.php
CHANGED
@@ -2,6 +2,9 @@
|
|
2 |
|
3 |
use FernleafSystems\Wordpress\Plugin\Shield;
|
4 |
|
|
|
|
|
|
|
5 |
class ICWP_WPSF_FeatureHandler_Autoupdates extends ICWP_WPSF_FeatureHandler_BaseWpsf {
|
6 |
|
7 |
/**
|
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 |
/**
|
src/features/base.php
CHANGED
@@ -27,6 +27,7 @@ abstract class ICWP_WPSF_FeatureHandler_Base {
|
|
27 |
|
28 |
/**
|
29 |
* @var ICWP_WPSF_FeatureHandler_Email
|
|
|
30 |
*/
|
31 |
private static $oEmailHandler;
|
32 |
|
@@ -45,11 +46,6 @@ abstract class ICWP_WPSF_FeatureHandler_Base {
|
|
45 |
*/
|
46 |
private $oReporting;
|
47 |
|
48 |
-
/**
|
49 |
-
* @var Shield\Modules\Base\Strings
|
50 |
-
*/
|
51 |
-
private $oStrings;
|
52 |
-
|
53 |
/**
|
54 |
* @var Shield\Modules\Base\UI
|
55 |
*/
|
@@ -119,20 +115,6 @@ abstract class ICWP_WPSF_FeatureHandler_Base {
|
|
119 |
add_action( $con->prefix( 'daily_cron' ), [ $this, 'runDailyCron' ] );
|
120 |
add_action( $con->prefix( 'hourly_cron' ), [ $this, 'runHourlyCron' ] );
|
121 |
|
122 |
-
// supply supported events for this module
|
123 |
-
add_filter( $con->prefix( 'get_all_events' ), function ( $aEvents ) {
|
124 |
-
return array_merge(
|
125 |
-
is_array( $aEvents ) ? $aEvents : [],
|
126 |
-
array_map(
|
127 |
-
function ( $aEvt ) {
|
128 |
-
$aEvt[ 'context' ] = $this->getSlug();
|
129 |
-
return $aEvt;
|
130 |
-
},
|
131 |
-
is_array( $this->getDef( 'events' ) ) ? $this->getDef( 'events' ) : []
|
132 |
-
)
|
133 |
-
);
|
134 |
-
} );
|
135 |
-
|
136 |
add_action( 'admin_enqueue_scripts', [ $this, 'onWpEnqueueAdminJs' ], 100 );
|
137 |
|
138 |
if ( is_admin() || is_network_admin() ) {
|
@@ -223,7 +205,7 @@ abstract class ICWP_WPSF_FeatureHandler_Base {
|
|
223 |
* @return false|Shield\Modules\Base\Upgrade|mixed
|
224 |
*/
|
225 |
public function getUpgradeHandler() {
|
226 |
-
return $this->
|
227 |
}
|
228 |
|
229 |
/**
|
@@ -272,10 +254,7 @@ abstract class ICWP_WPSF_FeatureHandler_Base {
|
|
272 |
return array_merge( $aAdminNotices, $this->getOptions()->getAdminNotices() );
|
273 |
}
|
274 |
|
275 |
-
|
276 |
-
* @return bool
|
277 |
-
*/
|
278 |
-
private function verifyModuleMeetRequirements() {
|
279 |
$bMeetsReqs = true;
|
280 |
|
281 |
$aPhpReqs = $this->getOptions()->getFeatureRequirement( 'php' );
|
@@ -313,7 +292,7 @@ abstract class ICWP_WPSF_FeatureHandler_Base {
|
|
313 |
$this->doExecuteProcessor();
|
314 |
}
|
315 |
}
|
316 |
-
catch ( \Exception $
|
317 |
}
|
318 |
}
|
319 |
|
@@ -330,48 +309,6 @@ abstract class ICWP_WPSF_FeatureHandler_Base {
|
|
330 |
}
|
331 |
|
332 |
public function onWpInit() {
|
333 |
-
|
334 |
-
$sShieldAction = $this->getCon()->getShieldAction();
|
335 |
-
if ( !empty( $sShieldAction ) ) {
|
336 |
-
do_action( $this->getCon()->prefix( 'shield_action' ), $sShieldAction );
|
337 |
-
}
|
338 |
-
|
339 |
-
if ( Services::Data()->getPhpVersionIsAtLeast( '7.0' ) ) {
|
340 |
-
add_action( 'cli_init', function () {
|
341 |
-
try {
|
342 |
-
$this->getWpCli()->execute();
|
343 |
-
}
|
344 |
-
catch ( \Exception $oE ) {
|
345 |
-
}
|
346 |
-
} );
|
347 |
-
}
|
348 |
-
|
349 |
-
if ( $this->isModuleRequest() ) {
|
350 |
-
|
351 |
-
if ( Services::WpGeneral()->isAjax() ) {
|
352 |
-
$this->loadAjaxHandler();
|
353 |
-
}
|
354 |
-
else {
|
355 |
-
try {
|
356 |
-
if ( $this->verifyModActionRequest() ) {
|
357 |
-
$this->handleModAction( Services::Request()->request( 'exec' ) );
|
358 |
-
}
|
359 |
-
}
|
360 |
-
catch ( \Exception $oE ) {
|
361 |
-
wp_nonce_ays( '' );
|
362 |
-
}
|
363 |
-
}
|
364 |
-
}
|
365 |
-
|
366 |
-
$this->runWizards();
|
367 |
-
|
368 |
-
// GDPR
|
369 |
-
if ( $this->isPremium() ) {
|
370 |
-
add_filter( $this->prefix( 'wpPrivacyExport' ), [ $this, 'onWpPrivacyExport' ], 10, 3 );
|
371 |
-
add_filter( $this->prefix( 'wpPrivacyErase' ), [ $this, 'onWpPrivacyErase' ], 10, 3 );
|
372 |
-
}
|
373 |
-
|
374 |
-
$this->loadDebug();
|
375 |
}
|
376 |
|
377 |
/**
|
@@ -395,17 +332,24 @@ abstract class ICWP_WPSF_FeatureHandler_Base {
|
|
395 |
*/
|
396 |
protected function loadProcessor() {
|
397 |
if ( !isset( $this->oProcessor ) ) {
|
398 |
-
|
399 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
400 |
return null;
|
401 |
}
|
402 |
-
$this->oProcessor = new $
|
403 |
}
|
404 |
return $this->oProcessor;
|
405 |
}
|
406 |
|
407 |
/**
|
408 |
-
*
|
|
|
409 |
*/
|
410 |
protected function getProcessorClassName() :string {
|
411 |
return implode( '_',
|
@@ -431,10 +375,7 @@ abstract class ICWP_WPSF_FeatureHandler_Base {
|
|
431 |
);
|
432 |
}
|
433 |
|
434 |
-
|
435 |
-
* @return bool
|
436 |
-
*/
|
437 |
-
public function isUpgrading() {
|
438 |
return $this->getCon()->getIsRebuildOptionsFromFile() || $this->getOptions()->getRebuildFromFile();
|
439 |
}
|
440 |
|
@@ -451,10 +392,7 @@ abstract class ICWP_WPSF_FeatureHandler_Base {
|
|
451 |
}
|
452 |
}
|
453 |
|
454 |
-
|
455 |
-
* @return string
|
456 |
-
*/
|
457 |
-
public function getOptionsStorageKey() {
|
458 |
return $this->getCon()->prefixOption( $this->sOptionsStoreKey ).'_options';
|
459 |
}
|
460 |
|
@@ -483,19 +421,15 @@ abstract class ICWP_WPSF_FeatureHandler_Base {
|
|
483 |
return add_query_arg( $aActionNonce, $this->getUrl_AdminPage() );
|
484 |
}
|
485 |
|
486 |
-
|
487 |
-
* @param string $sAction
|
488 |
-
* @return array
|
489 |
-
*/
|
490 |
-
protected function getModActionParams( $sAction ) {
|
491 |
$con = $this->getCon();
|
492 |
return [
|
493 |
'action' => $con->prefix(),
|
494 |
-
'exec' => $
|
495 |
'mod_slug' => $this->getModSlug(),
|
496 |
'ts' => Services::Request()->ts(),
|
497 |
'exec_nonce' => substr(
|
498 |
-
hash_hmac( 'md5', $
|
499 |
, 0, 6 )
|
500 |
];
|
501 |
}
|
@@ -549,12 +483,10 @@ abstract class ICWP_WPSF_FeatureHandler_Base {
|
|
549 |
* TODO: Get rid of this crap and/or handle the \Exception thrown in loadFeatureHandler()
|
550 |
* @return ICWP_WPSF_FeatureHandler_Email
|
551 |
* @throws \Exception
|
|
|
552 |
*/
|
553 |
public function getEmailHandler() {
|
554 |
-
|
555 |
-
self::$oEmailHandler = $this->getCon()->getModule( 'email' );
|
556 |
-
}
|
557 |
-
return self::$oEmailHandler;
|
558 |
}
|
559 |
|
560 |
/**
|
@@ -604,26 +536,16 @@ abstract class ICWP_WPSF_FeatureHandler_Base {
|
|
604 |
|| $opts->isOpt( $this->getEnableModOptKey(), true, true );
|
605 |
}
|
606 |
|
607 |
-
|
608 |
-
* @return string
|
609 |
-
*/
|
610 |
-
public function getEnableModOptKey() {
|
611 |
return 'enable_'.$this->getSlug();
|
612 |
}
|
613 |
|
614 |
-
|
615 |
-
* @return string
|
616 |
-
*/
|
617 |
-
public function getMainFeatureName() {
|
618 |
return __( $this->getOptions()->getFeatureProperty( 'name' ), 'wp-simple-firewall' );
|
619 |
}
|
620 |
|
621 |
-
|
622 |
-
|
623 |
-
* @return string
|
624 |
-
*/
|
625 |
-
public function getModSlug( $bWithPrefix = true ) {
|
626 |
-
return $bWithPrefix ? $this->prefix( $this->getSlug() ) : $this->getSlug();
|
627 |
}
|
628 |
|
629 |
/**
|
@@ -786,11 +708,11 @@ abstract class ICWP_WPSF_FeatureHandler_Base {
|
|
786 |
|
787 |
/**
|
788 |
* Get config 'definition'.
|
789 |
-
* @param string $
|
790 |
* @return mixed|null
|
791 |
*/
|
792 |
-
public function getDef( $
|
793 |
-
return $this->getOptions()->getDef( $
|
794 |
}
|
795 |
|
796 |
/**
|
@@ -813,24 +735,10 @@ abstract class ICWP_WPSF_FeatureHandler_Base {
|
|
813 |
return $bAsString ? implode( $sGlue, $errors ) : $errors;
|
814 |
}
|
815 |
|
816 |
-
|
817 |
-
* @return bool
|
818 |
-
*/
|
819 |
-
public function hasLastErrors() {
|
820 |
return count( $this->getLastErrors( false ) ) > 0;
|
821 |
}
|
822 |
|
823 |
-
/**
|
824 |
-
* @param string $sOptionKey
|
825 |
-
* @param mixed $mValueToTest
|
826 |
-
* @param bool $bStrict
|
827 |
-
* @return bool
|
828 |
-
*/
|
829 |
-
public function isOpt( $sOptionKey, $mValueToTest, $bStrict = false ) {
|
830 |
-
$mOptionValue = $this->getOptions()->getOpt( $sOptionKey );
|
831 |
-
return $bStrict ? $mOptionValue === $mValueToTest : $mOptionValue == $mValueToTest;
|
832 |
-
}
|
833 |
-
|
834 |
public function getTextOpt( string $key ) :string {
|
835 |
$sValue = $this->getOptions()->getOpt( $key, 'default' );
|
836 |
if ( $sValue == 'default' ) {
|
@@ -872,11 +780,8 @@ abstract class ICWP_WPSF_FeatureHandler_Base {
|
|
872 |
}
|
873 |
}
|
874 |
|
875 |
-
|
876 |
-
|
877 |
-
*/
|
878 |
-
public function isModuleRequest() {
|
879 |
-
return ( $this->getModSlug() == Services::Request()->request( 'mod_slug' ) );
|
880 |
}
|
881 |
|
882 |
/**
|
@@ -911,13 +816,13 @@ abstract class ICWP_WPSF_FeatureHandler_Base {
|
|
911 |
/**
|
912 |
* @return string[]
|
913 |
*/
|
914 |
-
public function getUiTrack() {
|
915 |
-
$
|
916 |
-
return is_array( $
|
917 |
}
|
918 |
|
919 |
-
public function setDismissedNotices( array $
|
920 |
-
$this->getOptions()->setOpt( 'dismissed_notices', $
|
921 |
}
|
922 |
|
923 |
public function setUiTrack( array $UI ) {
|
@@ -1054,11 +959,8 @@ abstract class ICWP_WPSF_FeatureHandler_Base {
|
|
1054 |
return $this;
|
1055 |
}
|
1056 |
|
1057 |
-
|
1058 |
-
|
1059 |
-
*/
|
1060 |
-
protected function isAdminOptionsPage() {
|
1061 |
-
return ( is_admin() && !Services::WpGeneral()->isAjax() && $this->isThisModulePage() );
|
1062 |
}
|
1063 |
|
1064 |
/**
|
@@ -1423,6 +1325,12 @@ abstract class ICWP_WPSF_FeatureHandler_Base {
|
|
1423 |
$oRndr->setTemplateEngineTwig();
|
1424 |
}
|
1425 |
|
|
|
|
|
|
|
|
|
|
|
|
|
1426 |
$render = $oRndr->setTemplate( $template )
|
1427 |
->setRenderVars( $data )
|
1428 |
->render();
|
@@ -1448,23 +1356,29 @@ abstract class ICWP_WPSF_FeatureHandler_Base {
|
|
1448 |
}
|
1449 |
|
1450 |
public function collectOptionsForTracking() :array {
|
1451 |
-
$
|
1452 |
$aOptionsData = $this->getOptions()->getOptionsForTracking();
|
1453 |
-
foreach ( $aOptionsData as $
|
1454 |
-
unset( $aOptionsData[ $
|
1455 |
// some cleaning to ensure we don't have disallowed characters
|
1456 |
-
$
|
1457 |
-
$sType = $
|
1458 |
if ( $sType == 'checkbox' ) { // only want a boolean 1 or 0
|
1459 |
-
$aOptionsData[ $
|
1460 |
}
|
1461 |
else {
|
1462 |
-
$aOptionsData[ $
|
1463 |
}
|
1464 |
}
|
1465 |
return $aOptionsData;
|
1466 |
}
|
1467 |
|
|
|
|
|
|
|
|
|
|
|
|
|
1468 |
/**
|
1469 |
* See plugin controller for the nature of $aData wpPrivacyExport()
|
1470 |
* @param array $aExportItems
|
@@ -1493,7 +1407,7 @@ abstract class ICWP_WPSF_FeatureHandler_Base {
|
|
1493 |
public function getOptions() {
|
1494 |
if ( !isset( $this->oOpts ) ) {
|
1495 |
$oCon = $this->getCon();
|
1496 |
-
$this->oOpts = $this->
|
1497 |
$this->oOpts->setPathToConfig( $oCon->getPath_ConfigFile( $this->getSlug() ) )
|
1498 |
->setRebuildFromFile( $oCon->getIsRebuildOptionsFromFile() )
|
1499 |
->setOptionsStorageKey( $this->getOptionsStorageKey() )
|
@@ -1508,14 +1422,10 @@ abstract class ICWP_WPSF_FeatureHandler_Base {
|
|
1508 |
*/
|
1509 |
public function getWpCli() {
|
1510 |
if ( !isset( $this->oWpCli ) ) {
|
1511 |
-
$this->oWpCli = $this->
|
1512 |
if ( !$this->oWpCli instanceof Shield\Modules\Base\WpCli ) {
|
1513 |
-
|
1514 |
-
if ( !$this->oWpCli instanceof Shield\Modules\Base\WpCli ) {
|
1515 |
-
throw new \Exception( 'WP-CLI not supported' );
|
1516 |
-
}
|
1517 |
}
|
1518 |
-
$this->oWpCli->setMod( $this );
|
1519 |
}
|
1520 |
return $this->oWpCli;
|
1521 |
}
|
@@ -1524,10 +1434,7 @@ abstract class ICWP_WPSF_FeatureHandler_Base {
|
|
1524 |
* @return null|Shield\Modules\Base\Strings
|
1525 |
*/
|
1526 |
public function getStrings() {
|
1527 |
-
|
1528 |
-
$this->oStrings = $this->loadStrings()->setMod( $this );
|
1529 |
-
}
|
1530 |
-
return $this->oStrings;
|
1531 |
}
|
1532 |
|
1533 |
/**
|
@@ -1535,12 +1442,11 @@ abstract class ICWP_WPSF_FeatureHandler_Base {
|
|
1535 |
*/
|
1536 |
public function getUIHandler() {
|
1537 |
if ( !isset( $this->oUI ) ) {
|
1538 |
-
$this->oUI = $this->
|
1539 |
if ( !$this->oUI instanceof Shield\Modules\Base\UI ) {
|
1540 |
// TODO: autoloader for base classes
|
1541 |
-
$this->oUI = $this->
|
1542 |
}
|
1543 |
-
$this->oUI->setMod( $this );
|
1544 |
}
|
1545 |
return $this->oUI;
|
1546 |
}
|
@@ -1550,42 +1456,31 @@ abstract class ICWP_WPSF_FeatureHandler_Base {
|
|
1550 |
*/
|
1551 |
public function getReportingHandler() {
|
1552 |
if ( !isset( $this->oReporting ) ) {
|
1553 |
-
$this->oReporting = $this->
|
1554 |
-
if ( $this->oReporting instanceof Shield\Modules\Base\BaseReporting ) {
|
1555 |
-
$this->oReporting->setMod( $this );
|
1556 |
-
}
|
1557 |
}
|
1558 |
return $this->oReporting;
|
1559 |
}
|
1560 |
|
1561 |
-
/**
|
1562 |
-
* @return $this
|
1563 |
-
*/
|
1564 |
protected function loadAdminNotices() {
|
1565 |
-
$
|
1566 |
-
if ( $
|
1567 |
-
$
|
1568 |
}
|
1569 |
-
return $this;
|
1570 |
}
|
1571 |
|
1572 |
-
/**
|
1573 |
-
* @return $this
|
1574 |
-
*/
|
1575 |
protected function loadAjaxHandler() {
|
1576 |
-
$oAj = $this->
|
1577 |
if ( !$oAj instanceof Shield\Modules\Base\AjaxHandlerBase ) {
|
1578 |
-
$
|
1579 |
}
|
1580 |
-
$oAj->setMod( $this );
|
1581 |
-
return $this;
|
1582 |
}
|
1583 |
|
1584 |
/**
|
1585 |
* @return Shield\Modules\Base\ShieldOptions|mixed
|
|
|
1586 |
*/
|
1587 |
protected function loadOptions() {
|
1588 |
-
return $this->
|
1589 |
}
|
1590 |
|
1591 |
protected function loadDebug() {
|
@@ -1602,12 +1497,13 @@ abstract class ICWP_WPSF_FeatureHandler_Base {
|
|
1602 |
* @return Shield\Modules\Base\Strings|mixed
|
1603 |
*/
|
1604 |
protected function loadStrings() {
|
1605 |
-
return $this->
|
1606 |
}
|
1607 |
|
1608 |
/**
|
1609 |
* @param $sClass
|
1610 |
* @return \stdClass|mixed|false
|
|
|
1611 |
*/
|
1612 |
private function loadClass( $sClass ) {
|
1613 |
$sC = $this->getNamespace().$sClass;
|
@@ -1619,10 +1515,10 @@ abstract class ICWP_WPSF_FeatureHandler_Base {
|
|
1619 |
* @param false $injectMod
|
1620 |
* @return false|Shield\Modules\ModConsumer
|
1621 |
*/
|
1622 |
-
private function loadModElement( $class, $injectMod = true ) {
|
1623 |
$element = false;
|
1624 |
try {
|
1625 |
-
$C = $this->findElementClass( $class, true
|
1626 |
/** @var Shield\Modules\ModConsumer $element */
|
1627 |
$element = @class_exists( $C ) ? new $C() : false;
|
1628 |
if ( $injectMod && method_exists( $element, 'setMod' ) ) {
|
@@ -1637,6 +1533,7 @@ abstract class ICWP_WPSF_FeatureHandler_Base {
|
|
1637 |
/**
|
1638 |
* @param $sClass
|
1639 |
* @return \stdClass|mixed|false
|
|
|
1640 |
*/
|
1641 |
private function loadClassFromBase( $sClass ) {
|
1642 |
$sC = $this->getBaseNamespace().$sClass;
|
@@ -1646,24 +1543,19 @@ abstract class ICWP_WPSF_FeatureHandler_Base {
|
|
1646 |
/**
|
1647 |
* @param string $element
|
1648 |
* @param bool $bThrowException
|
1649 |
-
* @param bool $bUseFoundationBase
|
1650 |
* @return string|null
|
1651 |
* @throws \Exception
|
1652 |
*/
|
1653 |
-
protected function findElementClass( $element, $
|
1654 |
-
$namespace = [
|
1655 |
-
$this->getNamespace(),
|
1656 |
-
];
|
1657 |
-
if ( $bUseFoundationBase ) {
|
1658 |
-
$namespace[] = $this->getBaseNamespace();
|
1659 |
-
}
|
1660 |
-
|
1661 |
$theClass = null;
|
1662 |
-
|
|
|
1663 |
$maybe = $NS.$element;
|
1664 |
if ( @class_exists( $maybe ) ) {
|
1665 |
-
|
1666 |
-
|
|
|
|
|
1667 |
}
|
1668 |
}
|
1669 |
|
@@ -1674,11 +1566,22 @@ abstract class ICWP_WPSF_FeatureHandler_Base {
|
|
1674 |
}
|
1675 |
|
1676 |
private function getBaseNamespace() {
|
1677 |
-
return '
|
|
|
|
|
|
|
|
|
1678 |
}
|
1679 |
|
1680 |
protected function getNamespace() :string {
|
1681 |
-
return
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1682 |
}
|
1683 |
|
1684 |
protected function getNamespaceBase() :string {
|
@@ -1693,33 +1596,4 @@ abstract class ICWP_WPSF_FeatureHandler_Base {
|
|
1693 |
public function savePluginOptions() {
|
1694 |
$this->saveModOptions();
|
1695 |
}
|
1696 |
-
|
1697 |
-
/**
|
1698 |
-
* @deprecated 10.0
|
1699 |
-
*/
|
1700 |
-
public function getOptionStoragePrefix() :string {
|
1701 |
-
return $this->getCon()->getOptionStoragePrefix();
|
1702 |
-
}
|
1703 |
-
|
1704 |
-
/**
|
1705 |
-
* @param string $sOptionKey
|
1706 |
-
* @param mixed $mDefault
|
1707 |
-
* @return mixed
|
1708 |
-
* @deprecated 10.0
|
1709 |
-
*/
|
1710 |
-
public function getOpt( $sOptionKey, $mDefault = false ) {
|
1711 |
-
return $this->getOptions()->getOpt( $sOptionKey, $mDefault );
|
1712 |
-
}
|
1713 |
-
|
1714 |
-
/**
|
1715 |
-
* Sets the value for the given option key
|
1716 |
-
* @param string $key
|
1717 |
-
* @param mixed $value
|
1718 |
-
* @return $this
|
1719 |
-
* @deprecated 10.0
|
1720 |
-
*/
|
1721 |
-
protected function setOpt( string $key, $value ) {
|
1722 |
-
$this->getOptions()->setOpt( $key, $value );
|
1723 |
-
return $this;
|
1724 |
-
}
|
1725 |
}
|
27 |
|
28 |
/**
|
29 |
* @var ICWP_WPSF_FeatureHandler_Email
|
30 |
+
* @deprecated 10.1
|
31 |
*/
|
32 |
private static $oEmailHandler;
|
33 |
|
46 |
*/
|
47 |
private $oReporting;
|
48 |
|
|
|
|
|
|
|
|
|
|
|
49 |
/**
|
50 |
* @var Shield\Modules\Base\UI
|
51 |
*/
|
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() ) {
|
205 |
* @return false|Shield\Modules\Base\Upgrade|mixed
|
206 |
*/
|
207 |
public function getUpgradeHandler() {
|
208 |
+
return $this->loadModElement( 'Upgrade' );
|
209 |
}
|
210 |
|
211 |
/**
|
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' );
|
292 |
$this->doExecuteProcessor();
|
293 |
}
|
294 |
}
|
295 |
+
catch ( \Exception $e ) {
|
296 |
}
|
297 |
}
|
298 |
|
309 |
}
|
310 |
|
311 |
public function onWpInit() {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
312 |
}
|
313 |
|
314 |
/**
|
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( '_',
|
375 |
);
|
376 |
}
|
377 |
|
378 |
+
public function isUpgrading() :bool {
|
|
|
|
|
|
|
379 |
return $this->getCon()->getIsRebuildOptionsFromFile() || $this->getOptions()->getRebuildFromFile();
|
380 |
}
|
381 |
|
392 |
}
|
393 |
}
|
394 |
|
395 |
+
public function getOptionsStorageKey() :string {
|
|
|
|
|
|
|
396 |
return $this->getCon()->prefixOption( $this->sOptionsStoreKey ).'_options';
|
397 |
}
|
398 |
|
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 |
}
|
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 |
/**
|
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 |
/**
|
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 |
/**
|
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' ) {
|
780 |
}
|
781 |
}
|
782 |
|
783 |
+
public function isModuleRequest() :bool {
|
784 |
+
return $this->getModSlug() === Services::Request()->request( 'mod_slug' );
|
|
|
|
|
|
|
785 |
}
|
786 |
|
787 |
/**
|
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 ) {
|
959 |
return $this;
|
960 |
}
|
961 |
|
962 |
+
protected function isAdminOptionsPage() :bool {
|
963 |
+
return is_admin() && !Services::WpGeneral()->isAjax() && $this->isThisModulePage();
|
|
|
|
|
|
|
964 |
}
|
965 |
|
966 |
/**
|
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();
|
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
|
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() )
|
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 |
}
|
1434 |
* @return null|Shield\Modules\Base\Strings
|
1435 |
*/
|
1436 |
public function getStrings() {
|
1437 |
+
return $this->loadStrings()->setMod( $this );
|
|
|
|
|
|
|
1438 |
}
|
1439 |
|
1440 |
/**
|
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 |
}
|
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() {
|
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;
|
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' ) ) {
|
1533 |
/**
|
1534 |
* @param $sClass
|
1535 |
* @return \stdClass|mixed|false
|
1536 |
+
* @deprecated 10.1
|
1537 |
*/
|
1538 |
private function loadClassFromBase( $sClass ) {
|
1539 |
$sC = $this->getBaseNamespace().$sClass;
|
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 |
|
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 {
|
1596 |
public function savePluginOptions() {
|
1597 |
$this->saveModOptions();
|
1598 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1599 |
}
|
src/features/base_wpsf.php
CHANGED
@@ -110,13 +110,6 @@ class ICWP_WPSF_FeatureHandler_BaseWpsf extends ICWP_WPSF_FeatureHandler_Base {
|
|
110 |
return $oCfg;
|
111 |
}
|
112 |
|
113 |
-
/**
|
114 |
-
* @return bool
|
115 |
-
*/
|
116 |
-
public function isWlEnabled() {
|
117 |
-
return $this->getCon()->getModule_SecAdmin()->isWlEnabled();
|
118 |
-
}
|
119 |
-
|
120 |
/**
|
121 |
* @return array
|
122 |
*/
|
@@ -255,4 +248,15 @@ class ICWP_WPSF_FeatureHandler_BaseWpsf extends ICWP_WPSF_FeatureHandler_Base {
|
|
255 |
}
|
256 |
return array_unique( array_filter( $aCleaned ) );
|
257 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
258 |
}
|
110 |
return $oCfg;
|
111 |
}
|
112 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
113 |
/**
|
114 |
* @return array
|
115 |
*/
|
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
CHANGED
@@ -3,6 +3,9 @@
|
|
3 |
use FernleafSystems\Wordpress\Plugin\Shield;
|
4 |
use FernleafSystems\Wordpress\Plugin\Shield\Modules\CommentsFilter;
|
5 |
|
|
|
|
|
|
|
6 |
class ICWP_WPSF_FeatureHandler_CommentsFilter extends ICWP_WPSF_FeatureHandler_BaseWpsf {
|
7 |
|
8 |
/**
|
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 |
/**
|
src/features/comms.php
CHANGED
@@ -2,12 +2,12 @@
|
|
2 |
|
3 |
use FernleafSystems\Wordpress\Plugin\Shield\Modules\Comms;
|
4 |
|
|
|
|
|
|
|
|
|
5 |
class ICWP_WPSF_FeatureHandler_Comms extends ICWP_WPSF_FeatureHandler_BaseWpsf {
|
6 |
|
7 |
-
protected function doPostConstruction() {
|
8 |
-
parent::doPostConstruction(); // TODO: Change the autogenerated stub
|
9 |
-
}
|
10 |
-
|
11 |
public function getSureSendController() :Comms\Lib\SureSend\SureSendController {
|
12 |
return ( new Comms\Lib\SureSend\SureSendController() )
|
13 |
->setMod( $this );
|
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 );
|
src/features/email.php
CHANGED
@@ -1,5 +1,8 @@
|
|
1 |
<?php declare( strict_types=1 );
|
2 |
|
|
|
|
|
|
|
3 |
class ICWP_WPSF_FeatureHandler_Email extends ICWP_WPSF_FeatureHandler_BaseWpsf {
|
4 |
|
5 |
protected function getNamespaceBase() :string {
|
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 {
|
src/features/events.php
CHANGED
@@ -2,6 +2,10 @@
|
|
2 |
|
3 |
use FernleafSystems\Wordpress\Plugin\Shield;
|
4 |
|
|
|
|
|
|
|
|
|
5 |
class ICWP_WPSF_FeatureHandler_Events extends ICWP_WPSF_FeatureHandler_BaseWpsf {
|
6 |
|
7 |
/**
|
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 |
/**
|
src/features/firewall.php
CHANGED
@@ -3,6 +3,9 @@
|
|
3 |
use FernleafSystems\Wordpress\Plugin\Shield;
|
4 |
use FernleafSystems\Wordpress\Plugin\Shield\Modules\Firewall;
|
5 |
|
|
|
|
|
|
|
6 |
class ICWP_WPSF_FeatureHandler_Firewall extends ICWP_WPSF_FeatureHandler_BaseWpsf {
|
7 |
|
8 |
/**
|
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 |
/**
|
src/features/hack_protect.php
CHANGED
@@ -4,6 +4,9 @@ use FernleafSystems\Wordpress\Plugin\Shield;
|
|
4 |
use FernleafSystems\Wordpress\Plugin\Shield\Modules\HackGuard;
|
5 |
use FernleafSystems\Wordpress\Services\Services;
|
6 |
|
|
|
|
|
|
|
7 |
class ICWP_WPSF_FeatureHandler_HackProtect extends ICWP_WPSF_FeatureHandler_BaseWpsf {
|
8 |
|
9 |
/**
|
@@ -80,6 +83,14 @@ class ICWP_WPSF_FeatureHandler_HackProtect extends ICWP_WPSF_FeatureHandler_Base
|
|
80 |
return $this->aScanCons[ $slug ];
|
81 |
}
|
82 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
83 |
protected function handleModAction( string $sAction ) {
|
84 |
switch ( $sAction ) {
|
85 |
case 'scan_file_download':
|
@@ -102,12 +113,6 @@ class ICWP_WPSF_FeatureHandler_HackProtect extends ICWP_WPSF_FeatureHandler_Base
|
|
102 |
|
103 |
$this->cleanFileExclusions();
|
104 |
|
105 |
-
if ( $opts->isOptChanged( 'scan_frequency' ) ) {
|
106 |
-
/** @var \ICWP_WPSF_Processor_HackProtect $oPro */
|
107 |
-
$oPro = $this->getProcessor();
|
108 |
-
$oPro->getSubProScanner()->deleteCron();
|
109 |
-
}
|
110 |
-
|
111 |
if ( count( $opts->getFilesToLock() ) === 0 || !$this->getCon()
|
112 |
->getModule_Plugin()
|
113 |
->getShieldNetApiController()
|
@@ -151,16 +156,16 @@ class ICWP_WPSF_FeatureHandler_HackProtect extends ICWP_WPSF_FeatureHandler_Base
|
|
151 |
}
|
152 |
|
153 |
/**
|
154 |
-
* @param string $
|
155 |
* @return int
|
156 |
*/
|
157 |
-
public function getLastScanAt( $
|
158 |
/** @var Shield\Databases\Events\Select $oSel */
|
159 |
$oSel = $this->getCon()
|
160 |
->getModule_Events()
|
161 |
->getDbHandler_Events()
|
162 |
->getQuerySelector();
|
163 |
-
$oEntry = $oSel->getLatestForEvent( $
|
164 |
return ( $oEntry instanceof Shield\Databases\Events\EntryVO ) ? $oEntry->created_at : 0;
|
165 |
}
|
166 |
|
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 |
/**
|
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':
|
113 |
|
114 |
$this->cleanFileExclusions();
|
115 |
|
|
|
|
|
|
|
|
|
|
|
|
|
116 |
if ( count( $opts->getFilesToLock() ) === 0 || !$this->getCon()
|
117 |
->getModule_Plugin()
|
118 |
->getShieldNetApiController()
|
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 |
|
src/features/headers.php
CHANGED
@@ -3,6 +3,9 @@
|
|
3 |
use FernleafSystems\Wordpress\Plugin\Shield\Modules\Headers;
|
4 |
use FernleafSystems\Wordpress\Services\Services;
|
5 |
|
|
|
|
|
|
|
6 |
class ICWP_WPSF_FeatureHandler_Headers extends ICWP_WPSF_FeatureHandler_BaseWpsf {
|
7 |
|
8 |
protected function preProcessOptions() {
|
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() {
|
src/features/insights.php
CHANGED
@@ -3,6 +3,9 @@
|
|
3 |
use FernleafSystems\Wordpress\Plugin\Shield;
|
4 |
use FernleafSystems\Wordpress\Services\Services;
|
5 |
|
|
|
|
|
|
|
6 |
class ICWP_WPSF_FeatureHandler_Insights extends ICWP_WPSF_FeatureHandler_BaseWpsf {
|
7 |
|
8 |
protected function onModulesLoaded() {
|
@@ -24,183 +27,14 @@ class ICWP_WPSF_FeatureHandler_Insights extends ICWP_WPSF_FeatureHandler_BaseWps
|
|
24 |
public function getUrl_SubInsightsPage( string $subPage ) :string {
|
25 |
return add_query_arg(
|
26 |
[ 'inav' => sanitize_key( $subPage ) ],
|
27 |
-
$this->
|
28 |
);
|
29 |
}
|
30 |
|
31 |
protected function renderModulePage( array $aData = [] ) :string {
|
32 |
-
|
33 |
-
$
|
34 |
-
|
35 |
-
$sNavSection = $oReq->query( 'inav', 'overview' );
|
36 |
-
$sSubNavSection = $oReq->query( 'subnav' );
|
37 |
-
|
38 |
-
$oModPlugin = $con->getModule_Plugin();
|
39 |
-
$oTourManager = $oModPlugin->getTourManager();
|
40 |
-
if ( !$oTourManager->isCompleted( 'insights_overview' ) && $oModPlugin->getActivateLength() > 600 ) {
|
41 |
-
$oTourManager->setCompleted( 'insights_overview' );
|
42 |
-
}
|
43 |
-
|
44 |
-
$bIsPro = $this->isPremium();
|
45 |
-
switch ( $sNavSection ) {
|
46 |
-
|
47 |
-
case 'audit':
|
48 |
-
/** @var Shield\Modules\AuditTrail\UI $UI */
|
49 |
-
$UI = $con->getModule_AuditTrail()->getUIHandler();
|
50 |
-
$aData = $UI->buildInsightsVars();
|
51 |
-
break;
|
52 |
-
|
53 |
-
case 'debug':
|
54 |
-
/** @var Shield\Modules\Plugin\UI $UI */
|
55 |
-
$UI = $con->getModule_Plugin()->getUIHandler();
|
56 |
-
$aData = $UI->buildInsightsVars_Debug();
|
57 |
-
break;
|
58 |
-
|
59 |
-
case 'ips':
|
60 |
-
/** @var Shield\Modules\IPs\UI $UI */
|
61 |
-
$UI = $con->getModule_IPs()->getUIHandler();
|
62 |
-
$aData = $UI->buildInsightsVars();
|
63 |
-
break;
|
64 |
-
|
65 |
-
case 'traffic':
|
66 |
-
/** @var Shield\Modules\Traffic\UI $UI */
|
67 |
-
$UI = $con->getModule_Traffic()->getUIHandler();
|
68 |
-
$aData = $UI->buildInsightsVars();
|
69 |
-
break;
|
70 |
-
|
71 |
-
case 'license':
|
72 |
-
/** @var Shield\Modules\License\UI $UILicense */
|
73 |
-
$UILicense = $con->getModule_License()->getUIHandler();
|
74 |
-
$aData = $UILicense->buildInsightsVars();
|
75 |
-
break;
|
76 |
-
|
77 |
-
case 'scans':
|
78 |
-
/** @var Shield\Modules\HackGuard\UI $UIHackGuard */
|
79 |
-
$UIHackGuard = $con->getModule_HackGuard()->getUIHandler();
|
80 |
-
$aData = $UIHackGuard->buildInsightsVars();
|
81 |
-
break;
|
82 |
-
|
83 |
-
case 'importexport':
|
84 |
-
$aData = $oModPlugin->getImpExpController()->buildInsightsVars();
|
85 |
-
break;
|
86 |
-
|
87 |
-
case 'reports':
|
88 |
-
/** @var Shield\Modules\Reporting\UI $UIReporting */
|
89 |
-
$UIReporting = $con->getModule_Reporting()->getUIHandler();
|
90 |
-
$aData = $UIReporting->buildInsightsVars();
|
91 |
-
break;
|
92 |
-
|
93 |
-
case 'users':
|
94 |
-
/** @var Shield\Modules\UserManagement\UI $UIUsers */
|
95 |
-
$UIUsers = $con->getModule( 'user_management' )->getUIHandler();
|
96 |
-
$aData = $UIUsers->buildInsightsVars();
|
97 |
-
break;
|
98 |
-
|
99 |
-
case 'settings':
|
100 |
-
$aData = [
|
101 |
-
'ajax' => [
|
102 |
-
'mod_options' => $con->getModule( Services::Request()->query( 'subnav' ) )
|
103 |
-
->getAjaxActionData( 'mod_options', true ),
|
104 |
-
'mod_opts_form_render' => $con->getModule( Services::Request()->query( 'subnav' ) )
|
105 |
-
->getAjaxActionData( 'mod_opts_form_render', true ),
|
106 |
-
],
|
107 |
-
];
|
108 |
-
break;
|
109 |
-
|
110 |
-
default:
|
111 |
-
case 'overview':
|
112 |
-
case 'index':
|
113 |
-
/** @var Shield\Modules\Insights\UI $UIInsights */
|
114 |
-
$UIInsights = $this->getUIHandler();
|
115 |
-
$aData = $UIInsights->buildInsightsVars();
|
116 |
-
break;
|
117 |
-
}
|
118 |
-
|
119 |
-
$aTopNav = [
|
120 |
-
'settings' => __( 'Settings', 'wp-simple-firewall' ),
|
121 |
-
'overview' => __( 'Overview', 'wp-simple-firewall' ),
|
122 |
-
'scans' => __( 'Scans', 'wp-simple-firewall' ),
|
123 |
-
'ips' => __( 'IPs', 'wp-simple-firewall' ),
|
124 |
-
'audit' => __( 'Logs', 'wp-simple-firewall' ),
|
125 |
-
'users' => __( 'Users', 'wp-simple-firewall' ),
|
126 |
-
'license' => __( 'Pro', 'wp-simple-firewall' ),
|
127 |
-
'traffic' => __( 'Traffic', 'wp-simple-firewall' ),
|
128 |
-
'importexport' => __( 'Import', 'wp-simple-firewall' ),
|
129 |
-
'reports' => __( 'Reports', 'wp-simple-firewall' ),
|
130 |
-
'debug' => __( 'Debug', 'wp-simple-firewall' ),
|
131 |
-
// 'importexport' => sprintf( '%s/%s', __( 'Import', 'wp-simple-firewall' ), __( 'Export', 'wp-simple-firewall' ) ),
|
132 |
-
];
|
133 |
-
if ( $bIsPro ) {
|
134 |
-
unset( $aTopNav[ 'license' ] );
|
135 |
-
$aTopNav[ 'license' ] = __( 'Pro', 'wp-simple-firewall' );
|
136 |
-
}
|
137 |
-
|
138 |
-
array_walk( $aTopNav, function ( &$sName, $sKey ) use ( $sNavSection ) {
|
139 |
-
$sName = [
|
140 |
-
'href' => add_query_arg( [ 'inav' => $sKey ], $this->getUrl_AdminPage() ),
|
141 |
-
'name' => $sName,
|
142 |
-
'slug' => $sKey,
|
143 |
-
'active' => $sKey === $sNavSection,
|
144 |
-
'subnavs' => [],
|
145 |
-
'icon' => ''
|
146 |
-
];
|
147 |
-
} );
|
148 |
-
|
149 |
-
$aSearchSelect = [];
|
150 |
-
$aSettingsSubNav = [];
|
151 |
-
foreach ( $this->getModulesSummaryData() as $slug => $summary ) {
|
152 |
-
if ( $summary[ 'show_mod_opts' ] ) {
|
153 |
-
$aSettingsSubNav[ $slug ] = [
|
154 |
-
'href' => add_query_arg( [ 'subnav' => $slug ], $aTopNav[ 'settings' ][ 'href' ] ),
|
155 |
-
'name' => $summary[ 'name' ],
|
156 |
-
'active' => $slug === $sSubNavSection,
|
157 |
-
'slug' => $slug
|
158 |
-
];
|
159 |
-
|
160 |
-
$aSearchSelect[ $summary[ 'name' ] ] = $summary[ 'options' ];
|
161 |
-
}
|
162 |
-
}
|
163 |
-
|
164 |
-
if ( empty( $aSettingsSubNav ) ) {
|
165 |
-
unset( $aTopNav[ 'settings' ] );
|
166 |
-
}
|
167 |
-
else {
|
168 |
-
$aTopNav[ 'settings' ][ 'subnavs' ] = $aSettingsSubNav;
|
169 |
-
}
|
170 |
-
|
171 |
-
$DP = Services::DataManipulation();
|
172 |
-
$aData = $DP->mergeArraysRecursive(
|
173 |
-
$this->getUIHandler()->getBaseDisplayData(),
|
174 |
-
[
|
175 |
-
'classes' => [
|
176 |
-
'page_container' => 'page-insights page-'.$sNavSection
|
177 |
-
],
|
178 |
-
'flags' => [
|
179 |
-
'show_promo' => !$bIsPro && ( $sNavSection != 'settings' ),
|
180 |
-
'show_guided_tour' => $oModPlugin->getIfShowIntroVideo(),
|
181 |
-
'tours' => [
|
182 |
-
'insights_overview' => $oTourManager->canShow( 'insights_overview' )
|
183 |
-
]
|
184 |
-
],
|
185 |
-
'hrefs' => [
|
186 |
-
'go_pro' => 'https://shsec.io/shieldgoprofeature',
|
187 |
-
'nav_home' => $this->getUrl_AdminPage(),
|
188 |
-
'top_nav' => $aTopNav,
|
189 |
-
'img_banner' => $con->getPluginUrl_Image( 'pluginlogo_banner-170x40.png' )
|
190 |
-
],
|
191 |
-
'strings' => $this->getStrings()->getDisplayStrings(),
|
192 |
-
'vars' => [
|
193 |
-
'changelog_id' => $con->getPluginSpec()[ 'meta' ][ 'announcekit_changelog_id' ],
|
194 |
-
'search_select' => $aSearchSelect
|
195 |
-
],
|
196 |
-
],
|
197 |
-
$aData
|
198 |
-
);
|
199 |
-
return $this->renderTemplate(
|
200 |
-
sprintf( '/wpadmin_pages/insights/%s/index.twig', $sNavSection ),
|
201 |
-
$aData,
|
202 |
-
true
|
203 |
-
);
|
204 |
}
|
205 |
|
206 |
public function insertCustomJsVars_Admin() {
|
@@ -210,11 +44,11 @@ class ICWP_WPSF_FeatureHandler_Insights extends ICWP_WPSF_FeatureHandler_BaseWps
|
|
210 |
|
211 |
$con = $this->getCon();
|
212 |
$aStdDepsJs = [ $con->prefix( 'plugin' ) ];
|
213 |
-
$
|
214 |
|
215 |
$oModPlugin = $con->getModule_Plugin();
|
216 |
$oTourManager = $oModPlugin->getTourManager();
|
217 |
-
switch ( $
|
218 |
|
219 |
case 'importexport':
|
220 |
|
@@ -279,11 +113,12 @@ class ICWP_WPSF_FeatureHandler_Insights extends ICWP_WPSF_FeatureHandler_BaseWps
|
|
279 |
$this->includeScriptIpDetect();
|
280 |
break;
|
281 |
|
|
|
282 |
case 'scans':
|
283 |
case 'audit':
|
|
|
284 |
case 'ips':
|
285 |
case 'debug':
|
286 |
-
case 'traffic':
|
287 |
case 'users':
|
288 |
|
289 |
$sAsset = 'shield-tables';
|
@@ -298,7 +133,7 @@ class ICWP_WPSF_FeatureHandler_Insights extends ICWP_WPSF_FeatureHandler_BaseWps
|
|
298 |
wp_enqueue_script( $sUnique );
|
299 |
|
300 |
$aStdDepsJs[] = $sUnique;
|
301 |
-
if ( $
|
302 |
$sAsset = 'shield-scans';
|
303 |
$sUnique = $con->prefix( $sAsset );
|
304 |
wp_register_script(
|
@@ -311,7 +146,7 @@ class ICWP_WPSF_FeatureHandler_Insights extends ICWP_WPSF_FeatureHandler_BaseWps
|
|
311 |
wp_enqueue_script( $sUnique );
|
312 |
}
|
313 |
|
314 |
-
if ( $
|
315 |
$sAsset = 'shield/ipanalyse';
|
316 |
$sUnique = $con->prefix( $sAsset );
|
317 |
wp_register_script(
|
@@ -324,7 +159,7 @@ class ICWP_WPSF_FeatureHandler_Insights extends ICWP_WPSF_FeatureHandler_BaseWps
|
|
324 |
wp_enqueue_script( $sUnique );
|
325 |
}
|
326 |
|
327 |
-
if ( $
|
328 |
$sUnique = $con->prefix( 'datepicker' );
|
329 |
wp_register_script(
|
330 |
$sUnique, //TODO: use an includes services for CNDJS
|
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() {
|
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() {
|
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 |
|
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';
|
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(
|
146 |
wp_enqueue_script( $sUnique );
|
147 |
}
|
148 |
|
149 |
+
if ( $iNav == 'ips' ) {
|
150 |
$sAsset = 'shield/ipanalyse';
|
151 |
$sUnique = $con->prefix( $sAsset );
|
152 |
wp_register_script(
|
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
|
src/features/ips.php
CHANGED
@@ -4,6 +4,9 @@ use FernleafSystems\Wordpress\Plugin\Shield;
|
|
4 |
use FernleafSystems\Wordpress\Plugin\Shield\Modules\IPs;
|
5 |
use FernleafSystems\Wordpress\Services\Services;
|
6 |
|
|
|
|
|
|
|
7 |
class ICWP_WPSF_FeatureHandler_Ips extends ICWP_WPSF_FeatureHandler_BaseWpsf {
|
8 |
|
9 |
const LIST_MANUAL_WHITE = 'MW';
|
@@ -138,56 +141,15 @@ class ICWP_WPSF_FeatureHandler_Ips extends ICWP_WPSF_FeatureHandler_BaseWpsf {
|
|
138 |
}
|
139 |
|
140 |
/**
|
141 |
-
*
|
142 |
*/
|
143 |
-
|
144 |
-
|
145 |
-
$this->addFilterIpsToWhiteList();
|
146 |
-
}
|
147 |
-
parent::onPluginShutdown();
|
148 |
-
}
|
149 |
-
|
150 |
-
protected function addFilterIpsToWhiteList() {
|
151 |
-
$aIps = [];
|
152 |
-
$oSp = Services::ServiceProviders();
|
153 |
-
|
154 |
-
if ( @class_exists( '\MwpWorkerResponder' ) ) {
|
155 |
-
foreach ( array_flip( $oSp->getIps_ManageWp( true ) ) as $sIp => $n ) {
|
156 |
-
$aIps[ $sIp ] = 'ManageWP';
|
157 |
-
}
|
158 |
-
}
|
159 |
-
|
160 |
-
if ( class_exists( 'ICWP_Plugin' ) ) {
|
161 |
-
foreach ( array_flip( $oSp->getIps_iControlWP( true ) ) as $sIp => $n ) {
|
162 |
-
$aIps[ $sIp ] = 'iControlWP';
|
163 |
-
}
|
164 |
-
}
|
165 |
-
|
166 |
-
$aIps = apply_filters( 'icwp_simple_firewall_whitelist_ips', $aIps );
|
167 |
-
|
168 |
-
if ( !empty( $aIps ) && is_array( $aIps ) ) {
|
169 |
-
$aWhiteIps = ( new IPs\Lib\Ops\RetrieveIpsForLists() )
|
170 |
-
->setDbHandler( $this->getDbHandler_IPs() )
|
171 |
-
->white();
|
172 |
-
foreach ( $aIps as $sIP => $sLabel ) {
|
173 |
-
if ( !in_array( $sIP, $aWhiteIps ) ) {
|
174 |
-
try {
|
175 |
-
( new IPs\Lib\Ops\AddIp() )
|
176 |
-
->setMod( $this )
|
177 |
-
->setIP( $sIP )
|
178 |
-
->toManualWhitelist( $sLabel );
|
179 |
-
}
|
180 |
-
catch ( Exception $oE ) {
|
181 |
-
}
|
182 |
-
}
|
183 |
-
}
|
184 |
-
}
|
185 |
}
|
186 |
|
187 |
/**
|
188 |
-
* @
|
189 |
*/
|
190 |
-
protected function
|
191 |
-
return 'IPs';
|
192 |
}
|
193 |
}
|
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';
|
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
CHANGED
@@ -4,6 +4,9 @@ use FernleafSystems\Wordpress\Plugin\Shield;
|
|
4 |
use FernleafSystems\Wordpress\Plugin\Shield\Modules\License;
|
5 |
use FernleafSystems\Wordpress\Services\Services;
|
6 |
|
|
|
|
|
|
|
7 |
class ICWP_WPSF_FeatureHandler_License extends ICWP_WPSF_FeatureHandler_BaseWpsf {
|
8 |
|
9 |
/**
|
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 |
/**
|
src/features/lockdown.php
CHANGED
@@ -2,6 +2,9 @@
|
|
2 |
|
3 |
use FernleafSystems\Wordpress\Plugin\Shield;
|
4 |
|
|
|
|
|
|
|
5 |
class ICWP_WPSF_FeatureHandler_Lockdown extends ICWP_WPSF_FeatureHandler_BaseWpsf {
|
6 |
|
7 |
/**
|
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 |
/**
|
src/features/login_protect.php
CHANGED
@@ -4,6 +4,9 @@ use FernleafSystems\Wordpress\Plugin\Shield;
|
|
4 |
use FernleafSystems\Wordpress\Plugin\Shield\Modules\LoginGuard;
|
5 |
use FernleafSystems\Wordpress\Services\Services;
|
6 |
|
|
|
|
|
|
|
7 |
class ICWP_WPSF_FeatureHandler_LoginProtect extends ICWP_WPSF_FeatureHandler_BaseWpsf {
|
8 |
|
9 |
/**
|
@@ -286,18 +289,7 @@ class ICWP_WPSF_FeatureHandler_LoginProtect extends ICWP_WPSF_FeatureHandler_Bas
|
|
286 |
wp_enqueue_style( 'wp-jquery-ui-dialog' );
|
287 |
}
|
288 |
|
289 |
-
/**
|
290 |
-
* @return string
|
291 |
-
*/
|
292 |
protected function getNamespaceBase() :string {
|
293 |
return 'LoginGuard';
|
294 |
}
|
295 |
-
|
296 |
-
/**
|
297 |
-
* @return string
|
298 |
-
* @deprecated 10.0
|
299 |
-
*/
|
300 |
-
public function getCustomLoginPath() {
|
301 |
-
return $this->getOptions()->getOpt( 'rename_wplogin_path', '' );
|
302 |
-
}
|
303 |
}
|
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 |
/**
|
289 |
wp_enqueue_style( 'wp-jquery-ui-dialog' );
|
290 |
}
|
291 |
|
|
|
|
|
|
|
292 |
protected function getNamespaceBase() :string {
|
293 |
return 'LoginGuard';
|
294 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
295 |
}
|
src/features/plugin.php
CHANGED
@@ -5,6 +5,9 @@ use FernleafSystems\Wordpress\Plugin\Shield\Modules\Plugin;
|
|
5 |
use FernleafSystems\Wordpress\Services\Services;
|
6 |
use FernleafSystems\Wordpress\Services\Utilities;
|
7 |
|
|
|
|
|
|
|
8 |
class ICWP_WPSF_FeatureHandler_Plugin extends ICWP_WPSF_FeatureHandler_BaseWpsf {
|
9 |
|
10 |
/**
|
@@ -205,6 +208,10 @@ class ICWP_WPSF_FeatureHandler_Plugin extends ICWP_WPSF_FeatureHandler_BaseWpsf
|
|
205 |
return (int)$this->getOptions()->getOpt( 'installation_time', 0 );
|
206 |
}
|
207 |
|
|
|
|
|
|
|
|
|
208 |
/**
|
209 |
* @return string
|
210 |
*/
|
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 |
/**
|
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 |
*/
|
src/features/reporting.php
CHANGED
@@ -3,6 +3,9 @@
|
|
3 |
use FernleafSystems\Wordpress\Plugin\Shield;
|
4 |
use FernleafSystems\Wordpress\Plugin\Shield\Modules\Reporting;
|
5 |
|
|
|
|
|
|
|
6 |
class ICWP_WPSF_FeatureHandler_Reporting extends ICWP_WPSF_FeatureHandler_BaseWpsf {
|
7 |
|
8 |
/**
|
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 |
/**
|
src/features/sessions.php
CHANGED
@@ -3,6 +3,9 @@
|
|
3 |
use FernleafSystems\Wordpress\Plugin\Shield;
|
4 |
use FernleafSystems\Wordpress\Services\Services;
|
5 |
|
|
|
|
|
|
|
6 |
class ICWP_WPSF_FeatureHandler_Sessions extends ICWP_WPSF_FeatureHandler_BaseWpsf {
|
7 |
|
8 |
/**
|
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 |
/**
|
src/features/traffic.php
CHANGED
@@ -4,6 +4,9 @@ use FernleafSystems\Wordpress\Plugin\Shield;
|
|
4 |
use FernleafSystems\Wordpress\Plugin\Shield\Modules\Traffic;
|
5 |
use FernleafSystems\Wordpress\Services\Services;
|
6 |
|
|
|
|
|
|
|
7 |
class ICWP_WPSF_FeatureHandler_Traffic extends ICWP_WPSF_FeatureHandler_BaseWpsf {
|
8 |
|
9 |
/**
|
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 |
/**
|
src/features/user_management.php
CHANGED
@@ -4,6 +4,9 @@ use FernleafSystems\Wordpress\Plugin\Shield;
|
|
4 |
use FernleafSystems\Wordpress\Plugin\Shield\Modules\UserManagement;
|
5 |
use FernleafSystems\Wordpress\Services\Services;
|
6 |
|
|
|
|
|
|
|
7 |
class ICWP_WPSF_FeatureHandler_UserManagement extends \ICWP_WPSF_FeatureHandler_BaseWpsf {
|
8 |
|
9 |
/**
|
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 |
/**
|
src/lib/src/Controller/Controller.php
CHANGED
@@ -9,19 +9,20 @@ use FernleafSystems\Wordpress\Services\Services;
|
|
9 |
/**
|
10 |
* Class Controller
|
11 |
* @package FernleafSystems\Wordpress\Plugin\Shield\Controller
|
12 |
-
* @property bool
|
13 |
-
* @property bool
|
14 |
-
* @property bool
|
15 |
-
* @property bool
|
16 |
-
* @property
|
17 |
-
* @property bool
|
18 |
-
* @property bool
|
19 |
-
* @property
|
20 |
-
* @property string
|
21 |
-
* @property string
|
22 |
-
* @property
|
23 |
-
* @property
|
24 |
-
* @property
|
|
|
25 |
*/
|
26 |
class Controller {
|
27 |
|
@@ -41,6 +42,7 @@ class Controller {
|
|
41 |
|
42 |
/**
|
43 |
* @var string
|
|
|
44 |
*/
|
45 |
private $sRootFile;
|
46 |
|
@@ -51,6 +53,7 @@ class Controller {
|
|
51 |
|
52 |
/**
|
53 |
* @var string
|
|
|
54 |
*/
|
55 |
private $sPluginBaseFile;
|
56 |
|
@@ -69,11 +72,6 @@ class Controller {
|
|
69 |
*/
|
70 |
protected static $sRequestId;
|
71 |
|
72 |
-
/**
|
73 |
-
* @var string
|
74 |
-
*/
|
75 |
-
private $sConfigOptionsHashWhenLoaded;
|
76 |
-
|
77 |
/**
|
78 |
* @var bool
|
79 |
*/
|
@@ -100,12 +98,12 @@ class Controller {
|
|
100 |
private $oEventsService;
|
101 |
|
102 |
/**
|
103 |
-
* @param string $
|
104 |
-
* @param array $
|
105 |
* @return $this
|
106 |
*/
|
107 |
-
public function fireEvent( $
|
108 |
-
$this->loadEventsService()->fireEvent( $
|
109 |
return $this;
|
110 |
}
|
111 |
|
@@ -129,25 +127,28 @@ class Controller {
|
|
129 |
}
|
130 |
|
131 |
/**
|
132 |
-
* @param string $
|
133 |
* @return Controller
|
134 |
* @throws \Exception
|
135 |
*/
|
136 |
-
public static function GetInstance( $
|
137 |
if ( !isset( static::$oInstance ) ) {
|
138 |
-
|
|
|
|
|
|
|
139 |
}
|
140 |
return static::$oInstance;
|
141 |
}
|
142 |
|
143 |
/**
|
144 |
-
* @param string $
|
145 |
* @throws \Exception
|
146 |
*/
|
147 |
-
protected function __construct( $
|
148 |
-
$this->sRootFile = $
|
149 |
-
$this->root_file = $
|
150 |
-
$this->base_file = $this->
|
151 |
$this->modules = [];
|
152 |
|
153 |
$this->loadServices();
|
@@ -195,16 +196,16 @@ class Controller {
|
|
195 |
* @return array
|
196 |
* @throws \Exception
|
197 |
*/
|
198 |
-
private function readPluginSpecification() {
|
199 |
-
$
|
200 |
-
$
|
201 |
-
if ( !empty( $
|
202 |
-
$
|
203 |
-
if ( empty( $
|
204 |
-
throw new \Exception( 'Could not load
|
205 |
}
|
206 |
}
|
207 |
-
return $
|
208 |
}
|
209 |
|
210 |
/**
|
@@ -538,10 +539,10 @@ class Controller {
|
|
538 |
}
|
539 |
|
540 |
public function onWpDashboardSetup() {
|
541 |
-
$
|
542 |
$this->isValidAdminArea() && (bool)$this->getPluginSpec_Property( 'show_dashboard_widget' )
|
543 |
);
|
544 |
-
if ( $
|
545 |
wp_add_dashboard_widget(
|
546 |
$this->prefix( 'dashboard_widget' ),
|
547 |
apply_filters( $this->prefix( 'dashboard_widget_title' ), $this->getHumanName() ),
|
@@ -580,29 +581,29 @@ class Controller {
|
|
580 |
}
|
581 |
|
582 |
public function ajaxAction() {
|
583 |
-
$
|
584 |
-
check_ajax_referer( $
|
585 |
|
586 |
ob_start();
|
587 |
-
$
|
588 |
$this->prefix( Services::WpUsers()->isUserLoggedIn() ? 'ajaxAuthAction' : 'ajaxNonAuthAction' ),
|
589 |
-
[], $
|
590 |
);
|
591 |
-
$
|
592 |
|
593 |
-
if ( is_array( $
|
594 |
-
$bSuccess = $
|
595 |
}
|
596 |
else {
|
597 |
$bSuccess = false;
|
598 |
-
$
|
599 |
}
|
600 |
|
601 |
wp_send_json(
|
602 |
[
|
603 |
'success' => $bSuccess,
|
604 |
-
'data' => $
|
605 |
-
'noise' => $
|
606 |
]
|
607 |
);
|
608 |
}
|
@@ -887,28 +888,28 @@ class Controller {
|
|
887 |
/**
|
888 |
* This will hook into the saving of plugin update information and if there is an update for this plugin, it'll add
|
889 |
* a data stamp to state when the update was first detected.
|
890 |
-
* @param \stdClass $
|
891 |
* @return \stdClass
|
892 |
*/
|
893 |
-
public function setUpdateFirstDetectedAt( $
|
894 |
|
895 |
-
if ( !empty( $
|
896 |
-
&& isset( $
|
897 |
// i.e. there's an update available
|
898 |
|
899 |
-
$
|
900 |
-
if ( !empty( $
|
901 |
-
$
|
902 |
-
if ( !isset( $
|
903 |
-
$
|
904 |
}
|
905 |
-
if ( !isset( $
|
906 |
-
$
|
907 |
}
|
908 |
}
|
909 |
}
|
910 |
|
911 |
-
return $
|
912 |
}
|
913 |
|
914 |
/**
|
@@ -985,27 +986,21 @@ class Controller {
|
|
985 |
return $aPlugins;
|
986 |
}
|
987 |
|
988 |
-
|
989 |
-
* @return array
|
990 |
-
*/
|
991 |
-
public function getLabels() {
|
992 |
|
993 |
-
$
|
994 |
|
995 |
$oDP = Services::Data();
|
996 |
-
foreach ( [ '16x16', '32x32', '128x128' ] as $
|
997 |
-
$
|
998 |
-
if ( !empty( $
|
999 |
-
$
|
1000 |
}
|
1001 |
}
|
1002 |
|
1003 |
-
return $
|
1004 |
}
|
1005 |
|
1006 |
-
/**
|
1007 |
-
* Hooked to 'shutdown'
|
1008 |
-
*/
|
1009 |
public function onWpShutdown() {
|
1010 |
$this->getSiteInstallationId();
|
1011 |
do_action( $this->prefix( 'pre_plugin_shutdown' ) );
|
@@ -1021,12 +1016,12 @@ class Controller {
|
|
1021 |
}
|
1022 |
|
1023 |
protected function deleteFlags() {
|
1024 |
-
$
|
1025 |
-
if ( $
|
1026 |
-
$
|
1027 |
}
|
1028 |
if ( $this->getIsResetPlugin() ) {
|
1029 |
-
$
|
1030 |
}
|
1031 |
}
|
1032 |
|
@@ -1151,8 +1146,8 @@ class Controller {
|
|
1151 |
* @return mixed|null
|
1152 |
*/
|
1153 |
protected function getPluginSpec_Property( string $key ) {
|
1154 |
-
$
|
1155 |
-
return $
|
1156 |
}
|
1157 |
|
1158 |
/**
|
@@ -1231,8 +1226,8 @@ class Controller {
|
|
1231 |
* @return string
|
1232 |
*/
|
1233 |
public function getHumanName() {
|
1234 |
-
$
|
1235 |
-
return empty( $
|
1236 |
}
|
1237 |
|
1238 |
/**
|
@@ -1249,49 +1244,30 @@ class Controller {
|
|
1249 |
return ( strpos( Services::WpGeneral()->getCurrentWpAdminPage(), $this->getPluginPrefix() ) === 0 );
|
1250 |
}
|
1251 |
|
1252 |
-
|
1253 |
-
|
1254 |
-
*/
|
1255 |
-
public function getIsPage_PluginMainDashboard() {
|
1256 |
-
return ( Services::WpGeneral()->getCurrentWpAdminPage() == $this->getPluginPrefix() );
|
1257 |
}
|
1258 |
|
1259 |
-
|
1260 |
-
|
1261 |
-
|
1262 |
-
public function getIsRebuildOptionsFromFile() {
|
1263 |
-
if ( isset( $this->bRebuildOptions ) ) {
|
1264 |
-
return $this->bRebuildOptions;
|
1265 |
}
|
1266 |
|
1267 |
// The first choice is to look for the file hash. If it's "always" empty, it means we could never
|
1268 |
// hash the file in the first place so it's not ever effectively used and it falls back to the rebuild file
|
1269 |
-
$
|
1270 |
-
$
|
1271 |
-
$
|
1272 |
-
$
|
1273 |
-
|
1274 |
-
$this->bRebuildOptions = true;
|
1275 |
|
1276 |
-
|
1277 |
-
|
1278 |
-
$
|
1279 |
-
|
1280 |
-
elseif ( isset( $oConOptions->mod_time ) && ( $sModifiedTime < $oConOptions->mod_time ) ) {
|
1281 |
-
$this->bRebuildOptions = false;
|
1282 |
-
}
|
1283 |
|
1284 |
-
$
|
1285 |
-
$
|
1286 |
-
$this->rebuild_options
|
1287 |
-
return $this->bRebuildOptions;
|
1288 |
-
}
|
1289 |
-
|
1290 |
-
/**
|
1291 |
-
* @return bool
|
1292 |
-
*/
|
1293 |
-
public function isUpgrading() {
|
1294 |
-
return $this->getIsRebuildOptionsFromFile();
|
1295 |
}
|
1296 |
|
1297 |
public function getIsResetPlugin() :bool {
|
@@ -1308,37 +1284,23 @@ class Controller {
|
|
1308 |
return $this->getPluginSpec_Property( 'wpms_network_admin_only' );
|
1309 |
}
|
1310 |
|
1311 |
-
|
1312 |
-
* @return string
|
1313 |
-
*/
|
1314 |
-
public function getParentSlug() {
|
1315 |
return $this->getPluginSpec_Property( 'slug_parent' );
|
1316 |
}
|
1317 |
|
1318 |
-
|
1319 |
-
|
1320 |
-
|
1321 |
-
*/
|
1322 |
-
public function getPluginBaseFile() {
|
1323 |
-
if ( !isset( $this->sPluginBaseFile ) ) {
|
1324 |
-
$this->sPluginBaseFile = plugin_basename( $this->getRootFile() );
|
1325 |
}
|
1326 |
-
return $this->
|
1327 |
}
|
1328 |
|
1329 |
-
|
1330 |
-
* @return string
|
1331 |
-
*/
|
1332 |
-
public function getPluginSlug() {
|
1333 |
return $this->getPluginSpec_Property( 'slug_plugin' );
|
1334 |
}
|
1335 |
|
1336 |
-
|
1337 |
-
|
1338 |
-
* @return string
|
1339 |
-
*/
|
1340 |
-
public function getPluginUrl( $sPath = '' ) {
|
1341 |
-
return add_query_arg( [ 'ver' => $this->getVersion() ], plugins_url( $sPath, $this->getRootFile() ) );
|
1342 |
}
|
1343 |
|
1344 |
public function getPluginUrl_Asset( string $asset ) :string {
|
@@ -1440,29 +1402,22 @@ class Controller {
|
|
1440 |
return path_join( $this->getRootDir(), 'plugin-spec.php' );
|
1441 |
}
|
1442 |
|
1443 |
-
|
1444 |
-
* Get the root directory for the plugin with the trailing slash
|
1445 |
-
* @return string
|
1446 |
-
*/
|
1447 |
-
public function getRootDir() {
|
1448 |
return dirname( $this->getRootFile() ).DIRECTORY_SEPARATOR;
|
1449 |
}
|
1450 |
|
1451 |
-
|
1452 |
-
|
1453 |
-
|
1454 |
-
public function getRootFile() {
|
1455 |
-
if ( empty( $this->sRootFile ) ) {
|
1456 |
-
$oVO = ( new \FernleafSystems\Wordpress\Services\Utilities\WpOrg\Plugin\Files() )
|
1457 |
->findPluginFromFile( __FILE__ );
|
1458 |
-
if ( $
|
1459 |
-
$this->
|
1460 |
}
|
1461 |
else {
|
1462 |
-
$this->
|
1463 |
}
|
1464 |
}
|
1465 |
-
return $this->
|
1466 |
}
|
1467 |
|
1468 |
/**
|
@@ -1498,20 +1453,14 @@ class Controller {
|
|
1498 |
return $opts->previous_version;
|
1499 |
}
|
1500 |
|
1501 |
-
|
1502 |
-
|
1503 |
-
|
1504 |
-
public function getVersionNumeric() {
|
1505 |
-
$aParts = explode( '.', $this->getVersion() );
|
1506 |
-
return ( $aParts[ 0 ]*100 + $aParts[ 1 ]*10 + $aParts[ 2 ] );
|
1507 |
}
|
1508 |
|
1509 |
-
|
1510 |
-
|
1511 |
-
|
1512 |
-
public function getShieldAction() {
|
1513 |
-
$sAction = sanitize_key( Services::Request()->query( 'shield_action', '' ) );
|
1514 |
-
return empty( $sAction ) ? '' : $sAction;
|
1515 |
}
|
1516 |
|
1517 |
/**
|
@@ -1525,11 +1474,6 @@ class Controller {
|
|
1525 |
self::$oControllerOptions = new \stdClass();
|
1526 |
}
|
1527 |
|
1528 |
-
// Used at the time of saving during WP Shutdown to determine whether saving is necessary. TODO: Extend to plugin options
|
1529 |
-
if ( empty( $this->sConfigOptionsHashWhenLoaded ) ) {
|
1530 |
-
$this->sConfigOptionsHashWhenLoaded = md5( serialize( self::$oControllerOptions ) );
|
1531 |
-
}
|
1532 |
-
|
1533 |
if ( $this->getIsRebuildOptionsFromFile() ) {
|
1534 |
self::$oControllerOptions->plugin_spec = $this->readPluginSpecification();
|
1535 |
}
|
@@ -1569,16 +1513,16 @@ class Controller {
|
|
1569 |
}
|
1570 |
|
1571 |
protected function saveCurrentPluginControllerOptions() {
|
1572 |
-
$
|
1573 |
add_filter( $this->prefix( 'bypass_is_plugin_admin' ), '__return_true' );
|
1574 |
if ( $this->plugin_deleting ) {
|
1575 |
-
$
|
1576 |
}
|
1577 |
else {
|
1578 |
-
$
|
1579 |
-
|
1580 |
-
$
|
1581 |
-
|
1582 |
}
|
1583 |
remove_filter( $this->prefix( 'bypass_is_plugin_admin' ), '__return_true' );
|
1584 |
}
|
@@ -1688,7 +1632,7 @@ class Controller {
|
|
1688 |
|
1689 |
/**
|
1690 |
* We let the \Exception from the core plugin feature to bubble up because it's critical.
|
1691 |
-
* @return \
|
1692 |
* @throws \Exception from loadFeatureHandler()
|
1693 |
*/
|
1694 |
public function loadCorePluginFeatureHandler() {
|
@@ -1710,15 +1654,13 @@ class Controller {
|
|
1710 |
* @throws \Exception
|
1711 |
*/
|
1712 |
public function loadAllFeatures() :bool {
|
1713 |
-
$
|
1714 |
-
foreach ( array_keys( $this->loadCorePluginFeatureHandler()->getActivePluginFeatures() ) as $sSlug ) {
|
1715 |
try {
|
1716 |
-
$this->getModule( $
|
1717 |
-
$bSuccess = true;
|
1718 |
}
|
1719 |
-
catch ( \Exception $
|
1720 |
if ( $this->isValidAdminArea() && $this->isPluginAdmin() ) {
|
1721 |
-
$this->sAdminNoticeError = $
|
1722 |
add_action( 'admin_notices', [ $this, 'adminNoticePluginFailedToLoad' ] );
|
1723 |
add_action( 'network_admin_notices', [ $this, 'adminNoticePluginFailedToLoad' ] );
|
1724 |
}
|
@@ -1756,102 +1698,118 @@ class Controller {
|
|
1756 |
return $mod;
|
1757 |
}
|
1758 |
|
1759 |
-
public function getModule_AuditTrail()
|
1760 |
return $this->getModule( 'audit_trail' );
|
1761 |
}
|
1762 |
|
1763 |
-
public function getModule_Comments()
|
1764 |
return $this->getModule( 'comments_filter' );
|
1765 |
}
|
1766 |
|
1767 |
-
public function getModule_Comms()
|
1768 |
return $this->getModule( 'comms' );
|
1769 |
}
|
1770 |
|
1771 |
-
public function
|
|
|
|
|
|
|
|
|
1772 |
return $this->getModule( 'events' );
|
1773 |
}
|
1774 |
|
1775 |
-
public function getModule_HackGuard()
|
1776 |
return $this->getModule( 'hack_protect' );
|
1777 |
}
|
1778 |
|
1779 |
-
public function getModule_Insights()
|
1780 |
return $this->getModule( 'insights' );
|
1781 |
}
|
1782 |
|
1783 |
-
public function
|
|
|
|
|
|
|
|
|
1784 |
return $this->getModule( 'ips' );
|
1785 |
}
|
1786 |
|
1787 |
-
public function getModule_License()
|
1788 |
return $this->getModule( 'license' );
|
1789 |
}
|
1790 |
|
1791 |
-
public function getModule_LoginGuard()
|
1792 |
return $this->getModule( 'login_protect' );
|
1793 |
}
|
1794 |
|
1795 |
-
public function getModule_Plugin()
|
1796 |
return $this->getModule( 'plugin' );
|
1797 |
}
|
1798 |
|
1799 |
-
public function getModule_Reporting()
|
1800 |
return $this->getModule( 'reporting' );
|
1801 |
}
|
1802 |
|
1803 |
-
public function getModule_SecAdmin()
|
1804 |
return $this->getModule( 'admin_access_restriction' );
|
1805 |
}
|
1806 |
|
1807 |
-
public function getModule_Sessions()
|
1808 |
return $this->getModule( 'sessions' );
|
1809 |
}
|
1810 |
|
1811 |
-
public function getModule_Traffic()
|
1812 |
return $this->getModule( 'traffic' );
|
1813 |
}
|
1814 |
|
1815 |
-
public function getModule_UserManagement()
|
1816 |
return $this->getModule( 'user_management' );
|
1817 |
}
|
1818 |
|
|
|
|
|
|
|
|
|
1819 |
/**
|
1820 |
-
* @param array $
|
1821 |
* @return \ICWP_WPSF_FeatureHandler_Base|mixed
|
1822 |
* @throws \Exception
|
1823 |
*/
|
1824 |
-
public function loadFeatureHandler( array $
|
1825 |
-
$modSlug = $
|
1826 |
$mod = isset( $this->modules[ $modSlug ] ) ? $this->modules[ $modSlug ] : null;
|
1827 |
-
if ( $mod instanceof \ICWP_WPSF_FeatureHandler_Base ) {
|
1828 |
return $mod;
|
1829 |
}
|
1830 |
|
1831 |
-
if ( empty( $
|
1832 |
-
$
|
|
|
|
|
|
|
1833 |
}
|
1834 |
|
1835 |
-
if ( !empty( $
|
1836 |
-
&& !Services::Data()->getPhpVersionIsAtLeast( $
|
1837 |
return null;
|
1838 |
}
|
1839 |
|
1840 |
-
$
|
1841 |
-
$sOptionsVarName = sprintf( 'oFeatureHandler%s', $
|
1842 |
|
1843 |
-
|
1844 |
-
|
|
|
|
|
1845 |
|
1846 |
// All this to prevent fatal errors if the plugin doesn't install/upgrade correctly
|
1847 |
-
if ( class_exists( $
|
1848 |
-
$
|
1849 |
-
}
|
1850 |
-
else {
|
1851 |
-
$sMessage = sprintf( 'Class "%s" is missing', $sClassName );
|
1852 |
throw new \Exception( $sMessage );
|
1853 |
}
|
1854 |
|
|
|
|
|
1855 |
$aMs = $this->modules;
|
1856 |
$aMs[ $modSlug ] = $this->{$sOptionsVarName};
|
1857 |
$this->modules = $aMs;
|
@@ -1904,18 +1862,6 @@ class Controller {
|
|
1904 |
return $oRndr;
|
1905 |
}
|
1906 |
|
1907 |
-
/**
|
1908 |
-
* Path of format -
|
1909 |
-
* wp-content/languages/plugins/wp-simple-firewall-de_DE.mo
|
1910 |
-
* @param string $sMoFilePath
|
1911 |
-
* @param string $sDomain
|
1912 |
-
* @return string
|
1913 |
-
* @deprecated 10.0
|
1914 |
-
*/
|
1915 |
-
public function overrideTranslations( $sMoFilePath, $sDomain ) {
|
1916 |
-
return $sMoFilePath;
|
1917 |
-
}
|
1918 |
-
|
1919 |
/**
|
1920 |
* @param array[] $aRegistered
|
1921 |
* @return array[]
|
9 |
/**
|
10 |
* Class Controller
|
11 |
* @package FernleafSystems\Wordpress\Plugin\Shield\Controller
|
12 |
+
* @property bool $is_activating
|
13 |
+
* @property bool $is_debug
|
14 |
+
* @property bool $modules_loaded
|
15 |
+
* @property bool $rebuild_options
|
16 |
+
* @property Shield\Modules\Integrations\Lib\MainWP\Common\MainWPVO $mwpVO
|
17 |
+
* @property bool $plugin_deactivating
|
18 |
+
* @property bool $plugin_deleting
|
19 |
+
* @property bool $plugin_reset
|
20 |
+
* @property false|string $file_forceoff
|
21 |
+
* @property string $base_file
|
22 |
+
* @property string $root_file
|
23 |
+
* @property bool $user_can_base_permissions
|
24 |
+
* @property Shield\Modules\Events\Lib\EventsService $service_events
|
25 |
+
* @property mixed[]|Shield\Modules\Base\ModCon[] $modules
|
26 |
*/
|
27 |
class Controller {
|
28 |
|
42 |
|
43 |
/**
|
44 |
* @var string
|
45 |
+
* @deprecated 10.1
|
46 |
*/
|
47 |
private $sRootFile;
|
48 |
|
53 |
|
54 |
/**
|
55 |
* @var string
|
56 |
+
* @deprecated 10.1
|
57 |
*/
|
58 |
private $sPluginBaseFile;
|
59 |
|
72 |
*/
|
73 |
protected static $sRequestId;
|
74 |
|
|
|
|
|
|
|
|
|
|
|
75 |
/**
|
76 |
* @var bool
|
77 |
*/
|
98 |
private $oEventsService;
|
99 |
|
100 |
/**
|
101 |
+
* @param string $event
|
102 |
+
* @param array $meta
|
103 |
* @return $this
|
104 |
*/
|
105 |
+
public function fireEvent( string $event, $meta = [] ) :self {
|
106 |
+
$this->loadEventsService()->fireEvent( $event, $meta );
|
107 |
return $this;
|
108 |
}
|
109 |
|
127 |
}
|
128 |
|
129 |
/**
|
130 |
+
* @param string $rootFile
|
131 |
* @return Controller
|
132 |
* @throws \Exception
|
133 |
*/
|
134 |
+
public static function GetInstance( $rootFile = null ) {
|
135 |
if ( !isset( static::$oInstance ) ) {
|
136 |
+
if ( empty( $rootFile ) ) {
|
137 |
+
throw new \Exception( 'Empty root file provided for instantiation' );
|
138 |
+
}
|
139 |
+
static::$oInstance = new static( $rootFile );
|
140 |
}
|
141 |
return static::$oInstance;
|
142 |
}
|
143 |
|
144 |
/**
|
145 |
+
* @param string $rootFile
|
146 |
* @throws \Exception
|
147 |
*/
|
148 |
+
protected function __construct( string $rootFile ) {
|
149 |
+
$this->sRootFile = $rootFile;
|
150 |
+
$this->root_file = $rootFile;
|
151 |
+
$this->base_file = $this->getPluginBaseFile();
|
152 |
$this->modules = [];
|
153 |
|
154 |
$this->loadServices();
|
196 |
* @return array
|
197 |
* @throws \Exception
|
198 |
*/
|
199 |
+
private function readPluginSpecification() :array {
|
200 |
+
$spec = [];
|
201 |
+
$content = Services::Data()->readFileContentsUsingInclude( $this->getPathPluginSpec() );
|
202 |
+
if ( !empty( $content ) ) {
|
203 |
+
$spec = json_decode( $content, true );
|
204 |
+
if ( empty( $spec ) || !is_array( $spec ) ) {
|
205 |
+
throw new \Exception( 'Could not load plugin spec configuration.' );
|
206 |
}
|
207 |
}
|
208 |
+
return $spec;
|
209 |
}
|
210 |
|
211 |
/**
|
539 |
}
|
540 |
|
541 |
public function onWpDashboardSetup() {
|
542 |
+
$show = apply_filters( $this->prefix( 'show_dashboard_widget' ),
|
543 |
$this->isValidAdminArea() && (bool)$this->getPluginSpec_Property( '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() ),
|
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 |
}
|
888 |
/**
|
889 |
* This will hook into the saving of plugin update information and if there is an update for this plugin, it'll add
|
890 |
* a data stamp to state when the update was first detected.
|
891 |
+
* @param \stdClass $updateData
|
892 |
* @return \stdClass
|
893 |
*/
|
894 |
+
public function setUpdateFirstDetectedAt( $updateData ) {
|
895 |
|
896 |
+
if ( !empty( $updateData ) && !empty( $updateData->response )
|
897 |
+
&& isset( $updateData->response[ $this->getPluginBaseFile() ] ) ) {
|
898 |
// i.e. there's an update available
|
899 |
|
900 |
+
$new = Services::WpPlugins()->getUpdateNewVersion( $this->getPluginBaseFile() );
|
901 |
+
if ( !empty( $new ) ) {
|
902 |
+
$opts = $this->getPluginControllerOptions();
|
903 |
+
if ( !isset( $opts->update_first_detected ) || ( count( $opts->update_first_detected ) > 3 ) ) {
|
904 |
+
$opts->update_first_detected = [];
|
905 |
}
|
906 |
+
if ( !isset( $opts->update_first_detected[ $new ] ) ) {
|
907 |
+
$opts->update_first_detected[ $new ] = Services::Request()->ts();
|
908 |
}
|
909 |
}
|
910 |
}
|
911 |
|
912 |
+
return $updateData;
|
913 |
}
|
914 |
|
915 |
/**
|
986 |
return $aPlugins;
|
987 |
}
|
988 |
|
989 |
+
public function getLabels() :array {
|
|
|
|
|
|
|
990 |
|
991 |
+
$labels = array_map( 'stripslashes', apply_filters( $this->prefix( 'plugin_labels' ), $this->getPluginSpec_Labels() ) );
|
992 |
|
993 |
$oDP = Services::Data();
|
994 |
+
foreach ( [ '16x16', '32x32', '128x128' ] as $dimension ) {
|
995 |
+
$key = 'icon_url_'.$dimension;
|
996 |
+
if ( !empty( $labels[ $key ] ) && !$oDP->isValidWebUrl( $labels[ $key ] ) ) {
|
997 |
+
$labels[ $key ] = $this->getPluginUrl_Image( $labels[ $key ] );
|
998 |
}
|
999 |
}
|
1000 |
|
1001 |
+
return $labels;
|
1002 |
}
|
1003 |
|
|
|
|
|
|
|
1004 |
public function onWpShutdown() {
|
1005 |
$this->getSiteInstallationId();
|
1006 |
do_action( $this->prefix( 'pre_plugin_shutdown' ) );
|
1016 |
}
|
1017 |
|
1018 |
protected function deleteFlags() {
|
1019 |
+
$FS = Services::WpFs();
|
1020 |
+
if ( $FS->exists( $this->getPath_Flags( 'rebuild' ) ) ) {
|
1021 |
+
$FS->deleteFile( $this->getPath_Flags( 'rebuild' ) );
|
1022 |
}
|
1023 |
if ( $this->getIsResetPlugin() ) {
|
1024 |
+
$FS->deleteFile( $this->getPath_Flags( 'reset' ) );
|
1025 |
}
|
1026 |
}
|
1027 |
|
1146 |
* @return mixed|null
|
1147 |
*/
|
1148 |
protected function getPluginSpec_Property( string $key ) {
|
1149 |
+
$data = $this->getPluginSpec()[ 'properties' ];
|
1150 |
+
return $data[ $key ] ?? null;
|
1151 |
}
|
1152 |
|
1153 |
/**
|
1226 |
* @return string
|
1227 |
*/
|
1228 |
public function getHumanName() {
|
1229 |
+
$labels = $this->getLabels();
|
1230 |
+
return empty( $labels[ 'Name' ] ) ? $this->getPluginSpec_Property( 'human_name' ) : $labels[ 'Name' ];
|
1231 |
}
|
1232 |
|
1233 |
/**
|
1244 |
return ( strpos( Services::WpGeneral()->getCurrentWpAdminPage(), $this->getPluginPrefix() ) === 0 );
|
1245 |
}
|
1246 |
|
1247 |
+
public function getIsPage_PluginMainDashboard() :bool {
|
1248 |
+
return Services::WpGeneral()->getCurrentWpAdminPage() === $this->getPluginPrefix();
|
|
|
|
|
|
|
1249 |
}
|
1250 |
|
1251 |
+
public function getIsRebuildOptionsFromFile() :bool {
|
1252 |
+
if ( isset( $this->rebuild_options ) ) {
|
1253 |
+
return $this->rebuild_options;
|
|
|
|
|
|
|
1254 |
}
|
1255 |
|
1256 |
// The first choice is to look for the file hash. If it's "always" empty, it means we could never
|
1257 |
// hash the file in the first place so it's not ever effectively used and it falls back to the rebuild file
|
1258 |
+
$opts = $this->getPluginControllerOptions();
|
1259 |
+
$specPath = $this->getPathPluginSpec();
|
1260 |
+
$specHash = @sha1_file( $specPath );
|
1261 |
+
$specModTS = Services::WpFs()->getModifiedTime( $specPath );
|
|
|
|
|
1262 |
|
1263 |
+
$this->rebuild_options =
|
1264 |
+
( $this->getVersion() !== Services::WpPlugins()->getPluginAsVo( $this->getPluginBaseFile() )->Version )
|
1265 |
+
|| ( empty( $opts->hash ) || !hash_equals( $opts->hash, $specHash ) )
|
1266 |
+
|| ( empty( $opts->mod_time || $opts->mod_time < $specModTS ) );
|
|
|
|
|
|
|
1267 |
|
1268 |
+
$opts->hash = $specHash;
|
1269 |
+
$opts->mod_time = $specModTS;
|
1270 |
+
return $this->rebuild_options;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1271 |
}
|
1272 |
|
1273 |
public function getIsResetPlugin() :bool {
|
1284 |
return $this->getPluginSpec_Property( 'wpms_network_admin_only' );
|
1285 |
}
|
1286 |
|
1287 |
+
public function getParentSlug() :string {
|
|
|
|
|
|
|
1288 |
return $this->getPluginSpec_Property( 'slug_parent' );
|
1289 |
}
|
1290 |
|
1291 |
+
public function getPluginBaseFile() :string {
|
1292 |
+
if ( !isset( $this->base_file ) ) {
|
1293 |
+
$this->base_file = plugin_basename( $this->getRootFile() );
|
|
|
|
|
|
|
|
|
1294 |
}
|
1295 |
+
return $this->base_file;
|
1296 |
}
|
1297 |
|
1298 |
+
public function getPluginSlug() :string {
|
|
|
|
|
|
|
1299 |
return $this->getPluginSpec_Property( 'slug_plugin' );
|
1300 |
}
|
1301 |
|
1302 |
+
public function getPluginUrl( string $path = '' ) :string {
|
1303 |
+
return add_query_arg( [ 'ver' => $this->getVersion() ], plugins_url( $path, $this->getRootFile() ) );
|
|
|
|
|
|
|
|
|
1304 |
}
|
1305 |
|
1306 |
public function getPluginUrl_Asset( string $asset ) :string {
|
1402 |
return path_join( $this->getRootDir(), 'plugin-spec.php' );
|
1403 |
}
|
1404 |
|
1405 |
+
public function getRootDir() :string {
|
|
|
|
|
|
|
|
|
1406 |
return dirname( $this->getRootFile() ).DIRECTORY_SEPARATOR;
|
1407 |
}
|
1408 |
|
1409 |
+
public function getRootFile() :string {
|
1410 |
+
if ( empty( $this->root_file ) ) {
|
1411 |
+
$VO = ( new \FernleafSystems\Wordpress\Services\Utilities\WpOrg\Plugin\Files() )
|
|
|
|
|
|
|
1412 |
->findPluginFromFile( __FILE__ );
|
1413 |
+
if ( $VO instanceof \FernleafSystems\Wordpress\Services\Core\VOs\WpPluginVo ) {
|
1414 |
+
$this->root_file = path_join( WP_PLUGIN_DIR, $VO->file );
|
1415 |
}
|
1416 |
else {
|
1417 |
+
$this->root_file = __FILE__;
|
1418 |
}
|
1419 |
}
|
1420 |
+
return $this->root_file;
|
1421 |
}
|
1422 |
|
1423 |
/**
|
1453 |
return $opts->previous_version;
|
1454 |
}
|
1455 |
|
1456 |
+
public function getVersionNumeric() :int {
|
1457 |
+
$parts = explode( '.', $this->getVersion() );
|
1458 |
+
return (int)( $parts[ 0 ]*100 + $parts[ 1 ]*10 + $parts[ 2 ] );
|
|
|
|
|
|
|
1459 |
}
|
1460 |
|
1461 |
+
public function getShieldAction() :string {
|
1462 |
+
$action = sanitize_key( Services::Request()->query( 'shield_action', '' ) );
|
1463 |
+
return empty( $action ) ? '' : $action;
|
|
|
|
|
|
|
1464 |
}
|
1465 |
|
1466 |
/**
|
1474 |
self::$oControllerOptions = new \stdClass();
|
1475 |
}
|
1476 |
|
|
|
|
|
|
|
|
|
|
|
1477 |
if ( $this->getIsRebuildOptionsFromFile() ) {
|
1478 |
self::$oControllerOptions->plugin_spec = $this->readPluginSpecification();
|
1479 |
}
|
1513 |
}
|
1514 |
|
1515 |
protected function saveCurrentPluginControllerOptions() {
|
1516 |
+
$WP = Services::WpGeneral();
|
1517 |
add_filter( $this->prefix( 'bypass_is_plugin_admin' ), '__return_true' );
|
1518 |
if ( $this->plugin_deleting ) {
|
1519 |
+
$WP->deleteOption( $this->getPluginControllerOptionsKey() );
|
1520 |
}
|
1521 |
else {
|
1522 |
+
$WP->updateOption(
|
1523 |
+
$this->getPluginControllerOptionsKey(),
|
1524 |
+
$this->getPluginControllerOptions()
|
1525 |
+
);
|
1526 |
}
|
1527 |
remove_filter( $this->prefix( 'bypass_is_plugin_admin' ), '__return_true' );
|
1528 |
}
|
1632 |
|
1633 |
/**
|
1634 |
* We let the \Exception from the core plugin feature to bubble up because it's critical.
|
1635 |
+
* @return Shield\Modules\Plugin\ModCon
|
1636 |
* @throws \Exception from loadFeatureHandler()
|
1637 |
*/
|
1638 |
public function loadCorePluginFeatureHandler() {
|
1654 |
* @throws \Exception
|
1655 |
*/
|
1656 |
public function loadAllFeatures() :bool {
|
1657 |
+
foreach ( array_keys( $this->loadCorePluginFeatureHandler()->getActivePluginFeatures() ) as $slug ) {
|
|
|
1658 |
try {
|
1659 |
+
$this->getModule( $slug );
|
|
|
1660 |
}
|
1661 |
+
catch ( \Exception $e ) {
|
1662 |
if ( $this->isValidAdminArea() && $this->isPluginAdmin() ) {
|
1663 |
+
$this->sAdminNoticeError = $e->getMessage();
|
1664 |
add_action( 'admin_notices', [ $this, 'adminNoticePluginFailedToLoad' ] );
|
1665 |
add_action( 'network_admin_notices', [ $this, 'adminNoticePluginFailedToLoad' ] );
|
1666 |
}
|
1698 |
return $mod;
|
1699 |
}
|
1700 |
|
1701 |
+
public function getModule_AuditTrail() :Shield\Modules\AuditTrail\ModCon {
|
1702 |
return $this->getModule( 'audit_trail' );
|
1703 |
}
|
1704 |
|
1705 |
+
public function getModule_Comments() :Shield\Modules\CommentsFilter\ModCon {
|
1706 |
return $this->getModule( 'comments_filter' );
|
1707 |
}
|
1708 |
|
1709 |
+
public function getModule_Comms() :Shield\Modules\Comms\ModCon {
|
1710 |
return $this->getModule( 'comms' );
|
1711 |
}
|
1712 |
|
1713 |
+
public function getModule_Email() :Shield\Modules\Email\ModCon {
|
1714 |
+
return $this->getModule( 'email' );
|
1715 |
+
}
|
1716 |
+
|
1717 |
+
public function getModule_Events() :Shield\Modules\Events\ModCon {
|
1718 |
return $this->getModule( 'events' );
|
1719 |
}
|
1720 |
|
1721 |
+
public function getModule_HackGuard() :Shield\Modules\HackGuard\ModCon {
|
1722 |
return $this->getModule( 'hack_protect' );
|
1723 |
}
|
1724 |
|
1725 |
+
public function getModule_Insights() :Shield\Modules\Insights\ModCon {
|
1726 |
return $this->getModule( 'insights' );
|
1727 |
}
|
1728 |
|
1729 |
+
public function getModule_Integrations() :Shield\Modules\Integrations\ModCon {
|
1730 |
+
return $this->getModule( 'integrations' );
|
1731 |
+
}
|
1732 |
+
|
1733 |
+
public function getModule_IPs() :Shield\Modules\IPs\ModCon {
|
1734 |
return $this->getModule( 'ips' );
|
1735 |
}
|
1736 |
|
1737 |
+
public function getModule_License() :Shield\Modules\License\ModCon {
|
1738 |
return $this->getModule( 'license' );
|
1739 |
}
|
1740 |
|
1741 |
+
public function getModule_LoginGuard() :Shield\Modules\LoginGuard\ModCon {
|
1742 |
return $this->getModule( 'login_protect' );
|
1743 |
}
|
1744 |
|
1745 |
+
public function getModule_Plugin() :Shield\Modules\Plugin\ModCon {
|
1746 |
return $this->getModule( 'plugin' );
|
1747 |
}
|
1748 |
|
1749 |
+
public function getModule_Reporting() :Shield\Modules\Reporting\ModCon {
|
1750 |
return $this->getModule( 'reporting' );
|
1751 |
}
|
1752 |
|
1753 |
+
public function getModule_SecAdmin() :Shield\Modules\SecurityAdmin\ModCon {
|
1754 |
return $this->getModule( 'admin_access_restriction' );
|
1755 |
}
|
1756 |
|
1757 |
+
public function getModule_Sessions() :Shield\Modules\Sessions\ModCon {
|
1758 |
return $this->getModule( 'sessions' );
|
1759 |
}
|
1760 |
|
1761 |
+
public function getModule_Traffic() :Shield\Modules\Traffic\ModCon {
|
1762 |
return $this->getModule( 'traffic' );
|
1763 |
}
|
1764 |
|
1765 |
+
public function getModule_UserManagement() :Shield\Modules\UserManagement\ModCon {
|
1766 |
return $this->getModule( 'user_management' );
|
1767 |
}
|
1768 |
|
1769 |
+
public function getModulesNamespace() :string {
|
1770 |
+
return '\FernleafSystems\Wordpress\Plugin\Shield\Modules';
|
1771 |
+
}
|
1772 |
+
|
1773 |
/**
|
1774 |
+
* @param array $modProps
|
1775 |
* @return \ICWP_WPSF_FeatureHandler_Base|mixed
|
1776 |
* @throws \Exception
|
1777 |
*/
|
1778 |
+
public function loadFeatureHandler( array $modProps ) {
|
1779 |
+
$modSlug = $modProps[ 'slug' ];
|
1780 |
$mod = isset( $this->modules[ $modSlug ] ) ? $this->modules[ $modSlug ] : null;
|
1781 |
+
if ( $mod instanceof \ICWP_WPSF_FeatureHandler_Base || $mod instanceof Shield\Modules\Base\ModCon ) {
|
1782 |
return $mod;
|
1783 |
}
|
1784 |
|
1785 |
+
if ( empty( $modProps[ 'storage_key' ] ) ) {
|
1786 |
+
$modProps[ 'storage_key' ] = $modSlug;
|
1787 |
+
}
|
1788 |
+
if ( empty( $modProps[ 'namespace' ] ) ) {
|
1789 |
+
$modProps[ 'namespace' ] = str_replace( ' ', '', ucwords( str_replace( '_', ' ', $modSlug ) ) );
|
1790 |
}
|
1791 |
|
1792 |
+
if ( !empty( $modProps[ 'min_php' ] )
|
1793 |
+
&& !Services::Data()->getPhpVersionIsAtLeast( $modProps[ 'min_php' ] ) ) {
|
1794 |
return null;
|
1795 |
}
|
1796 |
|
1797 |
+
$modName = $modProps[ 'namespace' ];
|
1798 |
+
$sOptionsVarName = sprintf( 'oFeatureHandler%s', $modName ); // e.g. oFeatureHandlerPlugin
|
1799 |
|
1800 |
+
$className = $this->getModulesNamespace().sprintf( '\\%s\\ModCon', $modName );
|
1801 |
+
if ( !@class_exists( $className ) ) {
|
1802 |
+
$className = sprintf( '%s_FeatureHandler_%s', strtoupper( $this->getPluginPrefix( '_' ) ), $modName );
|
1803 |
+
}
|
1804 |
|
1805 |
// All this to prevent fatal errors if the plugin doesn't install/upgrade correctly
|
1806 |
+
if ( !class_exists( $className ) ) {
|
1807 |
+
$sMessage = sprintf( 'Class "%s" is missing', $className );
|
|
|
|
|
|
|
1808 |
throw new \Exception( $sMessage );
|
1809 |
}
|
1810 |
|
1811 |
+
$this->{$sOptionsVarName} = new $className( $this, $modProps );
|
1812 |
+
|
1813 |
$aMs = $this->modules;
|
1814 |
$aMs[ $modSlug ] = $this->{$sOptionsVarName};
|
1815 |
$this->modules = $aMs;
|
1862 |
return $oRndr;
|
1863 |
}
|
1864 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1865 |
/**
|
1866 |
* @param array[] $aRegistered
|
1867 |
* @return array[]
|
src/lib/src/Controller/Utilities/Upgrade.php
CHANGED
@@ -16,7 +16,7 @@ class Upgrade {
|
|
16 |
foreach ( $con->modules as $mod ) {
|
17 |
$H = $mod->getUpgradeHandler();
|
18 |
if ( $H instanceof Shield\Modules\Base\Upgrade ) {
|
19 |
-
$H->
|
20 |
}
|
21 |
}
|
22 |
}
|
16 |
foreach ( $con->modules as $mod ) {
|
17 |
$H = $mod->getUpgradeHandler();
|
18 |
if ( $H instanceof Shield\Modules\Base\Upgrade ) {
|
19 |
+
$H->execute();
|
20 |
}
|
21 |
}
|
22 |
}
|
src/lib/src/Crons/PluginCronsConsumer.php
ADDED
@@ -0,0 +1,17 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php declare( strict_types=1 );
|
2 |
+
|
3 |
+
namespace FernleafSystems\Wordpress\Plugin\Shield\Crons;
|
4 |
+
|
5 |
+
trait PluginCronsConsumer {
|
6 |
+
|
7 |
+
public function runDailyCron() {
|
8 |
+
}
|
9 |
+
|
10 |
+
public function runHourlyCron() {
|
11 |
+
}
|
12 |
+
|
13 |
+
protected function setupCronHooks() {
|
14 |
+
add_action( $this->getCon()->prefix( 'daily_cron' ), [ $this, 'runDailyCron' ] );
|
15 |
+
add_action( $this->getCon()->prefix( 'hourly_cron' ), [ $this, 'runHourlyCron' ] );
|
16 |
+
}
|
17 |
+
}
|
src/lib/src/Databases/AdminNotes/Handler.php
CHANGED
@@ -1,4 +1,4 @@
|
|
1 |
-
<?php
|
2 |
|
3 |
namespace FernleafSystems\Wordpress\Plugin\Shield\Databases\AdminNotes;
|
4 |
|
1 |
+
<?php declare( strict_types=1 );
|
2 |
|
3 |
namespace FernleafSystems\Wordpress\Plugin\Shield\Databases\AdminNotes;
|
4 |
|
src/lib/src/Databases/Base/BaseQuery.php
CHANGED
@@ -210,13 +210,13 @@ abstract class BaseQuery {
|
|
210 |
}
|
211 |
|
212 |
/**
|
213 |
-
* @param int $
|
214 |
-
* @param string $
|
215 |
* @return $this
|
216 |
*/
|
217 |
-
public function filterByCreatedAt( $
|
218 |
-
if ( !preg_match( '#[^=<>]#', $
|
219 |
-
$this->addWhere( 'created_at', (int)$
|
220 |
}
|
221 |
return $this;
|
222 |
}
|
210 |
}
|
211 |
|
212 |
/**
|
213 |
+
* @param int $ts
|
214 |
+
* @param string $comparisonOp
|
215 |
* @return $this
|
216 |
*/
|
217 |
+
public function filterByCreatedAt( $ts, $comparisonOp ) {
|
218 |
+
if ( !preg_match( '#[^=<>]#', $comparisonOp ) && is_numeric( $ts ) ) {
|
219 |
+
$this->addWhere( 'created_at', (int)$ts, $comparisonOp );
|
220 |
}
|
221 |
return $this;
|
222 |
}
|
src/lib/src/Databases/Base/Handler.php
CHANGED
@@ -248,62 +248,4 @@ abstract class Handler {
|
|
248 |
unset( $this->bIsReady );
|
249 |
return $this;
|
250 |
}
|
251 |
-
|
252 |
-
/**
|
253 |
-
* @return string[]
|
254 |
-
* @deprecated 10.0
|
255 |
-
*/
|
256 |
-
public function enumerateColumns() :array {
|
257 |
-
return $this->getTableSchema()->enumerateColumns();
|
258 |
-
}
|
259 |
-
|
260 |
-
/**
|
261 |
-
* @return bool
|
262 |
-
* @throws \Exception
|
263 |
-
* @deprecated 10.0
|
264 |
-
*/
|
265 |
-
protected function verifyTableStructure() {
|
266 |
-
return true;
|
267 |
-
}
|
268 |
-
|
269 |
-
/**
|
270 |
-
* @return string[]
|
271 |
-
* @deprecated 10.0
|
272 |
-
*/
|
273 |
-
public function getColumns() :array {
|
274 |
-
return $this->getTableSchema()->getColumnNames();
|
275 |
-
}
|
276 |
-
|
277 |
-
/**
|
278 |
-
* @return string[]
|
279 |
-
* @deprecated 10.0
|
280 |
-
*/
|
281 |
-
public function getColumnsDefinition() :array {
|
282 |
-
return $this->getTableSchema()->enumerateColumns();
|
283 |
-
}
|
284 |
-
|
285 |
-
/**
|
286 |
-
* @return string[]
|
287 |
-
* @deprecated 10.0
|
288 |
-
*/
|
289 |
-
public function getColumnsActual() {
|
290 |
-
return Services::WpDb()->getColumnsForTable( $this->getTable(), 'strtolower' );
|
291 |
-
}
|
292 |
-
|
293 |
-
/**
|
294 |
-
* @return string
|
295 |
-
* @throws \Exception
|
296 |
-
* @deprecated 10.0
|
297 |
-
*/
|
298 |
-
protected function getDefaultCreateTableSql() :string {
|
299 |
-
throw new \Exception( 'No SQL' );
|
300 |
-
}
|
301 |
-
|
302 |
-
/**
|
303 |
-
* @return string
|
304 |
-
* @deprecated 10.0
|
305 |
-
*/
|
306 |
-
public function getSqlCreate() {
|
307 |
-
return $this->getDefaultCreateTableSql();
|
308 |
-
}
|
309 |
}
|
248 |
unset( $this->bIsReady );
|
249 |
return $this;
|
250 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
251 |
}
|
src/lib/src/Databases/Base/Insert.php
CHANGED
@@ -13,10 +13,7 @@ class Insert extends BaseQuery {
|
|
13 |
|
14 |
public function getInsertData() :array {
|
15 |
$dbh = $this->getDbH();
|
16 |
-
$cols =
|
17 |
-
$dbh->getTableSchema()->getColumnNames()
|
18 |
-
: $dbh->getColumnsActual();
|
19 |
-
|
20 |
return array_intersect_key(
|
21 |
is_array( $this->aInsertData ) ? $this->aInsertData : [],
|
22 |
array_flip( $cols )
|
@@ -41,12 +38,10 @@ class Insert extends BaseQuery {
|
|
41 |
$data = [];
|
42 |
}
|
43 |
|
44 |
-
$
|
45 |
-
|
46 |
-
$
|
47 |
-
|
48 |
-
|
49 |
-
$this->aInsertData = array_intersect_key( $data, array_flip( $cols ) );
|
50 |
return $this;
|
51 |
}
|
52 |
|
13 |
|
14 |
public function getInsertData() :array {
|
15 |
$dbh = $this->getDbH();
|
16 |
+
$cols = $dbh->getTableSchema()->getColumnNames();
|
|
|
|
|
|
|
17 |
return array_intersect_key(
|
18 |
is_array( $this->aInsertData ) ? $this->aInsertData : [],
|
19 |
array_flip( $cols )
|
38 |
$data = [];
|
39 |
}
|
40 |
|
41 |
+
$this->aInsertData = array_intersect_key(
|
42 |
+
$data,
|
43 |
+
array_flip( $this->getDbH()->getTableSchema()->getColumnNames() )
|
44 |
+
);
|
|
|
|
|
45 |
return $this;
|
46 |
}
|
47 |
|
src/lib/src/Databases/IPs/CommonFilters.php
CHANGED
@@ -2,14 +2,16 @@
|
|
2 |
|
3 |
namespace FernleafSystems\Wordpress\Plugin\Shield\Databases\IPs;
|
4 |
|
|
|
|
|
5 |
trait CommonFilters {
|
6 |
|
7 |
/**
|
8 |
-
* @param string $
|
9 |
* @return $this
|
10 |
*/
|
11 |
-
public function filterByIp( $
|
12 |
-
return $this->addWhereEquals( 'ip', $
|
13 |
}
|
14 |
|
15 |
/**
|
@@ -25,8 +27,8 @@ trait CommonFilters {
|
|
25 |
*/
|
26 |
public function filterByBlacklist() {
|
27 |
return $this->filterByLists( [
|
28 |
-
|
29 |
-
|
30 |
] );
|
31 |
}
|
32 |
|
@@ -34,7 +36,7 @@ trait CommonFilters {
|
|
34 |
* @return $this
|
35 |
*/
|
36 |
public function filterByWhitelist() {
|
37 |
-
return $this->filterByList(
|
38 |
}
|
39 |
|
40 |
/**
|
@@ -45,6 +47,10 @@ trait CommonFilters {
|
|
45 |
return $this->addWhereEquals( 'is_range', $bIsRange ? 1 : 0 );
|
46 |
}
|
47 |
|
|
|
|
|
|
|
|
|
48 |
/**
|
49 |
* @param string $nLastAccessAfter
|
50 |
* @return $this
|
2 |
|
3 |
namespace FernleafSystems\Wordpress\Plugin\Shield\Databases\IPs;
|
4 |
|
5 |
+
use FernleafSystems\Wordpress\Plugin\Shield\Modules\IPs\ModCon;
|
6 |
+
|
7 |
trait CommonFilters {
|
8 |
|
9 |
/**
|
10 |
+
* @param string $ip
|
11 |
* @return $this
|
12 |
*/
|
13 |
+
public function filterByIp( $ip ) {
|
14 |
+
return $this->addWhereEquals( 'ip', $ip );
|
15 |
}
|
16 |
|
17 |
/**
|
27 |
*/
|
28 |
public function filterByBlacklist() {
|
29 |
return $this->filterByLists( [
|
30 |
+
ModCon::LIST_AUTO_BLACK,
|
31 |
+
ModCon::LIST_MANUAL_BLACK
|
32 |
] );
|
33 |
}
|
34 |
|
36 |
* @return $this
|
37 |
*/
|
38 |
public function filterByWhitelist() {
|
39 |
+
return $this->filterByList( ModCon::LIST_MANUAL_WHITE );
|
40 |
}
|
41 |
|
42 |
/**
|
47 |
return $this->addWhereEquals( 'is_range', $bIsRange ? 1 : 0 );
|
48 |
}
|
49 |
|
50 |
+
public function filterByLabel( string $label ) :self {
|
51 |
+
return $this->addWhereEquals( 'label', $label );
|
52 |
+
}
|
53 |
+
|
54 |
/**
|
55 |
* @param string $nLastAccessAfter
|
56 |
* @return $this
|
src/lib/src/Databases/IPs/Handler.php
CHANGED
@@ -3,6 +3,7 @@
|
|
3 |
namespace FernleafSystems\Wordpress\Plugin\Shield\Databases\IPs;
|
4 |
|
5 |
use FernleafSystems\Wordpress\Plugin\Shield\Databases\Base;
|
|
|
6 |
use FernleafSystems\Wordpress\Plugin\Shield\Modules\Ips\Options;
|
7 |
use FernleafSystems\Wordpress\Services\Services;
|
8 |
|
@@ -25,7 +26,7 @@ class Handler extends Base\Handler {
|
|
25 |
public function deleteRowsOlderThan( $nTimeStamp ) {
|
26 |
return $this->getQueryDeleter()
|
27 |
->addWhereOlderThan( $nTimeStamp, 'last_access_at' )
|
28 |
-
->addWhere( 'list',
|
29 |
->query();
|
30 |
}
|
31 |
|
3 |
namespace FernleafSystems\Wordpress\Plugin\Shield\Databases\IPs;
|
4 |
|
5 |
use FernleafSystems\Wordpress\Plugin\Shield\Databases\Base;
|
6 |
+
use FernleafSystems\Wordpress\Plugin\Shield\Modules\IPs\ModCon;
|
7 |
use FernleafSystems\Wordpress\Plugin\Shield\Modules\Ips\Options;
|
8 |
use FernleafSystems\Wordpress\Services\Services;
|
9 |
|
26 |
public function deleteRowsOlderThan( $nTimeStamp ) {
|
27 |
return $this->getQueryDeleter()
|
28 |
->addWhereOlderThan( $nTimeStamp, 'last_access_at' )
|
29 |
+
->addWhere( 'list', ModCon::LIST_MANUAL_WHITE, '!=' )
|
30 |
->query();
|
31 |
}
|
32 |
|
src/lib/src/Databases/Reports/Common.php
CHANGED
@@ -1,24 +1,16 @@
|
|
1 |
-
<?php
|
2 |
|
3 |
namespace FernleafSystems\Wordpress\Plugin\Shield\Databases\Reports;
|
4 |
|
5 |
trait Common {
|
6 |
|
7 |
-
|
8 |
-
|
9 |
-
* @return $this
|
10 |
-
*/
|
11 |
-
public function filterByFrequency( $sFrequency ) {
|
12 |
-
return $this->addWhere( 'frequency', $sFrequency );
|
13 |
}
|
14 |
|
15 |
-
|
16 |
-
|
17 |
-
|
18 |
-
*/
|
19 |
-
public function filterByType( $sType ) {
|
20 |
-
if ( in_array( $sType, [ Handler::TYPE_INFO, Handler::TYPE_ALERT ] ) ) {
|
21 |
-
$this->addWhere( 'type', $sType );
|
22 |
}
|
23 |
return $this;
|
24 |
}
|
1 |
+
<?php declare( strict_types=1 );
|
2 |
|
3 |
namespace FernleafSystems\Wordpress\Plugin\Shield\Databases\Reports;
|
4 |
|
5 |
trait Common {
|
6 |
|
7 |
+
public function filterByFrequency( string $freq ) :self {
|
8 |
+
return $this->addWhere( 'frequency', $freq );
|
|
|
|
|
|
|
|
|
9 |
}
|
10 |
|
11 |
+
public function filterByType( string $type ) :self {
|
12 |
+
if ( in_array( $type, [ Handler::TYPE_INFO, Handler::TYPE_ALERT ] ) ) {
|
13 |
+
$this->addWhere( 'type', $type );
|
|
|
|
|
|
|
|
|
14 |
}
|
15 |
return $this;
|
16 |
}
|
src/lib/src/Databases/Reports/Handler.php
CHANGED
@@ -3,7 +3,6 @@
|
|
3 |
namespace FernleafSystems\Wordpress\Plugin\Shield\Databases\Reports;
|
4 |
|
5 |
use FernleafSystems\Wordpress\Plugin\Shield\Databases\Base;
|
6 |
-
use FernleafSystems\Wordpress\Plugin\Shield\Modules\Reporting\Options;
|
7 |
|
8 |
class Handler extends Base\Handler {
|
9 |
|
3 |
namespace FernleafSystems\Wordpress\Plugin\Shield\Databases\Reports;
|
4 |
|
5 |
use FernleafSystems\Wordpress\Plugin\Shield\Databases\Base;
|
|
|
6 |
|
7 |
class Handler extends Base\Handler {
|
8 |
|
src/lib/src/Modules/AuditTrail/AjaxHandler.php
CHANGED
@@ -5,7 +5,7 @@ namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\AuditTrail;
|
|
5 |
use FernleafSystems\Wordpress\Plugin\Shield;
|
6 |
use FernleafSystems\Wordpress\Services\Services;
|
7 |
|
8 |
-
class AjaxHandler extends Shield\Modules\
|
9 |
|
10 |
protected function processAjaxAction( string $action ) :array {
|
11 |
|
@@ -25,12 +25,9 @@ class AjaxHandler extends Shield\Modules\Base\AjaxHandlerShield {
|
|
25 |
return $aResponse;
|
26 |
}
|
27 |
|
28 |
-
|
29 |
-
|
30 |
-
|
31 |
-
protected function ajaxExec_AddParamToFirewallWhitelist() {
|
32 |
-
/** @var \ICWP_WPSF_FeatureHandler_AuditTrail $oMod */
|
33 |
-
$oMod = $this->getMod();
|
34 |
$bSuccess = false;
|
35 |
|
36 |
$nId = Services::Request()->post( 'rid' );
|
@@ -39,9 +36,9 @@ class AjaxHandler extends Shield\Modules\Base\AjaxHandlerShield {
|
|
39 |
}
|
40 |
else {
|
41 |
/** @var Shield\Databases\AuditTrail\EntryVO $oEntry */
|
42 |
-
$oEntry = $
|
43 |
-
|
44 |
-
|
45 |
|
46 |
if ( empty( $oEntry ) ) {
|
47 |
$sMessage = __( 'Audit entry could not be loaded.', 'wp-simple-firewall' );
|
@@ -54,7 +51,7 @@ class AjaxHandler extends Shield\Modules\Base\AjaxHandlerShield {
|
|
54 |
$sMessage = __( 'Parameter associated with this audit entry could not be found.', 'wp-simple-firewall' );
|
55 |
}
|
56 |
else {
|
57 |
-
/** @var \
|
58 |
$oModFire = $this->getCon()->getModule( 'firewall' );
|
59 |
$oModFire->addParamToWhitelist( $sParam, $sUri );
|
60 |
$sMessage = sprintf( __( 'Parameter "%s" whitelisted successfully', 'wp-simple-firewall' ), $sParam );
|
@@ -69,15 +66,12 @@ class AjaxHandler extends Shield\Modules\Base\AjaxHandlerShield {
|
|
69 |
];
|
70 |
}
|
71 |
|
72 |
-
|
73 |
-
|
74 |
-
|
75 |
-
private function ajaxExec_BuildTableAuditTrail() {
|
76 |
-
/** @var \ICWP_WPSF_FeatureHandler_AuditTrail $oMod */
|
77 |
-
$oMod = $this->getMod();
|
78 |
$oTableBuilder = ( new Shield\Tables\Build\AuditTrail() )
|
79 |
-
->setMod( $
|
80 |
-
->setDbHandler( $
|
81 |
|
82 |
return [
|
83 |
'success' => true,
|
5 |
use FernleafSystems\Wordpress\Plugin\Shield;
|
6 |
use FernleafSystems\Wordpress\Services\Services;
|
7 |
|
8 |
+
class AjaxHandler extends Shield\Modules\BaseShield\AjaxHandler {
|
9 |
|
10 |
protected function processAjaxAction( string $action ) :array {
|
11 |
|
25 |
return $aResponse;
|
26 |
}
|
27 |
|
28 |
+
protected function ajaxExec_AddParamToFirewallWhitelist() :array {
|
29 |
+
/** @var ModCon $mod */
|
30 |
+
$mod = $this->getMod();
|
|
|
|
|
|
|
31 |
$bSuccess = false;
|
32 |
|
33 |
$nId = Services::Request()->post( 'rid' );
|
36 |
}
|
37 |
else {
|
38 |
/** @var Shield\Databases\AuditTrail\EntryVO $oEntry */
|
39 |
+
$oEntry = $mod->getDbHandler_AuditTrail()
|
40 |
+
->getQuerySelector()
|
41 |
+
->byId( $nId );
|
42 |
|
43 |
if ( empty( $oEntry ) ) {
|
44 |
$sMessage = __( 'Audit entry could not be loaded.', 'wp-simple-firewall' );
|
51 |
$sMessage = __( 'Parameter associated with this audit entry could not be found.', 'wp-simple-firewall' );
|
52 |
}
|
53 |
else {
|
54 |
+
/** @var Shield\Modules\Firewall\ModCon $oModFire */
|
55 |
$oModFire = $this->getCon()->getModule( 'firewall' );
|
56 |
$oModFire->addParamToWhitelist( $sParam, $sUri );
|
57 |
$sMessage = sprintf( __( 'Parameter "%s" whitelisted successfully', 'wp-simple-firewall' ), $sParam );
|
66 |
];
|
67 |
}
|
68 |
|
69 |
+
private function ajaxExec_BuildTableAuditTrail() :array {
|
70 |
+
/** @var ModCon $mod */
|
71 |
+
$mod = $this->getMod();
|
|
|
|
|
|
|
72 |
$oTableBuilder = ( new Shield\Tables\Build\AuditTrail() )
|
73 |
+
->setMod( $mod )
|
74 |
+
->setDbHandler( $mod->getDbHandler_AuditTrail() );
|
75 |
|
76 |
return [
|
77 |
'success' => true,
|
src/lib/src/Modules/AuditTrail/Auditors/Upgrades.php
ADDED
@@ -0,0 +1,88 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php declare( strict_types=1 );
|
2 |
+
|
3 |
+
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\AuditTrail\Auditors;
|
4 |
+
|
5 |
+
use FernleafSystems\Wordpress\Services\Services;
|
6 |
+
|
7 |
+
class Upgrades extends Base {
|
8 |
+
|
9 |
+
/**
|
10 |
+
* @var array
|
11 |
+
*/
|
12 |
+
private $plugins;
|
13 |
+
|
14 |
+
/**
|
15 |
+
* @var array
|
16 |
+
*/
|
17 |
+
private $themes;
|
18 |
+
|
19 |
+
public function run() {
|
20 |
+
$this->init();
|
21 |
+
add_action( 'upgrader_process_complete', [ $this, 'auditUpgrades' ], 10, 2 );
|
22 |
+
/* add_action( 'upgrader_post_install', [ $this, 'auditUpgrade' ], 10, 3 ); */
|
23 |
+
}
|
24 |
+
|
25 |
+
private function init() {
|
26 |
+
$this->plugins = array_map( function ( $plugin ) {
|
27 |
+
return $plugin[ 'Version' ];
|
28 |
+
}, Services::WpPlugins()->getPlugins() );
|
29 |
+
$this->themes = array_map( function ( $theme ) {
|
30 |
+
return $theme->get( 'Version' );
|
31 |
+
}, Services::WpThemes()->getThemes() );
|
32 |
+
}
|
33 |
+
|
34 |
+
/**
|
35 |
+
* @param \WP_Upgrader $handler
|
36 |
+
* @param array $data
|
37 |
+
*/
|
38 |
+
public function auditUpgrades( $handler, $data ) {
|
39 |
+
$action = $data[ 'action' ] ?? null;
|
40 |
+
$type = $data[ 'type' ] ?? null;
|
41 |
+
if ( $action === 'update' && in_array( $type, [ 'plugin', 'theme' ] ) ) {
|
42 |
+
if ( !empty( $data[ 'plugins' ] ) && is_array( $data[ 'plugins' ] ) ) {
|
43 |
+
foreach ( $data[ 'plugins' ] as $item ) {
|
44 |
+
if ( isset( $this->plugins[ $item ] ) ) {
|
45 |
+
$this->handlePlugin( $item );
|
46 |
+
}
|
47 |
+
}
|
48 |
+
}
|
49 |
+
elseif ( !empty( $data[ 'themes' ] ) && is_array( $data[ 'themes' ] ) ) {
|
50 |
+
foreach ( $data[ 'themes' ] as $item ) {
|
51 |
+
if ( isset( $this->themes[ $item ] ) ) {
|
52 |
+
$this->handleTheme( $item );
|
53 |
+
}
|
54 |
+
}
|
55 |
+
}
|
56 |
+
}
|
57 |
+
}
|
58 |
+
|
59 |
+
private function handlePlugin( string $item ) {
|
60 |
+
$WPP = Services::WpPlugins();
|
61 |
+
$VO = $WPP->getPluginAsVo( $item, true );
|
62 |
+
$this->getCon()->fireEvent(
|
63 |
+
'plugin_upgraded',
|
64 |
+
[
|
65 |
+
'audit' => [
|
66 |
+
'file' => $VO->Name,
|
67 |
+
'from' => $this->plugins[ $item ],
|
68 |
+
'to' => $VO->Version,
|
69 |
+
]
|
70 |
+
]
|
71 |
+
);
|
72 |
+
}
|
73 |
+
|
74 |
+
private function handleTheme( string $item ) {
|
75 |
+
$WPT = Services::WpThemes();
|
76 |
+
$VO = $WPT->getThemeAsVo( $item, true );
|
77 |
+
$this->getCon()->fireEvent(
|
78 |
+
'theme_upgraded',
|
79 |
+
[
|
80 |
+
'audit' => [
|
81 |
+
'file' => $VO->Name,
|
82 |
+
'from' => $this->themes[ $item ],
|
83 |
+
'to' => $VO->Version,
|
84 |
+
]
|
85 |
+
]
|
86 |
+
);
|
87 |
+
}
|
88 |
+
}
|
src/lib/src/Modules/AuditTrail/Auditors/Users.php
CHANGED
@@ -2,44 +2,43 @@
|
|
2 |
|
3 |
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\AuditTrail\Auditors;
|
4 |
|
|
|
5 |
use FernleafSystems\Wordpress\Services\Services;
|
6 |
|
7 |
class Users extends Base {
|
8 |
|
|
|
|
|
9 |
public function run() {
|
10 |
-
|
11 |
add_action( 'user_register', [ $this, 'auditNewUserRegistered' ] );
|
12 |
add_action( 'delete_user', [ $this, 'auditDeleteUser' ], 30, 2 );
|
13 |
}
|
14 |
|
15 |
-
|
16 |
-
|
17 |
-
|
18 |
-
|
19 |
-
|
20 |
-
|
21 |
-
|
22 |
-
|
23 |
-
|
24 |
-
|
25 |
-
]
|
26 |
]
|
27 |
-
|
28 |
-
|
29 |
}
|
30 |
|
31 |
-
|
32 |
-
|
33 |
-
|
34 |
-
public function auditNewUserRegistered( $nUserId ) {
|
35 |
-
$oUser = empty( $nUserId ) ? null : Services::WpUsers()->getUserById( $nUserId );
|
36 |
-
if ( $oUser instanceof \WP_User ) {
|
37 |
$this->getCon()->fireEvent(
|
38 |
'user_registered',
|
39 |
[
|
40 |
'audit' => [
|
41 |
-
'user' => sanitize_user( $
|
42 |
-
'email' => $
|
43 |
]
|
44 |
]
|
45 |
);
|
@@ -47,20 +46,20 @@ class Users extends Base {
|
|
47 |
}
|
48 |
|
49 |
/**
|
50 |
-
* @param int $
|
51 |
* @param int $nReassigned
|
52 |
*/
|
53 |
-
public function auditDeleteUser( $
|
54 |
$oWpUsers = Services::WpUsers();
|
55 |
|
56 |
-
$
|
57 |
-
if ( $
|
58 |
$this->getCon()->fireEvent(
|
59 |
'user_deleted',
|
60 |
[
|
61 |
'audit' => [
|
62 |
-
'user' => sanitize_user( $
|
63 |
-
'email' => $
|
64 |
]
|
65 |
]
|
66 |
);
|
2 |
|
3 |
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\AuditTrail\Auditors;
|
4 |
|
5 |
+
use FernleafSystems\Wordpress\Plugin\Shield\Utilities\Consumer\WpLoginCapture;
|
6 |
use FernleafSystems\Wordpress\Services\Services;
|
7 |
|
8 |
class Users extends Base {
|
9 |
|
10 |
+
use WpLoginCapture;
|
11 |
+
|
12 |
public function run() {
|
13 |
+
$this->setupLoginCaptureHooks();
|
14 |
add_action( 'user_register', [ $this, 'auditNewUserRegistered' ] );
|
15 |
add_action( 'delete_user', [ $this, 'auditDeleteUser' ], 30, 2 );
|
16 |
}
|
17 |
|
18 |
+
protected function captureLogin( \WP_User $user ) {
|
19 |
+
$this->auditUserLoginSuccess( $user );
|
20 |
+
}
|
21 |
+
|
22 |
+
public function auditUserLoginSuccess( \WP_User $user ) {
|
23 |
+
$this->getCon()->fireEvent(
|
24 |
+
'user_login',
|
25 |
+
[
|
26 |
+
'audit' => [
|
27 |
+
'user' => $user->user_login,
|
|
|
28 |
]
|
29 |
+
]
|
30 |
+
);
|
31 |
}
|
32 |
|
33 |
+
public function auditNewUserRegistered( int $userID ) {
|
34 |
+
$user = empty( $userID ) ? null : Services::WpUsers()->getUserById( $userID );
|
35 |
+
if ( $user instanceof \WP_User ) {
|
|
|
|
|
|
|
36 |
$this->getCon()->fireEvent(
|
37 |
'user_registered',
|
38 |
[
|
39 |
'audit' => [
|
40 |
+
'user' => sanitize_user( $user->user_login ),
|
41 |
+
'email' => $user->user_email,
|
42 |
]
|
43 |
]
|
44 |
);
|
46 |
}
|
47 |
|
48 |
/**
|
49 |
+
* @param int $userID
|
50 |
* @param int $nReassigned
|
51 |
*/
|
52 |
+
public function auditDeleteUser( $userID, $nReassigned ) {
|
53 |
$oWpUsers = Services::WpUsers();
|
54 |
|
55 |
+
$user = empty( $userID ) ? null : $oWpUsers->getUserById( $userID );
|
56 |
+
if ( $user instanceof \WP_User ) {
|
57 |
$this->getCon()->fireEvent(
|
58 |
'user_deleted',
|
59 |
[
|
60 |
'audit' => [
|
61 |
+
'user' => sanitize_user( $user->user_login ),
|
62 |
+
'email' => $user->user_email,
|
63 |
]
|
64 |
]
|
65 |
);
|
src/lib/src/Modules/AuditTrail/Insights/OverviewCards.php
CHANGED
@@ -3,13 +3,14 @@
|
|
3 |
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\AuditTrail\Insights;
|
4 |
|
5 |
use FernleafSystems\Wordpress\Plugin\Shield;
|
|
|
6 |
|
7 |
class OverviewCards extends Shield\Modules\Base\Insights\OverviewCards {
|
8 |
|
9 |
public function build() :array {
|
10 |
-
/** @var \
|
11 |
$mod = $this->getMod();
|
12 |
-
/** @var
|
13 |
$opts = $this->getOptions();
|
14 |
|
15 |
$cardSection = [
|
@@ -24,40 +25,12 @@ class OverviewCards extends Shield\Modules\Base\Insights\OverviewCards {
|
|
24 |
$data[ 'key_opts' ][ 'mod' ] = $this->getModDisabledCard();
|
25 |
}
|
26 |
else {
|
27 |
-
$
|
28 |
-
|
29 |
-
|
30 |
-
|
31 |
-
|
32 |
-
|
33 |
-
$opts->isAuditPosts() ? $aAudit[] = __( 'posts', 'wp-simple-firewall' ) : $aNonAudit[] = __( 'posts', 'wp-simple-firewall' );
|
34 |
-
$opts->isAuditEmails() ? $aAudit[] = __( 'emails', 'wp-simple-firewall' ) : $aNonAudit[] = __( 'emails', 'wp-simple-firewall' );
|
35 |
-
$opts->isAuditWp() ? $aAudit[] = 'WP' : $aNonAudit[] = 'WP';
|
36 |
-
|
37 |
-
if ( empty( $aNonAudit ) ) {
|
38 |
-
$cards[ 'audit' ] = [
|
39 |
-
'name' => __( 'Audit Areas', 'wp-simple-firewall' ),
|
40 |
-
'state' => 1,
|
41 |
-
'summary' => __( 'All important events on your site are being logged', 'wp-simple-firewall' ),
|
42 |
-
'href' => $mod->getUrl_DirectLinkToOption( 'section_enable_audit_contexts' ),
|
43 |
-
];
|
44 |
-
}
|
45 |
-
elseif ( empty( $aAudit ) ) {
|
46 |
-
$cards[ 'audit' ] = [
|
47 |
-
'name' => __( 'Audit Areas', 'wp-simple-firewall' ),
|
48 |
-
'state' => -1,
|
49 |
-
'summary' => sprintf( __( 'No areas are set to be audited: %s', 'wp-simple-firewall' ), implode( ', ', $aAudit ) ),
|
50 |
-
'href' => $mod->getUrl_DirectLinkToSection( 'section_enable_audit_contexts' ),
|
51 |
-
];
|
52 |
-
}
|
53 |
-
else {
|
54 |
-
$cards[ 'nonaudit' ] = [
|
55 |
-
'name' => __( 'Audit Events', 'wp-simple-firewall' ),
|
56 |
-
'state' => 0,
|
57 |
-
'summary' => sprintf( __( "Important events aren't being audited: %s", 'wp-simple-firewall' ), implode( ', ', $aNonAudit ) ),
|
58 |
-
'href' => $mod->getUrl_DirectLinkToSection( 'section_enable_audit_contexts' ),
|
59 |
-
];
|
60 |
-
}
|
61 |
|
62 |
$cards[ 'audit_length' ] = [
|
63 |
'name' => __( 'Audit Trail', 'wp-simple-firewall' ),
|
3 |
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\AuditTrail\Insights;
|
4 |
|
5 |
use FernleafSystems\Wordpress\Plugin\Shield;
|
6 |
+
use FernleafSystems\Wordpress\Plugin\Shield\Modules\AuditTrail;
|
7 |
|
8 |
class OverviewCards extends Shield\Modules\Base\Insights\OverviewCards {
|
9 |
|
10 |
public function build() :array {
|
11 |
+
/** @var AuditTrail\ModCon $mod */
|
12 |
$mod = $this->getMod();
|
13 |
+
/** @var AuditTrail\Options $opts */
|
14 |
$opts = $this->getOptions();
|
15 |
|
16 |
$cardSection = [
|
25 |
$data[ 'key_opts' ][ 'mod' ] = $this->getModDisabledCard();
|
26 |
}
|
27 |
else {
|
28 |
+
$cards[ 'audit' ] = [
|
29 |
+
'name' => __( 'Audit Areas', 'wp-simple-firewall' ),
|
30 |
+
'state' => 1,
|
31 |
+
'summary' => __( 'All important events on your site are being logged', 'wp-simple-firewall' ),
|
32 |
+
'href' => $mod->getUrl_DirectLinkToOption( 'section_enable_audit_contexts' ),
|
33 |
+
];
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
34 |
|
35 |
$cards[ 'audit_length' ] = [
|
36 |
'name' => __( 'Audit Trail', 'wp-simple-firewall' ),
|
src/lib/src/Modules/AuditTrail/Lib/AuditWriter.php
CHANGED
@@ -17,16 +17,16 @@ class AuditWriter extends EventsListener {
|
|
17 |
private $aAuditLogs;
|
18 |
|
19 |
/**
|
20 |
-
* @param string $
|
21 |
* @param array $aMeta
|
22 |
*/
|
23 |
-
protected function captureEvent( $
|
24 |
$oCon = $this->getCon();
|
25 |
-
$aDef = $oCon->loadEventsService()->getEventDef( $
|
26 |
if ( $aDef[ 'audit' ] && empty( $aMeta[ 'suppress_audit' ] ) ) { // only audit if it's an auditable event
|
27 |
$oEntry = new AuditTrail\EntryVO();
|
28 |
$oEntry->rid = $this->getCon()->getShortRequestId();
|
29 |
-
$oEntry->event = $
|
30 |
$oEntry->category = $aDef[ 'cat' ];
|
31 |
$oEntry->context = $aDef[ 'context' ];
|
32 |
$oEntry->meta = isset( $aMeta[ 'audit' ] ) ? $aMeta[ 'audit' ] : [];
|
@@ -38,7 +38,7 @@ class AuditWriter extends EventsListener {
|
|
38 |
$aLogs[] = $oEntry;
|
39 |
}
|
40 |
else {
|
41 |
-
$aLogs[ $
|
42 |
}
|
43 |
|
44 |
$this->setLogs( $aLogs );
|
17 |
private $aAuditLogs;
|
18 |
|
19 |
/**
|
20 |
+
* @param string $evt
|
21 |
* @param array $aMeta
|
22 |
*/
|
23 |
+
protected function captureEvent( $evt, $aMeta = [] ) {
|
24 |
$oCon = $this->getCon();
|
25 |
+
$aDef = $oCon->loadEventsService()->getEventDef( $evt );
|
26 |
if ( $aDef[ 'audit' ] && empty( $aMeta[ 'suppress_audit' ] ) ) { // only audit if it's an auditable event
|
27 |
$oEntry = new AuditTrail\EntryVO();
|
28 |
$oEntry->rid = $this->getCon()->getShortRequestId();
|
29 |
+
$oEntry->event = $evt;
|
30 |
$oEntry->category = $aDef[ 'cat' ];
|
31 |
$oEntry->context = $aDef[ 'context' ];
|
32 |
$oEntry->meta = isset( $aMeta[ 'audit' ] ) ? $aMeta[ 'audit' ] : [];
|
38 |
$aLogs[] = $oEntry;
|
39 |
}
|
40 |
else {
|
41 |
+
$aLogs[ $evt ] = $oEntry;
|
42 |
}
|
43 |
|
44 |
$this->setLogs( $aLogs );
|
src/lib/src/Modules/AuditTrail/ModCon.php
ADDED
@@ -0,0 +1,105 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php declare( strict_types=1 );
|
2 |
+
|
3 |
+
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\AuditTrail;
|
4 |
+
|
5 |
+
use FernleafSystems\Wordpress\Plugin\Shield;
|
6 |
+
use FernleafSystems\Wordpress\Plugin\Shield\Modules\BaseShield;
|
7 |
+
use FernleafSystems\Wordpress\Services\Services;
|
8 |
+
|
9 |
+
class ModCon extends BaseShield\ModCon {
|
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() :bool {
|
20 |
+
return $this->getDbHandler_AuditTrail()->isReady() && parent::isReadyToExecute();
|
21 |
+
}
|
22 |
+
|
23 |
+
/**
|
24 |
+
* @return array
|
25 |
+
*/
|
26 |
+
public function getAllContexts() {
|
27 |
+
return [
|
28 |
+
'all' => 'All', //special
|
29 |
+
'wpsf' => $this->getCon()->getHumanName(),
|
30 |
+
'wordpress' => 'WordPress',
|
31 |
+
'users' => 'Users',
|
32 |
+
'posts' => 'Posts',
|
33 |
+
'plugins' => 'Plugins',
|
34 |
+
'themes' => 'Themes',
|
35 |
+
'emails' => 'Emails',
|
36 |
+
];
|
37 |
+
}
|
38 |
+
|
39 |
+
/**
|
40 |
+
* See plugin controller for the nature of $aData wpPrivacyExport()
|
41 |
+
*
|
42 |
+
* @param array $aExportItems
|
43 |
+
* @param string $sEmail
|
44 |
+
* @param int $nPage
|
45 |
+
* @return array
|
46 |
+
*/
|
47 |
+
public function onWpPrivacyExport( $aExportItems, $sEmail, $nPage = 1 ) {
|
48 |
+
|
49 |
+
$oUser = Services::WpUsers()->getUserByEmail( $sEmail );
|
50 |
+
|
51 |
+
$aExportItem = [
|
52 |
+
'group_id' => $this->prefix(),
|
53 |
+
'group_label' => sprintf( __( '[%s] Audit Trail Entries', 'wp-simple-firewall' ), $this->getCon()
|
54 |
+
->getHumanName() ),
|
55 |
+
'item_id' => $this->prefix( 'audit-trail' ),
|
56 |
+
'data' => [],
|
57 |
+
];
|
58 |
+
|
59 |
+
try {
|
60 |
+
/** @var Shield\Databases\AuditTrail\Select $oFinder */
|
61 |
+
$oFinder = $this->getDbHandler_AuditTrail()->getQuerySelector();
|
62 |
+
$oFinder->filterByUsername( $oUser->user_login );
|
63 |
+
|
64 |
+
$WP = Services::WpGeneral();
|
65 |
+
/** @var Shield\Databases\AuditTrail\EntryVO $oEntry */
|
66 |
+
foreach ( $oFinder->query() as $oEntry ) {
|
67 |
+
$aExportItem[ 'data' ][] = [
|
68 |
+
$sTimeStamp = $WP->getTimeStringForDisplay( $oEntry->getCreatedAt() ),
|
69 |
+
'name' => sprintf( '[%s] Audit Trail Entry', $sTimeStamp ),
|
70 |
+
'value' => sprintf( '[IP:%s] %s', $oEntry->ip, $oEntry->message )
|
71 |
+
];
|
72 |
+
}
|
73 |
+
|
74 |
+
if ( !empty( $aExportItem[ 'data' ] ) ) {
|
75 |
+
$aExportItems[] = $aExportItem;
|
76 |
+
}
|
77 |
+
}
|
78 |
+
catch ( \Exception $e ) {
|
79 |
+
}
|
80 |
+
|
81 |
+
return $aExportItems;
|
82 |
+
}
|
83 |
+
|
84 |
+
/**
|
85 |
+
* See plugin controller for the nature of $aData wpPrivacyErase()
|
86 |
+
*
|
87 |
+
* @param array $aData
|
88 |
+
* @param string $sEmail
|
89 |
+
* @param int $nPage
|
90 |
+
* @return array
|
91 |
+
*/
|
92 |
+
public function onWpPrivacyErase( $aData, $sEmail, $nPage = 1 ) {
|
93 |
+
try {
|
94 |
+
$oThisUsername = Services::WpUsers()->getUserByEmail( $sEmail )->user_login;
|
95 |
+
$this->getDbHandler_AuditTrail()
|
96 |
+
->getQueryDeleter()
|
97 |
+
->addWhereSearch( 'wp_username', $oThisUsername )
|
98 |
+
->all();
|
99 |
+
$aData[ 'messages' ][] = sprintf( '%s Audit Entries deleted', $this->getCon()->getHumanName() );
|
100 |
+
}
|
101 |
+
catch ( \Exception $e ) {
|
102 |
+
}
|
103 |
+
return $aData;
|
104 |
+
}
|
105 |
+
}
|
src/lib/src/Modules/AuditTrail/Options.php
CHANGED
@@ -2,22 +2,11 @@
|
|
2 |
|
3 |
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\AuditTrail;
|
4 |
|
5 |
-
use FernleafSystems\Wordpress\Plugin\Shield\Modules\
|
6 |
-
use FernleafSystems\Wordpress\Services\Services;
|
7 |
|
8 |
-
class Options extends
|
9 |
|
10 |
-
|
11 |
-
* @return string
|
12 |
-
*/
|
13 |
-
public function getDbTable_ChangeTracking() {
|
14 |
-
return $this->getCon()->prefixOption( $this->getDef( 'table_name_changetracking' ) );
|
15 |
-
}
|
16 |
-
|
17 |
-
/**
|
18 |
-
* @return int
|
19 |
-
*/
|
20 |
-
public function getAutoCleanDays() {
|
21 |
return (int)$this->getOpt( 'audit_trail_auto_clean' );
|
22 |
}
|
23 |
|
@@ -29,105 +18,65 @@ class Options extends Base\ShieldOptions {
|
|
29 |
|
30 |
/**
|
31 |
* @return bool
|
|
|
32 |
*/
|
33 |
-
public function
|
34 |
-
return
|
35 |
-
}
|
36 |
-
|
37 |
-
/**
|
38 |
-
* @return int
|
39 |
-
*/
|
40 |
-
public function getCTSnapshotsPerWeek() {
|
41 |
-
return (int)$this->getOpt( 'ct_snapshots_per_week', 7 );
|
42 |
-
}
|
43 |
-
|
44 |
-
/**
|
45 |
-
* @return int
|
46 |
-
*/
|
47 |
-
public function getCTMaxSnapshots() {
|
48 |
-
return (int)$this->getOpt( 'ct_max_snapshots', 28 );
|
49 |
-
}
|
50 |
-
|
51 |
-
/**
|
52 |
-
* @return int
|
53 |
-
*/
|
54 |
-
public function getCTSnapshotInterval() {
|
55 |
-
return WEEK_IN_SECONDS/$this->getCTSnapshotsPerWeek();
|
56 |
-
}
|
57 |
-
|
58 |
-
/**
|
59 |
-
* @return int
|
60 |
-
*/
|
61 |
-
public function getCTLastSnapshotAt() {
|
62 |
-
return $this->getOpt( 'ct_last_snapshot_at' );
|
63 |
-
}
|
64 |
-
|
65 |
-
/**
|
66 |
-
* @return bool
|
67 |
-
*/
|
68 |
-
public function isCTSnapshotDue() {
|
69 |
-
return ( Services::Request()->ts() - $this->getCTLastSnapshotAt() > $this->getCTSnapshotInterval() );
|
70 |
}
|
71 |
|
72 |
/**
|
73 |
* @return bool
|
|
|
74 |
*/
|
75 |
-
public function
|
76 |
-
return $this->isAuditEmails()
|
77 |
-
|| $this->isAuditPlugins()
|
78 |
-
|| $this->isAuditThemes()
|
79 |
-
|| $this->isAuditPosts()
|
80 |
-
|| $this->isAuditShield()
|
81 |
-
|| $this->isAuditUsers()
|
82 |
-
|| $this->isAuditWp();
|
83 |
-
}
|
84 |
-
|
85 |
-
/**
|
86 |
-
* @return bool
|
87 |
-
*/
|
88 |
-
public function isAuditEmails() {
|
89 |
return $this->isOpt( 'enable_audit_context_emails', 'Y' );
|
90 |
}
|
91 |
|
92 |
/**
|
93 |
* @return bool
|
|
|
94 |
*/
|
95 |
-
public function isAuditPlugins() {
|
96 |
return $this->isOpt( 'enable_audit_context_plugins', 'Y' );
|
97 |
}
|
98 |
|
99 |
/**
|
100 |
* @return bool
|
|
|
101 |
*/
|
102 |
-
public function isAuditPosts() {
|
103 |
return $this->isOpt( 'enable_audit_context_posts', 'Y' );
|
104 |
}
|
105 |
|
106 |
/**
|
107 |
* @return bool
|
|
|
108 |
*/
|
109 |
-
public function isAuditShield() {
|
110 |
return $this->isOpt( 'enable_audit_context_wpsf', 'Y' );
|
111 |
}
|
112 |
|
113 |
/**
|
114 |
* @return bool
|
|
|
115 |
*/
|
116 |
-
public function isAuditThemes() {
|
117 |
return $this->isOpt( 'enable_audit_context_themes', 'Y' );
|
118 |
}
|
119 |
|
120 |
/**
|
121 |
* @return bool
|
|
|
122 |
*/
|
123 |
-
public function isAuditUsers() {
|
124 |
return $this->isOpt( 'enable_audit_context_users', 'Y' );
|
125 |
}
|
126 |
|
127 |
/**
|
128 |
* @return bool
|
|
|
129 |
*/
|
130 |
-
public function isAuditWp() {
|
131 |
return $this->isOpt( 'enable_audit_context_wordpress', 'Y' );
|
132 |
}
|
133 |
|
@@ -137,12 +86,4 @@ class Options extends Base\ShieldOptions {
|
|
137 |
public function updateCTLastSnapshotAt() {
|
138 |
return $this->setOptAt( 'ct_last_snapshot_at' );
|
139 |
}
|
140 |
-
|
141 |
-
/**
|
142 |
-
* @return string
|
143 |
-
* @deprecated 10.0
|
144 |
-
*/
|
145 |
-
public function getDbTable_AuditTrail() {
|
146 |
-
return $this->getCon()->prefixOption( $this->getDef( 'audit_trail_table_name' ) );
|
147 |
-
}
|
148 |
}
|
2 |
|
3 |
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\AuditTrail;
|
4 |
|
5 |
+
use FernleafSystems\Wordpress\Plugin\Shield\Modules\BaseShield;
|
|
|
6 |
|
7 |
+
class Options extends BaseShield\Options {
|
8 |
|
9 |
+
public function getAutoCleanDays() :int {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
10 |
return (int)$this->getOpt( 'audit_trail_auto_clean' );
|
11 |
}
|
12 |
|
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 |
|
86 |
public function updateCTLastSnapshotAt() {
|
87 |
return $this->setOptAt( 'ct_last_snapshot_at' );
|
88 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
89 |
}
|
src/lib/src/Modules/AuditTrail/Processor.php
ADDED
@@ -0,0 +1,65 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\AuditTrail;
|
4 |
+
|
5 |
+
use FernleafSystems\Wordpress\Plugin\Shield\Databases;
|
6 |
+
use FernleafSystems\Wordpress\Plugin\Shield\Modules\BaseShield;
|
7 |
+
|
8 |
+
class Processor extends BaseShield\Processor {
|
9 |
+
|
10 |
+
/**
|
11 |
+
* @var Lib\AuditWriter
|
12 |
+
*/
|
13 |
+
private $auditWriter;
|
14 |
+
|
15 |
+
protected function run() {
|
16 |
+
$this->initAuditors();
|
17 |
+
$this->getSubProAuditor()->execute();
|
18 |
+
}
|
19 |
+
|
20 |
+
/**
|
21 |
+
* @return Lib\AuditWriter
|
22 |
+
*/
|
23 |
+
private function loadAuditorWriter() :Lib\AuditWriter {
|
24 |
+
if ( !isset( $this->auditWriter ) ) {
|
25 |
+
/** @var ModCon $mod */
|
26 |
+
$mod = $this->getMod();
|
27 |
+
$this->auditWriter = ( new Lib\AuditWriter( $this->getCon() ) )
|
28 |
+
->setDbHandler( $mod->getDbHandler_AuditTrail() );
|
29 |
+
}
|
30 |
+
return $this->auditWriter;
|
31 |
+
}
|
32 |
+
|
33 |
+
private function initAuditors() {
|
34 |
+
$this->loadAuditorWriter()->setIfCommit( true );
|
35 |
+
|
36 |
+
( new Auditors\Users() )
|
37 |
+
->setMod( $this->getMod() )
|
38 |
+
->run();
|
39 |
+
( new Auditors\Plugins() )
|
40 |
+
->setMod( $this->getMod() )
|
41 |
+
->run();
|
42 |
+
( new Auditors\Themes() )
|
43 |
+
->setMod( $this->getMod() )
|
44 |
+
->run();
|
45 |
+
( new Auditors\Wordpress() )
|
46 |
+
->setMod( $this->getMod() )
|
47 |
+
->run();
|
48 |
+
( new Auditors\Posts() )
|
49 |
+
->setMod( $this->getMod() )
|
50 |
+
->run();
|
51 |
+
( new Auditors\Emails() )
|
52 |
+
->setMod( $this->getMod() )
|
53 |
+
->run();
|
54 |
+
( new Auditors\Upgrades() )
|
55 |
+
->setMod( $this->getMod() )
|
56 |
+
->run();
|
57 |
+
}
|
58 |
+
|
59 |
+
/**
|
60 |
+
* @return \ICWP_WPSF_Processor_AuditTrail_Auditor|mixed
|
61 |
+
*/
|
62 |
+
public function getSubProAuditor() {
|
63 |
+
return new \ICWP_WPSF_Processor_AuditTrail_Auditor( $this->getMod() );
|
64 |
+
}
|
65 |
+
}
|
src/lib/src/Modules/AuditTrail/Strings.php
CHANGED
@@ -20,12 +20,18 @@ class Strings extends Base\Strings {
|
|
20 |
'plugin_file_edited' => [
|
21 |
__( 'An attempt was made to edit the plugin file "%s" directly through the WordPress editor.', 'wp-simple-firewall' )
|
22 |
],
|
|
|
|
|
|
|
23 |
'theme_activated' => [
|
24 |
__( 'Theme "%s" was activated.', 'wp-simple-firewall' )
|
25 |
],
|
26 |
'theme_file_edited' => [
|
27 |
__( 'An attempt was made to edit the theme file "%s" directly through the WordPress editor.', 'wp-simple-firewall' )
|
28 |
],
|
|
|
|
|
|
|
29 |
'core_updated' => [
|
30 |
__( 'WordPress Core was updated from "%s" to "%s".', 'wp-simple-firewall' )
|
31 |
],
|
20 |
'plugin_file_edited' => [
|
21 |
__( 'An attempt was made to edit the plugin file "%s" directly through the WordPress editor.', 'wp-simple-firewall' )
|
22 |
],
|
23 |
+
'plugin_upgraded' => [
|
24 |
+
__( 'Plugin "%s" was upgraded from version %s to version %s.', 'wp-simple-firewall' )
|
25 |
+
],
|
26 |
'theme_activated' => [
|
27 |
__( 'Theme "%s" was activated.', 'wp-simple-firewall' )
|
28 |
],
|
29 |
'theme_file_edited' => [
|
30 |
__( 'An attempt was made to edit the theme file "%s" directly through the WordPress editor.', 'wp-simple-firewall' )
|
31 |
],
|
32 |
+
'theme_upgraded' => [
|
33 |
+
__( 'Theme "%s" was upgraded from version %s to version %s.', 'wp-simple-firewall' )
|
34 |
+
],
|
35 |
'core_updated' => [
|
36 |
__( 'WordPress Core was updated from "%s" to "%s".', 'wp-simple-firewall' )
|
37 |
],
|
src/lib/src/Modules/AuditTrail/UI.php
CHANGED
@@ -4,13 +4,13 @@ namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\AuditTrail;
|
|
4 |
|
5 |
use FernleafSystems\Wordpress\Plugin\Shield\Databases;
|
6 |
use FernleafSystems\Wordpress\Plugin\Shield\Modules;
|
7 |
-
use FernleafSystems\Wordpress\Plugin\Shield\Modules\
|
8 |
|
9 |
-
class UI extends
|
10 |
|
11 |
-
public function
|
12 |
$con = $this->getCon();
|
13 |
-
/** @var
|
14 |
$mod = $this->getMod();
|
15 |
/** @var Databases\AuditTrail\Select $dbSel */
|
16 |
$dbSel = $mod->getDbHandler_AuditTrail()->getQuerySelector();
|
@@ -20,33 +20,44 @@ class UI extends Base\ShieldUI {
|
|
20 |
$aEventsSelect = array_intersect_key( $oEventStrings->getEventNames(), array_flip( $dbSel->getDistinctEvents() ) );
|
21 |
asort( $aEventsSelect );
|
22 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
23 |
return [
|
24 |
-
|
25 |
-
'
|
26 |
-
'
|
27 |
-
]
|
28 |
-
'flags' => [],
|
29 |
-
'strings' => [
|
30 |
-
'table_title' => sprintf( '%s: %s', __( 'Logs', 'wp-simple-firewall' ), __( 'Audit Trail', 'wp-simple-firewall' ) ),
|
31 |
-
'sub_title' => __( 'Use the Audit Trail Glossary for help interpreting log entries.', 'wp-simple-firewall' ),
|
32 |
-
'audit_trail_glossary' => __( 'Audit Trail Glossary', 'wp-simple-firewall' ),
|
33 |
-
'title_filter_form' => __( 'Audit Trail Filters', 'wp-simple-firewall' ),
|
34 |
-
'username_ignores' => __( "Providing a username will cause the 'logged-in' filter to be ignored.", 'wp-simple-firewall' ),
|
35 |
-
'exclude_your_ip' => __( 'Exclude Your Current IP', 'wp-simple-firewall' ),
|
36 |
-
'exclude_your_ip_tooltip' => __( 'Exclude Your IP From Results', 'wp-simple-firewall' ),
|
37 |
-
'context' => __( 'Context', 'wp-simple-firewall' ),
|
38 |
-
'event' => __( 'Event', 'wp-simple-firewall' ),
|
39 |
-
'show_after' => __( 'show results that occurred after', 'wp-simple-firewall' ),
|
40 |
-
'show_before' => __( 'show results that occurred before', 'wp-simple-firewall' ),
|
41 |
-
],
|
42 |
-
'vars' => [
|
43 |
-
'events_for_select' => $aEventsSelect,
|
44 |
-
'unique_ips' => $dbSel->getDistinctIps(),
|
45 |
-
'unique_users' => $dbSel->getDistinctUsernames(),
|
46 |
-
],
|
47 |
-
'hrefs' => [
|
48 |
-
'audit_trail_glossary' => 'https://shsec.io/audittrailglossary',
|
49 |
-
],
|
50 |
];
|
51 |
}
|
52 |
}
|
4 |
|
5 |
use FernleafSystems\Wordpress\Plugin\Shield\Databases;
|
6 |
use FernleafSystems\Wordpress\Plugin\Shield\Modules;
|
7 |
+
use FernleafSystems\Wordpress\Plugin\Shield\Modules\BaseShield;
|
8 |
|
9 |
+
class UI extends BaseShield\UI {
|
10 |
|
11 |
+
public function renderAuditTrailTable() :string {
|
12 |
$con = $this->getCon();
|
13 |
+
/** @var ModCon $mod */
|
14 |
$mod = $this->getMod();
|
15 |
/** @var Databases\AuditTrail\Select $dbSel */
|
16 |
$dbSel = $mod->getDbHandler_AuditTrail()->getQuerySelector();
|
20 |
$aEventsSelect = array_intersect_key( $oEventStrings->getEventNames(), array_flip( $dbSel->getDistinctEvents() ) );
|
21 |
asort( $aEventsSelect );
|
22 |
|
23 |
+
return $this->getMod()
|
24 |
+
->renderTemplate(
|
25 |
+
'/wpadmin_pages/insights/audit/audit_table.twig',
|
26 |
+
[
|
27 |
+
'ajax' => [
|
28 |
+
'render_table_audittrail' => $mod->getAjaxActionData( 'render_table_audittrail', true ),
|
29 |
+
'item_addparamwhite' => $mod->getAjaxActionData( 'item_addparamwhite', true )
|
30 |
+
],
|
31 |
+
'flags' => [],
|
32 |
+
'strings' => [
|
33 |
+
'table_title' => sprintf( '%s: %s', __( 'Logs', 'wp-simple-firewall' ), __( 'Audit Trail', 'wp-simple-firewall' ) ),
|
34 |
+
'sub_title' => __( 'Use the Audit Trail Glossary for help interpreting log entries.', 'wp-simple-firewall' ),
|
35 |
+
'title_filter_form' => __( 'Audit Trail Filters', 'wp-simple-firewall' ),
|
36 |
+
'username_ignores' => __( "Providing a username will cause the 'logged-in' filter to be ignored.", 'wp-simple-firewall' ),
|
37 |
+
'exclude_your_ip' => __( 'Exclude Your Current IP', 'wp-simple-firewall' ),
|
38 |
+
'exclude_your_ip_tooltip' => __( 'Exclude Your IP From Results', 'wp-simple-firewall' ),
|
39 |
+
'context' => __( 'Context', 'wp-simple-firewall' ),
|
40 |
+
'event' => __( 'Event', 'wp-simple-firewall' ),
|
41 |
+
'show_after' => __( 'show results that occurred after', 'wp-simple-firewall' ),
|
42 |
+
'show_before' => __( 'show results that occurred before', 'wp-simple-firewall' ),
|
43 |
+
],
|
44 |
+
'vars' => [
|
45 |
+
'events_for_select' => $aEventsSelect,
|
46 |
+
'unique_ips' => $dbSel->getDistinctIps(),
|
47 |
+
'unique_users' => $dbSel->getDistinctUsernames(),
|
48 |
+
],
|
49 |
+
],
|
50 |
+
true
|
51 |
+
);
|
52 |
+
}
|
53 |
+
|
54 |
+
protected function getSettingsRelatedLinks() :array {
|
55 |
+
$modInsights = $this->getCon()->getModule_Insights();
|
56 |
return [
|
57 |
+
[
|
58 |
+
'href' => $modInsights->getUrl_SubInsightsPage( 'audit' ),
|
59 |
+
'title' => __( 'View Audit Trail', 'wp-simple-firewall' ),
|
60 |
+
]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
61 |
];
|
62 |
}
|
63 |
}
|
src/lib/src/Modules/AuditTrail/WpCli.php
CHANGED
@@ -10,7 +10,7 @@ class WpCli extends Base\WpCli {
|
|
10 |
/**
|
11 |
* @inheritDoc
|
12 |
*/
|
13 |
-
protected function getCmdHandlers() {
|
14 |
return [
|
15 |
new AuditTrail\WpCli\Display()
|
16 |
];
|
10 |
/**
|
11 |
* @inheritDoc
|
12 |
*/
|
13 |
+
protected function getCmdHandlers() :array {
|
14 |
return [
|
15 |
new AuditTrail\WpCli\Display()
|
16 |
];
|
src/lib/src/Modules/AuditTrail/WpCli/Display.php
CHANGED
@@ -2,6 +2,7 @@
|
|
2 |
|
3 |
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\AuditTrail\WpCli;
|
4 |
|
|
|
5 |
use FernleafSystems\Wordpress\Plugin\Shield\Modules\Base;
|
6 |
use FernleafSystems\Wordpress\Plugin\Shield\Tables;
|
7 |
use WP_CLI;
|
@@ -63,11 +64,11 @@ class Display extends Base\WpCli\BaseWpCliCmd {
|
|
63 |
* @throws WP_CLI\ExitException
|
64 |
*/
|
65 |
public function cmdDisplay( array $null, array $aA ) {
|
66 |
-
/** @var
|
67 |
-
$
|
68 |
$oTableBuilder = ( new Tables\Build\AuditTrail() )
|
69 |
-
->setMod( $
|
70 |
-
->setDbHandler( $
|
71 |
( new Tables\Render\WpCliTable\AuditTrail() )
|
72 |
->setDataBuilder( $oTableBuilder )
|
73 |
->render();
|
2 |
|
3 |
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\AuditTrail\WpCli;
|
4 |
|
5 |
+
use FernleafSystems\Wordpress\Plugin\Shield\Modules\AuditTrail\ModCon;
|
6 |
use FernleafSystems\Wordpress\Plugin\Shield\Modules\Base;
|
7 |
use FernleafSystems\Wordpress\Plugin\Shield\Tables;
|
8 |
use WP_CLI;
|
64 |
* @throws WP_CLI\ExitException
|
65 |
*/
|
66 |
public function cmdDisplay( array $null, array $aA ) {
|
67 |
+
/** @var ModCon $mod */
|
68 |
+
$mod = $this->getMod();
|
69 |
$oTableBuilder = ( new Tables\Build\AuditTrail() )
|
70 |
+
->setMod( $mod )
|
71 |
+
->setDbHandler( $mod->getDbHandler_AuditTrail() );
|
72 |
( new Tables\Render\WpCliTable\AuditTrail() )
|
73 |
->setDataBuilder( $oTableBuilder )
|
74 |
->render();
|
src/lib/src/Modules/Autoupdates/AjaxHandler.php
CHANGED
@@ -1,9 +1,9 @@
|
|
1 |
-
<?php
|
2 |
|
3 |
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Autoupdates;
|
4 |
|
5 |
use FernleafSystems\Wordpress\Plugin\Shield;
|
6 |
|
7 |
-
class AjaxHandler extends Shield\Modules\
|
8 |
|
9 |
}
|
1 |
+
<?php declare( strict_types=1 );
|
2 |
|
3 |
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Autoupdates;
|
4 |
|
5 |
use FernleafSystems\Wordpress\Plugin\Shield;
|
6 |
|
7 |
+
class AjaxHandler extends Shield\Modules\BaseShield\AjaxHandler {
|
8 |
|
9 |
}
|
src/lib/src/Modules/Autoupdates/ModCon.php
ADDED
@@ -0,0 +1,9 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php declare( strict_types=1 );
|
2 |
+
|
3 |
+
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Autoupdates;
|
4 |
+
|
5 |
+
use FernleafSystems\Wordpress\Plugin\Shield\Modules\BaseShield;
|
6 |
+
|
7 |
+
class ModCon extends BaseShield\ModCon {
|
8 |
+
|
9 |
+
}
|
src/lib/src/Modules/Autoupdates/Options.php
CHANGED
@@ -2,10 +2,10 @@
|
|
2 |
|
3 |
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Autoupdates;
|
4 |
|
5 |
-
use FernleafSystems\Wordpress\Plugin\Shield\Modules\
|
6 |
use FernleafSystems\Wordpress\Services\Services;
|
7 |
|
8 |
-
class Options extends
|
9 |
|
10 |
/**
|
11 |
* @return array
|
2 |
|
3 |
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Autoupdates;
|
4 |
|
5 |
+
use FernleafSystems\Wordpress\Plugin\Shield\Modules\BaseShield;
|
6 |
use FernleafSystems\Wordpress\Services\Services;
|
7 |
|
8 |
+
class Options extends BaseShield\Options {
|
9 |
|
10 |
/**
|
11 |
* @return array
|
src/lib/src/Modules/Autoupdates/Processor.php
ADDED
@@ -0,0 +1,453 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Autoupdates;
|
4 |
+
|
5 |
+
use FernleafSystems\Wordpress\Plugin\Shield\Modules\BaseShield;
|
6 |
+
use FernleafSystems\Wordpress\Services\Services;
|
7 |
+
|
8 |
+
class Processor extends BaseShield\Processor {
|
9 |
+
|
10 |
+
/**
|
11 |
+
* @var array
|
12 |
+
*/
|
13 |
+
private $assetsVersions = [];
|
14 |
+
|
15 |
+
/**
|
16 |
+
* The allow_* core filters are run first in a "should_update" query. Then comes the "auto_update_core"
|
17 |
+
* filter. What this filter decides will ultimately determine the fate of any core upgrade.
|
18 |
+
*/
|
19 |
+
protected function run() {
|
20 |
+
/** @var Options $oOpts */
|
21 |
+
$oOpts = $this->getOptions();
|
22 |
+
|
23 |
+
$nPriority = $this->getHookPriority();
|
24 |
+
if ( Services::WpGeneral()->isClassicPress() ) {
|
25 |
+
add_filter( 'allow_patch_auto_core_updates', [ $this, 'autoupdate_core_minor' ], $nPriority );
|
26 |
+
add_filter( 'allow_minor_auto_core_updates', [ $this, 'autoupdate_core_major' ], $nPriority );
|
27 |
+
}
|
28 |
+
else {
|
29 |
+
add_filter( 'allow_minor_auto_core_updates', [ $this, 'autoupdate_core_minor' ], $nPriority );
|
30 |
+
add_filter( 'allow_major_auto_core_updates', [ $this, 'autoupdate_core_major' ], $nPriority );
|
31 |
+
}
|
32 |
+
|
33 |
+
add_filter( 'auto_update_plugin', [ $this, 'autoupdate_plugins' ], $nPriority, 2 );
|
34 |
+
add_filter( 'auto_update_theme', [ $this, 'autoupdate_themes' ], $nPriority, 2 );
|
35 |
+
add_filter( 'auto_update_core', [ $this, 'autoupdate_core' ], $nPriority, 2 );
|
36 |
+
|
37 |
+
if ( !$oOpts->isDisableAllAutoUpdates() ) {
|
38 |
+
//more parameter options here for later
|
39 |
+
add_filter( 'auto_core_update_send_email', [ $this, 'autoupdate_send_email' ], $nPriority, 1 );
|
40 |
+
add_filter( 'auto_core_update_email', [ $this, 'autoupdate_email_override' ], $nPriority, 1 );
|
41 |
+
add_filter( 'auto_plugin_theme_update_email', [ $this, 'autoupdate_email_override' ], $nPriority, 1 );
|
42 |
+
|
43 |
+
add_action( 'set_site_transient_update_core', [ $this, 'trackUpdateTimesCore' ] );
|
44 |
+
add_action( 'set_site_transient_update_plugins', [ $this, 'trackUpdateTimesPlugins' ] );
|
45 |
+
add_action( 'set_site_transient_update_themes', [ $this, 'trackUpdateTimesThemes' ] );
|
46 |
+
|
47 |
+
if ( $oOpts->isSendAutoupdatesNotificationEmail()
|
48 |
+
&& !Services::WpGeneral()->getWordpressIsAtLeastVersion( '5.5' ) ) {
|
49 |
+
$this->trackAssetsVersions();
|
50 |
+
add_action( 'automatic_updates_complete', [ $this, 'sendNotificationEmail' ] );
|
51 |
+
}
|
52 |
+
}
|
53 |
+
}
|
54 |
+
|
55 |
+
public function onWpLoaded() {
|
56 |
+
/** @var Options $opts */
|
57 |
+
$opts = $this->getOptions();
|
58 |
+
if ( $opts->isDisableAllAutoUpdates() ) {
|
59 |
+
$this->disableAllAutoUpdates();
|
60 |
+
}
|
61 |
+
}
|
62 |
+
|
63 |
+
private function disableAllAutoUpdates() {
|
64 |
+
remove_all_filters( 'automatic_updater_disabled' );
|
65 |
+
add_filter( 'automatic_updater_disabled', '__return_true', PHP_INT_MAX );
|
66 |
+
if ( !defined( 'WP_AUTO_UPDATE_CORE' ) ) {
|
67 |
+
define( 'WP_AUTO_UPDATE_CORE', false );
|
68 |
+
}
|
69 |
+
}
|
70 |
+
|
71 |
+
private function trackAssetsVersions() {
|
72 |
+
$aAssVers = $this->getTrackedAssetsVersions();
|
73 |
+
|
74 |
+
$oWpPlugins = Services::WpPlugins();
|
75 |
+
foreach ( array_keys( $oWpPlugins->getUpdates() ) as $sFile ) {
|
76 |
+
$aAssVers[ 'plugins' ][ $sFile ] = $oWpPlugins->getPluginAsVo( $sFile )->Version;
|
77 |
+
}
|
78 |
+
$oWpThemes = Services::WpThemes();
|
79 |
+
foreach ( array_keys( $oWpThemes->getUpdates() ) as $sFile ) {
|
80 |
+
$aAssVers[ 'themes' ][ $sFile ] = $oWpThemes->getTheme( $sFile )->get( 'Version' );
|
81 |
+
}
|
82 |
+
$this->assetsVersions = $aAssVers;
|
83 |
+
}
|
84 |
+
|
85 |
+
protected function getTrackedAssetsVersions() :array {
|
86 |
+
if ( empty( $this->assetsVersions ) || !is_array( $this->assetsVersions ) ) {
|
87 |
+
$this->assetsVersions = [
|
88 |
+
'plugins' => [],
|
89 |
+
'themes' => [],
|
90 |
+
];
|
91 |
+
}
|
92 |
+
return $this->assetsVersions;
|
93 |
+
}
|
94 |
+
|
95 |
+
/**
|
96 |
+
* @param \stdClass $oUpdates
|
97 |
+
*/
|
98 |
+
public function trackUpdateTimesCore( $oUpdates ) {
|
99 |
+
|
100 |
+
if ( !empty( $oUpdates ) && isset( $oUpdates->updates ) && is_array( $oUpdates->updates ) ) {
|
101 |
+
/** @var Options $oOpts */
|
102 |
+
$oOpts = $this->getOptions();
|
103 |
+
|
104 |
+
$aTk = $oOpts->getDelayTracking();
|
105 |
+
$aItemTk = isset( $aTk[ 'core' ][ 'wp' ] ) ? $aTk[ 'core' ][ 'wp' ] : [];
|
106 |
+
foreach ( $oUpdates->updates as $oUpdate ) {
|
107 |
+
if ( 'autoupdate' == $oUpdate->response ) {
|
108 |
+
$sVersion = $oUpdate->current;
|
109 |
+
if ( !isset( $aItemTk[ $sVersion ] ) ) {
|
110 |
+
$aItemTk[ $sVersion ] = Services::Request()->ts();
|
111 |
+
}
|
112 |
+
}
|
113 |
+
}
|
114 |
+
$aTk[ 'core' ][ 'wp' ] = array_slice( $aItemTk, -5 );
|
115 |
+
$oOpts->setDelayTracking( $aTk );
|
116 |
+
}
|
117 |
+
}
|
118 |
+
|
119 |
+
/**
|
120 |
+
* @param \stdClass $oUpdates
|
121 |
+
*/
|
122 |
+
public function trackUpdateTimesPlugins( $oUpdates ) {
|
123 |
+
$this->trackUpdateTimeCommon( $oUpdates, 'plugins' );
|
124 |
+
}
|
125 |
+
|
126 |
+
/**
|
127 |
+
* @param \stdClass $oUpdates
|
128 |
+
*/
|
129 |
+
public function trackUpdateTimesThemes( $oUpdates ) {
|
130 |
+
$this->trackUpdateTimeCommon( $oUpdates, 'themes' );
|
131 |
+
}
|
132 |
+
|
133 |
+
/**
|
134 |
+
* @param \stdClass $oUpdates
|
135 |
+
* @param string $sContext - plugins/themes
|
136 |
+
*/
|
137 |
+
protected function trackUpdateTimeCommon( $oUpdates, $sContext ) {
|
138 |
+
/** @var Options $oOpts */
|
139 |
+
$oOpts = $this->getOptions();
|
140 |
+
|
141 |
+
if ( !empty( $oUpdates ) && isset( $oUpdates->response ) && is_array( $oUpdates->response ) ) {
|
142 |
+
|
143 |
+
$aTk = $oOpts->getDelayTracking();
|
144 |
+
foreach ( $oUpdates->response as $sSlug => $oUpdate ) {
|
145 |
+
$aItemTk = isset( $aTk[ $sContext ][ $sSlug ] ) ? $aTk[ $sContext ][ $sSlug ] : [];
|
146 |
+
if ( is_array( $oUpdate ) ) {
|
147 |
+
$oUpdate = (object)$oUpdate;
|
148 |
+
}
|
149 |
+
|
150 |
+
$sNewVersion = isset( $oUpdate->new_version ) ? $oUpdate->new_version : '';
|
151 |
+
if ( !empty( $sNewVersion ) ) {
|
152 |
+
if ( !isset( $aItemTk[ $sNewVersion ] ) ) {
|
153 |
+
$aItemTk[ $sNewVersion ] = Services::Request()->ts();
|
154 |
+
}
|
155 |
+
$aTk[ $sContext ][ $sSlug ] = array_slice( $aItemTk, -3 );
|
156 |
+
}
|
157 |
+
}
|
158 |
+
$oOpts->setDelayTracking( $aTk );
|
159 |
+
}
|
160 |
+
}
|
161 |
+
|
162 |
+
/**
|
163 |
+
* This is a filter method designed to say whether a major core WordPress upgrade should be permitted,
|
164 |
+
* based on the plugin settings.
|
165 |
+
* @param bool $bUpdate
|
166 |
+
* @return bool
|
167 |
+
*/
|
168 |
+
public function autoupdate_core_major( $bUpdate ) {
|
169 |
+
/** @var Options $oOpts */
|
170 |
+
$oOpts = $this->getOptions();
|
171 |
+
|
172 |
+
if ( $oOpts->isDisableAllAutoUpdates() || $oOpts->isAutoUpdateCoreNever() ) {
|
173 |
+
$bUpdate = false;
|
174 |
+
}
|
175 |
+
elseif ( !$oOpts->isDelayUpdates() ) { // delay handled elsewhere
|
176 |
+
$bUpdate = $oOpts->isAutoUpdateCoreMajor();
|
177 |
+
}
|
178 |
+
|
179 |
+
return $bUpdate;
|
180 |
+
}
|
181 |
+
|
182 |
+
/**
|
183 |
+
* This is a filter method designed to say whether a minor core WordPress upgrade should be permitted,
|
184 |
+
* based on the plugin settings.
|
185 |
+
* @param bool $bUpdate
|
186 |
+
* @return bool
|
187 |
+
*/
|
188 |
+
public function autoupdate_core_minor( $bUpdate ) {
|
189 |
+
/** @var Options $oOpts */
|
190 |
+
$oOpts = $this->getOptions();
|
191 |
+
|
192 |
+
if ( $oOpts->isDisableAllAutoUpdates() || $oOpts->isAutoUpdateCoreNever() ) {
|
193 |
+
$bUpdate = false;
|
194 |
+
}
|
195 |
+
elseif ( !$oOpts->isDelayUpdates() ) {
|
196 |
+
$bUpdate = !$oOpts->isAutoUpdateCoreNever();
|
197 |
+
}
|
198 |
+
return $bUpdate;
|
199 |
+
}
|
200 |
+
|
201 |
+
/**
|
202 |
+
* @param bool $bDoAutoUpdate
|
203 |
+
* @param \stdClass $oCoreUpdate
|
204 |
+
* @return bool
|
205 |
+
*/
|
206 |
+
public function autoupdate_core( $bDoAutoUpdate, $oCoreUpdate ) {
|
207 |
+
/** @var Options $oOpts */
|
208 |
+
$oOpts = $this->getOptions();
|
209 |
+
|
210 |
+
if ( $oOpts->isDisableAllAutoUpdates() ) {
|
211 |
+
$bDoAutoUpdate = false;
|
212 |
+
}
|
213 |
+
elseif ( $this->isDelayed( $oCoreUpdate, 'core' ) ) {
|
214 |
+
$bDoAutoUpdate = false;
|
215 |
+
}
|
216 |
+
|
217 |
+
return $bDoAutoUpdate;
|
218 |
+
}
|
219 |
+
|
220 |
+
/**
|
221 |
+
* @param bool $bDoAutoUpdate
|
222 |
+
* @param \stdClass|string $mItem
|
223 |
+
* @return bool
|
224 |
+
*/
|
225 |
+
public function autoupdate_plugins( $bDoAutoUpdate, $mItem ) {
|
226 |
+
/** @var Options $oOpts */
|
227 |
+
$oOpts = $this->getOptions();
|
228 |
+
|
229 |
+
if ( $oOpts->isDisableAllAutoUpdates() ) {
|
230 |
+
$bDoAutoUpdate = false;
|
231 |
+
}
|
232 |
+
else {
|
233 |
+
$file = Services::WpGeneral()->getFileFromAutomaticUpdateItem( $mItem );
|
234 |
+
|
235 |
+
if ( $this->isDelayed( $file, 'plugins' ) ) {
|
236 |
+
$bDoAutoUpdate = false;
|
237 |
+
}
|
238 |
+
elseif ( $oOpts->isAutoupdateAllPlugins() ) {
|
239 |
+
$bDoAutoUpdate = true;
|
240 |
+
}
|
241 |
+
elseif ( $file === $this->getCon()->getPluginBaseFile() ) {
|
242 |
+
$sAuto = $oOpts->getSelfAutoUpdateOpt();
|
243 |
+
if ( $sAuto === 'immediate' ) {
|
244 |
+
$bDoAutoUpdate = true;
|
245 |
+
}
|
246 |
+
elseif ( $sAuto === 'disabled' ) {
|
247 |
+
$bDoAutoUpdate = false;
|
248 |
+
}
|
249 |
+
}
|
250 |
+
}
|
251 |
+
|
252 |
+
return $bDoAutoUpdate;
|
253 |
+
}
|
254 |
+
|
255 |
+
/**
|
256 |
+
* @param bool $bDoAutoUpdate
|
257 |
+
* @param \stdClass|string $mItem
|
258 |
+
* @return bool
|
259 |
+
*/
|
260 |
+
public function autoupdate_themes( $bDoAutoUpdate, $mItem ) {
|
261 |
+
/** @var Options $opts */
|
262 |
+
$opts = $this->getOptions();
|
263 |
+
|
264 |
+
if ( $opts->isDisableAllAutoUpdates() ) {
|
265 |
+
$bDoAutoUpdate = false;
|
266 |
+
}
|
267 |
+
else {
|
268 |
+
$file = Services::WpGeneral()->getFileFromAutomaticUpdateItem( $mItem, 'theme' );
|
269 |
+
|
270 |
+
if ( $this->isDelayed( $file, 'themes' ) ) {
|
271 |
+
$bDoAutoUpdate = false;
|
272 |
+
}
|
273 |
+
elseif ( $opts->isOpt( 'enable_autoupdate_themes', 'Y' ) ) {
|
274 |
+
$bDoAutoUpdate = true;
|
275 |
+
}
|
276 |
+
}
|
277 |
+
|
278 |
+
return $bDoAutoUpdate;
|
279 |
+
}
|
280 |
+
|
281 |
+
/**
|
282 |
+
* @param string|\stdClass $sSlug
|
283 |
+
* @param string $sContext
|
284 |
+
* @return bool
|
285 |
+
*/
|
286 |
+
private function isDelayed( $sSlug, $sContext = 'plugins' ) {
|
287 |
+
/** @var Options $oOpts */
|
288 |
+
$oOpts = $this->getOptions();
|
289 |
+
|
290 |
+
$bDelayed = false;
|
291 |
+
|
292 |
+
if ( $oOpts->isDelayUpdates() ) {
|
293 |
+
|
294 |
+
$aTk = $oOpts->getDelayTracking();
|
295 |
+
|
296 |
+
$sVersion = '';
|
297 |
+
if ( $sContext == 'core' ) {
|
298 |
+
$sVersion = $sSlug->current; // \stdClass from transient update_core
|
299 |
+
$sSlug = 'wp';
|
300 |
+
}
|
301 |
+
|
302 |
+
$aItemTk = isset( $aTk[ $sContext ][ $sSlug ] ) ? $aTk[ $sContext ][ $sSlug ] : [];
|
303 |
+
|
304 |
+
if ( $sContext == 'plugins' ) {
|
305 |
+
$oPlugin = Services::WpPlugins()->getUpdateInfo( $sSlug );
|
306 |
+
$sVersion = isset( $oPlugin->new_version ) ? $oPlugin->new_version : '';
|
307 |
+
}
|
308 |
+
elseif ( $sContext == 'themes' ) {
|
309 |
+
$aThemeInfo = Services::WpThemes()->getUpdateInfo( $sSlug );
|
310 |
+
$sVersion = isset( $aThemeInfo[ 'new_version' ] ) ? $aThemeInfo[ 'new_version' ] : '';
|
311 |
+
}
|
312 |
+
|
313 |
+
if ( !empty( $sVersion ) && isset( $aItemTk[ $sVersion ] ) ) {
|
314 |
+
$bDelayed = ( Services::Request()->ts() - $aItemTk[ $sVersion ] < $oOpts->getDelayUpdatesPeriod() );
|
315 |
+
}
|
316 |
+
}
|
317 |
+
|
318 |
+
return $bDelayed;
|
319 |
+
}
|
320 |
+
|
321 |
+
/**
|
322 |
+
* A filter on whether or not a notification email is sent after core upgrades are attempted.
|
323 |
+
* @param bool $bSendEmail
|
324 |
+
* @return bool
|
325 |
+
*/
|
326 |
+
public function autoupdate_send_email( $bSendEmail ) {
|
327 |
+
/** @var Options $opts */
|
328 |
+
$opts = $this->getOptions();
|
329 |
+
return $opts->isSendAutoupdatesNotificationEmail();
|
330 |
+
}
|
331 |
+
|
332 |
+
/**
|
333 |
+
* A filter on the target email address to which to send upgrade notification emails.
|
334 |
+
* @param array $aEmailParams
|
335 |
+
* @return array
|
336 |
+
*/
|
337 |
+
public function autoupdate_email_override( $aEmailParams ) {
|
338 |
+
$sOverride = $this->getOptions()->getOpt( 'override_email_address', '' );
|
339 |
+
if ( Services::Data()->validEmail( $sOverride ) ) {
|
340 |
+
$aEmailParams[ 'to' ] = $sOverride;
|
341 |
+
}
|
342 |
+
return $aEmailParams;
|
343 |
+
}
|
344 |
+
|
345 |
+
/**
|
346 |
+
* @param array $aUpdateResults
|
347 |
+
*/
|
348 |
+
public function sendNotificationEmail( $aUpdateResults ) {
|
349 |
+
if ( empty( $aUpdateResults ) || !is_array( $aUpdateResults ) ) {
|
350 |
+
return;
|
351 |
+
}
|
352 |
+
|
353 |
+
// Are there really updates?
|
354 |
+
$bReallyUpdates = false;
|
355 |
+
|
356 |
+
$body = [
|
357 |
+
sprintf(
|
358 |
+
__( 'This is a quick notification from the %s that WordPress Automatic Updates just completed on your site with the following results.', 'wp-simple-firewall' ),
|
359 |
+
$this->getCon()->getHumanName()
|
360 |
+
),
|
361 |
+
''
|
362 |
+
];
|
363 |
+
|
364 |
+
$aTrkd = $this->getTrackedAssetsVersions();
|
365 |
+
|
366 |
+
$oWpPlugins = Services::WpPlugins();
|
367 |
+
if ( !empty( $aUpdateResults[ 'plugin' ] ) && is_array( $aUpdateResults[ 'plugin' ] ) ) {
|
368 |
+
$bHasPluginUpdates = false;
|
369 |
+
$aTrkdPlugs = $aTrkd[ 'plugins' ];
|
370 |
+
|
371 |
+
$aTempContent[] = __( 'Plugins Updated:', 'wp-simple-firewall' );
|
372 |
+
foreach ( $aUpdateResults[ 'plugin' ] as $oUpdate ) {
|
373 |
+
$oP = $oWpPlugins->getPluginAsVo( $oUpdate->item->plugin, true );
|
374 |
+
$bValidUpdate = !empty( $oUpdate->result ) && !empty( $oUpdate->name )
|
375 |
+
&& isset( $aTrkdPlugs[ $oP->file ] )
|
376 |
+
&& version_compare( $aTrkdPlugs[ $oP->file ], $oP->Version, '<' );
|
377 |
+
if ( $bValidUpdate ) {
|
378 |
+
$aTempContent[] = ' - '.sprintf(
|
379 |
+
__( 'Plugin "%s" auto-updated from "%s" to version "%s"', 'wp-simple-firewall' ),
|
380 |
+
$oUpdate->name, $aTrkdPlugs[ $oP->file ], $oP->Version );
|
381 |
+
$bHasPluginUpdates = true;
|
382 |
+
}
|
383 |
+
}
|
384 |
+
$aTempContent[] = '';
|
385 |
+
|
386 |
+
if ( $bHasPluginUpdates ) {
|
387 |
+
$bReallyUpdates = true;
|
388 |
+
$body = array_merge( $body, $aTempContent );
|
389 |
+
}
|
390 |
+
}
|
391 |
+
|
392 |
+
if ( !empty( $aUpdateResults[ 'theme' ] ) && is_array( $aUpdateResults[ 'theme' ] ) ) {
|
393 |
+
$bHasThemesUpdates = false;
|
394 |
+
$aTrkdThemes = $aTrkd[ 'themes' ];
|
395 |
+
|
396 |
+
$aTempContent = [ __( 'Themes Updated:', 'wp-simple-firewall' ) ];
|
397 |
+
foreach ( $aUpdateResults[ 'theme' ] as $oUpdate ) {
|
398 |
+
$oItem = $oUpdate->item;
|
399 |
+
$bValidUpdate = isset( $oUpdate->result ) && $oUpdate->result && !empty( $oUpdate->name )
|
400 |
+
&& isset( $aTrkdThemes[ $oItem->theme ] )
|
401 |
+
&& version_compare( $aTrkdThemes[ $oItem->theme ], $oItem->new_version, '<' );
|
402 |
+
if ( $bValidUpdate ) {
|
403 |
+
$aTempContent[] = ' - '.sprintf(
|
404 |
+
__( 'Theme "%s" auto-updated from "%s" to version "%s"', 'wp-simple-firewall' ),
|
405 |
+
$oUpdate->name, $aTrkdThemes[ $oItem->theme ], $oItem->new_version );
|
406 |
+
$bHasThemesUpdates = true;
|
407 |
+
}
|
408 |
+
}
|
409 |
+
$aTempContent[] = '';
|
410 |
+
|
411 |
+
if ( $bHasThemesUpdates ) {
|
412 |
+
$bReallyUpdates = true;
|
413 |
+
$body = array_merge( $body, $aTempContent );
|
414 |
+
}
|
415 |
+
}
|
416 |
+
|
417 |
+
if ( !empty( $aUpdateResults[ 'core' ] ) && is_array( $aUpdateResults[ 'core' ] ) ) {
|
418 |
+
$bHasCoreUpdates = false;
|
419 |
+
$aTempContent = [ __( 'WordPress Core Updated:', 'wp-simple-firewall' ) ];
|
420 |
+
foreach ( $aUpdateResults[ 'core' ] as $oUpdate ) {
|
421 |
+
if ( isset( $oUpdate->result ) && !is_wp_error( $oUpdate->result ) ) {
|
422 |
+
$aTempContent[] = ' - '.sprintf( 'WordPress was automatically updated to "%s"', $oUpdate->name );
|
423 |
+
$bHasCoreUpdates = true;
|
424 |
+
}
|
425 |
+
}
|
426 |
+
$aTempContent[] = '';
|
427 |
+
|
428 |
+
if ( $bHasCoreUpdates ) {
|
429 |
+
$bReallyUpdates = true;
|
430 |
+
$body = array_merge( $body, $aTempContent );
|
431 |
+
}
|
432 |
+
}
|
433 |
+
|
434 |
+
if ( !$bReallyUpdates ) {
|
435 |
+
return;
|
436 |
+
}
|
437 |
+
|
438 |
+
$body[] = __( 'Thank you.', 'wp-simple-firewall' );
|
439 |
+
|
440 |
+
$this->getMod()
|
441 |
+
->getEmailProcessor()
|
442 |
+
->sendEmailWithWrap(
|
443 |
+
$this->getOptions()->getOpt( 'override_email_address' ),
|
444 |
+
sprintf( __( "Notice: %s", 'wp-simple-firewall' ), __( "Automatic Updates Completed", 'wp-simple-firewall' ) ),
|
445 |
+
$body
|
446 |
+
);
|
447 |
+
die();
|
448 |
+
}
|
449 |
+
|
450 |
+
private function getHookPriority() :int {
|
451 |
+
return (int)$this->getOptions()->getDef( 'action_hook_priority' );
|
452 |
+
}
|
453 |
+
}
|
src/lib/src/Modules/Autoupdates/UI.php
CHANGED
@@ -2,9 +2,8 @@
|
|
2 |
|
3 |
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Autoupdates;
|
4 |
|
5 |
-
use FernleafSystems\Wordpress\Plugin\Shield\Modules\
|
6 |
-
use FernleafSystems\Wordpress\Services\Services;
|
7 |
|
8 |
-
class UI extends
|
9 |
|
10 |
}
|
2 |
|
3 |
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Autoupdates;
|
4 |
|
5 |
+
use FernleafSystems\Wordpress\Plugin\Shield\Modules\BaseShield;
|
|
|
6 |
|
7 |
+
class UI extends BaseShield\UI {
|
8 |
|
9 |
}
|
src/lib/src/Modules/Base/AdminNotices.php
CHANGED
@@ -3,6 +3,7 @@
|
|
3 |
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Base;
|
4 |
|
5 |
use FernleafSystems\Wordpress\Plugin\Shield;
|
|
|
6 |
use FernleafSystems\Wordpress\Services\Services;
|
7 |
use FernleafSystems\Wordpress\Services\Utilities\PluginUserMeta;
|
8 |
|
@@ -17,15 +18,11 @@ class AdminNotices {
|
|
17 |
add_filter( $this->getCon()->prefix( 'ajaxAuthAction' ), [ $this, 'handleAuthAjax' ] );
|
18 |
}
|
19 |
|
20 |
-
|
21 |
-
|
22 |
-
|
23 |
-
*/
|
24 |
-
public function handleAuthAjax( $aAjaxResponse ) {
|
25 |
-
if ( empty( $aAjaxResponse ) && Services::Request()->request( 'exec' ) === 'dismiss_admin_notice' ) {
|
26 |
-
$aAjaxResponse = $this->ajaxExec_DismissAdminNotice();
|
27 |
}
|
28 |
-
return $
|
29 |
}
|
30 |
|
31 |
protected function ajaxExec_DismissAdminNotice() :array {
|
@@ -33,13 +30,13 @@ class AdminNotices {
|
|
33 |
|
34 |
$sNoticeId = sanitize_key( Services::Request()->query( 'notice_id', '' ) );
|
35 |
|
36 |
-
foreach ( $this->getAdminNotices() as $
|
37 |
-
if ( $sNoticeId == $
|
38 |
-
$this->setNoticeDismissed( $
|
39 |
$aAjaxResponse = [
|
40 |
'success' => true,
|
41 |
'message' => 'Admin notice dismissed', //not currently seen
|
42 |
-
'notice_id' => $
|
43 |
];
|
44 |
break;
|
45 |
}
|
@@ -81,37 +78,34 @@ class AdminNotices {
|
|
81 |
}
|
82 |
|
83 |
/**
|
84 |
-
* @return
|
85 |
*/
|
86 |
-
protected function getAdminNotices() {
|
87 |
return array_map(
|
88 |
-
function ( $
|
89 |
-
$
|
90 |
-
|
91 |
-
|
92 |
-
|
93 |
-
|
94 |
-
|
95 |
-
|
96 |
-
|
97 |
-
|
98 |
-
|
99 |
-
|
100 |
-
|
101 |
-
|
102 |
-
|
103 |
-
|
104 |
-
|
105 |
-
return ( new
|
106 |
},
|
107 |
$this->getOptions()->getAdminNotices()
|
108 |
);
|
109 |
}
|
110 |
|
111 |
-
|
112 |
-
* @param Shield\Utilities\AdminNotices\NoticeVO $notice
|
113 |
-
*/
|
114 |
-
protected function preProcessNotice( $notice ) {
|
115 |
$con = $this->getCon();
|
116 |
$opts = $this->getOptions();
|
117 |
|
@@ -152,7 +146,7 @@ class AdminNotices {
|
|
152 |
}
|
153 |
|
154 |
/**
|
155 |
-
* @param
|
156 |
* @return bool
|
157 |
*/
|
158 |
protected function isNoticeDismissed( $notice ) {
|
@@ -169,15 +163,15 @@ class AdminNotices {
|
|
169 |
}
|
170 |
|
171 |
/**
|
172 |
-
* @param
|
173 |
* @return bool
|
174 |
*/
|
175 |
-
protected function isDisplayNeeded( $
|
176 |
return true;
|
177 |
}
|
178 |
|
179 |
/**
|
180 |
-
* @param
|
181 |
* @return bool
|
182 |
*/
|
183 |
protected function isNoticeDismissedForCurrentUser( $notice ) {
|
@@ -201,15 +195,15 @@ class AdminNotices {
|
|
201 |
}
|
202 |
|
203 |
/**
|
204 |
-
* @param
|
205 |
* @throws \Exception
|
206 |
*/
|
207 |
-
protected function processNotice( $
|
208 |
-
throw new \Exception( 'Unsupported Notice ID: '.$
|
209 |
}
|
210 |
|
211 |
/**
|
212 |
-
* @param
|
213 |
* @return $this
|
214 |
*/
|
215 |
protected function setNoticeDismissed( $notice ) {
|
@@ -238,7 +232,7 @@ class AdminNotices {
|
|
238 |
}
|
239 |
|
240 |
/**
|
241 |
-
* @param
|
242 |
* @return string
|
243 |
*/
|
244 |
private function getNoticeMetaKey( $notice ) {
|
3 |
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Base;
|
4 |
|
5 |
use FernleafSystems\Wordpress\Plugin\Shield;
|
6 |
+
use FernleafSystems\Wordpress\Plugin\Shield\Utilities\AdminNotices\NoticeVO;
|
7 |
use FernleafSystems\Wordpress\Services\Services;
|
8 |
use FernleafSystems\Wordpress\Services\Utilities\PluginUserMeta;
|
9 |
|
18 |
add_filter( $this->getCon()->prefix( 'ajaxAuthAction' ), [ $this, 'handleAuthAjax' ] );
|
19 |
}
|
20 |
|
21 |
+
public function handleAuthAjax( array $ajaxResponse ) :array {
|
22 |
+
if ( empty( $ajaxResponse ) && Services::Request()->request( 'exec' ) === 'dismiss_admin_notice' ) {
|
23 |
+
$ajaxResponse = $this->ajaxExec_DismissAdminNotice();
|
|
|
|
|
|
|
|
|
24 |
}
|
25 |
+
return $ajaxResponse;
|
26 |
}
|
27 |
|
28 |
protected function ajaxExec_DismissAdminNotice() :array {
|
30 |
|
31 |
$sNoticeId = sanitize_key( Services::Request()->query( 'notice_id', '' ) );
|
32 |
|
33 |
+
foreach ( $this->getAdminNotices() as $notice ) {
|
34 |
+
if ( $sNoticeId == $notice->id ) {
|
35 |
+
$this->setNoticeDismissed( $notice );
|
36 |
$aAjaxResponse = [
|
37 |
'success' => true,
|
38 |
'message' => 'Admin notice dismissed', //not currently seen
|
39 |
+
'notice_id' => $notice->id,
|
40 |
];
|
41 |
break;
|
42 |
}
|
78 |
}
|
79 |
|
80 |
/**
|
81 |
+
* @return NoticeVO[]
|
82 |
*/
|
83 |
+
protected function getAdminNotices() :array {
|
84 |
return array_map(
|
85 |
+
function ( $noticeDef ) {
|
86 |
+
$noticeDef = Services::DataManipulation()
|
87 |
+
->mergeArraysRecursive(
|
88 |
+
[
|
89 |
+
'schedule' => 'conditions',
|
90 |
+
'type' => 'promo',
|
91 |
+
'plugin_page_only' => true,
|
92 |
+
'valid_admin' => true,
|
93 |
+
'plugin_admin' => 'yes',
|
94 |
+
'can_dismiss' => true,
|
95 |
+
'per_user' => false,
|
96 |
+
'display' => false,
|
97 |
+
'min_install_days' => 0,
|
98 |
+
'twig' => true,
|
99 |
+
],
|
100 |
+
$noticeDef
|
101 |
+
);
|
102 |
+
return ( new NoticeVO() )->applyFromArray( $noticeDef );
|
103 |
},
|
104 |
$this->getOptions()->getAdminNotices()
|
105 |
);
|
106 |
}
|
107 |
|
108 |
+
protected function preProcessNotice( NoticeVO $notice ) {
|
|
|
|
|
|
|
109 |
$con = $this->getCon();
|
110 |
$opts = $this->getOptions();
|
111 |
|
146 |
}
|
147 |
|
148 |
/**
|
149 |
+
* @param NoticeVO $notice
|
150 |
* @return bool
|
151 |
*/
|
152 |
protected function isNoticeDismissed( $notice ) {
|
163 |
}
|
164 |
|
165 |
/**
|
166 |
+
* @param NoticeVO $notice
|
167 |
* @return bool
|
168 |
*/
|
169 |
+
protected function isDisplayNeeded( NoticeVO $notice ) :bool {
|
170 |
return true;
|
171 |
}
|
172 |
|
173 |
/**
|
174 |
+
* @param NoticeVO $notice
|
175 |
* @return bool
|
176 |
*/
|
177 |
protected function isNoticeDismissedForCurrentUser( $notice ) {
|
195 |
}
|
196 |
|
197 |
/**
|
198 |
+
* @param NoticeVO $notice
|
199 |
* @throws \Exception
|
200 |
*/
|
201 |
+
protected function processNotice( NoticeVO $notice ) {
|
202 |
+
throw new \Exception( 'Unsupported Notice ID: '.$notice->id );
|
203 |
}
|
204 |
|
205 |
/**
|
206 |
+
* @param NoticeVO $notice
|
207 |
* @return $this
|
208 |
*/
|
209 |
protected function setNoticeDismissed( $notice ) {
|
232 |
}
|
233 |
|
234 |
/**
|
235 |
+
* @param NoticeVO $notice
|
236 |
* @return string
|
237 |
*/
|
238 |
private function getNoticeMetaKey( $notice ) {
|
src/lib/src/Modules/Base/AjaxHandler.php
ADDED
@@ -0,0 +1,95 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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 |
+
abstract class AjaxHandler {
|
9 |
+
|
10 |
+
use ModConsumer;
|
11 |
+
|
12 |
+
public function __construct() {
|
13 |
+
add_action( 'wp_loaded', [ $this, 'init' ] );
|
14 |
+
}
|
15 |
+
|
16 |
+
public function init() {
|
17 |
+
add_filter( $this->getCon()->prefix( 'ajaxAuthAction' ), [ $this, 'handleAjaxAuth' ], 10, 2 );
|
18 |
+
add_filter( $this->getCon()->prefix( 'ajaxNonAuthAction' ), [ $this, 'handleAjaxNonAuth' ], 10, 2 );
|
19 |
+
}
|
20 |
+
|
21 |
+
public function handleAjaxAuth( array $ajaxResponse, string $ajaxAction ) {
|
22 |
+
if ( !empty( $ajaxAction ) && ( empty( $ajaxResponse ) || !is_array( $ajaxResponse ) ) ) {
|
23 |
+
$ajaxResponse = $this->normaliseAjaxResponse( $this->processAjaxAction( $ajaxAction ) );
|
24 |
+
}
|
25 |
+
return $ajaxResponse;
|
26 |
+
}
|
27 |
+
|
28 |
+
public function handleAjaxNonAuth( array $ajaxResponse, string $ajaxAction ) :array {
|
29 |
+
if ( !empty( $ajaxAction ) && ( empty( $ajaxResponse ) || !is_array( $ajaxResponse ) ) ) {
|
30 |
+
$ajaxResponse = $this->normaliseAjaxResponse( $this->processAjaxAction( $ajaxAction ) );
|
31 |
+
}
|
32 |
+
return $ajaxResponse;
|
33 |
+
}
|
34 |
+
|
35 |
+
/**
|
36 |
+
* @param string $encoding
|
37 |
+
* @return array
|
38 |
+
*/
|
39 |
+
protected function getAjaxFormParams( $encoding = 'none' ) {
|
40 |
+
$req = Services::Request();
|
41 |
+
$aFormParams = [];
|
42 |
+
$sRaw = $req->post( 'form_params', '' );
|
43 |
+
|
44 |
+
if ( !empty( $sRaw ) ) {
|
45 |
+
|
46 |
+
$sMaybeEncoding = $req->post( 'enc_params' );
|
47 |
+
if ( in_array( $sMaybeEncoding, [ 'none', 'lz-string', 'b64' ] ) ) {
|
48 |
+
$encoding = $sMaybeEncoding;
|
49 |
+
}
|
50 |
+
|
51 |
+
switch ( $encoding ) {
|
52 |
+
case 'lz-string':
|
53 |
+
$sRaw = \LZCompressor\LZString::decompress( base64_decode( $sRaw ) );
|
54 |
+
break;
|
55 |
+
|
56 |
+
case 'b64':
|
57 |
+
$sRaw = base64_decode( $sRaw );
|
58 |
+
break;
|
59 |
+
|
60 |
+
case 'none':
|
61 |
+
default:
|
62 |
+
break;
|
63 |
+
}
|
64 |
+
|
65 |
+
parse_str( $sRaw, $aFormParams );
|
66 |
+
}
|
67 |
+
return $aFormParams;
|
68 |
+
}
|
69 |
+
|
70 |
+
protected function processAjaxAction( string $action ) :array {
|
71 |
+
return [];
|
72 |
+
}
|
73 |
+
|
74 |
+
/**
|
75 |
+
* We check for empty since if it's empty, there's nothing to normalize. It's a filter,
|
76 |
+
* so if we send something back non-empty, it'll be treated like a "handled" response and
|
77 |
+
* processing will finish
|
78 |
+
* @param array $ajaxResponse
|
79 |
+
* @return array
|
80 |
+
*/
|
81 |
+
protected function normaliseAjaxResponse( array $ajaxResponse ) {
|
82 |
+
if ( !empty( $ajaxResponse ) ) {
|
83 |
+
$ajaxResponse = array_merge(
|
84 |
+
[
|
85 |
+
'success' => false,
|
86 |
+
'page_reload' => false,
|
87 |
+
'message' => 'Unknown',
|
88 |
+
'html' => '',
|
89 |
+
],
|
90 |
+
$ajaxResponse
|
91 |
+
);
|
92 |
+
}
|
93 |
+
return $ajaxResponse;
|
94 |
+
}
|
95 |
+
}
|
src/lib/src/Modules/Base/AjaxHandlerBase.php
CHANGED
@@ -5,7 +5,12 @@ namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Base;
|
|
5 |
use FernleafSystems\Wordpress\Plugin\Shield\Modules\ModConsumer;
|
6 |
use FernleafSystems\Wordpress\Services\Services;
|
7 |
|
8 |
-
|
|
|
|
|
|
|
|
|
|
|
9 |
|
10 |
use ModConsumer;
|
11 |
|
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 |
|
src/lib/src/Modules/Base/AjaxHandlerShield.php
CHANGED
@@ -2,6 +2,11 @@
|
|
2 |
|
3 |
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Base;
|
4 |
|
|
|
|
|
|
|
|
|
|
|
5 |
class AjaxHandlerShield extends AjaxHandlerBase {
|
6 |
|
7 |
protected function processAjaxAction( string $action ) :array {
|
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 {
|
src/lib/src/Modules/Base/BaseProcessor.php
CHANGED
@@ -5,6 +5,11 @@ namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Base;
|
|
5 |
use FernleafSystems\Wordpress\Plugin\Shield\Modules;
|
6 |
use FernleafSystems\Wordpress\Services\Services;
|
7 |
|
|
|
|
|
|
|
|
|
|
|
8 |
class BaseProcessor {
|
9 |
|
10 |
use Modules\ModConsumer;
|
@@ -25,10 +30,10 @@ class BaseProcessor {
|
|
25 |
private $bHasExecuted;
|
26 |
|
27 |
/**
|
28 |
-
* @param
|
29 |
*/
|
30 |
-
public function __construct( $
|
31 |
-
$this->setMod( $
|
32 |
|
33 |
add_action( 'init', [ $this, 'onWpInit' ], 9 );
|
34 |
add_action( 'wp_loaded', [ $this, 'onWpLoaded' ] );
|
@@ -38,10 +43,10 @@ class BaseProcessor {
|
|
38 |
add_action( 'set_logged_in_cookie', [ $this, 'onWpSetLoggedInCookie' ], 5, 4 );
|
39 |
}
|
40 |
}
|
41 |
-
add_action( $
|
42 |
-
add_action( $
|
43 |
-
add_action( $
|
44 |
-
add_action( $
|
45 |
|
46 |
/**
|
47 |
* 2019-04-19:
|
5 |
use FernleafSystems\Wordpress\Plugin\Shield\Modules;
|
6 |
use FernleafSystems\Wordpress\Services\Services;
|
7 |
|
8 |
+
/**
|
9 |
+
* Class BaseProcessor
|
10 |
+
* @package FernleafSystems\Wordpress\Plugin\Shield\Modules\Base
|
11 |
+
* @deprecated 10.1
|
12 |
+
*/
|
13 |
class BaseProcessor {
|
14 |
|
15 |
use Modules\ModConsumer;
|
30 |
private $bHasExecuted;
|
31 |
|
32 |
/**
|
33 |
+
* @param ModCon $mod
|
34 |
*/
|
35 |
+
public function __construct( $mod ) {
|
36 |
+
$this->setMod( $mod );
|
37 |
|
38 |
add_action( 'init', [ $this, 'onWpInit' ], 9 );
|
39 |
add_action( 'wp_loaded', [ $this, 'onWpLoaded' ] );
|
43 |
add_action( 'set_logged_in_cookie', [ $this, 'onWpSetLoggedInCookie' ], 5, 4 );
|
44 |
}
|
45 |
}
|
46 |
+
add_action( $mod->prefix( 'plugin_shutdown' ), [ $this, 'onModuleShutdown' ] );
|
47 |
+
add_action( $mod->prefix( 'daily_cron' ), [ $this, 'runDailyCron' ] );
|
48 |
+
add_action( $mod->prefix( 'hourly_cron' ), [ $this, 'runHourlyCron' ] );
|
49 |
+
add_action( $mod->prefix( 'deactivate_plugin' ), [ $this, 'deactivatePlugin' ] );
|
50 |
|
51 |
/**
|
52 |
* 2019-04-19:
|
src/lib/src/Modules/Base/BaseReporting.php
CHANGED
@@ -5,6 +5,11 @@ namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Base;
|
|
5 |
use FernleafSystems\Wordpress\Plugin\Shield\Modules\ModConsumer;
|
6 |
use FernleafSystems\Wordpress\Plugin\Shield\Modules\Reporting\Lib\Reports;
|
7 |
|
|
|
|
|
|
|
|
|
|
|
8 |
abstract class BaseReporting {
|
9 |
|
10 |
use ModConsumer;
|
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;
|
src/lib/src/Modules/Base/{BaseModCon.php → ModCon.php}
RENAMED
@@ -6,9 +6,14 @@ use FernleafSystems\Wordpress\Plugin\Shield;
|
|
6 |
use FernleafSystems\Wordpress\Plugin\Shield\Modules;
|
7 |
use FernleafSystems\Wordpress\Services\Services;
|
8 |
|
9 |
-
|
|
|
|
|
|
|
|
|
10 |
|
11 |
use Modules\PluginControllerConsumer;
|
|
|
12 |
|
13 |
/**
|
14 |
* @var string
|
@@ -26,12 +31,7 @@ class BaseModCon {
|
|
26 |
protected $bImportExportWhitelistNotify = false;
|
27 |
|
28 |
/**
|
29 |
-
* @var \
|
30 |
-
*/
|
31 |
-
private static $oEmailHandler;
|
32 |
-
|
33 |
-
/**
|
34 |
-
* @var BaseProcessor
|
35 |
*/
|
36 |
private $oProcessor;
|
37 |
|
@@ -41,35 +41,40 @@ class BaseModCon {
|
|
41 |
private $oWizard;
|
42 |
|
43 |
/**
|
44 |
-
* @var Shield\
|
45 |
*/
|
46 |
-
private $
|
47 |
|
48 |
/**
|
49 |
-
* @var Shield\Modules\Base\
|
50 |
*/
|
51 |
-
private $
|
52 |
|
53 |
/**
|
54 |
* @var Shield\Modules\Base\Options
|
55 |
*/
|
56 |
private $oOpts;
|
57 |
|
|
|
|
|
|
|
|
|
|
|
58 |
/**
|
59 |
* @var Shield\Databases\Base\Handler[]
|
60 |
*/
|
61 |
private $aDbHandlers;
|
62 |
|
63 |
/**
|
64 |
-
* @param Shield\Controller\Controller $
|
65 |
* @param array $aMod
|
66 |
* @throws \Exception
|
67 |
*/
|
68 |
-
public function __construct( $
|
69 |
-
if ( !$
|
70 |
throw new \Exception( 'Plugin controller not supplied to Module' );
|
71 |
}
|
72 |
-
$this->setCon( $
|
73 |
|
74 |
if ( empty( $aMod[ 'storage_key' ] ) && empty( $aMod[ 'slug' ] ) ) {
|
75 |
throw new \Exception( 'Module storage key AND slug are undefined' );
|
@@ -81,59 +86,30 @@ class BaseModCon {
|
|
81 |
}
|
82 |
|
83 |
if ( $this->verifyModuleMeetRequirements() ) {
|
|
|
84 |
$this->setupHooks( $aMod );
|
85 |
$this->doPostConstruction();
|
86 |
}
|
87 |
}
|
88 |
|
89 |
-
|
90 |
-
|
91 |
-
*/
|
92 |
-
protected function setupHooks( $aModProps ) {
|
93 |
-
$oReq = Services::Request();
|
94 |
-
|
95 |
$nRunPriority = isset( $aModProps[ 'load_priority' ] ) ? $aModProps[ 'load_priority' ] : 100;
|
96 |
-
add_action( $this->prefix( 'run_processors' ), [ $this, 'onRunProcessors' ], $nRunPriority );
|
97 |
-
add_action( 'init', [ $this, 'onWpInit' ], 1 );
|
98 |
|
99 |
-
|
100 |
-
|
101 |
-
|
102 |
-
|
103 |
-
|
104 |
-
|
105 |
-
if ( $oReq->request( 'action' ) == $this->prefix()
|
106 |
-
&& check_admin_referer( $oReq->request( 'exec' ), 'exec_nonce' )
|
107 |
-
) {
|
108 |
-
add_action( $this->prefix( 'mod_request' ), [ $this, 'handleModRequest' ] );
|
109 |
-
}
|
110 |
-
}
|
111 |
|
112 |
$nMenuPri = isset( $aModProps[ 'menu_priority' ] ) ? $aModProps[ 'menu_priority' ] : 100;
|
113 |
-
add_filter( $
|
114 |
-
add_action( $
|
115 |
-
add_action( $
|
116 |
-
add_action( $
|
117 |
-
add_filter( $
|
118 |
-
|
119 |
-
add_filter( $
|
120 |
-
|
121 |
-
add_action( $this->prefix( 'daily_cron' ), [ $this, 'runDailyCron' ] );
|
122 |
-
add_action( $this->prefix( 'hourly_cron' ), [ $this, 'runHourlyCron' ] );
|
123 |
-
|
124 |
-
// supply our supported plugin events for this module
|
125 |
-
add_filter( $this->prefix( 'get_all_events' ), function ( $aEvents ) {
|
126 |
-
return array_merge(
|
127 |
-
is_array( $aEvents ) ? $aEvents : [],
|
128 |
-
array_map(
|
129 |
-
function ( $aEvt ) {
|
130 |
-
$aEvt[ 'context' ] = $this->getSlug();
|
131 |
-
return $aEvt;
|
132 |
-
},
|
133 |
-
is_array( $this->getDef( 'events' ) ) ? $this->getDef( 'events' ) : []
|
134 |
-
)
|
135 |
-
);
|
136 |
-
} );
|
137 |
|
138 |
add_action( 'admin_enqueue_scripts', [ $this, 'onWpEnqueueAdminJs' ], 100 );
|
139 |
|
@@ -144,7 +120,7 @@ class BaseModCon {
|
|
144 |
// if ( $this->isAdminOptionsPage() ) {
|
145 |
// add_action( 'current_screen', array( $this, 'onSetCurrentScreen' ) );
|
146 |
// }
|
147 |
-
|
148 |
$this->setupCustomHooks();
|
149 |
}
|
150 |
|
@@ -158,13 +134,10 @@ class BaseModCon {
|
|
158 |
$this->cleanupDatabases();
|
159 |
}
|
160 |
|
161 |
-
public function runHourlyCron() {
|
162 |
-
}
|
163 |
-
|
164 |
protected function cleanupDatabases() {
|
165 |
-
foreach ( $this->getDbHandlers( true ) as $
|
166 |
-
if ( $
|
167 |
-
$
|
168 |
}
|
169 |
}
|
170 |
}
|
@@ -183,49 +156,49 @@ class BaseModCon {
|
|
183 |
}
|
184 |
|
185 |
/**
|
186 |
-
* @param string $
|
187 |
* @return Shield\Databases\Base\Handler|mixed|false
|
188 |
*/
|
189 |
-
protected function getDbH( $
|
190 |
-
$
|
191 |
|
192 |
if ( !is_array( $this->aDbHandlers ) ) {
|
193 |
$this->aDbHandlers = [];
|
194 |
}
|
195 |
|
196 |
-
if ( !empty( $this->aDbHandlers[ $
|
197 |
-
$
|
198 |
}
|
199 |
else {
|
200 |
$aDbClasses = $this->getAllDbClasses();
|
201 |
-
if ( isset( $aDbClasses[ $
|
202 |
-
/** @var Shield\Databases\Base\Handler $
|
203 |
-
$
|
204 |
try {
|
205 |
-
$
|
206 |
}
|
207 |
-
catch ( \Exception $
|
208 |
}
|
209 |
}
|
210 |
-
$this->aDbHandlers[ $
|
211 |
}
|
212 |
|
213 |
-
return $
|
214 |
}
|
215 |
|
216 |
/**
|
217 |
* @return string[]
|
218 |
*/
|
219 |
private function getAllDbClasses() {
|
220 |
-
$
|
221 |
-
return is_array( $
|
222 |
}
|
223 |
|
224 |
/**
|
225 |
-
*
|
226 |
-
* Called upon construction and after plugin options are initialized.
|
227 |
*/
|
228 |
-
|
|
|
229 |
}
|
230 |
|
231 |
/**
|
@@ -274,10 +247,7 @@ class BaseModCon {
|
|
274 |
return array_merge( $aAdminNotices, $this->getOptions()->getAdminNotices() );
|
275 |
}
|
276 |
|
277 |
-
|
278 |
-
* @return bool
|
279 |
-
*/
|
280 |
-
private function verifyModuleMeetRequirements() {
|
281 |
$bMeetsReqs = true;
|
282 |
|
283 |
$aPhpReqs = $this->getOptions()->getFeatureRequirement( 'php' );
|
@@ -301,34 +271,67 @@ class BaseModCon {
|
|
301 |
return $bMeetsReqs;
|
302 |
}
|
303 |
|
|
|
|
|
|
|
304 |
public function onRunProcessors() {
|
305 |
-
|
306 |
-
|
307 |
-
}
|
308 |
-
if ( $this->getOptions()->getFeatureProperty( 'auto_load_processor' ) ) {
|
309 |
$this->loadProcessor();
|
310 |
}
|
311 |
-
|
312 |
-
$
|
|
|
|
|
|
|
|
|
|
|
313 |
}
|
314 |
}
|
315 |
|
316 |
/**
|
317 |
* @return bool
|
|
|
318 |
*/
|
319 |
-
protected function isReadyToExecute() {
|
320 |
-
return ( $this->getProcessor()
|
321 |
}
|
322 |
|
323 |
protected function doExecuteProcessor() {
|
324 |
$this->getProcessor()->execute();
|
325 |
}
|
326 |
|
327 |
-
/**
|
328 |
-
* A action added to WordPress 'init' hook
|
329 |
-
*/
|
330 |
public function onWpInit() {
|
331 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
332 |
|
333 |
$this->runWizards();
|
334 |
|
@@ -337,6 +340,8 @@ class BaseModCon {
|
|
337 |
add_filter( $this->prefix( 'wpPrivacyExport' ), [ $this, 'onWpPrivacyExport' ], 10, 3 );
|
338 |
add_filter( $this->prefix( 'wpPrivacyErase' ), [ $this, 'onWpPrivacyErase' ], 10, 3 );
|
339 |
}
|
|
|
|
|
340 |
}
|
341 |
|
342 |
/**
|
@@ -356,31 +361,37 @@ class BaseModCon {
|
|
356 |
|
357 |
/**
|
358 |
* Override this and adapt per feature
|
359 |
-
* @return BaseProcessor|mixed
|
360 |
*/
|
361 |
protected function loadProcessor() {
|
362 |
if ( !isset( $this->oProcessor ) ) {
|
363 |
-
|
364 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
365 |
return null;
|
366 |
}
|
367 |
-
$this->oProcessor = new $
|
368 |
}
|
369 |
return $this->oProcessor;
|
370 |
}
|
371 |
|
372 |
/**
|
373 |
-
*
|
374 |
-
* @
|
375 |
*/
|
376 |
-
protected function getProcessorClassName() {
|
377 |
-
return implode( '_',
|
378 |
-
|
379 |
-
|
380 |
-
|
381 |
-
|
382 |
-
|
383 |
-
|
384 |
}
|
385 |
|
386 |
/**
|
@@ -397,10 +408,7 @@ class BaseModCon {
|
|
397 |
);
|
398 |
}
|
399 |
|
400 |
-
|
401 |
-
* @return bool
|
402 |
-
*/
|
403 |
-
public function isUpgrading() {
|
404 |
return $this->getCon()->getIsRebuildOptionsFromFile() || $this->getOptions()->getRebuildFromFile();
|
405 |
}
|
406 |
|
@@ -417,24 +425,18 @@ class BaseModCon {
|
|
417 |
}
|
418 |
}
|
419 |
|
420 |
-
|
421 |
-
* @return string
|
422 |
-
*/
|
423 |
-
protected function getOptionsStorageKey() {
|
424 |
return $this->getCon()->prefixOption( $this->sOptionsStoreKey ).'_options';
|
425 |
}
|
426 |
|
427 |
/**
|
428 |
-
* @return BaseProcessor|mixed
|
429 |
*/
|
430 |
public function getProcessor() {
|
431 |
return $this->loadProcessor();
|
432 |
}
|
433 |
|
434 |
-
|
435 |
-
* @return string
|
436 |
-
*/
|
437 |
-
public function getUrl_AdminPage() {
|
438 |
return Services::WpGeneral()
|
439 |
->getUrl_AdminPage(
|
440 |
$this->getModSlug(),
|
@@ -443,113 +445,140 @@ class BaseModCon {
|
|
443 |
}
|
444 |
|
445 |
/**
|
446 |
-
* @param string $
|
447 |
* @return string
|
448 |
*/
|
449 |
-
public function
|
450 |
-
$
|
451 |
-
$
|
452 |
-
|
453 |
-
|
454 |
-
|
455 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
456 |
}
|
457 |
|
458 |
/**
|
459 |
-
* @
|
460 |
-
* @
|
461 |
*/
|
462 |
-
|
463 |
-
|
464 |
-
|
465 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
466 |
}
|
467 |
-
return $
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
468 |
}
|
469 |
|
470 |
/**
|
471 |
* TODO: Get rid of this crap and/or handle the \Exception thrown in loadFeatureHandler()
|
472 |
-
* @return \
|
473 |
* @throws \Exception
|
|
|
474 |
*/
|
475 |
public function getEmailHandler() {
|
476 |
-
|
477 |
-
self::$oEmailHandler = $this->getCon()->getModule( 'email' );
|
478 |
-
}
|
479 |
-
return self::$oEmailHandler;
|
480 |
}
|
481 |
|
482 |
/**
|
483 |
-
* @return \
|
484 |
*/
|
485 |
public function getEmailProcessor() {
|
486 |
return $this->getEmailHandler()->getProcessor();
|
487 |
}
|
488 |
|
489 |
/**
|
490 |
-
* @param bool $
|
491 |
* @return $this
|
492 |
*/
|
493 |
-
public function setIsMainFeatureEnabled( $
|
494 |
-
|
|
|
495 |
}
|
496 |
|
497 |
-
|
498 |
-
* @return bool
|
499 |
-
*/
|
500 |
-
public function isModuleEnabled() {
|
501 |
-
$oOpts = $this->getOptions();
|
502 |
/** @var Shield\Modules\Plugin\Options $oPluginOpts */
|
503 |
$oPluginOpts = $this->getCon()->getModule_Plugin()->getOptions();
|
504 |
|
505 |
if ( $this->getOptions()->getFeatureProperty( 'auto_enabled' ) === true ) {
|
506 |
// Auto enabled modules always run regardless
|
507 |
-
$
|
508 |
}
|
509 |
elseif ( $oPluginOpts->isPluginGloballyDisabled() ) {
|
510 |
-
$
|
511 |
}
|
512 |
elseif ( $this->getCon()->getIfForceOffActive() ) {
|
513 |
-
$
|
514 |
}
|
515 |
-
elseif ( $
|
516 |
-
|
|
|
517 |
}
|
518 |
else {
|
519 |
-
$
|
520 |
}
|
521 |
|
522 |
-
return $
|
523 |
}
|
524 |
|
525 |
-
|
526 |
-
|
527 |
-
|
528 |
-
|
529 |
-
return $this->isOpt( $this->getEnableModOptKey(), 'Y' )
|
530 |
-
|| $this->isOpt( $this->getEnableModOptKey(), true, true );
|
531 |
}
|
532 |
|
533 |
-
|
534 |
-
* @return string
|
535 |
-
*/
|
536 |
-
protected function getEnableModOptKey() {
|
537 |
return 'enable_'.$this->getSlug();
|
538 |
}
|
539 |
|
540 |
-
|
541 |
-
* @return string
|
542 |
-
*/
|
543 |
-
public function getMainFeatureName() {
|
544 |
return __( $this->getOptions()->getFeatureProperty( 'name' ), 'wp-simple-firewall' );
|
545 |
}
|
546 |
|
547 |
-
|
548 |
-
|
549 |
-
* @return string
|
550 |
-
*/
|
551 |
-
public function getModSlug( $bWithPrefix = true ) {
|
552 |
-
return $bWithPrefix ? $this->prefix( $this->getSlug() ) : $this->getSlug();
|
553 |
}
|
554 |
|
555 |
/**
|
@@ -592,16 +621,12 @@ class BaseModCon {
|
|
592 |
if ( !empty( $aAdditionalItems ) && is_array( $aAdditionalItems ) ) {
|
593 |
|
594 |
foreach ( $aAdditionalItems as $aMenuItem ) {
|
595 |
-
|
596 |
-
if ( empty( $aMenuItem[ 'callback' ] ) || !method_exists( $this, $aMenuItem[ 'callback' ] ) ) {
|
597 |
-
continue;
|
598 |
-
}
|
599 |
-
|
600 |
$sMenuPageTitle = $sHumanName.' - '.$aMenuItem[ 'title' ];
|
601 |
$aItems[ $sMenuPageTitle ] = [
|
602 |
-
$aMenuItem[ 'title' ],
|
603 |
$this->prefix( $aMenuItem[ 'slug' ] ),
|
604 |
-
[ $this, $aMenuItem[ 'callback' ] ]
|
|
|
605 |
];
|
606 |
}
|
607 |
}
|
@@ -610,74 +635,111 @@ class BaseModCon {
|
|
610 |
}
|
611 |
|
612 |
/**
|
613 |
-
*
|
|
|
|
|
614 |
*/
|
615 |
-
|
616 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
617 |
}
|
618 |
|
619 |
/**
|
620 |
-
* @param array $aSummaryData
|
621 |
* @return array
|
622 |
*/
|
623 |
-
|
624 |
-
|
625 |
-
$aSummaryData[ $this->getModSlug( false ) ] = $this->buildSummaryData();
|
626 |
-
}
|
627 |
-
return $aSummaryData;
|
628 |
}
|
629 |
|
630 |
/**
|
631 |
-
*
|
632 |
-
* @return array
|
633 |
*/
|
634 |
-
public function
|
635 |
-
return
|
|
|
|
|
|
|
|
|
|
|
636 |
}
|
637 |
|
638 |
/**
|
639 |
-
* @param array $aAllData
|
640 |
* @return array
|
641 |
*/
|
642 |
-
public function
|
643 |
-
|
644 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
645 |
|
646 |
-
|
647 |
-
|
648 |
-
|
649 |
-
|
650 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
651 |
}
|
652 |
|
653 |
-
|
654 |
-
* @return bool
|
655 |
-
*/
|
656 |
-
public function getIfShowModuleMenuItem() {
|
657 |
return (bool)$this->getOptions()->getFeatureProperty( 'show_module_menu_item' );
|
658 |
}
|
659 |
|
660 |
-
|
661 |
-
* @return bool
|
662 |
-
*/
|
663 |
-
public function getIfShowModuleLink() {
|
664 |
return (bool)$this->getOptions()->getFeatureProperty( 'show_module_options' );
|
665 |
}
|
666 |
|
667 |
-
/**
|
668 |
-
* @return bool
|
669 |
-
*/
|
670 |
-
public function getIfUseSessions() {
|
671 |
-
return $this->getOptions()->getFeatureProperty( 'use_sessions' );
|
672 |
-
}
|
673 |
-
|
674 |
/**
|
675 |
* Get config 'definition'.
|
676 |
-
* @param string $
|
677 |
* @return mixed|null
|
678 |
*/
|
679 |
-
public function getDef( $
|
680 |
-
return $this->getOptions()->getDef( $
|
681 |
}
|
682 |
|
683 |
/**
|
@@ -692,59 +754,27 @@ class BaseModCon {
|
|
692 |
* @param string $sGlue
|
693 |
* @return string|array
|
694 |
*/
|
695 |
-
public function getLastErrors( $bAsString =
|
696 |
-
$
|
697 |
-
if ( !is_array( $
|
698 |
-
$
|
699 |
}
|
700 |
-
return $bAsString ? implode( $sGlue, $
|
701 |
}
|
702 |
|
703 |
-
|
704 |
-
* @return bool
|
705 |
-
*/
|
706 |
-
public function hasLastErrors() {
|
707 |
return count( $this->getLastErrors( false ) ) > 0;
|
708 |
}
|
709 |
|
710 |
-
|
711 |
-
|
712 |
-
* @param mixed $mDefault
|
713 |
-
* @return mixed
|
714 |
-
*/
|
715 |
-
public function getOpt( $sOptionKey, $mDefault = false ) {
|
716 |
-
return $this->getOptions()->getOpt( $sOptionKey, $mDefault );
|
717 |
-
}
|
718 |
-
|
719 |
-
/**
|
720 |
-
* @param string $sOptionKey
|
721 |
-
* @param mixed $mValueToTest
|
722 |
-
* @param bool $bStrict
|
723 |
-
* @return bool
|
724 |
-
*/
|
725 |
-
public function isOpt( $sOptionKey, $mValueToTest, $bStrict = false ) {
|
726 |
-
$mOptionValue = $this->getOptions()->getOpt( $sOptionKey );
|
727 |
-
return $bStrict ? $mOptionValue === $mValueToTest : $mOptionValue == $mValueToTest;
|
728 |
-
}
|
729 |
-
|
730 |
-
/**
|
731 |
-
* @param string $sOptKey
|
732 |
-
* @return string
|
733 |
-
*/
|
734 |
-
public function getTextOpt( $sOptKey ) {
|
735 |
-
$sValue = $this->getOpt( $sOptKey, 'default' );
|
736 |
if ( $sValue == 'default' ) {
|
737 |
-
$sValue = $this->getTextOptDefault( $
|
738 |
}
|
739 |
-
return $sValue;
|
740 |
}
|
741 |
|
742 |
-
|
743 |
-
* Override this on each feature that has Text field options to supply the text field defaults
|
744 |
-
* @param string $sOptKey
|
745 |
-
* @return string
|
746 |
-
*/
|
747 |
-
public function getTextOptDefault( $sOptKey ) {
|
748 |
return 'Undefined Text Opt Default';
|
749 |
}
|
750 |
|
@@ -761,47 +791,30 @@ class BaseModCon {
|
|
761 |
$mErrors = [];
|
762 |
}
|
763 |
}
|
764 |
-
|
765 |
-
}
|
766 |
-
|
767 |
-
/**
|
768 |
-
* Sets the value for the given option key
|
769 |
-
* Note: We also set the ability to bypass admin access since setOpt() is a protected function
|
770 |
-
* @param string $sOptionKey
|
771 |
-
* @param mixed $mValue
|
772 |
-
* @return $this
|
773 |
-
*/
|
774 |
-
protected function setOpt( $sOptionKey, $mValue ) {
|
775 |
-
$this->getOptions()->setOpt( $sOptionKey, $mValue );
|
776 |
return $this;
|
777 |
}
|
778 |
|
779 |
-
|
780 |
-
|
781 |
-
|
782 |
-
|
783 |
-
$oVO = $this->getOptions();
|
784 |
-
foreach ( $aOptions as $sKey => $mValue ) {
|
785 |
-
$oVO->setOpt( $sKey, $mValue );
|
786 |
}
|
787 |
}
|
788 |
|
789 |
-
|
790 |
-
|
791 |
-
*/
|
792 |
-
public function isModuleRequest() {
|
793 |
-
return ( $this->getModSlug() == Services::Request()->request( 'mod_slug' ) );
|
794 |
}
|
795 |
|
796 |
/**
|
797 |
-
* @param string $
|
798 |
-
* @param bool $
|
799 |
-
* @return array
|
800 |
*/
|
801 |
-
public function getAjaxActionData( $
|
802 |
-
$
|
803 |
-
$
|
804 |
-
return $
|
805 |
}
|
806 |
|
807 |
/**
|
@@ -817,33 +830,25 @@ class BaseModCon {
|
|
817 |
/**
|
818 |
* @return string[]
|
819 |
*/
|
820 |
-
public function getDismissedNotices() {
|
821 |
-
$
|
822 |
-
return is_array( $
|
823 |
}
|
824 |
|
825 |
/**
|
826 |
* @return string[]
|
827 |
*/
|
828 |
-
public function getUiTrack() {
|
829 |
-
$
|
830 |
-
return is_array( $
|
831 |
}
|
832 |
|
833 |
-
|
834 |
-
|
835 |
-
* @return $this
|
836 |
-
*/
|
837 |
-
public function setDismissedNotices( $aDismissed ) {
|
838 |
-
return $this->setOpt( 'dismissed_notices', $aDismissed );
|
839 |
}
|
840 |
|
841 |
-
|
842 |
-
|
843 |
-
* @return $this
|
844 |
-
*/
|
845 |
-
public function setUiTrack( $aDismissed ) {
|
846 |
-
return $this->setOpt( 'ui_track', $aDismissed );
|
847 |
}
|
848 |
|
849 |
/**
|
@@ -862,9 +867,15 @@ class BaseModCon {
|
|
862 |
}
|
863 |
|
864 |
/**
|
|
|
865 |
* @return $this
|
866 |
*/
|
867 |
-
public function saveModOptions() {
|
|
|
|
|
|
|
|
|
|
|
868 |
$this->doPrePluginOptionsSave();
|
869 |
if ( apply_filters( $this->prefix( 'force_options_resave' ), false ) ) {
|
870 |
$this->getOptions()
|
@@ -877,6 +888,9 @@ class BaseModCon {
|
|
877 |
return $this;
|
878 |
}
|
879 |
|
|
|
|
|
|
|
880 |
private function store() {
|
881 |
add_filter( $this->prefix( 'bypass_is_plugin_admin' ), '__return_true', 1000 );
|
882 |
$this->getOptions()
|
@@ -902,6 +916,11 @@ class BaseModCon {
|
|
902 |
}
|
903 |
|
904 |
public function onPluginDelete() {
|
|
|
|
|
|
|
|
|
|
|
905 |
$this->getOptions()->deleteStorage();
|
906 |
}
|
907 |
|
@@ -911,7 +930,7 @@ class BaseModCon {
|
|
911 |
protected function getAllFormOptionsAndTypes() {
|
912 |
$aOpts = [];
|
913 |
|
914 |
-
foreach ( $this->buildOptions() as $aOptionsSection ) {
|
915 |
if ( !empty( $aOptionsSection ) ) {
|
916 |
foreach ( $aOptionsSection[ 'options' ] as $aOption ) {
|
917 |
$aOpts[ $aOption[ 'key' ] ] = $aOption[ 'type' ];
|
@@ -922,7 +941,7 @@ class BaseModCon {
|
|
922 |
return $aOpts;
|
923 |
}
|
924 |
|
925 |
-
|
926 |
}
|
927 |
|
928 |
/**
|
@@ -932,11 +951,17 @@ class BaseModCon {
|
|
932 |
if ( !$this->getCon()->isPluginAdmin() ) {
|
933 |
throw new \Exception( __( "You don't currently have permission to save settings.", 'wp-simple-firewall' ) );
|
934 |
}
|
|
|
935 |
$this->doSaveStandardOptions();
|
936 |
-
$this->preProcessOptions();
|
937 |
-
}
|
938 |
|
939 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
940 |
}
|
941 |
|
942 |
/**
|
@@ -956,11 +981,8 @@ class BaseModCon {
|
|
956 |
return $this;
|
957 |
}
|
958 |
|
959 |
-
|
960 |
-
|
961 |
-
*/
|
962 |
-
protected function isAdminOptionsPage() {
|
963 |
-
return ( is_admin() && !Services::WpGeneral()->isAjax() && $this->isThisModulePage() );
|
964 |
}
|
965 |
|
966 |
/**
|
@@ -1021,23 +1043,20 @@ class BaseModCon {
|
|
1021 |
elseif ( $sOptType == 'comma_separated_lists' ) {
|
1022 |
$sOptionValue = Services::Data()->extractCommaSeparatedList( $sOptionValue );
|
1023 |
}
|
1024 |
-
elseif ( $sOptType == 'multiple_select' ) {
|
1025 |
-
}
|
1026 |
}
|
1027 |
|
1028 |
// Prevent overwriting of non-editable fields
|
1029 |
if ( !in_array( $sOptType, [ 'noneditable_text' ] ) ) {
|
1030 |
-
$this->setOpt( $sKey, $sOptionValue );
|
1031 |
}
|
1032 |
}
|
1033 |
|
1034 |
-
|
1035 |
-
|
1036 |
-
|
1037 |
-
|
1038 |
-
|
1039 |
-
wp_schedule_single_event( Services::Request()->ts() + 15, $this->prefix( 'importexport_notify' ) );
|
1040 |
-
}
|
1041 |
}
|
1042 |
}
|
1043 |
|
@@ -1097,24 +1116,11 @@ class BaseModCon {
|
|
1097 |
return $this->getCon()->prefix( $sSuffix, $sGlue );
|
1098 |
}
|
1099 |
|
1100 |
-
/**
|
1101 |
-
* @param string
|
1102 |
-
* @return string
|
1103 |
-
*/
|
1104 |
-
public function getOptionStoragePrefix() {
|
1105 |
-
return $this->getCon()->getOptionStoragePrefix();
|
1106 |
-
}
|
1107 |
-
|
1108 |
/**
|
1109 |
* @uses echo()
|
1110 |
*/
|
1111 |
public function displayModuleAdminPage() {
|
1112 |
-
|
1113 |
-
echo $this->renderModulePage();
|
1114 |
-
}
|
1115 |
-
else {
|
1116 |
-
echo $this->renderRestrictedPage();
|
1117 |
-
}
|
1118 |
}
|
1119 |
|
1120 |
/**
|
@@ -1122,103 +1128,18 @@ class BaseModCon {
|
|
1122 |
* @param array $aData
|
1123 |
* @return string
|
1124 |
*/
|
1125 |
-
protected function renderModulePage( $aData = [] ) {
|
1126 |
-
|
1127 |
-
|
1128 |
-
|
1129 |
-
|
1130 |
-
|
1131 |
-
return $this->renderTemplate( 'index.php', $aData );
|
1132 |
-
}
|
1133 |
-
|
1134 |
-
/**
|
1135 |
-
* @return string
|
1136 |
-
*/
|
1137 |
-
protected function renderRestrictedPage() {
|
1138 |
-
$aData = Services::DataManipulation()
|
1139 |
-
->mergeArraysRecursive(
|
1140 |
-
$this->getBaseDisplayData(),
|
1141 |
-
[
|
1142 |
-
'ajax' => [
|
1143 |
-
'restricted_access' => $this->getAjaxActionData( 'restricted_access' )
|
1144 |
-
]
|
1145 |
-
]
|
1146 |
-
);
|
1147 |
-
return $this->renderTemplate( '/wpadmin_pages/security_admin/index.twig', $aData, true );
|
1148 |
-
}
|
1149 |
-
|
1150 |
-
/**
|
1151 |
-
* @return array
|
1152 |
-
*/
|
1153 |
-
protected function getBaseDisplayData() {
|
1154 |
-
$oCon = $this->getCon();
|
1155 |
-
|
1156 |
-
return [
|
1157 |
-
'sPluginName' => $oCon->getHumanName(),
|
1158 |
-
'sTagline' => $this->getOptions()->getFeatureTagline(),
|
1159 |
-
'nonce_field' => wp_nonce_field( $oCon->getPluginPrefix(), '_wpnonce', true, false ), //don't echo!
|
1160 |
-
'form_action' => 'admin.php?page='.$this->getModSlug(),
|
1161 |
-
'aPluginLabels' => $oCon->getLabels(),
|
1162 |
-
'help_video' => [
|
1163 |
-
'auto_show' => $this->getIfAutoShowHelpVideo(),
|
1164 |
-
'iframe_url' => $this->getHelpVideoUrl( $this->getHelpVideoId() ),
|
1165 |
-
'display_id' => 'ShieldHelpVideo'.$this->getSlug(),
|
1166 |
-
'options' => $this->getHelpVideoOptions(),
|
1167 |
-
'displayable' => $this->isHelpVideoDisplayable(),
|
1168 |
-
'show' => $this->isHelpVideoDisplayable() && !$this->getHelpVideoHasBeenClosed(),
|
1169 |
-
'width' => 772,
|
1170 |
-
'height' => 454,
|
1171 |
-
],
|
1172 |
-
|
1173 |
-
// 'sPageTitle' => sprintf( '%s: %s', $oCon->getHumanName(), $this->getMainFeatureName() ),
|
1174 |
-
'sPageTitle' => $this->getMainFeatureName(),
|
1175 |
-
'data' => [
|
1176 |
-
'mod_slug' => $this->getModSlug( true ),
|
1177 |
-
'mod_slug_short' => $this->getModSlug( false ),
|
1178 |
-
'all_options' => $this->buildOptions(),
|
1179 |
-
'hidden_options' => $this->getOptions()->getHiddenOptions()
|
1180 |
-
],
|
1181 |
-
'ajax' => [
|
1182 |
-
'mod_options' => $this->getAjaxActionData( 'mod_options' ),
|
1183 |
-
],
|
1184 |
-
'strings' => $this->getStrings()->getDisplayStrings(),
|
1185 |
-
'flags' => [
|
1186 |
-
'access_restricted' => !$this->canDisplayOptionsForm(),
|
1187 |
-
'show_ads' => $this->getIsShowMarketing(),
|
1188 |
-
'wrap_page_content' => true,
|
1189 |
-
'show_standard_options' => true,
|
1190 |
-
'show_content_help' => true,
|
1191 |
-
'show_alt_content' => false,
|
1192 |
-
'has_wizard' => $this->hasWizard(),
|
1193 |
-
],
|
1194 |
-
'hrefs' => [
|
1195 |
-
'go_pro' => 'https://shsec.io/shieldgoprofeature',
|
1196 |
-
'goprofooter' => 'https://shsec.io/goprofooter',
|
1197 |
-
'wizard_link' => $this->getUrl_WizardLanding(),
|
1198 |
-
'wizard_landing' => $this->getUrl_WizardLanding()
|
1199 |
-
],
|
1200 |
-
'content' => [
|
1201 |
-
'options_form' => '',
|
1202 |
-
'alt' => '',
|
1203 |
-
'actions' => '',
|
1204 |
-
'help' => '',
|
1205 |
-
'wizard_landing' => ''
|
1206 |
-
]
|
1207 |
-
];
|
1208 |
-
}
|
1209 |
-
|
1210 |
-
/**
|
1211 |
-
* @return string
|
1212 |
-
*/
|
1213 |
-
protected function getContentHelp() {
|
1214 |
-
return $this->renderTemplate( 'snippets/module-help-template.php', $this->getBaseDisplayData() );
|
1215 |
}
|
1216 |
|
1217 |
/**
|
1218 |
* @return string
|
1219 |
*/
|
1220 |
protected function getContentWizardLanding() {
|
1221 |
-
$aData = $this->getBaseDisplayData();
|
1222 |
if ( $this->hasWizard() ) {
|
1223 |
$aData[ 'content' ][ 'wizard_landing' ] = $this->getWizardHandler()->renderWizardLandingSnippet();
|
1224 |
}
|
@@ -1273,7 +1194,7 @@ class BaseModCon {
|
|
1273 |
/**
|
1274 |
* @return string
|
1275 |
*/
|
1276 |
-
|
1277 |
return $this->getUrl_Wizard( 'landing' );
|
1278 |
}
|
1279 |
|
@@ -1298,11 +1219,8 @@ class BaseModCon {
|
|
1298 |
return is_array( $aW ) ? $aW : [];
|
1299 |
}
|
1300 |
|
1301 |
-
|
1302 |
-
|
1303 |
-
*/
|
1304 |
-
public function hasWizard() {
|
1305 |
-
return ( count( $this->getWizardDefinitions() ) > 0 );
|
1306 |
}
|
1307 |
|
1308 |
/**
|
@@ -1317,7 +1235,7 @@ class BaseModCon {
|
|
1317 |
/**
|
1318 |
* @return bool
|
1319 |
*/
|
1320 |
-
|
1321 |
return apply_filters( $this->prefix( 'show_marketing' ), !$this->isPremium() );
|
1322 |
}
|
1323 |
|
@@ -1337,7 +1255,7 @@ class BaseModCon {
|
|
1337 |
return $this->getCon()
|
1338 |
->getRenderer()
|
1339 |
->setTemplate( $sTemplate )
|
1340 |
-
->setRenderVars( $this->getBaseDisplayData() )
|
1341 |
->setTemplateEngineTwig()
|
1342 |
->render();
|
1343 |
}
|
@@ -1349,7 +1267,7 @@ class BaseModCon {
|
|
1349 |
/**
|
1350 |
* @return bool
|
1351 |
*/
|
1352 |
-
|
1353 |
return $this->getOptions()->isAccessRestricted() ? $this->getCon()->isPluginAdmin() : true;
|
1354 |
}
|
1355 |
|
@@ -1419,32 +1337,32 @@ class BaseModCon {
|
|
1419 |
return $this->renderTemplate( $sTemplate, $aData, $bTwig );
|
1420 |
}
|
1421 |
|
1422 |
-
|
1423 |
-
|
1424 |
-
|
1425 |
-
* @param bool $bUseTwig
|
1426 |
-
* @return string
|
1427 |
-
*/
|
1428 |
-
public function renderTemplate( $sTemplate, $aData = [], $bUseTwig = false ) {
|
1429 |
-
if ( empty( $aData[ 'unique_render_id' ] ) ) {
|
1430 |
-
$aData[ 'unique_render_id' ] = 'noticeid-'.substr( md5( mt_rand() ), 0, 5 );
|
1431 |
}
|
1432 |
try {
|
1433 |
$oRndr = $this->getCon()->getRenderer();
|
1434 |
-
if ( $
|
1435 |
$oRndr->setTemplateEngineTwig();
|
1436 |
}
|
1437 |
|
1438 |
-
$
|
1439 |
-
|
1440 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
1441 |
}
|
1442 |
catch ( \Exception $oE ) {
|
1443 |
-
$
|
1444 |
error_log( $oE->getMessage() );
|
1445 |
}
|
1446 |
|
1447 |
-
return $
|
1448 |
}
|
1449 |
|
1450 |
/**
|
@@ -1459,25 +1377,10 @@ class BaseModCon {
|
|
1459 |
return $aTransferableOptions;
|
1460 |
}
|
1461 |
|
1462 |
-
|
1463 |
-
|
1464 |
-
|
1465 |
-
|
1466 |
-
$oVO = $this->getOptions();
|
1467 |
-
$aOptionsData = $this->getOptions()->getOptionsMaskSensitive();
|
1468 |
-
foreach ( $aOptionsData as $sOption => $mValue ) {
|
1469 |
-
unset( $aOptionsData[ $sOption ] );
|
1470 |
-
// some cleaning to ensure we don't have disallowed characters
|
1471 |
-
$sOption = preg_replace( '#[^_a-z]#', '', strtolower( $sOption ) );
|
1472 |
-
$sType = $oVO->getOptionType( $sOption );
|
1473 |
-
if ( $sType == 'checkbox' ) { // only want a boolean 1 or 0
|
1474 |
-
$aOptionsData[ $sOption ] = (int)( $mValue == 'Y' );
|
1475 |
-
}
|
1476 |
-
else {
|
1477 |
-
$aOptionsData[ $sOption ] = $mValue;
|
1478 |
-
}
|
1479 |
-
}
|
1480 |
-
return $aOptionsData;
|
1481 |
}
|
1482 |
|
1483 |
/**
|
@@ -1502,160 +1405,172 @@ class BaseModCon {
|
|
1502 |
return $aData;
|
1503 |
}
|
1504 |
|
1505 |
-
|
1506 |
-
|
1507 |
-
|
1508 |
-
|
1509 |
-
|
1510 |
-
|
1511 |
-
|
1512 |
-
|
1513 |
-
|
|
|
|
|
1514 |
}
|
1515 |
-
return $
|
1516 |
}
|
1517 |
|
1518 |
/**
|
1519 |
-
* @return
|
|
|
1520 |
*/
|
1521 |
-
|
1522 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
1523 |
}
|
1524 |
|
1525 |
/**
|
1526 |
-
* @return
|
1527 |
*/
|
1528 |
-
|
1529 |
-
return
|
1530 |
}
|
1531 |
|
1532 |
/**
|
1533 |
-
* @return
|
1534 |
*/
|
1535 |
-
|
1536 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1537 |
}
|
1538 |
|
1539 |
/**
|
1540 |
-
* @
|
1541 |
-
* @return mixed|null
|
1542 |
*/
|
1543 |
-
|
1544 |
-
|
1545 |
-
|
|
|
|
|
1546 |
}
|
1547 |
|
1548 |
-
|
1549 |
-
|
1550 |
-
|
1551 |
-
|
1552 |
-
|
1553 |
}
|
1554 |
|
1555 |
-
|
1556 |
-
|
1557 |
-
*/
|
1558 |
-
protected function getHelpVideoId() {
|
1559 |
-
return $this->getDef( 'help_video_id' );
|
1560 |
}
|
1561 |
|
1562 |
/**
|
1563 |
-
* @
|
1564 |
-
* @
|
1565 |
*/
|
1566 |
-
protected function
|
1567 |
-
return
|
1568 |
}
|
1569 |
|
1570 |
-
|
1571 |
-
|
1572 |
-
|
1573 |
-
|
1574 |
-
|
|
|
|
|
|
|
1575 |
}
|
1576 |
|
1577 |
/**
|
1578 |
-
* @return
|
1579 |
*/
|
1580 |
-
|
1581 |
-
|
1582 |
-
$oCon = $this->getCon();
|
1583 |
-
$this->oOpts = $this->loadOptions()->setMod( $this );
|
1584 |
-
$this->oOpts->setPathToConfig( $oCon->getPath_ConfigFile( $this->getSlug() ) )
|
1585 |
-
->setRebuildFromFile( $oCon->getIsRebuildOptionsFromFile() )
|
1586 |
-
->setOptionsStorageKey( $this->getOptionsStorageKey() )
|
1587 |
-
->setIfLoadOptionsFromStorage( !$oCon->getIsResetPlugin() );
|
1588 |
-
}
|
1589 |
-
return $this->oOpts;
|
1590 |
}
|
1591 |
|
1592 |
/**
|
1593 |
-
* @
|
|
|
|
|
1594 |
*/
|
1595 |
-
private function
|
|
|
1596 |
try {
|
1597 |
-
$
|
|
|
|
|
|
|
|
|
|
|
1598 |
}
|
1599 |
-
catch ( \Exception $
|
1600 |
-
$sNS = __NAMESPACE__;
|
1601 |
}
|
1602 |
-
return
|
1603 |
}
|
1604 |
|
1605 |
/**
|
1606 |
-
* @
|
|
|
|
|
|
|
1607 |
*/
|
1608 |
-
|
1609 |
-
|
1610 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1611 |
}
|
1612 |
-
return $this->oStrings;
|
1613 |
-
}
|
1614 |
|
1615 |
-
|
1616 |
-
|
1617 |
-
*/
|
1618 |
-
private function loadAdminNotices() {
|
1619 |
-
$oNotices = $this->loadClass( 'AdminNotices' );
|
1620 |
-
if ( $oNotices instanceof Shield\Modules\Base\AdminNotices ) {
|
1621 |
-
$oNotices->setMod( $this )->run();
|
1622 |
}
|
1623 |
-
return $
|
1624 |
}
|
1625 |
|
1626 |
-
|
1627 |
-
|
1628 |
-
* @return $this
|
1629 |
-
*/
|
1630 |
-
private function loadAjaxHandler() {
|
1631 |
-
$oAj = $this->loadClass( 'AjaxHandler' );
|
1632 |
-
if ( !$oAj instanceof Shield\Modules\Base\AjaxHandlerBase ) {
|
1633 |
-
$oAj = new Shield\Modules\Base\AjaxHandlerShield(); // TODO: Provide a better fallback
|
1634 |
-
}
|
1635 |
-
$oAj->setMod( $this );
|
1636 |
-
return $this;
|
1637 |
}
|
1638 |
|
1639 |
-
|
1640 |
-
|
1641 |
-
*/
|
1642 |
-
protected function loadOptions() {
|
1643 |
-
return $this->loadClass( 'Options' );
|
1644 |
}
|
1645 |
|
1646 |
-
|
1647 |
-
|
1648 |
-
|
1649 |
-
|
1650 |
-
|
1651 |
}
|
1652 |
|
1653 |
/**
|
1654 |
-
*
|
1655 |
-
* @return
|
|
|
1656 |
*/
|
1657 |
-
|
1658 |
-
$
|
1659 |
-
return @class_exists( $sC ) ? new $sC() : false;
|
1660 |
}
|
1661 |
}
|
6 |
use FernleafSystems\Wordpress\Plugin\Shield\Modules;
|
7 |
use FernleafSystems\Wordpress\Services\Services;
|
8 |
|
9 |
+
/**
|
10 |
+
* Class ModCon
|
11 |
+
* @package FernleafSystems\Wordpress\Plugin\Shield\Modules\Base
|
12 |
+
*/
|
13 |
+
abstract class ModCon {
|
14 |
|
15 |
use Modules\PluginControllerConsumer;
|
16 |
+
use Shield\Crons\PluginCronsConsumer;
|
17 |
|
18 |
/**
|
19 |
* @var string
|
31 |
protected $bImportExportWhitelistNotify = false;
|
32 |
|
33 |
/**
|
34 |
+
* @var Shield\Modules\Base\BaseProcessor
|
|
|
|
|
|
|
|
|
|
|
35 |
*/
|
36 |
private $oProcessor;
|
37 |
|
41 |
private $oWizard;
|
42 |
|
43 |
/**
|
44 |
+
* @var Shield\Modules\Base\Reporting
|
45 |
*/
|
46 |
+
private $oReporting;
|
47 |
|
48 |
/**
|
49 |
+
* @var Shield\Modules\Base\UI
|
50 |
*/
|
51 |
+
private $oUI;
|
52 |
|
53 |
/**
|
54 |
* @var Shield\Modules\Base\Options
|
55 |
*/
|
56 |
private $oOpts;
|
57 |
|
58 |
+
/**
|
59 |
+
* @var Shield\Modules\Base\WpCli
|
60 |
+
*/
|
61 |
+
private $oWpCli;
|
62 |
+
|
63 |
/**
|
64 |
* @var Shield\Databases\Base\Handler[]
|
65 |
*/
|
66 |
private $aDbHandlers;
|
67 |
|
68 |
/**
|
69 |
+
* @param Shield\Controller\Controller $pluginCon
|
70 |
* @param array $aMod
|
71 |
* @throws \Exception
|
72 |
*/
|
73 |
+
public function __construct( $pluginCon, $aMod = [] ) {
|
74 |
+
if ( !$pluginCon instanceof Shield\Controller\Controller ) {
|
75 |
throw new \Exception( 'Plugin controller not supplied to Module' );
|
76 |
}
|
77 |
+
$this->setCon( $pluginCon );
|
78 |
|
79 |
if ( empty( $aMod[ 'storage_key' ] ) && empty( $aMod[ 'slug' ] ) ) {
|
80 |
throw new \Exception( 'Module storage key AND slug are undefined' );
|
86 |
}
|
87 |
|
88 |
if ( $this->verifyModuleMeetRequirements() ) {
|
89 |
+
$this->handleAutoPageRedirects();
|
90 |
$this->setupHooks( $aMod );
|
91 |
$this->doPostConstruction();
|
92 |
}
|
93 |
}
|
94 |
|
95 |
+
protected function setupHooks( array $aModProps ) {
|
96 |
+
$con = $this->getCon();
|
|
|
|
|
|
|
|
|
97 |
$nRunPriority = isset( $aModProps[ 'load_priority' ] ) ? $aModProps[ 'load_priority' ] : 100;
|
|
|
|
|
98 |
|
99 |
+
add_action( $con->prefix( 'modules_loaded' ), function () {
|
100 |
+
$this->onModulesLoaded();
|
101 |
+
}, $nRunPriority );
|
102 |
+
add_action( $con->prefix( 'run_processors' ), [ $this, 'onRunProcessors' ], $nRunPriority );
|
103 |
+
add_action( 'init', [ $this, 'onWpInit' ], 1 );
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
104 |
|
105 |
$nMenuPri = isset( $aModProps[ 'menu_priority' ] ) ? $aModProps[ 'menu_priority' ] : 100;
|
106 |
+
add_filter( $con->prefix( 'submenu_items' ), [ $this, 'supplySubMenuItem' ], $nMenuPri );
|
107 |
+
add_action( $con->prefix( 'plugin_shutdown' ), [ $this, 'onPluginShutdown' ] );
|
108 |
+
add_action( $con->prefix( 'deactivate_plugin' ), [ $this, 'onPluginDeactivate' ] );
|
109 |
+
add_action( $con->prefix( 'delete_plugin' ), [ $this, 'onPluginDelete' ] );
|
110 |
+
add_filter( $con->prefix( 'aggregate_all_plugin_options' ), [ $this, 'aggregateOptionsValues' ] );
|
111 |
+
|
112 |
+
add_filter( $con->prefix( 'register_admin_notices' ), [ $this, 'fRegisterAdminNotices' ] );
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
113 |
|
114 |
add_action( 'admin_enqueue_scripts', [ $this, 'onWpEnqueueAdminJs' ], 100 );
|
115 |
|
120 |
// if ( $this->isAdminOptionsPage() ) {
|
121 |
// add_action( 'current_screen', array( $this, 'onSetCurrentScreen' ) );
|
122 |
// }
|
123 |
+
$this->setupCronHooks();
|
124 |
$this->setupCustomHooks();
|
125 |
}
|
126 |
|
134 |
$this->cleanupDatabases();
|
135 |
}
|
136 |
|
|
|
|
|
|
|
137 |
protected function cleanupDatabases() {
|
138 |
+
foreach ( $this->getDbHandlers( true ) as $dbh ) {
|
139 |
+
if ( $dbh instanceof Shield\Databases\Base\Handler && $dbh->isReady() ) {
|
140 |
+
$dbh->autoCleanDb();
|
141 |
}
|
142 |
}
|
143 |
}
|
156 |
}
|
157 |
|
158 |
/**
|
159 |
+
* @param string $dbhKey
|
160 |
* @return Shield\Databases\Base\Handler|mixed|false
|
161 |
*/
|
162 |
+
protected function getDbH( $dbhKey ) {
|
163 |
+
$dbh = false;
|
164 |
|
165 |
if ( !is_array( $this->aDbHandlers ) ) {
|
166 |
$this->aDbHandlers = [];
|
167 |
}
|
168 |
|
169 |
+
if ( !empty( $this->aDbHandlers[ $dbhKey ] ) ) {
|
170 |
+
$dbh = $this->aDbHandlers[ $dbhKey ];
|
171 |
}
|
172 |
else {
|
173 |
$aDbClasses = $this->getAllDbClasses();
|
174 |
+
if ( isset( $aDbClasses[ $dbhKey ] ) ) {
|
175 |
+
/** @var Shield\Databases\Base\Handler $dbh */
|
176 |
+
$dbh = new $aDbClasses[ $dbhKey ]();
|
177 |
try {
|
178 |
+
$dbh->setMod( $this )->tableInit();
|
179 |
}
|
180 |
+
catch ( \Exception $e ) {
|
181 |
}
|
182 |
}
|
183 |
+
$this->aDbHandlers[ $dbhKey ] = $dbh;
|
184 |
}
|
185 |
|
186 |
+
return $dbh;
|
187 |
}
|
188 |
|
189 |
/**
|
190 |
* @return string[]
|
191 |
*/
|
192 |
private function getAllDbClasses() {
|
193 |
+
$classes = $this->getOptions()->getDef( 'db_classes' );
|
194 |
+
return is_array( $classes ) ? $classes : [];
|
195 |
}
|
196 |
|
197 |
/**
|
198 |
+
* @return false|Shield\Modules\Base\Upgrade|mixed
|
|
|
199 |
*/
|
200 |
+
public function getUpgradeHandler() {
|
201 |
+
return $this->loadModElement( 'Upgrade' );
|
202 |
}
|
203 |
|
204 |
/**
|
247 |
return array_merge( $aAdminNotices, $this->getOptions()->getAdminNotices() );
|
248 |
}
|
249 |
|
250 |
+
private function verifyModuleMeetRequirements() :bool {
|
|
|
|
|
|
|
251 |
$bMeetsReqs = true;
|
252 |
|
253 |
$aPhpReqs = $this->getOptions()->getFeatureRequirement( 'php' );
|
271 |
return $bMeetsReqs;
|
272 |
}
|
273 |
|
274 |
+
protected function onModulesLoaded() {
|
275 |
+
}
|
276 |
+
|
277 |
public function onRunProcessors() {
|
278 |
+
$opts = $this->getOptions();
|
279 |
+
if ( $opts->getFeatureProperty( 'auto_load_processor' ) ) {
|
|
|
|
|
280 |
$this->loadProcessor();
|
281 |
}
|
282 |
+
try {
|
283 |
+
$bSkip = (bool)$opts->getFeatureProperty( 'skip_processor' );
|
284 |
+
if ( !$bSkip && !$this->isUpgrading() && $this->isModuleEnabled() && $this->isReadyToExecute() ) {
|
285 |
+
$this->doExecuteProcessor();
|
286 |
+
}
|
287 |
+
}
|
288 |
+
catch ( \Exception $e ) {
|
289 |
}
|
290 |
}
|
291 |
|
292 |
/**
|
293 |
* @return bool
|
294 |
+
* @throws \Exception
|
295 |
*/
|
296 |
+
protected function isReadyToExecute() :bool {
|
297 |
+
return !is_null( $this->getProcessor() );
|
298 |
}
|
299 |
|
300 |
protected function doExecuteProcessor() {
|
301 |
$this->getProcessor()->execute();
|
302 |
}
|
303 |
|
|
|
|
|
|
|
304 |
public function onWpInit() {
|
305 |
+
|
306 |
+
$shieldAction = $this->getCon()->getShieldAction();
|
307 |
+
if ( !empty( $shieldAction ) ) {
|
308 |
+
do_action( $this->getCon()->prefix( 'shield_action' ), $shieldAction );
|
309 |
+
}
|
310 |
+
|
311 |
+
add_action( 'cli_init', function () {
|
312 |
+
try {
|
313 |
+
$this->getWpCli()->execute();
|
314 |
+
}
|
315 |
+
catch ( \Exception $e ) {
|
316 |
+
}
|
317 |
+
} );
|
318 |
+
|
319 |
+
if ( $this->isModuleRequest() ) {
|
320 |
+
|
321 |
+
if ( Services::WpGeneral()->isAjax() ) {
|
322 |
+
$this->loadAjaxHandler();
|
323 |
+
}
|
324 |
+
else {
|
325 |
+
try {
|
326 |
+
if ( $this->verifyModActionRequest() ) {
|
327 |
+
$this->handleModAction( Services::Request()->request( 'exec' ) );
|
328 |
+
}
|
329 |
+
}
|
330 |
+
catch ( \Exception $e ) {
|
331 |
+
wp_nonce_ays( '' );
|
332 |
+
}
|
333 |
+
}
|
334 |
+
}
|
335 |
|
336 |
$this->runWizards();
|
337 |
|
340 |
add_filter( $this->prefix( 'wpPrivacyExport' ), [ $this, 'onWpPrivacyExport' ], 10, 3 );
|
341 |
add_filter( $this->prefix( 'wpPrivacyErase' ), [ $this, 'onWpPrivacyErase' ], 10, 3 );
|
342 |
}
|
343 |
+
|
344 |
+
$this->loadDebug();
|
345 |
}
|
346 |
|
347 |
/**
|
361 |
|
362 |
/**
|
363 |
* Override this and adapt per feature
|
364 |
+
* @return Shield\Modules\Base\BaseProcessor|mixed
|
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 );
|
379 |
}
|
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 |
/**
|
408 |
);
|
409 |
}
|
410 |
|
411 |
+
public function isUpgrading() :bool {
|
|
|
|
|
|
|
412 |
return $this->getCon()->getIsRebuildOptionsFromFile() || $this->getOptions()->getRebuildFromFile();
|
413 |
}
|
414 |
|
425 |
}
|
426 |
}
|
427 |
|
428 |
+
public function getOptionsStorageKey() :string {
|
|
|
|
|
|
|
429 |
return $this->getCon()->prefixOption( $this->sOptionsStoreKey ).'_options';
|
430 |
}
|
431 |
|
432 |
/**
|
433 |
+
* @return Shield\Modules\Base\BaseProcessor|\FernleafSystems\Utilities\Logic\OneTimeExecute|mixed
|
434 |
*/
|
435 |
public function getProcessor() {
|
436 |
return $this->loadProcessor();
|
437 |
}
|
438 |
|
439 |
+
public function getUrl_AdminPage() :string {
|
|
|
|
|
|
|
440 |
return Services::WpGeneral()
|
441 |
->getUrl_AdminPage(
|
442 |
$this->getModSlug(),
|
445 |
}
|
446 |
|
447 |
/**
|
448 |
+
* @param string $sAction
|
449 |
* @return string
|
450 |
*/
|
451 |
+
public function buildAdminActionNonceUrl( $sAction ) {
|
452 |
+
$aActionNonce = $this->getNonceActionData( $sAction );
|
453 |
+
$aActionNonce[ 'ts' ] = Services::Request()->ts();
|
454 |
+
return add_query_arg( $aActionNonce, $this->getUrl_AdminPage() );
|
455 |
+
}
|
456 |
+
|
457 |
+
protected function getModActionParams( string $action ) :array {
|
458 |
+
$con = $this->getCon();
|
459 |
+
return [
|
460 |
+
'action' => $con->prefix(),
|
461 |
+
'exec' => $action,
|
462 |
+
'mod_slug' => $this->getModSlug(),
|
463 |
+
'ts' => Services::Request()->ts(),
|
464 |
+
'exec_nonce' => substr(
|
465 |
+
hash_hmac( 'md5', $action.Services::Request()->ts(), $con->getSiteInstallationId() )
|
466 |
+
, 0, 6 )
|
467 |
+
];
|
468 |
}
|
469 |
|
470 |
/**
|
471 |
+
* @return bool
|
472 |
+
* @throws \Exception
|
473 |
*/
|
474 |
+
protected function verifyModActionRequest() :bool {
|
475 |
+
$bValid = false;
|
476 |
+
|
477 |
+
$con = $this->getCon();
|
478 |
+
$req = Services::Request();
|
479 |
+
|
480 |
+
$sExec = $req->request( 'exec' );
|
481 |
+
if ( !empty( $sExec ) && $req->request( 'action' ) == $con->prefix() ) {
|
482 |
+
|
483 |
+
|
484 |
+
if ( wp_verify_nonce( $req->request( 'exec_nonce' ), $sExec ) && $con->getMeetsBasePermissions() ) {
|
485 |
+
$bValid = true;
|
486 |
+
}
|
487 |
+
else {
|
488 |
+
$bValid = $req->request( 'exec_nonce' ) ===
|
489 |
+
substr( hash_hmac( 'md5', $sExec.$req->request( 'ts' ), $con->getSiteInstallationId() ), 0, 6 );
|
490 |
+
}
|
491 |
+
if ( !$bValid ) {
|
492 |
+
throw new \Exception( 'Invalid request' );
|
493 |
+
}
|
494 |
+
}
|
495 |
+
|
496 |
+
return $bValid;
|
497 |
+
}
|
498 |
+
|
499 |
+
public function getUrl_DirectLinkToOption( string $key ) :string {
|
500 |
+
$url = $this->getUrl_AdminPage();
|
501 |
+
$def = $this->getOptions()->getOptDefinition( $key );
|
502 |
+
if ( !empty( $def[ 'section' ] ) ) {
|
503 |
+
$url = $this->getUrl_DirectLinkToSection( $def[ 'section' ] );
|
504 |
}
|
505 |
+
return $url;
|
506 |
+
}
|
507 |
+
|
508 |
+
public function getUrl_DirectLinkToSection( string $section ) :string {
|
509 |
+
if ( $section == 'primary' ) {
|
510 |
+
$section = $this->getOptions()->getPrimarySection()[ 'slug' ];
|
511 |
+
}
|
512 |
+
return $this->getUrl_AdminPage().'#tab-'.$section;
|
513 |
}
|
514 |
|
515 |
/**
|
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.1
|
520 |
*/
|
521 |
public function getEmailHandler() {
|
522 |
+
return $this->getCon()->getModule_Email();
|
|
|
|
|
|
|
523 |
}
|
524 |
|
525 |
/**
|
526 |
+
* @return Modules\Email\Processor
|
527 |
*/
|
528 |
public function getEmailProcessor() {
|
529 |
return $this->getEmailHandler()->getProcessor();
|
530 |
}
|
531 |
|
532 |
/**
|
533 |
+
* @param bool $enable
|
534 |
* @return $this
|
535 |
*/
|
536 |
+
public function setIsMainFeatureEnabled( bool $enable ) {
|
537 |
+
$this->getOptions()->setOpt( 'enable_'.$this->getSlug(), $enable ? 'Y' : 'N' );
|
538 |
+
return $this;
|
539 |
}
|
540 |
|
541 |
+
public function isModuleEnabled() :bool {
|
|
|
|
|
|
|
|
|
542 |
/** @var Shield\Modules\Plugin\Options $oPluginOpts */
|
543 |
$oPluginOpts = $this->getCon()->getModule_Plugin()->getOptions();
|
544 |
|
545 |
if ( $this->getOptions()->getFeatureProperty( 'auto_enabled' ) === true ) {
|
546 |
// Auto enabled modules always run regardless
|
547 |
+
$enabled = true;
|
548 |
}
|
549 |
elseif ( $oPluginOpts->isPluginGloballyDisabled() ) {
|
550 |
+
$enabled = false;
|
551 |
}
|
552 |
elseif ( $this->getCon()->getIfForceOffActive() ) {
|
553 |
+
$enabled = false;
|
554 |
}
|
555 |
+
elseif ( $this->getOptions()->getFeatureProperty( 'premium' ) === true
|
556 |
+
&& !$this->isPremium() ) {
|
557 |
+
$enabled = false;
|
558 |
}
|
559 |
else {
|
560 |
+
$enabled = $this->isModOptEnabled();
|
561 |
}
|
562 |
|
563 |
+
return $enabled;
|
564 |
}
|
565 |
|
566 |
+
public function isModOptEnabled() :bool {
|
567 |
+
$opts = $this->getOptions();
|
568 |
+
return $opts->isOpt( $this->getEnableModOptKey(), 'Y' )
|
569 |
+
|| $opts->isOpt( $this->getEnableModOptKey(), true, true );
|
|
|
|
|
570 |
}
|
571 |
|
572 |
+
public function getEnableModOptKey() :string {
|
|
|
|
|
|
|
573 |
return 'enable_'.$this->getSlug();
|
574 |
}
|
575 |
|
576 |
+
public function getMainFeatureName() :string {
|
|
|
|
|
|
|
577 |
return __( $this->getOptions()->getFeatureProperty( 'name' ), 'wp-simple-firewall' );
|
578 |
}
|
579 |
|
580 |
+
public function getModSlug( bool $prefix = true ) :string {
|
581 |
+
return $prefix ? $this->prefix( $this->getSlug() ) : $this->getSlug();
|
|
|
|
|
|
|
|
|
582 |
}
|
583 |
|
584 |
/**
|
621 |
if ( !empty( $aAdditionalItems ) && is_array( $aAdditionalItems ) ) {
|
622 |
|
623 |
foreach ( $aAdditionalItems as $aMenuItem ) {
|
|
|
|
|
|
|
|
|
|
|
624 |
$sMenuPageTitle = $sHumanName.' - '.$aMenuItem[ 'title' ];
|
625 |
$aItems[ $sMenuPageTitle ] = [
|
626 |
+
__( $aMenuItem[ 'title' ], 'wp-simple-firewall' ),
|
627 |
$this->prefix( $aMenuItem[ 'slug' ] ),
|
628 |
+
[ $this, $aMenuItem[ 'callback' ] ],
|
629 |
+
true
|
630 |
];
|
631 |
}
|
632 |
}
|
635 |
}
|
636 |
|
637 |
/**
|
638 |
+
* Handles the case where we want to redirect certain menu requests to other pages
|
639 |
+
* of the plugin automatically. It lets us create custom menu items.
|
640 |
+
* This can of course be extended for any other types of redirect.
|
641 |
*/
|
642 |
+
public function handleAutoPageRedirects() {
|
643 |
+
$aConf = $this->getOptions()->getRawData_FullFeatureConfig();
|
644 |
+
if ( !empty( $aConf[ 'custom_redirects' ] ) && $this->getCon()->isValidAdminArea() ) {
|
645 |
+
foreach ( $aConf[ 'custom_redirects' ] as $aRedirect ) {
|
646 |
+
if ( Services::Request()->query( 'page' ) == $this->prefix( $aRedirect[ 'source_mod_page' ] ) ) {
|
647 |
+
Services::Response()->redirect(
|
648 |
+
$this->getCon()->getModule( $aRedirect[ 'target_mod_page' ] )->getUrl_AdminPage(),
|
649 |
+
$aRedirect[ 'query_args' ],
|
650 |
+
true,
|
651 |
+
false
|
652 |
+
);
|
653 |
+
}
|
654 |
+
}
|
655 |
+
}
|
656 |
}
|
657 |
|
658 |
/**
|
|
|
659 |
* @return array
|
660 |
*/
|
661 |
+
protected function getAdditionalMenuItem() {
|
662 |
+
return [];
|
|
|
|
|
|
|
663 |
}
|
664 |
|
665 |
/**
|
666 |
+
* TODO: not the place for this method.
|
667 |
+
* @return array[]
|
668 |
*/
|
669 |
+
public function getModulesSummaryData() {
|
670 |
+
return array_map(
|
671 |
+
function ( $mod ) {
|
672 |
+
return $mod->buildSummaryData();
|
673 |
+
},
|
674 |
+
$this->getCon()->modules
|
675 |
+
);
|
676 |
}
|
677 |
|
678 |
/**
|
|
|
679 |
* @return array
|
680 |
*/
|
681 |
+
public function buildSummaryData() {
|
682 |
+
$opts = $this->getOptions();
|
683 |
+
$sMenuTitle = $opts->getFeatureProperty( 'menu_title' );
|
684 |
+
|
685 |
+
$aSections = $opts->getSections();
|
686 |
+
foreach ( $aSections as $sSlug => $aSection ) {
|
687 |
+
try {
|
688 |
+
$aStrings = $this->getStrings()->getSectionStrings( $aSection[ 'slug' ] );
|
689 |
+
foreach ( $aStrings as $sKey => $sVal ) {
|
690 |
+
unset( $aSection[ $sKey ] );
|
691 |
+
$aSection[ $sKey ] = $sVal;
|
692 |
+
}
|
693 |
+
}
|
694 |
+
catch ( \Exception $e ) {
|
695 |
+
}
|
696 |
+
}
|
697 |
|
698 |
+
$aSum = [
|
699 |
+
'slug' => $this->getSlug(),
|
700 |
+
'enabled' => $this->getUIHandler()->isEnabledForUiSummary(),
|
701 |
+
'active' => $this->isThisModulePage() || $this->isPage_InsightsThisModule(),
|
702 |
+
'name' => $this->getMainFeatureName(),
|
703 |
+
'sidebar_name' => $opts->getFeatureProperty( 'sidebar_name' ),
|
704 |
+
'menu_title' => empty( $sMenuTitle ) ? $this->getMainFeatureName() : __( $sMenuTitle, 'wp-simple-firewall' ),
|
705 |
+
'href' => network_admin_url( 'admin.php?page='.$this->getModSlug() ),
|
706 |
+
'sections' => $aSections,
|
707 |
+
'options' => [],
|
708 |
+
'show_mod_opts' => $this->getIfShowModuleOpts(),
|
709 |
+
];
|
710 |
+
|
711 |
+
foreach ( $opts->getVisibleOptionsKeys() as $sOptKey ) {
|
712 |
+
try {
|
713 |
+
$aOptData = $this->getStrings()->getOptionStrings( $sOptKey );
|
714 |
+
$aOptData[ 'href' ] = $this->getUrl_DirectLinkToOption( $sOptKey );
|
715 |
+
$aSum[ 'options' ][ $sOptKey ] = $aOptData;
|
716 |
+
}
|
717 |
+
catch ( \Exception $e ) {
|
718 |
+
}
|
719 |
+
}
|
720 |
+
|
721 |
+
$aSum[ 'tooltip' ] = sprintf(
|
722 |
+
'%s',
|
723 |
+
empty( $aSum[ 'sidebar_name' ] ) ? $aSum[ 'name' ] : __( $aSum[ 'sidebar_name' ], 'wp-simple-firewall' )
|
724 |
+
);
|
725 |
+
return $aSum;
|
726 |
}
|
727 |
|
728 |
+
public function getIfShowModuleMenuItem() :bool {
|
|
|
|
|
|
|
729 |
return (bool)$this->getOptions()->getFeatureProperty( 'show_module_menu_item' );
|
730 |
}
|
731 |
|
732 |
+
public function getIfShowModuleOpts() :bool {
|
|
|
|
|
|
|
733 |
return (bool)$this->getOptions()->getFeatureProperty( 'show_module_options' );
|
734 |
}
|
735 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
736 |
/**
|
737 |
* Get config 'definition'.
|
738 |
+
* @param string $key
|
739 |
* @return mixed|null
|
740 |
*/
|
741 |
+
public function getDef( string $key ) {
|
742 |
+
return $this->getOptions()->getDef( $key );
|
743 |
}
|
744 |
|
745 |
/**
|
754 |
* @param string $sGlue
|
755 |
* @return string|array
|
756 |
*/
|
757 |
+
public function getLastErrors( $bAsString = false, $sGlue = " " ) {
|
758 |
+
$errors = $this->getOptions()->getOpt( 'last_errors' );
|
759 |
+
if ( !is_array( $errors ) ) {
|
760 |
+
$errors = [];
|
761 |
}
|
762 |
+
return $bAsString ? implode( $sGlue, $errors ) : $errors;
|
763 |
}
|
764 |
|
765 |
+
public function hasLastErrors() :bool {
|
|
|
|
|
|
|
766 |
return count( $this->getLastErrors( false ) ) > 0;
|
767 |
}
|
768 |
|
769 |
+
public function getTextOpt( string $key ) :string {
|
770 |
+
$sValue = $this->getOptions()->getOpt( $key, 'default' );
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
771 |
if ( $sValue == 'default' ) {
|
772 |
+
$sValue = $this->getTextOptDefault( $key );
|
773 |
}
|
774 |
+
return __( $sValue, 'wp-simple-firewall' );
|
775 |
}
|
776 |
|
777 |
+
public function getTextOptDefault( string $key ) :string {
|
|
|
|
|
|
|
|
|
|
|
778 |
return 'Undefined Text Opt Default';
|
779 |
}
|
780 |
|
791 |
$mErrors = [];
|
792 |
}
|
793 |
}
|
794 |
+
$this->getOptions()->setOpt( 'last_errors', $mErrors );
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
795 |
return $this;
|
796 |
}
|
797 |
|
798 |
+
public function setOptions( array $options ) {
|
799 |
+
$opts = $this->getOptions();
|
800 |
+
foreach ( $options as $key => $value ) {
|
801 |
+
$opts->setOpt( $key, $value );
|
|
|
|
|
|
|
802 |
}
|
803 |
}
|
804 |
|
805 |
+
public function isModuleRequest() :bool {
|
806 |
+
return $this->getModSlug() === Services::Request()->request( 'mod_slug' );
|
|
|
|
|
|
|
807 |
}
|
808 |
|
809 |
/**
|
810 |
+
* @param string $action
|
811 |
+
* @param bool $asJson
|
812 |
+
* @return array|string
|
813 |
*/
|
814 |
+
public function getAjaxActionData( string $action = '', $asJson = false ) {
|
815 |
+
$data = $this->getNonceActionData( $action );
|
816 |
+
$data[ 'ajaxurl' ] = admin_url( 'admin-ajax.php' );
|
817 |
+
return $asJson ? json_encode( (object)$data ) : $data;
|
818 |
}
|
819 |
|
820 |
/**
|
830 |
/**
|
831 |
* @return string[]
|
832 |
*/
|
833 |
+
public function getDismissedNotices() :array {
|
834 |
+
$notices = $this->getOptions()->getOpt( 'dismissed_notices' );
|
835 |
+
return is_array( $notices ) ? $notices : [];
|
836 |
}
|
837 |
|
838 |
/**
|
839 |
* @return string[]
|
840 |
*/
|
841 |
+
public function getUiTrack() :array {
|
842 |
+
$a = $this->getOptions()->getOpt( 'ui_track' );
|
843 |
+
return is_array( $a ) ? $a : [];
|
844 |
}
|
845 |
|
846 |
+
public function setDismissedNotices( array $dis ) {
|
847 |
+
$this->getOptions()->setOpt( 'dismissed_notices', $dis );
|
|
|
|
|
|
|
|
|
848 |
}
|
849 |
|
850 |
+
public function setUiTrack( array $UI ) {
|
851 |
+
$this->getOptions()->setOpt( 'ui_track', $UI );
|
|
|
|
|
|
|
|
|
852 |
}
|
853 |
|
854 |
/**
|
867 |
}
|
868 |
|
869 |
/**
|
870 |
+
* @param bool $bPreProcessOptions
|
871 |
* @return $this
|
872 |
*/
|
873 |
+
public function saveModOptions( $bPreProcessOptions = false ) {
|
874 |
+
|
875 |
+
if ( $bPreProcessOptions ) {
|
876 |
+
$this->preProcessOptions();
|
877 |
+
}
|
878 |
+
|
879 |
$this->doPrePluginOptionsSave();
|
880 |
if ( apply_filters( $this->prefix( 'force_options_resave' ), false ) ) {
|
881 |
$this->getOptions()
|
888 |
return $this;
|
889 |
}
|
890 |
|
891 |
+
protected function preProcessOptions() {
|
892 |
+
}
|
893 |
+
|
894 |
private function store() {
|
895 |
add_filter( $this->prefix( 'bypass_is_plugin_admin' ), '__return_true', 1000 );
|
896 |
$this->getOptions()
|
916 |
}
|
917 |
|
918 |
public function onPluginDelete() {
|
919 |
+
foreach ( $this->getDbHandlers( true ) as $oDbh ) {
|
920 |
+
if ( !empty( $oDbh ) ) {
|
921 |
+
$oDbh->tableDelete();
|
922 |
+
}
|
923 |
+
}
|
924 |
$this->getOptions()->deleteStorage();
|
925 |
}
|
926 |
|
930 |
protected function getAllFormOptionsAndTypes() {
|
931 |
$aOpts = [];
|
932 |
|
933 |
+
foreach ( $this->getUIHandler()->buildOptions() as $aOptionsSection ) {
|
934 |
if ( !empty( $aOptionsSection ) ) {
|
935 |
foreach ( $aOptionsSection[ 'options' ] as $aOption ) {
|
936 |
$aOpts[ $aOption[ 'key' ] ] = $aOption[ 'type' ];
|
941 |
return $aOpts;
|
942 |
}
|
943 |
|
944 |
+
protected function handleModAction( string $action ) {
|
945 |
}
|
946 |
|
947 |
/**
|
951 |
if ( !$this->getCon()->isPluginAdmin() ) {
|
952 |
throw new \Exception( __( "You don't currently have permission to save settings.", 'wp-simple-firewall' ) );
|
953 |
}
|
954 |
+
|
955 |
$this->doSaveStandardOptions();
|
|
|
|
|
956 |
|
957 |
+
$this->saveModOptions( true );
|
958 |
+
|
959 |
+
// only use this flag when the options are being updated with a MANUAL save.
|
960 |
+
if ( isset( $this->bImportExportWhitelistNotify ) && $this->bImportExportWhitelistNotify ) {
|
961 |
+
if ( !wp_next_scheduled( $this->prefix( 'importexport_notify' ) ) ) {
|
962 |
+
wp_schedule_single_event( Services::Request()->ts() + 15, $this->prefix( 'importexport_notify' ) );
|
963 |
+
}
|
964 |
+
}
|
965 |
}
|
966 |
|
967 |
/**
|
981 |
return $this;
|
982 |
}
|
983 |
|
984 |
+
protected function isAdminOptionsPage() :bool {
|
985 |
+
return is_admin() && !Services::WpGeneral()->isAjax() && $this->isThisModulePage();
|
|
|
|
|
|
|
986 |
}
|
987 |
|
988 |
/**
|
1043 |
elseif ( $sOptType == 'comma_separated_lists' ) {
|
1044 |
$sOptionValue = Services::Data()->extractCommaSeparatedList( $sOptionValue );
|
1045 |
}
|
1046 |
+
/* elseif ( $sOptType == 'multiple_select' ) { } */
|
|
|
1047 |
}
|
1048 |
|
1049 |
// Prevent overwriting of non-editable fields
|
1050 |
if ( !in_array( $sOptType, [ 'noneditable_text' ] ) ) {
|
1051 |
+
$this->getOptions()->setOpt( $sKey, $sOptionValue );
|
1052 |
}
|
1053 |
}
|
1054 |
|
1055 |
+
// Handle Import/Export exclusions
|
1056 |
+
if ( $this->isPremium() ) {
|
1057 |
+
( new Shield\Modules\Plugin\Lib\ImportExport\Options\SaveExcludedOptions() )
|
1058 |
+
->setMod( $this )
|
1059 |
+
->save( $aForm );
|
|
|
|
|
1060 |
}
|
1061 |
}
|
1062 |
|
1116 |
return $this->getCon()->prefix( $sSuffix, $sGlue );
|
1117 |
}
|
1118 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1119 |
/**
|
1120 |
* @uses echo()
|
1121 |
*/
|
1122 |
public function displayModuleAdminPage() {
|
1123 |
+
echo $this->renderModulePage();
|
|
|
|
|
|
|
|
|
|
|
1124 |
}
|
1125 |
|
1126 |
/**
|
1128 |
* @param array $aData
|
1129 |
* @return string
|
1130 |
*/
|
1131 |
+
protected function renderModulePage( array $aData = [] ) :string {
|
1132 |
+
return $this->renderTemplate(
|
1133 |
+
'index.php',
|
1134 |
+
Services::DataManipulation()->mergeArraysRecursive( $this->getUIHandler()->getBaseDisplayData(), $aData )
|
1135 |
+
);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1136 |
}
|
1137 |
|
1138 |
/**
|
1139 |
* @return string
|
1140 |
*/
|
1141 |
protected function getContentWizardLanding() {
|
1142 |
+
$aData = $this->getUIHandler()->getBaseDisplayData();
|
1143 |
if ( $this->hasWizard() ) {
|
1144 |
$aData[ 'content' ][ 'wizard_landing' ] = $this->getWizardHandler()->renderWizardLandingSnippet();
|
1145 |
}
|
1194 |
/**
|
1195 |
* @return string
|
1196 |
*/
|
1197 |
+
public function getUrl_WizardLanding() {
|
1198 |
return $this->getUrl_Wizard( 'landing' );
|
1199 |
}
|
1200 |
|
1219 |
return is_array( $aW ) ? $aW : [];
|
1220 |
}
|
1221 |
|
1222 |
+
public function hasWizard() :bool {
|
1223 |
+
return count( $this->getWizardDefinitions() ) > 0;
|
|
|
|
|
|
|
1224 |
}
|
1225 |
|
1226 |
/**
|
1235 |
/**
|
1236 |
* @return bool
|
1237 |
*/
|
1238 |
+
public function getIsShowMarketing() {
|
1239 |
return apply_filters( $this->prefix( 'show_marketing' ), !$this->isPremium() );
|
1240 |
}
|
1241 |
|
1255 |
return $this->getCon()
|
1256 |
->getRenderer()
|
1257 |
->setTemplate( $sTemplate )
|
1258 |
+
->setRenderVars( $this->getUIHandler()->getBaseDisplayData() )
|
1259 |
->setTemplateEngineTwig()
|
1260 |
->render();
|
1261 |
}
|
1267 |
/**
|
1268 |
* @return bool
|
1269 |
*/
|
1270 |
+
public function canDisplayOptionsForm() {
|
1271 |
return $this->getOptions()->isAccessRestricted() ? $this->getCon()->isPluginAdmin() : true;
|
1272 |
}
|
1273 |
|
1337 |
return $this->renderTemplate( $sTemplate, $aData, $bTwig );
|
1338 |
}
|
1339 |
|
1340 |
+
public function renderTemplate( string $template, array $data = [], bool $isTwig = false ) :string {
|
1341 |
+
if ( empty( $data[ 'unique_render_id' ] ) ) {
|
1342 |
+
$data[ 'unique_render_id' ] = 'noticeid-'.substr( md5( mt_rand() ), 0, 5 );
|
|
|
|
|
|
|
|
|
|
|
|
|
1343 |
}
|
1344 |
try {
|
1345 |
$oRndr = $this->getCon()->getRenderer();
|
1346 |
+
if ( $isTwig || preg_match( '#^.*\.twig$#i', $template ) ) {
|
1347 |
$oRndr->setTemplateEngineTwig();
|
1348 |
}
|
1349 |
|
1350 |
+
$data[ 'strings' ] = Services::DataManipulation()
|
1351 |
+
->mergeArraysRecursive(
|
1352 |
+
$this->getStrings()->getDisplayStrings(),
|
1353 |
+
$data[ 'strings' ] ?? []
|
1354 |
+
);
|
1355 |
+
|
1356 |
+
$render = $oRndr->setTemplate( $template )
|
1357 |
+
->setRenderVars( $data )
|
1358 |
+
->render();
|
1359 |
}
|
1360 |
catch ( \Exception $oE ) {
|
1361 |
+
$render = $oE->getMessage();
|
1362 |
error_log( $oE->getMessage() );
|
1363 |
}
|
1364 |
|
1365 |
+
return (string)$render;
|
1366 |
}
|
1367 |
|
1368 |
/**
|
1377 |
return $aTransferableOptions;
|
1378 |
}
|
1379 |
|
1380 |
+
public function getMainWpData() :array {
|
1381 |
+
return [
|
1382 |
+
'options' => $this->getOptions()->getTransferableOptions()
|
1383 |
+
];
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1384 |
}
|
1385 |
|
1386 |
/**
|
1405 |
return $aData;
|
1406 |
}
|
1407 |
|
1408 |
+
/**
|
1409 |
+
* @return null|Shield\Modules\Base\ShieldOptions|mixed
|
1410 |
+
*/
|
1411 |
+
public function getOptions() {
|
1412 |
+
if ( !isset( $this->oOpts ) ) {
|
1413 |
+
$oCon = $this->getCon();
|
1414 |
+
$this->oOpts = $this->loadModElement( 'Options' );
|
1415 |
+
$this->oOpts->setPathToConfig( $oCon->getPath_ConfigFile( $this->getSlug() ) )
|
1416 |
+
->setRebuildFromFile( $oCon->getIsRebuildOptionsFromFile() )
|
1417 |
+
->setOptionsStorageKey( $this->getOptionsStorageKey() )
|
1418 |
+
->setIfLoadOptionsFromStorage( !$oCon->getIsResetPlugin() );
|
1419 |
}
|
1420 |
+
return $this->oOpts;
|
1421 |
}
|
1422 |
|
1423 |
/**
|
1424 |
+
* @return Shield\Modules\Base\WpCli
|
1425 |
+
* @throws \Exception
|
1426 |
*/
|
1427 |
+
public function getWpCli() {
|
1428 |
+
if ( !isset( $this->oWpCli ) ) {
|
1429 |
+
$this->oWpCli = $this->loadModElement( 'WpCli' );
|
1430 |
+
if ( !$this->oWpCli instanceof Shield\Modules\Base\WpCli ) {
|
1431 |
+
throw new \Exception( 'WP-CLI not supported' );
|
1432 |
+
}
|
1433 |
+
}
|
1434 |
+
return $this->oWpCli;
|
1435 |
}
|
1436 |
|
1437 |
/**
|
1438 |
+
* @return null|Shield\Modules\Base\Strings
|
1439 |
*/
|
1440 |
+
public function getStrings() {
|
1441 |
+
return $this->loadStrings()->setMod( $this );
|
1442 |
}
|
1443 |
|
1444 |
/**
|
1445 |
+
* @return Shield\Modules\Base\UI
|
1446 |
*/
|
1447 |
+
public function getUIHandler() {
|
1448 |
+
if ( !isset( $this->oUI ) ) {
|
1449 |
+
$this->oUI = $this->loadModElement( 'UI' );
|
1450 |
+
if ( !$this->oUI instanceof Shield\Modules\Base\UI ) {
|
1451 |
+
// TODO: autoloader for base classes
|
1452 |
+
$this->oUI = $this->loadModElement( 'ShieldUI' );
|
1453 |
+
}
|
1454 |
+
}
|
1455 |
+
return $this->oUI;
|
1456 |
}
|
1457 |
|
1458 |
/**
|
1459 |
+
* @return Shield\Modules\Base\Reporting|mixed|false
|
|
|
1460 |
*/
|
1461 |
+
public function getReportingHandler() {
|
1462 |
+
if ( !isset( $this->oReporting ) ) {
|
1463 |
+
$this->oReporting = $this->loadModElement( 'Reporting' );
|
1464 |
+
}
|
1465 |
+
return $this->oReporting;
|
1466 |
}
|
1467 |
|
1468 |
+
protected function loadAdminNotices() {
|
1469 |
+
$N = $this->loadModElement( 'AdminNotices' );
|
1470 |
+
if ( $N instanceof Shield\Modules\Base\AdminNotices ) {
|
1471 |
+
$N->run();
|
1472 |
+
}
|
1473 |
}
|
1474 |
|
1475 |
+
protected function loadAjaxHandler() {
|
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()
|
1490 |
+
&& $this->getCon()->isPluginAdmin() ) {
|
1491 |
+
/** @var Shield\Modules\Base\Debug $debug */
|
1492 |
+
$debug = $this->loadModElement( 'Debug', true );
|
1493 |
+
$debug->run();
|
1494 |
+
}
|
1495 |
}
|
1496 |
|
1497 |
/**
|
1498 |
+
* @return Shield\Modules\Base\Strings|mixed
|
1499 |
*/
|
1500 |
+
protected function loadStrings() {
|
1501 |
+
return $this->loadModElement( 'Strings', true );
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1502 |
}
|
1503 |
|
1504 |
/**
|
1505 |
+
* @param string $class
|
1506 |
+
* @param false $injectMod
|
1507 |
+
* @return false|Shield\Modules\ModConsumer
|
1508 |
*/
|
1509 |
+
private function loadModElement( string $class, $injectMod = true ) {
|
1510 |
+
$element = false;
|
1511 |
try {
|
1512 |
+
$C = $this->findElementClass( $class, true );
|
1513 |
+
/** @var Shield\Modules\ModConsumer $element */
|
1514 |
+
$element = @class_exists( $C ) ? new $C() : false;
|
1515 |
+
if ( $injectMod && method_exists( $element, 'setMod' ) ) {
|
1516 |
+
$element->setMod( $this );
|
1517 |
+
}
|
1518 |
}
|
1519 |
+
catch ( \Exception $e ) {
|
|
|
1520 |
}
|
1521 |
+
return $element;
|
1522 |
}
|
1523 |
|
1524 |
/**
|
1525 |
+
* @param string $element
|
1526 |
+
* @param bool $bThrowException
|
1527 |
+
* @return string|null
|
1528 |
+
* @throws \Exception
|
1529 |
*/
|
1530 |
+
protected function findElementClass( string $element, $bThrowException = true ) {
|
1531 |
+
$theClass = null;
|
1532 |
+
|
1533 |
+
$roots = array_map( function ( $root ) {
|
1534 |
+
return rtrim( $root, '\\' ).'\\';
|
1535 |
+
}, $this->getNamespaceRoots() );
|
1536 |
+
|
1537 |
+
foreach ( $roots as $NS ) {
|
1538 |
+
$maybe = $NS.$element;
|
1539 |
+
if ( @class_exists( $maybe ) ) {
|
1540 |
+
if ( ( new \ReflectionClass( $maybe ) )->isInstantiable() ) {
|
1541 |
+
$theClass = $maybe;
|
1542 |
+
break;
|
1543 |
+
}
|
1544 |
+
}
|
1545 |
}
|
|
|
|
|
1546 |
|
1547 |
+
if ( $bThrowException && is_null( $theClass ) ) {
|
1548 |
+
throw new \Exception( sprintf( 'Could not find class for element "%s".', $element ) );
|
|
|
|
|
|
|
|
|
|
|
1549 |
}
|
1550 |
+
return $theClass;
|
1551 |
}
|
1552 |
|
1553 |
+
protected function getBaseNamespace() {
|
1554 |
+
return __NAMESPACE__;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1555 |
}
|
1556 |
|
1557 |
+
protected function getNamespace() :string {
|
1558 |
+
return ( new \ReflectionClass( $this ) )->getNamespaceName();
|
|
|
|
|
|
|
1559 |
}
|
1560 |
|
1561 |
+
protected function getNamespaceRoots() :array {
|
1562 |
+
return [
|
1563 |
+
$this->getNamespace(),
|
1564 |
+
$this->getBaseNamespace()
|
1565 |
+
];
|
1566 |
}
|
1567 |
|
1568 |
/**
|
1569 |
+
* Saves the options to the WordPress Options store.
|
1570 |
+
* @return void
|
1571 |
+
* @deprecated 8.4
|
1572 |
*/
|
1573 |
+
public function savePluginOptions() {
|
1574 |
+
$this->saveModOptions();
|
|
|
1575 |
}
|
1576 |
}
|
src/lib/src/Modules/Base/OneTimeExecute.php
DELETED
@@ -1,37 +0,0 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Base;
|
4 |
-
|
5 |
-
/**
|
6 |
-
* Trait OneTimeExecute
|
7 |
-
* @package FernleafSystems\Wordpress\Plugin\Shield\Modules\Base
|
8 |
-
* @deprecated 10.0
|
9 |
-
*/
|
10 |
-
trait OneTimeExecute {
|
11 |
-
|
12 |
-
private $bExecuted = false;
|
13 |
-
|
14 |
-
/**
|
15 |
-
* @return bool
|
16 |
-
*/
|
17 |
-
protected function canRun() {
|
18 |
-
return true;
|
19 |
-
}
|
20 |
-
|
21 |
-
public function execute() {
|
22 |
-
if ( !$this->isAlreadyExecuted() && $this->canRun() ) {
|
23 |
-
$this->bExecuted = true;
|
24 |
-
$this->run();
|
25 |
-
}
|
26 |
-
}
|
27 |
-
|
28 |
-
/**
|
29 |
-
* @return bool
|
30 |
-
*/
|
31 |
-
protected function isAlreadyExecuted() {
|
32 |
-
return (bool)$this->bExecuted;
|
33 |
-
}
|
34 |
-
|
35 |
-
protected function run() {
|
36 |
-
}
|
37 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
src/lib/src/Modules/Base/Options.php
CHANGED
@@ -364,10 +364,7 @@ class Options {
|
|
364 |
return $aKeys;
|
365 |
}
|
366 |
|
367 |
-
|
368 |
-
* @return array
|
369 |
-
*/
|
370 |
-
public function getOptionsForPluginUse() {
|
371 |
|
372 |
$aOptionsData = [];
|
373 |
|
@@ -562,18 +559,15 @@ class Options {
|
|
562 |
}
|
563 |
|
564 |
/**
|
565 |
-
* @param string $
|
566 |
-
* @param string $
|
567 |
* @return mixed|null
|
568 |
*/
|
569 |
-
public function getOptProperty( $
|
570 |
-
$
|
571 |
-
return
|
572 |
}
|
573 |
|
574 |
-
/**
|
575 |
-
* @return array
|
576 |
-
*/
|
577 |
public function getStoredOptions() :array {
|
578 |
try {
|
579 |
return $this->loadOptionsValuesFromStorage();
|
@@ -590,22 +584,14 @@ class Options {
|
|
590 |
return $this->aRawOptionsConfigData;
|
591 |
}
|
592 |
|
593 |
-
|
594 |
-
|
595 |
-
|
596 |
-
*/
|
597 |
-
protected function getRawData_AllOptions() {
|
598 |
-
$aRaw = $this->getRawData_FullFeatureConfig();
|
599 |
-
return ( isset( $aRaw[ 'options' ] ) && is_array( $aRaw[ 'options' ] ) ) ? $aRaw[ 'options' ] : [];
|
600 |
}
|
601 |
|
602 |
-
|
603 |
-
|
604 |
-
|
605 |
-
*/
|
606 |
-
protected function getRawData_OptionsSections() {
|
607 |
-
$aAllRawOptions = $this->getRawData_FullFeatureConfig();
|
608 |
-
return isset( $aAllRawOptions[ 'sections' ] ) ? $aAllRawOptions[ 'sections' ] : [];
|
609 |
}
|
610 |
|
611 |
protected function getRawData_Requirements() :array {
|
@@ -613,27 +599,18 @@ class Options {
|
|
613 |
return $raw[ 'requirements' ] ?? [];
|
614 |
}
|
615 |
|
616 |
-
|
617 |
-
|
618 |
-
|
619 |
-
*/
|
620 |
-
protected function getRawData_MenuItems() {
|
621 |
-
$aAllRawOptions = $this->getRawData_FullFeatureConfig();
|
622 |
-
return isset( $aAllRawOptions[ 'menu_items' ] ) ? $aAllRawOptions[ 'menu_items' ] : [];
|
623 |
}
|
624 |
|
625 |
-
|
626 |
-
|
627 |
-
|
628 |
-
|
629 |
-
*/
|
630 |
-
public function getRawData_SingleOption( $key ) {
|
631 |
-
foreach ( $this->getRawData_AllOptions() as $aOption ) {
|
632 |
-
if ( isset( $aOption[ 'key' ] ) && ( $key == $aOption[ 'key' ] ) ) {
|
633 |
-
return $aOption;
|
634 |
}
|
635 |
}
|
636 |
-
return
|
637 |
}
|
638 |
|
639 |
public function getRebuildFromFile() :bool {
|
@@ -644,11 +621,11 @@ class Options {
|
|
644 |
* @param string $key
|
645 |
* @return string
|
646 |
*/
|
647 |
-
public function getSelectOptionValueText( $key ) {
|
648 |
$sText = '';
|
649 |
-
foreach ( $this->getOptDefinition( $key )[ 'value_options' ] as $
|
650 |
-
if ( $
|
651 |
-
$sText = $
|
652 |
break;
|
653 |
}
|
654 |
}
|
@@ -678,16 +655,20 @@ class Options {
|
|
678 |
return (bool)$this->getFeatureProperty( 'run_if_verified_bot' );
|
679 |
}
|
680 |
|
|
|
|
|
|
|
|
|
681 |
public function isOptChanged( string $key ) :bool {
|
682 |
return is_array( $this->aOld ) && isset( $this->aOld[ $key ] );
|
683 |
}
|
684 |
|
685 |
-
public function isOptPremium( string $
|
686 |
-
return (bool)$this->getOptProperty( $
|
687 |
}
|
688 |
|
689 |
-
public function resetOptToDefault( string $
|
690 |
-
return $this->setOpt( $
|
691 |
}
|
692 |
|
693 |
/**
|
364 |
return $aKeys;
|
365 |
}
|
366 |
|
367 |
+
public function getOptionsForPluginUse() :array {
|
|
|
|
|
|
|
368 |
|
369 |
$aOptionsData = [];
|
370 |
|
559 |
}
|
560 |
|
561 |
/**
|
562 |
+
* @param string $key
|
563 |
+
* @param string $prop
|
564 |
* @return mixed|null
|
565 |
*/
|
566 |
+
public function getOptProperty( string $key, string $prop ) {
|
567 |
+
$opt = $this->getRawData_SingleOption( $key );
|
568 |
+
return $opt[ $prop ] ?? null;
|
569 |
}
|
570 |
|
|
|
|
|
|
|
571 |
public function getStoredOptions() :array {
|
572 |
try {
|
573 |
return $this->loadOptionsValuesFromStorage();
|
584 |
return $this->aRawOptionsConfigData;
|
585 |
}
|
586 |
|
587 |
+
protected function getRawData_AllOptions() :array {
|
588 |
+
$raw = $this->getRawData_FullFeatureConfig();
|
589 |
+
return $raw[ 'options' ] ?? [];
|
|
|
|
|
|
|
|
|
590 |
}
|
591 |
|
592 |
+
protected function getRawData_OptionsSections() :array {
|
593 |
+
$raw = $this->getRawData_FullFeatureConfig();
|
594 |
+
return $raw[ 'sections' ] ?? [];
|
|
|
|
|
|
|
|
|
595 |
}
|
596 |
|
597 |
protected function getRawData_Requirements() :array {
|
599 |
return $raw[ 'requirements' ] ?? [];
|
600 |
}
|
601 |
|
602 |
+
protected function getRawData_MenuItems() :array {
|
603 |
+
$raw = $this->getRawData_FullFeatureConfig();
|
604 |
+
return $raw[ 'menu_items' ] ?? [];
|
|
|
|
|
|
|
|
|
605 |
}
|
606 |
|
607 |
+
public function getRawData_SingleOption( string $key ) :array {
|
608 |
+
foreach ( $this->getRawData_AllOptions() as $opt ) {
|
609 |
+
if ( isset( $opt[ 'key' ] ) && ( $key == $opt[ 'key' ] ) ) {
|
610 |
+
return $opt;
|
|
|
|
|
|
|
|
|
|
|
611 |
}
|
612 |
}
|
613 |
+
return [];
|
614 |
}
|
615 |
|
616 |
public function getRebuildFromFile() :bool {
|
621 |
* @param string $key
|
622 |
* @return string
|
623 |
*/
|
624 |
+
public function getSelectOptionValueText( string $key ) {
|
625 |
$sText = '';
|
626 |
+
foreach ( $this->getOptDefinition( $key )[ 'value_options' ] as $opt ) {
|
627 |
+
if ( $opt[ 'value_key' ] == $this->getOpt( $key ) ) {
|
628 |
+
$sText = $opt[ 'text' ];
|
629 |
break;
|
630 |
}
|
631 |
}
|
655 |
return (bool)$this->getFeatureProperty( 'run_if_verified_bot' );
|
656 |
}
|
657 |
|
658 |
+
public function isOptAdvanced( string $key ) :bool {
|
659 |
+
return (bool)$this->getOptProperty( $key, 'advanced' );
|
660 |
+
}
|
661 |
+
|
662 |
public function isOptChanged( string $key ) :bool {
|
663 |
return is_array( $this->aOld ) && isset( $this->aOld[ $key ] );
|
664 |
}
|
665 |
|
666 |
+
public function isOptPremium( string $key ) :bool {
|
667 |
+
return (bool)$this->getOptProperty( $key, 'premium' );
|
668 |
}
|
669 |
|
670 |
+
public function resetOptToDefault( string $key ) :self {
|
671 |
+
return $this->setOpt( $key, $this->getOptDefault( $key ) );
|
672 |
}
|
673 |
|
674 |
/**
|
src/lib/src/Modules/Base/Processor.php
ADDED
@@ -0,0 +1,60 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
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 |
+
/**
|
16 |
+
* @param ModCon $mod
|
17 |
+
*/
|
18 |
+
public function __construct( $mod ) {
|
19 |
+
$this->setMod( $mod );
|
20 |
+
add_action( 'init', [ $this, 'onWpInit' ], 9 );
|
21 |
+
add_action( 'wp_loaded', [ $this, 'onWpLoaded' ] );
|
22 |
+
add_action( $mod->prefix( 'plugin_shutdown' ), [ $this, 'onModuleShutdown' ] );
|
23 |
+
$this->setupCronHooks();
|
24 |
+
}
|
25 |
+
|
26 |
+
public function onWpInit() {
|
27 |
+
}
|
28 |
+
|
29 |
+
public function onWpLoaded() {
|
30 |
+
}
|
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 |
+
}
|
src/lib/src/Modules/Base/Reporting.php
ADDED
@@ -0,0 +1,49 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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 |
+
abstract class Reporting {
|
9 |
+
|
10 |
+
use ModConsumer;
|
11 |
+
|
12 |
+
/**
|
13 |
+
* @return Reports\BaseReporter[]
|
14 |
+
*/
|
15 |
+
public function getAlertReporters() :array {
|
16 |
+
return $this->assignMod( $this->enumAlertReporters() );
|
17 |
+
}
|
18 |
+
|
19 |
+
/**
|
20 |
+
* @return Reports\BaseReporter[]
|
21 |
+
*/
|
22 |
+
public function getInfoReporters() :array {
|
23 |
+
return $this->assignMod( $this->enumInfoReporters() );
|
24 |
+
}
|
25 |
+
|
26 |
+
/**
|
27 |
+
* @return Reports\BaseReporter[]
|
28 |
+
*/
|
29 |
+
protected function enumAlertReporters() :array {
|
30 |
+
return [];
|
31 |
+
}
|
32 |
+
|
33 |
+
/**
|
34 |
+
* @return Reports\BaseReporter[]
|
35 |
+
*/
|
36 |
+
protected function enumInfoReporters() :array {
|
37 |
+
return [];
|
38 |
+
}
|
39 |
+
|
40 |
+
/**
|
41 |
+
* @param Reports\BaseReporter[] $aReporters
|
42 |
+
* @return array
|
43 |
+
*/
|
44 |
+
protected function assignMod( array $aReporters ) :array {
|
45 |
+
return array_map( function ( $reporter ) {
|
46 |
+
return $reporter->setMod( $this->getMod() );
|
47 |
+
}, $aReporters );
|
48 |
+
}
|
49 |
+
}
|
src/lib/src/Modules/Base/ShieldOptions.php
CHANGED
@@ -4,6 +4,11 @@ namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Base;
|
|
4 |
|
5 |
use FernleafSystems\Wordpress\Services\Services;
|
6 |
|
|
|
|
|
|
|
|
|
|
|
7 |
class ShieldOptions extends Options {
|
8 |
|
9 |
public function getInstallationDays() :int {
|
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 {
|
src/lib/src/Modules/Base/ShieldUI.php
CHANGED
@@ -5,12 +5,15 @@ namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Base;
|
|
5 |
use FernleafSystems\Wordpress\Plugin\Shield;
|
6 |
use FernleafSystems\Wordpress\Services\Services;
|
7 |
|
|
|
|
|
|
|
|
|
|
|
8 |
class ShieldUI extends UI {
|
9 |
|
10 |
-
|
11 |
-
|
12 |
-
*/
|
13 |
-
public function getBaseDisplayData() {
|
14 |
/** @var \ICWP_WPSF_FeatureHandler_BaseWpsf $mod */
|
15 |
$mod = $this->getMod();
|
16 |
|
@@ -40,11 +43,10 @@ class ShieldUI extends UI {
|
|
40 |
'sec_admin_login' => $mod->getSecAdminLoginAjaxData(),
|
41 |
],
|
42 |
'flags' => [
|
43 |
-
'show_promo' => !$this->getCon()->isPremiumActive(),
|
44 |
'has_session' => $mod->hasSession()
|
45 |
],
|
46 |
'hrefs' => [
|
47 |
-
'aar_forget_key' => $
|
48 |
$this->getCon()->getLabels()[ 'AuthorURI' ] : 'https://shsec.io/gc'
|
49 |
],
|
50 |
'classes' => [
|
@@ -55,7 +57,14 @@ class ShieldUI extends UI {
|
|
55 |
Services::Request()->query( 'inav', '' )
|
56 |
] ) )
|
57 |
],
|
|
|
|
|
|
|
58 |
]
|
59 |
);
|
60 |
}
|
|
|
|
|
|
|
|
|
61 |
}
|
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 |
|
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' => [
|
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/Strings.php
CHANGED
@@ -28,10 +28,13 @@ class Strings {
|
|
28 |
__( 'Better Bot Detection', 'wp-simple-firewall' ),
|
29 |
__( 'Password Policies', 'wp-simple-firewall' ),
|
30 |
__( 'WooCommerce Support', 'wp-simple-firewall' ),
|
|
|
31 |
];
|
32 |
$aProFeaturesDisplay = array_intersect_key( $aProFeatures, array_flip( array_rand( $aProFeatures, 6 ) ) );
|
33 |
$aProFeaturesDisplay[] = __( 'and much more!' );
|
34 |
|
|
|
|
|
35 |
return Services::DataManipulation()->mergeArraysRecursive(
|
36 |
[
|
37 |
'see_help_video' => __( 'Watch Help Video' ),
|
@@ -53,7 +56,7 @@ class Strings {
|
|
53 |
'logged_in' => __( 'Logged-In', 'wp-simple-firewall' ),
|
54 |
'username' => __( 'Username' ),
|
55 |
'blog' => __( 'Blog', 'wp-simple-firewall' ),
|
56 |
-
'save_all_settings' =>
|
57 |
'plugin_name' => $con->getHumanName(),
|
58 |
'options_title' => __( 'Options', 'wp-simple-firewall' ),
|
59 |
'options_summary' => __( 'Configure Module', 'wp-simple-firewall' ),
|
@@ -66,6 +69,8 @@ class Strings {
|
|
66 |
'select' => __( 'Select' ),
|
67 |
'filters_clear' => __( 'Clear Filters', 'wp-simple-firewall' ),
|
68 |
'filters_apply' => __( 'Apply Filters', 'wp-simple-firewall' ),
|
|
|
|
|
69 |
'jump_to_option' => __( 'Find Plugin Option', 'wp-simple-firewall' ),
|
70 |
'type_below_search' => __( 'Type below to search all plugin options', 'wp-simple-firewall' ),
|
71 |
'pro_only_option' => __( 'Pro Only', 'wp-simple-firewall' ),
|
@@ -74,6 +79,17 @@ class Strings {
|
|
74 |
'go_pro_option' => sprintf( '<a href="%s" target="_blank">%s</a>',
|
75 |
'https://shsec.io/shieldgoprofeature', __( 'Please upgrade to Pro to control this option', 'wp-simple-firewall' ) ),
|
76 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
77 |
'description' => __( 'Description', 'wp-simple-firewall' ),
|
78 |
'loading' => __( 'Loading', 'wp-simple-firewall' ),
|
79 |
'aar_title' => __( 'Plugin Access Restricted', 'wp-simple-firewall' ),
|
@@ -99,9 +115,19 @@ class Strings {
|
|
99 |
'pro_features' => __( 'Pro features include', 'wp-simple-firewall' ),
|
100 |
'join_thousands_H' => __( "Join The 1,000s Who've Already Upgraded Their WordPress Security To Better Protect Their Sites.", 'wp-simple-firewall' ),
|
101 |
'join_thousands_P' => implode( ', ', $aProFeaturesDisplay ),
|
102 |
-
'get_pro_protection' => __( '
|
103 |
-
|
104 |
-
'
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
105 |
|
106 |
'wphashes_token' => 'WPHashes.com API Token',
|
107 |
'is_opt_importexport' => __( 'Is this option included with import/export?', 'wp-simple-firewall' ),
|
28 |
__( 'Better Bot Detection', 'wp-simple-firewall' ),
|
29 |
__( 'Password Policies', 'wp-simple-firewall' ),
|
30 |
__( 'WooCommerce Support', 'wp-simple-firewall' ),
|
31 |
+
__( 'MainWP Integration', 'wp-simple-firewall' ),
|
32 |
];
|
33 |
$aProFeaturesDisplay = array_intersect_key( $aProFeatures, array_flip( array_rand( $aProFeatures, 6 ) ) );
|
34 |
$aProFeaturesDisplay[] = __( 'and much more!' );
|
35 |
|
36 |
+
$bIsAdvanced = $this->getCon()->getModule_Plugin()->isShowAdvanced();
|
37 |
+
|
38 |
return Services::DataManipulation()->mergeArraysRecursive(
|
39 |
[
|
40 |
'see_help_video' => __( 'Watch Help Video' ),
|
56 |
'logged_in' => __( 'Logged-In', 'wp-simple-firewall' ),
|
57 |
'username' => __( 'Username' ),
|
58 |
'blog' => __( 'Blog', 'wp-simple-firewall' ),
|
59 |
+
'save_all_settings' => __( 'Save Settings', 'wp-simple-firewall' ),
|
60 |
'plugin_name' => $con->getHumanName(),
|
61 |
'options_title' => __( 'Options', 'wp-simple-firewall' ),
|
62 |
'options_summary' => __( 'Configure Module', 'wp-simple-firewall' ),
|
69 |
'select' => __( 'Select' ),
|
70 |
'filters_clear' => __( 'Clear Filters', 'wp-simple-firewall' ),
|
71 |
'filters_apply' => __( 'Apply Filters', 'wp-simple-firewall' ),
|
72 |
+
'jump_to_module' => __( 'Jump To Module Settings', 'wp-simple-firewall' ),
|
73 |
+
'this_page' => __( 'This Page', 'wp-simple-firewall' ),
|
74 |
'jump_to_option' => __( 'Find Plugin Option', 'wp-simple-firewall' ),
|
75 |
'type_below_search' => __( 'Type below to search all plugin options', 'wp-simple-firewall' ),
|
76 |
'pro_only_option' => __( 'Pro Only', 'wp-simple-firewall' ),
|
79 |
'go_pro_option' => sprintf( '<a href="%s" target="_blank">%s</a>',
|
80 |
'https://shsec.io/shieldgoprofeature', __( 'Please upgrade to Pro to control this option', 'wp-simple-firewall' ) ),
|
81 |
|
82 |
+
'mode' => __( 'Mode', 'wp-simple-firewall' ),
|
83 |
+
'mode_simple' => __( 'Simple', 'wp-simple-firewall' ),
|
84 |
+
'mode_advanced' => __( '', 'wp-simple-firewall' ),
|
85 |
+
'mode_switchto' => sprintf( '%s: %s', __( 'Switch To', 'wp-simple-firewall' ),
|
86 |
+
$bIsAdvanced ? __( 'Simple', 'wp-simple-firewall' ) : __( 'Advanced', 'wp-simple-firewall' ) ),
|
87 |
+
'mode_switchfrom' => sprintf( '%s: %s', __( 'Mode', 'wp-simple-firewall' ),
|
88 |
+
$bIsAdvanced ? __( 'Advanced', 'wp-simple-firewall' ) : __( 'Simple', 'wp-simple-firewall' ) ),
|
89 |
+
|
90 |
+
'dashboard' => __( 'Dashboard', 'wp-simple-firewall' ),
|
91 |
+
'dashboard_shield' => sprintf( __( '%s Dashboard', 'wp-simple-firewall' ), $con->getHumanName() ),
|
92 |
+
|
93 |
'description' => __( 'Description', 'wp-simple-firewall' ),
|
94 |
'loading' => __( 'Loading', 'wp-simple-firewall' ),
|
95 |
'aar_title' => __( 'Plugin Access Restricted', 'wp-simple-firewall' ),
|
115 |
'pro_features' => __( 'Pro features include', 'wp-simple-firewall' ),
|
116 |
'join_thousands_H' => __( "Join The 1,000s Who've Already Upgraded Their WordPress Security To Better Protect Their Sites.", 'wp-simple-firewall' ),
|
117 |
'join_thousands_P' => implode( ', ', $aProFeaturesDisplay ),
|
118 |
+
'get_pro_protection' => __( 'Get Pro Protection', 'wp-simple-firewall' ),
|
119 |
+
|
120 |
+
'recommendation' => ucfirst( __( 'recommendation', 'wp-simple-firewall' ) ),
|
121 |
+
'suggestion' => ucfirst( __( 'suggestion', 'wp-simple-firewall' ) ),
|
122 |
+
'box_welcome_title' => sprintf( __( 'Welcome To %s Security Insights Dashboard', 'wp-simple-firewall' ), $con->getHumanName() ),
|
123 |
+
'options' => __( 'Options', 'wp-simple-firewall' ),
|
124 |
+
'not_available' => __( 'Sorry, this feature is included with Pro subscriptions.', 'wp-simple-firewall' ),
|
125 |
+
'not_enabled' => __( "This feature isn't currently enabled.", 'wp-simple-firewall' ),
|
126 |
+
'please_upgrade' => __( 'You can get this feature (along with loads more) by going Pro.', 'wp-simple-firewall' ),
|
127 |
+
'please_enable' => __( 'Please turn on this feature in the options.', 'wp-simple-firewall' ),
|
128 |
+
'no_security_notices' => __( 'There are no important security notices at this time.', 'wp-simple-firewall' ),
|
129 |
+
'this_is_wonderful' => __( 'This is wonderful!', 'wp-simple-firewall' ),
|
130 |
+
'yyyymmdd' => __( 'YYYY-MM-DD', 'wp-simple-firewall' ),
|
131 |
|
132 |
'wphashes_token' => 'WPHashes.com API Token',
|
133 |
'is_opt_importexport' => __( 'Is this option included with import/export?', 'wp-simple-firewall' ),
|
src/lib/src/Modules/Base/UI.php
CHANGED
@@ -17,8 +17,10 @@ class UI {
|
|
17 |
* It has to handle the conversion of stored values to data to be displayed to the user.
|
18 |
*/
|
19 |
public function buildOptions() {
|
|
|
20 |
|
21 |
-
$bPremiumEnabled = $
|
|
|
22 |
|
23 |
$opts = $this->getOptions();
|
24 |
$aOptions = $opts->getOptionsForPluginUse();
|
@@ -30,7 +32,8 @@ class UI {
|
|
30 |
foreach ( $aSection[ 'options' ] as $nKey => $aOption ) {
|
31 |
$aOption[ 'is_value_default' ] = ( $aOption[ 'value' ] === $aOption[ 'default' ] );
|
32 |
$bIsPrem = isset( $aOption[ 'premium' ] ) && $aOption[ 'premium' ];
|
33 |
-
|
|
|
34 |
$aSection[ 'options' ][ $nKey ] = $this->buildOptionForUi( $aOption );
|
35 |
}
|
36 |
else {
|
@@ -50,20 +53,22 @@ class UI {
|
|
50 |
->getSectionStrings( $aSection[ 'slug' ] )
|
51 |
);
|
52 |
}
|
53 |
-
catch ( \Exception $
|
54 |
}
|
55 |
$aOptions[ $nSectionKey ] = $aSection;
|
56 |
}
|
57 |
|
58 |
-
$
|
59 |
-
|
60 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
61 |
}
|
62 |
-
$aOptions[ $nSectionKey ][ 'warnings' ] = array_merge(
|
63 |
-
$aWarnings,
|
64 |
-
$this->getSectionWarnings( $aSection[ 'slug' ] )
|
65 |
-
);
|
66 |
-
$aOptions[ $nSectionKey ][ 'notices' ] = $this->getSectionNotices( $aSection[ 'slug' ] );
|
67 |
}
|
68 |
}
|
69 |
|
@@ -143,10 +148,25 @@ class UI {
|
|
143 |
return $aOptParams;
|
144 |
}
|
145 |
|
146 |
-
|
147 |
-
|
148 |
-
|
149 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
150 |
$mod = $this->getMod();
|
151 |
$con = $this->getCon();
|
152 |
|
@@ -187,7 +207,9 @@ class UI {
|
|
187 |
'hidden_options' => $this->getOptions()->getHiddenOptions()
|
188 |
],
|
189 |
'ajax' => [
|
190 |
-
'mod_options'
|
|
|
|
|
191 |
],
|
192 |
'vendors' => [
|
193 |
'widget_freshdesk' => '3000000081' /* TODO: plugin spec config */
|
17 |
* It has to handle the conversion of stored values to data to be displayed to the user.
|
18 |
*/
|
19 |
public function buildOptions() {
|
20 |
+
$con = $this->getCon();
|
21 |
|
22 |
+
$bPremiumEnabled = $con->isPremiumExtensionsEnabled();
|
23 |
+
$bShowAdvanced = $con->getModule_Plugin()->isShowAdvanced();
|
24 |
|
25 |
$opts = $this->getOptions();
|
26 |
$aOptions = $opts->getOptionsForPluginUse();
|
32 |
foreach ( $aSection[ 'options' ] as $nKey => $aOption ) {
|
33 |
$aOption[ 'is_value_default' ] = ( $aOption[ 'value' ] === $aOption[ 'default' ] );
|
34 |
$bIsPrem = isset( $aOption[ 'premium' ] ) && $aOption[ 'premium' ];
|
35 |
+
$bIsAdv = isset( $aOption[ 'advanced' ] ) && $aOption[ 'advanced' ];
|
36 |
+
if ( ( !$bIsPrem || $bPremiumEnabled ) && ( !$bIsAdv || $bShowAdvanced ) ) {
|
37 |
$aSection[ 'options' ][ $nKey ] = $this->buildOptionForUi( $aOption );
|
38 |
}
|
39 |
else {
|
53 |
->getSectionStrings( $aSection[ 'slug' ] )
|
54 |
);
|
55 |
}
|
56 |
+
catch ( \Exception $e ) {
|
57 |
}
|
58 |
$aOptions[ $nSectionKey ] = $aSection;
|
59 |
}
|
60 |
|
61 |
+
if ( isset( $aOptions[ $nSectionKey ] ) ) {
|
62 |
+
$aWarnings = [];
|
63 |
+
if ( !$opts->isSectionReqsMet( $aSection[ 'slug' ] ) ) {
|
64 |
+
$aWarnings[] = __( 'Unfortunately your WordPress and/or PHP versions are too old to support this feature.', 'wp-simple-firewall' );
|
65 |
+
}
|
66 |
+
$aOptions[ $nSectionKey ][ 'warnings' ] = array_merge(
|
67 |
+
$aWarnings,
|
68 |
+
$this->getSectionWarnings( $aSection[ 'slug' ] )
|
69 |
+
);
|
70 |
+
$aOptions[ $nSectionKey ][ 'notices' ] = $this->getSectionNotices( $aSection[ 'slug' ] );
|
71 |
}
|
|
|
|
|
|
|
|
|
|
|
72 |
}
|
73 |
}
|
74 |
|
148 |
return $aOptParams;
|
149 |
}
|
150 |
|
151 |
+
public function buildSelectData_ModuleSettings() :array {
|
152 |
+
return $this->getMod()->getModulesSummaryData();
|
153 |
+
}
|
154 |
+
|
155 |
+
public function buildSelectData_OptionsSearch() :array {
|
156 |
+
$modsToSearch = array_filter(
|
157 |
+
$this->getMod()->getModulesSummaryData(),
|
158 |
+
function ( $modSummary ) {
|
159 |
+
return !empty( $modSummary[ 'show_mod_opts' ] );
|
160 |
+
}
|
161 |
+
);
|
162 |
+
$searchSelect = [];
|
163 |
+
foreach ( $modsToSearch as $slug => $summary ) {
|
164 |
+
$searchSelect[ $summary[ 'name' ] ] = $summary[ 'options' ];
|
165 |
+
}
|
166 |
+
return $searchSelect;
|
167 |
+
}
|
168 |
+
|
169 |
+
public function getBaseDisplayData() :array {
|
170 |
$mod = $this->getMod();
|
171 |
$con = $this->getCon();
|
172 |
|
207 |
'hidden_options' => $this->getOptions()->getHiddenOptions()
|
208 |
],
|
209 |
'ajax' => [
|
210 |
+
'mod_options' => $mod->getAjaxActionData( 'mod_options', true ),
|
211 |
+
'mod_opts_form_render' => $mod->getAjaxActionData( 'mod_opts_form_render', true ),
|
212 |
+
// 'mod_options' => $mod->getAjaxActionData( 'mod_options' ),
|
213 |
],
|
214 |
'vendors' => [
|
215 |
'widget_freshdesk' => '3000000081' /* TODO: plugin spec config */
|
src/lib/src/Modules/Base/Upgrade.php
CHANGED
@@ -27,13 +27,12 @@ class Upgrade {
|
|
27 |
* version is less than the upgrade version, run the upgrade code.
|
28 |
*/
|
29 |
protected function upgradeModule() {
|
30 |
-
$
|
31 |
-
$
|
32 |
-
foreach ( $
|
33 |
-
$
|
34 |
-
if ( version_compare( $
|
35 |
-
|
36 |
-
$this->{$sMethod}();
|
37 |
}
|
38 |
}
|
39 |
}
|
27 |
* version is less than the upgrade version, run the upgrade code.
|
28 |
*/
|
29 |
protected function upgradeModule() {
|
30 |
+
$con = $this->getCon();
|
31 |
+
$previous = $con->getPreviousVersion();
|
32 |
+
foreach ( $con->getPluginSpec()[ 'version_upgrades' ] as $version ) {
|
33 |
+
$upgradeMethod = 'upgrade_'.str_replace( '.', '', $version );
|
34 |
+
if ( version_compare( $previous, $version, '<' ) && method_exists( $this, $upgradeMethod ) ) {
|
35 |
+
$this->{$upgradeMethod}();
|
|
|
36 |
}
|
37 |
}
|
38 |
}
|
src/lib/src/Modules/Base/WpCli.php
CHANGED
@@ -16,14 +16,14 @@ class WpCli {
|
|
16 |
$oHandler->setMod( $this->getMod() )->execute();
|
17 |
}
|
18 |
}
|
19 |
-
catch ( \Exception $
|
20 |
}
|
21 |
}
|
22 |
|
23 |
/**
|
24 |
* @return WpCli[]
|
25 |
*/
|
26 |
-
protected function getAllCmdHandlers() {
|
27 |
return array_merge(
|
28 |
[ new ModuleStandard() ],
|
29 |
$this->getCmdHandlers()
|
@@ -33,7 +33,7 @@ class WpCli {
|
|
33 |
/**
|
34 |
* @return WpCli[]
|
35 |
*/
|
36 |
-
protected function getCmdHandlers() {
|
37 |
return [];
|
38 |
}
|
39 |
}
|
16 |
$oHandler->setMod( $this->getMod() )->execute();
|
17 |
}
|
18 |
}
|
19 |
+
catch ( \Exception $e ) {
|
20 |
}
|
21 |
}
|
22 |
|
23 |
/**
|
24 |
* @return WpCli[]
|
25 |
*/
|
26 |
+
protected function getAllCmdHandlers() :array {
|
27 |
return array_merge(
|
28 |
[ new ModuleStandard() ],
|
29 |
$this->getCmdHandlers()
|
33 |
/**
|
34 |
* @return WpCli[]
|
35 |
*/
|
36 |
+
protected function getCmdHandlers() :array {
|
37 |
return [];
|
38 |
}
|
39 |
}
|
src/lib/src/Modules/Base/WpCli/BaseWpCliCmd.php
CHANGED
@@ -21,7 +21,7 @@ abstract class BaseWpCliCmd {
|
|
21 |
try {
|
22 |
$this->addCmds();
|
23 |
}
|
24 |
-
catch ( \Exception $
|
25 |
}
|
26 |
}
|
27 |
|
21 |
try {
|
22 |
$this->addCmds();
|
23 |
}
|
24 |
+
catch ( \Exception $e ) {
|
25 |
}
|
26 |
}
|
27 |
|
src/lib/src/Modules/BaseShield/AjaxHandler.php
ADDED
@@ -0,0 +1,71 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php declare( strict_types=1 );
|
2 |
+
|
3 |
+
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\BaseShield;
|
4 |
+
|
5 |
+
use FernleafSystems\Wordpress\Plugin\Shield\Modules\Base;
|
6 |
+
|
7 |
+
class AjaxHandler extends Base\AjaxHandler {
|
8 |
+
|
9 |
+
protected function processAjaxAction( string $action ) :array {
|
10 |
+
$response = [];
|
11 |
+
$mod = $this->getMod();
|
12 |
+
|
13 |
+
switch ( $action ) {
|
14 |
+
|
15 |
+
case 'mod_opts_form_render':
|
16 |
+
$response = $this->ajaxExec_ModOptionsFormRender();
|
17 |
+
break;
|
18 |
+
|
19 |
+
case 'mod_options':
|
20 |
+
$response = $this->ajaxExec_ModOptions();
|
21 |
+
break;
|
22 |
+
|
23 |
+
case 'wiz_process_step':
|
24 |
+
if ( $mod->hasWizard() ) {
|
25 |
+
$response = $mod->getWizardHandler()->ajaxExec_WizProcessStep();
|
26 |
+
}
|
27 |
+
break;
|
28 |
+
|
29 |
+
case 'wiz_render_step':
|
30 |
+
if ( $mod->hasWizard() ) {
|
31 |
+
$response = $mod->getWizardHandler()->ajaxExec_WizRenderStep();
|
32 |
+
}
|
33 |
+
break;
|
34 |
+
|
35 |
+
default:
|
36 |
+
$response = parent::processAjaxAction( $action );
|
37 |
+
}
|
38 |
+
|
39 |
+
return $response;
|
40 |
+
}
|
41 |
+
|
42 |
+
protected function ajaxExec_ModOptions() :array {
|
43 |
+
|
44 |
+
$name = $this->getCon()->getHumanName();
|
45 |
+
|
46 |
+
try {
|
47 |
+
$this->getMod()->saveOptionsSubmit();
|
48 |
+
$success = true;
|
49 |
+
$msg = sprintf( __( '%s Plugin options updated successfully.', 'wp-simple-firewall' ), $name );
|
50 |
+
}
|
51 |
+
catch ( \Exception $e ) {
|
52 |
+
$success = false;
|
53 |
+
$msg = sprintf( __( 'Failed to update %s plugin options.', 'wp-simple-firewall' ), $name )
|
54 |
+
.' '.$e->getMessage();
|
55 |
+
}
|
56 |
+
|
57 |
+
return [
|
58 |
+
'success' => $success,
|
59 |
+
'html' => '', //we reload the page
|
60 |
+
'message' => $msg
|
61 |
+
];
|
62 |
+
}
|
63 |
+
|
64 |
+
protected function ajaxExec_ModOptionsFormRender() :array {
|
65 |
+
return [
|
66 |
+
'success' => true,
|
67 |
+
'html' => $this->getMod()->renderOptionsForm(),
|
68 |
+
'message' => 'loaded'
|
69 |
+
];
|
70 |
+
}
|
71 |
+
}
|
src/lib/src/Modules/BaseShield/ModCon.php
ADDED
@@ -0,0 +1,256 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php declare( strict_types=1 );
|
2 |
+
|
3 |
+
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\BaseShield;
|
4 |
+
|
5 |
+
use FernleafSystems\Wordpress\Plugin\Shield;
|
6 |
+
use FernleafSystems\Wordpress\Plugin\Shield\Modules\Base;
|
7 |
+
use FernleafSystems\Wordpress\Plugin\Shield\Modules\Plugin;
|
8 |
+
use FernleafSystems\Wordpress\Services\Services;
|
9 |
+
use FernleafSystems\Wordpress\Services\Utilities;
|
10 |
+
use FernleafSystems\Wordpress\Services\Utilities\Net\IpIdentify;
|
11 |
+
|
12 |
+
class ModCon extends Base\ModCon {
|
13 |
+
|
14 |
+
/**
|
15 |
+
* @var bool
|
16 |
+
*/
|
17 |
+
protected static $bIsVerifiedBot;
|
18 |
+
|
19 |
+
/**
|
20 |
+
* @var bool
|
21 |
+
*/
|
22 |
+
private static $bVisitorIsWhitelisted;
|
23 |
+
|
24 |
+
/**
|
25 |
+
* @return bool
|
26 |
+
* @deprecated 10.1
|
27 |
+
*/
|
28 |
+
public function canCacheDirWrite() :bool {
|
29 |
+
return ( new Shield\Modules\Plugin\Lib\TestCacheDirWrite() )
|
30 |
+
->setMod( $this->getCon()->getModule_Plugin() )
|
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 |
+
*/
|
47 |
+
public function getDbHandler_Sessions() {
|
48 |
+
return $this->getCon()
|
49 |
+
->getModule_Sessions()
|
50 |
+
->getDbHandler_Sessions();
|
51 |
+
}
|
52 |
+
|
53 |
+
/**
|
54 |
+
* @return Shield\Databases\Session\EntryVO|null
|
55 |
+
*/
|
56 |
+
public function getSession() {
|
57 |
+
return $this->getCon()
|
58 |
+
->getModule_Sessions()
|
59 |
+
->getSessionCon()
|
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 |
+
*/
|
74 |
+
public function hasValidRequestIP() {
|
75 |
+
return Services::IP()->isValidIp( Services::IP()->getRequestIp() );
|
76 |
+
}
|
77 |
+
|
78 |
+
public function onWpInit() {
|
79 |
+
parent::onWpInit();
|
80 |
+
if ( $this->isThisModulePage() && !$this->isWizardPage() && ( $this->getSlug() != 'insights' ) ) {
|
81 |
+
$this->redirectToInsightsSubPage();
|
82 |
+
}
|
83 |
+
}
|
84 |
+
|
85 |
+
protected function redirectToInsightsSubPage() {
|
86 |
+
Services::Response()->redirect(
|
87 |
+
$this->getCon()->getModule_Insights()->getUrl_AdminPage(),
|
88 |
+
[
|
89 |
+
'inav' => 'settings',
|
90 |
+
'subnav' => $this->getSlug()
|
91 |
+
],
|
92 |
+
true, false
|
93 |
+
);
|
94 |
+
}
|
95 |
+
|
96 |
+
public function getCaptchaCfg() :Plugin\Lib\Captcha\CaptchaConfigVO {
|
97 |
+
$plugMod = $this->getCon()->getModule_Plugin();
|
98 |
+
/** @var Shield\Modules\Plugin\Options $plugOpts */
|
99 |
+
$plugOpts = $plugMod->getOptions();
|
100 |
+
$cfg = ( new Plugin\Lib\Captcha\CaptchaConfigVO() )->applyFromArray( $plugOpts->getCaptchaConfig() );
|
101 |
+
$cfg->invisible = $cfg->theme === 'invisible';
|
102 |
+
|
103 |
+
if ( $cfg->provider === Plugin\Lib\Captcha\CaptchaConfigVO::PROV_GOOGLE_RECAP2 ) {
|
104 |
+
$cfg->url_api = 'https://www.google.com/recaptcha/api.js';
|
105 |
+
}
|
106 |
+
elseif ( $cfg->provider === Plugin\Lib\Captcha\CaptchaConfigVO::PROV_HCAPTCHA ) {
|
107 |
+
$cfg->url_api = 'https://hcaptcha.com/1/api.js';
|
108 |
+
}
|
109 |
+
else {
|
110 |
+
error_log( 'CAPTCHA Provider not supported: '.$cfg->provider );
|
111 |
+
}
|
112 |
+
|
113 |
+
$cfg->js_handle = $this->getCon()->prefix( $cfg->provider );
|
114 |
+
|
115 |
+
return $cfg;
|
116 |
+
}
|
117 |
+
|
118 |
+
public function getSecAdminLoginAjaxData() :array {
|
119 |
+
// We set a custom mod_slug so that this module handles the ajax request
|
120 |
+
$dat = $this->getAjaxActionData( 'sec_admin_login' );
|
121 |
+
$dat[ 'mod_slug' ] = $this->prefix( 'admin_access_restriction' );
|
122 |
+
return $dat;
|
123 |
+
}
|
124 |
+
|
125 |
+
protected function getSecAdminCheckAjaxData() :array {
|
126 |
+
// We set a custom mod_slug so that this module handles the ajax request
|
127 |
+
$dat = $this->getAjaxActionData( 'sec_admin_check' );
|
128 |
+
$dat[ 'mod_slug' ] = $this->prefix( 'admin_access_restriction' );
|
129 |
+
return $dat;
|
130 |
+
}
|
131 |
+
|
132 |
+
public function getPluginReportEmail() :string {
|
133 |
+
return $this->getCon()
|
134 |
+
->getModule_Plugin()
|
135 |
+
->getPluginReportEmail();
|
136 |
+
}
|
137 |
+
|
138 |
+
/**
|
139 |
+
* @uses echo()
|
140 |
+
*/
|
141 |
+
public function displayModuleAdminPage() {
|
142 |
+
if ( $this->canDisplayOptionsForm() ) {
|
143 |
+
parent::displayModuleAdminPage();
|
144 |
+
}
|
145 |
+
else {
|
146 |
+
echo $this->renderRestrictedPage();
|
147 |
+
}
|
148 |
+
}
|
149 |
+
|
150 |
+
protected function renderRestrictedPage() :string {
|
151 |
+
/** @var Shield\Modules\SecurityAdmin\Options $secOpts */
|
152 |
+
$secOpts = $this->getCon()
|
153 |
+
->getModule_SecAdmin()
|
154 |
+
->getOptions();
|
155 |
+
$aData = Services::DataManipulation()
|
156 |
+
->mergeArraysRecursive(
|
157 |
+
$this->getUIHandler()->getBaseDisplayData(),
|
158 |
+
[
|
159 |
+
'ajax' => [
|
160 |
+
'restricted_access' => $this->getAjaxActionData( 'restricted_access' ),
|
161 |
+
],
|
162 |
+
'strings' => [
|
163 |
+
'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' ),
|
164 |
+
'click_email' => __( "Click here to send the verification email.", 'wp-simple-firewall' ),
|
165 |
+
'send_to_email' => sprintf( __( "Email will be sent to %s", 'wp-simple-firewall' ),
|
166 |
+
Utilities\Obfuscate::Email( $this->getPluginReportEmail() ) ),
|
167 |
+
'no_email_override' => __( "The Security Administrator has restricted the use of the email override feature.", 'wp-simple-firewall' ),
|
168 |
+
],
|
169 |
+
'flags' => [
|
170 |
+
'allow_email_override' => $secOpts->isEmailOverridePermitted()
|
171 |
+
]
|
172 |
+
]
|
173 |
+
);
|
174 |
+
return $this->renderTemplate( '/wpadmin_pages/security_admin/index.twig', $aData, true );
|
175 |
+
}
|
176 |
+
|
177 |
+
/**
|
178 |
+
* @return bool
|
179 |
+
*/
|
180 |
+
public function getIfSupport3rdParty() {
|
181 |
+
return $this->isPremium();
|
182 |
+
}
|
183 |
+
|
184 |
+
/**
|
185 |
+
* @return bool
|
186 |
+
* @throws \Exception
|
187 |
+
*/
|
188 |
+
protected function isReadyToExecute() :bool {
|
189 |
+
$opts = $this->getOptions();
|
190 |
+
return ( $opts->isModuleRunIfWhitelisted() || !$this->isVisitorWhitelisted() )
|
191 |
+
&& ( $opts->isModuleRunIfVerifiedBot() || !$this->isVerifiedBot() )
|
192 |
+
&& ( $opts->isModuleRunUnderWpCli() || !Services::WpGeneral()->isWpCli() )
|
193 |
+
&& parent::isReadyToExecute();
|
194 |
+
}
|
195 |
+
|
196 |
+
public function isVisitorWhitelisted() :bool {
|
197 |
+
if ( !isset( self::$bVisitorIsWhitelisted ) ) {
|
198 |
+
self::$bVisitorIsWhitelisted =
|
199 |
+
( new Shield\Modules\IPs\Lib\Ops\LookupIpOnList() )
|
200 |
+
->setDbHandler( $this->getCon()->getModule_IPs()->getDbHandler_IPs() )
|
201 |
+
->setIP( Services::IP()->getRequestIp() )
|
202 |
+
->setListTypeWhite()
|
203 |
+
->lookup()
|
204 |
+
instanceof Shield\Databases\IPs\EntryVO;
|
205 |
+
}
|
206 |
+
return self::$bVisitorIsWhitelisted;
|
207 |
+
}
|
208 |
+
|
209 |
+
public function isVerifiedBot() :bool {
|
210 |
+
if ( !isset( self::$bIsVerifiedBot ) ) {
|
211 |
+
$srvIP = Services::IP();
|
212 |
+
self::$bIsVerifiedBot = !$srvIP->isLoopback() &&
|
213 |
+
!in_array( $srvIP->getIpDetector()->getIPIdentity(), [
|
214 |
+
IpIdentify::UNKNOWN,
|
215 |
+
IpIdentify::THIS_SERVER,
|
216 |
+
IpIdentify::VISITOR,
|
217 |
+
] );
|
218 |
+
}
|
219 |
+
return self::$bIsVerifiedBot;
|
220 |
+
}
|
221 |
+
|
222 |
+
public function isXmlrpcBypass() :bool {
|
223 |
+
return $this->getCon()
|
224 |
+
->getModule_Plugin()
|
225 |
+
->isXmlrpcBypass();
|
226 |
+
}
|
227 |
+
|
228 |
+
/**
|
229 |
+
* @param string[] $aArray
|
230 |
+
* @param string $sPregReplacePattern
|
231 |
+
* @return string[]
|
232 |
+
*/
|
233 |
+
protected function cleanStringArray( $aArray, $sPregReplacePattern ) {
|
234 |
+
$aCleaned = [];
|
235 |
+
if ( !is_array( $aArray ) ) {
|
236 |
+
return $aCleaned;
|
237 |
+
}
|
238 |
+
|
239 |
+
foreach ( $aArray as $nKey => $sVal ) {
|
240 |
+
$sVal = preg_replace( $sPregReplacePattern, '', $sVal );
|
241 |
+
if ( !empty( $sVal ) ) {
|
242 |
+
$aCleaned[] = $sVal;
|
243 |
+
}
|
244 |
+
}
|
245 |
+
return array_unique( array_filter( $aCleaned ) );
|
246 |
+
}
|
247 |
+
|
248 |
+
protected function getNamespaceRoots() :array {
|
249 |
+
// Ensure order of namespaces is 'Module', 'BaseShield', then 'Base'
|
250 |
+
return [
|
251 |
+
$this->getNamespace(),
|
252 |
+
$this->getCon()->getModulesNamespace().'\\BaseShield',
|
253 |
+
$this->getBaseNamespace(),
|
254 |
+
];
|
255 |
+
}
|
256 |
+
}
|
src/lib/src/Modules/BaseShield/Options.php
ADDED
@@ -0,0 +1,30 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
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 |
+
class Options extends Base\Options {
|
9 |
+
|
10 |
+
public function getInstallationDays() :int {
|
11 |
+
$nTimeInstalled = $this->getCon()
|
12 |
+
->getModule_Plugin()
|
13 |
+
->getInstallDate();
|
14 |
+
if ( empty( $nTimeInstalled ) ) {
|
15 |
+
return 0;
|
16 |
+
}
|
17 |
+
return (int)round( ( Services::Request()->ts() - $nTimeInstalled )/DAY_IN_SECONDS );
|
18 |
+
}
|
19 |
+
|
20 |
+
public function isPremium() :bool {
|
21 |
+
return $this->getCon()->isPremiumActive();
|
22 |
+
}
|
23 |
+
|
24 |
+
public function isShowPromoAdminNotices() :bool {
|
25 |
+
return $this->getCon()
|
26 |
+
->getModule_Plugin()
|
27 |
+
->getOptions()
|
28 |
+
->isOpt( 'enable_upgrade_admin_notice', 'Y' );
|
29 |
+
}
|
30 |
+
}
|
src/lib/src/Modules/BaseShield/{ShieldModCon.php → Processor.php}
RENAMED
@@ -1,9 +1,9 @@
|
|
1 |
-
<?php
|
2 |
|
3 |
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\BaseShield;
|
4 |
|
5 |
use FernleafSystems\Wordpress\Plugin\Shield\Modules\Base;
|
6 |
|
7 |
-
class
|
8 |
|
9 |
}
|
1 |
+
<?php declare( strict_types=1 );
|
2 |
|
3 |
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\BaseShield;
|
4 |
|
5 |
use FernleafSystems\Wordpress\Plugin\Shield\Modules\Base;
|
6 |
|
7 |
+
class Processor extends Base\Processor {
|
8 |
|
9 |
}
|
src/lib/src/Modules/BaseShield/ShieldProcessor.php
CHANGED
@@ -5,24 +5,15 @@ namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\BaseShield;
|
|
5 |
use FernleafSystems\Wordpress\Plugin\Shield\Modules\Base;
|
6 |
use FernleafSystems\Wordpress\Services\Services;
|
7 |
|
|
|
|
|
|
|
|
|
|
|
8 |
class ShieldProcessor extends Base\BaseProcessor {
|
9 |
|
10 |
const RECAPTCHA_JS_HANDLE = 'icwp-google-recaptcha';
|
11 |
|
12 |
-
/**
|
13 |
-
* @var bool
|
14 |
-
*/
|
15 |
-
private static $bRecaptchaEnqueue = false;
|
16 |
-
|
17 |
-
/**
|
18 |
-
* Resets the object values to be re-used anew
|
19 |
-
*/
|
20 |
-
public function init() {
|
21 |
-
parent::init();
|
22 |
-
$con = $this->getCon();
|
23 |
-
add_filter( $con->prefix( 'collect_tracking_data' ), [ $this, 'tracking_DataCollect' ] );
|
24 |
-
}
|
25 |
-
|
26 |
/**
|
27 |
* @param \WP_User $oUser
|
28 |
* @return bool
|
@@ -39,25 +30,4 @@ class ShieldProcessor extends Base\BaseProcessor {
|
|
39 |
|
40 |
return $bIsSubject;
|
41 |
}
|
42 |
-
|
43 |
-
/**
|
44 |
-
* Filter used to collect plugin data for tracking. Fired from the plugin processor only if the option is enabled
|
45 |
-
* - it is not enabled by default.
|
46 |
-
* Note that in this case we "mask" options that have been identified as "sensitive" - i.e. could contain
|
47 |
-
* identifiable data.
|
48 |
-
*
|
49 |
-
* @param $aData
|
50 |
-
* @return array
|
51 |
-
*/
|
52 |
-
public function tracking_DataCollect( $aData ) {
|
53 |
-
if ( !is_array( $aData ) ) {
|
54 |
-
$aData = [];
|
55 |
-
}
|
56 |
-
$oMod = $this->getMod();
|
57 |
-
$aOptions = $oMod->collectOptionsForTracking();
|
58 |
-
if ( !empty( $aOptions ) ) {
|
59 |
-
$aData[ $oMod->getSlug() ] = [ 'options' => $oMod->collectOptionsForTracking() ];
|
60 |
-
}
|
61 |
-
return $aData;
|
62 |
-
}
|
63 |
}
|
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.1
|
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
|
30 |
|
31 |
return $bIsSubject;
|
32 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
33 |
}
|
src/lib/src/Modules/BaseShield/UI.php
ADDED
@@ -0,0 +1,66 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\BaseShield;
|
4 |
+
|
5 |
+
use FernleafSystems\Wordpress\Plugin\Shield\Modules\Base;
|
6 |
+
use FernleafSystems\Wordpress\Plugin\Shield;
|
7 |
+
use FernleafSystems\Wordpress\Services\Services;
|
8 |
+
|
9 |
+
class UI extends Base\UI {
|
10 |
+
|
11 |
+
public function getBaseDisplayData() :array {
|
12 |
+
$con = $this->getCon();
|
13 |
+
/** @var \ICWP_WPSF_FeatureHandler_BaseWpsf $mod */
|
14 |
+
$mod = $this->getMod();
|
15 |
+
|
16 |
+
return Services::DataManipulation()->mergeArraysRecursive(
|
17 |
+
parent::getBaseDisplayData(),
|
18 |
+
[
|
19 |
+
'head' => [
|
20 |
+
'html' => [
|
21 |
+
'lang' => Services::WpGeneral()->getLocale( '-' ),
|
22 |
+
'dir' => is_rtl() ? 'rtl' : 'ltr',
|
23 |
+
],
|
24 |
+
'meta' => [
|
25 |
+
[
|
26 |
+
'type' => 'http-equiv',
|
27 |
+
'type_type' => 'Cache-Control',
|
28 |
+
'content' => 'no-store, no-cache',
|
29 |
+
],
|
30 |
+
[
|
31 |
+
'type' => 'http-equiv',
|
32 |
+
'type_type' => 'Expires',
|
33 |
+
'content' => '0',
|
34 |
+
],
|
35 |
+
],
|
36 |
+
'scripts' => []
|
37 |
+
],
|
38 |
+
'ajax' => [
|
39 |
+
'sec_admin_login' => $mod->getSecAdminLoginAjaxData(),
|
40 |
+
],
|
41 |
+
'flags' => [
|
42 |
+
'has_session' => $mod->hasSession()
|
43 |
+
],
|
44 |
+
'hrefs' => [
|
45 |
+
'aar_forget_key' => $con->getModule_SecAdmin()->isWlEnabled() ?
|
46 |
+
$this->getCon()->getLabels()[ 'AuthorURI' ] : 'https://shsec.io/gc'
|
47 |
+
],
|
48 |
+
'classes' => [
|
49 |
+
'top_container' => implode( ' ', array_filter( [
|
50 |
+
'odp-outercontainer',
|
51 |
+
$this->getCon()->isPremiumActive() ? 'is-pro' : 'is-not-pro',
|
52 |
+
$mod->getModSlug(),
|
53 |
+
Services::Request()->query( 'inav', '' )
|
54 |
+
] ) )
|
55 |
+
],
|
56 |
+
'vars' => [
|
57 |
+
'related_hrefs' => $this->getSettingsRelatedLinks()
|
58 |
+
]
|
59 |
+
]
|
60 |
+
);
|
61 |
+
}
|
62 |
+
|
63 |
+
protected function getSettingsRelatedLinks() :array {
|
64 |
+
return [];
|
65 |
+
}
|
66 |
+
}
|
src/lib/src/Modules/CommentsFilter/AdminNotices.php
CHANGED
@@ -3,35 +3,32 @@
|
|
3 |
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\CommentsFilter;
|
4 |
|
5 |
use FernleafSystems\Wordpress\Plugin\Shield;
|
|
|
6 |
use FernleafSystems\Wordpress\Services\Services;
|
7 |
|
8 |
class AdminNotices extends Shield\Modules\Base\AdminNotices {
|
9 |
|
10 |
/**
|
11 |
-
* @
|
12 |
-
* @throws \Exception
|
13 |
*/
|
14 |
-
protected function processNotice( $
|
15 |
|
16 |
-
switch ( $
|
17 |
|
18 |
case 'akismet-running':
|
19 |
-
$this->buildNotice_AkismetRunning( $
|
20 |
break;
|
21 |
|
22 |
default:
|
23 |
-
parent::processNotice( $
|
24 |
break;
|
25 |
}
|
26 |
}
|
27 |
|
28 |
-
|
29 |
-
* @param Shield\Utilities\AdminNotices\NoticeVO $oNotice
|
30 |
-
*/
|
31 |
-
private function buildNotice_AkismetRunning( $oNotice ) {
|
32 |
$oWpPlugins = Services::WpPlugins();
|
33 |
|
34 |
-
$
|
35 |
'notice_attributes' => [],
|
36 |
'strings' => [
|
37 |
'title' => ucwords( __( 'Akismet Anti-SPAM plugin is also running', 'wp-simple-firewall' ) ),
|
@@ -46,28 +43,28 @@ class AdminNotices extends Shield\Modules\Base\AdminNotices {
|
|
46 |
}
|
47 |
|
48 |
/**
|
49 |
-
* @param Shield\Utilities\AdminNotices\NoticeVO $
|
50 |
* @return bool
|
51 |
*/
|
52 |
-
protected function isDisplayNeeded( $
|
53 |
/** @var Options $opts */
|
54 |
$opts = $this->getOptions();
|
55 |
|
56 |
-
switch ( $
|
57 |
|
58 |
case 'akismet-running':
|
59 |
$oWpPlugins = Services::WpPlugins();
|
60 |
$sPluginFile = $oWpPlugins->findPluginFileFromDirName( 'akismet' );
|
61 |
-
$
|
62 |
-
|
63 |
-
|
64 |
-
|
65 |
break;
|
66 |
|
67 |
default:
|
68 |
-
$
|
69 |
break;
|
70 |
}
|
71 |
-
return $
|
72 |
}
|
73 |
}
|
3 |
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\CommentsFilter;
|
4 |
|
5 |
use FernleafSystems\Wordpress\Plugin\Shield;
|
6 |
+
use FernleafSystems\Wordpress\Plugin\Shield\Utilities\AdminNotices\NoticeVO;
|
7 |
use FernleafSystems\Wordpress\Services\Services;
|
8 |
|
9 |
class AdminNotices extends Shield\Modules\Base\AdminNotices {
|
10 |
|
11 |
/**
|
12 |
+
* @inheritDoc
|
|
|
13 |
*/
|
14 |
+
protected function processNotice( NoticeVO $notice ) {
|
15 |
|
16 |
+
switch ( $notice->id ) {
|
17 |
|
18 |
case 'akismet-running':
|
19 |
+
$this->buildNotice_AkismetRunning( $notice );
|
20 |
break;
|
21 |
|
22 |
default:
|
23 |
+
parent::processNotice( $notice );
|
24 |
break;
|
25 |
}
|
26 |
}
|
27 |
|
28 |
+
private function buildNotice_AkismetRunning( NoticeVO $notice ) {
|
|
|
|
|
|
|
29 |
$oWpPlugins = Services::WpPlugins();
|
30 |
|
31 |
+
$notice->render_data = [
|
32 |
'notice_attributes' => [],
|
33 |
'strings' => [
|
34 |
'title' => ucwords( __( 'Akismet Anti-SPAM plugin is also running', 'wp-simple-firewall' ) ),
|
43 |
}
|
44 |
|
45 |
/**
|
46 |
+
* @param Shield\Utilities\AdminNotices\NoticeVO $notice
|
47 |
* @return bool
|
48 |
*/
|
49 |
+
protected function isDisplayNeeded( Shield\Utilities\AdminNotices\NoticeVO $notice ) :bool {
|
50 |
/** @var Options $opts */
|
51 |
$opts = $this->getOptions();
|
52 |
|
53 |
+
switch ( $notice->id ) {
|
54 |
|
55 |
case 'akismet-running':
|
56 |
$oWpPlugins = Services::WpPlugins();
|
57 |
$sPluginFile = $oWpPlugins->findPluginFileFromDirName( 'akismet' );
|
58 |
+
$needed = $this->getMod()->isModuleEnabled()
|
59 |
+
&& !empty( $sPluginFile )
|
60 |
+
&& $oWpPlugins->isActive( $sPluginFile )
|
61 |
+
&& $opts->isEnabledHumanCheck();
|
62 |
break;
|
63 |
|
64 |
default:
|
65 |
+
$needed = parent::isDisplayNeeded( $notice );
|
66 |
break;
|
67 |
}
|
68 |
+
return $needed;
|
69 |
}
|
70 |
}
|
src/lib/src/Modules/CommentsFilter/AjaxHandler.php
CHANGED
@@ -5,7 +5,7 @@ namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\CommentsFilter;
|
|
5 |
use FernleafSystems\Wordpress\Plugin\Shield;
|
6 |
use FernleafSystems\Wordpress\Services\Services;
|
7 |
|
8 |
-
class AjaxHandler extends Shield\Modules\
|
9 |
|
10 |
protected function processAjaxAction( string $action ) :array {
|
11 |
|
5 |
use FernleafSystems\Wordpress\Plugin\Shield;
|
6 |
use FernleafSystems\Wordpress\Services\Services;
|
7 |
|
8 |
+
class AjaxHandler extends Shield\Modules\BaseShield\AjaxHandler {
|
9 |
|
10 |
protected function processAjaxAction( string $action ) :array {
|
11 |
|
src/lib/src/Modules/CommentsFilter/Forms/Gasp.php
ADDED
@@ -0,0 +1,123 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\CommentsFilter\Forms;
|
4 |
+
|
5 |
+
use FernleafSystems\Utilities\Logic\OneTimeExecute;
|
6 |
+
use FernleafSystems\Wordpress\Plugin\Shield\Modules\CommentsFilter;
|
7 |
+
use FernleafSystems\Wordpress\Plugin\Shield\Modules\ModConsumer;
|
8 |
+
use FernleafSystems\Wordpress\Services\Services;
|
9 |
+
|
10 |
+
class Gasp {
|
11 |
+
|
12 |
+
use ModConsumer;
|
13 |
+
use OneTimeExecute;
|
14 |
+
|
15 |
+
/**
|
16 |
+
* The unique comment token assigned to this page
|
17 |
+
* @var string
|
18 |
+
*/
|
19 |
+
private $formID;
|
20 |
+
|
21 |
+
/**
|
22 |
+
* @var bool
|
23 |
+
*/
|
24 |
+
private $bFormItemPrinted = false;
|
25 |
+
|
26 |
+
protected function canRun() {
|
27 |
+
/** @var CommentsFilter\Options $opts */
|
28 |
+
$opts = $this->getOptions();
|
29 |
+
return !Services::Request()->isPost() && $opts->isEnabledGaspCheck() && !Services::WpUsers()->isUserLoggedIn();
|
30 |
+
}
|
31 |
+
|
32 |
+
protected function run() {
|
33 |
+
add_action( 'wp', [ $this, 'onWP' ] );
|
34 |
+
add_action( 'wp_footer', [ $this, 'maybeDequeueScript' ] );
|
35 |
+
add_action( 'wp_enqueue_scripts', [ $this, 'onWpEnqueueJs' ] );
|
36 |
+
}
|
37 |
+
|
38 |
+
public function onWP() {
|
39 |
+
add_action( 'comment_form', [ $this, 'printGaspFormItems' ], 1 );
|
40 |
+
}
|
41 |
+
|
42 |
+
public function onWpEnqueueJs() {
|
43 |
+
/** @var CommentsFilter\ModCon $mod */
|
44 |
+
$mod = $this->getMod();
|
45 |
+
/** @var CommentsFilter\Options $opts */
|
46 |
+
$opts = $this->getOptions();
|
47 |
+
$con = $this->getCon();
|
48 |
+
|
49 |
+
$sAsset = 'shield-comments';
|
50 |
+
$handle = $con->prefix( 'shield-comments' );
|
51 |
+
wp_register_script(
|
52 |
+
$handle,
|
53 |
+
$con->getPluginUrl_Js( $sAsset ),
|
54 |
+
[ 'jquery' ],
|
55 |
+
$con->getVersion(),
|
56 |
+
true
|
57 |
+
);
|
58 |
+
wp_enqueue_script( $handle );
|
59 |
+
|
60 |
+
$ts = Services::Request()->ts();
|
61 |
+
$aNonce = $mod->getAjaxActionData( 'comment_token'.Services::IP()->getRequestIp() );
|
62 |
+
$aNonce[ 'ts' ] = $ts;
|
63 |
+
$aNonce[ 'post_id' ] = Services::WpPost()->getCurrentPostId();
|
64 |
+
|
65 |
+
wp_localize_script(
|
66 |
+
$handle,
|
67 |
+
'shield_comments',
|
68 |
+
[
|
69 |
+
'ajax' => [
|
70 |
+
'comment_token' => $aNonce,
|
71 |
+
],
|
72 |
+
'vars' => [
|
73 |
+
'cbname' => 'cb_nombre'.rand(),
|
74 |
+
'botts' => $ts,
|
75 |
+
'token' => 'not created',
|
76 |
+
'uniq' => $this->getUniqueFormId(),
|
77 |
+
'cooldown' => $opts->getTokenCooldown(),
|
78 |
+
'expires' => $opts->getTokenExpireInterval(),
|
79 |
+
],
|
80 |
+
'strings' => [
|
81 |
+
'label' => $mod->getTextOpt( 'custom_message_checkbox' ),
|
82 |
+
'alert' => $mod->getTextOpt( 'custom_message_alert' ),
|
83 |
+
'comment_reload' => $mod->getTextOpt( 'custom_message_comment_reload' ),
|
84 |
+
'js_comment_wait' => $mod->getTextOpt( 'custom_message_comment_wait' ),
|
85 |
+
],
|
86 |
+
'flags' => [
|
87 |
+
'gasp' => true,
|
88 |
+
'recap' => $opts->isEnabledCaptcha() && $mod->getCaptchaCfg()->ready,
|
89 |
+
]
|
90 |
+
]
|
91 |
+
);
|
92 |
+
}
|
93 |
+
|
94 |
+
/**
|
95 |
+
* If the comment form component hasn't been printed, there's no comment form to protect.
|
96 |
+
*/
|
97 |
+
public function maybeDequeueScript() {
|
98 |
+
if ( empty( $this->bFormItemPrinted ) ) {
|
99 |
+
wp_dequeue_script( $this->getCon()->prefix( 'shield-comments' ) );
|
100 |
+
}
|
101 |
+
}
|
102 |
+
|
103 |
+
public function printGaspFormItems() {
|
104 |
+
$this->bFormItemPrinted = true;
|
105 |
+
echo $this->getMod()
|
106 |
+
->renderTemplate(
|
107 |
+
'snippets/comment_form_botbox.twig',
|
108 |
+
[ 'uniq' => $this->getUniqueFormId() ],
|
109 |
+
true
|
110 |
+
);
|
111 |
+
}
|
112 |
+
|
113 |
+
private function getUniqueFormId() :string {
|
114 |
+
if ( !isset( $this->formID ) ) {
|
115 |
+
$DP = Services::Data();
|
116 |
+
$id = $DP->generateRandomLetter().$DP->generateRandomString( rand( 7, 23 ), 7 );
|
117 |
+
$this->formID = preg_replace(
|
118 |
+
'#[^a-zA-Z0-9]#', '',
|
119 |
+
apply_filters( 'icwp_shield_cf_gasp_uniqid', $id ) );
|
120 |
+
}
|
121 |
+
return $this->formID;
|
122 |
+
}
|
123 |
+
}
|
src/lib/src/Modules/CommentsFilter/Forms/GoogleRecaptcha.php
ADDED
@@ -0,0 +1,44 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php declare( strict_types=1 );
|
2 |
+
|
3 |
+
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\CommentsFilter\Forms;
|
4 |
+
|
5 |
+
use FernleafSystems\Utilities\Logic\OneTimeExecute;
|
6 |
+
use FernleafSystems\Wordpress\Plugin\Shield\Modules\CommentsFilter;
|
7 |
+
use FernleafSystems\Wordpress\Plugin\Shield\Modules\ModConsumer;
|
8 |
+
use FernleafSystems\Wordpress\Services\Services;
|
9 |
+
|
10 |
+
class GoogleRecaptcha {
|
11 |
+
|
12 |
+
use ModConsumer;
|
13 |
+
use OneTimeExecute;
|
14 |
+
|
15 |
+
protected function canRun() {
|
16 |
+
/** @var CommentsFilter\ModCon $mod */
|
17 |
+
$mod = $this->getMod();
|
18 |
+
/** @var CommentsFilter\Options $opts */
|
19 |
+
$opts = $this->getOptions();
|
20 |
+
return $opts->isEnabledCaptcha() && $mod->getCaptchaCfg()->ready;
|
21 |
+
}
|
22 |
+
|
23 |
+
protected function run() {
|
24 |
+
add_action( 'wp', [ $this, 'setup' ] );
|
25 |
+
}
|
26 |
+
|
27 |
+
public function setup() {
|
28 |
+
if ( Services::WpComments()->isCommentsOpen() ) {
|
29 |
+
$this->getCon()
|
30 |
+
->getModule_Plugin()
|
31 |
+
->getCaptchaEnqueue()
|
32 |
+
->setMod( $this->getMod() )
|
33 |
+
->setToEnqueue();
|
34 |
+
add_action( 'comment_form_after_fields', [ $this, 'printGoogleRecaptchaCheck' ] );
|
35 |
+
}
|
36 |
+
}
|
37 |
+
|
38 |
+
public function printGoogleRecaptchaCheck() {
|
39 |
+
echo $this->getCon()
|
40 |
+
->getModule_Plugin()
|
41 |
+
->getCaptchaEnqueue()
|
42 |
+
->getCaptchaHtml();
|
43 |
+
}
|
44 |
+
}
|
src/lib/src/Modules/CommentsFilter/Insights/OverviewCards.php
CHANGED
@@ -3,14 +3,14 @@
|
|
3 |
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\CommentsFilter\Insights;
|
4 |
|
5 |
use FernleafSystems\Wordpress\Plugin\Shield;
|
6 |
-
use FernleafSystems\Wordpress\Plugin\Shield\Modules\CommentsFilter
|
7 |
|
8 |
class OverviewCards extends Shield\Modules\Base\Insights\OverviewCards {
|
9 |
|
10 |
public function build() :array {
|
11 |
-
/** @var \
|
12 |
$mod = $this->getMod();
|
13 |
-
/** @var Options $opts */
|
14 |
$opts = $this->getOptions();
|
15 |
|
16 |
$cardSection = [
|
3 |
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\CommentsFilter\Insights;
|
4 |
|
5 |
use FernleafSystems\Wordpress\Plugin\Shield;
|
6 |
+
use FernleafSystems\Wordpress\Plugin\Shield\Modules\CommentsFilter;
|
7 |
|
8 |
class OverviewCards extends Shield\Modules\Base\Insights\OverviewCards {
|
9 |
|
10 |
public function build() :array {
|
11 |
+
/** @var CommentsFilter\ModCon $mod */
|
12 |
$mod = $this->getMod();
|
13 |
+
/** @var CommentsFilter\Options $opts */
|
14 |
$opts = $this->getOptions();
|
15 |
|
16 |
$cardSection = [
|
src/lib/src/Modules/CommentsFilter/ModCon.php
ADDED
@@ -0,0 +1,95 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php declare( strict_types=1 );
|
2 |
+
|
3 |
+
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\CommentsFilter;
|
4 |
+
|
5 |
+
use FernleafSystems\Wordpress\Plugin\Shield\Databases;
|
6 |
+
use FernleafSystems\Wordpress\Plugin\Shield\Modules\BaseShield;
|
7 |
+
use FernleafSystems\Wordpress\Plugin\Shield\Modules\Plugin\Lib\Captcha\CaptchaConfigVO;
|
8 |
+
|
9 |
+
class ModCon extends BaseShield\ModCon {
|
10 |
+
|
11 |
+
public function getCaptchaCfg() :CaptchaConfigVO {
|
12 |
+
$cfg = parent::getCaptchaCfg();
|
13 |
+
$sStyle = $this->getOptions()->getOpt( 'google_recaptcha_style_comments' );
|
14 |
+
if ( $sStyle !== 'default' && $this->isPremium() ) {
|
15 |
+
$cfg->theme = $sStyle;
|
16 |
+
$cfg->invisible = $cfg->theme == 'invisible';
|
17 |
+
}
|
18 |
+
return $cfg;
|
19 |
+
}
|
20 |
+
|
21 |
+
public function ensureCorrectCaptchaConfig() {
|
22 |
+
/** @var Options $opts */
|
23 |
+
$opts = $this->getOptions();
|
24 |
+
|
25 |
+
$sStyle = $opts->getOpt( 'google_recaptcha_style_comments' );
|
26 |
+
if ( $this->isPremium() ) {
|
27 |
+
$oCfg = $this->getCaptchaCfg();
|
28 |
+
if ( $oCfg->provider == $oCfg::PROV_GOOGLE_RECAP2 ) {
|
29 |
+
if ( !$oCfg->invisible && $sStyle == 'invisible' ) {
|
30 |
+
$opts->setOpt( 'google_recaptcha_style_comments', 'default' );
|
31 |
+
}
|
32 |
+
}
|
33 |
+
}
|
34 |
+
elseif ( !in_array( $sStyle, [ 'disabled', 'default' ] ) ) {
|
35 |
+
$opts->setOpt( 'google_recaptcha_style_comments', 'default' );
|
36 |
+
}
|
37 |
+
}
|
38 |
+
|
39 |
+
public function getTextOptDefault( string $key ) :string {
|
40 |
+
|
41 |
+
switch ( $key ) {
|
42 |
+
case 'custom_message_checkbox':
|
43 |
+
$text = __( "I'm not a spammer.", 'wp-simple-firewall' );
|
44 |
+
break;
|
45 |
+
case 'custom_message_alert':
|
46 |
+
$text = __( "Please check the box to confirm you're not a spammer.", 'wp-simple-firewall' );
|
47 |
+
break;
|
48 |
+
case 'custom_message_comment_wait':
|
49 |
+
$text = __( "Please wait %s seconds before posting your comment.", 'wp-simple-firewall' );
|
50 |
+
break;
|
51 |
+
case 'custom_message_comment_reload':
|
52 |
+
$text = __( "Please reload this page to post a comment.", 'wp-simple-firewall' );
|
53 |
+
break;
|
54 |
+
default:
|
55 |
+
$text = parent::getTextOptDefault( $key );
|
56 |
+
break;
|
57 |
+
}
|
58 |
+
return $text;
|
59 |
+
}
|
60 |
+
|
61 |
+
protected function preProcessOptions() {
|
62 |
+
/** @var Options $opts */
|
63 |
+
$opts = $this->getOptions();
|
64 |
+
|
65 |
+
// clean roles
|
66 |
+
$opts->setOpt( 'trusted_user_roles',
|
67 |
+
array_unique( array_filter( array_map(
|
68 |
+
function ( $sRole ) {
|
69 |
+
return sanitize_key( strtolower( $sRole ) );
|
70 |
+
},
|
71 |
+
$opts->getTrustedRoles()
|
72 |
+
) ) )
|
73 |
+
);
|
74 |
+
|
75 |
+
$this->ensureCorrectCaptchaConfig();
|
76 |
+
}
|
77 |
+
|
78 |
+
public function isEnabledCaptcha() :bool {
|
79 |
+
/** @var Options $opts */
|
80 |
+
$opts = $this->getOptions();
|
81 |
+
return $this->isModOptEnabled() && !$opts->isEnabledCaptcha()
|
82 |
+
&& $this->getCaptchaCfg()->ready;
|
83 |
+
}
|
84 |
+
|
85 |
+
public function setEnabledGasp( bool $enabled = true ) {
|
86 |
+
$this->getOptions()->setOpt( 'enable_comments_gasp_protection', $enabled ? 'Y' : 'N' );
|
87 |
+
}
|
88 |
+
|
89 |
+
/**
|
90 |
+
* @return string
|
91 |
+
*/
|
92 |
+
public function getSpamBlacklistFile() {
|
93 |
+
return $this->getCon()->getPluginCachePath( 'spamblacklist.txt' );
|
94 |
+
}
|
95 |
+
}
|
src/lib/src/Modules/CommentsFilter/Options.php
CHANGED
@@ -2,21 +2,15 @@
|
|
2 |
|
3 |
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\CommentsFilter;
|
4 |
|
5 |
-
use FernleafSystems\Wordpress\Plugin\Shield\Modules\
|
6 |
|
7 |
-
class Options extends
|
8 |
|
9 |
-
|
10 |
-
* @return int
|
11 |
-
*/
|
12 |
-
public function getApprovedMinimum() {
|
13 |
return (int)$this->getOpt( 'trusted_commenter_minimum', 1 );
|
14 |
}
|
15 |
|
16 |
-
|
17 |
-
* @return string[]
|
18 |
-
*/
|
19 |
-
public function getHumanSpamFilterItems() {
|
20 |
$aDefault = $this->getOptDefault( 'human_spam_items' );
|
21 |
$aItems = apply_filters(
|
22 |
$this->getCon()->prefix( 'human_spam_items' ),
|
@@ -25,10 +19,7 @@ class Options extends Base\ShieldOptions {
|
|
25 |
return is_array( $aItems ) ? array_intersect( $aDefault, $aItems ) : $aDefault;
|
26 |
}
|
27 |
|
28 |
-
|
29 |
-
* @return int
|
30 |
-
*/
|
31 |
-
public function getTokenCooldown() {
|
32 |
if ( (int)$this->getOpt( 'comments_cooldown', 10 ) < 1 ) {
|
33 |
$this->resetOptToDefault( 'comments_cooldown' );
|
34 |
}
|
@@ -40,10 +31,7 @@ class Options extends Base\ShieldOptions {
|
|
40 |
);
|
41 |
}
|
42 |
|
43 |
-
|
44 |
-
* @return int
|
45 |
-
*/
|
46 |
-
public function getTokenExpireInterval() {
|
47 |
return (int)max( 0,
|
48 |
apply_filters(
|
49 |
$this->getCon()->prefix( 'comments_expire' ),
|
@@ -63,25 +51,16 @@ class Options extends Base\ShieldOptions {
|
|
63 |
return is_array( $aRoles ) ? $aRoles : [];
|
64 |
}
|
65 |
|
66 |
-
|
67 |
-
* @return bool
|
68 |
-
*/
|
69 |
-
public function isEnabledGaspCheck() {
|
70 |
return $this->isOpt( 'enable_comments_gasp_protection', 'Y' )
|
71 |
&& ( $this->getTokenExpireInterval() > $this->getTokenCooldown() );
|
72 |
}
|
73 |
|
74 |
-
|
75 |
-
* @return bool
|
76 |
-
*/
|
77 |
-
public function isEnabledCaptcha() {
|
78 |
return !$this->isOpt( 'google_recaptcha_style_comments', 'disabled' );
|
79 |
}
|
80 |
|
81 |
-
|
82 |
-
* @return bool
|
83 |
-
*/
|
84 |
-
public function isEnabledHumanCheck() {
|
85 |
return $this->isOpt( 'enable_comments_human_spam_filter', 'Y' )
|
86 |
&& count( $this->getHumanSpamFilterItems() ) > 0;
|
87 |
}
|
2 |
|
3 |
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\CommentsFilter;
|
4 |
|
5 |
+
use FernleafSystems\Wordpress\Plugin\Shield\Modules\BaseShield;
|
6 |
|
7 |
+
class Options extends BaseShield\Options {
|
8 |
|
9 |
+
public function getApprovedMinimum() :int {
|
|
|
|
|
|
|
10 |
return (int)$this->getOpt( 'trusted_commenter_minimum', 1 );
|
11 |
}
|
12 |
|
13 |
+
public function getHumanSpamFilterItems() :array {
|
|
|
|
|
|
|
14 |
$aDefault = $this->getOptDefault( 'human_spam_items' );
|
15 |
$aItems = apply_filters(
|
16 |
$this->getCon()->prefix( 'human_spam_items' ),
|
19 |
return is_array( $aItems ) ? array_intersect( $aDefault, $aItems ) : $aDefault;
|
20 |
}
|
21 |
|
22 |
+
public function getTokenCooldown() :int {
|
|
|
|
|
|
|
23 |
if ( (int)$this->getOpt( 'comments_cooldown', 10 ) < 1 ) {
|
24 |
$this->resetOptToDefault( 'comments_cooldown' );
|
25 |
}
|
31 |
);
|
32 |
}
|
33 |
|
34 |
+
public function getTokenExpireInterval() :int {
|
|
|
|
|
|
|
35 |
return (int)max( 0,
|
36 |
apply_filters(
|
37 |
$this->getCon()->prefix( 'comments_expire' ),
|
51 |
return is_array( $aRoles ) ? $aRoles : [];
|
52 |
}
|
53 |
|
54 |
+
public function isEnabledGaspCheck() :bool {
|
|
|
|
|
|
|
55 |
return $this->isOpt( 'enable_comments_gasp_protection', 'Y' )
|
56 |
&& ( $this->getTokenExpireInterval() > $this->getTokenCooldown() );
|
57 |
}
|
58 |
|
59 |
+
public function isEnabledCaptcha() :bool {
|
|
|
|
|
|
|
60 |
return !$this->isOpt( 'google_recaptcha_style_comments', 'disabled' );
|
61 |
}
|
62 |
|
63 |
+
public function isEnabledHumanCheck() :bool {
|
|
|
|
|
|
|
64 |
return $this->isOpt( 'enable_comments_human_spam_filter', 'Y' )
|
65 |
&& count( $this->getHumanSpamFilterItems() ) > 0;
|
66 |
}
|
src/lib/src/Modules/CommentsFilter/Processor.php
ADDED
@@ -0,0 +1,63 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php declare( strict_types=1 );
|
2 |
+
|
3 |
+
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\CommentsFilter;
|
4 |
+
|
5 |
+
use FernleafSystems\Wordpress\Plugin\Shield\Modules\BaseShield;
|
6 |
+
use FernleafSystems\Wordpress\Services\Services;
|
7 |
+
|
8 |
+
class Processor extends BaseShield\Processor {
|
9 |
+
|
10 |
+
public function onWpInit() {
|
11 |
+
/** @var Options $opts */
|
12 |
+
$opts = $this->getOptions();
|
13 |
+
$oWpUsers = Services::WpUsers();
|
14 |
+
|
15 |
+
$bLoadComProc = !$oWpUsers->isUserLoggedIn() ||
|
16 |
+
!( new Scan\IsEmailTrusted() )->trusted(
|
17 |
+
$oWpUsers->getCurrentWpUser()->user_email,
|
18 |
+
$opts->getApprovedMinimum(),
|
19 |
+
$opts->getTrustedRoles()
|
20 |
+
);
|
21 |
+
|
22 |
+
if ( $bLoadComProc ) {
|
23 |
+
|
24 |
+
( new Forms\GoogleRecaptcha() )
|
25 |
+
->setMod( $this->getMod() )
|
26 |
+
->execute();
|
27 |
+
|
28 |
+
if ( Services::Request()->isPost() ) {
|
29 |
+
( new Scan\Scanner() )
|
30 |
+
->setMod( $this->getMod() )
|
31 |
+
->execute();
|
32 |
+
add_filter( 'comment_notification_recipients', [ $this, 'clearCommentNotificationEmail' ], 100, 1 );
|
33 |
+
}
|
34 |
+
else {
|
35 |
+
( new Forms\Gasp() )
|
36 |
+
->setMod( $this->getMod() )
|
37 |
+
->execute();
|
38 |
+
}
|
39 |
+
}
|
40 |
+
}
|
41 |
+
|
42 |
+
public function runHourlyCron() {
|
43 |
+
/** @var Options $opts */
|
44 |
+
$opts = $this->getOptions();
|
45 |
+
if ( $opts->isEnabledGaspCheck() && function_exists( 'delete_expired_transients' ) ) {
|
46 |
+
delete_expired_transients(); // cleanup unused comment tokens
|
47 |
+
}
|
48 |
+
}
|
49 |
+
|
50 |
+
/**
|
51 |
+
* When you set a new comment as anything but 'spam' a notification email is sent to the post author.
|
52 |
+
* We suppress this for when we mark as trash by emptying the email notifications list.
|
53 |
+
* @param array $aEmails
|
54 |
+
* @return array
|
55 |
+
*/
|
56 |
+
public function clearCommentNotificationEmail( $aEmails ) {
|
57 |
+
$sStatus = apply_filters( $this->getCon()->prefix( 'cf_status' ), '' );
|
58 |
+
if ( in_array( $sStatus, [ 'reject', 'trash' ] ) ) {
|
59 |
+
$aEmails = [];
|
60 |
+
}
|
61 |
+
return $aEmails;
|
62 |
+
}
|
63 |
+
}
|
src/lib/src/Modules/CommentsFilter/Scan/Human.php
CHANGED
@@ -18,8 +18,8 @@ class Human {
|
|
18 |
* @return \WP_Error|true
|
19 |
*/
|
20 |
public function scan( $aCommData ) {
|
21 |
-
/** @var CommentsFilter\Options $
|
22 |
-
$
|
23 |
|
24 |
$aItemsToCheck = array_intersect_key(
|
25 |
[
|
@@ -30,7 +30,7 @@ class Human {
|
|
30 |
'ip_address' => Services::IP()->getRequestIp(),
|
31 |
'user_agent' => substr( Services::Request()->getUserAgent(), 0, 254 )
|
32 |
],
|
33 |
-
array_flip( $
|
34 |
);
|
35 |
|
36 |
$mResult = true;
|
@@ -58,11 +58,11 @@ class Human {
|
|
58 |
* @return string[]
|
59 |
*/
|
60 |
private function getSpamBlacklist() {
|
61 |
-
/** @var \
|
62 |
-
$
|
63 |
$aList = [];
|
64 |
$oFs = Services::WpFs();
|
65 |
-
$sBLFile = $
|
66 |
|
67 |
// Download if doesn't exist or expired.
|
68 |
if ( !$oFs->exists( $sBLFile )
|
@@ -81,11 +81,11 @@ class Human {
|
|
81 |
}
|
82 |
|
83 |
private function importBlacklist() {
|
84 |
-
/** @var \
|
85 |
-
$
|
86 |
-
$
|
87 |
-
$sBLFile = $
|
88 |
-
if ( !$
|
89 |
$sRawList = Services::HttpRequest()->getContent( $this->getOptions()
|
90 |
->getDef( 'url_spam_blacklist_terms' ) );
|
91 |
$sList = '';
|
@@ -93,7 +93,7 @@ class Human {
|
|
93 |
$sList = implode( "\n", array_map( 'base64_encode', array_filter( array_map( 'trim', explode( "\n", $sRawList ) ) ) ) );
|
94 |
}
|
95 |
// save the list to disk for the future.
|
96 |
-
$
|
97 |
}
|
98 |
}
|
99 |
}
|
18 |
* @return \WP_Error|true
|
19 |
*/
|
20 |
public function scan( $aCommData ) {
|
21 |
+
/** @var CommentsFilter\Options $opts */
|
22 |
+
$opts = $this->getOptions();
|
23 |
|
24 |
$aItemsToCheck = array_intersect_key(
|
25 |
[
|
30 |
'ip_address' => Services::IP()->getRequestIp(),
|
31 |
'user_agent' => substr( Services::Request()->getUserAgent(), 0, 254 )
|
32 |
],
|
33 |
+
array_flip( $opts->getHumanSpamFilterItems() )
|
34 |
);
|
35 |
|
36 |
$mResult = true;
|
58 |
* @return string[]
|
59 |
*/
|
60 |
private function getSpamBlacklist() {
|
61 |
+
/** @var CommentsFilter\ModCon $mod */
|
62 |
+
$mod = $this->getMod();
|
63 |
$aList = [];
|
64 |
$oFs = Services::WpFs();
|
65 |
+
$sBLFile = $mod->getSpamBlacklistFile();
|
66 |
|
67 |
// Download if doesn't exist or expired.
|
68 |
if ( !$oFs->exists( $sBLFile )
|
81 |
}
|
82 |
|
83 |
private function importBlacklist() {
|
84 |
+
/** @var CommentsFilter\ModCon $mod */
|
85 |
+
$mod = $this->getMod();
|
86 |
+
$FS = Services::WpFs();
|
87 |
+
$sBLFile = $mod->getSpamBlacklistFile();
|
88 |
+
if ( !$FS->exists( $sBLFile ) ) {
|
89 |
$sRawList = Services::HttpRequest()->getContent( $this->getOptions()
|
90 |
->getDef( 'url_spam_blacklist_terms' ) );
|
91 |
$sList = '';
|
93 |
$sList = implode( "\n", array_map( 'base64_encode', array_filter( array_map( 'trim', explode( "\n", $sRawList ) ) ) ) );
|
94 |
}
|
95 |
// save the list to disk for the future.
|
96 |
+
$FS->putFileContent( $sBLFile, $sList, true );
|
97 |
}
|
98 |
}
|
99 |
}
|
src/lib/src/Modules/CommentsFilter/Scan/Scanner.php
CHANGED
@@ -2,7 +2,8 @@
|
|
2 |
|
3 |
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\CommentsFilter\Scan;
|
4 |
|
5 |
-
use FernleafSystems\
|
|
|
6 |
use FernleafSystems\Wordpress\Plugin\Shield\Modules\ModConsumer;
|
7 |
use FernleafSystems\Wordpress\Plugin\Shield\Utilities;
|
8 |
use FernleafSystems\Wordpress\Services\Services;
|
@@ -10,6 +11,7 @@ use FernleafSystems\Wordpress\Services\Services;
|
|
10 |
class Scanner {
|
11 |
|
12 |
use ModConsumer;
|
|
|
13 |
|
14 |
/**
|
15 |
* @var string|int|null
|
@@ -21,7 +23,11 @@ class Scanner {
|
|
21 |
*/
|
22 |
private $sCommentExplanation;
|
23 |
|
24 |
-
|
|
|
|
|
|
|
|
|
25 |
if ( Services::WpComments()->isCommentSubmission() ) {
|
26 |
add_filter( 'preprocess_comment', [ $this, 'checkComment' ], 5 );
|
27 |
}
|
@@ -117,9 +123,9 @@ class Scanner {
|
|
117 |
* @return true|\WP_Error|null
|
118 |
*/
|
119 |
private function runScans( $aCommData ) {
|
120 |
-
/** @var \
|
121 |
$mod = $this->getMod();
|
122 |
-
/** @var Options $opts */
|
123 |
$opts = $this->getOptions();
|
124 |
|
125 |
$mResult = true;
|
@@ -163,7 +169,7 @@ class Scanner {
|
|
163 |
* @return bool
|
164 |
*/
|
165 |
public function getIfDoCommentsCheck( $nPostId, $sCommentEmail ) {
|
166 |
-
/** @var Options $opts */
|
167 |
$opts = $this->getOptions();
|
168 |
$post = Services::WpPost()->getById( $nPostId );
|
169 |
return $post instanceof \WP_Post
|
2 |
|
3 |
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\CommentsFilter\Scan;
|
4 |
|
5 |
+
use FernleafSystems\Utilities\Logic\OneTimeExecute;
|
6 |
+
use FernleafSystems\Wordpress\Plugin\Shield\Modules\CommentsFilter;
|
7 |
use FernleafSystems\Wordpress\Plugin\Shield\Modules\ModConsumer;
|
8 |
use FernleafSystems\Wordpress\Plugin\Shield\Utilities;
|
9 |
use FernleafSystems\Wordpress\Services\Services;
|
11 |
class Scanner {
|
12 |
|
13 |
use ModConsumer;
|
14 |
+
use OneTimeExecute;
|
15 |
|
16 |
/**
|
17 |
* @var string|int|null
|
23 |
*/
|
24 |
private $sCommentExplanation;
|
25 |
|
26 |
+
protected function canRun() {
|
27 |
+
return Services::Request()->isPost();
|
28 |
+
}
|
29 |
+
|
30 |
+
protected function run() {
|
31 |
if ( Services::WpComments()->isCommentSubmission() ) {
|
32 |
add_filter( 'preprocess_comment', [ $this, 'checkComment' ], 5 );
|
33 |
}
|
123 |
* @return true|\WP_Error|null
|
124 |
*/
|
125 |
private function runScans( $aCommData ) {
|
126 |
+
/** @var CommentsFilter\ModCon $mod */
|
127 |
$mod = $this->getMod();
|
128 |
+
/** @var CommentsFilter\Options $opts */
|
129 |
$opts = $this->getOptions();
|
130 |
|
131 |
$mResult = true;
|
169 |
* @return bool
|
170 |
*/
|
171 |
public function getIfDoCommentsCheck( $nPostId, $sCommentEmail ) {
|
172 |
+
/** @var CommentsFilter\Options $opts */
|
173 |
$opts = $this->getOptions();
|
174 |
$post = Services::WpPost()->getById( $nPostId );
|
175 |
return $post instanceof \WP_Post
|
src/lib/src/Modules/CommentsFilter/Strings.php
CHANGED
@@ -87,14 +87,14 @@ class Strings extends Base\Strings {
|
|
87 |
* @throws \Exception
|
88 |
*/
|
89 |
public function getOptionStrings( string $key ) :array {
|
90 |
-
/** @var
|
91 |
-
$
|
92 |
-
$
|
93 |
|
94 |
switch ( $key ) {
|
95 |
|
96 |
case 'enable_comments_filter' :
|
97 |
-
$sName = sprintf( __( 'Enable %s Module', 'wp-simple-firewall' ), $
|
98 |
$sSummary = __( 'Enable (or Disable) The Comment SPAM Protection Feature', 'wp-simple-firewall' );
|
99 |
$sDescription = sprintf( __( 'Un-Checking this option will completely disable the %s module.', 'wp-simple-firewall' ), __( 'Comment SPAM Protection', 'wp-simple-firewall' ) );
|
100 |
break;
|
@@ -154,7 +154,7 @@ class Strings extends Base\Strings {
|
|
154 |
$sDescription = [
|
155 |
__( 'You can choose the CAPTCHA display format that best suits your site, including the newer Invisible CAPTCHA, when you upgrade to PRO.', 'wp-simple-firewall' )
|
156 |
];
|
157 |
-
if ( !$
|
158 |
$sDescription[] = sprintf( '<a href="%s">%s</a>',
|
159 |
$this->getCon()
|
160 |
->getModule_Plugin()
|
87 |
* @throws \Exception
|
88 |
*/
|
89 |
public function getOptionStrings( string $key ) :array {
|
90 |
+
/** @var ModCon $mod */
|
91 |
+
$mod = $this->getMod();
|
92 |
+
$modName = $this->getMod()->getMainFeatureName();
|
93 |
|
94 |
switch ( $key ) {
|
95 |
|
96 |
case 'enable_comments_filter' :
|
97 |
+
$sName = sprintf( __( 'Enable %s Module', 'wp-simple-firewall' ), $modName );
|
98 |
$sSummary = __( 'Enable (or Disable) The Comment SPAM Protection Feature', 'wp-simple-firewall' );
|
99 |
$sDescription = sprintf( __( 'Un-Checking this option will completely disable the %s module.', 'wp-simple-firewall' ), __( 'Comment SPAM Protection', 'wp-simple-firewall' ) );
|
100 |
break;
|
154 |
$sDescription = [
|
155 |
__( 'You can choose the CAPTCHA display format that best suits your site, including the newer Invisible CAPTCHA, when you upgrade to PRO.', 'wp-simple-firewall' )
|
156 |
];
|
157 |
+
if ( !$mod->getCaptchaCfg()->ready ) {
|
158 |
$sDescription[] = sprintf( '<a href="%s">%s</a>',
|
159 |
$this->getCon()
|
160 |
->getModule_Plugin()
|
src/lib/src/Modules/CommentsFilter/UI.php
CHANGED
@@ -2,7 +2,8 @@
|
|
2 |
|
3 |
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\CommentsFilter;
|
4 |
|
5 |
-
use FernleafSystems\Wordpress\Plugin\Shield\Modules\
|
|
|
|
|
6 |
|
7 |
-
class UI extends Base\ShieldUI {
|
8 |
}
|
2 |
|
3 |
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\CommentsFilter;
|
4 |
|
5 |
+
use FernleafSystems\Wordpress\Plugin\Shield\Modules\BaseShield;
|
6 |
+
|
7 |
+
class UI extends BaseShield\UI {
|
8 |
|
|
|
9 |
}
|
src/lib/src/Modules/Comms/ModCon.php
ADDED
@@ -0,0 +1,13 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php declare( strict_types=1 );
|
2 |
+
|
3 |
+
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Comms;
|
4 |
+
|
5 |
+
use FernleafSystems\Wordpress\Plugin\Shield\Modules\BaseShield;
|
6 |
+
|
7 |
+
class ModCon extends BaseShield\ModCon {
|
8 |
+
|
9 |
+
public function getSureSendController() :Lib\SureSend\SureSendController {
|
10 |
+
return ( new Lib\SureSend\SureSendController() )
|
11 |
+
->setMod( $this );
|
12 |
+
}
|
13 |
+
}
|
src/lib/src/Modules/Comms/Options.php
CHANGED
@@ -2,8 +2,8 @@
|
|
2 |
|
3 |
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Comms;
|
4 |
|
5 |
-
use FernleafSystems\Wordpress\Plugin\Shield\Modules\
|
6 |
|
7 |
-
class Options extends
|
8 |
|
9 |
}
|
2 |
|
3 |
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Comms;
|
4 |
|
5 |
+
use FernleafSystems\Wordpress\Plugin\Shield\Modules\BaseShield;
|
6 |
|
7 |
+
class Options extends BaseShield\Options {
|
8 |
|
9 |
}
|
src/lib/src/Modules/Comms/Processor.php
ADDED
@@ -0,0 +1,9 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php declare( strict_types=1 );
|
2 |
+
|
3 |
+
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Comms;
|
4 |
+
|
5 |
+
use FernleafSystems\Wordpress\Plugin\Shield\Modules\BaseShield;
|
6 |
+
|
7 |
+
class Processor extends BaseShield\Processor {
|
8 |
+
|
9 |
+
}
|
src/lib/src/Modules/Email/ModCon.php
ADDED
@@ -0,0 +1,9 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php declare( strict_types=1 );
|
2 |
+
|
3 |
+
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Email;
|
4 |
+
|
5 |
+
use FernleafSystems\Wordpress\Plugin\Shield\Modules\BaseShield;
|
6 |
+
|
7 |
+
class ModCon extends BaseShield\ModCon {
|
8 |
+
|
9 |
+
}
|
src/lib/src/Modules/Email/Options.php
CHANGED
@@ -1,9 +1,9 @@
|
|
1 |
-
<?php
|
2 |
|
3 |
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Email;
|
4 |
|
5 |
-
use FernleafSystems\Wordpress\Plugin\Shield\Modules\
|
6 |
|
7 |
-
class Options extends
|
8 |
|
9 |
}
|
1 |
+
<?php declare( strict_types=1 );
|
2 |
|
3 |
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Email;
|
4 |
|
5 |
+
use FernleafSystems\Wordpress\Plugin\Shield\Modules\BaseShield;
|
6 |
|
7 |
+
class Options extends BaseShield\Options {
|
8 |
|
9 |
}
|
src/lib/src/Modules/Email/Processor.php
ADDED
@@ -0,0 +1,205 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Email;
|
4 |
+
|
5 |
+
use FernleafSystems\Wordpress\Plugin\Shield\Modules\BaseShield;
|
6 |
+
use FernleafSystems\Wordpress\Services\Services;
|
7 |
+
|
8 |
+
class Processor extends BaseShield\Processor {
|
9 |
+
|
10 |
+
protected function getEmailHeader() :array {
|
11 |
+
return [
|
12 |
+
__( 'Hi !', 'wp-simple-firewall' ),
|
13 |
+
'',
|
14 |
+
];
|
15 |
+
}
|
16 |
+
|
17 |
+
protected function getEmailFooter() :array {
|
18 |
+
$con = $this->getCon();
|
19 |
+
$WP = Services::WpGeneral();
|
20 |
+
|
21 |
+
{
|
22 |
+
$goPro = [
|
23 |
+
'Go PRO For The Equivalent Of 1 Cappuccino Per Month ☕',
|
24 |
+
'Go PRO For The Equivalent Of 1 Beer Per Month 🍺',
|
25 |
+
'Go PRO For The Equivalent Of 1 Glass Of Wine Per Month 🍷',
|
26 |
+
];
|
27 |
+
$benefits = [
|
28 |
+
'The Easiest, Frustration-Free Pro-Upgrade Available Anywhere',
|
29 |
+
'MainWP Integration',
|
30 |
+
'Powerful, Auto-Learning Malware Scanner',
|
31 |
+
'Plugin and Theme File Guard',
|
32 |
+
'Vulnerability Scanner',
|
33 |
+
'Traffic Rate Limiting',
|
34 |
+
'WooCommerce Support',
|
35 |
+
'Automatic Import/Export Sync Of Options Across Your WP Portfolio',
|
36 |
+
'Powerful User Password Policies',
|
37 |
+
'Exclusive Customer Support',
|
38 |
+
'That Warm And Fuzzy Feeling That Comes From Supporting Future Development',
|
39 |
+
];
|
40 |
+
shuffle( $benefits );
|
41 |
+
}
|
42 |
+
|
43 |
+
$footer = [
|
44 |
+
$this->getMod()
|
45 |
+
->renderTemplate( '/email/footer.twig', [
|
46 |
+
'strings' => [
|
47 |
+
'benefits' => $benefits,
|
48 |
+
'much_more' => 'And So Much More',
|
49 |
+
'upgrade' => $goPro[ array_rand( $goPro ) ],
|
50 |
+
'sent_from' => sprintf( __( 'Email sent from the %s Plugin v%s, on %s.', 'wp-simple-firewall' ),
|
51 |
+
$this->getCon()->getHumanName(),
|
52 |
+
$this->getCon()->getVersion(),
|
53 |
+
$WP->getHomeUrl()
|
54 |
+
),
|
55 |
+
'delays' => __( 'Note: Email delays are caused by website hosting and email providers.', 'wp-simple-firewall' ),
|
56 |
+
'time_sent' => sprintf( __( 'Time Sent: %s', 'wp-simple-firewall' ), $WP->getTimeStampForDisplay() ),
|
57 |
+
],
|
58 |
+
'hrefs' => [
|
59 |
+
'upgrade' => 'https://shsec.io/buyshieldproemailfooter',
|
60 |
+
'much_more' => 'https://shsec.io/gp'
|
61 |
+
],
|
62 |
+
'flags' => [
|
63 |
+
'is_pro' => $con->isPremiumActive(),
|
64 |
+
'is_whitelabelled' => $con->getModule_SecAdmin()->isWlEnabled()
|
65 |
+
]
|
66 |
+
] ),
|
67 |
+
];
|
68 |
+
|
69 |
+
return apply_filters( 'icwp_shield_email_footer', $footer );
|
70 |
+
}
|
71 |
+
|
72 |
+
/**
|
73 |
+
* Wraps up a message with header and footer
|
74 |
+
* @param string $to
|
75 |
+
* @param string $subject
|
76 |
+
* @param array $message
|
77 |
+
* @return bool
|
78 |
+
*/
|
79 |
+
public function sendEmailWithWrap( $to = '', $subject = '', $message = [] ) :bool {
|
80 |
+
$WP = Services::WpGeneral();
|
81 |
+
return $this->send(
|
82 |
+
$to,
|
83 |
+
$subject,
|
84 |
+
sprintf( '<html lang="%s">%s</html>',
|
85 |
+
$WP->getLocale( '-' ),
|
86 |
+
implode( "<br />", array_merge( $this->getEmailHeader(), $message, $this->getEmailFooter() ) )
|
87 |
+
)
|
88 |
+
);
|
89 |
+
}
|
90 |
+
|
91 |
+
public function sendEmailWithTemplate( string $templ, string $to, string $subject, array $body ) :bool {
|
92 |
+
return $this->send(
|
93 |
+
$to,
|
94 |
+
$subject,
|
95 |
+
$this->getMod()->renderTemplate(
|
96 |
+
$templ,
|
97 |
+
[
|
98 |
+
'header' => $this->getEmailHeader(),
|
99 |
+
'body' => $body,
|
100 |
+
'footer' => $this->getEmailFooter(),
|
101 |
+
'vars' => [
|
102 |
+
'lang' => Services::WpGeneral()->getLocale( '-' )
|
103 |
+
]
|
104 |
+
],
|
105 |
+
true
|
106 |
+
)
|
107 |
+
);
|
108 |
+
}
|
109 |
+
|
110 |
+
/**
|
111 |
+
* @param string $to
|
112 |
+
* @param string $subject
|
113 |
+
* @param string $body
|
114 |
+
* @return bool
|
115 |
+
* @uses wp_mail
|
116 |
+
*/
|
117 |
+
public function send( $to = '', $subject = '', $body = '' ) :bool {
|
118 |
+
|
119 |
+
$this->emailFilters( true );
|
120 |
+
$success = wp_mail(
|
121 |
+
$this->verifyEmailAddress( $to ),
|
122 |
+
sprintf( '[%s] %s', html_entity_decode( Services::WpGeneral()->getSiteName(), ENT_QUOTES ), $subject ),
|
123 |
+
$body
|
124 |
+
);
|
125 |
+
$this->emailFilters( false );
|
126 |
+
return (bool)$success;
|
127 |
+
}
|
128 |
+
|
129 |
+
/**
|
130 |
+
* @param $add - true to add, false to remove
|
131 |
+
*/
|
132 |
+
private function emailFilters( bool $add ) {
|
133 |
+
if ( $add ) {
|
134 |
+
add_filter( 'wp_mail_from', [ $this, 'setMailFrom' ], 100 );
|
135 |
+
add_filter( 'wp_mail_from_name', [ $this, 'setMailFromName' ], 100 );
|
136 |
+
add_filter( 'wp_mail_content_type', [ $this, 'setMailContentType' ], 100, 0 );
|
137 |
+
}
|
138 |
+
else {
|
139 |
+
remove_filter( 'wp_mail_from', [ $this, 'setMailFrom' ], 100 );
|
140 |
+
remove_filter( 'wp_mail_from_name', [ $this, 'setMailFromName' ], 100 );
|
141 |
+
remove_filter( 'wp_mail_content_type', [ $this, 'setMailContentType' ], 100 );
|
142 |
+
}
|
143 |
+
}
|
144 |
+
|
145 |
+
public function setMailContentType() :string {
|
146 |
+
return 'text/html';
|
147 |
+
}
|
148 |
+
|
149 |
+
/**
|
150 |
+
* @param string $from
|
151 |
+
* @return string
|
152 |
+
*/
|
153 |
+
public function setMailFrom( $from ) {
|
154 |
+
$DP = Services::Data();
|
155 |
+
$proposed = apply_filters( 'icwp_shield_from_email', '' );
|
156 |
+
if ( $DP->validEmail( $proposed ) ) {
|
157 |
+
$from = $proposed;
|
158 |
+
}
|
159 |
+
// We help out by trying to correct any funky "from" addresses
|
160 |
+
// So, at the very least, we don't fail on this for our emails.
|
161 |
+
if ( !$DP->validEmail( $from ) ) {
|
162 |
+
$urlParts = @parse_url( Services::WpGeneral()->getWpUrl() );
|
163 |
+
if ( !empty( $urlParts[ 'host' ] ) ) {
|
164 |
+
$proposed = 'wordpress@'.$urlParts[ 'host' ];
|
165 |
+
if ( $DP->validEmail( $proposed ) ) {
|
166 |
+
$from = $proposed;
|
167 |
+
}
|
168 |
+
}
|
169 |
+
}
|
170 |
+
return $from;
|
171 |
+
}
|
172 |
+
|
173 |
+
/**
|
174 |
+
* @param string $name
|
175 |
+
* @return string
|
176 |
+
*/
|
177 |
+
public function setMailFromName( $name ) :string {
|
178 |
+
$proposed = apply_filters( 'icwp_shield_from_email_name', '' );
|
179 |
+
if ( !empty( $proposed ) ) {
|
180 |
+
$name = $proposed;
|
181 |
+
}
|
182 |
+
else {
|
183 |
+
$name = sprintf( '%s - %s', $name, $this->getCon()->getHumanName() );
|
184 |
+
}
|
185 |
+
return $name;
|
186 |
+
}
|
187 |
+
|
188 |
+
/**
|
189 |
+
* Will send email to the default recipient setup in the object.
|
190 |
+
* @param string $subject
|
191 |
+
* @param array $message
|
192 |
+
* @return bool
|
193 |
+
*/
|
194 |
+
public function sendEmail( $subject, $message ) {
|
195 |
+
return $this->sendEmailWithWrap( null, $subject, $message );
|
196 |
+
}
|
197 |
+
|
198 |
+
/**
|
199 |
+
* @param string $email
|
200 |
+
* @return string
|
201 |
+
*/
|
202 |
+
public function verifyEmailAddress( $email = '' ) {
|
203 |
+
return Services::Data()->validEmail( $email ) ? $email : Services::WpGeneral()->getSiteAdminEmail();
|
204 |
+
}
|
205 |
+
}
|
src/lib/src/Modules/Email/Strings.php
CHANGED
@@ -1,4 +1,4 @@
|
|
1 |
-
<?php
|
2 |
|
3 |
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Email;
|
4 |
|
1 |
+
<?php declare( strict_types=1 );
|
2 |
|
3 |
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Email;
|
4 |
|
src/lib/src/Modules/Events/AjaxHandler.php
CHANGED
@@ -6,7 +6,7 @@ use FernleafSystems\Wordpress\Plugin\Shield;
|
|
6 |
use FernleafSystems\Wordpress\Plugin\Shield\Modules\Events;
|
7 |
use FernleafSystems\Wordpress\Services\Services;
|
8 |
|
9 |
-
class AjaxHandler extends Shield\Modules\
|
10 |
|
11 |
protected function processAjaxAction( string $action ) :array {
|
12 |
|
@@ -27,7 +27,7 @@ class AjaxHandler extends Shield\Modules\Base\AjaxHandlerShield {
|
|
27 |
}
|
28 |
|
29 |
private function ajaxExec_RenderChart() :array {
|
30 |
-
/** @var
|
31 |
$mod = $this->getMod();
|
32 |
|
33 |
$aParams = $this->getAjaxFormParams();
|
@@ -46,15 +46,15 @@ class AjaxHandler extends Shield\Modules\Base\AjaxHandlerShield {
|
|
46 |
}
|
47 |
|
48 |
private function ajaxExec_RenderChartPost() :array {
|
49 |
-
/** @var
|
50 |
-
$
|
51 |
-
$
|
52 |
$oChartReq = new Events\Charts\ChartRequestVO();
|
53 |
-
$oChartReq->render_location = $
|
54 |
-
$oChartReq->chart_params = $
|
55 |
|
56 |
$aChart = ( new Events\Charts\BuildData() )
|
57 |
-
->setMod( $
|
58 |
->build( $oChartReq );
|
59 |
|
60 |
return [
|
6 |
use FernleafSystems\Wordpress\Plugin\Shield\Modules\Events;
|
7 |
use FernleafSystems\Wordpress\Services\Services;
|
8 |
|
9 |
+
class AjaxHandler extends Shield\Modules\BaseShield\AjaxHandler {
|
10 |
|
11 |
protected function processAjaxAction( string $action ) :array {
|
12 |
|
27 |
}
|
28 |
|
29 |
private function ajaxExec_RenderChart() :array {
|
30 |
+
/** @var ModCon $mod */
|
31 |
$mod = $this->getMod();
|
32 |
|
33 |
$aParams = $this->getAjaxFormParams();
|
46 |
}
|
47 |
|
48 |
private function ajaxExec_RenderChartPost() :array {
|
49 |
+
/** @var ModCon $mod */
|
50 |
+
$mod = $this->getMod();
|
51 |
+
$req = Services::Request();
|
52 |
$oChartReq = new Events\Charts\ChartRequestVO();
|
53 |
+
$oChartReq->render_location = $req->post( 'render_location' );
|
54 |
+
$oChartReq->chart_params = $req->post( 'chart_params' );
|
55 |
|
56 |
$aChart = ( new Events\Charts\BuildData() )
|
57 |
+
->setMod( $mod )
|
58 |
->build( $oChartReq );
|
59 |
|
60 |
return [
|
src/lib/src/Modules/Events/Charts/BuildData.php
CHANGED
@@ -3,6 +3,7 @@
|
|
3 |
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Events\Charts;
|
4 |
|
5 |
use FernleafSystems\Wordpress\Plugin\Shield\Databases\Events;
|
|
|
6 |
use FernleafSystems\Wordpress\Plugin\Shield\Modules\ModConsumer;
|
7 |
use FernleafSystems\Wordpress\Services\Services;
|
8 |
|
@@ -11,17 +12,17 @@ class BuildData {
|
|
11 |
use ModConsumer;
|
12 |
|
13 |
/**
|
14 |
-
* @param ChartRequestVO $
|
15 |
* @return array
|
16 |
*/
|
17 |
-
public function build( ChartRequestVO $
|
18 |
-
/** @var
|
19 |
-
$
|
20 |
|
21 |
-
$this->preProcessRequest( $
|
22 |
|
23 |
/** @var Events\Handler $oDbhEvts */
|
24 |
-
$oDbhEvts = $
|
25 |
|
26 |
$nTick = 0;
|
27 |
$oTime = Services::Request()->carbon();
|
@@ -33,7 +34,7 @@ class BuildData {
|
|
33 |
|
34 |
/** @var Events\Select $oSelEvts */
|
35 |
$oSelEvts = $oDbhEvts->getQuerySelector();
|
36 |
-
switch ( $
|
37 |
case 'hourly':
|
38 |
$oSelEvts->filterByBoundary_Hour( $oTime->timestamp );
|
39 |
$oTime->subHour();
|
@@ -56,10 +57,10 @@ class BuildData {
|
|
56 |
break;
|
57 |
}
|
58 |
|
59 |
-
$aSeries[] = $oSelEvts->sumEvents( $
|
60 |
|
61 |
$nTick++;
|
62 |
-
} while ( $nTick < $
|
63 |
|
64 |
return [
|
65 |
'data' => [
|
@@ -88,7 +89,7 @@ class BuildData {
|
|
88 |
}
|
89 |
}
|
90 |
|
91 |
-
$aAll = array_keys( $this->getCon()->
|
92 |
if ( !empty( $oReq->chart_params[ 'stat_id' ] ) ) {
|
93 |
switch ( $oReq->chart_params[ 'stat_id' ] ) {
|
94 |
case 'comment_block':
|
3 |
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Events\Charts;
|
4 |
|
5 |
use FernleafSystems\Wordpress\Plugin\Shield\Databases\Events;
|
6 |
+
use FernleafSystems\Wordpress\Plugin\Shield\Modules\Events\ModCon;
|
7 |
use FernleafSystems\Wordpress\Plugin\Shield\Modules\ModConsumer;
|
8 |
use FernleafSystems\Wordpress\Services\Services;
|
9 |
|
12 |
use ModConsumer;
|
13 |
|
14 |
/**
|
15 |
+
* @param ChartRequestVO $req
|
16 |
* @return array
|
17 |
*/
|
18 |
+
public function build( ChartRequestVO $req ) {
|
19 |
+
/** @var ModCon $mod */
|
20 |
+
$mod = $this->getMod();
|
21 |
|
22 |
+
$this->preProcessRequest( $req );
|
23 |
|
24 |
/** @var Events\Handler $oDbhEvts */
|
25 |
+
$oDbhEvts = $mod->getDbHandler_Events();
|
26 |
|
27 |
$nTick = 0;
|
28 |
$oTime = Services::Request()->carbon();
|
34 |
|
35 |
/** @var Events\Select $oSelEvts */
|
36 |
$oSelEvts = $oDbhEvts->getQuerySelector();
|
37 |
+
switch ( $req->interval ) {
|
38 |
case 'hourly':
|
39 |
$oSelEvts->filterByBoundary_Hour( $oTime->timestamp );
|
40 |
$oTime->subHour();
|
57 |
break;
|
58 |
}
|
59 |
|
60 |
+
$aSeries[] = $oSelEvts->sumEvents( $req->events );
|
61 |
|
62 |
$nTick++;
|
63 |
+
} while ( $nTick < $req->ticks );
|
64 |
|
65 |
return [
|
66 |
'data' => [
|
89 |
}
|
90 |
}
|
91 |
|
92 |
+
$aAll = array_keys( $this->getCon()->loadEventsService()->getEvents() );
|
93 |
if ( !empty( $oReq->chart_params[ 'stat_id' ] ) ) {
|
94 |
switch ( $oReq->chart_params[ 'stat_id' ] ) {
|
95 |
case 'comment_block':
|
src/lib/src/Modules/Events/Consolidate/ConsolidateAllEvents.php
CHANGED
@@ -3,6 +3,7 @@
|
|
3 |
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Events\Consolidate;
|
4 |
|
5 |
use FernleafSystems\Wordpress\Plugin\Shield\Databases\Events;
|
|
|
6 |
use FernleafSystems\Wordpress\Plugin\Shield\Modules\ModConsumer;
|
7 |
use FernleafSystems\Wordpress\Services\Services;
|
8 |
|
@@ -24,9 +25,9 @@ class ConsolidateAllEvents {
|
|
24 |
* @param $sEvent
|
25 |
*/
|
26 |
protected function consolidateEventIntoHourly( $sEvent ) {
|
27 |
-
/** @var
|
28 |
-
$
|
29 |
-
$oDbH = $
|
30 |
|
31 |
$oTime = Services::Request()
|
32 |
->carbon()
|
@@ -76,9 +77,9 @@ class ConsolidateAllEvents {
|
|
76 |
* @param $sEvent
|
77 |
*/
|
78 |
protected function consolidateEventIntoDaily( $sEvent ) {
|
79 |
-
/** @var
|
80 |
-
$
|
81 |
-
$oDbH = $
|
82 |
|
83 |
$oTime = Services::Request()
|
84 |
->carbon()
|
@@ -125,12 +126,12 @@ class ConsolidateAllEvents {
|
|
125 |
/**
|
126 |
* Consolidates each event in weekly sums. Doesn't process events from the previous 2 whole weeks.
|
127 |
* Processes event for the previous 8 weeks.
|
128 |
-
* @param $
|
129 |
*/
|
130 |
-
protected function consolidateEventIntoWeekly( $
|
131 |
-
/** @var
|
132 |
-
$
|
133 |
-
$oDbH = $
|
134 |
|
135 |
$oTime = Services::Request()
|
136 |
->carbon()
|
@@ -142,7 +143,7 @@ class ConsolidateAllEvents {
|
|
142 |
/** @var Events\Select $oSel */
|
143 |
$oSel = $oDbH->getQuerySelector();
|
144 |
$nRecords = $oSel->filterByBoundary_Week( $oTime->timestamp )
|
145 |
-
->filterByEvent( $
|
146 |
->count();
|
147 |
|
148 |
if ( $nRecords > 1 ) {
|
@@ -150,17 +151,17 @@ class ConsolidateAllEvents {
|
|
150 |
$oSel = $oDbH->getQuerySelector();
|
151 |
/** @var Events\EntryVO[] $aRecords */
|
152 |
$nSum = $oSel->filterByBoundary_Week( $oTime->timestamp )
|
153 |
-
->sumEvent( $
|
154 |
|
155 |
if ( $nSum > 0 ) {
|
156 |
/** @var Events\Delete $oDel */
|
157 |
$oDel = $oDbH->getQueryDeleter();
|
158 |
$oDel->filterByBoundary_Week( $oTime->timestamp )
|
159 |
-
->filterByEvent( $
|
160 |
->query();
|
161 |
|
162 |
$oEntry = new Events\EntryVO();
|
163 |
-
$oEntry->event = $
|
164 |
$oEntry->count = $nSum;
|
165 |
$oEntry->created_at = $oTime->timestamp + 1;
|
166 |
/** @var Events\Insert $oQI */
|
@@ -180,9 +181,9 @@ class ConsolidateAllEvents {
|
|
180 |
* @param $sEvent
|
181 |
*/
|
182 |
protected function consolidateEventIntoMonthly( $sEvent ) {
|
183 |
-
/** @var
|
184 |
-
$
|
185 |
-
$
|
186 |
|
187 |
$oTime = Services::Request()
|
188 |
->carbon()
|
@@ -192,21 +193,21 @@ class ConsolidateAllEvents {
|
|
192 |
$nMonthCount = 0;
|
193 |
do {
|
194 |
/** @var Events\Select $oSel */
|
195 |
-
$oSel = $
|
196 |
$nRecords = $oSel->filterByBoundary_Month( $oTime->timestamp )
|
197 |
->filterByEvent( $sEvent )
|
198 |
->count();
|
199 |
|
200 |
if ( $nRecords > 1 ) {
|
201 |
/** @var Events\Select $oSel */
|
202 |
-
$oSel = $
|
203 |
/** @var Events\EntryVO[] $aRecords */
|
204 |
$nSum = $oSel->filterByBoundary_Month( $oTime->timestamp )
|
205 |
->sumEvent( $sEvent );
|
206 |
|
207 |
if ( $nSum > 0 ) {
|
208 |
/** @var Events\Delete $oDel */
|
209 |
-
$oDel = $
|
210 |
$oDel->filterByBoundary_Month( $oTime->timestamp )
|
211 |
->filterByEvent( $sEvent )
|
212 |
->query();
|
@@ -216,7 +217,7 @@ class ConsolidateAllEvents {
|
|
216 |
$oEntry->count = $nSum;
|
217 |
$oEntry->created_at = $oTime->timestamp + 1;
|
218 |
/** @var Events\Insert $oQI */
|
219 |
-
$oQI = $
|
220 |
$oQI->insert( $oEntry );
|
221 |
}
|
222 |
}
|
@@ -227,12 +228,12 @@ class ConsolidateAllEvents {
|
|
227 |
}
|
228 |
|
229 |
/**
|
230 |
-
* @param $
|
231 |
*/
|
232 |
-
protected function consolidateEventIntoYearly( $
|
233 |
-
/** @var
|
234 |
-
$
|
235 |
-
$oDbH = $
|
236 |
|
237 |
$oTime = Services::Request()
|
238 |
->carbon()
|
@@ -241,13 +242,13 @@ class ConsolidateAllEvents {
|
|
241 |
|
242 |
/** @var Events\Select $oSel */
|
243 |
$oSel = $oDbH->getQuerySelector();
|
244 |
-
$oOldest = $oSel->getOldestForEvent( $
|
245 |
|
246 |
do {
|
247 |
/** @var Events\Select $oSel */
|
248 |
$oSel = $oDbH->getQuerySelector();
|
249 |
$nRecords = $oSel->filterByBoundary_Year( $oTime->timestamp )
|
250 |
-
->filterByEvent( $
|
251 |
->count();
|
252 |
|
253 |
if ( $nRecords > 1 ) {
|
@@ -255,17 +256,17 @@ class ConsolidateAllEvents {
|
|
255 |
$oSel = $oDbH->getQuerySelector();
|
256 |
/** @var Events\EntryVO[] $aRecords */
|
257 |
$nSum = $oSel->filterByBoundary_Year( $oTime->timestamp )
|
258 |
-
->sumEvent( $
|
259 |
|
260 |
if ( $nSum > 0 ) {
|
261 |
/** @var Events\Delete $oDel */
|
262 |
$oDel = $oDbH->getQueryDeleter();
|
263 |
$oDel->filterByBoundary_Year( $oTime->timestamp )
|
264 |
-
->filterByEvent( $
|
265 |
->query();
|
266 |
|
267 |
$oEntry = new Events\EntryVO();
|
268 |
-
$oEntry->event = $
|
269 |
$oEntry->count = $nSum;
|
270 |
$oEntry->created_at = $oTime->timestamp + 1;
|
271 |
/** @var Events\Insert $oQI */
|
@@ -282,10 +283,10 @@ class ConsolidateAllEvents {
|
|
282 |
* @return string[]
|
283 |
*/
|
284 |
protected function getAllEvents() {
|
285 |
-
/** @var
|
286 |
-
$
|
287 |
/** @var Events\Select $oSel */
|
288 |
-
$oSel = $
|
289 |
return $oSel->getAllEvents();
|
290 |
}
|
291 |
}
|
3 |
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Events\Consolidate;
|
4 |
|
5 |
use FernleafSystems\Wordpress\Plugin\Shield\Databases\Events;
|
6 |
+
use FernleafSystems\Wordpress\Plugin\Shield\Modules\Events\ModCon;
|
7 |
use FernleafSystems\Wordpress\Plugin\Shield\Modules\ModConsumer;
|
8 |
use FernleafSystems\Wordpress\Services\Services;
|
9 |
|
25 |
* @param $sEvent
|
26 |
*/
|
27 |
protected function consolidateEventIntoHourly( $sEvent ) {
|
28 |
+
/** @var ModCon $mod */
|
29 |
+
$mod = $this->getMod();
|
30 |
+
$oDbH = $mod->getDbHandler_Events();
|
31 |
|
32 |
$oTime = Services::Request()
|
33 |
->carbon()
|
77 |
* @param $sEvent
|
78 |
*/
|
79 |
protected function consolidateEventIntoDaily( $sEvent ) {
|
80 |
+
/** @var ModCon $mod */
|
81 |
+
$mod = $this->getMod();
|
82 |
+
$oDbH = $mod->getDbHandler_Events();
|
83 |
|
84 |
$oTime = Services::Request()
|
85 |
->carbon()
|
126 |
/**
|
127 |
* Consolidates each event in weekly sums. Doesn't process events from the previous 2 whole weeks.
|
128 |
* Processes event for the previous 8 weeks.
|
129 |
+
* @param $event
|
130 |
*/
|
131 |
+
protected function consolidateEventIntoWeekly( $event ) {
|
132 |
+
/** @var ModCon $mod */
|
133 |
+
$mod = $this->getMod();
|
134 |
+
$oDbH = $mod->getDbHandler_Events();
|
135 |
|
136 |
$oTime = Services::Request()
|
137 |
->carbon()
|
143 |
/** @var Events\Select $oSel */
|
144 |
$oSel = $oDbH->getQuerySelector();
|
145 |
$nRecords = $oSel->filterByBoundary_Week( $oTime->timestamp )
|
146 |
+
->filterByEvent( $event )
|
147 |
->count();
|
148 |
|
149 |
if ( $nRecords > 1 ) {
|
151 |
$oSel = $oDbH->getQuerySelector();
|
152 |
/** @var Events\EntryVO[] $aRecords */
|
153 |
$nSum = $oSel->filterByBoundary_Week( $oTime->timestamp )
|
154 |
+
->sumEvent( $event );
|
155 |
|
156 |
if ( $nSum > 0 ) {
|
157 |
/** @var Events\Delete $oDel */
|
158 |
$oDel = $oDbH->getQueryDeleter();
|
159 |
$oDel->filterByBoundary_Week( $oTime->timestamp )
|
160 |
+
->filterByEvent( $event )
|
161 |
->query();
|
162 |
|
163 |
$oEntry = new Events\EntryVO();
|
164 |
+
$oEntry->event = $event;
|
165 |
$oEntry->count = $nSum;
|
166 |
$oEntry->created_at = $oTime->timestamp + 1;
|
167 |
/** @var Events\Insert $oQI */
|
181 |
* @param $sEvent
|
182 |
*/
|
183 |
protected function consolidateEventIntoMonthly( $sEvent ) {
|
184 |
+
/** @var ModCon $mod */
|
185 |
+
$mod = $this->getMod();
|
186 |
+
$dbh = $mod->getDbHandler_Events();
|
187 |
|
188 |
$oTime = Services::Request()
|
189 |
->carbon()
|
193 |
$nMonthCount = 0;
|
194 |
do {
|
195 |
/** @var Events\Select $oSel */
|
196 |
+
$oSel = $dbh->getQuerySelector();
|
197 |
$nRecords = $oSel->filterByBoundary_Month( $oTime->timestamp )
|
198 |
->filterByEvent( $sEvent )
|
199 |
->count();
|
200 |
|
201 |
if ( $nRecords > 1 ) {
|
202 |
/** @var Events\Select $oSel */
|
203 |
+
$oSel = $dbh->getQuerySelector();
|
204 |
/** @var Events\EntryVO[] $aRecords */
|
205 |
$nSum = $oSel->filterByBoundary_Month( $oTime->timestamp )
|
206 |
->sumEvent( $sEvent );
|
207 |
|
208 |
if ( $nSum > 0 ) {
|
209 |
/** @var Events\Delete $oDel */
|
210 |
+
$oDel = $dbh->getQueryDeleter();
|
211 |
$oDel->filterByBoundary_Month( $oTime->timestamp )
|
212 |
->filterByEvent( $sEvent )
|
213 |
->query();
|
217 |
$oEntry->count = $nSum;
|
218 |
$oEntry->created_at = $oTime->timestamp + 1;
|
219 |
/** @var Events\Insert $oQI */
|
220 |
+
$oQI = $dbh->getQueryInserter();
|
221 |
$oQI->insert( $oEntry );
|
222 |
}
|
223 |
}
|
228 |
}
|
229 |
|
230 |
/**
|
231 |
+
* @param $event
|
232 |
*/
|
233 |
+
protected function consolidateEventIntoYearly( $event ) {
|
234 |
+
/** @var ModCon $mod */
|
235 |
+
$mod = $this->getMod();
|
236 |
+
$oDbH = $mod->getDbHandler_Events();
|
237 |
|
238 |
$oTime = Services::Request()
|
239 |
->carbon()
|
242 |
|
243 |
/** @var Events\Select $oSel */
|
244 |
$oSel = $oDbH->getQuerySelector();
|
245 |
+
$oOldest = $oSel->getOldestForEvent( $event );
|
246 |
|
247 |
do {
|
248 |
/** @var Events\Select $oSel */
|
249 |
$oSel = $oDbH->getQuerySelector();
|
250 |
$nRecords = $oSel->filterByBoundary_Year( $oTime->timestamp )
|
251 |
+
->filterByEvent( $event )
|
252 |
->count();
|
253 |
|
254 |
if ( $nRecords > 1 ) {
|
256 |
$oSel = $oDbH->getQuerySelector();
|
257 |
/** @var Events\EntryVO[] $aRecords */
|
258 |
$nSum = $oSel->filterByBoundary_Year( $oTime->timestamp )
|
259 |
+
->sumEvent( $event );
|
260 |
|
261 |
if ( $nSum > 0 ) {
|
262 |
/** @var Events\Delete $oDel */
|
263 |
$oDel = $oDbH->getQueryDeleter();
|
264 |
$oDel->filterByBoundary_Year( $oTime->timestamp )
|
265 |
+
->filterByEvent( $event )
|
266 |
->query();
|
267 |
|
268 |
$oEntry = new Events\EntryVO();
|
269 |
+
$oEntry->event = $event;
|
270 |
$oEntry->count = $nSum;
|
271 |
$oEntry->created_at = $oTime->timestamp + 1;
|
272 |
/** @var Events\Insert $oQI */
|
283 |
* @return string[]
|
284 |
*/
|
285 |
protected function getAllEvents() {
|
286 |
+
/** @var ModCon $mod */
|
287 |
+
$mod = $this->getMod();
|
288 |
/** @var Events\Select $oSel */
|
289 |
+
$oSel = $mod->getDbHandler_Events()->getQuerySelector();
|
290 |
return $oSel->getAllEvents();
|
291 |
}
|
292 |
}
|
src/lib/src/Modules/Events/Lib/EventsListener.php
CHANGED
@@ -22,9 +22,9 @@ abstract class EventsListener {
|
|
22 |
$this->setCon( $con );
|
23 |
|
24 |
add_action( $con->prefix( 'event' ),
|
25 |
-
function ( $
|
26 |
-
if ( $con->loadEventsService()->isSupportedEvent( $
|
27 |
-
$this->captureEvent( $
|
28 |
}
|
29 |
}, 10, 2 );
|
30 |
|
@@ -34,10 +34,10 @@ abstract class EventsListener {
|
|
34 |
}
|
35 |
|
36 |
/**
|
37 |
-
* @param string $
|
38 |
* @param array $aMeta
|
39 |
*/
|
40 |
-
abstract protected function captureEvent( $
|
41 |
|
42 |
protected function onShutdown() {
|
43 |
|
22 |
$this->setCon( $con );
|
23 |
|
24 |
add_action( $con->prefix( 'event' ),
|
25 |
+
function ( $event, $meta = [] ) use ( $con ) {
|
26 |
+
if ( $con->loadEventsService()->isSupportedEvent( $event ) ) {
|
27 |
+
$this->captureEvent( $event, $meta );
|
28 |
}
|
29 |
}, 10, 2 );
|
30 |
|
34 |
}
|
35 |
|
36 |
/**
|
37 |
+
* @param string $evt
|
38 |
* @param array $aMeta
|
39 |
*/
|
40 |
+
abstract protected function captureEvent( $evt, $aMeta = [] );
|
41 |
|
42 |
protected function onShutdown() {
|
43 |
|
src/lib/src/Modules/Events/Lib/EventsService.php
CHANGED
@@ -14,13 +14,13 @@ class EventsService {
|
|
14 |
private $aEvents;
|
15 |
|
16 |
/**
|
17 |
-
* @param string $
|
18 |
-
* @param array $
|
19 |
* @return $this
|
20 |
*/
|
21 |
-
public function fireEvent( $
|
22 |
-
if ( $this->isSupportedEvent( $
|
23 |
-
do_action( $this->getCon()->prefix( 'event' ), $
|
24 |
}
|
25 |
return $this;
|
26 |
}
|
@@ -30,41 +30,46 @@ class EventsService {
|
|
30 |
*/
|
31 |
public function getEvents() :array {
|
32 |
if ( empty( $this->aEvents ) ) {
|
33 |
-
$
|
34 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
35 |
}
|
36 |
return $this->aEvents;
|
37 |
}
|
38 |
|
39 |
/**
|
40 |
-
* @param string $
|
41 |
* @return array|null
|
42 |
*/
|
43 |
-
public function getEventDef( $
|
44 |
-
return $this->isSupportedEvent( $
|
45 |
}
|
46 |
|
47 |
/**
|
48 |
* @return string[]
|
|
|
49 |
*/
|
50 |
public function getEventKeys() {
|
51 |
return array_keys( $this->getEvents() );
|
52 |
}
|
53 |
|
54 |
-
|
55 |
-
|
56 |
-
* @return bool
|
57 |
-
*/
|
58 |
-
public function isSupportedEvent( $sEventKey ) {
|
59 |
-
return in_array( $sEventKey, $this->getEventKeys() );
|
60 |
}
|
61 |
|
62 |
-
|
63 |
-
|
64 |
-
* @return array[]
|
65 |
-
*/
|
66 |
-
protected function buildEvents( $aEvents ) {
|
67 |
-
$aDefaults = [
|
68 |
'cat' => 1,
|
69 |
'stat' => true,
|
70 |
'audit' => true,
|
@@ -73,10 +78,10 @@ class EventsService {
|
|
73 |
'audit_multiple' => false, // allow multiple audit entries in the same request
|
74 |
'suppress_offense' => false, // events that normally trigger offense can be forcefully suppressed
|
75 |
];
|
76 |
-
foreach ( $
|
77 |
-
$
|
78 |
-
$
|
79 |
}
|
80 |
-
return $
|
81 |
}
|
82 |
}
|
14 |
private $aEvents;
|
15 |
|
16 |
/**
|
17 |
+
* @param string $event
|
18 |
+
* @param array $meta
|
19 |
* @return $this
|
20 |
*/
|
21 |
+
public function fireEvent( $event, $meta = [] ) {
|
22 |
+
if ( $this->isSupportedEvent( $event ) ) {
|
23 |
+
do_action( $this->getCon()->prefix( 'event' ), $event, $meta );
|
24 |
}
|
25 |
return $this;
|
26 |
}
|
30 |
*/
|
31 |
public function getEvents() :array {
|
32 |
if ( empty( $this->aEvents ) ) {
|
33 |
+
$events = [];
|
34 |
+
foreach ( $this->getCon()->modules as $mod ) {
|
35 |
+
$events = array_merge(
|
36 |
+
$events,
|
37 |
+
array_map(
|
38 |
+
function ( $evt ) use ( $mod ) {
|
39 |
+
$evt[ 'context' ] = $mod->getSlug();
|
40 |
+
return $evt;
|
41 |
+
},
|
42 |
+
is_array( $mod->getDef( 'events' ) ) ? $mod->getDef( 'events' ) : []
|
43 |
+
)
|
44 |
+
);
|
45 |
+
}
|
46 |
+
$this->aEvents = $this->buildEvents( $events );
|
47 |
}
|
48 |
return $this->aEvents;
|
49 |
}
|
50 |
|
51 |
/**
|
52 |
+
* @param string $eventKey
|
53 |
* @return array|null
|
54 |
*/
|
55 |
+
public function getEventDef( string $eventKey ) {
|
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 |
}
|
70 |
|
71 |
+
private function buildEvents( array $events ) :array {
|
72 |
+
$defaults = [
|
|
|
|
|
|
|
|
|
73 |
'cat' => 1,
|
74 |
'stat' => true,
|
75 |
'audit' => true,
|
78 |
'audit_multiple' => false, // allow multiple audit entries in the same request
|
79 |
'suppress_offense' => false, // events that normally trigger offense can be forcefully suppressed
|
80 |
];
|
81 |
+
foreach ( $events as $eventKey => $evt ) {
|
82 |
+
$events[ $eventKey ] = array_merge( $defaults, $evt );
|
83 |
+
$events[ $eventKey ][ 'key' ] = $eventKey;
|
84 |
}
|
85 |
+
return $events;
|
86 |
}
|
87 |
}
|
src/lib/src/Modules/Events/Lib/Reports/KeyStats.php
CHANGED
@@ -14,12 +14,12 @@ class KeyStats extends BaseReporter {
|
|
14 |
public function build() {
|
15 |
$aAlerts = [];
|
16 |
|
17 |
-
/** @var \
|
18 |
-
$
|
19 |
/** @var DBEvents\Select $oSelEvts */
|
20 |
-
$oSelEvts = $
|
21 |
/** @var Events\Strings $oStrings */
|
22 |
-
$oStrings = $
|
23 |
|
24 |
$aEventKeys = [
|
25 |
'ip_offense',
|
14 |
public function build() {
|
15 |
$aAlerts = [];
|
16 |
|
17 |
+
/** @var Events\ModCon $mod */
|
18 |
+
$mod = $this->getMod();
|
19 |
/** @var DBEvents\Select $oSelEvts */
|
20 |
+
$oSelEvts = $mod->getDbHandler_Events()->getQuerySelector();
|
21 |
/** @var Events\Strings $oStrings */
|
22 |
+
$oStrings = $mod->getStrings();
|
23 |
|
24 |
$aEventKeys = [
|
25 |
'ip_offense',
|
src/lib/src/Modules/Events/Lib/Reports/ScanRepairs.php
CHANGED
@@ -15,12 +15,12 @@ class ScanRepairs extends BaseReporter {
|
|
15 |
public function build() {
|
16 |
$aAlerts = [];
|
17 |
|
18 |
-
/** @var \
|
19 |
-
$
|
20 |
/** @var DBEvents\Select $oSelEvts */
|
21 |
-
$oSelEvts = $
|
22 |
/** @var Events\Strings $oStrings */
|
23 |
-
$oStrings = $
|
24 |
|
25 |
$oRep = $this->getReport();
|
26 |
|
15 |
public function build() {
|
16 |
$aAlerts = [];
|
17 |
|
18 |
+
/** @var Events\ModCon $mod */
|
19 |
+
$mod = $this->getMod();
|
20 |
/** @var DBEvents\Select $oSelEvts */
|
21 |
+
$oSelEvts = $mod->getDbHandler_Events()->getQuerySelector();
|
22 |
/** @var Events\Strings $oStrings */
|
23 |
+
$oStrings = $mod->getStrings();
|
24 |
|
25 |
$oRep = $this->getReport();
|
26 |
|
src/lib/src/Modules/Events/Lib/StatsWriter.php
CHANGED
@@ -16,12 +16,12 @@ class StatsWriter extends EventsListener {
|
|
16 |
private $aEventStats;
|
17 |
|
18 |
/**
|
19 |
-
* @param string $
|
20 |
* @param array $aMeta
|
21 |
*/
|
22 |
-
protected function captureEvent( $
|
23 |
$aStats = $this->getEventStats();
|
24 |
-
$aStats[ $
|
25 |
$this->setEventStats( $aStats );
|
26 |
}
|
27 |
|
16 |
private $aEventStats;
|
17 |
|
18 |
/**
|
19 |
+
* @param string $evt
|
20 |
* @param array $aMeta
|
21 |
*/
|
22 |
+
protected function captureEvent( $evt, $aMeta = [] ) {
|
23 |
$aStats = $this->getEventStats();
|
24 |
+
$aStats[ $evt ] = isset( $aMeta[ 'ts' ] ) ? $aMeta[ 'ts' ] : Services::Request()->ts();
|
25 |
$this->setEventStats( $aStats );
|
26 |
}
|
27 |
|
src/lib/src/Modules/Events/ModCon.php
ADDED
@@ -0,0 +1,23 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php declare( strict_types=1 );
|
2 |
+
|
3 |
+
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Events;
|
4 |
+
|
5 |
+
use FernleafSystems\Wordpress\Plugin\Shield;
|
6 |
+
use FernleafSystems\Wordpress\Plugin\Shield\Modules\BaseShield;
|
7 |
+
|
8 |
+
class ModCon extends BaseShield\ModCon {
|
9 |
+
|
10 |
+
public function getDbHandler_Events() :Shield\Databases\Events\Handler {
|
11 |
+
return $this->getDbH( 'events' );
|
12 |
+
}
|
13 |
+
|
14 |
+
/**
|
15 |
+
* @return bool
|
16 |
+
* @throws \Exception
|
17 |
+
*/
|
18 |
+
protected function isReadyToExecute() :bool {
|
19 |
+
return ( $this->getDbHandler_Events() instanceof Shield\Databases\Events\Handler )
|
20 |
+
&& $this->getDbHandler_Events()->isReady()
|
21 |
+
&& parent::isReadyToExecute();
|
22 |
+
}
|
23 |
+
}
|
src/lib/src/Modules/Events/Options.php
CHANGED
@@ -1,16 +1,9 @@
|
|
1 |
-
<?php
|
2 |
|
3 |
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Events;
|
4 |
|
5 |
-
use FernleafSystems\Wordpress\Plugin\Shield\Modules\
|
6 |
|
7 |
-
class Options extends
|
8 |
|
9 |
-
/**
|
10 |
-
* @return string
|
11 |
-
* @deprecated 10.0
|
12 |
-
*/
|
13 |
-
public function getDbTable_Events() :string {
|
14 |
-
return $this->getCon()->prefixOption( $this->getDef( 'events_table_name' ) );
|
15 |
-
}
|
16 |
}
|
1 |
+
<?php declare( strict_types=1 );
|
2 |
|
3 |
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Events;
|
4 |
|
5 |
+
use FernleafSystems\Wordpress\Plugin\Shield\Modules\BaseShield;
|
6 |
|
7 |
+
class Options extends BaseShield\Options {
|
8 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
9 |
}
|
src/lib/src/Modules/Events/Processor.php
ADDED
@@ -0,0 +1,92 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Events;
|
4 |
+
|
5 |
+
use FernleafSystems\Wordpress\Plugin\Shield\Modules\BaseShield;
|
6 |
+
use FernleafSystems\Wordpress\Plugin\Shield\Databases;
|
7 |
+
use FernleafSystems\Wordpress\Plugin\Shield\Modules\Events;
|
8 |
+
|
9 |
+
class Processor extends BaseShield\Processor {
|
10 |
+
|
11 |
+
/**
|
12 |
+
* @var Events\Lib\StatsWriter
|
13 |
+
*/
|
14 |
+
private $oStatsWriter;
|
15 |
+
|
16 |
+
protected function run() {
|
17 |
+
$this->loadStatsWriter()->setIfCommit( true );
|
18 |
+
add_action( $this->getCon()->prefix( 'dashboard_widget_content' ), [ $this, 'statsWidget' ], 10 );
|
19 |
+
}
|
20 |
+
|
21 |
+
/**
|
22 |
+
* @return Events\Lib\StatsWriter
|
23 |
+
*/
|
24 |
+
public function loadStatsWriter() {
|
25 |
+
if ( !isset( $this->oStatsWriter ) ) {
|
26 |
+
/** @var ModCon $mod */
|
27 |
+
$mod = $this->getMod();
|
28 |
+
$this->oStatsWriter = ( new Events\Lib\StatsWriter( $this->getCon() ) )
|
29 |
+
->setDbHandler( $mod->getDbHandler_Events() );
|
30 |
+
}
|
31 |
+
return $this->oStatsWriter;
|
32 |
+
}
|
33 |
+
|
34 |
+
public function statsWidget() {
|
35 |
+
/** @var Databases\Events\Select $oSelEvents */
|
36 |
+
$oSelEvents = $this->getCon()
|
37 |
+
->getModule_Events()
|
38 |
+
->getDbHandler_Events()
|
39 |
+
->getQuerySelector();
|
40 |
+
|
41 |
+
$aKeyStats = [
|
42 |
+
'comments' => [
|
43 |
+
__( 'Comment Blocks', 'wp-simple-firewall' ),
|
44 |
+
$oSelEvents->clearWheres()->sumEvents( [
|
45 |
+
'spam_block_bot',
|
46 |
+
'spam_block_human',
|
47 |
+
'spam_block_recaptcha'
|
48 |
+
] )
|
49 |
+
],
|
50 |
+
'firewall' => [
|
51 |
+
__( 'Firewall Blocks', 'wp-simple-firewall' ),
|
52 |
+
$oSelEvents->clearWheres()->sumEvent( 'firewall_block' )
|
53 |
+
],
|
54 |
+
'login_fail' => [
|
55 |
+
__( 'Login Blocks', 'wp-simple-firewall' ),
|
56 |
+
$oSelEvents->clearWheres()->sumEvent( 'login_block' )
|
57 |
+
],
|
58 |
+
'login_verified' => [
|
59 |
+
__( 'Login Verified', 'wp-simple-firewall' ),
|
60 |
+
$oSelEvents->clearWheres()->sumEvent( '2fa_success' )
|
61 |
+
],
|
62 |
+
'session_start' => [
|
63 |
+
__( 'User Sessions', 'wp-simple-firewall' ),
|
64 |
+
$oSelEvents->clearWheres()->sumEvent( 'session_start' )
|
65 |
+
],
|
66 |
+
'ip_killed' => [
|
67 |
+
__( 'IP Blocks', 'wp-simple-firewall' ),
|
68 |
+
$oSelEvents->clearWheres()->sumEvent( 'conn_kill' )
|
69 |
+
],
|
70 |
+
'ip_transgressions' => [
|
71 |
+
__( 'Total Offenses', 'wp-simple-firewall' ),
|
72 |
+
$oSelEvents->clearWheres()->sumEvent( 'ip_offense' )
|
73 |
+
],
|
74 |
+
];
|
75 |
+
|
76 |
+
$aDisplayData = [
|
77 |
+
'sHeading' => sprintf( __( '%s Statistics', 'wp-simple-firewall' ), $this->getCon()->getHumanName() ),
|
78 |
+
'aKeyStats' => $aKeyStats,
|
79 |
+
];
|
80 |
+
|
81 |
+
echo $this->getMod()->renderTemplate(
|
82 |
+
'snippets/widget_dashboard_statistics.php',
|
83 |
+
$aDisplayData
|
84 |
+
);
|
85 |
+
}
|
86 |
+
|
87 |
+
public function runDailyCron() {
|
88 |
+
( new Events\Consolidate\ConsolidateAllEvents() )
|
89 |
+
->setMod( $this->getMod() )
|
90 |
+
->run();
|
91 |
+
}
|
92 |
+
}
|
src/lib/src/Modules/Events/Reporting.php
CHANGED
@@ -2,10 +2,10 @@
|
|
2 |
|
3 |
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Events;
|
4 |
|
5 |
-
use FernleafSystems\Wordpress\Plugin\Shield\Modules\Base
|
6 |
use FernleafSystems\Wordpress\Plugin\Shield\Modules\Events\Lib\Reports;
|
7 |
|
8 |
-
class Reporting extends
|
9 |
|
10 |
/**
|
11 |
* @inheritDoc
|
2 |
|
3 |
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Events;
|
4 |
|
5 |
+
use FernleafSystems\Wordpress\Plugin\Shield\Modules\Base;
|
6 |
use FernleafSystems\Wordpress\Plugin\Shield\Modules\Events\Lib\Reports;
|
7 |
|
8 |
+
class Reporting extends Base\Reporting {
|
9 |
|
10 |
/**
|
11 |
* @inheritDoc
|
src/lib/src/Modules/Events/Strings.php
CHANGED
@@ -20,7 +20,7 @@ class Strings extends Base\Strings {
|
|
20 |
* @return string[]
|
21 |
*/
|
22 |
public function getEventNames( $bAuto = true ) {
|
23 |
-
$
|
24 |
'test_cron_run' => __( 'Test Cron Run', 'wp-simple-firewall' ),
|
25 |
'import_notify_sent' => __( 'Import Notify Sent', 'wp-simple-firewall' ),
|
26 |
'import_notify_received' => __( 'Import Notify Received', 'wp-simple-firewall' ),
|
@@ -237,9 +237,11 @@ class Strings extends Base\Strings {
|
|
237 |
'session_terminate' => __( 'Session Terminated', 'wp-simple-firewall' ),
|
238 |
'plugin_activated' => __( 'Plugin Activated', 'wp-simple-firewall' ),
|
239 |
'plugin_deactivated' => __( 'Plugin Deactivated', 'wp-simple-firewall' ),
|
|
|
240 |
'plugin_file_edited' => __( 'Plugin File Edited', 'wp-simple-firewall' ),
|
241 |
'theme_activated' => __( 'Theme Activated', 'wp-simple-firewall' ),
|
242 |
'theme_file_edited' => __( 'Theme File Edited', 'wp-simple-firewall' ),
|
|
|
243 |
'core_updated' => __( 'WP Core Updated', 'wp-simple-firewall' ),
|
244 |
'permalinks_structure' => __( 'Permalinks Updated', 'wp-simple-firewall' ),
|
245 |
'post_deleted' => __( 'Post Deleted', 'wp-simple-firewall' ),
|
@@ -260,13 +262,13 @@ class Strings extends Base\Strings {
|
|
260 |
];
|
261 |
|
262 |
if ( $bAuto ) {
|
263 |
-
foreach ( $
|
264 |
-
if ( empty( $
|
265 |
-
$
|
266 |
}
|
267 |
}
|
268 |
}
|
269 |
|
270 |
-
return $
|
271 |
}
|
272 |
}
|
20 |
* @return string[]
|
21 |
*/
|
22 |
public function getEventNames( $bAuto = true ) {
|
23 |
+
$names = [
|
24 |
'test_cron_run' => __( 'Test Cron Run', 'wp-simple-firewall' ),
|
25 |
'import_notify_sent' => __( 'Import Notify Sent', 'wp-simple-firewall' ),
|
26 |
'import_notify_received' => __( 'Import Notify Received', 'wp-simple-firewall' ),
|
237 |
'session_terminate' => __( 'Session Terminated', 'wp-simple-firewall' ),
|
238 |
'plugin_activated' => __( 'Plugin Activated', 'wp-simple-firewall' ),
|
239 |
'plugin_deactivated' => __( 'Plugin Deactivated', 'wp-simple-firewall' ),
|
240 |
+
'plugin_upgraded' => __( 'Plugin Upgraded', 'wp-simple-firewall' ),
|
241 |
'plugin_file_edited' => __( 'Plugin File Edited', 'wp-simple-firewall' ),
|
242 |
'theme_activated' => __( 'Theme Activated', 'wp-simple-firewall' ),
|
243 |
'theme_file_edited' => __( 'Theme File Edited', 'wp-simple-firewall' ),
|
244 |
+
'theme_upgraded' => __( 'Theme Upgraded', 'wp-simple-firewall' ),
|
245 |
'core_updated' => __( 'WP Core Updated', 'wp-simple-firewall' ),
|
246 |
'permalinks_structure' => __( 'Permalinks Updated', 'wp-simple-firewall' ),
|
247 |
'post_deleted' => __( 'Post Deleted', 'wp-simple-firewall' ),
|
262 |
];
|
263 |
|
264 |
if ( $bAuto ) {
|
265 |
+
foreach ( $names as $key => $name ) {
|
266 |
+
if ( empty( $name ) ) {
|
267 |
+
$names[ $key ] = ucwords( str_replace( '_', ' ', $key ) );
|
268 |
}
|
269 |
}
|
270 |
}
|
271 |
|
272 |
+
return $names;
|
273 |
}
|
274 |
}
|
src/lib/src/Modules/Firewall/Insights/OverviewCards.php
CHANGED
@@ -3,11 +3,12 @@
|
|
3 |
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Firewall\Insights;
|
4 |
|
5 |
use FernleafSystems\Wordpress\Plugin\Shield;
|
|
|
6 |
|
7 |
class OverviewCards extends Shield\Modules\Base\Insights\OverviewCards {
|
8 |
|
9 |
public function build() :array {
|
10 |
-
/** @var
|
11 |
$mod = $this->getMod();
|
12 |
/** @var \FernleafSystems\Wordpress\Plugin\Shield\Modules\Firewall\Options $opts */
|
13 |
$opts = $this->getOptions();
|
3 |
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Firewall\Insights;
|
4 |
|
5 |
use FernleafSystems\Wordpress\Plugin\Shield;
|
6 |
+
use FernleafSystems\Wordpress\Plugin\Shield\Modules\Firewall\ModCon;
|
7 |
|
8 |
class OverviewCards extends Shield\Modules\Base\Insights\OverviewCards {
|
9 |
|
10 |
public function build() :array {
|
11 |
+
/** @var ModCon $mod */
|
12 |
$mod = $this->getMod();
|
13 |
/** @var \FernleafSystems\Wordpress\Plugin\Shield\Modules\Firewall\Options $opts */
|
14 |
$opts = $this->getOptions();
|
src/lib/src/Modules/Firewall/ModCon.php
ADDED
@@ -0,0 +1,53 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php declare( strict_types=1 );
|
2 |
+
|
3 |
+
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Firewall;
|
4 |
+
|
5 |
+
use FernleafSystems\Wordpress\Plugin\Shield\Databases;
|
6 |
+
use FernleafSystems\Wordpress\Plugin\Shield\Modules\BaseShield;
|
7 |
+
|
8 |
+
class ModCon extends BaseShield\ModCon {
|
9 |
+
|
10 |
+
/**
|
11 |
+
* @param string $sParam
|
12 |
+
* @param string $sPage
|
13 |
+
*/
|
14 |
+
public function addParamToWhitelist( $sParam, $sPage = '*' ) {
|
15 |
+
/** @var Options $opts */
|
16 |
+
$opts = $this->getOptions();
|
17 |
+
|
18 |
+
if ( empty( $sPage ) ) {
|
19 |
+
$sPage = '*';
|
20 |
+
}
|
21 |
+
|
22 |
+
$aW = $opts->getCustomWhitelist();
|
23 |
+
$aParams = isset( $aW[ $sPage ] ) ? $aW[ $sPage ] : [];
|
24 |
+
$aParams[] = $sParam;
|
25 |
+
natsort( $aParams );
|
26 |
+
$aW[ $sPage ] = array_unique( $aParams );
|
27 |
+
|
28 |
+
$opts->setOpt( 'page_params_whitelist', $aW );
|
29 |
+
}
|
30 |
+
|
31 |
+
public function getBlockResponse() :string {
|
32 |
+
$response = $this->getOptions()->getOpt( 'block_response', '' );
|
33 |
+
return !empty( $response ) ? $response : 'redirect_die_message'; // TODO: use default
|
34 |
+
}
|
35 |
+
|
36 |
+
public function getTextOptDefault( string $key ) :string {
|
37 |
+
|
38 |
+
switch ( $key ) {
|
39 |
+
case 'text_firewalldie':
|
40 |
+
$text = sprintf(
|
41 |
+
__( "You were blocked by the %s.", 'wp-simple-firewall' ),
|
42 |
+
'<a href="https://wordpress.org/plugins/wp-simple-firewall/" target="_blank">'.$this->getCon()
|
43 |
+
->getHumanName().'</a>'
|
44 |
+
);
|
45 |
+
break;
|
46 |
+
|
47 |
+
default:
|
48 |
+
$text = parent::getTextOptDefault( $key );
|
49 |
+
break;
|
50 |
+
}
|
51 |
+
return $text;
|
52 |
+
}
|
53 |
+
}
|
src/lib/src/Modules/Firewall/Options.php
CHANGED
@@ -2,9 +2,9 @@
|
|
2 |
|
3 |
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Firewall;
|
4 |
|
5 |
-
use FernleafSystems\Wordpress\Plugin\Shield\Modules\
|
6 |
|
7 |
-
class Options extends
|
8 |
|
9 |
public function getCustomWhitelist() :array {
|
10 |
$aW = $this->getOpt( 'page_params_whitelist', [] );
|
2 |
|
3 |
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Firewall;
|
4 |
|
5 |
+
use FernleafSystems\Wordpress\Plugin\Shield\Modules\BaseShield;
|
6 |
|
7 |
+
class Options extends BaseShield\Options {
|
8 |
|
9 |
public function getCustomWhitelist() :array {
|
10 |
$aW = $this->getOpt( 'page_params_whitelist', [] );
|
src/lib/src/Modules/Firewall/Processor.php
ADDED
@@ -0,0 +1,435 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Firewall;
|
4 |
+
|
5 |
+
use FernleafSystems\Wordpress\Plugin\Shield\Modules\BaseShield;
|
6 |
+
use FernleafSystems\Wordpress\Services\Services;
|
7 |
+
|
8 |
+
class Processor extends BaseShield\Processor {
|
9 |
+
|
10 |
+
/**
|
11 |
+
* @var array
|
12 |
+
*/
|
13 |
+
private $aDieMessage;
|
14 |
+
|
15 |
+
/**
|
16 |
+
* @var array
|
17 |
+
*/
|
18 |
+
protected $aPatterns;
|
19 |
+
|
20 |
+
/**
|
21 |
+
* @var array
|
22 |
+
*/
|
23 |
+
private $aAuditBlockMessage;
|
24 |
+
|
25 |
+
/**
|
26 |
+
* After any parameter whitelisting has been accounted for
|
27 |
+
*
|
28 |
+
* @var array
|
29 |
+
*/
|
30 |
+
private $params;
|
31 |
+
|
32 |
+
protected function run() {
|
33 |
+
if ( $this->getIfPerformFirewallScan() && $this->getIfDoFirewallBlock() ) {
|
34 |
+
// Hooked here to ensure "plugins_loaded" has completely finished as some mailers aren't init'd.
|
35 |
+
add_action( 'init', function () {
|
36 |
+
$this->doPreFirewallBlock();
|
37 |
+
$this->doFirewallBlock();
|
38 |
+
}, 0 );
|
39 |
+
}
|
40 |
+
}
|
41 |
+
|
42 |
+
private function getIfDoFirewallBlock() :bool {
|
43 |
+
return !$this->isVisitorRequestPermitted();
|
44 |
+
}
|
45 |
+
|
46 |
+
private function getIfPerformFirewallScan() :bool {
|
47 |
+
$bPerformScan = true;
|
48 |
+
/** @var Options $opts */
|
49 |
+
$opts = $this->getOptions();
|
50 |
+
|
51 |
+
$sPath = Services::Request()->getPath();
|
52 |
+
|
53 |
+
if ( count( $this->getRawRequestParams() ) == 0 ) {
|
54 |
+
$bPerformScan = false;
|
55 |
+
}
|
56 |
+
elseif ( empty( $sPath ) ) {
|
57 |
+
$this->getCon()->fireEvent( 'firewall_skip' );
|
58 |
+
$bPerformScan = false;
|
59 |
+
}
|
60 |
+
elseif ( count( $this->getParamsToCheck() ) == 0 ) {
|
61 |
+
$bPerformScan = false;
|
62 |
+
}
|
63 |
+
// TODO: are we calling is_super_admin() too early?
|
64 |
+
elseif ( $opts->isIgnoreAdmin() && is_super_admin() ) {
|
65 |
+
$bPerformScan = false;
|
66 |
+
}
|
67 |
+
|
68 |
+
return $bPerformScan;
|
69 |
+
}
|
70 |
+
|
71 |
+
private function isVisitorRequestPermitted() :bool {
|
72 |
+
$opts = $this->getOptions();
|
73 |
+
|
74 |
+
$bRequestIsPermitted = true;
|
75 |
+
if ( $bRequestIsPermitted && $opts->isOpt( 'block_dir_traversal', 'Y' ) ) {
|
76 |
+
$bRequestIsPermitted = $this->doPassCheck( 'dirtraversal' );
|
77 |
+
}
|
78 |
+
if ( $bRequestIsPermitted && $opts->isOpt( 'block_sql_queries', 'Y' ) ) {
|
79 |
+
$bRequestIsPermitted = $this->doPassCheck( 'sqlqueries' );
|
80 |
+
}
|
81 |
+
if ( $bRequestIsPermitted && $opts->isOpt( 'block_wordpress_terms', 'Y' ) ) {
|
82 |
+
$bRequestIsPermitted = $this->doPassCheck( 'wpterms' );
|
83 |
+
}
|
84 |
+
if ( $bRequestIsPermitted && $opts->isOpt( 'block_field_truncation', 'Y' ) ) {
|
85 |
+
$bRequestIsPermitted = $this->doPassCheck( 'fieldtruncation' );
|
86 |
+
}
|
87 |
+
if ( $bRequestIsPermitted && $opts->isOpt( 'block_php_code', 'Y' ) ) {
|
88 |
+
$bRequestIsPermitted = $this->doPassCheck( 'phpcode' );
|
89 |
+
}
|
90 |
+
if ( $bRequestIsPermitted && $opts->isOpt( 'block_leading_schema', 'Y' ) ) {
|
91 |
+
$bRequestIsPermitted = $this->doPassCheck( 'schema' );
|
92 |
+
}
|
93 |
+
if ( $bRequestIsPermitted && $opts->isOpt( 'block_aggressive', 'Y' ) ) {
|
94 |
+
$bRequestIsPermitted = $this->doPassCheck( 'aggressive' );
|
95 |
+
}
|
96 |
+
if ( $bRequestIsPermitted && $opts->isOpt( 'block_exe_file_uploads', 'Y' ) ) {
|
97 |
+
$bRequestIsPermitted = $this->doPassCheckBlockExeFileUploads();
|
98 |
+
}
|
99 |
+
return $bRequestIsPermitted;
|
100 |
+
}
|
101 |
+
|
102 |
+
protected function doPassCheckBlockExeFileUploads() :bool {
|
103 |
+
/** @var ModCon $mod */
|
104 |
+
$mod = $this->getMod();
|
105 |
+
|
106 |
+
$sKey = 'exefile';
|
107 |
+
$bFAIL = false;
|
108 |
+
if ( isset( $_FILES ) && !empty( $_FILES ) ) {
|
109 |
+
$aFileNames = [];
|
110 |
+
foreach ( $_FILES as $aFile ) {
|
111 |
+
if ( !empty( $aFile[ 'name' ] ) ) {
|
112 |
+
$aFileNames[] = $aFile[ 'name' ];
|
113 |
+
}
|
114 |
+
}
|
115 |
+
$aMatchTerms = $this->getFirewallPatterns( 'exefile' );
|
116 |
+
if ( isset( $aMatchTerms[ 'regex' ] ) && is_array( $aMatchTerms[ 'regex' ] ) ) {
|
117 |
+
|
118 |
+
$aMatchTerms[ 'regex' ] = array_map(
|
119 |
+
function ( $term ) {
|
120 |
+
return '/'.$term.'/i';
|
121 |
+
},
|
122 |
+
$aMatchTerms[ 'regex' ]
|
123 |
+
);
|
124 |
+
foreach ( $aMatchTerms[ 'regex' ] as $sTerm ) {
|
125 |
+
foreach ( $aFileNames as $sParam => $mValue ) {
|
126 |
+
if ( is_scalar( $mValue ) && preg_match( $sTerm, (string)$mValue ) ) {
|
127 |
+
$bFAIL = true;
|
128 |
+
break( 2 );
|
129 |
+
}
|
130 |
+
}
|
131 |
+
}
|
132 |
+
}
|
133 |
+
if ( $bFAIL ) {
|
134 |
+
$this->getCon()
|
135 |
+
->fireEvent(
|
136 |
+
'block_exefile',
|
137 |
+
[
|
138 |
+
'audit' => [
|
139 |
+
'blockresponse' => $mod->getBlockResponse(),
|
140 |
+
'blockkey' => $sKey,
|
141 |
+
]
|
142 |
+
]
|
143 |
+
|
144 |
+
);
|
145 |
+
}
|
146 |
+
}
|
147 |
+
return !$bFAIL;
|
148 |
+
}
|
149 |
+
|
150 |
+
/**
|
151 |
+
* Returns false when check fails - that is, it should be blocked by the firewall.
|
152 |
+
*
|
153 |
+
* @param string $sBlockKey
|
154 |
+
* @return bool
|
155 |
+
*/
|
156 |
+
private function doPassCheck( string $sBlockKey ) :bool {
|
157 |
+
/** @var ModCon $mod */
|
158 |
+
$mod = $this->getMod();
|
159 |
+
|
160 |
+
$aMatchTerms = $this->getFirewallPatterns( $sBlockKey );
|
161 |
+
$aParamValues = $this->getParamsToCheck();
|
162 |
+
if ( empty( $aMatchTerms ) || empty( $aParamValues ) ) {
|
163 |
+
return true;
|
164 |
+
}
|
165 |
+
|
166 |
+
$sParam = '';
|
167 |
+
$mValue = '';
|
168 |
+
|
169 |
+
$bFAIL = false;
|
170 |
+
if ( isset( $aMatchTerms[ 'simple' ] ) && is_array( $aMatchTerms[ 'simple' ] ) ) {
|
171 |
+
|
172 |
+
foreach ( $aMatchTerms[ 'simple' ] as $sTerm ) {
|
173 |
+
foreach ( $aParamValues as $sParam => $mValue ) {
|
174 |
+
if ( is_scalar( $mValue ) && ( stripos( (string)$mValue, $sTerm ) !== false ) ) {
|
175 |
+
$bFAIL = true;
|
176 |
+
break( 2 );
|
177 |
+
}
|
178 |
+
}
|
179 |
+
}
|
180 |
+
}
|
181 |
+
|
182 |
+
if ( !$bFAIL && isset( $aMatchTerms[ 'regex' ] ) && is_array( $aMatchTerms[ 'regex' ] ) ) {
|
183 |
+
$aMatchTerms[ 'regex' ] = array_map(
|
184 |
+
function ( $term ) {
|
185 |
+
return '/'.$term.'/i';
|
186 |
+
},
|
187 |
+
$aMatchTerms[ 'regex' ]
|
188 |
+
);
|
189 |
+
foreach ( $aMatchTerms[ 'regex' ] as $sTerm ) {
|
190 |
+
foreach ( $aParamValues as $sParam => $mValue ) {
|
191 |
+
if ( is_scalar( $mValue ) && preg_match( $sTerm, (string)$mValue ) ) {
|
192 |
+
$sParam = sanitize_text_field( $sParam );
|
193 |
+
$mValue = sanitize_text_field( $mValue );
|
194 |
+
$bFAIL = true;
|
195 |
+
break( 2 );
|
196 |
+
}
|
197 |
+
}
|
198 |
+
}
|
199 |
+
}
|
200 |
+
|
201 |
+
if ( $bFAIL ) {
|
202 |
+
$this->addToFirewallDieMessage( __( "Something in the URL, Form or Cookie data wasn't appropriate.", 'wp-simple-firewall' ) );
|
203 |
+
|
204 |
+
$this->aAuditBlockMessage = [
|
205 |
+
sprintf( __( 'Firewall Trigger: %s.', 'wp-simple-firewall' ), $this->getFirewallBlockKeyName( $sBlockKey ) ),
|
206 |
+
__( 'Page parameter failed firewall check.', 'wp-simple-firewall' ),
|
207 |
+
sprintf( __( 'The offending parameter was "%s" with a value of "%s".', 'wp-simple-firewall' ), $sParam, $mValue )
|
208 |
+
];
|
209 |
+
|
210 |
+
$this->getCon()
|
211 |
+
->fireEvent(
|
212 |
+
'blockparam_'.$sBlockKey,
|
213 |
+
[
|
214 |
+
'audit' => [
|
215 |
+
'param' => $sParam,
|
216 |
+
'val' => $mValue,
|
217 |
+
'blockresponse' => $mod->getBlockResponse(),
|
218 |
+
'blockkey' => $sBlockKey,
|
219 |
+
]
|
220 |
+
]
|
221 |
+
);
|
222 |
+
}
|
223 |
+
|
224 |
+
return !$bFAIL;
|
225 |
+
}
|
226 |
+
|
227 |
+
/**
|
228 |
+
* @param string $sKey
|
229 |
+
* @return array|null
|
230 |
+
*/
|
231 |
+
protected function getFirewallPatterns( $sKey = null ) {
|
232 |
+
if ( !isset( $this->aPatterns ) ) {
|
233 |
+
$this->aPatterns = $this->getOptions()->getDef( 'firewall_patterns' );
|
234 |
+
}
|
235 |
+
if ( !empty( $sKey ) ) {
|
236 |
+
return isset( $this->aPatterns[ $sKey ] ) ? $this->aPatterns[ $sKey ] : null;
|
237 |
+
}
|
238 |
+
return $this->aPatterns;
|
239 |
+
}
|
240 |
+
|
241 |
+
private function doPreFirewallBlock() {
|
242 |
+
/** @var 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 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->params ) ) {
|
307 |
+
return $this->params;
|
308 |
+
}
|
309 |
+
|
310 |
+
/** @var Options $opts */
|
311 |
+
$opts = $this->getOptions();
|
312 |
+
|
313 |
+
$this->params = $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->params ) as $sParamKey ) {
|
323 |
+
if ( preg_match( $sWhitelistParam, $sParamKey ) ) {
|
324 |
+
unset( $this->params[ $sParamKey ] );
|
325 |
+
}
|
326 |
+
}
|
327 |
+
}
|
328 |
+
elseif ( isset( $this->params[ $sWhitelistParam ] ) ) {
|
329 |
+
unset( $this->params[ $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->params ) ) {
|
336 |
+
return $this->params;
|
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->params = [];
|
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->params ) ) {
|
354 |
+
unset( $this->params[ $sWhitelistParam ] );
|
355 |
+
}
|
356 |
+
}
|
357 |
+
}
|
358 |
+
break;
|
359 |
+
}
|
360 |
+
}
|
361 |
+
|
362 |
+
return $this->params;
|
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 |
+
$success = false;
|
371 |
+
if ( !empty( $this->aAuditBlockMessage ) ) {
|
372 |
+
$sIp = Services::IP()->getRequestIp();
|
373 |
+
$message = 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 |
+
$success = $this->getMod()
|
393 |
+
->getEmailProcessor()
|
394 |
+
->sendEmailWithWrap(
|
395 |
+
$recipient,
|
396 |
+
__( 'Firewall Block Alert', 'wp-simple-firewall' ),
|
397 |
+
$message
|
398 |
+
);
|
399 |
+
}
|
400 |
+
return $success;
|
401 |
+
}
|
402 |
+
|
403 |
+
private function getFirewallBlockKeyName( string $blockKey ) :string {
|
404 |
+
switch ( $blockKey ) {
|
405 |
+
case 'dirtraversal':
|
406 |
+
$name = __( 'Directory Traversal', 'wp-simple-firewall' );
|
407 |
+
break;
|
408 |
+
case 'wpterms':
|
409 |
+
$name = __( 'WordPress Terms', 'wp-simple-firewall' );
|
410 |
+
break;
|
411 |
+
case 'fieldtruncation':
|
412 |
+
$name = __( 'Field Truncation', 'wp-simple-firewall' );
|
413 |
+
break;
|
414 |
+
case 'sqlqueries':
|
415 |
+
$name = __( 'SQL Queries', 'wp-simple-firewall' );
|
416 |
+
break;
|
417 |
+
case 'exefile':
|
418 |
+
$name = __( 'EXE File Uploads', 'wp-simple-firewall' );
|
419 |
+
break;
|
420 |
+
case 'schema':
|
421 |
+
$name = __( 'Leading Schema', 'wp-simple-firewall' );
|
422 |
+
break;
|
423 |
+
case 'phpcode':
|
424 |
+
$name = __( 'PHP Code', 'wp-simple-firewall' );
|
425 |
+
break;
|
426 |
+
case 'aggressive':
|
427 |
+
$name = __( 'Aggressive Rules', 'wp-simple-firewall' );
|
428 |
+
break;
|
429 |
+
default:
|
430 |
+
$name = __( 'Unknown Rules', 'wp-simple-firewall' );
|
431 |
+
break;
|
432 |
+
}
|
433 |
+
return $name;
|
434 |
+
}
|
435 |
+
}
|
src/lib/src/Modules/Firewall/Strings.php
CHANGED
@@ -183,7 +183,7 @@ class Strings extends Base\Strings {
|
|
183 |
* @return string[][]
|
184 |
*/
|
185 |
protected function getAuditMessages() :array {
|
186 |
-
/** @var
|
187 |
$mod = $this->getMod();
|
188 |
|
189 |
$aMsgs = [
|
183 |
* @return string[][]
|
184 |
*/
|
185 |
protected function getAuditMessages() :array {
|
186 |
+
/** @var ModCon $mod */
|
187 |
$mod = $this->getMod();
|
188 |
|
189 |
$aMsgs = [
|
src/lib/src/Modules/Firewall/UI.php
CHANGED
@@ -2,7 +2,7 @@
|
|
2 |
|
3 |
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Firewall;
|
4 |
|
5 |
-
use FernleafSystems\Wordpress\Plugin\Shield\Modules\
|
6 |
|
7 |
-
class UI extends
|
8 |
}
|
2 |
|
3 |
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Firewall;
|
4 |
|
5 |
+
use FernleafSystems\Wordpress\Plugin\Shield\Modules\BaseShield;
|
6 |
|
7 |
+
class UI extends BaseShield\UI {
|
8 |
}
|
src/lib/src/Modules/HackGuard/AjaxHandler.php
CHANGED
@@ -8,7 +8,7 @@ use FernleafSystems\Wordpress\Plugin\Shield\Modules\HackGuard\Lib\FileLocker;
|
|
8 |
use FernleafSystems\Wordpress\Plugin\Shield\Modules\HackGuard\Scan;
|
9 |
use FernleafSystems\Wordpress\Services\Services;
|
10 |
|
11 |
-
class AjaxHandler extends Shield\Modules\
|
12 |
|
13 |
protected function processAjaxAction( string $action ) :array {
|
14 |
|
@@ -62,12 +62,9 @@ class AjaxHandler extends Shield\Modules\Base\AjaxHandlerShield {
|
|
62 |
return $aResponse;
|
63 |
}
|
64 |
|
65 |
-
|
66 |
-
|
67 |
-
|
68 |
-
private function ajaxExec_BuildTableScan() {
|
69 |
-
/** @var \ICWP_WPSF_FeatureHandler_HackProtect $oMod */
|
70 |
-
$oMod = $this->getMod();
|
71 |
|
72 |
$sScanSlug = Services::Request()->post( 'fScan' );
|
73 |
switch ( $sScanSlug ) {
|
@@ -109,8 +106,8 @@ class AjaxHandler extends Shield\Modules\Base\AjaxHandlerShield {
|
|
109 |
}
|
110 |
else {
|
111 |
$sHtml = $oTableBuilder
|
112 |
-
->setMod( $
|
113 |
-
->setDbHandler( $
|
114 |
->render();
|
115 |
}
|
116 |
|
@@ -120,13 +117,10 @@ class AjaxHandler extends Shield\Modules\Base\AjaxHandlerShield {
|
|
120 |
];
|
121 |
}
|
122 |
|
123 |
-
|
124 |
-
|
125 |
-
|
126 |
-
|
127 |
-
/** @var \ICWP_WPSF_FeatureHandler_HackProtect $oMod */
|
128 |
-
$oMod = $this->getMod();
|
129 |
-
$oFLCon = $oMod->getFileLocker();
|
130 |
$FS = Services::WpFs();
|
131 |
|
132 |
$nRID = Services::Request()->post( 'rid' );
|
@@ -187,7 +181,7 @@ class AjaxHandler extends Shield\Modules\Base\AjaxHandlerShield {
|
|
187 |
$aData[ 'vars' ][ 'change_detected_at' ] = $oCarb->setTimestamp( $oLock->detected_at )->diffForHumans();
|
188 |
$aData[ 'vars' ][ 'file_size_locked' ] = Shield\Utilities\Tool\FormatBytes::Format( strlen(
|
189 |
( new FileLocker\Ops\ReadOriginalFileContent() )
|
190 |
-
->setMod( $
|
191 |
->run( $oLock )
|
192 |
), 3 );
|
193 |
$aData[ 'vars' ][ 'file_size_modified' ] = $FS->exists( $oLock->file ) ?
|
@@ -212,10 +206,7 @@ class AjaxHandler extends Shield\Modules\Base\AjaxHandlerShield {
|
|
212 |
];
|
213 |
}
|
214 |
|
215 |
-
|
216 |
-
* @return array
|
217 |
-
*/
|
218 |
-
private function ajaxExec_FileLockerFileAction() {
|
219 |
$oReq = Services::Request();
|
220 |
$bSuccess = false;
|
221 |
|
@@ -242,45 +233,42 @@ class AjaxHandler extends Shield\Modules\Base\AjaxHandlerShield {
|
|
242 |
];
|
243 |
}
|
244 |
|
245 |
-
|
246 |
-
|
247 |
-
|
248 |
-
|
249 |
-
/** @var \ICWP_WPSF_FeatureHandler_HackProtect $oMod */
|
250 |
-
$oMod = $this->getMod();
|
251 |
-
$oReq = Services::Request();
|
252 |
|
253 |
-
$bReinstall = (bool)$
|
254 |
-
$bActivate = (bool)$
|
255 |
-
$
|
256 |
|
257 |
if ( $bReinstall ) {
|
258 |
-
/** @var Scan\Controller\Ptg $
|
259 |
-
$
|
260 |
-
$bActivate = $
|
261 |
}
|
262 |
|
263 |
if ( $bActivate ) {
|
264 |
-
Services::WpPlugins()->activate( $
|
265 |
}
|
266 |
|
267 |
return [ 'success' => true ];
|
268 |
}
|
269 |
|
270 |
/**
|
271 |
-
* @param string $
|
272 |
* @param bool $bIsBulkAction
|
273 |
* @return array
|
274 |
*/
|
275 |
-
private function ajaxExec_ScanItemAction( $
|
276 |
-
/** @var
|
277 |
-
$
|
278 |
|
279 |
-
$
|
280 |
|
281 |
-
if ( $
|
282 |
// A special case since this action is handled using Javascript
|
283 |
-
$
|
284 |
$sMessage = __( 'File download has started.', 'wp-simple-firewall' );
|
285 |
}
|
286 |
else {
|
@@ -301,20 +289,20 @@ class AjaxHandler extends Shield\Modules\Base\AjaxHandlerShield {
|
|
301 |
$aScanSlugs = [];
|
302 |
$aSuccessfulItems = [];
|
303 |
foreach ( $aItemIdsToProcess as $nId ) {
|
304 |
-
/** @var Shield\Databases\Scanner\EntryVO $
|
305 |
-
$
|
306 |
-
|
307 |
-
|
308 |
-
if ( $
|
309 |
-
$aScanSlugs[] = $
|
310 |
-
if ( $
|
311 |
$aSuccessfulItems[] = $nId;
|
312 |
}
|
313 |
}
|
314 |
}
|
315 |
|
316 |
if ( count( $aSuccessfulItems ) === count( $aItemIdsToProcess ) ) {
|
317 |
-
$
|
318 |
$sMessage = __( 'Action successful.' );
|
319 |
}
|
320 |
else {
|
@@ -322,12 +310,12 @@ class AjaxHandler extends Shield\Modules\Base\AjaxHandlerShield {
|
|
322 |
}
|
323 |
|
324 |
// We don't rescan for ignores.
|
325 |
-
if ( in_array( $
|
326 |
$sMessage .= ' '.__( 'Reloading', 'wp-simple-firewall' ).' ...';
|
327 |
}
|
328 |
else {
|
329 |
// rescan
|
330 |
-
$
|
331 |
$sMessage .= ' '.__( 'Rescanning', 'wp-simple-firewall' ).' ...';
|
332 |
}
|
333 |
}
|
@@ -338,24 +326,21 @@ class AjaxHandler extends Shield\Modules\Base\AjaxHandlerShield {
|
|
338 |
}
|
339 |
|
340 |
return [
|
341 |
-
'success' => $
|
342 |
-
'page_reload' => !in_array( $
|
343 |
'message' => $sMessage,
|
344 |
];
|
345 |
}
|
346 |
|
347 |
-
|
348 |
-
|
349 |
-
|
350 |
-
private function ajaxExec_CheckScans() {
|
351 |
-
/** @var \ICWP_WPSF_FeatureHandler_HackProtect $oMod */
|
352 |
-
$oMod = $this->getMod();
|
353 |
/** @var Strings $oStrings */
|
354 |
-
$oStrings = $
|
355 |
/** @var Shield\Databases\ScanQueue\Select $oSel */
|
356 |
-
$oSel = $
|
357 |
|
358 |
-
$oQueCon = $
|
359 |
$sCurrent = $oSel->getCurrentScan();
|
360 |
$bHasCurrent = !empty( $sCurrent );
|
361 |
if ( $bHasCurrent ) {
|
@@ -369,7 +354,7 @@ class AjaxHandler extends Shield\Modules\Base\AjaxHandlerShield {
|
|
369 |
'success' => true,
|
370 |
'running' => $oQueCon->getScansRunningStates(),
|
371 |
'vars' => [
|
372 |
-
'progress_html' => $
|
373 |
'/wpadmin_pages/insights/scans/modal/progress_snippet.twig',
|
374 |
[
|
375 |
'current_scan' => __( 'Current Scan', 'wp-simple-firewall' ),
|
@@ -387,55 +372,58 @@ class AjaxHandler extends Shield\Modules\Base\AjaxHandlerShield {
|
|
387 |
];
|
388 |
}
|
389 |
|
390 |
-
|
391 |
-
|
392 |
-
*/
|
393 |
-
private function ajaxExec_StartScans() {
|
394 |
-
/** @var \ICWP_WPSF_FeatureHandler_HackProtect $mod */
|
395 |
$mod = $this->getMod();
|
396 |
-
$
|
397 |
-
$
|
398 |
-
$
|
399 |
-
$
|
|
|
|
|
400 |
|
401 |
-
$
|
402 |
|
403 |
-
if ( !empty( $
|
404 |
-
$
|
405 |
|
406 |
$aUiTrack = $mod->getUiTrack();
|
407 |
-
$aUiTrack[ 'selected_scans' ] = $
|
408 |
$mod->setUiTrack( $aUiTrack );
|
409 |
|
410 |
-
$
|
411 |
-
foreach ( $
|
412 |
-
|
413 |
-
|
|
|
414 |
|
415 |
-
|
416 |
|
417 |
-
|
418 |
-
|
419 |
-
|
420 |
-
|
421 |
-
|
422 |
-
|
423 |
|
424 |
-
|
425 |
-
|
426 |
-
|
|
|
|
|
|
|
427 |
}
|
428 |
}
|
429 |
-
$
|
430 |
}
|
431 |
|
432 |
-
$
|
433 |
|
434 |
return [
|
435 |
-
'success' => $
|
436 |
-
'scans_running' => $
|
437 |
-
'page_reload' => $
|
438 |
-
'message' => $
|
439 |
];
|
440 |
}
|
441 |
}
|
8 |
use FernleafSystems\Wordpress\Plugin\Shield\Modules\HackGuard\Scan;
|
9 |
use FernleafSystems\Wordpress\Services\Services;
|
10 |
|
11 |
+
class AjaxHandler extends Shield\Modules\BaseShield\AjaxHandler {
|
12 |
|
13 |
protected function processAjaxAction( string $action ) :array {
|
14 |
|
62 |
return $aResponse;
|
63 |
}
|
64 |
|
65 |
+
private function ajaxExec_BuildTableScan() :array {
|
66 |
+
/** @var ModCon $mod */
|
67 |
+
$mod = $this->getMod();
|
|
|
|
|
|
|
68 |
|
69 |
$sScanSlug = Services::Request()->post( 'fScan' );
|
70 |
switch ( $sScanSlug ) {
|
106 |
}
|
107 |
else {
|
108 |
$sHtml = $oTableBuilder
|
109 |
+
->setMod( $mod )
|
110 |
+
->setDbHandler( $mod->getDbHandler_ScanResults() )
|
111 |
->render();
|
112 |
}
|
113 |
|
117 |
];
|
118 |
}
|
119 |
|
120 |
+
private function ajaxExec_FileLockerShowDiff() :array {
|
121 |
+
/** @var ModCon $mod */
|
122 |
+
$mod = $this->getMod();
|
123 |
+
$oFLCon = $mod->getFileLocker();
|
|
|
|
|
|
|
124 |
$FS = Services::WpFs();
|
125 |
|
126 |
$nRID = Services::Request()->post( 'rid' );
|
181 |
$aData[ 'vars' ][ 'change_detected_at' ] = $oCarb->setTimestamp( $oLock->detected_at )->diffForHumans();
|
182 |
$aData[ 'vars' ][ 'file_size_locked' ] = Shield\Utilities\Tool\FormatBytes::Format( strlen(
|
183 |
( new FileLocker\Ops\ReadOriginalFileContent() )
|
184 |
+
->setMod( $mod )
|
185 |
->run( $oLock )
|
186 |
), 3 );
|
187 |
$aData[ 'vars' ][ 'file_size_modified' ] = $FS->exists( $oLock->file ) ?
|
206 |
];
|
207 |
}
|
208 |
|
209 |
+
private function ajaxExec_FileLockerFileAction() :array {
|
|
|
|
|
|
|
210 |
$oReq = Services::Request();
|
211 |
$bSuccess = false;
|
212 |
|
233 |
];
|
234 |
}
|
235 |
|
236 |
+
private function ajaxExec_PluginReinstall() :array {
|
237 |
+
/** @var ModCon $mod */
|
238 |
+
$mod = $this->getMod();
|
239 |
+
$req = Services::Request();
|
|
|
|
|
|
|
240 |
|
241 |
+
$bReinstall = (bool)$req->post( 'reinstall' );
|
242 |
+
$bActivate = (bool)$req->post( 'activate' );
|
243 |
+
$file = sanitize_text_field( wp_unslash( $req->post( 'file' ) ) );
|
244 |
|
245 |
if ( $bReinstall ) {
|
246 |
+
/** @var Scan\Controller\Ptg $scan */
|
247 |
+
$scan = $mod->getScansCon()->getScanCon( 'ptg' );
|
248 |
+
$bActivate = $scan->actionPluginReinstall( $file );
|
249 |
}
|
250 |
|
251 |
if ( $bActivate ) {
|
252 |
+
Services::WpPlugins()->activate( $file );
|
253 |
}
|
254 |
|
255 |
return [ 'success' => true ];
|
256 |
}
|
257 |
|
258 |
/**
|
259 |
+
* @param string $action
|
260 |
* @param bool $bIsBulkAction
|
261 |
* @return array
|
262 |
*/
|
263 |
+
private function ajaxExec_ScanItemAction( $action, $bIsBulkAction = false ) :array {
|
264 |
+
/** @var ModCon $mod */
|
265 |
+
$mod = $this->getMod();
|
266 |
|
267 |
+
$success = false;
|
268 |
|
269 |
+
if ( $action == 'download' ) {
|
270 |
// A special case since this action is handled using Javascript
|
271 |
+
$success = true;
|
272 |
$sMessage = __( 'File download has started.', 'wp-simple-firewall' );
|
273 |
}
|
274 |
else {
|
289 |
$aScanSlugs = [];
|
290 |
$aSuccessfulItems = [];
|
291 |
foreach ( $aItemIdsToProcess as $nId ) {
|
292 |
+
/** @var Shield\Databases\Scanner\EntryVO $entry */
|
293 |
+
$entry = $mod->getDbHandler_ScanResults()
|
294 |
+
->getQuerySelector()
|
295 |
+
->byId( $nId );
|
296 |
+
if ( $entry instanceof Shield\Databases\Scanner\EntryVO ) {
|
297 |
+
$aScanSlugs[] = $entry->scan;
|
298 |
+
if ( $mod->getScanCon( $entry->scan )->executeItemAction( $nId, $action ) ) {
|
299 |
$aSuccessfulItems[] = $nId;
|
300 |
}
|
301 |
}
|
302 |
}
|
303 |
|
304 |
if ( count( $aSuccessfulItems ) === count( $aItemIdsToProcess ) ) {
|
305 |
+
$success = true;
|
306 |
$sMessage = __( 'Action successful.' );
|
307 |
}
|
308 |
else {
|
310 |
}
|
311 |
|
312 |
// We don't rescan for ignores.
|
313 |
+
if ( in_array( $action, [ 'ignore' ] ) ) {
|
314 |
$sMessage .= ' '.__( 'Reloading', 'wp-simple-firewall' ).' ...';
|
315 |
}
|
316 |
else {
|
317 |
// rescan
|
318 |
+
$mod->getScanQueueController()->startScans( $aScanSlugs );
|
319 |
$sMessage .= ' '.__( 'Rescanning', 'wp-simple-firewall' ).' ...';
|
320 |
}
|
321 |
}
|
326 |
}
|
327 |
|
328 |
return [
|
329 |
+
'success' => $success,
|
330 |
+
'page_reload' => !in_array( $action, [ 'download' ] ),
|
331 |
'message' => $sMessage,
|
332 |
];
|
333 |
}
|
334 |
|
335 |
+
private function ajaxExec_CheckScans() :array {
|
336 |
+
/** @var ModCon $mod */
|
337 |
+
$mod = $this->getMod();
|
|
|
|
|
|
|
338 |
/** @var Strings $oStrings */
|
339 |
+
$oStrings = $mod->getStrings();
|
340 |
/** @var Shield\Databases\ScanQueue\Select $oSel */
|
341 |
+
$oSel = $mod->getDbHandler_ScanQueue()->getQuerySelector();
|
342 |
|
343 |
+
$oQueCon = $mod->getScanQueueController();
|
344 |
$sCurrent = $oSel->getCurrentScan();
|
345 |
$bHasCurrent = !empty( $sCurrent );
|
346 |
if ( $bHasCurrent ) {
|
354 |
'success' => true,
|
355 |
'running' => $oQueCon->getScansRunningStates(),
|
356 |
'vars' => [
|
357 |
+
'progress_html' => $mod->renderTemplate(
|
358 |
'/wpadmin_pages/insights/scans/modal/progress_snippet.twig',
|
359 |
[
|
360 |
'current_scan' => __( 'Current Scan', 'wp-simple-firewall' ),
|
372 |
];
|
373 |
}
|
374 |
|
375 |
+
private function ajaxExec_StartScans() :array {
|
376 |
+
/** @var ModCon $mod */
|
|
|
|
|
|
|
377 |
$mod = $this->getMod();
|
378 |
+
/** @var Options $opts */
|
379 |
+
$opts = $this->getOptions();
|
380 |
+
$success = false;
|
381 |
+
$reloadPage = false;
|
382 |
+
$msg = __( 'No scans were selected', 'wp-simple-firewall' );
|
383 |
+
$formParams = $this->getAjaxFormParams();
|
384 |
|
385 |
+
$scanCon = $mod->getScanQueueController();
|
386 |
|
387 |
+
if ( !empty( $formParams ) ) {
|
388 |
+
$selected = array_keys( $formParams );
|
389 |
|
390 |
$aUiTrack = $mod->getUiTrack();
|
391 |
+
$aUiTrack[ 'selected_scans' ] = array_intersect( array_keys( $formParams ), $opts->getScanSlugs() );
|
392 |
$mod->setUiTrack( $aUiTrack );
|
393 |
|
394 |
+
$toScan = [];
|
395 |
+
foreach ( $selected as $slug ) {
|
396 |
+
try {
|
397 |
+
$thisScanCon = $mod->getScanCon( $slug );
|
398 |
+
if ( $thisScanCon->isScanningAvailable() ) {
|
399 |
|
400 |
+
$toScan[] = $slug;
|
401 |
|
402 |
+
if ( isset( $formParams[ 'opt_clear_ignore' ] ) ) {
|
403 |
+
$thisScanCon->resetIgnoreStatus();
|
404 |
+
}
|
405 |
+
if ( isset( $formParams[ 'opt_clear_notification' ] ) ) {
|
406 |
+
$thisScanCon->resetNotifiedStatus();
|
407 |
+
}
|
408 |
|
409 |
+
$success = true;
|
410 |
+
$reloadPage = true;
|
411 |
+
$msg = __( 'Scans started.', 'wp-simple-firewall' ).' '.__( 'Please wait, as this will take a few moments.', 'wp-simple-firewall' );
|
412 |
+
}
|
413 |
+
}
|
414 |
+
catch ( \Exception $e ) {
|
415 |
}
|
416 |
}
|
417 |
+
$scanCon->startScans( $toScan );
|
418 |
}
|
419 |
|
420 |
+
$isScanRunning = $scanCon->hasRunningScans();
|
421 |
|
422 |
return [
|
423 |
+
'success' => $success,
|
424 |
+
'scans_running' => $isScanRunning,
|
425 |
+
'page_reload' => $reloadPage && !$isScanRunning,
|
426 |
+
'message' => $msg,
|
427 |
];
|
428 |
}
|
429 |
}
|
src/lib/src/Modules/HackGuard/Insights/OverviewCards.php
CHANGED
@@ -8,7 +8,7 @@ use FernleafSystems\Wordpress\Plugin\Shield\Modules\HackGuard;
|
|
8 |
class OverviewCards extends Shield\Modules\Base\Insights\OverviewCards {
|
9 |
|
10 |
public function build() :array {
|
11 |
-
/** @var \
|
12 |
$mod = $this->getMod();
|
13 |
/** @var HackGuard\Options $opts */
|
14 |
$opts = $this->getOptions();
|
@@ -51,7 +51,7 @@ class OverviewCards extends Shield\Modules\Base\Insights\OverviewCards {
|
|
51 |
}
|
52 |
|
53 |
private function getCardsForWcf() :array {
|
54 |
-
/** @var \
|
55 |
$mod = $this->getMod();
|
56 |
/** @var HackGuard\Options $opts */
|
57 |
$opts = $this->getOptions();
|
@@ -95,7 +95,7 @@ class OverviewCards extends Shield\Modules\Base\Insights\OverviewCards {
|
|
95 |
}
|
96 |
|
97 |
private function getCardsForUfc() :array {
|
98 |
-
/** @var \
|
99 |
$mod = $this->getMod();
|
100 |
/** @var HackGuard\Options $opts */
|
101 |
$opts = $this->getOptions();
|
@@ -137,7 +137,7 @@ class OverviewCards extends Shield\Modules\Base\Insights\OverviewCards {
|
|
137 |
}
|
138 |
|
139 |
private function getCardsForPtg() :array {
|
140 |
-
/** @var \
|
141 |
$mod = $this->getMod();
|
142 |
$scanCon = $mod->getScanCon( HackGuard\Scan\Controller\Ptg::SCAN_SLUG );
|
143 |
|
@@ -168,7 +168,7 @@ class OverviewCards extends Shield\Modules\Base\Insights\OverviewCards {
|
|
168 |
}
|
169 |
|
170 |
private function getCardsForMal() :array {
|
171 |
-
/** @var \
|
172 |
$mod = $this->getMod();
|
173 |
$scanCon = $mod->getScanCon( HackGuard\Scan\Controller\Mal::SCAN_SLUG );
|
174 |
|
@@ -198,7 +198,7 @@ class OverviewCards extends Shield\Modules\Base\Insights\OverviewCards {
|
|
198 |
}
|
199 |
|
200 |
private function getCardsForApc() :array {
|
201 |
-
/** @var \
|
202 |
$mod = $this->getMod();
|
203 |
$scanCon = $mod->getScanCon( HackGuard\Scan\Controller\Apc::SCAN_SLUG );
|
204 |
|
@@ -227,7 +227,7 @@ class OverviewCards extends Shield\Modules\Base\Insights\OverviewCards {
|
|
227 |
}
|
228 |
|
229 |
private function getCardsForWpv() :array {
|
230 |
-
/** @var \
|
231 |
$mod = $this->getMod();
|
232 |
$scanCon = $mod->getScanCon( HackGuard\Scan\Controller\Wpv::SCAN_SLUG );
|
233 |
|
8 |
class OverviewCards extends Shield\Modules\Base\Insights\OverviewCards {
|
9 |
|
10 |
public function build() :array {
|
11 |
+
/** @var HackGuard\ModCon $mod */
|
12 |
$mod = $this->getMod();
|
13 |
/** @var HackGuard\Options $opts */
|
14 |
$opts = $this->getOptions();
|
51 |
}
|
52 |
|
53 |
private function getCardsForWcf() :array {
|
54 |
+
/** @var HackGuard\ModCon $mod */
|
55 |
$mod = $this->getMod();
|
56 |
/** @var HackGuard\Options $opts */
|
57 |
$opts = $this->getOptions();
|
95 |
}
|
96 |
|
97 |
private function getCardsForUfc() :array {
|
98 |
+
/** @var HackGuard\ModCon $mod */
|
99 |
$mod = $this->getMod();
|
100 |
/** @var HackGuard\Options $opts */
|
101 |
$opts = $this->getOptions();
|
137 |
}
|
138 |
|
139 |
private function getCardsForPtg() :array {
|
140 |
+
/** @var HackGuard\ModCon $mod */
|
141 |
$mod = $this->getMod();
|
142 |
$scanCon = $mod->getScanCon( HackGuard\Scan\Controller\Ptg::SCAN_SLUG );
|
143 |
|
168 |
}
|
169 |
|
170 |
private function getCardsForMal() :array {
|
171 |
+
/** @var HackGuard\ModCon $mod */
|
172 |
$mod = $this->getMod();
|
173 |
$scanCon = $mod->getScanCon( HackGuard\Scan\Controller\Mal::SCAN_SLUG );
|
174 |
|
198 |
}
|
199 |
|
200 |
private function getCardsForApc() :array {
|
201 |
+
/** @var HackGuard\ModCon $mod */
|
202 |
$mod = $this->getMod();
|
203 |
$scanCon = $mod->getScanCon( HackGuard\Scan\Controller\Apc::SCAN_SLUG );
|
204 |
|
227 |
}
|
228 |
|
229 |
private function getCardsForWpv() :array {
|
230 |
+
/** @var HackGuard\ModCon $mod */
|
231 |
$mod = $this->getMod();
|
232 |
$scanCon = $mod->getScanCon( HackGuard\Scan\Controller\Wpv::SCAN_SLUG );
|
233 |
|
src/lib/src/Modules/HackGuard/Lib/FileLocker/FileLockerController.php
CHANGED
@@ -18,9 +18,9 @@ class FileLockerController {
|
|
18 |
* @return bool
|
19 |
*/
|
20 |
public function isEnabled() {
|
21 |
-
/** @var HackGuard\Options $
|
22 |
-
$
|
23 |
-
return ( count( $
|
24 |
&& $this->getCon()
|
25 |
->getModule_Plugin()
|
26 |
->getShieldNetApiController()
|
@@ -31,10 +31,9 @@ class FileLockerController {
|
|
31 |
* @return bool
|
32 |
*/
|
33 |
protected function canRun() {
|
34 |
-
/** @var \
|
35 |
-
$
|
36 |
-
return $this->isEnabled()
|
37 |
-
&& $oMod->getDbHandler_FileLocker()->isReady();
|
38 |
}
|
39 |
|
40 |
protected function run() {
|
@@ -80,18 +79,18 @@ class FileLockerController {
|
|
80 |
}
|
81 |
|
82 |
/**
|
83 |
-
* @param FileLocker\EntryVO $
|
84 |
* @return string[]
|
85 |
*/
|
86 |
-
public function createFileDownloadLinks( $
|
87 |
-
/** @var \
|
88 |
-
$
|
89 |
$aLinks = [];
|
90 |
foreach ( [ 'original', 'current' ] as $sType ) {
|
91 |
-
$aActionNonce = $
|
92 |
-
$aActionNonce[ 'rid' ] = $
|
93 |
$aActionNonce[ 'rand' ] = rand();
|
94 |
-
$aLinks[ $sType ] = add_query_arg( $aActionNonce, $
|
95 |
}
|
96 |
return $aLinks;
|
97 |
}
|
@@ -124,13 +123,13 @@ class FileLockerController {
|
|
124 |
}
|
125 |
|
126 |
public function deleteAllLocks() {
|
127 |
-
/** @var \
|
128 |
$mod = $this->getMod();
|
129 |
$mod->getDbHandler_FileLocker()->tableDelete( true );
|
130 |
}
|
131 |
|
132 |
public function purge() {
|
133 |
-
/** @var \
|
134 |
$mod = $this->getMod();
|
135 |
$mod->getDbHandler_FileLocker()->tableDelete();
|
136 |
}
|
@@ -221,13 +220,4 @@ class FileLockerController {
|
|
221 |
$oFile->max_paths = $nMaxPaths;
|
222 |
return $oFile;
|
223 |
}
|
224 |
-
|
225 |
-
/**
|
226 |
-
* @return FileLocker\Handler
|
227 |
-
*/
|
228 |
-
private function getDbHandler() {
|
229 |
-
/** @var \ICWP_WPSF_FeatureHandler_HackProtect $oMod */
|
230 |
-
$oMod = $this->getMod();
|
231 |
-
return $oMod->getDbHandler_FileLocker();
|
232 |
-
}
|
233 |
}
|
18 |
* @return bool
|
19 |
*/
|
20 |
public function isEnabled() {
|
21 |
+
/** @var HackGuard\Options $opts */
|
22 |
+
$opts = $this->getOptions();
|
23 |
+
return ( count( $opts->getFilesToLock() ) > 0 )
|
24 |
&& $this->getCon()
|
25 |
->getModule_Plugin()
|
26 |
->getShieldNetApiController()
|
31 |
* @return bool
|
32 |
*/
|
33 |
protected function canRun() {
|
34 |
+
/** @var HackGuard\ModCon $mod */
|
35 |
+
$mod = $this->getMod();
|
36 |
+
return $this->isEnabled() && $mod->getDbHandler_FileLocker()->isReady();
|
|
|
37 |
}
|
38 |
|
39 |
protected function run() {
|
79 |
}
|
80 |
|
81 |
/**
|
82 |
+
* @param FileLocker\EntryVO $VO
|
83 |
* @return string[]
|
84 |
*/
|
85 |
+
public function createFileDownloadLinks( $VO ) {
|
86 |
+
/** @var HackGuard\ModCon $mod */
|
87 |
+
$mod = $this->getMod();
|
88 |
$aLinks = [];
|
89 |
foreach ( [ 'original', 'current' ] as $sType ) {
|
90 |
+
$aActionNonce = $mod->getNonceActionData( 'filelocker_download_'.$sType );
|
91 |
+
$aActionNonce[ 'rid' ] = $VO->id;
|
92 |
$aActionNonce[ 'rand' ] = rand();
|
93 |
+
$aLinks[ $sType ] = add_query_arg( $aActionNonce, $mod->getUrl_AdminPage() );
|
94 |
}
|
95 |
return $aLinks;
|
96 |
}
|
123 |
}
|
124 |
|
125 |
public function deleteAllLocks() {
|
126 |
+
/** @var HackGuard\ModCon $mod */
|
127 |
$mod = $this->getMod();
|
128 |
$mod->getDbHandler_FileLocker()->tableDelete( true );
|
129 |
}
|
130 |
|
131 |
public function purge() {
|
132 |
+
/** @var HackGuard\ModCon $mod */
|
133 |
$mod = $this->getMod();
|
134 |
$mod->getDbHandler_FileLocker()->tableDelete();
|
135 |
}
|
220 |
$oFile->max_paths = $nMaxPaths;
|
221 |
return $oFile;
|
222 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
223 |
}
|
src/lib/src/Modules/HackGuard/Lib/FileLocker/Ops/Accept.php
CHANGED
@@ -3,6 +3,7 @@
|
|
3 |
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\HackGuard\Lib\FileLocker\Ops;
|
4 |
|
5 |
use FernleafSystems\Wordpress\Plugin\Shield\Databases\FileLocker;
|
|
|
6 |
use FernleafSystems\Wordpress\Services\Services;
|
7 |
|
8 |
/**
|
@@ -17,16 +18,16 @@ class Accept extends BaseOps {
|
|
17 |
* @throws \ErrorException
|
18 |
*/
|
19 |
public function run( $oLock ) {
|
20 |
-
/** @var
|
21 |
-
$
|
22 |
|
23 |
$aPublicKey = $this->getPublicKey();
|
24 |
$sRawContent = ( new BuildEncryptedFilePayload() )
|
25 |
-
->setMod( $
|
26 |
->build( $oLock->file, reset( $aPublicKey ) );
|
27 |
|
28 |
/** @var FileLocker\Update $oUpdater */
|
29 |
-
$oUpdater = $
|
30 |
$bSuccess = $oUpdater->updateEntry( $oLock, [
|
31 |
'hash_original' => hash_file( 'sha1', $oLock->file ),
|
32 |
'content' => base64_encode( $sRawContent ),
|
3 |
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\HackGuard\Lib\FileLocker\Ops;
|
4 |
|
5 |
use FernleafSystems\Wordpress\Plugin\Shield\Databases\FileLocker;
|
6 |
+
use FernleafSystems\Wordpress\Plugin\Shield\Modules\HackGuard\ModCon;
|
7 |
use FernleafSystems\Wordpress\Services\Services;
|
8 |
|
9 |
/**
|
18 |
* @throws \ErrorException
|
19 |
*/
|
20 |
public function run( $oLock ) {
|
21 |
+
/** @var ModCon $mod */
|
22 |
+
$mod = $this->getMod();
|
23 |
|
24 |
$aPublicKey = $this->getPublicKey();
|
25 |
$sRawContent = ( new BuildEncryptedFilePayload() )
|
26 |
+
->setMod( $mod )
|
27 |
->build( $oLock->file, reset( $aPublicKey ) );
|
28 |
|
29 |
/** @var FileLocker\Update $oUpdater */
|
30 |
+
$oUpdater = $mod->getDbHandler_FileLocker()->getQueryUpdater();
|
31 |
$bSuccess = $oUpdater->updateEntry( $oLock, [
|
32 |
'hash_original' => hash_file( 'sha1', $oLock->file ),
|
33 |
'content' => base64_encode( $sRawContent ),
|
src/lib/src/Modules/HackGuard/Lib/FileLocker/Ops/AssessLocks.php
CHANGED
@@ -3,6 +3,7 @@
|
|
3 |
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\HackGuard\Lib\FileLocker\Ops;
|
4 |
|
5 |
use FernleafSystems\Wordpress\Plugin\Shield\Databases\FileLocker;
|
|
|
6 |
use FernleafSystems\Wordpress\Services\Utilities\File\Compare\CompareHash;
|
7 |
|
8 |
class AssessLocks extends BaseOps {
|
@@ -11,10 +12,10 @@ class AssessLocks extends BaseOps {
|
|
11 |
* @return int[]
|
12 |
*/
|
13 |
public function run() {
|
14 |
-
/** @var
|
15 |
-
$
|
16 |
/** @var FileLocker\Update $oUpd */
|
17 |
-
$oUpd = $
|
18 |
|
19 |
$this->removeDuplicates();
|
20 |
|
@@ -47,11 +48,11 @@ class AssessLocks extends BaseOps {
|
|
47 |
$aPaths = [];
|
48 |
foreach ( $this->getFileLocks() as $oLock ) {
|
49 |
if ( in_array( $oLock->file, $aPaths ) ) {
|
50 |
-
/** @var
|
51 |
-
$
|
52 |
-
$
|
53 |
-
|
54 |
-
|
55 |
}
|
56 |
else {
|
57 |
$aPaths[] = $oLock->file;
|
3 |
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\HackGuard\Lib\FileLocker\Ops;
|
4 |
|
5 |
use FernleafSystems\Wordpress\Plugin\Shield\Databases\FileLocker;
|
6 |
+
use FernleafSystems\Wordpress\Plugin\Shield\Modules\HackGuard\ModCon;
|
7 |
use FernleafSystems\Wordpress\Services\Utilities\File\Compare\CompareHash;
|
8 |
|
9 |
class AssessLocks extends BaseOps {
|
12 |
* @return int[]
|
13 |
*/
|
14 |
public function run() {
|
15 |
+
/** @var ModCon $mod */
|
16 |
+
$mod = $this->getMod();
|
17 |
/** @var FileLocker\Update $oUpd */
|
18 |
+
$oUpd = $mod->getDbHandler_FileLocker()->getQueryUpdater();
|
19 |
|
20 |
$this->removeDuplicates();
|
21 |
|
48 |
$aPaths = [];
|
49 |
foreach ( $this->getFileLocks() as $oLock ) {
|
50 |
if ( in_array( $oLock->file, $aPaths ) ) {
|
51 |
+
/** @var ModCon $mod */
|
52 |
+
$mod = $this->getMod();
|
53 |
+
$mod->getDbHandler_FileLocker()
|
54 |
+
->getQueryDeleter()
|
55 |
+
->deleteById( $oLock->id );
|
56 |
}
|
57 |
else {
|
58 |
$aPaths[] = $oLock->file;
|
src/lib/src/Modules/HackGuard/Lib/FileLocker/Ops/CreateFileLocks.php
CHANGED
@@ -3,6 +3,7 @@
|
|
3 |
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\HackGuard\Lib\FileLocker\Ops;
|
4 |
|
5 |
use FernleafSystems\Wordpress\Plugin\Shield\Databases\FileLocker;
|
|
|
6 |
use FernleafSystems\Wordpress\Services\Services;
|
7 |
|
8 |
/**
|
@@ -31,26 +32,26 @@ class CreateFileLocks extends BaseOps {
|
|
31 |
}
|
32 |
|
33 |
/**
|
34 |
-
* @param string $
|
35 |
* @throws \Exception
|
36 |
*/
|
37 |
-
private function processPath( $
|
38 |
-
/** @var
|
39 |
-
$
|
40 |
|
41 |
-
if ( Services::WpFs()->isFile( $
|
42 |
$oEntry = new FileLocker\EntryVO();
|
43 |
-
$oEntry->file = $
|
44 |
-
$oEntry->hash_original = hash_file( 'sha1', $
|
45 |
|
46 |
$aPublicKey = $this->getPublicKey();
|
47 |
$oEntry->public_key_id = key( $aPublicKey );
|
48 |
$oEntry->content = ( new BuildEncryptedFilePayload() )
|
49 |
-
->setMod( $
|
50 |
-
->build( $
|
51 |
|
52 |
/** @var FileLocker\Insert $oInserter */
|
53 |
-
$oInserter = $
|
54 |
$oInserter->insert( $oEntry );
|
55 |
|
56 |
$this->clearFileLocksCache();
|
3 |
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\HackGuard\Lib\FileLocker\Ops;
|
4 |
|
5 |
use FernleafSystems\Wordpress\Plugin\Shield\Databases\FileLocker;
|
6 |
+
use FernleafSystems\Wordpress\Plugin\Shield\Modules\HackGuard\ModCon;
|
7 |
use FernleafSystems\Wordpress\Services\Services;
|
8 |
|
9 |
/**
|
32 |
}
|
33 |
|
34 |
/**
|
35 |
+
* @param string $path
|
36 |
* @throws \Exception
|
37 |
*/
|
38 |
+
private function processPath( $path ) {
|
39 |
+
/** @var ModCon $mod */
|
40 |
+
$mod = $this->getMod();
|
41 |
|
42 |
+
if ( Services::WpFs()->isFile( $path ) ) {
|
43 |
$oEntry = new FileLocker\EntryVO();
|
44 |
+
$oEntry->file = $path;
|
45 |
+
$oEntry->hash_original = hash_file( 'sha1', $path );
|
46 |
|
47 |
$aPublicKey = $this->getPublicKey();
|
48 |
$oEntry->public_key_id = key( $aPublicKey );
|
49 |
$oEntry->content = ( new BuildEncryptedFilePayload() )
|
50 |
+
->setMod( $mod )
|
51 |
+
->build( $path, reset( $aPublicKey ) );
|
52 |
|
53 |
/** @var FileLocker\Insert $oInserter */
|
54 |
+
$oInserter = $mod->getDbHandler_FileLocker()->getQueryInserter();
|
55 |
$oInserter->insert( $oEntry );
|
56 |
|
57 |
$this->clearFileLocksCache();
|
src/lib/src/Modules/HackGuard/Lib/FileLocker/Ops/DeleteFileLock.php
CHANGED
@@ -3,6 +3,7 @@
|
|
3 |
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\HackGuard\Lib\FileLocker\Ops;
|
4 |
|
5 |
use FernleafSystems\Wordpress\Plugin\Shield\Databases\FileLocker;
|
|
|
6 |
|
7 |
/**
|
8 |
* Class DeleteFileLock
|
@@ -15,13 +16,13 @@ class DeleteFileLock extends BaseOps {
|
|
15 |
* @return bool
|
16 |
*/
|
17 |
public function delete( $oLock = null ) {
|
18 |
-
/** @var
|
19 |
-
$
|
20 |
if ( empty( $oLock ) ) {
|
21 |
$oLock = $this->findLockRecordForFile();
|
22 |
}
|
23 |
$bSuccess = $oLock instanceof FileLocker\EntryVO
|
24 |
-
&& $
|
25 |
->getQueryDeleter()
|
26 |
->deleteEntry( $oLock );
|
27 |
if ( $bSuccess ) {
|
3 |
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\HackGuard\Lib\FileLocker\Ops;
|
4 |
|
5 |
use FernleafSystems\Wordpress\Plugin\Shield\Databases\FileLocker;
|
6 |
+
use FernleafSystems\Wordpress\Plugin\Shield\Modules\HackGuard\ModCon;
|
7 |
|
8 |
/**
|
9 |
* Class DeleteFileLock
|
16 |
* @return bool
|
17 |
*/
|
18 |
public function delete( $oLock = null ) {
|
19 |
+
/** @var ModCon $mod */
|
20 |
+
$mod = $this->getMod();
|
21 |
if ( empty( $oLock ) ) {
|
22 |
$oLock = $this->findLockRecordForFile();
|
23 |
}
|
24 |
$bSuccess = $oLock instanceof FileLocker\EntryVO
|
25 |
+
&& $mod->getDbHandler_FileLocker()
|
26 |
->getQueryDeleter()
|
27 |
->deleteEntry( $oLock );
|
28 |
if ( $bSuccess ) {
|
src/lib/src/Modules/HackGuard/Lib/FileLocker/Ops/LoadFileLocks.php
CHANGED
@@ -3,6 +3,7 @@
|
|
3 |
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\HackGuard\Lib\FileLocker\Ops;
|
4 |
|
5 |
use FernleafSystems\Wordpress\Plugin\Shield\Databases\FileLocker;
|
|
|
6 |
use FernleafSystems\Wordpress\Plugin\Shield\Modules\ModConsumer;
|
7 |
|
8 |
/**
|
@@ -23,12 +24,12 @@ class LoadFileLocks {
|
|
23 |
*/
|
24 |
public function loadLocks() {
|
25 |
if ( is_null( self::$aFileLockRecords ) ) {
|
26 |
-
/** @var
|
27 |
-
$
|
28 |
|
29 |
self::$aFileLockRecords = [];
|
30 |
-
if ( $
|
31 |
-
$aAll = $
|
32 |
if ( is_array( $aAll ) ) {
|
33 |
foreach ( $aAll as $oLock ) {
|
34 |
self::$aFileLockRecords[ $oLock->id ] = $oLock;
|
@@ -58,7 +59,6 @@ class LoadFileLocks {
|
|
58 |
return array_filter(
|
59 |
$this->withProblems(),
|
60 |
function ( $oLock ) {
|
61 |
-
/** @var FileLocker\EntryVO $oLock */
|
62 |
return $oLock->notified_at == 0;
|
63 |
}
|
64 |
);
|
3 |
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\HackGuard\Lib\FileLocker\Ops;
|
4 |
|
5 |
use FernleafSystems\Wordpress\Plugin\Shield\Databases\FileLocker;
|
6 |
+
use FernleafSystems\Wordpress\Plugin\Shield\Modules\HackGuard\ModCon;
|
7 |
use FernleafSystems\Wordpress\Plugin\Shield\Modules\ModConsumer;
|
8 |
|
9 |
/**
|
24 |
*/
|
25 |
public function loadLocks() {
|
26 |
if ( is_null( self::$aFileLockRecords ) ) {
|
27 |
+
/** @var ModCon $mod */
|
28 |
+
$mod = $this->getMod();
|
29 |
|
30 |
self::$aFileLockRecords = [];
|
31 |
+
if ( $mod->getFileLocker()->isEnabled() ) {
|
32 |
+
$aAll = $mod->getDbHandler_FileLocker()->getQuerySelector()->all();
|
33 |
if ( is_array( $aAll ) ) {
|
34 |
foreach ( $aAll as $oLock ) {
|
35 |
self::$aFileLockRecords[ $oLock->id ] = $oLock;
|
59 |
return array_filter(
|
60 |
$this->withProblems(),
|
61 |
function ( $oLock ) {
|
|
|
62 |
return $oLock->notified_at == 0;
|
63 |
}
|
64 |
);
|
src/lib/src/Modules/HackGuard/Lib/FileLocker/Ops/PerformAction.php
CHANGED
@@ -3,6 +3,7 @@
|
|
3 |
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\HackGuard\Lib\FileLocker\Ops;
|
4 |
|
5 |
use FernleafSystems\Wordpress\Plugin\Shield\Databases;
|
|
|
6 |
|
7 |
/**
|
8 |
* Class PerformAction
|
@@ -17,8 +18,8 @@ class PerformAction extends BaseOps {
|
|
17 |
* @throws \Exception
|
18 |
*/
|
19 |
public function run( $nLockID, $sAction ) {
|
20 |
-
/** @var
|
21 |
-
$
|
22 |
|
23 |
if ( !in_array( $sAction, [ 'accept', 'restore', 'diff' ] ) ) {
|
24 |
throw new \Exception( __( 'Not a supported file lock action.', 'wp-simple-firewall' ) );
|
@@ -26,7 +27,7 @@ class PerformAction extends BaseOps {
|
|
26 |
if ( !is_numeric( $nLockID ) ) {
|
27 |
throw new \Exception( __( 'Please select a valid file.', 'wp-simple-firewall' ) );
|
28 |
}
|
29 |
-
$oLock = $
|
30 |
->getQuerySelector()
|
31 |
->byId( (int)$nLockID );
|
32 |
if ( !$oLock instanceof Databases\FileLocker\EntryVO ) {
|
3 |
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\HackGuard\Lib\FileLocker\Ops;
|
4 |
|
5 |
use FernleafSystems\Wordpress\Plugin\Shield\Databases;
|
6 |
+
use FernleafSystems\Wordpress\Plugin\Shield\Modules\HackGuard\ModCon;
|
7 |
|
8 |
/**
|
9 |
* Class PerformAction
|
18 |
* @throws \Exception
|
19 |
*/
|
20 |
public function run( $nLockID, $sAction ) {
|
21 |
+
/** @var ModCon $mod */
|
22 |
+
$mod = $this->getMod();
|
23 |
|
24 |
if ( !in_array( $sAction, [ 'accept', 'restore', 'diff' ] ) ) {
|
25 |
throw new \Exception( __( 'Not a supported file lock action.', 'wp-simple-firewall' ) );
|
27 |
if ( !is_numeric( $nLockID ) ) {
|
28 |
throw new \Exception( __( 'Please select a valid file.', 'wp-simple-firewall' ) );
|
29 |
}
|
30 |
+
$oLock = $mod->getDbHandler_FileLocker()
|
31 |
->getQuerySelector()
|
32 |
->byId( (int)$nLockID );
|
33 |
if ( !$oLock instanceof Databases\FileLocker\EntryVO ) {
|
src/lib/src/Modules/HackGuard/Lib/FileLocker/Ops/Restore.php
CHANGED
@@ -3,6 +3,7 @@
|
|
3 |
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\HackGuard\Lib\FileLocker\Ops;
|
4 |
|
5 |
use FernleafSystems\Wordpress\Plugin\Shield\Databases;
|
|
|
6 |
use FernleafSystems\Wordpress\Services\Services;
|
7 |
|
8 |
/**
|
@@ -23,10 +24,10 @@ class Restore extends BaseOps {
|
|
23 |
->run( $oRecord )
|
24 |
);
|
25 |
if ( $bReverted ) {
|
26 |
-
/** @var
|
27 |
-
$
|
28 |
/** @var Databases\FileLocker\Update $oUpd */
|
29 |
-
$oUpd = $
|
30 |
$oUpd->markReverted( $oRecord );
|
31 |
$this->clearFileLocksCache();
|
32 |
}
|
3 |
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\HackGuard\Lib\FileLocker\Ops;
|
4 |
|
5 |
use FernleafSystems\Wordpress\Plugin\Shield\Databases;
|
6 |
+
use FernleafSystems\Wordpress\Plugin\Shield\Modules\HackGuard\ModCon;
|
7 |
use FernleafSystems\Wordpress\Services\Services;
|
8 |
|
9 |
/**
|
24 |
->run( $oRecord )
|
25 |
);
|
26 |
if ( $bReverted ) {
|
27 |
+
/** @var ModCon $mod */
|
28 |
+
$mod = $this->getMod();
|
29 |
/** @var Databases\FileLocker\Update $oUpd */
|
30 |
+
$oUpd = $mod->getDbHandler_FileLocker()->getQueryUpdater();
|
31 |
$oUpd->markReverted( $oRecord );
|
32 |
$this->clearFileLocksCache();
|
33 |
}
|
src/lib/src/Modules/HackGuard/Lib/Reports/FileLockerAlerts.php
CHANGED
@@ -14,8 +14,8 @@ class FileLockerAlerts extends BaseReporter {
|
|
14 |
public function build() {
|
15 |
$aAlerts = [];
|
16 |
|
17 |
-
/** @var \
|
18 |
-
$
|
19 |
|
20 |
$oLockOps = ( new HackGuard\Lib\FileLocker\Ops\LoadFileLocks() )
|
21 |
->setMod( $this->getMod() );
|
@@ -26,7 +26,7 @@ class FileLockerAlerts extends BaseReporter {
|
|
26 |
'/components/reports/mod/hack_protect/alert_filelocker.twig',
|
27 |
[
|
28 |
'vars' => [
|
29 |
-
'count' => $
|
30 |
],
|
31 |
'strings' => [
|
32 |
'title' => __( 'File Locker Changes Detected', 'wp-simple-firewall' ),
|
@@ -50,10 +50,10 @@ class FileLockerAlerts extends BaseReporter {
|
|
50 |
* @param FileLocker\EntryVO[] $aNotNotified
|
51 |
*/
|
52 |
private function markAlertsAsNotified( $aNotNotified ) {
|
53 |
-
/** @var \
|
54 |
-
$
|
55 |
/** @var FileLocker\Update $oUpdater */
|
56 |
-
$oUpdater = $
|
57 |
foreach ( $aNotNotified as $oEntry ) {
|
58 |
$oUpdater->markNotified( $oEntry );
|
59 |
}
|
14 |
public function build() {
|
15 |
$aAlerts = [];
|
16 |
|
17 |
+
/** @var HackGuard\ModCon $mod */
|
18 |
+
$mod = $this->getMod();
|
19 |
|
20 |
$oLockOps = ( new HackGuard\Lib\FileLocker\Ops\LoadFileLocks() )
|
21 |
->setMod( $this->getMod() );
|
26 |
'/components/reports/mod/hack_protect/alert_filelocker.twig',
|
27 |
[
|
28 |
'vars' => [
|
29 |
+
'count' => $mod->getFileLocker()->countProblems()
|
30 |
],
|
31 |
'strings' => [
|
32 |
'title' => __( 'File Locker Changes Detected', 'wp-simple-firewall' ),
|
50 |
* @param FileLocker\EntryVO[] $aNotNotified
|
51 |
*/
|
52 |
private function markAlertsAsNotified( $aNotNotified ) {
|
53 |
+
/** @var HackGuard\ModCon $mod */
|
54 |
+
$mod = $this->getMod();
|
55 |
/** @var FileLocker\Update $oUpdater */
|
56 |
+
$oUpdater = $mod->getDbHandler_FileLocker()->getQueryUpdater();
|
57 |
foreach ( $aNotNotified as $oEntry ) {
|
58 |
$oUpdater->markNotified( $oEntry );
|
59 |
}
|
src/lib/src/Modules/HackGuard/Lib/Reports/Query/ScanCounts.php
ADDED
@@ -0,0 +1,88 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php declare( strict_types=1 );
|
2 |
+
|
3 |
+
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\HackGuard\Lib\Reports\Query;
|
4 |
+
|
5 |
+
use FernleafSystems\Wordpress\Plugin\Shield\Databases\Scanner;
|
6 |
+
use FernleafSystems\Wordpress\Plugin\Shield\Modules\HackGuard;
|
7 |
+
use FernleafSystems\Wordpress\Plugin\Shield\Modules\ModConsumer;
|
8 |
+
use FernleafSystems\Wordpress\Services\Services;
|
9 |
+
|
10 |
+
class ScanCounts {
|
11 |
+
|
12 |
+
use ModConsumer;
|
13 |
+
|
14 |
+
/**
|
15 |
+
* @var int
|
16 |
+
*/
|
17 |
+
public $from;
|
18 |
+
|
19 |
+
/**
|
20 |
+
* @var int
|
21 |
+
*/
|
22 |
+
public $to;
|
23 |
+
|
24 |
+
/**
|
25 |
+
* @var bool
|
26 |
+
*/
|
27 |
+
public $ignored = false;
|
28 |
+
|
29 |
+
/**
|
30 |
+
* @var bool
|
31 |
+
*/
|
32 |
+
public $notified = false;
|
33 |
+
|
34 |
+
public function __construct( $from = null, $to = null ) {
|
35 |
+
$this->from = is_int( $from ) ? $from : 0;
|
36 |
+
$this->to = is_int( $to ) ? $to : Services::Request()->ts();
|
37 |
+
}
|
38 |
+
|
39 |
+
/**
|
40 |
+
* @return int[] - key is scan slug
|
41 |
+
*/
|
42 |
+
public function all() :array {
|
43 |
+
return array_merge(
|
44 |
+
$this->standard(),
|
45 |
+
$this->filelocker()
|
46 |
+
);
|
47 |
+
}
|
48 |
+
|
49 |
+
/**
|
50 |
+
* @return int[]
|
51 |
+
*/
|
52 |
+
public function filelocker() :array {
|
53 |
+
return [
|
54 |
+
'filelocker' => count( ( new HackGuard\Lib\FileLocker\Ops\LoadFileLocks() )
|
55 |
+
->setMod( $this->getMod() )
|
56 |
+
->withProblemsNotNotified() )
|
57 |
+
];
|
58 |
+
}
|
59 |
+
|
60 |
+
/**
|
61 |
+
* @return int[] - key is scan slug
|
62 |
+
*/
|
63 |
+
public function standard() :array {
|
64 |
+
/** @var HackGuard\ModCon $mod */
|
65 |
+
$mod = $this->getMod();
|
66 |
+
/** @var HackGuard\Options $opts */
|
67 |
+
$opts = $this->getOptions();
|
68 |
+
/** @var Scanner\Select $qSel */
|
69 |
+
$qSel = $mod->getDbHandler_ScanResults()->getQuerySelector();
|
70 |
+
|
71 |
+
$counts = [];
|
72 |
+
|
73 |
+
foreach ( $opts->getScanSlugs() as $slug ) {
|
74 |
+
$qSel->filterByScan( $slug )
|
75 |
+
->filterByCreatedAt( $this->from, '>=' )
|
76 |
+
->filterByCreatedAt( $this->to, '<=' );
|
77 |
+
if ( isset( $this->ignored ) ) {
|
78 |
+
$this->ignored ? $qSel->filterByIgnored() : $qSel->filterByNotIgnored();
|
79 |
+
}
|
80 |
+
if ( isset( $this->notified ) ) {
|
81 |
+
$this->notified ? $qSel->filterByNotified() : $qSel->filterByNotNotified();
|
82 |
+
}
|
83 |
+
$counts[ $slug ] = $qSel->count();
|
84 |
+
}
|
85 |
+
|
86 |
+
return $counts;
|
87 |
+
}
|
88 |
+
}
|
src/lib/src/Modules/HackGuard/Lib/Reports/ScanAlerts.php
CHANGED
@@ -13,25 +13,30 @@ class ScanAlerts extends BaseReporter {
|
|
13 |
* @inheritDoc
|
14 |
*/
|
15 |
public function build() {
|
16 |
-
$
|
17 |
|
18 |
-
/** @var HackGuard\Strings $
|
19 |
-
$
|
20 |
-
$aScanNames = $oStrings->getScanNames();
|
21 |
|
22 |
-
$
|
23 |
-
|
24 |
-
|
25 |
-
|
26 |
-
|
27 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
28 |
];
|
29 |
}
|
30 |
-
$
|
31 |
'/components/reports/mod/hack_protect/alert_scanresults.twig',
|
32 |
[
|
33 |
'vars' => [
|
34 |
-
'scan_counts' => $
|
35 |
],
|
36 |
'strings' => [
|
37 |
'title' => __( 'New Scan Results', 'wp-simple-firewall' ),
|
@@ -46,14 +51,14 @@ class ScanAlerts extends BaseReporter {
|
|
46 |
$this->markAlertsAsNotified();
|
47 |
}
|
48 |
|
49 |
-
return $
|
50 |
}
|
51 |
|
52 |
private function markAlertsAsNotified() {
|
53 |
-
/** @var \
|
54 |
-
$
|
55 |
/** @var Scanner\Update $oUpdater */
|
56 |
-
$oUpdater = $
|
57 |
$oUpdater
|
58 |
->setUpdateWheres( [
|
59 |
'ignored_at' => 0,
|
@@ -64,35 +69,4 @@ class ScanAlerts extends BaseReporter {
|
|
64 |
] )
|
65 |
->query();
|
66 |
}
|
67 |
-
|
68 |
-
/**
|
69 |
-
* TODO: As class that can be used by ShieldCentral also
|
70 |
-
* @return int[] - key is scan slug
|
71 |
-
*/
|
72 |
-
private function countItemsForEachScan() {
|
73 |
-
/** @var \ICWP_WPSF_FeatureHandler_HackProtect $oMod */
|
74 |
-
$oMod = $this->getMod();
|
75 |
-
/** @var HackGuard\Options $oOpts */
|
76 |
-
$oOpts = $this->getOptions();
|
77 |
-
/** @var Scanner\Select $oSel */
|
78 |
-
$oSel = $oMod->getDbHandler_ScanResults()->getQuerySelector();
|
79 |
-
|
80 |
-
$aCounts = [];
|
81 |
-
|
82 |
-
$oRep = $this->getReport();
|
83 |
-
|
84 |
-
foreach ( $oOpts->getScanSlugs() as $sScanSlug ) {
|
85 |
-
$oSel->filterByScan( $sScanSlug )
|
86 |
-
->filterByNotNotified()
|
87 |
-
->filterByNotIgnored();
|
88 |
-
if ( !is_null( $oRep->interval_start_at ) ) {
|
89 |
-
$oSel->filterByCreatedAt( $oRep->interval_start_at, '>' );
|
90 |
-
}
|
91 |
-
if ( !is_null( $oRep->interval_end_at ) ) {
|
92 |
-
$oSel->filterByCreatedAt( $oRep->interval_end_at, '<' );
|
93 |
-
}
|
94 |
-
$aCounts[ $sScanSlug ] = $oSel->count();
|
95 |
-
}
|
96 |
-
return $aCounts;
|
97 |
-
}
|
98 |
}
|
13 |
* @inheritDoc
|
14 |
*/
|
15 |
public function build() {
|
16 |
+
$alerts = [];
|
17 |
|
18 |
+
/** @var HackGuard\Strings $strings */
|
19 |
+
$strings = $this->getMod()->getStrings();
|
|
|
20 |
|
21 |
+
$rep = $this->getReport();
|
22 |
+
$scanCounts = array_filter(
|
23 |
+
( new Query\ScanCounts( $rep->interval_start_at, $rep->interval_end_at ) )
|
24 |
+
->setMod( $this->getMod() )
|
25 |
+
->standard()
|
26 |
+
);
|
27 |
+
|
28 |
+
if ( !empty( $scanCounts ) ) {
|
29 |
+
foreach ( $scanCounts as $slug => $count ) {
|
30 |
+
$scanCounts[ $slug ] = [
|
31 |
+
'count' => $count,
|
32 |
+
'name' => $strings->getScanNames()[ $slug ],
|
33 |
];
|
34 |
}
|
35 |
+
$alerts[] = $this->getMod()->renderTemplate(
|
36 |
'/components/reports/mod/hack_protect/alert_scanresults.twig',
|
37 |
[
|
38 |
'vars' => [
|
39 |
+
'scan_counts' => $scanCounts
|
40 |
],
|
41 |
'strings' => [
|
42 |
'title' => __( 'New Scan Results', 'wp-simple-firewall' ),
|
51 |
$this->markAlertsAsNotified();
|
52 |
}
|
53 |
|
54 |
+
return $alerts;
|
55 |
}
|
56 |
|
57 |
private function markAlertsAsNotified() {
|
58 |
+
/** @var HackGuard\ModCon $mod */
|
59 |
+
$mod = $this->getMod();
|
60 |
/** @var Scanner\Update $oUpdater */
|
61 |
+
$oUpdater = $mod->getDbHandler_ScanResults()->getQueryUpdater();
|
62 |
$oUpdater
|
63 |
->setUpdateWheres( [
|
64 |
'ignored_at' => 0,
|
69 |
] )
|
70 |
->query();
|
71 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
72 |
}
|
src/lib/src/Modules/HackGuard/Lib/Snapshots/StoreAction/BaseAction.php
CHANGED
@@ -3,6 +3,7 @@
|
|
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\ModConsumer;
|
7 |
use FernleafSystems\Wordpress\Services\Core\VOs\WpPluginVo;
|
8 |
use FernleafSystems\Wordpress\Services\Core\VOs\WpThemeVo;
|
@@ -40,9 +41,9 @@ class BaseAction {
|
|
40 |
* @throws \Exception
|
41 |
*/
|
42 |
protected function getNewStore() {
|
43 |
-
/** @var
|
44 |
-
$
|
45 |
return ( new Snapshots\Store( $this->getAsset() ) )
|
46 |
-
->setWorkingDir( $
|
47 |
}
|
48 |
}
|
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\Plugin\Shield\Modules\ModConsumer;
|
8 |
use FernleafSystems\Wordpress\Services\Core\VOs\WpPluginVo;
|
9 |
use FernleafSystems\Wordpress\Services\Core\VOs\WpThemeVo;
|
41 |
* @throws \Exception
|
42 |
*/
|
43 |
protected function getNewStore() {
|
44 |
+
/** @var ModCon $mod */
|
45 |
+
$mod = $this->getMod();
|
46 |
return ( new Snapshots\Store( $this->getAsset() ) )
|
47 |
+
->setWorkingDir( $mod->getPtgSnapsBaseDir() );
|
48 |
}
|
49 |
}
|
src/lib/src/Modules/HackGuard/Lib/Snapshots/StoreAction/Build.php
CHANGED
@@ -3,6 +3,7 @@
|
|
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 |
|
@@ -31,10 +32,8 @@ class Build extends BaseAction {
|
|
31 |
}
|
32 |
|
33 |
if ( !empty( $aHashes ) ) {
|
34 |
-
/** @var \ICWP_WPSF_FeatureHandler_HackProtect $oMod */
|
35 |
-
$oMod = $this->getMod();
|
36 |
$oStore = ( new CreateNew() )
|
37 |
-
->setMod( $
|
38 |
->setAsset( $oAsset )
|
39 |
->run();
|
40 |
$oStore->setSnapData( $aHashes )
|
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 |
|
32 |
}
|
33 |
|
34 |
if ( !empty( $aHashes ) ) {
|
|
|
|
|
35 |
$oStore = ( new CreateNew() )
|
36 |
+
->setMod( $this->getMod() )
|
37 |
->setAsset( $oAsset )
|
38 |
->run();
|
39 |
$oStore->setSnapData( $aHashes )
|
src/lib/src/Modules/HackGuard/Lib/Snapshots/StoreAction/CleanAll.php
CHANGED
@@ -2,19 +2,20 @@
|
|
2 |
|
3 |
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\HackGuard\Lib\Snapshots\StoreAction;
|
4 |
|
|
|
5 |
use FernleafSystems\Wordpress\Plugin\Shield\Scans\Helpers\StandardDirectoryIterator;
|
6 |
use FernleafSystems\Wordpress\Services\Services;
|
7 |
|
8 |
class CleanAll extends BaseBulk {
|
9 |
|
10 |
public function run() {
|
11 |
-
/** @var
|
12 |
-
$
|
13 |
try {
|
14 |
$nBoundary = Services::Request()
|
15 |
->carbon()
|
16 |
->subDay()->timestamp;
|
17 |
-
$oDirIt = StandardDirectoryIterator::create( $
|
18 |
foreach ( $oDirIt as $oFile ) {
|
19 |
/** @var \SplFileInfo $oFile */
|
20 |
if ( $nBoundary > $oFile->getMTime() ) {
|
2 |
|
3 |
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\HackGuard\Lib\Snapshots\StoreAction;
|
4 |
|
5 |
+
use FernleafSystems\Wordpress\Plugin\Shield\Modules\HackGuard\ModCon;
|
6 |
use FernleafSystems\Wordpress\Plugin\Shield\Scans\Helpers\StandardDirectoryIterator;
|
7 |
use FernleafSystems\Wordpress\Services\Services;
|
8 |
|
9 |
class CleanAll extends BaseBulk {
|
10 |
|
11 |
public function run() {
|
12 |
+
/** @var ModCon $mod */
|
13 |
+
$mod = $this->getMod();
|
14 |
try {
|
15 |
$nBoundary = Services::Request()
|
16 |
->carbon()
|
17 |
->subDay()->timestamp;
|
18 |
+
$oDirIt = StandardDirectoryIterator::create( $mod->getPtgSnapsBaseDir() );
|
19 |
foreach ( $oDirIt as $oFile ) {
|
20 |
/** @var \SplFileInfo $oFile */
|
21 |
if ( $nBoundary > $oFile->getMTime() ) {
|
src/lib/src/Modules/HackGuard/Lib/Snapshots/StoreAction/DeleteAll.php
CHANGED
@@ -2,13 +2,14 @@
|
|
2 |
|
3 |
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\HackGuard\Lib\Snapshots\StoreAction;
|
4 |
|
|
|
5 |
use FernleafSystems\Wordpress\Services\Services;
|
6 |
|
7 |
class DeleteAll extends BaseBulk {
|
8 |
|
9 |
public function run() {
|
10 |
-
/** @var
|
11 |
-
$
|
12 |
-
Services::WpFs()->deleteDir( $
|
13 |
}
|
14 |
}
|
2 |
|
3 |
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\HackGuard\Lib\Snapshots\StoreAction;
|
4 |
|
5 |
+
use FernleafSystems\Wordpress\Plugin\Shield\Modules\HackGuard\ModCon;
|
6 |
use FernleafSystems\Wordpress\Services\Services;
|
7 |
|
8 |
class DeleteAll extends BaseBulk {
|
9 |
|
10 |
public function run() {
|
11 |
+
/** @var ModCon $mod */
|
12 |
+
$mod = $this->getMod();
|
13 |
+
Services::WpFs()->deleteDir( $mod->getPtgSnapsBaseDir() );
|
14 |
}
|
15 |
}
|
src/lib/src/Modules/HackGuard/ModCon.php
ADDED
@@ -0,0 +1,253 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php declare( strict_types=1 );
|
2 |
+
|
3 |
+
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\HackGuard;
|
4 |
+
|
5 |
+
use FernleafSystems\Wordpress\Plugin\Shield;
|
6 |
+
use FernleafSystems\Wordpress\Plugin\Shield\Databases;
|
7 |
+
use FernleafSystems\Wordpress\Plugin\Shield\Modules\BaseShield;
|
8 |
+
use FernleafSystems\Wordpress\Services\Services;
|
9 |
+
|
10 |
+
class ModCon extends BaseShield\ModCon {
|
11 |
+
|
12 |
+
/**
|
13 |
+
* @var Scan\ScansController
|
14 |
+
*/
|
15 |
+
private $scanCon;
|
16 |
+
|
17 |
+
/**
|
18 |
+
* @var Scan\Queue\Controller
|
19 |
+
*/
|
20 |
+
private $scanQueueCon;
|
21 |
+
|
22 |
+
/**
|
23 |
+
* @var Scan\Controller\Base[]
|
24 |
+
*/
|
25 |
+
private $scansCons;
|
26 |
+
|
27 |
+
/**
|
28 |
+
* @var Lib\FileLocker\FileLockerController
|
29 |
+
*/
|
30 |
+
private $oFileLocker;
|
31 |
+
|
32 |
+
protected function doPostConstruction() {
|
33 |
+
$this->setCustomCronSchedules();
|
34 |
+
}
|
35 |
+
|
36 |
+
public function onWpInit() {
|
37 |
+
parent::onWpInit();
|
38 |
+
$this->getScanQueueController();
|
39 |
+
}
|
40 |
+
|
41 |
+
public function getFileLocker() :Lib\FileLocker\FileLockerController {
|
42 |
+
if ( !isset( $this->oFileLocker ) ) {
|
43 |
+
$this->oFileLocker = ( new Lib\FileLocker\FileLockerController() )
|
44 |
+
->setMod( $this );
|
45 |
+
}
|
46 |
+
return $this->oFileLocker;
|
47 |
+
}
|
48 |
+
|
49 |
+
public function getScansCon() :Scan\ScansController {
|
50 |
+
if ( !isset( $this->scanCon ) ) {
|
51 |
+
$this->scanCon = ( new Scan\ScansController() )
|
52 |
+
->setMod( $this );
|
53 |
+
}
|
54 |
+
return $this->scanCon;
|
55 |
+
}
|
56 |
+
|
57 |
+
public function getScanQueueController() :Scan\Queue\Controller {
|
58 |
+
if ( !isset( $this->scanQueueCon ) ) {
|
59 |
+
$this->scanQueueCon = ( new Scan\Queue\Controller() )
|
60 |
+
->setMod( $this );
|
61 |
+
}
|
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 empty( $this->scansCons[ $slug ] ) ?
|
80 |
+
$this->getScansCon()->getScanCon( $slug ) : $this->scansCons[ $slug ];
|
81 |
+
}
|
82 |
+
|
83 |
+
public function getMainWpData() :array {
|
84 |
+
$issues = ( new Lib\Reports\Query\ScanCounts() )->setMod( $this );
|
85 |
+
$issues->notified = null;
|
86 |
+
return array_merge( parent::getMainWpData(), [
|
87 |
+
'scan_issues' => array_filter( $issues->all() )
|
88 |
+
] );
|
89 |
+
}
|
90 |
+
|
91 |
+
protected function handleModAction( string $action ) {
|
92 |
+
switch ( $action ) {
|
93 |
+
case 'scan_file_download':
|
94 |
+
( new Lib\Utility\FileDownloadHandler() )
|
95 |
+
->setDbHandler( $this->getDbHandler_ScanResults() )
|
96 |
+
->downloadByItemId( (int)Services::Request()->query( 'rid', 0 ) );
|
97 |
+
break;
|
98 |
+
case 'filelocker_download_original':
|
99 |
+
case 'filelocker_download_current':
|
100 |
+
$this->getFileLocker()->handleFileDownloadRequest();
|
101 |
+
break;
|
102 |
+
default:
|
103 |
+
break;
|
104 |
+
}
|
105 |
+
}
|
106 |
+
|
107 |
+
protected function preProcessOptions() {
|
108 |
+
/** @var Options $opts */
|
109 |
+
$opts = $this->getOptions();
|
110 |
+
|
111 |
+
$this->cleanFileExclusions();
|
112 |
+
|
113 |
+
if ( $opts->isOptChanged( 'scan_frequency' ) ) {
|
114 |
+
$this->getScansCon()->deleteCron();
|
115 |
+
}
|
116 |
+
|
117 |
+
if ( count( $opts->getFilesToLock() ) === 0 || !$this->getCon()
|
118 |
+
->getModule_Plugin()
|
119 |
+
->getShieldNetApiController()
|
120 |
+
->canHandshake() ) {
|
121 |
+
$opts->setOpt( 'file_locker', [] );
|
122 |
+
$this->getFileLocker()->purge();
|
123 |
+
}
|
124 |
+
|
125 |
+
$lockFiles = $opts->getFilesToLock();
|
126 |
+
if ( in_array( 'root_webconfig', $lockFiles ) && !Services::Data()->isWindows() ) {
|
127 |
+
unset( $lockFiles[ array_search( 'root_webconfig', $lockFiles ) ] );
|
128 |
+
$opts->setOpt( 'file_locker', $lockFiles );
|
129 |
+
}
|
130 |
+
|
131 |
+
foreach ( $this->getScansCon()->getAllScanCons() as $con ) {
|
132 |
+
if ( !$con->isEnabled() ) {
|
133 |
+
$con->purge();
|
134 |
+
}
|
135 |
+
}
|
136 |
+
}
|
137 |
+
|
138 |
+
/**
|
139 |
+
* @return $this
|
140 |
+
*/
|
141 |
+
protected function setCustomCronSchedules() {
|
142 |
+
/** @var Options $opts */
|
143 |
+
$opts = $this->getOptions();
|
144 |
+
$freq = $opts->getScanFrequency();
|
145 |
+
Services::WpCron()
|
146 |
+
->addNewSchedule(
|
147 |
+
$this->prefix( sprintf( 'per-day-%s', $freq ) ),
|
148 |
+
[
|
149 |
+
'interval' => DAY_IN_SECONDS/$freq,
|
150 |
+
'display' => sprintf( __( '%s per day', 'wp-simple-firewall' ), $freq )
|
151 |
+
]
|
152 |
+
);
|
153 |
+
return $this;
|
154 |
+
}
|
155 |
+
|
156 |
+
protected function cleanFileExclusions() {
|
157 |
+
/** @var Options $opts */
|
158 |
+
$opts = $this->getOptions();
|
159 |
+
$aExclusions = [];
|
160 |
+
|
161 |
+
$aToClean = $opts->getOpt( 'ufc_exclusions', [] );
|
162 |
+
if ( is_array( $aToClean ) ) {
|
163 |
+
foreach ( $aToClean as $nKey => $sExclusion ) {
|
164 |
+
$sExclusion = wp_normalize_path( trim( $sExclusion ) );
|
165 |
+
|
166 |
+
if ( preg_match( '/^#(.+)#$/', $sExclusion, $aMatches ) ) { // it's regex
|
167 |
+
// ignore it
|
168 |
+
}
|
169 |
+
elseif ( strpos( $sExclusion, '/' ) === false ) { // filename only
|
170 |
+
$sExclusion = trim( preg_replace( '#[^.0-9a-z_-]#i', '', $sExclusion ) );
|
171 |
+
}
|
172 |
+
|
173 |
+
if ( !empty( $sExclusion ) ) {
|
174 |
+
$aExclusions[] = $sExclusion;
|
175 |
+
}
|
176 |
+
}
|
177 |
+
}
|
178 |
+
|
179 |
+
$opts->setOpt( 'ufc_exclusions', array_unique( $aExclusions ) );
|
180 |
+
}
|
181 |
+
|
182 |
+
public function isPtgEnabled() :bool {
|
183 |
+
$opts = $this->getOptions();
|
184 |
+
return $this->isModuleEnabled() && $this->isPremium()
|
185 |
+
&& $opts->isOpt( 'ptg_enable', 'enabled' )
|
186 |
+
&& $opts->isOptReqsMet( 'ptg_enable' )
|
187 |
+
&& $this->canCacheDirWrite();
|
188 |
+
}
|
189 |
+
|
190 |
+
/**
|
191 |
+
* @return string|false
|
192 |
+
*/
|
193 |
+
public function getPtgSnapsBaseDir() {
|
194 |
+
return $this->getCon()->getPluginCachePath( 'ptguard/' );
|
195 |
+
}
|
196 |
+
|
197 |
+
public function hasWizard() :bool {
|
198 |
+
return false;
|
199 |
+
}
|
200 |
+
|
201 |
+
/**
|
202 |
+
* @return string
|
203 |
+
*/
|
204 |
+
public function getTempDir() {
|
205 |
+
$sDir = $this->getCon()->getPluginCachePath( 'scans' );
|
206 |
+
return Services::WpFs()->mkdir( $sDir ) ? $sDir : false;
|
207 |
+
}
|
208 |
+
|
209 |
+
public function getDbHandler_FileLocker() :Databases\FileLocker\Handler {
|
210 |
+
return $this->getDbH( 'file_protect' );
|
211 |
+
}
|
212 |
+
|
213 |
+
public function getDbHandler_ScanQueue() :Databases\ScanQueue\Handler {
|
214 |
+
return $this->getDbH( 'scanq' );
|
215 |
+
}
|
216 |
+
|
217 |
+
public function getDbHandler_ScanResults() :Databases\Scanner\Handler {
|
218 |
+
return $this->getDbH( 'scanresults' );
|
219 |
+
}
|
220 |
+
|
221 |
+
/**
|
222 |
+
* @return bool
|
223 |
+
* @throws \Exception
|
224 |
+
*/
|
225 |
+
protected function isReadyToExecute() :bool {
|
226 |
+
return ( $this->getDbHandler_ScanQueue() instanceof Databases\ScanQueue\Handler )
|
227 |
+
&& $this->getDbHandler_ScanQueue()->isReady()
|
228 |
+
&& ( $this->getDbHandler_ScanResults() instanceof Databases\Scanner\Handler )
|
229 |
+
&& $this->getDbHandler_ScanQueue()->isReady()
|
230 |
+
&& parent::isReadyToExecute();
|
231 |
+
}
|
232 |
+
|
233 |
+
public function onPluginDeactivate() {
|
234 |
+
// 1. Clean out the scanners
|
235 |
+
/** @var Options $oOpts */
|
236 |
+
$oOpts = $this->getOptions();
|
237 |
+
foreach ( $oOpts->getScanSlugs() as $slug ) {
|
238 |
+
$this->getScanCon( $slug )->purge();
|
239 |
+
}
|
240 |
+
$this->getDbHandler_ScanQueue()->tableDelete();
|
241 |
+
$this->getDbHandler_ScanResults()->tableDelete();
|
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 |
+
}
|
src/lib/src/Modules/HackGuard/Options.php
CHANGED
@@ -2,10 +2,10 @@
|
|
2 |
|
3 |
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\HackGuard;
|
4 |
|
5 |
-
use FernleafSystems\Wordpress\Plugin\Shield\Modules\
|
6 |
use FernleafSystems\Wordpress\Services\Services;
|
7 |
|
8 |
-
class Options extends
|
9 |
|
10 |
public function getFilesToLock() :array {
|
11 |
$locks = $this->getOpt( 'file_locker', [] );
|
@@ -19,22 +19,15 @@ class Options extends Base\ShieldOptions {
|
|
19 |
/**
|
20 |
* @return int[] - keys are the unique report hash
|
21 |
*/
|
22 |
-
public function getMalFalsePositiveReports() {
|
23 |
-
$
|
24 |
-
return is_array( $
|
25 |
}
|
26 |
|
27 |
-
|
28 |
-
|
29 |
-
* @return bool
|
30 |
-
*/
|
31 |
-
public function isMalFalsePositiveReported( $sReportHash ) :bool {
|
32 |
-
return isset( $this->getMalFalsePositiveReports()[ $sReportHash ] );
|
33 |
}
|
34 |
|
35 |
-
/**
|
36 |
-
* @return int
|
37 |
-
*/
|
38 |
public function getMalConfidenceBoundary() :int {
|
39 |
return (int)apply_filters( 'icwp_shield_fp_confidence_boundary', 50 );
|
40 |
}
|
@@ -78,86 +71,62 @@ class Options extends Base\ShieldOptions {
|
|
78 |
}
|
79 |
|
80 |
/**
|
81 |
-
* @param string $
|
82 |
-
* @param string $
|
83 |
* @return string[]
|
84 |
*/
|
85 |
-
public function getMalSignatures( $
|
86 |
-
$
|
87 |
-
$
|
88 |
-
if ( $
|
89 |
-
$
|
90 |
}
|
91 |
else {
|
92 |
-
$
|
93 |
array_map( 'trim',
|
94 |
-
explode( "\n", Services::HttpRequest()->getContent( $
|
95 |
),
|
96 |
-
function ( $
|
97 |
-
return ( ( strpos( $
|
98 |
}
|
99 |
);
|
100 |
|
101 |
-
if ( !empty( $
|
102 |
-
$
|
103 |
}
|
104 |
}
|
105 |
-
return $
|
106 |
}
|
107 |
|
108 |
-
|
109 |
-
* @return bool
|
110 |
-
*/
|
111 |
-
public function isMalAutoRepairSurgical() {
|
112 |
return $this->isOpt( 'mal_autorepair_surgical', 'Y' );
|
113 |
}
|
114 |
|
115 |
-
|
116 |
-
* @return bool
|
117 |
-
*/
|
118 |
-
public function isMalUseNetworkIntelligence() {
|
119 |
return $this->getMalConfidenceBoundary() > 0;
|
120 |
}
|
121 |
|
122 |
-
|
123 |
-
* @return bool
|
124 |
-
*/
|
125 |
-
public function isPtgReinstallLinks() {
|
126 |
return $this->isOpt( 'ptg_reinstall_links', 'Y' ) && $this->isPremium();
|
127 |
}
|
128 |
|
129 |
-
|
130 |
-
* @return bool
|
131 |
-
*/
|
132 |
-
public function isRepairFileAuto() {
|
133 |
return count( $this->getRepairAreas() ) > 0;
|
134 |
}
|
135 |
|
136 |
-
|
137 |
-
* @return bool
|
138 |
-
*/
|
139 |
-
public function isRepairFilePlugin() {
|
140 |
return in_array( 'plugin', $this->getRepairAreas() );
|
141 |
}
|
142 |
|
143 |
-
|
144 |
-
* @return bool
|
145 |
-
*/
|
146 |
-
public function isRepairFileTheme() {
|
147 |
return in_array( 'theme', $this->getRepairAreas() );
|
148 |
}
|
149 |
|
150 |
-
|
151 |
-
* @return bool
|
152 |
-
*/
|
153 |
-
public function isRepairFileWP() {
|
154 |
return in_array( 'wp', $this->getRepairAreas() );
|
155 |
}
|
156 |
|
157 |
-
|
158 |
-
* @return bool
|
159 |
-
*/
|
160 |
-
public function isWpvulnAutoupdatesEnabled() {
|
161 |
return $this->isOpt( 'wpvuln_scan_autoupdate', 'Y' );
|
162 |
}
|
163 |
|
@@ -220,9 +189,9 @@ class Options extends Base\ShieldOptions {
|
|
220 |
/**
|
221 |
* Provides an array where the key is the root dir, and the value is the specific file types.
|
222 |
* An empty array means all files.
|
223 |
-
* @return
|
224 |
*/
|
225 |
-
public function getUfcScanDirectories() {
|
226 |
$aDirs = [
|
227 |
path_join( ABSPATH, 'wp-admin' ) => [],
|
228 |
path_join( ABSPATH, 'wp-includes' ) => []
|
@@ -249,10 +218,7 @@ class Options extends Base\ShieldOptions {
|
|
249 |
return $this->getOpt( 'enable_unrecognised_file_cleaner_scan', 'disabled' );
|
250 |
}
|
251 |
|
252 |
-
|
253 |
-
* @return bool
|
254 |
-
*/
|
255 |
-
public function isUfsDeleteFiles() {
|
256 |
return $this->getUnrecognisedFileScannerOption() === 'enabled_delete_only';
|
257 |
}
|
258 |
|
@@ -301,10 +267,7 @@ class Options extends Base\ShieldOptions {
|
|
301 |
return $sPattern;
|
302 |
}
|
303 |
|
304 |
-
|
305 |
-
* @return bool
|
306 |
-
*/
|
307 |
-
public function isScanCron() {
|
308 |
return (bool)$this->getOpt( 'is_scan_cron' );
|
309 |
}
|
310 |
|
@@ -328,28 +291,4 @@ class Options extends Base\ShieldOptions {
|
|
328 |
}
|
329 |
) );
|
330 |
}
|
331 |
-
|
332 |
-
/**
|
333 |
-
* @return string
|
334 |
-
* @deprecated 10.0
|
335 |
-
*/
|
336 |
-
public function getDbTable_FileLocker() {
|
337 |
-
return $this->getCon()->prefixOption( $this->getDef( 'table_name_filelocker' ) );
|
338 |
-
}
|
339 |
-
|
340 |
-
/**
|
341 |
-
* @return string
|
342 |
-
* @deprecated 10.0
|
343 |
-
*/
|
344 |
-
public function getDbTable_Scanner() {
|
345 |
-
return $this->getCon()->prefixOption( $this->getDef( 'table_name_scanner' ) );
|
346 |
-
}
|
347 |
-
|
348 |
-
/**
|
349 |
-
* @return string
|
350 |
-
* @deprecated 10.0
|
351 |
-
*/
|
352 |
-
public function getDbTable_ScanQueue() :string {
|
353 |
-
return $this->getCon()->prefixOption( $this->getDef( 'table_name_scanqueue' ) );
|
354 |
-
}
|
355 |
}
|
2 |
|
3 |
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\HackGuard;
|
4 |
|
5 |
+
use FernleafSystems\Wordpress\Plugin\Shield\Modules\BaseShield;
|
6 |
use FernleafSystems\Wordpress\Services\Services;
|
7 |
|
8 |
+
class Options extends BaseShield\Options {
|
9 |
|
10 |
public function getFilesToLock() :array {
|
11 |
$locks = $this->getOpt( 'file_locker', [] );
|
19 |
/**
|
20 |
* @return int[] - keys are the unique report hash
|
21 |
*/
|
22 |
+
public function getMalFalsePositiveReports() :array {
|
23 |
+
$FP = $this->getOpt( 'mal_fp_reports', [] );
|
24 |
+
return is_array( $FP ) ? $FP : [];
|
25 |
}
|
26 |
|
27 |
+
public function isMalFalsePositiveReported( string $hash ) :bool {
|
28 |
+
return isset( $this->getMalFalsePositiveReports()[ $hash ] );
|
|
|
|
|
|
|
|
|
29 |
}
|
30 |
|
|
|
|
|
|
|
31 |
public function getMalConfidenceBoundary() :int {
|
32 |
return (int)apply_filters( 'icwp_shield_fp_confidence_boundary', 50 );
|
33 |
}
|
71 |
}
|
72 |
|
73 |
/**
|
74 |
+
* @param string $fileName
|
75 |
+
* @param string $url
|
76 |
* @return string[]
|
77 |
*/
|
78 |
+
public function getMalSignatures( string $fileName, string $url ) {
|
79 |
+
$FS = Services::WpFs();
|
80 |
+
$file = $this->getCon()->getPluginCachePath( $fileName );
|
81 |
+
if ( $FS->exists( $file ) ) {
|
82 |
+
$sigs = explode( "\n", $FS->getFileContent( $file, true ) );
|
83 |
}
|
84 |
else {
|
85 |
+
$sigs = array_filter(
|
86 |
array_map( 'trim',
|
87 |
+
explode( "\n", Services::HttpRequest()->getContent( $url ) )
|
88 |
),
|
89 |
+
function ( $line ) {
|
90 |
+
return ( ( strpos( $line, '#' ) !== 0 ) && strlen( $line ) > 0 );
|
91 |
}
|
92 |
);
|
93 |
|
94 |
+
if ( !empty( $sigs ) ) {
|
95 |
+
$FS->putFileContent( $file, implode( "\n", $sigs ), true );
|
96 |
}
|
97 |
}
|
98 |
+
return $sigs;
|
99 |
}
|
100 |
|
101 |
+
public function isMalAutoRepairSurgical() :bool {
|
|
|
|
|
|
|
102 |
return $this->isOpt( 'mal_autorepair_surgical', 'Y' );
|
103 |
}
|
104 |
|
105 |
+
public function isMalUseNetworkIntelligence() :bool {
|
|
|
|
|
|
|
106 |
return $this->getMalConfidenceBoundary() > 0;
|
107 |
}
|
108 |
|
109 |
+
public function isPtgReinstallLinks() :bool {
|
|
|
|
|
|
|
110 |
return $this->isOpt( 'ptg_reinstall_links', 'Y' ) && $this->isPremium();
|
111 |
}
|
112 |
|
113 |
+
public function isRepairFileAuto() :bool {
|
|
|
|
|
|
|
114 |
return count( $this->getRepairAreas() ) > 0;
|
115 |
}
|
116 |
|
117 |
+
public function isRepairFilePlugin() :bool {
|
|
|
|
|
|
|
118 |
return in_array( 'plugin', $this->getRepairAreas() );
|
119 |
}
|
120 |
|
121 |
+
public function isRepairFileTheme() :bool {
|
|
|
|
|
|
|
122 |
return in_array( 'theme', $this->getRepairAreas() );
|
123 |
}
|
124 |
|
125 |
+
public function isRepairFileWP() :bool {
|
|
|
|
|
|
|
126 |
return in_array( 'wp', $this->getRepairAreas() );
|
127 |
}
|
128 |
|
129 |
+
public function isWpvulnAutoupdatesEnabled() :bool {
|
|
|
|
|
|
|
130 |
return $this->isOpt( 'wpvuln_scan_autoupdate', 'Y' );
|
131 |
}
|
132 |
|
189 |
/**
|
190 |
* Provides an array where the key is the root dir, and the value is the specific file types.
|
191 |
* An empty array means all files.
|
192 |
+
* @return array[]
|
193 |
*/
|
194 |
+
public function getUfcScanDirectories() :array {
|
195 |
$aDirs = [
|
196 |
path_join( ABSPATH, 'wp-admin' ) => [],
|
197 |
path_join( ABSPATH, 'wp-includes' ) => []
|
218 |
return $this->getOpt( 'enable_unrecognised_file_cleaner_scan', 'disabled' );
|
219 |
}
|
220 |
|
221 |
+
public function isUfsDeleteFiles() :bool {
|
|
|
|
|
|
|
222 |
return $this->getUnrecognisedFileScannerOption() === 'enabled_delete_only';
|
223 |
}
|
224 |
|
267 |
return $sPattern;
|
268 |
}
|
269 |
|
270 |
+
public function isScanCron() :bool {
|
|
|
|
|
|
|
271 |
return (bool)$this->getOpt( 'is_scan_cron' );
|
272 |
}
|
273 |
|
291 |
}
|
292 |
) );
|
293 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
294 |
}
|
src/lib/src/Modules/HackGuard/Processor.php
ADDED
@@ -0,0 +1,45 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php declare( strict_types=1 );
|
2 |
+
|
3 |
+
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\HackGuard;
|
4 |
+
|
5 |
+
use FernleafSystems\Wordpress\Plugin\Shield\Modules\BaseShield;
|
6 |
+
|
7 |
+
class Processor extends BaseShield\Processor {
|
8 |
+
|
9 |
+
protected function run() {
|
10 |
+
/** @var ModCon $mod */
|
11 |
+
$mod = $this->getMod();
|
12 |
+
|
13 |
+
$mod->getScansCon()->execute();
|
14 |
+
|
15 |
+
/** @var Options $opts */
|
16 |
+
$opts = $this->getOptions();
|
17 |
+
if ( count( $opts->getFilesToLock() ) > 0 ) {
|
18 |
+
$mod->getFileLocker()->execute();
|
19 |
+
}
|
20 |
+
}
|
21 |
+
|
22 |
+
public function runHourlyCron() {
|
23 |
+
( new Lib\Snapshots\StoreAction\TouchAll() )
|
24 |
+
->setMod( $this->getMod() )
|
25 |
+
->run();
|
26 |
+
}
|
27 |
+
|
28 |
+
public function runDailyCron() {
|
29 |
+
( new Lib\Snapshots\StoreAction\CleanAll() )
|
30 |
+
->setMod( $this->getMod() )
|
31 |
+
->run();
|
32 |
+
}
|
33 |
+
|
34 |
+
public function onWpLoaded() {
|
35 |
+
( new Lib\Snapshots\StoreAction\ScheduleBuildAll() )
|
36 |
+
->setMod( $this->getMod() )
|
37 |
+
->hookBuild();
|
38 |
+
}
|
39 |
+
|
40 |
+
public function onModuleShutdown() {
|
41 |
+
( new Lib\Snapshots\StoreAction\ScheduleBuildAll() )
|
42 |
+
->setMod( $this->getMod() )
|
43 |
+
->schedule();
|
44 |
+
}
|
45 |
+
}
|
src/lib/src/Modules/HackGuard/Reporting.php
CHANGED
@@ -2,10 +2,10 @@
|
|
2 |
|
3 |
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\HackGuard;
|
4 |
|
5 |
-
use FernleafSystems\Wordpress\Plugin\Shield\Modules\Base
|
6 |
use FernleafSystems\Wordpress\Plugin\Shield\Modules\HackGuard\Lib\Reports;
|
7 |
|
8 |
-
class Reporting extends
|
9 |
|
10 |
/**
|
11 |
* @inheritDoc
|
2 |
|
3 |
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\HackGuard;
|
4 |
|
5 |
+
use FernleafSystems\Wordpress\Plugin\Shield\Modules\Base;
|
6 |
use FernleafSystems\Wordpress\Plugin\Shield\Modules\HackGuard\Lib\Reports;
|
7 |
|
8 |
+
class Reporting extends Base\Reporting {
|
9 |
|
10 |
/**
|
11 |
* @inheritDoc
|
src/lib/src/Modules/HackGuard/Scan/Controller/Base.php
CHANGED
@@ -2,18 +2,22 @@
|
|
2 |
|
3 |
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\HackGuard\Scan\Controller;
|
4 |
|
|
|
5 |
use FernleafSystems\Wordpress\Plugin\Shield\Databases;
|
6 |
use FernleafSystems\Wordpress\Plugin\Shield\Modules\HackGuard;
|
|
|
7 |
use FernleafSystems\Wordpress\Plugin\Shield\Modules\ModConsumer;
|
8 |
use FernleafSystems\Wordpress\Plugin\Shield\Scans;
|
9 |
use FernleafSystems\Wordpress\Plugin\Shield\Scans\Base\BaseResultItem;
|
10 |
use FernleafSystems\Wordpress\Plugin\Shield\Scans\Base\BaseResultsSet;
|
11 |
use FernleafSystems\Wordpress\Plugin\Shield\Scans\Base\BaseScanActionVO;
|
12 |
use FernleafSystems\Wordpress\Plugin\Shield\Scans\Base\Table\BaseEntryFormatter;
|
|
|
13 |
|
14 |
abstract class Base {
|
15 |
|
16 |
use ModConsumer;
|
|
|
17 |
|
18 |
const SCAN_SLUG = '';
|
19 |
|
@@ -29,65 +33,79 @@ abstract class Base {
|
|
29 |
public function __construct() {
|
30 |
}
|
31 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
32 |
public function cleanStalesResults() {
|
33 |
-
$
|
34 |
->setScanController( $this )
|
35 |
->retrieve();
|
36 |
-
foreach ( $
|
37 |
-
if ( !$this->isResultItemStale( $
|
38 |
-
$
|
39 |
}
|
40 |
}
|
41 |
( new HackGuard\Scan\Results\ResultsDelete() )
|
42 |
->setScanController( $this )
|
43 |
-
->delete( $
|
44 |
}
|
45 |
|
46 |
-
|
47 |
-
|
48 |
-
|
49 |
-
|
50 |
-
|
51 |
-
|
52 |
-
$oMod = $this->getMod();
|
53 |
-
$aActionNonce = $oMod->getNonceActionData( 'scan_file_download' );
|
54 |
-
$aActionNonce[ 'rid' ] = $oEntryVo->id;
|
55 |
-
return add_query_arg( $aActionNonce, $oMod->getUrl_AdminPage() );
|
56 |
}
|
57 |
|
58 |
-
|
59 |
-
|
60 |
-
|
61 |
-
|
62 |
-
|
63 |
-
|
64 |
-
|
65 |
-
$
|
66 |
-
|
67 |
-
|
68 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
69 |
}
|
70 |
|
71 |
/**
|
72 |
-
* @param BaseResultItem|mixed $
|
73 |
* @return bool
|
74 |
*/
|
75 |
-
abstract protected function isResultItemStale( $
|
76 |
|
77 |
/**
|
78 |
-
* @param int|string $
|
79 |
-
* @param string $
|
80 |
* @return bool
|
81 |
* @throws \Exception
|
82 |
*/
|
83 |
-
public function executeItemAction( $
|
84 |
$bSuccess = false;
|
85 |
|
86 |
-
if ( is_numeric( $
|
87 |
/** @var Databases\Scanner\EntryVO $oEntry */
|
88 |
$oEntry = $this->getScanResultsDbHandler()
|
89 |
->getQuerySelector()
|
90 |
-
->byId( $
|
91 |
if ( empty( $oEntry ) ) {
|
92 |
throw new \Exception( 'Item could not be found.' );
|
93 |
}
|
@@ -98,7 +116,7 @@ abstract class Base {
|
|
98 |
|
99 |
$bSuccess = $this->getItemActionHandler()
|
100 |
->setScanItem( $oItem )
|
101 |
-
->process( $
|
102 |
}
|
103 |
|
104 |
return $bSuccess;
|
@@ -108,22 +126,22 @@ abstract class Base {
|
|
108 |
* @return Scans\Base\BaseResultsSet|mixed
|
109 |
*/
|
110 |
protected function getItemsToAutoRepair() {
|
111 |
-
/** @var Databases\Scanner\Select $
|
112 |
-
$
|
113 |
-
$
|
114 |
-
|
115 |
return ( new HackGuard\Scan\Results\ConvertBetweenTypes() )
|
116 |
->setScanController( $this )
|
117 |
-
->fromVOsToResultsSet( $
|
118 |
}
|
119 |
|
120 |
/**
|
121 |
* @return bool
|
122 |
*/
|
123 |
public function updateAllAsNotified() {
|
124 |
-
/** @var Databases\Scanner\Update $
|
125 |
-
$
|
126 |
-
return $
|
127 |
}
|
128 |
|
129 |
/**
|
@@ -131,15 +149,15 @@ abstract class Base {
|
|
131 |
* @return Scans\Base\BaseResultsSet|mixed
|
132 |
*/
|
133 |
public function getAllResults( $bIncludeIgnored = false ) {
|
134 |
-
/** @var Databases\Scanner\Select $
|
135 |
-
$
|
136 |
-
$
|
137 |
if ( !$bIncludeIgnored ) {
|
138 |
-
$
|
139 |
}
|
140 |
return ( new HackGuard\Scan\Results\ConvertBetweenTypes() )
|
141 |
->setScanController( $this )
|
142 |
-
->fromVOsToResultsSet( $
|
143 |
}
|
144 |
|
145 |
/**
|
@@ -172,9 +190,6 @@ abstract class Base {
|
|
172 |
return $strings->getScanNames()[ static::SCAN_SLUG ];
|
173 |
}
|
174 |
|
175 |
-
/**
|
176 |
-
* @return bool
|
177 |
-
*/
|
178 |
public function isCronAutoRepair() :bool {
|
179 |
return false;
|
180 |
}
|
@@ -185,22 +200,16 @@ abstract class Base {
|
|
185 |
return true;
|
186 |
}
|
187 |
|
188 |
-
|
189 |
-
* @return bool
|
190 |
-
*/
|
191 |
-
public function isReady() {
|
192 |
return $this->isEnabled() && $this->isScanningAvailable();
|
193 |
}
|
194 |
|
195 |
-
|
196 |
-
|
197 |
-
|
198 |
-
|
199 |
-
|
200 |
-
$
|
201 |
-
/** @var HackGuard\Options $oOpts */
|
202 |
-
$oOpts = $this->getOptions();
|
203 |
-
return $oMod->isModuleEnabled() && ( !$this->isPremiumOnly() || $oOpts->isPremium() );
|
204 |
}
|
205 |
|
206 |
/**
|
@@ -252,66 +261,64 @@ abstract class Base {
|
|
252 |
return $this;
|
253 |
}
|
254 |
|
255 |
-
|
256 |
-
|
257 |
-
|
258 |
-
|
259 |
-
/** @var \ICWP_WPSF_FeatureHandler_HackProtect $oMod */
|
260 |
-
$oMod = $this->getMod();
|
261 |
-
return $oMod->getDbHandler_ScanResults();
|
262 |
}
|
263 |
|
264 |
-
|
265 |
-
* @return string
|
266 |
-
*/
|
267 |
-
public function getSlug() {
|
268 |
try {
|
269 |
-
$
|
270 |
}
|
271 |
-
catch ( \ReflectionException $
|
272 |
-
$
|
273 |
}
|
274 |
-
return $
|
275 |
}
|
276 |
|
277 |
/**
|
278 |
* @return BaseResultItem|mixed
|
279 |
*/
|
280 |
public function getNewResultItem() {
|
281 |
-
$
|
282 |
-
return new $
|
283 |
}
|
284 |
|
285 |
/**
|
286 |
* @return BaseResultsSet|mixed
|
287 |
*/
|
288 |
public function getNewResultsSet() {
|
289 |
-
$
|
290 |
-
return new $
|
291 |
}
|
292 |
|
293 |
/**
|
294 |
* @return BaseEntryFormatter|mixed
|
295 |
*/
|
296 |
public function getTableEntryFormatter() {
|
297 |
-
$
|
298 |
-
/** @var BaseEntryFormatter $
|
299 |
-
$
|
300 |
-
return $
|
301 |
-
|
302 |
-
|
303 |
}
|
304 |
|
305 |
-
|
306 |
-
* @return string
|
307 |
-
*/
|
308 |
-
public function getScanNamespace() {
|
309 |
try {
|
310 |
-
$
|
311 |
}
|
312 |
-
catch ( \Exception $
|
313 |
-
$
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
314 |
}
|
315 |
-
return rtrim( $sName, '\\' ).'\\';
|
316 |
}
|
317 |
}
|
2 |
|
3 |
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\HackGuard\Scan\Controller;
|
4 |
|
5 |
+
use FernleafSystems\Utilities\Logic\OneTimeExecute;
|
6 |
use FernleafSystems\Wordpress\Plugin\Shield\Databases;
|
7 |
use FernleafSystems\Wordpress\Plugin\Shield\Modules\HackGuard;
|
8 |
+
use FernleafSystems\Wordpress\Plugin\Shield\Modules\HackGuard\ModCon;
|
9 |
use FernleafSystems\Wordpress\Plugin\Shield\Modules\ModConsumer;
|
10 |
use FernleafSystems\Wordpress\Plugin\Shield\Scans;
|
11 |
use FernleafSystems\Wordpress\Plugin\Shield\Scans\Base\BaseResultItem;
|
12 |
use FernleafSystems\Wordpress\Plugin\Shield\Scans\Base\BaseResultsSet;
|
13 |
use FernleafSystems\Wordpress\Plugin\Shield\Scans\Base\BaseScanActionVO;
|
14 |
use FernleafSystems\Wordpress\Plugin\Shield\Scans\Base\Table\BaseEntryFormatter;
|
15 |
+
use FernleafSystems\Wordpress\Services\Services;
|
16 |
|
17 |
abstract class Base {
|
18 |
|
19 |
use ModConsumer;
|
20 |
+
use OneTimeExecute;
|
21 |
|
22 |
const SCAN_SLUG = '';
|
23 |
|
33 |
public function __construct() {
|
34 |
}
|
35 |
|
36 |
+
protected function run() {
|
37 |
+
add_action(
|
38 |
+
$this->getCon()->prefix( 'ondemand_scan_'.$this->getSlug() ),
|
39 |
+
function () {
|
40 |
+
/** @var HackGuard\ModCon $mod */
|
41 |
+
$mod = $this->getMod();
|
42 |
+
$mod->getScanQueueController()->startScans( [ $this->getSlug() ] );
|
43 |
+
}
|
44 |
+
);
|
45 |
+
}
|
46 |
+
|
47 |
public function cleanStalesResults() {
|
48 |
+
$results = ( new HackGuard\Scan\Results\ResultsRetrieve() )
|
49 |
->setScanController( $this )
|
50 |
->retrieve();
|
51 |
+
foreach ( $results->getItems() as $item ) {
|
52 |
+
if ( !$this->isResultItemStale( $item ) ) {
|
53 |
+
$results->removeItemByHash( $item->hash );
|
54 |
}
|
55 |
}
|
56 |
( new HackGuard\Scan\Results\ResultsDelete() )
|
57 |
->setScanController( $this )
|
58 |
+
->delete( $results );
|
59 |
}
|
60 |
|
61 |
+
public function createFileDownloadLink( Databases\Scanner\EntryVO $entry ) :string {
|
62 |
+
/** @var ModCon $mod */
|
63 |
+
$mod = $this->getMod();
|
64 |
+
$aActionNonce = $mod->getNonceActionData( 'scan_file_download' );
|
65 |
+
$aActionNonce[ 'rid' ] = $entry->id;
|
66 |
+
return add_query_arg( $aActionNonce, $mod->getUrl_AdminPage() );
|
|
|
|
|
|
|
|
|
67 |
}
|
68 |
|
69 |
+
public function getLastScanAt() :int {
|
70 |
+
/** @var Databases\Events\Select $sel */
|
71 |
+
$sel = $this->getCon()
|
72 |
+
->getModule_Events()
|
73 |
+
->getDbHandler_Events()
|
74 |
+
->getQuerySelector();
|
75 |
+
$entry = $sel->getLatestForEvent( $this->getSlug().'_scan_run' );
|
76 |
+
return ( $entry instanceof Databases\Events\EntryVO ) ? (int)$entry->created_at : 0;
|
77 |
+
}
|
78 |
+
|
79 |
+
public function getScanHasProblem() :bool {
|
80 |
+
/** @var ModCon $mod */
|
81 |
+
$mod = $this->getMod();
|
82 |
+
/** @var Databases\Scanner\Select $sel */
|
83 |
+
$sel = $mod->getDbHandler_ScanResults()->getQuerySelector();
|
84 |
+
return $sel->filterByNotIgnored()
|
85 |
+
->filterByScan( $this->getSlug() )
|
86 |
+
->count() > 0;
|
87 |
}
|
88 |
|
89 |
/**
|
90 |
+
* @param BaseResultItem|mixed $item
|
91 |
* @return bool
|
92 |
*/
|
93 |
+
abstract protected function isResultItemStale( $item );
|
94 |
|
95 |
/**
|
96 |
+
* @param int|string $itemID
|
97 |
+
* @param string $action
|
98 |
* @return bool
|
99 |
* @throws \Exception
|
100 |
*/
|
101 |
+
public function executeItemAction( $itemID, $action ) {
|
102 |
$bSuccess = false;
|
103 |
|
104 |
+
if ( is_numeric( $itemID ) ) {
|
105 |
/** @var Databases\Scanner\EntryVO $oEntry */
|
106 |
$oEntry = $this->getScanResultsDbHandler()
|
107 |
->getQuerySelector()
|
108 |
+
->byId( $itemID );
|
109 |
if ( empty( $oEntry ) ) {
|
110 |
throw new \Exception( 'Item could not be found.' );
|
111 |
}
|
116 |
|
117 |
$bSuccess = $this->getItemActionHandler()
|
118 |
->setScanItem( $oItem )
|
119 |
+
->process( $action );
|
120 |
}
|
121 |
|
122 |
return $bSuccess;
|
126 |
* @return Scans\Base\BaseResultsSet|mixed
|
127 |
*/
|
128 |
protected function getItemsToAutoRepair() {
|
129 |
+
/** @var Databases\Scanner\Select $sel */
|
130 |
+
$sel = $this->getScanResultsDbHandler()->getQuerySelector();
|
131 |
+
$sel->filterByScan( $this->getSlug() )
|
132 |
+
->filterByNotIgnored();
|
133 |
return ( new HackGuard\Scan\Results\ConvertBetweenTypes() )
|
134 |
->setScanController( $this )
|
135 |
+
->fromVOsToResultsSet( $sel->query() );
|
136 |
}
|
137 |
|
138 |
/**
|
139 |
* @return bool
|
140 |
*/
|
141 |
public function updateAllAsNotified() {
|
142 |
+
/** @var Databases\Scanner\Update $updater */
|
143 |
+
$updater = $this->getScanResultsDbHandler()->getQueryUpdater();
|
144 |
+
return $updater->setAllNotifiedForScan( $this->getSlug() );
|
145 |
}
|
146 |
|
147 |
/**
|
149 |
* @return Scans\Base\BaseResultsSet|mixed
|
150 |
*/
|
151 |
public function getAllResults( $bIncludeIgnored = false ) {
|
152 |
+
/** @var Databases\Scanner\Select $sel */
|
153 |
+
$sel = $this->getScanResultsDbHandler()->getQuerySelector();
|
154 |
+
$sel->filterByScan( $this->getSlug() );
|
155 |
if ( !$bIncludeIgnored ) {
|
156 |
+
$sel->filterByNotIgnored();
|
157 |
}
|
158 |
return ( new HackGuard\Scan\Results\ConvertBetweenTypes() )
|
159 |
->setScanController( $this )
|
160 |
+
->fromVOsToResultsSet( $sel->query() );
|
161 |
}
|
162 |
|
163 |
/**
|
190 |
return $strings->getScanNames()[ static::SCAN_SLUG ];
|
191 |
}
|
192 |
|
|
|
|
|
|
|
193 |
public function isCronAutoRepair() :bool {
|
194 |
return false;
|
195 |
}
|
200 |
return true;
|
201 |
}
|
202 |
|
203 |
+
public function isReady() :bool {
|
|
|
|
|
|
|
204 |
return $this->isEnabled() && $this->isScanningAvailable();
|
205 |
}
|
206 |
|
207 |
+
public function isScanningAvailable() :bool {
|
208 |
+
/** @var ModCon $mod */
|
209 |
+
$mod = $this->getMod();
|
210 |
+
/** @var HackGuard\Options $opts */
|
211 |
+
$opts = $this->getOptions();
|
212 |
+
return $mod->isModuleEnabled() && ( !$this->isPremiumOnly() || $opts->isPremium() );
|
|
|
|
|
|
|
213 |
}
|
214 |
|
215 |
/**
|
261 |
return $this;
|
262 |
}
|
263 |
|
264 |
+
public function getScanResultsDbHandler() :Databases\Scanner\Handler {
|
265 |
+
/** @var ModCon $mod */
|
266 |
+
$mod = $this->getMod();
|
267 |
+
return $mod->getDbHandler_ScanResults();
|
|
|
|
|
|
|
268 |
}
|
269 |
|
270 |
+
public function getSlug() :string {
|
|
|
|
|
|
|
271 |
try {
|
272 |
+
$slug = strtolower( ( new \ReflectionClass( $this ) )->getShortName() );
|
273 |
}
|
274 |
+
catch ( \ReflectionException $e ) {
|
275 |
+
$slug = '';
|
276 |
}
|
277 |
+
return $slug;
|
278 |
}
|
279 |
|
280 |
/**
|
281 |
* @return BaseResultItem|mixed
|
282 |
*/
|
283 |
public function getNewResultItem() {
|
284 |
+
$class = $this->getScanNamespace().'ResultItem';
|
285 |
+
return new $class();
|
286 |
}
|
287 |
|
288 |
/**
|
289 |
* @return BaseResultsSet|mixed
|
290 |
*/
|
291 |
public function getNewResultsSet() {
|
292 |
+
$class = $this->getScanNamespace().'ResultsSet';
|
293 |
+
return new $class();
|
294 |
}
|
295 |
|
296 |
/**
|
297 |
* @return BaseEntryFormatter|mixed
|
298 |
*/
|
299 |
public function getTableEntryFormatter() {
|
300 |
+
$class = $this->getScanNamespace().'Table\\EntryFormatter';
|
301 |
+
/** @var BaseEntryFormatter $formatter */
|
302 |
+
$formatter = new $class();
|
303 |
+
return $formatter->setScanController( $this )
|
304 |
+
->setMod( $this->getMod() )
|
305 |
+
->setScanActionVO( $this->getScanActionVO() );
|
306 |
}
|
307 |
|
308 |
+
public function getScanNamespace() :string {
|
|
|
|
|
|
|
309 |
try {
|
310 |
+
$ns = ( new \ReflectionClass( $this->getScanActionVO() ) )->getNamespaceName();
|
311 |
}
|
312 |
+
catch ( \Exception $e ) {
|
313 |
+
$ns = __NAMESPACE__;
|
314 |
+
}
|
315 |
+
return rtrim( $ns, '\\' ).'\\';
|
316 |
+
}
|
317 |
+
|
318 |
+
protected function scheduleOnDemandScan( int $nDelay = 3 ) {
|
319 |
+
$sHook = $this->getCon()->prefix( 'ondemand_scan_'.$this->getSlug() );
|
320 |
+
if ( !wp_next_scheduled( $sHook ) ) {
|
321 |
+
wp_schedule_single_event( Services::Request()->ts() + $nDelay, $sHook );
|
322 |
}
|
|
|
323 |
}
|
324 |
}
|
src/lib/src/Modules/HackGuard/Scan/Controller/BaseForAssets.php
CHANGED
@@ -8,16 +8,16 @@ use FernleafSystems\Wordpress\Services\Services;
|
|
8 |
abstract class BaseForAssets extends Base {
|
9 |
|
10 |
/**
|
11 |
-
* @param Scans\Ptg\ResultItem|Scans\Wpv\ResultItem|Scans\Apc\ResultItem $
|
12 |
* @return bool
|
13 |
*/
|
14 |
-
protected function isResultItemStale( $
|
15 |
-
if ( $
|
16 |
-
$oAsset = Services::WpPlugins()->getPluginAsVo( $
|
17 |
$bAssetExists = empty( $oAsset ) || $oAsset->active;
|
18 |
}
|
19 |
else {
|
20 |
-
$oAsset = Services::WpThemes()->getThemeAsVo( $
|
21 |
$bAssetExists = empty( $oAsset ) || ( $oAsset->active || $oAsset->is_parent );
|
22 |
}
|
23 |
return !$bAssetExists;
|
8 |
abstract class BaseForAssets extends Base {
|
9 |
|
10 |
/**
|
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 |
+
$oAsset = Services::WpPlugins()->getPluginAsVo( $item->slug );
|
17 |
$bAssetExists = empty( $oAsset ) || $oAsset->active;
|
18 |
}
|
19 |
else {
|
20 |
+
$oAsset = Services::WpThemes()->getThemeAsVo( $item->slug );
|
21 |
$bAssetExists = empty( $oAsset ) || ( $oAsset->active || $oAsset->is_parent );
|
22 |
}
|
23 |
return !$bAssetExists;
|
src/lib/src/Modules/HackGuard/Scan/Controller/Mal.php
CHANGED
@@ -52,11 +52,11 @@ class Mal extends Base {
|
|
52 |
}
|
53 |
|
54 |
/**
|
55 |
-
* @param Scans\Mal\ResultItem $
|
56 |
* @return bool
|
57 |
*/
|
58 |
-
protected function isResultItemStale( $
|
59 |
-
return !Services::WpFs()->exists( $
|
60 |
}
|
61 |
|
62 |
/**
|
52 |
}
|
53 |
|
54 |
/**
|
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 |
|
62 |
/**
|
src/lib/src/Modules/HackGuard/Scan/Controller/Ptg.php
CHANGED
@@ -11,6 +11,13 @@ class Ptg extends BaseForAssets {
|
|
11 |
|
12 |
const SCAN_SLUG = 'ptg';
|
13 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
14 |
/**
|
15 |
* @return Scans\Ptg\ResultsSet
|
16 |
*/
|
@@ -18,19 +25,19 @@ class Ptg extends BaseForAssets {
|
|
18 |
/** @var HackGuard\Options $opts */
|
19 |
$opts = $this->getOptions();
|
20 |
|
21 |
-
/** @var Scans\Ptg\ResultsSet $
|
22 |
-
$
|
23 |
|
24 |
if ( !$opts->isRepairFilePlugin() || !$opts->isRepairFileTheme() ) {
|
25 |
if ( $opts->isRepairFileTheme() ) {
|
26 |
-
$
|
27 |
}
|
28 |
elseif ( $opts->isRepairFilePlugin() ) {
|
29 |
-
$
|
30 |
}
|
31 |
}
|
32 |
|
33 |
-
return $
|
34 |
}
|
35 |
|
36 |
public function isCronAutoRepair() :bool {
|
@@ -40,14 +47,14 @@ class Ptg extends BaseForAssets {
|
|
40 |
}
|
41 |
|
42 |
/**
|
43 |
-
* @param Scans\Mal\ResultItem $
|
44 |
* @return bool
|
45 |
*/
|
46 |
-
protected function isResultItemStale( $
|
47 |
$bStale = false;
|
48 |
-
$oAsset = ( new WpOrg\Plugin\Files() )->findPluginFromFile( $
|
49 |
if ( empty( $oAsset ) ) {
|
50 |
-
$oAsset = ( new WpOrg\Theme\Files() )->findThemeFromFile( $
|
51 |
$bStale = empty( $oAsset );
|
52 |
}
|
53 |
return $bStale;
|
@@ -60,39 +67,29 @@ class Ptg extends BaseForAssets {
|
|
60 |
return new Scans\Ptg\Utilities\ItemActionHandler();
|
61 |
}
|
62 |
|
63 |
-
|
64 |
-
|
65 |
-
|
66 |
-
|
67 |
-
|
68 |
-
$bSuccess = false;
|
69 |
-
$oWpPs = Services::WpPlugins();
|
70 |
-
$oPl = $oWpPs->getPluginAsVo( $sBaseFile );
|
71 |
-
if ( $oPl->isWpOrg() && $oWpPs->reinstall( $oPl->file ) ) {
|
72 |
try {
|
73 |
( new HackGuard\Lib\Snapshots\StoreAction\Build() )
|
74 |
->setMod( $this->getMod() )
|
75 |
-
->setAsset( $
|
76 |
->run();
|
77 |
-
$
|
78 |
}
|
79 |
catch ( \Exception $e ) {
|
80 |
}
|
81 |
}
|
82 |
-
return $
|
83 |
}
|
84 |
|
85 |
-
/**
|
86 |
-
* @return bool
|
87 |
-
*/
|
88 |
public function isEnabled() :bool {
|
89 |
return $this->getOptions()->isOpt( 'ptg_enable', 'Y' ) && $this->getOptions()->isOptReqsMet( 'ptg_enable' );
|
90 |
}
|
91 |
|
92 |
-
|
93 |
-
* @return bool
|
94 |
-
*/
|
95 |
-
public function isScanningAvailable() {
|
96 |
return parent::isScanningAvailable()
|
97 |
&& $this->getOptions()->isOptReqsMet( 'ptg_enable' )
|
98 |
&& $this->getMod()->canCacheDirWrite();
|
11 |
|
12 |
const SCAN_SLUG = 'ptg';
|
13 |
|
14 |
+
protected function run() {
|
15 |
+
parent::run();
|
16 |
+
( new HackGuard\Scan\Utilities\PtgAddReinstallLinks() )
|
17 |
+
->setScanController( $this )
|
18 |
+
->execute();
|
19 |
+
}
|
20 |
+
|
21 |
/**
|
22 |
* @return Scans\Ptg\ResultsSet
|
23 |
*/
|
25 |
/** @var HackGuard\Options $opts */
|
26 |
$opts = $this->getOptions();
|
27 |
|
28 |
+
/** @var Scans\Ptg\ResultsSet $results */
|
29 |
+
$results = parent::getItemsToAutoRepair();
|
30 |
|
31 |
if ( !$opts->isRepairFilePlugin() || !$opts->isRepairFileTheme() ) {
|
32 |
if ( $opts->isRepairFileTheme() ) {
|
33 |
+
$results = $results->getResultsForThemesContext();
|
34 |
}
|
35 |
elseif ( $opts->isRepairFilePlugin() ) {
|
36 |
+
$results = $results->getResultsForPluginsContext();
|
37 |
}
|
38 |
}
|
39 |
|
40 |
+
return $results;
|
41 |
}
|
42 |
|
43 |
public function isCronAutoRepair() :bool {
|
47 |
}
|
48 |
|
49 |
/**
|
50 |
+
* @param Scans\Mal\ResultItem $item
|
51 |
* @return bool
|
52 |
*/
|
53 |
+
protected function isResultItemStale( $item ) {
|
54 |
$bStale = false;
|
55 |
+
$oAsset = ( new WpOrg\Plugin\Files() )->findPluginFromFile( $item->path_full );
|
56 |
if ( empty( $oAsset ) ) {
|
57 |
+
$oAsset = ( new WpOrg\Theme\Files() )->findThemeFromFile( $item->path_full );
|
58 |
$bStale = empty( $oAsset );
|
59 |
}
|
60 |
return $bStale;
|
67 |
return new Scans\Ptg\Utilities\ItemActionHandler();
|
68 |
}
|
69 |
|
70 |
+
public function actionPluginReinstall( string $file ) :bool {
|
71 |
+
$success = false;
|
72 |
+
$WPP = Services::WpPlugins();
|
73 |
+
$plugin = $WPP->getPluginAsVo( $file );
|
74 |
+
if ( $plugin->isWpOrg() && $WPP->reinstall( $plugin->file ) ) {
|
|
|
|
|
|
|
|
|
75 |
try {
|
76 |
( new HackGuard\Lib\Snapshots\StoreAction\Build() )
|
77 |
->setMod( $this->getMod() )
|
78 |
+
->setAsset( $plugin )
|
79 |
->run();
|
80 |
+
$success = true;
|
81 |
}
|
82 |
catch ( \Exception $e ) {
|
83 |
}
|
84 |
}
|
85 |
+
return $success;
|
86 |
}
|
87 |
|
|
|
|
|
|
|
88 |
public function isEnabled() :bool {
|
89 |
return $this->getOptions()->isOpt( 'ptg_enable', 'Y' ) && $this->getOptions()->isOptReqsMet( 'ptg_enable' );
|
90 |
}
|
91 |
|
92 |
+
public function isScanningAvailable() :bool {
|
|
|
|
|
|
|
93 |
return parent::isScanningAvailable()
|
94 |
&& $this->getOptions()->isOptReqsMet( 'ptg_enable' )
|
95 |
&& $this->getMod()->canCacheDirWrite();
|
src/lib/src/Modules/HackGuard/Scan/Controller/Ufc.php
CHANGED
@@ -18,11 +18,11 @@ class Ufc extends Base {
|
|
18 |
}
|
19 |
|
20 |
/**
|
21 |
-
* @param Scans\Mal\ResultItem $
|
22 |
* @return bool
|
23 |
*/
|
24 |
-
protected function isResultItemStale( $
|
25 |
-
return !Services::WpFs()->exists( $
|
26 |
}
|
27 |
|
28 |
public function isCronAutoRepair() :bool {
|
18 |
}
|
19 |
|
20 |
/**
|
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 |
|
28 |
public function isCronAutoRepair() :bool {
|
src/lib/src/Modules/HackGuard/Scan/Controller/Wcf.php
CHANGED
@@ -18,13 +18,13 @@ class Wcf extends Base {
|
|
18 |
}
|
19 |
|
20 |
/**
|
21 |
-
* @param Scans\Wcf\ResultItem $
|
22 |
* @return bool
|
23 |
*/
|
24 |
-
protected function isResultItemStale( $
|
25 |
$oCFH = Services::CoreFileHashes();
|
26 |
-
return !$oCFH->isCoreFile( $
|
27 |
-
|| Services::CoreFileHashes()->isCoreFileHashValid( $
|
28 |
}
|
29 |
|
30 |
public function isCronAutoRepair() :bool {
|
18 |
}
|
19 |
|
20 |
/**
|
21 |
+
* @param Scans\Wcf\ResultItem $item
|
22 |
* @return bool
|
23 |
*/
|
24 |
+
protected function isResultItemStale( $item ) {
|
25 |
$oCFH = Services::CoreFileHashes();
|
26 |
+
return !$oCFH->isCoreFile( $item->path_full )
|
27 |
+
|| Services::CoreFileHashes()->isCoreFileHashValid( $item->path_full );
|
28 |
}
|
29 |
|
30 |
public function isCronAutoRepair() :bool {
|
src/lib/src/Modules/HackGuard/Scan/Controller/Wpv.php
CHANGED
@@ -4,11 +4,57 @@ namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\HackGuard\Scan\Control
|
|
4 |
|
5 |
use FernleafSystems\Wordpress\Plugin\Shield\Modules\HackGuard;
|
6 |
use FernleafSystems\Wordpress\Plugin\Shield\Scans;
|
|
|
7 |
|
8 |
class Wpv extends BaseForAssets {
|
9 |
|
10 |
const SCAN_SLUG = 'wpv';
|
11 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
12 |
/**
|
13 |
* @return Scans\Wpv\Utilities\ItemActionHandler
|
14 |
*/
|
4 |
|
5 |
use FernleafSystems\Wordpress\Plugin\Shield\Modules\HackGuard;
|
6 |
use FernleafSystems\Wordpress\Plugin\Shield\Scans;
|
7 |
+
use FernleafSystems\Wordpress\Services\Services;
|
8 |
|
9 |
class Wpv extends BaseForAssets {
|
10 |
|
11 |
const SCAN_SLUG = 'wpv';
|
12 |
|
13 |
+
protected function run() {
|
14 |
+
parent::run();
|
15 |
+
/** @var HackGuard\Options $opts */
|
16 |
+
$opts = $this->getOptions();
|
17 |
+
|
18 |
+
add_action( 'upgrader_process_complete', function () {
|
19 |
+
$this->scheduleOnDemandScan();
|
20 |
+
}, 10, 0 );
|
21 |
+
add_action( 'deleted_plugin', function () {
|
22 |
+
$this->scheduleOnDemandScan();
|
23 |
+
}, 10, 0 );
|
24 |
+
add_action( 'load-plugins.php', function () {
|
25 |
+
( new HackGuard\Scan\Utilities\WpvAddPluginRows() )
|
26 |
+
->setScanController( $this )
|
27 |
+
->execute();
|
28 |
+
}, 10, 2 );
|
29 |
+
|
30 |
+
if ( $opts->isWpvulnAutoupdatesEnabled() ) {
|
31 |
+
add_filter( 'auto_update_plugin', [ $this, 'autoupdateVulnerablePlugins' ], PHP_INT_MAX, 2 );
|
32 |
+
}
|
33 |
+
}
|
34 |
+
|
35 |
+
/**
|
36 |
+
* @param bool $bDoAutoUpdate
|
37 |
+
* @param \stdClass|string $mItem
|
38 |
+
* @return bool
|
39 |
+
*/
|
40 |
+
public function autoupdateVulnerablePlugins( $bDoAutoUpdate, $mItem ) {
|
41 |
+
$itemFile = Services::WpGeneral()->getFileFromAutomaticUpdateItem( $mItem );
|
42 |
+
return $bDoAutoUpdate || count( $this->getPluginVulnerabilities( $itemFile ) ) > 0;
|
43 |
+
}
|
44 |
+
|
45 |
+
/**
|
46 |
+
* @param string $file
|
47 |
+
* @return Scans\Wpv\WpVulnDb\WpVulnVO[]
|
48 |
+
*/
|
49 |
+
public function getPluginVulnerabilities( $file ) {
|
50 |
+
return array_map(
|
51 |
+
function ( $item ) {
|
52 |
+
return $item->getWpVulnVo();
|
53 |
+
},
|
54 |
+
$this->getAllResults()->getItemsForSlug( $file )
|
55 |
+
);
|
56 |
+
}
|
57 |
+
|
58 |
/**
|
59 |
* @return Scans\Wpv\Utilities\ItemActionHandler
|
60 |
*/
|
src/lib/src/Modules/HackGuard/Scan/Queue/BuildScanAction.php
CHANGED
@@ -3,6 +3,7 @@
|
|
3 |
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\HackGuard\Scan\Queue;
|
4 |
|
5 |
use FernleafSystems\Wordpress\Plugin\Shield;
|
|
|
6 |
|
7 |
/**
|
8 |
* Class BuildScanAction
|
@@ -13,22 +14,22 @@ class BuildScanAction {
|
|
13 |
use Shield\Modules\ModConsumer;
|
14 |
|
15 |
/**
|
16 |
-
* @param string $
|
17 |
* @return Shield\Scans\Base\BaseScanActionVO|mixed
|
18 |
* @throws \Exception
|
19 |
*/
|
20 |
-
public function build( $
|
21 |
-
/** @var
|
22 |
-
$
|
23 |
|
24 |
-
$oAction = $
|
25 |
|
26 |
// Build the action definition:
|
27 |
|
28 |
$sClass = $oAction->getScanNamespace().'BuildScanAction';
|
29 |
/** @var Shield\Scans\Base\BaseBuildScanAction $oBuilder */
|
30 |
$oBuilder = new $sClass();
|
31 |
-
$oBuilder->setMod( $
|
32 |
->setScanActionVO( $oAction )
|
33 |
->build();
|
34 |
return $oAction;
|
3 |
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\HackGuard\Scan\Queue;
|
4 |
|
5 |
use FernleafSystems\Wordpress\Plugin\Shield;
|
6 |
+
use FernleafSystems\Wordpress\Plugin\Shield\Modules\HackGuard\ModCon;
|
7 |
|
8 |
/**
|
9 |
* Class BuildScanAction
|
14 |
use Shield\Modules\ModConsumer;
|
15 |
|
16 |
/**
|
17 |
+
* @param string $slug
|
18 |
* @return Shield\Scans\Base\BaseScanActionVO|mixed
|
19 |
* @throws \Exception
|
20 |
*/
|
21 |
+
public function build( $slug ) {
|
22 |
+
/** @var ModCon $mod */
|
23 |
+
$mod = $this->getMod();
|
24 |
|
25 |
+
$oAction = $mod->getScanCon( $slug )->getScanActionVO();
|
26 |
|
27 |
// Build the action definition:
|
28 |
|
29 |
$sClass = $oAction->getScanNamespace().'BuildScanAction';
|
30 |
/** @var Shield\Scans\Base\BaseBuildScanAction $oBuilder */
|
31 |
$oBuilder = new $sClass();
|
32 |
+
$oBuilder->setMod( $mod )
|
33 |
->setScanActionVO( $oAction )
|
34 |
->build();
|
35 |
return $oAction;
|
src/lib/src/Modules/HackGuard/Scan/Queue/CompleteQueue.php
CHANGED
@@ -4,6 +4,7 @@ namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\HackGuard\Scan\Queue;
|
|
4 |
|
5 |
use FernleafSystems\Wordpress\Plugin\Shield\Databases;
|
6 |
use FernleafSystems\Wordpress\Plugin\Shield\Modules\HackGuard;
|
|
|
7 |
use FernleafSystems\Wordpress\Plugin\Shield\Modules\ModConsumer;
|
8 |
use FernleafSystems\Wordpress\Plugin\Shield\Scans;
|
9 |
use FernleafSystems\Wordpress\Services\Services;
|
@@ -21,7 +22,7 @@ class CompleteQueue {
|
|
21 |
* Take care here not to confuse the 2x DB Handlers
|
22 |
*/
|
23 |
public function complete() {
|
24 |
-
/** @var
|
25 |
$mod = $this->getMod();
|
26 |
$con = $this->getCon();
|
27 |
/** @var Databases\ScanQueue\Handler $oDbH */
|
4 |
|
5 |
use FernleafSystems\Wordpress\Plugin\Shield\Databases;
|
6 |
use FernleafSystems\Wordpress\Plugin\Shield\Modules\HackGuard;
|
7 |
+
use FernleafSystems\Wordpress\Plugin\Shield\Modules\HackGuard\ModCon;
|
8 |
use FernleafSystems\Wordpress\Plugin\Shield\Modules\ModConsumer;
|
9 |
use FernleafSystems\Wordpress\Plugin\Shield\Scans;
|
10 |
use FernleafSystems\Wordpress\Services\Services;
|
22 |
* Take care here not to confuse the 2x DB Handlers
|
23 |
*/
|
24 |
public function complete() {
|
25 |
+
/** @var ModCon $mod */
|
26 |
$mod = $this->getMod();
|
27 |
$con = $this->getCon();
|
28 |
/** @var Databases\ScanQueue\Handler $oDbH */
|
src/lib/src/Modules/HackGuard/Scan/Queue/Controller.php
CHANGED
@@ -38,18 +38,18 @@ class Controller {
|
|
38 |
* @return bool[]
|
39 |
*/
|
40 |
public function getScansRunningStates() {
|
41 |
-
/** @var \
|
42 |
-
$
|
43 |
-
/** @var HackGuard\Options $
|
44 |
-
$
|
45 |
-
/** @var ScanQueue\Select $
|
46 |
-
$
|
47 |
|
48 |
// First clean the queue:
|
49 |
$this->cleanExpiredFromQueue();
|
50 |
|
51 |
-
$aScans = array_fill_keys( $
|
52 |
-
foreach ( $
|
53 |
$aScans[ $sInitScan ] = true;
|
54 |
}
|
55 |
return $aScans;
|
@@ -59,15 +59,15 @@ class Controller {
|
|
59 |
* @return bool
|
60 |
*/
|
61 |
protected function cleanExpiredFromQueue() {
|
62 |
-
/** @var \
|
63 |
-
$
|
64 |
-
/** @var HackGuard\Options $
|
65 |
-
$
|
66 |
$nExpiredBoundary = Services::Request()
|
67 |
->carbon()
|
68 |
-
->subSeconds( $
|
69 |
/** @var ScanQueue\Delete $oDel */
|
70 |
-
$oDel = $
|
71 |
return $oDel->addWhereOlderThan( $nExpiredBoundary )
|
72 |
->query();
|
73 |
}
|
@@ -83,18 +83,17 @@ class Controller {
|
|
83 |
* @return float
|
84 |
*/
|
85 |
public function getScanJobProgress() {
|
86 |
-
/** @var \
|
87 |
-
$
|
88 |
-
|
89 |
-
|
90 |
-
$oSel = $oDbH->getQuerySelector();
|
91 |
|
92 |
-
$aUnfinished = $
|
93 |
$nProgress = 1;
|
94 |
-
if ( $
|
95 |
-
$nInitiated = count( $
|
96 |
if ( $nInitiated > 0 ) {
|
97 |
-
$nProgress = 1 - ( count( $aUnfinished )/count( $
|
98 |
}
|
99 |
}
|
100 |
else {
|
38 |
* @return bool[]
|
39 |
*/
|
40 |
public function getScansRunningStates() {
|
41 |
+
/** @var HackGuard\ModCon $mod */
|
42 |
+
$mod = $this->getMod();
|
43 |
+
/** @var HackGuard\Options $opts */
|
44 |
+
$opts = $this->getOptions();
|
45 |
+
/** @var ScanQueue\Select $sel */
|
46 |
+
$sel = $mod->getDbHandler_ScanQueue()->getQuerySelector();
|
47 |
|
48 |
// First clean the queue:
|
49 |
$this->cleanExpiredFromQueue();
|
50 |
|
51 |
+
$aScans = array_fill_keys( $opts->getScanSlugs(), false );
|
52 |
+
foreach ( $sel->getInitiatedScans() as $sInitScan ) {
|
53 |
$aScans[ $sInitScan ] = true;
|
54 |
}
|
55 |
return $aScans;
|
59 |
* @return bool
|
60 |
*/
|
61 |
protected function cleanExpiredFromQueue() {
|
62 |
+
/** @var HackGuard\ModCon $mod */
|
63 |
+
$mod = $this->getMod();
|
64 |
+
/** @var HackGuard\Options $opts */
|
65 |
+
$opts = $this->getOptions();
|
66 |
$nExpiredBoundary = Services::Request()
|
67 |
->carbon()
|
68 |
+
->subSeconds( $opts->getMalQueueExpirationInterval() )->timestamp;
|
69 |
/** @var ScanQueue\Delete $oDel */
|
70 |
+
$oDel = $mod->getDbHandler_ScanQueue()->getQueryDeleter();
|
71 |
return $oDel->addWhereOlderThan( $nExpiredBoundary )
|
72 |
->query();
|
73 |
}
|
83 |
* @return float
|
84 |
*/
|
85 |
public function getScanJobProgress() {
|
86 |
+
/** @var HackGuard\ModCon $mod */
|
87 |
+
$mod = $this->getMod();
|
88 |
+
/** @var ScanQueue\Select $sel */
|
89 |
+
$sel = $mod->getDbHandler_ScanQueue()->getQuerySelector();
|
|
|
90 |
|
91 |
+
$aUnfinished = $sel->getUnfinishedScans();
|
92 |
$nProgress = 1;
|
93 |
+
if ( $sel->getUnfinishedScans() > 0 ) {
|
94 |
+
$nInitiated = count( $sel->getInitiatedScans() );
|
95 |
if ( $nInitiated > 0 ) {
|
96 |
+
$nProgress = 1 - ( count( $aUnfinished )/count( $sel->getInitiatedScans() ) );
|
97 |
}
|
98 |
}
|
99 |
else {
|
src/lib/src/Modules/HackGuard/Scan/Queue/QueueProcessor.php
CHANGED
@@ -4,6 +4,7 @@ namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\HackGuard\Scan\Queue;
|
|
4 |
|
5 |
use FernleafSystems\Wordpress\Plugin\Shield;
|
6 |
use FernleafSystems\Wordpress\Plugin\Shield\Databases\ScanQueue;
|
|
|
7 |
use FernleafSystems\Wordpress\Services\Services;
|
8 |
use FernleafSystems\Wordpress\Services\Utilities;
|
9 |
|
@@ -151,8 +152,8 @@ class QueueProcessor extends Utilities\BackgroundProcessing\BackgroundProcess {
|
|
151 |
* @return ScanQueue\Handler
|
152 |
*/
|
153 |
public function getDbHandler() {
|
154 |
-
/** @var
|
155 |
-
$
|
156 |
-
return $
|
157 |
}
|
158 |
}
|
4 |
|
5 |
use FernleafSystems\Wordpress\Plugin\Shield;
|
6 |
use FernleafSystems\Wordpress\Plugin\Shield\Databases\ScanQueue;
|
7 |
+
use FernleafSystems\Wordpress\Plugin\Shield\Modules\HackGuard\ModCon;
|
8 |
use FernleafSystems\Wordpress\Services\Services;
|
9 |
use FernleafSystems\Wordpress\Services\Utilities;
|
10 |
|
152 |
* @return ScanQueue\Handler
|
153 |
*/
|
154 |
public function getDbHandler() {
|
155 |
+
/** @var ModCon $mod */
|
156 |
+
$mod = $this->getMod();
|
157 |
+
return $mod->getDbHandler_ScanQueue();
|
158 |
}
|
159 |
}
|
src/lib/src/Modules/HackGuard/Scan/Queue/ScanExecute.php
CHANGED
@@ -19,16 +19,16 @@ class ScanExecute {
|
|
19 |
* @throws \Exception
|
20 |
*/
|
21 |
public function execute( $oEntry ) {
|
22 |
-
/** @var \
|
23 |
-
$
|
24 |
-
$oDbH = $
|
25 |
$oTypeConverter = ( new ConvertBetweenTypes() )->setDbHandler( $oDbH );
|
26 |
|
27 |
$oAction = $oTypeConverter->fromDbEntryToAction( $oEntry );
|
28 |
|
29 |
$this->getScanner( $oAction )
|
30 |
->setScanActionVO( $oAction )
|
31 |
-
->setMod( $
|
32 |
->run();
|
33 |
|
34 |
if ( $oAction->usleep > 0 ) {
|
19 |
* @throws \Exception
|
20 |
*/
|
21 |
public function execute( $oEntry ) {
|
22 |
+
/** @var Shield\Modules\HackGuard\ModCon $mod */
|
23 |
+
$mod = $this->getMod();
|
24 |
+
$oDbH = $mod->getDbHandler_ScanQueue();
|
25 |
$oTypeConverter = ( new ConvertBetweenTypes() )->setDbHandler( $oDbH );
|
26 |
|
27 |
$oAction = $oTypeConverter->fromDbEntryToAction( $oEntry );
|
28 |
|
29 |
$this->getScanner( $oAction )
|
30 |
->setScanActionVO( $oAction )
|
31 |
+
->setMod( $mod )
|
32 |
->run();
|
33 |
|
34 |
if ( $oAction->usleep > 0 ) {
|
src/lib/src/Modules/HackGuard/Scan/Queue/ScanInitiate.php
CHANGED
@@ -2,6 +2,7 @@
|
|
2 |
|
3 |
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\HackGuard\Scan\Queue;
|
4 |
|
|
|
5 |
use FernleafSystems\Wordpress\Plugin\Shield\Modules\ModConsumer;
|
6 |
use FernleafSystems\Wordpress\Plugin\Shield\Scans;
|
7 |
|
@@ -16,24 +17,24 @@ class ScanInitiate {
|
|
16 |
|
17 |
/**
|
18 |
* Build and Enqueue.
|
19 |
-
* @param string $
|
20 |
* @throws \Exception
|
21 |
*/
|
22 |
-
public function init( $
|
23 |
-
/** @var
|
24 |
-
$
|
25 |
-
$
|
26 |
|
27 |
-
if ( ( new IsScanEnqueued() )->setDbHandler( $
|
28 |
throw new \Exception( 'Scan is already running' );
|
29 |
}
|
30 |
|
31 |
$oAction = ( new BuildScanAction() )
|
32 |
-
->setMod( $
|
33 |
-
->build( $
|
34 |
|
35 |
( new ScanEnqueue() )
|
36 |
-
->setDbHandler( $
|
37 |
->setQueueProcessor( $this->getQueueProcessor() )
|
38 |
->setScanActionVO( $oAction )
|
39 |
->enqueue();
|
2 |
|
3 |
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\HackGuard\Scan\Queue;
|
4 |
|
5 |
+
use FernleafSystems\Wordpress\Plugin\Shield\Modules\HackGuard\ModCon;
|
6 |
use FernleafSystems\Wordpress\Plugin\Shield\Modules\ModConsumer;
|
7 |
use FernleafSystems\Wordpress\Plugin\Shield\Scans;
|
8 |
|
17 |
|
18 |
/**
|
19 |
* Build and Enqueue.
|
20 |
+
* @param string $slug
|
21 |
* @throws \Exception
|
22 |
*/
|
23 |
+
public function init( $slug ) {
|
24 |
+
/** @var ModCon $mod */
|
25 |
+
$mod = $this->getMod();
|
26 |
+
$dbh = $mod->getDbHandler_ScanQueue();
|
27 |
|
28 |
+
if ( ( new IsScanEnqueued() )->setDbHandler( $dbh )->check( $slug ) ) {
|
29 |
throw new \Exception( 'Scan is already running' );
|
30 |
}
|
31 |
|
32 |
$oAction = ( new BuildScanAction() )
|
33 |
+
->setMod( $mod )
|
34 |
+
->build( $slug );
|
35 |
|
36 |
( new ScanEnqueue() )
|
37 |
+
->setDbHandler( $dbh )
|
38 |
->setQueueProcessor( $this->getQueueProcessor() )
|
39 |
->setScanActionVO( $oAction )
|
40 |
->enqueue();
|
src/lib/src/Modules/HackGuard/Scan/ScanActionFromSlug.php
CHANGED
@@ -1,42 +1,35 @@
|
|
1 |
-
<?php
|
2 |
|
3 |
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\HackGuard\Scan;
|
4 |
|
5 |
-
use FernleafSystems\Wordpress\Plugin\Shield;
|
|
|
6 |
|
7 |
-
/**
|
8 |
-
* Class ScanActionFromSlug
|
9 |
-
* @package FernleafSystems\Wordpress\Plugin\Shield\Modules\HackGuard\Scan
|
10 |
-
*/
|
11 |
class ScanActionFromSlug {
|
12 |
|
13 |
-
|
14 |
-
|
15 |
-
|
16 |
-
|
17 |
-
|
18 |
-
$oVO = null;
|
19 |
-
switch ( $sScanSlug ) {
|
20 |
-
case 'apc':
|
21 |
-
$oVO = new Shield\Scans\Apc\ScanActionVO();
|
22 |
break;
|
23 |
-
case
|
24 |
-
$
|
25 |
break;
|
26 |
-
case
|
27 |
-
$
|
28 |
break;
|
29 |
-
case
|
30 |
-
$
|
31 |
break;
|
32 |
-
case
|
33 |
-
$
|
34 |
break;
|
35 |
-
case
|
36 |
-
$
|
37 |
break;
|
38 |
}
|
39 |
-
$
|
40 |
-
return $
|
41 |
}
|
42 |
}
|
1 |
+
<?php declare( strict_types=1 );
|
2 |
|
3 |
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\HackGuard\Scan;
|
4 |
|
5 |
+
use FernleafSystems\Wordpress\Plugin\Shield\Modules\HackGuard\Scan\Controller;
|
6 |
+
use FernleafSystems\Wordpress\Plugin\Shield\Scans;
|
7 |
|
|
|
|
|
|
|
|
|
8 |
class ScanActionFromSlug {
|
9 |
|
10 |
+
public static function GetAction( string $slug ) :Scans\Base\BaseScanActionVO {
|
11 |
+
$VO = null;
|
12 |
+
switch ( $slug ) {
|
13 |
+
case Controller\Apc::SCAN_SLUG:
|
14 |
+
$VO = new Scans\Apc\ScanActionVO();
|
|
|
|
|
|
|
|
|
15 |
break;
|
16 |
+
case Controller\Mal::SCAN_SLUG:
|
17 |
+
$VO = new Scans\Mal\ScanActionVO();
|
18 |
break;
|
19 |
+
case Controller\Ptg::SCAN_SLUG:
|
20 |
+
$VO = new Scans\Ptg\ScanActionVO();
|
21 |
break;
|
22 |
+
case Controller\Ufc::SCAN_SLUG:
|
23 |
+
$VO = new Scans\Ufc\ScanActionVO();
|
24 |
break;
|
25 |
+
case Controller\Wcf::SCAN_SLUG:
|
26 |
+
$VO = new Scans\Wcf\ScanActionVO();
|
27 |
break;
|
28 |
+
case Controller\Wpv::SCAN_SLUG:
|
29 |
+
$VO = new Scans\Wpv\ScanActionVO();
|
30 |
break;
|
31 |
}
|
32 |
+
$VO->scan = $slug;
|
33 |
+
return $VO;
|
34 |
}
|
35 |
}
|
src/lib/src/Modules/HackGuard/Scan/ScansController.php
ADDED
@@ -0,0 +1,185 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php declare( strict_types=1 );
|
2 |
+
|
3 |
+
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\HackGuard\Scan;
|
4 |
+
|
5 |
+
use FernleafSystems\Utilities\Logic\OneTimeExecute;
|
6 |
+
use FernleafSystems\Wordpress\Plugin\Shield\Crons\StandardCron;
|
7 |
+
use FernleafSystems\Wordpress\Plugin\Shield\Databases;
|
8 |
+
use FernleafSystems\Wordpress\Plugin\Shield\Modules\HackGuard;
|
9 |
+
use FernleafSystems\Wordpress\Plugin\Shield\Modules\HackGuard\Options;
|
10 |
+
use FernleafSystems\Wordpress\Plugin\Shield\Modules\ModConsumer;
|
11 |
+
use FernleafSystems\Wordpress\Services\Services;
|
12 |
+
|
13 |
+
class ScansController {
|
14 |
+
|
15 |
+
use ModConsumer;
|
16 |
+
use OneTimeExecute;
|
17 |
+
use StandardCron;
|
18 |
+
|
19 |
+
private $scanCons;
|
20 |
+
|
21 |
+
public function __construct() {
|
22 |
+
$this->scanCons = [];
|
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 |
+
}
|
31 |
+
$this->setupCron();
|
32 |
+
$this->handlePostScanCron();
|
33 |
+
}
|
34 |
+
|
35 |
+
/**
|
36 |
+
* @return Controller\Base[]
|
37 |
+
*/
|
38 |
+
public function getAllScanCons() :array {
|
39 |
+
/** @var Options $opts */
|
40 |
+
$opts = $this->getOptions();
|
41 |
+
foreach ( $opts->getScanSlugs() as $slug ) {
|
42 |
+
try {
|
43 |
+
$this->getScanCon( $slug );
|
44 |
+
}
|
45 |
+
catch ( \Exception $e ) {
|
46 |
+
}
|
47 |
+
}
|
48 |
+
return $this->scanCons;
|
49 |
+
}
|
50 |
+
|
51 |
+
/**
|
52 |
+
* @return int[] - key is scan slug
|
53 |
+
*/
|
54 |
+
public function getLastScansAt() :array {
|
55 |
+
/** @var Options $opts */
|
56 |
+
$opts = $this->getOptions();
|
57 |
+
/** @var Databases\Events\Select $oSel */
|
58 |
+
$oSel = $this->getCon()
|
59 |
+
->getModule_Events()
|
60 |
+
->getDbHandler_Events()
|
61 |
+
->getQuerySelector();
|
62 |
+
$aEvents = $oSel->getLatestForAllEvents();
|
63 |
+
|
64 |
+
$aLatest = [];
|
65 |
+
foreach ( $opts->getScanSlugs() as $slug ) {
|
66 |
+
$event = $slug.'_scan_run';
|
67 |
+
$aLatest[ $slug ] = isset( $aEvents[ $event ] ) ? (int)$aEvents[ $event ]->created_at : 0;
|
68 |
+
}
|
69 |
+
return $aLatest;
|
70 |
+
}
|
71 |
+
|
72 |
+
/**
|
73 |
+
* @param string $slug
|
74 |
+
* @return Controller\Base|mixed
|
75 |
+
* @throws \Exception
|
76 |
+
*/
|
77 |
+
public function getScanCon( string $slug ) {
|
78 |
+
if ( !isset( $this->scanCons[ $slug ] ) ) {
|
79 |
+
$class = __NAMESPACE__.'\\Controller\\'.ucwords( $slug );
|
80 |
+
if ( @class_exists( $class ) ) {
|
81 |
+
/** @var Controller\Base $obj */
|
82 |
+
$obj = new $class();
|
83 |
+
$this->scanCons[ $slug ] = $obj->setMod( $this->getMod() );
|
84 |
+
}
|
85 |
+
else {
|
86 |
+
throw new \Exception( 'Scan slug does not have a class: '.$slug );
|
87 |
+
}
|
88 |
+
}
|
89 |
+
return $this->scanCons[ $slug ];
|
90 |
+
}
|
91 |
+
|
92 |
+
private function handlePostScanCron() {
|
93 |
+
add_action( $this->getCon()->prefix( 'post_scan' ), function () {
|
94 |
+
$this->runAutoRepair();
|
95 |
+
} );
|
96 |
+
}
|
97 |
+
|
98 |
+
private function runAutoRepair() {
|
99 |
+
/** @var HackGuard\ModCon $mod */
|
100 |
+
$mod = $this->getMod();
|
101 |
+
/** @var HackGuard\Options $opts */
|
102 |
+
$opts = $this->getOptions();
|
103 |
+
foreach ( $opts->getScanSlugs() as $sSlug ) {
|
104 |
+
$oScanCon = $mod->getScanCon( $sSlug );
|
105 |
+
if ( $oScanCon->isCronAutoRepair() ) {
|
106 |
+
$oScanCon->runCronAutoRepair();
|
107 |
+
}
|
108 |
+
}
|
109 |
+
}
|
110 |
+
|
111 |
+
/**
|
112 |
+
* Cron callback
|
113 |
+
*/
|
114 |
+
public function runCron() {
|
115 |
+
Services::WpGeneral()->getIfAutoUpdatesInstalled() ? $this->resetCron() : $this->cronScan();
|
116 |
+
}
|
117 |
+
|
118 |
+
private function cronScan() {
|
119 |
+
/** @var HackGuard\ModCon $mod */
|
120 |
+
$mod = $this->getMod();
|
121 |
+
/** @var HackGuard\Options $opts */
|
122 |
+
$opts = $this->getOptions();
|
123 |
+
|
124 |
+
if ( $this->getCanScansExecute() ) {
|
125 |
+
$aScans = [];
|
126 |
+
foreach ( $opts->getScanSlugs() as $sScanSlug ) {
|
127 |
+
$oScanCon = $mod->getScanCon( $sScanSlug );
|
128 |
+
if ( $oScanCon->isScanningAvailable() && $oScanCon->isEnabled() ) {
|
129 |
+
$aScans[] = $sScanSlug;
|
130 |
+
}
|
131 |
+
}
|
132 |
+
|
133 |
+
$opts->setIsScanCron( true );
|
134 |
+
$mod->saveModOptions()
|
135 |
+
->getScanQueueController()
|
136 |
+
->startScans( $aScans );
|
137 |
+
}
|
138 |
+
else {
|
139 |
+
error_log( 'Shield scans cannot execute.' );
|
140 |
+
}
|
141 |
+
}
|
142 |
+
|
143 |
+
/**
|
144 |
+
* @return string[]
|
145 |
+
*/
|
146 |
+
public function getReasonsScansCantExecute() :array {
|
147 |
+
return array_keys( array_filter( [
|
148 |
+
'reason_not_call_self' => !$this->getCon()->getModule_Plugin()->getCanSiteCallToItself()
|
149 |
+
] ) );
|
150 |
+
}
|
151 |
+
|
152 |
+
public function getCanScansExecute() :bool {
|
153 |
+
return count( $this->getReasonsScansCantExecute() ) === 0;
|
154 |
+
}
|
155 |
+
|
156 |
+
protected function getCronFrequency() {
|
157 |
+
/** @var HackGuard\Options $opts */
|
158 |
+
$opts = $this->getOptions();
|
159 |
+
return $opts->getScanFrequency();
|
160 |
+
}
|
161 |
+
|
162 |
+
public function getFirstRunTimestamp() :int {
|
163 |
+
$c = Services::Request()->carbon( true );
|
164 |
+
$c->addHours( $c->minute < 40 ? 0 : 1 )
|
165 |
+
->minute( $c->minute < 40 ? 45 : 15 )
|
166 |
+
->second( 0 );
|
167 |
+
|
168 |
+
if ( $this->getCronFrequency() === 1 ) { // If it's a daily scan only, set to 3am by default
|
169 |
+
$hour = (int)apply_filters( $this->getCon()->prefix( 'daily_scan_cron_hour' ), 3 );
|
170 |
+
if ( $hour < 0 || $hour > 23 ) {
|
171 |
+
$hour = 3;
|
172 |
+
}
|
173 |
+
if ( $c->hour >= $hour ) {
|
174 |
+
$c->addDays( 1 );
|
175 |
+
}
|
176 |
+
$c->hour( $hour );
|
177 |
+
}
|
178 |
+
|
179 |
+
return $c->timestamp;
|
180 |
+
}
|
181 |
+
|
182 |
+
protected function getCronName() :string {
|
183 |
+
return $this->getCon()->prefix( $this->getOptions()->getDef( 'cron_all_scans' ) );
|
184 |
+
}
|
185 |
+
}
|
src/lib/src/Modules/HackGuard/Scan/Utilities/PtgAddReinstallLinks.php
ADDED
@@ -0,0 +1,112 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php declare( strict_types=1 );
|
2 |
+
|
3 |
+
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\HackGuard\Scan\Utilities;
|
4 |
+
|
5 |
+
use FernleafSystems\Utilities\Logic\OneTimeExecute;
|
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;
|
9 |
+
use FernleafSystems\Wordpress\Services\Services;
|
10 |
+
|
11 |
+
class PtgAddReinstallLinks {
|
12 |
+
|
13 |
+
use Controller\ScanControllerConsumer;
|
14 |
+
use OneTimeExecute;
|
15 |
+
|
16 |
+
/**
|
17 |
+
* @var int
|
18 |
+
*/
|
19 |
+
private $nColumnsCount;
|
20 |
+
|
21 |
+
/**
|
22 |
+
* @var int
|
23 |
+
*/
|
24 |
+
private $vulnCount;
|
25 |
+
|
26 |
+
protected function run() {
|
27 |
+
add_action( 'plugin_action_links', function ( $links, $file ) {
|
28 |
+
if ( is_array( $links ) && is_string( $file ) ) {
|
29 |
+
$links = $this->addActionLinkRefresh( $links, $file );
|
30 |
+
}
|
31 |
+
return $links;
|
32 |
+
}, 50, 2 );
|
33 |
+
add_action( 'admin_footer', function () {
|
34 |
+
$this->printPluginReinstallDialogs();
|
35 |
+
} );
|
36 |
+
add_action( 'admin_enqueue_scripts', function ( $hook ) {
|
37 |
+
if ( $hook === 'plugins.php' ) {
|
38 |
+
$this->insertCustomJsVars();
|
39 |
+
}
|
40 |
+
}, 9 ); // >5 && <10
|
41 |
+
}
|
42 |
+
|
43 |
+
protected function canRun() {
|
44 |
+
$scanCon = $this->getScanController();
|
45 |
+
/** @var HackGuard\Options $opts */
|
46 |
+
$opts = $scanCon->getOptions();
|
47 |
+
return $scanCon->isReady() && $opts->isPtgReinstallLinks();
|
48 |
+
}
|
49 |
+
|
50 |
+
private function addActionLinkRefresh( array $links, string $file ) :array {
|
51 |
+
$WPP = Services::WpPlugins();
|
52 |
+
|
53 |
+
$plugin = $WPP->getPluginAsVo( $file );
|
54 |
+
if ( $plugin instanceof WpPluginVo && $plugin->isWpOrg() && !$WPP->isUpdateAvailable( $file ) ) {
|
55 |
+
$template = '<a href="javascript:void(0)">%s</a>';
|
56 |
+
$links[ 'icwp-reinstall' ] = sprintf( $template, __( 'Re-Install', 'wp-simple-firewall' ) );
|
57 |
+
}
|
58 |
+
|
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()
|
87 |
+
->renderTemplate(
|
88 |
+
'snippets/dialog_plugins_reinstall.twig',
|
89 |
+
[
|
90 |
+
'strings' => [
|
91 |
+
'are_you_sure' => __( 'Are you sure?', 'wp-simple-firewll' ),
|
92 |
+
'really_reinstall' => __( 'Really Re-Install Plugin', 'wp-simple-firewll' ),
|
93 |
+
'wp_reinstall' => __( 'WordPress will now download and install the latest available version of this plugin.', 'wp-simple-firewll' ),
|
94 |
+
'in_case' => sprintf( '%s: %s',
|
95 |
+
__( 'Note', 'wp-simple-firewall' ),
|
96 |
+
__( 'In case of possible failure, it may be better to do this while the plugin is inactive.', 'wp-simple-firewll' )
|
97 |
+
),
|
98 |
+
'reinstall_first' => __( 'Re-install first?', 'wp-simple-firewall' ),
|
99 |
+
'corrupted' => __( "This ensures files for this plugin haven't been corrupted in any way.", 'wp-simple-firewall' ),
|
100 |
+
'choose' => __( "You can choose to 'Activate Only' (not recommended), or close this message to cancel activation.", 'wp-simple-firewall' ),
|
101 |
+
'editing_restricted' => __( 'Editing this option is currently restricted.', 'wp-simple-firewall' ),
|
102 |
+
'download' => sprintf(
|
103 |
+
__( 'For best security practices, %s will download and re-install the latest available version of this plugin.', 'wp-simple-firewall' ),
|
104 |
+
$scanCon->getCon()->getHumanName()
|
105 |
+
)
|
106 |
+
],
|
107 |
+
'js_snippets' => []
|
108 |
+
],
|
109 |
+
true
|
110 |
+
);
|
111 |
+
}
|
112 |
+
}
|
src/lib/src/Modules/HackGuard/Scan/Utilities/WpvAddPluginRows.php
ADDED
@@ -0,0 +1,151 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php declare( strict_types=1 );
|
2 |
+
|
3 |
+
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\HackGuard\Scan\Utilities;
|
4 |
+
|
5 |
+
use FernleafSystems\Utilities\Logic\OneTimeExecute;
|
6 |
+
use FernleafSystems\Wordpress\Plugin\Shield\Modules\HackGuard\Scan\Controller;
|
7 |
+
use FernleafSystems\Wordpress\Plugin\Shield\Scans\Wpv;
|
8 |
+
use FernleafSystems\Wordpress\Services\Services;
|
9 |
+
|
10 |
+
class WpvAddPluginRows {
|
11 |
+
|
12 |
+
use Controller\ScanControllerConsumer;
|
13 |
+
use OneTimeExecute;
|
14 |
+
|
15 |
+
/**
|
16 |
+
* @var int
|
17 |
+
*/
|
18 |
+
private $nColumnsCount;
|
19 |
+
|
20 |
+
/**
|
21 |
+
* @var int
|
22 |
+
*/
|
23 |
+
private $vulnCount;
|
24 |
+
|
25 |
+
protected function run() {
|
26 |
+
$this->addPluginVulnerabilityRows();
|
27 |
+
}
|
28 |
+
|
29 |
+
protected function canRun() {
|
30 |
+
return $this->isWpvulnPluginsHighlightEnabled() && $this->countVulnerablePlugins() > 0;
|
31 |
+
}
|
32 |
+
|
33 |
+
private function isWpvulnPluginsHighlightEnabled() :bool {
|
34 |
+
$scanCon = $this->getScanController();
|
35 |
+
if ( $scanCon->isEnabled() ) {
|
36 |
+
$opt = apply_filters( 'icwp_shield_wpvuln_scan_display', 'securityadmin' );
|
37 |
+
}
|
38 |
+
else {
|
39 |
+
$opt = 'disabled';
|
40 |
+
}
|
41 |
+
return ( $opt != 'disabled' ) && Services::WpUsers()->isUserAdmin()
|
42 |
+
&& ( ( $opt != 'securityadmin' ) || $scanCon->getCon()->isPluginAdmin() );
|
43 |
+
}
|
44 |
+
|
45 |
+
private function addPluginVulnerabilityRows() {
|
46 |
+
// These 3 add the 'Vulnerable' plugin status view.
|
47 |
+
// BUG: when vulnerable is active, only 1 plugin is available to "All" status. don't know fix.
|
48 |
+
add_action( 'pre_current_active_plugins', [ $this, 'addVulnerablePluginStatusView' ], 1000 );
|
49 |
+
add_filter( 'all_plugins', [ $this, 'filterPluginsToView' ], 1000 );
|
50 |
+
add_filter( 'views_plugins', [ $this, 'addPluginsStatusViewLink' ], 1000 );
|
51 |
+
add_filter( 'manage_plugins_columns', [ $this, 'fCountColumns' ], 1000 );
|
52 |
+
foreach ( Services::WpPlugins()->getInstalledPluginFiles() as $file ) {
|
53 |
+
add_action( "after_plugin_row_$file", [ $this, 'attachVulnerabilityWarning' ], 100, 2 );
|
54 |
+
}
|
55 |
+
}
|
56 |
+
|
57 |
+
public function addVulnerablePluginStatusView() {
|
58 |
+
if ( Services::Request()->query( 'plugin_status' ) == 'vulnerable' ) {
|
59 |
+
global $status;
|
60 |
+
$status = 'vulnerable';
|
61 |
+
}
|
62 |
+
add_filter( 'views_plugins', [ $this, 'addPluginsStatusViewLink' ], 1000 );
|
63 |
+
}
|
64 |
+
|
65 |
+
/**
|
66 |
+
* FILTER
|
67 |
+
* @param array $views
|
68 |
+
* @return array
|
69 |
+
*/
|
70 |
+
public function addPluginsStatusViewLink( $views ) {
|
71 |
+
global $status;
|
72 |
+
|
73 |
+
$views[ 'vulnerable' ] = sprintf( "<a href='%s' %s>%s</a>",
|
74 |
+
add_query_arg( 'plugin_status', 'vulnerable', 'plugins.php' ),
|
75 |
+
( 'vulnerable' === $status ) ? ' class="current"' : '',
|
76 |
+
sprintf( '%s <span class="count">(%s)</span>',
|
77 |
+
__( 'Vulnerable', 'wp-simple-firewall' ),
|
78 |
+
number_format_i18n( $this->countVulnerablePlugins() )
|
79 |
+
)
|
80 |
+
);
|
81 |
+
return $views;
|
82 |
+
}
|
83 |
+
|
84 |
+
/**
|
85 |
+
* FILTER
|
86 |
+
* @param array $plugins
|
87 |
+
* @return array
|
88 |
+
*/
|
89 |
+
public function filterPluginsToView( $plugins ) {
|
90 |
+
if ( Services::Request()->query( 'plugin_status' ) == 'vulnerable' ) {
|
91 |
+
/** @var Wpv\ResultsSet $oVulnerableRes */
|
92 |
+
$oVulnerableRes = $this->getScanController()->getAllResults();
|
93 |
+
global $status;
|
94 |
+
$status = 'vulnerable';
|
95 |
+
$plugins = array_intersect_key(
|
96 |
+
$plugins,
|
97 |
+
array_flip( $oVulnerableRes->getUniqueSlugs() )
|
98 |
+
);
|
99 |
+
}
|
100 |
+
return $plugins;
|
101 |
+
}
|
102 |
+
|
103 |
+
/**
|
104 |
+
* @param string $pluginFile
|
105 |
+
* @param array $pData
|
106 |
+
*/
|
107 |
+
public function attachVulnerabilityWarning( $pluginFile, $pData ) {
|
108 |
+
$scanCon = $this->getScanController();
|
109 |
+
|
110 |
+
$vulns = $scanCon->getPluginVulnerabilities( $pluginFile );
|
111 |
+
if ( count( $vulns ) > 0 ) {
|
112 |
+
$sOurName = $scanCon->getCon()->getHumanName();
|
113 |
+
echo $scanCon->getMod()
|
114 |
+
->renderTemplate(
|
115 |
+
'snippets/plugin-vulnerability.php',
|
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 |
+
$sOurName, '<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 |
+
'vulns' => $vulns,
|
126 |
+
'nColspan' => $this->nColumnsCount
|
127 |
+
]
|
128 |
+
);
|
129 |
+
}
|
130 |
+
}
|
131 |
+
|
132 |
+
/**
|
133 |
+
* @param array $cols
|
134 |
+
* @return array
|
135 |
+
*/
|
136 |
+
public function fCountColumns( $cols ) {
|
137 |
+
if ( !isset( $this->nColumnsCount ) ) {
|
138 |
+
$this->nColumnsCount = count( $cols );
|
139 |
+
}
|
140 |
+
return $cols;
|
141 |
+
}
|
142 |
+
|
143 |
+
private function countVulnerablePlugins() :int {
|
144 |
+
if ( !isset( $this->vulnCount ) ) {
|
145 |
+
$this->vulnCount = $this->getScanController()
|
146 |
+
->getAllResults()
|
147 |
+
->countUniqueSlugsForPluginsContext();
|
148 |
+
}
|
149 |
+
return $this->vulnCount;
|
150 |
+
}
|
151 |
+
}
|
src/lib/src/Modules/HackGuard/Strings.php
CHANGED
@@ -8,13 +8,8 @@ use FernleafSystems\Wordpress\Services\Services;
|
|
8 |
|
9 |
class Strings extends Base\Strings {
|
10 |
|
11 |
-
|
12 |
-
|
13 |
-
* @return string|null
|
14 |
-
*/
|
15 |
-
public function getScanName( $sSlug ) {
|
16 |
-
$aN = $this->getScanNames();
|
17 |
-
return isset( $aN[ $sSlug ] ) ? $aN[ $sSlug ] : null;
|
18 |
}
|
19 |
|
20 |
/**
|
@@ -175,62 +170,62 @@ class Strings extends Base\Strings {
|
|
175 |
* @throws \Exception
|
176 |
*/
|
177 |
public function getOptionStrings( string $key ) :array {
|
178 |
-
/** @var
|
179 |
$mod = $this->getMod();
|
180 |
-
$
|
181 |
|
182 |
switch ( $key ) {
|
183 |
|
184 |
case 'enable_hack_protect' :
|
185 |
-
$
|
186 |
-
$
|
187 |
-
$
|
188 |
break;
|
189 |
|
190 |
case 'scan_frequency' :
|
191 |
-
$
|
192 |
-
$
|
193 |
-
$
|
194 |
sprintf( '%s: %s', __( 'Default', 'wp-simple-firewall' ), __( 'Once every 24hrs.', 'wp-simple-firewall' ) ),
|
195 |
__( 'To improve security, increase the number of scans per day.', 'wp-simple-firewall' )
|
196 |
];
|
197 |
break;
|
198 |
|
199 |
case 'enable_plugin_vulnerabilities_scan' :
|
200 |
-
$
|
201 |
-
$
|
202 |
-
$
|
203 |
break;
|
204 |
|
205 |
case 'enable_wpvuln_scan' :
|
206 |
-
$
|
207 |
-
$
|
208 |
-
$
|
209 |
break;
|
210 |
|
211 |
case 'wpvuln_scan_autoupdate' :
|
212 |
-
$
|
213 |
-
$
|
214 |
-
$
|
215 |
break;
|
216 |
|
217 |
case 'enable_core_file_integrity_scan' :
|
218 |
-
$
|
219 |
Services::WpGeneral()->isClassicPress() ? 'ClassicPress' : 'WordPress'
|
220 |
);
|
221 |
-
$
|
222 |
Services::WpGeneral()->isClassicPress() ? 'ClassicPress' : 'WordPress'
|
223 |
);
|
224 |
-
$
|
225 |
sprintf( '%s - %s', __( 'Purpose', 'wp-simple-firewall' ), __( 'Regularly scan your WordPress core files for changes compared to official WordPress files.', 'wp-simple-firewall' ) ),
|
226 |
sprintf( '%s - %s', __( 'Recommendation', 'wp-simple-firewall' ), __( 'Keep this feature turned on, at all times.', 'wp-simple-firewall' ) )
|
227 |
];
|
228 |
break;
|
229 |
|
230 |
case 'mal_scan_enable' :
|
231 |
-
$
|
232 |
-
$
|
233 |
-
$
|
234 |
sprintf( '%s - %s', __( 'Purpose', 'wp-simple-firewall' ), __( 'Monitor and detect presence of Malware signatures.', 'wp-simple-firewall' ) ),
|
235 |
sprintf( '%s - %s', __( 'Recommendation', 'wp-simple-firewall' ), __( 'Keep this feature turned on, at all times.', 'wp-simple-firewall' ) ),
|
236 |
sprintf( '%s - %s', __( 'Note', 'wp-simple-firewall' ), __( 'Currently files of the following types are supported:', 'wp-simple-firewall' ) )
|
@@ -239,24 +234,24 @@ class Strings extends Base\Strings {
|
|
239 |
break;
|
240 |
|
241 |
case 'ptg_enable' :
|
242 |
-
$
|
243 |
-
$
|
244 |
|
245 |
-
$
|
246 |
__( "Looks for new files added to plugins or themes, and also for changes to existing files.", 'wp-simple-firewall' ),
|
247 |
sprintf( '%s - %s', __( 'Important', 'wp-simple-firewall' ), __( "Doesn't currently detect missing files.", 'wp-simple-firewall' ) ),
|
248 |
sprintf( '%s - %s', __( 'Recommendation', 'wp-simple-firewall' ), __( 'Keep this feature turned on, at all times.', 'wp-simple-firewall' ) )
|
249 |
];
|
250 |
if ( !$mod->canCacheDirWrite() ) {
|
251 |
-
$
|
252 |
'<code>'.$mod->getPtgSnapsBaseDir().'</code>' );
|
253 |
}
|
254 |
break;
|
255 |
|
256 |
case 'file_repair_areas' :
|
257 |
-
$
|
258 |
-
$
|
259 |
-
$
|
260 |
__( 'Will attempt to automatically repair files that have been changed or infected with malware.', 'wp-simple-firewall' ),
|
261 |
'- '.__( 'In the case of WordPress, original files will be downloaded from WordPress.org to repair any broken files.', 'wp-simple-firewall' ),
|
262 |
'- '.__( 'In the case of plugins & themes, only those installed from WordPress.org may be repaired.', 'wp-simple-firewall' ),
|
@@ -266,9 +261,9 @@ class Strings extends Base\Strings {
|
|
266 |
break;
|
267 |
|
268 |
case 'file_locker' :
|
269 |
-
$
|
270 |
-
$
|
271 |
-
$
|
272 |
__( 'Detects changes to the files, then lets you examine contents and revert as required.', 'wp-simple-firewall' ),
|
273 |
sprintf( '%s: %s', __( 'Note', 'wp-simple-firewall' ), __( 'Web.Config is only available for Windows/IIS.', 'wp-simple-firewall' ) )
|
274 |
];
|
@@ -277,118 +272,118 @@ class Strings extends Base\Strings {
|
|
277 |
->setMod( $this->getMod() )
|
278 |
->loadLocks();
|
279 |
if ( !empty( $aLocks ) ) {
|
280 |
-
$
|
281 |
foreach ( $aLocks as $oLock ) {
|
282 |
-
$
|
283 |
}
|
284 |
}
|
285 |
break;
|
286 |
|
287 |
case 'enable_unrecognised_file_cleaner_scan' :
|
288 |
-
$
|
289 |
-
$
|
290 |
-
$
|
291 |
break;
|
292 |
|
293 |
case 'ufc_scan_uploads' :
|
294 |
-
$
|
295 |
-
$
|
296 |
-
$
|
297 |
.'<br />'.__( 'The Uploads folder is primarily for media, but could be used to store nefarious files.', 'wp-simple-firewall' );
|
298 |
break;
|
299 |
|
300 |
case 'ufc_exclusions' :
|
301 |
-
$
|
302 |
-
$
|
303 |
$sDefaults = implode( ', ', $this->getOptions()->getOptDefault( 'ufc_exclusions' ) );
|
304 |
-
$
|
305 |
.'<br/><strong>'.__( 'No commas are necessary.', 'wp-simple-firewall' ).'</strong>'
|
306 |
.'<br/>'.sprintf( '%s: %s', __( 'Default', 'wp-simple-firewall' ), $sDefaults );
|
307 |
break;
|
308 |
|
309 |
case 'ic_enabled' :
|
310 |
-
$
|
311 |
-
$
|
312 |
-
$
|
313 |
break;
|
314 |
|
315 |
case 'ic_users' :
|
316 |
-
$
|
317 |
-
$
|
318 |
-
$
|
319 |
.'<br />'.__( 'An example of this might be some form of SQL Injection attack.', 'wp-simple-firewall' )
|
320 |
.'<br />'.sprintf( '%s: %s', __( 'Warning', 'wp-simple-firewall' ), __( 'Enabling this option for every page low may slow down your site with large numbers of users.', 'wp-simple-firewall' ) )
|
321 |
.'<br />'.sprintf( '%s: %s', __( 'Warning', 'wp-simple-firewall' ), __( 'This option may cause critical problem with 3rd party plugins that manage user accounts.', 'wp-simple-firewall' ) );
|
322 |
break;
|
323 |
|
324 |
case 'ptg_depth' : /* DELETED */
|
325 |
-
$
|
326 |
-
$
|
327 |
-
$
|
328 |
.'<br/>'.sprintf( __( 'Setting it to %s will remove this limit and all sub-folders will be scanned - not recommended', 'wp-simple-firewall' ), 0 );
|
329 |
break;
|
330 |
|
331 |
case 'ptg_reinstall_links' :
|
332 |
-
$
|
333 |
-
$
|
334 |
-
$
|
335 |
break;
|
336 |
|
337 |
case 'enabled_scan_apc' :
|
338 |
-
$
|
339 |
-
$
|
340 |
-
$
|
341 |
break;
|
342 |
|
343 |
case 'display_apc' :
|
344 |
-
$
|
345 |
-
$
|
346 |
-
$
|
347 |
break;
|
348 |
|
349 |
case 'mal_autorepair_core' :
|
350 |
-
$
|
351 |
-
$
|
352 |
-
$
|
353 |
break;
|
354 |
|
355 |
case 'mal_autorepair_surgical' :
|
356 |
-
$
|
357 |
-
$
|
358 |
-
$
|
359 |
.'<br />'.sprintf( '%s: %s', __( 'Warning', 'wp-simple-firewall' ), __( 'This could break your site if code removal leaves remaining code in an inconsistent state.', 'wp-simple-firewall' ) )
|
360 |
.'<br />'.sprintf( '%s: %s', __( 'Important', 'wp-simple-firewall' ), __( "Only applies to files that don't fall under the other categories for automatic repair.", 'wp-simple-firewall' ) );
|
361 |
break;
|
362 |
|
363 |
// REMOVED:
|
364 |
case 'mal_autorepair_plugins' :
|
365 |
-
$
|
366 |
-
$
|
367 |
-
$
|
368 |
.'<br />'.sprintf( '%s: %s', __( 'Important', 'wp-simple-firewall' ), __( 'Only compatible with plugins installed from WordPress.org.', 'wp-simple-firewall' ) )
|
369 |
.'<br />'.sprintf( '%s: %s', __( 'Important', 'wp-simple-firewall' ), __( "Also deletes suspected files if they weren't originally distributed with the plugin.", 'wp-simple-firewall' ) );
|
370 |
break;
|
371 |
case 'autorepair_themes' :
|
372 |
-
$
|
373 |
-
$
|
374 |
-
$
|
375 |
.'<br />'.sprintf( '%s: %s', __( 'Important', 'wp-simple-firewall' ), __( 'Only compatible with themes installed from WordPress.org.', 'wp-simple-firewall' ) )
|
376 |
.'<br />'.sprintf( '%s: %s', __( 'Important', 'wp-simple-firewall' ), __( "Also deletes suspected files if they weren't originally distributed with the theme.", 'wp-simple-firewall' ) );
|
377 |
break;
|
378 |
case 'wpvuln_scan_display' :
|
379 |
-
$
|
380 |
-
$
|
381 |
-
$
|
382 |
break;
|
383 |
case 'email_files_list' :
|
384 |
-
$
|
385 |
-
$
|
386 |
-
$
|
387 |
break;
|
388 |
case 'mal_fp_confidence' :
|
389 |
-
$
|
390 |
-
$
|
391 |
-
$
|
392 |
.'<br />'.__( "A false positive happens when a file appears to contain malware and shows up in scan results, but it's actually clean.", 'wp-simple-firewall' )
|
393 |
.' ('.__( "A false positive is similar to when an anti-virus alerts to a file that doesnt have a virus.", 'wp-simple-firewall' ).')'
|
394 |
.'<br />'.__( "The higher the confidence level, the more likely a result is a false positive.", 'wp-simple-firewall' )
|
@@ -402,16 +397,16 @@ class Strings extends Base\Strings {
|
|
402 |
.'<br />'.__( 'The more sites that share this information, the stronger and smarter the network becomes.', 'wp-simple-firewall' );
|
403 |
break;
|
404 |
case 'notification_interval' :
|
405 |
-
$
|
406 |
-
$
|
407 |
-
$
|
408 |
.'<br/>'.__( 'Specify the number of days to suppress repeat notifications.', 'wp-simple-firewall' )
|
409 |
.'<br/>'.sprintf( '%s: %s', __( 'Note', 'wp-simple-firewall' ), __( 'This is per discovered item or file, not per scan.', 'wp-simple-firewall' ) );
|
410 |
break;
|
411 |
case 'ptg_extensions' :
|
412 |
-
$
|
413 |
-
$
|
414 |
-
$
|
415 |
.'<br/>'.__( 'No commas(,) or periods(.) necessary.', 'wp-simple-firewall' )
|
416 |
.'<br/>'.__( 'Remove all extensions to scan all file type (not recommended).', 'wp-simple-firewall' );
|
417 |
break;
|
@@ -420,9 +415,9 @@ class Strings extends Base\Strings {
|
|
420 |
}
|
421 |
|
422 |
return [
|
423 |
-
'name' => $
|
424 |
-
'summary' => $
|
425 |
-
'description' => $
|
426 |
];
|
427 |
}
|
428 |
|
8 |
|
9 |
class Strings extends Base\Strings {
|
10 |
|
11 |
+
public function getScanName( string $slug ) :string {
|
12 |
+
return $this->getScanNames()[ $slug ];
|
|
|
|
|
|
|
|
|
|
|
13 |
}
|
14 |
|
15 |
/**
|
170 |
* @throws \Exception
|
171 |
*/
|
172 |
public function getOptionStrings( string $key ) :array {
|
173 |
+
/** @var ModCon $mod */
|
174 |
$mod = $this->getMod();
|
175 |
+
$modName = $mod->getMainFeatureName();
|
176 |
|
177 |
switch ( $key ) {
|
178 |
|
179 |
case 'enable_hack_protect' :
|
180 |
+
$name = sprintf( __( 'Enable %s Module', 'wp-simple-firewall' ), $modName );
|
181 |
+
$summary = sprintf( __( 'Enable (or Disable) The %s Module', 'wp-simple-firewall' ), $modName );
|
182 |
+
$desc = sprintf( __( 'Un-Checking this option will completely disable the %s module.', 'wp-simple-firewall' ), $modName );
|
183 |
break;
|
184 |
|
185 |
case 'scan_frequency' :
|
186 |
+
$name = __( 'Daily Scan Frequency', 'wp-simple-firewall' );
|
187 |
+
$summary = __( 'Number Of Times To Run All Scans Each Day', 'wp-simple-firewall' );
|
188 |
+
$desc = [
|
189 |
sprintf( '%s: %s', __( 'Default', 'wp-simple-firewall' ), __( 'Once every 24hrs.', 'wp-simple-firewall' ) ),
|
190 |
__( 'To improve security, increase the number of scans per day.', 'wp-simple-firewall' )
|
191 |
];
|
192 |
break;
|
193 |
|
194 |
case 'enable_plugin_vulnerabilities_scan' :
|
195 |
+
$name = __( 'Vulnerabilities Scanner', 'wp-simple-firewall' );
|
196 |
+
$summary = sprintf( __( 'Daily Cron - %s', 'wp-simple-firewall' ), __( 'Scans Plugins For Known Vulnerabilities', 'wp-simple-firewall' ) );
|
197 |
+
$desc = __( 'Runs a scan of all your plugins against a database of known WordPress plugin vulnerabilities.', 'wp-simple-firewall' );
|
198 |
break;
|
199 |
|
200 |
case 'enable_wpvuln_scan' :
|
201 |
+
$name = __( 'Vulnerability Scanner', 'wp-simple-firewall' );
|
202 |
+
$summary = __( 'Enable The Vulnerability Scanner', 'wp-simple-firewall' );
|
203 |
+
$desc = __( 'Runs a scan of all your plugins against a database of known WordPress vulnerabilities.', 'wp-simple-firewall' );
|
204 |
break;
|
205 |
|
206 |
case 'wpvuln_scan_autoupdate' :
|
207 |
+
$name = __( 'Automatic Updates', 'wp-simple-firewall' );
|
208 |
+
$summary = __( 'Apply Updates Automatically To Vulnerable Plugins', 'wp-simple-firewall' );
|
209 |
+
$desc = __( 'When an update becomes available, automatically apply updates to items with known vulnerabilities.', 'wp-simple-firewall' );
|
210 |
break;
|
211 |
|
212 |
case 'enable_core_file_integrity_scan' :
|
213 |
+
$name = sprintf( __( '%s Core Files', 'wp-simple-firewall' ),
|
214 |
Services::WpGeneral()->isClassicPress() ? 'ClassicPress' : 'WordPress'
|
215 |
);
|
216 |
+
$summary = sprintf( __( 'Scan And Monitor %s Core Files For Changes', 'wp-simple-firewall' ),
|
217 |
Services::WpGeneral()->isClassicPress() ? 'ClassicPress' : 'WordPress'
|
218 |
);
|
219 |
+
$desc = [
|
220 |
sprintf( '%s - %s', __( 'Purpose', 'wp-simple-firewall' ), __( 'Regularly scan your WordPress core files for changes compared to official WordPress files.', 'wp-simple-firewall' ) ),
|
221 |
sprintf( '%s - %s', __( 'Recommendation', 'wp-simple-firewall' ), __( 'Keep this feature turned on, at all times.', 'wp-simple-firewall' ) )
|
222 |
];
|
223 |
break;
|
224 |
|
225 |
case 'mal_scan_enable' :
|
226 |
+
$name = __( 'Malware', 'wp-simple-firewall' );
|
227 |
+
$summary = __( 'Scan And Monitor Files For Malware Infections', 'wp-simple-firewall' );
|
228 |
+
$desc = [
|
229 |
sprintf( '%s - %s', __( 'Purpose', 'wp-simple-firewall' ), __( 'Monitor and detect presence of Malware signatures.', 'wp-simple-firewall' ) ),
|
230 |
sprintf( '%s - %s', __( 'Recommendation', 'wp-simple-firewall' ), __( 'Keep this feature turned on, at all times.', 'wp-simple-firewall' ) ),
|
231 |
sprintf( '%s - %s', __( 'Note', 'wp-simple-firewall' ), __( 'Currently files of the following types are supported:', 'wp-simple-firewall' ) )
|
234 |
break;
|
235 |
|
236 |
case 'ptg_enable' :
|
237 |
+
$name = __( 'Plugins & Themes', 'wp-simple-firewall' );
|
238 |
+
$summary = __( 'Scan And Monitor Plugin & Theme Files For Changes', 'wp-simple-firewall' );
|
239 |
|
240 |
+
$desc = [
|
241 |
__( "Looks for new files added to plugins or themes, and also for changes to existing files.", 'wp-simple-firewall' ),
|
242 |
sprintf( '%s - %s', __( 'Important', 'wp-simple-firewall' ), __( "Doesn't currently detect missing files.", 'wp-simple-firewall' ) ),
|
243 |
sprintf( '%s - %s', __( 'Recommendation', 'wp-simple-firewall' ), __( 'Keep this feature turned on, at all times.', 'wp-simple-firewall' ) )
|
244 |
];
|
245 |
if ( !$mod->canCacheDirWrite() ) {
|
246 |
+
$desc[] = sprintf( __( 'Sorry, this feature is not available because we cannot write to disk at this location: %s', 'wp-simple-firewall' ),
|
247 |
'<code>'.$mod->getPtgSnapsBaseDir().'</code>' );
|
248 |
}
|
249 |
break;
|
250 |
|
251 |
case 'file_repair_areas' :
|
252 |
+
$name = __( 'Automatic File Repair', 'wp-simple-firewall' );
|
253 |
+
$summary = __( 'Automatically Repair Files That Have Changes Or Malware Infection', 'wp-simple-firewall' );
|
254 |
+
$desc = [
|
255 |
__( 'Will attempt to automatically repair files that have been changed or infected with malware.', 'wp-simple-firewall' ),
|
256 |
'- '.__( 'In the case of WordPress, original files will be downloaded from WordPress.org to repair any broken files.', 'wp-simple-firewall' ),
|
257 |
'- '.__( 'In the case of plugins & themes, only those installed from WordPress.org may be repaired.', 'wp-simple-firewall' ),
|
261 |
break;
|
262 |
|
263 |
case 'file_locker' :
|
264 |
+
$name = __( 'File Locker', 'wp-simple-firewall' );
|
265 |
+
$summary = __( 'Lock Files Against Tampering And Changes', 'wp-simple-firewall' );
|
266 |
+
$desc = [
|
267 |
__( 'Detects changes to the files, then lets you examine contents and revert as required.', 'wp-simple-firewall' ),
|
268 |
sprintf( '%s: %s', __( 'Note', 'wp-simple-firewall' ), __( 'Web.Config is only available for Windows/IIS.', 'wp-simple-firewall' ) )
|
269 |
];
|
272 |
->setMod( $this->getMod() )
|
273 |
->loadLocks();
|
274 |
if ( !empty( $aLocks ) ) {
|
275 |
+
$desc[] = __( 'Locked Files', 'wp-simple-firewall' ).':';
|
276 |
foreach ( $aLocks as $oLock ) {
|
277 |
+
$desc[] = sprintf( '<code>%s</code>', $oLock->file );
|
278 |
}
|
279 |
}
|
280 |
break;
|
281 |
|
282 |
case 'enable_unrecognised_file_cleaner_scan' :
|
283 |
+
$name = __( 'Unrecognised Files Scanner', 'wp-simple-firewall' );
|
284 |
+
$summary = __( 'Automatically Scans For Unrecognised Files In Core Directories', 'wp-simple-firewall' );
|
285 |
+
$desc = __( 'Scans for, and automatically deletes, any files in your core WordPress folders that are not part of your WordPress installation.', 'wp-simple-firewall' );
|
286 |
break;
|
287 |
|
288 |
case 'ufc_scan_uploads' :
|
289 |
+
$name = __( 'Scan Uploads', 'wp-simple-firewall' );
|
290 |
+
$summary = __( 'Scan Uploads Folder For PHP and Javascript', 'wp-simple-firewall' );
|
291 |
+
$desc = sprintf( '%s - %s', __( 'Warning', 'wp-simple-firewall' ), __( 'Take care when turning on this option - if you are unsure, leave it disabled.', 'wp-simple-firewall' ) )
|
292 |
.'<br />'.__( 'The Uploads folder is primarily for media, but could be used to store nefarious files.', 'wp-simple-firewall' );
|
293 |
break;
|
294 |
|
295 |
case 'ufc_exclusions' :
|
296 |
+
$name = __( 'File Exclusions', 'wp-simple-firewall' );
|
297 |
+
$summary = __( 'Provide A List Of Files To Be Excluded From The Scan', 'wp-simple-firewall' );
|
298 |
$sDefaults = implode( ', ', $this->getOptions()->getOptDefault( 'ufc_exclusions' ) );
|
299 |
+
$desc = __( 'Take a new line for each file you wish to exclude from the scan.', 'wp-simple-firewall' )
|
300 |
.'<br/><strong>'.__( 'No commas are necessary.', 'wp-simple-firewall' ).'</strong>'
|
301 |
.'<br/>'.sprintf( '%s: %s', __( 'Default', 'wp-simple-firewall' ), $sDefaults );
|
302 |
break;
|
303 |
|
304 |
case 'ic_enabled' :
|
305 |
+
$name = __( 'Enable Integrity Scan', 'wp-simple-firewall' );
|
306 |
+
$summary = __( 'Scans For Critical Changes Made To Your WordPress Site', 'wp-simple-firewall' );
|
307 |
+
$desc = __( 'Detects changes made to your WordPress site outside of WordPress.', 'wp-simple-firewall' );
|
308 |
break;
|
309 |
|
310 |
case 'ic_users' :
|
311 |
+
$name = __( 'Monitor User Accounts', 'wp-simple-firewall' );
|
312 |
+
$summary = __( 'Scans For Critical Changes Made To User Accounts', 'wp-simple-firewall' );
|
313 |
+
$desc = sprintf( __( 'Detects changes made to critical user account information that were made directly on the database and outside of the WordPress system.', 'wp-simple-firewall' ), 'author=' )
|
314 |
.'<br />'.__( 'An example of this might be some form of SQL Injection attack.', 'wp-simple-firewall' )
|
315 |
.'<br />'.sprintf( '%s: %s', __( 'Warning', 'wp-simple-firewall' ), __( 'Enabling this option for every page low may slow down your site with large numbers of users.', 'wp-simple-firewall' ) )
|
316 |
.'<br />'.sprintf( '%s: %s', __( 'Warning', 'wp-simple-firewall' ), __( 'This option may cause critical problem with 3rd party plugins that manage user accounts.', 'wp-simple-firewall' ) );
|
317 |
break;
|
318 |
|
319 |
case 'ptg_depth' : /* DELETED */
|
320 |
+
$name = __( 'Guard/Scan Depth', 'wp-simple-firewall' );
|
321 |
+
$summary = __( 'How Deep Into The Plugin Directories To Scan And Guard', 'wp-simple-firewall' );
|
322 |
+
$desc = __( 'The Guard normally scans only the top level of a folder. Increasing depth will increase scan times.', 'wp-simple-firewall' )
|
323 |
.'<br/>'.sprintf( __( 'Setting it to %s will remove this limit and all sub-folders will be scanned - not recommended', 'wp-simple-firewall' ), 0 );
|
324 |
break;
|
325 |
|
326 |
case 'ptg_reinstall_links' :
|
327 |
+
$name = __( 'Show Re-Install Links', 'wp-simple-firewall' );
|
328 |
+
$summary = __( 'Show Re-Install Links For Plugins', 'wp-simple-firewall' );
|
329 |
+
$desc = __( "Show links to re-install plugins and offer re-install when activating plugins.", 'wp-simple-firewall' );
|
330 |
break;
|
331 |
|
332 |
case 'enabled_scan_apc' :
|
333 |
+
$name = __( 'Abandoned Plugin Scanner', 'wp-simple-firewall' );
|
334 |
+
$summary = __( 'Enable The Abandoned Plugin Scanner', 'wp-simple-firewall' );
|
335 |
+
$desc = __( "Scan your WordPress.org assets for whether they've been abandoned.", 'wp-simple-firewall' );
|
336 |
break;
|
337 |
|
338 |
case 'display_apc' :
|
339 |
+
$name = __( 'Highlight Plugins', 'wp-simple-firewall' );
|
340 |
+
$summary = __( 'Highlight Abandoned Plugins', 'wp-simple-firewall' );
|
341 |
+
$desc = __( "Abandoned plugins will be highlighted on the main plugins page.", 'wp-simple-firewall' );
|
342 |
break;
|
343 |
|
344 |
case 'mal_autorepair_core' :
|
345 |
+
$name = __( 'Auto-Repair WP Core', 'wp-simple-firewall' );
|
346 |
+
$summary = __( 'Automatically Repair WordPress Core Files', 'wp-simple-firewall' );
|
347 |
+
$desc = __( "Automatically reinstall any core files found to have potential malware.", 'wp-simple-firewall' );
|
348 |
break;
|
349 |
|
350 |
case 'mal_autorepair_surgical' :
|
351 |
+
$name = __( 'Surgical Auto-Repair', 'wp-simple-firewall' );
|
352 |
+
$summary = __( 'Automatically Attempt To Surgically Remove Malware Code', 'wp-simple-firewall' );
|
353 |
+
$desc = __( "Attempts to automatically remove code from infected files.", 'wp-simple-firewall' )
|
354 |
.'<br />'.sprintf( '%s: %s', __( 'Warning', 'wp-simple-firewall' ), __( 'This could break your site if code removal leaves remaining code in an inconsistent state.', 'wp-simple-firewall' ) )
|
355 |
.'<br />'.sprintf( '%s: %s', __( 'Important', 'wp-simple-firewall' ), __( "Only applies to files that don't fall under the other categories for automatic repair.", 'wp-simple-firewall' ) );
|
356 |
break;
|
357 |
|
358 |
// REMOVED:
|
359 |
case 'mal_autorepair_plugins' :
|
360 |
+
$name = __( 'Auto-Repair WP Plugins', 'wp-simple-firewall' );
|
361 |
+
$summary = __( 'Automatically Repair WordPress.org Plugins', 'wp-simple-firewall' );
|
362 |
+
$desc = __( "Automatically repair any plugin files found to have potential malware.", 'wp-simple-firewall' )
|
363 |
.'<br />'.sprintf( '%s: %s', __( 'Important', 'wp-simple-firewall' ), __( 'Only compatible with plugins installed from WordPress.org.', 'wp-simple-firewall' ) )
|
364 |
.'<br />'.sprintf( '%s: %s', __( 'Important', 'wp-simple-firewall' ), __( "Also deletes suspected files if they weren't originally distributed with the plugin.", 'wp-simple-firewall' ) );
|
365 |
break;
|
366 |
case 'autorepair_themes' :
|
367 |
+
$name = __( 'Auto-Repair WP Themes', 'wp-simple-firewall' );
|
368 |
+
$summary = __( 'Automatically Repair WordPress.org Themes', 'wp-simple-firewall' );
|
369 |
+
$desc = __( "Automatically repair any theme files found to have potential malware.", 'wp-simple-firewall' )
|
370 |
.'<br />'.sprintf( '%s: %s', __( 'Important', 'wp-simple-firewall' ), __( 'Only compatible with themes installed from WordPress.org.', 'wp-simple-firewall' ) )
|
371 |
.'<br />'.sprintf( '%s: %s', __( 'Important', 'wp-simple-firewall' ), __( "Also deletes suspected files if they weren't originally distributed with the theme.", 'wp-simple-firewall' ) );
|
372 |
break;
|
373 |
case 'wpvuln_scan_display' :
|
374 |
+
$name = __( 'Highlight Plugins', 'wp-simple-firewall' );
|
375 |
+
$summary = __( 'Highlight Vulnerable Plugins Upon Display', 'wp-simple-firewall' );
|
376 |
+
$desc = __( 'Vulnerable plugins will be highlighted on the main plugins page.', 'wp-simple-firewall' );
|
377 |
break;
|
378 |
case 'email_files_list' :
|
379 |
+
$name = __( 'Email Files List', 'wp-simple-firewall' );
|
380 |
+
$summary = __( 'Scan Notification Emails Should Include Full Listing Of Files', 'wp-simple-firewall' );
|
381 |
+
$desc = __( 'Scanner notification emails will include a summary list of all affected files.', 'wp-simple-firewall' );
|
382 |
break;
|
383 |
case 'mal_fp_confidence' :
|
384 |
+
$name = __( 'Ignore False Positives Threshold', 'wp-simple-firewall' );
|
385 |
+
$summary = __( 'Ignore False Positives In Scan Results Automatically', 'wp-simple-firewall' );
|
386 |
+
$desc = __( "You can choose to ignore files with potential malware, depending on whether the confidence that it's a 'false positive' meets your minimum threshold.", 'wp-simple-firewall' )
|
387 |
.'<br />'.__( "A false positive happens when a file appears to contain malware and shows up in scan results, but it's actually clean.", 'wp-simple-firewall' )
|
388 |
.' ('.__( "A false positive is similar to when an anti-virus alerts to a file that doesnt have a virus.", 'wp-simple-firewall' ).')'
|
389 |
.'<br />'.__( "The higher the confidence level, the more likely a result is a false positive.", 'wp-simple-firewall' )
|
397 |
.'<br />'.__( 'The more sites that share this information, the stronger and smarter the network becomes.', 'wp-simple-firewall' );
|
398 |
break;
|
399 |
case 'notification_interval' :
|
400 |
+
$name = __( 'Repeat Notifications', 'wp-simple-firewall' );
|
401 |
+
$summary = __( 'Item Repeat Notifications Suppression Interval', 'wp-simple-firewall' );
|
402 |
+
$desc = __( 'How long the automated scans should wait before repeating a notification about an item.', 'wp-simple-firewall' )
|
403 |
.'<br/>'.__( 'Specify the number of days to suppress repeat notifications.', 'wp-simple-firewall' )
|
404 |
.'<br/>'.sprintf( '%s: %s', __( 'Note', 'wp-simple-firewall' ), __( 'This is per discovered item or file, not per scan.', 'wp-simple-firewall' ) );
|
405 |
break;
|
406 |
case 'ptg_extensions' :
|
407 |
+
$name = __( 'Included File Types', 'wp-simple-firewall' );
|
408 |
+
$summary = __( 'The File Types (by File Extension) Included In The Scan', 'wp-simple-firewall' );
|
409 |
+
$desc = __( 'Take a new line for each file extension.', 'wp-simple-firewall' )
|
410 |
.'<br/>'.__( 'No commas(,) or periods(.) necessary.', 'wp-simple-firewall' )
|
411 |
.'<br/>'.__( 'Remove all extensions to scan all file type (not recommended).', 'wp-simple-firewall' );
|
412 |
break;
|
415 |
}
|
416 |
|
417 |
return [
|
418 |
+
'name' => $name,
|
419 |
+
'summary' => $summary,
|
420 |
+
'description' => $desc,
|
421 |
];
|
422 |
}
|
423 |
|
src/lib/src/Modules/HackGuard/UI.php
CHANGED
@@ -3,47 +3,28 @@
|
|
3 |
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\HackGuard;
|
4 |
|
5 |
use FernleafSystems\Wordpress\Plugin\Shield;
|
6 |
-
use FernleafSystems\Wordpress\Plugin\Shield\Modules\
|
7 |
-
use FernleafSystems\Wordpress\Plugin\Shield\Modules\HackGuard\Lib\FileLocker\Ops\LoadFileLocks;
|
8 |
-
use FernleafSystems\Wordpress\Services\Core\VOs\WpPluginVo;
|
9 |
use FernleafSystems\Wordpress\Services\Services;
|
10 |
|
11 |
-
class UI extends
|
12 |
|
13 |
-
|
14 |
-
|
15 |
-
*/
|
16 |
-
public function buildInsightsVars() {
|
17 |
-
/** @var \ICWP_WPSF_FeatureHandler_HackProtect $mod */
|
18 |
$mod = $this->getMod();
|
19 |
/** @var Options $opts */
|
20 |
$opts = $this->getOptions();
|
21 |
|
22 |
-
$aLatestScans = array_map(
|
23 |
-
function ( $nTime ) {
|
24 |
-
return sprintf(
|
25 |
-
__( 'Last Scan: %s', 'wp-simple-firewall' ),
|
26 |
-
( $nTime > 0 ) ?
|
27 |
-
Services::Request()->carbon()->setTimestamp( $nTime )->diffForHumans()
|
28 |
-
: __( 'Never', 'wp-simple-firewall' )
|
29 |
-
);
|
30 |
-
},
|
31 |
-
$mod->getLastScansAt()
|
32 |
-
);
|
33 |
-
|
34 |
$aUiTrack = $mod->getUiTrack();
|
35 |
if ( empty( $aUiTrack[ 'selected_scans' ] ) ) {
|
36 |
$aUiTrack[ 'selected_scans' ] = $opts->getScanSlugs();
|
37 |
}
|
38 |
|
39 |
// Can Scan Checks:
|
40 |
-
$aReasonCantScan = $mod->
|
41 |
-
->getSubProScanner()
|
42 |
-
->getReasonsScansCantExecute();
|
43 |
|
44 |
/** @var \FernleafSystems\Wordpress\Plugin\Shield\Databases\Scanner\Select $oSelector */
|
45 |
$oSelector = $mod->getDbHandler_ScanResults()->getQuerySelector();
|
46 |
-
$
|
47 |
'ajax' => [
|
48 |
'scans_start' => $mod->getAjaxActionData( 'scans_start', true ),
|
49 |
'scans_check' => $mod->getAjaxActionData( 'scans_check', true ),
|
@@ -87,7 +68,13 @@ class UI extends Base\ShieldUI {
|
|
87 |
],
|
88 |
'vars' => [
|
89 |
'initial_check' => $mod->getScanQueueController()->hasRunningScans(),
|
90 |
-
'cannot_scan_reasons' => $aReasonCantScan
|
|
|
|
|
|
|
|
|
|
|
|
|
91 |
],
|
92 |
'scan_results' => [
|
93 |
],
|
@@ -169,24 +156,37 @@ class UI extends Base\ShieldUI {
|
|
169 |
],
|
170 |
];
|
171 |
|
172 |
-
/** @var Strings $
|
173 |
-
$
|
174 |
-
$
|
175 |
-
foreach ( $
|
176 |
-
|
177 |
-
|
178 |
-
|
179 |
-
|
180 |
-
|
181 |
-
|
182 |
-
$
|
183 |
-
|
184 |
-
$
|
185 |
-
$
|
186 |
-
$
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
187 |
}
|
188 |
|
189 |
-
return $
|
190 |
}
|
191 |
|
192 |
/**
|
@@ -205,11 +205,11 @@ class UI extends Base\ShieldUI {
|
|
205 |
* @return array
|
206 |
*/
|
207 |
protected function getFileLockerVars() {
|
208 |
-
/** @var
|
209 |
$mod = $this->getMod();
|
210 |
|
211 |
$oLockCon = $mod->getFileLocker();
|
212 |
-
$oLockLoader = ( new LoadFileLocks() )->setMod( $mod );
|
213 |
$aProblemLocks = $oLockLoader->withProblems();
|
214 |
$aGoodLocks = $oLockLoader->withoutProblems();
|
215 |
|
@@ -245,127 +245,26 @@ class UI extends Base\ShieldUI {
|
|
245 |
* @return array
|
246 |
*/
|
247 |
private function getInsightVarsScan_Ptg() {
|
248 |
-
/** @var
|
249 |
-
$
|
250 |
-
$oReq = Services::Request();
|
251 |
|
252 |
-
/** @var \ICWP_WPSF_Processor_HackProtect $oPro */
|
253 |
-
$oPro = $oMod->getProcessor();
|
254 |
-
$oProPtg = $oPro->getSubProScanner()->getSubProcessorPtg();
|
255 |
/** @var \FernleafSystems\Wordpress\Plugin\Shield\Databases\Scanner\Select $oSelector */
|
256 |
-
$oSelector = $
|
257 |
|
258 |
/** @var \FernleafSystems\Wordpress\Plugin\Shield\Databases\Scanner\EntryVO[] $aPtgResults */
|
259 |
$aPtgResults = $oSelector->filterByNotIgnored()
|
260 |
->filterByScan( 'ptg' )
|
261 |
->query();
|
262 |
-
/** @var Shield\Scans\Ptg\ResultsSet $oFullResults */
|
263 |
-
$oFullResults = ( new Shield\Modules\HackGuard\Scan\Results\ConvertBetweenTypes() )
|
264 |
-
->setScanController( $oMod->getScanCon( 'ptg' ) )
|
265 |
-
->fromVOsToResultsSet( $aPtgResults );
|
266 |
-
|
267 |
-
// Process Plugins
|
268 |
-
$aPlugins = $oFullResults->getAllResultsSetsForPluginsContext();
|
269 |
-
$oWpPlugins = Services::WpPlugins();
|
270 |
-
foreach ( $aPlugins as $sSlug => $oItemRS ) {
|
271 |
-
$aItems = $oItemRS->getAllItems();
|
272 |
-
/** @var \FernleafSystems\Wordpress\Plugin\Shield\Scans\Ptg\ResultItem $oIT */
|
273 |
-
$oIT = array_pop( $aItems );
|
274 |
-
$aMeta = $oProPtg->getSnapshotItemMeta( $oIT->slug );
|
275 |
-
if ( !empty( $aMeta[ 'ts' ] ) ) {
|
276 |
-
$aMeta[ 'ts' ] = $oReq->carbon()->setTimestamp( $aMeta[ 'ts' ] )->diffForHumans();
|
277 |
-
}
|
278 |
-
else {
|
279 |
-
$aMeta[ 'ts' ] = __( 'unknown', 'wp-simple-firewall' );
|
280 |
-
}
|
281 |
-
|
282 |
-
$bInstalled = $oWpPlugins->isInstalled( $oIT->slug );
|
283 |
-
$oPlgn = $oWpPlugins->getPluginAsVo( $oIT->slug );
|
284 |
-
$bIsWpOrg = $bInstalled && $oPlgn instanceof WpPluginVo && $oPlgn->isWpOrg();
|
285 |
-
$bHasUpdate = $bIsWpOrg && $oPlgn->hasUpdate();
|
286 |
-
$aProfile = [
|
287 |
-
'id' => $oSelector->filterByHash( $oIT->hash )->first()->id,
|
288 |
-
'name' => __( 'unknown', 'wp-simple-firewall' ),
|
289 |
-
'version' => __( 'unknown', 'wp-simple-firewall' ),
|
290 |
-
'root_dir' => $oWpPlugins->getInstallationDir( $oIT->slug ),
|
291 |
-
'slug' => $sSlug,
|
292 |
-
'is_wporg' => $bIsWpOrg,
|
293 |
-
'can_reinstall' => $bIsWpOrg,
|
294 |
-
'can_deactivate' => $bInstalled && ( $sSlug !== $this->getCon()->getPluginBaseFile() ),
|
295 |
-
'has_update' => $bHasUpdate,
|
296 |
-
'count_files' => $oItemRS->countItems(),
|
297 |
-
'date_snapshot' => $aMeta[ 'ts' ],
|
298 |
-
];
|
299 |
-
|
300 |
-
if ( $bInstalled ) {
|
301 |
-
$oP = $oWpPlugins->getPluginAsVo( $oIT->slug );
|
302 |
-
$aProfile[ 'name' ] = $oP->Name;
|
303 |
-
$aProfile[ 'version' ] = $oP->Version;
|
304 |
-
}
|
305 |
-
else {
|
306 |
-
// MISSING!
|
307 |
-
if ( is_array( $aMeta ) ) {
|
308 |
-
$aProfile[ 'name' ] = isset( $aMeta[ 'name' ] ) ? $aMeta[ 'name' ] : __( 'unknown', 'wp-simple-firewall' );
|
309 |
-
$aProfile[ 'version' ] = isset( $aMeta[ 'version' ] ) ? $aMeta[ 'version' ] : __( 'unknown', 'wp-simple-firewall' );
|
310 |
-
}
|
311 |
-
}
|
312 |
-
$aProfile[ 'name' ] = sprintf( '%s: %s', __( 'Plugin' ), $aProfile[ 'name' ] );
|
313 |
-
|
314 |
-
$aPlugins[ $sSlug ] = $aProfile;
|
315 |
-
}
|
316 |
-
|
317 |
-
// Process Themes
|
318 |
-
$aThemes = $oFullResults->getAllResultsSetsForThemesContext();
|
319 |
-
$oWpThemes = Services::WpThemes();
|
320 |
-
foreach ( $aThemes as $sSlug => $oItemRS ) {
|
321 |
-
$aItems = $oItemRS->getAllItems();
|
322 |
-
/** @var \FernleafSystems\Wordpress\Plugin\Shield\Scans\Ptg\ResultItem $oIT */
|
323 |
-
$oIT = array_pop( $aItems );
|
324 |
-
$aMeta = $oProPtg->getSnapshotItemMeta( $oIT->slug );
|
325 |
-
if ( !empty( $aMeta[ 'ts' ] ) ) {
|
326 |
-
$aMeta[ 'ts' ] = $oReq->carbon()->setTimestamp( $aMeta[ 'ts' ] )->diffForHumans();
|
327 |
-
}
|
328 |
-
else {
|
329 |
-
$aMeta[ 'ts' ] = __( 'unknown', 'wp-simple-firewall' );
|
330 |
-
}
|
331 |
-
|
332 |
-
$bInstalled = $oWpThemes->isInstalled( $oIT->slug );
|
333 |
-
$bIsWpOrg = $bInstalled && $oWpThemes->isWpOrg( $sSlug );
|
334 |
-
$bHasUpdate = $bIsWpOrg && $oWpThemes->isUpdateAvailable( $sSlug );
|
335 |
-
$aProfile = [
|
336 |
-
'id' => $oSelector->filterByHash( $oIT->hash )->first()->id,
|
337 |
-
'name' => __( 'unknown', 'wp-simple-firewall' ),
|
338 |
-
'version' => __( 'unknown', 'wp-simple-firewall' ),
|
339 |
-
'root_dir' => __( 'unknown', 'wp-simple-firewall' ),
|
340 |
-
'slug' => $sSlug,
|
341 |
-
'is_wporg' => $bIsWpOrg,
|
342 |
-
'can_reinstall' => $bIsWpOrg,
|
343 |
-
'can_deactivate' => false,
|
344 |
-
'has_update' => $bHasUpdate,
|
345 |
-
'count_files' => $oItemRS->countItems(),
|
346 |
-
'date_snapshot' => $aMeta[ 'ts' ],
|
347 |
-
];
|
348 |
-
if ( $bInstalled ) {
|
349 |
-
$oT = $oWpThemes->getTheme( $oIT->slug );
|
350 |
-
$aProfile[ 'name' ] = $oT->get( 'Name' );
|
351 |
-
$aProfile[ 'version' ] = $oT->get( 'Version' );
|
352 |
-
$aProfile[ 'root_dir' ] = $oWpThemes->getInstallationDir( $oIT->slug );
|
353 |
-
}
|
354 |
-
$aProfile[ 'name' ] = sprintf( '%s: %s', __( 'Theme' ), $aProfile[ 'name' ] );
|
355 |
-
|
356 |
-
$aThemes[ $sSlug ] = $aProfile;
|
357 |
-
}
|
358 |
|
359 |
return [
|
360 |
'flags' => [
|
361 |
-
'has_items' => $
|
362 |
'has_plugins' => !empty( $aPlugins ),
|
363 |
'has_themes' => !empty( $aThemes ),
|
364 |
'show_table' => false,
|
365 |
],
|
366 |
'hrefs' => [],
|
367 |
'vars' => [],
|
368 |
-
'assets' => $oMod->isPtgEnabled() ? array_merge( $aPlugins, $aThemes ) : [],
|
369 |
'strings' => [
|
370 |
'subtitle' => __( "Detects unauthorized changes to plugins/themes", 'wp-simple-firewall' ),
|
371 |
'files_with_problems' => __( 'Files with problems', 'wp-simple-firewall' ),
|
@@ -403,4 +302,14 @@ class UI extends Base\ShieldUI {
|
|
403 |
|
404 |
return $aWarnings;
|
405 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
406 |
}
|
3 |
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\HackGuard;
|
4 |
|
5 |
use FernleafSystems\Wordpress\Plugin\Shield;
|
6 |
+
use FernleafSystems\Wordpress\Plugin\Shield\Modules\BaseShield;
|
|
|
|
|
7 |
use FernleafSystems\Wordpress\Services\Services;
|
8 |
|
9 |
+
class UI extends BaseShield\UI {
|
10 |
|
11 |
+
public function buildInsightsVars() :array {
|
12 |
+
/** @var ModCon $mod */
|
|
|
|
|
|
|
13 |
$mod = $this->getMod();
|
14 |
/** @var Options $opts */
|
15 |
$opts = $this->getOptions();
|
16 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
17 |
$aUiTrack = $mod->getUiTrack();
|
18 |
if ( empty( $aUiTrack[ 'selected_scans' ] ) ) {
|
19 |
$aUiTrack[ 'selected_scans' ] = $opts->getScanSlugs();
|
20 |
}
|
21 |
|
22 |
// Can Scan Checks:
|
23 |
+
$aReasonCantScan = $mod->getScansCon()->getReasonsScansCantExecute();
|
|
|
|
|
24 |
|
25 |
/** @var \FernleafSystems\Wordpress\Plugin\Shield\Databases\Scanner\Select $oSelector */
|
26 |
$oSelector = $mod->getDbHandler_ScanResults()->getQuerySelector();
|
27 |
+
$data = [
|
28 |
'ajax' => [
|
29 |
'scans_start' => $mod->getAjaxActionData( 'scans_start', true ),
|
30 |
'scans_check' => $mod->getAjaxActionData( 'scans_check', true ),
|
68 |
],
|
69 |
'vars' => [
|
70 |
'initial_check' => $mod->getScanQueueController()->hasRunningScans(),
|
71 |
+
'cannot_scan_reasons' => $aReasonCantScan,
|
72 |
+
'related_hrefs' => [
|
73 |
+
[
|
74 |
+
'href' => $this->getCon()->getModule_HackGuard()->getUrl_AdminPage(),
|
75 |
+
'title' => __( 'Scan Settings', 'wp-simple-firewall' ),
|
76 |
+
],
|
77 |
+
]
|
78 |
],
|
79 |
'scan_results' => [
|
80 |
],
|
156 |
],
|
157 |
];
|
158 |
|
159 |
+
/** @var Strings $strings */
|
160 |
+
$strings = $mod->getStrings();
|
161 |
+
$name = $strings->getScanNames();
|
162 |
+
foreach ( $data[ 'scans' ] as $slug => &$scData ) {
|
163 |
+
try {
|
164 |
+
$scon = $mod->getScanCon( $slug );
|
165 |
+
}
|
166 |
+
catch ( \Exception $e ) {
|
167 |
+
continue;
|
168 |
+
}
|
169 |
+
$lastScanAt = $scon->getLastScanAt();
|
170 |
+
|
171 |
+
$scData[ 'flags' ][ 'is_available' ] = $scon->isScanningAvailable();
|
172 |
+
$scData[ 'flags' ][ 'is_restricted' ] = !$scon->isScanningAvailable();
|
173 |
+
$scData[ 'flags' ][ 'is_enabled' ] = $scon->isEnabled();
|
174 |
+
$scData[ 'flags' ][ 'is_selected' ] = $scon->isScanningAvailable() && in_array( $slug, $aUiTrack[ 'selected_scans' ] );
|
175 |
+
$scData[ 'vars' ][ 'last_scan_at_ts' ] = $lastScanAt;
|
176 |
+
$scData[ 'flags' ][ 'has_last_scan' ] = $lastScanAt > 0;
|
177 |
+
$scData[ 'vars' ][ 'last_scan_at' ] = sprintf(
|
178 |
+
__( 'Last Scan: %s', 'wp-simple-firewall' ),
|
179 |
+
( $lastScanAt > 0 ) ?
|
180 |
+
Services::Request()->carbon()->setTimestamp( $lastScanAt )->diffForHumans()
|
181 |
+
: __( 'Never', 'wp-simple-firewall' )
|
182 |
+
);
|
183 |
+
$scData[ 'strings' ][ 'title' ] = $name[ $slug ];
|
184 |
+
$scData[ 'hrefs' ][ 'options' ] = $mod->getUrl_DirectLinkToSection( 'section_scan_'.$slug );
|
185 |
+
$scData[ 'hrefs' ][ 'please_enable' ] = $mod->getUrl_DirectLinkToSection( 'section_scan_'.$slug );
|
186 |
+
$scData[ 'count' ] = $oSelector->countForScan( $slug );
|
187 |
}
|
188 |
|
189 |
+
return $data;
|
190 |
}
|
191 |
|
192 |
/**
|
205 |
* @return array
|
206 |
*/
|
207 |
protected function getFileLockerVars() {
|
208 |
+
/** @var ModCon $mod */
|
209 |
$mod = $this->getMod();
|
210 |
|
211 |
$oLockCon = $mod->getFileLocker();
|
212 |
+
$oLockLoader = ( new Lib\FileLocker\Ops\LoadFileLocks() )->setMod( $mod );
|
213 |
$aProblemLocks = $oLockLoader->withProblems();
|
214 |
$aGoodLocks = $oLockLoader->withoutProblems();
|
215 |
|
245 |
* @return array
|
246 |
*/
|
247 |
private function getInsightVarsScan_Ptg() {
|
248 |
+
/** @var ModCon $mod */
|
249 |
+
$mod = $this->getMod();
|
|
|
250 |
|
|
|
|
|
|
|
251 |
/** @var \FernleafSystems\Wordpress\Plugin\Shield\Databases\Scanner\Select $oSelector */
|
252 |
+
$oSelector = $mod->getDbHandler_ScanResults()->getQuerySelector();
|
253 |
|
254 |
/** @var \FernleafSystems\Wordpress\Plugin\Shield\Databases\Scanner\EntryVO[] $aPtgResults */
|
255 |
$aPtgResults = $oSelector->filterByNotIgnored()
|
256 |
->filterByScan( 'ptg' )
|
257 |
->query();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
258 |
|
259 |
return [
|
260 |
'flags' => [
|
261 |
+
'has_items' => $mod->isPtgEnabled() ? !empty( $aPtgResults ) : false,
|
262 |
'has_plugins' => !empty( $aPlugins ),
|
263 |
'has_themes' => !empty( $aThemes ),
|
264 |
'show_table' => false,
|
265 |
],
|
266 |
'hrefs' => [],
|
267 |
'vars' => [],
|
|
|
268 |
'strings' => [
|
269 |
'subtitle' => __( "Detects unauthorized changes to plugins/themes", 'wp-simple-firewall' ),
|
270 |
'files_with_problems' => __( 'Files with problems', 'wp-simple-firewall' ),
|
302 |
|
303 |
return $aWarnings;
|
304 |
}
|
305 |
+
|
306 |
+
protected function getSettingsRelatedLinks() :array {
|
307 |
+
$modInsights = $this->getCon()->getModule_Insights();
|
308 |
+
return [
|
309 |
+
[
|
310 |
+
'href' => $modInsights->getUrl_SubInsightsPage( 'scans' ),
|
311 |
+
'title' => __( 'Run Scans', 'wp-simple-firewall' ),
|
312 |
+
]
|
313 |
+
];
|
314 |
+
}
|
315 |
}
|
src/lib/src/Modules/Headers/Insights/OverviewCards.php
CHANGED
@@ -8,7 +8,7 @@ use FernleafSystems\Wordpress\Plugin\Shield\Modules\Headers\Options;
|
|
8 |
class OverviewCards extends Shield\Modules\Base\Insights\OverviewCards {
|
9 |
|
10 |
public function build() :array {
|
11 |
-
/** @var \
|
12 |
$mod = $this->getMod();
|
13 |
/** @var Options $opts */
|
14 |
$opts = $this->getOptions();
|
8 |
class OverviewCards extends Shield\Modules\Base\Insights\OverviewCards {
|
9 |
|
10 |
public function build() :array {
|
11 |
+
/** @var Shield\Modules\Headers\ModCon $mod */
|
12 |
$mod = $this->getMod();
|
13 |
/** @var Options $opts */
|
14 |
$opts = $this->getOptions();
|
src/lib/src/Modules/Headers/ModCon.php
ADDED
@@ -0,0 +1,90 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php declare( strict_types=1 );
|
2 |
+
|
3 |
+
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Headers;
|
4 |
+
|
5 |
+
use FernleafSystems\Wordpress\Plugin\Shield\Modules\BaseShield;
|
6 |
+
use FernleafSystems\Wordpress\Services\Services;
|
7 |
+
|
8 |
+
class ModCon extends BaseShield\ModCon {
|
9 |
+
|
10 |
+
protected function preProcessOptions() {
|
11 |
+
$this->cleanCspHosts();
|
12 |
+
$this->cleanCustomRules();
|
13 |
+
}
|
14 |
+
|
15 |
+
private function cleanCustomRules() {
|
16 |
+
/** @var Options $opts */
|
17 |
+
$opts = $this->getOptions();
|
18 |
+
$opts->setOpt( 'xcsp_custom', array_unique( array_filter( array_map(
|
19 |
+
function ( $sRule ) {
|
20 |
+
$sRule = trim( preg_replace( '#;|\s{2,}#', '', html_entity_decode( $sRule, ENT_QUOTES ) ) );
|
21 |
+
if ( !empty( $sRule ) ) {
|
22 |
+
$sRule .= ';';
|
23 |
+
}
|
24 |
+
return $sRule;
|
25 |
+
},
|
26 |
+
$opts->getOpt( 'xcsp_custom', [] )
|
27 |
+
) ) ) );
|
28 |
+
}
|
29 |
+
|
30 |
+
private function cleanCspHosts() {
|
31 |
+
/** @var Options $opts */
|
32 |
+
$opts = $this->getOptions();
|
33 |
+
|
34 |
+
$aValidDomains = [];
|
35 |
+
foreach ( $opts->getOpt( 'xcsp_hosts', [] ) as $sDomain ) {
|
36 |
+
$bValidDomain = false;
|
37 |
+
$sDomain = trim( $sDomain );
|
38 |
+
|
39 |
+
$bHttps = ( strpos( $sDomain, 'https://' ) === 0 );
|
40 |
+
$bHttp = ( strpos( $sDomain, 'http://' ) === 0 );
|
41 |
+
if ( $bHttp || $bHttps ) {
|
42 |
+
$sDomain = preg_replace( '#^http(s)?://#', '', $sDomain );
|
43 |
+
}
|
44 |
+
|
45 |
+
$sCustomProtocol = '';
|
46 |
+
// Special wildcard case
|
47 |
+
if ( $sDomain == '*' ) {
|
48 |
+
if ( $bHttps ) {
|
49 |
+
$this->getOptions()->setOpt( 'xcsp_https', 'Y' );
|
50 |
+
}
|
51 |
+
else {
|
52 |
+
$bValidDomain = true;
|
53 |
+
}
|
54 |
+
}
|
55 |
+
elseif ( strpos( $sDomain, '://' ) && preg_match( '#^([a-zA-Z]+://)#', $sDomain, $aMatches ) ) {
|
56 |
+
// there's a protocol specified
|
57 |
+
$sCustomProtocol = $aMatches[ 1 ];
|
58 |
+
$sDomain = str_replace( $sCustomProtocol, '', $sDomain );
|
59 |
+
}
|
60 |
+
|
61 |
+
// First we remove the wildcard and test domain, then add it back later.
|
62 |
+
$bWildCard = ( strpos( $sDomain, '*.' ) === 0 );
|
63 |
+
if ( $bWildCard ) {
|
64 |
+
$sDomain = preg_replace( '#^\*\.#', '', $sDomain );
|
65 |
+
}
|
66 |
+
|
67 |
+
if ( !empty ( $sDomain ) && Services::Data()->isValidDomainName( $sDomain ) ) {
|
68 |
+
$bValidDomain = true;
|
69 |
+
}
|
70 |
+
|
71 |
+
if ( $bValidDomain ) {
|
72 |
+
if ( $bWildCard ) {
|
73 |
+
$sDomain = '*.'.$sDomain;
|
74 |
+
}
|
75 |
+
if ( $bHttp ) {
|
76 |
+
// $sDomain = 'http://'.$sDomain; // it seems there's no need to "explicitly" state http://
|
77 |
+
}
|
78 |
+
elseif ( $bHttps ) {
|
79 |
+
$sDomain = 'https://'.$sDomain;
|
80 |
+
}
|
81 |
+
elseif ( !empty( $sCustomProtocol ) ) {
|
82 |
+
$sDomain = $sCustomProtocol.$sDomain;
|
83 |
+
}
|
84 |
+
$aValidDomains[] = $sDomain;
|
85 |
+
}
|
86 |
+
}
|
87 |
+
asort( $aValidDomains );
|
88 |
+
$opts->setOpt( 'xcsp_hosts', array_unique( $aValidDomains ) );
|
89 |
+
}
|
90 |
+
}
|
src/lib/src/Modules/Headers/Options.php
CHANGED
@@ -2,15 +2,13 @@
|
|
2 |
|
3 |
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Headers;
|
4 |
|
5 |
-
use FernleafSystems\Wordpress\Plugin\Shield\Modules\
|
6 |
|
7 |
-
class Options extends
|
8 |
|
9 |
-
|
10 |
-
|
11 |
-
|
12 |
-
public function getCspCustomRules() {
|
13 |
-
return $this->isPremium() ? $this->getOpt( 'xcsp_custom' ) : [];
|
14 |
}
|
15 |
|
16 |
/**
|
2 |
|
3 |
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Headers;
|
4 |
|
5 |
+
use FernleafSystems\Wordpress\Plugin\Shield\Modules\BaseShield;
|
6 |
|
7 |
+
class Options extends BaseShield\Options {
|
8 |
|
9 |
+
public function getCspCustomRules() :array {
|
10 |
+
$csp = $this->getOpt( 'xcsp_custom' );
|
11 |
+
return $this->isPremium() && is_array( $csp ) ? $csp : [];
|
|
|
|
|
12 |
}
|
13 |
|
14 |
/**
|
src/lib/src/Modules/Headers/Processor.php
ADDED
@@ -0,0 +1,199 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Headers;
|
4 |
+
|
5 |
+
use FernleafSystems\Wordpress\Plugin\Shield\Modules\BaseShield;
|
6 |
+
|
7 |
+
class Processor extends BaseShield\Processor {
|
8 |
+
|
9 |
+
/**
|
10 |
+
* @var bool
|
11 |
+
*/
|
12 |
+
private $bHeadersPushed;
|
13 |
+
|
14 |
+
/**
|
15 |
+
* @var array
|
16 |
+
*/
|
17 |
+
private $headers;
|
18 |
+
|
19 |
+
protected function run() {
|
20 |
+
if ( $this->getPushHeadersEarly() ) {
|
21 |
+
$this->sendHeaders();
|
22 |
+
}
|
23 |
+
else {
|
24 |
+
add_filter( 'wp_headers', [ $this, 'addToHeaders' ], PHP_INT_MAX );
|
25 |
+
add_action( 'send_headers', [ $this, 'sendHeaders' ], PHP_INT_MAX, 0 );
|
26 |
+
}
|
27 |
+
}
|
28 |
+
|
29 |
+
protected function getPushHeadersEarly() :bool {
|
30 |
+
return defined( 'WPCACHEHOME' ); //WP Super Cache
|
31 |
+
}
|
32 |
+
|
33 |
+
/**
|
34 |
+
* Tries to ensure duplicate headers are not sent. Previously sent/supplied headers take priority.
|
35 |
+
* @param array $wpHeaders
|
36 |
+
* @return array
|
37 |
+
*/
|
38 |
+
public function addToHeaders( $wpHeaders ) {
|
39 |
+
|
40 |
+
if ( !$this->isHeadersPushed() ) {
|
41 |
+
|
42 |
+
if ( !is_array( $wpHeaders ) ) {
|
43 |
+
$wpHeaders = [];
|
44 |
+
}
|
45 |
+
|
46 |
+
$alreadySent = array_map(
|
47 |
+
function ( $header ) {
|
48 |
+
return strtolower( trim( $header ) );
|
49 |
+
},
|
50 |
+
array_keys( $wpHeaders )
|
51 |
+
);
|
52 |
+
foreach ( $this->gatherSecurityHeaders() as $header => $value ) {
|
53 |
+
if ( !in_array( strtolower( $header ), $alreadySent ) ) {
|
54 |
+
$wpHeaders[ $header ] = $value;
|
55 |
+
}
|
56 |
+
}
|
57 |
+
$this->setHeadersPushed( true );
|
58 |
+
}
|
59 |
+
return $wpHeaders;
|
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 |
+
$headers = [];
|
82 |
+
|
83 |
+
if ( function_exists( 'headers_list' ) ) {
|
84 |
+
$sent = headers_list();
|
85 |
+
if ( is_array( $sent ) ) {
|
86 |
+
foreach ( $sent as $header ) {
|
87 |
+
if ( strpos( $header, ':' ) ) {
|
88 |
+
list( $key, $value ) = array_map( 'trim', explode( ':', $header, 2 ) );
|
89 |
+
$headers[ $key ] = $value;
|
90 |
+
}
|
91 |
+
}
|
92 |
+
}
|
93 |
+
}
|
94 |
+
|
95 |
+
return $headers;
|
96 |
+
}
|
97 |
+
|
98 |
+
private function getXFrameHeader() :array {
|
99 |
+
switch ( $this->getOptions()->getOpt( 'x_frame' ) ) {
|
100 |
+
case 'on_sameorigin':
|
101 |
+
$xFrame = 'SAMEORIGIN';
|
102 |
+
break;
|
103 |
+
case 'on_deny':
|
104 |
+
$xFrame = 'DENY';
|
105 |
+
break;
|
106 |
+
default:
|
107 |
+
$xFrame = '';
|
108 |
+
break;
|
109 |
+
}
|
110 |
+
return empty( $xFrame ) ? [] : [ 'x-frame-options' => $xFrame ];
|
111 |
+
}
|
112 |
+
|
113 |
+
private function getXssProtectionHeader() :array {
|
114 |
+
return [ 'X-XSS-Protection' => '1; mode=block' ];
|
115 |
+
}
|
116 |
+
|
117 |
+
private function getContentTypeOptionHeader() :array {
|
118 |
+
return [ 'X-Content-Type-Options' => 'nosniff' ];
|
119 |
+
}
|
120 |
+
|
121 |
+
private function getReferrerPolicyHeader() :array {
|
122 |
+
/** @var Options $opts */
|
123 |
+
$opts = $this->getOptions();
|
124 |
+
return [ 'Referrer-Policy' => $opts->getReferrerPolicyValue() ];
|
125 |
+
}
|
126 |
+
|
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 {
|
157 |
+
/** @var Options $opts */
|
158 |
+
$opts = $this->getOptions();
|
159 |
+
|
160 |
+
if ( $opts->isReferrerPolicyEnabled() ) {
|
161 |
+
$this->addHeader( $this->getReferrerPolicyHeader() );
|
162 |
+
}
|
163 |
+
if ( $opts->isEnabledXFrame() ) {
|
164 |
+
$this->addHeader( $this->getXFrameHeader() );
|
165 |
+
}
|
166 |
+
if ( $opts->isEnabledXssProtection() ) {
|
167 |
+
$this->addHeader( $this->getXssProtectionHeader() );
|
168 |
+
}
|
169 |
+
if ( $opts->isEnabledContentTypeHeader() ) {
|
170 |
+
$this->addHeader( $this->getContentTypeOptionHeader() );
|
171 |
+
}
|
172 |
+
if ( $opts->isEnabledContentSecurityPolicy() ) {
|
173 |
+
$this->addHeader( $this->setContentSecurityPolicyHeader() );
|
174 |
+
}
|
175 |
+
return $this->getHeaders();
|
176 |
+
}
|
177 |
+
|
178 |
+
private function getHeaders() :array {
|
179 |
+
if ( !isset( $this->headers ) || !is_array( $this->headers ) ) {
|
180 |
+
$this->headers = [];
|
181 |
+
}
|
182 |
+
return array_unique( $this->headers );
|
183 |
+
}
|
184 |
+
|
185 |
+
private function addHeader( array $header ) {
|
186 |
+
if ( !empty( $header ) && is_array( $header ) ) {
|
187 |
+
$this->headers = array_merge( $this->getHeaders(), $header );
|
188 |
+
}
|
189 |
+
}
|
190 |
+
|
191 |
+
private function isHeadersPushed() :bool {
|
192 |
+
return (bool)$this->bHeadersPushed;
|
193 |
+
}
|
194 |
+
|
195 |
+
private function setHeadersPushed( bool $pushed ) :self {
|
196 |
+
$this->bHeadersPushed = $pushed;
|
197 |
+
return $this;
|
198 |
+
}
|
199 |
+
}
|
src/lib/src/Modules/Headers/UI.php
CHANGED
@@ -2,7 +2,7 @@
|
|
2 |
|
3 |
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Headers;
|
4 |
|
5 |
-
use FernleafSystems\Wordpress\Plugin\Shield\Modules\
|
6 |
|
7 |
-
class UI extends
|
8 |
}
|
2 |
|
3 |
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Headers;
|
4 |
|
5 |
+
use FernleafSystems\Wordpress\Plugin\Shield\Modules\BaseShield;
|
6 |
|
7 |
+
class UI extends BaseShield\UI {
|
8 |
}
|
src/lib/src/Modules/IPs/AdminNotices.php
CHANGED
@@ -3,33 +3,30 @@
|
|
3 |
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\IPs;
|
4 |
|
5 |
use FernleafSystems\Wordpress\Plugin\Shield;
|
|
|
6 |
use FernleafSystems\Wordpress\Services\Services;
|
7 |
|
8 |
class AdminNotices extends Shield\Modules\Base\AdminNotices {
|
9 |
|
10 |
/**
|
11 |
-
* @
|
12 |
-
* @throws \Exception
|
13 |
*/
|
14 |
-
protected function processNotice( $
|
15 |
|
16 |
-
switch ( $
|
17 |
|
18 |
case 'visitor-whitelisted':
|
19 |
-
$this->buildNotice_VisitorWhitelisted( $
|
20 |
break;
|
21 |
|
22 |
default:
|
23 |
-
parent::processNotice( $
|
24 |
break;
|
25 |
}
|
26 |
}
|
27 |
|
28 |
-
|
29 |
-
|
30 |
-
*/
|
31 |
-
private function buildNotice_VisitorWhitelisted( $oNotice ) {
|
32 |
-
$oNotice->render_data = [
|
33 |
'notice_attributes' => [],
|
34 |
'strings' => [
|
35 |
'title' => sprintf(
|
@@ -46,24 +43,20 @@ class AdminNotices extends Shield\Modules\Base\AdminNotices {
|
|
46 |
];
|
47 |
}
|
48 |
|
49 |
-
|
50 |
-
|
51 |
-
|
52 |
-
*/
|
53 |
-
protected function isDisplayNeeded( $oNotice ) {
|
54 |
-
/** @var \ICWP_WPSF_FeatureHandler_Ips $oMod */
|
55 |
-
$oMod = $this->getMod();
|
56 |
|
57 |
-
switch ( $
|
58 |
|
59 |
case 'visitor-whitelisted':
|
60 |
-
$
|
61 |
break;
|
62 |
|
63 |
default:
|
64 |
-
$
|
65 |
break;
|
66 |
}
|
67 |
-
return $
|
68 |
}
|
69 |
}
|
3 |
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\IPs;
|
4 |
|
5 |
use FernleafSystems\Wordpress\Plugin\Shield;
|
6 |
+
use FernleafSystems\Wordpress\Plugin\Shield\Utilities\AdminNotices\NoticeVO;
|
7 |
use FernleafSystems\Wordpress\Services\Services;
|
8 |
|
9 |
class AdminNotices extends Shield\Modules\Base\AdminNotices {
|
10 |
|
11 |
/**
|
12 |
+
* @inheritDoc
|
|
|
13 |
*/
|
14 |
+
protected function processNotice( NoticeVO $notice ) {
|
15 |
|
16 |
+
switch ( $notice->id ) {
|
17 |
|
18 |
case 'visitor-whitelisted':
|
19 |
+
$this->buildNotice_VisitorWhitelisted( $notice );
|
20 |
break;
|
21 |
|
22 |
default:
|
23 |
+
parent::processNotice( $notice );
|
24 |
break;
|
25 |
}
|
26 |
}
|
27 |
|
28 |
+
private function buildNotice_VisitorWhitelisted( NoticeVO $notice ) {
|
29 |
+
$notice->render_data = [
|
|
|
|
|
|
|
30 |
'notice_attributes' => [],
|
31 |
'strings' => [
|
32 |
'title' => sprintf(
|
43 |
];
|
44 |
}
|
45 |
|
46 |
+
protected function isDisplayNeeded( NoticeVO $notice ) :bool {
|
47 |
+
/** @var ModCon $mod */
|
48 |
+
$mod = $this->getMod();
|
|
|
|
|
|
|
|
|
49 |
|
50 |
+
switch ( $notice->id ) {
|
51 |
|
52 |
case 'visitor-whitelisted':
|
53 |
+
$needed = $mod->isVisitorWhitelisted();
|
54 |
break;
|
55 |
|
56 |
default:
|
57 |
+
$needed = parent::isDisplayNeeded( $notice );
|
58 |
break;
|
59 |
}
|
60 |
+
return $needed;
|
61 |
}
|
62 |
}
|
src/lib/src/Modules/IPs/AjaxHandler.php
CHANGED
@@ -3,38 +3,44 @@
|
|
3 |
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\IPs;
|
4 |
|
5 |
use FernleafSystems\Wordpress\Plugin\Shield;
|
|
|
6 |
use FernleafSystems\Wordpress\Services\Services;
|
|
|
7 |
|
8 |
-
class AjaxHandler extends Shield\Modules\
|
9 |
|
10 |
protected function processAjaxAction( string $action ) :array {
|
11 |
|
12 |
switch ( $action ) {
|
13 |
case 'ip_insert':
|
14 |
-
$
|
15 |
break;
|
16 |
|
17 |
case 'ip_delete':
|
18 |
-
$
|
19 |
break;
|
20 |
|
21 |
case 'render_table_ip':
|
22 |
-
$
|
23 |
break;
|
24 |
|
25 |
case 'build_ip_analyse':
|
26 |
-
$
|
|
|
|
|
|
|
|
|
27 |
break;
|
28 |
|
29 |
default:
|
30 |
-
$
|
31 |
}
|
32 |
|
33 |
-
return $
|
34 |
}
|
35 |
|
36 |
private function ajaxExec_AddIp() :array {
|
37 |
-
/** @var
|
38 |
$mod = $this->getMod();
|
39 |
$oIpServ = Services::IP();
|
40 |
|
@@ -43,17 +49,17 @@ class AjaxHandler extends Shield\Modules\Base\AjaxHandlerShield {
|
|
43 |
$bSuccess = false;
|
44 |
$sMessage = __( "IP address wasn't added to the list", 'wp-simple-firewall' );
|
45 |
|
46 |
-
$
|
47 |
$sList = isset( $aFormParams[ 'list' ] ) ? $aFormParams[ 'list' ] : '';
|
48 |
|
49 |
-
$bAcceptableIp = $oIpServ->isValidIp( $
|
50 |
-
|| $oIpServ->isValidIp4Range( $
|
51 |
-
|| $oIpServ->isValidIp6Range( $
|
52 |
|
53 |
$bIsBlackList = $sList != $mod::LIST_MANUAL_WHITE;
|
54 |
|
55 |
// TODO: Bring this IP verification out of here and make it more accessible
|
56 |
-
if ( empty( $
|
57 |
$sMessage = __( "IP address not provided", 'wp-simple-firewall' );
|
58 |
}
|
59 |
elseif ( empty( $sList ) ) {
|
@@ -65,22 +71,22 @@ class AjaxHandler extends Shield\Modules\Base\AjaxHandlerShield {
|
|
65 |
elseif ( $bIsBlackList && !$mod->isPremium() ) {
|
66 |
$sMessage = __( "Please upgrade to Pro if you'd like to add IPs to the black list manually.", 'wp-simple-firewall' );
|
67 |
}
|
68 |
-
elseif ( $bIsBlackList && $oIpServ->checkIp( $oIpServ->getRequestIp(), $
|
69 |
$sMessage = __( "Manually black listing your current IP address is not supported.", 'wp-simple-firewall' );
|
70 |
}
|
71 |
-
elseif ( $bIsBlackList && in_array( $
|
72 |
$sMessage = __( "This IP is reserved and can't be blacklisted.", 'wp-simple-firewall' );
|
73 |
}
|
74 |
else {
|
75 |
-
$
|
76 |
$oIP = null;
|
77 |
switch ( $sList ) {
|
78 |
case $mod::LIST_MANUAL_WHITE:
|
79 |
try {
|
80 |
$oIP = ( new Shield\Modules\IPs\Lib\Ops\AddIp() )
|
81 |
->setMod( $mod )
|
82 |
-
->setIP( $
|
83 |
-
->toManualWhitelist( $
|
84 |
}
|
85 |
catch ( \Exception $oE ) {
|
86 |
}
|
@@ -90,8 +96,8 @@ class AjaxHandler extends Shield\Modules\Base\AjaxHandlerShield {
|
|
90 |
try {
|
91 |
$oIP = ( new Shield\Modules\IPs\Lib\Ops\AddIp() )
|
92 |
->setMod( $mod )
|
93 |
-
->setIP( $
|
94 |
-
->toManualBlacklist( $
|
95 |
}
|
96 |
catch ( \Exception $oE ) {
|
97 |
}
|
@@ -114,15 +120,15 @@ class AjaxHandler extends Shield\Modules\Base\AjaxHandlerShield {
|
|
114 |
}
|
115 |
|
116 |
private function ajaxExec_IpDelete() :array {
|
117 |
-
/** @var
|
118 |
-
$
|
119 |
$bSuccess = false;
|
120 |
$nId = Services::Request()->post( 'rid', -1 );
|
121 |
|
122 |
if ( !is_numeric( $nId ) || $nId < 0 ) {
|
123 |
$sMessage = __( 'Invalid entry selected', 'wp-simple-firewall' );
|
124 |
}
|
125 |
-
elseif ( $
|
126 |
$sMessage = __( 'IP address deleted', 'wp-simple-firewall' );
|
127 |
$bSuccess = true;
|
128 |
}
|
@@ -137,21 +143,106 @@ class AjaxHandler extends Shield\Modules\Base\AjaxHandlerShield {
|
|
137 |
}
|
138 |
|
139 |
private function ajaxExec_BuildTableIps() :array {
|
140 |
-
/** @var
|
141 |
$mod = $this->getMod();
|
142 |
|
143 |
-
$
|
144 |
-
$
|
145 |
|
146 |
return [
|
147 |
'success' => true,
|
148 |
'html' => ( new Shield\Tables\Build\Ip() )
|
149 |
->setMod( $mod )
|
150 |
-
->setDbHandler( $
|
151 |
->render()
|
152 |
];
|
153 |
}
|
154 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
155 |
private function ajaxExec_BuildIpAnalyse() :array {
|
156 |
try {
|
157 |
$ip = Services::Request()->post( 'fIp', '' );
|
3 |
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\IPs;
|
4 |
|
5 |
use FernleafSystems\Wordpress\Plugin\Shield;
|
6 |
+
use FernleafSystems\Wordpress\Plugin\Shield\Modules\IPs\Lib\Ops;
|
7 |
use FernleafSystems\Wordpress\Services\Services;
|
8 |
+
use FernleafSystems\Wordpress\Services\Utilities\Net\IpIdentify;
|
9 |
|
10 |
+
class AjaxHandler extends Shield\Modules\BaseShield\AjaxHandler {
|
11 |
|
12 |
protected function processAjaxAction( string $action ) :array {
|
13 |
|
14 |
switch ( $action ) {
|
15 |
case 'ip_insert':
|
16 |
+
$response = $this->ajaxExec_AddIp();
|
17 |
break;
|
18 |
|
19 |
case 'ip_delete':
|
20 |
+
$response = $this->ajaxExec_IpDelete();
|
21 |
break;
|
22 |
|
23 |
case 'render_table_ip':
|
24 |
+
$response = $this->ajaxExec_BuildTableIps();
|
25 |
break;
|
26 |
|
27 |
case 'build_ip_analyse':
|
28 |
+
$response = $this->ajaxExec_BuildIpAnalyse();
|
29 |
+
break;
|
30 |
+
|
31 |
+
case 'ip_analyse_action':
|
32 |
+
$response = $this->ajaxExec_IpAnalyseAction();
|
33 |
break;
|
34 |
|
35 |
default:
|
36 |
+
$response = parent::processAjaxAction( $action );
|
37 |
}
|
38 |
|
39 |
+
return $response;
|
40 |
}
|
41 |
|
42 |
private function ajaxExec_AddIp() :array {
|
43 |
+
/** @var ModCon $mod */
|
44 |
$mod = $this->getMod();
|
45 |
$oIpServ = Services::IP();
|
46 |
|
49 |
$bSuccess = false;
|
50 |
$sMessage = __( "IP address wasn't added to the list", 'wp-simple-firewall' );
|
51 |
|
52 |
+
$ip = preg_replace( '#[^/:.a-f\d]#i', '', ( isset( $aFormParams[ 'ip' ] ) ? $aFormParams[ 'ip' ] : '' ) );
|
53 |
$sList = isset( $aFormParams[ 'list' ] ) ? $aFormParams[ 'list' ] : '';
|
54 |
|
55 |
+
$bAcceptableIp = $oIpServ->isValidIp( $ip )
|
56 |
+
|| $oIpServ->isValidIp4Range( $ip )
|
57 |
+
|| $oIpServ->isValidIp6Range( $ip );
|
58 |
|
59 |
$bIsBlackList = $sList != $mod::LIST_MANUAL_WHITE;
|
60 |
|
61 |
// TODO: Bring this IP verification out of here and make it more accessible
|
62 |
+
if ( empty( $ip ) ) {
|
63 |
$sMessage = __( "IP address not provided", 'wp-simple-firewall' );
|
64 |
}
|
65 |
elseif ( empty( $sList ) ) {
|
71 |
elseif ( $bIsBlackList && !$mod->isPremium() ) {
|
72 |
$sMessage = __( "Please upgrade to Pro if you'd like to add IPs to the black list manually.", 'wp-simple-firewall' );
|
73 |
}
|
74 |
+
elseif ( $bIsBlackList && $oIpServ->checkIp( $oIpServ->getRequestIp(), $ip ) ) {
|
75 |
$sMessage = __( "Manually black listing your current IP address is not supported.", 'wp-simple-firewall' );
|
76 |
}
|
77 |
+
elseif ( $bIsBlackList && in_array( $ip, Services::IP()->getServerPublicIPs() ) ) {
|
78 |
$sMessage = __( "This IP is reserved and can't be blacklisted.", 'wp-simple-firewall' );
|
79 |
}
|
80 |
else {
|
81 |
+
$label = $aFormParams[ 'label' ] ?? '';
|
82 |
$oIP = null;
|
83 |
switch ( $sList ) {
|
84 |
case $mod::LIST_MANUAL_WHITE:
|
85 |
try {
|
86 |
$oIP = ( new Shield\Modules\IPs\Lib\Ops\AddIp() )
|
87 |
->setMod( $mod )
|
88 |
+
->setIP( $ip )
|
89 |
+
->toManualWhitelist( (string)$label );
|
90 |
}
|
91 |
catch ( \Exception $oE ) {
|
92 |
}
|
96 |
try {
|
97 |
$oIP = ( new Shield\Modules\IPs\Lib\Ops\AddIp() )
|
98 |
->setMod( $mod )
|
99 |
+
->setIP( $ip )
|
100 |
+
->toManualBlacklist( (string)$label );
|
101 |
}
|
102 |
catch ( \Exception $oE ) {
|
103 |
}
|
120 |
}
|
121 |
|
122 |
private function ajaxExec_IpDelete() :array {
|
123 |
+
/** @var ModCon $mod */
|
124 |
+
$mod = $this->getMod();
|
125 |
$bSuccess = false;
|
126 |
$nId = Services::Request()->post( 'rid', -1 );
|
127 |
|
128 |
if ( !is_numeric( $nId ) || $nId < 0 ) {
|
129 |
$sMessage = __( 'Invalid entry selected', 'wp-simple-firewall' );
|
130 |
}
|
131 |
+
elseif ( $mod->getDbHandler_IPs()->getQueryDeleter()->deleteById( $nId ) ) {
|
132 |
$sMessage = __( 'IP address deleted', 'wp-simple-firewall' );
|
133 |
$bSuccess = true;
|
134 |
}
|
143 |
}
|
144 |
|
145 |
private function ajaxExec_BuildTableIps() :array {
|
146 |
+
/** @var ModCon $mod */
|
147 |
$mod = $this->getMod();
|
148 |
|
149 |
+
$dbh = $mod->getDbHandler_IPs();
|
150 |
+
$dbh->autoCleanDb();
|
151 |
|
152 |
return [
|
153 |
'success' => true,
|
154 |
'html' => ( new Shield\Tables\Build\Ip() )
|
155 |
->setMod( $mod )
|
156 |
+
->setDbHandler( $dbh )
|
157 |
->render()
|
158 |
];
|
159 |
}
|
160 |
|
161 |
+
private function ajaxExec_IpAnalyseAction() :array {
|
162 |
+
$req = Services::Request();
|
163 |
+
|
164 |
+
$ip = $req->post( 'ip' );
|
165 |
+
|
166 |
+
$ipIdentifier = new IpIdentify( $ip );
|
167 |
+
try {
|
168 |
+
$ipID = $ipIdentifier->run();
|
169 |
+
$ipKey = key( $ipID );
|
170 |
+
$validIP = true;
|
171 |
+
}
|
172 |
+
catch ( \Exception $e ) {
|
173 |
+
$ipKey = IpIdentify::UNKNOWN;
|
174 |
+
$validIP = false;
|
175 |
+
}
|
176 |
+
|
177 |
+
$success = false;
|
178 |
+
|
179 |
+
if ( $ipKey !== IpIdentify::UNKNOWN ) {
|
180 |
+
$msg = sprintf( __( "IP can't be processed from this page as it's a known service IP: %s" ), $ipIdentifier->getName( $ipKey ) );
|
181 |
+
}
|
182 |
+
elseif ( !$validIP ) {
|
183 |
+
$msg = __( "IP provided was invalid.", 'wp-simple-firewall' );
|
184 |
+
}
|
185 |
+
else {
|
186 |
+
$dbh = $this->getCon()->getModule_IPs()->getDbHandler_IPs();
|
187 |
+
switch ( $req->post( 'ip_action' ) ) {
|
188 |
+
|
189 |
+
case 'block':
|
190 |
+
try {
|
191 |
+
$success = ( new Ops\AddIp() )
|
192 |
+
->setMod( $this->getMod() )
|
193 |
+
->setIP( $ip )
|
194 |
+
->toManualBlacklist() instanceof Shield\Databases\IPs\EntryVO;
|
195 |
+
}
|
196 |
+
catch ( \Exception $e ) {
|
197 |
+
}
|
198 |
+
$msg = $success ? __( 'IP address blocked.', 'wp-simple-firewall' )
|
199 |
+
: __( "IP address couldn't be blocked at this time.", 'wp-simple-firewall' );
|
200 |
+
break;
|
201 |
+
|
202 |
+
case 'unblock':
|
203 |
+
$success = ( new Ops\DeleteIp() )
|
204 |
+
->setDbHandler( $dbh )
|
205 |
+
->setIP( $ip )
|
206 |
+
->fromBlacklist();
|
207 |
+
$msg = $success ? __( 'IP address unblocked.', 'wp-simple-firewall' )
|
208 |
+
: __( "IP address couldn't be unblocked at this time.", 'wp-simple-firewall' );
|
209 |
+
break;
|
210 |
+
|
211 |
+
case 'bypass':
|
212 |
+
try {
|
213 |
+
$success = ( new Ops\AddIp() )
|
214 |
+
->setMod( $this->getMod() )
|
215 |
+
->setIP( $ip )
|
216 |
+
->toManualWhitelist() instanceof Shield\Databases\IPs\EntryVO;
|
217 |
+
}
|
218 |
+
catch ( \Exception $e ) {
|
219 |
+
}
|
220 |
+
$msg = $success ? __( 'IP address added to Bypass list.', 'wp-simple-firewall' )
|
221 |
+
: __( "IP address couldn't be added to Bypass list at this time.", 'wp-simple-firewall' );
|
222 |
+
break;
|
223 |
+
|
224 |
+
case 'unbypass':
|
225 |
+
$success = ( new Ops\DeleteIp() )
|
226 |
+
->setDbHandler( $dbh )
|
227 |
+
->setIP( $ip )
|
228 |
+
->fromWhiteList();
|
229 |
+
$msg = $success ? __( 'IP address removed from Bypass list.', 'wp-simple-firewall' )
|
230 |
+
: __( "IP address couldn't be removed from Bypass list at this time.", 'wp-simple-firewall' );
|
231 |
+
break;
|
232 |
+
|
233 |
+
default:
|
234 |
+
$msg = __( 'Unsupported Action.', 'wp-simple-firewall' );
|
235 |
+
break;
|
236 |
+
}
|
237 |
+
}
|
238 |
+
|
239 |
+
return [
|
240 |
+
'success' => $success,
|
241 |
+
'message' => $msg,
|
242 |
+
'page_reload' => true,
|
243 |
+
];
|
244 |
+
}
|
245 |
+
|
246 |
private function ajaxExec_BuildIpAnalyse() :array {
|
247 |
try {
|
248 |
$ip = Services::Request()->post( 'fIp', '' );
|
src/lib/src/Modules/IPs/Components/ImportIpsFromFile.php
CHANGED
@@ -11,33 +11,30 @@ class ImportIpsFromFile {
|
|
11 |
use Shield\Modules\ModConsumer;
|
12 |
|
13 |
public function run() {
|
14 |
-
foreach ( [ 'black', 'white' ] as $
|
15 |
-
$this->runFileImport( $
|
16 |
}
|
17 |
}
|
18 |
|
19 |
-
|
20 |
-
|
21 |
-
*/
|
22 |
-
private function runFileImport( $sType ) {
|
23 |
-
$oFS = Services::WpFs();
|
24 |
|
25 |
-
$
|
26 |
-
if ( $
|
27 |
-
$
|
28 |
-
if ( !empty( $
|
29 |
$oAdd = ( new IPs\Lib\Ops\AddIp() )->setMod( $this->getMod() );
|
30 |
-
foreach ( array_map( 'trim', explode( "\n", $
|
31 |
$oAdd->setIP( $sIP );
|
32 |
try {
|
33 |
-
$
|
34 |
: $oAdd->toManualBlacklist( 'file import' );
|
35 |
}
|
36 |
-
catch ( \Exception $
|
37 |
}
|
38 |
}
|
39 |
}
|
40 |
-
$
|
41 |
}
|
42 |
}
|
43 |
}
|
11 |
use Shield\Modules\ModConsumer;
|
12 |
|
13 |
public function run() {
|
14 |
+
foreach ( [ 'black', 'white' ] as $type ) {
|
15 |
+
$this->runFileImport( $type );
|
16 |
}
|
17 |
}
|
18 |
|
19 |
+
private function runFileImport( string $type ) {
|
20 |
+
$FS = Services::WpFs();
|
|
|
|
|
|
|
21 |
|
22 |
+
$fileImport = $FS->findFileInDir( 'ip_import_'.$type, $this->getCon()->getPath_Flags() );
|
23 |
+
if ( $FS->isFile( $fileImport ) ) {
|
24 |
+
$content = $FS->getFileContent( $fileImport );
|
25 |
+
if ( !empty( $content ) ) {
|
26 |
$oAdd = ( new IPs\Lib\Ops\AddIp() )->setMod( $this->getMod() );
|
27 |
+
foreach ( array_map( 'trim', explode( "\n", $content ) ) as $sIP ) {
|
28 |
$oAdd->setIP( $sIP );
|
29 |
try {
|
30 |
+
$type == 'white' ? $oAdd->toManualWhitelist( 'file import' )
|
31 |
: $oAdd->toManualBlacklist( 'file import' );
|
32 |
}
|
33 |
+
catch ( \Exception $e ) {
|
34 |
}
|
35 |
}
|
36 |
}
|
37 |
+
$FS->deleteFile( $fileImport );
|
38 |
}
|
39 |
}
|
40 |
}
|
src/lib/src/Modules/IPs/Components/ProcessOffense.php
CHANGED
@@ -17,9 +17,9 @@ class ProcessOffense {
|
|
17 |
use IpAddressConsumer;
|
18 |
|
19 |
public function run() {
|
20 |
-
/** @var \ICWP_WPSF_FeatureHandler_Ips $mod */
|
21 |
-
$mod = $this->getMod();
|
22 |
$con = $this->getCon();
|
|
|
|
|
23 |
/** @var IPs\Options $opts */
|
24 |
$opts = $this->getOptions();
|
25 |
|
@@ -29,7 +29,7 @@ class ProcessOffense {
|
|
29 |
->setIP( $this->getIP() )
|
30 |
->toAutoBlacklist();
|
31 |
}
|
32 |
-
catch ( \Exception $
|
33 |
$oIP = null;
|
34 |
}
|
35 |
|
17 |
use IpAddressConsumer;
|
18 |
|
19 |
public function run() {
|
|
|
|
|
20 |
$con = $this->getCon();
|
21 |
+
/** @var IPs\ModCon $mod */
|
22 |
+
$mod = $this->getMod();
|
23 |
/** @var IPs\Options $opts */
|
24 |
$opts = $this->getOptions();
|
25 |
|
29 |
->setIP( $this->getIP() )
|
30 |
->toAutoBlacklist();
|
31 |
}
|
32 |
+
catch ( \Exception $e ) {
|
33 |
$oIP = null;
|
34 |
}
|
35 |
|
src/lib/src/Modules/IPs/Components/QueryIpBlock.php
CHANGED
@@ -32,10 +32,10 @@ class QueryIpBlock {
|
|
32 |
|
33 |
$bIpBlocked = true;
|
34 |
|
35 |
-
/** @var \
|
36 |
-
$
|
37 |
/** @var Databases\IPs\Update $oUp */
|
38 |
-
$oUp = $
|
39 |
$oUp->updateLastAccessAt( $oIP );
|
40 |
}
|
41 |
return $bIpBlocked;
|
@@ -47,10 +47,10 @@ class QueryIpBlock {
|
|
47 |
private function getBlockedIpRecord() {
|
48 |
$oBlockIP = null;
|
49 |
|
50 |
-
/** @var \
|
51 |
-
$
|
52 |
$oIP = ( new IPs\Lib\Ops\LookupIpOnList() )
|
53 |
-
->setDbHandler( $
|
54 |
->setIP( $this->getIP() )
|
55 |
->setListTypeBlack()
|
56 |
->setIsIpBlocked( true )
|
@@ -61,11 +61,11 @@ class QueryIpBlock {
|
|
61 |
$oOpts = $this->getOptions();
|
62 |
|
63 |
// Clean out old IPs as we go so they don't show up in future queries.
|
64 |
-
if ( $oIP->list == $
|
65 |
&& $oIP->last_access_at < Services::Request()->ts() - $oOpts->getAutoExpireTime() ) {
|
66 |
|
67 |
( new IPs\Lib\Ops\DeleteIp() )
|
68 |
-
->setDbHandler( $
|
69 |
->setIP( Services::IP()->getRequestIp() )
|
70 |
->fromBlacklist();
|
71 |
}
|
32 |
|
33 |
$bIpBlocked = true;
|
34 |
|
35 |
+
/** @var IPs\ModCon $mod */
|
36 |
+
$mod = $this->getMod();
|
37 |
/** @var Databases\IPs\Update $oUp */
|
38 |
+
$oUp = $mod->getDbHandler_IPs()->getQueryUpdater();
|
39 |
$oUp->updateLastAccessAt( $oIP );
|
40 |
}
|
41 |
return $bIpBlocked;
|
47 |
private function getBlockedIpRecord() {
|
48 |
$oBlockIP = null;
|
49 |
|
50 |
+
/** @var IPs\ModCon $mod */
|
51 |
+
$mod = $this->getMod();
|
52 |
$oIP = ( new IPs\Lib\Ops\LookupIpOnList() )
|
53 |
+
->setDbHandler( $mod->getDbHandler_IPs() )
|
54 |
->setIP( $this->getIP() )
|
55 |
->setListTypeBlack()
|
56 |
->setIsIpBlocked( true )
|
61 |
$oOpts = $this->getOptions();
|
62 |
|
63 |
// Clean out old IPs as we go so they don't show up in future queries.
|
64 |
+
if ( $oIP->list == $mod::LIST_AUTO_BLACK
|
65 |
&& $oIP->last_access_at < Services::Request()->ts() - $oOpts->getAutoExpireTime() ) {
|
66 |
|
67 |
( new IPs\Lib\Ops\DeleteIp() )
|
68 |
+
->setDbHandler( $mod->getDbHandler_IPs() )
|
69 |
->setIP( Services::IP()->getRequestIp() )
|
70 |
->fromBlacklist();
|
71 |
}
|
src/lib/src/Modules/IPs/Components/QueryRemainingOffenses.php
CHANGED
@@ -3,9 +3,8 @@
|
|
3 |
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\IPs\Components;
|
4 |
|
5 |
use FernleafSystems\Wordpress\Plugin\Shield;
|
6 |
-
use FernleafSystems\Wordpress\Plugin\Shield\Databases
|
7 |
-
use FernleafSystems\Wordpress\Plugin\Shield\Modules\IPs
|
8 |
-
use FernleafSystems\Wordpress\Plugin\Shield\Modules\IPs\Options;
|
9 |
|
10 |
/**
|
11 |
* Class QueryRemainingOffenses
|
@@ -20,20 +19,20 @@ class QueryRemainingOffenses {
|
|
20 |
* @return int
|
21 |
*/
|
22 |
public function run() {
|
23 |
-
/** @var \
|
24 |
-
$
|
25 |
-
$oBlackIp = ( new Ops\LookupIpOnList() )
|
26 |
-
->setDbHandler( $
|
27 |
->setListTypeBlack()
|
28 |
->setIP( $this->getIP() )
|
29 |
->lookup( false );
|
30 |
|
31 |
$nOffenses = 0;
|
32 |
-
if ( $oBlackIp instanceof IPs\EntryVO ) {
|
33 |
$nOffenses = (int)$oBlackIp->transgressions;
|
34 |
}
|
35 |
|
36 |
-
/** @var Options $oOpts */
|
37 |
$oOpts = $this->getOptions();
|
38 |
return $oOpts->getOffenseLimit() - $nOffenses - 1;
|
39 |
}
|
3 |
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\IPs\Components;
|
4 |
|
5 |
use FernleafSystems\Wordpress\Plugin\Shield;
|
6 |
+
use FernleafSystems\Wordpress\Plugin\Shield\Databases;
|
7 |
+
use FernleafSystems\Wordpress\Plugin\Shield\Modules\IPs;
|
|
|
8 |
|
9 |
/**
|
10 |
* Class QueryRemainingOffenses
|
19 |
* @return int
|
20 |
*/
|
21 |
public function run() {
|
22 |
+
/** @var IPs\ModCon $mod */
|
23 |
+
$mod = $this->getMod();
|
24 |
+
$oBlackIp = ( new IPs\Lib\Ops\LookupIpOnList() )
|
25 |
+
->setDbHandler( $mod->getDbHandler_IPs() )
|
26 |
->setListTypeBlack()
|
27 |
->setIP( $this->getIP() )
|
28 |
->lookup( false );
|
29 |
|
30 |
$nOffenses = 0;
|
31 |
+
if ( $oBlackIp instanceof Databases\IPs\EntryVO ) {
|
32 |
$nOffenses = (int)$oBlackIp->transgressions;
|
33 |
}
|
34 |
|
35 |
+
/** @var IPs\Options $oOpts */
|
36 |
$oOpts = $this->getOptions();
|
37 |
return $oOpts->getOffenseLimit() - $nOffenses - 1;
|
38 |
}
|
src/lib/src/Modules/IPs/Components/UnblockIpByFlag.php
CHANGED
@@ -11,19 +11,19 @@ class UnblockIpByFlag {
|
|
11 |
use Shield\Modules\ModConsumer;
|
12 |
|
13 |
public function run() {
|
14 |
-
/** @var \
|
15 |
-
$
|
16 |
-
$
|
17 |
|
18 |
-
$sPathUnblockFlag = $
|
19 |
-
if ( $
|
20 |
-
$sContent = $
|
21 |
if ( !empty( $sContent ) ) {
|
22 |
|
23 |
$aLines = array_map( 'trim', explode( "\n", $sContent ) );
|
24 |
foreach ( $aLines as $sIp ) {
|
25 |
$bRemoved = ( new IPs\Lib\Ops\DeleteIp() )
|
26 |
-
->setDbHandler( $
|
27 |
->setIP( $sIp )
|
28 |
->fromBlacklist();
|
29 |
if ( $bRemoved ) {
|
@@ -31,7 +31,7 @@ class UnblockIpByFlag {
|
|
31 |
}
|
32 |
}
|
33 |
}
|
34 |
-
$
|
35 |
}
|
36 |
}
|
37 |
}
|
11 |
use Shield\Modules\ModConsumer;
|
12 |
|
13 |
public function run() {
|
14 |
+
/** @var IPs\ModCon $mod */
|
15 |
+
$mod = $this->getMod();
|
16 |
+
$FS = Services::WpFs();
|
17 |
|
18 |
+
$sPathUnblockFlag = $FS->findFileInDir( 'unblock', $this->getCon()->getPath_Flags() );
|
19 |
+
if ( $FS->isFile( $sPathUnblockFlag ) ) {
|
20 |
+
$sContent = $FS->getFileContent( $sPathUnblockFlag );
|
21 |
if ( !empty( $sContent ) ) {
|
22 |
|
23 |
$aLines = array_map( 'trim', explode( "\n", $sContent ) );
|
24 |
foreach ( $aLines as $sIp ) {
|
25 |
$bRemoved = ( new IPs\Lib\Ops\DeleteIp() )
|
26 |
+
->setDbHandler( $mod->getDbHandler_IPs() )
|
27 |
->setIP( $sIp )
|
28 |
->fromBlacklist();
|
29 |
if ( $bRemoved ) {
|
31 |
}
|
32 |
}
|
33 |
}
|
34 |
+
$FS->deleteFile( $sPathUnblockFlag );
|
35 |
}
|
36 |
}
|
37 |
}
|
src/lib/src/Modules/IPs/Lib/AutoUnblock.php
CHANGED
@@ -3,6 +3,7 @@
|
|
3 |
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\IPs\Lib;
|
4 |
|
5 |
use FernleafSystems\Wordpress\Plugin\Shield\Modules\IPs;
|
|
|
6 |
use FernleafSystems\Wordpress\Plugin\Shield\Modules\ModConsumer;
|
7 |
use FernleafSystems\Wordpress\Services\Services;
|
8 |
|
@@ -36,7 +37,7 @@ class AutoUnblock {
|
|
36 |
* @throws \Exception
|
37 |
*/
|
38 |
private function processAutoUnblockRequest() :bool {
|
39 |
-
/** @var \
|
40 |
$mod = $this->getMod();
|
41 |
/** @var IPs\Options $opts */
|
42 |
$opts = $this->getOptions();
|
@@ -96,7 +97,7 @@ class AutoUnblock {
|
|
96 |
* @throws \Exception
|
97 |
*/
|
98 |
private function processUserMagicLink() :bool {
|
99 |
-
/** @var \
|
100 |
$mod = $this->getMod();
|
101 |
/** @var IPs\Options $opts */
|
102 |
$opts = $this->getOptions();
|
@@ -165,7 +166,7 @@ class AutoUnblock {
|
|
165 |
* @throws \Exception
|
166 |
*/
|
167 |
private function sendMagicLinkEmail() {
|
168 |
-
/** @var \
|
169 |
$mod = $this->getMod();
|
170 |
$user = Services::WpUsers()->getCurrentWpUser();
|
171 |
|
3 |
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\IPs\Lib;
|
4 |
|
5 |
use FernleafSystems\Wordpress\Plugin\Shield\Modules\IPs;
|
6 |
+
use FernleafSystems\Wordpress\Plugin\Shield\Modules\IPs\ModCon;
|
7 |
use FernleafSystems\Wordpress\Plugin\Shield\Modules\ModConsumer;
|
8 |
use FernleafSystems\Wordpress\Services\Services;
|
9 |
|
37 |
* @throws \Exception
|
38 |
*/
|
39 |
private function processAutoUnblockRequest() :bool {
|
40 |
+
/** @var IPs\ModCon $mod */
|
41 |
$mod = $this->getMod();
|
42 |
/** @var IPs\Options $opts */
|
43 |
$opts = $this->getOptions();
|
97 |
* @throws \Exception
|
98 |
*/
|
99 |
private function processUserMagicLink() :bool {
|
100 |
+
/** @var IPs\ModCon $mod */
|
101 |
$mod = $this->getMod();
|
102 |
/** @var IPs\Options $opts */
|
103 |
$opts = $this->getOptions();
|
166 |
* @throws \Exception
|
167 |
*/
|
168 |
private function sendMagicLinkEmail() {
|
169 |
+
/** @var IPs\ModCon $mod */
|
170 |
$mod = $this->getMod();
|
171 |
$user = Services::WpUsers()->getCurrentWpUser();
|
172 |
|
src/lib/src/Modules/IPs/Lib/BlacklistHandler.php
CHANGED
@@ -13,7 +13,7 @@ class BlacklistHandler {
|
|
13 |
use OneTimeExecute;
|
14 |
|
15 |
protected function run() {
|
16 |
-
/** @var \
|
17 |
$mod = $this->getMod();
|
18 |
/** @var IPs\Options $oOpts */
|
19 |
$oOpts = $this->getOptions();
|
@@ -47,7 +47,7 @@ class BlacklistHandler {
|
|
47 |
}
|
48 |
|
49 |
public function loadBotDetectors() {
|
50 |
-
/** @var \
|
51 |
$mod = $this->getMod();
|
52 |
/** @var IPs\Options $opts */
|
53 |
$opts = $this->getOptions();
|
13 |
use OneTimeExecute;
|
14 |
|
15 |
protected function run() {
|
16 |
+
/** @var IPs\ModCon $mod */
|
17 |
$mod = $this->getMod();
|
18 |
/** @var IPs\Options $oOpts */
|
19 |
$oOpts = $this->getOptions();
|
47 |
}
|
48 |
|
49 |
public function loadBotDetectors() {
|
50 |
+
/** @var IPs\ModCon $mod */
|
51 |
$mod = $this->getMod();
|
52 |
/** @var IPs\Options $opts */
|
53 |
$opts = $this->getOptions();
|
src/lib/src/Modules/IPs/Lib/BlockRequest.php
CHANGED
@@ -46,7 +46,7 @@ class BlockRequest {
|
|
46 |
}
|
47 |
|
48 |
private function renderKillPage() {
|
49 |
-
/** @var \
|
50 |
$mod = $this->getMod();
|
51 |
/** @var IPs\Options $opts */
|
52 |
$opts = $this->getOptions();
|
@@ -65,11 +65,18 @@ class BlockRequest {
|
|
65 |
&& $opts->getCanRequestAutoUnblockEmailLink( $user );
|
66 |
$bCanAutoRecover = $bCanUauGasp || $bCanUauMagic;
|
67 |
|
68 |
-
$
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
69 |
'strings' => [
|
70 |
'title' => sprintf( __( "You've been blocked by the %s plugin", 'wp-simple-firewall' ),
|
71 |
sprintf( '<a href="%s" target="_blank">%s</a>',
|
72 |
-
$
|
73 |
$con->getHumanName()
|
74 |
)
|
75 |
),
|
@@ -116,7 +123,7 @@ class BlockRequest {
|
|
116 |
];
|
117 |
Services::WpGeneral()
|
118 |
->wpDie(
|
119 |
-
$mod->renderTemplate( '/pages/block/blocklist_die.twig', $
|
120 |
);
|
121 |
}
|
122 |
|
@@ -124,7 +131,7 @@ class BlockRequest {
|
|
124 |
* @return string
|
125 |
*/
|
126 |
private function renderEmailMagicLinkContent() {
|
127 |
-
/** @var \
|
128 |
$mod = $this->getMod();
|
129 |
/** @var IPs\Options $opts */
|
130 |
$opts = $this->getOptions();
|
46 |
}
|
47 |
|
48 |
private function renderKillPage() {
|
49 |
+
/** @var IPs\ModCon $mod */
|
50 |
$mod = $this->getMod();
|
51 |
/** @var IPs\Options $opts */
|
52 |
$opts = $this->getOptions();
|
65 |
&& $opts->getCanRequestAutoUnblockEmailLink( $user );
|
66 |
$bCanAutoRecover = $bCanUauGasp || $bCanUauMagic;
|
67 |
|
68 |
+
if ( !empty( $con->getLabels()[ 'PluginURI' ] ) ) {
|
69 |
+
$homeURL = $con->getLabels()[ 'PluginURI' ];
|
70 |
+
}
|
71 |
+
else {
|
72 |
+
$homeURL = $con->getPluginSpec()[ 'meta' ][ 'url_repo_home' ];
|
73 |
+
}
|
74 |
+
|
75 |
+
$data = [
|
76 |
'strings' => [
|
77 |
'title' => sprintf( __( "You've been blocked by the %s plugin", 'wp-simple-firewall' ),
|
78 |
sprintf( '<a href="%s" target="_blank">%s</a>',
|
79 |
+
$homeURL,
|
80 |
$con->getHumanName()
|
81 |
)
|
82 |
),
|
123 |
];
|
124 |
Services::WpGeneral()
|
125 |
->wpDie(
|
126 |
+
$mod->renderTemplate( '/pages/block/blocklist_die.twig', $data, true )
|
127 |
);
|
128 |
}
|
129 |
|
131 |
* @return string
|
132 |
*/
|
133 |
private function renderEmailMagicLinkContent() {
|
134 |
+
/** @var IPs\ModCon $mod */
|
135 |
$mod = $this->getMod();
|
136 |
/** @var IPs\Options $opts */
|
137 |
$opts = $this->getOptions();
|
src/lib/src/Modules/IPs/Lib/IpAnalyse/BuildDisplay.php
CHANGED
@@ -6,8 +6,10 @@ use FernleafSystems\Wordpress\Plugin\Shield\Databases;
|
|
6 |
use FernleafSystems\Wordpress\Plugin\Shield\Modules\GeoIp\Lookup;
|
7 |
use FernleafSystems\Wordpress\Plugin\Shield\Modules\IPs\Components\IpAddressConsumer;
|
8 |
use FernleafSystems\Wordpress\Plugin\Shield\Modules\IPs\Lib\Ops\LookupIpOnList;
|
|
|
9 |
use FernleafSystems\Wordpress\Plugin\Shield\Modules\ModConsumer;
|
10 |
use FernleafSystems\Wordpress\Services\Services;
|
|
|
11 |
|
12 |
class BuildDisplay {
|
13 |
|
@@ -19,7 +21,7 @@ class BuildDisplay {
|
|
19 |
* @throws \Exception
|
20 |
*/
|
21 |
public function run() :string {
|
22 |
-
/** @var
|
23 |
$mod = $this->getMod();
|
24 |
|
25 |
$ip = $this->getIP();
|
@@ -77,6 +79,28 @@ class BuildDisplay {
|
|
77 |
|
78 |
$sRDNS = gethostbyaddr( $ip );
|
79 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
80 |
return $this->getMod()->renderTemplate(
|
81 |
'/wpadmin_pages/insights/ips/ip_analyse/ip_general.twig',
|
82 |
[
|
@@ -84,6 +108,11 @@ class BuildDisplay {
|
|
84 |
'title_general' => __( 'Identifying Info', 'wp-simple-firewall' ),
|
85 |
'title_status' => __( 'IP Status', 'wp-simple-firewall' ),
|
86 |
|
|
|
|
|
|
|
|
|
|
|
87 |
'status' => [
|
88 |
'is_you' => __( 'Is It You?', 'wp-simple-firewall' ),
|
89 |
'offenses' => __( 'Number of offenses', 'wp-simple-firewall' ),
|
@@ -117,9 +146,7 @@ class BuildDisplay {
|
|
117 |
'is_bypass' => $oBypassIP instanceof Databases\IPs\EntryVO,
|
118 |
],
|
119 |
'identity' => [
|
120 |
-
'who_is_it' =>
|
121 |
-
->getIpDetector()
|
122 |
-
->getIPIdentity(),
|
123 |
'rdns' => $sRDNS === $ip ? __( 'Unavailable', 'wp-simple-firewall' ) : $sRDNS,
|
124 |
'country_name' => $validGeo ? $geo->getCountryName() : __( 'Unknown', 'wp-simple-firewall' ),
|
125 |
'timezone' => $validGeo ? $geo->getTimezone() : __( 'Unknown', 'wp-simple-firewall' ),
|
6 |
use FernleafSystems\Wordpress\Plugin\Shield\Modules\GeoIp\Lookup;
|
7 |
use FernleafSystems\Wordpress\Plugin\Shield\Modules\IPs\Components\IpAddressConsumer;
|
8 |
use FernleafSystems\Wordpress\Plugin\Shield\Modules\IPs\Lib\Ops\LookupIpOnList;
|
9 |
+
use FernleafSystems\Wordpress\Plugin\Shield\Modules\IPs\ModCon;
|
10 |
use FernleafSystems\Wordpress\Plugin\Shield\Modules\ModConsumer;
|
11 |
use FernleafSystems\Wordpress\Services\Services;
|
12 |
+
use FernleafSystems\Wordpress\Services\Utilities\Net\IpIdentify;
|
13 |
|
14 |
class BuildDisplay {
|
15 |
|
21 |
* @throws \Exception
|
22 |
*/
|
23 |
public function run() :string {
|
24 |
+
/** @var ModCon $mod */
|
25 |
$mod = $this->getMod();
|
26 |
|
27 |
$ip = $this->getIP();
|
79 |
|
80 |
$sRDNS = gethostbyaddr( $ip );
|
81 |
|
82 |
+
$ipIdentifier = new IpIdentify( $ip );
|
83 |
+
try {
|
84 |
+
$ipWhoIs = $ipIdentifier->run();
|
85 |
+
$ipIdKey = key( $ipWhoIs );
|
86 |
+
$ipID = current( $ipWhoIs );
|
87 |
+
}
|
88 |
+
catch ( \Exception $e ) {
|
89 |
+
$ipIdKey = IpIdentify::UNKNOWN;
|
90 |
+
$ipID = $ipIdentifier->getName( $ipIdKey );
|
91 |
+
}
|
92 |
+
|
93 |
+
if ( $ipIdKey === IpIdentify::UNKNOWN ) {
|
94 |
+
$ipEntry = ( new LookupIpOnList() )
|
95 |
+
->setDbHandler( $dbh )
|
96 |
+
->setIP( $ip )
|
97 |
+
->setListTypeWhite()
|
98 |
+
->lookup();
|
99 |
+
if ( $ipEntry instanceof Databases\IPs\EntryVO ) {
|
100 |
+
$ipID = $ipEntry->label;
|
101 |
+
}
|
102 |
+
}
|
103 |
+
|
104 |
return $this->getMod()->renderTemplate(
|
105 |
'/wpadmin_pages/insights/ips/ip_analyse/ip_general.twig',
|
106 |
[
|
108 |
'title_general' => __( 'Identifying Info', 'wp-simple-firewall' ),
|
109 |
'title_status' => __( 'IP Status', 'wp-simple-firewall' ),
|
110 |
|
111 |
+
'block_ip' => __( 'Block IP', 'wp-simple-firewall' ),
|
112 |
+
'unblock_ip' => __( 'Unblock IP', 'wp-simple-firewall' ),
|
113 |
+
'bypass_ip' => __( 'Add IP Bypass', 'wp-simple-firewall' ),
|
114 |
+
'unbypass_ip' => __( 'Remove IP Bypass', 'wp-simple-firewall' ),
|
115 |
+
|
116 |
'status' => [
|
117 |
'is_you' => __( 'Is It You?', 'wp-simple-firewall' ),
|
118 |
'offenses' => __( 'Number of offenses', 'wp-simple-firewall' ),
|
146 |
'is_bypass' => $oBypassIP instanceof Databases\IPs\EntryVO,
|
147 |
],
|
148 |
'identity' => [
|
149 |
+
'who_is_it' => $ipID,
|
|
|
|
|
150 |
'rdns' => $sRDNS === $ip ? __( 'Unavailable', 'wp-simple-firewall' ) : $sRDNS,
|
151 |
'country_name' => $validGeo ? $geo->getCountryName() : __( 'Unknown', 'wp-simple-firewall' ),
|
152 |
'timezone' => $validGeo ? $geo->getTimezone() : __( 'Unknown', 'wp-simple-firewall' ),
|
src/lib/src/Modules/IPs/Lib/OffenseTracker.php
CHANGED
@@ -17,13 +17,13 @@ class OffenseTracker extends EventsListener {
|
|
17 |
private $nOffenseCount = 0;
|
18 |
|
19 |
/**
|
20 |
-
* @param string $
|
21 |
* @param array $aMeta
|
22 |
*/
|
23 |
-
protected function captureEvent( $
|
24 |
$aDef = $this->getCon()
|
25 |
->loadEventsService()
|
26 |
-
->getEventDef( $
|
27 |
|
28 |
if ( !empty( $aDef ) && !empty( $aDef[ 'offense' ] ) && empty( $aMeta[ 'suppress_offense' ] ) ) {
|
29 |
$this->incrementCount( isset( $aMeta[ 'offense_count' ] ) ? $aMeta[ 'offense_count' ] : 1 );
|
17 |
private $nOffenseCount = 0;
|
18 |
|
19 |
/**
|
20 |
+
* @param string $evt
|
21 |
* @param array $aMeta
|
22 |
*/
|
23 |
+
protected function captureEvent( $evt, $aMeta = [] ) {
|
24 |
$aDef = $this->getCon()
|
25 |
->loadEventsService()
|
26 |
+
->getEventDef( $evt );
|
27 |
|
28 |
if ( !empty( $aDef ) && !empty( $aDef[ 'offense' ] ) && empty( $aMeta[ 'suppress_offense' ] ) ) {
|
29 |
$this->incrementCount( isset( $aMeta[ 'offense_count' ] ) ? $aMeta[ 'offense_count' ] : 1 );
|
src/lib/src/Modules/IPs/Lib/Ops/AddIp.php
CHANGED
@@ -4,6 +4,7 @@ namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\IPs\Lib\Ops;
|
|
4 |
|
5 |
use FernleafSystems\Wordpress\Plugin\Shield\Databases;
|
6 |
use FernleafSystems\Wordpress\Plugin\Shield\Modules;
|
|
|
7 |
use FernleafSystems\Wordpress\Services\Services;
|
8 |
|
9 |
/**
|
@@ -20,7 +21,7 @@ class AddIp {
|
|
20 |
* @throws \Exception
|
21 |
*/
|
22 |
public function toAutoBlacklist() {
|
23 |
-
/** @var
|
24 |
$mod = $this->getMod();
|
25 |
$oReq = Services::Request();
|
26 |
|
@@ -64,8 +65,8 @@ class AddIp {
|
|
64 |
* @throws \Exception
|
65 |
*/
|
66 |
public function toManualBlacklist( $sLabel = '' ) {
|
67 |
-
/** @var
|
68 |
-
$
|
69 |
$oIpServ = Services::IP();
|
70 |
|
71 |
$sIP = $this->getIP();
|
@@ -78,27 +79,27 @@ class AddIp {
|
|
78 |
|
79 |
if ( $oIpServ->isValidIpRange( $sIP ) ) {
|
80 |
( new DeleteIp() )
|
81 |
-
->setDbHandler( $
|
82 |
->setIP( $sIP )
|
83 |
->fromBlacklist();
|
84 |
}
|
85 |
|
86 |
$oIP = ( new LookupIpOnList() )
|
87 |
-
->setDbHandler( $
|
88 |
->setListTypeBlack()
|
89 |
->setIP( $sIP )
|
90 |
->lookup( false );
|
91 |
|
92 |
if ( !$oIP instanceof Databases\IPs\EntryVO ) {
|
93 |
-
$oIP = $this->add( $
|
94 |
}
|
95 |
|
96 |
$aUpdateData = [
|
97 |
'last_access_at' => Services::Request()->ts()
|
98 |
];
|
99 |
|
100 |
-
if ( $oIP->list != $
|
101 |
-
$aUpdateData[ 'list' ] = $
|
102 |
}
|
103 |
if ( $oIP->label != $sLabel ) {
|
104 |
$aUpdateData[ 'label' ] = $sLabel;
|
@@ -107,7 +108,7 @@ class AddIp {
|
|
107 |
$aUpdateData[ 'blocked_at' ] = Services::Request()->ts();
|
108 |
}
|
109 |
|
110 |
-
$
|
111 |
->getQueryUpdater()
|
112 |
->updateEntry( $oIP, $aUpdateData );
|
113 |
}
|
@@ -116,41 +117,41 @@ class AddIp {
|
|
116 |
}
|
117 |
|
118 |
/**
|
119 |
-
* @param string $
|
120 |
* @return Databases\IPs\EntryVO|null
|
121 |
* @throws \Exception
|
122 |
*/
|
123 |
-
public function toManualWhitelist( $
|
124 |
-
/** @var
|
125 |
-
$
|
126 |
$oIpServ = Services::IP();
|
127 |
|
128 |
-
$
|
129 |
-
if ( !$oIpServ->isValidIp( $
|
130 |
throw new \Exception( "IP address isn't valid." );
|
131 |
}
|
132 |
|
133 |
-
if ( $oIpServ->isValidIpRange( $
|
134 |
( new DeleteIp() )
|
135 |
-
->setDbHandler( $
|
136 |
-
->setIP( $
|
137 |
->fromWhiteList();
|
138 |
}
|
139 |
|
140 |
$oIP = ( new LookupIpOnList() )
|
141 |
-
->setDbHandler( $
|
142 |
->setIP( $this->getIP() )
|
143 |
->lookup( false );
|
144 |
if ( !$oIP instanceof Databases\IPs\EntryVO ) {
|
145 |
-
$oIP = $this->add( $
|
146 |
}
|
147 |
|
148 |
$aUpdateData = [];
|
149 |
-
if ( $oIP->list != $
|
150 |
-
$aUpdateData[ 'list' ] = $
|
151 |
}
|
152 |
-
if ( !empty( $
|
153 |
-
$aUpdateData[ 'label' ] = $
|
154 |
}
|
155 |
if ( $oIP->blocked_at > 0 ) {
|
156 |
$aUpdateData[ 'blocked_at' ] = 0;
|
@@ -160,7 +161,7 @@ class AddIp {
|
|
160 |
}
|
161 |
|
162 |
if ( !empty( $aUpdateData ) ) {
|
163 |
-
$
|
164 |
->getQueryUpdater()
|
165 |
->updateEntry( $oIP, $aUpdateData );
|
166 |
}
|
@@ -178,11 +179,11 @@ class AddIp {
|
|
178 |
private function add( $sList, $sLabel = '', $nLastAccessAt = null ) {
|
179 |
$oIP = null;
|
180 |
|
181 |
-
/** @var
|
182 |
-
$
|
183 |
|
184 |
// Never add a reserved IP to any black list
|
185 |
-
$oDbh = $
|
186 |
|
187 |
/** @var Databases\IPs\EntryVO $oTempIp */
|
188 |
$oTempIp = $oDbh->getVo();
|
4 |
|
5 |
use FernleafSystems\Wordpress\Plugin\Shield\Databases;
|
6 |
use FernleafSystems\Wordpress\Plugin\Shield\Modules;
|
7 |
+
use FernleafSystems\Wordpress\Plugin\Shield\Modules\IPs\ModCon;
|
8 |
use FernleafSystems\Wordpress\Services\Services;
|
9 |
|
10 |
/**
|
21 |
* @throws \Exception
|
22 |
*/
|
23 |
public function toAutoBlacklist() {
|
24 |
+
/** @var ModCon $mod */
|
25 |
$mod = $this->getMod();
|
26 |
$oReq = Services::Request();
|
27 |
|
65 |
* @throws \Exception
|
66 |
*/
|
67 |
public function toManualBlacklist( $sLabel = '' ) {
|
68 |
+
/** @var ModCon $mod */
|
69 |
+
$mod = $this->getMod();
|
70 |
$oIpServ = Services::IP();
|
71 |
|
72 |
$sIP = $this->getIP();
|
79 |
|
80 |
if ( $oIpServ->isValidIpRange( $sIP ) ) {
|
81 |
( new DeleteIp() )
|
82 |
+
->setDbHandler( $mod->getDbHandler_IPs() )
|
83 |
->setIP( $sIP )
|
84 |
->fromBlacklist();
|
85 |
}
|
86 |
|
87 |
$oIP = ( new LookupIpOnList() )
|
88 |
+
->setDbHandler( $mod->getDbHandler_IPs() )
|
89 |
->setListTypeBlack()
|
90 |
->setIP( $sIP )
|
91 |
->lookup( false );
|
92 |
|
93 |
if ( !$oIP instanceof Databases\IPs\EntryVO ) {
|
94 |
+
$oIP = $this->add( $mod::LIST_MANUAL_BLACK, $sLabel );
|
95 |
}
|
96 |
|
97 |
$aUpdateData = [
|
98 |
'last_access_at' => Services::Request()->ts()
|
99 |
];
|
100 |
|
101 |
+
if ( $oIP->list != $mod::LIST_MANUAL_BLACK ) {
|
102 |
+
$aUpdateData[ 'list' ] = $mod::LIST_MANUAL_BLACK;
|
103 |
}
|
104 |
if ( $oIP->label != $sLabel ) {
|
105 |
$aUpdateData[ 'label' ] = $sLabel;
|
108 |
$aUpdateData[ 'blocked_at' ] = Services::Request()->ts();
|
109 |
}
|
110 |
|
111 |
+
$mod->getDbHandler_IPs()
|
112 |
->getQueryUpdater()
|
113 |
->updateEntry( $oIP, $aUpdateData );
|
114 |
}
|
117 |
}
|
118 |
|
119 |
/**
|
120 |
+
* @param string $label
|
121 |
* @return Databases\IPs\EntryVO|null
|
122 |
* @throws \Exception
|
123 |
*/
|
124 |
+
public function toManualWhitelist( $label = '' ) {
|
125 |
+
/** @var ModCon $mod */
|
126 |
+
$mod = $this->getMod();
|
127 |
$oIpServ = Services::IP();
|
128 |
|
129 |
+
$ip = $this->getIP();
|
130 |
+
if ( !$oIpServ->isValidIp( $ip ) && !$oIpServ->isValidIpRange( $ip ) ) {
|
131 |
throw new \Exception( "IP address isn't valid." );
|
132 |
}
|
133 |
|
134 |
+
if ( $oIpServ->isValidIpRange( $ip ) ) {
|
135 |
( new DeleteIp() )
|
136 |
+
->setDbHandler( $mod->getDbHandler_IPs() )
|
137 |
+
->setIP( $ip )
|
138 |
->fromWhiteList();
|
139 |
}
|
140 |
|
141 |
$oIP = ( new LookupIpOnList() )
|
142 |
+
->setDbHandler( $mod->getDbHandler_IPs() )
|
143 |
->setIP( $this->getIP() )
|
144 |
->lookup( false );
|
145 |
if ( !$oIP instanceof Databases\IPs\EntryVO ) {
|
146 |
+
$oIP = $this->add( $mod::LIST_MANUAL_WHITE, $label );
|
147 |
}
|
148 |
|
149 |
$aUpdateData = [];
|
150 |
+
if ( $oIP->list != $mod::LIST_MANUAL_WHITE ) {
|
151 |
+
$aUpdateData[ 'list' ] = $mod::LIST_MANUAL_WHITE;
|
152 |
}
|
153 |
+
if ( !empty( $label ) && $oIP->label != $label ) {
|
154 |
+
$aUpdateData[ 'label' ] = $label;
|
155 |
}
|
156 |
if ( $oIP->blocked_at > 0 ) {
|
157 |
$aUpdateData[ 'blocked_at' ] = 0;
|
161 |
}
|
162 |
|
163 |
if ( !empty( $aUpdateData ) ) {
|
164 |
+
$mod->getDbHandler_IPs()
|
165 |
->getQueryUpdater()
|
166 |
->updateEntry( $oIP, $aUpdateData );
|
167 |
}
|
179 |
private function add( $sList, $sLabel = '', $nLastAccessAt = null ) {
|
180 |
$oIP = null;
|
181 |
|
182 |
+
/** @var ModCon $mod */
|
183 |
+
$mod = $this->getMod();
|
184 |
|
185 |
// Never add a reserved IP to any black list
|
186 |
+
$oDbh = $mod->getDbHandler_IPs();
|
187 |
|
188 |
/** @var Databases\IPs\EntryVO $oTempIp */
|
189 |
$oTempIp = $oDbh->getVo();
|
src/lib/src/Modules/IPs/Lib/Ops/DeleteIp.php
CHANGED
@@ -15,28 +15,19 @@ class DeleteIp {
|
|
15 |
use Databases\Base\HandlerConsumer;
|
16 |
use IPs\Components\IpAddressConsumer;
|
17 |
|
18 |
-
|
19 |
-
|
20 |
-
|
21 |
-
|
22 |
-
return $this->getDeleter()
|
23 |
-
->filterByBlacklist()
|
24 |
-
->query();
|
25 |
}
|
26 |
|
27 |
-
|
28 |
-
|
29 |
-
|
30 |
-
|
31 |
-
return $this->getDeleter()
|
32 |
-
->filterByWhitelist()
|
33 |
-
->query();
|
34 |
}
|
35 |
|
36 |
-
|
37 |
-
* @return Databases\IPs\Delete
|
38 |
-
*/
|
39 |
-
private function getDeleter() {
|
40 |
/** @var Databases\IPs\Delete $oDel */
|
41 |
$oDel = $this->getDbHandler()->getQueryDeleter();
|
42 |
return $oDel->filterByIp( $this->getIP() )
|
15 |
use Databases\Base\HandlerConsumer;
|
16 |
use IPs\Components\IpAddressConsumer;
|
17 |
|
18 |
+
public function fromBlacklist() :bool {
|
19 |
+
return (bool)$this->getDeleter()
|
20 |
+
->filterByBlacklist()
|
21 |
+
->query();
|
|
|
|
|
|
|
22 |
}
|
23 |
|
24 |
+
public function fromWhiteList() :bool {
|
25 |
+
return (bool)$this->getDeleter()
|
26 |
+
->filterByWhitelist()
|
27 |
+
->query();
|
|
|
|
|
|
|
28 |
}
|
29 |
|
30 |
+
private function getDeleter() :Databases\IPs\Delete {
|
|
|
|
|
|
|
31 |
/** @var Databases\IPs\Delete $oDel */
|
32 |
$oDel = $this->getDbHandler()->getQueryDeleter();
|
33 |
return $oDel->filterByIp( $this->getIP() )
|
src/lib/src/Modules/IPs/Lib/ProcessOffenses.php
CHANGED
@@ -11,7 +11,7 @@ class ProcessOffenses {
|
|
11 |
use ModConsumer;
|
12 |
|
13 |
public function run() {
|
14 |
-
/** @var \
|
15 |
$mod = $this->getMod();
|
16 |
|
17 |
$mod->loadOffenseTracker()->setIfCommit( true );
|
@@ -25,7 +25,7 @@ class ProcessOffenses {
|
|
25 |
}
|
26 |
|
27 |
private function processOffense() {
|
28 |
-
/** @var \
|
29 |
$mod = $this->getMod();
|
30 |
|
31 |
$oTracker = $mod->loadOffenseTracker();
|
11 |
use ModConsumer;
|
12 |
|
13 |
public function run() {
|
14 |
+
/** @var IPs\ModCon $mod */
|
15 |
$mod = $this->getMod();
|
16 |
|
17 |
$mod->loadOffenseTracker()->setIfCommit( true );
|
25 |
}
|
26 |
|
27 |
private function processOffense() {
|
28 |
+
/** @var IPs\ModCon $mod */
|
29 |
$mod = $this->getMod();
|
30 |
|
31 |
$oTracker = $mod->loadOffenseTracker();
|
src/lib/src/Modules/IPs/ModCon.php
ADDED
@@ -0,0 +1,121 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php declare( strict_types=1 );
|
2 |
+
|
3 |
+
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\IPs;
|
4 |
+
|
5 |
+
use FernleafSystems\Wordpress\Plugin\Shield;
|
6 |
+
use FernleafSystems\Wordpress\Plugin\Shield\Modules\BaseShield;
|
7 |
+
use FernleafSystems\Wordpress\Services\Services;
|
8 |
+
|
9 |
+
class ModCon extends BaseShield\ModCon {
|
10 |
+
|
11 |
+
const LIST_MANUAL_WHITE = 'MW';
|
12 |
+
const LIST_MANUAL_BLACK = 'MB';
|
13 |
+
const LIST_AUTO_BLACK = 'AB';
|
14 |
+
|
15 |
+
/**
|
16 |
+
* @var Lib\OffenseTracker
|
17 |
+
*/
|
18 |
+
private $oOffenseTracker;
|
19 |
+
|
20 |
+
/**
|
21 |
+
* @var Lib\BlacklistHandler
|
22 |
+
*/
|
23 |
+
private $oBlacklistHandler;
|
24 |
+
|
25 |
+
public function getBlacklistHandler() :Lib\BlacklistHandler {
|
26 |
+
if ( !isset( $this->oBlacklistHandler ) ) {
|
27 |
+
$this->oBlacklistHandler = ( new Lib\BlacklistHandler() )->setMod( $this );
|
28 |
+
}
|
29 |
+
return $this->oBlacklistHandler;
|
30 |
+
}
|
31 |
+
|
32 |
+
public function getDbHandler_IPs() :Shield\Databases\IPs\Handler {
|
33 |
+
return $this->getDbH( 'ips' );
|
34 |
+
}
|
35 |
+
|
36 |
+
/**
|
37 |
+
* @return bool
|
38 |
+
* @throws \Exception
|
39 |
+
*/
|
40 |
+
protected function isReadyToExecute() :bool {
|
41 |
+
$oIp = Services::IP();
|
42 |
+
return $oIp->isValidIp_PublicRange( $oIp->getRequestIp() )
|
43 |
+
&& ( $this->getDbHandler_IPs() instanceof Shield\Databases\IPs\Handler )
|
44 |
+
&& $this->getDbHandler_IPs()->isReady()
|
45 |
+
&& parent::isReadyToExecute();
|
46 |
+
}
|
47 |
+
|
48 |
+
protected function preProcessOptions() {
|
49 |
+
/** @var Options $opts */
|
50 |
+
$opts = $this->getOptions();
|
51 |
+
if ( !defined( strtoupper( $opts->getOpt( 'auto_expire' ).'_IN_SECONDS' ) ) ) {
|
52 |
+
$opts->resetOptToDefault( 'auto_expire' );
|
53 |
+
}
|
54 |
+
|
55 |
+
$nLimit = $opts->getOffenseLimit();
|
56 |
+
if ( !is_int( $nLimit ) || $nLimit < 0 ) {
|
57 |
+
$opts->resetOptToDefault( 'transgression_limit' );
|
58 |
+
}
|
59 |
+
|
60 |
+
$this->cleanPathWhitelist();
|
61 |
+
}
|
62 |
+
|
63 |
+
private function cleanPathWhitelist() {
|
64 |
+
/** @var Options $opts */
|
65 |
+
$opts = $this->getOptions();
|
66 |
+
$opts->setOpt( 'request_whitelist', array_unique( array_filter( array_map(
|
67 |
+
function ( $rule ) {
|
68 |
+
$rule = strtolower( trim( $rule ) );
|
69 |
+
if ( !empty( $rule ) ) {
|
70 |
+
$toCheck = array_unique( [
|
71 |
+
(string)parse_url( Services::WpGeneral()->getHomeUrl(), PHP_URL_PATH ),
|
72 |
+
(string)parse_url( Services::WpGeneral()->getWpUrl(), PHP_URL_PATH ),
|
73 |
+
] );
|
74 |
+
$regEx = sprintf( '#^%s$#i', str_replace( 'STAR', '.*', preg_quote( str_replace( '*', 'STAR', $rule ), '#' ) ) );
|
75 |
+
foreach ( $toCheck as $path ) {
|
76 |
+
$slashPath = rtrim( $path, '/' ).'/';
|
77 |
+
if ( preg_match( $regEx, $path ) || preg_match( $regEx, $slashPath ) ) {
|
78 |
+
$rule = false;
|
79 |
+
break;
|
80 |
+
}
|
81 |
+
}
|
82 |
+
}
|
83 |
+
return $rule;
|
84 |
+
},
|
85 |
+
$opts->getOpt( 'request_whitelist', [] ) // do not use Options getter as it formats into regex
|
86 |
+
) ) ) );
|
87 |
+
}
|
88 |
+
|
89 |
+
public function loadOffenseTracker() :Lib\OffenseTracker {
|
90 |
+
if ( !isset( $this->oOffenseTracker ) ) {
|
91 |
+
$this->oOffenseTracker = new Lib\OffenseTracker( $this->getCon() );
|
92 |
+
}
|
93 |
+
return $this->oOffenseTracker;
|
94 |
+
}
|
95 |
+
|
96 |
+
public function getTextOptDefault( string $key ) :string {
|
97 |
+
|
98 |
+
switch ( $key ) {
|
99 |
+
|
100 |
+
case 'text_loginfailed':
|
101 |
+
$text = sprintf( '%s: %s',
|
102 |
+
__( 'Warning', 'wp-simple-firewall' ),
|
103 |
+
__( 'Repeated login attempts that fail will result in a complete ban of your IP Address.', 'wp-simple-firewall' )
|
104 |
+
);
|
105 |
+
break;
|
106 |
+
|
107 |
+
case 'text_remainingtrans':
|
108 |
+
$text = sprintf( '%s: %s',
|
109 |
+
__( 'Warning', 'wp-simple-firewall' ),
|
110 |
+
__( 'You have %s remaining offenses(s) against this site and then your IP address will be completely blocked.', 'wp-simple-firewall' )
|
111 |
+
.'<br/><strong>'.__( 'Seriously, stop repeating what you are doing or you will be locked out.', 'wp-simple-firewall' ).'</strong>'
|
112 |
+
);
|
113 |
+
break;
|
114 |
+
|
115 |
+
default:
|
116 |
+
$text = parent::getTextOptDefault( $key );
|
117 |
+
break;
|
118 |
+
}
|
119 |
+
return $text;
|
120 |
+
}
|
121 |
+
}
|
src/lib/src/Modules/IPs/Options.php
CHANGED
@@ -2,10 +2,10 @@
|
|
2 |
|
3 |
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\IPs;
|
4 |
|
5 |
-
use FernleafSystems\Wordpress\Plugin\Shield\Modules\
|
6 |
use FernleafSystems\Wordpress\Services\Services;
|
7 |
|
8 |
-
class Options extends
|
9 |
|
10 |
/**
|
11 |
* @return int
|
@@ -163,12 +163,4 @@ class Options extends Base\ShieldOptions {
|
|
163 |
protected function isSelectOptionEnabled( $key ) {
|
164 |
return !$this->isOpt( $key, 'disabled' );
|
165 |
}
|
166 |
-
|
167 |
-
/**
|
168 |
-
* @return string
|
169 |
-
* @deprecated 10.0
|
170 |
-
*/
|
171 |
-
public function getDbTable_IPs() {
|
172 |
-
return $this->getCon()->prefixOption( $this->getDef( 'ip_lists_table_name' ) );
|
173 |
-
}
|
174 |
}
|
2 |
|
3 |
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\IPs;
|
4 |
|
5 |
+
use FernleafSystems\Wordpress\Plugin\Shield\Modules\BaseShield;
|
6 |
use FernleafSystems\Wordpress\Services\Services;
|
7 |
|
8 |
+
class Options extends BaseShield\Options {
|
9 |
|
10 |
/**
|
11 |
* @return int
|
163 |
protected function isSelectOptionEnabled( $key ) {
|
164 |
return !$this->isOpt( $key, 'disabled' );
|
165 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
166 |
}
|
src/lib/src/Modules/IPs/Processor.php
ADDED
@@ -0,0 +1,14 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php declare( strict_types=1 );
|
2 |
+
|
3 |
+
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\IPs;
|
4 |
+
|
5 |
+
use FernleafSystems\Wordpress\Plugin\Shield\Modules\BaseShield;
|
6 |
+
|
7 |
+
class Processor extends BaseShield\Processor {
|
8 |
+
|
9 |
+
protected function run() {
|
10 |
+
/** @var ModCon $mod */
|
11 |
+
$mod = $this->getMod();
|
12 |
+
$mod->getBlacklistHandler()->execute();
|
13 |
+
}
|
14 |
+
}
|
src/lib/src/Modules/IPs/Strings.php
CHANGED
@@ -13,10 +13,10 @@ class Strings extends Base\Strings {
|
|
13 |
* @throws \Exception
|
14 |
*/
|
15 |
public function getSectionStrings( string $section ) :array {
|
16 |
-
/** @var
|
17 |
-
$
|
18 |
$sPlugName = $this->getCon()->getHumanName();
|
19 |
-
$sModName = $
|
20 |
|
21 |
switch ( $section ) {
|
22 |
|
@@ -201,7 +201,7 @@ class Strings extends Base\Strings {
|
|
201 |
|
202 |
case 'track_loginfailed' :
|
203 |
$sName = __( 'Failed Login', 'wp-simple-firewall' );
|
204 |
-
$sSummary = __( 'Detect Failed Login Attempts
|
205 |
$sDescription = __( "Penalise a visitor when they try to login using a valid username, but it fails.", 'wp-simple-firewall' );
|
206 |
break;
|
207 |
|
13 |
* @throws \Exception
|
14 |
*/
|
15 |
public function getSectionStrings( string $section ) :array {
|
16 |
+
/** @var ModCon $mod */
|
17 |
+
$mod = $this->getMod();
|
18 |
$sPlugName = $this->getCon()->getHumanName();
|
19 |
+
$sModName = $mod->getMainFeatureName();
|
20 |
|
21 |
switch ( $section ) {
|
22 |
|
201 |
|
202 |
case 'track_loginfailed' :
|
203 |
$sName = __( 'Failed Login', 'wp-simple-firewall' );
|
204 |
+
$sSummary = __( 'Detect Failed Login Attempts For Users That Exist', 'wp-simple-firewall' );
|
205 |
$sDescription = __( "Penalise a visitor when they try to login using a valid username, but it fails.", 'wp-simple-firewall' );
|
206 |
break;
|
207 |
|
src/lib/src/Modules/IPs/UI.php
CHANGED
@@ -2,16 +2,16 @@
|
|
2 |
|
3 |
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\IPs;
|
4 |
|
5 |
-
use FernleafSystems\Wordpress\Plugin\Shield\Modules\
|
6 |
use FernleafSystems\Wordpress\Plugin\Shield\Modules\IPs\Lib\IpAnalyse\FindAllPluginIps;
|
7 |
use FernleafSystems\Wordpress\Plugin\Shield\Modules\IPs\Lib\Ops\RetrieveIpsForLists;
|
8 |
use FernleafSystems\Wordpress\Services\Services;
|
9 |
|
10 |
-
class UI extends
|
11 |
|
12 |
public function buildInsightsVars() :array {
|
13 |
$con = $this->getCon();
|
14 |
-
/** @var
|
15 |
$mod = $this->getMod();
|
16 |
/** @var Options $opts */
|
17 |
$opts = $this->getOptions();
|
@@ -61,6 +61,12 @@ class UI extends Base\ShieldUI {
|
|
61 |
'tab_ip_analysis' => __( 'IP Analysis', 'wp-simple-firewall' ),
|
62 |
],
|
63 |
'vars' => [
|
|
|
|
|
|
|
|
|
|
|
|
|
64 |
'unique_ips_black' => ( new RetrieveIpsForLists() )
|
65 |
->setDbHandler( $mod->getDbHandler_IPs() )
|
66 |
->black(),
|
@@ -107,7 +113,8 @@ class UI extends Base\ShieldUI {
|
|
107 |
'/wpadmin_pages/insights/ips/ip_analyse/index.twig',
|
108 |
[
|
109 |
'ajax' => [
|
110 |
-
'build_ip_analyse'
|
|
|
111 |
],
|
112 |
'strings' => [
|
113 |
'select_ip' => __( 'Select IP To Analyse', 'wp-simple-firewall' ),
|
@@ -124,4 +131,14 @@ class UI extends Base\ShieldUI {
|
|
124 |
true
|
125 |
);
|
126 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
127 |
}
|
2 |
|
3 |
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\IPs;
|
4 |
|
5 |
+
use FernleafSystems\Wordpress\Plugin\Shield\Modules\BaseShield;
|
6 |
use FernleafSystems\Wordpress\Plugin\Shield\Modules\IPs\Lib\IpAnalyse\FindAllPluginIps;
|
7 |
use FernleafSystems\Wordpress\Plugin\Shield\Modules\IPs\Lib\Ops\RetrieveIpsForLists;
|
8 |
use FernleafSystems\Wordpress\Services\Services;
|
9 |
|
10 |
+
class UI extends BaseShield\UI {
|
11 |
|
12 |
public function buildInsightsVars() :array {
|
13 |
$con = $this->getCon();
|
14 |
+
/** @var ModCon $mod */
|
15 |
$mod = $this->getMod();
|
16 |
/** @var Options $opts */
|
17 |
$opts = $this->getOptions();
|
61 |
'tab_ip_analysis' => __( 'IP Analysis', 'wp-simple-firewall' ),
|
62 |
],
|
63 |
'vars' => [
|
64 |
+
'related_hrefs' => [
|
65 |
+
[
|
66 |
+
'href' => $mod->getUrl_AdminPage(),
|
67 |
+
'title' => __( 'IP Block Settings', 'wp-simple-firewall' ),
|
68 |
+
],
|
69 |
+
],
|
70 |
'unique_ips_black' => ( new RetrieveIpsForLists() )
|
71 |
->setDbHandler( $mod->getDbHandler_IPs() )
|
72 |
->black(),
|
113 |
'/wpadmin_pages/insights/ips/ip_analyse/index.twig',
|
114 |
[
|
115 |
'ajax' => [
|
116 |
+
'build_ip_analyse' => $mod->getAjaxActionData( 'build_ip_analyse', true ),
|
117 |
+
'ip_analyse_action' => $mod->getAjaxActionData( 'ip_analyse_action', true ),
|
118 |
],
|
119 |
'strings' => [
|
120 |
'select_ip' => __( 'Select IP To Analyse', 'wp-simple-firewall' ),
|
131 |
true
|
132 |
);
|
133 |
}
|
134 |
+
|
135 |
+
protected function getSettingsRelatedLinks() :array {
|
136 |
+
$modInsights = $this->getCon()->getModule_Insights();
|
137 |
+
return [
|
138 |
+
[
|
139 |
+
'href' => $modInsights->getUrl_SubInsightsPage( 'ips' ),
|
140 |
+
'title' => __( 'Analyse & Manage IPs', 'wp-simple-firewall' ),
|
141 |
+
]
|
142 |
+
];
|
143 |
+
}
|
144 |
}
|
src/lib/src/Modules/IPs/Upgrade.php
CHANGED
@@ -2,13 +2,22 @@
|
|
2 |
|
3 |
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\IPs;
|
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_905() {
|
11 |
-
/** @var
|
12 |
$mod = $this->getMod();
|
13 |
$schema = $mod->getDbHandler_IPs()->getTableSchema();
|
14 |
Services::WpDb()->doSql(
|
@@ -21,7 +30,7 @@ class Upgrade extends Base\Upgrade {
|
|
21 |
* Support larger transgression counts [smallint(1) => int(10)]
|
22 |
*/
|
23 |
protected function upgrade_911() {
|
24 |
-
/** @var
|
25 |
$mod = $this->getMod();
|
26 |
$schema = $mod->getDbHandler_IPs()->getTableSchema();
|
27 |
Services::WpDb()->doSql(
|
@@ -37,12 +46,12 @@ class Upgrade extends Base\Upgrade {
|
|
37 |
* Support Magic Links for logged-in users.
|
38 |
*/
|
39 |
protected function upgrade_920() {
|
40 |
-
/** @var Options $
|
41 |
-
$
|
42 |
-
$current = $
|
43 |
if ( !is_array( $current ) ) {
|
44 |
$current = ( $current === 'gasp' ) ? [ 'gasp' ] : [];
|
45 |
-
$
|
46 |
}
|
47 |
}
|
48 |
}
|
2 |
|
3 |
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\IPs;
|
4 |
|
5 |
+
use FernleafSystems\Wordpress\Plugin\Shield\Databases\IPs\Delete;
|
6 |
use FernleafSystems\Wordpress\Plugin\Shield\Modules\Base;
|
7 |
use FernleafSystems\Wordpress\Services\Services;
|
8 |
|
9 |
class Upgrade extends Base\Upgrade {
|
10 |
|
11 |
+
protected function upgrade_1010() {
|
12 |
+
/** @var ModCon $mod */
|
13 |
+
$mod = $this->getMod();
|
14 |
+
/** @var Delete $del */
|
15 |
+
$del = $mod->getDbHandler_IPs()->getQueryDeleter();
|
16 |
+
$del->filterByLabel( 'iControlWP' )->query();
|
17 |
+
}
|
18 |
+
|
19 |
protected function upgrade_905() {
|
20 |
+
/** @var ModCon $mod */
|
21 |
$mod = $this->getMod();
|
22 |
$schema = $mod->getDbHandler_IPs()->getTableSchema();
|
23 |
Services::WpDb()->doSql(
|
30 |
* Support larger transgression counts [smallint(1) => int(10)]
|
31 |
*/
|
32 |
protected function upgrade_911() {
|
33 |
+
/** @var ModCon $mod */
|
34 |
$mod = $this->getMod();
|
35 |
$schema = $mod->getDbHandler_IPs()->getTableSchema();
|
36 |
Services::WpDb()->doSql(
|
46 |
* Support Magic Links for logged-in users.
|
47 |
*/
|
48 |
protected function upgrade_920() {
|
49 |
+
/** @var Options $opts */
|
50 |
+
$opts = $this->getOptions();
|
51 |
+
$current = $opts->getOpt( 'user_auto_recover' );
|
52 |
if ( !is_array( $current ) ) {
|
53 |
$current = ( $current === 'gasp' ) ? [ 'gasp' ] : [];
|
54 |
+
$opts->setOpt( 'user_auto_recover', $current );
|
55 |
}
|
56 |
}
|
57 |
}
|
src/lib/src/Modules/IPs/WpCli.php
CHANGED
@@ -10,7 +10,7 @@ class WpCli extends Base\WpCli {
|
|
10 |
/**
|
11 |
* @inheritDoc
|
12 |
*/
|
13 |
-
protected function getCmdHandlers() {
|
14 |
return [
|
15 |
new IPs\WpCli\Add(),
|
16 |
new IPs\WpCli\Remove(),
|
10 |
/**
|
11 |
* @inheritDoc
|
12 |
*/
|
13 |
+
protected function getCmdHandlers() :array {
|
14 |
return [
|
15 |
new IPs\WpCli\Add(),
|
16 |
new IPs\WpCli\Remove(),
|
src/lib/src/Modules/IPs/WpCli/Add.php
CHANGED
@@ -34,17 +34,17 @@ class Add extends BaseAddRemove {
|
|
34 |
*/
|
35 |
public function cmdIpAdd( array $null, array $aA ) {
|
36 |
|
37 |
-
$
|
38 |
|
39 |
$oAdder = ( new Ops\AddIp() )
|
40 |
->setMod( $this->getMod() )
|
41 |
->setIP( $aA[ 'ip' ] );
|
42 |
try {
|
43 |
if ( $aA[ 'list' ] === 'white' ) {
|
44 |
-
$oAdder->toManualWhitelist( $
|
45 |
}
|
46 |
else {
|
47 |
-
$oAdder->toManualBlacklist( $
|
48 |
}
|
49 |
}
|
50 |
catch ( \Exception $oE ) {
|
34 |
*/
|
35 |
public function cmdIpAdd( array $null, array $aA ) {
|
36 |
|
37 |
+
$label = $aA[ 'label' ] ?? 'none';
|
38 |
|
39 |
$oAdder = ( new Ops\AddIp() )
|
40 |
->setMod( $this->getMod() )
|
41 |
->setIP( $aA[ 'ip' ] );
|
42 |
try {
|
43 |
if ( $aA[ 'list' ] === 'white' ) {
|
44 |
+
$oAdder->toManualWhitelist( $label );
|
45 |
}
|
46 |
else {
|
47 |
+
$oAdder->toManualBlacklist( $label );
|
48 |
}
|
49 |
}
|
50 |
catch ( \Exception $oE ) {
|
src/lib/src/Modules/IPs/WpCli/Enumerate.php
CHANGED
@@ -4,6 +4,7 @@ namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\IPs\WpCli;
|
|
4 |
|
5 |
use FernleafSystems\Wordpress\Plugin\Shield\Modules\Base\WpCli\BaseWpCliCmd;
|
6 |
use FernleafSystems\Wordpress\Plugin\Shield\Modules\IPs\Lib\Ops;
|
|
|
7 |
use WP_CLI;
|
8 |
|
9 |
class Enumerate extends BaseWpCliCmd {
|
@@ -32,11 +33,11 @@ class Enumerate extends BaseWpCliCmd {
|
|
32 |
}
|
33 |
|
34 |
public function cmdPrint( $null, $aA ) {
|
35 |
-
/** @var
|
36 |
-
$
|
37 |
|
38 |
$oRtr = ( new Ops\RetrieveIpsForLists() )
|
39 |
-
->setDbHandler( $
|
40 |
$aIPs = $aA[ 'list' ] === 'white' ? $oRtr->white() : $oRtr->black();
|
41 |
$aIPs = array_map(
|
42 |
function ( $sIP ) {
|
4 |
|
5 |
use FernleafSystems\Wordpress\Plugin\Shield\Modules\Base\WpCli\BaseWpCliCmd;
|
6 |
use FernleafSystems\Wordpress\Plugin\Shield\Modules\IPs\Lib\Ops;
|
7 |
+
use FernleafSystems\Wordpress\Plugin\Shield\Modules\IPs\ModCon;
|
8 |
use WP_CLI;
|
9 |
|
10 |
class Enumerate extends BaseWpCliCmd {
|
33 |
}
|
34 |
|
35 |
public function cmdPrint( $null, $aA ) {
|
36 |
+
/** @var ModCon $mod */
|
37 |
+
$mod = $this->getMod();
|
38 |
|
39 |
$oRtr = ( new Ops\RetrieveIpsForLists() )
|
40 |
+
->setDbHandler( $mod->getDbHandler_IPs() );
|
41 |
$aIPs = $aA[ 'list' ] === 'white' ? $oRtr->white() : $oRtr->black();
|
42 |
$aIPs = array_map(
|
43 |
function ( $sIP ) {
|
src/lib/src/Modules/IPs/WpCli/Remove.php
CHANGED
@@ -2,7 +2,7 @@
|
|
2 |
|
3 |
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\IPs\WpCli;
|
4 |
|
5 |
-
use FernleafSystems\Wordpress\Plugin\Shield\Modules\IPs
|
6 |
use WP_CLI;
|
7 |
|
8 |
class Remove extends BaseAddRemove {
|
@@ -25,11 +25,11 @@ class Remove extends BaseAddRemove {
|
|
25 |
* @throws WP_CLI\ExitException
|
26 |
*/
|
27 |
public function cmdIpRemove( array $null, array $aA ) {
|
28 |
-
/** @var \
|
29 |
-
$
|
30 |
|
31 |
-
$oDel = ( new Ops\DeleteIp() )
|
32 |
-
->setDbHandler( $
|
33 |
->setIP( $aA[ 'ip' ] );
|
34 |
if ( $aA[ 'list' ] === 'white' ) {
|
35 |
$bSuccess = $oDel->fromWhiteList();
|
2 |
|
3 |
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\IPs\WpCli;
|
4 |
|
5 |
+
use FernleafSystems\Wordpress\Plugin\Shield\Modules\IPs;
|
6 |
use WP_CLI;
|
7 |
|
8 |
class Remove extends BaseAddRemove {
|
25 |
* @throws WP_CLI\ExitException
|
26 |
*/
|
27 |
public function cmdIpRemove( array $null, array $aA ) {
|
28 |
+
/** @var IPs\ModCon $mod */
|
29 |
+
$mod = $this->getMod();
|
30 |
|
31 |
+
$oDel = ( new IPs\Lib\Ops\DeleteIp() )
|
32 |
+
->setDbHandler( $mod->getDbHandler_IPs() )
|
33 |
->setIP( $aA[ 'ip' ] );
|
34 |
if ( $aA[ 'list' ] === 'white' ) {
|
35 |
$bSuccess = $oDel->fromWhiteList();
|
src/lib/src/Modules/Insights/ModCon.php
ADDED
@@ -0,0 +1,230 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php declare( strict_types=1 );
|
2 |
+
|
3 |
+
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Insights;
|
4 |
+
|
5 |
+
use FernleafSystems\Wordpress\Plugin\Shield\Modules;
|
6 |
+
use FernleafSystems\Wordpress\Plugin\Shield\Modules\BaseShield;
|
7 |
+
use FernleafSystems\Wordpress\Services\Services;
|
8 |
+
|
9 |
+
class ModCon extends BaseShield\ModCon {
|
10 |
+
|
11 |
+
protected function setupCustomHooks() {
|
12 |
+
add_action( 'admin_footer', function () {
|
13 |
+
/** @var UI $UI */
|
14 |
+
$UI = $this->getUIHandler();
|
15 |
+
$UI->printAdminFooterItems();
|
16 |
+
}, 100, 0 );
|
17 |
+
}
|
18 |
+
|
19 |
+
protected function onModulesLoaded() {
|
20 |
+
$this->maybeRedirectToAdmin();
|
21 |
+
}
|
22 |
+
|
23 |
+
private function maybeRedirectToAdmin() {
|
24 |
+
$con = $this->getCon();
|
25 |
+
$nActiveFor = $con->getModule_Plugin()->getActivateLength();
|
26 |
+
if ( !Services::WpGeneral()->isAjax() && is_admin() && !$con->isModulePage() && $nActiveFor < 4 ) {
|
27 |
+
Services::Response()->redirect( $this->getUrl_AdminPage() );
|
28 |
+
}
|
29 |
+
}
|
30 |
+
|
31 |
+
public function getUrl_IpAnalysis( string $ip ) :string {
|
32 |
+
return add_query_arg( [ 'analyse_ip' => $ip ], $this->getUrl_SubInsightsPage( 'ips' ) );
|
33 |
+
}
|
34 |
+
|
35 |
+
public function getUrl_SubInsightsPage( string $subPage ) :string {
|
36 |
+
return add_query_arg(
|
37 |
+
[ 'inav' => sanitize_key( $subPage ) ],
|
38 |
+
$this->getUrl_AdminPage()
|
39 |
+
);
|
40 |
+
}
|
41 |
+
|
42 |
+
protected function renderModulePage( array $aData = [] ) :string {
|
43 |
+
/** @var UI $UI */
|
44 |
+
$UI = $this->getUIHandler();
|
45 |
+
return $UI->renderPages();
|
46 |
+
}
|
47 |
+
|
48 |
+
public function insertCustomJsVars_Admin() {
|
49 |
+
parent::insertCustomJsVars_Admin();
|
50 |
+
|
51 |
+
if ( $this->isThisModulePage() ) {
|
52 |
+
|
53 |
+
$con = $this->getCon();
|
54 |
+
$aStdDepsJs = [ $con->prefix( 'plugin' ) ];
|
55 |
+
$iNav = Services::Request()->query( 'inav', 'overview' );
|
56 |
+
|
57 |
+
$oModPlugin = $con->getModule_Plugin();
|
58 |
+
$oTourManager = $oModPlugin->getTourManager();
|
59 |
+
switch ( $iNav ) {
|
60 |
+
|
61 |
+
case 'importexport':
|
62 |
+
|
63 |
+
$sAsset = 'shield/import';
|
64 |
+
$sUnique = $con->prefix( $sAsset );
|
65 |
+
wp_register_script(
|
66 |
+
$sUnique,
|
67 |
+
$con->getPluginUrl_Js( $sAsset ),
|
68 |
+
$aStdDepsJs,
|
69 |
+
$con->getVersion(),
|
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 |
+
private function includeScriptIpDetect() {
|
210 |
+
$con = $this->getCon();
|
211 |
+
/** @var Modules\Plugin\Options $opts */
|
212 |
+
$opts = $con->getModule_Plugin()->getOptions();
|
213 |
+
if ( $opts->isIpSourceAutoDetect() ) {
|
214 |
+
wp_register_script(
|
215 |
+
$con->prefix( 'ip_detect' ),
|
216 |
+
$con->getPluginUrl_Js( 'ip_detect.js' ),
|
217 |
+
[],
|
218 |
+
$con->getVersion(),
|
219 |
+
true
|
220 |
+
);
|
221 |
+
wp_enqueue_script( $con->prefix( 'ip_detect' ) );
|
222 |
+
|
223 |
+
wp_localize_script(
|
224 |
+
$con->prefix( 'ip_detect' ),
|
225 |
+
'icwp_wpsf_vars_ipdetect',
|
226 |
+
[ 'ajax' => $con->getModule_Plugin()->getAjaxActionData( 'ipdetect' ) ]
|
227 |
+
);
|
228 |
+
}
|
229 |
+
}
|
230 |
+
}
|
src/lib/src/Modules/Insights/Options.php
CHANGED
@@ -1,9 +1,9 @@
|
|
1 |
-
<?php
|
2 |
|
3 |
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Insights;
|
4 |
|
5 |
-
use FernleafSystems\Wordpress\Plugin\Shield\Modules\
|
6 |
|
7 |
-
class Options extends
|
8 |
|
9 |
}
|
1 |
+
<?php declare( strict_types=1 );
|
2 |
|
3 |
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Insights;
|
4 |
|
5 |
+
use FernleafSystems\Wordpress\Plugin\Shield\Modules\BaseShield;
|
6 |
|
7 |
+
class Options extends BaseShield\Options {
|
8 |
|
9 |
}
|
src/lib/src/Modules/Insights/UI.php
CHANGED
@@ -2,63 +2,268 @@
|
|
2 |
|
3 |
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Insights;
|
4 |
|
5 |
-
use FernleafSystems\Wordpress\Plugin\Shield
|
|
|
6 |
use FernleafSystems\Wordpress\Plugin\Shield\Modules\Insights\Lib\OverviewCards;
|
|
|
7 |
use FernleafSystems\Wordpress\Plugin\Shield\Utilities\Changelog\Retrieve;
|
8 |
use FernleafSystems\Wordpress\Services\Services;
|
9 |
|
10 |
-
class UI extends
|
11 |
|
12 |
-
public function
|
13 |
$con = $this->getCon();
|
14 |
-
|
15 |
-
/** @var \FernleafSystems\Wordpress\Plugin\Shield\Modules\Reporting\UI $uiReporting */
|
16 |
-
$uiReporting = $con->getModule_Reporting()->getUIHandler();
|
17 |
-
|
18 |
return [
|
19 |
'content' => [
|
20 |
'tab_updates' => $this->renderTabUpdates(),
|
21 |
'tab_freetrial' => $this->renderFreeTrial(),
|
22 |
-
'summary_stats' => $uiReporting->renderSummaryStats()
|
23 |
],
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
24 |
'vars' => [
|
25 |
'overview_cards' => ( new OverviewCards() )
|
26 |
->setMod( $this->getMod() )
|
27 |
->buildForShuffle(),
|
28 |
],
|
29 |
-
'hrefs' => [
|
30 |
-
'shield_pro_url' => 'https://shsec.io/shieldpro',
|
31 |
-
'shield_pro_more_info_url' => 'https://shsec.io/shld1',
|
32 |
-
],
|
33 |
-
'flags' => [
|
34 |
-
'show_ads' => false,
|
35 |
-
'show_standard_options' => false,
|
36 |
-
'show_alt_content' => true,
|
37 |
-
'is_pro' => $con->isPremiumActive(),
|
38 |
-
],
|
39 |
'strings' => [
|
40 |
-
'
|
41 |
-
'
|
42 |
-
'
|
43 |
-
'tab_summary_stats' => __( 'Summary Stats', 'wp-simple-firewall' ),
|
44 |
-
'click_filter_status' => __( 'Click To Filter By Security Status', 'wp-simple-firewall' ),
|
45 |
-
'click_filter_area' => __( 'Click To Filter By Security Area', 'wp-simple-firewall' ),
|
46 |
-
'discover' => __( 'Discover where your site security is doing well or areas that can be improved', 'wp-simple-firewall' ),
|
47 |
-
'clear_filter' => __( 'Clear Filter', 'wp-simple-firewall' ),
|
48 |
-
'click_to_toggle' => __( 'click to toggle', 'wp-simple-firewall' ),
|
49 |
-
'go_to_options' => sprintf(
|
50 |
__( 'Go To %s', 'wp-simple-firewall' ),
|
51 |
__( 'Options' )
|
52 |
),
|
53 |
-
'key' => __( 'Key' ),
|
54 |
-
'key_positive' => __( 'Positive Security', 'wp-simple-firewall' ),
|
55 |
-
'key_warning' => __( 'Potential Warning', 'wp-simple-firewall' ),
|
56 |
-
'key_danger' => __( 'Potential Danger', 'wp-simple-firewall' ),
|
57 |
-
'key_information' => __( 'Information', 'wp-simple-firewall' ),
|
58 |
],
|
59 |
];
|
60 |
}
|
61 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
62 |
private function renderFreeTrial() :string {
|
63 |
$user = Services::WpUsers()->getCurrentWpUser();
|
64 |
return $this->getMod()
|
@@ -66,7 +271,6 @@ class UI extends Base\ShieldUI {
|
|
66 |
'/forms/drip_trial_signup.twig',
|
67 |
[
|
68 |
'vars' => [
|
69 |
-
// the keys here must match the changelog item types
|
70 |
'activation_url' => Services::WpGeneral()->getHomeUrl(),
|
71 |
'email' => $user->user_email,
|
72 |
'name' => $user->user_firstname,
|
@@ -116,4 +320,66 @@ class UI extends Base\ShieldUI {
|
|
116 |
true
|
117 |
);
|
118 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
119 |
}
|
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\Plugin\Shield\Modules\Insights\Lib\OverviewCards;
|
8 |
+
use FernleafSystems\Wordpress\Plugin\Shield\Modules\Plugin\Insights\AdminNotes;
|
9 |
use FernleafSystems\Wordpress\Plugin\Shield\Utilities\Changelog\Retrieve;
|
10 |
use FernleafSystems\Wordpress\Services\Services;
|
11 |
|
12 |
+
class UI extends BaseShield\UI {
|
13 |
|
14 |
+
public function buildInsightsVars_Docs() :array {
|
15 |
$con = $this->getCon();
|
|
|
|
|
|
|
|
|
16 |
return [
|
17 |
'content' => [
|
18 |
'tab_updates' => $this->renderTabUpdates(),
|
19 |
'tab_freetrial' => $this->renderFreeTrial(),
|
|
|
20 |
],
|
21 |
+
'flags' => [
|
22 |
+
'is_pro' => $con->isPremiumActive(),
|
23 |
+
],
|
24 |
+
'strings' => [
|
25 |
+
'tab_freetrial' => __( 'Free Trial', 'wp-simple-firewall' ),
|
26 |
+
'tab_updates' => __( 'Updates and Changes', 'wp-simple-firewall' ),
|
27 |
+
],
|
28 |
+
];
|
29 |
+
}
|
30 |
+
|
31 |
+
private function buildInsightsVars_Overview() :array {
|
32 |
+
return [
|
33 |
'vars' => [
|
34 |
'overview_cards' => ( new OverviewCards() )
|
35 |
->setMod( $this->getMod() )
|
36 |
->buildForShuffle(),
|
37 |
],
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
38 |
'strings' => [
|
39 |
+
'click_clear_filter' => __( 'Click To Filter By Security Area or Status', 'wp-simple-firewall' ),
|
40 |
+
'clear_filter' => __( 'Clear Filter', 'wp-simple-firewall' ),
|
41 |
+
'go_to_options' => sprintf(
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
42 |
__( 'Go To %s', 'wp-simple-firewall' ),
|
43 |
__( 'Options' )
|
44 |
),
|
|
|
|
|
|
|
|
|
|
|
45 |
],
|
46 |
];
|
47 |
}
|
48 |
|
49 |
+
public function renderPages() :string {
|
50 |
+
$con = $this->getCon();
|
51 |
+
/** @var ModCon $mod */
|
52 |
+
$mod = $this->getMod();
|
53 |
+
$req = Services::Request();
|
54 |
+
|
55 |
+
$sNavSection = $req->query( 'inav', 'dashboard' );
|
56 |
+
$subNavSection = $req->query( 'subnav' );
|
57 |
+
|
58 |
+
$modPlugin = $con->getModule_Plugin();
|
59 |
+
$oTourManager = $modPlugin->getTourManager();
|
60 |
+
if ( !$oTourManager->isCompleted( 'insights_overview' ) && $modPlugin->getActivateLength() > 600 ) {
|
61 |
+
$oTourManager->setCompleted( 'insights_overview' );
|
62 |
+
}
|
63 |
+
|
64 |
+
switch ( $sNavSection ) {
|
65 |
+
|
66 |
+
case 'audit':
|
67 |
+
/** @var Shield\Modules\AuditTrail\UI $auditUI */
|
68 |
+
$auditUI = $con->getModule_AuditTrail()->getUIHandler();
|
69 |
+
$data = [
|
70 |
+
'content' => [
|
71 |
+
'table_audit' => $auditUI->renderAuditTrailTable(),
|
72 |
+
],
|
73 |
+
'vars' => [
|
74 |
+
'related_hrefs' => [
|
75 |
+
[
|
76 |
+
'href' => $con->getModule_AuditTrail()->getUrl_AdminPage(),
|
77 |
+
'title' => __( 'Audit Trail Settings', 'wp-simple-firewall' ),
|
78 |
+
],
|
79 |
+
[
|
80 |
+
'href' => 'https://shsec.io/audittrailglossary',
|
81 |
+
'title' => __( 'Audit Trail Glossary', 'wp-simple-firewall' ),
|
82 |
+
'new' => true,
|
83 |
+
],
|
84 |
+
[
|
85 |
+
'href' => $mod->getUrl_SubInsightsPage( 'traffic' ),
|
86 |
+
'title' => __( 'Traffic Log', 'wp-simple-firewall' ),
|
87 |
+
],
|
88 |
+
]
|
89 |
+
]
|
90 |
+
];
|
91 |
+
break;
|
92 |
+
|
93 |
+
case 'traffic':
|
94 |
+
/** @var Shield\Modules\Traffic\UI $trafficUI */
|
95 |
+
$trafficUI = $con->getModule_Traffic()->getUIHandler();
|
96 |
+
$data = [
|
97 |
+
'content' => [
|
98 |
+
'table_traffic' => $trafficUI->renderTrafficTable(),
|
99 |
+
],
|
100 |
+
'vars' => [
|
101 |
+
'related_hrefs' => [
|
102 |
+
[
|
103 |
+
'href' => $con->getModule_Traffic()->getUrl_AdminPage(),
|
104 |
+
'title' => __( 'Traffic Settings', 'wp-simple-firewall' ),
|
105 |
+
],
|
106 |
+
[
|
107 |
+
'href' => $mod->getUrl_SubInsightsPage( 'audit' ),
|
108 |
+
'title' => __( 'Audit Trail', 'wp-simple-firewall' ),
|
109 |
+
],
|
110 |
+
]
|
111 |
+
]
|
112 |
+
];
|
113 |
+
break;
|
114 |
+
|
115 |
+
case 'dashboard':
|
116 |
+
/** @var Shield\Modules\Plugin\UI $UI */
|
117 |
+
$UI = $con->getModule_Plugin()->getUIHandler();
|
118 |
+
$data = $UI->buildInsightsVars_Dashboard();
|
119 |
+
break;
|
120 |
+
|
121 |
+
case 'debug':
|
122 |
+
/** @var Shield\Modules\Plugin\UI $UI */
|
123 |
+
$UI = $con->getModule_Plugin()->getUIHandler();
|
124 |
+
$data = $UI->buildInsightsVars_Debug();
|
125 |
+
break;
|
126 |
+
|
127 |
+
case 'docs':
|
128 |
+
$data = $this->buildInsightsVars_Docs();
|
129 |
+
break;
|
130 |
+
|
131 |
+
case 'free_trial':
|
132 |
+
$data = [
|
133 |
+
'content' => [
|
134 |
+
'free_trial' => $this->renderFreeTrial()
|
135 |
+
]
|
136 |
+
];
|
137 |
+
break;
|
138 |
+
|
139 |
+
case 'importexport':
|
140 |
+
$data = $modPlugin->getImpExpController()->buildInsightsVars();
|
141 |
+
break;
|
142 |
+
|
143 |
+
case 'ips':
|
144 |
+
/** @var Shield\Modules\IPs\UI $UI */
|
145 |
+
$UI = $con->getModule_IPs()->getUIHandler();
|
146 |
+
$data = $UI->buildInsightsVars();
|
147 |
+
break;
|
148 |
+
|
149 |
+
case 'license':
|
150 |
+
/** @var Shield\Modules\License\UI $UILicense */
|
151 |
+
$UILicense = $con->getModule_License()->getUIHandler();
|
152 |
+
$data = $UILicense->buildInsightsVars();
|
153 |
+
break;
|
154 |
+
|
155 |
+
case 'notes':
|
156 |
+
$data = [
|
157 |
+
'content' => [
|
158 |
+
'notes' => ( new AdminNotes() )
|
159 |
+
->setMod( $modPlugin )
|
160 |
+
->render()
|
161 |
+
],
|
162 |
+
];
|
163 |
+
break;
|
164 |
+
|
165 |
+
case 'reports':
|
166 |
+
/** @var Shield\Modules\Reporting\UI $UIReporting */
|
167 |
+
$UIReporting = $con->getModule_Reporting()->getUIHandler();
|
168 |
+
$data = $UIReporting->buildInsightsVars();
|
169 |
+
break;
|
170 |
+
|
171 |
+
case 'scans':
|
172 |
+
/** @var Shield\Modules\HackGuard\UI $UIHackGuard */
|
173 |
+
$UIHackGuard = $con->getModule_HackGuard()->getUIHandler();
|
174 |
+
$data = $UIHackGuard->buildInsightsVars();
|
175 |
+
break;
|
176 |
+
|
177 |
+
case 'settings':
|
178 |
+
$data = $con->modules[ $subNavSection ]->getUIHandler()->getBaseDisplayData();
|
179 |
+
break;
|
180 |
+
|
181 |
+
case 'users':
|
182 |
+
/** @var Shield\Modules\UserManagement\UI $UIUsers */
|
183 |
+
$UIUsers = $con->modules[ 'user_management' ]->getUIHandler();
|
184 |
+
$data = $UIUsers->buildInsightsVars();
|
185 |
+
break;
|
186 |
+
|
187 |
+
case 'overview':
|
188 |
+
case 'index':
|
189 |
+
$data = $this->buildInsightsVars_Overview();
|
190 |
+
break;
|
191 |
+
default:
|
192 |
+
throw new \Exception( 'Not available' );
|
193 |
+
}
|
194 |
+
|
195 |
+
$availablePages = [
|
196 |
+
'settings' => __( 'Plugin Settings', 'wp-simple-firewall' ),
|
197 |
+
'dashboard' => __( 'Dashboard', 'wp-simple-firewall' ),
|
198 |
+
'overview' => __( 'Security Overview', 'wp-simple-firewall' ),
|
199 |
+
'scans' => __( 'Scans', 'wp-simple-firewall' ),
|
200 |
+
'docs' => __( 'Docs', 'wp-simple-firewall' ),
|
201 |
+
'ips' => __( 'IP Management and Analysis', 'wp-simple-firewall' ),
|
202 |
+
'audit' => __( 'Audit Trail', 'wp-simple-firewall' ),
|
203 |
+
'traffic' => __( 'Traffic', 'wp-simple-firewall' ),
|
204 |
+
'notes' => __( 'Admin Notes', 'wp-simple-firewall' ),
|
205 |
+
'users' => __( 'User Sessions', 'wp-simple-firewall' ),
|
206 |
+
'license' => __( 'ShieldPRO', 'wp-simple-firewall' ),
|
207 |
+
'importexport' => __( 'Import / Export', 'wp-simple-firewall' ),
|
208 |
+
'reports' => __( 'Reports', 'wp-simple-firewall' ),
|
209 |
+
'debug' => __( 'Debug', 'wp-simple-firewall' ),
|
210 |
+
'free_trial' => __( 'Free Trial', 'wp-simple-firewall' ),
|
211 |
+
];
|
212 |
+
|
213 |
+
$modsToSearch = array_filter(
|
214 |
+
$mod->getModulesSummaryData(),
|
215 |
+
function ( $modSummary ) {
|
216 |
+
return !empty( $modSummary[ 'show_mod_opts' ] );
|
217 |
+
}
|
218 |
+
);
|
219 |
+
|
220 |
+
$pageTitle = $availablePages[ $sNavSection ];
|
221 |
+
if ( !empty( $subNavSection ) ) {
|
222 |
+
$pageTitle = sprintf( '%s: %s',
|
223 |
+
__( 'Settings', 'wp-simple-firewall' ), $modsToSearch[ $subNavSection ][ 'name' ] );
|
224 |
+
}
|
225 |
+
|
226 |
+
$DP = Services::DataManipulation();
|
227 |
+
$data = $DP->mergeArraysRecursive(
|
228 |
+
$this->getBaseDisplayData(),
|
229 |
+
[
|
230 |
+
'classes' => [
|
231 |
+
'page_container' => 'page-insights page-'.$sNavSection
|
232 |
+
],
|
233 |
+
'flags' => [
|
234 |
+
'is_dashboard' => $sNavSection === 'dashboard',
|
235 |
+
'show_guided_tour' => $modPlugin->getIfShowIntroVideo(),
|
236 |
+
'tours' => [
|
237 |
+
'insights_overview' => false && $oTourManager->canShow( 'insights_overview' )
|
238 |
+
],
|
239 |
+
'is_advanced' => $modPlugin->isShowAdvanced()
|
240 |
+
],
|
241 |
+
'hrefs' => [
|
242 |
+
'back_to_dash' => $mod->getUrl_SubInsightsPage( 'dashboard' ),
|
243 |
+
'go_pro' => 'https://shsec.io/shieldgoprofeature',
|
244 |
+
'nav_home' => $mod->getUrl_AdminPage(),
|
245 |
+
'img_banner' => $con->getPluginUrl_Image( 'pluginlogo_banner-170x40.png' )
|
246 |
+
],
|
247 |
+
'strings' => [
|
248 |
+
'page_title' => $pageTitle
|
249 |
+
],
|
250 |
+
'vars' => [
|
251 |
+
'changelog_id' => $con->getPluginSpec()[ 'meta' ][ 'announcekit_changelog_id' ],
|
252 |
+
'mods' => $this->buildSelectData_ModuleSettings(),
|
253 |
+
'search_select' => $this->buildSelectData_OptionsSearch(),
|
254 |
+
'active_module_settings' => $subNavSection
|
255 |
+
],
|
256 |
+
],
|
257 |
+
$data
|
258 |
+
);
|
259 |
+
|
260 |
+
return $mod->renderTemplate(
|
261 |
+
sprintf( '/wpadmin_pages/insights/%s/index.twig', $sNavSection ),
|
262 |
+
$data,
|
263 |
+
true
|
264 |
+
);
|
265 |
+
}
|
266 |
+
|
267 |
private function renderFreeTrial() :string {
|
268 |
$user = Services::WpUsers()->getCurrentWpUser();
|
269 |
return $this->getMod()
|
271 |
'/forms/drip_trial_signup.twig',
|
272 |
[
|
273 |
'vars' => [
|
|
|
274 |
'activation_url' => Services::WpGeneral()->getHomeUrl(),
|
275 |
'email' => $user->user_email,
|
276 |
'name' => $user->user_firstname,
|
320 |
true
|
321 |
);
|
322 |
}
|
323 |
+
|
324 |
+
public function printAdminFooterItems() {
|
325 |
+
$this->printGoProFooter();
|
326 |
+
$this->printToastTemplate();
|
327 |
+
}
|
328 |
+
|
329 |
+
private function printGoProFooter() {
|
330 |
+
$con = $this->getCon();
|
331 |
+
$nav = Services::Request()->query( 'inav', 'dashboard' );
|
332 |
+
echo $this->getMod()->renderTemplate(
|
333 |
+
'snippets/go_pro_banner.twig',
|
334 |
+
[
|
335 |
+
'flags' => [
|
336 |
+
'show_promo' => $con->isModulePage()
|
337 |
+
&& !$con->isPremiumActive()
|
338 |
+
&& ( !in_array( $nav, [ 'scans' ] ) ),
|
339 |
+
],
|
340 |
+
'hrefs' => [
|
341 |
+
'go_pro' => 'https://shsec.io/shieldgoprofeature',
|
342 |
+
]
|
343 |
+
]
|
344 |
+
);
|
345 |
+
}
|
346 |
+
|
347 |
+
private function printToastTemplate() {
|
348 |
+
if ( $this->getCon()->isModulePage() ) {
|
349 |
+
echo $this->getMod()->renderTemplate(
|
350 |
+
'snippets/toaster.twig',
|
351 |
+
[
|
352 |
+
'strings' => [
|
353 |
+
'title' => $this->getCon()->getHumanName(),
|
354 |
+
],
|
355 |
+
'js_snippets' => []
|
356 |
+
]
|
357 |
+
);
|
358 |
+
}
|
359 |
+
}
|
360 |
+
|
361 |
+
private function printPluginDeactivateSurvey() {
|
362 |
+
if ( Services::WpPost()->isCurrentPage( 'plugins.php' ) ) {
|
363 |
+
|
364 |
+
$opts = [
|
365 |
+
'reason_confusing' => "It's too confusing",
|
366 |
+
'reason_expected' => "It's not what I expected",
|
367 |
+
'reason_accident' => "I downloaded it accidentally",
|
368 |
+
'reason_alternative' => "I'm already using an alternative",
|
369 |
+
'reason_trust' => "I don't trust the developer :(",
|
370 |
+
'reason_not_work' => "It doesn't work",
|
371 |
+
'reason_errors' => "I'm getting errors",
|
372 |
+
];
|
373 |
+
|
374 |
+
echo $this->getMod()->renderTemplate( 'snippets/plugin-deactivate-survey.php', [
|
375 |
+
'strings' => [
|
376 |
+
'editing_restricted' => __( 'Editing this option is currently restricted.', 'wp-simple-firewall' ),
|
377 |
+
],
|
378 |
+
'inputs' => [
|
379 |
+
'checkboxes' => Services::DataManipulation()->shuffleArray( $opts )
|
380 |
+
],
|
381 |
+
'js_snippets' => []
|
382 |
+
] );
|
383 |
+
}
|
384 |
+
}
|
385 |
}
|
src/lib/src/Modules/Integrations/Lib/MainWP/Client/Actions/ApiActionInit.php
ADDED
@@ -0,0 +1,50 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php declare( strict_types=1 );
|
2 |
+
|
3 |
+
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Integrations\Lib\MainWP\Client\Actions;
|
4 |
+
|
5 |
+
use FernleafSystems\Wordpress\Plugin\Shield\Modules\ModConsumer;
|
6 |
+
|
7 |
+
class ApiActionInit {
|
8 |
+
|
9 |
+
use ModConsumer;
|
10 |
+
|
11 |
+
public function run( string $action ) :array {
|
12 |
+
|
13 |
+
switch ( $action ) {
|
14 |
+
case 'license_check':
|
15 |
+
$valid = $this->getCon()
|
16 |
+
->getModule_License()
|
17 |
+
->getLicenseHandler()
|
18 |
+
->verify()
|
19 |
+
->hasValidWorkingLicense();
|
20 |
+
$response = [
|
21 |
+
'success' => $valid,
|
22 |
+
'message' => $valid ? __( 'ShieldPRO license verified', 'wp-simple-firewall' )
|
23 |
+
: __( "ShieldPRO license couldn't be found", 'wp-simple-firewall' )
|
24 |
+
];
|
25 |
+
break;
|
26 |
+
|
27 |
+
case 'mwp_enable':
|
28 |
+
$intMod = $this->getCon()->getModule_Integrations();
|
29 |
+
$intOpts = $intMod->getOptions();
|
30 |
+
$intOpts->setOpt( 'enable_mainwp', 'Y' );
|
31 |
+
$intMod->saveModOptions();
|
32 |
+
$valid = $intOpts->isOpt( 'enable_mainwp', 'Y' );
|
33 |
+
$response = [
|
34 |
+
'success' => $intOpts->isOpt( 'enable_mainwp', 'Y' ),
|
35 |
+
'message' => $valid ? __( 'MainWP Integration Enabled', 'wp-simple-firewall' )
|
36 |
+
: __( "MainWP Integration couldn't be enabled.", 'wp-simple-firewall' )
|
37 |
+
];
|
38 |
+
break;
|
39 |
+
|
40 |
+
default:
|
41 |
+
$response = [
|
42 |
+
'success' => false,
|
43 |
+
'message' => 'Not a supported Shield+MainWP Site Action',
|
44 |
+
];
|
45 |
+
break;
|
46 |
+
}
|
47 |
+
|
48 |
+
return $response;
|
49 |
+
}
|
50 |
+
}
|
src/lib/src/Modules/Integrations/Lib/MainWP/Client/Actions/Init.php
ADDED
@@ -0,0 +1,61 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php declare( strict_types=1 );
|
2 |
+
|
3 |
+
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Integrations\Lib\MainWP\Client\Actions;
|
4 |
+
|
5 |
+
use FernleafSystems\Wordpress\Plugin\Shield\Modules\Integrations\Lib\MainWP\{
|
6 |
+
Client\Auth\ReproduceClientAuthByKey,
|
7 |
+
Controller
|
8 |
+
};
|
9 |
+
use FernleafSystems\Wordpress\Plugin\Shield\Modules\IPs\Lib\Ops\AddIp;
|
10 |
+
use FernleafSystems\Wordpress\Plugin\Shield\Modules\ModConsumer;
|
11 |
+
use FernleafSystems\Wordpress\Services\Services;
|
12 |
+
|
13 |
+
class Init {
|
14 |
+
|
15 |
+
use ModConsumer;
|
16 |
+
|
17 |
+
public function run() {
|
18 |
+
if ( Controller::isMainWPChildVersionSupported() ) {
|
19 |
+
|
20 |
+
// Skip 2FA login if we can verify MainWP Authentication
|
21 |
+
add_filter( 'icwp_shield_2fa_skip', function ( $canSkip ) {
|
22 |
+
return $canSkip || ReproduceClientAuthByKey::Auth();
|
23 |
+
}, 20, 1 );
|
24 |
+
|
25 |
+
// Whitelist the MainWP Server IP
|
26 |
+
add_action( 'mainwp_child_site_stats', function () {
|
27 |
+
try {
|
28 |
+
( new AddIp() )
|
29 |
+
->setMod( $this->getCon()->getModule_IPs() )
|
30 |
+
->setIP( Services::IP()->getRequestIp() )
|
31 |
+
->toManualWhitelist( 'MainWP Server (automatically added)' );
|
32 |
+
}
|
33 |
+
catch ( \Exception $e ) {
|
34 |
+
}
|
35 |
+
}, 10, 0 );
|
36 |
+
|
37 |
+
// Augment Sync data with Shield Sync Data
|
38 |
+
add_filter( 'mainwp_site_sync_others_data', function ( $information, $othersData ) {
|
39 |
+
$con = $this->getCon();
|
40 |
+
if ( isset( $othersData[ $con->prefix( 'mainwp-sync' ) ] ) ) {
|
41 |
+
$information[ $con->prefix( 'mainwp-sync' ) ] = wp_json_encode( ( new Sync() )
|
42 |
+
->setMod( $this->getMod() )
|
43 |
+
->run() );
|
44 |
+
}
|
45 |
+
return $information;
|
46 |
+
}, 10, 2 );
|
47 |
+
|
48 |
+
// Execute custom actions via MainWP API.
|
49 |
+
add_filter( 'mainwp_child_extra_execution', function ( $information, $post ) {
|
50 |
+
$con = $this->getCon();
|
51 |
+
if ( !empty( $post[ $con->prefix( 'mainwp-action' ) ] ) ) {
|
52 |
+
$information[ $con->prefix( 'mainwp-action' ) ] =
|
53 |
+
wp_json_encode( ( new ApiActionInit() )
|
54 |
+
->setMod( $this->getMod() )
|
55 |
+
->run( $post[ $con->prefix( 'mainwp-action' ) ] ) );
|
56 |
+
}
|
57 |
+
return $information;
|
58 |
+
}, 10, 2 );
|
59 |
+
}
|
60 |
+
}
|
61 |
+
}
|
src/lib/src/Modules/Integrations/Lib/MainWP/Client/Actions/Sync.php
ADDED
@@ -0,0 +1,51 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php declare( strict_types=1 );
|
2 |
+
|
3 |
+
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Integrations\Lib\MainWP\Client\Actions;
|
4 |
+
|
5 |
+
use FernleafSystems\Wordpress\Plugin\Shield\Modules\Integrations\Options;
|
6 |
+
use FernleafSystems\Wordpress\Plugin\Shield\Modules\ModConsumer;
|
7 |
+
use FernleafSystems\Wordpress\Services\Services;
|
8 |
+
|
9 |
+
class Sync {
|
10 |
+
|
11 |
+
use ModConsumer;
|
12 |
+
|
13 |
+
public function run() :array {
|
14 |
+
$con = $this->getCon();
|
15 |
+
/** @var Options $intOpts */
|
16 |
+
$intOpts = $con->getModule_Integrations()->getOptions();
|
17 |
+
return [
|
18 |
+
'meta' => $this->buildMetaData(),
|
19 |
+
'modules' => ( $con->isPremiumActive() && $intOpts->isEnabledMainWP() ) ? $this->buildModulesData() : [],
|
20 |
+
];
|
21 |
+
}
|
22 |
+
|
23 |
+
/**
|
24 |
+
* @return mixed[]
|
25 |
+
*/
|
26 |
+
private function buildMetaData() :array {
|
27 |
+
$con = $this->getCon();
|
28 |
+
/** @var Options $intOpts */
|
29 |
+
$intOpts = $con->getModule_Integrations()->getOptions();
|
30 |
+
return [
|
31 |
+
'is_pro' => $con->isPremiumActive(),
|
32 |
+
'is_mainwp_on' => $con->isPremiumActive() && $intOpts->isEnabledMainWP(),
|
33 |
+
'installed_at' => $con->getModule_Plugin()->getInstallDate(),
|
34 |
+
'sync_at' => Services::Request()->ts(),
|
35 |
+
'version' => $con->getVersion(),
|
36 |
+
'has_update' => Services::WpPlugins()->isUpdateAvailable( $con->getPluginBaseFile() ),
|
37 |
+
];
|
38 |
+
}
|
39 |
+
|
40 |
+
/**
|
41 |
+
* @return array[]
|
42 |
+
*/
|
43 |
+
private function buildModulesData() :array {
|
44 |
+
$con = $this->getCon();
|
45 |
+
$data = [];
|
46 |
+
foreach ( $con->modules as $mod ) {
|
47 |
+
$data[ $mod->getSlug() ] = $mod->getMainWpData();
|
48 |
+
}
|
49 |
+
return $data;
|
50 |
+
}
|
51 |
+
}
|
src/lib/src/Modules/Integrations/Lib/MainWP/Client/Auth/ReproduceClientAuthByKey.php
ADDED
@@ -0,0 +1,32 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php declare( strict_types=1 );
|
2 |
+
|
3 |
+
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Integrations\Lib\MainWP\Client\Auth;
|
4 |
+
|
5 |
+
use FernleafSystems\Wordpress\Services\Services;
|
6 |
+
use MainWP\Child\MainWP_Connect;
|
7 |
+
|
8 |
+
/**
|
9 |
+
* This reproduces the authentication done by the MainWP client in MainWP_Child::parse_init().
|
10 |
+
*
|
11 |
+
* Class ReproduceClientAuthByKey
|
12 |
+
* @package FernleafSystems\Wordpress\Plugin\Shield\Modules\Integrations\Lib\MainWP\Client\Auth
|
13 |
+
*/
|
14 |
+
class ReproduceClientAuthByKey {
|
15 |
+
|
16 |
+
public static function Auth() :bool {
|
17 |
+
$req = Services::Request();
|
18 |
+
|
19 |
+
// 'function' for actions, 'where' for login
|
20 |
+
$functionOrWhere = $req->request( 'function' );
|
21 |
+
if ( empty( $functionOrWhere ) ) {
|
22 |
+
$functionOrWhere = $req->request( 'where' );
|
23 |
+
}
|
24 |
+
|
25 |
+
return (bool)MainWP_Connect::instance()->auth(
|
26 |
+
rawurldecode( (string)$req->request( 'mainwpsignature', '' ) ),
|
27 |
+
sanitize_text_field( $functionOrWhere ),
|
28 |
+
sanitize_text_field( $req->request( 'nonce' ) ),
|
29 |
+
sanitize_text_field( $req->request( 'nossl' ) )
|
30 |
+
);
|
31 |
+
}
|
32 |
+
}
|
src/lib/src/Modules/Integrations/Lib/MainWP/Common/Consumers/MWPSiteConsumer.php
ADDED
@@ -0,0 +1,26 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php declare( strict_types=1 );
|
2 |
+
|
3 |
+
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Integrations\Lib\MainWP\Common\Consumers;
|
4 |
+
|
5 |
+
use FernleafSystems\Wordpress\Plugin\Shield\Modules\Integrations\Lib\MainWP\Common\MWPSiteVO;
|
6 |
+
|
7 |
+
/**
|
8 |
+
* Trait MWPSiteConsumer
|
9 |
+
* @package FernleafSystems\Wordpress\Plugin\Shield\Modules\Integrations\Lib\MainWP\Common\Consumers
|
10 |
+
*/
|
11 |
+
trait MWPSiteConsumer {
|
12 |
+
|
13 |
+
/**
|
14 |
+
* @var MWPSiteVO
|
15 |
+
*/
|
16 |
+
private $mwpSite;
|
17 |
+
|
18 |
+
public function getMwpSite() :MWPSiteVO {
|
19 |
+
return $this->mwpSite;
|
20 |
+
}
|
21 |
+
|
22 |
+
public function setMwpSite( MWPSiteVO $site ) :self {
|
23 |
+
$this->mwpSite = $site;
|
24 |
+
return $this;
|
25 |
+
}
|
26 |
+
}
|
src/lib/src/Modules/Integrations/Lib/MainWP/Common/MWPExtensionVO.php
ADDED
@@ -0,0 +1,48 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php declare( strict_types=1 );
|
2 |
+
|
3 |
+
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Integrations\Lib\MainWP\Common;
|
4 |
+
|
5 |
+
use FernleafSystems\Utilities\Data\Adapter\StdClassAdapter;
|
6 |
+
use MainWP\Dashboard\MainWP_Extensions_Handler;
|
7 |
+
|
8 |
+
/**
|
9 |
+
* Class MWPExtensionVO
|
10 |
+
* @package FernleafSystems\Wordpress\Plugin\Shield\Modules\Integrations\Lib\MainWP\Common
|
11 |
+
* @property string $page - e.g. Extensions-Wp-Simple-Firewall
|
12 |
+
*/
|
13 |
+
class MWPExtensionVO {
|
14 |
+
|
15 |
+
use StdClassAdapter {
|
16 |
+
__get as __adapterGet;
|
17 |
+
}
|
18 |
+
|
19 |
+
/**
|
20 |
+
* @param string $property
|
21 |
+
* @return mixed
|
22 |
+
*/
|
23 |
+
public function __get( $property ) {
|
24 |
+
|
25 |
+
$mValue = $this->__adapterGet( $property );
|
26 |
+
|
27 |
+
switch ( $property ) {
|
28 |
+
case 'official_extension_data':
|
29 |
+
$mValue = $this->findOfficialExtensionData();
|
30 |
+
break;
|
31 |
+
default:
|
32 |
+
break;
|
33 |
+
}
|
34 |
+
|
35 |
+
return $mValue;
|
36 |
+
}
|
37 |
+
|
38 |
+
private function findOfficialExtensionData() :array {
|
39 |
+
$data = [];
|
40 |
+
foreach ( MainWP_Extensions_Handler::get_extensions() as $ext ) {
|
41 |
+
if ( $ext[ 'plugin' ] === $this->child_file ) {
|
42 |
+
$data = $ext;
|
43 |
+
break;
|
44 |
+
}
|
45 |
+
}
|
46 |
+
return $data;
|
47 |
+
}
|
48 |
+
}
|
src/lib/src/Modules/Integrations/Lib/MainWP/Common/MWPSiteVO.php
ADDED
@@ -0,0 +1,59 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php declare( strict_types=1 );
|
2 |
+
|
3 |
+
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Integrations\Lib\MainWP\Common;
|
4 |
+
|
5 |
+
use FernleafSystems\Utilities\Data\Adapter\StdClassAdapter;
|
6 |
+
use FernleafSystems\Wordpress\Services\Services;
|
7 |
+
use MainWP\Dashboard\MainWP_DB;
|
8 |
+
|
9 |
+
/**
|
10 |
+
* Class MWPSiteVO
|
11 |
+
* @package FernleafSystems\Wordpress\Plugin\Shield\Modules\Integrations\Lib\MainWP\Common
|
12 |
+
* @property object $siteobj // For use with MainWP functions
|
13 |
+
* @property array[] $plugins
|
14 |
+
* @property array[] $themes
|
15 |
+
*/
|
16 |
+
class MWPSiteVO {
|
17 |
+
|
18 |
+
use StdClassAdapter {
|
19 |
+
__get as __adapterGet;
|
20 |
+
}
|
21 |
+
|
22 |
+
/**
|
23 |
+
* @param int $siteID
|
24 |
+
* @return MWPSiteVO
|
25 |
+
* @throws \Exception
|
26 |
+
*/
|
27 |
+
public static function LoadByID( int $siteID ) :MWPSiteVO {
|
28 |
+
$raw = MainWP_DB::instance()->get_website_by_id( $siteID );
|
29 |
+
if ( empty( $raw ) ) {
|
30 |
+
throw new \Exception( 'Invalid Site ID' );
|
31 |
+
}
|
32 |
+
return ( new MWPSiteVO() )->applyFromArray(
|
33 |
+
Services::DataManipulation()->convertStdClassToArray( $raw )
|
34 |
+
);
|
35 |
+
}
|
36 |
+
|
37 |
+
/**
|
38 |
+
* @param string $property
|
39 |
+
* @return mixed
|
40 |
+
*/
|
41 |
+
public function __get( $property ) {
|
42 |
+
|
43 |
+
$mValue = $this->__adapterGet( $property );
|
44 |
+
|
45 |
+
switch ( $property ) {
|
46 |
+
case 'siteobj':
|
47 |
+
$mValue = Services::DataManipulation()->convertArrayToStdClass( $this->getRawDataAsArray() );
|
48 |
+
break;
|
49 |
+
case 'plugins':
|
50 |
+
case 'themes':
|
51 |
+
$mValue = json_decode( $mValue ?? '[]', true );
|
52 |
+
break;
|
53 |
+
default:
|
54 |
+
break;
|
55 |
+
}
|
56 |
+
|
57 |
+
return $mValue;
|
58 |
+
}
|
59 |
+
}
|
src/lib/src/Modules/Integrations/Lib/MainWP/Common/MainWPVO.php
ADDED
@@ -0,0 +1,56 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php declare( strict_types=1 );
|
2 |
+
|
3 |
+
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Integrations\Lib\MainWP\Common;
|
4 |
+
|
5 |
+
use FernleafSystems\Utilities\Data\Adapter\StdClassAdapter;
|
6 |
+
use MainWP\Dashboard\MainWP_Extensions_Handler;
|
7 |
+
|
8 |
+
/**
|
9 |
+
* Class MainwpVO
|
10 |
+
* @package FernleafSystems\Wordpress\Plugin\Shield\Modules\Integrations\Lib\MainWP\Common
|
11 |
+
* @property string $child_key
|
12 |
+
* @property string $child_file
|
13 |
+
* @property bool $is_client
|
14 |
+
* @property bool $is_server
|
15 |
+
* @property array $official_extension_data
|
16 |
+
* @property MWPExtensionVO $extension
|
17 |
+
*/
|
18 |
+
class MainWPVO {
|
19 |
+
|
20 |
+
use StdClassAdapter {
|
21 |
+
__get as __adapterGet;
|
22 |
+
}
|
23 |
+
|
24 |
+
/**
|
25 |
+
* @param string $property
|
26 |
+
* @return mixed
|
27 |
+
*/
|
28 |
+
public function __get( $property ) {
|
29 |
+
|
30 |
+
$mValue = $this->__adapterGet( $property );
|
31 |
+
|
32 |
+
switch ( $property ) {
|
33 |
+
case 'official_extension_data':
|
34 |
+
$mValue = $this->findOfficialExtensionData();
|
35 |
+
break;
|
36 |
+
case 'extension':
|
37 |
+
$mValue = ( new MWPExtensionVO() )->applyFromArray( $this->official_extension_data );
|
38 |
+
break;
|
39 |
+
default:
|
40 |
+
break;
|
41 |
+
}
|
42 |
+
|
43 |
+
return $mValue;
|
44 |
+
}
|
45 |
+
|
46 |
+
private function findOfficialExtensionData() :array {
|
47 |
+
$data = [];
|
48 |
+
foreach ( MainWP_Extensions_Handler::get_extensions() as $ext ) {
|
49 |
+
if ( $ext[ 'plugin' ] === $this->child_file ) {
|
50 |
+
$data = $ext;
|
51 |
+
break;
|
52 |
+
}
|
53 |
+
}
|
54 |
+
return $data;
|
55 |
+
}
|
56 |
+
}
|
src/lib/src/Modules/Integrations/Lib/MainWP/Common/SyncMetaVO.php
ADDED
@@ -0,0 +1,20 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php declare( strict_types=1 );
|
2 |
+
|
3 |
+
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Integrations\Lib\MainWP\Common;
|
4 |
+
|
5 |
+
use FernleafSystems\Utilities\Data\Adapter\StdClassAdapter;
|
6 |
+
|
7 |
+
/**
|
8 |
+
* Class SyncVO - property should align with Sync Meta
|
9 |
+
* @package FernleafSystems\Wordpress\Plugin\Shield\Modules\Integrations\Lib\MainWP\Common
|
10 |
+
* @property bool $is_pro
|
11 |
+
* @property bool $is_mainwp_on
|
12 |
+
* @property int $installed_at
|
13 |
+
* @property int $sync_at
|
14 |
+
* @property string $version
|
15 |
+
* @property bool $has_update
|
16 |
+
*/
|
17 |
+
class SyncMetaVO {
|
18 |
+
|
19 |
+
use StdClassAdapter;
|
20 |
+
}
|
src/lib/src/Modules/Integrations/Lib/MainWP/Common/SyncVO.php
ADDED
@@ -0,0 +1,37 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php declare( strict_types=1 );
|
2 |
+
|
3 |
+
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Integrations\Lib\MainWP\Common;
|
4 |
+
|
5 |
+
use FernleafSystems\Utilities\Data\Adapter\StdClassAdapter;
|
6 |
+
|
7 |
+
/**
|
8 |
+
* Class SyncVO
|
9 |
+
* @package FernleafSystems\Wordpress\Plugin\Shield\Modules\Integrations\Lib\MainWP\Common
|
10 |
+
* @property array[] $modules
|
11 |
+
* @property SyncMetaVO $meta
|
12 |
+
*/
|
13 |
+
class SyncVO {
|
14 |
+
|
15 |
+
use StdClassAdapter {
|
16 |
+
__get as __adapterGet;
|
17 |
+
}
|
18 |
+
|
19 |
+
/**
|
20 |
+
* @param string $property
|
21 |
+
* @return mixed
|
22 |
+
*/
|
23 |
+
public function __get( $property ) {
|
24 |
+
|
25 |
+
$mValue = $this->__adapterGet( $property );
|
26 |
+
|
27 |
+
switch ( $property ) {
|
28 |
+
case 'meta':
|
29 |
+
$mValue = ( new SyncMetaVO() )->applyFromArray( $mValue );
|
30 |
+
break;
|
31 |
+
default:
|
32 |
+
break;
|
33 |
+
}
|
34 |
+
|
35 |
+
return $mValue;
|
36 |
+
}
|
37 |
+
}
|
src/lib/src/Modules/Integrations/Lib/MainWP/Controller.php
ADDED
@@ -0,0 +1,88 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php declare( strict_types=1 );
|
2 |
+
|
3 |
+
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Integrations\Lib\MainWP;
|
4 |
+
|
5 |
+
use FernleafSystems\Utilities\Logic\OneTimeExecute;
|
6 |
+
use FernleafSystems\Wordpress\Plugin\Shield\Modules\Integrations\Lib\MainWP\Client;
|
7 |
+
use FernleafSystems\Wordpress\Plugin\Shield\Modules\Integrations\Lib\MainWP\Common\MainWPVO;
|
8 |
+
use FernleafSystems\Wordpress\Plugin\Shield\Modules\Integrations\Lib\MainWP\Server;
|
9 |
+
use FernleafSystems\Wordpress\Plugin\Shield\Modules\ModConsumer;
|
10 |
+
|
11 |
+
class Controller {
|
12 |
+
|
13 |
+
use ModConsumer;
|
14 |
+
use OneTimeExecute;
|
15 |
+
|
16 |
+
const MIN_VERSION_MAINWP = '4.1';
|
17 |
+
|
18 |
+
protected function run() {
|
19 |
+
try {
|
20 |
+
$this->runServerSide();
|
21 |
+
}
|
22 |
+
catch ( \Exception $e ) {
|
23 |
+
}
|
24 |
+
try {
|
25 |
+
$this->runClientSide();
|
26 |
+
}
|
27 |
+
catch ( \Exception $e ) {
|
28 |
+
}
|
29 |
+
}
|
30 |
+
|
31 |
+
/**
|
32 |
+
* @throws \Exception
|
33 |
+
*/
|
34 |
+
private function runClientSide() {
|
35 |
+
$con = $this->getCon();
|
36 |
+
$mwpVO = $con->mwpVO ?? new MainWPVO();
|
37 |
+
$mwpVO->is_client = $this->isMainWPChildActive();
|
38 |
+
|
39 |
+
if ( !$mwpVO->is_client ) {
|
40 |
+
throw new \Exception( 'MainWP Child not active' );
|
41 |
+
}
|
42 |
+
|
43 |
+
( new Client\Actions\Init() )
|
44 |
+
->setMod( $this->getMod() )
|
45 |
+
->run();
|
46 |
+
|
47 |
+
$con->mwpVO = $mwpVO;
|
48 |
+
}
|
49 |
+
|
50 |
+
/**
|
51 |
+
* @throws \Exception
|
52 |
+
*/
|
53 |
+
private function runServerSide() {
|
54 |
+
$con = $this->getCon();
|
55 |
+
$mwpVO = $con->mwpVO ?? new MainWPVO();
|
56 |
+
$mwpVO->is_server = false;
|
57 |
+
|
58 |
+
if ( !$this->isMainWPServerActive() ) {
|
59 |
+
throw new \Exception( 'MainWP not active' );
|
60 |
+
}
|
61 |
+
|
62 |
+
$mwpVO->child_key = ( new Server\Init() )
|
63 |
+
->setMod( $this->getMod() )
|
64 |
+
->run();
|
65 |
+
$mwpVO->child_file = $con->getRootFile();
|
66 |
+
|
67 |
+
$mwpVO->is_server = true;
|
68 |
+
|
69 |
+
$con->mwpVO = $mwpVO;
|
70 |
+
}
|
71 |
+
|
72 |
+
private function isMainWPChildActive() :bool {
|
73 |
+
return @class_exists( '\MainWP\Child\MainWP_Child' );
|
74 |
+
}
|
75 |
+
|
76 |
+
private function isMainWPServerActive() :bool {
|
77 |
+
return (bool)apply_filters( 'mainwp_activated_check', false );
|
78 |
+
}
|
79 |
+
|
80 |
+
public static function isMainWPChildVersionSupported() :bool {
|
81 |
+
return version_compare( \MainWP\Child\MainWP_Child::$version, self::MIN_VERSION_MAINWP, '>=' );
|
82 |
+
}
|
83 |
+
|
84 |
+
public static function isMainWPServerVersionSupported() :bool {
|
85 |
+
return defined( 'MAINWP_VERSION' )
|
86 |
+
&& version_compare( MAINWP_VERSION, self::MIN_VERSION_MAINWP, '>=' );
|
87 |
+
}
|
88 |
+
}
|
src/lib/src/Modules/Integrations/Lib/MainWP/Server/Actions/Action.php
ADDED
@@ -0,0 +1,42 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php declare( strict_types=1 );
|
2 |
+
|
3 |
+
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Integrations\Lib\MainWP\Server\Actions;
|
4 |
+
|
5 |
+
use FernleafSystems\Wordpress\Plugin\Shield\Modules\Integrations\Lib\MainWP;
|
6 |
+
use FernleafSystems\Wordpress\Plugin\Shield\Modules\ModConsumer;
|
7 |
+
use MainWP\Dashboard\MainWP_Connect;
|
8 |
+
|
9 |
+
class Action {
|
10 |
+
|
11 |
+
use ModConsumer;
|
12 |
+
use MainWP\Common\Consumers\MWPSiteConsumer;
|
13 |
+
|
14 |
+
/**
|
15 |
+
* @param string $actionToExecute
|
16 |
+
* @param array $params
|
17 |
+
* @return array
|
18 |
+
* @throws \Exception
|
19 |
+
*/
|
20 |
+
public function run( string $actionToExecute, array $params = [] ) :array {
|
21 |
+
$info = MainWP_Connect::fetch_url_authed(
|
22 |
+
$this->getMwpSite()->siteobj,
|
23 |
+
'extra_execution',
|
24 |
+
[
|
25 |
+
$this->getCon()->prefix( 'mainwp-action' ) => $actionToExecute,
|
26 |
+
$this->getCon()->prefix( 'mainwp-params' ) => $params
|
27 |
+
]
|
28 |
+
);
|
29 |
+
|
30 |
+
$key = $this->getCon()->prefix( 'mainwp-action' );
|
31 |
+
if ( empty( $info ) || !is_array( $info ) || !isset( $info[ $key ] ) ) {
|
32 |
+
throw new \Exception( 'Empty response from client site' );
|
33 |
+
}
|
34 |
+
|
35 |
+
$decoded = json_decode( $info[ $key ], true );
|
36 |
+
if ( empty( $decoded ) || !is_array( $decoded ) ) {
|
37 |
+
throw new \Exception( 'Invalid response from client site' );
|
38 |
+
}
|
39 |
+
|
40 |
+
return $decoded;
|
41 |
+
}
|
42 |
+
}
|
src/lib/src/Modules/Integrations/Lib/MainWP/Server/Actions/ShieldApiAction.php
ADDED
@@ -0,0 +1,29 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php declare( strict_types=1 );
|
2 |
+
|
3 |
+
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Integrations\Lib\MainWP\Server\Actions;
|
4 |
+
|
5 |
+
use FernleafSystems\Wordpress\Plugin\Shield\Modules\Integrations\Lib\MainWP\{
|
6 |
+
Common\Consumers\MWPSiteConsumer
|
7 |
+
};
|
8 |
+
use FernleafSystems\Wordpress\Plugin\Shield\Modules\ModConsumer;
|
9 |
+
|
10 |
+
class ShieldApiAction {
|
11 |
+
|
12 |
+
use ModConsumer;
|
13 |
+
use MWPSiteConsumer;
|
14 |
+
|
15 |
+
public function mwpEnable() :array {
|
16 |
+
return $this->runAction( 'mwp_enable' );
|
17 |
+
}
|
18 |
+
|
19 |
+
public function licenseCheck() :array {
|
20 |
+
return $this->runAction( 'license_check' );
|
21 |
+
}
|
22 |
+
|
23 |
+
private function runAction( string $action, array $params = [] ) :array {
|
24 |
+
return ( new Action() )
|
25 |
+
->setMod( $this->getMod() )
|
26 |
+
->setMwpSite( $this->getMwpSite() )
|
27 |
+
->run( $action, $params );
|
28 |
+
}
|
29 |
+
}
|
src/lib/src/Modules/Integrations/Lib/MainWP/Server/Actions/ShieldPluginAction.php
ADDED
@@ -0,0 +1,128 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php declare( strict_types=1 );
|
2 |
+
|
3 |
+
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Integrations\Lib\MainWP\Server\Actions;
|
4 |
+
|
5 |
+
use FernleafSystems\Wordpress\Plugin\Shield\Modules\Integrations\Lib\MainWP\{
|
6 |
+
Common\Consumers\MWPSiteConsumer,
|
7 |
+
Server\Data\ClientPluginStatus
|
8 |
+
};
|
9 |
+
use FernleafSystems\Wordpress\Plugin\Shield\Modules\ModConsumer;
|
10 |
+
use FernleafSystems\Wordpress\Services\Utilities\WpOrg\Plugin\Api;
|
11 |
+
use MainWP\Dashboard\MainWP_Connect;
|
12 |
+
use MainWP\Dashboard\MainWP_Sync;
|
13 |
+
|
14 |
+
class ShieldPluginAction {
|
15 |
+
|
16 |
+
use ModConsumer;
|
17 |
+
use MWPSiteConsumer;
|
18 |
+
|
19 |
+
public function alignByStatus() {
|
20 |
+
$oStatus = ( new ClientPluginStatus() )
|
21 |
+
->setMod( $this->getMod() )
|
22 |
+
->setMwpSite( $this->getMwpSite() );
|
23 |
+
|
24 |
+
switch ( $oStatus->status() ) {
|
25 |
+
|
26 |
+
case ClientPluginStatus::INACTIVE:
|
27 |
+
if ( $this->activate() ) {
|
28 |
+
$this->sync();
|
29 |
+
}
|
30 |
+
break;
|
31 |
+
|
32 |
+
case ClientPluginStatus::NEED_SYNC:
|
33 |
+
$this->sync();
|
34 |
+
break;
|
35 |
+
|
36 |
+
case ClientPluginStatus::VERSION_OLDER_THAN_SERVER:
|
37 |
+
$this->sync();
|
38 |
+
$this->upgrade();
|
39 |
+
$this->sync();
|
40 |
+
break;
|
41 |
+
|
42 |
+
case ClientPluginStatus::NOT_INSTALLED:
|
43 |
+
$this->install();
|
44 |
+
$this->sync();
|
45 |
+
break;
|
46 |
+
|
47 |
+
case ClientPluginStatus::ACTIVE:
|
48 |
+
default:
|
49 |
+
// nothing
|
50 |
+
break;
|
51 |
+
}
|
52 |
+
}
|
53 |
+
|
54 |
+
public function activate() :bool {
|
55 |
+
$siteObj = $this->getMwpSite()->siteobj;
|
56 |
+
$info = MainWP_Connect::fetch_url_authed(
|
57 |
+
$siteObj,
|
58 |
+
'plugin_action',
|
59 |
+
[
|
60 |
+
'action' => 'activate',
|
61 |
+
'plugin' => ( new ClientPluginStatus() )
|
62 |
+
->setMod( $this->getMod() )
|
63 |
+
->setMwpSite( $this->getMwpSite() )
|
64 |
+
->getInstalledPlugin()[ 'slug' ],
|
65 |
+
]
|
66 |
+
);
|
67 |
+
|
68 |
+
$status = $info[ 'status' ] ?? false;
|
69 |
+
return $status === 'SUCCESS';
|
70 |
+
}
|
71 |
+
|
72 |
+
public function deactivate() :bool {
|
73 |
+
$siteObj = $this->getMwpSite()->siteobj;
|
74 |
+
$info = MainWP_Connect::fetch_url_authed(
|
75 |
+
$siteObj,
|
76 |
+
'plugin_action',
|
77 |
+
[
|
78 |
+
'action' => 'deactivate',
|
79 |
+
'plugin' => ( new ClientPluginStatus() )
|
80 |
+
->setMod( $this->getMod() )
|
81 |
+
->setMwpSite( $this->getMwpSite() )
|
82 |
+
->getInstalledPlugin()[ 'slug' ],
|
83 |
+
]
|
84 |
+
);
|
85 |
+
|
86 |
+
$status = $info[ 'status' ] ?? false;
|
87 |
+
return $status === 'SUCCESS';
|
88 |
+
}
|
89 |
+
|
90 |
+
public function install() :bool {
|
91 |
+
$urlInstall = ( new Api() )
|
92 |
+
->setWorkingSlug( 'wp-simple-firewall' )
|
93 |
+
->getInfo()->download_link;
|
94 |
+
|
95 |
+
$info = MainWP_Connect::fetch_url_authed(
|
96 |
+
$this->getMwpSite()->siteobj,
|
97 |
+
'installplugintheme',
|
98 |
+
[
|
99 |
+
'type' => 'plugin',
|
100 |
+
'url' => wp_json_encode( $urlInstall ),
|
101 |
+
'activatePlugin' => 'yes',
|
102 |
+
'overwrite' => true,
|
103 |
+
]
|
104 |
+
);
|
105 |
+
return !empty( $info[ 'installation' ] ) && $info[ 'installation' ] === 'SUCCESS';
|
106 |
+
}
|
107 |
+
|
108 |
+
public function sync() :bool {
|
109 |
+
return (bool)MainWP_Sync::sync_site( $this->getMwpSite()->siteobj );
|
110 |
+
}
|
111 |
+
|
112 |
+
public function upgrade() :bool {
|
113 |
+
$siteObj = $this->getMwpSite()->siteobj;
|
114 |
+
MainWP_Connect::fetch_url_authed(
|
115 |
+
$siteObj,
|
116 |
+
'upgradeplugintheme',
|
117 |
+
[
|
118 |
+
'type' => 'plugin',
|
119 |
+
'list' => ( new ClientPluginStatus() )
|
120 |
+
->setMod( $this->getMod() )
|
121 |
+
->setMwpSite( $this->getMwpSite() )
|
122 |
+
->getInstalledPlugin()[ 'slug' ],
|
123 |
+
],
|
124 |
+
true
|
125 |
+
);
|
126 |
+
return true;
|
127 |
+
}
|
128 |
+
}
|
src/lib/src/Modules/Integrations/Lib/MainWP/Server/Ajax/AjaxHandlerMainwp.php
ADDED
@@ -0,0 +1,65 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php declare( strict_types=1 );
|
2 |
+
|
3 |
+
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Integrations\Lib\MainWP\Server\Ajax;
|
4 |
+
|
5 |
+
use FernleafSystems\Wordpress\Plugin\Shield;
|
6 |
+
use FernleafSystems\Wordpress\Plugin\Shield\Modules\Integrations\Lib\MainWP;
|
7 |
+
use FernleafSystems\Wordpress\Services\Services;
|
8 |
+
|
9 |
+
class AjaxHandlerMainwp extends Shield\Modules\BaseShield\AjaxHandler {
|
10 |
+
|
11 |
+
protected function processAjaxAction( string $action ) :array {
|
12 |
+
$resp = [];
|
13 |
+
|
14 |
+
// This allows us to provide a specific MainWP error message
|
15 |
+
if ( strpos( $action, 'mwp_' ) === 0 ) {
|
16 |
+
|
17 |
+
switch ( $action ) {
|
18 |
+
case 'mwp_sh_ext_table':
|
19 |
+
$resp = $this->ajaxExec_SiteAction();
|
20 |
+
break;
|
21 |
+
|
22 |
+
case 'mwp_sh_site_action':
|
23 |
+
$resp = $this->ajaxExec_ExtensionTableSites();
|
24 |
+
break;
|
25 |
+
|
26 |
+
default:
|
27 |
+
$resp = [
|
28 |
+
'success' => false,
|
29 |
+
'message' => sprintf( __( 'Not a supported MainWP+%s action.' ),
|
30 |
+
$this->getCon()->getHumanName() )
|
31 |
+
];
|
32 |
+
}
|
33 |
+
}
|
34 |
+
|
35 |
+
return $resp;
|
36 |
+
}
|
37 |
+
|
38 |
+
private function ajaxExec_SiteAction() :array {
|
39 |
+
$req = Services::Request();
|
40 |
+
|
41 |
+
$siteID = (int)$req->post( 'sid' );
|
42 |
+
$action = $req->post( 'saction' );
|
43 |
+
try {
|
44 |
+
if ( empty( $siteID ) ) {
|
45 |
+
throw new \Exception( 'invalid site ID' );
|
46 |
+
}
|
47 |
+
$resp = ( new PerformSiteAction() )
|
48 |
+
->setMwpSite( MainWP\Common\MWPSiteVO::LoadByID( $siteID ) )
|
49 |
+
->setMod( $this->getMod() )
|
50 |
+
->run( $action );
|
51 |
+
}
|
52 |
+
catch ( \Exception $e ) {
|
53 |
+
$resp = [
|
54 |
+
'success' => false,
|
55 |
+
'message' => $e->getMessage()
|
56 |
+
];
|
57 |
+
}
|
58 |
+
|
59 |
+
return $resp;
|
60 |
+
}
|
61 |
+
|
62 |
+
private function ajaxExec_ExtensionTableSites() {
|
63 |
+
|
64 |
+
}
|
65 |
+
}
|
src/lib/src/Modules/Integrations/Lib/MainWP/Server/Ajax/PerformSiteAction.php
ADDED
@@ -0,0 +1,119 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php declare( strict_types=1 );
|
2 |
+
|
3 |
+
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Integrations\Lib\MainWP\Server\Ajax;
|
4 |
+
|
5 |
+
use FernleafSystems\Wordpress\Plugin\Shield;
|
6 |
+
use FernleafSystems\Wordpress\Plugin\Shield\Modules\Integrations\Lib\MainWP;
|
7 |
+
use FernleafSystems\Wordpress\Plugin\Shield\Modules\Integrations\Lib\MainWP\Server;
|
8 |
+
|
9 |
+
class PerformSiteAction {
|
10 |
+
|
11 |
+
use MainWP\Common\Consumers\MWPSiteConsumer;
|
12 |
+
use Shield\Modules\ModConsumer;
|
13 |
+
|
14 |
+
public function run( string $action ) :array {
|
15 |
+
try {
|
16 |
+
$resp = [
|
17 |
+
'success' => true,
|
18 |
+
'message' => $this->{$action}()
|
19 |
+
];
|
20 |
+
}
|
21 |
+
catch ( \Exception $e ) {
|
22 |
+
$resp = [
|
23 |
+
'success' => false,
|
24 |
+
'message' => $e->getMessage(),
|
25 |
+
];
|
26 |
+
}
|
27 |
+
|
28 |
+
if ( !in_array( $action, [ 'sync' ] ) ) {
|
29 |
+
$this->getPluginActioner()->sync();
|
30 |
+
}
|
31 |
+
|
32 |
+
$resp[ 'page_reload' ] = true;
|
33 |
+
return $resp;
|
34 |
+
}
|
35 |
+
|
36 |
+
/**
|
37 |
+
* @return string
|
38 |
+
* @throws \Exception
|
39 |
+
*/
|
40 |
+
private function activate() :string {
|
41 |
+
if ( !$this->getPluginActioner()->activate() ) {
|
42 |
+
throw new \Exception( sprintf( __( 'Failed to activate %s plugin.' ), $this->getCon()->getHumanName() ) );
|
43 |
+
}
|
44 |
+
return sprintf( __( 'Successfully activated %s plugin.', 'wp-simple-firewall' ),
|
45 |
+
$this->getCon()->getHumanName() );
|
46 |
+
}
|
47 |
+
|
48 |
+
/**
|
49 |
+
* @return string
|
50 |
+
* @throws \Exception
|
51 |
+
*/
|
52 |
+
private function deactivate() :string {
|
53 |
+
if ( !$this->getPluginActioner()->deactivate() ) {
|
54 |
+
throw new \Exception( sprintf( __( 'Failed to deactivate %s plugin.' ), $this->getCon()->getHumanName() ) );
|
55 |
+
}
|
56 |
+
return sprintf( __( 'Successfully deactivated %s plugin.', 'wp-simple-firewall' ),
|
57 |
+
$this->getCon()->getHumanName() );
|
58 |
+
}
|
59 |
+
|
60 |
+
/**
|
61 |
+
* @return string
|
62 |
+
* @throws \Exception
|
63 |
+
*/
|
64 |
+
private function install() :string {
|
65 |
+
if ( !$this->getPluginActioner()->install() ) {
|
66 |
+
throw new \Exception( sprintf( __( 'Failed to install %s plugin.' ), $this->getCon()->getHumanName() ) );
|
67 |
+
}
|
68 |
+
return sprintf( __( 'Successfully installed %s plugin.', 'wp-simple-firewall' ),
|
69 |
+
$this->getCon()->getHumanName() );
|
70 |
+
}
|
71 |
+
|
72 |
+
/**
|
73 |
+
* @return string
|
74 |
+
* @throws \Exception
|
75 |
+
*/
|
76 |
+
private function license() :string {
|
77 |
+
$resp = $this->getApiActioner()->licenseCheck();
|
78 |
+
if ( empty( $resp[ 'success' ] ) ) {
|
79 |
+
throw new \Exception( $resp[ 'message' ] );
|
80 |
+
}
|
81 |
+
return $resp[ 'message' ];
|
82 |
+
}
|
83 |
+
|
84 |
+
/**
|
85 |
+
* @return string
|
86 |
+
* @throws \Exception
|
87 |
+
*/
|
88 |
+
private function mwp() :string {
|
89 |
+
$resp = $this->getApiActioner()->mwpEnable();
|
90 |
+
if ( empty( $resp[ 'success' ] ) ) {
|
91 |
+
throw new \Exception( $resp[ 'message' ] );
|
92 |
+
}
|
93 |
+
return $resp[ 'message' ];
|
94 |
+
}
|
95 |
+
|
96 |
+
/**
|
97 |
+
* @return string
|
98 |
+
* @throws \Exception
|
99 |
+
*/
|
100 |
+
private function sync() :string {
|
101 |
+
if ( !$this->getPluginActioner()->sync() ) {
|
102 |
+
throw new \Exception( sprintf( __( 'Failed to sync with %s plugin.' ), $this->getCon()->getHumanName() ) );
|
103 |
+
}
|
104 |
+
return sprintf( __( 'Successfully synced with %s plugin.', 'wp-simple-firewall' ),
|
105 |
+
$this->getCon()->getHumanName() );
|
106 |
+
}
|
107 |
+
|
108 |
+
private function getPluginActioner() :Server\Actions\ShieldPluginAction {
|
109 |
+
return ( new Server\Actions\ShieldPluginAction() )
|
110 |
+
->setMod( $this->getMod() )
|
111 |
+
->setMwpSite( $this->getMwpSite() );
|
112 |
+
}
|
113 |
+
|
114 |
+
private function getApiActioner() :Server\Actions\ShieldApiAction {
|
115 |
+
return ( new Server\Actions\ShieldApiAction() )
|
116 |
+
->setMod( $this->getMod() )
|
117 |
+
->setMwpSite( $this->getMwpSite() );
|
118 |
+
}
|
119 |
+
}
|
src/lib/src/Modules/Integrations/Lib/MainWP/Server/Data/ClientPluginStatus.php
ADDED
@@ -0,0 +1,105 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php declare( strict_types=1 );
|
2 |
+
|
3 |
+
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Integrations\Lib\MainWP\Server\Data;
|
4 |
+
|
5 |
+
use FernleafSystems\Wordpress\Plugin\Shield\Modules\Integrations\Lib\MainWP\Common\Consumers\MWPSiteConsumer;
|
6 |
+
use FernleafSystems\Wordpress\Plugin\Shield\Modules\ModConsumer;
|
7 |
+
|
8 |
+
class ClientPluginStatus {
|
9 |
+
|
10 |
+
use ModConsumer;
|
11 |
+
use MWPSiteConsumer;
|
12 |
+
|
13 |
+
const ACTIVE = 'acti';
|
14 |
+
const NEED_SYNC = 'nsync';
|
15 |
+
const NOT_PRO = 'npro';
|
16 |
+
const MWP_NOT_ON = 'mwpnoton';
|
17 |
+
const INACTIVE = 'inact';
|
18 |
+
const NOT_INSTALLED = 'ninst';
|
19 |
+
const VERSION_NEWER_THAN_SERVER = 'vnts';
|
20 |
+
const VERSION_OLDER_THAN_SERVER = 'vots';
|
21 |
+
|
22 |
+
public function status() :string {
|
23 |
+
$status = $this->detect();
|
24 |
+
return key( $status );
|
25 |
+
}
|
26 |
+
|
27 |
+
/**
|
28 |
+
* TODO: Consider things like global disabled / forceoff
|
29 |
+
* @return array
|
30 |
+
*/
|
31 |
+
public function detect() :array {
|
32 |
+
$sync = LoadShieldSyncData::Load( $this->getMwpSite() );
|
33 |
+
$m = $sync->meta;
|
34 |
+
|
35 |
+
if ( $this->isActive() ) {
|
36 |
+
|
37 |
+
if ( empty( $sync->getRawDataAsArray() ) ) {
|
38 |
+
$status = self::NEED_SYNC;
|
39 |
+
}
|
40 |
+
elseif ( empty( $m->is_pro ) ) {
|
41 |
+
$status = self::NOT_PRO;
|
42 |
+
}
|
43 |
+
elseif ( empty( $m->is_mainwp_on ) ) {
|
44 |
+
$status = self::MWP_NOT_ON;
|
45 |
+
}
|
46 |
+
else {
|
47 |
+
$versionStatus = version_compare( $this->getCon()->getVersion(), $m->version );
|
48 |
+
if ( $versionStatus === -1 ) {
|
49 |
+
$status = self::VERSION_NEWER_THAN_SERVER;
|
50 |
+
}
|
51 |
+
elseif ( $versionStatus === 1 ) {
|
52 |
+
$status = self::VERSION_OLDER_THAN_SERVER;
|
53 |
+
}
|
54 |
+
else {
|
55 |
+
$status = self::ACTIVE;
|
56 |
+
}
|
57 |
+
}
|
58 |
+
}
|
59 |
+
elseif ( $this->isInstalled() ) {
|
60 |
+
$status = self::INACTIVE;
|
61 |
+
}
|
62 |
+
else {
|
63 |
+
$status = self::NOT_INSTALLED;
|
64 |
+
}
|
65 |
+
return [ $status => $this->getStatusText()[ $status ] ];
|
66 |
+
}
|
67 |
+
|
68 |
+
/**
|
69 |
+
* @return array|null
|
70 |
+
*/
|
71 |
+
public function getInstalledPlugin() {
|
72 |
+
$thePlugin = null;
|
73 |
+
|
74 |
+
$baseName = basename( $this->getCon()->getPluginBaseFile() );
|
75 |
+
foreach ( $this->getMwpSite()->plugins as $plugin ) {
|
76 |
+
if ( basename( $plugin[ 'slug' ] ) === $baseName ) {
|
77 |
+
$thePlugin = $plugin;
|
78 |
+
break;
|
79 |
+
}
|
80 |
+
}
|
81 |
+
|
82 |
+
return $thePlugin;
|
83 |
+
}
|
84 |
+
|
85 |
+
public function isActive() :bool {
|
86 |
+
return !empty( $this->getInstalledPlugin()[ 'active' ] );
|
87 |
+
}
|
88 |
+
|
89 |
+
public function isInstalled() :bool {
|
90 |
+
return !empty( $this->getInstalledPlugin() );
|
91 |
+
}
|
92 |
+
|
93 |
+
public function getStatusText() :array {
|
94 |
+
return [
|
95 |
+
self::ACTIVE => __( 'Active' ),
|
96 |
+
self::NOT_PRO => __( 'Not Pro' ),
|
97 |
+
self::MWP_NOT_ON => __( 'MainWP Option Not Enabled' ),
|
98 |
+
self::NEED_SYNC => __( 'Sync Required' ),
|
99 |
+
self::INACTIVE => __( 'Installed' ),
|
100 |
+
self::NOT_INSTALLED => __( 'Not Installed' ),
|
101 |
+
self::VERSION_OLDER_THAN_SERVER => __( 'Update Required' ),
|
102 |
+
self::VERSION_NEWER_THAN_SERVER => __( 'Ahead Of Server' ),
|
103 |
+
];
|
104 |
+
}
|
105 |
+
}
|
src/lib/src/Modules/Integrations/Lib/MainWP/Server/Data/LoadShieldSyncData.php
ADDED
@@ -0,0 +1,23 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php declare( strict_types=1 );
|
2 |
+
|
3 |
+
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Integrations\Lib\MainWP\Server\Data;
|
4 |
+
|
5 |
+
use FernleafSystems\Wordpress\Plugin\Shield\Controller\Controller;
|
6 |
+
use FernleafSystems\Wordpress\Plugin\Shield\Modules\Integrations\Lib\MainWP\Common\{
|
7 |
+
MWPSiteVO,
|
8 |
+
SyncVO};
|
9 |
+
use MainWP\Dashboard\MainWP_DB;
|
10 |
+
|
11 |
+
class LoadShieldSyncData {
|
12 |
+
|
13 |
+
public static function Load( MWPSiteVO $site ) :SyncVO {
|
14 |
+
$data = MainWP_DB::instance()->get_website_option(
|
15 |
+
$site->getRawDataAsArray(),
|
16 |
+
Controller::GetInstance()->prefix( 'mainwp-sync' )
|
17 |
+
);
|
18 |
+
if ( empty( $data ) ) {
|
19 |
+
$data = '[]';
|
20 |
+
}
|
21 |
+
return ( new SyncVO() )->applyFromArray( json_decode( $data, true ) );
|
22 |
+
}
|
23 |
+
}
|
src/lib/src/Modules/Integrations/Lib/MainWP/Server/Data/SyncHandler.php
ADDED
@@ -0,0 +1,36 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php declare( strict_types=1 );
|
2 |
+
|
3 |
+
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Integrations\Lib\MainWP\Server\Data;
|
4 |
+
|
5 |
+
use FernleafSystems\Utilities\Logic\OneTimeExecute;
|
6 |
+
use FernleafSystems\Wordpress\Plugin\Shield\Modules\ModConsumer;
|
7 |
+
use MainWP\Dashboard\MainWP_DB;
|
8 |
+
|
9 |
+
class SyncHandler {
|
10 |
+
|
11 |
+
use ModConsumer;
|
12 |
+
use OneTimeExecute;
|
13 |
+
|
14 |
+
protected function run() {
|
15 |
+
add_action( 'mainwp_sync_others_data', function ( $othersData, $website ) {
|
16 |
+
$othersData[ $this->getCon()->prefix( 'mainwp-sync' ) ] = 'shield';
|
17 |
+
return $othersData;
|
18 |
+
}, 10, 2 );
|
19 |
+
add_action( 'mainwp_site_synced', function ( $website, $info ) {
|
20 |
+
$this->syncSite( $website, $info );
|
21 |
+
}, 10, 2 );
|
22 |
+
}
|
23 |
+
|
24 |
+
/**
|
25 |
+
* @param object $website
|
26 |
+
* @param array $info
|
27 |
+
*/
|
28 |
+
private function syncSite( $website, array $info ) {
|
29 |
+
$con = $this->getCon();
|
30 |
+
MainWP_DB::instance()->update_website_option(
|
31 |
+
$website,
|
32 |
+
$con->prefix( 'mainwp-sync' ),
|
33 |
+
$info[ $con->prefix( 'mainwp-sync' ) ] ?? '[]'
|
34 |
+
);
|
35 |
+
}
|
36 |
+
}
|
src/lib/src/Modules/Integrations/Lib/MainWP/Server/Init.php
ADDED
@@ -0,0 +1,65 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php declare( strict_types=1 );
|
2 |
+
|
3 |
+
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Integrations\Lib\MainWP\Server;
|
4 |
+
|
5 |
+
use FernleafSystems\Wordpress\Plugin\Shield\Modules\Integrations\Lib\MainWP\Controller;
|
6 |
+
use FernleafSystems\Wordpress\Plugin\Shield\Modules\Integrations\Lib\MainWP\Server\Ajax\AjaxHandlerMainwp;
|
7 |
+
use FernleafSystems\Wordpress\Plugin\Shield\Modules\Integrations\Lib\MainWP\Server\Data\SyncHandler;
|
8 |
+
use FernleafSystems\Wordpress\Plugin\Shield\Modules\Integrations\Lib\MainWP\Server\UI\ExtensionSettingsPage;
|
9 |
+
use FernleafSystems\Wordpress\Plugin\Shield\Modules\Integrations\Options;
|
10 |
+
use FernleafSystems\Wordpress\Plugin\Shield\Modules\ModConsumer;
|
11 |
+
use FernleafSystems\Wordpress\Services\Services;
|
12 |
+
|
13 |
+
class Init {
|
14 |
+
|
15 |
+
use ModConsumer;
|
16 |
+
|
17 |
+
/**
|
18 |
+
* @return string
|
19 |
+
* @throws \Exception
|
20 |
+
*/
|
21 |
+
public function run() :string {
|
22 |
+
$con = $this->getCon();
|
23 |
+
/** @var Options $integOpts */
|
24 |
+
$integOpts = $con->getModule_Integrations()->getOptions();
|
25 |
+
|
26 |
+
// TODO: Consider have an "error" screen message to show it's not enabled instead?
|
27 |
+
if ( !$integOpts->isEnabledMainWP() ) {
|
28 |
+
throw new \Exception( 'MainWP Extension is not enabled' );
|
29 |
+
}
|
30 |
+
|
31 |
+
$extensionsPage = ( new ExtensionSettingsPage() )->setMod( $this->getMod() );
|
32 |
+
add_filter( 'mainwp_getextensions', function ( $exts ) use ( $extensionsPage ) {
|
33 |
+
$con = $this->getCon();
|
34 |
+
$exts[] = [
|
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->getPluginUrl_Image( 'pluginlogo_col_32x32.png' ),
|
39 |
+
];
|
40 |
+
return $exts;
|
41 |
+
}, 10, 1 );
|
42 |
+
|
43 |
+
$childEnabled = apply_filters( 'mainwp_extension_enabled_check', $con->getRootFile() );
|
44 |
+
$key = $childEnabled[ 'key' ] ?? '';
|
45 |
+
if ( empty( $key ) ) {
|
46 |
+
throw new \Exception( 'No child key provided' );
|
47 |
+
}
|
48 |
+
|
49 |
+
if ( Controller::isMainWPServerVersionSupported() && $con->isPremiumActive() ) {
|
50 |
+
( new SyncHandler() )
|
51 |
+
->setMod( $this->getMod() )
|
52 |
+
->execute();
|
53 |
+
( new UI\ManageSites\SitesListTableHandler() )
|
54 |
+
->setMod( $this->getMod() )
|
55 |
+
->execute();
|
56 |
+
$extensionsPage->execute();
|
57 |
+
|
58 |
+
if ( $this->getMod()->isModuleRequest() && Services::WpGeneral()->isAjax() ) {
|
59 |
+
( new AjaxHandlerMainwp() )->setMod( $this->getMod() );
|
60 |
+
}
|
61 |
+
}
|
62 |
+
|
63 |
+
return $key;
|
64 |
+
}
|
65 |
+
}
|
src/lib/src/Modules/Integrations/Lib/MainWP/Server/UI/BaseRender.php
ADDED
@@ -0,0 +1,37 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php declare( strict_types=1 );
|
2 |
+
|
3 |
+
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Integrations\Lib\MainWP\Server\UI;
|
4 |
+
|
5 |
+
use FernleafSystems\Wordpress\Plugin\Shield\Modules\ModConsumer;
|
6 |
+
|
7 |
+
abstract class BaseRender {
|
8 |
+
|
9 |
+
use ModConsumer;
|
10 |
+
|
11 |
+
public function render() :string {
|
12 |
+
$con = $this->getCon();
|
13 |
+
|
14 |
+
try {
|
15 |
+
$output = $con->getRenderer()
|
16 |
+
->setTemplateEngineTwig()
|
17 |
+
->setTemplate( sprintf( '/integration/mainwp/%s.twig', $this->getTemplateSlug() ) )
|
18 |
+
->setRenderVars( $this->getData() )
|
19 |
+
->render();
|
20 |
+
}
|
21 |
+
catch ( \Exception $e ) {
|
22 |
+
$output = $e->getMessage();
|
23 |
+
}
|
24 |
+
return $output;
|
25 |
+
}
|
26 |
+
|
27 |
+
protected function getData() :array {
|
28 |
+
return [
|
29 |
+
'content' => [
|
30 |
+
],
|
31 |
+
'vars' => [
|
32 |
+
]
|
33 |
+
];
|
34 |
+
}
|
35 |
+
|
36 |
+
abstract protected function getTemplateSlug() :string;
|
37 |
+
}
|
src/lib/src/Modules/Integrations/Lib/MainWP/Server/UI/ExtensionSettingsPage.php
ADDED
@@ -0,0 +1,131 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php declare( strict_types=1 );
|
2 |
+
|
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;
|
9 |
+
use FernleafSystems\Wordpress\Services\Services;
|
10 |
+
|
11 |
+
class ExtensionSettingsPage {
|
12 |
+
|
13 |
+
use ModConsumer;
|
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 |
+
wp_register_style(
|
31 |
+
$handle,
|
32 |
+
$con->getPluginUrl_Css( 'mainwp.css' ),
|
33 |
+
[],
|
34 |
+
$con->getVersion()
|
35 |
+
);
|
36 |
+
wp_enqueue_style( $handle );
|
37 |
+
|
38 |
+
// $handle = 'semantic-ui-datatables-select';
|
39 |
+
// wp_register_script(
|
40 |
+
// $handle,
|
41 |
+
// 'https://cdn.datatables.net/select/1.3.1/js/dataTables.select.min.js',
|
42 |
+
// [ 'semantic-ui-datatables' ],
|
43 |
+
// $con->getVersion(),
|
44 |
+
// true
|
45 |
+
// );
|
46 |
+
// wp_enqueue_script( 'semantic-ui-datatables-select' );
|
47 |
+
// wp_register_style(
|
48 |
+
// $handle,
|
49 |
+
// 'https://cdn.datatables.net/select/1.3.1/css/select.dataTables.min.css',
|
50 |
+
// [ 'semantic-ui-datatables' ],
|
51 |
+
// $con->getVersion()
|
52 |
+
// );
|
53 |
+
// wp_enqueue_style( 'semantic-ui-datatables-select' );
|
54 |
+
}
|
55 |
+
} );
|
56 |
+
}
|
57 |
+
|
58 |
+
/**
|
59 |
+
* @throws \Exception
|
60 |
+
*/
|
61 |
+
public function render() {
|
62 |
+
$con = $this->getCon();
|
63 |
+
$req = Services::Request();
|
64 |
+
|
65 |
+
// Adjust the title at the top of the page so it's not "Wp Simple Firewall"
|
66 |
+
add_filter( 'mainwp_header_title', function () {
|
67 |
+
return $this->getCon()->getHumanName();
|
68 |
+
}, 100, 0 );
|
69 |
+
|
70 |
+
ob_start();
|
71 |
+
do_action( 'mainwp_pageheader_extensions', $this->getCon()->getRootFile() );
|
72 |
+
$mainwpHeader = ob_get_contents();
|
73 |
+
ob_clean();
|
74 |
+
do_action( 'mainwp_pagefooter_extensions', $this->getCon()->getRootFile() );
|
75 |
+
$mainwpFooter = ob_get_clean();
|
76 |
+
|
77 |
+
$currentTab = empty( $req->query( 'tab' ) ) ? 'sites' : $req->query( 'tab' );
|
78 |
+
if ( !$con->isPremiumActive() ) {
|
79 |
+
$pageRenderer = new PageRender\NotShieldPro();
|
80 |
+
}
|
81 |
+
elseif ( $this->serverPluginNeedsUpdate() ) {
|
82 |
+
$pageRenderer = new PageRender\PluginOutOfDate();
|
83 |
+
}
|
84 |
+
elseif ( !Controller::isMainWPServerVersionSupported() ) {
|
85 |
+
$pageRenderer = new PageRender\MwpOutOfDate();
|
86 |
+
}
|
87 |
+
else {
|
88 |
+
switch ( $currentTab ) {
|
89 |
+
case 'sites':
|
90 |
+
$pageRenderer = new PageRender\SitesList();
|
91 |
+
break;
|
92 |
+
default:
|
93 |
+
throw new \Exception( 'Not a supported tab' );
|
94 |
+
}
|
95 |
+
}
|
96 |
+
|
97 |
+
try {
|
98 |
+
echo $this->getMod()
|
99 |
+
->renderTemplate(
|
100 |
+
'/integration/mainwp/page_extension.twig',
|
101 |
+
[
|
102 |
+
'content' => [
|
103 |
+
'mainwp_header' => $mainwpHeader,
|
104 |
+
'mainwp_footer' => $mainwpFooter,
|
105 |
+
'page_inner' => $pageRenderer->setMod( $this->getMod() )->render(),
|
106 |
+
],
|
107 |
+
'vars' => [
|
108 |
+
'submenu' => [
|
109 |
+
[
|
110 |
+
'title' => 'Sites',
|
111 |
+
'href' => add_query_arg( [ 'tab' => 'sites' ], $req->getUri() ),
|
112 |
+
'icon' => 'globe',
|
113 |
+
'active' => $currentTab === 'sites',
|
114 |
+
]
|
115 |
+
],
|
116 |
+
]
|
117 |
+
],
|
118 |
+
true
|
119 |
+
);
|
120 |
+
}
|
121 |
+
catch ( \Exception $e ) {
|
122 |
+
var_dump( $e->getMessage() );
|
123 |
+
}
|
124 |
+
}
|
125 |
+
|
126 |
+
private function serverPluginNeedsUpdate() :bool {
|
127 |
+
return Services::WpPlugins()->isUpdateAvailable(
|
128 |
+
$this->getCon()->getPluginBaseFile()
|
129 |
+
);
|
130 |
+
}
|
131 |
+
}
|
src/lib/src/Modules/Integrations/Lib/MainWP/Server/UI/ManageSites/SitesListTableHandler.php
ADDED
@@ -0,0 +1,95 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php declare( strict_types=1 );
|
2 |
+
|
3 |
+
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Integrations\Lib\MainWP\Server\UI\ManageSites;
|
4 |
+
|
5 |
+
use FernleafSystems\Utilities\Logic\OneTimeExecute;
|
6 |
+
use FernleafSystems\Wordpress\Plugin\Shield\Modules\Integrations\Lib\MainWP\Common\MWPSiteVO;
|
7 |
+
use FernleafSystems\Wordpress\Plugin\Shield\Modules\Integrations\Lib\MainWP\Server\Data\ClientPluginStatus;
|
8 |
+
use FernleafSystems\Wordpress\Plugin\Shield\Modules\Integrations\Lib\MainWP\Server\Data\LoadShieldSyncData;
|
9 |
+
use FernleafSystems\Wordpress\Plugin\Shield\Modules\Integrations\Lib\MainWP\Server\UI\BaseRender;
|
10 |
+
use FernleafSystems\Wordpress\Plugin\Shield\Modules\ModConsumer;
|
11 |
+
use FernleafSystems\Wordpress\Services\Services;
|
12 |
+
|
13 |
+
class SitesListTableHandler extends BaseRender {
|
14 |
+
|
15 |
+
use ModConsumer;
|
16 |
+
use OneTimeExecute;
|
17 |
+
|
18 |
+
/**
|
19 |
+
* @var MWPSiteVO
|
20 |
+
*/
|
21 |
+
private $workingSite;
|
22 |
+
|
23 |
+
protected function run() {
|
24 |
+
add_filter( 'mainwp_sitestable_getcolumns', function ( $columns ) {
|
25 |
+
$columns[ 'shield' ] = 'Shield';
|
26 |
+
return $columns;
|
27 |
+
}, 10, 1 );
|
28 |
+
add_filter( 'mainwp_sitestable_item', function ( array $item ) {
|
29 |
+
$item[ 'shield' ] = $this->renderShieldColumnEntryForItem( $item );
|
30 |
+
return $item;
|
31 |
+
}, 10, 1 );
|
32 |
+
}
|
33 |
+
|
34 |
+
private function renderShieldColumnEntryForItem( array $item ) :string {
|
35 |
+
$this->workingSite = ( new MWPSiteVO() )->applyFromArray( $item );
|
36 |
+
return $this->render();
|
37 |
+
}
|
38 |
+
|
39 |
+
protected function getData() :array {
|
40 |
+
$con = $this->getCon();
|
41 |
+
|
42 |
+
$sync = LoadShieldSyncData::Load( $this->workingSite );
|
43 |
+
$status = ( new ClientPluginStatus() )
|
44 |
+
->setMod( $this->getMod() )
|
45 |
+
->setMwpSite( $this->workingSite )
|
46 |
+
->detect();
|
47 |
+
|
48 |
+
$statusKey = key( $status );
|
49 |
+
$isActive = $statusKey === ClientPluginStatus::ACTIVE;
|
50 |
+
if ( $isActive ) {
|
51 |
+
$issuesCount = array_sum( $sync->modules[ 'hack_protect' ][ 'scan_issues' ] );
|
52 |
+
}
|
53 |
+
else {
|
54 |
+
$issuesCount = 0;
|
55 |
+
}
|
56 |
+
|
57 |
+
return [
|
58 |
+
'flags' => [
|
59 |
+
'is_active' => $isActive,
|
60 |
+
'is_sync_rqd' => $statusKey === ClientPluginStatus::NEED_SYNC,
|
61 |
+
'is_inactive' => $statusKey === ClientPluginStatus::INACTIVE,
|
62 |
+
'is_notpro' => $statusKey === ClientPluginStatus::NOT_PRO,
|
63 |
+
'is_mwpnoton' => $statusKey === ClientPluginStatus::MWP_NOT_ON,
|
64 |
+
'is_version_mismatch' => in_array( $statusKey, [
|
65 |
+
ClientPluginStatus::VERSION_NEWER_THAN_SERVER,
|
66 |
+
ClientPluginStatus::VERSION_OLDER_THAN_SERVER,
|
67 |
+
] ),
|
68 |
+
],
|
69 |
+
'vars' => [
|
70 |
+
'status_key' => $statusKey,
|
71 |
+
'status_name' => current( $status ),
|
72 |
+
'issues_count' => $issuesCount,
|
73 |
+
'version' => $this->getCon()->getVersion()
|
74 |
+
],
|
75 |
+
'hrefs' => [
|
76 |
+
'this_extension' => Services::WpGeneral()
|
77 |
+
->getUrl_AdminPage( $con->mwpVO->official_extension_data[ 'page' ] ),
|
78 |
+
],
|
79 |
+
'strings' => [
|
80 |
+
'tooltip_inactive' => __( "Shield plugin is installed, but not active.", 'wp-simple-firewall' ),
|
81 |
+
'tooltip_notpro' => __( "The Shield plugin on this site doesn't have an active ShieldPRO license.", 'wp-simple-firewall' ),
|
82 |
+
'tooltip_mwpnoton' => __( "Shield's MainWP integration isn't enabled for this site.", 'wp-simple-firewall' ),
|
83 |
+
'tooltip_not_installed' => __( "Shield isn't installed on this site.", 'wp-simple-firewall' ),
|
84 |
+
'tooltip_sync_required' => __( "Sync Required.", 'wp-simple-firewall' ),
|
85 |
+
'tooltip_version_mismatch' => __( "Shield version on site doesn't match this server.", 'wp-simple-firewall' ),
|
86 |
+
'tooltip_please_update' => __( "Please update your Shield plugins to the same versions and re-sync.", 'wp-simple-firewall' ),
|
87 |
+
'tooltip_issues_found' => __( "Issues Found", 'wp-simple-firewall' ),
|
88 |
+
]
|
89 |
+
];
|
90 |
+
}
|
91 |
+
|
92 |
+
protected function getTemplateSlug() :string {
|
93 |
+
return 'tables/manage_sites_col';
|
94 |
+
}
|
95 |
+
}
|
src/lib/src/Modules/Integrations/Lib/MainWP/Server/UI/PageRender/MwpOutOfDate.php
ADDED
@@ -0,0 +1,30 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php declare( strict_types=1 );
|
2 |
+
|
3 |
+
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Integrations\Lib\MainWP\Server\UI\PageRender;
|
4 |
+
|
5 |
+
use FernleafSystems\Wordpress\Plugin\Shield\Modules\Integrations\Lib\MainWP\Controller;
|
6 |
+
use FernleafSystems\Wordpress\Plugin\Shield\Modules\Integrations\Lib\MainWP\Server\UI\BaseRender;
|
7 |
+
use FernleafSystems\Wordpress\Services\Services;
|
8 |
+
|
9 |
+
class MwpOutOfDate extends BaseRender {
|
10 |
+
|
11 |
+
protected function getData() :array {
|
12 |
+
return [
|
13 |
+
'strings' => [
|
14 |
+
'update' => __( "The MainWP Security plugin doesn't meet Shield's minimum requirements." ),
|
15 |
+
'min_version' => __( 'Minimum required MainWP server version' ),
|
16 |
+
'go_here' => __( 'Go to WordPress Updates' ),
|
17 |
+
],
|
18 |
+
'hrefs' => [
|
19 |
+
'update' => Services::WpGeneral()->getAdminUrl_Updates()
|
20 |
+
],
|
21 |
+
'vars' => [
|
22 |
+
'min_version' => Controller::MIN_VERSION_MAINWP
|
23 |
+
],
|
24 |
+
];
|
25 |
+
}
|
26 |
+
|
27 |
+
protected function getTemplateSlug() :string {
|
28 |
+
return 'pages/mwp_outofdate';
|
29 |
+
}
|
30 |
+
}
|
src/lib/src/Modules/Integrations/Lib/MainWP/Server/UI/PageRender/NotShieldPro.php
ADDED
@@ -0,0 +1,24 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php declare( strict_types=1 );
|
2 |
+
|
3 |
+
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Integrations\Lib\MainWP\Server\UI\PageRender;
|
4 |
+
|
5 |
+
use FernleafSystems\Wordpress\Plugin\Shield\Modules\Integrations\Lib\MainWP\Server\UI\BaseRender;
|
6 |
+
|
7 |
+
class NotShieldPro extends BaseRender {
|
8 |
+
|
9 |
+
protected function getData() :array {
|
10 |
+
return [
|
11 |
+
'strings' => [
|
12 |
+
'not_pro' => __( "Sorry, the MainWP server integration is available only for ShieldPRO clients." ),
|
13 |
+
'go_pro' => __( 'Upgrade To ShieldPRO' ),
|
14 |
+
],
|
15 |
+
'hrefs' => [
|
16 |
+
'go_pro' => 'https://shsec.io/mainwpservergopro'
|
17 |
+
],
|
18 |
+
];
|
19 |
+
}
|
20 |
+
|
21 |
+
protected function getTemplateSlug() :string {
|
22 |
+
return 'pages/mwp_for_pro';
|
23 |
+
}
|
24 |
+
}
|
src/lib/src/Modules/Integrations/Lib/MainWP/Server/UI/PageRender/PluginOutOfDate.php
ADDED
@@ -0,0 +1,25 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php declare( strict_types=1 );
|
2 |
+
|
3 |
+
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Integrations\Lib\MainWP\Server\UI\PageRender;
|
4 |
+
|
5 |
+
use FernleafSystems\Wordpress\Plugin\Shield\Modules\Integrations\Lib\MainWP\Server\UI\BaseRender;
|
6 |
+
use FernleafSystems\Wordpress\Services\Services;
|
7 |
+
|
8 |
+
class PluginOutOfDate extends BaseRender {
|
9 |
+
|
10 |
+
protected function getData() :array {
|
11 |
+
return [
|
12 |
+
'strings' => [
|
13 |
+
'update' => __( 'The Shield Security plugin on this site needs to be upgraded.' ),
|
14 |
+
'go_here' => __( 'Go to WordPress Updates' )
|
15 |
+
],
|
16 |
+
'hrefs' => [
|
17 |
+
'update' => Services::WpGeneral()->getAdminUrl_Updates()
|
18 |
+
],
|
19 |
+
];
|
20 |
+
}
|
21 |
+
|
22 |
+
protected function getTemplateSlug() :string {
|
23 |
+
return 'pages/shield_outofdate';
|
24 |
+
}
|
25 |
+
}
|
src/lib/src/Modules/Integrations/Lib/MainWP/Server/UI/PageRender/SitesList.php
ADDED
@@ -0,0 +1,138 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php declare( strict_types=1 );
|
2 |
+
|
3 |
+
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Integrations\Lib\MainWP\Server\UI\PageRender;
|
4 |
+
|
5 |
+
use FernleafSystems\Wordpress\Plugin\Shield\Modules\Integrations\Lib\MainWP\Common\MWPSiteVO;
|
6 |
+
use FernleafSystems\Wordpress\Plugin\Shield\Modules\Integrations\Lib\MainWP\Server\Data\ClientPluginStatus;
|
7 |
+
use FernleafSystems\Wordpress\Plugin\Shield\Modules\Integrations\Lib\MainWP\Server\Data\LoadShieldSyncData;
|
8 |
+
use FernleafSystems\Wordpress\Plugin\Shield\Modules\Integrations\Lib\MainWP\Server\UI\BaseRender;
|
9 |
+
use FernleafSystems\Wordpress\Services\Services;
|
10 |
+
|
11 |
+
class SitesList extends BaseRender {
|
12 |
+
|
13 |
+
protected function getData() :array {
|
14 |
+
$mod = $this->getMod();
|
15 |
+
$mwp = $this->getCon()->mwpVO;
|
16 |
+
$WP = Services::WpGeneral();
|
17 |
+
$req = Services::Request();
|
18 |
+
|
19 |
+
$statsHead = [
|
20 |
+
'connected' => 0,
|
21 |
+
'disconnected' => 0,
|
22 |
+
'with_issues' => 0,
|
23 |
+
'needs_update' => 0,
|
24 |
+
];
|
25 |
+
$sites = apply_filters( 'mainwp_getsites', $mwp->child_file, $mwp->child_key );
|
26 |
+
foreach ( $sites as &$site ) {
|
27 |
+
$mwpSite = MWPSiteVO::LoadByID( (int)$site[ 'id' ] );
|
28 |
+
$sync = LoadShieldSyncData::Load( $mwpSite );
|
29 |
+
$meta = $sync->meta;
|
30 |
+
|
31 |
+
$shd = $sync->getRawDataAsArray();
|
32 |
+
$status = ( new ClientPluginStatus() )
|
33 |
+
->setMod( $this->getMod() )
|
34 |
+
->setMwpSite( $mwpSite )
|
35 |
+
->detect();
|
36 |
+
$shd[ 'status_key' ] = key( $status );
|
37 |
+
$shd[ 'status' ] = current( $status );
|
38 |
+
|
39 |
+
$shd[ 'is_active' ] = $shd[ 'status_key' ] === ClientPluginStatus::ACTIVE;
|
40 |
+
$shd[ 'is_inactive' ] = $shd[ 'status_key' ] === ClientPluginStatus::INACTIVE;
|
41 |
+
$shd[ 'is_notinstalled' ] = $shd[ 'status_key' ] === ClientPluginStatus::NOT_INSTALLED;
|
42 |
+
$shd[ 'is_notpro' ] = $shd[ 'status_key' ] === ClientPluginStatus::NOT_PRO;
|
43 |
+
$shd[ 'is_mwpnoton' ] = $shd[ 'status_key' ] === ClientPluginStatus::MWP_NOT_ON;
|
44 |
+
$shd[ 'is_sync_rqd' ] = $shd[ 'status_key' ] === ClientPluginStatus::NEED_SYNC;
|
45 |
+
$shd[ 'is_version_mismatch' ] = in_array( $shd[ 'status_key' ], [
|
46 |
+
ClientPluginStatus::VERSION_NEWER_THAN_SERVER,
|
47 |
+
ClientPluginStatus::VERSION_OLDER_THAN_SERVER,
|
48 |
+
] );
|
49 |
+
|
50 |
+
if ( $shd[ 'is_active' ] ) {
|
51 |
+
|
52 |
+
$statsHead[ 'connected' ]++;
|
53 |
+
$shd[ 'sync_at_text' ] = $WP->getTimeStringForDisplay( $meta->sync_at );
|
54 |
+
$shd[ 'sync_at_diff' ] = $req->carbon()->setTimestamp( $meta->sync_at )->diffForHumans();
|
55 |
+
|
56 |
+
if ( empty( $sync->modules[ 'hack_protect' ][ 'scan_issues' ] ) ) {
|
57 |
+
$shd[ 'issues' ] = __( 'No Issues', 'wp-simple-firewall' );
|
58 |
+
$shd[ 'has_issues' ] = false;
|
59 |
+
}
|
60 |
+
else {
|
61 |
+
$shd[ 'has_issues' ] = true;
|
62 |
+
$shd[ 'issues' ] = array_sum( $sync->modules[ 'hack_protect' ][ 'scan_issues' ] );
|
63 |
+
$statsHead[ 'with_issues' ]++;
|
64 |
+
}
|
65 |
+
|
66 |
+
$shd[ 'issues_href' ] = add_query_arg(
|
67 |
+
[
|
68 |
+
'newWindow' => 'yes',
|
69 |
+
'websiteid' => $site[ 'id' ],
|
70 |
+
'location' => base64_encode( $this->getScanPageUrlPart() )
|
71 |
+
],
|
72 |
+
Services::WpGeneral()->getUrl_AdminPage( 'SiteOpen' )
|
73 |
+
);
|
74 |
+
}
|
75 |
+
else {
|
76 |
+
$statsHead[ 'disconnected' ]++;
|
77 |
+
}
|
78 |
+
|
79 |
+
$statsHead[ 'needs_update' ] += $meta->has_update ? 1 : 0;
|
80 |
+
|
81 |
+
$site[ 'shield' ] = $shd;
|
82 |
+
}
|
83 |
+
|
84 |
+
return [
|
85 |
+
'vars' => [
|
86 |
+
'sites' => $sites,
|
87 |
+
'stats_head' => $statsHead,
|
88 |
+
],
|
89 |
+
'ajax' => [
|
90 |
+
'mwp_sh_site_action' => $mod->getAjaxActionData( 'mwp_sh_site_action', true ),
|
91 |
+
'mwp_sh_ext_table' => $mod->getAjaxActionData( 'mwp_sh_ext_table', true ),
|
92 |
+
],
|
93 |
+
'strings' => [
|
94 |
+
'actions' => __( 'Actions', 'wp-simple-firewall' ),
|
95 |
+
'site' => __( 'Site', 'wp-simple-firewall' ),
|
96 |
+
'url' => __( 'URL', 'wp-simple-firewall' ),
|
97 |
+
'issues' => __( 'Issues', 'wp-simple-firewall' ),
|
98 |
+
'status' => __( 'Status', 'wp-simple-firewall' ),
|
99 |
+
'last_sync' => __( 'Last Sync', 'wp-simple-firewall' ),
|
100 |
+
'last_scan' => __( 'Last Scan', 'wp-simple-firewall' ),
|
101 |
+
'version' => __( 'Version', 'wp-simple-firewall' ),
|
102 |
+
'connected' => __( 'Connected', 'wp-simple-firewall' ),
|
103 |
+
'disconnected' => __( 'Disconnected', 'wp-simple-firewall' ),
|
104 |
+
'with_issues' => __( 'With Issues', 'wp-simple-firewall' ),
|
105 |
+
'needs_update' => __( 'Needs Update', 'wp-simple-firewall' ),
|
106 |
+
'st_inactive' => __( 'Shield Security plugin is installed but not activated.', 'wp-simple-firewall' ),
|
107 |
+
'st_notinstalled' => __( "Shield Security plugin not detected in last sync.", 'wp-simple-firewall' ),
|
108 |
+
'st_notpro' => __( "ShieldPRO isn't activated on this site.", 'wp-simple-firewall' ),
|
109 |
+
'st_mwpnoton' => __( "Shield's MainWP integration isn't enabled for this site.", 'wp-simple-firewall' ),
|
110 |
+
'st_sync_rqd' => __( 'Shield Security plugin needs to sync.', 'wp-simple-firewall' ),
|
111 |
+
'st_version_mismatch' => __( 'Shield Security plugin versions are out of sync.', 'wp-simple-firewall' ),
|
112 |
+
'st_unknown' => __( "Couldn't determine Shield plugin status.", 'wp-simple-firewall' ),
|
113 |
+
'act_sync' => __( 'Sync Shield', 'wp-simple-firewall' ),
|
114 |
+
'act_activate' => __( 'Activate Shield', 'wp-simple-firewall' ),
|
115 |
+
'act_align' => __( 'Align Shield', 'wp-simple-firewall' ),
|
116 |
+
'act_deactivate' => __( 'Deactivate Shield', 'wp-simple-firewall' ),
|
117 |
+
'act_install' => __( 'Install Shield', 'wp-simple-firewall' ),
|
118 |
+
'act_upgrade' => __( 'Upgrade Shield', 'wp-simple-firewall' ),
|
119 |
+
'act_uninstall' => __( 'Uninstall Shield', 'wp-simple-firewall' ),
|
120 |
+
'act_license' => __( 'Check ShieldPRO License', 'wp-simple-firewall' ),
|
121 |
+
'act_mwp' => __( 'Switch-On MainWP Integration', 'wp-simple-firewall' ),
|
122 |
+
]
|
123 |
+
];
|
124 |
+
}
|
125 |
+
|
126 |
+
private function getScanPageUrlPart() :string {
|
127 |
+
$WP = Services::WpGeneral();
|
128 |
+
return str_replace(
|
129 |
+
$WP->getAdminUrl(),
|
130 |
+
'',
|
131 |
+
$this->getCon()->getModule_Insights()->getUrl_SubInsightsPage( 'scans' )
|
132 |
+
);
|
133 |
+
}
|
134 |
+
|
135 |
+
protected function getTemplateSlug() :string {
|
136 |
+
return 'pages/sites';
|
137 |
+
}
|
138 |
+
}
|
src/lib/src/Modules/Integrations/ModCon.php
ADDED
@@ -0,0 +1,22 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php declare( strict_types=1 );
|
2 |
+
|
3 |
+
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Integrations;
|
4 |
+
|
5 |
+
use FernleafSystems\Wordpress\Plugin\Shield;
|
6 |
+
use FernleafSystems\Wordpress\Plugin\Shield\Modules\BaseShield;
|
7 |
+
|
8 |
+
class ModCon extends BaseShield\ModCon {
|
9 |
+
|
10 |
+
/**
|
11 |
+
* @var Lib\MainWP\Controller
|
12 |
+
*/
|
13 |
+
private $mwp;
|
14 |
+
|
15 |
+
public function getControllerMWP() :Lib\MainWP\Controller {
|
16 |
+
if ( empty( $this->mwp ) ) {
|
17 |
+
$this->mwp = ( new Lib\MainWP\Controller() )
|
18 |
+
->setMod( $this );
|
19 |
+
}
|
20 |
+
return $this->mwp;
|
21 |
+
}
|
22 |
+
}
|
src/lib/src/Modules/Integrations/Options.php
ADDED
@@ -0,0 +1,12 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php declare( strict_types=1 );
|
2 |
+
|
3 |
+
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Integrations;
|
4 |
+
|
5 |
+
use FernleafSystems\Wordpress\Plugin\Shield\Modules\BaseShield;
|
6 |
+
|
7 |
+
class Options extends BaseShield\Options {
|
8 |
+
|
9 |
+
public function isEnabledMainWP() :bool {
|
10 |
+
return $this->isOpt( 'enable_mainwp', 'Y' );
|
11 |
+
}
|
12 |
+
}
|
src/lib/src/Modules/Integrations/Processor.php
ADDED
@@ -0,0 +1,15 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php declare( strict_types=1 );
|
2 |
+
|
3 |
+
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Integrations;
|
4 |
+
|
5 |
+
use FernleafSystems\Wordpress\Plugin\Shield\Modules\BaseShield;
|
6 |
+
|
7 |
+
class Processor extends BaseShield\Processor {
|
8 |
+
|
9 |
+
protected function run() {
|
10 |
+
$this->getCon()
|
11 |
+
->getModule_Integrations()
|
12 |
+
->getControllerMWP()
|
13 |
+
->execute();
|
14 |
+
}
|
15 |
+
}
|
src/lib/src/Modules/Integrations/Strings.php
ADDED
@@ -0,0 +1,70 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php declare( strict_types=1 );
|
2 |
+
|
3 |
+
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Integrations;
|
4 |
+
|
5 |
+
use FernleafSystems\Wordpress\Plugin\Shield\Modules\Base;
|
6 |
+
|
7 |
+
class Strings extends Base\Strings {
|
8 |
+
|
9 |
+
/**
|
10 |
+
* @param string $section
|
11 |
+
* @return array
|
12 |
+
* @throws \Exception
|
13 |
+
*/
|
14 |
+
public function getSectionStrings( string $section ) :array {
|
15 |
+
|
16 |
+
switch ( $section ) {
|
17 |
+
|
18 |
+
case 'section_integrations':
|
19 |
+
$titleShort = __( 'Integrations', 'wp-simple-firewall' );
|
20 |
+
$title = __( 'Built-In Shield Integrations', 'wp-simple-firewall' );
|
21 |
+
$summary = [
|
22 |
+
sprintf( '%s - %s', __( 'Summary', 'wp-simple-firewall' ),
|
23 |
+
__( "Shield can automatically integrate with 3rd party plugins.", 'wp-simple-firewall' ) ),
|
24 |
+
sprintf( '%s - %s', __( 'Recommendation', 'wp-simple-firewall' ),
|
25 |
+
__( "Only enable the integrations you require.", 'wp-simple-firewall' ) ),
|
26 |
+
];
|
27 |
+
break;
|
28 |
+
|
29 |
+
default:
|
30 |
+
return parent::getSectionStrings( $section );
|
31 |
+
}
|
32 |
+
|
33 |
+
return [
|
34 |
+
'title' => $title,
|
35 |
+
'title_short' => $titleShort,
|
36 |
+
'summary' => is_array( $summary ) ? $summary : [],
|
37 |
+
];
|
38 |
+
}
|
39 |
+
|
40 |
+
/**
|
41 |
+
* @param string $key
|
42 |
+
* @return array
|
43 |
+
* @throws \Exception
|
44 |
+
*/
|
45 |
+
public function getOptionStrings( string $key ) :array {
|
46 |
+
|
47 |
+
switch ( $key ) {
|
48 |
+
|
49 |
+
case 'enable_mainwp' :
|
50 |
+
$name = __( 'MainWP Integration', 'wp-simple-firewall' );
|
51 |
+
$summary = __( "Turn-On Shield's Built-In Extension For MainWP Server And Client Installations", 'wp-simple-firewall' );
|
52 |
+
$desc = [
|
53 |
+
__( 'Easily integrate Shield Security to help you manage your site security from within MainWP.', 'wp-simple-firewall' ),
|
54 |
+
__( "You don't need to install a separate extension for MainWP.", 'wp-simple-firewall' ),
|
55 |
+
sprintf( '%s: %s', __( 'Important', 'wp-simple-firewall' ),
|
56 |
+
__( "If this is a MainWP client site, you should add your MainWP Admin Server's IP address to your IP bypass list.", 'wp-simple-firewall' ) )
|
57 |
+
];
|
58 |
+
break;
|
59 |
+
|
60 |
+
default:
|
61 |
+
return parent::getOptionStrings( $key );
|
62 |
+
}
|
63 |
+
|
64 |
+
return [
|
65 |
+
'name' => $name,
|
66 |
+
'summary' => $summary,
|
67 |
+
'description' => $desc,
|
68 |
+
];
|
69 |
+
}
|
70 |
+
}
|
src/lib/src/Modules/License/AdminNotices.php
CHANGED
@@ -3,29 +3,26 @@
|
|
3 |
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\License;
|
4 |
|
5 |
use FernleafSystems\Wordpress\Plugin\Shield;
|
|
|
6 |
|
7 |
class AdminNotices extends Shield\Modules\Base\AdminNotices {
|
8 |
|
9 |
/**
|
10 |
-
* @
|
11 |
-
* @throws \Exception
|
12 |
*/
|
13 |
-
protected function processNotice( $
|
14 |
-
switch ( $
|
15 |
case 'wphashes-token-fail':
|
16 |
-
$this->buildNotice_WpHashesTokenFailure( $
|
17 |
break;
|
18 |
default:
|
19 |
-
parent::processNotice( $
|
20 |
break;
|
21 |
}
|
22 |
}
|
23 |
|
24 |
-
|
25 |
-
|
26 |
-
*/
|
27 |
-
private function buildNotice_WpHashesTokenFailure( $oNotice ) {
|
28 |
-
$oNotice->render_data = [
|
29 |
'notice_attributes' => [],
|
30 |
'strings' => [
|
31 |
'title' => sprintf( '%s: %s', __( 'Warning', 'wp-simple-firewall' ),
|
@@ -46,25 +43,21 @@ class AdminNotices extends Shield\Modules\Base\AdminNotices {
|
|
46 |
];
|
47 |
}
|
48 |
|
49 |
-
|
50 |
-
|
51 |
-
|
52 |
-
*/
|
53 |
-
protected function isDisplayNeeded( $oNotice ) {
|
54 |
-
/** @var \ICWP_WPSF_FeatureHandler_License $oMod */
|
55 |
-
$oMod = $this->getMod();
|
56 |
|
57 |
-
switch ( $
|
58 |
|
59 |
case 'wphashes-token-fail':
|
60 |
-
$
|
61 |
-
|
62 |
break;
|
63 |
|
64 |
default:
|
65 |
-
$
|
66 |
break;
|
67 |
}
|
68 |
-
return $
|
69 |
}
|
70 |
}
|
3 |
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\License;
|
4 |
|
5 |
use FernleafSystems\Wordpress\Plugin\Shield;
|
6 |
+
use FernleafSystems\Wordpress\Plugin\Shield\Utilities\AdminNotices\NoticeVO;
|
7 |
|
8 |
class AdminNotices extends Shield\Modules\Base\AdminNotices {
|
9 |
|
10 |
/**
|
11 |
+
* @inheritDoc
|
|
|
12 |
*/
|
13 |
+
protected function processNotice( NoticeVO $notice ) {
|
14 |
+
switch ( $notice->id ) {
|
15 |
case 'wphashes-token-fail':
|
16 |
+
$this->buildNotice_WpHashesTokenFailure( $notice );
|
17 |
break;
|
18 |
default:
|
19 |
+
parent::processNotice( $notice );
|
20 |
break;
|
21 |
}
|
22 |
}
|
23 |
|
24 |
+
private function buildNotice_WpHashesTokenFailure( NoticeVO $notice ) {
|
25 |
+
$notice->render_data = [
|
|
|
|
|
|
|
26 |
'notice_attributes' => [],
|
27 |
'strings' => [
|
28 |
'title' => sprintf( '%s: %s', __( 'Warning', 'wp-simple-firewall' ),
|
43 |
];
|
44 |
}
|
45 |
|
46 |
+
protected function isDisplayNeeded( NoticeVO $notice ) :bool {
|
47 |
+
/** @var ModCon $mod */
|
48 |
+
$mod = $this->getMod();
|
|
|
|
|
|
|
|
|
49 |
|
50 |
+
switch ( $notice->id ) {
|
51 |
|
52 |
case 'wphashes-token-fail':
|
53 |
+
$needed = $this->getCon()->isPremiumActive()
|
54 |
+
&& !$mod->getWpHashesTokenManager()->hasToken();
|
55 |
break;
|
56 |
|
57 |
default:
|
58 |
+
$needed = parent::isDisplayNeeded( $notice );
|
59 |
break;
|
60 |
}
|
61 |
+
return $needed;
|
62 |
}
|
63 |
}
|
src/lib/src/Modules/License/AjaxHandler.php
CHANGED
@@ -6,7 +6,7 @@ use FernleafSystems\Wordpress\Plugin\Shield;
|
|
6 |
use FernleafSystems\Wordpress\Services\Services;
|
7 |
use FernleafSystems\Wordpress\Services\Utilities\Licenses\Keyless;
|
8 |
|
9 |
-
class AjaxHandler extends Shield\Modules\
|
10 |
|
11 |
protected function processAjaxAction( string $action ) :array {
|
12 |
|
@@ -56,9 +56,9 @@ class AjaxHandler extends Shield\Modules\Base\AjaxHandlerShield {
|
|
56 |
* @return array
|
57 |
*/
|
58 |
private function ajaxExec_LicenseHandling() {
|
59 |
-
/** @var
|
60 |
-
$
|
61 |
-
$sHandler = $
|
62 |
|
63 |
$bSuccess = false;
|
64 |
$sMessage = 'Unsupported license action';
|
6 |
use FernleafSystems\Wordpress\Services\Services;
|
7 |
use FernleafSystems\Wordpress\Services\Utilities\Licenses\Keyless;
|
8 |
|
9 |
+
class AjaxHandler extends Shield\Modules\BaseShield\AjaxHandler {
|
10 |
|
11 |
protected function processAjaxAction( string $action ) :array {
|
12 |
|
56 |
* @return array
|
57 |
*/
|
58 |
private function ajaxExec_LicenseHandling() {
|
59 |
+
/** @var ModCon $mod */
|
60 |
+
$mod = $this->getMod();
|
61 |
+
$sHandler = $mod->getLicenseHandler();
|
62 |
|
63 |
$bSuccess = false;
|
64 |
$sMessage = 'Unsupported license action';
|
src/lib/src/Modules/License/Lib/LicenseEmails.php
CHANGED
@@ -2,6 +2,7 @@
|
|
2 |
|
3 |
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\License\Lib;
|
4 |
|
|
|
5 |
use FernleafSystems\Wordpress\Plugin\Shield\Modules\ModConsumer;
|
6 |
use FernleafSystems\Wordpress\Services\Services;
|
7 |
|
@@ -10,57 +11,57 @@ class LicenseEmails {
|
|
10 |
use ModConsumer;
|
11 |
|
12 |
public function sendLicenseWarningEmail() {
|
13 |
-
/** @var
|
14 |
-
$
|
15 |
-
$
|
16 |
|
17 |
$bCanSend = Services::Request()
|
18 |
->carbon()
|
19 |
-
->subDay( 1 )->timestamp > $
|
20 |
|
21 |
if ( $bCanSend ) {
|
22 |
-
$
|
23 |
-
$
|
24 |
|
25 |
$aMessage = [
|
26 |
__( 'Attempts to verify Shield Pro license has just failed.', 'wp-simple-firewall' ),
|
27 |
-
sprintf( __( 'Please check your license on-site: %s', 'wp-simple-firewall' ), $
|
28 |
sprintf( __( 'If this problem persists, please contact support: %s', 'wp-simple-firewall' ), 'https://support.onedollarplugin.com/' )
|
29 |
];
|
30 |
-
$
|
31 |
-
|
32 |
-
|
33 |
-
|
34 |
-
|
35 |
-
|
36 |
$this->getCon()->fireEvent( 'lic_fail_email' );
|
37 |
}
|
38 |
}
|
39 |
|
40 |
public function sendLicenseDeactivatedEmail() {
|
41 |
-
/** @var
|
42 |
-
$
|
43 |
-
$
|
44 |
|
45 |
$bCanSend = Services::Request()
|
46 |
->carbon()
|
47 |
-
->subDay( 1 )->timestamp > $
|
48 |
|
49 |
if ( $bCanSend ) {
|
50 |
-
$
|
51 |
-
$
|
52 |
|
53 |
$aMessage = [
|
54 |
__( 'All attempts to verify Shield Pro license have failed.', 'wp-simple-firewall' ),
|
55 |
-
sprintf( __( 'Please check your license on-site: %s', 'wp-simple-firewall' ), $
|
56 |
sprintf( __( 'If this problem persists, please contact support: %s', 'wp-simple-firewall' ), 'https://support.onedollarplugin.com/' )
|
57 |
];
|
58 |
-
$
|
59 |
-
|
60 |
-
|
61 |
-
|
62 |
-
|
63 |
-
|
64 |
}
|
65 |
}
|
66 |
}
|
2 |
|
3 |
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\License\Lib;
|
4 |
|
5 |
+
use FernleafSystems\Wordpress\Plugin\Shield\Modules\License\ModCon;
|
6 |
use FernleafSystems\Wordpress\Plugin\Shield\Modules\ModConsumer;
|
7 |
use FernleafSystems\Wordpress\Services\Services;
|
8 |
|
11 |
use ModConsumer;
|
12 |
|
13 |
public function sendLicenseWarningEmail() {
|
14 |
+
/** @var ModCon $mod */
|
15 |
+
$mod = $this->getMod();
|
16 |
+
$opts = $this->getOptions();
|
17 |
|
18 |
$bCanSend = Services::Request()
|
19 |
->carbon()
|
20 |
+
->subDay( 1 )->timestamp > $opts->getOpt( 'last_warning_email_sent_at' );
|
21 |
|
22 |
if ( $bCanSend ) {
|
23 |
+
$opts->setOptAt( 'last_warning_email_sent_at' );
|
24 |
+
$mod->saveModOptions();
|
25 |
|
26 |
$aMessage = [
|
27 |
__( 'Attempts to verify Shield Pro license has just failed.', 'wp-simple-firewall' ),
|
28 |
+
sprintf( __( 'Please check your license on-site: %s', 'wp-simple-firewall' ), $mod->getUrl_AdminPage() ),
|
29 |
sprintf( __( 'If this problem persists, please contact support: %s', 'wp-simple-firewall' ), 'https://support.onedollarplugin.com/' )
|
30 |
];
|
31 |
+
$mod->getEmailProcessor()
|
32 |
+
->sendEmailWithWrap(
|
33 |
+
$mod->getPluginReportEmail(),
|
34 |
+
'Pro License Check Has Failed',
|
35 |
+
$aMessage
|
36 |
+
);
|
37 |
$this->getCon()->fireEvent( 'lic_fail_email' );
|
38 |
}
|
39 |
}
|
40 |
|
41 |
public function sendLicenseDeactivatedEmail() {
|
42 |
+
/** @var ModCon $mod */
|
43 |
+
$mod = $this->getMod();
|
44 |
+
$opts = $this->getOptions();
|
45 |
|
46 |
$bCanSend = Services::Request()
|
47 |
->carbon()
|
48 |
+
->subDay( 1 )->timestamp > $opts->getOpt( 'last_deactivated_email_sent_at' );
|
49 |
|
50 |
if ( $bCanSend ) {
|
51 |
+
$opts->setOptAt( 'last_deactivated_email_sent_at' );
|
52 |
+
$mod->saveModOptions();
|
53 |
|
54 |
$aMessage = [
|
55 |
__( 'All attempts to verify Shield Pro license have failed.', 'wp-simple-firewall' ),
|
56 |
+
sprintf( __( 'Please check your license on-site: %s', 'wp-simple-firewall' ), $mod->getUrl_AdminPage() ),
|
57 |
sprintf( __( 'If this problem persists, please contact support: %s', 'wp-simple-firewall' ), 'https://support.onedollarplugin.com/' )
|
58 |
];
|
59 |
+
$mod->getEmailProcessor()
|
60 |
+
->sendEmailWithWrap(
|
61 |
+
$mod->getPluginReportEmail(),
|
62 |
+
'[Action May Be Required] Pro License Has Been Deactivated',
|
63 |
+
$aMessage
|
64 |
+
);
|
65 |
}
|
66 |
}
|
67 |
}
|
src/lib/src/Modules/License/Lib/LicenseHandler.php
CHANGED
@@ -5,6 +5,7 @@ namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\License\Lib;
|
|
5 |
use FernleafSystems\Utilities\Logic\OneTimeExecute;
|
6 |
use FernleafSystems\Wordpress\Plugin\Shield\License\EddLicenseVO;
|
7 |
use FernleafSystems\Wordpress\Plugin\Shield\Modules;
|
|
|
8 |
use FernleafSystems\Wordpress\Plugin\Shield\ShieldNetApi\HandshakingNonce;
|
9 |
use FernleafSystems\Wordpress\Services\Services;
|
10 |
|
@@ -43,10 +44,10 @@ class LicenseHandler {
|
|
43 |
|
44 |
// performs the license check on-demand
|
45 |
add_action( $oCon->prefix( 'adhoc_cron_license_check' ), function () {
|
46 |
-
/** @var
|
47 |
-
$
|
48 |
try {
|
49 |
-
$
|
50 |
}
|
51 |
catch ( \Exception $oE ) {
|
52 |
}
|
@@ -126,16 +127,16 @@ class LicenseHandler {
|
|
126 |
* @return int
|
127 |
*/
|
128 |
public function getRegistrationExpiresAt() {
|
129 |
-
/** @var
|
130 |
-
$
|
131 |
-
$
|
132 |
|
133 |
-
$nVerifiedExpiredDays = $
|
134 |
-
+ $
|
135 |
|
136 |
-
$oLic = $
|
137 |
return (int)min(
|
138 |
-
$oLic->getExpiresAt() + $
|
139 |
$oLic->last_verified_at + $nVerifiedExpiredDays*DAY_IN_SECONDS
|
140 |
);
|
141 |
}
|
5 |
use FernleafSystems\Utilities\Logic\OneTimeExecute;
|
6 |
use FernleafSystems\Wordpress\Plugin\Shield\License\EddLicenseVO;
|
7 |
use FernleafSystems\Wordpress\Plugin\Shield\Modules;
|
8 |
+
use FernleafSystems\Wordpress\Plugin\Shield\Modules\License\ModCon;
|
9 |
use FernleafSystems\Wordpress\Plugin\Shield\ShieldNetApi\HandshakingNonce;
|
10 |
use FernleafSystems\Wordpress\Services\Services;
|
11 |
|
44 |
|
45 |
// performs the license check on-demand
|
46 |
add_action( $oCon->prefix( 'adhoc_cron_license_check' ), function () {
|
47 |
+
/** @var ModCon $mod */
|
48 |
+
$mod = $this->getMod();
|
49 |
try {
|
50 |
+
$mod->getLicenseHandler()->verify( true );
|
51 |
}
|
52 |
catch ( \Exception $oE ) {
|
53 |
}
|
127 |
* @return int
|
128 |
*/
|
129 |
public function getRegistrationExpiresAt() {
|
130 |
+
/** @var ModCon $mod */
|
131 |
+
$mod = $this->getMod();
|
132 |
+
$opts = $this->getOptions();
|
133 |
|
134 |
+
$nVerifiedExpiredDays = $opts->getDef( 'lic_verify_expire_days' )
|
135 |
+
+ $opts->getDef( 'lic_verify_expire_grace_days' );
|
136 |
|
137 |
+
$oLic = $mod->getLicenseHandler()->getLicense();
|
138 |
return (int)min(
|
139 |
+
$oLic->getExpiresAt() + $opts->getDef( 'lic_verify_expire_grace_days' )*DAY_IN_SECONDS,
|
140 |
$oLic->last_verified_at + $nVerifiedExpiredDays*DAY_IN_SECONDS
|
141 |
);
|
142 |
}
|
src/lib/src/Modules/License/Lib/Verify.php
CHANGED
@@ -14,19 +14,19 @@ class Verify {
|
|
14 |
* @throws \Exception
|
15 |
*/
|
16 |
public function run() {
|
17 |
-
$
|
18 |
-
/** @var \
|
19 |
-
$
|
20 |
-
/** @var License\Options $
|
21 |
-
$
|
22 |
-
$oHandler = $
|
23 |
|
24 |
$this->preVerify();
|
25 |
|
26 |
$oExisting = $oHandler->getLicense();
|
27 |
|
28 |
$oLookupLicense = ( new LookupRequest() )
|
29 |
-
->setMod( $
|
30 |
->lookup();
|
31 |
|
32 |
$bSuccessfulApiRequest = false;
|
@@ -36,11 +36,11 @@ class Verify {
|
|
36 |
$oExisting = $oLookupLicense;
|
37 |
$oExisting->updateLastVerifiedAt( true );
|
38 |
if ( !$oHandler->isActive() ) {
|
39 |
-
$
|
40 |
}
|
41 |
-
$
|
42 |
-
$
|
43 |
-
$
|
44 |
}
|
45 |
elseif ( $oLookupLicense->isReady() ) {
|
46 |
$bSuccessfulApiRequest = true;
|
@@ -50,7 +50,7 @@ class Verify {
|
|
50 |
}
|
51 |
elseif ( $oExisting->isReady() ) { // Has a stored license but license HTTP request failed
|
52 |
|
53 |
-
$
|
54 |
__( 'The most recent request to verify the site license encountered a problem.', 'wp-simple-firewall' )
|
55 |
] );
|
56 |
|
@@ -66,7 +66,7 @@ class Verify {
|
|
66 |
* We don't remove the license yet, but we warn the user
|
67 |
*/
|
68 |
( new LicenseEmails() )
|
69 |
-
->setMod( $
|
70 |
->sendLicenseWarningEmail();
|
71 |
}
|
72 |
}
|
@@ -76,7 +76,7 @@ class Verify {
|
|
76 |
}
|
77 |
|
78 |
$oExisting->last_request_at = Services::Request()->ts();
|
79 |
-
$
|
80 |
$this->getMod()->saveModOptions();
|
81 |
|
82 |
if ( !$bSuccessfulApiRequest ) {
|
14 |
* @throws \Exception
|
15 |
*/
|
16 |
public function run() {
|
17 |
+
$con = $this->getCon();
|
18 |
+
/** @var License\ModCon $mod */
|
19 |
+
$mod = $this->getMod();
|
20 |
+
/** @var License\Options $opts */
|
21 |
+
$opts = $this->getOptions();
|
22 |
+
$oHandler = $mod->getLicenseHandler();
|
23 |
|
24 |
$this->preVerify();
|
25 |
|
26 |
$oExisting = $oHandler->getLicense();
|
27 |
|
28 |
$oLookupLicense = ( new LookupRequest() )
|
29 |
+
->setMod( $mod )
|
30 |
->lookup();
|
31 |
|
32 |
$bSuccessfulApiRequest = false;
|
36 |
$oExisting = $oLookupLicense;
|
37 |
$oExisting->updateLastVerifiedAt( true );
|
38 |
if ( !$oHandler->isActive() ) {
|
39 |
+
$opts->setOptAt( 'license_activated_at' );
|
40 |
}
|
41 |
+
$mod->clearLastErrors();
|
42 |
+
$opts->setOpt( 'license_data', $oExisting->getRawDataAsArray() ); // need to do this before event
|
43 |
+
$con->fireEvent( 'lic_check_success' );
|
44 |
}
|
45 |
elseif ( $oLookupLicense->isReady() ) {
|
46 |
$bSuccessfulApiRequest = true;
|
50 |
}
|
51 |
elseif ( $oExisting->isReady() ) { // Has a stored license but license HTTP request failed
|
52 |
|
53 |
+
$mod->setLastErrors( [
|
54 |
__( 'The most recent request to verify the site license encountered a problem.', 'wp-simple-firewall' )
|
55 |
] );
|
56 |
|
66 |
* We don't remove the license yet, but we warn the user
|
67 |
*/
|
68 |
( new LicenseEmails() )
|
69 |
+
->setMod( $mod )
|
70 |
->sendLicenseWarningEmail();
|
71 |
}
|
72 |
}
|
76 |
}
|
77 |
|
78 |
$oExisting->last_request_at = Services::Request()->ts();
|
79 |
+
$opts->setOpt( 'license_data', $oExisting->getRawDataAsArray() );
|
80 |
$this->getMod()->saveModOptions();
|
81 |
|
82 |
if ( !$bSuccessfulApiRequest ) {
|
src/lib/src/Modules/License/ModCon.php
ADDED
@@ -0,0 +1,66 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php declare( strict_types=1 );
|
2 |
+
|
3 |
+
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\License;
|
4 |
+
|
5 |
+
use FernleafSystems\Wordpress\Plugin\Shield\Databases;
|
6 |
+
use FernleafSystems\Wordpress\Plugin\Shield\Modules\BaseShield;
|
7 |
+
use FernleafSystems\Wordpress\Services\Services;
|
8 |
+
|
9 |
+
class ModCon extends BaseShield\ModCon {
|
10 |
+
|
11 |
+
/**
|
12 |
+
* @var Lib\LicenseHandler
|
13 |
+
*/
|
14 |
+
private $licenseHandler;
|
15 |
+
|
16 |
+
/**
|
17 |
+
* @var Lib\WpHashes\ApiTokenManager
|
18 |
+
*/
|
19 |
+
private $wpHashesTokenManager;
|
20 |
+
|
21 |
+
/**
|
22 |
+
* @return Lib\LicenseHandler
|
23 |
+
*/
|
24 |
+
public function getLicenseHandler() :Lib\LicenseHandler {
|
25 |
+
if ( !isset( $this->licenseHandler ) ) {
|
26 |
+
$this->licenseHandler = ( new Lib\LicenseHandler() )->setMod( $this );
|
27 |
+
}
|
28 |
+
return $this->licenseHandler;
|
29 |
+
}
|
30 |
+
|
31 |
+
public function getWpHashesTokenManager() :Lib\WpHashes\ApiTokenManager {
|
32 |
+
if ( !isset( $this->wpHashesTokenManager ) ) {
|
33 |
+
$this->wpHashesTokenManager = ( new Lib\WpHashes\ApiTokenManager() )->setMod( $this );
|
34 |
+
}
|
35 |
+
return $this->wpHashesTokenManager;
|
36 |
+
}
|
37 |
+
|
38 |
+
protected function redirectToInsightsSubPage() {
|
39 |
+
Services::Response()->redirect(
|
40 |
+
$this->getCon()->getModule_Insights()->getUrl_AdminPage(),
|
41 |
+
[ 'inav' => 'license' ]
|
42 |
+
);
|
43 |
+
}
|
44 |
+
|
45 |
+
public function runHourlyCron() {
|
46 |
+
$this->getWpHashesTokenManager()->getToken();
|
47 |
+
}
|
48 |
+
|
49 |
+
public function onWpInit() {
|
50 |
+
parent::onWpInit();
|
51 |
+
$this->getWpHashesTokenManager()->execute();
|
52 |
+
}
|
53 |
+
|
54 |
+
public function getIfShowModuleMenuItem() :bool {
|
55 |
+
return parent::getIfShowModuleMenuItem() && !$this->isPremium();
|
56 |
+
}
|
57 |
+
|
58 |
+
public function onPluginShutdown() {
|
59 |
+
try {
|
60 |
+
$this->getLicenseHandler()->verify( false );
|
61 |
+
}
|
62 |
+
catch ( \Exception $e ) {
|
63 |
+
}
|
64 |
+
parent::onPluginShutdown();
|
65 |
+
}
|
66 |
+
}
|
src/lib/src/Modules/License/Options.php
CHANGED
@@ -2,8 +2,8 @@
|
|
2 |
|
3 |
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\License;
|
4 |
|
5 |
-
use FernleafSystems\Wordpress\Plugin\Shield\Modules\
|
6 |
|
7 |
-
class Options extends
|
8 |
|
9 |
}
|
2 |
|
3 |
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\License;
|
4 |
|
5 |
+
use FernleafSystems\Wordpress\Plugin\Shield\Modules\BaseShield;
|
6 |
|
7 |
+
class Options extends BaseShield\Options {
|
8 |
|
9 |
}
|
src/lib/src/Modules/License/Processor.php
ADDED
@@ -0,0 +1,14 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php declare( strict_types=1 );
|
2 |
+
|
3 |
+
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\License;
|
4 |
+
|
5 |
+
use FernleafSystems\Wordpress\Plugin\Shield\Modules\BaseShield;
|
6 |
+
|
7 |
+
class Processor extends BaseShield\Processor {
|
8 |
+
|
9 |
+
protected function run() {
|
10 |
+
/** @var ModCon $mod */
|
11 |
+
$mod = $this->getMod();
|
12 |
+
$mod->getLicenseHandler()->execute();
|
13 |
+
}
|
14 |
+
}
|
src/lib/src/Modules/License/UI.php
CHANGED
@@ -2,18 +2,18 @@
|
|
2 |
|
3 |
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\License;
|
4 |
|
5 |
-
use FernleafSystems\Wordpress\Plugin\Shield\Modules\
|
6 |
use FernleafSystems\Wordpress\Services\Services;
|
7 |
|
8 |
-
class UI extends
|
9 |
|
10 |
/**
|
11 |
* @return array
|
12 |
*/
|
13 |
public function buildInsightsVars() {
|
14 |
-
/** @var \ICWP_WPSF_FeatureHandler_License $mod */
|
15 |
-
$mod = $this->getMod();
|
16 |
$con = $this->getCon();
|
|
|
|
|
17 |
$opts = $this->getOptions();
|
18 |
$WP = Services::WpGeneral();
|
19 |
$oCarbon = Services::Request()->carbon();
|
@@ -55,7 +55,8 @@ class UI extends Base\ShieldUI {
|
|
55 |
'vars' => [
|
56 |
'license_table' => $aLicenseTableVars,
|
57 |
'activation_url' => $WP->getHomeUrl(),
|
58 |
-
'error' => $mod->getLastErrors( true )
|
|
|
59 |
],
|
60 |
'inputs' => [
|
61 |
'license_key' => [
|
@@ -68,10 +69,9 @@ class UI extends Base\ShieldUI {
|
|
68 |
'connection_debug' => $mod->getAjaxActionData( 'connection_debug' )
|
69 |
],
|
70 |
'aHrefs' => [
|
71 |
-
'shield_pro_url'
|
72 |
-
'
|
73 |
-
'
|
74 |
-
'keyless_cp' => $opts->getDef( 'keyless_cp' ),
|
75 |
],
|
76 |
'flags' => [
|
77 |
'show_ads' => false,
|
@@ -85,12 +85,31 @@ class UI extends Base\ShieldUI {
|
|
85 |
];
|
86 |
}
|
87 |
|
88 |
-
/**
|
89 |
-
* @return bool
|
90 |
-
*/
|
91 |
public function isEnabledForUiSummary() :bool {
|
92 |
-
/** @var
|
93 |
$mod = $this->getMod();
|
94 |
return $mod->getLicenseHandler()->hasValidWorkingLicense();
|
95 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
96 |
}
|
2 |
|
3 |
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\License;
|
4 |
|
5 |
+
use FernleafSystems\Wordpress\Plugin\Shield\Modules\BaseShield;
|
6 |
use FernleafSystems\Wordpress\Services\Services;
|
7 |
|
8 |
+
class UI extends BaseShield\UI {
|
9 |
|
10 |
/**
|
11 |
* @return array
|
12 |
*/
|
13 |
public function buildInsightsVars() {
|
|
|
|
|
14 |
$con = $this->getCon();
|
15 |
+
/** @var ModCon $mod */
|
16 |
+
$mod = $this->getMod();
|
17 |
$opts = $this->getOptions();
|
18 |
$WP = Services::WpGeneral();
|
19 |
$oCarbon = Services::Request()->carbon();
|
55 |
'vars' => [
|
56 |
'license_table' => $aLicenseTableVars,
|
57 |
'activation_url' => $WP->getHomeUrl(),
|
58 |
+
'error' => $mod->getLastErrors( true ),
|
59 |
+
'related_hrefs' => $this->getSettingsRelatedLinks()
|
60 |
],
|
61 |
'inputs' => [
|
62 |
'license_key' => [
|
69 |
'connection_debug' => $mod->getAjaxActionData( 'connection_debug' )
|
70 |
],
|
71 |
'aHrefs' => [
|
72 |
+
'shield_pro_url' => 'https://shsec.io/shieldpro',
|
73 |
+
'iframe_url' => $opts->getDef( 'landing_page_url' ),
|
74 |
+
'keyless_cp' => $opts->getDef( 'keyless_cp' ),
|
|
|
75 |
],
|
76 |
'flags' => [
|
77 |
'show_ads' => false,
|
85 |
];
|
86 |
}
|
87 |
|
|
|
|
|
|
|
88 |
public function isEnabledForUiSummary() :bool {
|
89 |
+
/** @var ModCon $mod */
|
90 |
$mod = $this->getMod();
|
91 |
return $mod->getLicenseHandler()->hasValidWorkingLicense();
|
92 |
}
|
93 |
+
|
94 |
+
protected function getSettingsRelatedLinks() :array {
|
95 |
+
$modInsights = $this->getCon()->getModule_Insights();
|
96 |
+
$links = [];
|
97 |
+
if ( !$this->getCon()->isPremiumActive() ) {
|
98 |
+
$links[] = [
|
99 |
+
'href' => $modInsights->getUrl_SubInsightsPage( 'free_trial' ),
|
100 |
+
'title' => __( 'Free Trial', 'wp-simple-firewall' ),
|
101 |
+
];
|
102 |
+
}
|
103 |
+
$links[] = [
|
104 |
+
'href' => 'https://shsec.io/c5',
|
105 |
+
'title' => __( 'License Activation', 'wp-simple-firewall' ),
|
106 |
+
'new' => true,
|
107 |
+
];
|
108 |
+
$links[] = [
|
109 |
+
'href' => 'https://shsec.io/gp',
|
110 |
+
'title' => __( 'ShieldPRO Features', 'wp-simple-firewall' ),
|
111 |
+
'new' => true,
|
112 |
+
];
|
113 |
+
return $links;
|
114 |
+
}
|
115 |
}
|
src/lib/src/Modules/License/WpCli.php
CHANGED
@@ -10,7 +10,7 @@ class WpCli extends Base\WpCli {
|
|
10 |
/**
|
11 |
* @inheritDoc
|
12 |
*/
|
13 |
-
protected function getCmdHandlers() {
|
14 |
return [
|
15 |
new License\WpCli\License()
|
16 |
];
|
10 |
/**
|
11 |
* @inheritDoc
|
12 |
*/
|
13 |
+
protected function getCmdHandlers() :array {
|
14 |
return [
|
15 |
new License\WpCli\License()
|
16 |
];
|
src/lib/src/Modules/License/WpCli/License.php
CHANGED
@@ -3,6 +3,7 @@
|
|
3 |
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\License\WpCli;
|
4 |
|
5 |
use FernleafSystems\Wordpress\Plugin\Shield\Modules\Base;
|
|
|
6 |
use WP_CLI;
|
7 |
|
8 |
class License extends Base\WpCli\BaseWpCliCmd {
|
@@ -68,9 +69,9 @@ class License extends Base\WpCli\BaseWpCliCmd {
|
|
68 |
if ( !$bConfirm ) {
|
69 |
WP_CLI::confirm( __( 'Are you sure you want to remove the ShieldPRO license?', 'wp-simple-firewall' ) );
|
70 |
}
|
71 |
-
/** @var
|
72 |
-
$
|
73 |
-
$
|
74 |
WP_CLI::success( __( 'License removed successfully.', 'wp-simple-firewall' ) );
|
75 |
}
|
76 |
}
|
@@ -79,9 +80,9 @@ class License extends Base\WpCli\BaseWpCliCmd {
|
|
79 |
* @throws WP_CLI\ExitException
|
80 |
*/
|
81 |
private function runStatus() {
|
82 |
-
/** @var
|
83 |
-
$
|
84 |
-
$
|
85 |
WP_CLI::success( __( 'Active license found.', 'wp-simple-firewall' ) )
|
86 |
: WP_CLI::error( __( 'No active license present.', 'wp-simple-firewall' ) );
|
87 |
}
|
@@ -90,14 +91,14 @@ class License extends Base\WpCli\BaseWpCliCmd {
|
|
90 |
* @throws WP_CLI\ExitException
|
91 |
*/
|
92 |
private function runVerify() {
|
93 |
-
/** @var
|
94 |
-
$
|
95 |
|
96 |
try {
|
97 |
if ( $this->getCon()->isPremiumActive() ) {
|
98 |
WP_CLI::log( 'Premium license is already active. Re-checking...' );
|
99 |
}
|
100 |
-
$bSuccess = $
|
101 |
->getLicenseHandler()
|
102 |
->verify()
|
103 |
->hasValidWorkingLicense();
|
3 |
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\License\WpCli;
|
4 |
|
5 |
use FernleafSystems\Wordpress\Plugin\Shield\Modules\Base;
|
6 |
+
use FernleafSystems\Wordpress\Plugin\Shield\Modules\License\ModCon;
|
7 |
use WP_CLI;
|
8 |
|
9 |
class License extends Base\WpCli\BaseWpCliCmd {
|
69 |
if ( !$bConfirm ) {
|
70 |
WP_CLI::confirm( __( 'Are you sure you want to remove the ShieldPRO license?', 'wp-simple-firewall' ) );
|
71 |
}
|
72 |
+
/** @var ModCon $mod */
|
73 |
+
$mod = $this->getMod();
|
74 |
+
$mod->getLicenseHandler()->clearLicense();
|
75 |
WP_CLI::success( __( 'License removed successfully.', 'wp-simple-firewall' ) );
|
76 |
}
|
77 |
}
|
80 |
* @throws WP_CLI\ExitException
|
81 |
*/
|
82 |
private function runStatus() {
|
83 |
+
/** @var ModCon $mod */
|
84 |
+
$mod = $this->getMod();
|
85 |
+
$mod->getLicenseHandler()->isActive() ?
|
86 |
WP_CLI::success( __( 'Active license found.', 'wp-simple-firewall' ) )
|
87 |
: WP_CLI::error( __( 'No active license present.', 'wp-simple-firewall' ) );
|
88 |
}
|
91 |
* @throws WP_CLI\ExitException
|
92 |
*/
|
93 |
private function runVerify() {
|
94 |
+
/** @var ModCon $mod */
|
95 |
+
$mod = $this->getMod();
|
96 |
|
97 |
try {
|
98 |
if ( $this->getCon()->isPremiumActive() ) {
|
99 |
WP_CLI::log( 'Premium license is already active. Re-checking...' );
|
100 |
}
|
101 |
+
$bSuccess = $mod
|
102 |
->getLicenseHandler()
|
103 |
->verify()
|
104 |
->hasValidWorkingLicense();
|
src/lib/src/Modules/Lockdown/Insights/OverviewCards.php
CHANGED
@@ -2,14 +2,14 @@
|
|
2 |
|
3 |
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Lockdown\Insights;
|
4 |
|
5 |
-
use FernleafSystems\Wordpress\Plugin\Shield;
|
6 |
|
7 |
-
class OverviewCards extends
|
8 |
|
9 |
public function build() :array {
|
10 |
-
/** @var \
|
11 |
$mod = $this->getMod();
|
12 |
-
/** @var
|
13 |
$opts = $this->getOptions();
|
14 |
|
15 |
$cardSection = [
|
2 |
|
3 |
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Lockdown\Insights;
|
4 |
|
5 |
+
use FernleafSystems\Wordpress\Plugin\Shield\Modules;
|
6 |
|
7 |
+
class OverviewCards extends Modules\Base\Insights\OverviewCards {
|
8 |
|
9 |
public function build() :array {
|
10 |
+
/** @var Modules\Lockdown\ModCon $mod */
|
11 |
$mod = $this->getMod();
|
12 |
+
/** @var Modules\Lockdown\Options $opts */
|
13 |
$opts = $this->getOptions();
|
14 |
|
15 |
$cardSection = [
|
src/lib/src/Modules/Lockdown/ModCon.php
ADDED
@@ -0,0 +1,31 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php declare( strict_types=1 );
|
2 |
+
|
3 |
+
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Lockdown;
|
4 |
+
|
5 |
+
use FernleafSystems\Wordpress\Plugin\Shield\Modules\BaseShield;
|
6 |
+
|
7 |
+
class ModCon extends BaseShield\ModCon {
|
8 |
+
|
9 |
+
/**
|
10 |
+
* @param string $namespace
|
11 |
+
* @return bool
|
12 |
+
*/
|
13 |
+
public function isPermittedAnonRestApiNamespace( $namespace ) {
|
14 |
+
/** @var Options $opts */
|
15 |
+
$opts = $this->getOptions();
|
16 |
+
return in_array( $namespace, $opts->getRestApiAnonymousExclusions() );
|
17 |
+
}
|
18 |
+
|
19 |
+
protected function preProcessOptions() {
|
20 |
+
$this->cleanApiExclusions();
|
21 |
+
}
|
22 |
+
|
23 |
+
private function cleanApiExclusions() {
|
24 |
+
/** @var Options $opts */
|
25 |
+
$opts = $this->getOptions();
|
26 |
+
$opts->setOpt(
|
27 |
+
'api_namespace_exclusions',
|
28 |
+
$this->cleanStringArray( $opts->getRestApiAnonymousExclusions(), '#[^a-z0-9_-]#i' )
|
29 |
+
);
|
30 |
+
}
|
31 |
+
}
|
src/lib/src/Modules/Lockdown/Options.php
CHANGED
@@ -2,9 +2,9 @@
|
|
2 |
|
3 |
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Lockdown;
|
4 |
|
5 |
-
use FernleafSystems\Wordpress\Plugin\Shield\Modules\
|
6 |
|
7 |
-
class Options extends
|
8 |
|
9 |
/**
|
10 |
* @return string[]
|
2 |
|
3 |
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Lockdown;
|
4 |
|
5 |
+
use FernleafSystems\Wordpress\Plugin\Shield\Modules\BaseShield;
|
6 |
|
7 |
+
class Options extends BaseShield\Options {
|
8 |
|
9 |
/**
|
10 |
* @return string[]
|
src/lib/src/Modules/Lockdown/Processor.php
ADDED
@@ -0,0 +1,134 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Lockdown;
|
4 |
+
|
5 |
+
use FernleafSystems\Wordpress\Plugin\Shield\Modules\BaseShield;
|
6 |
+
use FernleafSystems\Wordpress\Services\Services;
|
7 |
+
|
8 |
+
class Processor extends BaseShield\Processor {
|
9 |
+
|
10 |
+
protected function run() {
|
11 |
+
/** @var Options $opts */
|
12 |
+
$opts = $this->getOptions();
|
13 |
+
|
14 |
+
if ( $opts->isOptFileEditingDisabled() ) {
|
15 |
+
$this->blockFileEditing();
|
16 |
+
}
|
17 |
+
|
18 |
+
if ( $opts->isOpt( 'force_ssl_admin', 'Y' ) && function_exists( 'force_ssl_admin' ) ) {
|
19 |
+
if ( !defined( 'FORCE_SSL_ADMIN' ) ) {
|
20 |
+
define( 'FORCE_SSL_ADMIN', true );
|
21 |
+
}
|
22 |
+
force_ssl_admin( true );
|
23 |
+
}
|
24 |
+
|
25 |
+
if ( $opts->isOpt( 'hide_wordpress_generator_tag', 'Y' ) ) {
|
26 |
+
remove_action( 'wp_head', 'wp_generator' );
|
27 |
+
}
|
28 |
+
|
29 |
+
if ( $opts->isOpt( 'clean_wp_rubbish', 'Y' ) ) {
|
30 |
+
( new Lib\CleanRubbish() )
|
31 |
+
->setMod( $this->getMod() )
|
32 |
+
->execute();
|
33 |
+
}
|
34 |
+
|
35 |
+
if ( $opts->isXmlrpcDisabled() ) {
|
36 |
+
add_filter( 'xmlrpc_enabled', [ $this, 'disableXmlrpc' ], 1000, 0 );
|
37 |
+
add_filter( 'xmlrpc_methods', [ $this, 'disableXmlrpc' ], 1000, 0 );
|
38 |
+
}
|
39 |
+
}
|
40 |
+
|
41 |
+
private function blockFileEditing() {
|
42 |
+
if ( !defined( 'DISALLOW_FILE_EDIT' ) ) {
|
43 |
+
define( 'DISALLOW_FILE_EDIT', true );
|
44 |
+
}
|
45 |
+
|
46 |
+
add_filter( 'user_has_cap',
|
47 |
+
/**
|
48 |
+
* @param array $aAllCaps
|
49 |
+
* @param array $cap
|
50 |
+
* @param array $aArgs
|
51 |
+
* @return array
|
52 |
+
*/
|
53 |
+
function ( $aAllCaps, $cap, $aArgs ) {
|
54 |
+
$sRequestedCapability = $aArgs[ 0 ];
|
55 |
+
if ( in_array( $sRequestedCapability, [ 'edit_themes', 'edit_plugins', 'edit_files' ] ) ) {
|
56 |
+
$aAllCaps[ $sRequestedCapability ] = false;
|
57 |
+
}
|
58 |
+
return $aAllCaps;
|
59 |
+
},
|
60 |
+
PHP_INT_MAX, 3
|
61 |
+
);
|
62 |
+
}
|
63 |
+
|
64 |
+
public function onWpInit() {
|
65 |
+
/** @var Options $opts */
|
66 |
+
$opts = $this->getOptions();
|
67 |
+
|
68 |
+
if ( !Services::WpUsers()->isUserLoggedIn() ) {
|
69 |
+
$this->interceptCanonicalRedirects();
|
70 |
+
if ( $opts->isRestApiAnonymousAccessDisabled() ) {
|
71 |
+
add_filter( 'rest_authentication_errors', [ $this, 'disableAnonymousRestApi' ], 99 );
|
72 |
+
}
|
73 |
+
}
|
74 |
+
}
|
75 |
+
|
76 |
+
/**
|
77 |
+
* @return array|false
|
78 |
+
*/
|
79 |
+
public function disableXmlrpc() {
|
80 |
+
$this->getCon()->fireEvent( 'block_xml' );
|
81 |
+
return ( current_filter() == 'xmlrpc_enabled' ) ? false : [];
|
82 |
+
}
|
83 |
+
|
84 |
+
/**
|
85 |
+
* @uses wp_die()
|
86 |
+
*/
|
87 |
+
private function interceptCanonicalRedirects() {
|
88 |
+
|
89 |
+
if ( $this->getOptions()->isOpt( 'block_author_discovery', 'Y' ) ) {
|
90 |
+
$sAuthor = Services::Request()->query( 'author', '' );
|
91 |
+
if ( !empty( $sAuthor ) ) {
|
92 |
+
Services::WpGeneral()->wpDie( sprintf(
|
93 |
+
__( 'The "author" query parameter has been blocked by %s to protect against user login name fishing.', 'wp-simple-firewall' )
|
94 |
+
.sprintf( '<br /><a href="%s" target="_blank">%s</a>',
|
95 |
+
'https://shsec.io/7l',
|
96 |
+
__( 'Learn More.', 'wp-simple-firewall' )
|
97 |
+
),
|
98 |
+
$this->getCon()->getHumanName()
|
99 |
+
) );
|
100 |
+
}
|
101 |
+
}
|
102 |
+
}
|
103 |
+
|
104 |
+
/**
|
105 |
+
* Understand that if $mCurrentStatus is null, no check has been made. If true, something has
|
106 |
+
* authenticated the request, and if WP_Error, then an error is already present
|
107 |
+
* @param \WP_Error|true|null $mStatus
|
108 |
+
* @return \WP_Error
|
109 |
+
*/
|
110 |
+
public function disableAnonymousRestApi( $mStatus ) {
|
111 |
+
/** @var ModCon $mod */
|
112 |
+
$mod = $this->getMod();
|
113 |
+
$oWpRest = Services::Rest();
|
114 |
+
|
115 |
+
$sNamespace = $oWpRest->getNamespace();
|
116 |
+
if ( !empty( $sNamespace ) && $mStatus !== true && !is_wp_error( $mStatus )
|
117 |
+
&& !$mod->isPermittedAnonRestApiNamespace( $sNamespace ) ) {
|
118 |
+
|
119 |
+
$mStatus = new \WP_Error(
|
120 |
+
'shield_block_anon_restapi',
|
121 |
+
sprintf( __( 'Anonymous access to the WordPress Rest API has been restricted by %s.', 'wp-simple-firewall' ), $this->getCon()
|
122 |
+
->getHumanName() ),
|
123 |
+
[ 'status' => rest_authorization_required_code() ] );
|
124 |
+
|
125 |
+
$this->getCon()
|
126 |
+
->fireEvent(
|
127 |
+
'block_anonymous_restapi',
|
128 |
+
[ 'audit' => [ 'namespace' => $sNamespace ] ]
|
129 |
+
);
|
130 |
+
}
|
131 |
+
|
132 |
+
return $mStatus;
|
133 |
+
}
|
134 |
+
}
|
src/lib/src/Modules/Lockdown/UI.php
CHANGED
@@ -2,8 +2,8 @@
|
|
2 |
|
3 |
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Lockdown;
|
4 |
|
5 |
-
use FernleafSystems\Wordpress\Plugin\Shield\Modules\
|
6 |
|
7 |
-
class UI extends
|
8 |
|
9 |
}
|
2 |
|
3 |
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Lockdown;
|
4 |
|
5 |
+
use FernleafSystems\Wordpress\Plugin\Shield\Modules\BaseShield;
|
6 |
|
7 |
+
class UI extends BaseShield\UI {
|
8 |
|
9 |
}
|
src/lib/src/Modules/LoginGuard/AdminNotices.php
CHANGED
@@ -3,35 +3,32 @@
|
|
3 |
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\LoginGuard;
|
4 |
|
5 |
use FernleafSystems\Wordpress\Plugin\Shield;
|
|
|
6 |
|
7 |
class AdminNotices extends Shield\Modules\Base\AdminNotices {
|
8 |
|
9 |
/**
|
10 |
-
* @
|
11 |
-
* @throws \Exception
|
12 |
*/
|
13 |
-
protected function processNotice( $
|
14 |
|
15 |
-
switch ( $
|
16 |
|
17 |
case 'email-verification-sent':
|
18 |
-
$this->buildNotice_EmailVerificationSent( $
|
19 |
break;
|
20 |
|
21 |
default:
|
22 |
-
parent::processNotice( $
|
23 |
break;
|
24 |
}
|
25 |
}
|
26 |
|
27 |
-
|
28 |
-
|
29 |
-
|
30 |
-
private function buildNotice_EmailVerificationSent( $oNotice ) {
|
31 |
-
/** @var \ICWP_WPSF_FeatureHandler_LoginProtect $oMod */
|
32 |
-
$oMod = $this->getMod();
|
33 |
|
34 |
-
$
|
35 |
'notice_attributes' => [],
|
36 |
'strings' => [
|
37 |
'title' => $this->getCon()->getHumanName()
|
@@ -46,33 +43,27 @@ class AdminNotices extends Shield\Modules\Base\AdminNotices {
|
|
46 |
'how_turn_off' => __( "Disable 2FA by email", 'wp-simple-firewall' ),
|
47 |
],
|
48 |
'ajax' => [
|
49 |
-
'resend_verification_email' => $
|
50 |
-
'disable_2fa_email' => $
|
51 |
]
|
52 |
];
|
53 |
}
|
54 |
|
55 |
-
|
56 |
-
|
57 |
-
|
58 |
-
*/
|
59 |
-
protected function isDisplayNeeded( $oNotice ) {
|
60 |
-
/** @var \ICWP_WPSF_FeatureHandler_LoginProtect $oMod */
|
61 |
-
$oMod = $this->getMod();
|
62 |
-
/** @var Options $oOpts */
|
63 |
-
$oOpts = $this->getOptions();
|
64 |
|
65 |
-
switch ( $
|
66 |
|
67 |
case 'email-verification-sent':
|
68 |
-
$
|
69 |
-
|
70 |
break;
|
71 |
|
72 |
default:
|
73 |
-
$
|
74 |
break;
|
75 |
}
|
76 |
-
return $
|
77 |
}
|
78 |
}
|
3 |
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\LoginGuard;
|
4 |
|
5 |
use FernleafSystems\Wordpress\Plugin\Shield;
|
6 |
+
use FernleafSystems\Wordpress\Plugin\Shield\Utilities\AdminNotices\NoticeVO;
|
7 |
|
8 |
class AdminNotices extends Shield\Modules\Base\AdminNotices {
|
9 |
|
10 |
/**
|
11 |
+
* @inheritDoc
|
|
|
12 |
*/
|
13 |
+
protected function processNotice( NoticeVO $notice ) {
|
14 |
|
15 |
+
switch ( $notice->id ) {
|
16 |
|
17 |
case 'email-verification-sent':
|
18 |
+
$this->buildNotice_EmailVerificationSent( $notice );
|
19 |
break;
|
20 |
|
21 |
default:
|
22 |
+
parent::processNotice( $notice );
|
23 |
break;
|
24 |
}
|
25 |
}
|
26 |
|
27 |
+
private function buildNotice_EmailVerificationSent( NoticeVO $notice ) {
|
28 |
+
/** @var ModCon $mod */
|
29 |
+
$mod = $this->getMod();
|
|
|
|
|
|
|
30 |
|
31 |
+
$notice->render_data = [
|
32 |
'notice_attributes' => [],
|
33 |
'strings' => [
|
34 |
'title' => $this->getCon()->getHumanName()
|
43 |
'how_turn_off' => __( "Disable 2FA by email", 'wp-simple-firewall' ),
|
44 |
],
|
45 |
'ajax' => [
|
46 |
+
'resend_verification_email' => $mod->getAjaxActionData( 'resend_verification_email', true ),
|
47 |
+
'disable_2fa_email' => $mod->getAjaxActionData( 'disable_2fa_email', true ),
|
48 |
]
|
49 |
];
|
50 |
}
|
51 |
|
52 |
+
protected function isDisplayNeeded( NoticeVO $notice ) :bool {
|
53 |
+
/** @var Options $opts */
|
54 |
+
$opts = $this->getOptions();
|
|
|
|
|
|
|
|
|
|
|
|
|
55 |
|
56 |
+
switch ( $notice->id ) {
|
57 |
|
58 |
case 'email-verification-sent':
|
59 |
+
$needed = $opts->isEnabledEmailAuth()
|
60 |
+
&& !$opts->isEmailAuthenticationActive() && !$opts->getIfCanSendEmailVerified();
|
61 |
break;
|
62 |
|
63 |
default:
|
64 |
+
$needed = parent::isDisplayNeeded( $notice );
|
65 |
break;
|
66 |
}
|
67 |
+
return $needed;
|
68 |
}
|
69 |
}
|
src/lib/src/Modules/LoginGuard/AjaxHandler.php
CHANGED
@@ -6,7 +6,7 @@ use FernleafSystems\Wordpress\Plugin\Shield;
|
|
6 |
use FernleafSystems\Wordpress\Plugin\Shield\Modules\LoginGuard\Lib\TwoFactor;
|
7 |
use FernleafSystems\Wordpress\Services\Services;
|
8 |
|
9 |
-
class AjaxHandler extends Shield\Modules\
|
10 |
|
11 |
protected function processAjaxAction( string $action ) :array {
|
12 |
|
@@ -42,48 +42,39 @@ class AjaxHandler extends Shield\Modules\Base\AjaxHandlerShield {
|
|
42 |
return $aResponse;
|
43 |
}
|
44 |
|
45 |
-
|
46 |
-
|
47 |
-
|
48 |
-
protected function ajaxExec_GenBackupCodes() {
|
49 |
-
/** @var \ICWP_WPSF_FeatureHandler_LoginProtect $oMod */
|
50 |
-
$oMod = $this->getMod();
|
51 |
/** @var TwoFactor\Provider\Backup $oBU */
|
52 |
-
$oBU = $
|
53 |
-
|
54 |
-
$
|
55 |
|
56 |
-
foreach ( [ 20, 15, 10, 5 ] as $
|
57 |
-
$
|
58 |
}
|
59 |
|
60 |
return [
|
61 |
-
'code' => $
|
62 |
'success' => true
|
63 |
];
|
64 |
}
|
65 |
|
66 |
-
|
67 |
-
|
68 |
-
|
69 |
-
private function ajaxExec_DeleteBackupCodes() {
|
70 |
-
/** @var \ICWP_WPSF_FeatureHandler_LoginProtect $oMod */
|
71 |
-
$oMod = $this->getMod();
|
72 |
/** @var TwoFactor\Provider\Backup $oBU */
|
73 |
-
$oBU = $
|
74 |
-
|
75 |
$oBU->deleteSecret( Services::WpUsers()->getCurrentWpUser() );
|
76 |
-
$
|
77 |
return [
|
78 |
'success' => true
|
79 |
];
|
80 |
}
|
81 |
|
82 |
-
|
83 |
-
|
84 |
-
*/
|
85 |
-
private function ajaxExec_Disable2faEmail() {
|
86 |
-
/** @var \ICWP_WPSF_FeatureHandler_LoginProtect $mod */
|
87 |
$mod = $this->getMod();
|
88 |
$mod->setEnabled2FaEmail( false );
|
89 |
return [
|
@@ -93,17 +84,14 @@ class AjaxHandler extends Shield\Modules\Base\AjaxHandlerShield {
|
|
93 |
];
|
94 |
}
|
95 |
|
96 |
-
|
97 |
-
|
98 |
-
*/
|
99 |
-
private function ajaxExec_ProfileU2fRemove() {
|
100 |
-
/** @var \ICWP_WPSF_FeatureHandler_LoginProtect $mod */
|
101 |
$mod = $this->getMod();
|
102 |
|
103 |
-
$
|
104 |
( new TwoFactor\Provider\U2F() )
|
105 |
->setMod( $mod )
|
106 |
-
->removeRegisteredU2fId( Services::WpUsers()->getCurrentWpUser(), $
|
107 |
return [
|
108 |
'success' => true,
|
109 |
'message' => __( 'Registered U2F device removed from profile.', 'wp-simple-firewall' ),
|
@@ -115,13 +103,13 @@ class AjaxHandler extends Shield\Modules\Base\AjaxHandlerShield {
|
|
115 |
* @return array
|
116 |
*/
|
117 |
private function ajaxExec_ProfileYubikeyRemove() {
|
118 |
-
/** @var
|
119 |
-
$
|
120 |
|
121 |
-
$
|
122 |
( new TwoFactor\Provider\Yubikey() )
|
123 |
-
->setMod( $
|
124 |
-
->addRemoveRegisteredYubiId( Services::WpUsers()->getCurrentWpUser(), $
|
125 |
return [
|
126 |
'success' => true,
|
127 |
'message' => __( 'Yubikey removed from profile.', 'wp-simple-firewall' ),
|
@@ -133,17 +121,17 @@ class AjaxHandler extends Shield\Modules\Base\AjaxHandlerShield {
|
|
133 |
* @return array
|
134 |
*/
|
135 |
private function ajaxExec_ResendEmailVerification() {
|
136 |
-
/** @var
|
137 |
$mod = $this->getMod();
|
138 |
-
/** @var Options $
|
139 |
-
$
|
140 |
-
$
|
141 |
|
142 |
-
if ( !$
|
143 |
$sMessage = __( 'Email 2FA option is not currently enabled.', 'wp-simple-firewall' );
|
144 |
-
$
|
145 |
}
|
146 |
-
elseif ( $
|
147 |
$sMessage = __( 'Email sending has already been verified.', 'wp-simple-firewall' );
|
148 |
}
|
149 |
else {
|
@@ -153,7 +141,7 @@ class AjaxHandler extends Shield\Modules\Base\AjaxHandlerShield {
|
|
153 |
}
|
154 |
|
155 |
return [
|
156 |
-
'success' => $
|
157 |
'message' => $sMessage
|
158 |
];
|
159 |
}
|
6 |
use FernleafSystems\Wordpress\Plugin\Shield\Modules\LoginGuard\Lib\TwoFactor;
|
7 |
use FernleafSystems\Wordpress\Services\Services;
|
8 |
|
9 |
+
class AjaxHandler extends Shield\Modules\BaseShield\AjaxHandler {
|
10 |
|
11 |
protected function processAjaxAction( string $action ) :array {
|
12 |
|
42 |
return $aResponse;
|
43 |
}
|
44 |
|
45 |
+
protected function ajaxExec_GenBackupCodes() :array {
|
46 |
+
/** @var ModCon $mod */
|
47 |
+
$mod = $this->getMod();
|
|
|
|
|
|
|
48 |
/** @var TwoFactor\Provider\Backup $oBU */
|
49 |
+
$oBU = $mod->getLoginIntentController()
|
50 |
+
->getProviders()[ TwoFactor\Provider\Backup::SLUG ];
|
51 |
+
$pass = $oBU->resetSecret( Services::WpUsers()->getCurrentWpUser() );
|
52 |
|
53 |
+
foreach ( [ 20, 15, 10, 5 ] as $pos ) {
|
54 |
+
$pass = substr_replace( $pass, '-', $pos, 0 );
|
55 |
}
|
56 |
|
57 |
return [
|
58 |
+
'code' => $pass,
|
59 |
'success' => true
|
60 |
];
|
61 |
}
|
62 |
|
63 |
+
private function ajaxExec_DeleteBackupCodes() :array {
|
64 |
+
/** @var ModCon $mod */
|
65 |
+
$mod = $this->getMod();
|
|
|
|
|
|
|
66 |
/** @var TwoFactor\Provider\Backup $oBU */
|
67 |
+
$oBU = $mod->getLoginIntentController()
|
68 |
+
->getProviders()[ TwoFactor\Provider\Backup::SLUG ];
|
69 |
$oBU->deleteSecret( Services::WpUsers()->getCurrentWpUser() );
|
70 |
+
$mod->setFlashAdminNotice( __( 'Multi-factor login backup code has been removed from your profile', 'wp-simple-firewall' ) );
|
71 |
return [
|
72 |
'success' => true
|
73 |
];
|
74 |
}
|
75 |
|
76 |
+
private function ajaxExec_Disable2faEmail() :array {
|
77 |
+
/** @var ModCon $mod */
|
|
|
|
|
|
|
78 |
$mod = $this->getMod();
|
79 |
$mod->setEnabled2FaEmail( false );
|
80 |
return [
|
84 |
];
|
85 |
}
|
86 |
|
87 |
+
private function ajaxExec_ProfileU2fRemove() :array {
|
88 |
+
/** @var ModCon $mod */
|
|
|
|
|
|
|
89 |
$mod = $this->getMod();
|
90 |
|
91 |
+
$key = Services::Request()->post( 'u2fid' );
|
92 |
( new TwoFactor\Provider\U2F() )
|
93 |
->setMod( $mod )
|
94 |
+
->removeRegisteredU2fId( Services::WpUsers()->getCurrentWpUser(), $key );
|
95 |
return [
|
96 |
'success' => true,
|
97 |
'message' => __( 'Registered U2F device removed from profile.', 'wp-simple-firewall' ),
|
103 |
* @return array
|
104 |
*/
|
105 |
private function ajaxExec_ProfileYubikeyRemove() {
|
106 |
+
/** @var ModCon $mod */
|
107 |
+
$mod = $this->getMod();
|
108 |
|
109 |
+
$key = Services::Request()->post( 'yubikeyid' );
|
110 |
( new TwoFactor\Provider\Yubikey() )
|
111 |
+
->setMod( $mod )
|
112 |
+
->addRemoveRegisteredYubiId( Services::WpUsers()->getCurrentWpUser(), $key, false );
|
113 |
return [
|
114 |
'success' => true,
|
115 |
'message' => __( 'Yubikey removed from profile.', 'wp-simple-firewall' ),
|
121 |
* @return array
|
122 |
*/
|
123 |
private function ajaxExec_ResendEmailVerification() {
|
124 |
+
/** @var ModCon $mod */
|
125 |
$mod = $this->getMod();
|
126 |
+
/** @var Options $opts */
|
127 |
+
$opts = $this->getOptions();
|
128 |
+
$success = true;
|
129 |
|
130 |
+
if ( !$opts->isEnabledEmailAuth() ) {
|
131 |
$sMessage = __( 'Email 2FA option is not currently enabled.', 'wp-simple-firewall' );
|
132 |
+
$success = false;
|
133 |
}
|
134 |
+
elseif ( $opts->getIfCanSendEmailVerified() ) {
|
135 |
$sMessage = __( 'Email sending has already been verified.', 'wp-simple-firewall' );
|
136 |
}
|
137 |
else {
|
141 |
}
|
142 |
|
143 |
return [
|
144 |
+
'success' => $success,
|
145 |
'message' => $sMessage
|
146 |
];
|
147 |
}
|
src/lib/src/Modules/LoginGuard/Insights/OverviewCards.php
CHANGED
@@ -3,14 +3,14 @@
|
|
3 |
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\LoginGuard\Insights;
|
4 |
|
5 |
use FernleafSystems\Wordpress\Plugin\Shield;
|
6 |
-
use FernleafSystems\Wordpress\Plugin\Shield\Modules\LoginGuard
|
7 |
|
8 |
class OverviewCards extends Shield\Modules\Base\Insights\OverviewCards {
|
9 |
|
10 |
public function build() :array {
|
11 |
-
/** @var \
|
12 |
$mod = $this->getMod();
|
13 |
-
/** @var Options $opts */
|
14 |
$opts = $this->getOptions();
|
15 |
|
16 |
$cardSection = [
|
3 |
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\LoginGuard\Insights;
|
4 |
|
5 |
use FernleafSystems\Wordpress\Plugin\Shield;
|
6 |
+
use FernleafSystems\Wordpress\Plugin\Shield\Modules\LoginGuard;
|
7 |
|
8 |
class OverviewCards extends Shield\Modules\Base\Insights\OverviewCards {
|
9 |
|
10 |
public function build() :array {
|
11 |
+
/** @var LoginGuard\ModCon $mod */
|
12 |
$mod = $this->getMod();
|
13 |
+
/** @var LoginGuard\Options $opts */
|
14 |
$opts = $this->getOptions();
|
15 |
|
16 |
$cardSection = [
|
src/lib/src/Modules/LoginGuard/Lib/AntiBot/AntibotSetup.php
CHANGED
@@ -23,7 +23,7 @@ class AntibotSetup {
|
|
23 |
}
|
24 |
|
25 |
private function run() {
|
26 |
-
/** @var \
|
27 |
$mod = $this->getMod();
|
28 |
/** @var LoginGuard\Options $opts */
|
29 |
$opts = $this->getOptions();
|
@@ -40,12 +40,12 @@ class AntibotSetup {
|
|
40 |
}
|
41 |
|
42 |
if ( $mod->isEnabledCaptcha() ) {
|
43 |
-
$
|
44 |
-
if ( $
|
45 |
$aProtectionProviders[] = ( new AntiBot\ProtectionProviders\GoogleRecaptcha() )
|
46 |
->setMod( $mod );
|
47 |
}
|
48 |
-
elseif ( $
|
49 |
$aProtectionProviders[] = ( new AntiBot\ProtectionProviders\HCaptcha() )
|
50 |
->setMod( $mod );
|
51 |
}
|
23 |
}
|
24 |
|
25 |
private function run() {
|
26 |
+
/** @var LoginGuard\ModCon $mod */
|
27 |
$mod = $this->getMod();
|
28 |
/** @var LoginGuard\Options $opts */
|
29 |
$opts = $this->getOptions();
|
40 |
}
|
41 |
|
42 |
if ( $mod->isEnabledCaptcha() ) {
|
43 |
+
$cfg = $mod->getCaptchaCfg();
|
44 |
+
if ( $cfg->provider === CaptchaConfigVO::PROV_GOOGLE_RECAP2 ) {
|
45 |
$aProtectionProviders[] = ( new AntiBot\ProtectionProviders\GoogleRecaptcha() )
|
46 |
->setMod( $mod );
|
47 |
}
|
48 |
+
elseif ( $cfg->provider === CaptchaConfigVO::PROV_HCAPTCHA ) {
|
49 |
$aProtectionProviders[] = ( new AntiBot\ProtectionProviders\HCaptcha() )
|
50 |
->setMod( $mod );
|
51 |
}
|
src/lib/src/Modules/LoginGuard/Lib/AntiBot/FormProviders/WooCommerce.php
CHANGED
@@ -2,15 +2,15 @@
|
|
2 |
|
3 |
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\LoginGuard\Lib\AntiBot\FormProviders;
|
4 |
|
5 |
-
use FernleafSystems\Wordpress\Plugin\Shield\Modules\LoginGuard
|
6 |
|
7 |
class WooCommerce extends BaseFormProvider {
|
8 |
|
9 |
public function run() {
|
10 |
parent::run();
|
11 |
-
/** @var Options $
|
12 |
-
$
|
13 |
-
if ( $
|
14 |
$this->woocheckout();
|
15 |
}
|
16 |
}
|
@@ -50,10 +50,10 @@ class WooCommerce extends BaseFormProvider {
|
|
50 |
* @return void
|
51 |
*/
|
52 |
public function formInsertsPrint_WooLogin() {
|
53 |
-
/** @var \
|
54 |
-
$
|
55 |
$sInserts = $this->formInsertsBuild();
|
56 |
-
if ( $
|
57 |
$sInserts .= '<input type="hidden" name="login" value="Log in" />';
|
58 |
}
|
59 |
echo $sInserts;
|
@@ -63,10 +63,10 @@ class WooCommerce extends BaseFormProvider {
|
|
63 |
* @return void
|
64 |
*/
|
65 |
public function formInsertsPrint_WooRegister() {
|
66 |
-
/** @var \
|
67 |
-
$
|
68 |
$sInserts = $this->formInsertsBuild();
|
69 |
-
if ( $
|
70 |
$sInserts .= '<input type="hidden" name="register" value="Register" />';
|
71 |
}
|
72 |
echo $sInserts;
|
2 |
|
3 |
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\LoginGuard\Lib\AntiBot\FormProviders;
|
4 |
|
5 |
+
use FernleafSystems\Wordpress\Plugin\Shield\Modules\LoginGuard;
|
6 |
|
7 |
class WooCommerce extends BaseFormProvider {
|
8 |
|
9 |
public function run() {
|
10 |
parent::run();
|
11 |
+
/** @var LoginGuard\Options $opts */
|
12 |
+
$opts = $this->getOptions();
|
13 |
+
if ( $opts->isProtect( 'checkout_woo' ) ) {
|
14 |
$this->woocheckout();
|
15 |
}
|
16 |
}
|
50 |
* @return void
|
51 |
*/
|
52 |
public function formInsertsPrint_WooLogin() {
|
53 |
+
/** @var LoginGuard\ModCon $mod */
|
54 |
+
$mod = $this->getMod();
|
55 |
$sInserts = $this->formInsertsBuild();
|
56 |
+
if ( $mod->getCaptchaCfg()->invisible ) {
|
57 |
$sInserts .= '<input type="hidden" name="login" value="Log in" />';
|
58 |
}
|
59 |
echo $sInserts;
|
63 |
* @return void
|
64 |
*/
|
65 |
public function formInsertsPrint_WooRegister() {
|
66 |
+
/** @var LoginGuard\ModCon $mod */
|
67 |
+
$mod = $this->getMod();
|
68 |
$sInserts = $this->formInsertsBuild();
|
69 |
+
if ( $mod->getCaptchaCfg()->invisible ) {
|
70 |
$sInserts .= '<input type="hidden" name="register" value="Register" />';
|
71 |
}
|
72 |
echo $sInserts;
|
src/lib/src/Modules/LoginGuard/Lib/AntiBot/ProtectionProviders/GaspJs.php
CHANGED
@@ -22,13 +22,13 @@ class GaspJs extends BaseProtectionProvider {
|
|
22 |
return;
|
23 |
}
|
24 |
|
25 |
-
/** @var \
|
26 |
-
$
|
27 |
$this->setFactorTested( true );
|
28 |
|
29 |
-
$
|
30 |
-
$sGaspCheckBox = $
|
31 |
-
$sHoney = $
|
32 |
|
33 |
$sUsername = $oForm->getUserToAudit();
|
34 |
$sActionAttempted = $oForm->getActionToAudit();
|
@@ -71,10 +71,10 @@ class GaspJs extends BaseProtectionProvider {
|
|
71 |
|
72 |
public function onWpEnqueueJs() {
|
73 |
$con = $this->getCon();
|
74 |
-
/** @var \
|
75 |
$mod = $this->getMod();
|
76 |
-
/** @var LoginGuard\Options $
|
77 |
-
$
|
78 |
|
79 |
$sAsset = 'shield-antibot';
|
80 |
$sUnique = $con->prefix( $sAsset );
|
@@ -90,7 +90,7 @@ class GaspJs extends BaseProtectionProvider {
|
|
90 |
$sUnique,
|
91 |
'icwp_wpsf_vars_lpantibot',
|
92 |
[
|
93 |
-
'form_selectors' => implode( ',', $
|
94 |
'uniq' => preg_replace( '#[^a-zA-Z0-9]#', '', apply_filters( 'icwp_shield_lp_gasp_uniqid', uniqid() ) ),
|
95 |
'cbname' => $mod->getGaspKey(),
|
96 |
'strings' => [
|
@@ -99,7 +99,7 @@ class GaspJs extends BaseProtectionProvider {
|
|
99 |
'loading' => __( 'Loading', 'wp-simple-firewall' )
|
100 |
],
|
101 |
'flags' => [
|
102 |
-
'gasp' => $
|
103 |
'captcha' => $mod->isEnabledCaptcha(),
|
104 |
]
|
105 |
]
|
22 |
return;
|
23 |
}
|
24 |
|
25 |
+
/** @var LoginGuard\ModCon $mod */
|
26 |
+
$mod = $this->getMod();
|
27 |
$this->setFactorTested( true );
|
28 |
|
29 |
+
$req = Services::Request();
|
30 |
+
$sGaspCheckBox = $req->post( $mod->getGaspKey() );
|
31 |
+
$sHoney = $req->post( 'icwp_wpsf_login_email' );
|
32 |
|
33 |
$sUsername = $oForm->getUserToAudit();
|
34 |
$sActionAttempted = $oForm->getActionToAudit();
|
71 |
|
72 |
public function onWpEnqueueJs() {
|
73 |
$con = $this->getCon();
|
74 |
+
/** @var LoginGuard\ModCon $mod */
|
75 |
$mod = $this->getMod();
|
76 |
+
/** @var LoginGuard\Options $opts */
|
77 |
+
$opts = $this->getOptions();
|
78 |
|
79 |
$sAsset = 'shield-antibot';
|
80 |
$sUnique = $con->prefix( $sAsset );
|
90 |
$sUnique,
|
91 |
'icwp_wpsf_vars_lpantibot',
|
92 |
[
|
93 |
+
'form_selectors' => implode( ',', $opts->getAntiBotFormSelectors() ),
|
94 |
'uniq' => preg_replace( '#[^a-zA-Z0-9]#', '', apply_filters( 'icwp_shield_lp_gasp_uniqid', uniqid() ) ),
|
95 |
'cbname' => $mod->getGaspKey(),
|
96 |
'strings' => [
|
99 |
'loading' => __( 'Loading', 'wp-simple-firewall' )
|
100 |
],
|
101 |
'flags' => [
|
102 |
+
'gasp' => $opts->isEnabledGaspCheck(),
|
103 |
'captcha' => $mod->isEnabledCaptcha(),
|
104 |
]
|
105 |
]
|
src/lib/src/Modules/LoginGuard/Lib/AntiBot/ProtectionProviders/GoogleRecaptcha.php
CHANGED
@@ -2,6 +2,7 @@
|
|
2 |
|
3 |
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\LoginGuard\Lib\AntiBot\ProtectionProviders;
|
4 |
|
|
|
5 |
use FernleafSystems\Wordpress\Plugin\Shield\Utilities\ReCaptcha\TestRequest;
|
6 |
|
7 |
class GoogleRecaptcha extends BaseProtectionProvider {
|
@@ -50,9 +51,9 @@ class GoogleRecaptcha extends BaseProtectionProvider {
|
|
50 |
* @return string
|
51 |
*/
|
52 |
private function getCaptchaHtml() {
|
53 |
-
/** @var
|
54 |
-
$
|
55 |
-
if ( $
|
56 |
$sExtraStyles = '';
|
57 |
}
|
58 |
else {
|
2 |
|
3 |
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\LoginGuard\Lib\AntiBot\ProtectionProviders;
|
4 |
|
5 |
+
use FernleafSystems\Wordpress\Plugin\Shield\Modules\LoginGuard\ModCon;
|
6 |
use FernleafSystems\Wordpress\Plugin\Shield\Utilities\ReCaptcha\TestRequest;
|
7 |
|
8 |
class GoogleRecaptcha extends BaseProtectionProvider {
|
51 |
* @return string
|
52 |
*/
|
53 |
private function getCaptchaHtml() {
|
54 |
+
/** @var ModCon $mod */
|
55 |
+
$mod = $this->getMod();
|
56 |
+
if ( $mod->getCaptchaCfg()->invisible ) {
|
57 |
$sExtraStyles = '';
|
58 |
}
|
59 |
else {
|
src/lib/src/Modules/LoginGuard/Lib/CooldownRedirect.php
CHANGED
@@ -3,6 +3,7 @@
|
|
3 |
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\LoginGuard\Lib;
|
4 |
|
5 |
use FernleafSystems\Wordpress\Plugin\Shield\Modules;
|
|
|
6 |
use FernleafSystems\Wordpress\Plugin\Shield\Modules\LoginGuard;
|
7 |
use FernleafSystems\Wordpress\Services\Services;
|
8 |
|
@@ -30,10 +31,10 @@ class CooldownRedirect {
|
|
30 |
}
|
31 |
|
32 |
private function renderCooldownPage() {
|
33 |
-
/** @var
|
34 |
-
$
|
35 |
$nTimeRemaining = ( new LoginGuard\Lib\CooldownFlagFile() )
|
36 |
-
->setMod( $
|
37 |
->getCooldownRemaining();
|
38 |
$aData = [
|
39 |
'strings' => [
|
@@ -56,7 +57,7 @@ class CooldownRedirect {
|
|
56 |
];
|
57 |
Services::WpGeneral()
|
58 |
->wpDie(
|
59 |
-
$
|
60 |
);
|
61 |
}
|
62 |
}
|
3 |
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\LoginGuard\Lib;
|
4 |
|
5 |
use FernleafSystems\Wordpress\Plugin\Shield\Modules;
|
6 |
+
use FernleafSystems\Wordpress\Plugin\Shield\Modules\IPs\ModCon;
|
7 |
use FernleafSystems\Wordpress\Plugin\Shield\Modules\LoginGuard;
|
8 |
use FernleafSystems\Wordpress\Services\Services;
|
9 |
|
31 |
}
|
32 |
|
33 |
private function renderCooldownPage() {
|
34 |
+
/** @var ModCon $mod */
|
35 |
+
$mod = $this->getMod();
|
36 |
$nTimeRemaining = ( new LoginGuard\Lib\CooldownFlagFile() )
|
37 |
+
->setMod( $mod )
|
38 |
->getCooldownRemaining();
|
39 |
$aData = [
|
40 |
'strings' => [
|
57 |
];
|
58 |
Services::WpGeneral()
|
59 |
->wpDie(
|
60 |
+
$mod->renderTemplate( '/snippets/cooldown_login_block.twig', $aData, true )
|
61 |
);
|
62 |
}
|
63 |
}
|
src/lib/src/Modules/LoginGuard/Lib/Rename/RenameLogin.php
ADDED
@@ -0,0 +1,244 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\LoginGuard\Lib\Rename;
|
4 |
+
|
5 |
+
use FernleafSystems\Utilities\Logic\OneTimeExecute;
|
6 |
+
use FernleafSystems\Wordpress\Plugin\Shield\Modules;
|
7 |
+
use FernleafSystems\Wordpress\Plugin\Shield\Modules\LoginGuard;
|
8 |
+
use FernleafSystems\Wordpress\Plugin\Shield\Modules\LoginGuard\Options;
|
9 |
+
use FernleafSystems\Wordpress\Services\Services;
|
10 |
+
|
11 |
+
class RenameLogin {
|
12 |
+
|
13 |
+
use Modules\ModConsumer;
|
14 |
+
use OneTimeExecute;
|
15 |
+
|
16 |
+
protected function run() {
|
17 |
+
add_action( 'init', [ $this, 'onWpInit' ], 9 );
|
18 |
+
}
|
19 |
+
|
20 |
+
protected function canRun() {
|
21 |
+
/** @var Options $opts */
|
22 |
+
$opts = $this->getOptions();
|
23 |
+
return !empty( $opts->getCustomLoginPath() )
|
24 |
+
&& !$this->hasPluginConflict() && !$this->hasUnsupportedConfiguration();
|
25 |
+
}
|
26 |
+
|
27 |
+
public function onWpInit() {
|
28 |
+
/** @var LoginGuard\ModCon $mod */
|
29 |
+
$mod = $this->getMod();
|
30 |
+
|
31 |
+
if ( Services::WpGeneral()->isLoginUrl() &&
|
32 |
+
( $mod->isVisitorWhitelisted() || Services::WpUsers()->isUserLoggedIn() ) ) {
|
33 |
+
return;
|
34 |
+
}
|
35 |
+
if ( is_admin() && $mod->isVisitorWhitelisted() && !Services::WpUsers()->isUserLoggedIn() ) {
|
36 |
+
return;
|
37 |
+
}
|
38 |
+
|
39 |
+
$this->doBlockPossibleWpLoginLoad();
|
40 |
+
|
41 |
+
// Loads the wp-login.php if the correct URL is loaded
|
42 |
+
add_action( 'wp_loaded', [ $this, 'aLoadWpLogin' ] );
|
43 |
+
|
44 |
+
// Shouldn't be necessary, but in-case something else includes the wp-login.php, we block that too.
|
45 |
+
add_action( 'login_init', [ $this, 'aLoginFormAction' ], 0 );
|
46 |
+
|
47 |
+
// ensure that wp-login.php is never used in site urls or redirects
|
48 |
+
add_filter( 'site_url', [ $this, 'fCheckForLoginPhp' ], 20, 1 );
|
49 |
+
add_filter( 'network_site_url', [ $this, 'fCheckForLoginPhp' ], 20, 1 );
|
50 |
+
add_filter( 'wp_redirect', [ $this, 'fCheckForLoginPhp' ], 20, 1 );
|
51 |
+
if ( !Services::WpUsers()->isUserLoggedIn() ) {
|
52 |
+
add_filter( 'wp_redirect', [ $this, 'fProtectUnauthorizedLoginRedirect' ], 50, 1 );
|
53 |
+
}
|
54 |
+
add_filter( 'register_url', [ $this, 'blockRegisterUrlRedirect' ], 20, 1 );
|
55 |
+
|
56 |
+
add_filter( 'et_anticipate_exceptions', [ $this, 'fAddToEtMaintenanceExceptions' ] );
|
57 |
+
}
|
58 |
+
|
59 |
+
private function hasPluginConflict() :bool {
|
60 |
+
/** @var LoginGuard\ModCon $mod */
|
61 |
+
$mod = $this->getMod();
|
62 |
+
/** @var LoginGuard\Options $opts */
|
63 |
+
$opts = $this->getOptions();
|
64 |
+
|
65 |
+
$sMessage = '';
|
66 |
+
$bConflicted = false;
|
67 |
+
|
68 |
+
$path = $opts->getCustomLoginPath();
|
69 |
+
|
70 |
+
$WP = Services::WpGeneral();
|
71 |
+
if ( $WP->isMultisite() ) {
|
72 |
+
$sMessage = __( 'Your login URL is unchanged because the Rename WP Login feature is not currently supported on WPMS.', 'wp-simple-firewall' );
|
73 |
+
$bConflicted = true;
|
74 |
+
}
|
75 |
+
elseif ( class_exists( 'Rename_WP_Login' ) ) {
|
76 |
+
$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' );
|
77 |
+
$bConflicted = true;
|
78 |
+
}
|
79 |
+
elseif ( class_exists( 'Theme_My_Login' ) ) {
|
80 |
+
$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' );
|
81 |
+
$bConflicted = true;
|
82 |
+
}
|
83 |
+
elseif ( !$WP->isPermalinksEnabled() ) {
|
84 |
+
$sMessage = sprintf( __( 'Can not use the Rename WP Login feature because you have not enabled %s.', 'wp-simple-firewall' ), __( 'Permalinks' ) );
|
85 |
+
$bConflicted = true;
|
86 |
+
}
|
87 |
+
elseif ( $WP->isPermalinksEnabled() && ( $WP->getDoesWpSlugExist( $path ) || in_array( $path, $WP->getAutoRedirectLocations() ) ) ) {
|
88 |
+
$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 );
|
89 |
+
$bConflicted = true;
|
90 |
+
}
|
91 |
+
|
92 |
+
if ( $bConflicted ) {
|
93 |
+
$sNoticeMessage = sprintf( '<strong>%s</strong>: %s',
|
94 |
+
__( 'Warning', 'wp-simple-firewall' ),
|
95 |
+
$sMessage
|
96 |
+
);
|
97 |
+
$mod->setFlashAdminNotice( $sNoticeMessage, true );
|
98 |
+
}
|
99 |
+
|
100 |
+
return $bConflicted;
|
101 |
+
}
|
102 |
+
|
103 |
+
private function hasUnsupportedConfiguration() :bool {
|
104 |
+
/** @var LoginGuard\ModCon $mod */
|
105 |
+
$mod = $this->getMod();
|
106 |
+
$path = Services::Request()->getPath();
|
107 |
+
|
108 |
+
$unsupported = empty( $path );
|
109 |
+
if ( $unsupported ) {
|
110 |
+
$mod->setFlashAdminNotice(
|
111 |
+
sprintf(
|
112 |
+
'<strong>%s</strong>: %s',
|
113 |
+
__( 'Warning', 'wp-simple-firewall' ),
|
114 |
+
__( 'Your login URL is unchanged because your current hosting/PHP configuration cannot parse the necessary information.', 'wp-simple-firewall' )
|
115 |
+
),
|
116 |
+
true
|
117 |
+
);
|
118 |
+
}
|
119 |
+
|
120 |
+
return $unsupported;
|
121 |
+
}
|
122 |
+
|
123 |
+
public function doBlockPossibleWpLoginLoad() {
|
124 |
+
|
125 |
+
// 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)
|
126 |
+
$bDoBlock = is_admin() && !Services::WpGeneral()->isAjax()
|
127 |
+
&& !Services::WpUsers()->isUserLoggedIn();
|
128 |
+
|
129 |
+
// Next block option is where it's a direct attempt to access the old login URL
|
130 |
+
if ( !$bDoBlock ) {
|
131 |
+
$path = trim( Services::Request()->getPath(), '/' );
|
132 |
+
$possible = [
|
133 |
+
trim( home_url( 'wp-login.php', 'relative' ), '/' ),
|
134 |
+
trim( home_url( 'wp-signup.php', 'relative' ), '/' ),
|
135 |
+
trim( site_url( 'wp-signup.php', 'relative' ), '/' ),
|
136 |
+
// trim( site_url( 'wp-login.php', 'relative' ), '/' ), our own filters in run() scuttle us here so we have to build it manually
|
137 |
+
trim( rtrim( site_url( '', 'relative' ), '/' ).'/wp-login.php', '/' ),
|
138 |
+
trim( home_url( 'login', 'relative' ), '/' ),
|
139 |
+
trim( site_url( 'login', 'relative' ), '/' )
|
140 |
+
];
|
141 |
+
$bDoBlock = !empty( $path )
|
142 |
+
&& ( in_array( $path, $possible ) || preg_match( '/wp-login\.php/i', $path ) );
|
143 |
+
}
|
144 |
+
|
145 |
+
if ( $bDoBlock ) {
|
146 |
+
$this->doWpLoginFailedRedirect404();
|
147 |
+
}
|
148 |
+
}
|
149 |
+
|
150 |
+
/**
|
151 |
+
* @param string $sLocation
|
152 |
+
* @return string
|
153 |
+
*/
|
154 |
+
public function fCheckForLoginPhp( $sLocation ) {
|
155 |
+
/** @var LoginGuard\Options $opts */
|
156 |
+
$opts = $this->getOptions();
|
157 |
+
|
158 |
+
$sRedirectPath = parse_url( $sLocation, PHP_URL_PATH );
|
159 |
+
if ( strpos( $sRedirectPath, 'wp-login.php' ) !== false ) {
|
160 |
+
|
161 |
+
$sLoginUrl = home_url( $opts->getCustomLoginPath() );
|
162 |
+
$aQueryArgs = explode( '?', $sLocation );
|
163 |
+
if ( !empty( $aQueryArgs[ 1 ] ) ) {
|
164 |
+
parse_str( $aQueryArgs[ 1 ], $aNewQueryArgs );
|
165 |
+
$sLoginUrl = add_query_arg( $aNewQueryArgs, $sLoginUrl );
|
166 |
+
}
|
167 |
+
return $sLoginUrl;
|
168 |
+
}
|
169 |
+
return $sLocation;
|
170 |
+
}
|
171 |
+
|
172 |
+
/**
|
173 |
+
* @param string $sLocation
|
174 |
+
* @return string
|
175 |
+
*/
|
176 |
+
public function fProtectUnauthorizedLoginRedirect( $sLocation ) {
|
177 |
+
/** @var LoginGuard\Options $opts */
|
178 |
+
$opts = $this->getOptions();
|
179 |
+
|
180 |
+
if ( !Services::WpGeneral()->isLoginUrl() ) {
|
181 |
+
$sRedirectPath = trim( parse_url( $sLocation, PHP_URL_PATH ), '/' );
|
182 |
+
$bRedirectIsHiddenUrl = ( $sRedirectPath == $opts->getCustomLoginPath() );
|
183 |
+
if ( $bRedirectIsHiddenUrl && !Services::WpUsers()->isUserLoggedIn() ) {
|
184 |
+
$this->doWpLoginFailedRedirect404();
|
185 |
+
}
|
186 |
+
}
|
187 |
+
return $sLocation;
|
188 |
+
}
|
189 |
+
|
190 |
+
/**
|
191 |
+
* @param string $url
|
192 |
+
* @return string
|
193 |
+
*/
|
194 |
+
public function blockRegisterUrlRedirect( $url ) {
|
195 |
+
if ( strpos( Services::Request()->getPath(), 'wp-register.php' ) ) {
|
196 |
+
$this->doWpLoginFailedRedirect404();
|
197 |
+
die();
|
198 |
+
}
|
199 |
+
return $url;
|
200 |
+
}
|
201 |
+
|
202 |
+
public function aLoadWpLogin() {
|
203 |
+
if ( Services::WpGeneral()->isLoginUrl() ) {
|
204 |
+
@require_once( ABSPATH.'wp-login.php' );
|
205 |
+
die();
|
206 |
+
}
|
207 |
+
}
|
208 |
+
|
209 |
+
public function aLoginFormAction() {
|
210 |
+
if ( !Services::WpGeneral()->isLoginUrl() ) {
|
211 |
+
$this->doWpLoginFailedRedirect404();
|
212 |
+
die();
|
213 |
+
}
|
214 |
+
}
|
215 |
+
|
216 |
+
/**
|
217 |
+
* Add the custom login URL to the Elegant Themes Maintenance Mode plugin URL exceptions list
|
218 |
+
* @param array $aUrlExceptions
|
219 |
+
* @return array
|
220 |
+
*/
|
221 |
+
public function fAddToEtMaintenanceExceptions( $aUrlExceptions ) {
|
222 |
+
/** @var LoginGuard\Options $opts */
|
223 |
+
$opts = $this->getOptions();
|
224 |
+
$aUrlExceptions[] = $opts->getCustomLoginPath();
|
225 |
+
return $aUrlExceptions;
|
226 |
+
}
|
227 |
+
|
228 |
+
/**
|
229 |
+
* Will by default send a 404 response screen. Has a filter to specify redirect URL.
|
230 |
+
*/
|
231 |
+
protected function doWpLoginFailedRedirect404() {
|
232 |
+
$this->getCon()->fireEvent( 'hide_login_url' );
|
233 |
+
|
234 |
+
$sRedirectUrl = apply_filters( 'icwp_shield_renamewplogin_redirect_url', false );
|
235 |
+
if ( !empty( $sRedirectUrl ) ) {
|
236 |
+
$sRedirectUrl = esc_url( $sRedirectUrl );
|
237 |
+
if ( @parse_url( $sRedirectUrl ) !== false ) {
|
238 |
+
Services::Response()->redirect( $sRedirectUrl, [], false );
|
239 |
+
}
|
240 |
+
}
|
241 |
+
|
242 |
+
Services::Response()->sendApache404( '', Services::WpGeneral()->getHomeUrl() );
|
243 |
+
}
|
244 |
+
}
|
src/lib/src/Modules/LoginGuard/Lib/TwoFactor/LoginIntentPage.php
CHANGED
@@ -20,7 +20,7 @@ class LoginIntentPage {
|
|
20 |
*/
|
21 |
public function renderForm() {
|
22 |
$oIC = $this->getMfaCon();
|
23 |
-
/** @var \
|
24 |
$mod = $oIC->getMod();
|
25 |
/** @var LoginGuard\Options $opts */
|
26 |
$opts = $oIC->getOptions();
|
@@ -96,7 +96,7 @@ class LoginIntentPage {
|
|
96 |
],
|
97 |
'flags' => [
|
98 |
'can_skip_mfa' => $opts->isMfaSkip(),
|
99 |
-
'show_branded_links' => !$
|
100 |
]
|
101 |
];
|
102 |
|
@@ -113,34 +113,34 @@ class LoginIntentPage {
|
|
113 |
*/
|
114 |
private function renderPage() {
|
115 |
$oIC = $this->getMfaCon();
|
116 |
-
/** @var \
|
117 |
-
$
|
118 |
-
$
|
119 |
-
$
|
120 |
-
|
121 |
-
$aLabels = $
|
122 |
-
$sBannerUrl = empty( $aLabels[ 'url_login2fa_logourl' ] ) ? $
|
123 |
-
$nTimeRemaining = $
|
124 |
$aDisplayData = [
|
125 |
'strings' => [
|
126 |
'what_is_this' => __( 'What is this?', 'wp-simple-firewall' ),
|
127 |
-
'page_title' => sprintf( __( '%s Login Verification', 'wp-simple-firewall' ), $
|
128 |
],
|
129 |
'data' => [
|
130 |
'time_remaining' => $nTimeRemaining,
|
131 |
],
|
132 |
'hrefs' => [
|
133 |
-
'css_bootstrap' => $
|
134 |
-
'js_bootstrap' => $
|
135 |
'shield_logo' => 'https://ps.w.org/wp-simple-firewall/assets/banner-772x250.png',
|
136 |
'what_is_this' => 'https://icontrolwp.freshdesk.com/support/solutions/articles/3000064840',
|
137 |
],
|
138 |
'imgs' => [
|
139 |
'banner' => $sBannerUrl,
|
140 |
-
'favicon' => $
|
141 |
],
|
142 |
'flags' => [
|
143 |
-
'show_branded_links' => !$
|
144 |
'has_u2f' => isset( $oIC->getProvidersForUser(
|
145 |
Services::WpUsers()->getCurrentWpUser(), true )[ LoginGuard\Lib\TwoFactor\Provider\U2F::SLUG ] )
|
146 |
],
|
@@ -154,17 +154,17 @@ class LoginIntentPage {
|
|
154 |
$aDisplayData[ 'head' ] = [
|
155 |
'scripts' => [
|
156 |
[
|
157 |
-
'src' => $
|
158 |
],
|
159 |
[
|
160 |
-
'src' => $
|
161 |
]
|
162 |
]
|
163 |
];
|
164 |
}
|
165 |
|
166 |
-
return $
|
167 |
Services::DataManipulation()->mergeArraysRecursive(
|
168 |
-
$
|
169 |
}
|
170 |
}
|
20 |
*/
|
21 |
public function renderForm() {
|
22 |
$oIC = $this->getMfaCon();
|
23 |
+
/** @var LoginGuard\ModCon $mod */
|
24 |
$mod = $oIC->getMod();
|
25 |
/** @var LoginGuard\Options $opts */
|
26 |
$opts = $oIC->getOptions();
|
96 |
],
|
97 |
'flags' => [
|
98 |
'can_skip_mfa' => $opts->isMfaSkip(),
|
99 |
+
'show_branded_links' => !$con->getModule_SecAdmin()->isWlEnabled(), // white label mitigation
|
100 |
]
|
101 |
];
|
102 |
|
113 |
*/
|
114 |
private function renderPage() {
|
115 |
$oIC = $this->getMfaCon();
|
116 |
+
/** @var LoginGuard\ModCon $mod */
|
117 |
+
$mod = $oIC->getMod();
|
118 |
+
$con = $oIC->getCon();
|
119 |
+
$req = Services::Request();
|
120 |
+
|
121 |
+
$aLabels = $con->getLabels();
|
122 |
+
$sBannerUrl = empty( $aLabels[ 'url_login2fa_logourl' ] ) ? $con->getPluginUrl_Image( 'pluginlogo_banner-772x250.png' ) : $aLabels[ 'url_login2fa_logourl' ];
|
123 |
+
$nTimeRemaining = $mod->getSession()->login_intent_expires_at - $req->ts();
|
124 |
$aDisplayData = [
|
125 |
'strings' => [
|
126 |
'what_is_this' => __( 'What is this?', 'wp-simple-firewall' ),
|
127 |
+
'page_title' => sprintf( __( '%s Login Verification', 'wp-simple-firewall' ), $con->getHumanName() ),
|
128 |
],
|
129 |
'data' => [
|
130 |
'time_remaining' => $nTimeRemaining,
|
131 |
],
|
132 |
'hrefs' => [
|
133 |
+
'css_bootstrap' => $con->getPluginUrl_Css( 'bootstrap4.min' ),
|
134 |
+
'js_bootstrap' => $con->getPluginUrl_Js( 'bootstrap4.min' ),
|
135 |
'shield_logo' => 'https://ps.w.org/wp-simple-firewall/assets/banner-772x250.png',
|
136 |
'what_is_this' => 'https://icontrolwp.freshdesk.com/support/solutions/articles/3000064840',
|
137 |
],
|
138 |
'imgs' => [
|
139 |
'banner' => $sBannerUrl,
|
140 |
+
'favicon' => $con->getPluginUrl_Image( 'pluginlogo_24x24.png' ),
|
141 |
],
|
142 |
'flags' => [
|
143 |
+
'show_branded_links' => !$con->getModule_SecAdmin()->isWlEnabled(), // white label mitigation
|
144 |
'has_u2f' => isset( $oIC->getProvidersForUser(
|
145 |
Services::WpUsers()->getCurrentWpUser(), true )[ LoginGuard\Lib\TwoFactor\Provider\U2F::SLUG ] )
|
146 |
],
|
154 |
$aDisplayData[ 'head' ] = [
|
155 |
'scripts' => [
|
156 |
[
|
157 |
+
'src' => $con->getPluginUrl_Js( 'u2f-bundle.js' ),
|
158 |
],
|
159 |
[
|
160 |
+
'src' => $con->getPluginUrl_Js( 'u2f-frontend.js' ),
|
161 |
]
|
162 |
]
|
163 |
];
|
164 |
}
|
165 |
|
166 |
+
return $mod->renderTemplate( '/pages/login_intent/index.twig',
|
167 |
Services::DataManipulation()->mergeArraysRecursive(
|
168 |
+
$mod->getUIHandler()->getBaseDisplayData(), $aDisplayData ), true );
|
169 |
}
|
170 |
}
|
src/lib/src/Modules/LoginGuard/Lib/TwoFactor/MfaController.php
CHANGED
@@ -11,17 +11,13 @@ use FernleafSystems\Wordpress\Services\Services;
|
|
11 |
class MfaController {
|
12 |
|
13 |
use Shield\Modules\ModConsumer;
|
|
|
14 |
|
15 |
/**
|
16 |
* @var Provider\BaseProvider[]
|
17 |
*/
|
18 |
private $aProviders;
|
19 |
|
20 |
-
/**
|
21 |
-
* @var bool
|
22 |
-
*/
|
23 |
-
protected $bLoginAttemptCaptured;
|
24 |
-
|
25 |
/**
|
26 |
* @var LoginIntentPage
|
27 |
*/
|
@@ -29,23 +25,15 @@ class MfaController {
|
|
29 |
|
30 |
public function run() {
|
31 |
add_action( 'init', [ $this, 'onWpInit' ], 10, 2 );
|
32 |
-
add_action( 'wp_login', [ $this, 'onWpLogin' ], 10, 2 );
|
33 |
-
if ( !Services::WpUsers()->isProfilePage() ) { // This can be fired during profile update.
|
34 |
-
add_action( 'set_logged_in_cookie', [ $this, 'onWpSetLoggedInCookie' ], 5, 4 );
|
35 |
-
}
|
36 |
add_action( 'wp_loaded', [ $this, 'onWpLoaded' ], 10, 2 );
|
|
|
37 |
}
|
38 |
|
39 |
public function onWpInit() {
|
40 |
-
$
|
41 |
-
|
42 |
-
|
43 |
-
|
44 |
-
* @param string $sUsername
|
45 |
-
* @param \WP_User $oUser
|
46 |
-
*/
|
47 |
-
public function onWpLogin( $sUsername, $oUser ) {
|
48 |
-
$this->captureLoginIntent( $oUser );
|
49 |
}
|
50 |
|
51 |
public function onWpLoaded() {
|
@@ -58,51 +46,34 @@ class MfaController {
|
|
58 |
} );
|
59 |
}
|
60 |
|
61 |
-
|
62 |
-
|
63 |
-
* @param int $nExpire
|
64 |
-
* @param int $nExpiration
|
65 |
-
* @param int $nUserId
|
66 |
-
*/
|
67 |
-
public function onWpSetLoggedInCookie( $sCookie, $nExpire, $nExpiration, $nUserId ) {
|
68 |
-
$this->captureLoginIntent( Services::WpUsers()->getUserById( $nUserId ) );
|
69 |
}
|
70 |
|
71 |
-
|
72 |
-
|
73 |
-
|
74 |
-
|
75 |
-
|
76 |
-
$this->
|
77 |
-
|
78 |
-
|
79 |
-
|
80 |
-
if ( $this->isSubjectToLoginIntent( $oUser ) && !$this->canUserMfaSkip( $oUser ) ) {
|
81 |
-
|
82 |
-
$aProviders = $this->getProvidersForUser( $oUser );
|
83 |
-
if ( !empty( $aProviders ) ) {
|
84 |
-
foreach ( $aProviders as $oProvider ) {
|
85 |
-
$oProvider->captureLoginAttempt( $oUser );
|
86 |
-
}
|
87 |
-
|
88 |
-
$this->setLoginIntentExpiresAt(
|
89 |
-
Services::Request()
|
90 |
-
->carbon()
|
91 |
-
->addMinutes( $opts->getLoginIntentMinutes() )->timestamp
|
92 |
-
);
|
93 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
94 |
}
|
95 |
}
|
96 |
}
|
97 |
|
98 |
-
|
99 |
-
|
100 |
-
*/
|
101 |
-
private function assessLoginIntent() {
|
102 |
-
$oUser = Services::WpUsers()->getCurrentWpUser();
|
103 |
-
if ( $oUser instanceof \WP_User && $this->hasLoginIntent() ) {
|
104 |
|
105 |
-
if ( $this->isSubjectToLoginIntent( $
|
106 |
|
107 |
if ( $this->getLoginIntentExpiresAt() > Services::Request()->ts() ) {
|
108 |
$this->processActiveLoginIntent();
|
@@ -120,10 +91,7 @@ class MfaController {
|
|
120 |
}
|
121 |
}
|
122 |
|
123 |
-
|
124 |
-
* @return LoginIntentPage
|
125 |
-
*/
|
126 |
-
private function getLoginIntentPageHandler() {
|
127 |
if ( !isset( $this->oLoginIntentPageHandler ) ) {
|
128 |
$this->oLoginIntentPageHandler = ( new LoginIntentPage() )->setMfaController( $this );
|
129 |
}
|
@@ -133,7 +101,7 @@ class MfaController {
|
|
133 |
/**
|
134 |
* @return Provider\BaseProvider[]
|
135 |
*/
|
136 |
-
public function getProviders() {
|
137 |
if ( !is_array( $this->aProviders ) ) {
|
138 |
$this->aProviders = [
|
139 |
Provider\Email::SLUG => ( new Provider\Email() )->setMod( $this->getMod() ),
|
@@ -148,81 +116,81 @@ class MfaController {
|
|
148 |
|
149 |
/**
|
150 |
* Ensures that BackupCode provider isn't supplied on its own, and the user profile is setup for each.
|
151 |
-
* @param \WP_User $
|
152 |
* @param bool $bOnlyActiveProfiles
|
153 |
* @return Provider\BaseProvider[]
|
154 |
*/
|
155 |
-
public function getProvidersForUser( $
|
156 |
-
$
|
157 |
-
function ( $oProvider ) use ( $
|
158 |
/** @var Provider\BaseProvider $oProvider */
|
159 |
-
return $oProvider->isProviderAvailableToUser( $
|
160 |
-
&& ( !$bOnlyActiveProfiles || $oProvider->isProfileActive( $
|
161 |
}
|
162 |
);
|
163 |
|
164 |
// Neither BackupCode NOR U2F should EVER be the only 1 provider available.
|
165 |
-
if ( count( $
|
166 |
/** @var Provider\BaseProvider $oFirst */
|
167 |
-
$oFirst = reset( $
|
168 |
if ( !$oFirst::STANDALONE ) {
|
169 |
-
$
|
170 |
}
|
171 |
}
|
172 |
-
return $
|
173 |
}
|
174 |
|
175 |
/**
|
176 |
* hooked to 'init' and only run if a user is logged-in (not on the login request)
|
177 |
*/
|
178 |
private function processActiveLoginIntent() {
|
179 |
-
/** @var LoginGuard\Options $
|
180 |
-
$
|
181 |
-
$
|
182 |
-
$
|
183 |
-
$
|
184 |
-
$
|
185 |
|
186 |
// Is 2FA/login-intent submit
|
187 |
-
if ( $
|
188 |
|
189 |
-
if ( $
|
190 |
-
$
|
191 |
-
$sRedirectHref = $
|
192 |
-
empty( $sRedirectHref ) ? $
|
193 |
}
|
194 |
elseif ( $this->validateLoginIntentRequest() ) {
|
195 |
|
196 |
-
if ( $
|
197 |
( new MfaSkip() )
|
198 |
->setMod( $this->getMod() )
|
199 |
-
->addMfaSkip( $
|
200 |
}
|
201 |
|
202 |
-
$
|
203 |
|
204 |
$sFlash = __( 'Success', 'wp-simple-firewall' ).'! '.__( 'Thank you for authenticating your login.', 'wp-simple-firewall' );
|
205 |
-
if ( $
|
206 |
$sFlash .= ' '.__( 'If you used your Backup Code, you will need to reset it.', 'wp-simple-firewall' ); //TODO::
|
207 |
}
|
208 |
$this->getMod()->setFlashAdminNotice( $sFlash );
|
209 |
|
210 |
$this->removeLoginIntent();
|
211 |
|
212 |
-
$sRedirectHref = $
|
213 |
-
empty( $sRedirectHref ) ? $
|
214 |
}
|
215 |
else {
|
216 |
-
$
|
217 |
-
|
218 |
-
|
219 |
-
|
220 |
-
|
221 |
// We don't protect against loops here to prevent bypassing of the login intent page.
|
222 |
Services::Response()->redirect( Services::Request()->getUri(), [], true, false );
|
223 |
}
|
224 |
}
|
225 |
-
elseif ( $
|
226 |
$this->getLoginIntentPageHandler()->loadPage();
|
227 |
}
|
228 |
die();
|
@@ -234,54 +202,40 @@ class MfaController {
|
|
234 |
*/
|
235 |
private function validateLoginIntentRequest() {
|
236 |
try {
|
237 |
-
$
|
238 |
->setMfaController( $this )
|
239 |
->run();
|
240 |
}
|
241 |
-
catch ( \Exception $
|
242 |
-
$
|
243 |
}
|
244 |
-
return $
|
245 |
}
|
246 |
|
247 |
-
|
248 |
-
|
249 |
-
* @return bool
|
250 |
-
*/
|
251 |
-
private function canUserMfaSkip( $oUser ) {
|
252 |
-
$bCanSkip = ( new MfaSkip() )
|
253 |
->setMod( $this->getMod() )
|
254 |
-
->canMfaSkip( $
|
255 |
|
256 |
-
if ( !$
|
257 |
// custom support for WooCommerce Social login
|
258 |
-
$
|
259 |
-
$
|
260 |
}
|
261 |
|
262 |
-
return apply_filters( '
|
|
|
263 |
}
|
264 |
|
265 |
-
|
266 |
-
|
267 |
-
* @return bool
|
268 |
-
*/
|
269 |
-
private function isSubjectToLoginIntent( $oUser ) {
|
270 |
-
return count( $this->getProvidersForUser( $oUser, true ) ) > 0;
|
271 |
}
|
272 |
|
273 |
-
|
274 |
-
|
275 |
-
|
276 |
-
|
277 |
-
|
278 |
-
}
|
279 |
-
|
280 |
-
/**
|
281 |
-
* @return bool
|
282 |
-
*/
|
283 |
-
protected function hasLoginIntent() {
|
284 |
-
return $this->getLoginIntentExpiresAt() > 0;
|
285 |
}
|
286 |
|
287 |
/**
|
@@ -292,25 +246,26 @@ class MfaController {
|
|
292 |
return $this->setLoginIntentExpiresAt( 0 );
|
293 |
}
|
294 |
|
295 |
-
|
296 |
-
|
297 |
-
|
298 |
-
|
299 |
-
|
300 |
-
|
301 |
-
|
302 |
-
if ( $oMod->hasSession() ) {
|
303 |
-
/** @var Update $oUpd */
|
304 |
-
$oUpd = $oMod->getDbHandler_Sessions()->getQueryUpdater();
|
305 |
-
$oUpd->updateLoginIntentExpiresAt( $oMod->getSession(), $nExpirationTime );
|
306 |
}
|
307 |
return $this;
|
308 |
}
|
309 |
|
|
|
|
|
|
|
|
|
310 |
/**
|
311 |
-
* @return
|
|
|
312 |
*/
|
313 |
-
private function
|
314 |
-
return $this->
|
315 |
}
|
316 |
}
|
11 |
class MfaController {
|
12 |
|
13 |
use Shield\Modules\ModConsumer;
|
14 |
+
use Shield\Utilities\Consumer\WpLoginCapture;
|
15 |
|
16 |
/**
|
17 |
* @var Provider\BaseProvider[]
|
18 |
*/
|
19 |
private $aProviders;
|
20 |
|
|
|
|
|
|
|
|
|
|
|
21 |
/**
|
22 |
* @var LoginIntentPage
|
23 |
*/
|
25 |
|
26 |
public function run() {
|
27 |
add_action( 'init', [ $this, 'onWpInit' ], 10, 2 );
|
|
|
|
|
|
|
|
|
28 |
add_action( 'wp_loaded', [ $this, 'onWpLoaded' ], 10, 2 );
|
29 |
+
$this->setupLoginCaptureHooks();
|
30 |
}
|
31 |
|
32 |
public function onWpInit() {
|
33 |
+
$user = Services::WpUsers()->getCurrentWpUser();
|
34 |
+
if ( $user instanceof \WP_User ) {
|
35 |
+
$this->assessLoginIntent( $user );
|
36 |
+
}
|
|
|
|
|
|
|
|
|
|
|
37 |
}
|
38 |
|
39 |
public function onWpLoaded() {
|
46 |
} );
|
47 |
}
|
48 |
|
49 |
+
protected function captureLogin( \WP_User $user ) {
|
50 |
+
$this->captureLoginIntent( $user );
|
|
|
|
|
|
|
|
|
|
|
|
|
51 |
}
|
52 |
|
53 |
+
private function captureLoginIntent( \WP_User $user ) {
|
54 |
+
/** @var LoginGuard\Options $opts */
|
55 |
+
$opts = $this->getOptions();
|
56 |
+
if ( $this->isSubjectToLoginIntent( $user ) && !$this->canUserMfaSkip( $user ) ) {
|
57 |
+
|
58 |
+
$providers = $this->getProvidersForUser( $user );
|
59 |
+
if ( !empty( $providers ) ) {
|
60 |
+
foreach ( $providers as $provider ) {
|
61 |
+
$provider->captureLoginAttempt( $user );
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
62 |
}
|
63 |
+
|
64 |
+
$this->setLoginIntentExpiresAt(
|
65 |
+
Services::Request()
|
66 |
+
->carbon()
|
67 |
+
->addMinutes( $opts->getLoginIntentMinutes() )->timestamp
|
68 |
+
);
|
69 |
}
|
70 |
}
|
71 |
}
|
72 |
|
73 |
+
private function assessLoginIntent( \WP_User $user ) {
|
74 |
+
if ( $this->getLoginIntentExpiresAt() > 0 ) {
|
|
|
|
|
|
|
|
|
75 |
|
76 |
+
if ( $this->isSubjectToLoginIntent( $user ) ) {
|
77 |
|
78 |
if ( $this->getLoginIntentExpiresAt() > Services::Request()->ts() ) {
|
79 |
$this->processActiveLoginIntent();
|
91 |
}
|
92 |
}
|
93 |
|
94 |
+
private function getLoginIntentPageHandler() :LoginIntentPage {
|
|
|
|
|
|
|
95 |
if ( !isset( $this->oLoginIntentPageHandler ) ) {
|
96 |
$this->oLoginIntentPageHandler = ( new LoginIntentPage() )->setMfaController( $this );
|
97 |
}
|
101 |
/**
|
102 |
* @return Provider\BaseProvider[]
|
103 |
*/
|
104 |
+
public function getProviders() :array {
|
105 |
if ( !is_array( $this->aProviders ) ) {
|
106 |
$this->aProviders = [
|
107 |
Provider\Email::SLUG => ( new Provider\Email() )->setMod( $this->getMod() ),
|
116 |
|
117 |
/**
|
118 |
* Ensures that BackupCode provider isn't supplied on its own, and the user profile is setup for each.
|
119 |
+
* @param \WP_User $user
|
120 |
* @param bool $bOnlyActiveProfiles
|
121 |
* @return Provider\BaseProvider[]
|
122 |
*/
|
123 |
+
public function getProvidersForUser( $user, $bOnlyActiveProfiles = false ) {
|
124 |
+
$Ps = array_filter( $this->getProviders(),
|
125 |
+
function ( $oProvider ) use ( $user, $bOnlyActiveProfiles ) {
|
126 |
/** @var Provider\BaseProvider $oProvider */
|
127 |
+
return $oProvider->isProviderAvailableToUser( $user )
|
128 |
+
&& ( !$bOnlyActiveProfiles || $oProvider->isProfileActive( $user ) );
|
129 |
}
|
130 |
);
|
131 |
|
132 |
// Neither BackupCode NOR U2F should EVER be the only 1 provider available.
|
133 |
+
if ( count( $Ps ) === 1 ) {
|
134 |
/** @var Provider\BaseProvider $oFirst */
|
135 |
+
$oFirst = reset( $Ps );
|
136 |
if ( !$oFirst::STANDALONE ) {
|
137 |
+
$Ps = [];
|
138 |
}
|
139 |
}
|
140 |
+
return $Ps;
|
141 |
}
|
142 |
|
143 |
/**
|
144 |
* hooked to 'init' and only run if a user is logged-in (not on the login request)
|
145 |
*/
|
146 |
private function processActiveLoginIntent() {
|
147 |
+
/** @var LoginGuard\Options $opts */
|
148 |
+
$opts = $this->getOptions();
|
149 |
+
$con = $this->getCon();
|
150 |
+
$req = Services::Request();
|
151 |
+
$WPResp = Services::Response();
|
152 |
+
$WPUsers = Services::WpUsers();
|
153 |
|
154 |
// Is 2FA/login-intent submit
|
155 |
+
if ( $req->request( $this->getLoginIntentRequestFlag() ) == 1 ) {
|
156 |
|
157 |
+
if ( $req->post( 'cancel' ) == 1 ) {
|
158 |
+
$WPUsers->logoutUser(); // clears the login and login intent
|
159 |
+
$sRedirectHref = $req->post( 'cancel_href' );
|
160 |
+
empty( $sRedirectHref ) ? $WPResp->redirectToLogin() : $WPResp->redirect( $sRedirectHref );
|
161 |
}
|
162 |
elseif ( $this->validateLoginIntentRequest() ) {
|
163 |
|
164 |
+
if ( $req->post( 'skip_mfa' ) === 'Y' ) {
|
165 |
( new MfaSkip() )
|
166 |
->setMod( $this->getMod() )
|
167 |
+
->addMfaSkip( $WPUsers->getCurrentWpUser() );
|
168 |
}
|
169 |
|
170 |
+
$con->fireEvent( '2fa_success' );
|
171 |
|
172 |
$sFlash = __( 'Success', 'wp-simple-firewall' ).'! '.__( 'Thank you for authenticating your login.', 'wp-simple-firewall' );
|
173 |
+
if ( $opts->isEnabledBackupCodes() ) {
|
174 |
$sFlash .= ' '.__( 'If you used your Backup Code, you will need to reset it.', 'wp-simple-firewall' ); //TODO::
|
175 |
}
|
176 |
$this->getMod()->setFlashAdminNotice( $sFlash );
|
177 |
|
178 |
$this->removeLoginIntent();
|
179 |
|
180 |
+
$sRedirectHref = $req->post( 'redirect_to' );
|
181 |
+
empty( $sRedirectHref ) ? $WPResp->redirectHere() : $WPResp->redirect( rawurldecode( $sRedirectHref ) );
|
182 |
}
|
183 |
else {
|
184 |
+
$con->getAdminNotices()
|
185 |
+
->addFlash(
|
186 |
+
__( 'One or more of your authentication codes failed or was missing.', 'wp-simple-firewall' ),
|
187 |
+
true
|
188 |
+
);
|
189 |
// We don't protect against loops here to prevent bypassing of the login intent page.
|
190 |
Services::Response()->redirect( Services::Request()->getUri(), [], true, false );
|
191 |
}
|
192 |
}
|
193 |
+
elseif ( $opts->isUseLoginIntentPage() ) {
|
194 |
$this->getLoginIntentPageHandler()->loadPage();
|
195 |
}
|
196 |
die();
|
202 |
*/
|
203 |
private function validateLoginIntentRequest() {
|
204 |
try {
|
205 |
+
$valid = ( new ValidateLoginIntentRequest() )
|
206 |
->setMfaController( $this )
|
207 |
->run();
|
208 |
}
|
209 |
+
catch ( \Exception $e ) {
|
210 |
+
$valid = true;
|
211 |
}
|
212 |
+
return $valid;
|
213 |
}
|
214 |
|
215 |
+
private function canUserMfaSkip( \WP_User $user ) :bool {
|
216 |
+
$canSkip = ( new MfaSkip() )
|
|
|
|
|
|
|
|
|
217 |
->setMod( $this->getMod() )
|
218 |
+
->canMfaSkip( $user );
|
219 |
|
220 |
+
if ( !$canSkip && $this->getCon()->isPremiumActive() && @class_exists( 'WC_Social_Login' ) ) {
|
221 |
// custom support for WooCommerce Social login
|
222 |
+
$meta = $this->getCon()->getUserMeta( $user );
|
223 |
+
$canSkip = isset( $meta->wc_social_login_valid ) ? $meta->wc_social_login_valid : false;
|
224 |
}
|
225 |
|
226 |
+
return (bool)apply_filters( 'icwp_shield_2fa_skip',
|
227 |
+
apply_filters( 'odp-shield-2fa_skip', $canSkip ) );
|
228 |
}
|
229 |
|
230 |
+
public function isSubjectToLoginIntent( \WP_User $user ) :bool {
|
231 |
+
return count( $this->getProvidersForUser( $user, true ) ) > 0;
|
|
|
|
|
|
|
|
|
232 |
}
|
233 |
|
234 |
+
private function getLoginIntentExpiresAt() :int {
|
235 |
+
return $this->getCon()
|
236 |
+
->getModule_Sessions()
|
237 |
+
->getSessionCon()
|
238 |
+
->hasSession() ? (int)$this->getMod()->getSession()->login_intent_expires_at : 0;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
239 |
}
|
240 |
|
241 |
/**
|
246 |
return $this->setLoginIntentExpiresAt( 0 );
|
247 |
}
|
248 |
|
249 |
+
protected function setLoginIntentExpiresAt( int $expiresAt ) :self {
|
250 |
+
$sessMod = $this->getCon()->getModule_Sessions();
|
251 |
+
$sessCon = $sessMod->getSessionCon();
|
252 |
+
if ( $sessCon->hasSession() ) {
|
253 |
+
/** @var Update $upd */
|
254 |
+
$upd = $sessMod->getDbHandler_Sessions()->getQueryUpdater();
|
255 |
+
$upd->updateLoginIntentExpiresAt( $sessCon->getCurrent(), $expiresAt );
|
|
|
|
|
|
|
|
|
256 |
}
|
257 |
return $this;
|
258 |
}
|
259 |
|
260 |
+
private function getLoginIntentRequestFlag() :string {
|
261 |
+
return $this->getCon()->prefix( 'login-intent-request' );
|
262 |
+
}
|
263 |
+
|
264 |
/**
|
265 |
+
* @return bool
|
266 |
+
* @deprecated 10.1
|
267 |
*/
|
268 |
+
private function hasLoginIntent() :bool {
|
269 |
+
return $this->getLoginIntentExpiresAt() > 0;
|
270 |
}
|
271 |
}
|
src/lib/src/Modules/LoginGuard/Lib/TwoFactor/Profiles/CustomForms.php
CHANGED
@@ -36,9 +36,9 @@ class CustomForms {
|
|
36 |
*/
|
37 |
private function renderCustomProfileFormOutput( array $aPs ) {
|
38 |
$oMC = $this->getMfaCon();
|
39 |
-
$
|
40 |
|
41 |
-
$aProviders = $oMC->getProvidersForUser( $
|
42 |
if ( !empty( $aPs ) ) {
|
43 |
$aProviders = array_filter(
|
44 |
$aProviders,
|
36 |
*/
|
37 |
private function renderCustomProfileFormOutput( array $aPs ) {
|
38 |
$oMC = $this->getMfaCon();
|
39 |
+
$user = $this->getWpUser();
|
40 |
|
41 |
+
$aProviders = $oMC->getProvidersForUser( $user, true );
|
42 |
if ( !empty( $aPs ) ) {
|
43 |
$aProviders = array_filter(
|
44 |
$aProviders,
|
src/lib/src/Modules/LoginGuard/Lib/TwoFactor/Provider/Email.php
CHANGED
@@ -142,8 +142,7 @@ class Email extends BaseProvider {
|
|
142 |
*/
|
143 |
private function sendEmailTwoFactorVerify( \WP_User $user ) {
|
144 |
$sureCon = $this->getCon()->getModule_Comms()->getSureSendController();
|
145 |
-
$useSureSend = $sureCon->isEnabled2Fa()
|
146 |
-
&& $sureCon->canUserSend( $user );
|
147 |
|
148 |
try {
|
149 |
$code = $this->genNewCode( $user );
|
142 |
*/
|
143 |
private function sendEmailTwoFactorVerify( \WP_User $user ) {
|
144 |
$sureCon = $this->getCon()->getModule_Comms()->getSureSendController();
|
145 |
+
$useSureSend = $sureCon->isEnabled2Fa() && $sureCon->canUserSend( $user );
|
|
|
146 |
|
147 |
try {
|
148 |
$code = $this->genNewCode( $user );
|
src/lib/src/Modules/LoginGuard/Lib/TwoFactor/ValidateLoginIntentRequest.php
CHANGED
@@ -15,53 +15,53 @@ class ValidateLoginIntentRequest {
|
|
15 |
* @return bool
|
16 |
* @throws \Exception
|
17 |
*/
|
18 |
-
public function run() {
|
19 |
$oMfaCon = $this->getMfaCon();
|
20 |
-
/** @var LoginGuard\Options $
|
21 |
-
$
|
22 |
|
23 |
-
$
|
24 |
-
if ( !$
|
25 |
throw new \Exception( 'No user logged-in.' );
|
26 |
}
|
27 |
-
$
|
28 |
-
if ( empty( $
|
29 |
throw new \Exception( 'No valid providers' );
|
30 |
}
|
31 |
|
32 |
$aSuccessfulProviders = [];
|
33 |
|
34 |
-
$
|
35 |
{ // Backup code is special case
|
36 |
-
if ( isset( $
|
37 |
-
if ( $
|
38 |
-
$
|
39 |
-
$aSuccessfulProviders[] = $
|
40 |
}
|
41 |
-
unset( $
|
42 |
}
|
43 |
}
|
44 |
|
45 |
-
if ( !$
|
46 |
$aStates = [];
|
47 |
-
foreach ( $
|
48 |
-
$aStates[ $sSlug ] = $
|
49 |
if ( $aStates[ $sSlug ] ) {
|
50 |
-
$aSuccessfulProviders[] = $
|
51 |
}
|
52 |
}
|
53 |
|
54 |
$nSuccessful = count( array_filter( $aStates ) );
|
55 |
-
$
|
56 |
}
|
57 |
|
58 |
-
if ( $
|
59 |
// Some cleanup can only run if login is completely tested and completely valid.
|
60 |
-
foreach ( $aSuccessfulProviders as $
|
61 |
-
$
|
62 |
}
|
63 |
}
|
64 |
|
65 |
-
return $
|
66 |
}
|
67 |
}
|
15 |
* @return bool
|
16 |
* @throws \Exception
|
17 |
*/
|
18 |
+
public function run() :bool {
|
19 |
$oMfaCon = $this->getMfaCon();
|
20 |
+
/** @var LoginGuard\Options $opts */
|
21 |
+
$opts = $oMfaCon->getOptions();
|
22 |
|
23 |
+
$user = Services::WpUsers()->getCurrentWpUser();
|
24 |
+
if ( !$user instanceof \WP_User ) {
|
25 |
throw new \Exception( 'No user logged-in.' );
|
26 |
}
|
27 |
+
$providers = $oMfaCon->getProvidersForUser( $user, true );
|
28 |
+
if ( empty( $providers ) ) {
|
29 |
throw new \Exception( 'No valid providers' );
|
30 |
}
|
31 |
|
32 |
$aSuccessfulProviders = [];
|
33 |
|
34 |
+
$validated = false;
|
35 |
{ // Backup code is special case
|
36 |
+
if ( isset( $providers[ Provider\Backup::SLUG ] ) ) {
|
37 |
+
if ( $providers[ Provider\Backup::SLUG ]->validateLoginIntent( $user ) ) {
|
38 |
+
$validated = true;
|
39 |
+
$aSuccessfulProviders[] = $providers[ Provider\Backup::SLUG ];
|
40 |
}
|
41 |
+
unset( $providers[ Provider\Backup::SLUG ] );
|
42 |
}
|
43 |
}
|
44 |
|
45 |
+
if ( !$validated ) {
|
46 |
$aStates = [];
|
47 |
+
foreach ( $providers as $sSlug => $provider ) {
|
48 |
+
$aStates[ $sSlug ] = $provider->validateLoginIntent( $user );
|
49 |
if ( $aStates[ $sSlug ] ) {
|
50 |
+
$aSuccessfulProviders[] = $provider;
|
51 |
}
|
52 |
}
|
53 |
|
54 |
$nSuccessful = count( array_filter( $aStates ) );
|
55 |
+
$validated = $opts->isChainedAuth() ? $nSuccessful == count( $providers ) : $nSuccessful > 0;
|
56 |
}
|
57 |
|
58 |
+
if ( $validated ) {
|
59 |
// Some cleanup can only run if login is completely tested and completely valid.
|
60 |
+
foreach ( $aSuccessfulProviders as $provider ) {
|
61 |
+
$provider->postSuccessActions( $user );
|
62 |
}
|
63 |
}
|
64 |
|
65 |
+
return $validated;
|
66 |
}
|
67 |
}
|
src/lib/src/Modules/LoginGuard/ModCon.php
ADDED
@@ -0,0 +1,280 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php declare( strict_types=1 );
|
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;
|
8 |
+
|
9 |
+
class ModCon extends BaseShield\ModCon {
|
10 |
+
|
11 |
+
/**
|
12 |
+
* @var Lib\TwoFactor\MfaController
|
13 |
+
*/
|
14 |
+
private $loginIntentCon;
|
15 |
+
|
16 |
+
protected function preProcessOptions() {
|
17 |
+
/** @var Options $opts */
|
18 |
+
$opts = $this->getOptions();
|
19 |
+
/**
|
20 |
+
* $oWp = $this->loadWpFunctionsProcessor();
|
21 |
+
* $sCustomLoginPath = $this->cleanLoginUrlPath();
|
22 |
+
* if ( !empty( $sCustomLoginPath ) && $oWp->getIsPermalinksEnabled() ) {
|
23 |
+
* $oWp->resavePermalinks();
|
24 |
+
* }
|
25 |
+
*/
|
26 |
+
if ( $this->isModuleOptionsRequest() && $opts->isEnabledEmailAuth() && !$opts->getIfCanSendEmailVerified() ) {
|
27 |
+
$this->setIfCanSendEmail( false )
|
28 |
+
->sendEmailVerifyCanSend();
|
29 |
+
}
|
30 |
+
|
31 |
+
$aIds = $opts->getOpt( 'antibot_form_ids', [] );
|
32 |
+
foreach ( $aIds as $nKey => $sId ) {
|
33 |
+
$sId = trim( strip_tags( $sId ) );
|
34 |
+
if ( empty( $sId ) ) {
|
35 |
+
unset( $aIds[ $nKey ] );
|
36 |
+
}
|
37 |
+
else {
|
38 |
+
$aIds[ $nKey ] = $sId;
|
39 |
+
}
|
40 |
+
}
|
41 |
+
$opts->setOpt( 'antibot_form_ids', array_values( array_unique( $aIds ) ) );
|
42 |
+
|
43 |
+
$this->cleanLoginUrlPath();
|
44 |
+
$this->ensureCorrectCaptchaConfig();
|
45 |
+
}
|
46 |
+
|
47 |
+
public function ensureCorrectCaptchaConfig() {
|
48 |
+
/** @var Options $opts */
|
49 |
+
$opts = $this->getOptions();
|
50 |
+
|
51 |
+
$sStyle = $opts->getOpt( 'enable_google_recaptcha_login' );
|
52 |
+
if ( $this->isPremium() ) {
|
53 |
+
$cfg = $this->getCaptchaCfg();
|
54 |
+
if ( $cfg->provider == $cfg::PROV_GOOGLE_RECAP2 ) {
|
55 |
+
if ( !$cfg->invisible && $sStyle == 'invisible' ) {
|
56 |
+
$opts->setOpt( 'enable_google_recaptcha_login', 'default' );
|
57 |
+
}
|
58 |
+
}
|
59 |
+
}
|
60 |
+
elseif ( !in_array( $sStyle, [ 'disabled', 'default' ] ) ) {
|
61 |
+
$opts->setOpt( 'enable_google_recaptcha_login', 'default' );
|
62 |
+
}
|
63 |
+
}
|
64 |
+
|
65 |
+
protected function handleModAction( string $action ) {
|
66 |
+
switch ( $action ) {
|
67 |
+
case 'email_send_verify':
|
68 |
+
$this->processEmailSendVerify();
|
69 |
+
break;
|
70 |
+
default:
|
71 |
+
break;
|
72 |
+
}
|
73 |
+
}
|
74 |
+
|
75 |
+
/**
|
76 |
+
* @uses wp_redirect()
|
77 |
+
*/
|
78 |
+
private function processEmailSendVerify() {
|
79 |
+
/** @var Options $opts */
|
80 |
+
$opts = $this->getOptions();
|
81 |
+
$this->setIfCanSendEmail( true );
|
82 |
+
$this->saveModOptions();
|
83 |
+
|
84 |
+
if ( $opts->getIfCanSendEmailVerified() ) {
|
85 |
+
$bSuccess = true;
|
86 |
+
$sMessage = __( 'Email verification completed successfully.', 'wp-simple-firewall' );
|
87 |
+
}
|
88 |
+
else {
|
89 |
+
$bSuccess = false;
|
90 |
+
$sMessage = __( 'Email verification could not be completed.', 'wp-simple-firewall' );
|
91 |
+
}
|
92 |
+
$this->setFlashAdminNotice( $sMessage, !$bSuccess );
|
93 |
+
if ( Services::WpUsers()->isUserLoggedIn() ) {
|
94 |
+
Services::Response()->redirect( $this->getUrl_AdminPage() );
|
95 |
+
}
|
96 |
+
}
|
97 |
+
|
98 |
+
/**
|
99 |
+
* @param string $to
|
100 |
+
* @param bool $bSendAsLink
|
101 |
+
* @return bool
|
102 |
+
*/
|
103 |
+
public function sendEmailVerifyCanSend( $to = null, $bSendAsLink = true ) {
|
104 |
+
|
105 |
+
if ( !Services::Data()->validEmail( $to ) ) {
|
106 |
+
$to = get_bloginfo( 'admin_email' );
|
107 |
+
}
|
108 |
+
|
109 |
+
$msg = [
|
110 |
+
__( 'Before enabling 2-factor email authentication for your WordPress site, you must verify you can receive this email.', 'wp-simple-firewall' ),
|
111 |
+
__( 'This verifies your website can send email and that your account can receive emails sent from your site.', 'wp-simple-firewall' ),
|
112 |
+
''
|
113 |
+
];
|
114 |
+
|
115 |
+
if ( $bSendAsLink ) {
|
116 |
+
$msg[] = sprintf(
|
117 |
+
__( 'Click the verify link: %s', 'wp-simple-firewall' ),
|
118 |
+
add_query_arg( $this->getModActionParams( 'email_send_verify' ), Services::WpGeneral()->getHomeUrl() )
|
119 |
+
);
|
120 |
+
}
|
121 |
+
else {
|
122 |
+
$msg[] = sprintf( __( "Here's your code for the guided wizard: %s", 'wp-simple-firewall' ), $this->getCanEmailVerifyCode() );
|
123 |
+
}
|
124 |
+
|
125 |
+
return $this->getEmailProcessor()
|
126 |
+
->sendEmailWithWrap(
|
127 |
+
$to,
|
128 |
+
__( 'Email Sending Verification', 'wp-simple-firewall' ),
|
129 |
+
$msg
|
130 |
+
);
|
131 |
+
}
|
132 |
+
|
133 |
+
private function cleanLoginUrlPath() {
|
134 |
+
/** @var Options $opts */
|
135 |
+
$opts = $this->getOptions();
|
136 |
+
$path = $opts->getCustomLoginPath();
|
137 |
+
if ( !empty( $path ) ) {
|
138 |
+
$path = preg_replace( '#[^0-9a-zA-Z-]#', '', trim( $path, '/' ) );
|
139 |
+
$this->getOptions()->setOpt( 'rename_wplogin_path', $path );
|
140 |
+
}
|
141 |
+
}
|
142 |
+
|
143 |
+
/**
|
144 |
+
* @param bool $bAsOptDefaults
|
145 |
+
* @return array
|
146 |
+
*/
|
147 |
+
public function getOptEmailTwoFactorRolesDefaults( $bAsOptDefaults = true ) {
|
148 |
+
$aTwoAuthRoles = [
|
149 |
+
'type' => 'multiple_select',
|
150 |
+
0 => __( 'Subscribers', 'wp-simple-firewall' ),
|
151 |
+
1 => __( 'Contributors', 'wp-simple-firewall' ),
|
152 |
+
2 => __( 'Authors', 'wp-simple-firewall' ),
|
153 |
+
3 => __( 'Editors', 'wp-simple-firewall' ),
|
154 |
+
8 => __( 'Administrators', 'wp-simple-firewall' )
|
155 |
+
];
|
156 |
+
if ( $bAsOptDefaults ) {
|
157 |
+
unset( $aTwoAuthRoles[ 'type' ] );
|
158 |
+
unset( $aTwoAuthRoles[ 0 ] );
|
159 |
+
return array_keys( $aTwoAuthRoles );
|
160 |
+
}
|
161 |
+
return $aTwoAuthRoles;
|
162 |
+
}
|
163 |
+
|
164 |
+
public function getGaspKey() :string {
|
165 |
+
/** @var Options $opts */
|
166 |
+
$opts = $this->getOptions();
|
167 |
+
$sKey = $opts->getOpt( 'gasp_key' );
|
168 |
+
if ( empty( $sKey ) ) {
|
169 |
+
$sKey = uniqid();
|
170 |
+
$opts->setOpt( 'gasp_key', $sKey );
|
171 |
+
}
|
172 |
+
return $this->prefix( $sKey );
|
173 |
+
}
|
174 |
+
|
175 |
+
public function getTextImAHuman() :string {
|
176 |
+
return stripslashes( $this->getTextOpt( 'text_imahuman' ) );
|
177 |
+
}
|
178 |
+
|
179 |
+
public function getTextPleaseCheckBox() :string {
|
180 |
+
return stripslashes( $this->getTextOpt( 'text_pleasecheckbox' ) );
|
181 |
+
}
|
182 |
+
|
183 |
+
/**
|
184 |
+
* @return string
|
185 |
+
*/
|
186 |
+
public function getCanEmailVerifyCode() {
|
187 |
+
return strtoupper( substr( $this->getCon()->getSiteInstallationId(), 10, 6 ) );
|
188 |
+
}
|
189 |
+
|
190 |
+
public function isEnabledCaptcha() :bool {
|
191 |
+
return !$this->getOptions()->isOpt( 'enable_google_recaptcha_login', 'disabled' )
|
192 |
+
&& $this->getCaptchaCfg()->ready;
|
193 |
+
}
|
194 |
+
|
195 |
+
public function getCaptchaCfg() :CaptchaConfigVO {
|
196 |
+
$cfg = parent::getCaptchaCfg();
|
197 |
+
$sStyle = $this->getOptions()->getOpt( 'enable_google_recaptcha_login' );
|
198 |
+
if ( $sStyle !== 'default' && $this->isPremium() ) {
|
199 |
+
$cfg->theme = $sStyle;
|
200 |
+
$cfg->invisible = $cfg->theme == 'invisible';
|
201 |
+
}
|
202 |
+
return $cfg;
|
203 |
+
}
|
204 |
+
|
205 |
+
/**
|
206 |
+
* @return Lib\TwoFactor\MfaController
|
207 |
+
*/
|
208 |
+
public function getLoginIntentController() {
|
209 |
+
if ( !isset( $this->loginIntentCon ) ) {
|
210 |
+
$this->loginIntentCon = ( new Lib\TwoFactor\MfaController() )
|
211 |
+
->setMod( $this );
|
212 |
+
}
|
213 |
+
return $this->loginIntentCon;
|
214 |
+
}
|
215 |
+
|
216 |
+
public function setIsChainedAuth( bool $isChained ) {
|
217 |
+
$this->getOptions()->setOpt( 'enable_chained_authentication', $isChained ? 'Y' : 'N' );
|
218 |
+
}
|
219 |
+
|
220 |
+
/**
|
221 |
+
* @param bool $bCan
|
222 |
+
* @return $this
|
223 |
+
*/
|
224 |
+
public function setIfCanSendEmail( $bCan ) {
|
225 |
+
$this->getOptions()->setOpt( 'email_can_send_verified_at', $bCan ? Services::Request()->ts() : 0 );
|
226 |
+
return $this;
|
227 |
+
}
|
228 |
+
|
229 |
+
public function setEnabled2FaEmail( bool $enable ) {
|
230 |
+
$this->getOptions()->setOpt( 'enable_email_authentication', $enable ? 'Y' : 'N' );
|
231 |
+
}
|
232 |
+
|
233 |
+
public function setEnabled2FaGoogleAuthenticator( bool $enable ) {
|
234 |
+
$this->getOptions()->setOpt( 'enable_google_authenticator', $enable ? 'Y' : 'N' );
|
235 |
+
}
|
236 |
+
|
237 |
+
/**
|
238 |
+
* @return string
|
239 |
+
*/
|
240 |
+
public function getLoginIntentRequestFlag() {
|
241 |
+
return $this->getCon()->prefix( 'login-intent-request' );
|
242 |
+
}
|
243 |
+
|
244 |
+
public function getTextOptDefault( string $key ) :string {
|
245 |
+
|
246 |
+
switch ( $key ) {
|
247 |
+
case 'text_imahuman':
|
248 |
+
$text = __( "I'm a human.", 'wp-simple-firewall' );
|
249 |
+
break;
|
250 |
+
|
251 |
+
case 'text_pleasecheckbox':
|
252 |
+
$text = __( "Please check the box to show us you're a human.", 'wp-simple-firewall' );
|
253 |
+
break;
|
254 |
+
|
255 |
+
default:
|
256 |
+
$text = parent::getTextOptDefault( $key );
|
257 |
+
break;
|
258 |
+
}
|
259 |
+
return $text;
|
260 |
+
}
|
261 |
+
|
262 |
+
public function setEnabledGaspCheck( bool $enable ) {
|
263 |
+
$this->getOptions()->setOpt( 'enable_login_gasp_check', $enable ? 'Y' : 'N' );
|
264 |
+
}
|
265 |
+
|
266 |
+
public function insertCustomJsVars_Admin() {
|
267 |
+
parent::insertCustomJsVars_Admin();
|
268 |
+
|
269 |
+
wp_localize_script(
|
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 |
+
wp_enqueue_script( 'jquery-ui-dialog' );
|
278 |
+
wp_enqueue_style( 'wp-jquery-ui-dialog' );
|
279 |
+
}
|
280 |
+
}
|
src/lib/src/Modules/LoginGuard/Options.php
CHANGED
@@ -2,155 +2,98 @@
|
|
2 |
|
3 |
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\LoginGuard;
|
4 |
|
5 |
-
use FernleafSystems\Wordpress\Plugin\Shield\Modules\
|
6 |
-
use FernleafSystems\Wordpress\Services\Services;
|
7 |
|
8 |
-
|
9 |
-
* Class Options
|
10 |
-
* @package FernleafSystems\Wordpress\Plugin\Shield\Modules\LoginGuard
|
11 |
-
*/
|
12 |
-
class Options extends Base\ShieldOptions {
|
13 |
|
14 |
-
|
15 |
-
* @return int
|
16 |
-
*/
|
17 |
-
public function getLoginIntentMinutes() {
|
18 |
return (int)max( 1, apply_filters(
|
19 |
$this->getCon()->prefix( 'login_intent_timeout' ),
|
20 |
$this->getDef( 'login_intent_timeout' )
|
21 |
) );
|
22 |
}
|
23 |
|
24 |
-
|
25 |
-
|
26 |
-
|
27 |
-
public function getAntiBotFormSelectors() {
|
28 |
-
$aIds = $this->getOpt( 'antibot_form_ids', [] );
|
29 |
-
return ( $this->isPremium() && is_array( $aIds ) ) ? $aIds : [];
|
30 |
}
|
31 |
|
32 |
-
|
33 |
-
* @return int
|
34 |
-
*/
|
35 |
-
public function getCooldownInterval() {
|
36 |
return (int)$this->getOpt( 'login_limit_interval' );
|
37 |
}
|
38 |
|
39 |
-
|
40 |
-
|
41 |
-
*/
|
42 |
-
public function getCustomLoginPath() {
|
43 |
-
return $this->getOpt( 'rename_wplogin_path', '' );
|
44 |
}
|
45 |
|
46 |
/**
|
47 |
* @return array
|
48 |
*/
|
49 |
public function getEmail2FaRoles() {
|
50 |
-
/** @var
|
51 |
-
$
|
52 |
-
$
|
53 |
-
if ( empty( $
|
54 |
-
$
|
55 |
-
$this->setOpt( 'two_factor_auth_user_roles', $
|
56 |
}
|
57 |
if ( $this->isPremium() ) {
|
58 |
-
$
|
59 |
}
|
60 |
-
return is_array( $
|
61 |
}
|
62 |
|
63 |
-
|
64 |
-
* @return bool
|
65 |
-
*/
|
66 |
-
public function getIfCanSendEmailVerified() {
|
67 |
return (int)$this->getOpt( 'email_can_send_verified_at' ) > 0;
|
68 |
}
|
69 |
|
70 |
-
|
71 |
-
* @return int - seconds
|
72 |
-
*/
|
73 |
-
public function getMfaSkip() {
|
74 |
return DAY_IN_SECONDS*( $this->isPremium() ? (int)$this->getOpt( 'mfa_skip', 0 ) : 0 );
|
75 |
}
|
76 |
|
77 |
-
|
78 |
-
* @return string
|
79 |
-
*/
|
80 |
-
public function getYubikeyAppId() {
|
81 |
return (string)$this->getOpt( 'yubikey_app_id', '' );
|
82 |
}
|
83 |
|
84 |
-
|
85 |
-
* @return bool
|
86 |
-
*/
|
87 |
-
public function isMfaSkip() {
|
88 |
return $this->getMfaSkip() > 0;
|
89 |
}
|
90 |
|
91 |
-
|
92 |
-
* @return bool
|
93 |
-
*/
|
94 |
-
public function isChainedAuth() {
|
95 |
return $this->isOpt( 'enable_chained_authentication', 'Y' );
|
96 |
}
|
97 |
|
98 |
-
|
99 |
-
* Also considers whether email sending ability has been verified
|
100 |
-
* @return bool
|
101 |
-
*/
|
102 |
-
public function isEmailAuthenticationActive() {
|
103 |
return $this->getIfCanSendEmailVerified() && $this->isEnabledEmailAuth();
|
104 |
}
|
105 |
|
106 |
-
|
107 |
-
* @return bool
|
108 |
-
*/
|
109 |
-
public function isEnabledEmailAuth() {
|
110 |
return $this->isOpt( 'enable_email_authentication', 'Y' );
|
111 |
}
|
112 |
|
113 |
-
|
114 |
-
* @return bool
|
115 |
-
*/
|
116 |
-
public function isEnabledCooldown() {
|
117 |
return $this->getCooldownInterval() > 0;
|
118 |
}
|
119 |
|
120 |
-
|
121 |
-
* @return bool
|
122 |
-
*/
|
123 |
-
public function isEnabledGaspCheck() {
|
124 |
return $this->isOpt( 'enable_login_gasp_check', 'Y' );
|
125 |
}
|
126 |
|
127 |
-
|
128 |
-
|
129 |
-
|
130 |
-
public function isEnabledEmailAuthAnyUserSet() {
|
131 |
-
return $this->isEmailAuthenticationActive() && $this->isOpt( 'email_any_user_set', 'Y' ) && $this->isPremium();
|
132 |
}
|
133 |
|
134 |
-
|
135 |
-
* @return bool
|
136 |
-
*/
|
137 |
-
public function isEnabledBackupCodes() {
|
138 |
return $this->isPremium() && $this->isOpt( 'allow_backupcodes', 'Y' );
|
139 |
}
|
140 |
|
141 |
-
|
142 |
-
* @return bool
|
143 |
-
*/
|
144 |
-
public function isEnabledGoogleAuthenticator() {
|
145 |
return $this->isOpt( 'enable_google_authenticator', 'Y' );
|
146 |
}
|
147 |
|
148 |
-
|
149 |
-
|
150 |
-
*/
|
151 |
-
public function isEnabledU2F() {
|
152 |
-
return Services::Data()->getPhpVersionIsAtLeast( '7.0' )
|
153 |
-
&& $this->isPremium() && $this->isOpt( 'enable_u2f', 'Y' );
|
154 |
}
|
155 |
|
156 |
/**
|
@@ -175,34 +118,23 @@ class Options extends Base\ShieldOptions {
|
|
175 |
}
|
176 |
|
177 |
/**
|
178 |
-
* @param string $
|
179 |
* @return bool
|
180 |
*/
|
181 |
-
public function isProtect( $
|
182 |
$aLocs = $this->getOpt( 'bot_protection_locations' );
|
183 |
-
return in_array( $
|
184 |
}
|
185 |
|
186 |
-
|
187 |
-
* @return bool
|
188 |
-
*/
|
189 |
-
public function isUseLoginIntentPage() {
|
190 |
return $this->isOpt( 'use_login_intent_page', true );
|
191 |
}
|
192 |
|
193 |
-
|
194 |
-
* @return bool
|
195 |
-
*/
|
196 |
-
public function isEnabledYubikey() {
|
197 |
return $this->isOpt( 'enable_yubikey', 'Y' ) && $this->isYubikeyConfigReady();
|
198 |
}
|
199 |
|
200 |
-
|
201 |
-
|
202 |
-
*/
|
203 |
-
private function isYubikeyConfigReady() {
|
204 |
-
$sAppId = $this->getOpt( 'yubikey_app_id' );
|
205 |
-
$sApiKey = $this->getOpt( 'yubikey_api_key' );
|
206 |
-
return !empty( $sAppId ) && !empty( $sApiKey );
|
207 |
}
|
208 |
}
|
2 |
|
3 |
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\LoginGuard;
|
4 |
|
5 |
+
use FernleafSystems\Wordpress\Plugin\Shield\Modules\BaseShield;
|
|
|
6 |
|
7 |
+
class Options extends BaseShield\Options {
|
|
|
|
|
|
|
|
|
8 |
|
9 |
+
public function getLoginIntentMinutes() :int {
|
|
|
|
|
|
|
10 |
return (int)max( 1, apply_filters(
|
11 |
$this->getCon()->prefix( 'login_intent_timeout' ),
|
12 |
$this->getDef( 'login_intent_timeout' )
|
13 |
) );
|
14 |
}
|
15 |
|
16 |
+
public function getAntiBotFormSelectors() :array {
|
17 |
+
$ids = $this->getOpt( 'antibot_form_ids', [] );
|
18 |
+
return ( $this->isPremium() && is_array( $ids ) ) ? $ids : [];
|
|
|
|
|
|
|
19 |
}
|
20 |
|
21 |
+
public function getCooldownInterval() :int {
|
|
|
|
|
|
|
22 |
return (int)$this->getOpt( 'login_limit_interval' );
|
23 |
}
|
24 |
|
25 |
+
public function getCustomLoginPath() :string {
|
26 |
+
return (string)$this->getOpt( 'rename_wplogin_path', '' );
|
|
|
|
|
|
|
27 |
}
|
28 |
|
29 |
/**
|
30 |
* @return array
|
31 |
*/
|
32 |
public function getEmail2FaRoles() {
|
33 |
+
/** @var ModCon $mod */
|
34 |
+
$mod = $this->getMod();
|
35 |
+
$roles = $this->getOpt( 'two_factor_auth_user_roles', [] );
|
36 |
+
if ( empty( $roles ) || !is_array( $roles ) ) {
|
37 |
+
$roles = $mod->getOptEmailTwoFactorRolesDefaults();
|
38 |
+
$this->setOpt( 'two_factor_auth_user_roles', $roles );
|
39 |
}
|
40 |
if ( $this->isPremium() ) {
|
41 |
+
$roles = apply_filters( 'odp-shield-2fa_email_user_roles', $roles );
|
42 |
}
|
43 |
+
return is_array( $roles ) ? $roles : $mod->getOptEmailTwoFactorRolesDefaults();
|
44 |
}
|
45 |
|
46 |
+
public function getIfCanSendEmailVerified() :bool {
|
|
|
|
|
|
|
47 |
return (int)$this->getOpt( 'email_can_send_verified_at' ) > 0;
|
48 |
}
|
49 |
|
50 |
+
public function getMfaSkip() :int { // seconds
|
|
|
|
|
|
|
51 |
return DAY_IN_SECONDS*( $this->isPremium() ? (int)$this->getOpt( 'mfa_skip', 0 ) : 0 );
|
52 |
}
|
53 |
|
54 |
+
public function getYubikeyAppId() :string {
|
|
|
|
|
|
|
55 |
return (string)$this->getOpt( 'yubikey_app_id', '' );
|
56 |
}
|
57 |
|
58 |
+
public function isMfaSkip() :bool {
|
|
|
|
|
|
|
59 |
return $this->getMfaSkip() > 0;
|
60 |
}
|
61 |
|
62 |
+
public function isChainedAuth() :bool {
|
|
|
|
|
|
|
63 |
return $this->isOpt( 'enable_chained_authentication', 'Y' );
|
64 |
}
|
65 |
|
66 |
+
public function isEmailAuthenticationActive() :bool {
|
|
|
|
|
|
|
|
|
67 |
return $this->getIfCanSendEmailVerified() && $this->isEnabledEmailAuth();
|
68 |
}
|
69 |
|
70 |
+
public function isEnabledEmailAuth() :bool {
|
|
|
|
|
|
|
71 |
return $this->isOpt( 'enable_email_authentication', 'Y' );
|
72 |
}
|
73 |
|
74 |
+
public function isEnabledCooldown() :bool {
|
|
|
|
|
|
|
75 |
return $this->getCooldownInterval() > 0;
|
76 |
}
|
77 |
|
78 |
+
public function isEnabledGaspCheck() :bool {
|
|
|
|
|
|
|
79 |
return $this->isOpt( 'enable_login_gasp_check', 'Y' );
|
80 |
}
|
81 |
|
82 |
+
public function isEnabledEmailAuthAnyUserSet() :bool {
|
83 |
+
return $this->isEmailAuthenticationActive()
|
84 |
+
&& $this->isOpt( 'email_any_user_set', 'Y' ) && $this->isPremium();
|
|
|
|
|
85 |
}
|
86 |
|
87 |
+
public function isEnabledBackupCodes() :bool {
|
|
|
|
|
|
|
88 |
return $this->isPremium() && $this->isOpt( 'allow_backupcodes', 'Y' );
|
89 |
}
|
90 |
|
91 |
+
public function isEnabledGoogleAuthenticator() :bool {
|
|
|
|
|
|
|
92 |
return $this->isOpt( 'enable_google_authenticator', 'Y' );
|
93 |
}
|
94 |
|
95 |
+
public function isEnabledU2F() :bool {
|
96 |
+
return $this->isPremium() && $this->isOpt( 'enable_u2f', 'Y' );
|
|
|
|
|
|
|
|
|
97 |
}
|
98 |
|
99 |
/**
|
118 |
}
|
119 |
|
120 |
/**
|
121 |
+
* @param string $location - see config for keys, e.g. login, register, password, checkout_woo
|
122 |
* @return bool
|
123 |
*/
|
124 |
+
public function isProtect( $location ) {
|
125 |
$aLocs = $this->getOpt( 'bot_protection_locations' );
|
126 |
+
return in_array( $location, is_array( $aLocs ) ? $aLocs : $this->getOptDefault( 'bot_protection_locations' ) );
|
127 |
}
|
128 |
|
129 |
+
public function isUseLoginIntentPage() :bool {
|
|
|
|
|
|
|
130 |
return $this->isOpt( 'use_login_intent_page', true );
|
131 |
}
|
132 |
|
133 |
+
public function isEnabledYubikey() :bool {
|
|
|
|
|
|
|
134 |
return $this->isOpt( 'enable_yubikey', 'Y' ) && $this->isYubikeyConfigReady();
|
135 |
}
|
136 |
|
137 |
+
private function isYubikeyConfigReady() :bool {
|
138 |
+
return !empty( $this->getOpt( 'yubikey_app_id' ) ) && !empty( $this->getOpt( 'yubikey_api_key' ) );
|
|
|
|
|
|
|
|
|
|
|
139 |
}
|
140 |
}
|
src/lib/src/Modules/LoginGuard/Processor.php
ADDED
@@ -0,0 +1,28 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php declare( strict_types=1 );
|
2 |
+
|
3 |
+
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\LoginGuard;
|
4 |
+
|
5 |
+
use FernleafSystems\Wordpress\Plugin\Shield\Modules\BaseShield;
|
6 |
+
use FernleafSystems\Wordpress\Services\Services;
|
7 |
+
|
8 |
+
class Processor extends BaseShield\Processor {
|
9 |
+
|
10 |
+
protected function run() {
|
11 |
+
/** @var ModCon $mod */
|
12 |
+
$mod = $this->getMod();
|
13 |
+
|
14 |
+
// XML-RPC Compatibility
|
15 |
+
if ( Services::WpGeneral()->isXmlrpc() && $mod->isXmlrpcBypass() ) {
|
16 |
+
return;
|
17 |
+
}
|
18 |
+
|
19 |
+
( new Lib\Rename\RenameLogin() )
|
20 |
+
->setMod( $mod )
|
21 |
+
->execute();
|
22 |
+
|
23 |
+
if ( !$mod->isVisitorWhitelisted() ) {
|
24 |
+
( new Lib\AntiBot\AntibotSetup() )->setMod( $mod );
|
25 |
+
$mod->getLoginIntentController()->run();
|
26 |
+
}
|
27 |
+
}
|
28 |
+
}
|
src/lib/src/Modules/LoginGuard/Strings.php
CHANGED
@@ -3,7 +3,6 @@
|
|
3 |
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\LoginGuard;
|
4 |
|
5 |
use FernleafSystems\Wordpress\Plugin\Shield\Modules\Base;
|
6 |
-
use FernleafSystems\Wordpress\Services\Services;
|
7 |
|
8 |
class Strings extends Base\Strings {
|
9 |
|
@@ -145,98 +144,98 @@ class Strings extends Base\Strings {
|
|
145 |
* @throws \Exception
|
146 |
*/
|
147 |
public function getOptionStrings( string $key ) :array {
|
148 |
-
/** @var
|
149 |
$mod = $this->getMod();
|
150 |
-
$
|
151 |
|
152 |
switch ( $key ) {
|
153 |
|
154 |
case 'enable_login_protect' :
|
155 |
-
$
|
156 |
-
$
|
157 |
-
$
|
158 |
break;
|
159 |
|
160 |
case 'rename_wplogin_path' :
|
161 |
-
$
|
162 |
-
$
|
163 |
-
$
|
164 |
-
|
165 |
-
|
166 |
-
|
167 |
-
|
168 |
break;
|
169 |
|
170 |
case 'enable_chained_authentication' :
|
171 |
-
$
|
172 |
-
$
|
173 |
-
$
|
174 |
break;
|
175 |
|
176 |
case 'mfa_skip' :
|
177 |
-
$
|
178 |
-
$
|
179 |
-
$
|
180 |
break;
|
181 |
|
182 |
case 'allow_backupcodes' :
|
183 |
-
$
|
184 |
-
$
|
185 |
-
$
|
186 |
break;
|
187 |
|
188 |
case 'enable_google_authenticator' :
|
189 |
-
$
|
190 |
-
$
|
191 |
-
$
|
192 |
break;
|
193 |
|
194 |
case 'enable_email_authentication' :
|
195 |
-
$
|
196 |
-
$
|
197 |
-
$
|
198 |
break;
|
199 |
|
200 |
case 'email_any_user_set' :
|
201 |
-
$
|
202 |
-
$
|
203 |
-
$
|
204 |
break;
|
205 |
|
206 |
case 'two_factor_auth_user_roles' :
|
207 |
-
$
|
208 |
-
$
|
209 |
-
$
|
210 |
-
|
211 |
break;
|
212 |
|
213 |
case 'enable_google_recaptcha_login' :
|
214 |
-
$
|
215 |
-
$
|
216 |
-
$
|
217 |
-
|
218 |
-
|
219 |
-
|
220 |
break;
|
221 |
|
222 |
case 'bot_protection_locations' :
|
223 |
-
$
|
224 |
-
$
|
225 |
-
$
|
226 |
-
|
227 |
break;
|
228 |
|
229 |
case 'enable_login_gasp_check' :
|
230 |
-
$
|
231 |
-
$
|
232 |
-
$
|
233 |
-
|
234 |
break;
|
235 |
|
236 |
case 'antibot_form_ids' :
|
237 |
-
$
|
238 |
-
$
|
239 |
-
$
|
240 |
__( 'Provide DOM selectors to attached AntiBot protection to any form.', 'wp-simple-firewall' ),
|
241 |
__( 'IDs are prefixed with "#".', 'wp-simple-firewall' ),
|
242 |
__( 'Classes are prefixed with ".".', 'wp-simple-firewall' ),
|
@@ -245,83 +244,80 @@ class Strings extends Base\Strings {
|
|
245 |
break;
|
246 |
|
247 |
case 'login_limit_interval' :
|
248 |
-
$
|
249 |
-
$
|
250 |
-
$
|
251 |
-
|
252 |
-
|
253 |
-
|
254 |
break;
|
255 |
|
256 |
case 'enable_user_register_checking' :
|
257 |
-
$
|
258 |
-
$
|
259 |
-
$
|
260 |
break;
|
261 |
|
262 |
case 'enable_u2f' :
|
263 |
-
$
|
264 |
-
$
|
265 |
-
$
|
266 |
__( 'Allow users to register U2F devices to complete their login.', 'wp-simple-firewall' ),
|
267 |
__( "Currently only U2F keys are supported. Built-in fingerprint scanners aren't supported (yet).", 'wp-simple-firewall' ),
|
268 |
__( "Beta! This may only be used when at least 1 other 2FA option is enabled on a user account.", 'wp-simple-firewall' ),
|
269 |
];
|
270 |
-
if ( !Services::Data()->getPhpVersionIsAtLeast( '7.0' ) ) {
|
271 |
-
$sDescription[] = sprintf( '%s - %s', __( 'Important', 'wp-simple-firewall' ), __( "Requires PHP 7.0 or later.", 'wp-simple-firewall' ) );
|
272 |
-
}
|
273 |
break;
|
274 |
|
275 |
case 'enable_yubikey' :
|
276 |
-
$
|
277 |
-
$
|
278 |
-
$
|
279 |
break;
|
280 |
|
281 |
case 'yubikey_app_id' :
|
282 |
-
$
|
283 |
-
$
|
284 |
-
$
|
285 |
__( 'Combined with your Yubikey API Key this will form the basis of your Yubikey Authentication', 'wp-simple-firewall' ),
|
286 |
__( 'Please review the info link on how to obtain your own Yubikey App ID and API Key.', 'wp-simple-firewall' )
|
287 |
];
|
288 |
break;
|
289 |
|
290 |
case 'yubikey_api_key' :
|
291 |
-
$
|
292 |
-
$
|
293 |
-
$
|
294 |
-
|
295 |
break;
|
296 |
|
297 |
case 'yubikey_unique_keys' :
|
298 |
-
$
|
299 |
-
$
|
300 |
-
$
|
301 |
-
|
302 |
-
|
303 |
-
|
304 |
break;
|
305 |
|
306 |
case 'text_imahuman' :
|
307 |
-
$
|
308 |
-
$
|
309 |
-
$
|
310 |
-
|
311 |
break;
|
312 |
|
313 |
case 'text_pleasecheckbox' :
|
314 |
-
$
|
315 |
-
$
|
316 |
-
$
|
317 |
-
|
318 |
break;
|
319 |
|
320 |
// removed 9.0
|
321 |
case 'enable_antibot_js' :
|
322 |
-
$
|
323 |
-
$
|
324 |
-
$
|
325 |
break;
|
326 |
|
327 |
default:
|
@@ -329,9 +325,9 @@ class Strings extends Base\Strings {
|
|
329 |
}
|
330 |
|
331 |
return [
|
332 |
-
'name' => $
|
333 |
-
'summary' => $
|
334 |
-
'description' => $
|
335 |
];
|
336 |
}
|
337 |
}
|
3 |
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\LoginGuard;
|
4 |
|
5 |
use FernleafSystems\Wordpress\Plugin\Shield\Modules\Base;
|
|
|
6 |
|
7 |
class Strings extends Base\Strings {
|
8 |
|
144 |
* @throws \Exception
|
145 |
*/
|
146 |
public function getOptionStrings( string $key ) :array {
|
147 |
+
/** @var ModCon $mod */
|
148 |
$mod = $this->getMod();
|
149 |
+
$modName = $mod->getMainFeatureName();
|
150 |
|
151 |
switch ( $key ) {
|
152 |
|
153 |
case 'enable_login_protect' :
|
154 |
+
$name = sprintf( __( 'Enable %s Module', 'wp-simple-firewall' ), $modName );
|
155 |
+
$summary = sprintf( __( 'Enable (or Disable) The %s Module', 'wp-simple-firewall' ), $modName );
|
156 |
+
$desc = sprintf( __( 'Un-Checking this option will completely disable the %s module.', 'wp-simple-firewall' ), $modName );
|
157 |
break;
|
158 |
|
159 |
case 'rename_wplogin_path' :
|
160 |
+
$name = __( 'Hide WP Login Page', 'wp-simple-firewall' );
|
161 |
+
$summary = __( 'Hide The WordPress Login Page', 'wp-simple-firewall' );
|
162 |
+
$desc = __( 'Creating a path here will disable your wp-login.php', 'wp-simple-firewall' )
|
163 |
+
.'<br />'
|
164 |
+
.sprintf( __( 'Only letters and numbers are permitted: %s', 'wp-simple-firewall' ), '<strong>abc123</strong>' )
|
165 |
+
.'<br />'
|
166 |
+
.sprintf( __( 'Your current login URL is: %s', 'wp-simple-firewall' ), '<br /><strong> '.wp_login_url().'</strong>' );
|
167 |
break;
|
168 |
|
169 |
case 'enable_chained_authentication' :
|
170 |
+
$name = sprintf( __( 'Enable %s', 'wp-simple-firewall' ), __( 'Multi-Factor Authentication', 'wp-simple-firewall' ) );
|
171 |
+
$summary = __( 'Require All Active Authentication Factors', 'wp-simple-firewall' );
|
172 |
+
$desc = __( 'When enabled, all multi-factor authentication methods will be applied to a user login. Disable to require only one to login.', 'wp-simple-firewall' );
|
173 |
break;
|
174 |
|
175 |
case 'mfa_skip' :
|
176 |
+
$name = __( 'Multi-Factor Bypass', 'wp-simple-firewall' );
|
177 |
+
$summary = __( 'A User Can Bypass Multi-Factor Authentication (MFA) For The Set Number Of Days', 'wp-simple-firewall' );
|
178 |
+
$desc = __( 'Enter the number of days a user can bypass future MFA after a successful MFA-login. 0 to disable.', 'wp-simple-firewall' );
|
179 |
break;
|
180 |
|
181 |
case 'allow_backupcodes' :
|
182 |
+
$name = __( 'Allow Backup Codes', 'wp-simple-firewall' );
|
183 |
+
$summary = __( 'Allow Users To Generate A Backup Code', 'wp-simple-firewall' );
|
184 |
+
$desc = __( 'Allow users to generate a backup code that can be used to login if MFA factors are unavailable.', 'wp-simple-firewall' );
|
185 |
break;
|
186 |
|
187 |
case 'enable_google_authenticator' :
|
188 |
+
$name = sprintf( __( 'Enable %s', 'wp-simple-firewall' ), __( 'Google Authenticator', 'wp-simple-firewall' ) );
|
189 |
+
$summary = __( 'Allow Users To Use Google Authenticator', 'wp-simple-firewall' );
|
190 |
+
$desc = __( 'When enabled, users will have the option to add Google Authenticator to their WordPress user profile', 'wp-simple-firewall' );
|
191 |
break;
|
192 |
|
193 |
case 'enable_email_authentication' :
|
194 |
+
$name = sprintf( __( 'Enable %s', 'wp-simple-firewall' ), __( 'Email Authentication', 'wp-simple-firewall' ) );
|
195 |
+
$summary = sprintf( __( 'Two-Factor Login Authentication By %s', 'wp-simple-firewall' ), __( 'Email', 'wp-simple-firewall' ) );
|
196 |
+
$desc = __( 'All users will be required to verify their login by email-based two-factor authentication.', 'wp-simple-firewall' );
|
197 |
break;
|
198 |
|
199 |
case 'email_any_user_set' :
|
200 |
+
$name = __( 'Allow Any User', 'wp-simple-firewall' );
|
201 |
+
$summary = __( 'Allow Any User To Turn-On Two-Factor Authentication By Email.', 'wp-simple-firewall' );
|
202 |
+
$desc = __( 'Any user can turn on two-factor authentication by email from their profile.', 'wp-simple-firewall' );
|
203 |
break;
|
204 |
|
205 |
case 'two_factor_auth_user_roles' :
|
206 |
+
$name = sprintf( '%s - %s', __( 'Enforce', 'wp-simple-firewall' ), __( 'Email Authentication', 'wp-simple-firewall' ) );
|
207 |
+
$summary = __( 'All User Roles Subject To Email Authentication', 'wp-simple-firewall' );
|
208 |
+
$desc = __( 'Enforces email-based authentication on all users with the selected roles.', 'wp-simple-firewall' )
|
209 |
+
.'<br /><strong>'.sprintf( '%s: %s', __( 'Note', 'wp-simple-firewall' ), sprintf( __( 'This setting only applies to %s.', 'wp-simple-firewall' ), __( 'Email Authentication', 'wp-simple-firewall' ) ) ).'</strong>';
|
210 |
break;
|
211 |
|
212 |
case 'enable_google_recaptcha_login' :
|
213 |
+
$name = __( 'CAPTCHA', 'wp-simple-firewall' );
|
214 |
+
$summary = __( 'Protect WordPress Account Access Requests With CAPTCHA', 'wp-simple-firewall' );
|
215 |
+
$desc = __( 'Use CAPTCHA on the user account forms such as login, register, etc.', 'wp-simple-firewall' ).'<br />'
|
216 |
+
.sprintf( __( 'Use of any theme other than "%s", requires a Pro license.', 'wp-simple-firewall' ), __( 'Light Theme', 'wp-simple-firewall' ) )
|
217 |
+
.'<br/>'.sprintf( '%s - %s', __( 'Note', 'wp-simple-firewall' ), __( "You'll need to setup your CAPTCHA API Keys in 'General' settings.", 'wp-simple-firewall' ) )
|
218 |
+
.'<br/><strong>'.sprintf( '%s - %s', __( 'Important', 'wp-simple-firewall' ), __( "Some forms are more dynamic than others so if you experience problems, please use non-Invisible CAPTCHA.", 'wp-simple-firewall' ) ).'</strong>';
|
219 |
break;
|
220 |
|
221 |
case 'bot_protection_locations' :
|
222 |
+
$name = __( 'Protection Locations', 'wp-simple-firewall' );
|
223 |
+
$summary = __( 'Which Forms Should Be Protected', 'wp-simple-firewall' );
|
224 |
+
$desc = __( 'Choose the forms for which bot protection measures will be deployed.', 'wp-simple-firewall' ).'<br />'
|
225 |
+
.sprintf( '%s - %s', __( 'Note', 'wp-simple-firewall' ), sprintf( __( "Use with 3rd party systems such as %s, requires a Pro license.", 'wp-simple-firewall' ), 'WooCommerce' ) );
|
226 |
break;
|
227 |
|
228 |
case 'enable_login_gasp_check' :
|
229 |
+
$name = __( 'Bot Protection', 'wp-simple-firewall' );
|
230 |
+
$summary = __( 'Protect WP Login From Automated Login Attempts By Bots', 'wp-simple-firewall' );
|
231 |
+
$desc = __( 'Adds a dynamically (Javascript) generated checkbox to the login form that prevents bots using automated login techniques.', 'wp-simple-firewall' )
|
232 |
+
.'<br />'.sprintf( '%s: %s', __( 'Recommendation', 'wp-simple-firewall' ), __( 'ON', 'wp-simple-firewall' ) );
|
233 |
break;
|
234 |
|
235 |
case 'antibot_form_ids' :
|
236 |
+
$name = __( 'AntiBot Forms', 'wp-simple-firewall' );
|
237 |
+
$summary = __( 'Enter The Selectors Of The 3rd Party Login Forms For Use With AntiBot JS', 'wp-simple-firewall' );
|
238 |
+
$desc = [
|
239 |
__( 'Provide DOM selectors to attached AntiBot protection to any form.', 'wp-simple-firewall' ),
|
240 |
__( 'IDs are prefixed with "#".', 'wp-simple-firewall' ),
|
241 |
__( 'Classes are prefixed with ".".', 'wp-simple-firewall' ),
|
244 |
break;
|
245 |
|
246 |
case 'login_limit_interval' :
|
247 |
+
$name = __( 'Cooldown Period', 'wp-simple-firewall' );
|
248 |
+
$summary = __( 'Limit account access requests to every X seconds', 'wp-simple-firewall' );
|
249 |
+
$desc = __( 'WordPress will process only ONE account access attempt per number of seconds specified.', 'wp-simple-firewall' )
|
250 |
+
.'<br />'.__( 'Zero (0) turns this off.', 'wp-simple-firewall' )
|
251 |
+
.' '.sprintf( '%s: %s', __( 'Default', 'wp-simple-firewall' ), $this->getOptions()
|
252 |
+
->getOptDefault( 'login_limit_interval' ) );
|
253 |
break;
|
254 |
|
255 |
case 'enable_user_register_checking' :
|
256 |
+
$name = __( 'User Registration', 'wp-simple-firewall' );
|
257 |
+
$summary = __( 'Apply Brute Force Protection To User Registration And Lost Passwords', 'wp-simple-firewall' );
|
258 |
+
$desc = __( 'When enabled, settings in this section will also apply to new user registration and users trying to reset passwords.', 'wp-simple-firewall' );
|
259 |
break;
|
260 |
|
261 |
case 'enable_u2f' :
|
262 |
+
$name = __( 'Allow U2F', 'wp-simple-firewall' );
|
263 |
+
$summary = __( 'Allow Registration Of U2F Devices', 'wp-simple-firewall' );
|
264 |
+
$desc = [
|
265 |
__( 'Allow users to register U2F devices to complete their login.', 'wp-simple-firewall' ),
|
266 |
__( "Currently only U2F keys are supported. Built-in fingerprint scanners aren't supported (yet).", 'wp-simple-firewall' ),
|
267 |
__( "Beta! This may only be used when at least 1 other 2FA option is enabled on a user account.", 'wp-simple-firewall' ),
|
268 |
];
|
|
|
|
|
|
|
269 |
break;
|
270 |
|
271 |
case 'enable_yubikey' :
|
272 |
+
$name = __( 'Allow Yubikey OTP', 'wp-simple-firewall' );
|
273 |
+
$summary = __( 'Allow Yubikey Registration For One Time Passwords', 'wp-simple-firewall' );
|
274 |
+
$desc = __( 'Combined with your Yubikey API details this will form the basis of your Yubikey Authentication', 'wp-simple-firewall' );
|
275 |
break;
|
276 |
|
277 |
case 'yubikey_app_id' :
|
278 |
+
$name = __( 'Yubikey App ID', 'wp-simple-firewall' );
|
279 |
+
$summary = __( 'Your Unique Yubikey App ID', 'wp-simple-firewall' );
|
280 |
+
$desc = [
|
281 |
__( 'Combined with your Yubikey API Key this will form the basis of your Yubikey Authentication', 'wp-simple-firewall' ),
|
282 |
__( 'Please review the info link on how to obtain your own Yubikey App ID and API Key.', 'wp-simple-firewall' )
|
283 |
];
|
284 |
break;
|
285 |
|
286 |
case 'yubikey_api_key' :
|
287 |
+
$name = __( 'Yubikey API Key', 'wp-simple-firewall' );
|
288 |
+
$summary = __( 'Your Unique Yubikey App API Key', 'wp-simple-firewall' );
|
289 |
+
$desc = __( 'Combined with your Yubikey App ID this will form the basis of your Yubikey Authentication.', 'wp-simple-firewall' )
|
290 |
+
.'<br />'.__( 'Please review the info link on how to get your own Yubikey App ID and API Key.', 'wp-simple-firewall' );
|
291 |
break;
|
292 |
|
293 |
case 'yubikey_unique_keys' :
|
294 |
+
$name = __( 'Yubikey Unique Keys', 'wp-simple-firewall' );
|
295 |
+
$summary = __( 'This method for Yubikeys is no longer supported. Please see your user profile', 'wp-simple-firewall' );
|
296 |
+
$desc = '<strong>'.sprintf( '%s: %s', __( 'Format', 'wp-simple-firewall' ), 'Username,Yubikey' ).'</strong>'
|
297 |
+
.'<br />- '.__( 'Provide Username<->Yubikey Pairs that are usable for this site.', 'wp-simple-firewall' )
|
298 |
+
.'<br />- '.__( 'If a Username is not assigned a Yubikey, Yubikey Authentication is OFF for that user.', 'wp-simple-firewall' )
|
299 |
+
.'<br />- '.__( 'Each [Username,Key] pair should be separated by a new line: you only need to provide the first 12 characters of the yubikey.', 'wp-simple-firewall' );
|
300 |
break;
|
301 |
|
302 |
case 'text_imahuman' :
|
303 |
+
$name = __( 'GASP Checkbox Text', 'wp-simple-firewall' );
|
304 |
+
$summary = __( 'The User Message Displayed Next To The GASP Checkbox', 'wp-simple-firewall' );
|
305 |
+
$desc = __( "You can change the text displayed to the user beside the checkbox if you need a custom message.", 'wp-simple-firewall' )
|
306 |
+
.'<br />'.sprintf( '%s: %s', __( 'Default', 'wp-simple-firewall' ), $mod->getTextOptDefault( 'text_imahuman' ) );
|
307 |
break;
|
308 |
|
309 |
case 'text_pleasecheckbox' :
|
310 |
+
$name = __( 'GASP Alert Text', 'wp-simple-firewall' );
|
311 |
+
$summary = __( "The Message Displayed If The User Doesn't Check The Box", 'wp-simple-firewall' );
|
312 |
+
$desc = __( "You can change the text displayed to the user in the alert message if they don't check the box.", 'wp-simple-firewall' )
|
313 |
+
.'<br />'.sprintf( '%s: %s', __( 'Default', 'wp-simple-firewall' ), $mod->getTextOptDefault( 'text_pleasecheckbox' ) );
|
314 |
break;
|
315 |
|
316 |
// removed 9.0
|
317 |
case 'enable_antibot_js' :
|
318 |
+
$name = __( 'AntiBot JS', 'wp-simple-firewall' );
|
319 |
+
$summary = __( 'Use AntiBot JS Includes For Custom 3rd Party Forms', 'wp-simple-firewall' );
|
320 |
+
$desc = __( 'Important: This is experimental. Please contact support for further assistance.', 'wp-simple-firewall' );
|
321 |
break;
|
322 |
|
323 |
default:
|
325 |
}
|
326 |
|
327 |
return [
|
328 |
+
'name' => $name,
|
329 |
+
'summary' => $summary,
|
330 |
+
'description' => $desc,
|
331 |
];
|
332 |
}
|
333 |
}
|
src/lib/src/Modules/LoginGuard/UI.php
CHANGED
@@ -2,9 +2,9 @@
|
|
2 |
|
3 |
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\LoginGuard;
|
4 |
|
5 |
-
use FernleafSystems\Wordpress\Plugin\Shield\Modules\
|
6 |
|
7 |
-
class UI extends
|
8 |
|
9 |
protected function getSectionWarnings( string $section ) :array {
|
10 |
$aWarnings = [];
|
2 |
|
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 |
$aWarnings = [];
|
src/lib/src/Modules/ModConsumer.php
CHANGED
@@ -24,7 +24,7 @@ trait ModConsumer {
|
|
24 |
}
|
25 |
|
26 |
/**
|
27 |
-
* @return \ICWP_WPSF_FeatureHandler_Base|Modules\Base\
|
28 |
*/
|
29 |
public function getMod() {
|
30 |
return $this->oMod;
|
@@ -47,7 +47,7 @@ trait ModConsumer {
|
|
47 |
}
|
48 |
|
49 |
/**
|
50 |
-
* @param \ICWP_WPSF_FeatureHandler_Base|Modules\Base\
|
51 |
* @return $this
|
52 |
*/
|
53 |
public function setMod( $oMod ) {
|
24 |
}
|
25 |
|
26 |
/**
|
27 |
+
* @return \ICWP_WPSF_FeatureHandler_Base|Modules\Base\ModCon
|
28 |
*/
|
29 |
public function getMod() {
|
30 |
return $this->oMod;
|
47 |
}
|
48 |
|
49 |
/**
|
50 |
+
* @param \ICWP_WPSF_FeatureHandler_Base|Modules\Base\ModCon $oMod
|
51 |
* @return $this
|
52 |
*/
|
53 |
public function setMod( $oMod ) {
|
src/lib/src/Modules/Plugin/AdminNotices.php
CHANGED
@@ -4,102 +4,88 @@ namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Plugin;
|
|
4 |
|
5 |
use FernleafSystems\Wordpress\Plugin\Shield;
|
6 |
use FernleafSystems\Wordpress\Plugin\Shield\Modules\Plugin;
|
|
|
7 |
use FernleafSystems\Wordpress\Services\Services;
|
8 |
|
9 |
class AdminNotices extends Shield\Modules\Base\AdminNotices {
|
10 |
|
11 |
/**
|
12 |
-
* @
|
13 |
-
* @throws \Exception
|
14 |
*/
|
15 |
-
protected function processNotice( $
|
16 |
|
17 |
-
switch ( $
|
18 |
|
19 |
case 'php7':
|
20 |
-
$this->buildNotice_Php7( $
|
21 |
break;
|
22 |
|
23 |
case 'override-forceoff':
|
24 |
-
$this->buildNotice_OverrideForceoff( $
|
25 |
break;
|
26 |
|
27 |
case 'plugin-disabled':
|
28 |
-
$this->buildNotice_PluginDisabled( $
|
29 |
break;
|
30 |
|
31 |
case 'update-available':
|
32 |
-
$this->buildNotice_UpdateAvailable( $
|
33 |
break;
|
34 |
|
35 |
case 'compat-sgoptimize':
|
36 |
-
$this->buildNotice_CompatSgOptimize( $
|
37 |
-
break;
|
38 |
-
|
39 |
-
case 'cloudflare-apo':
|
40 |
-
$this->buildNotice_CloudflareAPO( $oNotice );
|
41 |
break;
|
42 |
|
43 |
case 'plugin-mailing-list-signup':
|
44 |
-
$this->buildNotice_PluginMailingListSignup( $
|
45 |
break;
|
46 |
|
47 |
case 'wizard_welcome':
|
48 |
-
$this->buildNotice_WelcomeWizard( $
|
49 |
break;
|
50 |
|
51 |
case 'allow-tracking':
|
52 |
-
$this->buildNotice_AllowTracking( $
|
53 |
break;
|
54 |
|
55 |
case 'rate-plugin':
|
56 |
-
$this->buildNotice_RatePlugin( $
|
57 |
break;
|
58 |
|
59 |
default:
|
60 |
-
parent::processNotice( $
|
61 |
break;
|
62 |
}
|
63 |
}
|
64 |
|
65 |
-
|
66 |
-
* @param array $aAjaxResponse
|
67 |
-
* @return array
|
68 |
-
*/
|
69 |
-
public function handleAuthAjax( $aAjaxResponse ) {
|
70 |
|
71 |
-
if ( empty( $
|
72 |
switch ( Services::Request()->request( 'exec' ) ) {
|
73 |
|
74 |
case 'set_plugin_tracking':
|
75 |
-
$
|
76 |
break;
|
77 |
|
78 |
default:
|
79 |
-
$
|
80 |
break;
|
81 |
}
|
82 |
}
|
83 |
-
return $
|
84 |
}
|
85 |
|
86 |
-
|
87 |
-
|
88 |
-
|
89 |
-
|
90 |
-
/** @var Options $oOpts */
|
91 |
-
$oOpts = $this->getOptions();
|
92 |
-
$oOpts->setPluginTrackingPermission( (bool)Services::Request()->query( 'agree', false ) );
|
93 |
return $this->ajaxExec_DismissAdminNotice();
|
94 |
}
|
95 |
|
96 |
-
|
97 |
-
* @param Shield\Utilities\AdminNotices\NoticeVO $oNotice
|
98 |
-
*/
|
99 |
-
private function buildNotice_Php7( $oNotice ) {
|
100 |
$sName = $this->getCon()->getHumanName();
|
101 |
|
102 |
-
$
|
103 |
'notice_attributes' => [],
|
104 |
'strings' => [
|
105 |
'title' => sprintf( '%s: %s', __( 'Warning', 'wp-simple-firewall' ),
|
@@ -120,13 +106,10 @@ class AdminNotices extends Shield\Modules\Base\AdminNotices {
|
|
120 |
];
|
121 |
}
|
122 |
|
123 |
-
|
124 |
-
* @param Shield\Utilities\AdminNotices\NoticeVO $oNotice
|
125 |
-
*/
|
126 |
-
private function buildNotice_OverrideForceoff( $oNotice ) {
|
127 |
$sName = $this->getCon()->getHumanName();
|
128 |
|
129 |
-
$
|
130 |
'notice_attributes' => [],
|
131 |
'strings' => [
|
132 |
'title' => sprintf( '%s: %s', __( 'Warning', 'wp-simple-firewall' ), sprintf( __( '%s is not protecting your site', 'wp-simple-firewall' ), $sName ) ),
|
@@ -143,16 +126,13 @@ class AdminNotices extends Shield\Modules\Base\AdminNotices {
|
|
143 |
];
|
144 |
}
|
145 |
|
146 |
-
|
147 |
-
|
148 |
-
*/
|
149 |
-
private function buildNotice_PluginDisabled( $oNotice ) {
|
150 |
-
$sName = $this->getCon()->getHumanName();
|
151 |
|
152 |
-
$
|
153 |
'notice_attributes' => [],
|
154 |
'strings' => [
|
155 |
-
'title' => sprintf( '%s: %s', __( 'Warning', 'wp-simple-firewall' ), sprintf( __( '%s is not protecting your site', 'wp-simple-firewall' ), $
|
156 |
'message' => implode( ' ', [
|
157 |
__( 'The plugin is currently switched-off completely.', 'wp-simple-firewall' ),
|
158 |
__( 'All features and any security protection they provide are disabled.', 'wp-simple-firewall' ),
|
@@ -165,13 +145,10 @@ class AdminNotices extends Shield\Modules\Base\AdminNotices {
|
|
165 |
];
|
166 |
}
|
167 |
|
168 |
-
|
169 |
-
* @param Shield\Utilities\AdminNotices\NoticeVO $oNotice
|
170 |
-
*/
|
171 |
-
private function buildNotice_CompatSgOptimize( $oNotice ) {
|
172 |
$sName = $this->getCon()->getHumanName();
|
173 |
|
174 |
-
$
|
175 |
'notice_attributes' => [],
|
176 |
'strings' => [
|
177 |
'title' => sprintf( '%s: %s', __( 'Warning', 'wp-simple-firewall' ),
|
@@ -190,34 +167,14 @@ class AdminNotices extends Shield\Modules\Base\AdminNotices {
|
|
190 |
];
|
191 |
}
|
192 |
|
193 |
-
|
194 |
-
* @param Shield\Utilities\AdminNotices\NoticeVO $notice
|
195 |
-
*/
|
196 |
-
private function buildNotice_CloudflareAPO( $notice ) {
|
197 |
-
$notice->render_data = [
|
198 |
-
'notice_attributes' => [],
|
199 |
-
'strings' => [
|
200 |
-
'title' => sprintf( '%s: %s', __( 'Warning', 'wp-simple-firewall' ),
|
201 |
-
__( "CloudFlare APO Conflict/Bug", 'wp-simple-firewall' ) ),
|
202 |
-
'message' => [
|
203 |
-
__( "CloudFlare's Automatic Platform Optimisation for WordPress breaks the ability to correctly detect visitor IP addresses.", 'wp-simple-firewall' ),
|
204 |
-
__( 'Until they fix this, please switch off APO for this domain on your CloudFlare control panel.', 'wp-simple-firewall' ),
|
205 |
-
],
|
206 |
-
],
|
207 |
-
];
|
208 |
-
}
|
209 |
-
|
210 |
-
/**
|
211 |
-
* @param Shield\Utilities\AdminNotices\NoticeVO $oNotice
|
212 |
-
*/
|
213 |
-
private function buildNotice_PluginMailingListSignup( $oNotice ) {
|
214 |
/** @var Options $oOpts */
|
215 |
$oOpts = $this->getOptions();
|
216 |
|
217 |
$sName = $this->getCon()->getHumanName();
|
218 |
$oUser = Services::WpUsers()->getCurrentWpUser();
|
219 |
|
220 |
-
$
|
221 |
'notice_attributes' => [],
|
222 |
'strings' => [
|
223 |
'yes' => "Yes please! I'd love to join in and learn more",
|
@@ -241,17 +198,14 @@ class AdminNotices extends Shield\Modules\Base\AdminNotices {
|
|
241 |
'vars' => [
|
242 |
'name' => $oUser->first_name,
|
243 |
'user_email' => $oUser->user_email,
|
244 |
-
'drip_form_id' => $
|
245 |
]
|
246 |
];
|
247 |
}
|
248 |
|
249 |
-
|
250 |
-
* @param Shield\Utilities\AdminNotices\NoticeVO $oNotice
|
251 |
-
*/
|
252 |
-
private function buildNotice_UpdateAvailable( $oNotice ) {
|
253 |
$sName = $this->getCon()->getHumanName();
|
254 |
-
$
|
255 |
'notice_attributes' => [],
|
256 |
'strings' => [
|
257 |
'title' => sprintf( __( 'Update available for the %s plugin', 'wp-simple-firewall' ), $sName ),
|
@@ -264,12 +218,9 @@ class AdminNotices extends Shield\Modules\Base\AdminNotices {
|
|
264 |
];
|
265 |
}
|
266 |
|
267 |
-
|
268 |
-
* @param Shield\Utilities\AdminNotices\NoticeVO $oNotice
|
269 |
-
*/
|
270 |
-
private function buildNotice_WelcomeWizard( $oNotice ) {
|
271 |
$sName = $this->getCon()->getHumanName();
|
272 |
-
$
|
273 |
'notice_attributes' => [],
|
274 |
'strings' => [
|
275 |
'dismiss' => __( "I don't need the setup wizard just now", 'wp-simple-firewall' ),
|
@@ -283,19 +234,16 @@ class AdminNotices extends Shield\Modules\Base\AdminNotices {
|
|
283 |
];
|
284 |
}
|
285 |
|
286 |
-
|
287 |
-
|
288 |
-
|
289 |
-
|
290 |
-
/** @var \ICWP_WPSF_FeatureHandler_Plugin $oMod */
|
291 |
-
$oMod = $this->getMod();
|
292 |
-
$sName = $this->getCon()->getHumanName();
|
293 |
|
294 |
-
$
|
295 |
'notice_attributes' => [],
|
296 |
'strings' => [
|
297 |
-
'title' => sprintf( __( "Make %s even better by sharing usage info?", 'wp-simple-firewall' ), $
|
298 |
-
'want_to_track' => sprintf( __( "We're hoping to understand how %s is configured and used.", 'wp-simple-firewall' ), $
|
299 |
'what_we_collect' => __( "We'd like to understand how effective it is on a global scale.", 'wp-simple-firewall' ),
|
300 |
'data_anon' => __( 'The data sent is always completely anonymous and we can never track you or your site.', 'wp-simple-firewall' ),
|
301 |
'can_turn_off' => __( 'It can be turned-off at any time within the plugin options.', 'wp-simple-firewall' ),
|
@@ -308,22 +256,19 @@ class AdminNotices extends Shield\Modules\Base\AdminNotices {
|
|
308 |
'no_help' => __( "No, I don't want to help", 'wp-simple-firewall' ),
|
309 |
],
|
310 |
'ajax' => [
|
311 |
-
'set_plugin_tracking' => $
|
312 |
],
|
313 |
'hrefs' => [
|
314 |
'learn_more' => 'https://translate.fernleafsystems.com',
|
315 |
-
'link_to_see' => $
|
316 |
'link_to_moreinfo' => 'https://shsec.io/shieldtrackinginfo',
|
317 |
|
318 |
]
|
319 |
];
|
320 |
}
|
321 |
|
322 |
-
|
323 |
-
|
324 |
-
*/
|
325 |
-
private function buildNotice_RatePlugin( $oNotice ) {
|
326 |
-
$oNotice->render_data = [
|
327 |
'notice_attributes' => [],
|
328 |
'strings' => [
|
329 |
'title' => __( 'Can You Help Us With A Quick Review?', 'wp-simple-firewall' ),
|
@@ -335,49 +280,41 @@ class AdminNotices extends Shield\Modules\Base\AdminNotices {
|
|
335 |
];
|
336 |
}
|
337 |
|
338 |
-
|
339 |
-
* @param Shield\Utilities\AdminNotices\NoticeVO $oNotice
|
340 |
-
* @return bool
|
341 |
-
*/
|
342 |
-
protected function isDisplayNeeded( $oNotice ) {
|
343 |
$oCon = $this->getCon();
|
344 |
/** @var Options $oOpts */
|
345 |
$oOpts = $this->getOptions();
|
346 |
|
347 |
-
switch ( $
|
348 |
|
349 |
case 'override-forceoff':
|
350 |
-
$
|
351 |
break;
|
352 |
|
353 |
case 'php7':
|
354 |
-
$
|
355 |
break;
|
356 |
|
357 |
case 'plugin-disabled':
|
358 |
-
$
|
359 |
break;
|
360 |
|
361 |
case 'update-available':
|
362 |
-
$
|
363 |
break;
|
364 |
|
365 |
case 'compat-sgoptimize':
|
366 |
-
$
|
367 |
-
break;
|
368 |
-
|
369 |
-
case 'cloudflare-apo':
|
370 |
-
$bNeeded = ( new Plugin\Components\TestForCloudflareAPO() )->run();
|
371 |
break;
|
372 |
|
373 |
case 'allow-tracking':
|
374 |
-
$
|
375 |
break;
|
376 |
|
377 |
default:
|
378 |
-
$
|
379 |
break;
|
380 |
}
|
381 |
-
return $
|
382 |
}
|
383 |
}
|
4 |
|
5 |
use FernleafSystems\Wordpress\Plugin\Shield;
|
6 |
use FernleafSystems\Wordpress\Plugin\Shield\Modules\Plugin;
|
7 |
+
use FernleafSystems\Wordpress\Plugin\Shield\Utilities\AdminNotices\NoticeVO;
|
8 |
use FernleafSystems\Wordpress\Services\Services;
|
9 |
|
10 |
class AdminNotices extends Shield\Modules\Base\AdminNotices {
|
11 |
|
12 |
/**
|
13 |
+
* @inheritDoc
|
|
|
14 |
*/
|
15 |
+
protected function processNotice( NoticeVO $notice ) {
|
16 |
|
17 |
+
switch ( $notice->id ) {
|
18 |
|
19 |
case 'php7':
|
20 |
+
$this->buildNotice_Php7( $notice );
|
21 |
break;
|
22 |
|
23 |
case 'override-forceoff':
|
24 |
+
$this->buildNotice_OverrideForceoff( $notice );
|
25 |
break;
|
26 |
|
27 |
case 'plugin-disabled':
|
28 |
+
$this->buildNotice_PluginDisabled( $notice );
|
29 |
break;
|
30 |
|
31 |
case 'update-available':
|
32 |
+
$this->buildNotice_UpdateAvailable( $notice );
|
33 |
break;
|
34 |
|
35 |
case 'compat-sgoptimize':
|
36 |
+
$this->buildNotice_CompatSgOptimize( $notice );
|
|
|
|
|
|
|
|
|
37 |
break;
|
38 |
|
39 |
case 'plugin-mailing-list-signup':
|
40 |
+
$this->buildNotice_PluginMailingListSignup( $notice );
|
41 |
break;
|
42 |
|
43 |
case 'wizard_welcome':
|
44 |
+
$this->buildNotice_WelcomeWizard( $notice );
|
45 |
break;
|
46 |
|
47 |
case 'allow-tracking':
|
48 |
+
$this->buildNotice_AllowTracking( $notice );
|
49 |
break;
|
50 |
|
51 |
case 'rate-plugin':
|
52 |
+
$this->buildNotice_RatePlugin( $notice );
|
53 |
break;
|
54 |
|
55 |
default:
|
56 |
+
parent::processNotice( $notice );
|
57 |
break;
|
58 |
}
|
59 |
}
|
60 |
|
61 |
+
public function handleAuthAjax( array $ajaxResponse ) :array {
|
|
|
|
|
|
|
|
|
62 |
|
63 |
+
if ( empty( $ajaxResponse ) ) {
|
64 |
switch ( Services::Request()->request( 'exec' ) ) {
|
65 |
|
66 |
case 'set_plugin_tracking':
|
67 |
+
$ajaxResponse = $this->ajaxExec_SetPluginTrackingPerm();
|
68 |
break;
|
69 |
|
70 |
default:
|
71 |
+
$ajaxResponse = parent::handleAuthAjax( $ajaxResponse );
|
72 |
break;
|
73 |
}
|
74 |
}
|
75 |
+
return $ajaxResponse;
|
76 |
}
|
77 |
|
78 |
+
private function ajaxExec_SetPluginTrackingPerm() :array {
|
79 |
+
/** @var Options $opts */
|
80 |
+
$opts = $this->getOptions();
|
81 |
+
$opts->setPluginTrackingPermission( (bool)Services::Request()->query( 'agree', false ) );
|
|
|
|
|
|
|
82 |
return $this->ajaxExec_DismissAdminNotice();
|
83 |
}
|
84 |
|
85 |
+
private function buildNotice_Php7( NoticeVO $notice ) {
|
|
|
|
|
|
|
86 |
$sName = $this->getCon()->getHumanName();
|
87 |
|
88 |
+
$notice->render_data = [
|
89 |
'notice_attributes' => [],
|
90 |
'strings' => [
|
91 |
'title' => sprintf( '%s: %s', __( 'Warning', 'wp-simple-firewall' ),
|
106 |
];
|
107 |
}
|
108 |
|
109 |
+
private function buildNotice_OverrideForceoff( NoticeVO $notice ) {
|
|
|
|
|
|
|
110 |
$sName = $this->getCon()->getHumanName();
|
111 |
|
112 |
+
$notice->render_data = [
|
113 |
'notice_attributes' => [],
|
114 |
'strings' => [
|
115 |
'title' => sprintf( '%s: %s', __( 'Warning', 'wp-simple-firewall' ), sprintf( __( '%s is not protecting your site', 'wp-simple-firewall' ), $sName ) ),
|
126 |
];
|
127 |
}
|
128 |
|
129 |
+
private function buildNotice_PluginDisabled( NoticeVO $notice ) {
|
130 |
+
$name = $this->getCon()->getHumanName();
|
|
|
|
|
|
|
131 |
|
132 |
+
$notice->render_data = [
|
133 |
'notice_attributes' => [],
|
134 |
'strings' => [
|
135 |
+
'title' => sprintf( '%s: %s', __( 'Warning', 'wp-simple-firewall' ), sprintf( __( '%s is not protecting your site', 'wp-simple-firewall' ), $name ) ),
|
136 |
'message' => implode( ' ', [
|
137 |
__( 'The plugin is currently switched-off completely.', 'wp-simple-firewall' ),
|
138 |
__( 'All features and any security protection they provide are disabled.', 'wp-simple-firewall' ),
|
145 |
];
|
146 |
}
|
147 |
|
148 |
+
private function buildNotice_CompatSgOptimize( NoticeVO $notice ) {
|
|
|
|
|
|
|
149 |
$sName = $this->getCon()->getHumanName();
|
150 |
|
151 |
+
$notice->render_data = [
|
152 |
'notice_attributes' => [],
|
153 |
'strings' => [
|
154 |
'title' => sprintf( '%s: %s', __( 'Warning', 'wp-simple-firewall' ),
|
167 |
];
|
168 |
}
|
169 |
|
170 |
+
private function buildNotice_PluginMailingListSignup( NoticeVO $notice ) {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
171 |
/** @var Options $oOpts */
|
172 |
$oOpts = $this->getOptions();
|
173 |
|
174 |
$sName = $this->getCon()->getHumanName();
|
175 |
$oUser = Services::WpUsers()->getCurrentWpUser();
|
176 |
|
177 |
+
$notice->render_data = [
|
178 |
'notice_attributes' => [],
|
179 |
'strings' => [
|
180 |
'yes' => "Yes please! I'd love to join in and learn more",
|
198 |
'vars' => [
|
199 |
'name' => $oUser->first_name,
|
200 |
'user_email' => $oUser->user_email,
|
201 |
+
'drip_form_id' => $notice->drip_form_id
|
202 |
]
|
203 |
];
|
204 |
}
|
205 |
|
206 |
+
private function buildNotice_UpdateAvailable( NoticeVO $notice ) {
|
|
|
|
|
|
|
207 |
$sName = $this->getCon()->getHumanName();
|
208 |
+
$notice->render_data = [
|
209 |
'notice_attributes' => [],
|
210 |
'strings' => [
|
211 |
'title' => sprintf( __( 'Update available for the %s plugin', 'wp-simple-firewall' ), $sName ),
|
218 |
];
|
219 |
}
|
220 |
|
221 |
+
private function buildNotice_WelcomeWizard( NoticeVO $notice ) {
|
|
|
|
|
|
|
222 |
$sName = $this->getCon()->getHumanName();
|
223 |
+
$notice->render_data = [
|
224 |
'notice_attributes' => [],
|
225 |
'strings' => [
|
226 |
'dismiss' => __( "I don't need the setup wizard just now", 'wp-simple-firewall' ),
|
234 |
];
|
235 |
}
|
236 |
|
237 |
+
private function buildNotice_AllowTracking( NoticeVO $notice ) {
|
238 |
+
/** @var ModCon $mod */
|
239 |
+
$mod = $this->getMod();
|
240 |
+
$name = $this->getCon()->getHumanName();
|
|
|
|
|
|
|
241 |
|
242 |
+
$notice->render_data = [
|
243 |
'notice_attributes' => [],
|
244 |
'strings' => [
|
245 |
+
'title' => sprintf( __( "Make %s even better by sharing usage info?", 'wp-simple-firewall' ), $name ),
|
246 |
+
'want_to_track' => sprintf( __( "We're hoping to understand how %s is configured and used.", 'wp-simple-firewall' ), $name ),
|
247 |
'what_we_collect' => __( "We'd like to understand how effective it is on a global scale.", 'wp-simple-firewall' ),
|
248 |
'data_anon' => __( 'The data sent is always completely anonymous and we can never track you or your site.', 'wp-simple-firewall' ),
|
249 |
'can_turn_off' => __( 'It can be turned-off at any time within the plugin options.', 'wp-simple-firewall' ),
|
256 |
'no_help' => __( "No, I don't want to help", 'wp-simple-firewall' ),
|
257 |
],
|
258 |
'ajax' => [
|
259 |
+
'set_plugin_tracking' => $mod->getAjaxActionData( 'set_plugin_tracking', true ),
|
260 |
],
|
261 |
'hrefs' => [
|
262 |
'learn_more' => 'https://translate.fernleafsystems.com',
|
263 |
+
'link_to_see' => $mod->getLinkToTrackingDataDump(),
|
264 |
'link_to_moreinfo' => 'https://shsec.io/shieldtrackinginfo',
|
265 |
|
266 |
]
|
267 |
];
|
268 |
}
|
269 |
|
270 |
+
private function buildNotice_RatePlugin( NoticeVO $notice ) {
|
271 |
+
$notice->render_data = [
|
|
|
|
|
|
|
272 |
'notice_attributes' => [],
|
273 |
'strings' => [
|
274 |
'title' => __( 'Can You Help Us With A Quick Review?', 'wp-simple-firewall' ),
|
280 |
];
|
281 |
}
|
282 |
|
283 |
+
protected function isDisplayNeeded( NoticeVO $notice ) :bool {
|
|
|
|
|
|
|
|
|
284 |
$oCon = $this->getCon();
|
285 |
/** @var Options $oOpts */
|
286 |
$oOpts = $this->getOptions();
|
287 |
|
288 |
+
switch ( $notice->id ) {
|
289 |
|
290 |
case 'override-forceoff':
|
291 |
+
$needed = $oCon->getIfForceOffActive();
|
292 |
break;
|
293 |
|
294 |
case 'php7':
|
295 |
+
$needed = !Services::Data()->getPhpVersionIsAtLeast( '7.0' );
|
296 |
break;
|
297 |
|
298 |
case 'plugin-disabled':
|
299 |
+
$needed = $oOpts->isPluginGloballyDisabled();
|
300 |
break;
|
301 |
|
302 |
case 'update-available':
|
303 |
+
$needed = Services::WpPlugins()->isUpdateAvailable( $oCon->getPluginBaseFile() );
|
304 |
break;
|
305 |
|
306 |
case 'compat-sgoptimize':
|
307 |
+
$needed = ( new Plugin\Components\SiteGroundPluginCompatibility() )->testIsIncompatible();
|
|
|
|
|
|
|
|
|
308 |
break;
|
309 |
|
310 |
case 'allow-tracking':
|
311 |
+
$needed = !$oOpts->isTrackingPermissionSet();
|
312 |
break;
|
313 |
|
314 |
default:
|
315 |
+
$needed = parent::isDisplayNeeded( $notice );
|
316 |
break;
|
317 |
}
|
318 |
+
return $needed;
|
319 |
}
|
320 |
}
|
src/lib/src/Modules/Plugin/AjaxHandler.php
CHANGED
@@ -7,7 +7,7 @@ use FernleafSystems\Wordpress\Plugin\Shield\Modules\Plugin;
|
|
7 |
use FernleafSystems\Wordpress\Services\Services;
|
8 |
use FernleafSystems\Wordpress\Services\Utilities\Net\FindSourceFromIp;
|
9 |
|
10 |
-
class AjaxHandler extends Shield\Modules\
|
11 |
|
12 |
protected function processAjaxAction( string $action ) :array {
|
13 |
switch ( $action ) {
|
@@ -66,92 +66,77 @@ class AjaxHandler extends Shield\Modules\Base\AjaxHandlerShield {
|
|
66 |
return $aResponse;
|
67 |
}
|
68 |
|
69 |
-
|
70 |
-
|
71 |
-
|
72 |
-
|
73 |
-
/** @var \ICWP_WPSF_FeatureHandler_Plugin $oMod */
|
74 |
-
$oMod = $this->getMod();
|
75 |
-
$aResults = [];
|
76 |
foreach ( $_POST as $sKey => $sValue ) {
|
77 |
if ( strpos( $sKey, 'reason_' ) === 0 ) {
|
78 |
-
$
|
79 |
}
|
80 |
}
|
81 |
-
$
|
82 |
-
|
83 |
-
|
84 |
-
|
85 |
-
|
86 |
-
|
87 |
return [ 'success' => true ];
|
88 |
}
|
89 |
|
90 |
-
|
91 |
-
|
92 |
-
|
93 |
-
|
94 |
-
/** @var \ICWP_WPSF_FeatureHandler_Plugin $oMod */
|
95 |
-
$oMod = $this->getMod();
|
96 |
-
$bSuccess = $oMod->getPluginBadgeCon()->setBadgeStateClosed();
|
97 |
return [
|
98 |
-
'success' => $
|
99 |
-
'message' => $
|
100 |
];
|
101 |
}
|
102 |
|
103 |
-
|
104 |
-
|
105 |
-
|
106 |
-
|
107 |
-
|
108 |
-
$oOpts = $this->getOptions();
|
109 |
-
if ( !$oOpts->isTrackingPermissionSet() ) {
|
110 |
-
$oOpts->setPluginTrackingPermission( (bool)Services::Request()->query( 'agree', false ) );
|
111 |
}
|
112 |
return [ 'success' => true ];
|
113 |
}
|
114 |
|
115 |
-
|
116 |
-
|
117 |
-
|
118 |
-
|
119 |
-
/** @var \ICWP_WPSF_FeatureHandler_Plugin $oMod */
|
120 |
-
$oMod = $this->getMod();
|
121 |
-
$oReq = Services::Request();
|
122 |
|
123 |
-
$
|
124 |
|
125 |
-
$aIds = $
|
126 |
if ( empty( $aIds ) || !is_array( $aIds ) ) {
|
127 |
-
$
|
128 |
-
$
|
129 |
}
|
130 |
-
elseif ( !in_array( $
|
131 |
-
$
|
132 |
}
|
133 |
else {
|
134 |
/** @var Shield\Databases\AdminNotes\Delete $oDel */
|
135 |
-
$oDel = $
|
136 |
foreach ( $aIds as $nId ) {
|
137 |
if ( is_numeric( $nId ) ) {
|
138 |
$oDel->deleteById( $nId );
|
139 |
}
|
140 |
}
|
141 |
-
$
|
142 |
-
$
|
143 |
}
|
144 |
|
145 |
return [
|
146 |
-
'success' => $
|
147 |
-
'message' => $
|
148 |
];
|
149 |
}
|
150 |
|
151 |
-
|
152 |
-
* @return array
|
153 |
-
*/
|
154 |
-
private function ajaxExec_DeleteForceOff() {
|
155 |
$bStillActive = $this->getCon()
|
156 |
->deleteForceOffFile()
|
157 |
->getIfForceOffActive();
|
@@ -162,61 +147,52 @@ class AjaxHandler extends Shield\Modules\Base\AjaxHandlerShield {
|
|
162 |
return [ 'success' => !$bStillActive ];
|
163 |
}
|
164 |
|
165 |
-
|
166 |
-
|
167 |
-
|
168 |
-
private function ajaxExec_RenderTableAdminNotes() {
|
169 |
-
/** @var \ICWP_WPSF_FeatureHandler_Plugin $oMod */
|
170 |
-
$oMod = $this->getMod();
|
171 |
return [
|
172 |
'success' => true,
|
173 |
'html' => ( new Shield\Tables\Build\AdminNotes() )
|
174 |
-
->setMod( $
|
175 |
-
->setDbHandler( $
|
176 |
->render()
|
177 |
];
|
178 |
}
|
179 |
|
180 |
-
|
181 |
-
|
182 |
-
|
183 |
-
private function ajaxExec_AdminNotesDelete() {
|
184 |
-
/** @var \ICWP_WPSF_FeatureHandler_Plugin $oMod */
|
185 |
-
$oMod = $this->getMod();
|
186 |
|
187 |
$sItemId = Services::Request()->post( 'rid' );
|
188 |
if ( empty( $sItemId ) ) {
|
189 |
-
$
|
190 |
}
|
191 |
else {
|
192 |
try {
|
193 |
-
$bSuccess = $
|
194 |
-
|
195 |
-
|
196 |
|
197 |
if ( $bSuccess ) {
|
198 |
-
$
|
199 |
}
|
200 |
else {
|
201 |
-
$
|
202 |
}
|
203 |
}
|
204 |
catch ( \Exception $oE ) {
|
205 |
-
$
|
206 |
}
|
207 |
}
|
208 |
|
209 |
return [
|
210 |
'success' => true,
|
211 |
-
'message' => $
|
212 |
];
|
213 |
}
|
214 |
|
215 |
-
|
216 |
-
|
217 |
-
*/
|
218 |
-
private function ajaxExec_ImportFromSite() {
|
219 |
-
$bSuccess = false;
|
220 |
$aFormParams = array_merge(
|
221 |
[
|
222 |
'confirm' => 'N'
|
@@ -244,60 +220,51 @@ class AjaxHandler extends Shield\Modules\Base\AjaxHandlerShield {
|
|
244 |
catch ( \Exception $oE ) {
|
245 |
$nCode = $oE->getCode();
|
246 |
}
|
247 |
-
$
|
248 |
-
$sMessage = $
|
249 |
}
|
250 |
|
251 |
return [
|
252 |
-
'success' => $
|
253 |
'message' => $sMessage
|
254 |
];
|
255 |
}
|
256 |
|
257 |
-
|
258 |
-
|
259 |
-
*/
|
260 |
-
private function ajaxExec_AdminNotesInsert() {
|
261 |
-
/** @var \ICWP_WPSF_FeatureHandler_Plugin $mod */
|
262 |
$mod = $this->getMod();
|
263 |
-
$
|
264 |
$aFormParams = $this->getAjaxFormParams();
|
265 |
|
266 |
$sNote = isset( $aFormParams[ 'admin_note' ] ) ? $aFormParams[ 'admin_note' ] : '';
|
267 |
if ( !$mod->getCanAdminNotes() ) {
|
268 |
-
$
|
269 |
}
|
270 |
elseif ( empty( $sNote ) ) {
|
271 |
-
$
|
272 |
}
|
273 |
else {
|
274 |
/** @var Shield\Databases\AdminNotes\Insert $oInserter */
|
275 |
$oInserter = $mod->getDbHandler_Notes()->getQueryInserter();
|
276 |
-
$
|
277 |
-
$
|
278 |
}
|
279 |
return [
|
280 |
-
'success' => $
|
281 |
-
'message' => $
|
282 |
];
|
283 |
}
|
284 |
|
285 |
-
|
286 |
-
|
287 |
-
*/
|
288 |
-
private function ajaxExec_TurnOffSiteGroundOptions() {
|
289 |
-
$bSuccess = ( new Plugin\Components\SiteGroundPluginCompatibility() )->switchOffOptions();
|
290 |
return [
|
291 |
-
'success' => $
|
292 |
-
'message' => $
|
293 |
: __( 'Switching-off conflicting options appears to have failed.', 'wp-simple-firewall' )
|
294 |
];
|
295 |
}
|
296 |
|
297 |
-
|
298 |
-
* @return array
|
299 |
-
*/
|
300 |
-
private function ajaxExec_IpDetect() {
|
301 |
/** @var Options $opts */
|
302 |
$opts = $this->getOptions();
|
303 |
$source = ( new FindSourceFromIp() )->run( Services::Request()->post( 'ip' ) );
|
@@ -310,13 +277,10 @@ class AjaxHandler extends Shield\Modules\Base\AjaxHandlerShield {
|
|
310 |
];
|
311 |
}
|
312 |
|
313 |
-
|
314 |
-
|
315 |
-
|
316 |
-
|
317 |
-
/** @var \ICWP_WPSF_FeatureHandler_Plugin $oMod */
|
318 |
-
$oMod = $this->getMod();
|
319 |
-
$oMod->getTourManager()->setCompleted( Services::Request()->post( 'tour_key' ) );
|
320 |
return [
|
321 |
'success' => true,
|
322 |
'message' => 'Tour Finished'
|
7 |
use FernleafSystems\Wordpress\Services\Services;
|
8 |
use FernleafSystems\Wordpress\Services\Utilities\Net\FindSourceFromIp;
|
9 |
|
10 |
+
class AjaxHandler extends Shield\Modules\BaseShield\AjaxHandler {
|
11 |
|
12 |
protected function processAjaxAction( string $action ) :array {
|
13 |
switch ( $action ) {
|
66 |
return $aResponse;
|
67 |
}
|
68 |
|
69 |
+
private function ajaxExec_SendDeactivateSurvey() :array {
|
70 |
+
/** @var ModCon $mod */
|
71 |
+
$mod = $this->getMod();
|
72 |
+
$results = [];
|
|
|
|
|
|
|
73 |
foreach ( $_POST as $sKey => $sValue ) {
|
74 |
if ( strpos( $sKey, 'reason_' ) === 0 ) {
|
75 |
+
$results[] = str_replace( 'reason_', '', $sKey ).': '.$sValue;
|
76 |
}
|
77 |
}
|
78 |
+
$mod->getEmailProcessor()
|
79 |
+
->send(
|
80 |
+
$mod->getSurveyEmail(),
|
81 |
+
'Shield Deactivation Survey',
|
82 |
+
implode( "\n<br/>", $results )
|
83 |
+
);
|
84 |
return [ 'success' => true ];
|
85 |
}
|
86 |
|
87 |
+
private function ajaxExec_PluginBadgeClose() :array {
|
88 |
+
/** @var ModCon $mod */
|
89 |
+
$mod = $this->getMod();
|
90 |
+
$success = $mod->getPluginBadgeCon()->setBadgeStateClosed();
|
|
|
|
|
|
|
91 |
return [
|
92 |
+
'success' => $success,
|
93 |
+
'message' => $success ? 'Badge Closed' : 'Badge Not Closed'
|
94 |
];
|
95 |
}
|
96 |
|
97 |
+
private function ajaxExec_SetPluginTrackingPerm() :array {
|
98 |
+
/** @var Options $opts */
|
99 |
+
$opts = $this->getOptions();
|
100 |
+
if ( !$opts->isTrackingPermissionSet() ) {
|
101 |
+
$opts->setPluginTrackingPermission( (bool)Services::Request()->query( 'agree', false ) );
|
|
|
|
|
|
|
102 |
}
|
103 |
return [ 'success' => true ];
|
104 |
}
|
105 |
|
106 |
+
private function ajaxExec_BulkItemAction() :array {
|
107 |
+
/** @var ModCon $mod */
|
108 |
+
$mod = $this->getMod();
|
109 |
+
$req = Services::Request();
|
|
|
|
|
|
|
110 |
|
111 |
+
$success = false;
|
112 |
|
113 |
+
$aIds = $req->post( 'ids' );
|
114 |
if ( empty( $aIds ) || !is_array( $aIds ) ) {
|
115 |
+
$success = false;
|
116 |
+
$msg = __( 'No items selected.', 'wp-simple-firewall' );
|
117 |
}
|
118 |
+
elseif ( !in_array( $req->post( 'bulk_action' ), [ 'delete' ] ) ) {
|
119 |
+
$msg = __( 'Not a supported action.', 'wp-simple-firewall' );
|
120 |
}
|
121 |
else {
|
122 |
/** @var Shield\Databases\AdminNotes\Delete $oDel */
|
123 |
+
$oDel = $mod->getDbHandler_Notes()->getQueryDeleter();
|
124 |
foreach ( $aIds as $nId ) {
|
125 |
if ( is_numeric( $nId ) ) {
|
126 |
$oDel->deleteById( $nId );
|
127 |
}
|
128 |
}
|
129 |
+
$success = true;
|
130 |
+
$msg = __( 'Selected items were deleted.', 'wp-simple-firewall' );
|
131 |
}
|
132 |
|
133 |
return [
|
134 |
+
'success' => $success,
|
135 |
+
'message' => $msg,
|
136 |
];
|
137 |
}
|
138 |
|
139 |
+
private function ajaxExec_DeleteForceOff() :array {
|
|
|
|
|
|
|
140 |
$bStillActive = $this->getCon()
|
141 |
->deleteForceOffFile()
|
142 |
->getIfForceOffActive();
|
147 |
return [ 'success' => !$bStillActive ];
|
148 |
}
|
149 |
|
150 |
+
private function ajaxExec_RenderTableAdminNotes() :array {
|
151 |
+
/** @var ModCon $mod */
|
152 |
+
$mod = $this->getMod();
|
|
|
|
|
|
|
153 |
return [
|
154 |
'success' => true,
|
155 |
'html' => ( new Shield\Tables\Build\AdminNotes() )
|
156 |
+
->setMod( $mod )
|
157 |
+
->setDbHandler( $mod->getDbHandler_Notes() )
|
158 |
->render()
|
159 |
];
|
160 |
}
|
161 |
|
162 |
+
private function ajaxExec_AdminNotesDelete() :array {
|
163 |
+
/** @var ModCon $mod */
|
164 |
+
$mod = $this->getMod();
|
|
|
|
|
|
|
165 |
|
166 |
$sItemId = Services::Request()->post( 'rid' );
|
167 |
if ( empty( $sItemId ) ) {
|
168 |
+
$msg = __( 'Note not found.', 'wp-simple-firewall' );
|
169 |
}
|
170 |
else {
|
171 |
try {
|
172 |
+
$bSuccess = $mod->getDbHandler_Notes()
|
173 |
+
->getQueryDeleter()
|
174 |
+
->deleteById( $sItemId );
|
175 |
|
176 |
if ( $bSuccess ) {
|
177 |
+
$msg = __( 'Note deleted', 'wp-simple-firewall' );
|
178 |
}
|
179 |
else {
|
180 |
+
$msg = __( "Note couldn't be deleted", 'wp-simple-firewall' );
|
181 |
}
|
182 |
}
|
183 |
catch ( \Exception $oE ) {
|
184 |
+
$msg = $oE->getMessage();
|
185 |
}
|
186 |
}
|
187 |
|
188 |
return [
|
189 |
'success' => true,
|
190 |
+
'message' => $msg
|
191 |
];
|
192 |
}
|
193 |
|
194 |
+
private function ajaxExec_ImportFromSite() :array {
|
195 |
+
$success = false;
|
|
|
|
|
|
|
196 |
$aFormParams = array_merge(
|
197 |
[
|
198 |
'confirm' => 'N'
|
220 |
catch ( \Exception $oE ) {
|
221 |
$nCode = $oE->getCode();
|
222 |
}
|
223 |
+
$success = $nCode == 0;
|
224 |
+
$sMessage = $success ? __( 'Options imported successfully', 'wp-simple-firewall' ) : __( 'Options failed to import', 'wp-simple-firewall' );
|
225 |
}
|
226 |
|
227 |
return [
|
228 |
+
'success' => $success,
|
229 |
'message' => $sMessage
|
230 |
];
|
231 |
}
|
232 |
|
233 |
+
private function ajaxExec_AdminNotesInsert() :array {
|
234 |
+
/** @var ModCon $mod */
|
|
|
|
|
|
|
235 |
$mod = $this->getMod();
|
236 |
+
$success = false;
|
237 |
$aFormParams = $this->getAjaxFormParams();
|
238 |
|
239 |
$sNote = isset( $aFormParams[ 'admin_note' ] ) ? $aFormParams[ 'admin_note' ] : '';
|
240 |
if ( !$mod->getCanAdminNotes() ) {
|
241 |
+
$msg = __( "Sorry, the Admin Notes feature isn't available.", 'wp-simple-firewall' );
|
242 |
}
|
243 |
elseif ( empty( $sNote ) ) {
|
244 |
+
$msg = __( 'Sorry, but it appears your note was empty.', 'wp-simple-firewall' );
|
245 |
}
|
246 |
else {
|
247 |
/** @var Shield\Databases\AdminNotes\Insert $oInserter */
|
248 |
$oInserter = $mod->getDbHandler_Notes()->getQueryInserter();
|
249 |
+
$success = $oInserter->create( $sNote );
|
250 |
+
$msg = $success ? __( 'Note created successfully.', 'wp-simple-firewall' ) : __( 'Note could not be created.', 'wp-simple-firewall' );
|
251 |
}
|
252 |
return [
|
253 |
+
'success' => $success,
|
254 |
+
'message' => $msg
|
255 |
];
|
256 |
}
|
257 |
|
258 |
+
private function ajaxExec_TurnOffSiteGroundOptions() :array {
|
259 |
+
$success = ( new Plugin\Components\SiteGroundPluginCompatibility() )->switchOffOptions();
|
|
|
|
|
|
|
260 |
return [
|
261 |
+
'success' => $success,
|
262 |
+
'message' => $success ? __( 'Switching-off conflicting options appears to have been successful.', 'wp-simple-firewall' )
|
263 |
: __( 'Switching-off conflicting options appears to have failed.', 'wp-simple-firewall' )
|
264 |
];
|
265 |
}
|
266 |
|
267 |
+
private function ajaxExec_IpDetect() :array {
|
|
|
|
|
|
|
268 |
/** @var Options $opts */
|
269 |
$opts = $this->getOptions();
|
270 |
$source = ( new FindSourceFromIp() )->run( Services::Request()->post( 'ip' ) );
|
277 |
];
|
278 |
}
|
279 |
|
280 |
+
private function ajaxExec_MarkTourFinished() :array {
|
281 |
+
/** @var ModCon $mod */
|
282 |
+
$mod = $this->getMod();
|
283 |
+
$mod->getTourManager()->setCompleted( Services::Request()->post( 'tour_key' ) );
|
|
|
|
|
|
|
284 |
return [
|
285 |
'success' => true,
|
286 |
'message' => 'Tour Finished'
|
src/lib/src/Modules/Plugin/Components/BadgeWidget.php
CHANGED
@@ -2,19 +2,21 @@
|
|
2 |
|
3 |
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Plugin\Components;
|
4 |
|
|
|
|
|
5 |
class BadgeWidget extends \WP_Widget {
|
6 |
|
7 |
use \FernleafSystems\Wordpress\Plugin\Shield\Modules\ModConsumer;
|
8 |
|
9 |
/**
|
10 |
* BadgeWidget constructor.
|
11 |
-
* @param
|
12 |
*/
|
13 |
-
public function __construct( $
|
14 |
-
if ( empty( $
|
15 |
return;
|
16 |
}
|
17 |
-
$this->setMod( $
|
18 |
$oCon = $this->getCon();
|
19 |
|
20 |
$sName = $oCon->getHumanName();
|
2 |
|
3 |
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Plugin\Components;
|
4 |
|
5 |
+
use FernleafSystems\Wordpress\Plugin\Shield\Modules\Plugin\ModCon;
|
6 |
+
|
7 |
class BadgeWidget extends \WP_Widget {
|
8 |
|
9 |
use \FernleafSystems\Wordpress\Plugin\Shield\Modules\ModConsumer;
|
10 |
|
11 |
/**
|
12 |
* BadgeWidget constructor.
|
13 |
+
* @param ModCon $mod
|
14 |
*/
|
15 |
+
public function __construct( $mod ) {
|
16 |
+
if ( empty( $mod ) ) {
|
17 |
return;
|
18 |
}
|
19 |
+
$this->setMod( $mod );
|
20 |
$oCon = $this->getCon();
|
21 |
|
22 |
$sName = $oCon->getHumanName();
|
src/lib/src/Modules/Plugin/Components/PluginBadge.php
CHANGED
@@ -3,7 +3,7 @@
|
|
3 |
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Plugin\Components;
|
4 |
|
5 |
use FernleafSystems\Wordpress\Plugin\Shield\Modules;
|
6 |
-
use FernleafSystems\Wordpress\Plugin\Shield\Modules\Plugin
|
7 |
use FernleafSystems\Wordpress\Services\Services;
|
8 |
|
9 |
/**
|
@@ -15,7 +15,7 @@ class PluginBadge {
|
|
15 |
use Modules\ModConsumer;
|
16 |
|
17 |
public function run() {
|
18 |
-
/** @var Options $opts */
|
19 |
$opts = $this->getOptions();
|
20 |
$bDisplay = $opts->isOpt( 'display_plugin_badge', 'Y' )
|
21 |
&& ( Services::Request()->cookie( $this->getCookieIdBadgeState() ) != 'closed' );
|
@@ -37,18 +37,15 @@ class PluginBadge {
|
|
37 |
* https://wordpress.org/support/topic/fatal-errors-after-update-to-7-0-2/#post-11169820
|
38 |
*/
|
39 |
public function addPluginBadgeWidget() {
|
40 |
-
/** @var \
|
41 |
-
$
|
42 |
-
if ( !empty( $
|
43 |
&& !class_exists( 'Tribe_WP_Widget_Factory' ) ) {
|
44 |
-
register_widget( new BadgeWidget( $
|
45 |
}
|
46 |
}
|
47 |
|
48 |
-
|
49 |
-
* @return string
|
50 |
-
*/
|
51 |
-
private function getCookieIdBadgeState() {
|
52 |
return $this->getCon()->prefix( 'badgeState' );
|
53 |
}
|
54 |
|
@@ -61,75 +58,86 @@ class PluginBadge {
|
|
61 |
}
|
62 |
|
63 |
/**
|
64 |
-
* @param bool $
|
65 |
* @return string
|
66 |
*/
|
67 |
-
public function render( $
|
68 |
$con = $this->getCon();
|
69 |
-
$sName = $con->getHumanName();
|
70 |
-
|
71 |
-
$badgeUrl = 'https://shsec.io/wpsecurityfirewall';
|
72 |
/** @var Modules\SecurityAdmin\Options $secAdminOpts */
|
73 |
$secAdminOpts = $con->getModule_SecAdmin()->getOptions();
|
74 |
-
|
|
|
75 |
$badgeUrl = $secAdminOpts->getOpt( 'wl_homeurl' );
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
76 |
}
|
77 |
|
78 |
-
$
|
79 |
-
|
80 |
-
|
81 |
-
|
82 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
83 |
}
|
84 |
|
85 |
-
$
|
86 |
'ajax' => [
|
87 |
'plugin_badge_close' => $this->getMod()->getAjaxActionData( 'plugin_badge_close', true ),
|
88 |
],
|
|
|
|
|
|
|
89 |
'flags' => [
|
90 |
'nofollow' => apply_filters( 'icwp_shield_badge_relnofollow', false ),
|
91 |
-
'is_floating' => $
|
92 |
],
|
93 |
'hrefs' => [
|
94 |
-
'badge' => $
|
95 |
-
'logo' => $
|
96 |
],
|
97 |
'strings' => [
|
98 |
-
'protected' =>
|
99 |
-
|
100 |
-
'<br/><span class="plugin-badge-name">'.$sName.'</span>' )
|
101 |
-
),
|
102 |
-
'name' => $sName,
|
103 |
],
|
104 |
];
|
105 |
|
106 |
try {
|
107 |
-
$
|
108 |
}
|
109 |
catch ( \Exception $oE ) {
|
110 |
-
$
|
111 |
}
|
112 |
-
|
113 |
-
return $sRender;
|
114 |
}
|
115 |
|
116 |
-
|
117 |
-
|
118 |
-
|
119 |
-
|
120 |
-
|
121 |
-
|
122 |
-
|
123 |
-
'closed',
|
124 |
-
DAY_IN_SECONDS
|
125 |
-
);
|
126 |
}
|
127 |
|
128 |
-
|
129 |
-
|
130 |
-
* @return void
|
131 |
-
*/
|
132 |
-
public function setIsDisplayPluginBadge( $bDisplay ) {
|
133 |
-
$this->getOptions()->setOpt( 'display_plugin_badge', $bDisplay ? 'Y' : 'N' );
|
134 |
}
|
135 |
}
|
3 |
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Plugin\Components;
|
4 |
|
5 |
use FernleafSystems\Wordpress\Plugin\Shield\Modules;
|
6 |
+
use FernleafSystems\Wordpress\Plugin\Shield\Modules\Plugin;
|
7 |
use FernleafSystems\Wordpress\Services\Services;
|
8 |
|
9 |
/**
|
15 |
use Modules\ModConsumer;
|
16 |
|
17 |
public function run() {
|
18 |
+
/** @var Plugin\Options $opts */
|
19 |
$opts = $this->getOptions();
|
20 |
$bDisplay = $opts->isOpt( 'display_plugin_badge', 'Y' )
|
21 |
&& ( Services::Request()->cookie( $this->getCookieIdBadgeState() ) != 'closed' );
|
37 |
* https://wordpress.org/support/topic/fatal-errors-after-update-to-7-0-2/#post-11169820
|
38 |
*/
|
39 |
public function addPluginBadgeWidget() {
|
40 |
+
/** @var Plugin\ModCon $mod */
|
41 |
+
$mod = $this->getMod();
|
42 |
+
if ( !empty( $mod ) && Services::WpGeneral()->getWordpressIsAtLeastVersion( '4.6.0' )
|
43 |
&& !class_exists( 'Tribe_WP_Widget_Factory' ) ) {
|
44 |
+
register_widget( new BadgeWidget( $mod ) );
|
45 |
}
|
46 |
}
|
47 |
|
48 |
+
private function getCookieIdBadgeState() :string {
|
|
|
|
|
|
|
49 |
return $this->getCon()->prefix( 'badgeState' );
|
50 |
}
|
51 |
|
58 |
}
|
59 |
|
60 |
/**
|
61 |
+
* @param bool $isFloating
|
62 |
* @return string
|
63 |
*/
|
64 |
+
public function render( $isFloating = false ) {
|
65 |
$con = $this->getCon();
|
|
|
|
|
|
|
66 |
/** @var Modules\SecurityAdmin\Options $secAdminOpts */
|
67 |
$secAdminOpts = $con->getModule_SecAdmin()->getOptions();
|
68 |
+
|
69 |
+
if ( $secAdminOpts->isEnabledWhitelabel() && $secAdminOpts->isReplacePluginBadge() ) {
|
70 |
$badgeUrl = $secAdminOpts->getOpt( 'wl_homeurl' );
|
71 |
+
$name = $secAdminOpts->getOpt( 'wl_pluginnamemain' );
|
72 |
+
$logo = $secAdminOpts->getOpt( 'wl_dashboardlogourl' );
|
73 |
+
}
|
74 |
+
else {
|
75 |
+
$badgeUrl = 'https://shsec.io/wpsecurityfirewall';
|
76 |
+
$name = $con->getHumanName();
|
77 |
+
$logo = $con->getPluginUrl_Image( 'shield/shield-security-logo-colour-32px.png' );
|
78 |
+
|
79 |
+
$lic = $con->getModule_License()
|
80 |
+
->getLicenseHandler()
|
81 |
+
->getLicense();
|
82 |
+
if ( !empty( $lic->aff_ref ) ) {
|
83 |
+
$badgeUrl = add_query_arg( [ 'ref' => $lic->aff_ref ], $badgeUrl );
|
84 |
+
}
|
85 |
}
|
86 |
|
87 |
+
$badgeAttrs = [
|
88 |
+
'name' => $name,
|
89 |
+
'url' => $badgeUrl,
|
90 |
+
'logo' => $logo,
|
91 |
+
'protected_by' => apply_filters( 'icwp_shield_plugin_badge_text',
|
92 |
+
sprintf( __( 'This Site Is Protected By %s', 'wp-simple-firewall' ),
|
93 |
+
'<br/><span class="plugin-badge-name">'.$name.'</span>' )
|
94 |
+
),
|
95 |
+
'custom_css' => '',
|
96 |
+
];
|
97 |
+
if ( $con->isPremiumActive() ) {
|
98 |
+
$badgeAttrs = apply_filters( 'icwp_shield_plugin_badge_attributes', $badgeAttrs, $isFloating );
|
99 |
}
|
100 |
|
101 |
+
$data = [
|
102 |
'ajax' => [
|
103 |
'plugin_badge_close' => $this->getMod()->getAjaxActionData( 'plugin_badge_close', true ),
|
104 |
],
|
105 |
+
'content' => [
|
106 |
+
'custom_css' => esc_js( $badgeAttrs[ 'custom_css' ] ),
|
107 |
+
],
|
108 |
'flags' => [
|
109 |
'nofollow' => apply_filters( 'icwp_shield_badge_relnofollow', false ),
|
110 |
+
'is_floating' => $isFloating
|
111 |
],
|
112 |
'hrefs' => [
|
113 |
+
'badge' => $badgeAttrs[ 'url' ],
|
114 |
+
'logo' => $badgeAttrs[ 'logo' ],
|
115 |
],
|
116 |
'strings' => [
|
117 |
+
'protected' => $badgeAttrs[ 'protected_by' ],
|
118 |
+
'name' => $badgeAttrs[ 'name' ],
|
|
|
|
|
|
|
119 |
],
|
120 |
];
|
121 |
|
122 |
try {
|
123 |
+
$render = $this->getMod()->renderTemplate( 'snippets/plugin_badge_widget', $data, true );
|
124 |
}
|
125 |
catch ( \Exception $oE ) {
|
126 |
+
$render = 'Could not generate badge: '.$oE->getMessage();
|
127 |
}
|
128 |
+
return $render;
|
|
|
129 |
}
|
130 |
|
131 |
+
public function setBadgeStateClosed() :bool {
|
132 |
+
return (bool)Services::Response()
|
133 |
+
->cookieSet(
|
134 |
+
$this->getCookieIdBadgeState(),
|
135 |
+
'closed',
|
136 |
+
DAY_IN_SECONDS
|
137 |
+
);
|
|
|
|
|
|
|
138 |
}
|
139 |
|
140 |
+
public function setIsDisplayPluginBadge( bool $isDisplay ) {
|
141 |
+
$this->getOptions()->setOpt( 'display_plugin_badge', $isDisplay ? 'Y' : 'N' );
|
|
|
|
|
|
|
|
|
142 |
}
|
143 |
}
|
src/lib/src/Modules/Plugin/Components/TestForCloudflareAPO.php
CHANGED
@@ -6,7 +6,7 @@ use FernleafSystems\Wordpress\Services\Services;
|
|
6 |
|
7 |
class TestForCloudflareAPO {
|
8 |
|
9 |
-
public function run() {
|
10 |
$req = Services::Request();
|
11 |
$srvIP = Services::IP();
|
12 |
$visitorIP = $srvIP->getRequestIp();
|
6 |
|
7 |
class TestForCloudflareAPO {
|
8 |
|
9 |
+
public function run() :bool {
|
10 |
$req = Services::Request();
|
11 |
$srvIP = Services::IP();
|
12 |
$visitorIP = $srvIP->getRequestIp();
|
src/lib/src/Modules/Plugin/Debug.php
CHANGED
@@ -1,11 +1,20 @@
|
|
1 |
-
<?php
|
2 |
|
3 |
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Plugin;
|
4 |
|
5 |
use FernleafSystems\Wordpress\Plugin\Shield\Modules;
|
|
|
6 |
|
7 |
class Debug extends Modules\Base\Debug {
|
8 |
|
9 |
public function run() {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
10 |
}
|
11 |
}
|
1 |
+
<?php declare( strict_types=1 );
|
2 |
|
3 |
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Plugin;
|
4 |
|
5 |
use FernleafSystems\Wordpress\Plugin\Shield\Modules;
|
6 |
+
use FernleafSystems\Wordpress\Services\Utilities\Net\IpIdentify;
|
7 |
|
8 |
class Debug extends Modules\Base\Debug {
|
9 |
|
10 |
public function run() {
|
11 |
+
$this->ipID();
|
12 |
+
die();
|
13 |
+
}
|
14 |
+
|
15 |
+
private function ipID() {
|
16 |
+
$id = ( new IpIdentify( '198.61.176.9' ) )
|
17 |
+
->run();
|
18 |
+
var_dump( $id );
|
19 |
}
|
20 |
}
|
src/lib/src/Modules/Plugin/Insights/AdminNotes.php
CHANGED
@@ -3,12 +3,13 @@
|
|
3 |
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Plugin\Insights;
|
4 |
|
5 |
use FernleafSystems\Wordpress\Plugin\Shield\Modules\ModConsumer;
|
|
|
6 |
|
7 |
class AdminNotes {
|
8 |
|
9 |
use ModConsumer;
|
10 |
|
11 |
-
public function
|
12 |
return $this->getMod()
|
13 |
->renderTemplate(
|
14 |
'/wpadmin_pages/insights/notes/admin_notes.twig',
|
@@ -18,8 +19,7 @@ class AdminNotes {
|
|
18 |
}
|
19 |
|
20 |
private function buildData() :array {
|
21 |
-
|
22 |
-
/** @var \ICWP_WPSF_FeatureHandler_Plugin $mod */
|
23 |
$mod = $this->getMod();
|
24 |
|
25 |
return [
|
@@ -30,9 +30,6 @@ class AdminNotes {
|
|
30 |
'item_insert' => $mod->getAjaxActionData( 'note_insert', true ),
|
31 |
'bulk_action' => $mod->getAjaxActionData( 'bulk_action', true ),
|
32 |
],
|
33 |
-
'flags' => [
|
34 |
-
'can_adminnotes' => $con->isPremiumActive(),
|
35 |
-
],
|
36 |
'strings' => [
|
37 |
'note_title' => __( 'Administrator Notes', 'wp-simple-firewall' ),
|
38 |
'use_this_area' => __( 'Use this feature to make ongoing notes and to-dos', 'wp-simple-firewall' ),
|
3 |
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Plugin\Insights;
|
4 |
|
5 |
use FernleafSystems\Wordpress\Plugin\Shield\Modules\ModConsumer;
|
6 |
+
use FernleafSystems\Wordpress\Plugin\Shield\Modules\Plugin\ModCon;
|
7 |
|
8 |
class AdminNotes {
|
9 |
|
10 |
use ModConsumer;
|
11 |
|
12 |
+
public function render() :string {
|
13 |
return $this->getMod()
|
14 |
->renderTemplate(
|
15 |
'/wpadmin_pages/insights/notes/admin_notes.twig',
|
19 |
}
|
20 |
|
21 |
private function buildData() :array {
|
22 |
+
/** @var ModCon $mod */
|
|
|
23 |
$mod = $this->getMod();
|
24 |
|
25 |
return [
|
30 |
'item_insert' => $mod->getAjaxActionData( 'note_insert', true ),
|
31 |
'bulk_action' => $mod->getAjaxActionData( 'bulk_action', true ),
|
32 |
],
|
|
|
|
|
|
|
33 |
'strings' => [
|
34 |
'note_title' => __( 'Administrator Notes', 'wp-simple-firewall' ),
|
35 |
'use_this_area' => __( 'Use this feature to make ongoing notes and to-dos', 'wp-simple-firewall' ),
|
src/lib/src/Modules/Plugin/Insights/DashboardCards.php
ADDED
@@ -0,0 +1,410 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php declare( strict_types=1 );
|
2 |
+
|
3 |
+
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Plugin\Insights;
|
4 |
+
|
5 |
+
use FernleafSystems\Wordpress\Plugin\Shield;
|
6 |
+
use FernleafSystems\Wordpress\Plugin\Shield\Databases\AdminNotes;
|
7 |
+
use FernleafSystems\Wordpress\Plugin\Shield\Modules\Plugin;
|
8 |
+
|
9 |
+
class DashboardCards {
|
10 |
+
|
11 |
+
use Shield\Modules\ModConsumer;
|
12 |
+
|
13 |
+
/**
|
14 |
+
* @return array
|
15 |
+
* @throws \Exception
|
16 |
+
*/
|
17 |
+
public function renderAll() :array {
|
18 |
+
$cards = array_merge(
|
19 |
+
[
|
20 |
+
'settings' => $this->renderSettingsCard()
|
21 |
+
],
|
22 |
+
array_map(
|
23 |
+
function ( $card ) {
|
24 |
+
return $this->renderStandardCard( $card );
|
25 |
+
},
|
26 |
+
array_filter(
|
27 |
+
$this->buildStandardCards(),
|
28 |
+
function ( $card ) {
|
29 |
+
return empty( $card[ 'hidden' ] );
|
30 |
+
}
|
31 |
+
)
|
32 |
+
)
|
33 |
+
);
|
34 |
+
|
35 |
+
if ( !empty( array_diff_key( $cards, array_flip( $this->getAllCardSlugs() ) ) ) ) {
|
36 |
+
throw new \Exception( 'Card(s) with unrecognised slug' );
|
37 |
+
}
|
38 |
+
|
39 |
+
// Merge ensures the order is as we want it, and the intersect ensure hidden cards are not included
|
40 |
+
return array_merge(
|
41 |
+
array_flip( array_intersect( $this->getAllCardSlugs(), array_keys( $cards ) ) ),
|
42 |
+
$cards
|
43 |
+
);
|
44 |
+
}
|
45 |
+
|
46 |
+
public function renderSettingsCard() :string {
|
47 |
+
$con = $this->getCon();
|
48 |
+
/** @var Plugin\ModCon $mod */
|
49 |
+
$mod = $this->getMod();
|
50 |
+
|
51 |
+
return $mod->renderTemplate(
|
52 |
+
'/wpadmin_pages/insights/dashboard/card_settings.twig',
|
53 |
+
[
|
54 |
+
'c' => [
|
55 |
+
'title' => __( 'Shield Settings', 'wp-simple-firewall' ),
|
56 |
+
'img' => $con->getPluginUrl_Image( 'bootstrap/sliders.svg' ),
|
57 |
+
'paras' => [
|
58 |
+
sprintf( __( "%s settings are arranged into modules.", 'wp-simple-firewall' ), $con->getHumanName() )
|
59 |
+
.' '.__( 'Choose the module you need from the dropdown.', 'wp-simple-firewall' )
|
60 |
+
],
|
61 |
+
'actions' => [
|
62 |
+
[
|
63 |
+
'text' => __( "Go To General Settings", 'wp-simple-firewall' ),
|
64 |
+
'href' => $mod->getUrl_AdminPage(),
|
65 |
+
],
|
66 |
+
[
|
67 |
+
'text' => __( "Scans & Hack Guard Settings", 'wp-simple-firewall' ),
|
68 |
+
'href' => $con->getModule_HackGuard()->getUrl_AdminPage(),
|
69 |
+
],
|
70 |
+
],
|
71 |
+
],
|
72 |
+
'strings' => [
|
73 |
+
'select' => __( "Select Module", 'wp-simple-firewall' )
|
74 |
+
],
|
75 |
+
'vars' => [
|
76 |
+
'mods' => $mod->getUIHandler()->buildSelectData_ModuleSettings(),
|
77 |
+
'search_select' => $mod->getUIHandler()->buildSelectData_OptionsSearch()
|
78 |
+
]
|
79 |
+
],
|
80 |
+
true
|
81 |
+
);
|
82 |
+
}
|
83 |
+
|
84 |
+
protected function renderStandardCard( $card ) {
|
85 |
+
/** @var Plugin\ModCon $mod */
|
86 |
+
$mod = $this->getMod();
|
87 |
+
return $mod->renderTemplate(
|
88 |
+
'/wpadmin_pages/insights/dashboard/card_std.twig',
|
89 |
+
[ 'c' => $card ],
|
90 |
+
true
|
91 |
+
);
|
92 |
+
}
|
93 |
+
|
94 |
+
private function buildStandardCards() :array {
|
95 |
+
$con = $this->getCon();
|
96 |
+
$modComments = $con->getModule_Comments();
|
97 |
+
$modInsights = $con->getModule_Insights();
|
98 |
+
$modPlugin = $con->getModule_Plugin();
|
99 |
+
|
100 |
+
/** @var AdminNotes\EntryVO $note */
|
101 |
+
$note = $modPlugin->getDbHandler_Notes()->getQuerySelector()->first();
|
102 |
+
$latestNote = $note instanceof AdminNotes\EntryVO ?
|
103 |
+
sprintf( 'Your most recent note: "%s"', $note->note ) :
|
104 |
+
__( 'No notes made yet.', 'wp-simple-firewall' );
|
105 |
+
|
106 |
+
return [
|
107 |
+
|
108 |
+
'overview' => [
|
109 |
+
'title' => __( 'Security Overview', 'wp-simple-firewall' ),
|
110 |
+
'img' => $con->getPluginUrl_Image( 'bootstrap/binoculars.svg' ),
|
111 |
+
'paras' => [
|
112 |
+
sprintf( __( "Review your entire Shield Security configuration at a glance to see what's working and what's not.", 'wp-simple-firewall' ), $con->getHumanName() ),
|
113 |
+
],
|
114 |
+
'actions' => [
|
115 |
+
[
|
116 |
+
'text' => __( "See My Security Overview", 'wp-simple-firewall' ),
|
117 |
+
'href' => $modInsights->getUrl_SubInsightsPage( 'overview' ),
|
118 |
+
],
|
119 |
+
]
|
120 |
+
],
|
121 |
+
|
122 |
+
'scans' => [
|
123 |
+
'title' => __( 'Scans and Protection', 'wp-simple-firewall' ),
|
124 |
+
'img' => $con->getPluginUrl_Image( 'bootstrap/shield-shaded.svg' ),
|
125 |
+
'paras' => [
|
126 |
+
sprintf( __( "Use %s Scans to automatically detect and repair intrusions on your site.", 'wp-simple-firewall' ), $con->getHumanName() ),
|
127 |
+
sprintf( __( "%s scans WordPress core files, plugins, themes and will detect Malware (ShieldPRO).", 'wp-simple-firewall' ), $con->getHumanName() ),
|
128 |
+
],
|
129 |
+
'actions' => [
|
130 |
+
[
|
131 |
+
'text' => __( "Run Scans", 'wp-simple-firewall' ),
|
132 |
+
'href' => $modInsights->getUrl_SubInsightsPage( 'scans' ),
|
133 |
+
],
|
134 |
+
[
|
135 |
+
'text' => __( "Scans & Hack Guard Settings", 'wp-simple-firewall' ),
|
136 |
+
'href' => $con->getModule_HackGuard()->getUrl_AdminPage(),
|
137 |
+
],
|
138 |
+
]
|
139 |
+
],
|
140 |
+
|
141 |
+
'sec_admin' => [
|
142 |
+
'title' => __( 'Security Admin', 'wp-simple-firewall' ),
|
143 |
+
'img' => $con->getPluginUrl_Image( 'bootstrap/person-badge.svg' ),
|
144 |
+
'paras' => [
|
145 |
+
sprintf( __( "Restrict access to %s itself and prevent unwanted changes to your site by other administrators.", 'wp-simple-firewall' ), $con->getHumanName() ),
|
146 |
+
],
|
147 |
+
'actions' => [
|
148 |
+
[
|
149 |
+
'text' => __( "Security Admin Settings", 'wp-simple-firewall' ),
|
150 |
+
'href' => $con->getModule_SecAdmin()->getUrl_AdminPage(),
|
151 |
+
],
|
152 |
+
]
|
153 |
+
],
|
154 |
+
|
155 |
+
'free_trial' => [
|
156 |
+
'title' => __( 'Free ShieldPRO Trial', 'wp-simple-firewall' ),
|
157 |
+
'img' => $con->getPluginUrl_Image( 'bootstrap/emoji-smile.svg' ),
|
158 |
+
'paras' => [
|
159 |
+
__( "Full, unrestricted access to ShieldPRO with no obligation.", 'wp-simple-firewall' ),
|
160 |
+
__( "Turn-on the ShieldPRO trial within 60 seconds.", 'wp-simple-firewall' )
|
161 |
+
],
|
162 |
+
'actions' => [
|
163 |
+
[
|
164 |
+
'text' => __( "Get The Free Trial", 'wp-simple-firewall' ),
|
165 |
+
'href' => $modInsights->getUrl_SubInsightsPage( 'free_trial' ),
|
166 |
+
],
|
167 |
+
],
|
168 |
+
'classes' => $con->isPremiumActive() ? [] : [ 'highlighted', 'text-white', 'bg-primary' ],
|
169 |
+
'hidden' => $con->isPremiumActive()
|
170 |
+
],
|
171 |
+
|
172 |
+
'ips' => [
|
173 |
+
'title' => __( 'IP Blocking and Bypass', 'wp-simple-firewall' ),
|
174 |
+
'img' => $con->getPluginUrl_Image( 'bootstrap/diagram-3.svg' ),
|
175 |
+
'paras' => [
|
176 |
+
__( "Shield automatically detects and blocks bad IP addresses based on your security settings.", 'wp-simple-firewall' ),
|
177 |
+
__( "The IP Analysis Tool shows you all information for a given IP as it relates to your site.", 'wp-simple-firewall' ),
|
178 |
+
],
|
179 |
+
'actions' => [
|
180 |
+
[
|
181 |
+
'text' => __( "Analyse & Manage IPs", 'wp-simple-firewall' ),
|
182 |
+
'href' => $modInsights->getUrl_SubInsightsPage( 'ips' ),
|
183 |
+
],
|
184 |
+
[
|
185 |
+
'text' => __( "IP Blocking Settings", 'wp-simple-firewall' ),
|
186 |
+
'href' => $con->getModule_IPs()->getUrl_AdminPage(),
|
187 |
+
],
|
188 |
+
]
|
189 |
+
],
|
190 |
+
|
191 |
+
'audit_trail' => [
|
192 |
+
'title' => __( 'Audit Trail', 'wp-simple-firewall' ),
|
193 |
+
'img' => $con->getPluginUrl_Image( 'bootstrap/person-lines-fill.svg' ),
|
194 |
+
'paras' => [
|
195 |
+
__( "Provides in-depth logging for all major WordPress events.", 'wp-simple-firewall' ),
|
196 |
+
],
|
197 |
+
'actions' => [
|
198 |
+
[
|
199 |
+
'text' => __( "View Audit Log", 'wp-simple-firewall' ),
|
200 |
+
'href' => $modInsights->getUrl_SubInsightsPage( 'audit' ),
|
201 |
+
],
|
202 |
+
[
|
203 |
+
'text' => __( "Audit Trail Settings", 'wp-simple-firewall' ),
|
204 |
+
'href' => $con->getModule_AuditTrail()->getUrl_AdminPage(),
|
205 |
+
],
|
206 |
+
]
|
207 |
+
],
|
208 |
+
|
209 |
+
'traffic' => [
|
210 |
+
'title' => __( 'Traffic Logging', 'wp-simple-firewall' ),
|
211 |
+
'img' => $con->getPluginUrl_Image( 'bootstrap/stoplights.svg' ),
|
212 |
+
'paras' => [
|
213 |
+
__( "Use traffic logging to monitor visitor requests to your site.", 'wp-simple-firewall' ),
|
214 |
+
__( "Traffic Rate Limiting lets you throttle requests from any single visitor.", 'wp-simple-firewall' ),
|
215 |
+
],
|
216 |
+
'actions' => [
|
217 |
+
[
|
218 |
+
'text' => __( "View Traffic Log", 'wp-simple-firewall' ),
|
219 |
+
'href' => $modInsights->getUrl_SubInsightsPage( 'traffic' ),
|
220 |
+
],
|
221 |
+
[
|
222 |
+
'text' => __( "Traffic Log Settings", 'wp-simple-firewall' ),
|
223 |
+
'href' => $con->getModule_Traffic()->getUrl_AdminPage(),
|
224 |
+
],
|
225 |
+
]
|
226 |
+
],
|
227 |
+
|
228 |
+
'users' => [
|
229 |
+
'title' => __( 'WordPress Users', 'wp-simple-firewall' ),
|
230 |
+
'img' => $con->getPluginUrl_Image( 'bootstrap/people.svg' ),
|
231 |
+
'paras' => [
|
232 |
+
__( "Adds fine control over user sessions, account re-use, password strength and expiration, and user suspension.", 'wp-simple-firewall' ),
|
233 |
+
],
|
234 |
+
'actions' => [
|
235 |
+
[
|
236 |
+
'text' => __( "User Settings", 'wp-simple-firewall' ),
|
237 |
+
'href' => $con->getModule_UserManagement()->getUrl_AdminPage(),
|
238 |
+
],
|
239 |
+
[
|
240 |
+
'text' => __( "Manage User Sessions", 'wp-simple-firewall' ),
|
241 |
+
'href' => $modInsights->getUrl_SubInsightsPage( 'users' ),
|
242 |
+
],
|
243 |
+
]
|
244 |
+
],
|
245 |
+
|
246 |
+
'comments' => [
|
247 |
+
'title' => __( 'Comment SPAM', 'wp-simple-firewall' ),
|
248 |
+
'img' => $con->getPluginUrl_Image( 'bootstrap/chat-right-dots-fill.svg' ),
|
249 |
+
'paras' => [
|
250 |
+
__( "Shield blocks 100% of all automated comments by bots (the most common type of SPAM).", 'wp-simple-firewall' ).
|
251 |
+
' '.__( "The Human SPAM filter will look for common spam words and content.", 'wp-simple-firewall' ),
|
252 |
+
sprintf( '%s: %s',
|
253 |
+
__( "Privacy Note", 'wp-simple-firewall' ),
|
254 |
+
__( "Unlike Akismet, your comments and data are never sent off-site for analysis.", 'wp-simple-firewall' )
|
255 |
+
)
|
256 |
+
],
|
257 |
+
'actions' => [
|
258 |
+
[
|
259 |
+
'text' => __( "Bot SPAM Settings", 'wp-simple-firewall' ),
|
260 |
+
'href' => $modComments->getUrl_DirectLinkToSection( 'section_bot_comment_spam_protection_filter' ),
|
261 |
+
],
|
262 |
+
[
|
263 |
+
'text' => __( "Human SPAM Settings", 'wp-simple-firewall' ),
|
264 |
+
'href' => $modComments->getUrl_DirectLinkToSection( 'section_human_spam_filter' ),
|
265 |
+
],
|
266 |
+
]
|
267 |
+
],
|
268 |
+
|
269 |
+
'import' => [
|
270 |
+
'title' => __( 'Import/Export', 'wp-simple-firewall' ),
|
271 |
+
'img' => $con->getPluginUrl_Image( 'bootstrap/arrow-down-up.svg' ),
|
272 |
+
'paras' => [
|
273 |
+
__( "Use the import/export feature to quickly setup a new site based on the settings of another site.", 'wp-simple-firewall' ),
|
274 |
+
__( "You can also setup automatic syncing of settings between sites.", 'wp-simple-firewall' ),
|
275 |
+
],
|
276 |
+
'actions' => [
|
277 |
+
[
|
278 |
+
'text' => __( "Run Import/Export", 'wp-simple-firewall' ),
|
279 |
+
'href' => $modInsights->getUrl_SubInsightsPage( 'importexport' ),
|
280 |
+
],
|
281 |
+
[
|
282 |
+
'text' => __( "Import/Export Settings", 'wp-simple-firewall' ),
|
283 |
+
'href' => $modPlugin->getUrl_DirectLinkToSection( 'section_importexport' ),
|
284 |
+
],
|
285 |
+
]
|
286 |
+
],
|
287 |
+
|
288 |
+
'license' => [
|
289 |
+
'title' => __( 'Go PRO!', 'wp-simple-firewall' ),
|
290 |
+
'img' => $con->getPluginUrl_Image( 'bootstrap/award.svg' ),
|
291 |
+
'paras' => [
|
292 |
+
__( "By upgrading to ShieldPRO, you support ongoing Shield development and get access to exclusive PRO features.", 'wp-simple-firewall' ),
|
293 |
+
],
|
294 |
+
'actions' => [
|
295 |
+
[
|
296 |
+
'text' => $con->isPremiumActive() ? __( "Manage PRO", 'wp-simple-firewall' ) : __( "Go PRO!", 'wp-simple-firewall' ),
|
297 |
+
'href' => $modInsights->getUrl_SubInsightsPage( 'license' ),
|
298 |
+
],
|
299 |
+
[
|
300 |
+
'text' => __( "See Exclusive ShieldPRO Features", 'wp-simple-firewall' ),
|
301 |
+
'href' => 'https://shsec.io/gp',
|
302 |
+
'new' => true,
|
303 |
+
],
|
304 |
+
],
|
305 |
+
'classes' => $con->isPremiumActive() ? [] : [ 'highlighted', 'text-white', 'bg-success' ]
|
306 |
+
],
|
307 |
+
|
308 |
+
'notes' => [
|
309 |
+
'title' => __( 'Admin Notes', 'wp-simple-firewall' ),
|
310 |
+
'img' => $con->getPluginUrl_Image( 'bootstrap/pencil-square.svg' ),
|
311 |
+
'paras' => [
|
312 |
+
__( "Use these to keep note of important items or to-dos.", 'wp-simple-firewall' ),
|
313 |
+
$latestNote
|
314 |
+
],
|
315 |
+
'actions' => [
|
316 |
+
[
|
317 |
+
'text' => __( "Manage Admin Notes", 'wp-simple-firewall' ),
|
318 |
+
'href' => $modInsights->getUrl_SubInsightsPage( 'notes' ),
|
319 |
+
],
|
320 |
+
]
|
321 |
+
],
|
322 |
+
|
323 |
+
'whitelabel' => [
|
324 |
+
'title' => __( 'Whitelabel', 'wp-simple-firewall' ),
|
325 |
+
'img' => $con->getPluginUrl_Image( 'bootstrap/sticky.svg' ),
|
326 |
+
'paras' => [
|
327 |
+
__( "Re-brand the Shield Security plugin your image.", 'wp-simple-firewall' ),
|
328 |
+
__( "Use this to enhance and solidify your brand with your clients and visitors.", 'wp-simple-firewall' ),
|
329 |
+
],
|
330 |
+
'actions' => [
|
331 |
+
[
|
332 |
+
'text' => __( "Manage White Label", 'wp-simple-firewall' ),
|
333 |
+
'href' => $con->getModule_SecAdmin()
|
334 |
+
->getUrl_DirectLinkToSection( 'section_whitelabel' ),
|
335 |
+
],
|
336 |
+
]
|
337 |
+
],
|
338 |
+
|
339 |
+
'integrations' => [
|
340 |
+
'title' => __( '3rd Party Integrations', 'wp-simple-firewall' ),
|
341 |
+
'img' => $con->getPluginUrl_Image( 'bootstrap/link-45deg.svg' ),
|
342 |
+
'paras' => [
|
343 |
+
__( "Shield integrates with 3rd party plugins and services.", 'wp-simple-firewall' ),
|
344 |
+
__( "Determine what integrations Shield should use and manage the settings for them.", 'wp-simple-firewall' ),
|
345 |
+
],
|
346 |
+
'actions' => [
|
347 |
+
[
|
348 |
+
'text' => __( "Manage Integrations", 'wp-simple-firewall' ),
|
349 |
+
'href' => $con->getModule_Integrations()->getUrl_AdminPage(),
|
350 |
+
],
|
351 |
+
]
|
352 |
+
],
|
353 |
+
|
354 |
+
'docs' => [
|
355 |
+
'title' => __( 'Docs', 'wp-simple-firewall' ),
|
356 |
+
'img' => $con->getPluginUrl_Image( 'bootstrap/book-half.svg' ),
|
357 |
+
'paras' => [
|
358 |
+
sprintf( __( "Important information about %s releases and changes.", 'wp-simple-firewall' ), $con->getHumanName() ),
|
359 |
+
],
|
360 |
+
'actions' => [
|
361 |
+
[
|
362 |
+
'text' => __( "View Docs", 'wp-simple-firewall' ),
|
363 |
+
'href' => $modInsights->getUrl_SubInsightsPage( 'docs' ),
|
364 |
+
],
|
365 |
+
]
|
366 |
+
],
|
367 |
+
|
368 |
+
'debug' => [
|
369 |
+
'title' => __( 'Debug Info', 'wp-simple-firewall' ),
|
370 |
+
'img' => $con->getPluginUrl_Image( 'bootstrap/bug.svg' ),
|
371 |
+
'paras' => [
|
372 |
+
__( "If you contact support, they may ask you to show them your Debug Information page.", 'wp-simple-firewall' ),
|
373 |
+
__( "It's also an interesting place to see a summary of your WordPress configuration in 1 place.", 'wp-simple-firewall' ),
|
374 |
+
],
|
375 |
+
'actions' => [
|
376 |
+
[
|
377 |
+
'text' => __( "View Debug Info", 'wp-simple-firewall' ),
|
378 |
+
'href' => $modInsights->getUrl_SubInsightsPage( 'debug' ),
|
379 |
+
],
|
380 |
+
]
|
381 |
+
],
|
382 |
+
|
383 |
+
];
|
384 |
+
}
|
385 |
+
|
386 |
+
/**
|
387 |
+
* Allows us to order Dashboard cards
|
388 |
+
*/
|
389 |
+
private function getAllCardSlugs() :array {
|
390 |
+
return [
|
391 |
+
'overview',
|
392 |
+
'settings',
|
393 |
+
'scans',
|
394 |
+
'free_trial',
|
395 |
+
'sec_admin',
|
396 |
+
'ips',
|
397 |
+
'audit_trail',
|
398 |
+
'traffic',
|
399 |
+
'users',
|
400 |
+
'comments',
|
401 |
+
'import',
|
402 |
+
'license',
|
403 |
+
'integrations',
|
404 |
+
'notes',
|
405 |
+
'whitelabel',
|
406 |
+
'docs',
|
407 |
+
'debug',
|
408 |
+
];
|
409 |
+
}
|
410 |
+
}
|
src/lib/src/Modules/Plugin/Insights/OverviewCards.php
CHANGED
@@ -3,15 +3,16 @@
|
|
3 |
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Plugin\Insights;
|
4 |
|
5 |
use FernleafSystems\Wordpress\Plugin\Shield;
|
|
|
6 |
use FernleafSystems\Wordpress\Services\Services;
|
7 |
use FernleafSystems\Wordpress\Services\Utilities\Ssl;
|
8 |
|
9 |
class OverviewCards extends Shield\Modules\Base\Insights\OverviewCards {
|
10 |
|
11 |
public function build() :array {
|
12 |
-
/** @var \
|
13 |
$mod = $this->getMod();
|
14 |
-
/** @var
|
15 |
$opts = $this->getOptions();
|
16 |
|
17 |
$cardSection = [
|
3 |
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Plugin\Insights;
|
4 |
|
5 |
use FernleafSystems\Wordpress\Plugin\Shield;
|
6 |
+
use FernleafSystems\Wordpress\Plugin\Shield\Modules\Plugin;
|
7 |
use FernleafSystems\Wordpress\Services\Services;
|
8 |
use FernleafSystems\Wordpress\Services\Utilities\Ssl;
|
9 |
|
10 |
class OverviewCards extends Shield\Modules\Base\Insights\OverviewCards {
|
11 |
|
12 |
public function build() :array {
|
13 |
+
/** @var Plugin\ModCon $mod */
|
14 |
$mod = $this->getMod();
|
15 |
+
/** @var Plugin\Options $opts */
|
16 |
$opts = $this->getOptions();
|
17 |
|
18 |
$cardSection = [
|
src/lib/src/Modules/Plugin/Lib/Captcha/CheckCaptchaSettings.php
CHANGED
@@ -24,11 +24,11 @@ class CheckCaptchaSettings {
|
|
24 |
}
|
25 |
|
26 |
public function verifyKeys() {
|
27 |
-
/** @var \
|
28 |
-
$
|
29 |
/** @var Plugin\Options $oOpts */
|
30 |
$oOpts = $this->getOptions();
|
31 |
-
$oCfg = $
|
32 |
|
33 |
$nAt = -1;
|
34 |
if ( $oCfg->ready && $oOpts->getOpt( 'captcha_checked_at' ) <= 0 ) {
|
@@ -50,9 +50,9 @@ class CheckCaptchaSettings {
|
|
50 |
* @return bool
|
51 |
*/
|
52 |
private function verifyHcaptcha() {
|
53 |
-
/** @var \
|
54 |
-
$
|
55 |
-
$oCfg = $
|
56 |
return substr_count( $oCfg->key, '-' ) > 1
|
57 |
&& strpos( $oCfg->secret, '0x' ) === 0;
|
58 |
}
|
@@ -60,24 +60,24 @@ class CheckCaptchaSettings {
|
|
60 |
/**
|
61 |
* @return bool
|
62 |
*/
|
63 |
-
private function verifyRecaptcha() {
|
64 |
-
/** @var \
|
65 |
-
$
|
66 |
|
67 |
$sResponse = Services::HttpRequest()->getContent( add_query_arg(
|
68 |
[
|
69 |
-
'secret' => $
|
70 |
'response' => rand(),
|
71 |
],
|
72 |
'https://www.google.com/recaptcha/api/siteverify'
|
73 |
) );
|
74 |
|
75 |
-
$
|
76 |
if ( !empty( $sResponse ) ) {
|
77 |
$aDec = json_decode( $sResponse, true );
|
78 |
-
$
|
79 |
-
|
80 |
}
|
81 |
-
return $
|
82 |
}
|
83 |
}
|
24 |
}
|
25 |
|
26 |
public function verifyKeys() {
|
27 |
+
/** @var Plugin\ModCon $mod */
|
28 |
+
$mod = $this->getMod();
|
29 |
/** @var Plugin\Options $oOpts */
|
30 |
$oOpts = $this->getOptions();
|
31 |
+
$oCfg = $mod->getCaptchaCfg();
|
32 |
|
33 |
$nAt = -1;
|
34 |
if ( $oCfg->ready && $oOpts->getOpt( 'captcha_checked_at' ) <= 0 ) {
|
50 |
* @return bool
|
51 |
*/
|
52 |
private function verifyHcaptcha() {
|
53 |
+
/** @var Plugin\ModCon $mod */
|
54 |
+
$mod = $this->getMod();
|
55 |
+
$oCfg = $mod->getCaptchaCfg();
|
56 |
return substr_count( $oCfg->key, '-' ) > 1
|
57 |
&& strpos( $oCfg->secret, '0x' ) === 0;
|
58 |
}
|
60 |
/**
|
61 |
* @return bool
|
62 |
*/
|
63 |
+
private function verifyRecaptcha() :bool {
|
64 |
+
/** @var Plugin\ModCon $mod */
|
65 |
+
$mod = $this->getMod();
|
66 |
|
67 |
$sResponse = Services::HttpRequest()->getContent( add_query_arg(
|
68 |
[
|
69 |
+
'secret' => $mod->getCaptchaCfg()->secret,
|
70 |
'response' => rand(),
|
71 |
],
|
72 |
'https://www.google.com/recaptcha/api/siteverify'
|
73 |
) );
|
74 |
|
75 |
+
$valid = false;
|
76 |
if ( !empty( $sResponse ) ) {
|
77 |
$aDec = json_decode( $sResponse, true );
|
78 |
+
$valid = is_array( $aDec ) && is_array( $aDec[ 'error-codes' ] )
|
79 |
+
&& !in_array( 'invalid-input-secret', $aDec[ 'error-codes' ] );
|
80 |
}
|
81 |
+
return $valid;
|
82 |
}
|
83 |
}
|
src/lib/src/Modules/Plugin/Lib/ImportExport/Export.php
CHANGED
@@ -32,17 +32,17 @@ class Export {
|
|
32 |
}
|
33 |
|
34 |
public function toJson() {
|
35 |
-
/** @var \
|
36 |
-
$
|
37 |
-
$
|
38 |
|
39 |
-
$sSecretKey = $
|
40 |
|
41 |
-
$sNetworkOpt = $
|
42 |
$bDoNetwork = !empty( $sNetworkOpt );
|
43 |
-
$sUrl = Services::Data()->validateSimpleHttpUrl( $
|
44 |
|
45 |
-
if ( !$
|
46 |
return; // we show no signs of responding to invalid secret keys or unwhitelisted URLs
|
47 |
}
|
48 |
|
@@ -66,14 +66,14 @@ class Export {
|
|
66 |
|
67 |
if ( $bDoNetwork ) {
|
68 |
if ( $sNetworkOpt === 'Y' ) {
|
69 |
-
$
|
70 |
$this->getCon()->fireEvent(
|
71 |
'whitelist_site_added',
|
72 |
[ 'audit' => [ 'site' => $sUrl ] ]
|
73 |
);
|
74 |
}
|
75 |
else {
|
76 |
-
$
|
77 |
$this->getCon()->fireEvent(
|
78 |
'whitelist_site_removed',
|
79 |
[ 'audit' => [ 'site' => $sUrl ] ]
|
32 |
}
|
33 |
|
34 |
public function toJson() {
|
35 |
+
/** @var Plugin\ModCon $mod */
|
36 |
+
$mod = $this->getMod();
|
37 |
+
$req = Services::Request();
|
38 |
|
39 |
+
$sSecretKey = $req->query( 'secret', '' );
|
40 |
|
41 |
+
$sNetworkOpt = $req->query( 'network', '' );
|
42 |
$bDoNetwork = !empty( $sNetworkOpt );
|
43 |
+
$sUrl = Services::Data()->validateSimpleHttpUrl( $req->query( 'url', '' ) );
|
44 |
|
45 |
+
if ( !$mod->isImportExportSecretKey( $sSecretKey ) && !$this->isUrlOnWhitelist( $sUrl ) ) {
|
46 |
return; // we show no signs of responding to invalid secret keys or unwhitelisted URLs
|
47 |
}
|
48 |
|
66 |
|
67 |
if ( $bDoNetwork ) {
|
68 |
if ( $sNetworkOpt === 'Y' ) {
|
69 |
+
$mod->addUrlToImportExportWhitelistUrls( $sUrl );
|
70 |
$this->getCon()->fireEvent(
|
71 |
'whitelist_site_added',
|
72 |
[ 'audit' => [ 'site' => $sUrl ] ]
|
73 |
);
|
74 |
}
|
75 |
else {
|
76 |
+
$mod->removeUrlFromImportExportWhitelistUrls( $sUrl );
|
77 |
$this->getCon()->fireEvent(
|
78 |
'whitelist_site_removed',
|
79 |
[ 'audit' => [ 'site' => $sUrl ] ]
|
src/lib/src/Modules/Plugin/Lib/ImportExport/Import.php
CHANGED
@@ -99,18 +99,18 @@ class Import {
|
|
99 |
* @throws \Exception
|
100 |
*/
|
101 |
public function fromSite( $sMasterSiteUrl = '', $sSecretKey = '', $bEnableNetwork = null ) {
|
102 |
-
/** @var Plugin\Options $
|
103 |
-
$
|
104 |
-
/** @var \
|
105 |
$mod = $this->getMod();
|
106 |
-
$
|
107 |
|
108 |
if ( empty( $sMasterSiteUrl ) ) {
|
109 |
-
$sMasterSiteUrl = $
|
110 |
}
|
111 |
|
112 |
-
$sOriginalMasterSiteUrl = $
|
113 |
-
$bHadMasterSiteUrl = $
|
114 |
$bCheckKeyFormat = !$bHadMasterSiteUrl;
|
115 |
$sSecretKey = sanitize_key( $sSecretKey );
|
116 |
|
@@ -135,13 +135,13 @@ class Import {
|
|
135 |
if ( !$bHasParts ) {
|
136 |
throw new \Exception( "Couldn't parse the URL into its parts", 4 );
|
137 |
}
|
138 |
-
$sMasterSiteUrl = $
|
139 |
if ( empty( $sMasterSiteUrl ) ) {
|
140 |
throw new \Exception( "Couldn't validate the URL.", 4 );
|
141 |
}
|
142 |
|
143 |
// Begin the handshake process.
|
144 |
-
$
|
145 |
'importexport_handshake_expires_at',
|
146 |
Services::Request()->ts() + 30
|
147 |
);
|
@@ -186,7 +186,7 @@ class Import {
|
|
186 |
// Fix for the overwriting of the Master Site URL with an empty string.
|
187 |
// Only do so if we're not turning it off. i.e on or no-change
|
188 |
if ( is_null( $bEnableNetwork ) ) {
|
189 |
-
if ( $bHadMasterSiteUrl && !$
|
190 |
$mod->setImportExportMasterImportUrl( $sOriginalMasterSiteUrl );
|
191 |
}
|
192 |
}
|
99 |
* @throws \Exception
|
100 |
*/
|
101 |
public function fromSite( $sMasterSiteUrl = '', $sSecretKey = '', $bEnableNetwork = null ) {
|
102 |
+
/** @var Plugin\Options $opts */
|
103 |
+
$opts = $this->getOptions();
|
104 |
+
/** @var Plugin\ModCon $mod */
|
105 |
$mod = $this->getMod();
|
106 |
+
$DP = Services::Data();
|
107 |
|
108 |
if ( empty( $sMasterSiteUrl ) ) {
|
109 |
+
$sMasterSiteUrl = $opts->getImportExportMasterImportUrl();
|
110 |
}
|
111 |
|
112 |
+
$sOriginalMasterSiteUrl = $opts->getImportExportMasterImportUrl();
|
113 |
+
$bHadMasterSiteUrl = $opts->hasImportExportMasterImportUrl();
|
114 |
$bCheckKeyFormat = !$bHadMasterSiteUrl;
|
115 |
$sSecretKey = sanitize_key( $sSecretKey );
|
116 |
|
135 |
if ( !$bHasParts ) {
|
136 |
throw new \Exception( "Couldn't parse the URL into its parts", 4 );
|
137 |
}
|
138 |
+
$sMasterSiteUrl = $DP->validateSimpleHttpUrl( $sMasterSiteUrl ); // final clean
|
139 |
if ( empty( $sMasterSiteUrl ) ) {
|
140 |
throw new \Exception( "Couldn't validate the URL.", 4 );
|
141 |
}
|
142 |
|
143 |
// Begin the handshake process.
|
144 |
+
$opts->setOpt(
|
145 |
'importexport_handshake_expires_at',
|
146 |
Services::Request()->ts() + 30
|
147 |
);
|
186 |
// Fix for the overwriting of the Master Site URL with an empty string.
|
187 |
// Only do so if we're not turning it off. i.e on or no-change
|
188 |
if ( is_null( $bEnableNetwork ) ) {
|
189 |
+
if ( $bHadMasterSiteUrl && !$opts->hasImportExportMasterImportUrl() ) {
|
190 |
$mod->setImportExportMasterImportUrl( $sOriginalMasterSiteUrl );
|
191 |
}
|
192 |
}
|
src/lib/src/Modules/Plugin/Lib/ImportExport/ImportExportController.php
CHANGED
@@ -109,19 +109,22 @@ class ImportExportController {
|
|
109 |
}
|
110 |
}
|
111 |
|
112 |
-
|
113 |
-
|
114 |
-
|
115 |
-
public function buildInsightsVars() {
|
116 |
-
/** @var \ICWP_WPSF_FeatureHandler_HackProtect $oMod */
|
117 |
-
$oMod = $this->getMod();
|
118 |
return [
|
119 |
'vars' => [
|
120 |
-
'file_upload_nonce' => $
|
121 |
-
'form_action' => $
|
|
|
|
|
|
|
|
|
|
|
|
|
122 |
],
|
123 |
'ajax' => [
|
124 |
-
'import_from_site' => $
|
125 |
],
|
126 |
'flags' => [
|
127 |
'can_importexport' => $this->getCon()->isPremiumActive(),
|
@@ -130,8 +133,8 @@ class ImportExportController {
|
|
130 |
'export_file_download' => $this->createExportFileDownloadLink()
|
131 |
],
|
132 |
'strings' => [
|
133 |
-
'tab_by_file'
|
134 |
-
'tab_by_site'
|
135 |
'title_import_file' => __( 'Import From File', 'wp-simple-firewall' ),
|
136 |
'subtitle_import_file' => __( 'Upload an exported options file you downloaded from another site', 'wp-simple-firewall' ),
|
137 |
'select_import_file' => __( 'Select file to import options from', 'wp-simple-firewall' ),
|
109 |
}
|
110 |
}
|
111 |
|
112 |
+
public function buildInsightsVars() :array {
|
113 |
+
/** @var Plugin\ModCon $mod */
|
114 |
+
$mod = $this->getMod();
|
|
|
|
|
|
|
115 |
return [
|
116 |
'vars' => [
|
117 |
+
'file_upload_nonce' => $mod->getNonceActionData( 'import_file_upload' ),
|
118 |
+
'form_action' => $mod->getUrl_AdminPage(),
|
119 |
+
'related_hrefs' => [
|
120 |
+
[
|
121 |
+
'href' => $mod->getUrl_DirectLinkToSection( 'section_importexport' ),
|
122 |
+
'title' => __( 'Import/Export Settings', 'wp-simple-firewall' ),
|
123 |
+
]
|
124 |
+
]
|
125 |
],
|
126 |
'ajax' => [
|
127 |
+
'import_from_site' => $mod->getAjaxActionData( 'import_from_site', true ),
|
128 |
],
|
129 |
'flags' => [
|
130 |
'can_importexport' => $this->getCon()->isPremiumActive(),
|
133 |
'export_file_download' => $this->createExportFileDownloadLink()
|
134 |
],
|
135 |
'strings' => [
|
136 |
+
'tab_by_file' => __( 'Import From File', 'wp-simple-firewall' ),
|
137 |
+
'tab_by_site' => __( 'Import From Another Site', 'wp-simple-firewall' ),
|
138 |
'title_import_file' => __( 'Import From File', 'wp-simple-firewall' ),
|
139 |
'subtitle_import_file' => __( 'Upload an exported options file you downloaded from another site', 'wp-simple-firewall' ),
|
140 |
'select_import_file' => __( 'Select file to import options from', 'wp-simple-firewall' ),
|
src/lib/src/Modules/Plugin/Lib/ImportExport/NotifyWhitelist.php
CHANGED
@@ -3,6 +3,7 @@
|
|
3 |
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Plugin\Lib\ImportExport;
|
4 |
|
5 |
use FernleafSystems\Wordpress\Plugin\Shield\Modules\ModConsumer;
|
|
|
6 |
use FernleafSystems\Wordpress\Services\Services;
|
7 |
|
8 |
class NotifyWhitelist {
|
@@ -10,17 +11,17 @@ class NotifyWhitelist {
|
|
10 |
use ModConsumer;
|
11 |
|
12 |
public function run() {
|
13 |
-
/** @var
|
14 |
-
$
|
15 |
$oHttpReq = Services::HttpRequest();
|
16 |
|
17 |
-
if ( $
|
18 |
|
19 |
$aQuery = [
|
20 |
'blocking' => false,
|
21 |
'body' => [ 'shield_action' => 'importexport_updatenotified' ]
|
22 |
];
|
23 |
-
foreach ( $
|
24 |
$oHttpReq->get( $sUrl, $aQuery );
|
25 |
}
|
26 |
|
3 |
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Plugin\Lib\ImportExport;
|
4 |
|
5 |
use FernleafSystems\Wordpress\Plugin\Shield\Modules\ModConsumer;
|
6 |
+
use FernleafSystems\Wordpress\Plugin\Shield\Modules\Plugin\ModCon;
|
7 |
use FernleafSystems\Wordpress\Services\Services;
|
8 |
|
9 |
class NotifyWhitelist {
|
11 |
use ModConsumer;
|
12 |
|
13 |
public function run() {
|
14 |
+
/** @var ModCon $mod */
|
15 |
+
$mod = $this->getMod();
|
16 |
$oHttpReq = Services::HttpRequest();
|
17 |
|
18 |
+
if ( $mod->hasImportExportWhitelistSites() ) {
|
19 |
|
20 |
$aQuery = [
|
21 |
'blocking' => false,
|
22 |
'body' => [ 'shield_action' => 'importexport_updatenotified' ]
|
23 |
];
|
24 |
+
foreach ( $mod->getImportExportWhitelist() as $sUrl ) {
|
25 |
$oHttpReq->get( $sUrl, $aQuery );
|
26 |
}
|
27 |
|
src/lib/src/Modules/Plugin/Lib/PluginTelemetry.php
ADDED
@@ -0,0 +1,166 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
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;
|
9 |
+
use FernleafSystems\Wordpress\Services\Services;
|
10 |
+
|
11 |
+
class PluginTelemetry {
|
12 |
+
|
13 |
+
use ModConsumer;
|
14 |
+
use OneTimeExecute;
|
15 |
+
|
16 |
+
protected function canRun() {
|
17 |
+
/** @var Plugin\Options $opts */
|
18 |
+
$opts = $this->getOptions();
|
19 |
+
return $opts->isTrackingEnabled() || !$opts->isTrackingPermissionSet();
|
20 |
+
}
|
21 |
+
|
22 |
+
protected function run() {
|
23 |
+
$con = $this->getCon();
|
24 |
+
switch ( $con->getShieldAction() ) {
|
25 |
+
case 'dump_tracking_data':
|
26 |
+
add_action( 'wp_loaded', function () {
|
27 |
+
if ( $this->getCon()->isPluginAdmin() ) {
|
28 |
+
echo sprintf( '<pre><code>%s</code></pre>',
|
29 |
+
print_r( $this->collectTrackingData(), true ) );
|
30 |
+
die();
|
31 |
+
}
|
32 |
+
} );
|
33 |
+
break;
|
34 |
+
default:
|
35 |
+
break;
|
36 |
+
}
|
37 |
+
|
38 |
+
add_action( $this->getCon()->prefix( 'daily_cron' ), [ $this, 'runDailyCron' ] );
|
39 |
+
}
|
40 |
+
|
41 |
+
public function runDailyCron() {
|
42 |
+
$this->sendTrackingData();
|
43 |
+
}
|
44 |
+
|
45 |
+
private function sendTrackingData() {
|
46 |
+
/** @var Plugin\Options $opts */
|
47 |
+
$opts = $this->getOptions();
|
48 |
+
|
49 |
+
$success = false;
|
50 |
+
|
51 |
+
$bCanSend = Services::Request()
|
52 |
+
->carbon()
|
53 |
+
->subWeek()->timestamp
|
54 |
+
> (int)$opts->getOpt( 'tracking_last_sent_at', 0 );
|
55 |
+
if ( $bCanSend && $opts->isTrackingEnabled() ) {
|
56 |
+
|
57 |
+
$data = $this->collectTrackingData();
|
58 |
+
if ( !empty( $data ) ) {
|
59 |
+
$opts->setOpt( 'tracking_last_sent_at', Services::Request()->ts() );
|
60 |
+
$success = Services::HttpRequest()->post(
|
61 |
+
$opts->getDef( 'tracking_post_url' ),
|
62 |
+
[
|
63 |
+
'timeout' => 20,
|
64 |
+
'redirection' => 5,
|
65 |
+
'httpversion' => '1.1',
|
66 |
+
'blocking' => true,
|
67 |
+
'body' => [ 'tracking_data' => $data ],
|
68 |
+
'user-agent' => 'SHIELD/'.$this->getCon()->getVersion().';'
|
69 |
+
]
|
70 |
+
);
|
71 |
+
}
|
72 |
+
}
|
73 |
+
|
74 |
+
return $success;
|
75 |
+
}
|
76 |
+
|
77 |
+
/**
|
78 |
+
* @return array[]
|
79 |
+
*/
|
80 |
+
public function collectTrackingData() :array {
|
81 |
+
$con = $this->getCon();
|
82 |
+
|
83 |
+
$data = $this->getBaseTrackingData();
|
84 |
+
foreach ( $con->modules as $mod ) {
|
85 |
+
$data[ $mod->getSlug() ] = $this->buildOptionsDataForMod( $mod );
|
86 |
+
}
|
87 |
+
|
88 |
+
if ( !empty( $data[ 'events' ] ) ) {
|
89 |
+
$data[ 'events' ][ 'stats' ] = $con->getModule_Events()
|
90 |
+
->getDbHandler_Events()
|
91 |
+
->getQuerySelector()
|
92 |
+
->sumAllEvents();
|
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'
|
103 |
+
];
|
104 |
+
foreach ( $keys as $key ) {
|
105 |
+
$data[ 'admin_access_restriction' ][ 'options' ][ $key ]
|
106 |
+
= empty( $data[ 'admin_access_restriction' ][ 'options' ][ $key ] ) ? 0 : 1;
|
107 |
+
}
|
108 |
+
}
|
109 |
+
if ( !empty( $data[ 'plugin' ] ) ) {
|
110 |
+
/** @var Plugin\ModCon $mod */
|
111 |
+
$mod = $this->getMod();
|
112 |
+
$data[ 'plugin' ][ 'options' ][ 'unique_installation_id' ] = $mod->getPluginInstallationId();
|
113 |
+
$data[ 'plugin' ][ 'options' ][ 'new_unique_installation_id' ] = $con->getSiteInstallationId();
|
114 |
+
}
|
115 |
+
|
116 |
+
return $data;
|
117 |
+
}
|
118 |
+
|
119 |
+
/**
|
120 |
+
* @param ModCon $mod
|
121 |
+
* @return array
|
122 |
+
*/
|
123 |
+
private function buildOptionsDataForMod( $mod ) :array {
|
124 |
+
$data = [];
|
125 |
+
|
126 |
+
$opts = $mod->getOptions();
|
127 |
+
$optionsData = $opts->getOptionsForTracking();
|
128 |
+
foreach ( $optionsData as $opt => $mValue ) {
|
129 |
+
unset( $optionsData[ $opt ] );
|
130 |
+
// some cleaning to ensure we don't have disallowed characters
|
131 |
+
$opt = preg_replace( '#[^_a-z]#', '', strtolower( $opt ) );
|
132 |
+
if ( $opts->getOptionType( $opt ) == 'checkbox' ) { // only want a boolean 1 or 0
|
133 |
+
$optionsData[ $opt ] = (int)( $mValue == 'Y' );
|
134 |
+
}
|
135 |
+
else {
|
136 |
+
$optionsData[ $opt ] = $mValue;
|
137 |
+
}
|
138 |
+
}
|
139 |
+
|
140 |
+
$data[ 'options' ] = $optionsData;
|
141 |
+
|
142 |
+
return $data;
|
143 |
+
}
|
144 |
+
|
145 |
+
private function getBaseTrackingData() :array {
|
146 |
+
$WP = Services::WpGeneral();
|
147 |
+
$WPP = Services::WpPlugins();
|
148 |
+
return [
|
149 |
+
'env' => [
|
150 |
+
'options' => [
|
151 |
+
'php' => Services::Data()->getPhpVersionCleaned(),
|
152 |
+
'wordpress' => $WP->getVersion(),
|
153 |
+
'slug' => $this->getCon()->getPluginSlug(),
|
154 |
+
'version' => $this->getCon()->getVersion(),
|
155 |
+
'is_wpms' => $WP->isMultisite() ? 1 : 0,
|
156 |
+
'is_cp' => $WP->isClassicPress() ? 1 : 0,
|
157 |
+
'ssl' => is_ssl() ? 1 : 0,
|
158 |
+
'locale' => get_locale(),
|
159 |
+
'plugins_total' => count( $WPP->getPlugins() ),
|
160 |
+
'plugins_active' => count( $WPP->getActivePlugins() ),
|
161 |
+
'plugins_updates' => count( $WPP->getUpdates() )
|
162 |
+
]
|
163 |
+
]
|
164 |
+
];
|
165 |
+
}
|
166 |
+
}
|
src/lib/src/Modules/Plugin/ModCon.php
ADDED
@@ -0,0 +1,549 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php declare( strict_types=1 );
|
2 |
+
|
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;
|
9 |
+
|
10 |
+
class ModCon extends BaseShield\ModCon {
|
11 |
+
|
12 |
+
/**
|
13 |
+
* @var Lib\ImportExport\ImportExportController
|
14 |
+
*/
|
15 |
+
private $importExportCon;
|
16 |
+
|
17 |
+
/**
|
18 |
+
* @var Components\PluginBadge
|
19 |
+
*/
|
20 |
+
private $pluginBadgeCon;
|
21 |
+
|
22 |
+
/**
|
23 |
+
* @var Shield\Utilities\ReCaptcha\Enqueue
|
24 |
+
*/
|
25 |
+
private $oCaptchaEnqueue;
|
26 |
+
|
27 |
+
/**
|
28 |
+
* @var Shield\ShieldNetApi\ShieldNetApiController
|
29 |
+
*/
|
30 |
+
private $shieldNetCon;
|
31 |
+
|
32 |
+
public function getImpExpController() :Lib\ImportExport\ImportExportController {
|
33 |
+
if ( !isset( $this->importExportCon ) ) {
|
34 |
+
$this->importExportCon = ( new Lib\ImportExport\ImportExportController() )
|
35 |
+
->setMod( $this );
|
36 |
+
}
|
37 |
+
return $this->importExportCon;
|
38 |
+
}
|
39 |
+
|
40 |
+
public function getPluginBadgeCon() :Components\PluginBadge {
|
41 |
+
if ( !isset( $this->pluginBadgeCon ) ) {
|
42 |
+
$this->pluginBadgeCon = ( new Components\PluginBadge() )
|
43 |
+
->setMod( $this );
|
44 |
+
}
|
45 |
+
return $this->pluginBadgeCon;
|
46 |
+
}
|
47 |
+
|
48 |
+
public function getShieldNetApiController() :Shield\ShieldNetApi\ShieldNetApiController {
|
49 |
+
if ( !isset( $this->shieldNetCon ) ) {
|
50 |
+
$this->shieldNetCon = ( new Shield\ShieldNetApi\ShieldNetApiController() )
|
51 |
+
->setMod( $this );
|
52 |
+
}
|
53 |
+
return $this->shieldNetCon;
|
54 |
+
}
|
55 |
+
|
56 |
+
protected function doPostConstruction() {
|
57 |
+
$this->setVisitorIpSource();
|
58 |
+
}
|
59 |
+
|
60 |
+
protected function preProcessOptions() {
|
61 |
+
( new Lib\Captcha\CheckCaptchaSettings() )
|
62 |
+
->setMod( $this )
|
63 |
+
->checkAll();
|
64 |
+
}
|
65 |
+
|
66 |
+
public function deleteAllPluginCrons() {
|
67 |
+
$con = $this->getCon();
|
68 |
+
$wpCrons = Services::WpCron();
|
69 |
+
|
70 |
+
foreach ( $wpCrons->getCrons() as $nKey => $aCronArgs ) {
|
71 |
+
foreach ( $aCronArgs as $sHook => $aCron ) {
|
72 |
+
if ( strpos( $sHook, $con->prefix() ) === 0
|
73 |
+
|| strpos( $sHook, $con->prefixOption() ) === 0 ) {
|
74 |
+
$wpCrons->deleteCronJob( $sHook );
|
75 |
+
}
|
76 |
+
}
|
77 |
+
}
|
78 |
+
}
|
79 |
+
|
80 |
+
public function onPluginShutdown() {
|
81 |
+
$preferred = Services::IP()->getIpDetector()->getLastSuccessfulSource();
|
82 |
+
if ( !empty( $preferred ) ) {
|
83 |
+
$this->getOptions()->setOpt( 'last_ip_detect_source', $preferred );
|
84 |
+
}
|
85 |
+
parent::onPluginShutdown();
|
86 |
+
}
|
87 |
+
|
88 |
+
public function onWpInit() {
|
89 |
+
parent::onWpInit();
|
90 |
+
$this->getImportExportSecretKey();
|
91 |
+
}
|
92 |
+
|
93 |
+
/**
|
94 |
+
* Forcefully sets preferred Visitor IP source in the Data component for use throughout the plugin
|
95 |
+
*/
|
96 |
+
private function setVisitorIpSource() {
|
97 |
+
/** @var Options $opts */
|
98 |
+
$opts = $this->getOptions();
|
99 |
+
if ( !$opts->isIpSourceAutoDetect() ) {
|
100 |
+
Services::IP()->setIpDetector(
|
101 |
+
( new VisitorIpDetection() )->setPreferredSource( $opts->getIpSource() )
|
102 |
+
);
|
103 |
+
}
|
104 |
+
}
|
105 |
+
|
106 |
+
protected function handleModAction( string $action ) {
|
107 |
+
switch ( $action ) {
|
108 |
+
|
109 |
+
case 'export_file_download':
|
110 |
+
header( 'Set-Cookie: fileDownload=true; path=/' );
|
111 |
+
( new Lib\ImportExport\Export() )
|
112 |
+
->setMod( $this )
|
113 |
+
->toFile();
|
114 |
+
break;
|
115 |
+
|
116 |
+
case 'import_file_upload':
|
117 |
+
try {
|
118 |
+
( new Lib\ImportExport\Import() )
|
119 |
+
->setMod( $this )
|
120 |
+
->fromFileUpload();
|
121 |
+
$bSuccess = true;
|
122 |
+
$sMessage = __( 'Options imported successfully', 'wp-simple-firewall' );
|
123 |
+
}
|
124 |
+
catch ( \Exception $oE ) {
|
125 |
+
$bSuccess = false;
|
126 |
+
$sMessage = $oE->getMessage();
|
127 |
+
}
|
128 |
+
$this->setFlashAdminNotice( $sMessage, !$bSuccess );
|
129 |
+
Services::Response()->redirect(
|
130 |
+
$this->getCon()->getModule_Insights()->getUrl_SubInsightsPage( 'importexport' )
|
131 |
+
);
|
132 |
+
break;
|
133 |
+
|
134 |
+
default:
|
135 |
+
break;
|
136 |
+
}
|
137 |
+
}
|
138 |
+
|
139 |
+
public function getCanSiteCallToItself() :bool {
|
140 |
+
$oHttp = Services::HttpRequest();
|
141 |
+
return $oHttp->get( Services::WpGeneral()->getHomeUrl(), [ 'timeout' => 20 ] )
|
142 |
+
&& $oHttp->lastResponse->getCode() < 400;
|
143 |
+
}
|
144 |
+
|
145 |
+
public function getActivePluginFeatures() :array {
|
146 |
+
$aActiveFeatures = $this->getDef( 'active_plugin_features' );
|
147 |
+
|
148 |
+
$aPluginFeatures = [];
|
149 |
+
if ( !empty( $aActiveFeatures ) && is_array( $aActiveFeatures ) ) {
|
150 |
+
|
151 |
+
foreach ( $aActiveFeatures as $nPosition => $aFeature ) {
|
152 |
+
if ( isset( $aFeature[ 'hidden' ] ) && $aFeature[ 'hidden' ] ) {
|
153 |
+
continue;
|
154 |
+
}
|
155 |
+
$aPluginFeatures[ $aFeature[ 'slug' ] ] = $aFeature;
|
156 |
+
}
|
157 |
+
}
|
158 |
+
return $aPluginFeatures;
|
159 |
+
}
|
160 |
+
|
161 |
+
public function getLinkToTrackingDataDump() :string {
|
162 |
+
return add_query_arg(
|
163 |
+
[ 'shield_action' => 'dump_tracking_data' ],
|
164 |
+
Services::WpGeneral()->getAdminUrl()
|
165 |
+
);
|
166 |
+
}
|
167 |
+
|
168 |
+
public function getPluginReportEmail() :string {
|
169 |
+
$e = (string)$this->getOptions()->getOpt( 'block_send_email_address' );
|
170 |
+
if ( $this->isPremium() ) {
|
171 |
+
$e = apply_filters( $this->getCon()->prefix( 'report_email' ), $e );
|
172 |
+
}
|
173 |
+
$e = trim( $e );
|
174 |
+
return Services::Data()->validEmail( $e ) ? $e : Services::WpGeneral()->getSiteAdminEmail();
|
175 |
+
}
|
176 |
+
|
177 |
+
/**
|
178 |
+
* This is the point where you would want to do any options verification
|
179 |
+
*/
|
180 |
+
protected function doPrePluginOptionsSave() {
|
181 |
+
/** @var Options $oOpts */
|
182 |
+
$oOpts = $this->getOptions();
|
183 |
+
|
184 |
+
$this->storeRealInstallDate();
|
185 |
+
|
186 |
+
if ( $oOpts->isTrackingEnabled() && !$oOpts->isTrackingPermissionSet() ) {
|
187 |
+
$oOpts->setOpt( 'tracking_permission_set_at', Services::Request()->ts() );
|
188 |
+
}
|
189 |
+
|
190 |
+
$this->cleanRecaptchaKey( 'google_recaptcha_site_key' );
|
191 |
+
$this->cleanRecaptchaKey( 'google_recaptcha_secret_key' );
|
192 |
+
|
193 |
+
$this->cleanImportExportWhitelistUrls();
|
194 |
+
$this->cleanImportExportMasterImportUrl();
|
195 |
+
|
196 |
+
$this->setPluginInstallationId();
|
197 |
+
}
|
198 |
+
|
199 |
+
public function getFirstInstallDate() :int {
|
200 |
+
return (int)Services::WpGeneral()->getOption( $this->getCon()->prefixOption( 'install_date' ) );
|
201 |
+
}
|
202 |
+
|
203 |
+
public function getInstallDate() :int {
|
204 |
+
return (int)$this->getOptions()->getOpt( 'installation_time', 0 );
|
205 |
+
}
|
206 |
+
|
207 |
+
public function isShowAdvanced() :bool {
|
208 |
+
return $this->getOptions()->isOpt( 'show_advanced', 'Y' );
|
209 |
+
}
|
210 |
+
|
211 |
+
/**
|
212 |
+
* @return string
|
213 |
+
*/
|
214 |
+
public function getOpenSslPrivateKey() {
|
215 |
+
$opts = $this->getOptions();
|
216 |
+
$key = null;
|
217 |
+
$oEnc = Services::Encrypt();
|
218 |
+
if ( $oEnc->isSupportedOpenSslDataEncryption() ) {
|
219 |
+
$key = $opts->getOpt( 'openssl_private_key' );
|
220 |
+
if ( empty( $key ) ) {
|
221 |
+
try {
|
222 |
+
$aKeys = $oEnc->createNewPrivatePublicKeyPair();
|
223 |
+
if ( !empty( $aKeys[ 'private' ] ) ) {
|
224 |
+
$key = $aKeys[ 'private' ];
|
225 |
+
$opts->setOpt( 'openssl_private_key', base64_encode( $key ) );
|
226 |
+
$this->saveModOptions();
|
227 |
+
}
|
228 |
+
}
|
229 |
+
catch ( \Exception $e ) {
|
230 |
+
}
|
231 |
+
}
|
232 |
+
else {
|
233 |
+
$key = base64_decode( $key );
|
234 |
+
}
|
235 |
+
}
|
236 |
+
return $key;
|
237 |
+
}
|
238 |
+
|
239 |
+
/**
|
240 |
+
* @return string|null
|
241 |
+
*/
|
242 |
+
public function getOpenSslPublicKey() {
|
243 |
+
$key = null;
|
244 |
+
if ( $this->hasOpenSslPrivateKey() ) {
|
245 |
+
try {
|
246 |
+
$key = Services::Encrypt()->getPublicKeyFromPrivateKey( $this->getOpenSslPrivateKey() );
|
247 |
+
}
|
248 |
+
catch ( \Exception $e ) {
|
249 |
+
}
|
250 |
+
}
|
251 |
+
return $key;
|
252 |
+
}
|
253 |
+
|
254 |
+
public function hasOpenSslPrivateKey() :bool {
|
255 |
+
return !empty( $this->getOpenSslPrivateKey() );
|
256 |
+
}
|
257 |
+
|
258 |
+
/**
|
259 |
+
* @return int - the real install timestamp
|
260 |
+
*/
|
261 |
+
public function storeRealInstallDate() {
|
262 |
+
$WP = Services::WpGeneral();
|
263 |
+
$ts = Services::Request()->ts();
|
264 |
+
|
265 |
+
$sOptKey = $this->getCon()->prefixOption( 'install_date' );
|
266 |
+
|
267 |
+
$nWpDate = $WP->getOption( $sOptKey );
|
268 |
+
if ( empty( $nWpDate ) ) {
|
269 |
+
$nWpDate = $ts;
|
270 |
+
}
|
271 |
+
|
272 |
+
$nPluginDate = $this->getInstallDate();
|
273 |
+
if ( $nPluginDate == 0 ) {
|
274 |
+
$nPluginDate = $ts;
|
275 |
+
}
|
276 |
+
|
277 |
+
$nFinal = min( $nPluginDate, $nWpDate );
|
278 |
+
$WP->updateOption( $sOptKey, $nFinal );
|
279 |
+
$this->getOptions()->setOpt( 'installation_time', $nPluginDate );
|
280 |
+
|
281 |
+
return $nFinal;
|
282 |
+
}
|
283 |
+
|
284 |
+
/**
|
285 |
+
* @param string $optionKey
|
286 |
+
*/
|
287 |
+
protected function cleanRecaptchaKey( $optionKey ) {
|
288 |
+
$opts = $this->getOptions();
|
289 |
+
$sCaptchaKey = trim( (string)$opts->getOpt( $optionKey, '' ) );
|
290 |
+
$nSpacePos = strpos( $sCaptchaKey, ' ' );
|
291 |
+
if ( $nSpacePos !== false ) {
|
292 |
+
$sCaptchaKey = substr( $sCaptchaKey, 0, $nSpacePos + 1 ); // cut off the string if there's spaces
|
293 |
+
}
|
294 |
+
$sCaptchaKey = preg_replace( '#[^0-9a-zA-Z_-]#', '', $sCaptchaKey ); // restrict character set
|
295 |
+
// if ( strlen( $sCaptchaKey ) != 40 ) {
|
296 |
+
// $sCaptchaKey = ''; // need to verify length is 40.
|
297 |
+
// }
|
298 |
+
$opts->setOpt( $optionKey, $sCaptchaKey );
|
299 |
+
}
|
300 |
+
|
301 |
+
/**
|
302 |
+
* Ensure we always a valid installation ID.
|
303 |
+
*
|
304 |
+
* @return string
|
305 |
+
* @deprecated but still used because it aligns with stats collection
|
306 |
+
*/
|
307 |
+
public function getPluginInstallationId() {
|
308 |
+
$ID = $this->getOptions()->getOpt( 'unique_installation_id', '' );
|
309 |
+
|
310 |
+
if ( !$this->isValidInstallId( $ID ) ) {
|
311 |
+
$ID = $this->setPluginInstallationId();
|
312 |
+
}
|
313 |
+
return $ID;
|
314 |
+
}
|
315 |
+
|
316 |
+
public function getActivateLength() :int {
|
317 |
+
return Services::Request()->ts() - (int)$this->getOptions()->getOpt( 'activated_at', 0 );
|
318 |
+
}
|
319 |
+
|
320 |
+
/**
|
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 |
+
}
|
328 |
+
|
329 |
+
public function getTourManager() :Lib\TourManager {
|
330 |
+
return ( new Lib\TourManager() )->setMod( $this );
|
331 |
+
}
|
332 |
+
|
333 |
+
public function setActivatedAt() {
|
334 |
+
$this->getOptions()->setOpt( 'activated_at', Services::Request()->ts() );
|
335 |
+
}
|
336 |
+
|
337 |
+
/**
|
338 |
+
* @param string $newID - leave empty to reset if the current isn't valid
|
339 |
+
* @return string
|
340 |
+
*/
|
341 |
+
protected function setPluginInstallationId( $newID = null ) {
|
342 |
+
// only reset if it's not of the correct type
|
343 |
+
if ( !$this->isValidInstallId( $newID ) ) {
|
344 |
+
$newID = $this->genInstallId();
|
345 |
+
}
|
346 |
+
$this->getOptions()->setOpt( 'unique_installation_id', $newID );
|
347 |
+
return $newID;
|
348 |
+
}
|
349 |
+
|
350 |
+
protected function genInstallId() :string {
|
351 |
+
return sha1(
|
352 |
+
$this->getInstallDate()
|
353 |
+
.Services::WpGeneral()->getWpUrl()
|
354 |
+
.Services::WpDb()->getPrefix()
|
355 |
+
);
|
356 |
+
}
|
357 |
+
|
358 |
+
public function hasImportExportWhitelistSites() :bool {
|
359 |
+
return count( $this->getImportExportWhitelist() ) > 0;
|
360 |
+
}
|
361 |
+
|
362 |
+
/**
|
363 |
+
* @return string[]
|
364 |
+
*/
|
365 |
+
public function getImportExportWhitelist() :array {
|
366 |
+
$list = $this->getOptions()->getOpt( 'importexport_whitelist', [] );
|
367 |
+
return is_array( $list ) ? $list : [];
|
368 |
+
}
|
369 |
+
|
370 |
+
/**
|
371 |
+
* @return string
|
372 |
+
*/
|
373 |
+
protected function getImportExportSecretKey() {
|
374 |
+
$opts = $this->getOptions();
|
375 |
+
$ID = $opts->getOpt( 'importexport_secretkey', '' );
|
376 |
+
if ( empty( $ID ) || $this->isImportExportSecretKeyExpired() ) {
|
377 |
+
$ID = sha1( $this->getCon()->getSiteInstallationId().wp_rand( 0, PHP_INT_MAX ) );
|
378 |
+
$opts->setOpt( 'importexport_secretkey', $ID )
|
379 |
+
->setOpt( 'importexport_secretkey_expires_at', Services::Request()->ts() + HOUR_IN_SECONDS );
|
380 |
+
}
|
381 |
+
return $ID;
|
382 |
+
}
|
383 |
+
|
384 |
+
protected function isImportExportSecretKeyExpired() :bool {
|
385 |
+
return Services::Request()->ts() >
|
386 |
+
$this->getOptions()->getOpt( 'importexport_secretkey_expires_at' );
|
387 |
+
}
|
388 |
+
|
389 |
+
public function isImportExportWhitelistNotify() :bool {
|
390 |
+
return $this->getOptions()->isOpt( 'importexport_whitelist_notify', 'Y' );
|
391 |
+
}
|
392 |
+
|
393 |
+
/**
|
394 |
+
* @param string $sUrl
|
395 |
+
* @return $this
|
396 |
+
*/
|
397 |
+
public function addUrlToImportExportWhitelistUrls( $sUrl ) {
|
398 |
+
$sUrl = Services::Data()->validateSimpleHttpUrl( $sUrl );
|
399 |
+
if ( $sUrl !== false ) {
|
400 |
+
$aWhitelistUrls = $this->getImportExportWhitelist();
|
401 |
+
$aWhitelistUrls[] = $sUrl;
|
402 |
+
$this->getOptions()->setOpt( 'importexport_whitelist', $aWhitelistUrls );
|
403 |
+
$this->saveModOptions();
|
404 |
+
}
|
405 |
+
return $this;
|
406 |
+
}
|
407 |
+
|
408 |
+
/**
|
409 |
+
* @param string $url
|
410 |
+
* @return $this
|
411 |
+
*/
|
412 |
+
public function removeUrlFromImportExportWhitelistUrls( $url ) {
|
413 |
+
$url = Services::Data()->validateSimpleHttpUrl( $url );
|
414 |
+
if ( $url !== false ) {
|
415 |
+
$aWhitelistUrls = $this->getImportExportWhitelist();
|
416 |
+
$sKey = array_search( $url, $aWhitelistUrls );
|
417 |
+
if ( $sKey !== false ) {
|
418 |
+
unset( $aWhitelistUrls[ $sKey ] );
|
419 |
+
}
|
420 |
+
$this->getOptions()->setOpt( 'importexport_whitelist', $aWhitelistUrls );
|
421 |
+
$this->saveModOptions();
|
422 |
+
}
|
423 |
+
return $this;
|
424 |
+
}
|
425 |
+
|
426 |
+
/**
|
427 |
+
* @param string $sKey
|
428 |
+
* @return bool
|
429 |
+
*/
|
430 |
+
public function isImportExportSecretKey( $sKey ) :bool {
|
431 |
+
return !empty( $sKey ) && $this->getImportExportSecretKey() == $sKey;
|
432 |
+
}
|
433 |
+
|
434 |
+
protected function cleanImportExportWhitelistUrls() {
|
435 |
+
$oDP = Services::Data();
|
436 |
+
|
437 |
+
$aCleaned = [];
|
438 |
+
$aWhitelistUrls = $this->getImportExportWhitelist();
|
439 |
+
foreach ( $aWhitelistUrls as $nKey => $sUrl ) {
|
440 |
+
|
441 |
+
$sUrl = $oDP->validateSimpleHttpUrl( $sUrl );
|
442 |
+
if ( $sUrl !== false ) {
|
443 |
+
$aCleaned[] = $sUrl;
|
444 |
+
}
|
445 |
+
}
|
446 |
+
$this->getOptions()->setOpt( 'importexport_whitelist', array_unique( $aCleaned ) );
|
447 |
+
}
|
448 |
+
|
449 |
+
protected function cleanImportExportMasterImportUrl() {
|
450 |
+
/** @var Options $oOpts */
|
451 |
+
$oOpts = $this->getOptions();
|
452 |
+
$url = Services::Data()->validateSimpleHttpUrl( $oOpts->getImportExportMasterImportUrl() );
|
453 |
+
if ( $url === false ) {
|
454 |
+
$url = '';
|
455 |
+
}
|
456 |
+
$this->getOptions()->setOpt( 'importexport_masterurl', $url );
|
457 |
+
}
|
458 |
+
|
459 |
+
/**
|
460 |
+
* @param string $url
|
461 |
+
* @return $this
|
462 |
+
*/
|
463 |
+
public function setImportExportMasterImportUrl( $url ) {
|
464 |
+
$this->getOptions()->setOpt( 'importexport_masterurl', $url ); //saving will clean the URL
|
465 |
+
return $this->saveModOptions();
|
466 |
+
}
|
467 |
+
|
468 |
+
/**
|
469 |
+
* @param string $sId
|
470 |
+
* @return bool
|
471 |
+
*/
|
472 |
+
protected function isValidInstallId( $sId ) {
|
473 |
+
return !empty( $sId ) && is_string( $sId ) && strlen( $sId ) == 40;
|
474 |
+
}
|
475 |
+
|
476 |
+
public function isXmlrpcBypass() :bool {
|
477 |
+
return $this->getOptions()->isOpt( 'enable_xmlrpc_compatibility', 'Y' );
|
478 |
+
}
|
479 |
+
|
480 |
+
public function getCanAdminNotes() :bool {
|
481 |
+
return Services::WpUsers()->isUserAdmin();
|
482 |
+
}
|
483 |
+
|
484 |
+
public function insertCustomJsVars_Admin() {
|
485 |
+
parent::insertCustomJsVars_Admin();
|
486 |
+
|
487 |
+
$con = $this->getCon();
|
488 |
+
if ( Services::WpPost()->isCurrentPage( 'plugins.php' ) ) {
|
489 |
+
$sFile = $con->getPluginBaseFile();
|
490 |
+
wp_localize_script(
|
491 |
+
$con->prefix( 'global-plugin' ),
|
492 |
+
'icwp_wpsf_vars_plugin',
|
493 |
+
[
|
494 |
+
'file' => $sFile,
|
495 |
+
'ajax' => [
|
496 |
+
'send_deactivate_survey' => $this->getAjaxActionData( 'send_deactivate_survey' ),
|
497 |
+
],
|
498 |
+
'hrefs' => [
|
499 |
+
'deactivate' => Services::WpPlugins()->getUrl_Deactivate( $sFile ),
|
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 |
+
wp_localize_script(
|
508 |
+
$con->prefix( 'plugin' ),
|
509 |
+
'icwp_wpsf_vars_tourmanager',
|
510 |
+
[ 'ajax' => $this->getAjaxActionData( 'mark_tour_finished' ) ]
|
511 |
+
);
|
512 |
+
wp_localize_script(
|
513 |
+
$con->prefix( 'plugin' ),
|
514 |
+
'icwp_wpsf_vars_plugin',
|
515 |
+
[
|
516 |
+
'strings' => [
|
517 |
+
'downloading_file' => __( 'Downloading file, please wait...', 'wp-simple-firewall' ),
|
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 {
|
525 |
+
return $this->getDbH( 'geoip' );
|
526 |
+
}
|
527 |
+
|
528 |
+
public function getDbHandler_Notes() :Shield\Databases\AdminNotes\Handler {
|
529 |
+
return $this->getDbH( 'notes' );
|
530 |
+
}
|
531 |
+
|
532 |
+
public function getCaptchaEnqueue() :Shield\Utilities\ReCaptcha\Enqueue {
|
533 |
+
if ( !isset( $this->oCaptchaEnqueue ) ) {
|
534 |
+
$this->oCaptchaEnqueue = ( new Shield\Utilities\ReCaptcha\Enqueue() )->setMod( $this );
|
535 |
+
}
|
536 |
+
return $this->oCaptchaEnqueue;
|
537 |
+
}
|
538 |
+
|
539 |
+
protected function getNamespaceBase() :string {
|
540 |
+
return 'Plugin';
|
541 |
+
}
|
542 |
+
|
543 |
+
/**
|
544 |
+
* @return string
|
545 |
+
*/
|
546 |
+
public function getSurveyEmail() {
|
547 |
+
return base64_decode( $this->getDef( 'survey_email' ) );
|
548 |
+
}
|
549 |
+
}
|
src/lib/src/Modules/Plugin/Options.php
CHANGED
@@ -2,10 +2,10 @@
|
|
2 |
|
3 |
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Plugin;
|
4 |
|
5 |
-
use FernleafSystems\Wordpress\Plugin\Shield\Modules\
|
6 |
use FernleafSystems\Wordpress\Services\Services;
|
7 |
|
8 |
-
class Options extends
|
9 |
|
10 |
public function getCaptchaConfig() :array {
|
11 |
return [
|
@@ -47,17 +47,11 @@ class Options extends Base\ShieldOptions {
|
|
47 |
return $this->getIpSource() == 'AUTO_DETECT_IP';
|
48 |
}
|
49 |
|
50 |
-
|
51 |
-
* @return bool
|
52 |
-
*/
|
53 |
-
public function isPluginGloballyDisabled() {
|
54 |
return !$this->isOpt( 'global_enable_plugin_features', 'Y' );
|
55 |
}
|
56 |
|
57 |
-
|
58 |
-
* @return bool
|
59 |
-
*/
|
60 |
-
public function isTrackingEnabled() {
|
61 |
return $this->isOpt( 'enable_tracking', 'Y' );
|
62 |
}
|
63 |
|
@@ -97,28 +91,4 @@ class Options extends Base\ShieldOptions {
|
|
97 |
public function setVisitorAddressSource( $sSource ) {
|
98 |
return $this->setOpt( 'visitor_address_source', $sSource );
|
99 |
}
|
100 |
-
|
101 |
-
/**
|
102 |
-
* @return bool
|
103 |
-
* @deprecated 10.0
|
104 |
-
*/
|
105 |
-
public function isOnFloatingPluginBadge() {
|
106 |
-
return $this->isOpt( 'display_plugin_badge', 'Y' );
|
107 |
-
}
|
108 |
-
|
109 |
-
/**
|
110 |
-
* @return string
|
111 |
-
* @deprecated 10.0
|
112 |
-
*/
|
113 |
-
public function getDbTable_GeoIp() :string {
|
114 |
-
return $this->getCon()->prefixOption( $this->getDef( 'geoip_table_name' ) );
|
115 |
-
}
|
116 |
-
|
117 |
-
/**
|
118 |
-
* @return string
|
119 |
-
* @deprecated 10.0
|
120 |
-
*/
|
121 |
-
public function getDbTable_Notes() :string {
|
122 |
-
return $this->getCon()->prefixOption( $this->getDef( 'db_notes_name' ) );
|
123 |
-
}
|
124 |
}
|
2 |
|
3 |
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Plugin;
|
4 |
|
5 |
+
use FernleafSystems\Wordpress\Plugin\Shield\Modules\BaseShield;
|
6 |
use FernleafSystems\Wordpress\Services\Services;
|
7 |
|
8 |
+
class Options extends BaseShield\Options {
|
9 |
|
10 |
public function getCaptchaConfig() :array {
|
11 |
return [
|
47 |
return $this->getIpSource() == 'AUTO_DETECT_IP';
|
48 |
}
|
49 |
|
50 |
+
public function isPluginGloballyDisabled() :bool {
|
|
|
|
|
|
|
51 |
return !$this->isOpt( 'global_enable_plugin_features', 'Y' );
|
52 |
}
|
53 |
|
54 |
+
public function isTrackingEnabled() :bool {
|
|
|
|
|
|
|
55 |
return $this->isOpt( 'enable_tracking', 'Y' );
|
56 |
}
|
57 |
|
91 |
public function setVisitorAddressSource( $sSource ) {
|
92 |
return $this->setOpt( 'visitor_address_source', $sSource );
|
93 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
94 |
}
|
src/lib/src/Modules/Plugin/Processor.php
ADDED
@@ -0,0 +1,90 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php declare( strict_types=1 );
|
2 |
+
|
3 |
+
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Plugin;
|
4 |
+
|
5 |
+
use FernleafSystems\Wordpress\Plugin\Shield\Modules\BaseShield;
|
6 |
+
use FernleafSystems\Wordpress\Plugin\Shield\Modules\Plugin\Lib\PluginTelemetry;
|
7 |
+
use FernleafSystems\Wordpress\Plugin\Shield\Utilities\Options\CleanStorage;
|
8 |
+
use FernleafSystems\Wordpress\Services\Services;
|
9 |
+
|
10 |
+
class Processor extends BaseShield\Processor {
|
11 |
+
|
12 |
+
protected function run() {
|
13 |
+
$con = $this->getCon();
|
14 |
+
/** @var ModCon $mod */
|
15 |
+
$mod = $this->getMod();
|
16 |
+
/** @var Options $opts */
|
17 |
+
$opts = $this->getOptions();
|
18 |
+
|
19 |
+
$this->removePluginConflicts();
|
20 |
+
( new Lib\OverrideLocale() )
|
21 |
+
->setMod( $this->getMod() )
|
22 |
+
->run();
|
23 |
+
|
24 |
+
$mod->getPluginBadgeCon()->run();
|
25 |
+
|
26 |
+
( new PluginTelemetry() )
|
27 |
+
->setMod( $mod )
|
28 |
+
->execute();
|
29 |
+
|
30 |
+
if ( $opts->isImportExportPermitted() ) {
|
31 |
+
$mod->getImpExpController()->run();
|
32 |
+
}
|
33 |
+
|
34 |
+
add_filter( $con->prefix( 'delete_on_deactivate' ), function ( $isDelete ) use ( $opts ) {
|
35 |
+
return $isDelete || $opts->isOpt( 'delete_on_deactivate', 'Y' );
|
36 |
+
} );
|
37 |
+
|
38 |
+
add_action( $con->prefix( 'dashboard_widget_content' ), function () {
|
39 |
+
$this->printDashboardWidget();
|
40 |
+
}, 11 );
|
41 |
+
}
|
42 |
+
|
43 |
+
private function printDashboardWidget() {
|
44 |
+
$con = $this->getCon();
|
45 |
+
/** @var Options $opts */
|
46 |
+
$opts = $this->getOptions();
|
47 |
+
$labels = $con->getLabels();
|
48 |
+
|
49 |
+
echo $this->getMod()->renderTemplate(
|
50 |
+
'snippets/widget_dashboard_plugin.php',
|
51 |
+
[
|
52 |
+
'install_days' => sprintf( __( 'Days Installed: %s', 'wp-simple-firewall' ), $opts->getInstallationDays() ),
|
53 |
+
'footer' => sprintf( __( '%s is provided by %s', 'wp-simple-firewall' ), $con->getHumanName(),
|
54 |
+
sprintf( '<a href="%s" target="_blank">%s</a>', $labels[ 'AuthorURI' ], $labels[ 'Author' ] )
|
55 |
+
),
|
56 |
+
'ip_address' => sprintf( __( 'Your IP address is: %s', 'wp-simple-firewall' ),
|
57 |
+
Services::IP()->getRequestIp() )
|
58 |
+
]
|
59 |
+
);
|
60 |
+
}
|
61 |
+
|
62 |
+
public function runDailyCron() {
|
63 |
+
$this->getCon()->fireEvent( 'test_cron_run' );
|
64 |
+
|
65 |
+
/** @var Options $oOpts */
|
66 |
+
$oOpts = $this->getOptions();
|
67 |
+
if ( $oOpts->isImportExportPermitted() ) {
|
68 |
+
try {
|
69 |
+
( new Lib\ImportExport\Import() )
|
70 |
+
->setMod( $this->getMod() )
|
71 |
+
->fromSite( $oOpts->getImportExportMasterImportUrl() );
|
72 |
+
}
|
73 |
+
catch ( \Exception $e ) {
|
74 |
+
}
|
75 |
+
}
|
76 |
+
|
77 |
+
( new CleanStorage() )
|
78 |
+
->setCon( $this->getCon() )
|
79 |
+
->run();
|
80 |
+
}
|
81 |
+
|
82 |
+
/**
|
83 |
+
* Lets you remove certain plugin conflicts that might interfere with this plugin
|
84 |
+
*/
|
85 |
+
protected function removePluginConflicts() {
|
86 |
+
if ( class_exists( 'AIO_WP_Security' ) && isset( $GLOBALS[ 'aio_wp_security' ] ) ) {
|
87 |
+
remove_action( 'init', [ $GLOBALS[ 'aio_wp_security' ], 'wp_security_plugin_init' ], 0 );
|
88 |
+
}
|
89 |
+
}
|
90 |
+
}
|
src/lib/src/Modules/Plugin/Strings.php
CHANGED
@@ -104,6 +104,11 @@ class Strings extends Base\Strings {
|
|
104 |
$titleShort = __( 'General Options', 'wp-simple-firewall' );
|
105 |
break;
|
106 |
|
|
|
|
|
|
|
|
|
|
|
107 |
case 'section_third_party_captcha' :
|
108 |
$title = __( 'CAPTCHA', 'wp-simple-firewall' );
|
109 |
$titleShort = __( 'CAPTCHA', 'wp-simple-firewall' );
|
@@ -143,19 +148,19 @@ class Strings extends Base\Strings {
|
|
143 |
* @throws \Exception
|
144 |
*/
|
145 |
public function getOptionStrings( string $key ) :array {
|
146 |
-
/** @var
|
147 |
-
$
|
148 |
-
/** @var Options $
|
149 |
-
$
|
150 |
-
$
|
151 |
|
152 |
switch ( $key ) {
|
153 |
|
154 |
case 'global_enable_plugin_features' :
|
155 |
-
$name = sprintf( __( 'Enable %s Protection', 'wp-simple-firewall' ), $
|
156 |
$summary = __( 'Switch Off To Disable All Security Protection', 'wp-simple-firewall' );
|
157 |
$desc = [
|
158 |
-
sprintf( __( "You can keep the security plugin activated, but temporarily disable all protection it provides.", 'wp-simple-firewall' ), $
|
159 |
sprintf( '<a href="%s">%s</a>',
|
160 |
$this->getCon()->getModule_Insights()->getUrl_SubInsightsPage( 'debug' ),
|
161 |
'Launch Debug Info Page'
|
@@ -163,13 +168,22 @@ class Strings extends Base\Strings {
|
|
163 |
];
|
164 |
break;
|
165 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
166 |
case 'enable_tracking' :
|
167 |
$name = __( 'Anonymous Usage Statistics', 'wp-simple-firewall' );
|
168 |
$summary = __( 'Permit Anonymous Usage Information Gathering', 'wp-simple-firewall' );
|
169 |
$desc = [
|
170 |
__( 'Allows us to gather information on statistics and features in-use across our client installations.', 'wp-simple-firewall' )
|
171 |
.' '.__( 'This information is strictly anonymous and contains no personally, or otherwise, identifiable data.', 'wp-simple-firewall' ),
|
172 |
-
sprintf( '<a href="%s" target="_blank">%s</a>', $
|
173 |
];
|
174 |
break;
|
175 |
|
@@ -180,7 +194,7 @@ class Strings extends Base\Strings {
|
|
180 |
.'<br />'.__( 'If the option you select becomes unavailable, we will revert to auto detection.', 'wp-simple-firewall' )
|
181 |
.'<br />'.sprintf(
|
182 |
__( 'Current source is: %s (%s)', 'wp-simple-firewall' ),
|
183 |
-
'<strong>'.$
|
184 |
Services::IP()->getRequestIp()
|
185 |
)
|
186 |
.sprintf(
|
104 |
$titleShort = __( 'General Options', 'wp-simple-firewall' );
|
105 |
break;
|
106 |
|
107 |
+
case 'section_integrations' :
|
108 |
+
$title = __( '3rd Party Integrations', 'wp-simple-firewall' );
|
109 |
+
$titleShort = __( 'Integrations', 'wp-simple-firewall' );
|
110 |
+
break;
|
111 |
+
|
112 |
case 'section_third_party_captcha' :
|
113 |
$title = __( 'CAPTCHA', 'wp-simple-firewall' );
|
114 |
$titleShort = __( 'CAPTCHA', 'wp-simple-firewall' );
|
148 |
* @throws \Exception
|
149 |
*/
|
150 |
public function getOptionStrings( string $key ) :array {
|
151 |
+
/** @var ModCon $mod */
|
152 |
+
$mod = $this->getMod();
|
153 |
+
/** @var Options $opts */
|
154 |
+
$opts = $this->getOptions();
|
155 |
+
$plugName = $this->getCon()->getHumanName();
|
156 |
|
157 |
switch ( $key ) {
|
158 |
|
159 |
case 'global_enable_plugin_features' :
|
160 |
+
$name = sprintf( __( 'Enable %s Protection', 'wp-simple-firewall' ), $plugName );
|
161 |
$summary = __( 'Switch Off To Disable All Security Protection', 'wp-simple-firewall' );
|
162 |
$desc = [
|
163 |
+
sprintf( __( "You can keep the security plugin activated, but temporarily disable all protection it provides.", 'wp-simple-firewall' ), $plugName ),
|
164 |
sprintf( '<a href="%s">%s</a>',
|
165 |
$this->getCon()->getModule_Insights()->getUrl_SubInsightsPage( 'debug' ),
|
166 |
'Launch Debug Info Page'
|
168 |
];
|
169 |
break;
|
170 |
|
171 |
+
case 'show_advanced' :
|
172 |
+
$name = __( 'Show All Options', 'wp-simple-firewall' );
|
173 |
+
$summary = __( 'Show All Options Including Those Marked As Advanced', 'wp-simple-firewall' );
|
174 |
+
$desc = [
|
175 |
+
__( 'Shield hides advanced options from view to simplify display.', 'wp-simple-firewall' ),
|
176 |
+
__( 'Turn this option on to display advanced options at all times.', 'wp-simple-firewall' )
|
177 |
+
];
|
178 |
+
break;
|
179 |
+
|
180 |
case 'enable_tracking' :
|
181 |
$name = __( 'Anonymous Usage Statistics', 'wp-simple-firewall' );
|
182 |
$summary = __( 'Permit Anonymous Usage Information Gathering', 'wp-simple-firewall' );
|
183 |
$desc = [
|
184 |
__( 'Allows us to gather information on statistics and features in-use across our client installations.', 'wp-simple-firewall' )
|
185 |
.' '.__( 'This information is strictly anonymous and contains no personally, or otherwise, identifiable data.', 'wp-simple-firewall' ),
|
186 |
+
sprintf( '<a href="%s" target="_blank">%s</a>', $mod->getLinkToTrackingDataDump(), __( 'Click to see the exact data that would be sent.', 'wp-simple-firewall' ) )
|
187 |
];
|
188 |
break;
|
189 |
|
194 |
.'<br />'.__( 'If the option you select becomes unavailable, we will revert to auto detection.', 'wp-simple-firewall' )
|
195 |
.'<br />'.sprintf(
|
196 |
__( 'Current source is: %s (%s)', 'wp-simple-firewall' ),
|
197 |
+
'<strong>'.$opts->getIpSource().'</strong>',
|
198 |
Services::IP()->getRequestIp()
|
199 |
)
|
200 |
.sprintf(
|
src/lib/src/Modules/Plugin/UI.php
CHANGED
@@ -2,14 +2,23 @@
|
|
2 |
|
3 |
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Plugin;
|
4 |
|
5 |
-
use FernleafSystems\Wordpress\Plugin\Shield\Modules\
|
6 |
-
use FernleafSystems\Wordpress\Plugin\Shield\Modules\Plugin\Insights\AdminNotes;
|
7 |
use FernleafSystems\Wordpress\Plugin\Shield\Modules\Plugin\Lib\Captcha\CheckCaptchaSettings;
|
8 |
use FernleafSystems\Wordpress\Plugin\Shield\Modules\Plugin\Lib\Debug\Collate;
|
9 |
use FernleafSystems\Wordpress\Plugin\Shield\Modules\Plugin\Lib\Debug\RecentEvents;
|
10 |
use FernleafSystems\Wordpress\Services\Services;
|
11 |
|
12 |
-
class UI extends
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
13 |
|
14 |
public function buildInsightsVars_Debug() :array {
|
15 |
return [
|
@@ -25,9 +34,6 @@ class UI extends Base\ShieldUI {
|
|
25 |
'recent_events' => ( new RecentEvents() )
|
26 |
->setMod( $this->getMod() )
|
27 |
->build(),
|
28 |
-
'admin_notes' => ( new AdminNotes() )
|
29 |
-
->setMod( $this->getMod() )
|
30 |
-
->build()
|
31 |
]
|
32 |
];
|
33 |
}
|
@@ -57,10 +63,10 @@ class UI extends Base\ShieldUI {
|
|
57 |
}
|
58 |
|
59 |
protected function getSectionWarnings( string $section ) :array {
|
60 |
-
/** @var
|
61 |
$mod = $this->getMod();
|
62 |
$opts = $this->getOptions();
|
63 |
-
$
|
64 |
|
65 |
switch ( $section ) {
|
66 |
case 'section_third_party_captcha':
|
@@ -71,7 +77,7 @@ class UI extends Base\ShieldUI {
|
|
71 |
->checkAll();
|
72 |
}
|
73 |
if ( $opts->getOpt( 'captcha_checked_at' ) == 0 ) {
|
74 |
-
$
|
75 |
__( "Your captcha key and secret haven't been verified.", 'wp-simple-firewall' ).' '
|
76 |
.__( "Please double-check and make sure you haven't mixed them about, and then re-save.", 'wp-simple-firewall' )
|
77 |
);
|
@@ -80,6 +86,16 @@ class UI extends Base\ShieldUI {
|
|
80 |
break;
|
81 |
}
|
82 |
|
83 |
-
return $
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
84 |
}
|
85 |
}
|
2 |
|
3 |
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Plugin;
|
4 |
|
5 |
+
use FernleafSystems\Wordpress\Plugin\Shield\Modules\BaseShield;
|
|
|
6 |
use FernleafSystems\Wordpress\Plugin\Shield\Modules\Plugin\Lib\Captcha\CheckCaptchaSettings;
|
7 |
use FernleafSystems\Wordpress\Plugin\Shield\Modules\Plugin\Lib\Debug\Collate;
|
8 |
use FernleafSystems\Wordpress\Plugin\Shield\Modules\Plugin\Lib\Debug\RecentEvents;
|
9 |
use FernleafSystems\Wordpress\Services\Services;
|
10 |
|
11 |
+
class UI extends BaseShield\UI {
|
12 |
+
|
13 |
+
public function buildInsightsVars_Dashboard() :array {
|
14 |
+
return [
|
15 |
+
'content' => [
|
16 |
+
'dashboard_cards' => ( new Insights\DashboardCards() )
|
17 |
+
->setMod( $this->getMod() )
|
18 |
+
->renderAll(),
|
19 |
+
],
|
20 |
+
];
|
21 |
+
}
|
22 |
|
23 |
public function buildInsightsVars_Debug() :array {
|
24 |
return [
|
34 |
'recent_events' => ( new RecentEvents() )
|
35 |
->setMod( $this->getMod() )
|
36 |
->build(),
|
|
|
|
|
|
|
37 |
]
|
38 |
];
|
39 |
}
|
63 |
}
|
64 |
|
65 |
protected function getSectionWarnings( string $section ) :array {
|
66 |
+
/** @var ModCon $mod */
|
67 |
$mod = $this->getMod();
|
68 |
$opts = $this->getOptions();
|
69 |
+
$warnings = [];
|
70 |
|
71 |
switch ( $section ) {
|
72 |
case 'section_third_party_captcha':
|
77 |
->checkAll();
|
78 |
}
|
79 |
if ( $opts->getOpt( 'captcha_checked_at' ) == 0 ) {
|
80 |
+
$warnings[] = sprintf(
|
81 |
__( "Your captcha key and secret haven't been verified.", 'wp-simple-firewall' ).' '
|
82 |
.__( "Please double-check and make sure you haven't mixed them about, and then re-save.", 'wp-simple-firewall' )
|
83 |
);
|
86 |
break;
|
87 |
}
|
88 |
|
89 |
+
return $warnings;
|
90 |
+
}
|
91 |
+
|
92 |
+
protected function getSettingsRelatedLinks() :array {
|
93 |
+
$modInsights = $this->getCon()->getModule_Insights();
|
94 |
+
return [
|
95 |
+
[
|
96 |
+
'href' => $modInsights->getUrl_SubInsightsPage( 'importexport' ),
|
97 |
+
'title' => __( 'Run Import/Export', 'wp-simple-firewall' ),
|
98 |
+
]
|
99 |
+
];
|
100 |
}
|
101 |
}
|
src/lib/src/Modules/Plugin/Upgrade.php
CHANGED
@@ -1,4 +1,4 @@
|
|
1 |
-
<?php
|
2 |
|
3 |
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Plugin;
|
4 |
|
@@ -7,8 +7,8 @@ use FernleafSystems\Wordpress\Plugin\Shield\Modules\Base;
|
|
7 |
class Upgrade extends Base\Upgrade {
|
8 |
|
9 |
protected function runEveryUpgrade() {
|
10 |
-
/** @var
|
11 |
-
$
|
12 |
-
$
|
13 |
}
|
14 |
}
|
1 |
+
<?php declare( strict_types=1 );
|
2 |
|
3 |
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Plugin;
|
4 |
|
7 |
class Upgrade extends Base\Upgrade {
|
8 |
|
9 |
protected function runEveryUpgrade() {
|
10 |
+
/** @var ModCon $mod */
|
11 |
+
$mod = $this->getMod();
|
12 |
+
$mod->deleteAllPluginCrons();
|
13 |
}
|
14 |
}
|
src/lib/src/Modules/Plugin/WpCli.php
CHANGED
@@ -10,7 +10,7 @@ class WpCli extends Base\WpCli {
|
|
10 |
/**
|
11 |
* @inheritDoc
|
12 |
*/
|
13 |
-
protected function getCmdHandlers() {
|
14 |
return [
|
15 |
new Plugin\WpCli\ForceOff(),
|
16 |
new Plugin\WpCli\Reset(),
|
10 |
/**
|
11 |
* @inheritDoc
|
12 |
*/
|
13 |
+
protected function getCmdHandlers() :array {
|
14 |
return [
|
15 |
new Plugin\WpCli\ForceOff(),
|
16 |
new Plugin\WpCli\Reset(),
|
src/lib/src/Modules/Reporting/Debug.php
CHANGED
@@ -7,5 +7,8 @@ use FernleafSystems\Wordpress\Plugin\Shield\Modules;
|
|
7 |
class Debug extends Modules\Base\Debug {
|
8 |
|
9 |
public function run() {
|
|
|
|
|
|
|
10 |
}
|
11 |
}
|
7 |
class Debug extends Modules\Base\Debug {
|
8 |
|
9 |
public function run() {
|
10 |
+
/** @var ModCon $mod */
|
11 |
+
$mod = $this->getMod();
|
12 |
+
$mod->getReportingController()->runHourlyCron();
|
13 |
}
|
14 |
}
|
src/lib/src/Modules/Reporting/Lib/ReportingController.php
CHANGED
@@ -3,6 +3,7 @@
|
|
3 |
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Reporting\Lib;
|
4 |
|
5 |
use FernleafSystems\Utilities\Logic\OneTimeExecute;
|
|
|
6 |
use FernleafSystems\Wordpress\Plugin\Shield\Databases\Reports as DBReports;
|
7 |
use FernleafSystems\Wordpress\Plugin\Shield\Modules;
|
8 |
use FernleafSystems\Wordpress\Plugin\Shield\Modules\Reporting\Lib\Reports\Build;
|
@@ -12,6 +13,7 @@ class ReportingController {
|
|
12 |
|
13 |
use Modules\ModConsumer;
|
14 |
use OneTimeExecute;
|
|
|
15 |
|
16 |
/**
|
17 |
* @return bool
|
@@ -23,7 +25,7 @@ class ReportingController {
|
|
23 |
}
|
24 |
|
25 |
protected function run() {
|
26 |
-
|
27 |
}
|
28 |
|
29 |
public function runHourlyCron() {
|
@@ -75,7 +77,7 @@ class ReportingController {
|
|
75 |
$record->frequency = $report->interval;
|
76 |
$record->interval_end_at = $report->interval_end_at;
|
77 |
|
78 |
-
/** @var \
|
79 |
$mod = $this->getMod();
|
80 |
return $mod->getDbHandler_Reports()
|
81 |
->getQueryInserter()
|
3 |
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Reporting\Lib;
|
4 |
|
5 |
use FernleafSystems\Utilities\Logic\OneTimeExecute;
|
6 |
+
use FernleafSystems\Wordpress\Plugin\Shield\Crons\PluginCronsConsumer;
|
7 |
use FernleafSystems\Wordpress\Plugin\Shield\Databases\Reports as DBReports;
|
8 |
use FernleafSystems\Wordpress\Plugin\Shield\Modules;
|
9 |
use FernleafSystems\Wordpress\Plugin\Shield\Modules\Reporting\Lib\Reports\Build;
|
13 |
|
14 |
use Modules\ModConsumer;
|
15 |
use OneTimeExecute;
|
16 |
+
use PluginCronsConsumer;
|
17 |
|
18 |
/**
|
19 |
* @return bool
|
25 |
}
|
26 |
|
27 |
protected function run() {
|
28 |
+
$this->setupCronHooks();
|
29 |
}
|
30 |
|
31 |
public function runHourlyCron() {
|
77 |
$record->frequency = $report->interval;
|
78 |
$record->interval_end_at = $report->interval_end_at;
|
79 |
|
80 |
+
/** @var Modules\Reporting\ModCon $mod */
|
81 |
$mod = $this->getMod();
|
82 |
return $mod->getDbHandler_Reports()
|
83 |
->getQueryInserter()
|
src/lib/src/Modules/Reporting/Lib/Reports/BaseReporter.php
CHANGED
@@ -28,11 +28,11 @@ abstract class BaseReporter {
|
|
28 |
}
|
29 |
|
30 |
/**
|
31 |
-
* @param ReportVO $
|
32 |
* @return $this
|
33 |
*/
|
34 |
-
public function setReport( ReportVO $
|
35 |
-
$this->rep = $
|
36 |
return $this;
|
37 |
}
|
38 |
}
|
28 |
}
|
29 |
|
30 |
/**
|
31 |
+
* @param ReportVO $rep
|
32 |
* @return $this
|
33 |
*/
|
34 |
+
public function setReport( ReportVO $rep ) {
|
35 |
+
$this->rep = $rep;
|
36 |
return $this;
|
37 |
}
|
38 |
}
|
src/lib/src/Modules/Reporting/Lib/Reports/Build/BaseBuilder.php
CHANGED
@@ -25,17 +25,14 @@ abstract class BaseBuilder {
|
|
25 |
*/
|
26 |
public function build() {
|
27 |
if ( $this->isReadyToSend() ) {
|
28 |
-
$
|
29 |
-
if ( !empty( $
|
30 |
-
$this->rep->content = $this->render( $
|
31 |
}
|
32 |
}
|
33 |
}
|
34 |
|
35 |
-
|
36 |
-
* @return bool
|
37 |
-
*/
|
38 |
-
protected function isReadyToSend() {
|
39 |
return !Services::WpGeneral()->isCron()
|
40 |
|| empty( $this->rep->previous )
|
41 |
|| Services::Request()->ts() > $this->rep->interval_end_at;
|
@@ -57,6 +54,9 @@ abstract class BaseBuilder {
|
|
57 |
$oCEnd = Services::Request()->carbon( true )->setTimestamp( $this->rep->interval_end_at );
|
58 |
|
59 |
switch ( $this->rep->interval ) {
|
|
|
|
|
|
|
60 |
case 'hourly':
|
61 |
$sTime = sprintf( 'The full hour from %s until %s on %s.',
|
62 |
$oCStart->format( 'H:i' ),
|
25 |
*/
|
26 |
public function build() {
|
27 |
if ( $this->isReadyToSend() ) {
|
28 |
+
$data = $this->gather();
|
29 |
+
if ( !empty( $data ) ) {
|
30 |
+
$this->rep->content = $this->render( $data );
|
31 |
}
|
32 |
}
|
33 |
}
|
34 |
|
35 |
+
protected function isReadyToSend() :bool {
|
|
|
|
|
|
|
36 |
return !Services::WpGeneral()->isCron()
|
37 |
|| empty( $this->rep->previous )
|
38 |
|| Services::Request()->ts() > $this->rep->interval_end_at;
|
54 |
$oCEnd = Services::Request()->carbon( true )->setTimestamp( $this->rep->interval_end_at );
|
55 |
|
56 |
switch ( $this->rep->interval ) {
|
57 |
+
case 'no_time': // TODO
|
58 |
+
$sTime = __( 'No Time Interval', 'wp-simple-firewall' );
|
59 |
+
break;
|
60 |
case 'hourly':
|
61 |
$sTime = sprintf( 'The full hour from %s until %s on %s.',
|
62 |
$oCStart->format( 'H:i' ),
|
src/lib/src/Modules/Reporting/Lib/Reports/Build/BuilderAlerts.php
CHANGED
@@ -3,7 +3,7 @@
|
|
3 |
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Reporting\Lib\Reports\Build;
|
4 |
|
5 |
use FernleafSystems\Wordpress\Plugin\Shield\Databases;
|
6 |
-
use FernleafSystems\Wordpress\Plugin\Shield\Modules\Base\
|
7 |
|
8 |
class BuilderAlerts extends BaseBuilder {
|
9 |
|
@@ -11,20 +11,19 @@ class BuilderAlerts extends BaseBuilder {
|
|
11 |
* @return string[]
|
12 |
*/
|
13 |
protected function gather() :array {
|
14 |
-
$
|
15 |
-
foreach ( $this->getCon()->modules as $
|
16 |
-
$
|
17 |
-
if ( $
|
18 |
-
foreach ( $
|
19 |
-
$
|
20 |
-
$
|
21 |
-
$
|
22 |
-
->build()
|
23 |
);
|
24 |
}
|
25 |
}
|
26 |
}
|
27 |
-
return $
|
28 |
}
|
29 |
|
30 |
protected function render( array $aGatheredData ) :string {
|
3 |
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Reporting\Lib\Reports\Build;
|
4 |
|
5 |
use FernleafSystems\Wordpress\Plugin\Shield\Databases;
|
6 |
+
use FernleafSystems\Wordpress\Plugin\Shield\Modules\Base\Reporting;
|
7 |
|
8 |
class BuilderAlerts extends BaseBuilder {
|
9 |
|
11 |
* @return string[]
|
12 |
*/
|
13 |
protected function gather() :array {
|
14 |
+
$reports = [];
|
15 |
+
foreach ( $this->getCon()->modules as $mod ) {
|
16 |
+
$repCon = $mod->getReportingHandler();
|
17 |
+
if ( $repCon instanceof Reporting ) {
|
18 |
+
foreach ( $repCon->getAlertReporters() as $reporter ) {
|
19 |
+
$reports = array_merge(
|
20 |
+
$reports,
|
21 |
+
$reporter->setReport( $this->rep )->build()
|
|
|
22 |
);
|
23 |
}
|
24 |
}
|
25 |
}
|
26 |
+
return $reports;
|
27 |
}
|
28 |
|
29 |
protected function render( array $aGatheredData ) :string {
|
src/lib/src/Modules/Reporting/Lib/Reports/Build/BuilderInfo.php
CHANGED
@@ -2,7 +2,7 @@
|
|
2 |
|
3 |
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Reporting\Lib\Reports\Build;
|
4 |
|
5 |
-
use FernleafSystems\Wordpress\Plugin\Shield\Modules\Base\
|
6 |
|
7 |
class BuilderInfo extends BaseBuilder {
|
8 |
|
@@ -10,20 +10,19 @@ class BuilderInfo extends BaseBuilder {
|
|
10 |
* @return string[]
|
11 |
*/
|
12 |
protected function gather() :array {
|
13 |
-
$
|
14 |
-
foreach ( $this->getCon()->modules as $
|
15 |
-
$
|
16 |
-
if ( $
|
17 |
-
foreach ( $
|
18 |
-
$
|
19 |
-
$
|
20 |
-
$
|
21 |
-
->build()
|
22 |
);
|
23 |
}
|
24 |
}
|
25 |
}
|
26 |
-
return $
|
27 |
}
|
28 |
|
29 |
protected function render( array $aGatheredData ) :string {
|
2 |
|
3 |
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Reporting\Lib\Reports\Build;
|
4 |
|
5 |
+
use FernleafSystems\Wordpress\Plugin\Shield\Modules\Base\Reporting;
|
6 |
|
7 |
class BuilderInfo extends BaseBuilder {
|
8 |
|
10 |
* @return string[]
|
11 |
*/
|
12 |
protected function gather() :array {
|
13 |
+
$reports = [];
|
14 |
+
foreach ( $this->getCon()->modules as $mod ) {
|
15 |
+
$repCon = $mod->getReportingHandler();
|
16 |
+
if ( $repCon instanceof Reporting ) {
|
17 |
+
foreach ( $repCon->getInfoReporters() as $reporter ) {
|
18 |
+
$reports = array_merge(
|
19 |
+
$reports,
|
20 |
+
$reporter->setReport( $this->rep )->build()
|
|
|
21 |
);
|
22 |
}
|
23 |
}
|
24 |
}
|
25 |
+
return $reports;
|
26 |
}
|
27 |
|
28 |
protected function render( array $aGatheredData ) :string {
|
src/lib/src/Modules/Reporting/Lib/Reports/CreateReportVO.php
CHANGED
@@ -17,12 +17,11 @@ class CreateReportVO {
|
|
17 |
private $rep;
|
18 |
|
19 |
/**
|
20 |
-
*
|
21 |
-
* @param string $sReportType
|
22 |
*/
|
23 |
-
public function __construct( $
|
24 |
$this->rep = new ReportVO();
|
25 |
-
$this->rep->type = $
|
26 |
}
|
27 |
|
28 |
/**
|
@@ -42,19 +41,18 @@ class CreateReportVO {
|
|
42 |
* @throws \Exception
|
43 |
*/
|
44 |
private function setReportInterval() {
|
45 |
-
/** @var Reporting\Options $
|
46 |
-
$
|
47 |
|
48 |
switch ( $this->rep->type ) {
|
49 |
case Reports\Handler::TYPE_ALERT:
|
50 |
-
$this->rep->interval = $
|
51 |
break;
|
52 |
case Reports\Handler::TYPE_INFO:
|
53 |
-
$this->rep->interval = $
|
54 |
break;
|
55 |
default:
|
56 |
throw new \Exception( 'Not a supported report type: '.$this->rep->type );
|
57 |
-
break;
|
58 |
}
|
59 |
return $this;
|
60 |
}
|
@@ -63,15 +61,14 @@ class CreateReportVO {
|
|
63 |
* @return $this
|
64 |
*/
|
65 |
private function setPreviousReport() {
|
66 |
-
/** @var \
|
67 |
-
$
|
68 |
-
/** @var Reports\Select $
|
69 |
-
$
|
70 |
-
|
71 |
-
|
72 |
-
|
73 |
-
|
74 |
-
->first();
|
75 |
return $this;
|
76 |
}
|
77 |
|
@@ -84,49 +81,52 @@ class CreateReportVO {
|
|
84 |
*/
|
85 |
private function setIntervalBoundaries() {
|
86 |
|
87 |
-
$
|
88 |
$nAddition = -1; // the previous hour, day, week, month
|
89 |
|
90 |
switch ( $this->rep->interval ) {
|
91 |
// case 'realtime':
|
92 |
// break;
|
|
|
|
|
|
|
|
|
93 |
case 'hourly':
|
94 |
-
$
|
95 |
-
$
|
96 |
-
$
|
97 |
break;
|
98 |
case 'daily':
|
99 |
-
$
|
100 |
-
$
|
101 |
-
$
|
102 |
break;
|
103 |
case 'weekly':
|
104 |
-
$
|
105 |
-
$
|
106 |
-
$
|
107 |
break;
|
108 |
case 'monthly':
|
109 |
-
$
|
110 |
-
$
|
111 |
-
$
|
112 |
break;
|
113 |
case 'yearly':
|
114 |
-
$
|
115 |
-
$
|
116 |
-
$
|
117 |
break;
|
118 |
default:
|
119 |
throw new \Exception( 'Not a supported frequency' );
|
120 |
-
break;
|
121 |
}
|
122 |
|
123 |
if ( $this->rep->previous instanceof Reports\EntryVO
|
124 |
-
&& $
|
125 |
throw new \Exception( 'Attempting to create a duplicate report based on interval.' );
|
126 |
}
|
127 |
|
128 |
-
$this->rep->interval_start_at = $
|
129 |
-
$this->rep->interval_end_at = $
|
130 |
|
131 |
return $this;
|
132 |
}
|
@@ -136,10 +136,10 @@ class CreateReportVO {
|
|
136 |
* @throws \Exception
|
137 |
*/
|
138 |
private function setReportId() {
|
139 |
-
/** @var \
|
140 |
-
$
|
141 |
/** @var Reports\Select $oSel */
|
142 |
-
$oSel = $
|
143 |
$nPrevID = $oSel->getLastReportId();
|
144 |
$this->rep->rid = is_numeric( $nPrevID ) ? $nPrevID + 1 : 1;
|
145 |
return $this;
|
17 |
private $rep;
|
18 |
|
19 |
/**
|
20 |
+
* @param string $reportType
|
|
|
21 |
*/
|
22 |
+
public function __construct( string $reportType ) {
|
23 |
$this->rep = new ReportVO();
|
24 |
+
$this->rep->type = $reportType;
|
25 |
}
|
26 |
|
27 |
/**
|
41 |
* @throws \Exception
|
42 |
*/
|
43 |
private function setReportInterval() {
|
44 |
+
/** @var Reporting\Options $opts */
|
45 |
+
$opts = $this->getOptions();
|
46 |
|
47 |
switch ( $this->rep->type ) {
|
48 |
case Reports\Handler::TYPE_ALERT:
|
49 |
+
$this->rep->interval = $opts->getFrequencyAlert();
|
50 |
break;
|
51 |
case Reports\Handler::TYPE_INFO:
|
52 |
+
$this->rep->interval = $opts->getFrequencyInfo();
|
53 |
break;
|
54 |
default:
|
55 |
throw new \Exception( 'Not a supported report type: '.$this->rep->type );
|
|
|
56 |
}
|
57 |
return $this;
|
58 |
}
|
61 |
* @return $this
|
62 |
*/
|
63 |
private function setPreviousReport() {
|
64 |
+
/** @var Reporting\ModCon $mod */
|
65 |
+
$mod = $this->getMod();
|
66 |
+
/** @var Reports\Select $sel */
|
67 |
+
$sel = $mod->getDbHandler_Reports()->getQuerySelector();
|
68 |
+
$this->rep->previous = $sel->filterByType( $this->rep->type )
|
69 |
+
->filterByFrequency( $this->rep->interval )
|
70 |
+
->setOrderBy( 'sent_at', 'DESC' )
|
71 |
+
->first();
|
|
|
72 |
return $this;
|
73 |
}
|
74 |
|
81 |
*/
|
82 |
private function setIntervalBoundaries() {
|
83 |
|
84 |
+
$carbon = Services::Request()->carbon( true );
|
85 |
$nAddition = -1; // the previous hour, day, week, month
|
86 |
|
87 |
switch ( $this->rep->interval ) {
|
88 |
// case 'realtime':
|
89 |
// break;
|
90 |
+
case 'no_time': // TODO
|
91 |
+
$start = 0;
|
92 |
+
$end = $carbon->timestamp;
|
93 |
+
break;
|
94 |
case 'hourly':
|
95 |
+
$carbon->addHours( $nAddition );
|
96 |
+
$start = $carbon->startOfHour()->timestamp;
|
97 |
+
$end = $carbon->endOfHour()->timestamp;
|
98 |
break;
|
99 |
case 'daily':
|
100 |
+
$carbon->addDays( $nAddition );
|
101 |
+
$start = $carbon->startOfDay()->timestamp;
|
102 |
+
$end = $carbon->endOfDay()->timestamp;
|
103 |
break;
|
104 |
case 'weekly':
|
105 |
+
$carbon->addWeeks( $nAddition );
|
106 |
+
$start = $carbon->startOfWeek()->timestamp;
|
107 |
+
$end = $carbon->endOfWeek()->timestamp;
|
108 |
break;
|
109 |
case 'monthly':
|
110 |
+
$carbon->addMonths( $nAddition );
|
111 |
+
$start = $carbon->startOfMonth()->timestamp;
|
112 |
+
$end = $carbon->endOfMonth()->timestamp;
|
113 |
break;
|
114 |
case 'yearly':
|
115 |
+
$carbon->addYears( $nAddition );
|
116 |
+
$start = $carbon->startOfYear()->timestamp;
|
117 |
+
$end = $carbon->endOfYear()->timestamp;
|
118 |
break;
|
119 |
default:
|
120 |
throw new \Exception( 'Not a supported frequency' );
|
|
|
121 |
}
|
122 |
|
123 |
if ( $this->rep->previous instanceof Reports\EntryVO
|
124 |
+
&& $end <= $this->rep->previous->interval_end_at ) {
|
125 |
throw new \Exception( 'Attempting to create a duplicate report based on interval.' );
|
126 |
}
|
127 |
|
128 |
+
$this->rep->interval_start_at = $start;
|
129 |
+
$this->rep->interval_end_at = $end;
|
130 |
|
131 |
return $this;
|
132 |
}
|
136 |
* @throws \Exception
|
137 |
*/
|
138 |
private function setReportId() {
|
139 |
+
/** @var Reporting\ModCon $mod */
|
140 |
+
$mod = $this->getMod();
|
141 |
/** @var Reports\Select $oSel */
|
142 |
+
$oSel = $mod->getDbHandler_Reports()->getQuerySelector();
|
143 |
$nPrevID = $oSel->getLastReportId();
|
144 |
$this->rep->rid = is_numeric( $nPrevID ) ? $nPrevID + 1 : 1;
|
145 |
return $this;
|
src/lib/src/Modules/Reporting/ModCon.php
ADDED
@@ -0,0 +1,25 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php declare( strict_types=1 );
|
2 |
+
|
3 |
+
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Reporting;
|
4 |
+
|
5 |
+
use FernleafSystems\Wordpress\Plugin\Shield\Databases;
|
6 |
+
use FernleafSystems\Wordpress\Plugin\Shield\Modules\BaseShield;
|
7 |
+
|
8 |
+
class ModCon extends BaseShield\ModCon {
|
9 |
+
|
10 |
+
/**
|
11 |
+
* @var Lib\ReportingController
|
12 |
+
*/
|
13 |
+
private $reportsCon;
|
14 |
+
|
15 |
+
public function getDbHandler_Reports() :Databases\Reports\Handler {
|
16 |
+
return $this->getDbH( 'reports' );
|
17 |
+
}
|
18 |
+
|
19 |
+
public function getReportingController() :Lib\ReportingController {
|
20 |
+
if ( !isset( $this->reportsCon ) ) {
|
21 |
+
$this->reportsCon = ( new Lib\ReportingController() )->setMod( $this );
|
22 |
+
}
|
23 |
+
return $this->reportsCon;
|
24 |
+
}
|
25 |
+
}
|
src/lib/src/Modules/Reporting/Options.php
CHANGED
@@ -2,9 +2,9 @@
|
|
2 |
|
3 |
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Reporting;
|
4 |
|
5 |
-
use FernleafSystems\Wordpress\Plugin\Shield\Modules\
|
6 |
|
7 |
-
class Options extends
|
8 |
|
9 |
public function getFrequencyAlert() :string {
|
10 |
return $this->getFrequency( 'alert' );
|
@@ -16,17 +16,9 @@ class Options extends Base\ShieldOptions {
|
|
16 |
|
17 |
private function getFrequency( string $type ) :string {
|
18 |
$key = 'frequency_'.$type;
|
19 |
-
$
|
20 |
-
return ( $this->isPremium() || in_array( $this->getOpt( $key ), [ 'disabled', $
|
21 |
? $this->getOpt( $key )
|
22 |
-
: $
|
23 |
-
}
|
24 |
-
|
25 |
-
/**
|
26 |
-
* @return string
|
27 |
-
* @deprecated 10.0
|
28 |
-
*/
|
29 |
-
public function getDbTable_Reports() :string {
|
30 |
-
return $this->getCon()->prefixOption( $this->getDef( 'reports_table_name' ) );
|
31 |
}
|
32 |
}
|
2 |
|
3 |
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Reporting;
|
4 |
|
5 |
+
use FernleafSystems\Wordpress\Plugin\Shield\Modules\BaseShield;
|
6 |
|
7 |
+
class Options extends BaseShield\Options {
|
8 |
|
9 |
public function getFrequencyAlert() :string {
|
10 |
return $this->getFrequency( 'alert' );
|
16 |
|
17 |
private function getFrequency( string $type ) :string {
|
18 |
$key = 'frequency_'.$type;
|
19 |
+
$default = $this->getOptDefault( $key );
|
20 |
+
return ( $this->isPremium() || in_array( $this->getOpt( $key ), [ 'disabled', $default ] ) )
|
21 |
? $this->getOpt( $key )
|
22 |
+
: $default;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
23 |
}
|
24 |
}
|
src/lib/src/Modules/Reporting/Processor.php
ADDED
@@ -0,0 +1,14 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php declare( strict_types=1 );
|
2 |
+
|
3 |
+
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Reporting;
|
4 |
+
|
5 |
+
use FernleafSystems\Wordpress\Plugin\Shield\Modules\BaseShield;
|
6 |
+
|
7 |
+
class Processor extends BaseShield\Processor {
|
8 |
+
|
9 |
+
protected function run() {
|
10 |
+
/** @var ModCon $mod */
|
11 |
+
$mod = $this->getMod();
|
12 |
+
$mod->getReportingController()->execute();
|
13 |
+
}
|
14 |
+
}
|
src/lib/src/Modules/Reporting/UI.php
CHANGED
@@ -3,10 +3,10 @@
|
|
3 |
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Reporting;
|
4 |
|
5 |
use FernleafSystems\Wordpress\Plugin\Shield\Databases;
|
6 |
-
use FernleafSystems\Wordpress\Plugin\Shield\Modules\
|
7 |
use FernleafSystems\Wordpress\Plugin\Shield\Modules\Events;
|
8 |
|
9 |
-
class UI extends
|
10 |
|
11 |
public function renderSummaryStats() :string {
|
12 |
$con = $this->getCon();
|
3 |
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Reporting;
|
4 |
|
5 |
use FernleafSystems\Wordpress\Plugin\Shield\Databases;
|
6 |
+
use FernleafSystems\Wordpress\Plugin\Shield\Modules\BaseShield;
|
7 |
use FernleafSystems\Wordpress\Plugin\Shield\Modules\Events;
|
8 |
|
9 |
+
class UI extends BaseShield\UI {
|
10 |
|
11 |
public function renderSummaryStats() :string {
|
12 |
$con = $this->getCon();
|
src/lib/src/Modules/SecurityAdmin/AdminNotices.php
CHANGED
@@ -3,40 +3,37 @@
|
|
3 |
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\SecurityAdmin;
|
4 |
|
5 |
use FernleafSystems\Wordpress\Plugin\Shield;
|
|
|
6 |
use FernleafSystems\Wordpress\Services\Services;
|
7 |
|
8 |
class AdminNotices extends Shield\Modules\Base\AdminNotices {
|
9 |
|
10 |
/**
|
11 |
-
* @
|
12 |
-
* @throws \Exception
|
13 |
*/
|
14 |
-
protected function processNotice( $
|
15 |
|
16 |
-
switch ( $
|
17 |
|
18 |
case 'admin-users-restricted':
|
19 |
-
$this->buildNotice_AdminUsersRestricted( $
|
20 |
break;
|
21 |
|
22 |
case 'certain-options-restricted':
|
23 |
-
$this->buildNotice_CertainOptionsRestricted( $
|
24 |
break;
|
25 |
|
26 |
default:
|
27 |
-
parent::processNotice( $
|
28 |
break;
|
29 |
}
|
30 |
}
|
31 |
|
32 |
-
|
33 |
-
* @param Shield\Utilities\AdminNotices\NoticeVO $oNotice
|
34 |
-
*/
|
35 |
-
private function buildNotice_CertainOptionsRestricted( $oNotice ) {
|
36 |
$oMod = $this->getMod();
|
37 |
$sName = $this->getCon()->getHumanName();
|
38 |
|
39 |
-
$
|
40 |
'notice_attributes' => [],
|
41 |
'strings' => [
|
42 |
'title' => sprintf( __( '%s Security Restrictions Applied', 'wp-simple-firewall' ), $sName ),
|
@@ -54,14 +51,11 @@ class AdminNotices extends Shield\Modules\Base\AdminNotices {
|
|
54 |
];
|
55 |
}
|
56 |
|
57 |
-
|
58 |
-
* @param Shield\Utilities\AdminNotices\NoticeVO $oNotice
|
59 |
-
*/
|
60 |
-
private function buildNotice_AdminUsersRestricted( $oNotice ) {
|
61 |
$oMod = $this->getMod();
|
62 |
$sName = $this->getCon()->getHumanName();
|
63 |
|
64 |
-
$
|
65 |
'notice_attributes' => [], // TODO
|
66 |
'strings' => [
|
67 |
'title' => sprintf( __( '%s Security Restrictions Applied', 'wp-simple-firewall' ), $sName ),
|
@@ -85,31 +79,27 @@ class AdminNotices extends Shield\Modules\Base\AdminNotices {
|
|
85 |
];
|
86 |
}
|
87 |
|
88 |
-
|
89 |
-
* @param Shield\Utilities\AdminNotices\NoticeVO $oNotice
|
90 |
-
* @return bool
|
91 |
-
*/
|
92 |
-
protected function isDisplayNeeded( $oNotice ) {
|
93 |
/** @var Options $oOpts */
|
94 |
$oOpts = $this->getOptions();
|
95 |
|
96 |
$sCurrentPage = Services::WpPost()->getCurrentPage();
|
97 |
|
98 |
-
switch ( $
|
99 |
|
100 |
case 'admin-users-restricted':
|
101 |
-
$
|
102 |
break;
|
103 |
|
104 |
case 'certain-options-restricted':
|
105 |
$sCurrentGetPage = Services::Request()->query( 'page' );
|
106 |
-
$
|
107 |
break;
|
108 |
|
109 |
default:
|
110 |
-
$
|
111 |
break;
|
112 |
}
|
113 |
-
return $
|
114 |
}
|
115 |
}
|
3 |
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\SecurityAdmin;
|
4 |
|
5 |
use FernleafSystems\Wordpress\Plugin\Shield;
|
6 |
+
use FernleafSystems\Wordpress\Plugin\Shield\Utilities\AdminNotices\NoticeVO;
|
7 |
use FernleafSystems\Wordpress\Services\Services;
|
8 |
|
9 |
class AdminNotices extends Shield\Modules\Base\AdminNotices {
|
10 |
|
11 |
/**
|
12 |
+
* @inheritDoc
|
|
|
13 |
*/
|
14 |
+
protected function processNotice( NoticeVO $notice ) {
|
15 |
|
16 |
+
switch ( $notice->id ) {
|
17 |
|
18 |
case 'admin-users-restricted':
|
19 |
+
$this->buildNotice_AdminUsersRestricted( $notice );
|
20 |
break;
|
21 |
|
22 |
case 'certain-options-restricted':
|
23 |
+
$this->buildNotice_CertainOptionsRestricted( $notice );
|
24 |
break;
|
25 |
|
26 |
default:
|
27 |
+
parent::processNotice( $notice );
|
28 |
break;
|
29 |
}
|
30 |
}
|
31 |
|
32 |
+
private function buildNotice_CertainOptionsRestricted( NoticeVO $notice ) {
|
|
|
|
|
|
|
33 |
$oMod = $this->getMod();
|
34 |
$sName = $this->getCon()->getHumanName();
|
35 |
|
36 |
+
$notice->render_data = [
|
37 |
'notice_attributes' => [],
|
38 |
'strings' => [
|
39 |
'title' => sprintf( __( '%s Security Restrictions Applied', 'wp-simple-firewall' ), $sName ),
|
51 |
];
|
52 |
}
|
53 |
|
54 |
+
private function buildNotice_AdminUsersRestricted( NoticeVO $notice ) {
|
|
|
|
|
|
|
55 |
$oMod = $this->getMod();
|
56 |
$sName = $this->getCon()->getHumanName();
|
57 |
|
58 |
+
$notice->render_data = [
|
59 |
'notice_attributes' => [], // TODO
|
60 |
'strings' => [
|
61 |
'title' => sprintf( __( '%s Security Restrictions Applied', 'wp-simple-firewall' ), $sName ),
|
79 |
];
|
80 |
}
|
81 |
|
82 |
+
protected function isDisplayNeeded( NoticeVO $notice ) :bool {
|
|
|
|
|
|
|
|
|
83 |
/** @var Options $oOpts */
|
84 |
$oOpts = $this->getOptions();
|
85 |
|
86 |
$sCurrentPage = Services::WpPost()->getCurrentPage();
|
87 |
|
88 |
+
switch ( $notice->id ) {
|
89 |
|
90 |
case 'admin-users-restricted':
|
91 |
+
$needed = in_array( $sCurrentPage, $oOpts->getDef( 'restricted_pages_users' ) );
|
92 |
break;
|
93 |
|
94 |
case 'certain-options-restricted':
|
95 |
$sCurrentGetPage = Services::Request()->query( 'page' );
|
96 |
+
$needed = empty( $sCurrentGetPage ) && in_array( $sCurrentPage, $oOpts->getOptionsPagesToRestrict() );
|
97 |
break;
|
98 |
|
99 |
default:
|
100 |
+
$needed = parent::isDisplayNeeded( $notice );
|
101 |
break;
|
102 |
}
|
103 |
+
return $needed;
|
104 |
}
|
105 |
}
|
src/lib/src/Modules/SecurityAdmin/AjaxHandler.php
CHANGED
@@ -5,7 +5,7 @@ namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\SecurityAdmin;
|
|
5 |
use FernleafSystems\Wordpress\Plugin\Shield;
|
6 |
use FernleafSystems\Wordpress\Services\Services;
|
7 |
|
8 |
-
class AjaxHandler extends Shield\Modules\
|
9 |
|
10 |
protected function processAjaxAction( string $action ) :array {
|
11 |
|
@@ -38,26 +38,23 @@ class AjaxHandler extends Shield\Modules\Base\AjaxHandlerShield {
|
|
38 |
* @return array
|
39 |
*/
|
40 |
private function ajaxExec_SecAdminCheck() {
|
41 |
-
/** @var
|
42 |
-
$
|
43 |
return [
|
44 |
-
'timeleft' => $
|
45 |
-
'success' => $
|
46 |
];
|
47 |
}
|
48 |
|
49 |
-
|
50 |
-
|
51 |
-
|
52 |
-
private function ajaxExec_SecAdminLogin() {
|
53 |
-
/** @var \ICWP_WPSF_FeatureHandler_AdminAccessRestriction $oMod */
|
54 |
-
$oMod = $this->getMod();
|
55 |
$bSuccess = false;
|
56 |
$sHtml = '';
|
57 |
|
58 |
-
if ( $
|
59 |
|
60 |
-
if ( $
|
61 |
$bSuccess = true;
|
62 |
$sMsg = __( 'Security Admin PIN Accepted.', 'wp-simple-firewall' )
|
63 |
.' '.__( 'Please wait', 'wp-simple-firewall' ).' ...';
|
@@ -117,17 +114,15 @@ class AjaxHandler extends Shield\Modules\Base\AjaxHandlerShield {
|
|
117 |
* @return string
|
118 |
*/
|
119 |
private function renderAdminAccessAjaxLoginForm( $sMessage = '' ) {
|
120 |
-
/** @var
|
121 |
-
$
|
122 |
-
|
123 |
-
$aData = [
|
124 |
'ajax' => [
|
125 |
-
'sec_admin_login' => json_encode( $
|
126 |
],
|
127 |
'strings' => [
|
128 |
'access_message' => empty( $sMessage ) ? __( 'Enter your Security Admin PIN', 'wp-simple-firewall' ) : $sMessage
|
129 |
]
|
130 |
-
];
|
131 |
-
return $oMod->renderTemplate( 'snippets/admin_access_login', $aData );
|
132 |
}
|
133 |
}
|
5 |
use FernleafSystems\Wordpress\Plugin\Shield;
|
6 |
use FernleafSystems\Wordpress\Services\Services;
|
7 |
|
8 |
+
class AjaxHandler extends Shield\Modules\BaseShield\AjaxHandler {
|
9 |
|
10 |
protected function processAjaxAction( string $action ) :array {
|
11 |
|
38 |
* @return array
|
39 |
*/
|
40 |
private function ajaxExec_SecAdminCheck() {
|
41 |
+
/** @var ModCon $mod */
|
42 |
+
$mod = $this->getMod();
|
43 |
return [
|
44 |
+
'timeleft' => $mod->getSecAdminTimeLeft(),
|
45 |
+
'success' => $mod->isSecAdminSessionValid()
|
46 |
];
|
47 |
}
|
48 |
|
49 |
+
private function ajaxExec_SecAdminLogin() :array {
|
50 |
+
/** @var ModCon $mod */
|
51 |
+
$mod = $this->getMod();
|
|
|
|
|
|
|
52 |
$bSuccess = false;
|
53 |
$sHtml = '';
|
54 |
|
55 |
+
if ( $mod->testSecAccessKeyRequest() ) {
|
56 |
|
57 |
+
if ( $mod->setSecurityAdminStatusOnOff( true ) ) {
|
58 |
$bSuccess = true;
|
59 |
$sMsg = __( 'Security Admin PIN Accepted.', 'wp-simple-firewall' )
|
60 |
.' '.__( 'Please wait', 'wp-simple-firewall' ).' ...';
|
114 |
* @return string
|
115 |
*/
|
116 |
private function renderAdminAccessAjaxLoginForm( $sMessage = '' ) {
|
117 |
+
/** @var ModCon $mod */
|
118 |
+
$mod = $this->getMod();
|
119 |
+
return $mod->renderTemplate( 'snippets/admin_access_login', [
|
|
|
120 |
'ajax' => [
|
121 |
+
'sec_admin_login' => json_encode( $mod->getSecAdminLoginAjaxData() )
|
122 |
],
|
123 |
'strings' => [
|
124 |
'access_message' => empty( $sMessage ) ? __( 'Enter your Security Admin PIN', 'wp-simple-firewall' ) : $sMessage
|
125 |
]
|
126 |
+
] );
|
|
|
127 |
}
|
128 |
}
|
src/lib/src/Modules/SecurityAdmin/Insights/OverviewCards.php
CHANGED
@@ -7,7 +7,7 @@ use FernleafSystems\Wordpress\Plugin\Shield;
|
|
7 |
class OverviewCards extends Shield\Modules\Base\Insights\OverviewCards {
|
8 |
|
9 |
public function build() :array {
|
10 |
-
/** @var \
|
11 |
$mod = $this->getMod();
|
12 |
/** @var Shield\Modules\SecurityAdmin\Options $opts */
|
13 |
$opts = $this->getOptions();
|
7 |
class OverviewCards extends Shield\Modules\Base\Insights\OverviewCards {
|
8 |
|
9 |
public function build() :array {
|
10 |
+
/** @var Shield\Modules\SecurityAdmin\ModCon $mod */
|
11 |
$mod = $this->getMod();
|
12 |
/** @var Shield\Modules\SecurityAdmin\Options $opts */
|
13 |
$opts = $this->getOptions();
|
src/lib/src/Modules/SecurityAdmin/Lib/Actions/RemoveSecAdmin.php
CHANGED
@@ -21,56 +21,47 @@ class RemoveSecAdmin {
|
|
21 |
}
|
22 |
|
23 |
public function sendConfirmationEmail() {
|
24 |
-
/** @var \
|
25 |
-
$
|
26 |
-
|
27 |
-
|
28 |
-
|
29 |
-
|
30 |
-
|
31 |
-
|
32 |
-
|
33 |
-
|
34 |
-
|
35 |
-
|
36 |
-
|
37 |
-
|
38 |
-
|
39 |
-
|
40 |
-
|
41 |
-
|
42 |
-
|
43 |
-
|
44 |
-
|
45 |
-
|
46 |
-
|
47 |
-
|
48 |
-
|
49 |
-
$sEmailSubject = __( 'Please Confirm Security Admin Removal', 'wp-simple-firewall' );
|
50 |
-
return $this->getMod()
|
51 |
-
->getEmailProcessor()
|
52 |
-
->sendEmailWithWrap( $sEmail, $sEmailSubject, $aMessage );
|
53 |
}
|
54 |
|
55 |
private function sendNotificationEmail() {
|
56 |
-
$sEmail = $this->getMod()->getPluginReportEmail();
|
57 |
-
if ( !Services::Data()->validEmail( $sEmail ) ) {
|
58 |
-
$sEmail = Services::WpGeneral()->getSiteAdminEmail();
|
59 |
-
}
|
60 |
-
|
61 |
-
$aMessage = [
|
62 |
-
__( 'This is an email notification to inform you that the Security Admin restriction has been removed.', 'wp-simple-firewall' ),
|
63 |
-
__( 'This was done using a confirmation email sent to the Security Administrator email address.', 'wp-simple-firewall' ),
|
64 |
-
__( 'All restrictions imposed by the Security Admin module have been lifted.', 'wp-simple-firewall' ),
|
65 |
-
'',
|
66 |
-
__( "Please understand that to reinstate the Security Admin features, you'll need to provide a new Security Admin password.", 'wp-simple-firewall' ),
|
67 |
-
'',
|
68 |
-
__( "Thank you.", 'wp-simple-firewall' )
|
69 |
-
];
|
70 |
-
|
71 |
-
$sEmailSubject = __( 'Security Admin restrictions have been removed', 'wp-simple-firewall' );
|
72 |
return $this->getMod()
|
73 |
->getEmailProcessor()
|
74 |
-
->sendEmailWithWrap(
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
75 |
}
|
76 |
}
|
21 |
}
|
22 |
|
23 |
public function sendConfirmationEmail() {
|
24 |
+
/** @var SecurityAdmin\ModCon $mod */
|
25 |
+
$mod = $this->getMod();
|
26 |
+
return $mod->getEmailProcessor()
|
27 |
+
->sendEmailWithWrap(
|
28 |
+
$mod->getPluginReportEmail(),
|
29 |
+
__( 'Please Confirm Security Admin Removal', 'wp-simple-firewall' ),
|
30 |
+
[
|
31 |
+
sprintf( __( 'A WordPress user (%s) has requested to remove the Security Admin restriction.', 'wp-simple-firewall' ),
|
32 |
+
Services::WpUsers()->getCurrentWpUsername() ).' '.
|
33 |
+
__( 'The purpose of this email is to confirm this action.', 'wp-simple-firewall' ),
|
34 |
+
__( 'Please click the link below to confirm the removal of the Security Admin restriction.', 'wp-simple-firewall' ),
|
35 |
+
'',
|
36 |
+
'<strong>'.sprintf( '%s: %s', __( 'Important', 'wp-simple-firewall' ),
|
37 |
+
__( 'This link must be opened in the same browser that was used to make this original request.', 'wp-simple-firewall' )
|
38 |
+
).'</strong>',
|
39 |
+
'',
|
40 |
+
sprintf( '%s: %s', __( 'Confirmation link', 'wp-simple-firewall' ),
|
41 |
+
$mod->buildAdminActionNonceUrl( 'remove_secadmin_confirm' ) ),
|
42 |
+
'',
|
43 |
+
__( "Please understand that to reinstate the Security Admin features, you'll need to provide a new Security Admin password.", 'wp-simple-firewall' ),
|
44 |
+
'',
|
45 |
+
__( "Thank you.", 'wp-simple-firewall' )
|
46 |
+
]
|
47 |
+
);
|
|
|
|
|
|
|
|
|
|
|
48 |
}
|
49 |
|
50 |
private function sendNotificationEmail() {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
51 |
return $this->getMod()
|
52 |
->getEmailProcessor()
|
53 |
+
->sendEmailWithWrap(
|
54 |
+
$this->getMod()->getPluginReportEmail(),
|
55 |
+
__( 'Security Admin restrictions have been removed', 'wp-simple-firewall' ),
|
56 |
+
[
|
57 |
+
__( 'This is an email notification to inform you that the Security Admin restriction has been removed.', 'wp-simple-firewall' ),
|
58 |
+
__( 'This was done using a confirmation email sent to the Security Administrator email address.', 'wp-simple-firewall' ),
|
59 |
+
__( 'All restrictions imposed by the Security Admin module have been lifted.', 'wp-simple-firewall' ),
|
60 |
+
'',
|
61 |
+
__( "Please understand that to reinstate the Security Admin features, you'll need to provide a new Security Admin password.", 'wp-simple-firewall' ),
|
62 |
+
'',
|
63 |
+
__( "Thank you.", 'wp-simple-firewall' )
|
64 |
+
]
|
65 |
+
);
|
66 |
}
|
67 |
}
|
src/lib/src/Modules/SecurityAdmin/Lib/Actions/SetSecAdminPin.php
CHANGED
@@ -9,11 +9,11 @@ class SetSecAdminPin {
|
|
9 |
use ModConsumer;
|
10 |
|
11 |
/**
|
12 |
-
* @param string $
|
13 |
* @throws \Exception
|
14 |
*/
|
15 |
-
public function run( $
|
16 |
-
if ( empty( $
|
17 |
throw new \Exception( 'Attempting to set an empty Security Admin Access Key.' );
|
18 |
}
|
19 |
if ( !$this->getCon()->isPluginAdmin() ) {
|
@@ -21,7 +21,7 @@ class SetSecAdminPin {
|
|
21 |
}
|
22 |
|
23 |
$this->getOptions()
|
24 |
-
->setOpt( 'admin_access_key', md5( $
|
25 |
$this->getMod()
|
26 |
->setIsMainFeatureEnabled( true )
|
27 |
->saveModOptions();
|
9 |
use ModConsumer;
|
10 |
|
11 |
/**
|
12 |
+
* @param string $pin
|
13 |
* @throws \Exception
|
14 |
*/
|
15 |
+
public function run( string $pin ) {
|
16 |
+
if ( empty( $pin ) ) {
|
17 |
throw new \Exception( 'Attempting to set an empty Security Admin Access Key.' );
|
18 |
}
|
19 |
if ( !$this->getCon()->isPluginAdmin() ) {
|
21 |
}
|
22 |
|
23 |
$this->getOptions()
|
24 |
+
->setOpt( 'admin_access_key', md5( $pin ) );
|
25 |
$this->getMod()
|
26 |
->setIsMainFeatureEnabled( true )
|
27 |
->saveModOptions();
|
src/lib/src/Modules/SecurityAdmin/Lib/WhiteLabel/ApplyLabels.php
CHANGED
@@ -75,7 +75,7 @@ class ApplyLabels {
|
|
75 |
* @return array
|
76 |
*/
|
77 |
public function applyPluginLabels( $pluginLabels ) {
|
78 |
-
/** @var \
|
79 |
$mod = $this->getMod();
|
80 |
|
81 |
$labels = $mod->getWhitelabelOptions();
|
75 |
* @return array
|
76 |
*/
|
77 |
public function applyPluginLabels( $pluginLabels ) {
|
78 |
+
/** @var SecurityAdmin\ModCon $mod */
|
79 |
$mod = $this->getMod();
|
80 |
|
81 |
$labels = $mod->getWhitelabelOptions();
|
src/lib/src/Modules/SecurityAdmin/ModCon.php
ADDED
@@ -0,0 +1,384 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php declare( strict_types=1 );
|
2 |
+
|
3 |
+
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\SecurityAdmin;
|
4 |
+
|
5 |
+
use FernleafSystems\Wordpress\Plugin\Shield;
|
6 |
+
use FernleafSystems\Wordpress\Plugin\Shield\Modules\BaseShield;
|
7 |
+
use FernleafSystems\Wordpress\Services\Services;
|
8 |
+
|
9 |
+
class ModCon extends BaseShield\ModCon {
|
10 |
+
|
11 |
+
const HASH_DELETE = '32f68a60cef40faedbc6af20298c1a1e';
|
12 |
+
|
13 |
+
/**
|
14 |
+
* @var bool
|
15 |
+
*/
|
16 |
+
private $bValidSecAdminRequest;
|
17 |
+
|
18 |
+
/**
|
19 |
+
* @var Lib\WhiteLabel\ApplyLabels
|
20 |
+
*/
|
21 |
+
private $whitelabelCon;
|
22 |
+
|
23 |
+
protected function setupCustomHooks() {
|
24 |
+
add_action( $this->prefix( 'pre_deactivate_plugin' ), [ $this, 'preDeactivatePlugin' ] );
|
25 |
+
}
|
26 |
+
|
27 |
+
public function getWhiteLabelController() :Lib\WhiteLabel\ApplyLabels {
|
28 |
+
if ( !$this->whitelabelCon instanceof Lib\WhiteLabel\ApplyLabels ) {
|
29 |
+
$this->whitelabelCon = ( new Lib\WhiteLabel\ApplyLabels() )
|
30 |
+
->setMod( $this );
|
31 |
+
}
|
32 |
+
return $this->whitelabelCon;
|
33 |
+
}
|
34 |
+
|
35 |
+
/**
|
36 |
+
* @return bool
|
37 |
+
* @throws \Exception
|
38 |
+
*/
|
39 |
+
protected function isReadyToExecute() :bool {
|
40 |
+
return $this->isEnabledSecurityAdmin() && parent::isReadyToExecute();
|
41 |
+
}
|
42 |
+
|
43 |
+
/**
|
44 |
+
* No checking of admin capabilities in-case of infinite loop with
|
45 |
+
* admin access caps check
|
46 |
+
* @return bool
|
47 |
+
*/
|
48 |
+
public function isRegisteredSecAdminUser() {
|
49 |
+
/** @var Options $opts */
|
50 |
+
$opts = $this->getOptions();
|
51 |
+
$sUser = Services::WpUsers()->getCurrentWpUsername();
|
52 |
+
return !empty( $sUser ) && in_array( $sUser, $opts->getSecurityAdminUsers() );
|
53 |
+
}
|
54 |
+
|
55 |
+
protected function preProcessOptions() {
|
56 |
+
/** @var Options $opts */
|
57 |
+
$opts = $this->getOptions();
|
58 |
+
|
59 |
+
if ( $this->isValidSecAdminRequest() ) {
|
60 |
+
$this->setSecurityAdminStatusOnOff( true );
|
61 |
+
}
|
62 |
+
|
63 |
+
// Verify whitelabel images
|
64 |
+
if ( $this->isWlEnabled() ) {
|
65 |
+
foreach ( [ 'wl_menuiconurl', 'wl_dashboardlogourl', 'wl_login2fa_logourl' ] as $key ) {
|
66 |
+
if ( !Services::Data()->isValidWebUrl( $this->buildWlImageUrl( $key ) ) ) {
|
67 |
+
$opts->resetOptToDefault( $key );
|
68 |
+
}
|
69 |
+
}
|
70 |
+
}
|
71 |
+
|
72 |
+
$opts->setOpt( 'sec_admin_users', $this->verifySecAdminUsers( $opts->getSecurityAdminUsers() ) );
|
73 |
+
}
|
74 |
+
|
75 |
+
/**
|
76 |
+
* Ensures that all entries are valid users.
|
77 |
+
* @param string[] $aSecUsers
|
78 |
+
* @return string[]
|
79 |
+
*/
|
80 |
+
private function verifySecAdminUsers( $aSecUsers ) {
|
81 |
+
/** @var Options $opts */
|
82 |
+
$opts = $this->getOptions();
|
83 |
+
$DP = Services::Data();
|
84 |
+
$WPU = Services::WpUsers();
|
85 |
+
|
86 |
+
$aFiltered = [];
|
87 |
+
foreach ( $aSecUsers as $nCurrentKey => $sUsernameOrEmail ) {
|
88 |
+
if ( $DP->validEmail( $sUsernameOrEmail ) ) {
|
89 |
+
$user = $WPU->getUserByEmail( $sUsernameOrEmail );
|
90 |
+
}
|
91 |
+
else {
|
92 |
+
$user = $WPU->getUserByUsername( $sUsernameOrEmail );
|
93 |
+
if ( is_null( $user ) && is_numeric( $sUsernameOrEmail ) ) {
|
94 |
+
$user = $WPU->getUserById( $sUsernameOrEmail );
|
95 |
+
}
|
96 |
+
}
|
97 |
+
|
98 |
+
if ( $user instanceof \WP_User && $user->ID > 0 && $WPU->isUserAdmin( $user ) ) {
|
99 |
+
$aFiltered[] = $user->user_login;
|
100 |
+
}
|
101 |
+
}
|
102 |
+
|
103 |
+
// We now run a bit of a sanity check to ensure that the current user is
|
104 |
+
// not adding users here that aren't themselves without a key to still gain access
|
105 |
+
$oCurrent = $WPU->getCurrentWpUser();
|
106 |
+
if ( !empty( $aFiltered ) && !$opts->hasSecurityPIN() && !in_array( $oCurrent->user_login, $aFiltered ) ) {
|
107 |
+
$aFiltered[] = $oCurrent->user_login;
|
108 |
+
}
|
109 |
+
|
110 |
+
natsort( $aFiltered );
|
111 |
+
return array_unique( $aFiltered );
|
112 |
+
}
|
113 |
+
|
114 |
+
public function getSecAdminTimeout() :int {
|
115 |
+
return (int)$this->getOptions()->getOpt( 'admin_access_timeout' )*MINUTE_IN_SECONDS;
|
116 |
+
}
|
117 |
+
|
118 |
+
/**
|
119 |
+
* Only returns greater than 0 if you have a valid Sec admin session
|
120 |
+
*/
|
121 |
+
public function getSecAdminTimeLeft() :int {
|
122 |
+
$nLeft = 0;
|
123 |
+
if ( $this->getCon()->getModule_Sessions()->getSessionCon()->hasSession() ) {
|
124 |
+
|
125 |
+
$nSecAdminAt = $this->getSession()->getSecAdminAt();
|
126 |
+
if ( $this->isRegisteredSecAdminUser() ) {
|
127 |
+
$nLeft = 0;
|
128 |
+
}
|
129 |
+
elseif ( $nSecAdminAt > 0 ) {
|
130 |
+
$nLeft = $this->getSecAdminTimeout() - ( Services::Request()->ts() - $nSecAdminAt );
|
131 |
+
}
|
132 |
+
}
|
133 |
+
return (int)max( 0, $nLeft );
|
134 |
+
}
|
135 |
+
|
136 |
+
protected function handleModAction( string $action ) {
|
137 |
+
switch ( $action ) {
|
138 |
+
case 'remove_secadmin_confirm':
|
139 |
+
( new Lib\Actions\RemoveSecAdmin() )
|
140 |
+
->setMod( $this )
|
141 |
+
->remove();
|
142 |
+
break;
|
143 |
+
default:
|
144 |
+
break;
|
145 |
+
}
|
146 |
+
}
|
147 |
+
|
148 |
+
public function isSecAdminSessionValid() :bool {
|
149 |
+
return $this->getSecAdminTimeLeft() > 0;
|
150 |
+
}
|
151 |
+
|
152 |
+
public function isEnabledSecurityAdmin() :bool {
|
153 |
+
/** @var Options $opts */
|
154 |
+
$opts = $this->getOptions();
|
155 |
+
return $this->isModOptEnabled() &&
|
156 |
+
( count( $opts->getSecurityAdminUsers() ) > 0 ||
|
157 |
+
( $opts->hasSecurityPIN() && $this->getSecAdminTimeout() > 0 )
|
158 |
+
);
|
159 |
+
}
|
160 |
+
|
161 |
+
/**
|
162 |
+
* @param bool $bSetOn
|
163 |
+
* @return bool
|
164 |
+
*/
|
165 |
+
public function setSecurityAdminStatusOnOff( $bSetOn = false ) {
|
166 |
+
/** @var Shield\Databases\Session\Update $oUpdater */
|
167 |
+
$oUpdater = $this->getDbHandler_Sessions()->getQueryUpdater();
|
168 |
+
return $bSetOn ?
|
169 |
+
$oUpdater->startSecurityAdmin( $this->getSession() )
|
170 |
+
: $oUpdater->terminateSecurityAdmin( $this->getSession() );
|
171 |
+
}
|
172 |
+
|
173 |
+
public function isValidSecAdminRequest() :bool {
|
174 |
+
return $this->isAccessKeyRequest() && $this->testSecAccessKeyRequest();
|
175 |
+
}
|
176 |
+
|
177 |
+
public function testSecAccessKeyRequest() :bool {
|
178 |
+
if ( !isset( $this->bValidSecAdminRequest ) ) {
|
179 |
+
$bValid = false;
|
180 |
+
$sReqKey = Services::Request()->post( 'sec_admin_key' );
|
181 |
+
if ( !empty( $sReqKey ) ) {
|
182 |
+
/** @var Options $opts */
|
183 |
+
$opts = $this->getOptions();
|
184 |
+
$bValid = hash_equals( $opts->getSecurityPIN(), md5( $sReqKey ) );
|
185 |
+
if ( !$bValid ) {
|
186 |
+
$sEscaped = isset( $_POST[ 'sec_admin_key' ] ) ? $_POST[ 'sec_admin_key' ] : '';
|
187 |
+
if ( !empty( $sEscaped ) ) {
|
188 |
+
// Workaround for escaping of passwords
|
189 |
+
$bValid = hash_equals( $opts->getSecurityPIN(), md5( $sEscaped ) );
|
190 |
+
if ( $bValid ) {
|
191 |
+
$opts->setOpt( 'admin_access_key', md5( $sReqKey ) );
|
192 |
+
}
|
193 |
+
}
|
194 |
+
}
|
195 |
+
|
196 |
+
$this->getCon()->fireEvent( $bValid ? 'key_success' : 'key_fail' );
|
197 |
+
}
|
198 |
+
|
199 |
+
$this->bValidSecAdminRequest = $bValid;
|
200 |
+
}
|
201 |
+
return $this->bValidSecAdminRequest;
|
202 |
+
}
|
203 |
+
|
204 |
+
private function isAccessKeyRequest() :bool {
|
205 |
+
return strlen( Services::Request()->post( 'sec_admin_key', '' ) ) > 0;
|
206 |
+
}
|
207 |
+
|
208 |
+
public function verifyAccessKey( string $key ) :bool {
|
209 |
+
/** @var Options $opts */
|
210 |
+
$opts = $this->getOptions();
|
211 |
+
return !empty( $key ) && hash_equals( $opts->getSecurityPIN(), md5( $key ) );
|
212 |
+
}
|
213 |
+
|
214 |
+
public function getWhitelabelOptions() :array {
|
215 |
+
$opts = $this->getOptions();
|
216 |
+
$main = $opts->getOpt( 'wl_pluginnamemain' );
|
217 |
+
$menu = $opts->getOpt( 'wl_namemenu' );
|
218 |
+
if ( empty( $menu ) ) {
|
219 |
+
$menu = $main;
|
220 |
+
}
|
221 |
+
|
222 |
+
return [
|
223 |
+
'name_main' => $main,
|
224 |
+
'name_menu' => $menu,
|
225 |
+
'name_company' => $opts->getOpt( 'wl_companyname' ),
|
226 |
+
'description' => $opts->getOpt( 'wl_description' ),
|
227 |
+
'url_home' => $opts->getOpt( 'wl_homeurl' ),
|
228 |
+
'url_icon' => $this->buildWlImageUrl( 'wl_menuiconurl' ),
|
229 |
+
'url_dashboardlogourl' => $this->buildWlImageUrl( 'wl_dashboardlogourl' ),
|
230 |
+
'url_login2fa_logourl' => $this->buildWlImageUrl( 'wl_login2fa_logourl' ),
|
231 |
+
];
|
232 |
+
}
|
233 |
+
|
234 |
+
/**
|
235 |
+
* We cater for 3 options:
|
236 |
+
* Full URL
|
237 |
+
* Relative path URL: i.e. starts with /
|
238 |
+
* Or Plugin image URL i.e. doesn't start with HTTP or /
|
239 |
+
* @param string $key
|
240 |
+
* @return string
|
241 |
+
*/
|
242 |
+
private function buildWlImageUrl( $key ) {
|
243 |
+
$opts = $this->getOptions();
|
244 |
+
|
245 |
+
$sLogoUrl = $opts->getOpt( $key );
|
246 |
+
if ( empty( $sLogoUrl ) ) {
|
247 |
+
$opts->resetOptToDefault( $key );
|
248 |
+
$sLogoUrl = $opts->getOpt( $key );
|
249 |
+
}
|
250 |
+
if ( !empty( $sLogoUrl ) && !Services::Data()->isValidWebUrl( $sLogoUrl ) && strpos( $sLogoUrl, '/' ) !== 0 ) {
|
251 |
+
$sLogoUrl = $this->getCon()->getPluginUrl_Image( $sLogoUrl );
|
252 |
+
if ( empty( $sLogoUrl ) ) {
|
253 |
+
$opts->resetOptToDefault( $key );
|
254 |
+
$sLogoUrl = $this->getCon()->getPluginUrl_Image( $opts->getOpt( $key ) );
|
255 |
+
}
|
256 |
+
}
|
257 |
+
|
258 |
+
return $sLogoUrl;
|
259 |
+
}
|
260 |
+
|
261 |
+
public function isWlEnabled() :bool {
|
262 |
+
/** @var Options $opts */
|
263 |
+
$opts = $this->getOptions();
|
264 |
+
return $opts->isEnabledWhitelabel() && $this->isPremium();
|
265 |
+
}
|
266 |
+
|
267 |
+
public function isWlHideUpdates() :bool {
|
268 |
+
return $this->isWlEnabled() && $this->getOptions()->isOpt( 'wl_hide_updates', 'Y' );
|
269 |
+
}
|
270 |
+
|
271 |
+
/**
|
272 |
+
* @param string $pin
|
273 |
+
* @return $this
|
274 |
+
* @throws \Exception
|
275 |
+
*/
|
276 |
+
public function setNewPinManually( string $pin ) {
|
277 |
+
if ( empty( $pin ) ) {
|
278 |
+
throw new \Exception( 'Attempting to set an empty Security PIN.' );
|
279 |
+
}
|
280 |
+
if ( !$this->getCon()->isPluginAdmin() ) {
|
281 |
+
throw new \Exception( 'User does not have permission to update the Security PIN.' );
|
282 |
+
}
|
283 |
+
|
284 |
+
$this->setIsMainFeatureEnabled( true );
|
285 |
+
$this->getOptions()->setOpt( 'admin_access_key', md5( $pin ) );
|
286 |
+
return $this->saveModOptions();
|
287 |
+
}
|
288 |
+
|
289 |
+
public function insertCustomJsVars_Admin() {
|
290 |
+
parent::insertCustomJsVars_Admin();
|
291 |
+
|
292 |
+
if ( $this->getSecAdminTimeLeft() > 0 ) {
|
293 |
+
$aInsertData = [
|
294 |
+
'ajax' => [
|
295 |
+
'check' => $this->getSecAdminCheckAjaxData(),
|
296 |
+
],
|
297 |
+
'is_sec_admin' => true, // if $nSecTimeLeft > 0
|
298 |
+
'timeleft' => $this->getSecAdminTimeLeft(), // JS uses milliseconds
|
299 |
+
'strings' => [
|
300 |
+
'confirm' => __( 'Security Admin session has timed-out.', 'wp-simple-firewall' ).' '.__( 'Reload now?', 'wp-simple-firewall' ),
|
301 |
+
'nearly' => __( 'Security Admin session has nearly timed-out.', 'wp-simple-firewall' ),
|
302 |
+
'expired' => __( 'Security Admin session has timed-out.', 'wp-simple-firewall' )
|
303 |
+
]
|
304 |
+
];
|
305 |
+
}
|
306 |
+
else {
|
307 |
+
$aInsertData = [
|
308 |
+
'ajax' => [
|
309 |
+
'req_email_remove' => $this->getAjaxActionData( 'req_email_remove' ),
|
310 |
+
],
|
311 |
+
'strings' => [
|
312 |
+
'are_you_sure' => __( 'Are you sure?', 'wp-simple-firewall' )
|
313 |
+
]
|
314 |
+
];
|
315 |
+
}
|
316 |
+
|
317 |
+
if ( !empty( $aInsertData ) ) {
|
318 |
+
wp_localize_script(
|
319 |
+
$this->prefix( 'plugin' ),
|
320 |
+
'icwp_wpsf_vars_secadmin',
|
321 |
+
$aInsertData
|
322 |
+
);
|
323 |
+
}
|
324 |
+
}
|
325 |
+
|
326 |
+
/**
|
327 |
+
* This is the point where you would want to do any options verification
|
328 |
+
*/
|
329 |
+
protected function doPrePluginOptionsSave() {
|
330 |
+
/** @var Options $opts */
|
331 |
+
$opts = $this->getOptions();
|
332 |
+
|
333 |
+
if ( hash_equals( $opts->getSecurityPIN(), self::HASH_DELETE ) ) {
|
334 |
+
$opts->clearSecurityAdminKey();
|
335 |
+
$this->setSecurityAdminStatusOnOff( false );
|
336 |
+
}
|
337 |
+
|
338 |
+
// Restricting Activate Plugins also means restricting the rest.
|
339 |
+
$aPluginsRestrictions = $opts->getAdminAccessArea_Plugins();
|
340 |
+
if ( in_array( 'activate_plugins', $aPluginsRestrictions ) ) {
|
341 |
+
$opts->setOpt(
|
342 |
+
'admin_access_restrict_plugins',
|
343 |
+
array_unique( array_merge( $aPluginsRestrictions, [
|
344 |
+
'install_plugins',
|
345 |
+
'update_plugins',
|
346 |
+
'delete_plugins'
|
347 |
+
] ) )
|
348 |
+
);
|
349 |
+
}
|
350 |
+
|
351 |
+
// Restricting Switch (Activate) Themes also means restricting the rest.
|
352 |
+
$aThemesRestrictions = $opts->getAdminAccessArea_Themes();
|
353 |
+
if ( in_array( 'switch_themes', $aThemesRestrictions ) && in_array( 'edit_theme_options', $aThemesRestrictions ) ) {
|
354 |
+
$opts->setOpt(
|
355 |
+
'admin_access_restrict_themes',
|
356 |
+
array_unique( array_merge( $aThemesRestrictions, [
|
357 |
+
'install_themes',
|
358 |
+
'update_themes',
|
359 |
+
'delete_themes'
|
360 |
+
] ) )
|
361 |
+
);
|
362 |
+
}
|
363 |
+
|
364 |
+
$aPostRestrictions = $opts->getAdminAccessArea_Posts();
|
365 |
+
if ( in_array( 'edit', $aPostRestrictions ) ) {
|
366 |
+
$opts->setOpt(
|
367 |
+
'admin_access_restrict_posts',
|
368 |
+
array_unique( array_merge( $aPostRestrictions, [ 'create', 'publish', 'delete' ] ) )
|
369 |
+
);
|
370 |
+
}
|
371 |
+
}
|
372 |
+
|
373 |
+
public function preDeactivatePlugin() {
|
374 |
+
if ( !$this->getCon()->isPluginAdmin() ) {
|
375 |
+
Services::WpGeneral()->wpDie(
|
376 |
+
__( "Sorry, this plugin is protected against unauthorised attempts to disable it.", 'wp-simple-firewall' )
|
377 |
+
.'<br />'.sprintf( '<a href="%s">%s</a>',
|
378 |
+
$this->getUrl_AdminPage(),
|
379 |
+
__( "You'll just need to authenticate first and try again.", 'wp-simple-firewall' )
|
380 |
+
)
|
381 |
+
);
|
382 |
+
}
|
383 |
+
}
|
384 |
+
}
|
src/lib/src/Modules/SecurityAdmin/Options.php
CHANGED
@@ -2,10 +2,10 @@
|
|
2 |
|
3 |
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\SecurityAdmin;
|
4 |
|
5 |
-
use FernleafSystems\Wordpress\Plugin\Shield\Modules\
|
6 |
use FernleafSystems\Wordpress\Services\Services;
|
7 |
|
8 |
-
class Options extends
|
9 |
|
10 |
public function clearSecurityAdminKey() :self {
|
11 |
return $this->setOpt( 'admin_access_key', '' );
|
@@ -91,7 +91,7 @@ class Options extends Base\ShieldOptions {
|
|
91 |
return $this->isEnabledWhitelabel() && $this->isOpt( 'wl_hide_updates', 'Y' );
|
92 |
}
|
93 |
|
94 |
-
public function
|
95 |
return $this->isOpt( 'wl_replace_badge_url', 'Y' );
|
96 |
}
|
97 |
}
|
2 |
|
3 |
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\SecurityAdmin;
|
4 |
|
5 |
+
use FernleafSystems\Wordpress\Plugin\Shield\Modules\BaseShield;
|
6 |
use FernleafSystems\Wordpress\Services\Services;
|
7 |
|
8 |
+
class Options extends BaseShield\Options {
|
9 |
|
10 |
public function clearSecurityAdminKey() :self {
|
11 |
return $this->setOpt( 'admin_access_key', '' );
|
91 |
return $this->isEnabledWhitelabel() && $this->isOpt( 'wl_hide_updates', 'Y' );
|
92 |
}
|
93 |
|
94 |
+
public function isReplacePluginBadge() :bool {
|
95 |
return $this->isOpt( 'wl_replace_badge_url', 'Y' );
|
96 |
}
|
97 |
}
|
src/lib/src/Modules/SecurityAdmin/Processor.php
ADDED
@@ -0,0 +1,426 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\SecurityAdmin;
|
4 |
+
|
5 |
+
use FernleafSystems\Wordpress\Plugin\Shield\Modules\BaseShield;
|
6 |
+
use FernleafSystems\Wordpress\Services\Services;
|
7 |
+
|
8 |
+
class Processor extends BaseShield\Processor {
|
9 |
+
|
10 |
+
protected function run() {
|
11 |
+
add_filter( $this->getCon()->prefix( 'is_plugin_admin' ), [ $this, 'adjustUserAdminPermissions' ] );
|
12 |
+
|
13 |
+
/** @var Options $opts */
|
14 |
+
$opts = $this->getOptions();
|
15 |
+
if ( $opts->isEnabledWhitelabel() ) {
|
16 |
+
/** @var ModCon $mod */
|
17 |
+
$mod = $this->getMod();
|
18 |
+
$mod->getWhiteLabelController()->execute();
|
19 |
+
}
|
20 |
+
}
|
21 |
+
|
22 |
+
/**
|
23 |
+
* @param bool $bHasPermission
|
24 |
+
* @return bool
|
25 |
+
*/
|
26 |
+
public function adjustUserAdminPermissions( $bHasPermission = true ) :bool {
|
27 |
+
/** @var ModCon $mod */
|
28 |
+
$mod = $this->getMod();
|
29 |
+
return $bHasPermission &&
|
30 |
+
( $mod->isRegisteredSecAdminUser() || $mod->isSecAdminSessionValid()
|
31 |
+
|| $mod->testSecAccessKeyRequest() );
|
32 |
+
}
|
33 |
+
|
34 |
+
public function onWpInit() {
|
35 |
+
if ( !$this->getCon()->isPluginAdmin() ) {
|
36 |
+
/** @var ModCon $mod */
|
37 |
+
$mod = $this->getMod();
|
38 |
+
/** @var Options $opts */
|
39 |
+
$opts = $this->getOptions();
|
40 |
+
|
41 |
+
if ( !$mod->isUpgrading() && !Services::WpGeneral()->isLoginRequest() ) {
|
42 |
+
add_filter( 'pre_update_option', [ $this, 'blockOptionsSaves' ], 1, 3 );
|
43 |
+
}
|
44 |
+
|
45 |
+
if ( $opts->isSecAdminRestrictUsersEnabled() ) {
|
46 |
+
add_filter( 'editable_roles', [ $this, 'restrictEditableRoles' ], 100, 1 );
|
47 |
+
add_filter( 'user_has_cap', [ $this, 'restrictAdminUserChanges' ], 100, 3 );
|
48 |
+
add_action( 'delete_user', [ $this, 'restrictAdminUserDelete' ], 100, 1 );
|
49 |
+
add_action( 'add_user_role', [ $this, 'restrictAddUserRole' ], 100, 2 );
|
50 |
+
add_action( 'remove_user_role', [ $this, 'restrictRemoveUserRole' ], 100, 2 );
|
51 |
+
add_action( 'set_user_role', [ $this, 'restrictSetUserRole' ], 100, 3 );
|
52 |
+
}
|
53 |
+
|
54 |
+
$aPluginRestrictions = $opts->getAdminAccessArea_Plugins();
|
55 |
+
if ( !empty( $aPluginRestrictions ) ) {
|
56 |
+
add_filter( 'user_has_cap', [ $this, 'disablePluginManipulation' ], 0, 3 );
|
57 |
+
}
|
58 |
+
|
59 |
+
$aThemeRestrictions = $opts->getAdminAccessArea_Themes();
|
60 |
+
if ( !empty( $aThemeRestrictions ) ) {
|
61 |
+
add_filter( 'user_has_cap', [ $this, 'disableThemeManipulation' ], 0, 3 );
|
62 |
+
}
|
63 |
+
|
64 |
+
$aPostRestrictions = $opts->getAdminAccessArea_Posts();
|
65 |
+
if ( !empty( $aPostRestrictions ) ) {
|
66 |
+
add_filter( 'user_has_cap', [ $this, 'disablePostsManipulation' ], 0, 3 );
|
67 |
+
}
|
68 |
+
|
69 |
+
if ( !$this->getCon()->isThisPluginModuleRequest() ) {
|
70 |
+
add_action( 'admin_footer', [ $this, 'printAdminAccessAjaxForm' ] );
|
71 |
+
}
|
72 |
+
}
|
73 |
+
}
|
74 |
+
|
75 |
+
/**
|
76 |
+
* @param int $nUserId
|
77 |
+
* @param string $sRole
|
78 |
+
*/
|
79 |
+
public function restrictAddUserRole( $nUserId, $sRole ) {
|
80 |
+
$oWpUsers = Services::WpUsers();
|
81 |
+
|
82 |
+
if ( $oWpUsers->getCurrentWpUserId() !== $nUserId && strtolower( $sRole ) === 'administrator' ) {
|
83 |
+
$oModUser = $oWpUsers->getUserById( $nUserId );
|
84 |
+
remove_action( 'remove_user_role', [ $this, 'restrictRemoveUserRole' ], 100 );
|
85 |
+
$oModUser->remove_role( 'administrator' );
|
86 |
+
add_action( 'remove_user_role', [ $this, 'restrictRemoveUserRole' ], 100, 2 );
|
87 |
+
}
|
88 |
+
}
|
89 |
+
|
90 |
+
/**
|
91 |
+
* @param int $nUserId
|
92 |
+
* @param string $sRole
|
93 |
+
* @param array $aOldRoles
|
94 |
+
*/
|
95 |
+
public function restrictSetUserRole( $nUserId, $sRole, $aOldRoles = [] ) {
|
96 |
+
$oWpUsers = Services::WpUsers();
|
97 |
+
|
98 |
+
$sRole = strtolower( $sRole );
|
99 |
+
if ( !is_array( $aOldRoles ) ) {
|
100 |
+
$aOldRoles = [];
|
101 |
+
}
|
102 |
+
|
103 |
+
if ( $oWpUsers->getCurrentWpUserId() !== $nUserId ) {
|
104 |
+
$bNewRoleIsAdmin = $sRole == 'administrator';
|
105 |
+
|
106 |
+
// 1. Setting administrator role where it doesn't previously exist
|
107 |
+
if ( $bNewRoleIsAdmin && !in_array( 'administrator', $aOldRoles ) ) {
|
108 |
+
$bRevert = true;
|
109 |
+
}
|
110 |
+
// 2. Setting non-administrator role when previous roles included administrator
|
111 |
+
elseif ( !$b
|