Shield Security for WordPress - Version 10.2.0

Version Description

Download this release

Release Info

Developer paultgoodchild
Plugin Icon 128x128 Shield Security for WordPress
Version 10.2.0
Comparing to
See all releases

Code changes from version 10.1.6 to 10.2.0

Files changed (281) hide show
  1. cl.json +91 -0
  2. icwp-wpsf.php +2 -2
  3. init.php +4 -5
  4. plugin-spec.php +140 -19
  5. readme.txt +1 -1
  6. resources/css/bootstrap4.min.css +0 -7
  7. resources/css/{mainwp.css → mainwp-extension.css} +0 -0
  8. resources/css/plugin.css +1 -1
  9. resources/images/shield/background-blob.svg +18 -0
  10. resources/images/shield/dash-background.jpg +0 -0
  11. resources/js/bootstrap4.bundle.min.js +0 -7
  12. resources/js/bootstrap4.min.js +0 -7
  13. resources/js/global-plugin.js +21 -3
  14. resources/js/plugin.js +64 -61
  15. resources/js/{shield-scans.js → shield/scans.js} +0 -0
  16. resources/js/{shield-tables.js → shield/tables.js} +0 -0
  17. resources/js/{shield-u2f-admin.js → shield/u2f-admin.js} +1 -4
  18. resources/js/{shield-userprofile.js → shield/userprofile.js} +1 -1
  19. src/config/feature-hack_protect.php +1 -1
  20. src/config/feature-headers.php +1 -69
  21. src/config/feature-login_protect.php +1 -4
  22. src/config/feature-user_management.php +1 -1
  23. src/features/admin_access_restriction.php +0 -396
  24. src/features/audit_trail.php +0 -111
  25. src/features/autoupdates.php +0 -16
  26. src/features/base.php +0 -1599
  27. src/features/base_wpsf.php +0 -262
  28. src/features/comments_filter.php +0 -112
  29. src/features/comms.php +0 -19
  30. src/features/email.php +0 -11
  31. src/features/events.php +0 -34
  32. src/features/firewall.php +0 -68
  33. src/features/hack_protect.php +0 -326
  34. src/features/headers.php +0 -98
  35. src/features/insights.php +0 -226
  36. src/features/ips.php +0 -155
  37. src/features/license.php +0 -87
  38. src/features/lockdown.php +0 -39
  39. src/features/login_protect.php +0 -295
  40. src/features/plugin.php +0 -553
  41. src/features/reporting.php +0 -46
  42. src/features/sessions.php +0 -48
  43. src/features/traffic.php +0 -51
  44. src/features/user_management.php +0 -138
  45. src/lib/src/Controller/Admin/AdminBarMenu.php +50 -0
  46. src/lib/src/Controller/Admin/DashboardWidget.php +35 -0
  47. src/lib/src/Controller/Admin/MainAdminMenu.php +87 -0
  48. src/lib/src/Controller/Ajax/Init.php +55 -0
  49. src/lib/src/Controller/Ajax/Response.php +28 -0
  50. src/lib/src/Controller/Assets/Enqueue.php +174 -0
  51. src/lib/src/Controller/Assets/Urls.php +64 -0
  52. src/lib/src/Controller/Config/Ops/LoadConfig.php +1 -1
  53. src/lib/src/Controller/Controller.php +196 -505
  54. src/lib/src/Controller/Utilities/Upgrade.php +29 -10
  55. src/lib/src/Crons/StandardCron.php +1 -1
  56. src/lib/src/Databases/Base/EntryVoConsumer.php +3 -3
  57. src/lib/src/Databases/Base/Handler.php +7 -11
  58. src/lib/src/Databases/Base/Insert.php +8 -8
  59. src/lib/src/Databases/IPs/CommonFilters.php +8 -4
  60. src/lib/src/Modules/AuditTrail/Options.php +0 -71
  61. src/lib/src/Modules/Autoupdates/Insights/OverviewCards.php +3 -3
  62. src/lib/src/Modules/Autoupdates/Processor.php +1 -1
  63. src/lib/src/Modules/Base/AdminNotices.php +1 -1
  64. src/lib/src/Modules/Base/AjaxHandlerBase.php +0 -110
  65. src/lib/src/Modules/Base/AjaxHandlerShield.php +0 -82
  66. src/lib/src/Modules/Base/BaseProcessor.php +1 -8
  67. src/lib/src/Modules/Base/BaseReporting.php +0 -54
  68. src/lib/src/Modules/Base/ModCon.php +53 -99
  69. src/lib/src/Modules/Base/Options.php +53 -68
  70. src/lib/src/Modules/Base/Processor.php +1 -28
  71. src/lib/src/Modules/Base/ShieldOptions.php +0 -34
  72. src/lib/src/Modules/Base/ShieldUI.php +0 -70
  73. src/lib/src/Modules/Base/UI.php +17 -16
  74. src/lib/src/Modules/BaseShield/ModCon.php +1 -19
  75. src/lib/src/Modules/BaseShield/ShieldProcessor.php +1 -20
  76. src/lib/src/Modules/CommentsFilter/Scan/Scanner.php +2 -2
  77. src/lib/src/Modules/Events/Lib/EventsService.php +0 -8
  78. src/lib/src/Modules/Events/Lib/Reports/KeyStats.php +1 -1
  79. src/lib/src/Modules/Events/Lib/Reports/ScanRepairs.php +1 -1
  80. src/lib/src/Modules/GeoIp/Lookup.php +12 -12
  81. src/lib/src/Modules/HackGuard/AjaxHandler.php +50 -50
  82. src/lib/src/Modules/HackGuard/Debug.php +19 -0
  83. src/lib/src/Modules/HackGuard/Lib/FileLocker/FileLockerController.php +2 -2
  84. src/lib/src/Modules/HackGuard/Lib/FileLocker/Ops/AssessLocks.php +1 -1
  85. src/lib/src/Modules/HackGuard/Lib/FileLocker/Ops/ReadOriginalFileContent.php +6 -6
  86. src/lib/src/Modules/HackGuard/Lib/FileLocker/Ops/Verify.php +4 -4
  87. src/lib/src/Modules/HackGuard/Lib/Snapshots/Build/BuildHashesFromDir.php +1 -1
  88. src/lib/src/Modules/HackGuard/Lib/Snapshots/Store.php +2 -2
  89. src/lib/src/Modules/HackGuard/Lib/Snapshots/StoreAction/Build.php +19 -20
  90. src/lib/src/Modules/HackGuard/Lib/Snapshots/StoreAction/CleanAll.php +1 -1
  91. src/lib/src/Modules/HackGuard/Lib/Snapshots/StoreAction/Delete.php +4 -4
  92. src/lib/src/Modules/HackGuard/Lib/Snapshots/StoreAction/ScheduleBuildAll.php +11 -14
  93. src/lib/src/Modules/HackGuard/Lib/Snapshots/StoreAction/TouchAll.php +5 -5
  94. src/lib/src/Modules/HackGuard/ModCon.php +1 -23
  95. src/lib/src/Modules/HackGuard/Options.php +1 -1
  96. src/lib/src/Modules/HackGuard/Scan/Controller/Base.php +17 -17
  97. src/lib/src/Modules/HackGuard/Scan/Controller/BaseForAssets.php +6 -6
  98. src/lib/src/Modules/HackGuard/Scan/Controller/Mal.php +9 -9
  99. src/lib/src/Modules/HackGuard/Scan/Controller/Ptg.php +5 -7
  100. src/lib/src/Modules/HackGuard/Scan/Controller/Ufc.php +1 -1
  101. src/lib/src/Modules/HackGuard/Scan/Controller/Wcf.php +3 -4
  102. src/lib/src/Modules/HackGuard/Scan/Queue/Build/QueueBuilder.php +5 -5
  103. src/lib/src/Modules/HackGuard/Scan/Queue/QueueProcessor.php +2 -2
  104. src/lib/src/Modules/HackGuard/Scan/ScansController.php +2 -4
  105. src/lib/src/Modules/HackGuard/Scan/Utilities/PtgAddReinstallLinks.php +25 -29
  106. src/lib/src/Modules/HackGuard/Scan/Utilities/WpvAddPluginRows.php +19 -7
  107. src/lib/src/Modules/HackGuard/UI.php +1 -4
  108. src/lib/src/Modules/HackGuard/Upgrade.php +12 -1
  109. src/lib/src/Modules/Headers/Insights/OverviewCards.php +1 -1
  110. src/lib/src/Modules/Headers/Options.php +16 -29
  111. src/lib/src/Modules/Headers/Processor.php +5 -28
  112. src/lib/src/Modules/Headers/Strings.php +7 -2
  113. src/lib/src/Modules/IPs/AjaxHandler.php +2 -2
  114. src/lib/src/Modules/IPs/BotTrack/Base.php +1 -4
  115. src/lib/src/Modules/IPs/BotTrack/TrackFakeWebCrawler.php +1 -1
  116. src/lib/src/Modules/IPs/BotTrack/TrackLoginFailed.php +10 -13
  117. src/lib/src/Modules/IPs/BotTrack/TrackLoginInvalid.php +16 -11
  118. src/lib/src/Modules/IPs/Components/QueryIpBlock.php +7 -15
  119. src/lib/src/Modules/IPs/Components/UnblockIpByFlag.php +4 -4
  120. src/lib/src/Modules/IPs/Lib/BlockRequest.php +3 -10
  121. src/lib/src/Modules/IPs/Lib/Ops/LookupIpOnList.php +28 -28
  122. src/lib/src/Modules/IPs/WpCli/Add.php +2 -2
  123. src/lib/src/Modules/Insights/ModCon.php +89 -178
  124. src/lib/src/Modules/Integrations/Lib/MainWP/Client/Actions/Sync.php +1 -1
  125. src/lib/src/Modules/Integrations/Lib/MainWP/Server/Data/ClientPluginStatus.php +1 -1
  126. src/lib/src/Modules/Integrations/Lib/MainWP/Server/Init.php +1 -1
  127. src/lib/src/Modules/Integrations/Lib/MainWP/Server/UI/ExtensionSettingsPage.php +10 -23
  128. src/lib/src/Modules/License/AjaxHandler.php +13 -13
  129. src/lib/src/Modules/License/Lib/LicenseHandler.php +1 -1
  130. src/lib/src/Modules/License/Lib/PluginNameSuffix.php +29 -0
  131. src/lib/src/Modules/License/Lib/WpHashes/ApiTokenManager.php +1 -1
  132. src/lib/src/Modules/License/Processor.php +6 -0
  133. src/lib/src/Modules/License/WpCli/License.php +2 -2
  134. src/lib/src/Modules/LoginGuard/AjaxHandler.php +14 -12
  135. src/lib/src/Modules/LoginGuard/Lib/AntiBot/FormProviders/BaseFormProvider.php +7 -7
  136. src/lib/src/Modules/LoginGuard/Lib/AntiBot/FormProviders/EasyDigitalDownloads.php +2 -2
  137. src/lib/src/Modules/LoginGuard/Lib/AntiBot/FormProviders/LearnPress.php +4 -4
  138. src/lib/src/Modules/LoginGuard/Lib/AntiBot/FormProviders/MemberPress.php +6 -6
  139. src/lib/src/Modules/LoginGuard/Lib/AntiBot/FormProviders/PaidMemberSubscriptions.php +2 -2
  140. src/lib/src/Modules/LoginGuard/Lib/AntiBot/FormProviders/ProfileBuilder.php +5 -5
  141. src/lib/src/Modules/LoginGuard/Lib/AntiBot/FormProviders/UltimateMember.php +6 -6
  142. src/lib/src/Modules/LoginGuard/Lib/AntiBot/FormProviders/UserRegistration.php +5 -5
  143. src/lib/src/Modules/LoginGuard/Lib/AntiBot/FormProviders/WPMembers.php +3 -3
  144. src/lib/src/Modules/LoginGuard/Lib/AntiBot/FormProviders/WooCommerce.php +6 -6
  145. src/lib/src/Modules/LoginGuard/Lib/AntiBot/FormProviders/WordPress.php +6 -6
  146. src/lib/src/Modules/LoginGuard/Lib/AntiBot/ProtectionProviders/GaspJs.php +6 -6
  147. src/lib/src/Modules/LoginGuard/Lib/AntiBot/ProtectionProviders/GoogleRecaptcha.php +2 -2
  148. src/lib/src/Modules/LoginGuard/Lib/TwoFactor/LoginIntentPage.php +7 -7
  149. src/lib/src/Modules/LoginGuard/Lib/TwoFactor/MfaController.php +0 -8
  150. src/lib/src/Modules/LoginGuard/Lib/TwoFactor/Provider/Backup.php +0 -6
  151. src/lib/src/Modules/LoginGuard/Lib/TwoFactor/Provider/BaseProvider.php +4 -21
  152. src/lib/src/Modules/LoginGuard/Lib/TwoFactor/Provider/Email.php +3 -20
  153. src/lib/src/Modules/LoginGuard/Lib/TwoFactor/Provider/GoogleAuth.php +5 -14
  154. src/lib/src/Modules/LoginGuard/Lib/TwoFactor/Provider/U2F.php +55 -86
  155. src/lib/src/Modules/LoginGuard/Lib/TwoFactor/Provider/Yubikey.php +16 -27
  156. src/lib/src/Modules/LoginGuard/ModCon.php +20 -8
  157. src/lib/src/Modules/LoginGuard/UI.php +16 -4
  158. src/lib/src/Modules/ModConsumer.php +8 -8
  159. src/lib/src/Modules/Plugin/AdminNotices.php +5 -5
  160. src/lib/src/Modules/Plugin/AjaxHandler.php +9 -9
  161. src/lib/src/Modules/Plugin/Components/PluginBadge.php +2 -2
  162. src/lib/src/Modules/Plugin/Components/SiteGroundPluginCompatibility.php +11 -14
  163. src/lib/src/Modules/Plugin/Lib/Debug/Collate.php +9 -0
  164. src/lib/src/Modules/Plugin/Lib/ImportExport/Export.php +1 -1
  165. src/lib/src/Modules/Plugin/Lib/ImportExport/Import.php +4 -4
  166. src/lib/src/Modules/Plugin/Lib/PluginTelemetry.php +7 -5
  167. src/lib/src/Modules/Plugin/Lib/TourManager.php +5 -5
  168. src/lib/src/Modules/Plugin/ModCon.php +39 -24
  169. src/lib/src/Modules/Plugin/WpCli/Import.php +3 -3
  170. src/lib/src/Modules/SecurityAdmin/Lib/WhiteLabel/ApplyLabels.php +13 -13
  171. src/lib/src/Modules/SecurityAdmin/ModCon.php +11 -11
  172. src/lib/src/Modules/SecurityAdmin/WpCli/Pin.php +2 -2
  173. src/lib/src/Modules/Sessions/Processor.php +0 -64
  174. src/lib/src/Modules/Traffic/Lib/Limit/Limiter.php +5 -5
  175. src/lib/src/Modules/UserManagement/Lib/Password/UserPasswordHandler.php +2 -16
  176. src/lib/src/Modules/UserManagement/ModCon.php +0 -8
  177. src/lib/src/Modules/UserManagement/Options.php +0 -8
  178. src/lib/src/Scans/Base/BaseScanActionVO.php +4 -4
  179. src/lib/src/Scans/Base/Files/BaseFileMapScan.php +6 -6
  180. src/lib/src/Scans/Base/Files/BaseFileScanner.php +2 -2
  181. src/lib/src/Scans/Base/Files/BaseScanFromFileMap.php +9 -10
  182. src/lib/src/Scans/Base/Table/BaseFileEntryFormatter.php +3 -3
  183. src/lib/src/Scans/Base/Utilities/ItemActionHandler.php +7 -7
  184. src/lib/src/Scans/Base/Utilities/ItemActionHandlerAssets.php +4 -4
  185. src/lib/src/Scans/Helpers/BuildHashesFromDir.php +1 -1
  186. src/lib/src/Scans/Helpers/WpCoreFile.php +1 -1
  187. src/lib/src/Scans/Mal/BuildFileMap.php +27 -31
  188. src/lib/src/Scans/Mal/FileScanner.php +115 -98
  189. src/lib/src/Scans/Mal/Scan.php +9 -8
  190. src/lib/src/Scans/Mal/ScanActionVO.php +2 -1
  191. src/lib/src/Scans/Mal/Table/EntryFormatter.php +8 -8
  192. src/lib/src/Scans/Mal/Utilities/FalsePositiveQuery.php +27 -31
  193. src/lib/src/Scans/Mal/Utilities/FalsePositiveReporter.php +1 -1
  194. src/lib/src/Scans/Mal/Utilities/Patterns.php +21 -21
  195. src/lib/src/Scans/Mal/Utilities/Repair.php +37 -37
  196. src/lib/src/Scans/Ptg/BuildFileMap.php +3 -3
  197. src/lib/src/Scans/Ptg/FileScanner.php +22 -22
  198. src/lib/src/Scans/Ptg/Utilities/ItemActionHandler.php +21 -21
  199. src/lib/src/Scans/Ptg/Utilities/Repair.php +10 -10
  200. src/lib/src/Scans/Ufc/BuildFileMap.php +2 -2
  201. src/lib/src/Scans/Ufc/FileScanner.php +10 -10
  202. src/lib/src/Scans/Wcf/FileScanner.php +12 -12
  203. src/lib/src/Scans/Wpv/WpVulnDb/WpVulnVO.php +28 -4
  204. src/lib/src/Tables/Build/Traffic.php +107 -70
  205. src/lib/src/Tables/Render/WpListTable/Base.php +4 -4
  206. src/lib/src/Tables/Render/WpListTable/ScanWpv.php +9 -9
  207. src/lib/src/Utilities/ReCaptcha/TestRequest.php +2 -2
  208. src/lib/src/Utilities/Time/WorldTimeApi.php +33 -0
  209. src/lib/vendor/autoload.php +1 -1
  210. src/lib/vendor/composer/ClassLoader.php +34 -2
  211. src/lib/vendor/composer/autoload_classmap.php +15 -59
  212. src/lib/vendor/composer/autoload_real.php +11 -9
  213. src/lib/vendor/composer/autoload_static.php +22 -66
  214. src/lib/vendor/composer/platform_check.php +26 -0
  215. src/lib/vendor/fernleafsystems/utilities/src/Data/Adapter/DynamicProperties.php +74 -0
  216. src/lib/vendor/fernleafsystems/utilities/src/Data/Adapter/DynamicPropertiesClass.php +13 -0
  217. src/lib/vendor/fernleafsystems/utilities/src/Data/Adapter/StdClassAdapter.php +8 -7
  218. src/lib/vendor/fernleafsystems/utilities/src/Data/CaptureOutput.php +13 -0
  219. src/lib/vendor/fernleafsystems/utilities/src/Logic/ExecOnce.php +31 -0
  220. src/lib/vendor/fernleafsystems/wordpress-services/src/Core/AdminNotices.php +3 -3
  221. src/lib/vendor/fernleafsystems/wordpress-services/src/Core/CoreFileHashes.php +31 -34
  222. src/lib/vendor/fernleafsystems/wordpress-services/src/Core/Fs.php +22 -37
  223. src/lib/vendor/fernleafsystems/wordpress-services/src/Core/General.php +3 -3
  224. src/lib/vendor/fernleafsystems/wordpress-services/src/Core/Nonce.php +18 -35
  225. src/lib/vendor/fernleafsystems/wordpress-services/src/Core/Request.php +116 -134
  226. src/lib/vendor/fernleafsystems/wordpress-services/src/Core/Response.php +32 -32
  227. src/lib/vendor/fernleafsystems/wordpress-services/src/Core/Themes.php +72 -96
  228. src/lib/vendor/fernleafsystems/wordpress-services/src/Core/Users.php +2 -2
  229. src/lib/vendor/fernleafsystems/wordpress-services/src/Core/VOs/WpBaseVo.php +3 -6
  230. src/lib/vendor/fernleafsystems/wordpress-services/src/Core/VOs/WpPluginVo.php +12 -15
  231. src/lib/vendor/fernleafsystems/wordpress-services/src/Core/VOs/WpThemeVo.php +16 -22
  232. src/lib/vendor/fernleafsystems/wordpress-services/src/Utilities/Data.php +19 -23
  233. src/lib/vendor/fernleafsystems/wordpress-services/src/Utilities/File/GetFileAsArray.php +8 -8
  234. src/lib/vendor/fernleafsystems/wordpress-services/src/Utilities/File/LocateStrInFile.php +59 -70
  235. src/lib/vendor/fernleafsystems/wordpress-services/src/Utilities/Net/IpIdentify.php +4 -4
  236. src/lib/vendor/fernleafsystems/wordpress-services/src/Utilities/Obfuscate.php +6 -6
  237. src/lib/vendor/fernleafsystems/wordpress-services/src/Utilities/Options/Transient.php +24 -24
  238. src/lib/vendor/fernleafsystems/wordpress-services/src/Utilities/Render.php +6 -6
  239. src/processors/admin_access_restriction.php +0 -450
  240. src/processors/audit_trail.php +0 -74
  241. src/processors/autoupdates.php +0 -456
  242. src/processors/comments_filter.php +0 -75
  243. src/processors/commentsfilter_botspam.php +0 -116
  244. src/processors/commentsfilter_googlerecaptcha.php +0 -35
  245. src/processors/email.php +0 -218
  246. src/processors/events.php +0 -94
  247. src/processors/firewall.php +0 -430
  248. src/processors/hack_protect.php +0 -26
  249. src/processors/hackprotect_integrity.php +0 -136
  250. src/processors/hackprotect_scan_apc.php +0 -11
  251. src/processors/hackprotect_scan_base.php +0 -46
  252. src/processors/hackprotect_scan_mal.php +0 -11
  253. src/processors/hackprotect_scan_ptg.php +0 -25
  254. src/processors/hackprotect_scan_ufc.php +0 -11
  255. src/processors/hackprotect_scan_wcf.php +0 -11
  256. src/processors/hackprotect_scan_wpv.php +0 -74
  257. src/processors/hackprotect_scanner.php +0 -89
  258. src/processors/headers.php +0 -230
  259. src/processors/lockdown.php +0 -136
  260. src/processors/login_protect.php +0 -52
  261. src/processors/loginprotect_wplogin.php +0 -236
  262. src/processors/plugin.php +0 -68
  263. src/processors/plugin_tracking.php +0 -29
  264. src/processors/sessions.php +0 -191
  265. src/processors/traffic.php +0 -24
  266. src/processors/user_management.php +0 -265
  267. src/processors/usermanagement_passwords.php +0 -333
  268. src/processors/usermanagement_sessions.php +0 -186
  269. src/wizards/base.php +16 -16
  270. src/wizards/plugin.php +15 -15
  271. templates/php/snippets/module-help-firewall.php +1 -1
  272. templates/php/snippets/plugin-vulnerability.php +0 -25
  273. templates/twig/components/options_form/main.twig +2 -2
  274. templates/twig/snippets/js/freshdesk_chatbot.twig +22 -0
  275. templates/twig/snippets/plugin_vulnerability.twig +21 -0
  276. templates/twig/wpadmin_pages/insights/base.twig +1 -1
  277. templates/twig/wpadmin_pages/insights/docs/index.twig +4 -2
  278. templates/twig/wpadmin_pages/insights/importexport/index.twig +5 -2
  279. templates/twig/wpadmin_pages/insights/ips/index.twig +7 -4
  280. templates/twig/wpadmin_pages/insights/ips/ip_analyse/ip_info.twig +8 -4
  281. templates/twig/wpadmin_pages/security_admin/index.twig +3 -2
cl.json CHANGED
@@ -1,4 +1,95 @@
1
  {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2
  "10.1": {
3
  "version": "10.1",
4
  "released_at": 1605606920,
1
  {
2
+ "10.2": {
3
+ "version": "10.2",
4
+ "released_at": 1613037000,
5
+ "hrefs": {
6
+ "release": "https://shsec.io/shieldrelease102",
7
+ "upgrade": "https://shsec.io/shieldupgradeguide102"
8
+ },
9
+ "href": "https://shsec.io/",
10
+ "title": "Removal of simple Content Security Policy settings and bugfixes",
11
+ "description": [
12
+ "We've decided to remove our simple Content Security Policy options as this feature is too complex.",
13
+ "We've also fixed a number of bugs and optimised how Shield loads and stores options and configurations."
14
+ ],
15
+ "items": [
16
+ {
17
+ "type": "new",
18
+ "pro_only": false,
19
+ "title": "Removed Content Security Policy Settings",
20
+ "description": [
21
+ "Due to the complexity of CSP and the superficial nature of our CSP implementation, we've decided to remove these options.",
22
+ "We explore the issue in full detail in our blog post on this topic."
23
+ ],
24
+ "href": "https://shsec.io/jb"
25
+ },
26
+ {
27
+ "type": "new",
28
+ "title": "Invalid user login tracking covers empty usernames.",
29
+ "description": [
30
+ "When tracking for bots logging in user invalid usernames (i.e. that don't exist) it'll also trigger an offense on empty usernames."
31
+ ]
32
+ },
33
+ {
34
+ "type": "improved",
35
+ "title": "Deleting Malware files doesn't initiate a new scan.",
36
+ "description": [
37
+ "This addresses a reported UX issue where bulk malware deletion isn't yet available and so instead of a full re-scan, the page just reloads."
38
+ ]
39
+ },
40
+ {
41
+ "type": "improved",
42
+ "title": "Malware scanners are more efficient.",
43
+ "description": [
44
+ "Malware scanning is involved - every PHP file has to be read and then searched using a large set of patterns.",
45
+ "So it takes time. Hopefully these tweaks will optimise this process a little and lead to faster scans."
46
+ ]
47
+ },
48
+ {
49
+ "type": "improved",
50
+ "title": "Add IP status to information in the traffic viewer.",
51
+ "description": [
52
+ "The traffic table will now display many offenses or whether the IP address is blocked."
53
+ ]
54
+ },
55
+ {
56
+ "type": "improved",
57
+ "title": "Upgrade Bootstrap Library to latest 4.6.0",
58
+ "description": [
59
+ "Asset enqueuing has been refactored and optimised and also now loading Bootstrap assets from CDNJS."
60
+ ]
61
+ },
62
+ {
63
+ "type": "improved",
64
+ "title": "Significant code cleanup.",
65
+ "description": []
66
+ },
67
+ {
68
+ "type": "improved",
69
+ "title": "Added cleanup code to remove stale entries in the WP Options table.",
70
+ "description": []
71
+ },
72
+ {
73
+ "type": "improved",
74
+ "title": "Added detection of server clock inconsistencies which break Google Authenticator.",
75
+ "description": []
76
+ },
77
+ {
78
+ "type": "fixed",
79
+ "title": "U2F/Yubikey Removal Bug",
80
+ "description": [
81
+ "A javascript issue prevented removal of U2F keys from user profiles."
82
+ ]
83
+ },
84
+ {
85
+ "type": "fixed",
86
+ "title": "FileLocker would fail to load file contents if it exceeded 64KB.",
87
+ "description": [
88
+ "We upgraded the database table definition to allow for much larger files."
89
+ ]
90
+ }
91
+ ]
92
+ },
93
  "10.1": {
94
  "version": "10.1",
95
  "released_at": 1605606920,
icwp-wpsf.php CHANGED
@@ -3,7 +3,7 @@
3
  * Plugin Name: Shield Security
4
  * Plugin URI: https://shsec.io/2f
5
  * Description: Powerful, Easy-To-Use #1 Rated WordPress Security System
6
- * Version: 10.1.6
7
  * Text Domain: wp-simple-firewall
8
  * Domain Path: /languages
9
  * Author: Shield Security
@@ -39,7 +39,7 @@ elseif ( @is_file( dirname( __FILE__ ).'/src/lib/vendor/autoload.php' ) ) {
39
 
40
  add_action( 'plugins_loaded', 'icwp_wpsf_init', 1 ); // use 0 for extensions to ensure hooks have been added.
41
  function icwp_wpsf_init() {
42
- $sRootFile = __FILE__;
43
  require_once( dirname( __FILE__ ).'/init.php' );
44
  }
45
 
3
  * Plugin Name: Shield Security
4
  * Plugin URI: https://shsec.io/2f
5
  * Description: Powerful, Easy-To-Use #1 Rated WordPress Security System
6
+ * Version: 10.2.0
7
  * Text Domain: wp-simple-firewall
8
  * Domain Path: /languages
9
  * Author: Shield Security
39
 
40
  add_action( 'plugins_loaded', 'icwp_wpsf_init', 1 ); // use 0 for extensions to ensure hooks have been added.
41
  function icwp_wpsf_init() {
42
+ $rootFile = __FILE__;
43
  require_once( dirname( __FILE__ ).'/init.php' );
44
  }
45
 
init.php CHANGED
@@ -2,9 +2,8 @@
2
 
3
  use FernleafSystems\Wordpress\Plugin\Shield;
4
 
5
- /** @var string $sRootFile */
6
  global $oICWP_Wpsf;
7
-
8
  if ( isset( $oICWP_Wpsf ) ) {
9
  error_log( 'Attempting to load the Shield Plugin twice?' );
10
  return;
@@ -49,13 +48,13 @@ class ICWP_WPSF_Shield_Security {
49
  }
50
 
51
  try {
52
- $oICWP_Wpsf_Controller = Shield\Controller\Controller::GetInstance( $sRootFile );
53
  $oICWP_Wpsf = ICWP_WPSF_Shield_Security::GetInstance( $oICWP_Wpsf_Controller );
54
  }
55
- catch ( \Exception $oE ) {
56
  if ( is_admin() ) {
57
  error_log( 'Perhaps due to a failed upgrade, the Shield plugin failed to load certain component(s) - you should remove the plugin and reinstall.' );
58
- error_log( $oE->getMessage() );
59
  }
60
  }
61
 
2
 
3
  use FernleafSystems\Wordpress\Plugin\Shield;
4
 
5
+ /** @var string $rootFile */
6
  global $oICWP_Wpsf;
 
7
  if ( isset( $oICWP_Wpsf ) ) {
8
  error_log( 'Attempting to load the Shield Plugin twice?' );
9
  return;
48
  }
49
 
50
  try {
51
+ $oICWP_Wpsf_Controller = Shield\Controller\Controller::GetInstance( $rootFile );
52
  $oICWP_Wpsf = ICWP_WPSF_Shield_Security::GetInstance( $oICWP_Wpsf_Controller );
53
  }
54
+ catch ( \Exception $e ) {
55
  if ( is_admin() ) {
56
  error_log( 'Perhaps due to a failed upgrade, the Shield plugin failed to load certain component(s) - you should remove the plugin and reinstall.' );
57
+ error_log( $e->getMessage() );
58
  }
59
  }
60
 
plugin-spec.php CHANGED
@@ -1,8 +1,8 @@
1
  {
2
  "properties": {
3
- "version": "10.1.6",
4
- "release_timestamp": 1611222790,
5
- "build": "202101.2101",
6
  "slug_parent": "icwp",
7
  "slug_plugin": "wpsf",
8
  "human_name": "Shield Security",
@@ -23,10 +23,6 @@
23
  "wordpress": "3.5.2"
24
  },
25
  "upgrade_reqs": {
26
- "7.0": {
27
- "php": "5.4",
28
- "wp": "3.5.2"
29
- },
30
  "10.0": {
31
  "php": "7.0",
32
  "wp": "3.5.2"
@@ -48,23 +44,17 @@
48
  "global-plugin"
49
  ],
50
  "js": [
51
- "jquery",
52
  "global-plugin"
53
  ]
54
  },
55
  "plugin_admin": {
56
  "css": [
57
- "bootstrap4.min",
58
  "bootstrap-select.min",
59
- "global-plugin",
60
  "plugin",
61
  "featherlight"
62
  ],
63
  "js": [
64
- "bootstrap4.bundle.min",
65
  "bootstrap-select.min",
66
- "jquery",
67
- "global-plugin",
68
  "plugin",
69
  "base64.min",
70
  "lz-string.min",
@@ -73,7 +63,141 @@
73
  ]
74
  },
75
  "frontend": {
76
- "css": null
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
77
  }
78
  },
79
  "menu": {
@@ -81,7 +205,6 @@
81
  "title": "Shield Security",
82
  "top_level": true,
83
  "do_submenu_fix": true,
84
- "callback": "onDisplayTopMenu",
85
  "icon_image": "pluginlogo_16x16.png",
86
  "has_submenu": true
87
  },
@@ -109,13 +232,11 @@
109
  }
110
  ],
111
  "version_upgrades": [
112
- "9.0.0",
113
- "9.0.3",
114
- "9.0.5",
115
  "9.1.1",
116
  "9.2.0",
117
  "9.2.2",
118
- "10.1.0"
 
119
  ],
120
  "action_links": {
121
  "remove": null,
1
  {
2
  "properties": {
3
+ "version": "10.2.0",
4
+ "release_timestamp": 1613037000,
5
+ "build": "202102.1107",
6
  "slug_parent": "icwp",
7
  "slug_plugin": "wpsf",
8
  "human_name": "Shield Security",
23
  "wordpress": "3.5.2"
24
  },
25
  "upgrade_reqs": {
 
 
 
 
26
  "10.0": {
27
  "php": "7.0",
28
  "wp": "3.5.2"
44
  "global-plugin"
45
  ],
46
  "js": [
 
47
  "global-plugin"
48
  ]
49
  },
50
  "plugin_admin": {
51
  "css": [
 
52
  "bootstrap-select.min",
 
53
  "plugin",
54
  "featherlight"
55
  ],
56
  "js": [
 
57
  "bootstrap-select.min",
 
 
58
  "plugin",
59
  "base64.min",
60
  "lz-string.min",
63
  ]
64
  },
65
  "frontend": {
66
+ "css": [],
67
+ "js": []
68
+ },
69
+ "register": {
70
+ "css": {
71
+ "bootstrap4.min": {
72
+ "url": "https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.6.0/css/bootstrap.min.css"
73
+ },
74
+ "bootstrap-select.min": {
75
+ "deps": [
76
+ "bootstrap4.min"
77
+ ]
78
+ },
79
+ "bootstrap-datepicker": {
80
+ "url": "https://cdnjs.cloudflare.com/ajax/libs/bootstrap-datepicker/1.8.0/css/bootstrap-datepicker.min.css",
81
+ "deps": [
82
+ "bootstrap4.min"
83
+ ]
84
+ },
85
+ "global-plugin": {},
86
+ "plugin": {
87
+ "deps": [
88
+ "bootstrap4.min",
89
+ "global-plugin"
90
+ ]
91
+ },
92
+ "wizard": {
93
+ "deps": [
94
+ "bootstrap4.min",
95
+ "global-plugin"
96
+ ]
97
+ },
98
+ "featherlight": {},
99
+ "chartist.min": {},
100
+ "chartist-plugin-legend": {
101
+ "deps": [
102
+ "chartist.min"
103
+ ]
104
+ },
105
+ "introjs.min": {}
106
+ },
107
+ "js": {
108
+ "bootstrap4.bundle.min": {
109
+ "url": "https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.6.0/js/bootstrap.bundle.min.js",
110
+ "deps": [
111
+ "wp-jquery"
112
+ ]
113
+ },
114
+ "bootstrap-select.min": {
115
+ "deps": [
116
+ "bootstrap4.bundle.min"
117
+ ]
118
+ },
119
+ "bootstrap-datepicker": {
120
+ "url": "https://cdnjs.cloudflare.com/ajax/libs/bootstrap-datepicker/1.8.0/js/bootstrap-datepicker.min.js",
121
+ "deps": [
122
+ "bootstrap4.bundle.min"
123
+ ]
124
+ },
125
+ "global-plugin": {
126
+ "deps": [
127
+ "wp-jquery"
128
+ ]
129
+ },
130
+ "plugin": {
131
+ "deps": [
132
+ "bootstrap4.bundle.min",
133
+ "global-plugin"
134
+ ]
135
+ },
136
+ "base64.min": {},
137
+ "lz-string.min": {},
138
+ "jquery.fileDownload": {},
139
+ "wizard": {},
140
+ "featherlight": {
141
+ "deps": [
142
+ "wp-jquery"
143
+ ]
144
+ },
145
+ "chartist.min": {},
146
+ "chartist-plugin-legend": {
147
+ "deps": [
148
+ "chartist.min"
149
+ ]
150
+ },
151
+ "charts": {
152
+ "deps": [
153
+ "chartist-plugin-legend"
154
+ ]
155
+ },
156
+ "shuffle": {},
157
+ "shield-card-shuffle": {
158
+ "deps": [
159
+ "shuffle"
160
+ ]
161
+ },
162
+ "introjs.min": {},
163
+ "shield/tables": {
164
+ "deps": [
165
+ "plugin"
166
+ ]
167
+ },
168
+ "shield/scans": {
169
+ "deps": [
170
+ "shield/tables"
171
+ ]
172
+ },
173
+ "shield/import": {
174
+ "deps": [
175
+ "plugin"
176
+ ]
177
+ },
178
+ "shield/ipanalyse": {
179
+ "deps": [
180
+ "plugin"
181
+ ]
182
+ },
183
+ "shield/mainwp-extension": {
184
+ "deps": [
185
+ "jquery"
186
+ ]
187
+ },
188
+ "shield/userprofile": {
189
+ "deps": [
190
+ "global-plugin"
191
+ ]
192
+ },
193
+ "u2f-bundle": {},
194
+ "shield/u2f-admin": {
195
+ "deps": [
196
+ "u2f-bundle",
197
+ "wp-jquery"
198
+ ]
199
+ }
200
+ }
201
  }
202
  },
203
  "menu": {
205
  "title": "Shield Security",
206
  "top_level": true,
207
  "do_submenu_fix": true,
 
208
  "icon_image": "pluginlogo_16x16.png",
209
  "has_submenu": true
210
  },
232
  }
233
  ],
234
  "version_upgrades": [
 
 
 
235
  "9.1.1",
236
  "9.2.0",
237
  "9.2.2",
238
+ "10.1.0",
239
+ "10.2.0"
240
  ],
241
  "action_links": {
242
  "remove": null,
readme.txt CHANGED
@@ -8,7 +8,7 @@ Requires at least: 3.5.2
8
  Requires PHP: 7.0
9
  Recommended PHP: 7.4
10
  Tested up to: 5.6
11
- Stable tag: 10.1.6
12
 
13
  The highest rated WordPress Security plugin, delivering unparalleled, all-in-one protection for you and your customers.
14
 
8
  Requires PHP: 7.0
9
  Recommended PHP: 7.4
10
  Tested up to: 5.6
11
+ Stable tag: 10.2.0
12
 
13
  The highest rated WordPress Security plugin, delivering unparalleled, all-in-one protection for you and your customers.
14
 
resources/css/bootstrap4.min.css DELETED
@@ -1,7 +0,0 @@
1
- /*!
2
- * Bootstrap v4.5.3 (https://getbootstrap.com/)
3
- * Copyright 2011-2020 The Bootstrap Authors
4
- * Copyright 2011-2020 Twitter, Inc.
5
- * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
6
- */:root{--blue:#007bff;--indigo:#6610f2;--purple:#6f42c1;--pink:#e83e8c;--red:#dc3545;--orange:#fd7e14;--yellow:#ffc107;--green:#28a745;--teal:#20c997;--cyan:#17a2b8;--white:#fff;--gray:#6c757d;--gray-dark:#343a40;--primary:#007bff;--secondary:#6c757d;--success:#28a745;--info:#17a2b8;--warning:#ffc107;--danger:#dc3545;--light:#f8f9fa;--dark:#343a40;--breakpoint-xs:0;--breakpoint-sm:576px;--breakpoint-md:768px;--breakpoint-lg:992px;--breakpoint-xl:1200px;--font-family-sans-serif:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";--font-family-monospace:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace}*,::after,::before{box-sizing:border-box}html{font-family:sans-serif;line-height:1.15;-webkit-text-size-adjust:100%;-webkit-tap-highlight-color:transparent}article,aside,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}body{margin:0;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";font-size:1rem;font-weight:400;line-height:1.5;color:#212529;text-align:left;background-color:#fff}[tabindex="-1"]:focus:not(:focus-visible){outline:0!important}hr{box-sizing:content-box;height:0;overflow:visible}h1,h2,h3,h4,h5,h6{margin-top:0;margin-bottom:.5rem}p{margin-top:0;margin-bottom:1rem}abbr[data-original-title],abbr[title]{text-decoration:underline;-webkit-text-decoration:underline dotted;text-decoration:underline dotted;cursor:help;border-bottom:0;-webkit-text-decoration-skip-ink:none;text-decoration-skip-ink:none}address{margin-bottom:1rem;font-style:normal;line-height:inherit}dl,ol,ul{margin-top:0;margin-bottom:1rem}ol ol,ol ul,ul ol,ul ul{margin-bottom:0}dt{font-weight:700}dd{margin-bottom:.5rem;margin-left:0}blockquote{margin:0 0 1rem}b,strong{font-weight:bolder}small{font-size:80%}sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}a{color:#007bff;text-decoration:none;background-color:transparent}a:hover{color:#0056b3;text-decoration:underline}a:not([href]):not([class]){color:inherit;text-decoration:none}a:not([href]):not([class]):hover{color:inherit;text-decoration:none}code,kbd,pre,samp{font-family:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;font-size:1em}pre{margin-top:0;margin-bottom:1rem;overflow:auto;-ms-overflow-style:scrollbar}figure{margin:0 0 1rem}img{vertical-align:middle;border-style:none}svg{overflow:hidden;vertical-align:middle}table{border-collapse:collapse}caption{padding-top:.75rem;padding-bottom:.75rem;color:#6c757d;text-align:left;caption-side:bottom}th{text-align:inherit;text-align:-webkit-match-parent}label{display:inline-block;margin-bottom:.5rem}button{border-radius:0}button:focus{outline:1px dotted;outline:5px auto -webkit-focus-ring-color}button,input,optgroup,select,textarea{margin:0;font-family:inherit;font-size:inherit;line-height:inherit}button,input{overflow:visible}button,select{text-transform:none}[role=button]{cursor:pointer}select{word-wrap:normal}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button}[type=button]:not(:disabled),[type=reset]:not(:disabled),[type=submit]:not(:disabled),button:not(:disabled){cursor:pointer}[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner,button::-moz-focus-inner{padding:0;border-style:none}input[type=checkbox],input[type=radio]{box-sizing:border-box;padding:0}textarea{overflow:auto;resize:vertical}fieldset{min-width:0;padding:0;margin:0;border:0}legend{display:block;width:100%;max-width:100%;padding:0;margin-bottom:.5rem;font-size:1.5rem;line-height:inherit;color:inherit;white-space:normal}progress{vertical-align:baseline}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{outline-offset:-2px;-webkit-appearance:none}[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{font:inherit;-webkit-appearance:button}output{display:inline-block}summary{display:list-item;cursor:pointer}template{display:none}[hidden]{display:none!important}.h1,.h2,.h3,.h4,.h5,.h6,h1,h2,h3,h4,h5,h6{margin-bottom:.5rem;font-weight:500;line-height:1.2}.h1,h1{font-size:2.5rem}.h2,h2{font-size:2rem}.h3,h3{font-size:1.75rem}.h4,h4{font-size:1.5rem}.h5,h5{font-size:1.25rem}.h6,h6{font-size:1rem}.lead{font-size:1.25rem;font-weight:300}.display-1{font-size:6rem;font-weight:300;line-height:1.2}.display-2{font-size:5.5rem;font-weight:300;line-height:1.2}.display-3{font-size:4.5rem;font-weight:300;line-height:1.2}.display-4{font-size:3.5rem;font-weight:300;line-height:1.2}hr{margin-top:1rem;margin-bottom:1rem;border:0;border-top:1px solid rgba(0,0,0,.1)}.small,small{font-size:80%;font-weight:400}.mark,mark{padding:.2em;background-color:#fcf8e3}.list-unstyled{padding-left:0;list-style:none}.list-inline{padding-left:0;list-style:none}.list-inline-item{display:inline-block}.list-inline-item:not(:last-child){margin-right:.5rem}.initialism{font-size:90%;text-transform:uppercase}.blockquote{margin-bottom:1rem;font-size:1.25rem}.blockquote-footer{display:block;font-size:80%;color:#6c757d}.blockquote-footer::before{content:"\2014\00A0"}.img-fluid{max-width:100%;height:auto}.img-thumbnail{padding:.25rem;background-color:#fff;border:1px solid #dee2e6;border-radius:.25rem;max-width:100%;height:auto}.figure{display:inline-block}.figure-img{margin-bottom:.5rem;line-height:1}.figure-caption{font-size:90%;color:#6c757d}code{font-size:87.5%;color:#e83e8c;word-wrap:break-word}a>code{color:inherit}kbd{padding:.2rem .4rem;font-size:87.5%;color:#fff;background-color:#212529;border-radius:.2rem}kbd kbd{padding:0;font-size:100%;font-weight:700}pre{display:block;font-size:87.5%;color:#212529}pre code{font-size:inherit;color:inherit;word-break:normal}.pre-scrollable{max-height:340px;overflow-y:scroll}.container,.container-fluid,.container-lg,.container-md,.container-sm,.container-xl{width:100%;padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}@media (min-width:576px){.container,.container-sm{max-width:540px}}@media (min-width:768px){.container,.container-md,.container-sm{max-width:720px}}@media (min-width:992px){.container,.container-lg,.container-md,.container-sm{max-width:960px}}@media (min-width:1200px){.container,.container-lg,.container-md,.container-sm,.container-xl{max-width:1140px}}.row{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;margin-right:-15px;margin-left:-15px}.no-gutters{margin-right:0;margin-left:0}.no-gutters>.col,.no-gutters>[class*=col-]{padding-right:0;padding-left:0}.col,.col-1,.col-10,.col-11,.col-12,.col-2,.col-3,.col-4,.col-5,.col-6,.col-7,.col-8,.col-9,.col-auto,.col-lg,.col-lg-1,.col-lg-10,.col-lg-11,.col-lg-12,.col-lg-2,.col-lg-3,.col-lg-4,.col-lg-5,.col-lg-6,.col-lg-7,.col-lg-8,.col-lg-9,.col-lg-auto,.col-md,.col-md-1,.col-md-10,.col-md-11,.col-md-12,.col-md-2,.col-md-3,.col-md-4,.col-md-5,.col-md-6,.col-md-7,.col-md-8,.col-md-9,.col-md-auto,.col-sm,.col-sm-1,.col-sm-10,.col-sm-11,.col-sm-12,.col-sm-2,.col-sm-3,.col-sm-4,.col-sm-5,.col-sm-6,.col-sm-7,.col-sm-8,.col-sm-9,.col-sm-auto,.col-xl,.col-xl-1,.col-xl-10,.col-xl-11,.col-xl-12,.col-xl-2,.col-xl-3,.col-xl-4,.col-xl-5,.col-xl-6,.col-xl-7,.col-xl-8,.col-xl-9,.col-xl-auto{position:relative;width:100%;padding-right:15px;padding-left:15px}.col{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;max-width:100%}.row-cols-1>*{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.row-cols-2>*{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.row-cols-3>*{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.row-cols-4>*{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.row-cols-5>*{-ms-flex:0 0 20%;flex:0 0 20%;max-width:20%}.row-cols-6>*{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-auto{-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:100%}.col-1{-ms-flex:0 0 8.333333%;flex:0 0 8.333333%;max-width:8.333333%}.col-2{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-3{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-4{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.col-5{-ms-flex:0 0 41.666667%;flex:0 0 41.666667%;max-width:41.666667%}.col-6{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-7{-ms-flex:0 0 58.333333%;flex:0 0 58.333333%;max-width:58.333333%}.col-8{-ms-flex:0 0 66.666667%;flex:0 0 66.666667%;max-width:66.666667%}.col-9{-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-10{-ms-flex:0 0 83.333333%;flex:0 0 83.333333%;max-width:83.333333%}.col-11{-ms-flex:0 0 91.666667%;flex:0 0 91.666667%;max-width:91.666667%}.col-12{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-first{-ms-flex-order:-1;order:-1}.order-last{-ms-flex-order:13;order:13}.order-0{-ms-flex-order:0;order:0}.order-1{-ms-flex-order:1;order:1}.order-2{-ms-flex-order:2;order:2}.order-3{-ms-flex-order:3;order:3}.order-4{-ms-flex-order:4;order:4}.order-5{-ms-flex-order:5;order:5}.order-6{-ms-flex-order:6;order:6}.order-7{-ms-flex-order:7;order:7}.order-8{-ms-flex-order:8;order:8}.order-9{-ms-flex-order:9;order:9}.order-10{-ms-flex-order:10;order:10}.order-11{-ms-flex-order:11;order:11}.order-12{-ms-flex-order:12;order:12}.offset-1{margin-left:8.333333%}.offset-2{margin-left:16.666667%}.offset-3{margin-left:25%}.offset-4{margin-left:33.333333%}.offset-5{margin-left:41.666667%}.offset-6{margin-left:50%}.offset-7{margin-left:58.333333%}.offset-8{margin-left:66.666667%}.offset-9{margin-left:75%}.offset-10{margin-left:83.333333%}.offset-11{margin-left:91.666667%}@media (min-width:576px){.col-sm{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;max-width:100%}.row-cols-sm-1>*{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.row-cols-sm-2>*{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.row-cols-sm-3>*{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.row-cols-sm-4>*{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.row-cols-sm-5>*{-ms-flex:0 0 20%;flex:0 0 20%;max-width:20%}.row-cols-sm-6>*{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-sm-auto{-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:100%}.col-sm-1{-ms-flex:0 0 8.333333%;flex:0 0 8.333333%;max-width:8.333333%}.col-sm-2{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-sm-3{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-sm-4{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.col-sm-5{-ms-flex:0 0 41.666667%;flex:0 0 41.666667%;max-width:41.666667%}.col-sm-6{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-sm-7{-ms-flex:0 0 58.333333%;flex:0 0 58.333333%;max-width:58.333333%}.col-sm-8{-ms-flex:0 0 66.666667%;flex:0 0 66.666667%;max-width:66.666667%}.col-sm-9{-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-sm-10{-ms-flex:0 0 83.333333%;flex:0 0 83.333333%;max-width:83.333333%}.col-sm-11{-ms-flex:0 0 91.666667%;flex:0 0 91.666667%;max-width:91.666667%}.col-sm-12{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-sm-first{-ms-flex-order:-1;order:-1}.order-sm-last{-ms-flex-order:13;order:13}.order-sm-0{-ms-flex-order:0;order:0}.order-sm-1{-ms-flex-order:1;order:1}.order-sm-2{-ms-flex-order:2;order:2}.order-sm-3{-ms-flex-order:3;order:3}.order-sm-4{-ms-flex-order:4;order:4}.order-sm-5{-ms-flex-order:5;order:5}.order-sm-6{-ms-flex-order:6;order:6}.order-sm-7{-ms-flex-order:7;order:7}.order-sm-8{-ms-flex-order:8;order:8}.order-sm-9{-ms-flex-order:9;order:9}.order-sm-10{-ms-flex-order:10;order:10}.order-sm-11{-ms-flex-order:11;order:11}.order-sm-12{-ms-flex-order:12;order:12}.offset-sm-0{margin-left:0}.offset-sm-1{margin-left:8.333333%}.offset-sm-2{margin-left:16.666667%}.offset-sm-3{margin-left:25%}.offset-sm-4{margin-left:33.333333%}.offset-sm-5{margin-left:41.666667%}.offset-sm-6{margin-left:50%}.offset-sm-7{margin-left:58.333333%}.offset-sm-8{margin-left:66.666667%}.offset-sm-9{margin-left:75%}.offset-sm-10{margin-left:83.333333%}.offset-sm-11{margin-left:91.666667%}}@media (min-width:768px){.col-md{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;max-width:100%}.row-cols-md-1>*{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.row-cols-md-2>*{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.row-cols-md-3>*{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.row-cols-md-4>*{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.row-cols-md-5>*{-ms-flex:0 0 20%;flex:0 0 20%;max-width:20%}.row-cols-md-6>*{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-md-auto{-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:100%}.col-md-1{-ms-flex:0 0 8.333333%;flex:0 0 8.333333%;max-width:8.333333%}.col-md-2{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-md-3{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-md-4{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.col-md-5{-ms-flex:0 0 41.666667%;flex:0 0 41.666667%;max-width:41.666667%}.col-md-6{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-md-7{-ms-flex:0 0 58.333333%;flex:0 0 58.333333%;max-width:58.333333%}.col-md-8{-ms-flex:0 0 66.666667%;flex:0 0 66.666667%;max-width:66.666667%}.col-md-9{-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-md-10{-ms-flex:0 0 83.333333%;flex:0 0 83.333333%;max-width:83.333333%}.col-md-11{-ms-flex:0 0 91.666667%;flex:0 0 91.666667%;max-width:91.666667%}.col-md-12{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-md-first{-ms-flex-order:-1;order:-1}.order-md-last{-ms-flex-order:13;order:13}.order-md-0{-ms-flex-order:0;order:0}.order-md-1{-ms-flex-order:1;order:1}.order-md-2{-ms-flex-order:2;order:2}.order-md-3{-ms-flex-order:3;order:3}.order-md-4{-ms-flex-order:4;order:4}.order-md-5{-ms-flex-order:5;order:5}.order-md-6{-ms-flex-order:6;order:6}.order-md-7{-ms-flex-order:7;order:7}.order-md-8{-ms-flex-order:8;order:8}.order-md-9{-ms-flex-order:9;order:9}.order-md-10{-ms-flex-order:10;order:10}.order-md-11{-ms-flex-order:11;order:11}.order-md-12{-ms-flex-order:12;order:12}.offset-md-0{margin-left:0}.offset-md-1{margin-left:8.333333%}.offset-md-2{margin-left:16.666667%}.offset-md-3{margin-left:25%}.offset-md-4{margin-left:33.333333%}.offset-md-5{margin-left:41.666667%}.offset-md-6{margin-left:50%}.offset-md-7{margin-left:58.333333%}.offset-md-8{margin-left:66.666667%}.offset-md-9{margin-left:75%}.offset-md-10{margin-left:83.333333%}.offset-md-11{margin-left:91.666667%}}@media (min-width:992px){.col-lg{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;max-width:100%}.row-cols-lg-1>*{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.row-cols-lg-2>*{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.row-cols-lg-3>*{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.row-cols-lg-4>*{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.row-cols-lg-5>*{-ms-flex:0 0 20%;flex:0 0 20%;max-width:20%}.row-cols-lg-6>*{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-lg-auto{-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:100%}.col-lg-1{-ms-flex:0 0 8.333333%;flex:0 0 8.333333%;max-width:8.333333%}.col-lg-2{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-lg-3{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-lg-4{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.col-lg-5{-ms-flex:0 0 41.666667%;flex:0 0 41.666667%;max-width:41.666667%}.col-lg-6{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-lg-7{-ms-flex:0 0 58.333333%;flex:0 0 58.333333%;max-width:58.333333%}.col-lg-8{-ms-flex:0 0 66.666667%;flex:0 0 66.666667%;max-width:66.666667%}.col-lg-9{-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-lg-10{-ms-flex:0 0 83.333333%;flex:0 0 83.333333%;max-width:83.333333%}.col-lg-11{-ms-flex:0 0 91.666667%;flex:0 0 91.666667%;max-width:91.666667%}.col-lg-12{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-lg-first{-ms-flex-order:-1;order:-1}.order-lg-last{-ms-flex-order:13;order:13}.order-lg-0{-ms-flex-order:0;order:0}.order-lg-1{-ms-flex-order:1;order:1}.order-lg-2{-ms-flex-order:2;order:2}.order-lg-3{-ms-flex-order:3;order:3}.order-lg-4{-ms-flex-order:4;order:4}.order-lg-5{-ms-flex-order:5;order:5}.order-lg-6{-ms-flex-order:6;order:6}.order-lg-7{-ms-flex-order:7;order:7}.order-lg-8{-ms-flex-order:8;order:8}.order-lg-9{-ms-flex-order:9;order:9}.order-lg-10{-ms-flex-order:10;order:10}.order-lg-11{-ms-flex-order:11;order:11}.order-lg-12{-ms-flex-order:12;order:12}.offset-lg-0{margin-left:0}.offset-lg-1{margin-left:8.333333%}.offset-lg-2{margin-left:16.666667%}.offset-lg-3{margin-left:25%}.offset-lg-4{margin-left:33.333333%}.offset-lg-5{margin-left:41.666667%}.offset-lg-6{margin-left:50%}.offset-lg-7{margin-left:58.333333%}.offset-lg-8{margin-left:66.666667%}.offset-lg-9{margin-left:75%}.offset-lg-10{margin-left:83.333333%}.offset-lg-11{margin-left:91.666667%}}@media (min-width:1200px){.col-xl{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;max-width:100%}.row-cols-xl-1>*{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.row-cols-xl-2>*{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.row-cols-xl-3>*{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.row-cols-xl-4>*{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.row-cols-xl-5>*{-ms-flex:0 0 20%;flex:0 0 20%;max-width:20%}.row-cols-xl-6>*{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-xl-auto{-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:100%}.col-xl-1{-ms-flex:0 0 8.333333%;flex:0 0 8.333333%;max-width:8.333333%}.col-xl-2{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-xl-3{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-xl-4{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.col-xl-5{-ms-flex:0 0 41.666667%;flex:0 0 41.666667%;max-width:41.666667%}.col-xl-6{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-xl-7{-ms-flex:0 0 58.333333%;flex:0 0 58.333333%;max-width:58.333333%}.col-xl-8{-ms-flex:0 0 66.666667%;flex:0 0 66.666667%;max-width:66.666667%}.col-xl-9{-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-xl-10{-ms-flex:0 0 83.333333%;flex:0 0 83.333333%;max-width:83.333333%}.col-xl-11{-ms-flex:0 0 91.666667%;flex:0 0 91.666667%;max-width:91.666667%}.col-xl-12{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-xl-first{-ms-flex-order:-1;order:-1}.order-xl-last{-ms-flex-order:13;order:13}.order-xl-0{-ms-flex-order:0;order:0}.order-xl-1{-ms-flex-order:1;order:1}.order-xl-2{-ms-flex-order:2;order:2}.order-xl-3{-ms-flex-order:3;order:3}.order-xl-4{-ms-flex-order:4;order:4}.order-xl-5{-ms-flex-order:5;order:5}.order-xl-6{-ms-flex-order:6;order:6}.order-xl-7{-ms-flex-order:7;order:7}.order-xl-8{-ms-flex-order:8;order:8}.order-xl-9{-ms-flex-order:9;order:9}.order-xl-10{-ms-flex-order:10;order:10}.order-xl-11{-ms-flex-order:11;order:11}.order-xl-12{-ms-flex-order:12;order:12}.offset-xl-0{margin-left:0}.offset-xl-1{margin-left:8.333333%}.offset-xl-2{margin-left:16.666667%}.offset-xl-3{margin-left:25%}.offset-xl-4{margin-left:33.333333%}.offset-xl-5{margin-left:41.666667%}.offset-xl-6{margin-left:50%}.offset-xl-7{margin-left:58.333333%}.offset-xl-8{margin-left:66.666667%}.offset-xl-9{margin-left:75%}.offset-xl-10{margin-left:83.333333%}.offset-xl-11{margin-left:91.666667%}}.table{width:100%;margin-bottom:1rem;color:#212529}.table td,.table th{padding:.75rem;vertical-align:top;border-top:1px solid #dee2e6}.table thead th{vertical-align:bottom;border-bottom:2px solid #dee2e6}.table tbody+tbody{border-top:2px solid #dee2e6}.table-sm td,.table-sm th{padding:.3rem}.table-bordered{border:1px solid #dee2e6}.table-bordered td,.table-bordered th{border:1px solid #dee2e6}.table-bordered thead td,.table-bordered thead th{border-bottom-width:2px}.table-borderless tbody+tbody,.table-borderless td,.table-borderless th,.table-borderless thead th{border:0}.table-striped tbody tr:nth-of-type(odd){background-color:rgba(0,0,0,.05)}.table-hover tbody tr:hover{color:#212529;background-color:rgba(0,0,0,.075)}.table-primary,.table-primary>td,.table-primary>th{background-color:#b8daff}.table-primary tbody+tbody,.table-primary td,.table-primary th,.table-primary thead th{border-color:#7abaff}.table-hover .table-primary:hover{background-color:#9fcdff}.table-hover .table-primary:hover>td,.table-hover .table-primary:hover>th{background-color:#9fcdff}.table-secondary,.table-secondary>td,.table-secondary>th{background-color:#d6d8db}.table-secondary tbody+tbody,.table-secondary td,.table-secondary th,.table-secondary thead th{border-color:#b3b7bb}.table-hover .table-secondary:hover{background-color:#c8cbcf}.table-hover .table-secondary:hover>td,.table-hover .table-secondary:hover>th{background-color:#c8cbcf}.table-success,.table-success>td,.table-success>th{background-color:#c3e6cb}.table-success tbody+tbody,.table-success td,.table-success th,.table-success thead th{border-color:#8fd19e}.table-hover .table-success:hover{background-color:#b1dfbb}.table-hover .table-success:hover>td,.table-hover .table-success:hover>th{background-color:#b1dfbb}.table-info,.table-info>td,.table-info>th{background-color:#bee5eb}.table-info tbody+tbody,.table-info td,.table-info th,.table-info thead th{border-color:#86cfda}.table-hover .table-info:hover{background-color:#abdde5}.table-hover .table-info:hover>td,.table-hover .table-info:hover>th{background-color:#abdde5}.table-warning,.table-warning>td,.table-warning>th{background-color:#ffeeba}.table-warning tbody+tbody,.table-warning td,.table-warning th,.table-warning thead th{border-color:#ffdf7e}.table-hover .table-warning:hover{background-color:#ffe8a1}.table-hover .table-warning:hover>td,.table-hover .table-warning:hover>th{background-color:#ffe8a1}.table-danger,.table-danger>td,.table-danger>th{background-color:#f5c6cb}.table-danger tbody+tbody,.table-danger td,.table-danger th,.table-danger thead th{border-color:#ed969e}.table-hover .table-danger:hover{background-color:#f1b0b7}.table-hover .table-danger:hover>td,.table-hover .table-danger:hover>th{background-color:#f1b0b7}.table-light,.table-light>td,.table-light>th{background-color:#fdfdfe}.table-light tbody+tbody,.table-light td,.table-light th,.table-light thead th{border-color:#fbfcfc}.table-hover .table-light:hover{background-color:#ececf6}.table-hover .table-light:hover>td,.table-hover .table-light:hover>th{background-color:#ececf6}.table-dark,.table-dark>td,.table-dark>th{background-color:#c6c8ca}.table-dark tbody+tbody,.table-dark td,.table-dark th,.table-dark thead th{border-color:#95999c}.table-hover .table-dark:hover{background-color:#b9bbbe}.table-hover .table-dark:hover>td,.table-hover .table-dark:hover>th{background-color:#b9bbbe}.table-active,.table-active>td,.table-active>th{background-color:rgba(0,0,0,.075)}.table-hover .table-active:hover{background-color:rgba(0,0,0,.075)}.table-hover .table-active:hover>td,.table-hover .table-active:hover>th{background-color:rgba(0,0,0,.075)}.table .thead-dark th{color:#fff;background-color:#343a40;border-color:#454d55}.table .thead-light th{color:#495057;background-color:#e9ecef;border-color:#dee2e6}.table-dark{color:#fff;background-color:#343a40}.table-dark td,.table-dark th,.table-dark thead th{border-color:#454d55}.table-dark.table-bordered{border:0}.table-dark.table-striped tbody tr:nth-of-type(odd){background-color:rgba(255,255,255,.05)}.table-dark.table-hover tbody tr:hover{color:#fff;background-color:rgba(255,255,255,.075)}@media (max-width:575.98px){.table-responsive-sm{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch}.table-responsive-sm>.table-bordered{border:0}}@media (max-width:767.98px){.table-responsive-md{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch}.table-responsive-md>.table-bordered{border:0}}@media (max-width:991.98px){.table-responsive-lg{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch}.table-responsive-lg>.table-bordered{border:0}}@media (max-width:1199.98px){.table-responsive-xl{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch}.table-responsive-xl>.table-bordered{border:0}}.table-responsive{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch}.table-responsive>.table-bordered{border:0}.form-control{display:block;width:100%;height:calc(1.5em + .75rem + 2px);padding:.375rem .75rem;font-size:1rem;font-weight:400;line-height:1.5;color:#495057;background-color:#fff;background-clip:padding-box;border:1px solid #ced4da;border-radius:.25rem;transition:border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.form-control{transition:none}}.form-control::-ms-expand{background-color:transparent;border:0}.form-control:-moz-focusring{color:transparent;text-shadow:0 0 0 #495057}.form-control:focus{color:#495057;background-color:#fff;border-color:#80bdff;outline:0;box-shadow:0 0 0 .2rem rgba(0,123,255,.25)}.form-control::-webkit-input-placeholder{color:#6c757d;opacity:1}.form-control::-moz-placeholder{color:#6c757d;opacity:1}.form-control:-ms-input-placeholder{color:#6c757d;opacity:1}.form-control::-ms-input-placeholder{color:#6c757d;opacity:1}.form-control::placeholder{color:#6c757d;opacity:1}.form-control:disabled,.form-control[readonly]{background-color:#e9ecef;opacity:1}input[type=date].form-control,input[type=datetime-local].form-control,input[type=month].form-control,input[type=time].form-control{-webkit-appearance:none;-moz-appearance:none;appearance:none}select.form-control:focus::-ms-value{color:#495057;background-color:#fff}.form-control-file,.form-control-range{display:block;width:100%}.col-form-label{padding-top:calc(.375rem + 1px);padding-bottom:calc(.375rem + 1px);margin-bottom:0;font-size:inherit;line-height:1.5}.col-form-label-lg{padding-top:calc(.5rem + 1px);padding-bottom:calc(.5rem + 1px);font-size:1.25rem;line-height:1.5}.col-form-label-sm{padding-top:calc(.25rem + 1px);padding-bottom:calc(.25rem + 1px);font-size:.875rem;line-height:1.5}.form-control-plaintext{display:block;width:100%;padding:.375rem 0;margin-bottom:0;font-size:1rem;line-height:1.5;color:#212529;background-color:transparent;border:solid transparent;border-width:1px 0}.form-control-plaintext.form-control-lg,.form-control-plaintext.form-control-sm{padding-right:0;padding-left:0}.form-control-sm{height:calc(1.5em + .5rem + 2px);padding:.25rem .5rem;font-size:.875rem;line-height:1.5;border-radius:.2rem}.form-control-lg{height:calc(1.5em + 1rem + 2px);padding:.5rem 1rem;font-size:1.25rem;line-height:1.5;border-radius:.3rem}select.form-control[multiple],select.form-control[size]{height:auto}textarea.form-control{height:auto}.form-group{margin-bottom:1rem}.form-text{display:block;margin-top:.25rem}.form-row{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;margin-right:-5px;margin-left:-5px}.form-row>.col,.form-row>[class*=col-]{padding-right:5px;padding-left:5px}.form-check{position:relative;display:block;padding-left:1.25rem}.form-check-input{position:absolute;margin-top:.3rem;margin-left:-1.25rem}.form-check-input:disabled~.form-check-label,.form-check-input[disabled]~.form-check-label{color:#6c757d}.form-check-label{margin-bottom:0}.form-check-inline{display:-ms-inline-flexbox;display:inline-flex;-ms-flex-align:center;align-items:center;padding-left:0;margin-right:.75rem}.form-check-inline .form-check-input{position:static;margin-top:0;margin-right:.3125rem;margin-left:0}.valid-feedback{display:none;width:100%;margin-top:.25rem;font-size:80%;color:#28a745}.valid-tooltip{position:absolute;top:100%;left:0;z-index:5;display:none;max-width:100%;padding:.25rem .5rem;margin-top:.1rem;font-size:.875rem;line-height:1.5;color:#fff;background-color:rgba(40,167,69,.9);border-radius:.25rem}.is-valid~.valid-feedback,.is-valid~.valid-tooltip,.was-validated :valid~.valid-feedback,.was-validated :valid~.valid-tooltip{display:block}.form-control.is-valid,.was-validated .form-control:valid{border-color:#28a745;padding-right:calc(1.5em + .75rem);background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='8' height='8' viewBox='0 0 8 8'%3e%3cpath fill='%2328a745' d='M2.3 6.73L.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3e%3c/svg%3e");background-repeat:no-repeat;background-position:right calc(.375em + .1875rem) center;background-size:calc(.75em + .375rem) calc(.75em + .375rem)}.form-control.is-valid:focus,.was-validated .form-control:valid:focus{border-color:#28a745;box-shadow:0 0 0 .2rem rgba(40,167,69,.25)}.was-validated textarea.form-control:valid,textarea.form-control.is-valid{padding-right:calc(1.5em + .75rem);background-position:top calc(.375em + .1875rem) right calc(.375em + .1875rem)}.custom-select.is-valid,.was-validated .custom-select:valid{border-color:#28a745;padding-right:calc(.75em + 2.3125rem);background:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='4' height='5' viewBox='0 0 4 5'%3e%3cpath fill='%23343a40' d='M2 0L0 2h4zm0 5L0 3h4z'/%3e%3c/svg%3e") no-repeat right .75rem center/8px 10px,url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='8' height='8' viewBox='0 0 8 8'%3e%3cpath fill='%2328a745' d='M2.3 6.73L.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3e%3c/svg%3e") #fff no-repeat center right 1.75rem/calc(.75em + .375rem) calc(.75em + .375rem)}.custom-select.is-valid:focus,.was-validated .custom-select:valid:focus{border-color:#28a745;box-shadow:0 0 0 .2rem rgba(40,167,69,.25)}.form-check-input.is-valid~.form-check-label,.was-validated .form-check-input:valid~.form-check-label{color:#28a745}.form-check-input.is-valid~.valid-feedback,.form-check-input.is-valid~.valid-tooltip,.was-validated .form-check-input:valid~.valid-feedback,.was-validated .form-check-input:valid~.valid-tooltip{display:block}.custom-control-input.is-valid~.custom-control-label,.was-validated .custom-control-input:valid~.custom-control-label{color:#28a745}.custom-control-input.is-valid~.custom-control-label::before,.was-validated .custom-control-input:valid~.custom-control-label::before{border-color:#28a745}.custom-control-input.is-valid:checked~.custom-control-label::before,.was-validated .custom-control-input:valid:checked~.custom-control-label::before{border-color:#34ce57;background-color:#34ce57}.custom-control-input.is-valid:focus~.custom-control-label::before,.was-validated .custom-control-input:valid:focus~.custom-control-label::before{box-shadow:0 0 0 .2rem rgba(40,167,69,.25)}.custom-control-input.is-valid:focus:not(:checked)~.custom-control-label::before,.was-validated .custom-control-input:valid:focus:not(:checked)~.custom-control-label::before{border-color:#28a745}.custom-file-input.is-valid~.custom-file-label,.was-validated .custom-file-input:valid~.custom-file-label{border-color:#28a745}.custom-file-input.is-valid:focus~.custom-file-label,.was-validated .custom-file-input:valid:focus~.custom-file-label{border-color:#28a745;box-shadow:0 0 0 .2rem rgba(40,167,69,.25)}.invalid-feedback{display:none;width:100%;margin-top:.25rem;font-size:80%;color:#dc3545}.invalid-tooltip{position:absolute;top:100%;left:0;z-index:5;display:none;max-width:100%;padding:.25rem .5rem;margin-top:.1rem;font-size:.875rem;line-height:1.5;color:#fff;background-color:rgba(220,53,69,.9);border-radius:.25rem}.is-invalid~.invalid-feedback,.is-invalid~.invalid-tooltip,.was-validated :invalid~.invalid-feedback,.was-validated :invalid~.invalid-tooltip{display:block}.form-control.is-invalid,.was-validated .form-control:invalid{border-color:#dc3545;padding-right:calc(1.5em + .75rem);background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' fill='none' stroke='%23dc3545' viewBox='0 0 12 12'%3e%3ccircle cx='6' cy='6' r='4.5'/%3e%3cpath stroke-linejoin='round' d='M5.8 3.6h.4L6 6.5z'/%3e%3ccircle cx='6' cy='8.2' r='.6' fill='%23dc3545' stroke='none'/%3e%3c/svg%3e");background-repeat:no-repeat;background-position:right calc(.375em + .1875rem) center;background-size:calc(.75em + .375rem) calc(.75em + .375rem)}.form-control.is-invalid:focus,.was-validated .form-control:invalid:focus{border-color:#dc3545;box-shadow:0 0 0 .2rem rgba(220,53,69,.25)}.was-validated textarea.form-control:invalid,textarea.form-control.is-invalid{padding-right:calc(1.5em + .75rem);background-position:top calc(.375em + .1875rem) right calc(.375em + .1875rem)}.custom-select.is-invalid,.was-validated .custom-select:invalid{border-color:#dc3545;padding-right:calc(.75em + 2.3125rem);background:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='4' height='5' viewBox='0 0 4 5'%3e%3cpath fill='%23343a40' d='M2 0L0 2h4zm0 5L0 3h4z'/%3e%3c/svg%3e") no-repeat right .75rem center/8px 10px,url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' fill='none' stroke='%23dc3545' viewBox='0 0 12 12'%3e%3ccircle cx='6' cy='6' r='4.5'/%3e%3cpath stroke-linejoin='round' d='M5.8 3.6h.4L6 6.5z'/%3e%3ccircle cx='6' cy='8.2' r='.6' fill='%23dc3545' stroke='none'/%3e%3c/svg%3e") #fff no-repeat center right 1.75rem/calc(.75em + .375rem) calc(.75em + .375rem)}.custom-select.is-invalid:focus,.was-validated .custom-select:invalid:focus{border-color:#dc3545;box-shadow:0 0 0 .2rem rgba(220,53,69,.25)}.form-check-input.is-invalid~.form-check-label,.was-validated .form-check-input:invalid~.form-check-label{color:#dc3545}.form-check-input.is-invalid~.invalid-feedback,.form-check-input.is-invalid~.invalid-tooltip,.was-validated .form-check-input:invalid~.invalid-feedback,.was-validated .form-check-input:invalid~.invalid-tooltip{display:block}.custom-control-input.is-invalid~.custom-control-label,.was-validated .custom-control-input:invalid~.custom-control-label{color:#dc3545}.custom-control-input.is-invalid~.custom-control-label::before,.was-validated .custom-control-input:invalid~.custom-control-label::before{border-color:#dc3545}.custom-control-input.is-invalid:checked~.custom-control-label::before,.was-validated .custom-control-input:invalid:checked~.custom-control-label::before{border-color:#e4606d;background-color:#e4606d}.custom-control-input.is-invalid:focus~.custom-control-label::before,.was-validated .custom-control-input:invalid:focus~.custom-control-label::before{box-shadow:0 0 0 .2rem rgba(220,53,69,.25)}.custom-control-input.is-invalid:focus:not(:checked)~.custom-control-label::before,.was-validated .custom-control-input:invalid:focus:not(:checked)~.custom-control-label::before{border-color:#dc3545}.custom-file-input.is-invalid~.custom-file-label,.was-validated .custom-file-input:invalid~.custom-file-label{border-color:#dc3545}.custom-file-input.is-invalid:focus~.custom-file-label,.was-validated .custom-file-input:invalid:focus~.custom-file-label{border-color:#dc3545;box-shadow:0 0 0 .2rem rgba(220,53,69,.25)}.form-inline{display:-ms-flexbox;display:flex;-ms-flex-flow:row wrap;flex-flow:row wrap;-ms-flex-align:center;align-items:center}.form-inline .form-check{width:100%}@media (min-width:576px){.form-inline label{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;-ms-flex-pack:center;justify-content:center;margin-bottom:0}.form-inline .form-group{display:-ms-flexbox;display:flex;-ms-flex:0 0 auto;flex:0 0 auto;-ms-flex-flow:row wrap;flex-flow:row wrap;-ms-flex-align:center;align-items:center;margin-bottom:0}.form-inline .form-control{display:inline-block;width:auto;vertical-align:middle}.form-inline .form-control-plaintext{display:inline-block}.form-inline .custom-select,.form-inline .input-group{width:auto}.form-inline .form-check{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;-ms-flex-pack:center;justify-content:center;width:auto;padding-left:0}.form-inline .form-check-input{position:relative;-ms-flex-negative:0;flex-shrink:0;margin-top:0;margin-right:.25rem;margin-left:0}.form-inline .custom-control{-ms-flex-align:center;align-items:center;-ms-flex-pack:center;justify-content:center}.form-inline .custom-control-label{margin-bottom:0}}.btn{display:inline-block;font-weight:400;color:#212529;text-align:center;vertical-align:middle;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;background-color:transparent;border:1px solid transparent;padding:.375rem .75rem;font-size:1rem;line-height:1.5;border-radius:.25rem;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.btn{transition:none}}.btn:hover{color:#212529;text-decoration:none}.btn.focus,.btn:focus{outline:0;box-shadow:0 0 0 .2rem rgba(0,123,255,.25)}.btn.disabled,.btn:disabled{opacity:.65}.btn:not(:disabled):not(.disabled){cursor:pointer}a.btn.disabled,fieldset:disabled a.btn{pointer-events:none}.btn-primary{color:#fff;background-color:#007bff;border-color:#007bff}.btn-primary:hover{color:#fff;background-color:#0069d9;border-color:#0062cc}.btn-primary.focus,.btn-primary:focus{color:#fff;background-color:#0069d9;border-color:#0062cc;box-shadow:0 0 0 .2rem rgba(38,143,255,.5)}.btn-primary.disabled,.btn-primary:disabled{color:#fff;background-color:#007bff;border-color:#007bff}.btn-primary:not(:disabled):not(.disabled).active,.btn-primary:not(:disabled):not(.disabled):active,.show>.btn-primary.dropdown-toggle{color:#fff;background-color:#0062cc;border-color:#005cbf}.btn-primary:not(:disabled):not(.disabled).active:focus,.btn-primary:not(:disabled):not(.disabled):active:focus,.show>.btn-primary.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(38,143,255,.5)}.btn-secondary{color:#fff;background-color:#6c757d;border-color:#6c757d}.btn-secondary:hover{color:#fff;background-color:#5a6268;border-color:#545b62}.btn-secondary.focus,.btn-secondary:focus{color:#fff;background-color:#5a6268;border-color:#545b62;box-shadow:0 0 0 .2rem rgba(130,138,145,.5)}.btn-secondary.disabled,.btn-secondary:disabled{color:#fff;background-color:#6c757d;border-color:#6c757d}.btn-secondary:not(:disabled):not(.disabled).active,.btn-secondary:not(:disabled):not(.disabled):active,.show>.btn-secondary.dropdown-toggle{color:#fff;background-color:#545b62;border-color:#4e555b}.btn-secondary:not(:disabled):not(.disabled).active:focus,.btn-secondary:not(:disabled):not(.disabled):active:focus,.show>.btn-secondary.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(130,138,145,.5)}.btn-success{color:#fff;background-color:#28a745;border-color:#28a745}.btn-success:hover{color:#fff;background-color:#218838;border-color:#1e7e34}.btn-success.focus,.btn-success:focus{color:#fff;background-color:#218838;border-color:#1e7e34;box-shadow:0 0 0 .2rem rgba(72,180,97,.5)}.btn-success.disabled,.btn-success:disabled{color:#fff;background-color:#28a745;border-color:#28a745}.btn-success:not(:disabled):not(.disabled).active,.btn-success:not(:disabled):not(.disabled):active,.show>.btn-success.dropdown-toggle{color:#fff;background-color:#1e7e34;border-color:#1c7430}.btn-success:not(:disabled):not(.disabled).active:focus,.btn-success:not(:disabled):not(.disabled):active:focus,.show>.btn-success.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(72,180,97,.5)}.btn-info{color:#fff;background-color:#17a2b8;border-color:#17a2b8}.btn-info:hover{color:#fff;background-color:#138496;border-color:#117a8b}.btn-info.focus,.btn-info:focus{color:#fff;background-color:#138496;border-color:#117a8b;box-shadow:0 0 0 .2rem rgba(58,176,195,.5)}.btn-info.disabled,.btn-info:disabled{color:#fff;background-color:#17a2b8;border-color:#17a2b8}.btn-info:not(:disabled):not(.disabled).active,.btn-info:not(:disabled):not(.disabled):active,.show>.btn-info.dropdown-toggle{color:#fff;background-color:#117a8b;border-color:#10707f}.btn-info:not(:disabled):not(.disabled).active:focus,.btn-info:not(:disabled):not(.disabled):active:focus,.show>.btn-info.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(58,176,195,.5)}.btn-warning{color:#212529;background-color:#ffc107;border-color:#ffc107}.btn-warning:hover{color:#212529;background-color:#e0a800;border-color:#d39e00}.btn-warning.focus,.btn-warning:focus{color:#212529;background-color:#e0a800;border-color:#d39e00;box-shadow:0 0 0 .2rem rgba(222,170,12,.5)}.btn-warning.disabled,.btn-warning:disabled{color:#212529;background-color:#ffc107;border-color:#ffc107}.btn-warning:not(:disabled):not(.disabled).active,.btn-warning:not(:disabled):not(.disabled):active,.show>.btn-warning.dropdown-toggle{color:#212529;background-color:#d39e00;border-color:#c69500}.btn-warning:not(:disabled):not(.disabled).active:focus,.btn-warning:not(:disabled):not(.disabled):active:focus,.show>.btn-warning.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(222,170,12,.5)}.btn-danger{color:#fff;background-color:#dc3545;border-color:#dc3545}.btn-danger:hover{color:#fff;background-color:#c82333;border-color:#bd2130}.btn-danger.focus,.btn-danger:focus{color:#fff;background-color:#c82333;border-color:#bd2130;box-shadow:0 0 0 .2rem rgba(225,83,97,.5)}.btn-danger.disabled,.btn-danger:disabled{color:#fff;background-color:#dc3545;border-color:#dc3545}.btn-danger:not(:disabled):not(.disabled).active,.btn-danger:not(:disabled):not(.disabled):active,.show>.btn-danger.dropdown-toggle{color:#fff;background-color:#bd2130;border-color:#b21f2d}.btn-danger:not(:disabled):not(.disabled).active:focus,.btn-danger:not(:disabled):not(.disabled):active:focus,.show>.btn-danger.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(225,83,97,.5)}.btn-light{color:#212529;background-color:#f8f9fa;border-color:#f8f9fa}.btn-light:hover{color:#212529;background-color:#e2e6ea;border-color:#dae0e5}.btn-light.focus,.btn-light:focus{color:#212529;background-color:#e2e6ea;border-color:#dae0e5;box-shadow:0 0 0 .2rem rgba(216,217,219,.5)}.btn-light.disabled,.btn-light:disabled{color:#212529;background-color:#f8f9fa;border-color:#f8f9fa}.btn-light:not(:disabled):not(.disabled).active,.btn-light:not(:disabled):not(.disabled):active,.show>.btn-light.dropdown-toggle{color:#212529;background-color:#dae0e5;border-color:#d3d9df}.btn-light:not(:disabled):not(.disabled).active:focus,.btn-light:not(:disabled):not(.disabled):active:focus,.show>.btn-light.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(216,217,219,.5)}.btn-dark{color:#fff;background-color:#343a40;border-color:#343a40}.btn-dark:hover{color:#fff;background-color:#23272b;border-color:#1d2124}.btn-dark.focus,.btn-dark:focus{color:#fff;background-color:#23272b;border-color:#1d2124;box-shadow:0 0 0 .2rem rgba(82,88,93,.5)}.btn-dark.disabled,.btn-dark:disabled{color:#fff;background-color:#343a40;border-color:#343a40}.btn-dark:not(:disabled):not(.disabled).active,.btn-dark:not(:disabled):not(.disabled):active,.show>.btn-dark.dropdown-toggle{color:#fff;background-color:#1d2124;border-color:#171a1d}.btn-dark:not(:disabled):not(.disabled).active:focus,.btn-dark:not(:disabled):not(.disabled):active:focus,.show>.btn-dark.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(82,88,93,.5)}.btn-outline-primary{color:#007bff;border-color:#007bff}.btn-outline-primary:hover{color:#fff;background-color:#007bff;border-color:#007bff}.btn-outline-primary.focus,.btn-outline-primary:focus{box-shadow:0 0 0 .2rem rgba(0,123,255,.5)}.btn-outline-primary.disabled,.btn-outline-primary:disabled{color:#007bff;background-color:transparent}.btn-outline-primary:not(:disabled):not(.disabled).active,.btn-outline-primary:not(:disabled):not(.disabled):active,.show>.btn-outline-primary.dropdown-toggle{color:#fff;background-color:#007bff;border-color:#007bff}.btn-outline-primary:not(:disabled):not(.disabled).active:focus,.btn-outline-primary:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-primary.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(0,123,255,.5)}.btn-outline-secondary{color:#6c757d;border-color:#6c757d}.btn-outline-secondary:hover{color:#fff;background-color:#6c757d;border-color:#6c757d}.btn-outline-secondary.focus,.btn-outline-secondary:focus{box-shadow:0 0 0 .2rem rgba(108,117,125,.5)}.btn-outline-secondary.disabled,.btn-outline-secondary:disabled{color:#6c757d;background-color:transparent}.btn-outline-secondary:not(:disabled):not(.disabled).active,.btn-outline-secondary:not(:disabled):not(.disabled):active,.show>.btn-outline-secondary.dropdown-toggle{color:#fff;background-color:#6c757d;border-color:#6c757d}.btn-outline-secondary:not(:disabled):not(.disabled).active:focus,.btn-outline-secondary:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-secondary.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(108,117,125,.5)}.btn-outline-success{color:#28a745;border-color:#28a745}.btn-outline-success:hover{color:#fff;background-color:#28a745;border-color:#28a745}.btn-outline-success.focus,.btn-outline-success:focus{box-shadow:0 0 0 .2rem rgba(40,167,69,.5)}.btn-outline-success.disabled,.btn-outline-success:disabled{color:#28a745;background-color:transparent}.btn-outline-success:not(:disabled):not(.disabled).active,.btn-outline-success:not(:disabled):not(.disabled):active,.show>.btn-outline-success.dropdown-toggle{color:#fff;background-color:#28a745;border-color:#28a745}.btn-outline-success:not(:disabled):not(.disabled).active:focus,.btn-outline-success:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-success.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(40,167,69,.5)}.btn-outline-info{color:#17a2b8;border-color:#17a2b8}.btn-outline-info:hover{color:#fff;background-color:#17a2b8;border-color:#17a2b8}.btn-outline-info.focus,.btn-outline-info:focus{box-shadow:0 0 0 .2rem rgba(23,162,184,.5)}.btn-outline-info.disabled,.btn-outline-info:disabled{color:#17a2b8;background-color:transparent}.btn-outline-info:not(:disabled):not(.disabled).active,.btn-outline-info:not(:disabled):not(.disabled):active,.show>.btn-outline-info.dropdown-toggle{color:#fff;background-color:#17a2b8;border-color:#17a2b8}.btn-outline-info:not(:disabled):not(.disabled).active:focus,.btn-outline-info:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-info.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(23,162,184,.5)}.btn-outline-warning{color:#ffc107;border-color:#ffc107}.btn-outline-warning:hover{color:#212529;background-color:#ffc107;border-color:#ffc107}.btn-outline-warning.focus,.btn-outline-warning:focus{box-shadow:0 0 0 .2rem rgba(255,193,7,.5)}.btn-outline-warning.disabled,.btn-outline-warning:disabled{color:#ffc107;background-color:transparent}.btn-outline-warning:not(:disabled):not(.disabled).active,.btn-outline-warning:not(:disabled):not(.disabled):active,.show>.btn-outline-warning.dropdown-toggle{color:#212529;background-color:#ffc107;border-color:#ffc107}.btn-outline-warning:not(:disabled):not(.disabled).active:focus,.btn-outline-warning:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-warning.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(255,193,7,.5)}.btn-outline-danger{color:#dc3545;border-color:#dc3545}.btn-outline-danger:hover{color:#fff;background-color:#dc3545;border-color:#dc3545}.btn-outline-danger.focus,.btn-outline-danger:focus{box-shadow:0 0 0 .2rem rgba(220,53,69,.5)}.btn-outline-danger.disabled,.btn-outline-danger:disabled{color:#dc3545;background-color:transparent}.btn-outline-danger:not(:disabled):not(.disabled).active,.btn-outline-danger:not(:disabled):not(.disabled):active,.show>.btn-outline-danger.dropdown-toggle{color:#fff;background-color:#dc3545;border-color:#dc3545}.btn-outline-danger:not(:disabled):not(.disabled).active:focus,.btn-outline-danger:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-danger.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(220,53,69,.5)}.btn-outline-light{color:#f8f9fa;border-color:#f8f9fa}.btn-outline-light:hover{color:#212529;background-color:#f8f9fa;border-color:#f8f9fa}.btn-outline-light.focus,.btn-outline-light:focus{box-shadow:0 0 0 .2rem rgba(248,249,250,.5)}.btn-outline-light.disabled,.btn-outline-light:disabled{color:#f8f9fa;background-color:transparent}.btn-outline-light:not(:disabled):not(.disabled).active,.btn-outline-light:not(:disabled):not(.disabled):active,.show>.btn-outline-light.dropdown-toggle{color:#212529;background-color:#f8f9fa;border-color:#f8f9fa}.btn-outline-light:not(:disabled):not(.disabled).active:focus,.btn-outline-light:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-light.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(248,249,250,.5)}.btn-outline-dark{color:#343a40;border-color:#343a40}.btn-outline-dark:hover{color:#fff;background-color:#343a40;border-color:#343a40}.btn-outline-dark.focus,.btn-outline-dark:focus{box-shadow:0 0 0 .2rem rgba(52,58,64,.5)}.btn-outline-dark.disabled,.btn-outline-dark:disabled{color:#343a40;background-color:transparent}.btn-outline-dark:not(:disabled):not(.disabled).active,.btn-outline-dark:not(:disabled):not(.disabled):active,.show>.btn-outline-dark.dropdown-toggle{color:#fff;background-color:#343a40;border-color:#343a40}.btn-outline-dark:not(:disabled):not(.disabled).active:focus,.btn-outline-dark:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-dark.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(52,58,64,.5)}.btn-link{font-weight:400;color:#007bff;text-decoration:none}.btn-link:hover{color:#0056b3;text-decoration:underline}.btn-link.focus,.btn-link:focus{text-decoration:underline}.btn-link.disabled,.btn-link:disabled{color:#6c757d;pointer-events:none}.btn-group-lg>.btn,.btn-lg{padding:.5rem 1rem;font-size:1.25rem;line-height:1.5;border-radius:.3rem}.btn-group-sm>.btn,.btn-sm{padding:.25rem .5rem;font-size:.875rem;line-height:1.5;border-radius:.2rem}.btn-block{display:block;width:100%}.btn-block+.btn-block{margin-top:.5rem}input[type=button].btn-block,input[type=reset].btn-block,input[type=submit].btn-block{width:100%}.fade{transition:opacity .15s linear}@media (prefers-reduced-motion:reduce){.fade{transition:none}}.fade:not(.show){opacity:0}.collapse:not(.show){display:none}.collapsing{position:relative;height:0;overflow:hidden;transition:height .35s ease}@media (prefers-reduced-motion:reduce){.collapsing{transition:none}}.dropdown,.dropleft,.dropright,.dropup{position:relative}.dropdown-toggle{white-space:nowrap}.dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:"";border-top:.3em solid;border-right:.3em solid transparent;border-bottom:0;border-left:.3em solid transparent}.dropdown-toggle:empty::after{margin-left:0}.dropdown-menu{position:absolute;top:100%;left:0;z-index:1000;display:none;float:left;min-width:10rem;padding:.5rem 0;margin:.125rem 0 0;font-size:1rem;color:#212529;text-align:left;list-style:none;background-color:#fff;background-clip:padding-box;border:1px solid rgba(0,0,0,.15);border-radius:.25rem}.dropdown-menu-left{right:auto;left:0}.dropdown-menu-right{right:0;left:auto}@media (min-width:576px){.dropdown-menu-sm-left{right:auto;left:0}.dropdown-menu-sm-right{right:0;left:auto}}@media (min-width:768px){.dropdown-menu-md-left{right:auto;left:0}.dropdown-menu-md-right{right:0;left:auto}}@media (min-width:992px){.dropdown-menu-lg-left{right:auto;left:0}.dropdown-menu-lg-right{right:0;left:auto}}@media (min-width:1200px){.dropdown-menu-xl-left{right:auto;left:0}.dropdown-menu-xl-right{right:0;left:auto}}.dropup .dropdown-menu{top:auto;bottom:100%;margin-top:0;margin-bottom:.125rem}.dropup .dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:"";border-top:0;border-right:.3em solid transparent;border-bottom:.3em solid;border-left:.3em solid transparent}.dropup .dropdown-toggle:empty::after{margin-left:0}.dropright .dropdown-menu{top:0;right:auto;left:100%;margin-top:0;margin-left:.125rem}.dropright .dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:"";border-top:.3em solid transparent;border-right:0;border-bottom:.3em solid transparent;border-left:.3em solid}.dropright .dropdown-toggle:empty::after{margin-left:0}.dropright .dropdown-toggle::after{vertical-align:0}.dropleft .dropdown-menu{top:0;right:100%;left:auto;margin-top:0;margin-right:.125rem}.dropleft .dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:""}.dropleft .dropdown-toggle::after{display:none}.dropleft .dropdown-toggle::before{display:inline-block;margin-right:.255em;vertical-align:.255em;content:"";border-top:.3em solid transparent;border-right:.3em solid;border-bottom:.3em solid transparent}.dropleft .dropdown-toggle:empty::after{margin-left:0}.dropleft .dropdown-toggle::before{vertical-align:0}.dropdown-menu[x-placement^=bottom],.dropdown-menu[x-placement^=left],.dropdown-menu[x-placement^=right],.dropdown-menu[x-placement^=top]{right:auto;bottom:auto}.dropdown-divider{height:0;margin:.5rem 0;overflow:hidden;border-top:1px solid #e9ecef}.dropdown-item{display:block;width:100%;padding:.25rem 1.5rem;clear:both;font-weight:400;color:#212529;text-align:inherit;white-space:nowrap;background-color:transparent;border:0}.dropdown-item:focus,.dropdown-item:hover{color:#16181b;text-decoration:none;background-color:#f8f9fa}.dropdown-item.active,.dropdown-item:active{color:#fff;text-decoration:none;background-color:#007bff}.dropdown-item.disabled,.dropdown-item:disabled{color:#6c757d;pointer-events:none;background-color:transparent}.dropdown-menu.show{display:block}.dropdown-header{display:block;padding:.5rem 1.5rem;margin-bottom:0;font-size:.875rem;color:#6c757d;white-space:nowrap}.dropdown-item-text{display:block;padding:.25rem 1.5rem;color:#212529}.btn-group,.btn-group-vertical{position:relative;display:-ms-inline-flexbox;display:inline-flex;vertical-align:middle}.btn-group-vertical>.btn,.btn-group>.btn{position:relative;-ms-flex:1 1 auto;flex:1 1 auto}.btn-group-vertical>.btn:hover,.btn-group>.btn:hover{z-index:1}.btn-group-vertical>.btn.active,.btn-group-vertical>.btn:active,.btn-group-vertical>.btn:focus,.btn-group>.btn.active,.btn-group>.btn:active,.btn-group>.btn:focus{z-index:1}.btn-toolbar{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;-ms-flex-pack:start;justify-content:flex-start}.btn-toolbar .input-group{width:auto}.btn-group>.btn-group:not(:first-child),.btn-group>.btn:not(:first-child){margin-left:-1px}.btn-group>.btn-group:not(:last-child)>.btn,.btn-group>.btn:not(:last-child):not(.dropdown-toggle){border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn-group:not(:first-child)>.btn,.btn-group>.btn:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.dropdown-toggle-split{padding-right:.5625rem;padding-left:.5625rem}.dropdown-toggle-split::after,.dropright .dropdown-toggle-split::after,.dropup .dropdown-toggle-split::after{margin-left:0}.dropleft .dropdown-toggle-split::before{margin-right:0}.btn-group-sm>.btn+.dropdown-toggle-split,.btn-sm+.dropdown-toggle-split{padding-right:.375rem;padding-left:.375rem}.btn-group-lg>.btn+.dropdown-toggle-split,.btn-lg+.dropdown-toggle-split{padding-right:.75rem;padding-left:.75rem}.btn-group-vertical{-ms-flex-direction:column;flex-direction:column;-ms-flex-align:start;align-items:flex-start;-ms-flex-pack:center;justify-content:center}.btn-group-vertical>.btn,.btn-group-vertical>.btn-group{width:100%}.btn-group-vertical>.btn-group:not(:first-child),.btn-group-vertical>.btn:not(:first-child){margin-top:-1px}.btn-group-vertical>.btn-group:not(:last-child)>.btn,.btn-group-vertical>.btn:not(:last-child):not(.dropdown-toggle){border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn-group:not(:first-child)>.btn,.btn-group-vertical>.btn:not(:first-child){border-top-left-radius:0;border-top-right-radius:0}.btn-group-toggle>.btn,.btn-group-toggle>.btn-group>.btn{margin-bottom:0}.btn-group-toggle>.btn input[type=checkbox],.btn-group-toggle>.btn input[type=radio],.btn-group-toggle>.btn-group>.btn input[type=checkbox],.btn-group-toggle>.btn-group>.btn input[type=radio]{position:absolute;clip:rect(0,0,0,0);pointer-events:none}.input-group{position:relative;display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;-ms-flex-align:stretch;align-items:stretch;width:100%}.input-group>.custom-file,.input-group>.custom-select,.input-group>.form-control,.input-group>.form-control-plaintext{position:relative;-ms-flex:1 1 auto;flex:1 1 auto;width:1%;min-width:0;margin-bottom:0}.input-group>.custom-file+.custom-file,.input-group>.custom-file+.custom-select,.input-group>.custom-file+.form-control,.input-group>.custom-select+.custom-file,.input-group>.custom-select+.custom-select,.input-group>.custom-select+.form-control,.input-group>.form-control+.custom-file,.input-group>.form-control+.custom-select,.input-group>.form-control+.form-control,.input-group>.form-control-plaintext+.custom-file,.input-group>.form-control-plaintext+.custom-select,.input-group>.form-control-plaintext+.form-control{margin-left:-1px}.input-group>.custom-file .custom-file-input:focus~.custom-file-label,.input-group>.custom-select:focus,.input-group>.form-control:focus{z-index:3}.input-group>.custom-file .custom-file-input:focus{z-index:4}.input-group>.custom-select:not(:last-child),.input-group>.form-control:not(:last-child){border-top-right-radius:0;border-bottom-right-radius:0}.input-group>.custom-select:not(:first-child),.input-group>.form-control:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.input-group>.custom-file{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center}.input-group>.custom-file:not(:last-child) .custom-file-label,.input-group>.custom-file:not(:last-child) .custom-file-label::after{border-top-right-radius:0;border-bottom-right-radius:0}.input-group>.custom-file:not(:first-child) .custom-file-label{border-top-left-radius:0;border-bottom-left-radius:0}.input-group-append,.input-group-prepend{display:-ms-flexbox;display:flex}.input-group-append .btn,.input-group-prepend .btn{position:relative;z-index:2}.input-group-append .btn:focus,.input-group-prepend .btn:focus{z-index:3}.input-group-append .btn+.btn,.input-group-append .btn+.input-group-text,.input-group-append .input-group-text+.btn,.input-group-append .input-group-text+.input-group-text,.input-group-prepend .btn+.btn,.input-group-prepend .btn+.input-group-text,.input-group-prepend .input-group-text+.btn,.input-group-prepend .input-group-text+.input-group-text{margin-left:-1px}.input-group-prepend{margin-right:-1px}.input-group-append{margin-left:-1px}.input-group-text{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;padding:.375rem .75rem;margin-bottom:0;font-size:1rem;font-weight:400;line-height:1.5;color:#495057;text-align:center;white-space:nowrap;background-color:#e9ecef;border:1px solid #ced4da;border-radius:.25rem}.input-group-text input[type=checkbox],.input-group-text input[type=radio]{margin-top:0}.input-group-lg>.custom-select,.input-group-lg>.form-control:not(textarea){height:calc(1.5em + 1rem + 2px)}.input-group-lg>.custom-select,.input-group-lg>.form-control,.input-group-lg>.input-group-append>.btn,.input-group-lg>.input-group-append>.input-group-text,.input-group-lg>.input-group-prepend>.btn,.input-group-lg>.input-group-prepend>.input-group-text{padding:.5rem 1rem;font-size:1.25rem;line-height:1.5;border-radius:.3rem}.input-group-sm>.custom-select,.input-group-sm>.form-control:not(textarea){height:calc(1.5em + .5rem + 2px)}.input-group-sm>.custom-select,.input-group-sm>.form-control,.input-group-sm>.input-group-append>.btn,.input-group-sm>.input-group-append>.input-group-text,.input-group-sm>.input-group-prepend>.btn,.input-group-sm>.input-group-prepend>.input-group-text{padding:.25rem .5rem;font-size:.875rem;line-height:1.5;border-radius:.2rem}.input-group-lg>.custom-select,.input-group-sm>.custom-select{padding-right:1.75rem}.input-group>.input-group-append:last-child>.btn:not(:last-child):not(.dropdown-toggle),.input-group>.input-group-append:last-child>.input-group-text:not(:last-child),.input-group>.input-group-append:not(:last-child)>.btn,.input-group>.input-group-append:not(:last-child)>.input-group-text,.input-group>.input-group-prepend>.btn,.input-group>.input-group-prepend>.input-group-text{border-top-right-radius:0;border-bottom-right-radius:0}.input-group>.input-group-append>.btn,.input-group>.input-group-append>.input-group-text,.input-group>.input-group-prepend:first-child>.btn:not(:first-child),.input-group>.input-group-prepend:first-child>.input-group-text:not(:first-child),.input-group>.input-group-prepend:not(:first-child)>.btn,.input-group>.input-group-prepend:not(:first-child)>.input-group-text{border-top-left-radius:0;border-bottom-left-radius:0}.custom-control{position:relative;z-index:1;display:block;min-height:1.5rem;padding-left:1.5rem;-webkit-print-color-adjust:exact;color-adjust:exact}.custom-control-inline{display:-ms-inline-flexbox;display:inline-flex;margin-right:1rem}.custom-control-input{position:absolute;left:0;z-index:-1;width:1rem;height:1.25rem;opacity:0}.custom-control-input:checked~.custom-control-label::before{color:#fff;border-color:#007bff;background-color:#007bff}.custom-control-input:focus~.custom-control-label::before{box-shadow:0 0 0 .2rem rgba(0,123,255,.25)}.custom-control-input:focus:not(:checked)~.custom-control-label::before{border-color:#80bdff}.custom-control-input:not(:disabled):active~.custom-control-label::before{color:#fff;background-color:#b3d7ff;border-color:#b3d7ff}.custom-control-input:disabled~.custom-control-label,.custom-control-input[disabled]~.custom-control-label{color:#6c757d}.custom-control-input:disabled~.custom-control-label::before,.custom-control-input[disabled]~.custom-control-label::before{background-color:#e9ecef}.custom-control-label{position:relative;margin-bottom:0;vertical-align:top}.custom-control-label::before{position:absolute;top:.25rem;left:-1.5rem;display:block;width:1rem;height:1rem;pointer-events:none;content:"";background-color:#fff;border:#adb5bd solid 1px}.custom-control-label::after{position:absolute;top:.25rem;left:-1.5rem;display:block;width:1rem;height:1rem;content:"";background:no-repeat 50%/50% 50%}.custom-checkbox .custom-control-label::before{border-radius:.25rem}.custom-checkbox .custom-control-input:checked~.custom-control-label::after{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='8' height='8' viewBox='0 0 8 8'%3e%3cpath fill='%23fff' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26l2.974 2.99L8 2.193z'/%3e%3c/svg%3e")}.custom-checkbox .custom-control-input:indeterminate~.custom-control-label::before{border-color:#007bff;background-color:#007bff}.custom-checkbox .custom-control-input:indeterminate~.custom-control-label::after{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='4' height='4' viewBox='0 0 4 4'%3e%3cpath stroke='%23fff' d='M0 2h4'/%3e%3c/svg%3e")}.custom-checkbox .custom-control-input:disabled:checked~.custom-control-label::before{background-color:rgba(0,123,255,.5)}.custom-checkbox .custom-control-input:disabled:indeterminate~.custom-control-label::before{background-color:rgba(0,123,255,.5)}.custom-radio .custom-control-label::before{border-radius:50%}.custom-radio .custom-control-input:checked~.custom-control-label::after{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='%23fff'/%3e%3c/svg%3e")}.custom-radio .custom-control-input:disabled:checked~.custom-control-label::before{background-color:rgba(0,123,255,.5)}.custom-switch{padding-left:2.25rem}.custom-switch .custom-control-label::before{left:-2.25rem;width:1.75rem;pointer-events:all;border-radius:.5rem}.custom-switch .custom-control-label::after{top:calc(.25rem + 2px);left:calc(-2.25rem + 2px);width:calc(1rem - 4px);height:calc(1rem - 4px);background-color:#adb5bd;border-radius:.5rem;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out,-webkit-transform .15s ease-in-out;transition:transform .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;transition:transform .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out,-webkit-transform .15s ease-in-out}@media (prefers-reduced-motion:reduce){.custom-switch .custom-control-label::after{transition:none}}.custom-switch .custom-control-input:checked~.custom-control-label::after{background-color:#fff;-webkit-transform:translateX(.75rem);transform:translateX(.75rem)}.custom-switch .custom-control-input:disabled:checked~.custom-control-label::before{background-color:rgba(0,123,255,.5)}.custom-select{display:inline-block;width:100%;height:calc(1.5em + .75rem + 2px);padding:.375rem 1.75rem .375rem .75rem;font-size:1rem;font-weight:400;line-height:1.5;color:#495057;vertical-align:middle;background:#fff url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='4' height='5' viewBox='0 0 4 5'%3e%3cpath fill='%23343a40' d='M2 0L0 2h4zm0 5L0 3h4z'/%3e%3c/svg%3e") no-repeat right .75rem center/8px 10px;border:1px solid #ced4da;border-radius:.25rem;-webkit-appearance:none;-moz-appearance:none;appearance:none}.custom-select:focus{border-color:#80bdff;outline:0;box-shadow:0 0 0 .2rem rgba(0,123,255,.25)}.custom-select:focus::-ms-value{color:#495057;background-color:#fff}.custom-select[multiple],.custom-select[size]:not([size="1"]){height:auto;padding-right:.75rem;background-image:none}.custom-select:disabled{color:#6c757d;background-color:#e9ecef}.custom-select::-ms-expand{display:none}.custom-select:-moz-focusring{color:transparent;text-shadow:0 0 0 #495057}.custom-select-sm{height:calc(1.5em + .5rem + 2px);padding-top:.25rem;padding-bottom:.25rem;padding-left:.5rem;font-size:.875rem}.custom-select-lg{height:calc(1.5em + 1rem + 2px);padding-top:.5rem;padding-bottom:.5rem;padding-left:1rem;font-size:1.25rem}.custom-file{position:relative;display:inline-block;width:100%;height:calc(1.5em + .75rem + 2px);margin-bottom:0}.custom-file-input{position:relative;z-index:2;width:100%;height:calc(1.5em + .75rem + 2px);margin:0;opacity:0}.custom-file-input:focus~.custom-file-label{border-color:#80bdff;box-shadow:0 0 0 .2rem rgba(0,123,255,.25)}.custom-file-input:disabled~.custom-file-label,.custom-file-input[disabled]~.custom-file-label{background-color:#e9ecef}.custom-file-input:lang(en)~.custom-file-label::after{content:"Browse"}.custom-file-input~.custom-file-label[data-browse]::after{content:attr(data-browse)}.custom-file-label{position:absolute;top:0;right:0;left:0;z-index:1;height:calc(1.5em + .75rem + 2px);padding:.375rem .75rem;font-weight:400;line-height:1.5;color:#495057;background-color:#fff;border:1px solid #ced4da;border-radius:.25rem}.custom-file-label::after{position:absolute;top:0;right:0;bottom:0;z-index:3;display:block;height:calc(1.5em + .75rem);padding:.375rem .75rem;line-height:1.5;color:#495057;content:"Browse";background-color:#e9ecef;border-left:inherit;border-radius:0 .25rem .25rem 0}.custom-range{width:100%;height:1.4rem;padding:0;background-color:transparent;-webkit-appearance:none;-moz-appearance:none;appearance:none}.custom-range:focus{outline:0}.custom-range:focus::-webkit-slider-thumb{box-shadow:0 0 0 1px #fff,0 0 0 .2rem rgba(0,123,255,.25)}.custom-range:focus::-moz-range-thumb{box-shadow:0 0 0 1px #fff,0 0 0 .2rem rgba(0,123,255,.25)}.custom-range:focus::-ms-thumb{box-shadow:0 0 0 1px #fff,0 0 0 .2rem rgba(0,123,255,.25)}.custom-range::-moz-focus-outer{border:0}.custom-range::-webkit-slider-thumb{width:1rem;height:1rem;margin-top:-.25rem;background-color:#007bff;border:0;border-radius:1rem;-webkit-transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;-webkit-appearance:none;appearance:none}@media (prefers-reduced-motion:reduce){.custom-range::-webkit-slider-thumb{-webkit-transition:none;transition:none}}.custom-range::-webkit-slider-thumb:active{background-color:#b3d7ff}.custom-range::-webkit-slider-runnable-track{width:100%;height:.5rem;color:transparent;cursor:pointer;background-color:#dee2e6;border-color:transparent;border-radius:1rem}.custom-range::-moz-range-thumb{width:1rem;height:1rem;background-color:#007bff;border:0;border-radius:1rem;-moz-transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;-moz-appearance:none;appearance:none}@media (prefers-reduced-motion:reduce){.custom-range::-moz-range-thumb{-moz-transition:none;transition:none}}.custom-range::-moz-range-thumb:active{background-color:#b3d7ff}.custom-range::-moz-range-track{width:100%;height:.5rem;color:transparent;cursor:pointer;background-color:#dee2e6;border-color:transparent;border-radius:1rem}.custom-range::-ms-thumb{width:1rem;height:1rem;margin-top:0;margin-right:.2rem;margin-left:.2rem;background-color:#007bff;border:0;border-radius:1rem;-ms-transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;appearance:none}@media (prefers-reduced-motion:reduce){.custom-range::-ms-thumb{-ms-transition:none;transition:none}}.custom-range::-ms-thumb:active{background-color:#b3d7ff}.custom-range::-ms-track{width:100%;height:.5rem;color:transparent;cursor:pointer;background-color:transparent;border-color:transparent;border-width:.5rem}.custom-range::-ms-fill-lower{background-color:#dee2e6;border-radius:1rem}.custom-range::-ms-fill-upper{margin-right:15px;background-color:#dee2e6;border-radius:1rem}.custom-range:disabled::-webkit-slider-thumb{background-color:#adb5bd}.custom-range:disabled::-webkit-slider-runnable-track{cursor:default}.custom-range:disabled::-moz-range-thumb{background-color:#adb5bd}.custom-range:disabled::-moz-range-track{cursor:default}.custom-range:disabled::-ms-thumb{background-color:#adb5bd}.custom-control-label::before,.custom-file-label,.custom-select{transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.custom-control-label::before,.custom-file-label,.custom-select{transition:none}}.nav{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;padding-left:0;margin-bottom:0;list-style:none}.nav-link{display:block;padding:.5rem 1rem}.nav-link:focus,.nav-link:hover{text-decoration:none}.nav-link.disabled{color:#6c757d;pointer-events:none;cursor:default}.nav-tabs{border-bottom:1px solid #dee2e6}.nav-tabs .nav-item{margin-bottom:-1px}.nav-tabs .nav-link{border:1px solid transparent;border-top-left-radius:.25rem;border-top-right-radius:.25rem}.nav-tabs .nav-link:focus,.nav-tabs .nav-link:hover{border-color:#e9ecef #e9ecef #dee2e6}.nav-tabs .nav-link.disabled{color:#6c757d;background-color:transparent;border-color:transparent}.nav-tabs .nav-item.show .nav-link,.nav-tabs .nav-link.active{color:#495057;background-color:#fff;border-color:#dee2e6 #dee2e6 #fff}.nav-tabs .dropdown-menu{margin-top:-1px;border-top-left-radius:0;border-top-right-radius:0}.nav-pills .nav-link{border-radius:.25rem}.nav-pills .nav-link.active,.nav-pills .show>.nav-link{color:#fff;background-color:#007bff}.nav-fill .nav-item,.nav-fill>.nav-link{-ms-flex:1 1 auto;flex:1 1 auto;text-align:center}.nav-justified .nav-item,.nav-justified>.nav-link{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;text-align:center}.tab-content>.tab-pane{display:none}.tab-content>.active{display:block}.navbar{position:relative;display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;-ms-flex-align:center;align-items:center;-ms-flex-pack:justify;justify-content:space-between;padding:.5rem 1rem}.navbar .container,.navbar .container-fluid,.navbar .container-lg,.navbar .container-md,.navbar .container-sm,.navbar .container-xl{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;-ms-flex-align:center;align-items:center;-ms-flex-pack:justify;justify-content:space-between}.navbar-brand{display:inline-block;padding-top:.3125rem;padding-bottom:.3125rem;margin-right:1rem;font-size:1.25rem;line-height:inherit;white-space:nowrap}.navbar-brand:focus,.navbar-brand:hover{text-decoration:none}.navbar-nav{display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column;padding-left:0;margin-bottom:0;list-style:none}.navbar-nav .nav-link{padding-right:0;padding-left:0}.navbar-nav .dropdown-menu{position:static;float:none}.navbar-text{display:inline-block;padding-top:.5rem;padding-bottom:.5rem}.navbar-collapse{-ms-flex-preferred-size:100%;flex-basis:100%;-ms-flex-positive:1;flex-grow:1;-ms-flex-align:center;align-items:center}.navbar-toggler{padding:.25rem .75rem;font-size:1.25rem;line-height:1;background-color:transparent;border:1px solid transparent;border-radius:.25rem}.navbar-toggler:focus,.navbar-toggler:hover{text-decoration:none}.navbar-toggler-icon{display:inline-block;width:1.5em;height:1.5em;vertical-align:middle;content:"";background:no-repeat center center;background-size:100% 100%}@media (max-width:575.98px){.navbar-expand-sm>.container,.navbar-expand-sm>.container-fluid,.navbar-expand-sm>.container-lg,.navbar-expand-sm>.container-md,.navbar-expand-sm>.container-sm,.navbar-expand-sm>.container-xl{padding-right:0;padding-left:0}}@media (min-width:576px){.navbar-expand-sm{-ms-flex-flow:row nowrap;flex-flow:row nowrap;-ms-flex-pack:start;justify-content:flex-start}.navbar-expand-sm .navbar-nav{-ms-flex-direction:row;flex-direction:row}.navbar-expand-sm .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-sm .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-sm>.container,.navbar-expand-sm>.container-fluid,.navbar-expand-sm>.container-lg,.navbar-expand-sm>.container-md,.navbar-expand-sm>.container-sm,.navbar-expand-sm>.container-xl{-ms-flex-wrap:nowrap;flex-wrap:nowrap}.navbar-expand-sm .navbar-collapse{display:-ms-flexbox!important;display:flex!important;-ms-flex-preferred-size:auto;flex-basis:auto}.navbar-expand-sm .navbar-toggler{display:none}}@media (max-width:767.98px){.navbar-expand-md>.container,.navbar-expand-md>.container-fluid,.navbar-expand-md>.container-lg,.navbar-expand-md>.container-md,.navbar-expand-md>.container-sm,.navbar-expand-md>.container-xl{padding-right:0;padding-left:0}}@media (min-width:768px){.navbar-expand-md{-ms-flex-flow:row nowrap;flex-flow:row nowrap;-ms-flex-pack:start;justify-content:flex-start}.navbar-expand-md .navbar-nav{-ms-flex-direction:row;flex-direction:row}.navbar-expand-md .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-md .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-md>.container,.navbar-expand-md>.container-fluid,.navbar-expand-md>.container-lg,.navbar-expand-md>.container-md,.navbar-expand-md>.container-sm,.navbar-expand-md>.container-xl{-ms-flex-wrap:nowrap;flex-wrap:nowrap}.navbar-expand-md .navbar-collapse{display:-ms-flexbox!important;display:flex!important;-ms-flex-preferred-size:auto;flex-basis:auto}.navbar-expand-md .navbar-toggler{display:none}}@media (max-width:991.98px){.navbar-expand-lg>.container,.navbar-expand-lg>.container-fluid,.navbar-expand-lg>.container-lg,.navbar-expand-lg>.container-md,.navbar-expand-lg>.container-sm,.navbar-expand-lg>.container-xl{padding-right:0;padding-left:0}}@media (min-width:992px){.navbar-expand-lg{-ms-flex-flow:row nowrap;flex-flow:row nowrap;-ms-flex-pack:start;justify-content:flex-start}.navbar-expand-lg .navbar-nav{-ms-flex-direction:row;flex-direction:row}.navbar-expand-lg .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-lg .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-lg>.container,.navbar-expand-lg>.container-fluid,.navbar-expand-lg>.container-lg,.navbar-expand-lg>.container-md,.navbar-expand-lg>.container-sm,.navbar-expand-lg>.container-xl{-ms-flex-wrap:nowrap;flex-wrap:nowrap}.navbar-expand-lg .navbar-collapse{display:-ms-flexbox!important;display:flex!important;-ms-flex-preferred-size:auto;flex-basis:auto}.navbar-expand-lg .navbar-toggler{display:none}}@media (max-width:1199.98px){.navbar-expand-xl>.container,.navbar-expand-xl>.container-fluid,.navbar-expand-xl>.container-lg,.navbar-expand-xl>.container-md,.navbar-expand-xl>.container-sm,.navbar-expand-xl>.container-xl{padding-right:0;padding-left:0}}@media (min-width:1200px){.navbar-expand-xl{-ms-flex-flow:row nowrap;flex-flow:row nowrap;-ms-flex-pack:start;justify-content:flex-start}.navbar-expand-xl .navbar-nav{-ms-flex-direction:row;flex-direction:row}.navbar-expand-xl .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-xl .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-xl>.container,.navbar-expand-xl>.container-fluid,.navbar-expand-xl>.container-lg,.navbar-expand-xl>.container-md,.navbar-expand-xl>.container-sm,.navbar-expand-xl>.container-xl{-ms-flex-wrap:nowrap;flex-wrap:nowrap}.navbar-expand-xl .navbar-collapse{display:-ms-flexbox!important;display:flex!important;-ms-flex-preferred-size:auto;flex-basis:auto}.navbar-expand-xl .navbar-toggler{display:none}}.navbar-expand{-ms-flex-flow:row nowrap;flex-flow:row nowrap;-ms-flex-pack:start;justify-content:flex-start}.navbar-expand>.container,.navbar-expand>.container-fluid,.navbar-expand>.container-lg,.navbar-expand>.container-md,.navbar-expand>.container-sm,.navbar-expand>.container-xl{padding-right:0;padding-left:0}.navbar-expand .navbar-nav{-ms-flex-direction:row;flex-direction:row}.navbar-expand .navbar-nav .dropdown-menu{position:absolute}.navbar-expand .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand>.container,.navbar-expand>.container-fluid,.navbar-expand>.container-lg,.navbar-expand>.container-md,.navbar-expand>.container-sm,.navbar-expand>.container-xl{-ms-flex-wrap:nowrap;flex-wrap:nowrap}.navbar-expand .navbar-collapse{display:-ms-flexbox!important;display:flex!important;-ms-flex-preferred-size:auto;flex-basis:auto}.navbar-expand .navbar-toggler{display:none}.navbar-light .navbar-brand{color:rgba(0,0,0,.9)}.navbar-light .navbar-brand:focus,.navbar-light .navbar-brand:hover{color:rgba(0,0,0,.9)}.navbar-light .navbar-nav .nav-link{color:rgba(0,0,0,.5)}.navbar-light .navbar-nav .nav-link:focus,.navbar-light .navbar-nav .nav-link:hover{color:rgba(0,0,0,.7)}.navbar-light .navbar-nav .nav-link.disabled{color:rgba(0,0,0,.3)}.navbar-light .navbar-nav .active>.nav-link,.navbar-light .navbar-nav .nav-link.active,.navbar-light .navbar-nav .nav-link.show,.navbar-light .navbar-nav .show>.nav-link{color:rgba(0,0,0,.9)}.navbar-light .navbar-toggler{color:rgba(0,0,0,.5);border-color:rgba(0,0,0,.1)}.navbar-light .navbar-toggler-icon{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='30' height='30' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%280, 0, 0, 0.5%29' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e")}.navbar-light .navbar-text{color:rgba(0,0,0,.5)}.navbar-light .navbar-text a{color:rgba(0,0,0,.9)}.navbar-light .navbar-text a:focus,.navbar-light .navbar-text a:hover{color:rgba(0,0,0,.9)}.navbar-dark .navbar-brand{color:#fff}.navbar-dark .navbar-brand:focus,.navbar-dark .navbar-brand:hover{color:#fff}.navbar-dark .navbar-nav .nav-link{color:rgba(255,255,255,.5)}.navbar-dark .navbar-nav .nav-link:focus,.navbar-dark .navbar-nav .nav-link:hover{color:rgba(255,255,255,.75)}.navbar-dark .navbar-nav .nav-link.disabled{color:rgba(255,255,255,.25)}.navbar-dark .navbar-nav .active>.nav-link,.navbar-dark .navbar-nav .nav-link.active,.navbar-dark .navbar-nav .nav-link.show,.navbar-dark .navbar-nav .show>.nav-link{color:#fff}.navbar-dark .navbar-toggler{color:rgba(255,255,255,.5);border-color:rgba(255,255,255,.1)}.navbar-dark .navbar-toggler-icon{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='30' height='30' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%28255, 255, 255, 0.5%29' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e")}.navbar-dark .navbar-text{color:rgba(255,255,255,.5)}.navbar-dark .navbar-text a{color:#fff}.navbar-dark .navbar-text a:focus,.navbar-dark .navbar-text a:hover{color:#fff}.card{position:relative;display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column;min-width:0;word-wrap:break-word;background-color:#fff;background-clip:border-box;border:1px solid rgba(0,0,0,.125);border-radius:.25rem}.card>hr{margin-right:0;margin-left:0}.card>.list-group{border-top:inherit;border-bottom:inherit}.card>.list-group:first-child{border-top-width:0;border-top-left-radius:calc(.25rem - 1px);border-top-right-radius:calc(.25rem - 1px)}.card>.list-group:last-child{border-bottom-width:0;border-bottom-right-radius:calc(.25rem - 1px);border-bottom-left-radius:calc(.25rem - 1px)}.card>.card-header+.list-group,.card>.list-group+.card-footer{border-top:0}.card-body{-ms-flex:1 1 auto;flex:1 1 auto;min-height:1px;padding:1.25rem}.card-title{margin-bottom:.75rem}.card-subtitle{margin-top:-.375rem;margin-bottom:0}.card-text:last-child{margin-bottom:0}.card-link:hover{text-decoration:none}.card-link+.card-link{margin-left:1.25rem}.card-header{padding:.75rem 1.25rem;margin-bottom:0;background-color:rgba(0,0,0,.03);border-bottom:1px solid rgba(0,0,0,.125)}.card-header:first-child{border-radius:calc(.25rem - 1px) calc(.25rem - 1px) 0 0}.card-footer{padding:.75rem 1.25rem;background-color:rgba(0,0,0,.03);border-top:1px solid rgba(0,0,0,.125)}.card-footer:last-child{border-radius:0 0 calc(.25rem - 1px) calc(.25rem - 1px)}.card-header-tabs{margin-right:-.625rem;margin-bottom:-.75rem;margin-left:-.625rem;border-bottom:0}.card-header-pills{margin-right:-.625rem;margin-left:-.625rem}.card-img-overlay{position:absolute;top:0;right:0;bottom:0;left:0;padding:1.25rem;border-radius:calc(.25rem - 1px)}.card-img,.card-img-bottom,.card-img-top{-ms-flex-negative:0;flex-shrink:0;width:100%}.card-img,.card-img-top{border-top-left-radius:calc(.25rem - 1px);border-top-right-radius:calc(.25rem - 1px)}.card-img,.card-img-bottom{border-bottom-right-radius:calc(.25rem - 1px);border-bottom-left-radius:calc(.25rem - 1px)}.card-deck .card{margin-bottom:15px}@media (min-width:576px){.card-deck{display:-ms-flexbox;display:flex;-ms-flex-flow:row wrap;flex-flow:row wrap;margin-right:-15px;margin-left:-15px}.card-deck .card{-ms-flex:1 0 0%;flex:1 0 0%;margin-right:15px;margin-bottom:0;margin-left:15px}}.card-group>.card{margin-bottom:15px}@media (min-width:576px){.card-group{display:-ms-flexbox;display:flex;-ms-flex-flow:row wrap;flex-flow:row wrap}.card-group>.card{-ms-flex:1 0 0%;flex:1 0 0%;margin-bottom:0}.card-group>.card+.card{margin-left:0;border-left:0}.card-group>.card:not(:last-child){border-top-right-radius:0;border-bottom-right-radius:0}.card-group>.card:not(:last-child) .card-header,.card-group>.card:not(:last-child) .card-img-top{border-top-right-radius:0}.card-group>.card:not(:last-child) .card-footer,.card-group>.card:not(:last-child) .card-img-bottom{border-bottom-right-radius:0}.card-group>.card:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.card-group>.card:not(:first-child) .card-header,.card-group>.card:not(:first-child) .card-img-top{border-top-left-radius:0}.card-group>.card:not(:first-child) .card-footer,.card-group>.card:not(:first-child) .card-img-bottom{border-bottom-left-radius:0}}.card-columns .card{margin-bottom:.75rem}@media (min-width:576px){.card-columns{-webkit-column-count:3;-moz-column-count:3;column-count:3;-webkit-column-gap:1.25rem;-moz-column-gap:1.25rem;column-gap:1.25rem;orphans:1;widows:1}.card-columns .card{display:inline-block;width:100%}}.accordion{overflow-anchor:none}.accordion>.card{overflow:hidden}.accordion>.card:not(:last-of-type){border-bottom:0;border-bottom-right-radius:0;border-bottom-left-radius:0}.accordion>.card:not(:first-of-type){border-top-left-radius:0;border-top-right-radius:0}.accordion>.card>.card-header{border-radius:0;margin-bottom:-1px}.breadcrumb{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;padding:.75rem 1rem;margin-bottom:1rem;list-style:none;background-color:#e9ecef;border-radius:.25rem}.breadcrumb-item{display:-ms-flexbox;display:flex}.breadcrumb-item+.breadcrumb-item{padding-left:.5rem}.breadcrumb-item+.breadcrumb-item::before{display:inline-block;padding-right:.5rem;color:#6c757d;content:"/"}.breadcrumb-item+.breadcrumb-item:hover::before{text-decoration:underline}.breadcrumb-item+.breadcrumb-item:hover::before{text-decoration:none}.breadcrumb-item.active{color:#6c757d}.pagination{display:-ms-flexbox;display:flex;padding-left:0;list-style:none;border-radius:.25rem}.page-link{position:relative;display:block;padding:.5rem .75rem;margin-left:-1px;line-height:1.25;color:#007bff;background-color:#fff;border:1px solid #dee2e6}.page-link:hover{z-index:2;color:#0056b3;text-decoration:none;background-color:#e9ecef;border-color:#dee2e6}.page-link:focus{z-index:3;outline:0;box-shadow:0 0 0 .2rem rgba(0,123,255,.25)}.page-item:first-child .page-link{margin-left:0;border-top-left-radius:.25rem;border-bottom-left-radius:.25rem}.page-item:last-child .page-link{border-top-right-radius:.25rem;border-bottom-right-radius:.25rem}.page-item.active .page-link{z-index:3;color:#fff;background-color:#007bff;border-color:#007bff}.page-item.disabled .page-link{color:#6c757d;pointer-events:none;cursor:auto;background-color:#fff;border-color:#dee2e6}.pagination-lg .page-link{padding:.75rem 1.5rem;font-size:1.25rem;line-height:1.5}.pagination-lg .page-item:first-child .page-link{border-top-left-radius:.3rem;border-bottom-left-radius:.3rem}.pagination-lg .page-item:last-child .page-link{border-top-right-radius:.3rem;border-bottom-right-radius:.3rem}.pagination-sm .page-link{padding:.25rem .5rem;font-size:.875rem;line-height:1.5}.pagination-sm .page-item:first-child .page-link{border-top-left-radius:.2rem;border-bottom-left-radius:.2rem}.pagination-sm .page-item:last-child .page-link{border-top-right-radius:.2rem;border-bottom-right-radius:.2rem}.badge{display:inline-block;padding:.25em .4em;font-size:75%;font-weight:700;line-height:1;text-align:center;white-space:nowrap;vertical-align:baseline;border-radius:.25rem;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.badge{transition:none}}a.badge:focus,a.badge:hover{text-decoration:none}.badge:empty{display:none}.btn .badge{position:relative;top:-1px}.badge-pill{padding-right:.6em;padding-left:.6em;border-radius:10rem}.badge-primary{color:#fff;background-color:#007bff}a.badge-primary:focus,a.badge-primary:hover{color:#fff;background-color:#0062cc}a.badge-primary.focus,a.badge-primary:focus{outline:0;box-shadow:0 0 0 .2rem rgba(0,123,255,.5)}.badge-secondary{color:#fff;background-color:#6c757d}a.badge-secondary:focus,a.badge-secondary:hover{color:#fff;background-color:#545b62}a.badge-secondary.focus,a.badge-secondary:focus{outline:0;box-shadow:0 0 0 .2rem rgba(108,117,125,.5)}.badge-success{color:#fff;background-color:#28a745}a.badge-success:focus,a.badge-success:hover{color:#fff;background-color:#1e7e34}a.badge-success.focus,a.badge-success:focus{outline:0;box-shadow:0 0 0 .2rem rgba(40,167,69,.5)}.badge-info{color:#fff;background-color:#17a2b8}a.badge-info:focus,a.badge-info:hover{color:#fff;background-color:#117a8b}a.badge-info.focus,a.badge-info:focus{outline:0;box-shadow:0 0 0 .2rem rgba(23,162,184,.5)}.badge-warning{color:#212529;background-color:#ffc107}a.badge-warning:focus,a.badge-warning:hover{color:#212529;background-color:#d39e00}a.badge-warning.focus,a.badge-warning:focus{outline:0;box-shadow:0 0 0 .2rem rgba(255,193,7,.5)}.badge-danger{color:#fff;background-color:#dc3545}a.badge-danger:focus,a.badge-danger:hover{color:#fff;background-color:#bd2130}a.badge-danger.focus,a.badge-danger:focus{outline:0;box-shadow:0 0 0 .2rem rgba(220,53,69,.5)}.badge-light{color:#212529;background-color:#f8f9fa}a.badge-light:focus,a.badge-light:hover{color:#212529;background-color:#dae0e5}a.badge-light.focus,a.badge-light:focus{outline:0;box-shadow:0 0 0 .2rem rgba(248,249,250,.5)}.badge-dark{color:#fff;background-color:#343a40}a.badge-dark:focus,a.badge-dark:hover{color:#fff;background-color:#1d2124}a.badge-dark.focus,a.badge-dark:focus{outline:0;box-shadow:0 0 0 .2rem rgba(52,58,64,.5)}.jumbotron{padding:2rem 1rem;margin-bottom:2rem;background-color:#e9ecef;border-radius:.3rem}@media (min-width:576px){.jumbotron{padding:4rem 2rem}}.jumbotron-fluid{padding-right:0;padding-left:0;border-radius:0}.alert{position:relative;padding:.75rem 1.25rem;margin-bottom:1rem;border:1px solid transparent;border-radius:.25rem}.alert-heading{color:inherit}.alert-link{font-weight:700}.alert-dismissible{padding-right:4rem}.alert-dismissible .close{position:absolute;top:0;right:0;z-index:2;padding:.75rem 1.25rem;color:inherit}.alert-primary{color:#004085;background-color:#cce5ff;border-color:#b8daff}.alert-primary hr{border-top-color:#9fcdff}.alert-primary .alert-link{color:#002752}.alert-secondary{color:#383d41;background-color:#e2e3e5;border-color:#d6d8db}.alert-secondary hr{border-top-color:#c8cbcf}.alert-secondary .alert-link{color:#202326}.alert-success{color:#155724;background-color:#d4edda;border-color:#c3e6cb}.alert-success hr{border-top-color:#b1dfbb}.alert-success .alert-link{color:#0b2e13}.alert-info{color:#0c5460;background-color:#d1ecf1;border-color:#bee5eb}.alert-info hr{border-top-color:#abdde5}.alert-info .alert-link{color:#062c33}.alert-warning{color:#856404;background-color:#fff3cd;border-color:#ffeeba}.alert-warning hr{border-top-color:#ffe8a1}.alert-warning .alert-link{color:#533f03}.alert-danger{color:#721c24;background-color:#f8d7da;border-color:#f5c6cb}.alert-danger hr{border-top-color:#f1b0b7}.alert-danger .alert-link{color:#491217}.alert-light{color:#818182;background-color:#fefefe;border-color:#fdfdfe}.alert-light hr{border-top-color:#ececf6}.alert-light .alert-link{color:#686868}.alert-dark{color:#1b1e21;background-color:#d6d8d9;border-color:#c6c8ca}.alert-dark hr{border-top-color:#b9bbbe}.alert-dark .alert-link{color:#040505}@-webkit-keyframes progress-bar-stripes{from{background-position:1rem 0}to{background-position:0 0}}@keyframes progress-bar-stripes{from{background-position:1rem 0}to{background-position:0 0}}.progress{display:-ms-flexbox;display:flex;height:1rem;overflow:hidden;line-height:0;font-size:.75rem;background-color:#e9ecef;border-radius:.25rem}.progress-bar{display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column;-ms-flex-pack:center;justify-content:center;overflow:hidden;color:#fff;text-align:center;white-space:nowrap;background-color:#007bff;transition:width .6s ease}@media (prefers-reduced-motion:reduce){.progress-bar{transition:none}}.progress-bar-striped{background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-size:1rem 1rem}.progress-bar-animated{-webkit-animation:progress-bar-stripes 1s linear infinite;animation:progress-bar-stripes 1s linear infinite}@media (prefers-reduced-motion:reduce){.progress-bar-animated{-webkit-animation:none;animation:none}}.media{display:-ms-flexbox;display:flex;-ms-flex-align:start;align-items:flex-start}.media-body{-ms-flex:1;flex:1}.list-group{display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column;padding-left:0;margin-bottom:0;border-radius:.25rem}.list-group-item-action{width:100%;color:#495057;text-align:inherit}.list-group-item-action:focus,.list-group-item-action:hover{z-index:1;color:#495057;text-decoration:none;background-color:#f8f9fa}.list-group-item-action:active{color:#212529;background-color:#e9ecef}.list-group-item{position:relative;display:block;padding:.75rem 1.25rem;background-color:#fff;border:1px solid rgba(0,0,0,.125)}.list-group-item:first-child{border-top-left-radius:inherit;border-top-right-radius:inherit}.list-group-item:last-child{border-bottom-right-radius:inherit;border-bottom-left-radius:inherit}.list-group-item.disabled,.list-group-item:disabled{color:#6c757d;pointer-events:none;background-color:#fff}.list-group-item.active{z-index:2;color:#fff;background-color:#007bff;border-color:#007bff}.list-group-item+.list-group-item{border-top-width:0}.list-group-item+.list-group-item.active{margin-top:-1px;border-top-width:1px}.list-group-horizontal{-ms-flex-direction:row;flex-direction:row}.list-group-horizontal>.list-group-item:first-child{border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal>.list-group-item:last-child{border-top-right-radius:.25rem;border-bottom-left-radius:0}.list-group-horizontal>.list-group-item.active{margin-top:0}.list-group-horizontal>.list-group-item+.list-group-item{border-top-width:1px;border-left-width:0}.list-group-horizontal>.list-group-item+.list-group-item.active{margin-left:-1px;border-left-width:1px}@media (min-width:576px){.list-group-horizontal-sm{-ms-flex-direction:row;flex-direction:row}.list-group-horizontal-sm>.list-group-item:first-child{border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal-sm>.list-group-item:last-child{border-top-right-radius:.25rem;border-bottom-left-radius:0}.list-group-horizontal-sm>.list-group-item.active{margin-top:0}.list-group-horizontal-sm>.list-group-item+.list-group-item{border-top-width:1px;border-left-width:0}.list-group-horizontal-sm>.list-group-item+.list-group-item.active{margin-left:-1px;border-left-width:1px}}@media (min-width:768px){.list-group-horizontal-md{-ms-flex-direction:row;flex-direction:row}.list-group-horizontal-md>.list-group-item:first-child{border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal-md>.list-group-item:last-child{border-top-right-radius:.25rem;border-bottom-left-radius:0}.list-group-horizontal-md>.list-group-item.active{margin-top:0}.list-group-horizontal-md>.list-group-item+.list-group-item{border-top-width:1px;border-left-width:0}.list-group-horizontal-md>.list-group-item+.list-group-item.active{margin-left:-1px;border-left-width:1px}}@media (min-width:992px){.list-group-horizontal-lg{-ms-flex-direction:row;flex-direction:row}.list-group-horizontal-lg>.list-group-item:first-child{border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal-lg>.list-group-item:last-child{border-top-right-radius:.25rem;border-bottom-left-radius:0}.list-group-horizontal-lg>.list-group-item.active{margin-top:0}.list-group-horizontal-lg>.list-group-item+.list-group-item{border-top-width:1px;border-left-width:0}.list-group-horizontal-lg>.list-group-item+.list-group-item.active{margin-left:-1px;border-left-width:1px}}@media (min-width:1200px){.list-group-horizontal-xl{-ms-flex-direction:row;flex-direction:row}.list-group-horizontal-xl>.list-group-item:first-child{border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal-xl>.list-group-item:last-child{border-top-right-radius:.25rem;border-bottom-left-radius:0}.list-group-horizontal-xl>.list-group-item.active{margin-top:0}.list-group-horizontal-xl>.list-group-item+.list-group-item{border-top-width:1px;border-left-width:0}.list-group-horizontal-xl>.list-group-item+.list-group-item.active{margin-left:-1px;border-left-width:1px}}.list-group-flush{border-radius:0}.list-group-flush>.list-group-item{border-width:0 0 1px}.list-group-flush>.list-group-item:last-child{border-bottom-width:0}.list-group-item-primary{color:#004085;background-color:#b8daff}.list-group-item-primary.list-group-item-action:focus,.list-group-item-primary.list-group-item-action:hover{color:#004085;background-color:#9fcdff}.list-group-item-primary.list-group-item-action.active{color:#fff;background-color:#004085;border-color:#004085}.list-group-item-secondary{color:#383d41;background-color:#d6d8db}.list-group-item-secondary.list-group-item-action:focus,.list-group-item-secondary.list-group-item-action:hover{color:#383d41;background-color:#c8cbcf}.list-group-item-secondary.list-group-item-action.active{color:#fff;background-color:#383d41;border-color:#383d41}.list-group-item-success{color:#155724;background-color:#c3e6cb}.list-group-item-success.list-group-item-action:focus,.list-group-item-success.list-group-item-action:hover{color:#155724;background-color:#b1dfbb}.list-group-item-success.list-group-item-action.active{color:#fff;background-color:#155724;border-color:#155724}.list-group-item-info{color:#0c5460;background-color:#bee5eb}.list-group-item-info.list-group-item-action:focus,.list-group-item-info.list-group-item-action:hover{color:#0c5460;background-color:#abdde5}.list-group-item-info.list-group-item-action.active{color:#fff;background-color:#0c5460;border-color:#0c5460}.list-group-item-warning{color:#856404;background-color:#ffeeba}.list-group-item-warning.list-group-item-action:focus,.list-group-item-warning.list-group-item-action:hover{color:#856404;background-color:#ffe8a1}.list-group-item-warning.list-group-item-action.active{color:#fff;background-color:#856404;border-color:#856404}.list-group-item-danger{color:#721c24;background-color:#f5c6cb}.list-group-item-danger.list-group-item-action:focus,.list-group-item-danger.list-group-item-action:hover{color:#721c24;background-color:#f1b0b7}.list-group-item-danger.list-group-item-action.active{color:#fff;background-color:#721c24;border-color:#721c24}.list-group-item-light{color:#818182;background-color:#fdfdfe}.list-group-item-light.list-group-item-action:focus,.list-group-item-light.list-group-item-action:hover{color:#818182;background-color:#ececf6}.list-group-item-light.list-group-item-action.active{color:#fff;background-color:#818182;border-color:#818182}.list-group-item-dark{color:#1b1e21;background-color:#c6c8ca}.list-group-item-dark.list-group-item-action:focus,.list-group-item-dark.list-group-item-action:hover{color:#1b1e21;background-color:#b9bbbe}.list-group-item-dark.list-group-item-action.active{color:#fff;background-color:#1b1e21;border-color:#1b1e21}.close{float:right;font-size:1.5rem;font-weight:700;line-height:1;color:#000;text-shadow:0 1px 0 #fff;opacity:.5}.close:hover{color:#000;text-decoration:none}.close:not(:disabled):not(.disabled):focus,.close:not(:disabled):not(.disabled):hover{opacity:.75}button.close{padding:0;background-color:transparent;border:0}a.close.disabled{pointer-events:none}.toast{-ms-flex-preferred-size:350px;flex-basis:350px;max-width:350px;font-size:.875rem;background-color:rgba(255,255,255,.85);background-clip:padding-box;border:1px solid rgba(0,0,0,.1);box-shadow:0 .25rem .75rem rgba(0,0,0,.1);opacity:0;border-radius:.25rem}.toast:not(:last-child){margin-bottom:.75rem}.toast.showing{opacity:1}.toast.show{display:block;opacity:1}.toast.hide{display:none}.toast-header{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;padding:.25rem .75rem;color:#6c757d;background-color:rgba(255,255,255,.85);background-clip:padding-box;border-bottom:1px solid rgba(0,0,0,.05);border-top-left-radius:calc(.25rem - 1px);border-top-right-radius:calc(.25rem - 1px)}.toast-body{padding:.75rem}.modal-open{overflow:hidden}.modal-open .modal{overflow-x:hidden;overflow-y:auto}.modal{position:fixed;top:0;left:0;z-index:1050;display:none;width:100%;height:100%;overflow:hidden;outline:0}.modal-dialog{position:relative;width:auto;margin:.5rem;pointer-events:none}.modal.fade .modal-dialog{transition:-webkit-transform .3s ease-out;transition:transform .3s ease-out;transition:transform .3s ease-out,-webkit-transform .3s ease-out;-webkit-transform:translate(0,-50px);transform:translate(0,-50px)}@media (prefers-reduced-motion:reduce){.modal.fade .modal-dialog{transition:none}}.modal.show .modal-dialog{-webkit-transform:none;transform:none}.modal.modal-static .modal-dialog{-webkit-transform:scale(1.02);transform:scale(1.02)}.modal-dialog-scrollable{display:-ms-flexbox;display:flex;max-height:calc(100% - 1rem)}.modal-dialog-scrollable .modal-content{max-height:calc(100vh - 1rem);overflow:hidden}.modal-dialog-scrollable .modal-footer,.modal-dialog-scrollable .modal-header{-ms-flex-negative:0;flex-shrink:0}.modal-dialog-scrollable .modal-body{overflow-y:auto}.modal-dialog-centered{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;min-height:calc(100% - 1rem)}.modal-dialog-centered::before{display:block;height:calc(100vh - 1rem);height:-webkit-min-content;height:-moz-min-content;height:min-content;content:""}.modal-dialog-centered.modal-dialog-scrollable{-ms-flex-direction:column;flex-direction:column;-ms-flex-pack:center;justify-content:center;height:100%}.modal-dialog-centered.modal-dialog-scrollable .modal-content{max-height:none}.modal-dialog-centered.modal-dialog-scrollable::before{content:none}.modal-content{position:relative;display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column;width:100%;pointer-events:auto;background-color:#fff;background-clip:padding-box;border:1px solid rgba(0,0,0,.2);border-radius:.3rem;outline:0}.modal-backdrop{position:fixed;top:0;left:0;z-index:1040;width:100vw;height:100vh;background-color:#000}.modal-backdrop.fade{opacity:0}.modal-backdrop.show{opacity:.5}.modal-header{display:-ms-flexbox;display:flex;-ms-flex-align:start;align-items:flex-start;-ms-flex-pack:justify;justify-content:space-between;padding:1rem 1rem;border-bottom:1px solid #dee2e6;border-top-left-radius:calc(.3rem - 1px);border-top-right-radius:calc(.3rem - 1px)}.modal-header .close{padding:1rem 1rem;margin:-1rem -1rem -1rem auto}.modal-title{margin-bottom:0;line-height:1.5}.modal-body{position:relative;-ms-flex:1 1 auto;flex:1 1 auto;padding:1rem}.modal-footer{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;-ms-flex-align:center;align-items:center;-ms-flex-pack:end;justify-content:flex-end;padding:.75rem;border-top:1px solid #dee2e6;border-bottom-right-radius:calc(.3rem - 1px);border-bottom-left-radius:calc(.3rem - 1px)}.modal-footer>*{margin:.25rem}.modal-scrollbar-measure{position:absolute;top:-9999px;width:50px;height:50px;overflow:scroll}@media (min-width:576px){.modal-dialog{max-width:500px;margin:1.75rem auto}.modal-dialog-scrollable{max-height:calc(100% - 3.5rem)}.modal-dialog-scrollable .modal-content{max-height:calc(100vh - 3.5rem)}.modal-dialog-centered{min-height:calc(100% - 3.5rem)}.modal-dialog-centered::before{height:calc(100vh - 3.5rem);height:-webkit-min-content;height:-moz-min-content;height:min-content}.modal-sm{max-width:300px}}@media (min-width:992px){.modal-lg,.modal-xl{max-width:800px}}@media (min-width:1200px){.modal-xl{max-width:1140px}}.tooltip{position:absolute;z-index:1070;display:block;margin:0;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";font-style:normal;font-weight:400;line-height:1.5;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;white-space:normal;line-break:auto;font-size:.875rem;word-wrap:break-word;opacity:0}.tooltip.show{opacity:.9}.tooltip .arrow{position:absolute;display:block;width:.8rem;height:.4rem}.tooltip .arrow::before{position:absolute;content:"";border-color:transparent;border-style:solid}.bs-tooltip-auto[x-placement^=top],.bs-tooltip-top{padding:.4rem 0}.bs-tooltip-auto[x-placement^=top] .arrow,.bs-tooltip-top .arrow{bottom:0}.bs-tooltip-auto[x-placement^=top] .arrow::before,.bs-tooltip-top .arrow::before{top:0;border-width:.4rem .4rem 0;border-top-color:#000}.bs-tooltip-auto[x-placement^=right],.bs-tooltip-right{padding:0 .4rem}.bs-tooltip-auto[x-placement^=right] .arrow,.bs-tooltip-right .arrow{left:0;width:.4rem;height:.8rem}.bs-tooltip-auto[x-placement^=right] .arrow::before,.bs-tooltip-right .arrow::before{right:0;border-width:.4rem .4rem .4rem 0;border-right-color:#000}.bs-tooltip-auto[x-placement^=bottom],.bs-tooltip-bottom{padding:.4rem 0}.bs-tooltip-auto[x-placement^=bottom] .arrow,.bs-tooltip-bottom .arrow{top:0}.bs-tooltip-auto[x-placement^=bottom] .arrow::before,.bs-tooltip-bottom .arrow::before{bottom:0;border-width:0 .4rem .4rem;border-bottom-color:#000}.bs-tooltip-auto[x-placement^=left],.bs-tooltip-left{padding:0 .4rem}.bs-tooltip-auto[x-placement^=left] .arrow,.bs-tooltip-left .arrow{right:0;width:.4rem;height:.8rem}.bs-tooltip-auto[x-placement^=left] .arrow::before,.bs-tooltip-left .arrow::before{left:0;border-width:.4rem 0 .4rem .4rem;border-left-color:#000}.tooltip-inner{max-width:200px;padding:.25rem .5rem;color:#fff;text-align:center;background-color:#000;border-radius:.25rem}.popover{position:absolute;top:0;left:0;z-index:1060;display:block;max-width:276px;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";font-style:normal;font-weight:400;line-height:1.5;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;white-space:normal;line-break:auto;font-size:.875rem;word-wrap:break-word;background-color:#fff;background-clip:padding-box;border:1px solid rgba(0,0,0,.2);border-radius:.3rem}.popover .arrow{position:absolute;display:block;width:1rem;height:.5rem;margin:0 .3rem}.popover .arrow::after,.popover .arrow::before{position:absolute;display:block;content:"";border-color:transparent;border-style:solid}.bs-popover-auto[x-placement^=top],.bs-popover-top{margin-bottom:.5rem}.bs-popover-auto[x-placement^=top]>.arrow,.bs-popover-top>.arrow{bottom:calc(-.5rem - 1px)}.bs-popover-auto[x-placement^=top]>.arrow::before,.bs-popover-top>.arrow::before{bottom:0;border-width:.5rem .5rem 0;border-top-color:rgba(0,0,0,.25)}.bs-popover-auto[x-placement^=top]>.arrow::after,.bs-popover-top>.arrow::after{bottom:1px;border-width:.5rem .5rem 0;border-top-color:#fff}.bs-popover-auto[x-placement^=right],.bs-popover-right{margin-left:.5rem}.bs-popover-auto[x-placement^=right]>.arrow,.bs-popover-right>.arrow{left:calc(-.5rem - 1px);width:.5rem;height:1rem;margin:.3rem 0}.bs-popover-auto[x-placement^=right]>.arrow::before,.bs-popover-right>.arrow::before{left:0;border-width:.5rem .5rem .5rem 0;border-right-color:rgba(0,0,0,.25)}.bs-popover-auto[x-placement^=right]>.arrow::after,.bs-popover-right>.arrow::after{left:1px;border-width:.5rem .5rem .5rem 0;border-right-color:#fff}.bs-popover-auto[x-placement^=bottom],.bs-popover-bottom{margin-top:.5rem}.bs-popover-auto[x-placement^=bottom]>.arrow,.bs-popover-bottom>.arrow{top:calc(-.5rem - 1px)}.bs-popover-auto[x-placement^=bottom]>.arrow::before,.bs-popover-bottom>.arrow::before{top:0;border-width:0 .5rem .5rem .5rem;border-bottom-color:rgba(0,0,0,.25)}.bs-popover-auto[x-placement^=bottom]>.arrow::after,.bs-popover-bottom>.arrow::after{top:1px;border-width:0 .5rem .5rem .5rem;border-bottom-color:#fff}.bs-popover-auto[x-placement^=bottom] .popover-header::before,.bs-popover-bottom .popover-header::before{position:absolute;top:0;left:50%;display:block;width:1rem;margin-left:-.5rem;content:"";border-bottom:1px solid #f7f7f7}.bs-popover-auto[x-placement^=left],.bs-popover-left{margin-right:.5rem}.bs-popover-auto[x-placement^=left]>.arrow,.bs-popover-left>.arrow{right:calc(-.5rem - 1px);width:.5rem;height:1rem;margin:.3rem 0}.bs-popover-auto[x-placement^=left]>.arrow::before,.bs-popover-left>.arrow::before{right:0;border-width:.5rem 0 .5rem .5rem;border-left-color:rgba(0,0,0,.25)}.bs-popover-auto[x-placement^=left]>.arrow::after,.bs-popover-left>.arrow::after{right:1px;border-width:.5rem 0 .5rem .5rem;border-left-color:#fff}.popover-header{padding:.5rem .75rem;margin-bottom:0;font-size:1rem;background-color:#f7f7f7;border-bottom:1px solid #ebebeb;border-top-left-radius:calc(.3rem - 1px);border-top-right-radius:calc(.3rem - 1px)}.popover-header:empty{display:none}.popover-body{padding:.5rem .75rem;color:#212529}.carousel{position:relative}.carousel.pointer-event{-ms-touch-action:pan-y;touch-action:pan-y}.carousel-inner{position:relative;width:100%;overflow:hidden}.carousel-inner::after{display:block;clear:both;content:""}.carousel-item{position:relative;display:none;float:left;width:100%;margin-right:-100%;-webkit-backface-visibility:hidden;backface-visibility:hidden;transition:-webkit-transform .6s ease-in-out;transition:transform .6s ease-in-out;transition:transform .6s ease-in-out,-webkit-transform .6s ease-in-out}@media (prefers-reduced-motion:reduce){.carousel-item{transition:none}}.carousel-item-next,.carousel-item-prev,.carousel-item.active{display:block}.active.carousel-item-right,.carousel-item-next:not(.carousel-item-left){-webkit-transform:translateX(100%);transform:translateX(100%)}.active.carousel-item-left,.carousel-item-prev:not(.carousel-item-right){-webkit-transform:translateX(-100%);transform:translateX(-100%)}.carousel-fade .carousel-item{opacity:0;transition-property:opacity;-webkit-transform:none;transform:none}.carousel-fade .carousel-item-next.carousel-item-left,.carousel-fade .carousel-item-prev.carousel-item-right,.carousel-fade .carousel-item.active{z-index:1;opacity:1}.carousel-fade .active.carousel-item-left,.carousel-fade .active.carousel-item-right{z-index:0;opacity:0;transition:opacity 0s .6s}@media (prefers-reduced-motion:reduce){.carousel-fade .active.carousel-item-left,.carousel-fade .active.carousel-item-right{transition:none}}.carousel-control-next,.carousel-control-prev{position:absolute;top:0;bottom:0;z-index:1;display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;-ms-flex-pack:center;justify-content:center;width:15%;color:#fff;text-align:center;opacity:.5;transition:opacity .15s ease}@media (prefers-reduced-motion:reduce){.carousel-control-next,.carousel-control-prev{transition:none}}.carousel-control-next:focus,.carousel-control-next:hover,.carousel-control-prev:focus,.carousel-control-prev:hover{color:#fff;text-decoration:none;outline:0;opacity:.9}.carousel-control-prev{left:0}.carousel-control-next{right:0}.carousel-control-next-icon,.carousel-control-prev-icon{display:inline-block;width:20px;height:20px;background:no-repeat 50%/100% 100%}.carousel-control-prev-icon{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='%23fff' width='8' height='8' viewBox='0 0 8 8'%3e%3cpath d='M5.25 0l-4 4 4 4 1.5-1.5L4.25 4l2.5-2.5L5.25 0z'/%3e%3c/svg%3e")}.carousel-control-next-icon{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='%23fff' width='8' height='8' viewBox='0 0 8 8'%3e%3cpath d='M2.75 0l-1.5 1.5L3.75 4l-2.5 2.5L2.75 8l4-4-4-4z'/%3e%3c/svg%3e")}.carousel-indicators{position:absolute;right:0;bottom:0;left:0;z-index:15;display:-ms-flexbox;display:flex;-ms-flex-pack:center;justify-content:center;padding-left:0;margin-right:15%;margin-left:15%;list-style:none}.carousel-indicators li{box-sizing:content-box;-ms-flex:0 1 auto;flex:0 1 auto;width:30px;height:3px;margin-right:3px;margin-left:3px;text-indent:-999px;cursor:pointer;background-color:#fff;background-clip:padding-box;border-top:10px solid transparent;border-bottom:10px solid transparent;opacity:.5;transition:opacity .6s ease}@media (prefers-reduced-motion:reduce){.carousel-indicators li{transition:none}}.carousel-indicators .active{opacity:1}.carousel-caption{position:absolute;right:15%;bottom:20px;left:15%;z-index:10;padding-top:20px;padding-bottom:20px;color:#fff;text-align:center}@-webkit-keyframes spinner-border{to{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}@keyframes spinner-border{to{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}.spinner-border{display:inline-block;width:2rem;height:2rem;vertical-align:text-bottom;border:.25em solid currentColor;border-right-color:transparent;border-radius:50%;-webkit-animation:spinner-border .75s linear infinite;animation:spinner-border .75s linear infinite}.spinner-border-sm{width:1rem;height:1rem;border-width:.2em}@-webkit-keyframes spinner-grow{0%{-webkit-transform:scale(0);transform:scale(0)}50%{opacity:1;-webkit-transform:none;transform:none}}@keyframes spinner-grow{0%{-webkit-transform:scale(0);transform:scale(0)}50%{opacity:1;-webkit-transform:none;transform:none}}.spinner-grow{display:inline-block;width:2rem;height:2rem;vertical-align:text-bottom;background-color:currentColor;border-radius:50%;opacity:0;-webkit-animation:spinner-grow .75s linear infinite;animation:spinner-grow .75s linear infinite}.spinner-grow-sm{width:1rem;height:1rem}.align-baseline{vertical-align:baseline!important}.align-top{vertical-align:top!important}.align-middle{vertical-align:middle!important}.align-bottom{vertical-align:bottom!important}.align-text-bottom{vertical-align:text-bottom!important}.align-text-top{vertical-align:text-top!important}.bg-primary{background-color:#007bff!important}a.bg-primary:focus,a.bg-primary:hover,button.bg-primary:focus,button.bg-primary:hover{background-color:#0062cc!important}.bg-secondary{background-color:#6c757d!important}a.bg-secondary:focus,a.bg-secondary:hover,button.bg-secondary:focus,button.bg-secondary:hover{background-color:#545b62!important}.bg-success{background-color:#28a745!important}a.bg-success:focus,a.bg-success:hover,button.bg-success:focus,button.bg-success:hover{background-color:#1e7e34!important}.bg-info{background-color:#17a2b8!important}a.bg-info:focus,a.bg-info:hover,button.bg-info:focus,button.bg-info:hover{background-color:#117a8b!important}.bg-warning{background-color:#ffc107!important}a.bg-warning:focus,a.bg-warning:hover,button.bg-warning:focus,button.bg-warning:hover{background-color:#d39e00!important}.bg-danger{background-color:#dc3545!important}a.bg-danger:focus,a.bg-danger:hover,button.bg-danger:focus,button.bg-danger:hover{background-color:#bd2130!important}.bg-light{background-color:#f8f9fa!important}a.bg-light:focus,a.bg-light:hover,button.bg-light:focus,button.bg-light:hover{background-color:#dae0e5!important}.bg-dark{background-color:#343a40!important}a.bg-dark:focus,a.bg-dark:hover,button.bg-dark:focus,button.bg-dark:hover{background-color:#1d2124!important}.bg-white{background-color:#fff!important}.bg-transparent{background-color:transparent!important}.border{border:1px solid #dee2e6!important}.border-top{border-top:1px solid #dee2e6!important}.border-right{border-right:1px solid #dee2e6!important}.border-bottom{border-bottom:1px solid #dee2e6!important}.border-left{border-left:1px solid #dee2e6!important}.border-0{border:0!important}.border-top-0{border-top:0!important}.border-right-0{border-right:0!important}.border-bottom-0{border-bottom:0!important}.border-left-0{border-left:0!important}.border-primary{border-color:#007bff!important}.border-secondary{border-color:#6c757d!important}.border-success{border-color:#28a745!important}.border-info{border-color:#17a2b8!important}.border-warning{border-color:#ffc107!important}.border-danger{border-color:#dc3545!important}.border-light{border-color:#f8f9fa!important}.border-dark{border-color:#343a40!important}.border-white{border-color:#fff!important}.rounded-sm{border-radius:.2rem!important}.rounded{border-radius:.25rem!important}.rounded-top{border-top-left-radius:.25rem!important;border-top-right-radius:.25rem!important}.rounded-right{border-top-right-radius:.25rem!important;border-bottom-right-radius:.25rem!important}.rounded-bottom{border-bottom-right-radius:.25rem!important;border-bottom-left-radius:.25rem!important}.rounded-left{border-top-left-radius:.25rem!important;border-bottom-left-radius:.25rem!important}.rounded-lg{border-radius:.3rem!important}.rounded-circle{border-radius:50%!important}.rounded-pill{border-radius:50rem!important}.rounded-0{border-radius:0!important}.clearfix::after{display:block;clear:both;content:""}.d-none{display:none!important}.d-inline{display:inline!important}.d-inline-block{display:inline-block!important}.d-block{display:block!important}.d-table{display:table!important}.d-table-row{display:table-row!important}.d-table-cell{display:table-cell!important}.d-flex{display:-ms-flexbox!important;display:flex!important}.d-inline-flex{display:-ms-inline-flexbox!important;display:inline-flex!important}@media (min-width:576px){.d-sm-none{display:none!important}.d-sm-inline{display:inline!important}.d-sm-inline-block{display:inline-block!important}.d-sm-block{display:block!important}.d-sm-table{display:table!important}.d-sm-table-row{display:table-row!important}.d-sm-table-cell{display:table-cell!important}.d-sm-flex{display:-ms-flexbox!important;display:flex!important}.d-sm-inline-flex{display:-ms-inline-flexbox!important;display:inline-flex!important}}@media (min-width:768px){.d-md-none{display:none!important}.d-md-inline{display:inline!important}.d-md-inline-block{display:inline-block!important}.d-md-block{display:block!important}.d-md-table{display:table!important}.d-md-table-row{display:table-row!important}.d-md-table-cell{display:table-cell!important}.d-md-flex{display:-ms-flexbox!important;display:flex!important}.d-md-inline-flex{display:-ms-inline-flexbox!important;display:inline-flex!important}}@media (min-width:992px){.d-lg-none{display:none!important}.d-lg-inline{display:inline!important}.d-lg-inline-block{display:inline-block!important}.d-lg-block{display:block!important}.d-lg-table{display:table!important}.d-lg-table-row{display:table-row!important}.d-lg-table-cell{display:table-cell!important}.d-lg-flex{display:-ms-flexbox!important;display:flex!important}.d-lg-inline-flex{display:-ms-inline-flexbox!important;display:inline-flex!important}}@media (min-width:1200px){.d-xl-none{display:none!important}.d-xl-inline{display:inline!important}.d-xl-inline-block{display:inline-block!important}.d-xl-block{display:block!important}.d-xl-table{display:table!important}.d-xl-table-row{display:table-row!important}.d-xl-table-cell{display:table-cell!important}.d-xl-flex{display:-ms-flexbox!important;display:flex!important}.d-xl-inline-flex{display:-ms-inline-flexbox!important;display:inline-flex!important}}@media print{.d-print-none{display:none!important}.d-print-inline{display:inline!important}.d-print-inline-block{display:inline-block!important}.d-print-block{display:block!important}.d-print-table{display:table!important}.d-print-table-row{display:table-row!important}.d-print-table-cell{display:table-cell!important}.d-print-flex{display:-ms-flexbox!important;display:flex!important}.d-print-inline-flex{display:-ms-inline-flexbox!important;display:inline-flex!important}}.embed-responsive{position:relative;display:block;width:100%;padding:0;overflow:hidden}.embed-responsive::before{display:block;content:""}.embed-responsive .embed-responsive-item,.embed-responsive embed,.embed-responsive iframe,.embed-responsive object,.embed-responsive video{position:absolute;top:0;bottom:0;left:0;width:100%;height:100%;border:0}.embed-responsive-21by9::before{padding-top:42.857143%}.embed-responsive-16by9::before{padding-top:56.25%}.embed-responsive-4by3::before{padding-top:75%}.embed-responsive-1by1::before{padding-top:100%}.flex-row{-ms-flex-direction:row!important;flex-direction:row!important}.flex-column{-ms-flex-direction:column!important;flex-direction:column!important}.flex-row-reverse{-ms-flex-direction:row-reverse!important;flex-direction:row-reverse!important}.flex-column-reverse{-ms-flex-direction:column-reverse!important;flex-direction:column-reverse!important}.flex-wrap{-ms-flex-wrap:wrap!important;flex-wrap:wrap!important}.flex-nowrap{-ms-flex-wrap:nowrap!important;flex-wrap:nowrap!important}.flex-wrap-reverse{-ms-flex-wrap:wrap-reverse!important;flex-wrap:wrap-reverse!important}.flex-fill{-ms-flex:1 1 auto!important;flex:1 1 auto!important}.flex-grow-0{-ms-flex-positive:0!important;flex-grow:0!important}.flex-grow-1{-ms-flex-positive:1!important;flex-grow:1!important}.flex-shrink-0{-ms-flex-negative:0!important;flex-shrink:0!important}.flex-shrink-1{-ms-flex-negative:1!important;flex-shrink:1!important}.justify-content-start{-ms-flex-pack:start!important;justify-content:flex-start!important}.justify-content-end{-ms-flex-pack:end!important;justify-content:flex-end!important}.justify-content-center{-ms-flex-pack:center!important;justify-content:center!important}.justify-content-between{-ms-flex-pack:justify!important;justify-content:space-between!important}.justify-content-around{-ms-flex-pack:distribute!important;justify-content:space-around!important}.align-items-start{-ms-flex-align:start!important;align-items:flex-start!important}.align-items-end{-ms-flex-align:end!important;align-items:flex-end!important}.align-items-center{-ms-flex-align:center!important;align-items:center!important}.align-items-baseline{-ms-flex-align:baseline!important;align-items:baseline!important}.align-items-stretch{-ms-flex-align:stretch!important;align-items:stretch!important}.align-content-start{-ms-flex-line-pack:start!important;align-content:flex-start!important}.align-content-end{-ms-flex-line-pack:end!important;align-content:flex-end!important}.align-content-center{-ms-flex-line-pack:center!important;align-content:center!important}.align-content-between{-ms-flex-line-pack:justify!important;align-content:space-between!important}.align-content-around{-ms-flex-line-pack:distribute!important;align-content:space-around!important}.align-content-stretch{-ms-flex-line-pack:stretch!important;align-content:stretch!important}.align-self-auto{-ms-flex-item-align:auto!important;align-self:auto!important}.align-self-start{-ms-flex-item-align:start!important;align-self:flex-start!important}.align-self-end{-ms-flex-item-align:end!important;align-self:flex-end!important}.align-self-center{-ms-flex-item-align:center!important;align-self:center!important}.align-self-baseline{-ms-flex-item-align:baseline!important;align-self:baseline!important}.align-self-stretch{-ms-flex-item-align:stretch!important;align-self:stretch!important}@media (min-width:576px){.flex-sm-row{-ms-flex-direction:row!important;flex-direction:row!important}.flex-sm-column{-ms-flex-direction:column!important;flex-direction:column!important}.flex-sm-row-reverse{-ms-flex-direction:row-reverse!important;flex-direction:row-reverse!important}.flex-sm-column-reverse{-ms-flex-direction:column-reverse!important;flex-direction:column-reverse!important}.flex-sm-wrap{-ms-flex-wrap:wrap!important;flex-wrap:wrap!important}.flex-sm-nowrap{-ms-flex-wrap:nowrap!important;flex-wrap:nowrap!important}.flex-sm-wrap-reverse{-ms-flex-wrap:wrap-reverse!important;flex-wrap:wrap-reverse!important}.flex-sm-fill{-ms-flex:1 1 auto!important;flex:1 1 auto!important}.flex-sm-grow-0{-ms-flex-positive:0!important;flex-grow:0!important}.flex-sm-grow-1{-ms-flex-positive:1!important;flex-grow:1!important}.flex-sm-shrink-0{-ms-flex-negative:0!important;flex-shrink:0!important}.flex-sm-shrink-1{-ms-flex-negative:1!important;flex-shrink:1!important}.justify-content-sm-start{-ms-flex-pack:start!important;justify-content:flex-start!important}.justify-content-sm-end{-ms-flex-pack:end!important;justify-content:flex-end!important}.justify-content-sm-center{-ms-flex-pack:center!important;justify-content:center!important}.justify-content-sm-between{-ms-flex-pack:justify!important;justify-content:space-between!important}.justify-content-sm-around{-ms-flex-pack:distribute!important;justify-content:space-around!important}.align-items-sm-start{-ms-flex-align:start!important;align-items:flex-start!important}.align-items-sm-end{-ms-flex-align:end!important;align-items:flex-end!important}.align-items-sm-center{-ms-flex-align:center!important;align-items:center!important}.align-items-sm-baseline{-ms-flex-align:baseline!important;align-items:baseline!important}.align-items-sm-stretch{-ms-flex-align:stretch!important;align-items:stretch!important}.align-content-sm-start{-ms-flex-line-pack:start!important;align-content:flex-start!important}.align-content-sm-end{-ms-flex-line-pack:end!important;align-content:flex-end!important}.align-content-sm-center{-ms-flex-line-pack:center!important;align-content:center!important}.align-content-sm-between{-ms-flex-line-pack:justify!important;align-content:space-between!important}.align-content-sm-around{-ms-flex-line-pack:distribute!important;align-content:space-around!important}.align-content-sm-stretch{-ms-flex-line-pack:stretch!important;align-content:stretch!important}.align-self-sm-auto{-ms-flex-item-align:auto!important;align-self:auto!important}.align-self-sm-start{-ms-flex-item-align:start!important;align-self:flex-start!important}.align-self-sm-end{-ms-flex-item-align:end!important;align-self:flex-end!important}.align-self-sm-center{-ms-flex-item-align:center!important;align-self:center!important}.align-self-sm-baseline{-ms-flex-item-align:baseline!important;align-self:baseline!important}.align-self-sm-stretch{-ms-flex-item-align:stretch!important;align-self:stretch!important}}@media (min-width:768px){.flex-md-row{-ms-flex-direction:row!important;flex-direction:row!important}.flex-md-column{-ms-flex-direction:column!important;flex-direction:column!important}.flex-md-row-reverse{-ms-flex-direction:row-reverse!important;flex-direction:row-reverse!important}.flex-md-column-reverse{-ms-flex-direction:column-reverse!important;flex-direction:column-reverse!important}.flex-md-wrap{-ms-flex-wrap:wrap!important;flex-wrap:wrap!important}.flex-md-nowrap{-ms-flex-wrap:nowrap!important;flex-wrap:nowrap!important}.flex-md-wrap-reverse{-ms-flex-wrap:wrap-reverse!important;flex-wrap:wrap-reverse!important}.flex-md-fill{-ms-flex:1 1 auto!important;flex:1 1 auto!important}.flex-md-grow-0{-ms-flex-positive:0!important;flex-grow:0!important}.flex-md-grow-1{-ms-flex-positive:1!important;flex-grow:1!important}.flex-md-shrink-0{-ms-flex-negative:0!important;flex-shrink:0!important}.flex-md-shrink-1{-ms-flex-negative:1!important;flex-shrink:1!important}.justify-content-md-start{-ms-flex-pack:start!important;justify-content:flex-start!important}.justify-content-md-end{-ms-flex-pack:end!important;justify-content:flex-end!important}.justify-content-md-center{-ms-flex-pack:center!important;justify-content:center!important}.justify-content-md-between{-ms-flex-pack:justify!important;justify-content:space-between!important}.justify-content-md-around{-ms-flex-pack:distribute!important;justify-content:space-around!important}.align-items-md-start{-ms-flex-align:start!important;align-items:flex-start!important}.align-items-md-end{-ms-flex-align:end!important;align-items:flex-end!important}.align-items-md-center{-ms-flex-align:center!important;align-items:center!important}.align-items-md-baseline{-ms-flex-align:baseline!important;align-items:baseline!important}.align-items-md-stretch{-ms-flex-align:stretch!important;align-items:stretch!important}.align-content-md-start{-ms-flex-line-pack:start!important;align-content:flex-start!important}.align-content-md-end{-ms-flex-line-pack:end!important;align-content:flex-end!important}.align-content-md-center{-ms-flex-line-pack:center!important;align-content:center!important}.align-content-md-between{-ms-flex-line-pack:justify!important;align-content:space-between!important}.align-content-md-around{-ms-flex-line-pack:distribute!important;align-content:space-around!important}.align-content-md-stretch{-ms-flex-line-pack:stretch!important;align-content:stretch!important}.align-self-md-auto{-ms-flex-item-align:auto!important;align-self:auto!important}.align-self-md-start{-ms-flex-item-align:start!important;align-self:flex-start!important}.align-self-md-end{-ms-flex-item-align:end!important;align-self:flex-end!important}.align-self-md-center{-ms-flex-item-align:center!important;align-self:center!important}.align-self-md-baseline{-ms-flex-item-align:baseline!important;align-self:baseline!important}.align-self-md-stretch{-ms-flex-item-align:stretch!important;align-self:stretch!important}}@media (min-width:992px){.flex-lg-row{-ms-flex-direction:row!important;flex-direction:row!important}.flex-lg-column{-ms-flex-direction:column!important;flex-direction:column!important}.flex-lg-row-reverse{-ms-flex-direction:row-reverse!important;flex-direction:row-reverse!important}.flex-lg-column-reverse{-ms-flex-direction:column-reverse!important;flex-direction:column-reverse!important}.flex-lg-wrap{-ms-flex-wrap:wrap!important;flex-wrap:wrap!important}.flex-lg-nowrap{-ms-flex-wrap:nowrap!important;flex-wrap:nowrap!important}.flex-lg-wrap-reverse{-ms-flex-wrap:wrap-reverse!important;flex-wrap:wrap-reverse!important}.flex-lg-fill{-ms-flex:1 1 auto!important;flex:1 1 auto!important}.flex-lg-grow-0{-ms-flex-positive:0!important;flex-grow:0!important}.flex-lg-grow-1{-ms-flex-positive:1!important;flex-grow:1!important}.flex-lg-shrink-0{-ms-flex-negative:0!important;flex-shrink:0!important}.flex-lg-shrink-1{-ms-flex-negative:1!important;flex-shrink:1!important}.justify-content-lg-start{-ms-flex-pack:start!important;justify-content:flex-start!important}.justify-content-lg-end{-ms-flex-pack:end!important;justify-content:flex-end!important}.justify-content-lg-center{-ms-flex-pack:center!important;justify-content:center!important}.justify-content-lg-between{-ms-flex-pack:justify!important;justify-content:space-between!important}.justify-content-lg-around{-ms-flex-pack:distribute!important;justify-content:space-around!important}.align-items-lg-start{-ms-flex-align:start!important;align-items:flex-start!important}.align-items-lg-end{-ms-flex-align:end!important;align-items:flex-end!important}.align-items-lg-center{-ms-flex-align:center!important;align-items:center!important}.align-items-lg-baseline{-ms-flex-align:baseline!important;align-items:baseline!important}.align-items-lg-stretch{-ms-flex-align:stretch!important;align-items:stretch!important}.align-content-lg-start{-ms-flex-line-pack:start!important;align-content:flex-start!important}.align-content-lg-end{-ms-flex-line-pack:end!important;align-content:flex-end!important}.align-content-lg-center{-ms-flex-line-pack:center!important;align-content:center!important}.align-content-lg-between{-ms-flex-line-pack:justify!important;align-content:space-between!important}.align-content-lg-around{-ms-flex-line-pack:distribute!important;align-content:space-around!important}.align-content-lg-stretch{-ms-flex-line-pack:stretch!important;align-content:stretch!important}.align-self-lg-auto{-ms-flex-item-align:auto!important;align-self:auto!important}.align-self-lg-start{-ms-flex-item-align:start!important;align-self:flex-start!important}.align-self-lg-end{-ms-flex-item-align:end!important;align-self:flex-end!important}.align-self-lg-center{-ms-flex-item-align:center!important;align-self:center!important}.align-self-lg-baseline{-ms-flex-item-align:baseline!important;align-self:baseline!important}.align-self-lg-stretch{-ms-flex-item-align:stretch!important;align-self:stretch!important}}@media (min-width:1200px){.flex-xl-row{-ms-flex-direction:row!important;flex-direction:row!important}.flex-xl-column{-ms-flex-direction:column!important;flex-direction:column!important}.flex-xl-row-reverse{-ms-flex-direction:row-reverse!important;flex-direction:row-reverse!important}.flex-xl-column-reverse{-ms-flex-direction:column-reverse!important;flex-direction:column-reverse!important}.flex-xl-wrap{-ms-flex-wrap:wrap!important;flex-wrap:wrap!important}.flex-xl-nowrap{-ms-flex-wrap:nowrap!important;flex-wrap:nowrap!important}.flex-xl-wrap-reverse{-ms-flex-wrap:wrap-reverse!important;flex-wrap:wrap-reverse!important}.flex-xl-fill{-ms-flex:1 1 auto!important;flex:1 1 auto!important}.flex-xl-grow-0{-ms-flex-positive:0!important;flex-grow:0!important}.flex-xl-grow-1{-ms-flex-positive:1!important;flex-grow:1!important}.flex-xl-shrink-0{-ms-flex-negative:0!important;flex-shrink:0!important}.flex-xl-shrink-1{-ms-flex-negative:1!important;flex-shrink:1!important}.justify-content-xl-start{-ms-flex-pack:start!important;justify-content:flex-start!important}.justify-content-xl-end{-ms-flex-pack:end!important;justify-content:flex-end!important}.justify-content-xl-center{-ms-flex-pack:center!important;justify-content:center!important}.justify-content-xl-between{-ms-flex-pack:justify!important;justify-content:space-between!important}.justify-content-xl-around{-ms-flex-pack:distribute!important;justify-content:space-around!important}.align-items-xl-start{-ms-flex-align:start!important;align-items:flex-start!important}.align-items-xl-end{-ms-flex-align:end!important;align-items:flex-end!important}.align-items-xl-center{-ms-flex-align:center!important;align-items:center!important}.align-items-xl-baseline{-ms-flex-align:baseline!important;align-items:baseline!important}.align-items-xl-stretch{-ms-flex-align:stretch!important;align-items:stretch!important}.align-content-xl-start{-ms-flex-line-pack:start!important;align-content:flex-start!important}.align-content-xl-end{-ms-flex-line-pack:end!important;align-content:flex-end!important}.align-content-xl-center{-ms-flex-line-pack:center!important;align-content:center!important}.align-content-xl-between{-ms-flex-line-pack:justify!important;align-content:space-between!important}.align-content-xl-around{-ms-flex-line-pack:distribute!important;align-content:space-around!important}.align-content-xl-stretch{-ms-flex-line-pack:stretch!important;align-content:stretch!important}.align-self-xl-auto{-ms-flex-item-align:auto!important;align-self:auto!important}.align-self-xl-start{-ms-flex-item-align:start!important;align-self:flex-start!important}.align-self-xl-end{-ms-flex-item-align:end!important;align-self:flex-end!important}.align-self-xl-center{-ms-flex-item-align:center!important;align-self:center!important}.align-self-xl-baseline{-ms-flex-item-align:baseline!important;align-self:baseline!important}.align-self-xl-stretch{-ms-flex-item-align:stretch!important;align-self:stretch!important}}.float-left{float:left!important}.float-right{float:right!important}.float-none{float:none!important}@media (min-width:576px){.float-sm-left{float:left!important}.float-sm-right{float:right!important}.float-sm-none{float:none!important}}@media (min-width:768px){.float-md-left{float:left!important}.float-md-right{float:right!important}.float-md-none{float:none!important}}@media (min-width:992px){.float-lg-left{float:left!important}.float-lg-right{float:right!important}.float-lg-none{float:none!important}}@media (min-width:1200px){.float-xl-left{float:left!important}.float-xl-right{float:right!important}.float-xl-none{float:none!important}}.user-select-all{-webkit-user-select:all!important;-moz-user-select:all!important;-ms-user-select:all!important;user-select:all!important}.user-select-auto{-webkit-user-select:auto!important;-moz-user-select:auto!important;-ms-user-select:auto!important;user-select:auto!important}.user-select-none{-webkit-user-select:none!important;-moz-user-select:none!important;-ms-user-select:none!important;user-select:none!important}.overflow-auto{overflow:auto!important}.overflow-hidden{overflow:hidden!important}.position-static{position:static!important}.position-relative{position:relative!important}.position-absolute{position:absolute!important}.position-fixed{position:fixed!important}.position-sticky{position:-webkit-sticky!important;position:sticky!important}.fixed-top{position:fixed;top:0;right:0;left:0;z-index:1030}.fixed-bottom{position:fixed;right:0;bottom:0;left:0;z-index:1030}@supports ((position:-webkit-sticky) or (position:sticky)){.sticky-top{position:-webkit-sticky;position:sticky;top:0;z-index:1020}}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;overflow:visible;clip:auto;white-space:normal}.shadow-sm{box-shadow:0 .125rem .25rem rgba(0,0,0,.075)!important}.shadow{box-shadow:0 .5rem 1rem rgba(0,0,0,.15)!important}.shadow-lg{box-shadow:0 1rem 3rem rgba(0,0,0,.175)!important}.shadow-none{box-shadow:none!important}.w-25{width:25%!important}.w-50{width:50%!important}.w-75{width:75%!important}.w-100{width:100%!important}.w-auto{width:auto!important}.h-25{height:25%!important}.h-50{height:50%!important}.h-75{height:75%!important}.h-100{height:100%!important}.h-auto{height:auto!important}.mw-100{max-width:100%!important}.mh-100{max-height:100%!important}.min-vw-100{min-width:100vw!important}.min-vh-100{min-height:100vh!important}.vw-100{width:100vw!important}.vh-100{height:100vh!important}.m-0{margin:0!important}.mt-0,.my-0{margin-top:0!important}.mr-0,.mx-0{margin-right:0!important}.mb-0,.my-0{margin-bottom:0!important}.ml-0,.mx-0{margin-left:0!important}.m-1{margin:.25rem!important}.mt-1,.my-1{margin-top:.25rem!important}.mr-1,.mx-1{margin-right:.25rem!important}.mb-1,.my-1{margin-bottom:.25rem!important}.ml-1,.mx-1{margin-left:.25rem!important}.m-2{margin:.5rem!important}.mt-2,.my-2{margin-top:.5rem!important}.mr-2,.mx-2{margin-right:.5rem!important}.mb-2,.my-2{margin-bottom:.5rem!important}.ml-2,.mx-2{margin-left:.5rem!important}.m-3{margin:1rem!important}.mt-3,.my-3{margin-top:1rem!important}.mr-3,.mx-3{margin-right:1rem!important}.mb-3,.my-3{margin-bottom:1rem!important}.ml-3,.mx-3{margin-left:1rem!important}.m-4{margin:1.5rem!important}.mt-4,.my-4{margin-top:1.5rem!important}.mr-4,.mx-4{margin-right:1.5rem!important}.mb-4,.my-4{margin-bottom:1.5rem!important}.ml-4,.mx-4{margin-left:1.5rem!important}.m-5{margin:3rem!important}.mt-5,.my-5{margin-top:3rem!important}.mr-5,.mx-5{margin-right:3rem!important}.mb-5,.my-5{margin-bottom:3rem!important}.ml-5,.mx-5{margin-left:3rem!important}.p-0{padding:0!important}.pt-0,.py-0{padding-top:0!important}.pr-0,.px-0{padding-right:0!important}.pb-0,.py-0{padding-bottom:0!important}.pl-0,.px-0{padding-left:0!important}.p-1{padding:.25rem!important}.pt-1,.py-1{padding-top:.25rem!important}.pr-1,.px-1{padding-right:.25rem!important}.pb-1,.py-1{padding-bottom:.25rem!important}.pl-1,.px-1{padding-left:.25rem!important}.p-2{padding:.5rem!important}.pt-2,.py-2{padding-top:.5rem!important}.pr-2,.px-2{padding-right:.5rem!important}.pb-2,.py-2{padding-bottom:.5rem!important}.pl-2,.px-2{padding-left:.5rem!important}.p-3{padding:1rem!important}.pt-3,.py-3{padding-top:1rem!important}.pr-3,.px-3{padding-right:1rem!important}.pb-3,.py-3{padding-bottom:1rem!important}.pl-3,.px-3{padding-left:1rem!important}.p-4{padding:1.5rem!important}.pt-4,.py-4{padding-top:1.5rem!important}.pr-4,.px-4{padding-right:1.5rem!important}.pb-4,.py-4{padding-bottom:1.5rem!important}.pl-4,.px-4{padding-left:1.5rem!important}.p-5{padding:3rem!important}.pt-5,.py-5{padding-top:3rem!important}.pr-5,.px-5{padding-right:3rem!important}.pb-5,.py-5{padding-bottom:3rem!important}.pl-5,.px-5{padding-left:3rem!important}.m-n1{margin:-.25rem!important}.mt-n1,.my-n1{margin-top:-.25rem!important}.mr-n1,.mx-n1{margin-right:-.25rem!important}.mb-n1,.my-n1{margin-bottom:-.25rem!important}.ml-n1,.mx-n1{margin-left:-.25rem!important}.m-n2{margin:-.5rem!important}.mt-n2,.my-n2{margin-top:-.5rem!important}.mr-n2,.mx-n2{margin-right:-.5rem!important}.mb-n2,.my-n2{margin-bottom:-.5rem!important}.ml-n2,.mx-n2{margin-left:-.5rem!important}.m-n3{margin:-1rem!important}.mt-n3,.my-n3{margin-top:-1rem!important}.mr-n3,.mx-n3{margin-right:-1rem!important}.mb-n3,.my-n3{margin-bottom:-1rem!important}.ml-n3,.mx-n3{margin-left:-1rem!important}.m-n4{margin:-1.5rem!important}.mt-n4,.my-n4{margin-top:-1.5rem!important}.mr-n4,.mx-n4{margin-right:-1.5rem!important}.mb-n4,.my-n4{margin-bottom:-1.5rem!important}.ml-n4,.mx-n4{margin-left:-1.5rem!important}.m-n5{margin:-3rem!important}.mt-n5,.my-n5{margin-top:-3rem!important}.mr-n5,.mx-n5{margin-right:-3rem!important}.mb-n5,.my-n5{margin-bottom:-3rem!important}.ml-n5,.mx-n5{margin-left:-3rem!important}.m-auto{margin:auto!important}.mt-auto,.my-auto{margin-top:auto!important}.mr-auto,.mx-auto{margin-right:auto!important}.mb-auto,.my-auto{margin-bottom:auto!important}.ml-auto,.mx-auto{margin-left:auto!important}@media (min-width:576px){.m-sm-0{margin:0!important}.mt-sm-0,.my-sm-0{margin-top:0!important}.mr-sm-0,.mx-sm-0{margin-right:0!important}.mb-sm-0,.my-sm-0{margin-bottom:0!important}.ml-sm-0,.mx-sm-0{margin-left:0!important}.m-sm-1{margin:.25rem!important}.mt-sm-1,.my-sm-1{margin-top:.25rem!important}.mr-sm-1,.mx-sm-1{margin-right:.25rem!important}.mb-sm-1,.my-sm-1{margin-bottom:.25rem!important}.ml-sm-1,.mx-sm-1{margin-left:.25rem!important}.m-sm-2{margin:.5rem!important}.mt-sm-2,.my-sm-2{margin-top:.5rem!important}.mr-sm-2,.mx-sm-2{margin-right:.5rem!important}.mb-sm-2,.my-sm-2{margin-bottom:.5rem!important}.ml-sm-2,.mx-sm-2{margin-left:.5rem!important}.m-sm-3{margin:1rem!important}.mt-sm-3,.my-sm-3{margin-top:1rem!important}.mr-sm-3,.mx-sm-3{margin-right:1rem!important}.mb-sm-3,.my-sm-3{margin-bottom:1rem!important}.ml-sm-3,.mx-sm-3{margin-left:1rem!important}.m-sm-4{margin:1.5rem!important}.mt-sm-4,.my-sm-4{margin-top:1.5rem!important}.mr-sm-4,.mx-sm-4{margin-right:1.5rem!important}.mb-sm-4,.my-sm-4{margin-bottom:1.5rem!important}.ml-sm-4,.mx-sm-4{margin-left:1.5rem!important}.m-sm-5{margin:3rem!important}.mt-sm-5,.my-sm-5{margin-top:3rem!important}.mr-sm-5,.mx-sm-5{margin-right:3rem!important}.mb-sm-5,.my-sm-5{margin-bottom:3rem!important}.ml-sm-5,.mx-sm-5{margin-left:3rem!important}.p-sm-0{padding:0!important}.pt-sm-0,.py-sm-0{padding-top:0!important}.pr-sm-0,.px-sm-0{padding-right:0!important}.pb-sm-0,.py-sm-0{padding-bottom:0!important}.pl-sm-0,.px-sm-0{padding-left:0!important}.p-sm-1{padding:.25rem!important}.pt-sm-1,.py-sm-1{padding-top:.25rem!important}.pr-sm-1,.px-sm-1{padding-right:.25rem!important}.pb-sm-1,.py-sm-1{padding-bottom:.25rem!important}.pl-sm-1,.px-sm-1{padding-left:.25rem!important}.p-sm-2{padding:.5rem!important}.pt-sm-2,.py-sm-2{padding-top:.5rem!important}.pr-sm-2,.px-sm-2{padding-right:.5rem!important}.pb-sm-2,.py-sm-2{padding-bottom:.5rem!important}.pl-sm-2,.px-sm-2{padding-left:.5rem!important}.p-sm-3{padding:1rem!important}.pt-sm-3,.py-sm-3{padding-top:1rem!important}.pr-sm-3,.px-sm-3{padding-right:1rem!important}.pb-sm-3,.py-sm-3{padding-bottom:1rem!important}.pl-sm-3,.px-sm-3{padding-left:1rem!important}.p-sm-4{padding:1.5rem!important}.pt-sm-4,.py-sm-4{padding-top:1.5rem!important}.pr-sm-4,.px-sm-4{padding-right:1.5rem!important}.pb-sm-4,.py-sm-4{padding-bottom:1.5rem!important}.pl-sm-4,.px-sm-4{padding-left:1.5rem!important}.p-sm-5{padding:3rem!important}.pt-sm-5,.py-sm-5{padding-top:3rem!important}.pr-sm-5,.px-sm-5{padding-right:3rem!important}.pb-sm-5,.py-sm-5{padding-bottom:3rem!important}.pl-sm-5,.px-sm-5{padding-left:3rem!important}.m-sm-n1{margin:-.25rem!important}.mt-sm-n1,.my-sm-n1{margin-top:-.25rem!important}.mr-sm-n1,.mx-sm-n1{margin-right:-.25rem!important}.mb-sm-n1,.my-sm-n1{margin-bottom:-.25rem!important}.ml-sm-n1,.mx-sm-n1{margin-left:-.25rem!important}.m-sm-n2{margin:-.5rem!important}.mt-sm-n2,.my-sm-n2{margin-top:-.5rem!important}.mr-sm-n2,.mx-sm-n2{margin-right:-.5rem!important}.mb-sm-n2,.my-sm-n2{margin-bottom:-.5rem!important}.ml-sm-n2,.mx-sm-n2{margin-left:-.5rem!important}.m-sm-n3{margin:-1rem!important}.mt-sm-n3,.my-sm-n3{margin-top:-1rem!important}.mr-sm-n3,.mx-sm-n3{margin-right:-1rem!important}.mb-sm-n3,.my-sm-n3{margin-bottom:-1rem!important}.ml-sm-n3,.mx-sm-n3{margin-left:-1rem!important}.m-sm-n4{margin:-1.5rem!important}.mt-sm-n4,.my-sm-n4{margin-top:-1.5rem!important}.mr-sm-n4,.mx-sm-n4{margin-right:-1.5rem!important}.mb-sm-n4,.my-sm-n4{margin-bottom:-1.5rem!important}.ml-sm-n4,.mx-sm-n4{margin-left:-1.5rem!important}.m-sm-n5{margin:-3rem!important}.mt-sm-n5,.my-sm-n5{margin-top:-3rem!important}.mr-sm-n5,.mx-sm-n5{margin-right:-3rem!important}.mb-sm-n5,.my-sm-n5{margin-bottom:-3rem!important}.ml-sm-n5,.mx-sm-n5{margin-left:-3rem!important}.m-sm-auto{margin:auto!important}.mt-sm-auto,.my-sm-auto{margin-top:auto!important}.mr-sm-auto,.mx-sm-auto{margin-right:auto!important}.mb-sm-auto,.my-sm-auto{margin-bottom:auto!important}.ml-sm-auto,.mx-sm-auto{margin-left:auto!important}}@media (min-width:768px){.m-md-0{margin:0!important}.mt-md-0,.my-md-0{margin-top:0!important}.mr-md-0,.mx-md-0{margin-right:0!important}.mb-md-0,.my-md-0{margin-bottom:0!important}.ml-md-0,.mx-md-0{margin-left:0!important}.m-md-1{margin:.25rem!important}.mt-md-1,.my-md-1{margin-top:.25rem!important}.mr-md-1,.mx-md-1{margin-right:.25rem!important}.mb-md-1,.my-md-1{margin-bottom:.25rem!important}.ml-md-1,.mx-md-1{margin-left:.25rem!important}.m-md-2{margin:.5rem!important}.mt-md-2,.my-md-2{margin-top:.5rem!important}.mr-md-2,.mx-md-2{margin-right:.5rem!important}.mb-md-2,.my-md-2{margin-bottom:.5rem!important}.ml-md-2,.mx-md-2{margin-left:.5rem!important}.m-md-3{margin:1rem!important}.mt-md-3,.my-md-3{margin-top:1rem!important}.mr-md-3,.mx-md-3{margin-right:1rem!important}.mb-md-3,.my-md-3{margin-bottom:1rem!important}.ml-md-3,.mx-md-3{margin-left:1rem!important}.m-md-4{margin:1.5rem!important}.mt-md-4,.my-md-4{margin-top:1.5rem!important}.mr-md-4,.mx-md-4{margin-right:1.5rem!important}.mb-md-4,.my-md-4{margin-bottom:1.5rem!important}.ml-md-4,.mx-md-4{margin-left:1.5rem!important}.m-md-5{margin:3rem!important}.mt-md-5,.my-md-5{margin-top:3rem!important}.mr-md-5,.mx-md-5{margin-right:3rem!important}.mb-md-5,.my-md-5{margin-bottom:3rem!important}.ml-md-5,.mx-md-5{margin-left:3rem!important}.p-md-0{padding:0!important}.pt-md-0,.py-md-0{padding-top:0!important}.pr-md-0,.px-md-0{padding-right:0!important}.pb-md-0,.py-md-0{padding-bottom:0!important}.pl-md-0,.px-md-0{padding-left:0!important}.p-md-1{padding:.25rem!important}.pt-md-1,.py-md-1{padding-top:.25rem!important}.pr-md-1,.px-md-1{padding-right:.25rem!important}.pb-md-1,.py-md-1{padding-bottom:.25rem!important}.pl-md-1,.px-md-1{padding-left:.25rem!important}.p-md-2{padding:.5rem!important}.pt-md-2,.py-md-2{padding-top:.5rem!important}.pr-md-2,.px-md-2{padding-right:.5rem!important}.pb-md-2,.py-md-2{padding-bottom:.5rem!important}.pl-md-2,.px-md-2{padding-left:.5rem!important}.p-md-3{padding:1rem!important}.pt-md-3,.py-md-3{padding-top:1rem!important}.pr-md-3,.px-md-3{padding-right:1rem!important}.pb-md-3,.py-md-3{padding-bottom:1rem!important}.pl-md-3,.px-md-3{padding-left:1rem!important}.p-md-4{padding:1.5rem!important}.pt-md-4,.py-md-4{padding-top:1.5rem!important}.pr-md-4,.px-md-4{padding-right:1.5rem!important}.pb-md-4,.py-md-4{padding-bottom:1.5rem!important}.pl-md-4,.px-md-4{padding-left:1.5rem!important}.p-md-5{padding:3rem!important}.pt-md-5,.py-md-5{padding-top:3rem!important}.pr-md-5,.px-md-5{padding-right:3rem!important}.pb-md-5,.py-md-5{padding-bottom:3rem!important}.pl-md-5,.px-md-5{padding-left:3rem!important}.m-md-n1{margin:-.25rem!important}.mt-md-n1,.my-md-n1{margin-top:-.25rem!important}.mr-md-n1,.mx-md-n1{margin-right:-.25rem!important}.mb-md-n1,.my-md-n1{margin-bottom:-.25rem!important}.ml-md-n1,.mx-md-n1{margin-left:-.25rem!important}.m-md-n2{margin:-.5rem!important}.mt-md-n2,.my-md-n2{margin-top:-.5rem!important}.mr-md-n2,.mx-md-n2{margin-right:-.5rem!important}.mb-md-n2,.my-md-n2{margin-bottom:-.5rem!important}.ml-md-n2,.mx-md-n2{margin-left:-.5rem!important}.m-md-n3{margin:-1rem!important}.mt-md-n3,.my-md-n3{margin-top:-1rem!important}.mr-md-n3,.mx-md-n3{margin-right:-1rem!important}.mb-md-n3,.my-md-n3{margin-bottom:-1rem!important}.ml-md-n3,.mx-md-n3{margin-left:-1rem!important}.m-md-n4{margin:-1.5rem!important}.mt-md-n4,.my-md-n4{margin-top:-1.5rem!important}.mr-md-n4,.mx-md-n4{margin-right:-1.5rem!important}.mb-md-n4,.my-md-n4{margin-bottom:-1.5rem!important}.ml-md-n4,.mx-md-n4{margin-left:-1.5rem!important}.m-md-n5{margin:-3rem!important}.mt-md-n5,.my-md-n5{margin-top:-3rem!important}.mr-md-n5,.mx-md-n5{margin-right:-3rem!important}.mb-md-n5,.my-md-n5{margin-bottom:-3rem!important}.ml-md-n5,.mx-md-n5{margin-left:-3rem!important}.m-md-auto{margin:auto!important}.mt-md-auto,.my-md-auto{margin-top:auto!important}.mr-md-auto,.mx-md-auto{margin-right:auto!important}.mb-md-auto,.my-md-auto{margin-bottom:auto!important}.ml-md-auto,.mx-md-auto{margin-left:auto!important}}@media (min-width:992px){.m-lg-0{margin:0!important}.mt-lg-0,.my-lg-0{margin-top:0!important}.mr-lg-0,.mx-lg-0{margin-right:0!important}.mb-lg-0,.my-lg-0{margin-bottom:0!important}.ml-lg-0,.mx-lg-0{margin-left:0!important}.m-lg-1{margin:.25rem!important}.mt-lg-1,.my-lg-1{margin-top:.25rem!important}.mr-lg-1,.mx-lg-1{margin-right:.25rem!important}.mb-lg-1,.my-lg-1{margin-bottom:.25rem!important}.ml-lg-1,.mx-lg-1{margin-left:.25rem!important}.m-lg-2{margin:.5rem!important}.mt-lg-2,.my-lg-2{margin-top:.5rem!important}.mr-lg-2,.mx-lg-2{margin-right:.5rem!important}.mb-lg-2,.my-lg-2{margin-bottom:.5rem!important}.ml-lg-2,.mx-lg-2{margin-left:.5rem!important}.m-lg-3{margin:1rem!important}.mt-lg-3,.my-lg-3{margin-top:1rem!important}.mr-lg-3,.mx-lg-3{margin-right:1rem!important}.mb-lg-3,.my-lg-3{margin-bottom:1rem!important}.ml-lg-3,.mx-lg-3{margin-left:1rem!important}.m-lg-4{margin:1.5rem!important}.mt-lg-4,.my-lg-4{margin-top:1.5rem!important}.mr-lg-4,.mx-lg-4{margin-right:1.5rem!important}.mb-lg-4,.my-lg-4{margin-bottom:1.5rem!important}.ml-lg-4,.mx-lg-4{margin-left:1.5rem!important}.m-lg-5{margin:3rem!important}.mt-lg-5,.my-lg-5{margin-top:3rem!important}.mr-lg-5,.mx-lg-5{margin-right:3rem!important}.mb-lg-5,.my-lg-5{margin-bottom:3rem!important}.ml-lg-5,.mx-lg-5{margin-left:3rem!important}.p-lg-0{padding:0!important}.pt-lg-0,.py-lg-0{padding-top:0!important}.pr-lg-0,.px-lg-0{padding-right:0!important}.pb-lg-0,.py-lg-0{padding-bottom:0!important}.pl-lg-0,.px-lg-0{padding-left:0!important}.p-lg-1{padding:.25rem!important}.pt-lg-1,.py-lg-1{padding-top:.25rem!important}.pr-lg-1,.px-lg-1{padding-right:.25rem!important}.pb-lg-1,.py-lg-1{padding-bottom:.25rem!important}.pl-lg-1,.px-lg-1{padding-left:.25rem!important}.p-lg-2{padding:.5rem!important}.pt-lg-2,.py-lg-2{padding-top:.5rem!important}.pr-lg-2,.px-lg-2{padding-right:.5rem!important}.pb-lg-2,.py-lg-2{padding-bottom:.5rem!important}.pl-lg-2,.px-lg-2{padding-left:.5rem!important}.p-lg-3{padding:1rem!important}.pt-lg-3,.py-lg-3{padding-top:1rem!important}.pr-lg-3,.px-lg-3{padding-right:1rem!important}.pb-lg-3,.py-lg-3{padding-bottom:1rem!important}.pl-lg-3,.px-lg-3{padding-left:1rem!important}.p-lg-4{padding:1.5rem!important}.pt-lg-4,.py-lg-4{padding-top:1.5rem!important}.pr-lg-4,.px-lg-4{padding-right:1.5rem!important}.pb-lg-4,.py-lg-4{padding-bottom:1.5rem!important}.pl-lg-4,.px-lg-4{padding-left:1.5rem!important}.p-lg-5{padding:3rem!important}.pt-lg-5,.py-lg-5{padding-top:3rem!important}.pr-lg-5,.px-lg-5{padding-right:3rem!important}.pb-lg-5,.py-lg-5{padding-bottom:3rem!important}.pl-lg-5,.px-lg-5{padding-left:3rem!important}.m-lg-n1{margin:-.25rem!important}.mt-lg-n1,.my-lg-n1{margin-top:-.25rem!important}.mr-lg-n1,.mx-lg-n1{margin-right:-.25rem!important}.mb-lg-n1,.my-lg-n1{margin-bottom:-.25rem!important}.ml-lg-n1,.mx-lg-n1{margin-left:-.25rem!important}.m-lg-n2{margin:-.5rem!important}.mt-lg-n2,.my-lg-n2{margin-top:-.5rem!important}.mr-lg-n2,.mx-lg-n2{margin-right:-.5rem!important}.mb-lg-n2,.my-lg-n2{margin-bottom:-.5rem!important}.ml-lg-n2,.mx-lg-n2{margin-left:-.5rem!important}.m-lg-n3{margin:-1rem!important}.mt-lg-n3,.my-lg-n3{margin-top:-1rem!important}.mr-lg-n3,.mx-lg-n3{margin-right:-1rem!important}.mb-lg-n3,.my-lg-n3{margin-bottom:-1rem!important}.ml-lg-n3,.mx-lg-n3{margin-left:-1rem!important}.m-lg-n4{margin:-1.5rem!important}.mt-lg-n4,.my-lg-n4{margin-top:-1.5rem!important}.mr-lg-n4,.mx-lg-n4{margin-right:-1.5rem!important}.mb-lg-n4,.my-lg-n4{margin-bottom:-1.5rem!important}.ml-lg-n4,.mx-lg-n4{margin-left:-1.5rem!important}.m-lg-n5{margin:-3rem!important}.mt-lg-n5,.my-lg-n5{margin-top:-3rem!important}.mr-lg-n5,.mx-lg-n5{margin-right:-3rem!important}.mb-lg-n5,.my-lg-n5{margin-bottom:-3rem!important}.ml-lg-n5,.mx-lg-n5{margin-left:-3rem!important}.m-lg-auto{margin:auto!important}.mt-lg-auto,.my-lg-auto{margin-top:auto!important}.mr-lg-auto,.mx-lg-auto{margin-right:auto!important}.mb-lg-auto,.my-lg-auto{margin-bottom:auto!important}.ml-lg-auto,.mx-lg-auto{margin-left:auto!important}}@media (min-width:1200px){.m-xl-0{margin:0!important}.mt-xl-0,.my-xl-0{margin-top:0!important}.mr-xl-0,.mx-xl-0{margin-right:0!important}.mb-xl-0,.my-xl-0{margin-bottom:0!important}.ml-xl-0,.mx-xl-0{margin-left:0!important}.m-xl-1{margin:.25rem!important}.mt-xl-1,.my-xl-1{margin-top:.25rem!important}.mr-xl-1,.mx-xl-1{margin-right:.25rem!important}.mb-xl-1,.my-xl-1{margin-bottom:.25rem!important}.ml-xl-1,.mx-xl-1{margin-left:.25rem!important}.m-xl-2{margin:.5rem!important}.mt-xl-2,.my-xl-2{margin-top:.5rem!important}.mr-xl-2,.mx-xl-2{margin-right:.5rem!important}.mb-xl-2,.my-xl-2{margin-bottom:.5rem!important}.ml-xl-2,.mx-xl-2{margin-left:.5rem!important}.m-xl-3{margin:1rem!important}.mt-xl-3,.my-xl-3{margin-top:1rem!important}.mr-xl-3,.mx-xl-3{margin-right:1rem!important}.mb-xl-3,.my-xl-3{margin-bottom:1rem!important}.ml-xl-3,.mx-xl-3{margin-left:1rem!important}.m-xl-4{margin:1.5rem!important}.mt-xl-4,.my-xl-4{margin-top:1.5rem!important}.mr-xl-4,.mx-xl-4{margin-right:1.5rem!important}.mb-xl-4,.my-xl-4{margin-bottom:1.5rem!important}.ml-xl-4,.mx-xl-4{margin-left:1.5rem!important}.m-xl-5{margin:3rem!important}.mt-xl-5,.my-xl-5{margin-top:3rem!important}.mr-xl-5,.mx-xl-5{margin-right:3rem!important}.mb-xl-5,.my-xl-5{margin-bottom:3rem!important}.ml-xl-5,.mx-xl-5{margin-left:3rem!important}.p-xl-0{padding:0!important}.pt-xl-0,.py-xl-0{padding-top:0!important}.pr-xl-0,.px-xl-0{padding-right:0!important}.pb-xl-0,.py-xl-0{padding-bottom:0!important}.pl-xl-0,.px-xl-0{padding-left:0!important}.p-xl-1{padding:.25rem!important}.pt-xl-1,.py-xl-1{padding-top:.25rem!important}.pr-xl-1,.px-xl-1{padding-right:.25rem!important}.pb-xl-1,.py-xl-1{padding-bottom:.25rem!important}.pl-xl-1,.px-xl-1{padding-left:.25rem!important}.p-xl-2{padding:.5rem!important}.pt-xl-2,.py-xl-2{padding-top:.5rem!important}.pr-xl-2,.px-xl-2{padding-right:.5rem!important}.pb-xl-2,.py-xl-2{padding-bottom:.5rem!important}.pl-xl-2,.px-xl-2{padding-left:.5rem!important}.p-xl-3{padding:1rem!important}.pt-xl-3,.py-xl-3{padding-top:1rem!important}.pr-xl-3,.px-xl-3{padding-right:1rem!important}.pb-xl-3,.py-xl-3{padding-bottom:1rem!important}.pl-xl-3,.px-xl-3{padding-left:1rem!important}.p-xl-4{padding:1.5rem!important}.pt-xl-4,.py-xl-4{padding-top:1.5rem!important}.pr-xl-4,.px-xl-4{padding-right:1.5rem!important}.pb-xl-4,.py-xl-4{padding-bottom:1.5rem!important}.pl-xl-4,.px-xl-4{padding-left:1.5rem!important}.p-xl-5{padding:3rem!important}.pt-xl-5,.py-xl-5{padding-top:3rem!important}.pr-xl-5,.px-xl-5{padding-right:3rem!important}.pb-xl-5,.py-xl-5{padding-bottom:3rem!important}.pl-xl-5,.px-xl-5{padding-left:3rem!important}.m-xl-n1{margin:-.25rem!important}.mt-xl-n1,.my-xl-n1{margin-top:-.25rem!important}.mr-xl-n1,.mx-xl-n1{margin-right:-.25rem!important}.mb-xl-n1,.my-xl-n1{margin-bottom:-.25rem!important}.ml-xl-n1,.mx-xl-n1{margin-left:-.25rem!important}.m-xl-n2{margin:-.5rem!important}.mt-xl-n2,.my-xl-n2{margin-top:-.5rem!important}.mr-xl-n2,.mx-xl-n2{margin-right:-.5rem!important}.mb-xl-n2,.my-xl-n2{margin-bottom:-.5rem!important}.ml-xl-n2,.mx-xl-n2{margin-left:-.5rem!important}.m-xl-n3{margin:-1rem!important}.mt-xl-n3,.my-xl-n3{margin-top:-1rem!important}.mr-xl-n3,.mx-xl-n3{margin-right:-1rem!important}.mb-xl-n3,.my-xl-n3{margin-bottom:-1rem!important}.ml-xl-n3,.mx-xl-n3{margin-left:-1rem!important}.m-xl-n4{margin:-1.5rem!important}.mt-xl-n4,.my-xl-n4{margin-top:-1.5rem!important}.mr-xl-n4,.mx-xl-n4{margin-right:-1.5rem!important}.mb-xl-n4,.my-xl-n4{margin-bottom:-1.5rem!important}.ml-xl-n4,.mx-xl-n4{margin-left:-1.5rem!important}.m-xl-n5{margin:-3rem!important}.mt-xl-n5,.my-xl-n5{margin-top:-3rem!important}.mr-xl-n5,.mx-xl-n5{margin-right:-3rem!important}.mb-xl-n5,.my-xl-n5{margin-bottom:-3rem!important}.ml-xl-n5,.mx-xl-n5{margin-left:-3rem!important}.m-xl-auto{margin:auto!important}.mt-xl-auto,.my-xl-auto{margin-top:auto!important}.mr-xl-auto,.mx-xl-auto{margin-right:auto!important}.mb-xl-auto,.my-xl-auto{margin-bottom:auto!important}.ml-xl-auto,.mx-xl-auto{margin-left:auto!important}}.stretched-link::after{position:absolute;top:0;right:0;bottom:0;left:0;z-index:1;pointer-events:auto;content:"";background-color:rgba(0,0,0,0)}.text-monospace{font-family:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace!important}.text-justify{text-align:justify!important}.text-wrap{white-space:normal!important}.text-nowrap{white-space:nowrap!important}.text-truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.text-left{text-align:left!important}.text-right{text-align:right!important}.text-center{text-align:center!important}@media (min-width:576px){.text-sm-left{text-align:left!important}.text-sm-right{text-align:right!important}.text-sm-center{text-align:center!important}}@media (min-width:768px){.text-md-left{text-align:left!important}.text-md-right{text-align:right!important}.text-md-center{text-align:center!important}}@media (min-width:992px){.text-lg-left{text-align:left!important}.text-lg-right{text-align:right!important}.text-lg-center{text-align:center!important}}@media (min-width:1200px){.text-xl-left{text-align:left!important}.text-xl-right{text-align:right!important}.text-xl-center{text-align:center!important}}.text-lowercase{text-transform:lowercase!important}.text-uppercase{text-transform:uppercase!important}.text-capitalize{text-transform:capitalize!important}.font-weight-light{font-weight:300!important}.font-weight-lighter{font-weight:lighter!important}.font-weight-normal{font-weight:400!important}.font-weight-bold{font-weight:700!important}.font-weight-bolder{font-weight:bolder!important}.font-italic{font-style:italic!important}.text-white{color:#fff!important}.text-primary{color:#007bff!important}a.text-primary:focus,a.text-primary:hover{color:#0056b3!important}.text-secondary{color:#6c757d!important}a.text-secondary:focus,a.text-secondary:hover{color:#494f54!important}.text-success{color:#28a745!important}a.text-success:focus,a.text-success:hover{color:#19692c!important}.text-info{color:#17a2b8!important}a.text-info:focus,a.text-info:hover{color:#0f6674!important}.text-warning{color:#ffc107!important}a.text-warning:focus,a.text-warning:hover{color:#ba8b00!important}.text-danger{color:#dc3545!important}a.text-danger:focus,a.text-danger:hover{color:#a71d2a!important}.text-light{color:#f8f9fa!important}a.text-light:focus,a.text-light:hover{color:#cbd3da!important}.text-dark{color:#343a40!important}a.text-dark:focus,a.text-dark:hover{color:#121416!important}.text-body{color:#212529!important}.text-muted{color:#6c757d!important}.text-black-50{color:rgba(0,0,0,.5)!important}.text-white-50{color:rgba(255,255,255,.5)!important}.text-hide{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.text-decoration-none{text-decoration:none!important}.text-break{word-break:break-word!important;word-wrap:break-word!important}.text-reset{color:inherit!important}.visible{visibility:visible!important}.invisible{visibility:hidden!important}@media print{*,::after,::before{text-shadow:none!important;box-shadow:none!important}a:not(.btn){text-decoration:underline}abbr[title]::after{content:" (" attr(title) ")"}pre{white-space:pre-wrap!important}blockquote,pre{border:1px solid #adb5bd;page-break-inside:avoid}thead{display:table-header-group}img,tr{page-break-inside:avoid}h2,h3,p{orphans:3;widows:3}h2,h3{page-break-after:avoid}@page{size:a3}body{min-width:992px!important}.container{min-width:992px!important}.navbar{display:none}.badge{border:1px solid #000}.table{border-collapse:collapse!important}.table td,.table th{background-color:#fff!important}.table-bordered td,.table-bordered th{border:1px solid #dee2e6!important}.table-dark{color:inherit}.table-dark tbody+tbody,.table-dark td,.table-dark th,.table-dark thead th{border-color:#dee2e6}.table .thead-dark th{color:inherit;border-color:#dee2e6}}
7
- /*# sourceMappingURL=bootstrap.min.css.map */
 
 
 
 
 
 
 
resources/css/{mainwp.css → mainwp-extension.css} RENAMED
File without changes
resources/css/plugin.css CHANGED
@@ -762,7 +762,7 @@ th.column-trans {
762
  width: 114px;
763
  }
764
  th.column-request_info {
765
- width: 100px;
766
  }
767
  /** INSIGHTS **/
768
  /* TABLES */
762
  width: 114px;
763
  }
764
  th.column-request_info {
765
+ width: 120px;
766
  }
767
  /** INSIGHTS **/
768
  /* TABLES */
resources/images/shield/background-blob.svg ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <svg xmlns="http://www.w3.org/2000/svg"
2
+ width="943.431" height="890.376" viewBox="0 0 943.431 890.376"
3
+ style="transform: scale(0.9) translateX(10%);"
4
+ class="w-full h-full md:h-64 lg:h-full lg:mt-0 mt-12">
5
+ <g transform="translate(-643.895 11)">
6
+ <path style="fill:#5c9d04;opacity:.35;"
7
+ d="M555.3,327.441c-43.408,77.248-113.273,9.045-242.451,24.411S94.4,458.988,38.594,388.9-22.334,158.029,89.639,71.518,370.069-21.118,486.485,42.862,598.712,250.194,555.3,327.441Z"
8
+ transform="matrix(-0.966, 0.259, -0.259, -0.966, 1494.893, 663.611)" />
9
+ <path style="opacity:0.178;fill:#5c9d04;"
10
+ d="M33,467.787C84.03,578.144,166.168,480.709,318.039,502.66s256.832,153.056,322.439,52.933,71.631-329.832-60.013-453.422S250.772-30.171,113.905,61.233-18.037,357.43,33,467.787Z"
11
+ transform="translate(1587.326 285.412) rotate(120)" />
12
+ <path style="opacity:0.351;fill:#5c9d04;"
13
+ d="M555.3,116.48C511.9,13.675,442.031,104.442,312.853,83.992S94.4-58.588,38.594,34.682-22.334,341.94,89.639,457.073s280.429,123.284,396.846,38.137S598.712,219.284,555.3,116.48Z"
14
+ transform="matrix(0.966, 0.259, -0.259, 0.966, 786.828, 94.786)" />
15
+ <g transform="translate(844 262.148)" class="hidden lg:block">
16
+ </g>
17
+ </g>
18
+ </svg>
resources/images/shield/dash-background.jpg DELETED
Binary file
resources/js/bootstrap4.bundle.min.js DELETED
@@ -1,7 +0,0 @@
1
- /*!
2
- * Bootstrap v4.5.3 (https://getbootstrap.com/)
3
- * Copyright 2011-2020 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)
4
- * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
5
- */
6
- !function(t,e){"object"==typeof exports&&"undefined"!=typeof module?e(exports,require("jquery")):"function"==typeof define&&define.amd?define(["exports","jquery"],e):e((t="undefined"!=typeof globalThis?globalThis:t||self).bootstrap={},t.jQuery)}(this,(function(t,e){"use strict";function n(t){return t&&"object"==typeof t&&"default"in t?t:{default:t}}var i=n(e);function o(t,e){for(var n=0;n<e.length;n++){var i=e[n];i.enumerable=i.enumerable||!1,i.configurable=!0,"value"in i&&(i.writable=!0),Object.defineProperty(t,i.key,i)}}function r(t,e,n){return e&&o(t.prototype,e),n&&o(t,n),t}function a(){return(a=Object.assign||function(t){for(var e=1;e<arguments.length;e++){var n=arguments[e];for(var i in n)Object.prototype.hasOwnProperty.call(n,i)&&(t[i]=n[i])}return t}).apply(this,arguments)}function s(t){var e=this,n=!1;return i.default(this).one(l.TRANSITION_END,(function(){n=!0})),setTimeout((function(){n||l.triggerTransitionEnd(e)}),t),this}var l={TRANSITION_END:"bsTransitionEnd",getUID:function(t){do{t+=~~(1e6*Math.random())}while(document.getElementById(t));return t},getSelectorFromElement:function(t){var e=t.getAttribute("data-target");if(!e||"#"===e){var n=t.getAttribute("href");e=n&&"#"!==n?n.trim():""}try{return document.querySelector(e)?e:null}catch(t){return null}},getTransitionDurationFromElement:function(t){if(!t)return 0;var e=i.default(t).css("transition-duration"),n=i.default(t).css("transition-delay"),o=parseFloat(e),r=parseFloat(n);return o||r?(e=e.split(",")[0],n=n.split(",")[0],1e3*(parseFloat(e)+parseFloat(n))):0},reflow:function(t){return t.offsetHeight},triggerTransitionEnd:function(t){i.default(t).trigger("transitionend")},supportsTransitionEnd:function(){return Boolean("transitionend")},isElement:function(t){return(t[0]||t).nodeType},typeCheckConfig:function(t,e,n){for(var i in n)if(Object.prototype.hasOwnProperty.call(n,i)){var o=n[i],r=e[i],a=r&&l.isElement(r)?"element":null===(s=r)||"undefined"==typeof s?""+s:{}.toString.call(s).match(/\s([a-z]+)/i)[1].toLowerCase();if(!new RegExp(o).test(a))throw new Error(t.toUpperCase()+': Option "'+i+'" provided type "'+a+'" but expected type "'+o+'".')}var s},findShadowRoot:function(t){if(!document.documentElement.attachShadow)return null;if("function"==typeof t.getRootNode){var e=t.getRootNode();return e instanceof ShadowRoot?e:null}return t instanceof ShadowRoot?t:t.parentNode?l.findShadowRoot(t.parentNode):null},jQueryDetection:function(){if("undefined"==typeof i.default)throw new TypeError("Bootstrap's JavaScript requires jQuery. jQuery must be included before Bootstrap's JavaScript.");var t=i.default.fn.jquery.split(" ")[0].split(".");if(t[0]<2&&t[1]<9||1===t[0]&&9===t[1]&&t[2]<1||t[0]>=4)throw new Error("Bootstrap's JavaScript requires at least jQuery v1.9.1 but less than v4.0.0")}};l.jQueryDetection(),i.default.fn.emulateTransitionEnd=s,i.default.event.special[l.TRANSITION_END]={bindType:"transitionend",delegateType:"transitionend",handle:function(t){if(i.default(t.target).is(this))return t.handleObj.handler.apply(this,arguments)}};var u="alert",f=i.default.fn[u],d=function(){function t(t){this._element=t}var e=t.prototype;return e.close=function(t){var e=this._element;t&&(e=this._getRootElement(t)),this._triggerCloseEvent(e).isDefaultPrevented()||this._removeElement(e)},e.dispose=function(){i.default.removeData(this._element,"bs.alert"),this._element=null},e._getRootElement=function(t){var e=l.getSelectorFromElement(t),n=!1;return e&&(n=document.querySelector(e)),n||(n=i.default(t).closest(".alert")[0]),n},e._triggerCloseEvent=function(t){var e=i.default.Event("close.bs.alert");return i.default(t).trigger(e),e},e._removeElement=function(t){var e=this;if(i.default(t).removeClass("show"),i.default(t).hasClass("fade")){var n=l.getTransitionDurationFromElement(t);i.default(t).one(l.TRANSITION_END,(function(n){return e._destroyElement(t,n)})).emulateTransitionEnd(n)}else this._destroyElement(t)},e._destroyElement=function(t){i.default(t).detach().trigger("closed.bs.alert").remove()},t._jQueryInterface=function(e){return this.each((function(){var n=i.default(this),o=n.data("bs.alert");o||(o=new t(this),n.data("bs.alert",o)),"close"===e&&o[e](this)}))},t._handleDismiss=function(t){return function(e){e&&e.preventDefault(),t.close(this)}},r(t,null,[{key:"VERSION",get:function(){return"4.5.3"}}]),t}();i.default(document).on("click.bs.alert.data-api",'[data-dismiss="alert"]',d._handleDismiss(new d)),i.default.fn[u]=d._jQueryInterface,i.default.fn[u].Constructor=d,i.default.fn[u].noConflict=function(){return i.default.fn[u]=f,d._jQueryInterface};var c=i.default.fn.button,h=function(){function t(t){this._element=t,this.shouldAvoidTriggerChange=!1}var e=t.prototype;return e.toggle=function(){var t=!0,e=!0,n=i.default(this._element).closest('[data-toggle="buttons"]')[0];if(n){var o=this._element.querySelector('input:not([type="hidden"])');if(o){if("radio"===o.type)if(o.checked&&this._element.classList.contains("active"))t=!1;else{var r=n.querySelector(".active");r&&i.default(r).removeClass("active")}t&&("checkbox"!==o.type&&"radio"!==o.type||(o.checked=!this._element.classList.contains("active")),this.shouldAvoidTriggerChange||i.default(o).trigger("change")),o.focus(),e=!1}}this._element.hasAttribute("disabled")||this._element.classList.contains("disabled")||(e&&this._element.setAttribute("aria-pressed",!this._element.classList.contains("active")),t&&i.default(this._element).toggleClass("active"))},e.dispose=function(){i.default.removeData(this._element,"bs.button"),this._element=null},t._jQueryInterface=function(e,n){return this.each((function(){var o=i.default(this),r=o.data("bs.button");r||(r=new t(this),o.data("bs.button",r)),r.shouldAvoidTriggerChange=n,"toggle"===e&&r[e]()}))},r(t,null,[{key:"VERSION",get:function(){return"4.5.3"}}]),t}();i.default(document).on("click.bs.button.data-api",'[data-toggle^="button"]',(function(t){var e=t.target,n=e;if(i.default(e).hasClass("btn")||(e=i.default(e).closest(".btn")[0]),!e||e.hasAttribute("disabled")||e.classList.contains("disabled"))t.preventDefault();else{var o=e.querySelector('input:not([type="hidden"])');if(o&&(o.hasAttribute("disabled")||o.classList.contains("disabled")))return void t.preventDefault();"INPUT"!==n.tagName&&"LABEL"===e.tagName||h._jQueryInterface.call(i.default(e),"toggle","INPUT"===n.tagName)}})).on("focus.bs.button.data-api blur.bs.button.data-api",'[data-toggle^="button"]',(function(t){var e=i.default(t.target).closest(".btn")[0];i.default(e).toggleClass("focus",/^focus(in)?$/.test(t.type))})),i.default(window).on("load.bs.button.data-api",(function(){for(var t=[].slice.call(document.querySelectorAll('[data-toggle="buttons"] .btn')),e=0,n=t.length;e<n;e++){var i=t[e],o=i.querySelector('input:not([type="hidden"])');o.checked||o.hasAttribute("checked")?i.classList.add("active"):i.classList.remove("active")}for(var r=0,a=(t=[].slice.call(document.querySelectorAll('[data-toggle="button"]'))).length;r<a;r++){var s=t[r];"true"===s.getAttribute("aria-pressed")?s.classList.add("active"):s.classList.remove("active")}})),i.default.fn.button=h._jQueryInterface,i.default.fn.button.Constructor=h,i.default.fn.button.noConflict=function(){return i.default.fn.button=c,h._jQueryInterface};var p="carousel",m=".bs.carousel",g=i.default.fn[p],v={interval:5e3,keyboard:!0,slide:!1,pause:"hover",wrap:!0,touch:!0},_={interval:"(number|boolean)",keyboard:"boolean",slide:"(boolean|string)",pause:"(string|boolean)",wrap:"boolean",touch:"boolean"},b={TOUCH:"touch",PEN:"pen"},y=function(){function t(t,e){this._items=null,this._interval=null,this._activeElement=null,this._isPaused=!1,this._isSliding=!1,this.touchTimeout=null,this.touchStartX=0,this.touchDeltaX=0,this._config=this._getConfig(e),this._element=t,this._indicatorsElement=this._element.querySelector(".carousel-indicators"),this._touchSupported="ontouchstart"in document.documentElement||navigator.maxTouchPoints>0,this._pointerEvent=Boolean(window.PointerEvent||window.MSPointerEvent),this._addEventListeners()}var e=t.prototype;return e.next=function(){this._isSliding||this._slide("next")},e.nextWhenVisible=function(){var t=i.default(this._element);!document.hidden&&t.is(":visible")&&"hidden"!==t.css("visibility")&&this.next()},e.prev=function(){this._isSliding||this._slide("prev")},e.pause=function(t){t||(this._isPaused=!0),this._element.querySelector(".carousel-item-next, .carousel-item-prev")&&(l.triggerTransitionEnd(this._element),this.cycle(!0)),clearInterval(this._interval),this._interval=null},e.cycle=function(t){t||(this._isPaused=!1),this._interval&&(clearInterval(this._interval),this._interval=null),this._config.interval&&!this._isPaused&&(this._interval=setInterval((document.visibilityState?this.nextWhenVisible:this.next).bind(this),this._config.interval))},e.to=function(t){var e=this;this._activeElement=this._element.querySelector(".active.carousel-item");var n=this._getItemIndex(this._activeElement);if(!(t>this._items.length-1||t<0))if(this._isSliding)i.default(this._element).one("slid.bs.carousel",(function(){return e.to(t)}));else{if(n===t)return this.pause(),void this.cycle();var o=t>n?"next":"prev";this._slide(o,this._items[t])}},e.dispose=function(){i.default(this._element).off(m),i.default.removeData(this._element,"bs.carousel"),this._items=null,this._config=null,this._element=null,this._interval=null,this._isPaused=null,this._isSliding=null,this._activeElement=null,this._indicatorsElement=null},e._getConfig=function(t){return t=a({},v,t),l.typeCheckConfig(p,t,_),t},e._handleSwipe=function(){var t=Math.abs(this.touchDeltaX);if(!(t<=40)){var e=t/this.touchDeltaX;this.touchDeltaX=0,e>0&&this.prev(),e<0&&this.next()}},e._addEventListeners=function(){var t=this;this._config.keyboard&&i.default(this._element).on("keydown.bs.carousel",(function(e){return t._keydown(e)})),"hover"===this._config.pause&&i.default(this._element).on("mouseenter.bs.carousel",(function(e){return t.pause(e)})).on("mouseleave.bs.carousel",(function(e){return t.cycle(e)})),this._config.touch&&this._addTouchEventListeners()},e._addTouchEventListeners=function(){var t=this;if(this._touchSupported){var e=function(e){t._pointerEvent&&b[e.originalEvent.pointerType.toUpperCase()]?t.touchStartX=e.originalEvent.clientX:t._pointerEvent||(t.touchStartX=e.originalEvent.touches[0].clientX)},n=function(e){t._pointerEvent&&b[e.originalEvent.pointerType.toUpperCase()]&&(t.touchDeltaX=e.originalEvent.clientX-t.touchStartX),t._handleSwipe(),"hover"===t._config.pause&&(t.pause(),t.touchTimeout&&clearTimeout(t.touchTimeout),t.touchTimeout=setTimeout((function(e){return t.cycle(e)}),500+t._config.interval))};i.default(this._element.querySelectorAll(".carousel-item img")).on("dragstart.bs.carousel",(function(t){return t.preventDefault()})),this._pointerEvent?(i.default(this._element).on("pointerdown.bs.carousel",(function(t){return e(t)})),i.default(this._element).on("pointerup.bs.carousel",(function(t){return n(t)})),this._element.classList.add("pointer-event")):(i.default(this._element).on("touchstart.bs.carousel",(function(t){return e(t)})),i.default(this._element).on("touchmove.bs.carousel",(function(e){return function(e){e.originalEvent.touches&&e.originalEvent.touches.length>1?t.touchDeltaX=0:t.touchDeltaX=e.originalEvent.touches[0].clientX-t.touchStartX}(e)})),i.default(this._element).on("touchend.bs.carousel",(function(t){return n(t)})))}},e._keydown=function(t){if(!/input|textarea/i.test(t.target.tagName))switch(t.which){case 37:t.preventDefault(),this.prev();break;case 39:t.preventDefault(),this.next()}},e._getItemIndex=function(t){return this._items=t&&t.parentNode?[].slice.call(t.parentNode.querySelectorAll(".carousel-item")):[],this._items.indexOf(t)},e._getItemByDirection=function(t,e){var n="next"===t,i="prev"===t,o=this._getItemIndex(e),r=this._items.length-1;if((i&&0===o||n&&o===r)&&!this._config.wrap)return e;var a=(o+("prev"===t?-1:1))%this._items.length;return-1===a?this._items[this._items.length-1]:this._items[a]},e._triggerSlideEvent=function(t,e){var n=this._getItemIndex(t),o=this._getItemIndex(this._element.querySelector(".active.carousel-item")),r=i.default.Event("slide.bs.carousel",{relatedTarget:t,direction:e,from:o,to:n});return i.default(this._element).trigger(r),r},e._setActiveIndicatorElement=function(t){if(this._indicatorsElement){var e=[].slice.call(this._indicatorsElement.querySelectorAll(".active"));i.default(e).removeClass("active");var n=this._indicatorsElement.children[this._getItemIndex(t)];n&&i.default(n).addClass("active")}},e._slide=function(t,e){var n,o,r,a=this,s=this._element.querySelector(".active.carousel-item"),u=this._getItemIndex(s),f=e||s&&this._getItemByDirection(t,s),d=this._getItemIndex(f),c=Boolean(this._interval);if("next"===t?(n="carousel-item-left",o="carousel-item-next",r="left"):(n="carousel-item-right",o="carousel-item-prev",r="right"),f&&i.default(f).hasClass("active"))this._isSliding=!1;else if(!this._triggerSlideEvent(f,r).isDefaultPrevented()&&s&&f){this._isSliding=!0,c&&this.pause(),this._setActiveIndicatorElement(f);var h=i.default.Event("slid.bs.carousel",{relatedTarget:f,direction:r,from:u,to:d});if(i.default(this._element).hasClass("slide")){i.default(f).addClass(o),l.reflow(f),i.default(s).addClass(n),i.default(f).addClass(n);var p=parseInt(f.getAttribute("data-interval"),10);p?(this._config.defaultInterval=this._config.defaultInterval||this._config.interval,this._config.interval=p):this._config.interval=this._config.defaultInterval||this._config.interval;var m=l.getTransitionDurationFromElement(s);i.default(s).one(l.TRANSITION_END,(function(){i.default(f).removeClass(n+" "+o).addClass("active"),i.default(s).removeClass("active "+o+" "+n),a._isSliding=!1,setTimeout((function(){return i.default(a._element).trigger(h)}),0)})).emulateTransitionEnd(m)}else i.default(s).removeClass("active"),i.default(f).addClass("active"),this._isSliding=!1,i.default(this._element).trigger(h);c&&this.cycle()}},t._jQueryInterface=function(e){return this.each((function(){var n=i.default(this).data("bs.carousel"),o=a({},v,i.default(this).data());"object"==typeof e&&(o=a({},o,e));var r="string"==typeof e?e:o.slide;if(n||(n=new t(this,o),i.default(this).data("bs.carousel",n)),"number"==typeof e)n.to(e);else if("string"==typeof r){if("undefined"==typeof n[r])throw new TypeError('No method named "'+r+'"');n[r]()}else o.interval&&o.ride&&(n.pause(),n.cycle())}))},t._dataApiClickHandler=function(e){var n=l.getSelectorFromElement(this);if(n){var o=i.default(n)[0];if(o&&i.default(o).hasClass("carousel")){var r=a({},i.default(o).data(),i.default(this).data()),s=this.getAttribute("data-slide-to");s&&(r.interval=!1),t._jQueryInterface.call(i.default(o),r),s&&i.default(o).data("bs.carousel").to(s),e.preventDefault()}}},r(t,null,[{key:"VERSION",get:function(){return"4.5.3"}},{key:"Default",get:function(){return v}}]),t}();i.default(document).on("click.bs.carousel.data-api","[data-slide], [data-slide-to]",y._dataApiClickHandler),i.default(window).on("load.bs.carousel.data-api",(function(){for(var t=[].slice.call(document.querySelectorAll('[data-ride="carousel"]')),e=0,n=t.length;e<n;e++){var o=i.default(t[e]);y._jQueryInterface.call(o,o.data())}})),i.default.fn[p]=y._jQueryInterface,i.default.fn[p].Constructor=y,i.default.fn[p].noConflict=function(){return i.default.fn[p]=g,y._jQueryInterface};var w="collapse",E=i.default.fn[w],T={toggle:!0,parent:""},C={toggle:"boolean",parent:"(string|element)"},S=function(){function t(t,e){this._isTransitioning=!1,this._element=t,this._config=this._getConfig(e),this._triggerArray=[].slice.call(document.querySelectorAll('[data-toggle="collapse"][href="#'+t.id+'"],[data-toggle="collapse"][data-target="#'+t.id+'"]'));for(var n=[].slice.call(document.querySelectorAll('[data-toggle="collapse"]')),i=0,o=n.length;i<o;i++){var r=n[i],a=l.getSelectorFromElement(r),s=[].slice.call(document.querySelectorAll(a)).filter((function(e){return e===t}));null!==a&&s.length>0&&(this._selector=a,this._triggerArray.push(r))}this._parent=this._config.parent?this._getParent():null,this._config.parent||this._addAriaAndCollapsedClass(this._element,this._triggerArray),this._config.toggle&&this.toggle()}var e=t.prototype;return e.toggle=function(){i.default(this._element).hasClass("show")?this.hide():this.show()},e.show=function(){var e,n,o=this;if(!this._isTransitioning&&!i.default(this._element).hasClass("show")&&(this._parent&&0===(e=[].slice.call(this._parent.querySelectorAll(".show, .collapsing")).filter((function(t){return"string"==typeof o._config.parent?t.getAttribute("data-parent")===o._config.parent:t.classList.contains("collapse")}))).length&&(e=null),!(e&&(n=i.default(e).not(this._selector).data("bs.collapse"))&&n._isTransitioning))){var r=i.default.Event("show.bs.collapse");if(i.default(this._element).trigger(r),!r.isDefaultPrevented()){e&&(t._jQueryInterface.call(i.default(e).not(this._selector),"hide"),n||i.default(e).data("bs.collapse",null));var a=this._getDimension();i.default(this._element).removeClass("collapse").addClass("collapsing"),this._element.style[a]=0,this._triggerArray.length&&i.default(this._triggerArray).removeClass("collapsed").attr("aria-expanded",!0),this.setTransitioning(!0);var s="scroll"+(a[0].toUpperCase()+a.slice(1)),u=l.getTransitionDurationFromElement(this._element);i.default(this._element).one(l.TRANSITION_END,(function(){i.default(o._element).removeClass("collapsing").addClass("collapse show"),o._element.style[a]="",o.setTransitioning(!1),i.default(o._element).trigger("shown.bs.collapse")})).emulateTransitionEnd(u),this._element.style[a]=this._element[s]+"px"}}},e.hide=function(){var t=this;if(!this._isTransitioning&&i.default(this._element).hasClass("show")){var e=i.default.Event("hide.bs.collapse");if(i.default(this._element).trigger(e),!e.isDefaultPrevented()){var n=this._getDimension();this._element.style[n]=this._element.getBoundingClientRect()[n]+"px",l.reflow(this._element),i.default(this._element).addClass("collapsing").removeClass("collapse show");var o=this._triggerArray.length;if(o>0)for(var r=0;r<o;r++){var a=this._triggerArray[r],s=l.getSelectorFromElement(a);if(null!==s)i.default([].slice.call(document.querySelectorAll(s))).hasClass("show")||i.default(a).addClass("collapsed").attr("aria-expanded",!1)}this.setTransitioning(!0);this._element.style[n]="";var u=l.getTransitionDurationFromElement(this._element);i.default(this._element).one(l.TRANSITION_END,(function(){t.setTransitioning(!1),i.default(t._element).removeClass("collapsing").addClass("collapse").trigger("hidden.bs.collapse")})).emulateTransitionEnd(u)}}},e.setTransitioning=function(t){this._isTransitioning=t},e.dispose=function(){i.default.removeData(this._element,"bs.collapse"),this._config=null,this._parent=null,this._element=null,this._triggerArray=null,this._isTransitioning=null},e._getConfig=function(t){return(t=a({},T,t)).toggle=Boolean(t.toggle),l.typeCheckConfig(w,t,C),t},e._getDimension=function(){return i.default(this._element).hasClass("width")?"width":"height"},e._getParent=function(){var e,n=this;l.isElement(this._config.parent)?(e=this._config.parent,"undefined"!=typeof this._config.parent.jquery&&(e=this._config.parent[0])):e=document.querySelector(this._config.parent);var o='[data-toggle="collapse"][data-parent="'+this._config.parent+'"]',r=[].slice.call(e.querySelectorAll(o));return i.default(r).each((function(e,i){n._addAriaAndCollapsedClass(t._getTargetFromElement(i),[i])})),e},e._addAriaAndCollapsedClass=function(t,e){var n=i.default(t).hasClass("show");e.length&&i.default(e).toggleClass("collapsed",!n).attr("aria-expanded",n)},t._getTargetFromElement=function(t){var e=l.getSelectorFromElement(t);return e?document.querySelector(e):null},t._jQueryInterface=function(e){return this.each((function(){var n=i.default(this),o=n.data("bs.collapse"),r=a({},T,n.data(),"object"==typeof e&&e?e:{});if(!o&&r.toggle&&"string"==typeof e&&/show|hide/.test(e)&&(r.toggle=!1),o||(o=new t(this,r),n.data("bs.collapse",o)),"string"==typeof e){if("undefined"==typeof o[e])throw new TypeError('No method named "'+e+'"');o[e]()}}))},r(t,null,[{key:"VERSION",get:function(){return"4.5.3"}},{key:"Default",get:function(){return T}}]),t}();i.default(document).on("click.bs.collapse.data-api",'[data-toggle="collapse"]',(function(t){"A"===t.currentTarget.tagName&&t.preventDefault();var e=i.default(this),n=l.getSelectorFromElement(this),o=[].slice.call(document.querySelectorAll(n));i.default(o).each((function(){var t=i.default(this),n=t.data("bs.collapse")?"toggle":e.data();S._jQueryInterface.call(t,n)}))})),i.default.fn[w]=S._jQueryInterface,i.default.fn[w].Constructor=S,i.default.fn[w].noConflict=function(){return i.default.fn[w]=E,S._jQueryInterface};var D="undefined"!=typeof window&&"undefined"!=typeof document&&"undefined"!=typeof navigator,N=function(){for(var t=["Edge","Trident","Firefox"],e=0;e<t.length;e+=1)if(D&&navigator.userAgent.indexOf(t[e])>=0)return 1;return 0}();var k=D&&window.Promise?function(t){var e=!1;return function(){e||(e=!0,window.Promise.resolve().then((function(){e=!1,t()})))}}:function(t){var e=!1;return function(){e||(e=!0,setTimeout((function(){e=!1,t()}),N))}};function A(t){return t&&"[object Function]"==={}.toString.call(t)}function I(t,e){if(1!==t.nodeType)return[];var n=t.ownerDocument.defaultView.getComputedStyle(t,null);return e?n[e]:n}function O(t){return"HTML"===t.nodeName?t:t.parentNode||t.host}function x(t){if(!t)return document.body;switch(t.nodeName){case"HTML":case"BODY":return t.ownerDocument.body;case"#document":return t.body}var e=I(t),n=e.overflow,i=e.overflowX,o=e.overflowY;return/(auto|scroll|overlay)/.test(n+o+i)?t:x(O(t))}function j(t){return t&&t.referenceNode?t.referenceNode:t}var L=D&&!(!window.MSInputMethodContext||!document.documentMode),P=D&&/MSIE 10/.test(navigator.userAgent);function F(t){return 11===t?L:10===t?P:L||P}function R(t){if(!t)return document.documentElement;for(var e=F(10)?document.body:null,n=t.offsetParent||null;n===e&&t.nextElementSibling;)n=(t=t.nextElementSibling).offsetParent;var i=n&&n.nodeName;return i&&"BODY"!==i&&"HTML"!==i?-1!==["TH","TD","TABLE"].indexOf(n.nodeName)&&"static"===I(n,"position")?R(n):n:t?t.ownerDocument.documentElement:document.documentElement}function H(t){return null!==t.parentNode?H(t.parentNode):t}function M(t,e){if(!(t&&t.nodeType&&e&&e.nodeType))return document.documentElement;var n=t.compareDocumentPosition(e)&Node.DOCUMENT_POSITION_FOLLOWING,i=n?t:e,o=n?e:t,r=document.createRange();r.setStart(i,0),r.setEnd(o,0);var a,s,l=r.commonAncestorContainer;if(t!==l&&e!==l||i.contains(o))return"BODY"===(s=(a=l).nodeName)||"HTML"!==s&&R(a.firstElementChild)!==a?R(l):l;var u=H(t);return u.host?M(u.host,e):M(t,H(e).host)}function B(t){var e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"top",n="top"===e?"scrollTop":"scrollLeft",i=t.nodeName;if("BODY"===i||"HTML"===i){var o=t.ownerDocument.documentElement,r=t.ownerDocument.scrollingElement||o;return r[n]}return t[n]}function q(t,e){var n=arguments.length>2&&void 0!==arguments[2]&&arguments[2],i=B(e,"top"),o=B(e,"left"),r=n?-1:1;return t.top+=i*r,t.bottom+=i*r,t.left+=o*r,t.right+=o*r,t}function Q(t,e){var n="x"===e?"Left":"Top",i="Left"===n?"Right":"Bottom";return parseFloat(t["border"+n+"Width"])+parseFloat(t["border"+i+"Width"])}function W(t,e,n,i){return Math.max(e["offset"+t],e["scroll"+t],n["client"+t],n["offset"+t],n["scroll"+t],F(10)?parseInt(n["offset"+t])+parseInt(i["margin"+("Height"===t?"Top":"Left")])+parseInt(i["margin"+("Height"===t?"Bottom":"Right")]):0)}function U(t){var e=t.body,n=t.documentElement,i=F(10)&&getComputedStyle(n);return{height:W("Height",e,n,i),width:W("Width",e,n,i)}}var V=function(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")},Y=function(){function t(t,e){for(var n=0;n<e.length;n++){var i=e[n];i.enumerable=i.enumerable||!1,i.configurable=!0,"value"in i&&(i.writable=!0),Object.defineProperty(t,i.key,i)}}return function(e,n,i){return n&&t(e.prototype,n),i&&t(e,i),e}}(),z=function(t,e,n){return e in t?Object.defineProperty(t,e,{value:n,enumerable:!0,configurable:!0,writable:!0}):t[e]=n,t},X=Object.assign||function(t){for(var e=1;e<arguments.length;e++){var n=arguments[e];for(var i in n)Object.prototype.hasOwnProperty.call(n,i)&&(t[i]=n[i])}return t};function K(t){return X({},t,{right:t.left+t.width,bottom:t.top+t.height})}function G(t){var e={};try{if(F(10)){e=t.getBoundingClientRect();var n=B(t,"top"),i=B(t,"left");e.top+=n,e.left+=i,e.bottom+=n,e.right+=i}else e=t.getBoundingClientRect()}catch(t){}var o={left:e.left,top:e.top,width:e.right-e.left,height:e.bottom-e.top},r="HTML"===t.nodeName?U(t.ownerDocument):{},a=r.width||t.clientWidth||o.width,s=r.height||t.clientHeight||o.height,l=t.offsetWidth-a,u=t.offsetHeight-s;if(l||u){var f=I(t);l-=Q(f,"x"),u-=Q(f,"y"),o.width-=l,o.height-=u}return K(o)}function $(t,e){var n=arguments.length>2&&void 0!==arguments[2]&&arguments[2],i=F(10),o="HTML"===e.nodeName,r=G(t),a=G(e),s=x(t),l=I(e),u=parseFloat(l.borderTopWidth),f=parseFloat(l.borderLeftWidth);n&&o&&(a.top=Math.max(a.top,0),a.left=Math.max(a.left,0));var d=K({top:r.top-a.top-u,left:r.left-a.left-f,width:r.width,height:r.height});if(d.marginTop=0,d.marginLeft=0,!i&&o){var c=parseFloat(l.marginTop),h=parseFloat(l.marginLeft);d.top-=u-c,d.bottom-=u-c,d.left-=f-h,d.right-=f-h,d.marginTop=c,d.marginLeft=h}return(i&&!n?e.contains(s):e===s&&"BODY"!==s.nodeName)&&(d=q(d,e)),d}function J(t){var e=arguments.length>1&&void 0!==arguments[1]&&arguments[1],n=t.ownerDocument.documentElement,i=$(t,n),o=Math.max(n.clientWidth,window.innerWidth||0),r=Math.max(n.clientHeight,window.innerHeight||0),a=e?0:B(n),s=e?0:B(n,"left"),l={top:a-i.top+i.marginTop,left:s-i.left+i.marginLeft,width:o,height:r};return K(l)}function Z(t){var e=t.nodeName;if("BODY"===e||"HTML"===e)return!1;if("fixed"===I(t,"position"))return!0;var n=O(t);return!!n&&Z(n)}function tt(t){if(!t||!t.parentElement||F())return document.documentElement;for(var e=t.parentElement;e&&"none"===I(e,"transform");)e=e.parentElement;return e||document.documentElement}function et(t,e,n,i){var o=arguments.length>4&&void 0!==arguments[4]&&arguments[4],r={top:0,left:0},a=o?tt(t):M(t,j(e));if("viewport"===i)r=J(a,o);else{var s=void 0;"scrollParent"===i?"BODY"===(s=x(O(e))).nodeName&&(s=t.ownerDocument.documentElement):s="window"===i?t.ownerDocument.documentElement:i;var l=$(s,a,o);if("HTML"!==s.nodeName||Z(a))r=l;else{var u=U(t.ownerDocument),f=u.height,d=u.width;r.top+=l.top-l.marginTop,r.bottom=f+l.top,r.left+=l.left-l.marginLeft,r.right=d+l.left}}var c="number"==typeof(n=n||0);return r.left+=c?n:n.left||0,r.top+=c?n:n.top||0,r.right-=c?n:n.right||0,r.bottom-=c?n:n.bottom||0,r}function nt(t){return t.width*t.height}function it(t,e,n,i,o){var r=arguments.length>5&&void 0!==arguments[5]?arguments[5]:0;if(-1===t.indexOf("auto"))return t;var a=et(n,i,r,o),s={top:{width:a.width,height:e.top-a.top},right:{width:a.right-e.right,height:a.height},bottom:{width:a.width,height:a.bottom-e.bottom},left:{width:e.left-a.left,height:a.height}},l=Object.keys(s).map((function(t){return X({key:t},s[t],{area:nt(s[t])})})).sort((function(t,e){return e.area-t.area})),u=l.filter((function(t){var e=t.width,i=t.height;return e>=n.clientWidth&&i>=n.clientHeight})),f=u.length>0?u[0].key:l[0].key,d=t.split("-")[1];return f+(d?"-"+d:"")}function ot(t,e,n){var i=arguments.length>3&&void 0!==arguments[3]?arguments[3]:null,o=i?tt(e):M(e,j(n));return $(n,o,i)}function rt(t){var e=t.ownerDocument.defaultView.getComputedStyle(t),n=parseFloat(e.marginTop||0)+parseFloat(e.marginBottom||0),i=parseFloat(e.marginLeft||0)+parseFloat(e.marginRight||0);return{width:t.offsetWidth+i,height:t.offsetHeight+n}}function at(t){var e={left:"right",right:"left",bottom:"top",top:"bottom"};return t.replace(/left|right|bottom|top/g,(function(t){return e[t]}))}function st(t,e,n){n=n.split("-")[0];var i=rt(t),o={width:i.width,height:i.height},r=-1!==["right","left"].indexOf(n),a=r?"top":"left",s=r?"left":"top",l=r?"height":"width",u=r?"width":"height";return o[a]=e[a]+e[l]/2-i[l]/2,o[s]=n===s?e[s]-i[u]:e[at(s)],o}function lt(t,e){return Array.prototype.find?t.find(e):t.filter(e)[0]}function ut(t,e,n){return(void 0===n?t:t.slice(0,function(t,e,n){if(Array.prototype.findIndex)return t.findIndex((function(t){return t[e]===n}));var i=lt(t,(function(t){return t[e]===n}));return t.indexOf(i)}(t,"name",n))).forEach((function(t){t.function&&console.warn("`modifier.function` is deprecated, use `modifier.fn`!");var n=t.function||t.fn;t.enabled&&A(n)&&(e.offsets.popper=K(e.offsets.popper),e.offsets.reference=K(e.offsets.reference),e=n(e,t))})),e}function ft(){if(!this.state.isDestroyed){var t={instance:this,styles:{},arrowStyles:{},attributes:{},flipped:!1,offsets:{}};t.offsets.reference=ot(this.state,this.popper,this.reference,this.options.positionFixed),t.placement=it(this.options.placement,t.offsets.reference,this.popper,this.reference,this.options.modifiers.flip.boundariesElement,this.options.modifiers.flip.padding),t.originalPlacement=t.placement,t.positionFixed=this.options.positionFixed,t.offsets.popper=st(this.popper,t.offsets.reference,t.placement),t.offsets.popper.position=this.options.positionFixed?"fixed":"absolute",t=ut(this.modifiers,t),this.state.isCreated?this.options.onUpdate(t):(this.state.isCreated=!0,this.options.onCreate(t))}}function dt(t,e){return t.some((function(t){var n=t.name;return t.enabled&&n===e}))}function ct(t){for(var e=[!1,"ms","Webkit","Moz","O"],n=t.charAt(0).toUpperCase()+t.slice(1),i=0;i<e.length;i++){var o=e[i],r=o?""+o+n:t;if("undefined"!=typeof document.body.style[r])return r}return null}function ht(){return this.state.isDestroyed=!0,dt(this.modifiers,"applyStyle")&&(this.popper.removeAttribute("x-placement"),this.popper.style.position="",this.popper.style.top="",this.popper.style.left="",this.popper.style.right="",this.popper.style.bottom="",this.popper.style.willChange="",this.popper.style[ct("transform")]=""),this.disableEventListeners(),this.options.removeOnDestroy&&this.popper.parentNode.removeChild(this.popper),this}function pt(t){var e=t.ownerDocument;return e?e.defaultView:window}function mt(t,e,n,i){n.updateBound=i,pt(t).addEventListener("resize",n.updateBound,{passive:!0});var o=x(t);return function t(e,n,i,o){var r="BODY"===e.nodeName,a=r?e.ownerDocument.defaultView:e;a.addEventListener(n,i,{passive:!0}),r||t(x(a.parentNode),n,i,o),o.push(a)}(o,"scroll",n.updateBound,n.scrollParents),n.scrollElement=o,n.eventsEnabled=!0,n}function gt(){this.state.eventsEnabled||(this.state=mt(this.reference,this.options,this.state,this.scheduleUpdate))}function vt(){var t,e;this.state.eventsEnabled&&(cancelAnimationFrame(this.scheduleUpdate),this.state=(t=this.reference,e=this.state,pt(t).removeEventListener("resize",e.updateBound),e.scrollParents.forEach((function(t){t.removeEventListener("scroll",e.updateBound)})),e.updateBound=null,e.scrollParents=[],e.scrollElement=null,e.eventsEnabled=!1,e))}function _t(t){return""!==t&&!isNaN(parseFloat(t))&&isFinite(t)}function bt(t,e){Object.keys(e).forEach((function(n){var i="";-1!==["width","height","top","right","bottom","left"].indexOf(n)&&_t(e[n])&&(i="px"),t.style[n]=e[n]+i}))}var yt=D&&/Firefox/i.test(navigator.userAgent);function wt(t,e,n){var i=lt(t,(function(t){return t.name===e})),o=!!i&&t.some((function(t){return t.name===n&&t.enabled&&t.order<i.order}));if(!o){var r="`"+e+"`",a="`"+n+"`";console.warn(a+" modifier is required by "+r+" modifier in order to work, be sure to include it before "+r+"!")}return o}var Et=["auto-start","auto","auto-end","top-start","top","top-end","right-start","right","right-end","bottom-end","bottom","bottom-start","left-end","left","left-start"],Tt=Et.slice(3);function Ct(t){var e=arguments.length>1&&void 0!==arguments[1]&&arguments[1],n=Tt.indexOf(t),i=Tt.slice(n+1).concat(Tt.slice(0,n));return e?i.reverse():i}var St="flip",Dt="clockwise",Nt="counterclockwise";function kt(t,e,n,i){var o=[0,0],r=-1!==["right","left"].indexOf(i),a=t.split(/(\+|\-)/).map((function(t){return t.trim()})),s=a.indexOf(lt(a,(function(t){return-1!==t.search(/,|\s/)})));a[s]&&-1===a[s].indexOf(",")&&console.warn("Offsets separated by white space(s) are deprecated, use a comma (,) instead.");var l=/\s*,\s*|\s+/,u=-1!==s?[a.slice(0,s).concat([a[s].split(l)[0]]),[a[s].split(l)[1]].concat(a.slice(s+1))]:[a];return(u=u.map((function(t,i){var o=(1===i?!r:r)?"height":"width",a=!1;return t.reduce((function(t,e){return""===t[t.length-1]&&-1!==["+","-"].indexOf(e)?(t[t.length-1]=e,a=!0,t):a?(t[t.length-1]+=e,a=!1,t):t.concat(e)}),[]).map((function(t){return function(t,e,n,i){var o=t.match(/((?:\-|\+)?\d*\.?\d*)(.*)/),r=+o[1],a=o[2];if(!r)return t;if(0===a.indexOf("%")){var s=void 0;switch(a){case"%p":s=n;break;case"%":case"%r":default:s=i}return K(s)[e]/100*r}if("vh"===a||"vw"===a){return("vh"===a?Math.max(document.documentElement.clientHeight,window.innerHeight||0):Math.max(document.documentElement.clientWidth,window.innerWidth||0))/100*r}return r}(t,o,e,n)}))}))).forEach((function(t,e){t.forEach((function(n,i){_t(n)&&(o[e]+=n*("-"===t[i-1]?-1:1))}))})),o}var At={placement:"bottom",positionFixed:!1,eventsEnabled:!0,removeOnDestroy:!1,onCreate:function(){},onUpdate:function(){},modifiers:{shift:{order:100,enabled:!0,fn:function(t){var e=t.placement,n=e.split("-")[0],i=e.split("-")[1];if(i){var o=t.offsets,r=o.reference,a=o.popper,s=-1!==["bottom","top"].indexOf(n),l=s?"left":"top",u=s?"width":"height",f={start:z({},l,r[l]),end:z({},l,r[l]+r[u]-a[u])};t.offsets.popper=X({},a,f[i])}return t}},offset:{order:200,enabled:!0,fn:function(t,e){var n=e.offset,i=t.placement,o=t.offsets,r=o.popper,a=o.reference,s=i.split("-")[0],l=void 0;return l=_t(+n)?[+n,0]:kt(n,r,a,s),"left"===s?(r.top+=l[0],r.left-=l[1]):"right"===s?(r.top+=l[0],r.left+=l[1]):"top"===s?(r.left+=l[0],r.top-=l[1]):"bottom"===s&&(r.left+=l[0],r.top+=l[1]),t.popper=r,t},offset:0},preventOverflow:{order:300,enabled:!0,fn:function(t,e){var n=e.boundariesElement||R(t.instance.popper);t.instance.reference===n&&(n=R(n));var i=ct("transform"),o=t.instance.popper.style,r=o.top,a=o.left,s=o[i];o.top="",o.left="",o[i]="";var l=et(t.instance.popper,t.instance.reference,e.padding,n,t.positionFixed);o.top=r,o.left=a,o[i]=s,e.boundaries=l;var u=e.priority,f=t.offsets.popper,d={primary:function(t){var n=f[t];return f[t]<l[t]&&!e.escapeWithReference&&(n=Math.max(f[t],l[t])),z({},t,n)},secondary:function(t){var n="right"===t?"left":"top",i=f[n];return f[t]>l[t]&&!e.escapeWithReference&&(i=Math.min(f[n],l[t]-("right"===t?f.width:f.height))),z({},n,i)}};return u.forEach((function(t){var e=-1!==["left","top"].indexOf(t)?"primary":"secondary";f=X({},f,d[e](t))})),t.offsets.popper=f,t},priority:["left","right","top","bottom"],padding:5,boundariesElement:"scrollParent"},keepTogether:{order:400,enabled:!0,fn:function(t){var e=t.offsets,n=e.popper,i=e.reference,o=t.placement.split("-")[0],r=Math.floor,a=-1!==["top","bottom"].indexOf(o),s=a?"right":"bottom",l=a?"left":"top",u=a?"width":"height";return n[s]<r(i[l])&&(t.offsets.popper[l]=r(i[l])-n[u]),n[l]>r(i[s])&&(t.offsets.popper[l]=r(i[s])),t}},arrow:{order:500,enabled:!0,fn:function(t,e){var n;if(!wt(t.instance.modifiers,"arrow","keepTogether"))return t;var i=e.element;if("string"==typeof i){if(!(i=t.instance.popper.querySelector(i)))return t}else if(!t.instance.popper.contains(i))return console.warn("WARNING: `arrow.element` must be child of its popper element!"),t;var o=t.placement.split("-")[0],r=t.offsets,a=r.popper,s=r.reference,l=-1!==["left","right"].indexOf(o),u=l?"height":"width",f=l?"Top":"Left",d=f.toLowerCase(),c=l?"left":"top",h=l?"bottom":"right",p=rt(i)[u];s[h]-p<a[d]&&(t.offsets.popper[d]-=a[d]-(s[h]-p)),s[d]+p>a[h]&&(t.offsets.popper[d]+=s[d]+p-a[h]),t.offsets.popper=K(t.offsets.popper);var m=s[d]+s[u]/2-p/2,g=I(t.instance.popper),v=parseFloat(g["margin"+f]),_=parseFloat(g["border"+f+"Width"]),b=m-t.offsets.popper[d]-v-_;return b=Math.max(Math.min(a[u]-p,b),0),t.arrowElement=i,t.offsets.arrow=(z(n={},d,Math.round(b)),z(n,c,""),n),t},element:"[x-arrow]"},flip:{order:600,enabled:!0,fn:function(t,e){if(dt(t.instance.modifiers,"inner"))return t;if(t.flipped&&t.placement===t.originalPlacement)return t;var n=et(t.instance.popper,t.instance.reference,e.padding,e.boundariesElement,t.positionFixed),i=t.placement.split("-")[0],o=at(i),r=t.placement.split("-")[1]||"",a=[];switch(e.behavior){case St:a=[i,o];break;case Dt:a=Ct(i);break;case Nt:a=Ct(i,!0);break;default:a=e.behavior}return a.forEach((function(s,l){if(i!==s||a.length===l+1)return t;i=t.placement.split("-")[0],o=at(i);var u=t.offsets.popper,f=t.offsets.reference,d=Math.floor,c="left"===i&&d(u.right)>d(f.left)||"right"===i&&d(u.left)<d(f.right)||"top"===i&&d(u.bottom)>d(f.top)||"bottom"===i&&d(u.top)<d(f.bottom),h=d(u.left)<d(n.left),p=d(u.right)>d(n.right),m=d(u.top)<d(n.top),g=d(u.bottom)>d(n.bottom),v="left"===i&&h||"right"===i&&p||"top"===i&&m||"bottom"===i&&g,_=-1!==["top","bottom"].indexOf(i),b=!!e.flipVariations&&(_&&"start"===r&&h||_&&"end"===r&&p||!_&&"start"===r&&m||!_&&"end"===r&&g),y=!!e.flipVariationsByContent&&(_&&"start"===r&&p||_&&"end"===r&&h||!_&&"start"===r&&g||!_&&"end"===r&&m),w=b||y;(c||v||w)&&(t.flipped=!0,(c||v)&&(i=a[l+1]),w&&(r=function(t){return"end"===t?"start":"start"===t?"end":t}(r)),t.placement=i+(r?"-"+r:""),t.offsets.popper=X({},t.offsets.popper,st(t.instance.popper,t.offsets.reference,t.placement)),t=ut(t.instance.modifiers,t,"flip"))})),t},behavior:"flip",padding:5,boundariesElement:"viewport",flipVariations:!1,flipVariationsByContent:!1},inner:{order:700,enabled:!1,fn:function(t){var e=t.placement,n=e.split("-")[0],i=t.offsets,o=i.popper,r=i.reference,a=-1!==["left","right"].indexOf(n),s=-1===["top","left"].indexOf(n);return o[a?"left":"top"]=r[n]-(s?o[a?"width":"height"]:0),t.placement=at(e),t.offsets.popper=K(o),t}},hide:{order:800,enabled:!0,fn:function(t){if(!wt(t.instance.modifiers,"hide","preventOverflow"))return t;var e=t.offsets.reference,n=lt(t.instance.modifiers,(function(t){return"preventOverflow"===t.name})).boundaries;if(e.bottom<n.top||e.left>n.right||e.top>n.bottom||e.right<n.left){if(!0===t.hide)return t;t.hide=!0,t.attributes["x-out-of-boundaries"]=""}else{if(!1===t.hide)return t;t.hide=!1,t.attributes["x-out-of-boundaries"]=!1}return t}},computeStyle:{order:850,enabled:!0,fn:function(t,e){var n=e.x,i=e.y,o=t.offsets.popper,r=lt(t.instance.modifiers,(function(t){return"applyStyle"===t.name})).gpuAcceleration;void 0!==r&&console.warn("WARNING: `gpuAcceleration` option moved to `computeStyle` modifier and will not be supported in future versions of Popper.js!");var a=void 0!==r?r:e.gpuAcceleration,s=R(t.instance.popper),l=G(s),u={position:o.position},f=function(t,e){var n=t.offsets,i=n.popper,o=n.reference,r=Math.round,a=Math.floor,s=function(t){return t},l=r(o.width),u=r(i.width),f=-1!==["left","right"].indexOf(t.placement),d=-1!==t.placement.indexOf("-"),c=e?f||d||l%2==u%2?r:a:s,h=e?r:s;return{left:c(l%2==1&&u%2==1&&!d&&e?i.left-1:i.left),top:h(i.top),bottom:h(i.bottom),right:c(i.right)}}(t,window.devicePixelRatio<2||!yt),d="bottom"===n?"top":"bottom",c="right"===i?"left":"right",h=ct("transform"),p=void 0,m=void 0;if(m="bottom"===d?"HTML"===s.nodeName?-s.clientHeight+f.bottom:-l.height+f.bottom:f.top,p="right"===c?"HTML"===s.nodeName?-s.clientWidth+f.right:-l.width+f.right:f.left,a&&h)u[h]="translate3d("+p+"px, "+m+"px, 0)",u[d]=0,u[c]=0,u.willChange="transform";else{var g="bottom"===d?-1:1,v="right"===c?-1:1;u[d]=m*g,u[c]=p*v,u.willChange=d+", "+c}var _={"x-placement":t.placement};return t.attributes=X({},_,t.attributes),t.styles=X({},u,t.styles),t.arrowStyles=X({},t.offsets.arrow,t.arrowStyles),t},gpuAcceleration:!0,x:"bottom",y:"right"},applyStyle:{order:900,enabled:!0,fn:function(t){var e,n;return bt(t.instance.popper,t.styles),e=t.instance.popper,n=t.attributes,Object.keys(n).forEach((function(t){!1!==n[t]?e.setAttribute(t,n[t]):e.removeAttribute(t)})),t.arrowElement&&Object.keys(t.arrowStyles).length&&bt(t.arrowElement,t.arrowStyles),t},onLoad:function(t,e,n,i,o){var r=ot(o,e,t,n.positionFixed),a=it(n.placement,r,e,t,n.modifiers.flip.boundariesElement,n.modifiers.flip.padding);return e.setAttribute("x-placement",a),bt(e,{position:n.positionFixed?"fixed":"absolute"}),n},gpuAcceleration:void 0}}},It=function(){function t(e,n){var i=this,o=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{};V(this,t),this.scheduleUpdate=function(){return requestAnimationFrame(i.update)},this.update=k(this.update.bind(this)),this.options=X({},t.Defaults,o),this.state={isDestroyed:!1,isCreated:!1,scrollParents:[]},this.reference=e&&e.jquery?e[0]:e,this.popper=n&&n.jquery?n[0]:n,this.options.modifiers={},Object.keys(X({},t.Defaults.modifiers,o.modifiers)).forEach((function(e){i.options.modifiers[e]=X({},t.Defaults.modifiers[e]||{},o.modifiers?o.modifiers[e]:{})})),this.modifiers=Object.keys(this.options.modifiers).map((function(t){return X({name:t},i.options.modifiers[t])})).sort((function(t,e){return t.order-e.order})),this.modifiers.forEach((function(t){t.enabled&&A(t.onLoad)&&t.onLoad(i.reference,i.popper,i.options,t,i.state)})),this.update();var r=this.options.eventsEnabled;r&&this.enableEventListeners(),this.state.eventsEnabled=r}return Y(t,[{key:"update",value:function(){return ft.call(this)}},{key:"destroy",value:function(){return ht.call(this)}},{key:"enableEventListeners",value:function(){return gt.call(this)}},{key:"disableEventListeners",value:function(){return vt.call(this)}}]),t}();It.Utils=("undefined"!=typeof window?window:global).PopperUtils,It.placements=Et,It.Defaults=At;var Ot="dropdown",xt=i.default.fn[Ot],jt=new RegExp("38|40|27"),Lt={offset:0,flip:!0,boundary:"scrollParent",reference:"toggle",display:"dynamic",popperConfig:null},Pt={offset:"(number|string|function)",flip:"boolean",boundary:"(string|element)",reference:"(string|element)",display:"string",popperConfig:"(null|object)"},Ft=function(){function t(t,e){this._element=t,this._popper=null,this._config=this._getConfig(e),this._menu=this._getMenuElement(),this._inNavbar=this._detectNavbar(),this._addEventListeners()}var e=t.prototype;return e.toggle=function(){if(!this._element.disabled&&!i.default(this._element).hasClass("disabled")){var e=i.default(this._menu).hasClass("show");t._clearMenus(),e||this.show(!0)}},e.show=function(e){if(void 0===e&&(e=!1),!(this._element.disabled||i.default(this._element).hasClass("disabled")||i.default(this._menu).hasClass("show"))){var n={relatedTarget:this._element},o=i.default.Event("show.bs.dropdown",n),r=t._getParentFromElement(this._element);if(i.default(r).trigger(o),!o.isDefaultPrevented()){if(!this._inNavbar&&e){if("undefined"==typeof It)throw new TypeError("Bootstrap's dropdowns require Popper.js (https://popper.js.org/)");var a=this._element;"parent"===this._config.reference?a=r:l.isElement(this._config.reference)&&(a=this._config.reference,"undefined"!=typeof this._config.reference.jquery&&(a=this._config.reference[0])),"scrollParent"!==this._config.boundary&&i.default(r).addClass("position-static"),this._popper=new It(a,this._menu,this._getPopperConfig())}"ontouchstart"in document.documentElement&&0===i.default(r).closest(".navbar-nav").length&&i.default(document.body).children().on("mouseover",null,i.default.noop),this._element.focus(),this._element.setAttribute("aria-expanded",!0),i.default(this._menu).toggleClass("show"),i.default(r).toggleClass("show").trigger(i.default.Event("shown.bs.dropdown",n))}}},e.hide=function(){if(!this._element.disabled&&!i.default(this._element).hasClass("disabled")&&i.default(this._menu).hasClass("show")){var e={relatedTarget:this._element},n=i.default.Event("hide.bs.dropdown",e),o=t._getParentFromElement(this._element);i.default(o).trigger(n),n.isDefaultPrevented()||(this._popper&&this._popper.destroy(),i.default(this._menu).toggleClass("show"),i.default(o).toggleClass("show").trigger(i.default.Event("hidden.bs.dropdown",e)))}},e.dispose=function(){i.default.removeData(this._element,"bs.dropdown"),i.default(this._element).off(".bs.dropdown"),this._element=null,this._menu=null,null!==this._popper&&(this._popper.destroy(),this._popper=null)},e.update=function(){this._inNavbar=this._detectNavbar(),null!==this._popper&&this._popper.scheduleUpdate()},e._addEventListeners=function(){var t=this;i.default(this._element).on("click.bs.dropdown",(function(e){e.preventDefault(),e.stopPropagation(),t.toggle()}))},e._getConfig=function(t){return t=a({},this.constructor.Default,i.default(this._element).data(),t),l.typeCheckConfig(Ot,t,this.constructor.DefaultType),t},e._getMenuElement=function(){if(!this._menu){var e=t._getParentFromElement(this._element);e&&(this._menu=e.querySelector(".dropdown-menu"))}return this._menu},e._getPlacement=function(){var t=i.default(this._element.parentNode),e="bottom-start";return t.hasClass("dropup")?e=i.default(this._menu).hasClass("dropdown-menu-right")?"top-end":"top-start":t.hasClass("dropright")?e="right-start":t.hasClass("dropleft")?e="left-start":i.default(this._menu).hasClass("dropdown-menu-right")&&(e="bottom-end"),e},e._detectNavbar=function(){return i.default(this._element).closest(".navbar").length>0},e._getOffset=function(){var t=this,e={};return"function"==typeof this._config.offset?e.fn=function(e){return e.offsets=a({},e.offsets,t._config.offset(e.offsets,t._element)||{}),e}:e.offset=this._config.offset,e},e._getPopperConfig=function(){var t={placement:this._getPlacement(),modifiers:{offset:this._getOffset(),flip:{enabled:this._config.flip},preventOverflow:{boundariesElement:this._config.boundary}}};return"static"===this._config.display&&(t.modifiers.applyStyle={enabled:!1}),a({},t,this._config.popperConfig)},t._jQueryInterface=function(e){return this.each((function(){var n=i.default(this).data("bs.dropdown");if(n||(n=new t(this,"object"==typeof e?e:null),i.default(this).data("bs.dropdown",n)),"string"==typeof e){if("undefined"==typeof n[e])throw new TypeError('No method named "'+e+'"');n[e]()}}))},t._clearMenus=function(e){if(!e||3!==e.which&&("keyup"!==e.type||9===e.which))for(var n=[].slice.call(document.querySelectorAll('[data-toggle="dropdown"]')),o=0,r=n.length;o<r;o++){var a=t._getParentFromElement(n[o]),s=i.default(n[o]).data("bs.dropdown"),l={relatedTarget:n[o]};if(e&&"click"===e.type&&(l.clickEvent=e),s){var u=s._menu;if(i.default(a).hasClass("show")&&!(e&&("click"===e.type&&/input|textarea/i.test(e.target.tagName)||"keyup"===e.type&&9===e.which)&&i.default.contains(a,e.target))){var f=i.default.Event("hide.bs.dropdown",l);i.default(a).trigger(f),f.isDefaultPrevented()||("ontouchstart"in document.documentElement&&i.default(document.body).children().off("mouseover",null,i.default.noop),n[o].setAttribute("aria-expanded","false"),s._popper&&s._popper.destroy(),i.default(u).removeClass("show"),i.default(a).removeClass("show").trigger(i.default.Event("hidden.bs.dropdown",l)))}}}},t._getParentFromElement=function(t){var e,n=l.getSelectorFromElement(t);return n&&(e=document.querySelector(n)),e||t.parentNode},t._dataApiKeydownHandler=function(e){if(!(/input|textarea/i.test(e.target.tagName)?32===e.which||27!==e.which&&(40!==e.which&&38!==e.which||i.default(e.target).closest(".dropdown-menu").length):!jt.test(e.which))&&!this.disabled&&!i.default(this).hasClass("disabled")){var n=t._getParentFromElement(this),o=i.default(n).hasClass("show");if(o||27!==e.which){if(e.preventDefault(),e.stopPropagation(),!o||27===e.which||32===e.which)return 27===e.which&&i.default(n.querySelector('[data-toggle="dropdown"]')).trigger("focus"),void i.default(this).trigger("click");var r=[].slice.call(n.querySelectorAll(".dropdown-menu .dropdown-item:not(.disabled):not(:disabled)")).filter((function(t){return i.default(t).is(":visible")}));if(0!==r.length){var a=r.indexOf(e.target);38===e.which&&a>0&&a--,40===e.which&&a<r.length-1&&a++,a<0&&(a=0),r[a].focus()}}}},r(t,null,[{key:"VERSION",get:function(){return"4.5.3"}},{key:"Default",get:function(){return Lt}},{key:"DefaultType",get:function(){return Pt}}]),t}();i.default(document).on("keydown.bs.dropdown.data-api",'[data-toggle="dropdown"]',Ft._dataApiKeydownHandler).on("keydown.bs.dropdown.data-api",".dropdown-menu",Ft._dataApiKeydownHandler).on("click.bs.dropdown.data-api keyup.bs.dropdown.data-api",Ft._clearMenus).on("click.bs.dropdown.data-api",'[data-toggle="dropdown"]',(function(t){t.preventDefault(),t.stopPropagation(),Ft._jQueryInterface.call(i.default(this),"toggle")})).on("click.bs.dropdown.data-api",".dropdown form",(function(t){t.stopPropagation()})),i.default.fn[Ot]=Ft._jQueryInterface,i.default.fn[Ot].Constructor=Ft,i.default.fn[Ot].noConflict=function(){return i.default.fn[Ot]=xt,Ft._jQueryInterface};var Rt=i.default.fn.modal,Ht={backdrop:!0,keyboard:!0,focus:!0,show:!0},Mt={backdrop:"(boolean|string)",keyboard:"boolean",focus:"boolean",show:"boolean"},Bt=function(){function t(t,e){this._config=this._getConfig(e),this._element=t,this._dialog=t.querySelector(".modal-dialog"),this._backdrop=null,this._isShown=!1,this._isBodyOverflowing=!1,this._ignoreBackdropClick=!1,this._isTransitioning=!1,this._scrollbarWidth=0}var e=t.prototype;return e.toggle=function(t){return this._isShown?this.hide():this.show(t)},e.show=function(t){var e=this;if(!this._isShown&&!this._isTransitioning){i.default(this._element).hasClass("fade")&&(this._isTransitioning=!0);var n=i.default.Event("show.bs.modal",{relatedTarget:t});i.default(this._element).trigger(n),this._isShown||n.isDefaultPrevented()||(this._isShown=!0,this._checkScrollbar(),this._setScrollbar(),this._adjustDialog(),this._setEscapeEvent(),this._setResizeEvent(),i.default(this._element).on("click.dismiss.bs.modal",'[data-dismiss="modal"]',(function(t){return e.hide(t)})),i.default(this._dialog).on("mousedown.dismiss.bs.modal",(function(){i.default(e._element).one("mouseup.dismiss.bs.modal",(function(t){i.default(t.target).is(e._element)&&(e._ignoreBackdropClick=!0)}))})),this._showBackdrop((function(){return e._showElement(t)})))}},e.hide=function(t){var e=this;if(t&&t.preventDefault(),this._isShown&&!this._isTransitioning){var n=i.default.Event("hide.bs.modal");if(i.default(this._element).trigger(n),this._isShown&&!n.isDefaultPrevented()){this._isShown=!1;var o=i.default(this._element).hasClass("fade");if(o&&(this._isTransitioning=!0),this._setEscapeEvent(),this._setResizeEvent(),i.default(document).off("focusin.bs.modal"),i.default(this._element).removeClass("show"),i.default(this._element).off("click.dismiss.bs.modal"),i.default(this._dialog).off("mousedown.dismiss.bs.modal"),o){var r=l.getTransitionDurationFromElement(this._element);i.default(this._element).one(l.TRANSITION_END,(function(t){return e._hideModal(t)})).emulateTransitionEnd(r)}else this._hideModal()}}},e.dispose=function(){[window,this._element,this._dialog].forEach((function(t){return i.default(t).off(".bs.modal")})),i.default(document).off("focusin.bs.modal"),i.default.removeData(this._element,"bs.modal"),this._config=null,this._element=null,this._dialog=null,this._backdrop=null,this._isShown=null,this._isBodyOverflowing=null,this._ignoreBackdropClick=null,this._isTransitioning=null,this._scrollbarWidth=null},e.handleUpdate=function(){this._adjustDialog()},e._getConfig=function(t){return t=a({},Ht,t),l.typeCheckConfig("modal",t,Mt),t},e._triggerBackdropTransition=function(){var t=this;if("static"===this._config.backdrop){var e=i.default.Event("hidePrevented.bs.modal");if(i.default(this._element).trigger(e),e.isDefaultPrevented())return;var n=this._element.scrollHeight>document.documentElement.clientHeight;n||(this._element.style.overflowY="hidden"),this._element.classList.add("modal-static");var o=l.getTransitionDurationFromElement(this._dialog);i.default(this._element).off(l.TRANSITION_END),i.default(this._element).one(l.TRANSITION_END,(function(){t._element.classList.remove("modal-static"),n||i.default(t._element).one(l.TRANSITION_END,(function(){t._element.style.overflowY=""})).emulateTransitionEnd(t._element,o)})).emulateTransitionEnd(o),this._element.focus()}else this.hide()},e._showElement=function(t){var e=this,n=i.default(this._element).hasClass("fade"),o=this._dialog?this._dialog.querySelector(".modal-body"):null;this._element.parentNode&&this._element.parentNode.nodeType===Node.ELEMENT_NODE||document.body.appendChild(this._element),this._element.style.display="block",this._element.removeAttribute("aria-hidden"),this._element.setAttribute("aria-modal",!0),this._element.setAttribute("role","dialog"),i.default(this._dialog).hasClass("modal-dialog-scrollable")&&o?o.scrollTop=0:this._element.scrollTop=0,n&&l.reflow(this._element),i.default(this._element).addClass("show"),this._config.focus&&this._enforceFocus();var r=i.default.Event("shown.bs.modal",{relatedTarget:t}),a=function(){e._config.focus&&e._element.focus(),e._isTransitioning=!1,i.default(e._element).trigger(r)};if(n){var s=l.getTransitionDurationFromElement(this._dialog);i.default(this._dialog).one(l.TRANSITION_END,a).emulateTransitionEnd(s)}else a()},e._enforceFocus=function(){var t=this;i.default(document).off("focusin.bs.modal").on("focusin.bs.modal",(function(e){document!==e.target&&t._element!==e.target&&0===i.default(t._element).has(e.target).length&&t._element.focus()}))},e._setEscapeEvent=function(){var t=this;this._isShown?i.default(this._element).on("keydown.dismiss.bs.modal",(function(e){t._config.keyboard&&27===e.which?(e.preventDefault(),t.hide()):t._config.keyboard||27!==e.which||t._triggerBackdropTransition()})):this._isShown||i.default(this._element).off("keydown.dismiss.bs.modal")},e._setResizeEvent=function(){var t=this;this._isShown?i.default(window).on("resize.bs.modal",(function(e){return t.handleUpdate(e)})):i.default(window).off("resize.bs.modal")},e._hideModal=function(){var t=this;this._element.style.display="none",this._element.setAttribute("aria-hidden",!0),this._element.removeAttribute("aria-modal"),this._element.removeAttribute("role"),this._isTransitioning=!1,this._showBackdrop((function(){i.default(document.body).removeClass("modal-open"),t._resetAdjustments(),t._resetScrollbar(),i.default(t._element).trigger("hidden.bs.modal")}))},e._removeBackdrop=function(){this._backdrop&&(i.default(this._backdrop).remove(),this._backdrop=null)},e._showBackdrop=function(t){var e=this,n=i.default(this._element).hasClass("fade")?"fade":"";if(this._isShown&&this._config.backdrop){if(this._backdrop=document.createElement("div"),this._backdrop.className="modal-backdrop",n&&this._backdrop.classList.add(n),i.default(this._backdrop).appendTo(document.body),i.default(this._element).on("click.dismiss.bs.modal",(function(t){e._ignoreBackdropClick?e._ignoreBackdropClick=!1:t.target===t.currentTarget&&e._triggerBackdropTransition()})),n&&l.reflow(this._backdrop),i.default(this._backdrop).addClass("show"),!t)return;if(!n)return void t();var o=l.getTransitionDurationFromElement(this._backdrop);i.default(this._backdrop).one(l.TRANSITION_END,t).emulateTransitionEnd(o)}else if(!this._isShown&&this._backdrop){i.default(this._backdrop).removeClass("show");var r=function(){e._removeBackdrop(),t&&t()};if(i.default(this._element).hasClass("fade")){var a=l.getTransitionDurationFromElement(this._backdrop);i.default(this._backdrop).one(l.TRANSITION_END,r).emulateTransitionEnd(a)}else r()}else t&&t()},e._adjustDialog=function(){var t=this._element.scrollHeight>document.documentElement.clientHeight;!this._isBodyOverflowing&&t&&(this._element.style.paddingLeft=this._scrollbarWidth+"px"),this._isBodyOverflowing&&!t&&(this._element.style.paddingRight=this._scrollbarWidth+"px")},e._resetAdjustments=function(){this._element.style.paddingLeft="",this._element.style.paddingRight=""},e._checkScrollbar=function(){var t=document.body.getBoundingClientRect();this._isBodyOverflowing=Math.round(t.left+t.right)<window.innerWidth,this._scrollbarWidth=this._getScrollbarWidth()},e._setScrollbar=function(){var t=this;if(this._isBodyOverflowing){var e=[].slice.call(document.querySelectorAll(".fixed-top, .fixed-bottom, .is-fixed, .sticky-top")),n=[].slice.call(document.querySelectorAll(".sticky-top"));i.default(e).each((function(e,n){var o=n.style.paddingRight,r=i.default(n).css("padding-right");i.default(n).data("padding-right",o).css("padding-right",parseFloat(r)+t._scrollbarWidth+"px")})),i.default(n).each((function(e,n){var o=n.style.marginRight,r=i.default(n).css("margin-right");i.default(n).data("margin-right",o).css("margin-right",parseFloat(r)-t._scrollbarWidth+"px")}));var o=document.body.style.paddingRight,r=i.default(document.body).css("padding-right");i.default(document.body).data("padding-right",o).css("padding-right",parseFloat(r)+this._scrollbarWidth+"px")}i.default(document.body).addClass("modal-open")},e._resetScrollbar=function(){var t=[].slice.call(document.querySelectorAll(".fixed-top, .fixed-bottom, .is-fixed, .sticky-top"));i.default(t).each((function(t,e){var n=i.default(e).data("padding-right");i.default(e).removeData("padding-right"),e.style.paddingRight=n||""}));var e=[].slice.call(document.querySelectorAll(".sticky-top"));i.default(e).each((function(t,e){var n=i.default(e).data("margin-right");"undefined"!=typeof n&&i.default(e).css("margin-right",n).removeData("margin-right")}));var n=i.default(document.body).data("padding-right");i.default(document.body).removeData("padding-right"),document.body.style.paddingRight=n||""},e._getScrollbarWidth=function(){var t=document.createElement("div");t.className="modal-scrollbar-measure",document.body.appendChild(t);var e=t.getBoundingClientRect().width-t.clientWidth;return document.body.removeChild(t),e},t._jQueryInterface=function(e,n){return this.each((function(){var o=i.default(this).data("bs.modal"),r=a({},Ht,i.default(this).data(),"object"==typeof e&&e?e:{});if(o||(o=new t(this,r),i.default(this).data("bs.modal",o)),"string"==typeof e){if("undefined"==typeof o[e])throw new TypeError('No method named "'+e+'"');o[e](n)}else r.show&&o.show(n)}))},r(t,null,[{key:"VERSION",get:function(){return"4.5.3"}},{key:"Default",get:function(){return Ht}}]),t}();i.default(document).on("click.bs.modal.data-api",'[data-toggle="modal"]',(function(t){var e,n=this,o=l.getSelectorFromElement(this);o&&(e=document.querySelector(o));var r=i.default(e).data("bs.modal")?"toggle":a({},i.default(e).data(),i.default(this).data());"A"!==this.tagName&&"AREA"!==this.tagName||t.preventDefault();var s=i.default(e).one("show.bs.modal",(function(t){t.isDefaultPrevented()||s.one("hidden.bs.modal",(function(){i.default(n).is(":visible")&&n.focus()}))}));Bt._jQueryInterface.call(i.default(e),r,this)})),i.default.fn.modal=Bt._jQueryInterface,i.default.fn.modal.Constructor=Bt,i.default.fn.modal.noConflict=function(){return i.default.fn.modal=Rt,Bt._jQueryInterface};var qt=["background","cite","href","itemtype","longdesc","poster","src","xlink:href"],Qt={"*":["class","dir","id","lang","role",/^aria-[\w-]*$/i],a:["target","href","title","rel"],area:[],b:[],br:[],col:[],code:[],div:[],em:[],hr:[],h1:[],h2:[],h3:[],h4:[],h5:[],h6:[],i:[],img:["src","srcset","alt","title","width","height"],li:[],ol:[],p:[],pre:[],s:[],small:[],span:[],sub:[],sup:[],strong:[],u:[],ul:[]},Wt=/^(?:(?:https?|mailto|ftp|tel|file):|[^#&/:?]*(?:[#/?]|$))/gi,Ut=/^data:(?:image\/(?:bmp|gif|jpeg|jpg|png|tiff|webp)|video\/(?:mpeg|mp4|ogg|webm)|audio\/(?:mp3|oga|ogg|opus));base64,[\d+/a-z]+=*$/i;function Vt(t,e,n){if(0===t.length)return t;if(n&&"function"==typeof n)return n(t);for(var i=(new window.DOMParser).parseFromString(t,"text/html"),o=Object.keys(e),r=[].slice.call(i.body.querySelectorAll("*")),a=function(t,n){var i=r[t],a=i.nodeName.toLowerCase();if(-1===o.indexOf(i.nodeName.toLowerCase()))return i.parentNode.removeChild(i),"continue";var s=[].slice.call(i.attributes),l=[].concat(e["*"]||[],e[a]||[]);s.forEach((function(t){(function(t,e){var n=t.nodeName.toLowerCase();if(-1!==e.indexOf(n))return-1===qt.indexOf(n)||Boolean(t.nodeValue.match(Wt)||t.nodeValue.match(Ut));for(var i=e.filter((function(t){return t instanceof RegExp})),o=0,r=i.length;o<r;o++)if(n.match(i[o]))return!0;return!1})(t,l)||i.removeAttribute(t.nodeName)}))},s=0,l=r.length;s<l;s++)a(s);return i.body.innerHTML}var Yt="tooltip",zt=i.default.fn[Yt],Xt=new RegExp("(^|\\s)bs-tooltip\\S+","g"),Kt=["sanitize","whiteList","sanitizeFn"],Gt={animation:"boolean",template:"string",title:"(string|element|function)",trigger:"string",delay:"(number|object)",html:"boolean",selector:"(string|boolean)",placement:"(string|function)",offset:"(number|string|function)",container:"(string|element|boolean)",fallbackPlacement:"(string|array)",boundary:"(string|element)",sanitize:"boolean",sanitizeFn:"(null|function)",whiteList:"object",popperConfig:"(null|object)"},$t={AUTO:"auto",TOP:"top",RIGHT:"right",BOTTOM:"bottom",LEFT:"left"},Jt={animation:!0,template:'<div class="tooltip" role="tooltip"><div class="arrow"></div><div class="tooltip-inner"></div></div>',trigger:"hover focus",title:"",delay:0,html:!1,selector:!1,placement:"top",offset:0,container:!1,fallbackPlacement:"flip",boundary:"scrollParent",sanitize:!0,sanitizeFn:null,whiteList:Qt,popperConfig:null},Zt={HIDE:"hide.bs.tooltip",HIDDEN:"hidden.bs.tooltip",SHOW:"show.bs.tooltip",SHOWN:"shown.bs.tooltip",INSERTED:"inserted.bs.tooltip",CLICK:"click.bs.tooltip",FOCUSIN:"focusin.bs.tooltip",FOCUSOUT:"focusout.bs.tooltip",MOUSEENTER:"mouseenter.bs.tooltip",MOUSELEAVE:"mouseleave.bs.tooltip"},te=function(){function t(t,e){if("undefined"==typeof It)throw new TypeError("Bootstrap's tooltips require Popper.js (https://popper.js.org/)");this._isEnabled=!0,this._timeout=0,this._hoverState="",this._activeTrigger={},this._popper=null,this.element=t,this.config=this._getConfig(e),this.tip=null,this._setListeners()}var e=t.prototype;return e.enable=function(){this._isEnabled=!0},e.disable=function(){this._isEnabled=!1},e.toggleEnabled=function(){this._isEnabled=!this._isEnabled},e.toggle=function(t){if(this._isEnabled)if(t){var e=this.constructor.DATA_KEY,n=i.default(t.currentTarget).data(e);n||(n=new this.constructor(t.currentTarget,this._getDelegateConfig()),i.default(t.currentTarget).data(e,n)),n._activeTrigger.click=!n._activeTrigger.click,n._isWithActiveTrigger()?n._enter(null,n):n._leave(null,n)}else{if(i.default(this.getTipElement()).hasClass("show"))return void this._leave(null,this);this._enter(null,this)}},e.dispose=function(){clearTimeout(this._timeout),i.default.removeData(this.element,this.constructor.DATA_KEY),i.default(this.element).off(this.constructor.EVENT_KEY),i.default(this.element).closest(".modal").off("hide.bs.modal",this._hideModalHandler),this.tip&&i.default(this.tip).remove(),this._isEnabled=null,this._timeout=null,this._hoverState=null,this._activeTrigger=null,this._popper&&this._popper.destroy(),this._popper=null,this.element=null,this.config=null,this.tip=null},e.show=function(){var t=this;if("none"===i.default(this.element).css("display"))throw new Error("Please use show on visible elements");var e=i.default.Event(this.constructor.Event.SHOW);if(this.isWithContent()&&this._isEnabled){i.default(this.element).trigger(e);var n=l.findShadowRoot(this.element),o=i.default.contains(null!==n?n:this.element.ownerDocument.documentElement,this.element);if(e.isDefaultPrevented()||!o)return;var r=this.getTipElement(),a=l.getUID(this.constructor.NAME);r.setAttribute("id",a),this.element.setAttribute("aria-describedby",a),this.setContent(),this.config.animation&&i.default(r).addClass("fade");var s="function"==typeof this.config.placement?this.config.placement.call(this,r,this.element):this.config.placement,u=this._getAttachment(s);this.addAttachmentClass(u);var f=this._getContainer();i.default(r).data(this.constructor.DATA_KEY,this),i.default.contains(this.element.ownerDocument.documentElement,this.tip)||i.default(r).appendTo(f),i.default(this.element).trigger(this.constructor.Event.INSERTED),this._popper=new It(this.element,r,this._getPopperConfig(u)),i.default(r).addClass("show"),"ontouchstart"in document.documentElement&&i.default(document.body).children().on("mouseover",null,i.default.noop);var d=function(){t.config.animation&&t._fixTransition();var e=t._hoverState;t._hoverState=null,i.default(t.element).trigger(t.constructor.Event.SHOWN),"out"===e&&t._leave(null,t)};if(i.default(this.tip).hasClass("fade")){var c=l.getTransitionDurationFromElement(this.tip);i.default(this.tip).one(l.TRANSITION_END,d).emulateTransitionEnd(c)}else d()}},e.hide=function(t){var e=this,n=this.getTipElement(),o=i.default.Event(this.constructor.Event.HIDE),r=function(){"show"!==e._hoverState&&n.parentNode&&n.parentNode.removeChild(n),e._cleanTipClass(),e.element.removeAttribute("aria-describedby"),i.default(e.element).trigger(e.constructor.Event.HIDDEN),null!==e._popper&&e._popper.destroy(),t&&t()};if(i.default(this.element).trigger(o),!o.isDefaultPrevented()){if(i.default(n).removeClass("show"),"ontouchstart"in document.documentElement&&i.default(document.body).children().off("mouseover",null,i.default.noop),this._activeTrigger.click=!1,this._activeTrigger.focus=!1,this._activeTrigger.hover=!1,i.default(this.tip).hasClass("fade")){var a=l.getTransitionDurationFromElement(n);i.default(n).one(l.TRANSITION_END,r).emulateTransitionEnd(a)}else r();this._hoverState=""}},e.update=function(){null!==this._popper&&this._popper.scheduleUpdate()},e.isWithContent=function(){return Boolean(this.getTitle())},e.addAttachmentClass=function(t){i.default(this.getTipElement()).addClass("bs-tooltip-"+t)},e.getTipElement=function(){return this.tip=this.tip||i.default(this.config.template)[0],this.tip},e.setContent=function(){var t=this.getTipElement();this.setElementContent(i.default(t.querySelectorAll(".tooltip-inner")),this.getTitle()),i.default(t).removeClass("fade show")},e.setElementContent=function(t,e){"object"!=typeof e||!e.nodeType&&!e.jquery?this.config.html?(this.config.sanitize&&(e=Vt(e,this.config.whiteList,this.config.sanitizeFn)),t.html(e)):t.text(e):this.config.html?i.default(e).parent().is(t)||t.empty().append(e):t.text(i.default(e).text())},e.getTitle=function(){var t=this.element.getAttribute("data-original-title");return t||(t="function"==typeof this.config.title?this.config.title.call(this.element):this.config.title),t},e._getPopperConfig=function(t){var e=this;return a({},{placement:t,modifiers:{offset:this._getOffset(),flip:{behavior:this.config.fallbackPlacement},arrow:{element:".arrow"},preventOverflow:{boundariesElement:this.config.boundary}},onCreate:function(t){t.originalPlacement!==t.placement&&e._handlePopperPlacementChange(t)},onUpdate:function(t){return e._handlePopperPlacementChange(t)}},this.config.popperConfig)},e._getOffset=function(){var t=this,e={};return"function"==typeof this.config.offset?e.fn=function(e){return e.offsets=a({},e.offsets,t.config.offset(e.offsets,t.element)||{}),e}:e.offset=this.config.offset,e},e._getContainer=function(){return!1===this.config.container?document.body:l.isElement(this.config.container)?i.default(this.config.container):i.default(document).find(this.config.container)},e._getAttachment=function(t){return $t[t.toUpperCase()]},e._setListeners=function(){var t=this;this.config.trigger.split(" ").forEach((function(e){if("click"===e)i.default(t.element).on(t.constructor.Event.CLICK,t.config.selector,(function(e){return t.toggle(e)}));else if("manual"!==e){var n="hover"===e?t.constructor.Event.MOUSEENTER:t.constructor.Event.FOCUSIN,o="hover"===e?t.constructor.Event.MOUSELEAVE:t.constructor.Event.FOCUSOUT;i.default(t.element).on(n,t.config.selector,(function(e){return t._enter(e)})).on(o,t.config.selector,(function(e){return t._leave(e)}))}})),this._hideModalHandler=function(){t.element&&t.hide()},i.default(this.element).closest(".modal").on("hide.bs.modal",this._hideModalHandler),this.config.selector?this.config=a({},this.config,{trigger:"manual",selector:""}):this._fixTitle()},e._fixTitle=function(){var t=typeof this.element.getAttribute("data-original-title");(this.element.getAttribute("title")||"string"!==t)&&(this.element.setAttribute("data-original-title",this.element.getAttribute("title")||""),this.element.setAttribute("title",""))},e._enter=function(t,e){var n=this.constructor.DATA_KEY;(e=e||i.default(t.currentTarget).data(n))||(e=new this.constructor(t.currentTarget,this._getDelegateConfig()),i.default(t.currentTarget).data(n,e)),t&&(e._activeTrigger["focusin"===t.type?"focus":"hover"]=!0),i.default(e.getTipElement()).hasClass("show")||"show"===e._hoverState?e._hoverState="show":(clearTimeout(e._timeout),e._hoverState="show",e.config.delay&&e.config.delay.show?e._timeout=setTimeout((function(){"show"===e._hoverState&&e.show()}),e.config.delay.show):e.show())},e._leave=function(t,e){var n=this.constructor.DATA_KEY;(e=e||i.default(t.currentTarget).data(n))||(e=new this.constructor(t.currentTarget,this._getDelegateConfig()),i.default(t.currentTarget).data(n,e)),t&&(e._activeTrigger["focusout"===t.type?"focus":"hover"]=!1),e._isWithActiveTrigger()||(clearTimeout(e._timeout),e._hoverState="out",e.config.delay&&e.config.delay.hide?e._timeout=setTimeout((function(){"out"===e._hoverState&&e.hide()}),e.config.delay.hide):e.hide())},e._isWithActiveTrigger=function(){for(var t in this._activeTrigger)if(this._activeTrigger[t])return!0;return!1},e._getConfig=function(t){var e=i.default(this.element).data();return Object.keys(e).forEach((function(t){-1!==Kt.indexOf(t)&&delete e[t]})),"number"==typeof(t=a({},this.constructor.Default,e,"object"==typeof t&&t?t:{})).delay&&(t.delay={show:t.delay,hide:t.delay}),"number"==typeof t.title&&(t.title=t.title.toString()),"number"==typeof t.content&&(t.content=t.content.toString()),l.typeCheckConfig(Yt,t,this.constructor.DefaultType),t.sanitize&&(t.template=Vt(t.template,t.whiteList,t.sanitizeFn)),t},e._getDelegateConfig=function(){var t={};if(this.config)for(var e in this.config)this.constructor.Default[e]!==this.config[e]&&(t[e]=this.config[e]);return t},e._cleanTipClass=function(){var t=i.default(this.getTipElement()),e=t.attr("class").match(Xt);null!==e&&e.length&&t.removeClass(e.join(""))},e._handlePopperPlacementChange=function(t){this.tip=t.instance.popper,this._cleanTipClass(),this.addAttachmentClass(this._getAttachment(t.placement))},e._fixTransition=function(){var t=this.getTipElement(),e=this.config.animation;null===t.getAttribute("x-placement")&&(i.default(t).removeClass("fade"),this.config.animation=!1,this.hide(),this.show(),this.config.animation=e)},t._jQueryInterface=function(e){return this.each((function(){var n=i.default(this),o=n.data("bs.tooltip"),r="object"==typeof e&&e;if((o||!/dispose|hide/.test(e))&&(o||(o=new t(this,r),n.data("bs.tooltip",o)),"string"==typeof e)){if("undefined"==typeof o[e])throw new TypeError('No method named "'+e+'"');o[e]()}}))},r(t,null,[{key:"VERSION",get:function(){return"4.5.3"}},{key:"Default",get:function(){return Jt}},{key:"NAME",get:function(){return Yt}},{key:"DATA_KEY",get:function(){return"bs.tooltip"}},{key:"Event",get:function(){return Zt}},{key:"EVENT_KEY",get:function(){return".bs.tooltip"}},{key:"DefaultType",get:function(){return Gt}}]),t}();i.default.fn[Yt]=te._jQueryInterface,i.default.fn[Yt].Constructor=te,i.default.fn[Yt].noConflict=function(){return i.default.fn[Yt]=zt,te._jQueryInterface};var ee="popover",ne=i.default.fn[ee],ie=new RegExp("(^|\\s)bs-popover\\S+","g"),oe=a({},te.Default,{placement:"right",trigger:"click",content:"",template:'<div class="popover" role="tooltip"><div class="arrow"></div><h3 class="popover-header"></h3><div class="popover-body"></div></div>'}),re=a({},te.DefaultType,{content:"(string|element|function)"}),ae={HIDE:"hide.bs.popover",HIDDEN:"hidden.bs.popover",SHOW:"show.bs.popover",SHOWN:"shown.bs.popover",INSERTED:"inserted.bs.popover",CLICK:"click.bs.popover",FOCUSIN:"focusin.bs.popover",FOCUSOUT:"focusout.bs.popover",MOUSEENTER:"mouseenter.bs.popover",MOUSELEAVE:"mouseleave.bs.popover"},se=function(t){var e,n;function o(){return t.apply(this,arguments)||this}n=t,(e=o).prototype=Object.create(n.prototype),e.prototype.constructor=e,e.__proto__=n;var a=o.prototype;return a.isWithContent=function(){return this.getTitle()||this._getContent()},a.addAttachmentClass=function(t){i.default(this.getTipElement()).addClass("bs-popover-"+t)},a.getTipElement=function(){return this.tip=this.tip||i.default(this.config.template)[0],this.tip},a.setContent=function(){var t=i.default(this.getTipElement());this.setElementContent(t.find(".popover-header"),this.getTitle());var e=this._getContent();"function"==typeof e&&(e=e.call(this.element)),this.setElementContent(t.find(".popover-body"),e),t.removeClass("fade show")},a._getContent=function(){return this.element.getAttribute("data-content")||this.config.content},a._cleanTipClass=function(){var t=i.default(this.getTipElement()),e=t.attr("class").match(ie);null!==e&&e.length>0&&t.removeClass(e.join(""))},o._jQueryInterface=function(t){return this.each((function(){var e=i.default(this).data("bs.popover"),n="object"==typeof t?t:null;if((e||!/dispose|hide/.test(t))&&(e||(e=new o(this,n),i.default(this).data("bs.popover",e)),"string"==typeof t)){if("undefined"==typeof e[t])throw new TypeError('No method named "'+t+'"');e[t]()}}))},r(o,null,[{key:"VERSION",get:function(){return"4.5.3"}},{key:"Default",get:function(){return oe}},{key:"NAME",get:function(){return ee}},{key:"DATA_KEY",get:function(){return"bs.popover"}},{key:"Event",get:function(){return ae}},{key:"EVENT_KEY",get:function(){return".bs.popover"}},{key:"DefaultType",get:function(){return re}}]),o}(te);i.default.fn[ee]=se._jQueryInterface,i.default.fn[ee].Constructor=se,i.default.fn[ee].noConflict=function(){return i.default.fn[ee]=ne,se._jQueryInterface};var le="scrollspy",ue=i.default.fn[le],fe={offset:10,method:"auto",target:""},de={offset:"number",method:"string",target:"(string|element)"},ce=function(){function t(t,e){var n=this;this._element=t,this._scrollElement="BODY"===t.tagName?window:t,this._config=this._getConfig(e),this._selector=this._config.target+" .nav-link,"+this._config.target+" .list-group-item,"+this._config.target+" .dropdown-item",this._offsets=[],this._targets=[],this._activeTarget=null,this._scrollHeight=0,i.default(this._scrollElement).on("scroll.bs.scrollspy",(function(t){return n._process(t)})),this.refresh(),this._process()}var e=t.prototype;return e.refresh=function(){var t=this,e=this._scrollElement===this._scrollElement.window?"offset":"position",n="auto"===this._config.method?e:this._config.method,o="position"===n?this._getScrollTop():0;this._offsets=[],this._targets=[],this._scrollHeight=this._getScrollHeight(),[].slice.call(document.querySelectorAll(this._selector)).map((function(t){var e,r=l.getSelectorFromElement(t);if(r&&(e=document.querySelector(r)),e){var a=e.getBoundingClientRect();if(a.width||a.height)return[i.default(e)[n]().top+o,r]}return null})).filter((function(t){return t})).sort((function(t,e){return t[0]-e[0]})).forEach((function(e){t._offsets.push(e[0]),t._targets.push(e[1])}))},e.dispose=function(){i.default.removeData(this._element,"bs.scrollspy"),i.default(this._scrollElement).off(".bs.scrollspy"),this._element=null,this._scrollElement=null,this._config=null,this._selector=null,this._offsets=null,this._targets=null,this._activeTarget=null,this._scrollHeight=null},e._getConfig=function(t){if("string"!=typeof(t=a({},fe,"object"==typeof t&&t?t:{})).target&&l.isElement(t.target)){var e=i.default(t.target).attr("id");e||(e=l.getUID(le),i.default(t.target).attr("id",e)),t.target="#"+e}return l.typeCheckConfig(le,t,de),t},e._getScrollTop=function(){return this._scrollElement===window?this._scrollElement.pageYOffset:this._scrollElement.scrollTop},e._getScrollHeight=function(){return this._scrollElement.scrollHeight||Math.max(document.body.scrollHeight,document.documentElement.scrollHeight)},e._getOffsetHeight=function(){return this._scrollElement===window?window.innerHeight:this._scrollElement.getBoundingClientRect().height},e._process=function(){var t=this._getScrollTop()+this._config.offset,e=this._getScrollHeight(),n=this._config.offset+e-this._getOffsetHeight();if(this._scrollHeight!==e&&this.refresh(),t>=n){var i=this._targets[this._targets.length-1];this._activeTarget!==i&&this._activate(i)}else{if(this._activeTarget&&t<this._offsets[0]&&this._offsets[0]>0)return this._activeTarget=null,void this._clear();for(var o=this._offsets.length;o--;){this._activeTarget!==this._targets[o]&&t>=this._offsets[o]&&("undefined"==typeof this._offsets[o+1]||t<this._offsets[o+1])&&this._activate(this._targets[o])}}},e._activate=function(t){this._activeTarget=t,this._clear();var e=this._selector.split(",").map((function(e){return e+'[data-target="'+t+'"],'+e+'[href="'+t+'"]'})),n=i.default([].slice.call(document.querySelectorAll(e.join(","))));n.hasClass("dropdown-item")?(n.closest(".dropdown").find(".dropdown-toggle").addClass("active"),n.addClass("active")):(n.addClass("active"),n.parents(".nav, .list-group").prev(".nav-link, .list-group-item").addClass("active"),n.parents(".nav, .list-group").prev(".nav-item").children(".nav-link").addClass("active")),i.default(this._scrollElement).trigger("activate.bs.scrollspy",{relatedTarget:t})},e._clear=function(){[].slice.call(document.querySelectorAll(this._selector)).filter((function(t){return t.classList.contains("active")})).forEach((function(t){return t.classList.remove("active")}))},t._jQueryInterface=function(e){return this.each((function(){var n=i.default(this).data("bs.scrollspy");if(n||(n=new t(this,"object"==typeof e&&e),i.default(this).data("bs.scrollspy",n)),"string"==typeof e){if("undefined"==typeof n[e])throw new TypeError('No method named "'+e+'"');n[e]()}}))},r(t,null,[{key:"VERSION",get:function(){return"4.5.3"}},{key:"Default",get:function(){return fe}}]),t}();i.default(window).on("load.bs.scrollspy.data-api",(function(){for(var t=[].slice.call(document.querySelectorAll('[data-spy="scroll"]')),e=t.length;e--;){var n=i.default(t[e]);ce._jQueryInterface.call(n,n.data())}})),i.default.fn[le]=ce._jQueryInterface,i.default.fn[le].Constructor=ce,i.default.fn[le].noConflict=function(){return i.default.fn[le]=ue,ce._jQueryInterface};var he=i.default.fn.tab,pe=function(){function t(t){this._element=t}var e=t.prototype;return e.show=function(){var t=this;if(!(this._element.parentNode&&this._element.parentNode.nodeType===Node.ELEMENT_NODE&&i.default(this._element).hasClass("active")||i.default(this._element).hasClass("disabled"))){var e,n,o=i.default(this._element).closest(".nav, .list-group")[0],r=l.getSelectorFromElement(this._element);if(o){var a="UL"===o.nodeName||"OL"===o.nodeName?"> li > .active":".active";n=(n=i.default.makeArray(i.default(o).find(a)))[n.length-1]}var s=i.default.Event("hide.bs.tab",{relatedTarget:this._element}),u=i.default.Event("show.bs.tab",{relatedTarget:n});if(n&&i.default(n).trigger(s),i.default(this._element).trigger(u),!u.isDefaultPrevented()&&!s.isDefaultPrevented()){r&&(e=document.querySelector(r)),this._activate(this._element,o);var f=function(){var e=i.default.Event("hidden.bs.tab",{relatedTarget:t._element}),o=i.default.Event("shown.bs.tab",{relatedTarget:n});i.default(n).trigger(e),i.default(t._element).trigger(o)};e?this._activate(e,e.parentNode,f):f()}}},e.dispose=function(){i.default.removeData(this._element,"bs.tab"),this._element=null},e._activate=function(t,e,n){var o=this,r=(!e||"UL"!==e.nodeName&&"OL"!==e.nodeName?i.default(e).children(".active"):i.default(e).find("> li > .active"))[0],a=n&&r&&i.default(r).hasClass("fade"),s=function(){return o._transitionComplete(t,r,n)};if(r&&a){var u=l.getTransitionDurationFromElement(r);i.default(r).removeClass("show").one(l.TRANSITION_END,s).emulateTransitionEnd(u)}else s()},e._transitionComplete=function(t,e,n){if(e){i.default(e).removeClass("active");var o=i.default(e.parentNode).find("> .dropdown-menu .active")[0];o&&i.default(o).removeClass("active"),"tab"===e.getAttribute("role")&&e.setAttribute("aria-selected",!1)}if(i.default(t).addClass("active"),"tab"===t.getAttribute("role")&&t.setAttribute("aria-selected",!0),l.reflow(t),t.classList.contains("fade")&&t.classList.add("show"),t.parentNode&&i.default(t.parentNode).hasClass("dropdown-menu")){var r=i.default(t).closest(".dropdown")[0];if(r){var a=[].slice.call(r.querySelectorAll(".dropdown-toggle"));i.default(a).addClass("active")}t.setAttribute("aria-expanded",!0)}n&&n()},t._jQueryInterface=function(e){return this.each((function(){var n=i.default(this),o=n.data("bs.tab");if(o||(o=new t(this),n.data("bs.tab",o)),"string"==typeof e){if("undefined"==typeof o[e])throw new TypeError('No method named "'+e+'"');o[e]()}}))},r(t,null,[{key:"VERSION",get:function(){return"4.5.3"}}]),t}();i.default(document).on("click.bs.tab.data-api",'[data-toggle="tab"], [data-toggle="pill"], [data-toggle="list"]',(function(t){t.preventDefault(),pe._jQueryInterface.call(i.default(this),"show")})),i.default.fn.tab=pe._jQueryInterface,i.default.fn.tab.Constructor=pe,i.default.fn.tab.noConflict=function(){return i.default.fn.tab=he,pe._jQueryInterface};var me=i.default.fn.toast,ge={animation:"boolean",autohide:"boolean",delay:"number"},ve={animation:!0,autohide:!0,delay:500},_e=function(){function t(t,e){this._element=t,this._config=this._getConfig(e),this._timeout=null,this._setListeners()}var e=t.prototype;return e.show=function(){var t=this,e=i.default.Event("show.bs.toast");if(i.default(this._element).trigger(e),!e.isDefaultPrevented()){this._clearTimeout(),this._config.animation&&this._element.classList.add("fade");var n=function(){t._element.classList.remove("showing"),t._element.classList.add("show"),i.default(t._element).trigger("shown.bs.toast"),t._config.autohide&&(t._timeout=setTimeout((function(){t.hide()}),t._config.delay))};if(this._element.classList.remove("hide"),l.reflow(this._element),this._element.classList.add("showing"),this._config.animation){var o=l.getTransitionDurationFromElement(this._element);i.default(this._element).one(l.TRANSITION_END,n).emulateTransitionEnd(o)}else n()}},e.hide=function(){if(this._element.classList.contains("show")){var t=i.default.Event("hide.bs.toast");i.default(this._element).trigger(t),t.isDefaultPrevented()||this._close()}},e.dispose=function(){this._clearTimeout(),this._element.classList.contains("show")&&this._element.classList.remove("show"),i.default(this._element).off("click.dismiss.bs.toast"),i.default.removeData(this._element,"bs.toast"),this._element=null,this._config=null},e._getConfig=function(t){return t=a({},ve,i.default(this._element).data(),"object"==typeof t&&t?t:{}),l.typeCheckConfig("toast",t,this.constructor.DefaultType),t},e._setListeners=function(){var t=this;i.default(this._element).on("click.dismiss.bs.toast",'[data-dismiss="toast"]',(function(){return t.hide()}))},e._close=function(){var t=this,e=function(){t._element.classList.add("hide"),i.default(t._element).trigger("hidden.bs.toast")};if(this._element.classList.remove("show"),this._config.animation){var n=l.getTransitionDurationFromElement(this._element);i.default(this._element).one(l.TRANSITION_END,e).emulateTransitionEnd(n)}else e()},e._clearTimeout=function(){clearTimeout(this._timeout),this._timeout=null},t._jQueryInterface=function(e){return this.each((function(){var n=i.default(this),o=n.data("bs.toast");if(o||(o=new t(this,"object"==typeof e&&e),n.data("bs.toast",o)),"string"==typeof e){if("undefined"==typeof o[e])throw new TypeError('No method named "'+e+'"');o[e](this)}}))},r(t,null,[{key:"VERSION",get:function(){return"4.5.3"}},{key:"DefaultType",get:function(){return ge}},{key:"Default",get:function(){return ve}}]),t}();i.default.fn.toast=_e._jQueryInterface,i.default.fn.toast.Constructor=_e,i.default.fn.toast.noConflict=function(){return i.default.fn.toast=me,_e._jQueryInterface},t.Alert=d,t.Button=h,t.Carousel=y,t.Collapse=S,t.Dropdown=Ft,t.Modal=Bt,t.Popover=se,t.Scrollspy=ce,t.Tab=pe,t.Toast=_e,t.Tooltip=te,t.Util=l,Object.defineProperty(t,"__esModule",{value:!0})}));
7
- //# sourceMappingURL=bootstrap.bundle.min.js.map
 
 
 
 
 
 
 
resources/js/bootstrap4.min.js DELETED
@@ -1,7 +0,0 @@
1
- /*!
2
- * Bootstrap v4.5.3 (https://getbootstrap.com/)
3
- * Copyright 2011-2020 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)
4
- * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
5
- */
6
- !function(t,e){"object"==typeof exports&&"undefined"!=typeof module?e(exports,require("jquery"),require("popper.js")):"function"==typeof define&&define.amd?define(["exports","jquery","popper.js"],e):e((t="undefined"!=typeof globalThis?globalThis:t||self).bootstrap={},t.jQuery,t.Popper)}(this,(function(t,e,n){"use strict";function i(t){return t&&"object"==typeof t&&"default"in t?t:{default:t}}var o=i(e),a=i(n);function s(t,e){for(var n=0;n<e.length;n++){var i=e[n];i.enumerable=i.enumerable||!1,i.configurable=!0,"value"in i&&(i.writable=!0),Object.defineProperty(t,i.key,i)}}function l(t,e,n){return e&&s(t.prototype,e),n&&s(t,n),t}function r(){return(r=Object.assign||function(t){for(var e=1;e<arguments.length;e++){var n=arguments[e];for(var i in n)Object.prototype.hasOwnProperty.call(n,i)&&(t[i]=n[i])}return t}).apply(this,arguments)}function u(t){var e=this,n=!1;return o.default(this).one(d.TRANSITION_END,(function(){n=!0})),setTimeout((function(){n||d.triggerTransitionEnd(e)}),t),this}var d={TRANSITION_END:"bsTransitionEnd",getUID:function(t){do{t+=~~(1e6*Math.random())}while(document.getElementById(t));return t},getSelectorFromElement:function(t){var e=t.getAttribute("data-target");if(!e||"#"===e){var n=t.getAttribute("href");e=n&&"#"!==n?n.trim():""}try{return document.querySelector(e)?e:null}catch(t){return null}},getTransitionDurationFromElement:function(t){if(!t)return 0;var e=o.default(t).css("transition-duration"),n=o.default(t).css("transition-delay"),i=parseFloat(e),a=parseFloat(n);return i||a?(e=e.split(",")[0],n=n.split(",")[0],1e3*(parseFloat(e)+parseFloat(n))):0},reflow:function(t){return t.offsetHeight},triggerTransitionEnd:function(t){o.default(t).trigger("transitionend")},supportsTransitionEnd:function(){return Boolean("transitionend")},isElement:function(t){return(t[0]||t).nodeType},typeCheckConfig:function(t,e,n){for(var i in n)if(Object.prototype.hasOwnProperty.call(n,i)){var o=n[i],a=e[i],s=a&&d.isElement(a)?"element":null===(l=a)||"undefined"==typeof l?""+l:{}.toString.call(l).match(/\s([a-z]+)/i)[1].toLowerCase();if(!new RegExp(o).test(s))throw new Error(t.toUpperCase()+': Option "'+i+'" provided type "'+s+'" but expected type "'+o+'".')}var l},findShadowRoot:function(t){if(!document.documentElement.attachShadow)return null;if("function"==typeof t.getRootNode){var e=t.getRootNode();return e instanceof ShadowRoot?e:null}return t instanceof ShadowRoot?t:t.parentNode?d.findShadowRoot(t.parentNode):null},jQueryDetection:function(){if("undefined"==typeof o.default)throw new TypeError("Bootstrap's JavaScript requires jQuery. jQuery must be included before Bootstrap's JavaScript.");var t=o.default.fn.jquery.split(" ")[0].split(".");if(t[0]<2&&t[1]<9||1===t[0]&&9===t[1]&&t[2]<1||t[0]>=4)throw new Error("Bootstrap's JavaScript requires at least jQuery v1.9.1 but less than v4.0.0")}};d.jQueryDetection(),o.default.fn.emulateTransitionEnd=u,o.default.event.special[d.TRANSITION_END]={bindType:"transitionend",delegateType:"transitionend",handle:function(t){if(o.default(t.target).is(this))return t.handleObj.handler.apply(this,arguments)}};var f="alert",c=o.default.fn[f],h=function(){function t(t){this._element=t}var e=t.prototype;return e.close=function(t){var e=this._element;t&&(e=this._getRootElement(t)),this._triggerCloseEvent(e).isDefaultPrevented()||this._removeElement(e)},e.dispose=function(){o.default.removeData(this._element,"bs.alert"),this._element=null},e._getRootElement=function(t){var e=d.getSelectorFromElement(t),n=!1;return e&&(n=document.querySelector(e)),n||(n=o.default(t).closest(".alert")[0]),n},e._triggerCloseEvent=function(t){var e=o.default.Event("close.bs.alert");return o.default(t).trigger(e),e},e._removeElement=function(t){var e=this;if(o.default(t).removeClass("show"),o.default(t).hasClass("fade")){var n=d.getTransitionDurationFromElement(t);o.default(t).one(d.TRANSITION_END,(function(n){return e._destroyElement(t,n)})).emulateTransitionEnd(n)}else this._destroyElement(t)},e._destroyElement=function(t){o.default(t).detach().trigger("closed.bs.alert").remove()},t._jQueryInterface=function(e){return this.each((function(){var n=o.default(this),i=n.data("bs.alert");i||(i=new t(this),n.data("bs.alert",i)),"close"===e&&i[e](this)}))},t._handleDismiss=function(t){return function(e){e&&e.preventDefault(),t.close(this)}},l(t,null,[{key:"VERSION",get:function(){return"4.5.3"}}]),t}();o.default(document).on("click.bs.alert.data-api",'[data-dismiss="alert"]',h._handleDismiss(new h)),o.default.fn[f]=h._jQueryInterface,o.default.fn[f].Constructor=h,o.default.fn[f].noConflict=function(){return o.default.fn[f]=c,h._jQueryInterface};var g=o.default.fn.button,m=function(){function t(t){this._element=t,this.shouldAvoidTriggerChange=!1}var e=t.prototype;return e.toggle=function(){var t=!0,e=!0,n=o.default(this._element).closest('[data-toggle="buttons"]')[0];if(n){var i=this._element.querySelector('input:not([type="hidden"])');if(i){if("radio"===i.type)if(i.checked&&this._element.classList.contains("active"))t=!1;else{var a=n.querySelector(".active");a&&o.default(a).removeClass("active")}t&&("checkbox"!==i.type&&"radio"!==i.type||(i.checked=!this._element.classList.contains("active")),this.shouldAvoidTriggerChange||o.default(i).trigger("change")),i.focus(),e=!1}}this._element.hasAttribute("disabled")||this._element.classList.contains("disabled")||(e&&this._element.setAttribute("aria-pressed",!this._element.classList.contains("active")),t&&o.default(this._element).toggleClass("active"))},e.dispose=function(){o.default.removeData(this._element,"bs.button"),this._element=null},t._jQueryInterface=function(e,n){return this.each((function(){var i=o.default(this),a=i.data("bs.button");a||(a=new t(this),i.data("bs.button",a)),a.shouldAvoidTriggerChange=n,"toggle"===e&&a[e]()}))},l(t,null,[{key:"VERSION",get:function(){return"4.5.3"}}]),t}();o.default(document).on("click.bs.button.data-api",'[data-toggle^="button"]',(function(t){var e=t.target,n=e;if(o.default(e).hasClass("btn")||(e=o.default(e).closest(".btn")[0]),!e||e.hasAttribute("disabled")||e.classList.contains("disabled"))t.preventDefault();else{var i=e.querySelector('input:not([type="hidden"])');if(i&&(i.hasAttribute("disabled")||i.classList.contains("disabled")))return void t.preventDefault();"INPUT"!==n.tagName&&"LABEL"===e.tagName||m._jQueryInterface.call(o.default(e),"toggle","INPUT"===n.tagName)}})).on("focus.bs.button.data-api blur.bs.button.data-api",'[data-toggle^="button"]',(function(t){var e=o.default(t.target).closest(".btn")[0];o.default(e).toggleClass("focus",/^focus(in)?$/.test(t.type))})),o.default(window).on("load.bs.button.data-api",(function(){for(var t=[].slice.call(document.querySelectorAll('[data-toggle="buttons"] .btn')),e=0,n=t.length;e<n;e++){var i=t[e],o=i.querySelector('input:not([type="hidden"])');o.checked||o.hasAttribute("checked")?i.classList.add("active"):i.classList.remove("active")}for(var a=0,s=(t=[].slice.call(document.querySelectorAll('[data-toggle="button"]'))).length;a<s;a++){var l=t[a];"true"===l.getAttribute("aria-pressed")?l.classList.add("active"):l.classList.remove("active")}})),o.default.fn.button=m._jQueryInterface,o.default.fn.button.Constructor=m,o.default.fn.button.noConflict=function(){return o.default.fn.button=g,m._jQueryInterface};var p="carousel",_=".bs.carousel",v=o.default.fn[p],b={interval:5e3,keyboard:!0,slide:!1,pause:"hover",wrap:!0,touch:!0},y={interval:"(number|boolean)",keyboard:"boolean",slide:"(boolean|string)",pause:"(string|boolean)",wrap:"boolean",touch:"boolean"},E={TOUCH:"touch",PEN:"pen"},w=function(){function t(t,e){this._items=null,this._interval=null,this._activeElement=null,this._isPaused=!1,this._isSliding=!1,this.touchTimeout=null,this.touchStartX=0,this.touchDeltaX=0,this._config=this._getConfig(e),this._element=t,this._indicatorsElement=this._element.querySelector(".carousel-indicators"),this._touchSupported="ontouchstart"in document.documentElement||navigator.maxTouchPoints>0,this._pointerEvent=Boolean(window.PointerEvent||window.MSPointerEvent),this._addEventListeners()}var e=t.prototype;return e.next=function(){this._isSliding||this._slide("next")},e.nextWhenVisible=function(){var t=o.default(this._element);!document.hidden&&t.is(":visible")&&"hidden"!==t.css("visibility")&&this.next()},e.prev=function(){this._isSliding||this._slide("prev")},e.pause=function(t){t||(this._isPaused=!0),this._element.querySelector(".carousel-item-next, .carousel-item-prev")&&(d.triggerTransitionEnd(this._element),this.cycle(!0)),clearInterval(this._interval),this._interval=null},e.cycle=function(t){t||(this._isPaused=!1),this._interval&&(clearInterval(this._interval),this._interval=null),this._config.interval&&!this._isPaused&&(this._interval=setInterval((document.visibilityState?this.nextWhenVisible:this.next).bind(this),this._config.interval))},e.to=function(t){var e=this;this._activeElement=this._element.querySelector(".active.carousel-item");var n=this._getItemIndex(this._activeElement);if(!(t>this._items.length-1||t<0))if(this._isSliding)o.default(this._element).one("slid.bs.carousel",(function(){return e.to(t)}));else{if(n===t)return this.pause(),void this.cycle();var i=t>n?"next":"prev";this._slide(i,this._items[t])}},e.dispose=function(){o.default(this._element).off(_),o.default.removeData(this._element,"bs.carousel"),this._items=null,this._config=null,this._element=null,this._interval=null,this._isPaused=null,this._isSliding=null,this._activeElement=null,this._indicatorsElement=null},e._getConfig=function(t){return t=r({},b,t),d.typeCheckConfig(p,t,y),t},e._handleSwipe=function(){var t=Math.abs(this.touchDeltaX);if(!(t<=40)){var e=t/this.touchDeltaX;this.touchDeltaX=0,e>0&&this.prev(),e<0&&this.next()}},e._addEventListeners=function(){var t=this;this._config.keyboard&&o.default(this._element).on("keydown.bs.carousel",(function(e){return t._keydown(e)})),"hover"===this._config.pause&&o.default(this._element).on("mouseenter.bs.carousel",(function(e){return t.pause(e)})).on("mouseleave.bs.carousel",(function(e){return t.cycle(e)})),this._config.touch&&this._addTouchEventListeners()},e._addTouchEventListeners=function(){var t=this;if(this._touchSupported){var e=function(e){t._pointerEvent&&E[e.originalEvent.pointerType.toUpperCase()]?t.touchStartX=e.originalEvent.clientX:t._pointerEvent||(t.touchStartX=e.originalEvent.touches[0].clientX)},n=function(e){t._pointerEvent&&E[e.originalEvent.pointerType.toUpperCase()]&&(t.touchDeltaX=e.originalEvent.clientX-t.touchStartX),t._handleSwipe(),"hover"===t._config.pause&&(t.pause(),t.touchTimeout&&clearTimeout(t.touchTimeout),t.touchTimeout=setTimeout((function(e){return t.cycle(e)}),500+t._config.interval))};o.default(this._element.querySelectorAll(".carousel-item img")).on("dragstart.bs.carousel",(function(t){return t.preventDefault()})),this._pointerEvent?(o.default(this._element).on("pointerdown.bs.carousel",(function(t){return e(t)})),o.default(this._element).on("pointerup.bs.carousel",(function(t){return n(t)})),this._element.classList.add("pointer-event")):(o.default(this._element).on("touchstart.bs.carousel",(function(t){return e(t)})),o.default(this._element).on("touchmove.bs.carousel",(function(e){return function(e){e.originalEvent.touches&&e.originalEvent.touches.length>1?t.touchDeltaX=0:t.touchDeltaX=e.originalEvent.touches[0].clientX-t.touchStartX}(e)})),o.default(this._element).on("touchend.bs.carousel",(function(t){return n(t)})))}},e._keydown=function(t){if(!/input|textarea/i.test(t.target.tagName))switch(t.which){case 37:t.preventDefault(),this.prev();break;case 39:t.preventDefault(),this.next()}},e._getItemIndex=function(t){return this._items=t&&t.parentNode?[].slice.call(t.parentNode.querySelectorAll(".carousel-item")):[],this._items.indexOf(t)},e._getItemByDirection=function(t,e){var n="next"===t,i="prev"===t,o=this._getItemIndex(e),a=this._items.length-1;if((i&&0===o||n&&o===a)&&!this._config.wrap)return e;var s=(o+("prev"===t?-1:1))%this._items.length;return-1===s?this._items[this._items.length-1]:this._items[s]},e._triggerSlideEvent=function(t,e){var n=this._getItemIndex(t),i=this._getItemIndex(this._element.querySelector(".active.carousel-item")),a=o.default.Event("slide.bs.carousel",{relatedTarget:t,direction:e,from:i,to:n});return o.default(this._element).trigger(a),a},e._setActiveIndicatorElement=function(t){if(this._indicatorsElement){var e=[].slice.call(this._indicatorsElement.querySelectorAll(".active"));o.default(e).removeClass("active");var n=this._indicatorsElement.children[this._getItemIndex(t)];n&&o.default(n).addClass("active")}},e._slide=function(t,e){var n,i,a,s=this,l=this._element.querySelector(".active.carousel-item"),r=this._getItemIndex(l),u=e||l&&this._getItemByDirection(t,l),f=this._getItemIndex(u),c=Boolean(this._interval);if("next"===t?(n="carousel-item-left",i="carousel-item-next",a="left"):(n="carousel-item-right",i="carousel-item-prev",a="right"),u&&o.default(u).hasClass("active"))this._isSliding=!1;else if(!this._triggerSlideEvent(u,a).isDefaultPrevented()&&l&&u){this._isSliding=!0,c&&this.pause(),this._setActiveIndicatorElement(u);var h=o.default.Event("slid.bs.carousel",{relatedTarget:u,direction:a,from:r,to:f});if(o.default(this._element).hasClass("slide")){o.default(u).addClass(i),d.reflow(u),o.default(l).addClass(n),o.default(u).addClass(n);var g=parseInt(u.getAttribute("data-interval"),10);g?(this._config.defaultInterval=this._config.defaultInterval||this._config.interval,this._config.interval=g):this._config.interval=this._config.defaultInterval||this._config.interval;var m=d.getTransitionDurationFromElement(l);o.default(l).one(d.TRANSITION_END,(function(){o.default(u).removeClass(n+" "+i).addClass("active"),o.default(l).removeClass("active "+i+" "+n),s._isSliding=!1,setTimeout((function(){return o.default(s._element).trigger(h)}),0)})).emulateTransitionEnd(m)}else o.default(l).removeClass("active"),o.default(u).addClass("active"),this._isSliding=!1,o.default(this._element).trigger(h);c&&this.cycle()}},t._jQueryInterface=function(e){return this.each((function(){var n=o.default(this).data("bs.carousel"),i=r({},b,o.default(this).data());"object"==typeof e&&(i=r({},i,e));var a="string"==typeof e?e:i.slide;if(n||(n=new t(this,i),o.default(this).data("bs.carousel",n)),"number"==typeof e)n.to(e);else if("string"==typeof a){if("undefined"==typeof n[a])throw new TypeError('No method named "'+a+'"');n[a]()}else i.interval&&i.ride&&(n.pause(),n.cycle())}))},t._dataApiClickHandler=function(e){var n=d.getSelectorFromElement(this);if(n){var i=o.default(n)[0];if(i&&o.default(i).hasClass("carousel")){var a=r({},o.default(i).data(),o.default(this).data()),s=this.getAttribute("data-slide-to");s&&(a.interval=!1),t._jQueryInterface.call(o.default(i),a),s&&o.default(i).data("bs.carousel").to(s),e.preventDefault()}}},l(t,null,[{key:"VERSION",get:function(){return"4.5.3"}},{key:"Default",get:function(){return b}}]),t}();o.default(document).on("click.bs.carousel.data-api","[data-slide], [data-slide-to]",w._dataApiClickHandler),o.default(window).on("load.bs.carousel.data-api",(function(){for(var t=[].slice.call(document.querySelectorAll('[data-ride="carousel"]')),e=0,n=t.length;e<n;e++){var i=o.default(t[e]);w._jQueryInterface.call(i,i.data())}})),o.default.fn[p]=w._jQueryInterface,o.default.fn[p].Constructor=w,o.default.fn[p].noConflict=function(){return o.default.fn[p]=v,w._jQueryInterface};var T="collapse",C=o.default.fn[T],S={toggle:!0,parent:""},N={toggle:"boolean",parent:"(string|element)"},D=function(){function t(t,e){this._isTransitioning=!1,this._element=t,this._config=this._getConfig(e),this._triggerArray=[].slice.call(document.querySelectorAll('[data-toggle="collapse"][href="#'+t.id+'"],[data-toggle="collapse"][data-target="#'+t.id+'"]'));for(var n=[].slice.call(document.querySelectorAll('[data-toggle="collapse"]')),i=0,o=n.length;i<o;i++){var a=n[i],s=d.getSelectorFromElement(a),l=[].slice.call(document.querySelectorAll(s)).filter((function(e){return e===t}));null!==s&&l.length>0&&(this._selector=s,this._triggerArray.push(a))}this._parent=this._config.parent?this._getParent():null,this._config.parent||this._addAriaAndCollapsedClass(this._element,this._triggerArray),this._config.toggle&&this.toggle()}var e=t.prototype;return e.toggle=function(){o.default(this._element).hasClass("show")?this.hide():this.show()},e.show=function(){var e,n,i=this;if(!this._isTransitioning&&!o.default(this._element).hasClass("show")&&(this._parent&&0===(e=[].slice.call(this._parent.querySelectorAll(".show, .collapsing")).filter((function(t){return"string"==typeof i._config.parent?t.getAttribute("data-parent")===i._config.parent:t.classList.contains("collapse")}))).length&&(e=null),!(e&&(n=o.default(e).not(this._selector).data("bs.collapse"))&&n._isTransitioning))){var a=o.default.Event("show.bs.collapse");if(o.default(this._element).trigger(a),!a.isDefaultPrevented()){e&&(t._jQueryInterface.call(o.default(e).not(this._selector),"hide"),n||o.default(e).data("bs.collapse",null));var s=this._getDimension();o.default(this._element).removeClass("collapse").addClass("collapsing"),this._element.style[s]=0,this._triggerArray.length&&o.default(this._triggerArray).removeClass("collapsed").attr("aria-expanded",!0),this.setTransitioning(!0);var l="scroll"+(s[0].toUpperCase()+s.slice(1)),r=d.getTransitionDurationFromElement(this._element);o.default(this._element).one(d.TRANSITION_END,(function(){o.default(i._element).removeClass("collapsing").addClass("collapse show"),i._element.style[s]="",i.setTransitioning(!1),o.default(i._element).trigger("shown.bs.collapse")})).emulateTransitionEnd(r),this._element.style[s]=this._element[l]+"px"}}},e.hide=function(){var t=this;if(!this._isTransitioning&&o.default(this._element).hasClass("show")){var e=o.default.Event("hide.bs.collapse");if(o.default(this._element).trigger(e),!e.isDefaultPrevented()){var n=this._getDimension();this._element.style[n]=this._element.getBoundingClientRect()[n]+"px",d.reflow(this._element),o.default(this._element).addClass("collapsing").removeClass("collapse show");var i=this._triggerArray.length;if(i>0)for(var a=0;a<i;a++){var s=this._triggerArray[a],l=d.getSelectorFromElement(s);if(null!==l)o.default([].slice.call(document.querySelectorAll(l))).hasClass("show")||o.default(s).addClass("collapsed").attr("aria-expanded",!1)}this.setTransitioning(!0);this._element.style[n]="";var r=d.getTransitionDurationFromElement(this._element);o.default(this._element).one(d.TRANSITION_END,(function(){t.setTransitioning(!1),o.default(t._element).removeClass("collapsing").addClass("collapse").trigger("hidden.bs.collapse")})).emulateTransitionEnd(r)}}},e.setTransitioning=function(t){this._isTransitioning=t},e.dispose=function(){o.default.removeData(this._element,"bs.collapse"),this._config=null,this._parent=null,this._element=null,this._triggerArray=null,this._isTransitioning=null},e._getConfig=function(t){return(t=r({},S,t)).toggle=Boolean(t.toggle),d.typeCheckConfig(T,t,N),t},e._getDimension=function(){return o.default(this._element).hasClass("width")?"width":"height"},e._getParent=function(){var e,n=this;d.isElement(this._config.parent)?(e=this._config.parent,"undefined"!=typeof this._config.parent.jquery&&(e=this._config.parent[0])):e=document.querySelector(this._config.parent);var i='[data-toggle="collapse"][data-parent="'+this._config.parent+'"]',a=[].slice.call(e.querySelectorAll(i));return o.default(a).each((function(e,i){n._addAriaAndCollapsedClass(t._getTargetFromElement(i),[i])})),e},e._addAriaAndCollapsedClass=function(t,e){var n=o.default(t).hasClass("show");e.length&&o.default(e).toggleClass("collapsed",!n).attr("aria-expanded",n)},t._getTargetFromElement=function(t){var e=d.getSelectorFromElement(t);return e?document.querySelector(e):null},t._jQueryInterface=function(e){return this.each((function(){var n=o.default(this),i=n.data("bs.collapse"),a=r({},S,n.data(),"object"==typeof e&&e?e:{});if(!i&&a.toggle&&"string"==typeof e&&/show|hide/.test(e)&&(a.toggle=!1),i||(i=new t(this,a),n.data("bs.collapse",i)),"string"==typeof e){if("undefined"==typeof i[e])throw new TypeError('No method named "'+e+'"');i[e]()}}))},l(t,null,[{key:"VERSION",get:function(){return"4.5.3"}},{key:"Default",get:function(){return S}}]),t}();o.default(document).on("click.bs.collapse.data-api",'[data-toggle="collapse"]',(function(t){"A"===t.currentTarget.tagName&&t.preventDefault();var e=o.default(this),n=d.getSelectorFromElement(this),i=[].slice.call(document.querySelectorAll(n));o.default(i).each((function(){var t=o.default(this),n=t.data("bs.collapse")?"toggle":e.data();D._jQueryInterface.call(t,n)}))})),o.default.fn[T]=D._jQueryInterface,o.default.fn[T].Constructor=D,o.default.fn[T].noConflict=function(){return o.default.fn[T]=C,D._jQueryInterface};var k="dropdown",A=o.default.fn[k],I=new RegExp("38|40|27"),j={offset:0,flip:!0,boundary:"scrollParent",reference:"toggle",display:"dynamic",popperConfig:null},O={offset:"(number|string|function)",flip:"boolean",boundary:"(string|element)",reference:"(string|element)",display:"string",popperConfig:"(null|object)"},x=function(){function t(t,e){this._element=t,this._popper=null,this._config=this._getConfig(e),this._menu=this._getMenuElement(),this._inNavbar=this._detectNavbar(),this._addEventListeners()}var e=t.prototype;return e.toggle=function(){if(!this._element.disabled&&!o.default(this._element).hasClass("disabled")){var e=o.default(this._menu).hasClass("show");t._clearMenus(),e||this.show(!0)}},e.show=function(e){if(void 0===e&&(e=!1),!(this._element.disabled||o.default(this._element).hasClass("disabled")||o.default(this._menu).hasClass("show"))){var n={relatedTarget:this._element},i=o.default.Event("show.bs.dropdown",n),s=t._getParentFromElement(this._element);if(o.default(s).trigger(i),!i.isDefaultPrevented()){if(!this._inNavbar&&e){if("undefined"==typeof a.default)throw new TypeError("Bootstrap's dropdowns require Popper.js (https://popper.js.org/)");var l=this._element;"parent"===this._config.reference?l=s:d.isElement(this._config.reference)&&(l=this._config.reference,"undefined"!=typeof this._config.reference.jquery&&(l=this._config.reference[0])),"scrollParent"!==this._config.boundary&&o.default(s).addClass("position-static"),this._popper=new a.default(l,this._menu,this._getPopperConfig())}"ontouchstart"in document.documentElement&&0===o.default(s).closest(".navbar-nav").length&&o.default(document.body).children().on("mouseover",null,o.default.noop),this._element.focus(),this._element.setAttribute("aria-expanded",!0),o.default(this._menu).toggleClass("show"),o.default(s).toggleClass("show").trigger(o.default.Event("shown.bs.dropdown",n))}}},e.hide=function(){if(!this._element.disabled&&!o.default(this._element).hasClass("disabled")&&o.default(this._menu).hasClass("show")){var e={relatedTarget:this._element},n=o.default.Event("hide.bs.dropdown",e),i=t._getParentFromElement(this._element);o.default(i).trigger(n),n.isDefaultPrevented()||(this._popper&&this._popper.destroy(),o.default(this._menu).toggleClass("show"),o.default(i).toggleClass("show").trigger(o.default.Event("hidden.bs.dropdown",e)))}},e.dispose=function(){o.default.removeData(this._element,"bs.dropdown"),o.default(this._element).off(".bs.dropdown"),this._element=null,this._menu=null,null!==this._popper&&(this._popper.destroy(),this._popper=null)},e.update=function(){this._inNavbar=this._detectNavbar(),null!==this._popper&&this._popper.scheduleUpdate()},e._addEventListeners=function(){var t=this;o.default(this._element).on("click.bs.dropdown",(function(e){e.preventDefault(),e.stopPropagation(),t.toggle()}))},e._getConfig=function(t){return t=r({},this.constructor.Default,o.default(this._element).data(),t),d.typeCheckConfig(k,t,this.constructor.DefaultType),t},e._getMenuElement=function(){if(!this._menu){var e=t._getParentFromElement(this._element);e&&(this._menu=e.querySelector(".dropdown-menu"))}return this._menu},e._getPlacement=function(){var t=o.default(this._element.parentNode),e="bottom-start";return t.hasClass("dropup")?e=o.default(this._menu).hasClass("dropdown-menu-right")?"top-end":"top-start":t.hasClass("dropright")?e="right-start":t.hasClass("dropleft")?e="left-start":o.default(this._menu).hasClass("dropdown-menu-right")&&(e="bottom-end"),e},e._detectNavbar=function(){return o.default(this._element).closest(".navbar").length>0},e._getOffset=function(){var t=this,e={};return"function"==typeof this._config.offset?e.fn=function(e){return e.offsets=r({},e.offsets,t._config.offset(e.offsets,t._element)||{}),e}:e.offset=this._config.offset,e},e._getPopperConfig=function(){var t={placement:this._getPlacement(),modifiers:{offset:this._getOffset(),flip:{enabled:this._config.flip},preventOverflow:{boundariesElement:this._config.boundary}}};return"static"===this._config.display&&(t.modifiers.applyStyle={enabled:!1}),r({},t,this._config.popperConfig)},t._jQueryInterface=function(e){return this.each((function(){var n=o.default(this).data("bs.dropdown");if(n||(n=new t(this,"object"==typeof e?e:null),o.default(this).data("bs.dropdown",n)),"string"==typeof e){if("undefined"==typeof n[e])throw new TypeError('No method named "'+e+'"');n[e]()}}))},t._clearMenus=function(e){if(!e||3!==e.which&&("keyup"!==e.type||9===e.which))for(var n=[].slice.call(document.querySelectorAll('[data-toggle="dropdown"]')),i=0,a=n.length;i<a;i++){var s=t._getParentFromElement(n[i]),l=o.default(n[i]).data("bs.dropdown"),r={relatedTarget:n[i]};if(e&&"click"===e.type&&(r.clickEvent=e),l){var u=l._menu;if(o.default(s).hasClass("show")&&!(e&&("click"===e.type&&/input|textarea/i.test(e.target.tagName)||"keyup"===e.type&&9===e.which)&&o.default.contains(s,e.target))){var d=o.default.Event("hide.bs.dropdown",r);o.default(s).trigger(d),d.isDefaultPrevented()||("ontouchstart"in document.documentElement&&o.default(document.body).children().off("mouseover",null,o.default.noop),n[i].setAttribute("aria-expanded","false"),l._popper&&l._popper.destroy(),o.default(u).removeClass("show"),o.default(s).removeClass("show").trigger(o.default.Event("hidden.bs.dropdown",r)))}}}},t._getParentFromElement=function(t){var e,n=d.getSelectorFromElement(t);return n&&(e=document.querySelector(n)),e||t.parentNode},t._dataApiKeydownHandler=function(e){if(!(/input|textarea/i.test(e.target.tagName)?32===e.which||27!==e.which&&(40!==e.which&&38!==e.which||o.default(e.target).closest(".dropdown-menu").length):!I.test(e.which))&&!this.disabled&&!o.default(this).hasClass("disabled")){var n=t._getParentFromElement(this),i=o.default(n).hasClass("show");if(i||27!==e.which){if(e.preventDefault(),e.stopPropagation(),!i||27===e.which||32===e.which)return 27===e.which&&o.default(n.querySelector('[data-toggle="dropdown"]')).trigger("focus"),void o.default(this).trigger("click");var a=[].slice.call(n.querySelectorAll(".dropdown-menu .dropdown-item:not(.disabled):not(:disabled)")).filter((function(t){return o.default(t).is(":visible")}));if(0!==a.length){var s=a.indexOf(e.target);38===e.which&&s>0&&s--,40===e.which&&s<a.length-1&&s++,s<0&&(s=0),a[s].focus()}}}},l(t,null,[{key:"VERSION",get:function(){return"4.5.3"}},{key:"Default",get:function(){return j}},{key:"DefaultType",get:function(){return O}}]),t}();o.default(document).on("keydown.bs.dropdown.data-api",'[data-toggle="dropdown"]',x._dataApiKeydownHandler).on("keydown.bs.dropdown.data-api",".dropdown-menu",x._dataApiKeydownHandler).on("click.bs.dropdown.data-api keyup.bs.dropdown.data-api",x._clearMenus).on("click.bs.dropdown.data-api",'[data-toggle="dropdown"]',(function(t){t.preventDefault(),t.stopPropagation(),x._jQueryInterface.call(o.default(this),"toggle")})).on("click.bs.dropdown.data-api",".dropdown form",(function(t){t.stopPropagation()})),o.default.fn[k]=x._jQueryInterface,o.default.fn[k].Constructor=x,o.default.fn[k].noConflict=function(){return o.default.fn[k]=A,x._jQueryInterface};var P=o.default.fn.modal,R={backdrop:!0,keyboard:!0,focus:!0,show:!0},L={backdrop:"(boolean|string)",keyboard:"boolean",focus:"boolean",show:"boolean"},q=function(){function t(t,e){this._config=this._getConfig(e),this._element=t,this._dialog=t.querySelector(".modal-dialog"),this._backdrop=null,this._isShown=!1,this._isBodyOverflowing=!1,this._ignoreBackdropClick=!1,this._isTransitioning=!1,this._scrollbarWidth=0}var e=t.prototype;return e.toggle=function(t){return this._isShown?this.hide():this.show(t)},e.show=function(t){var e=this;if(!this._isShown&&!this._isTransitioning){o.default(this._element).hasClass("fade")&&(this._isTransitioning=!0);var n=o.default.Event("show.bs.modal",{relatedTarget:t});o.default(this._element).trigger(n),this._isShown||n.isDefaultPrevented()||(this._isShown=!0,this._checkScrollbar(),this._setScrollbar(),this._adjustDialog(),this._setEscapeEvent(),this._setResizeEvent(),o.default(this._element).on("click.dismiss.bs.modal",'[data-dismiss="modal"]',(function(t){return e.hide(t)})),o.default(this._dialog).on("mousedown.dismiss.bs.modal",(function(){o.default(e._element).one("mouseup.dismiss.bs.modal",(function(t){o.default(t.target).is(e._element)&&(e._ignoreBackdropClick=!0)}))})),this._showBackdrop((function(){return e._showElement(t)})))}},e.hide=function(t){var e=this;if(t&&t.preventDefault(),this._isShown&&!this._isTransitioning){var n=o.default.Event("hide.bs.modal");if(o.default(this._element).trigger(n),this._isShown&&!n.isDefaultPrevented()){this._isShown=!1;var i=o.default(this._element).hasClass("fade");if(i&&(this._isTransitioning=!0),this._setEscapeEvent(),this._setResizeEvent(),o.default(document).off("focusin.bs.modal"),o.default(this._element).removeClass("show"),o.default(this._element).off("click.dismiss.bs.modal"),o.default(this._dialog).off("mousedown.dismiss.bs.modal"),i){var a=d.getTransitionDurationFromElement(this._element);o.default(this._element).one(d.TRANSITION_END,(function(t){return e._hideModal(t)})).emulateTransitionEnd(a)}else this._hideModal()}}},e.dispose=function(){[window,this._element,this._dialog].forEach((function(t){return o.default(t).off(".bs.modal")})),o.default(document).off("focusin.bs.modal"),o.default.removeData(this._element,"bs.modal"),this._config=null,this._element=null,this._dialog=null,this._backdrop=null,this._isShown=null,this._isBodyOverflowing=null,this._ignoreBackdropClick=null,this._isTransitioning=null,this._scrollbarWidth=null},e.handleUpdate=function(){this._adjustDialog()},e._getConfig=function(t){return t=r({},R,t),d.typeCheckConfig("modal",t,L),t},e._triggerBackdropTransition=function(){var t=this;if("static"===this._config.backdrop){var e=o.default.Event("hidePrevented.bs.modal");if(o.default(this._element).trigger(e),e.isDefaultPrevented())return;var n=this._element.scrollHeight>document.documentElement.clientHeight;n||(this._element.style.overflowY="hidden"),this._element.classList.add("modal-static");var i=d.getTransitionDurationFromElement(this._dialog);o.default(this._element).off(d.TRANSITION_END),o.default(this._element).one(d.TRANSITION_END,(function(){t._element.classList.remove("modal-static"),n||o.default(t._element).one(d.TRANSITION_END,(function(){t._element.style.overflowY=""})).emulateTransitionEnd(t._element,i)})).emulateTransitionEnd(i),this._element.focus()}else this.hide()},e._showElement=function(t){var e=this,n=o.default(this._element).hasClass("fade"),i=this._dialog?this._dialog.querySelector(".modal-body"):null;this._element.parentNode&&this._element.parentNode.nodeType===Node.ELEMENT_NODE||document.body.appendChild(this._element),this._element.style.display="block",this._element.removeAttribute("aria-hidden"),this._element.setAttribute("aria-modal",!0),this._element.setAttribute("role","dialog"),o.default(this._dialog).hasClass("modal-dialog-scrollable")&&i?i.scrollTop=0:this._element.scrollTop=0,n&&d.reflow(this._element),o.default(this._element).addClass("show"),this._config.focus&&this._enforceFocus();var a=o.default.Event("shown.bs.modal",{relatedTarget:t}),s=function(){e._config.focus&&e._element.focus(),e._isTransitioning=!1,o.default(e._element).trigger(a)};if(n){var l=d.getTransitionDurationFromElement(this._dialog);o.default(this._dialog).one(d.TRANSITION_END,s).emulateTransitionEnd(l)}else s()},e._enforceFocus=function(){var t=this;o.default(document).off("focusin.bs.modal").on("focusin.bs.modal",(function(e){document!==e.target&&t._element!==e.target&&0===o.default(t._element).has(e.target).length&&t._element.focus()}))},e._setEscapeEvent=function(){var t=this;this._isShown?o.default(this._element).on("keydown.dismiss.bs.modal",(function(e){t._config.keyboard&&27===e.which?(e.preventDefault(),t.hide()):t._config.keyboard||27!==e.which||t._triggerBackdropTransition()})):this._isShown||o.default(this._element).off("keydown.dismiss.bs.modal")},e._setResizeEvent=function(){var t=this;this._isShown?o.default(window).on("resize.bs.modal",(function(e){return t.handleUpdate(e)})):o.default(window).off("resize.bs.modal")},e._hideModal=function(){var t=this;this._element.style.display="none",this._element.setAttribute("aria-hidden",!0),this._element.removeAttribute("aria-modal"),this._element.removeAttribute("role"),this._isTransitioning=!1,this._showBackdrop((function(){o.default(document.body).removeClass("modal-open"),t._resetAdjustments(),t._resetScrollbar(),o.default(t._element).trigger("hidden.bs.modal")}))},e._removeBackdrop=function(){this._backdrop&&(o.default(this._backdrop).remove(),this._backdrop=null)},e._showBackdrop=function(t){var e=this,n=o.default(this._element).hasClass("fade")?"fade":"";if(this._isShown&&this._config.backdrop){if(this._backdrop=document.createElement("div"),this._backdrop.className="modal-backdrop",n&&this._backdrop.classList.add(n),o.default(this._backdrop).appendTo(document.body),o.default(this._element).on("click.dismiss.bs.modal",(function(t){e._ignoreBackdropClick?e._ignoreBackdropClick=!1:t.target===t.currentTarget&&e._triggerBackdropTransition()})),n&&d.reflow(this._backdrop),o.default(this._backdrop).addClass("show"),!t)return;if(!n)return void t();var i=d.getTransitionDurationFromElement(this._backdrop);o.default(this._backdrop).one(d.TRANSITION_END,t).emulateTransitionEnd(i)}else if(!this._isShown&&this._backdrop){o.default(this._backdrop).removeClass("show");var a=function(){e._removeBackdrop(),t&&t()};if(o.default(this._element).hasClass("fade")){var s=d.getTransitionDurationFromElement(this._backdrop);o.default(this._backdrop).one(d.TRANSITION_END,a).emulateTransitionEnd(s)}else a()}else t&&t()},e._adjustDialog=function(){var t=this._element.scrollHeight>document.documentElement.clientHeight;!this._isBodyOverflowing&&t&&(this._element.style.paddingLeft=this._scrollbarWidth+"px"),this._isBodyOverflowing&&!t&&(this._element.style.paddingRight=this._scrollbarWidth+"px")},e._resetAdjustments=function(){this._element.style.paddingLeft="",this._element.style.paddingRight=""},e._checkScrollbar=function(){var t=document.body.getBoundingClientRect();this._isBodyOverflowing=Math.round(t.left+t.right)<window.innerWidth,this._scrollbarWidth=this._getScrollbarWidth()},e._setScrollbar=function(){var t=this;if(this._isBodyOverflowing){var e=[].slice.call(document.querySelectorAll(".fixed-top, .fixed-bottom, .is-fixed, .sticky-top")),n=[].slice.call(document.querySelectorAll(".sticky-top"));o.default(e).each((function(e,n){var i=n.style.paddingRight,a=o.default(n).css("padding-right");o.default(n).data("padding-right",i).css("padding-right",parseFloat(a)+t._scrollbarWidth+"px")})),o.default(n).each((function(e,n){var i=n.style.marginRight,a=o.default(n).css("margin-right");o.default(n).data("margin-right",i).css("margin-right",parseFloat(a)-t._scrollbarWidth+"px")}));var i=document.body.style.paddingRight,a=o.default(document.body).css("padding-right");o.default(document.body).data("padding-right",i).css("padding-right",parseFloat(a)+this._scrollbarWidth+"px")}o.default(document.body).addClass("modal-open")},e._resetScrollbar=function(){var t=[].slice.call(document.querySelectorAll(".fixed-top, .fixed-bottom, .is-fixed, .sticky-top"));o.default(t).each((function(t,e){var n=o.default(e).data("padding-right");o.default(e).removeData("padding-right"),e.style.paddingRight=n||""}));var e=[].slice.call(document.querySelectorAll(".sticky-top"));o.default(e).each((function(t,e){var n=o.default(e).data("margin-right");"undefined"!=typeof n&&o.default(e).css("margin-right",n).removeData("margin-right")}));var n=o.default(document.body).data("padding-right");o.default(document.body).removeData("padding-right"),document.body.style.paddingRight=n||""},e._getScrollbarWidth=function(){var t=document.createElement("div");t.className="modal-scrollbar-measure",document.body.appendChild(t);var e=t.getBoundingClientRect().width-t.clientWidth;return document.body.removeChild(t),e},t._jQueryInterface=function(e,n){return this.each((function(){var i=o.default(this).data("bs.modal"),a=r({},R,o.default(this).data(),"object"==typeof e&&e?e:{});if(i||(i=new t(this,a),o.default(this).data("bs.modal",i)),"string"==typeof e){if("undefined"==typeof i[e])throw new TypeError('No method named "'+e+'"');i[e](n)}else a.show&&i.show(n)}))},l(t,null,[{key:"VERSION",get:function(){return"4.5.3"}},{key:"Default",get:function(){return R}}]),t}();o.default(document).on("click.bs.modal.data-api",'[data-toggle="modal"]',(function(t){var e,n=this,i=d.getSelectorFromElement(this);i&&(e=document.querySelector(i));var a=o.default(e).data("bs.modal")?"toggle":r({},o.default(e).data(),o.default(this).data());"A"!==this.tagName&&"AREA"!==this.tagName||t.preventDefault();var s=o.default(e).one("show.bs.modal",(function(t){t.isDefaultPrevented()||s.one("hidden.bs.modal",(function(){o.default(n).is(":visible")&&n.focus()}))}));q._jQueryInterface.call(o.default(e),a,this)})),o.default.fn.modal=q._jQueryInterface,o.default.fn.modal.Constructor=q,o.default.fn.modal.noConflict=function(){return o.default.fn.modal=P,q._jQueryInterface};var F=["background","cite","href","itemtype","longdesc","poster","src","xlink:href"],Q={"*":["class","dir","id","lang","role",/^aria-[\w-]*$/i],a:["target","href","title","rel"],area:[],b:[],br:[],col:[],code:[],div:[],em:[],hr:[],h1:[],h2:[],h3:[],h4:[],h5:[],h6:[],i:[],img:["src","srcset","alt","title","width","height"],li:[],ol:[],p:[],pre:[],s:[],small:[],span:[],sub:[],sup:[],strong:[],u:[],ul:[]},B=/^(?:(?:https?|mailto|ftp|tel|file):|[^#&/:?]*(?:[#/?]|$))/gi,H=/^data:(?:image\/(?:bmp|gif|jpeg|jpg|png|tiff|webp)|video\/(?:mpeg|mp4|ogg|webm)|audio\/(?:mp3|oga|ogg|opus));base64,[\d+/a-z]+=*$/i;function U(t,e,n){if(0===t.length)return t;if(n&&"function"==typeof n)return n(t);for(var i=(new window.DOMParser).parseFromString(t,"text/html"),o=Object.keys(e),a=[].slice.call(i.body.querySelectorAll("*")),s=function(t,n){var i=a[t],s=i.nodeName.toLowerCase();if(-1===o.indexOf(i.nodeName.toLowerCase()))return i.parentNode.removeChild(i),"continue";var l=[].slice.call(i.attributes),r=[].concat(e["*"]||[],e[s]||[]);l.forEach((function(t){(function(t,e){var n=t.nodeName.toLowerCase();if(-1!==e.indexOf(n))return-1===F.indexOf(n)||Boolean(t.nodeValue.match(B)||t.nodeValue.match(H));for(var i=e.filter((function(t){return t instanceof RegExp})),o=0,a=i.length;o<a;o++)if(n.match(i[o]))return!0;return!1})(t,r)||i.removeAttribute(t.nodeName)}))},l=0,r=a.length;l<r;l++)s(l);return i.body.innerHTML}var M="tooltip",W=o.default.fn[M],V=new RegExp("(^|\\s)bs-tooltip\\S+","g"),z=["sanitize","whiteList","sanitizeFn"],K={animation:"boolean",template:"string",title:"(string|element|function)",trigger:"string",delay:"(number|object)",html:"boolean",selector:"(string|boolean)",placement:"(string|function)",offset:"(number|string|function)",container:"(string|element|boolean)",fallbackPlacement:"(string|array)",boundary:"(string|element)",sanitize:"boolean",sanitizeFn:"(null|function)",whiteList:"object",popperConfig:"(null|object)"},X={AUTO:"auto",TOP:"top",RIGHT:"right",BOTTOM:"bottom",LEFT:"left"},Y={animation:!0,template:'<div class="tooltip" role="tooltip"><div class="arrow"></div><div class="tooltip-inner"></div></div>',trigger:"hover focus",title:"",delay:0,html:!1,selector:!1,placement:"top",offset:0,container:!1,fallbackPlacement:"flip",boundary:"scrollParent",sanitize:!0,sanitizeFn:null,whiteList:Q,popperConfig:null},$={HIDE:"hide.bs.tooltip",HIDDEN:"hidden.bs.tooltip",SHOW:"show.bs.tooltip",SHOWN:"shown.bs.tooltip",INSERTED:"inserted.bs.tooltip",CLICK:"click.bs.tooltip",FOCUSIN:"focusin.bs.tooltip",FOCUSOUT:"focusout.bs.tooltip",MOUSEENTER:"mouseenter.bs.tooltip",MOUSELEAVE:"mouseleave.bs.tooltip"},J=function(){function t(t,e){if("undefined"==typeof a.default)throw new TypeError("Bootstrap's tooltips require Popper.js (https://popper.js.org/)");this._isEnabled=!0,this._timeout=0,this._hoverState="",this._activeTrigger={},this._popper=null,this.element=t,this.config=this._getConfig(e),this.tip=null,this._setListeners()}var e=t.prototype;return e.enable=function(){this._isEnabled=!0},e.disable=function(){this._isEnabled=!1},e.toggleEnabled=function(){this._isEnabled=!this._isEnabled},e.toggle=function(t){if(this._isEnabled)if(t){var e=this.constructor.DATA_KEY,n=o.default(t.currentTarget).data(e);n||(n=new this.constructor(t.currentTarget,this._getDelegateConfig()),o.default(t.currentTarget).data(e,n)),n._activeTrigger.click=!n._activeTrigger.click,n._isWithActiveTrigger()?n._enter(null,n):n._leave(null,n)}else{if(o.default(this.getTipElement()).hasClass("show"))return void this._leave(null,this);this._enter(null,this)}},e.dispose=function(){clearTimeout(this._timeout),o.default.removeData(this.element,this.constructor.DATA_KEY),o.default(this.element).off(this.constructor.EVENT_KEY),o.default(this.element).closest(".modal").off("hide.bs.modal",this._hideModalHandler),this.tip&&o.default(this.tip).remove(),this._isEnabled=null,this._timeout=null,this._hoverState=null,this._activeTrigger=null,this._popper&&this._popper.destroy(),this._popper=null,this.element=null,this.config=null,this.tip=null},e.show=function(){var t=this;if("none"===o.default(this.element).css("display"))throw new Error("Please use show on visible elements");var e=o.default.Event(this.constructor.Event.SHOW);if(this.isWithContent()&&this._isEnabled){o.default(this.element).trigger(e);var n=d.findShadowRoot(this.element),i=o.default.contains(null!==n?n:this.element.ownerDocument.documentElement,this.element);if(e.isDefaultPrevented()||!i)return;var s=this.getTipElement(),l=d.getUID(this.constructor.NAME);s.setAttribute("id",l),this.element.setAttribute("aria-describedby",l),this.setContent(),this.config.animation&&o.default(s).addClass("fade");var r="function"==typeof this.config.placement?this.config.placement.call(this,s,this.element):this.config.placement,u=this._getAttachment(r);this.addAttachmentClass(u);var f=this._getContainer();o.default(s).data(this.constructor.DATA_KEY,this),o.default.contains(this.element.ownerDocument.documentElement,this.tip)||o.default(s).appendTo(f),o.default(this.element).trigger(this.constructor.Event.INSERTED),this._popper=new a.default(this.element,s,this._getPopperConfig(u)),o.default(s).addClass("show"),"ontouchstart"in document.documentElement&&o.default(document.body).children().on("mouseover",null,o.default.noop);var c=function(){t.config.animation&&t._fixTransition();var e=t._hoverState;t._hoverState=null,o.default(t.element).trigger(t.constructor.Event.SHOWN),"out"===e&&t._leave(null,t)};if(o.default(this.tip).hasClass("fade")){var h=d.getTransitionDurationFromElement(this.tip);o.default(this.tip).one(d.TRANSITION_END,c).emulateTransitionEnd(h)}else c()}},e.hide=function(t){var e=this,n=this.getTipElement(),i=o.default.Event(this.constructor.Event.HIDE),a=function(){"show"!==e._hoverState&&n.parentNode&&n.parentNode.removeChild(n),e._cleanTipClass(),e.element.removeAttribute("aria-describedby"),o.default(e.element).trigger(e.constructor.Event.HIDDEN),null!==e._popper&&e._popper.destroy(),t&&t()};if(o.default(this.element).trigger(i),!i.isDefaultPrevented()){if(o.default(n).removeClass("show"),"ontouchstart"in document.documentElement&&o.default(document.body).children().off("mouseover",null,o.default.noop),this._activeTrigger.click=!1,this._activeTrigger.focus=!1,this._activeTrigger.hover=!1,o.default(this.tip).hasClass("fade")){var s=d.getTransitionDurationFromElement(n);o.default(n).one(d.TRANSITION_END,a).emulateTransitionEnd(s)}else a();this._hoverState=""}},e.update=function(){null!==this._popper&&this._popper.scheduleUpdate()},e.isWithContent=function(){return Boolean(this.getTitle())},e.addAttachmentClass=function(t){o.default(this.getTipElement()).addClass("bs-tooltip-"+t)},e.getTipElement=function(){return this.tip=this.tip||o.default(this.config.template)[0],this.tip},e.setContent=function(){var t=this.getTipElement();this.setElementContent(o.default(t.querySelectorAll(".tooltip-inner")),this.getTitle()),o.default(t).removeClass("fade show")},e.setElementContent=function(t,e){"object"!=typeof e||!e.nodeType&&!e.jquery?this.config.html?(this.config.sanitize&&(e=U(e,this.config.whiteList,this.config.sanitizeFn)),t.html(e)):t.text(e):this.config.html?o.default(e).parent().is(t)||t.empty().append(e):t.text(o.default(e).text())},e.getTitle=function(){var t=this.element.getAttribute("data-original-title");return t||(t="function"==typeof this.config.title?this.config.title.call(this.element):this.config.title),t},e._getPopperConfig=function(t){var e=this;return r({},{placement:t,modifiers:{offset:this._getOffset(),flip:{behavior:this.config.fallbackPlacement},arrow:{element:".arrow"},preventOverflow:{boundariesElement:this.config.boundary}},onCreate:function(t){t.originalPlacement!==t.placement&&e._handlePopperPlacementChange(t)},onUpdate:function(t){return e._handlePopperPlacementChange(t)}},this.config.popperConfig)},e._getOffset=function(){var t=this,e={};return"function"==typeof this.config.offset?e.fn=function(e){return e.offsets=r({},e.offsets,t.config.offset(e.offsets,t.element)||{}),e}:e.offset=this.config.offset,e},e._getContainer=function(){return!1===this.config.container?document.body:d.isElement(this.config.container)?o.default(this.config.container):o.default(document).find(this.config.container)},e._getAttachment=function(t){return X[t.toUpperCase()]},e._setListeners=function(){var t=this;this.config.trigger.split(" ").forEach((function(e){if("click"===e)o.default(t.element).on(t.constructor.Event.CLICK,t.config.selector,(function(e){return t.toggle(e)}));else if("manual"!==e){var n="hover"===e?t.constructor.Event.MOUSEENTER:t.constructor.Event.FOCUSIN,i="hover"===e?t.constructor.Event.MOUSELEAVE:t.constructor.Event.FOCUSOUT;o.default(t.element).on(n,t.config.selector,(function(e){return t._enter(e)})).on(i,t.config.selector,(function(e){return t._leave(e)}))}})),this._hideModalHandler=function(){t.element&&t.hide()},o.default(this.element).closest(".modal").on("hide.bs.modal",this._hideModalHandler),this.config.selector?this.config=r({},this.config,{trigger:"manual",selector:""}):this._fixTitle()},e._fixTitle=function(){var t=typeof this.element.getAttribute("data-original-title");(this.element.getAttribute("title")||"string"!==t)&&(this.element.setAttribute("data-original-title",this.element.getAttribute("title")||""),this.element.setAttribute("title",""))},e._enter=function(t,e){var n=this.constructor.DATA_KEY;(e=e||o.default(t.currentTarget).data(n))||(e=new this.constructor(t.currentTarget,this._getDelegateConfig()),o.default(t.currentTarget).data(n,e)),t&&(e._activeTrigger["focusin"===t.type?"focus":"hover"]=!0),o.default(e.getTipElement()).hasClass("show")||"show"===e._hoverState?e._hoverState="show":(clearTimeout(e._timeout),e._hoverState="show",e.config.delay&&e.config.delay.show?e._timeout=setTimeout((function(){"show"===e._hoverState&&e.show()}),e.config.delay.show):e.show())},e._leave=function(t,e){var n=this.constructor.DATA_KEY;(e=e||o.default(t.currentTarget).data(n))||(e=new this.constructor(t.currentTarget,this._getDelegateConfig()),o.default(t.currentTarget).data(n,e)),t&&(e._activeTrigger["focusout"===t.type?"focus":"hover"]=!1),e._isWithActiveTrigger()||(clearTimeout(e._timeout),e._hoverState="out",e.config.delay&&e.config.delay.hide?e._timeout=setTimeout((function(){"out"===e._hoverState&&e.hide()}),e.config.delay.hide):e.hide())},e._isWithActiveTrigger=function(){for(var t in this._activeTrigger)if(this._activeTrigger[t])return!0;return!1},e._getConfig=function(t){var e=o.default(this.element).data();return Object.keys(e).forEach((function(t){-1!==z.indexOf(t)&&delete e[t]})),"number"==typeof(t=r({},this.constructor.Default,e,"object"==typeof t&&t?t:{})).delay&&(t.delay={show:t.delay,hide:t.delay}),"number"==typeof t.title&&(t.title=t.title.toString()),"number"==typeof t.content&&(t.content=t.content.toString()),d.typeCheckConfig(M,t,this.constructor.DefaultType),t.sanitize&&(t.template=U(t.template,t.whiteList,t.sanitizeFn)),t},e._getDelegateConfig=function(){var t={};if(this.config)for(var e in this.config)this.constructor.Default[e]!==this.config[e]&&(t[e]=this.config[e]);return t},e._cleanTipClass=function(){var t=o.default(this.getTipElement()),e=t.attr("class").match(V);null!==e&&e.length&&t.removeClass(e.join(""))},e._handlePopperPlacementChange=function(t){this.tip=t.instance.popper,this._cleanTipClass(),this.addAttachmentClass(this._getAttachment(t.placement))},e._fixTransition=function(){var t=this.getTipElement(),e=this.config.animation;null===t.getAttribute("x-placement")&&(o.default(t).removeClass("fade"),this.config.animation=!1,this.hide(),this.show(),this.config.animation=e)},t._jQueryInterface=function(e){return this.each((function(){var n=o.default(this),i=n.data("bs.tooltip"),a="object"==typeof e&&e;if((i||!/dispose|hide/.test(e))&&(i||(i=new t(this,a),n.data("bs.tooltip",i)),"string"==typeof e)){if("undefined"==typeof i[e])throw new TypeError('No method named "'+e+'"');i[e]()}}))},l(t,null,[{key:"VERSION",get:function(){return"4.5.3"}},{key:"Default",get:function(){return Y}},{key:"NAME",get:function(){return M}},{key:"DATA_KEY",get:function(){return"bs.tooltip"}},{key:"Event",get:function(){return $}},{key:"EVENT_KEY",get:function(){return".bs.tooltip"}},{key:"DefaultType",get:function(){return K}}]),t}();o.default.fn[M]=J._jQueryInterface,o.default.fn[M].Constructor=J,o.default.fn[M].noConflict=function(){return o.default.fn[M]=W,J._jQueryInterface};var G="popover",Z=o.default.fn[G],tt=new RegExp("(^|\\s)bs-popover\\S+","g"),et=r({},J.Default,{placement:"right",trigger:"click",content:"",template:'<div class="popover" role="tooltip"><div class="arrow"></div><h3 class="popover-header"></h3><div class="popover-body"></div></div>'}),nt=r({},J.DefaultType,{content:"(string|element|function)"}),it={HIDE:"hide.bs.popover",HIDDEN:"hidden.bs.popover",SHOW:"show.bs.popover",SHOWN:"shown.bs.popover",INSERTED:"inserted.bs.popover",CLICK:"click.bs.popover",FOCUSIN:"focusin.bs.popover",FOCUSOUT:"focusout.bs.popover",MOUSEENTER:"mouseenter.bs.popover",MOUSELEAVE:"mouseleave.bs.popover"},ot=function(t){var e,n;function i(){return t.apply(this,arguments)||this}n=t,(e=i).prototype=Object.create(n.prototype),e.prototype.constructor=e,e.__proto__=n;var a=i.prototype;return a.isWithContent=function(){return this.getTitle()||this._getContent()},a.addAttachmentClass=function(t){o.default(this.getTipElement()).addClass("bs-popover-"+t)},a.getTipElement=function(){return this.tip=this.tip||o.default(this.config.template)[0],this.tip},a.setContent=function(){var t=o.default(this.getTipElement());this.setElementContent(t.find(".popover-header"),this.getTitle());var e=this._getContent();"function"==typeof e&&(e=e.call(this.element)),this.setElementContent(t.find(".popover-body"),e),t.removeClass("fade show")},a._getContent=function(){return this.element.getAttribute("data-content")||this.config.content},a._cleanTipClass=function(){var t=o.default(this.getTipElement()),e=t.attr("class").match(tt);null!==e&&e.length>0&&t.removeClass(e.join(""))},i._jQueryInterface=function(t){return this.each((function(){var e=o.default(this).data("bs.popover"),n="object"==typeof t?t:null;if((e||!/dispose|hide/.test(t))&&(e||(e=new i(this,n),o.default(this).data("bs.popover",e)),"string"==typeof t)){if("undefined"==typeof e[t])throw new TypeError('No method named "'+t+'"');e[t]()}}))},l(i,null,[{key:"VERSION",get:function(){return"4.5.3"}},{key:"Default",get:function(){return et}},{key:"NAME",get:function(){return G}},{key:"DATA_KEY",get:function(){return"bs.popover"}},{key:"Event",get:function(){return it}},{key:"EVENT_KEY",get:function(){return".bs.popover"}},{key:"DefaultType",get:function(){return nt}}]),i}(J);o.default.fn[G]=ot._jQueryInterface,o.default.fn[G].Constructor=ot,o.default.fn[G].noConflict=function(){return o.default.fn[G]=Z,ot._jQueryInterface};var at="scrollspy",st=o.default.fn[at],lt={offset:10,method:"auto",target:""},rt={offset:"number",method:"string",target:"(string|element)"},ut=function(){function t(t,e){var n=this;this._element=t,this._scrollElement="BODY"===t.tagName?window:t,this._config=this._getConfig(e),this._selector=this._config.target+" .nav-link,"+this._config.target+" .list-group-item,"+this._config.target+" .dropdown-item",this._offsets=[],this._targets=[],this._activeTarget=null,this._scrollHeight=0,o.default(this._scrollElement).on("scroll.bs.scrollspy",(function(t){return n._process(t)})),this.refresh(),this._process()}var e=t.prototype;return e.refresh=function(){var t=this,e=this._scrollElement===this._scrollElement.window?"offset":"position",n="auto"===this._config.method?e:this._config.method,i="position"===n?this._getScrollTop():0;this._offsets=[],this._targets=[],this._scrollHeight=this._getScrollHeight(),[].slice.call(document.querySelectorAll(this._selector)).map((function(t){var e,a=d.getSelectorFromElement(t);if(a&&(e=document.querySelector(a)),e){var s=e.getBoundingClientRect();if(s.width||s.height)return[o.default(e)[n]().top+i,a]}return null})).filter((function(t){return t})).sort((function(t,e){return t[0]-e[0]})).forEach((function(e){t._offsets.push(e[0]),t._targets.push(e[1])}))},e.dispose=function(){o.default.removeData(this._element,"bs.scrollspy"),o.default(this._scrollElement).off(".bs.scrollspy"),this._element=null,this._scrollElement=null,this._config=null,this._selector=null,this._offsets=null,this._targets=null,this._activeTarget=null,this._scrollHeight=null},e._getConfig=function(t){if("string"!=typeof(t=r({},lt,"object"==typeof t&&t?t:{})).target&&d.isElement(t.target)){var e=o.default(t.target).attr("id");e||(e=d.getUID(at),o.default(t.target).attr("id",e)),t.target="#"+e}return d.typeCheckConfig(at,t,rt),t},e._getScrollTop=function(){return this._scrollElement===window?this._scrollElement.pageYOffset:this._scrollElement.scrollTop},e._getScrollHeight=function(){return this._scrollElement.scrollHeight||Math.max(document.body.scrollHeight,document.documentElement.scrollHeight)},e._getOffsetHeight=function(){return this._scrollElement===window?window.innerHeight:this._scrollElement.getBoundingClientRect().height},e._process=function(){var t=this._getScrollTop()+this._config.offset,e=this._getScrollHeight(),n=this._config.offset+e-this._getOffsetHeight();if(this._scrollHeight!==e&&this.refresh(),t>=n){var i=this._targets[this._targets.length-1];this._activeTarget!==i&&this._activate(i)}else{if(this._activeTarget&&t<this._offsets[0]&&this._offsets[0]>0)return this._activeTarget=null,void this._clear();for(var o=this._offsets.length;o--;){this._activeTarget!==this._targets[o]&&t>=this._offsets[o]&&("undefined"==typeof this._offsets[o+1]||t<this._offsets[o+1])&&this._activate(this._targets[o])}}},e._activate=function(t){this._activeTarget=t,this._clear();var e=this._selector.split(",").map((function(e){return e+'[data-target="'+t+'"],'+e+'[href="'+t+'"]'})),n=o.default([].slice.call(document.querySelectorAll(e.join(","))));n.hasClass("dropdown-item")?(n.closest(".dropdown").find(".dropdown-toggle").addClass("active"),n.addClass("active")):(n.addClass("active"),n.parents(".nav, .list-group").prev(".nav-link, .list-group-item").addClass("active"),n.parents(".nav, .list-group").prev(".nav-item").children(".nav-link").addClass("active")),o.default(this._scrollElement).trigger("activate.bs.scrollspy",{relatedTarget:t})},e._clear=function(){[].slice.call(document.querySelectorAll(this._selector)).filter((function(t){return t.classList.contains("active")})).forEach((function(t){return t.classList.remove("active")}))},t._jQueryInterface=function(e){return this.each((function(){var n=o.default(this).data("bs.scrollspy");if(n||(n=new t(this,"object"==typeof e&&e),o.default(this).data("bs.scrollspy",n)),"string"==typeof e){if("undefined"==typeof n[e])throw new TypeError('No method named "'+e+'"');n[e]()}}))},l(t,null,[{key:"VERSION",get:function(){return"4.5.3"}},{key:"Default",get:function(){return lt}}]),t}();o.default(window).on("load.bs.scrollspy.data-api",(function(){for(var t=[].slice.call(document.querySelectorAll('[data-spy="scroll"]')),e=t.length;e--;){var n=o.default(t[e]);ut._jQueryInterface.call(n,n.data())}})),o.default.fn[at]=ut._jQueryInterface,o.default.fn[at].Constructor=ut,o.default.fn[at].noConflict=function(){return o.default.fn[at]=st,ut._jQueryInterface};var dt=o.default.fn.tab,ft=function(){function t(t){this._element=t}var e=t.prototype;return e.show=function(){var t=this;if(!(this._element.parentNode&&this._element.parentNode.nodeType===Node.ELEMENT_NODE&&o.default(this._element).hasClass("active")||o.default(this._element).hasClass("disabled"))){var e,n,i=o.default(this._element).closest(".nav, .list-group")[0],a=d.getSelectorFromElement(this._element);if(i){var s="UL"===i.nodeName||"OL"===i.nodeName?"> li > .active":".active";n=(n=o.default.makeArray(o.default(i).find(s)))[n.length-1]}var l=o.default.Event("hide.bs.tab",{relatedTarget:this._element}),r=o.default.Event("show.bs.tab",{relatedTarget:n});if(n&&o.default(n).trigger(l),o.default(this._element).trigger(r),!r.isDefaultPrevented()&&!l.isDefaultPrevented()){a&&(e=document.querySelector(a)),this._activate(this._element,i);var u=function(){var e=o.default.Event("hidden.bs.tab",{relatedTarget:t._element}),i=o.default.Event("shown.bs.tab",{relatedTarget:n});o.default(n).trigger(e),o.default(t._element).trigger(i)};e?this._activate(e,e.parentNode,u):u()}}},e.dispose=function(){o.default.removeData(this._element,"bs.tab"),this._element=null},e._activate=function(t,e,n){var i=this,a=(!e||"UL"!==e.nodeName&&"OL"!==e.nodeName?o.default(e).children(".active"):o.default(e).find("> li > .active"))[0],s=n&&a&&o.default(a).hasClass("fade"),l=function(){return i._transitionComplete(t,a,n)};if(a&&s){var r=d.getTransitionDurationFromElement(a);o.default(a).removeClass("show").one(d.TRANSITION_END,l).emulateTransitionEnd(r)}else l()},e._transitionComplete=function(t,e,n){if(e){o.default(e).removeClass("active");var i=o.default(e.parentNode).find("> .dropdown-menu .active")[0];i&&o.default(i).removeClass("active"),"tab"===e.getAttribute("role")&&e.setAttribute("aria-selected",!1)}if(o.default(t).addClass("active"),"tab"===t.getAttribute("role")&&t.setAttribute("aria-selected",!0),d.reflow(t),t.classList.contains("fade")&&t.classList.add("show"),t.parentNode&&o.default(t.parentNode).hasClass("dropdown-menu")){var a=o.default(t).closest(".dropdown")[0];if(a){var s=[].slice.call(a.querySelectorAll(".dropdown-toggle"));o.default(s).addClass("active")}t.setAttribute("aria-expanded",!0)}n&&n()},t._jQueryInterface=function(e){return this.each((function(){var n=o.default(this),i=n.data("bs.tab");if(i||(i=new t(this),n.data("bs.tab",i)),"string"==typeof e){if("undefined"==typeof i[e])throw new TypeError('No method named "'+e+'"');i[e]()}}))},l(t,null,[{key:"VERSION",get:function(){return"4.5.3"}}]),t}();o.default(document).on("click.bs.tab.data-api",'[data-toggle="tab"], [data-toggle="pill"], [data-toggle="list"]',(function(t){t.preventDefault(),ft._jQueryInterface.call(o.default(this),"show")})),o.default.fn.tab=ft._jQueryInterface,o.default.fn.tab.Constructor=ft,o.default.fn.tab.noConflict=function(){return o.default.fn.tab=dt,ft._jQueryInterface};var ct=o.default.fn.toast,ht={animation:"boolean",autohide:"boolean",delay:"number"},gt={animation:!0,autohide:!0,delay:500},mt=function(){function t(t,e){this._element=t,this._config=this._getConfig(e),this._timeout=null,this._setListeners()}var e=t.prototype;return e.show=function(){var t=this,e=o.default.Event("show.bs.toast");if(o.default(this._element).trigger(e),!e.isDefaultPrevented()){this._clearTimeout(),this._config.animation&&this._element.classList.add("fade");var n=function(){t._element.classList.remove("showing"),t._element.classList.add("show"),o.default(t._element).trigger("shown.bs.toast"),t._config.autohide&&(t._timeout=setTimeout((function(){t.hide()}),t._config.delay))};if(this._element.classList.remove("hide"),d.reflow(this._element),this._element.classList.add("showing"),this._config.animation){var i=d.getTransitionDurationFromElement(this._element);o.default(this._element).one(d.TRANSITION_END,n).emulateTransitionEnd(i)}else n()}},e.hide=function(){if(this._element.classList.contains("show")){var t=o.default.Event("hide.bs.toast");o.default(this._element).trigger(t),t.isDefaultPrevented()||this._close()}},e.dispose=function(){this._clearTimeout(),this._element.classList.contains("show")&&this._element.classList.remove("show"),o.default(this._element).off("click.dismiss.bs.toast"),o.default.removeData(this._element,"bs.toast"),this._element=null,this._config=null},e._getConfig=function(t){return t=r({},gt,o.default(this._element).data(),"object"==typeof t&&t?t:{}),d.typeCheckConfig("toast",t,this.constructor.DefaultType),t},e._setListeners=function(){var t=this;o.default(this._element).on("click.dismiss.bs.toast",'[data-dismiss="toast"]',(function(){return t.hide()}))},e._close=function(){var t=this,e=function(){t._element.classList.add("hide"),o.default(t._element).trigger("hidden.bs.toast")};if(this._element.classList.remove("show"),this._config.animation){var n=d.getTransitionDurationFromElement(this._element);o.default(this._element).one(d.TRANSITION_END,e).emulateTransitionEnd(n)}else e()},e._clearTimeout=function(){clearTimeout(this._timeout),this._timeout=null},t._jQueryInterface=function(e){return this.each((function(){var n=o.default(this),i=n.data("bs.toast");if(i||(i=new t(this,"object"==typeof e&&e),n.data("bs.toast",i)),"string"==typeof e){if("undefined"==typeof i[e])throw new TypeError('No method named "'+e+'"');i[e](this)}}))},l(t,null,[{key:"VERSION",get:function(){return"4.5.3"}},{key:"DefaultType",get:function(){return ht}},{key:"Default",get:function(){return gt}}]),t}();o.default.fn.toast=mt._jQueryInterface,o.default.fn.toast.Constructor=mt,o.default.fn.toast.noConflict=function(){return o.default.fn.toast=ct,mt._jQueryInterface},t.Alert=h,t.Button=m,t.Carousel=w,t.Collapse=D,t.Dropdown=x,t.Modal=q,t.Popover=ot,t.Scrollspy=ut,t.Tab=ft,t.Toast=mt,t.Tooltip=J,t.Util=d,Object.defineProperty(t,"__esModule",{value:!0})}));
7
- //# sourceMappingURL=bootstrap.min.js.map
 
 
 
 
 
 
 
resources/js/global-plugin.js CHANGED
@@ -45,9 +45,23 @@ var iCWP_WPSF_ParseAjaxResponse = new function () {
45
  parsed = JSON.parse( raw );
46
  }
47
  catch ( e ) {
48
- parsed = JSON.parse(
49
- raw.substring( raw.indexOf( '{' ), raw.lastIndexOf( '}' ) + 1 )
50
- );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
51
  }
52
  return parsed;
53
  };
@@ -57,6 +71,8 @@ var iCWP_WPSF_StandardAjax = new function () {
57
  this.send_ajax_req = function ( reqData ) {
58
  iCWP_WPSF_BodyOverlay.show();
59
 
 
 
60
  jQuery.ajax( {
61
  type: "POST",
62
  url: ajaxurl,
@@ -81,6 +97,8 @@ var iCWP_WPSF_StandardAjax = new function () {
81
  iCWP_WPSF_BodyOverlay.hide();
82
  }
83
  }
 
 
84
  } )
85
  };
86
  }();
45
  parsed = JSON.parse( raw );
46
  }
47
  catch ( e ) {
48
+ var openJsonTag = '##APTO_OPEN##';
49
+ var closeJsonTag = '##APTO_CLOSE##';
50
+ var start = 0;
51
+ var end = 0;
52
+
53
+ if ( raw.indexOf( openJsonTag ) >= 0 ) {
54
+ start = raw.indexOf( openJsonTag ) + openJsonTag.length;
55
+ end = raw.indexOf( closeJsonTag );
56
+ try {
57
+ parsed = JSON.parse( raw.substring( start, end ) );
58
+ }
59
+ catch ( e ) {
60
+ start = raw.indexOf( '{' );
61
+ end = raw.lastIndexOf( '}' ) + 1;
62
+ parsed = JSON.parse( raw.substring( start, end ) );
63
+ }
64
+ }
65
  }
66
  return parsed;
67
  };
71
  this.send_ajax_req = function ( reqData ) {
72
  iCWP_WPSF_BodyOverlay.show();
73
 
74
+ reqData.apto_wrap_response = 1;
75
+
76
  jQuery.ajax( {
77
  type: "POST",
78
  url: ajaxurl,
97
  iCWP_WPSF_BodyOverlay.hide();
98
  }
99
  }
100
+ } ).fail( function () {
101
+ alert( 'Something went wrong with the request - it was either blocked or there was an error.' );
102
  } )
103
  };
104
  }();
resources/js/plugin.js CHANGED
@@ -126,6 +126,69 @@ var iCWP_WPSF_OptionsFormSubmit = new function () {
126
  aAjaxReqParams = aParams;
127
  };
128
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
129
  var submitOptionsForm = function ( event ) {
130
  iCWP_WPSF_BodyOverlay.show();
131
 
@@ -151,67 +214,7 @@ var iCWP_WPSF_OptionsFormSubmit = new function () {
151
  } );
152
 
153
  if ( $bPasswordsReady ) {
154
- /**
155
- * First try with base64 and failover to lz-string upon abject failure.
156
- * This works around mod_security rules that even unpack b64 encoded params and look
157
- * for patterns within them.
158
- */
159
- var aReq = jQuery.extend(
160
- aAjaxReqParams,
161
- {
162
- 'form_params': Base64.encode( $oForm.serialize() ),
163
- 'enc_params': 'b64'
164
- }
165
- );
166
-
167
- jQuery.post( ajaxurl, aReq,
168
- function ( oResponse ) {
169
- var sMessage;
170
- if ( oResponse === null || typeof oResponse.data === 'undefined'
171
- || typeof oResponse.data.message === 'undefined' ) {
172
- sMessage = oResponse.success ? 'Success' : 'Failure';
173
- }
174
- else {
175
- sMessage = oResponse.data.message;
176
- }
177
- iCWP_WPSF_Toaster.showMessage( sMessage, oResponse.success );
178
- // iCWP_WPSF_Growl.showMessage( sMessage, oResponse.success );
179
- }
180
- ).fail(
181
- function () {
182
- iCWP_WPSF_Toaster.showMessage( 'The request was blocked. Retrying an alternative...', false );
183
- aReq = jQuery.extend(
184
- aAjaxReqParams,
185
- {
186
- 'form_params': Base64.encode( LZString.compress( $oForm.serialize() ) ),
187
- 'enc_params': 'lz-string'
188
- }
189
- );
190
- jQuery.post( ajaxurl, aReq,
191
- function ( oResponse ) {
192
- var sMessage;
193
- if ( oResponse === null || typeof oResponse.data === 'undefined'
194
- || typeof oResponse.data.message === 'undefined' ) {
195
- sMessage = oResponse.success ? 'Success' : 'Failure';
196
- }
197
- else {
198
- sMessage = oResponse.data.message;
199
- }
200
- iCWP_WPSF_Toaster.showMessage( sMessage, oResponse.success );
201
- }
202
- )
203
- }
204
- ).always( function () {
205
- bRequestCurrentlyRunning = false;
206
- setTimeout( function () {
207
- location.reload();
208
- }, 1000 );
209
- }
210
- );
211
- }
212
- else {
213
- bRequestCurrentlyRunning = false;
214
- iCWP_WPSF_BodyOverlay.hide();
215
  }
216
  };
217
 
126
  aAjaxReqParams = aParams;
127
  };
128
 
129
+ /**
130
+ * First try with base64 and failover to lz-string upon abject failure.
131
+ * This works around mod_security rules that even unpack b64 encoded params and look
132
+ * for patterns within them.
133
+ */
134
+ var sendForm = function ( $oForm, useCompression = false ) {
135
+
136
+ let formData = $oForm.serialize();
137
+ if ( useCompression ) {
138
+ formData = LZString.compress( formData );
139
+ }
140
+
141
+ let reqs = jQuery.extend(
142
+ aAjaxReqParams,
143
+ {
144
+ 'form_params': Base64.encode( formData ),
145
+ 'enc_params': useCompression ? 'lz-string' : 'b64',
146
+ 'apto_wrap_response': 1
147
+ }
148
+ );
149
+
150
+ jQuery.ajax(
151
+ {
152
+ type: "POST",
153
+ url: ajaxurl,
154
+ data: reqs,
155
+ dataType: "text",
156
+ success: function ( raw ) {
157
+ handleResponse( raw );
158
+ },
159
+ }
160
+ ).fail( function () {
161
+ alert( 'fail()' );
162
+ if ( useCompression ) {
163
+ handleResponse( raw );
164
+ }
165
+ else {
166
+ iCWP_WPSF_Toaster.showMessage( 'The request was blocked. Retrying an alternative...', false );
167
+ sendForm( $oForm, true );
168
+ }
169
+
170
+ } ).always( function () {
171
+ bRequestCurrentlyRunning = false;
172
+ setTimeout( function () {
173
+ location.reload();
174
+ }, 1000 );
175
+ } );
176
+
177
+ };
178
+
179
+ var handleResponse = function ( raw ) {
180
+ let response = iCWP_WPSF_ParseAjaxResponse.parseIt( raw );
181
+ let msg;
182
+ if ( response === null || typeof response.data === 'undefined'
183
+ || typeof response.data.message === 'undefined' ) {
184
+ msg = response.success ? 'Success' : 'Failure';
185
+ }
186
+ else {
187
+ msg = response.data.message;
188
+ }
189
+ iCWP_WPSF_Toaster.showMessage( msg, response.success );
190
+ };
191
+
192
  var submitOptionsForm = function ( event ) {
193
  iCWP_WPSF_BodyOverlay.show();
194
 
214
  } );
215
 
216
  if ( $bPasswordsReady ) {
217
+ sendForm( $oForm, false );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
218
  }
219
  };
220
 
resources/js/{shield-scans.js → shield/scans.js} RENAMED
File without changes
resources/js/{shield-tables.js → shield/tables.js} RENAMED
File without changes
resources/js/{shield-u2f-admin.js → shield/u2f-admin.js} RENAMED
@@ -27,7 +27,6 @@ if ( typeof icwp_wpsf_vars_u2f !== 'undefined' ) {
27
  .css( 'color', 'green' );
28
  } )
29
  .catch( function ( response ) {
30
- console.log( response );
31
  $oU2fStatus.text( icwp_wpsf_vars_u2f.strings.failed );
32
  $oU2fStatus.css( 'font-weight', 'bolder' )
33
  .css( 'color', 'red' );
@@ -44,15 +43,13 @@ if ( typeof icwp_wpsf_vars_u2f !== 'undefined' ) {
44
 
45
  } );
46
 
47
-
48
  jQuery.fn.icwpWpsfProfileU2f = function () {
49
 
50
  var initialise = function () {
51
  jQuery( document ).ready( function () {
52
  jQuery( 'a.icwpWpsf-U2FRemove' ).on( 'click', function ( evt ) {
53
  evt.preventDefault();
54
- icwp_wpsf_vars_u2f.ajax.u2f_remove.u2fid =
55
- jQuery( evt.target ).data( 'u2fid' );
56
  iCWP_WPSF_StandardAjax.send_ajax_req( icwp_wpsf_vars_u2f.ajax.u2f_remove );
57
  return false;
58
  } )
27
  .css( 'color', 'green' );
28
  } )
29
  .catch( function ( response ) {
 
30
  $oU2fStatus.text( icwp_wpsf_vars_u2f.strings.failed );
31
  $oU2fStatus.css( 'font-weight', 'bolder' )
32
  .css( 'color', 'red' );
43
 
44
  } );
45
 
 
46
  jQuery.fn.icwpWpsfProfileU2f = function () {
47
 
48
  var initialise = function () {
49
  jQuery( document ).ready( function () {
50
  jQuery( 'a.icwpWpsf-U2FRemove' ).on( 'click', function ( evt ) {
51
  evt.preventDefault();
52
+ icwp_wpsf_vars_u2f.ajax.u2f_remove.u2fid = jQuery( evt.currentTarget ).data( 'u2fid' );
 
53
  iCWP_WPSF_StandardAjax.send_ajax_req( icwp_wpsf_vars_u2f.ajax.u2f_remove );
54
  return false;
55
  } )
resources/js/{shield-userprofile.js → shield/userprofile.js} RENAMED
@@ -6,7 +6,7 @@ if ( typeof icwp_wpsf_vars_profileyubikey !== 'undefined' ) {
6
  jQuery( 'a.icwpWpsf-YubikeyRemove' ).on( 'click', function ( evt ) {
7
  evt.preventDefault();
8
  icwp_wpsf_vars_profileyubikey.yubikey_remove.yubikeyid =
9
- jQuery( evt.target ).data( 'yubikeyid' );
10
  iCWP_WPSF_StandardAjax.send_ajax_req( icwp_wpsf_vars_profileyubikey.yubikey_remove );
11
  return false;
12
  } )
6
  jQuery( 'a.icwpWpsf-YubikeyRemove' ).on( 'click', function ( evt ) {
7
  evt.preventDefault();
8
  icwp_wpsf_vars_profileyubikey.yubikey_remove.yubikeyid =
9
+ jQuery( evt.currentTarget ).data( 'yubikeyid' );
10
  iCWP_WPSF_StandardAjax.send_ajax_req( icwp_wpsf_vars_profileyubikey.yubikey_remove );
11
  return false;
12
  } )
src/config/feature-hack_protect.php CHANGED
@@ -417,7 +417,7 @@
417
  "file": "varchar(256) NOT NULL COMMENT 'File Path relative to ABSPATH'",
418
  "hash_original": "varchar(40) NOT NULL COMMENT 'SHA1 File Hash Original'",
419
  "hash_current": "varchar(40) NOT NULL COMMENT 'SHA1 File Hash Current'",
420
- "content": "blob COMMENT 'Content'",
421
  "public_key_id": "TINYINT(2) UNSIGNED NOT NULL COMMENT 'Public Key ID'",
422
  "detected_at": "int(15) UNSIGNED NOT NULL DEFAULT 0 COMMENT 'TS Change Last Detected'",
423
  "reverted_at": "int(15) UNSIGNED NOT NULL DEFAULT 0 COMMENT 'TS Reverted To Backup'",
417
  "file": "varchar(256) NOT NULL COMMENT 'File Path relative to ABSPATH'",
418
  "hash_original": "varchar(40) NOT NULL COMMENT 'SHA1 File Hash Original'",
419
  "hash_current": "varchar(40) NOT NULL COMMENT 'SHA1 File Hash Current'",
420
+ "content": "MEDIUMBLOB COMMENT 'Content'",
421
  "public_key_id": "TINYINT(2) UNSIGNED NOT NULL COMMENT 'Public Key ID'",
422
  "detected_at": "int(15) UNSIGNED NOT NULL DEFAULT 0 COMMENT 'TS Change Last Detected'",
423
  "reverted_at": "int(15) UNSIGNED NOT NULL DEFAULT 0 COMMENT 'TS Reverted To Backup'",
src/config/feature-headers.php CHANGED
@@ -167,6 +167,7 @@
167
  {
168
  "key": "enable_x_content_security_policy",
169
  "section": "section_content_security_policy",
 
170
  "default": "N",
171
  "type": "checkbox",
172
  "link_info": "https://shsec.io/7d",
@@ -175,75 +176,6 @@
175
  "summary": "Enable (or Disable) The Content Security Policy module",
176
  "description": "Allows for permission and restriction of all resources loaded on your site."
177
  },
178
- {
179
- "key": "xcsp_self",
180
- "section": "section_content_security_policy",
181
- "default": "Y",
182
- "type": "checkbox",
183
- "link_info": "",
184
- "link_blog": "",
185
- "name": "Self",
186
- "summary": "Allow 'self' Directive",
187
- "description": "Using 'self' is generally recommended. It essentially means that resources from your own host:protocol are permitted."
188
- },
189
- {
190
- "key": "xcsp_inline",
191
- "section": "section_content_security_policy",
192
- "default": "Y",
193
- "type": "checkbox",
194
- "link_info": "",
195
- "link_blog": "",
196
- "name": "Inline Entities",
197
- "summary": "Allow Inline Scripts and CSS",
198
- "description": "Allows parsing of Javascript and CSS declared in-line in your html document."
199
- },
200
- {
201
- "key": "xcsp_data",
202
- "section": "section_content_security_policy",
203
- "default": "Y",
204
- "type": "checkbox",
205
- "link_info": "",
206
- "link_blog": "",
207
- "name": "Embedded Data",
208
- "summary": "Allow 'data:' Directives",
209
- "description": "Allows use of embedded data directives, most commonly used for images and fonts."
210
- },
211
- {
212
- "key": "xcsp_eval",
213
- "section": "section_content_security_policy",
214
- "default": "Y",
215
- "type": "checkbox",
216
- "link_info": "",
217
- "link_blog": "",
218
- "name": "Allow eval()",
219
- "summary": "Allow Javascript eval()",
220
- "description": "Permits the use of Javascript the eval() function."
221
- },
222
- {
223
- "key": "xcsp_https",
224
- "section": "section_content_security_policy",
225
- "default": "N",
226
- "type": "checkbox",
227
- "link_info": "",
228
- "link_blog": "",
229
- "name": "HTTPS",
230
- "summary": "HTTPS Resource Loading",
231
- "description": "Allows loading of any content provided over HTTPS."
232
- },
233
- {
234
- "key": "xcsp_hosts",
235
- "section": "section_content_security_policy",
236
- "sensitive": true,
237
- "default": [
238
- "*"
239
- ],
240
- "type": "array",
241
- "link_info": "https://shsec.io/ga",
242
- "link_blog": "",
243
- "name": "Permitted Hosts",
244
- "summary": "Permitted Hosts and Domains",
245
- "description": "You can explicitly state which hosts/domain from which content may be loaded. Take great care and test your site as you may block legitimate resources. If in-doubt, leave blank or use '*' only. Note: You can force only HTTPS for a given domain by prefixing it with 'https://'."
246
- },
247
  {
248
  "key": "xcsp_custom",
249
  "section": "section_content_security_policy",
167
  {
168
  "key": "enable_x_content_security_policy",
169
  "section": "section_content_security_policy",
170
+ "premium": true,
171
  "default": "N",
172
  "type": "checkbox",
173
  "link_info": "https://shsec.io/7d",
176
  "summary": "Enable (or Disable) The Content Security Policy module",
177
  "description": "Allows for permission and restriction of all resources loaded on your site."
178
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
179
  {
180
  "key": "xcsp_custom",
181
  "section": "section_content_security_policy",
src/config/feature-login_protect.php CHANGED
@@ -358,10 +358,7 @@
358
  "advanced": true,
359
  "premium": true,
360
  "type": "array",
361
- "default": [
362
- "form#ihc_login_form",
363
- "form#createuser"
364
- ],
365
  "link_info": "https://shsec.io/hg",
366
  "link_blog": "",
367
  "name": "AntiBot Forms",
358
  "advanced": true,
359
  "premium": true,
360
  "type": "array",
361
+ "default": [],
 
 
 
362
  "link_info": "https://shsec.io/hg",
363
  "link_blog": "",
364
  "name": "AntiBot Forms",
src/config/feature-user_management.php CHANGED
@@ -136,7 +136,7 @@
136
  "default": 48,
137
  "min": 0,
138
  "type": "integer",
139
- "link_info": "https://icontrolwp.freshdesk.com/support/solutions/articles/3000070590",
140
  "link_blog": "",
141
  "name": "Idle Timeout",
142
  "summary": "Specify How Many Hours After Inactivity To Automatically Logout User",
136
  "default": 48,
137
  "min": 0,
138
  "type": "integer",
139
+ "link_info": "https://support.getshieldsecurity.com/support/solutions/articles/3000070590",
140
  "link_blog": "",
141
  "name": "Idle Timeout",
142
  "summary": "Specify How Many Hours After Inactivity To Automatically Logout User",
src/features/admin_access_restriction.php DELETED
@@ -1,396 +0,0 @@
1
- <?php
2
-
3
- use FernleafSystems\Wordpress\Plugin\Shield;
4
- use FernleafSystems\Wordpress\Plugin\Shield\Modules\SecurityAdmin;
5
- use FernleafSystems\Wordpress\Services\Services;
6
-
7
- /**
8
- * @deprecated 10.1
9
- */
10
- class ICWP_WPSF_FeatureHandler_AdminAccessRestriction extends ICWP_WPSF_FeatureHandler_BaseWpsf {
11
-
12
- const HASH_DELETE = '32f68a60cef40faedbc6af20298c1a1e';
13
-
14
- /**
15
- * @var bool
16
- */
17
- private $bValidSecAdminRequest;
18
-
19
- /**
20
- * @var SecurityAdmin\Lib\WhiteLabel\ApplyLabels
21
- */
22
- private $oWhiteLabelController;
23
-
24
- protected function setupCustomHooks() {
25
- parent::setupCustomHooks();
26
- add_action( $this->prefix( 'pre_deactivate_plugin' ), [ $this, 'preDeactivatePlugin' ] );
27
- }
28
-
29
- public function getWhiteLabelController() :SecurityAdmin\Lib\WhiteLabel\ApplyLabels {
30
- if ( !$this->oWhiteLabelController instanceof SecurityAdmin\Lib\WhiteLabel\ApplyLabels ) {
31
- $this->oWhiteLabelController = ( new SecurityAdmin\Lib\WhiteLabel\ApplyLabels() )
32
- ->setMod( $this );
33
- }
34
- return $this->oWhiteLabelController;
35
- }
36
-
37
- /**
38
- * @return bool
39
- * @throws \Exception
40
- */
41
- protected function isReadyToExecute() {
42
- return $this->isEnabledSecurityAdmin() && parent::isReadyToExecute();
43
- }
44
-
45
- /**
46
- * No checking of admin capabilities in-case of infinite loop with
47
- * admin access caps check
48
- * @return bool
49
- */
50
- public function isRegisteredSecAdminUser() {
51
- /** @var SecurityAdmin\Options $opts */
52
- $opts = $this->getOptions();
53
- $sUser = Services::WpUsers()->getCurrentWpUsername();
54
- return !empty( $sUser ) && in_array( $sUser, $opts->getSecurityAdminUsers() );
55
- }
56
-
57
- protected function preProcessOptions() {
58
- /** @var SecurityAdmin\Options $opts */
59
- $opts = $this->getOptions();
60
-
61
- if ( $this->isValidSecAdminRequest() ) {
62
- $this->setSecurityAdminStatusOnOff( true );
63
- }
64
-
65
- // Verify whitelabel images
66
- if ( $this->isWlEnabled() ) {
67
- $aImages = [
68
- 'wl_menuiconurl',
69
- 'wl_dashboardlogourl',
70
- 'wl_login2fa_logourl',
71
- ];
72
- $oOpts = $this->getOptions();
73
- foreach ( $aImages as $sKey ) {
74
- if ( !Services::Data()->isValidWebUrl( $this->buildWlImageUrl( $sKey ) ) ) {
75
- $oOpts->resetOptToDefault( $sKey );
76
- }
77
- }
78
- }
79
-
80
- $opts->setOpt( 'sec_admin_users', $this->verifySecAdminUsers( $opts->getSecurityAdminUsers() ) );
81
- }
82
-
83
- /**
84
- * Ensures that all entries are valid users.
85
- * @param string[] $aSecUsers
86
- * @return string[]
87
- */
88
- private function verifySecAdminUsers( $aSecUsers ) {
89
- $oDP = Services::Data();
90
- $oWpUsers = Services::WpUsers();
91
- /** @var SecurityAdmin\Options $opts */
92
- $opts = $this->getOptions();
93
-
94
- $aFiltered = [];
95
- foreach ( $aSecUsers as $nCurrentKey => $sUsernameOrEmail ) {
96
- if ( $oDP->validEmail( $sUsernameOrEmail ) ) {
97
- $oUser = $oWpUsers->getUserByEmail( $sUsernameOrEmail );
98
- }
99
- else {
100
- $oUser = $oWpUsers->getUserByUsername( $sUsernameOrEmail );
101
- if ( is_null( $oUser ) && is_numeric( $sUsernameOrEmail ) ) {
102
- $oUser = $oWpUsers->getUserById( $sUsernameOrEmail );
103
- }
104
- }
105
-
106
- if ( $oUser instanceof WP_User && $oUser->ID > 0 && $oWpUsers->isUserAdmin( $oUser ) ) {
107
- $aFiltered[] = $oUser->user_login;
108
- }
109
- }
110
-
111
- // We now run a bit of a sanity check to ensure that the current user is
112
- // not adding users here that aren't themselves without a key to still gain access
113
- $oCurrent = $oWpUsers->getCurrentWpUser();
114
- if ( !empty( $aFiltered ) && !$opts->hasSecurityPIN() && !in_array( $oCurrent->user_login, $aFiltered ) ) {
115
- $aFiltered[] = $oCurrent->user_login;
116
- }
117
-
118
- natsort( $aFiltered );
119
- return array_unique( $aFiltered );
120
- }
121
-
122
- public function getSecAdminTimeout() :int {
123
- return (int)$this->getOptions()->getOpt( 'admin_access_timeout' )*MINUTE_IN_SECONDS;
124
- }
125
-
126
- /**
127
- * Only returns greater than 0 if you have a valid Sec admin session
128
- */
129
- public function getSecAdminTimeLeft() :int {
130
- $nLeft = 0;
131
- if ( $this->hasSession() ) {
132
-
133
- $nSecAdminAt = $this->getSession()->getSecAdminAt();
134
- if ( $this->isRegisteredSecAdminUser() ) {
135
- $nLeft = 0;
136
- }
137
- elseif ( $nSecAdminAt > 0 ) {
138
- $nLeft = $this->getSecAdminTimeout() - ( Services::Request()->ts() - $nSecAdminAt );
139
- }
140
- }
141
- return (int)max( 0, $nLeft );
142
- }
143
-
144
- protected function handleModAction( string $sAction ) {
145
- switch ( $sAction ) {
146
- case 'remove_secadmin_confirm':
147
- ( new SecurityAdmin\Lib\Actions\RemoveSecAdmin() )
148
- ->setMod( $this )
149
- ->remove();
150
- break;
151
- default:
152
- break;
153
- }
154
- }
155
-
156
- public function isSecAdminSessionValid() :bool {
157
- return $this->getSecAdminTimeLeft() > 0;
158
- }
159
-
160
- public function isEnabledSecurityAdmin() :bool {
161
- /** @var SecurityAdmin\Options $opts */
162
- $opts = $this->getOptions();
163
- return $this->isModOptEnabled() &&
164
- ( count( $opts->getSecurityAdminUsers() ) > 0 ||
165
- ( $opts->hasSecurityPIN() && $this->getSecAdminTimeout() > 0 )
166
- );
167
- }
168
-
169
- /**
170
- * @param bool $bSetOn
171
- * @return bool
172
- */
173
- public function setSecurityAdminStatusOnOff( $bSetOn = false ) {
174
- /** @var Shield\Databases\Session\Update $oUpdater */
175
- $oUpdater = $this->getDbHandler_Sessions()->getQueryUpdater();
176
- return $bSetOn ?
177
- $oUpdater->startSecurityAdmin( $this->getSession() )
178
- : $oUpdater->terminateSecurityAdmin( $this->getSession() );
179
- }
180
-
181
- public function isValidSecAdminRequest() :bool {
182
- return $this->isAccessKeyRequest() && $this->testSecAccessKeyRequest();
183
- }
184
-
185
- public function testSecAccessKeyRequest() :bool {
186
- if ( !isset( $this->bValidSecAdminRequest ) ) {
187
- $bValid = false;
188
- $sReqKey = Services::Request()->post( 'sec_admin_key' );
189
- if ( !empty( $sReqKey ) ) {
190
- /** @var SecurityAdmin\Options $opts */
191
- $opts = $this->getOptions();
192
- $bValid = hash_equals( $opts->getSecurityPIN(), md5( $sReqKey ) );
193
- if ( !$bValid ) {
194
- $sEscaped = isset( $_POST[ 'sec_admin_key' ] ) ? $_POST[ 'sec_admin_key' ] : '';
195
- if ( !empty( $sEscaped ) ) {
196
- // Workaround for escaping of passwords
197
- $bValid = hash_equals( $opts->getSecurityPIN(), md5( $sEscaped ) );
198
- if ( $bValid ) {
199
- $opts->setOpt( 'admin_access_key', md5( $sReqKey ) );
200
- }
201
- }
202
- }
203
-
204
- $this->getCon()->fireEvent( $bValid ? 'key_success' : 'key_fail' );
205
- }
206
-
207
- $this->bValidSecAdminRequest = $bValid;
208
- }
209
- return $this->bValidSecAdminRequest;
210
- }
211
-
212
- private function isAccessKeyRequest() :bool {
213
- return strlen( Services::Request()->post( 'sec_admin_key', '' ) ) > 0;
214
- }
215
-
216
- public function verifyAccessKey( string $key ) :bool {
217
- /** @var SecurityAdmin\Options $opts */
218
- $opts = $this->getOptions();
219
- return !empty( $key ) && hash_equals( $opts->getSecurityPIN(), md5( $key ) );
220
- }
221
-
222
- public function getWhitelabelOptions() :array {
223
- $opts = $this->getOptions();
224
- $sMain = $opts->getOpt( 'wl_pluginnamemain' );
225
- $sMenu = $opts->getOpt( 'wl_namemenu' );
226
- if ( empty( $sMenu ) ) {
227
- $sMenu = $sMain;
228
- }
229
-
230
- return [
231
- 'name_main' => $sMain,
232
- 'name_menu' => $sMenu,
233
- 'name_company' => $opts->getOpt( 'wl_companyname' ),
234
- 'description' => $opts->getOpt( 'wl_description' ),
235
- 'url_home' => $opts->getOpt( 'wl_homeurl' ),
236
- 'url_icon' => $this->buildWlImageUrl( 'wl_menuiconurl' ),
237
- 'url_dashboardlogourl' => $this->buildWlImageUrl( 'wl_dashboardlogourl' ),
238
- 'url_login2fa_logourl' => $this->buildWlImageUrl( 'wl_login2fa_logourl' ),
239
- ];
240
- }
241
-
242
- /**
243
- * We cater for 3 options:
244
- * Full URL
245
- * Relative path URL: i.e. starts with /
246
- * Or Plugin image URL i.e. doesn't start with HTTP or /
247
- * @param string $key
248
- * @return string
249
- */
250
- private function buildWlImageUrl( $key ) {
251
- $opts = $this->getOptions();
252
-
253
- $sLogoUrl = $opts->getOpt( $key );
254
- if ( empty( $sLogoUrl ) ) {
255
- $opts->resetOptToDefault( $key );
256
- $sLogoUrl = $opts->getOpt( $key );
257
- }
258
- if ( !empty( $sLogoUrl ) && !Services::Data()->isValidWebUrl( $sLogoUrl ) && strpos( $sLogoUrl, '/' ) !== 0 ) {
259
- $sLogoUrl = $this->getCon()->getPluginUrl_Image( $sLogoUrl );
260
- if ( empty( $sLogoUrl ) ) {
261
- $opts->resetOptToDefault( $key );
262
- $sLogoUrl = $this->getCon()->getPluginUrl_Image( $opts->getOpt( $key ) );
263
- }
264
- }
265
-
266
- return $sLogoUrl;
267
- }
268
-
269
- public function isWlEnabled() :bool {
270
- /** @var SecurityAdmin\Options $opts */
271
- $opts = $this->getOptions();
272
- return $opts->isEnabledWhitelabel() && $this->isPremium();
273
- }
274
-
275
- public function isWlHideUpdates() :bool {
276
- return $this->isWlEnabled() && $this->getOptions()->isOpt( 'wl_hide_updates', 'Y' );
277
- }
278
-
279
- /**
280
- * @param string $pin
281
- * @return $this
282
- * @throws \Exception
283
- */
284
- public function setNewPinManually( string $pin ) {
285
- if ( empty( $pin ) ) {
286
- throw new \Exception( 'Attempting to set an empty Security PIN.' );
287
- }
288
- if ( !$this->getCon()->isPluginAdmin() ) {
289
- throw new \Exception( 'User does not have permission to update the Security PIN.' );
290
- }
291
-
292
- $this->setIsMainFeatureEnabled( true );
293
- $this->getOptions()->setOpt( 'admin_access_key', md5( $pin ) );
294
- return $this->saveModOptions();
295
- }
296
-
297
- public function insertCustomJsVars_Admin() {
298
- parent::insertCustomJsVars_Admin();
299
-
300
- if ( $this->getSecAdminTimeLeft() > 0 ) {
301
- $aInsertData = [
302
- 'ajax' => [
303
- 'check' => $this->getSecAdminCheckAjaxData(),
304
- ],
305
- 'is_sec_admin' => true, // if $nSecTimeLeft > 0
306
- 'timeleft' => $this->getSecAdminTimeLeft(), // JS uses milliseconds
307
- 'strings' => [
308
- 'confirm' => __( 'Security Admin session has timed-out.', 'wp-simple-firewall' ).' '.__( 'Reload now?', 'wp-simple-firewall' ),
309
- 'nearly' => __( 'Security Admin session has nearly timed-out.', 'wp-simple-firewall' ),
310
- 'expired' => __( 'Security Admin session has timed-out.', 'wp-simple-firewall' )
311
- ]
312
- ];
313
- }
314
- else {
315
- $aInsertData = [
316
- 'ajax' => [
317
- 'req_email_remove' => $this->getAjaxActionData( 'req_email_remove' ),
318
- ],
319
- 'strings' => [
320
- 'are_you_sure' => __( 'Are you sure?', 'wp-simple-firewall' )
321
- ]
322
- ];
323
- }
324
-
325
- if ( !empty( $aInsertData ) ) {
326
- wp_localize_script(
327
- $this->prefix( 'plugin' ),
328
- 'icwp_wpsf_vars_secadmin',
329
- $aInsertData
330
- );
331
- }
332
- }
333
-
334
- /**
335
- * This is the point where you would want to do any options verification
336
- */
337
- protected function doPrePluginOptionsSave() {
338
- /** @var SecurityAdmin\Options $opts */
339
- $opts = $this->getOptions();
340
-
341
- if ( hash_equals( $opts->getSecurityPIN(), self::HASH_DELETE ) ) {
342
- $opts->clearSecurityAdminKey();
343
- $this->setSecurityAdminStatusOnOff( false );
344
- }
345
-
346
- // Restricting Activate Plugins also means restricting the rest.
347
- $aPluginsRestrictions = $opts->getAdminAccessArea_Plugins();
348
- if ( in_array( 'activate_plugins', $aPluginsRestrictions ) ) {
349
- $opts->setOpt(
350
- 'admin_access_restrict_plugins',
351
- array_unique( array_merge( $aPluginsRestrictions, [
352
- 'install_plugins',
353
- 'update_plugins',
354
- 'delete_plugins'
355
- ] ) )
356
- );
357
- }
358
-
359
- // Restricting Switch (Activate) Themes also means restricting the rest.
360
- $aThemesRestrictions = $opts->getAdminAccessArea_Themes();
361
- if ( in_array( 'switch_themes', $aThemesRestrictions ) && in_array( 'edit_theme_options', $aThemesRestrictions ) ) {
362
- $opts->setOpt(
363
- 'admin_access_restrict_themes',
364
- array_unique( array_merge( $aThemesRestrictions, [
365
- 'install_themes',
366
- 'update_themes',
367
- 'delete_themes'
368
- ] ) )
369
- );
370
- }
371
-
372
- $aPostRestrictions = $opts->getAdminAccessArea_Posts();
373
- if ( in_array( 'edit', $aPostRestrictions ) ) {
374
- $opts->setOpt(
375
- 'admin_access_restrict_posts',
376
- array_unique( array_merge( $aPostRestrictions, [ 'create', 'publish', 'delete' ] ) )
377
- );
378
- }
379
- }
380
-
381
- public function preDeactivatePlugin() {
382
- if ( !$this->getCon()->isPluginAdmin() ) {
383
- Services::WpGeneral()->wpDie(
384
- __( "Sorry, this plugin is protected against unauthorised attempts to disable it.", 'wp-simple-firewall' )
385
- .'<br />'.sprintf( '<a href="%s">%s</a>',
386
- $this->getUrl_AdminPage(),
387
- __( "You'll just need to authenticate first and try again.", 'wp-simple-firewall' )
388
- )
389
- );
390
- }
391
- }
392
-
393
- protected function getNamespaceBase() :string {
394
- return 'SecurityAdmin';
395
- }
396
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/features/audit_trail.php DELETED
@@ -1,111 +0,0 @@
1
- <?php
2
-
3
- use FernleafSystems\Wordpress\Plugin\Shield;
4
- use FernleafSystems\Wordpress\Services\Services;
5
-
6
- /**
7
- * @deprecated 10.1
8
- */
9
- class ICWP_WPSF_FeatureHandler_AuditTrail extends ICWP_WPSF_FeatureHandler_BaseWpsf {
10
-
11
- public function getDbHandler_AuditTrail() :Shield\Databases\AuditTrail\Handler {
12
- return $this->getDbH( 'audit' );
13
- }
14
-
15
- /**
16
- * @return bool
17
- * @throws \Exception
18
- */
19
- protected function isReadyToExecute() {
20
- return $this->getDbHandler_AuditTrail()->isReady()
21
- && parent::isReadyToExecute();
22
- }
23
-
24
- /**
25
- * @return array
26
- */
27
- public function getAllContexts() {
28
- return [
29
- 'all' => 'All', //special
30
- 'wpsf' => $this->getCon()->getHumanName(),
31
- 'wordpress' => 'WordPress',
32
- 'users' => 'Users',
33
- 'posts' => 'Posts',
34
- 'plugins' => 'Plugins',
35
- 'themes' => 'Themes',
36
- 'emails' => 'Emails',
37
- ];
38
- }
39
-
40
- /**
41
- * See plugin controller for the nature of $aData wpPrivacyExport()
42
- *
43
- * @param array $aExportItems
44
- * @param string $sEmail
45
- * @param int $nPage
46
- * @return array
47
- */
48
- public function onWpPrivacyExport( $aExportItems, $sEmail, $nPage = 1 ) {
49
-
50
- $oUser = Services::WpUsers()->getUserByEmail( $sEmail );
51
-
52
- $aExportItem = [
53
- 'group_id' => $this->prefix(),
54
- 'group_label' => sprintf( __( '[%s] Audit Trail Entries', 'wp-simple-firewall' ), $this->getCon()
55
- ->getHumanName() ),
56
- 'item_id' => $this->prefix( 'audit-trail' ),
57
- 'data' => [],
58
- ];
59
-
60
- try {
61
- /** @var Shield\Databases\AuditTrail\Select $oFinder */
62
- $oFinder = $this->getDbHandler_AuditTrail()
63
- ->getQuerySelector();
64
- $oFinder->filterByUsername( $oUser->user_login );
65
-
66
- $oWp = Services::WpGeneral();
67
- /** @var Shield\Databases\AuditTrail\EntryVO $oEntry */
68
- foreach ( $oFinder->query() as $oEntry ) {
69
- $aExportItem[ 'data' ][] = [
70
- $sTimeStamp = $oWp->getTimeStringForDisplay( $oEntry->getCreatedAt() ),
71
- 'name' => sprintf( '[%s] Audit Trail Entry', $sTimeStamp ),
72
- 'value' => sprintf( '[IP:%s] %s', $oEntry->ip, $oEntry->message )
73
- ];
74
- }
75
-
76
- if ( !empty( $aExportItem[ 'data' ] ) ) {
77
- $aExportItems[] = $aExportItem;
78
- }
79
- }
80
- catch ( \Exception $oE ) {
81
- }
82
-
83
- return $aExportItems;
84
- }
85
-
86
- /**
87
- * See plugin controller for the nature of $aData wpPrivacyErase()
88
- *
89
- * @param array $aData
90
- * @param string $sEmail
91
- * @param int $nPage
92
- * @return array
93
- */
94
- public function onWpPrivacyErase( $aData, $sEmail, $nPage = 1 ) {
95
- try {
96
- $oThisUsername = Services::WpUsers()->getUserByEmail( $sEmail )->user_login;
97
- $this->getDbHandler_AuditTrail()
98
- ->getQueryDeleter()
99
- ->addWhereSearch( 'wp_username', $oThisUsername )
100
- ->all();
101
- $aData[ 'messages' ][] = sprintf( '%s Audit Entries deleted', $this->getCon()->getHumanName() );
102
- }
103
- catch ( \Exception $oE ) {
104
- }
105
- return $aData;
106
- }
107
-
108
- protected function getNamespaceBase() :string {
109
- return 'AuditTrail';
110
- }
111
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/features/autoupdates.php DELETED
@@ -1,16 +0,0 @@
1
- <?php
2
-
3
- use FernleafSystems\Wordpress\Plugin\Shield;
4
-
5
- /**
6
- * @deprecated 10.1
7
- */
8
- class ICWP_WPSF_FeatureHandler_Autoupdates extends ICWP_WPSF_FeatureHandler_BaseWpsf {
9
-
10
- /**
11
- * @return string
12
- */
13
- protected function getNamespaceBase() :string {
14
- return 'Autoupdates';
15
- }
16
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/features/base.php DELETED
@@ -1,1599 +0,0 @@
1
- <?php
2
-
3
- use FernleafSystems\Wordpress\Plugin\Shield;
4
- use FernleafSystems\Wordpress\Services\Services;
5
-
6
- /**
7
- * Class ICWP_WPSF_FeatureHandler_Base
8
- */
9
- abstract class ICWP_WPSF_FeatureHandler_Base {
10
-
11
- use Shield\Modules\PluginControllerConsumer;
12
-
13
- /**
14
- * @var string
15
- */
16
- private $sOptionsStoreKey;
17
-
18
- /**
19
- * @var string
20
- */
21
- protected $sModSlug;
22
-
23
- /**
24
- * @var bool
25
- */
26
- protected $bImportExportWhitelistNotify = false;
27
-
28
- /**
29
- * @var ICWP_WPSF_FeatureHandler_Email
30
- * @deprecated 10.1
31
- */
32
- private static $oEmailHandler;
33
-
34
- /**
35
- * @var Shield\Modules\Base\BaseProcessor
36
- */
37
- private $oProcessor;
38
-
39
- /**
40
- * @var ICWP_WPSF_Wizard_Base
41
- */
42
- private $oWizard;
43
-
44
- /**
45
- * @var Shield\Modules\Base\BaseReporting
46
- */
47
- private $oReporting;
48
-
49
- /**
50
- * @var Shield\Modules\Base\UI
51
- */
52
- private $oUI;
53
-
54
- /**
55
- * @var Shield\Modules\Base\Options
56
- */
57
- private $oOpts;
58
-
59
- /**
60
- * @var Shield\Modules\Base\WpCli
61
- */
62
- private $oWpCli;
63
-
64
- /**
65
- * @var Shield\Databases\Base\Handler[]
66
- */
67
- private $aDbHandlers;
68
-
69
- /**
70
- * @param Shield\Controller\Controller $oPluginController
71
- * @param array $aMod
72
- * @throws \Exception
73
- */
74
- public function __construct( $oPluginController, $aMod = [] ) {
75
- if ( !$oPluginController instanceof Shield\Controller\Controller ) {
76
- throw new \Exception( 'Plugin controller not supplied to Module' );
77
- }
78
- $this->setCon( $oPluginController );
79
-
80
- if ( empty( $aMod[ 'storage_key' ] ) && empty( $aMod[ 'slug' ] ) ) {
81
- throw new \Exception( 'Module storage key AND slug are undefined' );
82
- }
83
-
84
- $this->sOptionsStoreKey = empty( $aMod[ 'storage_key' ] ) ? $aMod[ 'slug' ] : $aMod[ 'storage_key' ];
85
- if ( isset( $aMod[ 'slug' ] ) ) {
86
- $this->sModSlug = $aMod[ 'slug' ];
87
- }
88
-
89
- if ( $this->verifyModuleMeetRequirements() ) {
90
- $this->handleAutoPageRedirects();
91
- $this->setupHooks( $aMod );
92
- $this->doPostConstruction();
93
- }
94
- }
95
-
96
- protected function setupHooks( array $aModProps ) {
97
- $con = $this->getCon();
98
- $nRunPriority = isset( $aModProps[ 'load_priority' ] ) ? $aModProps[ 'load_priority' ] : 100;
99
-
100
- add_action( $con->prefix( 'modules_loaded' ), function () {
101
- $this->onModulesLoaded();
102
- }, $nRunPriority );
103
- add_action( $con->prefix( 'run_processors' ), [ $this, 'onRunProcessors' ], $nRunPriority );
104
- add_action( 'init', [ $this, 'onWpInit' ], 1 );
105
-
106
- $nMenuPri = isset( $aModProps[ 'menu_priority' ] ) ? $aModProps[ 'menu_priority' ] : 100;
107
- add_filter( $con->prefix( 'submenu_items' ), [ $this, 'supplySubMenuItem' ], $nMenuPri );
108
- add_action( $con->prefix( 'plugin_shutdown' ), [ $this, 'onPluginShutdown' ] );
109
- add_action( $con->prefix( 'deactivate_plugin' ), [ $this, 'onPluginDeactivate' ] );
110
- add_action( $con->prefix( 'delete_plugin' ), [ $this, 'onPluginDelete' ] );
111
- add_filter( $con->prefix( 'aggregate_all_plugin_options' ), [ $this, 'aggregateOptionsValues' ] );
112
-
113
- add_filter( $con->prefix( 'register_admin_notices' ), [ $this, 'fRegisterAdminNotices' ] );
114
-
115
- add_action( $con->prefix( 'daily_cron' ), [ $this, 'runDailyCron' ] );
116
- add_action( $con->prefix( 'hourly_cron' ), [ $this, 'runHourlyCron' ] );
117
-
118
- add_action( 'admin_enqueue_scripts', [ $this, 'onWpEnqueueAdminJs' ], 100 );
119
-
120
- if ( is_admin() || is_network_admin() ) {
121
- $this->loadAdminNotices();
122
- }
123
-
124
- // if ( $this->isAdminOptionsPage() ) {
125
- // add_action( 'current_screen', array( $this, 'onSetCurrentScreen' ) );
126
- // }
127
-
128
- $this->setupCustomHooks();
129
- }
130
-
131
- protected function setupCustomHooks() {
132
- }
133
-
134
- protected function doPostConstruction() {
135
- }
136
-
137
- public function runDailyCron() {
138
- $this->cleanupDatabases();
139
- }
140
-
141
- public function runHourlyCron() {
142
- }
143
-
144
- protected function cleanupDatabases() {
145
- foreach ( $this->getDbHandlers( true ) as $oDbh ) {
146
- if ( $oDbh instanceof Shield\Databases\Base\Handler && $oDbh->isReady() ) {
147
- $oDbh->autoCleanDb();
148
- }
149
- }
150
- }
151
-
152
- /**
153
- * @param bool $bInitAll
154
- * @return Shield\Databases\Base\Handler[]
155
- */
156
- protected function getDbHandlers( $bInitAll = false ) {
157
- if ( $bInitAll ) {
158
- foreach ( $this->getAllDbClasses() as $sDbSlug => $sDbClass ) {
159
- $this->getDbH( $sDbSlug );
160
- }
161
- }
162
- return is_array( $this->aDbHandlers ) ? $this->aDbHandlers : [];
163
- }
164
-
165
- /**
166
- * @param string $sDbhKey
167
- * @return Shield\Databases\Base\Handler|mixed|false
168
- */
169
- protected function getDbH( $sDbhKey ) {
170
- $dbh = false;
171
-
172
- if ( !is_array( $this->aDbHandlers ) ) {
173
- $this->aDbHandlers = [];
174
- }
175
-
176
- if ( !empty( $this->aDbHandlers[ $sDbhKey ] ) ) {
177
- $dbh = $this->aDbHandlers[ $sDbhKey ];
178
- }
179
- else {
180
- $aDbClasses = $this->getAllDbClasses();
181
- if ( isset( $aDbClasses[ $sDbhKey ] ) ) {
182
- /** @var Shield\Databases\Base\Handler $dbh */
183
- $dbh = new $aDbClasses[ $sDbhKey ]();
184
- try {
185
- $dbh->setMod( $this )->tableInit();
186
- }
187
- catch ( \Exception $e ) {
188
- }
189
- }
190
- $this->aDbHandlers[ $sDbhKey ] = $dbh;
191
- }
192
-
193
- return $dbh;
194
- }
195
-
196
- /**
197
- * @return string[]
198
- */
199
- private function getAllDbClasses() {
200
- $classes = $this->getOptions()->getDef( 'db_classes' );
201
- return is_array( $classes ) ? $classes : [];
202
- }
203
-
204
- /**
205
- * @return false|Shield\Modules\Base\Upgrade|mixed
206
- */
207
- public function getUpgradeHandler() {
208
- return $this->loadModElement( 'Upgrade' );
209
- }
210
-
211
- /**
212
- * @param string $sEncoding
213
- * @return array
214
- */
215
- public function getAjaxFormParams( $sEncoding = 'none' ) {
216
- $oReq = Services::Request();
217
- $aFormParams = [];
218
- $sRaw = $oReq->post( 'form_params', '' );
219
-
220
- if ( !empty( $sRaw ) ) {
221
-
222
- $sMaybeEncoding = $oReq->post( 'enc_params' );
223
- if ( in_array( $sMaybeEncoding, [ 'none', 'lz-string', 'b64' ] ) ) {
224
- $sEncoding = $sMaybeEncoding;
225
- }
226
-
227
- switch ( $sEncoding ) {
228
- case 'lz-string':
229
- $sRaw = \LZCompressor\LZString::decompress( base64_decode( $sRaw ) );
230
- break;
231
-
232
- case 'b64':
233
- $sRaw = base64_decode( $sRaw );
234
- break;
235
-
236
- case 'none':
237
- default:
238
- break;
239
- }
240
-
241
- parse_str( $sRaw, $aFormParams );
242
- }
243
- return $aFormParams;
244
- }
245
-
246
- /**
247
- * @param array $aAdminNotices
248
- * @return array
249
- */
250
- public function fRegisterAdminNotices( $aAdminNotices ) {
251
- if ( !is_array( $aAdminNotices ) ) {
252
- $aAdminNotices = [];
253
- }
254
- return array_merge( $aAdminNotices, $this->getOptions()->getAdminNotices() );
255
- }
256
-
257
- private function verifyModuleMeetRequirements() :bool {
258
- $bMeetsReqs = true;
259
-
260
- $aPhpReqs = $this->getOptions()->getFeatureRequirement( 'php' );
261
- if ( !empty( $aPhpReqs ) ) {
262
-
263
- if ( !empty( $aPhpReqs[ 'version' ] ) ) {
264
- $bMeetsReqs = $bMeetsReqs && Services::Data()->getPhpVersionIsAtLeast( $aPhpReqs[ 'version' ] );
265
- }
266
- if ( !empty( $aPhpReqs[ 'functions' ] ) && is_array( $aPhpReqs[ 'functions' ] ) ) {
267
- foreach ( $aPhpReqs[ 'functions' ] as $sFunction ) {
268
- $bMeetsReqs = $bMeetsReqs && function_exists( $sFunction );
269
- }
270
- }
271
- if ( !empty( $aPhpReqs[ 'constants' ] ) && is_array( $aPhpReqs[ 'constants' ] ) ) {
272
- foreach ( $aPhpReqs[ 'constants' ] as $sConstant ) {
273
- $bMeetsReqs = $bMeetsReqs && defined( $sConstant );
274
- }
275
- }
276
- }
277
-
278
- return $bMeetsReqs;
279
- }
280
-
281
- protected function onModulesLoaded() {
282
- }
283
-
284
- public function onRunProcessors() {
285
- $oOpts = $this->getOptions();
286
- if ( $oOpts->getFeatureProperty( 'auto_load_processor' ) ) {
287
- $this->loadProcessor();
288
- }
289
- try {
290
- $bSkip = (bool)$oOpts->getFeatureProperty( 'skip_processor' );
291
- if ( !$bSkip && !$this->isUpgrading() && $this->isModuleEnabled() && $this->isReadyToExecute() ) {
292
- $this->doExecuteProcessor();
293
- }
294
- }
295
- catch ( \Exception $e ) {
296
- }
297
- }
298
-
299
- /**
300
- * @return bool
301
- * @throws \Exception
302
- */
303
- protected function isReadyToExecute() {
304
- return !is_null( $this->getProcessor() );
305
- }
306
-
307
- protected function doExecuteProcessor() {
308
- $this->getProcessor()->execute();
309
- }
310
-
311
- public function onWpInit() {
312
- }
313
-
314
- /**
315
- * We have to do it this way as the "page hook" is built upon the top-level plugin
316
- * menu name. But what if we white label? So we need to dynamically grab the page hook
317
- */
318
- public function onSetCurrentScreen() {
319
- global $page_hook;
320
- add_action( 'load-'.$page_hook, [ $this, 'onLoadOptionsScreen' ] );
321
- }
322
-
323
- public function onLoadOptionsScreen() {
324
- if ( $this->getCon()->isValidAdminArea() ) {
325
- $this->buildContextualHelp();
326
- }
327
- }
328
-
329
- /**
330
- * Override this and adapt per feature
331
- * @return Shield\Modules\Base\BaseProcessor|mixed
332
- */
333
- protected function loadProcessor() {
334
- if ( !isset( $this->oProcessor ) ) {
335
- try {
336
- // TODO: Remove 'abstract' from base processor after transition to new processors is complete
337
- $class = $this->findElementClass( 'Processor', true );
338
- }
339
- catch ( Exception $e ) {
340
- $class = $this->getProcessorClassName();
341
- }
342
- if ( !@class_exists( $class ) ) {
343
- return null;
344
- }
345
- $this->oProcessor = new $class( $this );
346
- }
347
- return $this->oProcessor;
348
- }
349
-
350
- /**
351
- * This is the old method
352
- * @deprecated 10.1
353
- */
354
- protected function getProcessorClassName() :string {
355
- return implode( '_',
356
- [
357
- strtoupper( $this->getCon()->getPluginPrefix( '_' ) ),
358
- 'Processor',
359
- str_replace( ' ', '', ucwords( str_replace( '_', ' ', $this->getSlug() ) ) )
360
- ]
361
- );
362
- }
363
-
364
- /**
365
- * Override this and adapt per feature
366
- * @return string
367
- */
368
- protected function getWizardClassName() {
369
- return implode( '_',
370
- [
371
- strtoupper( $this->getCon()->getPluginPrefix( '_' ) ),
372
- 'Wizard',
373
- str_replace( ' ', '', ucwords( str_replace( '_', ' ', $this->getSlug() ) ) )
374
- ]
375
- );
376
- }
377
-
378
- public function isUpgrading() :bool {
379
- return $this->getCon()->getIsRebuildOptionsFromFile() || $this->getOptions()->getRebuildFromFile();
380
- }
381
-
382
- /**
383
- * Hooked to the plugin's main plugin_shutdown action
384
- */
385
- public function onPluginShutdown() {
386
- if ( !$this->getCon()->plugin_deleting ) {
387
- if ( rand( 1, 40 ) === 2 ) {
388
- // cleanup databases randomly just in-case cron doesn't run.
389
- $this->cleanupDatabases();
390
- }
391
- $this->saveModOptions();
392
- }
393
- }
394
-
395
- public function getOptionsStorageKey() :string {
396
- return $this->getCon()->prefixOption( $this->sOptionsStoreKey ).'_options';
397
- }
398
-
399
- /**
400
- * @return Shield\Modules\Base\BaseProcessor|\FernleafSystems\Utilities\Logic\OneTimeExecute|mixed
401
- */
402
- public function getProcessor() {
403
- return $this->loadProcessor();
404
- }
405
-
406
- public function getUrl_AdminPage() :string {
407
- return Services::WpGeneral()
408
- ->getUrl_AdminPage(
409
- $this->getModSlug(),
410
- $this->getCon()->getIsWpmsNetworkAdminOnly()
411
- );
412
- }
413
-
414
- /**
415
- * @param string $sAction
416
- * @return string
417
- */
418
- public function buildAdminActionNonceUrl( $sAction ) {
419
- $aActionNonce = $this->getNonceActionData( $sAction );
420
- $aActionNonce[ 'ts' ] = Services::Request()->ts();
421
- return add_query_arg( $aActionNonce, $this->getUrl_AdminPage() );
422
- }
423
-
424
- protected function getModActionParams( string $action ) :array {
425
- $con = $this->getCon();
426
- return [
427
- 'action' => $con->prefix(),
428
- 'exec' => $action,
429
- 'mod_slug' => $this->getModSlug(),
430
- 'ts' => Services::Request()->ts(),
431
- 'exec_nonce' => substr(
432
- hash_hmac( 'md5', $action.Services::Request()->ts(), $con->getSiteInstallationId() )
433
- , 0, 6 )
434
- ];
435
- }
436
-
437
- /**
438
- * @return bool
439
- * @throws \Exception
440
- */
441
- protected function verifyModActionRequest() :bool {
442
- $bValid = false;
443
-
444
- $con = $this->getCon();
445
- $req = Services::Request();
446
-
447
- $sExec = $req->request( 'exec' );
448
- if ( !empty( $sExec ) && $req->request( 'action' ) == $con->prefix() ) {
449
-
450
-
451
- if ( wp_verify_nonce( $req->request( 'exec_nonce' ), $sExec ) && $con->getMeetsBasePermissions() ) {
452
- $bValid = true;
453
- }
454
- else {
455
- $bValid = $req->request( 'exec_nonce' ) ===
456
- substr( hash_hmac( 'md5', $sExec.$req->request( 'ts' ), $con->getSiteInstallationId() ), 0, 6 );
457
- }
458
- if ( !$bValid ) {
459
- throw new Exception( 'Invalid request' );
460
- }
461
- }
462
-
463
- return $bValid;
464
- }
465
-
466
- public function getUrl_DirectLinkToOption( string $key ) :string {
467
- $url = $this->getUrl_AdminPage();
468
- $def = $this->getOptions()->getOptDefinition( $key );
469
- if ( !empty( $def[ 'section' ] ) ) {
470
- $url = $this->getUrl_DirectLinkToSection( $def[ 'section' ] );
471
- }
472
- return $url;
473
- }
474
-
475
- public function getUrl_DirectLinkToSection( string $section ) :string {
476
- if ( $section == 'primary' ) {
477
- $section = $this->getOptions()->getPrimarySection()[ 'slug' ];
478
- }
479
- return $this->getUrl_AdminPage().'#tab-'.$section;
480
- }
481
-
482
- /**
483
- * TODO: Get rid of this crap and/or handle the \Exception thrown in loadFeatureHandler()
484
- * @return ICWP_WPSF_FeatureHandler_Email
485
- * @throws \Exception
486
- * @deprecated 10.1
487
- */
488
- public function getEmailHandler() {
489
- return $this->getCon()->getModule( 'email' );
490
- }
491
-
492
- /**
493
- * @return ICWP_WPSF_Processor_Email
494
- */
495
- public function getEmailProcessor() {
496
- return $this->getEmailHandler()->getProcessor();
497
- }
498
-
499
- /**
500
- * @param bool $enable
501
- * @return $this
502
- */
503
- public function setIsMainFeatureEnabled( bool $enable ) {
504
- $this->getOptions()->setOpt( 'enable_'.$this->getSlug(), $enable ? 'Y' : 'N' );
505
- return $this;
506
- }
507
-
508
- public function isModuleEnabled() :bool {
509
- /** @var Shield\Modules\Plugin\Options $oPluginOpts */
510
- $oPluginOpts = $this->getCon()->getModule_Plugin()->getOptions();
511
-
512
- if ( $this->getOptions()->getFeatureProperty( 'auto_enabled' ) === true ) {
513
- // Auto enabled modules always run regardless
514
- $enabled = true;
515
- }
516
- elseif ( $oPluginOpts->isPluginGloballyDisabled() ) {
517
- $enabled = false;
518
- }
519
- elseif ( $this->getCon()->getIfForceOffActive() ) {
520
- $enabled = false;
521
- }
522
- elseif ( $this->getOptions()->getFeatureProperty( 'premium' ) === true
523
- && !$this->isPremium() ) {
524
- $enabled = false;
525
- }
526
- else {
527
- $enabled = $this->isModOptEnabled();
528
- }
529
-
530
- return $enabled;
531
- }
532
-
533
- public function isModOptEnabled() :bool {
534
- $opts = $this->getOptions();
535
- return $opts->isOpt( $this->getEnableModOptKey(), 'Y' )
536
- || $opts->isOpt( $this->getEnableModOptKey(), true, true );
537
- }
538
-
539
- public function getEnableModOptKey() :string {
540
- return 'enable_'.$this->getSlug();
541
- }
542
-
543
- public function getMainFeatureName() :string {
544
- return __( $this->getOptions()->getFeatureProperty( 'name' ), 'wp-simple-firewall' );
545
- }
546
-
547
- public function getModSlug( bool $prefix = true ) :string {
548
- return $prefix ? $this->prefix( $this->getSlug() ) : $this->getSlug();
549
- }
550
-
551
- /**
552
- * @return string
553
- */
554
- public function getSlug() {
555
- if ( !isset( $this->sModSlug ) ) {
556
- $this->sModSlug = $this->getOptions()->getFeatureProperty( 'slug' );
557
- }
558
- return $this->sModSlug;
559
- }
560
-
561
- /**
562
- * @param array $aItems
563
- * @return array
564
- */
565
- public function supplySubMenuItem( $aItems ) {
566
-
567
- $sTitle = $this->getOptions()->getFeatureProperty( 'menu_title' );
568
- $sTitle = empty( $sTitle ) ? $this->getMainFeatureName() : __( $sTitle, 'wp-simple-firewall' );
569
-
570
- if ( !empty( $sTitle ) ) {
571
-
572
- $sHumanName = $this->getCon()->getHumanName();
573
-
574
- $bMenuHighlighted = $this->getOptions()->getFeatureProperty( 'highlight_menu_item' );
575
- if ( $bMenuHighlighted ) {
576
- $sTitle = sprintf( '<span class="icwp_highlighted">%s</span>', $sTitle );
577
- }
578
-
579
- $sMenuPageTitle = $sTitle.' - '.$sHumanName;
580
- $aItems[ $sMenuPageTitle ] = [
581
- $sTitle,
582
- $this->getModSlug(),
583
- [ $this, 'displayModuleAdminPage' ],
584
- $this->getIfShowModuleMenuItem()
585
- ];
586
-
587
- $aAdditionalItems = $this->getOptions()->getAdditionalMenuItems();
588
- if ( !empty( $aAdditionalItems ) && is_array( $aAdditionalItems ) ) {
589
-
590
- foreach ( $aAdditionalItems as $aMenuItem ) {
591
- $sMenuPageTitle = $sHumanName.' - '.$aMenuItem[ 'title' ];
592
- $aItems[ $sMenuPageTitle ] = [
593
- __( $aMenuItem[ 'title' ], 'wp-simple-firewall' ),
594
- $this->prefix( $aMenuItem[ 'slug' ] ),
595
- [ $this, $aMenuItem[ 'callback' ] ],
596
- true
597
- ];
598
- }
599
- }
600
- }
601
- return $aItems;
602
- }
603
-
604
- /**
605
- * Handles the case where we want to redirect certain menu requests to other pages
606
- * of the plugin automatically. It lets us create custom menu items.
607
- * This can of course be extended for any other types of redirect.
608
- */
609
- public function handleAutoPageRedirects() {
610
- $aConf = $this->getOptions()->getRawData_FullFeatureConfig();
611
- if ( !empty( $aConf[ 'custom_redirects' ] ) && $this->getCon()->isValidAdminArea() ) {
612
- foreach ( $aConf[ 'custom_redirects' ] as $aRedirect ) {
613
- if ( Services::Request()->query( 'page' ) == $this->prefix( $aRedirect[ 'source_mod_page' ] ) ) {
614
- Services::Response()->redirect(
615
- $this->getCon()->getModule( $aRedirect[ 'target_mod_page' ] )->getUrl_AdminPage(),
616
- $aRedirect[ 'query_args' ],
617
- true,
618
- false
619
- );
620
- }
621
- }
622
- }
623
- }
624
-
625
- /**
626
- * @return array
627
- */
628
- protected function getAdditionalMenuItem() {
629
- return [];
630
- }
631
-
632
- /**
633
- * TODO: not the place for this method.
634
- * @return array[]
635
- */
636
- public function getModulesSummaryData() {
637
- return array_map(
638
- function ( $mod ) {
639
- return $mod->buildSummaryData();
640
- },
641
- $this->getCon()->modules
642
- );
643
- }
644
-
645
- /**
646
- * @return array
647
- */
648
- public function buildSummaryData() {
649
- $opts = $this->getOptions();
650
- $sMenuTitle = $opts->getFeatureProperty( 'menu_title' );
651
-
652
- $aSections = $opts->getSections();
653
- foreach ( $aSections as $sSlug => $aSection ) {
654
- try {
655
- $aStrings = $this->getStrings()->getSectionStrings( $aSection[ 'slug' ] );
656
- foreach ( $aStrings as $sKey => $sVal ) {
657
- unset( $aSection[ $sKey ] );
658
- $aSection[ $sKey ] = $sVal;
659
- }
660
- }
661
- catch ( \Exception $e ) {
662
- }
663
- }
664
-
665
- $aSum = [
666
- 'slug' => $this->getSlug(),
667
- 'enabled' => $this->getUIHandler()->isEnabledForUiSummary(),
668
- 'active' => $this->isThisModulePage() || $this->isPage_InsightsThisModule(),
669
- 'name' => $this->getMainFeatureName(),
670
- 'sidebar_name' => $opts->getFeatureProperty( 'sidebar_name' ),
671
- 'menu_title' => empty( $sMenuTitle ) ? $this->getMainFeatureName() : __( $sMenuTitle, 'wp-simple-firewall' ),
672
- 'href' => network_admin_url( 'admin.php?page='.$this->getModSlug() ),
673
- 'sections' => $aSections,
674
- 'options' => [],
675
- 'show_mod_opts' => $this->getIfShowModuleOpts(),
676
- ];
677
-
678
- foreach ( $opts->getVisibleOptionsKeys() as $sOptKey ) {
679
- try {
680
- $aOptData = $this->getStrings()->getOptionStrings( $sOptKey );
681
- $aOptData[ 'href' ] = $this->getUrl_DirectLinkToOption( $sOptKey );
682
- $aSum[ 'options' ][ $sOptKey ] = $aOptData;
683
- }
684
- catch ( \Exception $e ) {
685
- }
686
- }
687
-
688
- $aSum[ 'tooltip' ] = sprintf(
689
- '%s',
690
- empty( $aSum[ 'sidebar_name' ] ) ? $aSum[ 'name' ] : __( $aSum[ 'sidebar_name' ], 'wp-simple-firewall' )
691
- );
692
- return $aSum;
693
- }
694
-
695
- /**
696
- * @return bool
697
- */
698
- public function getIfShowModuleMenuItem() {
699
- return (bool)$this->getOptions()->getFeatureProperty( 'show_module_menu_item' );
700
- }
701
-
702
- /**
703
- * @return bool
704
- */
705
- public function getIfShowModuleOpts() {
706
- return (bool)$this->getOptions()->getFeatureProperty( 'show_module_options' );
707
- }
708
-
709
- /**
710
- * Get config 'definition'.
711
- * @param string $key
712
- * @return mixed|null
713
- */
714
- public function getDef( string $key ) {
715
- return $this->getOptions()->getDef( $key );
716
- }
717
-
718
- /**
719
- * @return $this
720
- */
721
- public function clearLastErrors() {
722
- return $this->setLastErrors( [] );
723
- }
724
-
725
- /**
726
- * @param bool $bAsString
727
- * @param string $sGlue
728
- * @return string|array
729
- */
730
- public function getLastErrors( $bAsString = false, $sGlue = " " ) {
731
- $errors = $this->getOptions()->getOpt( 'last_errors' );
732
- if ( !is_array( $errors ) ) {
733
- $errors = [];
734
- }
735
- return $bAsString ? implode( $sGlue, $errors ) : $errors;
736
- }
737
-
738
- public function hasLastErrors() :bool {
739
- return count( $this->getLastErrors( false ) ) > 0;
740
- }
741
-
742
- public function getTextOpt( string $key ) :string {
743
- $sValue = $this->getOptions()->getOpt( $key, 'default' );
744
- if ( $sValue == 'default' ) {
745
- $sValue = $this->getTextOptDefault( $key );
746
- }
747
- return __( $sValue, 'wp-simple-firewall' );
748
- }
749
-
750
- /**
751
- * Override this on each feature that has Text field options to supply the text field defaults
752
- * @param string $sOptKey
753
- * @return string
754
- */
755
- public function getTextOptDefault( $sOptKey ) {
756
- return 'Undefined Text Opt Default';
757
- }
758
-
759
- /**
760
- * @param array|string $mErrors
761
- * @return $this
762
- */
763
- public function setLastErrors( $mErrors = [] ) {
764
- if ( !is_array( $mErrors ) ) {
765
- if ( is_string( $mErrors ) ) {
766
- $mErrors = [ $mErrors ];
767
- }
768
- else {
769
- $mErrors = [];
770
- }
771
- }
772
- $this->getOptions()->setOpt( 'last_errors', $mErrors );
773
- return $this;
774
- }
775
-
776
- public function setOptions( array $options ) {
777
- $opts = $this->getOptions();
778
- foreach ( $options as $key => $value ) {
779
- $opts->setOpt( $key, $value );
780
- }
781
- }
782
-
783
- public function isModuleRequest() :bool {
784
- return $this->getModSlug() === Services::Request()->request( 'mod_slug' );
785
- }
786
-
787
- /**
788
- * @param string $sAction
789
- * @param bool $bAsJsonEncodedObject
790
- * @return array|string
791
- */
792
- public function getAjaxActionData( $sAction = '', $bAsJsonEncodedObject = false ) {
793
- $aData = $this->getNonceActionData( $sAction );
794
- $aData[ 'ajaxurl' ] = admin_url( 'admin-ajax.php' );
795
- return $bAsJsonEncodedObject ? json_encode( (object)$aData ) : $aData;
796
- }
797
-
798
- /**
799
- * @param string $sAction
800
- * @return array
801
- */
802
- public function getNonceActionData( $sAction = '' ) {
803
- $aData = $this->getCon()->getNonceActionData( $sAction );
804
- $aData[ 'mod_slug' ] = $this->getModSlug();
805
- return $aData;
806
- }
807
-
808
- /**
809
- * @return string[]
810
- */
811
- public function getDismissedNotices() :array {
812
- $notices = $this->getOptions()->getOpt( 'dismissed_notices' );
813
- return is_array( $notices ) ? $notices : [];
814
- }
815
-
816
- /**
817
- * @return string[]
818
- */
819
- public function getUiTrack() :array {
820
- $a = $this->getOptions()->getOpt( 'ui_track' );
821
- return is_array( $a ) ? $a : [];
822
- }
823
-
824
- public function setDismissedNotices( array $dis ) {
825
- $this->getOptions()->setOpt( 'dismissed_notices', $dis );
826
- }
827
-
828
- public function setUiTrack( array $UI ) {
829
- $this->getOptions()->setOpt( 'ui_track', $UI );
830
- }
831
-
832
- /**
833
- * @return \ICWP_WPSF_Wizard_Base|null
834
- */
835
- public function getWizardHandler() {
836
- if ( !isset( $this->oWizard ) ) {
837
- $sClassName = $this->getWizardClassName();
838
- if ( !class_exists( $sClassName ) ) {
839
- return null;
840
- }
841
- $this->oWizard = new $sClassName();
842
- $this->oWizard->setMod( $this );
843
- }
844
- return $this->oWizard;
845
- }
846
-
847
- /**
848
- * @param bool $bPreProcessOptions
849
- * @return $this
850
- */
851
- public function saveModOptions( $bPreProcessOptions = false ) {
852
-
853
- if ( $bPreProcessOptions ) {
854
- $this->preProcessOptions();
855
- }
856
-
857
- $this->doPrePluginOptionsSave();
858
- if ( apply_filters( $this->prefix( 'force_options_resave' ), false ) ) {
859
- $this->getOptions()
860
- ->setNeedSave( true );
861
- }
862
-
863
- // we set the flag that options have been updated. (only use this flag if it's a MANUAL options update)
864
- $this->bImportExportWhitelistNotify = $this->getOptions()->getNeedSave();
865
- $this->store();
866
- return $this;
867
- }
868
-
869
- protected function preProcessOptions() {
870
- }
871
-
872
- private function store() {
873
- add_filter( $this->prefix( 'bypass_is_plugin_admin' ), '__return_true', 1000 );
874
- $this->getOptions()
875
- ->doOptionsSave( $this->getCon()->getIsResetPlugin(), $this->isPremium() );
876
- remove_filter( $this->prefix( 'bypass_is_plugin_admin' ), '__return_true', 1000 );
877
- }
878
-
879
- /**
880
- * @param array $aAggregatedOptions
881
- * @return array
882
- */
883
- public function aggregateOptionsValues( $aAggregatedOptions ) {
884
- return array_merge( $aAggregatedOptions, $this->getOptions()->getAllOptionsValues() );
885
- }
886
-
887
- /**
888
- * This is the point where you would want to do any options verification
889
- */
890
- protected function doPrePluginOptionsSave() {
891
- }
892
-
893
- public function onPluginDeactivate() {
894
- }
895
-
896
- public function onPluginDelete() {
897
- foreach ( $this->getDbHandlers( true ) as $oDbh ) {
898
- if ( !empty( $oDbh ) ) {
899
- $oDbh->tableDelete();
900
- }
901
- }
902
- $this->getOptions()->deleteStorage();
903
- }
904
-
905
- /**
906
- * @return array - map of each option to its option type
907
- */
908
- protected function getAllFormOptionsAndTypes() {
909
- $aOpts = [];
910
-
911
- foreach ( $this->getUIHandler()->buildOptions() as $aOptionsSection ) {
912
- if ( !empty( $aOptionsSection ) ) {
913
- foreach ( $aOptionsSection[ 'options' ] as $aOption ) {
914
- $aOpts[ $aOption[ 'key' ] ] = $aOption[ 'type' ];
915
- }
916
- }
917
- }
918
-
919
- return $aOpts;
920
- }
921
-
922
- protected function handleModAction( string $sAction ) {
923
- }
924
-
925
- /**
926
- * @throws \Exception
927
- */
928
- public function saveOptionsSubmit() {
929
- if ( !$this->getCon()->isPluginAdmin() ) {
930
- throw new \Exception( __( "You don't currently have permission to save settings.", 'wp-simple-firewall' ) );
931
- }
932
-
933
- $this->doSaveStandardOptions();
934
-
935
- $this->saveModOptions( true );
936
-
937
- // only use this flag when the options are being updated with a MANUAL save.
938
- if ( isset( $this->bImportExportWhitelistNotify ) && $this->bImportExportWhitelistNotify ) {
939
- if ( !wp_next_scheduled( $this->prefix( 'importexport_notify' ) ) ) {
940
- wp_schedule_single_event( Services::Request()->ts() + 15, $this->prefix( 'importexport_notify' ) );
941
- }
942
- }
943
- }
944
-
945
- /**
946
- * @param string $sMsg
947
- * @param bool $bError
948
- * @param bool $bShowOnLogin
949
- * @return $this
950
- */
951
- public function setFlashAdminNotice( $sMsg, $bError = false, $bShowOnLogin = false ) {
952
- $this->getCon()
953
- ->getAdminNotices()
954
- ->addFlash(
955
- sprintf( '[%s] %s', $this->getCon()->getHumanName(), $sMsg ),
956
- $bError,
957
- $bShowOnLogin
958
- );
959
- return $this;
960
- }
961
-
962
- protected function isAdminOptionsPage() :bool {
963
- return is_admin() && !Services::WpGeneral()->isAjax() && $this->isThisModulePage();
964
- }
965
-
966
- /**
967
- * @return bool
968
- */
969
- public function isPremium() {
970
- return $this->getCon()->isPremiumActive();
971
- }
972
-
973
- /**
974
- * @throws \Exception
975
- */
976
- private function doSaveStandardOptions() {
977
- $aForm = $this->getAjaxFormParams( 'b64' ); // standard options use b64 and failover to lz-string
978
-
979
- foreach ( $this->getAllFormOptionsAndTypes() as $sKey => $sOptType ) {
980
-
981
- $sOptionValue = isset( $aForm[ $sKey ] ) ? $aForm[ $sKey ] : null;
982
- if ( is_null( $sOptionValue ) ) {
983
-
984
- if ( in_array( $sOptType, [ 'text', 'email' ] ) ) { //text box, and it's null, don't update
985
- continue;
986
- }
987
- elseif ( $sOptType == 'checkbox' ) { //if it was a checkbox, and it's null, it means 'N'
988
- $sOptionValue = 'N';
989
- }
990
- elseif ( $sOptType == 'integer' ) { //if it was a integer, and it's null, it means '0'
991
- $sOptionValue = 0;
992
- }
993
- elseif ( $sOptType == 'multiple_select' ) {
994
- $sOptionValue = [];
995
- }
996
- }
997
- else { //handle any pre-processing we need to.
998
-
999
- if ( $sOptType == 'text' || $sOptType == 'email' ) {
1000
- $sOptionValue = trim( $sOptionValue );
1001
- }
1002
- if ( $sOptType == 'integer' ) {
1003
- $sOptionValue = intval( $sOptionValue );
1004
- }
1005
- elseif ( $sOptType == 'password' ) {
1006
- $sTempValue = trim( $sOptionValue );
1007
- if ( empty( $sTempValue ) ) {
1008
- continue;
1009
- }
1010
-
1011
- $sConfirm = isset( $aForm[ $sKey.'_confirm' ] ) ? $aForm[ $sKey.'_confirm' ] : null;
1012
- if ( $sTempValue !== $sConfirm ) {
1013
- throw new \Exception( __( 'Password values do not match.', 'wp-simple-firewall' ) );
1014
- }
1015
-
1016
- $sOptionValue = md5( $sTempValue );
1017
- }
1018
- elseif ( $sOptType == 'array' ) { //arrays are textareas, where each is separated by newline
1019
- $sOptionValue = array_filter( explode( "\n", esc_textarea( $sOptionValue ) ), 'trim' );
1020
- }
1021
- elseif ( $sOptType == 'comma_separated_lists' ) {
1022
- $sOptionValue = Services::Data()->extractCommaSeparatedList( $sOptionValue );
1023
- }
1024
- /* elseif ( $sOptType == 'multiple_select' ) { } */
1025
- }
1026
-
1027
- // Prevent overwriting of non-editable fields
1028
- if ( !in_array( $sOptType, [ 'noneditable_text' ] ) ) {
1029
- $this->getOptions()->setOpt( $sKey, $sOptionValue );
1030
- }
1031
- }
1032
-
1033
- // Handle Import/Export exclusions
1034
- if ( $this->isPremium() ) {
1035
- ( new Shield\Modules\Plugin\Lib\ImportExport\Options\SaveExcludedOptions() )
1036
- ->setMod( $this )
1037
- ->save( $aForm );
1038
- }
1039
- }
1040
-
1041
- protected function runWizards() {
1042
- if ( $this->isWizardPage() && $this->hasWizard() ) {
1043
- $oWiz = $this->getWizardHandler();
1044
- if ( $oWiz instanceof ICWP_WPSF_Wizard_Base ) {
1045
- $oWiz->init();
1046
- }
1047
- }
1048
- }
1049
-
1050
- /**
1051
- * @return bool
1052
- */
1053
- public function isThisModulePage() {
1054
- return $this->getCon()->isModulePage()
1055
- && Services::Request()->query( 'page' ) == $this->getModSlug();
1056
- }
1057
-
1058
- /**
1059
- * @return bool
1060
- */
1061
- public function isPage_Insights() {
1062
- return Services::Request()->query( 'page' ) == $this->getCon()->getModule_Insights()->getModSlug();
1063
- }
1064
-
1065
- /**
1066
- * @return bool
1067
- */
1068
- public function isPage_InsightsThisModule() {
1069
- return $this->isPage_Insights()
1070
- && Services::Request()->query( 'subnav' ) == $this->getSlug();
1071
- }
1072
-
1073
- /**
1074
- * @return bool
1075
- */
1076
- protected function isModuleOptionsRequest() {
1077
- return Services::Request()->post( 'mod_slug' ) === $this->getModSlug();
1078
- }
1079
-
1080
- /**
1081
- * @return bool
1082
- */
1083
- protected function isWizardPage() {
1084
- return ( $this->getCon()->getShieldAction() == 'wizard' && $this->isThisModulePage() );
1085
- }
1086
-
1087
- /**
1088
- * Will prefix and return any string with the unique plugin prefix.
1089
- * @param string $sSuffix
1090
- * @param string $sGlue
1091
- * @return string
1092
- */
1093
- public function prefix( $sSuffix = '', $sGlue = '-' ) {
1094
- return $this->getCon()->prefix( $sSuffix, $sGlue );
1095
- }
1096
-
1097
- /**
1098
- * @uses echo()
1099
- */
1100
- public function displayModuleAdminPage() {
1101
- echo $this->renderModulePage();
1102
- }
1103
-
1104
- /**
1105
- * Override this to customize anything with the display of the page
1106
- * @param array $aData
1107
- * @return string
1108
- */
1109
- protected function renderModulePage( array $aData = [] ) :string {
1110
- return $this->renderTemplate(
1111
- 'index.php',
1112
- Services::DataManipulation()->mergeArraysRecursive( $this->getUIHandler()->getBaseDisplayData(), $aData )
1113
- );
1114
- }
1115
-
1116
- /**
1117
- * @return string
1118
- */
1119
- protected function getContentWizardLanding() {
1120
- $aData = $this->getUIHandler()->getBaseDisplayData();
1121
- if ( $this->hasWizard() ) {
1122
- $aData[ 'content' ][ 'wizard_landing' ] = $this->getWizardHandler()->renderWizardLandingSnippet();
1123
- }
1124
- return $this->renderTemplate( 'snippets/module-wizard-template.php', $aData );
1125
- }
1126
-
1127
- protected function buildContextualHelp() {
1128
- if ( !function_exists( 'get_current_screen' ) ) {
1129
- require_once( ABSPATH.'wp-admin/includes/screen.php' );
1130
- }
1131
- $screen = get_current_screen();
1132
- //$screen->remove_help_tabs();
1133
- $screen->add_help_tab( [
1134
- 'id' => 'my-plugin-default',
1135
- 'title' => __( 'Default' ),
1136
- 'content' => 'This is where I would provide tabbed help to the user on how everything in my admin panel works. Formatted HTML works fine in here too'
1137
- ] );
1138
- //add more help tabs as needed with unique id's
1139
-
1140
- // Help sidebars are optional
1141
- $screen->set_help_sidebar(
1142
- '<p><strong>'.__( 'For more information:' ).'</strong></p>'.
1143
- '<p><a href="http://wordpress.org/support/" target="_blank">'._( 'Support Forums' ).'</a></p>'
1144
- );
1145
- }
1146
-
1147
- /**
1148
- * @param string $sWizardSlug
1149
- * @return string
1150
- * @uses nonce
1151
- */
1152
- public function getUrl_Wizard( $sWizardSlug ) {
1153
- $aDef = $this->getWizardDefinition( $sWizardSlug );
1154
- if ( empty( $aDef[ 'min_user_permissions' ] ) ) { // i.e. no login/minimum perms
1155
- $sUrl = Services::WpGeneral()->getHomeUrl();
1156
- }
1157
- else {
1158
- $sUrl = Services::WpGeneral()->getAdminUrl( 'admin.php' );
1159
- }
1160
-
1161
- return add_query_arg(
1162
- [
1163
- 'page' => $this->getModSlug(),
1164
- 'shield_action' => 'wizard',
1165
- 'wizard' => $sWizardSlug,
1166
- 'nonwizard' => wp_create_nonce( 'wizard'.$sWizardSlug )
1167
- ],
1168
- $sUrl
1169
- );
1170
- }
1171
-
1172
- /**
1173
- * @return string
1174
- */
1175
- public function getUrl_WizardLanding() {
1176
- return $this->getUrl_Wizard( 'landing' );
1177
- }
1178
-
1179
- /**
1180
- * @param string $sWizardSlug
1181
- * @return array
1182
- */
1183
- public function getWizardDefinition( $sWizardSlug ) {
1184
- $aDef = null;
1185
- if ( $this->hasWizardDefinition( $sWizardSlug ) ) {
1186
- $aW = $this->getWizardDefinitions();
1187
- $aDef = $aW[ $sWizardSlug ];
1188
- }
1189
- return $aDef;
1190
- }
1191
-
1192
- /**
1193
- * @return array
1194
- */
1195
- public function getWizardDefinitions() {
1196
- $aW = $this->getDef( 'wizards' );
1197
- return is_array( $aW ) ? $aW : [];
1198
- }
1199
-
1200
- public function hasWizard() :bool {
1201
- return count( $this->getWizardDefinitions() ) > 0;
1202
- }
1203
-
1204
- /**
1205
- * @param string $sWizardSlug
1206
- * @return bool
1207
- */
1208
- public function hasWizardDefinition( $sWizardSlug ) {
1209
- $aW = $this->getWizardDefinitions();
1210
- return !empty( $aW[ $sWizardSlug ] );
1211
- }
1212
-
1213
- /**
1214
- * @return bool
1215
- */
1216
- public function getIsShowMarketing() {
1217
- return apply_filters( $this->prefix( 'show_marketing' ), !$this->isPremium() );
1218
- }
1219
-
1220
- /**
1221
- * @return string
1222
- */
1223
- public function renderOptionsForm() {
1224
-
1225
- if ( $this->canDisplayOptionsForm() ) {
1226
- $sTemplate = 'components/options_form/main.twig';
1227
- }
1228
- else {
1229
- $sTemplate = 'subfeature-access_restricted';
1230
- }
1231
-
1232
- try {
1233
- return $this->getCon()
1234
- ->getRenderer()
1235
- ->setTemplate( $sTemplate )
1236
- ->setRenderVars( $this->getUIHandler()->getBaseDisplayData() )
1237
- ->setTemplateEngineTwig()
1238
- ->render();
1239
- }
1240
- catch ( \Exception $oE ) {
1241
- return 'Error rendering options form: '.$oE->getMessage();
1242
- }
1243
- }
1244
-
1245
- /**
1246
- * @return bool
1247
- */
1248
- public function canDisplayOptionsForm() {
1249
- return $this->getOptions()->isAccessRestricted() ? $this->getCon()->isPluginAdmin() : true;
1250
- }
1251
-
1252
- public function onWpEnqueueAdminJs() {
1253
- $this->insertCustomJsVars_Admin();
1254
- }
1255
-
1256
- /**
1257
- * Override this with custom JS vars for your particular module.
1258
- */
1259
- public function insertCustomJsVars_Admin() {
1260
-
1261
- if ( $this->isThisModulePage() ) {
1262
- wp_localize_script(
1263
- $this->prefix( 'plugin' ),
1264
- 'icwp_wpsf_vars_base',
1265
- [
1266
- 'ajax' => [
1267
- 'mod_options' => $this->getAjaxActionData( 'mod_options' ),
1268
- 'mod_opts_form_render' => $this->getAjaxActionData( 'mod_opts_form_render' ),
1269
- ]
1270
- ]
1271
- );
1272
- }
1273
- }
1274
-
1275
- /**
1276
- * @param array $aData
1277
- * @param string $sSubView
1278
- */
1279
- protected function display( $aData = [], $sSubView = '' ) {
1280
- }
1281
-
1282
- /**
1283
- * @param array $aData
1284
- * @return string
1285
- * @throws \Exception
1286
- */
1287
- public function renderAdminNotice( $aData ) {
1288
- if ( empty( $aData[ 'notice_attributes' ] ) ) {
1289
- throw new \Exception( 'notice_attributes is empty' );
1290
- }
1291
-
1292
- if ( !isset( $aData[ 'icwp_admin_notice_template' ] ) ) {
1293
- $aData[ 'icwp_admin_notice_template' ] = $aData[ 'notice_attributes' ][ 'notice_id' ];
1294
- }
1295
-
1296
- if ( !isset( $aData[ 'notice_classes' ] ) ) {
1297
- $aData[ 'notice_classes' ] = [];
1298
- }
1299
- if ( is_array( $aData[ 'notice_classes' ] ) ) {
1300
- $aData[ 'notice_classes' ][] = $aData[ 'notice_attributes' ][ 'type' ];
1301
- if ( empty( $aData[ 'notice_classes' ] )
1302
- || ( !in_array( 'error', $aData[ 'notice_classes' ] ) && !in_array( 'updated', $aData[ 'notice_classes' ] ) ) ) {
1303
- $aData[ 'notice_classes' ][] = 'updated';
1304
- }
1305
- }
1306
- $aData[ 'notice_classes' ] = implode( ' ', $aData[ 'notice_classes' ] );
1307
-
1308
- $aAjaxData = $this->getAjaxActionData( 'dismiss_admin_notice' );
1309
- $aAjaxData[ 'hide' ] = 1;
1310
- $aAjaxData[ 'notice_id' ] = $aData[ 'notice_attributes' ][ 'notice_id' ];
1311
- $aData[ 'ajax' ][ 'dismiss_admin_notice' ] = json_encode( $aAjaxData );
1312
-
1313
- $bTwig = $aData[ 'notice_attributes' ][ 'twig' ];
1314
- $sTemplate = $bTwig ? '/notices/'.$aAjaxData[ 'notice_id' ] : 'notices/admin-notice-template';
1315
- return $this->renderTemplate( $sTemplate, $aData, $bTwig );
1316
- }
1317
-
1318
- public function renderTemplate( string $template, array $data = [], bool $isTwig = false ) :string {
1319
- if ( empty( $data[ 'unique_render_id' ] ) ) {
1320
- $data[ 'unique_render_id' ] = 'noticeid-'.substr( md5( mt_rand() ), 0, 5 );
1321
- }
1322
- try {
1323
- $oRndr = $this->getCon()->getRenderer();
1324
- if ( $isTwig || preg_match( '#^.*\.twig$#i', $template ) ) {
1325
- $oRndr->setTemplateEngineTwig();
1326
- }
1327
-
1328
- $data[ 'strings' ] = Services::DataManipulation()
1329
- ->mergeArraysRecursive(
1330
- $this->getStrings()->getDisplayStrings(),
1331
- $data[ 'strings' ] ?? []
1332
- );
1333
-
1334
- $render = $oRndr->setTemplate( $template )
1335
- ->setRenderVars( $data )
1336
- ->render();
1337
- }
1338
- catch ( \Exception $oE ) {
1339
- $render = $oE->getMessage();
1340
- error_log( $oE->getMessage() );
1341
- }
1342
-
1343
- return (string)$render;
1344
- }
1345
-
1346
- /**
1347
- * @param array $aTransferableOptions
1348
- * @return array
1349
- */
1350
- public function exportTransferableOptions( $aTransferableOptions ) {
1351
- if ( !is_array( $aTransferableOptions ) ) {
1352
- $aTransferableOptions = [];
1353
- }
1354
- $aTransferableOptions[ $this->getOptionsStorageKey() ] = $this->getOptions()->getTransferableOptions();
1355
- return $aTransferableOptions;
1356
- }
1357
-
1358
- public function collectOptionsForTracking() :array {
1359
- $opts = $this->getOptions();
1360
- $aOptionsData = $this->getOptions()->getOptionsForTracking();
1361
- foreach ( $aOptionsData as $opt => $mValue ) {
1362
- unset( $aOptionsData[ $opt ] );
1363
- // some cleaning to ensure we don't have disallowed characters
1364
- $opt = preg_replace( '#[^_a-z]#', '', strtolower( $opt ) );
1365
- $sType = $opts->getOptionType( $opt );
1366
- if ( $sType == 'checkbox' ) { // only want a boolean 1 or 0
1367
- $aOptionsData[ $opt ] = (int)( $mValue == 'Y' );
1368
- }
1369
- else {
1370
- $aOptionsData[ $opt ] = $mValue;
1371
- }
1372
- }
1373
- return $aOptionsData;
1374
- }
1375
-
1376
- public function getMainWpData() :array {
1377
- return [
1378
- 'options' => $this->getOptions()->getTransferableOptions()
1379
- ];
1380
- }
1381
-
1382
- /**
1383
- * See plugin controller for the nature of $aData wpPrivacyExport()
1384
- * @param array $aExportItems
1385
- * @param string $sEmail
1386
- * @param int $nPage
1387
- * @return array
1388
- */
1389
- public function onWpPrivacyExport( $aExportItems, $sEmail, $nPage = 1 ) {
1390
- return $aExportItems;
1391
- }
1392
-
1393
- /**
1394
- * See plugin controller for the nature of $aData wpPrivacyErase()
1395
- * @param array $aData
1396
- * @param string $sEmail
1397
- * @param int $nPage
1398
- * @return array
1399
- */
1400
- public function onWpPrivacyErase( $aData, $sEmail, $nPage = 1 ) {
1401
- return $aData;
1402
- }
1403
-
1404
- /**
1405
- * @return null|Shield\Modules\Base\ShieldOptions|mixed
1406
- */
1407
- public function getOptions() {
1408
- if ( !isset( $this->oOpts ) ) {
1409
- $oCon = $this->getCon();
1410
- $this->oOpts = $this->loadModElement( 'Options' );
1411
- $this->oOpts->setPathToConfig( $oCon->getPath_ConfigFile( $this->getSlug() ) )
1412
- ->setRebuildFromFile( $oCon->getIsRebuildOptionsFromFile() )
1413
- ->setOptionsStorageKey( $this->getOptionsStorageKey() )
1414
- ->setIfLoadOptionsFromStorage( !$oCon->getIsResetPlugin() );
1415
- }
1416
- return $this->oOpts;
1417
- }
1418
-
1419
- /**
1420
- * @return Shield\Modules\Base\WpCli
1421
- * @throws \Exception
1422
- */
1423
- public function getWpCli() {
1424
- if ( !isset( $this->oWpCli ) ) {
1425
- $this->oWpCli = $this->loadModElement( 'WpCli' );
1426
- if ( !$this->oWpCli instanceof Shield\Modules\Base\WpCli ) {
1427
- throw new \Exception( 'WP-CLI not supported' );
1428
- }
1429
- }
1430
- return $this->oWpCli;
1431
- }
1432
-
1433
- /**
1434
- * @return null|Shield\Modules\Base\Strings
1435
- */
1436
- public function getStrings() {
1437
- return $this->loadStrings()->setMod( $this );
1438
- }
1439
-
1440
- /**
1441
- * @return Shield\Modules\Base\UI
1442
- */
1443
- public function getUIHandler() {
1444
- if ( !isset( $this->oUI ) ) {
1445
- $this->oUI = $this->loadModElement( 'UI' );
1446
- if ( !$this->oUI instanceof Shield\Modules\Base\UI ) {
1447
- // TODO: autoloader for base classes
1448
- $this->oUI = $this->loadModElement( 'ShieldUI' );
1449
- }
1450
- }
1451
- return $this->oUI;
1452
- }
1453
-
1454
- /**
1455
- * @return Shield\Modules\Base\BaseReporting|mixed|false
1456
- */
1457
- public function getReportingHandler() {
1458
- if ( !isset( $this->oReporting ) ) {
1459
- $this->oReporting = $this->loadModElement( 'Reporting' );
1460
- }
1461
- return $this->oReporting;
1462
- }
1463
-
1464
- protected function loadAdminNotices() {
1465
- $N = $this->loadModElement( 'AdminNotices' );
1466
- if ( $N instanceof Shield\Modules\Base\AdminNotices ) {
1467
- $N->run();
1468
- }
1469
- }
1470
-
1471
- protected function loadAjaxHandler() {
1472
- $oAj = $this->loadModElement( 'AjaxHandler' );
1473
- if ( !$oAj instanceof Shield\Modules\Base\AjaxHandlerBase ) {
1474
- $this->loadModElement( 'AjaxHandlerShield' );
1475
- }
1476
- }
1477
-
1478
- /**
1479
- * @return Shield\Modules\Base\ShieldOptions|mixed
1480
- * @deprecated 10.1
1481
- */
1482
- protected function loadOptions() {
1483
- return $this->loadModElement( 'Options' );
1484
- }
1485
-
1486
- protected function loadDebug() {
1487
- $req = Services::Request();
1488
- if ( $req->query( 'debug' ) && $req->query( 'mod' ) == $this->getModSlug()
1489
- && $this->getCon()->isPluginAdmin() ) {
1490
- /** @var Shield\Modules\Base\Debug $debug */
1491
- $debug = $this->loadModElement( 'Debug', true );
1492
- $debug->run();
1493
- }
1494
- }
1495
-
1496
- /**
1497
- * @return Shield\Modules\Base\Strings|mixed
1498
- */
1499
- protected function loadStrings() {
1500
- return $this->loadModElement( 'Strings', true );
1501
- }
1502
-
1503
- /**
1504
- * @param $sClass
1505
- * @return \stdClass|mixed|false
1506
- * @deprecated 10.1
1507
- */
1508
- private function loadClass( $sClass ) {
1509
- $sC = $this->getNamespace().$sClass;
1510
- return @class_exists( $sC ) ? new $sC() : false;
1511
- }
1512
-
1513
- /**
1514
- * @param string $class
1515
- * @param false $injectMod
1516
- * @return false|Shield\Modules\ModConsumer
1517
- */
1518
- private function loadModElement( string $class, $injectMod = true ) {
1519
- $element = false;
1520
- try {
1521
- $C = $this->findElementClass( $class, true );
1522
- /** @var Shield\Modules\ModConsumer $element */
1523
- $element = @class_exists( $C ) ? new $C() : false;
1524
- if ( $injectMod && method_exists( $element, 'setMod' ) ) {
1525
- $element->setMod( $this );
1526
- }
1527
- }
1528
- catch ( \Exception $e ) {
1529
- }
1530
- return $element;
1531
- }
1532
-
1533
- /**
1534
- * @param $sClass
1535
- * @return \stdClass|mixed|false
1536
- * @deprecated 10.1
1537
- */
1538
- private function loadClassFromBase( $sClass ) {
1539
- $sC = $this->getBaseNamespace().$sClass;
1540
- return @class_exists( $sC ) ? new $sC() : false;
1541
- }
1542
-
1543
- /**
1544
- * @param string $element
1545
- * @param bool $bThrowException
1546
- * @return string|null
1547
- * @throws \Exception
1548
- */
1549
- protected function findElementClass( string $element, $bThrowException = true ) {
1550
- $theClass = null;
1551
-
1552
- foreach ( $this->getNamespaceRoots() as $NS ) {
1553
- $maybe = $NS.$element;
1554
- if ( @class_exists( $maybe ) ) {
1555
- if ( ( new ReflectionClass( $maybe ) )->isInstantiable() ) {
1556
- $theClass = $maybe;
1557
- break;
1558
- }
1559
- }
1560
- }
1561
-
1562
- if ( $bThrowException && is_null( $theClass ) ) {
1563
- throw new \Exception( sprintf( 'Could not find class for element "%s".', $element ) );
1564
- }
1565
- return $theClass;
1566
- }
1567
-
1568
- private function getBaseNamespace() {
1569
- return $this->getModulesNamespace().'Base\\';
1570
- }
1571
-
1572
- protected function getModulesNamespace() :string {
1573
- return '\FernleafSystems\Wordpress\Plugin\Shield\Modules\\';
1574
- }
1575
-
1576
- protected function getNamespace() :string {
1577
- return $this->getModulesNamespace().$this->getNamespaceBase().'\\';
1578
- }
1579
-
1580
- protected function getNamespaceRoots() :array {
1581
- return [
1582
- $this->getNamespace(),
1583
- $this->getBaseNamespace()
1584
- ];
1585
- }
1586
-
1587
- protected function getNamespaceBase() :string {
1588
- return 'Base';
1589
- }
1590
-
1591
- /**
1592
- * Saves the options to the WordPress Options store.
1593
- * @return void
1594
- * @deprecated 8.4
1595
- */
1596
- public function savePluginOptions() {
1597
- $this->saveModOptions();
1598
- }
1599
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/features/base_wpsf.php DELETED
@@ -1,262 +0,0 @@
1
- <?php
2
-
3
- use FernleafSystems\Wordpress\Plugin\Shield;
4
- use FernleafSystems\Wordpress\Plugin\Shield\Modules\Plugin;
5
- use FernleafSystems\Wordpress\Services\Services;
6
- use FernleafSystems\Wordpress\Services\Utilities;
7
- use FernleafSystems\Wordpress\Services\Utilities\Net\IpIdentify;
8
-
9
- class ICWP_WPSF_FeatureHandler_BaseWpsf extends ICWP_WPSF_FeatureHandler_Base {
10
-
11
- /**
12
- * @var bool
13
- */
14
- protected static $bIsVerifiedBot;
15
-
16
- /**
17
- * @var bool
18
- */
19
- private static $bVisitorIsWhitelisted;
20
-
21
- /**
22
- * @return bool
23
- */
24
- public function canCacheDirWrite() {
25
- return ( new Shield\Modules\Plugin\Lib\TestCacheDirWrite() )
26
- ->setMod( $this->getCon()->getModule_Plugin() )
27
- ->canWrite();
28
- }
29
-
30
- /**
31
- * @return \ICWP_WPSF_Processor_Sessions
32
- */
33
- public function getSessionsProcessor() {
34
- return $this->getCon()
35
- ->getModule_Sessions()
36
- ->getProcessor();
37
- }
38
-
39
- /**
40
- * @return Shield\Databases\Session\Handler
41
- */
42
- public function getDbHandler_Sessions() {
43
- return $this->getCon()
44
- ->getModule_Sessions()
45
- ->getDbHandler_Sessions();
46
- }
47
-
48
- /**
49
- * @return Shield\Databases\Session\EntryVO|null
50
- */
51
- public function getSession() {
52
- $oP = $this->getSessionsProcessor();
53
- return is_null( $oP ) ? null : $oP->getCurrentSession();
54
- }
55
-
56
- /**
57
- * @return bool
58
- */
59
- public function hasSession() {
60
- return $this->getSession() instanceof Shield\Databases\Session\EntryVO;
61
- }
62
-
63
- /**
64
- * @return bool
65
- */
66
- public function hasValidRequestIP() {
67
- return Services::IP()->isValidIp( Services::IP()->getRequestIp() );
68
- }
69
-
70
- public function onWpInit() {
71
- parent::onWpInit();
72
- if ( $this->isThisModulePage() && !$this->isWizardPage() && ( $this->getSlug() != 'insights' ) ) {
73
- $this->redirectToInsightsSubPage();
74
- }
75
- }
76
-
77
- protected function redirectToInsightsSubPage() {
78
- Services::Response()->redirect(
79
- $this->getCon()->getModule_Insights()->getUrl_AdminPage(),
80
- [
81
- 'inav' => 'settings',
82
- 'subnav' => $this->getSlug()
83
- ],
84
- true, false
85
- );
86
- }
87
-
88
- /**
89
- * @return Plugin\Lib\Captcha\CaptchaConfigVO
90
- */
91
- public function getCaptchaCfg() {
92
- $oPlugMod = $this->getCon()->getModule_Plugin();
93
- /** @var Shield\Modules\Plugin\Options $oOpts */
94
- $oOpts = $oPlugMod->getOptions();
95
- $oCfg = ( new Plugin\Lib\Captcha\CaptchaConfigVO() )->applyFromArray( $oOpts->getCaptchaConfig() );
96
- $oCfg->invisible = $oCfg->theme === 'invisible';
97
-
98
- if ( $oCfg->provider === Plugin\Lib\Captcha\CaptchaConfigVO::PROV_GOOGLE_RECAP2 ) {
99
- $oCfg->url_api = 'https://www.google.com/recaptcha/api.js';
100
- }
101
- elseif ( $oCfg->provider === Plugin\Lib\Captcha\CaptchaConfigVO::PROV_HCAPTCHA ) {
102
- $oCfg->url_api = 'https://hcaptcha.com/1/api.js';
103
- }
104
- else {
105
- error_log( 'CAPTCHA Provider not supported: '.$oCfg->provider );
106
- }
107
-
108
- $oCfg->js_handle = $this->getCon()->prefix( $oCfg->provider );
109
-
110
- return $oCfg;
111
- }
112
-
113
- /**
114
- * @return array
115
- */
116
- public function getSecAdminLoginAjaxData() {
117
- // We set a custom mod_slug so that this module handles the ajax request
118
- $aAjaxData = $this->getAjaxActionData( 'sec_admin_login' );
119
- $aAjaxData[ 'mod_slug' ] = $this->prefix( 'admin_access_restriction' );
120
- return $aAjaxData;
121
- }
122
-
123
- /**
124
- * @return array
125
- */
126
- protected function getSecAdminCheckAjaxData() {
127
- // We set a custom mod_slug so that this module handles the ajax request
128
- $aAjaxData = $this->getAjaxActionData( 'sec_admin_check' );
129
- $aAjaxData[ 'mod_slug' ] = $this->prefix( 'admin_access_restriction' );
130
- return $aAjaxData;
131
- }
132
-
133
- public function getPluginReportEmail() :string {
134
- return $this->getCon()
135
- ->getModule_Plugin()
136
- ->getPluginReportEmail();
137
- }
138
-
139
- /**
140
- * @uses echo()
141
- */
142
- public function displayModuleAdminPage() {
143
- if ( $this->canDisplayOptionsForm() ) {
144
- parent::displayModuleAdminPage();
145
- }
146
- else {
147
- echo $this->renderRestrictedPage();
148
- }
149
- }
150
-
151
- /**
152
- * @return string
153
- */
154
- protected function renderRestrictedPage() {
155
- /** @var Shield\Modules\SecurityAdmin\Options $oSecOpts */
156
- $oSecOpts = $this->getCon()
157
- ->getModule_SecAdmin()
158
- ->getOptions();
159
- $aData = Services::DataManipulation()
160
- ->mergeArraysRecursive(
161
- $this->getUIHandler()->getBaseDisplayData(),
162
- [
163
- 'ajax' => [
164
- 'restricted_access' => $this->getAjaxActionData( 'restricted_access' ),
165
- ],
166
- 'strings' => [
167
- 'force_remove_email' => __( "If you've forgotten your PIN, a link can be sent to the plugin administrator email address to remove this restriction.", 'wp-simple-firewall' ),
168
- 'click_email' => __( "Click here to send the verification email.", 'wp-simple-firewall' ),
169
- 'send_to_email' => sprintf( __( "Email will be sent to %s", 'wp-simple-firewall' ),
170
- Utilities\Obfuscate::Email( $this->getPluginReportEmail() ) ),
171
- 'no_email_override' => __( "The Security Administrator has restricted the use of the email override feature.", 'wp-simple-firewall' ),
172
- ],
173
- 'flags' => [
174
- 'allow_email_override' => $oSecOpts->isEmailOverridePermitted()
175
- ]
176
- ]
177
- );
178
- return $this->renderTemplate( '/wpadmin_pages/security_admin/index.twig', $aData, true );
179
- }
180
-
181
- /**
182
- * @return bool
183
- */
184
- public function getIfSupport3rdParty() {
185
- return $this->isPremium();
186
- }
187
-
188
- /**
189
- * @return bool
190
- * @throws \Exception
191
- */
192
- protected function isReadyToExecute() {
193
- $opts = $this->getOptions();
194
- return ( $opts->isModuleRunIfWhitelisted() || !$this->isVisitorWhitelisted() )
195
- && ( $opts->isModuleRunIfVerifiedBot() || !$this->isVerifiedBot() )
196
- && ( $opts->isModuleRunUnderWpCli() || !Services::WpGeneral()->isWpCli() )
197
- && parent::isReadyToExecute();
198
- }
199
-
200
- public function isVisitorWhitelisted() :bool {
201
- if ( !isset( self::$bVisitorIsWhitelisted ) ) {
202
- self::$bVisitorIsWhitelisted =
203
- ( new Shield\Modules\IPs\Lib\Ops\LookupIpOnList() )
204
- ->setDbHandler( $this->getCon()->getModule_IPs()->getDbHandler_IPs() )
205
- ->setIP( Services::IP()->getRequestIp() )
206
- ->setListTypeWhite()
207
- ->lookup()
208
- instanceof Shield\Databases\IPs\EntryVO;
209
- }
210
- return self::$bVisitorIsWhitelisted;
211
- }
212
-
213
- public function isVerifiedBot() :bool {
214
- if ( !isset( self::$bIsVerifiedBot ) ) {
215
- $srvIP = Services::IP();
216
- self::$bIsVerifiedBot = !$srvIP->isLoopback() &&
217
- !in_array( $srvIP->getIpDetector()->getIPIdentity(), [
218
- IpIdentify::UNKNOWN,
219
- IpIdentify::THIS_SERVER,
220
- IpIdentify::VISITOR,
221
- ] );
222
- }
223
- return self::$bIsVerifiedBot;
224
- }
225
-
226
- public function isXmlrpcBypass() :bool {
227
- return $this->getCon()
228
- ->getModule_Plugin()
229
- ->isXmlrpcBypass();
230
- }
231
-
232
- /**
233
- * @param string[] $aArray
234
- * @param string $sPregReplacePattern
235
- * @return string[]
236
- */
237
- protected function cleanStringArray( $aArray, $sPregReplacePattern ) {
238
- $aCleaned = [];
239
- if ( !is_array( $aArray ) ) {
240
- return $aCleaned;
241
- }
242
-
243
- foreach ( $aArray as $nKey => $sVal ) {
244
- $sVal = preg_replace( $sPregReplacePattern, '', $sVal );
245
- if ( !empty( $sVal ) ) {
246
- $aCleaned[] = $sVal;
247
- }
248
- }
249
- return array_unique( array_filter( $aCleaned ) );
250
- }
251
-
252
- protected function getNamespaceRoots() :array {
253
- // Ensure order of namespaces is 'Module', 'Shield', then 'Base'
254
- return array_unique( array_merge(
255
- [
256
- $this->getNamespace(),
257
- $this->getModulesNamespace().'BaseShield\\',
258
- ],
259
- parent::getNamespaceRoots()
260
- ) );
261
- }
262
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/features/comments_filter.php DELETED
@@ -1,112 +0,0 @@
1
- <?php
2
-
3
- use FernleafSystems\Wordpress\Plugin\Shield;
4
- use FernleafSystems\Wordpress\Plugin\Shield\Modules\CommentsFilter;
5
-
6
- /**
7
- * @deprecated 10.1
8
- */
9
- class ICWP_WPSF_FeatureHandler_CommentsFilter extends ICWP_WPSF_FeatureHandler_BaseWpsf {
10
-
11
- /**
12
- * @return Shield\Modules\Plugin\Lib\Captcha\CaptchaConfigVO
13
- */
14
- public function getCaptchaCfg() {
15
- $oCfg = parent::getCaptchaCfg();
16
- $sStyle = $this->getOptions()->getOpt( 'google_recaptcha_style_comments' );
17
- if ( $sStyle !== 'default' && $this->isPremium() ) {
18
- $oCfg->theme = $sStyle;
19
- $oCfg->invisible = $oCfg->theme == 'invisible';
20
- }
21
- return $oCfg;
22
- }
23
-
24
- public function ensureCorrectCaptchaConfig() {
25
- /** @var CommentsFilter\Options $opts */
26
- $opts = $this->getOptions();
27
-
28
- $sStyle = $opts->getOpt( 'google_recaptcha_style_comments' );
29
- if ( $this->isPremium() ) {
30
- $oCfg = $this->getCaptchaCfg();
31
- if ( $oCfg->provider == $oCfg::PROV_GOOGLE_RECAP2 ) {
32
- if ( !$oCfg->invisible && $sStyle == 'invisible' ) {
33
- $opts->setOpt( 'google_recaptcha_style_comments', 'default' );
34
- }
35
- }
36
- }
37
- elseif ( !in_array( $sStyle, [ 'disabled', 'default' ] ) ) {
38
- $opts->setOpt( 'google_recaptcha_style_comments', 'default' );
39
- }
40
- }
41
-
42
- /**
43
- * @param string $sOptKey
44
- * @return string
45
- */
46
- public function getTextOptDefault( $sOptKey ) {
47
-
48
- switch ( $sOptKey ) {
49
- case 'custom_message_checkbox':
50
- $sText = __( "I'm not a spammer.", 'wp-simple-firewall' );
51
- break;
52
- case 'custom_message_alert':
53
- $sText = __( "Please check the box to confirm you're not a spammer.", 'wp-simple-firewall' );
54
- break;
55
- case 'custom_message_comment_wait':
56
- $sText = __( "Please wait %s seconds before posting your comment.", 'wp-simple-firewall' );
57
- break;
58
- case 'custom_message_comment_reload':
59
- $sText = __( "Please reload this page to post a comment.", 'wp-simple-firewall' );
60
- break;
61
- default:
62
- $sText = parent::getTextOptDefault( $sOptKey );
63
- break;
64
- }
65
- return $sText;
66
- }
67
-
68
- protected function preProcessOptions() {
69
- /** @var CommentsFilter\Options $opts */
70
- $opts = $this->getOptions();
71
-
72
- // clean roles
73
- $opts->setOpt( 'trusted_user_roles',
74
- array_unique( array_filter( array_map(
75
- function ( $sRole ) {
76
- return sanitize_key( strtolower( $sRole ) );
77
- },
78
- $opts->getTrustedRoles()
79
- ) ) )
80
- );
81
-
82
- $this->ensureCorrectCaptchaConfig();
83
- }
84
-
85
- /**
86
- * @return bool
87
- */
88
- public function isEnabledCaptcha() {
89
- /** @var CommentsFilter\Options $opts */
90
- $opts = $this->getOptions();
91
- return $this->isModOptEnabled() && !$opts->isEnabledCaptcha()
92
- && $this->getCaptchaCfg()->ready;
93
- }
94
-
95
- public function setEnabledGasp( bool $enabled = true ) {
96
- $this->getOptions()->setOpt( 'enable_comments_gasp_protection', $enabled ? 'Y' : 'N' );
97
- }
98
-
99
- /**
100
- * @return string
101
- */
102
- protected function getNamespaceBase() :string {
103
- return 'CommentsFilter';
104
- }
105
-
106
- /**
107
- * @return string
108
- */
109
- public function getSpamBlacklistFile() {
110
- return $this->getCon()->getPluginCachePath( 'spamblacklist.txt' );
111
- }
112
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/features/comms.php DELETED
@@ -1,19 +0,0 @@
1
- <?php declare( strict_types=1 );
2
-
3
- use FernleafSystems\Wordpress\Plugin\Shield\Modules\Comms;
4
-
5
- /**
6
- * Class ICWP_WPSF_FeatureHandler_Comms
7
- * @deprecated 10.1
8
- */
9
- class ICWP_WPSF_FeatureHandler_Comms extends ICWP_WPSF_FeatureHandler_BaseWpsf {
10
-
11
- public function getSureSendController() :Comms\Lib\SureSend\SureSendController {
12
- return ( new Comms\Lib\SureSend\SureSendController() )
13
- ->setMod( $this );
14
- }
15
-
16
- protected function getNamespaceBase() :string {
17
- return 'Comms';
18
- }
19
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/features/email.php DELETED
@@ -1,11 +0,0 @@
1
- <?php declare( strict_types=1 );
2
-
3
- /**
4
- * @deprecated 10.1
5
- */
6
- class ICWP_WPSF_FeatureHandler_Email extends ICWP_WPSF_FeatureHandler_BaseWpsf {
7
-
8
- protected function getNamespaceBase() :string {
9
- return 'Email';
10
- }
11
- }
 
 
 
 
 
 
 
 
 
 
 
src/features/events.php DELETED
@@ -1,34 +0,0 @@
1
- <?php
2
-
3
- use FernleafSystems\Wordpress\Plugin\Shield;
4
-
5
- /**
6
- * Class ICWP_WPSF_FeatureHandler_Events
7
- * @deprecated 10.1
8
- */
9
- class ICWP_WPSF_FeatureHandler_Events extends ICWP_WPSF_FeatureHandler_BaseWpsf {
10
-
11
- /**
12
- * @return false|Shield\Databases\Events\Handler
13
- */
14
- public function getDbHandler_Events() {
15
- return $this->getDbH( 'events' );
16
- }
17
-
18
- /**
19
- * @return bool
20
- * @throws \Exception
21
- */
22
- protected function isReadyToExecute() {
23
- return ( $this->getDbHandler_Events() instanceof Shield\Databases\Events\Handler )
24
- && $this->getDbHandler_Events()->isReady()
25
- && parent::isReadyToExecute();
26
- }
27
-
28
- /**
29
- * @return string
30
- */
31
- protected function getNamespaceBase() :string {
32
- return 'Events';
33
- }
34
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/features/firewall.php DELETED
@@ -1,68 +0,0 @@
1
- <?php
2
-
3
- use FernleafSystems\Wordpress\Plugin\Shield;
4
- use FernleafSystems\Wordpress\Plugin\Shield\Modules\Firewall;
5
-
6
- /**
7
- * @deprecated 10.1
8
- */
9
- class ICWP_WPSF_FeatureHandler_Firewall extends ICWP_WPSF_FeatureHandler_BaseWpsf {
10
-
11
- /**
12
- * @param string $sParam
13
- * @param string $sPage
14
- */
15
- public function addParamToWhitelist( $sParam, $sPage = '*' ) {
16
- /** @var Firewall\Options $oOpts */
17
- $oOpts = $this->getOptions();
18
-
19
- if ( empty( $sPage ) ) {
20
- $sPage = '*';
21
- }
22
-
23
- $aW = $oOpts->getCustomWhitelist();
24
- $aParams = isset( $aW[ $sPage ] ) ? $aW[ $sPage ] : [];
25
- $aParams[] = $sParam;
26
- natsort( $aParams );
27
- $aW[ $sPage ] = array_unique( $aParams );
28
-
29
- $oOpts->setOpt( 'page_params_whitelist', $aW );
30
- }
31
-
32
- /**
33
- * @return string
34
- */
35
- public function getBlockResponse() {
36
- $response = $this->getOptions()->getOpt( 'block_response', '' );
37
- return !empty( $response ) ? $response : 'redirect_die_message'; // TODO: use default
38
- }
39
-
40
- /**
41
- * @param string $sOptKey
42
- * @return string
43
- */
44
- public function getTextOptDefault( $sOptKey ) {
45
-
46
- switch ( $sOptKey ) {
47
- case 'text_firewalldie':
48
- $sText = sprintf(
49
- __( "You were blocked by the %s.", 'wp-simple-firewall' ),
50
- '<a href="https://wordpress.org/plugins/wp-simple-firewall/" target="_blank">'.$this->getCon()
51
- ->getHumanName().'</a>'
52
- );
53
- break;
54
-
55
- default:
56
- $sText = parent::getTextOptDefault( $sOptKey );
57
- break;
58
- }
59
- return $sText;
60
- }
61
-
62
- /**
63
- * @return string
64
- */
65
- protected function getNamespaceBase() :string {
66
- return 'Firewall';
67
- }
68
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/features/hack_protect.php DELETED
@@ -1,326 +0,0 @@
1
- <?php
2
-
3
- use FernleafSystems\Wordpress\Plugin\Shield;
4
- use FernleafSystems\Wordpress\Plugin\Shield\Modules\HackGuard;
5
- use FernleafSystems\Wordpress\Services\Services;
6
-
7
- /**
8
- * @deprecated 10.1
9
- */
10
- class ICWP_WPSF_FeatureHandler_HackProtect extends ICWP_WPSF_FeatureHandler_BaseWpsf {
11
-
12
- /**
13
- * @var HackGuard\Scan\Queue\Controller
14
- */
15
- private $oScanQueueController;
16
-
17
- /**
18
- * @var HackGuard\Scan\Controller\Base[]
19
- */
20
- private $aScanCons;
21
-
22
- /**
23
- * @var HackGuard\Lib\FileLocker\FileLockerController
24
- */
25
- private $oFileLocker;
26
-
27
- protected function doPostConstruction() {
28
- $this->setCustomCronSchedules();
29
- }
30
-
31
- public function onWpInit() {
32
- parent::onWpInit();
33
- $this->getScanQueueController();
34
- }
35
-
36
- public function getFileLocker() :HackGuard\Lib\FileLocker\FileLockerController {
37
- if ( !isset( $this->oFileLocker ) ) {
38
- $this->oFileLocker = ( new HackGuard\Lib\FileLocker\FileLockerController() )
39
- ->setMod( $this );
40
- }
41
- return $this->oFileLocker;
42
- }
43
-
44
- public function getScanQueueController() :HackGuard\Scan\Queue\Controller {
45
- if ( !isset( $this->oScanQueueController ) ) {
46
- $this->oScanQueueController = ( new HackGuard\Scan\Queue\Controller() )
47
- ->setMod( $this );
48
- }
49
- return $this->oScanQueueController;
50
- }
51
-
52
- /**
53
- * @return HackGuard\Scan\Controller\Base[]
54
- */
55
- public function getAllScanCons() :array {
56
- /** @var HackGuard\Options $opts */
57
- $opts = $this->getOptions();
58
- foreach ( $opts->getScanSlugs() as $scanSlug ) {
59
- $this->getScanCon( $scanSlug );
60
- }
61
- return $this->aScanCons;
62
- }
63
-
64
- /**
65
- * @param string $slug
66
- * @return HackGuard\Scan\Controller\Base|mixed
67
- */
68
- public function getScanCon( string $slug ) {
69
- if ( !is_array( $this->aScanCons ) ) {
70
- $this->aScanCons = [];
71
- }
72
- if ( !isset( $this->aScanCons[ $slug ] ) ) {
73
- $class = $this->getNamespace().'Scan\Controller\\'.ucwords( $slug );
74
- if ( @class_exists( $class ) ) {
75
- /** @var HackGuard\Scan\Controller\Base $oObj */
76
- $oObj = new $class();
77
- $this->aScanCons[ $slug ] = $oObj->setMod( $this );
78
- }
79
- else {
80
- $this->aScanCons[ $slug ] = false;
81
- }
82
- }
83
- return $this->aScanCons[ $slug ];
84
- }
85
-
86
- public function getMainWpData() :array {
87
- $issues = ( new HackGuard\Lib\Reports\Query\ScanCounts() )->setMod( $this );
88
- $issues->notified = null;
89
- return array_merge( parent::getMainWpData(), [
90
- 'scan_issues' => array_filter( $issues->all() )
91
- ] );
92
- }
93
-
94
- protected function handleModAction( string $sAction ) {
95
- switch ( $sAction ) {
96
- case 'scan_file_download':
97
- ( new HackGuard\Lib\Utility\FileDownloadHandler() )
98
- ->setDbHandler( $this->getDbHandler_ScanResults() )
99
- ->downloadByItemId( (int)Services::Request()->query( 'rid', 0 ) );
100
- break;
101
- case 'filelocker_download_original':
102
- case 'filelocker_download_current':
103
- $this->getFileLocker()->handleFileDownloadRequest();
104
- break;
105
- default:
106
- break;
107
- }
108
- }
109
-
110
- protected function preProcessOptions() {
111
- /** @var HackGuard\Options $opts */
112
- $opts = $this->getOptions();
113
-
114
- $this->cleanFileExclusions();
115
-
116
- if ( count( $opts->getFilesToLock() ) === 0 || !$this->getCon()
117
- ->getModule_Plugin()
118
- ->getShieldNetApiController()
119
- ->canHandshake() ) {
120
- $opts->setOpt( 'file_locker', [] );
121
- $this->getFileLocker()->purge();
122
- }
123
-
124
- $lockFiles = $opts->getFilesToLock();
125
- if ( in_array( 'root_webconfig', $lockFiles ) && !Services::Data()->isWindows() ) {
126
- unset( $lockFiles[ array_search( 'root_webconfig', $lockFiles ) ] );
127
- $opts->setOpt( 'file_locker', $lockFiles );
128
- }
129
-
130
- foreach ( $this->getAllScanCons() as $con ) {
131
- if ( !$con->isEnabled() ) {
132
- $con->purge();
133
- }
134
- }
135
- }
136
-
137
- /**
138
- * @return int[] - key is scan slug
139
- */
140
- public function getLastScansAt() :array {
141
- /** @var HackGuard\Options $oOpts */
142
- $oOpts = $this->getOptions();
143
- /** @var Shield\Databases\Events\Select $oSel */
144
- $oSel = $this->getCon()
145
- ->getModule_Events()
146
- ->getDbHandler_Events()
147
- ->getQuerySelector();
148
- $aEvents = $oSel->getLatestForAllEvents();
149
-
150
- $aLatest = [];
151
- foreach ( $oOpts->getScanSlugs() as $sScan ) {
152
- $sEvt = $sScan.'_scan_run';
153
- $aLatest[ $sScan ] = isset( $aEvents[ $sEvt ] ) ? $aEvents[ $sEvt ]->created_at : 0;
154
- }
155
- return $aLatest;
156
- }
157
-
158
- /**
159
- * @param string $scan ptg, wcf, ufc, wpv
160
- * @return int
161
- */
162
- public function getLastScanAt( $scan ) {
163
- /** @var Shield\Databases\Events\Select $oSel */
164
- $oSel = $this->getCon()
165
- ->getModule_Events()
166
- ->getDbHandler_Events()
167
- ->getQuerySelector();
168
- $oEntry = $oSel->getLatestForEvent( $scan.'_scan_run' );
169
- return ( $oEntry instanceof Shield\Databases\Events\EntryVO ) ? $oEntry->created_at : 0;
170
- }
171
-
172
- /**
173
- * @return $this
174
- */
175
- protected function setCustomCronSchedules() {
176
- /** @var HackGuard\Options $opts */
177
- $opts = $this->getOptions();
178
- $freq = $opts->getScanFrequency();
179
- Services::WpCron()
180
- ->addNewSchedule(
181
- $this->prefix( sprintf( 'per-day-%s', $freq ) ),
182
- [
183
- 'interval' => DAY_IN_SECONDS/$freq,
184
- 'display' => sprintf( __( '%s per day', 'wp-simple-firewall' ), $freq )
185
- ]
186
- );
187
- return $this;
188
- }
189
-
190
- protected function cleanFileExclusions() {
191
- /** @var HackGuard\Options $opts */
192
- $opts = $this->getOptions();
193
- $aExclusions = [];
194
-
195
- $aToClean = $opts->getOpt( 'ufc_exclusions', [] );
196
- if ( is_array( $aToClean ) ) {
197
- foreach ( $aToClean as $nKey => $sExclusion ) {
198
- $sExclusion = wp_normalize_path( trim( $sExclusion ) );
199
-
200
- if ( preg_match( '/^#(.+)#$/', $sExclusion, $aMatches ) ) { // it's regex
201
- // ignore it
202
- }
203
- elseif ( strpos( $sExclusion, '/' ) === false ) { // filename only
204
- $sExclusion = trim( preg_replace( '#[^.0-9a-z_-]#i', '', $sExclusion ) );
205
- }
206
-
207
- if ( !empty( $sExclusion ) ) {
208
- $aExclusions[] = $sExclusion;
209
- }
210
- }
211
- }
212
-
213
- $opts->setOpt( 'ufc_exclusions', array_unique( $aExclusions ) );
214
- }
215
-
216
- /**
217
- * @return bool
218
- */
219
- public function isWpvulnPluginsHighlightEnabled() {
220
- $oWpvCon = $this->getScanCon( 'wpv' );
221
- if ( $oWpvCon->isEnabled() ) {
222
- $sOpt = apply_filters( 'icwp_shield_wpvuln_scan_display', 'securityadmin' );
223
- }
224
- else {
225
- $sOpt = 'disabled';
226
- }
227
- return ( $sOpt != 'disabled' ) && Services::WpUsers()->isUserAdmin()
228
- && ( ( $sOpt != 'securityadmin' ) || $this->getCon()->isPluginAdmin() );
229
- }
230
-
231
- public function isPtgEnabled() :bool {
232
- $opts = $this->getOptions();
233
- return $this->isModuleEnabled() && $this->isPremium()
234
- && $opts->isOpt( 'ptg_enable', 'enabled' )
235
- && $opts->isOptReqsMet( 'ptg_enable' )
236
- && $this->canCacheDirWrite();
237
- }
238
-
239
- public function insertCustomJsVars_Admin() {
240
- parent::insertCustomJsVars_Admin();
241
-
242
- /** @var HackGuard\Options $opts */
243
- $opts = $this->getOptions();
244
- if ( Services::WpPost()->isCurrentPage( 'plugins.php' )
245
- && $opts->isPtgReinstallLinks() && $this->getScanCon( 'ptg' )->isReady() ) {
246
- wp_localize_script(
247
- $this->prefix( 'global-plugin' ),
248
- 'icwp_wpsf_vars_hp',
249
- [
250
- 'ajax_plugin_reinstall' => $this->getAjaxActionData( 'plugin_reinstall' ),
251
- 'reinstallable' => Services::WpPlugins()->getInstalledWpOrgPluginFiles(),
252
- 'strings' => [
253
- 'reinstall_first' => __( 'Re-install First', 'wp-simple-firewall' )
254
- .'. '.__( 'Then Activate', 'wp-simple-firewall' ),
255
- 'okay_reinstall' => sprintf( '%s, %s',
256
- __( 'Yes', 'wp-simple-firewall' ), __( 'Re-Install It', 'wp-simple-firewall' ) ),
257
- 'activate_only' => __( 'Activate Only', 'wp-simple-firewall' ),
258
- 'cancel' => __( 'Cancel', 'wp-simple-firewall' ),
259
- ]
260
- ]
261
- );
262
- wp_enqueue_script( 'jquery-ui-dialog' ); // jquery and jquery-ui should be dependencies, didn't check though...
263
- wp_enqueue_style( 'wp-jquery-ui-dialog' );
264
- }
265
- }
266
-
267
- /**
268
- * @return string|false
269
- */
270
- public function getPtgSnapsBaseDir() {
271
- return $this->getCon()->getPluginCachePath( 'ptguard/' );
272
- }
273
-
274
- public function hasWizard() :bool {
275
- return false;
276
- }
277
-
278
- /**
279
- * @return string
280
- */
281
- public function getTempDir() {
282
- $sDir = $this->getCon()->getPluginCachePath( 'scans' );
283
- return Services::WpFs()->mkdir( $sDir ) ? $sDir : false;
284
- }
285
-
286
- public function getDbHandler_FileLocker() :Shield\Databases\FileLocker\Handler {
287
- return $this->getDbH( 'file_protect' );
288
- }
289
-
290
- public function getDbHandler_ScanQueue() :Shield\Databases\ScanQueue\Handler {
291
- return $this->getDbH( 'scanq' );
292
- }
293
-
294
- public function getDbHandler_ScanResults() :Shield\Databases\Scanner\Handler {
295
- return $this->getDbH( 'scanresults' );
296
- }
297
-
298
- /**
299
- * @return bool
300
- * @throws \Exception
301
- */
302
- protected function isReadyToExecute() {
303
- return ( $this->getDbHandler_ScanQueue() instanceof Shield\Databases\ScanQueue\Handler )
304
- && $this->getDbHandler_ScanQueue()->isReady()
305
- && ( $this->getDbHandler_ScanResults() instanceof Shield\Databases\Scanner\Handler )
306
- && $this->getDbHandler_ScanQueue()->isReady()
307
- && parent::isReadyToExecute();
308
- }
309
-
310
- public function onPluginDeactivate() {
311
- // 1. Clean out the scanners
312
- /** @var HackGuard\Options $oOpts */
313
- $oOpts = $this->getOptions();
314
- foreach ( $oOpts->getScanSlugs() as $sSlug ) {
315
- $this->getScanCon( $sSlug )->purge();
316
- }
317
- $this->getDbHandler_ScanQueue()->tableDelete();
318
- $this->getDbHandler_ScanResults()->tableDelete();
319
- // 2. Clean out the file locker
320
- $this->getFileLocker()->purge();
321
- }
322
-
323
- protected function getNamespaceBase() :string {
324
- return 'HackGuard';
325
- }
326
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/features/headers.php DELETED
@@ -1,98 +0,0 @@
1
- <?php
2
-
3
- use FernleafSystems\Wordpress\Plugin\Shield\Modules\Headers;
4
- use FernleafSystems\Wordpress\Services\Services;
5
-
6
- /**
7
- * @deprecated 10.1
8
- */
9
- class ICWP_WPSF_FeatureHandler_Headers extends ICWP_WPSF_FeatureHandler_BaseWpsf {
10
-
11
- protected function preProcessOptions() {
12
- $this->cleanCspHosts();
13
- $this->cleanCustomRules();
14
- }
15
-
16
- private function cleanCustomRules() {
17
- /** @var Headers\Options $opts */
18
- $opts = $this->getOptions();
19
- $opts->setOpt( 'xcsp_custom', array_unique( array_filter( array_map(
20
- function ( $sRule ) {
21
- $sRule = trim( preg_replace( '#;|\s{2,}#', '', html_entity_decode( $sRule, ENT_QUOTES ) ) );
22
- if ( !empty( $sRule ) ) {
23
- $sRule .= ';';
24
- }
25
- return $sRule;
26
- },
27
- $opts->getOpt( 'xcsp_custom', [] )
28
- ) ) ) );
29
- }
30
-
31
- private function cleanCspHosts() {
32
- /** @var Headers\Options $oOpts */
33
- $oOpts = $this->getOptions();
34
-
35
- $aValidDomains = [];
36
- foreach ( $oOpts->getOpt( 'xcsp_hosts', [] ) as $sDomain ) {
37
- $bValidDomain = false;
38
- $sDomain = trim( $sDomain );
39
-
40
- $bHttps = ( strpos( $sDomain, 'https://' ) === 0 );
41
- $bHttp = ( strpos( $sDomain, 'http://' ) === 0 );
42
- if ( $bHttp || $bHttps ) {
43
- $sDomain = preg_replace( '#^http(s)?://#', '', $sDomain );
44
- }
45
-
46
- $sCustomProtocol = '';
47
- // Special wildcard case
48
- if ( $sDomain == '*' ) {
49
- if ( $bHttps ) {
50
- $this->getOptions()->setOpt( 'xcsp_https', 'Y' );
51
- }
52
- else {
53
- $bValidDomain = true;
54
- }
55
- }
56
- elseif ( strpos( $sDomain, '://' ) && preg_match( '#^([a-zA-Z]+://)#', $sDomain, $aMatches ) ) {
57
- // there's a protocol specified
58
- $sCustomProtocol = $aMatches[ 1 ];
59
- $sDomain = str_replace( $sCustomProtocol, '', $sDomain );
60
- }
61
-
62
- // First we remove the wildcard and test domain, then add it back later.
63
- $bWildCard = ( strpos( $sDomain, '*.' ) === 0 );
64
- if ( $bWildCard ) {
65
- $sDomain = preg_replace( '#^\*\.#', '', $sDomain );
66
- }
67
-
68
- if ( !empty ( $sDomain ) && Services::Data()->isValidDomainName( $sDomain ) ) {
69
- $bValidDomain = true;
70
- }
71
-
72
- if ( $bValidDomain ) {
73
- if ( $bWildCard ) {
74
- $sDomain = '*.'.$sDomain;
75
- }
76
- if ( $bHttp ) {
77
- // $sDomain = 'http://'.$sDomain; // it seems there's no need to "explicitly" state http://
78
- }
79
- elseif ( $bHttps ) {
80
- $sDomain = 'https://'.$sDomain;
81
- }
82
- elseif ( !empty( $sCustomProtocol ) ) {
83
- $sDomain = $sCustomProtocol.$sDomain;
84
- }
85
- $aValidDomains[] = $sDomain;
86
- }
87
- }
88
- asort( $aValidDomains );
89
- $oOpts->setOpt( 'xcsp_hosts', array_unique( $aValidDomains ) );
90
- }
91
-
92
- /**
93
- * @return string
94
- */
95
- protected function getNamespaceBase() :string {
96
- return 'Headers';
97
- }
98
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/features/insights.php DELETED
@@ -1,226 +0,0 @@
1
- <?php
2
-
3
- use FernleafSystems\Wordpress\Plugin\Shield;
4
- use FernleafSystems\Wordpress\Services\Services;
5
-
6
- /**
7
- * @deprecated 10.1
8
- */
9
- class ICWP_WPSF_FeatureHandler_Insights extends ICWP_WPSF_FeatureHandler_BaseWpsf {
10
-
11
- protected function onModulesLoaded() {
12
- $this->maybeRedirectToAdmin();
13
- }
14
-
15
- private function maybeRedirectToAdmin() {
16
- $con = $this->getCon();
17
- $nActiveFor = $con->getModule_Plugin()->getActivateLength();
18
- if ( !Services::WpGeneral()->isAjax() && is_admin() && !$con->isModulePage() && $nActiveFor < 4 ) {
19
- Services::Response()->redirect( $this->getUrl_AdminPage() );
20
- }
21
- }
22
-
23
- public function getUrl_IpAnalysis( string $ip ) :string {
24
- return add_query_arg( [ 'analyse_ip' => $ip ], $this->getUrl_SubInsightsPage( 'ips' ) );
25
- }
26
-
27
- public function getUrl_SubInsightsPage( string $subPage ) :string {
28
- return add_query_arg(
29
- [ 'inav' => sanitize_key( $subPage ) ],
30
- $this->getUrl_AdminPage()
31
- );
32
- }
33
-
34
- protected function renderModulePage( array $aData = [] ) :string {
35
- /** @var Shield\Modules\Insights\UI $UI */
36
- $UI = $this->getUIHandler();
37
- return $UI->renderPages();
38
- }
39
-
40
- public function insertCustomJsVars_Admin() {
41
- parent::insertCustomJsVars_Admin();
42
-
43
- if ( $this->isThisModulePage() ) {
44
-
45
- $con = $this->getCon();
46
- $aStdDepsJs = [ $con->prefix( 'plugin' ) ];
47
- $iNav = Services::Request()->query( 'inav', 'overview' );
48
-
49
- $oModPlugin = $con->getModule_Plugin();
50
- $oTourManager = $oModPlugin->getTourManager();
51
- switch ( $iNav ) {
52
-
53
- case 'importexport':
54
-
55
- $sAsset = 'shield/import';
56
- $sUnique = $con->prefix( $sAsset );
57
- wp_register_script(
58
- $sUnique,
59
- $con->getPluginUrl_Js( $sAsset ),
60
- $aStdDepsJs,
61
- $con->getVersion(),
62
- false
63
- );
64
- wp_enqueue_script( $sUnique );
65
- break;
66
-
67
- case 'overview':
68
- case 'reports':
69
-
70
- $aDeps = $aStdDepsJs;
71
-
72
- $aJsAssets = [
73
- 'chartist.min',
74
- 'chartist-plugin-legend',
75
- 'charts',
76
- 'shuffle',
77
- 'shield-card-shuffle'
78
- ];
79
- if ( $oTourManager->canShow( 'insights_overview' ) ) {
80
- array_unshift( $aJsAssets, 'introjs.min.js' );
81
- }
82
- foreach ( $aJsAssets as $sAsset ) {
83
- $sUnique = $con->prefix( $sAsset );
84
- wp_register_script(
85
- $sUnique,
86
- $con->getPluginUrl_Js( $sAsset ),
87
- $aDeps,
88
- $con->getVersion(),
89
- false
90
- );
91
- wp_enqueue_script( $sUnique );
92
- $aDeps[] = $sUnique;
93
- }
94
-
95
- $aDeps = [];
96
- $aCssAssets = [ 'chartist.min', 'chartist-plugin-legend' ];
97
- if ( $oTourManager->canShow( 'insights_overview' ) ) {
98
- array_unshift( $aCssAssets, 'introjs.min.css' );
99
- }
100
- foreach ( $aCssAssets as $sAsset ) {
101
- $sUnique = $con->prefix( $sAsset );
102
- wp_register_style(
103
- $sUnique,
104
- $con->getPluginUrl_Css( $sAsset ),
105
- $aDeps,
106
- $con->getVersion(),
107
- false
108
- );
109
- wp_enqueue_style( $sUnique );
110
- $aDeps[] = $sUnique;
111
- }
112
-
113
- $this->includeScriptIpDetect();
114
- break;
115
-
116
- case 'notes':
117
- case 'scans':
118
- case 'audit':
119
- case 'traffic':
120
- case 'ips':
121
- case 'debug':
122
- case 'users':
123
-
124
- $sAsset = 'shield-tables';
125
- $sUnique = $con->prefix( $sAsset );
126
- wp_register_script(
127
- $sUnique,
128
- $con->getPluginUrl_Js( $sAsset ),
129
- $aStdDepsJs,
130
- $con->getVersion(),
131
- false
132
- );
133
- wp_enqueue_script( $sUnique );
134
-
135
- $aStdDepsJs[] = $sUnique;
136
- if ( $iNav == 'scans' ) {
137
- $sAsset = 'shield-scans';
138
- $sUnique = $con->prefix( $sAsset );
139
- wp_register_script(
140
- $sUnique,
141
- $con->getPluginUrl_Js( $sAsset ),
142
- array_unique( $aStdDepsJs ),
143
- $con->getVersion(),
144
- false
145
- );
146
- wp_enqueue_script( $sUnique );
147
- }
148
-
149
- if ( $iNav == 'ips' ) {
150
- $sAsset = 'shield/ipanalyse';
151
- $sUnique = $con->prefix( $sAsset );
152
- wp_register_script(
153
- $sUnique,
154
- $con->getPluginUrl_Js( $sAsset ),
155
- array_unique( $aStdDepsJs ),
156
- $con->getVersion(),
157
- false
158
- );
159
- wp_enqueue_script( $sUnique );
160
- }
161
-
162
- if ( in_array( $iNav, [ 'audit', 'traffic' ] ) ) {
163
- $sUnique = $con->prefix( 'datepicker' );
164
- wp_register_script(
165
- $sUnique, //TODO: use an includes services for CNDJS
166
- 'https://cdnjs.cloudflare.com/ajax/libs/bootstrap-datepicker/1.8.0/js/bootstrap-datepicker.min.js',
167
- array_unique( $aStdDepsJs ),
168
- $con->getVersion(),
169
- false
170
- );
171
- wp_enqueue_script( $sUnique );
172
-
173
- wp_register_style(
174
- $sUnique,
175
- 'https://cdnjs.cloudflare.com/ajax/libs/bootstrap-datepicker/1.8.0/css/bootstrap-datepicker.min.css',
176
- [],
177
- $con->getVersion(),
178
- false
179
- );
180
- wp_enqueue_style( $sUnique );
181
- }
182
-
183
- break;
184
- }
185
-
186
- wp_localize_script(
187
- $con->prefix( 'plugin' ),
188
- 'icwp_wpsf_vars_insights',
189
- [
190
- 'strings' => [
191
- 'downloading_file' => __( 'Downloading file, please wait...', 'wp-simple-firewall' ),
192
- 'downloading_file_problem' => __( 'There was a problem downloading the file.', 'wp-simple-firewall' ),
193
- 'select_action' => __( 'Please select an action to perform.', 'wp-simple-firewall' ),
194
- 'are_you_sure' => __( 'Are you sure?', 'wp-simple-firewall' ),
195
- ],
196
- ]
197
- );
198
- }
199
- }
200
-
201
- private function includeScriptIpDetect() {
202
- $con = $this->getCon();
203
- /** @var Shield\Modules\Plugin\Options $opts */
204
- $opts = $con->getModule_Plugin()->getOptions();
205
- if ( $opts->isIpSourceAutoDetect() ) {
206
- wp_register_script(
207
- $con->prefix( 'ip_detect' ),
208
- $con->getPluginUrl_Js( 'ip_detect.js' ),
209
- [],
210
- $con->getVersion(),
211
- true
212
- );
213
- wp_enqueue_script( $con->prefix( 'ip_detect' ) );
214
-
215
- wp_localize_script(
216
- $con->prefix( 'ip_detect' ),
217
- 'icwp_wpsf_vars_ipdetect',
218
- [ 'ajax' => $con->getModule_Plugin()->getAjaxActionData( 'ipdetect' ) ]
219
- );
220
- }
221
- }
222
-
223
- protected function getNamespaceBase() :string {
224
- return 'Insights';
225
- }
226
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/features/ips.php DELETED
@@ -1,155 +0,0 @@
1
- <?php
2
-
3
- use FernleafSystems\Wordpress\Plugin\Shield;
4
- use FernleafSystems\Wordpress\Plugin\Shield\Modules\IPs;
5
- use FernleafSystems\Wordpress\Services\Services;
6
-
7
- /**
8
- * @deprecated 10.1
9
- */
10
- class ICWP_WPSF_FeatureHandler_Ips extends ICWP_WPSF_FeatureHandler_BaseWpsf {
11
-
12
- const LIST_MANUAL_WHITE = 'MW';
13
- const LIST_MANUAL_BLACK = 'MB';
14
- const LIST_AUTO_BLACK = 'AB';
15
-
16
- /**
17
- * @var IPs\Lib\OffenseTracker
18
- */
19
- private $oOffenseTracker;
20
-
21
- /**
22
- * @var IPs\Lib\BlacklistHandler
23
- */
24
- private $oBlacklistHandler;
25
-
26
- /**
27
- * @return IPs\Lib\BlacklistHandler
28
- */
29
- public function getBlacklistHandler() {
30
- if ( !isset( $this->oBlacklistHandler ) ) {
31
- $this->oBlacklistHandler = ( new IPs\Lib\BlacklistHandler() )->setMod( $this );
32
- }
33
- return $this->oBlacklistHandler;
34
- }
35
-
36
- /**
37
- * @return IPs\Lib\BlacklistHandler
38
- */
39
- public function getProcessor() {
40
- return $this->getBlacklistHandler();
41
- }
42
-
43
- /**
44
- * @return false|Shield\Databases\IPs\Handler
45
- */
46
- public function getDbHandler_IPs() {
47
- return $this->getDbH( 'ips' );
48
- }
49
-
50
- /**
51
- * @return bool
52
- * @throws \Exception
53
- */
54
- protected function isReadyToExecute() {
55
- $oIp = Services::IP();
56
- return $oIp->isValidIp_PublicRange( $oIp->getRequestIp() )
57
- && ( $this->getDbHandler_IPs() instanceof Shield\Databases\IPs\Handler )
58
- && $this->getDbHandler_IPs()->isReady()
59
- && parent::isReadyToExecute();
60
- }
61
-
62
- protected function preProcessOptions() {
63
- /** @var IPs\Options $oOpts */
64
- $oOpts = $this->getOptions();
65
- if ( !defined( strtoupper( $oOpts->getOpt( 'auto_expire' ).'_IN_SECONDS' ) ) ) {
66
- $oOpts->resetOptToDefault( 'auto_expire' );
67
- }
68
-
69
- $nLimit = $oOpts->getOffenseLimit();
70
- if ( !is_int( $nLimit ) || $nLimit < 0 ) {
71
- $oOpts->resetOptToDefault( 'transgression_limit' );
72
- }
73
-
74
- $this->cleanPathWhitelist();
75
- }
76
-
77
- private function cleanPathWhitelist() {
78
- /** @var IPs\Options $opts */
79
- $opts = $this->getOptions();
80
- $opts->setOpt( 'request_whitelist', array_unique( array_filter( array_map(
81
- function ( $sRule ) {
82
- $sRule = strtolower( trim( $sRule ) );
83
- if ( !empty( $sRule ) ) {
84
- $aToCheck = [
85
- parse_url( Services::WpGeneral()->getHomeUrl(), PHP_URL_PATH ),
86
- parse_url( Services::WpGeneral()->getWpUrl(), PHP_URL_PATH ),
87
- ];
88
- $sRegEx = sprintf( '#^%s$#i', str_replace( 'STAR', '.*', preg_quote( str_replace( '*', 'STAR', $sRule ), '#' ) ) );
89
- foreach ( $aToCheck as $sPath ) {
90
- $sSlashPath = rtrim( $sPath, '/' ).'/';
91
- if ( preg_match( $sRegEx, $sPath ) || preg_match( $sRegEx, $sSlashPath ) ) {
92
- $sRule = false;
93
- break;
94
- }
95
- }
96
- }
97
- return $sRule;
98
- },
99
- $opts->getOpt( 'request_whitelist', [] ) // do not use Options getter as it formats into regex
100
- ) ) ) );
101
- }
102
-
103
- /**
104
- * @return IPs\Lib\OffenseTracker
105
- */
106
- public function loadOffenseTracker() {
107
- if ( !isset( $this->oOffenseTracker ) ) {
108
- $this->oOffenseTracker = new IPs\Lib\OffenseTracker( $this->getCon() );
109
- }
110
- return $this->oOffenseTracker;
111
- }
112
-
113
- /**
114
- * @param string $sOptKey
115
- * @return string
116
- */
117
- public function getTextOptDefault( $sOptKey ) {
118
-
119
- switch ( $sOptKey ) {
120
-
121
- case 'text_loginfailed':
122
- $sText = sprintf( '%s: %s',
123
- __( 'Warning', 'wp-simple-firewall' ),
124
- __( 'Repeated login attempts that fail will result in a complete ban of your IP Address.', 'wp-simple-firewall' )
125
- );
126
- break;
127
-
128
- case 'text_remainingtrans':
129
- $sText = sprintf( '%s: %s',
130
- __( 'Warning', 'wp-simple-firewall' ),
131
- __( 'You have %s remaining offenses(s) against this site and then your IP address will be completely blocked.', 'wp-simple-firewall' )
132
- .'<br/><strong>'.__( 'Seriously, stop repeating what you are doing or you will be locked out.', 'wp-simple-firewall' ).'</strong>'
133
- );
134
- break;
135
-
136
- default:
137
- $sText = parent::getTextOptDefault( $sOptKey );
138
- break;
139
- }
140
- return $sText;
141
- }
142
-
143
- /**
144
- * @return string
145
- */
146
- protected function getNamespaceBase() :string {
147
- return 'IPs';
148
- }
149
-
150
- /**
151
- * @deprecated 10.1
152
- */
153
- protected function addFilterIpsToWhiteList() {
154
- }
155
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/features/license.php DELETED
@@ -1,87 +0,0 @@
1
- <?php
2
-
3
- use FernleafSystems\Wordpress\Plugin\Shield;
4
- use FernleafSystems\Wordpress\Plugin\Shield\Modules\License;
5
- use FernleafSystems\Wordpress\Services\Services;
6
-
7
- /**
8
- * @deprecated 10.1
9
- */
10
- class ICWP_WPSF_FeatureHandler_License extends ICWP_WPSF_FeatureHandler_BaseWpsf {
11
-
12
- /**
13
- * @var License\Lib\LicenseHandler
14
- */
15
- private $oLicHandler;
16
-
17
- /**
18
- * @var License\Lib\WpHashes\ApiTokenManager
19
- */
20
- private $oWpHashesTokenManager;
21
-
22
- /**
23
- * @return License\Lib\LicenseHandler
24
- */
25
- public function getProcessor() {
26
- return $this->getLicenseHandler();
27
- }
28
-
29
- /**
30
- * @return License\Lib\LicenseHandler
31
- */
32
- public function getLicenseHandler() {
33
- if ( !isset( $this->oLicHandler ) ) {
34
- $this->oLicHandler = ( new License\Lib\LicenseHandler() )->setMod( $this );
35
- }
36
- return $this->oLicHandler;
37
- }
38
-
39
- /**
40
- * @return License\Lib\WpHashes\ApiTokenManager
41
- */
42
- public function getWpHashesTokenManager() {
43
- if ( !isset( $this->oWpHashesTokenManager ) ) {
44
- $this->oWpHashesTokenManager = ( new License\Lib\WpHashes\ApiTokenManager() )->setMod( $this );
45
- }
46
- return $this->oWpHashesTokenManager;
47
- }
48
-
49
- protected function redirectToInsightsSubPage() {
50
- Services::Response()->redirect(
51
- $this->getCon()->getModule_Insights()->getUrl_AdminPage(),
52
- [ 'inav' => 'license' ]
53
- );
54
- }
55
-
56
- public function runHourlyCron() {
57
- $this->getWpHashesTokenManager()->getToken();
58
- }
59
-
60
- public function onWpInit() {
61
- parent::onWpInit();
62
- $this->getWpHashesTokenManager()->execute();
63
- }
64
-
65
- /**
66
- * @return bool
67
- */
68
- public function getIfShowModuleMenuItem() {
69
- return parent::getIfShowModuleMenuItem() && !$this->isPremium();
70
- }
71
-
72
- public function onPluginShutdown() {
73
- try {
74
- $this->getLicenseHandler()->verify( false );
75
- }
76
- catch ( Exception $oE ) {
77
- }
78
- parent::onPluginShutdown();
79
- }
80
-
81
- /**
82
- * @return string
83
- */
84
- protected function getNamespaceBase() :string {
85
- return 'License';
86
- }
87
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/features/lockdown.php DELETED
@@ -1,39 +0,0 @@
1
- <?php
2
-
3
- use FernleafSystems\Wordpress\Plugin\Shield;
4
-
5
- /**
6
- * @deprecated 10.1
7
- */
8
- class ICWP_WPSF_FeatureHandler_Lockdown extends ICWP_WPSF_FeatureHandler_BaseWpsf {
9
-
10
- /**
11
- * @param string $namespace
12
- * @return bool
13
- */
14
- public function isPermittedAnonRestApiNamespace( $namespace ) {
15
- /** @var Shield\Modules\Lockdown\Options $opts */
16
- $opts = $this->getOptions();
17
- return in_array( $namespace, $opts->getRestApiAnonymousExclusions() );
18
- }
19
-
20
- protected function preProcessOptions() {
21
- $this->cleanApiExclusions();
22
- }
23
-
24
- private function cleanApiExclusions() {
25
- /** @var Shield\Modules\Lockdown\Options $opts */
26
- $opts = $this->getOptions();
27
- $opts->setOpt(
28
- 'api_namespace_exclusions',
29
- $this->cleanStringArray( $opts->getRestApiAnonymousExclusions(), '#[^a-z0-9_-]#i' )
30
- );
31
- }
32
-
33
- /**
34
- * @return string
35
- */
36
- protected function getNamespaceBase() :string {
37
- return 'Lockdown';
38
- }
39
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/features/login_protect.php DELETED
@@ -1,295 +0,0 @@
1
- <?php
2
-
3
- use FernleafSystems\Wordpress\Plugin\Shield;
4
- use FernleafSystems\Wordpress\Plugin\Shield\Modules\LoginGuard;
5
- use FernleafSystems\Wordpress\Services\Services;
6
-
7
- /**
8
- * @deprecated 10.1
9
- */
10
- class ICWP_WPSF_FeatureHandler_LoginProtect extends ICWP_WPSF_FeatureHandler_BaseWpsf {
11
-
12
- /**
13
- * @var LoginGuard\Lib\TwoFactor\MfaController
14
- */
15
- private $oLoginIntentController;
16
-
17
- protected function preProcessOptions() {
18
- /** @var LoginGuard\Options $opts */
19
- $opts = $this->getOptions();
20
- /**
21
- * $oWp = $this->loadWpFunctionsProcessor();
22
- * $sCustomLoginPath = $this->cleanLoginUrlPath();
23
- * if ( !empty( $sCustomLoginPath ) && $oWp->getIsPermalinksEnabled() ) {
24
- * $oWp->resavePermalinks();
25
- * }
26
- */
27
- if ( $this->isModuleOptionsRequest() && $opts->isEnabledEmailAuth() && !$opts->getIfCanSendEmailVerified() ) {
28
- $this->setIfCanSendEmail( false )
29
- ->sendEmailVerifyCanSend();
30
- }
31
-
32
- $aIds = $opts->getOpt( 'antibot_form_ids', [] );
33
- foreach ( $aIds as $nKey => $sId ) {
34
- $sId = trim( strip_tags( $sId ) );
35
- if ( empty( $sId ) ) {
36
- unset( $aIds[ $nKey ] );
37
- }
38
- else {
39
- $aIds[ $nKey ] = $sId;
40
- }
41
- }
42
- $opts->setOpt( 'antibot_form_ids', array_values( array_unique( $aIds ) ) );
43
-
44
- $this->cleanLoginUrlPath();
45
- $this->ensureCorrectCaptchaConfig();
46
- }
47
-
48
- public function ensureCorrectCaptchaConfig() {
49
- /** @var LoginGuard\Options $oOpts */
50
- $oOpts = $this->getOptions();
51
-
52
- $sStyle = $oOpts->getOpt( 'enable_google_recaptcha_login' );
53
- if ( $this->isPremium() ) {
54
- $oCfg = $this->getCaptchaCfg();
55
- if ( $oCfg->provider == $oCfg::PROV_GOOGLE_RECAP2 ) {
56
- if ( !$oCfg->invisible && $sStyle == 'invisible' ) {
57
- $oOpts->setOpt( 'enable_google_recaptcha_login', 'default' );
58
- }
59
- }
60
- }
61
- elseif ( !in_array( $sStyle, [ 'disabled', 'default' ] ) ) {
62
- $oOpts->setOpt( 'enable_google_recaptcha_login', 'default' );
63
- }
64
- }
65
-
66
- protected function handleModAction( string $sAction ) {
67
- switch ( $sAction ) {
68
- case 'email_send_verify':
69
- $this->processEmailSendVerify();
70
- break;
71
- default:
72
- break;
73
- }
74
- }
75
-
76
- /**
77
- * @uses wp_redirect()
78
- */
79
- private function processEmailSendVerify() {
80
- /** @var LoginGuard\Options $opts */
81
- $opts = $this->getOptions();
82
- $this->setIfCanSendEmail( true );
83
- $this->saveModOptions();
84
-
85
- if ( $opts->getIfCanSendEmailVerified() ) {
86
- $bSuccess = true;
87
- $sMessage = __( 'Email verification completed successfully.', 'wp-simple-firewall' );
88
- }
89
- else {
90
- $bSuccess = false;
91
- $sMessage = __( 'Email verification could not be completed.', 'wp-simple-firewall' );
92
- }
93
- $this->setFlashAdminNotice( $sMessage, !$bSuccess );
94
- if ( Services::WpUsers()->isUserLoggedIn() ) {
95
- Services::Response()->redirect( $this->getUrl_AdminPage() );
96
- }
97
- }
98
-
99
- /**
100
- * @param string $sEmail
101
- * @param bool $bSendAsLink
102
- * @return bool
103
- */
104
- public function sendEmailVerifyCanSend( $sEmail = null, $bSendAsLink = true ) {
105
-
106
- if ( !Services::Data()->validEmail( $sEmail ) ) {
107
- $sEmail = get_bloginfo( 'admin_email' );
108
- }
109
-
110
- $aMessage = [
111
- __( 'Before enabling 2-factor email authentication for your WordPress site, you must verify you can receive this email.', 'wp-simple-firewall' ),
112
- __( 'This verifies your website can send email and that your account can receive emails sent from your site.', 'wp-simple-firewall' ),
113
- ''
114
- ];
115
-
116
- if ( $bSendAsLink ) {
117
- $aMessage[] = sprintf(
118
- __( 'Click the verify link: %s', 'wp-simple-firewall' ),
119
- add_query_arg( $this->getModActionParams( 'email_send_verify' ), Services::WpGeneral()->getHomeUrl() )
120
- );
121
- }
122
- else {
123
- $aMessage[] = sprintf( __( "Here's your code for the guided wizard: %s", 'wp-simple-firewall' ), $this->getCanEmailVerifyCode() );
124
- }
125
-
126
- $sEmailSubject = __( 'Email Sending Verification', 'wp-simple-firewall' );
127
- return $this->getEmailProcessor()
128
- ->sendEmailWithWrap( $sEmail, $sEmailSubject, $aMessage );
129
- }
130
-
131
- private function cleanLoginUrlPath() {
132
- /** @var LoginGuard\Options $opts */
133
- $opts = $this->getOptions();
134
- $path = $opts->getCustomLoginPath();
135
- if ( !empty( $path ) ) {
136
- $path = preg_replace( '#[^0-9a-zA-Z-]#', '', trim( $path, '/' ) );
137
- $this->getOptions()->setOpt( 'rename_wplogin_path', $path );
138
- }
139
- }
140
-
141
- /**
142
- * @param bool $bAsOptDefaults
143
- * @return array
144
- */
145
- public function getOptEmailTwoFactorRolesDefaults( $bAsOptDefaults = true ) {
146
- $aTwoAuthRoles = [
147
- 'type' => 'multiple_select',
148
- 0 => __( 'Subscribers', 'wp-simple-firewall' ),
149
- 1 => __( 'Contributors', 'wp-simple-firewall' ),
150
- 2 => __( 'Authors', 'wp-simple-firewall' ),
151
- 3 => __( 'Editors', 'wp-simple-firewall' ),
152
- 8 => __( 'Administrators', 'wp-simple-firewall' )
153
- ];
154
- if ( $bAsOptDefaults ) {
155
- unset( $aTwoAuthRoles[ 'type' ] );
156
- unset( $aTwoAuthRoles[ 0 ] );
157
- return array_keys( $aTwoAuthRoles );
158
- }
159
- return $aTwoAuthRoles;
160
- }
161
-
162
- public function getGaspKey() :string {
163
- /** @var LoginGuard\Options $opts */
164
- $opts = $this->getOptions();
165
- $sKey = $opts->getOpt( 'gasp_key' );
166
- if ( empty( $sKey ) ) {
167
- $sKey = uniqid();
168
- $opts->setOpt( 'gasp_key', $sKey );
169
- }
170
- return $this->prefix( $sKey );
171
- }
172
-
173
- /**
174
- * @return string
175
- */
176
- public function getTextImAHuman() {
177
- return stripslashes( $this->getTextOpt( 'text_imahuman' ) );
178
- }
179
-
180
- /**
181
- * @return string
182
- */
183
- public function getTextPleaseCheckBox() {
184
- return stripslashes( $this->getTextOpt( 'text_pleasecheckbox' ) );
185
- }
186
-
187
- /**
188
- * @return string
189
- */
190
- public function getCanEmailVerifyCode() {
191
- return strtoupper( substr( $this->getCon()->getSiteInstallationId(), 10, 6 ) );
192
- }
193
-
194
- public function isEnabledCaptcha() :bool {
195
- return !$this->getOptions()->isOpt( 'enable_google_recaptcha_login', 'disabled' )
196
- && $this->getCaptchaCfg()->ready;
197
- }
198
-
199
- /**
200
- * @return Shield\Modules\Plugin\Lib\Captcha\CaptchaConfigVO
201
- */
202
- public function getCaptchaCfg() {
203
- $oCfg = parent::getCaptchaCfg();
204
- $sStyle = $this->getOptions()->getOpt( 'enable_google_recaptcha_login' );
205
- if ( $sStyle !== 'default' && $this->isPremium() ) {
206
- $oCfg->theme = $sStyle;
207
- $oCfg->invisible = $oCfg->theme == 'invisible';
208
- }
209
- return $oCfg;
210
- }
211
-
212
- /**
213
- * @return LoginGuard\Lib\TwoFactor\MfaController
214
- */
215
- public function getLoginIntentController() {
216
- if ( !isset( $this->oLoginIntentController ) ) {
217
- $this->oLoginIntentController = ( new LoginGuard\Lib\TwoFactor\MfaController() )
218
- ->setMod( $this );
219
- }
220
- return $this->oLoginIntentController;
221
- }
222
-
223
- public function setIsChainedAuth( bool $isChained ) {
224
- $this->getOptions()->setOpt( 'enable_chained_authentication', $isChained ? 'Y' : 'N' );
225
- }
226
-
227
- /**
228
- * @param bool $bCan
229
- * @return $this
230
- */
231
- public function setIfCanSendEmail( $bCan ) {
232
- $this->getOptions()->setOpt( 'email_can_send_verified_at', $bCan ? Services::Request()->ts() : 0 );
233
- return $this;
234
- }
235
-
236
- public function setEnabled2FaEmail( bool $enable ) {
237
- $this->getOptions()->setOpt( 'enable_email_authentication', $enable ? 'Y' : 'N' );
238
- }
239
-
240
- public function setEnabled2FaGoogleAuthenticator( bool $enable ) {
241
- $this->getOptions()->setOpt( 'enable_google_authenticator', $enable ? 'Y' : 'N' );
242
- }
243
-
244
- /**
245
- * @return string
246
- */
247
- public function getLoginIntentRequestFlag() {
248
- return $this->getCon()->prefix( 'login-intent-request' );
249
- }
250
-
251
- /**
252
- * @param string $sOptKey
253
- * @return string
254
- */
255
- public function getTextOptDefault( $sOptKey ) {
256
-
257
- switch ( $sOptKey ) {
258
- case 'text_imahuman':
259
- $sText = __( "I'm a human.", 'wp-simple-firewall' );
260
- break;
261
-
262
- case 'text_pleasecheckbox':
263
- $sText = __( "Please check the box to show us you're a human.", 'wp-simple-firewall' );
264
- break;
265
-
266
- default:
267
- $sText = parent::getTextOptDefault( $sOptKey );
268
- break;
269
- }
270
- return $sText;
271
- }
272
-
273
- public function setEnabledGaspCheck( bool $enable ) {
274
- $this->getOptions()->setOpt( 'enable_login_gasp_check', $enable ? 'Y' : 'N' );
275
- }
276
-
277
- public function insertCustomJsVars_Admin() {
278
- parent::insertCustomJsVars_Admin();
279
-
280
- wp_localize_script(
281
- $this->getCon()->prefix( 'global-plugin' ),
282
- 'icwp_wpsf_vars_lg',
283
- [
284
- 'ajax_gen_backup_codes' => $this->getAjaxActionData( 'gen_backup_codes' ),
285
- 'ajax_del_backup_codes' => $this->getAjaxActionData( 'del_backup_codes' ),
286
- ]
287
- );
288
- wp_enqueue_script( 'jquery-ui-dialog' );
289
- wp_enqueue_style( 'wp-jquery-ui-dialog' );
290
- }
291
-
292
- protected function getNamespaceBase() :string {
293
- return 'LoginGuard';
294
- }
295
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/features/plugin.php DELETED
@@ -1,553 +0,0 @@
1
- <?php
2
-
3
- use FernleafSystems\Wordpress\Plugin\Shield;
4
- use FernleafSystems\Wordpress\Plugin\Shield\Modules\Plugin;
5
- use FernleafSystems\Wordpress\Services\Services;
6
- use FernleafSystems\Wordpress\Services\Utilities;
7
-
8
- /**
9
- * @deprecated 10.1
10
- */
11
- class ICWP_WPSF_FeatureHandler_Plugin extends ICWP_WPSF_FeatureHandler_BaseWpsf {
12
-
13
- /**
14
- * @var Plugin\Lib\ImportExport\ImportExportController
15
- */
16
- private $oImportExportController;
17
-
18
- /**
19
- * @var Plugin\Components\PluginBadge
20
- */
21
- private $oPluginBadgeController;
22
-
23
- /**
24
- * @var Shield\Utilities\ReCaptcha\Enqueue
25
- */
26
- private $oCaptchaEnqueue;
27
-
28
- /**
29
- * @var Shield\ShieldNetApi\ShieldNetApiController
30
- */
31
- private $oShieldNetApiController;
32
-
33
- public function getImpExpController() :Plugin\Lib\ImportExport\ImportExportController {
34
- if ( !isset( $this->oImportExportController ) ) {
35
- $this->oImportExportController = ( new Plugin\Lib\ImportExport\ImportExportController() )
36
- ->setMod( $this );
37
- }
38
- return $this->oImportExportController;
39
- }
40
-
41
- public function getPluginBadgeCon() :Plugin\Components\PluginBadge {
42
- if ( !isset( $this->oPluginBadgeController ) ) {
43
- $this->oPluginBadgeController = ( new Plugin\Components\PluginBadge() )
44
- ->setMod( $this );
45
- }
46
- return $this->oPluginBadgeController;
47
- }
48
-
49
- public function getShieldNetApiController() :Shield\ShieldNetApi\ShieldNetApiController {
50
- if ( !isset( $this->oShieldNetApiController ) ) {
51
- $this->oShieldNetApiController = ( new Shield\ShieldNetApi\ShieldNetApiController() )
52
- ->setMod( $this );
53
- }
54
- return $this->oShieldNetApiController;
55
- }
56
-
57
- protected function doPostConstruction() {
58
- $this->setVisitorIpSource();
59
- }
60
-
61
- protected function preProcessOptions() {
62
- ( new Plugin\Lib\Captcha\CheckCaptchaSettings() )
63
- ->setMod( $this )
64
- ->checkAll();
65
- }
66
-
67
- public function deleteAllPluginCrons() {
68
- $con = $this->getCon();
69
- $oWpCron = Services::WpCron();
70
-
71
- foreach ( $oWpCron->getCrons() as $nKey => $aCronArgs ) {
72
- foreach ( $aCronArgs as $sHook => $aCron ) {
73
- if ( strpos( $sHook, $con->prefix() ) === 0
74
- || strpos( $sHook, $con->prefixOption() ) === 0 ) {
75
- $oWpCron->deleteCronJob( $sHook );
76
- }
77
- }
78
- }
79
- }
80
-
81
- /**
82
- * Hooked to the plugin's main plugin_shutdown action
83
- */
84
- public function onPluginShutdown() {
85
- $preferred = Services::IP()->getIpDetector()->getLastSuccessfulSource();
86
- if ( !empty( $preferred ) ) {
87
- $this->getOptions()->setOpt( 'last_ip_detect_source', $preferred );
88
- }
89
- parent::onPluginShutdown();
90
- }
91
-
92
- public function onWpInit() {
93
- parent::onWpInit();
94
- $this->getImportExportSecretKey();
95
- }
96
-
97
- /**
98
- * Forcefully sets preferred Visitor IP source in the Data component for use throughout the plugin
99
- */
100
- private function setVisitorIpSource() {
101
- /** @var Plugin\Options $opts */
102
- $opts = $this->getOptions();
103
- if ( !$opts->isIpSourceAutoDetect() ) {
104
- Services::IP()->setIpDetector(
105
- ( new Utilities\Net\VisitorIpDetection() )->setPreferredSource( $opts->getIpSource() )
106
- );
107
- }
108
- }
109
-
110
- protected function handleModAction( string $sAction ) {
111
- switch ( $sAction ) {
112
-
113
- case 'export_file_download':
114
- header( 'Set-Cookie: fileDownload=true; path=/' );
115
- ( new Plugin\Lib\ImportExport\Export() )
116
- ->setMod( $this )
117
- ->toFile();
118
- break;
119
-
120
- case 'import_file_upload':
121
- try {
122
- ( new Plugin\Lib\ImportExport\Import() )
123
- ->setMod( $this )
124
- ->fromFileUpload();
125
- $bSuccess = true;
126
- $sMessage = __( 'Options imported successfully', 'wp-simple-firewall' );
127
- }
128
- catch ( \Exception $oE ) {
129
- $bSuccess = false;
130
- $sMessage = $oE->getMessage();
131
- }
132
- $this->setFlashAdminNotice( $sMessage, !$bSuccess );
133
- Services::Response()->redirect(
134
- $this->getCon()->getModule_Insights()->getUrl_SubInsightsPage( 'importexport' )
135
- );
136
- break;
137
-
138
- default:
139
- break;
140
- }
141
- }
142
-
143
- public function getCanSiteCallToItself() :bool {
144
- $oHttp = Services::HttpRequest();
145
- return $oHttp->get( Services::WpGeneral()->getHomeUrl(), [ 'timeout' => 20 ] )
146
- && $oHttp->lastResponse->getCode() < 400;
147
- }
148
-
149
- public function getActivePluginFeatures() :array {
150
- $aActiveFeatures = $this->getDef( 'active_plugin_features' );
151
-
152
- $aPluginFeatures = [];
153
- if ( !empty( $aActiveFeatures ) && is_array( $aActiveFeatures ) ) {
154
-
155
- foreach ( $aActiveFeatures as $nPosition => $aFeature ) {
156
- if ( isset( $aFeature[ 'hidden' ] ) && $aFeature[ 'hidden' ] ) {
157
- continue;
158
- }
159
- $aPluginFeatures[ $aFeature[ 'slug' ] ] = $aFeature;
160
- }
161
- }
162
- return $aPluginFeatures;
163
- }
164
-
165
- public function getLinkToTrackingDataDump() :string {
166
- return add_query_arg(
167
- [ 'shield_action' => 'dump_tracking_data' ],
168
- Services::WpGeneral()->getAdminUrl()
169
- );
170
- }
171
-
172
- public function getPluginReportEmail() :string {
173
- $e = (string)$this->getOptions()->getOpt( 'block_send_email_address' );
174
- if ( $this->isPremium() ) {
175
- $e = apply_filters( $this->getCon()->prefix( 'report_email' ), $e );
176
- }
177
- $e = trim( $e );
178
- return Services::Data()->validEmail( $e ) ? $e : Services::WpGeneral()->getSiteAdminEmail();
179
- }
180
-
181
- /**
182
- * This is the point where you would want to do any options verification
183
- */
184
- protected function doPrePluginOptionsSave() {
185
- /** @var Plugin\Options $oOpts */
186
- $oOpts = $this->getOptions();
187
-
188
- $this->storeRealInstallDate();
189
-
190
- if ( $oOpts->isTrackingEnabled() && !$oOpts->isTrackingPermissionSet() ) {
191
- $oOpts->setOpt( 'tracking_permission_set_at', Services::Request()->ts() );
192
- }
193
-
194
- $this->cleanRecaptchaKey( 'google_recaptcha_site_key' );
195
- $this->cleanRecaptchaKey( 'google_recaptcha_secret_key' );
196
-
197
- $this->cleanImportExportWhitelistUrls();
198
- $this->cleanImportExportMasterImportUrl();
199
-
200
- $this->setPluginInstallationId();
201
- }
202
-
203
- public function getFirstInstallDate() :int {
204
- return (int)Services::WpGeneral()->getOption( $this->getCon()->prefixOption( 'install_date' ) );
205
- }
206
-
207
- public function getInstallDate() :int {
208
- return (int)$this->getOptions()->getOpt( 'installation_time', 0 );
209
- }
210
-
211
- public function isShowAdvanced() :bool {
212
- return $this->getOptions()->isOpt( 'show_advanced', 'Y' );
213
- }
214
-
215
- /**
216
- * @return string
217
- */
218
- public function getOpenSslPrivateKey() {
219
- $opts = $this->getOptions();
220
- $key = null;
221
- $oEnc = Services::Encrypt();
222
- if ( $oEnc->isSupportedOpenSslDataEncryption() ) {
223
- $key = $opts->getOpt( 'openssl_private_key' );
224
- if ( empty( $key ) ) {
225
- try {
226
- $aKeys = $oEnc->createNewPrivatePublicKeyPair();
227
- if ( !empty( $aKeys[ 'private' ] ) ) {
228
- $key = $aKeys[ 'private' ];
229
- $opts->setOpt( 'openssl_private_key', base64_encode( $key ) );
230
- $this->saveModOptions();
231
- }
232
- }
233
- catch ( \Exception $e ) {
234
- }
235
- }
236
- else {
237
- $key = base64_decode( $key );
238
- }
239
- }
240
- return $key;
241
- }
242
-
243
- /**
244
- * @return string|null
245
- */
246
- public function getOpenSslPublicKey() {
247
- $sKey = null;
248
- if ( $this->hasOpenSslPrivateKey() ) {
249
- try {
250
- $sKey = Services::Encrypt()->getPublicKeyFromPrivateKey( $this->getOpenSslPrivateKey() );
251
- }
252
- catch ( \Exception $e ) {
253
- }
254
- }
255
- return $sKey;
256
- }
257
-
258
- public function hasOpenSslPrivateKey() :bool {
259
- return !empty( $this->getOpenSslPrivateKey() );
260
- }
261
-
262
- /**
263
- * @return int - the real install timestamp
264
- */
265
- public function storeRealInstallDate() {
266
- $oWP = Services::WpGeneral();
267
- $nNow = Services::Request()->ts();
268
-
269
- $sOptKey = $this->getCon()->prefixOption( 'install_date' );
270
-
271
- $nWpDate = $oWP->getOption( $sOptKey );
272
- if ( empty( $nWpDate ) ) {
273
- $nWpDate = $nNow;
274
- }
275
-
276
- $nPluginDate = $this->getInstallDate();
277
- if ( $nPluginDate == 0 ) {
278
- $nPluginDate = $nNow;
279
- }
280
-
281
- $nFinal = min( $nPluginDate, $nWpDate );
282
- $oWP->updateOption( $sOptKey, $nFinal );
283
- $this->getOptions()->setOpt( 'installation_time', $nPluginDate );
284
-
285
- return $nFinal;
286
- }
287
-
288
- /**
289
- * @param string $optionKey
290
- */
291
- protected function cleanRecaptchaKey( $optionKey ) {
292
- $opts = $this->getOptions();
293
- $sCaptchaKey = trim( (string)$opts->getOpt( $optionKey, '' ) );
294
- $nSpacePos = strpos( $sCaptchaKey, ' ' );
295
- if ( $nSpacePos !== false ) {
296
- $sCaptchaKey = substr( $sCaptchaKey, 0, $nSpacePos + 1 ); // cut off the string if there's spaces
297
- }
298
- $sCaptchaKey = preg_replace( '#[^0-9a-zA-Z_-]#', '', $sCaptchaKey ); // restrict character set
299
- // if ( strlen( $sCaptchaKey ) != 40 ) {
300
- // $sCaptchaKey = ''; // need to verify length is 40.
301
- // }
302
- $opts->setOpt( $optionKey, $sCaptchaKey );
303
- }
304
-
305
- /**
306
- * Ensure we always a valid installation ID.
307
- *
308
- * @return string
309
- * @deprecated but still used because it aligns with stats collection
310
- */
311
- public function getPluginInstallationId() {
312
- $ID = $this->getOptions()->getOpt( 'unique_installation_id', '' );
313
-
314
- if ( !$this->isValidInstallId( $ID ) ) {
315
- $ID = $this->setPluginInstallationId();
316
- }
317
- return $ID;
318
- }
319
-
320
- public function getActivateLength() :int {
321
- return Services::Request()->ts() - (int)$this->getOptions()->getOpt( 'activated_at', 0 );
322
- }
323
-
324
- /**
325
- * hidden 20200121
326
- * @return bool
327
- */
328
- public function getIfShowIntroVideo() {
329
- return false && ( $this->getActivateLength() < 8 )
330
- && ( Services::Request()->ts() - $this->getInstallDate() < 15 );
331
- }
332
-
333
- public function getTourManager() :Plugin\Lib\TourManager {
334
- return ( new Plugin\Lib\TourManager() )->setMod( $this );
335
- }
336
-
337
- public function setActivatedAt() {
338
- $this->getOptions()->setOpt( 'activated_at', Services::Request()->ts() );
339
- }
340
-
341
- /**
342
- * @param string $newID - leave empty to reset if the current isn't valid
343
- * @return string
344
- */
345
- protected function setPluginInstallationId( $newID = null ) {
346
- // only reset if it's not of the correct type
347
- if ( !$this->isValidInstallId( $newID ) ) {
348
- $newID = $this->genInstallId();
349
- }
350
- $this->getOptions()->setOpt( 'unique_installation_id', $newID );
351
- return $newID;
352
- }
353
-
354
- protected function genInstallId() :string {
355
- return sha1(
356
- $this->getInstallDate()
357
- .Services::WpGeneral()->getWpUrl()
358
- .Services::WpDb()->getPrefix()
359
- );
360
- }
361
-
362
- public function hasImportExportWhitelistSites() :bool {
363
- return count( $this->getImportExportWhitelist() ) > 0;
364
- }
365
-
366
- /**
367
- * @return string[]
368
- */
369
- public function getImportExportWhitelist() :array {
370
- $list = $this->getOptions()->getOpt( 'importexport_whitelist', [] );
371
- return is_array( $list ) ? $list : [];
372
- }
373
-
374
- /**
375
- * @return string
376
- */
377
- protected function getImportExportSecretKey() {
378
- $opts = $this->getOptions();
379
- $ID = $opts->getOpt( 'importexport_secretkey', '' );
380
- if ( empty( $ID ) || $this->isImportExportSecretKeyExpired() ) {
381
- $ID = sha1( $this->getCon()->getSiteInstallationId().wp_rand( 0, PHP_INT_MAX ) );
382
- $opts->setOpt( 'importexport_secretkey', $ID )
383
- ->setOpt( 'importexport_secretkey_expires_at', Services::Request()->ts() + HOUR_IN_SECONDS );
384
- }
385
- return $ID;
386
- }
387
-
388
- protected function isImportExportSecretKeyExpired() :bool {
389
- return Services::Request()->ts() >
390
- $this->getOptions()->getOpt( 'importexport_secretkey_expires_at' );
391
- }
392
-
393
- public function isImportExportWhitelistNotify() :bool {
394
- return $this->getOptions()->isOpt( 'importexport_whitelist_notify', 'Y' );
395
- }
396
-
397
- /**
398
- * @param string $sUrl
399
- * @return $this
400
- */
401
- public function addUrlToImportExportWhitelistUrls( $sUrl ) {
402
- $sUrl = Services::Data()->validateSimpleHttpUrl( $sUrl );
403
- if ( $sUrl !== false ) {
404
- $aWhitelistUrls = $this->getImportExportWhitelist();
405
- $aWhitelistUrls[] = $sUrl;
406
- $this->getOptions()->setOpt( 'importexport_whitelist', $aWhitelistUrls );
407
- $this->saveModOptions();
408
- }
409
- return $this;
410
- }
411
-
412
- /**
413
- * @param string $url
414
- * @return $this
415
- */
416
- public function removeUrlFromImportExportWhitelistUrls( $url ) {
417
- $url = Services::Data()->validateSimpleHttpUrl( $url );
418
- if ( $url !== false ) {
419
- $aWhitelistUrls = $this->getImportExportWhitelist();
420
- $sKey = array_search( $url, $aWhitelistUrls );
421
- if ( $sKey !== false ) {
422
- unset( $aWhitelistUrls[ $sKey ] );
423
- }
424
- $this->getOptions()->setOpt( 'importexport_whitelist', $aWhitelistUrls );
425
- $this->saveModOptions();
426
- }
427
- return $this;
428
- }
429
-
430
- /**
431
- * @param string $sKey
432
- * @return bool
433
- */
434
- public function isImportExportSecretKey( $sKey ) :bool {
435
- return !empty( $sKey ) && $this->getImportExportSecretKey() == $sKey;
436
- }
437
-
438
- protected function cleanImportExportWhitelistUrls() {
439
- $oDP = Services::Data();
440
-
441
- $aCleaned = [];
442
- $aWhitelistUrls = $this->getImportExportWhitelist();
443
- foreach ( $aWhitelistUrls as $nKey => $sUrl ) {
444
-
445
- $sUrl = $oDP->validateSimpleHttpUrl( $sUrl );
446
- if ( $sUrl !== false ) {
447
- $aCleaned[] = $sUrl;
448
- }
449
- }
450
- $this->getOptions()->setOpt( 'importexport_whitelist', array_unique( $aCleaned ) );
451
- }
452
-
453
- protected function cleanImportExportMasterImportUrl() {
454
- /** @var Plugin\Options $oOpts */
455
- $oOpts = $this->getOptions();
456
- $url = Services::Data()->validateSimpleHttpUrl( $oOpts->getImportExportMasterImportUrl() );
457
- if ( $url === false ) {
458
- $url = '';
459
- }
460
- $this->getOptions()->setOpt( 'importexport_masterurl', $url );
461
- }
462
-
463
- /**
464
- * @param string $url
465
- * @return $this
466
- */
467
- public function setImportExportMasterImportUrl( $url ) {
468
- $this->getOptions()->setOpt( 'importexport_masterurl', $url ); //saving will clean the URL
469
- return $this->saveModOptions();
470
- }
471
-
472
- /**
473
- * @param string $sId
474
- * @return bool
475
- */
476
- protected function isValidInstallId( $sId ) {
477
- return !empty( $sId ) && is_string( $sId ) && strlen( $sId ) == 40;
478
- }
479
-
480
- public function isXmlrpcBypass() :bool {
481
- return $this->getOptions()->isOpt( 'enable_xmlrpc_compatibility', 'Y' );
482
- }
483
-
484
- public function getCanAdminNotes() :bool {
485
- return Services::WpUsers()->isUserAdmin();
486
- }
487
-
488
- public function insertCustomJsVars_Admin() {
489
- parent::insertCustomJsVars_Admin();
490
-
491
- $con = $this->getCon();
492
- if ( Services::WpPost()->isCurrentPage( 'plugins.php' ) ) {
493
- $sFile = $con->getPluginBaseFile();
494
- wp_localize_script(
495
- $con->prefix( 'global-plugin' ),
496
- 'icwp_wpsf_vars_plugin',
497
- [
498
- 'file' => $sFile,
499
- 'ajax' => [
500
- 'send_deactivate_survey' => $this->getAjaxActionData( 'send_deactivate_survey' ),
501
- ],
502
- 'hrefs' => [
503
- 'deactivate' => Services::WpPlugins()->getUrl_Deactivate( $sFile ),
504
- ],
505
- ]
506
- );
507
- wp_enqueue_script( 'jquery-ui-dialog' ); // jquery and jquery-ui should be dependencies, didn't check though...
508
- wp_enqueue_style( 'wp-jquery-ui-dialog' );
509
- }
510
-
511
- wp_localize_script(
512
- $con->prefix( 'plugin' ),
513
- 'icwp_wpsf_vars_tourmanager',
514
- [ 'ajax' => $this->getAjaxActionData( 'mark_tour_finished' ) ]
515
- );
516
- wp_localize_script(
517
- $con->prefix( 'plugin' ),
518
- 'icwp_wpsf_vars_plugin',
519
- [
520
- 'strings' => [
521
- 'downloading_file' => __( 'Downloading file, please wait...', 'wp-simple-firewall' ),
522
- 'problem_downloading_file' => __( 'There was a problem downloading the file.', 'wp-simple-firewall' ),
523
- ],
524
- ]
525
- );
526
- }
527
-
528
- public function getDbHandler_GeoIp() :Shield\Databases\GeoIp\Handler {
529
- return $this->getDbH( 'geoip' );
530
- }
531
-
532
- public function getDbHandler_Notes() :Shield\Databases\AdminNotes\Handler {
533
- return $this->getDbH( 'notes' );
534
- }
535
-
536
- public function getCaptchaEnqueue() :Shield\Utilities\ReCaptcha\Enqueue {
537
- if ( !isset( $this->oCaptchaEnqueue ) ) {
538
- $this->oCaptchaEnqueue = ( new Shield\Utilities\ReCaptcha\Enqueue() )->setMod( $this );
539
- }
540
- return $this->oCaptchaEnqueue;
541
- }
542
-
543
- protected function getNamespaceBase() :string {
544
- return 'Plugin';
545
- }
546
-
547
- /**
548
- * @return string
549
- */
550
- public function getSurveyEmail() {
551
- return base64_decode( $this->getDef( 'survey_email' ) );
552
- }
553
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/features/reporting.php DELETED
@@ -1,46 +0,0 @@
1
- <?php
2
-
3
- use FernleafSystems\Wordpress\Plugin\Shield;
4
- use FernleafSystems\Wordpress\Plugin\Shield\Modules\Reporting;
5
-
6
- /**
7
- * @deprecated 10.1
8
- */
9
- class ICWP_WPSF_FeatureHandler_Reporting extends ICWP_WPSF_FeatureHandler_BaseWpsf {
10
-
11
- /**
12
- * @var Reporting\Lib\ReportingController
13
- */
14
- private $oReportsController;
15
-
16
- /**
17
- * @return Shield\Databases\Reports\Handler
18
- */
19
- public function getDbHandler_Reports() {
20
- return $this->getDbH( 'reports' );
21
- }
22
-
23
- /**
24
- * @return string
25
- */
26
- protected function getNamespaceBase() :string {
27
- return 'Reporting';
28
- }
29
-
30
- /**
31
- * @return Reporting\Lib\ReportingController
32
- */
33
- public function getReportingController() {
34
- if ( !isset( $this->oReportsController ) ) {
35
- $this->oReportsController = ( new Reporting\Lib\ReportingController() )->setMod( $this );
36
- }
37
- return $this->oReportsController;
38
- }
39
-
40
- /**
41
- * @return Reporting\Lib\ReportingController
42
- */
43
- public function getProcessor() {
44
- return $this->getReportingController();
45
- }
46
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/features/sessions.php DELETED
@@ -1,48 +0,0 @@
1
- <?php
2
-
3
- use FernleafSystems\Wordpress\Plugin\Shield;
4
- use FernleafSystems\Wordpress\Services\Services;
5
-
6
- /**
7
- * @deprecated 10.1
8
- */
9
- class ICWP_WPSF_FeatureHandler_Sessions extends ICWP_WPSF_FeatureHandler_BaseWpsf {
10
-
11
- /**
12
- * @return bool
13
- */
14
- public function isAutoAddSessions() {
15
- $opts = $this->getOptions();
16
- $oReq = Services::Request();
17
- $nStartedAt = $opts->getOpt( 'autoadd_sessions_started_at', 0 );
18
- if ( $nStartedAt < 1 ) {
19
- $nStartedAt = $oReq->ts();
20
- $opts->setOpt( 'autoadd_sessions_started_at', $nStartedAt );
21
- }
22
- return ( $oReq->ts() - $nStartedAt ) < 20;
23
- }
24
-
25
- /**
26
- * @return false|Shield\Databases\Session\Handler
27
- */
28
- public function getDbHandler_Sessions() {
29
- return $this->getDbH( 'session' );
30
- }
31
-
32
- /**
33
- * @return bool
34
- * @throws \Exception
35
- */
36
- protected function isReadyToExecute() {
37
- return ( $this->getDbHandler_Sessions() instanceof Shield\Databases\Session\Handler )
38
- && $this->getDbHandler_Sessions()->isReady()
39
- && parent::isReadyToExecute();
40
- }
41
-
42
- /**
43
- * @return string
44
- */
45
- protected function getNamespaceBase() :string {
46
- return 'Sessions';
47
- }
48
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/features/traffic.php DELETED
@@ -1,51 +0,0 @@
1
- <?php
2
-
3
- use FernleafSystems\Wordpress\Plugin\Shield;
4
- use FernleafSystems\Wordpress\Plugin\Shield\Modules\Traffic;
5
- use FernleafSystems\Wordpress\Services\Services;
6
-
7
- /**
8
- * @deprecated 10.1
9
- */
10
- class ICWP_WPSF_FeatureHandler_Traffic extends ICWP_WPSF_FeatureHandler_BaseWpsf {
11
-
12
- /**
13
- * @return false|Shield\Databases\Traffic\Handler
14
- */
15
- public function getDbHandler_Traffic() {
16
- return $this->getDbH( 'traffic' );
17
- }
18
-
19
- /**
20
- * We clean the database after saving.
21
- */
22
- protected function preProcessOptions() {
23
- /** @var Traffic\Options $oOpts */
24
- $oOpts = $this->getOptions();
25
-
26
- $aExcls = $oOpts->getCustomExclusions();
27
- foreach ( $aExcls as &$sExcl ) {
28
- $sExcl = trim( esc_js( $sExcl ) );
29
- }
30
- $oOpts->setOpt( 'custom_exclusions', array_filter( $aExcls ) );
31
- }
32
-
33
- /**
34
- * @return bool
35
- * @throws \Exception
36
- */
37
- protected function isReadyToExecute() {
38
- $oIp = Services::IP();
39
- return $oIp->isValidIp_PublicRange( $oIp->getRequestIp() )
40
- && ( $this->getDbHandler_Traffic() instanceof Shield\Databases\Traffic\Handler )
41
- && $this->getDbHandler_Traffic()->isReady()
42
- && parent::isReadyToExecute();
43
- }
44
-
45
- /**
46
- * @return string
47
- */
48
- protected function getNamespaceBase() :string {
49
- return 'Traffic';
50
- }
51
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/features/user_management.php DELETED
@@ -1,138 +0,0 @@
1
- <?php
2
-
3
- use FernleafSystems\Wordpress\Plugin\Shield;
4
- use FernleafSystems\Wordpress\Plugin\Shield\Modules\UserManagement;
5
- use FernleafSystems\Wordpress\Services\Services;
6
-
7
- /**
8
- * @deprecated 10.1
9
- */
10
- class ICWP_WPSF_FeatureHandler_UserManagement extends \ICWP_WPSF_FeatureHandler_BaseWpsf {
11
-
12
- /**
13
- * Should have no default email. If no email is set, no notification is sent.
14
- * @return string[]
15
- */
16
- public function getAdminLoginNotificationEmails() {
17
- $aEmails = [];
18
-
19
- $sEmails = $this->getOptions()->getOpt( 'enable_admin_login_email_notification', '' );
20
- if ( !empty( $sEmails ) ) {
21
- $aEmails = array_values( array_unique( array_filter(
22
- array_map(
23
- function ( $sEmail ) {
24
- return trim( strtolower( $sEmail ) );
25
- },
26
- explode( ',', $sEmails )
27
- ),
28
- function ( $sEmail ) {
29
- return Services::Data()->validEmail( $sEmail );
30
- }
31
- ) ) );
32
- if ( !$this->isPremium() && !empty( $aEmails ) ) {
33
- $aEmails = array_slice( $aEmails, 0, 1 );
34
- }
35
- }
36
-
37
- return $aEmails;
38
- }
39
-
40
- protected function preProcessOptions() {
41
- /** @var UserManagement\Options $opts */
42
- $opts = $this->getOptions();
43
-
44
- $opts->setOpt( 'enable_admin_login_email_notification', implode( ', ', $this->getAdminLoginNotificationEmails() ) );
45
-
46
- if ( $opts->getIdleTimeoutInterval() > $opts->getMaxSessionTime() ) {
47
- $opts->setOpt( 'session_idle_timeout_interval', $opts->getOpt( 'session_timeout_interval' )*24 );
48
- }
49
-
50
- $opts->setOpt( 'auto_idle_roles',
51
- array_unique( array_filter( array_map(
52
- function ( $sRole ) {
53
- return preg_replace( '#[^\sa-z0-9_-]#i', '', trim( strtolower( $sRole ) ) );
54
- },
55
- $opts->getSuspendAutoIdleUserRoles()
56
- ) ) )
57
- );
58
-
59
- $aChecks = $opts->getEmailValidationChecks();
60
- if ( !empty( $aChecks ) ) {
61
- $aChecks[] = 'syntax';
62
- }
63
- $opts->setOpt( 'email_checks', array_unique( $aChecks ) );
64
- }
65
-
66
- public function isUserSessionsManagementEnabled() :bool {
67
- return $this->isModOptEnabled() && $this->getDbHandler_Sessions()->isReady();
68
- }
69
-
70
- public function isSendUserEmailLoginNotification() :bool {
71
- return $this->isPremium() && $this->getOptions()->isOpt( 'enable_user_login_email_notification', 'Y' );
72
- }
73
-
74
- /**
75
- * @param int $nStrength
76
- * @return int
77
- */
78
- public function getPassStrengthName( $nStrength ) {
79
- $aMap = [
80
- __( 'Very Weak', 'wp-simple-firewall' ),
81
- __( 'Weak', 'wp-simple-firewall' ),
82
- __( 'Medium', 'wp-simple-firewall' ),
83
- __( 'Strong', 'wp-simple-firewall' ),
84
- __( 'Very Strong', 'wp-simple-firewall' ),
85
- ];
86
- return $aMap[ max( 0, min( 4, $nStrength ) ) ];
87
- }
88
-
89
- /**
90
- * @param int $nUserId
91
- * @param bool $bAdd - set true to add, false to remove
92
- */
93
- public function addRemoveHardSuspendUserId( $nUserId, $bAdd = true ) {
94
- /** @var UserManagement\Options $opts */
95
- $opts = $this->getOptions();
96
-
97
- $aIds = $opts->getSuspendHardUserIds();
98
-
99
- $oMeta = $this->getCon()->getUserMeta( Services::WpUsers()->getUserById( $nUserId ) );
100
- $bIdSuspended = isset( $aIds[ $nUserId ] ) || $oMeta->hard_suspended_at > 0;
101
-
102
- if ( $bAdd && !$bIdSuspended ) {
103
- $oMeta->hard_suspended_at = Services::Request()->ts();
104
- $aIds[ $nUserId ] = $oMeta->hard_suspended_at;
105
- $this->getCon()->fireEvent(
106
- 'user_hard_suspended',
107
- [
108
- 'audit' => [
109
- 'user_id' => $nUserId,
110
- 'admin' => Services::WpUsers()->getCurrentWpUsername(),
111
- ]
112
- ]
113
- );
114
- }
115
- elseif ( !$bAdd && $bIdSuspended ) {
116
- $oMeta->hard_suspended_at = 0;
117
- unset( $aIds[ $nUserId ] );
118
- $this->getCon()->fireEvent(
119
- 'user_hard_unsuspended',
120
- [
121
- 'audit' => [
122
- 'user_id' => $nUserId,
123
- 'admin' => Services::WpUsers()->getCurrentWpUsername(),
124
- ]
125
- ]
126
- );
127
- }
128
-
129
- $opts->setOpt( 'hard_suspended_userids', $aIds );
130
- }
131
-
132
- /**
133
- * @return string
134
- */
135
- protected function getNamespaceBase() :string {
136
- return 'UserManagement';
137
- }
138
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/lib/src/Controller/Admin/AdminBarMenu.php ADDED
@@ -0,0 +1,50 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php declare( strict_types=1 );
2
+
3
+ namespace FernleafSystems\Wordpress\Plugin\Shield\Controller\Admin;
4
+
5
+ use FernleafSystems\Utilities\Logic\ExecOnce;
6
+ use FernleafSystems\Wordpress\Plugin\Shield\Modules\PluginControllerConsumer;
7
+
8
+ class AdminBarMenu {
9
+
10
+ use PluginControllerConsumer;
11
+ use ExecOnce;
12
+
13
+ protected function canRun() :bool {
14
+ $con = $this->getCon();
15
+ return $con->isValidAdminArea( true ) &&
16
+ apply_filters( $con->prefix( 'shield/show_admin_bar_menu' ), $con->cfg->properties[ 'show_admin_bar_menu' ] );
17
+ }
18
+
19
+ protected function run() {
20
+ add_action( 'admin_init', function ( $adminBar ) {
21
+ $this->createAdminBarMenu( $adminBar );
22
+ } );
23
+ }
24
+
25
+ /**
26
+ * @param \WP_Admin_Bar $adminBar
27
+ */
28
+ private function createAdminBarMenu( $adminBar ) {
29
+ $con = $this->getCon();
30
+
31
+ $items = apply_filters( $con->prefix( 'admin_bar_menu_items' ), [] );
32
+ if ( !empty( $items ) && is_array( $items ) ) {
33
+ $warningCount = 0;
34
+ foreach ( $items as $item ) {
35
+ $warningCount += isset( $item[ 'warnings' ] ) ? $item[ 'warnings' ] : 0;
36
+ }
37
+
38
+ $sNodeId = $con->prefix( 'adminbarmenu' );
39
+ $adminBar->add_node( [
40
+ 'id' => $sNodeId,
41
+ 'title' => $con->getHumanName()
42
+ .sprintf( '<div class="wp-core-ui wp-ui-notification shield-counter"><span aria-hidden="true">%s</span></div>', $warningCount ),
43
+ ] );
44
+ foreach ( $items as $item ) {
45
+ $item[ 'parent' ] = $sNodeId;
46
+ $adminBar->add_menu( $item );
47
+ }
48
+ }
49
+ }
50
+ }
src/lib/src/Controller/Admin/DashboardWidget.php ADDED
@@ -0,0 +1,35 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php declare( strict_types=1 );
2
+
3
+ namespace FernleafSystems\Wordpress\Plugin\Shield\Controller\Admin;
4
+
5
+ use FernleafSystems\Utilities\Logic\ExecOnce;
6
+ use FernleafSystems\Wordpress\Plugin\Shield\Modules\PluginControllerConsumer;
7
+
8
+ class DashboardWidget {
9
+
10
+ use PluginControllerConsumer;
11
+ use ExecOnce;
12
+
13
+ protected function canRun() :bool {
14
+ $con = $this->getCon();
15
+ return $con->isValidAdminArea() &&
16
+ apply_filters( 'shield/show_dashboard_widget', $con->cfg->properties[ 'show_dashboard_widget' ] ?? true );
17
+ }
18
+
19
+ protected function run() {
20
+ add_action( 'wp_dashboard_setup', function () {
21
+ $this->createWidget();
22
+ } );
23
+ }
24
+
25
+ private function createWidget() {
26
+ $con = $this->getCon();
27
+ wp_add_dashboard_widget(
28
+ $con->prefix( 'dashboard_widget' ),
29
+ apply_filters( $con->prefix( 'dashboard_widget_title' ), $con->getHumanName() ),
30
+ function () {
31
+ do_action( $this->getCon()->prefix( 'dashboard_widget_content' ) );
32
+ }
33
+ );
34
+ }
35
+ }
src/lib/src/Controller/Admin/MainAdminMenu.php ADDED
@@ -0,0 +1,87 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php declare( strict_types=1 );
2
+
3
+ namespace FernleafSystems\Wordpress\Plugin\Shield\Controller\Admin;
4
+
5
+ use FernleafSystems\Utilities\Logic\ExecOnce;
6
+ use FernleafSystems\Wordpress\Plugin\Shield\Modules\PluginControllerConsumer;
7
+
8
+ class MainAdminMenu {
9
+
10
+ use PluginControllerConsumer;
11
+ use ExecOnce;
12
+
13
+ protected function canRun() :bool {
14
+ $con = $this->getCon();
15
+ return $con->isValidAdminArea()
16
+ && apply_filters( 'shield/show_admin_menu', $con->cfg->menu[ 'show' ] ?? true );
17
+ }
18
+
19
+ protected function run() {
20
+ add_action( 'admin_menu', function () {
21
+ $this->createAdminMenu();
22
+ } );
23
+ add_action( 'network_admin_menu', function () {
24
+ $this->createAdminMenu();
25
+ } );
26
+ }
27
+
28
+ private function createAdminMenu() {
29
+ $con = $this->getCon();
30
+
31
+ $menu = $con->cfg->menu;
32
+ if ( $menu[ 'top_level' ] ) {
33
+
34
+ $labels = $con->getLabels();
35
+ $sMenuTitle = empty( $labels[ 'MenuTitle' ] ) ? $menu[ 'title' ] : $labels[ 'MenuTitle' ];
36
+ if ( is_null( $sMenuTitle ) ) {
37
+ $sMenuTitle = $con->getHumanName();
38
+ }
39
+
40
+ $sMenuIcon = $con->urls->forImage( $menu[ 'icon_image' ] );
41
+ $sIconUrl = empty( $labels[ 'icon_url_16x16' ] ) ? $sMenuIcon : $labels[ 'icon_url_16x16' ];
42
+
43
+ $parentMenuID = $con->getPluginPrefix();
44
+ add_menu_page(
45
+ $con->getHumanName(),
46
+ $sMenuTitle,
47
+ $con->getBasePermissions(),
48
+ $parentMenuID,
49
+ [ $this, 'onDisplayTopMenu' ],
50
+ $sIconUrl
51
+ );
52
+
53
+ if ( $menu[ 'has_submenu' ] ) {
54
+
55
+ $menuItems = apply_filters( $con->prefix( 'submenu_items' ), [] );
56
+ if ( !empty( $menuItems ) ) {
57
+ foreach ( $menuItems as $sMenuTitle => $aMenu ) {
58
+ list( $sMenuItemText, $sMenuItemId, $aMenuCallBack, $bShowItem ) = $aMenu;
59
+ add_submenu_page(
60
+ $bShowItem ? $parentMenuID : null,
61
+ $sMenuTitle,
62
+ $sMenuItemText,
63
+ $con->getBasePermissions(),
64
+ $sMenuItemId,
65
+ $aMenuCallBack
66
+ );
67
+ }
68
+ }
69
+ }
70
+
71
+ if ( $menu[ 'do_submenu_fix' ] ) {
72
+ $this->fixSubmenu();
73
+ }
74
+ }
75
+ }
76
+
77
+ public function onDisplayTopMenu() {
78
+ }
79
+
80
+ private function fixSubmenu() {
81
+ global $submenu;
82
+ $menuID = $this->getCon()->getPluginPrefix();
83
+ if ( isset( $submenu[ $menuID ] ) ) {
84
+ unset( $submenu[ $menuID ][ 0 ] );
85
+ }
86
+ }
87
+ }
src/lib/src/Controller/Ajax/Init.php ADDED
@@ -0,0 +1,55 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php declare( strict_types=1 );
2
+
3
+ namespace FernleafSystems\Wordpress\Plugin\Shield\Controller\Ajax;
4
+
5
+ use FernleafSystems\Utilities\Logic\ExecOnce;
6
+ use FernleafSystems\Wordpress\Plugin\Shield\Modules\PluginControllerConsumer;
7
+ use FernleafSystems\Wordpress\Services\Services;
8
+
9
+ class Init {
10
+
11
+ use ExecOnce;
12
+ use PluginControllerConsumer;
13
+
14
+ protected function canRun() :bool {
15
+ return Services::WpGeneral()->isAjax();
16
+ }
17
+
18
+ protected function run() {
19
+ add_action( 'wp_ajax_'.$this->getCon()->prefix(), function () {
20
+ $this->ajaxAction();
21
+ } );
22
+ add_action( 'wp_ajax_nopriv_'.$this->getCon()->prefix(), function () {
23
+ $this->ajaxAction();
24
+ } );
25
+ }
26
+
27
+ private function ajaxAction() {
28
+ $nonceAction = Services::Request()->request( 'exec' );
29
+ check_ajax_referer( $nonceAction, 'exec_nonce' );
30
+
31
+ ob_start();
32
+ $response = apply_filters(
33
+ $this->getCon()->prefix( Services::WpUsers()->isUserLoggedIn() ? 'ajaxAuthAction' : 'ajaxNonAuthAction' ),
34
+ [], $nonceAction
35
+ );
36
+ $noise = ob_get_clean();
37
+
38
+ if ( is_array( $response ) && isset( $response[ 'success' ] ) ) {
39
+ $success = $response[ 'success' ];
40
+ }
41
+ else {
42
+ $success = false;
43
+ $response = [];
44
+ }
45
+
46
+ ( new Response() )->issue(
47
+ [
48
+ 'success' => $success,
49
+ 'data' => $response,
50
+ 'noise' => $noise
51
+ ],
52
+ false
53
+ );
54
+ }
55
+ }
src/lib/src/Controller/Ajax/Response.php ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php declare( strict_types=1 );
2
+
3
+ namespace FernleafSystems\Wordpress\Plugin\Shield\Controller\Ajax;
4
+
5
+ use FernleafSystems\Wordpress\Services\Services;
6
+
7
+ class Response {
8
+
9
+ public function issue( array $response, $wrap = false ) {
10
+ $wrap = $wrap || (bool)Services::Request()->request( 'apto_wrap_response' );
11
+
12
+ if ( !headers_sent() ) {
13
+ header( 'Content-Type: application/json; charset='.get_option( 'blog_charset' ) );
14
+ nocache_headers();
15
+ if ( isset( $response[ 'status_code' ] ) ) {
16
+ status_header( $response[ 'status_code' ] );
17
+ }
18
+ }
19
+ if ( $wrap ) {
20
+ echo '##APTO_OPEN##';
21
+ }
22
+ echo wp_json_encode( $response );
23
+ if ( $wrap ) {
24
+ echo '##APTO_CLOSE##';
25
+ }
26
+ die( '' );
27
+ }
28
+ }
src/lib/src/Controller/Assets/Enqueue.php ADDED
@@ -0,0 +1,174 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php declare( strict_types=1 );
2
+
3
+ namespace FernleafSystems\Wordpress\Plugin\Shield\Controller\Assets;
4
+
5
+ use FernleafSystems\Utilities\Logic\ExecOnce;
6
+ use FernleafSystems\Wordpress\Plugin\Shield\Modules\PluginControllerConsumer;
7
+ use FernleafSystems\Wordpress\Services\Services;
8
+
9
+ class Enqueue {
10
+
11
+ use PluginControllerConsumer;
12
+ use ExecOnce;
13
+
14
+ const CSS = 'css';
15
+ const JS = 'js';
16
+
17
+ private $adminHookSuffix = '';
18
+
19
+ protected function canRun() :bool {
20
+ $WP = Services::WpGeneral();
21
+ return !$WP->isAjax() && !$WP->isCron()
22
+ && !empty( $this->getCon()->cfg->includes[ 'register' ] );
23
+ }
24
+
25
+ protected function run() {
26
+ add_action( 'wp_enqueue_scripts', function () {
27
+ $this->enqueue();
28
+ }, 1000 );
29
+ add_action( 'admin_enqueue_scripts', function ( $hook_suffix ) {
30
+ $this->adminHookSuffix = $hook_suffix;
31
+ $this->enqueue();
32
+ }, 1000 );
33
+ }
34
+
35
+ protected function enqueue() {
36
+
37
+ // Register all plugin assets
38
+ $this->registerAssets();
39
+
40
+ // Get standard enqueues
41
+ if ( current_action() == 'admin_enqueue_scripts' ) {
42
+ $assets = $this->getAdminAssetsToEnq();
43
+ }
44
+ else {
45
+ $assets = $this->getFrontendAssetsToEnq();
46
+ }
47
+
48
+ // Get custom enqueues from modules or elsewhere
49
+ $customAssets = $this->getCustomEnqueues();
50
+
51
+ // Combine enqueues and enqueue assets
52
+ foreach ( [ self::CSS, self::JS ] as $type ) {
53
+ if ( !empty( $customAssets[ $type ] ) ) {
54
+ $assets[ $type ] = array_unique( array_merge( $assets[ $type ], $customAssets[ $type ] ) );
55
+ }
56
+ $this->runEnqueueOnAssets( $type, $assets[ $type ] );
57
+ }
58
+
59
+ // Get module localisations
60
+ $this->localise();
61
+ }
62
+
63
+ private function localise() {
64
+ $localz = [];
65
+ foreach ( $this->getCon()->modules as $module ) {
66
+ foreach ( $module->getScriptLocalisations() as $local ) {
67
+ $localz[] = $local;
68
+ }
69
+ }
70
+
71
+ $localz = apply_filters( 'shield/custom_localisations', $localz, $this->adminHookSuffix );
72
+
73
+ foreach ( $localz as $local ) {
74
+ if ( is_array( $local ) && count( $local ) === 3 ) { //sanity
75
+ wp_localize_script( $this->normaliseHandle( $local[ 0 ] ), $local[ 1 ], $local[ 2 ] );
76
+ }
77
+ else {
78
+ error_log( 'Invalid localisation: '.var_export( $local, true ) );
79
+ }
80
+ }
81
+ }
82
+
83
+ /**
84
+ * Registers all assets in the plugin with the global population of assets (if not already included)
85
+ * This allows us to easily share assets amongst plugins and especially to use the Foundation Classes
86
+ * plugin to cater for most shared assets.
87
+ */
88
+ private function registerAssets() {
89
+ $con = $this->getCon();
90
+
91
+ $assetKeys = [
92
+ self::CSS => [],
93
+ self::JS => [],
94
+ ];
95
+
96
+ $incl = $con->cfg->includes[ 'register' ];
97
+
98
+ foreach ( array_keys( $assetKeys ) as $type ) {
99
+
100
+ foreach ( $incl[ $type ] as $key => $spec ) {
101
+ if ( !in_array( $key, $assetKeys[ $type ] ) ) {
102
+
103
+ $handle = $this->normaliseHandle( $key );
104
+ if ( $type === self::CSS ) {
105
+ $url = $spec[ 'url' ] ?? $con->urls->forCss( $key );
106
+ $reg = wp_register_style(
107
+ $handle,
108
+ $url,
109
+ $this->prefixKeys( $spec[ 'deps' ] ?? [] ),
110
+ $con->getVersion()
111
+ );
112
+ }
113
+ else {
114
+ $url = $spec[ 'url' ] ?? $con->urls->forJs( $key );
115
+ $reg = wp_register_script(
116
+ $handle,
117
+ $url,
118
+ $this->prefixKeys( $spec[ 'deps' ] ?? [] ),
119
+ $con->getVersion()
120
+ );
121
+ }
122
+
123
+ if ( $reg ) {
124
+ $assetKeys[ $type ][] = $handle;
125
+ }
126
+ }
127
+ }
128
+ }
129
+ }
130
+
131
+ private function getCustomEnqueues() :array {
132
+ $enqueues = [
133
+ self::CSS => [],
134
+ self::JS => [],
135
+ ];
136
+ foreach ( $this->getCon()->modules as $module ) {
137
+ $custom = $module->getCustomScriptEnqueues();
138
+ foreach ( array_keys( $enqueues ) as $type ) {
139
+ if ( !empty( $custom[ $type ] ) ) {
140
+ $enqueues[ $type ] = array_merge( $enqueues[ $type ], $custom[ $type ] );
141
+ }
142
+ }
143
+ }
144
+ return apply_filters( 'shield/custom_enqueues', $enqueues, $this->adminHookSuffix );
145
+ }
146
+
147
+ private function prefixKeys( array $keys ) :array {
148
+ return array_map( function ( $handle ) {
149
+ return strpos( $handle, 'wp-' ) === 0 ?
150
+ preg_replace( '#^wp-#', '', $handle )
151
+ : $this->normaliseHandle( $handle );
152
+ }, $keys );
153
+ }
154
+
155
+ private function normaliseHandle( string $handle ) :string {
156
+ return str_replace( '/', '-', $this->getCon()->prefix( $handle ) );
157
+ }
158
+
159
+ private function getAdminAssetsToEnq() {
160
+ $con = $this->getCon();
161
+ return $con->cfg->includes[ $con->getIsPage_PluginAdmin() ? 'plugin_admin' : 'admin' ];
162
+ }
163
+
164
+ private function getFrontendAssetsToEnq() :array {
165
+ return $this->getCon()->cfg->includes[ 'frontend' ] ?? [];
166
+ }
167
+
168
+ private function runEnqueueOnAssets( string $type, array $asset ) {
169
+ array_map(
170
+ $type == self::CSS ? 'wp_enqueue_style' : 'wp_enqueue_script',
171
+ $this->prefixKeys( $asset )
172
+ );
173
+ }
174
+ }
src/lib/src/Controller/Assets/Urls.php ADDED
@@ -0,0 +1,64 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php declare( strict_types=1 );
2
+
3
+ namespace FernleafSystems\Wordpress\Plugin\Shield\Controller\Assets;
4
+
5
+ use FernleafSystems\Wordpress\Plugin\Shield\Modules\PluginControllerConsumer;
6
+ use FernleafSystems\Wordpress\Services\Services;
7
+
8
+ class Urls {
9
+
10
+ use PluginControllerConsumer;
11
+
12
+ public function forCss( string $asset ) :string {
13
+ $url = $this->lookupAssetUrlInSpec( $asset, 'css' );
14
+ return empty( $url ) ?
15
+ $this->forAsset( 'css/'.Services::Data()->addExtensionToFilePath( $asset, 'css' ) )
16
+ : $url;
17
+ }
18
+
19
+ public function forImage( string $asset ) :string {
20
+ return $this->forAsset( 'images/'.$asset );
21
+ }
22
+
23
+ public function forJs( string $asset ) :string {
24
+ $url = $this->lookupAssetUrlInSpec( $asset, 'js' );
25
+ return empty( $url ) ?
26
+ $this->forAsset( 'js/'.Services::Data()->addExtensionToFilePath( $asset, 'js' ) )
27
+ : $url;
28
+ }
29
+
30
+ public function forAsset( string $asset ) :string {
31
+ $con = $this->getCon();
32
+
33
+ $path = $con->getPath_Assets( $asset );
34
+ if ( Services::WpFs()->exists( $path ) ) {
35
+ $url = Services::Includes()->addIncludeModifiedParam(
36
+ $this->forPluginItem( $con->getPluginSpec_Path( 'assets' ).'/'.$asset ),
37
+ $path
38
+ );
39
+ }
40
+ else {
41
+ $url = '';
42
+ }
43
+
44
+ return $url;
45
+ }
46
+
47
+ public function forPluginItem( string $path = '' ) :string {
48
+ $con = $this->getCon();
49
+ return add_query_arg( [ 'ver' => $con->getVersion() ], plugins_url( $path, $con->getRootFile() ) );
50
+ }
51
+
52
+ /**
53
+ * @param string $asset
54
+ * @param string $type
55
+ * @return mixed|null
56
+ */
57
+ protected function lookupAssetUrlInSpec( string $asset, string $type ) {
58
+ $registrations = $this->getCon()->cfg->includes[ 'register' ][ $type ];
59
+ if ( isset( $registrations[ $asset ] ) && !empty( $registrations[ $asset ][ 'url' ] ) ) {
60
+ return $registrations[ $asset ][ 'url' ];
61
+ }
62
+ return null;
63
+ }
64
+ }
src/lib/src/Controller/Config/Ops/LoadConfig.php CHANGED
@@ -40,7 +40,7 @@ class LoadConfig {
40
  $version = $def[ 'properties' ][ 'version' ] ?? '0';
41
 
42
  $rebuild = empty( $def[ 'hash' ] ) || !hash_equals( $def[ 'hash' ], $specHash )
43
- || ( $version !== Services::WpPlugins()->getPluginAsVo( $con->getPluginBaseFile() )->Version );
44
  $def[ 'hash' ] = $specHash;
45
  }
46
 
40
  $version = $def[ 'properties' ][ 'version' ] ?? '0';
41
 
42
  $rebuild = empty( $def[ 'hash' ] ) || !hash_equals( $def[ 'hash' ], $specHash )
43
+ || ( $version !== Services::WpPlugins()->getPluginAsVo( $con->base_file )->Version );
44
  $def[ 'hash' ] = $specHash;
45
  }
46
 
src/lib/src/Controller/Controller.php CHANGED
@@ -11,6 +11,7 @@ use FernleafSystems\Wordpress\Services\Utilities\Options\Transient;
11
  * Class Controller
12
  * @package FernleafSystems\Wordpress\Plugin\Shield\Controller
13
  * @property Config\ConfigVO $cfg
 
14
  * @property bool $is_activating
15
  * @property bool $is_debug
16
  * @property bool $modules_loaded
@@ -42,18 +43,6 @@ class Controller {
42
  */
43
  public static $oInstance;
44
 
45
- /**
46
- * @var string
47
- * @deprecated 10.1
48
- */
49
- private $sRootFile;
50
-
51
- /**
52
- * @var string
53
- * @deprecated 10.1
54
- */
55
- private $sPluginBaseFile;
56
-
57
  /**
58
  * @var array
59
  */
@@ -138,9 +127,8 @@ class Controller {
138
  * @throws \Exception
139
  */
140
  protected function __construct( string $rootFile ) {
141
- $this->sRootFile = $rootFile;
142
  $this->root_file = $rootFile;
143
- $this->base_file = $this->getPluginBaseFile();
144
  $this->modules = [];
145
 
146
  $this->loadServices();
@@ -169,6 +157,12 @@ class Controller {
169
  }
170
  break;
171
 
 
 
 
 
 
 
172
  case 'is_debug':
173
  if ( is_null( $val ) ) {
174
  $val = ( new Shield\Controller\Utilities\DebugMode() )
@@ -192,23 +186,6 @@ class Controller {
192
  Services::GetInstance();
193
  }
194
 
195
- /**
196
- * @return array
197
- * @throws \Exception
198
- * @deprecated 10.1.4
199
- */
200
- private function readPluginSpecification() :array {
201
- $spec = [];
202
- $content = Services::Data()->readFileContentsUsingInclude( $this->getPathPluginSpec() );
203
- if ( !empty( $content ) ) {
204
- $spec = json_decode( $content, true );
205
- if ( empty( $spec ) || !is_array( $spec ) ) {
206
- throw new \Exception( 'Could not load plugin spec configuration.' );
207
- }
208
- }
209
- return $spec;
210
- }
211
-
212
  /**
213
  * @param bool $bCheckOnlyFrontEnd
214
  * @throws \Exception
@@ -315,9 +292,9 @@ class Controller {
315
 
316
  public function onWpActivatePlugin() {
317
  $this->is_activating = true;
318
- $oModPlugin = $this->getModule_Plugin();
319
- if ( $oModPlugin instanceof \ICWP_WPSF_FeatureHandler_Base ) {
320
- $oModPlugin->setActivatedAt();
321
  }
322
  }
323
 
@@ -363,21 +340,12 @@ class Controller {
363
  add_action( 'wp_loaded', [ $this, 'onWpLoaded' ] );
364
  add_action( 'admin_init', [ $this, 'onWpAdminInit' ] );
365
 
366
- add_action( 'admin_menu', [ $this, 'onWpAdminMenu' ] );
367
- add_action( 'network_admin_menu', [ $this, 'onWpAdminMenu' ] );
368
-
369
- if ( Services::WpGeneral()->isAjax() ) {
370
- add_action( 'wp_ajax_'.$this->prefix(), [ $this, 'ajaxAction' ] );
371
- add_action( 'wp_ajax_nopriv_'.$this->prefix(), [ $this, 'ajaxAction' ] );
372
- }
373
-
374
- $sBaseFile = $this->getPluginBaseFile();
375
  add_filter( 'all_plugins', [ $this, 'filter_hidePluginFromTableList' ] );
376
  add_filter( 'all_plugins', [ $this, 'doPluginLabels' ] );
377
- add_filter( 'plugin_action_links_'.$sBaseFile, [ $this, 'onWpPluginActionLinks' ], 50, 1 );
378
  add_filter( 'plugin_row_meta', [ $this, 'onPluginRowMeta' ], 50, 2 );
379
  add_filter( 'site_transient_update_plugins', [ $this, 'filter_hidePluginUpdatesFromUI' ] );
380
- add_action( 'in_plugin_update_message-'.$sBaseFile, [ $this, 'onWpPluginUpdateMessage' ] );
381
  add_filter( 'site_transient_update_plugins', [ $this, 'blockIncompatibleUpdates' ] );
382
  add_filter( 'auto_update_plugin', [ $this, 'onWpAutoUpdate' ], 500, 2 );
383
  add_filter( 'set_site_transient_update_plugins', [ $this, 'setUpdateFirstDetectedAt' ] );
@@ -401,10 +369,12 @@ class Controller {
401
  }
402
 
403
  public function onWpAdminInit() {
404
- add_action( 'admin_bar_menu', [ $this, 'onWpAdminBarMenu' ], 100 );
405
- add_action( 'wp_dashboard_setup', [ $this, 'onWpDashboardSetup' ] );
406
- add_action( 'admin_enqueue_scripts', [ $this, 'onWpEnqueueAdminCss' ], 100 );
407
- add_action( 'admin_enqueue_scripts', [ $this, 'onWpEnqueueAdminJs' ], 5 );
 
 
408
 
409
  if ( Services::Request()->query( $this->prefix( 'runtests' ) ) && $this->isPluginAdmin() ) {
410
  $this->runTests();
@@ -418,25 +388,26 @@ class Controller {
418
 
419
  /**
420
  * In order to prevent certain errors when the back button is used
421
- * @param array $aHeaders
422
  * @return array
423
  */
424
- public function adjustNocacheHeaders( $aHeaders ) {
425
- if ( is_array( $aHeaders ) && !empty( $aHeaders[ 'Cache-Control' ] ) ) {
426
- $aHs = array_map( 'trim', explode( ',', $aHeaders[ 'Cache-Control' ] ) );
427
- $aHs[] = 'no-store';
428
- $aHeaders[ 'Cache-Control' ] = implode( ', ', array_unique( $aHs ) );
429
  }
430
- return $aHeaders;
431
  }
432
 
433
  public function onWpInit() {
434
  $this->getMeetsBasePermissions();
435
- add_action( 'wp_enqueue_scripts', [ $this, 'onWpEnqueueFrontendCss' ], 99 );
436
-
437
  if ( $this->isModulePage() ) {
438
  add_filter( 'nocache_headers', [ $this, 'adjustNocacheHeaders' ] );
439
  }
 
 
 
440
  }
441
 
442
  /**
@@ -445,10 +416,10 @@ class Controller {
445
  * @return string - the unique, never-changing site install ID.
446
  */
447
  public function getSiteInstallationId() {
448
- $oWP = Services::WpGeneral();
449
  $sOptKey = $this->prefixOption( 'install_id' );
450
 
451
- $mStoredID = $oWP->getOption( $sOptKey );
452
  if ( is_array( $mStoredID ) && !empty( $mStoredID[ 'id' ] ) ) {
453
  $sID = $mStoredID[ 'id' ];
454
  $bUpdate = true;
@@ -467,13 +438,13 @@ class Controller {
467
  $sID = \Ramsey\Uuid\Uuid::uuid4()->toString();
468
  }
469
  catch ( \Exception $e ) {
470
- $sID = sha1( uniqid( $oWP->getHomeUrl( '', true ), true ) );
471
  }
472
  $bUpdate = true;
473
  }
474
 
475
  if ( $bUpdate ) {
476
- $oWP->updateOption( $sOptKey, $sID );
477
  }
478
 
479
  return $sID;
@@ -481,17 +452,23 @@ class Controller {
481
 
482
  /**
483
  * TODO: Use to set ID after license verify where applicable
484
- * @param string $sID
485
  */
486
- public function setSiteInstallID( $sID ) {
487
- if ( !empty( $sID ) && ( \Ramsey\Uuid\Uuid::isValid( $sID ) ) ) {
488
- Services::WpGeneral()->updateOption( $this->prefixOption( 'install_id' ), $sID );
489
  }
490
  }
491
 
492
  public function onWpLoaded() {
493
  $this->getAdminNotices();
494
  $this->initCrons();
 
 
 
 
 
 
495
  }
496
 
497
  protected function initCrons() {
@@ -503,56 +480,6 @@ class Controller {
503
  ->run();
504
  }
505
 
506
- public function onWpAdminMenu() {
507
- if ( $this->isValidAdminArea() ) {
508
- $this->createPluginMenu();
509
- }
510
- }
511
-
512
- /**
513
- * @param \WP_Admin_Bar $oAdminBar
514
- */
515
- public function onWpAdminBarMenu( $oAdminBar ) {
516
- $bShow = apply_filters( $this->prefix( 'show_admin_bar_menu' ),
517
- $this->isValidAdminArea( true ) && $this->cfg->properties[ 'show_admin_bar_menu' ]
518
- );
519
- if ( $bShow ) {
520
- $aMenuItems = apply_filters( $this->prefix( 'admin_bar_menu_items' ), [] );
521
- if ( !empty( $aMenuItems ) && is_array( $aMenuItems ) ) {
522
- $nCountWarnings = 0;
523
- foreach ( $aMenuItems as $aMenuItem ) {
524
- $nCountWarnings += isset( $aMenuItem[ 'warnings' ] ) ? $aMenuItem[ 'warnings' ] : 0;
525
- }
526
-
527
- $sNodeId = $this->prefix( 'adminbarmenu' );
528
- $oAdminBar->add_node( [
529
- 'id' => $sNodeId,
530
- 'title' => $this->getHumanName()
531
- .sprintf( '<div class="wp-core-ui wp-ui-notification shield-counter"><span aria-hidden="true">%s</span></div>', $nCountWarnings ),
532
- ] );
533
- foreach ( $aMenuItems as $aMenuItem ) {
534
- $aMenuItem[ 'parent' ] = $sNodeId;
535
- $oAdminBar->add_menu( $aMenuItem );
536
- }
537
- }
538
- }
539
- }
540
-
541
- public function onWpDashboardSetup() {
542
- $show = apply_filters( $this->prefix( 'show_dashboard_widget' ),
543
- $this->isValidAdminArea() && $this->cfg->properties[ 'show_dashboard_widget' ]
544
- );
545
- if ( $show ) {
546
- wp_add_dashboard_widget(
547
- $this->prefix( 'dashboard_widget' ),
548
- apply_filters( $this->prefix( 'dashboard_widget_title' ), $this->getHumanName() ),
549
- function () {
550
- do_action( $this->prefix( 'dashboard_widget_content' ) );
551
- }
552
- );
553
- }
554
- }
555
-
556
  /**
557
  * @return Shield\Utilities\AdminNotices\Controller
558
  */
@@ -580,119 +507,20 @@ class Controller {
580
  ];
581
  }
582
 
583
- public function ajaxAction() {
584
- $nonceAction = Services::Request()->request( 'exec' );
585
- check_ajax_referer( $nonceAction, 'exec_nonce' );
586
-
587
- ob_start();
588
- $response = apply_filters(
589
- $this->prefix( Services::WpUsers()->isUserLoggedIn() ? 'ajaxAuthAction' : 'ajaxNonAuthAction' ),
590
- [], $nonceAction
591
- );
592
- $noise = ob_get_clean();
593
-
594
- if ( is_array( $response ) && isset( $response[ 'success' ] ) ) {
595
- $bSuccess = $response[ 'success' ];
596
- }
597
- else {
598
- $bSuccess = false;
599
- $response = [];
600
- }
601
-
602
- wp_send_json(
603
- [
604
- 'success' => $bSuccess,
605
- 'data' => $response,
606
- 'noise' => $noise
607
- ]
608
- );
609
- }
610
-
611
- /**
612
- * @return bool
613
- */
614
- protected function createPluginMenu() {
615
- $menu = $this->cfg->menu;
616
-
617
- if ( apply_filters( $this->prefix( 'filter_hidePluginMenu' ), !$menu[ 'show' ] ) ) {
618
- return true;
619
- }
620
-
621
- if ( $menu[ 'top_level' ] ) {
622
-
623
- $labels = $this->getLabels();
624
- $sMenuTitle = empty( $labels[ 'MenuTitle' ] ) ? $menu[ 'title' ] : $labels[ 'MenuTitle' ];
625
- if ( is_null( $sMenuTitle ) ) {
626
- $sMenuTitle = $this->getHumanName();
627
- }
628
-
629
- $sMenuIcon = $this->getPluginUrl_Image( $menu[ 'icon_image' ] );
630
- $sIconUrl = empty( $labels[ 'icon_url_16x16' ] ) ? $sMenuIcon : $labels[ 'icon_url_16x16' ];
631
-
632
- $sFullParentMenuId = $this->getPluginPrefix();
633
- add_menu_page(
634
- $this->getHumanName(),
635
- $sMenuTitle,
636
- $this->getBasePermissions(),
637
- $sFullParentMenuId,
638
- [ $this, $menu[ 'callback' ] ],
639
- $sIconUrl
640
- );
641
-
642
- if ( $menu[ 'has_submenu' ] ) {
643
-
644
- $menuItems = apply_filters( $this->prefix( 'submenu_items' ), [] );
645
- if ( !empty( $menuItems ) ) {
646
- foreach ( $menuItems as $sMenuTitle => $aMenu ) {
647
- list( $sMenuItemText, $sMenuItemId, $aMenuCallBack, $bShowItem ) = $aMenu;
648
- add_submenu_page(
649
- $bShowItem ? $sFullParentMenuId : null,
650
- $sMenuTitle,
651
- $sMenuItemText,
652
- $this->getBasePermissions(),
653
- $sMenuItemId,
654
- $aMenuCallBack
655
- );
656
- }
657
- }
658
- }
659
-
660
- if ( $menu[ 'do_submenu_fix' ] ) {
661
- $this->fixSubmenu();
662
- }
663
- }
664
- return true;
665
- }
666
-
667
- protected function fixSubmenu() {
668
- global $submenu;
669
- $sFullParentMenuId = $this->getPluginPrefix();
670
- if ( isset( $submenu[ $sFullParentMenuId ] ) ) {
671
- unset( $submenu[ $sFullParentMenuId ][ 0 ] );
672
- }
673
- }
674
-
675
  /**
676
- * Displaying all views now goes through this central function and we work out
677
- * what to display based on the name of current hook/filter being processed.
678
- */
679
- public function onDisplayTopMenu() {
680
- }
681
-
682
- /**
683
- * @param array $aPluginMeta
684
- * @param string $sPluginFile
685
  * @return array
686
  */
687
- public function onPluginRowMeta( $aPluginMeta, $sPluginFile ) {
688
 
689
- if ( $sPluginFile == $this->getPluginBaseFile() ) {
690
  $sTemplate = '<strong><a href="%s" target="_blank">%s</a></strong>';
691
  foreach ( $this->cfg->plugin_meta as $aHref ) {
692
- array_push( $aPluginMeta, sprintf( $sTemplate, $aHref[ 'href' ], $aHref[ 'name' ] ) );
693
  }
694
  }
695
- return $aPluginMeta;
696
  }
697
 
698
  /**
@@ -710,8 +538,8 @@ class Controller {
710
  $links = $this->cfg->action_links[ 'add' ];
711
  if ( is_array( $links ) ) {
712
 
713
- $bPro = $this->isPremiumActive();
714
- $oDP = Services::Data();
715
  $sLinkTemplate = '<a href="%s" target="%s" title="%s">%s</a>';
716
  foreach ( $links as $aLink ) {
717
  $aLink = array_merge(
@@ -726,13 +554,13 @@ class Controller {
726
  $aLink
727
  );
728
 
729
- $sShow = $aLink[ 'show' ];
730
- $bShow = ( $sShow == 'always' ) || ( $bPro && $sShow == 'pro' ) || ( !$bPro && $sShow == 'free' );
731
- if ( !$oDP->isValidWebUrl( $aLink[ 'href' ] ) && method_exists( $this, $aLink[ 'href' ] ) ) {
732
  $aLink[ 'href' ] = $this->{$aLink[ 'href' ]}();
733
  }
734
 
735
- if ( !$bShow || !$oDP->isValidWebUrl( $aLink[ 'href' ] )
736
  || empty( $aLink[ 'name' ] ) || empty( $aLink[ 'href' ] ) ) {
737
  continue;
738
  }
@@ -754,133 +582,41 @@ class Controller {
754
  return $aActionLinks;
755
  }
756
 
757
- public function onWpEnqueueFrontendCss() {
758
- $includes = $this->cfg->includes[ 'frontend' ];
759
- if ( isset( $includes[ 'css' ] ) && !empty( $includes[ 'css' ] ) && is_array( $includes[ 'css' ] ) ) {
760
-
761
- $aDeps = [];
762
- foreach ( $includes[ 'css' ] as $sAsset ) {
763
- $sUrl = $this->getPluginUrl_Css( $sAsset );
764
- if ( !empty( $sUrl ) ) {
765
- $sAsset = $this->prefix( $sAsset );
766
- wp_register_style( $sAsset, $sUrl, $aDeps, $this->getVersion() );
767
- wp_enqueue_style( $sAsset );
768
- $aDeps[] = $aDeps;
769
- }
770
- }
771
- }
772
- }
773
-
774
- public function onWpEnqueueAdminJs() {
775
-
776
- $aIncludes = [];
777
- if ( $this->getIsPage_PluginAdmin() ) {
778
- $includes = $this->cfg->includes[ 'plugin_admin' ];
779
- if ( !empty( $includes[ 'js' ] ) && is_array( $includes[ 'js' ] ) ) {
780
- $aIncludes = $includes[ 'js' ];
781
- }
782
- }
783
- elseif ( $this->isValidAdminArea() ) {
784
- $includes = $this->cfg->includes[ 'admin' ];
785
- if ( !empty( $includes[ 'js' ] ) && is_array( $includes[ 'js' ] ) ) {
786
- $aIncludes = $includes[ 'js' ];
787
- }
788
- }
789
-
790
- $nativeWP = [ 'jquery' ];
791
-
792
- $aDeps = [];
793
- foreach ( $aIncludes as $asset ) {
794
-
795
- // Built-in handles
796
- if ( in_array( $asset, $nativeWP ) ) {
797
- if ( wp_script_is( $asset, 'registered' ) ) {
798
- wp_enqueue_script( $asset );
799
- $aDeps[] = $asset;
800
- }
801
- }
802
- else {
803
- $sUrl = $this->getPluginUrl_Js( $asset );
804
- if ( !empty( $sUrl ) ) {
805
- $asset = $this->prefix( $asset );
806
- wp_register_script( $asset, $sUrl, $aDeps, $this->getVersion() );
807
- wp_enqueue_script( $asset );
808
- $aDeps[] = $asset;
809
- }
810
- }
811
- }
812
- }
813
-
814
- public function onWpEnqueueAdminCss() {
815
-
816
- $aIncludes = [];
817
- if ( $this->getIsPage_PluginAdmin() ) {
818
- $includes = $this->cfg->includes[ 'plugin_admin' ];
819
- if ( !empty( $includes[ 'css' ] ) && is_array( $includes[ 'css' ] ) ) {
820
- $aIncludes = $includes[ 'css' ];
821
- }
822
- }
823
- elseif ( $this->isValidAdminArea() ) {
824
- $includes = $this->cfg->includes[ 'admin' ];
825
- if ( !empty( $includes[ 'css' ] ) && is_array( $includes[ 'css' ] ) ) {
826
- $aIncludes = $includes[ 'css' ];
827
- }
828
- }
829
-
830
- $aDeps = [];
831
- foreach ( $aIncludes as $asset ) {
832
- $sUrl = $this->getPluginUrl_Css( $asset );
833
- if ( !empty( $sUrl ) ) {
834
- $asset = $this->prefix( $asset );
835
- wp_register_style( $asset, $sUrl, $aDeps, $this->getVersion() );
836
- wp_enqueue_style( $asset );
837
- $aDeps[] = $asset;
838
- }
839
- }
840
- }
841
-
842
  /**
843
  * Displays a message in the plugins listing when a plugin has an update available.
844
  */
845
  public function onWpPluginUpdateMessage() {
846
- $sMessage = __( 'Update Now To Keep Your Security Current With The Latest Features.', 'wp-simple-firewall' );
847
- if ( empty( $sMessage ) ) {
848
- $sMessage = '';
849
- }
850
- else {
851
- $sMessage = sprintf(
852
- ' <span class="%s plugin_update_message">%s</span>',
853
- $this->getPluginPrefix(),
854
- $sMessage
855
- );
856
- }
857
- echo $sMessage;
858
  }
859
 
860
  /**
861
  * Prevents upgrades to Shield versions when the system PHP version is too old.
862
- * @param \stdClass $oUpdates
863
  * @return \stdClass
864
  */
865
- public function blockIncompatibleUpdates( $oUpdates ) {
866
- $sFile = $this->getPluginBaseFile();
867
- if ( !empty( $oUpdates->response ) && isset( $oUpdates->response[ $sFile ] ) ) {
868
- $aUpgradeReqs = $this->getPluginSpec()[ 'upgrade_reqs' ];
869
- if ( is_array( $aUpgradeReqs ) ) {
870
- foreach ( $aUpgradeReqs as $sShieldVer => $aReqs ) {
871
- $bNeedsHidden = version_compare( $oUpdates->response[ $sFile ]->new_version, $sShieldVer, '>=' )
872
  && (
873
  !Services::Data()->getPhpVersionIsAtLeast( $aReqs[ 'php' ] )
874
  || !Services::WpGeneral()->getWordpressIsAtLeastVersion( $aReqs[ 'wp' ] )
875
  );
876
  if ( $bNeedsHidden ) {
877
- unset( $oUpdates->response[ $sFile ] );
878
  break;
879
  }
880
  }
881
  }
882
  }
883
- return $oUpdates;
884
  }
885
 
886
  /**
@@ -891,10 +627,10 @@ class Controller {
891
  */
892
  public function setUpdateFirstDetectedAt( $data ) {
893
 
894
- if ( !empty( $data ) && !empty( $data->response ) && isset( $data->response[ $this->getPluginBaseFile() ] ) ) {
895
  // i.e. update available
896
 
897
- $new = Services::WpPlugins()->getUpdateNewVersion( $this->getPluginBaseFile() );
898
  if ( !empty( $new ) && isset( $this->cfg ) ) {
899
  $updates = $this->cfg->update_first_detected;
900
  if ( count( $updates ) > 3 ) {
@@ -921,17 +657,17 @@ class Controller {
921
  $WP = Services::WpGeneral();
922
  $oWpPlugins = Services::WpPlugins();
923
 
924
- $sFile = $WP->getFileFromAutomaticUpdateItem( $mItem );
925
 
926
  // The item in question is this plugin...
927
- if ( $sFile === $this->getPluginBaseFile() ) {
928
  $autoupdateSelf = $this->cfg->properties[ 'autoupdate' ];
929
 
930
  if ( !$WP->isRunningAutomaticUpdates() && $autoupdateSelf == 'confidence' ) {
931
  $autoupdateSelf = 'yes'; // so that we appear to be automatically updating
932
  }
933
 
934
- $new = $oWpPlugins->getUpdateNewVersion( $sFile );
935
 
936
  switch ( $autoupdateSelf ) {
937
 
@@ -966,16 +702,16 @@ class Controller {
966
  * @return array
967
  */
968
  public function doPluginLabels( $aPlugins ) {
969
- $aLabelData = $this->getLabels();
970
- if ( empty( $aLabelData ) ) {
971
  return $aPlugins;
972
  }
973
 
974
- $sPluginFile = $this->getPluginBaseFile();
975
  // For this plugin, overwrite any specified settings
976
- if ( array_key_exists( $sPluginFile, $aPlugins ) ) {
977
- foreach ( $aLabelData as $sLabelKey => $sLabel ) {
978
- $aPlugins[ $sPluginFile ][ $sLabelKey ] = $sLabel;
979
  }
980
  }
981
 
@@ -1032,7 +768,7 @@ class Controller {
1032
  */
1033
  public function filter_hidePluginFromTableList( $plugins ) {
1034
  if ( apply_filters( $this->prefix( 'hide_plugin' ), false ) ) {
1035
- unset( $plugins[ $this->getPluginBaseFile() ] );
1036
  }
1037
  return $plugins;
1038
  }
@@ -1047,7 +783,7 @@ class Controller {
1047
  */
1048
  public function filter_hidePluginUpdatesFromUI( $plugins ) {
1049
  if ( !Services::WpGeneral()->isCron() && apply_filters( $this->prefix( 'hide_plugin_updates' ), false ) ) {
1050
- unset( $plugins->response[ $this->getPluginBaseFile() ] );
1051
  }
1052
  return $plugins;
1053
  }
@@ -1058,13 +794,13 @@ class Controller {
1058
  * @return string
1059
  */
1060
  public function prefix( $suffix = '', $glue = '-' ) {
1061
- $sPrefix = $this->getPluginPrefix( $glue );
1062
 
1063
- if ( $suffix == $sPrefix || strpos( $suffix, $sPrefix.$glue ) === 0 ) { //it already has the full prefix
1064
  return $suffix;
1065
  }
1066
 
1067
- return sprintf( '%s%s%s', $sPrefix, empty( $suffix ) ? '' : $glue, empty( $suffix ) ? '' : $suffix );
1068
  }
1069
 
1070
  public function prefixOption( string $suffix = '' ) :string {
@@ -1094,52 +830,6 @@ class Controller {
1094
  return $this->getPluginControllerOptions()->plugin_spec;
1095
  }
1096
 
1097
- /**
1098
- * @param string $key
1099
- * @return array
1100
- * @deprecated 10.1.4
1101
- */
1102
- protected function getPluginSpec_ActionLinks( string $key ) :array {
1103
- $aData = $this->getPluginSpec()[ 'action_links' ];
1104
- return $aData[ $key ] ?? [];
1105
- }
1106
-
1107
- /**
1108
- * @param string $key
1109
- * @return mixed|null
1110
- * @deprecated 10.1.4
1111
- */
1112
- protected function getPluginSpec_Include( string $key ) {
1113
- $aData = $this->getPluginSpec()[ 'includes' ];
1114
- return $aData[ $key ] ?? null;
1115
- }
1116
-
1117
- /**
1118
- * @param string $key
1119
- * @return array|string
1120
- * @deprecated 10.1.4
1121
- */
1122
- protected function getPluginSpec_Labels( string $key = '' ) {
1123
- $oSpec = $this->getPluginSpec();
1124
- $aLabels = isset( $oSpec[ 'labels' ] ) ? $oSpec[ 'labels' ] : [];
1125
-
1126
- if ( empty( $key ) ) {
1127
- return $aLabels;
1128
- }
1129
-
1130
- return isset( $oSpec[ 'labels' ][ $key ] ) ? $oSpec[ 'labels' ][ $key ] : null;
1131
- }
1132
-
1133
- /**
1134
- * @param string $key
1135
- * @return mixed|null
1136
- * @deprecated 10.1.4
1137
- */
1138
- protected function getPluginSpec_Menu( string $key ) {
1139
- $aData = $this->getPluginSpec()[ 'menu' ];
1140
- return $aData[ $key ] ?? null;
1141
- }
1142
-
1143
  /**
1144
  * @param string $key
1145
  * @return string|null
@@ -1163,40 +853,17 @@ class Controller {
1163
  /**
1164
  * @param string $key
1165
  * @return mixed|null
1166
- * @deprecated 10.1.4 - getCfgProperty()
1167
  */
1168
  protected function getPluginSpec_Property( string $key ) {
1169
- if ( isset( $this->cfg ) ) {
1170
- return $this->cfg->properties[ $key ];
1171
- }
1172
- $data = $this->getPluginSpec()[ 'properties' ];
1173
- return $data[ $key ] ?? null;
1174
- }
1175
-
1176
- /**
1177
- * @return array
1178
- * @deprecated 10.1.4
1179
- */
1180
- protected function getPluginSpec_PluginMeta() {
1181
- $aSpec = $this->getPluginSpec();
1182
- return ( isset( $aSpec[ 'plugin_meta' ] ) && is_array( $aSpec[ 'plugin_meta' ] ) ) ? $aSpec[ 'plugin_meta' ] : [];
1183
- }
1184
-
1185
- /**
1186
- * @param string $key
1187
- * @return mixed|null
1188
- * @deprecated 10.1.4
1189
- */
1190
- protected function getPluginSpec_Requirement( string $key ) {
1191
- $aData = $this->getPluginSpec()[ 'requirements' ];
1192
- return $aData[ $key ] ?? null;
1193
  }
1194
 
1195
  public function getBasePermissions() :string {
1196
  if ( isset( $this->cfg ) ) {
1197
  return $this->cfg->properties[ 'base_permissions' ];
1198
  }
1199
- return $this->getPluginSpec_Property( 'base_permissions' );
1200
  }
1201
 
1202
  public function isValidAdminArea( bool $bCheckUserPerms = false ) :bool {
@@ -1204,11 +871,11 @@ class Controller {
1204
  return false;
1205
  }
1206
 
1207
- $oWp = Services::WpGeneral();
1208
- if ( !$oWp->isMultisite() && is_admin() ) {
1209
  return true;
1210
  }
1211
- elseif ( $oWp->isMultisite() && $this->getIsWpmsNetworkAdminOnly() && ( is_network_admin() || $oWp->isAjax() ) ) {
1212
  return true;
1213
  }
1214
  return false;
@@ -1244,8 +911,8 @@ class Controller {
1244
  return $this->getPluginPrefix( '_' ).'_';
1245
  }
1246
 
1247
- public function getPluginPrefix( string $sGlue = '-' ) :string {
1248
- return sprintf( '%s%s%s', $this->getParentSlug(), $sGlue, $this->getPluginSlug() );
1249
  }
1250
 
1251
  /**
@@ -1254,21 +921,15 @@ class Controller {
1254
  */
1255
  public function getHumanName() {
1256
  $labels = $this->getLabels();
1257
- return empty( $labels[ 'Name' ] ) ? $this->getPluginSpec_Property( 'human_name' ) : $labels[ 'Name' ];
1258
  }
1259
 
1260
- /**
1261
- * @return string
1262
- */
1263
- public function isLoggingEnabled() {
1264
- return $this->getPluginSpec_Property( 'logging_enabled' );
1265
  }
1266
 
1267
- /**
1268
- * @return bool
1269
- */
1270
- public function getIsPage_PluginAdmin() {
1271
- return ( strpos( Services::WpGeneral()->getCurrentWpAdminPage(), $this->getPluginPrefix() ) === 0 );
1272
  }
1273
 
1274
  public function getIsPage_PluginMainDashboard() :bool {
@@ -1277,7 +938,7 @@ class Controller {
1277
 
1278
  /**
1279
  * @return bool
1280
- * @deprecated 10.1.4
1281
  */
1282
  public function getIsRebuildOptionsFromFile() :bool {
1283
  return $this->rebuild_options;
@@ -1290,17 +951,18 @@ class Controller {
1290
  return (bool)$this->plugin_reset;
1291
  }
1292
 
1293
- /**
1294
- * @return bool
1295
- */
1296
- public function getIsWpmsNetworkAdminOnly() {
1297
- return $this->getPluginSpec_Property( 'wpms_network_admin_only' );
1298
  }
1299
 
1300
  public function getParentSlug() :string {
1301
- return $this->getPluginSpec_Property( 'slug_parent' );
1302
  }
1303
 
 
 
 
 
1304
  public function getPluginBaseFile() :string {
1305
  if ( !isset( $this->base_file ) ) {
1306
  $this->base_file = plugin_basename( $this->getRootFile() );
@@ -1309,13 +971,16 @@ class Controller {
1309
  }
1310
 
1311
  public function getPluginSlug() :string {
1312
- return $this->getPluginSpec_Property( 'slug_plugin' );
1313
  }
1314
 
1315
  public function getPluginUrl( string $path = '' ) :string {
1316
  return add_query_arg( [ 'ver' => $this->getVersion() ], plugins_url( $path, $this->getRootFile() ) );
1317
  }
1318
 
 
 
 
1319
  public function getPluginUrl_Asset( string $asset ) :string {
1320
  $url = '';
1321
  $sAssetPath = $this->getPath_Assets( $asset );
@@ -1326,16 +991,25 @@ class Controller {
1326
  return $url;
1327
  }
1328
 
 
 
 
1329
  public function getPluginUrl_Css( string $asset ) :string {
1330
- return $this->getPluginUrl_Asset( 'css/'.Services::Data()->addExtensionToFilePath( $asset, 'css' ) );
1331
  }
1332
 
 
 
 
1333
  public function getPluginUrl_Image( string $asset ) :string {
1334
- return $this->getPluginUrl_Asset( 'images/'.$asset );
1335
  }
1336
 
 
 
 
1337
  public function getPluginUrl_Js( string $asset ) :string {
1338
- return $this->getPluginUrl_Asset( 'js/'.Services::Data()->addExtensionToFilePath( $asset, 'js' ) );
1339
  }
1340
 
1341
  public function getPluginUrl_AdminMainPage() :string {
@@ -1433,41 +1107,20 @@ class Controller {
1433
  return $this->root_file;
1434
  }
1435
 
1436
- /**
1437
- * @return int
1438
- */
1439
- public function getReleaseTimestamp() {
1440
- return $this->getPluginSpec_Property( 'release_timestamp' );
1441
  }
1442
 
1443
  public function getTextDomain() :string {
1444
- return $this->getPluginSpec_Property( 'text_domain' );
1445
  }
1446
 
1447
- /**
1448
- * @return string
1449
- */
1450
- public function getBuild() {
1451
- return $this->getPluginSpec_Property( 'build' );
1452
  }
1453
 
1454
- /**
1455
- * @return string
1456
- */
1457
- public function getVersion() {
1458
- return $this->getPluginSpec_Property( 'version' );
1459
- }
1460
-
1461
- /**
1462
- * @return string
1463
- * @deprecated 10.1.4
1464
- */
1465
- public function getPreviousVersion() :string {
1466
- $opts = $this->getPluginControllerOptions();
1467
- if ( empty( $opts->previous_version ) ) {
1468
- $opts->previous_version = $this->getVersion();
1469
- }
1470
- return $opts->previous_version;
1471
  }
1472
 
1473
  public function getVersionNumeric() :int {
@@ -1503,11 +1156,8 @@ class Controller {
1503
  }
1504
  }
1505
 
1506
- /**
1507
- * @return bool
1508
- */
1509
- public function isPremiumExtensionsEnabled() {
1510
- return (bool)$this->getPluginSpec_Property( 'enable_premium' );
1511
  }
1512
 
1513
  public function isPremiumActive() :bool {
@@ -1519,7 +1169,6 @@ class Controller {
1519
  }
1520
 
1521
  protected function saveCurrentPluginControllerOptions() {
1522
- $WP = Services::WpGeneral();
1523
  add_filter( $this->prefix( 'bypass_is_plugin_admin' ), '__return_true' );
1524
 
1525
  if ( $this->plugin_deleting ) {
@@ -1528,13 +1177,6 @@ class Controller {
1528
  elseif ( isset( $this->cfg ) ) {
1529
  Config\Ops\Save::ToWp( $this->cfg, $this->getConfigStoreKey() );
1530
  }
1531
- else {
1532
- /* @deprecated 10.1.4 */
1533
- $WP->updateOption(
1534
- $this->getPluginControllerOptionsKey(),
1535
- $this->getPluginControllerOptions()
1536
- );
1537
- }
1538
  remove_filter( $this->prefix( 'bypass_is_plugin_admin' ), '__return_true' );
1539
  }
1540
 
@@ -1552,17 +1194,9 @@ class Controller {
1552
  return 'aptoweb_controller_'.substr( md5( get_class() ), 0, 6 );
1553
  }
1554
 
1555
- /**
1556
- * @return string
1557
- * @deprecated 10.1.4
1558
- */
1559
- private function getPluginControllerOptionsKey() {
1560
- return strtolower( get_class() );
1561
- }
1562
-
1563
  public function deactivateSelf() {
1564
  if ( $this->isPluginAdmin() && function_exists( 'deactivate_plugins' ) ) {
1565
- deactivate_plugins( $this->getPluginBaseFile() );
1566
  }
1567
  }
1568
 
@@ -1652,8 +1286,8 @@ class Controller {
1652
  * @throws \Exception from loadFeatureHandler()
1653
  */
1654
  public function loadCorePluginFeatureHandler() {
1655
- if ( !isset( $this->modules[ 'plugin' ] )
1656
- || !$this->modules[ 'plugin' ] instanceof \ICWP_WPSF_FeatureHandler_Base ) {
1657
  $this->loadFeatureHandler(
1658
  [
1659
  'slug' => 'plugin',
@@ -1697,11 +1331,11 @@ class Controller {
1697
 
1698
  /**
1699
  * @param string $slug
1700
- * @return \ICWP_WPSF_FeatureHandler_Base|null|mixed
1701
  */
1702
  public function getModule( string $slug ) {
1703
  $mod = isset( $this->modules[ $slug ] ) ? $this->modules[ $slug ] : null;
1704
- if ( !$mod instanceof \ICWP_WPSF_FeatureHandler_Base ) {
1705
  try {
1706
  $mods = $this->loadCorePluginFeatureHandler()->getActivePluginFeatures();
1707
  if ( isset( $mods[ $slug ] ) ) {
@@ -1788,13 +1422,13 @@ class Controller {
1788
 
1789
  /**
1790
  * @param array $modProps
1791
- * @return \ICWP_WPSF_FeatureHandler_Base|mixed
1792
  * @throws \Exception
1793
  */
1794
  public function loadFeatureHandler( array $modProps ) {
1795
  $modSlug = $modProps[ 'slug' ];
1796
  $mod = isset( $this->modules[ $modSlug ] ) ? $this->modules[ $modSlug ] : null;
1797
- if ( $mod instanceof \ICWP_WPSF_FeatureHandler_Base || $mod instanceof Shield\Modules\Base\ModCon ) {
1798
  return $mod;
1799
  }
1800
 
@@ -1992,4 +1626,61 @@ class Controller {
1992
  ->run();
1993
  }
1994
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1995
  }
11
  * Class Controller
12
  * @package FernleafSystems\Wordpress\Plugin\Shield\Controller
13
  * @property Config\ConfigVO $cfg
14
+ * @property Shield\Controller\Assets\Urls $urls
15
  * @property bool $is_activating
16
  * @property bool $is_debug
17
  * @property bool $modules_loaded
43
  */
44
  public static $oInstance;
45
 
 
 
 
 
 
 
 
 
 
 
 
 
46
  /**
47
  * @var array
48
  */
127
  * @throws \Exception
128
  */
129
  protected function __construct( string $rootFile ) {
 
130
  $this->root_file = $rootFile;
131
+ $this->base_file = plugin_basename( $this->getRootFile() );
132
  $this->modules = [];
133
 
134
  $this->loadServices();
157
  }
158
  break;
159
 
160
+ case 'urls':
161
+ if ( !$val instanceof Shield\Controller\Assets\Urls ) {
162
+ $val = ( new Shield\Controller\Assets\Urls() )->setCon( $this );
163
+ }
164
+ break;
165
+
166
  case 'is_debug':
167
  if ( is_null( $val ) ) {
168
  $val = ( new Shield\Controller\Utilities\DebugMode() )
186
  Services::GetInstance();
187
  }
188
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
189
  /**
190
  * @param bool $bCheckOnlyFrontEnd
191
  * @throws \Exception
292
 
293
  public function onWpActivatePlugin() {
294
  $this->is_activating = true;
295
+ $modPlugin = $this->getModule_Plugin();
296
+ if ( $modPlugin instanceof Shield\Modules\Base\ModCon ) {
297
+ $modPlugin->setActivatedAt();
298
  }
299
  }
300
 
340
  add_action( 'wp_loaded', [ $this, 'onWpLoaded' ] );
341
  add_action( 'admin_init', [ $this, 'onWpAdminInit' ] );
342
 
 
 
 
 
 
 
 
 
 
343
  add_filter( 'all_plugins', [ $this, 'filter_hidePluginFromTableList' ] );
344
  add_filter( 'all_plugins', [ $this, 'doPluginLabels' ] );
345
+ add_filter( 'plugin_action_links_'.$this->base_file, [ $this, 'onWpPluginActionLinks' ], 50, 1 );
346
  add_filter( 'plugin_row_meta', [ $this, 'onPluginRowMeta' ], 50, 2 );
347
  add_filter( 'site_transient_update_plugins', [ $this, 'filter_hidePluginUpdatesFromUI' ] );
348
+ add_action( 'in_plugin_update_message-'.$this->base_file, [ $this, 'onWpPluginUpdateMessage' ] );
349
  add_filter( 'site_transient_update_plugins', [ $this, 'blockIncompatibleUpdates' ] );
350
  add_filter( 'auto_update_plugin', [ $this, 'onWpAutoUpdate' ], 500, 2 );
351
  add_filter( 'set_site_transient_update_plugins', [ $this, 'setUpdateFirstDetectedAt' ] );
369
  }
370
 
371
  public function onWpAdminInit() {
372
+ ( new Admin\AdminBarMenu() )
373
+ ->setCon( $this )
374
+ ->execute();
375
+ ( new Admin\DashboardWidget() )
376
+ ->setCon( $this )
377
+ ->execute();
378
 
379
  if ( Services::Request()->query( $this->prefix( 'runtests' ) ) && $this->isPluginAdmin() ) {
380
  $this->runTests();
388
 
389
  /**
390
  * In order to prevent certain errors when the back button is used
391
+ * @param array $headers
392
  * @return array
393
  */
394
+ public function adjustNocacheHeaders( $headers ) {
395
+ if ( is_array( $headers ) && !empty( $headers[ 'Cache-Control' ] ) ) {
396
+ $Hs = array_map( 'trim', explode( ',', $headers[ 'Cache-Control' ] ) );
397
+ $Hs[] = 'no-store';
398
+ $headers[ 'Cache-Control' ] = implode( ', ', array_unique( $Hs ) );
399
  }
400
+ return $headers;
401
  }
402
 
403
  public function onWpInit() {
404
  $this->getMeetsBasePermissions();
 
 
405
  if ( $this->isModulePage() ) {
406
  add_filter( 'nocache_headers', [ $this, 'adjustNocacheHeaders' ] );
407
  }
408
+ ( new Ajax\Init() )
409
+ ->setCon( $this )
410
+ ->execute();
411
  }
412
 
413
  /**
416
  * @return string - the unique, never-changing site install ID.
417
  */
418
  public function getSiteInstallationId() {
419
+ $WP = Services::WpGeneral();
420
  $sOptKey = $this->prefixOption( 'install_id' );
421
 
422
+ $mStoredID = $WP->getOption( $sOptKey );
423
  if ( is_array( $mStoredID ) && !empty( $mStoredID[ 'id' ] ) ) {
424
  $sID = $mStoredID[ 'id' ];
425
  $bUpdate = true;
438
  $sID = \Ramsey\Uuid\Uuid::uuid4()->toString();
439
  }
440
  catch ( \Exception $e ) {
441
+ $sID = sha1( uniqid( $WP->getHomeUrl( '', true ), true ) );
442
  }
443
  $bUpdate = true;
444
  }
445
 
446
  if ( $bUpdate ) {
447
+ $WP->updateOption( $sOptKey, $sID );
448
  }
449
 
450
  return $sID;
452
 
453
  /**
454
  * TODO: Use to set ID after license verify where applicable
455
+ * @param string $ID
456
  */
457
+ public function setSiteInstallID( $ID ) {
458
+ if ( !empty( $ID ) && ( \Ramsey\Uuid\Uuid::isValid( $ID ) ) ) {
459
+ Services::WpGeneral()->updateOption( $this->prefixOption( 'install_id' ), $ID );
460
  }
461
  }
462
 
463
  public function onWpLoaded() {
464
  $this->getAdminNotices();
465
  $this->initCrons();
466
+ ( new Shield\Controller\Assets\Enqueue() )
467
+ ->setCon( $this )
468
+ ->execute();
469
+ ( new Admin\MainAdminMenu() )
470
+ ->setCon( $this )
471
+ ->execute();
472
  }
473
 
474
  protected function initCrons() {
480
  ->run();
481
  }
482
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
483
  /**
484
  * @return Shield\Utilities\AdminNotices\Controller
485
  */
507
  ];
508
  }
509
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
510
  /**
511
+ * @param array $pluginMeta
512
+ * @param string $pluginFile
 
 
 
 
 
 
 
513
  * @return array
514
  */
515
+ public function onPluginRowMeta( $pluginMeta, $pluginFile ) {
516
 
517
+ if ( $pluginFile === $this->base_file ) {
518
  $sTemplate = '<strong><a href="%s" target="_blank">%s</a></strong>';
519
  foreach ( $this->cfg->plugin_meta as $aHref ) {
520
+ array_push( $pluginMeta, sprintf( $sTemplate, $aHref[ 'href' ], $aHref[ 'name' ] ) );
521
  }
522
  }
523
+ return $pluginMeta;
524
  }
525
 
526
  /**
538
  $links = $this->cfg->action_links[ 'add' ];
539
  if ( is_array( $links ) ) {
540
 
541
+ $isPro = $this->isPremiumActive();
542
+ $DP = Services::Data();
543
  $sLinkTemplate = '<a href="%s" target="%s" title="%s">%s</a>';
544
  foreach ( $links as $aLink ) {
545
  $aLink = array_merge(
554
  $aLink
555
  );
556
 
557
+ $show = $aLink[ 'show' ];
558
+ $bShow = ( $show == 'always' ) || ( $isPro && $show == 'pro' ) || ( !$isPro && $show == 'free' );
559
+ if ( !$DP->isValidWebUrl( $aLink[ 'href' ] ) && method_exists( $this, $aLink[ 'href' ] ) ) {
560
  $aLink[ 'href' ] = $this->{$aLink[ 'href' ]}();
561
  }
562
 
563
+ if ( !$bShow || !$DP->isValidWebUrl( $aLink[ 'href' ] )
564
  || empty( $aLink[ 'name' ] ) || empty( $aLink[ 'href' ] ) ) {
565
  continue;
566
  }
582
  return $aActionLinks;
583
  }
584
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
585
  /**
586
  * Displays a message in the plugins listing when a plugin has an update available.
587
  */
588
  public function onWpPluginUpdateMessage() {
589
+ echo sprintf(
590
+ ' <span class="%s plugin_update_message">%s</span>',
591
+ $this->getPluginPrefix(),
592
+ __( 'Update Now To Keep Your Security Current With The Latest Features.', 'wp-simple-firewall' )
593
+ );
 
 
 
 
 
 
 
594
  }
595
 
596
  /**
597
  * Prevents upgrades to Shield versions when the system PHP version is too old.
598
+ * @param \stdClass $updates
599
  * @return \stdClass
600
  */
601
+ public function blockIncompatibleUpdates( $updates ) {
602
+ $file = $this->base_file;
603
+ if ( !empty( $updates->response ) && isset( $updates->response[ $file ] ) ) {
604
+ $reqs = $this->cfg->upgrade_reqs;
605
+ if ( is_array( $reqs ) ) {
606
+ foreach ( $reqs as $sShieldVer => $aReqs ) {
607
+ $bNeedsHidden = version_compare( $updates->response[ $file ]->new_version, $sShieldVer, '>=' )
608
  && (
609
  !Services::Data()->getPhpVersionIsAtLeast( $aReqs[ 'php' ] )
610
  || !Services::WpGeneral()->getWordpressIsAtLeastVersion( $aReqs[ 'wp' ] )
611
  );
612
  if ( $bNeedsHidden ) {
613
+ unset( $updates->response[ $file ] );
614
  break;
615
  }
616
  }
617
  }
618
  }
619
+ return $updates;
620
  }
621
 
622
  /**
627
  */
628
  public function setUpdateFirstDetectedAt( $data ) {
629
 
630
+ if ( !empty( $data ) && !empty( $data->response ) && isset( $data->response[ $this->base_file ] ) ) {
631
  // i.e. update available
632
 
633
+ $new = Services::WpPlugins()->getUpdateNewVersion( $this->base_file );
634
  if ( !empty( $new ) && isset( $this->cfg ) ) {
635
  $updates = $this->cfg->update_first_detected;
636
  if ( count( $updates ) > 3 ) {
657
  $WP = Services::WpGeneral();
658
  $oWpPlugins = Services::WpPlugins();
659
 
660
+ $file = $WP->getFileFromAutomaticUpdateItem( $mItem );
661
 
662
  // The item in question is this plugin...
663
+ if ( $file === $this->base_file ) {
664
  $autoupdateSelf = $this->cfg->properties[ 'autoupdate' ];
665
 
666
  if ( !$WP->isRunningAutomaticUpdates() && $autoupdateSelf == 'confidence' ) {
667
  $autoupdateSelf = 'yes'; // so that we appear to be automatically updating
668
  }
669
 
670
+ $new = $oWpPlugins->getUpdateNewVersion( $file );
671
 
672
  switch ( $autoupdateSelf ) {
673
 
702
  * @return array
703
  */
704
  public function doPluginLabels( $aPlugins ) {
705
+ $labels = $this->getLabels();
706
+ if ( empty( $labels ) ) {
707
  return $aPlugins;
708
  }
709
 
710
+ $file = $this->base_file;
711
  // For this plugin, overwrite any specified settings
712
+ if ( array_key_exists( $file, $aPlugins ) ) {
713
+ foreach ( $labels as $sLabelKey => $sLabel ) {
714
+ $aPlugins[ $file ][ $sLabelKey ] = $sLabel;
715
  }
716
  }
717
 
768
  */
769
  public function filter_hidePluginFromTableList( $plugins ) {
770
  if ( apply_filters( $this->prefix( 'hide_plugin' ), false ) ) {
771
+ unset( $plugins[ $this->base_file ] );
772
  }
773
  return $plugins;
774
  }
783
  */
784
  public function filter_hidePluginUpdatesFromUI( $plugins ) {
785
  if ( !Services::WpGeneral()->isCron() && apply_filters( $this->prefix( 'hide_plugin_updates' ), false ) ) {
786
+ unset( $plugins->response[ $this->base_file ] );
787
  }
788
  return $plugins;
789
  }
794
  * @return string
795
  */
796
  public function prefix( $suffix = '', $glue = '-' ) {
797
+ $prefix = $this->getPluginPrefix( $glue );
798
 
799
+ if ( $suffix == $prefix || strpos( $suffix, $prefix.$glue ) === 0 ) { //it already has the full prefix
800
  return $suffix;
801
  }
802
 
803
+ return sprintf( '%s%s%s', $prefix, empty( $suffix ) ? '' : $glue, empty( $suffix ) ? '' : $suffix );
804
  }
805
 
806
  public function prefixOption( string $suffix = '' ) :string {
830
  return $this->getPluginControllerOptions()->plugin_spec;
831
  }
832
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
833
  /**
834
  * @param string $key
835
  * @return string|null
853
  /**
854
  * @param string $key
855
  * @return mixed|null
856
+ * @deprecated 10.2.0 - getCfgProperty()
857
  */
858
  protected function getPluginSpec_Property( string $key ) {
859
+ return $this->cfg->properties[ $key ] ?? null;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
860
  }
861
 
862
  public function getBasePermissions() :string {
863
  if ( isset( $this->cfg ) ) {
864
  return $this->cfg->properties[ 'base_permissions' ];
865
  }
866
+ return $this->getCfgProperty( 'base_permissions' );
867
  }
868
 
869
  public function isValidAdminArea( bool $bCheckUserPerms = false ) :bool {
871
  return false;
872
  }
873
 
874
+ $WP = Services::WpGeneral();
875
+ if ( !$WP->isMultisite() && is_admin() ) {
876
  return true;
877
  }
878
+ elseif ( $WP->isMultisite() && $this->getIsWpmsNetworkAdminOnly() && ( is_network_admin() || $WP->isAjax() ) ) {
879
  return true;
880
  }
881
  return false;
911
  return $this->getPluginPrefix( '_' ).'_';
912
  }
913
 
914
+ public function getPluginPrefix( string $glue = '-' ) :string {
915
+ return sprintf( '%s%s%s', $this->getParentSlug(), $glue, $this->getPluginSlug() );
916
  }
917
 
918
  /**
921
  */
922
  public function getHumanName() {
923
  $labels = $this->getLabels();
924
+ return empty( $labels[ 'Name' ] ) ? $this->getCfgProperty( 'human_name' ) : $labels[ 'Name' ];
925
  }
926
 
927
+ public function isLoggingEnabled() :bool {
928
+ return (bool)$this->getCfgProperty( 'logging_enabled' );
 
 
 
929
  }
930
 
931
+ public function getIsPage_PluginAdmin() :bool {
932
+ return strpos( Services::WpGeneral()->getCurrentWpAdminPage(), $this->getPluginPrefix() ) === 0;
 
 
 
933
  }
934
 
935
  public function getIsPage_PluginMainDashboard() :bool {
938
 
939
  /**
940
  * @return bool
941
+ * @deprecated 10.2
942
  */
943
  public function getIsRebuildOptionsFromFile() :bool {
944
  return $this->rebuild_options;
951
  return (bool)$this->plugin_reset;
952
  }
953
 
954
+ public function getIsWpmsNetworkAdminOnly() :bool {
955
+ return (bool)$this->getCfgProperty( 'wpms_network_admin_only' );
 
 
 
956
  }
957
 
958
  public function getParentSlug() :string {
959
+ return $this->getCfgProperty( 'slug_parent' );
960
  }
961
 
962
+ /**
963
+ * @return string
964
+ * @deprecated 10.2.0
965
+ */
966
  public function getPluginBaseFile() :string {
967
  if ( !isset( $this->base_file ) ) {
968
  $this->base_file = plugin_basename( $this->getRootFile() );
971
  }
972
 
973
  public function getPluginSlug() :string {
974
+ return $this->getCfgProperty( 'slug_plugin' );
975
  }
976
 
977
  public function getPluginUrl( string $path = '' ) :string {
978
  return add_query_arg( [ 'ver' => $this->getVersion() ], plugins_url( $path, $this->getRootFile() ) );
979
  }
980
 
981
+ /**
982
+ * @deprecated 10.2
983
+ */
984
  public function getPluginUrl_Asset( string $asset ) :string {
985
  $url = '';
986
  $sAssetPath = $this->getPath_Assets( $asset );
991
  return $url;
992
  }
993
 
994
+ /**
995
+ * @deprecated 10.2
996
+ */
997
  public function getPluginUrl_Css( string $asset ) :string {
998
+ return $this->urls->forCss( $asset );
999
  }
1000
 
1001
+ /**
1002
+ * @deprecated 10.2
1003
+ */
1004
  public function getPluginUrl_Image( string $asset ) :string {
1005
+ return $this->urls->forImage( $asset );
1006
  }
1007
 
1008
+ /**
1009
+ * @deprecated 10.2
1010
+ */
1011
  public function getPluginUrl_Js( string $asset ) :string {
1012
+ return $this->urls->forJs( $asset );
1013
  }
1014
 
1015
  public function getPluginUrl_AdminMainPage() :string {
1107
  return $this->root_file;
1108
  }
1109
 
1110
+ public function getReleaseTimestamp() :int {
1111
+ return $this->getCfgProperty( 'release_timestamp' );
 
 
 
1112
  }
1113
 
1114
  public function getTextDomain() :string {
1115
+ return $this->getCfgProperty( 'text_domain' );
1116
  }
1117
 
1118
+ public function getBuild() :string {
1119
+ return $this->getCfgProperty( 'build' );
 
 
 
1120
  }
1121
 
1122
+ public function getVersion() :string {
1123
+ return $this->getCfgProperty( 'version' );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1124
  }
1125
 
1126
  public function getVersionNumeric() :int {
1156
  }
1157
  }
1158
 
1159
+ public function isPremiumExtensionsEnabled() :bool {
1160
+ return (bool)$this->getCfgProperty( 'enable_premium' );
 
 
 
1161
  }
1162
 
1163
  public function isPremiumActive() :bool {
1169
  }
1170
 
1171
  protected function saveCurrentPluginControllerOptions() {
 
1172
  add_filter( $this->prefix( 'bypass_is_plugin_admin' ), '__return_true' );
1173
 
1174
  if ( $this->plugin_deleting ) {
1177
  elseif ( isset( $this->cfg ) ) {
1178
  Config\Ops\Save::ToWp( $this->cfg, $this->getConfigStoreKey() );
1179
  }
 
 
 
 
 
 
 
1180
  remove_filter( $this->prefix( 'bypass_is_plugin_admin' ), '__return_true' );
1181
  }
1182
 
1194
  return 'aptoweb_controller_'.substr( md5( get_class() ), 0, 6 );
1195
  }
1196
 
 
 
 
 
 
 
 
 
1197
  public function deactivateSelf() {
1198
  if ( $this->isPluginAdmin() && function_exists( 'deactivate_plugins' ) ) {
1199
+ deactivate_plugins( [ $this->base_file ] );
1200
  }
1201
  }
1202
 
1286
  * @throws \Exception from loadFeatureHandler()
1287
  */
1288
  public function loadCorePluginFeatureHandler() {
1289
+ $plugin = $this->modules[ 'plugin' ] ?? null;
1290
+ if ( !$plugin instanceof Shield\Modules\Plugin\ModCon ) {
1291
  $this->loadFeatureHandler(
1292
  [
1293
  'slug' => 'plugin',
1331
 
1332
  /**
1333
  * @param string $slug
1334
+ * @return Shield\Modules\Base\ModCon|null|mixed
1335
  */
1336
  public function getModule( string $slug ) {
1337
  $mod = isset( $this->modules[ $slug ] ) ? $this->modules[ $slug ] : null;
1338
+ if ( !$mod instanceof Shield\Modules\Base\ModCon ) {
1339
  try {
1340
  $mods = $this->loadCorePluginFeatureHandler()->getActivePluginFeatures();
1341
  if ( isset( $mods[ $slug ] ) ) {
1422
 
1423
  /**
1424
  * @param array $modProps
1425
+ * @return Shield\Modules\Base\ModCon|mixed
1426
  * @throws \Exception
1427
  */
1428
  public function loadFeatureHandler( array $modProps ) {
1429
  $modSlug = $modProps[ 'slug' ];
1430
  $mod = isset( $this->modules[ $modSlug ] ) ? $this->modules[ $modSlug ] : null;
1431
+ if ( $mod instanceof Shield\Modules\Base\ModCon ) {
1432
  return $mod;
1433
  }
1434
 
1626
  ->run();
1627
  }
1628
  }
1629
+
1630
+ /**
1631
+ * @deprecated 10.2
1632
+ */
1633
+ public function onWpAdminMenu() {
1634
+ }
1635
+
1636
+ /**
1637
+ * @param \WP_Admin_Bar $adminBar
1638
+ * @deprecated 10.2
1639
+ */
1640
+ public function onWpAdminBarMenu( $adminBar ) {
1641
+ }
1642
+
1643
+ /**
1644
+ * @deprecated 10.2
1645
+ */
1646
+ public function onWpDashboardSetup() {
1647
+ }
1648
+
1649
+ /**
1650
+ * @deprecated 10.2
1651
+ */
1652
+ protected function createPluginMenu() :bool {
1653
+ }
1654
+
1655
+ /**
1656
+ * @deprecated 10.2
1657
+ */
1658
+ protected function fixSubmenu() {
1659
+ }
1660
+
1661
+ /**
1662
+ * Displaying all views now goes through this central function and we work out
1663
+ * what to display based on the name of current hook/filter being processed.
1664
+ * @deprecated 10.2
1665
+ */
1666
+ public function onDisplayTopMenu() {
1667
+ }
1668
+
1669
+ /**
1670
+ * @deprecated 10.2
1671
+ */
1672
+ public function onWpEnqueueFrontendCss() {
1673
+ }
1674
+
1675
+ /**
1676
+ * @deprecated 10.2
1677
+ */
1678
+ public function onWpEnqueueAdminJs() {
1679
+ }
1680
+
1681
+ /**
1682
+ * @deprecated 10.2
1683
+ */
1684
+ public function onWpEnqueueAdminCss() {
1685
+ }
1686
  }
src/lib/src/Controller/Utilities/Upgrade.php CHANGED
@@ -1,26 +1,45 @@
1
- <?php
2
 
3
  namespace FernleafSystems\Wordpress\Plugin\Shield\Controller\Utilities;
4
 
 
5
  use FernleafSystems\Wordpress\Plugin\Shield;
 
6
 
7
  class Upgrade {
8
 
9
  use Shield\Modules\PluginControllerConsumer;
10
- use \FernleafSystems\Utilities\Logic\OneTimeExecute;
 
 
 
 
 
11
 
12
  protected function run() {
13
  $con = $this->getCon();
14
 
15
- if ( $con->cfg->previous_version !== $con->getVersion() ) {
16
- foreach ( $con->modules as $mod ) {
17
- $H = $mod->getUpgradeHandler();
18
- if ( $H instanceof Shield\Modules\Base\Upgrade ) {
19
- $H->execute();
20
- }
21
- }
22
- }
23
 
24
  $con->cfg->previous_version = $con->getVersion();
25
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
26
  }
1
+ <?php declare( strict_types=1 );
2
 
3
  namespace FernleafSystems\Wordpress\Plugin\Shield\Controller\Utilities;
4
 
5
+ use FernleafSystems\Utilities\Logic\ExecOnce;
6
  use FernleafSystems\Wordpress\Plugin\Shield;
7
+ use FernleafSystems\Wordpress\Services\Services;
8
 
9
  class Upgrade {
10
 
11
  use Shield\Modules\PluginControllerConsumer;
12
+ use ExecOnce;
13
+
14
+ protected function canRun() :bool {
15
+ $con = $this->getCon();
16
+ return $con->cfg->previous_version !== $con->getVersion();
17
+ }
18
 
19
  protected function run() {
20
  $con = $this->getCon();
21
 
22
+ $this->upgradeModules();
23
+ do_action( $con->prefix( 'plugin_shutdown' ), function () {
24
+ $this->deleteOldModConfigs();
25
+ } );
 
 
 
 
26
 
27
  $con->cfg->previous_version = $con->getVersion();
28
  }
29
+
30
+ private function deleteOldModConfigs() {
31
+ $DB = Services::WpDb();
32
+ $DB->doSql(
33
+ sprintf( 'DELETE from `%s` where `option_name` LIKE "shield_mod_config_%%"', $DB->getTable_Options() )
34
+ );
35
+ }
36
+
37
+ private function upgradeModules() {
38
+ foreach ( $this->getCon()->modules as $mod ) {
39
+ $H = $mod->getUpgradeHandler();
40
+ if ( $H instanceof Shield\Modules\Base\Upgrade ) {
41
+ $H->execute();
42
+ }
43
+ }
44
+ }
45
  }
src/lib/src/Crons/StandardCron.php CHANGED
@@ -18,7 +18,7 @@ trait StandardCron {
18
  ->setNextRun( $this->getFirstRunTimestamp() )
19
  ->createCronJob( $this->getCronName(), [ $this, 'runCron' ] );
20
  }
21
- catch ( \Exception $oE ) {
22
  }
23
  add_action( $this->getCon()->prefix( 'deactivate_plugin' ), [ $this, 'deleteCron' ] );
24
  }
18
  ->setNextRun( $this->getFirstRunTimestamp() )
19
  ->createCronJob( $this->getCronName(), [ $this, 'runCron' ] );
20
  }
21
+ catch ( \Exception $e ) {
22
  }
23
  add_action( $this->getCon()->prefix( 'deactivate_plugin' ), [ $this, 'deleteCron' ] );
24
  }
src/lib/src/Databases/Base/EntryVoConsumer.php CHANGED
@@ -21,11 +21,11 @@ trait EntryVoConsumer {
21
  }
22
 
23
  /**
24
- * @param EntryVO $oE
25
  * @return $this
26
  */
27
- public function setEntryVO( $oE ) {
28
- $this->oEntryVO = $oE;
29
  return $this;
30
  }
31
  }
21
  }
22
 
23
  /**
24
+ * @param EntryVO $entry
25
  * @return $this
26
  */
27
+ public function setEntryVO( $entry ) {
28
+ $this->oEntryVO = $entry;
29
  return $this;
30
  }
31
  }
src/lib/src/Databases/Base/Handler.php CHANGED
@@ -166,10 +166,9 @@ abstract class Handler {
166
 
167
  public function tableTrimExcess( int $nRowsLimit ) :self {
168
  try {
169
- $this->getQueryDeleter()
170
- ->deleteExcess( $nRowsLimit );
171
  }
172
- catch ( \Exception $oE ) {
173
  }
174
  return $this;
175
  }
@@ -228,17 +227,14 @@ abstract class Handler {
228
  return $sch;
229
  }
230
 
231
- /**
232
- * @return string
233
- */
234
- private function getNamespace() {
235
  try {
236
- $sNS = ( new \ReflectionClass( $this ) )->getNamespaceName();
237
  }
238
- catch ( \Exception $oE ) {
239
- $sNS = __NAMESPACE__;
240
  }
241
- return rtrim( $sNS, '\\' ).'\\';
242
  }
243
 
244
  /**
166
 
167
  public function tableTrimExcess( int $nRowsLimit ) :self {
168
  try {
169
+ $this->getQueryDeleter()->deleteExcess( $nRowsLimit );
 
170
  }
171
+ catch ( \Exception $e ) {
172
  }
173
  return $this;
174
  }
227
  return $sch;
228
  }
229
 
230
+ private function getNamespace() :string {
 
 
 
231
  try {
232
+ $namespace = ( new \ReflectionClass( $this ) )->getNamespaceName();
233
  }
234
+ catch ( \Exception $e ) {
235
+ $namespace = __NAMESPACE__;
236
  }
237
+ return rtrim( $namespace, '\\' ).'\\';
238
  }
239
 
240
  /**
src/lib/src/Databases/Base/Insert.php CHANGED
@@ -72,16 +72,16 @@ class Insert extends BaseQuery {
72
  public function query() {
73
  try {
74
  $this->verifyInsertData();
75
- $bSuccess = Services::WpDb()
76
- ->insertDataIntoTable(
77
- $this->getDbH()->getTable(),
78
- $this->getInsertData()
79
- );
80
  }
81
- catch ( \Exception $oE ) {
82
- $bSuccess = false;
83
  }
84
- return $bSuccess;
85
  }
86
 
87
  /**
72
  public function query() {
73
  try {
74
  $this->verifyInsertData();
75
+ $success = Services::WpDb()
76
+ ->insertDataIntoTable(
77
+ $this->getDbH()->getTable(),
78
+ $this->getInsertData()
79
+ );
80
  }
81
+ catch ( \Exception $e ) {
82
+ $success = false;
83
  }
84
+ return $success;
85
  }
86
 
87
  /**
src/lib/src/Databases/IPs/CommonFilters.php CHANGED
@@ -40,14 +40,18 @@ trait CommonFilters {
40
  }
41
 
42
  /**
43
- * @param bool $bIsRange
44
  * @return $this
45
  */
46
- public function filterByIsRange( $bIsRange ) {
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
 
40
  }
41
 
42
  /**
43
+ * @param bool $isRange
44
  * @return $this
45
  */
46
+ public function filterByIsRange( bool $isRange ) {
47
+ return $this->addWhereEquals( 'is_range', $isRange ? 1 : 0 );
48
  }
49
 
50
+ /**
51
+ * @param string $label
52
+ * @return $this
53
+ */
54
+ public function filterByLabel( string $label ) {
55
  return $this->addWhereEquals( 'label', $label );
56
  }
57
 
src/lib/src/Modules/AuditTrail/Options.php CHANGED
@@ -15,75 +15,4 @@ class Options extends BaseShield\Options {
15
  (int)$this->getOpt( 'audit_trail_max_entries' ) :
16
  (int)$this->getDef( 'audit_trail_free_max_entries' );
17
  }
18
-
19
- /**
20
- * @return bool
21
- * @deprecated 10.1
22
- */
23
- public function isEnabledAuditing() :bool {
24
- return true;
25
- }
26
-
27
- /**
28
- * @return bool
29
- * @deprecated 10.1
30
- */
31
- public function isAuditEmails() :bool {
32
- return $this->isOpt( 'enable_audit_context_emails', 'Y' );
33
- }
34
-
35
- /**
36
- * @return bool
37
- * @deprecated 10.1
38
- */
39
- public function isAuditPlugins() :bool {
40
- return $this->isOpt( 'enable_audit_context_plugins', 'Y' );
41
- }
42
-
43
- /**
44
- * @return bool
45
- * @deprecated 10.1
46
- */
47
- public function isAuditPosts() :bool {
48
- return $this->isOpt( 'enable_audit_context_posts', 'Y' );
49
- }
50
-
51
- /**
52
- * @return bool
53
- * @deprecated 10.1
54
- */
55
- public function isAuditShield() :bool {
56
- return $this->isOpt( 'enable_audit_context_wpsf', 'Y' );
57
- }
58
-
59
- /**
60
- * @return bool
61
- * @deprecated 10.1
62
- */
63
- public function isAuditThemes() :bool {
64
- return $this->isOpt( 'enable_audit_context_themes', 'Y' );
65
- }
66
-
67
- /**
68
- * @return bool
69
- * @deprecated 10.1
70
- */
71
- public function isAuditUsers() :bool {
72
- return $this->isOpt( 'enable_audit_context_users', 'Y' );
73
- }
74
-
75
- /**
76
- * @return bool
77
- * @deprecated 10.1
78
- */
79
- public function isAuditWp() :bool {
80
- return $this->isOpt( 'enable_audit_context_wordpress', 'Y' );
81
- }
82
-
83
- /**
84
- * @return $this
85
- */
86
- public function updateCTLastSnapshotAt() {
87
- return $this->setOptAt( 'ct_last_snapshot_at' );
88
- }
89
  }
15
  (int)$this->getOpt( 'audit_trail_max_entries' ) :
16
  (int)$this->getDef( 'audit_trail_free_max_entries' );
17
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
18
  }
src/lib/src/Modules/Autoupdates/Insights/OverviewCards.php CHANGED
@@ -3,15 +3,15 @@
3
  namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Autoupdates\Insights;
4
 
5
  use FernleafSystems\Wordpress\Plugin\Shield;
6
- use FernleafSystems\Wordpress\Plugin\Shield\Modules\Autoupdates\Options;
7
  use FernleafSystems\Wordpress\Services\Services;
8
 
9
  class OverviewCards extends Shield\Modules\Base\Insights\OverviewCards {
10
 
11
  public function build() :array {
12
- /** @var \ICWP_WPSF_FeatureHandler_Autoupdates $mod */
13
  $mod = $this->getMod();
14
- /** @var Options $opts */
15
  $opts = $this->getOptions();
16
  $WP = Services::WpGeneral();
17
 
3
  namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Autoupdates\Insights;
4
 
5
  use FernleafSystems\Wordpress\Plugin\Shield;
6
+ use FernleafSystems\Wordpress\Plugin\Shield\Modules\Autoupdates;
7
  use FernleafSystems\Wordpress\Services\Services;
8
 
9
  class OverviewCards extends Shield\Modules\Base\Insights\OverviewCards {
10
 
11
  public function build() :array {
12
+ /** @var Autoupdates\ModCon $mod */
13
  $mod = $this->getMod();
14
+ /** @var Autoupdates\Options $opts */
15
  $opts = $this->getOptions();
16
  $WP = Services::WpGeneral();
17
 
src/lib/src/Modules/Autoupdates/Processor.php CHANGED
@@ -238,7 +238,7 @@ class Processor extends BaseShield\Processor {
238
  elseif ( $oOpts->isAutoupdateAllPlugins() ) {
239
  $bDoAutoUpdate = true;
240
  }
241
- elseif ( $file === $this->getCon()->getPluginBaseFile() ) {
242
  $sAuto = $oOpts->getSelfAutoUpdateOpt();
243
  if ( $sAuto === 'immediate' ) {
244
  $bDoAutoUpdate = true;
238
  elseif ( $oOpts->isAutoupdateAllPlugins() ) {
239
  $bDoAutoUpdate = true;
240
  }
241
+ elseif ( $file === $this->getCon()->base_file ) {
242
  $sAuto = $oOpts->getSelfAutoUpdateOpt();
243
  if ( $sAuto === 'immediate' ) {
244
  $bDoAutoUpdate = true;
src/lib/src/Modules/Base/AdminNotices.php CHANGED
@@ -69,7 +69,7 @@ class AdminNotices {
69
  $aNotices[] = $notice;
70
  }
71
  }
72
- catch ( \Exception $oE ) {
73
  }
74
  }
75
  }
69
  $aNotices[] = $notice;
70
  }
71
  }
72
+ catch ( \Exception $e ) {
73
  }
74
  }
75
  }
src/lib/src/Modules/Base/AjaxHandlerBase.php DELETED
@@ -1,110 +0,0 @@
1
- <?php
2
-
3
- namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Base;
4
-
5
- use FernleafSystems\Wordpress\Plugin\Shield\Modules\ModConsumer;
6
- use FernleafSystems\Wordpress\Services\Services;
7
-
8
- /**
9
- * Class AjaxHandlerBase
10
- * @package FernleafSystems\Wordpress\Plugin\Shield\Modules\Base
11
- * @deprecated 10.1
12
- */
13
- abstract class AjaxHandlerBase {
14
-
15
- use ModConsumer;
16
-
17
- public function __construct() {
18
- add_action( 'wp_loaded', [ $this, 'init' ] );
19
- }
20
-
21
- public function init() {
22
- add_filter( $this->getCon()->prefix( 'ajaxAuthAction' ), [ $this, 'handleAjaxAuth' ], 10, 2 );
23
- add_filter( $this->getCon()->prefix( 'ajaxNonAuthAction' ), [ $this, 'handleAjaxNonAuth' ], 10, 2 );
24
- }
25
-
26
- /**
27
- * @param array $aAjaxResponse
28
- * @param string $sAjaxAction
29
- * @return array
30
- */
31
- public function handleAjaxAuth( $aAjaxResponse, $sAjaxAction ) {
32
- if ( !empty( $sAjaxAction ) && ( empty( $aAjaxResponse ) || !is_array( $aAjaxResponse ) ) ) {
33
- $aAjaxResponse = $this->normaliseAjaxResponse( $this->processAjaxAction( $sAjaxAction ) );
34
- }
35
- return $aAjaxResponse;
36
- }
37
-
38
- /**
39
- * @param array $aAjaxResponse
40
- * @param string $sAjaxAction
41
- * @return array
42
- */
43
- public function handleAjaxNonAuth( $aAjaxResponse, $sAjaxAction ) {
44
- if ( !empty( $sAjaxAction ) && ( empty( $aAjaxResponse ) || !is_array( $aAjaxResponse ) ) ) {
45
- $aAjaxResponse = $this->normaliseAjaxResponse( $this->processAjaxAction( $sAjaxAction ) );
46
- }
47
- return $aAjaxResponse;
48
- }
49
-
50
- /**
51
- * @param string $sEncoding
52
- * @return array
53
- */
54
- protected function getAjaxFormParams( $sEncoding = 'none' ) {
55
- $oReq = Services::Request();
56
- $aFormParams = [];
57
- $sRaw = $oReq->post( 'form_params', '' );
58
-
59
- if ( !empty( $sRaw ) ) {
60
-
61
- $sMaybeEncoding = $oReq->post( 'enc_params' );
62
- if ( in_array( $sMaybeEncoding, [ 'none', 'lz-string', 'b64' ] ) ) {
63
- $sEncoding = $sMaybeEncoding;
64
- }
65
-
66
- switch ( $sEncoding ) {
67
- case 'lz-string':
68
- $sRaw = \LZCompressor\LZString::decompress( base64_decode( $sRaw ) );
69
- break;
70
-
71
- case 'b64':
72
- $sRaw = base64_decode( $sRaw );
73
- break;
74
-
75
- case 'none':
76
- default:
77
- break;
78
- }
79
-
80
- parse_str( $sRaw, $aFormParams );
81
- }
82
- return $aFormParams;
83
- }
84
-
85
- protected function processAjaxAction( string $action ) :array {
86
- return [];
87
- }
88
-
89
- /**
90
- * We check for empty since if it's empty, there's nothing to normalize. It's a filter,
91
- * so if we send something back non-empty, it'll be treated like a "handled" response and
92
- * processing will finish
93
- * @param array $aAjaxResponse
94
- * @return array
95
- */
96
- protected function normaliseAjaxResponse( $aAjaxResponse ) {
97
- if ( !empty( $aAjaxResponse ) ) {
98
- $aAjaxResponse = array_merge(
99
- [
100
- 'success' => false,
101
- 'page_reload' => false,
102
- 'message' => 'Unknown',
103
- 'html' => '',
104
- ],
105
- $aAjaxResponse
106
- );
107
- }
108
- return $aAjaxResponse;
109
- }
110
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/lib/src/Modules/Base/AjaxHandlerShield.php DELETED
@@ -1,82 +0,0 @@
1
- <?php
2
-
3
- namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Base;
4
-
5
- /**
6
- * Class AjaxHandlerShield
7
- * @package FernleafSystems\Wordpress\Plugin\Shield\Modules\Base
8
- * @deprecated 10.1
9
- */
10
- class AjaxHandlerShield extends AjaxHandlerBase {
11
-
12
- protected function processAjaxAction( string $action ) :array {
13
- $aResponse = [];
14
- $mod = $this->getMod();
15
-
16
- switch ( $action ) {
17
-
18
- case 'mod_opts_form_render':
19
- $aResponse = $this->ajaxExec_ModOptionsFormRender();
20
- break;
21
-
22
- case 'mod_options':
23
- $aResponse = $this->ajaxExec_ModOptions();
24
- break;
25
-
26
- case 'wiz_process_step':
27
- if ( $mod->hasWizard() ) {
28
- $aResponse = $mod->getWizardHandler()
29
- ->ajaxExec_WizProcessStep();
30
- }
31
- break;
32
-
33
- case 'wiz_render_step':
34
- if ( $mod->hasWizard() ) {
35
- $aResponse = $mod->getWizardHandler()
36
- ->ajaxExec_WizRenderStep();
37
- }
38
- break;
39
-
40
- default:
41
- $aResponse = parent::processAjaxAction( $action );
42
- }
43
-
44
- return $aResponse;
45
- }
46
-
47
- /**
48
- * @return array
49
- */
50
- protected function ajaxExec_ModOptions() {
51
-
52
- $sName = $this->getCon()->getHumanName();
53
-
54
- try {
55
- $this->getMod()->saveOptionsSubmit();
56
- $bSuccess = true;
57
- $sMessage = sprintf( __( '%s Plugin options updated successfully.', 'wp-simple-firewall' ), $sName );
58
- }
59
- catch ( \Exception $oE ) {
60
- $bSuccess = false;
61
- $sMessage = sprintf( __( 'Failed to update %s plugin options.', 'wp-simple-firewall' ), $sName )
62
- .' '.$oE->getMessage();
63
- }
64
-
65
- return [
66
- 'success' => $bSuccess,
67
- 'html' => '', //we reload the page
68
- 'message' => $sMessage
69
- ];
70
- }
71
-
72
- /**
73
- * @return array
74
- */
75
- protected function ajaxExec_ModOptionsFormRender() {
76
- return [
77
- 'success' => true,
78
- 'html' => $this->getMod()->renderOptionsForm(),
79
- 'message' => 'loaded'
80
- ];
81
- }
82
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/lib/src/Modules/Base/BaseProcessor.php CHANGED
@@ -8,7 +8,7 @@ use FernleafSystems\Wordpress\Services\Services;
8
  /**
9
  * Class BaseProcessor
10
  * @package FernleafSystems\Wordpress\Plugin\Shield\Modules\Base
11
- * @deprecated 10.1
12
  */
13
  class BaseProcessor {
14
 
@@ -148,13 +148,6 @@ class BaseProcessor {
148
  return Services::WpGeneral()->getLocale( '-' );
149
  }
150
 
151
- /**
152
- * @return \ICWP_WPSF_Processor_Email
153
- */
154
- public function getEmailProcessor() {
155
- return $this->getMod()->getEmailProcessor();
156
- }
157
-
158
  /**
159
  * @param string $key
160
  * @return BaseProcessor|mixed|null
8
  /**
9
  * Class BaseProcessor
10
  * @package FernleafSystems\Wordpress\Plugin\Shield\Modules\Base
11
+ * @deprecated 10.2
12
  */
13
  class BaseProcessor {
14
 
148
  return Services::WpGeneral()->getLocale( '-' );
149
  }
150
 
 
 
 
 
 
 
 
151
  /**
152
  * @param string $key
153
  * @return BaseProcessor|mixed|null
src/lib/src/Modules/Base/BaseReporting.php DELETED
@@ -1,54 +0,0 @@
1
- <?php
2
-
3
- namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Base;
4
-
5
- use FernleafSystems\Wordpress\Plugin\Shield\Modules\ModConsumer;
6
- use FernleafSystems\Wordpress\Plugin\Shield\Modules\Reporting\Lib\Reports;
7
-
8
- /**
9
- * Class BaseReporting
10
- * @package FernleafSystems\Wordpress\Plugin\Shield\Modules\Base
11
- * @deprecated 10.1
12
- */
13
- abstract class BaseReporting {
14
-
15
- use ModConsumer;
16
-
17
- /**
18
- * @return Reports\BaseReporter[]
19
- */
20
- public function getAlertReporters() :array {
21
- return $this->assignMod( $this->enumAlertReporters() );
22
- }
23
-
24
- /**
25
- * @return Reports\BaseReporter[]
26
- */
27
- public function getInfoReporters() :array {
28
- return $this->assignMod( $this->enumInfoReporters() );
29
- }
30
-
31
- /**
32
- * @return Reports\BaseReporter[]
33
- */
34
- protected function enumAlertReporters() :array {
35
- return [];
36
- }
37
-
38
- /**
39
- * @return Reports\BaseReporter[]
40
- */
41
- protected function enumInfoReporters() :array {
42
- return [];
43
- }
44
-
45
- /**
46
- * @param Reports\BaseReporter[] $aReporters
47
- * @return array
48
- */
49
- protected function assignMod( array $aReporters ) :array {
50
- return array_map( function ( $oReporter ) {
51
- return $oReporter->setMod( $this->getMod() );
52
- }, $aReporters );
53
- }
54
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/lib/src/Modules/Base/ModCon.php CHANGED
@@ -111,8 +111,6 @@ abstract class ModCon {
111
 
112
  add_filter( $con->prefix( 'register_admin_notices' ), [ $this, 'fRegisterAdminNotices' ] );
113
 
114
- add_action( 'admin_enqueue_scripts', [ $this, 'onWpEnqueueAdminJs' ], 100 );
115
-
116
  if ( is_admin() || is_network_admin() ) {
117
  $this->loadAdminNotices();
118
  }
@@ -361,18 +359,14 @@ abstract class ModCon {
361
 
362
  /**
363
  * Override this and adapt per feature
364
- * @return Shield\Modules\Base\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 );
@@ -380,20 +374,6 @@ abstract class ModCon {
380
  return $this->oProcessor;
381
  }
382
 
383
- /**
384
- * This is the old method
385
- * @deprecated 10.1
386
- */
387
- protected function getProcessorClassName() :string {
388
- return '\\'.implode( '_',
389
- [
390
- strtoupper( $this->getCon()->getPluginPrefix( '_' ) ),
391
- 'Processor',
392
- str_replace( ' ', '', ucwords( str_replace( '_', ' ', $this->getSlug() ) ) )
393
- ]
394
- );
395
- }
396
-
397
  /**
398
  * Override this and adapt per feature
399
  * @return string
@@ -409,7 +389,7 @@ abstract class ModCon {
409
  }
410
 
411
  public function isUpgrading() :bool {
412
- return $this->getCon()->getIsRebuildOptionsFromFile() || $this->getOptions()->getRebuildFromFile();
413
  }
414
 
415
  /**
@@ -516,7 +496,7 @@ abstract class ModCon {
516
  * TODO: Get rid of this crap and/or handle the \Exception thrown in loadFeatureHandler()
517
  * @return Modules\Email\ModCon
518
  * @throws \Exception
519
- * @deprecated 10.1
520
  */
521
  public function getEmailHandler() {
522
  return $this->getCon()->getModule_Email();
@@ -818,13 +798,13 @@ abstract class ModCon {
818
  }
819
 
820
  /**
821
- * @param string $sAction
822
  * @return array
823
  */
824
- public function getNonceActionData( $sAction = '' ) {
825
- $aData = $this->getCon()->getNonceActionData( $sAction );
826
- $aData[ 'mod_slug' ] = $this->getModSlug();
827
- return $aData;
828
  }
829
 
830
  /**
@@ -928,17 +908,17 @@ abstract class ModCon {
928
  * @return array - map of each option to its option type
929
  */
930
  protected function getAllFormOptionsAndTypes() {
931
- $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' ];
937
  }
938
  }
939
  }
940
 
941
- return $aOpts;
942
  }
943
 
944
  protected function handleModAction( string $action ) {
@@ -965,17 +945,17 @@ abstract class ModCon {
965
  }
966
 
967
  /**
968
- * @param string $sMsg
969
- * @param bool $bError
970
  * @param bool $bShowOnLogin
971
  * @return $this
972
  */
973
- public function setFlashAdminNotice( $sMsg, $bError = false, $bShowOnLogin = false ) {
974
  $this->getCon()
975
  ->getAdminNotices()
976
  ->addFlash(
977
- sprintf( '[%s] %s', $this->getCon()->getHumanName(), $sMsg ),
978
- $bError,
979
  $bShowOnLogin
980
  );
981
  return $this;
@@ -985,10 +965,7 @@ abstract class ModCon {
985
  return is_admin() && !Services::WpGeneral()->isAjax() && $this->isThisModulePage();
986
  }
987
 
988
- /**
989
- * @return bool
990
- */
991
- public function isPremium() {
992
  return $this->getCon()->isPremiumActive();
993
  }
994
 
@@ -1062,47 +1039,32 @@ abstract class ModCon {
1062
 
1063
  protected function runWizards() {
1064
  if ( $this->isWizardPage() && $this->hasWizard() ) {
1065
- $oWiz = $this->getWizardHandler();
1066
- if ( $oWiz instanceof \ICWP_WPSF_Wizard_Base ) {
1067
- $oWiz->init();
1068
  }
1069
  }
1070
  }
1071
 
1072
- /**
1073
- * @return bool
1074
- */
1075
- public function isThisModulePage() {
1076
  return $this->getCon()->isModulePage()
1077
  && Services::Request()->query( 'page' ) == $this->getModSlug();
1078
  }
1079
 
1080
- /**
1081
- * @return bool
1082
- */
1083
- public function isPage_Insights() {
1084
  return Services::Request()->query( 'page' ) == $this->getCon()->getModule_Insights()->getModSlug();
1085
  }
1086
 
1087
- /**
1088
- * @return bool
1089
- */
1090
- public function isPage_InsightsThisModule() {
1091
  return $this->isPage_Insights()
1092
  && Services::Request()->query( 'subnav' ) == $this->getSlug();
1093
  }
1094
 
1095
- /**
1096
- * @return bool
1097
- */
1098
- protected function isModuleOptionsRequest() {
1099
  return Services::Request()->post( 'mod_slug' ) === $this->getModSlug();
1100
  }
1101
 
1102
- /**
1103
- * @return bool
1104
- */
1105
- protected function isWizardPage() {
1106
  return ( $this->getCon()->getShieldAction() == 'wizard' && $this->isThisModulePage() );
1107
  }
1108
 
@@ -1259,30 +1221,19 @@ abstract class ModCon {
1259
  ->setTemplateEngineTwig()
1260
  ->render();
1261
  }
1262
- catch ( \Exception $oE ) {
1263
- return 'Error rendering options form: '.$oE->getMessage();
1264
  }
1265
  }
1266
 
1267
- /**
1268
- * @return bool
1269
- */
1270
- public function canDisplayOptionsForm() {
1271
  return $this->getOptions()->isAccessRestricted() ? $this->getCon()->isPluginAdmin() : true;
1272
  }
1273
 
1274
- public function onWpEnqueueAdminJs() {
1275
- $this->insertCustomJsVars_Admin();
1276
- }
1277
-
1278
- /**
1279
- * Override this with custom JS vars for your particular module.
1280
- */
1281
- public function insertCustomJsVars_Admin() {
1282
-
1283
- if ( $this->isThisModulePage() ) {
1284
- wp_localize_script(
1285
- $this->prefix( 'plugin' ),
1286
  'icwp_wpsf_vars_base',
1287
  [
1288
  'ajax' => [
@@ -1290,8 +1241,19 @@ abstract class ModCon {
1290
  'mod_opts_form_render' => $this->getAjaxActionData( 'mod_opts_form_render' ),
1291
  ]
1292
  ]
1293
- );
1294
- }
 
 
 
 
 
 
 
 
 
 
 
1295
  }
1296
 
1297
  /**
@@ -1357,9 +1319,9 @@ abstract class ModCon {
1357
  ->setRenderVars( $data )
1358
  ->render();
1359
  }
1360
- catch ( \Exception $oE ) {
1361
- $render = $oE->getMessage();
1362
- error_log( $oE->getMessage() );
1363
  }
1364
 
1365
  return (string)$render;
@@ -1406,16 +1368,16 @@ abstract class ModCon {
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
  }
@@ -1476,14 +1438,6 @@ abstract class ModCon {
1476
  $this->loadModElement( 'AjaxHandler' );
1477
  }
1478
 
1479
- /**
1480
- * @return Shield\Modules\Base\ShieldOptions|mixed
1481
- * @deprecated 10.1
1482
- */
1483
- protected function loadOptions() {
1484
- return $this->loadModElement( 'Options' );
1485
- }
1486
-
1487
  protected function loadDebug() {
1488
  $req = Services::Request();
1489
  if ( $req->query( 'debug' ) && $req->query( 'mod' ) == $this->getModSlug()
111
 
112
  add_filter( $con->prefix( 'register_admin_notices' ), [ $this, 'fRegisterAdminNotices' ] );
113
 
 
 
114
  if ( is_admin() || is_network_admin() ) {
115
  $this->loadAdminNotices();
116
  }
359
 
360
  /**
361
  * Override this and adapt per feature
362
+ * @return Shield\Modules\Base\Processor|mixed
363
  */
364
  protected function loadProcessor() {
365
  if ( !isset( $this->oProcessor ) ) {
366
  try {
 
367
  $class = $this->findElementClass( 'Processor', true );
368
  }
369
  catch ( \Exception $e ) {
 
 
 
370
  return null;
371
  }
372
  $this->oProcessor = new $class( $this );
374
  return $this->oProcessor;
375
  }
376
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
377
  /**
378
  * Override this and adapt per feature
379
  * @return string
389
  }
390
 
391
  public function isUpgrading() :bool {
392
+ return $this->getCon()->cfg->rebuilt || $this->getOptions()->getRebuildFromFile();
393
  }
394
 
395
  /**
496
  * TODO: Get rid of this crap and/or handle the \Exception thrown in loadFeatureHandler()
497
  * @return Modules\Email\ModCon
498
  * @throws \Exception
499
+ * @deprecated 10.2
500
  */
501
  public function getEmailHandler() {
502
  return $this->getCon()->getModule_Email();
798
  }
799
 
800
  /**
801
+ * @param string $action
802
  * @return array
803
  */
804
+ public function getNonceActionData( $action = '' ) {
805
+ $data = $this->getCon()->getNonceActionData( $action );
806
+ $data[ 'mod_slug' ] = $this->getModSlug();
807
+ return $data;
808
  }
809
 
810
  /**
908
  * @return array - map of each option to its option type
909
  */
910
  protected function getAllFormOptionsAndTypes() {
911
+ $opts = [];
912
 
913
  foreach ( $this->getUIHandler()->buildOptions() as $aOptionsSection ) {
914
  if ( !empty( $aOptionsSection ) ) {
915
  foreach ( $aOptionsSection[ 'options' ] as $aOption ) {
916
+ $opts[ $aOption[ 'key' ] ] = $aOption[ 'type' ];
917
  }
918
  }
919
  }
920
 
921
+ return $opts;
922
  }
923
 
924
  protected function handleModAction( string $action ) {
945
  }
946
 
947
  /**
948
+ * @param string $msg
949
+ * @param bool $isError
950
  * @param bool $bShowOnLogin
951
  * @return $this
952
  */
953
+ public function setFlashAdminNotice( $msg, $isError = false, $bShowOnLogin = false ) {
954
  $this->getCon()
955
  ->getAdminNotices()
956
  ->addFlash(
957
+ sprintf( '[%s] %s', $this->getCon()->getHumanName(), $msg ),
958
+ $isError,
959
  $bShowOnLogin
960
  );
961
  return $this;
965
  return is_admin() && !Services::WpGeneral()->isAjax() && $this->isThisModulePage();
966
  }
967
 
968
+ public function isPremium() :bool {
 
 
 
969
  return $this->getCon()->isPremiumActive();
970
  }
971
 
1039
 
1040
  protected function runWizards() {
1041
  if ( $this->isWizardPage() && $this->hasWizard() ) {
1042
+ $wiz = $this->getWizardHandler();
1043
+ if ( $wiz instanceof \ICWP_WPSF_Wizard_Base ) {
1044
+ $wiz->init();
1045
  }
1046
  }
1047
  }
1048
 
1049
+ public function isThisModulePage() :bool {
 
 
 
1050
  return $this->getCon()->isModulePage()
1051
  && Services::Request()->query( 'page' ) == $this->getModSlug();
1052
  }
1053
 
1054
+ public function isPage_Insights() :bool {
 
 
 
1055
  return Services::Request()->query( 'page' ) == $this->getCon()->getModule_Insights()->getModSlug();
1056
  }
1057
 
1058
+ public function isPage_InsightsThisModule() :bool {
 
 
 
1059
  return $this->isPage_Insights()
1060
  && Services::Request()->query( 'subnav' ) == $this->getSlug();
1061
  }
1062
 
1063
+ protected function isModuleOptionsRequest() :bool {
 
 
 
1064
  return Services::Request()->post( 'mod_slug' ) === $this->getModSlug();
1065
  }
1066
 
1067
+ protected function isWizardPage() :bool {
 
 
 
1068
  return ( $this->getCon()->getShieldAction() == 'wizard' && $this->isThisModulePage() );
1069
  }
1070
 
1221
  ->setTemplateEngineTwig()
1222
  ->render();
1223
  }
1224
+ catch ( \Exception $e ) {
1225
+ return 'Error rendering options form: '.$e->getMessage();
1226
  }
1227
  }
1228
 
1229
+ public function canDisplayOptionsForm() :bool {
 
 
 
1230
  return $this->getOptions()->isAccessRestricted() ? $this->getCon()->isPluginAdmin() : true;
1231
  }
1232
 
1233
+ public function getScriptLocalisations() :array {
1234
+ return [
1235
+ [
1236
+ 'plugin',
 
 
 
 
 
 
 
 
1237
  'icwp_wpsf_vars_base',
1238
  [
1239
  'ajax' => [
1241
  'mod_opts_form_render' => $this->getAjaxActionData( 'mod_opts_form_render' ),
1242
  ]
1243
  ]
1244
+ ]
1245
+ ];
1246
+ }
1247
+
1248
+ public function getCustomScriptEnqueues() :array {
1249
+ return [];
1250
+ }
1251
+
1252
+ /**
1253
+ * Override this with custom JS vars for your particular module.
1254
+ * @deprecated 10.2
1255
+ */
1256
+ public function insertCustomJsVars_Admin() {
1257
  }
1258
 
1259
  /**
1319
  ->setRenderVars( $data )
1320
  ->render();
1321
  }
1322
+ catch ( \Exception $e ) {
1323
+ $render = $e->getMessage();
1324
+ error_log( $e->getMessage() );
1325
  }
1326
 
1327
  return (string)$render;
1368
  }
1369
 
1370
  /**
1371
+ * @return null|Shield\Modules\Base\Options|mixed
1372
  */
1373
  public function getOptions() {
1374
  if ( !isset( $this->oOpts ) ) {
1375
+ $con = $this->getCon();
1376
  $this->oOpts = $this->loadModElement( 'Options' );
1377
+ $this->oOpts->setPathToConfig( $con->getPath_ConfigFile( $this->getSlug() ) )
1378
+ ->setRebuildFromFile( $con->cfg->rebuilt )
1379
  ->setOptionsStorageKey( $this->getOptionsStorageKey() )
1380
+ ->setIfLoadOptionsFromStorage( !$con->getIsResetPlugin() );
1381
  }
1382
  return $this->oOpts;
1383
  }
1438
  $this->loadModElement( 'AjaxHandler' );
1439
  }
1440
 
 
 
 
 
 
 
 
 
1441
  protected function loadDebug() {
1442
  $req = Services::Request();
1443
  if ( $req->query( 'debug' ) && $req->query( 'mod' ) == $this->getModSlug()
src/lib/src/Modules/Base/Options.php CHANGED
@@ -5,6 +5,7 @@ namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Base;
5
  use FernleafSystems\Wordpress\Plugin\Shield\Modules\Base\Options\OptValueSanitize;
6
  use FernleafSystems\Wordpress\Plugin\Shield\Modules\ModConsumer;
7
  use FernleafSystems\Wordpress\Services\Services;
 
8
 
9
  class Options {
10
 
@@ -88,9 +89,6 @@ class Options {
88
  return $oWp->deleteOption( $this->getOptionsStorageKey() );
89
  }
90
 
91
- /**
92
- * @return array
93
- */
94
  public function getAllOptionsValues() :array {
95
  return $this->getStoredOptions();
96
  }
@@ -286,26 +284,23 @@ class Options {
286
  return $aSections;
287
  }
288
 
289
- /**
290
- * @return array
291
- */
292
- public function getPrimarySection() {
293
- $aSec = [];
294
  foreach ( $this->getSections() as $aS ) {
295
  if ( isset( $aS[ 'primary' ] ) && $aS[ 'primary' ] ) {
296
- $aSec = $aS;
297
  break;
298
  }
299
  }
300
- return $aSec;
301
  }
302
 
303
  /**
304
- * @param string $sSlug
305
  * @return array
306
  */
307
- public function getSection_Requirements( $sSlug ) {
308
- $aSection = $this->getSection( $sSlug );
309
  $aReqs = ( is_array( $aSection ) && isset( $aSection[ 'reqs' ] ) ) ? $aSection[ 'reqs' ] : [];
310
  return array_merge(
311
  [
@@ -326,27 +321,27 @@ class Options {
326
  }
327
 
328
  /**
329
- * @param string $sSectionSlug
330
  * @return bool
331
  */
332
- public function isSectionReqsMet( $sSectionSlug ) {
333
- $aReqs = $this->getSection_Requirements( $sSectionSlug );
334
- return Services::Data()->getPhpVersionIsAtLeast( $aReqs[ 'php_min' ] )
335
- && Services::WpGeneral()->getWordpressIsAtLeastVersion( $aReqs[ 'wp_min' ] );
336
  }
337
 
338
  /**
339
- * @param string $sOptKey
340
  * @return bool
341
  */
342
- public function isOptReqsMet( $sOptKey ) {
343
- return $this->isSectionReqsMet( $this->getOptProperty( $sOptKey, 'section' ) );
344
  }
345
 
346
  /**
347
  * @return string[]
348
  */
349
- public function getVisibleOptionsKeys() {
350
  $aKeys = [];
351
 
352
  foreach ( $this->getRawData_AllOptions() as $aOptionDef ) {
@@ -392,15 +387,15 @@ class Options {
392
  }
393
 
394
  /**
395
- * @param string $sSectionSlug
396
  * @return array[]
397
  */
398
- protected function getOptionsForSection( $sSectionSlug ) {
399
 
400
  $aAllOptions = [];
401
  foreach ( $this->getRawData_AllOptions() as $aOptionDef ) {
402
 
403
- if ( ( $aOptionDef[ 'section' ] != $sSectionSlug ) || ( isset( $aOptionDef[ 'hidden' ] ) && $aOptionDef[ 'hidden' ] ) ) {
404
  continue;
405
  }
406
 
@@ -432,10 +427,7 @@ class Options {
432
  return $aAllOptions;
433
  }
434
 
435
- /**
436
- * @return array
437
- */
438
- public function getAdditionalMenuItems() {
439
  return $this->getRawData_MenuItems();
440
  }
441
 
@@ -497,32 +489,29 @@ class Options {
497
  }
498
 
499
  /**
500
- * @param string $sKey
501
  * @param mixed $mValueToTest
502
- * @param bool $bStrict
503
  * @return bool
504
  */
505
- public function isOpt( $sKey, $mValueToTest, $bStrict = false ) :bool {
506
- $mOptionValue = $this->getOpt( $sKey );
507
- return $bStrict ? $mOptionValue === $mValueToTest : $mOptionValue == $mValueToTest;
508
  }
509
 
510
  /**
511
- * @param string $sKey
512
  * @return string|null
513
  */
514
- public function getOptionType( $sKey ) {
515
- $aDef = $this->getRawData_SingleOption( $sKey );
516
- if ( !empty( $aDef ) && isset( $aDef[ 'type' ] ) ) {
517
- return $aDef[ 'type' ];
518
  }
519
  return null;
520
  }
521
 
522
- /**
523
- * @return array
524
- */
525
- public function getOptionsKeys() {
526
  if ( !isset( $this->aOptionsKeys ) ) {
527
  $this->aOptionsKeys = [];
528
  foreach ( $this->getRawData_AllOptions() as $aOption ) {
@@ -733,7 +722,7 @@ class Options {
733
  ->run( $sOptKey, $mNewValue );
734
  $bVerified = true;
735
  }
736
- catch ( \Exception $oE ) {
737
  $bVerified = false;
738
  }
739
 
@@ -894,21 +883,21 @@ class Options {
894
  }
895
 
896
  /**
897
- * @param bool $bReload
898
  * @return array
899
  * @throws \Exception
900
  */
901
- private function loadOptionsValuesFromStorage( bool $bReload = false ) :array {
902
 
903
- if ( $bReload || empty( $this->aOptionsValues ) ) {
904
 
905
  if ( $this->getIfLoadOptionsFromStorage() ) {
906
 
907
- $sStorageKey = $this->getOptionsStorageKey();
908
- if ( empty( $sStorageKey ) ) {
909
  throw new \Exception( 'Options Storage Key Is Empty' );
910
  }
911
- $this->aOptionsValues = Services::WpGeneral()->getOption( $sStorageKey, [] );
912
  }
913
  }
914
  if ( !is_array( $this->aOptionsValues ) ) {
@@ -919,36 +908,32 @@ class Options {
919
  }
920
 
921
  private function readConfiguration() :array {
922
- $WP = Services::WpGeneral();
923
-
924
- $sStorageKey = $this->getConfigStorageKey();
925
- $aConfig = $WP->getOption( $sStorageKey );
926
 
927
- $bRebuild = $this->getRebuildFromFile() || empty( $aConfig );
928
- if ( !$bRebuild && !empty( $aConfig ) && is_array( $aConfig ) ) {
929
-
930
- if ( !isset( $aConfig[ 'meta_modts' ] ) ) {
931
- $aConfig[ 'meta_modts' ] = 0;
932
  }
933
- $bRebuild = $this->getConfigModTime() > $aConfig[ 'meta_modts' ];
934
  }
935
 
936
  if ( $bRebuild ) {
937
  try {
938
- $aConfig = $this->readConfigurationJson();
939
  }
940
- catch ( \Exception $oE ) {
941
  if ( Services::WpGeneral()->isDebug() ) {
942
- trigger_error( $oE->getMessage() );
943
  }
944
- $aConfig = [];
945
  }
946
- $aConfig[ 'meta_modts' ] = $this->getConfigModTime();
947
- $WP->updateOption( $sStorageKey, $aConfig );
948
  }
949
 
950
  $this->setRebuildFromFile( $bRebuild );
951
- return $aConfig;
952
  }
953
 
954
  /**
@@ -974,7 +959,7 @@ class Options {
974
  return Services::Data()->readFileContentsUsingInclude( $this->getPathToConfig() );
975
  }
976
 
977
- private function getConfigStorageKey() :string {
978
  return 'shield_mod_config_'.md5(
979
  str_replace( wp_normalize_path( ABSPATH ), '', wp_normalize_path( $this->getPathToConfig() ) )
980
  );
5
  use FernleafSystems\Wordpress\Plugin\Shield\Modules\Base\Options\OptValueSanitize;
6
  use FernleafSystems\Wordpress\Plugin\Shield\Modules\ModConsumer;
7
  use FernleafSystems\Wordpress\Services\Services;
8
+ use FernleafSystems\Wordpress\Services\Utilities\Options\Transient;
9
 
10
  class Options {
11
 
89
  return $oWp->deleteOption( $this->getOptionsStorageKey() );
90
  }
91
 
 
 
 
92
  public function getAllOptionsValues() :array {
93
  return $this->getStoredOptions();
94
  }
284
  return $aSections;
285
  }
286
 
287
+ public function getPrimarySection() :array {
288
+ $section = [];
 
 
 
289
  foreach ( $this->getSections() as $aS ) {
290
  if ( isset( $aS[ 'primary' ] ) && $aS[ 'primary' ] ) {
291
+ $section = $aS;
292
  break;
293
  }
294
  }
295
+ return $section;
296
  }
297
 
298
  /**
299
+ * @param string $slug
300
  * @return array
301
  */
302
+ public function getSection_Requirements( $slug ) {
303
+ $aSection = $this->getSection( $slug );
304
  $aReqs = ( is_array( $aSection ) && isset( $aSection[ 'reqs' ] ) ) ? $aSection[ 'reqs' ] : [];
305
  return array_merge(
306
  [
321
  }
322
 
323
  /**
324
+ * @param string $slug
325
  * @return bool
326
  */
327
+ public function isSectionReqsMet( $slug ) :bool {
328
+ $reqs = $this->getSection_Requirements( $slug );
329
+ return Services::Data()->getPhpVersionIsAtLeast( $reqs[ 'php_min' ] )
330
+ && Services::WpGeneral()->getWordpressIsAtLeastVersion( $reqs[ 'wp_min' ] );
331
  }
332
 
333
  /**
334
+ * @param string $optKey
335
  * @return bool
336
  */
337
+ public function isOptReqsMet( $optKey ) :bool {
338
+ return $this->isSectionReqsMet( $this->getOptProperty( $optKey, 'section' ) );
339
  }
340
 
341
  /**
342
  * @return string[]
343
  */
344
+ public function getVisibleOptionsKeys() :array {
345
  $aKeys = [];
346
 
347
  foreach ( $this->getRawData_AllOptions() as $aOptionDef ) {
387
  }
388
 
389
  /**
390
+ * @param string $slug
391
  * @return array[]
392
  */
393
+ protected function getOptionsForSection( $slug ) :array {
394
 
395
  $aAllOptions = [];
396
  foreach ( $this->getRawData_AllOptions() as $aOptionDef ) {
397
 
398
+ if ( ( $aOptionDef[ 'section' ] != $slug ) || ( isset( $aOptionDef[ 'hidden' ] ) && $aOptionDef[ 'hidden' ] ) ) {
399
  continue;
400
  }
401
 
427
  return $aAllOptions;
428
  }
429
 
430
+ public function getAdditionalMenuItems() :array {
 
 
 
431
  return $this->getRawData_MenuItems();
432
  }
433
 
489
  }
490
 
491
  /**
492
+ * @param string $key
493
  * @param mixed $mValueToTest
494
+ * @param bool $strict
495
  * @return bool
496
  */
497
+ public function isOpt( string $key, $mValueToTest, $strict = false ) :bool {
498
+ $mOptionValue = $this->getOpt( $key );
499
+ return $strict ? $mOptionValue === $mValueToTest : $mOptionValue == $mValueToTest;
500
  }
501
 
502
  /**
503
+ * @param string $key
504
  * @return string|null
505
  */
506
+ public function getOptionType( $key ) {
507
+ $def = $this->getRawData_SingleOption( $key );
508
+ if ( !empty( $def ) && isset( $def[ 'type' ] ) ) {
509
+ return $def[ 'type' ];
510
  }
511
  return null;
512
  }
513
 
514
+ public function getOptionsKeys() :array {
 
 
 
515
  if ( !isset( $this->aOptionsKeys ) ) {
516
  $this->aOptionsKeys = [];
517
  foreach ( $this->getRawData_AllOptions() as $aOption ) {
722
  ->run( $sOptKey, $mNewValue );
723
  $bVerified = true;
724
  }
725
+ catch ( \Exception $e ) {
726
  $bVerified = false;
727
  }
728
 
883
  }
884
 
885
  /**
886
+ * @param bool $reload
887
  * @return array
888
  * @throws \Exception
889
  */
890
+ private function loadOptionsValuesFromStorage( bool $reload = false ) :array {
891
 
892
+ if ( $reload || empty( $this->aOptionsValues ) ) {
893
 
894
  if ( $this->getIfLoadOptionsFromStorage() ) {
895
 
896
+ $key = $this->getOptionsStorageKey();
897
+ if ( empty( $key ) ) {
898
  throw new \Exception( 'Options Storage Key Is Empty' );
899
  }
900
+ $this->aOptionsValues = Services::WpGeneral()->getOption( $key, [] );
901
  }
902
  }
903
  if ( !is_array( $this->aOptionsValues ) ) {
908
  }
909
 
910
  private function readConfiguration() :array {
911
+ $cfg = Transient::Get( $this->getConfigStorageKey() );
 
 
 
912
 
913
+ $bRebuild = $this->getRebuildFromFile() || empty( $cfg ) || !is_array( $cfg );
914
+ if ( !$bRebuild ) {
915
+ if ( !isset( $cfg[ 'meta_modts' ] ) ) {
916
+ $cfg[ 'meta_modts' ] = 0;
 
917
  }
918
+ $bRebuild = $this->getConfigModTime() > $cfg[ 'meta_modts' ];
919
  }
920
 
921
  if ( $bRebuild ) {
922
  try {
923
+ $cfg = $this->readConfigurationJson();
924
  }
925
+ catch ( \Exception $e ) {
926
  if ( Services::WpGeneral()->isDebug() ) {
927
+ trigger_error( $e->getMessage() );
928
  }
929
+ $cfg = [];
930
  }
931
+ $cfg[ 'meta_modts' ] = $this->getConfigModTime();
932
+ Transient::Set( $this->getConfigStorageKey(), $cfg );
933
  }
934
 
935
  $this->setRebuildFromFile( $bRebuild );
936
+ return $cfg;
937
  }
938
 
939
  /**
959
  return Services::Data()->readFileContentsUsingInclude( $this->getPathToConfig() );
960
  }
961
 
962
+ public function getConfigStorageKey() :string {
963
  return 'shield_mod_config_'.md5(
964
  str_replace( wp_normalize_path( ABSPATH ), '', wp_normalize_path( $this->getPathToConfig() ) )
965
  );
src/lib/src/Modules/Base/Processor.php CHANGED
@@ -4,12 +4,11 @@ namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Base;
4
 
5
  use FernleafSystems\Utilities\Logic\OneTimeExecute;
6
  use FernleafSystems\Wordpress\Plugin\Shield;
7
- use FernleafSystems\Wordpress\Plugin\Shield\Modules;
8
 
9
  abstract class Processor {
10
 
11
- use Modules\ModConsumer;
12
  use Shield\Crons\PluginCronsConsumer;
 
13
  use OneTimeExecute;
14
 
15
  /**
@@ -31,30 +30,4 @@ abstract class Processor {
31
 
32
  public function onModuleShutdown() {
33
  }
34
-
35
- /**
36
- * @deprecated 10.1
37
- */
38
- public function deactivatePlugin() {
39
- }
40
-
41
- /**
42
- * @var BaseProcessor[]
43
- * @deprecated 10.1
44
- */
45
- protected $aSubPros;
46
-
47
- /**
48
- * @deprecated 10.1
49
- */
50
- public function onWpEnqueueJs() {
51
- }
52
-
53
- /**
54
- * @return Modules\Email\Processor
55
- * @deprecated 10.1
56
- */
57
- public function getEmailProcessor() {
58
- return $this->getMod()->getEmailProcessor();
59
- }
60
  }
4
 
5
  use FernleafSystems\Utilities\Logic\OneTimeExecute;
6
  use FernleafSystems\Wordpress\Plugin\Shield;
 
7
 
8
  abstract class Processor {
9
 
 
10
  use Shield\Crons\PluginCronsConsumer;
11
+ use Shield\Modules\ModConsumer;
12
  use OneTimeExecute;
13
 
14
  /**
30
 
31
  public function onModuleShutdown() {
32
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
33
  }
src/lib/src/Modules/Base/ShieldOptions.php DELETED
@@ -1,34 +0,0 @@
1
- <?php
2
-
3
- namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Base;
4
-
5
- use FernleafSystems\Wordpress\Services\Services;
6
-
7
- /**
8
- * Class ShieldOptions
9
- * @package FernleafSystems\Wordpress\Plugin\Shield\Modules\Base
10
- * @deprecated 10.1
11
- */
12
- class ShieldOptions extends Options {
13
-
14
- public function getInstallationDays() :int {
15
- $nTimeInstalled = $this->getCon()
16
- ->getModule_Plugin()
17
- ->getInstallDate();
18
- if ( empty( $nTimeInstalled ) ) {
19
- return 0;
20
- }
21
- return (int)round( ( Services::Request()->ts() - $nTimeInstalled )/DAY_IN_SECONDS );
22
- }
23
-
24
- public function isPremium() :bool {
25
- return $this->getCon()->isPremiumActive();
26
- }
27
-
28
- public function isShowPromoAdminNotices() :bool {
29
- return $this->getCon()
30
- ->getModule_Plugin()
31
- ->getOptions()
32
- ->isOpt( 'enable_upgrade_admin_notice', 'Y' );
33
- }
34
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/lib/src/Modules/Base/ShieldUI.php DELETED
@@ -1,70 +0,0 @@
1
- <?php
2
-
3
- namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Base;
4
-
5
- use FernleafSystems\Wordpress\Plugin\Shield;
6
- use FernleafSystems\Wordpress\Services\Services;
7
-
8
- /**
9
- * Class ShieldUI
10
- * @package FernleafSystems\Wordpress\Plugin\Shield\Modules\Base
11
- * @deprecated 10.1
12
- */
13
- class ShieldUI extends UI {
14
-
15
- public function getBaseDisplayData() :array {
16
- $con = $this->getCon();
17
- /** @var \ICWP_WPSF_FeatureHandler_BaseWpsf $mod */
18
- $mod = $this->getMod();
19
-
20
- return Services::DataManipulation()->mergeArraysRecursive(
21
- parent::getBaseDisplayData(),
22
- [
23
- 'head' => [
24
- 'html' => [
25
- 'lang' => Services::WpGeneral()->getLocale( '-' ),
26
- 'dir' => is_rtl() ? 'rtl' : 'ltr',
27
- ],
28
- 'meta' => [
29
- [
30
- 'type' => 'http-equiv',
31
- 'type_type' => 'Cache-Control',
32
- 'content' => 'no-store, no-cache',
33
- ],
34
- [
35
- 'type' => 'http-equiv',
36
- 'type_type' => 'Expires',
37
- 'content' => '0',
38
- ],
39
- ],
40
- 'scripts' => []
41
- ],
42
- 'ajax' => [
43
- 'sec_admin_login' => $mod->getSecAdminLoginAjaxData(),
44
- ],
45
- 'flags' => [
46
- 'has_session' => $mod->hasSession()
47
- ],
48
- 'hrefs' => [
49
- 'aar_forget_key' => $con->getModule_SecAdmin()->isWlEnabled() ?
50
- $this->getCon()->getLabels()[ 'AuthorURI' ] : 'https://shsec.io/gc'
51
- ],
52
- 'classes' => [
53
- 'top_container' => implode( ' ', array_filter( [
54
- 'odp-outercontainer',
55
- $this->getCon()->isPremiumActive() ? 'is-pro' : 'is-not-pro',
56
- $mod->getModSlug(),
57
- Services::Request()->query( 'inav', '' )
58
- ] ) )
59
- ],
60
- 'vars' => [
61
- 'related_hrefs' => $this->getSettingsRelatedLinks()
62
- ]
63
- ]
64
- );
65
- }
66
-
67
- protected function getSettingsRelatedLinks() :array {
68
- return [];
69
- }
70
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/lib/src/Modules/Base/UI.php CHANGED
@@ -143,7 +143,7 @@ class UI {
143
  }
144
  $aOptParams = Services::DataManipulation()->mergeArraysRecursive( $aOptParams, $aOptStrings );
145
  }
146
- catch ( \Exception $oE ) {
147
  }
148
  return $aOptParams;
149
  }
@@ -169,6 +169,7 @@ class UI {
169
  public function getBaseDisplayData() :array {
170
  $mod = $this->getMod();
171
  $con = $this->getCon();
 
172
 
173
  /** @var Shield\Modules\Plugin\Options $oPluginOptions */
174
  $oPluginOptions = $con->getModule_Plugin()->getOptions();
@@ -234,23 +235,23 @@ class UI {
234
  'wizard_landing' => $mod->getUrl_WizardLanding(),
235
 
236
  'form_action' => Services::Request()->getUri(),
237
- 'css_bootstrap' => $con->getPluginUrl_Css( 'bootstrap4.min' ),
238
- 'css_pages' => $con->getPluginUrl_Css( 'pages' ),
239
- 'css_steps' => $con->getPluginUrl_Css( 'jquery.steps' ),
240
- 'css_fancybox' => $con->getPluginUrl_Css( 'jquery.fancybox.min' ),
241
- 'css_globalplugin' => $con->getPluginUrl_Css( 'global-plugin' ),
242
- 'css_wizard' => $con->getPluginUrl_Css( 'wizard' ),
243
  'js_jquery' => Services::Includes()->getUrl_Jquery(),
244
- 'js_bootstrap' => $con->getPluginUrl_Js( 'bootstrap4.bundle.min' ),
245
- 'js_fancybox' => $con->getPluginUrl_Js( 'jquery.fancybox.min' ),
246
- 'js_globalplugin' => $con->getPluginUrl_Js( 'global-plugin' ),
247
- 'js_steps' => $con->getPluginUrl_Js( 'jquery.steps.min' ),
248
- 'js_wizard' => $con->getPluginUrl_Js( 'wizard' ),
249
  ],
250
  'imgs' => [
251
- 'favicon' => $con->getPluginUrl_Image( 'pluginlogo_24x24.png' ),
252
- 'plugin_banner' => $con->getPluginUrl_Image( 'banner-1500x500-transparent.png' ),
253
- 'background' => $con->getPluginUrl_Image( 'shield/dash-background.jpg' )
254
  ],
255
  'content' => [
256
  'options_form' => '',
@@ -365,7 +366,7 @@ class UI {
365
  try {
366
  $NS = ( new \ReflectionClass( $this ) )->getNamespaceName();
367
  }
368
- catch ( \Exception $oE ) {
369
  $NS = __NAMESPACE__;
370
  }
371
 
143
  }
144
  $aOptParams = Services::DataManipulation()->mergeArraysRecursive( $aOptParams, $aOptStrings );
145
  }
146
+ catch ( \Exception $e ) {
147
  }
148
  return $aOptParams;
149
  }
169
  public function getBaseDisplayData() :array {
170
  $mod = $this->getMod();
171
  $con = $this->getCon();
172
+ $urlBuilder = $con->urls;
173
 
174
  /** @var Shield\Modules\Plugin\Options $oPluginOptions */
175
  $oPluginOptions = $con->getModule_Plugin()->getOptions();
235
  'wizard_landing' => $mod->getUrl_WizardLanding(),
236
 
237
  'form_action' => Services::Request()->getUri(),
238
+ 'css_bootstrap' => $urlBuilder->forCss( 'bootstrap4.min' ),
239
+ 'css_pages' => $urlBuilder->forCss( 'pages' ),
240
+ 'css_steps' => $urlBuilder->forCss( 'jquery.steps' ),
241
+ 'css_fancybox' => $urlBuilder->forCss( 'jquery.fancybox.min' ),
242
+ 'css_globalplugin' => $urlBuilder->forCss( 'global-plugin' ),
243
+ 'css_wizard' => $urlBuilder->forCss( 'wizard' ),
244
  'js_jquery' => Services::Includes()->getUrl_Jquery(),
245
+ 'js_bootstrap' => $urlBuilder->forJs( 'bootstrap4.bundle.min' ),
246
+ 'js_fancybox' => $urlBuilder->forJs( 'jquery.fancybox.min' ),
247
+ 'js_globalplugin' => $urlBuilder->forJs( 'global-plugin' ),
248
+ 'js_steps' => $urlBuilder->forJs( 'jquery.steps.min' ),
249
+ 'js_wizard' => $urlBuilder->forJs( 'wizard' ),
250
  ],
251
  'imgs' => [
252
+ 'favicon' => $urlBuilder->forImage( 'pluginlogo_24x24.png' ),
253
+ 'plugin_banner' => $urlBuilder->forImage( 'banner-1500x500-transparent.png' ),
254
+ 'background_svg' => $urlBuilder->forImage( 'shield/background-blob.svg' )
255
  ],
256
  'content' => [
257
  'options_form' => '',
366
  try {
367
  $NS = ( new \ReflectionClass( $this ) )->getNamespaceName();
368
  }
369
+ catch ( \Exception $e ) {
370
  $NS = __NAMESPACE__;
371
  }
372
 
src/lib/src/Modules/BaseShield/ModCon.php CHANGED
@@ -23,7 +23,7 @@ class ModCon extends Base\ModCon {
23
 
24
  /**
25
  * @return bool
26
- * @deprecated 10.1
27
  */
28
  public function canCacheDirWrite() :bool {
29
  return ( new Shield\Modules\Plugin\Lib\TestCacheDirWrite() )
@@ -31,16 +31,6 @@ class ModCon extends Base\ModCon {
31
  ->canWrite();
32
  }
33
 
34
- /**
35
- * @return Shield\Modules\Sessions\Processor
36
- * @deprecated 10.1
37
- */
38
- public function getSessionsProcessor() :Shield\Modules\Sessions\Processor {
39
- return $this->getCon()
40
- ->getModule_Sessions()
41
- ->getProcessor();
42
- }
43
-
44
  /**
45
  * @return Shield\Databases\Session\Handler
46
  */
@@ -60,14 +50,6 @@ class ModCon extends Base\ModCon {
60
  ->getCurrent();
61
  }
62
 
63
- /**
64
- * @return bool
65
- * @deprecated 10.1
66
- */
67
- public function hasSession() :bool {
68
- return $this->getSession() instanceof Shield\Databases\Session\EntryVO;
69
- }
70
-
71
  /**
72
  * @return bool
73
  */
23
 
24
  /**
25
  * @return bool
26
+ * @deprecated 10.2
27
  */
28
  public function canCacheDirWrite() :bool {
29
  return ( new Shield\Modules\Plugin\Lib\TestCacheDirWrite() )
31
  ->canWrite();
32
  }
33
 
 
 
 
 
 
 
 
 
 
 
34
  /**
35
  * @return Shield\Databases\Session\Handler
36
  */
50
  ->getCurrent();
51
  }
52
 
 
 
 
 
 
 
 
 
53
  /**
54
  * @return bool
55
  */
src/lib/src/Modules/BaseShield/ShieldProcessor.php CHANGED
@@ -3,31 +3,12 @@
3
  namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\BaseShield;
4
 
5
  use FernleafSystems\Wordpress\Plugin\Shield\Modules\Base;
6
- use FernleafSystems\Wordpress\Services\Services;
7
 
8
  /**
9
  * Class ShieldProcessor
10
  * @package FernleafSystems\Wordpress\Plugin\Shield\Modules\BaseShield
11
- * @deprecated 10.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
20
- */
21
- protected function isUserSubjectToLoginIntent( $oUser = null ) {
22
- $bIsSubject = false;
23
-
24
- if ( !$oUser instanceof \WP_User ) {
25
- $oUser = Services::WpUsers()->getCurrentWpUser();
26
- }
27
- if ( $oUser instanceof \WP_User ) {
28
- $bIsSubject = apply_filters( $this->getCon()->prefix( 'user_subject_to_login_intent' ), false, $oUser );
29
- }
30
-
31
- return $bIsSubject;
32
- }
33
  }
3
  namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\BaseShield;
4
 
5
  use FernleafSystems\Wordpress\Plugin\Shield\Modules\Base;
 
6
 
7
  /**
8
  * Class ShieldProcessor
9
  * @package FernleafSystems\Wordpress\Plugin\Shield\Modules\BaseShield
10
+ * @deprecated 10.2
11
  */
12
  class ShieldProcessor extends Base\BaseProcessor {
13
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
14
  }
src/lib/src/Modules/CommentsFilter/Scan/Scanner.php CHANGED
@@ -149,8 +149,8 @@ class Scanner {
149
  ->test();
150
  }
151
  }
152
- catch ( \Exception $oE ) {
153
- $mResult = new \WP_Error( 'recaptcha', $oE->getMessage(), [] );
154
  }
155
  }
156
 
149
  ->test();
150
  }
151
  }
152
+ catch ( \Exception $e ) {
153
+ $mResult = new \WP_Error( 'recaptcha', $e->getMessage(), [] );
154
  }
155
  }
156
 
src/lib/src/Modules/Events/Lib/EventsService.php CHANGED
@@ -56,14 +56,6 @@ class EventsService {
56
  return $this->isSupportedEvent( $eventKey ) ? $this->getEvents()[ $eventKey ] : null;
57
  }
58
 
59
- /**
60
- * @return string[]
61
- * @deprecated 10.1
62
- */
63
- public function getEventKeys() {
64
- return array_keys( $this->getEvents() );
65
- }
66
-
67
  public function isSupportedEvent( string $eventKey ) :bool {
68
  return in_array( $eventKey, array_keys( $this->getEvents() ) );
69
  }
56
  return $this->isSupportedEvent( $eventKey ) ? $this->getEvents()[ $eventKey ] : null;
57
  }
58
 
 
 
 
 
 
 
 
 
59
  public function isSupportedEvent( string $eventKey ) :bool {
60
  return in_array( $eventKey, array_keys( $this->getEvents() ) );
61
  }
src/lib/src/Modules/Events/Lib/Reports/KeyStats.php CHANGED
@@ -53,7 +53,7 @@ class KeyStats extends BaseReporter {
53
  ];
54
  }
55
  }
56
- catch ( \Exception $oE ) {
57
  }
58
  }
59
 
53
  ];
54
  }
55
  }
56
+ catch ( \Exception $e ) {
57
  }
58
  }
59
 
src/lib/src/Modules/Events/Lib/Reports/ScanRepairs.php CHANGED
@@ -42,7 +42,7 @@ class ScanRepairs extends BaseReporter {
42
  ];
43
  }
44
  }
45
- catch ( \Exception $oE ) {
46
  }
47
  }
48
 
42
  ];
43
  }
44
  }
45
+ catch ( \Exception $e ) {
46
  }
47
  }
48
 
src/lib/src/Modules/GeoIp/Lookup.php CHANGED
@@ -24,27 +24,27 @@ class Lookup {
24
  return $this->aIP[ $ip ];
25
  }
26
 
27
- /** @var Databases\GeoIp\Handler $oDbH */
28
- $oDbH = $this->getDbHandler();
29
- /** @var Databases\GeoIp\Select $oSel */
30
- $oSel = $oDbH->getQuerySelector();
31
- $oIP = $oSel->byIp( $ip );
32
 
33
  /**
34
  * We look up the IP and if the request fails, we store it anyway so that we don't repeatedly
35
  * bombard the API. The address will eventually be expired over time and lookup will process
36
  * again at a later date, as required
37
  */
38
- if ( empty( $oIP ) ) {
39
- $oIP = new Databases\GeoIp\EntryVO();
40
- $oIP->ip = $ip;
41
- $oIP->meta = $this->redirectliIpLookup();
42
  /** @var Databases\GeoIp\Insert $oIsrt */
43
- $oDbH->getQueryInserter()->insert( $oIP );
44
  }
45
 
46
- $this->aIP[ $ip ] = $oIP;
47
- return $oIP;
48
  }
49
 
50
  private function redirectliIpLookup() :array {
24
  return $this->aIP[ $ip ];
25
  }
26
 
27
+ /** @var Databases\GeoIp\Handler $dbh */
28
+ $dbh = $this->getDbHandler();
29
+ /** @var Databases\GeoIp\Select $select */
30
+ $select = $dbh->getQuerySelector();
31
+ $IP = $select->byIp( $ip );
32
 
33
  /**
34
  * We look up the IP and if the request fails, we store it anyway so that we don't repeatedly
35
  * bombard the API. The address will eventually be expired over time and lookup will process
36
  * again at a later date, as required
37
  */
38
+ if ( empty( $IP ) ) {
39
+ $IP = new Databases\GeoIp\EntryVO();
40
+ $IP->ip = $ip;
41
+ $IP->meta = $this->redirectliIpLookup();
42
  /** @var Databases\GeoIp\Insert $oIsrt */
43
+ $dbh->getQueryInserter()->insert( $IP );
44
  }
45
 
46
+ $this->aIP[ $ip ] = $IP;
47
+ return $IP;
48
  }
49
 
50
  private function redirectliIpLookup() :array {
src/lib/src/Modules/HackGuard/AjaxHandler.php CHANGED
@@ -12,23 +12,23 @@ class AjaxHandler extends Shield\Modules\BaseShield\AjaxHandler {
12
 
13
  protected function processAjaxAction( string $action ) :array {
14
 
15
- $oReq = Services::Request();
16
  switch ( $action ) {
17
 
18
  case 'scans_start':
19
- $aResponse = $this->ajaxExec_StartScans();
20
  break;
21
 
22
  case 'scans_check':
23
- $aResponse = $this->ajaxExec_CheckScans();
24
  break;
25
 
26
  case 'item_action':
27
- $aResponse = $this->ajaxExec_ScanItemAction( $oReq->post( 'item_action' ), false );
28
  break;
29
 
30
  case 'bulk_action':
31
- $aResponse = $this->ajaxExec_ScanItemAction( $oReq->post( 'bulk_action' ), true );
32
  break;
33
 
34
  case 'item_asset_deactivate':
@@ -36,30 +36,30 @@ class AjaxHandler extends Shield\Modules\BaseShield\AjaxHandler {
36
  case 'item_delete':
37
  case 'item_ignore':
38
  case 'item_repair':
39
- $aResponse = $this->ajaxExec_ScanItemAction( str_replace( 'item_', '', $action ) );
40
  break;
41
 
42
  case 'render_table_scan':
43
- $aResponse = $this->ajaxExec_BuildTableScan();
44
  break;
45
 
46
  case 'plugin_reinstall':
47
- $aResponse = $this->ajaxExec_PluginReinstall();
48
  break;
49
 
50
  case 'filelocker_showdiff':
51
- $aResponse = $this->ajaxExec_FileLockerShowDiff();
52
  break;
53
 
54
  case 'filelocker_fileaction':
55
- $aResponse = $this->ajaxExec_FileLockerFileAction();
56
  break;
57
 
58
  default:
59
- $aResponse = parent::processAjaxAction( $action );
60
  }
61
 
62
- return $aResponse;
63
  }
64
 
65
  private function ajaxExec_BuildTableScan() :array {
@@ -190,8 +190,8 @@ class AjaxHandler extends Shield\Modules\BaseShield\AjaxHandler {
190
  $aData[ 'vars' ][ 'file_name' ] = basename( $oLock->file );
191
  $aData[ 'success' ] = true;
192
  }
193
- catch ( \Exception $oE ) {
194
- $aData[ 'error' ] = $oE->getMessage();
195
  }
196
 
197
  return [
@@ -207,29 +207,27 @@ class AjaxHandler extends Shield\Modules\BaseShield\AjaxHandler {
207
  }
208
 
209
  private function ajaxExec_FileLockerFileAction() :array {
210
- $oReq = Services::Request();
211
- $bSuccess = false;
212
 
213
- if ( $oReq->post( 'confirmed' ) == '1' ) {
214
- $nRID = $oReq->post( 'rid' );
215
- $sAction = $oReq->post( 'file_action' );
216
  try {
217
- $bSuccess = ( new FileLocker\Ops\PerformAction() )
218
  ->setMod( $this->getMod() )
219
- ->run( $nRID, $sAction );
220
- $sMessage = __( 'Requested action completed successfully.', 'wp-simple-firewall' );
221
  }
222
- catch ( \Exception $oE ) {
223
- $sMessage = __( 'Requested action failed.', 'wp-simple-firewall' );
224
  }
225
  }
226
  else {
227
- $sMessage = __( 'Please check the box to confirm this action', 'wp-simple-firewall' );
228
  }
229
 
230
  return [
231
- 'success' => $bSuccess,
232
- 'message' => $sMessage,
233
  ];
234
  }
235
 
@@ -269,58 +267,60 @@ class AjaxHandler extends Shield\Modules\BaseShield\AjaxHandler {
269
  if ( $action == 'download' ) {
270
  // A special case since this action is handled using Javascript
271
  $success = true;
272
- $sMessage = __( 'File download has started.', 'wp-simple-firewall' );
273
  }
274
  else {
275
  if ( $bIsBulkAction ) {
276
- $aItemIdsToProcess = (array)Services::Request()->post( 'ids', [] );
277
  }
278
  else {
279
- $aItemIdsToProcess = [ Services::Request()->post( 'rid' ) ];
280
  }
281
- /** @var int[] $aItemIdsToProcess */
282
- $aItemIdsToProcess = array_filter( array_map( 'intval', $aItemIdsToProcess ) );
283
 
284
- if ( empty( $aItemIdsToProcess ) ) {
285
- $sMessage = __( 'Unsupported item(s) selected', 'wp-simple-firewall' );
286
  }
287
  else {
288
  try {
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 {
309
- $sMessage = __( 'An error occurred.' ).' '.__( 'Some items may not have been processed.' );
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
  }
322
- catch ( \Exception $oE ) {
323
- $sMessage = $oE->getMessage();
324
  }
325
  }
326
  }
@@ -328,7 +328,7 @@ class AjaxHandler extends Shield\Modules\BaseShield\AjaxHandler {
328
  return [
329
  'success' => $success,
330
  'page_reload' => !in_array( $action, [ 'download' ] ),
331
- 'message' => $sMessage,
332
  ];
333
  }
334
 
12
 
13
  protected function processAjaxAction( string $action ) :array {
14
 
15
+ $req = Services::Request();
16
  switch ( $action ) {
17
 
18
  case 'scans_start':
19
+ $response = $this->ajaxExec_StartScans();
20
  break;
21
 
22
  case 'scans_check':
23
+ $response = $this->ajaxExec_CheckScans();
24
  break;
25
 
26
  case 'item_action':
27
+ $response = $this->ajaxExec_ScanItemAction( $req->post( 'item_action' ), false );
28
  break;
29
 
30
  case 'bulk_action':
31
+ $response = $this->ajaxExec_ScanItemAction( $req->post( 'bulk_action' ), true );
32
  break;
33
 
34
  case 'item_asset_deactivate':
36
  case 'item_delete':
37
  case 'item_ignore':
38
  case 'item_repair':
39
+ $response = $this->ajaxExec_ScanItemAction( str_replace( 'item_', '', $action ) );
40
  break;
41
 
42
  case 'render_table_scan':
43
+ $response = $this->ajaxExec_BuildTableScan();
44
  break;
45
 
46
  case 'plugin_reinstall':
47
+ $response = $this->ajaxExec_PluginReinstall();
48
  break;
49
 
50
  case 'filelocker_showdiff':
51
+ $response = $this->ajaxExec_FileLockerShowDiff();
52
  break;
53
 
54
  case 'filelocker_fileaction':
55
+ $response = $this->ajaxExec_FileLockerFileAction();
56
  break;
57
 
58
  default:
59
+ $response = parent::processAjaxAction( $action );
60
  }
61
 
62
+ return $response;
63
  }
64
 
65
  private function ajaxExec_BuildTableScan() :array {
190
  $aData[ 'vars' ][ 'file_name' ] = basename( $oLock->file );
191
  $aData[ 'success' ] = true;
192
  }
193
+ catch ( \Exception $e ) {
194
+ $aData[ 'error' ] = $e->getMessage();
195
  }
196
 
197
  return [
207
  }
208
 
209
  private function ajaxExec_FileLockerFileAction() :array {
210
+ $req = Services::Request();
211
+ $success = false;
212
 
213
+ if ( $req->post( 'confirmed' ) == '1' ) {
 
 
214
  try {
215
+ $success = ( new FileLocker\Ops\PerformAction() )
216
  ->setMod( $this->getMod() )
217
+ ->run( $req->post( 'rid' ), $req->post( 'file_action' ) );
218
+ $msg = __( 'Requested action completed successfully.', 'wp-simple-firewall' );
219
  }
220
+ catch ( \Exception $e ) {
221
+ $msg = __( 'Requested action failed.', 'wp-simple-firewall' );
222
  }
223
  }
224
  else {
225
+ $msg = __( 'Please check the box to confirm this action', 'wp-simple-firewall' );
226
  }
227
 
228
  return [
229
+ 'success' => $success,
230
+ 'message' => $msg,
231
  ];
232
  }
233
 
267
  if ( $action == 'download' ) {
268
  // A special case since this action is handled using Javascript
269
  $success = true;
270
+ $msg = __( 'File download has started.', 'wp-simple-firewall' );
271
  }
272
  else {
273
  if ( $bIsBulkAction ) {
274
+ $itemIDs = (array)Services::Request()->post( 'ids', [] );
275
  }
276
  else {
277
+ $itemIDs = [ Services::Request()->post( 'rid' ) ];
278
  }
279
+ /** @var int[] $itemIDs */
280
+ $itemIDs = array_filter( array_map( 'intval', $itemIDs ) );
281
 
282
+ if ( empty( $itemIDs ) ) {
283
+ $msg = __( 'Unsupported item(s) selected', 'wp-simple-firewall' );
284
  }
285
  else {
286
  try {
287
+ $scanSlugs = [];
288
  $aSuccessfulItems = [];
289
+ foreach ( $itemIDs as $ID ) {
290
  /** @var Shield\Databases\Scanner\EntryVO $entry */
291
  $entry = $mod->getDbHandler_ScanResults()
292
  ->getQuerySelector()
293
+ ->byId( $ID );
294
  if ( $entry instanceof Shield\Databases\Scanner\EntryVO ) {
295
+ $scanSlugs[] = $entry->scan;
296
+ if ( $mod->getScanCon( $entry->scan )->executeItemAction( $ID, $action ) ) {
297
+ $aSuccessfulItems[] = $ID;
298
  }
299
  }
300
  }
301
 
302
+ if ( count( $aSuccessfulItems ) === count( $itemIDs ) ) {
303
  $success = true;
304
+ $msg = __( 'Action successful.' );
305
  }
306
  else {
307
+ $msg = __( 'An error occurred.' ).' '.__( 'Some items may not have been processed.' );
308
  }
309
 
310
  // We don't rescan for ignores.
311
+ $rescanSlugs = array_diff( $scanSlugs, [ Scan\Controller\Mal::SCAN_SLUG ] );
312
+
313
+ if ( empty( $rescanSlugs ) || in_array( $action, [ 'ignore' ] ) ) {
314
+ $msg .= ' '.__( 'Reloading', 'wp-simple-firewall' ).' ...';
315
  }
316
  else {
317
  // rescan
318
+ $mod->getScanQueueController()->startScans( $rescanSlugs );
319
+ $msg .= ' '.__( 'Rescanning', 'wp-simple-firewall' ).' ...';
320
  }
321
  }
322
+ catch ( \Exception $e ) {
323
+ $msg = $e->getMessage();
324
  }
325
  }
326
  }
328
  return [
329
  'success' => $success,
330
  'page_reload' => !in_array( $action, [ 'download' ] ),
331
+ 'message' => $msg,
332
  ];
333
  }
334
 
src/lib/src/Modules/HackGuard/Debug.php ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php declare( strict_types=1 );
2
+
3
+ namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\HackGuard;
4
+
5
+ use FernleafSystems\Wordpress\Plugin\Shield\Modules;
6
+ use FernleafSystems\Wordpress\Plugin\Shield\Scans\Mal\Signatures;
7
+ use FernleafSystems\Wordpress\Services\Utilities\Net\IpIdentify;
8
+
9
+ class Debug extends Modules\Base\Debug {
10
+
11
+ public function run() {
12
+ $this->dumpSigs();
13
+ die();
14
+ }
15
+
16
+ private function dumpSigs() {
17
+ var_dump(Signatures::getAll());
18
+ }
19
+ }
src/lib/src/Modules/HackGuard/Lib/FileLocker/FileLockerController.php CHANGED
@@ -162,8 +162,8 @@ class FileLockerController {
162
  ->setWorkingFile( $this->getFile( $sFileKey ) )
163
  ->create();
164
  }
165
- catch ( \Exception $oE ) {
166
- error_log( $oE->getMessage() );
167
  }
168
  }
169
  }
162
  ->setWorkingFile( $this->getFile( $sFileKey ) )
163
  ->create();
164
  }
165
+ catch ( \Exception $e ) {
166
+ error_log( $e->getMessage() );
167
  }
168
  }
169
  }
src/lib/src/Modules/HackGuard/Lib/FileLocker/Ops/AssessLocks.php CHANGED
@@ -35,7 +35,7 @@ class AssessLocks extends BaseOps {
35
  }
36
  }
37
  }
38
- catch ( \InvalidArgumentException $oE ) {
39
  $oUpd->markProblem( $oLock );
40
  $aProblemIds[] = $oLock->id;
41
  }
35
  }
36
  }
37
  }
38
+ catch ( \InvalidArgumentException $e ) {
39
  $oUpd->markProblem( $oLock );
40
  $aProblemIds[] = $oLock->id;
41
  }
src/lib/src/Modules/HackGuard/Lib/FileLocker/Ops/ReadOriginalFileContent.php CHANGED
@@ -14,17 +14,17 @@ use FernleafSystems\Wordpress\Services\Utilities\Encrypt\OpenSslEncryptVo;
14
  class ReadOriginalFileContent extends BaseOps {
15
 
16
  /**
17
- * @param Databases\FileLocker\EntryVO $oLock
18
  * @return string
19
  */
20
- public function run( $oLock ) {
21
  try {
22
- $sContent = $this->useOriginalFile( $oLock );
23
  }
24
- catch ( \Exception $oE ) {
25
- $sContent = $this->useCacheAndApi( $oLock );
26
  }
27
- return $sContent;
28
  }
29
 
30
  /**
14
  class ReadOriginalFileContent extends BaseOps {
15
 
16
  /**
17
+ * @param Databases\FileLocker\EntryVO $lock
18
  * @return string
19
  */
20
+ public function run( $lock ) {
21
  try {
22
+ $content = $this->useOriginalFile( $lock );
23
  }
24
+ catch ( \Exception $e ) {
25
+ $content = $this->useCacheAndApi( $lock );
26
  }
27
+ return $content;
28
  }
29
 
30
  /**
src/lib/src/Modules/HackGuard/Lib/FileLocker/Ops/Verify.php CHANGED
@@ -12,14 +12,14 @@ use FernleafSystems\Wordpress\Services\Utilities\File\Compare\CompareHash;
12
  class Verify {
13
 
14
  /**
15
- * @param EntryVO $oRecord
16
  * @return bool
17
  */
18
- public function run( $oRecord ) {
19
  try {
20
- return ( new CompareHash() )->isEqualFileSha1( $oRecord->file, $oRecord->hash );
21
  }
22
- catch ( \InvalidArgumentException $oE ) {
23
  return false;
24
  }
25
  }
12
  class Verify {
13
 
14
  /**
15
+ * @param EntryVO $record
16
  * @return bool
17
  */
18
+ public function run( $record ) {
19
  try {
20
+ return ( new CompareHash() )->isEqualFileSha1( $record->file, $record->hash );
21
  }
22
+ catch ( \InvalidArgumentException $e ) {
23
  return false;
24
  }
25
  }
src/lib/src/Modules/HackGuard/Lib/Snapshots/Build/BuildHashesFromDir.php CHANGED
@@ -43,7 +43,7 @@ class BuildHashesFromDir {
43
  $aSnaps[ $sKey ] = hash_file( $sAlgo, $sFullPath );
44
  }
45
  }
46
- catch ( \Exception $oE ) {
47
  }
48
  return $aSnaps;
49
  }
43
  $aSnaps[ $sKey ] = hash_file( $sAlgo, $sFullPath );
44
  }
45
  }
46
+ catch ( \Exception $e ) {
47
  }
48
  return $aSnaps;
49
  }
src/lib/src/Modules/HackGuard/Lib/Snapshots/Store.php CHANGED
@@ -115,7 +115,7 @@ class Store {
115
  try {
116
  $this->aSnapData = $this->readSnapData();
117
  }
118
- catch ( \Exception $oE ) {
119
  $this->aSnapData = [];
120
  }
121
  return $this;
@@ -128,7 +128,7 @@ class Store {
128
  try {
129
  $this->aSnapMeta = $this->readSnapMeta();
130
  }
131
- catch ( \Exception $oE ) {
132
  $this->aSnapMeta = [];
133
  }
134
  return $this;
115
  try {
116
  $this->aSnapData = $this->readSnapData();
117
  }
118
+ catch ( \Exception $e ) {
119
  $this->aSnapData = [];
120
  }
121
  return $this;
128
  try {
129
  $this->aSnapMeta = $this->readSnapMeta();
130
  }
131
+ catch ( \Exception $e ) {
132
  $this->aSnapMeta = [];
133
  }
134
  return $this;
src/lib/src/Modules/HackGuard/Lib/Snapshots/StoreAction/Build.php CHANGED
@@ -3,7 +3,6 @@
3
  namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\HackGuard\Lib\Snapshots\StoreAction;
4
 
5
  use FernleafSystems\Wordpress\Plugin\Shield\Modules\HackGuard\Lib\Snapshots;
6
- use FernleafSystems\Wordpress\Plugin\Shield\Modules\HackGuard\ModCon;
7
  use FernleafSystems\Wordpress\Services\Core\VOs\WpPluginVo;
8
  use FernleafSystems\Wordpress\Services\Services;
9
 
@@ -13,30 +12,30 @@ class Build extends BaseAction {
13
  * @throws \Exception
14
  */
15
  public function run() {
16
- $oAsset = $this->getAsset();
17
  try {
18
- $aHashes = ( new Snapshots\Build\BuildHashesFromApi() )->build( $oAsset );
19
  }
20
- catch ( \Exception $oE ) {
21
  }
22
 
23
  $aMeta = $this->generateMeta();
24
- if ( empty( $aHashes ) ) {
25
- $aHashes = ( new Snapshots\Build\BuildHashesForAsset() )
26
  ->setHashAlgo( 'md5' )
27
- ->build( $oAsset );
28
  $aMeta[ 'live_hashes' ] = false;
29
  }
30
  else {
31
  $aMeta[ 'live_hashes' ] = true;
32
  }
33
 
34
- if ( !empty( $aHashes ) ) {
35
  $oStore = ( new CreateNew() )
36
  ->setMod( $this->getMod() )
37
- ->setAsset( $oAsset )
38
  ->run();
39
- $oStore->setSnapData( $aHashes )
40
  ->setSnapMeta( $aMeta )
41
  ->save();
42
  }
@@ -46,18 +45,18 @@ class Build extends BaseAction {
46
  * @return array
47
  */
48
  private function generateMeta() {
49
- $oAsset = $this->getAsset();
50
- $aMeta = [
51
  'ts' => Services::Request()->ts(),
52
  'snap_version' => $this->getCon()->getVersion(),
53
  ];
54
- $aMeta[ 'unique_id' ] = ( $oAsset instanceof WpPluginVo ) ?
55
- $oAsset->file
56
- : $oAsset->stylesheet;
57
- $aMeta[ 'name' ] = ( $oAsset instanceof WpPluginVo ) ?
58
- $oAsset->Name
59
- : $oAsset->wp_theme->get( 'Name' );
60
- $aMeta[ 'version' ] = $oAsset->version;
61
- return $aMeta;
62
  }
63
  }
3
  namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\HackGuard\Lib\Snapshots\StoreAction;
4
 
5
  use FernleafSystems\Wordpress\Plugin\Shield\Modules\HackGuard\Lib\Snapshots;
 
6
  use FernleafSystems\Wordpress\Services\Core\VOs\WpPluginVo;
7
  use FernleafSystems\Wordpress\Services\Services;
8
 
12
  * @throws \Exception
13
  */
14
  public function run() {
15
+ $asset = $this->getAsset();
16
  try {
17
+ $hashes = ( new Snapshots\Build\BuildHashesFromApi() )->build( $asset );
18
  }
19
+ catch ( \Exception $e ) {
20
  }
21
 
22
  $aMeta = $this->generateMeta();
23
+ if ( empty( $hashes ) ) {
24
+ $hashes = ( new Snapshots\Build\BuildHashesForAsset() )
25
  ->setHashAlgo( 'md5' )
26
+ ->build( $asset );
27
  $aMeta[ 'live_hashes' ] = false;
28
  }
29
  else {
30
  $aMeta[ 'live_hashes' ] = true;
31
  }
32
 
33
+ if ( !empty( $hashes ) ) {
34
  $oStore = ( new CreateNew() )
35
  ->setMod( $this->getMod() )
36
+ ->setAsset( $asset )
37
  ->run();
38
+ $oStore->setSnapData( $hashes )
39
  ->setSnapMeta( $aMeta )
40
  ->save();
41
  }
45
  * @return array
46
  */
47
  private function generateMeta() {
48
+ $asset = $this->getAsset();
49
+ $meta = [
50
  'ts' => Services::Request()->ts(),
51
  'snap_version' => $this->getCon()->getVersion(),
52
  ];
53
+ $meta[ 'unique_id' ] = ( $asset instanceof WpPluginVo ) ?
54
+ $asset->file
55
+ : $asset->stylesheet;
56
+ $meta[ 'name' ] = ( $asset instanceof WpPluginVo ) ?
57
+ $asset->Name
58
+ : $asset->wp_theme->get( 'Name' );
59
+ $meta[ 'version' ] = $asset->version;
60
+ return $meta;
61
  }
62
  }
src/lib/src/Modules/HackGuard/Lib/Snapshots/StoreAction/CleanAll.php CHANGED
@@ -23,7 +23,7 @@ class CleanAll extends BaseBulk {
23
  }
24
  }
25
  }
26
- catch ( \Exception $oE ) {
27
  }
28
  }
29
  }
23
  }
24
  }
25
  }
26
+ catch ( \Exception $e ) {
27
  }
28
  }
29
  }
src/lib/src/Modules/HackGuard/Lib/Snapshots/StoreAction/Delete.php CHANGED
@@ -8,15 +8,15 @@ class Delete extends BaseAction {
8
 
9
  public function run() {
10
  try {
11
- $oStore = ( new Load() )
12
  ->setMod( $this->getMod() )
13
  ->setAsset( $this->getAsset() )
14
  ->run();
15
- foreach ( [ $oStore->getSnapStorePath(), $oStore->getSnapStoreMetaPath() ] as $sPath ) {
16
- Services::WpFs()->deleteFile( $sPath );
17
  }
18
  }
19
- catch ( \Exception $oE ) {
20
  }
21
  }
22
  }
8
 
9
  public function run() {
10
  try {
11
+ $store = ( new Load() )
12
  ->setMod( $this->getMod() )
13
  ->setAsset( $this->getAsset() )
14
  ->run();
15
+ foreach ( [ $store->getSnapStorePath(), $store->getSnapStoreMetaPath() ] as $path ) {
16
+ Services::WpFs()->deleteFile( $path );
17
  }
18
  }
19
+ catch ( \Exception $e ) {
20
  }
21
  }
22
  }
src/lib/src/Modules/HackGuard/Lib/Snapshots/StoreAction/ScheduleBuildAll.php CHANGED
@@ -9,14 +9,14 @@ use FernleafSystems\Wordpress\Services\Services;
9
  class ScheduleBuildAll extends BaseBulk {
10
 
11
  public function build() {
12
- foreach ( $this->getAssetsThatNeedBuilt() as $oAsset ) {
13
  try {
14
  ( new Build() )
15
  ->setMod( $this->getMod() )
16
- ->setAsset( $oAsset )
17
  ->run();
18
  }
19
- catch ( \Exception $oE ) {
20
  }
21
  }
22
  }
@@ -43,27 +43,24 @@ class ScheduleBuildAll extends BaseBulk {
43
  ( new FindAssetsToSnap() )
44
  ->setMod( $this->getMod() )
45
  ->run(),
46
- function ( $oAsset ) {
47
- /** @var VOs\WpPluginVo|VOs\WpThemeVo $oAsset */
48
  try {
49
- $aMeta = ( new Load() )
50
  ->setMod( $this->getMod() )
51
- ->setAsset( $oAsset )
52
  ->run()
53
  ->getSnapMeta();
54
  }
55
- catch ( \Exception $oE ) {
56
- $aMeta = null;
57
  }
58
- return ( empty( $aMeta ) || $oAsset->version !== $aMeta[ 'version' ] );
59
  }
60
  );
61
  }
62
 
63
- /**
64
- * @return string
65
- */
66
- private function getCronHook() {
67
  return $this->getCon()->prefix( 'ptg_build_snapshots' );
68
  }
69
  }
9
  class ScheduleBuildAll extends BaseBulk {
10
 
11
  public function build() {
12
+ foreach ( $this->getAssetsThatNeedBuilt() as $asset ) {
13
  try {
14
  ( new Build() )
15
  ->setMod( $this->getMod() )
16
+ ->setAsset( $asset )
17
  ->run();
18
  }
19
+ catch ( \Exception $e ) {
20
  }
21
  }
22
  }
43
  ( new FindAssetsToSnap() )
44
  ->setMod( $this->getMod() )
45
  ->run(),
46
+ function ( $asset ) {
47
+ /** @var VOs\WpPluginVo|VOs\WpThemeVo $asset */
48
  try {
49
+ $meta = ( new Load() )
50
  ->setMod( $this->getMod() )
51
+ ->setAsset( $asset )
52
  ->run()
53
  ->getSnapMeta();
54
  }
55
+ catch ( \Exception $e ) {
56
+ $meta = null;
57
  }
58
+ return ( empty( $meta ) || $asset->version !== $meta[ 'version' ] );
59
  }
60
  );
61
  }
62
 
63
+ private function getCronHook() :string {
 
 
 
64
  return $this->getCon()->prefix( 'ptg_build_snapshots' );
65
  }
66
  }
src/lib/src/Modules/HackGuard/Lib/Snapshots/StoreAction/TouchAll.php CHANGED
@@ -8,17 +8,17 @@ use FernleafSystems\Wordpress\Services\Services;
8
  class TouchAll extends BaseBulk {
9
 
10
  public function run() {
11
- foreach ( ( new FindAssetsToSnap() )->setMod( $this->getMod() )->run() as $oAsset ) {
12
  try {
13
  $oStore = ( new Load() )
14
  ->setMod( $this->getMod() )
15
- ->setAsset( $oAsset )
16
  ->run();
17
- foreach ( [ $oStore->getSnapStorePath(), $oStore->getSnapStoreMetaPath() ] as $sPath ) {
18
- Services::WpFs()->touch( $sPath );
19
  }
20
  }
21
- catch ( \Exception $oE ) {
22
  }
23
  }
24
  }
8
  class TouchAll extends BaseBulk {
9
 
10
  public function run() {
11
+ foreach ( ( new FindAssetsToSnap() )->setMod( $this->getMod() )->run() as $asset ) {
12
  try {
13
  $oStore = ( new Load() )
14
  ->setMod( $this->getMod() )
15
+ ->setAsset( $asset )
16
  ->run();
17
+ foreach ( [ $oStore->getSnapStorePath(), $oStore->getSnapStoreMetaPath() ] as $path ) {
18
+ Services::WpFs()->touch( $path );
19
  }
20
  }
21
+ catch ( \Exception $e ) {
22
  }
23
  }
24
  }
src/lib/src/Modules/HackGuard/ModCon.php CHANGED
@@ -19,11 +19,6 @@ class ModCon extends BaseShield\ModCon {
19
  */
20
  private $scanQueueCon;
21
 
22
- /**
23
- * @var Scan\Controller\Base[]
24
- */
25
- private $scansCons;
26
-
27
  /**
28
  * @var Lib\FileLocker\FileLockerController
29
  */
@@ -62,22 +57,13 @@ class ModCon extends BaseShield\ModCon {
62
  return $this->scanQueueCon;
63
  }
64
 
65
- /**
66
- * @return Scan\Controller\Base[]
67
- * @deprecated 10.1
68
- */
69
- public function getAllScanCons() :array {
70
- return $this->scansCons ?? $this->getScansCon()->getAllScanCons();
71
- }
72
-
73
  /**
74
  * @param string $slug
75
  * @return Scan\Controller\Base|mixed
76
  * @throws \Exception
77
  */
78
  public function getScanCon( string $slug ) {
79
- return empty( $this->scansCons[ $slug ] ) ?
80
- $this->getScansCon()->getScanCon( $slug ) : $this->scansCons[ $slug ];
81
  }
82
 
83
  public function getMainWpData() :array {
@@ -242,12 +228,4 @@ class ModCon extends BaseShield\ModCon {
242
  // 2. Clean out the file locker
243
  $this->getFileLocker()->purge();
244
  }
245
-
246
- /**
247
- * @return bool
248
- * @deprecated 10.1
249
- */
250
- public function isWpvulnPluginsHighlightEnabled() :bool {
251
- return false;
252
- }
253
  }
19
  */
20
  private $scanQueueCon;
21
 
 
 
 
 
 
22
  /**
23
  * @var Lib\FileLocker\FileLockerController
24
  */
57
  return $this->scanQueueCon;
58
  }
59
 
 
 
 
 
 
 
 
 
60
  /**
61
  * @param string $slug
62
  * @return Scan\Controller\Base|mixed
63
  * @throws \Exception
64
  */
65
  public function getScanCon( string $slug ) {
66
+ return $this->getScansCon()->getScanCon( $slug );
 
67
  }
68
 
69
  public function getMainWpData() :array {
228
  // 2. Clean out the file locker
229
  $this->getFileLocker()->purge();
230
  }
 
 
 
 
 
 
 
 
231
  }
src/lib/src/Modules/HackGuard/Options.php CHANGED
@@ -29,7 +29,7 @@ class Options extends BaseShield\Options {
29
  }
30
 
31
  public function getMalConfidenceBoundary() :int {
32
- return (int)apply_filters( 'icwp_shield_fp_confidence_boundary', 50 );
33
  }
34
 
35
  /**
29
  }
30
 
31
  public function getMalConfidenceBoundary() :int {
32
+ return (int)apply_filters( 'shield/fp_confidence_boundary', 65 );
33
  }
34
 
35
  /**
src/lib/src/Modules/HackGuard/Scan/Controller/Base.php CHANGED
@@ -2,7 +2,7 @@
2
 
3
  namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\HackGuard\Scan\Controller;
4
 
5
- use FernleafSystems\Utilities\Logic\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;
@@ -17,7 +17,7 @@ use FernleafSystems\Wordpress\Services\Services;
17
  abstract class Base {
18
 
19
  use ModConsumer;
20
- use OneTimeExecute;
21
 
22
  const SCAN_SLUG = '';
23
 
@@ -90,7 +90,7 @@ abstract class Base {
90
  * @param BaseResultItem|mixed $item
91
  * @return bool
92
  */
93
- abstract protected function isResultItemStale( $item );
94
 
95
  /**
96
  * @param int|string $itemID
@@ -98,28 +98,28 @@ abstract class Base {
98
  * @return bool
99
  * @throws \Exception
100
  */
101
- public function executeItemAction( $itemID, $action ) {
102
- $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
  }
112
 
113
- $oItem = ( new HackGuard\Scan\Results\ConvertBetweenTypes() )
114
  ->setScanController( $this )
115
- ->convertVoToResultItem( $oEntry );
116
 
117
- $bSuccess = $this->getItemActionHandler()
118
- ->setScanItem( $oItem )
119
- ->process( $action );
120
  }
121
 
122
- return $bSuccess;
123
  }
124
 
125
  /**
@@ -244,7 +244,7 @@ abstract class Base {
244
  ->setScanItem( $oItem )
245
  ->repair();
246
  }
247
- catch ( \Exception $oE ) {
248
  }
249
  }
250
  $this->cleanStalesResults();
2
 
3
  namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\HackGuard\Scan\Controller;
4
 
5
+ use FernleafSystems\Utilities\Logic\ExecOnce;
6
  use FernleafSystems\Wordpress\Plugin\Shield\Databases;
7
  use FernleafSystems\Wordpress\Plugin\Shield\Modules\HackGuard;
8
  use FernleafSystems\Wordpress\Plugin\Shield\Modules\HackGuard\ModCon;
17
  abstract class Base {
18
 
19
  use ModConsumer;
20
+ use ExecOnce;
21
 
22
  const SCAN_SLUG = '';
23
 
90
  * @param BaseResultItem|mixed $item
91
  * @return bool
92
  */
93
+ abstract protected function isResultItemStale( $item ) :bool;
94
 
95
  /**
96
  * @param int|string $itemID
98
  * @return bool
99
  * @throws \Exception
100
  */
101
+ public function executeItemAction( $itemID, string $action ) {
102
+ $success = false;
103
 
104
  if ( is_numeric( $itemID ) ) {
105
+ /** @var Databases\Scanner\EntryVO $entry */
106
+ $entry = $this->getScanResultsDbHandler()
107
+ ->getQuerySelector()
108
+ ->byId( $itemID );
109
+ if ( empty( $entry ) ) {
110
  throw new \Exception( 'Item could not be found.' );
111
  }
112
 
113
+ $entry = ( new HackGuard\Scan\Results\ConvertBetweenTypes() )
114
  ->setScanController( $this )
115
+ ->convertVoToResultItem( $entry );
116
 
117
+ $success = $this->getItemActionHandler()
118
+ ->setScanItem( $entry )
119
+ ->process( $action );
120
  }
121
 
122
+ return $success;
123
  }
124
 
125
  /**
244
  ->setScanItem( $oItem )
245
  ->repair();
246
  }
247
+ catch ( \Exception $e ) {
248
  }
249
  }
250
  $this->cleanStalesResults();
src/lib/src/Modules/HackGuard/Scan/Controller/BaseForAssets.php CHANGED
@@ -11,15 +11,15 @@ abstract class BaseForAssets extends Base {
11
  * @param Scans\Ptg\ResultItem|Scans\Wpv\ResultItem|Scans\Apc\ResultItem $item
12
  * @return bool
13
  */
14
- protected function isResultItemStale( $item ) {
15
  if ( $item->context == 'plugins' ) {
16
- $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;
24
  }
25
  }
11
  * @param Scans\Ptg\ResultItem|Scans\Wpv\ResultItem|Scans\Apc\ResultItem $item
12
  * @return bool
13
  */
14
+ protected function isResultItemStale( $item ) :bool {
15
  if ( $item->context == 'plugins' ) {
16
+ $asset = Services::WpPlugins()->getPluginAsVo( $item->slug );
17
+ $stale = empty( $asset ) || !$asset->active;
18
  }
19
  else {
20
+ $asset = Services::WpThemes()->getThemeAsVo( $item->slug );
21
+ $stale = empty( $asset ) || ( !$asset->active && !$asset->is_parent );
22
  }
23
+ return $stale;
24
  }
25
  }
src/lib/src/Modules/HackGuard/Scan/Controller/Mal.php CHANGED
@@ -21,13 +21,13 @@ class Mal extends Base {
21
 
22
  $oRes = new Scans\Mal\ResultsSet();
23
 
24
- /** @var Scans\Mal\ResultItem $oItem */
25
- foreach ( parent::getItemsToAutoRepair()->getAllItems() as $oItem ) {
26
 
27
  try {
28
  if ( $opts->isRepairFilePlugin()
29
- && ( new WpOrg\Plugin\Files() )->isValidFileFromPlugin( $oItem->path_full ) ) {
30
- $oRes->addItem( $oItem );
31
  }
32
  }
33
  catch ( \InvalidArgumentException $e ) {
@@ -35,16 +35,16 @@ class Mal extends Base {
35
 
36
  try {
37
  if ( $opts->isRepairFileTheme()
38
- && ( new WpOrg\Theme\Files() )->isValidFileFromTheme( $oItem->path_full ) ) {
39
- $oRes->addItem( $oItem );
40
  }
41
  }
42
  catch ( \InvalidArgumentException $e ) {
43
  }
44
 
45
  if ( !$opts->isRepairFileWP()
46
- && Services::CoreFileHashes()->isCoreFile( $oItem->path_full ) ) {
47
- $oRes->addItem( $oItem );
48
  }
49
  }
50
 
@@ -55,7 +55,7 @@ class Mal extends Base {
55
  * @param Scans\Mal\ResultItem $item
56
  * @return bool
57
  */
58
- protected function isResultItemStale( $item ) {
59
  return !Services::WpFs()->exists( $item->path_full );
60
  }
61
 
21
 
22
  $oRes = new Scans\Mal\ResultsSet();
23
 
24
+ /** @var Scans\Mal\ResultItem $item */
25
+ foreach ( parent::getItemsToAutoRepair()->getAllItems() as $item ) {
26
 
27
  try {
28
  if ( $opts->isRepairFilePlugin()
29
+ && ( new WpOrg\Plugin\Files() )->isValidFileFromPlugin( $item->path_full ) ) {
30
+ $oRes->addItem( $item );
31
  }
32
  }
33
  catch ( \InvalidArgumentException $e ) {
35
 
36
  try {
37
  if ( $opts->isRepairFileTheme()
38
+ && ( new WpOrg\Theme\Files() )->isValidFileFromTheme( $item->path_full ) ) {
39
+ $oRes->addItem( $item );
40
  }
41
  }
42
  catch ( \InvalidArgumentException $e ) {
43
  }
44
 
45
  if ( !$opts->isRepairFileWP()
46
+ && Services::CoreFileHashes()->isCoreFile( $item->path_full ) ) {
47
+ $oRes->addItem( $item );
48
  }
49
  }
50
 
55
  * @param Scans\Mal\ResultItem $item
56
  * @return bool
57
  */
58
+ protected function isResultItemStale( $item ) :bool {
59
  return !Services::WpFs()->exists( $item->path_full );
60
  }
61
 
src/lib/src/Modules/HackGuard/Scan/Controller/Ptg.php CHANGED
@@ -50,14 +50,12 @@ class Ptg extends BaseForAssets {
50
  * @param Scans\Mal\ResultItem $item
51
  * @return bool
52
  */
53
- protected function isResultItemStale( $item ) {
54
- $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;
61
  }
62
 
63
  /**
50
  * @param Scans\Mal\ResultItem $item
51
  * @return bool
52
  */
53
+ protected function isResultItemStale( $item ) :bool {
54
+ $asset = ( new WpOrg\Plugin\Files() )->findPluginFromFile( $item->path_full );
55
+ if ( empty( $asset ) ) {
56
+ $asset = ( new WpOrg\Theme\Files() )->findThemeFromFile( $item->path_full );
 
 
57
  }
58
+ return empty( $asset );
59
  }
60
 
61
  /**
src/lib/src/Modules/HackGuard/Scan/Controller/Ufc.php CHANGED
@@ -21,7 +21,7 @@ class Ufc extends Base {
21
  * @param Scans\Mal\ResultItem $item
22
  * @return bool
23
  */
24
- protected function isResultItemStale( $item ) {
25
  return !Services::WpFs()->exists( $item->path_full );
26
  }
27
 
21
  * @param Scans\Mal\ResultItem $item
22
  * @return bool
23
  */
24
+ protected function isResultItemStale( $item ) :bool {
25
  return !Services::WpFs()->exists( $item->path_full );
26
  }
27
 
src/lib/src/Modules/HackGuard/Scan/Controller/Wcf.php CHANGED
@@ -21,10 +21,9 @@ class Wcf extends Base {
21
  * @param Scans\Wcf\ResultItem $item
22
  * @return bool
23
  */
24
- protected function isResultItemStale( $item ) {
25
- $oCFH = Services::CoreFileHashes();
26
- return !$oCFH->isCoreFile( $item->path_full )
27
- || Services::CoreFileHashes()->isCoreFileHashValid( $item->path_full );
28
  }
29
 
30
  public function isCronAutoRepair() :bool {
21
  * @param Scans\Wcf\ResultItem $item
22
  * @return bool
23
  */
24
+ protected function isResultItemStale( $item ) :bool {
25
+ $CFH = Services::CoreFileHashes();
26
+ return !$CFH->isCoreFile( $item->path_full ) || $CFH->isCoreFileHashValid( $item->path_full );
 
27
  }
28
 
29
  public function isCronAutoRepair() :bool {
src/lib/src/Modules/HackGuard/Scan/Queue/Build/QueueBuilder.php CHANGED
@@ -49,19 +49,19 @@ class QueueBuilder extends Utilities\BackgroundProcessing\BackgroundProcess {
49
  * in the next pass through. Or, return false to remove the
50
  * item from the queue.
51
  *
52
- * @param string $sScanSlug .
53
  * @return mixed
54
  */
55
- protected function task( $sScanSlug ) {
56
 
57
  try {
58
  ( new HackGuard\Scan\Queue\ScanInitiate() )
59
  ->setMod( $this->getMod() )
60
  ->setQueueProcessor( $this->getQueueProcessor() )
61
- ->init( $sScanSlug );
62
  }
63
- catch ( \Exception $oE ) {
64
- // error_log( $oE->getMessage() );
65
  }
66
 
67
  // deletes the scan from the to-be-built array
49
  * in the next pass through. Or, return false to remove the
50
  * item from the queue.
51
  *
52
+ * @param string $slug .
53
  * @return mixed
54
  */
55
+ protected function task( $slug ) {
56
 
57
  try {
58
  ( new HackGuard\Scan\Queue\ScanInitiate() )
59
  ->setMod( $this->getMod() )
60
  ->setQueueProcessor( $this->getQueueProcessor() )
61
+ ->init( $slug );
62
  }
63
+ catch ( \Exception $e ) {
64
+ // error_log( $e->getMessage() );
65
  }
66
 
67
  // deletes the scan from the to-be-built array
src/lib/src/Modules/HackGuard/Scan/Queue/QueueProcessor.php CHANGED
@@ -54,8 +54,8 @@ class QueueProcessor extends Utilities\BackgroundProcessing\BackgroundProcess {
54
  ->setMod( $this->getMod() )
55
  ->execute( $oEntry );
56
  }
57
- catch ( \Exception $oE ) {
58
- // error_log( $oE->getMessage() );
59
  }
60
 
61
  $oUpd->setFinished( $oEntry );
54
  ->setMod( $this->getMod() )
55
  ->execute( $oEntry );
56
  }
57
+ catch ( \Exception $e ) {
58
+ // error_log( $e->getMessage() );
59
  }
60
 
61
  $oUpd->setFinished( $oEntry );
src/lib/src/Modules/HackGuard/Scan/ScansController.php CHANGED
@@ -2,7 +2,7 @@
2
 
3
  namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\HackGuard\Scan;
4
 
5
- use FernleafSystems\Utilities\Logic\OneTimeExecute;
6
  use FernleafSystems\Wordpress\Plugin\Shield\Crons\StandardCron;
7
  use FernleafSystems\Wordpress\Plugin\Shield\Databases;
8
  use FernleafSystems\Wordpress\Plugin\Shield\Modules\HackGuard;
@@ -13,7 +13,7 @@ use FernleafSystems\Wordpress\Services\Services;
13
  class ScansController {
14
 
15
  use ModConsumer;
16
- use OneTimeExecute;
17
  use StandardCron;
18
 
19
  private $scanCons;
@@ -23,8 +23,6 @@ class ScansController {
23
  }
24
 
25
  protected function run() {
26
- /** @var HackGuard\ModCon $mod */
27
- $mod = $this->getMod();
28
  foreach ( $this->getAllScanCons() as $scanCon ) {
29
  $scanCon->execute();
30
  }
2
 
3
  namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\HackGuard\Scan;
4
 
5
+ use FernleafSystems\Utilities\Logic\ExecOnce;
6
  use FernleafSystems\Wordpress\Plugin\Shield\Crons\StandardCron;
7
  use FernleafSystems\Wordpress\Plugin\Shield\Databases;
8
  use FernleafSystems\Wordpress\Plugin\Shield\Modules\HackGuard;
13
  class ScansController {
14
 
15
  use ModConsumer;
16
+ use ExecOnce;
17
  use StandardCron;
18
 
19
  private $scanCons;
23
  }
24
 
25
  protected function run() {
 
 
26
  foreach ( $this->getAllScanCons() as $scanCon ) {
27
  $scanCon->execute();
28
  }
src/lib/src/Modules/HackGuard/Scan/Utilities/PtgAddReinstallLinks.php CHANGED
@@ -2,7 +2,7 @@
2
 
3
  namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\HackGuard\Scan\Utilities;
4
 
5
- use FernleafSystems\Utilities\Logic\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;
@@ -11,7 +11,7 @@ use FernleafSystems\Wordpress\Services\Services;
11
  class PtgAddReinstallLinks {
12
 
13
  use Controller\ScanControllerConsumer;
14
- use OneTimeExecute;
15
 
16
  /**
17
  * @var int
@@ -33,14 +33,32 @@ class PtgAddReinstallLinks {
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();
@@ -59,28 +77,6 @@ class PtgAddReinstallLinks {
59
  return $links;
60
  }
61
 
62
- private function insertCustomJsVars() {
63
- $scanCon = $this->getScanController();
64
- wp_localize_script(
65
- $scanCon->getCon()->prefix( 'global-plugin' ),
66
- 'icwp_wpsf_vars_hp',
67
- [
68
- 'ajax_plugin_reinstall' => $scanCon->getMod()->getAjaxActionData( 'plugin_reinstall' ),
69
- 'reinstallable' => Services::WpPlugins()->getInstalledWpOrgPluginFiles(),
70
- 'strings' => [
71
- 'reinstall_first' => __( 'Re-install First', 'wp-simple-firewall' )
72
- .'. '.__( 'Then Activate', 'wp-simple-firewall' ),
73
- 'okay_reinstall' => sprintf( '%s, %s',
74
- __( 'Yes', 'wp-simple-firewall' ), __( 'Re-Install It', 'wp-simple-firewall' ) ),
75
- 'activate_only' => __( 'Activate Only', 'wp-simple-firewall' ),
76
- 'cancel' => __( 'Cancel', 'wp-simple-firewall' ),
77
- ]
78
- ]
79
- );
80
- wp_enqueue_script( 'jquery-ui-dialog' ); // jquery and jquery-ui should be dependencies, didn't check though...
81
- wp_enqueue_style( 'wp-jquery-ui-dialog' );
82
- }
83
-
84
  private function printPluginReinstallDialogs() {
85
  $scanCon = $this->getScanController();
86
  echo $scanCon->getMod()
2
 
3
  namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\HackGuard\Scan\Utilities;
4
 
5
+ use FernleafSystems\Utilities\Logic\ExecOnce;
6
  use FernleafSystems\Wordpress\Plugin\Shield\Modules\HackGuard;
7
  use FernleafSystems\Wordpress\Plugin\Shield\Modules\HackGuard\Scan\Controller;
8
  use FernleafSystems\Wordpress\Services\Core\VOs\WpPluginVo;
11
  class PtgAddReinstallLinks {
12
 
13
  use Controller\ScanControllerConsumer;
14
+ use ExecOnce;
15
 
16
  /**
17
  * @var int
33
  add_action( 'admin_footer', function () {
34
  $this->printPluginReinstallDialogs();
35
  } );
36
+
37
+ add_filter( 'shield/custom_localisations', function ( array $localz, $hook ) {
38
+ if ( in_array( $hook, [ 'plugins.php', ] ) ) {
39
+ $localz[] = [
40
+ 'global-plugin',
41
+ 'icwp_wpsf_vars_hp',
42
+ [
43
+ 'ajax_plugin_reinstall' => $this->getScanController()->getMod()
44
+ ->getAjaxActionData( 'plugin_reinstall' ),
45
+ 'reinstallable' => Services::WpPlugins()->getInstalledWpOrgPluginFiles(),
46
+ 'strings' => [
47
+ 'reinstall_first' => __( 'Re-install First', 'wp-simple-firewall' )
48
+ .'. '.__( 'Then Activate', 'wp-simple-firewall' ),
49
+ 'okay_reinstall' => sprintf( '%s, %s',
50
+ __( 'Yes', 'wp-simple-firewall' ), __( 'Re-Install It', 'wp-simple-firewall' ) ),
51
+ 'activate_only' => __( 'Activate Only', 'wp-simple-firewall' ),
52
+ 'cancel' => __( 'Cancel', 'wp-simple-firewall' ),
53
+ ]
54
+ ]
55
+ ];
56
  }
57
+ return $localz;
58
+ }, 10, 2 );
59
  }
60
 
61
+ protected function canRun() :bool {
62
  $scanCon = $this->getScanController();
63
  /** @var HackGuard\Options $opts */
64
  $opts = $scanCon->getOptions();
77
  return $links;
78
  }
79
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
80
  private function printPluginReinstallDialogs() {
81
  $scanCon = $this->getScanController();
82
  echo $scanCon->getMod()
src/lib/src/Modules/HackGuard/Scan/Utilities/WpvAddPluginRows.php CHANGED
@@ -109,22 +109,34 @@ class WpvAddPluginRows {
109
 
110
  $vulns = $scanCon->getPluginVulnerabilities( $pluginFile );
111
  if ( count( $vulns ) > 0 ) {
112
- $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
  }
109
 
110
  $vulns = $scanCon->getPluginVulnerabilities( $pluginFile );
111
  if ( count( $vulns ) > 0 ) {
112
+ $name = $scanCon->getCon()->getHumanName();
113
  echo $scanCon->getMod()
114
  ->renderTemplate(
115
+ '/snippets/plugin_vulnerability.twig',
116
  [
117
+ 'strings' => [
118
  'known_vuln' => sprintf( __( '%s has discovered that the currently installed version of the %s plugin has known security vulnerabilities.', 'wp-simple-firewall' ),
119
+ $name, '<strong>'.$pData[ 'Name' ].'</strong>' ),
120
  'name' => __( 'Vulnerability Name', 'wp-simple-firewall' ),
121
  'type' => __( 'Vulnerability Type', 'wp-simple-firewall' ),
122
  'fixed_versions' => __( 'Fixed Versions', 'wp-simple-firewall' ),
123
  'more_info' => __( 'More Info', 'wp-simple-firewall' ),
124
  ],
125
+ 'vars' => [
126
+ 'vulns' => array_map(
127
+ function ( $vuln ) {
128
+ $data = $vuln->getRawData();
129
+ if ( empty( $data[ 'url' ] ) ) {
130
+ $data[ 'url' ] = $vuln->url;
131
+ }
132
+ return $data;
133
+ },
134
+ $vulns
135
+ ),
136
+ 'colspan' => $this->nColumnsCount
137
+ ],
138
+ ],
139
+ true
140
  );
141
  }
142
  }
src/lib/src/Modules/HackGuard/UI.php CHANGED
@@ -241,10 +241,7 @@ class UI extends BaseShield\UI {
241
  ];
242
  }
243
 
244
- /**
245
- * @return array
246
- */
247
- private function getInsightVarsScan_Ptg() {
248
  /** @var ModCon $mod */
249
  $mod = $this->getMod();
250
 
241
  ];
242
  }
243
 
244
+ private function getInsightVarsScan_Ptg() :array {
 
 
 
245
  /** @var ModCon $mod */
246
  $mod = $this->getMod();
247
 
src/lib/src/Modules/HackGuard/Upgrade.php CHANGED
@@ -1,11 +1,22 @@
1
- <?php
2
 
3
  namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\HackGuard;
4
 
5
  use FernleafSystems\Wordpress\Plugin\Shield\Modules\Base;
 
6
 
7
  class Upgrade extends Base\Upgrade {
8
 
 
 
 
 
 
 
 
 
 
 
9
  protected function upgrade_900() {
10
  /** @var Options $opts */
11
  $opts = $this->getOptions();
1
+ <?php declare( strict_types=1 );
2
 
3
  namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\HackGuard;
4
 
5
  use FernleafSystems\Wordpress\Plugin\Shield\Modules\Base;
6
+ use FernleafSystems\Wordpress\Services\Services;
7
 
8
  class Upgrade extends Base\Upgrade {
9
 
10
+ protected function upgrade_1020() {
11
+ /** @var ModCon $mod */
12
+ $mod = $this->getMod();
13
+ $schema = $mod->getDbHandler_FileLocker()->getTableSchema();
14
+ Services::WpDb()->doSql(
15
+ sprintf( "ALTER TABLE `%s` MODIFY `%s` %s;",
16
+ $schema->table, 'content', $schema->enumerateColumns()[ 'content' ] )
17
+ );
18
+ }
19
+
20
  protected function upgrade_900() {
21
  /** @var Options $opts */
22
  $opts = $this->getOptions();
src/lib/src/Modules/Headers/Insights/OverviewCards.php CHANGED
@@ -39,7 +39,7 @@ class OverviewCards extends Shield\Modules\Base\Insights\OverviewCards {
39
  $bCsp = $opts->isEnabledContentSecurityPolicy();
40
  $cards[ 'csp' ] = [
41
  'name' => __( 'Content Security Policies', 'wp-simple-firewall' ),
42
- 'state' => $bCsp ? 1 : -1,
43
  'summary' => $bCsp ?
44
  __( 'Content Security Policy is turned on', 'wp-simple-firewall' )
45
  : __( "Content Security Policies aren't active", 'wp-simple-firewall' ),
39
  $bCsp = $opts->isEnabledContentSecurityPolicy();
40
  $cards[ 'csp' ] = [
41
  'name' => __( 'Content Security Policies', 'wp-simple-firewall' ),
42
+ 'state' => $bCsp ? 1 : 0,
43
  'summary' => $bCsp ?
44
  __( 'Content Security Policy is turned on', 'wp-simple-firewall' )
45
  : __( "Content Security Policies aren't active", 'wp-simple-firewall' ),
src/lib/src/Modules/Headers/Options.php CHANGED
@@ -2,57 +2,44 @@
2
 
3
  namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Headers;
4
 
5
- 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
  /**
15
  * Using this function without first checking isReferrerPolicyEnabled() will result in empty
16
  * referrer policy header in the case of "disabled"
17
- * @return string
18
  */
19
- public function getReferrerPolicyValue() {
20
- $sValue = $this->getOpt( 'x_referrer_policy' );
21
- return in_array( $sValue, [ 'empty', 'disabled' ] ) ? '' : $sValue;
22
  }
23
 
24
- /**
25
- * @return bool
26
- */
27
- public function isEnabledContentSecurityPolicy() {
28
- return $this->isOpt( 'enable_x_content_security_policy', 'Y' );
29
  }
30
 
31
- /**
32
- * @return bool
33
- */
34
- public function isEnabledContentTypeHeader() {
35
  return $this->isOpt( 'x_content_type', 'Y' );
36
  }
37
 
38
- /**
39
- * @return bool
40
- */
41
- public function isEnabledXssProtection() {
42
  return $this->isOpt( 'x_xss_protect', 'Y' );
43
  }
44
 
45
- /**
46
- * @return bool
47
- */
48
- public function isEnabledXFrame() {
49
  return in_array( $this->getOpt( 'x_frame' ), [ 'on_sameorigin', 'on_deny' ] );
50
  }
51
 
52
- /**
53
- * @return bool
54
- */
55
- public function isReferrerPolicyEnabled() {
56
  return !$this->isOpt( 'x_referrer_policy', 'disabled' );
57
  }
58
  }
2
 
3
  namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Headers;
4
 
5
+ class Options extends \FernleafSystems\Wordpress\Plugin\Shield\Modules\BaseShield\Options {
 
 
6
 
7
  public function getCspCustomRules() :array {
8
  $csp = $this->getOpt( 'xcsp_custom' );
9
+ if ( !is_array( $csp ) ) {
10
+ $csp = [];
11
+ $this->setOpt( 'xcsp_custom', $csp );
12
+ }
13
+ return $this->isPremium() ? array_filter( array_map( 'trim', $csp ) ) : [];
14
  }
15
 
16
  /**
17
  * Using this function without first checking isReferrerPolicyEnabled() will result in empty
18
  * referrer policy header in the case of "disabled"
 
19
  */
20
+ public function getReferrerPolicyValue() :string {
21
+ $value = $this->getOpt( 'x_referrer_policy' );
22
+ return in_array( $value, [ 'empty', 'disabled' ] ) ? '' : $value;
23
  }
24
 
25
+ public function isEnabledContentSecurityPolicy() :bool {
26
+ return $this->isOpt( 'enable_x_content_security_policy', 'Y' )
27
+ && !empty( $this->getCspCustomRules() );
 
 
28
  }
29
 
30
+ public function isEnabledContentTypeHeader() :bool {
 
 
 
31
  return $this->isOpt( 'x_content_type', 'Y' );
32
  }
33
 
34
+ public function isEnabledXssProtection() :bool {
 
 
 
35
  return $this->isOpt( 'x_xss_protect', 'Y' );
36
  }
37
 
38
+ public function isEnabledXFrame() :bool {
 
 
 
39
  return in_array( $this->getOpt( 'x_frame' ), [ 'on_sameorigin', 'on_deny' ] );
40
  }
41
 
42
+ public function isReferrerPolicyEnabled() :bool {
 
 
 
43
  return !$this->isOpt( 'x_referrer_policy', 'disabled' );
44
  }
45
  }
src/lib/src/Modules/Headers/Processor.php CHANGED
@@ -9,7 +9,7 @@ class Processor extends BaseShield\Processor {
9
  /**
10
  * @var bool
11
  */
12
- private $bHeadersPushed;
13
 
14
  /**
15
  * @var array
@@ -77,7 +77,7 @@ class Processor extends BaseShield\Processor {
77
  /**
78
  * @return string[] - array of all previously sent headers. Keys are header names, values are header values.
79
  */
80
- private function getAlreadySentHeaders() {
81
  $headers = [];
82
 
83
  if ( function_exists( 'headers_list' ) ) {
@@ -127,30 +127,7 @@ class Processor extends BaseShield\Processor {
127
  private function setContentSecurityPolicyHeader() :array {
128
  /** @var Options $opts */
129
  $opts = $this->getOptions();
130
-
131
- $aDefaultSrcDirectives = [];
132
-
133
- if ( $opts->isOpt( 'xcsp_self', 'Y' ) ) {
134
- $aDefaultSrcDirectives[] = "'self'";
135
- }
136
- if ( $opts->isOpt( 'xcsp_data', 'Y' ) ) {
137
- $aDefaultSrcDirectives[] = "data:";
138
- }
139
- if ( $opts->isOpt( 'xcsp_inline', 'Y' ) ) {
140
- $aDefaultSrcDirectives[] = "'unsafe-inline'";
141
- }
142
- if ( $opts->isOpt( 'xcsp_eval', 'Y' ) ) {
143
- $aDefaultSrcDirectives[] = "'unsafe-eval'";
144
- }
145
- if ( $opts->isOpt( 'xcsp_https', 'Y' ) ) {
146
- $aDefaultSrcDirectives[] = "https:";
147
- }
148
-
149
- $aDefaultSrcDirectives[] = implode( " ", $opts->getOpt( 'xcsp_hosts', [] ) );
150
-
151
- $rules = $opts->getCspCustomRules();
152
- array_unshift( $rules, sprintf( 'default-src %s;', implode( " ", $aDefaultSrcDirectives ) ) );
153
- return [ 'Content-Security-Policy' => implode( ' ', $rules ) ];
154
  }
155
 
156
  private function gatherSecurityHeaders() :array {
@@ -189,11 +166,11 @@ class Processor extends BaseShield\Processor {
189
  }
190
 
191
  private function isHeadersPushed() :bool {
192
- return (bool)$this->bHeadersPushed;
193
  }
194
 
195
  private function setHeadersPushed( bool $pushed ) :self {
196
- $this->bHeadersPushed = $pushed;
197
  return $this;
198
  }
199
  }
9
  /**
10
  * @var bool
11
  */
12
+ private $pushed = false;
13
 
14
  /**
15
  * @var array
77
  /**
78
  * @return string[] - array of all previously sent headers. Keys are header names, values are header values.
79
  */
80
+ private function getAlreadySentHeaders() :array {
81
  $headers = [];
82
 
83
  if ( function_exists( 'headers_list' ) ) {
127
  private function setContentSecurityPolicyHeader() :array {
128
  /** @var Options $opts */
129
  $opts = $this->getOptions();
130
+ return [ 'Content-Security-Policy' => implode( ' ', $opts->getCspCustomRules() ) ];
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
131
  }
132
 
133
  private function gatherSecurityHeaders() :array {
166
  }
167
 
168
  private function isHeadersPushed() :bool {
169
+ return (bool)$this->pushed;
170
  }
171
 
172
  private function setHeadersPushed( bool $pushed ) :self {
173
+ $this->pushed = $pushed;
174
  return $this;
175
  }
176
  }
src/lib/src/Modules/Headers/Strings.php CHANGED
@@ -144,8 +144,13 @@ class Strings extends Base\Strings {
144
  case 'xcsp_custom' :
145
  $sName = __( 'Manual Rules', 'wp-simple-firewall' );
146
  $sSummary = __( 'Manual CSP Rules', 'wp-simple-firewall' );
147
- $sDescription = __( 'Manual CSP rules which are not covered by the rules above.', 'wp-simple-firewall' )
148
- .'<br />- '.__( 'Take a new line per rule.', 'wp-simple-firewall' );
 
 
 
 
 
149
  break;
150
 
151
  default:
144
  case 'xcsp_custom' :
145
  $sName = __( 'Manual Rules', 'wp-simple-firewall' );
146
  $sSummary = __( 'Manual CSP Rules', 'wp-simple-firewall' );
147
+ $sDescription = [
148
+ __( 'Manual CSP rules which are not covered by the rules above.', 'wp-simple-firewall' ),
149
+ '- '.__( 'Take a new line per rule.', 'wp-simple-firewall' ),
150
+ '- '.__( 'We provide this feature as-is: to allow you to add custom CSP rules to your site.', 'wp-simple-firewall' ),
151
+ '- '.__( "We don't provide support for creating CSP rules and whether they're correct for your site.", 'wp-simple-firewall' ),
152
+ '- '.__( "Many WordPress caching plugins ignore HTTP Headers - if they're not showing up, disable page caching.", 'wp-simple-firewall' )
153
+ ];
154
  break;
155
 
156
  default:
src/lib/src/Modules/IPs/AjaxHandler.php CHANGED
@@ -88,7 +88,7 @@ class AjaxHandler extends Shield\Modules\BaseShield\AjaxHandler {
88
  ->setIP( $ip )
89
  ->toManualWhitelist( (string)$label );
90
  }
91
- catch ( \Exception $oE ) {
92
  }
93
  break;
94
 
@@ -99,7 +99,7 @@ class AjaxHandler extends Shield\Modules\BaseShield\AjaxHandler {
99
  ->setIP( $ip )
100
  ->toManualBlacklist( (string)$label );
101
  }
102
- catch ( \Exception $oE ) {
103
  }
104
  break;
105
 
88
  ->setIP( $ip )
89
  ->toManualWhitelist( (string)$label );
90
  }
91
+ catch ( \Exception $e ) {
92
  }
93
  break;
94
 
99
  ->setIP( $ip )
100
  ->toManualBlacklist( (string)$label );
101
  }
102
+ catch ( \Exception $e ) {
103
  }
104
  break;
105
 
src/lib/src/Modules/IPs/BotTrack/Base.php CHANGED
@@ -42,10 +42,7 @@ abstract class Base {
42
  );
43
  }
44
 
45
- /**
46
- * @return array
47
- */
48
- protected function getAuditData() {
49
  return [
50
  'path' => Services::Request()->getPath()
51
  ];
42
  );
43
  }
44
 
45
+ protected function getAuditData() :array {
 
 
 
46
  return [
47
  'path' => Services::Request()->getPath()
48
  ];
src/lib/src/Modules/IPs/BotTrack/TrackFakeWebCrawler.php CHANGED
@@ -16,7 +16,7 @@ class TrackFakeWebCrawler extends Base {
16
  try {
17
  $this->getIfVisitorIdentifiesAsCrawler(); // TEST this logic
18
  }
19
- catch ( \Exception $oE ) {
20
  $this->doTransgression();
21
  }
22
  }
16
  try {
17
  $this->getIfVisitorIdentifiesAsCrawler(); // TEST this logic
18
  }
19
+ catch ( \Exception $e ) {
20
  $this->doTransgression();
21
  }
22
  }
src/lib/src/Modules/IPs/BotTrack/TrackLoginFailed.php CHANGED
@@ -16,32 +16,29 @@ class TrackLoginFailed extends Base {
16
  protected function process() {
17
  add_filter( 'authenticate',
18
  /**
19
- * @param null|\WP_User|\WP_Error $oUser
20
- * @param string $sLogin
21
- * @param string $sPass
22
  * @return null|\WP_User|\WP_Error
23
  */
24
- function ( $oUser, $sLogin, $sPass ) {
25
- if ( is_wp_error( $oUser ) && !empty( $sLogin )
26
- && !empty( $sPass ) && Services::WpUsers()->exists( $sLogin ) ) {
27
- $this->user_login = Services::Data()->validEmail( $sLogin ) ? $sLogin : sanitize_user( $sLogin );
28
  $this->doTransgression();
29
 
30
  // Adds an extra message to login failed
31
- $oUser->add(
32
  $this->getCon()->prefix( 'transgression-warning' ),
33
  $this->getMod()->getTextOpt( 'text_loginfailed' )
34
  );
35
  }
36
- return $oUser;
37
  },
38
  21, 3 ); //right after username/password check
39
  }
40
 
41
- /**
42
- * @return array
43
- */
44
- protected function getAuditData() {
45
  return [
46
  'login' => $this->user_login
47
  ];
16
  protected function process() {
17
  add_filter( 'authenticate',
18
  /**
19
+ * @param null|\WP_User|\WP_Error $user
20
+ * @param string $login
21
+ * @param string $pass
22
  * @return null|\WP_User|\WP_Error
23
  */
24
+ function ( $user, $login, $pass ) {
25
+ if ( is_wp_error( $user ) && !empty( $login )
26
+ && !empty( $pass ) && Services::WpUsers()->exists( $login ) ) {
27
+ $this->user_login = Services::Data()->validEmail( $login ) ? $login : sanitize_user( $login );
28
  $this->doTransgression();
29
 
30
  // Adds an extra message to login failed
31
+ $user->add(
32
  $this->getCon()->prefix( 'transgression-warning' ),
33
  $this->getMod()->getTextOpt( 'text_loginfailed' )
34
  );
35
  }
36
+ return $user;
37
  },
38
  21, 3 ); //right after username/password check
39
  }
40
 
41
+ protected function getAuditData() :array {
 
 
 
42
  return [
43
  'login' => $this->user_login
44
  ];
src/lib/src/Modules/IPs/BotTrack/TrackLoginInvalid.php CHANGED
@@ -16,24 +16,29 @@ class TrackLoginInvalid extends Base {
16
  protected function process() {
17
  add_filter( 'authenticate',
18
  /**
19
- * @param null|\WP_User|\WP_Error $oUser
20
- * @param string $sLogin
 
21
  * @return null|\WP_User|\WP_Error
22
  */
23
- function ( $oUser, $sLogin ) {
24
- if ( !empty( $sLogin ) && !Services::WpUsers()->exists( $sLogin ) ) {
25
- $this->user_login = Services::Data()->validEmail( $sLogin ) ? $sLogin : sanitize_user( $sLogin );
 
 
 
 
 
 
 
26
  $this->doTransgression();
27
  }
28
- return $oUser;
29
  },
30
- 5, 2 );
31
  }
32
 
33
- /**
34
- * @return array
35
- */
36
- protected function getAuditData() {
37
  return [
38
  'login' => $this->user_login
39
  ];
16
  protected function process() {
17
  add_filter( 'authenticate',
18
  /**
19
+ * @param null|\WP_User|\WP_Error $user
20
+ * @param string $login
21
+ * @param string $pass
22
  * @return null|\WP_User|\WP_Error
23
  */
24
+ function ( $user, $login, $pass ) {
25
+ if ( Services::Request()->isPost() && is_wp_error( $user ) && !empty( $pass )
26
+ && ( empty( $login ) || !Services::WpUsers()->exists( $login ) ) ) {
27
+
28
+ if ( empty( $login ) ) {
29
+ $this->user_login = 'empty username';
30
+ }
31
+ else {
32
+ $this->user_login = Services::Data()->validEmail( $login ) ? $login : sanitize_user( $login );
33
+ }
34
  $this->doTransgression();
35
  }
36
+ return $user;
37
  },
38
+ 21, 3 );
39
  }
40
 
41
+ protected function getAuditData() :array {
 
 
 
42
  return [
43
  'login' => $this->user_login
44
  ];
src/lib/src/Modules/IPs/Components/QueryIpBlock.php CHANGED
@@ -16,29 +16,21 @@ class QueryIpBlock {
16
  use Shield\Modules\ModConsumer;
17
  use IpAddressConsumer;
18
 
19
- /**
20
- * @var string
21
- */
22
- private $sIP;
23
-
24
- /**
25
- * @return bool - true if IP is blocked, false otherwise
26
- */
27
- public function run() {
28
- $bIpBlocked = false;
29
 
30
  $oIP = $this->getBlockedIpRecord();
31
  if ( $oIP instanceof Databases\IPs\EntryVO ) {
32
 
33
- $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;
42
  }
43
 
44
  /**
16
  use Shield\Modules\ModConsumer;
17
  use IpAddressConsumer;
18
 
19
+ public function run() :bool {
20
+ $isBlocked = false;
 
 
 
 
 
 
 
 
21
 
22
  $oIP = $this->getBlockedIpRecord();
23
  if ( $oIP instanceof Databases\IPs\EntryVO ) {
24
 
25
+ $isBlocked = true;
26
 
27
  /** @var IPs\ModCon $mod */
28
  $mod = $this->getMod();
29
+ /** @var Databases\IPs\Update $upd */
30
+ $upd = $mod->getDbHandler_IPs()->getQueryUpdater();
31
+ $upd->updateLastAccessAt( $oIP );
32
  }
33
+ return $isBlocked;
34
  }
35
 
36
  /**
src/lib/src/Modules/IPs/Components/UnblockIpByFlag.php CHANGED
@@ -15,9 +15,9 @@ class UnblockIpByFlag {
15
  $mod = $this->getMod();
16
  $FS = Services::WpFs();
17
 
18
- $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 ) );
@@ -31,7 +31,7 @@ class UnblockIpByFlag {
31
  }
32
  }
33
  }
34
- $FS->deleteFile( $sPathUnblockFlag );
35
  }
36
  }
37
  }
15
  $mod = $this->getMod();
16
  $FS = Services::WpFs();
17
 
18
+ $path = $FS->findFileInDir( 'unblock', $this->getCon()->getPath_Flags() );
19
+ if ( !empty( $path ) && $FS->isFile( $path ) ) {
20
+ $sContent = $FS->getFileContent( $path );
21
  if ( !empty( $sContent ) ) {
22
 
23
  $aLines = array_map( 'trim', explode( "\n", $sContent ) );
31
  }
32
  }
33
  }
34
+ $FS->deleteFile( $path );
35
  }
36
  }
37
  }
src/lib/src/Modules/IPs/Lib/BlockRequest.php CHANGED
@@ -15,8 +15,7 @@ class BlockRequest {
15
  if ( $this->isBlocked() ) {
16
 
17
  if ( $this->isAutoUnBlocked() ) {
18
- // TODO: flash message
19
- Services::Response()->redirectToAdmin();
20
  }
21
 
22
  // don't log killed requests
@@ -26,20 +25,14 @@ class BlockRequest {
26
  }
27
  }
28
 
29
- /**
30
- * @return bool
31
- */
32
- private function isBlocked() {
33
  return ( new IPs\Components\QueryIpBlock() )
34
  ->setMod( $this->getMod() )
35
  ->setIp( Services::IP()->getRequestIp() )
36
  ->run();
37
  }
38
 
39
- /**
40
- * @return bool
41
- */
42
- private function isAutoUnBlocked() {
43
  return ( new AutoUnblock() )
44
  ->setMod( $this->getMod() )
45
  ->run();
15
  if ( $this->isBlocked() ) {
16
 
17
  if ( $this->isAutoUnBlocked() ) {
18
+ Services::Response()->redirectToHome();
 
19
  }
20
 
21
  // don't log killed requests
25
  }
26
  }
27
 
28
+ private function isBlocked() :bool {
 
 
 
29
  return ( new IPs\Components\QueryIpBlock() )
30
  ->setMod( $this->getMod() )
31
  ->setIp( Services::IP()->getRequestIp() )
32
  ->run();
33
  }
34
 
35
+ private function isAutoUnBlocked() :bool {
 
 
 
36
  return ( new AutoUnblock() )
37
  ->setMod( $this->getMod() )
38
  ->run();
src/lib/src/Modules/IPs/Lib/Ops/LookupIpOnList.php CHANGED
@@ -14,26 +14,26 @@ class LookupIpOnList {
14
  /**
15
  * @var string
16
  */
17
- private $sListType;
18
 
19
  /**
20
  * @var bool
21
  */
22
- private $bIsBlocked;
23
 
24
  /**
25
- * @param bool $bIncludeRanges
26
  * @return Databases\IPs\EntryVO|null
27
  * @version 8.6.0 - switched to lookup ranges first
28
  */
29
- public function lookup( $bIncludeRanges = true ) {
30
  $IP = null;
31
  if ( !empty( $this->getIP() ) ) {
32
- if ( $bIncludeRanges ) {
33
- foreach ( $this->lookupRange() as $oMaybeIp ) {
34
  try {
35
- if ( Services::IP()->checkIp( $this->getIP(), $oMaybeIp->ip ) ) {
36
- $IP = $oMaybeIp;
37
  break;
38
  }
39
  }
@@ -52,57 +52,57 @@ class LookupIpOnList {
52
  * @return Databases\IPs\EntryVO|null
53
  */
54
  public function lookupIp() {
55
- /** @var Databases\IPs\Select $oSelect */
56
- $oSelect = $this->getDbHandler()->getQuerySelector();
57
 
58
  if ( $this->getListType() == 'white' ) {
59
- $oSelect->filterByWhitelist();
60
  }
61
  elseif ( $this->getListType() == 'black' ) {
62
- $oSelect->filterByBlacklist();
63
  if ( !is_null( $this->isIpBlocked() ) ) {
64
- $oSelect->filterByBlocked( $this->isIpBlocked() );
65
  }
66
  }
67
 
68
- return $oSelect->filterByIsRange( false )
69
- ->filterByIp( $this->getIP() )
70
- ->first();
71
  }
72
 
73
  /**
74
  * @return Databases\IPs\EntryVO[]
75
  */
76
  public function lookupRange() {
77
- /** @var Databases\IPs\Select $oSelect */
78
- $oSelect = $this->getDbHandler()->getQuerySelector();
79
 
80
  if ( $this->getListType() == 'white' ) {
81
- $oSelect->filterByWhitelist();
82
  }
83
  elseif ( $this->getListType() == 'black' ) {
84
- $oSelect->filterByBlacklist();
85
  if ( !is_null( $this->isIpBlocked() ) ) {
86
- $oSelect->filterByBlocked( $this->isIpBlocked() );
87
  }
88
  }
89
 
90
- $aIps = $oSelect->filterByIsRange( true )->query();
91
- return is_array( $aIps ) ? $aIps : [];
92
  }
93
 
94
  /**
95
  * @return string
96
  */
97
  public function getListType() {
98
- return $this->sListType;
99
  }
100
 
101
  /**
102
  * @return bool|null
103
  */
104
  public function isIpBlocked() {
105
- return $this->bIsBlocked;
106
  }
107
 
108
  /**
@@ -110,7 +110,7 @@ class LookupIpOnList {
110
  * @return $this
111
  */
112
  public function setIsIpBlocked( $bIsBlocked ) {
113
- $this->bIsBlocked = $bIsBlocked;
114
  return $this;
115
  }
116
 
@@ -118,7 +118,7 @@ class LookupIpOnList {
118
  * @return $this
119
  */
120
  public function setListTypeBlack() {
121
- $this->sListType = 'black';
122
  return $this;
123
  }
124
 
@@ -126,7 +126,7 @@ class LookupIpOnList {
126
  * @return $this
127
  */
128
  public function setListTypeWhite() {
129
- $this->sListType = 'white';
130
  return $this;
131
  }
132
  }
14
  /**
15
  * @var string
16
  */
17
+ private $listType;
18
 
19
  /**
20
  * @var bool
21
  */
22
+ private $isBlocked;
23
 
24
  /**
25
+ * @param bool $includeRanges
26
  * @return Databases\IPs\EntryVO|null
27
  * @version 8.6.0 - switched to lookup ranges first
28
  */
29
+ public function lookup( $includeRanges = true ) {
30
  $IP = null;
31
  if ( !empty( $this->getIP() ) ) {
32
+ if ( $includeRanges ) {
33
+ foreach ( $this->lookupRange() as $maybe ) {
34
  try {
35
+ if ( Services::IP()->checkIp( $this->getIP(), $maybe->ip ) ) {
36
+ $IP = $maybe;
37
  break;
38
  }
39
  }
52
  * @return Databases\IPs\EntryVO|null
53
  */
54
  public function lookupIp() {
55
+ /** @var Databases\IPs\Select $select */
56
+ $select = $this->getDbHandler()->getQuerySelector();
57
 
58
  if ( $this->getListType() == 'white' ) {
59
+ $select->filterByWhitelist();
60
  }
61
  elseif ( $this->getListType() == 'black' ) {
62
+ $select->filterByBlacklist();
63
  if ( !is_null( $this->isIpBlocked() ) ) {
64
+ $select->filterByBlocked( $this->isIpBlocked() );
65
  }
66
  }
67
 
68
+ return $select->filterByIsRange( false )
69
+ ->filterByIp( $this->getIP() )
70
+ ->first();
71
  }
72
 
73
  /**
74
  * @return Databases\IPs\EntryVO[]
75
  */
76
  public function lookupRange() {
77
+ /** @var Databases\IPs\Select $select */
78
+ $select = $this->getDbHandler()->getQuerySelector();
79
 
80
  if ( $this->getListType() == 'white' ) {
81
+ $select->filterByWhitelist();
82
  }
83
  elseif ( $this->getListType() == 'black' ) {
84
+ $select->filterByBlacklist();
85
  if ( !is_null( $this->isIpBlocked() ) ) {
86
+ $select->filterByBlocked( $this->isIpBlocked() );
87
  }
88
  }
89
 
90
+ $IPs = $select->filterByIsRange( true )->query();
91
+ return is_array( $IPs ) ? $IPs : [];
92
  }
93
 
94
  /**
95
  * @return string
96
  */
97
  public function getListType() {
98
+ return $this->listType;
99
  }
100
 
101
  /**
102
  * @return bool|null
103
  */
104
  public function isIpBlocked() {
105
+ return $this->isBlocked;
106
  }
107
 
108
  /**
110
  * @return $this
111
  */
112
  public function setIsIpBlocked( $bIsBlocked ) {
113
+ $this->isBlocked = $bIsBlocked;
114
  return $this;
115
  }
116
 
118
  * @return $this
119
  */
120
  public function setListTypeBlack() {
121
+ $this->listType = 'black';
122
  return $this;
123
  }
124
 
126
  * @return $this
127
  */
128
  public function setListTypeWhite() {
129
+ $this->listType = 'white';
130
  return $this;
131
  }
132
  }
src/lib/src/Modules/IPs/WpCli/Add.php CHANGED
@@ -47,8 +47,8 @@ class Add extends BaseAddRemove {
47
  $oAdder->toManualBlacklist( $label );
48
  }
49
  }
50
- catch ( \Exception $oE ) {
51
- WP_CLI::error( $oE->getMessage() );
52
  }
53
  WP_CLI::success( __( 'IP address added successfully.', 'wp-simple-firewall' ) );
54
  }
47
  $oAdder->toManualBlacklist( $label );
48
  }
49
  }
50
+ catch ( \Exception $e ) {
51
+ WP_CLI::error( $e->getMessage() );
52
  }
53
  WP_CLI::success( __( 'IP address added successfully.', 'wp-simple-firewall' ) );
54
  }
src/lib/src/Modules/Insights/ModCon.php CHANGED
@@ -2,7 +2,7 @@
2
 
3
  namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Insights;
4
 
5
- use FernleafSystems\Wordpress\Plugin\Shield\Modules;
6
  use FernleafSystems\Wordpress\Plugin\Shield\Modules\BaseShield;
7
  use FernleafSystems\Wordpress\Services\Services;
8
 
@@ -45,186 +45,97 @@ class ModCon extends BaseShield\ModCon {
45
  return $UI->renderPages();
46
  }
47
 
48
- public function 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
  }
2
 
3
  namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Insights;
4
 
5
+ use FernleafSystems\Wordpress\Plugin\Shield\Controller\Assets\Enqueue;
6
  use FernleafSystems\Wordpress\Plugin\Shield\Modules\BaseShield;
7
  use FernleafSystems\Wordpress\Services\Services;
8
 
45
  return $UI->renderPages();
46
  }
47
 
48
+ public function getScriptLocalisations() :array {
49
+ $con = $this->getCon();
50
+ $locals = parent::getScriptLocalisations();
51
+ $locals[] = [
52
+ 'plugin',
53
+ 'icwp_wpsf_vars_insights',
54
+ [
55
+ 'strings' => [
56
+ 'downloading_file' => __( 'Downloading file, please wait...', 'wp-simple-firewall' ),
57
+ 'downloading_file_problem' => __( 'There was a problem downloading the file.', 'wp-simple-firewall' ),
58
+ 'select_action' => __( 'Please select an action to perform.', 'wp-simple-firewall' ),
59
+ 'are_you_sure' => __( 'Are you sure?', 'wp-simple-firewall' ),
60
+ ],
61
+ ]
62
+ ];
63
+ $locals[] = [
64
+ $con->prefix( 'ip_detect' ),
65
+ 'icwp_wpsf_vars_ipdetect',
66
+ [ 'ajax' => $con->getModule_Plugin()->getAjaxActionData( 'ipdetect' ) ]
67
+ ];
68
+
69
+ return $locals;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
70
  }
71
 
72
+ public function getCustomScriptEnqueues() :array {
73
+ $enq = [
74
+ Enqueue::CSS => [],
75
+ Enqueue::JS => [],
76
+ ];
77
+
78
  $con = $this->getCon();
79
+ $iNav = Services::Request()->query( 'inav', 'overview' );
80
+ $oTourManager = $con->getModule_Plugin()->getTourManager();
81
+
82
+ switch ( $iNav ) {
83
+
84
+ case 'importexport':
85
+ $enq[ Enqueue::JS ][] = 'shield/import';
86
+ break;
87
+
88
+ case 'overview':
89
+ case 'reports':
90
+
91
+ $enq[ Enqueue::JS ] = [
92
+ 'chartist.min',
93
+ 'chartist-plugin-legend',
94
+ 'charts',
95
+ 'shuffle',
96
+ 'shield-card-shuffle',
97
+ 'ip_detect'
98
+ ];
99
+ $enq[ Enqueue::CSS ] = [
100
+ 'chartist.min',
101
+ 'chartist-plugin-legend'
102
+ ];
103
+
104
+ if ( $oTourManager->canShow( 'insights_overview' ) ) {
105
+ $enq[ Enqueue::JS ][] = 'introjs.min';
106
+ $enq[ Enqueue::CSS ][] = 'introjs.min';
107
+ }
108
+ break;
109
+
110
+ case 'notes':
111
+ case 'scans':
112
+ case 'audit':
113
+ case 'traffic':
114
+ case 'ips':
115
+ case 'debug':
116
+ case 'users':
117
+
118
+ $enq[ Enqueue::JS ][] = 'shield-tables';
119
+ if ( $iNav == 'scans' ) {
120
+ $enq[ Enqueue::JS ][] = 'shield-scans';
121
+ }
122
+ elseif ( $iNav == 'ips' ) {
123
+ $enq[ Enqueue::JS ][] = 'shield/ipanalyse';
124
+ }
125
+
126
+ if ( in_array( $iNav, [ 'audit', 'traffic' ] ) ) {
127
+ $enq[ Enqueue::JS ][] = 'bootstrap-datepicker';
128
+ $enq[ Enqueue::CSS ][] = 'bootstrap-datepicker';
129
+ }
130
+ break;
131
  }
132
+
133
+ return $enq;
134
+ }
135
+
136
+ /**
137
+ * @deprecated 10.2
138
+ */
139
+ private function includeScriptIpDetect() {
140
  }
141
  }
src/lib/src/Modules/Integrations/Lib/MainWP/Client/Actions/Sync.php CHANGED
@@ -33,7 +33,7 @@ class Sync {
33
  'installed_at' => $con->getModule_Plugin()->getInstallDate(),
34
  'sync_at' => Services::Request()->ts(),
35
  'version' => $con->getVersion(),
36
- 'has_update' => Services::WpPlugins()->isUpdateAvailable( $con->getPluginBaseFile() ),
37
  ];
38
  }
39
 
33
  'installed_at' => $con->getModule_Plugin()->getInstallDate(),
34
  'sync_at' => Services::Request()->ts(),
35
  'version' => $con->getVersion(),
36
+ 'has_update' => Services::WpPlugins()->isUpdateAvailable( $con->base_file ),
37
  ];
38
  }
39
 
src/lib/src/Modules/Integrations/Lib/MainWP/Server/Data/ClientPluginStatus.php CHANGED
@@ -71,7 +71,7 @@ class ClientPluginStatus {
71
  public function getInstalledPlugin() {
72
  $thePlugin = null;
73
 
74
- $baseName = basename( $this->getCon()->getPluginBaseFile() );
75
  foreach ( $this->getMwpSite()->plugins as $plugin ) {
76
  if ( basename( $plugin[ 'slug' ] ) === $baseName ) {
77
  $thePlugin = $plugin;
71
  public function getInstalledPlugin() {
72
  $thePlugin = null;
73
 
74
+ $baseName = basename( $this->getCon()->base_file );
75
  foreach ( $this->getMwpSite()->plugins as $plugin ) {
76
  if ( basename( $plugin[ 'slug' ] ) === $baseName ) {
77
  $thePlugin = $plugin;
src/lib/src/Modules/Integrations/Lib/MainWP/Server/Init.php CHANGED
@@ -35,7 +35,7 @@ class Init {
35
  'plugin' => $this->getCon()->getRootFile(),
36
  // while this is a "callback" field, a Closure isn't supported as it's serialized for DB storage. Sigh.
37
  'callback' => [ $extensionsPage, 'render' ],
38
- 'icon' => $con->getPluginUrl_Image( 'pluginlogo_col_32x32.png' ),
39
  ];
40
  return $exts;
41
  }, 10, 1 );
35
  'plugin' => $this->getCon()->getRootFile(),
36
  // while this is a "callback" field, a Closure isn't supported as it's serialized for DB storage. Sigh.
37
  'callback' => [ $extensionsPage, 'render' ],
38
+ 'icon' => $con->urls->forImage( 'pluginlogo_col_32x32.png' ),
39
  ];
40
  return $exts;
41
  }, 10, 1 );
src/lib/src/Modules/Integrations/Lib/MainWP/Server/UI/ExtensionSettingsPage.php CHANGED
@@ -3,6 +3,7 @@
3
  namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Integrations\Lib\MainWP\Server\UI;
4
 
5
  use FernleafSystems\Utilities\Logic\OneTimeExecute;
 
6
  use FernleafSystems\Wordpress\Plugin\Shield\Modules\Integrations\Lib\MainWP\Controller;
7
  use FernleafSystems\Wordpress\Plugin\Shield\Modules\Integrations\Lib\MainWP\Server\UI\PageRender;
8
  use FernleafSystems\Wordpress\Plugin\Shield\Modules\ModConsumer;
@@ -14,26 +15,13 @@ class ExtensionSettingsPage {
14
  use OneTimeExecute;
15
 
16
  protected function run() {
17
- add_action( 'admin_enqueue_scripts', function ( $hook ) {
18
- $con = $this->getCon();
19
- if ( 'mainwp_page_'.$con->mwpVO->extension->page === $hook ) {
20
- $handle = $con->prefix( 'mainwp-extension' );
21
- wp_register_script(
22
- $handle,
23
- $con->getPluginUrl_Js( 'shield/mainwp-extension.js' ),
24
- [ 'jquery' ],
25
- $con->getVersion(),
26
- true
27
- );
28
- wp_enqueue_script( $handle );
29
 
30
- 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(
@@ -52,7 +40,8 @@ class ExtensionSettingsPage {
52
  // );
53
  // wp_enqueue_style( 'semantic-ui-datatables-select' );
54
  }
55
- } );
 
56
  }
57
 
58
  /**
@@ -124,8 +113,6 @@ class ExtensionSettingsPage {
124
  }
125
 
126
  private function serverPluginNeedsUpdate() :bool {
127
- return Services::WpPlugins()->isUpdateAvailable(
128
- $this->getCon()->getPluginBaseFile()
129
- );
130
  }
131
  }
3
  namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Integrations\Lib\MainWP\Server\UI;
4
 
5
  use FernleafSystems\Utilities\Logic\OneTimeExecute;
6
+ use FernleafSystems\Wordpress\Plugin\Shield\Controller\Assets\Enqueue;
7
  use FernleafSystems\Wordpress\Plugin\Shield\Modules\Integrations\Lib\MainWP\Controller;
8
  use FernleafSystems\Wordpress\Plugin\Shield\Modules\Integrations\Lib\MainWP\Server\UI\PageRender;
9
  use FernleafSystems\Wordpress\Plugin\Shield\Modules\ModConsumer;
15
  use OneTimeExecute;
16
 
17
  protected function run() {
 
 
 
 
 
 
 
 
 
 
 
 
18
 
19
+ add_filter( 'shield/custom_enqueues', function ( array $enqueues, $hook ) {
20
+
21
+ if ( 'mainwp_page_'.$this->getCon()->mwpVO->extension->page === $hook ) {
22
+
23
+ $enqueues[ Enqueue::JS ][] = 'shield/mainwp-extension';
24
+ $enqueues[ Enqueue::CSS ][] = 'mainwp-extension';
 
25
 
26
  // $handle = 'semantic-ui-datatables-select';
27
  // wp_register_script(
40
  // );
41
  // wp_enqueue_style( 'semantic-ui-datatables-select' );
42
  }
43
+ return $enqueues;
44
+ }, 10,2 );
45
  }
46
 
47
  /**
113
  }
114
 
115
  private function serverPluginNeedsUpdate() :bool {
116
+ return Services::WpPlugins()->isUpdateAvailable( $this->getCon()->base_file );
 
 
117
  }
118
  }
src/lib/src/Modules/License/AjaxHandler.php CHANGED
@@ -60,43 +60,43 @@ class AjaxHandler extends Shield\Modules\BaseShield\AjaxHandler {
60
  $mod = $this->getMod();
61
  $sHandler = $mod->getLicenseHandler();
62
 
63
- $bSuccess = false;
64
- $sMessage = 'Unsupported license action';
65
 
66
  $sLicenseAction = Services::Request()->post( 'license-action' );
67
 
68
  if ( $sLicenseAction == 'clear' ) {
69
- $bSuccess = true;
70
  $sHandler->deactivate( false );
71
  $sHandler->clearLicense();
72
- $sMessage = __( 'Success', 'wp-simple-firewall' ).'! '
73
- .__( 'Reloading page', 'wp-simple-firewall' ).'...';
74
  }
75
  elseif ( $sLicenseAction == 'check' ) {
76
 
77
  $nCheckInterval = $sHandler->getLicenseNotCheckedForInterval();
78
  if ( $nCheckInterval < 20 ) {
79
  $nWait = 20 - $nCheckInterval;
80
- $sMessage = sprintf(
81
  __( 'Please wait %s before attempting another license check.', 'wp-simple-firewall' ),
82
  sprintf( _n( '%s second', '%s seconds', $nWait, 'wp-simple-firewall' ), $nWait )
83
  );
84
  }
85
  else {
86
  try {
87
- $bSuccess = $sHandler->verify( true )
88
- ->hasValidWorkingLicense();
89
- $sMessage = $bSuccess ? __( 'Valid license found.', 'wp-simple-firewall' ) : __( "Valid license couldn't be found.", 'wp-simple-firewall' );
90
  }
91
- catch ( \Exception $oE ) {
92
- $sMessage = $oE->getMessage();
93
  }
94
  }
95
  }
96
 
97
  return [
98
- 'success' => $bSuccess,
99
- 'message' => $sMessage,
100
  ];
101
  }
102
  }
60
  $mod = $this->getMod();
61
  $sHandler = $mod->getLicenseHandler();
62
 
63
+ $success = false;
64
+ $msg = 'Unsupported license action';
65
 
66
  $sLicenseAction = Services::Request()->post( 'license-action' );
67
 
68
  if ( $sLicenseAction == 'clear' ) {
69
+ $success = true;
70
  $sHandler->deactivate( false );
71
  $sHandler->clearLicense();
72
+ $msg = __( 'Success', 'wp-simple-firewall' ).'! '
73
+ .__( 'Reloading page', 'wp-simple-firewall' ).'...';
74
  }
75
  elseif ( $sLicenseAction == 'check' ) {
76
 
77
  $nCheckInterval = $sHandler->getLicenseNotCheckedForInterval();
78
  if ( $nCheckInterval < 20 ) {
79
  $nWait = 20 - $nCheckInterval;
80
+ $msg = sprintf(
81
  __( 'Please wait %s before attempting another license check.', 'wp-simple-firewall' ),
82
  sprintf( _n( '%s second', '%s seconds', $nWait, 'wp-simple-firewall' ), $nWait )
83
  );
84
  }
85
  else {
86
  try {
87
+ $success = $sHandler->verify( true )
88
+ ->hasValidWorkingLicense();
89
+ $msg = $success ? __( 'Valid license found.', 'wp-simple-firewall' ) : __( "Valid license couldn't be found.", 'wp-simple-firewall' );
90
  }
91
+ catch ( \Exception $e ) {
92
+ $msg = $e->getMessage();
93
  }
94
  }
95
  }
96
 
97
  return [
98
+ 'success' => $success,
99
+ 'message' => $msg,
100
  ];
101
  }
102
  }
src/lib/src/Modules/License/Lib/LicenseHandler.php CHANGED
@@ -47,7 +47,7 @@ class LicenseHandler {
47
  try {
48
  $mod->getLicenseHandler()->verify( true );
49
  }
50
- catch ( \Exception $oE ) {
51
  }
52
  } );
53
  }
47
  try {
48
  $mod->getLicenseHandler()->verify( true );
49
  }
50
+ catch ( \Exception $e ) {
51
  }
52
  } );
53
  }
src/lib/src/Modules/License/Lib/PluginNameSuffix.php ADDED
@@ -0,0 +1,29 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\License\Lib;
4
+
5
+ use FernleafSystems\Utilities\Logic\OneTimeExecute;
6
+ use FernleafSystems\Wordpress\Plugin\Shield\Modules;
7
+ use FernleafSystems\Wordpress\Plugin\Shield\Modules\SecurityAdmin;
8
+
9
+ class PluginNameSuffix {
10
+
11
+ use Modules\ModConsumer;
12
+ use OneTimeExecute;
13
+
14
+ protected function canRun() {
15
+ $con = $this->getCon();
16
+ /** @var SecurityAdmin\Options $optsSecAdmin */
17
+ $optsSecAdmin = $con->getModule_SecAdmin()->getOptions();
18
+ return (bool)apply_filters( 'shield/add_pro_suffix', $con->isPremiumActive() && !$optsSecAdmin->isEnabledWhitelabel() );
19
+ }
20
+
21
+ protected function run() {
22
+ add_filter( $this->getCon()->prefix( 'plugin_labels' ), function ( $labels ) {
23
+ $labels[ 'Name' ] = 'ShieldPRO';
24
+ $labels[ 'Title' ] = 'ShieldPRO';
25
+ $labels[ 'MenuTitle' ] = 'ShieldPRO';
26
+ return $labels;
27
+ } );
28
+ }
29
+ }
src/lib/src/Modules/License/Lib/WpHashes/ApiTokenManager.php CHANGED
@@ -51,7 +51,7 @@ class ApiTokenManager {
51
  try {
52
  $aT = array_merge( $aT, $this->solicitApiToken() );
53
  }
54
- catch ( \Exception $oE ) {
55
  }
56
  $aT[ 'attempt_at' ] = Services::Request()->ts();
57
  $aT[ 'next_attempt_from' ] = Services::Request()->ts() + HOUR_IN_SECONDS;
51
  try {
52
  $aT = array_merge( $aT, $this->solicitApiToken() );
53
  }
54
+ catch ( \Exception $e ) {
55
  }
56
  $aT[ 'attempt_at' ] = Services::Request()->ts();
57
  $aT[ 'next_attempt_from' ] = Services::Request()->ts() + HOUR_IN_SECONDS;
src/lib/src/Modules/License/Processor.php CHANGED
@@ -11,4 +11,10 @@ class Processor extends BaseShield\Processor {
11
  $mod = $this->getMod();
12
  $mod->getLicenseHandler()->execute();
13
  }
 
 
 
 
 
 
14
  }
11
  $mod = $this->getMod();
12
  $mod->getLicenseHandler()->execute();
13
  }
14
+
15
+ public function onWpLoaded() {
16
+ ( new Lib\PluginNameSuffix() )
17
+ ->setMod( $this->getMod() )
18
+ ->execute();
19
+ }
20
  }
src/lib/src/Modules/License/WpCli/License.php CHANGED
@@ -104,9 +104,9 @@ class License extends Base\WpCli\BaseWpCliCmd {
104
  ->hasValidWorkingLicense();
105
  $sMessage = $bSuccess ? __( 'Valid license found and installed.', 'wp-simple-firewall' ) : __( "Valid license couldn't be found.", 'wp-simple-firewall' );
106
  }
107
- catch ( \Exception $oE ) {
108
  $bSuccess = false;
109
- $sMessage = $oE->getMessage();
110
  }
111
 
112
  $bSuccess ? WP_CLI::success( $sMessage ) : WP_CLI::error( $sMessage );
104
  ->hasValidWorkingLicense();
105
  $sMessage = $bSuccess ? __( 'Valid license found and installed.', 'wp-simple-firewall' ) : __( "Valid license couldn't be found.", 'wp-simple-firewall' );
106
  }
107
+ catch ( \Exception $e ) {
108
  $bSuccess = false;
109
+ $sMessage = $e->getMessage();
110
  }
111
 
112
  $bSuccess ? WP_CLI::success( $sMessage ) : WP_CLI::error( $sMessage );
src/lib/src/Modules/LoginGuard/AjaxHandler.php CHANGED
@@ -12,34 +12,34 @@ class AjaxHandler extends Shield\Modules\BaseShield\AjaxHandler {
12
 
13
  switch ( $action ) {
14
  case 'gen_backup_codes':
15
- $aResponse = $this->ajaxExec_GenBackupCodes();
16
  break;
17
 
18
  case 'del_backup_codes':
19
- $aResponse = $this->ajaxExec_DeleteBackupCodes();
20
  break;
21
 
22
  case 'disable_2fa_email':
23
- $aResponse = $this->ajaxExec_Disable2faEmail();
24
  break;
25
 
26
  case 'resend_verification_email':
27
- $aResponse = $this->ajaxExec_ResendEmailVerification();
28
  break;
29
 
30
  case 'u2f_remove':
31
- $aResponse = $this->ajaxExec_ProfileU2fRemove();
32
  break;
33
 
34
  case 'yubikey_remove':
35
- $aResponse = $this->ajaxExec_ProfileYubikeyRemove();
36
  break;
37
 
38
  default:
39
- $aResponse = parent::processAjaxAction( $action );
40
  }
41
 
42
- return $aResponse;
43
  }
44
 
45
  protected function ajaxExec_GenBackupCodes() :array {
@@ -89,11 +89,13 @@ class AjaxHandler extends Shield\Modules\BaseShield\AjaxHandler {
89
  $mod = $this->getMod();
90
 
91
  $key = Services::Request()->post( 'u2fid' );
92
- ( 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' ),
98
  'page_reload' => true
99
  ];
12
 
13
  switch ( $action ) {
14
  case 'gen_backup_codes':
15
+ $response = $this->ajaxExec_GenBackupCodes();
16
  break;
17
 
18
  case 'del_backup_codes':
19
+ $response = $this->ajaxExec_DeleteBackupCodes();
20
  break;
21
 
22
  case 'disable_2fa_email':
23
+ $response = $this->ajaxExec_Disable2faEmail();
24
  break;
25
 
26
  case 'resend_verification_email':
27
+ $response = $this->ajaxExec_ResendEmailVerification();
28
  break;
29
 
30
  case 'u2f_remove':
31
+ $response = $this->ajaxExec_ProfileU2fRemove();
32
  break;
33
 
34
  case 'yubikey_remove':
35
+ $response = $this->ajaxExec_ProfileYubikeyRemove();
36
  break;
37
 
38
  default:
39
+ $response = parent::processAjaxAction( $action );
40
  }
41
 
42
+ return $response;
43
  }
44
 
45
  protected function ajaxExec_GenBackupCodes() :array {
89
  $mod = $this->getMod();
90
 
91
  $key = Services::Request()->post( 'u2fid' );
92
+ if ( !empty( $key ) ) {
93
+ ( new TwoFactor\Provider\U2F() )
94
+ ->setMod( $mod )
95
+ ->removeRegisteredU2fId( Services::WpUsers()->getCurrentWpUser(), $key );
96
+ }
97
  return [
98
+ 'success' => !empty( $key ),
99
  'message' => __( 'Registered U2F device removed from profile.', 'wp-simple-firewall' ),
100
  'page_reload' => true
101
  ];
src/lib/src/Modules/LoginGuard/Lib/AntiBot/FormProviders/BaseFormProvider.php CHANGED
@@ -46,21 +46,21 @@ abstract class BaseFormProvider {
46
  try {
47
  $this->checkProviders();
48
  }
49
- catch ( \Exception $oE ) {
50
- Services::WpGeneral()->wpDie( $oE->getMessage() );
51
  }
52
  }
53
 
54
  public function run() {
55
- /** @var LoginGuard\Options $oOpts */
56
- $oOpts = $this->getOptions();
57
- if ( $oOpts->isProtectLogin() ) {
58
  $this->login();
59
  }
60
- if ( $oOpts->isProtectRegister() ) {
61
  $this->register();
62
  }
63
- if ( $oOpts->isProtectLostPassword() ) {
64
  $this->lostpassword();
65
  }
66
  }
46
  try {
47
  $this->checkProviders();
48
  }
49
+ catch ( \Exception $e ) {
50
+ Services::WpGeneral()->wpDie( $e->getMessage() );
51
  }
52
  }
53
 
54
  public function run() {
55
+ /** @var LoginGuard\Options $opts */
56
+ $opts = $this->getOptions();
57
+ if ( $opts->isProtectLogin() ) {
58
  $this->login();
59
  }
60
+ if ( $opts->isProtectRegister() ) {
61
  $this->register();
62
  }
63
+ if ( $opts->isProtectLostPassword() ) {
64
  $this->lostpassword();
65
  }
66
  }
src/lib/src/Modules/LoginGuard/Lib/AntiBot/FormProviders/EasyDigitalDownloads.php CHANGED
@@ -18,9 +18,9 @@ class EasyDigitalDownloads extends BaseFormProvider {
18
  $this->setActionToAudit( 'edd-register' )
19
  ->checkProviders();
20
  }
21
- catch ( \Exception $oE ) {
22
  if ( function_exists( 'edd_set_error' ) ) {
23
- edd_set_error( $this->getCon()->prefix( rand() ), $oE->getMessage() );
24
  }
25
  }
26
  }
18
  $this->setActionToAudit( 'edd-register' )
19
  ->checkProviders();
20
  }
21
+ catch ( \Exception $e ) {
22
  if ( function_exists( 'edd_set_error' ) ) {
23
+ edd_set_error( $this->getCon()->prefix( rand() ), $e->getMessage() );
24
  }
25
  }
26
  }
src/lib/src/Modules/LoginGuard/Lib/AntiBot/FormProviders/LearnPress.php CHANGED
@@ -30,8 +30,8 @@ class LearnPress extends BaseFormProvider {
30
  $this->setActionToAudit( 'learnpress-login' )
31
  ->checkProviders();
32
  }
33
- catch ( \Exception $oE ) {
34
- $sFieldNameOrError = new \WP_Error( 'shield-fail-login', $oE->getMessage() );
35
  }
36
  }
37
  return $sFieldNameOrError;
@@ -47,8 +47,8 @@ class LearnPress extends BaseFormProvider {
47
  $this->setActionToAudit( 'learnpress-register' )
48
  ->checkProviders();
49
  }
50
- catch ( \Exception $oE ) {
51
- $sFieldNameOrError = new \WP_Error( 'shield-fail-register', $oE->getMessage() );
52
  }
53
  }
54
  return $sFieldNameOrError;
30
  $this->setActionToAudit( 'learnpress-login' )
31
  ->checkProviders();
32
  }
33
+ catch ( \Exception $e ) {
34
+ $sFieldNameOrError = new \WP_Error( 'shield-fail-login', $e->getMessage() );
35
  }
36
  }
37
  return $sFieldNameOrError;
47
  $this->setActionToAudit( 'learnpress-register' )
48
  ->checkProviders();
49
  }
50
+ catch ( \Exception $e ) {
51
+ $sFieldNameOrError = new \WP_Error( 'shield-fail-register', $e->getMessage() );
52
  }
53
  }
54
  return $sFieldNameOrError;
src/lib/src/Modules/LoginGuard/Lib/AntiBot/FormProviders/MemberPress.php CHANGED
@@ -35,8 +35,8 @@ class MemberPress extends BaseFormProvider {
35
  $this->setActionToAudit( 'memberpress-login' )
36
  ->checkProviders();
37
  }
38
- catch ( \Exception $oE ) {
39
- $aErrors[] = $oE->getMessage();
40
  }
41
  }
42
  return $aErrors;
@@ -52,8 +52,8 @@ class MemberPress extends BaseFormProvider {
52
  $this->setActionToAudit( 'memberpress-lostpassword' )
53
  ->checkProviders();
54
  }
55
- catch ( \Exception $oE ) {
56
- $aErrors[] = $oE->getMessage();
57
  }
58
  }
59
  return $aErrors;
@@ -70,8 +70,8 @@ class MemberPress extends BaseFormProvider {
70
  $this->setActionToAudit( 'memberpress-register' )
71
  ->checkProviders();
72
  }
73
- catch ( \Exception $oE ) {
74
- $aErrors[] = $oE->getMessage();
75
  }
76
  }
77
  return $aErrors;
35
  $this->setActionToAudit( 'memberpress-login' )
36
  ->checkProviders();
37
  }
38
+ catch ( \Exception $e ) {
39
+ $aErrors[] = $e->getMessage();
40
  }
41
  }
42
  return $aErrors;
52
  $this->setActionToAudit( 'memberpress-lostpassword' )
53
  ->checkProviders();
54
  }
55
+ catch ( \Exception $e ) {
56
+ $aErrors[] = $e->getMessage();
57
  }
58
  }
59
  return $aErrors;
70
  $this->setActionToAudit( 'memberpress-register' )
71
  ->checkProviders();
72
  }
73
+ catch ( \Exception $e ) {
74
+ $aErrors[] = $e->getMessage();
75
  }
76
  }
77
  return $aErrors;
src/lib/src/Modules/LoginGuard/Lib/AntiBot/FormProviders/PaidMemberSubscriptions.php CHANGED
@@ -19,8 +19,8 @@ class PaidMemberSubscriptions extends BaseFormProvider {
19
  $this->setActionToAudit( 'paidmembersubscriptions-register' )
20
  ->checkProviders();
21
  }
22
- catch ( \Exception $oE ) {
23
- \pms_errors()->add( 'shield-fail-register', $oE->getMessage() );
24
  }
25
  }
26
  }
19
  $this->setActionToAudit( 'paidmembersubscriptions-register' )
20
  ->checkProviders();
21
  }
22
+ catch ( \Exception $e ) {
23
+ \pms_errors()->add( 'shield-fail-register', $e->getMessage() );
24
  }
25
  }
26
  }
src/lib/src/Modules/LoginGuard/Lib/AntiBot/FormProviders/ProfileBuilder.php CHANGED
@@ -15,18 +15,18 @@ class ProfileBuilder extends BaseFormProvider {
15
  }
16
 
17
  /**
18
- * @param array $aErrors
19
  * @return array
20
  */
21
- public function checkRegister( $aErrors ) {
22
  try {
23
  $this->setActionToAudit( 'profilebuilder-register' )
24
  ->checkProviders();
25
  }
26
- catch ( \Exception $oE ) {
27
- $aErrors[ 'shield-fail-register' ] =
28
  '<span class="wppb-form-error">Bot</span>';
29
  }
30
- return $aErrors;
31
  }
32
  }
15
  }
16
 
17
  /**
18
+ * @param array $errors
19
  * @return array
20
  */
21
+ public function checkRegister( $errors ) {
22
  try {
23
  $this->setActionToAudit( 'profilebuilder-register' )
24
  ->checkProviders();
25
  }
26
+ catch ( \Exception $e ) {
27
+ $errors[ 'shield-fail-register' ] =
28
  '<span class="wppb-form-error">Bot</span>';
29
  }
30
+ return $errors;
31
  }
32
  }
src/lib/src/Modules/LoginGuard/Lib/AntiBot/FormProviders/UltimateMember.php CHANGED
@@ -32,8 +32,8 @@ class UltimateMember extends BaseFormProvider {
32
  $this->setActionToAudit( 'ultimatemember-login' )
33
  ->checkProviders();
34
  }
35
- catch ( \Exception $oE ) {
36
- \UM()->form()->add_error( 'shield-fail-login', $oE->getMessage() );
37
  }
38
  }
39
 
@@ -42,8 +42,8 @@ class UltimateMember extends BaseFormProvider {
42
  $this->setActionToAudit( 'ultimatemember-lostpassword' )
43
  ->checkProviders();
44
  }
45
- catch ( \Exception $oE ) {
46
- \UM()->form()->add_error( 'shield-fail-lostpassword', $oE->getMessage() );
47
  }
48
  }
49
 
@@ -52,8 +52,8 @@ class UltimateMember extends BaseFormProvider {
52
  $this->setActionToAudit( 'ultimatemember-register' )
53
  ->checkProviders();
54
  }
55
- catch ( \Exception $oE ) {
56
- \UM()->form()->add_error( 'shield-fail-register', $oE->getMessage() );
57
  }
58
  }
59
  }
32
  $this->setActionToAudit( 'ultimatemember-login' )
33
  ->checkProviders();
34
  }
35
+ catch ( \Exception $e ) {
36
+ \UM()->form()->add_error( 'shield-fail-login', $e->getMessage() );
37
  }
38
  }
39
 
42
  $this->setActionToAudit( 'ultimatemember-lostpassword' )
43
  ->checkProviders();
44
  }
45
+ catch ( \Exception $e ) {
46
+ \UM()->form()->add_error( 'shield-fail-lostpassword', $e->getMessage() );
47
  }
48
  }
49
 
52
  $this->setActionToAudit( 'ultimatemember-register' )
53
  ->checkProviders();
54
  }
55
+ catch ( \Exception $e ) {
56
+ \UM()->form()->add_error( 'shield-fail-register', $e->getMessage() );
57
  }
58
  }
59
  }
src/lib/src/Modules/LoginGuard/Lib/AntiBot/FormProviders/UserRegistration.php CHANGED
@@ -25,19 +25,19 @@ class UserRegistration extends BaseFormProvider {
25
  }
26
 
27
  /**
28
- * @param array $aResponse
29
  * @param array $aFormData
30
  * @param int $nFormID
31
  * @return mixed
32
  */
33
- public function checkRegister( $aResponse, $aFormData, $nFormID ) {
34
  try {
35
  $this->setActionToAudit( 'userregistration-register' )
36
  ->checkProviders();
37
  }
38
- catch ( \Exception $oE ) {
39
- $aResponse[] = $oE->getMessage();
40
  }
41
- return $aResponse;
42
  }
43
  }
25
  }
26
 
27
  /**
28
+ * @param array $response
29
  * @param array $aFormData
30
  * @param int $nFormID
31
  * @return mixed
32
  */
33
+ public function checkRegister( $response, $aFormData, $nFormID ) {
34
  try {
35
  $this->setActionToAudit( 'userregistration-register' )
36
  ->checkProviders();
37
  }
38
+ catch ( \Exception $e ) {
39
+ $response[] = $e->getMessage();
40
  }
41
+ return $response;
42
  }
43
  }
src/lib/src/Modules/LoginGuard/Lib/AntiBot/FormProviders/WPMembers.php CHANGED
@@ -28,7 +28,7 @@ class WPMembers extends BaseFormProvider {
28
  $this->setActionToAudit( 'wpmembers-lostpassword' )
29
  ->checkProviders();
30
  }
31
- catch ( \Exception $oE ) {
32
  $args[ 'user' ] = null;
33
  $args[ 'email' ] = null;
34
  }
@@ -43,9 +43,9 @@ class WPMembers extends BaseFormProvider {
43
  $this->setActionToAudit( 'wpmembers-register' )
44
  ->checkProviders();
45
  }
46
- catch ( \Exception $oE ) {
47
  global $wpmem_themsg;
48
- $wpmem_themsg = $oE->getMessage();
49
  }
50
  }
51
  }
28
  $this->setActionToAudit( 'wpmembers-lostpassword' )
29
  ->checkProviders();
30
  }
31
+ catch ( \Exception $e ) {
32
  $args[ 'user' ] = null;
33
  $args[ 'email' ] = null;
34
  }
43
  $this->setActionToAudit( 'wpmembers-register' )
44
  ->checkProviders();
45
  }
46
+ catch ( \Exception $e ) {
47
  global $wpmem_themsg;
48
+ $wpmem_themsg = $e->getMessage();
49
  }
50
  }
51
  }
src/lib/src/Modules/LoginGuard/Lib/AntiBot/FormProviders/WooCommerce.php CHANGED
@@ -98,9 +98,9 @@ class WooCommerce extends BaseFormProvider {
98
  ->checkProviders();
99
  }
100
  }
101
- catch ( \Exception $oE ) {
102
  $oUserOrError = $this->giveMeWpError( $oUserOrError );
103
- $oUserOrError->add( $this->getCon()->prefix( rand() ), $oE->getMessage() );
104
  }
105
  return $oUserOrError;
106
  }
@@ -116,9 +116,9 @@ class WooCommerce extends BaseFormProvider {
116
  ->setActionToAudit( 'woo-register' )
117
  ->checkProviders();
118
  }
119
- catch ( \Exception $oE ) {
120
  $oWpError = $this->giveMeWpError( $oWpError );
121
- $oWpError->add( $this->getCon()->prefix( rand() ), $oE->getMessage() );
122
  }
123
  return $oWpError;
124
  }
@@ -134,9 +134,9 @@ class WooCommerce extends BaseFormProvider {
134
  $this->setActionToAudit( 'woo-checkout' )
135
  ->checkProviders();
136
  }
137
- catch ( \Exception $oE ) {
138
  $oWpError = $this->giveMeWpError( $oWpError );
139
- $oWpError->add( $this->getCon()->prefix( rand() ), $oE->getMessage() );
140
  }
141
  return $oWpError;
142
  }
98
  ->checkProviders();
99
  }
100
  }
101
+ catch ( \Exception $e ) {
102
  $oUserOrError = $this->giveMeWpError( $oUserOrError );
103
+ $oUserOrError->add( $this->getCon()->prefix( rand() ), $e->getMessage() );
104
  }
105
  return $oUserOrError;
106
  }
116
  ->setActionToAudit( 'woo-register' )
117
  ->checkProviders();
118
  }
119
+ catch ( \Exception $e ) {
120
  $oWpError = $this->giveMeWpError( $oWpError );
121
+ $oWpError->add( $this->getCon()->prefix( rand() ), $e->getMessage() );
122
  }
123
  return $oWpError;
124
  }
134
  $this->setActionToAudit( 'woo-checkout' )
135
  ->checkProviders();
136
  }
137
+ catch ( \Exception $e ) {
138
  $oWpError = $this->giveMeWpError( $oWpError );
139
+ $oWpError->add( $this->getCon()->prefix( rand() ), $e->getMessage() );
140
  }
141
  return $oWpError;
142
  }
src/lib/src/Modules/LoginGuard/Lib/AntiBot/FormProviders/WordPress.php CHANGED
@@ -40,9 +40,9 @@ class WordPress extends BaseFormProvider {
40
  ->checkProviders();
41
  }
42
  }
43
- catch ( \Exception $oE ) {
44
  $oUserOrError = $this->giveMeWpError( $oUserOrError );
45
- $oUserOrError->add( $this->getCon()->prefix( rand() ), $oE->getMessage() );
46
  }
47
  return $oUserOrError;
48
  }
@@ -57,9 +57,9 @@ class WordPress extends BaseFormProvider {
57
  ->setActionToAudit( 'reset-password' )
58
  ->checkProviders();
59
  }
60
- catch ( \Exception $oE ) {
61
  $oWpError = $this->giveMeWpError( $oWpError );
62
- $oWpError->add( $this->getCon()->prefix( rand() ), $oE->getMessage() );
63
  }
64
  return $oWpError;
65
  }
@@ -75,9 +75,9 @@ class WordPress extends BaseFormProvider {
75
  ->setActionToAudit( 'register' )
76
  ->checkProviders();
77
  }
78
- catch ( \Exception $oE ) {
79
  $oWpError = $this->giveMeWpError( $oWpError );
80
- $oWpError->add( $this->getCon()->prefix( rand() ), $oE->getMessage() );
81
  }
82
  return $oWpError;
83
  }
40
  ->checkProviders();
41
  }
42
  }
43
+ catch ( \Exception $e ) {
44
  $oUserOrError = $this->giveMeWpError( $oUserOrError );
45
+ $oUserOrError->add( $this->getCon()->prefix( rand() ), $e->getMessage() );
46
  }
47
  return $oUserOrError;
48
  }
57
  ->setActionToAudit( 'reset-password' )
58
  ->checkProviders();
59
  }
60
+ catch ( \Exception $e ) {
61
  $oWpError = $this->giveMeWpError( $oWpError );
62
+ $oWpError->add( $this->getCon()->prefix( rand() ), $e->getMessage() );
63
  }
64
  return $oWpError;
65
  }
75
  ->setActionToAudit( 'register' )
76
  ->checkProviders();
77
  }
78
+ catch ( \Exception $e ) {
79
  $oWpError = $this->giveMeWpError( $oWpError );
80
+ $oWpError->add( $this->getCon()->prefix( rand() ), $e->getMessage() );
81
  }
82
  return $oWpError;
83
  }
src/lib/src/Modules/LoginGuard/Lib/AntiBot/ProtectionProviders/GaspJs.php CHANGED
@@ -76,18 +76,18 @@ class GaspJs extends BaseProtectionProvider {
76
  /** @var LoginGuard\Options $opts */
77
  $opts = $this->getOptions();
78
 
79
- $sAsset = 'shield-antibot';
80
- $sUnique = $con->prefix( $sAsset );
81
  wp_register_script(
82
- $sUnique,
83
- $con->getPluginUrl_Js( $sAsset ),
84
  [ 'jquery' ],
85
  $con->getVersion()
86
  );
87
- wp_enqueue_script( $sUnique );
88
 
89
  wp_localize_script(
90
- $sUnique,
91
  'icwp_wpsf_vars_lpantibot',
92
  [
93
  'form_selectors' => implode( ',', $opts->getAntiBotFormSelectors() ),
76
  /** @var LoginGuard\Options $opts */
77
  $opts = $this->getOptions();
78
 
79
+ $asset = 'shield-antibot';
80
+ $uniq = $con->prefix( $asset );
81
  wp_register_script(
82
+ $uniq,
83
+ $con->getPluginUrl_Js( $asset ),
84
  [ 'jquery' ],
85
  $con->getVersion()
86
  );
87
+ wp_enqueue_script( $uniq );
88
 
89
  wp_localize_script(
90
+ $uniq,
91
  'icwp_wpsf_vars_lpantibot',
92
  [
93
  'form_selectors' => implode( ',', $opts->getAntiBotFormSelectors() ),
src/lib/src/Modules/LoginGuard/Lib/AntiBot/ProtectionProviders/GoogleRecaptcha.php CHANGED
@@ -26,9 +26,9 @@ class GoogleRecaptcha extends BaseProtectionProvider {
26
  ->setMod( $this->getMod() )
27
  ->test();
28
  }
29
- catch ( \Exception $oE ) {
30
  $this->processFailure();
31
- throw $oE;
32
  }
33
  }
34
  }
26
  ->setMod( $this->getMod() )
27
  ->test();
28
  }
29
+ catch ( \Exception $e ) {
30
  $this->processFailure();
31
+ throw $e;
32
  }
33
  }
34
  }
src/lib/src/Modules/LoginGuard/Lib/TwoFactor/LoginIntentPage.php CHANGED
@@ -119,7 +119,7 @@ class LoginIntentPage {
119
  $req = Services::Request();
120
 
121
  $aLabels = $con->getLabels();
122
- $sBannerUrl = empty( $aLabels[ 'url_login2fa_logourl' ] ) ? $con->getPluginUrl_Image( 'pluginlogo_banner-772x250.png' ) : $aLabels[ 'url_login2fa_logourl' ];
123
  $nTimeRemaining = $mod->getSession()->login_intent_expires_at - $req->ts();
124
  $aDisplayData = [
125
  'strings' => [
@@ -130,14 +130,14 @@ class LoginIntentPage {
130
  'time_remaining' => $nTimeRemaining,
131
  ],
132
  'hrefs' => [
133
- 'css_bootstrap' => $con->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
@@ -154,10 +154,10 @@ class LoginIntentPage {
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
  ];
119
  $req = Services::Request();
120
 
121
  $aLabels = $con->getLabels();
122
+ $sBannerUrl = empty( $aLabels[ 'url_login2fa_logourl' ] ) ? $con->urls->forImage( 'pluginlogo_banner-772x250.png' ) : $aLabels[ 'url_login2fa_logourl' ];
123
  $nTimeRemaining = $mod->getSession()->login_intent_expires_at - $req->ts();
124
  $aDisplayData = [
125
  'strings' => [
130
  'time_remaining' => $nTimeRemaining,
131
  ],
132
  'hrefs' => [
133
+ 'css_bootstrap' => $con->urls->forCss( 'bootstrap4.min' ),
134
+ 'js_bootstrap' => $con->urls->forJs( 'bootstrap4.bundle.min' ),
135
  'shield_logo' => 'https://ps.w.org/wp-simple-firewall/assets/banner-772x250.png',
136
+ 'what_is_this' => 'https://support.getshieldsecurity.com/support/solutions/articles/3000064840',
137
  ],
138
  'imgs' => [
139
  'banner' => $sBannerUrl,
140
+ 'favicon' => $con->urls->forImage( 'pluginlogo_24x24.png' ),
141
  ],
142
  'flags' => [
143
  'show_branded_links' => !$con->getModule_SecAdmin()->isWlEnabled(), // white label mitigation
154
  $aDisplayData[ 'head' ] = [
155
  'scripts' => [
156
  [
157
+ 'src' => $con->urls->forJs( 'u2f-bundle.js' ),
158
  ],
159
  [
160
+ 'src' => $con->urls->forJs( 'u2f-frontend.js' ),
161
  ]
162
  ]
163
  ];
src/lib/src/Modules/LoginGuard/Lib/TwoFactor/MfaController.php CHANGED
@@ -261,12 +261,4 @@ class MfaController {
261
  private function getLoginIntentRequestFlag() :string {
262
  return $this->getCon()->prefix( 'login-intent-request' );
263
  }
264
-
265
- /**
266
- * @return bool
267
- * @deprecated 10.1
268
- */
269
- private function hasLoginIntent() :bool {
270
- return $this->getLoginIntentExpiresAt() > 0;
271
- }
272
  }
261
  private function getLoginIntentRequestFlag() :string {
262
  return $this->getCon()->prefix( 'login-intent-request' );
263
  }
 
 
 
 
 
 
 
 
264
  }
src/lib/src/Modules/LoginGuard/Lib/TwoFactor/Provider/Backup.php CHANGED
@@ -80,12 +80,6 @@ class Backup extends BaseProvider {
80
  return $this;
81
  }
82
 
83
- /**
84
- * Backup Code are 1-time only and if you have MFA, then we need to remove all the other tracking factors
85
- * @param \WP_User $user
86
- * @param string $otp
87
- * @return bool
88
- */
89
  protected function processOtp( \WP_User $user, string $otp ) :bool {
90
  return $this->validateBackupCode( $user, $otp );
91
  }
80
  return $this;
81
  }
82
 
 
 
 
 
 
 
83
  protected function processOtp( \WP_User $user, string $otp ) :bool {
84
  return $this->validateBackupCode( $user, $otp );
85
  }
src/lib/src/Modules/LoginGuard/Lib/TwoFactor/Provider/BaseProvider.php CHANGED
@@ -66,27 +66,15 @@ abstract class BaseProvider {
66
  return $this->isSecretValid( $this->getSecret( $user ) );
67
  }
68
 
69
- /**
70
- * @param \WP_User $user
71
- * @return bool
72
- */
73
- protected function isEnforced( \WP_User $user ) {
74
  return false;
75
  }
76
 
77
- /**
78
- * @param \WP_User $user
79
- * @return bool
80
- */
81
- public function isProfileActive( \WP_User $user ) {
82
  return $this->hasValidSecret( $user );
83
  }
84
 
85
- /**
86
- * @param \WP_User $user
87
- * @return bool
88
- */
89
- public function isProviderAvailableToUser( \WP_User $user ) {
90
  return $this->isProviderEnabled();
91
  }
92
 
@@ -150,11 +138,6 @@ abstract class BaseProvider {
150
  return '';
151
  }
152
 
153
- /**
154
- * @param \WP_User $user
155
- * @param string $otp
156
- * @return bool
157
- */
158
  abstract protected function processOtp( \WP_User $user, string $otp ) :bool;
159
 
160
  /**
@@ -194,7 +177,7 @@ abstract class BaseProvider {
194
  /**
195
  * @param \WP_User $user
196
  */
197
- protected function processRemovalFromAccount( $user ) {
198
  }
199
 
200
  /**
66
  return $this->isSecretValid( $this->getSecret( $user ) );
67
  }
68
 
69
+ protected function isEnforced( \WP_User $user ) :bool {
 
 
 
 
70
  return false;
71
  }
72
 
73
+ public function isProfileActive( \WP_User $user ) :bool {
 
 
 
 
74
  return $this->hasValidSecret( $user );
75
  }
76
 
77
+ public function isProviderAvailableToUser( \WP_User $user ) :bool {
 
 
 
 
78
  return $this->isProviderEnabled();
79
  }
80
 
138
  return '';
139
  }
140
 
 
 
 
 
 
141
  abstract protected function processOtp( \WP_User $user, string $otp ) :bool;
142
 
143
  /**
177
  /**
178
  * @param \WP_User $user
179
  */
180
+ protected function processRemovalFromAccount( \WP_User $user ) {
181
  }
182
 
183
  /**
src/lib/src/Modules/LoginGuard/Lib/TwoFactor/Provider/Email.php CHANGED
@@ -45,11 +45,6 @@ class Email extends BaseProvider {
45
  return $this;
46
  }
47
 
48
- /**
49
- * @param \WP_User $user
50
- * @param string $otp
51
- * @return bool
52
- */
53
  protected function processOtp( \WP_User $user, string $otp ) :bool {
54
  $valid = false;
55
  foreach ( $this->getAllCodes( $user ) as $secret => $expiresAt ) {
@@ -106,11 +101,7 @@ class Email extends BaseProvider {
106
  }
107
  }
108
 
109
- /**
110
- * @param \WP_User $user
111
- * @return bool
112
- */
113
- public function isProfileActive( \WP_User $user ) {
114
  /** @var LoginGuard\Options $opts */
115
  $opts = $this->getOptions();
116
  return parent::isProfileActive( $user ) &&
@@ -118,11 +109,7 @@ class Email extends BaseProvider {
118
  ( $this->hasValidatedProfile( $user ) && $opts->isEnabledEmailAuthAnyUserSet() ) );
119
  }
120
 
121
- /**
122
- * @param \WP_User $user
123
- * @return bool
124
- */
125
- protected function isEnforced( \WP_User $user ) {
126
  /** @var LoginGuard\Options $opts */
127
  $opts = $this->getOptions();
128
  return count( array_intersect( $opts->getEmail2FaRoles(), $user->roles ) ) > 0;
@@ -229,11 +216,7 @@ class Email extends BaseProvider {
229
  return $opts->isEmailAuthenticationActive();
230
  }
231
 
232
- /**
233
- * @param \WP_User $user
234
- * @return bool
235
- */
236
- public function isProviderAvailableToUser( \WP_User $user ) {
237
  /** @var LoginGuard\Options $opts */
238
  $opts = $this->getOptions();
239
  return parent::isProviderAvailableToUser( $user )
45
  return $this;
46
  }
47
 
 
 
 
 
 
48
  protected function processOtp( \WP_User $user, string $otp ) :bool {
49
  $valid = false;
50
  foreach ( $this->getAllCodes( $user ) as $secret => $expiresAt ) {
101
  }
102
  }
103
 
104
+ public function isProfileActive( \WP_User $user ) :bool {
 
 
 
 
105
  /** @var LoginGuard\Options $opts */
106
  $opts = $this->getOptions();
107
  return parent::isProfileActive( $user ) &&
109
  ( $this->hasValidatedProfile( $user ) && $opts->isEnabledEmailAuthAnyUserSet() ) );
110
  }
111
 
112
+ protected function isEnforced( \WP_User $user ) :bool {
 
 
 
 
113
  /** @var LoginGuard\Options $opts */
114
  $opts = $this->getOptions();
115
  return count( array_intersect( $opts->getEmail2FaRoles(), $user->roles ) ) > 0;
216
  return $opts->isEmailAuthenticationActive();
217
  }
218
 
219
+ public function isProviderAvailableToUser( \WP_User $user ) :bool {
 
 
 
 
220
  /** @var LoginGuard\Options $opts */
221
  $opts = $this->getOptions();
222
  return parent::isProviderAvailableToUser( $user )
src/lib/src/Modules/LoginGuard/Lib/TwoFactor/Provider/GoogleAuth.php CHANGED
@@ -15,11 +15,7 @@ class GoogleAuth extends BaseProvider {
15
  */
16
  private $oWorkingSecret;
17
 
18
- /**
19
- * @param \WP_User $user
20
- * @return bool
21
- */
22
- public function isProfileActive( \WP_User $user ) {
23
  return parent::isProfileActive( $user ) && $this->hasValidatedProfile( $user );
24
  }
25
 
@@ -99,7 +95,7 @@ class GoogleAuth extends BaseProvider {
99
  * @param \WP_User $user
100
  * @return $this
101
  */
102
- protected function processRemovalFromAccount( $user ) {
103
  $this->setProfileValidated( $user, false )
104
  ->resetSecret( $user );
105
  return $this;
@@ -155,11 +151,6 @@ class GoogleAuth extends BaseProvider {
155
  ];
156
  }
157
 
158
- /**
159
- * @param \WP_User $user
160
- * @param string $otp
161
- * @return bool
162
- */
163
  protected function processOtp( \WP_User $user, string $otp ) :bool {
164
  return $this->validateGaCode( $user, $otp );
165
  }
@@ -176,9 +167,9 @@ class GoogleAuth extends BaseProvider {
176
  $valid = (bool)( new GoogleAuthenticator\GoogleAuthenticator() )
177
  ->authenticate( $this->getSecret( $user ), $otp );
178
  }
179
- catch ( \Exception $oE ) {
180
  }
181
- catch ( \Psr\Cache\CacheException $oE ) {
182
  }
183
  }
184
  return $valid;
@@ -208,7 +199,7 @@ class GoogleAuth extends BaseProvider {
208
  try {
209
  return $this->getGaSecret( $user )->getSecretKey();
210
  }
211
- catch ( \InvalidArgumentException $oE ) {
212
  return '';
213
  }
214
  }
15
  */
16
  private $oWorkingSecret;
17
 
18
+ public function isProfileActive( \WP_User $user ) :bool {
 
 
 
 
19
  return parent::isProfileActive( $user ) && $this->hasValidatedProfile( $user );
20
  }
21
 
95
  * @param \WP_User $user
96
  * @return $this
97
  */
98
+ protected function processRemovalFromAccount( \WP_User $user ) {
99
  $this->setProfileValidated( $user, false )
100
  ->resetSecret( $user );
101
  return $this;
151
  ];
152
  }
153
 
 
 
 
 
 
154
  protected function processOtp( \WP_User $user, string $otp ) :bool {
155
  return $this->validateGaCode( $user, $otp );
156
  }
167
  $valid = (bool)( new GoogleAuthenticator\GoogleAuthenticator() )
168
  ->authenticate( $this->getSecret( $user ), $otp );
169
  }
170
+ catch ( \Exception $e ) {
171
  }
172
+ catch ( \Psr\Cache\CacheException $e ) {
173
  }
174
  }
175
  return $valid;
199
  try {
200
  return $this->getGaSecret( $user )->getSecretKey();
201
  }
202
+ catch ( \InvalidArgumentException $e ) {
203
  return '';
204
  }
205
  }
src/lib/src/Modules/LoginGuard/Lib/TwoFactor/Provider/U2F.php CHANGED
@@ -2,6 +2,7 @@
2
 
3
  namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\LoginGuard\Lib\TwoFactor\Provider;
4
 
 
5
  use FernleafSystems\Wordpress\Plugin\Shield\Modules\LoginGuard;
6
  use FernleafSystems\Wordpress\Services\Services;
7
  use u2flib_server\RegisterRequest;
@@ -12,64 +13,50 @@ class U2F extends BaseProvider {
12
  const SLUG = 'u2f';
13
  const DEFAULT_SECRET = '[]';
14
 
15
- /**
16
- * @param \WP_User $user
17
- * @return bool
18
- */
19
- public function isProfileActive( \WP_User $user ) {
20
  return parent::isProfileActive( $user ) && $this->hasValidatedProfile( $user );
21
  }
22
 
23
  public function setupProfile() {
24
- add_action( 'admin_enqueue_scripts', function ( $sHook ) {
25
- if ( in_array( $sHook, [ 'profile.php', ] ) ) {
26
- $this->enqueueAdminU2f();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
27
  }
28
- } );
29
- }
30
-
31
- private function enqueueAdminU2f() {
32
- $aDeps = [];
33
- foreach ( [ 'u2f-bundle', 'shield-u2f-admin' ] as $sScript ) {
34
- wp_enqueue_script(
35
- $this->getCon()->prefix( $sScript ),
36
- $this->getCon()->getPluginUrl_Js( $sScript ),
37
- $aDeps
38
- );
39
- $aDeps[] = $this->getCon()->prefix( $sScript );
40
- }
41
 
42
- $user = Services::WpUsers()->getCurrentWpUser();
43
- try {
44
- list( $oReg, $aSigns ) = $this->createNewU2fRegistrationRequest( $user );
45
- wp_localize_script(
46
- $this->getCon()->prefix( 'shield-u2f-admin' ),
47
- 'icwp_wpsf_vars_u2f',
48
- [
49
- 'reg_request' => $oReg,
50
- 'signs' => $aSigns,
51
- 'ajax' => [
52
- 'u2f_remove' => $this->getMod()->getAjaxActionData( 'u2f_remove' )
53
- ],
54
- 'flags' => [
55
- 'has_validated' => $this->hasValidatedProfile( $user )
56
- ],
57
- 'strings' => [
58
- 'not_supported' => __( 'U2F Security Key registration is not supported in this browser', 'wp-simple-firewall' ),
59
- 'failed' => __( 'Key registration failed.', 'wp-simple-firewall' )
60
- .' '.__( "Perhaps the device isn't supported, or you've already registered it.", 'wp-simple-firewall' )
61
- .' '.__( 'Please retry or refresh the page.', 'wp-simple-firewall' ),
62
- 'do_save' => __( 'Key registration was successful.', 'wp-simple-firewall' )
63
- .' '.__( 'Please now save your profile settings.', 'wp-simple-firewall' ),
64
- 'prompt_dialog' => __( 'Please provide a label to identify the new U2F device.', 'wp-simple-firewall' ),
65
- 'err_no_label' => __( 'Device registration may not proceed without a unique label.', 'wp-simple-firewall' ),
66
- 'err_invalid_label' => __( 'Device label must contain letters, numbers, underscore, or hypen, and be no more than 16 characters.', 'wp-simple-firewall' ),
67
- ]
68
- ]
69
- );
70
- }
71
- catch ( \Exception $oE ) {
72
- }
73
  }
74
 
75
  /**
@@ -102,7 +89,7 @@ class U2F extends BaseProvider {
102
  ]
103
  ];
104
  }
105
- catch ( \Exception $oE ) {
106
  }
107
 
108
  return $aFieldData;
@@ -196,8 +183,8 @@ class U2F extends BaseProvider {
196
  * @inheritDoc
197
  */
198
  public function handleUserProfileSubmit( \WP_User $user ) {
199
- $bError = false;
200
- $sMsg = null;
201
 
202
  $sU2fResponse = Services::Request()->post( 'icwp_wpsf_new_u2f_response' );
203
  if ( !empty( $sU2fResponse ) ) {
@@ -225,42 +212,24 @@ class U2F extends BaseProvider {
225
  $this->addRegistration( $user, $aConfirmedReg )
226
  ->setProfileValidated( $user );
227
 
228
- $sMsg = __( 'U2F Device was successfully registered on your profile.', 'wp-simple-firewall' );
229
  }
230
- catch ( \Exception $oE ) {
231
- $bError = true;
232
- $sMsg = sprintf( __( 'U2F Device registration failed with the following error: %s', 'wp-simple-firewall' ),
233
- $oE->getMessage() );
234
  }
235
  }
236
- elseif ( Services::Request()->post( 'wpsf_u2f_key_delete' ) === 'Y' ) {
237
- $this->processRemovalFromAccount( $user );
238
- $sMsg = __( 'U2F Device was removed from your profile.', 'wp-simple-firewall' );
239
- }
240
 
241
- if ( !empty( $sMsg ) ) {
242
- $this->getMod()->setFlashAdminNotice( $sMsg, $bError );
243
  }
244
  }
245
 
246
- /**
247
- * @param \WP_User $user
248
- * @param string $otp
249
- * @return bool
250
- */
251
  protected function processOtp( \WP_User $user, string $otp ) :bool {
252
  return $this->validateU2F( $user, $otp );
253
  }
254
 
255
- /**
256
- * @param \WP_User $user
257
- * @return $this
258
- */
259
- protected function processRemovalFromAccount( $user ) {
260
- return $this->setProfileValidated( $user, false )
261
- ->deleteSecret( $user );
262
- }
263
-
264
  /**
265
  * @param \WP_User $user
266
  * @param array $aReg
@@ -314,10 +283,10 @@ class U2F extends BaseProvider {
314
  * @return $this
315
  */
316
  public function removeRegisteredU2fId( \WP_User $user, $sU2fID ) {
317
- $aRegs = $this->getRegistrations( $user );
318
- if ( isset( $aRegs[ $sU2fID ] ) ) {
319
- unset( $aRegs[ $sU2fID ] );
320
- $this->storeRegistrations( $user, $aRegs );
321
  }
322
  return $this;
323
  }
@@ -334,8 +303,8 @@ class U2F extends BaseProvider {
334
  $aReg[ 'used_at' ] = Services::Request()->ts();
335
  $this->addRegistration( $user, $aReg );
336
  }
337
- catch ( \Exception $oE ) {
338
- error_log( $oE->getMessage() );
339
  }
340
 
341
  return !empty( $oRegistration );
2
 
3
  namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\LoginGuard\Lib\TwoFactor\Provider;
4
 
5
+ use FernleafSystems\Wordpress\Plugin\Shield\Controller\Assets\Enqueue;
6
  use FernleafSystems\Wordpress\Plugin\Shield\Modules\LoginGuard;
7
  use FernleafSystems\Wordpress\Services\Services;
8
  use u2flib_server\RegisterRequest;
13
  const SLUG = 'u2f';
14
  const DEFAULT_SECRET = '[]';
15
 
16
+ public function isProfileActive( \WP_User $user ) :bool {
 
 
 
 
17
  return parent::isProfileActive( $user ) && $this->hasValidatedProfile( $user );
18
  }
19
 
20
  public function setupProfile() {
21
+ add_filter( 'shield/custom_enqueues', function ( array $enqueues, $hook ) {
22
+ if ( in_array( $hook, [ 'profile.php', ] ) ) {
23
+
24
+ $enqueues[ Enqueue::JS ][] = 'shield/u2f-admin';
25
+
26
+ add_filter( 'shield/custom_localisations', function ( array $localz ) {
27
+ $user = Services::WpUsers()->getCurrentWpUser();
28
+ list( $reg, $signs ) = $this->createNewU2fRegistrationRequest( $user );
29
+ $localz[] = [
30
+ 'shield/u2f-admin',
31
+ 'icwp_wpsf_vars_u2f',
32
+ [
33
+ 'reg_request' => $reg,
34
+ 'signs' => $signs,
35
+ 'ajax' => [
36
+ 'u2f_remove' => $this->getMod()->getAjaxActionData( 'u2f_remove' )
37
+ ],
38
+ 'flags' => [
39
+ 'has_validated' => $this->hasValidatedProfile( $user )
40
+ ],
41
+ 'strings' => [
42
+ 'not_supported' => __( 'U2F Security Key registration is not supported in this browser', 'wp-simple-firewall' ),
43
+ 'failed' => __( 'Key registration failed.', 'wp-simple-firewall' )
44
+ .' '.__( "Perhaps the device isn't supported, or you've already registered it.", 'wp-simple-firewall' )
45
+ .' '.__( 'Please retry or refresh the page.', 'wp-simple-firewall' ),
46
+ 'do_save' => __( 'Key registration was successful.', 'wp-simple-firewall' )
47
+ .' '.__( 'Please now save your profile settings.', 'wp-simple-firewall' ),
48
+ 'prompt_dialog' => __( 'Please provide a label to identify the new U2F device.', 'wp-simple-firewall' ),
49
+ 'err_no_label' => __( 'Device registration may not proceed without a unique label.', 'wp-simple-firewall' ),
50
+ 'err_invalid_label' => __( 'Device label must contain letters, numbers, underscore, or hypen, and be no more than 16 characters.', 'wp-simple-firewall' ),
51
+ ]
52
+ ]
53
+ ];
54
+ return $localz;
55
+ } );
56
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
57
 
58
+ return $enqueues;
59
+ }, 10, 2 );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
60
  }
61
 
62
  /**
89
  ]
90
  ];
91
  }
92
+ catch ( \Exception $e ) {
93
  }
94
 
95
  return $aFieldData;
183
  * @inheritDoc
184
  */
185
  public function handleUserProfileSubmit( \WP_User $user ) {
186
+ $isError = false;
187
+ $msg = null;
188
 
189
  $sU2fResponse = Services::Request()->post( 'icwp_wpsf_new_u2f_response' );
190
  if ( !empty( $sU2fResponse ) ) {
212
  $this->addRegistration( $user, $aConfirmedReg )
213
  ->setProfileValidated( $user );
214
 
215
+ $msg = __( 'U2F Device was successfully registered on your profile.', 'wp-simple-firewall' );
216
  }
217
+ catch ( \Exception $e ) {
218
+ $isError = true;
219
+ $msg = sprintf( __( 'U2F Device registration failed with the following error: %s', 'wp-simple-firewall' ),
220
+ $e->getMessage() );
221
  }
222
  }
 
 
 
 
223
 
224
+ if ( !empty( $msg ) ) {
225
+ $this->getMod()->setFlashAdminNotice( $msg, $isError );
226
  }
227
  }
228
 
 
 
 
 
 
229
  protected function processOtp( \WP_User $user, string $otp ) :bool {
230
  return $this->validateU2F( $user, $otp );
231
  }
232
 
 
 
 
 
 
 
 
 
 
233
  /**
234
  * @param \WP_User $user
235
  * @param array $aReg
283
  * @return $this
284
  */
285
  public function removeRegisteredU2fId( \WP_User $user, $sU2fID ) {
286
+ $regs = $this->getRegistrations( $user );
287
+ if ( isset( $regs[ $sU2fID ] ) ) {
288
+ unset( $regs[ $sU2fID ] );
289
+ $this->storeRegistrations( $user, $regs );
290
  }
291
  return $this;
292
  }
303
  $aReg[ 'used_at' ] = Services::Request()->ts();
304
  $this->addRegistration( $user, $aReg );
305
  }
306
+ catch ( \Exception $e ) {
307
+ error_log( $e->getMessage() );
308
  }
309
 
310
  return !empty( $oRegistration );
src/lib/src/Modules/LoginGuard/Lib/TwoFactor/Provider/Yubikey.php CHANGED
@@ -2,6 +2,7 @@
2
 
3
  namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\LoginGuard\Lib\TwoFactor\Provider;
4
 
 
5
  use FernleafSystems\Wordpress\Plugin\Shield\Modules\LoginGuard;
6
  use FernleafSystems\Wordpress\Services\Services;
7
 
@@ -12,29 +13,22 @@ class Yubikey extends BaseProvider {
12
  const URL_YUBIKEY_VERIFY = 'https://api.yubico.com/wsapi/2.0/verify';
13
 
14
  public function setupProfile() {
15
- add_action( 'admin_enqueue_scripts', function ( $sHook ) {
16
- if ( in_array( $sHook, [ 'profile.php', ] ) ) {
17
- $this->enqueueYubikeyJS();
18
- }
19
- } );
20
- }
21
 
22
- /**
23
- * Enqueue the Javascript for removing Yubikey
24
- */
25
- private function enqueueYubikeyJS() {
26
- $oCon = $this->getCon();
27
- $sScript = 'shield-userprofile';
28
- wp_enqueue_script(
29
- $oCon->prefix( $sScript ),
30
- $oCon->getPluginUrl_Js( $sScript ),
31
- [ 'jquery', $oCon->prefix( 'global-plugin' ) ]
32
- );
33
- wp_localize_script(
34
- $oCon->prefix( $sScript ),
35
- 'icwp_wpsf_vars_profileyubikey',
36
- [ 'yubikey_remove' => $this->getMod()->getAjaxActionData( 'yubikey_remove' ) ]
37
- );
38
  }
39
 
40
  public function renderUserProfileOptions( \WP_User $user ) :string {
@@ -138,11 +132,6 @@ class Yubikey extends BaseProvider {
138
  return array_filter( array_map( 'trim', explode( ',', $this->getSecret( $user ) ) ) );
139
  }
140
 
141
- /**
142
- * @param \WP_User $user
143
- * @param string $otp
144
- * @return bool
145
- */
146
  protected function processOtp( \WP_User $user, string $otp ) :bool {
147
  $valid = false;
148
 
2
 
3
  namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\LoginGuard\Lib\TwoFactor\Provider;
4
 
5
+ use FernleafSystems\Wordpress\Plugin\Shield\Controller\Assets\Enqueue;
6
  use FernleafSystems\Wordpress\Plugin\Shield\Modules\LoginGuard;
7
  use FernleafSystems\Wordpress\Services\Services;
8
 
13
  const URL_YUBIKEY_VERIFY = 'https://api.yubico.com/wsapi/2.0/verify';
14
 
15
  public function setupProfile() {
 
 
 
 
 
 
16
 
17
+ add_filter( 'shield/custom_enqueues', function ( array $enqueues, $hook ) {
18
+ if ( in_array( $hook, [ 'profile.php', ] ) ) {
19
+ $enqueues[ Enqueue::JS ][] = 'shield/userprofile';
20
+
21
+ add_filter( 'shield/custom_localisations', function ( array $localz ) {
22
+ $localz[] = [
23
+ 'shield/userprofile',
24
+ 'icwp_wpsf_vars_profileyubikey',
25
+ [ 'yubikey_remove' => $this->getMod()->getAjaxActionData( 'yubikey_remove' ) ]
26
+ ];
27
+ return $localz;
28
+ } );
29
+ }
30
+ return $enqueues;
31
+ }, 10, 2 );
 
32
  }
33
 
34
  public function renderUserProfileOptions( \WP_User $user ) :string {
132
  return array_filter( array_map( 'trim', explode( ',', $this->getSecret( $user ) ) ) );
133
  }
134
 
 
 
 
 
 
135
  protected function processOtp( \WP_User $user, string $otp ) :bool {
136
  $valid = false;
137
 
src/lib/src/Modules/LoginGuard/ModCon.php CHANGED
@@ -2,6 +2,7 @@
2
 
3
  namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\LoginGuard;
4
 
 
5
  use FernleafSystems\Wordpress\Plugin\Shield\Modules\BaseShield;
6
  use FernleafSystems\Wordpress\Plugin\Shield\Modules\Plugin\Lib\Captcha\CaptchaConfigVO;
7
  use FernleafSystems\Wordpress\Services\Services;
@@ -263,18 +264,29 @@ class ModCon extends BaseShield\ModCon {
263
  $this->getOptions()->setOpt( 'enable_login_gasp_check', $enable ? 'Y' : 'N' );
264
  }
265
 
266
- public function 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
  }
2
 
3
  namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\LoginGuard;
4
 
5
+ use FernleafSystems\Wordpress\Plugin\Shield\Controller\Assets\Enqueue;
6
  use FernleafSystems\Wordpress\Plugin\Shield\Modules\BaseShield;
7
  use FernleafSystems\Wordpress\Plugin\Shield\Modules\Plugin\Lib\Captcha\CaptchaConfigVO;
8
  use FernleafSystems\Wordpress\Services\Services;
264
  $this->getOptions()->setOpt( 'enable_login_gasp_check', $enable ? 'Y' : 'N' );
265
  }
266
 
267
+ public function getScriptLocalisations() :array {
268
+ $locals = parent::getScriptLocalisations();
269
+ $locals[] = [
270
+ 'global-plugin',
 
271
  'icwp_wpsf_vars_lg',
272
  [
273
  'ajax_gen_backup_codes' => $this->getAjaxActionData( 'gen_backup_codes' ),
274
  'ajax_del_backup_codes' => $this->getAjaxActionData( 'del_backup_codes' ),
275
  ]
276
+ ];
277
+ return $locals;
278
+ }
279
+
280
+ public function getCustomScriptEnqueues() :array {
281
+ $enqs = [];
282
+ if ( is_admin() || is_network_admin() ) {
283
+ $enqs[ Enqueue::CSS ] = [
284
+ 'wp-wp-jquery-ui-dialog'
285
+ ];
286
+ $enqs[ Enqueue::JS ] = [
287
+ 'wp-jquery-ui-dialog'
288
+ ];
289
+ }
290
+ return $enqs;
291
  }
292
  }
src/lib/src/Modules/LoginGuard/UI.php CHANGED
@@ -3,27 +3,39 @@
3
  namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\LoginGuard;
4
 
5
  use FernleafSystems\Wordpress\Plugin\Shield\Modules\BaseShield;
 
6
 
7
  class UI extends BaseShield\UI {
8
 
9
  protected function getSectionWarnings( string $section ) :array {
10
- $aWarnings = [];
11
 
12
  if ( $section == 'section_brute_force_login_protection' && !$this->getCon()->isPremiumActive() ) {
13
  $sIntegration = $this->getPremiumOnlyIntegration();
14
  if ( !empty( $sIntegration ) ) {
15
- $aWarnings[] = sprintf( __( 'Support for login protection with %s is a Pro-only feature.', 'wp-simple-firewall' ), $sIntegration );
 
 
 
 
 
 
 
 
 
 
 
16
  }
17
  }
18
 
19
  if ( $section == 'section_2fa_email' ) {
20
- $aWarnings[] =
21
  __( '2FA by email demands that your WP site is properly configured to send email.', 'wp-simple-firewall' )
22
  .'<br/>'.__( 'This is a common problem and you may get locked out in the future if you ignore this.', 'wp-simple-firewall' )
23
  .' '.sprintf( '<a href="%s" target="_blank" class="alert-link">%s</a>', 'https://shsec.io/dd', __( 'Learn More.', 'wp-simple-firewall' ) );
24
  }
25
 
26
- return $aWarnings;
27
  }
28
 
29
  /**
3
  namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\LoginGuard;
4
 
5
  use FernleafSystems\Wordpress\Plugin\Shield\Modules\BaseShield;
6
+ use FernleafSystems\Wordpress\Plugin\Shield\Utilities\Time\WorldTimeApi;
7
 
8
  class UI extends BaseShield\UI {
9
 
10
  protected function getSectionWarnings( string $section ) :array {
11
+ $warnings = [];
12
 
13
  if ( $section == 'section_brute_force_login_protection' && !$this->getCon()->isPremiumActive() ) {
14
  $sIntegration = $this->getPremiumOnlyIntegration();
15
  if ( !empty( $sIntegration ) ) {
16
+ $warnings[] = sprintf( __( 'Support for login protection with %s is a Pro-only feature.', 'wp-simple-firewall' ), $sIntegration );
17
+ }
18
+ }
19
+
20
+ if ( $section == 'section_2fa_ga' ) {
21
+ try {
22
+ $diff = ( new WorldTimeApi() )->diffServerWithReal();
23
+ if ( $diff > 10 ) {
24
+ $warnings[] = __( 'It appears that your server time configuration is out of sync - Please contact your server admin, as features like Google Authenticator wont work.', 'wp-simple-firewall' );
25
+ }
26
+ }
27
+ catch ( \Exception $e ) {
28
  }
29
  }
30
 
31
  if ( $section == 'section_2fa_email' ) {
32
+ $warnings[] =
33
  __( '2FA by email demands that your WP site is properly configured to send email.', 'wp-simple-firewall' )
34
  .'<br/>'.__( 'This is a common problem and you may get locked out in the future if you ignore this.', 'wp-simple-firewall' )
35
  .' '.sprintf( '<a href="%s" target="_blank" class="alert-link">%s</a>', 'https://shsec.io/dd', __( 'Learn More.', 'wp-simple-firewall' ) );
36
  }
37
 
38
+ return $warnings;
39
  }
40
 
41
  /**
src/lib/src/Modules/ModConsumer.php CHANGED
@@ -12,7 +12,7 @@ use FernleafSystems\Wordpress\Plugin\Shield\Modules;
12
  trait ModConsumer {
13
 
14
  /**
15
- * @var \ICWP_WPSF_FeatureHandler_Base
16
  */
17
  private $oMod;
18
 
@@ -24,7 +24,7 @@ trait ModConsumer {
24
  }
25
 
26
  /**
27
- * @return \ICWP_WPSF_FeatureHandler_Base|Modules\Base\ModCon
28
  */
29
  public function getMod() {
30
  return $this->oMod;
@@ -38,20 +38,20 @@ trait ModConsumer {
38
  }
39
 
40
  /**
41
- * @param Controller $oCon
42
  * @return $this
43
  */
44
- public function setCon( $oCon ) {
45
- $this->getMod()->setCon( $oCon );
46
  return $this;
47
  }
48
 
49
  /**
50
- * @param \ICWP_WPSF_FeatureHandler_Base|Modules\Base\ModCon $oMod
51
  * @return $this
52
  */
53
- public function setMod( $oMod ) {
54
- $this->oMod = $oMod;
55
  return $this;
56
  }
57
  }
12
  trait ModConsumer {
13
 
14
  /**
15
+ * @var Modules\Base\ModCon
16
  */
17
  private $oMod;
18
 
24
  }
25
 
26
  /**
27
+ * @return Modules\Base\ModCon
28
  */
29
  public function getMod() {
30
  return $this->oMod;
38
  }
39
 
40
  /**
41
+ * @param Controller $con
42
  * @return $this
43
  */
44
+ public function setCon( $con ) {
45
+ $this->getMod()->setCon( $con );
46
  return $this;
47
  }
48
 
49
  /**
50
+ * @param Modules\Base\ModCon $mod
51
  * @return $this
52
  */
53
+ public function setMod( $mod ) {
54
+ $this->oMod = $mod;
55
  return $this;
56
  }
57
  }
src/lib/src/Modules/Plugin/AdminNotices.php CHANGED
@@ -242,7 +242,7 @@ class AdminNotices extends Shield\Modules\Base\AdminNotices {
242
  'dismiss' => __( 'Dismiss this notice', 'wp-simple-firewall' )
243
  ],
244
  'hrefs' => [
245
- 'upgrade_link' => Services::WpPlugins()->getUrl_Upgrade( $this->getCon()->getPluginBaseFile() )
246
  ]
247
  ];
248
  }
@@ -310,7 +310,7 @@ class AdminNotices extends Shield\Modules\Base\AdminNotices {
310
  }
311
 
312
  protected function isDisplayNeeded( NoticeVO $notice ) :bool {
313
- $oCon = $this->getCon();
314
  /** @var Options $oOpts */
315
  $oOpts = $this->getOptions();
316
 
@@ -321,7 +321,7 @@ class AdminNotices extends Shield\Modules\Base\AdminNotices {
321
  break;
322
 
323
  case 'override-forceoff':
324
- $needed = $oCon->getIfForceOffActive();
325
  break;
326
 
327
  case 'php7':
@@ -333,7 +333,7 @@ class AdminNotices extends Shield\Modules\Base\AdminNotices {
333
  break;
334
 
335
  case 'update-available':
336
- $needed = Services::WpPlugins()->isUpdateAvailable( $oCon->getPluginBaseFile() );
337
  break;
338
 
339
  case 'compat-sgoptimize':
@@ -354,7 +354,7 @@ class AdminNotices extends Shield\Modules\Base\AdminNotices {
354
  private function isNeeded_PluginTooOld() :bool {
355
  $needed = false;
356
  $con = $this->getCon();
357
- if ( Services::WpPlugins()->isUpdateAvailable( $con->getPluginBaseFile() ) ) {
358
  $versions = Transient::Get( $con->prefix( 'releases' ) );
359
  if ( !is_array( $versions ) ) {
360
  $versions = ( new Shield\Utilities\Github\ListTags() )->run( 'FernleafSystems/Shield-Security-for-WordPress' );
242
  'dismiss' => __( 'Dismiss this notice', 'wp-simple-firewall' )
243
  ],
244
  'hrefs' => [
245
+ 'upgrade_link' => Services::WpPlugins()->getUrl_Upgrade( $this->getCon()->base_file )
246
  ]
247
  ];
248
  }
310
  }
311
 
312
  protected function isDisplayNeeded( NoticeVO $notice ) :bool {
313
+ $con = $this->getCon();
314
  /** @var Options $oOpts */
315
  $oOpts = $this->getOptions();
316
 
321
  break;
322
 
323
  case 'override-forceoff':
324
+ $needed = $con->getIfForceOffActive();
325
  break;
326
 
327
  case 'php7':
333
  break;
334
 
335
  case 'update-available':
336
+ $needed = Services::WpPlugins()->isUpdateAvailable( $con->base_file );
337
  break;
338
 
339
  case 'compat-sgoptimize':
354
  private function isNeeded_PluginTooOld() :bool {
355
  $needed = false;
356
  $con = $this->getCon();
357
+ if ( Services::WpPlugins()->isUpdateAvailable( $con->base_file ) ) {
358
  $versions = Transient::Get( $con->prefix( 'releases' ) );
359
  if ( !is_array( $versions ) ) {
360
  $versions = ( new Shield\Utilities\Github\ListTags() )->run( 'FernleafSystems/Shield-Security-for-WordPress' );
src/lib/src/Modules/Plugin/AjaxHandler.php CHANGED
@@ -180,8 +180,8 @@ class AjaxHandler extends Shield\Modules\BaseShield\AjaxHandler {
180
  $msg = __( "Note couldn't be deleted", 'wp-simple-firewall' );
181
  }
182
  }
183
- catch ( \Exception $oE ) {
184
- $msg = $oE->getMessage();
185
  }
186
  }
187
 
@@ -202,7 +202,7 @@ class AjaxHandler extends Shield\Modules\BaseShield\AjaxHandler {
202
 
203
  // TODO: align with wizard AND combine with file upload errors
204
  if ( $aFormParams[ 'confirm' ] !== 'Y' ) {
205
- $sMessage = __( 'Please check the box to confirm your intent to overwrite settings', 'wp-simple-firewall' );
206
  }
207
  else {
208
  $sMasterSiteUrl = $aFormParams[ 'MasterSiteUrl' ];
@@ -213,20 +213,20 @@ class AjaxHandler extends Shield\Modules\BaseShield\AjaxHandler {
213
 
214
  /** @var Shield\Databases\AdminNotes\Insert $oInserter */
215
  try {
216
- $nCode = ( new Plugin\Lib\ImportExport\Import() )
217
  ->setMod( $this->getMod() )
218
  ->fromSite( $sMasterSiteUrl, $sSecretKey, $bNetwork );
219
  }
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
 
180
  $msg = __( "Note couldn't be deleted", 'wp-simple-firewall' );
181
  }
182
  }
183
+ catch ( \Exception $e ) {
184
+ $msg = $e->getMessage();
185
  }
186
  }
187
 
202
 
203
  // TODO: align with wizard AND combine with file upload errors
204
  if ( $aFormParams[ 'confirm' ] !== 'Y' ) {
205
+ $msg = __( 'Please check the box to confirm your intent to overwrite settings', 'wp-simple-firewall' );
206
  }
207
  else {
208
  $sMasterSiteUrl = $aFormParams[ 'MasterSiteUrl' ];
213
 
214
  /** @var Shield\Databases\AdminNotes\Insert $oInserter */
215
  try {
216
+ $code = ( new Plugin\Lib\ImportExport\Import() )
217
  ->setMod( $this->getMod() )
218
  ->fromSite( $sMasterSiteUrl, $sSecretKey, $bNetwork );
219
  }
220
+ catch ( \Exception $e ) {
221
+ $code = $e->getCode();
222
  }
223
+ $success = $code == 0;
224
+ $msg = $success ? __( 'Options imported successfully', 'wp-simple-firewall' ) : __( 'Options failed to import', 'wp-simple-firewall' );
225
  }
226
 
227
  return [
228
  'success' => $success,
229
+ 'message' => $msg
230
  ];
231
  }
232
 
src/lib/src/Modules/Plugin/Components/PluginBadge.php CHANGED
@@ -122,8 +122,8 @@ class PluginBadge {
122
  try {
123
  $render = $this->getMod()->renderTemplate( 'snippets/plugin_badge_widget', $data, true );
124
  }
125
- catch ( \Exception $oE ) {
126
- $render = 'Could not generate badge: '.$oE->getMessage();
127
  }
128
  return $render;
129
  }
122
  try {
123
  $render = $this->getMod()->renderTemplate( 'snippets/plugin_badge_widget', $data, true );
124
  }
125
+ catch ( \Exception $e ) {
126
+ $render = 'Could not generate badge: '.$e->getMessage();
127
  }
128
  return $render;
129
  }
src/lib/src/Modules/Plugin/Components/SiteGroundPluginCompatibility.php CHANGED
@@ -18,43 +18,40 @@ class SiteGroundPluginCompatibility {
18
  }
19
  }
20
  }
21
- catch ( \Exception $oE ) {
22
  }
23
  }
24
  return $bIncompatExist;
25
  }
26
 
27
- /**
28
- * @return bool
29
- */
30
- public function isSGOptimizerPluginAsExpected() {
31
- $bExpected = false;
32
  try {
33
  $oRefl = new \ReflectionClass( '\SiteGround_Optimizer\Options\Options' );
34
- $bExpected = $oRefl->getMethod( 'is_enabled' )->isStatic()
35
- && $oRefl->getMethod( 'disable_option' )->isStatic();
36
  }
37
- catch ( \Exception $oE ) {
38
  }
39
- return $bExpected;
40
  }
41
 
42
  /**
43
  * @return bool
44
  */
45
  public function switchOffOptions() {
46
- $bSuccess = false;
47
  if ( $this->isSGOptimizerPluginAsExpected() ) {
48
  try {
49
  foreach ( $this->getIncompatOptions() as $sOption ) {
50
  \SiteGround_Optimizer\Options\Options::disable_option( $sOption );
51
  }
52
- $bSuccess = !$this->testIsIncompatible();
53
  }
54
- catch ( \Exception $oE ) {
55
  }
56
  }
57
- return $bSuccess;
58
  }
59
 
60
  /**
18
  }
19
  }
20
  }
21
+ catch ( \Exception $e ) {
22
  }
23
  }
24
  return $bIncompatExist;
25
  }
26
 
27
+ public function isSGOptimizerPluginAsExpected() :bool {
28
+ $isExpected = false;
 
 
 
29
  try {
30
  $oRefl = new \ReflectionClass( '\SiteGround_Optimizer\Options\Options' );
31
+ $isExpected = $oRefl->getMethod( 'is_enabled' )->isStatic()
32
+ && $oRefl->getMethod( 'disable_option' )->isStatic();
33
  }
34
+ catch ( \Exception $e ) {
35
  }
36
+ return $isExpected;
37
  }
38
 
39
  /**
40
  * @return bool
41
  */
42
  public function switchOffOptions() {
43
+ $success = false;
44
  if ( $this->isSGOptimizerPluginAsExpected() ) {
45
  try {
46
  foreach ( $this->getIncompatOptions() as $sOption ) {
47
  \SiteGround_Optimizer\Options\Options::disable_option( $sOption );
48
  }
49
+ $success = !$this->testIsIncompatible();
50
  }
51
+ catch ( \Exception $e ) {
52
  }
53
  }
54
+ return $success;
55
  }
56
 
57
  /**
src/lib/src/Modules/Plugin/Lib/Debug/Collate.php CHANGED
@@ -4,6 +4,7 @@ namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Plugin\Lib\Debug;
4
 
5
  use FernleafSystems\Wordpress\Plugin\Shield\Modules\ModConsumer;
6
  use FernleafSystems\Wordpress\Plugin\Shield\Modules\Plugin\Options;
 
7
  use FernleafSystems\Wordpress\Plugin\Shield\Utilities\Tool\FormatBytes;
8
  use FernleafSystems\Wordpress\Services\Services;
9
  use FernleafSystems\Wordpress\Services\Utilities\Integrations\WpHashes\ApiPing;
@@ -60,9 +61,17 @@ class Collate {
60
 
61
  $totalDisk = disk_total_space( ABSPATH );
62
  $freeDisk = disk_free_space( ABSPATH );
 
 
 
 
 
 
 
63
  return [
64
  'Host OS' => PHP_OS,
65
  'Server Hostname' => gethostname(),
 
66
  'Server IPs' => implode( ', ', $aIPs ),
67
  'CloudFlare' => empty( $req->server( 'HTTP_CF_REQUEST_ID' ) ) ? 'No' : 'Yes',
68
  'rDNS' => empty( $rDNS ) ? '-' : $rDNS,
4
 
5
  use FernleafSystems\Wordpress\Plugin\Shield\Modules\ModConsumer;
6
  use FernleafSystems\Wordpress\Plugin\Shield\Modules\Plugin\Options;
7
+ use FernleafSystems\Wordpress\Plugin\Shield\Utilities\Time\WorldTimeApi;
8
  use FernleafSystems\Wordpress\Plugin\Shield\Utilities\Tool\FormatBytes;
9
  use FernleafSystems\Wordpress\Services\Services;
10
  use FernleafSystems\Wordpress\Services\Utilities\Integrations\WpHashes\ApiPing;
61
 
62
  $totalDisk = disk_total_space( ABSPATH );
63
  $freeDisk = disk_free_space( ABSPATH );
64
+ try {
65
+ $diff = ( new WorldTimeApi() )->diffServerWithReal();
66
+ }
67
+ catch ( \Exception $e ) {
68
+ $diff = 'failed';
69
+ }
70
+
71
  return [
72
  'Host OS' => PHP_OS,
73
  'Server Hostname' => gethostname(),
74
+ 'Server Time Difference' => $diff,
75
  'Server IPs' => implode( ', ', $aIPs ),
76
  'CloudFlare' => empty( $req->server( 'HTTP_CF_REQUEST_ID' ) ) ? 'No' : 'Yes',
77
  'rDNS' => empty( $rDNS ) ? '-' : $rDNS,
src/lib/src/Modules/Plugin/Lib/ImportExport/Export.php CHANGED
@@ -26,7 +26,7 @@ class Export {
26
  break;
27
  }
28
  }
29
- catch ( \Exception $oE ) {
30
  }
31
  die();
32
  }
26
  break;
27
  }
28
  }
29
+ catch ( \Exception $e ) {
30
  }
31
  die();
32
  }
src/lib/src/Modules/Plugin/Lib/ImportExport/Import.php CHANGED
@@ -25,21 +25,21 @@ class Import {
25
  break;
26
  }
27
  }
28
- catch ( \Exception $oE ) {
29
  }
30
  die();
31
  }
32
 
33
  /**
34
- * @param string $sPath
35
  * @throws \Exception
36
  */
37
- public function fromFile( $sPath ) {
38
  if ( !$this->getCon()->isPluginAdmin() ) {
39
  throw new \Exception( __( 'Not currently logged-in as security admin', 'wp-simple-firewall' ) );
40
  }
41
 
42
- $sContent = Services::WpFs()->getFileContent( $sPath );
43
  if ( empty( $sContent ) ) {
44
  throw new \Exception( __( 'Uploaded file was empty', 'wp-simple-firewall' ) );
45
  }
25
  break;
26
  }
27
  }
28
+ catch ( \Exception $e ) {
29
  }
30
  die();
31
  }
32
 
33
  /**
34
+ * @param string $path
35
  * @throws \Exception
36
  */
37
+ public function fromFile( $path ) {
38
  if ( !$this->getCon()->isPluginAdmin() ) {
39
  throw new \Exception( __( 'Not currently logged-in as security admin', 'wp-simple-firewall' ) );
40
  }
41
 
42
+ $sContent = Services::WpFs()->getFileContent( $path );
43
  if ( empty( $sContent ) ) {
44
  throw new \Exception( __( 'Uploaded file was empty', 'wp-simple-firewall' ) );
45
  }
src/lib/src/Modules/Plugin/Lib/PluginTelemetry.php CHANGED
@@ -3,6 +3,7 @@
3
  namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Plugin\Lib;
4
 
5
  use FernleafSystems\Utilities\Logic\OneTimeExecute;
 
6
  use FernleafSystems\Wordpress\Plugin\Shield\Modules\Base\ModCon;
7
  use FernleafSystems\Wordpress\Plugin\Shield\Modules\ModConsumer;
8
  use FernleafSystems\Wordpress\Plugin\Shield\Modules\Plugin;
@@ -86,17 +87,18 @@ class PluginTelemetry {
86
  }
87
 
88
  if ( !empty( $data[ 'events' ] ) ) {
89
- $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'
3
  namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Plugin\Lib;
4
 
5
  use FernleafSystems\Utilities\Logic\OneTimeExecute;
6
+ use FernleafSystems\Wordpress\Plugin\Shield\Databases\Events\Select;
7
  use FernleafSystems\Wordpress\Plugin\Shield\Modules\Base\ModCon;
8
  use FernleafSystems\Wordpress\Plugin\Shield\Modules\ModConsumer;
9
  use FernleafSystems\Wordpress\Plugin\Shield\Modules\Plugin;
87
  }
88
 
89
  if ( !empty( $data[ 'events' ] ) ) {
90
+ /** @var Select $select */
91
+ $select = $con->getModule_Events()
92
+ ->getDbHandler_Events()
93
+ ->getQuerySelector();
94
+ $data[ 'events' ][ 'stats' ] = $select->sumAllEvents();
95
  }
96
  if ( !empty( $data[ 'login_protect' ] ) ) {
97
  $data[ 'login_protect' ][ 'options' ][ 'email_can_send_verified_at' ] =
98
  $data[ 'login_protect' ][ 'options' ][ 'email_can_send_verified_at' ] > 0 ? 1 : 0;
99
  }
100
  if ( !empty( $data[ 'admin_access_restriction' ] ) ) {
101
+ $keys = [
102
  'admin_access_restrict_plugins',
103
  'admin_access_restrict_themes',
104
  'admin_access_restrict_posts'
src/lib/src/Modules/Plugin/Lib/TourManager.php CHANGED
@@ -28,12 +28,12 @@ class TourManager {
28
  public function isCompleted( $sTourKey ) {
29
  try {
30
  $aTrs = $this->getTours();
31
- $bShown = isset( $aTrs[ $sTourKey ] ) && $aTrs[ $sTourKey ] > 0;
32
  }
33
- catch ( \Exception $oE ) {
34
- $bShown = true; // in-case there's a meta saving issue.
35
  }
36
- return $bShown;
37
  }
38
 
39
  /**
@@ -49,7 +49,7 @@ class TourManager {
49
  $this->getCon()
50
  ->getCurrentUserMeta()->tours = $aTrs;
51
  }
52
- catch ( \Exception $oE ) {
53
  }
54
  }
55
  return $this;
28
  public function isCompleted( $sTourKey ) {
29
  try {
30
  $aTrs = $this->getTours();
31
+ $shown = isset( $aTrs[ $sTourKey ] ) && $aTrs[ $sTourKey ] > 0;
32
  }
33
+ catch ( \Exception $e ) {
34
+ $shown = true; // in-case there's a meta saving issue.
35
  }
36
+ return $shown;
37
  }
38
 
39
  /**
49
  $this->getCon()
50
  ->getCurrentUserMeta()->tours = $aTrs;
51
  }
52
+ catch ( \Exception $e ) {
53
  }
54
  }
55
  return $this;
src/lib/src/Modules/Plugin/ModCon.php CHANGED
@@ -3,6 +3,7 @@
3
  namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Plugin;
4
 
5
  use FernleafSystems\Wordpress\Plugin\Shield;
 
6
  use FernleafSystems\Wordpress\Plugin\Shield\Modules\BaseShield;
7
  use FernleafSystems\Wordpress\Services\Services;
8
  use FernleafSystems\Wordpress\Services\Utilities\Net\VisitorIpDetection;
@@ -118,14 +119,14 @@ class ModCon extends BaseShield\ModCon {
118
  ( new Lib\ImportExport\Import() )
119
  ->setMod( $this )
120
  ->fromFileUpload();
121
- $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
  );
@@ -321,7 +322,7 @@ class ModCon extends BaseShield\ModCon {
321
  * hidden 20200121
322
  * @return bool
323
  */
324
- public function getIfShowIntroVideo() {
325
  return false && ( $this->getActivateLength() < 8 )
326
  && ( Services::Request()->ts() - $this->getInstallDate() < 15 );
327
  }
@@ -481,36 +482,35 @@ class ModCon extends BaseShield\ModCon {
481
  return Services::WpUsers()->isUserAdmin();
482
  }
483
 
484
- public function 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' => [
@@ -518,7 +518,22 @@ class ModCon extends BaseShield\ModCon {
518
  'problem_downloading_file' => __( 'There was a problem downloading the file.', 'wp-simple-firewall' ),
519
  ],
520
  ]
521
- );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
522
  }
523
 
524
  public function getDbHandler_GeoIp() :Shield\Databases\GeoIp\Handler {
3
  namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Plugin;
4
 
5
  use FernleafSystems\Wordpress\Plugin\Shield;
6
+ use FernleafSystems\Wordpress\Plugin\Shield\Controller\Assets\Enqueue;
7
  use FernleafSystems\Wordpress\Plugin\Shield\Modules\BaseShield;
8
  use FernleafSystems\Wordpress\Services\Services;
9
  use FernleafSystems\Wordpress\Services\Utilities\Net\VisitorIpDetection;
119
  ( new Lib\ImportExport\Import() )
120
  ->setMod( $this )
121
  ->fromFileUpload();
122
+ $success = true;
123
+ $msg = __( 'Options imported successfully', 'wp-simple-firewall' );
124
  }
125
+ catch ( \Exception $e ) {
126
+ $success = false;
127
+ $msg = $e->getMessage();
128
  }
129
+ $this->setFlashAdminNotice( $msg, !$success );
130
  Services::Response()->redirect(
131
  $this->getCon()->getModule_Insights()->getUrl_SubInsightsPage( 'importexport' )
132
  );
322
  * hidden 20200121
323
  * @return bool
324
  */
325
+ public function getIfShowIntroVideo() :bool {
326
  return false && ( $this->getActivateLength() < 8 )
327
  && ( Services::Request()->ts() - $this->getInstallDate() < 15 );
328
  }
482
  return Services::WpUsers()->isUserAdmin();
483
  }
484
 
485
+ public function getScriptLocalisations() :array {
 
 
486
  $con = $this->getCon();
487
+ $locals = parent::getScriptLocalisations();
488
+
489
  if ( Services::WpPost()->isCurrentPage( 'plugins.php' ) ) {
490
+ $file = $con->base_file;
491
+ $locals[] = [
492
+ 'global-plugin',
493
  'icwp_wpsf_vars_plugin',
494
  [
495
+ 'file' => $file,
496
  'ajax' => [
497
  'send_deactivate_survey' => $this->getAjaxActionData( 'send_deactivate_survey' ),
498
  ],
499
  'hrefs' => [
500
+ 'deactivate' => Services::WpPlugins()->getUrl_Deactivate( $file ),
501
  ],
502
  ]
503
+ ];
 
 
504
  }
505
 
506
+ $locals[] = [
507
+ 'plugin',
508
  'icwp_wpsf_vars_tourmanager',
509
  [ 'ajax' => $this->getAjaxActionData( 'mark_tour_finished' ) ]
510
+ ];
511
+
512
+ $locals[] = [
513
+ 'plugin',
514
  'icwp_wpsf_vars_plugin',
515
  [
516
  'strings' => [
518
  'problem_downloading_file' => __( 'There was a problem downloading the file.', 'wp-simple-firewall' ),
519
  ],
520
  ]
521
+ ];
522
+
523
+ return $locals;
524
+ }
525
+
526
+ public function getCustomScriptEnqueues() :array {
527
+ $enqs = [];
528
+ if ( Services::WpPost()->isCurrentPage( 'plugins.php' ) ) {
529
+ $enqs[ Enqueue::CSS ] = [
530
+ 'wp-wp-jquery-ui-dialog'
531
+ ];
532
+ $enqs[ Enqueue::JS ] = [
533
+ 'wp-jquery-ui-dialog'
534
+ ];
535
+ }
536
+ return $enqs;
537
  }
538
 
539
  public function getDbHandler_GeoIp() :Shield\Databases\GeoIp\Handler {
src/lib/src/Modules/Plugin/WpCli/Import.php CHANGED
@@ -82,14 +82,14 @@ class Import extends Base\WpCli\BaseWpCliCmd {
82
  $this->runImportFromFile( $sSource, WP_CLI\Utils\get_flag_value( $aA, 'delete-file', false ) );
83
  }
84
  }
85
- catch ( \Exception $oE ) {
86
  WP_CLI::error_multi_line(
87
  [
88
  __( 'The import encountered an error.', 'wp-simple-firewall' ),
89
- $oE->getMessage(),
90
  ]
91
  );
92
- WP_CLI::halt( $oE->getCode() );
93
  }
94
 
95
  WP_CLI::success( __( 'Plugin settings imported successfully.', 'wp-simple-firewall' ) );
82
  $this->runImportFromFile( $sSource, WP_CLI\Utils\get_flag_value( $aA, 'delete-file', false ) );
83
  }
84
  }
85
+ catch ( \Exception $e ) {
86
  WP_CLI::error_multi_line(
87
  [
88
  __( 'The import encountered an error.', 'wp-simple-firewall' ),
89
+ $e->getMessage(),
90
  ]
91
  );
92
+ WP_CLI::halt( $e->getCode() );
93
  }
94
 
95
  WP_CLI::success( __( 'Plugin settings imported successfully.', 'wp-simple-firewall' ) );
src/lib/src/Modules/SecurityAdmin/Lib/WhiteLabel/ApplyLabels.php CHANGED
@@ -55,8 +55,8 @@ class ApplyLabels {
55
  */
56
  public function adjustUpdateDataCount( $aUpdateData ) {
57
 
58
- $sFile = $this->getCon()->getPluginBaseFile();
59
- if ( Services::WpPlugins()->isUpdateAvailable( $sFile ) ) {
60
  $aUpdateData[ 'counts' ][ 'total' ]--;
61
  $aUpdateData[ 'counts' ][ 'plugins' ]--;
62
  }
@@ -66,8 +66,8 @@ class ApplyLabels {
66
 
67
  public function hideFromPluginEditor() {
68
  $con = $this->getCon();
69
- $sJs = Services::Data()->readFileContentsUsingInclude( $con->getPath_AssetJs( 'whitelabel.js' ) );
70
- echo sprintf( '<script type="text/javascript">%s</script>', sprintf( $sJs, $con->getPluginBaseFile() ) );
71
  }
72
 
73
  /**
@@ -112,11 +112,11 @@ class ApplyLabels {
112
  /**
113
  * @filter
114
  * @param array $aPluginMeta
115
- * @param string $sPluginBaseFileName
116
  * @return array
117
  */
118
- public function removePluginMetaLinks( $aPluginMeta, $sPluginBaseFileName ) {
119
- if ( $sPluginBaseFileName == $this->getCon()->getPluginBaseFile() ) {
120
  unset( $aPluginMeta[ 2 ] ); // View details
121
  unset( $aPluginMeta[ 3 ] ); // Rate 5*
122
  }
@@ -125,15 +125,15 @@ class ApplyLabels {
125
 
126
  /**
127
  * Hides the update if the page loaded is the plugins page or the updates page.
128
- * @param \stdClass $oPlugins
129
  * @return \stdClass
130
  */
131
- public function hidePluginUpdatesFromUI( $oPlugins ) {
132
- $sFile = $this->getCon()->getPluginBaseFile();
133
- if ( isset( $oPlugins->response[ $sFile ] ) ) {
134
- unset( $oPlugins->response[ $sFile ] );
135
  }
136
- return $oPlugins;
137
  }
138
 
139
  private function isNeedToHideUpdates() :bool {
55
  */
56
  public function adjustUpdateDataCount( $aUpdateData ) {
57
 
58
+ $file = $this->getCon()->base_file;
59
+ if ( Services::WpPlugins()->isUpdateAvailable( $file ) ) {
60
  $aUpdateData[ 'counts' ][ 'total' ]--;
61
  $aUpdateData[ 'counts' ][ 'plugins' ]--;
62
  }
66
 
67
  public function hideFromPluginEditor() {
68
  $con = $this->getCon();
69
+ $js = Services::Data()->readFileContentsUsingInclude( $con->getPath_AssetJs( 'whitelabel.js' ) );
70
+ echo sprintf( '<script type="text/javascript">%s</script>', sprintf( $js, $con->base_file ) );
71
  }
72
 
73
  /**
112
  /**
113
  * @filter
114
  * @param array $aPluginMeta
115
+ * @param string $pluginBaseFile
116
  * @return array
117
  */
118
+ public function removePluginMetaLinks( $aPluginMeta, $pluginBaseFile ) {
119
+ if ( $pluginBaseFile == $this->getCon()->base_file ) {
120
  unset( $aPluginMeta[ 2 ] ); // View details
121
  unset( $aPluginMeta[ 3 ] ); // Rate 5*
122
  }
125
 
126
  /**
127
  * Hides the update if the page loaded is the plugins page or the updates page.
128
+ * @param \stdClass $plugins
129
  * @return \stdClass
130
  */
131
+ public function hidePluginUpdatesFromUI( $plugins ) {
132
+ $file = $this->getCon()->base_file;
133
+ if ( isset( $plugins->response[ $file ] ) ) {
134
+ unset( $plugins->response[ $file ] );
135
  }
136
+ return $plugins;
137
  }
138
 
139
  private function isNeedToHideUpdates() :bool {
src/lib/src/Modules/SecurityAdmin/ModCon.php CHANGED
@@ -286,11 +286,11 @@ class ModCon extends BaseShield\ModCon {
286
  return $this->saveModOptions();
287
  }
288
 
289
- public function insertCustomJsVars_Admin() {
290
- parent::insertCustomJsVars_Admin();
291
 
292
  if ( $this->getSecAdminTimeLeft() > 0 ) {
293
- $aInsertData = [
294
  'ajax' => [
295
  'check' => $this->getSecAdminCheckAjaxData(),
296
  ],
@@ -304,7 +304,7 @@ class ModCon extends BaseShield\ModCon {
304
  ];
305
  }
306
  else {
307
- $aInsertData = [
308
  'ajax' => [
309
  'req_email_remove' => $this->getAjaxActionData( 'req_email_remove' ),
310
  ],
@@ -314,13 +314,13 @@ class ModCon extends BaseShield\ModCon {
314
  ];
315
  }
316
 
317
- if ( !empty( $aInsertData ) ) {
318
- wp_localize_script(
319
- $this->prefix( 'plugin' ),
320
- 'icwp_wpsf_vars_secadmin',
321
- $aInsertData
322
- );
323
- }
324
  }
325
 
326
  /**
286
  return $this->saveModOptions();
287
  }
288
 
289
+ public function getScriptLocalisations() :array {
290
+ $locals = parent::getScriptLocalisations();
291
 
292
  if ( $this->getSecAdminTimeLeft() > 0 ) {
293
+ $data = [
294
  'ajax' => [
295
  'check' => $this->getSecAdminCheckAjaxData(),
296
  ],
304
  ];
305
  }
306
  else {
307
+ $data = [
308
  'ajax' => [
309
  'req_email_remove' => $this->getAjaxActionData( 'req_email_remove' ),
310
  ],
314
  ];
315
  }
316
 
317
+ $locals[] = [
318
+ 'plugin',
319
+ 'icwp_wpsf_vars_secadmin',
320
+ $data
321
+ ];
322
+
323
+ return $locals;
324
  }
325
 
326
  /**
src/lib/src/Modules/SecurityAdmin/WpCli/Pin.php CHANGED
@@ -65,11 +65,11 @@ class Pin extends BaseWpCliCmd {
65
  sprintf( __( 'Security admin pin set to: "%s"', 'wp-simple-firewall' ), $newPIN )
66
  );
67
  }
68
- catch ( \Exception $oE ) {
69
  WP_CLI::error_multi_line(
70
  [
71
  __( 'Setting Security admin pin failed.', 'wp-simple-firewall' ),
72
- $oE->getMessage()
73
  ]
74
  );
75
  WP_CLI::halt( 1 );
65
  sprintf( __( 'Security admin pin set to: "%s"', 'wp-simple-firewall' ), $newPIN )
66
  );
67
  }
68
+ catch ( \Exception $e ) {
69
  WP_CLI::error_multi_line(
70
  [
71
  __( 'Setting Security admin pin failed.', 'wp-simple-firewall' ),
72
+ $e->getMessage()
73
  ]
74
  );
75
  WP_CLI::halt( 1 );
src/lib/src/Modules/Sessions/Processor.php CHANGED
@@ -99,68 +99,4 @@ class Processor extends BaseShield\Processor {
99
  $mod->getSessionCon()->terminateCurrentSession();
100
  $mod->getSessionCon()->queryCreateSession( $this->getCon()->getSessionId( true ), $user );
101
  }
102
-
103
- /**
104
- * @return bool
105
- * @deprecated 10.1
106
- */
107
- public function terminateCurrentSession() {
108
- $success = false;
109
-
110
- $oSes = $this->getCurrentSession();
111
- if ( $oSes instanceof Session\EntryVO ) {
112
- $success = ( new Lib\Ops\Terminate() )
113
- ->setMod( $this->getMod() )
114
- ->byRecordId( $oSes->id );
115
- }
116
-
117
- $this->current = null;
118
- $this->getCon()->clearSession();
119
-
120
- return $success;
121
- }
122
-
123
- /**
124
- * @return Session\EntryVO|null
125
- * @deprecated 10.1
126
- */
127
- public function getCurrentSession() {
128
- /** @var ModCon $mod */
129
- $mod = $this->getMod();
130
- return empty( $this->current ) ? $mod->getSessionCon()->getCurrent() : $this->current;
131
- }
132
-
133
- /**
134
- * @return Session\EntryVO|null
135
- * @deprecated 10.1
136
- */
137
- public function loadCurrentSession() {
138
- $sess = null;
139
- $con = $this->getCon();
140
- if ( did_action( 'init' ) && $con->hasSessionId() ) {
141
- $sess = $this->queryGetSession( $con->getSessionId() );
142
- }
143
- return $sess;
144
- }
145
-
146
- /**
147
- * @param string $sessionID
148
- * @param string $username
149
- * @return bool
150
- * @deprecated 10.1
151
- */
152
- protected function queryCreateSession( $sessionID, $username ) {
153
- return false;
154
- }
155
-
156
- /**
157
- * Checks for and gets a user session.
158
- * @param string $username
159
- * @param string $sessionID
160
- * @return Session\EntryVO|null
161
- * @deprecated 10.1
162
- */
163
- private function queryGetSession( $sessionID, $username = '' ) {
164
- return null;
165
- }
166
  }
99
  $mod->getSessionCon()->terminateCurrentSession();
100
  $mod->getSessionCon()->queryCreateSession( $this->getCon()->getSessionId( true ), $user );
101
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
102
  }
src/lib/src/Modules/Traffic/Lib/Limit/Limiter.php CHANGED
@@ -28,15 +28,15 @@ class Limiter {
28
  ->setMod( $this->getMod() )
29
  ->runTest( Services::IP()->getRequestIp() );
30
  }
31
- catch ( \Exception $oE ) {
32
- /** @var Traffic\Options $oOpts */
33
- $oOpts = $this->getOptions();
34
  $this->getCon()->fireEvent(
35
  'request_limit_exceeded',
36
  [
37
  'audit' => [
38
- 'count' => $oOpts->getLimitRequestCount(),
39
- 'span' => $oOpts->getLimitTimeSpan(),
40
  ]
41
  ]
42
  );
28
  ->setMod( $this->getMod() )
29
  ->runTest( Services::IP()->getRequestIp() );
30
  }
31
+ catch ( \Exception $e ) {
32
+ /** @var Traffic\Options $opts */
33
+ $opts = $this->getOptions();
34
  $this->getCon()->fireEvent(
35
  'request_limit_exceeded',
36
  [
37
  'audit' => [
38
+ 'count' => $opts->getLimitRequestCount(),
39
+ 'span' => $opts->getLimitTimeSpan(),
40
  ]
41
  ]
42
  );
src/lib/src/Modules/UserManagement/Lib/Password/UserPasswordHandler.php CHANGED
@@ -219,20 +219,6 @@ class UserPasswordHandler {
219
  return true;
220
  }
221
 
222
- /**
223
- * Unused
224
- * @return bool
225
- * private function verifyApiAccess() {
226
- * try {
227
- * $this->sendRequestToPwnedRange( 'P@ssw0rd' );
228
- * }
229
- * catch ( \Exception $oE ) {
230
- * return false;
231
- * }
232
- * return true;
233
- * }
234
- */
235
-
236
  /**
237
  * @param string $password
238
  * @return bool
@@ -244,7 +230,7 @@ class UserPasswordHandler {
244
  $sPassHash = strtoupper( hash( 'sha1', $password ) );
245
  $sSubHash = substr( $sPassHash, 0, 5 );
246
 
247
- $bSuccess = $oHttpReq->get(
248
  sprintf( '%s/%s', $this->getOptions()->getDef( 'pwned_api_url_password_range' ), $sSubHash ),
249
  [
250
  'headers' => [ 'user-agent' => sprintf( '%s WP Plugin-v%s', 'Shield', $this->getCon()->getVersion() ) ]
@@ -253,7 +239,7 @@ class UserPasswordHandler {
253
 
254
  $sError = '';
255
  $nErrorCode = 2; // Default To Error
256
- if ( !$bSuccess ) {
257
  $sError = 'API request failed';
258
  $nErrorCode = 999; // We don't fail PWNED passwords on failed API requests.
259
  }
219
  return true;
220
  }
221
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
222
  /**
223
  * @param string $password
224
  * @return bool
230
  $sPassHash = strtoupper( hash( 'sha1', $password ) );
231
  $sSubHash = substr( $sPassHash, 0, 5 );
232
 
233
+ $success = $oHttpReq->get(
234
  sprintf( '%s/%s', $this->getOptions()->getDef( 'pwned_api_url_password_range' ), $sSubHash ),
235
  [
236
  'headers' => [ 'user-agent' => sprintf( '%s WP Plugin-v%s', 'Shield', $this->getCon()->getVersion() ) ]
239
 
240
  $sError = '';
241
  $nErrorCode = 2; // Default To Error
242
+ if ( !$success ) {
243
  $sError = 'API request failed';
244
  $nErrorCode = 999; // We don't fail PWNED passwords on failed API requests.
245
  }
src/lib/src/Modules/UserManagement/ModCon.php CHANGED
@@ -61,14 +61,6 @@ class ModCon extends BaseShield\ModCon {
61
  $opts->setOpt( 'email_checks', array_unique( $aChecks ) );
62
  }
63
 
64
- /**
65
- * @return bool
66
- * @deprecated 10.1
67
- */
68
- public function isUserSessionsManagementEnabled() :bool {
69
- return $this->isModOptEnabled() && $this->getDbHandler_Sessions()->isReady();
70
- }
71
-
72
  public function isSendUserEmailLoginNotification() :bool {
73
  return $this->isPremium() && $this->getOptions()->isOpt( 'enable_user_login_email_notification', 'Y' );
74
  }
61
  $opts->setOpt( 'email_checks', array_unique( $aChecks ) );
62
  }
63
 
 
 
 
 
 
 
 
 
64
  public function isSendUserEmailLoginNotification() :bool {
65
  return $this->isPremium() && $this->getOptions()->isOpt( 'enable_user_login_email_notification', 'Y' );
66
  }
src/lib/src/Modules/UserManagement/Options.php CHANGED
@@ -83,14 +83,6 @@ class Options extends BaseShield\Options {
83
  return $this->isOpt( 'pass_prevent_pwned', 'Y' );
84
  }
85
 
86
- /**
87
- * @return bool
88
- * @deprecated 10.1
89
- */
90
- public function isPassForceUpdateExisting() :bool {
91
- return $this->isOpt( 'pass_force_existing', 'Y' );
92
- }
93
-
94
  public function isPasswordPoliciesEnabled() :bool {
95
  return $this->isOpt( 'enable_password_policies', 'Y' )
96
  && $this->isOptReqsMet( 'enable_password_policies' );
83
  return $this->isOpt( 'pass_prevent_pwned', 'Y' );
84
  }
85
 
 
 
 
 
 
 
 
 
86
  public function isPasswordPoliciesEnabled() :bool {
87
  return $this->isOpt( 'enable_password_policies', 'Y' )
88
  && $this->isOptReqsMet( 'enable_password_policies' );
src/lib/src/Scans/Base/BaseScanActionVO.php CHANGED
@@ -56,11 +56,11 @@ abstract class BaseScanActionVO {
56
  */
57
  public function getScanNamespace() {
58
  try {
59
- $sName = ( new \ReflectionClass( $this ) )->getNamespaceName();
60
  }
61
- catch ( \Exception $oE ) {
62
- $sName = __NAMESPACE__;
63
  }
64
- return rtrim( $sName, '\\' ).'\\';
65
  }
66
  }
56
  */
57
  public function getScanNamespace() {
58
  try {
59
+ $namespace = ( new \ReflectionClass( $this ) )->getNamespaceName();
60
  }
61
+ catch ( \Exception $e ) {
62
+ $namespace = __NAMESPACE__;
63
  }
64
+ return rtrim( $namespace, '\\' ).'\\';
65
  }
66
  }
src/lib/src/Scans/Base/Files/BaseFileMapScan.php CHANGED
@@ -14,20 +14,20 @@ abstract class BaseFileMapScan extends Base\BaseScan {
14
  * @return $this
15
  */
16
  protected function scanSlice() {
17
- /** @var Base\BaseScanActionVO $oAction */
18
- $oAction = $this->getScanActionVO();
19
 
20
  $oTempRs = $this->getScanFromFileMap()
21
- ->setScanActionVO( $oAction )
22
  ->run();
23
 
24
- $aNewItems = [];
25
  if ( $oTempRs->hasItems() ) {
26
  foreach ( $oTempRs->getAllItems() as $oItem ) {
27
- $aNewItems[] = $oItem->getRawDataAsArray();
28
  }
29
  }
30
- $oAction->results = $aNewItems;
31
 
32
  return $this;
33
  }
14
  * @return $this
15
  */
16
  protected function scanSlice() {
17
+ /** @var Base\BaseScanActionVO $action */
18
+ $action = $this->getScanActionVO();
19
 
20
  $oTempRs = $this->getScanFromFileMap()
21
+ ->setScanActionVO( $action )
22
  ->run();
23
 
24
+ $newItems = [];
25
  if ( $oTempRs->hasItems() ) {
26
  foreach ( $oTempRs->getAllItems() as $oItem ) {
27
+ $newItems[] = $oItem->getRawDataAsArray();
28
  }
29
  }
30
+ $action->results = $newItems;
31
 
32
  return $this;
33
  }
src/lib/src/Scans/Base/Files/BaseFileScanner.php CHANGED
@@ -14,8 +14,8 @@ abstract class BaseFileScanner {
14
  use Shield\Scans\Common\ScanActionConsumer;
15
 
16
  /**
17
- * @param string $sFullPath
18
  * @return Shield\Scans\Base\BaseResultItem|null
19
  */
20
- abstract public function scan( $sFullPath );
21
  }
14
  use Shield\Scans\Common\ScanActionConsumer;
15
 
16
  /**
17
+ * @param string $fullPath
18
  * @return Shield\Scans\Base\BaseResultItem|null
19
  */
20
+ abstract public function scan( string $fullPath );
21
  }
src/lib/src/Scans/Base/Files/BaseScanFromFileMap.php CHANGED
@@ -18,20 +18,19 @@ abstract class BaseScanFromFileMap {
18
  * @return Scans\Base\BaseResultsSet
19
  */
20
  public function run() {
21
- $oAction = $this->getScanActionVO();
22
- $oResultSet = $oAction->getNewResultsSet();
23
-
24
- if ( is_array( $oAction->items ) ) {
25
-
26
- foreach ( $oAction->items as $nKey => $sFullPath ) {
27
- $oItem = $this->getFileScanner()->scan( $sFullPath );
28
- if ( $oItem instanceof Scans\Base\BaseResultItem ) {
29
- $oResultSet->addItem( $oItem );
30
  }
31
  }
32
  }
33
 
34
- return $oResultSet;
35
  }
36
 
37
  /**
18
  * @return Scans\Base\BaseResultsSet
19
  */
20
  public function run() {
21
+ $action = $this->getScanActionVO();
22
+ $results = $action->getNewResultsSet();
23
+
24
+ if ( is_array( $action->items ) ) {
25
+ foreach ( $action->items as $key => $fullPath ) {
26
+ $item = $this->getFileScanner()->scan( $fullPath );
27
+ if ( $item instanceof Scans\Base\BaseResultItem ) {
28
+ $results->addItem( $item );
 
29
  }
30
  }
31
  }
32
 
33
+ return $results;
34
  }
35
 
36
  /**
src/lib/src/Scans/Base/Table/BaseFileEntryFormatter.php CHANGED
@@ -13,10 +13,10 @@ abstract class BaseFileEntryFormatter extends BaseEntryFormatter {
13
  */
14
  protected function getBaseData() {
15
  $aData = parent::getBaseData();
16
- $oIt = $this->getResultItem();
17
  $aData[ 'explanation' ] = $this->getExplanation();
18
- $aData[ 'path' ] = $oIt->path_fragment;
19
- $aData[ 'path_relabs' ] = Services::WpFs()->getPathRelativeToAbsPath( $oIt->path_full );
20
  $aData[ 'path_details' ] = [];
21
  $aData[ 'created_at' ] = $this->formatTimestampField( $this->getEntryVO()->created_at );
22
  $aData[ 'custom_row' ] = false;
13
  */
14
  protected function getBaseData() {
15
  $aData = parent::getBaseData();
16
+ $item = $this->getResultItem();
17
  $aData[ 'explanation' ] = $this->getExplanation();
18
+ $aData[ 'path' ] = $item->path_fragment;
19
+ $aData[ 'path_relabs' ] = Services::WpFs()->getPathRelativeToAbsPath( $item->path_full );
20
  $aData[ 'path_details' ] = [];
21
  $aData[ 'created_at' ] = $this->formatTimestampField( $this->getEntryVO()->created_at );
22
  $aData[ 'custom_row' ] = false;
src/lib/src/Scans/Base/Utilities/ItemActionHandler.php CHANGED
@@ -14,29 +14,29 @@ abstract class ItemActionHandler {
14
  use HackGuard\Scan\Controller\ScanControllerConsumer;
15
 
16
  /**
17
- * @param string $sAction
18
  * @return bool
19
  * @throws \Exception
20
  */
21
- public function process( $sAction ) {
22
- switch ( $sAction ) {
23
  case 'delete':
24
- $bSuccess = $this->delete();
25
  break;
26
 
27
  case 'ignore':
28
- $bSuccess = $this->ignore();
29
  break;
30
 
31
  case 'repair':
32
- $bSuccess = $this->repair();
33
  break;
34
 
35
  default:
36
  throw new \Exception( 'Unsupported Scan Item Action' );
37
  break;
38
  }
39
- return $bSuccess;
40
  }
41
 
42
  /**
14
  use HackGuard\Scan\Controller\ScanControllerConsumer;
15
 
16
  /**
17
+ * @param string $action
18
  * @return bool
19
  * @throws \Exception
20
  */
21
+ public function process( $action ) {
22
+ switch ( $action ) {
23
  case 'delete':
24
+ $success = $this->delete();
25
  break;
26
 
27
  case 'ignore':
28
+ $success = $this->ignore();
29
  break;
30
 
31
  case 'repair':
32
+ $success = $this->repair();
33
  break;
34
 
35
  default:
36
  throw new \Exception( 'Unsupported Scan Item Action' );
37
  break;
38
  }
39
+ return $success;
40
  }
41
 
42
  /**
src/lib/src/Scans/Base/Utilities/ItemActionHandlerAssets.php CHANGED
@@ -10,19 +10,19 @@ use FernleafSystems\Wordpress\Services\Services;
10
  abstract class ItemActionHandlerAssets extends ItemActionHandler {
11
 
12
  /**
13
- * @param string $sAction
14
  * @return bool
15
  * @throws \Exception
16
  */
17
- public function process( $sAction ) {
18
- switch ( $sAction ) {
19
 
20
  case 'asset_deactivate':
21
  $bSuccess = $this->assetDeactivate();
22
  break;
23
 
24
  default:
25
- $bSuccess = parent::process( $sAction );
26
  break;
27
  }
28
 
10
  abstract class ItemActionHandlerAssets extends ItemActionHandler {
11
 
12
  /**
13
+ * @param string $action
14
  * @return bool
15
  * @throws \Exception
16
  */
17
+ public function process( $action ) {
18
+ switch ( $action ) {
19
 
20
  case 'asset_deactivate':
21
  $bSuccess = $this->assetDeactivate();
22
  break;
23
 
24
  default:
25
+ $bSuccess = parent::process( $action );
26
  break;
27
  }
28
 
src/lib/src/Scans/Helpers/BuildHashesFromDir.php CHANGED
@@ -41,7 +41,7 @@ class BuildHashesFromDir {
41
  $aSnaps[ $sKey ] = hash_file( $sAlgo, $sFullPath );
42
  }
43
  }
44
- catch ( \Exception $oE ) {
45
  }
46
  return $aSnaps;
47
  }
41
  $aSnaps[ $sKey ] = hash_file( $sAlgo, $sFullPath );
42
  }
43
  }
44
+ catch ( \Exception $e ) {
45
  }
46
  return $aSnaps;
47
  }
src/lib/src/Scans/Helpers/WpCoreFile.php CHANGED
@@ -33,7 +33,7 @@ class WpCoreFile {
33
  $oFiles->replaceFileFromVcs( $sPath );
34
  $bSuccess = true;
35
  }
36
- catch ( \InvalidArgumentException $oE ) {
37
  }
38
  }
39
  return $bSuccess;
33
  $oFiles->replaceFileFromVcs( $sPath );
34
  $bSuccess = true;
35
  }
36
+ catch ( \InvalidArgumentException $e ) {
37
  }
38
  }
39
  return $bSuccess;
src/lib/src/Scans/Mal/BuildFileMap.php CHANGED
@@ -16,72 +16,68 @@ class BuildFileMap {
16
  /**
17
  * @return string[]
18
  */
19
- public function build() {
20
- $aFiles = [];
21
  $this->preBuild();
22
 
23
- /** @var ScanActionVO $oAction */
24
- $oAction = $this->getScanActionVO();
25
 
26
- foreach ( $oAction->scan_root_dirs as $sScanDir => $nDepth ) {
27
  try {
28
- foreach ( StandardDirectoryIterator::create( $sScanDir, (int)$nDepth, $oAction->file_exts, false ) as $oFsItem ) {
29
- /** @var \SplFileInfo $oFsItem */
30
- $sFullPath = wp_normalize_path( $oFsItem->getPathname() );
31
  try {
32
- if ( !$this->isWhitelistedPath( $sFullPath ) && $oFsItem->getSize() > 0 ) {
33
- $aFiles[] = $sFullPath;
34
  }
35
  }
36
- catch ( \Exception $oE ) {
37
  }
38
  }
39
  }
40
- catch ( \Exception $oE ) {
41
  error_log(
42
  sprintf( 'Shield file scanner (%s) attempted to read directory (%s) but there was error: "%s".',
43
- $oAction->scan, $sScanDir, $oE->getMessage() )
44
  );
45
  }
46
  }
47
- return $aFiles;
48
  }
49
 
50
  protected function preBuild() {
51
- /** @var ScanActionVO $oAction */
52
- $oAction = $this->getScanActionVO();
53
 
54
- if ( empty( $oAction->scan_root_dirs ) || !is_array( $oAction->scan_root_dirs ) ) {
55
- $oAction->scan_root_dirs = [
56
  ABSPATH => 1,
57
  path_join( ABSPATH, WPINC ) => 0,
58
  path_join( ABSPATH, 'wp-admin' ) => 0,
59
  WP_CONTENT_DIR => 0,
60
  ];
61
  }
62
- if ( empty( $oAction->file_exts ) ) {
63
- $oAction->file_exts = [ 'php', 'php5' ];
64
  }
65
- if ( !is_array( $oAction->paths_whitelisted ) ) {
66
- $oAction->paths_whitelisted = [];
67
  }
68
  }
69
 
70
- /**
71
- * @param string $sThePath
72
- * @return bool
73
- */
74
- private function isWhitelistedPath( $sThePath ) {
75
- $bWhitelisted = false;
76
 
77
  /** @var ScanActionVO $oAction */
78
  $oAction = $this->getScanActionVO();
79
  foreach ( $oAction->paths_whitelisted as $sWlPath ) {
80
- if ( stripos( $sThePath, $sWlPath ) === 0 ) {
81
- $bWhitelisted = true;
82
  break;
83
  }
84
  }
85
- return $bWhitelisted;
86
  }
87
  }
16
  /**
17
  * @return string[]
18
  */
19
+ public function build() :array {
20
+ $files = [];
21
  $this->preBuild();
22
 
23
+ /** @var ScanActionVO $action */
24
+ $action = $this->getScanActionVO();
25
 
26
+ foreach ( $action->scan_root_dirs as $scanDir => $depth ) {
27
  try {
28
+ foreach ( StandardDirectoryIterator::create( $scanDir, (int)$depth, $action->file_exts, false ) as $item ) {
29
+ /** @var \SplFileInfo $item */
30
+ $fullPath = wp_normalize_path( $item->getPathname() );
31
  try {
32
+ if ( !$this->isWhitelistedPath( $fullPath ) && $item->getSize() > 0 ) {
33
+ $files[] = $fullPath;
34
  }
35
  }
36
+ catch ( \Exception $e ) {
37
  }
38
  }
39
  }
40
+ catch ( \Exception $e ) {
41
  error_log(
42
  sprintf( 'Shield file scanner (%s) attempted to read directory (%s) but there was error: "%s".',
43
+ $action->scan, $scanDir, $e->getMessage() )
44
  );
45
  }
46
  }
47
+ return $files;
48
  }
49
 
50
  protected function preBuild() {
51
+ /** @var ScanActionVO $action */
52
+ $action = $this->getScanActionVO();
53
 
54
+ if ( empty( $action->scan_root_dirs ) || !is_array( $action->scan_root_dirs ) ) {
55
+ $action->scan_root_dirs = [
56
  ABSPATH => 1,
57
  path_join( ABSPATH, WPINC ) => 0,
58
  path_join( ABSPATH, 'wp-admin' ) => 0,
59
  WP_CONTENT_DIR => 0,
60
  ];
61
  }
62
+ if ( empty( $action->file_exts ) ) {
63
+ $action->file_exts = [ 'php', 'php5' ];
64
  }
65
+ if ( !is_array( $action->paths_whitelisted ) ) {
66
+ $action->paths_whitelisted = [];
67
  }
68
  }
69
 
70
+ private function isWhitelistedPath( string $path ) :bool {
71
+ $whitelisted = false;
 
 
 
 
72
 
73
  /** @var ScanActionVO $oAction */
74
  $oAction = $this->getScanActionVO();
75
  foreach ( $oAction->paths_whitelisted as $sWlPath ) {
76
+ if ( stripos( $path, $sWlPath ) === 0 ) {
77
+ $whitelisted = true;
78
  break;
79
  }
80
  }
81
+ return $whitelisted;
82
  }
83
  }
src/lib/src/Scans/Mal/FileScanner.php CHANGED
@@ -16,209 +16,226 @@ use FernleafSystems\Wordpress\Services\Utilities;
16
  class FileScanner extends Shield\Scans\Base\Files\BaseFileScanner {
17
 
18
  /**
19
- * @param string $sFullPath
 
 
 
 
 
 
 
 
 
20
  * @return ResultItem|null
21
  */
22
- public function scan( $sFullPath ) {
23
- $oItem = null;
24
 
25
- /** @var ScanActionVO $oAction */
26
- $oAction = $this->getScanActionVO();
27
 
28
  try {
29
- $oLocator = ( new Utilities\File\LocateStrInFile() )->setPath( $sFullPath );
30
-
31
  { // Simple Patterns first
32
- $oLocator->setIsRegEx( false );
33
- foreach ( $oAction->patterns_simple as $sSig ) {
34
- $oItem = $this->scanForSig( $oLocator, $sSig );
35
- if ( $oItem instanceof ResultItem ) {
36
- return $oItem;
37
  }
38
  }
39
  }
40
 
41
- { // RegEx Patterns
42
- $oLocator->setIsRegEx( true );
43
- foreach ( $oAction->patterns_regex as $sSig ) {
44
- $oItem = $this->scanForSig( $oLocator, $sSig );
45
- if ( $oItem instanceof ResultItem ) {
46
- return $oItem;
 
 
 
 
 
 
 
 
 
 
47
  }
48
  }
49
  }
50
  }
51
- catch ( \Exception $oE ) {
52
  }
53
 
54
- return $oItem;
55
  }
56
 
57
  /**
58
- * @param Utilities\File\LocateStrInFile $oLocator
59
- * @param string $sSig
60
  * @return ResultItem|null
61
  */
62
- private function scanForSig( $oLocator, $sSig ) {
63
- $oResultItem = null;
 
64
 
65
- $aLines = $oLocator->setNeedle( $sSig )
66
- ->run();
67
- $sFullPath = $oLocator->getPath();
68
- if ( !empty( $aLines ) ) {
69
 
70
- if ( $this->canExcludeFile( $sFullPath ) ) { // we report false positives: file and lines
71
- $oReporter = ( new Shield\Scans\Mal\Utilities\FalsePositiveReporter() )
 
 
72
  ->setMod( $this->getMod() );
73
- foreach ( $aLines as $nLine => $sLine ) {
74
- $oReporter->reportLine( $sFullPath, $sLine, true );
75
  }
76
- $oReporter->reportPath( $sFullPath, true );
77
  }
78
  else {
79
- /** @var ScanActionVO $oAction */
80
- $oAction = $this->getScanActionVO();
81
 
82
- if ( $oAction->confidence_threshold > 0 ) {
83
- $bReportItem = false;
84
  // 1. First check whether the FP of the whole file means we can filter it
85
  $nFalsePositiveConfidence = ( new Shield\Scans\Mal\Utilities\FalsePositiveQuery() )
86
  ->setMod( $this->getMod() )
87
- ->queryPath( $sFullPath );
88
- if ( $nFalsePositiveConfidence < $oAction->confidence_threshold ) {
89
  // 2. Check each line and filter out fp confident lines
90
  $aLineScores = ( new Shield\Scans\Mal\Utilities\FalsePositiveQuery() )
91
  ->setMod( $this->getMod() )
92
- ->queryFileLines( $sFullPath, array_keys( $aLines ) );
93
- $aLines = array_filter(
94
  $aLineScores,
95
- function ( $nScore ) use ( $oAction ) {
96
- return $nScore < $oAction->confidence_threshold;
97
  }
98
  );
99
 
100
- if ( empty( $aLines ) ) {
101
  // Now send False Positive report for entire file based on all file lines being FPs.
102
  ( new Shield\Scans\Mal\Utilities\FalsePositiveReporter() )
103
  ->setMod( $this->getMod() )
104
- ->reportPath( $sFullPath, true );
105
  }
106
  else {
107
- $bReportItem = true;
108
  }
109
  }
110
  }
111
  else {
112
- $bReportItem = true;
113
  }
114
 
115
- if ( $bReportItem ) {
116
- $oResultItem = $this->getResultItemFromLines( array_keys( $aLines ), $sFullPath, $sSig );
117
  }
118
  }
119
  }
120
- return $oResultItem;
121
  }
122
 
123
  /**
124
- * @param string[] $aLines
125
- * @param string $sFullPath
126
- * @param string $sSig
127
  * @return ResultItem
128
  */
129
- private function getResultItemFromLines( $aLines, $sFullPath, $sSig ) {
130
  $oResultItem = new ResultItem();
131
- $oResultItem->path_full = wp_normalize_path( $sFullPath );
132
  $oResultItem->path_fragment = str_replace( wp_normalize_path( ABSPATH ), '', $oResultItem->path_full );
133
  $oResultItem->is_mal = true;
134
- $oResultItem->mal_sig = base64_encode( $sSig );
135
  $oResultItem->fp_confidence = 0;
136
- $oResultItem->file_lines = $aLines;
137
  return $oResultItem;
138
  }
139
 
140
  /**
141
- * @param string $sFullPath - normalized
142
  * @return bool
143
  */
144
- private function canExcludeFile( $sFullPath ) {
145
- return $this->isValidCoreFile( $sFullPath )
146
- || $this->isPluginFileValid( $sFullPath ) || $this->isThemeFileValid( $sFullPath );
147
  }
148
 
149
  /**
150
- * @param string $sFullPath - normalized
151
  * @return bool
152
  */
153
- private function isPluginFileValid( $sFullPath ) {
154
- $bIsValidFile = false;
155
  try {
156
  $oPluginFiles = new Utilities\WpOrg\Plugin\Files();
157
- $oPlugin = $oPluginFiles->findPluginFromFile( $sFullPath );
158
- if ( $oPlugin instanceof WpPluginVo ) {
159
- $bIsValidFile = $oPlugin->isWpOrg() ?
160
- $oPluginFiles->verifyFileContents( $sFullPath )
161
- : $this->verifyPremiumAssetFile( $sFullPath, $oPlugin );
162
  }
163
  }
164
- catch ( \Exception $oE ) {
165
  }
166
 
167
- return $bIsValidFile;
168
  }
169
 
170
  /**
171
- * @param string $sFullPath - normalized
172
  * @return bool
173
  */
174
- private function isThemeFileValid( $sFullPath ) {
175
- $bIsValidFile = false;
176
  try {
177
  $oThemeFiles = new Utilities\WpOrg\Theme\Files();
178
- $oTheme = $oThemeFiles->findThemeFromFile( $sFullPath );
179
- if ( $oTheme instanceof WpThemeVo ) {
180
- $bIsValidFile = $oTheme->isWpOrg() ?
181
- $oThemeFiles->verifyFileContents( $sFullPath )
182
- : $this->verifyPremiumAssetFile( $sFullPath, $oTheme );
183
  }
184
  }
185
- catch ( \Exception $oE ) {
186
  }
187
 
188
- return $bIsValidFile;
189
  }
190
 
191
  /**
192
- * @param string $sFullPath
193
  * @param WpPluginVo|WpThemeVo $oPluginOrTheme
194
  * @return bool
195
  * @throws \Exception
196
  */
197
- private function verifyPremiumAssetFile( $sFullPath, $oPluginOrTheme ) {
198
- $bIsValidFile = false;
199
- $aHashes = ( new Lib\Snapshots\Build\BuildHashesFromApi() )
200
  ->build( $oPluginOrTheme );
201
- $sFragment = str_replace( $oPluginOrTheme->getInstallDir(), '', $sFullPath );
202
- if ( !empty( $aHashes ) && !empty( $aHashes[ $sFragment ] ) ) {
203
- $bIsValidFile = ( new Utilities\File\Compare\CompareHash() )
204
- ->isEqualFileMd5( $sFullPath, $aHashes[ $sFragment ] );
205
  }
206
- return $bIsValidFile;
207
  }
208
 
209
  /**
210
- * @param string $sFullPath
211
  * @return bool
212
  */
213
- private function isValidCoreFile( $sFullPath ) {
214
- $sCoreHash = Services::CoreFileHashes()->getFileHash( $sFullPath );
215
  try {
216
- $bValid = !empty( $sCoreHash )
217
- && ( new Utilities\File\Compare\CompareHash() )->isEqualFileMd5( $sFullPath, $sCoreHash );
218
  }
219
- catch ( \Exception $oE ) {
220
- $bValid = false;
221
  }
222
- return $bValid;
223
  }
224
  }
16
  class FileScanner extends Shield\Scans\Base\Files\BaseFileScanner {
17
 
18
  /**
19
+ * @var Utilities\File\LocateStrInFile
20
+ */
21
+ private $locator;
22
+
23
+ public function __construct() {
24
+ $this->locator = new Utilities\File\LocateStrInFile();
25
+ }
26
+
27
+ /**
28
+ * @param string $fullPath
29
  * @return ResultItem|null
30
  */
31
+ public function scan( string $fullPath ) {
32
+ $item = null;
33
 
34
+ /** @var ScanActionVO $action */
35
+ $action = $this->getScanActionVO();
36
 
37
  try {
38
+ $this->locator->setPath( $fullPath );
 
39
  { // Simple Patterns first
40
+ $this->locator->setIsRegEx( false );
41
+ foreach ( $action->patterns_simple as $signature ) {
42
+ $item = $this->scanForSig( $signature );
43
+ if ( $item instanceof ResultItem ) {
44
+ return $item;
45
  }
46
  }
47
  }
48
 
49
+ // RegEx Patterns
50
+ if ( empty( $action->patterns_fullregex ) ) {
51
+ $this->locator->setIsRegEx( true );
52
+ foreach ( $action->patterns_regex as $signature ) {
53
+ $item = $this->scanForSig( $signature );
54
+ if ( $item instanceof ResultItem ) {
55
+ return $item;
56
+ }
57
+ }
58
+ }
59
+ else { // Full regex patterns
60
+ $this->locator->setIsRegEx( true );
61
+ foreach ( $action->patterns_fullregex as $signature ) {
62
+ $item = $this->scanForSig( $signature );
63
+ if ( $item instanceof ResultItem ) {
64
+ return $item;
65
  }
66
  }
67
  }
68
  }
69
+ catch ( \Exception $e ) {
70
  }
71
 
72
+ return $item;
73
  }
74
 
75
  /**
76
+ * @param string $signature
 
77
  * @return ResultItem|null
78
  */
79
+ private function scanForSig( string $signature ) {
80
+ $resultItem = null;
81
+ $lines = $this->locator->setNeedle( $signature )->run();
82
 
83
+ if ( !empty( $lines ) ) {
 
 
 
84
 
85
+ $fullPath = $this->locator->getPath();
86
+
87
+ if ( $this->canExcludeFile( $fullPath ) ) { // we report false positives: file and lines
88
+ $reporter = ( new Shield\Scans\Mal\Utilities\FalsePositiveReporter() )
89
  ->setMod( $this->getMod() );
90
+ foreach ( $lines as $linNum => $line ) {
91
+ $reporter->reportLine( $fullPath, $line, true );
92
  }
93
+ $reporter->reportPath( $fullPath, true );
94
  }
95
  else {
96
+ /** @var ScanActionVO $action */
97
+ $action = $this->getScanActionVO();
98
 
99
+ if ( $action->confidence_threshold > 0 ) {
100
+ $reportItem = false;
101
  // 1. First check whether the FP of the whole file means we can filter it
102
  $nFalsePositiveConfidence = ( new Shield\Scans\Mal\Utilities\FalsePositiveQuery() )
103
  ->setMod( $this->getMod() )
104
+ ->queryPath( $fullPath );
105
+ if ( $nFalsePositiveConfidence < $action->confidence_threshold ) {
106
  // 2. Check each line and filter out fp confident lines
107
  $aLineScores = ( new Shield\Scans\Mal\Utilities\FalsePositiveQuery() )
108
  ->setMod( $this->getMod() )
109
+ ->queryFileLines( $fullPath, array_keys( $lines ) );
110
+ $lines = array_filter(
111
  $aLineScores,
112
+ function ( $score ) use ( $action ) {
113
+ return $score < $action->confidence_threshold;
114
  }
115
  );
116
 
117
+ if ( empty( $lines ) ) {
118
  // Now send False Positive report for entire file based on all file lines being FPs.
119
  ( new Shield\Scans\Mal\Utilities\FalsePositiveReporter() )
120
  ->setMod( $this->getMod() )
121
+ ->reportPath( $fullPath, true );
122
  }
123
  else {
124
+ $reportItem = true;
125
  }
126
  }
127
  }
128
  else {
129
+ $reportItem = true;
130
  }
131
 
132
+ if ( $reportItem ) {
133
+ $resultItem = $this->getResultItemFromLines( array_keys( $lines ), $fullPath, $signature );
134
  }
135
  }
136
  }
137
+ return $resultItem;
138
  }
139
 
140
  /**
141
+ * @param string[] $lines
142
+ * @param string $fullPath
143
+ * @param string $signature
144
  * @return ResultItem
145
  */
146
+ private function getResultItemFromLines( array $lines, string $fullPath, string $signature ) :ResultItem {
147
  $oResultItem = new ResultItem();
148
+ $oResultItem->path_full = wp_normalize_path( $fullPath );
149
  $oResultItem->path_fragment = str_replace( wp_normalize_path( ABSPATH ), '', $oResultItem->path_full );
150
  $oResultItem->is_mal = true;
151
+ $oResultItem->mal_sig = base64_encode( $signature );
152
  $oResultItem->fp_confidence = 0;
153
+ $oResultItem->file_lines = $lines;
154
  return $oResultItem;
155
  }
156
 
157
  /**
158
+ * @param string $fullPath - normalized
159
  * @return bool
160
  */
161
+ private function canExcludeFile( string $fullPath ) :bool {
162
+ return $this->isValidCoreFile( $fullPath )
163
+ || $this->isPluginFileValid( $fullPath ) || $this->isThemeFileValid( $fullPath );
164
  }
165
 
166
  /**
167
+ * @param string $fullPath - normalized
168
  * @return bool
169
  */
170
+ private function isPluginFileValid( string $fullPath ) :bool {
171
+ $valid = false;
172
  try {
173
  $oPluginFiles = new Utilities\WpOrg\Plugin\Files();
174
+ $plugin = $oPluginFiles->findPluginFromFile( $fullPath );
175
+ if ( $plugin instanceof WpPluginVo ) {
176
+ $valid = $plugin->isWpOrg() ?
177
+ $oPluginFiles->verifyFileContents( $fullPath )
178
+ : $this->verifyPremiumAssetFile( $fullPath, $plugin );
179
  }
180
  }
181
+ catch ( \Exception $e ) {
182
  }
183
 
184
+ return $valid;
185
  }
186
 
187
  /**
188
+ * @param string $fullPath - normalized
189
  * @return bool
190
  */
191
+ private function isThemeFileValid( string $fullPath ) :bool {
192
+ $valid = false;
193
  try {
194
  $oThemeFiles = new Utilities\WpOrg\Theme\Files();
195
+ $theme = $oThemeFiles->findThemeFromFile( $fullPath );
196
+ if ( $theme instanceof WpThemeVo ) {
197
+ $valid = $theme->isWpOrg() ?
198
+ $oThemeFiles->verifyFileContents( $fullPath )
199
+ : $this->verifyPremiumAssetFile( $fullPath, $theme );
200
  }
201
  }
202
+ catch ( \Exception $e ) {
203
  }
204
 
205
+ return $valid;
206
  }
207
 
208
  /**
209
+ * @param string $fullPath
210
  * @param WpPluginVo|WpThemeVo $oPluginOrTheme
211
  * @return bool
212
  * @throws \Exception
213
  */
214
+ private function verifyPremiumAssetFile( $fullPath, $oPluginOrTheme ) :bool {
215
+ $valid = false;
216
+ $hashes = ( new Lib\Snapshots\Build\BuildHashesFromApi() )
217
  ->build( $oPluginOrTheme );
218
+ $fragment = str_replace( $oPluginOrTheme->getInstallDir(), '', $fullPath );
219
+ if ( !empty( $hashes ) && !empty( $hashes[ $fragment ] ) ) {
220
+ $valid = ( new Utilities\File\Compare\CompareHash() )
221
+ ->isEqualFileMd5( $fullPath, $hashes[ $fragment ] );
222
  }
223
+ return $valid;
224
  }
225
 
226
  /**
227
+ * @param string $fullPath
228
  * @return bool
229
  */
230
+ private function isValidCoreFile( $fullPath ) :bool {
231
+ $hash = Services::CoreFileHashes()->getFileHash( $fullPath );
232
  try {
233
+ $valid = !empty( $hash )
234
+ && ( new Utilities\File\Compare\CompareHash() )->isEqualFileMd5( $fullPath, $hash );
235
  }
236
+ catch ( \Exception $e ) {
237
+ $valid = false;
238
  }
239
+ return $valid;
240
  }
241
  }
src/lib/src/Scans/Mal/Scan.php CHANGED
@@ -17,19 +17,20 @@ class Scan extends Shield\Scans\Base\Files\BaseFileMapScan {
17
  protected function preScan() {
18
  parent::preScan();
19
 
20
- /** @var HackGuard\Options $oOpts */
21
- $oOpts = $this->getOptions();
22
 
23
- /** @var ScanActionVO $oScanVO */
24
- $oScanVO = $this->getScanActionVO();
25
 
26
- $oScanVO->confidence_threshold = $oOpts->getMalConfidenceBoundary();
27
 
28
- $aPatterns = ( new Utilities\Patterns() )
29
  ->setMod( $this->getMod() )
30
  ->retrieve();
31
- $oScanVO->patterns_simple = $aPatterns[ 'simple' ];
32
- $oScanVO->patterns_regex = $aPatterns[ 'regex' ];
 
33
  }
34
 
35
  /**
17
  protected function preScan() {
18
  parent::preScan();
19
 
20
+ /** @var HackGuard\Options $opts */
21
+ $opts = $this->getOptions();
22
 
23
+ /** @var ScanActionVO $action */
24
+ $action = $this->getScanActionVO();
25
 
26
+ $action->confidence_threshold = $opts->getMalConfidenceBoundary();
27
 
28
+ $patterns = ( new Utilities\Patterns() )
29
  ->setMod( $this->getMod() )
30
  ->retrieve();
31
+ $action->patterns_simple = $patterns[ 'simple' ];
32
+ $action->patterns_regex = $patterns[ 'regex' ];
33
+ $action->patterns_fullregex = $patterns[ 'fullregex' ] ?? [];
34
  }
35
 
36
  /**
src/lib/src/Scans/Mal/ScanActionVO.php CHANGED
@@ -1,4 +1,4 @@
1
- <?php
2
 
3
  namespace FernleafSystems\Wordpress\Plugin\Shield\Scans\Mal;
4
 
@@ -10,6 +10,7 @@ use FernleafSystems\Wordpress\Plugin\Shield\Scans\Base\BaseScanActionVO;
10
  * @property string[] $file_exts
11
  * @property string[] $scan_root_dirs
12
  * @property string[] $paths_whitelisted
 
13
  * @property string[] $patterns_regex
14
  * @property string[] $patterns_simple
15
  * @property int $confidence_threshold
1
+ <?php declare( strict_types=1 );
2
 
3
  namespace FernleafSystems\Wordpress\Plugin\Shield\Scans\Mal;
4
 
10
  * @property string[] $file_exts
11
  * @property string[] $scan_root_dirs
12
  * @property string[] $paths_whitelisted
13
+ * @property string[] $patterns_fullregex
14
  * @property string[] $patterns_regex
15
  * @property string[] $patterns_simple
16
  * @property int $confidence_threshold
src/lib/src/Scans/Mal/Table/EntryFormatter.php CHANGED
@@ -80,24 +80,24 @@ class EntryFormatter extends BaseFileEntryFormatter {
80
  * @inheritDoc
81
  */
82
  protected function getSupportedActions() {
83
- $aActions = parent::getSupportedActions();
84
 
85
- /** @var Mal\ResultItem $oIt */
86
- $oIt = $this->getResultItem();
87
 
88
  try {
89
  $bCanRepair = ( new Mal\Utilities\Repair() )
90
  ->setMod( $this->getMod() )
91
- ->setScanItem( $oIt )
92
  ->canRepair();
93
  }
94
- catch ( \Exception $oE ) {
95
  $bCanRepair = false;
96
  }
97
 
98
- $aActions[] = $bCanRepair ? 'repair' : 'delete';
99
- $aActions[] = 'download';
100
 
101
- return $aActions;
102
  }
103
  }
80
  * @inheritDoc
81
  */
82
  protected function getSupportedActions() {
83
+ $actions = parent::getSupportedActions();
84
 
85
+ /** @var Mal\ResultItem $item */
86
+ $item = $this->getResultItem();
87
 
88
  try {
89
  $bCanRepair = ( new Mal\Utilities\Repair() )
90
  ->setMod( $this->getMod() )
91
+ ->setScanItem( $item )
92
  ->canRepair();
93
  }
94
+ catch ( \Exception $e ) {
95
  $bCanRepair = false;
96
  }
97
 
98
+ $actions[] = $bCanRepair ? 'repair' : 'delete';
99
+ $actions[] = 'download';
100
 
101
+ return $actions;
102
  }
103
  }
src/lib/src/Scans/Mal/Utilities/FalsePositiveQuery.php CHANGED
@@ -15,71 +15,67 @@ class FalsePositiveQuery {
15
  use Modules\ModConsumer;
16
 
17
  /**
18
- * @param string $sFullPath
19
  * @param int[] $aLines
20
  * @return int[] - key is the file line number, value is the false positive confidence score
21
  */
22
- public function queryFileLines( $sFullPath, $aLines ) {
23
- $aScores = [];
24
- /** @var Modules\HackGuard\Options $oOpts */
25
- $oOpts = $this->getOptions();
26
- if ( $oOpts->isMalUseNetworkIntelligence() ) {
27
  try {
28
- $aFile = ( new ExtractLinesFromFile() )->run( $sFullPath, $aLines );
29
  foreach ( $aFile as $nLineNum => $sLine ) {
30
- $aScores[ $nLineNum ] = $this->queryLine( $sFullPath, $sLine );
31
  }
32
  }
33
- catch ( \Exception $oE ) {
34
  }
35
  }
36
- return $aScores;
37
  }
38
 
39
- /**
40
- * @param string $sFullPath
41
- * @return int
42
- */
43
- public function queryPath( $sFullPath ) {
44
  $nFpConfidence = 0;
45
 
46
- /** @var Modules\HackGuard\Options $oOpts */
47
- $oOpts = $this->getOptions();
48
- if ( $oOpts->isMalUseNetworkIntelligence() ) {
49
- $sApiToken = $this->getCon()
50
- ->getModule_License()
51
- ->getWpHashesTokenManager()
52
- ->getToken();
53
- $aData = ( new Malware\Confidence\Retrieve( $sApiToken ) )->retrieveForFile( $sFullPath );
54
- if ( isset( $aData[ 'score' ] ) ) {
55
- $nFpConfidence = (int)$aData[ 'score' ];
56
  }
57
  }
58
  return $nFpConfidence;
59
  }
60
 
61
  /**
62
- * @param string $sFile - path to file containing line
63
- * @param string $sLine
64
  * @return int
65
  */
66
- public function queryLine( $sFile, $sLine ) {
67
  $nFpConfidence = 0;
68
 
69
  /** @var Modules\HackGuard\Options $oOpts */
70
  $oOpts = $this->getOptions();
71
  if ( $oOpts->isMalUseNetworkIntelligence() ) {
72
- $sApiToken = $this->getCon()
73
  ->getModule_License()
74
  ->getWpHashesTokenManager()
75
  ->getToken();
76
  try {
77
- $aData = ( new Malware\Confidence\Retrieve( $sApiToken ) )->retrieveForFileLine( $sFile, $sLine );
78
  if ( isset( $aData[ 'score' ] ) ) {
79
  $nFpConfidence = (int)$aData[ 'score' ];
80
  }
81
  }
82
- catch ( \Exception $oE ) {
83
  }
84
  }
85
  return $nFpConfidence;
15
  use Modules\ModConsumer;
16
 
17
  /**
18
+ * @param string $fullPath
19
  * @param int[] $aLines
20
  * @return int[] - key is the file line number, value is the false positive confidence score
21
  */
22
+ public function queryFileLines( $fullPath, $aLines ) {
23
+ $scores = [];
24
+ /** @var Modules\HackGuard\Options $opts */
25
+ $opts = $this->getOptions();
26
+ if ( $opts->isMalUseNetworkIntelligence() ) {
27
  try {
28
+ $aFile = ( new ExtractLinesFromFile() )->run( $fullPath, $aLines );
29
  foreach ( $aFile as $nLineNum => $sLine ) {
30
+ $scores[ $nLineNum ] = $this->queryLine( $fullPath, $sLine );
31
  }
32
  }
33
+ catch ( \Exception $e ) {
34
  }
35
  }
36
+ return $scores;
37
  }
38
 
39
+ public function queryPath( string $fullPath ) :int {
 
 
 
 
40
  $nFpConfidence = 0;
41
 
42
+ /** @var Modules\HackGuard\Options $opts */
43
+ $opts = $this->getOptions();
44
+ if ( $opts->isMalUseNetworkIntelligence() ) {
45
+ $apiToken = $this->getCon()
46
+ ->getModule_License()
47
+ ->getWpHashesTokenManager()
48
+ ->getToken();
49
+ $data = ( new Malware\Confidence\Retrieve( $apiToken ) )->retrieveForFile( $fullPath );
50
+ if ( isset( $data[ 'score' ] ) ) {
51
+ $nFpConfidence = (int)$data[ 'score' ];
52
  }
53
  }
54
  return $nFpConfidence;
55
  }
56
 
57
  /**
58
+ * @param string $file - path to file containing line
59
+ * @param string $line
60
  * @return int
61
  */
62
+ public function queryLine( $file, $line ) {
63
  $nFpConfidence = 0;
64
 
65
  /** @var Modules\HackGuard\Options $oOpts */
66
  $oOpts = $this->getOptions();
67
  if ( $oOpts->isMalUseNetworkIntelligence() ) {
68
+ $token = $this->getCon()
69
  ->getModule_License()
70
  ->getWpHashesTokenManager()
71
  ->getToken();
72
  try {
73
+ $aData = ( new Malware\Confidence\Retrieve( $token ) )->retrieveForFileLine( $file, $line );
74
  if ( isset( $aData[ 'score' ] ) ) {
75
  $nFpConfidence = (int)$aData[ 'score' ];
76
  }
77
  }
78
+ catch ( \Exception $e ) {
79
  }
80
  }
81
  return $nFpConfidence;
src/lib/src/Scans/Mal/Utilities/FalsePositiveReporter.php CHANGED
@@ -106,7 +106,7 @@ class FalsePositiveReporter {
106
  ->report( $sFile, $sLine, $bIsFalsePositive );
107
  }
108
  }
109
- catch ( \Exception $oE ) {
110
  }
111
  }
112
  $this->updateReportedCache( $sReportHash );
106
  ->report( $sFile, $sLine, $bIsFalsePositive );
107
  }
108
  }
109
+ catch ( \Exception $e ) {
110
  }
111
  }
112
  $this->updateReportedCache( $sReportHash );
src/lib/src/Scans/Mal/Utilities/Patterns.php CHANGED
@@ -21,40 +21,40 @@ class Patterns {
21
  /** @var Modules\HackGuard\ModCon $mod */
22
  $mod = $this->getMod();
23
 
24
- $oCacheDef = new Cache\CacheDefVO();
25
- $oCacheDef->dir = $mod->getTempDir();
26
- if ( !empty( $oCacheDef->dir ) ) {
27
- $oCacheDef->file_fragment = 'cache_patterns.txt';
28
- $oCacheDef->expiration = HOUR_IN_SECONDS;
29
  ( new Cache\LoadFromCache() )
30
- ->setCacheDef( $oCacheDef )
31
  ->load();
32
  }
33
 
34
- if ( empty( $oCacheDef->data ) ) {
35
- $sApiToken = $this->getCon()
36
- ->getModule_License()
37
- ->getWpHashesTokenManager()
38
- ->getToken();
39
  // First attempt to download from WP Hashes API.
40
- $aPatts = ( new Malware\Patterns\Retrieve( $sApiToken ) )->getPatterns();
41
 
42
  // Fallback to original method
43
- if ( !is_array( $aPatts ) || empty( $aPatts[ 'simple' ] ) || empty( $aPatts[ 'regex' ] ) ) {
44
- /** @var Modules\HackGuard\Options $oOpts */
45
- $oOpts = $this->getOptions();
46
- $aPatts = [
47
- 'simple' => $oOpts->getMalSignaturesSimple(),
48
- 'regex' => $oOpts->getMalSignaturesRegex(),
49
  ];
50
  }
51
 
52
- $oCacheDef->data = $aPatts;
53
  ( new Cache\StoreToCache() )
54
- ->setCacheDef( $oCacheDef )
55
  ->store();
56
  }
57
 
58
- return $oCacheDef->data;
59
  }
60
  }
21
  /** @var Modules\HackGuard\ModCon $mod */
22
  $mod = $this->getMod();
23
 
24
+ $cacher = new Cache\CacheDefVO();
25
+ $cacher->dir = $mod->getTempDir();
26
+ if ( !empty( $cacher->dir ) ) {
27
+ $cacher->file_fragment = 'cache_patterns.txt';
28
+ $cacher->expiration = HOUR_IN_SECONDS;
29
  ( new Cache\LoadFromCache() )
30
+ ->setCacheDef( $cacher )
31
  ->load();
32
  }
33
 
34
+ if ( empty( $cacher->data ) ) {
35
+ $token = $this->getCon()
36
+ ->getModule_License()
37
+ ->getWpHashesTokenManager()
38
+ ->getToken();
39
  // First attempt to download from WP Hashes API.
40
+ $patterns = ( new Malware\Patterns\Retrieve( $token ) )->getPatterns();
41
 
42
  // Fallback to original method
43
+ if ( !is_array( $patterns ) || empty( $patterns[ 'simple' ] ) || empty( $patterns[ 'regex' ] ) ) {
44
+ /** @var Modules\HackGuard\Options $opts */
45
+ $opts = $this->getOptions();
46
+ $patterns = [
47
+ 'simple' => $opts->getMalSignaturesSimple(),
48
+ 'regex' => $opts->getMalSignaturesRegex(),
49
  ];
50
  }
51
 
52
+ $cacher->data = $patterns;
53
  ( new Cache\StoreToCache() )
54
+ ->setCacheDef( $cacher )
55
  ->store();
56
  }
57
 
58
+ return $cacher->data;
59
  }
60
  }
src/lib/src/Scans/Mal/Utilities/Repair.php CHANGED
@@ -19,62 +19,62 @@ class Repair extends Shield\Scans\Base\Utilities\BaseRepair {
19
  * @return bool
20
  */
21
  public function repairItem() {
22
- /** @var ResultItem $oItem */
23
- $oItem = $this->getScanItem();
24
- /** @var Shield\Modules\HackGuard\Options $oOpts */
25
- $oOpts = $this->getOptions();
26
- $bSuccess = false;
27
 
28
  try {
29
- $bCanRepair = $this->canRepair();
30
  }
31
  catch ( \Exception $e ) {
32
- $bCanRepair = false;
33
  }
34
 
35
- if ( $bCanRepair ) {
36
 
37
- if ( Services\Services::CoreFileHashes()->isCoreFile( $oItem->path_fragment ) ) {
38
- $bSuccess = $this->repairCoreItem( $oItem );
39
  }
40
  else {
41
- $oPlugin = ( new WpOrg\Plugin\Files() )->findPluginFromFile( $oItem->path_full );
42
- if ( $oPlugin instanceof Services\Core\VOs\WpPluginVo && $oPlugin->isWpOrg() ) {
43
 
44
- $bSuccess = $this->repairItemInPlugin( $oItem );
45
  }
46
  else {
47
- $oTheme = ( new WpOrg\Theme\Files() )->findThemeFromFile( $oItem->path_full );
48
- if ( $oTheme instanceof Services\Core\VOs\WpThemeVo && $oTheme->isWpOrg() ) {
49
 
50
- $bSuccess = $this->repairItemInTheme( $oItem );
51
  }
52
- elseif ( $oOpts->isMalAutoRepairSurgical() ) {
53
- $bSuccess = $this->repairSurgicalItem( $oItem );
54
  }
55
  }
56
  }
57
  }
58
  elseif ( $this->isAllowDelete() ) {
59
- $bSuccess = $this->repairItemByDelete( $oItem );
60
  }
61
 
62
- if ( $bSuccess ) {
63
  // 1) Report the file as being malware.
64
  ( new Shield\Scans\Mal\Utilities\FalsePositiveReporter() )
65
  ->setMod( $this->getMod() )
66
- ->reportResultItem( $oItem, false );
67
  }
68
 
69
- return $bSuccess;
70
  }
71
 
72
  /**
73
- * @param ResultItem $oItem
74
  * @return bool
75
  */
76
- private function repairItemByDelete( $oItem ) {
77
- return Services\Services::WpFs()->deleteFile( $oItem->path_full );
78
  }
79
 
80
  /**
@@ -151,7 +151,7 @@ class Repair extends Shield\Scans\Base\Utilities\BaseRepair {
151
  try {
152
  $bSuccess = $oFiles->replaceFileFromVcs( $oItem->path_fragment );
153
  }
154
- catch ( \InvalidArgumentException $oE ) {
155
  $bSuccess = false;
156
  }
157
  return $bSuccess;
@@ -168,7 +168,7 @@ class Repair extends Shield\Scans\Base\Utilities\BaseRepair {
168
  ( new Services\Utilities\File\RemoveLineFromFile() )->run( $oItem->path_full, $nLine );
169
  $bSuccess = true;
170
  }
171
- catch ( \Exception $oE ) {
172
  $bSuccess = false;
173
  break;
174
  }
@@ -181,21 +181,21 @@ class Repair extends Shield\Scans\Base\Utilities\BaseRepair {
181
  * @return bool
182
  */
183
  private function repairItemInPlugin( $oItem ) {
184
- $bSuccess = false;
185
 
186
  $oFiles = new WpOrg\Plugin\Files();
187
  try {
188
  if ( $oFiles->isValidFileFromPlugin( $oItem->path_full ) ) {
189
- $bSuccess = $oFiles->replaceFileFromVcs( $oItem->path_full );
190
  }
191
  elseif ( $this->isAllowDelete() ) {
192
- $bSuccess = (bool)Services\Services::WpFs()->deleteFile( $oItem->path_full );
193
  }
194
  }
195
- catch ( \InvalidArgumentException $oE ) {
196
  }
197
 
198
- return $bSuccess;
199
  }
200
 
201
  /**
@@ -203,20 +203,20 @@ class Repair extends Shield\Scans\Base\Utilities\BaseRepair {
203
  * @return bool
204
  */
205
  private function repairItemInTheme( $oItem ) {
206
- $bSuccess = false;
207
 
208
  $oFiles = new WpOrg\Theme\Files();
209
  try {
210
  if ( $oFiles->isValidFileFromTheme( $oItem->path_full ) ) {
211
- $bSuccess = $oFiles->replaceFileFromVcs( $oItem->path_full );
212
  }
213
  elseif ( $this->isAllowDelete() ) {
214
- $bSuccess = (bool)Services\Services::WpFs()->deleteFile( $oItem->path_full );
215
  }
216
  }
217
- catch ( \InvalidArgumentException $oE ) {
218
  }
219
 
220
- return $bSuccess;
221
  }
222
  }
19
  * @return bool
20
  */
21
  public function repairItem() {
22
+ /** @var ResultItem $item */
23
+ $item = $this->getScanItem();
24
+ /** @var Shield\Modules\HackGuard\Options $opts */
25
+ $opts = $this->getOptions();
26
+ $success = false;
27
 
28
  try {
29
+ $canRepair = $this->canRepair();
30
  }
31
  catch ( \Exception $e ) {
32
+ $canRepair = false;
33
  }
34
 
35
+ if ( $canRepair ) {
36
 
37
+ if ( Services\Services::CoreFileHashes()->isCoreFile( $item->path_fragment ) ) {
38
+ $success = $this->repairCoreItem( $item );
39
  }
40
  else {
41
+ $plugin = ( new WpOrg\Plugin\Files() )->findPluginFromFile( $item->path_full );
42
+ if ( $plugin instanceof Services\Core\VOs\WpPluginVo && $plugin->isWpOrg() ) {
43
 
44
+ $success = $this->repairItemInPlugin( $item );
45
  }
46
  else {
47
+ $theme = ( new WpOrg\Theme\Files() )->findThemeFromFile( $item->path_full );
48
+ if ( $theme instanceof Services\Core\VOs\WpThemeVo && $theme->isWpOrg() ) {
49
 
50
+ $success = $this->repairItemInTheme( $item );
51
  }
52
+ elseif ( $opts->isMalAutoRepairSurgical() ) {
53
+ $success = $this->repairSurgicalItem( $item );
54
  }
55
  }
56
  }
57
  }
58
  elseif ( $this->isAllowDelete() ) {
59
+ $success = $this->repairItemByDelete( $item );
60
  }
61
 
62
+ if ( $success ) {
63
  // 1) Report the file as being malware.
64
  ( new Shield\Scans\Mal\Utilities\FalsePositiveReporter() )
65
  ->setMod( $this->getMod() )
66
+ ->reportResultItem( $item, false );
67
  }
68
 
69
+ return $success;
70
  }
71
 
72
  /**
73
+ * @param ResultItem $item
74
  * @return bool
75
  */
76
+ private function repairItemByDelete( $item ) {
77
+ return Services\Services::WpFs()->deleteFile( $item->path_full );
78
  }
79
 
80
  /**
151
  try {
152
  $bSuccess = $oFiles->replaceFileFromVcs( $oItem->path_fragment );
153
  }
154
+ catch ( \InvalidArgumentException $e ) {
155
  $bSuccess = false;
156
  }
157
  return $bSuccess;
168
  ( new Services\Utilities\File\RemoveLineFromFile() )->run( $oItem->path_full, $nLine );
169
  $bSuccess = true;
170
  }
171
+ catch ( \Exception $e ) {
172
  $bSuccess = false;
173
  break;
174
  }
181
  * @return bool
182
  */
183
  private function repairItemInPlugin( $oItem ) {
184
+ $success = false;
185
 
186
  $oFiles = new WpOrg\Plugin\Files();
187
  try {
188
  if ( $oFiles->isValidFileFromPlugin( $oItem->path_full ) ) {
189
+ $success = $oFiles->replaceFileFromVcs( $oItem->path_full );
190
  }
191
  elseif ( $this->isAllowDelete() ) {
192
+ $success = (bool)Services\Services::WpFs()->deleteFile( $oItem->path_full );
193
  }
194
  }
195
+ catch ( \InvalidArgumentException $e ) {
196
  }
197
 
198
+ return $success;
199
  }
200
 
201
  /**
203
  * @return bool
204
  */
205
  private function repairItemInTheme( $oItem ) {
206
+ $success = false;
207
 
208
  $oFiles = new WpOrg\Theme\Files();
209
  try {
210
  if ( $oFiles->isValidFileFromTheme( $oItem->path_full ) ) {
211
+ $success = $oFiles->replaceFileFromVcs( $oItem->path_full );
212
  }
213
  elseif ( $this->isAllowDelete() ) {
214
+ $success = (bool)Services\Services::WpFs()->deleteFile( $oItem->path_full );
215
  }
216
  }
217
+ catch ( \InvalidArgumentException $e ) {
218
  }
219
 
220
+ return $success;
221
  }
222
  }
src/lib/src/Scans/Ptg/BuildFileMap.php CHANGED
@@ -33,14 +33,14 @@ class BuildFileMap {
33
  $aFiles[] = str_replace( $sAbsPath, '', wp_normalize_path( $oFsItem->getPathname() ) );
34
  }
35
  }
36
- catch ( \Exception $oE ) {
37
  }
38
  }
39
  }
40
- catch ( \Exception $oE ) {
41
  error_log(
42
  sprintf( 'Shield file scanner (%s) attempted to read directory (%s) but there was error: "%s".',
43
- $oAction->scan, $sScanDir, $oE->getMessage() )
44
  );
45
  }
46
  }
33
  $aFiles[] = str_replace( $sAbsPath, '', wp_normalize_path( $oFsItem->getPathname() ) );
34
  }
35
  }
36
+ catch ( \Exception $e ) {
37
  }
38
  }
39
  }
40
+ catch ( \Exception $e ) {
41
  error_log(
42
  sprintf( 'Shield file scanner (%s) attempted to read directory (%s) but there was error: "%s".',
43
+ $oAction->scan, $sScanDir, $e->getMessage() )
44
  );
45
  }
46
  }
src/lib/src/Scans/Ptg/FileScanner.php CHANGED
@@ -21,40 +21,40 @@ class FileScanner extends Shield\Scans\Base\Files\BaseFileScanner {
21
  private $oAssetStore;
22
 
23
  /**
24
- * @param string $sFullPath - in this case it's relative to ABSPATH
25
  * @return ResultItem|null
26
  */
27
- public function scan( $sFullPath ) {
28
- $oItem = null;
29
  // file paths are stored in the queue relatives to ABSPATH
30
- $sFullPath = path_join( wp_normalize_path( ABSPATH ), $sFullPath );
31
  try {
32
- $oAsset = ( new Plugin\Files() )->findPluginFromFile( $sFullPath );
33
- if ( empty( $oAsset ) ) {
34
- $oAsset = ( new Theme\Files() )->findThemeFromFile( $sFullPath );
35
  }
36
- if ( empty( $oAsset ) ) {
37
- throw new \Exception( 'Could not load asset' );
38
  }
39
 
40
- $aHashes = $this->getHashes( $oAsset );
41
- $sPathFragment = str_replace( $oAsset->getInstallDir(), '', $sFullPath );
42
- if ( empty( $aHashes[ $sPathFragment ] ) ) {
43
- $oItem = $this->getNewItem( $oAsset, $sFullPath );
44
- $oItem->path_fragment = $sPathFragment;
45
- $oItem->is_unrecognised = true;
46
  }
47
- elseif ( !( new CompareHash() )->isEqualFileMd5( $sFullPath, $aHashes[ $sPathFragment ] ) ) {
48
- $oItem = $this->getNewItem( $oAsset, $sFullPath );
49
- $oItem->path_fragment = $sPathFragment;
50
- $oItem->is_different = true;
51
  }
52
  }
53
- catch ( \Exception $oE ) {
54
- error_log( $oE->getMessage() );
55
  }
56
 
57
- return $oItem;
58
  }
59
 
60
  /**
21
  private $oAssetStore;
22
 
23
  /**
24
+ * @param string $fullPath - in this case it's relative to ABSPATH
25
  * @return ResultItem|null
26
  */
27
+ public function scan( string $fullPath ) {
28
+ $item = null;
29
  // file paths are stored in the queue relatives to ABSPATH
30
+ $fullPath = path_join( wp_normalize_path( ABSPATH ), $fullPath );
31
  try {
32
+ $asset = ( new Plugin\Files() )->findPluginFromFile( $fullPath );
33
+ if ( empty( $asset ) ) {
34
+ $asset = ( new Theme\Files() )->findThemeFromFile( $fullPath );
35
  }
36
+ if ( empty( $asset ) ) {
37
+ throw new \Exception( sprintf( 'Could not load asset for: %s', $fullPath ) );
38
  }
39
 
40
+ $assetHashes = $this->getHashes( $asset );
41
+ $pathFragment = str_replace( $asset->getInstallDir(), '', $fullPath );
42
+ if ( empty( $assetHashes[ $pathFragment ] ) ) {
43
+ $item = $this->getNewItem( $asset, $fullPath );
44
+ $item->path_fragment = $pathFragment;
45
+ $item->is_unrecognised = true;
46
  }
47
+ elseif ( !( new CompareHash() )->isEqualFileMd5( $fullPath, $assetHashes[ $pathFragment ] ) ) {
48
+ $item = $this->getNewItem( $asset, $fullPath );
49
+ $item->path_fragment = $pathFragment;
50
+ $item->is_different = true;
51
  }
52
  }
53
+ catch ( \Exception $e ) {
54
+ error_log( $e->getMessage() );
55
  }
56
 
57
+ return $item;
58
  }
59
 
60
  /**
src/lib/src/Scans/Ptg/Utilities/ItemActionHandler.php CHANGED
@@ -11,12 +11,12 @@ use FernleafSystems\Wordpress\Services\Services;
11
  class ItemActionHandler extends Base\Utilities\ItemActionHandlerAssets {
12
 
13
  /**
14
- * @param string $sAction
15
  * @return bool
16
  * @throws \Exception
17
  */
18
- public function process( $sAction ) {
19
- switch ( $sAction ) {
20
 
21
  case 'asset_accept':
22
  $bSuccess = $this->assetAccept();
@@ -27,7 +27,7 @@ class ItemActionHandler extends Base\Utilities\ItemActionHandlerAssets {
27
  break;
28
 
29
  default:
30
- $bSuccess = parent::process( $sAction );
31
  break;
32
  }
33
 
@@ -64,38 +64,38 @@ class ItemActionHandler extends Base\Utilities\ItemActionHandlerAssets {
64
  * @throws \Exception
65
  */
66
  protected function assetReinstall() {
67
- /** @var ResultItem $oItem */
68
- $oItem = $this->getScanItem();
69
 
70
- $bSuccess = false;
71
 
72
- $oWpP = Services::WpPlugins();
73
- $oWpT = Services::WpThemes();
74
- if ( $oWpP->isInstalled( $oItem->slug ) ) {
75
- $oAsset = $oWpP->getPluginAsVo( $oItem->slug );
76
- if ( $oAsset->isWpOrg() ) {
77
- $bSuccess = $oWpP->reinstall( $oItem->slug );
78
  }
79
  }
80
- elseif ( $oWpT->isInstalled( $oItem->slug ) ) {
81
- $oAsset = $oWpT->getThemeAsVo( $oItem->slug );
82
- if ( $oAsset->isWpOrg() ) {
83
- $bSuccess = $oWpT->reinstall( $oItem->slug );
84
  }
85
  }
86
 
87
- if ( $bSuccess ) {
88
  try {
89
  ( new Snapshots\StoreAction\Build() )
90
  ->setMod( $this->getMod() )
91
- ->setAsset( $this->getAssetFromSlug( $oItem->slug ) )
92
  ->run();
93
  }
94
- catch ( \Exception $oE ) {
95
  }
96
  }
97
 
98
- return $bSuccess;
99
  }
100
 
101
  /**
11
  class ItemActionHandler extends Base\Utilities\ItemActionHandlerAssets {
12
 
13
  /**
14
+ * @param string $action
15
  * @return bool
16
  * @throws \Exception
17
  */
18
+ public function process( $action ) {
19
+ switch ( $action ) {
20
 
21
  case 'asset_accept':
22
  $bSuccess = $this->assetAccept();
27
  break;
28
 
29
  default:
30
+ $bSuccess = parent::process( $action );
31
  break;
32
  }
33
 
64
  * @throws \Exception
65
  */
66
  protected function assetReinstall() {
67
+ /** @var ResultItem $item */
68
+ $item = $this->getScanItem();
69
 
70
+ $success = false;
71
 
72
+ $WPP = Services::WpPlugins();
73
+ $WPT = Services::WpThemes();
74
+ if ( $WPP->isInstalled( $item->slug ) ) {
75
+ $asset = $WPP->getPluginAsVo( $item->slug );
76
+ if ( $asset->isWpOrg() ) {
77
+ $success = $WPP->reinstall( $item->slug );
78
  }
79
  }
80
+ elseif ( $WPT->isInstalled( $item->slug ) ) {
81
+ $asset = $WPT->getThemeAsVo( $item->slug );
82
+ if ( $asset->isWpOrg() ) {
83
+ $success = $WPT->reinstall( $item->slug );
84
  }
85
  }
86
 
87
+ if ( $success ) {
88
  try {
89
  ( new Snapshots\StoreAction\Build() )
90
  ->setMod( $this->getMod() )
91
+ ->setAsset( $this->getAssetFromSlug( $item->slug ) )
92
  ->run();
93
  }
94
+ catch ( \Exception $e ) {
95
  }
96
  }
97
 
98
+ return $success;
99
  }
100
 
101
  /**
src/lib/src/Scans/Ptg/Utilities/Repair.php CHANGED
@@ -42,19 +42,19 @@ class Repair extends Scans\Base\Utilities\BaseRepair {
42
  * @return bool
43
  */
44
  private function repairPluginFile( $sPath ) {
45
- $bSuccess = false;
46
  $oFiles = new WpOrg\Plugin\Files();
47
  try {
48
  if ( $oFiles->isValidFileFromPlugin( $sPath ) ) {
49
- $bSuccess = $oFiles->replaceFileFromVcs( $sPath );
50
  }
51
  elseif ( $this->isAllowDelete() ) {
52
- $bSuccess = (bool)Services::WpFs()->deleteFile( $sPath );
53
  }
54
  }
55
- catch ( \InvalidArgumentException $oE ) {
56
  }
57
- return (bool)$bSuccess;
58
  }
59
 
60
  /**
@@ -62,19 +62,19 @@ class Repair extends Scans\Base\Utilities\BaseRepair {
62
  * @return bool
63
  */
64
  private function repairThemeFile( $sPath ) {
65
- $bSuccess = false;
66
  $oFiles = new WpOrg\Theme\Files();
67
  try {
68
  if ( $oFiles->isValidFileFromTheme( $sPath ) ) {
69
- $bSuccess = $oFiles->replaceFileFromVcs( $sPath );
70
  }
71
  elseif ( $this->isAllowDelete() ) {
72
- $bSuccess = (bool)Services::WpFs()->deleteFile( $sPath );
73
  }
74
  }
75
- catch ( \InvalidArgumentException $oE ) {
76
  }
77
- return (bool)$bSuccess;
78
  }
79
 
80
  /**
42
  * @return bool
43
  */
44
  private function repairPluginFile( $sPath ) {
45
+ $success = false;
46
  $oFiles = new WpOrg\Plugin\Files();
47
  try {
48
  if ( $oFiles->isValidFileFromPlugin( $sPath ) ) {
49
+ $success = $oFiles->replaceFileFromVcs( $sPath );
50
  }
51
  elseif ( $this->isAllowDelete() ) {
52
+ $success = (bool)Services::WpFs()->deleteFile( $sPath );
53
  }
54
  }
55
+ catch ( \InvalidArgumentException $e ) {
56
  }
57
+ return (bool)$success;
58
  }
59
 
60
  /**
62
  * @return bool
63
  */
64
  private function repairThemeFile( $sPath ) {
65
+ $success = false;
66
  $oFiles = new WpOrg\Theme\Files();
67
  try {
68
  if ( $oFiles->isValidFileFromTheme( $sPath ) ) {
69
+ $success = $oFiles->replaceFileFromVcs( $sPath );
70
  }
71
  elseif ( $this->isAllowDelete() ) {
72
+ $success = (bool)Services::WpFs()->deleteFile( $sPath );
73
  }
74
  }
75
+ catch ( \InvalidArgumentException $e ) {
76
  }
77
+ return (bool)$success;
78
  }
79
 
80
  /**
src/lib/src/Scans/Ufc/BuildFileMap.php CHANGED
@@ -44,10 +44,10 @@ class BuildFileMap {
44
  }
45
  }
46
  }
47
- catch ( \Exception $oE ) {
48
  error_log(
49
  sprintf( 'Shield file scanner (%s) attempted to read directory (%s) but there was error: "%s".',
50
- $oAction->scan, $sScanDir, $oE->getMessage() )
51
  );
52
  }
53
  }
44
  }
45
  }
46
  }
47
+ catch ( \Exception $e ) {
48
  error_log(
49
  sprintf( 'Shield file scanner (%s) attempted to read directory (%s) but there was error: "%s".',
50
+ $oAction->scan, $sScanDir, $e->getMessage() )
51
  );
52
  }
53
  }
src/lib/src/Scans/Ufc/FileScanner.php CHANGED
@@ -12,21 +12,21 @@ use FernleafSystems\Wordpress\Services\Services;
12
  class FileScanner extends Shield\Scans\Base\Files\BaseFileScanner {
13
 
14
  /**
15
- * @param string $sFullPath
16
  * @return ResultItem|null
17
  */
18
- public function scan( $sFullPath ) {
19
- $oResult = null;
20
 
21
- $sFullPath = wp_normalize_path( $sFullPath );
22
- if ( !$this->isExcluded( $sFullPath ) ) {
23
- /** @var ResultItem $oResult */
24
- $oResult = $this->getScanActionVO()->getNewResultItem();
25
- $oResult->path_full = $sFullPath;
26
- $oResult->path_fragment = Services::CoreFileHashes()->getFileFragment( $sFullPath );
27
  }
28
 
29
- return $oResult;
30
  }
31
 
32
  /**
12
  class FileScanner extends Shield\Scans\Base\Files\BaseFileScanner {
13
 
14
  /**
15
+ * @param string $fullPath
16
  * @return ResultItem|null
17
  */
18
+ public function scan( string $fullPath ) {
19
+ $item = null;
20
 
21
+ $fullPath = wp_normalize_path( $fullPath );
22
+ if ( !$this->isExcluded( $fullPath ) ) {
23
+ /** @var ResultItem $item */
24
+ $item = $this->getScanActionVO()->getNewResultItem();
25
+ $item->path_full = $fullPath;
26
+ $item->path_fragment = Services::CoreFileHashes()->getFileFragment( $fullPath );
27
  }
28
 
29
+ return $item;
30
  }
31
 
32
  /**
src/lib/src/Scans/Wcf/FileScanner.php CHANGED
@@ -13,17 +13,17 @@ use FernleafSystems\Wordpress\Services\Utilities\File\Compare\CompareHash;
13
  class FileScanner extends Shield\Scans\Base\Files\BaseFileScanner {
14
 
15
  /**
16
- * @param string $sFullPath
17
  * @return ResultItem|null
18
  */
19
- public function scan( $sFullPath ) {
20
  $oResult = null;
21
  $oHashes = Services::CoreFileHashes();
22
 
23
  /** @var ResultItem $oRes */
24
  $oRes = $this->getScanActionVO()->getNewResultItem();
25
- $oRes->path_full = $sFullPath;
26
- $oRes->path_fragment = $oHashes->getFileFragment( $sFullPath );
27
  $oRes->md5_file_wp = $oHashes->getFileHash( $oRes->path_fragment );
28
  $oRes->is_missing = !Services::WpFs()->exists( $oRes->path_full );
29
  $oRes->is_checksumfail = !$oRes->is_missing && $this->isChecksumFail( $oRes );
@@ -58,19 +58,19 @@ class FileScanner extends Shield\Scans\Base\Files\BaseFileScanner {
58
  }
59
 
60
  /**
61
- * @param ResultItem $oRes
62
  * @return bool
63
  */
64
- private function isChecksumFail( $oRes ) {
65
- $bFail = false;
66
- if ( !$oRes->is_missing ) {
67
  try {
68
- $bFail = ( strpos( $oRes->path_full, '.php' ) > 0 )
69
- && !( new CompareHash() )->isEqualFileMd5( $oRes->path_full, $oRes->md5_file_wp );
70
  }
71
- catch ( \Exception $oE ) {
72
  }
73
  }
74
- return $bFail;
75
  }
76
  }
13
  class FileScanner extends Shield\Scans\Base\Files\BaseFileScanner {
14
 
15
  /**
16
+ * @param string $fullPath
17
  * @return ResultItem|null
18
  */
19
+ public function scan( string $fullPath ) {
20
  $oResult = null;
21
  $oHashes = Services::CoreFileHashes();
22
 
23
  /** @var ResultItem $oRes */
24
  $oRes = $this->getScanActionVO()->getNewResultItem();
25
+ $oRes->path_full = $fullPath;
26
+ $oRes->path_fragment = $oHashes->getFileFragment( $fullPath );
27
  $oRes->md5_file_wp = $oHashes->getFileHash( $oRes->path_fragment );
28
  $oRes->is_missing = !Services::WpFs()->exists( $oRes->path_full );
29
  $oRes->is_checksumfail = !$oRes->is_missing && $this->isChecksumFail( $oRes );
58
  }
59
 
60
  /**
61
+ * @param ResultItem $item
62
  * @return bool
63
  */
64
+ private function isChecksumFail( $item ) {
65
+ $fail = false;
66
+ if ( !$item->is_missing ) {
67
  try {
68
+ $fail = ( strpos( $item->path_full, '.php' ) > 0 )
69
+ && !( new CompareHash() )->isEqualFileMd5( $item->path_full, $item->md5_file_wp );
70
  }
71
+ catch ( \Exception $e ) {
72
  }
73
  }
74
+ return $fail;
75
  }
76
  }
src/lib/src/Scans/Wpv/WpVulnDb/WpVulnVO.php CHANGED
@@ -1,10 +1,14 @@
1
- <?php
2
 
3
  namespace FernleafSystems\Wordpress\Plugin\Shield\Scans\Wpv\WpVulnDb;
4
 
 
 
5
  /**
6
  * Class WpVulnVO
 
7
  * @property int $id
 
8
  * @property string $title
9
  * @property string $vuln_type
10
  * @property string $fixed_in
@@ -12,14 +16,34 @@ namespace FernleafSystems\Wordpress\Plugin\Shield\Scans\Wpv\WpVulnDb;
12
  * @property int $updated_at
13
  * @property int $created_at
14
  * @property int $published_date
15
- * @package FernleafSystems\Wordpress\Plugin\Shield\Scans\Wpv\WpVulnDb
16
  */
17
- class WpVulnVO {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
18
 
19
- use \FernleafSystems\Utilities\Data\Adapter\StdClassAdapter;
 
 
 
 
 
20
 
21
  /**
22
  * @return string
 
23
  */
24
  public function getUrl() {
25
  return sprintf( 'https://wpvulndb.com/vulnerabilities/%s', $this->id );
1
+ <?php declare( strict_types=1 );
2
 
3
  namespace FernleafSystems\Wordpress\Plugin\Shield\Scans\Wpv\WpVulnDb;
4
 
5
+ use FernleafSystems\Utilities\Data\Adapter\DynamicPropertiesClass;
6
+
7
  /**
8
  * Class WpVulnVO
9
+ * @package FernleafSystems\Wordpress\Plugin\Shield\Scans\Wpv\WpVulnDb
10
  * @property int $id
11
+ * @property string $url
12
  * @property string $title
13
  * @property string $vuln_type
14
  * @property string $fixed_in
16
  * @property int $updated_at
17
  * @property int $created_at
18
  * @property int $published_date
 
19
  */
20
+ class WpVulnVO extends DynamicPropertiesClass {
21
+
22
+ const URL_BASE = 'https://wpscan.com/vulnerability/%s';
23
+
24
+ /**
25
+ * @inheritDoc
26
+ */
27
+ public function __get( string $key ) {
28
+ $val = parent::__get( $key );
29
+ switch ( $key ) {
30
+
31
+ case 'url':
32
+ if ( empty( $val ) ) {
33
+ $val = sprintf( self::URL_BASE, $this->id );
34
+ }
35
+ break;
36
 
37
+ default:
38
+ break;
39
+ }
40
+
41
+ return $val;
42
+ }
43
 
44
  /**
45
  * @return string
46
+ * @deprecated 10.2
47
  */
48
  public function getUrl() {
49
  return sprintf( 'https://wpvulndb.com/vulnerabilities/%s', $this->id );
src/lib/src/Tables/Build/Traffic.php CHANGED
@@ -4,13 +4,11 @@ namespace FernleafSystems\Wordpress\Plugin\Shield\Tables\Build;
4
 
5
  use FernleafSystems\Wordpress\Plugin\Shield\Databases;
6
  use FernleafSystems\Wordpress\Plugin\Shield\Modules\GeoIp\Lookup;
 
 
7
  use FernleafSystems\Wordpress\Plugin\Shield\Tables;
8
  use FernleafSystems\Wordpress\Services\Services;
9
 
10
- /**
11
- * Class Traffic
12
- * @package FernleafSystems\Wordpress\Plugin\Shield\Tables\Build
13
- */
14
  class Traffic extends BaseBuild {
15
 
16
  /**
@@ -18,36 +16,36 @@ class Traffic extends BaseBuild {
18
  * @return $this
19
  */
20
  protected function applyCustomQueryFilters() {
21
- $aParams = $this->getParams();
22
- /** @var Databases\Traffic\Select $oSelector */
23
- $oSelector = $this->getWorkingSelector();
24
 
25
  $oIp = Services::IP();
26
  // If an IP is specified, it takes priority
27
- if ( $oIp->isValidIp( $aParams[ 'fIp' ] ) ) {
28
- $oSelector->filterByIp( inet_pton( $aParams[ 'fIp' ] ) );
29
  }
30
- elseif ( $aParams[ 'fExcludeYou' ] == 'Y' ) {
31
- $oSelector->filterByNotIp( inet_pton( $oIp->getRequestIp() ) );
32
  }
33
 
34
  // if username is provided, this takes priority over "logged-in" (even if it's invalid)
35
- if ( !empty( $aParams[ 'fUsername' ] ) ) {
36
- $oUser = Services::WpUsers()->getUserByUsername( $aParams[ 'fUsername' ] );
37
  if ( !empty( $oUser ) ) {
38
- $oSelector->filterByUserId( $oUser->ID );
39
  }
40
  }
41
- elseif ( $aParams[ 'fLoggedIn' ] >= 0 ) {
42
- $oSelector->filterByIsLoggedIn( $aParams[ 'fLoggedIn' ] );
43
  }
44
 
45
- if ( $aParams[ 'fOffense' ] >= 0 ) {
46
- $oSelector->filterByIsTransgression( $aParams[ 'fOffense' ] );
47
  }
48
 
49
- $oSelector->filterByPathContains( $aParams[ 'fPath' ] );
50
- $oSelector->filterByResponseCode( $aParams[ 'fResponse' ] );
51
 
52
  return $this;
53
  }
@@ -73,7 +71,7 @@ class Traffic extends BaseBuild {
73
  * @return array[]
74
  */
75
  public function getEntriesFormatted() :array {
76
- $aEntries = [];
77
 
78
  $oWpUsers = Services::WpUsers();
79
  $oGeoIpLookup = ( new Lookup() )->setDbHandler( $this->getCon()
@@ -82,83 +80,122 @@ class Traffic extends BaseBuild {
82
  $srvIP = Services::IP();
83
  $you = $srvIP->getRequestIp();
84
 
85
- $aUsers = [ 0 => __( 'No', 'wp-simple-firewall' ) ];
86
- foreach ( $this->getEntriesRaw() as $nKey => $oEntry ) {
87
- /** @var Databases\Traffic\EntryVO $oEntry */
88
- $ip = $oEntry->ip;
 
89
 
90
- list( $sPreQuery, $sQuery ) = explode( '?', $oEntry->path.'?', 2 );
91
- $sQuery = trim( $sQuery, '?' );
92
- $sPath = strtoupper( $oEntry->verb ).': <code>'.$sPreQuery
93
- .( empty( $sQuery ) ? '' : '?<br/>'.$sQuery ).'</code>';
94
 
95
  $sCodeType = 'success';
96
- if ( $oEntry->code >= 400 ) {
97
  $sCodeType = 'danger';
98
  }
99
- elseif ( $oEntry->code >= 300 ) {
100
  $sCodeType = 'warning';
101
  }
102
 
103
- $aE = $oEntry->getRawDataAsArray();
104
- $aE[ 'path' ] = $sPath;
105
- $aE[ 'code' ] = sprintf( '<span class="badge badge-%s">%s</span>', $sCodeType, $oEntry->code );
106
- $aE[ 'trans' ] = sprintf(
107
  '<span class="badge badge-%s">%s</span>',
108
- $oEntry->trans ? 'danger' : 'info',
109
- $oEntry->trans ? __( 'Yes', 'wp-simple-firewall' ) : __( 'No', 'wp-simple-firewall' )
110
  );
111
- $aE[ 'ip' ] = $ip;
112
- $aE[ 'created_at' ] = $this->formatTimestampField( $oEntry->created_at );
113
 
114
  try {
115
- $aE[ 'is_you' ] = $srvIP->checkIp( $you, $oEntry->ip );
116
  }
117
  catch ( \Exception $e ) {
118
- $aE[ 'is_you' ] = false;
119
  }
120
- $sIpLink = sprintf( '%s%s',
121
- $this->getIpAnalysisLink( $oEntry->ip ),
122
- $aE[ 'is_you' ] ? ' <small>'.__( 'You', 'wp-simple-firewall' ).')</small>' : ''
123
  );
124
 
125
- if ( $oEntry->uid > 0 ) {
126
- if ( !isset( $aUsers[ $oEntry->uid ] ) ) {
127
- $oUser = $oWpUsers->getUserById( $oEntry->uid );
128
- $aUsers[ $oEntry->uid ] = empty( $oUser ) ? __( 'Unknown', 'wp-simple-firewall' ) :
129
  sprintf( '<a href="%s" target="_blank" title="Go To Profile">%s</a>',
130
- $oWpUsers->getAdminUrl_ProfileEdit( $oUser ), $oUser->user_login );
131
  }
132
  }
133
 
134
- $oGeoIp = $oGeoIpLookup
 
 
 
 
 
 
135
  ->setIP( $ip )
136
  ->lookupIp();
137
- $sCountryIso = $oGeoIp->getCountryCode();
138
- if ( empty( $sCountryIso ) ) {
139
- $sCountry = __( 'Unknown', 'wp-simple-firewall' );
140
  }
141
  else {
142
- $sFlag = sprintf( 'https://www.countryflags.io/%s/flat/16.png', strtolower( $sCountryIso ) );
143
- $sCountry = sprintf( '<img class="icon-flag" src="%s" alt="%s"/> %s', $sFlag, $sCountryIso, $oGeoIp->getCountryName() );
 
 
 
 
144
  }
145
 
146
- $aDetails = [
147
- sprintf( '%s: %s', __( 'IP', 'wp-simple-firewall' ), $sIpLink ),
148
- sprintf( '%s: %s', __( 'Logged-In', 'wp-simple-firewall' ), $aUsers[ $oEntry->uid ] ),
149
- sprintf( '%s: %s', __( 'Location', 'wp-simple-firewall' ), $sCountry ),
150
- esc_html( esc_js( sprintf( '%s - %s', __( 'User Agent', 'wp-simple-firewall' ), $oEntry->ua ) ) )
151
- ];
152
- $aE[ 'visitor' ] = '<div>'.implode( '</div><div>', $aDetails ).'</div>';
153
-
154
- $aInfo = [
155
- sprintf( '%s: %s', __( 'Response', 'wp-simple-firewall' ), $aE[ 'code' ] ),
156
- sprintf( '%s: %s', __( 'Offense', 'wp-simple-firewall' ), $aE[ 'trans' ] ),
157
- ];
158
- $aE[ 'request_info' ] = '<div>'.implode( '</div><div>', $aInfo ).'</div>';
159
- $aEntries[ $nKey ] = $aE;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
160
  }
161
- return $aEntries;
162
  }
163
 
164
  /**
4
 
5
  use FernleafSystems\Wordpress\Plugin\Shield\Databases;
6
  use FernleafSystems\Wordpress\Plugin\Shield\Modules\GeoIp\Lookup;
7
+ use FernleafSystems\Wordpress\Plugin\Shield\Modules\IPs\Lib\Ops\LookupIpOnList;
8
+ use FernleafSystems\Wordpress\Plugin\Shield\Modules\IPs\ModCon;
9
  use FernleafSystems\Wordpress\Plugin\Shield\Tables;
10
  use FernleafSystems\Wordpress\Services\Services;
11
 
 
 
 
 
12
  class Traffic extends BaseBuild {
13
 
14
  /**
16
  * @return $this
17
  */
18
  protected function applyCustomQueryFilters() {
19
+ $params = $this->getParams();
20
+ /** @var Databases\Traffic\Select $select */
21
+ $select = $this->getWorkingSelector();
22
 
23
  $oIp = Services::IP();
24
  // If an IP is specified, it takes priority
25
+ if ( $oIp->isValidIp( $params[ 'fIp' ] ) ) {
26
+ $select->filterByIp( inet_pton( $params[ 'fIp' ] ) );
27
  }
28
+ elseif ( $params[ 'fExcludeYou' ] == 'Y' ) {
29
+ $select->filterByNotIp( inet_pton( $oIp->getRequestIp() ) );
30
  }
31
 
32
  // if username is provided, this takes priority over "logged-in" (even if it's invalid)
33
+ if ( !empty( $params[ 'fUsername' ] ) ) {
34
+ $oUser = Services::WpUsers()->getUserByUsername( $params[ 'fUsername' ] );
35
  if ( !empty( $oUser ) ) {
36
+ $select->filterByUserId( $oUser->ID );
37
  }
38
  }
39
+ elseif ( $params[ 'fLoggedIn' ] >= 0 ) {
40
+ $select->filterByIsLoggedIn( $params[ 'fLoggedIn' ] );
41
  }
42
 
43
+ if ( $params[ 'fOffense' ] >= 0 ) {
44
+ $select->filterByIsTransgression( $params[ 'fOffense' ] );
45
  }
46
 
47
+ $select->filterByPathContains( $params[ 'fPath' ] );
48
+ $select->filterByResponseCode( $params[ 'fResponse' ] );
49
 
50
  return $this;
51
  }
71
  * @return array[]
72
  */
73
  public function getEntriesFormatted() :array {
74
+ $entries = [];
75
 
76
  $oWpUsers = Services::WpUsers();
77
  $oGeoIpLookup = ( new Lookup() )->setDbHandler( $this->getCon()
80
  $srvIP = Services::IP();
81
  $you = $srvIP->getRequestIp();
82
 
83
+ $users = [ 0 => __( 'No', 'wp-simple-firewall' ) ];
84
+ $ipInfos = [];
85
+ foreach ( $this->getEntriesRaw() as $key => $record ) {
86
+ /** @var Databases\Traffic\EntryVO $record */
87
+ $ip = $record->ip;
88
 
89
+ list( $preQuery, $query ) = explode( '?', $record->path.'?', 2 );
90
+ $query = trim( $query, '?' );
91
+ $sPath = strtoupper( $record->verb ).': <code>'.$preQuery
92
+ .( empty( $query ) ? '' : '?<br/>'.$query ).'</code>';
93
 
94
  $sCodeType = 'success';
95
+ if ( $record->code >= 400 ) {
96
  $sCodeType = 'danger';
97
  }
98
+ elseif ( $record->code >= 300 ) {
99
  $sCodeType = 'warning';
100
  }
101
 
102
+ $e = $record->getRawDataAsArray();
103
+ $e[ 'path' ] = $sPath;
104
+ $e[ 'code' ] = sprintf( '<span class="badge badge-%s">%s</span>', $sCodeType, $record->code );
105
+ $e[ 'trans' ] = sprintf(
106
  '<span class="badge badge-%s">%s</span>',
107
+ $record->trans ? 'danger' : 'info',
108
+ $record->trans ? __( 'Yes', 'wp-simple-firewall' ) : __( 'No', 'wp-simple-firewall' )
109
  );
110
+ $e[ 'ip' ] = $ip;
111
+ $e[ 'created_at' ] = $this->formatTimestampField( $record->created_at );
112
 
113
  try {
114
+ $e[ 'is_you' ] = $srvIP->checkIp( $you, $record->ip );
115
  }
116
  catch ( \Exception $e ) {
117
+ $e[ 'is_you' ] = false;
118
  }
119
+ $ipLink = sprintf( '%s%s',
120
+ $this->getIpAnalysisLink( $record->ip ),
121
+ $e[ 'is_you' ] ? ' <small>('.__( 'This Is You', 'wp-simple-firewall' ).')</small>' : ''
122
  );
123
 
124
+ if ( $record->uid > 0 ) {
125
+ if ( !isset( $users[ $record->uid ] ) ) {
126
+ $user = $oWpUsers->getUserById( $record->uid );
127
+ $users[ $record->uid ] = empty( $user ) ? __( 'Unknown', 'wp-simple-firewall' ) :
128
  sprintf( '<a href="%s" target="_blank" title="Go To Profile">%s</a>',
129
+ $oWpUsers->getAdminUrl_ProfileEdit( $user ), $user->user_login );
130
  }
131
  }
132
 
133
+ if ( !empty( $record->ip ) ) {
134
+ if ( !isset( $ipInfos[ $record->ip ] ) ) {
135
+ $ipInfos[ $record->ip ] = $this->getIpInfo( $record->ip );
136
+ }
137
+ }
138
+
139
+ $geoIP = $oGeoIpLookup
140
  ->setIP( $ip )
141
  ->lookupIp();
142
+ $countryISO = $geoIP->getCountryCode();
143
+ if ( empty( $countryISO ) ) {
144
+ $country = __( 'Unknown', 'wp-simple-firewall' );
145
  }
146
  else {
147
+ $country = sprintf(
148
+ '<img class="icon-flag" src="%s" alt="%s"/> %s',
149
+ sprintf( 'https://www.countryflags.io/%s/flat/16.png', strtolower( $countryISO ) ),
150
+ $countryISO,
151
+ $geoIP->getCountryName()
152
+ );
153
  }
154
 
155
+ $e[ 'visitor' ] = sprintf( '<div>%s</div>', implode( '</div><div>', [
156
+ sprintf( '%s: %s', __( 'IP', 'wp-simple-firewall' ), $ipLink ),
157
+ sprintf( '%s: %s', __( 'IP Status', 'wp-simple-firewall' ), $ipInfos[ $record->ip ] ?? 'n/a' ),
158
+ sprintf( '%s: %s', __( 'Logged-In', 'wp-simple-firewall' ), $users[ $record->uid ] ),
159
+ sprintf( '%s: %s', __( 'Location', 'wp-simple-firewall' ), $country ),
160
+ esc_html( esc_js( sprintf( '%s - %s', __( 'User Agent', 'wp-simple-firewall' ), $record->ua ) ) ),
161
+ ] ) );
162
+
163
+ $e[ 'request_info' ] = sprintf( '<div>%s</div>', implode( '</div><div>', [
164
+ sprintf( '%s: %s', __( 'Response', 'wp-simple-firewall' ), $e[ 'code' ] ),
165
+ sprintf( '%s: %s', __( 'Offense', 'wp-simple-firewall' ), $e[ 'trans' ] ),
166
+ ] ) );
167
+
168
+ $entries[ $key ] = $e;
169
+ }
170
+ return $entries;
171
+ }
172
+
173
+ private function getIpInfo( string $ip ) :string {
174
+ $record = ( new LookupIpOnList() )
175
+ ->setDbHandler( $this->getCon()->getModule_IPs()->getDbHandler_IPs() )
176
+ ->setIP( $ip )
177
+ ->lookup();
178
+
179
+ $badgeTemplate = '<span class="badge badge-%s">%s</span>';
180
+ if ( $record->blocked_at > 0 || $record->list === ModCon::LIST_MANUAL_BLACK ) {
181
+ $status = sprintf( $badgeTemplate, 'danger', __( 'Blocked', 'wp-simple-firewall' ) );
182
+ }
183
+ elseif ( $record->list === ModCon::LIST_AUTO_BLACK ) {
184
+ $status = sprintf( $badgeTemplate,
185
+ 'warning',
186
+ sprintf( _n( '%s offense', '%s offenses', $record->transgressions, 'wp-simple-firewall' ), $record->transgressions )
187
+ );
188
+ }
189
+ elseif ( $record->list === ModCon::LIST_MANUAL_WHITE ) {
190
+ $status = sprintf( $badgeTemplate,
191
+ 'success',
192
+ __( 'Bypass', 'wp-simple-firewall' )
193
+ );
194
+ }
195
+ else {
196
+ $status = __( 'No Record', 'wp-simple-firewall' );
197
  }
198
+ return $status;
199
  }
200
 
201
  /**
src/lib/src/Tables/Render/WpListTable/Base.php CHANGED
@@ -42,12 +42,12 @@ class Base extends \WP_List_Table {
42
  }
43
 
44
  /**
45
- * @param object $aItem
46
- * @param string $sColName
47
  * @return string
48
  */
49
- public function column_default( $aItem, $sColName ) {
50
- return $aItem[ $sColName ];
51
  }
52
 
53
  /**
42
  }
43
 
44
  /**
45
+ * @param object $item
46
+ * @param string $colName
47
  * @return string
48
  */
49
+ public function column_default( $item, $colName ) {
50
+ return $item[ $colName ];
51
  }
52
 
53
  /**
src/lib/src/Tables/Render/WpListTable/ScanWpv.php CHANGED
@@ -41,20 +41,20 @@ class ScanWpv extends ScanBase {
41
  }
42
 
43
  /**
44
- * @param array $aItem
45
  * @return string
46
  */
47
- public function column_vulnerability( $aItem ) {
48
- /** @var Scans\Wpv\WpVulnDb\WpVulnVO $oVo */
49
- $oVo = $aItem[ 'wpvuln_vo' ];
50
- $sContent = sprintf( '<span class="vuln-title">%s</span>', $oVo->title );
51
 
52
- $aButtons = [
53
- $this->getActionButton_Ignore( $aItem[ 'id' ] ),
54
  sprintf( '<a href="%s" class="btn btn-sm btn-link text-info" target="_blank">%s</a>',
55
- $oVo->getUrl(), __( 'More Info', 'wp-simple-firewall' ) ),
56
  ];
57
- return $sContent.$this->buildActions( $aButtons );
58
  }
59
 
60
  /**
41
  }
42
 
43
  /**
44
+ * @param array $item
45
  * @return string
46
  */
47
+ public function column_vulnerability( $item ) {
48
+ /** @var Scans\Wpv\WpVulnDb\WpVulnVO $vuln */
49
+ $vuln = $item[ 'wpvuln_vo' ];
50
+ $sContent = sprintf( '<span class="vuln-title">%s</span>', $vuln->title );
51
 
52
+ $buttons = [
53
+ $this->getActionButton_Ignore( $item[ 'id' ] ),
54
  sprintf( '<a href="%s" class="btn btn-sm btn-link text-info" target="_blank">%s</a>',
55
+ $vuln->url, __( 'More Info', 'wp-simple-firewall' ) ),
56
  ];
57
+ return $sContent.$this->buildActions( $buttons );
58
  }
59
 
60
  /**
src/lib/src/Utilities/ReCaptcha/TestRequest.php CHANGED
@@ -20,9 +20,9 @@ class TestRequest {
20
  $this->runTest();
21
  $this->getCon()->fireEvent( 'recaptcha_success' );
22
  }
23
- catch ( \Exception $oE ) {
24
  $this->getCon()->fireEvent( 'recaptcha_fail' );
25
- throw $oE;
26
  }
27
  return true;
28
  }
20
  $this->runTest();
21
  $this->getCon()->fireEvent( 'recaptcha_success' );
22
  }
23
+ catch ( \Exception $e ) {
24
  $this->getCon()->fireEvent( 'recaptcha_fail' );
25
+ throw $e;
26
  }
27
  return true;
28
  }
src/lib/src/Utilities/Time/WorldTimeApi.php ADDED
@@ -0,0 +1,33 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php declare( strict_types=1 );
2
+
3
+ namespace FernleafSystems\Wordpress\Plugin\Shield\Utilities\Time;
4
+
5
+ use FernleafSystems\Wordpress\Services\Services;
6
+
7
+ class WorldTimeApi {
8
+
9
+ /**
10
+ * @return int
11
+ * @throws \Exception
12
+ */
13
+ public function current() :int {
14
+ $raw = Services::HttpRequest()
15
+ ->getContent( 'https://showcase.api.linx.twenty57.net/UnixTime/tounixtimestamp?datetime=now' );
16
+ if ( empty( $raw ) ) {
17
+ throw new \Exception( 'Request to World Clock Api Failed' );
18
+ }
19
+ $dec = json_decode( $raw, true );
20
+ if ( empty( $dec ) ) {
21
+ throw new \Exception( 'Failed to decode World Clock Api response' );
22
+ }
23
+ return (int)$dec[ 'UnixTimeStamp' ];
24
+ }
25
+
26
+ /**
27
+ * @return int
28
+ * @throws \Exception
29
+ */
30
+ public function diffServerWithReal() :int {
31
+ return 13;// time() - $this->current();
32
+ }
33
+ }
src/lib/vendor/autoload.php CHANGED
@@ -4,4 +4,4 @@
4
 
5
  require_once __DIR__ . '/composer/autoload_real.php';
6
 
7
- return ComposerAutoloaderInit0b573d8879ea3b08a114d68dbb7a4533::getLoader();
4
 
5
  require_once __DIR__ . '/composer/autoload_real.php';
6
 
7
+ return ComposerAutoloaderInit4fc2c6daaffaf40b64b79b6d26830171::getLoader();
src/lib/vendor/composer/ClassLoader.php CHANGED
@@ -37,11 +37,13 @@ namespace Composer\Autoload;
37
  *
38
  * @author Fabien Potencier <fabien@symfony.com>
39
  * @author Jordi Boggiano <j.boggiano@seld.be>
40
- * @see http://www.php-fig.org/psr/psr-0/
41
- * @see http://www.php-fig.org/psr/psr-4/
42
  */
43
  class ClassLoader
44
  {
 
 
45
  // PSR-4
46
  private $prefixLengthsPsr4 = array();
47
  private $prefixDirsPsr4 = array();
@@ -57,6 +59,13 @@ class ClassLoader
57
  private $missingClasses = array();
58
  private $apcuPrefix;
59
 
 
 
 
 
 
 
 
60
  public function getPrefixes()
61
  {
62
  if (!empty($this->prefixesPsr0)) {
@@ -300,6 +309,15 @@ class ClassLoader
300
  public function register($prepend = false)
301
  {
302
  spl_autoload_register(array($this, 'loadClass'), true, $prepend);
 
 
 
 
 
 
 
 
 
303
  }
304
 
305
  /**
@@ -308,6 +326,10 @@ class ClassLoader
308
  public function unregister()
309
  {
310
  spl_autoload_unregister(array($this, 'loadClass'));
 
 
 
 
311
  }
312
 
313
  /**
@@ -367,6 +389,16 @@ class ClassLoader
367
  return $file;
368
  }
369
 
 
 
 
 
 
 
 
 
 
 
370
  private function findFileWithExtension($class, $ext)
371
  {
372
  // PSR-4 lookup
37
  *
38
  * @author Fabien Potencier <fabien@symfony.com>
39
  * @author Jordi Boggiano <j.boggiano@seld.be>
40
+ * @see https://www.php-fig.org/psr/psr-0/
41
+ * @see https://www.php-fig.org/psr/psr-4/
42
  */
43
  class ClassLoader
44
  {
45
+ private $vendorDir;
46
+
47
  // PSR-4
48
  private $prefixLengthsPsr4 = array();
49
  private $prefixDirsPsr4 = array();
59
  private $missingClasses = array();
60
  private $apcuPrefix;
61
 
62
+ private static $registeredLoaders = array();
63
+
64
+ public function __construct($vendorDir = null)
65
+ {
66
+ $this->vendorDir = $vendorDir;
67
+ }
68
+
69
  public function getPrefixes()
70
  {
71
  if (!empty($this->prefixesPsr0)) {
309
  public function register($prepend = false)
310
  {
311
  spl_autoload_register(array($this, 'loadClass'), true, $prepend);
312
+
313
+ if (null === $this->vendorDir) {
314
+ //no-op
315
+ } elseif ($prepend) {
316
+ self::$registeredLoaders = array($this->vendorDir => $this) + self::$registeredLoaders;
317
+ } else {
318
+ unset(self::$registeredLoaders[$this->vendorDir]);
319
+ self::$registeredLoaders[$this->vendorDir] = $this;
320
+ }
321
  }
322
 
323
  /**
326
  public function unregister()
327
  {
328
  spl_autoload_unregister(array($this, 'loadClass'));
329
+
330
+ if (null !== $this->vendorDir) {
331
+ unset(self::$registeredLoaders[$this->vendorDir]);
332
+ }
333
  }
334
 
335
  /**
389
  return $file;
390
  }
391
 
392
+ /**
393
+ * Returns the currently registered loaders indexed by their corresponding vendor directories.
394
+ *
395
+ * @return self[]
396
+ */
397
+ public static function getRegisteredLoaders()
398
+ {
399
+ return self::$registeredLoaders;
400
+ }
401
+
402
  private function findFileWithExtension($class, $ext)
403
  {
404
  // PSR-4 lookup
src/lib/vendor/composer/autoload_classmap.php CHANGED
@@ -14,6 +14,7 @@ return array(
14
  'Carbon\\Laravel\\ServiceProvider' => $vendorDir . '/nesbot/carbon/src/Carbon/Laravel/ServiceProvider.php',
15
  'Carbon\\Translator' => $vendorDir . '/nesbot/carbon/src/Carbon/Translator.php',
16
  'Carbon\\Upgrade' => $vendorDir . '/nesbot/carbon/src/Carbon/Upgrade.php',
 
17
  'Dolondro\\GoogleAuthenticator\\GoogleAuthenticator' => $vendorDir . '/dolondro/google-authenticator/src/GoogleAuthenticator.php',
18
  'Dolondro\\GoogleAuthenticator\\QrImageGenerator\\EndroidQrImageGenerator' => $vendorDir . '/dolondro/google-authenticator/src/QrImageGenerator/EndroidQrImageGenerator.php',
19
  'Dolondro\\GoogleAuthenticator\\QrImageGenerator\\GoogleQrImageGenerator' => $vendorDir . '/dolondro/google-authenticator/src/QrImageGenerator/GoogleQrImageGenerator.php',
@@ -21,10 +22,12 @@ return array(
21
  'Dolondro\\GoogleAuthenticator\\Secret' => $vendorDir . '/dolondro/google-authenticator/src/Secret.php',
22
  'Dolondro\\GoogleAuthenticator\\SecretFactory' => $vendorDir . '/dolondro/google-authenticator/src/SecretFactory.php',
23
  'Elliotchance\\Iterator\\AbstractPagedIterator' => $vendorDir . '/elliotchance/iterator/src/Elliotchance/Iterator/AbstractPagedIterator.php',
24
- 'Elliotchance\\Iterator\\PagedIterator1' => $vendorDir . '/elliotchance/iterator/tests/Elliotchance/Iterator/PagedIteratorTest.php',
25
- 'Elliotchance\\Iterator\\PagedIterator2' => $vendorDir . '/elliotchance/iterator/tests/Elliotchance/Iterator/PagedIteratorTest.php',
26
  'Elliotchance\\Iterator\\PagedIteratorTest' => $vendorDir . '/elliotchance/iterator/tests/Elliotchance/Iterator/PagedIteratorTest.php',
 
 
27
  'FernleafSystems\\Utilities\\Data\\Adapter\\StdClassAdapter' => $vendorDir . '/fernleafsystems/utilities/src/Data/Adapter/StdClassAdapter.php',
 
 
28
  'FernleafSystems\\Utilities\\Logic\\OneTimeExecute' => $vendorDir . '/fernleafsystems/utilities/src/Logic/OneTimeExecute.php',
29
  'FernleafSystems\\Utilities\\Response' => $vendorDir . '/fernleafsystems/utilities/src/Response.php',
30
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\ChangeTrack\\Diff\\Base' => $baseDir . '/src/ChangeTrack/Diff/Base.php',
@@ -52,6 +55,13 @@ return array(
52
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\ChangeTrack\\Snapshot\\BuildUsers' => $baseDir . '/src/ChangeTrack/Snapshot/BuildUsers.php',
53
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\ChangeTrack\\Snapshot\\Collate' => $baseDir . '/src/ChangeTrack/Snapshot/Collate.php',
54
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\ChangeTrack\\Snapshot\\SnapshotsConsumer' => $baseDir . '/src/ChangeTrack/Snapshot/SnapshotsConsumer.php',
 
 
 
 
 
 
 
55
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Controller\\Config\\ConfigVO' => $baseDir . '/src/Controller/Config/ConfigVO.php',
56
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Controller\\Config\\Ops\\LoadConfig' => $baseDir . '/src/Controller/Config/Ops/LoadConfig.php',
57
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Controller\\Config\\Ops\\Read' => $baseDir . '/src/Controller/Config/Ops/Read.php',
@@ -196,10 +206,7 @@ return array(
196
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\BaseShield\\UI' => $baseDir . '/src/Modules/BaseShield/UI.php',
197
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Base\\AdminNotices' => $baseDir . '/src/Modules/Base/AdminNotices.php',
198
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Base\\AjaxHandler' => $baseDir . '/src/Modules/Base/AjaxHandler.php',
199
- 'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Base\\AjaxHandlerBase' => $baseDir . '/src/Modules/Base/AjaxHandlerBase.php',
200
- 'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Base\\AjaxHandlerShield' => $baseDir . '/src/Modules/Base/AjaxHandlerShield.php',
201
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Base\\BaseProcessor' => $baseDir . '/src/Modules/Base/BaseProcessor.php',
202
- 'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Base\\BaseReporting' => $baseDir . '/src/Modules/Base/BaseReporting.php',
203
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Base\\Debug' => $baseDir . '/src/Modules/Base/Debug.php',
204
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Base\\Insights\\OverviewCards' => $baseDir . '/src/Modules/Base/Insights/OverviewCards.php',
205
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Base\\ModCon' => $baseDir . '/src/Modules/Base/ModCon.php',
@@ -207,8 +214,6 @@ return array(
207
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Base\\Options\\OptValueSanitize' => $baseDir . '/src/Modules/Base/Options/OptValueSanitize.php',
208
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Base\\Processor' => $baseDir . '/src/Modules/Base/Processor.php',
209
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Base\\Reporting' => $baseDir . '/src/Modules/Base/Reporting.php',
210
- 'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Base\\ShieldOptions' => $baseDir . '/src/Modules/Base/ShieldOptions.php',
211
- 'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Base\\ShieldUI' => $baseDir . '/src/Modules/Base/ShieldUI.php',
212
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Base\\Strings' => $baseDir . '/src/Modules/Base/Strings.php',
213
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Base\\UI' => $baseDir . '/src/Modules/Base/UI.php',
214
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Base\\Upgrade' => $baseDir . '/src/Modules/Base/Upgrade.php',
@@ -262,6 +267,7 @@ return array(
262
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Firewall\\UI' => $baseDir . '/src/Modules/Firewall/UI.php',
263
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\GeoIp\\Lookup' => $baseDir . '/src/Modules/GeoIp/Lookup.php',
264
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\HackGuard\\AjaxHandler' => $baseDir . '/src/Modules/HackGuard/AjaxHandler.php',
 
265
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\HackGuard\\Insights\\OverviewCards' => $baseDir . '/src/Modules/HackGuard/Insights/OverviewCards.php',
266
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\HackGuard\\Lib\\FileLocker\\File' => $baseDir . '/src/Modules/HackGuard/Lib/FileLocker/File.php',
267
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\HackGuard\\Lib\\FileLocker\\FileLockerController' => $baseDir . '/src/Modules/HackGuard/Lib/FileLocker/FileLockerController.php',
@@ -420,6 +426,7 @@ return array(
420
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\License\\Lib\\LicenseEmails' => $baseDir . '/src/Modules/License/Lib/LicenseEmails.php',
421
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\License\\Lib\\LicenseHandler' => $baseDir . '/src/Modules/License/Lib/LicenseHandler.php',
422
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\License\\Lib\\LookupRequest' => $baseDir . '/src/Modules/License/Lib/LookupRequest.php',
 
423
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\License\\Lib\\Verify' => $baseDir . '/src/Modules/License/Lib/Verify.php',
424
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\License\\Lib\\WpHashes\\ApiTokenManager' => $baseDir . '/src/Modules/License/Lib/WpHashes/ApiTokenManager.php',
425
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\License\\ModCon' => $baseDir . '/src/Modules/License/ModCon.php',
@@ -741,6 +748,7 @@ return array(
741
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Utilities\\ReCaptcha\\Enqueue' => $baseDir . '/src/Utilities/ReCaptcha/Enqueue.php',
742
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Utilities\\ReCaptcha\\TestRequest' => $baseDir . '/src/Utilities/ReCaptcha/TestRequest.php',
743
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Utilities\\ReCaptcha\\WordpressPost' => $baseDir . '/src/Utilities/ReCaptcha/WordpressPost.php',
 
744
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Utilities\\Tool\\FormatBytes' => $baseDir . '/src/Utilities/Tool/FormatBytes.php',
745
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Utilities\\Tool\\IpListSort' => $baseDir . '/src/Utilities/Tool/IpListSort.php',
746
  'FernleafSystems\\Wordpress\\Services\\Core\\AdminNotices' => $vendorDir . '/fernleafsystems/wordpress-services/src/Core/AdminNotices.php',
@@ -892,59 +900,7 @@ return array(
892
  'FernleafSystems\\Wordpress\\Services\\Utilities\\WpOrg\\Wp\\Versions' => $vendorDir . '/fernleafsystems/wordpress-services/src/Utilities/WpOrg/Wp/Versions.php',
893
  'Html2Text\\Html2Text' => $vendorDir . '/soundasleep/html2text/src/Html2Text.php',
894
  'Html2Text\\Html2TextException' => $vendorDir . '/soundasleep/html2text/src/Html2TextException.php',
895
- 'ICWP_WPSF_FeatureHandler_AdminAccessRestriction' => $baseDir . '/../features/admin_access_restriction.php',
896
- 'ICWP_WPSF_FeatureHandler_AuditTrail' => $baseDir . '/../features/audit_trail.php',
897
- 'ICWP_WPSF_FeatureHandler_Autoupdates' => $baseDir . '/../features/autoupdates.php',
898
- 'ICWP_WPSF_FeatureHandler_Base' => $baseDir . '/../features/base.php',
899
- 'ICWP_WPSF_FeatureHandler_BaseWpsf' => $baseDir . '/../features/base_wpsf.php',
900
- 'ICWP_WPSF_FeatureHandler_CommentsFilter' => $baseDir . '/../features/comments_filter.php',
901
- 'ICWP_WPSF_FeatureHandler_Comms' => $baseDir . '/../features/comms.php',
902
- 'ICWP_WPSF_FeatureHandler_Email' => $baseDir . '/../features/email.php',
903
- 'ICWP_WPSF_FeatureHandler_Events' => $baseDir . '/../features/events.php',
904
- 'ICWP_WPSF_FeatureHandler_Firewall' => $baseDir . '/../features/firewall.php',
905
- 'ICWP_WPSF_FeatureHandler_HackProtect' => $baseDir . '/../features/hack_protect.php',
906
- 'ICWP_WPSF_FeatureHandler_Headers' => $baseDir . '/../features/headers.php',
907
- 'ICWP_WPSF_FeatureHandler_Insights' => $baseDir . '/../features/insights.php',
908
- 'ICWP_WPSF_FeatureHandler_Ips' => $baseDir . '/../features/ips.php',
909
- 'ICWP_WPSF_FeatureHandler_License' => $baseDir . '/../features/license.php',
910
- 'ICWP_WPSF_FeatureHandler_Lockdown' => $baseDir . '/../features/lockdown.php',
911
- 'ICWP_WPSF_FeatureHandler_LoginProtect' => $baseDir . '/../features/login_protect.php',
912
- 'ICWP_WPSF_FeatureHandler_Plugin' => $baseDir . '/../features/plugin.php',
913
- 'ICWP_WPSF_FeatureHandler_Reporting' => $baseDir . '/../features/reporting.php',
914
- 'ICWP_WPSF_FeatureHandler_Sessions' => $baseDir . '/../features/sessions.php',
915
- 'ICWP_WPSF_FeatureHandler_Traffic' => $baseDir . '/../features/traffic.php',
916
- 'ICWP_WPSF_FeatureHandler_UserManagement' => $baseDir . '/../features/user_management.php',
917
- 'ICWP_WPSF_Processor_AdminAccessRestriction' => $baseDir . '/../processors/admin_access_restriction.php',
918
- 'ICWP_WPSF_Processor_AuditTrail' => $baseDir . '/../processors/audit_trail.php',
919
  'ICWP_WPSF_Processor_AuditTrail_Auditor' => $baseDir . '/../processors/audit_trail_auditor.php',
920
- 'ICWP_WPSF_Processor_Autoupdates' => $baseDir . '/../processors/autoupdates.php',
921
- 'ICWP_WPSF_Processor_CommentsFilter' => $baseDir . '/../processors/comments_filter.php',
922
- 'ICWP_WPSF_Processor_CommentsFilter_BotSpam' => $baseDir . '/../processors/commentsfilter_botspam.php',
923
- 'ICWP_WPSF_Processor_CommentsFilter_GoogleRecaptcha' => $baseDir . '/../processors/commentsfilter_googlerecaptcha.php',
924
- 'ICWP_WPSF_Processor_Email' => $baseDir . '/../processors/email.php',
925
- 'ICWP_WPSF_Processor_Events' => $baseDir . '/../processors/events.php',
926
- 'ICWP_WPSF_Processor_Firewall' => $baseDir . '/../processors/firewall.php',
927
- 'ICWP_WPSF_Processor_HackProtect' => $baseDir . '/../processors/hack_protect.php',
928
- 'ICWP_WPSF_Processor_HackProtect_Apc' => $baseDir . '/../processors/hackprotect_scan_apc.php',
929
- 'ICWP_WPSF_Processor_HackProtect_Integrity' => $baseDir . '/../processors/hackprotect_integrity.php',
930
- 'ICWP_WPSF_Processor_HackProtect_Mal' => $baseDir . '/../processors/hackprotect_scan_mal.php',
931
- 'ICWP_WPSF_Processor_HackProtect_Ptg' => $baseDir . '/../processors/hackprotect_scan_ptg.php',
932
- 'ICWP_WPSF_Processor_HackProtect_Scanner' => $baseDir . '/../processors/hackprotect_scanner.php',
933
- 'ICWP_WPSF_Processor_HackProtect_Ufc' => $baseDir . '/../processors/hackprotect_scan_ufc.php',
934
- 'ICWP_WPSF_Processor_HackProtect_Wcf' => $baseDir . '/../processors/hackprotect_scan_wcf.php',
935
- 'ICWP_WPSF_Processor_HackProtect_Wpv' => $baseDir . '/../processors/hackprotect_scan_wpv.php',
936
- 'ICWP_WPSF_Processor_Headers' => $baseDir . '/../processors/headers.php',
937
- 'ICWP_WPSF_Processor_Lockdown' => $baseDir . '/../processors/lockdown.php',
938
- 'ICWP_WPSF_Processor_LoginProtect' => $baseDir . '/../processors/login_protect.php',
939
- 'ICWP_WPSF_Processor_LoginProtect_WpLogin' => $baseDir . '/../processors/loginprotect_wplogin.php',
940
- 'ICWP_WPSF_Processor_Plugin' => $baseDir . '/../processors/plugin.php',
941
- 'ICWP_WPSF_Processor_Plugin_Tracking' => $baseDir . '/../processors/plugin_tracking.php',
942
- 'ICWP_WPSF_Processor_ScanBase' => $baseDir . '/../processors/hackprotect_scan_base.php',
943
- 'ICWP_WPSF_Processor_Sessions' => $baseDir . '/../processors/sessions.php',
944
- 'ICWP_WPSF_Processor_Traffic' => $baseDir . '/../processors/traffic.php',
945
- 'ICWP_WPSF_Processor_UserManagement' => $baseDir . '/../processors/user_management.php',
946
- 'ICWP_WPSF_Processor_UserManagement_Passwords' => $baseDir . '/../processors/usermanagement_passwords.php',
947
- 'ICWP_WPSF_Processor_UserManagement_Sessions' => $baseDir . '/../processors/usermanagement_sessions.php',
948
  'ICWP_WPSF_Wizard_Base' => $baseDir . '/../wizards/base.php',
949
  'ICWP_WPSF_Wizard_BaseWpsf' => $baseDir . '/../wizards/base_wpsf.php',
950
  'ICWP_WPSF_Wizard_LoginProtect' => $baseDir . '/../wizards/login_protect.php',
14
  'Carbon\\Laravel\\ServiceProvider' => $vendorDir . '/nesbot/carbon/src/Carbon/Laravel/ServiceProvider.php',
15
  'Carbon\\Translator' => $vendorDir . '/nesbot/carbon/src/Carbon/Translator.php',
16
  'Carbon\\Upgrade' => $vendorDir . '/nesbot/carbon/src/Carbon/Upgrade.php',
17
+ 'Composer\\InstalledVersions' => $vendorDir . '/composer/InstalledVersions.php',
18
  'Dolondro\\GoogleAuthenticator\\GoogleAuthenticator' => $vendorDir . '/dolondro/google-authenticator/src/GoogleAuthenticator.php',
19
  'Dolondro\\GoogleAuthenticator\\QrImageGenerator\\EndroidQrImageGenerator' => $vendorDir . '/dolondro/google-authenticator/src/QrImageGenerator/EndroidQrImageGenerator.php',
20
  'Dolondro\\GoogleAuthenticator\\QrImageGenerator\\GoogleQrImageGenerator' => $vendorDir . '/dolondro/google-authenticator/src/QrImageGenerator/GoogleQrImageGenerator.php',
22
  'Dolondro\\GoogleAuthenticator\\Secret' => $vendorDir . '/dolondro/google-authenticator/src/Secret.php',
23
  'Dolondro\\GoogleAuthenticator\\SecretFactory' => $vendorDir . '/dolondro/google-authenticator/src/SecretFactory.php',
24
  'Elliotchance\\Iterator\\AbstractPagedIterator' => $vendorDir . '/elliotchance/iterator/src/Elliotchance/Iterator/AbstractPagedIterator.php',
 
 
25
  'Elliotchance\\Iterator\\PagedIteratorTest' => $vendorDir . '/elliotchance/iterator/tests/Elliotchance/Iterator/PagedIteratorTest.php',
26
+ 'FernleafSystems\\Utilities\\Data\\Adapter\\DynamicProperties' => $vendorDir . '/fernleafsystems/utilities/src/Data/Adapter/DynamicProperties.php',
27
+ 'FernleafSystems\\Utilities\\Data\\Adapter\\DynamicPropertiesClass' => $vendorDir . '/fernleafsystems/utilities/src/Data/Adapter/DynamicPropertiesClass.php',
28
  'FernleafSystems\\Utilities\\Data\\Adapter\\StdClassAdapter' => $vendorDir . '/fernleafsystems/utilities/src/Data/Adapter/StdClassAdapter.php',
29
+ 'FernleafSystems\\Utilities\\Data\\CaptureOutput' => $vendorDir . '/fernleafsystems/utilities/src/Data/CaptureOutput.php',
30
+ 'FernleafSystems\\Utilities\\Logic\\ExecOnce' => $vendorDir . '/fernleafsystems/utilities/src/Logic/ExecOnce.php',
31
  'FernleafSystems\\Utilities\\Logic\\OneTimeExecute' => $vendorDir . '/fernleafsystems/utilities/src/Logic/OneTimeExecute.php',
32
  'FernleafSystems\\Utilities\\Response' => $vendorDir . '/fernleafsystems/utilities/src/Response.php',
33
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\ChangeTrack\\Diff\\Base' => $baseDir . '/src/ChangeTrack/Diff/Base.php',
55
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\ChangeTrack\\Snapshot\\BuildUsers' => $baseDir . '/src/ChangeTrack/Snapshot/BuildUsers.php',
56
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\ChangeTrack\\Snapshot\\Collate' => $baseDir . '/src/ChangeTrack/Snapshot/Collate.php',
57
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\ChangeTrack\\Snapshot\\SnapshotsConsumer' => $baseDir . '/src/ChangeTrack/Snapshot/SnapshotsConsumer.php',
58
+ 'FernleafSystems\\Wordpress\\Plugin\\Shield\\Controller\\Admin\\AdminBarMenu' => $baseDir . '/src/Controller/Admin/AdminBarMenu.php',
59
+ 'FernleafSystems\\Wordpress\\Plugin\\Shield\\Controller\\Admin\\DashboardWidget' => $baseDir . '/src/Controller/Admin/DashboardWidget.php',
60
+ 'FernleafSystems\\Wordpress\\Plugin\\Shield\\Controller\\Admin\\MainAdminMenu' => $baseDir . '/src/Controller/Admin/MainAdminMenu.php',
61
+ 'FernleafSystems\\Wordpress\\Plugin\\Shield\\Controller\\Ajax\\Init' => $baseDir . '/src/Controller/Ajax/Init.php',
62
+ 'FernleafSystems\\Wordpress\\Plugin\\Shield\\Controller\\Ajax\\Response' => $baseDir . '/src/Controller/Ajax/Response.php',
63
+ 'FernleafSystems\\Wordpress\\Plugin\\Shield\\Controller\\Assets\\Enqueue' => $baseDir . '/src/Controller/Assets/Enqueue.php',
64
+ 'FernleafSystems\\Wordpress\\Plugin\\Shield\\Controller\\Assets\\Urls' => $baseDir . '/src/Controller/Assets/Urls.php',
65
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Controller\\Config\\ConfigVO' => $baseDir . '/src/Controller/Config/ConfigVO.php',
66
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Controller\\Config\\Ops\\LoadConfig' => $baseDir . '/src/Controller/Config/Ops/LoadConfig.php',
67
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Controller\\Config\\Ops\\Read' => $baseDir . '/src/Controller/Config/Ops/Read.php',
206
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\BaseShield\\UI' => $baseDir . '/src/Modules/BaseShield/UI.php',
207
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Base\\AdminNotices' => $baseDir . '/src/Modules/Base/AdminNotices.php',
208
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Base\\AjaxHandler' => $baseDir . '/src/Modules/Base/AjaxHandler.php',
 
 
209
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Base\\BaseProcessor' => $baseDir . '/src/Modules/Base/BaseProcessor.php',
 
210
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Base\\Debug' => $baseDir . '/src/Modules/Base/Debug.php',
211
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Base\\Insights\\OverviewCards' => $baseDir . '/src/Modules/Base/Insights/OverviewCards.php',
212
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Base\\ModCon' => $baseDir . '/src/Modules/Base/ModCon.php',
214
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Base\\Options\\OptValueSanitize' => $baseDir . '/src/Modules/Base/Options/OptValueSanitize.php',
215
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Base\\Processor' => $baseDir . '/src/Modules/Base/Processor.php',
216
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Base\\Reporting' => $baseDir . '/src/Modules/Base/Reporting.php',
 
 
217
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Base\\Strings' => $baseDir . '/src/Modules/Base/Strings.php',
218
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Base\\UI' => $baseDir . '/src/Modules/Base/UI.php',
219
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Base\\Upgrade' => $baseDir . '/src/Modules/Base/Upgrade.php',
267
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Firewall\\UI' => $baseDir . '/src/Modules/Firewall/UI.php',
268
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\GeoIp\\Lookup' => $baseDir . '/src/Modules/GeoIp/Lookup.php',
269
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\HackGuard\\AjaxHandler' => $baseDir . '/src/Modules/HackGuard/AjaxHandler.php',
270
+ 'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\HackGuard\\Debug' => $baseDir . '/src/Modules/HackGuard/Debug.php',
271
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\HackGuard\\Insights\\OverviewCards' => $baseDir . '/src/Modules/HackGuard/Insights/OverviewCards.php',
272
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\HackGuard\\Lib\\FileLocker\\File' => $baseDir . '/src/Modules/HackGuard/Lib/FileLocker/File.php',
273
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\HackGuard\\Lib\\FileLocker\\FileLockerController' => $baseDir . '/src/Modules/HackGuard/Lib/FileLocker/FileLockerController.php',
426
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\License\\Lib\\LicenseEmails' => $baseDir . '/src/Modules/License/Lib/LicenseEmails.php',
427
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\License\\Lib\\LicenseHandler' => $baseDir . '/src/Modules/License/Lib/LicenseHandler.php',
428
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\License\\Lib\\LookupRequest' => $baseDir . '/src/Modules/License/Lib/LookupRequest.php',
429
+ 'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\License\\Lib\\PluginNameSuffix' => $baseDir . '/src/Modules/License/Lib/PluginNameSuffix.php',
430
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\License\\Lib\\Verify' => $baseDir . '/src/Modules/License/Lib/Verify.php',
431
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\License\\Lib\\WpHashes\\ApiTokenManager' => $baseDir . '/src/Modules/License/Lib/WpHashes/ApiTokenManager.php',
432
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\License\\ModCon' => $baseDir . '/src/Modules/License/ModCon.php',
748
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Utilities\\ReCaptcha\\Enqueue' => $baseDir . '/src/Utilities/ReCaptcha/Enqueue.php',
749
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Utilities\\ReCaptcha\\TestRequest' => $baseDir . '/src/Utilities/ReCaptcha/TestRequest.php',
750
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Utilities\\ReCaptcha\\WordpressPost' => $baseDir . '/src/Utilities/ReCaptcha/WordpressPost.php',
751
+ 'FernleafSystems\\Wordpress\\Plugin\\Shield\\Utilities\\Time\\WorldTimeApi' => $baseDir . '/src/Utilities/Time/WorldTimeApi.php',
752
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Utilities\\Tool\\FormatBytes' => $baseDir . '/src/Utilities/Tool/FormatBytes.php',
753
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Utilities\\Tool\\IpListSort' => $baseDir . '/src/Utilities/Tool/IpListSort.php',
754
  'FernleafSystems\\Wordpress\\Services\\Core\\AdminNotices' => $vendorDir . '/fernleafsystems/wordpress-services/src/Core/AdminNotices.php',
900
  'FernleafSystems\\Wordpress\\Services\\Utilities\\WpOrg\\Wp\\Versions' => $vendorDir . '/fernleafsystems/wordpress-services/src/Utilities/WpOrg/Wp/Versions.php',
901
  'Html2Text\\Html2Text' => $vendorDir . '/soundasleep/html2text/src/Html2Text.php',
902
  'Html2Text\\Html2TextException' => $vendorDir . '/soundasleep/html2text/src/Html2TextException.php',
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
903
  'ICWP_WPSF_Processor_AuditTrail_Auditor' => $baseDir . '/../processors/audit_trail_auditor.php',
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
904
  'ICWP_WPSF_Wizard_Base' => $baseDir . '/../wizards/base.php',
905
  'ICWP_WPSF_Wizard_BaseWpsf' => $baseDir . '/../wizards/base_wpsf.php',
906
  'ICWP_WPSF_Wizard_LoginProtect' => $baseDir . '/../wizards/login_protect.php',
src/lib/vendor/composer/autoload_real.php CHANGED
@@ -2,7 +2,7 @@
2
 
3
  // autoload_real.php @generated by Composer
4
 
5
- class ComposerAutoloaderInit0b573d8879ea3b08a114d68dbb7a4533
6
  {
7
  private static $loader;
8
 
@@ -22,15 +22,17 @@ class ComposerAutoloaderInit0b573d8879ea3b08a114d68dbb7a4533
22
  return self::$loader;
23
  }
24
 
25
- spl_autoload_register(array('ComposerAutoloaderInit0b573d8879ea3b08a114d68dbb7a4533', 'loadClassLoader'), true, true);
26
- self::$loader = $loader = new \Composer\Autoload\ClassLoader();
27
- spl_autoload_unregister(array('ComposerAutoloaderInit0b573d8879ea3b08a114d68dbb7a4533', 'loadClassLoader'));
 
 
28
 
29
  $useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded());
30
  if ($useStaticLoader) {
31
- require_once __DIR__ . '/autoload_static.php';
32
 
33
- call_user_func(\Composer\Autoload\ComposerStaticInit0b573d8879ea3b08a114d68dbb7a4533::getInitializer($loader));
34
  } else {
35
  $map = require __DIR__ . '/autoload_namespaces.php';
36
  foreach ($map as $namespace => $path) {
@@ -51,19 +53,19 @@ class ComposerAutoloaderInit0b573d8879ea3b08a114d68dbb7a4533
51
  $loader->register(true);
52
 
53
  if ($useStaticLoader) {
54
- $includeFiles = Composer\Autoload\ComposerStaticInit0b573d8879ea3b08a114d68dbb7a4533::$files;
55
  } else {
56
  $includeFiles = require __DIR__ . '/autoload_files.php';
57
  }
58
  foreach ($includeFiles as $fileIdentifier => $file) {
59
- composerRequire0b573d8879ea3b08a114d68dbb7a4533($fileIdentifier, $file);
60
  }
61
 
62
  return $loader;
63
  }
64
  }
65
 
66
- function composerRequire0b573d8879ea3b08a114d68dbb7a4533($fileIdentifier, $file)
67
  {
68
  if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) {
69
  require $file;
2
 
3
  // autoload_real.php @generated by Composer
4
 
5
+ class ComposerAutoloaderInit4fc2c6daaffaf40b64b79b6d26830171
6
  {
7
  private static $loader;
8
 
22
  return self::$loader;
23
  }
24
 
25
+ require __DIR__ . '/platform_check.php';
26
+
27
+ spl_autoload_register(array('ComposerAutoloaderInit4fc2c6daaffaf40b64b79b6d26830171', 'loadClassLoader'), true, true);
28
+ self::$loader = $loader = new \Composer\Autoload\ClassLoader(\dirname(\dirname(__FILE__)));
29
+ spl_autoload_unregister(array('ComposerAutoloaderInit4fc2c6daaffaf40b64b79b6d26830171', 'loadClassLoader'));
30
 
31
  $useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded());
32
  if ($useStaticLoader) {
33
+ require __DIR__ . '/autoload_static.php';
34
 
35
+ call_user_func(\Composer\Autoload\ComposerStaticInit4fc2c6daaffaf40b64b79b6d26830171::getInitializer($loader));
36
  } else {
37
  $map = require __DIR__ . '/autoload_namespaces.php';
38
  foreach ($map as $namespace => $path) {
53
  $loader->register(true);
54
 
55
  if ($useStaticLoader) {
56
+ $includeFiles = Composer\Autoload\ComposerStaticInit4fc2c6daaffaf40b64b79b6d26830171::$files;
57
  } else {
58
  $includeFiles = require __DIR__ . '/autoload_files.php';
59
  }
60
  foreach ($includeFiles as $fileIdentifier => $file) {
61
+ composerRequire4fc2c6daaffaf40b64b79b6d26830171($fileIdentifier, $file);
62
  }
63
 
64
  return $loader;
65
  }
66
  }
67
 
68
+ function composerRequire4fc2c6daaffaf40b64b79b6d26830171($fileIdentifier, $file)
69
  {
70
  if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) {
71
  require $file;
src/lib/vendor/composer/autoload_static.php CHANGED
@@ -4,7 +4,7 @@
4
 
5
  namespace Composer\Autoload;
6
 
7
- class ComposerStaticInit0b573d8879ea3b08a114d68dbb7a4533
8
  {
9
  public static $files = array (
10
  '0e6d7bf4a5811bfa5cf40c5ccd6fae6a' => __DIR__ . '/..' . '/symfony/polyfill-mbstring/bootstrap.php',
@@ -181,6 +181,7 @@ class ComposerStaticInit0b573d8879ea3b08a114d68dbb7a4533
181
  'Carbon\\Laravel\\ServiceProvider' => __DIR__ . '/..' . '/nesbot/carbon/src/Carbon/Laravel/ServiceProvider.php',
182
  'Carbon\\Translator' => __DIR__ . '/..' . '/nesbot/carbon/src/Carbon/Translator.php',
183
  'Carbon\\Upgrade' => __DIR__ . '/..' . '/nesbot/carbon/src/Carbon/Upgrade.php',
 
184
  'Dolondro\\GoogleAuthenticator\\GoogleAuthenticator' => __DIR__ . '/..' . '/dolondro/google-authenticator/src/GoogleAuthenticator.php',
185
  'Dolondro\\GoogleAuthenticator\\QrImageGenerator\\EndroidQrImageGenerator' => __DIR__ . '/..' . '/dolondro/google-authenticator/src/QrImageGenerator/EndroidQrImageGenerator.php',
186
  'Dolondro\\GoogleAuthenticator\\QrImageGenerator\\GoogleQrImageGenerator' => __DIR__ . '/..' . '/dolondro/google-authenticator/src/QrImageGenerator/GoogleQrImageGenerator.php',
@@ -188,10 +189,12 @@ class ComposerStaticInit0b573d8879ea3b08a114d68dbb7a4533
188
  'Dolondro\\GoogleAuthenticator\\Secret' => __DIR__ . '/..' . '/dolondro/google-authenticator/src/Secret.php',
189
  'Dolondro\\GoogleAuthenticator\\SecretFactory' => __DIR__ . '/..' . '/dolondro/google-authenticator/src/SecretFactory.php',
190
  'Elliotchance\\Iterator\\AbstractPagedIterator' => __DIR__ . '/..' . '/elliotchance/iterator/src/Elliotchance/Iterator/AbstractPagedIterator.php',
191
- 'Elliotchance\\Iterator\\PagedIterator1' => __DIR__ . '/..' . '/elliotchance/iterator/tests/Elliotchance/Iterator/PagedIteratorTest.php',
192
- 'Elliotchance\\Iterator\\PagedIterator2' => __DIR__ . '/..' . '/elliotchance/iterator/tests/Elliotchance/Iterator/PagedIteratorTest.php',
193
  'Elliotchance\\Iterator\\PagedIteratorTest' => __DIR__ . '/..' . '/elliotchance/iterator/tests/Elliotchance/Iterator/PagedIteratorTest.php',
 
 
194
  'FernleafSystems\\Utilities\\Data\\Adapter\\StdClassAdapter' => __DIR__ . '/..' . '/fernleafsystems/utilities/src/Data/Adapter/StdClassAdapter.php',
 
 
195
  'FernleafSystems\\Utilities\\Logic\\OneTimeExecute' => __DIR__ . '/..' . '/fernleafsystems/utilities/src/Logic/OneTimeExecute.php',
196
  'FernleafSystems\\Utilities\\Response' => __DIR__ . '/..' . '/fernleafsystems/utilities/src/Response.php',
197
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\ChangeTrack\\Diff\\Base' => __DIR__ . '/../..' . '/src/ChangeTrack/Diff/Base.php',
@@ -219,6 +222,13 @@ class ComposerStaticInit0b573d8879ea3b08a114d68dbb7a4533
219
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\ChangeTrack\\Snapshot\\BuildUsers' => __DIR__ . '/../..' . '/src/ChangeTrack/Snapshot/BuildUsers.php',
220
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\ChangeTrack\\Snapshot\\Collate' => __DIR__ . '/../..' . '/src/ChangeTrack/Snapshot/Collate.php',
221
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\ChangeTrack\\Snapshot\\SnapshotsConsumer' => __DIR__ . '/../..' . '/src/ChangeTrack/Snapshot/SnapshotsConsumer.php',
 
 
 
 
 
 
 
222
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Controller\\Config\\ConfigVO' => __DIR__ . '/../..' . '/src/Controller/Config/ConfigVO.php',
223
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Controller\\Config\\Ops\\LoadConfig' => __DIR__ . '/../..' . '/src/Controller/Config/Ops/LoadConfig.php',
224
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Controller\\Config\\Ops\\Read' => __DIR__ . '/../..' . '/src/Controller/Config/Ops/Read.php',
@@ -363,10 +373,7 @@ class ComposerStaticInit0b573d8879ea3b08a114d68dbb7a4533
363
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\BaseShield\\UI' => __DIR__ . '/../..' . '/src/Modules/BaseShield/UI.php',
364
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Base\\AdminNotices' => __DIR__ . '/../..' . '/src/Modules/Base/AdminNotices.php',
365
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Base\\AjaxHandler' => __DIR__ . '/../..' . '/src/Modules/Base/AjaxHandler.php',
366
- 'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Base\\AjaxHandlerBase' => __DIR__ . '/../..' . '/src/Modules/Base/AjaxHandlerBase.php',
367
- 'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Base\\AjaxHandlerShield' => __DIR__ . '/../..' . '/src/Modules/Base/AjaxHandlerShield.php',
368
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Base\\BaseProcessor' => __DIR__ . '/../..' . '/src/Modules/Base/BaseProcessor.php',
369
- 'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Base\\BaseReporting' => __DIR__ . '/../..' . '/src/Modules/Base/BaseReporting.php',
370
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Base\\Debug' => __DIR__ . '/../..' . '/src/Modules/Base/Debug.php',
371
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Base\\Insights\\OverviewCards' => __DIR__ . '/../..' . '/src/Modules/Base/Insights/OverviewCards.php',
372
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Base\\ModCon' => __DIR__ . '/../..' . '/src/Modules/Base/ModCon.php',
@@ -374,8 +381,6 @@ class ComposerStaticInit0b573d8879ea3b08a114d68dbb7a4533
374
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Base\\Options\\OptValueSanitize' => __DIR__ . '/../..' . '/src/Modules/Base/Options/OptValueSanitize.php',
375
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Base\\Processor' => __DIR__ . '/../..' . '/src/Modules/Base/Processor.php',
376
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Base\\Reporting' => __DIR__ . '/../..' . '/src/Modules/Base/Reporting.php',
377
- 'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Base\\ShieldOptions' => __DIR__ . '/../..' . '/src/Modules/Base/ShieldOptions.php',
378
- 'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Base\\ShieldUI' => __DIR__ . '/../..' . '/src/Modules/Base/ShieldUI.php',
379
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Base\\Strings' => __DIR__ . '/../..' . '/src/Modules/Base/Strings.php',
380
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Base\\UI' => __DIR__ . '/../..' . '/src/Modules/Base/UI.php',
381
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Base\\Upgrade' => __DIR__ . '/../..' . '/src/Modules/Base/Upgrade.php',
@@ -429,6 +434,7 @@ class ComposerStaticInit0b573d8879ea3b08a114d68dbb7a4533
429
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Firewall\\UI' => __DIR__ . '/../..' . '/src/Modules/Firewall/UI.php',
430
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\GeoIp\\Lookup' => __DIR__ . '/../..' . '/src/Modules/GeoIp/Lookup.php',
431
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\HackGuard\\AjaxHandler' => __DIR__ . '/../..' . '/src/Modules/HackGuard/AjaxHandler.php',
 
432
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\HackGuard\\Insights\\OverviewCards' => __DIR__ . '/../..' . '/src/Modules/HackGuard/Insights/OverviewCards.php',
433
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\HackGuard\\Lib\\FileLocker\\File' => __DIR__ . '/../..' . '/src/Modules/HackGuard/Lib/FileLocker/File.php',
434
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\HackGuard\\Lib\\FileLocker\\FileLockerController' => __DIR__ . '/../..' . '/src/Modules/HackGuard/Lib/FileLocker/FileLockerController.php',
@@ -587,6 +593,7 @@ class ComposerStaticInit0b573d8879ea3b08a114d68dbb7a4533
587
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\License\\Lib\\LicenseEmails' => __DIR__ . '/../..' . '/src/Modules/License/Lib/LicenseEmails.php',
588
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\License\\Lib\\LicenseHandler' => __DIR__ . '/../..' . '/src/Modules/License/Lib/LicenseHandler.php',
589
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\License\\Lib\\LookupRequest' => __DIR__ . '/../..' . '/src/Modules/License/Lib/LookupRequest.php',
 
590
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\License\\Lib\\Verify' => __DIR__ . '/../..' . '/src/Modules/License/Lib/Verify.php',
591
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\License\\Lib\\WpHashes\\ApiTokenManager' => __DIR__ . '/../..' . '/src/Modules/License/Lib/WpHashes/ApiTokenManager.php',
592
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\License\\ModCon' => __DIR__ . '/../..' . '/src/Modules/License/ModCon.php',
@@ -908,6 +915,7 @@ class ComposerStaticInit0b573d8879ea3b08a114d68dbb7a4533
908
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Utilities\\ReCaptcha\\Enqueue' => __DIR__ . '/../..' . '/src/Utilities/ReCaptcha/Enqueue.php',
909
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Utilities\\ReCaptcha\\TestRequest' => __DIR__ . '/../..' . '/src/Utilities/ReCaptcha/TestRequest.php',
910
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Utilities\\ReCaptcha\\WordpressPost' => __DIR__ . '/../..' . '/src/Utilities/ReCaptcha/WordpressPost.php',
 
911
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Utilities\\Tool\\FormatBytes' => __DIR__ . '/../..' . '/src/Utilities/Tool/FormatBytes.php',
912
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Utilities\\Tool\\IpListSort' => __DIR__ . '/../..' . '/src/Utilities/Tool/IpListSort.php',
913
  'FernleafSystems\\Wordpress\\Services\\Core\\AdminNotices' => __DIR__ . '/..' . '/fernleafsystems/wordpress-services/src/Core/AdminNotices.php',
@@ -1059,59 +1067,7 @@ class ComposerStaticInit0b573d8879ea3b08a114d68dbb7a4533
1059
  'FernleafSystems\\Wordpress\\Services\\Utilities\\WpOrg\\Wp\\Versions' => __DIR__ . '/..' . '/fernleafsystems/wordpress-services/src/Utilities/WpOrg/Wp/Versions.php',
1060
  'Html2Text\\Html2Text' => __DIR__ . '/..' . '/soundasleep/html2text/src/Html2Text.php',
1061
  'Html2Text\\Html2TextException' => __DIR__ . '/..' . '/soundasleep/html2text/src/Html2TextException.php',
1062
- 'ICWP_WPSF_FeatureHandler_AdminAccessRestriction' => __DIR__ . '/../..' . '/../features/admin_access_restriction.php',
1063
- 'ICWP_WPSF_FeatureHandler_AuditTrail' => __DIR__ . '/../..' . '/../features/audit_trail.php',
1064
- 'ICWP_WPSF_FeatureHandler_Autoupdates' => __DIR__ . '/../..' . '/../features/autoupdates.php',
1065
- 'ICWP_WPSF_FeatureHandler_Base' => __DIR__ . '/../..' . '/../features/base.php',
1066
- 'ICWP_WPSF_FeatureHandler_BaseWpsf' => __DIR__ . '/../..' . '/../features/base_wpsf.php',
1067
- 'ICWP_WPSF_FeatureHandler_CommentsFilter' => __DIR__ . '/../..' . '/../features/comments_filter.php',
1068
- 'ICWP_WPSF_FeatureHandler_Comms' => __DIR__ . '/../..' . '/../features/comms.php',
1069
- 'ICWP_WPSF_FeatureHandler_Email' => __DIR__ . '/../..' . '/../features/email.php',
1070
- 'ICWP_WPSF_FeatureHandler_Events' => __DIR__ . '/../..' . '/../features/events.php',
1071
- 'ICWP_WPSF_FeatureHandler_Firewall' => __DIR__ . '/../..' . '/../features/firewall.php',
1072
- 'ICWP_WPSF_FeatureHandler_HackProtect' => __DIR__ . '/../..' . '/../features/hack_protect.php',
1073
- 'ICWP_WPSF_FeatureHandler_Headers' => __DIR__ . '/../..' . '/../features/headers.php',
1074
- 'ICWP_WPSF_FeatureHandler_Insights' => __DIR__ . '/../..' . '/../features/insights.php',
1075
- 'ICWP_WPSF_FeatureHandler_Ips' => __DIR__ . '/../..' . '/../features/ips.php',
1076
- 'ICWP_WPSF_FeatureHandler_License' => __DIR__ . '/../..' . '/../features/license.php',
1077
- 'ICWP_WPSF_FeatureHandler_Lockdown' => __DIR__ . '/../..' . '/../features/lockdown.php',
1078
- 'ICWP_WPSF_FeatureHandler_LoginProtect' => __DIR__ . '/../..' . '/../features/login_protect.php',
1079
- 'ICWP_WPSF_FeatureHandler_Plugin' => __DIR__ . '/../..' . '/../features/plugin.php',
1080
- 'ICWP_WPSF_FeatureHandler_Reporting' => __DIR__ . '/../..' . '/../features/reporting.php',
1081
- 'ICWP_WPSF_FeatureHandler_Sessions' => __DIR__ . '/../..' . '/../features/sessions.php',
1082
- 'ICWP_WPSF_FeatureHandler_Traffic' => __DIR__ . '/../..' . '/../features/traffic.php',
1083
- 'ICWP_WPSF_FeatureHandler_UserManagement' => __DIR__ . '/../..' . '/../features/user_management.php',
1084
- 'ICWP_WPSF_Processor_AdminAccessRestriction' => __DIR__ . '/../..' . '/../processors/admin_access_restriction.php',
1085
- 'ICWP_WPSF_Processor_AuditTrail' => __DIR__ . '/../..' . '/../processors/audit_trail.php',
1086
  'ICWP_WPSF_Processor_AuditTrail_Auditor' => __DIR__ . '/../..' . '/../processors/audit_trail_auditor.php',
1087
- 'ICWP_WPSF_Processor_Autoupdates' => __DIR__ . '/../..' . '/../processors/autoupdates.php',
1088
- 'ICWP_WPSF_Processor_CommentsFilter' => __DIR__ . '/../..' . '/../processors/comments_filter.php',
1089
- 'ICWP_WPSF_Processor_CommentsFilter_BotSpam' => __DIR__ . '/../..' . '/../processors/commentsfilter_botspam.php',
1090
- 'ICWP_WPSF_Processor_CommentsFilter_GoogleRecaptcha' => __DIR__ . '/../..' . '/../processors/commentsfilter_googlerecaptcha.php',
1091
- 'ICWP_WPSF_Processor_Email' => __DIR__ . '/../..' . '/../processors/email.php',
1092
- 'ICWP_WPSF_Processor_Events' => __DIR__ . '/../..' . '/../processors/events.php',
1093
- 'ICWP_WPSF_Processor_Firewall' => __DIR__ . '/../..' . '/../processors/firewall.php',
1094
- 'ICWP_WPSF_Processor_HackProtect' => __DIR__ . '/../..' . '/../processors/hack_protect.php',
1095
- 'ICWP_WPSF_Processor_HackProtect_Apc' => __DIR__ . '/../..' . '/../processors/hackprotect_scan_apc.php',
1096
- 'ICWP_WPSF_Processor_HackProtect_Integrity' => __DIR__ . '/../..' . '/../processors/hackprotect_integrity.php',
1097
- 'ICWP_WPSF_Processor_HackProtect_Mal' => __DIR__ . '/../..' . '/../processors/hackprotect_scan_mal.php',
1098
- 'ICWP_WPSF_Processor_HackProtect_Ptg' => __DIR__ . '/../..' . '/../processors/hackprotect_scan_ptg.php',
1099
- 'ICWP_WPSF_Processor_HackProtect_Scanner' => __DIR__ . '/../..' . '/../processors/hackprotect_scanner.php',
1100
- 'ICWP_WPSF_Processor_HackProtect_Ufc' => __DIR__ . '/../..' . '/../processors/hackprotect_scan_ufc.php',
1101
- 'ICWP_WPSF_Processor_HackProtect_Wcf' => __DIR__ . '/../..' . '/../processors/hackprotect_scan_wcf.php',
1102
- 'ICWP_WPSF_Processor_HackProtect_Wpv' => __DIR__ . '/../..' . '/../processors/hackprotect_scan_wpv.php',
1103
- 'ICWP_WPSF_Processor_Headers' => __DIR__ . '/../..' . '/../processors/headers.php',
1104
- 'ICWP_WPSF_Processor_Lockdown' => __DIR__ . '/../..' . '/../processors/lockdown.php',
1105
- 'ICWP_WPSF_Processor_LoginProtect' => __DIR__ . '/../..' . '/../processors/login_protect.php',
1106
- 'ICWP_WPSF_Processor_LoginProtect_WpLogin' => __DIR__ . '/../..' . '/../processors/loginprotect_wplogin.php',
1107
- 'ICWP_WPSF_Processor_Plugin' => __DIR__ . '/../..' . '/../processors/plugin.php',
1108
- 'ICWP_WPSF_Processor_Plugin_Tracking' => __DIR__ . '/../..' . '/../processors/plugin_tracking.php',
1109
- 'ICWP_WPSF_Processor_ScanBase' => __DIR__ . '/../..' . '/../processors/hackprotect_scan_base.php',
1110
- 'ICWP_WPSF_Processor_Sessions' => __DIR__ . '/../..' . '/../processors/sessions.php',
1111
- 'ICWP_WPSF_Processor_Traffic' => __DIR__ . '/../..' . '/../processors/traffic.php',
1112
- 'ICWP_WPSF_Processor_UserManagement' => __DIR__ . '/../..' . '/../processors/user_management.php',
1113
- 'ICWP_WPSF_Processor_UserManagement_Passwords' => __DIR__ . '/../..' . '/../processors/usermanagement_passwords.php',
1114
- 'ICWP_WPSF_Processor_UserManagement_Sessions' => __DIR__ . '/../..' . '/../processors/usermanagement_sessions.php',
1115
  'ICWP_WPSF_Wizard_Base' => __DIR__ . '/../..' . '/../wizards/base.php',
1116
  'ICWP_WPSF_Wizard_BaseWpsf' => __DIR__ . '/../..' . '/../wizards/base_wpsf.php',
1117
  'ICWP_WPSF_Wizard_LoginProtect' => __DIR__ . '/../..' . '/../wizards/login_protect.php',
@@ -1682,12 +1638,12 @@ class ComposerStaticInit0b573d8879ea3b08a114d68dbb7a4533
1682
  public static function getInitializer(ClassLoader $loader)
1683
  {
1684
  return \Closure::bind(function () use ($loader) {
1685
- $loader->prefixLengthsPsr4 = ComposerStaticInit0b573d8879ea3b08a114d68dbb7a4533::$prefixLengthsPsr4;
1686
- $loader->prefixDirsPsr4 = ComposerStaticInit0b573d8879ea3b08a114d68dbb7a4533::$prefixDirsPsr4;
1687
- $loader->fallbackDirsPsr4 = ComposerStaticInit0b573d8879ea3b08a114d68dbb7a4533::$fallbackDirsPsr4;
1688
- $loader->prefixesPsr0 = ComposerStaticInit0b573d8879ea3b08a114d68dbb7a4533::$prefixesPsr0;
1689
- $loader->fallbackDirsPsr0 = ComposerStaticInit0b573d8879ea3b08a114d68dbb7a4533::$fallbackDirsPsr0;
1690
- $loader->classMap = ComposerStaticInit0b573d8879ea3b08a114d68dbb7a4533::$classMap;
1691
 
1692
  }, null, ClassLoader::class);
1693
  }
4
 
5
  namespace Composer\Autoload;
6
 
7
+ class ComposerStaticInit4fc2c6daaffaf40b64b79b6d26830171
8
  {
9
  public static $files = array (
10
  '0e6d7bf4a5811bfa5cf40c5ccd6fae6a' => __DIR__ . '/..' . '/symfony/polyfill-mbstring/bootstrap.php',
181
  'Carbon\\Laravel\\ServiceProvider' => __DIR__ . '/..' . '/nesbot/carbon/src/Carbon/Laravel/ServiceProvider.php',
182
  'Carbon\\Translator' => __DIR__ . '/..' . '/nesbot/carbon/src/Carbon/Translator.php',
183
  'Carbon\\Upgrade' => __DIR__ . '/..' . '/nesbot/carbon/src/Carbon/Upgrade.php',
184
+ 'Composer\\InstalledVersions' => __DIR__ . '/..' . '/composer/InstalledVersions.php',
185
  'Dolondro\\GoogleAuthenticator\\GoogleAuthenticator' => __DIR__ . '/..' . '/dolondro/google-authenticator/src/GoogleAuthenticator.php',
186
  'Dolondro\\GoogleAuthenticator\\QrImageGenerator\\EndroidQrImageGenerator' => __DIR__ . '/..' . '/dolondro/google-authenticator/src/QrImageGenerator/EndroidQrImageGenerator.php',
187
  'Dolondro\\GoogleAuthenticator\\QrImageGenerator\\GoogleQrImageGenerator' => __DIR__ . '/..' . '/dolondro/google-authenticator/src/QrImageGenerator/GoogleQrImageGenerator.php',
189
  'Dolondro\\GoogleAuthenticator\\Secret' => __DIR__ . '/..' . '/dolondro/google-authenticator/src/Secret.php',
190
  'Dolondro\\GoogleAuthenticator\\SecretFactory' => __DIR__ . '/..' . '/dolondro/google-authenticator/src/SecretFactory.php',
191
  'Elliotchance\\Iterator\\AbstractPagedIterator' => __DIR__ . '/..' . '/elliotchance/iterator/src/Elliotchance/Iterator/AbstractPagedIterator.php',
 
 
192
  'Elliotchance\\Iterator\\PagedIteratorTest' => __DIR__ . '/..' . '/elliotchance/iterator/tests/Elliotchance/Iterator/PagedIteratorTest.php',
193
+ 'FernleafSystems\\Utilities\\Data\\Adapter\\DynamicProperties' => __DIR__ . '/..' . '/fernleafsystems/utilities/src/Data/Adapter/DynamicProperties.php',
194
+ 'FernleafSystems\\Utilities\\Data\\Adapter\\DynamicPropertiesClass' => __DIR__ . '/..' . '/fernleafsystems/utilities/src/Data/Adapter/DynamicPropertiesClass.php',
195
  'FernleafSystems\\Utilities\\Data\\Adapter\\StdClassAdapter' => __DIR__ . '/..' . '/fernleafsystems/utilities/src/Data/Adapter/StdClassAdapter.php',
196
+ 'FernleafSystems\\Utilities\\Data\\CaptureOutput' => __DIR__ . '/..' . '/fernleafsystems/utilities/src/Data/CaptureOutput.php',
197
+ 'FernleafSystems\\Utilities\\Logic\\ExecOnce' => __DIR__ . '/..' . '/fernleafsystems/utilities/src/Logic/ExecOnce.php',
198
  'FernleafSystems\\Utilities\\Logic\\OneTimeExecute' => __DIR__ . '/..' . '/fernleafsystems/utilities/src/Logic/OneTimeExecute.php',
199
  'FernleafSystems\\Utilities\\Response' => __DIR__ . '/..' . '/fernleafsystems/utilities/src/Response.php',
200
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\ChangeTrack\\Diff\\Base' => __DIR__ . '/../..' . '/src/ChangeTrack/Diff/Base.php',
222
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\ChangeTrack\\Snapshot\\BuildUsers' => __DIR__ . '/../..' . '/src/ChangeTrack/Snapshot/BuildUsers.php',
223
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\ChangeTrack\\Snapshot\\Collate' => __DIR__ . '/../..' . '/src/ChangeTrack/Snapshot/Collate.php',
224
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\ChangeTrack\\Snapshot\\SnapshotsConsumer' => __DIR__ . '/../..' . '/src/ChangeTrack/Snapshot/SnapshotsConsumer.php',
225
+ 'FernleafSystems\\Wordpress\\Plugin\\Shield\\Controller\\Admin\\AdminBarMenu' => __DIR__ . '/../..' . '/src/Controller/Admin/AdminBarMenu.php',
226
+ 'FernleafSystems\\Wordpress\\Plugin\\Shield\\Controller\\Admin\\DashboardWidget' => __DIR__ . '/../..' . '/src/Controller/Admin/DashboardWidget.php',
227
+ 'FernleafSystems\\Wordpress\\Plugin\\Shield\\Controller\\Admin\\MainAdminMenu' => __DIR__ . '/../..' . '/src/Controller/Admin/MainAdminMenu.php',
228
+ 'FernleafSystems\\Wordpress\\Plugin\\Shield\\Controller\\Ajax\\Init' => __DIR__ . '/../..' . '/src/Controller/Ajax/Init.php',
229
+ 'FernleafSystems\\Wordpress\\Plugin\\Shield\\Controller\\Ajax\\Response' => __DIR__ . '/../..' . '/src/Controller/Ajax/Response.php',
230
+ 'FernleafSystems\\Wordpress\\Plugin\\Shield\\Controller\\Assets\\Enqueue' => __DIR__ . '/../..' . '/src/Controller/Assets/Enqueue.php',
231
+ 'FernleafSystems\\Wordpress\\Plugin\\Shield\\Controller\\Assets\\Urls' => __DIR__ . '/../..' . '/src/Controller/Assets/Urls.php',
232
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Controller\\Config\\ConfigVO' => __DIR__ . '/../..' . '/src/Controller/Config/ConfigVO.php',
233
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Controller\\Config\\Ops\\LoadConfig' => __DIR__ . '/../..' . '/src/Controller/Config/Ops/LoadConfig.php',
234
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Controller\\Config\\Ops\\Read' => __DIR__ . '/../..' . '/src/Controller/Config/Ops/Read.php',
373
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\BaseShield\\UI' => __DIR__ . '/../..' . '/src/Modules/BaseShield/UI.php',
374
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Base\\AdminNotices' => __DIR__ . '/../..' . '/src/Modules/Base/AdminNotices.php',
375
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Base\\AjaxHandler' => __DIR__ . '/../..' . '/src/Modules/Base/AjaxHandler.php',
 
 
376
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Base\\BaseProcessor' => __DIR__ . '/../..' . '/src/Modules/Base/BaseProcessor.php',
 
377
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Base\\Debug' => __DIR__ . '/../..' . '/src/Modules/Base/Debug.php',
378
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Base\\Insights\\OverviewCards' => __DIR__ . '/../..' . '/src/Modules/Base/Insights/OverviewCards.php',
379
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Base\\ModCon' => __DIR__ . '/../..' . '/src/Modules/Base/ModCon.php',
381
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Base\\Options\\OptValueSanitize' => __DIR__ . '/../..' . '/src/Modules/Base/Options/OptValueSanitize.php',
382
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Base\\Processor' => __DIR__ . '/../..' . '/src/Modules/Base/Processor.php',
383
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Base\\Reporting' => __DIR__ . '/../..' . '/src/Modules/Base/Reporting.php',
 
 
384
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Base\\Strings' => __DIR__ . '/../..' . '/src/Modules/Base/Strings.php',
385
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Base\\UI' => __DIR__ . '/../..' . '/src/Modules/Base/UI.php',
386
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Base\\Upgrade' => __DIR__ . '/../..' . '/src/Modules/Base/Upgrade.php',
434
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Firewall\\UI' => __DIR__ . '/../..' . '/src/Modules/Firewall/UI.php',
435
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\GeoIp\\Lookup' => __DIR__ . '/../..' . '/src/Modules/GeoIp/Lookup.php',
436
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\HackGuard\\AjaxHandler' => __DIR__ . '/../..' . '/src/Modules/HackGuard/AjaxHandler.php',
437
+ 'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\HackGuard\\Debug' => __DIR__ . '/../..' . '/src/Modules/HackGuard/Debug.php',
438
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\HackGuard\\Insights\\OverviewCards' => __DIR__ . '/../..' . '/src/Modules/HackGuard/Insights/OverviewCards.php',
439
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\HackGuard\\Lib\\FileLocker\\File' => __DIR__ . '/../..' . '/src/Modules/HackGuard/Lib/FileLocker/File.php',
440
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\HackGuard\\Lib\\FileLocker\\FileLockerController' => __DIR__ . '/../..' . '/src/Modules/HackGuard/Lib/FileLocker/FileLockerController.php',
593
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\License\\Lib\\LicenseEmails' => __DIR__ . '/../..' . '/src/Modules/License/Lib/LicenseEmails.php',
594
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\License\\Lib\\LicenseHandler' => __DIR__ . '/../..' . '/src/Modules/License/Lib/LicenseHandler.php',
595
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\License\\Lib\\LookupRequest' => __DIR__ . '/../..' . '/src/Modules/License/Lib/LookupRequest.php',
596
+ 'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\License\\Lib\\PluginNameSuffix' => __DIR__ . '/../..' . '/src/Modules/License/Lib/PluginNameSuffix.php',
597
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\License\\Lib\\Verify' => __DIR__ . '/../..' . '/src/Modules/License/Lib/Verify.php',
598
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\License\\Lib\\WpHashes\\ApiTokenManager' => __DIR__ . '/../..' . '/src/Modules/License/Lib/WpHashes/ApiTokenManager.php',
599
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\License\\ModCon' => __DIR__ . '/../..' . '/src/Modules/License/ModCon.php',
915
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Utilities\\ReCaptcha\\Enqueue' => __DIR__ . '/../..' . '/src/Utilities/ReCaptcha/Enqueue.php',
916
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Utilities\\ReCaptcha\\TestRequest' => __DIR__ . '/../..' . '/src/Utilities/ReCaptcha/TestRequest.php',
917
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Utilities\\ReCaptcha\\WordpressPost' => __DIR__ . '/../..' . '/src/Utilities/ReCaptcha/WordpressPost.php',
918
+ 'FernleafSystems\\Wordpress\\Plugin\\Shield\\Utilities\\Time\\WorldTimeApi' => __DIR__ . '/../..' . '/src/Utilities/Time/WorldTimeApi.php',
919
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Utilities\\Tool\\FormatBytes' => __DIR__ . '/../..' . '/src/Utilities/Tool/FormatBytes.php',
920
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Utilities\\Tool\\IpListSort' => __DIR__ . '/../..' . '/src/Utilities/Tool/IpListSort.php',
921
  'FernleafSystems\\Wordpress\\Services\\Core\\AdminNotices' => __DIR__ . '/..' . '/fernleafsystems/wordpress-services/src/Core/AdminNotices.php',
1067
  'FernleafSystems\\Wordpress\\Services\\Utilities\\WpOrg\\Wp\\Versions' => __DIR__ . '/..' . '/fernleafsystems/wordpress-services/src/Utilities/WpOrg/Wp/Versions.php',
1068
  'Html2Text\\Html2Text' => __DIR__ . '/..' . '/soundasleep/html2text/src/Html2Text.php',
1069
  'Html2Text\\Html2TextException' => __DIR__ . '/..' . '/soundasleep/html2text/src/Html2TextException.php',
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1070
  'ICWP_WPSF_Processor_AuditTrail_Auditor' => __DIR__ . '/../..' . '/../processors/audit_trail_auditor.php',
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1071
  'ICWP_WPSF_Wizard_Base' => __DIR__ . '/../..' . '/../wizards/base.php',
1072
  'ICWP_WPSF_Wizard_BaseWpsf' => __DIR__ . '/../..' . '/../wizards/base_wpsf.php',
1073
  'ICWP_WPSF_Wizard_LoginProtect' => __DIR__ . '/../..' . '/../wizards/login_protect.php',
1638
  public static function getInitializer(ClassLoader $loader)
1639
  {
1640
  return \Closure::bind(function () use ($loader) {
1641
+ $loader->prefixLengthsPsr4 = ComposerStaticInit4fc2c6daaffaf40b64b79b6d26830171::$prefixLengthsPsr4;
1642
+ $loader->prefixDirsPsr4 = ComposerStaticInit4fc2c6daaffaf40b64b79b6d26830171::$prefixDirsPsr4;
1643
+ $loader->fallbackDirsPsr4 = ComposerStaticInit4fc2c6daaffaf40b64b79b6d26830171::$fallbackDirsPsr4;
1644
+ $loader->prefixesPsr0 = ComposerStaticInit4fc2c6daaffaf40b64b79b6d26830171::$prefixesPsr0;
1645
+ $loader->fallbackDirsPsr0 = ComposerStaticInit4fc2c6daaffaf40b64b79b6d26830171::$fallbackDirsPsr0;
1646
+ $loader->classMap = ComposerStaticInit4fc2c6daaffaf40b64b79b6d26830171::$classMap;
1647
 
1648
  }, null, ClassLoader::class);
1649
  }
src/lib/vendor/composer/platform_check.php ADDED
@@ -0,0 +1,26 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // platform_check.php @generated by Composer
4
+
5
+ $issues = array();
6
+
7
+ if (!(PHP_VERSION_ID >= 70000)) {
8
+ $issues[] = 'Your Composer dependencies require a PHP version ">= 7.0.0". You are running ' . PHP_VERSION . '.';
9
+ }
10
+
11
+ if ($issues) {
12
+ if (!headers_sent()) {
13
+ header('HTTP/1.1 500 Internal Server Error');
14
+ }
15
+ if (!ini_get('display_errors')) {
16
+ if (PHP_SAPI === 'cli' || PHP_SAPI === 'phpdbg') {
17
+ fwrite(STDERR, 'Composer detected issues in your platform:' . PHP_EOL.PHP_EOL . implode(PHP_EOL, $issues) . PHP_EOL.PHP_EOL);
18
+ } elseif (!headers_sent()) {
19
+ echo 'Composer detected issues in your platform:' . PHP_EOL.PHP_EOL . str_replace('You are running '.PHP_VERSION.'.', '', implode(PHP_EOL, $issues)) . PHP_EOL.PHP_EOL;
20
+ }
21
+ }
22
+ trigger_error(
23
+ 'Composer detected issues in your platform: ' . implode(' ', $issues),
24
+ E_USER_ERROR
25
+ );
26
+ }
src/lib/vendor/fernleafsystems/utilities/src/Data/Adapter/DynamicProperties.php ADDED
@@ -0,0 +1,74 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace FernleafSystems\Utilities\Data\Adapter;
4
+
5
+ /**
6
+ * Trait DynamicProperties
7
+ * @package FernleafSystems\Utilities\Data\Adapter
8
+ */
9
+ trait DynamicProperties {
10
+
11
+ private $raw = [];
12
+
13
+ /**
14
+ * @param string $key
15
+ * @return mixed
16
+ */
17
+ public function __get( string $key ) {
18
+ $data = $this->getRawData();
19
+ return $data[ $key ] ?? null;
20
+ }
21
+
22
+ public function __isset( string $key ) :bool {
23
+ return array_key_exists( $key, $this->getRawData() );
24
+ }
25
+
26
+ /**
27
+ * @param string $key
28
+ * @param mixed $value
29
+ * @return $this
30
+ */
31
+ public function __set( string $key, $value ) :self {
32
+ $data = $this->getRawData();
33
+ $data[ $key ] = $value;
34
+ return $this->applyFromArray( $data );
35
+ }
36
+
37
+ /**
38
+ * @param string $key
39
+ * @return $this
40
+ */
41
+ public function __unset( string $key ) {
42
+ $data = $this->getRawData();
43
+ if ( array_key_exists( $key, $data ) ) {
44
+ unset( $data[ $key ] );
45
+ $this->applyFromArray( $data );
46
+ }
47
+ return $this;
48
+ }
49
+
50
+ public function applyFromArray( $data, array $restrictedKeys = [] ) :self {
51
+ if ( !empty( $restrictedKeys ) ) {
52
+ $data = array_intersect_key( $data, array_flip( $restrictedKeys ) );
53
+ }
54
+ $this->raw = $data;
55
+ return $this;
56
+ }
57
+
58
+ public function reset() :self {
59
+ $this->raw = [];
60
+ return $this;
61
+ }
62
+
63
+ public function getRawData() :array {
64
+ return is_array( $this->raw ) ? $this->raw : [];
65
+ }
66
+
67
+ /**
68
+ * @return array
69
+ * @deprecated
70
+ */
71
+ public function getRawDataAsArray() :array {
72
+ return $this->getRawData();
73
+ }
74
+ }
src/lib/vendor/fernleafsystems/utilities/src/Data/Adapter/DynamicPropertiesClass.php ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace FernleafSystems\Utilities\Data\Adapter;
4
+
5
+ /**
6
+ * Use this instead of the trait wherever possible for more flexible inheritance
7
+ * Class DynamicPropertiesBase
8
+ * @package FernleafSystems\Utilities\Data\Adapter
9
+ */
10
+ class DynamicPropertiesClass {
11
+
12
+ use DynamicProperties;
13
+ }
src/lib/vendor/fernleafsystems/utilities/src/Data/Adapter/StdClassAdapter.php CHANGED
@@ -4,7 +4,8 @@ namespace FernleafSystems\Utilities\Data\Adapter;
4
 
5
  /**
6
  * Trait StdClassAdapter
7
- * @package FernleafSystems\Utilities\Data\Adapter
 
8
  */
9
  trait StdClassAdapter {
10
 
@@ -59,7 +60,7 @@ trait StdClassAdapter {
59
  * @param array $aRestrictedKeys
60
  * @return $this
61
  */
62
- public function applyFromArray( $aDataValues, $aRestrictedKeys = array() ) {
63
  if ( !empty( $aRestrictedKeys ) ) {
64
  $aDataValues = array_intersect_key( $aDataValues, array_flip( $aRestrictedKeys ) );
65
  }
@@ -71,13 +72,13 @@ trait StdClassAdapter {
71
  * @return $this
72
  */
73
  public function reset() {
74
- $this->aRaw = array();
75
  return $this;
76
  }
77
 
78
  /**
79
- * @deprecated
80
  * @return \stdClass
 
81
  */
82
  public function getRawData() {
83
  return (object)$this->getRawDataAsArray();
@@ -88,7 +89,7 @@ trait StdClassAdapter {
88
  */
89
  public function getRawDataAsArray() {
90
  if ( !is_array( $this->aRaw ) ) {
91
- $this->aRaw = array();
92
  }
93
  return $this->aRaw;
94
  }
@@ -107,7 +108,7 @@ trait StdClassAdapter {
107
  * @param array $aDefault
108
  * @return array
109
  */
110
- public function getArrayParam( $sKey, $aDefault = array() ) {
111
  return is_array( $this->{$sKey} ) ? $this->{$sKey} : $aDefault;
112
  }
113
 
@@ -141,9 +142,9 @@ trait StdClassAdapter {
141
  }
142
 
143
  /**
144
- * @deprecated use applyFromArray()
145
  * @param object $oRaw
146
  * @return $this
 
147
  */
148
  public function setRawData( $oRaw ) {
149
  return $this->applyFromArray( (array)$oRaw );
4
 
5
  /**
6
  * Trait StdClassAdapter
7
+ * @package FernleafSystems\Utilities\Data\Adapter
8
+ * @deprecated 1.4
9
  */
10
  trait StdClassAdapter {
11
 
60
  * @param array $aRestrictedKeys
61
  * @return $this
62
  */
63
+ public function applyFromArray( $aDataValues, $aRestrictedKeys = [] ) {
64
  if ( !empty( $aRestrictedKeys ) ) {
65
  $aDataValues = array_intersect_key( $aDataValues, array_flip( $aRestrictedKeys ) );
66
  }
72
  * @return $this
73
  */
74
  public function reset() {
75
+ $this->aRaw = [];
76
  return $this;
77
  }
78
 
79
  /**
 
80
  * @return \stdClass
81
+ * @deprecated
82
  */
83
  public function getRawData() {
84
  return (object)$this->getRawDataAsArray();
89
  */
90
  public function getRawDataAsArray() {
91
  if ( !is_array( $this->aRaw ) ) {
92
+ $this->aRaw = [];
93
  }
94
  return $this->aRaw;
95
  }
108
  * @param array $aDefault
109
  * @return array
110
  */
111
+ public function getArrayParam( $sKey, $aDefault = [] ) {
112
  return is_array( $this->{$sKey} ) ? $this->{$sKey} : $aDefault;
113
  }
114
 
142
  }
143
 
144
  /**
 
145
  * @param object $oRaw
146
  * @return $this
147
+ * @deprecated use applyFromArray()
148
  */
149
  public function setRawData( $oRaw ) {
150
  return $this->applyFromArray( (array)$oRaw );
src/lib/vendor/fernleafsystems/utilities/src/Data/CaptureOutput.php ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php declare( strict_types=1 );
2
+
3
+ namespace FernleafSystems\Utilities\Data;
4
+
5
+ class CaptureOutput {
6
+
7
+ public static function Capture( callable $func, array $args = [] ) :string {
8
+ ob_start();
9
+ empty( $args ) ? call_user_func( $func ) : call_user_func_array( $func, $args );
10
+ $out = ob_get_clean();
11
+ return is_string( $out ) ? $out : 'No output from capture';
12
+ }
13
+ }
src/lib/vendor/fernleafsystems/utilities/src/Logic/ExecOnce.php ADDED
@@ -0,0 +1,31 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace FernleafSystems\Utilities\Logic;
4
+
5
+ trait ExecOnce {
6
+
7
+ private $hasExecuted = false;
8
+
9
+ protected function canRun() :bool {
10
+ return true;
11
+ }
12
+
13
+ public function execute() {
14
+ if ( !$this->isAlreadyExecuted() && $this->canRun() ) {
15
+ $this->hasExecuted = true;
16
+ $this->run();
17
+ }
18
+ }
19
+
20
+ protected function isAlreadyExecuted() :bool {
21
+ return (bool)$this->hasExecuted;
22
+ }
23
+
24
+ public function resetExecution() :self {
25
+ $this->hasExecuted = false;
26
+ return $this;
27
+ }
28
+
29
+ protected function run() {
30
+ }
31
+ }
src/lib/vendor/fernleafsystems/wordpress-services/src/Core/AdminNotices.php CHANGED
@@ -184,10 +184,10 @@ class AdminNotices {
184
  }
185
 
186
  /**
187
- * @param $sMessage
188
  */
189
- public function addFlashMessage( $sMessage ) {
190
- Services::Data()->setCookie( $this->getActionPrefix().'flash', esc_attr( $sMessage ) );
191
  }
192
 
193
  protected function flashNotice() {
184
  }
185
 
186
  /**
187
+ * @param $msg
188
  */
189
+ public function addFlashMessage( string $msg ) {
190
+ Services::Response()->cookieSet( $this->getActionPrefix().'flash', esc_attr( $msg ) );
191
  }
192
 
193
  protected function flashNotice() {
src/lib/vendor/fernleafsystems/wordpress-services/src/Core/CoreFileHashes.php CHANGED
@@ -14,81 +14,78 @@ class CoreFileHashes {
14
  /**
15
  * @var array
16
  */
17
- private $aHashes;
18
 
19
  /**
20
  * Filters out wp-content plugins/themes data.
21
- * @return array
22
  */
23
- public function getHashes() {
24
- if ( !isset( $this->aHashes ) ) {
25
- $aHashes = Services::WpGeneral()->getCoreChecksums();
26
 
27
- $this->aHashes = array_intersect_key(
28
- $aHashes,
29
  array_flip( array_filter(
30
- array_keys( $aHashes ),
31
- function ( $sFilePath ) {
32
- return preg_match( '#wp-content/(plugins|themes)#i', $sFilePath ) === 0;
33
  }
34
  ) )
35
  );
36
  }
37
- return $this->aHashes;
38
  }
39
 
40
  /**
41
- * @param string $sFile
42
  * @return string|null
43
  */
44
- public function getFileHash( $sFile ) {
45
- $sNorm = $this->getFileFragment( $sFile );
46
  return $this->isCoreFile( $sNorm ) ? $this->getHashes()[ $sNorm ] : null;
47
  }
48
 
49
  /**
50
- * @param string $sFile
51
  * @return string
52
  */
53
- public function getFileFragment( $sFile ) {
54
- return Services::WpFs()->getPathRelativeToAbsPath( $sFile );
55
  }
56
 
57
  /**
58
- * @param string $sFile
59
  * @return string
60
  */
61
- public function getAbsolutePathFromFragment( $sFile ) {
62
- return wp_normalize_path( path_join( ABSPATH, $this->getFileFragment( $sFile ) ) );
63
  }
64
 
65
  /**
66
- * @param string $sFile
67
  * @return bool
68
  */
69
- public function isCoreFile( $sFile ) {
70
- return array_key_exists( $this->getFileFragment( $sFile ), $this->getHashes() );
71
  }
72
 
73
  /**
74
- * @param string $sFullPath
75
  * @return bool
76
  */
77
- public function isCoreFileHashValid( $sFullPath ) {
78
  try {
79
- $bValid = $this->isCoreFile( $sFullPath )
80
- && ( new CompareHash() )->isEqualFileMd5( $sFullPath, $this->getFileHash( $sFullPath ) );
81
  }
82
  catch ( \Exception $oE ) {
83
- $bValid = false;
84
  }
85
- return $bValid;
86
  }
87
 
88
- /**
89
- * @return bool
90
- */
91
- public function isReady() {
92
- return ( count( $this->getHashes() ) > 0 );
93
  }
94
  }
14
  /**
15
  * @var array
16
  */
17
+ private $hashes;
18
 
19
  /**
20
  * Filters out wp-content plugins/themes data.
21
+ * @return string[]
22
  */
23
+ public function getHashes() :array {
24
+ if ( !isset( $this->hashes ) ) {
25
+ $hashes = Services::WpGeneral()->getCoreChecksums();
26
 
27
+ $this->hashes = array_intersect_key(
28
+ $hashes,
29
  array_flip( array_filter(
30
+ array_keys( $hashes ),
31
+ function ( $file ) {
32
+ return preg_match( '#wp-content/(plugins|themes)#i', $file ) === 0;
33
  }
34
  ) )
35
  );
36
  }
37
+ return $this->hashes;
38
  }
39
 
40
  /**
41
+ * @param string $file
42
  * @return string|null
43
  */
44
+ public function getFileHash( $file ) {
45
+ $sNorm = $this->getFileFragment( $file );
46
  return $this->isCoreFile( $sNorm ) ? $this->getHashes()[ $sNorm ] : null;
47
  }
48
 
49
  /**
50
+ * @param string $file
51
  * @return string
52
  */
53
+ public function getFileFragment( $file ) :string {
54
+ return Services::WpFs()->getPathRelativeToAbsPath( $file );
55
  }
56
 
57
  /**
58
+ * @param string $file
59
  * @return string
60
  */
61
+ public function getAbsolutePathFromFragment( $file ) :string {
62
+ return wp_normalize_path( path_join( ABSPATH, $this->getFileFragment( $file ) ) );
63
  }
64
 
65
  /**
66
+ * @param string $file
67
  * @return bool
68
  */
69
+ public function isCoreFile( $file ) :bool {
70
+ return array_key_exists( $this->getFileFragment( $file ), $this->getHashes() );
71
  }
72
 
73
  /**
74
+ * @param string $fullPath
75
  * @return bool
76
  */
77
+ public function isCoreFileHashValid( $fullPath ) :bool {
78
  try {
79
+ $valid = $this->isCoreFile( $fullPath )
80
+ && ( new CompareHash() )->isEqualFileMd5( $fullPath, $this->getFileHash( $fullPath ) );
81
  }
82
  catch ( \Exception $oE ) {
83
+ $valid = false;
84
  }
85
+ return $valid;
86
  }
87
 
88
+ public function isReady() :bool {
89
+ return count( $this->getHashes() ) > 0;
 
 
 
90
  }
91
  }
src/lib/vendor/fernleafsystems/wordpress-services/src/Core/Fs.php CHANGED
@@ -228,14 +228,14 @@ class Fs {
228
  }
229
 
230
  /**
231
- * @param $sPath
232
  * @return string
233
  */
234
- public function getPathRelativeToAbsPath( $sPath ) {
235
  return preg_replace(
236
  sprintf( '#^%s#i', preg_quote( wp_normalize_path( ABSPATH ), '#' ) ),
237
  '',
238
- wp_normalize_path( $sPath )
239
  );
240
  }
241
 
@@ -469,56 +469,39 @@ class Fs {
469
  return function_exists( 'rename' ) ? @rename( $sFilePathSource, $sFilePathDestination ) : null;
470
  }
471
 
472
- /**
473
- * @param string $sPath
474
- * @return bool
475
- */
476
- public function isDir( $sPath ) {
477
- $oFs = $this->getWpfs();
478
- if ( $oFs && $oFs->is_dir( $sPath ) ) {
479
- return true;
480
- }
481
- return function_exists( 'is_dir' ) ? is_dir( $sPath ) : false;
482
  }
483
 
484
- /**
485
- * @param $sPath
486
- * @return bool|mixed
487
- */
488
- public function isFile( $sPath ) {
489
- $oFs = $this->getWpfs();
490
- if ( $oFs && $oFs->is_file( $sPath ) ) {
491
- return true;
492
- }
493
- return function_exists( 'is_file' ) ? is_file( $sPath ) : null;
494
  }
495
 
496
- /**
497
- * @return bool
498
- */
499
- public function isFilesystemAccessDirect() {
500
- return ( $this->getWpfs() instanceof \WP_Filesystem_Direct );
501
  }
502
 
503
  /**
504
- * @param string $sPath
505
  * @return bool
506
  */
507
- public function mkdir( $sPath ) {
508
- return wp_mkdir_p( $sPath );
509
  }
510
 
511
  /**
512
- * @param string $sFilePath
513
  * @param int $nTime
514
  * @return bool|mixed
515
  */
516
- public function touch( $sFilePath, $nTime = null ) {
517
  $oFs = $this->getWpfs();
518
- if ( $oFs && $oFs->touch( $sFilePath, $nTime ) ) {
519
  return true;
520
  }
521
- return function_exists( 'touch' ) ? @touch( $sFilePath, $nTime ) : null;
522
  }
523
 
524
  /**
@@ -531,8 +514,10 @@ class Fs {
531
  return $this->oWpfs;
532
  }
533
 
534
- /**
535
- */
 
 
536
  private function initFileSystem() {
537
  if ( is_null( $this->oWpfs ) ) {
538
  $this->oWpfs = false;
228
  }
229
 
230
  /**
231
+ * @param $path
232
  * @return string
233
  */
234
+ public function getPathRelativeToAbsPath( $path ) :string {
235
  return preg_replace(
236
  sprintf( '#^%s#i', preg_quote( wp_normalize_path( ABSPATH ), '#' ) ),
237
  '',
238
+ wp_normalize_path( $path )
239
  );
240
  }
241
 
469
  return function_exists( 'rename' ) ? @rename( $sFilePathSource, $sFilePathDestination ) : null;
470
  }
471
 
472
+ public function isDir( string $path ) :bool {
473
+ return ( $this->hasWpfs() && $this->getWpfs()->is_dir( $path ) )
474
+ || ( function_exists( 'is_dir' ) ? is_dir( $path ) : false );
 
 
 
 
 
 
 
475
  }
476
 
477
+ public function isFile( $path ) :bool {
478
+ return ( $this->hasWpfs() && $this->getWpfs()->is_file( $path ) )
479
+ || ( function_exists( 'is_file' ) ? is_file( $path ) : false );
 
 
 
 
 
 
 
480
  }
481
 
482
+ public function isFilesystemAccessDirect() :bool {
483
+ return $this->getWpfs() instanceof \WP_Filesystem_Direct;
 
 
 
484
  }
485
 
486
  /**
487
+ * @param string $path
488
  * @return bool
489
  */
490
+ public function mkdir( $path ) {
491
+ return wp_mkdir_p( $path );
492
  }
493
 
494
  /**
495
+ * @param string $path
496
  * @param int $nTime
497
  * @return bool|mixed
498
  */
499
+ public function touch( $path, $nTime = null ) {
500
  $oFs = $this->getWpfs();
501
+ if ( $oFs && $oFs->touch( $path, $nTime ) ) {
502
  return true;
503
  }
504
+ return function_exists( 'touch' ) ? @touch( $path, $nTime ) : null;
505
  }
506
 
507
  /**
514
  return $this->oWpfs;
515
  }
516
 
517
+ protected function hasWpfs() :bool {
518
+ return $this->getWpfs() instanceof \WP_Filesystem_Base;
519
+ }
520
+
521
  private function initFileSystem() {
522
  if ( is_null( $this->oWpfs ) ) {
523
  $this->oWpfs = false;
src/lib/vendor/fernleafsystems/wordpress-services/src/Core/General.php CHANGED
@@ -976,14 +976,14 @@ class General {
976
  }
977
 
978
  /**
979
- * @param string $sUrl
980
  * @param array $aQueryParams
981
  * @param bool $bSafe
982
  * @param bool $bProtectAgainstInfiniteLoops - if false, ignores the redirect loop protection
983
  * @deprecated
984
  */
985
- public function doRedirect( $sUrl, $aQueryParams = [], $bSafe = true, $bProtectAgainstInfiniteLoops = true ) {
986
- Services::Response()->redirect( $sUrl, $aQueryParams, $bSafe, $bProtectAgainstInfiniteLoops );
987
  }
988
 
989
  /**
976
  }
977
 
978
  /**
979
+ * @param string $url
980
  * @param array $aQueryParams
981
  * @param bool $bSafe
982
  * @param bool $bProtectAgainstInfiniteLoops - if false, ignores the redirect loop protection
983
  * @deprecated
984
  */
985
+ public function doRedirect( $url, $aQueryParams = [], $bSafe = true, $bProtectAgainstInfiniteLoops = true ) {
986
+ Services::Response()->redirect( $url, $aQueryParams, $bSafe, $bProtectAgainstInfiniteLoops );
987
  }
988
 
989
  /**
src/lib/vendor/fernleafsystems/wordpress-services/src/Core/Nonce.php CHANGED
@@ -16,7 +16,7 @@ class Nonce {
16
  /**
17
  * @var string
18
  */
19
- private $sAction;
20
 
21
  public function create() {
22
  if ( !$this->hasAction() ) {
@@ -26,15 +26,15 @@ class Nonce {
26
  }
27
 
28
  /**
29
- * @param string $sNonce
30
  * @return false|int
31
  * @throws \Exception
32
  */
33
- public function verify( $sNonce ) {
34
  if ( !$this->hasAction() ) {
35
  throw new \Exception( 'No action specified for nonce' );
36
  }
37
- return $this->isIncludeUserId() ? wp_verify_nonce( $sNonce, $this->getAction() ) : $this->verifyNonceNoUser( $sNonce );
38
  }
39
 
40
  /**
@@ -48,12 +48,12 @@ class Nonce {
48
  }
49
 
50
  /**
51
- * @param $sNonce
52
  * @return int
53
  * @throws \Exception
54
  */
55
- private function verifyNonceNoUser( $sNonce ) {
56
- if ( empty( $sNonce ) ) {
57
  throw new \Exception( 'Nonce is empty' );
58
  }
59
 
@@ -62,55 +62,38 @@ class Nonce {
62
 
63
  // Nonce generated 0-12 hours ago.
64
  $expected = substr( wp_hash( $i.'|'.$this->getAction().'|'.$token, 'nonce' ), -12, 10 );
65
- if ( hash_equals( $expected, $sNonce ) ) {
66
  return 1;
67
  }
68
 
69
  // Nonce generated 12-24 hours ago.
70
  $expected = substr( wp_hash( ( $i - 1 ).'|'.$this->getAction().'|'.$token, 'nonce' ), -12, 10 );
71
- if ( hash_equals( $expected, $sNonce ) ) {
72
  return 2;
73
  }
74
 
75
  throw new \Exception( 'Nonce verification failed.' );
76
  }
77
 
78
- /**
79
- * @return string
80
- */
81
- public function getAction() {
82
- return (string)$this->sAction;
83
  }
84
 
85
- /**
86
- * @return bool
87
- */
88
- public function hasAction() {
89
- return !empty( $this->sAction );
90
  }
91
 
92
- /**
93
- * @return bool
94
- */
95
- public function isIncludeUserId() {
96
  return isset( $this->bIncludeUserId ) ? (bool)$this->bIncludeUserId : true;
97
  }
98
 
99
- /**
100
- * @param bool $sAction
101
- * @return $this
102
- */
103
- public function setAction( $sAction ) {
104
- $this->sAction = $sAction;
105
  return $this;
106
  }
107
 
108
- /**
109
- * @param bool $bUse
110
- * @return $this
111
- */
112
- public function setIncludeUserId( $bUse ) {
113
- $this->bIncludeUserId = $bUse;
114
  return $this;
115
  }
116
  }
16
  /**
17
  * @var string
18
  */
19
+ private $action;
20
 
21
  public function create() {
22
  if ( !$this->hasAction() ) {
26
  }
27
 
28
  /**
29
+ * @param string $nonce
30
  * @return false|int
31
  * @throws \Exception
32
  */
33
+ public function verify( $nonce ) {
34
  if ( !$this->hasAction() ) {
35
  throw new \Exception( 'No action specified for nonce' );
36
  }
37
+ return $this->isIncludeUserId() ? wp_verify_nonce( $nonce, $this->getAction() ) : $this->verifyNonceNoUser( $nonce );
38
  }
39
 
40
  /**
48
  }
49
 
50
  /**
51
+ * @param $nonce
52
  * @return int
53
  * @throws \Exception
54
  */
55
+ private function verifyNonceNoUser( $nonce ) {
56
+ if ( empty( $nonce ) ) {
57
  throw new \Exception( 'Nonce is empty' );
58
  }
59
 
62
 
63
  // Nonce generated 0-12 hours ago.
64
  $expected = substr( wp_hash( $i.'|'.$this->getAction().'|'.$token, 'nonce' ), -12, 10 );
65
+ if ( hash_equals( $expected, $nonce ) ) {
66
  return 1;
67
  }
68
 
69
  // Nonce generated 12-24 hours ago.
70
  $expected = substr( wp_hash( ( $i - 1 ).'|'.$this->getAction().'|'.$token, 'nonce' ), -12, 10 );
71
+ if ( hash_equals( $expected, $nonce ) ) {
72
  return 2;
73
  }
74
 
75
  throw new \Exception( 'Nonce verification failed.' );
76
  }
77
 
78
+ public function getAction() :string {
79
+ return (string)$this->action;
 
 
 
80
  }
81
 
82
+ public function hasAction() :bool {
83
+ return !empty( $this->action );
 
 
 
84
  }
85
 
86
+ public function isIncludeUserId() :bool {
 
 
 
87
  return isset( $this->bIncludeUserId ) ? (bool)$this->bIncludeUserId : true;
88
  }
89
 
90
+ public function setAction( string $action ) :self {
91
+ $this->action = $action;
 
 
 
 
92
  return $this;
93
  }
94
 
95
+ public function setIncludeUserId( bool $use ) :self {
96
+ $this->bIncludeUserId = $use;
 
 
 
 
97
  return $this;
98
  }
99
  }
src/lib/vendor/fernleafsystems/wordpress-services/src/Core/Request.php CHANGED
@@ -14,27 +14,27 @@ class Request {
14
  /**
15
  * @var array
16
  */
17
- private $aPost;
18
 
19
  /**
20
  * @var array
21
  */
22
- private $aQuery;
23
 
24
  /**
25
  * @var array
26
  */
27
- private $aCookie;
28
 
29
  /**
30
  * @var array
31
  */
32
- private $aServer;
33
 
34
  /**
35
  * @var array
36
  */
37
- private $aEnv;
38
 
39
  /**
40
  * @var int
@@ -49,36 +49,35 @@ class Request {
49
  /**
50
  * @var string
51
  */
52
- private $sContent;
53
 
54
  /**
55
  * Request constructor.
56
  */
57
  public function __construct() {
58
- $this->aPost = is_array( $_POST ) ? $_POST : [];
59
- $this->aQuery = is_array( $_GET ) ? $_GET : [];
60
- $this->aCookie = &$_COOKIE;
61
- $this->aServer = is_array( $_SERVER ) ? $_SERVER : [];
62
- $this->aEnv = is_array( $_ENV ) ? $_ENV : [];
 
 
 
 
 
63
  $this->ts();
64
  }
65
 
66
- /**
67
- * @return string
68
- */
69
- public function getContent() {
70
- if ( !isset( $this->sContent ) ) {
71
- $this->sContent = file_get_contents( 'php://input' );
72
  }
73
- return $this->sContent;
74
  }
75
 
76
- /**
77
- * @return string
78
- */
79
- public function getMethod() {
80
- $sRequestMethod = $this->server( 'REQUEST_METHOD' );
81
- return ( empty( $sRequestMethod ) ? '' : strtolower( $sRequestMethod ) );
82
  }
83
 
84
  /**
@@ -86,20 +85,17 @@ class Request {
86
  * @return int
87
  */
88
  public function mts( $bMsOnly = false ) {
89
- $nT = $this->ts();
90
  if ( empty( $this->nMts ) ) {
91
- $nT = $bMsOnly ? 0 : $nT;
92
  }
93
  else {
94
- $nT = $bMsOnly ? preg_replace( '#^[0-9]+\.#', '', $this->nMts ) : $this->nMts;
95
  }
96
- return $nT;
97
  }
98
 
99
- /**
100
- * @return int
101
- */
102
- public function ts() {
103
  if ( empty( $this->nTs ) ) {
104
  $this->nTs = time();
105
  $this->nMts = function_exists( 'microtime' ) ? @microtime( true ) : false;
@@ -112,172 +108,146 @@ class Request {
112
  * @return Carbon
113
  */
114
  public function carbon( $bSetTimezone = false ) {
115
- $oWP = Services::WpGeneral();
116
- $oC = new Carbon();
117
- $oC->setTimestamp( $this->ts() );
118
- $oC->setLocale( $oWP->getLocaleCountry() );
119
  if ( $bSetTimezone ) {
120
- $sTZ = $oWP->getOption( 'timezone_string' );
121
- if ( !empty( $sTZ ) ) {
122
- $oC->setTimezone( $sTZ );
123
  }
124
  }
125
- return $oC;
126
  }
127
 
128
- /**
129
- * @param bool $bIncludeCookie
130
- * @return array
131
- */
132
- public function getRawRequestParams( $bIncludeCookie = true ) {
133
- $aParams = array_merge( $this->aQuery, $this->aPost );
134
- return $bIncludeCookie ? array_merge( $aParams, $this->aCookie ) : $aParams;
135
  }
136
 
137
- /**
138
- * @return string
139
- */
140
- public function getHost() {
141
- return $this->server( 'HTTP_HOST' );
142
  }
143
 
144
- /**
145
- * @return string URI Path in lowercase
146
- */
147
- public function getPath() {
148
  return $this->getUriParts()[ 'path' ];
149
  }
150
 
151
- /**
152
- * @return string
153
- */
154
- public function getServerAddress() {
155
- return $this->server( 'SERVER_ADDR' );
156
  }
157
 
158
- /**
159
- * @return string
160
- */
161
- public function getUri() {
162
- return $this->server( 'REQUEST_URI' );
163
  }
164
 
165
- /**
166
- * @return array
167
- */
168
- public function getUriParts() {
169
- $sPath = $this->getUri();
170
- if ( strpos( $sPath, '?' ) !== false ) {
171
- list( $sPath, $sQuery ) = explode( '?', $sPath, 2 );
172
  }
173
  else {
174
- $sQuery = '';
175
  }
176
  return [
177
- 'path' => $sPath,
178
- 'query' => $sQuery,
179
  ];
180
  }
181
 
182
- /**
183
- * @return string
184
- */
185
- public function getUserAgent() {
186
- return $this->server( 'HTTP_USER_AGENT' );
187
  }
188
 
189
- /**
190
- * @return bool
191
- */
192
- public function isPost() {
193
- return ( $this->getMethod() == 'post' );
194
  }
195
 
196
- /**
197
- * @return int
198
- */
199
- public function countQuery() {
200
- return $this->count( str_replace( 'count', '', __FUNCTION__ ) );
201
  }
202
 
203
- /**
204
- * @return int
205
- */
206
- public function countPost() {
207
- return $this->count( str_replace( 'count', '', __FUNCTION__ ) );
208
  }
209
 
210
- /**
211
- * @param string $sContainer
212
- * @return int
213
- */
214
- private function count( $sContainer ) {
215
- $sArray = 'a'.$sContainer;
216
- $aArray = $this->{$sArray};
217
- return is_array( $aArray ) ? count( $aArray ) : 0;
218
  }
219
 
220
  /**
221
- * @param string $sKey
222
- * @param null $mDefault
223
  * @return mixed|null
224
  */
225
- public function cookie( $sKey, $mDefault = null ) {
226
- return $this->arrayFetch( __FUNCTION__, $sKey, $mDefault );
227
  }
228
 
229
  /**
230
- * @param string $sKey
231
- * @param null $mDefault
232
  * @return mixed|null
233
  */
234
- public function env( $sKey, $mDefault = null ) {
235
- return $this->arrayFetch( __FUNCTION__, $sKey, $mDefault );
236
  }
237
 
238
  /**
239
- * @param string $sKey
240
- * @param null $mDefault
241
  * @return mixed|null
242
  */
243
- public function post( $sKey, $mDefault = null ) {
244
- return $this->arrayFetch( __FUNCTION__, $sKey, $mDefault );
245
  }
246
 
247
  /**
248
- * @param string $sKey
249
- * @param null $mDefault
250
  * @return mixed|null
251
  */
252
- public function query( $sKey, $mDefault = null ) {
253
- return $this->arrayFetch( __FUNCTION__, $sKey, $mDefault );
254
  }
255
 
256
  /**
257
  * POST > GET > COOKIE
258
- * @param string $sKey
259
- * @param bool $bIncludeCookie
260
- * @param null $mDefault
261
  * @return mixed|null
262
  */
263
- public function request( $sKey, $bIncludeCookie = false, $mDefault = null ) {
264
- $mFetchVal = $this->post( $sKey );
265
- if ( is_null( $mFetchVal ) ) {
266
- $mFetchVal = $this->query( $sKey );
267
- if ( $bIncludeCookie && is_null( $mFetchVal ) ) {
268
- $mFetchVal = $this->cookie( $sKey );
269
  }
270
  }
271
- return is_null( $mFetchVal ) ? $mDefault : $mFetchVal;
272
  }
273
 
274
  /**
275
- * @param string $sKey
276
- * @param null $mDefault
277
  * @return mixed|null
278
  */
279
- public function server( $sKey, $mDefault = null ) {
280
- return $this->arrayFetch( __FUNCTION__, $sKey, $mDefault );
 
 
 
 
 
 
 
 
 
 
 
281
  }
282
 
283
  /**
@@ -285,6 +255,7 @@ class Request {
285
  * @param string $sKey
286
  * @param mixed $mDefault
287
  * @return mixed|null
 
288
  */
289
  private function arrayFetch( $sContainer, $sKey, $mDefault = null ) {
290
  $sArray = 'a'.ucfirst( $sContainer );
@@ -335,4 +306,15 @@ class Request {
335
  public function getRequestUriParts() {
336
  return $this->getUriParts();
337
  }
 
 
 
 
 
 
 
 
 
 
 
338
  }
14
  /**
15
  * @var array
16
  */
17
+ private $post;
18
 
19
  /**
20
  * @var array
21
  */
22
+ private $query;
23
 
24
  /**
25
  * @var array
26
  */
27
+ private $cookie;
28
 
29
  /**
30
  * @var array
31
  */
32
+ private $server;
33
 
34
  /**
35
  * @var array
36
  */
37
+ private $env;
38
 
39
  /**
40
  * @var int
49
  /**
50
  * @var string
51
  */
52
+ private $content;
53
 
54
  /**
55
  * Request constructor.
56
  */
57
  public function __construct() {
58
+ $this->post = is_array( $_POST ) ? $_POST : [];
59
+ $this->query = is_array( $_GET ) ? $_GET : [];
60
+ if ( is_array( $_COOKIE ) ) {
61
+ $this->cookie = &$_COOKIE;
62
+ }
63
+ else {
64
+ $this->cookie = [];
65
+ }
66
+ $this->server = is_array( $_SERVER ) ? $_SERVER : [];
67
+ $this->env = is_array( $_ENV ) ? $_ENV : [];
68
  $this->ts();
69
  }
70
 
71
+ public function getContent() :string {
72
+ if ( !isset( $this->content ) ) {
73
+ $this->content = file_get_contents( 'php://input' );
 
 
 
74
  }
75
+ return (string)$this->content;
76
  }
77
 
78
+ public function getMethod() :string {
79
+ $method = (string)$this->server( 'REQUEST_METHOD' );
80
+ return empty( $method ) ? '' : strtolower( $method );
 
 
 
81
  }
82
 
83
  /**
85
  * @return int
86
  */
87
  public function mts( $bMsOnly = false ) {
88
+ $now = $this->ts();
89
  if ( empty( $this->nMts ) ) {
90
+ $now = $bMsOnly ? 0 : $now;
91
  }
92
  else {
93
+ $now = $bMsOnly ? preg_replace( '#^[0-9]+\.#', '', $this->nMts ) : $this->nMts;
94
  }
95
+ return $now;
96
  }
97
 
98
+ public function ts() :int {
 
 
 
99
  if ( empty( $this->nTs ) ) {
100
  $this->nTs = time();
101
  $this->nMts = function_exists( 'microtime' ) ? @microtime( true ) : false;
108
  * @return Carbon
109
  */
110
  public function carbon( $bSetTimezone = false ) {
111
+ $WP = Services::WpGeneral();
112
+ $carbon = new Carbon();
113
+ $carbon->setTimestamp( $this->ts() );
114
+ $carbon->setLocale( $WP->getLocaleCountry() );
115
  if ( $bSetTimezone ) {
116
+ $TZ = $WP->getOption( 'timezone_string' );
117
+ if ( !empty( $TZ ) ) {
118
+ $carbon->setTimezone( $TZ );
119
  }
120
  }
121
+ return $carbon;
122
  }
123
 
124
+ public function getRawRequestParams( bool $includeCookies = true ) :array {
125
+ $params = array_merge( $this->query, $this->post );
126
+ return $includeCookies ? array_merge( $params, $this->cookie ) : $params;
 
 
 
 
127
  }
128
 
129
+ public function getHost() :string {
130
+ return (string)$this->server( 'HTTP_HOST' );
 
 
 
131
  }
132
 
133
+ public function getPath() :string {
 
 
 
134
  return $this->getUriParts()[ 'path' ];
135
  }
136
 
137
+ public function getServerAddress() :string {
138
+ return (string)$this->server( 'SERVER_ADDR' );
 
 
 
139
  }
140
 
141
+ public function getUri() :string {
142
+ return (string)$this->server( 'REQUEST_URI' );
 
 
 
143
  }
144
 
145
+ public function getUriParts() :array {
146
+ $path = $this->getUri();
147
+ if ( strpos( $path, '?' ) !== false ) {
148
+ list( $path, $query ) = explode( '?', $path, 2 );
 
 
 
149
  }
150
  else {
151
+ $query = '';
152
  }
153
  return [
154
+ 'path' => $path,
155
+ 'query' => $query,
156
  ];
157
  }
158
 
159
+ public function getUserAgent() :string {
160
+ return (string)$this->server( 'HTTP_USER_AGENT' );
 
 
 
161
  }
162
 
163
+ public function isGet() :bool {
164
+ return $this->getMethod() == 'get';
 
 
 
165
  }
166
 
167
+ public function isPost() :bool {
168
+ return $this->getMethod() == 'post';
 
 
 
169
  }
170
 
171
+ public function countQuery() :int {
172
+ return count( $this->query );
 
 
 
173
  }
174
 
175
+ public function countPost() :int {
176
+ return count( $this->post );
 
 
 
 
 
 
177
  }
178
 
179
  /**
180
+ * @param string $key
181
+ * @param null $default
182
  * @return mixed|null
183
  */
184
+ public function cookie( $key, $default = null ) {
185
+ return $this->fetch( $this->cookie, $key, $default );
186
  }
187
 
188
  /**
189
+ * @param string $key
190
+ * @param null $default
191
  * @return mixed|null
192
  */
193
+ public function env( $key, $default = null ) {
194
+ return $this->fetch( $this->env, $key, $default );
195
  }
196
 
197
  /**
198
+ * @param string $key
199
+ * @param null $default
200
  * @return mixed|null
201
  */
202
+ public function post( $key, $default = null ) {
203
+ return $this->fetch( $this->post, $key, $default );
204
  }
205
 
206
  /**
207
+ * @param string $key
208
+ * @param null $default
209
  * @return mixed|null
210
  */
211
+ public function query( $key, $default = null ) {
212
+ return $this->fetch( $this->query, $key, $default );
213
  }
214
 
215
  /**
216
  * POST > GET > COOKIE
217
+ * @param string $key
218
+ * @param bool $includeCookies
219
+ * @param null $default
220
  * @return mixed|null
221
  */
222
+ public function request( $key, $includeCookies = false, $default = null ) {
223
+ $value = $this->post( $key );
224
+ if ( is_null( $value ) ) {
225
+ $value = $this->query( $key );
226
+ if ( $includeCookies && is_null( $value ) ) {
227
+ $value = $this->cookie( $key );
228
  }
229
  }
230
+ return is_null( $value ) ? $default : $value;
231
  }
232
 
233
  /**
234
+ * @param string $key
235
+ * @param null $default
236
  * @return mixed|null
237
  */
238
+ public function server( $key, $default = null ) {
239
+ return $this->fetch( $this->server, $key, $default );
240
+ }
241
+
242
+ /**
243
+ * @param array $container
244
+ * @param string $key
245
+ * @param mixed $default
246
+ * @return mixed|null
247
+ */
248
+ private function fetch( array $container, $key, $default = null ) {
249
+ $value = $container[ $key ] ?? $default;
250
+ return is_null( $value ) ? $default : $value;
251
  }
252
 
253
  /**
255
  * @param string $sKey
256
  * @param mixed $mDefault
257
  * @return mixed|null
258
+ * @deprecated
259
  */
260
  private function arrayFetch( $sContainer, $sKey, $mDefault = null ) {
261
  $sArray = 'a'.ucfirst( $sContainer );
306
  public function getRequestUriParts() {
307
  return $this->getUriParts();
308
  }
309
+
310
+ /**
311
+ * @param string $sContainer
312
+ * @return int
313
+ * @deprecated
314
+ */
315
+ private function count( $sContainer ) {
316
+ $sArray = 'a'.$sContainer;
317
+ $aArray = $this->{$sArray};
318
+ return is_array( $aArray ) ? count( $aArray ) : 0;
319
+ }
320
  }
src/lib/vendor/fernleafsystems/wordpress-services/src/Core/Response.php CHANGED
@@ -17,60 +17,60 @@ class Response {
17
  }
18
 
19
  /**
20
- * @param string $sKey
21
  * @return bool
22
  */
23
- public function cookieDelete( $sKey ) {
24
- unset( $_COOKIE[ $sKey ] );
25
- return $this->cookieSet( $sKey, '', -1000000 );
26
  }
27
 
28
  /**
29
- * @param string $sKey
30
- * @param string $mValue
31
- * @param int $nExpireLength
32
- * @param null $sPath
33
- * @param null $sDomain
34
- * @param bool $bSsl
35
  * @return bool
36
  */
37
- public function cookieSet( $sKey, $mValue, $nExpireLength = 3600, $sPath = null, $sDomain = null, $bSsl = null ) {
38
  if ( function_exists( 'headers_sent' ) && headers_sent() ) {
39
  return false;
40
  }
41
- $_COOKIE[ $sKey ] = $mValue;
42
  return setcookie(
43
- $sKey,
44
- $mValue,
45
- (int)( Services::Request()->ts() + $nExpireLength ),
46
- ( is_null( $sPath ) && defined( 'COOKIEPATH' ) ) ? COOKIEPATH : $sPath,
47
- ( is_null( $sDomain ) && defined( 'COOKIE_DOMAIN' ) ) ? COOKIE_DOMAIN : $sDomain,
48
- is_null( $bSsl ) ? ( is_ssl() ? true : false ) : $bSsl
49
  );
50
  }
51
 
52
  /**
53
- * @param string $sStringContent
54
- * @param string $sFilename
55
  * @return bool
56
  */
57
- public function downloadStringAsFile( $sStringContent, $sFilename ) {
58
  header( "Content-type: application/octet-stream" );
59
- header( "Content-disposition: attachment; filename=".$sFilename );
60
  header( "Content-Transfer-Encoding: binary" );
61
- header( "Content-Length: ".strlen( $sStringContent ) );
62
- echo $sStringContent;
63
  die();
64
  }
65
 
66
  /**
67
- * @param string $sUrl
68
- * @param array $aQueryParams
69
- * @param bool $bSafe
70
  * @param bool $bProtectAgainstInfiniteLoops - if false, ignores the redirect loop protection
71
  */
72
- public function redirect( $sUrl, $aQueryParams = [], $bSafe = true, $bProtectAgainstInfiniteLoops = true ) {
73
- $sUrl = empty( $aQueryParams ) ? $sUrl : add_query_arg( $aQueryParams, $sUrl );
74
 
75
  // we prevent any repetitive redirect loops
76
  if ( $bProtectAgainstInfiniteLoops ) {
@@ -78,15 +78,15 @@ class Response {
78
  return;
79
  }
80
  else {
81
- Services::Data()->setCookie( 'icwp-isredirect', 'yes', 7 );
82
  }
83
  }
84
 
85
  // based on: https://make.wordpress.org/plugins/2015/04/20/fixing-add_query_arg-and-remove_query_arg-usage/
86
  // we now escape the URL to be absolutely sure since we can't guarantee the URL coming through there
87
- $sUrl = esc_url_raw( $sUrl );
88
  header( 'Cache-Control: no-store, no-cache' );
89
- $bSafe ? wp_safe_redirect( $sUrl ) : wp_redirect( $sUrl );
90
  exit();
91
  }
92
 
17
  }
18
 
19
  /**
20
+ * @param string $key
21
  * @return bool
22
  */
23
+ public function cookieDelete( $key ) {
24
+ unset( $_COOKIE[ $key ] );
25
+ return $this->cookieSet( $key, '', -1000000 );
26
  }
27
 
28
  /**
29
+ * @param string $key
30
+ * @param string $value
31
+ * @param int $duration
32
+ * @param null $path
33
+ * @param null $domain
34
+ * @param bool $ssl
35
  * @return bool
36
  */
37
+ public function cookieSet( $key, $value, $duration = 3600, $path = null, $domain = null, $ssl = null ) {
38
  if ( function_exists( 'headers_sent' ) && headers_sent() ) {
39
  return false;
40
  }
41
+ $_COOKIE[ $key ] = $value;
42
  return setcookie(
43
+ $key,
44
+ $value,
45
+ (int)( Services::Request()->ts() + $duration ),
46
+ ( is_null( $path ) && defined( 'COOKIEPATH' ) ) ? COOKIEPATH : $path,
47
+ ( is_null( $domain ) && defined( 'COOKIE_DOMAIN' ) ) ? COOKIE_DOMAIN : $domain,
48
+ is_null( $ssl ) ? ( is_ssl() ? true : false ) : $ssl
49
  );
50
  }
51
 
52
  /**
53
+ * @param string $content
54
+ * @param string $filename
55
  * @return bool
56
  */
57
+ public function downloadStringAsFile( $content, $filename ) {
58
  header( "Content-type: application/octet-stream" );
59
+ header( "Content-disposition: attachment; filename=".$filename );
60
  header( "Content-Transfer-Encoding: binary" );
61
+ header( "Content-Length: ".strlen( $content ) );
62
+ echo $content;
63
  die();
64
  }
65
 
66
  /**
67
+ * @param string $url
68
+ * @param array $queryParams
69
+ * @param bool $safe
70
  * @param bool $bProtectAgainstInfiniteLoops - if false, ignores the redirect loop protection
71
  */
72
+ public function redirect( $url, $queryParams = [], $safe = true, $bProtectAgainstInfiniteLoops = true ) {
73
+ $url = empty( $queryParams ) ? $url : add_query_arg( $queryParams, $url );
74
 
75
  // we prevent any repetitive redirect loops
76
  if ( $bProtectAgainstInfiniteLoops ) {
78
  return;
79
  }
80
  else {
81
+ $this->cookieSet( 'icwp-isredirect', 'yes', 7 );
82
  }
83
  }
84
 
85
  // based on: https://make.wordpress.org/plugins/2015/04/20/fixing-add_query_arg-and-remove_query_arg-usage/
86
  // we now escape the URL to be absolutely sure since we can't guarantee the URL coming through there
87
+ $url = esc_url_raw( $url );
88
  header( 'Cache-Control: no-store, no-cache' );
89
+ $safe ? wp_safe_redirect( $url ) : wp_redirect( $url );
90
  exit();
91
  }
92
 
src/lib/vendor/fernleafsystems/wordpress-services/src/Core/Themes.php CHANGED
@@ -19,15 +19,15 @@ class Themes {
19
  private $aLoadedVOs;
20
 
21
  /**
22
- * @param string $sThemeStylesheet
23
  * @return bool
24
  */
25
- public function activate( $sThemeStylesheet ) {
26
- if ( empty( $sThemeStylesheet ) ) {
27
  return false;
28
  }
29
 
30
- $oTheme = $this->getTheme( $sThemeStylesheet );
31
  if ( !$oTheme->exists() ) {
32
  return false;
33
  }
@@ -37,40 +37,40 @@ class Themes {
37
  // Now test currently active theme
38
  $oCurrentTheme = $this->getCurrent();
39
 
40
- return ( $sThemeStylesheet == $oCurrentTheme->get_stylesheet() );
41
  }
42
 
43
  /**
44
- * @param string $sStylesheet
45
  * @return bool|\WP_Error
46
  */
47
- public function delete( $sStylesheet ) {
48
- if ( empty( $sStylesheet ) ) {
49
  return false;
50
  }
51
  if ( !function_exists( 'delete_theme' ) ) {
52
  require_once( ABSPATH.'wp-admin/includes/theme.php' );
53
  }
54
- return function_exists( 'delete_theme' ) ? delete_theme( $sStylesheet ) : false;
55
  }
56
 
57
  /**
58
- * @param string $sSlug
59
  * @return bool
60
  */
61
- public function installFromWpOrg( $sSlug ) {
62
- $bSuccess = false;
63
  try {
64
- $oTheme = ( new Api() )
65
- ->setWorkingSlug( $sSlug )
66
  ->getInfo();
67
- if ( !empty( $oTheme->download_link ) ) {
68
- $bSuccess = $this->install( $oTheme->download_link, true )[ 'successful' ];
69
  }
70
  }
71
- catch ( \Exception $oE ) {
72
  }
73
- return $bSuccess;
74
  }
75
 
76
  /**
@@ -100,33 +100,33 @@ class Themes {
100
  }
101
 
102
  /**
103
- * @param string $sSlug
104
  * @param bool $bUseBackup
105
  * @return bool
106
  */
107
- public function reinstall( $sSlug, $bUseBackup = false ) {
108
  $bSuccess = false;
109
 
110
- if ( $this->isInstalled( $sSlug ) ) {
111
- $oFS = Services::WpFs();
112
 
113
- $oTheme = $this->getTheme( $sSlug );
114
 
115
  $sDir = $oTheme->get_stylesheet_directory();
116
- $sBackupDir = dirname( $sDir ).'/../../'.$sSlug.'bak'.time();
117
  if ( $bUseBackup ) {
118
  rename( $sDir, $sBackupDir );
119
  }
120
 
121
- $bSuccess = $this->installFromWpOrg( $sSlug );
122
  if ( $bSuccess ) {
123
  wp_update_themes(); //refreshes our update information
124
  if ( $bUseBackup ) {
125
- $oFS->deleteDir( $sBackupDir );
126
  }
127
  }
128
  elseif ( $bUseBackup ) {
129
- $oFS->deleteDir( $sDir );
130
  rename( $sBackupDir, $sDir );
131
  }
132
  }
@@ -134,15 +134,15 @@ class Themes {
134
  }
135
 
136
  /**
137
- * @param string $sFile
138
  * @return array
139
  */
140
- public function update( $sFile ) {
141
  require_once( ABSPATH.'wp-admin/includes/upgrade.php' );
142
  require_once( ABSPATH.'wp-admin/includes/class-wp-upgrader.php' );
143
 
144
  $oSkin = new \Automatic_Upgrader_Skin();
145
- $mResult = ( new \Theme_Upgrader( $oSkin ) )->upgrade( $sFile );
146
 
147
  return [
148
  'successful' => $mResult === true,
@@ -165,20 +165,16 @@ class Themes {
165
  return $this->getTheme();
166
  }
167
 
168
- /**
169
- * @param string $sStylesheet
170
- * @return bool
171
- */
172
- public function getExists( $sStylesheet ) {
173
- return $this->getTheme( $sStylesheet )->exists();
174
  }
175
 
176
  /**
177
- * @param string $sSlug - the folder name of the theme
178
  * @return string
179
  */
180
- public function getInstallationDir( $sSlug ) {
181
- return wp_normalize_path( $this->getTheme( $sSlug )->get_stylesheet_directory() );
182
  }
183
 
184
  /**
@@ -192,24 +188,24 @@ class Themes {
192
  }
193
 
194
  /**
195
- * @param string $sStylesheet
196
  * @param bool $bReload
197
  * @return WpThemeVo|null
198
  */
199
- public function getThemeAsVo( $sStylesheet, $bReload = false ) {
200
  try {
201
  if ( !is_array( $this->aLoadedVOs ) ) {
202
  $this->aLoadedVOs = [];
203
  }
204
- if ( $bReload || !isset( $this->aLoadedVOs[ $sStylesheet ] ) ) {
205
- $this->aLoadedVOs[ $sStylesheet ] = new WpThemeVo( $sStylesheet );
206
  }
207
- $oAsset = $this->aLoadedVOs[ $sStylesheet ];
208
  }
209
- catch ( \Exception $oE ) {
210
- $oAsset = null;
211
  }
212
- return $oAsset;
213
  }
214
 
215
  /**
@@ -218,8 +214,8 @@ class Themes {
218
  public function getThemesAsVo() {
219
  return array_filter(
220
  array_map(
221
- function ( $sStyleSheet ) {
222
- return $this->getThemeAsVo( $sStyleSheet );
223
  },
224
  $this->getInstalledStylesheets()
225
  )
@@ -229,11 +225,10 @@ class Themes {
229
  /**
230
  * @return string[]
231
  */
232
- public function getInstalledStylesheets() {
233
  return array_map(
234
- function ( $oTheme ) {
235
- /** @var \WP_Theme $oTheme */
236
- return $oTheme->get_stylesheet();
237
  },
238
  $this->getThemes()
239
  );
@@ -244,18 +239,18 @@ class Themes {
244
  * Abstracts the WordPress wp_get_themes()
245
  * @return \WP_Theme[]
246
  */
247
- public function getThemes() {
248
  require_once( ABSPATH.'wp-admin/includes/theme.php' );
249
  return function_exists( 'wp_get_themes' ) ? wp_get_themes() : [];
250
  }
251
 
252
  /**
253
- * @param string $sSlug
254
  * @return array|null
255
  */
256
- public function getUpdateInfo( $sSlug ) {
257
- $aU = $this->getUpdates();
258
- return isset( $aU[ $sSlug ] ) ? $aU[ $sSlug ] : null;
259
  }
260
 
261
  /**
@@ -282,20 +277,20 @@ class Themes {
282
  * @return array[] - keys are theme stylesheets
283
  */
284
  public function getAllExtendedData() {
285
- $oData = Services::WpGeneral()->getTransient( 'update_themes' );
286
  return array_merge(
287
- isset( $oData->no_update ) ? $oData->no_update : [],
288
- isset( $oData->response ) ? $oData->response : []
289
  );
290
  }
291
 
292
  /**
293
- * @param string $sSlug
294
  * @return array
295
  */
296
- public function getExtendedData( $sSlug ) {
297
- $aData = $this->getAllExtendedData();
298
- return isset( $aData[ $sSlug ] ) ? $aData[ $sSlug ] : [];
299
  }
300
 
301
  /**
@@ -303,49 +298,30 @@ class Themes {
303
  * @param bool $bCheckIsActiveParent
304
  * @return bool
305
  */
306
- public function isActive( $sSlug, $bCheckIsActiveParent = false ) {
307
  return ( $this->isInstalled( $sSlug ) && $this->getCurrent()->get_stylesheet() == $sSlug )
308
  || ( $bCheckIsActiveParent && $this->isActiveParent( $sSlug ) );
309
  }
310
 
311
- /**
312
- * @return bool
313
- */
314
- public function isActiveThemeAChild() {
315
- $oTheme = $this->getCurrent();
316
- return ( $oTheme->get_stylesheet() != $oTheme->get_template() );
317
  }
318
 
319
- /**
320
- * @param string $sSlug
321
- * @return bool - true if this is the Parent of the currently active theme
322
- */
323
- public function isActiveParent( $sSlug ) {
324
- return ( $this->isInstalled( $sSlug ) && $this->getCurrent()->get_template() == $sSlug );
325
  }
326
 
327
- /**
328
- * @param string $sSlug The directory slug.
329
- * @return bool
330
- */
331
- public function isInstalled( $sSlug ) {
332
- return ( !empty( $sSlug ) && $this->getExists( $sSlug ) );
333
  }
334
 
335
- /**
336
- * @param string $sSlug
337
- * @return bool
338
- */
339
- public function isUpdateAvailable( $sSlug ) {
340
- return !is_null( $this->getUpdateInfo( $sSlug ) );
341
  }
342
 
343
- /**
344
- * @param string $sBaseName
345
- * @return bool
346
- */
347
- public function isWpOrg( $sBaseName ) {
348
- return $this->getThemeAsVo( $sBaseName )->isWpOrg();
349
  }
350
 
351
  /**
19
  private $aLoadedVOs;
20
 
21
  /**
22
+ * @param string $stylesheet
23
  * @return bool
24
  */
25
+ public function activate( $stylesheet ) {
26
+ if ( empty( $stylesheet ) ) {
27
  return false;
28
  }
29
 
30
+ $oTheme = $this->getTheme( $stylesheet );
31
  if ( !$oTheme->exists() ) {
32
  return false;
33
  }
37
  // Now test currently active theme
38
  $oCurrentTheme = $this->getCurrent();
39
 
40
+ return ( $stylesheet == $oCurrentTheme->get_stylesheet() );
41
  }
42
 
43
  /**
44
+ * @param string $stylesheet
45
  * @return bool|\WP_Error
46
  */
47
+ public function delete( $stylesheet ) {
48
+ if ( empty( $stylesheet ) ) {
49
  return false;
50
  }
51
  if ( !function_exists( 'delete_theme' ) ) {
52
  require_once( ABSPATH.'wp-admin/includes/theme.php' );
53
  }
54
+ return function_exists( 'delete_theme' ) ? delete_theme( $stylesheet ) : false;
55
  }
56
 
57
  /**
58
+ * @param string $slug
59
  * @return bool
60
  */
61
+ public function installFromWpOrg( $slug ) {
62
+ $success = false;
63
  try {
64
+ $theme = ( new Api() )
65
+ ->setWorkingSlug( $slug )
66
  ->getInfo();
67
+ if ( !empty( $theme->download_link ) ) {
68
+ $success = $this->install( $theme->download_link, true )[ 'successful' ];
69
  }
70
  }
71
+ catch ( \Exception $e ) {
72
  }
73
+ return $success;
74
  }
75
 
76
  /**
100
  }
101
 
102
  /**
103
+ * @param string $slug
104
  * @param bool $bUseBackup
105
  * @return bool
106
  */
107
+ public function reinstall( $slug, $bUseBackup = false ) {
108
  $bSuccess = false;
109
 
110
+ if ( $this->isInstalled( $slug ) ) {
111
+ $FS = Services::WpFs();
112
 
113
+ $oTheme = $this->getTheme( $slug );
114
 
115
  $sDir = $oTheme->get_stylesheet_directory();
116
+ $sBackupDir = dirname( $sDir ).'/../../'.$slug.'bak'.time();
117
  if ( $bUseBackup ) {
118
  rename( $sDir, $sBackupDir );
119
  }
120
 
121
+ $bSuccess = $this->installFromWpOrg( $slug );
122
  if ( $bSuccess ) {
123
  wp_update_themes(); //refreshes our update information
124
  if ( $bUseBackup ) {
125
+ $FS->deleteDir( $sBackupDir );
126
  }
127
  }
128
  elseif ( $bUseBackup ) {
129
+ $FS->deleteDir( $sDir );
130
  rename( $sBackupDir, $sDir );
131
  }
132
  }
134
  }
135
 
136
  /**
137
+ * @param string $file
138
  * @return array
139
  */
140
+ public function update( $file ) :array {
141
  require_once( ABSPATH.'wp-admin/includes/upgrade.php' );
142
  require_once( ABSPATH.'wp-admin/includes/class-wp-upgrader.php' );
143
 
144
  $oSkin = new \Automatic_Upgrader_Skin();
145
+ $mResult = ( new \Theme_Upgrader( $oSkin ) )->upgrade( $file );
146
 
147
  return [
148
  'successful' => $mResult === true,
165
  return $this->getTheme();
166
  }
167
 
168
+ public function getExists( string $stylesheet ) :bool {
169
+ return $this->getTheme( $stylesheet )->exists();
 
 
 
 
170
  }
171
 
172
  /**
173
+ * @param string $slug - the folder name of the theme
174
  * @return string
175
  */
176
+ public function getInstallationDir( $slug ) {
177
+ return wp_normalize_path( $this->getTheme( $slug )->get_stylesheet_directory() );
178
  }
179
 
180
  /**
188
  }
189
 
190
  /**
191
+ * @param string $stylesheet
192
  * @param bool $bReload
193
  * @return WpThemeVo|null
194
  */
195
+ public function getThemeAsVo( $stylesheet, $bReload = false ) {
196
  try {
197
  if ( !is_array( $this->aLoadedVOs ) ) {
198
  $this->aLoadedVOs = [];
199
  }
200
+ if ( $bReload || !isset( $this->aLoadedVOs[ $stylesheet ] ) ) {
201
+ $this->aLoadedVOs[ $stylesheet ] = new WpThemeVo( $stylesheet );
202
  }
203
+ $asset = $this->aLoadedVOs[ $stylesheet ];
204
  }
205
+ catch ( \Exception $e ) {
206
+ $asset = null;
207
  }
208
+ return $asset;
209
  }
210
 
211
  /**
214
  public function getThemesAsVo() {
215
  return array_filter(
216
  array_map(
217
+ function ( $stylesheet ) {
218
+ return $this->getThemeAsVo( $stylesheet );
219
  },
220
  $this->getInstalledStylesheets()
221
  )
225
  /**
226
  * @return string[]
227
  */
228
+ public function getInstalledStylesheets() :array {
229
  return array_map(
230
+ function ( $theme ) {
231
+ return $theme->get_stylesheet();
 
232
  },
233
  $this->getThemes()
234
  );
239
  * Abstracts the WordPress wp_get_themes()
240
  * @return \WP_Theme[]
241
  */
242
+ public function getThemes() :array {
243
  require_once( ABSPATH.'wp-admin/includes/theme.php' );
244
  return function_exists( 'wp_get_themes' ) ? wp_get_themes() : [];
245
  }
246
 
247
  /**
248
+ * @param string $slug
249
  * @return array|null
250
  */
251
+ public function getUpdateInfo( $slug ) {
252
+ $updates = $this->getUpdates();
253
+ return $updates[ $slug ] ?? null;
254
  }
255
 
256
  /**
277
  * @return array[] - keys are theme stylesheets
278
  */
279
  public function getAllExtendedData() {
280
+ $data = Services::WpGeneral()->getTransient( 'update_themes' );
281
  return array_merge(
282
+ $data->no_update ?? [],
283
+ $data->response ?? []
284
  );
285
  }
286
 
287
  /**
288
+ * @param string $slug
289
  * @return array
290
  */
291
+ public function getExtendedData( $slug ) {
292
+ $data = $this->getAllExtendedData();
293
+ return $data[ $slug ] ?? [];
294
  }
295
 
296
  /**
298
  * @param bool $bCheckIsActiveParent
299
  * @return bool
300
  */
301
+ public function isActive( $sSlug, $bCheckIsActiveParent = false ) :bool {
302
  return ( $this->isInstalled( $sSlug ) && $this->getCurrent()->get_stylesheet() == $sSlug )
303
  || ( $bCheckIsActiveParent && $this->isActiveParent( $sSlug ) );
304
  }
305
 
306
+ public function isActiveThemeAChild() :bool {
307
+ $current = $this->getCurrent();
308
+ return $current->get_stylesheet() !== $current->get_template();
 
 
 
309
  }
310
 
311
+ public function isActiveParent( string $slug ) :bool {
312
+ return $this->isInstalled( $slug ) && $this->getCurrent()->get_template() == $slug;
 
 
 
 
313
  }
314
 
315
+ public function isInstalled( string $slug ) :bool {
316
+ return !empty( $slug ) && $this->getExists( $slug );
 
 
 
 
317
  }
318
 
319
+ public function isUpdateAvailable( string $slug ) :bool {
320
+ return !is_null( $this->getUpdateInfo( $slug ) );
 
 
 
 
321
  }
322
 
323
+ public function isWpOrg( string $stylesheet ) :bool {
324
+ return $this->getThemeAsVo( $stylesheet )->isWpOrg();
 
 
 
 
325
  }
326
 
327
  /**
src/lib/vendor/fernleafsystems/wordpress-services/src/Core/Users.php CHANGED
@@ -170,8 +170,8 @@ class Users {
170
  * @return null|\WP_User
171
  */
172
  public function getUserBy( $byKey, $value ) {
173
- $oU = function_exists( 'get_user_by' ) ? get_user_by( $byKey, $value ) : null;
174
- return empty( $oU ) ? null : $oU;
175
  }
176
 
177
  /**
170
  * @return null|\WP_User
171
  */
172
  public function getUserBy( $byKey, $value ) {
173
+ $user = function_exists( 'get_user_by' ) ? get_user_by( $byKey, $value ) : null;
174
+ return empty( $user ) ? null : $user;
175
  }
176
 
177
  /**
src/lib/vendor/fernleafsystems/wordpress-services/src/Core/VOs/WpBaseVo.php CHANGED
@@ -62,7 +62,7 @@ abstract class WpBaseVo {
62
  /**
63
  * @return string
64
  */
65
- abstract public function getInstallDir();
66
 
67
  /**
68
  * @return bool
@@ -72,10 +72,7 @@ abstract class WpBaseVo {
72
  return !empty( $this->new_version ) && version_compare( $this->new_version, $this->Version, '>' );
73
  }
74
 
75
- /**
76
- * @return bool
77
- */
78
- abstract public function isWpOrg();
79
 
80
  /**
81
  * @return array
@@ -85,7 +82,7 @@ abstract class WpBaseVo {
85
  /**
86
  * @return string[]
87
  */
88
- protected function getExtendedDataSlugs() {
89
  return [
90
  'new_version',
91
  ];
62
  /**
63
  * @return string
64
  */
65
+ abstract public function getInstallDir():string;
66
 
67
  /**
68
  * @return bool
72
  return !empty( $this->new_version ) && version_compare( $this->new_version, $this->Version, '>' );
73
  }
74
 
75
+ abstract public function isWpOrg() :bool;
 
 
 
76
 
77
  /**
78
  * @return array
82
  /**
83
  * @return string[]
84
  */
85
+ protected function getExtendedDataSlugs() :array {
86
  return [
87
  'new_version',
88
  ];
src/lib/vendor/fernleafsystems/wordpress-services/src/Core/VOs/WpPluginVo.php CHANGED
@@ -28,18 +28,18 @@ class WpPluginVo extends WpBaseVo {
28
 
29
  /**
30
  * WpPluginVo constructor.
31
- * @param string $sBaseFile
32
  * @throws \Exception
33
  */
34
- public function __construct( $sBaseFile ) {
35
- $oWpPlugins = Services::WpPlugins();
36
- $aPlug = $oWpPlugins->getPlugin( $sBaseFile );
37
- if ( empty( $aPlug ) ) {
38
- throw new \Exception( sprintf( 'Plugin file %s does not exist', $sBaseFile ) );
39
  }
40
- $this->applyFromArray( $aPlug );
41
- $this->file = $sBaseFile;
42
- $this->active = $oWpPlugins->isActive( $sBaseFile );
43
  }
44
 
45
  /**
@@ -79,14 +79,11 @@ class WpPluginVo extends WpBaseVo {
79
  /**
80
  * @return string
81
  */
82
- public function getInstallDir() {
83
  return wp_normalize_path( trailingslashit( dirname( path_join( WP_PLUGIN_DIR, $this->file ) ) ) );
84
  }
85
 
86
- /**
87
- * @return bool
88
- */
89
- public function isWpOrg() {
90
  $this->id; // loads the data
91
  return isset( $this->id ) ? strpos( $this->id, 'w.org/' ) === 0 : false;
92
  }
@@ -101,7 +98,7 @@ class WpPluginVo extends WpBaseVo {
101
  /**
102
  * @return string[]
103
  */
104
- protected function getExtendedDataSlugs() {
105
  return array_merge( parent::getExtendedDataSlugs(), [
106
  'id',
107
  'slug',
28
 
29
  /**
30
  * WpPluginVo constructor.
31
+ * @param string $baseFile
32
  * @throws \Exception
33
  */
34
+ public function __construct( string $baseFile ) {
35
+ $WPP = Services::WpPlugins();
36
+ $p = $WPP->getPlugin( $baseFile );
37
+ if ( empty( $p ) ) {
38
+ throw new \Exception( sprintf( 'Plugin file %s does not exist', $baseFile ) );
39
  }
40
+ $this->applyFromArray( $p );
41
+ $this->file = $baseFile;
42
+ $this->active = $WPP->isActive( $baseFile );
43
  }
44
 
45
  /**
79
  /**
80
  * @return string
81
  */
82
+ public function getInstallDir() :string {
83
  return wp_normalize_path( trailingslashit( dirname( path_join( WP_PLUGIN_DIR, $this->file ) ) ) );
84
  }
85
 
86
+ public function isWpOrg() :bool {
 
 
 
87
  $this->id; // loads the data
88
  return isset( $this->id ) ? strpos( $this->id, 'w.org/' ) === 0 : false;
89
  }
98
  /**
99
  * @return string[]
100
  */
101
+ protected function getExtendedDataSlugs() :array {
102
  return array_merge( parent::getExtendedDataSlugs(), [
103
  'id',
104
  'slug',
src/lib/vendor/fernleafsystems/wordpress-services/src/Core/VOs/WpThemeVo.php CHANGED
@@ -24,20 +24,20 @@ class WpThemeVo extends WpBaseVo {
24
 
25
  /**
26
  * WpPluginVo constructor.
27
- * @param string $sStylesheet - the name of the theme folder.
28
  * @throws \Exception
29
  */
30
- public function __construct( $sStylesheet ) {
31
- $oWpTheme = Services::WpThemes();
32
- $oT = $oWpTheme->getTheme( $sStylesheet );
33
- if ( empty( $oT ) ) {
34
- throw new \Exception( sprintf( 'Theme file %s does not exist', $sStylesheet ) );
35
  }
36
- $this->wp_theme = $oT;
37
- $this->stylesheet = $sStylesheet;
38
- $this->active = $oWpTheme->isActive( $sStylesheet );
39
- $this->is_child = $this->active && $oWpTheme->isActiveThemeAChild();
40
- $this->is_parent = !$this->active && $oWpTheme->isActiveParent( $sStylesheet );
41
  }
42
 
43
  /**
@@ -75,7 +75,7 @@ class WpThemeVo extends WpBaseVo {
75
  /**
76
  * @return string[]
77
  */
78
- private function getWpThemeKeys() {
79
  return [
80
  'Name',
81
  'ThemeURI',
@@ -91,17 +91,11 @@ class WpThemeVo extends WpBaseVo {
91
  ];
92
  }
93
 
94
- /**
95
- * @return string
96
- */
97
- public function getInstallDir() {
98
  return wp_normalize_path( trailingslashit( $this->wp_theme->get_stylesheet_directory() ) );
99
  }
100
 
101
- /**
102
- * @return bool
103
- */
104
- public function isWpOrg() {
105
  $this->wp_info;
106
  return !empty( $this->wp_info );
107
  }
@@ -116,7 +110,7 @@ class WpThemeVo extends WpBaseVo {
116
  /**
117
  * @return string[]
118
  */
119
- protected function getExtendedDataSlugs() {
120
  return array_merge( parent::getExtendedDataSlugs(), [
121
  'theme',
122
  'package',
@@ -135,7 +129,7 @@ class WpThemeVo extends WpBaseVo {
135
  ->setWorkingSlug( $this->stylesheet )
136
  ->getInfo();
137
  }
138
- catch ( \Exception $oE ) {
139
  $oInfo = false;
140
  }
141
  return $oInfo;
24
 
25
  /**
26
  * WpPluginVo constructor.
27
+ * @param string $stylesheet - the name of the theme folder.
28
  * @throws \Exception
29
  */
30
+ public function __construct( string $stylesheet ) {
31
+ $WPT = Services::WpThemes();
32
+ $t = $WPT->getTheme( $stylesheet );
33
+ if ( empty( $t ) ) {
34
+ throw new \Exception( sprintf( 'Theme file %s does not exist', $stylesheet ) );
35
  }
36
+ $this->wp_theme = $t;
37
+ $this->stylesheet = $stylesheet;
38
+ $this->active = $WPT->isActive( $stylesheet );
39
+ $this->is_child = $this->active && $WPT->isActiveThemeAChild();
40
+ $this->is_parent = !$this->active && $WPT->isActiveParent( $stylesheet );
41
  }
42
 
43
  /**
75
  /**
76
  * @return string[]
77
  */
78
+ private function getWpThemeKeys() :array {
79
  return [
80
  'Name',
81
  'ThemeURI',
91
  ];
92
  }
93
 
94
+ public function getInstallDir() :string {
 
 
 
95
  return wp_normalize_path( trailingslashit( $this->wp_theme->get_stylesheet_directory() ) );
96
  }
97
 
98
+ public function isWpOrg() :bool {
 
 
 
99
  $this->wp_info;
100
  return !empty( $this->wp_info );
101
  }
110
  /**
111
  * @return string[]
112
  */
113
+ protected function getExtendedDataSlugs() :array {
114
  return array_merge( parent::getExtendedDataSlugs(), [
115
  'theme',
116
  'package',
129
  ->setWorkingSlug( $this->stylesheet )
130
  ->getInfo();
131
  }
132
+ catch ( \Exception $e ) {
133
  $oInfo = false;
134
  }
135
  return $oInfo;
src/lib/vendor/fernleafsystems/wordpress-services/src/Utilities/Data.php CHANGED
@@ -48,21 +48,17 @@ class Data {
48
  * PHP Code.
49
  * Why use this? In the name of naive security, silly web hosts can prevent reading the contents of
50
  * non-PHP files so we simply put the content we want to have read into a php file and then "include" it.
51
- * @param string $sFile
52
  * @return string
53
  */
54
- public function readFileContentsUsingInclude( $sFile ) {
55
  ob_start();
56
- include( $sFile );
57
  return ob_get_clean();
58
  }
59
 
60
- /**
61
- * @param string $sUrl
62
- * @return string
63
- */
64
- public function urlStripQueryPart( $sUrl ) {
65
- return preg_replace( '#\s?\?.*$#', '', $sUrl );
66
  }
67
 
68
  /**
@@ -74,35 +70,35 @@ class Data {
74
  }
75
 
76
  /**
77
- * @param string $sUrl
78
  * @return bool
79
  */
80
- public function isValidWebUrl( $sUrl ) {
81
- $sUrl = trim( $this->urlStripQueryPart( $sUrl ) );
82
- return filter_var( $sUrl, FILTER_VALIDATE_URL )
83
- && in_array( parse_url( $sUrl, PHP_URL_SCHEME ), [ 'http', 'https' ] );
84
  }
85
 
86
  /**
87
- * @param string $sUrl
88
  * @return bool
89
  */
90
- public function verifyUrl( $sUrl ) {
91
  try {
92
- $bValid = $this->isValidWebUrl( $sUrl ) && ( new HttpUtil() )->checkUrl( $sUrl );
93
  }
94
- catch ( \Exception $oE ) {
95
- $bValid = false;
96
  }
97
- return $bValid;
98
  }
99
 
100
  /**
101
- * @param string $sEmail
102
  * @return bool
103
  */
104
- public function validEmail( $sEmail ) {
105
- return ( !empty( $sEmail ) && is_email( $sEmail ) );
106
  }
107
 
108
  /**
48
  * PHP Code.
49
  * Why use this? In the name of naive security, silly web hosts can prevent reading the contents of
50
  * non-PHP files so we simply put the content we want to have read into a php file and then "include" it.
51
+ * @param string $file
52
  * @return string
53
  */
54
+ public function readFileContentsUsingInclude( string $file ) {
55
  ob_start();
56
+ include( $file );
57
  return ob_get_clean();
58
  }
59
 
60
+ public function urlStripQueryPart( $url ) :string {
61
+ return preg_replace( '#\s?\?.*$#', '', $url );
 
 
 
 
62
  }
63
 
64
  /**
70
  }
71
 
72
  /**
73
+ * @param string $url
74
  * @return bool
75
  */
76
+ public function isValidWebUrl( $url ) {
77
+ $url = trim( $this->urlStripQueryPart( $url ) );
78
+ return filter_var( $url, FILTER_VALIDATE_URL )
79
+ && in_array( parse_url( $url, PHP_URL_SCHEME ), [ 'http', 'https' ] );
80
  }
81
 
82
  /**
83
+ * @param string $url
84
  * @return bool
85
  */
86
+ public function verifyUrl( $url ) :bool {
87
  try {
88
+ $valid = $this->isValidWebUrl( $url ) && ( new HttpUtil() )->checkUrl( $url );
89
  }
90
+ catch ( \Exception $e ) {
91
+ $valid = false;
92
  }
93
+ return $valid;
94
  }
95
 
96
  /**
97
+ * @param string $email
98
  * @return bool
99
  */
100
+ public function validEmail( $email ) :bool {
101
+ return !empty( $email ) && is_email( $email );
102
  }
103
 
104
  /**
src/lib/vendor/fernleafsystems/wordpress-services/src/Utilities/File/GetFileAsArray.php CHANGED
@@ -14,22 +14,22 @@ use FernleafSystems\Wordpress\Services\Services;
14
  class GetFileAsArray {
15
 
16
  /**
17
- * @param string $sPath
18
- * @param string $sExplodeOn
19
  * @return string[]
20
  * @throws \Exception
21
  */
22
- public function run( $sPath, $sExplodeOn = "\n" ) {
23
- $oFs = Services::WpFs();
24
- if ( !$oFs->isFile( $sPath ) ) {
25
  throw new \InvalidArgumentException( 'File does not exist' );
26
  }
27
 
28
- $sContents = $oFs->getFileContent( $sPath );
29
- if ( empty( $sContents ) ) {
30
  throw new \Exception( 'File is empty' );
31
  }
32
 
33
- return explode( $sExplodeOn, $sContents );
34
  }
35
  }
14
  class GetFileAsArray {
15
 
16
  /**
17
+ * @param string $path
18
+ * @param string $splitOn
19
  * @return string[]
20
  * @throws \Exception
21
  */
22
+ public function run( $path, $splitOn = '\r\n|\r|\n' ) :array {
23
+ $FS = Services::WpFs();
24
+ if ( !$FS->isFile( $path ) ) {
25
  throw new \InvalidArgumentException( 'File does not exist' );
26
  }
27
 
28
+ $content = $FS->getFileContent( $path );
29
+ if ( empty( $content ) ) {
30
  throw new \Exception( 'File is empty' );
31
  }
32
 
33
+ return preg_split( '/\r\n|\r|\n/', $content );
34
  }
35
  }
src/lib/vendor/fernleafsystems/wordpress-services/src/Utilities/File/LocateStrInFile.php CHANGED
@@ -13,44 +13,55 @@ class LocateStrInFile {
13
  /**
14
  * @var string
15
  */
16
- private $sNeedle;
17
 
18
  /**
19
  * @var string
20
  */
21
- private $sContent;
22
 
23
  /**
24
  * @var string
25
  */
26
- private $sPath;
27
 
28
  /**
29
  * @var string[]
30
  */
31
- private $aLines;
32
 
33
  /**
34
  * @var bool
35
  */
36
- private $bIsRegExNeedle;
37
 
38
  /**
39
  * @return string[]
40
  */
41
- public function run() {
42
  return $this->isRegEx() ? $this->runAsRegEx() : $this->runAsSimple();
43
  }
44
 
45
  /**
46
  * @return string[] - keys are line numbers
47
  */
48
- protected function runAsRegEx() {
49
- $sNeedle = $this->getNeedle();
 
 
 
 
 
 
 
 
 
 
 
50
  return array_filter(
51
  $this->getLines(),
52
- function ( $sLine ) use ( $sNeedle ) {
53
- return preg_match( '/'.$sNeedle.'/im', $sLine );
54
  }
55
  );
56
  }
@@ -58,18 +69,12 @@ class LocateStrInFile {
58
  /**
59
  * @return string[] - keys are line numbers
60
  */
61
- protected function runAsSimple() {
62
- $aLines = [];
63
- $sNeedle = $this->getNeedle();
64
  if ( stripos( $this->getContent(), $this->getNeedle() ) !== false ) {
65
- $aLines = array_filter(
66
- $this->getLines(),
67
- function ( $sLine ) use ( $sNeedle ) {
68
- return ( strpos( $sLine, $sNeedle ) !== false );
69
- }
70
- );
71
  }
72
- return $aLines;
73
  }
74
 
75
  /**
@@ -78,7 +83,7 @@ class LocateStrInFile {
78
  * @throws \InvalidArgumentException
79
  * @deprecated
80
  */
81
- public function inFile( $sPath ) {
82
  return $this->setPath( $sPath )
83
  ->run();
84
  }
@@ -86,81 +91,65 @@ class LocateStrInFile {
86
  /**
87
  * @return string[]
88
  */
89
- protected function getLines() {
90
- if ( is_null( $this->aLines ) ) {
91
- $this->aLines = explode( "\n", $this->getContent() );
 
 
92
  }
93
- return $this->aLines;
94
  }
95
 
96
- /**
97
- * @return string
98
- */
99
- public function getContent() {
100
- if ( is_null( $this->sContent ) ) {
101
- $this->sContent = Services::WpFs()->getFileContent( $this->getPath() );
102
  }
103
- return $this->sContent;
104
  }
105
 
106
- /**
107
- * @return string
108
- */
109
- public function getNeedle() {
110
- return $this->sNeedle;
111
  }
112
 
113
- /**
114
- * @return string
115
- */
116
- public function getPath() {
117
- return $this->sPath;
118
  }
119
 
120
- /**
121
- * @return bool
122
- */
123
- public function isRegEx() {
124
- return (bool)$this->bIsRegExNeedle;
125
  }
126
 
127
- /**
128
- * @param bool $bIsRegEx
129
- * @return $this
130
- */
131
- public function setIsRegEx( $bIsRegEx ) {
132
- $this->bIsRegExNeedle = $bIsRegEx;
133
  return $this;
134
  }
135
 
136
- /**
137
- * @param string $sStr
138
- * @return $this
139
- */
140
- public function setNeedle( $sStr ) {
141
- $this->sNeedle = $sStr;
142
  return $this;
143
  }
144
 
145
  /**
146
- * @param string $sPath
147
  * @return $this
148
  * @throws \InvalidArgumentException
 
149
  */
150
- public function setPath( $sPath ) {
151
- if ( !Services::WpFs()->isFile( $sPath ) ) {
152
- throw new \InvalidArgumentException( 'File does not exist' );
 
 
 
153
  }
154
- $this->sPath = $sPath;
 
155
  return $this->reset();
156
  }
157
 
158
- /**
159
- * @return $this
160
- */
161
- protected function reset() {
162
- $this->sContent = null;
163
- $this->aLines = null;
164
  return $this;
165
  }
166
  }
13
  /**
14
  * @var string
15
  */
16
+ private $needle;
17
 
18
  /**
19
  * @var string
20
  */
21
+ private $content;
22
 
23
  /**
24
  * @var string
25
  */
26
+ private $path;
27
 
28
  /**
29
  * @var string[]
30
  */
31
+ private $lines;
32
 
33
  /**
34
  * @var bool
35
  */
36
+ private $isRegExNeedle;
37
 
38
  /**
39
  * @return string[]
40
  */
41
+ public function run() :array {
42
  return $this->isRegEx() ? $this->runAsRegEx() : $this->runAsSimple();
43
  }
44
 
45
  /**
46
  * @return string[] - keys are line numbers
47
  */
48
+ protected function runAsRegEx() :array {
49
+ $lines = [];
50
+
51
+ if ( preg_match_all( '/('.$this->getNeedle().')/i', $this->getContent(), $matches, PREG_PATTERN_ORDER ) ) {
52
+ foreach ( $matches[ 0 ] as $match ) {
53
+ // use + for numerical index
54
+ $lines = $lines + $this->findLinesFor( $match );
55
+ }
56
+ }
57
+ return $lines;
58
+ }
59
+
60
+ protected function findLinesFor( string $for ) :array {
61
  return array_filter(
62
  $this->getLines(),
63
+ function ( $line ) use ( $for ) {
64
+ return stripos( $line, $for ) !== false;
65
  }
66
  );
67
  }
69
  /**
70
  * @return string[] - keys are line numbers
71
  */
72
+ protected function runAsSimple() :array {
73
+ $lines = [];
 
74
  if ( stripos( $this->getContent(), $this->getNeedle() ) !== false ) {
75
+ $lines = $lines + $this->findLinesFor( $this->getNeedle() );
 
 
 
 
 
76
  }
77
+ return $lines;
78
  }
79
 
80
  /**
83
  * @throws \InvalidArgumentException
84
  * @deprecated
85
  */
86
+ public function inFile( $sPath ) :array {
87
  return $this->setPath( $sPath )
88
  ->run();
89
  }
91
  /**
92
  * @return string[]
93
  */
94
+ protected function getLines() :array {
95
+ if ( is_null( $this->lines ) ) {
96
+ $this->lines = array_filter(
97
+ array_map( 'trim', preg_split( '/\r\n|\r|\n/', $this->getContent() ) )
98
+ );
99
  }
100
+ return $this->lines;
101
  }
102
 
103
+ public function getContent() :string {
104
+ if ( is_null( $this->content ) ) {
105
+ $this->content = Services::WpFs()->getFileContent( $this->getPath() );
 
 
 
106
  }
107
+ return $this->content;
108
  }
109
 
110
+ public function getNeedle() :string {
111
+ return $this->needle;
 
 
 
112
  }
113
 
114
+ public function getPath() :string {
115
+ return $this->path;
 
 
 
116
  }
117
 
118
+ public function isRegEx() :bool {
119
+ return (bool)$this->isRegExNeedle;
 
 
 
120
  }
121
 
122
+ public function setIsRegEx( bool $isRegEx ) :self {
123
+ $this->isRegExNeedle = $isRegEx;
 
 
 
 
124
  return $this;
125
  }
126
 
127
+ public function setNeedle( string $needle ) :self {
128
+ $this->needle = $needle;
 
 
 
 
129
  return $this;
130
  }
131
 
132
  /**
133
+ * @param string $path
134
  * @return $this
135
  * @throws \InvalidArgumentException
136
+ * @throws \Exception
137
  */
138
+ public function setPath( string $path ) :self {
139
+ if ( !Services::WpFs()->isFile( $path ) ) {
140
+ throw new \InvalidArgumentException( "File doesn't exist" );
141
+ }
142
+ if ( !is_readable( $path ) ) {
143
+ throw new \Exception( "File isn't readable" );
144
  }
145
+ $this->path = $path;
146
+ $this->getContent();
147
  return $this->reset();
148
  }
149
 
150
+ protected function reset() :self {
151
+ $this->content = null;
152
+ $this->lines = null;
 
 
 
153
  return $this;
154
  }
155
  }
src/lib/vendor/fernleafsystems/wordpress-services/src/Utilities/Net/IpIdentify.php CHANGED
@@ -56,10 +56,7 @@ class IpIdentify {
56
  throw new \Exception( "A valid IP address was not provided." );
57
  }
58
 
59
- if ( $srvIP->checkIp( $this->ip, $srvIP->getServerPublicIPs() ) ) {
60
- $is = self::THIS_SERVER;
61
- }
62
- elseif ( $srvProviders->isIp_AppleBot( $this->ip, $this->agent ) ) {
63
  $is = self::APPLE;
64
  }
65
  elseif ( $srvProviders->isIp_BaiduBot( $this->ip, $this->agent ) ) {
@@ -116,6 +113,9 @@ class IpIdentify {
116
  elseif ( $srvProviders->isIp_YandexBot( $this->ip, $this->agent ) ) {
117
  $is = self::YANDEX;
118
  }
 
 
 
119
  elseif ( $srvIP->checkIp( $this->ip, $srvIP->getRequestIp() ) ) {
120
  $is = self::VISITOR;
121
  }
56
  throw new \Exception( "A valid IP address was not provided." );
57
  }
58
 
59
+ if ( $srvProviders->isIp_AppleBot( $this->ip, $this->agent ) ) {
 
 
 
60
  $is = self::APPLE;
61
  }
62
  elseif ( $srvProviders->isIp_BaiduBot( $this->ip, $this->agent ) ) {
113
  elseif ( $srvProviders->isIp_YandexBot( $this->ip, $this->agent ) ) {
114
  $is = self::YANDEX;
115
  }
116
+ elseif ( $srvIP->checkIp( $this->ip, $srvIP->getServerPublicIPs() ) ) {
117
+ $is = self::THIS_SERVER;
118
+ }
119
  elseif ( $srvIP->checkIp( $this->ip, $srvIP->getRequestIp() ) ) {
120
  $is = self::VISITOR;
121
  }
src/lib/vendor/fernleafsystems/wordpress-services/src/Utilities/Obfuscate.php CHANGED
@@ -1,17 +1,17 @@
1
- <?php
2
 
3
  namespace FernleafSystems\Wordpress\Services\Utilities;
4
 
5
  class Obfuscate {
6
 
7
  /**
8
- * @param string $sEmail
9
  * @return string
10
  */
11
- public static function Email( $sEmail ) {
12
- list( $sP1, $sP2 ) = explode( '@', $sEmail, 2 );
13
- return substr( $sP1, 0, 1 ).'****'.substr( $sP1, -1, 1 )
14
  .'@'.
15
- substr( $sP2, 0, 1 ).'****'.substr( $sP2, strrpos( $sP2, '.' ) );
16
  }
17
  }
1
+ <?php declare( strict_types=1 );
2
 
3
  namespace FernleafSystems\Wordpress\Services\Utilities;
4
 
5
  class Obfuscate {
6
 
7
  /**
8
+ * @param string $email
9
  * @return string
10
  */
11
+ public static function Email( string $email ) :string {
12
+ list( $left, $right ) = explode( '@', $email, 2 );
13
+ return substr( $left, 0, 1 ).'****'.substr( $left, -1, 1 )
14
  .'@'.
15
+ substr( $right, 0, 1 ).'****'.substr( $right, strrpos( $right, '.' ) );
16
  }
17
  }
src/lib/vendor/fernleafsystems/wordpress-services/src/Utilities/Options/Transient.php CHANGED
@@ -13,33 +13,33 @@ use FernleafSystems\Wordpress\Services\Services;
13
  class Transient {
14
 
15
  /**
16
- * @param string $sKey
17
- * @param bool $bIgnoreWPMS
18
  * @return bool
19
  */
20
- public static function Delete( $sKey, $bIgnoreWPMS = true ) {
21
  $oWP = Services::WpGeneral();
22
  return $oWP->canUseTransients() ?
23
- $oWP->deleteTransient( $sKey )
24
- : Services::WpGeneral()->deleteOption( System::PREFIX.'trans_'.$sKey, $bIgnoreWPMS );
25
  }
26
 
27
  /**
28
- * @param string $sKey
29
- * @param null $mDefault
30
- * @param bool $bIgnoreWPMS
31
  * @return mixed|null
32
  */
33
- public static function Get( $sKey, $mDefault = null, $bIgnoreWPMS = true ) {
34
  $mVal = null;
35
 
36
  $oWP = Services::WpGeneral();
37
 
38
  if ( $oWP->canUseTransients() ) {
39
- $mVal = $oWP->getTransient( $sKey );
40
  }
41
  else {
42
- $aData = $oWP->getOption( System::PREFIX.'trans_'.$sKey, null, $bIgnoreWPMS );
43
  if ( !empty( $aData ) && is_array( $aData ) && isset( $aData[ 'data' ] )
44
  && isset( $aData[ 'expires_at' ] ) ) {
45
  if ( $aData[ 'expires_at' ] === 0 || Services::Request()->ts() < $aData[ 'expires_at' ] ) {
@@ -48,34 +48,34 @@ class Transient {
48
  }
49
  }
50
 
51
- return is_null( $mVal ) ? $mDefault : $mVal;
52
  }
53
 
54
  /**
55
- * @param string $sKey
56
- * @param mixed $mData
57
- * @param int $nLifeTime
58
- * @param bool $bIgnoreWPMS
59
  * @return bool
60
  */
61
- public static function Set( $sKey, $mData, $nLifeTime = 0, $bIgnoreWPMS = true ) {
62
- if ( is_null( $mData ) ) {
63
- self::Delete( $sKey );
64
  }
65
 
66
  $oWP = Services::WpGeneral();
67
 
68
  if ( $oWP->canUseTransients() ) {
69
- return $oWP->setTransient( $sKey, $mData, $nLifeTime );
70
  }
71
  else {
72
  return $oWP->updateOption(
73
- System::PREFIX.'trans_'.$sKey,
74
  [
75
- 'data' => $mData,
76
- 'expires_at' => empty( $nLifeTime ) ? 0 : Services::Request()->ts() + max( 0, $nLifeTime ),
77
  ],
78
- $bIgnoreWPMS
79
  );
80
  }
81
  }
13
  class Transient {
14
 
15
  /**
16
+ * @param string $key
17
+ * @param bool $ignoreWPMS
18
  * @return bool
19
  */
20
+ public static function Delete( $key, $ignoreWPMS = true ) {
21
  $oWP = Services::WpGeneral();
22
  return $oWP->canUseTransients() ?
23
+ $oWP->deleteTransient( $key )
24
+ : Services::WpGeneral()->deleteOption( System::PREFIX.'trans_'.$key, $ignoreWPMS );
25
  }
26
 
27
  /**
28
+ * @param string $key
29
+ * @param null $default
30
+ * @param bool $ignoreWPMS
31
  * @return mixed|null
32
  */
33
+ public static function Get( $key, $default = null, $ignoreWPMS = true ) {
34
  $mVal = null;
35
 
36
  $oWP = Services::WpGeneral();
37
 
38
  if ( $oWP->canUseTransients() ) {
39
+ $mVal = $oWP->getTransient( $key );
40
  }
41
  else {
42
+ $aData = $oWP->getOption( System::PREFIX.'trans_'.$key, null, $ignoreWPMS );
43
  if ( !empty( $aData ) && is_array( $aData ) && isset( $aData[ 'data' ] )
44
  && isset( $aData[ 'expires_at' ] ) ) {
45
  if ( $aData[ 'expires_at' ] === 0 || Services::Request()->ts() < $aData[ 'expires_at' ] ) {
48
  }
49
  }
50
 
51
+ return is_null( $mVal ) ? $default : $mVal;
52
  }
53
 
54
  /**
55
+ * @param string $key
56
+ * @param mixed $data
57
+ * @param int $lifeTime
58
+ * @param bool $ignoreWPMS
59
  * @return bool
60
  */
61
+ public static function Set( $key, $data, $lifeTime = 0, $ignoreWPMS = true ) {
62
+ if ( is_null( $data ) ) {
63
+ self::Delete( $key );
64
  }
65
 
66
  $oWP = Services::WpGeneral();
67
 
68
  if ( $oWP->canUseTransients() ) {
69
+ return $oWP->setTransient( $key, $data, $lifeTime );
70
  }
71
  else {
72
  return $oWP->updateOption(
73
+ System::PREFIX.'trans_'.$key,
74
  [
75
+ 'data' => $data,
76
+ 'expires_at' => empty( $lifeTime ) ? 0 : Services::Request()->ts() + max( 0, $lifeTime ),
77
  ],
78
+ $ignoreWPMS
79
  );
80
  }
81
  }
src/lib/vendor/fernleafsystems/wordpress-services/src/Utilities/Render.php CHANGED
@@ -74,17 +74,17 @@ class Render {
74
  extract( $this->getRenderVars() );
75
  }
76
 
77
- $sTemplate = path_join( $this->getTemplateRoot(), $this->getTemplate() );
78
- if ( Services::WpFs()->isFile( $sTemplate ) ) {
79
  ob_start();
80
- include( $sTemplate );
81
- $sContents = ob_get_clean();
82
  }
83
  else {
84
- $sContents = 'Error: Template file not found: '.$sTemplate;
85
  }
86
 
87
- return $sContents;
88
  }
89
 
90
  /**
74
  extract( $this->getRenderVars() );
75
  }
76
 
77
+ $template = path_join( $this->getTemplateRoot(), $this->getTemplate() );
78
+ if ( Services::WpFs()->isFile( $template ) ) {
79
  ob_start();
80
+ include( $template );
81
+ $contents = ob_get_clean();
82
  }
83
  else {
84
+ $contents = 'Error: Template file not found: '.$template;
85
  }
86
 
87
+ return $contents;
88
  }
89
 
90
  /**
src/processors/admin_access_restriction.php DELETED
@@ -1,450 +0,0 @@
1
- <?php
2
-
3
- use FernleafSystems\Wordpress\Plugin\Shield\Modules;
4
- use FernleafSystems\Wordpress\Plugin\Shield\Modules\SecurityAdmin;
5
- use FernleafSystems\Wordpress\Services\Services;
6
-
7
- /**
8
- * @deprecated 10.1
9
- */
10
- class ICWP_WPSF_Processor_AdminAccessRestriction extends Modules\BaseShield\ShieldProcessor {
11
-
12
- /**
13
- * @var string
14
- */
15
- protected $sOptionRegexPattern;
16
-
17
- public function run() {
18
- add_filter( $this->getCon()->prefix( 'is_plugin_admin' ), [ $this, 'adjustUserAdminPermissions' ] );
19
-
20
- /** @var SecurityAdmin\Options $opts */
21
- $opts = $this->getOptions();
22
- if ( $opts->isEnabledWhitelabel() ) {
23
- /** @var SecurityAdmin\ModCon $mod */
24
- $mod = $this->getMod();
25
- $mod->getWhiteLabelController()->execute();
26
- }
27
- }
28
-
29
- /**
30
- * @param bool $bHasPermission
31
- * @return bool
32
- */
33
- public function adjustUserAdminPermissions( $bHasPermission = true ) {
34
- /** @var SecurityAdmin\ModCon $mod */
35
- $mod = $this->getMod();
36
- return $bHasPermission && ( $mod->isRegisteredSecAdminUser() || $mod->isSecAdminSessionValid()
37
- || $mod->testSecAccessKeyRequest() );
38
- }
39
-
40
- public function onWpInit() {
41
- if ( !$this->getCon()->isPluginAdmin() ) {
42
- /** @var SecurityAdmin\ModCon $mod */
43
- $mod = $this->getMod();
44
- /** @var SecurityAdmin\Options $opts */
45
- $opts = $this->getOptions();
46
-
47
- if ( !$mod->isUpgrading() && !Services::WpGeneral()->isLoginRequest() ) {
48
- add_filter( 'pre_update_option', [ $this, 'blockOptionsSaves' ], 1, 3 );
49
- }
50
-
51
- if ( $opts->isSecAdminRestrictUsersEnabled() ) {
52
- add_filter( 'editable_roles', [ $this, 'restrictEditableRoles' ], 100, 1 );
53
- add_filter( 'user_has_cap', [ $this, 'restrictAdminUserChanges' ], 100, 3 );
54
- add_action( 'delete_user', [ $this, 'restrictAdminUserDelete' ], 100, 1 );
55
- add_action( 'add_user_role', [ $this, 'restrictAddUserRole' ], 100, 2 );
56
- add_action( 'remove_user_role', [ $this, 'restrictRemoveUserRole' ], 100, 2 );
57
- add_action( 'set_user_role', [ $this, 'restrictSetUserRole' ], 100, 3 );
58
- }
59
-
60
- $aPluginRestrictions = $opts->getAdminAccessArea_Plugins();
61
- if ( !empty( $aPluginRestrictions ) ) {
62
- add_filter( 'user_has_cap', [ $this, 'disablePluginManipulation' ], 0, 3 );
63
- }
64
-
65
- $aThemeRestrictions = $opts->getAdminAccessArea_Themes();
66
- if ( !empty( $aThemeRestrictions ) ) {
67
- add_filter( 'user_has_cap', [ $this, 'disableThemeManipulation' ], 0, 3 );
68
- }
69
-
70
- $aPostRestrictions = $opts->getAdminAccessArea_Posts();
71
- if ( !empty( $aPostRestrictions ) ) {
72
- add_filter( 'user_has_cap', [ $this, 'disablePostsManipulation' ], 0, 3 );
73
- }
74
-
75
- if ( !$this->getCon()->isThisPluginModuleRequest() ) {
76
- add_action( 'admin_footer', [ $this, 'printAdminAccessAjaxForm' ] );
77
- }
78
- }
79
- }
80
-
81
- /**
82
- * Override the original collection to then add plugin statistics to the mix
83
- * @param $aData
84
- * @return array
85
- */
86
- public function tracking_DataCollect( $aData ) {
87
- return $aData;
88
- }
89
-
90
- /**
91
- * @param int $nUserId
92
- * @param string $sRole
93
- */
94
- public function restrictAddUserRole( $nUserId, $sRole ) {
95
- $oWpUsers = Services::WpUsers();
96
-
97
- if ( $oWpUsers->getCurrentWpUserId() !== $nUserId && strtolower( $sRole ) === 'administrator' ) {
98
- $oModUser = $oWpUsers->getUserById( $nUserId );
99
- remove_action( 'remove_user_role', [ $this, 'restrictRemoveUserRole' ], 100 );
100
- $oModUser->remove_role( 'administrator' );
101
- add_action( 'remove_user_role', [ $this, 'restrictRemoveUserRole' ], 100, 2 );
102
- }
103
- }
104
-
105
- /**
106
- * @param int $nUserId
107
- * @param string $sRole
108
- * @param array $aOldRoles
109
- */
110
- public function restrictSetUserRole( $nUserId, $sRole, $aOldRoles = [] ) {
111
- $oWpUsers = Services::WpUsers();
112
-
113
- $sRole = strtolower( $sRole );
114
- if ( !is_array( $aOldRoles ) ) {
115
- $aOldRoles = [];
116
- }
117
-
118
- if ( $oWpUsers->getCurrentWpUserId() !== $nUserId ) {
119
- $bNewRoleIsAdmin = $sRole == 'administrator';
120
-
121
- // 1. Setting administrator role where it doesn't previously exist
122
- if ( $bNewRoleIsAdmin && !in_array( 'administrator', $aOldRoles ) ) {
123
- $bRevert = true;
124
- }
125
- // 2. Setting non-administrator role when previous roles included administrator
126
- elseif ( !$bNewRoleIsAdmin && in_array( 'administrator', $aOldRoles ) ) {
127
- $bRevert = true;
128
- }
129
- else {
130
- $bRevert = false;
131
- }
132
-
133
- if ( $bRevert ) {
134
- $oModUser = $oWpUsers->getUserById( $nUserId );
135
- remove_action( 'add_user_role', [ $this, 'restrictAddUserRole' ], 100 );
136
- remove_action( 'remove_user_role', [ $this, 'restrictRemoveUserRole' ], 100 );
137
- $oModUser->remove_role( $sRole );
138
- foreach ( $aOldRoles as $sPreExistingRoles ) {
139
- $oModUser->add_role( $sPreExistingRoles );
140
- }
141
- add_action( 'add_user_role', [ $this, 'restrictAddUserRole' ], 100, 2 );
142
- add_action( 'remove_user_role', [ $this, 'restrictRemoveUserRole' ], 100, 2 );
143
- }
144
- }
145
- }
146
-
147
- /**
148
- * @param int $nUserId
149
- * @param string $sRole
150
- */
151
- public function restrictRemoveUserRole( $nUserId, $sRole ) {
152
- $oWpUsers = Services::WpUsers();
153
-
154
- if ( $oWpUsers->getCurrentWpUserId() !== $nUserId && strtolower( $sRole ) === 'administrator' ) {
155
- $oModUser = $oWpUsers->getUserById( $nUserId );
156
- remove_action( 'add_user_role', [ $this, 'restrictAddUserRole' ], 100 );
157
- $oModUser->add_role( 'administrator' );
158
- add_action( 'add_user_role', [ $this, 'restrictAddUserRole' ], 100, 2 );
159
- }
160
- }
161
-
162
- /**
163
- * @param int $nId
164
- */
165
- public function restrictAdminUserDelete( $nId ) {
166
- $oWpUsers = Services::WpUsers();
167
- $oUserToDelete = $oWpUsers->getUserById( $nId );
168
- if ( $oUserToDelete && $oWpUsers->isUserAdmin( $oUserToDelete ) ) {
169
- Services::WpGeneral()
170
- ->wpDie( __( 'Sorry, deleting administrators is currently restricted to your Security Admin', 'wp-simple-firewall' ) );
171
- }
172
- }
173
-
174
- /**
175
- * @param array[] $aAllRoles
176
- * @return array[]
177
- */
178
- public function restrictEditableRoles( $aAllRoles ) {
179
- if ( isset( $aAllRoles[ 'administrator' ] ) ) {
180
- unset( $aAllRoles[ 'administrator' ] );
181
- }
182
- return $aAllRoles;
183
- }
184
-
185
- /**
186
- * This hooked function captures the attempts to modify the user role using the standard
187
- * WordPress profile edit pages. It doesn't sufficiently capture the AJAX request to
188
- * modify user roles. (see user role hooks)
189
- * @param array $aAllCaps
190
- * @param $cap
191
- * @param array $aArgs
192
- * @return array
193
- */
194
- public function restrictAdminUserChanges( $aAllCaps, $cap, $aArgs ) {
195
- /** @var string $sUserCap */
196
- $sUserCap = $aArgs[ 0 ];
197
-
198
- $aReleventCaps = [ 'edit_users', 'create_users' ];
199
-
200
- // If we're registered with Admin Access we don't modify anything
201
- if ( in_array( $sUserCap, $aReleventCaps ) ) {
202
- $bBlockCapability = false;
203
-
204
- $oReq = Services::Request();
205
- $oWpUsers = Services::WpUsers();
206
-
207
- // Find the WP_User for the POST
208
- $oPostUser = false;
209
- $sPostUserlogin = $oReq->post( 'user_login' );
210
- if ( empty( $sPostUserlogin ) ) {
211
- $nPostUserId = $oReq->post( 'user_id' );
212
- if ( !empty( $nPostUserId ) ) {
213
- $oPostUser = $oWpUsers->getUserById( $nPostUserId );
214
- }
215
- }
216
- else {
217
- $oPostUser = $oWpUsers->getUserByUsername( $sPostUserlogin );
218
- }
219
-
220
- $sRequestRole = strtolower( $oReq->post( 'role', '' ) );
221
-
222
- if ( $oPostUser instanceof WP_User ) {
223
- // editing an existing user other than yourself?
224
- if ( $oPostUser->user_login != $oWpUsers->getCurrentWpUsername() ) {
225
-
226
- if ( $oWpUsers->isUserAdmin( $oPostUser ) || ( $sRequestRole == 'administrator' ) ) {
227
- $bBlockCapability = true;
228
- }
229
- }
230
- }
231
- elseif ( $sRequestRole == 'administrator' ) { //creating a new admin user?
232
- $bBlockCapability = true;
233
- }
234
-
235
- if ( $bBlockCapability ) {
236
- $aAllCaps[ $sUserCap ] = false;
237
- }
238
- }
239
-
240
- return $aAllCaps;
241
- }
242
-
243
- protected function getUserPagesToRestrict() :array {
244
- return [
245
- /* 'user-new.php', */
246
- 'user-edit.php',
247
- 'users.php',
248
- ];
249
- }
250
-
251
- /**
252
- * Need to always re-test isPluginAdmin() because there's a dynamic filter in there to
253
- * permit saving by the plugin itself.
254
- *
255
- * Right before a plugin option is due to update it will check that we have permissions to do so
256
- * and if not, will * revert the option to save to the previous one.
257
- * @param mixed $mNewOptionValue
258
- * @param string $key
259
- * @param mixed $mOldValue
260
- * @return mixed
261
- */
262
- public function blockOptionsSaves( $mNewOptionValue, $key, $mOldValue ) {
263
-
264
- if ( !$this->getCon()->isPluginAdmin() && is_string( $key )
265
- && ( $this->isOptionForThisPlugin( $key ) || $this->isOptionRestricted( $key ) ) ) {
266
- $mNewOptionValue = $mOldValue;
267
- }
268
-
269
- return $mNewOptionValue;
270
- }
271
-
272
- private function isOptionForThisPlugin( string $key ) :bool {
273
- return preg_match( $this->getOptionRegexPattern(), $key ) > 0;
274
- }
275
-
276
- private function isOptionRestricted( string $key ) :bool {
277
- /** @var SecurityAdmin\Options $opts */
278
- $opts = $this->getOptions();
279
- return $opts->getAdminAccessArea_Options()
280
- && in_array( $key, $opts->getOptionsToRestrict() );
281
- }
282
-
283
- /**
284
- * @param array $aAllCaps
285
- * @param $cap
286
- * @param array $aArgs
287
- * @return array
288
- */
289
- public function disablePluginManipulation( $aAllCaps, $cap, $aArgs ) {
290
- /** @var SecurityAdmin\Options $opts */
291
- $opts = $this->getOptions();
292
- $req = Services::Request();
293
-
294
- /** @var string $sRequestedCapability */
295
- $sRequestedCapability = $aArgs[ 0 ];
296
-
297
- // special case for plugin info thickbox for changelog
298
- $bIsChangelog = defined( 'IFRAME_REQUEST' )
299
- && ( $sRequestedCapability === 'install_plugins' )
300
- && ( $req->query( 'section' ) == 'changelog' )
301
- && $req->query( 'plugin' );
302
- if ( $bIsChangelog ) {
303
- return $aAllCaps;
304
- }
305
-
306
- $aEditCapabilities = [ 'activate_plugins', 'delete_plugins', 'install_plugins', 'update_plugins' ];
307
-
308
- if ( in_array( $sRequestedCapability, $aEditCapabilities ) ) {
309
- $aAreaRestrictions = $opts->getAdminAccessArea_Plugins();
310
- if ( in_array( $sRequestedCapability, $aAreaRestrictions ) ) {
311
- $aAllCaps[ $sRequestedCapability ] = false;
312
- }
313
- }
314
-
315
- return $aAllCaps;
316
- }
317
-
318
- /**
319
- * @param array $aAllCaps
320
- * @param $cap
321
- * @param array $aArgs
322
- * @return array
323
- */
324
- public function disableThemeManipulation( $aAllCaps, $cap, $aArgs ) {
325
- // If we're registered with Admin Access we don't modify anything
326
- if ( $this->getCon()->isPluginAdmin() ) {
327
- return $aAllCaps;
328
- }
329
-
330
- /** @var SecurityAdmin\Options $opts */
331
- $opts = $this->getOptions();
332
-
333
- /** @var string $sRequestedCapability */
334
- $sRequestedCapability = $aArgs[ 0 ];
335
- $aEditCapabilities = [
336
- 'switch_themes',
337
- 'edit_theme_options',
338
- 'install_themes',
339
- 'update_themes',
340
- 'delete_themes'
341
- ];
342
-
343
- if ( in_array( $sRequestedCapability, $aEditCapabilities ) ) {
344
- $aAreaRestrictions = $opts->getAdminAccessArea_Themes();
345
- if ( in_array( $sRequestedCapability, $aAreaRestrictions ) ) {
346
- $aAllCaps[ $sRequestedCapability ] = false;
347
- }
348
- }
349
-
350
- return $aAllCaps;
351
- }
352
-
353
- /**
354
- * @param array $aAllCaps
355
- * @param $cap
356
- * @param array $args
357
- * @return array
358
- */
359
- public function disablePostsManipulation( $aAllCaps, $cap, $args ) {
360
- if ( $this->getCon()->isPluginAdmin() ) {
361
- return $aAllCaps;
362
- }
363
-
364
- /** @var SecurityAdmin\Options $opts */
365
- $opts = $this->getOptions();
366
-
367
- /** @var string $sRequestedCapability */
368
- $sRequestedCapability = $args[ 0 ];
369
- $aEditCapabilities = [
370
- 'edit_post',
371
- 'publish_post',
372
- 'delete_post',
373
- 'edit_posts',
374
- 'publish_posts',
375
- 'delete_posts',
376
- 'edit_page',
377
- 'publish_page',
378
- 'delete_page',
379
- 'edit_pages',
380
- 'publish_pages',
381
- 'delete_pages'
382
- ];
383
- if ( in_array( $sRequestedCapability, $aEditCapabilities ) ) {
384
- $sRequestedCapabilityTrimmed = str_replace( [
385
- '_posts',
386
- '_pages',
387
- '_post',
388
- '_page'
389
- ], '', $sRequestedCapability ); //Order of items in this array is important!
390
- $aAreaRestrictions = $opts->getAdminAccessArea_Posts();
391
- if ( in_array( $sRequestedCapabilityTrimmed, $aAreaRestrictions ) ) {
392
- $aAllCaps[ $sRequestedCapability ] = false;
393
- }
394
- }
395
- return $aAllCaps;
396
- }
397
-
398
- /**
399
- * @return string
400
- */
401
- protected function getOptionRegexPattern() {
402
- if ( !isset( $this->sOptionRegexPattern ) ) {
403
- $this->sOptionRegexPattern = sprintf( '/^%s.*_options$/',
404
- $this->getCon()->getOptionStoragePrefix()
405
- );
406
- }
407
- return $this->sOptionRegexPattern;
408
- }
409
-
410
- public function printAdminAccessAjaxForm() {
411
- /** @var SecurityAdmin\ModCon $mod */
412
- $mod = $this->getMod();
413
- /** @var SecurityAdmin\Options $opts */
414
- $opts = $this->getOptions();
415
-
416
- $aRenderData = [
417
- 'flags' => [
418
- 'restrict_options' => $opts->getAdminAccessArea_Options()
419
- ],
420
- 'strings' => [
421
- 'editing_restricted' => __( 'Editing this option is currently restricted.', 'wp-simple-firewall' ),
422
- 'unlock_link' => $this->getUnlockLinkHtml(),
423
- ],
424
- 'js_snippets' => [
425
- 'options_to_restrict' => "'".implode( "','", $opts->getOptionsToRestrict() )."'",
426
- ],
427
- 'ajax' => [
428
- 'sec_admin_login_box' => $mod->getAjaxActionData( 'sec_admin_login_box', true )
429
- ]
430
- ];
431
- add_thickbox();
432
- echo $mod->renderTemplate( 'snippets/admin_access_login_box.php', $aRenderData );
433
- }
434
-
435
- /**
436
- * @param string $sLinkText
437
- * @return string
438
- */
439
- protected function getUnlockLinkHtml( $sLinkText = '' ) {
440
- if ( empty( $sLinkText ) ) {
441
- $sLinkText = __( 'Unlock', 'wp-simple-firewall' );
442
- }
443
- return sprintf(
444
- '<a href="%1$s" title="%2$s" class="thickbox">%3$s</a>',
445
- '#TB_inline?width=400&height=180&inlineId=WpsfAdminAccessLogin',
446
- __( 'Security Admin Login', 'wp-simple-firewall' ),
447
- $sLinkText
448
- );
449
- }
450
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/processors/audit_trail.php DELETED
@@ -1,74 +0,0 @@
1
- <?php
2
-
3
- use FernleafSystems\Wordpress\Plugin\Shield\Modules;
4
- use FernleafSystems\Wordpress\Plugin\Shield\Modules\AuditTrail\Auditors;
5
- use FernleafSystems\Wordpress\Plugin\Shield\Modules\AuditTrail\Lib\AuditWriter;
6
- use FernleafSystems\Wordpress\Plugin\Shield\Modules\AuditTrail\ModCon;
7
-
8
- /**
9
- * @deprecated 10.1
10
- */
11
- class ICWP_WPSF_Processor_AuditTrail extends Modules\BaseShield\ShieldProcessor {
12
-
13
- /**
14
- * @var AuditWriter
15
- */
16
- private $oAuditor;
17
-
18
- public function run() {
19
- $this->initAuditors();
20
- $this->getSubProAuditor()->execute();
21
- }
22
-
23
- /**
24
- * @return AuditWriter
25
- */
26
- private function loadAuditorWriter() {
27
- if ( !isset( $this->oAuditor ) ) {
28
- /** @var ModCon $mod */
29
- $mod = $this->getMod();
30
- $this->oAuditor = ( new AuditWriter( $this->getCon() ) )
31
- ->setDbHandler( $mod->getDbHandler_AuditTrail() );
32
- }
33
- return $this->oAuditor;
34
- }
35
-
36
- private function initAuditors() {
37
- $this->loadAuditorWriter()->setIfCommit( true );
38
-
39
- ( new Auditors\Users() )
40
- ->setMod( $this->getMod() )
41
- ->run();
42
- ( new Auditors\Plugins() )
43
- ->setMod( $this->getMod() )
44
- ->run();
45
- ( new Auditors\Themes() )
46
- ->setMod( $this->getMod() )
47
- ->run();
48
- ( new Auditors\Wordpress() )
49
- ->setMod( $this->getMod() )
50
- ->run();
51
- ( new Auditors\Posts() )
52
- ->setMod( $this->getMod() )
53
- ->run();
54
- ( new Auditors\Emails() )
55
- ->setMod( $this->getMod() )
56
- ->run();
57
- ( new Auditors\Upgrades() )
58
- ->setMod( $this->getMod() )
59
- ->run();
60
- }
61
-
62
- /**
63
- * @return ICWP_WPSF_Processor_AuditTrail_Auditor|mixed
64
- */
65
- public function getSubProAuditor() {
66
- return $this->getSubPro( 'auditor' );
67
- }
68
-
69
- protected function getSubProMap() :array {
70
- return [
71
- 'auditor' => 'ICWP_WPSF_Processor_AuditTrail_Auditor',
72
- ];
73
- }
74
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/processors/autoupdates.php DELETED
@@ -1,456 +0,0 @@
1
- <?php
2
-
3
- use FernleafSystems\Wordpress\Plugin\Shield\Modules;
4
- use FernleafSystems\Wordpress\Services\Services;
5
-
6
- /**
7
- * @deprecated 10.1
8
- */
9
- class ICWP_WPSF_Processor_Autoupdates extends Modules\BaseShield\ShieldProcessor {
10
-
11
- /**
12
- * @var array
13
- */
14
- private $aAssetsVersions = [];
15
-
16
- /**
17
- * The allow_* core filters are run first in a "should_update" query. Then comes the "auto_update_core"
18
- * filter. What this filter decides will ultimately determine the fate of any core upgrade.
19
- */
20
- public function run() {
21
- /** @var Modules\Autoupdates\Options $oOpts */
22
- $oOpts = $this->getOptions();
23
-
24
- $nPriority = $this->getHookPriority();
25
- if ( Services::WpGeneral()->isClassicPress() ) {
26
- add_filter( 'allow_patch_auto_core_updates', [ $this, 'autoupdate_core_minor' ], $nPriority );
27
- add_filter( 'allow_minor_auto_core_updates', [ $this, 'autoupdate_core_major' ], $nPriority );
28
- }
29
- else {
30
- add_filter( 'allow_minor_auto_core_updates', [ $this, 'autoupdate_core_minor' ], $nPriority );
31
- add_filter( 'allow_major_auto_core_updates', [ $this, 'autoupdate_core_major' ], $nPriority );
32
- }
33
-
34
- add_filter( 'auto_update_plugin', [ $this, 'autoupdate_plugins' ], $nPriority, 2 );
35
- add_filter( 'auto_update_theme', [ $this, 'autoupdate_themes' ], $nPriority, 2 );
36
- add_filter( 'auto_update_core', [ $this, 'autoupdate_core' ], $nPriority, 2 );
37
-
38
- if ( !$oOpts->isDisableAllAutoUpdates() ) {
39
- //more parameter options here for later
40
- add_filter( 'auto_core_update_send_email', [ $this, 'autoupdate_send_email' ], $nPriority, 1 );
41
- add_filter( 'auto_core_update_email', [ $this, 'autoupdate_email_override' ], $nPriority, 1 );
42
- add_filter( 'auto_plugin_theme_update_email', [ $this, 'autoupdate_email_override' ], $nPriority, 1 );
43
-
44
- add_action( 'set_site_transient_update_core', [ $this, 'trackUpdateTimesCore' ] );
45
- add_action( 'set_site_transient_update_plugins', [ $this, 'trackUpdateTimesPlugins' ] );
46
- add_action( 'set_site_transient_update_themes', [ $this, 'trackUpdateTimesThemes' ] );
47
-
48
- if ( $oOpts->isSendAutoupdatesNotificationEmail()
49
- && !Services::WpGeneral()->getWordpressIsAtLeastVersion( '5.5' ) ) {
50
- $this->trackAssetsVersions();
51
- add_action( 'automatic_updates_complete', [ $this, 'sendNotificationEmail' ] );
52
- }
53
- }
54
- }
55
-
56
- public function onWpLoaded() {
57
- /** @var Modules\Autoupdates\Options $oOpts */
58
- $oOpts = $this->getOptions();
59
- if ( $oOpts->isDisableAllAutoUpdates() ) {
60
- $this->disableAllAutoUpdates();
61
- }
62
- }
63
-
64
- private function disableAllAutoUpdates() {
65
- remove_all_filters( 'automatic_updater_disabled' );
66
- add_filter( 'automatic_updater_disabled', '__return_true', PHP_INT_MAX );
67
- if ( !defined( 'WP_AUTO_UPDATE_CORE' ) ) {
68
- define( 'WP_AUTO_UPDATE_CORE', false );
69
- }
70
- }
71
-
72
- private function trackAssetsVersions() {
73
- $aAssVers = $this->getTrackedAssetsVersions();
74
-
75
- $oWpPlugins = Services::WpPlugins();
76
- foreach ( array_keys( $oWpPlugins->getUpdates() ) as $sFile ) {
77
- $aAssVers[ 'plugins' ][ $sFile ] = $oWpPlugins->getPluginAsVo( $sFile )->Version;
78
- }
79
- $oWpThemes = Services::WpThemes();
80
- foreach ( array_keys( $oWpThemes->getUpdates() ) as $sFile ) {
81
- $aAssVers[ 'themes' ][ $sFile ] = $oWpThemes->getTheme( $sFile )->get( 'Version' );
82
- }
83
- $this->aAssetsVersions = $aAssVers;
84
- }
85
-
86
- /**
87
- * @return array
88
- */
89
- protected function getTrackedAssetsVersions() {
90
- if ( empty( $this->aAssetsVersions ) || !is_array( $this->aAssetsVersions ) ) {
91
- $this->aAssetsVersions = [
92
- 'plugins' => [],
93
- 'themes' => [],
94
- ];
95
- }
96
- return $this->aAssetsVersions;
97
- }
98
-
99
- /**
100
- * @param stdClass $oUpdates
101
- */
102
- public function trackUpdateTimesCore( $oUpdates ) {
103
-
104
- if ( !empty( $oUpdates ) && isset( $oUpdates->updates ) && is_array( $oUpdates->updates ) ) {
105
- /** @var Modules\Autoupdates\Options $oOpts */
106
- $oOpts = $this->getOptions();
107
-
108
- $aTk = $oOpts->getDelayTracking();
109
- $aItemTk = isset( $aTk[ 'core' ][ 'wp' ] ) ? $aTk[ 'core' ][ 'wp' ] : [];
110
- foreach ( $oUpdates->updates as $oUpdate ) {
111
- if ( 'autoupdate' == $oUpdate->response ) {
112
- $sVersion = $oUpdate->current;
113
- if ( !isset( $aItemTk[ $sVersion ] ) ) {
114
- $aItemTk[ $sVersion ] = Services::Request()->ts();
115
- }
116
- }
117
- }
118
- $aTk[ 'core' ][ 'wp' ] = array_slice( $aItemTk, -5 );
119
- $oOpts->setDelayTracking( $aTk );
120
- }
121
- }
122
-
123
- /**
124
- * @param stdClass $oUpdates
125
- */
126
- public function trackUpdateTimesPlugins( $oUpdates ) {
127
- $this->trackUpdateTimeCommon( $oUpdates, 'plugins' );
128
- }
129
-
130
- /**
131
- * @param stdClass $oUpdates
132
- */
133
- public function trackUpdateTimesThemes( $oUpdates ) {
134
- $this->trackUpdateTimeCommon( $oUpdates, 'themes' );
135
- }
136
-
137
- /**
138
- * @param stdClass $oUpdates
139
- * @param string $sContext - plugins/themes
140
- */
141
- protected function trackUpdateTimeCommon( $oUpdates, $sContext ) {
142
- /** @var Modules\Autoupdates\Options $oOpts */
143
- $oOpts = $this->getOptions();
144
-
145
- if ( !empty( $oUpdates ) && isset( $oUpdates->response ) && is_array( $oUpdates->response ) ) {
146
-
147
- $aTk = $oOpts->getDelayTracking();
148
- foreach ( $oUpdates->response as $sSlug => $oUpdate ) {
149
- $aItemTk = isset( $aTk[ $sContext ][ $sSlug ] ) ? $aTk[ $sContext ][ $sSlug ] : [];
150
- if ( is_array( $oUpdate ) ) {
151
- $oUpdate = (object)$oUpdate;
152
- }
153
-
154
- $sNewVersion = isset( $oUpdate->new_version ) ? $oUpdate->new_version : '';
155
- if ( !empty( $sNewVersion ) ) {
156
- if ( !isset( $aItemTk[ $sNewVersion ] ) ) {
157
- $aItemTk[ $sNewVersion ] = Services::Request()->ts();
158
- }
159
- $aTk[ $sContext ][ $sSlug ] = array_slice( $aItemTk, -3 );
160
- }
161
- }
162
- $oOpts->setDelayTracking( $aTk );
163
- }
164
- }
165
-
166
- /**
167
- * This is a filter method designed to say whether a major core WordPress upgrade should be permitted,
168
- * based on the plugin settings.
169
- * @param bool $bUpdate
170
- * @return bool
171
- */
172
- public function autoupdate_core_major( $bUpdate ) {
173
- /** @var Modules\Autoupdates\Options $oOpts */
174
- $oOpts = $this->getOptions();
175
-
176
- if ( $oOpts->isDisableAllAutoUpdates() || $oOpts->isAutoUpdateCoreNever() ) {
177
- $bUpdate = false;
178
- }
179
- elseif ( !$oOpts->isDelayUpdates() ) { // delay handled elsewhere
180
- $bUpdate = $oOpts->isAutoUpdateCoreMajor();
181
- }
182
-
183
- return $bUpdate;
184
- }
185
-
186
- /**
187
- * This is a filter method designed to say whether a minor core WordPress upgrade should be permitted,
188
- * based on the plugin settings.
189
- * @param bool $bUpdate
190
- * @return bool
191
- */
192
- public function autoupdate_core_minor( $bUpdate ) {
193
- /** @var Modules\Autoupdates\Options $oOpts */
194
- $oOpts = $this->getOptions();
195
-
196
- if ( $oOpts->isDisableAllAutoUpdates() || $oOpts->isAutoUpdateCoreNever() ) {
197
- $bUpdate = false;
198
- }
199
- elseif ( !$oOpts->isDelayUpdates() ) {
200
- $bUpdate = !$oOpts->isAutoUpdateCoreNever();
201
- }
202
- return $bUpdate;
203
- }
204
-
205
- /**
206
- * @param bool $bDoAutoUpdate
207
- * @param \stdClass $oCoreUpdate
208
- * @return bool
209
- */
210
- public function autoupdate_core( $bDoAutoUpdate, $oCoreUpdate ) {
211
- /** @var Modules\Autoupdates\Options $oOpts */
212
- $oOpts = $this->getOptions();
213
-
214
- if ( $oOpts->isDisableAllAutoUpdates() ) {
215
- $bDoAutoUpdate = false;
216
- }
217
- elseif ( $this->isDelayed( $oCoreUpdate, 'core' ) ) {
218
- $bDoAutoUpdate = false;
219
- }
220
-
221
- return $bDoAutoUpdate;
222
- }
223
-
224
- /**
225
- * @param bool $bDoAutoUpdate
226
- * @param \stdClass|string $mItem
227
- * @return bool
228
- */
229
- public function autoupdate_plugins( $bDoAutoUpdate, $mItem ) {
230
- /** @var Modules\Autoupdates\Options $oOpts */
231
- $oOpts = $this->getOptions();
232
-
233
- if ( $oOpts->isDisableAllAutoUpdates() ) {
234
- $bDoAutoUpdate = false;
235
- }
236
- else {
237
- $file = Services::WpGeneral()->getFileFromAutomaticUpdateItem( $mItem );
238
-
239
- if ( $this->isDelayed( $file, 'plugins' ) ) {
240
- $bDoAutoUpdate = false;
241
- }
242
- elseif ( $oOpts->isAutoupdateAllPlugins() ) {
243
- $bDoAutoUpdate = true;
244
- }
245
- elseif ( $file === $this->getCon()->getPluginBaseFile() ) {
246
- $sAuto = $oOpts->getSelfAutoUpdateOpt();
247
- if ( $sAuto === 'immediate' ) {
248
- $bDoAutoUpdate = true;
249
- }
250
- elseif ( $sAuto === 'disabled' ) {
251
- $bDoAutoUpdate = false;
252
- }
253
- }
254
- }
255
-
256
- return $bDoAutoUpdate;
257
- }
258
-
259
- /**
260
- * @param bool $bDoAutoUpdate
261
- * @param stdClass|string $mItem
262
- * @return bool
263
- */
264
- public function autoupdate_themes( $bDoAutoUpdate, $mItem ) {
265
- /** @var Modules\Autoupdates\Options $opts */
266
- $opts = $this->getOptions();
267
-
268
- if ( $opts->isDisableAllAutoUpdates() ) {
269
- $bDoAutoUpdate = false;
270
- }
271
- else {
272
- $file = Services::WpGeneral()->getFileFromAutomaticUpdateItem( $mItem, 'theme' );
273
-
274
- if ( $this->isDelayed( $file, 'themes' ) ) {
275
- $bDoAutoUpdate = false;
276
- }
277
- elseif ( $opts->isOpt( 'enable_autoupdate_themes', 'Y' ) ) {
278
- $bDoAutoUpdate = true;
279
- }
280
- }
281
-
282
- return $bDoAutoUpdate;
283
- }
284
-
285
- /**
286
- * @param string|stdClass $sSlug
287
- * @param string $sContext
288
- * @return bool
289
- */
290
- private function isDelayed( $sSlug, $sContext = 'plugins' ) {
291
- /** @var Modules\Autoupdates\Options $oOpts */
292
- $oOpts = $this->getOptions();
293
-
294
- $bDelayed = false;
295
-
296
- if ( $oOpts->isDelayUpdates() ) {
297
-
298
- $aTk = $oOpts->getDelayTracking();
299
-
300
- $sVersion = '';
301
- if ( $sContext == 'core' ) {
302
- $sVersion = $sSlug->current; // stdClass from transient update_core
303
- $sSlug = 'wp';
304
- }
305
-
306
- $aItemTk = isset( $aTk[ $sContext ][ $sSlug ] ) ? $aTk[ $sContext ][ $sSlug ] : [];
307
-
308
- if ( $sContext == 'plugins' ) {
309
- $oPlugin = Services::WpPlugins()->getUpdateInfo( $sSlug );
310
- $sVersion = isset( $oPlugin->new_version ) ? $oPlugin->new_version : '';
311
- }
312
- elseif ( $sContext == 'themes' ) {
313
- $aThemeInfo = Services::WpThemes()->getUpdateInfo( $sSlug );
314
- $sVersion = isset( $aThemeInfo[ 'new_version' ] ) ? $aThemeInfo[ 'new_version' ] : '';
315
- }
316
-
317
- if ( !empty( $sVersion ) && isset( $aItemTk[ $sVersion ] ) ) {
318
- $bDelayed = ( Services::Request()->ts() - $aItemTk[ $sVersion ] < $oOpts->getDelayUpdatesPeriod() );
319
- }
320
- }
321
-
322
- return $bDelayed;
323
- }
324
-
325
- /**
326
- * A filter on whether or not a notification email is sent after core upgrades are attempted.
327
- * @param bool $bSendEmail
328
- * @return bool
329
- */
330
- public function autoupdate_send_email( $bSendEmail ) {
331
- /** @var Modules\Autoupdates\Options $oOpts */
332
- $oOpts = $this->getOptions();
333
- return $oOpts->isSendAutoupdatesNotificationEmail();
334
- }
335
-
336
- /**
337
- * A filter on the target email address to which to send upgrade notification emails.
338
- * @param array $aEmailParams
339
- * @return array
340
- */
341
- public function autoupdate_email_override( $aEmailParams ) {
342
- $sOverride = $this->getOptions()->getOpt( 'override_email_address', '' );
343
- if ( Services::Data()->validEmail( $sOverride ) ) {
344
- $aEmailParams[ 'to' ] = $sOverride;
345
- }
346
- return $aEmailParams;
347
- }
348
-
349
- /**
350
- * @param array $aUpdateResults
351
- */
352
- public function sendNotificationEmail( $aUpdateResults ) {
353
- if ( empty( $aUpdateResults ) || !is_array( $aUpdateResults ) ) {
354
- return;
355
- }
356
-
357
- // Are there really updates?
358
- $bReallyUpdates = false;
359
-
360
- $aBody = [
361
- sprintf(
362
- __( 'This is a quick notification from the %s that WordPress Automatic Updates just completed on your site with the following results.', 'wp-simple-firewall' ),
363
- $this->getCon()->getHumanName()
364
- ),
365
- ''
366
- ];
367
-
368
- $aTrkd = $this->getTrackedAssetsVersions();
369
-
370
- $oWpPlugins = Services::WpPlugins();
371
- if ( !empty( $aUpdateResults[ 'plugin' ] ) && is_array( $aUpdateResults[ 'plugin' ] ) ) {
372
- $bHasPluginUpdates = false;
373
- $aTrkdPlugs = $aTrkd[ 'plugins' ];
374
-
375
- $aTempContent[] = __( 'Plugins Updated:', 'wp-simple-firewall' );
376
- foreach ( $aUpdateResults[ 'plugin' ] as $oUpdate ) {
377
- $oP = $oWpPlugins->getPluginAsVo( $oUpdate->item->plugin, true );
378
- $bValidUpdate = !empty( $oUpdate->result ) && !empty( $oUpdate->name )
379
- && isset( $aTrkdPlugs[ $oP->file ] )
380
- && version_compare( $aTrkdPlugs[ $oP->file ], $oP->Version, '<' );
381
- if ( $bValidUpdate ) {
382
- $aTempContent[] = ' - '.sprintf(
383
- __( 'Plugin "%s" auto-updated from "%s" to version "%s"', 'wp-simple-firewall' ),
384
- $oUpdate->name, $aTrkdPlugs[ $oP->file ], $oP->Version );
385
- $bHasPluginUpdates = true;
386
- }
387
- }
388
- $aTempContent[] = '';
389
-
390
- if ( $bHasPluginUpdates ) {
391
- $bReallyUpdates = true;
392
- $aBody = array_merge( $aBody, $aTempContent );
393
- }
394
- }
395
-
396
- if ( !empty( $aUpdateResults[ 'theme' ] ) && is_array( $aUpdateResults[ 'theme' ] ) ) {
397
- $bHasThemesUpdates = false;
398
- $aTrkdThemes = $aTrkd[ 'themes' ];
399
-
400
- $aTempContent = [ __( 'Themes Updated:', 'wp-simple-firewall' ) ];
401
- foreach ( $aUpdateResults[ 'theme' ] as $oUpdate ) {
402
- $oItem = $oUpdate->item;
403
- $bValidUpdate = isset( $oUpdate->result ) && $oUpdate->result && !empty( $oUpdate->name )
404
- && isset( $aTrkdThemes[ $oItem->theme ] )
405
- && version_compare( $aTrkdThemes[ $oItem->theme ], $oItem->new_version, '<' );
406
- if ( $bValidUpdate ) {
407
- $aTempContent[] = ' - '.sprintf(
408
- __( 'Theme "%s" auto-updated from "%s" to version "%s"', 'wp-simple-firewall' ),
409
- $oUpdate->name, $aTrkdThemes[ $oItem->theme ], $oItem->new_version );
410
- $bHasThemesUpdates = true;
411
- }
412
- }
413
- $aTempContent[] = '';
414
-
415
- if ( $bHasThemesUpdates ) {
416
- $bReallyUpdates = true;
417
- $aBody = array_merge( $aBody, $aTempContent );
418
- }
419
- }
420
-
421
- if ( !empty( $aUpdateResults[ 'core' ] ) && is_array( $aUpdateResults[ 'core' ] ) ) {
422
- $bHasCoreUpdates = false;
423
- $aTempContent = [ __( 'WordPress Core Updated:', 'wp-simple-firewall' ) ];
424
- foreach ( $aUpdateResults[ 'core' ] as $oUpdate ) {
425
- if ( isset( $oUpdate->result ) && !is_wp_error( $oUpdate->result ) ) {
426
- $aTempContent[] = ' - '.sprintf( 'WordPress was automatically updated to "%s"', $oUpdate->name );
427
- $bHasCoreUpdates = true;
428
- }
429
- }
430
- $aTempContent[] = '';
431
-
432
- if ( $bHasCoreUpdates ) {
433
- $bReallyUpdates = true;
434
- $aBody = array_merge( $aBody, $aTempContent );
435
- }
436
- }
437
-
438
- if ( !$bReallyUpdates ) {
439
- return;
440
- }
441
-
442
- $aBody[] = __( 'Thank you.', 'wp-simple-firewall' );
443
-
444
- $sTitle = sprintf( __( "Notice: %s", 'wp-simple-firewall' ), __( "Automatic Updates Completed", 'wp-simple-firewall' ) );
445
- $this->getEmailProcessor()
446
- ->sendEmailWithWrap( $this->getOptions()->getOpt( 'override_email_address' ), $sTitle, $aBody );
447
- die();
448
- }
449
-
450
- /**
451
- * @return int
452
- */
453
- private function getHookPriority() {
454
- return $this->getOptions()->getDef( 'action_hook_priority' );
455
- }
456
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/processors/comments_filter.php DELETED
@@ -1,75 +0,0 @@
1
- <?php
2
-
3
- use FernleafSystems\Wordpress\Plugin\Shield\Modules;
4
- use FernleafSystems\Wordpress\Plugin\Shield\Modules\CommentsFilter;
5
- use FernleafSystems\Wordpress\Services\Services;
6
-
7
- /**
8
- * @deprecated 10.1
9
- */
10
- class ICWP_WPSF_Processor_CommentsFilter extends Modules\BaseShield\ShieldProcessor {
11
-
12
- public function run() {
13
- }
14
-
15
- public function onWpInit() {
16
- /** @var CommentsFilter\ModCon $mod */
17
- $mod = $this->getMod();
18
- /** @var CommentsFilter\Options $opts */
19
- $opts = $this->getOptions();
20
- $oWpUsers = Services::WpUsers();
21
-
22
- $bLoadComProc = !$oWpUsers->isUserLoggedIn() ||
23
- !( new CommentsFilter\Scan\IsEmailTrusted() )->trusted(
24
- $oWpUsers->getCurrentWpUser()->user_email,
25
- $opts->getApprovedMinimum(),
26
- $opts->getTrustedRoles()
27
- );
28
-
29
- if ( $bLoadComProc ) {
30
-
31
- if ( $opts->isEnabledCaptcha() && $mod->getCaptchaCfg()->ready ) {
32
- $this->getSubPro( 'recaptcha' )->execute();
33
- }
34
-
35
- if ( Services::Request()->isPost() ) {
36
- add_filter( 'comment_notification_recipients', [ $this, 'clearCommentNotificationEmail' ], 100, 1 );
37
- }
38
- elseif ( $opts->isEnabledGaspCheck() ) {
39
- $this->getSubPro( 'bot' )->execute();
40
- }
41
- }
42
- }
43
-
44
- public function runHourlyCron() {
45
- /** @var CommentsFilter\Options $opts */
46
- $opts = $this->getOptions();
47
- if ( $opts->isEnabledGaspCheck() && function_exists( 'delete_expired_transients' ) ) {
48
- delete_expired_transients(); // cleanup unused comment tokens
49
- }
50
- }
51
-
52
- /**
53
- * @return array
54
- */
55
- protected function getSubProMap() :array {
56
- return [
57
- 'bot' => 'ICWP_WPSF_Processor_CommentsFilter_BotSpam',
58
- 'recaptcha' => 'ICWP_WPSF_Processor_CommentsFilter_GoogleRecaptcha',
59
- ];
60
- }
61
-
62
- /**
63
- * When you set a new comment as anything but 'spam' a notification email is sent to the post author.
64
- * We suppress this for when we mark as trash by emptying the email notifications list.
65
- * @param array $aEmails
66
- * @return array
67
- */
68
- public function clearCommentNotificationEmail( $aEmails ) {
69
- $sStatus = apply_filters( $this->getCon()->prefix( 'cf_status' ), '' );
70
- if ( in_array( $sStatus, [ 'reject', 'trash' ] ) ) {
71
- $aEmails = [];
72
- }
73
- return $aEmails;
74
- }
75
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/processors/commentsfilter_botspam.php DELETED
@@ -1,116 +0,0 @@
1
- <?php
2
-
3
- use FernleafSystems\Wordpress\Plugin\Shield\Modules;
4
- use FernleafSystems\Wordpress\Plugin\Shield\Modules\CommentsFilter;
5
- use FernleafSystems\Wordpress\Services\Services;
6
-
7
- /**
8
- * @deprecated 10.1
9
- */
10
- class ICWP_WPSF_Processor_CommentsFilter_BotSpam extends Modules\BaseShield\ShieldProcessor {
11
-
12
- /**
13
- * The unique comment token assigned to this page
14
- * @var string
15
- */
16
- private $sFormId;
17
-
18
- /**
19
- * @var bool
20
- */
21
- private $bFormItemPrinted = false;
22
-
23
- public function run() {
24
- add_action( 'wp', [ $this, 'onWp' ] );
25
- add_action( 'wp_footer', [ $this, 'maybeDequeueScript' ] );
26
- }
27
-
28
- public function onWp() {
29
- add_action( 'comment_form', [ $this, 'printGaspFormItems' ], 1 );
30
- }
31
-
32
- public function onWpEnqueueJs() {
33
- /** @var CommentsFilter\ModCon $mod */
34
- $mod = $this->getMod();
35
- /** @var CommentsFilter\Options $opts */
36
- $opts = $this->getOptions();
37
- $con = $this->getCon();
38
-
39
- $sAsset = 'shield-comments';
40
- $sUnique = $con->prefix( 'shield-comments' );
41
- wp_register_script(
42
- $sUnique,
43
- $con->getPluginUrl_Js( $sAsset ),
44
- [ 'jquery' ],
45
- $con->getVersion(),
46
- true
47
- );
48
- wp_enqueue_script( $sUnique );
49
-
50
- $nTs = Services::Request()->ts();
51
- $aNonce = $mod->getAjaxActionData( 'comment_token'.Services::IP()->getRequestIp() );
52
- $aNonce[ 'ts' ] = $nTs;
53
- $aNonce[ 'post_id' ] = Services::WpPost()->getCurrentPostId();
54
-
55
- wp_localize_script(
56
- $sUnique,
57
- 'shield_comments',
58
- [
59
- 'ajax' => [
60
- 'comment_token' => $aNonce,
61
- ],
62
- 'vars' => [
63
- 'cbname' => 'cb_nombre'.rand(),
64
- 'botts' => $nTs,
65
- 'token' => 'not created',
66
- 'uniq' => $this->getUniqueFormId(),
67
- 'cooldown' => $opts->getTokenCooldown(),
68
- 'expires' => $opts->getTokenExpireInterval(),
69
- ],
70
- 'strings' => [
71
- 'label' => $mod->getTextOpt( 'custom_message_checkbox' ),
72
- 'alert' => $mod->getTextOpt( 'custom_message_alert' ),
73
- 'comment_reload' => $mod->getTextOpt( 'custom_message_comment_reload' ),
74
- 'js_comment_wait' => $mod->getTextOpt( 'custom_message_comment_wait' ),
75
- ],
76
- 'flags' => [
77
- 'gasp' => true,
78
- 'recap' => $opts->isEnabledCaptcha() && $mod->getCaptchaCfg()->ready,
79
- ]
80
- ]
81
- );
82
- }
83
-
84
- /**
85
- * If the comment form component hasn't been printed, there's no comment form to protect.
86
- */
87
- public function maybeDequeueScript() {
88
- if ( empty( $this->bFormItemPrinted ) ) {
89
- wp_dequeue_script( $this->getCon()->prefix( 'shield-comments' ) );
90
- }
91
- }
92
-
93
- public function printGaspFormItems() {
94
- $this->bFormItemPrinted = true;
95
- echo $this->getMod()
96
- ->renderTemplate(
97
- 'snippets/comment_form_botbox.twig',
98
- [ 'uniq' => $this->getUniqueFormId() ],
99
- true
100
- );
101
- }
102
-
103
- /**
104
- * @return string
105
- */
106
- private function getUniqueFormId() {
107
- if ( !isset( $this->sFormId ) ) {
108
- $oDp = Services::Data();
109
- $sId = $oDp->generateRandomLetter().$oDp->generateRandomString( rand( 7, 23 ), 7 );
110
- $this->sFormId = preg_replace(
111
- '#[^a-zA-Z0-9]#', '',
112
- apply_filters( 'icwp_shield_cf_gasp_uniqid', $sId ) );
113
- }
114
- return $this->sFormId;
115
- }
116
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/processors/commentsfilter_googlerecaptcha.php DELETED
@@ -1,35 +0,0 @@
1
- <?php
2
-
3
- use FernleafSystems\Wordpress\Plugin\Shield\Modules;
4
- use FernleafSystems\Wordpress\Services\Services;
5
-
6
- /**
7
- * @deprecated 10.1
8
- */
9
- class ICWP_WPSF_Processor_CommentsFilter_GoogleRecaptcha extends Modules\BaseShield\ShieldProcessor {
10
-
11
- public function run() {
12
- add_action( 'wp', [ $this, 'setup' ] );
13
- }
14
-
15
- /**
16
- * The WP Query is alive and well at this stage so we can assume certain data is available.
17
- */
18
- public function setup() {
19
- if ( Services::WpComments()->isCommentsOpen() ) {
20
- $this->getCon()
21
- ->getModule_Plugin()
22
- ->getCaptchaEnqueue()
23
- ->setMod( $this->getMod() )
24
- ->setToEnqueue();
25
- add_action( 'comment_form_after_fields', [ $this, 'printGoogleRecaptchaCheck' ] );
26
- }
27
- }
28
-
29
- public function printGoogleRecaptchaCheck() {
30
- echo $this->getCon()
31
- ->getModule_Plugin()
32
- ->getCaptchaEnqueue()
33
- ->getCaptchaHtml();
34
- }
35
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/processors/email.php DELETED
@@ -1,218 +0,0 @@
1
- <?php
2
-
3
- use FernleafSystems\Wordpress\Plugin\Shield;
4
- use FernleafSystems\Wordpress\Plugin\Shield\Modules;
5
- use FernleafSystems\Wordpress\Services\Services;
6
-
7
- /**
8
- * @deprecated 10.1
9
- */
10
- class ICWP_WPSF_Processor_Email extends Modules\BaseShield\ShieldProcessor {
11
-
12
- const Slug = 'email';
13
-
14
- /**
15
- * @return array
16
- */
17
- protected function getEmailHeader() {
18
- return [
19
- __( 'Hi !', 'wp-simple-firewall' ),
20
- '',
21
- ];
22
- }
23
-
24
- /**
25
- * @return array
26
- */
27
- protected function getEmailFooter() {
28
- $con = $this->getCon();
29
- $oWp = Services::WpGeneral();
30
-
31
- {
32
- $aGoProPhrases = [
33
- 'Go PRO For The Equivalent Of 1 Cappuccino Per Month &#9749;',
34
- 'Go PRO For The Equivalent Of 1 Beer Per Month &#127866;',
35
- 'Go PRO For The Equivalent Of 1 Glass Of Wine Per Month &#127863;',
36
- ];
37
- $aBenefits = [
38
- 'The Easiest, Frustration-Free Pro-Upgrade Available Anywhere',
39
- 'Powerful, Auto-Learning Malware Scanner',
40
- 'Plugin and Theme File Guard',
41
- 'Vulnerability Scanner',
42
- 'Traffic Rate Limiting',
43
- 'WooCommerce Support',
44
- 'Automatic Import/Export Sync Of Options Across Your WP Portfolio',
45
- 'Powerful User Password Policies',
46
- 'Exclusive Customer Support',
47
- 'That Warm And Fuzzy Feeling That Comes From Supporting Future Development',
48
- ];
49
- shuffle( $aBenefits );
50
- }
51
-
52
- $aFooter = [
53
- $this->getMod()
54
- ->renderTemplate( '/email/footer.twig', [
55
- 'strings' => [
56
- 'benefits' => $aBenefits,
57
- 'much_more' => 'And So Much More',
58
- 'upgrade' => $aGoProPhrases[ array_rand( $aGoProPhrases ) ],
59
- 'sent_from' => sprintf( __( 'Email sent from the %s Plugin v%s, on %s.', 'wp-simple-firewall' ),
60
- $this->getCon()->getHumanName(),
61
- $this->getCon()->getVersion(),
62
- $oWp->getHomeUrl()
63
- ),
64
- 'delays' => __( 'Note: Email delays are caused by website hosting and email providers.', 'wp-simple-firewall' ),
65
- 'time_sent' => sprintf( __( 'Time Sent: %s', 'wp-simple-firewall' ), $oWp->getTimeStampForDisplay() ),
66
- ],
67
- 'hrefs' => [
68
- 'upgrade' => 'https://shsec.io/buyshieldproemailfooter',
69
- 'much_more' => 'https://shsec.io/gp'
70
- ],
71
- 'flags' => [
72
- 'is_pro' => $con->isPremiumActive(),
73
- 'is_whitelabelled' => $con->getModule_SecAdmin()->isWlEnabled()
74
- ]
75
- ] ),
76
- ];
77
-
78
- return apply_filters( 'icwp_shield_email_footer', $aFooter );
79
- }
80
-
81
- /**
82
- * Wraps up a message with header and footer
83
- * @param string $sAddress
84
- * @param string $sSubject
85
- * @param array $aMessage
86
- * @return bool
87
- */
88
- public function sendEmailWithWrap( $sAddress = '', $sSubject = '', $aMessage = [] ) :bool {
89
- $oWP = Services::WpGeneral();
90
- return $this->send(
91
- $sAddress,
92
- sprintf( '[%s] %s', html_entity_decode( $oWP->getSiteName(), ENT_QUOTES ), $sSubject ),
93
- sprintf( '<html lang="%s">%s</html>',
94
- $oWP->getLocale( '-' ),
95
- implode( "<br />", array_merge( $this->getEmailHeader(), $aMessage, $this->getEmailFooter() ) )
96
- )
97
- );
98
- }
99
-
100
- public function sendEmailWithTemplate( string $templ, string $to, string $subject, array $body ) :bool {
101
- return $this->send(
102
- $to,
103
- $subject,
104
- $this->getMod()->renderTemplate(
105
- $templ,
106
- [
107
- 'header' => $this->getEmailHeader(),
108
- 'body' => $body,
109
- 'footer' => $this->getEmailFooter(),
110
- 'vars' => [
111
- 'lang' => Services::WpGeneral()->getLocale( '-' )
112
- ]
113
- ],
114
- true
115
- )
116
- );
117
- }
118
-
119
- /**
120
- * @param string $sAddress
121
- * @param string $sSubject
122
- * @param string $sMessageBody
123
- * @return bool
124
- * @uses wp_mail
125
- */
126
- public function send( $sAddress = '', $sSubject = '', $sMessageBody = '' ) :bool {
127
-
128
- $this->emailFilters( true );
129
- $bSuccess = wp_mail(
130
- $this->verifyEmailAddress( $sAddress ),
131
- $sSubject,
132
- $sMessageBody
133
- );
134
- $this->emailFilters( false );
135
-
136
- return (bool)$bSuccess;
137
- }
138
-
139
- /**
140
- * @param $bAdd - true to add, false to remove
141
- */
142
- protected function emailFilters( $bAdd ) {
143
- if ( $bAdd ) {
144
- add_filter( 'wp_mail_from', [ $this, 'setMailFrom' ], 100 );
145
- add_filter( 'wp_mail_from_name', [ $this, 'setMailFromName' ], 100 );
146
- add_filter( 'wp_mail_content_type', [ $this, 'setMailContentType' ], 100, 0 );
147
- }
148
- else {
149
- remove_filter( 'wp_mail_from', [ $this, 'setMailFrom' ], 100 );
150
- remove_filter( 'wp_mail_from_name', [ $this, 'setMailFromName' ], 100 );
151
- remove_filter( 'wp_mail_content_type', [ $this, 'setMailContentType' ], 100 );
152
- }
153
- }
154
-
155
- /**
156
- * @return string
157
- */
158
- public function setMailContentType() {
159
- return 'text/html';
160
- }
161
-
162
- /**
163
- * @param string $sFrom
164
- * @return string
165
- */
166
- public function setMailFrom( $sFrom ) {
167
- $oDP = Services::Data();
168
- $sProposedFrom = apply_filters( 'icwp_shield_from_email', '' );
169
- if ( $oDP->validEmail( $sProposedFrom ) ) {
170
- $sFrom = $sProposedFrom;
171
- }
172
- // We help out by trying to correct any funky "from" addresses
173
- // So, at the very least, we don't fail on this for our emails.
174
- if ( !$oDP->validEmail( $sFrom ) ) {
175
- $aUrlParts = @parse_url( Services::WpGeneral()->getWpUrl() );
176
- if ( !empty( $aUrlParts[ 'host' ] ) ) {
177
- $sProposedFrom = 'wordpress@'.$aUrlParts[ 'host' ];
178
- if ( $oDP->validEmail( $sProposedFrom ) ) {
179
- $sFrom = $sProposedFrom;
180
- }
181
- }
182
- }
183
- return $sFrom;
184
- }
185
-
186
- /**
187
- * @param string $sFromName
188
- * @return string
189
- */
190
- public function setMailFromName( $sFromName ) {
191
- $sProposedFromName = apply_filters( 'icwp_shield_from_email_name', '' );
192
- if ( !empty( $sProposedFromName ) ) {
193
- $sFromName = $sProposedFromName;
194
- }
195
- else {
196
- $sFromName = sprintf( '%s - %s', $sFromName, $this->getCon()->getHumanName() );
197
- }
198
- return $sFromName;
199
- }
200
-
201
- /**
202
- * Will send email to the default recipient setup in the object.
203
- * @param string $sEmailSubject
204
- * @param array $aMessage
205
- * @return bool
206
- */
207
- public function sendEmail( $sEmailSubject, $aMessage ) {
208
- return $this->sendEmailWithWrap( null, $sEmailSubject, $aMessage );
209
- }
210
-
211
- /**
212
- * @param string $sEmail
213
- * @return string
214
- */
215
- public function verifyEmailAddress( $sEmail = '' ) {
216
- return Services::Data()->validEmail( $sEmail ) ? $sEmail : Services::WpGeneral()->getSiteAdminEmail();
217
- }
218
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/processors/events.php DELETED
@@ -1,94 +0,0 @@
1
- <?php
2
-
3
- use FernleafSystems\Wordpress\Plugin\Shield;
4
- use FernleafSystems\Wordpress\Plugin\Shield\Databases;
5
- use FernleafSystems\Wordpress\Plugin\Shield\Modules\Events;
6
- use FernleafSystems\Wordpress\Plugin\Shield\Modules\Events\ModCon;
7
-
8
- /**
9
- * @deprecated 10.1
10
- */
11
- class ICWP_WPSF_Processor_Events extends Shield\Modules\BaseShield\ShieldProcessor {
12
-
13
- /**
14
- * @var Events\Lib\StatsWriter
15
- */
16
- private $oStatsWriter;
17
-
18
- public function run() {
19
- $this->loadStatsWriter()->setIfCommit( true );
20
- add_action( $this->getCon()->prefix( 'dashboard_widget_content' ), [ $this, 'statsWidget' ], 10 );
21
- }
22
-
23
- /**
24
- * @return Events\Lib\StatsWriter
25
- */
26
- public function loadStatsWriter() {
27
- if ( !isset( $this->oStatsWriter ) ) {
28
- /** @var ModCon $mod */
29
- $mod = $this->getMod();
30
- $this->oStatsWriter = ( new Events\Lib\StatsWriter( $this->getCon() ) )
31
- ->setDbHandler( $mod->getDbHandler_Events() );
32
- }
33
- return $this->oStatsWriter;
34
- }
35
-
36
- public function statsWidget() {
37
- /** @var Databases\Events\Select $oSelEvents */
38
- $oSelEvents = $this->getCon()
39
- ->getModule_Events()
40
- ->getDbHandler_Events()
41
- ->getQuerySelector();
42
-
43
- $aKeyStats = [
44
- 'comments' => [
45
- __( 'Comment Blocks', 'wp-simple-firewall' ),
46
- $oSelEvents->clearWheres()->sumEvents( [
47
- 'spam_block_bot',
48
- 'spam_block_human',
49
- 'spam_block_recaptcha'
50
- ] )
51
- ],
52
- 'firewall' => [
53
- __( 'Firewall Blocks', 'wp-simple-firewall' ),
54
- $oSelEvents->clearWheres()->sumEvent( 'firewall_block' )
55
- ],
56
- 'login_fail' => [
57
- __( 'Login Blocks', 'wp-simple-firewall' ),
58
- $oSelEvents->clearWheres()->sumEvent( 'login_block' )
59
- ],
60
- 'login_verified' => [
61
- __( 'Login Verified', 'wp-simple-firewall' ),
62
- $oSelEvents->clearWheres()->sumEvent( '2fa_success' )
63
- ],
64
- 'session_start' => [
65
- __( 'User Sessions', 'wp-simple-firewall' ),
66
- $oSelEvents->clearWheres()->sumEvent( 'session_start' )
67
- ],
68
- 'ip_killed' => [
69
- __( 'IP Blocks', 'wp-simple-firewall' ),
70
- $oSelEvents->clearWheres()->sumEvent( 'conn_kill' )
71
- ],
72
- 'ip_transgressions' => [
73
- __( 'Total Offenses', 'wp-simple-firewall' ),
74
- $oSelEvents->clearWheres()->sumEvent( 'ip_offense' )
75
- ],
76
- ];
77
-
78
- $aDisplayData = [
79
- 'sHeading' => sprintf( __( '%s Statistics', 'wp-simple-firewall' ), $this->getCon()->getHumanName() ),
80
- 'aKeyStats' => $aKeyStats,
81
- ];
82
-
83
- echo $this->getMod()->renderTemplate(
84
- 'snippets/widget_dashboard_statistics.php',
85
- $aDisplayData
86
- );
87
- }
88
-
89
- public function runDailyCron() {
90
- ( new Events\Consolidate\ConsolidateAllEvents() )
91
- ->setMod( $this->getMod() )
92
- ->run();
93
- }
94
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/processors/firewall.php DELETED
@@ -1,430 +0,0 @@
1
- <?php
2
-
3
- use FernleafSystems\Wordpress\Plugin\Shield\Modules;
4
- use FernleafSystems\Wordpress\Plugin\Shield\Modules\Firewall;
5
- use FernleafSystems\Wordpress\Services\Services;
6
-
7
- /**
8
- * @deprecated 10.1
9
- */
10
- class ICWP_WPSF_Processor_Firewall extends Modules\BaseShield\ShieldProcessor {
11
-
12
- /**
13
- * @var array
14
- */
15
- private $aDieMessage;
16
-
17
- /**
18
- * @var array
19
- */
20
- protected $aPatterns;
21
-
22
- /**
23
- * @var array
24
- */
25
- private $aAuditBlockMessage;
26
-
27
- /**
28
- * After any parameter whitelisting has been accounted for
29
- *
30
- * @var array
31
- */
32
- private $aPageParams;
33
-
34
- public function run() {
35
- if ( $this->getIfPerformFirewallScan() && $this->getIfDoFirewallBlock() ) {
36
- // Hooked here to ensure "plugins_loaded" has completely finished as some mailers aren't init'd.
37
- add_action( 'init', function () {
38
- $this->doPreFirewallBlock();
39
- $this->doFirewallBlock();
40
- }, 0 );
41
- }
42
- }
43
-
44
- private function getIfDoFirewallBlock() :bool {
45
- return !$this->isVisitorRequestPermitted();
46
- }
47
-
48
- private function getIfPerformFirewallScan() :bool {
49
- $bPerformScan = true;
50
- /** @var Modules\Firewall\Options $opts */
51
- $opts = $this->getOptions();
52
-
53
- $sPath = Services::Request()->getPath();
54
-
55
- if ( count( $this->getRawRequestParams() ) == 0 ) {
56
- $bPerformScan = false;
57
- }
58
- elseif ( empty( $sPath ) ) {
59
- $this->getCon()->fireEvent( 'firewall_skip' );
60
- $bPerformScan = false;
61
- }
62
- elseif ( count( $this->getParamsToCheck() ) == 0 ) {
63
- $bPerformScan = false;
64
- }
65
- // TODO: are we calling is_super_admin() too early?
66
- elseif ( $opts->isIgnoreAdmin() && is_super_admin() ) {
67
- $bPerformScan = false;
68
- }
69
-
70
- return $bPerformScan;
71
- }
72
-
73
- private function isVisitorRequestPermitted() :bool {
74
- $opts = $this->getOptions();
75
-
76
- $bRequestIsPermitted = true;
77
- if ( $bRequestIsPermitted && $opts->isOpt( 'block_dir_traversal', 'Y' ) ) {
78
- $bRequestIsPermitted = $this->doPassCheck( 'dirtraversal' );
79
- }
80
- if ( $bRequestIsPermitted && $opts->isOpt( 'block_sql_queries', 'Y' ) ) {
81
- $bRequestIsPermitted = $this->doPassCheck( 'sqlqueries' );
82
- }
83
- if ( $bRequestIsPermitted && $opts->isOpt( 'block_wordpress_terms', 'Y' ) ) {
84
- $bRequestIsPermitted = $this->doPassCheck( 'wpterms' );
85
- }
86
- if ( $bRequestIsPermitted && $opts->isOpt( 'block_field_truncation', 'Y' ) ) {
87
- $bRequestIsPermitted = $this->doPassCheck( 'fieldtruncation' );
88
- }
89
- if ( $bRequestIsPermitted && $opts->isOpt( 'block_php_code', 'Y' ) ) {
90
- $bRequestIsPermitted = $this->doPassCheck( 'phpcode' );
91
- }
92
- if ( $bRequestIsPermitted && $opts->isOpt( 'block_leading_schema', 'Y' ) ) {
93
- $bRequestIsPermitted = $this->doPassCheck( 'schema' );
94
- }
95
- if ( $bRequestIsPermitted && $opts->isOpt( 'block_aggressive', 'Y' ) ) {
96
- $bRequestIsPermitted = $this->doPassCheck( 'aggressive' );
97
- }
98
- if ( $bRequestIsPermitted && $opts->isOpt( 'block_exe_file_uploads', 'Y' ) ) {
99
- $bRequestIsPermitted = $this->doPassCheckBlockExeFileUploads();
100
- }
101
- return $bRequestIsPermitted;
102
- }
103
-
104
- protected function doPassCheckBlockExeFileUploads() :bool {
105
- /** @var Firewall\ModCon $mod */
106
- $mod = $this->getMod();
107
-
108
- $sKey = 'exefile';
109
- $bFAIL = false;
110
- if ( isset( $_FILES ) && !empty( $_FILES ) ) {
111
- $aFileNames = [];
112
- foreach ( $_FILES as $aFile ) {
113
- if ( !empty( $aFile[ 'name' ] ) ) {
114
- $aFileNames[] = $aFile[ 'name' ];
115
- }
116
- }
117
- $aMatchTerms = $this->getFirewallPatterns( 'exefile' );
118
- if ( isset( $aMatchTerms[ 'regex' ] ) && is_array( $aMatchTerms[ 'regex' ] ) ) {
119
-
120
- $aMatchTerms[ 'regex' ] = array_map( [ $this, 'prepRegexTerms' ], $aMatchTerms[ 'regex' ] );
121
- foreach ( $aMatchTerms[ 'regex' ] as $sTerm ) {
122
- foreach ( $aFileNames as $sParam => $mValue ) {
123
- if ( is_scalar( $mValue ) && preg_match( $sTerm, (string)$mValue ) ) {
124
- $bFAIL = true;
125
- break( 2 );
126
- }
127
- }
128
- }
129
- }
130
- if ( $bFAIL ) {
131
- $this->getCon()
132
- ->fireEvent(
133
- 'block_exefile',
134
- [
135
- 'audit' => [
136
- 'blockresponse' => $mod->getBlockResponse(),
137
- 'blockkey' => $sKey,
138
- ]
139
- ]
140
-
141
- );
142
- }
143
- }
144
- return !$bFAIL;
145
- }
146
-
147
- /**
148
- * Returns false when check fails - that is, it should be blocked by the firewall.
149
- *
150
- * @param string $sBlockKey
151
- * @return bool
152
- */
153
- private function doPassCheck( string $sBlockKey ) :bool {
154
- /** @var Firewall\ModCon $mod */
155
- $mod = $this->getMod();
156
-
157
- $aMatchTerms = $this->getFirewallPatterns( $sBlockKey );
158
- $aParamValues = $this->getParamsToCheck();
159
- if ( empty( $aMatchTerms ) || empty( $aParamValues ) ) {
160
- return true;
161
- }
162
-
163
- $sParam = '';
164
- $mValue = '';
165
-
166
- $bFAIL = false;
167
- if ( isset( $aMatchTerms[ 'simple' ] ) && is_array( $aMatchTerms[ 'simple' ] ) ) {
168
-
169
- foreach ( $aMatchTerms[ 'simple' ] as $sTerm ) {
170
- foreach ( $aParamValues as $sParam => $mValue ) {
171
- if ( is_scalar( $mValue ) && ( stripos( (string)$mValue, $sTerm ) !== false ) ) {
172
- $bFAIL = true;
173
- break( 2 );
174
- }
175
- }
176
- }
177
- }
178
-
179
- if ( !$bFAIL && isset( $aMatchTerms[ 'regex' ] ) && is_array( $aMatchTerms[ 'regex' ] ) ) {
180
- $aMatchTerms[ 'regex' ] = array_map( [ $this, 'prepRegexTerms' ], $aMatchTerms[ 'regex' ] );
181
- foreach ( $aMatchTerms[ 'regex' ] as $sTerm ) {
182
- foreach ( $aParamValues as $sParam => $mValue ) {
183
- if ( is_scalar( $mValue ) && preg_match( $sTerm, (string)$mValue ) ) {
184
- $sParam = sanitize_text_field( $sParam );
185
- $mValue = sanitize_text_field( $mValue );
186
- $bFAIL = true;
187
- break( 2 );
188
- }
189
- }
190
- }
191
- }
192
-
193
- if ( $bFAIL ) {
194
- $this->addToFirewallDieMessage( __( "Something in the URL, Form or Cookie data wasn't appropriate.", 'wp-simple-firewall' ) );
195
-
196
- $this->aAuditBlockMessage = [
197
- sprintf( __( 'Firewall Trigger: %s.', 'wp-simple-firewall' ), $this->getFirewallBlockKeyName( $sBlockKey ) ),
198
- __( 'Page parameter failed firewall check.', 'wp-simple-firewall' ),
199
- sprintf( __( 'The offending parameter was "%s" with a value of "%s".', 'wp-simple-firewall' ), $sParam, $mValue )
200
- ];
201
-
202
- $this->getCon()
203
- ->fireEvent(
204
- 'blockparam_'.$sBlockKey,
205
- [
206
- 'audit' => [
207
- 'param' => $sParam,
208
- 'val' => $mValue,
209
- 'blockresponse' => $mod->getBlockResponse(),
210
- 'blockkey' => $sBlockKey,
211
- ]
212
- ]
213
- );
214
- }
215
-
216
- return !$bFAIL;
217
- }
218
-
219
- /**
220
- * @param string $sKey
221
- * @return array|null
222
- */
223
- protected function getFirewallPatterns( $sKey = null ) {
224
- if ( !isset( $this->aPatterns ) ) {
225
- $this->aPatterns = $this->getOptions()->getDef( 'firewall_patterns' );
226
- }
227
- if ( !empty( $sKey ) ) {
228
- return isset( $this->aPatterns[ $sKey ] ) ? $this->aPatterns[ $sKey ] : null;
229
- }
230
- return $this->aPatterns;
231
- }
232
-
233
- /**
234
- * @param string $sTerm
235
- * @return string
236
- */
237
- private function prepRegexTerms( $sTerm ) {
238
- return '/'.$sTerm.'/i';
239
- }
240
-
241
- private function doPreFirewallBlock() {
242
- /** @var Modules\Firewall\Options $opts */
243
- $opts = $this->getOptions();
244
- if ( $opts->isSendBlockEmail() ) {
245
- $recipient = $this->getMod()->getPluginReportEmail();
246
- $this->getCon()->fireEvent(
247
- $this->sendBlockEmail( $recipient ) ? 'fw_email_success' : 'fw_email_fail',
248
- [ 'audit' => [ 'recipient' => $recipient ] ]
249
- );
250
- }
251
- $this->getCon()->fireEvent( 'firewall_block' );
252
- }
253
-
254
- private function doFirewallBlock() {
255
- /** @var Firewall\ModCon $mod */
256
- $mod = $this->getMod();
257
-
258
- switch ( $mod->getBlockResponse() ) {
259
- case 'redirect_die':
260
- Services::WpGeneral()->wpDie( 'Firewall Triggered' );
261
- break;
262
- case 'redirect_die_message':
263
- Services::WpGeneral()->wpDie( $this->getFirewallDieMessageForDisplay() );
264
- break;
265
- case 'redirect_home':
266
- Services::Response()->redirectToHome();
267
- break;
268
- case 'redirect_404':
269
- header( 'Cache-Control: no-store, no-cache' );
270
- Services::WpGeneral()->turnOffCache();
271
- Services::Response()->sendApache404();
272
- break;
273
- default:
274
- break;
275
- }
276
- die();
277
- }
278
-
279
- protected function getFirewallDieMessage() :array {
280
- if ( !isset( $this->aDieMessage ) || !is_array( $this->aDieMessage ) ) {
281
- $this->aDieMessage = [ $this->getMod()->getTextOpt( 'text_firewalldie' ) ];
282
- }
283
- return $this->aDieMessage;
284
- }
285
-
286
- protected function getFirewallDieMessageForDisplay() :string {
287
- $messages = apply_filters(
288
- $this->getCon()->prefix( 'firewall_die_message' ),
289
- $this->getFirewallDieMessage()
290
- );
291
- return implode( ' ', is_array( $messages ) ? $messages : [] );
292
- }
293
-
294
- /**
295
- * @param string $sMessagePart
296
- * @return $this
297
- */
298
- protected function addToFirewallDieMessage( $sMessagePart ) {
299
- $aMessages = $this->getFirewallDieMessage();
300
- $aMessages[] = $sMessagePart;
301
- $this->aDieMessage = $aMessages;
302
- return $this;
303
- }
304
-
305
- private function getParamsToCheck() :array {
306
- if ( isset( $this->aPageParams ) ) {
307
- return $this->aPageParams;
308
- }
309
-
310
- /** @var Modules\Firewall\Options $opts */
311
- $opts = $this->getOptions();
312
-
313
- $this->aPageParams = $this->getRawRequestParams();
314
- $aWhitelist = Services::DataManipulation()
315
- ->mergeArraysRecursive( $opts->getDef( 'default_whitelist' ), $opts->getCustomWhitelist() );
316
-
317
- // first we remove globally whitelisted request parameters
318
- if ( !empty( $aWhitelist[ '*' ] ) && is_array( $aWhitelist[ '*' ] ) ) {
319
- foreach ( $aWhitelist[ '*' ] as $sWhitelistParam ) {
320
-
321
- if ( preg_match( '#^/.+/$#', $sWhitelistParam ) ) {
322
- foreach ( array_keys( $this->aPageParams ) as $sParamKey ) {
323
- if ( preg_match( $sWhitelistParam, $sParamKey ) ) {
324
- unset( $this->aPageParams[ $sParamKey ] );
325
- }
326
- }
327
- }
328
- elseif ( isset( $this->aPageParams[ $sWhitelistParam ] ) ) {
329
- unset( $this->aPageParams[ $sWhitelistParam ] );
330
- }
331
- }
332
- }
333
-
334
- // If the parameters to check is already empty, we return it to save any further processing.
335
- if ( empty( $this->aPageParams ) ) {
336
- return $this->aPageParams;
337
- }
338
-
339
- // Now we run through the list of whitelist pages
340
- $sRequestPage = Services::Request()->getPath();
341
- foreach ( $aWhitelist as $sWhitelistPageName => $aWhitelistPageParams ) {
342
-
343
- // if the page is white listed
344
- if ( strpos( $sRequestPage, $sWhitelistPageName ) !== false ) {
345
-
346
- // if the page has no particular parameters specified there is nothing to check since the whole page is white listed.
347
- if ( empty( $aWhitelistPageParams ) ) {
348
- $this->aPageParams = [];
349
- }
350
- else {
351
- // Otherwise we run through any whitelisted parameters and remove them.
352
- foreach ( $aWhitelistPageParams as $sWhitelistParam ) {
353
- if ( array_key_exists( $sWhitelistParam, $this->aPageParams ) ) {
354
- unset( $this->aPageParams[ $sWhitelistParam ] );
355
- }
356
- }
357
- }
358
- break;
359
- }
360
- }
361
-
362
- return $this->aPageParams;
363
- }
364
-
365
- private function getRawRequestParams() :array {
366
- return Services::Request()->getRawRequestParams( $this->getOptions()->isOpt( 'include_cookie_checks', 'Y' ) );
367
- }
368
-
369
- private function sendBlockEmail( string $recipient ) :bool {
370
- $bSuccess = false;
371
- if ( !empty( $this->aAuditBlockMessage ) ) {
372
- $sIp = Services::IP()->getRequestIp();
373
- $aMessage = array_merge(
374
- [
375
- sprintf( __( '%s has blocked a page visit to your site.', 'wp-simple-firewall' ), $this->getCon()
376
- ->getHumanName() ),
377
- __( 'Log details for this visitor are below:', 'wp-simple-firewall' ),
378
- '- '.sprintf( '%s: %s', __( 'IP Address', 'wp-simple-firewall' ), $sIp ),
379
- ],
380
- array_map(
381
- function ( $sLine ) {
382
- return '- '.$sLine;
383
- },
384
- $this->aAuditBlockMessage
385
- ),
386
- [
387
- '',
388
- sprintf( __( 'You can look up the offending IP Address here: %s', 'wp-simple-firewall' ), 'http://ip-lookup.net/?ip='.$sIp )
389
- ]
390
- );
391
-
392
- $bSuccess = $this->getEmailProcessor()
393
- ->sendEmailWithWrap( $recipient, __( 'Firewall Block Alert', 'wp-simple-firewall' ), $aMessage );
394
- }
395
- return $bSuccess;
396
- }
397
-
398
- private function getFirewallBlockKeyName( string $blockKey ) :string {
399
- switch ( $blockKey ) {
400
- case 'dirtraversal':
401
- $name = __( 'Directory Traversal', 'wp-simple-firewall' );
402
- break;
403
- case 'wpterms':
404
- $name = __( 'WordPress Terms', 'wp-simple-firewall' );
405
- break;
406
- case 'fieldtruncation':
407
- $name = __( 'Field Truncation', 'wp-simple-firewall' );
408
- break;
409
- case 'sqlqueries':
410
- $name = __( 'SQL Queries', 'wp-simple-firewall' );
411
- break;
412
- case 'exefile':
413
- $name = __( 'EXE File Uploads', 'wp-simple-firewall' );
414
- break;
415
- case 'schema':
416
- $name = __( 'Leading Schema', 'wp-simple-firewall' );
417
- break;
418
- case 'phpcode':
419
- $name = __( 'PHP Code', 'wp-simple-firewall' );
420
- break;
421
- case 'aggressive':
422
- $name = __( 'Aggressive Rules', 'wp-simple-firewall' );
423
- break;
424
- default:
425
- $name = __( 'Unknown Rules', 'wp-simple-firewall' );
426
- break;
427
- }
428
- return $name;
429
- }
430
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/processors/hack_protect.php DELETED
@@ -1,26 +0,0 @@
1
- <?php
2
-
3
- use FernleafSystems\Wordpress\Plugin\Shield;
4
- use FernleafSystems\Wordpress\Plugin\Shield\Databases;
5
- use FernleafSystems\Wordpress\Plugin\Shield\Modules;
6
- use FernleafSystems\Wordpress\Plugin\Shield\Modules\HackGuard;
7
-
8
- /**
9
- * @deprecated 10.1
10
- */
11
- class ICWP_WPSF_Processor_HackProtect extends Modules\BaseShield\ShieldProcessor {
12
-
13
- public function run() {
14
- die('hasdf');
15
- }
16
-
17
- public function getSubProScanner() :\ICWP_WPSF_Processor_HackProtect_Scanner {
18
- return $this->getSubPro( 'scanner' );
19
- }
20
-
21
- protected function getSubProMap() :array {
22
- return [
23
- 'scanner' => 'ICWP_WPSF_Processor_HackProtect_Scanner',
24
- ];
25
- }
26
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/processors/hackprotect_integrity.php DELETED
@@ -1,136 +0,0 @@
1
- <?php
2
-
3
- use FernleafSystems\Wordpress\Plugin\Shield\Modules\BaseShield\ShieldProcessor;
4
- use FernleafSystems\Wordpress\Plugin\Shield\Modules\HackGuard;
5
- use FernleafSystems\Wordpress\Services\Services;
6
-
7
- class ICWP_WPSF_Processor_HackProtect_Integrity extends ShieldProcessor {
8
-
9
- public function run() {
10
- $this->setupSnapshots();
11
- add_action( 'user_register', [ $this, 'snapshotUsers' ] );
12
- add_action( 'profile_update', [ $this, 'snapshotUsers' ] );
13
- add_action( 'after_password_reset', [ $this, 'snapshotUsers' ] );
14
- }
15
-
16
- /**
17
- * @return array[] - associative arrays where keys are $this->getStandardUserFields()
18
- */
19
- public function getSnapshotUsers() {
20
- $aUs = $this->getOptions()->getOpt( 'snapshot_users' );
21
- return is_array( $aUs ) ? $aUs : [];
22
- }
23
-
24
- /**
25
- * @return array
26
- */
27
- public function getStandardUserFields() {
28
- return [ 'user_login', 'user_email', 'user_pass' ];
29
- }
30
-
31
- /**
32
- * @return bool
33
- */
34
- public function hasSnapshotUsers() {
35
- return ( count( $this->getSnapshotUsers() ) > 0 );
36
- }
37
-
38
- protected function setupSnapshots() {
39
- $this->snapshotUsers();
40
- }
41
-
42
- protected function verifyUsers() {
43
- $aSnapshot = $this->getSnapshotUsers();
44
- $aFieldsToCheck = $this->getStandardUserFields();
45
-
46
- foreach ( Services::WpUsers()->getAllUsers() as $oUser ) {
47
-
48
- if ( !array_key_exists( $oUser->ID, $aSnapshot ) ) {
49
- // Unrecognised user ID exists.
50
- $this->deleteUserById( $oUser->ID );
51
- }
52
- else {
53
- $aSnapUser = $aSnapshot[ $oUser->ID ];
54
- $bAltered = false;
55
- foreach ( $aFieldsToCheck as $sField ) {
56
- if ( $aSnapUser[ $sField ] != $oUser->get( $sField ) ) { //Field has been altered
57
- $bAltered = true;
58
- }
59
- }
60
-
61
- if ( $bAltered ) {
62
- $this->resetUserToSnapshot( $oUser->ID );
63
- }
64
- }
65
- }
66
- }
67
-
68
- /**
69
- * @param int $nId
70
- * @return bool
71
- */
72
- public function deleteUserById( $nId ) {
73
- $oDb = Services::WpDb();
74
- return $oDb->deleteRowsFromTableWhere(
75
- $oDb->getTable_Users(),
76
- [ 'ID' => $nId ]
77
- ) > 0;
78
- }
79
-
80
- /**
81
- * @param int $nId
82
- * @return bool
83
- */
84
- public function resetUserToSnapshot( $nId ) {
85
- $aSnapshot = $this->getSnapshotUsers();
86
- $aUser = $aSnapshot[ $nId ];
87
-
88
- $oDb = Services::WpDb();
89
- return $oDb->updateRowsFromTableWhere(
90
- $oDb->getTable_Users(),
91
- $aUser,
92
- [ 'ID' => $nId ]
93
- ) > 0;
94
- }
95
-
96
- /**
97
- * Guarded: Only ever snapshots when option is enabled.
98
- *
99
- * @param bool $bUpdate
100
- * @return $this
101
- */
102
- public function snapshotUsers( $bUpdate = false ) {
103
-
104
- if ( $bUpdate || !$this->hasSnapshotUsers() ) {
105
-
106
- $aUsersToStore = [];
107
- $aFields = $this->getStandardUserFields();
108
- foreach ( Services::WpUsers()->getAllUsers() as $oUser ) {
109
-
110
- $aUserData = [];
111
- foreach ( $aFields as $sField ) {
112
- $aUserData[ $sField ] = $oUser->get( $sField );
113
- }
114
- $aUsersToStore[ $oUser->ID ] = $aUserData;
115
- }
116
- // store snapshot users
117
- }
118
- return $this;
119
- }
120
-
121
- /**
122
- * Cron callback
123
- */
124
- public function runCron() {
125
- $this->verifyUsers();
126
- }
127
-
128
- /**
129
- * @return int
130
- */
131
- protected function getCronFrequency() {
132
- /** @var HackGuard\Options $oOpts */
133
- $oOpts = $this->getOptions();
134
- return $oOpts->getScanFrequency();
135
- }
136
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/processors/hackprotect_scan_apc.php DELETED
@@ -1,11 +0,0 @@
1
- <?php
2
-
3
- use FernleafSystems\Wordpress\Plugin\Shield;
4
-
5
- /**
6
- * @deprecated 10.1
7
- */
8
- class ICWP_WPSF_Processor_HackProtect_Apc extends ICWP_WPSF_Processor_ScanBase {
9
-
10
- const SCAN_SLUG = 'apc';
11
- }
 
 
 
 
 
 
 
 
 
 
 
src/processors/hackprotect_scan_base.php DELETED
@@ -1,46 +0,0 @@
1
- <?php
2
-
3
- use FernleafSystems\Wordpress\Plugin\Shield;
4
- use FernleafSystems\Wordpress\Plugin\Shield\Modules\HackGuard;
5
- use FernleafSystems\Wordpress\Services\Services;
6
-
7
- /**
8
- * @deprecated 10.1
9
- */
10
- abstract class ICWP_WPSF_Processor_ScanBase extends Shield\Modules\BaseShield\ShieldProcessor {
11
-
12
- use Shield\Scans\Common\ScanActionConsumer;
13
-
14
- const SCAN_SLUG = 'base';
15
-
16
- /**
17
- * @param int $nDelay
18
- * @deprecated 10.1
19
- */
20
- public function scheduleOnDemandScan( $nDelay = 3 ) {
21
- }
22
-
23
- /**
24
- * @return Shield\Scans\Base\BaseScanActionVO|mixed
25
- * @deprecated 10.1
26
- */
27
- public function getScanActionVO() {
28
- return $this->getThisScanCon()->getScanActionVO();
29
- }
30
-
31
- /**
32
- * @return HackGuard\Scan\Controller\Base|mixed
33
- * @deprecated 10.1
34
- */
35
- protected function getThisScanCon() {
36
- /** @var HackGuard\ModCon $mod */
37
- $mod = $this->getMod();
38
- return $mod->getScanCon( static::SCAN_SLUG );
39
- }
40
-
41
- /**
42
- * @deprecated 10.1
43
- */
44
- public function hookOnDemandScan() {
45
- }
46
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/processors/hackprotect_scan_mal.php DELETED
@@ -1,11 +0,0 @@
1
- <?php
2
-
3
- use FernleafSystems\Wordpress\Plugin\Shield;
4
-
5
- /**
6
- * @deprecated 10.1
7
- */
8
- class ICWP_WPSF_Processor_HackProtect_Mal extends ICWP_WPSF_Processor_ScanBase {
9
-
10
- const SCAN_SLUG = 'mal';
11
- }
 
 
 
 
 
 
 
 
 
 
 
src/processors/hackprotect_scan_ptg.php DELETED
@@ -1,25 +0,0 @@
1
- <?php
2
-
3
- use FernleafSystems\Wordpress\Plugin\Shield;
4
- use FernleafSystems\Wordpress\Plugin\Shield\Modules\HackGuard;
5
- use FernleafSystems\Wordpress\Services;
6
-
7
- /**
8
- * @deprecated 10.1
9
- */
10
- class ICWP_WPSF_Processor_HackProtect_Ptg extends ICWP_WPSF_Processor_ScanBase {
11
-
12
- const SCAN_SLUG = 'ptg';
13
-
14
- /**
15
- * @param array $aLinks
16
- * @param string $sPluginFile
17
- * @return string[]
18
- */
19
- public function addActionLinkRefresh( $aLinks, $sPluginFile ) {
20
- return $aLinks;
21
- }
22
-
23
- public function printPluginReinstallDialogs() {
24
- }
25
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/processors/hackprotect_scan_ufc.php DELETED
@@ -1,11 +0,0 @@
1
- <?php
2
-
3
- use FernleafSystems\Wordpress\Plugin\Shield;
4
-
5
- /**
6
- * @deprecated 10.1
7
- */
8
- class ICWP_WPSF_Processor_HackProtect_Ufc extends ICWP_WPSF_Processor_ScanBase {
9
-
10
- const SCAN_SLUG = 'ufc';
11
- }
 
 
 
 
 
 
 
 
 
 
 
src/processors/hackprotect_scan_wcf.php DELETED
@@ -1,11 +0,0 @@
1
- <?php
2
-
3
- use FernleafSystems\Wordpress\Plugin\Shield;
4
-
5
- /**
6
- * @deprecated 10.1
7
- */
8
- class ICWP_WPSF_Processor_HackProtect_Wcf extends ICWP_WPSF_Processor_ScanBase {
9
-
10
- const SCAN_SLUG = 'wcf';
11
- }
 
 
 
 
 
 
 
 
 
 
 
src/processors/hackprotect_scan_wpv.php DELETED
@@ -1,74 +0,0 @@
1
- <?php
2
-
3
- use FernleafSystems\Wordpress\Plugin\Shield;
4
-
5
- /**
6
- * @deprecated 10.1
7
- */
8
- class ICWP_WPSF_Processor_HackProtect_Wpv extends ICWP_WPSF_Processor_ScanBase {
9
-
10
- const SCAN_SLUG = 'wpv';
11
-
12
- /**
13
- * @param bool $bDoAutoUpdate
14
- * @param \stdClass|string $mItem
15
- * @return bool
16
- */
17
- public function autoupdateVulnerablePlugins( $bDoAutoUpdate, $mItem ) {
18
- return $bDoAutoUpdate;
19
- }
20
-
21
- /**
22
- * @param array $aColumns
23
- * @return array
24
- */
25
- public function fCountColumns( $aColumns ) {
26
- return $aColumns;
27
- }
28
-
29
- public function addPluginVulnerabilityRows() {
30
- }
31
-
32
- public function addVulnerablePluginStatusView() {
33
- }
34
-
35
- /**
36
- * FILTER
37
- * @param array $aViews
38
- * @return array
39
- */
40
- public function addPluginsStatusViewLink( $aViews ) {
41
- return $aViews;
42
- }
43
-
44
- /**
45
- * FILTER
46
- * @param array $aPlugins
47
- * @return array
48
- */
49
- public function filterPluginsToView( $aPlugins ) {
50
- return $aPlugins;
51
- }
52
-
53
- /**
54
- * @param string $sPluginFile
55
- * @param array $aPluginData
56
- */
57
- public function attachVulnerabilityWarning( $sPluginFile, $aPluginData ) {
58
- }
59
-
60
- /**
61
- * @param string $sFile
62
- * @return Shield\Scans\Wpv\WpVulnDb\WpVulnVO[]
63
- */
64
- private function getPluginVulnerabilities( $sFile ) {
65
- return [];
66
- }
67
-
68
- /**
69
- * @return bool
70
- */
71
- private function countVulnerablePlugins() {
72
- return 0;
73
- }
74
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/processors/hackprotect_scanner.php DELETED
@@ -1,89 +0,0 @@
1
- <?php
2
-
3
- use FernleafSystems\Wordpress\Plugin\Shield;
4
- use FernleafSystems\Wordpress\Plugin\Shield\Modules\BaseShield;
5
- use FernleafSystems\Wordpress\Plugin\Shield\Modules\HackGuard;
6
- use FernleafSystems\Wordpress\Services\Services;
7
-
8
- /**
9
- * @deprecated 10.1
10
- */
11
- class ICWP_WPSF_Processor_HackProtect_Scanner extends BaseShield\ShieldProcessor {
12
-
13
- use Shield\Crons\StandardCron;
14
-
15
- public function run() {
16
- }
17
-
18
- public function getSubProcessorPtg() :\ICWP_WPSF_Processor_HackProtect_Ptg {
19
- return $this->getSubPro( 'ptg' );
20
- }
21
-
22
- protected function getSubProMap() :array {
23
- return [];
24
- }
25
-
26
- private function handlePostScanCron() {
27
- }
28
-
29
- private function runAutoRepair() {
30
- }
31
-
32
- public function runHourlyCron() {
33
- }
34
-
35
- public function runDailyCron() {
36
- }
37
-
38
- public function onWpLoaded() {
39
- }
40
-
41
- public function onModuleShutdown() {
42
- }
43
-
44
- /**
45
- * Cron callback
46
- */
47
- public function runCron() {
48
- }
49
-
50
- private function cronScan() {
51
- }
52
-
53
- public function getReasonsScansCantExecute() :array {
54
- return [];
55
- }
56
-
57
- public function getCanScansExecute() :bool {
58
- }
59
-
60
- protected function getCronFrequency() {
61
- /** @var Shield\Modules\HackGuard\Options $opts */
62
- $opts = $this->getOptions();
63
- return $opts->getScanFrequency();
64
- }
65
-
66
- public function getFirstRunTimestamp() :int {
67
- $c = Services::Request()->carbon( true );
68
- $c->addHours( $c->minute < 40 ? 0 : 1 )
69
- ->minute( $c->minute < 40 ? 45 : 15 )
70
- ->second( 0 );
71
-
72
- if ( $this->getCronFrequency() === 1 ) { // If it's a daily scan only, set to 3am by default
73
- $hour = (int)apply_filters( $this->getCon()->prefix( 'daily_scan_cron_hour' ), 3 );
74
- if ( $hour < 0 || $hour > 23 ) {
75
- $hour = 3;
76
- }
77
- if ( $c->hour >= $hour ) {
78
- $c->addDays( 1 );
79
- }
80
- $c->hour( $hour );
81
- }
82
-
83
- return $c->timestamp;
84
- }
85
-
86
- protected function getCronName() :string {
87
- return $this->getCon()->prefix( $this->getOptions()->getDef( 'cron_all_scans' ) );
88
- }
89
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/processors/headers.php DELETED
@@ -1,230 +0,0 @@
1
- <?php
2
-
3
- use FernleafSystems\Wordpress\Plugin\Shield\Modules;
4
- use FernleafSystems\Wordpress\Plugin\Shield\Modules\Headers;
5
-
6
- /**
7
- * Class ICWP_WPSF_Processor_Lockdown
8
- * @deprecated 10.1
9
- */
10
- class ICWP_WPSF_Processor_Headers extends Modules\BaseShield\ShieldProcessor {
11
-
12
- /**
13
- * @var bool
14
- */
15
- private $bHeadersPushed;
16
-
17
- /**
18
- * @var array
19
- */
20
- private $aHeaders;
21
-
22
- public function run() {
23
- if ( $this->getPushHeadersEarly() ) {
24
- $this->sendHeaders();
25
- }
26
- else {
27
- add_filter( 'wp_headers', [ $this, 'addToHeaders' ], PHP_INT_MAX );
28
- add_action( 'send_headers', [ $this, 'sendHeaders' ], PHP_INT_MAX, 0 );
29
- }
30
- }
31
-
32
- /**
33
- * @return bool
34
- */
35
- protected function getPushHeadersEarly() {
36
- return defined( 'WPCACHEHOME' ); //WP Super Cache
37
- }
38
-
39
- /**
40
- * Tries to ensure duplicate headers are not sent. Previously sent/supplied headers take priority.
41
- * @param array $aCurrentWpHeaders
42
- * @return array
43
- */
44
- public function addToHeaders( $aCurrentWpHeaders ) {
45
- if ( !$this->isHeadersPushed() ) {
46
- $aAlreadySentHeaders = array_map(
47
- function ( $sHeader ) {
48
- return strtolower( trim( $sHeader ) );
49
- },
50
- ( is_array( $aCurrentWpHeaders ) ? array_keys( $aCurrentWpHeaders ) : [] )
51
- );
52
- foreach ( $this->gatherSecurityHeaders() as $sHeader => $sValue ) {
53
- if ( !in_array( strtolower( $sHeader ), $aAlreadySentHeaders ) ) {
54
- $aCurrentWpHeaders[ $sHeader ] = $sValue;
55
- }
56
- }
57
- $this->setHeadersPushed( true );
58
- }
59
- return $aCurrentWpHeaders;
60
- }
61
-
62
- /**
63
- * Tries to ensure duplicate headers are not sent.
64
- */
65
- public function sendHeaders() {
66
- if ( !$this->isHeadersPushed() ) {
67
- $aAlreadySent = array_map( 'strtolower', array_keys( $this->getAlreadySentHeaders() ) );
68
- foreach ( $this->gatherSecurityHeaders() as $sName => $sValue ) {
69
- if ( !in_array( strtolower( $sName ), $aAlreadySent ) ) {
70
- @header( sprintf( '%s: %s', $sName, $sValue ) );
71
- }
72
- }
73
- $this->setHeadersPushed( true );
74
- }
75
- }
76
-
77
- /**
78
- * @return string[] - array of all previously sent headers. Keys are header names, values are header values.
79
- */
80
- private function getAlreadySentHeaders() {
81
- $aHeaders = [];
82
-
83
- if ( function_exists( 'headers_list' ) ) {
84
- $aSent = headers_list();
85
- if ( is_array( $aSent ) ) {
86
- foreach ( $aSent as $sHeader ) {
87
- if ( strpos( $sHeader, ':' ) ) {
88
- list( $sKey, $sValue ) = array_map( 'trim', explode( ':', $sHeader, 2 ) );
89
- $aHeaders[ $sKey ] = $sValue;
90
- }
91
- }
92
- }
93
- }
94
-
95
- return $aHeaders;
96
- }
97
-
98
- /**
99
- * @return array|null
100
- */
101
- private function getXFrameHeader() {
102
- switch ( $this->getOptions()->getOpt( 'x_frame' ) ) {
103
- case 'on_sameorigin':
104
- $sXFrameOption = 'SAMEORIGIN';
105
- break;
106
- case 'on_deny':
107
- $sXFrameOption = 'DENY';
108
- break;
109
- default:
110
- $sXFrameOption = '';
111
- break;
112
- }
113
- return !empty( $sXFrameOption ) ? [ 'x-frame-options' => $sXFrameOption ] : null;
114
- }
115
-
116
- /**
117
- * @return array
118
- */
119
- private function getXssProtectionHeader() {
120
- return [ 'X-XSS-Protection' => '1; mode=block' ];
121
- }
122
-
123
- /**
124
- * @return array
125
- */
126
- private function getContentTypeOptionHeader() {
127
- return [ 'X-Content-Type-Options' => 'nosniff' ];
128
- }
129
-
130
- /**
131
- * @return array|null
132
- */
133
- private function getReferrerPolicyHeader() {
134
- /** @var Headers\Options $oOpts */
135
- $oOpts = $this->getOptions();
136
- return [ 'Referrer-Policy' => $oOpts->getReferrerPolicyValue() ];
137
- }
138
-
139
- /**
140
- * @return array|null
141
- */
142
- private function setContentSecurityPolicyHeader() {
143
- /** @var Headers\Options $oOpts */
144
- $oOpts = $this->getOptions();
145
-
146
- $aDefaultSrcDirectives = [];
147
-
148
- if ( $oOpts->isOpt( 'xcsp_self', 'Y' ) ) {
149
- $aDefaultSrcDirectives[] = "'self'";
150
- }
151
- if ( $oOpts->isOpt( 'xcsp_data', 'Y' ) ) {
152
- $aDefaultSrcDirectives[] = "data:";
153
- }
154
- if ( $oOpts->isOpt( 'xcsp_inline', 'Y' ) ) {
155
- $aDefaultSrcDirectives[] = "'unsafe-inline'";
156
- }
157
- if ( $oOpts->isOpt( 'xcsp_eval', 'Y' ) ) {
158
- $aDefaultSrcDirectives[] = "'unsafe-eval'";
159
- }
160
- if ( $oOpts->isOpt( 'xcsp_https', 'Y' ) ) {
161
- $aDefaultSrcDirectives[] = "https:";
162
- }
163
-
164
- $aDefaultSrcDirectives[] = implode( " ", $oOpts->getOpt( 'xcsp_hosts', [] ) );
165
-
166
- $aRules = $oOpts->getCspCustomRules();
167
- array_unshift( $aRules, sprintf( 'default-src %s;', implode( " ", $aDefaultSrcDirectives ) ) );
168
- return [ 'Content-Security-Policy' => implode( ' ', $aRules ) ];
169
- }
170
-
171
- /**
172
- * @return array
173
- */
174
- private function gatherSecurityHeaders() {
175
- /** @var Headers\Options $oOpts */
176
- $oOpts = $this->getOptions();
177
-
178
- if ( $oOpts->isReferrerPolicyEnabled() ) {
179
- $this->addHeader( $this->getReferrerPolicyHeader() );
180
- }
181
- if ( $oOpts->isEnabledXFrame() ) {
182
- $this->addHeader( $this->getXFrameHeader() );
183
- }
184
- if ( $oOpts->isEnabledXssProtection() ) {
185
- $this->addHeader( $this->getXssProtectionHeader() );
186
- }
187
- if ( $oOpts->isEnabledContentTypeHeader() ) {
188
- $this->addHeader( $this->getContentTypeOptionHeader() );
189
- }
190
- if ( $oOpts->isEnabledContentSecurityPolicy() ) {
191
- $this->addHeader( $this->setContentSecurityPolicyHeader() );
192
- }
193
- return $this->getHeaders();
194
- }
195
-
196
- /**
197
- * @return array
198
- */
199
- private function getHeaders() {
200
- if ( !isset( $this->aHeaders ) || !is_array( $this->aHeaders ) ) {
201
- $this->aHeaders = [];
202
- }
203
- return array_unique( $this->aHeaders );
204
- }
205
-
206
- /**
207
- * @param array $aHeader
208
- */
209
- private function addHeader( $aHeader ) {
210
- if ( !empty( $aHeader ) && is_array( $aHeader ) ) {
211
- $this->aHeaders = array_merge( $this->getHeaders(), $aHeader );
212
- }
213
- }
214
-
215
- /**
216
- * @return bool
217
- */
218
- private function isHeadersPushed() {
219
- return (bool)$this->bHeadersPushed;
220
- }
221
-
222
- /**
223
- * @param bool $bHeadersPushed
224
- * @return $this
225
- */
226
- private function setHeadersPushed( $bHeadersPushed ) {
227
- $this->bHeadersPushed = $bHeadersPushed;
228
- return $this;
229
- }
230
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/processors/lockdown.php DELETED
@@ -1,136 +0,0 @@
1
- <?php
2
-
3
- use FernleafSystems\Wordpress\Plugin\Shield\Modules;
4
- use FernleafSystems\Wordpress\Plugin\Shield\Modules\Lockdown;
5
- use FernleafSystems\Wordpress\Services\Services;
6
-
7
- /**
8
- * @deprecated 10.1
9
- */
10
- class ICWP_WPSF_Processor_Lockdown extends Modules\BaseShield\ShieldProcessor {
11
-
12
- public function run() {
13
- /** @var Lockdown\Options $opts */
14
- $opts = $this->getOptions();
15
-
16
- if ( $opts->isOptFileEditingDisabled() ) {
17
- $this->blockFileEditing();
18
- }
19
-
20
- if ( $opts->isOpt( 'force_ssl_admin', 'Y' ) && function_exists( 'force_ssl_admin' ) ) {
21
- if ( !defined( 'FORCE_SSL_ADMIN' ) ) {
22
- define( 'FORCE_SSL_ADMIN', true );
23
- }
24
- force_ssl_admin( true );
25
- }
26
-
27
- if ( $opts->isOpt( 'hide_wordpress_generator_tag', 'Y' ) ) {
28
- remove_action( 'wp_head', 'wp_generator' );
29
- }
30
-
31
- if ( $opts->isOpt( 'clean_wp_rubbish', 'Y' ) ) {
32
- ( new Lockdown\Lib\CleanRubbish() )
33
- ->setMod( $this->getMod() )
34
- ->execute();
35
- }
36
-
37
- if ( $opts->isXmlrpcDisabled() ) {
38
- add_filter( 'xmlrpc_enabled', [ $this, 'disableXmlrpc' ], 1000, 0 );
39
- add_filter( 'xmlrpc_methods', [ $this, 'disableXmlrpc' ], 1000, 0 );
40
- }
41
- }
42
-
43
- private function blockFileEditing() {
44
- if ( !defined( 'DISALLOW_FILE_EDIT' ) ) {
45
- define( 'DISALLOW_FILE_EDIT', true );
46
- }
47
-
48
- add_filter( 'user_has_cap',
49
- /**
50
- * @param array $aAllCaps
51
- * @param array $cap
52
- * @param array $aArgs
53
- * @return array
54
- */
55
- function ( $aAllCaps, $cap, $aArgs ) {
56
- $sRequestedCapability = $aArgs[ 0 ];
57
- if ( in_array( $sRequestedCapability, [ 'edit_themes', 'edit_plugins', 'edit_files' ] ) ) {
58
- $aAllCaps[ $sRequestedCapability ] = false;
59
- }
60
- return $aAllCaps;
61
- },
62
- PHP_INT_MAX, 3
63
- );
64
- }
65
-
66
- public function onWpInit() {
67
- /** @var Lockdown\Options $opts */
68
- $opts = $this->getOptions();
69
-
70
- if ( !Services::WpUsers()->isUserLoggedIn() ) {
71
- $this->interceptCanonicalRedirects();
72
- if ( $opts->isRestApiAnonymousAccessDisabled() ) {
73
- add_filter( 'rest_authentication_errors', [ $this, 'disableAnonymousRestApi' ], 99 );
74
- }
75
- }
76
- }
77
-
78
- /**
79
- * @return array|false
80
- */
81
- public function disableXmlrpc() {
82
- $this->getCon()->fireEvent( 'block_xml' );
83
- return ( current_filter() == 'xmlrpc_enabled' ) ? false : [];
84
- }
85
-
86
- /**
87
- * @uses wp_die()
88
- */
89
- private function interceptCanonicalRedirects() {
90
-
91
- if ( $this->getOptions()->isOpt( 'block_author_discovery', 'Y' ) ) {
92
- $sAuthor = Services::Request()->query( 'author', '' );
93
- if ( !empty( $sAuthor ) ) {
94
- Services::WpGeneral()->wpDie( sprintf(
95
- __( 'The "author" query parameter has been blocked by %s to protect against user login name fishing.', 'wp-simple-firewall' )
96
- .sprintf( '<br /><a href="%s" target="_blank">%s</a>',
97
- 'https://shsec.io/7l',
98
- __( 'Learn More.', 'wp-simple-firewall' )
99
- ),
100
- $this->getCon()->getHumanName()
101
- ) );
102
- }
103
- }
104
- }
105
-
106
- /**
107
- * Understand that if $mCurrentStatus is null, no check has been made. If true, something has
108
- * authenticated the request, and if WP_Error, then an error is already present
109
- * @param WP_Error|true|null $mStatus
110
- * @return WP_Error
111
- */
112
- public function disableAnonymousRestApi( $mStatus ) {
113
- /** @var \ICWP_WPSF_FeatureHandler_Lockdown $mod */
114
- $mod = $this->getMod();
115
- $oWpRest = Services::Rest();
116
-
117
- $sNamespace = $oWpRest->getNamespace();
118
- if ( !empty( $sNamespace ) && $mStatus !== true && !is_wp_error( $mStatus )
119
- && !$mod->isPermittedAnonRestApiNamespace( $sNamespace ) ) {
120
-
121
- $mStatus = new \WP_Error(
122
- 'shield_block_anon_restapi',
123
- sprintf( __( 'Anonymous access to the WordPress Rest API has been restricted by %s.', 'wp-simple-firewall' ), $this->getCon()
124
- ->getHumanName() ),
125
- [ 'status' => rest_authorization_required_code() ] );
126
-
127
- $this->getCon()
128
- ->fireEvent(
129
- 'block_anonymous_restapi',
130
- [ 'audit' => [ 'namespace' => $sNamespace ] ]
131
- );
132
- }
133
-
134
- return $mStatus;
135
- }
136
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/processors/login_protect.php DELETED
@@ -1,52 +0,0 @@
1
- <?php
2
-
3
- use FernleafSystems\Wordpress\Plugin\Shield\Modules;
4
- use FernleafSystems\Wordpress\Plugin\Shield\Modules\LoginGuard;
5
- use FernleafSystems\Wordpress\Plugin\Shield\Modules\LoginGuard\Lib\AntiBot;
6
- use FernleafSystems\Wordpress\Services\Services;
7
-
8
- /**
9
- * @deprecated 10.1
10
- */
11
- class ICWP_WPSF_Processor_LoginProtect extends Modules\BaseShield\ShieldProcessor {
12
-
13
- public function run() {
14
- /** @var LoginGuard\ModCon $mod */
15
- $mod = $this->getMod();
16
-
17
- // XML-RPC Compatibility
18
- if ( Services::WpGeneral()->isXmlrpc() && $mod->isXmlrpcBypass() ) {
19
- return;
20
- }
21
-
22
- // So we can allow access to the login pages if IP is whitelisted
23
- /** @var LoginGuard\Options $opts */
24
- $opts = $this->getOptions();
25
- if ( !empty( $opts->getCustomLoginPath() ) ) {
26
- $this->getSubPro( 'rename' )->execute();
27
- }
28
-
29
- if ( !$mod->isVisitorWhitelisted() ) {
30
- ( new AntiBot\AntibotSetup() )->setMod( $mod );
31
- $mod->getLoginIntentController()->run();
32
- }
33
- }
34
-
35
- /**
36
- * Override the original collection to then add plugin statistics to the mix
37
- * @param $aData
38
- * @return array
39
- */
40
- public function tracking_DataCollect( $aData ) {
41
- return $aData;
42
- }
43
-
44
- /**
45
- * @return array
46
- */
47
- protected function getSubProMap() :array {
48
- return [
49
- 'rename' => 'ICWP_WPSF_Processor_LoginProtect_WpLogin',
50
- ];
51
- }
52
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/processors/loginprotect_wplogin.php DELETED
@@ -1,236 +0,0 @@
1
- <?php
2
-
3
- use FernleafSystems\Wordpress\Plugin\Shield\Modules;
4
- use FernleafSystems\Wordpress\Plugin\Shield\Modules\LoginGuard;
5
- use FernleafSystems\Wordpress\Services\Services;
6
-
7
- /**
8
- * @deprecated 10.1
9
- */
10
- class ICWP_WPSF_Processor_LoginProtect_WpLogin extends Modules\BaseShield\ShieldProcessor {
11
-
12
- public function onWpInit() {
13
- /** @var LoginGuard\ModCon $mod */
14
- $mod = $this->getMod();
15
-
16
- if ( $this->checkForPluginConflict() || $this->checkForUnsupportedConfiguration() ) {
17
- return;
18
- }
19
- if ( Services::WpGeneral()->isLoginUrl() &&
20
- ( $mod->isVisitorWhitelisted() || Services::WpUsers()->isUserLoggedIn() ) ) {
21
- return;
22
- }
23
- if ( is_admin() && $mod->isVisitorWhitelisted() && !Services::WpUsers()->isUserLoggedIn() ) {
24
- return;
25
- }
26
-
27
- $this->doBlockPossibleWpLoginLoad();
28
-
29
- // Loads the wp-login.php if the correct URL is loaded
30
- add_action( 'wp_loaded', [ $this, 'aLoadWpLogin' ] );
31
-
32
- // Shouldn't be necessary, but in-case something else includes the wp-login.php, we block that too.
33
- add_action( 'login_init', [ $this, 'aLoginFormAction' ], 0 );
34
-
35
- // ensure that wp-login.php is never used in site urls or redirects
36
- add_filter( 'site_url', [ $this, 'fCheckForLoginPhp' ], 20, 1 );
37
- add_filter( 'network_site_url', [ $this, 'fCheckForLoginPhp' ], 20, 1 );
38
- add_filter( 'wp_redirect', [ $this, 'fCheckForLoginPhp' ], 20, 1 );
39
- if ( !Services::WpUsers()->isUserLoggedIn() ) {
40
- add_filter( 'wp_redirect', [ $this, 'fProtectUnauthorizedLoginRedirect' ], 50, 1 );
41
- }
42
- add_filter( 'register_url', [ $this, 'blockRegisterUrlRedirect' ], 20, 1 );
43
-
44
- add_filter( 'et_anticipate_exceptions', [ $this, 'fAddToEtMaintenanceExceptions' ] );
45
- }
46
-
47
- /**
48
- * @return bool - true if conflict exists
49
- */
50
- protected function checkForPluginConflict() {
51
- /** @var LoginGuard\ModCon $mod */
52
- $mod = $this->getMod();
53
- /** @var LoginGuard\Options $opts */
54
- $opts = $this->getOptions();
55
-
56
- $sMessage = '';
57
- $bConflicted = false;
58
-
59
- $path = $opts->getCustomLoginPath();
60
-
61
- $WP = Services::WpGeneral();
62
- if ( $WP->isMultisite() ) {
63
- $sMessage = __( 'Your login URL is unchanged because the Rename WP Login feature is not currently supported on WPMS.', 'wp-simple-firewall' );
64
- $bConflicted = true;
65
- }
66
- elseif ( class_exists( 'Rename_WP_Login' ) ) {
67
- $sMessage = sprintf( __( 'Can not use the Rename WP Login feature because you have the "%s" plugin installed and it is active.', 'wp-simple-firewall' ), 'Rename WP Login' );
68
- $bConflicted = true;
69
- }
70
- elseif ( class_exists( 'Theme_My_Login' ) ) {
71
- $sMessage = sprintf( __( 'Can not use the Rename WP Login feature because you have the "%s" plugin installed and it is active.', 'wp-simple-firewall' ), 'Theme My Login' );
72
- $bConflicted = true;
73
- }
74
- elseif ( !$WP->isPermalinksEnabled() ) {
75
- $sMessage = sprintf( __( 'Can not use the Rename WP Login feature because you have not enabled %s.', 'wp-simple-firewall' ), __( 'Permalinks' ) );
76
- $bConflicted = true;
77
- }
78
- elseif ( $WP->isPermalinksEnabled() && ( $WP->getDoesWpSlugExist( $path ) || in_array( $path, $WP->getAutoRedirectLocations() ) ) ) {
79
- $sMessage = sprintf( __( 'Can not use the Rename WP Login feature because you have chosen a path ("%s") that is reserved on your WordPress site.', 'wp-simple-firewall' ), $path );
80
- $bConflicted = true;
81
- }
82
-
83
- if ( $bConflicted ) {
84
- $sNoticeMessage = sprintf( '<strong>%s</strong>: %s',
85
- __( 'Warning', 'wp-simple-firewall' ),
86
- $sMessage
87
- );
88
- $mod->setFlashAdminNotice( $sNoticeMessage, true );
89
- }
90
-
91
- return $bConflicted;
92
- }
93
-
94
- /**
95
- * @return bool
96
- */
97
- private function checkForUnsupportedConfiguration() {
98
- /** @var LoginGuard\ModCon $mod */
99
- $mod = $this->getMod();
100
- $path = Services::Request()->getPath();
101
- if ( empty( $path ) ) {
102
-
103
- $sNoticeMessage = sprintf(
104
- '<strong>%s</strong>: %s',
105
- __( 'Warning', 'wp-simple-firewall' ),
106
- __( 'Your login URL is unchanged because your current hosting/PHP configuration cannot parse the necessary information.', 'wp-simple-firewall' )
107
- );
108
- $mod->setFlashAdminNotice( $sNoticeMessage, true );
109
- return true;
110
- }
111
- return false;
112
- }
113
-
114
- public function doBlockPossibleWpLoginLoad() {
115
-
116
- // To begin, we block if it's an access to the admin area and the user isn't logged in (and it's not ajax)
117
- $bDoBlock = is_admin()
118
- && !Services::WpGeneral()->isAjax() && !Services::WpUsers()->isUserLoggedIn();
119
-
120
- // Next block option is where it's a direct attempt to access the old login URL
121
- if ( !$bDoBlock ) {
122
- $sPath = trim( Services::Request()->getPath(), '/' );
123
- $aPossiblePaths = [
124
- trim( home_url( 'wp-login.php', 'relative' ), '/' ),
125
- trim( home_url( 'wp-signup.php', 'relative' ), '/' ),
126
- trim( site_url( 'wp-signup.php', 'relative' ), '/' ),
127
- // trim( site_url( 'wp-login.php', 'relative' ), '/' ), our own filters in run() scuttle us here so we have to build it manually
128
- trim( rtrim( site_url( '', 'relative' ), '/' ).'/wp-login.php', '/' ),
129
- trim( home_url( 'login', 'relative' ), '/' ),
130
- trim( site_url( 'login', 'relative' ), '/' )
131
- ];
132
- $bDoBlock = !empty( $sPath )
133
- && ( in_array( $sPath, $aPossiblePaths ) || preg_match( '/wp-login\.php/i', $sPath ) );
134
- }
135
-
136
- if ( $bDoBlock ) {
137
- $this->doWpLoginFailedRedirect404();
138
- }
139
- }
140
-
141
- /**
142
- * @param string $sLocation
143
- * @return string
144
- */
145
- public function fCheckForLoginPhp( $sLocation ) {
146
- /** @var LoginGuard\Options $opts */
147
- $opts = $this->getOptions();
148
-
149
- $sRedirectPath = parse_url( $sLocation, PHP_URL_PATH );
150
- if ( strpos( $sRedirectPath, 'wp-login.php' ) !== false ) {
151
-
152
- $sLoginUrl = home_url( $opts->getCustomLoginPath() );
153
- $aQueryArgs = explode( '?', $sLocation );
154
- if ( !empty( $aQueryArgs[ 1 ] ) ) {
155
- parse_str( $aQueryArgs[ 1 ], $aNewQueryArgs );
156
- $sLoginUrl = add_query_arg( $aNewQueryArgs, $sLoginUrl );
157
- }
158
- return $sLoginUrl;
159
- }
160
- return $sLocation;
161
- }
162
-
163
- /**
164
- * @param string $sLocation
165
- * @return string
166
- */
167
- public function fProtectUnauthorizedLoginRedirect( $sLocation ) {
168
- /** @var LoginGuard\Options $opts */
169
- $opts = $this->getOptions();
170
-
171
- if ( !Services::WpGeneral()->isLoginUrl() ) {
172
- $sRedirectPath = trim( parse_url( $sLocation, PHP_URL_PATH ), '/' );
173
- $bRedirectIsHiddenUrl = ( $sRedirectPath == $opts->getCustomLoginPath() );
174
- if ( $bRedirectIsHiddenUrl && !Services::WpUsers()->isUserLoggedIn() ) {
175
- $this->doWpLoginFailedRedirect404();
176
- }
177
- }
178
- return $sLocation;
179
- }
180
-
181
- /**
182
- * @param string $sUrl
183
- * @return string
184
- */
185
- public function blockRegisterUrlRedirect( $sUrl ) {
186
- $sPath = Services::Request()->getPath();
187
- if ( strpos( $sPath, 'wp-register.php' ) ) {
188
- $this->doWpLoginFailedRedirect404();
189
- die();
190
- }
191
- return $sUrl;
192
- }
193
-
194
- public function aLoadWpLogin() {
195
- if ( Services::WpGeneral()->isLoginUrl() ) {
196
- @require_once( ABSPATH.'wp-login.php' );
197
- die();
198
- }
199
- }
200
-
201
- public function aLoginFormAction() {
202
- if ( !Services::WpGeneral()->isLoginUrl() ) {
203
- $this->doWpLoginFailedRedirect404();
204
- die();
205
- }
206
- }
207
-
208
- /**
209
- * Add the custom login URL to the Elegant Themes Maintenance Mode plugin URL exceptions list
210
- * @param array $aUrlExceptions
211
- * @return array
212
- */
213
- public function fAddToEtMaintenanceExceptions( $aUrlExceptions ) {
214
- /** @var LoginGuard\Options $opts */
215
- $opts = $this->getOptions();
216
- $aUrlExceptions[] = $opts->getCustomLoginPath();
217
- return $aUrlExceptions;
218
- }
219
-
220
- /**
221
- * Will by default send a 404 response screen. Has a filter to specify redirect URL.
222
- */
223
- protected function doWpLoginFailedRedirect404() {
224
- $this->getCon()->fireEvent( 'hide_login_url' );
225
-
226
- $sRedirectUrl = apply_filters( 'icwp_shield_renamewplogin_redirect_url', false );
227
- if ( !empty( $sRedirectUrl ) ) {
228
- $sRedirectUrl = esc_url( $sRedirectUrl );
229
- if ( @parse_url( $sRedirectUrl ) !== false ) {
230
- Services::Response()->redirect( $sRedirectUrl, [], false );
231
- }
232
- }
233
-
234
- Services::Response()->sendApache404( '', Services::WpGeneral()->getHomeUrl() );
235
- }
236
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/processors/plugin.php DELETED
@@ -1,68 +0,0 @@
1
- <?php
2
-
3
- use FernleafSystems\Wordpress\Plugin\Shield;
4
- use FernleafSystems\Wordpress\Plugin\Shield\Modules;
5
-
6
- /**
7
- * @deprecated 10.1
8
- */
9
- class ICWP_WPSF_Processor_Plugin extends Modules\BaseShield\ShieldProcessor {
10
-
11
- public function run() {
12
- }
13
-
14
- public function printDashboardWidget() {
15
- }
16
-
17
- /**
18
- * @return \ICWP_WPSF_Processor_Plugin_Tracking
19
- */
20
- protected function getSubProTracking() {
21
- return $this->getSubPro( 'tracking' );
22
- }
23
-
24
- /**
25
- * @return array
26
- */
27
- protected function getSubProMap() :array {
28
- return [
29
- 'tracking' => 'ICWP_WPSF_Processor_Plugin_Tracking',
30
- ];
31
- }
32
-
33
- public function printAdminFooterItems() {
34
- }
35
-
36
- /**
37
- * Sets this plugin to be the first loaded of all the plugins.
38
- */
39
- private function printToastTemplate() {
40
- }
41
-
42
- private function printPluginDeactivateSurvey() {
43
- }
44
-
45
- /**
46
- * @deprecated 10.1
47
- */
48
- public function dumpTrackingData() {
49
- }
50
-
51
- public function runDailyCron() {
52
- }
53
-
54
- /**
55
- * Lets you remove certain plugin conflicts that might interfere with this plugin
56
- */
57
- protected function removePluginConflicts() {
58
- }
59
-
60
- /**
61
- * Override the original collection to then add plugin statistics to the mix
62
- * @param array $aData
63
- * @return array
64
- */
65
- public function tracking_DataCollect( $aData ) {
66
- return parent::tracking_DataCollect( $aData );
67
- }
68
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/processors/plugin_tracking.php DELETED
@@ -1,29 +0,0 @@
1
- <?php
2
-
3
- use FernleafSystems\Wordpress\Plugin\Shield;
4
-
5
- /**
6
- * @deprecated 10.1
7
- */
8
- class ICWP_WPSF_Processor_Plugin_Tracking extends Shield\Modules\BaseShield\ShieldProcessor {
9
-
10
- public function runDailyCron() {
11
- }
12
-
13
- private function sendTrackingData() {
14
- }
15
-
16
- /**
17
- * @return array
18
- */
19
- public function collectTrackingData() {
20
- return [];
21
- }
22
-
23
- /**
24
- * @return array
25
- */
26
- protected function getBaseTrackingData() {
27
- return [];
28
- }
29
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/processors/sessions.php DELETED
@@ -1,191 +0,0 @@
1
- <?php
2
-
3
- use FernleafSystems\Wordpress\Plugin\Shield\Databases\Session;
4
- use FernleafSystems\Wordpress\Plugin\Shield\Modules;
5
- use FernleafSystems\Wordpress\Plugin\Shield\Modules\Sessions;
6
- use FernleafSystems\Wordpress\Services\Services;
7
-
8
- /**
9
- * @deprecated 10.1
10
- */
11
- class ICWP_WPSF_Processor_Sessions extends Modules\BaseShield\ShieldProcessor {
12
-
13
- /**
14
- * @var Session\EntryVO
15
- */
16
- private $oCurrent;
17
-
18
- public function run() {
19
- if ( !Services::WpUsers()->isProfilePage() ) { // only on logout
20
- add_action( 'clear_auth_cookie', function () {
21
- $this->terminateCurrentSession();
22
- }, 0 );
23
- }
24
- add_filter( 'login_message', [ $this, 'printLinkToAdmin' ] );
25
- }
26
-
27
- /**
28
- * @param string $sUsername
29
- * @param \WP_User $user
30
- */
31
- public function onWpLogin( $sUsername, $user ) {
32
- if ( !$user instanceof \WP_User ) {
33
- $user = Services::WpUsers()->getUserByUsername( $sUsername );
34
- }
35
- $this->activateUserSession( $user );
36
- }
37
-
38
- /**
39
- * @param string $sCookie
40
- * @param int $nExpire
41
- * @param int $nExpiration
42
- * @param int $nUserId
43
- */
44
- public function onWpSetLoggedInCookie( $sCookie, $nExpire, $nExpiration, $nUserId ) {
45
- $this->activateUserSession( Services::WpUsers()->getUserById( $nUserId ) );
46
- }
47
-
48
- public function onWpLoaded() {
49
- if ( Services::WpUsers()->isUserLoggedIn() && !Services::Rest()->isRest() ) {
50
- $this->autoAddSession();
51
- }
52
- }
53
-
54
- public function onModuleShutdown() {
55
- /** @var Sessions\ModCon $mod */
56
- $mod = $this->getMod();
57
-
58
- if ( !Services::Rest()->isRest() && !$this->getCon()->plugin_deleting ) {
59
- $oSession = $this->getCurrentSession();
60
- if ( $oSession instanceof Session\EntryVO ) {
61
- /** @var Session\Update $oUpd */
62
- $oUpd = $mod->getDbHandler_Sessions()->getQueryUpdater();
63
- $oUpd->updateLastActivity( $this->getCurrentSession() );
64
- }
65
- }
66
-
67
- parent::onModuleShutdown();
68
- }
69
-
70
- private function autoAddSession() {
71
- /** @var Sessions\ModCon $mod */
72
- $mod = $this->getMod();
73
- if ( !$mod->getSession() && $mod->isAutoAddSessions() ) {
74
- $this->queryCreateSession(
75
- $this->getCon()->getSessionId( true ),
76
- Services::WpUsers()->getCurrentWpUsername()
77
- );
78
- }
79
- }
80
-
81
- /**
82
- * Only show Go To Admin link for Authors and above.
83
- * @param string $sMessage
84
- * @return string
85
- * @throws \Exception
86
- */
87
- public function printLinkToAdmin( $sMessage = '' ) {
88
- /** @var Sessions\ModCon $mod */
89
- $mod = $this->getMod();
90
- $user = Services::WpUsers()->getCurrentWpUser();
91
-
92
- if ( in_array( Services::Request()->query( 'action' ), [ '', 'login' ] )
93
- && ( $user instanceof \WP_User ) && $mod->getSessionCon()->hasSession() ) {
94
- $sMessage .= sprintf( '<p class="message">%s<br />%s</p>',
95
- __( "You're already logged-in.", 'wp-simple-firewall' )
96
- .sprintf( ' <span style="white-space: nowrap">(%s)</span>', $user->user_login ),
97
- ( $user->user_level >= 2 ) ? sprintf( '<a href="%s">%s</a>',
98
- Services::WpGeneral()->getAdminUrl(),
99
- __( "Go To Admin", 'wp-simple-firewall' ).' &rarr;' ) : '' );
100
- }
101
- return $sMessage;
102
- }
103
-
104
- /**
105
- * @param \WP_User $oUser
106
- * @return bool
107
- */
108
- private function activateUserSession( $oUser ) {
109
- if ( !$this->isLoginCaptured() && $oUser instanceof \WP_User ) {
110
- $this->setLoginCaptured();
111
- // If they have a currently active session, terminate it (i.e. we replace it)
112
- $this->terminateCurrentSession();
113
- $this->queryCreateSession( $this->getCon()->getSessionId( true ), $oUser->user_login );
114
- }
115
- return true;
116
- }
117
-
118
- /**
119
- * @return bool
120
- */
121
- public function terminateCurrentSession() {
122
- $bSuccess = false;
123
-
124
- $oSes = $this->getCurrentSession();
125
- if ( $oSes instanceof Session\EntryVO ) {
126
- $bSuccess = ( new Sessions\Lib\Ops\Terminate() )
127
- ->setMod( $this->getMod() )
128
- ->byRecordId( $oSes->id );
129
- }
130
-
131
- $this->oCurrent = null;
132
- $this->getCon()->clearSession();
133
-
134
- return $bSuccess;
135
- }
136
-
137
- /**
138
- * @return Session\EntryVO|null
139
- */
140
- public function getCurrentSession() {
141
- if ( empty( $this->oCurrent ) ) {
142
- $this->oCurrent = $this->loadCurrentSession();
143
- }
144
- return $this->oCurrent;
145
- }
146
-
147
- /**
148
- * @return Session\EntryVO|null
149
- */
150
- public function loadCurrentSession() {
151
- $oSession = null;
152
- $oCon = $this->getCon();
153
- if ( did_action( 'init' ) && $oCon->hasSessionId() ) {
154
- $oSession = $this->queryGetSession( $oCon->getSessionId() );
155
- }
156
- return $oSession;
157
- }
158
-
159
- /**
160
- * @param string $sSessionId
161
- * @param string $sUsername
162
- * @return bool
163
- */
164
- protected function queryCreateSession( $sSessionId, $sUsername ) {
165
- /** @var Sessions\ModCon $mod */
166
- $mod = $this->getMod();
167
- if ( empty( $sSessionId ) || empty( $sUsername ) ) {
168
- return null;
169
- }
170
-
171
- $this->getCon()->fireEvent( 'session_start' );
172
-
173
- /** @var Session\Insert $oInsert */
174
- $oInsert = $mod->getDbHandler_Sessions()->getQueryInserter();
175
- return $oInsert->create( $sSessionId, $sUsername );
176
- }
177
-
178
- /**
179
- * Checks for and gets a user session.
180
- * @param string $sUsername
181
- * @param string $sSessionId
182
- * @return Session\EntryVO|null
183
- */
184
- private function queryGetSession( $sSessionId, $sUsername = '' ) {
185
- /** @var Sessions\ModCon $mod */
186
- $mod = $this->getMod();
187
- /** @var Session\Select $oSel */
188
- $oSel = $mod->getDbHandler_Sessions()->getQuerySelector();
189
- return $oSel->retrieveUserSession( $sSessionId, $sUsername );
190
- }
191
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/processors/traffic.php DELETED
@@ -1,24 +0,0 @@
1
- <?php
2
-
3
- use FernleafSystems\Wordpress\Plugin\Shield\Modules;
4
- use FernleafSystems\Wordpress\Plugin\Shield\Modules\Traffic\Lib;
5
-
6
- /**
7
- * Class ICWP_WPSF_Processor_Traffic
8
- * @deprecated 10.1
9
- */
10
- class ICWP_WPSF_Processor_Traffic extends Modules\BaseShield\ShieldProcessor {
11
-
12
- public function run() {
13
- /** @var Modules\Traffic\Options $opts */
14
- $opts = $this->getOptions();
15
- if ( $opts->isTrafficLoggerEnabled() ) {
16
- ( new Lib\Logger() )
17
- ->setMod( $this->getMod() )
18
- ->run();
19
- ( new Lib\Limit\Limiter() )
20
- ->setMod( $this->getMod() )
21
- ->run();
22
- }
23
- }
24
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/processors/user_management.php DELETED
@@ -1,265 +0,0 @@
1
- <?php
2
-
3
- use FernleafSystems\Wordpress\Plugin\Shield\Modules;
4
- use FernleafSystems\Wordpress\Plugin\Shield\Modules\UserManagement;
5
- use FernleafSystems\Wordpress\Services\Services;
6
-
7
- /**
8
- * @deprecated 10.1
9
- */
10
- class ICWP_WPSF_Processor_UserManagement extends Modules\BaseShield\ShieldProcessor {
11
-
12
- /**
13
- * This module is set to "run if whitelisted", so we must ensure any
14
- * actions taken by this module respect whether the current visitor is whitelisted.
15
- */
16
- public function run() {
17
- /** @var UserManagement\ModCon $mod */
18
- $mod = $this->getMod();
19
- /** @var UserManagement\Options $opts */
20
- $opts = $this->getOptions();
21
-
22
- // Adds last login indicator column
23
- add_filter( 'manage_users_columns', [ $this, 'addUserStatusLastLogin' ] );
24
- add_filter( 'wpmu_users_columns', [ $this, 'addUserStatusLastLogin' ] );
25
-
26
- /** Everything from this point on must consider XMLRPC compatibility **/
27
-
28
- // XML-RPC Compatibility
29
- if ( Services::WpGeneral()->isXmlrpc() && $mod->isXmlrpcBypass() ) {
30
- return;
31
- }
32
-
33
- // This controller will handle visitor whitelisted status internally.
34
- ( new UserManagement\Lib\Suspend\UserSuspendController() )
35
- ->setMod( $this->getMod() )
36
- ->execute();
37
-
38
- if ( !$mod->isVisitorWhitelisted() ) {
39
-
40
- /** Everything from this point on must consider XMLRPC compatibility **/
41
- if ( $mod->isUserSessionsManagementEnabled() ) {
42
- $this->getSubPro( 'sessions' )->execute();
43
- }
44
-
45
- if ( $opts->isPasswordPoliciesEnabled() ) {
46
- $this->getSubPro( 'passwords' )->execute();
47
- }
48
-
49
- // All newly created users have their first seen and password start date set
50
- add_action( 'user_register', function ( $nUserId ) {
51
- $this->getCon()->getUserMeta( Services::WpUsers()->getUserById( $nUserId ) );
52
- } );
53
-
54
- ( new UserManagement\Lib\Registration\EmailValidate() )
55
- ->setMod( $this->getMod() )
56
- ->run();
57
- }
58
- }
59
-
60
- public function onWpInit() {
61
- $WPU = Services::WpUsers();
62
- if ( $WPU->isUserLoggedIn() ) {
63
- $this->setPasswordStartedAt( $WPU->getCurrentWpUser() ); // used by Password Policies
64
- }
65
- }
66
-
67
- /**
68
- * @param string $sUsername
69
- * @param \WP_User $user
70
- */
71
- public function onWpLogin( $sUsername, $user = null ) {
72
- if ( !$user instanceof \WP_User ) {
73
- $user = Services::WpUsers()->getUserByUsername( $sUsername );
74
- }
75
- $this->setPasswordStartedAt( $user )// used by Password Policies
76
- ->setUserLastLoginTime( $user )
77
- ->sendLoginNotifications( $user );
78
- }
79
-
80
- /**
81
- * @param \WP_User $user - not checking that user is valid
82
- * @return $this
83
- */
84
- private function sendLoginNotifications( \WP_User $user ) {
85
- /** @var UserManagement\ModCon $mod */
86
- $mod = $this->getMod();
87
- $aAdminEmails = $mod->getAdminLoginNotificationEmails();
88
- $bAdmin = count( $aAdminEmails ) > 0;
89
- $bUser = $mod->isSendUserEmailLoginNotification();
90
-
91
- // do some magic logic so we don't send both to the same person (the assumption being that the admin
92
- // email recipient is actually an admin (or they'll maybe not get any).
93
- if ( $bAdmin && $bUser && in_array( strtolower( $user->user_email ), $aAdminEmails ) ) {
94
- $bUser = false;
95
- }
96
-
97
- if ( $bAdmin ) {
98
- $this->sendAdminLoginEmailNotification( $user );
99
- }
100
- if ( $bUser && !$this->isUserSubjectToLoginIntent( $user ) ) {
101
- $this->sendUserLoginEmailNotification( $user );
102
- }
103
- return $this;
104
- }
105
-
106
- private function setPasswordStartedAt( \WP_User $user ) :self {
107
- $this->getCon()
108
- ->getUserMeta( $user )
109
- ->setPasswordStartedAt( $user->user_pass );
110
- return $this;
111
- }
112
-
113
- protected function setUserLastLoginTime( \WP_User $user ) :self {
114
- $meta = $this->getCon()->getUserMeta( $user );
115
- $meta->last_login_at = Services::Request()->ts();
116
- return $this;
117
- }
118
-
119
- /**
120
- * Adds the column to the users listing table to indicate
121
- * @param array $aColumns
122
- * @return array
123
- */
124
- public function addUserStatusLastLogin( $aColumns ) {
125
-
126
- $sCustomColumnName = $this->getCon()->prefix( 'col_user_status' );
127
- if ( !isset( $aColumns[ $sCustomColumnName ] ) ) {
128
- $aColumns[ $sCustomColumnName ] = __( 'User Status', 'wp-simple-firewall' );
129
- }
130
-
131
- add_filter( 'manage_users_custom_column',
132
- function ( $sContent, $sColumnName, $nUserId ) use ( $sCustomColumnName ) {
133
-
134
- if ( $sColumnName == $sCustomColumnName ) {
135
- $sValue = __( 'Not Recorded', 'wp-simple-firewall' );
136
- $oUser = Services::WpUsers()->getUserById( $nUserId );
137
- if ( $oUser instanceof \WP_User ) {
138
- $nLastLoginTime = $this->getCon()->getUserMeta( $oUser )->last_login_at;
139
- if ( $nLastLoginTime > 0 ) {
140
- $sValue = Services::Request()
141
- ->carbon()
142
- ->setTimestamp( $nLastLoginTime )
143
- ->diffForHumans();
144
- }
145
- }
146
- $sNewContent = sprintf( '%s: %s', __( 'Last Login', 'wp-simple-firewall' ), $sValue );
147
- $sContent = empty( $sContent ) ? $sNewContent : $sContent.'<br/>'.$sNewContent;
148
- }
149
-
150
- return $sContent;
151
- },
152
- 10, 3
153
- );
154
-
155
- return $aColumns;
156
- }
157
-
158
- /**
159
- * @param \WP_User $oUser
160
- * @return bool
161
- */
162
- private function sendAdminLoginEmailNotification( $oUser ) {
163
- /** @var UserManagement\ModCon $mod */
164
- $mod = $this->getMod();
165
- $con = $this->getCon();
166
-
167
- $aUserCapToRolesMap = [
168
- 'network_admin' => 'manage_network',
169
- 'administrator' => 'manage_options',
170
- 'editor' => 'edit_pages',
171
- 'author' => 'publish_posts',
172
- 'contributor' => 'delete_posts',
173
- 'subscriber' => 'read',
174
- ];
175
-
176
- $sRoleToCheck = strtolower( apply_filters(
177
- $con->prefix( 'login-notification-email-role' ), 'administrator' ) );
178
- if ( !array_key_exists( $sRoleToCheck, $aUserCapToRolesMap ) ) {
179
- $sRoleToCheck = 'administrator';
180
- }
181
- $sHumanName = ucwords( str_replace( '_', ' ', $sRoleToCheck ) ).'+';
182
-
183
- $bIsUserSignificantEnough = false;
184
- foreach ( $aUserCapToRolesMap as $sRole => $sCap ) {
185
- if ( isset( $oUser->allcaps[ $sCap ] ) && $oUser->allcaps[ $sCap ] ) {
186
- $bIsUserSignificantEnough = true;
187
- }
188
- if ( $sRoleToCheck == $sRole ) {
189
- break; // we've hit our role limit.
190
- }
191
- }
192
- if ( !$bIsUserSignificantEnough ) {
193
- return false;
194
- }
195
-
196
- $sHomeUrl = Services::WpGeneral()->getHomeUrl();
197
-
198
- $aMessage = [
199
- sprintf( __( 'As requested, %s is notifying you of a successful %s login to a WordPress site that you manage.', 'wp-simple-firewall' ),
200
- $con->getHumanName(),
201
- $sHumanName
202
- ),
203
- '',
204
- sprintf( __( 'Important: %s', 'wp-simple-firewall' ), __( 'This user may now be subject to additional Two-Factor Authentication before completing their login.', 'wp-simple-firewall' ) ),
205
- '',
206
- __( 'Details for this user are below:', 'wp-simple-firewall' ),
207
- '- '.sprintf( '%s: %s', __( 'Site URL', 'wp-simple-firewall' ), $sHomeUrl ),
208
- '- '.sprintf( '%s: %s', __( 'Username', 'wp-simple-firewall' ), $oUser->user_login ),
209
- '- '.sprintf( '%s: %s', __( 'Email', 'wp-simple-firewall' ), $oUser->user_email ),
210
- '- '.sprintf( '%s: %s', __( 'IP Address', 'wp-simple-firewall' ), Services::IP()->getRequestIp() ),
211
- '',
212
- __( 'Thanks.', 'wp-simple-firewall' )
213
- ];
214
-
215
- $oEmailer = $this->getMod()
216
- ->getEmailProcessor();
217
- foreach ( $mod->getAdminLoginNotificationEmails() as $to ) {
218
- $oEmailer->sendEmailWithWrap(
219
- $to,
220
- sprintf( '%s - %s', __( 'Notice', 'wp-simple-firewall' ), sprintf( __( '%s Just Logged Into %s', 'wp-simple-firewall' ), $sHumanName, $sHomeUrl ) ),
221
- $aMessage
222
- );
223
- }
224
-
225
- return true;
226
- }
227
-
228
- /**
229
- * @param \WP_User $oUser
230
- * @return bool
231
- */
232
- private function sendUserLoginEmailNotification( $oUser ) {
233
- $oWp = Services::WpGeneral();
234
- $aMessage = [
235
- sprintf( __( '%s is notifying you of a successful login to your WordPress account.', 'wp-simple-firewall' ), $this->getCon()
236
- ->getHumanName() ),
237
- '',
238
- __( 'Details for this login are below:', 'wp-simple-firewall' ),
239
- '- '.sprintf( '%s: %s', __( 'Site URL', 'wp-simple-firewall' ), $oWp->getHomeUrl() ),
240
- '- '.sprintf( '%s: %s', __( 'Username', 'wp-simple-firewall' ), $oUser->user_login ),
241
- '- '.sprintf( '%s: %s', __( 'IP Address', 'wp-simple-firewall' ), Services::IP()->getRequestIp() ),
242
- '- '.sprintf( '%s: %s', __( 'Time', 'wp-simple-firewall' ), $oWp->getTimeStampForDisplay() ),
243
- '',
244
- __( 'If this is unexpected or suspicious, please contact your site administrator immediately.', 'wp-simple-firewall' ),
245
- '',
246
- __( 'Thanks.', 'wp-simple-firewall' )
247
- ];
248
-
249
- return $this
250
- ->getMod()
251
- ->getEmailProcessor()
252
- ->sendEmailWithWrap(
253
- $oUser->user_email,
254
- sprintf( '%s - %s', __( 'Notice', 'wp-simple-firewall' ), __( 'A login to your WordPress account just occurred', 'wp-simple-firewall' ) ),
255
- $aMessage
256
- );
257
- }
258
-
259
- protected function getSubProMap() :array {
260
- return [
261
- 'passwords' => 'ICWP_WPSF_Processor_UserManagement_Passwords',
262
- 'sessions' => 'ICWP_WPSF_Processor_UserManagement_Sessions',
263
- ];
264
- }
265
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/processors/usermanagement_passwords.php DELETED
@@ -1,333 +0,0 @@
1
- <?php
2
-
3
- use FernleafSystems\Wordpress\Plugin\Shield\Modules;
4
- use FernleafSystems\Wordpress\Plugin\Shield\Modules\UserManagement;
5
- use FernleafSystems\Wordpress\Services\Services;
6
-
7
- /**
8
- * @deprecated 10.1
9
- */
10
- class ICWP_WPSF_Processor_UserManagement_Passwords extends Modules\BaseShield\ShieldProcessor {
11
-
12
- public function run() {
13
- add_action( 'password_reset', [ $this, 'onPasswordReset' ], 100, 1 );
14
- add_filter( 'registration_errors', [ $this, 'checkPassword' ], 100, 3 );
15
- add_action( 'user_profile_update_errors', [ $this, 'checkPassword' ], 100, 3 );
16
- add_action( 'validate_password_reset', [ $this, 'checkPassword' ], 100, 3 );
17
- }
18
-
19
- /**
20
- * @param string $sUsername
21
- * @param \WP_User $user
22
- */
23
- public function onWpLogin( $sUsername, $user ) {
24
- $this->captureLogin( $user );
25
- }
26
-
27
- /**
28
- * @param string $sCookie
29
- * @param int $nExpire
30
- * @param int $nExpiration
31
- * @param int $nUserId
32
- */
33
- public function onWpSetLoggedInCookie( $sCookie, $nExpire, $nExpiration, $nUserId ) {
34
- $this->captureLogin( Services::WpUsers()->getUserById( $nUserId ) );
35
- }
36
-
37
- /**
38
- * @param \WP_User $user
39
- */
40
- private function captureLogin( $user ) {
41
- $password = $this->getLoginPassword();
42
-
43
- if ( $user instanceof \WP_User
44
- && Services::Request()->isPost()
45
- && !$this->isLoginCaptured() && !empty( $password ) ) {
46
-
47
- $this->setLoginCaptured();
48
- try {
49
- $this->applyPasswordChecks( $password );
50
- $failed = false;
51
- }
52
- catch ( \Exception $e ) {
53
- $failed = ( $e->getCode() != 999 ); // We don't fail when the PWNED API is not available.
54
- }
55
- $this->setPasswordFailedFlag( $user, $failed );
56
- }
57
- }
58
-
59
- public function onWpLoaded() {
60
- if ( is_admin() && !Services::WpGeneral()->isAjax() && !Services::Request()->isPost()
61
- && Services::WpUsers()->isUserLoggedIn() ) {
62
- $this->processExpiredPassword();
63
- $this->processFailedCheckPassword();
64
- }
65
- }
66
-
67
- /**
68
- * @param \WP_User $user
69
- */
70
- public function onPasswordReset( $user ) {
71
- if ( $user instanceof \WP_User && $user->ID > 0 ) {
72
- $meta = $this->getCon()->getUserMeta( $user );
73
- unset( $meta->pass_hash );
74
- $meta->pass_started_at = 0;
75
- }
76
- }
77
-
78
- private function processExpiredPassword() {
79
- /** @var UserManagement\Options $oOpts */
80
- $oOpts = $this->getOptions();
81
- if ( $oOpts->isPassExpirationEnabled() ) {
82
- $nPassStartedAt = (int)$this->getCon()->getCurrentUserMeta()->pass_started_at;
83
- if ( $nPassStartedAt > 0 ) {
84
- if ( Services::Request()->ts() - $nPassStartedAt > $oOpts->getPassExpireTimeout() ) {
85
- $this->getCon()->fireEvent( 'pass_expired' );
86
- $this->redirectToResetPassword(
87
- sprintf( __( 'Your password has expired (after %s days).', 'wp-simple-firewall' ), $oOpts->getPassExpireDays() )
88
- );
89
- }
90
- }
91
- }
92
- }
93
-
94
- private function processFailedCheckPassword() {
95
- /** @var UserManagement\Options $opts */
96
- $opts = $this->getOptions();
97
- $oMeta = $this->getCon()->getCurrentUserMeta();
98
-
99
- $checkFailed = $opts->isPassForceUpdateExisting()
100
- && isset( $oMeta->pass_check_failed_at ) && $oMeta->pass_check_failed_at > 0;
101
-
102
- if ( $checkFailed ) {
103
- $this->redirectToResetPassword(
104
- __( "Your password doesn't meet requirements set by your security administrator.", 'wp-simple-firewall' )
105
- );
106
- }
107
- }
108
-
109
- /**
110
- * IMPORTANT: User must be logged-in for this to work correctly
111
- * We have a 2 minute delay between redirects because some custom user forms redirect to custom
112
- * password reset pages. This prevents users following this flow.
113
- * @param string $sMessage
114
- * @uses wp_redirect()
115
- */
116
- private function redirectToResetPassword( $sMessage ) {
117
- $nNow = Services::Request()->ts();
118
-
119
- $oMeta = $this->getCon()->getCurrentUserMeta();
120
- $nLastRedirect = (int)$oMeta->pass_reset_last_redirect_at;
121
- if ( $nNow - $nLastRedirect > MINUTE_IN_SECONDS*2 ) {
122
-
123
- $oMeta->pass_reset_last_redirect_at = $nNow;
124
-
125
- $oWpUsers = Services::WpUsers();
126
- $sAction = Services::Request()->query( 'action' );
127
- $oUser = $oWpUsers->getCurrentWpUser();
128
- if ( $oUser && ( !Services::WpGeneral()->isLoginUrl() || !in_array( $sAction, [ 'rp', 'resetpass' ] ) ) ) {
129
-
130
- $sMessage .= ' '.__( 'For your security, please use the password section below to update your password.', 'wp-simple-firewall' );
131
- $this->getMod()
132
- ->setFlashAdminNotice( $sMessage, true, true );
133
- $this->getCon()->fireEvent( 'password_policy_force_change' );
134
- Services::Response()->redirect( $oWpUsers->getPasswordResetUrl( $oUser ) );
135
- }
136
- }
137
- }
138
-
139
- /**
140
- * @param \WP_Error $oErrors
141
- * @return \WP_Error
142
- */
143
- public function checkPassword( $oErrors ) {
144
- $aExistingCodes = $oErrors->get_error_code();
145
- if ( empty( $aExistingCodes ) ) {
146
- $password = $this->getLoginPassword();
147
- if ( !empty( $password ) ) {
148
- $aFailureMsg = '';
149
- try {
150
- $this->applyPasswordChecks( $password );
151
- $bChecksPassed = true;
152
- }
153
- catch ( \Exception $oE ) {
154
- $bChecksPassed = ( $oE->getCode() === 999 );
155
- $aFailureMsg = $oE->getMessage();
156
- }
157
-
158
- if ( $bChecksPassed ) {
159
- if ( Services::WpUsers()->isUserLoggedIn() ) {
160
- $this->getCon()->getCurrentUserMeta()->pass_check_failed_at = 0;
161
- }
162
- }
163
- else {
164
- $sMessage = __( 'Your security administrator has imposed requirements for password quality.', 'wp-simple-firewall' );
165
- if ( !empty( $aFailureMsg ) ) {
166
- $sMessage .= '<br/>'.sprintf( __( 'Reason', 'wp-simple-firewall' ).': '.$aFailureMsg );
167
- }
168
- $oErrors->add( 'shield_password_policy', $sMessage );
169
- $this->getCon()->fireEvent( 'password_policy_block' );
170
- }
171
- }
172
- }
173
-
174
- return $oErrors;
175
- }
176
-
177
- /**
178
- * @param string $password
179
- * @throws \Exception
180
- */
181
- protected function applyPasswordChecks( string $password ) {
182
- /** @var UserManagement\Options $opts */
183
- $opts = $this->getOptions();
184
-
185
- if ( $opts->getPassMinLength() > 0 ) {
186
- $this->testPasswordMeetsMinimumLength( $password, $opts->getPassMinLength() );
187
- }
188
- if ( $opts->getPassMinStrength() > 0 ) {
189
- $this->testPasswordMeetsMinimumStrength( $password, $opts->getPassMinStrength() );
190
- }
191
- if ( $opts->isPassPreventPwned() ) {
192
- $this->sendRequestToPwnedRange( $password );
193
- }
194
- }
195
-
196
- /**
197
- * @param string $password
198
- * @param int $min
199
- * @return bool
200
- * @throws \Exception
201
- */
202
- private function testPasswordMeetsMinimumLength( string $password, int $min ) {
203
- $length = strlen( $password );
204
- if ( $length < $min ) {
205
- throw new \Exception( sprintf( __( 'Password length (%s) too short (min: %s characters)', 'wp-simple-firewall' ), $length, $min ) );
206
- }
207
- return true;
208
- }
209
-
210
- /**
211
- * @param string $password
212
- * @param int $min
213
- * @return bool
214
- * @throws \Exception
215
- */
216
- private function testPasswordMeetsMinimumStrength( string $password, int $min ) {
217
- $aResults = ( new \ZxcvbnPhp\Zxcvbn() )->passwordStrength( $password );
218
-
219
- $nScore = $aResults[ 'score' ];
220
-
221
- if ( $nScore < $min ) {
222
- /** @var UserManagement\ModCon $mod */
223
- $mod = $this->getMod();
224
- throw new \Exception( sprintf( "Password strength (%s) doesn't meet the minimum required strength (%s).",
225
- $mod->getPassStrengthName( $nScore ), $mod->getPassStrengthName( $min ) ) );
226
- }
227
- return true;
228
- }
229
-
230
- /**
231
- * Unused
232
- * @return bool
233
- * private function verifyApiAccess() {
234
- * try {
235
- * $this->sendRequestToPwnedRange( 'P@ssw0rd' );
236
- * }
237
- * catch ( \Exception $oE ) {
238
- * return false;
239
- * }
240
- * return true;
241
- * }
242
- */
243
-
244
- /**
245
- * @param string $password
246
- * @return bool
247
- * @throws \Exception
248
- */
249
- private function sendRequestToPwnedRange( string $password ) {
250
- $oHttpReq = Services::HttpRequest();
251
-
252
- $sPassHash = strtoupper( hash( 'sha1', $password ) );
253
- $sSubHash = substr( $sPassHash, 0, 5 );
254
-
255
- $bSuccess = $oHttpReq->get(
256
- sprintf( '%s/%s', $this->getOptions()->getDef( 'pwned_api_url_password_range' ), $sSubHash ),
257
- [
258
- 'headers' => [ 'user-agent' => sprintf( '%s WP Plugin-v%s', 'Shield', $this->getCon()->getVersion() ) ]
259
- ]
260
- );
261
-
262
- $sError = '';
263
- $nErrorCode = 2; // Default To Error
264
- if ( !$bSuccess ) {
265
- $sError = 'API request failed';
266
- $nErrorCode = 999; // We don't fail PWNED passwords on failed API requests.
267
- }
268
- else {
269
- $nHttpCode = $oHttpReq->lastResponse->getCode();
270
- if ( empty( $nHttpCode ) ) {
271
- $sError = 'Unexpected Error: No response code available from the Pwned API';
272
- }
273
- elseif ( $nHttpCode != 200 ) {
274
- $sError = 'Unexpected Error: The response from the Pwned API was unexpected';
275
- }
276
- elseif ( empty( $oHttpReq->lastResponse->body ) ) {
277
- $sError = 'Unexpected Error: The response from the Pwned API was empty';
278
- }
279
- else {
280
- $nPwnedCount = 0;
281
- foreach ( array_map( 'trim', explode( "\n", trim( $oHttpReq->lastResponse->body ) ) ) as $sRow ) {
282
- if ( $sSubHash.substr( strtoupper( $sRow ), 0, 35 ) == $sPassHash ) {
283
- $nPwnedCount = substr( $sRow, 36 );
284
- break;
285
- }
286
- }
287
- if ( $nPwnedCount > 0 ) {
288
- $sError = __( 'Please use a different password.', 'wp-simple-firewall' )
289
- .'<br/>'.__( 'This password has been pwned.', 'wp-simple-firewall' )
290
- .' '.sprintf(
291
- '(<a href="%s" target="_blank">%s</a>)',
292
- 'https://www.troyhunt.com/ive-just-launched-pwned-passwords-version-2/',
293
- sprintf( __( '%s times', 'wp-simple-firewall' ), $nPwnedCount )
294
- );
295
- }
296
- else {
297
- // Success: Password is not pwned
298
- $nErrorCode = 0;
299
- }
300
- }
301
- }
302
-
303
- if ( $nErrorCode != 0 ) {
304
- throw new \Exception( '[Pwned Request] '.$sError, $nErrorCode );
305
- }
306
-
307
- return true;
308
- }
309
-
310
- private function getLoginPassword() :string {
311
- $sPass = '';
312
-
313
- // Edd: edd_user_pass; Woo: password;
314
- foreach ( [ 'pwd', 'pass1' ] as $key ) {
315
- $sP = Services::Request()->post( $key );
316
- if ( !empty( $sP ) ) {
317
- $sPass = $sP;
318
- break;
319
- }
320
- }
321
- return $sPass;
322
- }
323
-
324
- /**
325
- * @param \WP_User $user
326
- * @param bool $failed
327
- */
328
- private function setPasswordFailedFlag( \WP_User $user, bool $failed = false ) {
329
- $this->getCon()
330
- ->getUserMeta( $user )
331
- ->pass_check_failed_at = $failed ? Services::Request()->ts() : 0;
332
- }
333
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/processors/usermanagement_sessions.php DELETED
@@ -1,186 +0,0 @@
1
- <?php
2
-
3
- use FernleafSystems\Wordpress\Plugin\Shield\Modules;
4
- use FernleafSystems\Wordpress\Plugin\Shield\Modules\UserManagement;
5
- use FernleafSystems\Wordpress\Services\Services;
6
-
7
- /**
8
- * @deprecated 10.1
9
- */
10
- class ICWP_WPSF_Processor_UserManagement_Sessions extends Modules\BaseShield\ShieldProcessor {
11
-
12
- public function run() {
13
- add_filter( 'wp_login_errors', [ $this, 'addLoginMessage' ] );
14
- add_filter( 'auth_cookie_expiration', [ $this, 'setMaxAuthCookieExpiration' ], 100, 1 );
15
- }
16
-
17
- /**
18
- * Cron callback
19
- */
20
- public function runDailyCron() {
21
- ( new UserManagement\Lib\CleanExpired() )
22
- ->setMod( $this->getMod() )
23
- ->run();
24
- }
25
-
26
- /**
27
- * @param string $username
28
- * @param \WP_User $user
29
- */
30
- public function onWpLogin( $username, $user ) {
31
- if ( !$user instanceof \WP_User ) {
32
- $user = Services::WpUsers()->getUserByUsername( $username );
33
- }
34
- $this->enforceSessionLimits( $user );
35
- }
36
-
37
- /**
38
- * @param string $sCookie
39
- * @param int $nExpire
40
- * @param int $nExpiration
41
- * @param int $nUserId
42
- */
43
- public function onWpSetLoggedInCookie( $sCookie, $nExpire, $nExpiration, $nUserId ) {
44
- $this->enforceSessionLimits( Services::WpUsers()->getUserById( $nUserId ) );
45
- }
46
-
47
- public function onWpLoaded() {
48
- if ( Services::WpUsers()->isUserLoggedIn() && !Services::Rest()->isRest() ) {
49
- $this->checkCurrentSession();
50
- }
51
- }
52
-
53
- private function checkCurrentSession() {
54
- $con = $this->getCon();
55
- /** @var UserManagement\ModCon $mod */
56
- $mod = $this->getMod();
57
- try {
58
- if ( $mod->hasValidRequestIP() ) {
59
- $this->assessSession();
60
- }
61
- }
62
- catch ( \Exception $e ) {
63
- $event = $e->getMessage();
64
- $con->fireEvent( $event );
65
- $con->getModule_Sessions()
66
- ->getSessionCon()
67
- ->terminateCurrentSession();
68
- $WPU = Services::WpUsers();
69
- is_admin() ? $WPU->forceUserRelogin( [ 'shield-forcelogout' => $event ] ) : $WPU->logoutUser( true );
70
- }
71
- }
72
-
73
- /**
74
- * @throws \Exception
75
- */
76
- private function assessSession() {
77
- /** @var UserManagement\Options $oOpts */
78
- $oOpts = $this->getOptions();
79
-
80
- $sess = $this->getMod()->getSession();
81
- if ( empty( $sess ) ) {
82
- throw new \Exception( 'session_notfound' );
83
- }
84
-
85
- $nTime = Services::Request()->ts();
86
-
87
- if ( $oOpts->hasMaxSessionTimeout() && ( $nTime - $sess->logged_in_at > $oOpts->getMaxSessionTime() ) ) {
88
- throw new \Exception( 'session_expired' );
89
- }
90
-
91
- if ( $oOpts->hasSessionIdleTimeout() && ( $nTime - $sess->last_activity_at > $oOpts->getIdleTimeoutInterval() ) ) {
92
- throw new \Exception( 'session_idle' );
93
- }
94
-
95
- $oIP = Services::IP();
96
- if ( $oOpts->isLockToIp() && $oIP->getRequestIp() != $sess->ip ) {
97
- // We force-refresh the server IPs just to be sure.
98
- Services::IP()->getServerPublicIPs( true );
99
- if ( !$oIP->isLoopback() ) {
100
- throw new \Exception( 'session_iplock' );
101
- }
102
- }
103
- // TODO: 'session_browserlock';
104
- }
105
-
106
- /**
107
- * @param int $nTimeout
108
- * @return int
109
- */
110
- public function setMaxAuthCookieExpiration( $nTimeout ) {
111
- /** @var UserManagement\Options $oOpts */
112
- $oOpts = $this->getOptions();
113
- return $oOpts->hasMaxSessionTimeout() ? min( $nTimeout, $oOpts->getMaxSessionTime() ) : $nTimeout;
114
- }
115
-
116
- /**
117
- * @param \WP_User $user
118
- */
119
- protected function enforceSessionLimits( $user ) {
120
- /** @var UserManagement\Options $opts */
121
- $opts = $this->getOptions();
122
-
123
- $nSessionLimit = $opts->getOpt( 'session_username_concurrent_limit', 1 );
124
- if ( !$this->isLoginCaptured() && $nSessionLimit > 0 && $user instanceof WP_User ) {
125
- $this->setLoginCaptured();
126
- try {
127
- $this->getMod()
128
- ->getDbHandler_Sessions()
129
- ->getQueryDeleter()
130
- ->addWhere( 'wp_username', $user->user_login )
131
- ->deleteExcess( $nSessionLimit, 'last_activity_at', true );
132
- }
133
- catch ( \Exception $e ) {
134
- }
135
- }
136
- }
137
-
138
- /**
139
- * @param \WP_Error $error
140
- * @return \WP_Error
141
- */
142
- public function addLoginMessage( $error ) {
143
-
144
- if ( !$error instanceof \WP_Error ) {
145
- $error = new \WP_Error();
146
- }
147
-
148
- $sForceLogout = Services::Request()->query( 'shield-forcelogout' );
149
- if ( $sForceLogout ) {
150
-
151
- switch ( $sForceLogout ) {
152
- case 'session_expired':
153
- $sMessage = __( 'Your session has expired.', 'wp-simple-firewall' );
154
- break;
155
-
156
- case 'session_idle':
157
- $sMessage = __( 'Your session was idle for too long.', 'wp-simple-firewall' );
158
- break;
159
-
160
- case 'session_iplock':
161
- $sMessage = __( 'Your session was locked to another IP Address.', 'wp-simple-firewall' );
162
- break;
163
-
164
- case 'session_notfound':
165
- $sMessage = sprintf(
166
- __( 'You do not currently have a %s user session.', 'wp-simple-firewall' ),
167
- $this->getCon()->getHumanName()
168
- );
169
- break;
170
-
171
- case 'session_browserlock':
172
- $sMessage = __( 'Your browser appears to have changed for this session.', 'wp-simple-firewall' );
173
- break;
174
-
175
- case 'session_unverified':
176
- default:
177
- $sMessage = __( 'Your session was terminated.', 'wp-simple-firewall' );
178
- break;
179
- }
180
-
181
- $sMessage .= '<br />'.__( 'Please login again.', 'wp-simple-firewall' );
182
- $error->add( 'shield-forcelogout', $sMessage );
183
- }
184
- return $error;
185
- }
186
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/wizards/base.php CHANGED
@@ -46,7 +46,7 @@ abstract class ICWP_WPSF_Wizard_Base {
46
  $aResponse[ 'message' ] = 'Please login to run this wizard.';
47
  }
48
  }
49
- catch ( Exception $oE ) {
50
  }
51
 
52
  return $aResponse;
@@ -70,7 +70,7 @@ abstract class ICWP_WPSF_Wizard_Base {
70
 
71
  Services::WpGeneral()->wpDie( $sDieMessage );
72
  }
73
- catch ( Exception $oE ) {
74
  if ( $sWizard == 'landing' ) {
75
  $this->loadWizardLanding();
76
  }
@@ -82,12 +82,12 @@ abstract class ICWP_WPSF_Wizard_Base {
82
  */
83
  protected function loadWizard() {
84
  try {
85
- $sContent = $this->renderWizard();
86
  }
87
- catch ( Exception $oE ) {
88
- $sContent = $oE->getMessage();
89
  }
90
- echo $sContent;
91
  die();
92
  }
93
 
@@ -96,17 +96,17 @@ abstract class ICWP_WPSF_Wizard_Base {
96
  */
97
  public function renderWizardLandingPage() {
98
  try {
99
- $sContent = $this->getMod()
100
  ->renderTemplate(
101
  'wizard/pages/landing.twig',
102
  $this->getRenderData_PageWizardLanding(),
103
  true
104
  );
105
  }
106
- catch ( Exception $oE ) {
107
- $sContent = $oE->getMessage();
108
  }
109
- return $sContent;
110
  }
111
 
112
  /**
@@ -114,17 +114,17 @@ abstract class ICWP_WPSF_Wizard_Base {
114
  */
115
  public function renderWizardLandingSnippet() {
116
  try {
117
- $sContent = $this->getMod()
118
  ->renderTemplate(
119
  'wizard/snippets/wizard_landing.twig',
120
  $this->getRenderData_PageWizardLanding(),
121
  true
122
  );
123
  }
124
- catch ( Exception $oE ) {
125
- $sContent = $oE->getMessage();
126
  }
127
- return $sContent;
128
  }
129
 
130
  /**
@@ -366,8 +366,8 @@ abstract class ICWP_WPSF_Wizard_Base {
366
  try {
367
  $aNextStepDef[ 'content' ] = $this->renderWizardStep( $aNextStepDef[ 'slug' ] );
368
  }
369
- catch ( Exception $oE ) {
370
- $aNextStepDef[ 'content' ] = 'Content could not be displayed due to error: '.$oE->getMessage();
371
  }
372
 
373
  return $aNextStepDef;
46
  $aResponse[ 'message' ] = 'Please login to run this wizard.';
47
  }
48
  }
49
+ catch ( Exception $e ) {
50
  }
51
 
52
  return $aResponse;
70
 
71
  Services::WpGeneral()->wpDie( $sDieMessage );
72
  }
73
+ catch ( \Exception $e ) {
74
  if ( $sWizard == 'landing' ) {
75
  $this->loadWizardLanding();
76
  }
82
  */
83
  protected function loadWizard() {
84
  try {
85
+ $content = $this->renderWizard();
86
  }
87
+ catch ( \Exception $e ) {
88
+ $content = $e->getMessage();
89
  }
90
+ echo $content;
91
  die();
92
  }
93
 
96
  */
97
  public function renderWizardLandingPage() {
98
  try {
99
+ $content = $this->getMod()
100
  ->renderTemplate(
101
  'wizard/pages/landing.twig',
102
  $this->getRenderData_PageWizardLanding(),
103
  true
104
  );
105
  }
106
+ catch ( \Exception $e ) {
107
+ $content = $e->getMessage();
108
  }
109
+ return $content;
110
  }
111
 
112
  /**
114
  */
115
  public function renderWizardLandingSnippet() {
116
  try {
117
+ $content = $this->getMod()
118
  ->renderTemplate(
119
  'wizard/snippets/wizard_landing.twig',
120
  $this->getRenderData_PageWizardLanding(),
121
  true
122
  );
123
  }
124
+ catch ( \Exception $e ) {
125
+ $content = $e->getMessage();
126
  }
127
+ return $content;
128
  }
129
 
130
  /**
366
  try {
367
  $aNextStepDef[ 'content' ] = $this->renderWizardStep( $aNextStepDef[ 'slug' ] );
368
  }
369
+ catch ( \Exception $e ) {
370
+ $aNextStepDef[ 'content' ] = 'Content could not be displayed due to error: '.$e->getMessage();
371
  }
372
 
373
  return $aNextStepDef;
src/wizards/plugin.php CHANGED
@@ -365,27 +365,27 @@ class ICWP_WPSF_Wizard_Plugin extends ICWP_WPSF_Wizard_BaseWpsf {
365
  */
366
  private function wizardLicense() {
367
 
368
- $bSuccess = false;
369
 
370
  $mod = $this->getCon()->getModule_License();
371
  try {
372
- $bSuccess = $mod->getLicenseHandler()
373
  ->verify( true )
374
  ->hasValidWorkingLicense();
375
- if ( $bSuccess ) {
376
- $sMessage = __( 'License was found and successfully installed.', 'wp-simple-firewall' );
377
  }
378
  else {
379
- $sMessage = __( 'License could not be found.', 'wp-simple-firewall' );
380
  }
381
  }
382
- catch ( Exception $oE ) {
383
- $sMessage = __( $oE->getMessage(), 'wp-simple-firewall' );
384
  }
385
 
386
  return ( new \FernleafSystems\Utilities\Response() )
387
- ->setSuccessful( $bSuccess )
388
- ->setMessageText( $sMessage );
389
  }
390
 
391
  /**
@@ -403,9 +403,9 @@ class ICWP_WPSF_Wizard_Plugin extends ICWP_WPSF_Wizard_BaseWpsf {
403
  ->setMod( $this->getMod() )
404
  ->fromSite( $sMasterSiteUrl, $sSecretKey, $bEnabledNetwork );
405
  }
406
- catch ( Exception $oE ) {
407
- $sSiteResponse = $oE->getMessage();
408
- $nCode = $oE->getCode();
409
  }
410
 
411
  $aErrors = [
@@ -451,8 +451,8 @@ class ICWP_WPSF_Wizard_Plugin extends ICWP_WPSF_Wizard_BaseWpsf {
451
  $bSuccess = true;
452
  $sMessage = __( 'Security Admin PIN setup was successful.', 'wp-simple-firewall' );
453
  }
454
- catch ( Exception $oE ) {
455
- $sMessage = __( $oE->getMessage(), 'wp-simple-firewall' );
456
  }
457
  }
458
 
@@ -762,7 +762,7 @@ class ICWP_WPSF_Wizard_Plugin extends ICWP_WPSF_Wizard_BaseWpsf {
762
  ->addWhereSearch( 'message', $sItem )
763
  ->query();
764
  }
765
- catch ( \Exception $oE ) {
766
  $aResults = [];
767
  }
768
  // $aResults = array_intersect_key( $aResults, array_flip( [ 'wp_username', 'message' ] ) );
365
  */
366
  private function wizardLicense() {
367
 
368
+ $success = false;
369
 
370
  $mod = $this->getCon()->getModule_License();
371
  try {
372
+ $success = $mod->getLicenseHandler()
373
  ->verify( true )
374
  ->hasValidWorkingLicense();
375
+ if ( $success ) {
376
+ $msg = __( 'License was found and successfully installed.', 'wp-simple-firewall' );
377
  }
378
  else {
379
+ $msg = __( 'License could not be found.', 'wp-simple-firewall' );
380
  }
381
  }
382
+ catch ( Exception $e ) {
383
+ $msg = __( $e->getMessage(), 'wp-simple-firewall' );
384
  }
385
 
386
  return ( new \FernleafSystems\Utilities\Response() )
387
+ ->setSuccessful( $success )
388
+ ->setMessageText( $msg );
389
  }
390
 
391
  /**
403
  ->setMod( $this->getMod() )
404
  ->fromSite( $sMasterSiteUrl, $sSecretKey, $bEnabledNetwork );
405
  }
406
+ catch ( Exception $e ) {
407
+ $sSiteResponse = $e->getMessage();
408
+ $nCode = $e->getCode();
409
  }
410
 
411
  $aErrors = [
451
  $bSuccess = true;
452
  $sMessage = __( 'Security Admin PIN setup was successful.', 'wp-simple-firewall' );
453
  }
454
+ catch ( \Exception $e ) {
455
+ $sMessage = __( $e->getMessage(), 'wp-simple-firewall' );
456
  }
457
  }
458
 
762
  ->addWhereSearch( 'message', $sItem )
763
  ->query();
764
  }
765
+ catch ( \Exception $e ) {
766
  $aResults = [];
767
  }
768
  // $aResults = array_intersect_key( $aResults, array_flip( [ 'wp_username', 'message' ] ) );
templates/php/snippets/module-help-firewall.php CHANGED
@@ -1,5 +1,5 @@
1
  <h5>What is the Firewall?</h5>
2
  <p>This is a big topic and is best covered in-detail in our helpdesk section.</p>
3
  <p><a href="https://shsec.io/ak" target="_blank">
4
- https://icontrolwp.freshdesk.com/support/solutions/folders/3000000263</a> .
5
  </p>
1
  <h5>What is the Firewall?</h5>
2
  <p>This is a big topic and is best covered in-detail in our helpdesk section.</p>
3
  <p><a href="https://shsec.io/ak" target="_blank">
4
+ https://support.getshieldsecurity.com/support/solutions/folders/3000000263</a> .
5
  </p>
templates/php/snippets/plugin-vulnerability.php DELETED
@@ -1,25 +0,0 @@
1
- <tr class="icwp-plugin-vulnerability plugin-update-tr">
2
- <td colspan="<?php echo $nColspan; ?>" class="colspanchange">
3
- <p class="vuln-title"><?php echo $strings[ 'known_vuln' ]; ?></p>
4
- <table>
5
- <tr>
6
- <th><?php echo $strings['name'];?></th>
7
- <th><?php echo $strings['type'];?></th>
8
- <th><?php echo $strings['fixed_versions'];?></th>
9
- <th><?php echo $strings['more_info'];?></th>
10
- </tr>
11
- <?php foreach ( $vulns as $oVuln ) :
12
- /** @var \FernleafSystems\Wordpress\Plugin\Shield\Scans\Wpv\WpVulnDb\WpVulnVO $oVuln */ ?>
13
- <tr>
14
- <td><?php echo $oVuln->title; ?></td>
15
- <td><?php echo $oVuln->vuln_type; ?></td>
16
- <td><?php echo $oVuln->fixed_in; ?></td>
17
- <td><a href="<?php echo $oVuln->getUrl(); ?>" target="_blank">
18
- <?php echo $strings[ 'more_info' ]; ?>
19
- </a>
20
- </td>
21
- </tr>
22
- <?php endforeach; ?>
23
- </table>
24
- </td>
25
- </tr>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
templates/twig/components/options_form/main.twig CHANGED
@@ -1,8 +1,8 @@
1
  <form action="{{ form_action }}" method="post" class="icwpOptionsForm form" novalidate="novalidate"
2
  autocomplete="off">
3
 
4
- <div id="ModuleOptionsNav" class="insights-sub-nav" role="tablist" aria-orientation="horizontal">
5
- <ul class="nav nav-tabs">
6
  {% for opt_sect_key,opt_section in data.all_options %}
7
  <li class="nav-item">
8
 
1
  <form action="{{ form_action }}" method="post" class="icwpOptionsForm form" novalidate="novalidate"
2
  autocomplete="off">
3
 
4
+ <div id="ModuleOptionsNav" class="insights-sub-nav" aria-orientation="horizontal">
5
+ <ul class="nav nav-tabs" role="tablist">
6
  {% for opt_sect_key,opt_section in data.all_options %}
7
  <li class="nav-item">
8
 
templates/twig/snippets/js/freshdesk_chatbot.twig ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <script> (function ( d, w, c ) {
2
+ if ( !d.getElementById( "spd-busns-spt" ) ) {
3
+ var n = d.getElementsByTagName( 'script' )[ 0 ],
4
+ s = d.createElement( 'script' );
5
+ var loaded = false;
6
+ s.id = "spd-busns-spt";
7
+ s.async = "async";
8
+ s.src = "https://cdn.freshbots.ai/assets/share/js/fbotsChat.min.js";
9
+ s.setAttribute( "data-prdct-hash", "72739a6abc259fc06bd3aae6d959178cf1ee37c7" );
10
+ s.setAttribute( "data-region", "us" );
11
+ s.setAttribute( "data-ext-client-id", "72d53bdc450411eb99f74a925608a346" );
12
+ if ( c ) {
13
+ s.onreadystatechange = s.onload = function () {
14
+ if ( !loaded ) {
15
+ c();
16
+ }
17
+ loaded = true;
18
+ };
19
+ }
20
+ n.parentNode.insertBefore( s, n );
21
+ }
22
+ })( document, window ); </script>
templates/twig/snippets/plugin_vulnerability.twig ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <tr class="icwp-plugin-vulnerability plugin-update-tr">
2
+ <td colspan="{{ vars.colspan }}" class="colspanchange">
3
+ <p class="vuln-title">{{ strings.known_vuln|raw }}</p>
4
+ <table>
5
+ <tr>
6
+ <th>{{ strings.name }}</th>
7
+ <th>{{ strings.type }}</th>
8
+ <th>{{ strings.fixed_versions }}</th>
9
+ <th>{{ strings.more_info }}</th>
10
+ </tr>
11
+ {% for vuln in vars.vulns %}
12
+ <tr>
13
+ <td>{{ vuln.title }}</td>
14
+ <td>{{ vuln.vuln_type }}</td>
15
+ <td>{{ vuln.fixed_in }}</td>
16
+ <td><a href="{{ vuln.url }}" target="_blank">{{ strings.more_info }}</a></td>
17
+ </tr>
18
+ {% endfor %}
19
+ </table>
20
+ </td>
21
+ </tr>
templates/twig/wpadmin_pages/insights/base.twig CHANGED
@@ -46,7 +46,7 @@
46
  } );
47
  </script>
48
  <script async src="https://cdn.announcekit.app/widget.js"></script>
49
- {% include '/snippets/js/widget_freshdesk.twig' %}
50
  {% endblock %}
51
 
52
  {% block page_main %}
46
  } );
47
  </script>
48
  <script async src="https://cdn.announcekit.app/widget.js"></script>
49
+ {% include '/snippets/js/freshdesk_chatbot.twig' %}
50
  {% endblock %}
51
 
52
  {% block page_main %}
templates/twig/wpadmin_pages/insights/docs/index.twig CHANGED
@@ -7,14 +7,16 @@
7
  <ul class="nav nav-tabs">
8
 
9
  <li class="nav-item">
10
- <a class="nav-link active" data-toggle="tab" role="tab" href="#tabUpdates">
 
11
  {{ strings.tab_updates }}
12
  </a>
13
  </li>
14
 
15
  {% if not flags.is_pro %}
16
  <li class="nav-item">
17
- <a class="nav-link" data-toggle="tab" role="tab" href="#tabFreeTrial">
 
18
  {{ strings.tab_freetrial }}
19
  </a>
20
  </li>
7
  <ul class="nav nav-tabs">
8
 
9
  <li class="nav-item">
10
+ <a class="nav-link active" data-toggle="tab" role="tab" href="#tabUpdates"
11
+ data-bs-toggle="tab">
12
  {{ strings.tab_updates }}
13
  </a>
14
  </li>
15
 
16
  {% if not flags.is_pro %}
17
  <li class="nav-item">
18
+ <a class="nav-link" data-toggle="tab" role="tab" href="#tabFreeTrial"
19
+ data-bs-toggle="tab">
20
  {{ strings.tab_freetrial }}
21
  </a>
22
  </li>
templates/twig/wpadmin_pages/insights/importexport/index.twig CHANGED
@@ -6,18 +6,21 @@
6
  <div class="col insights-sub-nav insights_section">
7
  <ul class="nav nav-tabs">
8
  <li class="nav-item">
9
- <a class="nav-link active" data-toggle="tab" role="tab" href="#byFile">
 
10
  {{ strings.tab_by_file }}
11
  </a>
12
  </li>
13
  <li class="nav-item">
14
- <a class="nav-link" data-toggle="tab" role="tab" href="#bySite">
 
15
  {{ strings.tab_by_site }}
16
  </a>
17
  </li>
18
  </ul>
19
  </div>
20
  </div>
 
21
  <div class="tab-content">
22
  <div class="tab-pane fade show active" id="byFile" role="tabpanel" aria-labelledby="byFile">
23
  <div class="row">
6
  <div class="col insights-sub-nav insights_section">
7
  <ul class="nav nav-tabs">
8
  <li class="nav-item">
9
+ <a class="nav-link active" data-toggle="tab" role="tab" href="#byFile" aria-selected="true"
10
+ data-bs-toggle="tab">
11
  {{ strings.tab_by_file }}
12
  </a>
13
  </li>
14
  <li class="nav-item">
15
+ <a class="nav-link" data-toggle="tab" role="tab" href="#bySite"
16
+ data-bs-toggle="tab">
17
  {{ strings.tab_by_site }}
18
  </a>
19
  </li>
20
  </ul>
21
  </div>
22
  </div>
23
+
24
  <div class="tab-content">
25
  <div class="tab-pane fade show active" id="byFile" role="tabpanel" aria-labelledby="byFile">
26
  <div class="row">
templates/twig/wpadmin_pages/insights/ips/index.twig CHANGED
@@ -3,19 +3,22 @@
3
  {% block page_main %}
4
  <div class="row">
5
  <div class="col insights-sub-nav insights_section">
6
- <ul class="nav nav-tabs" id="TabsIps">
7
  <li class="nav-item">
8
- <a class="nav-link active" data-toggle="tab" role="tab" href="#ipanalysis">
 
9
  {{ strings.tab_ip_analysis }}
10
  </a>
11
  </li>
12
  <li class="nav-item">
13
- <a class="nav-link" data-toggle="tab" role="tab" href="#blocklist">
 
14
  {{ strings.tab_manage_block }}
15
  </a>
16
  </li>
17
  <li class="nav-item">
18
- <a class="nav-link" data-toggle="tab" role="tab" href="#allowedlist">
 
19
  {{ strings.tab_manage_bypass }}
20
  </a>
21
  </li>
3
  {% block page_main %}
4
  <div class="row">
5
  <div class="col insights-sub-nav insights_section">
6
+ <ul class="nav nav-tabs" id="TabsIps" role="tablist">
7
  <li class="nav-item">
8
+ <a class="nav-link active" data-toggle="tab" role="tab" href="#ipanalysis"
9
+ data-bs-toggle="tab">
10
  {{ strings.tab_ip_analysis }}
11
  </a>
12
  </li>
13
  <li class="nav-item">
14
+ <a class="nav-link" data-toggle="tab" role="tab" href="#blocklist"
15
+ data-bs-toggle="tab">
16
  {{ strings.tab_manage_block }}
17
  </a>
18
  </li>
19
  <li class="nav-item">
20
+ <a class="nav-link" data-toggle="tab" role="tab" href="#allowedlist"
21
+ data-bs-toggle="tab">
22
  {{ strings.tab_manage_bypass }}
23
  </a>
24
  </li>
templates/twig/wpadmin_pages/insights/ips/ip_analyse/ip_info.twig CHANGED
@@ -5,22 +5,26 @@
5
 
6
  <ul class="nav nav-pills card-header-pills mr-auto">
7
  <li class="nav-item">
8
- <a class="nav-link active" data-toggle="tab" role="tab" href="#tabIpInfoGeneral">
 
9
  {{ strings.nav_general }}
10
  </a>
11
  </li>
12
  <li class="nav-item">
13
- <a class="nav-link" data-toggle="tab" role="tab" href="#tabIpInfoSessions">
 
14
  {{ strings.nav_sessions }}
15
  </a>
16
  </li>
17
  <li class="nav-item">
18
- <a class="nav-link" data-toggle="tab" role="tab" href="#tabIpInfoAudit">
 
19
  {{ strings.nav_audit }}
20
  </a>
21
  </li>
22
  <li class="nav-item">
23
- <a class="nav-link" data-toggle="tab" role="tab" href="#tabIpInfoTraffic">
 
24
  {{ strings.nav_traffic }}
25
  </a>
26
  </li>
5
 
6
  <ul class="nav nav-pills card-header-pills mr-auto">
7
  <li class="nav-item">
8
+ <a class="nav-link active" data-toggle="tab" role="tab" href="#tabIpInfoGeneral"
9
+ data-bs-toggle="pill">
10
  {{ strings.nav_general }}
11
  </a>
12
  </li>
13
  <li class="nav-item">
14
+ <a class="nav-link" data-toggle="tab" role="tab" href="#tabIpInfoSessions"
15
+ data-bs-toggle="pill">
16
  {{ strings.nav_sessions }}
17
  </a>
18
  </li>
19
  <li class="nav-item">
20
+ <a class="nav-link" data-toggle="tab" role="tab" href="#tabIpInfoAudit"
21
+ data-bs-toggle="pill">
22
  {{ strings.nav_audit }}
23
  </a>
24
  </li>
25
  <li class="nav-item">
26
+ <a class="nav-link" data-toggle="tab" role="tab" href="#tabIpInfoTraffic"
27
+ data-bs-toggle="pill">
28
  {{ strings.nav_traffic }}
29
  </a>
30
  </li>
templates/twig/wpadmin_pages/security_admin/index.twig CHANGED
@@ -77,8 +77,9 @@
77
  {% block inline_styles %}
78
  <style>
79
  #wpwrap {
80
- background-image: url({{ imgs.background }});
81
  background-size:100%;
 
82
  }
83
  #wpcontent,
84
  #wpbody-content {
@@ -88,5 +89,5 @@
88
  {% endblock %}
89
 
90
  {% block inline_scripts %}
91
- {% include '/snippets/js/widget_freshdesk.twig' %}
92
  {% endblock %}
77
  {% block inline_styles %}
78
  <style>
79
  #wpwrap {
80
+ background-image: url({{ imgs.background_svg }});
81
  background-size:100%;
82
+ background-repeat:no-repeat;
83
  }
84
  #wpcontent,
85
  #wpbody-content {
89
  {% endblock %}
90
 
91
  {% block inline_scripts %}
92
+ {% include '/snippets/js/freshdesk_chatbot.twig' %}
93
  {% endblock %}