Shield Security for WordPress - Version 8.4.4

Version Description

  • Current Release = Released: 6th December, 2019 - Release Notes

  • (v.4) IMPROVED: Discovered serious conflict with SiteGround Optimizer plugin. Provided admin notice and automatic fixing.

  • (v.4) FIXED: Protected against spurious error log notices when comparing hashes with "nothing".

Download this release

Release Info

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

Code changes from version 8.3.0 to 8.4.4

Files changed (190) hide show
  1. changelog.html +728 -706
  2. filesnotfound.php +1 -6
  3. icwp-plugin-controller.php +0 -25
  4. icwp-wpsf.php +5 -5
  5. plugin-spec.php +11 -11
  6. readme.txt +38 -115
  7. resources/css/chartist.min.css +1 -1
  8. resources/css/plugin.css +1 -2
  9. resources/js/chartist.min.js +3 -3
  10. resources/js/charts.js +17 -1
  11. resources/js/global-plugin.js +1 -1
  12. resources/js/shield-comments.js +137 -0
  13. src/common/icwp-data.php +5 -5
  14. src/common/icwp-edd.php +0 -172
  15. src/common/icwp-foundation.php +4 -239
  16. src/common/icwp-optionsvo.php +0 -991
  17. src/common/icwp-render.php +0 -316
  18. src/common/icwp-request.php +0 -310
  19. src/common/icwp-serviceproviders.php +11 -16
  20. src/common/icwp-wpdb.php +0 -237
  21. src/common/icwp-wpfilesystem.php +0 -492
  22. src/common/icwp-wpfunctions-plugins.php +0 -547
  23. src/common/icwp-wpfunctions-themes.php +0 -365
  24. src/common/icwp-wpfunctions.php +0 -936
  25. src/common/icwp-wpincludes.php +0 -79
  26. src/common/icwp-wpupgrades.php +0 -358
  27. src/common/wp-admin-notices.php +4 -186
  28. src/common/wp-comments.php +0 -105
  29. src/common/wp-users.php +0 -290
  30. src/common/wp-widget.php +1 -0
  31. src/config/changelog.json +4 -4
  32. src/config/feature-admin_access_restriction.php +23 -22
  33. src/config/feature-audit_trail.php +18 -18
  34. src/config/feature-autoupdates.php +5 -5
  35. src/config/feature-comments_filter.php +19 -19
  36. src/config/feature-firewall.php +3 -3
  37. src/config/feature-hack_protect.php +26 -26
  38. src/config/feature-headers.php +11 -11
  39. src/config/feature-ips.php +25 -24
  40. src/config/feature-license.php +1 -1
  41. src/config/feature-lockdown.php +7 -7
  42. src/config/feature-login_protect.php +28 -28
  43. src/config/feature-plugin.php +19 -10
  44. src/config/feature-traffic.php +4 -4
  45. src/config/feature-user_management.php +12 -12
  46. src/features/admin_access_restriction.php +0 -79
  47. src/features/audit_trail.php +1 -115
  48. src/features/autoupdates.php +158 -137
  49. src/features/base.php +17 -66
  50. src/features/base_wpsf.php +8 -6
  51. src/features/events.php +1 -1
  52. src/features/hack_protect.php +5 -38
  53. src/features/headers.php +2 -1
  54. src/features/insights.php +75 -45
  55. src/features/ips.php +1 -119
  56. src/features/license.php +2 -2
  57. src/features/login_protect.php +3 -3
  58. src/features/plugin.php +11 -12
  59. src/features/sessions.php +1 -1
  60. src/features/statistics.php +1 -1
  61. src/features/traffic.php +1 -9
  62. src/features/user_management.php +0 -9
  63. src/lib/src/AuditTrail/Auditor.php +0 -14
  64. src/lib/src/Controller/Controller.php +14 -0
  65. src/lib/src/Databases/AuditTrail/Handler.php +1 -3
  66. src/lib/src/Databases/AuditTrail/Select.php +0 -12
  67. src/lib/src/Databases/Base/BaseQuery.php +45 -4
  68. src/lib/src/Databases/Events/Common.php +26 -0
  69. src/lib/src/Databases/Events/Delete.php +1 -0
  70. src/lib/src/Databases/Events/Select.php +23 -17
  71. src/lib/src/Databases/IPs/CommonFilters.php +8 -0
  72. src/lib/src/Deprecated/Foundation.php +1 -166
  73. src/lib/src/Modules/Autoupdates/AjaxHandler.php +4 -4
  74. src/lib/src/Modules/Autoupdates/Options.php +133 -0
  75. src/lib/src/Modules/Base/AdminNotices.php +9 -9
  76. src/lib/src/Modules/Base/BaseModCon.php +10 -45
  77. src/lib/src/Modules/Base/BaseProcessor.php +0 -28
  78. src/lib/src/Modules/Base/Options.php +1 -1
  79. src/lib/src/Modules/Base/Strings.php +1 -1
  80. src/lib/src/Modules/CommentsFilter/AjaxHandler.php +42 -0
  81. src/lib/src/Modules/CommentsFilter/Scan/Human.php +1 -1
  82. src/lib/src/Modules/CommentsFilter/Token/Create.php +45 -0
  83. src/lib/src/Modules/Events/AjaxHandler.php +34 -27
  84. src/lib/src/Modules/Events/Charts/BuildData.php +130 -0
  85. src/lib/src/Modules/Events/Charts/ChartRequestVO.php +20 -0
  86. src/lib/src/Modules/Events/Consolidate/ConsolidateAllEvents.php +291 -0
  87. src/lib/src/Modules/HackGuard/Options.php +2 -2
  88. src/lib/src/Modules/HackGuard/Strings.php +1 -1
  89. src/lib/src/Modules/IPs/AjaxHandler.php +3 -1
  90. src/lib/src/Modules/IPs/Components/LookupIpOnList.php +105 -0
  91. src/lib/src/Modules/IPs/Options.php +1 -2
  92. src/lib/src/Modules/Plugin/AdminNotices.php +35 -1
  93. src/lib/src/Modules/Plugin/AjaxHandler.php +20 -3
  94. src/lib/src/Modules/Plugin/Components/BadgeWidget.php +15 -1
  95. src/lib/src/Modules/Plugin/Components/PluginBadge.php +1 -1
  96. src/lib/src/Modules/Plugin/Components/SiteGroundPluginCompatibility.php +69 -0
  97. src/lib/src/Modules/Plugin/Strings.php +1 -1
  98. src/lib/src/Modules/PluginControllerConsumer.php +8 -2
  99. src/lib/src/Modules/SecurityAdmin/Options.php +1 -1
  100. src/lib/src/Scans/Mal/FileScanner.php +30 -57
  101. src/lib/src/Scans/Mal/Scan.php +0 -12
  102. src/lib/src/Scans/Mal/ScanActionVO.php +0 -2
  103. src/lib/src/Scans/Mal/Utilities/FalsePositiveQuery.php +81 -0
  104. src/lib/src/Scans/Mal/Utilities/Signatures.php +1 -0
  105. src/lib/src/Scans/Mal/Utilities/Whitelist.php +1 -0
  106. src/lib/src/Scans/Ptg/Scan.php +0 -1
  107. src/lib/src/Tables/Build/ScanMal.php +1 -1
  108. src/lib/src/Tables/Build/Traffic.php +1 -1
  109. src/lib/src/Tables/Render/Base.php +3 -2
  110. src/lib/src/Utilities/VisitorIpDetection.php +0 -187
  111. src/lib/vendor/composer/autoload_classmap.php +10 -27
  112. src/lib/vendor/composer/autoload_static.php +10 -27
  113. src/lib/vendor/fernleafsystems/wordpress-services/src/Core/Plugins.php +27 -58
  114. src/lib/vendor/fernleafsystems/wordpress-services/src/Core/Themes.php +29 -59
  115. src/lib/vendor/fernleafsystems/wordpress-services/src/Core/Upgrades/UpgraderSkin.php +12 -18
  116. src/lib/vendor/fernleafsystems/wordpress-services/src/Core/Upgrades/UpgraderSkinLegacy.php +38 -0
  117. src/lib/vendor/fernleafsystems/wordpress-services/src/Core/VOs/WpPluginVo.php +7 -0
  118. src/lib/vendor/fernleafsystems/wordpress-services/src/Core/VOs/WpThemeVo.php +7 -0
  119. src/lib/vendor/fernleafsystems/wordpress-services/src/Utilities/DataManipulation.php +8 -0
  120. src/lib/vendor/fernleafsystems/wordpress-services/src/Utilities/File/Compare/CompareHash.php +32 -12
  121. src/lib/vendor/fernleafsystems/wordpress-services/src/Utilities/File/ExtractLinesFromFile.php +1 -3
  122. src/lib/vendor/fernleafsystems/wordpress-services/src/Utilities/Integrations/WpHashes/ApiBase.php +42 -1
  123. src/lib/vendor/fernleafsystems/wordpress-services/src/Utilities/Integrations/WpHashes/Hashes/ClassicPress.php +1 -0
  124. src/lib/vendor/fernleafsystems/wordpress-services/src/Utilities/Integrations/WpHashes/Hashes/PluginThemeBase.php +1 -0
  125. src/lib/vendor/fernleafsystems/wordpress-services/src/Utilities/Integrations/WpHashes/Hashes/WordPress.php +1 -0
  126. src/lib/vendor/fernleafsystems/wordpress-services/src/Utilities/Integrations/WpHashes/Malware/Confidence/Base.php +1 -1
  127. src/lib/vendor/fernleafsystems/wordpress-services/src/Utilities/Integrations/WpHashes/Malware/Confidence/RequestVO.php +1 -0
  128. src/lib/vendor/fernleafsystems/wordpress-services/src/Utilities/Integrations/WpHashes/Malware/Confidence/Retrieve.php +19 -1
  129. src/lib/vendor/fernleafsystems/wordpress-services/src/Utilities/IpUtils.php +29 -12
  130. src/lib/vendor/symfony/polyfill-mbstring/Mbstring.php +13 -2
  131. src/processors/adminaccess_whitelabel.php +1 -1
  132. src/processors/audit_trail.php +1 -1
  133. src/processors/autoupdates.php +55 -52
  134. src/processors/base.php +0 -265
  135. src/processors/base_plugin.php +0 -10
  136. src/processors/base_wpsf.php +0 -198
  137. src/processors/basedb.php +0 -103
  138. src/processors/comments_filter.php +1 -1
  139. src/processors/commentsfilter_botspam.php +81 -44
  140. src/processors/events.php +6 -0
  141. src/processors/firewall.php +1 -1
  142. src/processors/hackprotect_scan_base.php +0 -7
  143. src/processors/hackprotect_scan_mal.php +2 -2
  144. src/processors/hackprotect_scan_ufc.php +1 -1
  145. src/processors/hackprotect_scan_wcf.php +2 -2
  146. src/processors/hackprotect_scanner.php +0 -7
  147. src/processors/ips.php +8 -1
  148. src/processors/lockdown.php +1 -1
  149. src/processors/loginprotect_intentprovider_email.php +2 -2
  150. src/processors/loginprotect_intentprovider_ga.php +1 -1
  151. src/processors/loginprotect_intentprovider_yubikey.php +1 -1
  152. src/processors/loginprotect_wplogin.php +7 -3
  153. src/processors/plugin_tracking.php +3 -3
  154. src/processors/sessions.php +0 -17
  155. src/processors/traffic_logger.php +3 -3
  156. src/processors/usermanagement_passwords.php +14 -27
  157. src/query/base/statistics_base.php +0 -252
  158. src/query/statistics/reporting.php +0 -11
  159. src/wizards/base.php +3 -3
  160. src/wizards/base_wpsf.php +4 -2
  161. src/wizards/login_protect.php +2 -2
  162. src/wizards/plugin.php +4 -4
  163. templates/php/snippets/module-help-admin_access_restriction.php +2 -2
  164. templates/php/snippets/module-help-firewall.php +1 -1
  165. templates/php/snippets/module-help-headers.php +1 -1
  166. templates/php/snippets/module-help-login_protect.php +1 -1
  167. templates/php/snippets/module-help-plugin.php +1 -1
  168. templates/php/snippets/pro.php +0 -310
  169. templates/twig/features/feature-base.twig +1 -1
  170. templates/twig/notices/compat-sgoptimize.twig +25 -0
  171. templates/twig/notices/rate-plugin.twig +1 -1
  172. templates/twig/snippets/comment_form_botbox.twig +1 -82
  173. templates/twig/wizard/slides/welcome/optin.twig +1 -1
  174. templates/twig/wpadmin_pages/base.twig +3 -0
  175. templates/twig/wpadmin_pages/insights/base.twig +3 -0
  176. templates/twig/wpadmin_pages/insights/insights/stats.twig +0 -14
  177. templates/twig/wpadmin_pages/insights/license/license.twig +6 -9
  178. templates/twig/wpadmin_pages/insights/original/audit_trail.twig +0 -34
  179. templates/twig/wpadmin_pages/insights/original/index.twig +0 -202
  180. templates/twig/wpadmin_pages/insights/original/mod_summary.twig +0 -21
  181. templates/twig/wpadmin_pages/insights/original/notices.twig +0 -58
  182. templates/twig/wpadmin_pages/insights/original/recent_events.twig +0 -18
  183. templates/twig/wpadmin_pages/insights/original/stats.twig +0 -20
  184. templates/twig/wpadmin_pages/insights/original/title.twig +0 -23
  185. templates/twig/wpadmin_pages/insights/{insights → overview}/index.twig +52 -7
  186. templates/twig/wpadmin_pages/insights/{insights → overview}/notices.twig +0 -0
  187. templates/twig/wpadmin_pages/insights/{insights → overview}/recent_events.twig +0 -0
  188. templates/twig/wpadmin_pages/insights/overview/stats.twig +19 -0
  189. templates/twig/wpadmin_pages/insights/reports/index.twig +2 -4
  190. unsupported.php +1 -1
changelog.html CHANGED
@@ -1,675 +1,699 @@
1
- <!DOCTYPE html><html><head><meta charset="utf-8"><title>Untitled Document.md</title><style></style></head><body id="preview">
2
- <p>= 7.1 - Series =<br>
3
- <em>Released: 21st February, 2019</em> - <a href="https://icwp.io/ek">Release Notes</a></p>
4
- <ul>
5
- <li><strong>(v.2)</strong> IMPROVED: Firewall email notification content now better reflect the information in the audit trail.</li>
6
- <li><strong>(v.2)</strong> FIX: Firewall email notification was breaking in some instances.</li>
7
- <li><strong>(v.1)</strong> FIX: IP retrieval.</li>
8
- <li><strong>(v.0)</strong> NEW: Moved Import/Export UI from Wizard to main Shield Dashboard.</li>
9
- <li><strong>(v.0)</strong> NEW: [<strong>PRO</strong>] Option to import/export settings using file downloads/uploads</li>
10
- <li><strong>(v.0)</strong> NEW: [<strong>PRO</strong>] Option to allow visitors to automatically unblock themselves (once in 24hrs)</li>
11
- <li><strong>(v.0)</strong> NEW: Integrated changelog directly into plugin admin for easy updates (between releases)</li>
12
- <li><strong>(v.0)</strong> FIXED: WP Core files scanner now correctly ignores certain files as it used to do, pre-v7. e.g. wp-config-sample.php</li>
13
- <li><strong>(v.0)</strong> FIXED: Shield was indicating plugin/theme file editing was possible, when it in-fact was disabled.</li>
14
- <li><strong>(v.0)</strong> IMPROVED: Consolidate crons into fewer crons. e.g. all scans run under the same cron.</li>
15
- </ul>
16
- <p>= 7.0 - Series =<br>
17
- <em>Released: 28th January, 2019</em> - <a href="https://icwp.io/ef">Release Notes</a></p>
18
- <ul>
19
- <li><strong>(v.4)</strong> IMPROVED: Refactored IP address blocking with improved audit trail messages.</li>
20
- <li><strong>(v.4)</strong> CHANGED: Expanded anonymous REST API whitelist to include ‘wpstatistics’ namespace.</li>
21
- <li><strong>(v.4)</strong> IMPROVED: Access protection for shield temp/caching dir.</li>
22
- <li><strong>(v.4)</strong> IMPROVED: Clarification on reCAPTCHA - v3 is <strong>not</strong> supported.</li>
23
- <li><strong>(v.4)</strong> IMPROVED: Clarification on user sessions timeout - Shield sets an absolutely session maximum.</li>
24
- <li><strong>(v.4)</strong> IMPROVED: Options form submission is adjusted to work around poorly restrictive webhosts.</li>
25
- <li><strong>(v.4)</strong> FIX: Various tweaks and fixes across the plugin.</li>
26
- <li><strong>(v.4)</strong> FIX: Error with ClassicPress.</li>
27
- <li><strong>(v.3)</strong> NEW: Automatically whitelist anonymous REST API Access for 3 plugins: Contact Form 7, WooCommerce, JetPack.</li>
28
- <li><strong>(v.3)</strong> IMPROVED: Security admin login failure messages are clearer.</li>
29
- <li><strong>(v.3)</strong> IMPROVED: Admin notification for email sending 2FA verification easily lets you resend email.</li>
30
- <li><strong>(v.3)</strong> IMPROVED: File download code for WordPress Core file scanner repairs.</li>
31
- <li><strong>(v.3)</strong> IMPROVED: Attempt to also capture B/CC email addresses included in outgoing emails in Audit logs.</li>
32
- <li><strong>(v.3)</strong> FIX: Allow use of IPv4 ranges in whitelist again.</li>
33
- <li><strong>(v.3)</strong> CHANGED: Numerous code refactoring and improvements building upon the major v7 release and prepping for v7.1.</li>
34
- <li><strong>(v.1-2)</strong> FIXED: Some JS fixes.</li>
35
- <li><strong>(v.0)</strong> NEW: New primary UI for Shield site security management. Easy access to scans, audit trail, user sessions etc.</li>
36
- <li><strong>(v.0)</strong> NEW: Supports only PHP 5.4 or higher</li>
37
- <li><strong>(v.0)</strong> NEW: Rebuilt scans architecture and UI</li>
38
- <li><strong>(v.0)</strong> NEW: A huge amount of code cleaning and refactoring</li>
39
- <li><strong>(v.0)</strong> CHANGED: Too many many changes and bug fixes to list -best to just take a look! :)</li>
40
- </ul>
41
- <p>= 6.10 - Series =<br>
42
- <em>Released: 15th October, 2018</em> - <a href="https://icwp.io/dg">Release Notes</a></p>
43
- <ul>
44
- <li><strong>(v.9)</strong> FIXED: Admin notices displaying to non-admins.</li>
45
- <li><strong>(v.7)</strong> ADDED: [<strong>PRO</strong>] New option to specify usernames for Security Admin role.</li>
46
- <li><strong>(v.7)</strong> IMPROVED: Idle user detection.</li>
47
- <li><strong>(v.7)</strong> IMPROVED: Support for redirect/cancel URLs in 2FA login page.</li>
48
- <li><strong>(v.7)</strong> CHANGED: Final release before Shield v7. Small warning shown on plugins page if PHP &lt; 5.4</li>
49
- <li><strong>(v.6)</strong> ADDED: New option to control plugin automatic updates.</li>
50
- <li><strong>(v.6)</strong> IMPROVED: Enhancements to the experimental bot JS.</li>
51
- <li><strong>(v.6)</strong> IMPROVED: Support for Easy Digital Downloads forms.</li>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
52
  <li><strong>(v.5)</strong> Release skipped.</li>
53
- <li><strong>(v.4)</strong> FIXED: Couldnt deactivate plugin.</li>
54
- <li><strong>(v.3)</strong> ADDED: Support for Ultimate Member forms</li>
55
- <li><strong>(v.3)</strong> ADDED: Support for LearnPress login/registration forms</li>
56
- <li><strong>(v.3)</strong> FIXED: Security Admin now correctly honours the WordPress Options zone setting.</li>
57
- <li><strong>(v.3)</strong> IMPROVED: Distinguish which sub-site (sub-domain) for WPMS installations on <a href="https://icwp.io/c1">Traffic Watcher</a>.</li>
58
- <li><strong>(v.3)</strong> IMPROVED: Servers own IP lookup is only attempted once.</li>
59
- <li><strong>(v.3)</strong> ADDED: Experimental feature to help with some custom 3rd party login/registration forms</li>
60
- <li><strong>(v.2)</strong> IMPROVED: Visitor IP address detection</li>
61
- <li><strong>(v.2)</strong> IMPROVED: Automatic whitelisting of Manage WP IP addresses</li>
62
- <li><strong>(v.2)</strong> IMPROVED: SPAM Comments code enhanced and optimised</li>
63
- <li><strong>(v.2)</strong> IMPROVED: IP Whitelisting code enhanced and optimised</li>
64
- <li><strong>(v.2)</strong> IMPROVED: Code cleaning and refactoring.</li>
65
- <li><strong>(v.1)</strong> FIXED: Googlebot PHP error notice.</li>
66
- <li><strong>(v.0)</strong> NEW: [<strong>PRO</strong>] 2FA Login Backup Codes - all users can create a backup login code in-case their MFA factors are temporarily unavailable.</li>
67
- <li><strong>(v.0)</strong> NEW: [<strong>PRO</strong>] White Label - you can now specify custom image for 2FA login screen.</li>
68
- <li><strong>(v.0)</strong> ADDED: [<strong>PRO</strong>] Custom Exclusion Rules for Traffic Watcher so you can exclude certain User Agents and request paths.</li>
69
- <li><strong>(v.0)</strong> ADDED: Detection of official spiders/bots for Google, Bing, Apple and Yandex - these visitors will never get blacklisted.</li>
70
- <li><strong>(v.0)</strong> IMPROVED: Two-Factor Authentication system much improved (+ critical bug fix).</li>
71
- <li><strong>(v.0)</strong> IMPROVED: Audit Trail entries for 2FA login factors.</li>
72
- <li><strong>(v.0)</strong> IMPROVED: Fixes for Two-Factor Authentication wizard UX.</li>
73
- <li><strong>(v.0)</strong> IMPROVED: Traffic Watcher now honours the IP Whitelist.</li>
74
- <li><strong>(v.0)</strong> IMPROVED: Security Admin restriction for creating/editing/deleting Administrator users is much improved.</li>
75
- <li><strong>(v.0)</strong> IMPROVED: All Shield cookies are SSL-only by default for HTTPS sites.</li>
76
- <li><strong>(v.0)</strong> FIXED: GASP checkbox Javascript breaking in a particular scenario.</li>
77
- <li><strong>(v.0)</strong> ADDED: Optional plugin deactivation survey.</li>
78
- </ul>
79
- <p>= 6.9.0 - Series =<br>
80
- <em>Released: 6th September, 2018</em> - <a href="https://icwp.io/dc">Release Notes</a></p>
81
- <ul>
82
- <li><strong>(v.0)</strong> NEW: [<strong>PRO</strong>] <a href="https://icwp.io/c1">Traffic Watcher</a> - live tracking of all requests to your site.</li>
83
- <li><strong>(v.0)</strong> NEW: [<strong>PRO</strong>] <a href="https://icwp.io/c1">Yubikey</a> - Allows for multiple Yubikeys on the same user profile.</li>
84
- <li><strong>(v.0)</strong> ADDED: [<strong>PRO</strong>] Option to include listing of affected files within Hack Guard notification emails.</li>
85
- <li><strong>(v.0)</strong> ADDED: Option to delete the Security Admin Access Key</li>
86
- <li><strong>(v.0)</strong> ADDED: Option to add WooCommerce roles to 2FA-Email setting.</li>
87
- <li><strong>(v.0)</strong> CHANGED: Basic Stats system now requires minimum PHP v5.4.</li>
88
- <li><strong>(v.0)</strong> CHANGED: Password Policies now requires minimum WordPress v4.4.</li>
89
- <li><strong>(v.0)</strong> IMPROVED: Password expiration now redirects to the set password screen, instead of the user profile.</li>
90
- <li><strong>(v.0)</strong> IMPROVED: Password capture for purposes of password policies is improved.</li>
91
- <li><strong>(v.0)</strong> IMPROVED: You can now delete the forceoff file from inside the WP Admin.</li>
92
- <li><strong>(v.0)</strong> IMPROVED: Audit Trail entries for emails will identify the file thats calling the <code>wp_mail</code> function.</li>
93
- <li><strong>(v.0)</strong> IMPROVED: Audit Trail entries for post editing will identify the post type wherever possible.</li>
94
- <li><strong>(v.0)</strong> IMPROVED: Audit Trail entries will try to display all message text correctly.</li>
95
- <li><strong>(v.0)</strong> IMPROVED: Login/Register/Password forms are only checked when visitor is not logged-in.</li>
96
- <li><strong>(v.0)</strong> IMPROVED: Major database code refactoring and other code improvements.</li>
97
- <li><strong>(v.0)</strong> IMPROVED: User sessions handling.</li>
98
- <li><strong>(v.0)</strong> IMPROVED: Security Admin UX - ajax session checking, with admin notifications and auto-page reload.</li>
99
- <li><strong>(v.0)</strong> IMPROVED: Security Admin password setting now requires a confirmation password entry.</li>
100
- <li><strong>(v.0)</strong> IMPROVED: Refined Cooldown timing system.</li>
101
- <li><strong>(v.0)</strong> IMPROVED: Refined Bot checkbox Javascript.</li>
102
- <li><strong>(v.0)</strong> IMPROVED: Cron entry cleanup after deactivation.</li>
103
- <li><strong>(v.0)</strong> UPDATED: Bootstrap libraries to latest release v4.1.3.</li>
104
- <li><strong>(v.0)</strong> FIXED: Potential bug with Plugin/Themes guard scanning.</li>
105
- <li><strong>(v.0)</strong> FIXED: PHP Warning(s).</li>
106
- </ul>
107
- <p>= 6.8 Series =<br>
108
- <em>Released: 11th June, 2018</em> - <a href="https://icwp.io/d4">Release Notes</a></p>
109
- <ul>
110
- <li><strong>(v.2)</strong> FIXED: Bug with multi-factor authentication verification.</li>
111
- <li><strong>(v.2)</strong> FIXED: Bug with chosen reCAPTCHA style not being honoured on login pages</li>
112
- <li><strong>(v.2)</strong> FIXED: Bug with Invisible reCAPTCHA + WooCommerce</li>
113
- <li><strong>(v.2)</strong> FIXED: Bug with Pwned passwords always being checked even if setting turned off.</li>
114
- <li><strong>(v.1)</strong> FIXED: A couple of bugs with WooCommerce reCAPTCHA processing.</li>
115
- <li><strong>(v.1)</strong> FIXED: A bug with user sessions cleaning</li>
116
- <li><strong>(v.0)</strong> ADDED: [<strong>PRO</strong>] White Label - ability to re-brand the entire Shield Security plugin to your company brand.</li>
117
- <li><strong>(v.0)</strong> ADDED: [<strong>PRO</strong>] Option for all users to receive notification email upon login to their accounts.</li>
118
- <li><strong>(v.0)</strong> IMPROVED: Completely rebuilt the bot and reCAPTCHA login protection system.</li>
119
- <li><strong>(v.0)</strong> IMPROVED: Import/Export system hugely improved with respect to automated push of options from Master sites.</li>
120
- <li><strong>(v.0)</strong> IMPROVED: A different approach to sessions management that should handle sessions a bit better.</li>
121
- <li><strong>(v.0)</strong> IMPROVED: Expired user sessions are cleaned from the DB using a cron, and on Insights Dashboard load.</li>
122
- </ul>
123
- <p>= 6.7 Series =<br>
124
- <em>Released: 21st May, 2018</em> - <a href="https://icwp.io/cx">Release Notes</a></p>
125
- <ul>
126
- <li><strong>(v.2)</strong> ADDED: [<strong>PRO</strong>] Admin Notes feature - Notes can now be easily deleted (editing will not be possible).</li>
127
- <li><strong>(v.2)</strong> UPDATED: Some translations.</li>
128
- <li><strong>(v.2)</strong> FIXED: A few bugs with the Insights Dashboard.</li>
129
- <li><strong>(v.2)</strong> FIXED: Removed the dependency on jQuery with Invisible reCAPTCHA.</li>
130
- <li><strong>(v.1)</strong> FIXED: A few bugs with the Insights Dashboard</li>
131
- <li><strong>(v.1)</strong> ADDED: [<strong>PRO</strong>] Admin Notes feature - you can now add notes to the Shield plugin in the Insights Dashboard.</li>
132
- <li><strong>(v.0)</strong> ADDED: All-New Insights Dashboard providing a high-level overview of your site security, with recommendations.</li>
133
- <li><strong>(v.0)</strong> ADDED: Helpful, explanatory videos directly into the Guided Welcome Wizard.</li>
134
- <li><strong>(v.0)</strong> ADDED: A simple test cron to demonstrate whether your site crons are running.</li>
135
- <li><strong>(v.0)</strong> ADDED: [<strong>PRO</strong>] Full support for new WordPress GDPR Privacy Policy controls for exporting and erasing data.</li>
136
- <li><strong>(v.0)</strong> ADDED: [<strong>PRO</strong>] New GDPR guided wizard for exporting/erasing particular data based on custom search results.</li>
137
- <li><strong>(v.0)</strong> CHANGED: Guided Wizards now load through WP admin to fix ajax problems for poorly configured SSL on some sites</li>
138
- <li><strong>(v.0)</strong> IMPROVED: Upgraded Bootstrap library to 4.1.1.</li>
139
- <li><strong>(v.0)</strong> IMPROVED: Compatibility with AIO Events Cal - they like to force their old Twig libraries on everyone else.</li>
140
- </ul>
141
- <p>= 6.6 Series =<br>
142
- <em>Released: 19th March, 2018</em> - <a href="https://icwp.io/c3">Release Notes</a></p>
143
- <ul>
144
- <li><strong>(v.7)</strong> IMPROVED: reCAPTCHA JS is only included on pages where its actually used by Shield.</li>
145
- <li><strong>(v.7)</strong> IMPROVED: Upgrade Bootstrap library to 4.1.0.</li>
146
- <li><strong>(v.7)</strong> IMPROVED: Include jQuery for the plugin badge as required</li>
147
- <li><strong>(v.6)</strong> ADDED: Small exclusion in the firewall for a jetpack parameter.</li>
148
- <li><strong>(v.6)</strong> ADDED: SVGs to the default list of files scanned by the plugin guard.</li>
149
- <li><strong>(v.6)</strong> ADDED: Workaround for a <a href="https://wordpress.org/support/topic/forcefully-executing-wp_footer-not-compatible-with-other-plugins/">ridiculous NGG bug</a>.</li>
150
- <li><strong>(v.1-4)</strong> FIXED: Various small fixes and improvements</li>
151
- <li><strong>(v.4)</strong> FIXED: PHP Fatal Error on wp object cache.</li>
152
- <li><strong>(v.0)</strong> NEW: [<strong>PRO</strong>] <a href="https://icwp.io/c1">Keyless Activation of Pro licenses</a>.</li>
153
- <li><strong>(v.0)</strong> ADDED: <a href="https://icwp.io/c2">WordPress Password Policies</a>.</li>
154
- <li><strong>(v.0)</strong> ADDED: Pwned Passwords Detection.</li>
155
- <li><strong>(v.0)</strong> IMPROVED: Major rewrite of plugin AJAX handling.</li>
156
- <li><strong>(v.0)</strong> IMPROVED: Notices to indicate the time of the last scans.</li>
157
- <li><strong>(v.0)</strong> FIXED: A few bugs</li>
158
- </ul>
159
- <p>= 6.5 Series =<br>
160
- <em>Released: 5th March, 2018</em> - <a href="https://icwp.io/bu">Release Notes</a></p>
161
- <ul>
162
- <li><strong>(v.0)</strong> IMPROVED: <a href="https://icwp.io/bq">Plugin Guard</a> better handles the case where a plugin/theme has been entirely renamed/removed.</li>
163
- <li><strong>(v.0)</strong> IMPROVED: Attempts to access the XML-RPC system when its disabled will now result in a transgression increment in the IP Black List</li>
164
- <li><strong>(v.0)</strong> IMPROVED: Try to prevent black listing the servers own public IP address where visitor IP address detection is not correctly configured.</li>
165
- <li><strong>(v.0)</strong> ADDED: [<strong>PRO</strong>] Provisional support for not processing 2FA logins for Woocommerce Social Login plugin.</li>
166
- <li><strong>(v.0)</strong> FIXED: Plugin Guard better handles ignoring <a href="http://non-WordPress.org">non-WordPress.org</a> Plugins/Themes</li>
167
- <li><strong>(v.0)</strong> FIXED: A few small bugs</li>
168
- </ul>
169
- <p>= 6.4 Series =<br>
170
- <em>Released: 26th February, 2018</em> - <a href="https://icwp.io/br">Release Notes</a></p>
171
- <ul>
172
- <li><strong>(v.1-4)</strong> FIXED: Various Fixes</li>
173
- <li><strong>(v.0)</strong> ADDED: [<strong>PRO</strong>] New Scanner to <a href="https://icwp.io/bq">detect file changes for active plugins and themes</a></li>
174
- <li><strong>(v.0)</strong> IMPROVED: Automatic updates for vulnerable plugins ignores <a href="https://icwp.io/bc">automatic updates delay setting</a></li>
175
- <li><strong>(v.0)</strong> CHANGED: Email notifications for scanners will now link to the Wizard where possible, instead of listing files.</li>
176
- </ul>
177
- <p>= 6.3 Series =<br>
178
- <em>Released: 12th February, 2018</em> - <a href="https://icwp.io/bc">Release Notes</a></p>
179
- <ul>
180
- <li><strong>(v.3)</strong> FIXED: Bug with automatic updates delay setting</li>
181
- <li><strong>(v.2)</strong> CHANGED: Changed a text that seems to cause servers to swallow-up emails. <a href="https://icwp.io/bi">See here for more reliable email</a></li>
182
- <li><strong>(v.1)</strong> FIXED: Options page javascript to work around conflicts.</li>
183
- <li><strong>(v.0)</strong> ADDED: [<strong>PRO</strong>] <a href="https://icwp.io/bc">Automatic updates stability delay</a></li>
184
- <li><strong>(v.0)</strong> IMPROVED: Complete <a href="https://icwp.io/bd">plugin UI rebuild</a>, using the new Bootstrap 4.</li>
185
- <li><strong>(v.0)</strong> FIXED: A few bugs with Google Authenticator.</li>
186
- </ul>
187
- <p>= 6.2 Series =<br>
188
- <em>Released: 31st January, 2018</em> - <a href="https://icwp.io/b6">Release Notes</a></p>
189
- <ul>
190
- <li><strong>(v.2)</strong> FIXED: Fix for IP Manager PHP error.</li>
191
- <li><strong>(v.2)</strong> IMPROVED: Two-factor verification email.</li>
192
- <li><strong>(v.1)</strong> FIXED: Bug where administrator login email notification setting is not being honoured.</li>
193
- <li><strong>(v.1)</strong> IMPROVED: If a site is having trouble with database creation, User Sessions wont lock you out.</li>
194
- <li><strong>(v.0)</strong> IMPROVED: Major overhaul of the Shield User Sessions system.</li>
195
- <li><strong>(v.0)</strong> IMPROVED: Link the Security Admin authentication with the new Sessions system.</li>
196
- <li><strong>(v.0)</strong> IMPROVED: Major overhaul to plugins user meta data storage, limiting to a single DB entry for all data.</li>
197
- <li><strong>(v.0)</strong> ADDED: [<strong>PRO</strong>] Ability to increase frequency of file system scans up to once every hour.</li>
198
- <li><strong>(v.0)</strong> ADDED: [<strong>PRO</strong>] Add a remember me option, to allow users to skip Multi-factor authentication for a set number of days.</li>
199
- </ul>
200
- <p>= 6.1 Series =<br>
201
- <em>Released: 15th January, 2018</em> - <a href="https://icwp.io/ay">Release Notes</a></p>
202
- <ul>
203
- <li><strong>(v.1)</strong> FIXED: Verify link missing from the two-factor authentication verification email.</li>
204
- <li><strong>(v.0)</strong> ADDED: 3x more Shield Wizards: Multi-factor Authentication, Core File Scanning, Unrecognised File Scanning.</li>
205
- <li><strong>(v.0)</strong> ADDED: You can now use regular expressions for file exclusions in the Unrecognised File Scanner’.</li>
206
- <li><strong>(v.0)</strong> CHANGED: File Scanner email notifications now link to the appropriate scanner wizard directly.</li>
207
- <li><strong>(v.0)</strong> IMPROVED: Plugin options pages restyling.</li>
208
- <li><strong>(v.0)</strong> IMPROVED: Plugin refactoring and improvements.</li>
209
- </ul>
210
- <p>= 6.0 Series =<br>
211
  <em>Released: 18th December, 2017</em></p>
212
  <ul>
213
- <li><strong>(v.0)</strong> ADDED: All-new Shield Welcome and Setup Wizard - more helpful guided wizards to come.</li>
214
- <li><strong>(v.0)</strong> ADDED: [<strong>PRO</strong>] <a href="https://icwp.io/at">Shield options import and export</a></li>
215
- <li><strong>(v.0)</strong> ADDED: [<strong>PRO</strong>] In conjunction with import/export - Shield Security Network: automated options syncing.</li>
216
- <li><strong>(v.0)</strong> CHANGED: Going forward, new features and options will <a href="https://icwp.io/au">support only PHP 5.4+</a>. Existing features will remain unaffected.</li>
217
  </ul>
218
- <p>= 5.20 Series =<br>
219
  <em>Released: 11th December, 2017</em></p>
220
  <ul>
221
- <li><strong>(v.0)</strong> IMPROVED: [<strong>PRO</strong>] Audit Trail length are configurable. Length for free is 50 entries (the original unpaginated limit)</li>
222
- <li><strong>(v.0)</strong> IMPROVED: Large redesign of options sections to be more intuitive and cleaner</li>
223
- <li><strong>(v.0)</strong> IMPROVED: Added dedicated help section for each module.</li>
224
- <li><strong>(v.0)</strong> IMPROVED: Certain modules have an new <em>Actions</em> centre, such a Audit Trail viewer and User Sessions manager</li>
225
- <li><strong>(v.0)</strong> IMPROVED: Audit Trails are now ajax-paginated. You can browse through all your audit trail entries</li>
226
- <li><strong>(v.0)</strong> IMPROVED: User session tables are also ajax-paginated.</li>
227
  </ul>
228
- <p>= 5.19 Series =<br>
229
  <em>Released: 4th December, 2017</em></p>
230
  <ul>
231
- <li><strong>(v.1)</strong> FIXED: Plugin Vulnerabilities scan for premium plugins.</li>
232
- <li><strong>(v.0)</strong> ADDED: [<strong>PRO</strong>] Automated WordPress plugins vulnerability scanner with auto updates email notifications</li>
233
- <li><strong>(v.0)</strong> ADDED: Added Google reCAPTCHA support for register/forget password pages.</li>
234
- <li><strong>(v.0)</strong> ADDED: [<strong>PRO</strong>] Support for Multi-Factor Authentication for WooCommerce and other 3rd party plugins.</li>
235
- <li><strong>(v.0)</strong> ADDED: [<strong>PRO</strong>] Bot-protection/Google reCAPTCHA support for BuddyPress register pages.</li>
236
  </ul>
237
- <p>= 5.18 Series =<br>
238
  <em>Released: 27th November, 2017</em></p>
239
  <ul>
240
- <li><strong>(v.0)</strong> ADDED: [<strong>PRO</strong>] Invisible Google reCAPTCHA option.</li>
241
- <li><strong>(v.0)</strong> ADDED: [<strong>PRO</strong>] Support for Google reCAPTCHA themes - light and dark.</li>
242
- <li><strong>(v.0)</strong> IMPROVEMENT: Google reCAPTCHA is more reliable and configurable.</li>
243
  </ul>
244
- <p>= 5.17 Series =<br>
245
  <em>Released: 23rd November, 2017</em></p>
246
  <ul>
247
- <li><strong>(v.0)</strong> ADDED: Shield Security goes Pro! Added new options and extras to premium clients.</li>
248
- <li><strong>(v.0)</strong> IMPROVEMENT: Fix and improvement to Google reCAPTCHA.</li>
249
- <li><strong>(v.0)</strong> ADDED: [<strong>PRO</strong>] Support for Woocommerce and Easy Digital Downloads login/registration form protection.</li>
250
- <li><strong>(v.0)</strong> ADDED: [<strong>PRO</strong>] Ability to customise most user-facing texts.</li>
251
- <li><strong>(v.0)</strong> ADDED: [<strong>PRO</strong>] Extra IP Transgression signal.</li>
252
  </ul>
253
- <p>= 5.16 Series =<br>
254
  <em>Released: 16th October, 2017</em></p>
255
  <p>With this release, we fixed a clash of options for Google reCAPTCHA. Every attempt was made to ensure no interruption to your existing settings, but please check to ensure your reCAPTCHA settings are as you expect them to be.</p>
256
  <ul>
257
- <li><strong>(v.4)</strong> FIX: Error with incorrect/unprefixed database table name used in SQL query.</li>
258
- <li><strong>(v.3)</strong> IMPROVEMENT: Tweak to the Visitor IP Auto-detection to better ensure CloudFlare IP addresses are ignored.</li>
259
- <li><strong>(v.3)</strong> IMPROVEMENT: Plugin Badge will now stay closed when a visitor closes it.</li>
260
- <li><strong>(v.2)</strong> FIX: Removed some namespace parsing that broke on sites with PHP 5.2.</li>
261
- <li><strong>(v.1)</strong> FIX: 404 page displayed for password reset request when Login URL is renamed.</li>
262
- <li><strong>(v.0)</strong> IMPROVEMENT: Much better auto-detection of valid request/visitor IP addresses.</li>
263
- <li><strong>(v.0)</strong> FIX: Clashing of reCAPTCHA options for Comments and Login Protection.</li>
264
- <li><strong>(v.0)</strong> IMPROVEMENT: Statistic Reporting database management and pruning.</li>
265
- <li><strong>(v.0)</strong> FIX: Various system fixes and improvements.</li>
266
- </ul>
267
- <p>= 5.15 Series =<br>
268
  <em>Released: 21st September, 2017</em></p>
269
  <ul>
270
- <li><strong>(v.1)</strong> FIX: Processing AJAX requests from the Network Admin side of WordPress.</li>
271
- <li><strong>(v.1)</strong> IMPROVEMENTS: Better handling of file exclusions in the Hack Guard module.</li>
272
- <li><strong>(v.1)</strong> IMPROVEMENTS: Better handling of fatal errors in loading Shield where some core files are missing.</li>
273
- <li><strong>(v.0)</strong> ADDED: New HTTP Security Header: Referrer Policy.</li>
274
- <li><strong>(v.0)</strong> ADDED: Supports paths for file exclusions in the Unrecognised File Scanner.</li>
275
- <li><strong>(v.0)</strong> IMPROVEMENTS: Better interception of unintentional redirects to the hidden Login URL (e.g. /wp-admin/customize.php).</li>
276
- <li><strong>(v.0)</strong> IMPROVEMENTS: Better handling of email sending entries in the Audit Trail.</li>
277
- <li><strong>(v.0)</strong> IMPROVEMENTS: Improved (tabbed) display of Audit Trail.</li>
278
- <li><strong>(v.0)</strong> IMPROVEMENTS: Better generation &amp; handling of the One Time Password for email-based two-factor authentication.</li>
279
- <li><strong>(v.0)</strong> IMPROVEMENTS: Some code clean up and refactoring.</li>
280
- </ul>
281
- <p>= 5.14 Series =<br>
282
  <em>Released: 9th September, 2017</em></p>
283
  <ul>
284
- <li><strong>(v.0)</strong> ADDED: Option for administrators to manually override and set the source of the visitor IP address.</li>
285
- <li><strong>(v.0)</strong> UPDATED: In-plugin documentation links to updated and revised helpdesk articles/blogs.</li>
286
- <li><strong>(v.0)</strong> IMPROVEMENTS: Strip out any non-alphanumeric characters uses in the generation of Google Authenticator URLs.</li>
287
- <li><strong>(v.0)</strong> FIX: Shield now ignores any requests sent to Rest API URIs with respect to Shield user sessions.</li>
288
  </ul>
289
- <p>= 5.13 Series =<br>
290
  <em>Released: 15th August, 2017</em></p>
291
  <ul>
292
- <li><strong>(v.2)</strong> IMPROVEMENTS: Small adjustment to handling of Shield User sessions in conjunction with WordPress sessions.</li>
293
- <li><strong>(v.2)</strong> FIX: Restore display of help links for options.</li>
294
- <li><strong>(v.1)</strong> FIX: PHP 5.2 incompatibility.</li>
295
- <li><strong>(v.0)</strong> ADDED: New option for <a href="https://icwp.io/94">Unrecognised File Scanner</a> to scan the Uploads folder for JS and PHP files.</li>
296
- <li><strong>(v.0)</strong> ADDED: Option to provide custom list of files to be excluded from the <a href="https://icwp.io/94">Unrecognised File Scanner</a>.</li>
297
  </ul>
298
- <p>= 5.12 Series =<br>
299
  <em>Released: 3rd August, 2017</em></p>
300
  <ul>
301
- <li><strong>(v.2)</strong> IMPROVEMENTS: Improved support for Windows IIS hosting for <a href="https://icwp.io/94">Unrecognised File Scanner</a></li>
302
- <li><strong>(v.2)</strong> CHANGED: Removed the email-based 2FA automatic login link.</li>
303
- <li><strong>(v.2)</strong> FIX: Potential bug with Shield not recognising plugin configuration updates and not rebuilding options accordingly.</li>
304
- <li><strong>(v.1)</strong> ADDED: A few more exclusions for the <a href="https://icwp.io/94">Unrecognised File Scanner</a></li>
305
- <li><strong>(v.1)</strong> FIX: Fix for Fatal error.</li>
306
- <li><strong>(v.0)</strong> ADDED: <a href="https://icwp.io/94">Unrecognised File Scanner</a> release. Automatically detect and delete<br>
307
- any files present in core WordPress directories that aren’t part of your core installation.</li>
308
- <li><strong>(v.0)</strong> ADDED: Updated Firewall rules for SQL under the Aggressive rule set.</li>
309
  </ul>
310
- <p>= 5.11 Series =<br>
311
  <em>Released: 26th July, 2017</em></p>
312
  <ul>
313
- <li><strong>(v.1)</strong> FIX: JSON syntax</li>
314
- <li><strong>(v.0)</strong> IMPROVEMENTS: Final preparation for <a href="https://icwp.io/83">Shield Central</a> release.</li>
315
  </ul>
316
- <p>= 5.10 Series =<br>
317
  <em>Released: 19th June, 2017</em></p>
318
  <ul>
319
- <li><strong>(v.2)</strong> FIXED: Fatal error with GASP + Password Reset.</li>
320
- <li><strong>(v.2)</strong> FIXED: Fatal error with failing reCAPTCHA HTTP requests.</li>
321
- <li><strong>(v.1)</strong> IMPROVEMENTS: Further preparation for <a href="https://icwp.io/83">Shield Central</a> release.</li>
322
- <li><strong>(v.0)</strong> ADDED: More in-depth reporting and statistics gathering - options for reports will be made available<br>
323
- in a later release.</li>
324
  </ul>
325
- <p>= 5.9 Series =<br>
326
  <em>Released: 31st May, 2017</em></p>
327
  <ul>
328
- <li><strong>(v.0)</strong> ADDED: Help Videos for 1 or 2 modules. More to come and just testing format and uptake.</li>
329
- <li><strong>(v.0)</strong> ADDED: Special handling for WP Fastest Cache.</li>
330
  <li><strong>(v.0)</strong> CHANGE: Configuration for automatic self-update for the Shield plugin has been removed.</li>
331
- <li><strong>(v.0)</strong> CHANGE: No longer remove an existing user session when accessed from another IP address. Just redirect.<br>
332
- Protects existing, legitimate sessions from being forcefully expired.</li>
333
- <li><strong>(v.0)</strong> FIXED: Danish string translation.</li>
334
  </ul>
335
- <p>= 5.8 Series =<br>
336
  <em>Released: 7th April, 2017</em></p>
337
  <ul>
338
- <li><strong>(v.2)</strong> IMPROVEMENTS: The core file scanner now works more reliably for international WordPress installations.</li>
339
  <li><strong>(v.2)</strong> CHANGE: Login Cooldown now uses only the flag file as an indicator of login times.</li>
340
  <li><strong>(v.2)</strong> CHANGE: Filter to allow for changing the two factor timeout period, from 5 (minutes). Filter: <code>icwp-wpsf-login_intent_timeout</code></li>
341
  <li><strong>(v.2)</strong> CHANGE: Changed timeout for two-factor authentication email to 5 minutes to account for slower email-sending providers.</li>
342
  <li><strong>(v.2)</strong> CHANGE: Added further clarification to the Login Notification email indicating that two-factor authentication was pending.</li>
343
- <li><strong>(v.1)</strong> FIXED: Fixed a couple of bugs with the Login Authentication Portal, for certain edge cases.</li>
344
- <li><strong>(v.0)</strong> CHANGE: Major overhaul of <a href="https://icwp.io/87">Two-Factor / Multi-Factor Login Authentication</a>.</li>
345
- <li><strong>(v.0)</strong> CHANGE: <a href="https://icwp.io/86">Introduction of Login Authentication Portal</a> for improved Multi-Factor Authentication.</li>
346
- <li><strong>(v.0)</strong> ADDED: Option to choose between two-factor or multi-factor login authentication.</li>
347
- <li><strong>(v.0)</strong> ADDED: Administrators can remove Google Authenticator from another users profile.</li>
348
- <li><strong>(v.0)</strong> ADDED: When Security Admin is active, only Security Admins may remove Google Authenticator from other admins.</li>
349
  <li><strong>(v.0)</strong> CHANGE: Yubikey login authentication is now managed directly from the User Profile screen, as with Google Authenticator.</li>
350
  <li><strong>(v.0)</strong> CHANGE: Email-based login authentication no longer uses a separate database table.</li>
351
- <li><strong>(v.0)</strong> FIXED: Core file scanning now adequately handles Windows/Unix new lines during scan.</li>
352
- <li><strong>(v.0)</strong> FIXED: Certain crons werent setup correctly.</li>
353
- <li><strong>(v.0)</strong> IMPROVEMENTS: Further preparation for <a href="https://icwp.io/83">Shield Central</a> release.</li>
354
  </ul>
355
  <p>= 5.7 Series =</p>
356
  <ul>
357
- <li><strong>(v.3)</strong> FIXED: Attempt to improve the Google Authenticator flow for more reliable activation.</li>
358
- <li><strong>(v.2)</strong> IMPROVEMENTS: More admin notices when saving Google Authenticator settings.</li>
359
- <li><strong>(v.2)</strong> IMPROVEMENTS: Further preparation for <a href="https://icwp.io/83">Shield Central</a> release.</li>
360
  <li><strong>(v.1)</strong> Skipped</li>
361
- <li><strong>(v.0)</strong> ADDED: Shortcode for displaying plugin badge in pages/posts.</li>
362
  <li><strong>(v.0)</strong> CHANGE: Enabled JS eval() for the Content Security Policy by default.</li>
363
- <li><strong>(v.0)</strong> IMPROVEMENTS: Replace YAML configuration files with JSON.</li>
364
- <li><strong>(v.0)</strong> IMPROVEMENTS: Preparation for <a href="https://icwp.io/83">Shield Central</a> release.</li>
365
- <li><strong>(v.0)</strong> IMPROVEMENTS: Security Admin notices are more refined and optimized.</li>
366
- <li><strong>(v.0)</strong> IMPROVEMENTS: Removed unnecessary files/code.</li>
367
  </ul>
368
  <p>= 5.6 Series =</p>
369
  <ul>
370
- <li>
371
- <p><strong>(v.2)</strong> CHANGE: Fix an instance where the hidden Login URL would be leaded.</p>
 
 
 
 
 
 
372
  </li>
373
- <li>
374
- <p><strong>(v.1)</strong> CHANGE: Replaying of Yubikey one-time-passwords is no longer permitted.</p>
375
- </li>
376
- <li>
377
- <p><strong>(v.1)</strong> ADDED: Filter for login form GASP fields.</p>
378
- </li>
379
- <li>
380
- <p><strong>(v.1)</strong> ADDED: Filter for comment form GASP fields.</p>
381
- </li>
382
- <li>
383
- <p><strong>(v.1)</strong> CHANGE: Improved compatibility of HTTP Headers with WP Super Cache.</p>
384
- </li>
385
- <li>
386
- <p><strong>(v.0)</strong> ADDED: Option to disable anonymous Rest API access. WordPress v4.7+ only. Note that if another plugin<br>
387
- or service authenticates the request it will be honoured, whether anonymous or not.<br>
388
- = 5.5 Series =</p>
389
- </li>
390
- <li>
391
- <p><strong>(v.6)</strong> IMPROVED: Fixed possible leak of the Login URL from the ‘Hide WP Login URL’ feature.</p>
392
- </li>
393
- <li>
394
- <p><strong>(v.5)</strong> ADDED: Ability to add custom protocols to the domains (apart from http/s) to the Content Security Policy</p>
395
- </li>
396
- <li>
397
- <p><strong>(v.5)</strong> FIXED: Bug where automatic update emails would contain empty plugins.</p>
398
- </li>
399
- <li>
400
- <p><strong>(v.5)</strong> FIXED: Javascript scope on GASP form elements.</p>
401
- </li>
402
- <li>
403
- <p><strong>(v.5)</strong> FIXED: Various fixes and code improvements.</p>
404
- </li>
405
- <li>
406
- <p><strong>(v.4)</strong> FIXED: Bug with data cleaning/storage that caused stored options to balloon resulting in database timeouts. (only certain options affected)</p>
407
- </li>
408
- <li>
409
- <p><strong>(v.4)</strong> IMPROVED: Sometimes “anti-virus” scanners scared normal, everyday hard-working folk by identifying a Shield file as being a virus, because they’re not very clever - reduced chances of this.</p>
410
- </li>
411
- <li>
412
- <p><strong>(v.3)</strong> ADDED: Fix for WordPress Multisite where the correct database prefix wasn’t being used.</p>
413
- </li>
414
- <li>
415
- <p><strong>(v.2)</strong> ADDED: Filter to allow modification of the email footer</p>
416
- </li>
417
- <li>
418
- <p><strong>(v.2)</strong> ADDED: Block auto-updates on Shield itself if PHP &lt; 5.3 and new version is v6.0+</p>
419
- </li>
420
- <li>
421
- <p><strong>(v.2)</strong> FIXED: Missing Link</p>
422
- </li>
423
- <li>
424
- <p><strong>(v.2)</strong> FIXED: Plugin Installation ID wasn’t always being set</p>
425
- </li>
426
- <li>
427
- <p><strong>(v.2)</strong> TRANSLATIONS: Dutch (56%)</p>
428
- </li>
429
- <li>
430
- <p><strong>(v.1)</strong> ADDED: Built-in forceful protection in the form of a wp_die() against the (currently) un-patched W3 Total Cache XSS vulnerability <a href="https://icwp.io/7j">more info</a></p>
431
- </li>
432
- <li>
433
- <p><strong>(v.1)</strong> IMPROVED: Better XMLRPC Lockdown - prevents ANY XMLRPC command processing.</p>
434
- </li>
435
- <li>
436
- <p><strong>(v.1)</strong> IMPROVED: Make certain strings translatable</p>
437
- </li>
438
- <li>
439
- <p><strong>(v.1)</strong> IMPROVED: Wrap-up certain login form elements into spans/divs to allow styling etc.</p>
440
- </li>
441
- <li>
442
- <p><strong>(v.1)</strong> IMPROVED: PHP Version number cleaning during stats tracking.</p>
443
- </li>
444
- <li>
445
- <p><strong>(v.0)</strong> ADDED: Options and statistics tracking ability. Over time we are looking to share statistics and performance metrics of Shield.</p>
446
- </li>
447
- <li>
448
- <p><strong>(v.0)</strong> IMPROVED: Performance for options loading, especially for web hosts that don’t permit file writing</p>
449
- </li>
450
- <li>
451
- <p><strong>(v.0)</strong> CHANGED: Numerous fixes and code improvements.</p>
452
- </li>
453
- <li>
454
- <p><strong>(v.0)</strong> CHANGED: Removed query that deletes old GASP comment tokens on normal page loads.</p>
455
- </li>
456
- <li>
457
- <p><strong>(v.0)</strong> CHANGED: Google reCAPTCHA is now based on the locale of the website, not auto-detected.</p>
458
- </li>
459
- <li>
460
- <p><strong>(v.0)</strong> FIXED: Now URL encodes the username in the link for two-factor authentication by email.</p>
461
- </li>
462
- <li>
463
- <p><strong>(v.0)</strong> FIXED: If the xmlrpc.php has been deleted, this is now ignore by the file scanner</p>
464
- </li>
465
- <li>
466
- <p><strong>(v.0)</strong> TRANSLATIONS: Dutch (38%), Portuguese (32%)</p>
467
  </li>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
468
  </ul>
469
  <p>= 5.4 Series =</p>
470
  <ul>
471
- <li><strong>(v.5)</strong> CHANGED: User Management module is no-longer enabled by default on clean installations</li>
472
- <li><strong>(v.5)</strong> CHANGED: Made the GASP checkbox for Login protection clickable by label. <a href="https://github.com/FernleafSystems/Shield/pull/22">Thanks Aubrey!</a></li>
473
- <li><strong>(v.5)</strong> CHANGED: Shield Statistics only shows for WordPress admins (instead of all users)</li>
474
- <li><strong>(v.5)</strong> FIXED: Added a couple of guards to ensure data is of the correct format to prevent spurious errors</li>
475
- <li><strong>(v.5)</strong> FIXED: Bug where automatic file repair links from emails were not working.</li>
476
  <li><strong>(v.4)</strong> SKIPPED.</li>
477
- <li><strong>(v.3)</strong> FIXED: Various fixes and improvements</li>
478
- <li><strong>(v.3)</strong> CHANGED: Lots of cleaning of old code.</li>
479
- <li><strong>(v.3)</strong> REMOVED: Various old, unused options, and the force_ssl_login option as its deprecated by WordPress Core</li>
480
- <li><strong>(v.3)</strong> TRANSLATIONS: Dutch (36%), Swedish (35%)</li>
481
- <li><strong>(v.3)</strong> FIXED: Various fixes and improvements</li>
482
- <li><strong>(v.3)</strong> CHANGED: Lots of cleaning of old code.</li>
483
- <li><strong>(v.3)</strong> REMOVED: Various old, unused options, and the force_ssl_login option as its deprecated by WordPress Core</li>
484
- <li><strong>(v.3)</strong> TRANSLATIONS: Dutch (36%), Swedish (35%)</li>
485
- <li><strong>(v.2)</strong> ADDED: A guard around certain modules like, User Sessions, to ensure the DB has been initiated properly before use.</li>
486
- <li><strong>(v.2)</strong> ADDED: Exclusion for Swedish license files that dont exist in the SVN repo.</li>
487
- <li><strong>(v.2)</strong> ADDED: Parameter exclusion for reCAPTCHA.</li>
488
- <li><strong>(v.2)</strong> CHANGED: <a href="https://icwp.io/7b">HTTP Security Headers</a> module is enabled by default on new installs.</li>
489
- <li><strong>(v.1)</strong> FIXED: Nasty bug that caused an infinite loop bug in some configurations.</li>
490
- <li><strong>(v.0)</strong> ADDED: Per-site plugin statistics gathering - summary display on admin dashboard.</li>
491
- <li><strong>(v.0)</strong> ADDED: HTML class to the Im a human checkbox field.</li>
492
- <li><strong>(v.0)</strong> ADDED: Ability to change minimum user role for login notification emails with use of <code>add_filter()</code>. See FAQs.</li>
493
- <li><strong>(v.0)</strong> REMOVED: Option Prevent Remote Login causes more trouble with than its worth with too many hosting configurations.</li>
494
- <li><strong>(v.0)</strong> CHANGED: For websites that dont run WP Crons correctly, added code for automatic database cleaning.</li>
495
- <li><strong>(v.0)</strong> CLEANED: Removed Twig render code as it was never being used.</li>
496
  </ul>
497
  <p>= 5.3 Series =</p>
498
  <ul>
499
- <li><strong>(v.2)</strong> IMPROVED: <a href="https://icwp.io/7b">HTTP Security Headers</a> Content Security Policy now supports specifying HTTPS for domains/hosts.</li>
500
- <li><strong>(v.2)</strong> FIXED: Human Comment SPAM Feature didnt fire under certain circumstances.</li>
501
- <li><strong>(v.2)</strong> FIXED: Fixed parsing of Human Comment SPAM dictionary words.</li>
502
- <li><strong>(v.1)</strong> TRANSLATIONS: Dutch (32%)</li>
503
- <li><strong>(v.0)</strong> ADDED: New Feature - <a href="https://icwp.io/7b">HTTP Security Headers</a>.</li>
504
- <li><strong>(v.0)</strong> FIXED: Prevent renaming WP Login to “/login”</li>
505
  </ul>
506
  <p>= 5.2 Series =</p>
507
  <ul>
508
- <li><strong>(v.0)</strong> ADDED: Guard against core file scanner and automatic WordPress updates clashing.</li>
509
- <li><strong>(v.0)</strong> CHANGED: Logic for brute force login checking is improved - they all run before username/password checking</li>
510
- <li><strong>(v.0)</strong> FIXED: Certain older versions of PHP dont like combined IPv4 and IPv6 filter flags</li>
511
- <li><strong>(v.0)</strong> FIXED: Google reCAPTCHA for WordPress sites that have restrictive settings for sockets etc.</li>
512
- <li><strong>(v.0)</strong> REMOVED: <a href="https://icwp.io/75">Plugin vulnerabilities scanner</a>. Its out-of-date and unsuitable.</li>
513
  </ul>
514
  <p>= 5.1 Series =</p>
515
  <ul>
516
- <li><strong>(v.0)</strong> FIXED: Improved compatibility with bbPress.</li>
517
- <li><strong>(v.0)</strong> CHANGED: Optimizations around options and definitions (storing fewer options data)</li>
518
- <li><strong>(v.0)</strong> CHANGED: Improved styling and responsiveness of plugin badge.</li>
519
- <li><strong>(v.0)</strong> ADDED: Ability to programmatically export/import options - further preparation for iControlWP+Shield integration.</li>
520
- <li><strong>(v.0)</strong> FIXED: Issue where Core automatic updates would fail, but notification email was sent anyway</li>
521
  </ul>
522
  <p>= 5.0 Series =</p>
523
  <ul>
524
- <li><strong>(v.3)</strong> FIXED: Issue with setting session cookies with PHP 7</li>
525
- <li><strong>(v.2)</strong> FIXED: <a href="https://icwp.io/5s">Rename WordPress Login URL</a> bug</li>
526
- <li><strong>(v.2)</strong> CHANGED: reCAPTCHA text usage corrected throughout plugin.</li>
527
- <li><strong>(v.1)</strong> CHANGED: Removed the whole wp-content directory from the <a href="https://icwp.io/wpsf40">Core File Scanner</a> feature.</li>
528
- <li><strong>(v.1)</strong> CHANGED: A WordPress filter to change the plugin badge text content (see FAQ)</li>
529
- <li><strong>(v.1)</strong> CHANGED: Tweaked the plugin badge styling.</li>
530
- <li><strong>(v.1)</strong> CHANGED: All emails sent by the plugin contain the name of the site and the current plugin version in the email footer.</li>
531
- <li><strong>(v.1)</strong> ADDED: In-plugin links to blogs and info articles for Google ReCaptcha and <a href="https://icwp.io/wpsf43">Google Authenticator</a></li>
532
- <li><strong>(v.0)</strong> NEW: WordPress Simple Firewall plugin has been re-branded and is called <strong>Shield</strong></li>
533
- <li><strong>(v.0)</strong> ADDED: NEW feature - <a href="https://icwp.io/shld2">Google ReCaptcha</a> for Comment SPAM and Login protection.</li>
534
- <li><strong>(v.0)</strong> ADDED: Support for this plugin is now Premium. Added Premium Support page that links to Helpdesk.</li>
535
- <li><strong>(v.0)</strong> CHANGED: Refactor of comment spam code.</li>
536
- <li><strong>(v.0)</strong> CHANGED: Core File Scanner now handles the odd Hungarian distribution.</li>
537
- </ul>
538
- <p>= 4.17 Series =<br>
539
  <em>Released: 17th February, 2016</em></p>
540
  <ul>
541
- <li><strong>(v.0)</strong> ADDED: NEW feature - <a href="https://icwp.io/wpsf43">Google Authenticator Login option</a>.</li>
542
- <li><strong>(v.0)</strong> ADDED: <a href="https://icwp.io/wpsf40">Core File Scanner</a> now includes an automatic link to repair files (you must be logged in as admin for this link to work!).</li>
543
- <li><strong>(v.0)</strong> ADDED: NEW - if you already have a logged-in session and you open the login screen, youll be provided with a link to go straight to the admin area.</li>
544
- <li><strong>(v.0)</strong> CHANGED: Email-based Two-Factor Authentication is now stateless/session-less - it will not check validity per-page load.</li>
545
- <li><strong>(v.0)</strong> CHANGED: Changes to the email-based authentication system - now only 1 option and it no longer locks to IP or browser.</li>
546
- <li><strong>(v.0)</strong> CHANGED: Various efficiency improvements including reduced SQL updates.</li>
547
- <li><strong>(v.0)</strong> CHANGED: Email system is improved and now send emails from the default WordPress sender. This may be <a href="https://icontrolwp.freshdesk.com/support/solutions/articles/3000048723">changed with filter</a>.</li>
548
  </ul>
549
- <p>= 4.16 Series =<br>
550
  <em>Released: 20th January, 2016</em></p>
551
  <ul>
552
- <li><strong>(v.2)</strong> CHANGED: Further changes and improvements to the <a href="https://icwp.io/wpsf40">Core File Scanner</a>.</li>
553
- <li><strong>(v.2)</strong> CHANGED: Improvements to the <a href="https://icwp.io/wpsf27">automatic black list system</a> for failed login attempts.</li>
554
- <li><strong>(v.2)</strong> TRANSLATIONS: Turkish (100%)</li>
555
- <li><strong>(v.1)</strong> CHANGED: Improved the contents of the <a href="https://icwp.io/wpsf40">Core File Scanner</a> notification email with links to original source files.</li>
556
- <li><strong>(v.1)</strong> CHANGED: Now also excluding the /wp-content/languages/ directory since translations may update independently.</li>
557
- <li><strong>(v.1)</strong> CHANGED: Handles the special case of <a href="https://wordpress.org/support/topic/problem-with-checksum-hashes">old index.php files</a></li>
558
- <li><strong>(v.0)</strong> ADDED: Feature: <a href="https://icwp.io/wpsf40">Automatically scans WordPress Core files</a> and detects alterations from the default WordPress Core File data</li>
559
- <li><strong>(v.0)</strong> ADDED: Feature: to automatically attempt to repair/replace WordPress Core files that are discovered which have been altered.</li>
560
- <li><strong>(v.0)</strong> ADDED: Option to toggle the <a href="https://icwp.io/wpsf41">Plugin Vulnerabilities cron</a>.</li>
561
- <li><strong>(v.0)</strong> ADDED: Two-Factor Authentication links now honour the WordPress redirect_to parameter.</li>
562
- </ul>
563
- <p>= 4.15 Series =<br>
564
  <em>Released: 6th January, 2016</em></p>
565
  <ul>
566
- <li><strong>(v.0)</strong> ADDED: New and updated Firewall rules as well as a new Aggressive option that looks for additional request data. Disabled by default, but may cause an increase in false positives.</li>
567
- <li><strong>(v.0)</strong> CHANGED: Improved and optimized Firewall processing.</li>
568
- <li><strong>(v.0)</strong> FIXED: <a href="https://github.com/FernleafSystems/wp-simple-firewall/issues/3">Issue</a> where automatic update notification emails are sent out without any update notices (probably due to failed updates).</li>
569
- <li><strong>(v.0)</strong> FIXED: Small conflict with WP Login Rename and other security plugins.</li>
570
- <li><strong>(v.0)</strong> TRANSLATIONS: Czech (91%), Finnish (98%), Turkish (98%).</li>
571
  </ul>
572
- <p>= 4.14 Series =<br>
573
  <em>Released: 20th November, 2015</em></p>
574
  <ul>
575
- <li><strong>(v.2)</strong> ADDED: User notice message displayed when the Theme My Login plugin is active and you try to rename your login URL - It is not compatible.</li>
576
- <li><strong>(v.1)</strong> ADDED: Added WordPress filter option to specify URL instead of present a 404 when Rename WP Login is active. <a href="https://icontrolwp.freshdesk.com/solution/articles/3000044812">more info</a></li>
577
- <li><strong>(v.1)</strong> ADDED: Added Unique Plugin Installation ID to be utilized in the future.</li>
578
- <li><strong>(v.1)</strong> FIXED: WordPress Comments bug where some comments didnt pass through the SPAM filters in a certain scenario.</li>
579
- <li><strong>(v.0)</strong> ADDED: <a href="https://icwp.io/wpsf33">Custom Automatic Update Notifications Email</a> that runs separately to the in-built WordPress core notification email.</li>
580
- <li><strong>(v.0)</strong> ADDED: Filter to remove the admin area IP address footer text</li>
581
- <li><strong>(v.0)</strong> CHANGED: Added native support for PayPal return links - whitelisting verify_sign parameter.</li>
582
- <li><strong>(v.0)</strong> CHANGED: Tweak patterns for matching on WordPress terms’.</li>
583
- <li><strong>(v.0)</strong> TRANSLATIONS: Danish (100%), Czech (92%), Turkish (92%), Finnish (88%),</li>
584
- <li><strong>(v.0)</strong> FIXED: Small bugs and readying for WordPress 4.4</li>
585
- </ul>
586
- <p>= 4.13 Series =<br>
587
  <em>Released: 22nd October, 2015</em></p>
588
  <ul>
589
- <li><strong>(v.0)</strong> NEW: Added option to block the modification, addition/promotion and deletion of WordPress administrators users within the Security Admin module.</li>
590
- <li><strong>(v.0)</strong> NEW: Renamed Admin Access module to Security Admin’.</li>
591
- <li><strong>(v.0)</strong> CHANGED: Simplified and consolidated the use of cookies for User Session - sets and removes cookies better to reduce their usage.</li>
592
- <li><strong>(v.0)</strong> CHANGED: Simplified and consolidated the use of cookies for Two Factor Login Authentication.</li>
593
- <li><strong>(v.0)</strong> CHANGED: Cleaned up some Comment SPAM filtering code.</li>
594
- <li><strong>(v.0)</strong> CHANGED: Comments Filter doesnt use cookies unless a session cookie for the visitor already exists.</li>
595
- <li><strong>(v.0)</strong> CHANGED: IP Manager Automatic Black List - default black list duration is now 1 minute &amp; default transgressions limit is 10</li>
596
- <li><strong>(v.0)</strong> CHANGED: Improvements to the database create queries: use MySQL Engine defaults (instead of MyISAM); use WordPress dbDelta() for updates.</li>
597
- <li><strong>(v.0)</strong> CHANGED: Various code optimizations and cleaning.</li>
598
- </ul>
599
- <p>= 4.12 Series =<br>
600
  <em>Released: 10th October, 2015</em></p>
601
  <ul>
602
- <li><strong>(v.0)</strong> NEW: Option to completely disable the XML-RPC system. <a href="https://icwp.io/wpsf31">more info</a></li>
603
- <li><strong>(v.0)</strong> CHANGED: Logged-in users are automatically forwarded to the WordPress admin only if they are Administrators.</li>
604
  </ul>
605
- <p>= 4.11 Series =<br>
606
  <em>Released: 5th October, 2015</em></p>
607
  <ul>
608
- <li><strong>(v.0)</strong> NEW: Ability to now completely block the update/changing of certain WordPress site options. <a href="https://icwp.io/wpsf30">more info</a></li>
609
- <li><strong>(v.0)</strong> FIXED: Various small bugs with the IP Manager UI ajax.</li>
610
- <li><strong>(v.0)</strong> FIXED: Uncaught PHP Exception when a sites hosting isnt properly configured to handle IPv6 addresses.</li>
611
- <li><strong>(v.0)</strong> TRANSLATIONS: Danish - 57%, Czech - 100%, Finnish - 94%</li>
612
  </ul>
613
- <p>= 4.10 Series =<br>
614
  <em>Released: 23rd August, 2015</em></p>
615
  <ul>
616
- <li>
617
- <p><strong>(v.4)</strong> REFACTOR: Notifications system is more reliable and most notices can be hidden/closed (at least for the current page load as some notices are persistent).</p>
618
- </li>
619
- <li>
620
- <p><strong>(v.4)</strong> REMOVED: The old manual black list option has been completely removed - in favour of the automatic black list system.</p>
621
- </li>
622
- <li>
623
- <p><strong>(v.4)</strong> CHANGED: Revised the order of certain hooks being created to avoid the possibility of pluggable.php not being loaded for PHP Shutdown.</p>
624
- </li>
625
- <li>
626
- <p><strong>(v.4)</strong> CHANGED: The presence of IP addresses in the IP Whitelist will force the IP Manager feature to be enabled.</p>
627
- </li>
628
- <li>
629
- <p><strong>(v.4)</strong> CHANGED: We now make an attempt to prevent the caching of WordPress wp_die() pages that we generate. (compatible with at least W3TC, Super Cache)</p>
630
- </li>
631
- <li>
632
- <p><strong>(v.4)</strong> TRANSLATIONS: Turkish - 100%, Danish - 3%</p>
633
- </li>
634
- <li>
635
- <p><strong>(v.3)</strong> FIXED: Another PHP 5.2 incompatibility.</p>
636
- </li>
637
- <li>
638
- <p><strong>(v.2)</strong> ADDED: White Listing UI to the IP Manager - CIDR ranges are supported (also automatically migrates IPs, except ranges, from legacy to new)</p>
639
- </li>
640
- <li>
641
- <p><strong>(v.2)</strong> ADDED: Returned the black marking of failed WP login attempts to the automatic black list system</p>
642
  </li>
643
- <li>
644
- <p><strong>(v.2)</strong> ADDED: Using a 3rd party API service: <a href="https://www.ipify.org/">ipify.org</a> - to find the server’s own IP address so we can ensure it’s not used in the black lists</p>
645
  </li>
646
- <li>
647
- <p><strong>(v.2)</strong> CHANGED: AJAX calls are handled more robustly with actual error messages where possible.</p>
 
 
 
648
  </li>
649
- <li>
650
- <p><strong>(v.2)</strong> FIXED: A few black list processing bugs.</p>
651
  </li>
652
- <li>
653
- <p><strong>(v.1)</strong> ADDED: UI to view and remove IP address from Automatic Black List Engine.</p>
654
- </li>
655
- <li>
656
- <p><strong>(v.1)</strong> FIX: Removed transgression counting on failed logins - WP data is inconsistent.</p>
657
- </li>
658
- <li>
659
- <p><strong>(v.1)</strong> CHANGED: Original legacy white list now takes priority over new auto black list</p>
660
- </li>
661
- <li>
662
- <p><strong>(v.1)</strong> CHANGED: Default transgressions limit is now 7</p>
663
- </li>
664
- <li>
665
- <p><strong>(v.1)</strong> ADDED: Ability to reset plugin options to default using ‘reset’ flag file. <a href="https://icwp.io/wpsf28">more info</a></p>
666
- </li>
667
- <li>
668
- <p><strong>(v.0)</strong> NEW FEATURE: ‘FABLE’ - <a href="https://icwp.io/wpsf27">Fully Automatic Black Listing Engine</a>.</p>
669
- </li>
670
- </ul>
671
- <p>Simply put, FABLE will automatically block all malicious traffic by IP, based on their activity. This Security Plugin will track malicious behaviour<br>
672
- and count all transgressions that visitors make against the site. Once a particular visitor exceeds the specified number transgressions, FABLE<br>
673
  will outright block any access they have to your WordPress site.</p>
674
  <p>What makes the FABLE system better?</p>
675
  <ul>
@@ -685,24 +709,24 @@ will outright block any access they have to your WordPress site.</p>
685
  <li>Attempt to login with an invalid username/password combination</li>
686
  <li>Any attempt to login while the login cooldown system is in-effect</li>
687
  <li>Any login attempt that trips the GASP Login protection system</li>
688
- <li>Any login attempt with a username that doesnt exist</li>
689
  <li>Any attempt to access /wp-admin/, /login/, or wp-login.php while the Rename WP Login setting is active</li>
690
  <li>Any comment that gets labelled as SPAM by the plugin</li>
691
- <li>Failed attempt to authenticate with the plugins Admin Access Protection module</li>
692
  <li>Any trigger of a Firewall block rule</li>
693
  </ul>
694
- <p>= 4.9 Series =<br>
695
  <em>Released: 7th July, 2015</em></p>
696
  <ul>
697
  <li><strong>(v.8)</strong> CHANGED: Firewall, User Sessions and Lockdown Feature Modules are now enabled by default for new installations.</li>
698
- <li><strong>(v.8)</strong> FIX: Some server email programs cant handle colons (:) in the email subject (because supporting all characters would be waaay too radical man).</li>
699
- <li><strong>(v.8)</strong> ADDED: Function to better get the WordPress home URL to prevent interference from other plugins.</li>
700
- <li><strong>(v.8)</strong> CHANGED: Updated Text For <a href="https://icwp.io/6e">Author Scan Block</a> feature.</li>
701
  <li><strong>(v.7)</strong> CHANGED: How author query blocking works to be more reliable and stricter - only runs when users are not logged in, and it will DIE instead of redirect.</li>
702
  <li><strong>(v.6)</strong> ADDED: New Option: prevent detection of usernames using the ?author=N query. (location under section: Lockdown -&gt; Obscurity)</li>
703
- <li><strong>(v.6)</strong> FIXED: Infinite redirect loop logic prevents redirect for rejected comment SPAM thats posted in bulk. This results in email notifications for spam comments.</li>
704
  <li><strong>(v.5)</strong> ADDED: The plugin will load itself first before all other plugins</li>
705
- <li><strong>(v.5)</strong> FIXED: No longer using parse_url() to determine the request URL as its too inconsistent and unreliable.</li>
706
  <li><strong>(v.4)</strong> FIX: Audit Trail Viewer display issue with non-escaped HTML (Thanks Chris!)</li>
707
  <li><strong>(v.4)</strong> ADDED: An admin warning for sites with PHP version less than 5.3.2 (future versions will require this as a minimum)</li>
708
  <li><strong>(v.4)</strong> TRANSLATIONS: Danish - 6%, Spanish - 76%</li>
@@ -712,9 +736,9 @@ will outright block any access they have to your WordPress site.</p>
712
  <li><strong>(v.2)</strong> ADDED: Email notifications sent out to report email address on a daily cron. <a href="https://www.icontrolwp.com/2015/07/plugin-vulnerability-email-notifications/">more info</a></li>
713
  <li><strong>(v.2)</strong> FIX: Work around a WordPress inline plugin update Javascript bug.</li>
714
  <li><strong>(v.1)</strong> FIX: Fix syntax support for earlier versions of PHP.</li>
715
- <li><strong>(v.0)</strong> FEATURE: Plugin Vulnerabilities Detection: If youre running plugins with known vulnerabilities you will be warned - <a href="https://icwp.io/wpsf22">more info</a></li>
716
  </ul>
717
- <p>= 4.8 Series =<br>
718
  <em>Released: 21st June, 2015</em></p>
719
  <ul>
720
  <li><strong>(v.0)</strong> FEATURE: Admin Access Restriction Areas - Restrict access to certain WordPress areas and functionality to <strong>Administrators</strong> with the Admin Access key.</li>
@@ -722,15 +746,15 @@ will outright block any access they have to your WordPress site.</p>
722
  <li><strong>(v.0)</strong> ADDED: Admin Access Restriction Area - Themes. You can now restrict access to certain Theme actions - activate, install, update, delete.</li>
723
  <li><strong>(v.0)</strong> ADDED: Admin Access Restriction Area - Pages/Post. You can now restrict access to certain Page/Post actions - Create/Edit, Publish, Delete.</li>
724
  </ul>
725
- <p>= 4.7 Series =<br>
726
  <em>Released: 29th April, 2015</em></p>
727
  <ul>
728
  <li><strong>(v.7)</strong> FIXED: The text used to explain why some comments were marked as spam was broken.</li>
729
  <li><strong>(v.7)</strong> FIXED: Group sign-up form now honours your SSL setting.</li>
730
  <li><strong>(v.7)</strong> TRANSLATIONS: Spanish - 74%, Russian - 91%, Turkish - 94%, Polish- 95%, Finnish - 100%</li>
731
- <li><strong>(v.6)</strong> FIXED: Verifying ability to send/receive email doesnt complete if Admin Access Protection is turned on.</li>
732
- <li><strong>(v.6)</strong> FIXED: GASP Login Protection feature breaks because certain key options arent initialized when the feature is enabled.</li>
733
- <li><strong>(v.6)</strong> FIXED: Some more info links were empty.</li>
734
  <li><strong>(v.4)</strong> ADDED: Email Sending Verification when enabling two-factor authentication - this ensures your site can send (and you can receive) emails.</li>
735
  <li><strong>(v.4)</strong> ADDED: Section Summaries - each option tab contains a small text summary outlining the purpose and recommendation for each.</li>
736
  <li><strong>(v.4)</strong> CHANGED: The Admin Access Key input is now a password field.</li>
@@ -745,16 +769,16 @@ will outright block any access they have to your WordPress site.</p>
745
  <li><strong>(v.1)</strong> FIX: Silence warnings from filesystem touch() command.</li>
746
  <li><strong>(v.1)</strong> TRANSLATIONS: Polish (100%), Finnish (100%), Czech (73%), Arabic (34%)</li>
747
  <li><strong>(v.0)</strong> UPDATED: Options page user interface re-design.</li>
748
- <li><strong>(v.0)</strong> FIX: Audit trail time now reflects the users timezone correctly.</li>
749
  <li><strong>(v.0)</strong> FIX: Better compatibility with BBPress.</li>
750
  <li><strong>(v.0)</strong> UPDATED: Underlying plugin code improvements.</li>
751
  <li><strong>(v.0)</strong> TRANSLATIONS: Russian (100%), Czech (70%), Polish (97%)</li>
752
  </ul>
753
- <p>= 4.6 Series =<br>
754
  <em>Released: 10th April, 2015</em></p>
755
  <ul>
756
- <li><strong>(v.3)</strong> SECURITY: Added protection against XSS vulnerability in WordPress comments. <a href="https://icwp.io/63">Learn More</a> - Note: This is not a vulnerability with the Firewall plugin.</li>
757
- <li><strong>(v.3)</strong> SECURITY: Added extra precautions to WordPress URL redirects. <a href="https://icwp.io/64">Learn More</a>.</li>
758
  <li><strong>(v.3)</strong> TRANSLATIONS: Russian (70%), Czech (67%)</li>
759
  <li><strong>(v.2)</strong> FIX: Bug with the database table verification logic.</li>
760
  <li><strong>(v.2)</strong> TRANSLATIONS: Russian (New- 54%), Romanian (100%), Turkish (89%), Czech (53%)</li>
@@ -762,10 +786,10 @@ will outright block any access they have to your WordPress site.</p>
762
  <li><strong>(v.1)</strong> UPDATED: Plugin Badge styling</li>
763
  <li><strong>(v.1)</strong> UPDATED: Updated Czech(41%) and Spanish (60%) translations</li>
764
  <li><strong>(v.0)</strong> ADDED: New feature that displays the last login time for all users on the users listing page (User Management feature must be enabled).</li>
765
- <li><strong>(v.0)</strong> ADDED: <strong>Completely optional</strong> promotional Plugin Badge option - help us promote the plugin and reassure your site visitors at the same time. <a href="https://icwp.io/5x">Learn More</a></li>
766
  <li><strong>(v.0)</strong> UPDATED: Updated Czech(38%) translations</li>
767
  </ul>
768
- <p>= 4.5 Series =<br>
769
  <em>Released: 6th March, 2015</em></p>
770
  <ul>
771
  <li><strong>(v.5)</strong> CHANGED: Updated Finnish (100%), Czech (16%) translations</li>
@@ -779,47 +803,47 @@ will outright block any access they have to your WordPress site.</p>
779
  <li><strong>(v.1)</strong> ADDED: New feature- GASP Login Protection can now be applied to lost password form - enabled by default</li>
780
  <li><strong>(v.0)</strong> ADDED: New feature- GASP Login Protection can now be applied to user registrations - enabled by default</li>
781
  </ul>
782
- <p>= 4.4 Series =<br>
783
  <em>Released: 21st February, 2015</em></p>
784
  <ul>
785
  <li><strong>(v.2)</strong> ADDED: Romanian Translation.</li>
786
  <li><strong>(v.2)</strong> ADDED: A plugin minimum-requirements processing system.</li>
787
  <li><strong>(v.2)</strong> IMPROVED: The WordPress admin-UI code is simpler and cleaner.</li>
788
  <li><strong>(v.1)</strong> ADDED: <strong>Significant</strong> performance enhancement in plugin loading times (up to 50% reduction).</li>
789
- <li><strong>(v.0)</strong> CHANGED: The Prevent Remote Login option now tries to detect web hosting server compatibility before allowing it to be enabled.</li>
790
- <li><strong>(v.0)</strong> CHANGED: More lax in finding the forceOff file when users are trying to turn off the firewall.</li>
791
  <li><strong>(v.0)</strong> CHANGED: Parsing the URL no longer outputs warnings that might interfere with response headers.</li>
792
  </ul>
793
- <p>= 4.3 Series =<br>
794
  <em>Released: 15th January, 2015</em></p>
795
  <ul>
796
  <li><strong>(v.6)</strong> FIXES: More thorough validation of whitelisted IP addresses</li>
797
  <li><strong>(v.5)</strong> FIXES: Some hosting environments need absolute file paths for PHP include()/require()</li>
798
  <li><strong>(v.5)</strong> CHANGED: Streamlined the detection of whitelisting and added in-plugin notification if <strong>you</strong> are whitelisted</li>
799
- <li><strong>(v.4)</strong> FIXES: Work around for cases where PHP cant successfully run parse_url()</li>
800
  <li><strong>(v.2)</strong> IMPROVED: Refactoring for better code organisation</li>
801
- <li>ADDED: New Feature - <a href="https://icwp.io/5s">Rename WP Login Page</a>.</li>
802
  <li>ADDED: UI indicators on whether plugins will be automatically updated in the plugins listing.</li>
803
- <li>CHANGED: IP Address WhiteList is now global for the whole plugin, and can be accessed under the Dashboard area</li>
804
  <li>IMPROVED: Firewall processing code is simplified and more efficient.</li>
805
  </ul>
806
- <p>= 4.2.1 =<br>
807
  <em>Released: 22th December, 2014</em></p>
808
  <ul>
809
  <li>FIXED: Changes to how feature specifications are read from disk to prevent .tmp file build up.</li>
810
  </ul>
811
- <p>= 4.2.0 =<br>
812
  <em>Released: 12th December, 2014</em></p>
813
  <ul>
814
  <li>ADDED: Audit Trail Auto Cleaning - default cleans out entries older than 30 days.</li>
815
  <li>FIXED: Various small bug fixes and code cleaning.</li>
816
  </ul>
817
- <p>= 4.1.4 =<br>
818
  <em>Released: 24th November, 2014</em></p>
819
  <ul>
820
  <li>FIXED: Fixed small logic bug which prevented deactivation of the plugin on the UI.</li>
821
  </ul>
822
- <p>= 4.1.3 =<br>
823
  <em>Released: 19th November, 2014</em></p>
824
  <ul>
825
  <li>IMPROVED: User Sessions are simplified.</li>
@@ -827,25 +851,25 @@ will outright block any access they have to your WordPress site.</p>
827
  </ul>
828
  <p>= 4.1.2 =</p>
829
  <ul>
830
- <li>ADDED: Self-correcting database table validation - if the structure of a database table isnt what is expected, itll be re-created.</li>
831
  </ul>
832
  <p>= 4.1.1 =</p>
833
  <ul>
834
  <li>WARNING: Due to new IPv6 support, all databases tables will be rebuilt - all active user sessions will be destroyed.</li>
835
- <li>ADDED: Preliminary support for IPv6 addresses throughout. We dont support whitelist ranges but IPv6 addresses are handled much more reliably in general.</li>
836
- <li>ADDED: New audit trail concept added called immutable that represents entries that will never be deleted - such entries would usually involve actions taken on the audit trail itself.</li>
837
  <li>FIXED: Support for audit trail events with longer names.</li>
838
  <li>IMPROVED: Comments Filtering - It now honours the WordPress settings for previously approved comment authors and never filters such comments.</li>
839
  <li>REMOVED: Option to enable GASP Comments Filtering for logged-in users has been completely removed - this reduces plugin options complexity. All logged-in users by-pass <strong>all</strong> comments filtering.</li>
840
  <li>FIXED: Prevention against plugin redirect loops under certain conditions.</li>
841
- <li>FIXED: IP whitelisting wasnt working under certain cases.</li>
842
  </ul>
843
  <p>= 4.0.0 =</p>
844
  <ul>
845
  <li>ADDED: New Feature - Audit Trail</li>
846
  <li>ADDED: Audit Trail options include: Plugins, Themes, Email, WordPress Core, Posts/Pages, Shield plugin</li>
847
  <li>FIXED: Full and proper cleanup of plugin options, crons, and databases upon deactivation.</li>
848
- <li>REMOVED: Firewall Log. This is no longer an option and is instead integrated into the Shield Audit Trail.</li>
849
  </ul>
850
  <p>= 3.5.5 =</p>
851
  <ul>
@@ -855,18 +879,18 @@ will outright block any access they have to your WordPress site.</p>
855
  </ul>
856
  <p>= 3.5.3 =</p>
857
  <ul>
858
- <li>ADDED: A warning message on the WordPress admin if the forceOff override is active.</li>
859
- <li>CHANGED: The forceOff system is now temporary - i.e. it doesnt save the configuration, and so once this file is removed, the plugin returns to the settings specified.</li>
860
- <li>CHANGED: The forceOn option is now removed.</li>
861
- <li>FIXED: Problems with certain hosting environments reading in files with the “.yaml extension - <a href="https://wordpress.org/support/topic/yaml-breaks-plugin">support ref</a></li>
862
- <li>FIXED: Small issue where when the file system paths change, some variables dont update properly.</li>
863
  </ul>
864
  <p>= 3.5.0 =</p>
865
  <ul>
866
  <li>CHANGED: Plugin features are now configured <a href="https://github.com/mustangostang/spyc/">using YAML</a> - no more in-PHP configuration.</li>
867
  <li>REMOVED: A few options from User Sessions Management as they were unnecessary.</li>
868
  <li>CHANGED: Database storing tables now have consistent naming.</li>
869
- <li>FIXED: Issue with User Sessions Management where 0 was specified for session length, resulting in lock out.</li>
870
  <li>FIXED: Firewall log gathering.</li>
871
  <li>FIXED: Various PHP warning notices.</li>
872
  </ul>
@@ -886,10 +910,10 @@ will outright block any access they have to your WordPress site.</p>
886
  <p>= 3.2.0 =</p>
887
  <ul>
888
  <li>ADDED: Options to allow by-pass XML-RPC so as to be compatible with WordPress iPhone/Android apps.</li>
889
- <li>UPDATED: Login screen message when youre forced logged-out due to 2-factor auth failure on IP or cookie.</li>
890
  <li>CHANGED: Tweaked method for setting admin access protection on/off</li>
891
  <li>CHANGED: comment filtering code refactoring.</li>
892
- <li>FIXED: Options that were multiple selects werent saving correctly</li>
893
  </ul>
894
  <p>= 3.1.5 =</p>
895
  <ul>
@@ -914,7 +938,7 @@ will outright block any access they have to your WordPress site.</p>
914
  <p>= 3.1.0 =</p>
915
  <ul>
916
  <li>ADDED: option to check the logged-in user session only on WordPress admin pages (now the default setting)</li>
917
- <li>ADDED: option to auto-forward to the WordPress dashboard when you go to wp-login.php and youre already logged in.</li>
918
  <li>ADDED: message to login screen when no user session is found</li>
919
  <li>CHANGED: does not verify session when performing AJAX request. (need to build appropriate AJAX response)</li>
920
  <li>FIX: for wp_login action not passing second argument</li>
@@ -949,33 +973,33 @@ will outright block any access they have to your WordPress site.</p>
949
  <p>= 2.6.4 =</p>
950
  <ul>
951
  <li>ENHANCED: Dashboard now shows a more visual summary of settings and removes duplicate options settings with links to sections.</li>
952
- <li>ENHANCED: WordPress Lock Down options now also set the corresponding WordPress defines if theyre not already.</li>
953
  </ul>
954
  <p>= 2.6.3 =</p>
955
  <ul>
956
  <li>ADDED: More in-line plugin links to help/blog resources</li>
957
- <li>ENHANCED: <a href="https://icwp.io/5b">Admin Access Protection</a> is further enhanced in 3 ways:</li>
958
- </ul>
959
- <ol>
960
- <li>More robust cookie values using MD5s</li>
961
  <li>Blocks plugin options updating right at the point of WordPress options update so nothing can rewrite the actual plugin options.</li>
962
  <li>Locks the current Admin Access session to your IP address - effectively only 1 Shield admin allowed at a time.</li>
963
- </ol>
964
  <p>= 2.6.2 =</p>
965
  <ul>
966
- <li>ENHANCED: Added option to completely reject a SPAM comment and redirect to the home page (so it doesnt fill up your database with rubbish)</li>
967
  <li>ADDED: Plugin now has an internal stats counter for spam and other significant plugin events.</li>
968
  </ul>
969
  <p>= 2.6.1 =</p>
970
  <ul>
971
  <li>ADDED: Plugin now installs with default SPAM blacklist.</li>
972
- <li>ADDED: Now automatically checks and updates the SPAM blacklist when its older than 48hrs.</li>
973
  <li>ENHANCED: Comment messages indicate where the SPAM content was found when marking human-based spam messages.</li>
974
  </ul>
975
  <p>= 2.6.0 =</p>
976
  <p><strong>Major Features Release: Please review SPAM comments filtering options to determine where SPAM goes</strong></p>
977
  <ul>
978
- <li>FEATURE: Added Human SPAM comments filtering - replacement for Akismet that doesnt use or send any data to 3rd party services. Uses <a href="https://github.com/splorp/wordpress-comment-blacklist">Blacklist provided and maintained by Grant Hutchinson</a></li>
979
  <li>ENHANCED: Two-Factor Login now automatically logs in the user to the admin area without them having to re-login again.</li>
980
  <li>ENHANCED: Added ability to terminate all currently (two-factor) verified logins.</li>
981
  <li>ENHANCED: Spam filter/scanning adds an explanation to the SPAM content to show why a message was filtered.</li>
@@ -988,7 +1012,7 @@ will outright block any access they have to your WordPress site.</p>
988
  </ul>
989
  <p>= 2.5.8 =</p>
990
  <ul>
991
- <li>FEATURE: Added PHP Code Firewall checking option.</li>
992
  </ul>
993
  <p>= 2.5.7 =</p>
994
  <ul>
@@ -1000,9 +1024,9 @@ will outright block any access they have to your WordPress site.</p>
1000
  </ul>
1001
  <p>= 2.5.5 =</p>
1002
  <ul>
1003
- <li>FEATURE: Added Lockdown feature to force login to WordPress over SSL.</li>
1004
- <li>FEATURE: Added Lockdown feature to force WordPress Admin dashboard to be delivered over SSL.</li>
1005
- <li>FIX: Admin restricted access feature wasnt disabled with the forceOff option.</li>
1006
  </ul>
1007
  <p>= 2.5.4 =</p>
1008
  <ul>
@@ -1023,7 +1047,7 @@ will outright block any access they have to your WordPress site.</p>
1023
  </ul>
1024
  <p>= 2.5.0 =</p>
1025
  <ul>
1026
- <li>FEATURE: Two-Factor Authenticated Login using <a href="https://icwp.io/4i">Yubikey</a> One Time Passwords (OTP).</li>
1027
  </ul>
1028
  <p>= 2.4.3 =</p>
1029
  <ul>
@@ -1032,7 +1056,7 @@ will outright block any access they have to your WordPress site.</p>
1032
  </ul>
1033
  <p>= 2.4.2 =</p>
1034
  <ul>
1035
- <li>ADDED: Contextual help links for many options. More to come…</li>
1036
  <li>ADDED: More Portuguese (Brazil) translations (~80%)</li>
1037
  </ul>
1038
  <p>= 2.4.1 =</p>
@@ -1040,7 +1064,7 @@ will outright block any access they have to your WordPress site.</p>
1040
  <li>ADDED: More strings to the translation set for better multilingual support</li>
1041
  <li>ADDED: Portuguese (Brazil) translations (~40%)</li>
1042
  <li>UPDATED: Hebrew Translations</li>
1043
- <li>FIXED: Automatic cleaning of database logs wasnt actually working as expected. Should now be fixed.</li>
1044
  </ul>
1045
  <p>= 2.4.0 =</p>
1046
  <ul>
@@ -1055,23 +1079,23 @@ will outright block any access they have to your WordPress site.</p>
1055
  <ul>
1056
  <li>ADDED: Hebrew Translations. Thanks <a href="http://atar4u.com">Ahrale</a>!</li>
1057
  <li>ADDED: Automatic trimming of the Firewall access log to 7 days - it just grows too large otherwise.</li>
1058
- <li>FIX: The previously added automatic clean up of old comments and login protect database entries was wiping out the valid login protect<br>
1059
- entries and was forcing users to re-login every 24hrs.</li>
1060
  <li>FIX: Some small bugs, errors, and PHPDoc Comments.</li>
1061
  </ul>
1062
  <p>= 2.3.2 =</p>
1063
  <ul>
1064
- <li>ADDED: Automatic cleaning of GASP Comments Filter and Login Protection database entries (older than 24hrs) using WordPress Cron (everyday @ 6am)</li>
1065
  <li>CHANGED: Huge code refactoring to allow for more easily use with other WordPress plugins.</li>
1066
  </ul>
1067
  <p>= 2.2.5 =</p>
1068
  <ul>
1069
- <li>ADDED: Email sending options for automatic update notifications - options to change the notification email address, or turn it off completely.</li>
1070
  </ul>
1071
  <p>= 2.2.4 =</p>
1072
  <ul>
1073
  <li>FIX: Small bug fix.</li>
1074
- <li>CHANGED: When running a force automatic updates process, tries to remove influence from other plugins and uses only this plugins automatic updates settings.</li>
1075
  <li>CHANGED: A bit of automatic updates code refactoring.</li>
1076
  </ul>
1077
  <p>= 2.2.2 =</p>
@@ -1081,7 +1105,7 @@ entries and was forcing users to re-login every 24hrs.</li>
1081
  </ul>
1082
  <p>= 2.2.1 =</p>
1083
  <ul>
1084
- <li>ADDED: Verified compatibility with WordPress 3.8</li>
1085
  </ul>
1086
  <p>= 2.2.0 =</p>
1087
  <ul>
@@ -1091,8 +1115,8 @@ entries and was forcing users to re-login every 24hrs.</li>
1091
  </ul>
1092
  <p>= 2.1.5 =</p>
1093
  <ul>
1094
- <li>IMPROVED: Improved logic for Firewall whitelisting for pages and parameters to ensure whitelisting rules are followed.</li>
1095
- <li>CHANGED: The whitelisting rule for posting pages/posts is only for the content and the firewall checking will apply to all other page parameters.</li>
1096
  </ul>
1097
  <p>= 2.1.4 =</p>
1098
  <ul>
@@ -1102,17 +1126,17 @@ entries and was forcing users to re-login every 24hrs.</li>
1102
  <ul>
1103
  <li>FIX: A bug that prevented auto-updates of this plugin.</li>
1104
  <li>FIX: Not being able to hide translations and upgrade notices.</li>
1105
- <li>ADDED: Tweaks to auto-update feature to allow interfacing with the iControlWP service to customize the auto update system.</li>
1106
  </ul>
1107
  <p>= 2.1.0 =</p>
1108
  <ul>
1109
- <li>ADDED: A button that lets you run the WordPress Automatic Updates process on-demand (so you dont have to wait for WordPress cron).</li>
1110
  <li>CHANGED: The plugin now sets more options to be turned on by default when the plugin is first activated.</li>
1111
  <li>CHANGED: A lot of optimizations and code refactoring.</li>
1112
  </ul>
1113
  <p>= 2.0.3 =</p>
1114
  <ul>
1115
- <li>FIX: Whoops, sorry, accidentally removed the option to toggle disable file editing”. Its back now.</li>
1116
  </ul>
1117
  <p>= 2.0.2 =</p>
1118
  <ul>
@@ -1120,8 +1144,8 @@ entries and was forcing users to re-login every 24hrs.</li>
1120
  </ul>
1121
  <p>= 2.0.1 =</p>
1122
  <ul>
1123
- <li>ADDED: Localization capabilities. All we need now are translators! <a href="http://translate.icontrolwp.com/">Go here to get started</a>.</li>
1124
- <li>ADDED: Option to mask the WordPress version so the real version is never publicly visible.</li>
1125
  </ul>
1126
  <p>= 1.9.2 =</p>
1127
  <ul>
@@ -1129,23 +1153,23 @@ entries and was forcing users to re-login every 24hrs.</li>
1129
  </ul>
1130
  <p>= 1.9.1 =</p>
1131
  <ul>
1132
- <li>ADDED: Increased admin access security features - blocks the deactivation of itself if youre not authenticated fully with the plugin.</li>
1133
- <li>ADDED: If youre not authenticated with the plugin, the plugin listing view wont have Deactivate or Edit links.</li>
1134
  </ul>
1135
  <p>= 1.9.0 =</p>
1136
  <ul>
1137
- <li>ADDED: New WordPress Automatic Updates Configuration settings</li>
1138
  </ul>
1139
  <p>= 1.8.2 =</p>
1140
  <ul>
1141
- <li>ADDED: Notification of available plugin upgrade is now an option under the Dashboard’</li>
1142
- <li>CHANGED: Certain admin and upgrade notices now only appear when youre authenticated with the plugin (if this is enabled)</li>
1143
- <li>FIXED: PHP Notice with undefined index.</li>
1144
  </ul>
1145
  <p>= 1.8.1 =</p>
1146
  <ul>
1147
- <li>ADDED: Feature- Access Key Restriction <a href="https://icwp.io/2s">more info</a>.</li>
1148
- <li>ADDED: Feature- WordPress Lockdown. Currently only provides 1 option, but more to come.</li>
1149
  </ul>
1150
  <p>= 1.7.3 =</p>
1151
  <ul>
@@ -1154,13 +1178,13 @@ entries and was forcing users to re-login every 24hrs.</li>
1154
  </ul>
1155
  <p>= 1.7.1 =</p>
1156
  <ul>
1157
- <li>ADDED: Much more efficiency yet again in the loading/saving of the plugin options.</li>
1158
  </ul>
1159
  <p>= 1.7.0 =</p>
1160
  <ul>
1161
- <li>ADDED: Preliminary WordPress Multisite (WPMS/WPMU) Support.</li>
1162
- <li>CHANGED: The Firewall now kicks in on the plugins_loaded hook instead of as the actual firewall plugin is initialized (as a result<br>
1163
- of WP Multisite support).</li>
1164
  </ul>
1165
  <p>= 1.6.2 =</p>
1166
  <ul>
@@ -1168,78 +1192,78 @@ of WP Multisite support).</li>
1168
  </ul>
1169
  <p>= 1.6.1 =</p>
1170
  <ul>
1171
- <li>ADDED: Options to fully customize the text displayed by the GASP comments section.</li>
1172
- <li>ADDED: Option to include logged-in users in the GASP Comments Filter.</li>
1173
  </ul>
1174
  <p>= 1.6.0 =</p>
1175
  <ul>
1176
- <li>ADDED: A new section - Comments Filtering that will form the basis for filtering comments with SPAM etc.</li>
1177
- <li>ADDED: Option to add enhanced GASP based comments filtering to prevent SPAM bots posting comments to your site.</li>
1178
  </ul>
1179
  <p>= 1.5.6 =</p>
1180
  <ul>
1181
- <li>IMPROVED: Whitelist/Blacklist IP range processing to better cater for ranges when saving, with more thorough checking.</li>
1182
- <li>IMPROVED: Whitelist/Blacklist IP range processing for 32-bit systems.</li>
1183
- <li>FIXED: A bug with Whitelist/Blacklist IP checking.</li>
1184
  </ul>
1185
  <p>= 1.5.5 =</p>
1186
  <ul>
1187
- <li>FIXED: Quite a few bugs fixed.</li>
1188
  </ul>
1189
  <p>= 1.5.4 =</p>
1190
  <ul>
1191
- <li>FIXED: Typo error.</li>
1192
  </ul>
1193
  <p>= 1.5.3 =</p>
1194
  <ul>
1195
- <li>FIXED: Some of the firewall processors were saving unnecessary data.</li>
1196
  </ul>
1197
  <p>= 1.5.2 =</p>
1198
  <ul>
1199
  <li>CHANGED: The method for finding the client IP address is more thorough, in a bid to work with Proxy servers etc.</li>
1200
- <li>FIXED: PHP notice reported here: <a href="http://wordpress.org/support/topic/getting-errors-when-logged-in">http://wordpress.org/support/topic/getting-errors-when-logged-in</a></li>
1201
  </ul>
1202
  <p>= 1.5.1 =</p>
1203
  <ul>
1204
- <li>FIXED: Bug fix where IP address didnt show in email.</li>
1205
- <li>FIXED: Attempt to fix problem where update message never hides.</li>
1206
  </ul>
1207
  <p>= 1.5.0 =</p>
1208
  <ul>
1209
- <li>ADDED: A new IP whitelist on the Login Protect that lets you by-pass login protect rules for given IP addresses.</li>
1210
  <li>REMOVED: Firewall rule for wp-login.php and whitelisted IPs.</li>
1211
  </ul>
1212
  <p>= 1.4.2 =</p>
1213
  <ul>
1214
- <li>ADDED: The plugin now has an option to automatically upgrade itself when an update is detected - enabled by default.</li>
1215
  </ul>
1216
  <p>= 1.4.1 =</p>
1217
  <ul>
1218
- <li>ADDED: The plugin will now displays an admin notice when a plugin upgrade is available with a link to immediately update.</li>
1219
- <li>ADDED: Plugin collision: removes the main hook by All In One WordPress Security’. No need to have both plugins running.</li>
1220
- <li>ADDED: Improved Login Cooldown Feature- works more like email throttling as it now uses an extra filesystem-based level of protection.</li>
1221
- <li>FIXED: Login Cooldown Feature didnt take effect in certain circumstances.</li>
1222
  </ul>
1223
  <p>= 1.4.0 =</p>
1224
  <ul>
1225
- <li>ADDED: All-new plugin options handling making them more efficient, easier to manage/update, using far fewer WordPress database options.</li>
1226
  <li>CHANGED: Huge improvements on database calls and efficiency in loading plugin options.</li>
1227
- <li>FIXED: Nonce implementation.</li>
1228
  </ul>
1229
  <p>= 1.3.2 =</p>
1230
  <ul>
1231
- <li>FIXED: Small compatibility issue with Quick Cache menu not showing.</li>
1232
  </ul>
1233
  <p>= 1.3.0 =</p>
1234
  <ul>
1235
- <li>ADDED: Email Throttle Feature - this will prevent you getting bombarded by 1000s of emails in case youre hit by a bot.</li>
1236
- <li>ADDED: Another Firewall die() option. New option will print a message and uses the wp_die() function instead.</li>
1237
- <li>ADDED: Refactored and improved the logging system (upgrading will delete your current logs!).</li>
1238
- <li>ADDED: Option to separately log Login Protect features.</li>
1239
- <li>ADDED: Option to by-pass 2-factor authentication in the case sending the verification email fails<br>
1240
- (so you don’t get locked out if your hosting doesn’t support email!).</li>
1241
  <li>CHANGED: Login Protect checking now better logs out users immediately with a redirect.</li>
1242
- <li>CHANGED: We now escape the log data being printed - just in case theres any HTML/JS etc in there we dont want.</li>
1243
  <li>CHANGED: Optimized and cleaned a lot of the option caching code to improve reliability and performance (more to come).</li>
1244
  </ul>
1245
  <p>= 1.2.7 =</p>
@@ -1248,13 +1272,13 @@ of WP Multisite support).</li>
1248
  </ul>
1249
  <p>= 1.2.6 =</p>
1250
  <ul>
1251
- <li>ADDED: Ability to import settings from WordPress Firewall 2 plugin options - note, doesnt import page and variables whitelisting.</li>
1252
  <li>FIX: A reported bug - parameter values could also be arrays.</li>
1253
  </ul>
1254
  <p>= 1.2.5 =</p>
1255
  <ul>
1256
- <li>ADDED: New Feature - Option to add a checkbox that blocks automated SPAM Bots trying to log into your site.</li>
1257
- <li>ADDED: Added a clear user message when they verify their 2-factor authentication.</li>
1258
  <li>FIX: A few bugfixes and logic corrections.</li>
1259
  </ul>
1260
  <p>= 1.2.4 =</p>
@@ -1272,23 +1296,23 @@ of WP Multisite support).</li>
1272
  </ul>
1273
  <p>= 1.2.1 =</p>
1274
  <ul>
1275
- <li>ADDED: New Feature - Login Wait Interval. To reduce the effectiveness of brute force login attacks, you can add an interval by<br>
1276
- which WordPress will wait before processing any more login attempts on a site.</li>
1277
  <li>CHANGED: Optimized some settings for performance.</li>
1278
  <li>CHANGED: Cleaned up the UI when the Firewall / Login Protect features are disabled (more to come).</li>
1279
  <li>CHANGED: Further code improvements (more to come).</li>
1280
  </ul>
1281
  <p>= 1.2.0 =</p>
1282
  <ul>
1283
- <li>ADDED: New Feature - <strong>Login Protect</strong>. Added 2-Factor Login Authentication for all users and their associated IP addresses.</li>
1284
  <li>CHANGED: The method for processing the IP address lists is improved.</li>
1285
  <li>CHANGED: Improved .htaccess rules (thanks MickeyRoush)</li>
1286
  <li>CHANGED: Mailing method now uses WP_MAIL</li>
1287
- <li>CHANGED: Lots of code improvements.</li>
1288
  </ul>
1289
  <p>= 1.1.6 =</p>
1290
  <ul>
1291
- <li>ADDED: Option to include Cookies in the firewall checking.</li>
1292
  </ul>
1293
  <p>= 1.1.5 =</p>
1294
  <ul>
@@ -1311,13 +1335,13 @@ which WordPress will wait before processing any more login attempts on a site.</
1311
  </ul>
1312
  <p>= 1.1.1 =</p>
1313
  <ul>
1314
- <li>Fix: Block notification emails werent showing the user-friendly IP Address format.</li>
1315
  </ul>
1316
  <p>= 1.1.0 =</p>
1317
  <ul>
1318
- <li>You can now specify IP ranges in whitelists and blacklists. To do this separate the start and end address with a hyphen (-) E.g. For everything between 1.2.3.4 and 1.2.3.10, you would do: 1.2.3.4-1.2.3.10</li>
1319
  <li>You can now specify which email address to send the notification emails.</li>
1320
- <li>You can now add a comment to IP addresses in the whitelist/blacklist. To do this, write your IP address then type a SPACE and write whatever you want (dont take a new line).</li>
1321
  <li>You can now set to delete ALL firewall settings when you deactivate the plugin.</li>
1322
  <li>Improved formatting of the firewall log.</li>
1323
  </ul>
@@ -1329,12 +1353,10 @@ which WordPress will wait before processing any more login attempts on a site.</
1329
  <p>= 1.1.2 =</p>
1330
  <ul>
1331
  <li>CHANGED: Logging now has its own dedicated database table.</li>
1332
- <li>Fix: Block notification emails werent showing the user-friendly IP Address format.</li>
1333
- <li>You can now specify IP ranges in whitelists and blacklists. To do this separate the start and end address with a hyphen (-) E.g. For everything between 1.2.3.4 and 1.2.3.10, you would do: 1.2.3.4-1.2.3.10</li>
1334
  <li>You can now specify which email address to send the notification emails.</li>
1335
- <li>You can now add a comment to IP addresses in the whitelist/blacklist. To do this, write your IP address then type a SPACE and write whatever you want (dont take a new line).</li>
1336
  <li>You can now set to delete ALL firewall settings when you deactivate the plugin.</li>
1337
  <li>Improved formatting of the firewall log.</li>
1338
- </ul>
1339
-
1340
- </body></html>
1
+ <p>= 8.3 - Series =
2
+ <em>Released: 18th November, 2019</em> - <a href="https://shsec.io/g3">Release Notes</a></p>
3
+ <ul>
4
+ <li><strong>(v.0)</strong> IMPROVED: Improvements to Malware scanner to <a href="https://shsec.io/g3">now track malware results</a> by specific lines, not just by file.</li>
5
+ <li><strong>(v.0)</strong> IMPROVED: Support colons (:) in IP addresses during visitor IP address detection.</li>
6
+ <li><strong>(v.0)</strong> IMPROVED: Ensure license lookups use the correct site URL.</li>
7
+ <li><strong>(v.0)</strong> IMPROVED: Attempt to ensure that if there is an interruption in the API, malware patterns are available for scanning.</li>
8
+ <li><strong>(v.0)</strong> IMPROVED: Added default firewall whitelist parameter for AffiliateWP requests.</li>
9
+ <li><strong>(v.0)</strong> IMPROVED: Spanish, French, Japanese translations.</li>
10
+ </ul>
11
+ <p>= 8.2 - Series =
12
+ <em>Released: 1st October, 2019</em> - <a href="https://shsec.io/g0">Release Notes</a></p>
13
+ <ul>
14
+ <li><strong>(v.3)</strong> FIXED: Fix for reported RXSS vulnerability - <a href="https://shsec.io/g1">more info</a>.</li>
15
+ <li><strong>(v.3)</strong> FIXED: Fix for Rest API detection.</li>
16
+ <li><strong>(v.3)</strong> FIXED: Fix for translation of some strings.</li>
17
+ <li><strong>(v.2)</strong> FIXED: Fixes for scans running under Windows/IIS.</li>
18
+ <li><strong>(v.2)</strong> IMPROVED: Adds a check that a site can send an HTTP request to itself before allowing scans to run.</li>
19
+ <li><strong>(v.2)</strong> IMPROVED: Scans clean up after themselves better, if they fail to run.</li>
20
+ <li><strong>(v.2)</strong> IMPROVED: Server&#39;s own IP address detection when site migrated to a new host.</li>
21
+ <li><strong>(v.2)</strong> UPDATED: International translations.</li>
22
+ <li><strong>(v.2)</strong> FIXED: PHP notices when data wasn&#39;t as expected.</li>
23
+ <li><strong>(v.1)</strong> IMPROVED: Further reduce Malware false positives by also using SVN trunk data when verifying files for plugins and themes.</li>
24
+ <li><strong>(v.1)</strong> ADDED: Initial support for repairing Themes that have been installed from WordPress.org.</li>
25
+ <li><strong>(v.1)</strong> ADDED: Support for using <a href="https://wphashes.com">WP Hashes.com</a> for WordPress.org themes (already done for plugins).</li>
26
+ <li><strong>(v.1)</strong> FIXED: PHP notices in the logs.</li>
27
+ <li><strong>(v.0)</strong> IMPROVED: [<strong>PRO</strong>] Malware scanner now uses network intelligence to the gather information on malware results.</li>
28
+ <li><strong>(v.0)</strong> NEW: Traffic Watcher feature is now free for all users (no longer Pro-only).</li>
29
+ <li><strong>(v.0)</strong> IMPROVED: Scanning cron is improved and more efficient.</li>
30
+ <li><strong>(v.0)</strong> ADDED: Bulk Delete/Repair/Ignore actions now available for Malware scan results.</li>
31
+ <li><strong>(v.0)</strong> IMPROVED: Malware scan results now provide details of affected line numbers and patterns discovered.</li>
32
+ <li><strong>(v.0)</strong> IMPROVED: Malware scanner only scans <code>wp-admin</code>, <code>wp-includes</code>, <code>wp-content</code> folders, and files in top-level directory.</li>
33
+ <li><strong>(v.0)</strong> IMPROVED: Malware scanner now excludes <code>wp-content/cache/</code> directory.</li>
34
+ <li><strong>(v.0)</strong> IMPROVED: Malware scanner performance improved with caching.</li>
35
+ <li><strong>(v.0)</strong> IMPROVED: Malware auto-repair now works more consistently.</li>
36
+ <li><strong>(v.0)</strong> IMPROVED: Updated default firewall whitelist rules.</li>
37
+ <li><strong>(v.0)</strong> IMPROVED: If the PWNED Passwords API request fails entirely, the password check is skipped.</li>
38
+ <li><strong>(v.0)</strong> ADDED: Japanese translations are at 100%.</li>
39
+ <li><strong>(v.0)</strong> IMPROVED: Dutch translations are greatly improved (a huge thank you to Fred!).</li>
40
+ <li><strong>(v.0)</strong> FIXED: Audit Trail correctly logs multiple occurrences for the same type of event on the same page request.</li>
41
+ <li><strong>(v.0)</strong> FIXED: Audit Trail now correctly logs Google reCAPTCHA failure events.</li>
42
+ <li><strong>(v.0)</strong> FIXED: PHP error when firewall was set to kill response without a user message.</li>
43
+ </ul>
44
+ <p>= 8.1 - Series =
45
+ <em>Released: 18th September, 2019</em> - <a href="https://shsec.io/fy">Release Notes</a></p>
46
+ <ul>
47
+ <li><strong>(v.1)</strong> FIXED: Error for sites pre-5.0 that don&#39;t have function <code>determine_locale()</code></li>
48
+ <li><strong>(v.0)</strong> IMPROVED: Massive improvements to asynchronous scans in performance and reliability.</li>
49
+ <li><strong>(v.0)</strong> ADDED: [<strong>PRO</strong>] Possible to supply multiple email addresses for Administrator login notifications.</li>
50
+ <li><strong>(v.0)</strong> ADDED: New firewall whitelist rule to prevent firewall blocks when activating certain plugins.</li>
51
+ <li><strong>(v.0)</strong> IMPROVED: Prevent errors caused by other plugins not passing correctly-formatted data through WP filters.</li>
52
+ <li><strong>(v.0)</strong> ADDED: Japanese translations (14%).</li>
53
+ <li><strong>(v.0)</strong> IMPROVED: Plugin locale now respects user profile locale setting.</li>
54
+ <li><strong>(v.0)</strong> IMPROVED: Audit Trail filter for specific events.</li>
55
+ <li><strong>(v.0)</strong> IMPROVED: Lots of cleanup of deprecated PHP code following the the v7-v8 upgrade.</li>
56
+ </ul>
57
+ <p>= 8.0 - Series =
58
+ <em>Released: 27th August, 2019</em> - <a href="https://shsec.io/fv">Release Notes</a></p>
59
+ <ul>
60
+ <li><strong>(v.2)</strong> IMPROVED: Password strength metering now better aligns with WordPress library (PHP 5.6+)</li>
61
+ <li><strong>(v.2)</strong> IMPROVED: Dutch translations have been adjusted.</li>
62
+ <li><strong>(v.2)</strong> FIXED: Setting &#39;Month&#39; for IP block duration wasn&#39;t being applied.</li>
63
+ <li><strong>(v.2)</strong> FIXED: Certain admin notices not displayed when they should be.</li>
64
+ <li><strong>(v.1)</strong> FIXED: Comment SPAM blocking wasn&#39;t working if set to &quot;Detect and Reject&quot;.</li>
65
+ <li><strong>(v.1)</strong> FIXED: Shield Widget/Badge broken in some cases.</li>
66
+ <li><strong>(v.1)</strong> ADDED: You can force Shield to operate in any <a href="https://shsec.io/gistshieldlocale">locale, regardless of site locale</a>.</li>
67
+ <li><strong>(v.1)</strong> ADDED: Russian translations are now at 100% and some Dutch translations have been adjusted.</li>
68
+ <li><strong>(v.0)</strong> NEW: [<strong>PRO</strong>] New Malware Scanner with automated file repair for WordPress.org Plugins and Core.</li>
69
+ <li><strong>(v.0)</strong> NEW: Complete overhaul of events system to better audit and collect statistics.</li>
70
+ <li><strong>(v.0)</strong> IMPROVED: Asynchronous scans - scans run in the background and so support more restrictive hosting.</li>
71
+ <li><strong>(v.0)</strong> IMPROVED: Plugin notification system is much improved.</li>
72
+ <li><strong>(v.0)</strong> IMPROVED: [<strong>PRO</strong>] Plugin Guard uses SVN repositories for file references <a href="https://shsec.io/fw">via WP Hashes API</a>.</li>
73
+ <li><strong>(v.0)</strong> CHANGED: Comment SPAM system now uses WordPress Transients API instead of dedicated DB table.</li>
74
+ <li><strong>(v.0)</strong> ADDED: 100% Translation coverage for French, Spanish, German, Portuguese, Serbian, Bosnian, Dutch. (Russian on the way)</li>
75
+ <li><strong>(v.0)</strong> CHANGED: Major code cleaning/refactoring for much of the plugin. More to come.</li>
76
+ </ul>
77
+ <p>= 7.4 - Series =
78
+ <em>Released: 13th May, 2019</em> - <a href="https://shsec.io/fc">Release Notes</a></p>
79
+ <ul>
80
+ <li><strong>(v.2)</strong> NEW: Options finder/jumper menu lets you find and jump to any option in the plugin instantly.</li>
81
+ <li><strong>(v.2)</strong> NEW: Help/explainer videos for a few sections - more to come.</li>
82
+ <li><strong>(v.2)</strong> FIXES: Fixes for a few problems introduced with the recent UI changes.</li>
83
+ <li><strong>(v.2)</strong> FIXED: Welcome wizard launching was broken.</li>
84
+ <li><strong>(v.1)</strong> NEW: Adjustments and redesign of Shield options pages.</li>
85
+ <li><strong>(v.1)</strong> IMPROVED: Further prep for better internationalization.</li>
86
+ <li><strong>(v.0)</strong> NEW: [<strong>PRO</strong>] <a href="https://shsec.io/fa">Manual/Automatic User Suspension</a></li>
87
+ <li><strong>(v.0)</strong> NEW: Comment SPAM - Increase minimum number of approved comments before scanning is skipped</li>
88
+ <li><strong>(v.0)</strong> NEW: [<strong>PRO</strong>] Comment SPAM - Trusted user roles where comments scanning is skipped</li>
89
+ <li><strong>(v.0)</strong> IMPROVED: AntiBot JS was improperly included when not required.</li>
90
+ <li><strong>(v.0)</strong> IMPROVED: Added a GeoIP caching table and removed bundled GeoIP database - greatly reduces download size.</li>
91
+ <li><strong>(v.0)</strong> FIXED: Inconsistent behaviour when PWA plugin is active and it infinitely reloads pages.</li>
92
+ <li><strong>(v.0)</strong> FIXED: Inconsistent behaviour with Anonymous API blocking.</li>
93
+ <li><strong>(v.0)</strong> IMPROVED: Code improvements and refactoring.</li>
94
+ <li><strong>(v.0)</strong> ADDED: Prep for upcoming malware scanner.</li>
95
+ </ul>
96
+ <p>= 7.3 - Series =
97
+ <em>Released: 15th April, 2019</em> - <a href="https://shsec.io/f0">Release Notes</a></p>
98
+ <ul>
99
+ <li><strong>(v.2)</strong> IMPROVED: Provided inline links for new <a href="https://shsec.io/ez">Bot Signals</a> options.</li>
100
+ <li><strong>(v.2)</strong> CHANGED: Added a workaround for WPML plugin using old, buggy version of TWIG library.</li>
101
+ <li><strong>(v.1)</strong> FIX: Protection against 404 tracking blocking visitors in some cases.</li>
102
+ <li><strong>(v.0)</strong> NEW: [<strong>PRO</strong>] <a href="https://shsec.io/ez">7x New Bot Signals</a> - rules to catch and block bad bots.</li>
103
+ <li><strong>(v.0)</strong> ADDED: Date picker for filtering Audit Log entries.</li>
104
+ <li><strong>(v.0)</strong> IMPROVED: Audit Log viewer now combines entries from the same request into 1 for better readability.</li>
105
+ <li><strong>(v.0)</strong> CHANGED: Use a more refined clearing of WP Fastest Cache.</li>
106
+ <li><strong>(v.0)</strong> FIX: Error displayed when deleting plugins in some cases.</li>
107
+ <li><strong>(v.0)</strong> UPDATED: Translations for Chinese, Finnish, Turkish, Dutch, Italian, and German.</li>
108
+ </ul>
109
+ <p>= 7.2 - Series =
110
+ <em>Released: 7th March, 2019</em> - <a href="https://shsec.io/ep">Release Notes</a></p>
111
+ <ul>
112
+ <li><strong>(v.2)</strong> SKIPPED: with error.</li>
113
+ <li><strong>(v.1)</strong> NEW: Provisional support for WP-CLI - no longer blocks Security Admin protected operations</li>
114
+ <li><strong>(v.1)</strong> FIX: Fix PHP warning notice on login page.</li>
115
+ <li><strong>(v.1)</strong> FIX: Unrecognised file scanning not operating as expected on Windows hosts.</li>
116
+ <li><strong>(v.0)</strong> NEW: <a href="https://shsec.io/eq">Scanner to detect and alert</a> to presence of abandoned plugins.</li>
117
+ <li><strong>(v.0)</strong> FIX: Fix bug with Security Admin passwords.</li>
118
+ <li><strong>(v.0)</strong> FIX: Fix bug with vulnerability scanner not correctly comparing versions.</li>
119
+ </ul>
120
+ <p>= 7.1 - Series =
121
+ <em>Released: 21st February, 2019</em> - <a href="https://shsec.io/ek">Release Notes</a></p>
122
+ <ul>
123
+ <li><strong>(v.2)</strong> IMPROVED: Firewall email notification content now better reflect the information in the audit trail.</li>
124
+ <li><strong>(v.2)</strong> FIX: Firewall email notification was breaking in some instances.</li>
125
+ <li><strong>(v.1)</strong> FIX: IP retrieval.</li>
126
+ <li><strong>(v.0)</strong> NEW: Moved Import/Export UI from Wizard to main Shield Dashboard.</li>
127
+ <li><strong>(v.0)</strong> NEW: [<strong>PRO</strong>] Option to import/export settings using file downloads/uploads</li>
128
+ <li><strong>(v.0)</strong> NEW: [<strong>PRO</strong>] Option to allow visitors to automatically unblock themselves (once in 24hrs)</li>
129
+ <li><strong>(v.0)</strong> NEW: Integrated changelog directly into plugin admin for easy updates (between releases)</li>
130
+ <li><strong>(v.0)</strong> FIXED: WP Core files scanner now correctly ignores certain files as it used to do, pre-v7. e.g. wp-config-sample.php</li>
131
+ <li><strong>(v.0)</strong> FIXED: Shield was indicating plugin/theme file editing was possible, when it in-fact was disabled.</li>
132
+ <li><strong>(v.0)</strong> IMPROVED: Consolidate crons into fewer crons. e.g. all scans run under the same cron.</li>
133
+ </ul>
134
+ <p>= 7.0 - Series =
135
+ <em>Released: 28th January, 2019</em> - <a href="https://shsec.io/ef">Release Notes</a></p>
136
+ <ul>
137
+ <li><strong>(v.4)</strong> IMPROVED: Refactored IP address blocking with improved audit trail messages.</li>
138
+ <li><strong>(v.4)</strong> CHANGED: Expanded anonymous REST API whitelist to include &#39;wpstatistics&#39; namespace.</li>
139
+ <li><strong>(v.4)</strong> IMPROVED: Access protection for shield temp/caching dir.</li>
140
+ <li><strong>(v.4)</strong> IMPROVED: Clarification on reCAPTCHA - v3 is <strong>not</strong> supported.</li>
141
+ <li><strong>(v.4)</strong> IMPROVED: Clarification on user sessions timeout - Shield sets an absolutely session maximum.</li>
142
+ <li><strong>(v.4)</strong> IMPROVED: Options form submission is adjusted to work around poorly restrictive webhosts.</li>
143
+ <li><strong>(v.4)</strong> FIX: Various tweaks and fixes across the plugin.</li>
144
+ <li><strong>(v.4)</strong> FIX: Error with ClassicPress.</li>
145
+ <li><strong>(v.3)</strong> NEW: Automatically whitelist anonymous REST API Access for 3 plugins: Contact Form 7, WooCommerce, JetPack.</li>
146
+ <li><strong>(v.3)</strong> IMPROVED: Security admin login failure messages are clearer.</li>
147
+ <li><strong>(v.3)</strong> IMPROVED: Admin notification for email sending 2FA verification easily lets you resend email.</li>
148
+ <li><strong>(v.3)</strong> IMPROVED: File download code for WordPress Core file scanner repairs.</li>
149
+ <li><strong>(v.3)</strong> IMPROVED: Attempt to also capture B/CC email addresses included in outgoing emails in Audit logs.</li>
150
+ <li><strong>(v.3)</strong> FIX: Allow use of IPv4 ranges in whitelist again.</li>
151
+ <li><strong>(v.3)</strong> CHANGED: Numerous code refactoring and improvements building upon the major v7 release and prepping for v7.1.</li>
152
+ <li><strong>(v.1-2)</strong> FIXED: Some JS fixes.</li>
153
+ <li><strong>(v.0)</strong> NEW: New primary UI for Shield site security management. Easy access to scans, audit trail, user sessions etc.</li>
154
+ <li><strong>(v.0)</strong> NEW: Supports only PHP 5.4 or higher</li>
155
+ <li><strong>(v.0)</strong> NEW: Rebuilt scans architecture and UI</li>
156
+ <li><strong>(v.0)</strong> NEW: A huge amount of code cleaning and refactoring</li>
157
+ <li><strong>(v.0)</strong> CHANGED: Too many many changes and bug fixes to list -best to just take a look! :)</li>
158
+ </ul>
159
+ <p>= 6.10 - Series =
160
+ <em>Released: 15th October, 2018</em> - <a href="https://shsec.io/dg">Release Notes</a></p>
161
+ <ul>
162
+ <li><strong>(v.9)</strong> FIXED: Admin notices displaying to non-admins.</li>
163
+ <li><strong>(v.7)</strong> ADDED: [<strong>PRO</strong>] New option to specify usernames for Security Admin role.</li>
164
+ <li><strong>(v.7)</strong> IMPROVED: Idle user detection.</li>
165
+ <li><strong>(v.7)</strong> IMPROVED: Support for redirect/cancel URLs in 2FA login page.</li>
166
+ <li><strong>(v.7)</strong> CHANGED: Final release before Shield v7. Small warning shown on plugins page if PHP &lt; 5.4</li>
167
+ <li><strong>(v.6)</strong> ADDED: New option to control plugin automatic updates.</li>
168
+ <li><strong>(v.6)</strong> IMPROVED: Enhancements to the experimental bot JS.</li>
169
+ <li><strong>(v.6)</strong> IMPROVED: Support for Easy Digital Downloads forms.</li>
170
  <li><strong>(v.5)</strong> Release skipped.</li>
171
+ <li><strong>(v.4)</strong> FIXED: Couldn&#39;t deactivate plugin.</li>
172
+ <li><strong>(v.3)</strong> ADDED: Support for Ultimate Member forms</li>
173
+ <li><strong>(v.3)</strong> ADDED: Support for LearnPress login/registration forms</li>
174
+ <li><strong>(v.3)</strong> FIXED: Security Admin now correctly honours the WordPress Options zone setting.</li>
175
+ <li><strong>(v.3)</strong> IMPROVED: Distinguish which sub-site (sub-domain) for WPMS installations on <a href="https://shsec.io/c1">Traffic Watcher</a>.</li>
176
+ <li><strong>(v.3)</strong> IMPROVED: Server&#39;s own IP lookup is only attempted once.</li>
177
+ <li><strong>(v.3)</strong> ADDED: Experimental feature to help with some custom 3rd party login/registration forms</li>
178
+ <li><strong>(v.2)</strong> IMPROVED: Visitor IP address detection</li>
179
+ <li><strong>(v.2)</strong> IMPROVED: Automatic whitelisting of Manage WP IP addresses</li>
180
+ <li><strong>(v.2)</strong> IMPROVED: SPAM Comments code enhanced and optimised</li>
181
+ <li><strong>(v.2)</strong> IMPROVED: IP Whitelisting code enhanced and optimised</li>
182
+ <li><strong>(v.2)</strong> IMPROVED: Code cleaning and refactoring.</li>
183
+ <li><strong>(v.1)</strong> FIXED: Googlebot PHP error notice.</li>
184
+ <li><strong>(v.0)</strong> NEW: [<strong>PRO</strong>] 2FA Login Backup Codes - all users can create a backup login code in-case their MFA factors are temporarily unavailable.</li>
185
+ <li><strong>(v.0)</strong> NEW: [<strong>PRO</strong>] White Label - you can now specify custom image for 2FA login screen.</li>
186
+ <li><strong>(v.0)</strong> ADDED: [<strong>PRO</strong>] Custom Exclusion Rules for Traffic Watcher so you can exclude certain User Agents and request paths.</li>
187
+ <li><strong>(v.0)</strong> ADDED: Detection of official spiders/bots for Google, Bing, Apple and Yandex - these visitors will never get blacklisted.</li>
188
+ <li><strong>(v.0)</strong> IMPROVED: Two-Factor Authentication system much improved (+ critical bug fix).</li>
189
+ <li><strong>(v.0)</strong> IMPROVED: Audit Trail entries for 2FA login factors.</li>
190
+ <li><strong>(v.0)</strong> IMPROVED: Fixes for Two-Factor Authentication wizard UX.</li>
191
+ <li><strong>(v.0)</strong> IMPROVED: Traffic Watcher now honours the IP Whitelist.</li>
192
+ <li><strong>(v.0)</strong> IMPROVED: Security Admin restriction for creating/editing/deleting Administrator users is much improved.</li>
193
+ <li><strong>(v.0)</strong> IMPROVED: All Shield cookies are SSL-only by default for HTTPS sites.</li>
194
+ <li><strong>(v.0)</strong> FIXED: GASP checkbox Javascript breaking in a particular scenario.</li>
195
+ <li><strong>(v.0)</strong> ADDED: Optional plugin deactivation survey.</li>
196
+ </ul>
197
+ <p>= 6.9.0 - Series =
198
+ <em>Released: 6th September, 2018</em> - <a href="https://shsec.io/dc">Release Notes</a></p>
199
+ <ul>
200
+ <li><strong>(v.0)</strong> NEW: [<strong>PRO</strong>] <a href="https://shsec.io/c1">Traffic Watcher</a> - live tracking of all requests to your site.</li>
201
+ <li><strong>(v.0)</strong> NEW: [<strong>PRO</strong>] <a href="https://shsec.io/c1">Yubikey</a> - Allows for multiple Yubikeys on the same user profile.</li>
202
+ <li><strong>(v.0)</strong> ADDED: [<strong>PRO</strong>] Option to include listing of affected files within Hack Guard notification emails.</li>
203
+ <li><strong>(v.0)</strong> ADDED: Option to delete the Security Admin Access Key</li>
204
+ <li><strong>(v.0)</strong> ADDED: Option to add WooCommerce roles to 2FA-Email setting.</li>
205
+ <li><strong>(v.0)</strong> CHANGED: Basic Stats system now requires minimum PHP v5.4.</li>
206
+ <li><strong>(v.0)</strong> CHANGED: Password Policies now requires minimum WordPress v4.4.</li>
207
+ <li><strong>(v.0)</strong> IMPROVED: Password expiration now redirects to the &#39;set password&#39; screen, instead of the user profile.</li>
208
+ <li><strong>(v.0)</strong> IMPROVED: Password capture for purposes of password policies is improved.</li>
209
+ <li><strong>(v.0)</strong> IMPROVED: You can now delete the &#39;forceoff&#39; file from inside the WP Admin.</li>
210
+ <li><strong>(v.0)</strong> IMPROVED: Audit Trail entries for emails will identify the file that&#39;s calling the <code>wp_mail</code> function.</li>
211
+ <li><strong>(v.0)</strong> IMPROVED: Audit Trail entries for post editing will identify the post type wherever possible.</li>
212
+ <li><strong>(v.0)</strong> IMPROVED: Audit Trail entries will try to display all message text correctly.</li>
213
+ <li><strong>(v.0)</strong> IMPROVED: Login/Register/Password forms are only checked when visitor is not logged-in.</li>
214
+ <li><strong>(v.0)</strong> IMPROVED: Major database code refactoring and other code improvements.</li>
215
+ <li><strong>(v.0)</strong> IMPROVED: User sessions handling.</li>
216
+ <li><strong>(v.0)</strong> IMPROVED: Security Admin UX - ajax session checking, with admin notifications and auto-page reload.</li>
217
+ <li><strong>(v.0)</strong> IMPROVED: Security Admin password setting now requires a confirmation password entry.</li>
218
+ <li><strong>(v.0)</strong> IMPROVED: Refined Cooldown timing system.</li>
219
+ <li><strong>(v.0)</strong> IMPROVED: Refined Bot checkbox Javascript.</li>
220
+ <li><strong>(v.0)</strong> IMPROVED: Cron entry cleanup after deactivation.</li>
221
+ <li><strong>(v.0)</strong> UPDATED: Bootstrap libraries to latest release v4.1.3.</li>
222
+ <li><strong>(v.0)</strong> FIXED: Potential bug with Plugin/Themes guard scanning.</li>
223
+ <li><strong>(v.0)</strong> FIXED: PHP Warning(s).</li>
224
+ </ul>
225
+ <p>= 6.8 Series =
226
+ <em>Released: 11th June, 2018</em> - <a href="https://shsec.io/d4">Release Notes</a></p>
227
+ <ul>
228
+ <li><strong>(v.2)</strong> FIXED: Bug with multi-factor authentication verification.</li>
229
+ <li><strong>(v.2)</strong> FIXED: Bug with chosen reCAPTCHA style not being honoured on login pages</li>
230
+ <li><strong>(v.2)</strong> FIXED: Bug with Invisible reCAPTCHA + WooCommerce</li>
231
+ <li><strong>(v.2)</strong> FIXED: Bug with Pwned passwords always being checked even if setting turned off.</li>
232
+ <li><strong>(v.1)</strong> FIXED: A couple of bugs with WooCommerce reCAPTCHA processing.</li>
233
+ <li><strong>(v.1)</strong> FIXED: A bug with user sessions cleaning</li>
234
+ <li><strong>(v.0)</strong> ADDED: [<strong>PRO</strong>] White Label - ability to re-brand the entire Shield Security plugin to your company brand.</li>
235
+ <li><strong>(v.0)</strong> ADDED: [<strong>PRO</strong>] Option for all users to receive notification email upon login to their accounts.</li>
236
+ <li><strong>(v.0)</strong> IMPROVED: Completely rebuilt the bot and reCAPTCHA login protection system.</li>
237
+ <li><strong>(v.0)</strong> IMPROVED: Import/Export system hugely improved with respect to automated push of options from Master sites.</li>
238
+ <li><strong>(v.0)</strong> IMPROVED: A different approach to sessions management that should handle sessions a bit better.</li>
239
+ <li><strong>(v.0)</strong> IMPROVED: Expired user sessions are cleaned from the DB using a cron, and on Insights Dashboard load.</li>
240
+ </ul>
241
+ <p>= 6.7 Series =
242
+ <em>Released: 21st May, 2018</em> - <a href="https://shsec.io/cx">Release Notes</a></p>
243
+ <ul>
244
+ <li><strong>(v.2)</strong> ADDED: [<strong>PRO</strong>] Admin Notes feature - Notes can now be easily deleted (editing will not be possible).</li>
245
+ <li><strong>(v.2)</strong> UPDATED: Some translations.</li>
246
+ <li><strong>(v.2)</strong> FIXED: A few bugs with the Insights Dashboard.</li>
247
+ <li><strong>(v.2)</strong> FIXED: Removed the dependency on jQuery with Invisible reCAPTCHA.</li>
248
+ <li><strong>(v.1)</strong> FIXED: A few bugs with the Insights Dashboard</li>
249
+ <li><strong>(v.1)</strong> ADDED: [<strong>PRO</strong>] Admin Notes feature - you can now add notes to the Shield plugin in the Insights Dashboard.</li>
250
+ <li><strong>(v.0)</strong> ADDED: All-New Insights Dashboard providing a high-level overview of your site security, with recommendations.</li>
251
+ <li><strong>(v.0)</strong> ADDED: Helpful, explanatory videos directly into the Guided Welcome Wizard.</li>
252
+ <li><strong>(v.0)</strong> ADDED: A simple test cron to demonstrate whether your site crons are running.</li>
253
+ <li><strong>(v.0)</strong> ADDED: [<strong>PRO</strong>] Full support for new WordPress GDPR Privacy Policy controls for exporting and erasing data.</li>
254
+ <li><strong>(v.0)</strong> ADDED: [<strong>PRO</strong>] New GDPR guided wizard for exporting/erasing particular data based on custom search results.</li>
255
+ <li><strong>(v.0)</strong> CHANGED: Guided Wizards now load through WP admin to fix ajax problems for poorly configured SSL on some sites</li>
256
+ <li><strong>(v.0)</strong> IMPROVED: Upgraded Bootstrap library to 4.1.1.</li>
257
+ <li><strong>(v.0)</strong> IMPROVED: Compatibility with AIO Events Cal - they like to force their old Twig libraries on everyone else.</li>
258
+ </ul>
259
+ <p>= 6.6 Series =
260
+ <em>Released: 19th March, 2018</em> - <a href="https://shsec.io/c3">Release Notes</a></p>
261
+ <ul>
262
+ <li><strong>(v.7)</strong> IMPROVED: reCAPTCHA JS is only included on pages where it&#39;s actually used by Shield.</li>
263
+ <li><strong>(v.7)</strong> IMPROVED: Upgrade Bootstrap library to 4.1.0.</li>
264
+ <li><strong>(v.7)</strong> IMPROVED: Include jQuery for the plugin badge as required</li>
265
+ <li><strong>(v.6)</strong> ADDED: Small exclusion in the firewall for a jetpack parameter.</li>
266
+ <li><strong>(v.6)</strong> ADDED: SVGs to the default list of files scanned by the plugin guard.</li>
267
+ <li><strong>(v.6)</strong> ADDED: Workaround for a <a href="https://wordpress.org/support/topic/forcefully-executing-wp_footer-not-compatible-with-other-plugins/">ridiculous NGG bug</a>.</li>
268
+ <li><strong>(v.1-4)</strong> FIXED: Various small fixes and improvements</li>
269
+ <li><strong>(v.4)</strong> FIXED: PHP Fatal Error on wp object cache.</li>
270
+ <li><strong>(v.0)</strong> NEW: [<strong>PRO</strong>] <a href="https://shsec.io/c1">Keyless Activation of Pro licenses</a>.</li>
271
+ <li><strong>(v.0)</strong> ADDED: <a href="https://shsec.io/c2">WordPress Password Policies</a>.</li>
272
+ <li><strong>(v.0)</strong> ADDED: Pwned Passwords Detection.</li>
273
+ <li><strong>(v.0)</strong> IMPROVED: Major rewrite of plugin AJAX handling.</li>
274
+ <li><strong>(v.0)</strong> IMPROVED: Notices to indicate the time of the last scans.</li>
275
+ <li><strong>(v.0)</strong> FIXED: A few bugs</li>
276
+ </ul>
277
+ <p>= 6.5 Series =
278
+ <em>Released: 5th March, 2018</em> - <a href="https://shsec.io/bu">Release Notes</a></p>
279
+ <ul>
280
+ <li><strong>(v.0)</strong> IMPROVED: <a href="https://shsec.io/bq">Plugin Guard</a> better handles the case where a plugin/theme has been entirely renamed/removed.</li>
281
+ <li><strong>(v.0)</strong> IMPROVED: Attempts to access the XML-RPC system when it&#39;s disabled will now result in a transgression increment in the IP Black List</li>
282
+ <li><strong>(v.0)</strong> IMPROVED: Try to prevent black listing the server&#39;s own public IP address where visitor IP address detection is not correctly configured.</li>
283
+ <li><strong>(v.0)</strong> ADDED: [<strong>PRO</strong>] Provisional support for not processing 2FA logins for Woocommerce Social Login plugin.</li>
284
+ <li><strong>(v.0)</strong> FIXED: Plugin Guard better handles ignoring non-WordPress.org Plugins/Themes</li>
285
+ <li><strong>(v.0)</strong> FIXED: A few small bugs</li>
286
+ </ul>
287
+ <p>= 6.4 Series =
288
+ <em>Released: 26th February, 2018</em> - <a href="https://shsec.io/br">Release Notes</a></p>
289
+ <ul>
290
+ <li><strong>(v.1-4)</strong> FIXED: Various Fixes</li>
291
+ <li><strong>(v.0)</strong> ADDED: [<strong>PRO</strong>] New Scanner to <a href="https://shsec.io/bq">detect file changes for active plugins and themes</a></li>
292
+ <li><strong>(v.0)</strong> IMPROVED: Automatic updates for vulnerable plugins ignores <a href="https://shsec.io/bc">automatic updates delay setting</a></li>
293
+ <li><strong>(v.0)</strong> CHANGED: Email notifications for scanners will now link to the Wizard where possible, instead of listing files.</li>
294
+ </ul>
295
+ <p>= 6.3 Series =
296
+ <em>Released: 12th February, 2018</em> - <a href="https://shsec.io/bc">Release Notes</a></p>
297
+ <ul>
298
+ <li><strong>(v.3)</strong> FIXED: Bug with automatic updates delay setting</li>
299
+ <li><strong>(v.2)</strong> CHANGED: Changed a text that seems to cause servers to swallow-up emails. <a href="https://shsec.io/bi">See here for more reliable email</a></li>
300
+ <li><strong>(v.1)</strong> FIXED: Options page javascript to work around conflicts.</li>
301
+ <li><strong>(v.0)</strong> ADDED: [<strong>PRO</strong>] <a href="https://shsec.io/bc">Automatic updates stability delay</a></li>
302
+ <li><strong>(v.0)</strong> IMPROVED: Complete <a href="https://shsec.io/bd">plugin UI rebuild</a>, using the new Bootstrap 4.</li>
303
+ <li><strong>(v.0)</strong> FIXED: A few bugs with Google Authenticator.</li>
304
+ </ul>
305
+ <p>= 6.2 Series =
306
+ <em>Released: 31st January, 2018</em> - <a href="https://shsec.io/b6">Release Notes</a></p>
307
+ <ul>
308
+ <li><strong>(v.2)</strong> FIXED: Fix for IP Manager PHP error.</li>
309
+ <li><strong>(v.2)</strong> IMPROVED: Two-factor verification email.</li>
310
+ <li><strong>(v.1)</strong> FIXED: Bug where administrator login email notification setting is not being honoured.</li>
311
+ <li><strong>(v.1)</strong> IMPROVED: If a site is having trouble with database creation, User Sessions wont lock you out.</li>
312
+ <li><strong>(v.0)</strong> IMPROVED: Major overhaul of the Shield User Sessions system.</li>
313
+ <li><strong>(v.0)</strong> IMPROVED: Link the Security Admin authentication with the new Sessions system.</li>
314
+ <li><strong>(v.0)</strong> IMPROVED: Major overhaul to plugin&#39;s user meta data storage, limiting to a single DB entry for all data.</li>
315
+ <li><strong>(v.0)</strong> ADDED: [<strong>PRO</strong>] Ability to increase frequency of file system scans up to once every hour.</li>
316
+ <li><strong>(v.0)</strong> ADDED: [<strong>PRO</strong>] Add a &quot;remember me&quot; option, to allow users to skip Multi-factor authentication for a set number of days.</li>
317
+ </ul>
318
+ <p>= 6.1 Series =
319
+ <em>Released: 15th January, 2018</em> - <a href="https://shsec.io/ay">Release Notes</a></p>
320
+ <ul>
321
+ <li><strong>(v.1)</strong> FIXED: Verify link missing from the two-factor authentication verification email.</li>
322
+ <li><strong>(v.0)</strong> ADDED: 3x more Shield Wizards: Multi-factor Authentication, Core File Scanning, Unrecognised File Scanning.</li>
323
+ <li><strong>(v.0)</strong> ADDED: You can now use regular expressions for file exclusions in the &#39;Unrecognised File Scanner&#39;.</li>
324
+ <li><strong>(v.0)</strong> CHANGED: File Scanner email notifications now link to the appropriate scanner wizard directly.</li>
325
+ <li><strong>(v.0)</strong> IMPROVED: Plugin options pages restyling.</li>
326
+ <li><strong>(v.0)</strong> IMPROVED: Plugin refactoring and improvements.</li>
327
+ </ul>
328
+ <p>= 6.0 Series =
329
  <em>Released: 18th December, 2017</em></p>
330
  <ul>
331
+ <li><strong>(v.0)</strong> ADDED: All-new Shield Welcome and Setup Wizard - more helpful guided wizards to come.</li>
332
+ <li><strong>(v.0)</strong> ADDED: [<strong>PRO</strong>] <a href="https://shsec.io/at">Shield options import and export</a></li>
333
+ <li><strong>(v.0)</strong> ADDED: [<strong>PRO</strong>] In conjunction with import/export - Shield Security Network: automated options syncing.</li>
334
+ <li><strong>(v.0)</strong> CHANGED: Going forward, new features and options will <a href="https://shsec.io/au">support only PHP 5.4+</a>. Existing features will remain unaffected.</li>
335
  </ul>
336
+ <p>= 5.20 Series =
337
  <em>Released: 11th December, 2017</em></p>
338
  <ul>
339
+ <li><strong>(v.0)</strong> IMPROVED: [<strong>PRO</strong>] Audit Trail length are configurable. Length for free is 50 entries (the original unpaginated limit)</li>
340
+ <li><strong>(v.0)</strong> IMPROVED: Large redesign of options sections to be more intuitive and cleaner</li>
341
+ <li><strong>(v.0)</strong> IMPROVED: Added dedicated help section for each module.</li>
342
+ <li><strong>(v.0)</strong> IMPROVED: Certain modules have an new <em>Actions</em> centre, such a Audit Trail viewer and User Sessions manager</li>
343
+ <li><strong>(v.0)</strong> IMPROVED: Audit Trails are now ajax-paginated. You can browse through all your audit trail entries</li>
344
+ <li><strong>(v.0)</strong> IMPROVED: User session tables are also ajax-paginated.</li>
345
  </ul>
346
+ <p>= 5.19 Series =
347
  <em>Released: 4th December, 2017</em></p>
348
  <ul>
349
+ <li><strong>(v.1)</strong> FIXED: Plugin Vulnerabilities scan for premium plugins.</li>
350
+ <li><strong>(v.0)</strong> ADDED: [<strong>PRO</strong>] Automated WordPress plugins vulnerability scanner with auto updates email notifications</li>
351
+ <li><strong>(v.0)</strong> ADDED: Added Google reCAPTCHA support for register/forget password pages.</li>
352
+ <li><strong>(v.0)</strong> ADDED: [<strong>PRO</strong>] Support for Multi-Factor Authentication for WooCommerce and other 3rd party plugins.</li>
353
+ <li><strong>(v.0)</strong> ADDED: [<strong>PRO</strong>] Bot-protection/Google reCAPTCHA support for BuddyPress register pages.</li>
354
  </ul>
355
+ <p>= 5.18 Series =
356
  <em>Released: 27th November, 2017</em></p>
357
  <ul>
358
+ <li><strong>(v.0)</strong> ADDED: [<strong>PRO</strong>] Invisible Google reCAPTCHA option.</li>
359
+ <li><strong>(v.0)</strong> ADDED: [<strong>PRO</strong>] Support for Google reCAPTCHA themes - light and dark.</li>
360
+ <li><strong>(v.0)</strong> IMPROVEMENT: Google reCAPTCHA is more reliable and configurable.</li>
361
  </ul>
362
+ <p>= 5.17 Series =
363
  <em>Released: 23rd November, 2017</em></p>
364
  <ul>
365
+ <li><strong>(v.0)</strong> ADDED: Shield Security goes Pro! Added new options and extras to premium clients.</li>
366
+ <li><strong>(v.0)</strong> IMPROVEMENT: Fix and improvement to Google reCAPTCHA.</li>
367
+ <li><strong>(v.0)</strong> ADDED: [<strong>PRO</strong>] Support for Woocommerce and Easy Digital Downloads login/registration form protection.</li>
368
+ <li><strong>(v.0)</strong> ADDED: [<strong>PRO</strong>] Ability to customise most user-facing texts.</li>
369
+ <li><strong>(v.0)</strong> ADDED: [<strong>PRO</strong>] Extra IP Transgression signal.</li>
370
  </ul>
371
+ <p>= 5.16 Series =
372
  <em>Released: 16th October, 2017</em></p>
373
  <p>With this release, we fixed a clash of options for Google reCAPTCHA. Every attempt was made to ensure no interruption to your existing settings, but please check to ensure your reCAPTCHA settings are as you expect them to be.</p>
374
  <ul>
375
+ <li><strong>(v.4)</strong> FIX: Error with incorrect/unprefixed database table name used in SQL query.</li>
376
+ <li><strong>(v.3)</strong> IMPROVEMENT: Tweak to the Visitor IP Auto-detection to better ensure CloudFlare IP addresses are ignored.</li>
377
+ <li><strong>(v.3)</strong> IMPROVEMENT: Plugin Badge will now stay closed when a visitor closes it.</li>
378
+ <li><strong>(v.2)</strong> FIX: Removed some namespace parsing that broke on sites with PHP 5.2.</li>
379
+ <li><strong>(v.1)</strong> FIX: 404 page displayed for password reset request when Login URL is renamed.</li>
380
+ <li><strong>(v.0)</strong> IMPROVEMENT: Much better auto-detection of valid request/visitor IP addresses.</li>
381
+ <li><strong>(v.0)</strong> FIX: Clashing of reCAPTCHA options for Comments and Login Protection.</li>
382
+ <li><strong>(v.0)</strong> IMPROVEMENT: Statistic Reporting database management and pruning.</li>
383
+ <li><strong>(v.0)</strong> FIX: Various system fixes and improvements.</li>
384
+ </ul>
385
+ <p>= 5.15 Series =
386
  <em>Released: 21st September, 2017</em></p>
387
  <ul>
388
+ <li><strong>(v.1)</strong> FIX: Processing AJAX requests from the Network Admin side of WordPress.</li>
389
+ <li><strong>(v.1)</strong> IMPROVEMENTS: Better handling of file exclusions in the Hack Guard module.</li>
390
+ <li><strong>(v.1)</strong> IMPROVEMENTS: Better handling of fatal errors in loading Shield where some core files are missing.</li>
391
+ <li><strong>(v.0)</strong> ADDED: New HTTP Security Header: Referrer Policy.</li>
392
+ <li><strong>(v.0)</strong> ADDED: Supports paths for file exclusions in the Unrecognised File Scanner.</li>
393
+ <li><strong>(v.0)</strong> IMPROVEMENTS: Better interception of unintentional redirects to the hidden Login URL (e.g. /wp-admin/customize.php).</li>
394
+ <li><strong>(v.0)</strong> IMPROVEMENTS: Better handling of email sending entries in the Audit Trail.</li>
395
+ <li><strong>(v.0)</strong> IMPROVEMENTS: Improved (tabbed) display of Audit Trail.</li>
396
+ <li><strong>(v.0)</strong> IMPROVEMENTS: Better generation &amp; handling of the One Time Password for email-based two-factor authentication.</li>
397
+ <li><strong>(v.0)</strong> IMPROVEMENTS: Some code clean up and refactoring.</li>
398
+ </ul>
399
+ <p>= 5.14 Series =
400
  <em>Released: 9th September, 2017</em></p>
401
  <ul>
402
+ <li><strong>(v.0)</strong> ADDED: Option for administrators to manually override and set the source of the visitor IP address.</li>
403
+ <li><strong>(v.0)</strong> UPDATED: In-plugin documentation links to updated and revised helpdesk articles/blogs.</li>
404
+ <li><strong>(v.0)</strong> IMPROVEMENTS: Strip out any non-alphanumeric characters uses in the generation of Google Authenticator URLs.</li>
405
+ <li><strong>(v.0)</strong> FIX: Shield now ignores any requests sent to Rest API URIs with respect to Shield user sessions.</li>
406
  </ul>
407
+ <p>= 5.13 Series =
408
  <em>Released: 15th August, 2017</em></p>
409
  <ul>
410
+ <li><strong>(v.2)</strong> IMPROVEMENTS: Small adjustment to handling of Shield User sessions in conjunction with WordPress sessions.</li>
411
+ <li><strong>(v.2)</strong> FIX: Restore display of help links for options.</li>
412
+ <li><strong>(v.1)</strong> FIX: PHP 5.2 incompatibility.</li>
413
+ <li><strong>(v.0)</strong> ADDED: New option for <a href="https://shsec.io/94">Unrecognised File Scanner</a> to scan the Uploads folder for JS and PHP files.</li>
414
+ <li><strong>(v.0)</strong> ADDED: Option to provide custom list of files to be excluded from the <a href="https://shsec.io/94">Unrecognised File Scanner</a>.</li>
415
  </ul>
416
+ <p>= 5.12 Series =
417
  <em>Released: 3rd August, 2017</em></p>
418
  <ul>
419
+ <li><strong>(v.2)</strong> IMPROVEMENTS: Improved support for Windows IIS hosting for <a href="https://shsec.io/94">Unrecognised File Scanner</a></li>
420
+ <li><strong>(v.2)</strong> CHANGED: Removed the email-based 2FA automatic login link.</li>
421
+ <li><strong>(v.2)</strong> FIX: Potential bug with Shield not recognising plugin configuration updates and not rebuilding options accordingly.</li>
422
+ <li><strong>(v.1)</strong> ADDED: A few more exclusions for the <a href="https://shsec.io/94">Unrecognised File Scanner</a></li>
423
+ <li><strong>(v.1)</strong> FIX: Fix for Fatal error.</li>
424
+ <li><strong>(v.0)</strong> ADDED: <a href="https://shsec.io/94">Unrecognised File Scanner</a> release. Automatically detect and delete<pre><code> <span class="hljs-keyword">any</span> <span class="hljs-built_in">files</span> present <span class="hljs-keyword">in</span> core WordPress <span class="hljs-built_in">directories</span> that aren<span class="hljs-string">'t part of your core installation.</span>
425
+ </code></pre></li>
426
+ <li><strong>(v.0)</strong> ADDED: Updated Firewall rules for SQL under the &#39;Aggressive&#39; rule set.</li>
427
  </ul>
428
+ <p>= 5.11 Series =
429
  <em>Released: 26th July, 2017</em></p>
430
  <ul>
431
+ <li><strong>(v.1)</strong> FIX: JSON syntax</li>
432
+ <li><strong>(v.0)</strong> IMPROVEMENTS: Final preparation for <a href="https://shsec.io/83">Shield Central</a> release.</li>
433
  </ul>
434
+ <p>= 5.10 Series =
435
  <em>Released: 19th June, 2017</em></p>
436
  <ul>
437
+ <li><strong>(v.2)</strong> FIXED: Fatal error with GASP + Password Reset.</li>
438
+ <li><strong>(v.2)</strong> FIXED: Fatal error with failing reCAPTCHA HTTP requests.</li>
439
+ <li><strong>(v.1)</strong> IMPROVEMENTS: Further preparation for <a href="https://shsec.io/83">Shield Central</a> release.</li>
440
+ <li><strong>(v.0)</strong> ADDED: More in-depth reporting and statistics gathering - options for reports will be made available<pre><code> <span class="hljs-keyword">in</span> <span class="hljs-selector-tag">a</span> later release.
441
+ </code></pre></li>
442
  </ul>
443
+ <p>= 5.9 Series =
444
  <em>Released: 31st May, 2017</em></p>
445
  <ul>
446
+ <li><strong>(v.0)</strong> ADDED: Help Videos for 1 or 2 modules. More to come and just testing format and uptake.</li>
447
+ <li><strong>(v.0)</strong> ADDED: Special handling for WP Fastest Cache.</li>
448
  <li><strong>(v.0)</strong> CHANGE: Configuration for automatic self-update for the Shield plugin has been removed.</li>
449
+ <li><strong>(v.0)</strong> CHANGE: No longer remove an existing user session when accessed from another IP address. Just redirect.<pre><code> Protects existing, legitimate sessions <span class="hljs-keyword">from</span> <span class="hljs-keyword">being</span> forcefully expired.
450
+ </code></pre></li>
451
+ <li><strong>(v.0)</strong> FIXED: Danish string translation.</li>
452
  </ul>
453
+ <p>= 5.8 Series =
454
  <em>Released: 7th April, 2017</em></p>
455
  <ul>
456
+ <li><strong>(v.2)</strong> IMPROVEMENTS: The core file scanner now works more reliably for international WordPress installations.</li>
457
  <li><strong>(v.2)</strong> CHANGE: Login Cooldown now uses only the flag file as an indicator of login times.</li>
458
  <li><strong>(v.2)</strong> CHANGE: Filter to allow for changing the two factor timeout period, from 5 (minutes). Filter: <code>icwp-wpsf-login_intent_timeout</code></li>
459
  <li><strong>(v.2)</strong> CHANGE: Changed timeout for two-factor authentication email to 5 minutes to account for slower email-sending providers.</li>
460
  <li><strong>(v.2)</strong> CHANGE: Added further clarification to the Login Notification email indicating that two-factor authentication was pending.</li>
461
+ <li><strong>(v.1)</strong> FIXED: Fixed a couple of bugs with the Login Authentication Portal, for certain edge cases.</li>
462
+ <li><strong>(v.0)</strong> CHANGE: Major overhaul of <a href="https://shsec.io/87">Two-Factor / Multi-Factor Login Authentication</a>.</li>
463
+ <li><strong>(v.0)</strong> CHANGE: <a href="https://shsec.io/86">Introduction of Login Authentication Portal</a> for improved Multi-Factor Authentication.</li>
464
+ <li><strong>(v.0)</strong> ADDED: Option to choose between two-factor or multi-factor login authentication.</li>
465
+ <li><strong>(v.0)</strong> ADDED: Administrators can remove Google Authenticator from another user&#39;s profile.</li>
466
+ <li><strong>(v.0)</strong> ADDED: When Security Admin is active, only Security Admins may remove Google Authenticator from other admins.</li>
467
  <li><strong>(v.0)</strong> CHANGE: Yubikey login authentication is now managed directly from the User Profile screen, as with Google Authenticator.</li>
468
  <li><strong>(v.0)</strong> CHANGE: Email-based login authentication no longer uses a separate database table.</li>
469
+ <li><strong>(v.0)</strong> FIXED: Core file scanning now adequately handles Windows/Unix new lines during scan.</li>
470
+ <li><strong>(v.0)</strong> FIXED: Certain crons weren&#39;t setup correctly.</li>
471
+ <li><strong>(v.0)</strong> IMPROVEMENTS: Further preparation for <a href="https://shsec.io/83">Shield Central</a> release.</li>
472
  </ul>
473
  <p>= 5.7 Series =</p>
474
  <ul>
475
+ <li><strong>(v.3)</strong> FIXED: Attempt to improve the Google Authenticator flow for more reliable activation.</li>
476
+ <li><strong>(v.2)</strong> IMPROVEMENTS: More admin notices when saving Google Authenticator settings.</li>
477
+ <li><strong>(v.2)</strong> IMPROVEMENTS: Further preparation for <a href="https://shsec.io/83">Shield Central</a> release.</li>
478
  <li><strong>(v.1)</strong> Skipped</li>
479
+ <li><strong>(v.0)</strong> ADDED: Shortcode for displaying plugin badge in pages/posts.</li>
480
  <li><strong>(v.0)</strong> CHANGE: Enabled JS eval() for the Content Security Policy by default.</li>
481
+ <li><strong>(v.0)</strong> IMPROVEMENTS: Replace YAML configuration files with JSON.</li>
482
+ <li><strong>(v.0)</strong> IMPROVEMENTS: Preparation for <a href="https://shsec.io/83">Shield Central</a> release.</li>
483
+ <li><strong>(v.0)</strong> IMPROVEMENTS: Security Admin notices are more refined and optimized.</li>
484
+ <li><strong>(v.0)</strong> IMPROVEMENTS: Removed unnecessary files/code.</li>
485
  </ul>
486
  <p>= 5.6 Series =</p>
487
  <ul>
488
+ <li><strong>(v.2)</strong> CHANGE: Fix an instance where the hidden Login URL would be leaded.</li>
489
+ <li><strong>(v.1)</strong> CHANGE: Replaying of Yubikey one-time-passwords is no longer permitted.</li>
490
+ <li><strong>(v.1)</strong> ADDED: Filter for login form GASP fields.</li>
491
+ <li><strong>(v.1)</strong> ADDED: Filter for comment form GASP fields.</li>
492
+ <li><strong>(v.1)</strong> CHANGE: Improved compatibility of HTTP Headers with WP Super Cache.</li>
493
+ <li><p><strong>(v.0)</strong> ADDED: Option to disable anonymous Rest API access. WordPress v4.7+ only. Note that if another plugin</p>
494
+ <pre><code> <span class="hljs-keyword">or</span> service authenticates <span class="hljs-keyword">the</span> request <span class="hljs-keyword">it</span> will be honoured, whether anonymous <span class="hljs-keyword">or</span> <span class="hljs-keyword">not</span>.
495
+ </code></pre><p>= 5.5 Series =</p>
496
  </li>
497
+ <li><p><strong>(v.6)</strong> IMPROVED: Fixed possible leak of the Login URL from the &#39;Hide WP Login URL&#39; feature.</p>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
498
  </li>
499
+ <li><strong>(v.5)</strong> ADDED: Ability to add custom protocols to the domains (apart from http/s) to the Content Security Policy</li>
500
+ <li><strong>(v.5)</strong> FIXED: Bug where automatic update emails would contain empty plugins.</li>
501
+ <li><strong>(v.5)</strong> FIXED: Javascript scope on GASP form elements.</li>
502
+ <li><strong>(v.5)</strong> FIXED: Various fixes and code improvements.</li>
503
+ <li><strong>(v.4)</strong> FIXED: Bug with data cleaning/storage that caused stored options to balloon resulting in database timeouts. (only certain options affected)</li>
504
+ <li><strong>(v.4)</strong> IMPROVED: Sometimes &quot;anti-virus&quot; scanners scared normal, everyday hard-working folk by identifying a Shield file as being a virus, because they&#39;re not very clever - reduced chances of this.</li>
505
+ <li><strong>(v.3)</strong> ADDED: Fix for WordPress Multisite where the correct database prefix wasn&#39;t being used.</li>
506
+ <li><strong>(v.2)</strong> ADDED: Filter to allow modification of the email footer</li>
507
+ <li><strong>(v.2)</strong> ADDED: Block auto-updates on Shield itself if PHP &lt; 5.3 and new version is v6.0+</li>
508
+ <li><strong>(v.2)</strong> FIXED: Missing Link</li>
509
+ <li><strong>(v.2)</strong> FIXED: Plugin Installation ID wasn&#39;t always being set</li>
510
+ <li><strong>(v.2)</strong> TRANSLATIONS: Dutch (56%)</li>
511
+ <li><strong>(v.1)</strong> ADDED: Built-in forceful protection in the form of a wp_die() against the (currently) un-patched W3 Total Cache XSS vulnerability <a href="https://shsec.io/7j">more info</a></li>
512
+ <li><strong>(v.1)</strong> IMPROVED: Better XMLRPC Lockdown - prevents ANY XMLRPC command processing.</li>
513
+ <li><strong>(v.1)</strong> IMPROVED: Make certain strings translatable</li>
514
+ <li><strong>(v.1)</strong> IMPROVED: Wrap-up certain login form elements into spans/divs to allow styling etc.</li>
515
+ <li><strong>(v.1)</strong> IMPROVED: PHP Version number cleaning during stats tracking.</li>
516
+ <li><strong>(v.0)</strong> ADDED: Options and statistics tracking ability. Over time we are looking to share statistics and performance metrics of Shield.</li>
517
+ <li><strong>(v.0)</strong> IMPROVED: Performance for options loading, especially for web hosts that don&#39;t permit file writing</li>
518
+ <li><strong>(v.0)</strong> CHANGED: Numerous fixes and code improvements.</li>
519
+ <li><strong>(v.0)</strong> CHANGED: Removed query that deletes old GASP comment tokens on normal page loads.</li>
520
+ <li><strong>(v.0)</strong> CHANGED: Google reCAPTCHA is now based on the locale of the website, not auto-detected.</li>
521
+ <li><strong>(v.0)</strong> FIXED: Now URL encodes the username in the link for two-factor authentication by email.</li>
522
+ <li><strong>(v.0)</strong> FIXED: If the xmlrpc.php has been deleted, this is now ignore by the file scanner</li>
523
+ <li><strong>(v.0)</strong> TRANSLATIONS: Dutch (38%), Portuguese (32%)</li>
524
  </ul>
525
  <p>= 5.4 Series =</p>
526
  <ul>
527
+ <li><strong>(v.5)</strong> CHANGED: User Management module is no-longer enabled by default on clean installations</li>
528
+ <li><strong>(v.5)</strong> CHANGED: Made the GASP checkbox for Login protection clickable by label. <a href="https://github.com/FernleafSystems/Shield/pull/22">Thanks Aubrey!</a></li>
529
+ <li><strong>(v.5)</strong> CHANGED: Shield Statistics only shows for WordPress admins (instead of all users)</li>
530
+ <li><strong>(v.5)</strong> FIXED: Added a couple of guards to ensure data is of the correct format to prevent spurious errors</li>
531
+ <li><strong>(v.5)</strong> FIXED: Bug where automatic file repair links from emails we&#39;re not working.</li>
532
  <li><strong>(v.4)</strong> SKIPPED.</li>
533
+ <li><strong>(v.3)</strong> FIXED: Various fixes and improvements</li>
534
+ <li><strong>(v.3)</strong> CHANGED: Lots of cleaning of old code.</li>
535
+ <li><strong>(v.3)</strong> REMOVED: Various old, unused options, and the force_ssl_login option as it&#39;s deprecated by WordPress Core</li>
536
+ <li><strong>(v.3)</strong> TRANSLATIONS: Dutch (36%), Swedish (35%)</li>
537
+ <li><strong>(v.3)</strong> FIXED: Various fixes and improvements</li>
538
+ <li><strong>(v.3)</strong> CHANGED: Lots of cleaning of old code.</li>
539
+ <li><strong>(v.3)</strong> REMOVED: Various old, unused options, and the force_ssl_login option as it&#39;s deprecated by WordPress Core</li>
540
+ <li><strong>(v.3)</strong> TRANSLATIONS: Dutch (36%), Swedish (35%)</li>
541
+ <li><strong>(v.2)</strong> ADDED: A guard around certain modules like, User Sessions, to ensure the DB has been initiated properly before use.</li>
542
+ <li><strong>(v.2)</strong> ADDED: Exclusion for Swedish license files that don&#39;t exist in the SVN repo.</li>
543
+ <li><strong>(v.2)</strong> ADDED: Parameter exclusion for reCAPTCHA.</li>
544
+ <li><strong>(v.2)</strong> CHANGED: <a href="https://shsec.io/7b">HTTP Security Headers</a> module is enabled by default on new installs.</li>
545
+ <li><strong>(v.1)</strong> FIXED: Nasty bug that caused an infinite loop bug in some configurations.</li>
546
+ <li><strong>(v.0)</strong> ADDED: Per-site plugin statistics gathering - summary display on admin dashboard.</li>
547
+ <li><strong>(v.0)</strong> ADDED: HTML class to the &quot;I&#39;m a human&quot; checkbox field.</li>
548
+ <li><strong>(v.0)</strong> ADDED: Ability to change minimum user role for login notification emails with use of <code>add_filter()</code>. See FAQs.</li>
549
+ <li><strong>(v.0)</strong> REMOVED: Option &#39;Prevent Remote Login&#39; causes more trouble with than it&#39;s worth with too many hosting configurations.</li>
550
+ <li><strong>(v.0)</strong> CHANGED: For websites that don&#39;t run WP Crons correctly, added code for automatic database cleaning.</li>
551
+ <li><strong>(v.0)</strong> CLEANED: Removed Twig render code as it was never being used.</li>
552
  </ul>
553
  <p>= 5.3 Series =</p>
554
  <ul>
555
+ <li><strong>(v.2)</strong> IMPROVED: <a href="https://shsec.io/7b">HTTP Security Headers</a> Content Security Policy now supports specifying HTTPS for domains/hosts.</li>
556
+ <li><strong>(v.2)</strong> FIXED: Human Comment SPAM Feature didn&#39;t fire under certain circumstances.</li>
557
+ <li><strong>(v.2)</strong> FIXED: Fixed parsing of Human Comment SPAM dictionary words.</li>
558
+ <li><strong>(v.1)</strong> TRANSLATIONS: Dutch (32%)</li>
559
+ <li><strong>(v.0)</strong> ADDED: New Feature - <a href="https://shsec.io/7b">HTTP Security Headers</a>.</li>
560
+ <li><strong>(v.0)</strong> FIXED: Prevent renaming WP Login to &quot;/login&quot;</li>
561
  </ul>
562
  <p>= 5.2 Series =</p>
563
  <ul>
564
+ <li><strong>(v.0)</strong> ADDED: Guard against core file scanner and automatic WordPress updates clashing.</li>
565
+ <li><strong>(v.0)</strong> CHANGED: Logic for brute force login checking is improved - they all run before username/password checking</li>
566
+ <li><strong>(v.0)</strong> FIXED: Certain older versions of PHP don&#39;t like combined IPv4 and IPv6 filter flags</li>
567
+ <li><strong>(v.0)</strong> FIXED: Google reCAPTCHA for WordPress sites that have restrictive settings for sockets etc.</li>
568
+ <li><strong>(v.0)</strong> REMOVED: <a href="https://shsec.io/75">Plugin vulnerabilities scanner</a>. It&#39;s out-of-date and unsuitable.</li>
569
  </ul>
570
  <p>= 5.1 Series =</p>
571
  <ul>
572
+ <li><strong>(v.0)</strong> FIXED: Improved compatibility with bbPress.</li>
573
+ <li><strong>(v.0)</strong> CHANGED: Optimizations around options and definitions (storing fewer options data)</li>
574
+ <li><strong>(v.0)</strong> CHANGED: Improved styling and responsiveness of plugin badge.</li>
575
+ <li><strong>(v.0)</strong> ADDED: Ability to programmatically export/import options - further preparation for iControlWP+Shield integration.</li>
576
+ <li><strong>(v.0)</strong> FIXED: Issue where Core automatic updates would fail, but notification email was sent anyway</li>
577
  </ul>
578
  <p>= 5.0 Series =</p>
579
  <ul>
580
+ <li><strong>(v.3)</strong> FIXED: Issue with setting session cookies with PHP 7</li>
581
+ <li><strong>(v.2)</strong> FIXED: <a href="https://shsec.io/5s">Rename WordPress Login URL</a> bug</li>
582
+ <li><strong>(v.2)</strong> CHANGED: reCAPTCHA text usage corrected throughout plugin.</li>
583
+ <li><strong>(v.1)</strong> CHANGED: Removed the whole &#39;wp-content&#39; directory from the <a href="https://shsec.io/wpsf40">Core File Scanner</a> feature.</li>
584
+ <li><strong>(v.1)</strong> CHANGED: A WordPress filter to change the plugin badge text content (see FAQ)</li>
585
+ <li><strong>(v.1)</strong> CHANGED: Tweaked the plugin badge styling.</li>
586
+ <li><strong>(v.1)</strong> CHANGED: All emails sent by the plugin contain the name of the site and the current plugin version in the email footer.</li>
587
+ <li><strong>(v.1)</strong> ADDED: In-plugin links to blogs and info articles for Google ReCaptcha and <a href="https://shsec.io/wpsf43">Google Authenticator</a></li>
588
+ <li><strong>(v.0)</strong> NEW: WordPress Simple Firewall plugin has been re-branded and is called <strong>Shield</strong></li>
589
+ <li><strong>(v.0)</strong> ADDED: NEW feature - <a href="https://shsec.io/shld2">Google ReCaptcha</a> for Comment SPAM and Login protection.</li>
590
+ <li><strong>(v.0)</strong> ADDED: Support for this plugin is now Premium. Added Premium Support page that links to Helpdesk.</li>
591
+ <li><strong>(v.0)</strong> CHANGED: Refactor of comment spam code.</li>
592
+ <li><strong>(v.0)</strong> CHANGED: Core File Scanner now handles the odd Hungarian distribution.</li>
593
+ </ul>
594
+ <p>= 4.17 Series =
595
  <em>Released: 17th February, 2016</em></p>
596
  <ul>
597
+ <li><strong>(v.0)</strong> ADDED: NEW feature - <a href="https://shsec.io/wpsf43">Google Authenticator Login option</a>.</li>
598
+ <li><strong>(v.0)</strong> ADDED: <a href="https://shsec.io/wpsf40">Core File Scanner</a> now includes an automatic link to repair files (you must be logged in as admin for this link to work!).</li>
599
+ <li><strong>(v.0)</strong> ADDED: NEW - if you already have a logged-in session and you open the login screen, you&#39;ll be provided with a link to go straight to the admin area.</li>
600
+ <li><strong>(v.0)</strong> CHANGED: Email-based Two-Factor Authentication is now stateless/session-less - it will not check validity per-page load.</li>
601
+ <li><strong>(v.0)</strong> CHANGED: Changes to the email-based authentication system - now only 1 option and it no longer locks to IP or browser.</li>
602
+ <li><strong>(v.0)</strong> CHANGED: Various efficiency improvements including reduced SQL updates.</li>
603
+ <li><strong>(v.0)</strong> CHANGED: Email system is improved and now send emails from the default WordPress sender. This may be <a href="https://icontrolwp.freshdesk.com/support/solutions/articles/3000048723">changed with filter</a>.</li>
604
  </ul>
605
+ <p>= 4.16 Series =
606
  <em>Released: 20th January, 2016</em></p>
607
  <ul>
608
+ <li><strong>(v.2)</strong> CHANGED: Further changes and improvements to the <a href="https://shsec.io/wpsf40">Core File Scanner</a>.</li>
609
+ <li><strong>(v.2)</strong> CHANGED: Improvements to the <a href="https://shsec.io/wpsf27">automatic black list system</a> for failed login attempts.</li>
610
+ <li><strong>(v.2)</strong> TRANSLATIONS: Turkish (100%)</li>
611
+ <li><strong>(v.1)</strong> CHANGED: Improved the contents of the <a href="https://shsec.io/wpsf40">Core File Scanner</a> notification email with links to original source files.</li>
612
+ <li><strong>(v.1)</strong> CHANGED: Now also excluding the /wp-content/languages/ directory since translations may update independently.</li>
613
+ <li><strong>(v.1)</strong> CHANGED: Handles the special case of <a href="https://wordpress.org/support/topic/problem-with-checksum-hashes">old index.php files</a></li>
614
+ <li><strong>(v.0)</strong> ADDED: Feature: <a href="https://shsec.io/wpsf40">Automatically scans WordPress Core files</a> and detects alterations from the default WordPress Core File data</li>
615
+ <li><strong>(v.0)</strong> ADDED: Feature: to automatically attempt to repair/replace WordPress Core files that are discovered which have been altered.</li>
616
+ <li><strong>(v.0)</strong> ADDED: Option to toggle the <a href="https://shsec.io/wpsf41">Plugin Vulnerabilities cron</a>.</li>
617
+ <li><strong>(v.0)</strong> ADDED: Two-Factor Authentication links now honour the WordPress &#39;redirect_to&#39; parameter.</li>
618
+ </ul>
619
+ <p>= 4.15 Series =
620
  <em>Released: 6th January, 2016</em></p>
621
  <ul>
622
+ <li><strong>(v.0)</strong> ADDED: New and updated Firewall rules as well as a new &#39;Aggressive&#39; option that looks for additional request data. Disabled by default, but may cause an increase in false positives.</li>
623
+ <li><strong>(v.0)</strong> CHANGED: Improved and optimized Firewall processing.</li>
624
+ <li><strong>(v.0)</strong> FIXED: <a href="https://github.com/FernleafSystems/wp-simple-firewall/issues/3">Issue</a> where automatic update notification emails are sent out without any update notices (probably due to failed updates).</li>
625
+ <li><strong>(v.0)</strong> FIXED: Small conflict with WP Login Rename and other security plugins.</li>
626
+ <li><strong>(v.0)</strong> TRANSLATIONS: Czech (91%), Finnish (98%), Turkish (98%).</li>
627
  </ul>
628
+ <p>= 4.14 Series =
629
  <em>Released: 20th November, 2015</em></p>
630
  <ul>
631
+ <li><strong>(v.2)</strong> ADDED: User notice message displayed when the &#39;Theme My Login&#39; plugin is active and you try to rename your login URL - It is not compatible.</li>
632
+ <li><strong>(v.1)</strong> ADDED: Added WordPress filter option to specify URL instead of present a 404 when Rename WP Login is active. <a href="https://icontrolwp.freshdesk.com/solution/articles/3000044812">more info</a></li>
633
+ <li><strong>(v.1)</strong> ADDED: Added &#39;Unique Plugin Installation ID&#39; to be utilized in the future.</li>
634
+ <li><strong>(v.1)</strong> FIXED: WordPress Comments bug where some comments didn&#39;t pass through the SPAM filters in a certain scenario.</li>
635
+ <li><strong>(v.0)</strong> ADDED: <a href="https://shsec.io/wpsf33">Custom Automatic Update Notifications Email</a> that runs separately to the in-built WordPress core notification email.</li>
636
+ <li><strong>(v.0)</strong> ADDED: Filter to remove the admin area IP address footer text</li>
637
+ <li><strong>(v.0)</strong> CHANGED: Added native support for PayPal return links - whitelisting &quot;verify_sign&quot; parameter.</li>
638
+ <li><strong>(v.0)</strong> CHANGED: Tweak patterns for matching on &#39;WordPress terms&#39;.</li>
639
+ <li><strong>(v.0)</strong> TRANSLATIONS: Danish (100%), Czech (92%), Turkish (92%), Finnish (88%),</li>
640
+ <li><strong>(v.0)</strong> FIXED: Small bugs and readying for WordPress 4.4</li>
641
+ </ul>
642
+ <p>= 4.13 Series =
643
  <em>Released: 22nd October, 2015</em></p>
644
  <ul>
645
+ <li><strong>(v.0)</strong> NEW: Added option to block the modification, addition/promotion and deletion of WordPress administrators users within the &#39;Security Admin&#39; module.</li>
646
+ <li><strong>(v.0)</strong> NEW: Renamed &#39;Admin Access&#39; module to &#39;Security Admin&#39;.</li>
647
+ <li><strong>(v.0)</strong> CHANGED: Simplified and consolidated the use of cookies for User Session - sets and removes cookies better to reduce their usage.</li>
648
+ <li><strong>(v.0)</strong> CHANGED: Simplified and consolidated the use of cookies for Two Factor Login Authentication.</li>
649
+ <li><strong>(v.0)</strong> CHANGED: Cleaned up some Comment SPAM filtering code.</li>
650
+ <li><strong>(v.0)</strong> CHANGED: Comments Filter doesn&#39;t use cookies unless a session cookie for the visitor already exists.</li>
651
+ <li><strong>(v.0)</strong> CHANGED: IP Manager Automatic Black List - default black list duration is now 1 minute &amp; default transgressions limit is 10</li>
652
+ <li><strong>(v.0)</strong> CHANGED: Improvements to the database create queries: use MySQL Engine defaults (instead of MyISAM); use WordPress dbDelta() for updates.</li>
653
+ <li><strong>(v.0)</strong> CHANGED: Various code optimizations and cleaning.</li>
654
+ </ul>
655
+ <p>= 4.12 Series =
656
  <em>Released: 10th October, 2015</em></p>
657
  <ul>
658
+ <li><strong>(v.0)</strong> NEW: Option to completely disable the XML-RPC system. <a href="https://shsec.io/wpsf31">more info</a></li>
659
+ <li><strong>(v.0)</strong> CHANGED: Logged-in users are automatically forwarded to the WordPress admin only if they are Administrators.</li>
660
  </ul>
661
+ <p>= 4.11 Series =
662
  <em>Released: 5th October, 2015</em></p>
663
  <ul>
664
+ <li><strong>(v.0)</strong> NEW: Ability to now completely block the update/changing of certain WordPress site options. <a href="https://shsec.io/wpsf30">more info</a></li>
665
+ <li><strong>(v.0)</strong> FIXED: Various small bugs with the IP Manager UI ajax.</li>
666
+ <li><strong>(v.0)</strong> FIXED: Uncaught PHP Exception when a site&#39;s hosting isn&#39;t properly configured to handle IPv6 addresses.</li>
667
+ <li><strong>(v.0)</strong> TRANSLATIONS: Danish - 57%, Czech - 100%, Finnish - 94%</li>
668
  </ul>
669
+ <p>= 4.10 Series =
670
  <em>Released: 23rd August, 2015</em></p>
671
  <ul>
672
+ <li><strong>(v.4)</strong> REFACTOR: Notifications system is more reliable and most notices can be hidden/closed (at least for the current page load as some notices are persistent).</li>
673
+ <li><strong>(v.4)</strong> REMOVED: The old manual black list option has been completely removed - in favour of the automatic black list system.</li>
674
+ <li><strong>(v.4)</strong> CHANGED: Revised the order of certain hooks being created to avoid the possibility of pluggable.php not being loaded for PHP Shutdown.</li>
675
+ <li><strong>(v.4)</strong> CHANGED: The presence of IP addresses in the IP Whitelist will force the IP Manager feature to be enabled.</li>
676
+ <li><strong>(v.4)</strong> CHANGED: We now make an attempt to prevent the caching of WordPress wp_die() pages that we generate. (compatible with at least W3TC, Super Cache)</li>
677
+ <li><p><strong>(v.4)</strong> TRANSLATIONS: Turkish - 100%, Danish - 3%</p>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
678
  </li>
679
+ <li><p><strong>(v.3)</strong> FIXED: Another PHP 5.2 incompatibility.</p>
 
680
  </li>
681
+ <li><strong>(v.2)</strong> ADDED: White Listing UI to the IP Manager - CIDR ranges are supported (also automatically migrates IPs, except ranges, from legacy to new)</li>
682
+ <li><strong>(v.2)</strong> ADDED: Returned the black marking of failed WP login attempts to the automatic black list system</li>
683
+ <li><strong>(v.2)</strong> ADDED: Using a 3rd party API service: <a href="https://www.ipify.org/">ipify.org</a> - to find the server&#39;s own IP address so we can ensure it&#39;s not used in the black lists</li>
684
+ <li><strong>(v.2)</strong> CHANGED: AJAX calls are handled more robustly with actual error messages where possible.</li>
685
+ <li><p><strong>(v.2)</strong> FIXED: A few black list processing bugs.</p>
686
  </li>
687
+ <li><p><strong>(v.1)</strong> ADDED: UI to view and remove IP address from Automatic Black List Engine.</p>
 
688
  </li>
689
+ <li><strong>(v.1)</strong> FIX: Removed transgression counting on failed logins - WP data is inconsistent.</li>
690
+ <li><strong>(v.1)</strong> CHANGED: Original legacy white list now takes priority over new auto black list</li>
691
+ <li><strong>(v.1)</strong> CHANGED: Default transgressions limit is now 7</li>
692
+ <li><strong>(v.1)</strong> ADDED: Ability to reset plugin options to default using &#39;reset&#39; flag file. <a href="https://shsec.io/wpsf28">more info</a></li>
693
+ <li><strong>(v.0)</strong> NEW FEATURE: &#39;FABLE&#39; - <a href="https://shsec.io/wpsf27">Fully Automatic Black Listing Engine</a>.</li>
694
+ </ul>
695
+ <p>Simply put, FABLE will automatically block all malicious traffic by IP, based on their activity. This Security Plugin will track malicious behaviour
696
+ and count all transgressions that visitors make against the site. Once a particular visitor exceeds the specified number transgressions, FABLE
 
 
 
 
 
 
 
 
 
 
 
 
 
697
  will outright block any access they have to your WordPress site.</p>
698
  <p>What makes the FABLE system better?</p>
699
  <ul>
709
  <li>Attempt to login with an invalid username/password combination</li>
710
  <li>Any attempt to login while the login cooldown system is in-effect</li>
711
  <li>Any login attempt that trips the GASP Login protection system</li>
712
+ <li>Any login attempt with a username that doesn&#39;t exist</li>
713
  <li>Any attempt to access /wp-admin/, /login/, or wp-login.php while the Rename WP Login setting is active</li>
714
  <li>Any comment that gets labelled as SPAM by the plugin</li>
715
+ <li>Failed attempt to authenticate with the plugin&#39;s Admin Access Protection module</li>
716
  <li>Any trigger of a Firewall block rule</li>
717
  </ul>
718
+ <p>= 4.9 Series =
719
  <em>Released: 7th July, 2015</em></p>
720
  <ul>
721
  <li><strong>(v.8)</strong> CHANGED: Firewall, User Sessions and Lockdown Feature Modules are now enabled by default for new installations.</li>
722
+ <li><strong>(v.8)</strong> FIX: Some server email programs can&#39;t handle colons (:) in the email subject (because supporting all characters would be waaay too radical man).</li>
723
+ <li><strong>(v.8)</strong> ADDED: Function to better get the WordPress home URL to prevent interference from other plugins.</li>
724
+ <li><strong>(v.8)</strong> CHANGED: Updated Text For <a href="https://shsec.io/6e">Author Scan Block</a> feature.</li>
725
  <li><strong>(v.7)</strong> CHANGED: How author query blocking works to be more reliable and stricter - only runs when users are not logged in, and it will DIE instead of redirect.</li>
726
  <li><strong>(v.6)</strong> ADDED: New Option: prevent detection of usernames using the ?author=N query. (location under section: Lockdown -&gt; Obscurity)</li>
727
+ <li><strong>(v.6)</strong> FIXED: Infinite redirect loop logic prevents redirect for rejected comment SPAM that&#39;s posted in bulk. This results in email notifications for spam comments.</li>
728
  <li><strong>(v.5)</strong> ADDED: The plugin will load itself first before all other plugins</li>
729
+ <li><strong>(v.5)</strong> FIXED: No longer using parse_url() to determine the request URL as it&#39;s too inconsistent and unreliable.</li>
730
  <li><strong>(v.4)</strong> FIX: Audit Trail Viewer display issue with non-escaped HTML (Thanks Chris!)</li>
731
  <li><strong>(v.4)</strong> ADDED: An admin warning for sites with PHP version less than 5.3.2 (future versions will require this as a minimum)</li>
732
  <li><strong>(v.4)</strong> TRANSLATIONS: Danish - 6%, Spanish - 76%</li>
736
  <li><strong>(v.2)</strong> ADDED: Email notifications sent out to report email address on a daily cron. <a href="https://www.icontrolwp.com/2015/07/plugin-vulnerability-email-notifications/">more info</a></li>
737
  <li><strong>(v.2)</strong> FIX: Work around a WordPress inline plugin update Javascript bug.</li>
738
  <li><strong>(v.1)</strong> FIX: Fix syntax support for earlier versions of PHP.</li>
739
+ <li><strong>(v.0)</strong> FEATURE: Plugin Vulnerabilities Detection: If you&#39;re running plugins with known vulnerabilities you will be warned - <a href="https://shsec.io/wpsf22">more info</a></li>
740
  </ul>
741
+ <p>= 4.8 Series =
742
  <em>Released: 21st June, 2015</em></p>
743
  <ul>
744
  <li><strong>(v.0)</strong> FEATURE: Admin Access Restriction Areas - Restrict access to certain WordPress areas and functionality to <strong>Administrators</strong> with the Admin Access key.</li>
746
  <li><strong>(v.0)</strong> ADDED: Admin Access Restriction Area - Themes. You can now restrict access to certain Theme actions - activate, install, update, delete.</li>
747
  <li><strong>(v.0)</strong> ADDED: Admin Access Restriction Area - Pages/Post. You can now restrict access to certain Page/Post actions - Create/Edit, Publish, Delete.</li>
748
  </ul>
749
+ <p>= 4.7 Series =
750
  <em>Released: 29th April, 2015</em></p>
751
  <ul>
752
  <li><strong>(v.7)</strong> FIXED: The text used to explain why some comments were marked as spam was broken.</li>
753
  <li><strong>(v.7)</strong> FIXED: Group sign-up form now honours your SSL setting.</li>
754
  <li><strong>(v.7)</strong> TRANSLATIONS: Spanish - 74%, Russian - 91%, Turkish - 94%, Polish- 95%, Finnish - 100%</li>
755
+ <li><strong>(v.6)</strong> FIXED: Verifying ability to send/receive email doesn&#39;t complete if Admin Access Protection is turned on.</li>
756
+ <li><strong>(v.6)</strong> FIXED: GASP Login Protection feature breaks because certain key options aren&#39;t initialized when the feature is enabled.</li>
757
+ <li><strong>(v.6)</strong> FIXED: Some &quot;more info&quot; links were empty.</li>
758
  <li><strong>(v.4)</strong> ADDED: Email Sending Verification when enabling two-factor authentication - this ensures your site can send (and you can receive) emails.</li>
759
  <li><strong>(v.4)</strong> ADDED: Section Summaries - each option tab contains a small text summary outlining the purpose and recommendation for each.</li>
760
  <li><strong>(v.4)</strong> CHANGED: The Admin Access Key input is now a password field.</li>
769
  <li><strong>(v.1)</strong> FIX: Silence warnings from filesystem touch() command.</li>
770
  <li><strong>(v.1)</strong> TRANSLATIONS: Polish (100%), Finnish (100%), Czech (73%), Arabic (34%)</li>
771
  <li><strong>(v.0)</strong> UPDATED: Options page user interface re-design.</li>
772
+ <li><strong>(v.0)</strong> FIX: Audit trail time now reflects the user&#39;s timezone correctly.</li>
773
  <li><strong>(v.0)</strong> FIX: Better compatibility with BBPress.</li>
774
  <li><strong>(v.0)</strong> UPDATED: Underlying plugin code improvements.</li>
775
  <li><strong>(v.0)</strong> TRANSLATIONS: Russian (100%), Czech (70%), Polish (97%)</li>
776
  </ul>
777
+ <p>= 4.6 Series =
778
  <em>Released: 10th April, 2015</em></p>
779
  <ul>
780
+ <li><strong>(v.3)</strong> SECURITY: Added protection against XSS vulnerability in WordPress comments. <a href="https://shsec.io/63">Learn More</a> - Note: This is not a vulnerability with the Firewall plugin.</li>
781
+ <li><strong>(v.3)</strong> SECURITY: Added extra precautions to WordPress URL redirects. <a href="https://shsec.io/64">Learn More</a>.</li>
782
  <li><strong>(v.3)</strong> TRANSLATIONS: Russian (70%), Czech (67%)</li>
783
  <li><strong>(v.2)</strong> FIX: Bug with the database table verification logic.</li>
784
  <li><strong>(v.2)</strong> TRANSLATIONS: Russian (New- 54%), Romanian (100%), Turkish (89%), Czech (53%)</li>
786
  <li><strong>(v.1)</strong> UPDATED: Plugin Badge styling</li>
787
  <li><strong>(v.1)</strong> UPDATED: Updated Czech(41%) and Spanish (60%) translations</li>
788
  <li><strong>(v.0)</strong> ADDED: New feature that displays the last login time for all users on the users listing page (User Management feature must be enabled).</li>
789
+ <li><strong>(v.0)</strong> ADDED: <strong>Completely optional</strong> promotional Plugin Badge option - help us promote the plugin and reassure your site visitors at the same time. <a href="https://shsec.io/5x">Learn More</a></li>
790
  <li><strong>(v.0)</strong> UPDATED: Updated Czech(38%) translations</li>
791
  </ul>
792
+ <p>= 4.5 Series =
793
  <em>Released: 6th March, 2015</em></p>
794
  <ul>
795
  <li><strong>(v.5)</strong> CHANGED: Updated Finnish (100%), Czech (16%) translations</li>
803
  <li><strong>(v.1)</strong> ADDED: New feature- GASP Login Protection can now be applied to lost password form - enabled by default</li>
804
  <li><strong>(v.0)</strong> ADDED: New feature- GASP Login Protection can now be applied to user registrations - enabled by default</li>
805
  </ul>
806
+ <p>= 4.4 Series =
807
  <em>Released: 21st February, 2015</em></p>
808
  <ul>
809
  <li><strong>(v.2)</strong> ADDED: Romanian Translation.</li>
810
  <li><strong>(v.2)</strong> ADDED: A plugin minimum-requirements processing system.</li>
811
  <li><strong>(v.2)</strong> IMPROVED: The WordPress admin-UI code is simpler and cleaner.</li>
812
  <li><strong>(v.1)</strong> ADDED: <strong>Significant</strong> performance enhancement in plugin loading times (up to 50% reduction).</li>
813
+ <li><strong>(v.0)</strong> CHANGED: The &#39;Prevent Remote Login&#39; option now tries to detect web hosting server compatibility before allowing it to be enabled.</li>
814
+ <li><strong>(v.0)</strong> CHANGED: More lax in finding the &#39;forceOff&#39; file when users are trying to turn off the firewall.</li>
815
  <li><strong>(v.0)</strong> CHANGED: Parsing the URL no longer outputs warnings that might interfere with response headers.</li>
816
  </ul>
817
+ <p>= 4.3 Series =
818
  <em>Released: 15th January, 2015</em></p>
819
  <ul>
820
  <li><strong>(v.6)</strong> FIXES: More thorough validation of whitelisted IP addresses</li>
821
  <li><strong>(v.5)</strong> FIXES: Some hosting environments need absolute file paths for PHP include()/require()</li>
822
  <li><strong>(v.5)</strong> CHANGED: Streamlined the detection of whitelisting and added in-plugin notification if <strong>you</strong> are whitelisted</li>
823
+ <li><strong>(v.4)</strong> FIXES: Work around for cases where PHP can&#39;t successfully run parse_url()</li>
824
  <li><strong>(v.2)</strong> IMPROVED: Refactoring for better code organisation</li>
825
+ <li>ADDED: New Feature - <a href="https://shsec.io/5s">Rename WP Login Page</a>.</li>
826
  <li>ADDED: UI indicators on whether plugins will be automatically updated in the plugins listing.</li>
827
+ <li>CHANGED: IP Address WhiteList is now global for the whole plugin, and can be accessed under the &quot;Dashboard&quot; area</li>
828
  <li>IMPROVED: Firewall processing code is simplified and more efficient.</li>
829
  </ul>
830
+ <p>= 4.2.1 =
831
  <em>Released: 22th December, 2014</em></p>
832
  <ul>
833
  <li>FIXED: Changes to how feature specifications are read from disk to prevent .tmp file build up.</li>
834
  </ul>
835
+ <p>= 4.2.0 =
836
  <em>Released: 12th December, 2014</em></p>
837
  <ul>
838
  <li>ADDED: Audit Trail Auto Cleaning - default cleans out entries older than 30 days.</li>
839
  <li>FIXED: Various small bug fixes and code cleaning.</li>
840
  </ul>
841
+ <p>= 4.1.4 =
842
  <em>Released: 24th November, 2014</em></p>
843
  <ul>
844
  <li>FIXED: Fixed small logic bug which prevented deactivation of the plugin on the UI.</li>
845
  </ul>
846
+ <p>= 4.1.3 =
847
  <em>Released: 19th November, 2014</em></p>
848
  <ul>
849
  <li>IMPROVED: User Sessions are simplified.</li>
851
  </ul>
852
  <p>= 4.1.2 =</p>
853
  <ul>
854
+ <li>ADDED: Self-correcting database table validation - if the structure of a database table isn&#39;t what is expected, it&#39;ll be re-created.</li>
855
  </ul>
856
  <p>= 4.1.1 =</p>
857
  <ul>
858
  <li>WARNING: Due to new IPv6 support, all databases tables will be rebuilt - all active user sessions will be destroyed.</li>
859
+ <li>ADDED: Preliminary support for IPv6 addresses throughout. We don&#39;t support whitelist ranges but IPv6 addresses are handled much more reliably in general.</li>
860
+ <li>ADDED: New audit trail concept added called &quot;immutable&quot; that represents entries that will never be deleted - such entries would usually involve actions taken on the audit trail itself.</li>
861
  <li>FIXED: Support for audit trail events with longer names.</li>
862
  <li>IMPROVED: Comments Filtering - It now honours the WordPress settings for previously approved comment authors and never filters such comments.</li>
863
  <li>REMOVED: Option to enable GASP Comments Filtering for logged-in users has been completely removed - this reduces plugin options complexity. All logged-in users by-pass <strong>all</strong> comments filtering.</li>
864
  <li>FIXED: Prevention against plugin redirect loops under certain conditions.</li>
865
+ <li>FIXED: IP whitelisting wasn&#39;t working under certain cases.</li>
866
  </ul>
867
  <p>= 4.0.0 =</p>
868
  <ul>
869
  <li>ADDED: New Feature - Audit Trail</li>
870
  <li>ADDED: Audit Trail options include: Plugins, Themes, Email, WordPress Core, Posts/Pages, Shield plugin</li>
871
  <li>FIXED: Full and proper cleanup of plugin options, crons, and databases upon deactivation.</li>
872
+ <li>REMOVED: Firewall Log. This is no longer an option and is instead integrated into the &quot;Shield&quot; Audit Trail.</li>
873
  </ul>
874
  <p>= 3.5.5 =</p>
875
  <ul>
879
  </ul>
880
  <p>= 3.5.3 =</p>
881
  <ul>
882
+ <li>ADDED: A warning message on the WordPress admin if the &quot;forceOff&quot; override is active.</li>
883
+ <li>CHANGED: The &#39;forceOff&#39; system is now temporary - i.e. it doesn&#39;t save the configuration, and so once this file is removed, the plugin returns to the settings specified.</li>
884
+ <li>CHANGED: The &#39;forceOn&#39; option is now removed.</li>
885
+ <li>FIXED: Problems with certain hosting environments reading in files with the &quot;.yaml&quot; extension - <a href="https://wordpress.org/support/topic/yaml-breaks-plugin">support ref</a></li>
886
+ <li>FIXED: Small issue where when the file system paths change, some variables don&#39;t update properly.</li>
887
  </ul>
888
  <p>= 3.5.0 =</p>
889
  <ul>
890
  <li>CHANGED: Plugin features are now configured <a href="https://github.com/mustangostang/spyc/">using YAML</a> - no more in-PHP configuration.</li>
891
  <li>REMOVED: A few options from User Sessions Management as they were unnecessary.</li>
892
  <li>CHANGED: Database storing tables now have consistent naming.</li>
893
+ <li>FIXED: Issue with User Sessions Management where &#39;0&#39; was specified for session length, resulting in lock out.</li>
894
  <li>FIXED: Firewall log gathering.</li>
895
  <li>FIXED: Various PHP warning notices.</li>
896
  </ul>
910
  <p>= 3.2.0 =</p>
911
  <ul>
912
  <li>ADDED: Options to allow by-pass XML-RPC so as to be compatible with WordPress iPhone/Android apps.</li>
913
+ <li>UPDATED: Login screen message when you&#39;re forced logged-out due to 2-factor auth failure on IP or cookie.</li>
914
  <li>CHANGED: Tweaked method for setting admin access protection on/off</li>
915
  <li>CHANGED: comment filtering code refactoring.</li>
916
+ <li>FIXED: Options that were &quot;multiple selects&quot; weren&#39;t saving correctly</li>
917
  </ul>
918
  <p>= 3.1.5 =</p>
919
  <ul>
938
  <p>= 3.1.0 =</p>
939
  <ul>
940
  <li>ADDED: option to check the logged-in user session only on WordPress admin pages (now the default setting)</li>
941
+ <li>ADDED: option to auto-forward to the WordPress dashboard when you go to wp-login.php and you&#39;re already logged in.</li>
942
  <li>ADDED: message to login screen when no user session is found</li>
943
  <li>CHANGED: does not verify session when performing AJAX request. (need to build appropriate AJAX response)</li>
944
  <li>FIX: for wp_login action not passing second argument</li>
973
  <p>= 2.6.4 =</p>
974
  <ul>
975
  <li>ENHANCED: Dashboard now shows a more visual summary of settings and removes duplicate options settings with links to sections.</li>
976
+ <li>ENHANCED: WordPress Lock Down options now also set the corresponding WordPress defines if they&#39;re not already.</li>
977
  </ul>
978
  <p>= 2.6.3 =</p>
979
  <ul>
980
  <li>ADDED: More in-line plugin links to help/blog resources</li>
981
+ <li><p>ENHANCED: <a href="https://shsec.io/5b">Admin Access Protection</a> is further enhanced in 3 ways:</p>
982
+ </li>
983
+ <li><p>More robust cookie values using MD5s</p>
984
+ </li>
985
  <li>Blocks plugin options updating right at the point of WordPress options update so nothing can rewrite the actual plugin options.</li>
986
  <li>Locks the current Admin Access session to your IP address - effectively only 1 Shield admin allowed at a time.</li>
987
+ </ul>
988
  <p>= 2.6.2 =</p>
989
  <ul>
990
+ <li>ENHANCED: Added option to completely reject a SPAM comment and redirect to the home page (so it doesn&#39;t fill up your database with rubbish)</li>
991
  <li>ADDED: Plugin now has an internal stats counter for spam and other significant plugin events.</li>
992
  </ul>
993
  <p>= 2.6.1 =</p>
994
  <ul>
995
  <li>ADDED: Plugin now installs with default SPAM blacklist.</li>
996
+ <li>ADDED: Now automatically checks and updates the SPAM blacklist when it&#39;s older than 48hrs.</li>
997
  <li>ENHANCED: Comment messages indicate where the SPAM content was found when marking human-based spam messages.</li>
998
  </ul>
999
  <p>= 2.6.0 =</p>
1000
  <p><strong>Major Features Release: Please review SPAM comments filtering options to determine where SPAM goes</strong></p>
1001
  <ul>
1002
+ <li>FEATURE: Added Human SPAM comments filtering - replacement for Akismet that doesn&#39;t use or send any data to 3rd party services. Uses <a href="https://github.com/splorp/wordpress-comment-blacklist">Blacklist provided and maintained by Grant Hutchinson</a></li>
1003
  <li>ENHANCED: Two-Factor Login now automatically logs in the user to the admin area without them having to re-login again.</li>
1004
  <li>ENHANCED: Added ability to terminate all currently (two-factor) verified logins.</li>
1005
  <li>ENHANCED: Spam filter/scanning adds an explanation to the SPAM content to show why a message was filtered.</li>
1012
  </ul>
1013
  <p>= 2.5.8 =</p>
1014
  <ul>
1015
+ <li>FEATURE: Added &#39;PHP Code&#39; Firewall checking option.</li>
1016
  </ul>
1017
  <p>= 2.5.7 =</p>
1018
  <ul>
1024
  </ul>
1025
  <p>= 2.5.5 =</p>
1026
  <ul>
1027
+ <li>FEATURE: Added &#39;Lockdown&#39; feature to force login to WordPress over SSL.</li>
1028
+ <li>FEATURE: Added &#39;Lockdown&#39; feature to force WordPress Admin dashboard to be delivered over SSL.</li>
1029
+ <li>FIX: Admin restricted access feature wasn&#39;t disabled with the &quot;forceOff&quot; option.</li>
1030
  </ul>
1031
  <p>= 2.5.4 =</p>
1032
  <ul>
1047
  </ul>
1048
  <p>= 2.5.0 =</p>
1049
  <ul>
1050
+ <li>FEATURE: Two-Factor Authenticated Login using <a href="https://shsec.io/4i">Yubikey</a> One Time Passwords (OTP).</li>
1051
  </ul>
1052
  <p>= 2.4.3 =</p>
1053
  <ul>
1056
  </ul>
1057
  <p>= 2.4.2 =</p>
1058
  <ul>
1059
+ <li>ADDED: Contextual help links for many options. More to come...</li>
1060
  <li>ADDED: More Portuguese (Brazil) translations (~80%)</li>
1061
  </ul>
1062
  <p>= 2.4.1 =</p>
1064
  <li>ADDED: More strings to the translation set for better multilingual support</li>
1065
  <li>ADDED: Portuguese (Brazil) translations (~40%)</li>
1066
  <li>UPDATED: Hebrew Translations</li>
1067
+ <li>FIXED: Automatic cleaning of database logs wasn&#39;t actually working as expected. Should now be fixed.</li>
1068
  </ul>
1069
  <p>= 2.4.0 =</p>
1070
  <ul>
1079
  <ul>
1080
  <li>ADDED: Hebrew Translations. Thanks <a href="http://atar4u.com">Ahrale</a>!</li>
1081
  <li>ADDED: Automatic trimming of the Firewall access log to 7 days - it just grows too large otherwise.</li>
1082
+ <li>FIX: The previously added automatic clean up of old comments and login protect database entries was wiping out the valid login protect<pre><code> entries <span class="hljs-keyword">and</span> was forcing users <span class="hljs-keyword">to</span> re-login <span class="hljs-keyword">every</span> <span class="hljs-number">24</span>hrs.
1083
+ </code></pre></li>
1084
  <li>FIX: Some small bugs, errors, and PHPDoc Comments.</li>
1085
  </ul>
1086
  <p>= 2.3.2 =</p>
1087
  <ul>
1088
+ <li>ADDED: Automatic cleaning of GASP Comments Filter and Login Protection database entries (older than 24hrs) using WordPress Cron (everyday @ 6am)</li>
1089
  <li>CHANGED: Huge code refactoring to allow for more easily use with other WordPress plugins.</li>
1090
  </ul>
1091
  <p>= 2.2.5 =</p>
1092
  <ul>
1093
+ <li>ADDED: Email sending options for automatic update notifications - options to change the notification email address, or turn it off completely.</li>
1094
  </ul>
1095
  <p>= 2.2.4 =</p>
1096
  <ul>
1097
  <li>FIX: Small bug fix.</li>
1098
+ <li>CHANGED: When running a force automatic updates process, tries to remove influence from other plugins and uses only this plugin&#39;s automatic updates settings.</li>
1099
  <li>CHANGED: A bit of automatic updates code refactoring.</li>
1100
  </ul>
1101
  <p>= 2.2.2 =</p>
1105
  </ul>
1106
  <p>= 2.2.1 =</p>
1107
  <ul>
1108
+ <li>ADDED: Verified compatibility with WordPress 3.8</li>
1109
  </ul>
1110
  <p>= 2.2.0 =</p>
1111
  <ul>
1115
  </ul>
1116
  <p>= 2.1.5 =</p>
1117
  <ul>
1118
+ <li>IMPROVED: Improved logic for Firewall whitelisting for pages and parameters to ensure whitelisting rules are followed.</li>
1119
+ <li>CHANGED: The whitelisting rule for posting pages/posts is only for the &quot;content&quot; and the firewall checking will apply to all other page parameters.</li>
1120
  </ul>
1121
  <p>= 2.1.4 =</p>
1122
  <ul>
1126
  <ul>
1127
  <li>FIX: A bug that prevented auto-updates of this plugin.</li>
1128
  <li>FIX: Not being able to hide translations and upgrade notices.</li>
1129
+ <li>ADDED: Tweaks to auto-update feature to allow interfacing with the iControlWP service to customize the auto update system.</li>
1130
  </ul>
1131
  <p>= 2.1.0 =</p>
1132
  <ul>
1133
+ <li>ADDED: A button that lets you run the WordPress Automatic Updates process on-demand (so you don&#39;t have to wait for WordPress cron).</li>
1134
  <li>CHANGED: The plugin now sets more options to be turned on by default when the plugin is first activated.</li>
1135
  <li>CHANGED: A lot of optimizations and code refactoring.</li>
1136
  </ul>
1137
  <p>= 2.0.3 =</p>
1138
  <ul>
1139
+ <li>FIX: Whoops, sorry, accidentally removed the option to toggle &quot;disable file editing&quot;. It&#39;s back now.</li>
1140
  </ul>
1141
  <p>= 2.0.2 =</p>
1142
  <ul>
1144
  </ul>
1145
  <p>= 2.0.1 =</p>
1146
  <ul>
1147
+ <li>ADDED: Localization capabilities. All we need now are translators! <a href="http://translate.icontrolwp.com/">Go here to get started</a>.</li>
1148
+ <li>ADDED: Option to mask the WordPress version so the real version is never publicly visible.</li>
1149
  </ul>
1150
  <p>= 1.9.2 =</p>
1151
  <ul>
1153
  </ul>
1154
  <p>= 1.9.1 =</p>
1155
  <ul>
1156
+ <li>ADDED: Increased admin access security features - blocks the deactivation of itself if you&#39;re not authenticated fully with the plugin.</li>
1157
+ <li>ADDED: If you&#39;re not authenticated with the plugin, the plugin listing view wont have &#39;Deactivate&#39; or &#39;Edit&#39; links.</li>
1158
  </ul>
1159
  <p>= 1.9.0 =</p>
1160
  <ul>
1161
+ <li>ADDED: New WordPress Automatic Updates Configuration settings</li>
1162
  </ul>
1163
  <p>= 1.8.2 =</p>
1164
  <ul>
1165
+ <li>ADDED: Notification of available plugin upgrade is now an option under the &#39;Dashboard&#39;</li>
1166
+ <li>CHANGED: Certain admin and upgrade notices now only appear when you&#39;re authenticated with the plugin (if this is enabled)</li>
1167
+ <li>FIXED: PHP Notice with undefined index.</li>
1168
  </ul>
1169
  <p>= 1.8.1 =</p>
1170
  <ul>
1171
+ <li>ADDED: Feature- Access Key Restriction <a href="https://shsec.io/2s">more info</a>.</li>
1172
+ <li>ADDED: Feature- WordPress Lockdown. Currently only provides 1 option, but more to come.</li>
1173
  </ul>
1174
  <p>= 1.7.3 =</p>
1175
  <ul>
1178
  </ul>
1179
  <p>= 1.7.1 =</p>
1180
  <ul>
1181
+ <li>ADDED: Much more efficiency yet again in the loading/saving of the plugin options.</li>
1182
  </ul>
1183
  <p>= 1.7.0 =</p>
1184
  <ul>
1185
+ <li>ADDED: Preliminary WordPress Multisite (WPMS/WPMU) Support.</li>
1186
+ <li>CHANGED: The Firewall now kicks in on the &#39;plugins_loaded&#39; hook instead of as the actual firewall plugin is initialized (as a result<pre><code> <span class="hljs-keyword">of</span> WP Multisite support).
1187
+ </code></pre></li>
1188
  </ul>
1189
  <p>= 1.6.2 =</p>
1190
  <ul>
1192
  </ul>
1193
  <p>= 1.6.1 =</p>
1194
  <ul>
1195
+ <li>ADDED: Options to fully customize the text displayed by the GASP comments section.</li>
1196
+ <li>ADDED: Option to include logged-in users in the GASP Comments Filter.</li>
1197
  </ul>
1198
  <p>= 1.6.0 =</p>
1199
  <ul>
1200
+ <li>ADDED: A new section - &#39;Comments Filtering&#39; that will form the basis for filtering comments with SPAM etc.</li>
1201
+ <li>ADDED: Option to add enhanced GASP based comments filtering to prevent SPAM bots posting comments to your site.</li>
1202
  </ul>
1203
  <p>= 1.5.6 =</p>
1204
  <ul>
1205
+ <li>IMPROVED: Whitelist/Blacklist IP range processing to better cater for ranges when saving, with more thorough checking.</li>
1206
+ <li>IMPROVED: Whitelist/Blacklist IP range processing for 32-bit systems.</li>
1207
+ <li>FIXED: A bug with Whitelist/Blacklist IP checking.</li>
1208
  </ul>
1209
  <p>= 1.5.5 =</p>
1210
  <ul>
1211
+ <li>FIXED: Quite a few bugs fixed.</li>
1212
  </ul>
1213
  <p>= 1.5.4 =</p>
1214
  <ul>
1215
+ <li>FIXED: Typo error.</li>
1216
  </ul>
1217
  <p>= 1.5.3 =</p>
1218
  <ul>
1219
+ <li>FIXED: Some of the firewall processors were saving unnecessary data.</li>
1220
  </ul>
1221
  <p>= 1.5.2 =</p>
1222
  <ul>
1223
  <li>CHANGED: The method for finding the client IP address is more thorough, in a bid to work with Proxy servers etc.</li>
1224
+ <li>FIXED: PHP notice reported here: <a href="http://wordpress.org/support/topic/getting-errors-when-logged-in">http://wordpress.org/support/topic/getting-errors-when-logged-in</a></li>
1225
  </ul>
1226
  <p>= 1.5.1 =</p>
1227
  <ul>
1228
+ <li>FIXED: Bug fix where IP address didn&#39;t show in email.</li>
1229
+ <li>FIXED: Attempt to fix problem where update message never hides.</li>
1230
  </ul>
1231
  <p>= 1.5.0 =</p>
1232
  <ul>
1233
+ <li>ADDED: A new IP whitelist on the Login Protect that lets you by-pass login protect rules for given IP addresses.</li>
1234
  <li>REMOVED: Firewall rule for wp-login.php and whitelisted IPs.</li>
1235
  </ul>
1236
  <p>= 1.4.2 =</p>
1237
  <ul>
1238
+ <li>ADDED: The plugin now has an option to automatically upgrade itself when an update is detected - enabled by default.</li>
1239
  </ul>
1240
  <p>= 1.4.1 =</p>
1241
  <ul>
1242
+ <li>ADDED: The plugin will now displays an admin notice when a plugin upgrade is available with a link to immediately update.</li>
1243
+ <li>ADDED: Plugin collision: removes the main hook by &#39;All In One WordPress Security&#39;. No need to have both plugins running.</li>
1244
+ <li>ADDED: Improved Login Cooldown Feature- works more like email throttling as it now uses an extra filesystem-based level of protection.</li>
1245
+ <li>FIXED: Login Cooldown Feature didn&#39;t take effect in certain circumstances.</li>
1246
  </ul>
1247
  <p>= 1.4.0 =</p>
1248
  <ul>
1249
+ <li>ADDED: All-new plugin options handling making them more efficient, easier to manage/update, using far fewer WordPress database options.</li>
1250
  <li>CHANGED: Huge improvements on database calls and efficiency in loading plugin options.</li>
1251
+ <li>FIXED: Nonce implementation.</li>
1252
  </ul>
1253
  <p>= 1.3.2 =</p>
1254
  <ul>
1255
+ <li>FIXED: Small compatibility issue with Quick Cache menu not showing.</li>
1256
  </ul>
1257
  <p>= 1.3.0 =</p>
1258
  <ul>
1259
+ <li>ADDED: Email Throttle Feature - this will prevent you getting bombarded by 1000s of emails in case you&#39;re hit by a bot.</li>
1260
+ <li>ADDED: Another Firewall die() option. New option will print a message and uses the wp_die() function instead.</li>
1261
+ <li>ADDED: Refactored and improved the logging system (upgrading will delete your current logs!).</li>
1262
+ <li>ADDED: Option to separately log Login Protect features.</li>
1263
+ <li>ADDED: Option to by-pass 2-factor authentication in the case sending the verification email fails<pre><code> <span class="hljs-comment">(so you don't get locked out if your hosting doesn't support email!)</span>.
1264
+ </code></pre></li>
1265
  <li>CHANGED: Login Protect checking now better logs out users immediately with a redirect.</li>
1266
+ <li>CHANGED: We now escape the log data being printed - just in case there&#39;s any HTML/JS etc in there we don&#39;t want.</li>
1267
  <li>CHANGED: Optimized and cleaned a lot of the option caching code to improve reliability and performance (more to come).</li>
1268
  </ul>
1269
  <p>= 1.2.7 =</p>
1272
  </ul>
1273
  <p>= 1.2.6 =</p>
1274
  <ul>
1275
+ <li>ADDED: Ability to import settings from WordPress Firewall 2 plugin options - note, doesn&#39;t import page and variables whitelisting.</li>
1276
  <li>FIX: A reported bug - parameter values could also be arrays.</li>
1277
  </ul>
1278
  <p>= 1.2.5 =</p>
1279
  <ul>
1280
+ <li>ADDED: New Feature - Option to add a checkbox that blocks automated SPAM Bots trying to log into your site.</li>
1281
+ <li>ADDED: Added a clear user message when they verify their 2-factor authentication.</li>
1282
  <li>FIX: A few bugfixes and logic corrections.</li>
1283
  </ul>
1284
  <p>= 1.2.4 =</p>
1296
  </ul>
1297
  <p>= 1.2.1 =</p>
1298
  <ul>
1299
+ <li>ADDED: New Feature - Login Wait Interval. To reduce the effectiveness of brute force login attacks, you can add an interval by<pre><code> which WordPress will <span class="hljs-built_in">wait</span> <span class="hljs-keyword">before</span> processing <span class="hljs-keyword">any</span> more login attempts <span class="hljs-keyword">on</span> <span class="hljs-title">a</span> <span class="hljs-title">site</span>.
1300
+ </code></pre></li>
1301
  <li>CHANGED: Optimized some settings for performance.</li>
1302
  <li>CHANGED: Cleaned up the UI when the Firewall / Login Protect features are disabled (more to come).</li>
1303
  <li>CHANGED: Further code improvements (more to come).</li>
1304
  </ul>
1305
  <p>= 1.2.0 =</p>
1306
  <ul>
1307
+ <li>ADDED: New Feature - <strong>Login Protect</strong>. Added 2-Factor Login Authentication for all users and their associated IP addresses.</li>
1308
  <li>CHANGED: The method for processing the IP address lists is improved.</li>
1309
  <li>CHANGED: Improved .htaccess rules (thanks MickeyRoush)</li>
1310
  <li>CHANGED: Mailing method now uses WP_MAIL</li>
1311
+ <li>CHANGED: Lot&#39;s of code improvements.</li>
1312
  </ul>
1313
  <p>= 1.1.6 =</p>
1314
  <ul>
1315
+ <li>ADDED: Option to include Cookies in the firewall checking.</li>
1316
  </ul>
1317
  <p>= 1.1.5 =</p>
1318
  <ul>
1335
  </ul>
1336
  <p>= 1.1.1 =</p>
1337
  <ul>
1338
+ <li>Fix: Block notification emails weren&#39;t showing the user-friendly IP Address format.</li>
1339
  </ul>
1340
  <p>= 1.1.0 =</p>
1341
  <ul>
1342
+ <li>You can now specify IP ranges in whitelists and blacklists. To do this separate the start and end address with a hyphen (-) E.g. For everything between 1.2.3.4 and 1.2.3.10, you would do: 1.2.3.4-1.2.3.10</li>
1343
  <li>You can now specify which email address to send the notification emails.</li>
1344
+ <li>You can now add a comment to IP addresses in the whitelist/blacklist. To do this, write your IP address then type a SPACE and write whatever you want (don&#39;t take a new line).</li>
1345
  <li>You can now set to delete ALL firewall settings when you deactivate the plugin.</li>
1346
  <li>Improved formatting of the firewall log.</li>
1347
  </ul>
1353
  <p>= 1.1.2 =</p>
1354
  <ul>
1355
  <li>CHANGED: Logging now has its own dedicated database table.</li>
1356
+ <li>Fix: Block notification emails weren&#39;t showing the user-friendly IP Address format.</li>
1357
+ <li>You can now specify IP ranges in whitelists and blacklists. To do this separate the start and end address with a hyphen (-) E.g. For everything between 1.2.3.4 and 1.2.3.10, you would do: 1.2.3.4-1.2.3.10</li>
1358
  <li>You can now specify which email address to send the notification emails.</li>
1359
+ <li>You can now add a comment to IP addresses in the whitelist/blacklist. To do this, write your IP address then type a SPACE and write whatever you want (don&#39;t take a new line).</li>
1360
  <li>You can now set to delete ALL firewall settings when you deactivate the plugin.</li>
1361
  <li>Improved formatting of the firewall log.</li>
1362
+ </ul>
 
 
filesnotfound.php CHANGED
@@ -1,12 +1,7 @@
1
  <?php
2
 
3
  foreach (
4
- [
5
- 'ICWP_WPSF_FeatureHandler_Base',
6
- 'ICWP_WPSF_FeatureHandler_BaseWpsf',
7
- 'ICWP_WPSF_Processor_Base',
8
- 'ICWP_WPSF_Processor_BaseWpsf',
9
- ] as $sClass
10
  ) {
11
  if ( !@class_exists( $sClass ) ) {
12
  add_action( 'admin_notices', 'icwp_wpsf_checkfilesnotfound' );
1
  <?php
2
 
3
  foreach (
4
+ [ 'ICWP_WPSF_FeatureHandler_Base', 'ICWP_WPSF_FeatureHandler_BaseWpsf', ] as $sClass
 
 
 
 
 
5
  ) {
6
  if ( !@class_exists( $sClass ) ) {
7
  add_action( 'admin_notices', 'icwp_wpsf_checkfilesnotfound' );
icwp-plugin-controller.php DELETED
@@ -1,25 +0,0 @@
1
- <?php
2
- /**
3
- * Copyright (c) 2019 One Dollar Plugin <support@onedollarplugin.com>
4
- * All rights reserved.
5
- * "Shield" (formerly WordPress Simple Firewall) is distributed under the GNU
6
- * General Public License, Version 2, June 1991. Copyright (C) 1989, 1991 Free
7
- * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110, USA
8
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
9
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
10
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
11
- * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
12
- * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
13
- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
14
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
15
- * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
16
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
17
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
18
- */
19
-
20
- /**
21
- * Class ICWP_WPSF_Plugin_Controller
22
- * @deprecated 7.5
23
- */
24
- class ICWP_WPSF_Plugin_Controller extends \FernleafSystems\Wordpress\Plugin\Shield\Controller\Controller {
25
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
icwp-wpsf.php CHANGED
@@ -1,17 +1,17 @@
1
  <?php
2
  /*
3
  * Plugin Name: Shield Security
4
- * Plugin URI: https://icwp.io/2f
5
  * Description: Powerful, Easy-To-Use #1 Rated WordPress Security System
6
- * Version: 8.3.0
7
  * Text Domain: wp-simple-firewall
8
  * Domain Path: /languages
9
- * Author: One Dollar Plugin
10
- * Author URI: https://icwp.io/bv
11
  */
12
 
13
  /**
14
- * Copyright (c) 2019 One Dollar Plugin <support@onedollarplugin.com>
15
  * All rights reserved.
16
  * "Shield" (formerly WordPress Simple Firewall) is distributed under the GNU
17
  * General Public License, Version 2, June 1991. Copyright (C) 1989, 1991 Free
1
  <?php
2
  /*
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: 8.4.4
7
  * Text Domain: wp-simple-firewall
8
  * Domain Path: /languages
9
+ * Author: Shield Security
10
+ * Author URI: https://shsec.io/bv
11
  */
12
 
13
  /**
14
+ * Copyright (c) 2019 Shield Security <support@shieldsecurity.io>
15
  * All rights reserved.
16
  * "Shield" (formerly WordPress Simple Firewall) is distributed under the GNU
17
  * General Public License, Version 2, June 1991. Copyright (C) 1989, 1991 Free
plugin-spec.php CHANGED
@@ -1,8 +1,8 @@
1
  {
2
  "properties": {
3
- "version": "8.3.0",
4
- "release_timestamp": 1574070468,
5
- "build": "201911.1802",
6
  "slug_parent": "icwp",
7
  "slug_plugin": "wpsf",
8
  "human_name": "Shield",
@@ -75,23 +75,23 @@
75
  "Name": "Shield",
76
  "Description": "Ultimate WP Security Protection - Scans, 2FA, Firewall, SPAM, Audit Trail, Security Admin, and so much more.",
77
  "Title": "Shield Security",
78
- "Author": "One Dollar Plugin",
79
- "AuthorName": "One Dollar Plugin",
80
- "PluginURI": "https://icwp.io/2f",
81
- "AuthorURI": "https://icwp.io/bv",
82
  "icon_url_16x16": "pluginlogo_16x16.png",
83
  "icon_url_32x32": "pluginlogo_32x32.png",
84
  "icon_url_128x128": "pluginlogo_128x128.png"
85
  },
86
  "meta": {
87
- "url_repo_home": "https://icwp.io/eh",
88
  "headway_changelog_id": "xaoEZJ",
89
- "privacy_policy_href": "https://icwp.io/shieldprivacypolicy"
90
  },
91
  "plugin_meta": [
92
  {
93
  "name": "5&#10025; Rate This Plugin",
94
- "href": "https://icwp.io/wpsf29"
95
  }
96
  ],
97
  "action_links": {
@@ -107,7 +107,7 @@
107
  {
108
  "name": "&uarr; Go Pro &uarr;",
109
  "title": "For just $1/month. Seriously.",
110
- "href": "https://icwp.io/d8",
111
  "target": "_blank",
112
  "highlight": true,
113
  "show": "free"
1
  {
2
  "properties": {
3
+ "version": "8.4.4",
4
+ "release_timestamp": 1575630000,
5
+ "build": "201912.0601",
6
  "slug_parent": "icwp",
7
  "slug_plugin": "wpsf",
8
  "human_name": "Shield",
75
  "Name": "Shield",
76
  "Description": "Ultimate WP Security Protection - Scans, 2FA, Firewall, SPAM, Audit Trail, Security Admin, and so much more.",
77
  "Title": "Shield Security",
78
+ "Author": "Shield Security",
79
+ "AuthorName": "Shield Security",
80
+ "PluginURI": "https://shsec.io/2f",
81
+ "AuthorURI": "https://shsec.io/bv",
82
  "icon_url_16x16": "pluginlogo_16x16.png",
83
  "icon_url_32x32": "pluginlogo_32x32.png",
84
  "icon_url_128x128": "pluginlogo_128x128.png"
85
  },
86
  "meta": {
87
+ "url_repo_home": "https://shsec.io/eh",
88
  "headway_changelog_id": "xaoEZJ",
89
+ "privacy_policy_href": "https://shsec.io/shieldprivacypolicy"
90
  },
91
  "plugin_meta": [
92
  {
93
  "name": "5&#10025; Rate This Plugin",
94
+ "href": "https://shsec.io/wpsf29"
95
  }
96
  ],
97
  "action_links": {
107
  {
108
  "name": "&uarr; Go Pro &uarr;",
109
  "title": "For just $1/month. Seriously.",
110
+ "href": "https://shsec.io/d8",
111
  "target": "_blank",
112
  "highlight": true,
113
  "show": "free"
readme.txt CHANGED
@@ -1,6 +1,6 @@
1
- === Shield Security: Protection with Smarter Automation ===
2
  Contributors: onedollarplugin, paultgoodchild
3
- Donate link: https://icwp.io/bw
4
  License: GPLv3
5
  License URI: http://www.gnu.org/licenses/gpl.html
6
  Tags: scan, malware, firewall, two factor authentication, login protection
@@ -8,7 +8,7 @@ Requires at least: 3.5.2
8
  Requires PHP: 5.4.0
9
  Recommended PHP: 7.0
10
  Tested up to: 5.3
11
- Stable tag: 8.3.0
12
 
13
  Smarter security protection from hackers through automation. Powerful scanners, 2-Factor Auth, limit logins, auto IP blocks & more.
14
 
@@ -18,7 +18,7 @@ Smarter security protection from hackers through automation. Powerful scanners,
18
 
19
  Shield - highest average 5* rating for any WordPress security plugin (2019/05). [See what people are saying here](https://wordpress.org/support/plugin/wp-simple-firewall/reviews/?filter=5).
20
 
21
- #### It's 2019 - Don't settle for just another security plugin. Get *smarter* security.
22
 
23
  You don't need another 100 email notifications.
24
 
@@ -107,9 +107,9 @@ From November 2017, Shield Security now has a Pro version for those that need to
107
  ### Dedicated Premium Support
108
 
109
  The Shield Security team prioritises email technical support over the WordPress.org forums.
110
- Individual, dedicated technical support is only available to customers who have [purchased Shield Pro](https://icwp.io/ab).
111
 
112
- Learn more on going Pro at [our One Dollar Plugin store](https://icwp.io/ab).
113
 
114
  = Our Mission =
115
 
@@ -136,19 +136,19 @@ downloading and installing Shield now
136
  * Exclusive membership to a private security group where you can learn more about WordPress security.
137
 
138
  = Super Admin Security Protection =
139
- The **only** WordPress security plugin with a WordPress-independent security key to protect itself. [more info](https://icwp.io/wpsf05)
140
 
141
  = Audit Trail Activity Monitor =
142
  With the Audit Trail you can review all major actions that have taken place on your WordPress site, by all users.
143
 
144
  = Firewall Protection =
145
- Blocks all web requests to the site that violate the firewall security rules! [more info](https://icwp.io/wpsf06)
146
 
147
  = Brute Force Login Guard and Two-Factor Authentication =
148
- Provides effective security against Brute Force Hacking and email based Two-Factor Authenticated login. [more info](https://icwp.io/wpsf07)
149
 
150
  = Comment SPAM (Full replacement and upgrade from Akismet) =
151
- Blocks **ALL** automatic Bot-SPAM, and catches Human Comments SPAM without sending data to 3rd parties or charging subscription fees. [more info](https://icwp.io/wpsf08)
152
 
153
  = FABLE - Fully Automatic Black Listing Engine =
154
  No more manual IP Black lists. This plugin handles the blocking of IP addresses for hosts that are naughty.
@@ -172,9 +172,9 @@ and force users to verify themselves when they login.
172
 
173
  Three core security features provide layers to protect the WordPress Login system.
174
 
175
- 1. [Email-based 2-Factor Login Authentication](https://icwp.io/2v) based on IP address! (prevents brute force login attacks)
176
- 1. [Login Cooldown Interval](https://icwp.io/2t) - WordPress will only process 1 login per interval in seconds (prevents brute force login attacks)
177
- 1. [GASP Anti-Bot Login Form Protection](https://icwp.io/2u) - Adds 2 protection checks for all WordPress login attempts (prevents brute force login attacks using Bots)
178
 
179
  These options alone will protect and secure your WordPress sites from nearly all forms of Brute Force login attacks.
180
 
@@ -227,7 +227,7 @@ A new menu item will appear on the left-hand side called 'Shield'.
227
 
228
  == Frequently Asked Questions ==
229
 
230
- Please see the dedicated [help centre](https://icwp.io/firewallhelp) for details on features and some FAQs.
231
 
232
  = How does the Shield compare with other WordPress Security Plugins? =
233
 
@@ -318,15 +318,15 @@ When enabled the plugin will prevent more than 1 login attempt to your site ever
318
  of 60 seconds, only 1 login attempt will be processed every 60 seconds. If you login incorrectly, you wont be able to attempt another
319
  login for a further 60 seconds.
320
 
321
- More Info: https://icwp.io/2t
322
 
323
  = How does the GASP Login Guard work? =
324
 
325
- This is best [described on the blog](https://icwp.io/2u)
326
 
327
  = How does the 2-factor authentication work? =
328
 
329
- [2-Factor Authentication is best described here](https://icwp.io/2v).
330
 
331
  = I'm getting an update message although I have auto update enabled? =
332
 
@@ -368,101 +368,24 @@ You'll also have access to our email technical support team.
368
 
369
  You will always be able to use Shield Security and its free features in-full.
370
 
371
- [Go Pro for just $1/month](https://icwp.io/aa).
372
-
373
- = 8.3.0 - Current Release =
374
- *Released: 18th November, 2019* - [Release Notes](https://icwp.io/g3)
375
-
376
- * **(v.0)** IMPROVED: Improvements to Malware scanner to [now track malware results](https://icwp.io/g3) by specific lines, not just by file.
377
- * **(v.0)** IMPROVED: Support colons (:) in IP addresses during visitor IP address detection.
378
- * **(v.0)** IMPROVED: Ensure license lookups use the correct site URL.
379
- * **(v.0)** IMPROVED: Attempt to ensure that if there is an interruption in the API, malware patterns are available for scanning.
380
- * **(v.0)** IMPROVED: Added default firewall whitelist parameter for AffiliateWP requests.
381
- * **(v.0)** IMPROVED: Spanish, French, Japanese translations.
382
-
383
- = 8.2 - Series =
384
- *Released: 1st October, 2019* - [Release Notes](https://icwp.io/g0)
385
-
386
- * **(v.3)** FIXED: Fix for reported RXSS vulnerability - [more info](https://icwp.io/g1).
387
- * **(v.3)** FIXED: Fix for Rest API detection.
388
- * **(v.3)** FIXED: Fix for translation of some strings.
389
- * **(v.2)** FIXED: Fixes for scans running under Windows/IIS.
390
- * **(v.2)** IMPROVED: Adds a check that a site can send an HTTP request to itself before allowing scans to run.
391
- * **(v.2)** IMPROVED: Scans clean up after themselves better, if they fail to run.
392
- * **(v.2)** IMPROVED: Server's own IP address detection when site migrated to a new host.
393
- * **(v.2)** UPDATED: International translations.
394
- * **(v.2)** FIXED: PHP notices when data wasn't as expected.
395
- * **(v.1)** IMPROVED: Further reduce Malware false positives by also using SVN trunk data when verifying files for plugins and themes.
396
- * **(v.1)** ADDED: Initial support for repairing Themes that have been installed from WordPress.org.
397
- * **(v.1)** ADDED: Support for using [WP Hashes.com](https://wphashes.com) for WordPress.org themes (already done for plugins).
398
- * **(v.1)** FIXED: PHP notices in the logs.
399
- * **(v.0)** IMPROVED: [**PRO**] Malware scanner now uses network intelligence to the gather information on malware results.
400
- * **(v.0)** NEW: Traffic Watcher feature is now free for all users (no longer Pro-only).
401
- * **(v.0)** IMPROVED: Scanning cron is improved and more efficient.
402
- * **(v.0)** ADDED: Bulk Delete/Repair/Ignore actions now available for Malware scan results.
403
- * **(v.0)** IMPROVED: Malware scan results now provide details of affected line numbers and patterns discovered.
404
- * **(v.0)** IMPROVED: Malware scanner only scans `wp-admin`, `wp-includes`, `wp-content` folders, and files in top-level directory.
405
- * **(v.0)** IMPROVED: Malware scanner now excludes `wp-content/cache/` directory.
406
- * **(v.0)** IMPROVED: Malware scanner performance improved with caching.
407
- * **(v.0)** IMPROVED: Malware auto-repair now works more consistently.
408
- * **(v.0)** IMPROVED: Updated default firewall whitelist rules.
409
- * **(v.0)** IMPROVED: If the PWNED Passwords API request fails entirely, the password check is skipped.
410
- * **(v.0)** ADDED: Japanese translations are at 100%.
411
- * **(v.0)** IMPROVED: Dutch translations are greatly improved (a huge thank you to Fred!).
412
- * **(v.0)** FIXED: Audit Trail correctly logs multiple occurrences for the same type of event on the same page request.
413
- * **(v.0)** FIXED: Audit Trail now correctly logs Google reCAPTCHA failure events.
414
- * **(v.0)** FIXED: PHP error when firewall was set to kill response without a user message.
415
-
416
- = 8.1 - Series =
417
- *Released: 18th September, 2019* - [Release Notes](https://icwp.io/fy)
418
-
419
- * **(v.1)** FIXED: Error for sites pre-5.0 that don't have function `determine_locale()`
420
- * **(v.0)** IMPROVED: Massive improvements to asynchronous scans in performance and reliability.
421
- * **(v.0)** ADDED: [**PRO**] Possible to supply multiple email addresses for Administrator login notifications.
422
- * **(v.0)** ADDED: New firewall whitelist rule to prevent firewall blocks when activating certain plugins.
423
- * **(v.0)** IMPROVED: Prevent errors caused by other plugins not passing correctly-formatted data through WP filters.
424
- * **(v.0)** ADDED: Japanese translations (14%).
425
- * **(v.0)** IMPROVED: Plugin locale now respects user profile locale setting.
426
- * **(v.0)** IMPROVED: Audit Trail filter for specific events.
427
- * **(v.0)** IMPROVED: Lots of cleanup of deprecated PHP code following the the v7-v8 upgrade.
428
-
429
- = 8.0 - Series =
430
- *Released: 27th August, 2019* - [Release Notes](https://icwp.io/fv)
431
-
432
- * **(v.2)** IMPROVED: Password strength metering now better aligns with WordPress library (PHP 5.6+)
433
- * **(v.2)** IMPROVED: Dutch translations have been adjusted.
434
- * **(v.2)** FIXED: Setting 'Month' for IP block duration wasn't being applied.
435
- * **(v.2)** FIXED: Certain admin notices not displayed when they should be.
436
- * **(v.1)** FIXED: Comment SPAM blocking wasn't working if set to "Detect and Reject".
437
- * **(v.1)** FIXED: Shield Widget/Badge broken in some cases.
438
- * **(v.1)** ADDED: You can force Shield to operate in any [locale, regardless of site locale](https://icwp.io/gistshieldlocale).
439
- * **(v.1)** ADDED: Russian translations are now at 100% and some Dutch translations have been adjusted.
440
- * **(v.0)** NEW: [**PRO**] New Malware Scanner with automated file repair for WordPress.org Plugins and Core.
441
- * **(v.0)** NEW: Complete overhaul of events system to better audit and collect statistics.
442
- * **(v.0)** IMPROVED: Asynchronous scans - scans run in the background and so support more restrictive hosting.
443
- * **(v.0)** IMPROVED: Plugin notification system is much improved.
444
- * **(v.0)** IMPROVED: [**PRO**] Plugin Guard uses SVN repositories for file references [via WP Hashes API](https://icwp.io/fw).
445
- * **(v.0)** CHANGED: Comment SPAM system now uses WordPress Transients API instead of dedicated DB table.
446
- * **(v.0)** ADDED: 100% Translation coverage for French, Spanish, German, Portuguese, Serbian, Bosnian, Dutch. (Russian on the way)
447
- * **(v.0)** CHANGED: Major code cleaning/refactoring for much of the plugin. More to come.
448
-
449
- = 7.4 - Series =
450
- *Released: 13th May, 2019* - [Release Notes](https://icwp.io/fc)
451
-
452
- * **(v.2)** NEW: Options finder/jumper menu lets you find and jump to any option in the plugin instantly.
453
- * **(v.2)** NEW: Help/explainer videos for a few sections - more to come.
454
- * **(v.2)** FIXES: Fixes for a few problems introduced with the recent UI changes.
455
- * **(v.2)** FIXED: Welcome wizard launching was broken.
456
- * **(v.1)** NEW: Adjustments and redesign of Shield options pages.
457
- * **(v.1)** IMPROVED: Further prep for better internationalization.
458
- * **(v.0)** NEW: [**PRO**] [Manual/Automatic User Suspension](https://icwp.io/fa)
459
- * **(v.0)** NEW: Comment SPAM - Increase minimum number of approved comments before scanning is skipped
460
- * **(v.0)** NEW: [**PRO**] Comment SPAM - Trusted user roles where comments scanning is skipped
461
- * **(v.0)** IMPROVED: AntiBot JS was improperly included when not required.
462
- * **(v.0)** IMPROVED: Added a GeoIP caching table and removed bundled GeoIP database - greatly reduces download size.
463
- * **(v.0)** FIXED: Inconsistent behaviour when PWA plugin is active and it infinitely reloads pages.
464
- * **(v.0)** FIXED: Inconsistent behaviour with Anonymous API blocking.
465
- * **(v.0)** IMPROVED: Code improvements and refactoring.
466
- * **(v.0)** ADDED: Prep for upcoming malware scanner.
467
-
468
- #### [Full Changelog](https://icwp.io/shieldwporgfullchangelog)
1
+ === Shield Security: Protection with Smarter Automation ===
2
  Contributors: onedollarplugin, paultgoodchild
3
+ Donate link: https://shsec.io/bw
4
  License: GPLv3
5
  License URI: http://www.gnu.org/licenses/gpl.html
6
  Tags: scan, malware, firewall, two factor authentication, login protection
8
  Requires PHP: 5.4.0
9
  Recommended PHP: 7.0
10
  Tested up to: 5.3
11
+ Stable tag: 8.4.4
12
 
13
  Smarter security protection from hackers through automation. Powerful scanners, 2-Factor Auth, limit logins, auto IP blocks & more.
14
 
18
 
19
  Shield - highest average 5* rating for any WordPress security plugin (2019/05). [See what people are saying here](https://wordpress.org/support/plugin/wp-simple-firewall/reviews/?filter=5).
20
 
21
+ #### It's 2020 - Don't settle for just another security plugin. Get *smarter* security.
22
 
23
  You don't need another 100 email notifications.
24
 
107
  ### Dedicated Premium Support
108
 
109
  The Shield Security team prioritises email technical support over the WordPress.org forums.
110
+ Individual, dedicated technical support is only available to customers who have [purchased Shield Pro](https://shsec.io/ab).
111
 
112
+ Discover all the perks turning your security Pro at [our Shield Security store](https://shsec.io/ab).
113
 
114
  = Our Mission =
115
 
136
  * Exclusive membership to a private security group where you can learn more about WordPress security.
137
 
138
  = Super Admin Security Protection =
139
+ The **only** WordPress security plugin with a WordPress-independent security key to protect itself. [more info](https://shsec.io/wpsf05)
140
 
141
  = Audit Trail Activity Monitor =
142
  With the Audit Trail you can review all major actions that have taken place on your WordPress site, by all users.
143
 
144
  = Firewall Protection =
145
+ Blocks all web requests to the site that violate the firewall security rules! [more info](https://shsec.io/wpsf06)
146
 
147
  = Brute Force Login Guard and Two-Factor Authentication =
148
+ Provides effective security against Brute Force Hacking and email based Two-Factor Authenticated login. [more info](https://shsec.io/wpsf07)
149
 
150
  = Comment SPAM (Full replacement and upgrade from Akismet) =
151
+ Blocks **ALL** automatic Bot-SPAM, and catches Human Comments SPAM without sending data to 3rd parties or charging subscription fees. [more info](https://shsec.io/wpsf08)
152
 
153
  = FABLE - Fully Automatic Black Listing Engine =
154
  No more manual IP Black lists. This plugin handles the blocking of IP addresses for hosts that are naughty.
172
 
173
  Three core security features provide layers to protect the WordPress Login system.
174
 
175
+ 1. [Email-based 2-Factor Login Authentication](https://shsec.io/2v) based on IP address! (prevents brute force login attacks)
176
+ 1. [Login Cooldown Interval](https://shsec.io/2t) - WordPress will only process 1 login per interval in seconds (prevents brute force login attacks)
177
+ 1. [GASP Anti-Bot Login Form Protection](https://shsec.io/2u) - Adds 2 protection checks for all WordPress login attempts (prevents brute force login attacks using Bots)
178
 
179
  These options alone will protect and secure your WordPress sites from nearly all forms of Brute Force login attacks.
180
 
227
 
228
  == Frequently Asked Questions ==
229
 
230
+ Please see the dedicated [help centre](https://shsec.io/firewallhelp) for details on features and some FAQs.
231
 
232
  = How does the Shield compare with other WordPress Security Plugins? =
233
 
318
  of 60 seconds, only 1 login attempt will be processed every 60 seconds. If you login incorrectly, you wont be able to attempt another
319
  login for a further 60 seconds.
320
 
321
+ More Info: https://shsec.io/2t
322
 
323
  = How does the GASP Login Guard work? =
324
 
325
+ This is best [described on the blog](https://shsec.io/2u)
326
 
327
  = How does the 2-factor authentication work? =
328
 
329
+ [2-Factor Authentication is best described here](https://shsec.io/2v).
330
 
331
  = I'm getting an update message although I have auto update enabled? =
332
 
368
 
369
  You will always be able to use Shield Security and its free features in-full.
370
 
371
+ [Go Pro for just $1/month](https://shsec.io/aa).
372
+
373
+ = 8.4.4 - Current Release =
374
+ *Released: 6th December, 2019* - [Release Notes](https://shsec.io/g5)
375
+
376
+ * **(v.4)** IMPROVED: Discovered serious conflict with SiteGround Optimizer plugin. Provided admin notice and automatic fixing.
377
+ * **(v.4)** FIXED: Protected against spurious error log notices when comparing hashes with "nothing".
378
+
379
+ = 8.4 - Series =
380
+ *Released: 29th November, 2019* - [Release Notes](https://shsec.io/g5)
381
+
382
+ * **(v.3)** FIXED: Reduce chances of fatal error occurring during upgrade.
383
+ * **(v.0)** ADDED: Charts of important events on Overview page highlight effectiveness of Shield.
384
+ * **(v.0)** ADDED: Support for whitelisting IPv6 ranges.
385
+ * **(v.0)** ADDED: Allow Audit Trail logging for Shield's Bot Detection features for all free installations.
386
+ * **(v.0)** IMPROVED: Malware scanner false-positive lookups now use further intelligence from API.
387
+ * **(v.0)** IMPROVED: Refactor Comment SPAM implementation away from inline-Javascript.
388
+ * **(v.0)** IMPROVED: Consolidate Events/Statistics database table to significantly reduce DB size.
389
+ * **(v.0)** CLEANED: Significant clean-out of old, deprecated, retired code.
390
+
391
+ #### [Full Shield Security Changelog](https://shsec.io/shieldwporgfullchangelog)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
resources/css/chartist.min.css CHANGED
@@ -1 +1 @@
1
- .ct-double-octave:after,.ct-major-eleventh:after,.ct-major-second:after,.ct-major-seventh:after,.ct-major-sixth:after,.ct-major-tenth:after,.ct-major-third:after,.ct-major-twelfth:after,.ct-minor-second:after,.ct-minor-seventh:after,.ct-minor-sixth:after,.ct-minor-third:after,.ct-octave:after,.ct-perfect-fifth:after,.ct-perfect-fourth:after,.ct-square:after{content:"";clear:both}.ct-label{fill:rgba(0,0,0,.4);color:rgba(0,0,0,.4);font-size:.75rem;line-height:1}.ct-grid-background,.ct-line{fill:none}.ct-chart-bar .ct-label,.ct-chart-line .ct-label{display:block;display:-webkit-box;display:-moz-box;display:-ms-flexbox;display:-webkit-flex;display:flex}.ct-chart-donut .ct-label,.ct-chart-pie .ct-label{dominant-baseline:central}.ct-label.ct-horizontal.ct-start{-webkit-box-align:flex-end;-webkit-align-items:flex-end;-ms-flex-align:flex-end;align-items:flex-end;-webkit-box-pack:flex-start;-webkit-justify-content:flex-start;-ms-flex-pack:flex-start;justify-content:flex-start;text-align:left;text-anchor:start}.ct-label.ct-horizontal.ct-end{-webkit-box-align:flex-start;-webkit-align-items:flex-start;-ms-flex-align:flex-start;align-items:flex-start;-webkit-box-pack:flex-start;-webkit-justify-content:flex-start;-ms-flex-pack:flex-start;justify-content:flex-start;text-align:left;text-anchor:start}.ct-label.ct-vertical.ct-start{-webkit-box-align:flex-end;-webkit-align-items:flex-end;-ms-flex-align:flex-end;align-items:flex-end;-webkit-box-pack:flex-end;-webkit-justify-content:flex-end;-ms-flex-pack:flex-end;justify-content:flex-end;text-align:right;text-anchor:end}.ct-label.ct-vertical.ct-end{-webkit-box-align:flex-end;-webkit-align-items:flex-end;-ms-flex-align:flex-end;align-items:flex-end;-webkit-box-pack:flex-start;-webkit-justify-content:flex-start;-ms-flex-pack:flex-start;justify-content:flex-start;text-align:left;text-anchor:start}.ct-chart-bar .ct-label.ct-horizontal.ct-start{-webkit-box-align:flex-end;-webkit-align-items:flex-end;-ms-flex-align:flex-end;align-items:flex-end;-webkit-box-pack:center;-webkit-justify-content:center;-ms-flex-pack:center;justify-content:center;text-align:center;text-anchor:start}.ct-chart-bar .ct-label.ct-horizontal.ct-end{-webkit-box-align:flex-start;-webkit-align-items:flex-start;-ms-flex-align:flex-start;align-items:flex-start;-webkit-box-pack:center;-webkit-justify-content:center;-ms-flex-pack:center;justify-content:center;text-align:center;text-anchor:start}.ct-chart-bar.ct-horizontal-bars .ct-label.ct-horizontal.ct-start{-webkit-box-align:flex-end;-webkit-align-items:flex-end;-ms-flex-align:flex-end;align-items:flex-end;-webkit-box-pack:flex-start;-webkit-justify-content:flex-start;-ms-flex-pack:flex-start;justify-content:flex-start;text-align:left;text-anchor:start}.ct-chart-bar.ct-horizontal-bars .ct-label.ct-horizontal.ct-end{-webkit-box-align:flex-start;-webkit-align-items:flex-start;-ms-flex-align:flex-start;align-items:flex-start;-webkit-box-pack:flex-start;-webkit-justify-content:flex-start;-ms-flex-pack:flex-start;justify-content:flex-start;text-align:left;text-anchor:start}.ct-chart-bar.ct-horizontal-bars .ct-label.ct-vertical.ct-start{-webkit-box-align:center;-webkit-align-items:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:flex-end;-webkit-justify-content:flex-end;-ms-flex-pack:flex-end;justify-content:flex-end;text-align:right;text-anchor:end}.ct-chart-bar.ct-horizontal-bars .ct-label.ct-vertical.ct-end{-webkit-box-align:center;-webkit-align-items:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:flex-start;-webkit-justify-content:flex-start;-ms-flex-pack:flex-start;justify-content:flex-start;text-align:left;text-anchor:end}.ct-grid{stroke:rgba(0,0,0,.2);stroke-width:1px;stroke-dasharray:2px}.ct-point{stroke-width:10px;stroke-linecap:round}.ct-line{stroke-width:4px}.ct-area{stroke:none;fill-opacity:.1}.ct-bar{fill:none;stroke-width:10px}.ct-slice-donut{fill:none;stroke-width:60px}.ct-series-a .ct-bar,.ct-series-a .ct-line,.ct-series-a .ct-point,.ct-series-a .ct-slice-donut{stroke:#d70206}.ct-series-a .ct-area,.ct-series-a .ct-slice-donut-solid,.ct-series-a .ct-slice-pie{fill:#d70206}.ct-series-b .ct-bar,.ct-series-b .ct-line,.ct-series-b .ct-point,.ct-series-b .ct-slice-donut{stroke:#f05b4f}.ct-series-b .ct-area,.ct-series-b .ct-slice-donut-solid,.ct-series-b .ct-slice-pie{fill:#f05b4f}.ct-series-c .ct-bar,.ct-series-c .ct-line,.ct-series-c .ct-point,.ct-series-c .ct-slice-donut{stroke:#f4c63d}.ct-series-c .ct-area,.ct-series-c .ct-slice-donut-solid,.ct-series-c .ct-slice-pie{fill:#f4c63d}.ct-series-d .ct-bar,.ct-series-d .ct-line,.ct-series-d .ct-point,.ct-series-d .ct-slice-donut{stroke:#d17905}.ct-series-d .ct-area,.ct-series-d .ct-slice-donut-solid,.ct-series-d .ct-slice-pie{fill:#d17905}.ct-series-e .ct-bar,.ct-series-e .ct-line,.ct-series-e .ct-point,.ct-series-e .ct-slice-donut{stroke:#453d3f}.ct-series-e .ct-area,.ct-series-e .ct-slice-donut-solid,.ct-series-e .ct-slice-pie{fill:#453d3f}.ct-series-f .ct-bar,.ct-series-f .ct-line,.ct-series-f .ct-point,.ct-series-f .ct-slice-donut{stroke:#59922b}.ct-series-f .ct-area,.ct-series-f .ct-slice-donut-solid,.ct-series-f .ct-slice-pie{fill:#59922b}.ct-series-g .ct-bar,.ct-series-g .ct-line,.ct-series-g .ct-point,.ct-series-g .ct-slice-donut{stroke:#0544d3}.ct-series-g .ct-area,.ct-series-g .ct-slice-donut-solid,.ct-series-g .ct-slice-pie{fill:#0544d3}.ct-series-h .ct-bar,.ct-series-h .ct-line,.ct-series-h .ct-point,.ct-series-h .ct-slice-donut{stroke:#6b0392}.ct-series-h .ct-area,.ct-series-h .ct-slice-donut-solid,.ct-series-h .ct-slice-pie{fill:#6b0392}.ct-series-i .ct-bar,.ct-series-i .ct-line,.ct-series-i .ct-point,.ct-series-i .ct-slice-donut{stroke:#f05b4f}.ct-series-i .ct-area,.ct-series-i .ct-slice-donut-solid,.ct-series-i .ct-slice-pie{fill:#f05b4f}.ct-series-j .ct-bar,.ct-series-j .ct-line,.ct-series-j .ct-point,.ct-series-j .ct-slice-donut{stroke:#dda458}.ct-series-j .ct-area,.ct-series-j .ct-slice-donut-solid,.ct-series-j .ct-slice-pie{fill:#dda458}.ct-series-k .ct-bar,.ct-series-k .ct-line,.ct-series-k .ct-point,.ct-series-k .ct-slice-donut{stroke:#eacf7d}.ct-series-k .ct-area,.ct-series-k .ct-slice-donut-solid,.ct-series-k .ct-slice-pie{fill:#eacf7d}.ct-series-l .ct-bar,.ct-series-l .ct-line,.ct-series-l .ct-point,.ct-series-l .ct-slice-donut{stroke:#86797d}.ct-series-l .ct-area,.ct-series-l .ct-slice-donut-solid,.ct-series-l .ct-slice-pie{fill:#86797d}.ct-series-m .ct-bar,.ct-series-m .ct-line,.ct-series-m .ct-point,.ct-series-m .ct-slice-donut{stroke:#b2c326}.ct-series-m .ct-area,.ct-series-m .ct-slice-donut-solid,.ct-series-m .ct-slice-pie{fill:#b2c326}.ct-series-n .ct-bar,.ct-series-n .ct-line,.ct-series-n .ct-point,.ct-series-n .ct-slice-donut{stroke:#6188e2}.ct-series-n .ct-area,.ct-series-n .ct-slice-donut-solid,.ct-series-n .ct-slice-pie{fill:#6188e2}.ct-series-o .ct-bar,.ct-series-o .ct-line,.ct-series-o .ct-point,.ct-series-o .ct-slice-donut{stroke:#a748ca}.ct-series-o .ct-area,.ct-series-o .ct-slice-donut-solid,.ct-series-o .ct-slice-pie{fill:#a748ca}.ct-square{display:block;position:relative;width:100%}.ct-square:before{display:block;float:left;content:"";width:0;height:0;padding-bottom:100%}.ct-square:after{display:table}.ct-square>svg{display:block;position:absolute;top:0;left:0}.ct-minor-second{display:block;position:relative;width:100%}.ct-minor-second:before{display:block;float:left;content:"";width:0;height:0;padding-bottom:93.75%}.ct-minor-second:after{display:table}.ct-minor-second>svg{display:block;position:absolute;top:0;left:0}.ct-major-second{display:block;position:relative;width:100%}.ct-major-second:before{display:block;float:left;content:"";width:0;height:0;padding-bottom:88.8888888889%}.ct-major-second:after{display:table}.ct-major-second>svg{display:block;position:absolute;top:0;left:0}.ct-minor-third{display:block;position:relative;width:100%}.ct-minor-third:before{display:block;float:left;content:"";width:0;height:0;padding-bottom:83.3333333333%}.ct-minor-third:after{display:table}.ct-minor-third>svg{display:block;position:absolute;top:0;left:0}.ct-major-third{display:block;position:relative;width:100%}.ct-major-third:before{display:block;float:left;content:"";width:0;height:0;padding-bottom:80%}.ct-major-third:after{display:table}.ct-major-third>svg{display:block;position:absolute;top:0;left:0}.ct-perfect-fourth{display:block;position:relative;width:100%}.ct-perfect-fourth:before{display:block;float:left;content:"";width:0;height:0;padding-bottom:75%}.ct-perfect-fourth:after{display:table}.ct-perfect-fourth>svg{display:block;position:absolute;top:0;left:0}.ct-perfect-fifth{display:block;position:relative;width:100%}.ct-perfect-fifth:before{display:block;float:left;content:"";width:0;height:0;padding-bottom:66.6666666667%}.ct-perfect-fifth:after{display:table}.ct-perfect-fifth>svg{display:block;position:absolute;top:0;left:0}.ct-minor-sixth{display:block;position:relative;width:100%}.ct-minor-sixth:before{display:block;float:left;content:"";width:0;height:0;padding-bottom:62.5%}.ct-minor-sixth:after{display:table}.ct-minor-sixth>svg{display:block;position:absolute;top:0;left:0}.ct-golden-section{display:block;position:relative;width:100%}.ct-golden-section:before{display:block;float:left;content:"";width:0;height:0;padding-bottom:61.804697157%}.ct-golden-section:after{content:"";display:table;clear:both}.ct-golden-section>svg{display:block;position:absolute;top:0;left:0}.ct-major-sixth{display:block;position:relative;width:100%}.ct-major-sixth:before{display:block;float:left;content:"";width:0;height:0;padding-bottom:60%}.ct-major-sixth:after{display:table}.ct-major-sixth>svg{display:block;position:absolute;top:0;left:0}.ct-minor-seventh{display:block;position:relative;width:100%}.ct-minor-seventh:before{display:block;float:left;content:"";width:0;height:0;padding-bottom:56.25%}.ct-minor-seventh:after{display:table}.ct-minor-seventh>svg{display:block;position:absolute;top:0;left:0}.ct-major-seventh{display:block;position:relative;width:100%}.ct-major-seventh:before{display:block;float:left;content:"";width:0;height:0;padding-bottom:53.3333333333%}.ct-major-seventh:after{display:table}.ct-major-seventh>svg{display:block;position:absolute;top:0;left:0}.ct-octave{display:block;position:relative;width:100%}.ct-octave:before{display:block;float:left;content:"";width:0;height:0;padding-bottom:50%}.ct-octave:after{display:table}.ct-octave>svg{display:block;position:absolute;top:0;left:0}.ct-major-tenth{display:block;position:relative;width:100%}.ct-major-tenth:before{display:block;float:left;content:"";width:0;height:0;padding-bottom:40%}.ct-major-tenth:after{display:table}.ct-major-tenth>svg{display:block;position:absolute;top:0;left:0}.ct-major-eleventh{display:block;position:relative;width:100%}.ct-major-eleventh:before{display:block;float:left;content:"";width:0;height:0;padding-bottom:37.5%}.ct-major-eleventh:after{display:table}.ct-major-eleventh>svg{display:block;position:absolute;top:0;left:0}.ct-major-twelfth{display:block;position:relative;width:100%}.ct-major-twelfth:before{display:block;float:left;content:"";width:0;height:0;padding-bottom:33.3333333333%}.ct-major-twelfth:after{display:table}.ct-major-twelfth>svg{display:block;position:absolute;top:0;left:0}.ct-double-octave{display:block;position:relative;width:100%}.ct-double-octave:before{display:block;float:left;content:"";width:0;height:0;padding-bottom:25%}.ct-double-octave:after{display:table}.ct-double-octave>svg{display:block;position:absolute;top:0;left:0}
1
+ .ct-double-octave:after,.ct-golden-section:after,.ct-major-eleventh:after,.ct-major-second:after,.ct-major-seventh:after,.ct-major-sixth:after,.ct-major-tenth:after,.ct-major-third:after,.ct-major-twelfth:after,.ct-minor-second:after,.ct-minor-seventh:after,.ct-minor-sixth:after,.ct-minor-third:after,.ct-octave:after,.ct-perfect-fifth:after,.ct-perfect-fourth:after,.ct-square:after{content:"";clear:both}.ct-label{fill:rgba(0,0,0,.4);color:rgba(0,0,0,.4);font-size:.75rem;line-height:1}.ct-chart-bar .ct-label,.ct-chart-line .ct-label{display:block;display:-webkit-box;display:-moz-box;display:-ms-flexbox;display:-webkit-flex;display:flex}.ct-chart-donut .ct-label,.ct-chart-pie .ct-label{dominant-baseline:central}.ct-label.ct-horizontal.ct-start{-webkit-box-align:flex-end;-webkit-align-items:flex-end;-ms-flex-align:flex-end;align-items:flex-end;-webkit-box-pack:flex-start;-webkit-justify-content:flex-start;-ms-flex-pack:flex-start;justify-content:flex-start;text-align:left;text-anchor:start}.ct-label.ct-horizontal.ct-end{-webkit-box-align:flex-start;-webkit-align-items:flex-start;-ms-flex-align:flex-start;align-items:flex-start;-webkit-box-pack:flex-start;-webkit-justify-content:flex-start;-ms-flex-pack:flex-start;justify-content:flex-start;text-align:left;text-anchor:start}.ct-label.ct-vertical.ct-start{-webkit-box-align:flex-end;-webkit-align-items:flex-end;-ms-flex-align:flex-end;align-items:flex-end;-webkit-box-pack:flex-end;-webkit-justify-content:flex-end;-ms-flex-pack:flex-end;justify-content:flex-end;text-align:right;text-anchor:end}.ct-label.ct-vertical.ct-end{-webkit-box-align:flex-end;-webkit-align-items:flex-end;-ms-flex-align:flex-end;align-items:flex-end;-webkit-box-pack:flex-start;-webkit-justify-content:flex-start;-ms-flex-pack:flex-start;justify-content:flex-start;text-align:left;text-anchor:start}.ct-chart-bar .ct-label.ct-horizontal.ct-start{-webkit-box-align:flex-end;-webkit-align-items:flex-end;-ms-flex-align:flex-end;align-items:flex-end;-webkit-box-pack:center;-webkit-justify-content:center;-ms-flex-pack:center;justify-content:center;text-align:center;text-anchor:start}.ct-chart-bar .ct-label.ct-horizontal.ct-end{-webkit-box-align:flex-start;-webkit-align-items:flex-start;-ms-flex-align:flex-start;align-items:flex-start;-webkit-box-pack:center;-webkit-justify-content:center;-ms-flex-pack:center;justify-content:center;text-align:center;text-anchor:start}.ct-chart-bar.ct-horizontal-bars .ct-label.ct-horizontal.ct-start{-webkit-box-align:flex-end;-webkit-align-items:flex-end;-ms-flex-align:flex-end;align-items:flex-end;-webkit-box-pack:flex-start;-webkit-justify-content:flex-start;-ms-flex-pack:flex-start;justify-content:flex-start;text-align:left;text-anchor:start}.ct-chart-bar.ct-horizontal-bars .ct-label.ct-horizontal.ct-end{-webkit-box-align:flex-start;-webkit-align-items:flex-start;-ms-flex-align:flex-start;align-items:flex-start;-webkit-box-pack:flex-start;-webkit-justify-content:flex-start;-ms-flex-pack:flex-start;justify-content:flex-start;text-align:left;text-anchor:start}.ct-chart-bar.ct-horizontal-bars .ct-label.ct-vertical.ct-start{-webkit-box-align:center;-webkit-align-items:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:flex-end;-webkit-justify-content:flex-end;-ms-flex-pack:flex-end;justify-content:flex-end;text-align:right;text-anchor:end}.ct-chart-bar.ct-horizontal-bars .ct-label.ct-vertical.ct-end{-webkit-box-align:center;-webkit-align-items:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:flex-start;-webkit-justify-content:flex-start;-ms-flex-pack:flex-start;justify-content:flex-start;text-align:left;text-anchor:end}.ct-grid{stroke:rgba(0,0,0,.2);stroke-width:1px;stroke-dasharray:2px}.ct-grid-background{fill:none}.ct-point{stroke-width:10px;stroke-linecap:round}.ct-line{fill:none;stroke-width:4px}.ct-area{stroke:none;fill-opacity:.1}.ct-bar{fill:none;stroke-width:10px}.ct-slice-donut{fill:none;stroke-width:60px}.ct-series-a .ct-bar,.ct-series-a .ct-line,.ct-series-a .ct-point,.ct-series-a .ct-slice-donut{stroke:#d70206}.ct-series-a .ct-area,.ct-series-a .ct-slice-donut-solid,.ct-series-a .ct-slice-pie{fill:#d70206}.ct-series-b .ct-bar,.ct-series-b .ct-line,.ct-series-b .ct-point,.ct-series-b .ct-slice-donut{stroke:#f05b4f}.ct-series-b .ct-area,.ct-series-b .ct-slice-donut-solid,.ct-series-b .ct-slice-pie{fill:#f05b4f}.ct-series-c .ct-bar,.ct-series-c .ct-line,.ct-series-c .ct-point,.ct-series-c .ct-slice-donut{stroke:#f4c63d}.ct-series-c .ct-area,.ct-series-c .ct-slice-donut-solid,.ct-series-c .ct-slice-pie{fill:#f4c63d}.ct-series-d .ct-bar,.ct-series-d .ct-line,.ct-series-d .ct-point,.ct-series-d .ct-slice-donut{stroke:#d17905}.ct-series-d .ct-area,.ct-series-d .ct-slice-donut-solid,.ct-series-d .ct-slice-pie{fill:#d17905}.ct-series-e .ct-bar,.ct-series-e .ct-line,.ct-series-e .ct-point,.ct-series-e .ct-slice-donut{stroke:#453d3f}.ct-series-e .ct-area,.ct-series-e .ct-slice-donut-solid,.ct-series-e .ct-slice-pie{fill:#453d3f}.ct-series-f .ct-bar,.ct-series-f .ct-line,.ct-series-f .ct-point,.ct-series-f .ct-slice-donut{stroke:#59922b}.ct-series-f .ct-area,.ct-series-f .ct-slice-donut-solid,.ct-series-f .ct-slice-pie{fill:#59922b}.ct-series-g .ct-bar,.ct-series-g .ct-line,.ct-series-g .ct-point,.ct-series-g .ct-slice-donut{stroke:#0544d3}.ct-series-g .ct-area,.ct-series-g .ct-slice-donut-solid,.ct-series-g .ct-slice-pie{fill:#0544d3}.ct-series-h .ct-bar,.ct-series-h .ct-line,.ct-series-h .ct-point,.ct-series-h .ct-slice-donut{stroke:#6b0392}.ct-series-h .ct-area,.ct-series-h .ct-slice-donut-solid,.ct-series-h .ct-slice-pie{fill:#6b0392}.ct-series-i .ct-bar,.ct-series-i .ct-line,.ct-series-i .ct-point,.ct-series-i .ct-slice-donut{stroke:#f05b4f}.ct-series-i .ct-area,.ct-series-i .ct-slice-donut-solid,.ct-series-i .ct-slice-pie{fill:#f05b4f}.ct-series-j .ct-bar,.ct-series-j .ct-line,.ct-series-j .ct-point,.ct-series-j .ct-slice-donut{stroke:#dda458}.ct-series-j .ct-area,.ct-series-j .ct-slice-donut-solid,.ct-series-j .ct-slice-pie{fill:#dda458}.ct-series-k .ct-bar,.ct-series-k .ct-line,.ct-series-k .ct-point,.ct-series-k .ct-slice-donut{stroke:#eacf7d}.ct-series-k .ct-area,.ct-series-k .ct-slice-donut-solid,.ct-series-k .ct-slice-pie{fill:#eacf7d}.ct-series-l .ct-bar,.ct-series-l .ct-line,.ct-series-l .ct-point,.ct-series-l .ct-slice-donut{stroke:#86797d}.ct-series-l .ct-area,.ct-series-l .ct-slice-donut-solid,.ct-series-l .ct-slice-pie{fill:#86797d}.ct-series-m .ct-bar,.ct-series-m .ct-line,.ct-series-m .ct-point,.ct-series-m .ct-slice-donut{stroke:#b2c326}.ct-series-m .ct-area,.ct-series-m .ct-slice-donut-solid,.ct-series-m .ct-slice-pie{fill:#b2c326}.ct-series-n .ct-bar,.ct-series-n .ct-line,.ct-series-n .ct-point,.ct-series-n .ct-slice-donut{stroke:#6188e2}.ct-series-n .ct-area,.ct-series-n .ct-slice-donut-solid,.ct-series-n .ct-slice-pie{fill:#6188e2}.ct-series-o .ct-bar,.ct-series-o .ct-line,.ct-series-o .ct-point,.ct-series-o .ct-slice-donut{stroke:#a748ca}.ct-series-o .ct-area,.ct-series-o .ct-slice-donut-solid,.ct-series-o .ct-slice-pie{fill:#a748ca}.ct-square{display:block;position:relative;width:100%}.ct-square:before{display:block;float:left;content:"";width:0;height:0;padding-bottom:100%}.ct-square:after{display:table}.ct-square>svg{display:block;position:absolute;top:0;left:0}.ct-minor-second{display:block;position:relative;width:100%}.ct-minor-second:before{display:block;float:left;content:"";width:0;height:0;padding-bottom:93.75%}.ct-minor-second:after{display:table}.ct-minor-second>svg{display:block;position:absolute;top:0;left:0}.ct-major-second{display:block;position:relative;width:100%}.ct-major-second:before{display:block;float:left;content:"";width:0;height:0;padding-bottom:88.8888888889%}.ct-major-second:after{display:table}.ct-major-second>svg{display:block;position:absolute;top:0;left:0}.ct-minor-third{display:block;position:relative;width:100%}.ct-minor-third:before{display:block;float:left;content:"";width:0;height:0;padding-bottom:83.3333333333%}.ct-minor-third:after{display:table}.ct-minor-third>svg{display:block;position:absolute;top:0;left:0}.ct-major-third{display:block;position:relative;width:100%}.ct-major-third:before{display:block;float:left;content:"";width:0;height:0;padding-bottom:80%}.ct-major-third:after{display:table}.ct-major-third>svg{display:block;position:absolute;top:0;left:0}.ct-perfect-fourth{display:block;position:relative;width:100%}.ct-perfect-fourth:before{display:block;float:left;content:"";width:0;height:0;padding-bottom:75%}.ct-perfect-fourth:after{display:table}.ct-perfect-fourth>svg{display:block;position:absolute;top:0;left:0}.ct-perfect-fifth{display:block;position:relative;width:100%}.ct-perfect-fifth:before{display:block;float:left;content:"";width:0;height:0;padding-bottom:66.6666666667%}.ct-perfect-fifth:after{display:table}.ct-perfect-fifth>svg{display:block;position:absolute;top:0;left:0}.ct-minor-sixth{display:block;position:relative;width:100%}.ct-minor-sixth:before{display:block;float:left;content:"";width:0;height:0;padding-bottom:62.5%}.ct-minor-sixth:after{display:table}.ct-minor-sixth>svg{display:block;position:absolute;top:0;left:0}.ct-golden-section{display:block;position:relative;width:100%}.ct-golden-section:before{display:block;float:left;content:"";width:0;height:0;padding-bottom:61.804697157%}.ct-golden-section:after{display:table}.ct-golden-section>svg{display:block;position:absolute;top:0;left:0}.ct-major-sixth{display:block;position:relative;width:100%}.ct-major-sixth:before{display:block;float:left;content:"";width:0;height:0;padding-bottom:60%}.ct-major-sixth:after{display:table}.ct-major-sixth>svg{display:block;position:absolute;top:0;left:0}.ct-minor-seventh{display:block;position:relative;width:100%}.ct-minor-seventh:before{display:block;float:left;content:"";width:0;height:0;padding-bottom:56.25%}.ct-minor-seventh:after{display:table}.ct-minor-seventh>svg{display:block;position:absolute;top:0;left:0}.ct-major-seventh{display:block;position:relative;width:100%}.ct-major-seventh:before{display:block;float:left;content:"";width:0;height:0;padding-bottom:53.3333333333%}.ct-major-seventh:after{display:table}.ct-major-seventh>svg{display:block;position:absolute;top:0;left:0}.ct-octave{display:block;position:relative;width:100%}.ct-octave:before{display:block;float:left;content:"";width:0;height:0;padding-bottom:50%}.ct-octave:after{display:table}.ct-octave>svg{display:block;position:absolute;top:0;left:0}.ct-major-tenth{display:block;position:relative;width:100%}.ct-major-tenth:before{display:block;float:left;content:"";width:0;height:0;padding-bottom:40%}.ct-major-tenth:after{display:table}.ct-major-tenth>svg{display:block;position:absolute;top:0;left:0}.ct-major-eleventh{display:block;position:relative;width:100%}.ct-major-eleventh:before{display:block;float:left;content:"";width:0;height:0;padding-bottom:37.5%}.ct-major-eleventh:after{display:table}.ct-major-eleventh>svg{display:block;position:absolute;top:0;left:0}.ct-major-twelfth{display:block;position:relative;width:100%}.ct-major-twelfth:before{display:block;float:left;content:"";width:0;height:0;padding-bottom:33.3333333333%}.ct-major-twelfth:after{display:table}.ct-major-twelfth>svg{display:block;position:absolute;top:0;left:0}.ct-double-octave{display:block;position:relative;width:100%}.ct-double-octave:before{display:block;float:left;content:"";width:0;height:0;padding-bottom:25%}.ct-double-octave:after{display:table}.ct-double-octave>svg{display:block;position:absolute;top:0;left:0}
resources/css/plugin.css CHANGED
@@ -934,14 +934,13 @@ td.cell_delete_note {
934
  }
935
 
936
  #SectionStats {
937
- max-height: 300px;
938
  overflow: auto;
939
  }
940
  .stat.card {
941
  }
942
  .stat.card .card-header {
943
  font-size: smaller;
944
- white-space: nowrap;
945
  }
946
  .stat.card .card-body {
947
  font-size: x-large;
934
  }
935
 
936
  #SectionStats {
 
937
  overflow: auto;
938
  }
939
  .stat.card {
940
  }
941
  .stat.card .card-header {
942
  font-size: smaller;
943
+ /*white-space: nowrap;*/
944
  }
945
  .stat.card .card-body {
946
  font-size: x-large;
resources/js/chartist.min.js CHANGED
@@ -1,10 +1,10 @@
1
- /* Chartist.js 0.11.3
2
  * Copyright © 2019 Gion Kunz
3
  * Free to use under either the WTFPL license or the MIT license.
4
  * https://raw.githubusercontent.com/gionkunz/chartist-js/master/LICENSE-WTFPL
5
  * https://raw.githubusercontent.com/gionkunz/chartist-js/master/LICENSE-MIT
6
  */
7
 
8
- !function(a,b){"function"==typeof define&&define.amd?define("Chartist",[],function(){return a.Chartist=b()}):"object"==typeof module&&module.exports?module.exports=b():a.Chartist=b()}(this,function(){var a={version:"0.11.3"};return function(a,b){"use strict";var c=a.window,d=a.document;b.namespaces={svg:"http://www.w3.org/2000/svg",xmlns:"http://www.w3.org/2000/xmlns/",xhtml:"http://www.w3.org/1999/xhtml",xlink:"http://www.w3.org/1999/xlink",ct:"http://gionkunz.github.com/chartist-js/ct"},b.noop=function(a){return a},b.alphaNumerate=function(a){return String.fromCharCode(97+a%26)},b.extend=function(a){var c,d,e;for(a=a||{},c=1;c<arguments.length;c++){d=arguments[c];for(var f in d)e=d[f],"object"!=typeof e||null===e||e instanceof Array?a[f]=e:a[f]=b.extend(a[f],e)}return a},b.replaceAll=function(a,b,c){return a.replace(new RegExp(b,"g"),c)},b.ensureUnit=function(a,b){return"number"==typeof a&&(a+=b),a},b.quantity=function(a){if("string"==typeof a){var b=/^(\d+)\s*(.*)$/g.exec(a);return{value:+b[1],unit:b[2]||void 0}}return{value:a}},b.querySelector=function(a){return a instanceof Node?a:d.querySelector(a)},b.times=function(a){return Array.apply(null,new Array(a))},b.sum=function(a,b){return a+(b?b:0)},b.mapMultiply=function(a){return function(b){return b*a}},b.mapAdd=function(a){return function(b){return b+a}},b.serialMap=function(a,c){var d=[],e=Math.max.apply(null,a.map(function(a){return a.length}));return b.times(e).forEach(function(b,e){var f=a.map(function(a){return a[e]});d[e]=c.apply(null,f)}),d},b.roundWithPrecision=function(a,c){var d=Math.pow(10,c||b.precision);return Math.round(a*d)/d},b.precision=8,b.escapingMap={"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;","'":"&#039;"},b.serialize=function(a){return null===a||void 0===a?a:("number"==typeof a?a=""+a:"object"==typeof a&&(a=JSON.stringify({data:a})),Object.keys(b.escapingMap).reduce(function(a,c){return b.replaceAll(a,c,b.escapingMap[c])},a))},b.deserialize=function(a){if("string"!=typeof a)return a;a=Object.keys(b.escapingMap).reduce(function(a,c){return b.replaceAll(a,b.escapingMap[c],c)},a);try{a=JSON.parse(a),a=void 0!==a.data?a.data:a}catch(c){}return a},b.createSvg=function(a,c,d,e){var f;return c=c||"100%",d=d||"100%",Array.prototype.slice.call(a.querySelectorAll("svg")).filter(function(a){return a.getAttributeNS(b.namespaces.xmlns,"ct")}).forEach(function(b){a.removeChild(b)}),f=new b.Svg("svg").attr({width:c,height:d}).addClass(e),f._node.style.width=c,f._node.style.height=d,a.appendChild(f._node),f},b.normalizeData=function(a,c,d){var e,f={raw:a,normalized:{}};return f.normalized.series=b.getDataArray({series:a.series||[]},c,d),e=f.normalized.series.every(function(a){return a instanceof Array})?Math.max.apply(null,f.normalized.series.map(function(a){return a.length})):f.normalized.series.length,f.normalized.labels=(a.labels||[]).slice(),Array.prototype.push.apply(f.normalized.labels,b.times(Math.max(0,e-f.normalized.labels.length)).map(function(){return""})),c&&b.reverseData(f.normalized),f},b.safeHasProperty=function(a,b){return null!==a&&"object"==typeof a&&a.hasOwnProperty(b)},b.isDataHoleValue=function(a){return null===a||void 0===a||"number"==typeof a&&isNaN(a)},b.reverseData=function(a){a.labels.reverse(),a.series.reverse();for(var b=0;b<a.series.length;b++)"object"==typeof a.series[b]&&void 0!==a.series[b].data?a.series[b].data.reverse():a.series[b]instanceof Array&&a.series[b].reverse()},b.getDataArray=function(a,c,d){function e(a){if(b.safeHasProperty(a,"value"))return e(a.value);if(b.safeHasProperty(a,"data"))return e(a.data);if(a instanceof Array)return a.map(e);if(!b.isDataHoleValue(a)){if(d){var c={};return"string"==typeof d?c[d]=b.getNumberOrUndefined(a):c.y=b.getNumberOrUndefined(a),c.x=a.hasOwnProperty("x")?b.getNumberOrUndefined(a.x):c.x,c.y=a.hasOwnProperty("y")?b.getNumberOrUndefined(a.y):c.y,c}return b.getNumberOrUndefined(a)}}return a.series.map(e)},b.normalizePadding=function(a,b){return b=b||0,"number"==typeof a?{top:a,right:a,bottom:a,left:a}:{top:"number"==typeof a.top?a.top:b,right:"number"==typeof a.right?a.right:b,bottom:"number"==typeof a.bottom?a.bottom:b,left:"number"==typeof a.left?a.left:b}},b.getMetaData=function(a,b){var c=a.data?a.data[b]:a[b];return c?c.meta:void 0},b.orderOfMagnitude=function(a){return Math.floor(Math.log(Math.abs(a))/Math.LN10)},b.projectLength=function(a,b,c){return b/c.range*a},b.getAvailableHeight=function(a,c){return Math.max((b.quantity(c.height).value||a.height())-(c.chartPadding.top+c.chartPadding.bottom)-c.axisX.offset,0)},b.getHighLow=function(a,c,d){function e(a){if(void 0!==a)if(a instanceof Array)for(var b=0;b<a.length;b++)e(a[b]);else{var c=d?+a[d]:+a;g&&c>f.high&&(f.high=c),h&&c<f.low&&(f.low=c)}}c=b.extend({},c,d?c["axis"+d.toUpperCase()]:{});var f={high:void 0===c.high?-Number.MAX_VALUE:+c.high,low:void 0===c.low?Number.MAX_VALUE:+c.low},g=void 0===c.high,h=void 0===c.low;return(g||h)&&e(a),(c.referenceValue||0===c.referenceValue)&&(f.high=Math.max(c.referenceValue,f.high),f.low=Math.min(c.referenceValue,f.low)),f.high<=f.low&&(0===f.low?f.high=1:f.low<0?f.high=0:f.high>0?f.low=0:(f.high=1,f.low=0)),f},b.isNumeric=function(a){return null!==a&&isFinite(a)},b.isFalseyButZero=function(a){return!a&&0!==a},b.getNumberOrUndefined=function(a){return b.isNumeric(a)?+a:void 0},b.isMultiValue=function(a){return"object"==typeof a&&("x"in a||"y"in a)},b.getMultiValue=function(a,c){return b.isMultiValue(a)?b.getNumberOrUndefined(a[c||"y"]):b.getNumberOrUndefined(a)},b.rho=function(a){function b(a,c){return a%c===0?c:b(c,a%c)}function c(a){return a*a+1}if(1===a)return a;var d,e=2,f=2;if(a%2===0)return 2;do e=c(e)%a,f=c(c(f))%a,d=b(Math.abs(e-f),a);while(1===d);return d},b.getBounds=function(a,c,d,e){function f(a,b){return a===(a+=b)&&(a*=1+(b>0?o:-o)),a}var g,h,i,j=0,k={high:c.high,low:c.low};k.valueRange=k.high-k.low,k.oom=b.orderOfMagnitude(k.valueRange),k.step=Math.pow(10,k.oom),k.min=Math.floor(k.low/k.step)*k.step,k.max=Math.ceil(k.high/k.step)*k.step,k.range=k.max-k.min,k.numberOfSteps=Math.round(k.range/k.step);var l=b.projectLength(a,k.step,k),m=l<d,n=e?b.rho(k.range):0;if(e&&b.projectLength(a,1,k)>=d)k.step=1;else if(e&&n<k.step&&b.projectLength(a,n,k)>=d)k.step=n;else for(;;){if(m&&b.projectLength(a,k.step,k)<=d)k.step*=2;else{if(m||!(b.projectLength(a,k.step/2,k)>=d))break;if(k.step/=2,e&&k.step%1!==0){k.step*=2;break}}if(j++>1e3)throw new Error("Exceeded maximum number of iterations while optimizing scale step!")}var o=2.221e-16;for(k.step=Math.max(k.step,o),h=k.min,i=k.max;h+k.step<=k.low;)h=f(h,k.step);for(;i-k.step>=k.high;)i=f(i,-k.step);k.min=h,k.max=i,k.range=k.max-k.min;var p=[];for(g=k.min;g<=k.max;g=f(g,k.step)){var q=b.roundWithPrecision(g);q!==p[p.length-1]&&p.push(q)}return k.values=p,k},b.polarToCartesian=function(a,b,c,d){var e=(d-90)*Math.PI/180;return{x:a+c*Math.cos(e),y:b+c*Math.sin(e)}},b.createChartRect=function(a,c,d){var e=!(!c.axisX&&!c.axisY),f=e?c.axisY.offset:0,g=e?c.axisX.offset:0,h=a.width()||b.quantity(c.width).value||0,i=a.height()||b.quantity(c.height).value||0,j=b.normalizePadding(c.chartPadding,d);h=Math.max(h,f+j.left+j.right),i=Math.max(i,g+j.top+j.bottom);var k={padding:j,width:function(){return this.x2-this.x1},height:function(){return this.y1-this.y2}};return e?("start"===c.axisX.position?(k.y2=j.top+g,k.y1=Math.max(i-j.bottom,k.y2+1)):(k.y2=j.top,k.y1=Math.max(i-j.bottom-g,k.y2+1)),"start"===c.axisY.position?(k.x1=j.left+f,k.x2=Math.max(h-j.right,k.x1+1)):(k.x1=j.left,k.x2=Math.max(h-j.right-f,k.x1+1))):(k.x1=j.left,k.x2=Math.max(h-j.right,k.x1+1),k.y2=j.top,k.y1=Math.max(i-j.bottom,k.y2+1)),k},b.createGrid=function(a,c,d,e,f,g,h,i){var j={};j[d.units.pos+"1"]=a,j[d.units.pos+"2"]=a,j[d.counterUnits.pos+"1"]=e,j[d.counterUnits.pos+"2"]=e+f;var k=g.elem("line",j,h.join(" "));i.emit("draw",b.extend({type:"grid",axis:d,index:c,group:g,element:k},j))},b.createGridBackground=function(a,b,c,d){var e=a.elem("rect",{x:b.x1,y:b.y2,width:b.width(),height:b.height()},c,!0);d.emit("draw",{type:"gridBackground",group:a,element:e})},b.createLabel=function(a,c,e,f,g,h,i,j,k,l,m){var n,o={};if(o[g.units.pos]=a+i[g.units.pos],o[g.counterUnits.pos]=i[g.counterUnits.pos],o[g.units.len]=c,o[g.counterUnits.len]=Math.max(0,h-10),l){var p=d.createElement("span");p.className=k.join(" "),p.setAttribute("xmlns",b.namespaces.xhtml),p.innerText=f[e],p.style[g.units.len]=Math.round(o[g.units.len])+"px",p.style[g.counterUnits.len]=Math.round(o[g.counterUnits.len])+"px",n=j.foreignObject(p,b.extend({style:"overflow: visible;"},o))}else n=j.elem("text",o,k.join(" ")).text(f[e]);m.emit("draw",b.extend({type:"label",axis:g,index:e,group:j,element:n,text:f[e]},o))},b.getSeriesOption=function(a,b,c){if(a.name&&b.series&&b.series[a.name]){var d=b.series[a.name];return d.hasOwnProperty(c)?d[c]:b[c]}return b[c]},b.optionsProvider=function(a,d,e){function f(a){var f=h;if(h=b.extend({},j),d)for(i=0;i<d.length;i++){var g=c.matchMedia(d[i][0]);g.matches&&(h=b.extend(h,d[i][1]))}e&&a&&e.emit("optionsChanged",{previousOptions:f,currentOptions:h})}function g(){k.forEach(function(a){a.removeListener(f)})}var h,i,j=b.extend({},a),k=[];if(!c.matchMedia)throw"window.matchMedia not found! Make sure you're using a polyfill.";if(d)for(i=0;i<d.length;i++){var l=c.matchMedia(d[i][0]);l.addListener(f),k.push(l)}return f(),{removeMediaQueryListeners:g,getCurrentOptions:function(){return b.extend({},h)}}},b.splitIntoSegments=function(a,c,d){var e={increasingX:!1,fillHoles:!1};d=b.extend({},e,d);for(var f=[],g=!0,h=0;h<a.length;h+=2)void 0===b.getMultiValue(c[h/2].value)?d.fillHoles||(g=!0):(d.increasingX&&h>=2&&a[h]<=a[h-2]&&(g=!0),g&&(f.push({pathCoordinates:[],valueData:[]}),g=!1),f[f.length-1].pathCoordinates.push(a[h],a[h+1]),f[f.length-1].valueData.push(c[h/2]));return f}}(this,a),function(a,b){"use strict";b.Interpolation={},b.Interpolation.none=function(a){var c={fillHoles:!1};return a=b.extend({},c,a),function(c,d){for(var e=new b.Svg.Path,f=!0,g=0;g<c.length;g+=2){var h=c[g],i=c[g+1],j=d[g/2];void 0!==b.getMultiValue(j.value)?(f?e.move(h,i,!1,j):e.line(h,i,!1,j),f=!1):a.fillHoles||(f=!0)}return e}},b.Interpolation.simple=function(a){var c={divisor:2,fillHoles:!1};a=b.extend({},c,a);var d=1/Math.max(1,a.divisor);return function(c,e){for(var f,g,h,i=new b.Svg.Path,j=0;j<c.length;j+=2){var k=c[j],l=c[j+1],m=(k-f)*d,n=e[j/2];void 0!==n.value?(void 0===h?i.move(k,l,!1,n):i.curve(f+m,g,k-m,l,k,l,!1,n),f=k,g=l,h=n):a.fillHoles||(f=k=h=void 0)}return i}},b.Interpolation.cardinal=function(a){var c={tension:1,fillHoles:!1};a=b.extend({},c,a);var d=Math.min(1,Math.max(0,a.tension)),e=1-d;return function f(c,g){var h=b.splitIntoSegments(c,g,{fillHoles:a.fillHoles});if(h.length){if(h.length>1){var i=[];return h.forEach(function(a){i.push(f(a.pathCoordinates,a.valueData))}),b.Svg.Path.join(i)}if(c=h[0].pathCoordinates,g=h[0].valueData,c.length<=4)return b.Interpolation.none()(c,g);for(var j,k=(new b.Svg.Path).move(c[0],c[1],!1,g[0]),l=0,m=c.length;m-2*!j>l;l+=2){var n=[{x:+c[l-2],y:+c[l-1]},{x:+c[l],y:+c[l+1]},{x:+c[l+2],y:+c[l+3]},{x:+c[l+4],y:+c[l+5]}];j?l?m-4===l?n[3]={x:+c[0],y:+c[1]}:m-2===l&&(n[2]={x:+c[0],y:+c[1]},n[3]={x:+c[2],y:+c[3]}):n[0]={x:+c[m-2],y:+c[m-1]}:m-4===l?n[3]=n[2]:l||(n[0]={x:+c[l],y:+c[l+1]}),k.curve(d*(-n[0].x+6*n[1].x+n[2].x)/6+e*n[2].x,d*(-n[0].y+6*n[1].y+n[2].y)/6+e*n[2].y,d*(n[1].x+6*n[2].x-n[3].x)/6+e*n[2].x,d*(n[1].y+6*n[2].y-n[3].y)/6+e*n[2].y,n[2].x,n[2].y,!1,g[(l+2)/2])}return k}return b.Interpolation.none()([])}},b.Interpolation.monotoneCubic=function(a){var c={fillHoles:!1};return a=b.extend({},c,a),function d(c,e){var f=b.splitIntoSegments(c,e,{fillHoles:a.fillHoles,increasingX:!0});if(f.length){if(f.length>1){var g=[];return f.forEach(function(a){g.push(d(a.pathCoordinates,a.valueData))}),b.Svg.Path.join(g)}if(c=f[0].pathCoordinates,e=f[0].valueData,c.length<=4)return b.Interpolation.none()(c,e);var h,i,j=[],k=[],l=c.length/2,m=[],n=[],o=[],p=[];for(h=0;h<l;h++)j[h]=c[2*h],k[h]=c[2*h+1];for(h=0;h<l-1;h++)o[h]=k[h+1]-k[h],p[h]=j[h+1]-j[h],n[h]=o[h]/p[h];for(m[0]=n[0],m[l-1]=n[l-2],h=1;h<l-1;h++)0===n[h]||0===n[h-1]||n[h-1]>0!=n[h]>0?m[h]=0:(m[h]=3*(p[h-1]+p[h])/((2*p[h]+p[h-1])/n[h-1]+(p[h]+2*p[h-1])/n[h]),isFinite(m[h])||(m[h]=0));for(i=(new b.Svg.Path).move(j[0],k[0],!1,e[0]),h=0;h<l-1;h++)i.curve(j[h]+p[h]/3,k[h]+m[h]*p[h]/3,j[h+1]-p[h]/3,k[h+1]-m[h+1]*p[h]/3,j[h+1],k[h+1],!1,e[h+1]);return i}return b.Interpolation.none()([])}},b.Interpolation.step=function(a){var c={postpone:!0,fillHoles:!1};return a=b.extend({},c,a),function(c,d){for(var e,f,g,h=new b.Svg.Path,i=0;i<c.length;i+=2){var j=c[i],k=c[i+1],l=d[i/2];void 0!==l.value?(void 0===g?h.move(j,k,!1,l):(a.postpone?h.line(j,f,!1,g):h.line(e,k,!1,l),h.line(j,k,!1,l)),e=j,f=k,g=l):a.fillHoles||(e=f=g=void 0)}return h}}}(this,a),function(a,b){"use strict";b.EventEmitter=function(){function a(a,b){d[a]=d[a]||[],d[a].push(b)}function b(a,b){d[a]&&(b?(d[a].splice(d[a].indexOf(b),1),0===d[a].length&&delete d[a]):delete d[a])}function c(a,b){d[a]&&d[a].forEach(function(a){a(b)}),d["*"]&&d["*"].forEach(function(c){c(a,b)})}var d=[];return{addEventHandler:a,removeEventHandler:b,emit:c}}}(this,a),function(a,b){"use strict";function c(a){var b=[];if(a.length)for(var c=0;c<a.length;c++)b.push(a[c]);return b}function d(a,c){var d=c||this.prototype||b.Class,e=Object.create(d);b.Class.cloneDefinitions(e,a);var f=function(){var a,c=e.constructor||function(){};return a=this===b?Object.create(e):this,c.apply(a,Array.prototype.slice.call(arguments,0)),a};return f.prototype=e,f["super"]=d,f.extend=this.extend,f}function e(){var a=c(arguments),b=a[0];return a.splice(1,a.length-1).forEach(function(a){Object.getOwnPropertyNames(a).forEach(function(c){delete b[c],Object.defineProperty(b,c,Object.getOwnPropertyDescriptor(a,c))})}),b}b.Class={extend:d,cloneDefinitions:e}}(this,a),function(a,b){"use strict";function c(a,c,d){return a&&(this.data=a||{},this.data.labels=this.data.labels||[],this.data.series=this.data.series||[],this.eventEmitter.emit("data",{type:"update",data:this.data})),c&&(this.options=b.extend({},d?this.options:this.defaultOptions,c),this.initializeTimeoutId||(this.optionsProvider.removeMediaQueryListeners(),this.optionsProvider=b.optionsProvider(this.options,this.responsiveOptions,this.eventEmitter))),this.initializeTimeoutId||this.createChart(this.optionsProvider.getCurrentOptions()),this}function d(){return this.initializeTimeoutId?i.clearTimeout(this.initializeTimeoutId):(i.removeEventListener("resize",this.resizeListener),this.optionsProvider.removeMediaQueryListeners()),this}function e(a,b){return this.eventEmitter.addEventHandler(a,b),this}function f(a,b){return this.eventEmitter.removeEventHandler(a,b),this}function g(){i.addEventListener("resize",this.resizeListener),this.optionsProvider=b.optionsProvider(this.options,this.responsiveOptions,this.eventEmitter),this.eventEmitter.addEventHandler("optionsChanged",function(){this.update()}.bind(this)),this.options.plugins&&this.options.plugins.forEach(function(a){a instanceof Array?a[0](this,a[1]):a(this)}.bind(this)),this.eventEmitter.emit("data",{type:"initial",data:this.data}),this.createChart(this.optionsProvider.getCurrentOptions()),this.initializeTimeoutId=void 0}function h(a,c,d,e,f){this.container=b.querySelector(a),this.data=c||{},this.data.labels=this.data.labels||[],this.data.series=this.data.series||[],this.defaultOptions=d,this.options=e,this.responsiveOptions=f,this.eventEmitter=b.EventEmitter(),this.supportsForeignObject=b.Svg.isSupported("Extensibility"),this.supportsAnimations=b.Svg.isSupported("AnimationEventsAttribute"),this.resizeListener=function(){this.update()}.bind(this),this.container&&(this.container.__chartist__&&this.container.__chartist__.detach(),this.container.__chartist__=this),this.initializeTimeoutId=setTimeout(g.bind(this),0)}var i=a.window;b.Base=b.Class.extend({constructor:h,optionsProvider:void 0,container:void 0,svg:void 0,eventEmitter:void 0,createChart:function(){throw new Error("Base chart type can't be instantiated!")},update:c,detach:d,on:e,off:f,version:b.version,supportsForeignObject:!1})}(this,a),function(a,b){"use strict";function c(a,c,d,e,f){a instanceof Element?this._node=a:(this._node=y.createElementNS(b.namespaces.svg,a),"svg"===a&&this.attr({"xmlns:ct":b.namespaces.ct})),c&&this.attr(c),d&&this.addClass(d),e&&(f&&e._node.firstChild?e._node.insertBefore(this._node,e._node.firstChild):e._node.appendChild(this._node))}function d(a,c){return"string"==typeof a?c?this._node.getAttributeNS(c,a):this._node.getAttribute(a):(Object.keys(a).forEach(function(c){if(void 0!==a[c])if(c.indexOf(":")!==-1){var d=c.split(":");this._node.setAttributeNS(b.namespaces[d[0]],c,a[c])}else this._node.setAttribute(c,a[c])}.bind(this)),this)}function e(a,c,d,e){return new b.Svg(a,c,d,this,e)}function f(){return this._node.parentNode instanceof SVGElement?new b.Svg(this._node.parentNode):null}function g(){for(var a=this._node;"svg"!==a.nodeName;)a=a.parentNode;return new b.Svg(a)}function h(a){var c=this._node.querySelector(a);return c?new b.Svg(c):null}function i(a){var c=this._node.querySelectorAll(a);return c.length?new b.Svg.List(c):null}function j(){return this._node}function k(a,c,d,e){if("string"==typeof a){var f=y.createElement("div");f.innerHTML=a,a=f.firstChild}a.setAttribute("xmlns",b.namespaces.xmlns);var g=this.elem("foreignObject",c,d,e);return g._node.appendChild(a),g}function l(a){return this._node.appendChild(y.createTextNode(a)),this}function m(){for(;this._node.firstChild;)this._node.removeChild(this._node.firstChild);return this}function n(){return this._node.parentNode.removeChild(this._node),this.parent()}function o(a){return this._node.parentNode.replaceChild(a._node,this._node),a}function p(a,b){return b&&this._node.firstChild?this._node.insertBefore(a._node,this._node.firstChild):this._node.appendChild(a._node),this}function q(){return this._node.getAttribute("class")?this._node.getAttribute("class").trim().split(/\s+/):[]}function r(a){return this._node.setAttribute("class",this.classes(this._node).concat(a.trim().split(/\s+/)).filter(function(a,b,c){return c.indexOf(a)===b}).join(" ")),this}function s(a){var b=a.trim().split(/\s+/);return this._node.setAttribute("class",this.classes(this._node).filter(function(a){return b.indexOf(a)===-1}).join(" ")),this}function t(){return this._node.setAttribute("class",""),this}function u(){return this._node.getBoundingClientRect().height}function v(){return this._node.getBoundingClientRect().width}function w(a,c,d){return void 0===c&&(c=!0),Object.keys(a).forEach(function(e){function f(a,c){var f,g,h,i={};a.easing&&(h=a.easing instanceof Array?a.easing:b.Svg.Easing[a.easing],delete a.easing),a.begin=b.ensureUnit(a.begin,"ms"),a.dur=b.ensureUnit(a.dur,"ms"),h&&(a.calcMode="spline",a.keySplines=h.join(" "),a.keyTimes="0;1"),c&&(a.fill="freeze",i[e]=a.from,this.attr(i),g=b.quantity(a.begin||0).value,a.begin="indefinite"),f=this.elem("animate",b.extend({attributeName:e},a)),c&&setTimeout(function(){try{f._node.beginElement()}catch(b){i[e]=a.to,this.attr(i),f.remove()}}.bind(this),g),d&&f._node.addEventListener("beginEvent",function(){d.emit("animationBegin",{element:this,animate:f._node,params:a})}.bind(this)),f._node.addEventListener("endEvent",function(){d&&d.emit("animationEnd",{element:this,animate:f._node,params:a}),c&&(i[e]=a.to,this.attr(i),f.remove())}.bind(this))}a[e]instanceof Array?a[e].forEach(function(a){f.bind(this)(a,!1)}.bind(this)):f.bind(this)(a[e],c)}.bind(this)),this}function x(a){var c=this;this.svgElements=[];for(var d=0;d<a.length;d++)this.svgElements.push(new b.Svg(a[d]));Object.keys(b.Svg.prototype).filter(function(a){return["constructor","parent","querySelector","querySelectorAll","replace","append","classes","height","width"].indexOf(a)===-1}).forEach(function(a){c[a]=function(){var d=Array.prototype.slice.call(arguments,0);return c.svgElements.forEach(function(c){b.Svg.prototype[a].apply(c,d)}),c}})}var y=a.document;b.Svg=b.Class.extend({constructor:c,attr:d,elem:e,parent:f,root:g,querySelector:h,querySelectorAll:i,getNode:j,foreignObject:k,text:l,empty:m,remove:n,replace:o,append:p,classes:q,addClass:r,removeClass:s,removeAllClasses:t,height:u,width:v,animate:w}),b.Svg.isSupported=function(a){return y.implementation.hasFeature("http://www.w3.org/TR/SVG11/feature#"+a,"1.1")};var z={easeInSine:[.47,0,.745,.715],easeOutSine:[.39,.575,.565,1],easeInOutSine:[.445,.05,.55,.95],easeInQuad:[.55,.085,.68,.53],easeOutQuad:[.25,.46,.45,.94],easeInOutQuad:[.455,.03,.515,.955],easeInCubic:[.55,.055,.675,.19],easeOutCubic:[.215,.61,.355,1],easeInOutCubic:[.645,.045,.355,1],easeInQuart:[.895,.03,.685,.22],easeOutQuart:[.165,.84,.44,1],easeInOutQuart:[.77,0,.175,1],easeInQuint:[.755,.05,.855,.06],easeOutQuint:[.23,1,.32,1],easeInOutQuint:[.86,0,.07,1],easeInExpo:[.95,.05,.795,.035],easeOutExpo:[.19,1,.22,1],easeInOutExpo:[1,0,0,1],easeInCirc:[.6,.04,.98,.335],easeOutCirc:[.075,.82,.165,1],easeInOutCirc:[.785,.135,.15,.86],easeInBack:[.6,-.28,.735,.045],easeOutBack:[.175,.885,.32,1.275],easeInOutBack:[.68,-.55,.265,1.55]};b.Svg.Easing=z,b.Svg.List=b.Class.extend({constructor:x})}(this,a),function(a,b){"use strict";function c(a,c,d,e,f,g){var h=b.extend({command:f?a.toLowerCase():a.toUpperCase()},c,g?{data:g}:{});d.splice(e,0,h)}function d(a,b){a.forEach(function(c,d){t[c.command.toLowerCase()].forEach(function(e,f){b(c,e,d,f,a)})})}function e(a,c){this.pathElements=[],this.pos=0,this.close=a,this.options=b.extend({},u,c)}function f(a){return void 0!==a?(this.pos=Math.max(0,Math.min(this.pathElements.length,a)),this):this.pos}function g(a){return this.pathElements.splice(this.pos,a),this}function h(a,b,d,e){return c("M",{x:+a,y:+b},this.pathElements,this.pos++,d,e),this}function i(a,b,d,e){return c("L",{x:+a,y:+b},this.pathElements,this.pos++,d,e),this}function j(a,b,d,e,f,g,h,i){return c("C",{x1:+a,y1:+b,x2:+d,y2:+e,x:+f,y:+g},this.pathElements,this.pos++,h,i),this}function k(a,b,d,e,f,g,h,i,j){return c("A",{rx:+a,ry:+b,xAr:+d,lAf:+e,sf:+f,x:+g,y:+h},this.pathElements,this.pos++,i,j),this}function l(a){var c=a.replace(/([A-Za-z])([0-9])/g,"$1 $2").replace(/([0-9])([A-Za-z])/g,"$1 $2").split(/[\s,]+/).reduce(function(a,b){return b.match(/[A-Za-z]/)&&a.push([]),a[a.length-1].push(b),a},[]);"Z"===c[c.length-1][0].toUpperCase()&&c.pop();var d=c.map(function(a){var c=a.shift(),d=t[c.toLowerCase()];return b.extend({command:c},d.reduce(function(b,c,d){return b[c]=+a[d],b},{}))}),e=[this.pos,0];return Array.prototype.push.apply(e,d),Array.prototype.splice.apply(this.pathElements,e),this.pos+=d.length,this}function m(){var a=Math.pow(10,this.options.accuracy);return this.pathElements.reduce(function(b,c){var d=t[c.command.toLowerCase()].map(function(b){return this.options.accuracy?Math.round(c[b]*a)/a:c[b]}.bind(this));return b+c.command+d.join(",")}.bind(this),"")+(this.close?"Z":"")}function n(a,b){return d(this.pathElements,function(c,d){c[d]*="x"===d[0]?a:b}),this}function o(a,b){return d(this.pathElements,function(c,d){c[d]+="x"===d[0]?a:b}),this}function p(a){return d(this.pathElements,function(b,c,d,e,f){var g=a(b,c,d,e,f);(g||0===g)&&(b[c]=g)}),this}function q(a){var c=new b.Svg.Path(a||this.close);return c.pos=this.pos,c.pathElements=this.pathElements.slice().map(function(a){return b.extend({},a)}),c.options=b.extend({},this.options),c}function r(a){var c=[new b.Svg.Path];return this.pathElements.forEach(function(d){d.command===a.toUpperCase()&&0!==c[c.length-1].pathElements.length&&c.push(new b.Svg.Path),c[c.length-1].pathElements.push(d)}),c}function s(a,c,d){for(var e=new b.Svg.Path(c,d),f=0;f<a.length;f++)for(var g=a[f],h=0;h<g.pathElements.length;h++)e.pathElements.push(g.pathElements[h]);return e}var t={m:["x","y"],l:["x","y"],c:["x1","y1","x2","y2","x","y"],a:["rx","ry","xAr","lAf","sf","x","y"]},u={accuracy:3};b.Svg.Path=b.Class.extend({constructor:e,position:f,remove:g,move:h,line:i,curve:j,arc:k,scale:n,translate:o,transform:p,parse:l,stringify:m,clone:q,splitByCommand:r}),b.Svg.Path.elementDescriptions=t,b.Svg.Path.join=s}(this,a),function(a,b){"use strict";function c(a,b,c,d){this.units=a,this.counterUnits=a===e.x?e.y:e.x,this.chartRect=b,this.axisLength=b[a.rectEnd]-b[a.rectStart],this.gridOffset=b[a.rectOffset],this.ticks=c,this.options=d}function d(a,c,d,e,f){var g=e["axis"+this.units.pos.toUpperCase()],h=this.ticks.map(this.projectValue.bind(this)),i=this.ticks.map(g.labelInterpolationFnc);h.forEach(function(j,k){var l,m={x:0,y:0};l=h[k+1]?h[k+1]-j:Math.max(this.axisLength-j,30),b.isFalseyButZero(i[k])&&""!==i[k]||("x"===this.units.pos?(j=this.chartRect.x1+j,m.x=e.axisX.labelOffset.x,"start"===e.axisX.position?m.y=this.chartRect.padding.top+e.axisX.labelOffset.y+(d?5:20):m.y=this.chartRect.y1+e.axisX.labelOffset.y+(d?5:20)):(j=this.chartRect.y1-j,m.y=e.axisY.labelOffset.y-(d?l:0),"start"===e.axisY.position?m.x=d?this.chartRect.padding.left+e.axisY.labelOffset.x:this.chartRect.x1-10:m.x=this.chartRect.x2+e.axisY.labelOffset.x+10),g.showGrid&&b.createGrid(j,k,this,this.gridOffset,this.chartRect[this.counterUnits.len](),a,[e.classNames.grid,e.classNames[this.units.dir]],f),g.showLabel&&b.createLabel(j,l,k,i,this,g.offset,m,c,[e.classNames.label,e.classNames[this.units.dir],"start"===g.position?e.classNames[g.position]:e.classNames.end],d,f))}.bind(this))}var e=(a.window,a.document,{x:{pos:"x",len:"width",dir:"horizontal",rectStart:"x1",rectEnd:"x2",rectOffset:"y2"},y:{pos:"y",len:"height",dir:"vertical",rectStart:"y2",rectEnd:"y1",rectOffset:"x1"}});b.Axis=b.Class.extend({constructor:c,createGridAndLabels:d,projectValue:function(a,b,c){throw new Error("Base axis can't be instantiated!")}}),b.Axis.units=e}(this,a),function(a,b){"use strict";function c(a,c,d,e){var f=e.highLow||b.getHighLow(c,e,a.pos);this.bounds=b.getBounds(d[a.rectEnd]-d[a.rectStart],f,e.scaleMinSpace||20,e.onlyInteger),this.range={min:this.bounds.min,max:this.bounds.max},b.AutoScaleAxis["super"].constructor.call(this,a,d,this.bounds.values,e)}function d(a){return this.axisLength*(+b.getMultiValue(a,this.units.pos)-this.bounds.min)/this.bounds.range}a.window,a.document;b.AutoScaleAxis=b.Axis.extend({constructor:c,projectValue:d})}(this,a),function(a,b){"use strict";function c(a,c,d,e){var f=e.highLow||b.getHighLow(c,e,a.pos);this.divisor=e.divisor||1,this.ticks=e.ticks||b.times(this.divisor).map(function(a,b){return f.low+(f.high-f.low)/this.divisor*b}.bind(this)),this.ticks.sort(function(a,b){return a-b}),this.range={min:f.low,max:f.high},b.FixedScaleAxis["super"].constructor.call(this,a,d,this.ticks,e),this.stepLength=this.axisLength/this.divisor}function d(a){return this.axisLength*(+b.getMultiValue(a,this.units.pos)-this.range.min)/(this.range.max-this.range.min)}a.window,a.document;b.FixedScaleAxis=b.Axis.extend({constructor:c,projectValue:d})}(this,a),function(a,b){"use strict";function c(a,c,d,e){b.StepAxis["super"].constructor.call(this,a,d,e.ticks,e);var f=Math.max(1,e.ticks.length-(e.stretch?1:0));this.stepLength=this.axisLength/f}function d(a,b){return this.stepLength*b}a.window,a.document;b.StepAxis=b.Axis.extend({constructor:c,projectValue:d})}(this,a),function(a,b){"use strict";function c(a){var c=b.normalizeData(this.data,a.reverseData,!0);this.svg=b.createSvg(this.container,a.width,a.height,a.classNames.chart);var d,f,g=this.svg.elem("g").addClass(a.classNames.gridGroup),h=this.svg.elem("g"),i=this.svg.elem("g").addClass(a.classNames.labelGroup),j=b.createChartRect(this.svg,a,e.padding);d=void 0===a.axisX.type?new b.StepAxis(b.Axis.units.x,c.normalized.series,j,b.extend({},a.axisX,{ticks:c.normalized.labels,stretch:a.fullWidth})):a.axisX.type.call(b,b.Axis.units.x,c.normalized.series,j,a.axisX),f=void 0===a.axisY.type?new b.AutoScaleAxis(b.Axis.units.y,c.normalized.series,j,b.extend({},a.axisY,{high:b.isNumeric(a.high)?a.high:a.axisY.high,low:b.isNumeric(a.low)?a.low:a.axisY.low})):a.axisY.type.call(b,b.Axis.units.y,c.normalized.series,j,a.axisY),d.createGridAndLabels(g,i,this.supportsForeignObject,a,this.eventEmitter),f.createGridAndLabels(g,i,this.supportsForeignObject,a,this.eventEmitter),a.showGridBackground&&b.createGridBackground(g,j,a.classNames.gridBackground,this.eventEmitter),c.raw.series.forEach(function(e,g){var i=h.elem("g");i.attr({"ct:series-name":e.name,"ct:meta":b.serialize(e.meta)}),i.addClass([a.classNames.series,e.className||a.classNames.series+"-"+b.alphaNumerate(g)].join(" "));var k=[],l=[];c.normalized.series[g].forEach(function(a,h){var i={x:j.x1+d.projectValue(a,h,c.normalized.series[g]),y:j.y1-f.projectValue(a,h,c.normalized.series[g])};k.push(i.x,i.y),l.push({value:a,valueIndex:h,meta:b.getMetaData(e,h)})}.bind(this));var m={lineSmooth:b.getSeriesOption(e,a,"lineSmooth"),showPoint:b.getSeriesOption(e,a,"showPoint"),showLine:b.getSeriesOption(e,a,"showLine"),showArea:b.getSeriesOption(e,a,"showArea"),areaBase:b.getSeriesOption(e,a,"areaBase")},n="function"==typeof m.lineSmooth?m.lineSmooth:m.lineSmooth?b.Interpolation.monotoneCubic():b.Interpolation.none(),o=n(k,l);if(m.showPoint&&o.pathElements.forEach(function(c){var h=i.elem("line",{x1:c.x,y1:c.y,x2:c.x+.01,y2:c.y},a.classNames.point).attr({"ct:value":[c.data.value.x,c.data.value.y].filter(b.isNumeric).join(","),"ct:meta":b.serialize(c.data.meta)});this.eventEmitter.emit("draw",{type:"point",value:c.data.value,index:c.data.valueIndex,meta:c.data.meta,series:e,seriesIndex:g,axisX:d,axisY:f,group:i,element:h,x:c.x,y:c.y})}.bind(this)),m.showLine){var p=i.elem("path",{d:o.stringify()},a.classNames.line,!0);this.eventEmitter.emit("draw",{type:"line",values:c.normalized.series[g],path:o.clone(),chartRect:j,index:g,series:e,seriesIndex:g,seriesMeta:e.meta,axisX:d,axisY:f,group:i,element:p})}if(m.showArea&&f.range){var q=Math.max(Math.min(m.areaBase,f.range.max),f.range.min),r=j.y1-f.projectValue(q);o.splitByCommand("M").filter(function(a){return a.pathElements.length>1}).map(function(a){var b=a.pathElements[0],c=a.pathElements[a.pathElements.length-1];return a.clone(!0).position(0).remove(1).move(b.x,r).line(b.x,b.y).position(a.pathElements.length+1).line(c.x,r)}).forEach(function(b){var h=i.elem("path",{d:b.stringify()},a.classNames.area,!0);this.eventEmitter.emit("draw",{type:"area",values:c.normalized.series[g],path:b.clone(),series:e,seriesIndex:g,axisX:d,axisY:f,chartRect:j,index:g,group:i,element:h})}.bind(this))}}.bind(this)),this.eventEmitter.emit("created",{bounds:f.bounds,chartRect:j,axisX:d,axisY:f,svg:this.svg,options:a})}function d(a,c,d,f){b.Line["super"].constructor.call(this,a,c,e,b.extend({},e,d),f)}var e=(a.window,a.document,{axisX:{offset:30,position:"end",labelOffset:{x:0,y:0},showLabel:!0,showGrid:!0,labelInterpolationFnc:b.noop,type:void 0},axisY:{offset:40,position:"start",labelOffset:{x:0,y:0},showLabel:!0,showGrid:!0,labelInterpolationFnc:b.noop,type:void 0,scaleMinSpace:20,onlyInteger:!1},width:void 0,height:void 0,showLine:!0,showPoint:!0,showArea:!1,areaBase:0,lineSmooth:!0,showGridBackground:!1,low:void 0,high:void 0,chartPadding:{top:15,right:15,bottom:5,left:10},fullWidth:!1,reverseData:!1,classNames:{chart:"ct-chart-line",label:"ct-label",labelGroup:"ct-labels",series:"ct-series",line:"ct-line",point:"ct-point",area:"ct-area",grid:"ct-grid",gridGroup:"ct-grids",gridBackground:"ct-grid-background",vertical:"ct-vertical",horizontal:"ct-horizontal",start:"ct-start",end:"ct-end"}});b.Line=b.Base.extend({constructor:d,createChart:c})}(this,a),function(a,b){"use strict";function c(a){var c,d;a.distributeSeries?(c=b.normalizeData(this.data,a.reverseData,a.horizontalBars?"x":"y"),c.normalized.series=c.normalized.series.map(function(a){return[a]})):c=b.normalizeData(this.data,a.reverseData,a.horizontalBars?"x":"y"),this.svg=b.createSvg(this.container,a.width,a.height,a.classNames.chart+(a.horizontalBars?" "+a.classNames.horizontalBars:""));var f=this.svg.elem("g").addClass(a.classNames.gridGroup),g=this.svg.elem("g"),h=this.svg.elem("g").addClass(a.classNames.labelGroup);if(a.stackBars&&0!==c.normalized.series.length){var i=b.serialMap(c.normalized.series,function(){
9
- return Array.prototype.slice.call(arguments).map(function(a){return a}).reduce(function(a,b){return{x:a.x+(b&&b.x)||0,y:a.y+(b&&b.y)||0}},{x:0,y:0})});d=b.getHighLow([i],a,a.horizontalBars?"x":"y")}else d=b.getHighLow(c.normalized.series,a,a.horizontalBars?"x":"y");d.high=+a.high||(0===a.high?0:d.high),d.low=+a.low||(0===a.low?0:d.low);var j,k,l,m,n,o=b.createChartRect(this.svg,a,e.padding);k=a.distributeSeries&&a.stackBars?c.normalized.labels.slice(0,1):c.normalized.labels,a.horizontalBars?(j=m=void 0===a.axisX.type?new b.AutoScaleAxis(b.Axis.units.x,c.normalized.series,o,b.extend({},a.axisX,{highLow:d,referenceValue:0})):a.axisX.type.call(b,b.Axis.units.x,c.normalized.series,o,b.extend({},a.axisX,{highLow:d,referenceValue:0})),l=n=void 0===a.axisY.type?new b.StepAxis(b.Axis.units.y,c.normalized.series,o,{ticks:k}):a.axisY.type.call(b,b.Axis.units.y,c.normalized.series,o,a.axisY)):(l=m=void 0===a.axisX.type?new b.StepAxis(b.Axis.units.x,c.normalized.series,o,{ticks:k}):a.axisX.type.call(b,b.Axis.units.x,c.normalized.series,o,a.axisX),j=n=void 0===a.axisY.type?new b.AutoScaleAxis(b.Axis.units.y,c.normalized.series,o,b.extend({},a.axisY,{highLow:d,referenceValue:0})):a.axisY.type.call(b,b.Axis.units.y,c.normalized.series,o,b.extend({},a.axisY,{highLow:d,referenceValue:0})));var p=a.horizontalBars?o.x1+j.projectValue(0):o.y1-j.projectValue(0),q=[];l.createGridAndLabels(f,h,this.supportsForeignObject,a,this.eventEmitter),j.createGridAndLabels(f,h,this.supportsForeignObject,a,this.eventEmitter),a.showGridBackground&&b.createGridBackground(f,o,a.classNames.gridBackground,this.eventEmitter),c.raw.series.forEach(function(d,e){var f,h,i=e-(c.raw.series.length-1)/2;f=a.distributeSeries&&!a.stackBars?l.axisLength/c.normalized.series.length/2:a.distributeSeries&&a.stackBars?l.axisLength/2:l.axisLength/c.normalized.series[e].length/2,h=g.elem("g"),h.attr({"ct:series-name":d.name,"ct:meta":b.serialize(d.meta)}),h.addClass([a.classNames.series,d.className||a.classNames.series+"-"+b.alphaNumerate(e)].join(" ")),c.normalized.series[e].forEach(function(g,k){var r,s,t,u;if(u=a.distributeSeries&&!a.stackBars?e:a.distributeSeries&&a.stackBars?0:k,r=a.horizontalBars?{x:o.x1+j.projectValue(g&&g.x?g.x:0,k,c.normalized.series[e]),y:o.y1-l.projectValue(g&&g.y?g.y:0,u,c.normalized.series[e])}:{x:o.x1+l.projectValue(g&&g.x?g.x:0,u,c.normalized.series[e]),y:o.y1-j.projectValue(g&&g.y?g.y:0,k,c.normalized.series[e])},l instanceof b.StepAxis&&(l.options.stretch||(r[l.units.pos]+=f*(a.horizontalBars?-1:1)),r[l.units.pos]+=a.stackBars||a.distributeSeries?0:i*a.seriesBarDistance*(a.horizontalBars?-1:1)),t=q[k]||p,q[k]=t-(p-r[l.counterUnits.pos]),void 0!==g){var v={};v[l.units.pos+"1"]=r[l.units.pos],v[l.units.pos+"2"]=r[l.units.pos],!a.stackBars||"accumulate"!==a.stackMode&&a.stackMode?(v[l.counterUnits.pos+"1"]=p,v[l.counterUnits.pos+"2"]=r[l.counterUnits.pos]):(v[l.counterUnits.pos+"1"]=t,v[l.counterUnits.pos+"2"]=q[k]),v.x1=Math.min(Math.max(v.x1,o.x1),o.x2),v.x2=Math.min(Math.max(v.x2,o.x1),o.x2),v.y1=Math.min(Math.max(v.y1,o.y2),o.y1),v.y2=Math.min(Math.max(v.y2,o.y2),o.y1);var w=b.getMetaData(d,k);s=h.elem("line",v,a.classNames.bar).attr({"ct:value":[g.x,g.y].filter(b.isNumeric).join(","),"ct:meta":b.serialize(w)}),this.eventEmitter.emit("draw",b.extend({type:"bar",value:g,index:k,meta:w,series:d,seriesIndex:e,axisX:m,axisY:n,chartRect:o,group:h,element:s},v))}}.bind(this))}.bind(this)),this.eventEmitter.emit("created",{bounds:j.bounds,chartRect:o,axisX:m,axisY:n,svg:this.svg,options:a})}function d(a,c,d,f){b.Bar["super"].constructor.call(this,a,c,e,b.extend({},e,d),f)}var e=(a.window,a.document,{axisX:{offset:30,position:"end",labelOffset:{x:0,y:0},showLabel:!0,showGrid:!0,labelInterpolationFnc:b.noop,scaleMinSpace:30,onlyInteger:!1},axisY:{offset:40,position:"start",labelOffset:{x:0,y:0},showLabel:!0,showGrid:!0,labelInterpolationFnc:b.noop,scaleMinSpace:20,onlyInteger:!1},width:void 0,height:void 0,high:void 0,low:void 0,referenceValue:0,chartPadding:{top:15,right:15,bottom:5,left:10},seriesBarDistance:15,stackBars:!1,stackMode:"accumulate",horizontalBars:!1,distributeSeries:!1,reverseData:!1,showGridBackground:!1,classNames:{chart:"ct-chart-bar",horizontalBars:"ct-horizontal-bars",label:"ct-label",labelGroup:"ct-labels",series:"ct-series",bar:"ct-bar",grid:"ct-grid",gridGroup:"ct-grids",gridBackground:"ct-grid-background",vertical:"ct-vertical",horizontal:"ct-horizontal",start:"ct-start",end:"ct-end"}});b.Bar=b.Base.extend({constructor:d,createChart:c})}(this,a),function(a,b){"use strict";function c(a,b,c){var d=b.x>a.x;return d&&"explode"===c||!d&&"implode"===c?"start":d&&"implode"===c||!d&&"explode"===c?"end":"middle"}function d(a){var d,e,g,h,i,j=b.normalizeData(this.data),k=[],l=a.startAngle;this.svg=b.createSvg(this.container,a.width,a.height,a.donut?a.classNames.chartDonut:a.classNames.chartPie),e=b.createChartRect(this.svg,a,f.padding),g=Math.min(e.width()/2,e.height()/2),i=a.total||j.normalized.series.reduce(function(a,b){return a+b},0);var m=b.quantity(a.donutWidth);"%"===m.unit&&(m.value*=g/100),g-=a.donut&&!a.donutSolid?m.value/2:0,h="outside"===a.labelPosition||a.donut&&!a.donutSolid?g:"center"===a.labelPosition?0:a.donutSolid?g-m.value/2:g/2,h+=a.labelOffset;var n={x:e.x1+e.width()/2,y:e.y2+e.height()/2},o=1===j.raw.series.filter(function(a){return a.hasOwnProperty("value")?0!==a.value:0!==a}).length;j.raw.series.forEach(function(a,b){k[b]=this.svg.elem("g",null,null)}.bind(this)),a.showLabel&&(d=this.svg.elem("g",null,null)),j.raw.series.forEach(function(e,f){if(0!==j.normalized.series[f]||!a.ignoreEmptyValues){k[f].attr({"ct:series-name":e.name}),k[f].addClass([a.classNames.series,e.className||a.classNames.series+"-"+b.alphaNumerate(f)].join(" "));var p=i>0?l+j.normalized.series[f]/i*360:0,q=Math.max(0,l-(0===f||o?0:.2));p-q>=359.99&&(p=q+359.99);var r,s,t,u=b.polarToCartesian(n.x,n.y,g,q),v=b.polarToCartesian(n.x,n.y,g,p),w=new b.Svg.Path(!a.donut||a.donutSolid).move(v.x,v.y).arc(g,g,0,p-l>180,0,u.x,u.y);a.donut?a.donutSolid&&(t=g-m.value,r=b.polarToCartesian(n.x,n.y,t,l-(0===f||o?0:.2)),s=b.polarToCartesian(n.x,n.y,t,p),w.line(r.x,r.y),w.arc(t,t,0,p-l>180,1,s.x,s.y)):w.line(n.x,n.y);var x=a.classNames.slicePie;a.donut&&(x=a.classNames.sliceDonut,a.donutSolid&&(x=a.classNames.sliceDonutSolid));var y=k[f].elem("path",{d:w.stringify()},x);if(y.attr({"ct:value":j.normalized.series[f],"ct:meta":b.serialize(e.meta)}),a.donut&&!a.donutSolid&&(y._node.style.strokeWidth=m.value+"px"),this.eventEmitter.emit("draw",{type:"slice",value:j.normalized.series[f],totalDataSum:i,index:f,meta:e.meta,series:e,group:k[f],element:y,path:w.clone(),center:n,radius:g,startAngle:l,endAngle:p}),a.showLabel){var z;z=1===j.raw.series.length?{x:n.x,y:n.y}:b.polarToCartesian(n.x,n.y,h,l+(p-l)/2);var A;A=j.normalized.labels&&!b.isFalseyButZero(j.normalized.labels[f])?j.normalized.labels[f]:j.normalized.series[f];var B=a.labelInterpolationFnc(A,f);if(B||0===B){var C=d.elem("text",{dx:z.x,dy:z.y,"text-anchor":c(n,z,a.labelDirection)},a.classNames.label).text(""+B);this.eventEmitter.emit("draw",{type:"label",index:f,group:d,element:C,text:""+B,x:z.x,y:z.y})}}l=p}}.bind(this)),this.eventEmitter.emit("created",{chartRect:e,svg:this.svg,options:a})}function e(a,c,d,e){b.Pie["super"].constructor.call(this,a,c,f,b.extend({},f,d),e)}var f=(a.window,a.document,{width:void 0,height:void 0,chartPadding:5,classNames:{chartPie:"ct-chart-pie",chartDonut:"ct-chart-donut",series:"ct-series",slicePie:"ct-slice-pie",sliceDonut:"ct-slice-donut",sliceDonutSolid:"ct-slice-donut-solid",label:"ct-label"},startAngle:0,total:void 0,donut:!1,donutSolid:!1,donutWidth:60,showLabel:!0,labelOffset:0,labelPosition:"inside",labelInterpolationFnc:b.noop,labelDirection:"neutral",reverseData:!1,ignoreEmptyValues:!1});b.Pie=b.Base.extend({constructor:e,createChart:d,determineAnchorPosition:c})}(this,a),a});
10
  //# sourceMappingURL=chartist.min.js.map
1
+ /* Chartist.js 0.11.4
2
  * Copyright © 2019 Gion Kunz
3
  * Free to use under either the WTFPL license or the MIT license.
4
  * https://raw.githubusercontent.com/gionkunz/chartist-js/master/LICENSE-WTFPL
5
  * https://raw.githubusercontent.com/gionkunz/chartist-js/master/LICENSE-MIT
6
  */
7
 
8
+ !function(a,b){"function"==typeof define&&define.amd?define("Chartist",[],function(){return a.Chartist=b()}):"object"==typeof module&&module.exports?module.exports=b():a.Chartist=b()}(this,function(){var a={version:"0.11.4"};return function(a,b){"use strict";var c=a.window,d=a.document;b.namespaces={svg:"http://www.w3.org/2000/svg",xmlns:"http://www.w3.org/2000/xmlns/",xhtml:"http://www.w3.org/1999/xhtml",xlink:"http://www.w3.org/1999/xlink",ct:"http://gionkunz.github.com/chartist-js/ct"},b.noop=function(a){return a},b.alphaNumerate=function(a){return String.fromCharCode(97+a%26)},b.extend=function(a){var c,d,e;for(a=a||{},c=1;c<arguments.length;c++){d=arguments[c];for(var f in d)e=d[f],"object"!=typeof e||null===e||e instanceof Array?a[f]=e:a[f]=b.extend(a[f],e)}return a},b.replaceAll=function(a,b,c){return a.replace(new RegExp(b,"g"),c)},b.ensureUnit=function(a,b){return"number"==typeof a&&(a+=b),a},b.quantity=function(a){if("string"==typeof a){var b=/^(\d+)\s*(.*)$/g.exec(a);return{value:+b[1],unit:b[2]||void 0}}return{value:a}},b.querySelector=function(a){return a instanceof Node?a:d.querySelector(a)},b.times=function(a){return Array.apply(null,new Array(a))},b.sum=function(a,b){return a+(b?b:0)},b.mapMultiply=function(a){return function(b){return b*a}},b.mapAdd=function(a){return function(b){return b+a}},b.serialMap=function(a,c){var d=[],e=Math.max.apply(null,a.map(function(a){return a.length}));return b.times(e).forEach(function(b,e){var f=a.map(function(a){return a[e]});d[e]=c.apply(null,f)}),d},b.roundWithPrecision=function(a,c){var d=Math.pow(10,c||b.precision);return Math.round(a*d)/d},b.precision=8,b.escapingMap={"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;","'":"&#039;"},b.serialize=function(a){return null===a||void 0===a?a:("number"==typeof a?a=""+a:"object"==typeof a&&(a=JSON.stringify({data:a})),Object.keys(b.escapingMap).reduce(function(a,c){return b.replaceAll(a,c,b.escapingMap[c])},a))},b.deserialize=function(a){if("string"!=typeof a)return a;a=Object.keys(b.escapingMap).reduce(function(a,c){return b.replaceAll(a,b.escapingMap[c],c)},a);try{a=JSON.parse(a),a=void 0!==a.data?a.data:a}catch(c){}return a},b.createSvg=function(a,c,d,e){var f;return c=c||"100%",d=d||"100%",Array.prototype.slice.call(a.querySelectorAll("svg")).filter(function(a){return a.getAttributeNS(b.namespaces.xmlns,"ct")}).forEach(function(b){a.removeChild(b)}),f=new b.Svg("svg").attr({width:c,height:d}).addClass(e),f._node.style.width=c,f._node.style.height=d,a.appendChild(f._node),f},b.normalizeData=function(a,c,d){var e,f={raw:a,normalized:{}};return f.normalized.series=b.getDataArray({series:a.series||[]},c,d),e=f.normalized.series.every(function(a){return a instanceof Array})?Math.max.apply(null,f.normalized.series.map(function(a){return a.length})):f.normalized.series.length,f.normalized.labels=(a.labels||[]).slice(),Array.prototype.push.apply(f.normalized.labels,b.times(Math.max(0,e-f.normalized.labels.length)).map(function(){return""})),c&&b.reverseData(f.normalized),f},b.safeHasProperty=function(a,b){return null!==a&&"object"==typeof a&&a.hasOwnProperty(b)},b.isDataHoleValue=function(a){return null===a||void 0===a||"number"==typeof a&&isNaN(a)},b.reverseData=function(a){a.labels.reverse(),a.series.reverse();for(var b=0;b<a.series.length;b++)"object"==typeof a.series[b]&&void 0!==a.series[b].data?a.series[b].data.reverse():a.series[b]instanceof Array&&a.series[b].reverse()},b.getDataArray=function(a,c,d){function e(a){if(b.safeHasProperty(a,"value"))return e(a.value);if(b.safeHasProperty(a,"data"))return e(a.data);if(a instanceof Array)return a.map(e);if(!b.isDataHoleValue(a)){if(d){var c={};return"string"==typeof d?c[d]=b.getNumberOrUndefined(a):c.y=b.getNumberOrUndefined(a),c.x=a.hasOwnProperty("x")?b.getNumberOrUndefined(a.x):c.x,c.y=a.hasOwnProperty("y")?b.getNumberOrUndefined(a.y):c.y,c}return b.getNumberOrUndefined(a)}}return a.series.map(e)},b.normalizePadding=function(a,b){return b=b||0,"number"==typeof a?{top:a,right:a,bottom:a,left:a}:{top:"number"==typeof a.top?a.top:b,right:"number"==typeof a.right?a.right:b,bottom:"number"==typeof a.bottom?a.bottom:b,left:"number"==typeof a.left?a.left:b}},b.getMetaData=function(a,b){var c=a.data?a.data[b]:a[b];return c?c.meta:void 0},b.orderOfMagnitude=function(a){return Math.floor(Math.log(Math.abs(a))/Math.LN10)},b.projectLength=function(a,b,c){return b/c.range*a},b.getAvailableHeight=function(a,c){return Math.max((b.quantity(c.height).value||a.height())-(c.chartPadding.top+c.chartPadding.bottom)-c.axisX.offset,0)},b.getHighLow=function(a,c,d){function e(a){if(void 0!==a)if(a instanceof Array)for(var b=0;b<a.length;b++)e(a[b]);else{var c=d?+a[d]:+a;g&&c>f.high&&(f.high=c),h&&c<f.low&&(f.low=c)}}c=b.extend({},c,d?c["axis"+d.toUpperCase()]:{});var f={high:void 0===c.high?-Number.MAX_VALUE:+c.high,low:void 0===c.low?Number.MAX_VALUE:+c.low},g=void 0===c.high,h=void 0===c.low;return(g||h)&&e(a),(c.referenceValue||0===c.referenceValue)&&(f.high=Math.max(c.referenceValue,f.high),f.low=Math.min(c.referenceValue,f.low)),f.high<=f.low&&(0===f.low?f.high=1:f.low<0?f.high=0:f.high>0?f.low=0:(f.high=1,f.low=0)),f},b.isNumeric=function(a){return null!==a&&isFinite(a)},b.isFalseyButZero=function(a){return!a&&0!==a},b.getNumberOrUndefined=function(a){return b.isNumeric(a)?+a:void 0},b.isMultiValue=function(a){return"object"==typeof a&&("x"in a||"y"in a)},b.getMultiValue=function(a,c){return b.isMultiValue(a)?b.getNumberOrUndefined(a[c||"y"]):b.getNumberOrUndefined(a)},b.rho=function(a){function b(a,c){return a%c===0?c:b(c,a%c)}function c(a){return a*a+1}if(1===a)return a;var d,e=2,f=2;if(a%2===0)return 2;do e=c(e)%a,f=c(c(f))%a,d=b(Math.abs(e-f),a);while(1===d);return d},b.getBounds=function(a,c,d,e){function f(a,b){return a===(a+=b)&&(a*=1+(b>0?o:-o)),a}var g,h,i,j=0,k={high:c.high,low:c.low};k.valueRange=k.high-k.low,k.oom=b.orderOfMagnitude(k.valueRange),k.step=Math.pow(10,k.oom),k.min=Math.floor(k.low/k.step)*k.step,k.max=Math.ceil(k.high/k.step)*k.step,k.range=k.max-k.min,k.numberOfSteps=Math.round(k.range/k.step);var l=b.projectLength(a,k.step,k),m=l<d,n=e?b.rho(k.range):0;if(e&&b.projectLength(a,1,k)>=d)k.step=1;else if(e&&n<k.step&&b.projectLength(a,n,k)>=d)k.step=n;else for(;;){if(m&&b.projectLength(a,k.step,k)<=d)k.step*=2;else{if(m||!(b.projectLength(a,k.step/2,k)>=d))break;if(k.step/=2,e&&k.step%1!==0){k.step*=2;break}}if(j++>1e3)throw new Error("Exceeded maximum number of iterations while optimizing scale step!")}var o=2.221e-16;for(k.step=Math.max(k.step,o),h=k.min,i=k.max;h+k.step<=k.low;)h=f(h,k.step);for(;i-k.step>=k.high;)i=f(i,-k.step);k.min=h,k.max=i,k.range=k.max-k.min;var p=[];for(g=k.min;g<=k.max;g=f(g,k.step)){var q=b.roundWithPrecision(g);q!==p[p.length-1]&&p.push(q)}return k.values=p,k},b.polarToCartesian=function(a,b,c,d){var e=(d-90)*Math.PI/180;return{x:a+c*Math.cos(e),y:b+c*Math.sin(e)}},b.createChartRect=function(a,c,d){var e=!(!c.axisX&&!c.axisY),f=e?c.axisY.offset:0,g=e?c.axisX.offset:0,h=a.width()||b.quantity(c.width).value||0,i=a.height()||b.quantity(c.height).value||0,j=b.normalizePadding(c.chartPadding,d);h=Math.max(h,f+j.left+j.right),i=Math.max(i,g+j.top+j.bottom);var k={padding:j,width:function(){return this.x2-this.x1},height:function(){return this.y1-this.y2}};return e?("start"===c.axisX.position?(k.y2=j.top+g,k.y1=Math.max(i-j.bottom,k.y2+1)):(k.y2=j.top,k.y1=Math.max(i-j.bottom-g,k.y2+1)),"start"===c.axisY.position?(k.x1=j.left+f,k.x2=Math.max(h-j.right,k.x1+1)):(k.x1=j.left,k.x2=Math.max(h-j.right-f,k.x1+1))):(k.x1=j.left,k.x2=Math.max(h-j.right,k.x1+1),k.y2=j.top,k.y1=Math.max(i-j.bottom,k.y2+1)),k},b.createGrid=function(a,c,d,e,f,g,h,i){var j={};j[d.units.pos+"1"]=a,j[d.units.pos+"2"]=a,j[d.counterUnits.pos+"1"]=e,j[d.counterUnits.pos+"2"]=e+f;var k=g.elem("line",j,h.join(" "));i.emit("draw",b.extend({type:"grid",axis:d,index:c,group:g,element:k},j))},b.createGridBackground=function(a,b,c,d){var e=a.elem("rect",{x:b.x1,y:b.y2,width:b.width(),height:b.height()},c,!0);d.emit("draw",{type:"gridBackground",group:a,element:e})},b.createLabel=function(a,c,e,f,g,h,i,j,k,l,m){var n,o={};if(o[g.units.pos]=a+i[g.units.pos],o[g.counterUnits.pos]=i[g.counterUnits.pos],o[g.units.len]=c,o[g.counterUnits.len]=Math.max(0,h-10),l){var p=d.createElement("span");p.className=k.join(" "),p.setAttribute("xmlns",b.namespaces.xhtml),p.innerText=f[e],p.style[g.units.len]=Math.round(o[g.units.len])+"px",p.style[g.counterUnits.len]=Math.round(o[g.counterUnits.len])+"px",n=j.foreignObject(p,b.extend({style:"overflow: visible;"},o))}else n=j.elem("text",o,k.join(" ")).text(f[e]);m.emit("draw",b.extend({type:"label",axis:g,index:e,group:j,element:n,text:f[e]},o))},b.getSeriesOption=function(a,b,c){if(a.name&&b.series&&b.series[a.name]){var d=b.series[a.name];return d.hasOwnProperty(c)?d[c]:b[c]}return b[c]},b.optionsProvider=function(a,d,e){function f(a){var f=h;if(h=b.extend({},j),d)for(i=0;i<d.length;i++){var g=c.matchMedia(d[i][0]);g.matches&&(h=b.extend(h,d[i][1]))}e&&a&&e.emit("optionsChanged",{previousOptions:f,currentOptions:h})}function g(){k.forEach(function(a){a.removeListener(f)})}var h,i,j=b.extend({},a),k=[];if(!c.matchMedia)throw"window.matchMedia not found! Make sure you're using a polyfill.";if(d)for(i=0;i<d.length;i++){var l=c.matchMedia(d[i][0]);l.addListener(f),k.push(l)}return f(),{removeMediaQueryListeners:g,getCurrentOptions:function(){return b.extend({},h)}}},b.splitIntoSegments=function(a,c,d){var e={increasingX:!1,fillHoles:!1};d=b.extend({},e,d);for(var f=[],g=!0,h=0;h<a.length;h+=2)void 0===b.getMultiValue(c[h/2].value)?d.fillHoles||(g=!0):(d.increasingX&&h>=2&&a[h]<=a[h-2]&&(g=!0),g&&(f.push({pathCoordinates:[],valueData:[]}),g=!1),f[f.length-1].pathCoordinates.push(a[h],a[h+1]),f[f.length-1].valueData.push(c[h/2]));return f}}(this||global,a),function(a,b){"use strict";b.Interpolation={},b.Interpolation.none=function(a){var c={fillHoles:!1};return a=b.extend({},c,a),function(c,d){for(var e=new b.Svg.Path,f=!0,g=0;g<c.length;g+=2){var h=c[g],i=c[g+1],j=d[g/2];void 0!==b.getMultiValue(j.value)?(f?e.move(h,i,!1,j):e.line(h,i,!1,j),f=!1):a.fillHoles||(f=!0)}return e}},b.Interpolation.simple=function(a){var c={divisor:2,fillHoles:!1};a=b.extend({},c,a);var d=1/Math.max(1,a.divisor);return function(c,e){for(var f,g,h,i=new b.Svg.Path,j=0;j<c.length;j+=2){var k=c[j],l=c[j+1],m=(k-f)*d,n=e[j/2];void 0!==n.value?(void 0===h?i.move(k,l,!1,n):i.curve(f+m,g,k-m,l,k,l,!1,n),f=k,g=l,h=n):a.fillHoles||(f=k=h=void 0)}return i}},b.Interpolation.cardinal=function(a){var c={tension:1,fillHoles:!1};a=b.extend({},c,a);var d=Math.min(1,Math.max(0,a.tension)),e=1-d;return function f(c,g){var h=b.splitIntoSegments(c,g,{fillHoles:a.fillHoles});if(h.length){if(h.length>1){var i=[];return h.forEach(function(a){i.push(f(a.pathCoordinates,a.valueData))}),b.Svg.Path.join(i)}if(c=h[0].pathCoordinates,g=h[0].valueData,c.length<=4)return b.Interpolation.none()(c,g);for(var j,k=(new b.Svg.Path).move(c[0],c[1],!1,g[0]),l=0,m=c.length;m-2*!j>l;l+=2){var n=[{x:+c[l-2],y:+c[l-1]},{x:+c[l],y:+c[l+1]},{x:+c[l+2],y:+c[l+3]},{x:+c[l+4],y:+c[l+5]}];j?l?m-4===l?n[3]={x:+c[0],y:+c[1]}:m-2===l&&(n[2]={x:+c[0],y:+c[1]},n[3]={x:+c[2],y:+c[3]}):n[0]={x:+c[m-2],y:+c[m-1]}:m-4===l?n[3]=n[2]:l||(n[0]={x:+c[l],y:+c[l+1]}),k.curve(d*(-n[0].x+6*n[1].x+n[2].x)/6+e*n[2].x,d*(-n[0].y+6*n[1].y+n[2].y)/6+e*n[2].y,d*(n[1].x+6*n[2].x-n[3].x)/6+e*n[2].x,d*(n[1].y+6*n[2].y-n[3].y)/6+e*n[2].y,n[2].x,n[2].y,!1,g[(l+2)/2])}return k}return b.Interpolation.none()([])}},b.Interpolation.monotoneCubic=function(a){var c={fillHoles:!1};return a=b.extend({},c,a),function d(c,e){var f=b.splitIntoSegments(c,e,{fillHoles:a.fillHoles,increasingX:!0});if(f.length){if(f.length>1){var g=[];return f.forEach(function(a){g.push(d(a.pathCoordinates,a.valueData))}),b.Svg.Path.join(g)}if(c=f[0].pathCoordinates,e=f[0].valueData,c.length<=4)return b.Interpolation.none()(c,e);var h,i,j=[],k=[],l=c.length/2,m=[],n=[],o=[],p=[];for(h=0;h<l;h++)j[h]=c[2*h],k[h]=c[2*h+1];for(h=0;h<l-1;h++)o[h]=k[h+1]-k[h],p[h]=j[h+1]-j[h],n[h]=o[h]/p[h];for(m[0]=n[0],m[l-1]=n[l-2],h=1;h<l-1;h++)0===n[h]||0===n[h-1]||n[h-1]>0!=n[h]>0?m[h]=0:(m[h]=3*(p[h-1]+p[h])/((2*p[h]+p[h-1])/n[h-1]+(p[h]+2*p[h-1])/n[h]),isFinite(m[h])||(m[h]=0));for(i=(new b.Svg.Path).move(j[0],k[0],!1,e[0]),h=0;h<l-1;h++)i.curve(j[h]+p[h]/3,k[h]+m[h]*p[h]/3,j[h+1]-p[h]/3,k[h+1]-m[h+1]*p[h]/3,j[h+1],k[h+1],!1,e[h+1]);return i}return b.Interpolation.none()([])}},b.Interpolation.step=function(a){var c={postpone:!0,fillHoles:!1};return a=b.extend({},c,a),function(c,d){for(var e,f,g,h=new b.Svg.Path,i=0;i<c.length;i+=2){var j=c[i],k=c[i+1],l=d[i/2];void 0!==l.value?(void 0===g?h.move(j,k,!1,l):(a.postpone?h.line(j,f,!1,g):h.line(e,k,!1,l),h.line(j,k,!1,l)),e=j,f=k,g=l):a.fillHoles||(e=f=g=void 0)}return h}}}(this||global,a),function(a,b){"use strict";b.EventEmitter=function(){function a(a,b){d[a]=d[a]||[],d[a].push(b)}function b(a,b){d[a]&&(b?(d[a].splice(d[a].indexOf(b),1),0===d[a].length&&delete d[a]):delete d[a])}function c(a,b){d[a]&&d[a].forEach(function(a){a(b)}),d["*"]&&d["*"].forEach(function(c){c(a,b)})}var d=[];return{addEventHandler:a,removeEventHandler:b,emit:c}}}(this||global,a),function(a,b){"use strict";function c(a){var b=[];if(a.length)for(var c=0;c<a.length;c++)b.push(a[c]);return b}function d(a,c){var d=c||this.prototype||b.Class,e=Object.create(d);b.Class.cloneDefinitions(e,a);var f=function(){var a,c=e.constructor||function(){};return a=this===b?Object.create(e):this,c.apply(a,Array.prototype.slice.call(arguments,0)),a};return f.prototype=e,f["super"]=d,f.extend=this.extend,f}function e(){var a=c(arguments),b=a[0];return a.splice(1,a.length-1).forEach(function(a){Object.getOwnPropertyNames(a).forEach(function(c){delete b[c],Object.defineProperty(b,c,Object.getOwnPropertyDescriptor(a,c))})}),b}b.Class={extend:d,cloneDefinitions:e}}(this||global,a),function(a,b){"use strict";function c(a,c,d){return a&&(this.data=a||{},this.data.labels=this.data.labels||[],this.data.series=this.data.series||[],this.eventEmitter.emit("data",{type:"update",data:this.data})),c&&(this.options=b.extend({},d?this.options:this.defaultOptions,c),this.initializeTimeoutId||(this.optionsProvider.removeMediaQueryListeners(),this.optionsProvider=b.optionsProvider(this.options,this.responsiveOptions,this.eventEmitter))),this.initializeTimeoutId||this.createChart(this.optionsProvider.getCurrentOptions()),this}function d(){return this.initializeTimeoutId?i.clearTimeout(this.initializeTimeoutId):(i.removeEventListener("resize",this.resizeListener),this.optionsProvider.removeMediaQueryListeners()),this}function e(a,b){return this.eventEmitter.addEventHandler(a,b),this}function f(a,b){return this.eventEmitter.removeEventHandler(a,b),this}function g(){i.addEventListener("resize",this.resizeListener),this.optionsProvider=b.optionsProvider(this.options,this.responsiveOptions,this.eventEmitter),this.eventEmitter.addEventHandler("optionsChanged",function(){this.update()}.bind(this)),this.options.plugins&&this.options.plugins.forEach(function(a){a instanceof Array?a[0](this,a[1]):a(this)}.bind(this)),this.eventEmitter.emit("data",{type:"initial",data:this.data}),this.createChart(this.optionsProvider.getCurrentOptions()),this.initializeTimeoutId=void 0}function h(a,c,d,e,f){this.container=b.querySelector(a),this.data=c||{},this.data.labels=this.data.labels||[],this.data.series=this.data.series||[],this.defaultOptions=d,this.options=e,this.responsiveOptions=f,this.eventEmitter=b.EventEmitter(),this.supportsForeignObject=b.Svg.isSupported("Extensibility"),this.supportsAnimations=b.Svg.isSupported("AnimationEventsAttribute"),this.resizeListener=function(){this.update()}.bind(this),this.container&&(this.container.__chartist__&&this.container.__chartist__.detach(),this.container.__chartist__=this),this.initializeTimeoutId=setTimeout(g.bind(this),0)}var i=a.window;b.Base=b.Class.extend({constructor:h,optionsProvider:void 0,container:void 0,svg:void 0,eventEmitter:void 0,createChart:function(){throw new Error("Base chart type can't be instantiated!")},update:c,detach:d,on:e,off:f,version:b.version,supportsForeignObject:!1})}(this||global,a),function(a,b){"use strict";function c(a,c,d,e,f){a instanceof Element?this._node=a:(this._node=y.createElementNS(b.namespaces.svg,a),"svg"===a&&this.attr({"xmlns:ct":b.namespaces.ct})),c&&this.attr(c),d&&this.addClass(d),e&&(f&&e._node.firstChild?e._node.insertBefore(this._node,e._node.firstChild):e._node.appendChild(this._node))}function d(a,c){return"string"==typeof a?c?this._node.getAttributeNS(c,a):this._node.getAttribute(a):(Object.keys(a).forEach(function(c){if(void 0!==a[c])if(c.indexOf(":")!==-1){var d=c.split(":");this._node.setAttributeNS(b.namespaces[d[0]],c,a[c])}else this._node.setAttribute(c,a[c])}.bind(this)),this)}function e(a,c,d,e){return new b.Svg(a,c,d,this,e)}function f(){return this._node.parentNode instanceof SVGElement?new b.Svg(this._node.parentNode):null}function g(){for(var a=this._node;"svg"!==a.nodeName;)a=a.parentNode;return new b.Svg(a)}function h(a){var c=this._node.querySelector(a);return c?new b.Svg(c):null}function i(a){var c=this._node.querySelectorAll(a);return c.length?new b.Svg.List(c):null}function j(){return this._node}function k(a,c,d,e){if("string"==typeof a){var f=y.createElement("div");f.innerHTML=a,a=f.firstChild}a.setAttribute("xmlns",b.namespaces.xmlns);var g=this.elem("foreignObject",c,d,e);return g._node.appendChild(a),g}function l(a){return this._node.appendChild(y.createTextNode(a)),this}function m(){for(;this._node.firstChild;)this._node.removeChild(this._node.firstChild);return this}function n(){return this._node.parentNode.removeChild(this._node),this.parent()}function o(a){return this._node.parentNode.replaceChild(a._node,this._node),a}function p(a,b){return b&&this._node.firstChild?this._node.insertBefore(a._node,this._node.firstChild):this._node.appendChild(a._node),this}function q(){return this._node.getAttribute("class")?this._node.getAttribute("class").trim().split(/\s+/):[]}function r(a){return this._node.setAttribute("class",this.classes(this._node).concat(a.trim().split(/\s+/)).filter(function(a,b,c){return c.indexOf(a)===b}).join(" ")),this}function s(a){var b=a.trim().split(/\s+/);return this._node.setAttribute("class",this.classes(this._node).filter(function(a){return b.indexOf(a)===-1}).join(" ")),this}function t(){return this._node.setAttribute("class",""),this}function u(){return this._node.getBoundingClientRect().height}function v(){return this._node.getBoundingClientRect().width}function w(a,c,d){return void 0===c&&(c=!0),Object.keys(a).forEach(function(e){function f(a,c){var f,g,h,i={};a.easing&&(h=a.easing instanceof Array?a.easing:b.Svg.Easing[a.easing],delete a.easing),a.begin=b.ensureUnit(a.begin,"ms"),a.dur=b.ensureUnit(a.dur,"ms"),h&&(a.calcMode="spline",a.keySplines=h.join(" "),a.keyTimes="0;1"),c&&(a.fill="freeze",i[e]=a.from,this.attr(i),g=b.quantity(a.begin||0).value,a.begin="indefinite"),f=this.elem("animate",b.extend({attributeName:e},a)),c&&setTimeout(function(){try{f._node.beginElement()}catch(b){i[e]=a.to,this.attr(i),f.remove()}}.bind(this),g),d&&f._node.addEventListener("beginEvent",function(){d.emit("animationBegin",{element:this,animate:f._node,params:a})}.bind(this)),f._node.addEventListener("endEvent",function(){d&&d.emit("animationEnd",{element:this,animate:f._node,params:a}),c&&(i[e]=a.to,this.attr(i),f.remove())}.bind(this))}a[e]instanceof Array?a[e].forEach(function(a){f.bind(this)(a,!1)}.bind(this)):f.bind(this)(a[e],c)}.bind(this)),this}function x(a){var c=this;this.svgElements=[];for(var d=0;d<a.length;d++)this.svgElements.push(new b.Svg(a[d]));Object.keys(b.Svg.prototype).filter(function(a){return["constructor","parent","querySelector","querySelectorAll","replace","append","classes","height","width"].indexOf(a)===-1}).forEach(function(a){c[a]=function(){var d=Array.prototype.slice.call(arguments,0);return c.svgElements.forEach(function(c){b.Svg.prototype[a].apply(c,d)}),c}})}var y=a.document;b.Svg=b.Class.extend({constructor:c,attr:d,elem:e,parent:f,root:g,querySelector:h,querySelectorAll:i,getNode:j,foreignObject:k,text:l,empty:m,remove:n,replace:o,append:p,classes:q,addClass:r,removeClass:s,removeAllClasses:t,height:u,width:v,animate:w}),b.Svg.isSupported=function(a){return y.implementation.hasFeature("http://www.w3.org/TR/SVG11/feature#"+a,"1.1")};var z={easeInSine:[.47,0,.745,.715],easeOutSine:[.39,.575,.565,1],easeInOutSine:[.445,.05,.55,.95],easeInQuad:[.55,.085,.68,.53],easeOutQuad:[.25,.46,.45,.94],easeInOutQuad:[.455,.03,.515,.955],easeInCubic:[.55,.055,.675,.19],easeOutCubic:[.215,.61,.355,1],easeInOutCubic:[.645,.045,.355,1],easeInQuart:[.895,.03,.685,.22],easeOutQuart:[.165,.84,.44,1],easeInOutQuart:[.77,0,.175,1],easeInQuint:[.755,.05,.855,.06],easeOutQuint:[.23,1,.32,1],easeInOutQuint:[.86,0,.07,1],easeInExpo:[.95,.05,.795,.035],easeOutExpo:[.19,1,.22,1],easeInOutExpo:[1,0,0,1],easeInCirc:[.6,.04,.98,.335],easeOutCirc:[.075,.82,.165,1],easeInOutCirc:[.785,.135,.15,.86],easeInBack:[.6,-.28,.735,.045],easeOutBack:[.175,.885,.32,1.275],easeInOutBack:[.68,-.55,.265,1.55]};b.Svg.Easing=z,b.Svg.List=b.Class.extend({constructor:x})}(this||global,a),function(a,b){"use strict";function c(a,c,d,e,f,g){var h=b.extend({command:f?a.toLowerCase():a.toUpperCase()},c,g?{data:g}:{});d.splice(e,0,h)}function d(a,b){a.forEach(function(c,d){t[c.command.toLowerCase()].forEach(function(e,f){b(c,e,d,f,a)})})}function e(a,c){this.pathElements=[],this.pos=0,this.close=a,this.options=b.extend({},u,c)}function f(a){return void 0!==a?(this.pos=Math.max(0,Math.min(this.pathElements.length,a)),this):this.pos}function g(a){return this.pathElements.splice(this.pos,a),this}function h(a,b,d,e){return c("M",{x:+a,y:+b},this.pathElements,this.pos++,d,e),this}function i(a,b,d,e){return c("L",{x:+a,y:+b},this.pathElements,this.pos++,d,e),this}function j(a,b,d,e,f,g,h,i){return c("C",{x1:+a,y1:+b,x2:+d,y2:+e,x:+f,y:+g},this.pathElements,this.pos++,h,i),this}function k(a,b,d,e,f,g,h,i,j){return c("A",{rx:+a,ry:+b,xAr:+d,lAf:+e,sf:+f,x:+g,y:+h},this.pathElements,this.pos++,i,j),this}function l(a){var c=a.replace(/([A-Za-z])([0-9])/g,"$1 $2").replace(/([0-9])([A-Za-z])/g,"$1 $2").split(/[\s,]+/).reduce(function(a,b){return b.match(/[A-Za-z]/)&&a.push([]),a[a.length-1].push(b),a},[]);"Z"===c[c.length-1][0].toUpperCase()&&c.pop();var d=c.map(function(a){var c=a.shift(),d=t[c.toLowerCase()];return b.extend({command:c},d.reduce(function(b,c,d){return b[c]=+a[d],b},{}))}),e=[this.pos,0];return Array.prototype.push.apply(e,d),Array.prototype.splice.apply(this.pathElements,e),this.pos+=d.length,this}function m(){var a=Math.pow(10,this.options.accuracy);return this.pathElements.reduce(function(b,c){var d=t[c.command.toLowerCase()].map(function(b){return this.options.accuracy?Math.round(c[b]*a)/a:c[b]}.bind(this));return b+c.command+d.join(",")}.bind(this),"")+(this.close?"Z":"")}function n(a,b){return d(this.pathElements,function(c,d){c[d]*="x"===d[0]?a:b}),this}function o(a,b){return d(this.pathElements,function(c,d){c[d]+="x"===d[0]?a:b}),this}function p(a){return d(this.pathElements,function(b,c,d,e,f){var g=a(b,c,d,e,f);(g||0===g)&&(b[c]=g)}),this}function q(a){var c=new b.Svg.Path(a||this.close);return c.pos=this.pos,c.pathElements=this.pathElements.slice().map(function(a){return b.extend({},a)}),c.options=b.extend({},this.options),c}function r(a){var c=[new b.Svg.Path];return this.pathElements.forEach(function(d){d.command===a.toUpperCase()&&0!==c[c.length-1].pathElements.length&&c.push(new b.Svg.Path),c[c.length-1].pathElements.push(d)}),c}function s(a,c,d){for(var e=new b.Svg.Path(c,d),f=0;f<a.length;f++)for(var g=a[f],h=0;h<g.pathElements.length;h++)e.pathElements.push(g.pathElements[h]);return e}var t={m:["x","y"],l:["x","y"],c:["x1","y1","x2","y2","x","y"],a:["rx","ry","xAr","lAf","sf","x","y"]},u={accuracy:3};b.Svg.Path=b.Class.extend({constructor:e,position:f,remove:g,move:h,line:i,curve:j,arc:k,scale:n,translate:o,transform:p,parse:l,stringify:m,clone:q,splitByCommand:r}),b.Svg.Path.elementDescriptions=t,b.Svg.Path.join=s}(this||global,a),function(a,b){"use strict";function c(a,b,c,d){this.units=a,this.counterUnits=a===e.x?e.y:e.x,this.chartRect=b,this.axisLength=b[a.rectEnd]-b[a.rectStart],this.gridOffset=b[a.rectOffset],this.ticks=c,this.options=d}function d(a,c,d,e,f){var g=e["axis"+this.units.pos.toUpperCase()],h=this.ticks.map(this.projectValue.bind(this)),i=this.ticks.map(g.labelInterpolationFnc);h.forEach(function(j,k){var l,m={x:0,y:0};l=h[k+1]?h[k+1]-j:Math.max(this.axisLength-j,30),b.isFalseyButZero(i[k])&&""!==i[k]||("x"===this.units.pos?(j=this.chartRect.x1+j,m.x=e.axisX.labelOffset.x,"start"===e.axisX.position?m.y=this.chartRect.padding.top+e.axisX.labelOffset.y+(d?5:20):m.y=this.chartRect.y1+e.axisX.labelOffset.y+(d?5:20)):(j=this.chartRect.y1-j,m.y=e.axisY.labelOffset.y-(d?l:0),"start"===e.axisY.position?m.x=d?this.chartRect.padding.left+e.axisY.labelOffset.x:this.chartRect.x1-10:m.x=this.chartRect.x2+e.axisY.labelOffset.x+10),g.showGrid&&b.createGrid(j,k,this,this.gridOffset,this.chartRect[this.counterUnits.len](),a,[e.classNames.grid,e.classNames[this.units.dir]],f),g.showLabel&&b.createLabel(j,l,k,i,this,g.offset,m,c,[e.classNames.label,e.classNames[this.units.dir],"start"===g.position?e.classNames[g.position]:e.classNames.end],d,f))}.bind(this))}var e=(a.window,a.document,{x:{pos:"x",len:"width",dir:"horizontal",rectStart:"x1",rectEnd:"x2",rectOffset:"y2"},y:{pos:"y",len:"height",dir:"vertical",rectStart:"y2",rectEnd:"y1",rectOffset:"x1"}});b.Axis=b.Class.extend({constructor:c,createGridAndLabels:d,projectValue:function(a,b,c){throw new Error("Base axis can't be instantiated!")}}),b.Axis.units=e}(this||global,a),function(a,b){"use strict";function c(a,c,d,e){var f=e.highLow||b.getHighLow(c,e,a.pos);this.bounds=b.getBounds(d[a.rectEnd]-d[a.rectStart],f,e.scaleMinSpace||20,e.onlyInteger),this.range={min:this.bounds.min,max:this.bounds.max},b.AutoScaleAxis["super"].constructor.call(this,a,d,this.bounds.values,e)}function d(a){return this.axisLength*(+b.getMultiValue(a,this.units.pos)-this.bounds.min)/this.bounds.range}a.window,a.document;b.AutoScaleAxis=b.Axis.extend({constructor:c,projectValue:d})}(this||global,a),function(a,b){"use strict";function c(a,c,d,e){var f=e.highLow||b.getHighLow(c,e,a.pos);this.divisor=e.divisor||1,this.ticks=e.ticks||b.times(this.divisor).map(function(a,b){return f.low+(f.high-f.low)/this.divisor*b}.bind(this)),this.ticks.sort(function(a,b){return a-b}),this.range={min:f.low,max:f.high},b.FixedScaleAxis["super"].constructor.call(this,a,d,this.ticks,e),this.stepLength=this.axisLength/this.divisor}function d(a){return this.axisLength*(+b.getMultiValue(a,this.units.pos)-this.range.min)/(this.range.max-this.range.min)}a.window,a.document;b.FixedScaleAxis=b.Axis.extend({constructor:c,projectValue:d})}(this||global,a),function(a,b){"use strict";function c(a,c,d,e){b.StepAxis["super"].constructor.call(this,a,d,e.ticks,e);var f=Math.max(1,e.ticks.length-(e.stretch?1:0));this.stepLength=this.axisLength/f}function d(a,b){return this.stepLength*b}a.window,a.document;b.StepAxis=b.Axis.extend({constructor:c,projectValue:d})}(this||global,a),function(a,b){"use strict";function c(a){var c=b.normalizeData(this.data,a.reverseData,!0);this.svg=b.createSvg(this.container,a.width,a.height,a.classNames.chart);var d,f,g=this.svg.elem("g").addClass(a.classNames.gridGroup),h=this.svg.elem("g"),i=this.svg.elem("g").addClass(a.classNames.labelGroup),j=b.createChartRect(this.svg,a,e.padding);d=void 0===a.axisX.type?new b.StepAxis(b.Axis.units.x,c.normalized.series,j,b.extend({},a.axisX,{ticks:c.normalized.labels,stretch:a.fullWidth})):a.axisX.type.call(b,b.Axis.units.x,c.normalized.series,j,a.axisX),f=void 0===a.axisY.type?new b.AutoScaleAxis(b.Axis.units.y,c.normalized.series,j,b.extend({},a.axisY,{high:b.isNumeric(a.high)?a.high:a.axisY.high,low:b.isNumeric(a.low)?a.low:a.axisY.low})):a.axisY.type.call(b,b.Axis.units.y,c.normalized.series,j,a.axisY),d.createGridAndLabels(g,i,this.supportsForeignObject,a,this.eventEmitter),f.createGridAndLabels(g,i,this.supportsForeignObject,a,this.eventEmitter),a.showGridBackground&&b.createGridBackground(g,j,a.classNames.gridBackground,this.eventEmitter),c.raw.series.forEach(function(e,g){var i=h.elem("g");i.attr({"ct:series-name":e.name,"ct:meta":b.serialize(e.meta)}),i.addClass([a.classNames.series,e.className||a.classNames.series+"-"+b.alphaNumerate(g)].join(" "));var k=[],l=[];c.normalized.series[g].forEach(function(a,h){var i={x:j.x1+d.projectValue(a,h,c.normalized.series[g]),y:j.y1-f.projectValue(a,h,c.normalized.series[g])};k.push(i.x,i.y),l.push({value:a,valueIndex:h,meta:b.getMetaData(e,h)})}.bind(this));var m={lineSmooth:b.getSeriesOption(e,a,"lineSmooth"),showPoint:b.getSeriesOption(e,a,"showPoint"),showLine:b.getSeriesOption(e,a,"showLine"),showArea:b.getSeriesOption(e,a,"showArea"),areaBase:b.getSeriesOption(e,a,"areaBase")},n="function"==typeof m.lineSmooth?m.lineSmooth:m.lineSmooth?b.Interpolation.monotoneCubic():b.Interpolation.none(),o=n(k,l);if(m.showPoint&&o.pathElements.forEach(function(c){var h=i.elem("line",{x1:c.x,y1:c.y,x2:c.x+.01,y2:c.y},a.classNames.point).attr({"ct:value":[c.data.value.x,c.data.value.y].filter(b.isNumeric).join(","),"ct:meta":b.serialize(c.data.meta)});this.eventEmitter.emit("draw",{type:"point",value:c.data.value,index:c.data.valueIndex,meta:c.data.meta,series:e,seriesIndex:g,axisX:d,axisY:f,group:i,element:h,x:c.x,y:c.y})}.bind(this)),m.showLine){var p=i.elem("path",{d:o.stringify()},a.classNames.line,!0);this.eventEmitter.emit("draw",{type:"line",values:c.normalized.series[g],path:o.clone(),chartRect:j,index:g,series:e,seriesIndex:g,seriesMeta:e.meta,axisX:d,axisY:f,group:i,element:p})}if(m.showArea&&f.range){var q=Math.max(Math.min(m.areaBase,f.range.max),f.range.min),r=j.y1-f.projectValue(q);o.splitByCommand("M").filter(function(a){return a.pathElements.length>1}).map(function(a){var b=a.pathElements[0],c=a.pathElements[a.pathElements.length-1];return a.clone(!0).position(0).remove(1).move(b.x,r).line(b.x,b.y).position(a.pathElements.length+1).line(c.x,r)}).forEach(function(b){var h=i.elem("path",{d:b.stringify()},a.classNames.area,!0);this.eventEmitter.emit("draw",{type:"area",values:c.normalized.series[g],path:b.clone(),series:e,seriesIndex:g,axisX:d,axisY:f,chartRect:j,index:g,group:i,element:h})}.bind(this))}}.bind(this)),this.eventEmitter.emit("created",{bounds:f.bounds,chartRect:j,axisX:d,axisY:f,svg:this.svg,options:a})}function d(a,c,d,f){b.Line["super"].constructor.call(this,a,c,e,b.extend({},e,d),f)}var e=(a.window,a.document,{axisX:{offset:30,position:"end",labelOffset:{x:0,y:0},showLabel:!0,showGrid:!0,labelInterpolationFnc:b.noop,type:void 0},axisY:{offset:40,position:"start",labelOffset:{x:0,y:0},showLabel:!0,showGrid:!0,labelInterpolationFnc:b.noop,type:void 0,scaleMinSpace:20,onlyInteger:!1},width:void 0,height:void 0,showLine:!0,showPoint:!0,showArea:!1,areaBase:0,lineSmooth:!0,showGridBackground:!1,low:void 0,high:void 0,chartPadding:{top:15,right:15,bottom:5,left:10},fullWidth:!1,reverseData:!1,classNames:{chart:"ct-chart-line",label:"ct-label",labelGroup:"ct-labels",series:"ct-series",line:"ct-line",point:"ct-point",area:"ct-area",grid:"ct-grid",gridGroup:"ct-grids",gridBackground:"ct-grid-background",vertical:"ct-vertical",horizontal:"ct-horizontal",start:"ct-start",end:"ct-end"}});b.Line=b.Base.extend({constructor:d,createChart:c})}(this||global,a),function(a,b){"use strict";function c(a){var c,d;a.distributeSeries?(c=b.normalizeData(this.data,a.reverseData,a.horizontalBars?"x":"y"),c.normalized.series=c.normalized.series.map(function(a){return[a]})):c=b.normalizeData(this.data,a.reverseData,a.horizontalBars?"x":"y"),this.svg=b.createSvg(this.container,a.width,a.height,a.classNames.chart+(a.horizontalBars?" "+a.classNames.horizontalBars:""));var f=this.svg.elem("g").addClass(a.classNames.gridGroup),g=this.svg.elem("g"),h=this.svg.elem("g").addClass(a.classNames.labelGroup);
9
+ if(a.stackBars&&0!==c.normalized.series.length){var i=b.serialMap(c.normalized.series,function(){return Array.prototype.slice.call(arguments).map(function(a){return a}).reduce(function(a,b){return{x:a.x+(b&&b.x)||0,y:a.y+(b&&b.y)||0}},{x:0,y:0})});d=b.getHighLow([i],a,a.horizontalBars?"x":"y")}else d=b.getHighLow(c.normalized.series,a,a.horizontalBars?"x":"y");d.high=+a.high||(0===a.high?0:d.high),d.low=+a.low||(0===a.low?0:d.low);var j,k,l,m,n,o=b.createChartRect(this.svg,a,e.padding);k=a.distributeSeries&&a.stackBars?c.normalized.labels.slice(0,1):c.normalized.labels,a.horizontalBars?(j=m=void 0===a.axisX.type?new b.AutoScaleAxis(b.Axis.units.x,c.normalized.series,o,b.extend({},a.axisX,{highLow:d,referenceValue:0})):a.axisX.type.call(b,b.Axis.units.x,c.normalized.series,o,b.extend({},a.axisX,{highLow:d,referenceValue:0})),l=n=void 0===a.axisY.type?new b.StepAxis(b.Axis.units.y,c.normalized.series,o,{ticks:k}):a.axisY.type.call(b,b.Axis.units.y,c.normalized.series,o,a.axisY)):(l=m=void 0===a.axisX.type?new b.StepAxis(b.Axis.units.x,c.normalized.series,o,{ticks:k}):a.axisX.type.call(b,b.Axis.units.x,c.normalized.series,o,a.axisX),j=n=void 0===a.axisY.type?new b.AutoScaleAxis(b.Axis.units.y,c.normalized.series,o,b.extend({},a.axisY,{highLow:d,referenceValue:0})):a.axisY.type.call(b,b.Axis.units.y,c.normalized.series,o,b.extend({},a.axisY,{highLow:d,referenceValue:0})));var p=a.horizontalBars?o.x1+j.projectValue(0):o.y1-j.projectValue(0),q=[];l.createGridAndLabels(f,h,this.supportsForeignObject,a,this.eventEmitter),j.createGridAndLabels(f,h,this.supportsForeignObject,a,this.eventEmitter),a.showGridBackground&&b.createGridBackground(f,o,a.classNames.gridBackground,this.eventEmitter),c.raw.series.forEach(function(d,e){var f,h,i=e-(c.raw.series.length-1)/2;f=a.distributeSeries&&!a.stackBars?l.axisLength/c.normalized.series.length/2:a.distributeSeries&&a.stackBars?l.axisLength/2:l.axisLength/c.normalized.series[e].length/2,h=g.elem("g"),h.attr({"ct:series-name":d.name,"ct:meta":b.serialize(d.meta)}),h.addClass([a.classNames.series,d.className||a.classNames.series+"-"+b.alphaNumerate(e)].join(" ")),c.normalized.series[e].forEach(function(g,k){var r,s,t,u;if(u=a.distributeSeries&&!a.stackBars?e:a.distributeSeries&&a.stackBars?0:k,r=a.horizontalBars?{x:o.x1+j.projectValue(g&&g.x?g.x:0,k,c.normalized.series[e]),y:o.y1-l.projectValue(g&&g.y?g.y:0,u,c.normalized.series[e])}:{x:o.x1+l.projectValue(g&&g.x?g.x:0,u,c.normalized.series[e]),y:o.y1-j.projectValue(g&&g.y?g.y:0,k,c.normalized.series[e])},l instanceof b.StepAxis&&(l.options.stretch||(r[l.units.pos]+=f*(a.horizontalBars?-1:1)),r[l.units.pos]+=a.stackBars||a.distributeSeries?0:i*a.seriesBarDistance*(a.horizontalBars?-1:1)),t=q[k]||p,q[k]=t-(p-r[l.counterUnits.pos]),void 0!==g){var v={};v[l.units.pos+"1"]=r[l.units.pos],v[l.units.pos+"2"]=r[l.units.pos],!a.stackBars||"accumulate"!==a.stackMode&&a.stackMode?(v[l.counterUnits.pos+"1"]=p,v[l.counterUnits.pos+"2"]=r[l.counterUnits.pos]):(v[l.counterUnits.pos+"1"]=t,v[l.counterUnits.pos+"2"]=q[k]),v.x1=Math.min(Math.max(v.x1,o.x1),o.x2),v.x2=Math.min(Math.max(v.x2,o.x1),o.x2),v.y1=Math.min(Math.max(v.y1,o.y2),o.y1),v.y2=Math.min(Math.max(v.y2,o.y2),o.y1);var w=b.getMetaData(d,k);s=h.elem("line",v,a.classNames.bar).attr({"ct:value":[g.x,g.y].filter(b.isNumeric).join(","),"ct:meta":b.serialize(w)}),this.eventEmitter.emit("draw",b.extend({type:"bar",value:g,index:k,meta:w,series:d,seriesIndex:e,axisX:m,axisY:n,chartRect:o,group:h,element:s},v))}}.bind(this))}.bind(this)),this.eventEmitter.emit("created",{bounds:j.bounds,chartRect:o,axisX:m,axisY:n,svg:this.svg,options:a})}function d(a,c,d,f){b.Bar["super"].constructor.call(this,a,c,e,b.extend({},e,d),f)}var e=(a.window,a.document,{axisX:{offset:30,position:"end",labelOffset:{x:0,y:0},showLabel:!0,showGrid:!0,labelInterpolationFnc:b.noop,scaleMinSpace:30,onlyInteger:!1},axisY:{offset:40,position:"start",labelOffset:{x:0,y:0},showLabel:!0,showGrid:!0,labelInterpolationFnc:b.noop,scaleMinSpace:20,onlyInteger:!1},width:void 0,height:void 0,high:void 0,low:void 0,referenceValue:0,chartPadding:{top:15,right:15,bottom:5,left:10},seriesBarDistance:15,stackBars:!1,stackMode:"accumulate",horizontalBars:!1,distributeSeries:!1,reverseData:!1,showGridBackground:!1,classNames:{chart:"ct-chart-bar",horizontalBars:"ct-horizontal-bars",label:"ct-label",labelGroup:"ct-labels",series:"ct-series",bar:"ct-bar",grid:"ct-grid",gridGroup:"ct-grids",gridBackground:"ct-grid-background",vertical:"ct-vertical",horizontal:"ct-horizontal",start:"ct-start",end:"ct-end"}});b.Bar=b.Base.extend({constructor:d,createChart:c})}(this||global,a),function(a,b){"use strict";function c(a,b,c){var d=b.x>a.x;return d&&"explode"===c||!d&&"implode"===c?"start":d&&"implode"===c||!d&&"explode"===c?"end":"middle"}function d(a){var d,e,g,h,i,j=b.normalizeData(this.data),k=[],l=a.startAngle;this.svg=b.createSvg(this.container,a.width,a.height,a.donut?a.classNames.chartDonut:a.classNames.chartPie),e=b.createChartRect(this.svg,a,f.padding),g=Math.min(e.width()/2,e.height()/2),i=a.total||j.normalized.series.reduce(function(a,b){return a+b},0);var m=b.quantity(a.donutWidth);"%"===m.unit&&(m.value*=g/100),g-=a.donut&&!a.donutSolid?m.value/2:0,h="outside"===a.labelPosition||a.donut&&!a.donutSolid?g:"center"===a.labelPosition?0:a.donutSolid?g-m.value/2:g/2,h+=a.labelOffset;var n={x:e.x1+e.width()/2,y:e.y2+e.height()/2},o=1===j.raw.series.filter(function(a){return a.hasOwnProperty("value")?0!==a.value:0!==a}).length;j.raw.series.forEach(function(a,b){k[b]=this.svg.elem("g",null,null)}.bind(this)),a.showLabel&&(d=this.svg.elem("g",null,null)),j.raw.series.forEach(function(e,f){if(0!==j.normalized.series[f]||!a.ignoreEmptyValues){k[f].attr({"ct:series-name":e.name}),k[f].addClass([a.classNames.series,e.className||a.classNames.series+"-"+b.alphaNumerate(f)].join(" "));var p=i>0?l+j.normalized.series[f]/i*360:0,q=Math.max(0,l-(0===f||o?0:.2));p-q>=359.99&&(p=q+359.99);var r,s,t,u=b.polarToCartesian(n.x,n.y,g,q),v=b.polarToCartesian(n.x,n.y,g,p),w=new b.Svg.Path(!a.donut||a.donutSolid).move(v.x,v.y).arc(g,g,0,p-l>180,0,u.x,u.y);a.donut?a.donutSolid&&(t=g-m.value,r=b.polarToCartesian(n.x,n.y,t,l-(0===f||o?0:.2)),s=b.polarToCartesian(n.x,n.y,t,p),w.line(r.x,r.y),w.arc(t,t,0,p-l>180,1,s.x,s.y)):w.line(n.x,n.y);var x=a.classNames.slicePie;a.donut&&(x=a.classNames.sliceDonut,a.donutSolid&&(x=a.classNames.sliceDonutSolid));var y=k[f].elem("path",{d:w.stringify()},x);if(y.attr({"ct:value":j.normalized.series[f],"ct:meta":b.serialize(e.meta)}),a.donut&&!a.donutSolid&&(y._node.style.strokeWidth=m.value+"px"),this.eventEmitter.emit("draw",{type:"slice",value:j.normalized.series[f],totalDataSum:i,index:f,meta:e.meta,series:e,group:k[f],element:y,path:w.clone(),center:n,radius:g,startAngle:l,endAngle:p}),a.showLabel){var z;z=1===j.raw.series.length?{x:n.x,y:n.y}:b.polarToCartesian(n.x,n.y,h,l+(p-l)/2);var A;A=j.normalized.labels&&!b.isFalseyButZero(j.normalized.labels[f])?j.normalized.labels[f]:j.normalized.series[f];var B=a.labelInterpolationFnc(A,f);if(B||0===B){var C=d.elem("text",{dx:z.x,dy:z.y,"text-anchor":c(n,z,a.labelDirection)},a.classNames.label).text(""+B);this.eventEmitter.emit("draw",{type:"label",index:f,group:d,element:C,text:""+B,x:z.x,y:z.y})}}l=p}}.bind(this)),this.eventEmitter.emit("created",{chartRect:e,svg:this.svg,options:a})}function e(a,c,d,e){b.Pie["super"].constructor.call(this,a,c,f,b.extend({},f,d),e)}var f=(a.window,a.document,{width:void 0,height:void 0,chartPadding:5,classNames:{chartPie:"ct-chart-pie",chartDonut:"ct-chart-donut",series:"ct-series",slicePie:"ct-slice-pie",sliceDonut:"ct-slice-donut",sliceDonutSolid:"ct-slice-donut-solid",label:"ct-label"},startAngle:0,total:void 0,donut:!1,donutSolid:!1,donutWidth:60,showLabel:!0,labelOffset:0,labelPosition:"inside",labelInterpolationFnc:b.noop,labelDirection:"neutral",reverseData:!1,ignoreEmptyValues:!1});b.Pie=b.Base.extend({constructor:e,createChart:d,determineAnchorPosition:c})}(this||global,a),a});
10
  //# sourceMappingURL=chartist.min.js.map
resources/js/charts.js CHANGED
@@ -70,11 +70,27 @@ jQuery.fn.icwpWpsfAjaxChart = function ( aOptions ) {
70
 
71
  $oChartContainer.html('');
72
  new Chartist.Line(
73
- '.icwpAjaxContainerChart',
74
  oResponse.data.chart.data,
75
  {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
76
  axisY: {
 
77
  onlyInteger: true,
 
78
  labelInterpolationFnc: function ( value ) {
79
  return value;
80
  }
70
 
71
  $oChartContainer.html('');
72
  new Chartist.Line(
73
+ $oThis.selector+' .icwpAjaxContainerChart',
74
  oResponse.data.chart.data,
75
  {
76
+ height: '100px',
77
+ fullWidth: true,
78
+ showArea: false,
79
+ chartPadding: {
80
+ top: 10,
81
+ right: 10,
82
+ bottom: 10,
83
+ left: 10
84
+ },
85
+ axisX: {
86
+ offset: 5,
87
+ showLabel: false,
88
+ showGrid: false,
89
+ },
90
  axisY: {
91
+ offset: 25,
92
  onlyInteger: true,
93
+ showLabel: true,
94
  labelInterpolationFnc: function ( value ) {
95
  return value;
96
  }
resources/js/global-plugin.js CHANGED
@@ -172,7 +172,7 @@ if ( typeof icwp_wpsf_vars_hp !== 'undefined' ) {
172
  jQuery.post( ajaxurl, $aData, function ( oResponse ) {
173
 
174
  } ).always( function () {
175
- location.reload( true );
176
  bActivate = null;
177
  }
178
  );
172
  jQuery.post( ajaxurl, $aData, function ( oResponse ) {
173
 
174
  } ).always( function () {
175
+ location.reload();
176
  bActivate = null;
177
  }
178
  );
resources/js/shield-comments.js ADDED
@@ -0,0 +1,137 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /** @var shield_comments object */
2
+ if ( typeof shield_comments !== 'undefined' ) {
3
+ var iCWP_WPSF_ShieldCommentGuard = new function () {
4
+
5
+ var submitButton;
6
+ var origButtonValue;
7
+ var nTimerCounter;
8
+ var sCountdownTimer;
9
+
10
+ this.initialise = function () {
11
+ jQuery( document ).ready( function () {
12
+ insertPlaceHolder_Gasp( this );
13
+ } );
14
+ };
15
+
16
+ var reEnableButton = function () {
17
+ let nRemaining = shield_comments.vars.cooldown - nTimerCounter;
18
+ submitButton.value = shield_comments.strings.js_comment_wait.replace( "%s", nRemaining );
19
+ if ( nTimerCounter >= shield_comments.vars.cooldown ) {
20
+ submitButton.value = origButtonValue;
21
+ submitButton.disabled = false;
22
+ clearInterval( sCountdownTimer );
23
+ }
24
+ nTimerCounter++;
25
+ };
26
+
27
+ var assignElements = function ( shiep ) {
28
+ var maybecheckbox = document.getElementById( '_shieldcb_nombre' );
29
+ if ( typeof (maybecheckbox) === "undefined" || maybecheckbox === null ) {
30
+ var cbnombre = document.createElement( "input" );
31
+ cbnombre.type = "hidden";
32
+ cbnombre.id = "_shieldcb_nombre";
33
+ cbnombre.name = "cb_nombre";
34
+ cbnombre.value = shield_comments.vars.cbname;
35
+ shiep.appendChild( cbnombre );
36
+
37
+ document.body.style.cursor = 'wait';
38
+ submitButton.disabled = true;
39
+
40
+ var aAjaxVars = shield_comments.ajax.comment_token;
41
+ jQuery.post( aAjaxVars.ajaxurl, aAjaxVars,
42
+ function ( oResponse ) {
43
+ if ( typeof (oResponse) !== "undefined" && oResponse !== null ) {
44
+ if ( oResponse.success ) {
45
+ var inputBotts = document.createElement( "input" );
46
+ inputBotts.type = "hidden";
47
+ inputBotts.name = "botts";
48
+ inputBotts.value = aAjaxVars.ts;
49
+ var inputToken = document.createElement( "input" );
50
+ inputToken.type = "hidden";
51
+ inputToken.name = "comment_token";
52
+ inputToken.value = oResponse.data.token;
53
+
54
+ shiep.appendChild( inputBotts );
55
+ shiep.appendChild( inputToken );
56
+ }
57
+ }
58
+ }
59
+ ).fail(
60
+ function () {
61
+ alert( 'There was a problem with the request. Please try reloading the page.' );
62
+ }
63
+ ).always( function () {
64
+ submitButton.disabled = false;
65
+ document.body.style.cursor = 'default';
66
+ }
67
+ );
68
+ }
69
+ };
70
+
71
+ var reDisableButton = function () {
72
+ submitButton.value = shield_comments.strings.comment_reload;
73
+ submitButton.disabled = true;
74
+ };
75
+
76
+ var insertPlaceHolder_Gasp = function ( form ) {
77
+ var shiep = document.getElementById( shield_comments.vars.uniq );
78
+ if ( typeof (shiep) === "undefined" || shiep === null ) {
79
+ return;
80
+ }
81
+
82
+ var shieThe_cb = document.createElement( "input" );
83
+ shieThe_cb.type = "checkbox";
84
+ shieThe_cb.value = "Y";
85
+ shieThe_cb.name = shield_comments.vars.cbname;
86
+ shieThe_cb.id = '_' + shieThe_cb.name;
87
+ shieThe_cb.onchange = function () {
88
+ assignElements( shiep );
89
+ };
90
+
91
+ var shieThe_lab = document.createElement( "label" );
92
+ var shieThe_labspan = document.createElement( "span" );
93
+ shieThe_labspan.innerHTML = '&nbsp;' + shield_comments.strings.label;
94
+
95
+ shieThe_lab.appendChild( shieThe_cb );
96
+ shieThe_lab.appendChild( shieThe_labspan );
97
+
98
+ var shishoney = document.createElement( "input" );
99
+ shishoney.type = "hidden";
100
+ shishoney.name = "sugar_sweet_email";
101
+
102
+ shiep.appendChild( shishoney );
103
+ shiep.appendChild( shieThe_lab );
104
+
105
+ var comForm = shieThe_cb.form;
106
+ var subbuttonList = comForm.querySelectorAll( 'input[type="submit"]' );
107
+
108
+ if ( typeof (subbuttonList) !== "undefined" ) {
109
+
110
+ submitButton = subbuttonList[ 0 ];
111
+
112
+ if ( typeof (submitButton) !== "undefined" ) {
113
+
114
+ if ( shield_comments.vars.cooldown > 0 ) {
115
+ submitButton.disabled = true;
116
+ origButtonValue = submitButton.value;
117
+ nTimerCounter = 0;
118
+ reEnableButton();
119
+ sCountdownTimer = setInterval( reEnableButton, 1000 );
120
+ }
121
+ if ( shield_comments.vars.expires > 0 ) {
122
+ setTimeout( reDisableButton, (1000 * shield_comments.vars.expires - 1000) );
123
+ }
124
+ }
125
+ }
126
+
127
+ shieThe_cb.form.onsubmit = function () {
128
+ if ( shieThe_cb.checked !== true ) {
129
+ alert( shield_comments.strings.alert );
130
+ return false;
131
+ }
132
+ return true;
133
+ };
134
+ };
135
+ }();
136
+ iCWP_WPSF_ShieldCommentGuard.initialise();
137
+ }
src/common/icwp-data.php CHANGED
@@ -135,7 +135,7 @@ class ICWP_WPSF_DataProcessor extends ICWP_WPSF_Foundation {
135
  * @return string
136
  */
137
  public function urlStripSchema( $sUrl ) {
138
- return preg_replace( '#^((http|https):)?\/\/#i', '', $sUrl );
139
  }
140
 
141
  /**
@@ -458,7 +458,7 @@ class ICWP_WPSF_DataProcessor extends ICWP_WPSF_Foundation {
458
  * @deprecated
459
  */
460
  public function cookie( $sKey, $mDefault = null, $bTrim = true ) {
461
- return $this->loadRequest()->cookie( $sKey, $mDefault, $bTrim );
462
  }
463
 
464
  /**
@@ -468,7 +468,7 @@ class ICWP_WPSF_DataProcessor extends ICWP_WPSF_Foundation {
468
  * @deprecated
469
  */
470
  public function env( $sKey, $mDefault = null ) {
471
- return $this->loadRequest()->env( $sKey, $mDefault );
472
  }
473
 
474
  /**
@@ -479,7 +479,7 @@ class ICWP_WPSF_DataProcessor extends ICWP_WPSF_Foundation {
479
  * @deprecated
480
  */
481
  public function post( $sKey, $mDefault = null, $bTrim = true ) {
482
- return $this->loadRequest()->post( $sKey, $mDefault, $bTrim );
483
  }
484
 
485
  /**
@@ -490,7 +490,7 @@ class ICWP_WPSF_DataProcessor extends ICWP_WPSF_Foundation {
490
  * @deprecated
491
  */
492
  public function query( $sKey, $mDefault = null, $bTrim = true ) {
493
- return $this->loadRequest()->query( $sKey, $mDefault, $bTrim );
494
  }
495
 
496
  /**
135
  * @return string
136
  */
137
  public function urlStripSchema( $sUrl ) {
138
+ return preg_replace( '#^((http|https):)?//#i', '', $sUrl );
139
  }
140
 
141
  /**
458
  * @deprecated
459
  */
460
  public function cookie( $sKey, $mDefault = null, $bTrim = true ) {
461
+ return \FernleafSystems\Wordpress\Services\Services::Request()->cookie( $sKey, $mDefault );
462
  }
463
 
464
  /**
468
  * @deprecated
469
  */
470
  public function env( $sKey, $mDefault = null ) {
471
+ return \FernleafSystems\Wordpress\Services\Services::Request()->env( $sKey, $mDefault );
472
  }
473
 
474
  /**
479
  * @deprecated
480
  */
481
  public function post( $sKey, $mDefault = null, $bTrim = true ) {
482
+ return \FernleafSystems\Wordpress\Services\Services::Request()->post( $sKey, $mDefault );
483
  }
484
 
485
  /**
490
  * @deprecated
491
  */
492
  public function query( $sKey, $mDefault = null, $bTrim = true ) {
493
+ return \FernleafSystems\Wordpress\Services\Services::Request()->query( $sKey, $mDefault );
494
  }
495
 
496
  /**
src/common/icwp-edd.php DELETED
@@ -1,172 +0,0 @@
1
- <?php
2
-
3
- use FernleafSystems\Wordpress\Plugin\Shield\License\EddLicenseVO;
4
- use FernleafSystems\Wordpress\Services\Services;
5
-
6
- class ICWP_WPSF_Edd {
7
-
8
- /**
9
- * @var ICWP_WPSF_Edd
10
- */
11
- protected static $oInstance = null;
12
-
13
- /**
14
- * @var array
15
- */
16
- private $aAdditionalRequestParams;
17
-
18
- /**
19
- * @return ICWP_WPSF_Edd
20
- */
21
- public static function GetInstance() {
22
- if ( is_null( self::$oInstance ) ) {
23
- self::$oInstance = new self();
24
- }
25
- return self::$oInstance;
26
- }
27
-
28
- /**
29
- * A simple outgoing POST request to see that we can communicate with the ODP servers
30
- * @param string $sStoreUrl
31
- * @return string
32
- */
33
- public function ping( $sStoreUrl ) {
34
- $sStoreUrl = add_query_arg( [ 'license_ping' => 'Y' ], $sStoreUrl );
35
- $aParams = [
36
- 'body' => [
37
- 'ping' => 'pong',
38
- 'license' => 'abcdefghi',
39
- 'item_id' => '123',
40
- 'url' => Services::WpGeneral()->getWpUrl()
41
- ]
42
- ];
43
-
44
- $oHttpReq = Services::HttpRequest();
45
- if ( $oHttpReq->post( $sStoreUrl, $aParams ) ) {
46
- $aResult = @json_decode( $oHttpReq->lastResponse->body, true );
47
- $sResult = ( isset( $aResult[ 'success' ] ) && $aResult[ 'success' ] ) ? 'success' : 'unknown failure';
48
- }
49
- else {
50
- $sResult = $oHttpReq->lastError->get_error_message();
51
- }
52
- return $sResult;
53
- }
54
-
55
- /**
56
- * @param string $sStoreUrl
57
- * @param string $sKey
58
- * @param string $sItemId
59
- * @return \FernleafSystems\Wordpress\Plugin\Shield\License\EddLicenseVO
60
- */
61
- public function activateLicense( $sStoreUrl, $sKey, $sItemId ) {
62
- return $this->commonLicenseAction( 'activate_license', $sStoreUrl, $sKey, $sItemId );
63
- }
64
-
65
- /**
66
- * @param string $sStoreUrl
67
- * @param string $sItemId
68
- * @return \FernleafSystems\Wordpress\Plugin\Shield\License\EddLicenseVO
69
- */
70
- public function activateLicenseKeyless( $sStoreUrl, $sItemId ) {
71
- return $this->activateLicense( $sStoreUrl, '', $sItemId );
72
- }
73
-
74
- /**
75
- * @param string $sStoreUrl
76
- * @param string $sKey
77
- * @param string $sItemId
78
- * @return \FernleafSystems\Wordpress\Plugin\Shield\License\EddLicenseVO|null
79
- */
80
- public function checkLicense( $sStoreUrl, $sKey, $sItemId ) {
81
- return $this->commonLicenseAction( 'check_license', $sStoreUrl, $sKey, $sItemId );
82
- }
83
-
84
- /**
85
- * @param string $sStoreUrl
86
- * @param string $sKey
87
- * @param string $sItemId
88
- * @return \FernleafSystems\Wordpress\Plugin\Shield\License\EddLicenseVO
89
- */
90
- public function deactivateLicense( $sStoreUrl, $sKey, $sItemId ) {
91
- return $this->commonLicenseAction( 'deactivate_license', $sStoreUrl, $sKey, $sItemId );
92
- }
93
-
94
- /**
95
- * @param string $sAction
96
- * @param string $sStoreUrl
97
- * @param string $sKey
98
- * @param string $sItemId
99
- * @return EddLicenseVO
100
- */
101
- private function commonLicenseAction( $sAction, $sStoreUrl, $sKey, $sItemId ) {
102
- $oWp = Services::WpGeneral();
103
- $aLicenseLookupParams = [
104
- 'timeout' => 60,
105
- 'body' => array_merge(
106
- [
107
- 'edd_action' => $sAction,
108
- 'license' => $sKey,
109
- 'item_id' => $sItemId,
110
- 'url' => $oWp->getHomeUrl(),
111
- 'alt_url' => $oWp->getWpUrl()
112
- ],
113
- $this->getRequestParams()
114
- )
115
- ];
116
-
117
- return ( new EddLicenseVO() )
118
- ->applyFromArray( $this->sendReq( $sStoreUrl, $aLicenseLookupParams, false ) )
119
- ->setLastRequestAt( Services::Request()->ts() );
120
- }
121
-
122
- /**
123
- * first attempts GET, then POST if the GET is successful but the data is not right
124
- * @param string $sUrl
125
- * @param array $aArgs
126
- * @param bool $bAsPost
127
- * @return array
128
- */
129
- private function sendReq( $sUrl, $aArgs, $bAsPost = false ) {
130
- $aResponse = [];
131
- $oHttpReq = Services::HttpRequest();
132
-
133
- if ( $bAsPost ) {
134
- if ( $oHttpReq->post( $sUrl, $aArgs ) ) {
135
- $aResponse = empty( $oHttpReq->lastResponse->body ) ? [] : @json_decode( $oHttpReq->lastResponse->body, true );
136
- }
137
- return $aResponse;
138
- }
139
- else if ( $oHttpReq->get( $sUrl, $aArgs ) ) {
140
- $aResponse = empty( $oHttpReq->lastResponse->body ) ? [] : @json_decode( $oHttpReq->lastResponse->body, true );
141
- if ( empty( $aResponse ) ) {
142
- $aResponse = $this->sendReq( $sUrl, $aArgs, true );
143
- }
144
- }
145
-
146
- return $aResponse;
147
- }
148
-
149
- /**
150
- * @param array $aData
151
- * @return \FernleafSystems\Wordpress\Plugin\Shield\License\EddLicenseVO
152
- */
153
- public function getLicenseVoFromData( $aData ) {
154
- return ( new \FernleafSystems\Wordpress\Plugin\Shield\License\EddLicenseVO() )->applyFromArray( $aData );
155
- }
156
-
157
- /**
158
- * @return array
159
- */
160
- public function getRequestParams() {
161
- return is_array( $this->aAdditionalRequestParams ) ? $this->aAdditionalRequestParams : [];
162
- }
163
-
164
- /**
165
- * @param array $aParams
166
- * @return $this
167
- */
168
- public function setRequestParams( $aParams = [] ) {
169
- $this->aAdditionalRequestParams = is_array( $aParams ) ? $aParams : [];
170
- return $this;
171
- }
172
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/common/icwp-foundation.php CHANGED
@@ -1,14 +1,13 @@
1
  <?php
2
 
 
 
 
 
3
  class ICWP_WPSF_Foundation {
4
 
5
  const DEFAULT_SERVICE_PREFIX = 'icwp_wpsf_';
6
 
7
- /**
8
- * @var array
9
- */
10
- private static $aDic;
11
-
12
  /**
13
  * @param string $sSuffix
14
  * @return string
@@ -16,238 +15,4 @@ class ICWP_WPSF_Foundation {
16
  protected function prefix( $sSuffix ) {
17
  return self::DEFAULT_SERVICE_PREFIX.$sSuffix;
18
  }
19
-
20
- /**
21
- * @return ICWP_WPSF_DataProcessor
22
- */
23
- static public function loadDP() {
24
- $sKey = 'icwp-data';
25
- if ( !self::isServiceReady( $sKey ) ) {
26
- self::setService( $sKey, ICWP_WPSF_DataProcessor::GetInstance() );
27
- }
28
- return self::getService( $sKey );
29
- }
30
-
31
- /**
32
- * @return ICWP_WPSF_WpFilesystem
33
- */
34
- static public function loadFS() {
35
- $sKey = 'icwp-wpfilesystem';
36
- if ( !self::isServiceReady( $sKey ) ) {
37
- self::setService( $sKey, ICWP_WPSF_WpFilesystem::GetInstance() );
38
- }
39
- return self::getService( $sKey );
40
- }
41
-
42
- /**
43
- * @return ICWP_WPSF_WpFunctions
44
- */
45
- static public function loadWp() {
46
- $sKey = 'icwp-wpfunctions';
47
- if ( !self::isServiceReady( $sKey ) ) {
48
- self::setService( $sKey, ICWP_WPSF_WpFunctions::GetInstance() );
49
- }
50
- return self::getService( $sKey );
51
- }
52
-
53
- /**
54
- * @return ICWP_WPSF_WpFunctions_Plugins
55
- * @deprecated
56
- */
57
- public function loadWpPlugins() {
58
- $sKey = 'icwp-wpfunctions-plugins';
59
- if ( !self::isServiceReady( $sKey ) ) {
60
- self::setService( $sKey, ICWP_WPSF_WpFunctions_Plugins::GetInstance() );
61
- }
62
- return self::getService( $sKey );
63
- }
64
-
65
- /**
66
- * @return ICWP_WPSF_WpFunctions_Themes
67
- * @deprecated
68
- */
69
- public function loadWpThemes() {
70
- $sKey = 'icwp-wpfunctions-themes';
71
- if ( !self::isServiceReady( $sKey ) ) {
72
- self::setService( $sKey, ICWP_WPSF_WpFunctions_Themes::GetInstance() );
73
- }
74
- return self::getService( $sKey );
75
- }
76
-
77
- /**
78
- * @return ICWP_WPSF_WpCron
79
- * @deprecated
80
- */
81
- static public function loadWpCronProcessor() {
82
- $sKey = 'icwp-wpcron';
83
- if ( !self::isServiceReady( $sKey ) ) {
84
- self::setService( $sKey, ICWP_WPSF_WpCron::GetInstance() );
85
- }
86
- return self::getService( $sKey );
87
- }
88
-
89
- /**
90
- * @return ICWP_WPSF_WpUpgrades
91
- * @deprecated
92
- */
93
- static public function loadWpUpgrades() {
94
- $sKey = 'icwp-wpupgrades';
95
- if ( !self::isServiceReady( $sKey ) ) {
96
- self::setService( $sKey, ICWP_WPSF_WpUpgrades::GetInstance() );
97
- }
98
- return self::getService( $sKey );
99
- }
100
-
101
- /**
102
- * @return ICWP_WPSF_WpDb
103
- */
104
- static public function loadDbProcessor() {
105
- $sKey = 'icwp-wpdb';
106
- if ( !self::isServiceReady( $sKey ) ) {
107
- self::setService( $sKey, ICWP_WPSF_WpDb::GetInstance() );
108
- }
109
- return self::getService( $sKey );
110
- }
111
-
112
- /**
113
- * @return ICWP_WPSF_Request
114
- */
115
- public function loadRequest() {
116
- $sKey = 'icwp-request';
117
- if ( !self::isServiceReady( $sKey ) ) {
118
- self::setService( $sKey, ICWP_WPSF_Request::GetInstance() );
119
- }
120
- return self::getService( $sKey );
121
- }
122
-
123
- /**
124
- * @return ICWP_WPSF_ServiceProviders
125
- */
126
- public function loadServiceProviders() {
127
- $sKey = 'icwp-serviceproviders';
128
- if ( !self::isServiceReady( $sKey ) ) {
129
- self::setService( $sKey, ICWP_WPSF_ServiceProviders::GetInstance() );
130
- }
131
- return self::getService( $sKey );
132
- }
133
-
134
- /**
135
- * @return ICWP_WPSF_WpIncludes
136
- * @deprecated
137
- */
138
- static public function loadWpIncludes() {
139
- $sKey = 'icwp-wpincludes';
140
- if ( !self::isServiceReady( $sKey ) ) {
141
- self::setService( $sKey, ICWP_WPSF_WpIncludes::GetInstance() );
142
- }
143
- return self::getService( $sKey );
144
- }
145
-
146
- /**
147
- * @param string $sTemplatePath
148
- * @return ICWP_WPSF_Render
149
- */
150
- static public function loadRenderer( $sTemplatePath = '' ) {
151
- $sKey = 'icwp-render';
152
- if ( !self::isServiceReady( $sKey ) ) {
153
- self::setService( $sKey, ICWP_WPSF_Render::GetInstance() );
154
- }
155
-
156
- /** @var ICWP_WPSF_Render $oR */
157
- $oR = self::getService( $sKey );
158
- if ( !empty( $sTemplatePath ) ) {
159
- $oR->setTemplateRoot( $sTemplatePath );
160
- }
161
- return ( clone $oR );
162
- }
163
-
164
- /**
165
- * @return ICWP_WPSF_WpAdminNotices
166
- */
167
- static public function loadWpNotices() {
168
- $sKey = 'wp-admin-notices';
169
- if ( !self::isServiceReady( $sKey ) ) {
170
- self::setService( $sKey, ICWP_WPSF_WpAdminNotices::GetInstance() );
171
- }
172
- return self::getService( $sKey );
173
- }
174
-
175
- /**
176
- * @return ICWP_WPSF_WpUsers
177
- */
178
- static public function loadWpUsers() {
179
- $sKey = 'wp-users';
180
- if ( !self::isServiceReady( $sKey ) ) {
181
- self::setService( $sKey, ICWP_WPSF_WpUsers::GetInstance() );
182
- }
183
- return self::getService( $sKey );
184
- }
185
-
186
- /**
187
- * @return ICWP_WPSF_WpComments
188
- */
189
- static public function loadWpComments() {
190
- $sKey = 'wp-comments';
191
- if ( !self::isServiceReady( $sKey ) ) {
192
- self::setService( $sKey, ICWP_WPSF_WpComments::GetInstance() );
193
- }
194
- return self::getService( $sKey );
195
- }
196
-
197
- /**
198
- * @return ICWP_WPSF_Edd
199
- */
200
- static public function loadEdd() {
201
- $sKey = 'icwp-edd';
202
- if ( !self::isServiceReady( $sKey ) ) {
203
- self::setService( $sKey, ICWP_WPSF_Edd::GetInstance() );
204
- }
205
- return self::getService( $sKey );
206
- }
207
-
208
- /**
209
- * @return array
210
- */
211
- static private function getDic() {
212
- if ( !is_array( self::$aDic ) ) {
213
- self::$aDic = [];
214
- }
215
- return self::$aDic;
216
- }
217
-
218
- /**
219
- * @param string $sService
220
- * @return mixed
221
- */
222
- static private function getService( $sService ) {
223
- $aDic = self::getDic();
224
- return $aDic[ $sService ];
225
- }
226
-
227
- /**
228
- * @param string $sService
229
- * @return bool
230
- */
231
- static private function isServiceReady( $sService ) {
232
- $aDic = self::getDic();
233
- return !empty( $aDic[ $sService ] );
234
- }
235
-
236
- /**
237
- * @param string $sServiceKey
238
- * @param mixed $oService
239
- */
240
- static private function setService( $sServiceKey, $oService ) {
241
- $aDic = self::getDic();
242
- $aDic[ $sServiceKey ] = $oService;
243
- self::$aDic = $aDic;
244
- }
245
-
246
- /**
247
- * @return ICWP_WPSF_WpAdminNotices
248
- * @deprecated
249
- */
250
- static public function loadAdminNoticesProcessor() {
251
- return self::loadWpNotices();
252
- }
253
  }
1
  <?php
2
 
3
+ /**
4
+ * Class ICWP_WPSF_Foundation
5
+ * @deprecated 8.4
6
+ */
7
  class ICWP_WPSF_Foundation {
8
 
9
  const DEFAULT_SERVICE_PREFIX = 'icwp_wpsf_';
10
 
 
 
 
 
 
11
  /**
12
  * @param string $sSuffix
13
  * @return string
15
  protected function prefix( $sSuffix ) {
16
  return self::DEFAULT_SERVICE_PREFIX.$sSuffix;
17
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
18
  }
src/common/icwp-optionsvo.php DELETED
@@ -1,991 +0,0 @@
1
- <?php
2
-
3
- use FernleafSystems\Wordpress\Services\Services;
4
-
5
- class ICWP_WPSF_OptionsVO extends ICWP_WPSF_Foundation {
6
-
7
- /**
8
- * @var array
9
- */
10
- protected $aOptionsValues;
11
-
12
- /**
13
- * @var array
14
- */
15
- protected $aOld;
16
-
17
- /**
18
- * @var array
19
- */
20
- protected $aRawOptionsConfigData;
21
-
22
- /**
23
- * @var boolean
24
- */
25
- protected $bNeedSave;
26
-
27
- /**
28
- * @var boolean
29
- */
30
- protected $bRebuildFromFile = false;
31
-
32
- /**
33
- * @var string
34
- */
35
- protected $aOptionsKeys;
36
-
37
- /**
38
- * @var string
39
- */
40
- protected $sOptionsStorageKey;
41
-
42
- /**
43
- * by default we load from saved
44
- * @var string
45
- */
46
- protected $bLoadFromSaved = true;
47
-
48
- /**
49
- * @var string
50
- */
51
- protected $sPathToConfig;
52
-
53
- /**
54
- */
55
- public function __construct() {
56
- }
57
-
58
- /**
59
- * @return bool
60
- */
61
- public function cleanTransientStorage() {
62
- return $this->loadWp()->deleteTransient( $this->getSpecTransientStorageKey() );
63
- }
64
-
65
- /**
66
- * @param bool $bDeleteFirst Used primarily with plugin reset
67
- * @param bool $bIsPremiumLicensed
68
- * @return bool
69
- */
70
- public function doOptionsSave( $bDeleteFirst = false, $bIsPremiumLicensed = false ) {
71
- if ( !$this->getNeedSave() ) {
72
- return true;
73
- }
74
- $this->cleanOptions();
75
- if ( !$bIsPremiumLicensed ) {
76
- $this->resetPremiumOptsToDefault();
77
- }
78
- $this->setNeedSave( false );
79
- if ( $bDeleteFirst ) {
80
- $this->loadWp()->deleteOption( $this->getOptionsStorageKey() );
81
- }
82
- return $this->loadWp()->updateOption( $this->getOptionsStorageKey(), $this->getAllOptionsValues() );
83
- }
84
-
85
- /**
86
- * @return bool
87
- */
88
- public function doOptionsDelete() {
89
- $oWp = $this->loadWp();
90
- $oWp->deleteTransient( $this->getSpecTransientStorageKey() );
91
- return $oWp->deleteOption( $this->getOptionsStorageKey() );
92
- }
93
-
94
- /**
95
- * @return array
96
- */
97
- public function getAllOptionsValues() {
98
- return $this->getStoredOptions();
99
- }
100
-
101
- /**
102
- * @return string
103
- */
104
- public function getSlug() {
105
- return $this->getFeatureProperty( 'slug' );
106
- }
107
-
108
- /**
109
- * Returns an array of all the transferable options and their values
110
- * @return array
111
- */
112
- public function getTransferableOptions() {
113
- $aTransferable = [];
114
-
115
- foreach ( $this->getRawData_AllOptions() as $nKey => $aOptionData ) {
116
- if ( !isset( $aOptionData[ 'transferable' ] ) || $aOptionData[ 'transferable' ] === true ) {
117
- $aTransferable[ $aOptionData[ 'key' ] ] = $this->getOpt( $aOptionData[ 'key' ] );
118
- }
119
- }
120
- return $aTransferable;
121
- }
122
-
123
- /**
124
- * Returns an array of all the options with the values for "sensitive" options masked out.
125
- * @return array
126
- */
127
- public function getOptionsMaskSensitive() {
128
-
129
- $aOptions = $this->getAllOptionsValues();
130
- foreach ( $this->getOptionsKeys() as $sKey ) {
131
- if ( !isset( $aOptions[ $sKey ] ) ) {
132
- $aOptions[ $sKey ] = $this->getOptDefault( $sKey );
133
- }
134
- }
135
- foreach ( $this->getRawData_AllOptions() as $nKey => $aOptionData ) {
136
- if ( isset( $aOptionData[ 'sensitive' ] ) && $aOptionData[ 'sensitive' ] === true ) {
137
- unset( $aOptions[ $aOptionData[ 'key' ] ] );
138
- }
139
- }
140
- return $aOptions;
141
- }
142
-
143
- /**
144
- * @param $sProperty
145
- * @return null|mixed
146
- */
147
- public function getFeatureProperty( $sProperty ) {
148
- $aRawConfig = $this->getRawData_FullFeatureConfig();
149
- return ( isset( $aRawConfig[ 'properties' ] ) && isset( $aRawConfig[ 'properties' ][ $sProperty ] ) ) ? $aRawConfig[ 'properties' ][ $sProperty ] : null;
150
- }
151
-
152
- /**
153
- * @param string
154
- * @return null|array
155
- */
156
- public function getFeatureDefinition( $sDefinition ) {
157
- $aRawConfig = $this->getRawData_FullFeatureConfig();
158
- return ( isset( $aRawConfig[ 'definitions' ] ) && isset( $aRawConfig[ 'definitions' ][ $sDefinition ] ) ) ? $aRawConfig[ 'definitions' ][ $sDefinition ] : null;
159
- }
160
-
161
- /**
162
- * @param string $sReq
163
- * @return null|mixed
164
- */
165
- public function getFeatureRequirement( $sReq ) {
166
- $aReqs = $this->getRawData_Requirements();
167
- return ( is_array( $aReqs ) && isset( $aReqs[ $sReq ] ) ) ? $aReqs[ $sReq ] : null;
168
- }
169
-
170
- /**
171
- * @return array
172
- */
173
- public function getAdminNotices() {
174
- $aRawConfig = $this->getRawData_FullFeatureConfig();
175
- return ( isset( $aRawConfig[ 'admin_notices' ] ) && is_array( $aRawConfig[ 'admin_notices' ] ) ) ? $aRawConfig[ 'admin_notices' ] : [];
176
- }
177
-
178
- /**
179
- * @return string
180
- */
181
- public function getFeatureTagline() {
182
- return $this->getFeatureProperty( 'tagline' );
183
- }
184
-
185
- /**
186
- * @return boolean
187
- */
188
- public function getIfLoadOptionsFromStorage() {
189
- return $this->bLoadFromSaved;
190
- }
191
-
192
- /**
193
- * Determines whether the given option key is a valid option
194
- * @param string
195
- * @return boolean
196
- */
197
- public function isValidOptionKey( $sOptionKey ) {
198
- return in_array( $sOptionKey, $this->getOptionsKeys() );
199
- }
200
-
201
- /**
202
- * @return array[]
203
- */
204
- public function getHiddenOptions() {
205
-
206
- $aOptionsData = [];
207
-
208
- foreach ( $this->getRawData_OptionsSections() as $nPosition => $aRawSection ) {
209
-
210
- // if hidden isn't specified we skip
211
- if ( !isset( $aRawSection[ 'hidden' ] ) || !$aRawSection[ 'hidden' ] ) {
212
- continue;
213
- }
214
- foreach ( $this->getRawData_AllOptions() as $aRawOption ) {
215
-
216
- if ( $aRawOption[ 'section' ] != $aRawSection[ 'slug' ] ) {
217
- continue;
218
- }
219
- $aOptionsData[ $aRawOption[ 'key' ] ] = $this->getOpt( $aRawOption[ 'key' ] );
220
- }
221
- }
222
- return $aOptionsData;
223
- }
224
-
225
- /**
226
- * @param string $sSlug
227
- * @return array|null
228
- */
229
- public function getSection( $sSlug ) {
230
- $aSections = $this->getSections();
231
- return isset( $aSections[ $sSlug ] ) ? $aSections[ $sSlug ] : null;
232
- }
233
-
234
- /**
235
- * @param bool $bIncludeHidden
236
- * @return array[]
237
- */
238
- public function getSections( $bIncludeHidden = false ) {
239
- $aSections = [];
240
- foreach ( $this->getRawData_OptionsSections() as $aRawSection ) {
241
- if ( $bIncludeHidden || !isset( $aRawSection[ 'hidden' ] ) || !$aRawSection[ 'hidden' ] ) {
242
- $aSections[ $aRawSection[ 'slug' ] ] = $aRawSection;
243
- }
244
- }
245
- return $aSections;
246
- }
247
-
248
- /**
249
- * @return array
250
- */
251
- public function getPrimarySection() {
252
- $aSec = [];
253
- foreach ( $this->getSections() as $aS ) {
254
- if ( isset( $aS[ 'primary' ] ) && $aS[ 'primary' ] ) {
255
- $aSec = $aS;
256
- break;
257
- }
258
- }
259
- return $aSec;
260
- }
261
-
262
- /**
263
- * @param string $sSlug
264
- * @return array
265
- */
266
- public function getSection_Requirements( $sSlug ) {
267
- $aSection = $this->getSection( $sSlug );
268
- $aReqs = ( is_array( $aSection ) && isset( $aSection[ 'reqs' ] ) ) ? $aSection[ 'reqs' ] : [];
269
- return array_merge(
270
- [
271
- 'php_min' => '5.2.4',
272
- 'wp_min' => '3.5.0',
273
- ],
274
- $aReqs
275
- );
276
- }
277
-
278
- /**
279
- * @param string $sSlug
280
- * @return array|null
281
- */
282
- public function getSectionHelpVideo( $sSlug ) {
283
- $aSection = $this->getSection( $sSlug );
284
- return ( is_array( $aSection ) && isset( $aSection[ 'help_video' ] ) ) ? $aSection[ 'help_video' ] : null;
285
- }
286
-
287
- /**
288
- * @param string $sSectionSlug
289
- * @return bool
290
- */
291
- public function isSectionReqsMet( $sSectionSlug ) {
292
- $aReqs = $this->getSection_Requirements( $sSectionSlug );
293
- $bMet = Services::Data()->getPhpVersionIsAtLeast( $aReqs[ 'php_min' ] )
294
- && Services::WpGeneral()->getWordpressIsAtLeastVersion( $aReqs[ 'wp_min' ] );
295
- return $bMet;
296
- }
297
-
298
- /**
299
- * @param string $sOptKey
300
- * @return bool
301
- */
302
- public function isOptReqsMet( $sOptKey ) {
303
- return $this->isSectionReqsMet( $this->getOptProperty( $sOptKey, 'section' ) );
304
- }
305
-
306
- /**
307
- * @return string[]
308
- */
309
- public function getVisibleOptionsKeys() {
310
- $aKeys = [];
311
-
312
- foreach ( $this->getRawData_AllOptions() as $aOptionDef ) {
313
- if ( isset( $aOptionDef[ 'hidden' ] ) && $aOptionDef[ 'hidden' ] ) {
314
- continue;
315
- }
316
- $aSection = $this->getSection( $aOptionDef[ 'section' ] );
317
- if ( empty( $aSection ) || ( isset( $aSection[ 'hidden' ] ) && $aSection[ 'hidden' ] ) ) {
318
- continue;
319
- }
320
-
321
- $aKeys[] = $aOptionDef[ 'key' ];
322
- }
323
-
324
- return $aKeys;
325
- }
326
-
327
- /**
328
- * @return array
329
- */
330
- public function getOptionsForPluginUse() {
331
-
332
- $aOptionsData = [];
333
-
334
- foreach ( $this->getRawData_OptionsSections() as $aRawSection ) {
335
-
336
- if ( isset( $aRawSection[ 'hidden' ] ) && $aRawSection[ 'hidden' ] ) {
337
- continue;
338
- }
339
-
340
- $aRawSection = array_merge(
341
- [
342
- 'primary' => false,
343
- 'options' => $this->getOptionsForSection( $aRawSection[ 'slug' ] ),
344
- 'help_video_id' => ''
345
- ],
346
- $aRawSection
347
- );
348
-
349
- if ( !empty( $aRawSection[ 'options' ] ) ) {
350
- $aOptionsData[] = $aRawSection;
351
- }
352
- }
353
-
354
- return $aOptionsData;
355
- }
356
-
357
- /**
358
- * @param string $sSectionSlug
359
- * @return array[]
360
- */
361
- protected function getOptionsForSection( $sSectionSlug ) {
362
-
363
- $aAllOptions = [];
364
- foreach ( $this->getRawData_AllOptions() as $aOptionDef ) {
365
-
366
- if ( ( $aOptionDef[ 'section' ] != $sSectionSlug ) || ( isset( $aOptionDef[ 'hidden' ] ) && $aOptionDef[ 'hidden' ] ) ) {
367
- continue;
368
- }
369
-
370
- if ( isset( $aOptionDef[ 'hidden' ] ) && $aOptionDef[ 'hidden' ] ) {
371
- continue;
372
- }
373
-
374
- $aOptionDef = array_merge(
375
- [
376
- 'link_info' => '',
377
- 'link_blog' => '',
378
- 'help_video_id' => '',
379
- 'value_options' => []
380
- ],
381
- $aOptionDef
382
- );
383
- $aOptionDef[ 'value' ] = $this->getOpt( $aOptionDef[ 'key' ] );
384
-
385
- if ( in_array( $aOptionDef[ 'type' ], [ 'select', 'multiple_select' ] ) ) {
386
- $aNewValueOptions = [];
387
- foreach ( $aOptionDef[ 'value_options' ] as $aValueOptions ) {
388
- $aNewValueOptions[ $aValueOptions[ 'value_key' ] ] = __( $aValueOptions[ 'text' ], 'wp-simple-firewall' );
389
- }
390
- $aOptionDef[ 'value_options' ] = $aNewValueOptions;
391
- }
392
-
393
- $aAllOptions[] = $aOptionDef;
394
- }
395
- return $aAllOptions;
396
- }
397
-
398
- /**
399
- * @return array
400
- */
401
- public function getAdditionalMenuItems() {
402
- return $this->getRawData_MenuItems();
403
- }
404
-
405
- /**
406
- * @return string
407
- */
408
- public function getNeedSave() {
409
- return $this->bNeedSave;
410
- }
411
-
412
- /**
413
- * @param string $sKey
414
- * @return mixed|null
415
- */
416
- public function getOldValue( $sKey ) {
417
- return $this->isOptChanged( $sKey ) ? $this->aOld[ $sKey ] : null;
418
- }
419
-
420
- /**
421
- * @param string $sOptionKey
422
- * @param mixed $mDefault
423
- * @return mixed
424
- */
425
- public function getOpt( $sOptionKey, $mDefault = false ) {
426
- $aOptionsValues = $this->getAllOptionsValues();
427
- if ( !isset( $aOptionsValues[ $sOptionKey ] ) && $this->isValidOptionKey( $sOptionKey ) ) {
428
- $this->setOpt( $sOptionKey, $this->getOptDefault( $sOptionKey, $mDefault ) );
429
- }
430
- return isset( $this->aOptionsValues[ $sOptionKey ] ) ? $this->aOptionsValues[ $sOptionKey ] : $mDefault;
431
- }
432
-
433
- /**
434
- * @param string $sOptionKey
435
- * @param mixed $mDefault
436
- * @return mixed|null
437
- */
438
- public function getOptDefault( $sOptionKey, $mDefault = null ) {
439
- foreach ( $this->getRawData_AllOptions() as $aOption ) {
440
- if ( $aOption[ 'key' ] == $sOptionKey ) {
441
- if ( isset( $aOption[ 'default' ] ) ) {
442
- $mDefault = $aOption[ 'default' ];
443
- break;
444
- }
445
- if ( isset( $aOption[ 'value' ] ) ) {
446
- $mDefault = $aOption[ 'value' ];
447
- break;
448
- }
449
- }
450
- }
451
- return $mDefault;
452
- }
453
-
454
- /**
455
- * @param string $sOptionKey
456
- * @return array
457
- */
458
- public function getOptDefinition( $sOptionKey ) {
459
- $aDef = [];
460
- foreach ( $this->getRawData_AllOptions() as $aOption ) {
461
- if ( $aOption[ 'key' ] == $sOptionKey ) {
462
- $aDef = $aOption;
463
- break;
464
- }
465
- }
466
- return $aDef;
467
- }
468
-
469
- /**
470
- * @param $sKey
471
- * @param mixed $mValueToTest
472
- * @param boolean $bStrict
473
- * @return bool
474
- */
475
- public function getOptIs( $sKey, $mValueToTest, $bStrict = false ) {
476
- $mOptionValue = $this->getOpt( $sKey );
477
- return $bStrict ? $mOptionValue === $mValueToTest : $mOptionValue == $mValueToTest;
478
- }
479
-
480
- /**
481
- * @param string $sKey
482
- * @return string|null
483
- */
484
- public function getOptionType( $sKey ) {
485
- $aDef = $this->getRawData_SingleOption( $sKey );
486
- if ( !empty( $aDef ) && isset( $aDef[ 'type' ] ) ) {
487
- return $aDef[ 'type' ];
488
- }
489
- return null;
490
- }
491
-
492
- /**
493
- * @return array
494
- */
495
- public function getOptionsKeys() {
496
- if ( !isset( $this->aOptionsKeys ) ) {
497
- $this->aOptionsKeys = [];
498
- foreach ( $this->getRawData_AllOptions() as $aOption ) {
499
- $this->aOptionsKeys[] = $aOption[ 'key' ];
500
- }
501
- $this->aOptionsKeys = array_merge( $this->aOptionsKeys, $this->getCommonStandardOptions() );
502
- }
503
- return $this->aOptionsKeys;
504
- }
505
-
506
- /**
507
- * @return string
508
- */
509
- public function getPathToConfig() {
510
- return $this->sPathToConfig;
511
- }
512
-
513
- /**
514
- * @return string
515
- */
516
- protected function getConfigModTime() {
517
- return Services::WpFs()->getModifiedTime( $this->getPathToConfig() );
518
- }
519
-
520
- /**
521
- * @return string
522
- */
523
- public function getOptionsStorageKey() {
524
- return $this->sOptionsStorageKey;
525
- }
526
-
527
- /**
528
- * @param string $sOptKey
529
- * @param string $sProperty
530
- * @return mixed|null
531
- */
532
- public function getOptProperty( $sOptKey, $sProperty ) {
533
- $aOpt = $this->getRawData_SingleOption( $sOptKey );
534
- return ( is_array( $aOpt ) && isset( $aOpt[ $sProperty ] ) ) ? $aOpt[ $sProperty ] : null;
535
- }
536
-
537
- /**
538
- * @return array
539
- */
540
- public function getStoredOptions() {
541
- try {
542
- return $this->loadOptionsValuesFromStorage();
543
- }
544
- catch ( Exception $oE ) {
545
- return [];
546
- }
547
- }
548
-
549
- /**
550
- * @return array
551
- */
552
- public function getRawData_FullFeatureConfig() {
553
- if ( empty( $this->aRawOptionsConfigData ) ) {
554
- $this->aRawOptionsConfigData = $this->readConfiguration();
555
- }
556
- return $this->aRawOptionsConfigData;
557
- }
558
-
559
- /**
560
- * Return the section of the Raw config that is the "options" key only.
561
- * @return array
562
- */
563
- protected function getRawData_AllOptions() {
564
- $aRaw = $this->getRawData_FullFeatureConfig();
565
- return ( isset( $aRaw[ 'options' ] ) && is_array( $aRaw[ 'options' ] ) ) ? $aRaw[ 'options' ] : [];
566
- }
567
-
568
- /**
569
- * Return the section of the Raw config that is the "options" key only.
570
- * @return array
571
- */
572
- protected function getRawData_OptionsSections() {
573
- $aAllRawOptions = $this->getRawData_FullFeatureConfig();
574
- return isset( $aAllRawOptions[ 'sections' ] ) ? $aAllRawOptions[ 'sections' ] : [];
575
- }
576
-
577
- /**
578
- * Return the section of the Raw config that is the "options" key only.
579
- * @return array
580
- */
581
- protected function getRawData_Requirements() {
582
- $aAllRawOptions = $this->getRawData_FullFeatureConfig();
583
- return isset( $aAllRawOptions[ 'requirements' ] ) ? $aAllRawOptions[ 'requirements' ] : [];
584
- }
585
-
586
- /**
587
- * Return the section of the Raw config that is the "options" key only.
588
- * @return array
589
- */
590
- protected function getRawData_MenuItems() {
591
- $aAllRawOptions = $this->getRawData_FullFeatureConfig();
592
- return isset( $aAllRawOptions[ 'menu_items' ] ) ? $aAllRawOptions[ 'menu_items' ] : [];
593
- }
594
-
595
- /**
596
- * Return the section of the Raw config that is the "options" key only.
597
- * @param string $sOptionKey
598
- * @return array
599
- */
600
- public function getRawData_SingleOption( $sOptionKey ) {
601
- foreach ( $this->getRawData_AllOptions() as $aOption ) {
602
- if ( isset( $aOption[ 'key' ] ) && ( $sOptionKey == $aOption[ 'key' ] ) ) {
603
- return $aOption;
604
- }
605
- }
606
- return null;
607
- }
608
-
609
- /**
610
- * @return boolean
611
- */
612
- public function getRebuildFromFile() {
613
- return $this->bRebuildFromFile;
614
- }
615
-
616
- /**
617
- * @param string $sKey
618
- * @return string
619
- */
620
- public function getSelectOptionValueText( $sKey ) {
621
- $sText = '';
622
- foreach ( $this->getOptDefinition( $sKey )[ 'value_options' ] as $aOpt ) {
623
- if ( $aOpt[ 'value_key' ] == $this->getOpt( $sKey ) ) {
624
- $sText = $aOpt[ 'text' ];
625
- break;
626
- }
627
- }
628
- return $sText;
629
- }
630
-
631
- /**
632
- * @return bool
633
- */
634
- public function isAccessRestricted() {
635
- $bAccessRestricted = $this->getFeatureProperty( 'access_restricted' );
636
- return is_null( $bAccessRestricted ) ? true : (bool)$bAccessRestricted;
637
- }
638
-
639
- /**
640
- * @return bool
641
- */
642
- public function isModulePremium() {
643
- return (bool)$this->getFeatureProperty( 'premium' );
644
- }
645
-
646
- /**
647
- * @return bool
648
- */
649
- public function isModuleRunIfWhitelisted() {
650
- $bState = $this->getFeatureProperty( 'run_if_whitelisted' );
651
- return is_null( $bState ) ? true : (bool)$bState;
652
- }
653
-
654
- /**
655
- * @return bool
656
- */
657
- public function isModuleRunUnderWpCli() {
658
- $bState = $this->getFeatureProperty( 'run_if_wpcli' );
659
- return is_null( $bState ) ? true : (bool)$bState;
660
- }
661
-
662
- /**
663
- * @return bool
664
- */
665
- public function isModuleRunIfVerifiedBot() {
666
- return (bool)$this->getFeatureProperty( 'run_if_verified_bot' );
667
- }
668
-
669
- /**
670
- * @param string $sKey
671
- * @return bool
672
- */
673
- public function isOptChanged( $sKey ) {
674
- return is_array( $this->aOld ) && isset( $this->aOld[ $sKey ] );
675
- }
676
-
677
- /**
678
- * @param string $sOptionKey
679
- * @return bool true if premium is set and true, false otherwise.
680
- */
681
- public function isOptPremium( $sOptionKey ) {
682
- return (bool)$this->getOptProperty( $sOptionKey, 'premium' );
683
- }
684
-
685
- /**
686
- * @param string $sOptionKey
687
- * @return boolean
688
- */
689
- public function resetOptToDefault( $sOptionKey ) {
690
- return $this->setOpt( $sOptionKey, $this->getOptDefault( $sOptionKey ) );
691
- }
692
-
693
- /**
694
- * Will traverse each premium option and set it to the default.
695
- */
696
- public function resetPremiumOptsToDefault() {
697
- foreach ( $this->getRawData_AllOptions() as $aOption ) {
698
- if ( isset( $aOption[ 'premium' ] ) && $aOption[ 'premium' ] ) {
699
- $this->resetOptToDefault( $aOption[ 'key' ] );
700
- }
701
- }
702
- }
703
-
704
- /**
705
- * @param string $sKey
706
- * @return $this
707
- */
708
- public function setOptionsStorageKey( $sKey ) {
709
- $this->sOptionsStorageKey = $sKey;
710
- return $this;
711
- }
712
-
713
- /**
714
- * @param boolean $bLoadFromSaved
715
- * @return $this
716
- */
717
- public function setIfLoadOptionsFromStorage( $bLoadFromSaved ) {
718
- $this->bLoadFromSaved = $bLoadFromSaved;
719
- return $this;
720
- }
721
-
722
- /**
723
- * @param boolean $bNeed
724
- */
725
- public function setNeedSave( $bNeed ) {
726
- $this->bNeedSave = $bNeed;
727
- }
728
-
729
- /**
730
- * @param boolean $bRebuild
731
- * @return $this
732
- */
733
- public function setRebuildFromFile( $bRebuild ) {
734
- $this->bRebuildFromFile = $bRebuild;
735
- return $this;
736
- }
737
-
738
- /**
739
- * @param array $aOptions
740
- */
741
- public function setMultipleOptions( $aOptions ) {
742
- if ( is_array( $aOptions ) ) {
743
- foreach ( $aOptions as $sKey => $mValue ) {
744
- $this->setOpt( $sKey, $mValue );
745
- }
746
- }
747
- }
748
-
749
- /**
750
- * @param string $sOptKey
751
- * @param mixed $mNewValue
752
- * @return mixed
753
- */
754
- public function setOpt( $sOptKey, $mNewValue ) {
755
-
756
- // We can't use getOpt() to find the current value since we'll create an infinite loop
757
- $aOptionsValues = $this->getAllOptionsValues();
758
- $mCurrent = isset( $aOptionsValues[ $sOptKey ] ) ? $aOptionsValues[ $sOptKey ] : null;
759
-
760
- $mNewValue = $this->ensureOptValueState( $sOptKey, $mNewValue );
761
-
762
- // Here we try to ensure that values that are repeatedly changed properly reflect their changed
763
- // states, as they may be reverted back to their original state and we "think" it's been changed.
764
- $bValueIsDifferent = serialize( $mCurrent ) !== serialize( $mNewValue );
765
- // basically if we're actually resetting back to the original value
766
- $bIsResetting = $bValueIsDifferent && $this->isOptChanged( $sOptKey )
767
- && ( serialize( $this->getOldValue( $sOptKey ) ) === serialize( $mNewValue ) );
768
-
769
- if ( $bValueIsDifferent && $this->verifyCanSet( $sOptKey, $mNewValue ) ) {
770
- $this->setNeedSave( true );
771
-
772
- //Load the config and do some pre-set verification where possible. This will slowly grow.
773
- $aOption = $this->getRawData_SingleOption( $sOptKey );
774
- if ( !empty( $aOption[ 'type' ] ) ) {
775
- if ( $aOption[ 'type' ] == 'boolean' && !is_bool( $mNewValue ) ) {
776
- return $this->resetOptToDefault( $sOptKey );
777
- }
778
- }
779
- $this->setOldOptValue( $sOptKey, $mCurrent );
780
- $this->aOptionsValues[ $sOptKey ] = $mNewValue;
781
- }
782
-
783
- if ( $bIsResetting ) {
784
- unset( $this->aOld[ $sOptKey ] );
785
- }
786
-
787
- return true;
788
- }
789
-
790
- /**
791
- * Ensures that set options values are of the correct type
792
- * @param string $sOptKey
793
- * @param mixed $mValue
794
- * @return mixed
795
- */
796
- private function ensureOptValueState( $sOptKey, $mValue ) {
797
- switch ( $this->getOptionType( $sOptKey ) ) {
798
- case 'integer':
799
- $mValue = (int)$mValue;
800
- break;
801
-
802
- case 'text':
803
- case 'email':
804
- $mValue = (string)$mValue;
805
- break;
806
- }
807
- return $mValue;
808
- }
809
-
810
- /**
811
- * @param string $sOptKey
812
- * @param mixed $mPotentialValue
813
- * @return bool
814
- */
815
- private function verifyCanSet( $sOptKey, $mPotentialValue ) {
816
- $bValid = true;
817
-
818
- switch ( $this->getOptionType( $sOptKey ) ) {
819
-
820
- case 'integer':
821
- $nMin = $this->getOptProperty( $sOptKey, 'min' );
822
- if ( !is_null( $nMin ) ) {
823
- $bValid = $mPotentialValue >= $nMin;
824
- }
825
- $nMax = $this->getOptProperty( $sOptKey, 'max' );
826
- if ( !is_null( $nMax ) ) {
827
- $bValid = $mPotentialValue <= $nMax;
828
- }
829
- break;
830
-
831
- case 'email':
832
- $bValid = empty( $mPotentialValue ) || Services::Data()->validEmail( $mPotentialValue );
833
- break;
834
- }
835
- return $bValid;
836
- }
837
-
838
- /**
839
- * @param string $sOptionKey
840
- * @param mixed $mValue
841
- * @return $this
842
- */
843
- private function setOldOptValue( $sOptionKey, $mValue ) {
844
- if ( !is_array( $this->aOld ) ) {
845
- $this->aOld = [];
846
- }
847
- if ( !isset( $this->aOld[ $sOptionKey ] ) ) {
848
- $this->aOld[ $sOptionKey ] = $mValue;
849
- }
850
- return $this;
851
- }
852
-
853
- /**
854
- * @param string $sOptionKey
855
- * @return mixed
856
- */
857
- public function unsetOpt( $sOptionKey ) {
858
-
859
- unset( $this->aOptionsValues[ $sOptionKey ] );
860
- $this->setNeedSave( true );
861
- return true;
862
- }
863
-
864
- /** PRIVATE STUFF */
865
-
866
- /**
867
- * @return array
868
- */
869
- protected function getCommonStandardOptions() {
870
- return [ 'help_video_options' ];
871
- }
872
-
873
- /**
874
- */
875
- private function cleanOptions() {
876
- if ( !empty( $this->aOptionsValues ) && is_array( $this->aOptionsValues ) ) {
877
- $this->aOptionsValues = array_intersect_key(
878
- $this->getAllOptionsValues(),
879
- array_flip( $this->getOptionsKeys() )
880
- );
881
- }
882
- }
883
-
884
- /**
885
- * @param bool $bReload
886
- * @return array|mixed
887
- * @throws Exception
888
- */
889
- private function loadOptionsValuesFromStorage( $bReload = false ) {
890
-
891
- if ( $bReload || empty( $this->aOptionsValues ) ) {
892
-
893
- if ( $this->getIfLoadOptionsFromStorage() ) {
894
-
895
- $sStorageKey = $this->getOptionsStorageKey();
896
- if ( empty( $sStorageKey ) ) {
897
- throw new Exception( 'Options Storage Key Is Empty' );
898
- }
899
- $this->aOptionsValues = $this->loadWp()->getOption( $sStorageKey, [] );
900
- }
901
- }
902
- if ( !is_array( $this->aOptionsValues ) ) {
903
- $this->aOptionsValues = [];
904
- $this->setNeedSave( true );
905
- }
906
- return $this->aOptionsValues;
907
- }
908
-
909
- /**
910
- * @return array
911
- */
912
- private function readConfiguration() {
913
- $oWp = $this->loadWp();
914
-
915
- $sTransientKey = $this->getSpecTransientStorageKey();
916
- $aConfig = $oWp->getTransient( $sTransientKey );
917
-
918
- $bRebuild = $this->getRebuildFromFile() || empty( $aConfig );
919
- if ( !$bRebuild && !empty( $aConfig ) && is_array( $aConfig ) ) {
920
-
921
- if ( !isset( $aConfig[ 'meta_modts' ] ) ) {
922
- $aConfig[ 'meta_modts' ] = 0;
923
- }
924
- $bRebuild = $this->getConfigModTime() > $aConfig[ 'meta_modts' ];
925
- }
926
-
927
- if ( $bRebuild ) {
928
- try {
929
- $aConfig = $this->readConfigurationJson();
930
- }
931
- catch ( Exception $oE ) {
932
- if ( Services::WpGeneral()->isDebug() ) {
933
- trigger_error( $oE->getMessage() );
934
- }
935
- $aConfig = [];
936
- }
937
- $aConfig[ 'meta_modts' ] = $this->getConfigModTime();
938
- $oWp->setTransient( $sTransientKey, $aConfig, DAY_IN_SECONDS );
939
- }
940
-
941
- $this->setRebuildFromFile( $bRebuild );
942
- return $aConfig;
943
- }
944
-
945
- /**
946
- * @return array
947
- * @throws Exception
948
- */
949
- private function readConfigurationJson() {
950
- $aConfig = json_decode( $this->readConfigurationFileContents(), true );
951
- if ( empty( $aConfig ) ) {
952
- throw new Exception( sprintf( 'Reading JSON configuration from file "%s" failed.', $this->getSlug() ) );
953
- }
954
- return $aConfig;
955
- }
956
-
957
- /**
958
- * @return string
959
- * @throws Exception
960
- */
961
- private function readConfigurationFileContents() {
962
- if ( !$this->getConfigFileExists() ) {
963
- throw new Exception( sprintf( 'Configuration file "%s" does not exist.', $this->getPathToConfig() ) );
964
- }
965
- return Services::Data()->readFileContentsUsingInclude( $this->getPathToConfig() );
966
- }
967
-
968
- /**
969
- * @return string
970
- */
971
- private function getSpecTransientStorageKey() {
972
- return 'icwp_'.md5( $this->getPathToConfig() );
973
- }
974
-
975
- /**
976
- * @return bool
977
- */
978
- private function getConfigFileExists() {
979
- $sPath = $this->getPathToConfig();
980
- return !empty( $sPath ) && Services::WpFs()->isFile( $sPath );
981
- }
982
-
983
- /**
984
- * @param string $sPathToConfig
985
- * @return $this
986
- */
987
- public function setPathToConfig( $sPathToConfig ) {
988
- $this->sPathToConfig = $sPathToConfig;
989
- return $this;
990
- }
991
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/common/icwp-render.php DELETED
@@ -1,316 +0,0 @@
1
- <?php
2
-
3
- class ICWP_WPSF_Render extends ICWP_WPSF_Foundation {
4
-
5
- const TEMPLATE_ENGINE_TWIG = 0;
6
- const TEMPLATE_ENGINE_PHP = 1;
7
- const TEMPLATE_ENGINE_HTML = 2;
8
-
9
- /**
10
- * @var ICWP_WPSF_Render
11
- */
12
- protected static $oInstance = null;
13
-
14
- private function __construct() {
15
- }
16
-
17
- /**
18
- * @return ICWP_WPSF_Render
19
- */
20
- public static function GetInstance() {
21
- if ( is_null( self::$oInstance ) ) {
22
- self::$oInstance = new self();
23
- }
24
- return self::$oInstance;
25
- }
26
-
27
- /**
28
- * @var array
29
- */
30
- protected $aRenderVars;
31
-
32
- /**
33
- * @var string
34
- */
35
- protected $sTemplateRootMain;
36
-
37
- /**
38
- * @var string
39
- */
40
- protected $aTemplateRoots;
41
-
42
- /**
43
- * @var string
44
- */
45
- protected $sTemplate;
46
-
47
- /**
48
- * @var int
49
- */
50
- protected $nTemplateEngine;
51
-
52
- /**
53
- * @return string
54
- * @throws \Exception
55
- */
56
- public function render() {
57
-
58
- switch ( $this->getTemplateEngine() ) {
59
-
60
- case self::TEMPLATE_ENGINE_TWIG :
61
- $sOutput = $this->renderTwig();
62
- break;
63
-
64
- case self::TEMPLATE_ENGINE_HTML :
65
- $sOutput = $this->renderHtml();
66
- break;
67
-
68
- default:
69
- $sOutput = $this->renderPhp();
70
- break;
71
- }
72
- return $sOutput;
73
- }
74
-
75
- /**
76
- * @return string
77
- */
78
- private function renderHtml() {
79
- ob_start();
80
- @include( $this->getTemplateRootMain().ltrim( $this->getTemplate(), DIRECTORY_SEPARATOR ) );
81
- return ob_get_clean();
82
- }
83
-
84
- /**
85
- * @return string
86
- */
87
- private function renderPhp() {
88
- if ( count( $this->getRenderVars() ) > 0 ) {
89
- extract( $this->getRenderVars() );
90
- }
91
-
92
- $sTemplate = $this->getTemplateRootMain().ltrim( $this->getTemplate(), DIRECTORY_SEPARATOR );
93
- if ( $this->loadFS()->isFile( $sTemplate ) ) {
94
- ob_start();
95
- include( $sTemplate );
96
- $sContents = ob_get_clean();
97
- }
98
- else {
99
- $sContents = 'Error: Template file not found: '.$sTemplate;
100
- }
101
-
102
- return $sContents;
103
- }
104
-
105
- /**
106
- * @return string
107
- * @throws Exception
108
- */
109
- private function renderTwig() {
110
- return $this->getTwigEnvironment()->render( $this->getTemplate(), $this->getRenderVars() );
111
- }
112
-
113
- /**
114
- */
115
- public function display() {
116
- echo $this->render();
117
- return $this;
118
- }
119
-
120
- /**
121
- * @return $this
122
- */
123
- public function clearRenderVars() {
124
- return $this->setRenderVars( [] );
125
- }
126
-
127
- /**
128
- * @return \Twig\Environment|\Twig_Environment
129
- */
130
- protected function getTwigEnvironment() {
131
- $aConf = [
132
- 'debug' => true,
133
- 'strict_variables' => true,
134
- ];
135
- if ( class_exists( 'Twig_Environment' ) ) {
136
- $oEnv = new Twig_Environment( new Twig_Loader_Filesystem( $this->getTemplateRootMain() ), $aConf );
137
- }
138
- else {
139
- $oEnv = new \Twig\Environment( new \Twig\Loader\FilesystemLoader( $this->getTemplateRootMain() ), $aConf );
140
- }
141
- return $oEnv;
142
- }
143
-
144
- /**
145
- * @return string
146
- */
147
- public function getTemplate() {
148
- $this->sTemplate = \FernleafSystems\Wordpress\Services\Services::Data()
149
- ->addExtensionToFilePath( $this->sTemplate, $this->getEngineStub() );
150
- return $this->sTemplate;
151
- }
152
-
153
- /**
154
- * @return int
155
- */
156
- public function getTemplateEngine() {
157
- if ( !isset( $this->nTemplateEngine )
158
- || !in_array( $this->nTemplateEngine, [
159
- self::TEMPLATE_ENGINE_TWIG,
160
- self::TEMPLATE_ENGINE_PHP,
161
- self::TEMPLATE_ENGINE_HTML
162
- ] ) ) {
163
- $this->nTemplateEngine = self::TEMPLATE_ENGINE_PHP;
164
- }
165
- return $this->nTemplateEngine;
166
- }
167
-
168
- /**
169
- * @param string $sTemplate
170
- * @return string
171
- */
172
- public function getTemplateExists( $sTemplate = '' ) {
173
- $sFullPath = $this->getTemplateFullPath( $sTemplate );
174
- return $this->loadFS()->exists( $sFullPath );
175
- }
176
-
177
- /**
178
- * @param string $sTemplate
179
- * @return string
180
- */
181
- public function getTemplateFullPath( $sTemplate = '' ) {
182
- if ( empty( $sTemplate ) ) {
183
- $sTemplate = $this->getTemplate();
184
- }
185
- $sTemplate = \FernleafSystems\Wordpress\Services\Services::Data()
186
- ->addExtensionToFilePath( $sTemplate, $this->getEngineStub() );
187
- return path_join( $this->getTemplateRootMain(), $sTemplate );
188
- }
189
-
190
- /**
191
- * @return string
192
- */
193
- public function getTemplateRootMain() {
194
- $sPath = rtrim( $this->sTemplateRootMain, DIRECTORY_SEPARATOR );
195
- $sStub = $this->getEngineStub();
196
- if ( !preg_match( sprintf( '#%s$#', $sStub ), $sPath ) ) {
197
- $sPath = $sPath.DIRECTORY_SEPARATOR.$sStub;
198
- }
199
- return $sPath.DIRECTORY_SEPARATOR;
200
- }
201
-
202
- /**
203
- * For use with Twig
204
- * @return array
205
- */
206
- public function getTemplateRoots() {
207
- if ( !is_array( $this->aTemplateRoots ) ) {
208
- $this->aTemplateRoots = [];
209
- }
210
- array_unshift( $this->aTemplateRoots, $this->getTemplateRootMain() );
211
- return array_unique( array_filter( $this->aTemplateRoots ) );
212
- }
213
-
214
- /**
215
- * @return array
216
- */
217
- public function getRenderVars() {
218
- return $this->aRenderVars;
219
- }
220
-
221
- /**
222
- * @param array $aVars
223
- * @return $this
224
- */
225
- public function setRenderVars( $aVars ) {
226
- $this->aRenderVars = $aVars;
227
- return $this;
228
- }
229
-
230
- /**
231
- * @param string $sPath
232
- * @return $this
233
- */
234
- public function setTemplate( $sPath ) {
235
- // if ( !preg_match( '#\.twig$#', $sPath ) ) {
236
- // $sPath = $sPath . '.twig';
237
- // }
238
- $this->sTemplate = $sPath;
239
- return $this;
240
- }
241
-
242
- /**
243
- * @return $this
244
- */
245
- public function setTemplateEngineHtml() {
246
- return $this->setTemplateEngine( self::TEMPLATE_ENGINE_HTML );
247
- }
248
-
249
- /**
250
- * @return $this
251
- */
252
- public function setTemplateEnginePhp() {
253
- return $this->setTemplateEngine( self::TEMPLATE_ENGINE_PHP );
254
- }
255
-
256
- /**
257
- * @return $this
258
- */
259
- public function setTemplateEngineTwig() {
260
- return $this->setTemplateEngine( self::TEMPLATE_ENGINE_TWIG );
261
- }
262
-
263
- /**
264
- * @param int $nEngine
265
- * @return $this
266
- */
267
- protected function setTemplateEngine( $nEngine ) {
268
- $this->nTemplateEngine = $nEngine;
269
- return $this;
270
- }
271
-
272
- /**
273
- * @param string $sPath
274
- * @return $this
275
- */
276
- public function addTemplateRoot( $sPath ) {
277
- $aRoots = $this->getTemplateRoots();
278
- $aRoots[] = $sPath;
279
- $this->aTemplateRoots = $aRoots;
280
- return $this;
281
- }
282
-
283
- /**
284
- * @param string $sPath
285
- * @return $this
286
- */
287
- public function setTemplateRoot( $sPath ) {
288
- $this->sTemplateRootMain = $sPath;
289
- return $this;
290
- }
291
-
292
- /**
293
- * @return string
294
- */
295
- private function getEngineStub() {
296
- switch ( $this->getTemplateEngine() ) {
297
-
298
- case self::TEMPLATE_ENGINE_TWIG:
299
- $sStub = 'twig';
300
- break;
301
-
302
- case self::TEMPLATE_ENGINE_HTML:
303
- $sStub = 'html';
304
- break;
305
-
306
- case self::TEMPLATE_ENGINE_PHP:
307
- $sStub = 'php';
308
- break;
309
-
310
- default:
311
- $sStub = 'php';
312
- break;
313
- }
314
- return $sStub;
315
- }
316
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/common/icwp-request.php DELETED
@@ -1,310 +0,0 @@
1
- <?php
2
-
3
- class ICWP_WPSF_Request extends ICWP_WPSF_Foundation {
4
-
5
- /**
6
- * @var ICWP_WPSF_Request
7
- */
8
- protected static $oInstance = null;
9
-
10
- /**
11
- * @var int
12
- */
13
- protected static $nTime = null;
14
-
15
- /**
16
- * @var float
17
- */
18
- protected static $nMicroTime = null;
19
-
20
- /**
21
- * @var array
22
- */
23
- protected $aRequestUriParts;
24
-
25
- protected function __construct() {
26
- }
27
-
28
- /**
29
- * @return ICWP_WPSF_Request
30
- */
31
- public static function GetInstance() {
32
- if ( is_null( self::$oInstance ) ) {
33
- self::$oInstance = new self();
34
- }
35
- return self::$oInstance;
36
- }
37
-
38
- /**
39
- * @param string $sKey
40
- * @param string $mDefault
41
- * @param bool $bTrim -automatically trim whitespace
42
- * @return mixed|null
43
- */
44
- public function cookie( $sKey, $mDefault = null, $bTrim = true ) {
45
- $mVal = $this->loadDP()->arrayFetch( $_COOKIE, $sKey, $mDefault );
46
- return ( $bTrim && is_scalar( $mVal ) ) ? trim( $mVal ) : $mVal;
47
- }
48
-
49
- /**
50
- * @param string $sKey
51
- * @param mixed $mDefault
52
- * @return mixed|null
53
- */
54
- public function env( $sKey, $mDefault = null ) {
55
- return $this->loadDP()->arrayFetch( $_ENV, $sKey, $mDefault );
56
- }
57
-
58
- /**
59
- * @param string $sKey
60
- * @param null $mDefault
61
- * @param bool $bTrim -automatically trim whitespace
62
- * @return mixed|null
63
- */
64
- public function post( $sKey, $mDefault = null, $bTrim = true ) {
65
- $mVal = $this->loadDP()->arrayFetch( $_POST, $sKey, $mDefault );
66
- return ( $bTrim && is_scalar( $mVal ) ) ? trim( $mVal ) : $mVal;
67
- }
68
-
69
- /**
70
- * @param string $sKey
71
- * @param null $mDefault
72
- * @param bool $bTrim -automatically trim whitespace
73
- * @return mixed|null
74
- */
75
- public function query( $sKey, $mDefault = null, $bTrim = true ) {
76
- $mVal = $this->loadDP()->arrayFetch( $_GET, $sKey, $mDefault );
77
- return ( $bTrim && is_scalar( $mVal ) ) ? trim( $mVal ) : $mVal;
78
- }
79
-
80
- /**
81
- * @param string $sKey
82
- * @param null $mDefault
83
- * @param bool $bTrim -automatically trim whitespace
84
- * @return mixed|null
85
- */
86
- public function server( $sKey, $mDefault = null, $bTrim = true ) {
87
- $mVal = $this->loadDP()->arrayFetch( $_SERVER, $sKey, $mDefault );
88
- return ( $bTrim && is_scalar( $mVal ) ) ? trim( $mVal ) : $mVal;
89
- }
90
-
91
- /**
92
- * @param string $sKey
93
- * @param null $mDefault
94
- * @param bool $bIncludeCookie
95
- * @param bool $bTrim -automatically trim whitespace
96
- * @return mixed|null
97
- */
98
- public function request( $sKey, $bIncludeCookie = false, $mDefault = null, $bTrim = true ) {
99
- $mVal = $this->post( $sKey, null, $bTrim );
100
- if ( is_null( $mVal ) ) {
101
- $mVal = $this->query( $sKey, null, $bTrim );
102
- if ( is_null( $mVal && $bIncludeCookie ) ) {
103
- $mVal = $this->cookie( $sKey );
104
- }
105
- }
106
- return is_null( $mVal ) ? $mDefault : ( $bTrim && is_scalar( $mVal ) ) ? trim( $mVal ) : $mVal;
107
- }
108
-
109
- /**
110
- * @return string
111
- */
112
- public function getHost() {
113
- return $this->server( 'HTTP_HOST' );
114
- }
115
-
116
- /**
117
- * @return string
118
- */
119
- public function getMethod() {
120
- $sRequestMethod = $this->server( 'REQUEST_METHOD' );
121
- return ( empty( $sRequestMethod ) ? 'get' : strtolower( $sRequestMethod ) );
122
- }
123
-
124
- /**
125
- * @param bool $bIncludeCookie
126
- * @return array
127
- */
128
- public function getParams( $bIncludeCookie = true ) {
129
- $aParams = array_merge( $_GET, $_POST );
130
- return $bIncludeCookie ? array_merge( $aParams, $_COOKIE ) : $aParams;
131
- }
132
-
133
- /**
134
- * @return string URI Path in lowercase
135
- */
136
- public function getPath() {
137
- return $this->getUriParts()[ 'path' ];
138
- }
139
-
140
- /**
141
- * @return string
142
- */
143
- public function getUri() {
144
- return $this->server( 'REQUEST_URI' );
145
- }
146
-
147
- /**
148
- * @return array
149
- */
150
- public function getUriParts() {
151
- if ( !isset( $this->aRequestUriParts ) ) {
152
- $aExploded = explode( '?', $this->getUri(), 2 );
153
- $this->aRequestUriParts = [
154
- 'path' => empty( $aExploded[ 0 ] ) ? '' : $aExploded[ 0 ],
155
- 'query' => empty( $aExploded[ 1 ] ) ? '' : $aExploded[ 1 ],
156
- ];
157
- }
158
- return $this->aRequestUriParts;
159
- }
160
-
161
- /**
162
- * @return string
163
- */
164
- public function getUserAgent() {
165
- return $this->server( 'HTTP_USER_AGENT' );
166
- }
167
-
168
- /**
169
- * @return string|null
170
- */
171
- public function getScriptName() {
172
- $sScriptName = $this->server( 'SCRIPT_NAME' );
173
- return !empty( $sScriptName ) ? $sScriptName : $this->server( 'PHP_SELF' );
174
- }
175
-
176
- /**
177
- * @return bool
178
- */
179
- public function isMethodPost() {
180
- return ( $this->getMethod() == 'post' );
181
- }
182
-
183
- /**
184
- * TODO: scrap?
185
- * Taken from http://www.phacks.net/detecting-search-engine-bot-and-web-spiders/
186
- */
187
- public function isSearchEngineBot() {
188
-
189
- $sUserAgent = $this->server( 'HTTP_USER_AGENT' );
190
- if ( empty( $sUserAgent ) ) {
191
- return false;
192
- }
193
-
194
- $sBots = 'Googlebot|bingbot|Twitterbot|Baiduspider|ia_archiver|R6_FeedFetcher|NetcraftSurveyAgent'
195
- .'|Sogou web spider|Yahoo! Slurp|facebookexternalhit|PrintfulBot|msnbot|UnwindFetchor|urlresolver|Butterfly|TweetmemeBot';
196
-
197
- return ( preg_match( "/$sBots/", $sUserAgent ) > 0 );
198
- }
199
-
200
- /**
201
- * @param string $sRequestedUriPath
202
- * @param string $sHostName - you can also send a full and valid URL
203
- */
204
- public function sendResponseApache404( $sRequestedUriPath = '', $sHostName = '' ) {
205
- if ( empty( $sRequestedUriPath ) ) {
206
- $sRequestedUriPath = $this->server( 'REQUEST_URI' );
207
- }
208
-
209
- if ( empty( $sHostName ) ) {
210
- $sHostName = $this->server( 'SERVER_NAME' );
211
- }
212
- else if ( filter_var( $sHostName, FILTER_VALIDATE_URL ) ) {
213
- $sHostName = parse_url( $sRequestedUriPath, PHP_URL_HOST );
214
- }
215
-
216
- $bSsl = is_ssl() || $this->server( 'HTTP_X_FORWARDED_PROTO' ) == 'https';
217
- header( 'HTTP/1.1 404 Not Found' );
218
- $nPort = $bSsl ? 443 : (int)$this->server( 'SERVER_PORT' );
219
- $sDie = sprintf(
220
- '<html><head><title>404 Not Found</title><style type="text/css"></style></head><body><h1>Not Found</h1><p>The requested URL %s was not found on this server.</p><p>Additionally, a 404 Not Found error was encountered while trying to use an ErrorDocument to handle the request.</p><hr><address>Apache Server at %s Port %s</address></body></html>',
221
- preg_replace( '#[^a-z0-9_&;=%/-]#i', '', esc_html( $sRequestedUriPath ) ),
222
- $sHostName,
223
- empty( $nPort ) ? 80 : $nPort
224
- );
225
- die( $sDie );
226
- }
227
-
228
- /**
229
- * @param string $sStringContent
230
- * @param string $sFilename
231
- */
232
- public function downloadStringAsFile( $sStringContent, $sFilename ) {
233
- header( "Content-type: application/octet-stream" );
234
- header( "Content-disposition: attachment; filename=".$sFilename );
235
- header( "Content-Transfer-Encoding: binary" );
236
- header( "Content-Length: ".strlen( $sStringContent ) );
237
- echo $sStringContent;
238
- die();
239
- }
240
-
241
- /**
242
- * @param $sKey
243
- * @param $mValue
244
- * @param int $nExpireLength
245
- * @param null $sPath
246
- * @param null $sDomain
247
- * @param bool $bSsl
248
- * @return bool
249
- */
250
- public function setCookie( $sKey, $mValue, $nExpireLength = 3600, $sPath = null, $sDomain = null, $bSsl = true ) {
251
- $_COOKIE[ $sKey ] = $mValue;
252
- if ( function_exists( 'headers_sent' ) && headers_sent() ) {
253
- return false;
254
- }
255
- return setcookie(
256
- $sKey,
257
- $mValue,
258
- (int)( $this->ts() + $nExpireLength ),
259
- ( is_null( $sPath ) && defined( 'COOKIEPATH' ) ) ? COOKIEPATH : $sPath,
260
- ( is_null( $sDomain ) && defined( 'COOKIE_DOMAIN' ) ) ? COOKIE_DOMAIN : $sDomain,
261
- $bSsl && is_ssl()
262
- );
263
- }
264
-
265
- /**
266
- * @param string $sKey
267
- * @return bool
268
- */
269
- public function setDeleteCookie( $sKey ) {
270
- if ( isset( $_COOKIE[ $sKey ] ) ) {
271
- unset( $_COOKIE[ $sKey ] );
272
- }
273
- return $this->setCookie( $sKey, '', -3600 );
274
- }
275
-
276
- /**
277
- * @return int
278
- */
279
- public function ts() {
280
- if ( !isset( self::$nTime ) ) {
281
- self::$nTime = time();
282
- self::$nMicroTime = function_exists( 'microtime' ) ? @microtime( true ) : false;
283
- }
284
- return self::$nTime;
285
- }
286
-
287
- /**
288
- * @param bool $bMillisecondOnly
289
- * @return int
290
- */
291
- public function mts( $bMillisecondOnly = false ) {
292
- $nT = $this->ts();
293
- if ( empty( self::$nMicroTime ) ) {
294
- $nT = $bMillisecondOnly ? 0 : $nT;
295
- }
296
- else {
297
- $nT = $bMillisecondOnly ? preg_replace( '#^[0-9]+\.#', '', self::$nMicroTime ) : self::$nMicroTime;
298
- }
299
- return $nT;
300
- }
301
-
302
- /**
303
- * alias
304
- * @return int
305
- * @deprecated
306
- */
307
- public function time() {
308
- return $this->ts();
309
- }
310
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/common/icwp-serviceproviders.php CHANGED
@@ -10,11 +10,6 @@ class ICWP_WPSF_ServiceProviders extends ICWP_WPSF_Foundation {
10
  const URL_STATUS_CAKE_IPS = 'https://app.statuscake.com/Workfloor/Locations.php?format=json';
11
  const URL_ICONTROLWP_IPS = 'https://serviceips.icontrolwp.com/';
12
 
13
- /**
14
- * @var string
15
- */
16
- protected $sPrefix = '';
17
-
18
  /**
19
  * @var ICWP_WPSF_ServiceProviders
20
  */
@@ -34,7 +29,7 @@ class ICWP_WPSF_ServiceProviders extends ICWP_WPSF_Foundation {
34
  * @return string[][]
35
  */
36
  public function getIps_CloudFlare() {
37
- $oWp = $this->loadWp();
38
 
39
  $sStoreKey = $this->prefix( 'serviceips_cloudflare' );
40
  $aIps = $oWp->getTransient( $sStoreKey );
@@ -76,7 +71,7 @@ class ICWP_WPSF_ServiceProviders extends ICWP_WPSF_Foundation {
76
  * @return array[]|string[]
77
  */
78
  public function getIps_iControlWP( $bFlat = false ) {
79
- $oWp = $this->loadWp();
80
 
81
  $sStoreKey = $this->prefix( 'serviceips_icontrolwp' );
82
  $aIps = $oWp->getTransient( $sStoreKey );
@@ -92,7 +87,7 @@ class ICWP_WPSF_ServiceProviders extends ICWP_WPSF_Foundation {
92
  * @return array[]
93
  */
94
  public function getIps_ManageWp() {
95
- $oWp = $this->loadWp();
96
 
97
  $sStoreKey = $this->prefix( 'serviceips_managewp' );
98
  $aIps = $oWp->getTransient( $sStoreKey );
@@ -107,7 +102,7 @@ class ICWP_WPSF_ServiceProviders extends ICWP_WPSF_Foundation {
107
  * @return string[][]
108
  */
109
  public function getIps_Pingdom() {
110
- $oWp = $this->loadWp();
111
 
112
  $sStoreKey = $this->prefix( 'serviceips_pingdom' );
113
  $aIps = $oWp->getTransient( $sStoreKey );
@@ -125,7 +120,7 @@ class ICWP_WPSF_ServiceProviders extends ICWP_WPSF_Foundation {
125
  * @return string[]
126
  */
127
  public function getIps_Statuscake() {
128
- $oWp = $this->loadWp();
129
 
130
  $sStoreKey = $this->prefix( 'serviceips_statuscake' );
131
  $aIps = $oWp->getTransient( $sStoreKey );
@@ -140,7 +135,7 @@ class ICWP_WPSF_ServiceProviders extends ICWP_WPSF_Foundation {
140
  * @return array[]
141
  */
142
  public function getIps_UptimeRobot() {
143
- $oWp = $this->loadWp();
144
 
145
  $sStoreKey = $this->prefix( 'serviceips_uptimerobot' );
146
  $aIps = $oWp->getTransient( $sStoreKey );
@@ -160,7 +155,7 @@ class ICWP_WPSF_ServiceProviders extends ICWP_WPSF_Foundation {
160
  * @return bool
161
  */
162
  public function isIp_AppleBot( $sIp, $sUserAgent ) {
163
- $oWp = $this->loadWp();
164
 
165
  $sStoreKey = $this->prefix( 'serviceips_applebot' );
166
  $aIps = $oWp->getTransient( $sStoreKey );
@@ -182,7 +177,7 @@ class ICWP_WPSF_ServiceProviders extends ICWP_WPSF_Foundation {
182
  * @return bool
183
  */
184
  public function isIp_BaiduBot( $sIp, $sUserAgent ) {
185
- $oWp = $this->loadWp();
186
 
187
  $sStoreKey = $this->prefix( 'serviceips_baidubot' );
188
  $aIps = $oWp->getTransient( $sStoreKey );
@@ -275,7 +270,7 @@ class ICWP_WPSF_ServiceProviders extends ICWP_WPSF_Foundation {
275
  * @return bool
276
  */
277
  public function isIp_GoogleBot( $sIp, $sUserAgent ) {
278
- $oWp = $this->loadWp();
279
 
280
  $sStoreKey = $this->prefix( 'serviceips_googlebot' );
281
  $aIps = $oWp->getTransient( $sStoreKey );
@@ -340,7 +335,7 @@ class ICWP_WPSF_ServiceProviders extends ICWP_WPSF_Foundation {
340
  * @return bool
341
  */
342
  public function isIp_YandexBot( $sIp, $sUserAgent ) {
343
- $oWp = $this->loadWp();
344
 
345
  $sStoreKey = $this->prefix( 'serviceips_yandexbot' );
346
  $aIps = $oWp->getTransient( $sStoreKey );
@@ -363,7 +358,7 @@ class ICWP_WPSF_ServiceProviders extends ICWP_WPSF_Foundation {
363
  * @return bool
364
  */
365
  public function isIp_YahooBot( $sIp, $sUserAgent ) {
366
- $oWp = $this->loadWp();
367
 
368
  $sStoreKey = $this->prefix( 'serviceips_yahoobot' );
369
  $aIps = $oWp->getTransient( $sStoreKey );
10
  const URL_STATUS_CAKE_IPS = 'https://app.statuscake.com/Workfloor/Locations.php?format=json';
11
  const URL_ICONTROLWP_IPS = 'https://serviceips.icontrolwp.com/';
12
 
 
 
 
 
 
13
  /**
14
  * @var ICWP_WPSF_ServiceProviders
15
  */
29
  * @return string[][]
30
  */
31
  public function getIps_CloudFlare() {
32
+ $oWp = Services::WpGeneral();
33
 
34
  $sStoreKey = $this->prefix( 'serviceips_cloudflare' );
35
  $aIps = $oWp->getTransient( $sStoreKey );
71
  * @return array[]|string[]
72
  */
73
  public function getIps_iControlWP( $bFlat = false ) {
74
+ $oWp = Services::WpGeneral();
75
 
76
  $sStoreKey = $this->prefix( 'serviceips_icontrolwp' );
77
  $aIps = $oWp->getTransient( $sStoreKey );
87
  * @return array[]
88
  */
89
  public function getIps_ManageWp() {
90
+ $oWp = Services::WpGeneral();
91
 
92
  $sStoreKey = $this->prefix( 'serviceips_managewp' );
93
  $aIps = $oWp->getTransient( $sStoreKey );
102
  * @return string[][]
103
  */
104
  public function getIps_Pingdom() {
105
+ $oWp = Services::WpGeneral();
106
 
107
  $sStoreKey = $this->prefix( 'serviceips_pingdom' );
108
  $aIps = $oWp->getTransient( $sStoreKey );
120
  * @return string[]
121
  */
122
  public function getIps_Statuscake() {
123
+ $oWp = Services::WpGeneral();
124
 
125
  $sStoreKey = $this->prefix( 'serviceips_statuscake' );
126
  $aIps = $oWp->getTransient( $sStoreKey );
135
  * @return array[]
136
  */
137
  public function getIps_UptimeRobot() {
138
+ $oWp = Services::WpGeneral();
139
 
140
  $sStoreKey = $this->prefix( 'serviceips_uptimerobot' );
141
  $aIps = $oWp->getTransient( $sStoreKey );
155
  * @return bool
156
  */
157
  public function isIp_AppleBot( $sIp, $sUserAgent ) {
158
+ $oWp = Services::WpGeneral();
159
 
160
  $sStoreKey = $this->prefix( 'serviceips_applebot' );
161
  $aIps = $oWp->getTransient( $sStoreKey );
177
  * @return bool
178
  */
179
  public function isIp_BaiduBot( $sIp, $sUserAgent ) {
180
+ $oWp = Services::WpGeneral();
181
 
182
  $sStoreKey = $this->prefix( 'serviceips_baidubot' );
183
  $aIps = $oWp->getTransient( $sStoreKey );
270
  * @return bool
271
  */
272
  public function isIp_GoogleBot( $sIp, $sUserAgent ) {
273
+ $oWp = Services::WpGeneral();
274
 
275
  $sStoreKey = $this->prefix( 'serviceips_googlebot' );
276
  $aIps = $oWp->getTransient( $sStoreKey );
335
  * @return bool
336
  */
337
  public function isIp_YandexBot( $sIp, $sUserAgent ) {
338
+ $oWp = Services::WpGeneral();
339
 
340
  $sStoreKey = $this->prefix( 'serviceips_yandexbot' );
341
  $aIps = $oWp->getTransient( $sStoreKey );
358
  * @return bool
359
  */
360
  public function isIp_YahooBot( $sIp, $sUserAgent ) {
361
+ $oWp = Services::WpGeneral();
362
 
363
  $sStoreKey = $this->prefix( 'serviceips_yahoobot' );
364
  $aIps = $oWp->getTransient( $sStoreKey );
src/common/icwp-wpdb.php DELETED
@@ -1,237 +0,0 @@
1
- <?php
2
-
3
- class ICWP_WPSF_WpDb {
4
-
5
- /**
6
- * @var ICWP_WPSF_WpDb
7
- */
8
- protected static $oInstance = null;
9
-
10
- /**
11
- * @var wpdb
12
- */
13
- protected $oWpdb;
14
-
15
- /**
16
- * @return ICWP_WPSF_WpDb
17
- */
18
- public static function GetInstance() {
19
- if ( is_null( self::$oInstance ) ) {
20
- self::$oInstance = new self;
21
- }
22
- return self::$oInstance;
23
- }
24
-
25
- public function __construct() {
26
- }
27
-
28
- /**
29
- * @param string $sSQL
30
- * @return array
31
- */
32
- public function dbDelta( $sSQL ) {
33
- require_once( ABSPATH.'wp-admin/includes/upgrade.php' );
34
- return dbDelta( $sSQL );
35
- }
36
-
37
- /**
38
- * @param string $sTable
39
- * @param array $aWhere - delete where (associative array)
40
- * @return false|int
41
- */
42
- public function deleteRowsFromTableWhere( $sTable, $aWhere ) {
43
- return $this->loadWpdb()->delete( $sTable, $aWhere );
44
- }
45
-
46
- /**
47
- * Will completely remove this table from the database
48
- * @param string $sTable
49
- * @return bool|int
50
- */
51
- public function doDropTable( $sTable ) {
52
- $sQuery = sprintf( 'DROP TABLE IF EXISTS `%s`', $sTable );
53
- return $this->doSql( $sQuery );
54
- }
55
-
56
- /**
57
- * Alias for doTruncateTable()
58
- * @param string $sTable
59
- * @return bool|int
60
- */
61
- public function doEmptyTable( $sTable ) {
62
- return $this->doTruncateTable( $sTable );
63
- }
64
-
65
- /**
66
- * Given any SQL query, will perform it using the WordPress database object.
67
- * @param string $sSqlQuery
68
- * @return integer|boolean (number of rows affected or just true/false)
69
- */
70
- public function doSql( $sSqlQuery ) {
71
- return $this->loadWpdb()->query( $sSqlQuery );
72
- }
73
-
74
- /**
75
- * @param string $sTable
76
- * @return bool|int
77
- */
78
- public function doTruncateTable( $sTable ) {
79
- if ( !$this->getIfTableExists( $sTable ) ) {
80
- return false;
81
- }
82
- $sQuery = sprintf( 'TRUNCATE TABLE `%s`', $sTable );
83
- return $this->doSql( $sQuery );
84
- }
85
-
86
- public function getCharCollate() {
87
- return $this->getWpdb()->get_charset_collate();
88
- }
89
-
90
- /**
91
- * @param string $sTable
92
- * @return bool
93
- */
94
- public function getIfTableExists( $sTable ) {
95
- $sQuery = sprintf( "SHOW TABLES LIKE '%s'", $sTable );
96
- $mResult = $this->loadWpdb()->get_var( $sQuery );
97
- return !is_null( $mResult );
98
- }
99
-
100
- /**
101
- * @param string $sTableName
102
- * @param string $sArrayMapCallBack
103
- * @return array
104
- */
105
- public function getColumnsForTable( $sTableName, $sArrayMapCallBack = '' ) {
106
- $aColumns = $this->loadWpdb()->get_col( "DESCRIBE ".$sTableName, 0 );
107
-
108
- if ( !empty( $sArrayMapCallBack ) && function_exists( $sArrayMapCallBack ) ) {
109
- return array_map( $sArrayMapCallBack, $aColumns );
110
- }
111
- return $aColumns;
112
- }
113
-
114
- /**
115
- * @param bool $bSiteBase
116
- * @return string
117
- */
118
- public function getPrefix( $bSiteBase = true ) {
119
- $oDb = $this->loadWpdb();
120
- return $bSiteBase ? $oDb->base_prefix : $oDb->prefix;
121
- }
122
-
123
- /**
124
- * @return string
125
- */
126
- public function getTable_Comments() {
127
- return $this->loadWpdb()->comments;
128
- }
129
-
130
- /**
131
- * @return string
132
- */
133
- public function getTable_Options() {
134
- return $this->loadWpdb()->options;
135
- }
136
-
137
- /**
138
- * @return string
139
- */
140
- public function getTable_Posts() {
141
- return $this->loadWpdb()->posts;
142
- }
143
-
144
- /**
145
- * @return string
146
- */
147
- public function getTable_Users() {
148
- return $this->loadWpdb()->users;
149
- }
150
-
151
- /**
152
- * @param $sSql
153
- * @return null|mixed
154
- */
155
- public function getVar( $sSql ) {
156
- return $this->loadWpdb()->get_var( $sSql );
157
- }
158
-
159
- /**
160
- * @param string $sTable
161
- * @param array $aData
162
- * @return int|false
163
- */
164
- public function insertDataIntoTable( $sTable, $aData ) {
165
- return $this->loadWpdb()->insert( $sTable, $aData );
166
- }
167
-
168
- /**
169
- * @param string $sTable
170
- * @param string $nFormat
171
- * @return mixed
172
- */
173
- public function selectAllFromTable( $sTable, $nFormat = ARRAY_A ) {
174
- $sQuery = sprintf( "SELECT * FROM `%s` WHERE `deleted_at` = 0", $sTable );
175
- return $this->loadWpdb()->get_results( $sQuery, $nFormat );
176
- }
177
-
178
- /**
179
- * @param string $sQuery
180
- * @param $nFormat
181
- * @return array|boolean
182
- */
183
- public function selectCustom( $sQuery, $nFormat = ARRAY_A ) {
184
- return $this->loadWpdb()->get_results( $sQuery, $nFormat );
185
- }
186
-
187
- /**
188
- * @param string $sQuery
189
- * @param string $nTotalCountKey
190
- * @return array|boolean
191
- */
192
- public function selectCount( $sQuery, $nTotalCountKey = 'total' ) {
193
- $nCount = -1;
194
- $aResult = $this->selectCustom( $sQuery, ARRAY_A );
195
- if ( is_array( $aResult ) ) {
196
- $nCount = (int)$aResult[ 0 ][ $nTotalCountKey ];
197
- }
198
- return $nCount;
199
- }
200
-
201
- /**
202
- * @param $sQuery
203
- * @param string $nFormat
204
- * @return null|object|array
205
- */
206
- public function selectRow( $sQuery, $nFormat = ARRAY_A ) {
207
- return $this->loadWpdb()->get_row( $sQuery, $nFormat );
208
- }
209
-
210
- /**
211
- * @param string $sTable
212
- * @param array $aData - new insert data (associative array, column=>data)
213
- * @param array $aWhere - insert where (associative array)
214
- * @return integer|boolean (number of rows affected)
215
- */
216
- public function updateRowsFromTableWhere( $sTable, $aData, $aWhere ) {
217
- return $this->loadWpdb()->update( $sTable, $aData, $aWhere );
218
- }
219
-
220
- /**
221
- * Loads our WPDB object if required.
222
- * @return \wpdb
223
- */
224
- protected function loadWpdb() {
225
- if ( is_null( $this->oWpdb ) ) {
226
- $this->oWpdb = $this->getWpdb();
227
- }
228
- return $this->oWpdb;
229
- }
230
-
231
- /**
232
- */
233
- private function getWpdb() {
234
- global $wpdb;
235
- return $wpdb;
236
- }
237
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/common/icwp-wpfilesystem.php DELETED
@@ -1,492 +0,0 @@
1
- <?php
2
-
3
- use FernleafSystems\Wordpress\Services\Services;
4
-
5
- class ICWP_WPSF_WpFilesystem {
6
-
7
- /**
8
- * @var ICWP_WPSF_WpFilesystem
9
- */
10
- protected static $oInstance = null;
11
-
12
- /**
13
- * @var WP_Filesystem_Base
14
- */
15
- protected $oWpfs = null;
16
-
17
- /**
18
- * @var string
19
- */
20
- protected $sWpConfigPath = null;
21
-
22
- /**
23
- * @return ICWP_WPSF_WpFilesystem
24
- */
25
- public static function GetInstance() {
26
- if ( is_null( self::$oInstance ) ) {
27
- self::$oInstance = new self();
28
- }
29
- return self::$oInstance;
30
- }
31
-
32
- /**
33
- * @param string $sBase
34
- * @param string $sPath
35
- * @return string
36
- */
37
- public function pathJoin( $sBase, $sPath ) {
38
- return rtrim( $sBase, DIRECTORY_SEPARATOR ).DIRECTORY_SEPARATOR.ltrim( $sPath, DIRECTORY_SEPARATOR );
39
- }
40
-
41
- /**
42
- * @param $sFilePath
43
- * @return boolean|null true/false whether file/directory exists
44
- */
45
- public function exists( $sFilePath ) {
46
- $oFs = $this->getWpfs();
47
- if ( $oFs && $oFs->exists( $sFilePath ) ) {
48
- return true;
49
- }
50
- return function_exists( 'file_exists' ) ? file_exists( $sFilePath ) : null;
51
- }
52
-
53
- /**
54
- * @param string $sNeedle
55
- * @param string $sDir
56
- * @param boolean $bIncludeExtension
57
- * @param boolean $bCaseSensitive
58
- * @return string|null - the full path to the file if found
59
- */
60
- public function fileExistsInDir( $sNeedle, $sDir, $bIncludeExtension = true, $bCaseSensitive = false ) {
61
- if ( empty( $sNeedle ) || empty( $sDir ) ) {
62
- return false;
63
- }
64
-
65
- if ( !$bCaseSensitive ) {
66
- $sNeedle = strtolower( $sNeedle );
67
- }
68
-
69
- //if the file you're searching for doesn't have an extension, then we don't include extensions in search
70
- $nDotPosition = strpos( $sNeedle, '.' );
71
- $bHasExtension = $nDotPosition !== false;
72
- $bIncludeExtension = $bIncludeExtension && $bHasExtension;
73
-
74
- $sNeedlePreExtension = $bHasExtension ? substr( $sNeedle, 0, $nDotPosition ) : $sNeedle;
75
-
76
- $bFound = false;
77
- $sFullPath = null;
78
- foreach ( $this->getFilesInDir( $sDir ) as $oFileItem ) {
79
-
80
- $sFilename = $oFileItem->getFilename();
81
- if ( !$bCaseSensitive ) {
82
- $sFilename = strtolower( $sFilename );
83
- }
84
-
85
- if ( $bIncludeExtension ) {
86
- $bFound = ( $sFilename == $sNeedle );
87
- }
88
- else {
89
- // This is not entirely accurate as it only finds whether a file "starts" with needle, ignoring subsequent characters
90
- $bFound = ( strpos( $sFilename, $sNeedlePreExtension ) === 0 );
91
- }
92
-
93
- if ( $bFound ) {
94
- $sFullPath = $oFileItem->getPathname();
95
- break;
96
- }
97
- }
98
-
99
- return $sFullPath;
100
- }
101
-
102
- /**
103
- * @param string $sDir
104
- * @param int $nMaxDepth - set to zero for no max
105
- * @param RecursiveDirectoryIterator $oDirIterator
106
- * @return SplFileInfo[]
107
- */
108
- public function getFilesInDir( $sDir, $nMaxDepth = 1, $oDirIterator = null ) {
109
- $aList = [];
110
-
111
- try {
112
- if ( empty( $oDirIterator ) ) {
113
- $oDirIterator = new RecursiveDirectoryIterator( $sDir );
114
- if ( method_exists( $oDirIterator, 'setFlags' ) ) {
115
- $oDirIterator->setFlags( RecursiveDirectoryIterator::SKIP_DOTS );
116
- }
117
- }
118
-
119
- $oRecurIter = new RecursiveIteratorIterator( $oDirIterator );
120
- $oRecurIter->setMaxDepth( $nMaxDepth - 1 ); //since they start at zero.
121
-
122
- /** @var SplFileInfo $oFile */
123
- foreach ( $oRecurIter as $oFile ) {
124
- $aList[] = clone $oFile;
125
- }
126
- }
127
- catch ( Exception $oE ) { // UnexpectedValueException, RuntimeException, Exception
128
- }
129
-
130
- return $aList;
131
- }
132
-
133
- /**
134
- * Returns a path where all backslashes "\" are converted to "/"
135
- * @param string $sPath
136
- * @return string
137
- */
138
- public function normalizeFilePathDS( $sPath ) {
139
- return ( DIRECTORY_SEPARATOR == '/' ) ? $sPath : str_replace( '\\', '/', $sPath );
140
- }
141
-
142
- protected function setWpConfigPath() {
143
- $this->sWpConfigPath = ABSPATH.'wp-config.php';
144
- if ( !$this->exists( $this->sWpConfigPath ) ) {
145
- $this->sWpConfigPath = ABSPATH.'../wp-config.php';
146
- if ( !$this->exists( $this->sWpConfigPath ) ) {
147
- $this->sWpConfigPath = false;
148
- }
149
- }
150
- }
151
-
152
- public function getContent_WpConfig() {
153
- return $this->getFileContent( $this->sWpConfigPath );
154
- }
155
-
156
- /**
157
- * @param string $sContent
158
- * @return bool
159
- */
160
- public function putContent_WpConfig( $sContent ) {
161
- return $this->putFileContent( $this->sWpConfigPath, $sContent );
162
- }
163
-
164
- /**
165
- * @param string $sUrl
166
- * @param boolean $bSecure
167
- * @return boolean
168
- */
169
- public function isUrlLive( $sUrl, $bSecure = false ) {
170
- $sSchema = $bSecure ? 'https://' : 'http://';
171
- $sUrl = ( strpos( $sUrl, 'http' ) !== 0 ) ? $sSchema.$sUrl : $sUrl;
172
- return ( $this->getUrl( $sUrl ) != false );
173
- }
174
-
175
- /**
176
- * @return string
177
- */
178
- public function getWpConfigPath() {
179
- return $this->sWpConfigPath;
180
- }
181
-
182
- /**
183
- * @param string $sUrl
184
- * @param array $aRequestArgs
185
- * @param bool $bAlwaysRawResponse
186
- * @return array|WP_Error|bool
187
- */
188
- public function requestUrl( $sUrl, $aRequestArgs = [], $bAlwaysRawResponse = false ) {
189
-
190
- $mResult = wp_remote_request( $sUrl, $aRequestArgs );
191
- if ( $bAlwaysRawResponse ) {
192
- return $mResult;
193
- }
194
- if ( is_wp_error( $mResult ) || !isset( $mResult[ 'response' ][ 'code' ] ) || $mResult[ 'response' ][ 'code' ] != 200 ) {
195
- return false;
196
- }
197
- return $mResult;
198
- }
199
-
200
- /**
201
- * @param string $sUrl
202
- * @param array $aRequestArgs
203
- * @return array|bool
204
- */
205
- public function getUrl( $sUrl, $aRequestArgs = [] ) {
206
- $aRequestArgs[ 'method' ] = 'GET';
207
- return $this->requestUrl( $sUrl, $aRequestArgs );
208
- }
209
-
210
- /**
211
- * @param string $sUrl
212
- * @param array $aRequestArgs
213
- * @return false|string
214
- */
215
- public function getUrlContent( $sUrl, $aRequestArgs = [] ) {
216
- $aResponse = $this->getUrl( $sUrl, $aRequestArgs );
217
- if ( !$aResponse || !isset( $aResponse[ 'body' ] ) ) {
218
- return false;
219
- }
220
- return $aResponse[ 'body' ];
221
- }
222
-
223
- /**
224
- * @param string $sUrl
225
- * @param array $aRequestArgs
226
- * @return array|false
227
- * @deprecated
228
- */
229
- public function postUrl( $sUrl, $aRequestArgs = [] ) {
230
- $aRequestArgs[ 'method' ] = 'POST';
231
- return $this->requestUrl( $sUrl, $aRequestArgs );
232
- }
233
-
234
- public function getCanWpRemoteGet() {
235
- $aUrlsToTest = [
236
- 'https://www.microsoft.com',
237
- 'https://www.google.com',
238
- 'https://www.facebook.com'
239
- ];
240
- foreach ( $aUrlsToTest as $sUrl ) {
241
- if ( $this->getUrl( $sUrl ) !== false ) {
242
- return true;
243
- }
244
- }
245
- return false;
246
- }
247
-
248
- public function getCanDiskWrite() {
249
- $sFilePath = __DIR__.'/testfile.'.rand().'txt';
250
- $sContents = "Testing icwp file read and write.";
251
-
252
- // Write, read, verify, delete.
253
- if ( $this->putFileContent( $sFilePath, $sContents ) ) {
254
- $sFileContents = $this->getFileContent( $sFilePath );
255
- if ( !is_null( $sFileContents ) && $sFileContents === $sContents ) {
256
- return $this->deleteFile( $sFilePath );
257
- }
258
- }
259
- return false;
260
- }
261
-
262
- /**
263
- * @param string $sFilePath
264
- * @return int|null
265
- */
266
- public function getModifiedTime( $sFilePath ) {
267
- return $this->getTime( $sFilePath, 'modified' );
268
- }
269
-
270
- /**
271
- * @param string $sFilePath
272
- * @return int|null
273
- */
274
- public function getAccessedTime( $sFilePath ) {
275
- return $this->getTime( $sFilePath, 'accessed' );
276
- }
277
-
278
- /**
279
- * @param string $sFilePath
280
- * @param string $sProperty
281
- * @return int|null
282
- */
283
- public function getTime( $sFilePath, $sProperty = 'modified' ) {
284
-
285
- if ( !$this->exists( $sFilePath ) ) {
286
- return null;
287
- }
288
-
289
- $oFs = $this->getWpfs();
290
- switch ( $sProperty ) {
291
-
292
- case 'modified' :
293
- return $oFs ? $oFs->mtime( $sFilePath ) : filemtime( $sFilePath );
294
- break;
295
- case 'accessed' :
296
- return $oFs ? $oFs->atime( $sFilePath ) : fileatime( $sFilePath );
297
- break;
298
- default:
299
- return null;
300
- break;
301
- }
302
- }
303
-
304
- /**
305
- * @param string $sFilePath
306
- * @return NULL|boolean
307
- */
308
- public function getCanReadWriteFile( $sFilePath ) {
309
- if ( !file_exists( $sFilePath ) ) {
310
- return null;
311
- }
312
-
313
- $nFileSize = filesize( $sFilePath );
314
- if ( $nFileSize === 0 ) {
315
- return null;
316
- }
317
-
318
- $sFileContent = $this->getFileContent( $sFilePath );
319
- if ( empty( $sFileContent ) ) {
320
- return false; //can't even read the file!
321
- }
322
- return $this->putFileContent( $sFilePath, $sFileContent );
323
- }
324
-
325
- /**
326
- * @param string $sFilePath
327
- * @return string|null
328
- */
329
- public function getFileContent( $sFilePath ) {
330
- $sContents = null;
331
- $oFs = $this->getWpfs();
332
- if ( $oFs ) {
333
- $sContents = $oFs->get_contents( $sFilePath );
334
- }
335
-
336
- if ( empty( $sContents ) && function_exists( 'file_get_contents' ) ) {
337
- $sContents = file_get_contents( $sFilePath );
338
- }
339
- return $sContents;
340
- }
341
-
342
- /**
343
- * @param $sFilePath
344
- * @return bool
345
- */
346
- public function getFileSize( $sFilePath ) {
347
- $oFs = $this->getWpfs();
348
- if ( $oFs && ( $oFs->size( $sFilePath ) > 0 ) ) {
349
- return $oFs->size( $sFilePath );
350
- }
351
- return @filesize( $sFilePath );
352
- }
353
-
354
- /**
355
- * @param string|null $sBaseDir
356
- * @param string $sPrefix
357
- * @param string $outsRandomDir
358
- * @return bool|string
359
- */
360
- public function getTempDir( $sBaseDir = null, $sPrefix = '', &$outsRandomDir = '' ) {
361
- $sTemp = rtrim( ( is_null( $sBaseDir ) ? get_temp_dir() : $sBaseDir ), DIRECTORY_SEPARATOR ).DIRECTORY_SEPARATOR;
362
-
363
- $sCharset = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz0123456789';
364
- do {
365
- $sDir = $sPrefix;
366
- for ( $i = 0 ; $i < 8 ; $i++ ) {
367
- $sDir .= $sCharset[ ( rand()%strlen( $sCharset ) ) ];
368
- }
369
- } while ( is_dir( $sTemp.$sDir ) );
370
-
371
- $outsRandomDir = $sDir;
372
-
373
- $bSuccess = true;
374
- if ( !@mkdir( $sTemp.$sDir, 0755, true ) ) {
375
- $bSuccess = false;
376
- }
377
- return ( $bSuccess ? $sTemp.$sDir : false );
378
- }
379
-
380
- /**
381
- * @param string $sFilePath
382
- * @param string $sContents
383
- * @return boolean
384
- */
385
- public function putFileContent( $sFilePath, $sContents ) {
386
- $oFs = $this->getWpfs();
387
- if ( $oFs && $oFs->put_contents( $sFilePath, $sContents, FS_CHMOD_FILE ) ) {
388
- return true;
389
- }
390
-
391
- if ( function_exists( 'file_put_contents' ) ) {
392
- return file_put_contents( $sFilePath, $sContents ) !== false;
393
- }
394
- return false;
395
- }
396
-
397
- /**
398
- * Recursive delete
399
- * @param string $sDir
400
- * @return bool
401
- */
402
- public function deleteDir( $sDir ) {
403
- $oFs = $this->getWpfs();
404
- if ( $oFs && $oFs->rmdir( $sDir, true ) ) {
405
- return true;
406
- }
407
- return @rmdir( $sDir );
408
- }
409
-
410
- /**
411
- * @param string $sFilePath
412
- * @return boolean|null
413
- */
414
- public function deleteFile( $sFilePath ) {
415
- $oFs = $this->getWpfs();
416
- if ( $oFs && $oFs->delete( $sFilePath ) ) {
417
- return true;
418
- }
419
- return function_exists( 'unlink' ) ? @unlink( $sFilePath ) : null;
420
- }
421
-
422
- /**
423
- * @param string $sFilePathSource
424
- * @param string $sFilePathDestination
425
- * @return bool|null
426
- */
427
- public function move( $sFilePathSource, $sFilePathDestination ) {
428
- $oFs = $this->getWpfs();
429
- if ( $oFs && $oFs->move( $sFilePathSource, $sFilePathDestination ) ) {
430
- return true;
431
- }
432
- return function_exists( 'rename' ) ? @rename( $sFilePathSource, $sFilePathDestination ) : null;
433
- }
434
-
435
- /**
436
- * @param $sFilePath
437
- * @return bool|mixed
438
- */
439
- public function isFile( $sFilePath ) {
440
- $oFs = $this->getWpfs();
441
- if ( $oFs && $oFs->is_file( $sFilePath ) ) {
442
- return true;
443
- }
444
- return function_exists( 'is_file' ) ? is_file( $sFilePath ) : null;
445
- }
446
-
447
- /**
448
- * @param $sDirPath
449
- * @return bool
450
- */
451
- public function mkdir( $sDirPath ) {
452
- return wp_mkdir_p( $sDirPath );
453
- }
454
-
455
- /**
456
- * @param string $sFilePath
457
- * @param int $nTime
458
- * @return bool|mixed
459
- */
460
- public function touch( $sFilePath, $nTime = null ) {
461
- $oFs = $this->getWpfs();
462
- if ( $oFs && $oFs->touch( $sFilePath, $nTime ) ) {
463
- return true;
464
- }
465
- return function_exists( 'touch' ) ? @touch( $sFilePath, $nTime ) : null;
466
- }
467
-
468
- /**
469
- * @return WP_Filesystem_Base
470
- */
471
- protected function getWpfs() {
472
- if ( is_null( $this->oWpfs ) ) {
473
- $this->initFileSystem();
474
- }
475
- return $this->oWpfs;
476
- }
477
-
478
- /**
479
- */
480
- private function initFileSystem() {
481
- if ( is_null( $this->oWpfs ) ) {
482
- $this->oWpfs = false;
483
- require_once( ABSPATH.'wp-admin/includes/file.php' );
484
- if ( WP_Filesystem() ) {
485
- global $wp_filesystem;
486
- if ( isset( $wp_filesystem ) && is_object( $wp_filesystem ) ) {
487
- $this->oWpfs = $wp_filesystem;
488
- }
489
- }
490
- }
491
- }
492
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/common/icwp-wpfunctions-plugins.php DELETED
@@ -1,547 +0,0 @@
1
- <?php
2
-
3
- class ICWP_WPSF_WpFunctions_Plugins extends ICWP_WPSF_Foundation {
4
-
5
- /**
6
- * @var ICWP_WPSF_WpFunctions_Plugins
7
- */
8
- protected static $oInstance = null;
9
-
10
- private function __construct() {
11
- }
12
-
13
- /**
14
- * @return ICWP_WPSF_WpFunctions_Plugins
15
- */
16
- public static function GetInstance() {
17
- if ( is_null( self::$oInstance ) ) {
18
- self::$oInstance = new self();
19
- }
20
- return self::$oInstance;
21
- }
22
-
23
- /**
24
- * @param string $sPluginFile
25
- * @param bool $bNetworkWide
26
- * @return null|WP_Error
27
- */
28
- public function activate( $sPluginFile, $bNetworkWide = false ) {
29
- return activate_plugin( $sPluginFile, '', $bNetworkWide );
30
- }
31
-
32
- /**
33
- * @param string $sPluginFile
34
- * @param bool $bNetworkWide
35
- * @return null|WP_Error
36
- */
37
- protected function activateQuietly( $sPluginFile, $bNetworkWide = false ) {
38
- return activate_plugin( $sPluginFile, '', $bNetworkWide, true );
39
- }
40
-
41
- /**
42
- * @param string $sPluginFile
43
- * @param bool $bNetworkWide
44
- */
45
- public function deactivate( $sPluginFile, $bNetworkWide = false ) {
46
- deactivate_plugins( $sPluginFile, '', $bNetworkWide );
47
- }
48
-
49
- /**
50
- * @param string $sPluginFile
51
- * @param bool $bNetworkWide
52
- */
53
- protected function deactivateQuietly( $sPluginFile, $bNetworkWide = false ) {
54
- deactivate_plugins( $sPluginFile, true, $bNetworkWide );
55
- }
56
-
57
- /**
58
- * @param string $sFile
59
- * @param bool $bNetworkWide
60
- * @return bool
61
- */
62
- public function delete( $sFile, $bNetworkWide = false ) {
63
- if ( !$this->isInstalled( $sFile ) ) {
64
- return false;
65
- }
66
-
67
- if ( $this->isActive( $sFile ) ) {
68
- $this->deactivate( $sFile, $bNetworkWide );
69
- }
70
- $this->uninstall( $sFile );
71
-
72
- // delete the folder
73
- $sPluginDir = dirname( $sFile );
74
- if ( $sPluginDir == '.' ) { //it's not within a sub-folder
75
- $sPluginDir = $sFile;
76
- }
77
- $sPath = path_join( WP_PLUGIN_DIR, $sPluginDir );
78
- return $this->loadFS()->deleteDir( $sPath );
79
- }
80
-
81
- /**
82
- * @param string $sUrlToInstall
83
- * @param bool $bOverwrite
84
- * @param bool $bMaintenanceMode
85
- * @return array
86
- */
87
- public function install( $sUrlToInstall, $bOverwrite = true, $bMaintenanceMode = false ) {
88
- $this->loadWpUpgrades();
89
-
90
- $aResult = [
91
- 'successful' => true,
92
- 'plugin_info' => '',
93
- 'errors' => []
94
- ];
95
-
96
- $oUpgraderSkin = new ICWP_Upgrader_Skin();
97
- $oUpgrader = new ICWP_Plugin_Upgrader( $oUpgraderSkin );
98
- $oUpgrader->setOverwriteMode( $bOverwrite );
99
- if ( $bMaintenanceMode ) {
100
- $oUpgrader->maintenance_mode( true );
101
- }
102
-
103
- ob_start();
104
- $sInstallResult = $oUpgrader->install( $sUrlToInstall );
105
- ob_end_clean();
106
-
107
- if ( $bMaintenanceMode ) {
108
- $oUpgrader->maintenance_mode( false );
109
- }
110
-
111
- if ( is_wp_error( $oUpgraderSkin->m_aErrors[ 0 ] ) ) {
112
- $aResult[ 'successful' ] = false;
113
- $aResult[ 'errors' ] = $oUpgraderSkin->m_aErrors[ 0 ]->get_error_messages();
114
- }
115
- else {
116
- $aResult[ 'plugin_info' ] = $oUpgrader->plugin_info();
117
- }
118
-
119
- $aResult[ 'feedback' ] = $oUpgraderSkin->getFeedback();
120
- $aResult[ 'raw' ] = $sInstallResult;
121
- return $aResult;
122
- }
123
-
124
- /**
125
- * @param $sSlug
126
- * @return array|bool
127
- */
128
- public function installFromWpOrg( $sSlug ) {
129
- include_once( ABSPATH.'wp-admin/includes/plugin-install.php' );
130
-
131
- $api = plugins_api( 'plugin_information', [
132
- 'slug' => $sSlug,
133
- 'fields' => [
134
- 'sections' => false,
135
- ],
136
- ] );
137
-
138
- if ( !is_wp_error( $api ) ) {
139
- return $this->install( $api->download_link, true, true );
140
- }
141
- return false;
142
- }
143
-
144
- /**
145
- * @param string $sFile
146
- * @param bool $bUseBackup
147
- * @return bool
148
- */
149
- public function reinstall( $sFile, $bUseBackup = false ) {
150
- $bSuccess = false;
151
-
152
- if ( $this->isInstalled( $sFile ) ) {
153
-
154
- $sSlug = $this->getSlug( $sFile );
155
- if ( !empty( $sSlug ) ) {
156
- $oFS = $this->loadFS();
157
-
158
- $sDir = dirname( path_join( WP_PLUGIN_DIR, $sFile ) );
159
- $sBackupDir = WP_PLUGIN_DIR.'/../'.basename( $sDir ).'bak'.time();
160
- if ( $bUseBackup ) {
161
- rename( $sDir, $sBackupDir );
162
- }
163
-
164
- $aResult = $this->installFromWpOrg( $sSlug );
165
- $bSuccess = $aResult[ 'successful' ];
166
- if ( $bSuccess ) {
167
- wp_update_plugins(); //refreshes our update information
168
- if ( $bUseBackup ) {
169
- $oFS->deleteDir( $sBackupDir );
170
- }
171
- }
172
- else {
173
- if ( $bUseBackup ) {
174
- $oFS->deleteDir( $sDir );
175
- rename( $sBackupDir, $sDir );
176
- }
177
- }
178
- }
179
- }
180
- return $bSuccess;
181
- }
182
-
183
- /**
184
- * @param string $sFile
185
- * @return array
186
- */
187
- public function update( $sFile ) {
188
- $this->loadWpUpgrades();
189
-
190
- $aResult = [
191
- 'successful' => 1,
192
- 'errors' => []
193
- ];
194
-
195
- $oUpgraderSkin = new ICWP_Bulk_Plugin_Upgrader_Skin();
196
- $oUpgrader = new Plugin_Upgrader( $oUpgraderSkin );
197
- ob_start();
198
- $oUpgrader->bulk_upgrade( [ $sFile ] );
199
- ob_end_clean();
200
-
201
- if ( isset( $oUpgraderSkin->m_aErrors[ 0 ] ) ) {
202
- if ( is_wp_error( $oUpgraderSkin->m_aErrors[ 0 ] ) ) {
203
- $aResult[ 'successful' ] = 0;
204
- $aResult[ 'errors' ] = $oUpgraderSkin->m_aErrors[ 0 ]->get_error_messages();
205
- }
206
- }
207
- $aResult[ 'feedback' ] = $oUpgraderSkin->getFeedback();
208
- return $aResult;
209
- }
210
-
211
- /**
212
- * @param string $sPluginFile
213
- * @return true
214
- */
215
- public function uninstall( $sPluginFile ) {
216
- return uninstall_plugin( $sPluginFile );
217
- }
218
-
219
- /**
220
- * @return boolean|null
221
- */
222
- protected function checkForUpdates() {
223
-
224
- if ( class_exists( 'WPRC_Installer' ) && method_exists( 'WPRC_Installer', 'wprc_update_plugins' ) ) {
225
- WPRC_Installer::wprc_update_plugins();
226
- return true;
227
- }
228
- else if ( function_exists( 'wp_update_plugins' ) ) {
229
- return ( wp_update_plugins() !== false );
230
- }
231
- return null;
232
- }
233
-
234
- /**
235
- */
236
- protected function clearUpdates() {
237
- $sKey = 'update_plugins';
238
- $oResponse = $this->loadWp()->getTransient( $sKey );
239
- if ( !is_object( $oResponse ) ) {
240
- $oResponse = new stdClass();
241
- }
242
- $oResponse->last_checked = 0;
243
- $this->loadWp()->setTransient( $sKey, $oResponse );
244
- }
245
-
246
- /**
247
- * @param string $sValueToCompare
248
- * @param string $sKey
249
- * @return null|string
250
- */
251
- public function findPluginBy( $sValueToCompare, $sKey = 'Name' ) {
252
- $sFile = null;
253
-
254
- foreach ( $this->getPlugins() as $sBaseFileName => $aPluginData ) {
255
- if ( isset( $aPluginData[ $sKey ] ) && $sValueToCompare == $aPluginData[ $sKey ] ) {
256
- $sFile = $sBaseFileName;
257
- break;
258
- }
259
- }
260
-
261
- return $sFile;
262
- }
263
-
264
- /**
265
- * @param string $sFile - plugin base file, e.g. wp-folder/wp-plugin.php
266
- * @return string
267
- */
268
- public function getInstallationDir( $sFile ) {
269
- return dirname( path_join( WP_PLUGIN_DIR, $sFile ) );
270
- }
271
-
272
- /**
273
- * @param string $sPluginFile
274
- * @return array|null
275
- */
276
- public function getPlugin( $sPluginFile ) {
277
- return $this->isInstalled( $sPluginFile ) ? $this->getPlugins()[ $sPluginFile ] : null;
278
- }
279
-
280
- /**
281
- * @param string $sDirName
282
- * @return string|null
283
- */
284
- public function getFileFromDirName( $sDirName ) {
285
- $sFile = null;
286
- if ( !empty( $sDirName ) ) {
287
- foreach ( $this->getInstalledBaseFiles() as $sF ) {
288
- if ( strpos( $sFile, $sDirName.'/' ) === 0 ) {
289
- $sFile = $sF;
290
- break;
291
- }
292
- }
293
- }
294
- return $sFile;
295
- }
296
-
297
- /**
298
- * @param string $sPluginFile
299
- * @return null|stdClass
300
- */
301
- public function getPluginDataAsObject( $sPluginFile ) {
302
- $aPlugin = $this->getPlugin( $sPluginFile );
303
- return is_null( $aPlugin ) ? null : $this->loadDP()->convertArrayToStdClass( $aPlugin );
304
- }
305
-
306
- /**
307
- * @param string $sPluginFile
308
- * @return int
309
- */
310
- public function getActivePluginLoadPosition( $sPluginFile ) {
311
- $nPosition = array_search( $sPluginFile, $this->getActivePlugins() );
312
- return ( $nPosition === false ) ? -1 : $nPosition;
313
- }
314
-
315
- /**
316
- * @return array
317
- */
318
- public function getActivePlugins() {
319
- $oWp = $this->loadWp();
320
- $aActive = $oWp->getOption( ( $oWp->isMultisite() ? 'active_sitewide_plugins' : 'active_plugins' ) );
321
- return is_array( $aActive ) ? $aActive : [];
322
- }
323
-
324
- /**
325
- * @return array
326
- */
327
- public function getInstalledBaseFiles() {
328
- return array_keys( $this->getPlugins() );
329
- }
330
-
331
- /**
332
- * @return array[]
333
- */
334
- public function getPlugins() {
335
- if ( !function_exists( 'get_plugins' ) ) {
336
- require_once( ABSPATH.'wp-admin/includes/plugin.php' );
337
- }
338
- $aP = function_exists( 'get_plugins' ) ? get_plugins() : [];
339
- return is_array( $aP ) ? $aP : [];
340
- }
341
-
342
- /**
343
- * @return stdClass[] - keys are plugin base files
344
- */
345
- public function getAllExtendedData() {
346
- $oData = $this->loadWp()->getTransient( 'update_plugins' );
347
- return array_merge(
348
- isset( $oData->no_update ) ? $oData->no_update : [],
349
- isset( $oData->response ) ? $oData->response : []
350
- );
351
- }
352
-
353
- /**
354
- * @param $sBaseFile
355
- * @return null|stdClass
356
- */
357
- public function getExtendedData( $sBaseFile ) {
358
- $aData = $this->getAllExtendedData();
359
- return isset( $aData[ $sBaseFile ] ) ? $aData[ $sBaseFile ] : null;
360
- }
361
-
362
- /**
363
- * @return array
364
- */
365
- public function getAllSlugs() {
366
- $aSlugs = [];
367
-
368
- foreach ( $this->getAllExtendedData() as $sBaseName => $oPlugData ) {
369
- if ( isset( $oPlugData->slug ) ) {
370
- $aSlugs[ $sBaseName ] = $oPlugData->slug;
371
- }
372
- }
373
-
374
- return $aSlugs;
375
- }
376
-
377
- /**
378
- * @param $sBaseName
379
- * @return string
380
- */
381
- public function getSlug( $sBaseName ) {
382
- $oPluginInfo = $this->getExtendedData( $sBaseName );
383
- return isset( $oPluginInfo->slug ) ? $oPluginInfo->slug : '';
384
- }
385
-
386
- /**
387
- * @param string $sFile
388
- * @return stdClass|null
389
- */
390
- public function getUpdateInfo( $sFile ) {
391
- $aU = $this->getUpdates();
392
- return isset( $aU[ $sFile ] ) ? $aU[ $sFile ] : null;
393
- }
394
-
395
- /**
396
- * @param string $sFile
397
- * @return string
398
- */
399
- public function getUpdateNewVersion( $sFile ) {
400
- $oInfo = $this->getUpdateInfo( $sFile );
401
- return ( !is_null( $oInfo ) && isset( $oInfo->new_version ) ) ? $oInfo->new_version : '';
402
- }
403
-
404
- /**
405
- * @param bool $bForceUpdateCheck
406
- * @return stdClass[]
407
- */
408
- public function getUpdates( $bForceUpdateCheck = false ) {
409
- if ( $bForceUpdateCheck ) {
410
- $this->clearUpdates();
411
- $this->checkForUpdates();
412
- }
413
- $aUpdates = $this->loadWp()->getWordpressUpdates( 'plugins' );
414
- return is_array( $aUpdates ) ? $aUpdates : [];
415
- }
416
-
417
- /**
418
- * @param string $sPluginFile
419
- * @return string
420
- */
421
- public function getUrl_Activate( $sPluginFile ) {
422
- return $this->getUrl_Action( $sPluginFile, 'activate' );
423
- }
424
-
425
- /**
426
- * @param string $sPluginFile
427
- * @return string
428
- */
429
- public function getUrl_Deactivate( $sPluginFile ) {
430
- return $this->getUrl_Action( $sPluginFile, 'deactivate' );
431
- }
432
-
433
- /**
434
- * @param string $sPluginFile
435
- * @return string
436
- */
437
- public function getUrl_Upgrade( $sPluginFile ) {
438
- $aQueryArgs = [
439
- 'action' => 'upgrade-plugin',
440
- 'plugin' => urlencode( $sPluginFile ),
441
- '_wpnonce' => wp_create_nonce( 'upgrade-plugin_'.$sPluginFile )
442
- ];
443
- return add_query_arg( $aQueryArgs, self_admin_url( 'update.php' ) );
444
- }
445
-
446
- /**
447
- * @param string $sPluginFile
448
- * @param string $sAction
449
- * @return string
450
- */
451
- protected function getUrl_Action( $sPluginFile, $sAction ) {
452
- return add_query_arg(
453
- [
454
- 'action' => $sAction,
455
- 'plugin' => urlencode( $sPluginFile ),
456
- '_wpnonce' => wp_create_nonce( $sAction.'-plugin_'.$sPluginFile )
457
- ],
458
- self_admin_url( 'plugins.php' )
459
- );
460
- }
461
-
462
- /**
463
- * @param string $sFile
464
- * @return bool
465
- */
466
- public function isActive( $sFile ) {
467
- return ( $this->isInstalled( $sFile ) && is_plugin_active( $sFile ) );
468
- }
469
-
470
- /**
471
- * @param string $sFile The full plugin file.
472
- * @return bool
473
- */
474
- public function isInstalled( $sFile ) {
475
- return in_array( $sFile, $this->getInstalledBaseFiles() );
476
- }
477
-
478
- /**
479
- * @param string $sFile
480
- * @return boolean
481
- */
482
- public function isUpdateAvailable( $sFile ) {
483
- return !is_null( $this->getUpdateInfo( $sFile ) );
484
- }
485
-
486
- /**
487
- * @param string $sBaseName
488
- * @return bool
489
- */
490
- public function isWpOrg( $sBaseName ) {
491
- $oPluginInfo = $this->getExtendedData( $sBaseName );
492
- return isset( $oPluginInfo->id ) ? strpos( $oPluginInfo->id, 'w.org/' ) === 0 : false;
493
- }
494
-
495
- /**
496
- * @param string $sFile
497
- * @param int $nDesiredPosition
498
- */
499
- public function setActivePluginLoadPosition( $sFile, $nDesiredPosition = 0 ) {
500
- $oWp = $this->loadWp();
501
-
502
- $aActive = $this->loadDP()
503
- ->setArrayValueToPosition(
504
- $oWp->getOption( 'active_plugins' ),
505
- $sFile,
506
- $nDesiredPosition
507
- );
508
- $oWp->updateOption( 'active_plugins', $aActive );
509
-
510
- if ( $oWp->isMultisite() ) {
511
- $aActive = $this->loadDP()
512
- ->setArrayValueToPosition( $oWp->getOption( 'active_sitewide_plugins' ), $sFile, $nDesiredPosition );
513
- $oWp->updateOption( 'active_sitewide_plugins', $aActive );
514
- }
515
- }
516
-
517
- /**
518
- * @param string $sFile
519
- */
520
- public function setActivePluginLoadFirst( $sFile ) {
521
- $this->setActivePluginLoadPosition( $sFile, 0 );
522
- }
523
-
524
- /**
525
- * @param string $sFile
526
- */
527
- public function setActivePluginLoadLast( $sFile ) {
528
- $this->setActivePluginLoadPosition( $sFile, 1000 );
529
- }
530
-
531
- /**
532
- * @param string $sPluginFile
533
- * @return string
534
- * @deprecated
535
- */
536
- public function getLinkPluginUpgrade( $sPluginFile ) {
537
- return $this->getUrl_Upgrade( $sPluginFile );
538
- }
539
-
540
- /**
541
- * @return array
542
- * @deprecated
543
- */
544
- public function getInstalledPluginFiles() {
545
- return $this->getInstalledBaseFiles();
546
- }
547
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/common/icwp-wpfunctions-themes.php DELETED
@@ -1,365 +0,0 @@
1
- <?php
2
-
3
- class ICWP_WPSF_WpFunctions_Themes extends ICWP_WPSF_Foundation {
4
-
5
- /**
6
- * @var ICWP_WPSF_WpFunctions_Themes
7
- */
8
- protected static $oInstance = null;
9
-
10
- private function __construct() {
11
- }
12
-
13
- /**
14
- * @return ICWP_WPSF_WpFunctions_Themes
15
- */
16
- public static function GetInstance() {
17
- if ( is_null( self::$oInstance ) ) {
18
- self::$oInstance = new self();
19
- }
20
- return self::$oInstance;
21
- }
22
-
23
- /**
24
- * @param string $sThemeStylesheet
25
- * @return bool
26
- */
27
- public function activate( $sThemeStylesheet ) {
28
- if ( empty( $sThemeStylesheet ) ) {
29
- return false;
30
- }
31
-
32
- $oTheme = $this->getTheme( $sThemeStylesheet );
33
- if ( !$oTheme->exists() ) {
34
- return false;
35
- }
36
-
37
- switch_theme( $oTheme->get_stylesheet() );
38
-
39
- // Now test currently active theme
40
- $oCurrentTheme = $this->getCurrent();
41
-
42
- return ( !is_null( $oCurrentTheme ) && ( $sThemeStylesheet == $oCurrentTheme->get_stylesheet() ) );
43
- }
44
-
45
- /**
46
- * @param string $sStylesheet
47
- * @return bool|WP_Error
48
- */
49
- public function delete( $sStylesheet ) {
50
- if ( empty( $sStylesheet ) ) {
51
- return false;
52
- }
53
- if ( !function_exists( 'delete_theme' ) ) {
54
- require_once( ABSPATH.'wp-admin/includes/theme.php' );
55
- }
56
- return function_exists( 'delete_theme' ) ? delete_theme( $sStylesheet ) : false;
57
- }
58
-
59
- /**
60
- * @param $sSlug
61
- * @return array|bool
62
- */
63
- public function installFromWpOrg( $sSlug ) {
64
- include_once( ABSPATH.'wp-admin/includes/plugin-install.php' );
65
-
66
- $oApi = $this->getExtendedData( $sSlug );
67
-
68
- if ( !is_wp_error( $oApi ) ) {
69
- return $this->install( $oApi->download_link, true, true );
70
- }
71
- return false;
72
- }
73
-
74
- /**
75
- * @param string $sUrlToInstall
76
- * @param bool $bOverwrite
77
- * @param bool $bMaintenanceMode
78
- * @return array
79
- */
80
- public function install( $sUrlToInstall, $bOverwrite = true, $bMaintenanceMode = false ) {
81
- $this->loadWpUpgrades();
82
-
83
- $aResult = [
84
- 'successful' => true,
85
- 'plugin_info' => '',
86
- 'errors' => []
87
- ];
88
-
89
- $oUpgraderSkin = new ICWP_Upgrader_Skin();
90
- $oUpgrader = new ICWP_Theme_Upgrader( $oUpgraderSkin );
91
- $oUpgrader->setOverwriteMode( $bOverwrite );
92
- if ( $bMaintenanceMode ) {
93
- $oUpgrader->maintenance_mode( true );
94
- }
95
-
96
- ob_start();
97
- $sInstallResult = $oUpgrader->install( $sUrlToInstall );
98
- ob_end_clean();
99
-
100
- if ( $bMaintenanceMode ) {
101
- $oUpgrader->maintenance_mode( false );
102
- }
103
-
104
- if ( is_wp_error( $oUpgraderSkin->m_aErrors[ 0 ] ) ) {
105
- $aResult[ 'successful' ] = false;
106
- $aResult[ 'errors' ] = $oUpgraderSkin->m_aErrors[ 0 ]->get_error_messages();
107
- }
108
- else {
109
- $aResult[ 'theme_info' ] = $oUpgrader->theme_info();
110
- }
111
-
112
- $aResult[ 'feedback' ] = $oUpgraderSkin->getFeedback();
113
- return $aResult;
114
- }
115
-
116
- /**
117
- * @param string $sSlug
118
- * @param bool $bUseBackup
119
- * @return bool
120
- */
121
- public function reinstall( $sSlug, $bUseBackup = false ) {
122
- $bSuccess = false;
123
-
124
- if ( $this->isInstalled( $sSlug ) ) {
125
- $oFS = $this->loadFS();
126
-
127
- $oTheme = $this->getTheme( $sSlug );
128
-
129
- $sDir = $oTheme->get_stylesheet_directory();
130
- $sBackupDir = dirname( $sDir ).'/../../'.$sSlug.'bak'.time();
131
- if ( $bUseBackup ) {
132
- rename( $sDir, $sBackupDir );
133
- }
134
-
135
- $aResult = $this->installFromWpOrg( $sSlug );
136
- $bSuccess = $aResult[ 'successful' ];
137
- if ( $bSuccess ) {
138
- wp_update_themes(); //refreshes our update information
139
- if ( $bUseBackup ) {
140
- $oFS->deleteDir( $sBackupDir );
141
- }
142
- }
143
- else {
144
- if ( $bUseBackup ) {
145
- $oFS->deleteDir( $sDir );
146
- rename( $sBackupDir, $sDir );
147
- }
148
- }
149
- }
150
- return $bSuccess;
151
- }
152
-
153
- /**
154
- * @param string $sFile
155
- * @return array
156
- */
157
- public function update( $sFile ) {
158
- $this->loadWpUpgrades();
159
-
160
- $aResult = [
161
- 'successful' => 1,
162
- 'errors' => []
163
- ];
164
-
165
- $oUpgraderSkin = new ICWP_Bulk_Theme_Upgrader_Skin();
166
- $oUpgrader = new Theme_Upgrader( $oUpgraderSkin );
167
- ob_start();
168
- $oUpgrader->bulk_upgrade( [ $sFile ] );
169
- ob_end_clean();
170
-
171
- if ( isset( $oUpgraderSkin->m_aErrors[ 0 ] ) ) {
172
- if ( is_wp_error( $oUpgraderSkin->m_aErrors[ 0 ] ) ) {
173
- $aResult[ 'successful' ] = 0;
174
- $aResult[ 'errors' ] = $oUpgraderSkin->m_aErrors[ 0 ]->get_error_messages();
175
- }
176
- }
177
- $aResult[ 'feedback' ] = $oUpgraderSkin->getFeedback();
178
- return $aResult;
179
- }
180
-
181
- /**
182
- * @return string|WP_Theme
183
- */
184
- public function getCurrentThemeName() {
185
- return $this->loadWp()->getWordpressIsAtLeastVersion( '3.4.0' ) ? $this->getCurrent()
186
- ->get( 'Name' ) : get_current_theme();
187
- }
188
-
189
- /**
190
- * @return null|WP_Theme
191
- */
192
- public function getCurrent() {
193
- return $this->getTheme();
194
- }
195
-
196
- /**
197
- * @param string $sStylesheet
198
- * @return bool
199
- */
200
- public function getExists( $sStylesheet ) {
201
- $oTheme = $this->getTheme( $sStylesheet );
202
- return ( !is_null( $oTheme ) && ( $oTheme instanceof WP_Theme ) && $oTheme->exists() );
203
- }
204
-
205
- /**
206
- * @param string $sStylesheet
207
- * @return null|WP_Theme
208
- */
209
- public function getTheme( $sStylesheet = null ) {
210
- if ( $this->loadWp()->getWordpressIsAtLeastVersion( '3.4.0' ) ) {
211
- if ( !function_exists( 'wp_get_theme' ) ) {
212
- require_once( ABSPATH.'wp-admin/includes/theme.php' );
213
- }
214
- return function_exists( 'wp_get_theme' ) ? wp_get_theme( $sStylesheet ) : null;
215
- }
216
- $aThemes = $this->getThemes();
217
- return array_key_exists( $sStylesheet, $aThemes ) ? $aThemes[ $sStylesheet ] : null;
218
- }
219
-
220
- /**
221
- * Abstracts the WordPress wp_get_themes()
222
- * @return array|WP_Theme[]
223
- */
224
- public function getThemes() {
225
- if ( !function_exists( 'wp_get_themes' ) ) {
226
- require_once( ABSPATH.'wp-admin/includes/theme.php' );
227
- }
228
- return function_exists( 'wp_get_themes' ) ? wp_get_themes() : get_themes();
229
- }
230
-
231
- /**
232
- * @param string $sSlug
233
- * @return array|null
234
- */
235
- public function getUpdateInfo( $sSlug ) {
236
- $aU = $this->getUpdates();
237
- return isset( $aU[ $sSlug ] ) ? $aU[ $sSlug ] : null;
238
- }
239
-
240
- /**
241
- * @param bool $bForceUpdateCheck
242
- * @return array
243
- */
244
- public function getUpdates( $bForceUpdateCheck = false ) {
245
- if ( $bForceUpdateCheck ) {
246
- $this->clearUpdates();
247
- $this->checkForUpdates();
248
- }
249
- $aUpdates = $this->loadWp()->getWordpressUpdates( 'themes' );
250
- return is_array( $aUpdates ) ? $aUpdates : [];
251
- }
252
-
253
- /**
254
- * @return null|WP_Theme
255
- */
256
- public function getCurrentParent() {
257
- $oTheme = $this->getCurrent();
258
- return $this->isActiveThemeAChild() ? $this->getTheme( $oTheme->get_template() ) : null;
259
- }
260
-
261
- /**
262
- * @param string $sBase
263
- * @return object|WP_Error
264
- */
265
- public function getExtendedData( $sBase ) {
266
- include_once( ABSPATH.'wp-admin/includes/theme.php' );
267
-
268
- $oApi = themes_api( 'theme_information', [
269
- 'slug' => $sBase,
270
- 'fields' => [
271
- 'sections' => false,
272
- ],
273
- ] );
274
- return $oApi;
275
- }
276
-
277
- /**
278
- * @param string $sSlug
279
- * @param bool $bCheckIsActiveParent
280
- * @return bool
281
- */
282
- public function isActive( $sSlug, $bCheckIsActiveParent = false ) {
283
- return ( $this->isInstalled( $sSlug ) && $this->getCurrent()->get_stylesheet() == $sSlug )
284
- || ( $bCheckIsActiveParent && $this->isActiveParent( $sSlug ) );
285
- }
286
-
287
- /**
288
- * @return bool
289
- */
290
- public function isActiveThemeAChild() {
291
- $oTheme = $this->getCurrent();
292
- return ( $oTheme->get_stylesheet() != $oTheme->get_template() );
293
- }
294
-
295
- /**
296
- * @param string $sSlug
297
- * @return bool - true if this is the Parent of the currently active theme
298
- */
299
- public function isActiveParent( $sSlug ) {
300
- return ( $this->isInstalled( $sSlug ) && $this->getCurrent()->get_template() == $sSlug );
301
- }
302
-
303
- /**
304
- * @param string $sSlug The directory slug.
305
- * @return bool
306
- */
307
- public function isInstalled( $sSlug ) {
308
- return !empty( $sSlug ) && !is_null( $this->getTheme( $sSlug ) );
309
- }
310
-
311
- /**
312
- * @param string $sSlug
313
- * @return boolean
314
- */
315
- public function isUpdateAvailable( $sSlug ) {
316
- return !is_null( $this->getUpdateInfo( $sSlug ) );
317
- }
318
-
319
- /**
320
- * @param string $sBaseName
321
- * @return bool
322
- */
323
- public function isWpOrg( $sBaseName ) {
324
- $bIsWpOrg = false;
325
- $oInfo = $this->getExtendedData( $sBaseName );
326
- if ( !empty( $oInfo ) && !is_wp_error( $oInfo ) && isset( $oInfo->download_link ) ) {
327
- $bIsWpOrg = strpos( $oInfo->download_link, 'https://downloads.wordpress.org' ) === 0;
328
- }
329
- return $bIsWpOrg;
330
- }
331
-
332
- /**
333
- * @return boolean|null
334
- */
335
- protected function checkForUpdates() {
336
-
337
- if ( class_exists( 'WPRC_Installer' ) && method_exists( 'WPRC_Installer', 'wprc_update_themes' ) ) {
338
- WPRC_Installer::wprc_update_themes();
339
- return true;
340
- }
341
- else if ( function_exists( 'wp_update_themes' ) ) {
342
- return ( wp_update_themes() !== false );
343
- }
344
- return null;
345
- }
346
-
347
- /**
348
- */
349
- protected function clearUpdates() {
350
- $sKey = 'update_themes';
351
- $oResponse = $this->loadWp()->getTransient( $sKey );
352
- if ( !is_object( $oResponse ) ) {
353
- $oResponse = new stdClass();
354
- }
355
- $oResponse->last_checked = 0;
356
- $this->loadWp()->setTransient( $sKey, $oResponse );
357
- }
358
-
359
- /**
360
- * @return array
361
- */
362
- public function wpmsGetSiteAllowedThemes() {
363
- return ( function_exists( 'get_site_allowed_themes' ) ? get_site_allowed_themes() : [] );
364
- }
365
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/common/icwp-wpfunctions.php DELETED
@@ -1,936 +0,0 @@
1
- <?php
2
-
3
- class ICWP_WPSF_WpFunctions extends ICWP_WPSF_Foundation {
4
-
5
- /**
6
- * @var WP_Automatic_Updater
7
- */
8
- protected $oWpAutomaticUpdater;
9
-
10
- /**
11
- * @var ICWP_WPSF_WpFunctions
12
- */
13
- protected static $oInstance = null;
14
-
15
- /**
16
- * @return ICWP_WPSF_WpFunctions
17
- */
18
- public static function GetInstance() {
19
- if ( is_null( self::$oInstance ) ) {
20
- self::$oInstance = new self();
21
- }
22
- return self::$oInstance;
23
- }
24
-
25
- /**
26
- * @var string
27
- */
28
- protected $sWpVersion;
29
-
30
- public function __construct() {
31
- }
32
-
33
- /**
34
- * @return null|string
35
- */
36
- public function findWpLoad() {
37
- return $this->findWpCoreFile( 'wp-load.php' );
38
- }
39
-
40
- /**
41
- * @param $sFilename
42
- * @return null|string
43
- */
44
- public function findWpCoreFile( $sFilename ) {
45
- $sLoaderPath = __DIR__;
46
- $nLimiter = 0;
47
- $nMaxLimit = count( explode( DIRECTORY_SEPARATOR, trim( $sLoaderPath, DIRECTORY_SEPARATOR ) ) );
48
- $bFound = false;
49
-
50
- do {
51
- if ( @is_file( $sLoaderPath.DIRECTORY_SEPARATOR.$sFilename ) ) {
52
- $bFound = true;
53
- break;
54
- }
55
- $sLoaderPath = realpath( $sLoaderPath.DIRECTORY_SEPARATOR.'..' );
56
- $nLimiter++;
57
- } while ( $nLimiter < $nMaxLimit );
58
-
59
- return $bFound ? $sLoaderPath.DIRECTORY_SEPARATOR.$sFilename : null;
60
- }
61
-
62
- /**
63
- * @param string $sRedirect
64
- * @return bool
65
- */
66
- public function doForceRunAutomaticUpdates( $sRedirect = '' ) {
67
-
68
- $lock_name = 'auto_updater.lock'; //ref: /wp-admin/includes/class-wp-upgrader.php
69
- delete_option( $lock_name );
70
- if ( !defined( 'DOING_CRON' ) ) {
71
- define( 'DOING_CRON', true ); // this prevents WP from disabling plugins pre-upgrade
72
- }
73
-
74
- // does the actual updating
75
- wp_maybe_auto_update();
76
-
77
- if ( !empty( $sRedirect ) ) {
78
- $this->doRedirect( network_admin_url( $sRedirect ) );
79
- }
80
- return true;
81
- }
82
-
83
- /**
84
- * @return bool
85
- */
86
- public function isRunningAutomaticUpdates() {
87
- return ( get_option( 'auto_updater.lock' ) ? true : false );
88
- }
89
-
90
- /**
91
- * The full plugin file to be upgraded.
92
- * @param string $sPluginFile
93
- * @return boolean
94
- */
95
- public function doPluginUpgrade( $sPluginFile ) {
96
- $oWpPlugins = $this->loadWpPlugins();
97
- if ( !$oWpPlugins->isUpdateAvailable( $sPluginFile )
98
- || ( isset( $GLOBALS[ 'pagenow' ] ) && $GLOBALS[ 'pagenow' ] == 'update.php' ) ) {
99
- return true;
100
- }
101
- wp_redirect( $oWpPlugins->getUrl_Upgrade( $sPluginFile ) );
102
- exit();
103
- }
104
-
105
- /**
106
- * Clears any WordPress caches
107
- */
108
- public function doBustCache() {
109
- global $_wp_using_ext_object_cache, $wp_object_cache;
110
- $_wp_using_ext_object_cache = false;
111
- if ( !empty( $wp_object_cache ) ) {
112
- @$wp_object_cache->flush();
113
- }
114
- }
115
-
116
- /**
117
- * @return bool
118
- */
119
- public function isPermalinksEnabled() {
120
- return ( $this->getOption( 'permalink_structure' ) ? true : false );
121
- }
122
-
123
- /**
124
- * @return array
125
- * @see wp_redirect_admin_locations()
126
- */
127
- public function getAutoRedirectLocations() {
128
- return [ 'wp-admin', 'dashboard', 'admin', 'login', 'wp-login.php' ];
129
- }
130
-
131
- /**
132
- * @return array|false
133
- */
134
- public function getCoreUpdates() {
135
- include_once( ABSPATH.'wp-admin/includes/update.php' );
136
- return get_core_updates();
137
- }
138
-
139
- /**
140
- * @return string
141
- */
142
- public function getDirUploads() {
143
- $aDirParts = wp_get_upload_dir();
144
- $bHasUploads = is_array( $aDirParts ) && !empty( $aDirParts[ 'basedir' ] )
145
- && $this->loadFS()->exists( $aDirParts[ 'basedir' ] );
146
- return $bHasUploads ? $aDirParts[ 'basedir' ] : '';
147
- }
148
-
149
- /**
150
- * @param string $sPath
151
- * @return string
152
- */
153
- public function getUrl_WpAdmin( $sPath = '' ) {
154
- return get_admin_url( null, $sPath );
155
- }
156
-
157
- /**
158
- * @param string $sPath
159
- * @return string
160
- */
161
- public function getHomeUrl( $sPath = '' ) {
162
- $sUrl = home_url( $sPath );
163
- if ( empty( $sUrl ) ) {
164
- remove_all_filters( 'home_url' );
165
- $sUrl = home_url( $sPath );
166
- }
167
- return $sUrl;
168
- }
169
-
170
- /**
171
- * @param bool $bRemoveSchema
172
- * @return string
173
- */
174
- public function getWpUrl( $bRemoveSchema = false ) {
175
- $sUrl = network_site_url();
176
- if ( empty( $sUrl ) ) {
177
- remove_all_filters( 'site_url' );
178
- remove_all_filters( 'network_site_url' );
179
- $sUrl = network_site_url();
180
- }
181
- if ( $bRemoveSchema ) {
182
- $sUrl = preg_replace( '#^((http|https):)?\/\/#i', '', $sUrl );
183
- }
184
- return $sUrl;
185
- }
186
-
187
- /**
188
- * @param string $sSeparator
189
- * @return string
190
- */
191
- public function getLocale( $sSeparator = '_' ) {
192
- return str_replace( '_', $sSeparator, get_locale() );
193
- }
194
-
195
- /**
196
- * @return string
197
- */
198
- public function getLocaleForChecksums() {
199
- global $wp_local_package;
200
- return empty( $wp_local_package ) ? 'en_US' : $wp_local_package;
201
- }
202
-
203
- /**
204
- * @param stdClass|string $mItem
205
- * @param string $sContext from plugin|theme
206
- * @return string
207
- */
208
- public function getFileFromAutomaticUpdateItem( $mItem, $sContext = 'plugin' ) {
209
- if ( is_object( $mItem ) && isset( $mItem->{$sContext} ) ) { // WP 3.8.2+
210
- $mItem = $mItem->{$sContext};
211
- }
212
- else if ( !is_string( $mItem ) ) { // WP pre-3.8.2
213
- $mItem = '';
214
- }
215
- return $mItem;
216
- }
217
-
218
- /**
219
- * @return array
220
- */
221
- public function getThemes() {
222
- if ( !function_exists( 'wp_get_themes' ) ) {
223
- require_once( ABSPATH.'wp-admin/includes/theme.php' );
224
- }
225
- return function_exists( 'wp_get_themes' ) ? wp_get_themes() : [];
226
- }
227
-
228
- /**
229
- * @param string $sType - plugins, themes
230
- * @return array
231
- */
232
- public function getWordpressUpdates( $sType = 'plugins' ) {
233
- $oCurrent = $this->getTransient( 'update_'.$sType );
234
- return ( isset( $oCurrent->response ) && is_array( $oCurrent->response ) ) ? $oCurrent->response : [];
235
- }
236
-
237
- /**
238
- * @return array
239
- */
240
- public function getWordpressUpdates_Themes() {
241
- return $this->getWordpressUpdates( 'themes' );
242
- }
243
-
244
- /**
245
- * @param string $sKey
246
- * @return mixed
247
- */
248
- public function getTransient( $sKey ) {
249
- // TODO: Handle multisite
250
-
251
- if ( function_exists( 'get_site_transient' ) ) {
252
- $mResult = get_site_transient( $sKey );
253
- if ( empty( $mResult ) ) {
254
- remove_all_filters( 'pre_site_transient_'.$sKey );
255
- $mResult = get_site_transient( $sKey );
256
- }
257
- }
258
- else if ( version_compare( $this->getVersion(), '2.7.9', '<=' ) ) {
259
- $mResult = get_option( $sKey );
260
- }
261
- else if ( version_compare( $this->getVersion(), '2.9.9', '<=' ) ) {
262
- $mResult = apply_filters( 'transient_'.$sKey, get_option( '_transient_'.$sKey ) );
263
- }
264
- else {
265
- $mResult = apply_filters( 'site_transient_'.$sKey, get_option( '_site_transient_'.$sKey ) );
266
- }
267
- return $mResult;
268
- }
269
-
270
- /**
271
- * @param string $sKey
272
- * @param mixed $mValue
273
- * @param int $nExpire
274
- * @return bool
275
- */
276
- public function setTransient( $sKey, $mValue, $nExpire = 0 ) {
277
- return set_site_transient( $sKey, $mValue, $nExpire );
278
- }
279
-
280
- /**
281
- * @param $sKey
282
- * @return bool
283
- */
284
- public function deleteTransient( $sKey ) {
285
-
286
- if ( version_compare( $this->getVersion(), '2.7.9', '<=' ) ) {
287
- $bResult = delete_option( $sKey );
288
- }
289
- else if ( function_exists( 'delete_site_transient' ) ) {
290
- $bResult = delete_site_transient( $sKey );
291
- }
292
- else if ( version_compare( $this->getVersion(), '2.9.9', '<=' ) ) {
293
- $bResult = delete_option( '_transient_'.$sKey );
294
- }
295
- else {
296
- $bResult = delete_option( '_site_transient_'.$sKey );
297
- }
298
- return $bResult;
299
- }
300
-
301
- /**
302
- * @return string
303
- */
304
- public function getVersion() {
305
-
306
- if ( empty( $this->sWpVersion ) ) {
307
- $sVersionFile = ABSPATH.WPINC.'/version.php';
308
- $sVersionContents = file_get_contents( $sVersionFile );
309
-
310
- if ( preg_match( '/wp_version\s=\s\'([^(\'|")]+)\'/i', $sVersionContents, $aMatches ) ) {
311
- $this->sWpVersion = $aMatches[ 1 ];
312
- }
313
- else {
314
- global $wp_version;
315
- $this->sWpVersion = $wp_version;
316
- }
317
- }
318
- return $this->sWpVersion;
319
- }
320
-
321
- /**
322
- * @param string $sVersionToMeet
323
- * @return boolean
324
- */
325
- public function getWordpressIsAtLeastVersion( $sVersionToMeet ) {
326
- return version_compare( $this->getVersion(), $sVersionToMeet, '>=' );
327
- }
328
-
329
- /**
330
- * @param string $sPluginBaseFilename
331
- * @return boolean
332
- */
333
- public function isPluginAutomaticallyUpdated( $sPluginBaseFilename ) {
334
- $oUpdater = $this->getWpAutomaticUpdater();
335
- if ( !$oUpdater ) {
336
- return false;
337
- }
338
-
339
- // This is due to a change in the filter introduced in version 3.8.2
340
- if ( $this->getWordpressIsAtLeastVersion( '3.8.2' ) ) {
341
- $mPluginItem = new stdClass();
342
- $mPluginItem->plugin = $sPluginBaseFilename;
343
- }
344
- else {
345
- $mPluginItem = $sPluginBaseFilename;
346
- }
347
-
348
- return $oUpdater->should_update( 'plugin', $mPluginItem, WP_PLUGIN_DIR );
349
- }
350
-
351
- /**
352
- * @return bool
353
- */
354
- public function canCoreUpdateAutomatically() {
355
- global $required_php_version, $required_mysql_version;
356
- $future_minor_update = (object)[
357
- 'current' => $this->getVersion().'.1.next.minor',
358
- 'version' => $this->getVersion().'.1.next.minor',
359
- 'php_version' => $required_php_version,
360
- 'mysql_version' => $required_mysql_version,
361
- ];
362
- return $this->getWpAutomaticUpdater()
363
- ->should_update( 'core', $future_minor_update, ABSPATH );
364
- }
365
-
366
- /**
367
- * See: /wp-admin/update-core.php core_upgrade_preamble()
368
- * @return bool
369
- */
370
- public function hasCoreUpdate() {
371
- $aUpdates = $this->getCoreUpdates();
372
- return ( isset( $aUpdates[ 0 ]->response ) && 'latest' != $aUpdates[ 0 ]->response );
373
- }
374
-
375
- public function redirectHere() {
376
- $this->doRedirect( $this->loadRequest()->getUri() );
377
- }
378
-
379
- /**
380
- * @param array $aQueryParams
381
- */
382
- public function redirectToLogin( $aQueryParams = [] ) {
383
- $this->doRedirect( wp_login_url(), $aQueryParams );
384
- }
385
-
386
- /**
387
- * @param array $aQueryParams
388
- */
389
- public function redirectToAdmin( $aQueryParams = [] ) {
390
- $this->doRedirect( is_multisite() ? get_admin_url() : admin_url(), $aQueryParams );
391
- }
392
-
393
- /**
394
- * @param array $aQueryParams
395
- */
396
- public function redirectToHome( $aQueryParams = [] ) {
397
- $this->doRedirect( home_url(), $aQueryParams );
398
- }
399
-
400
- /**
401
- * @param string $sUrl
402
- * @param array $aQueryParams
403
- * @param bool $bSafe
404
- * @param bool $bProtectAgainstInfiniteLoops - if false, ignores the redirect loop protection
405
- */
406
- public function doRedirect( $sUrl, $aQueryParams = [], $bSafe = true, $bProtectAgainstInfiniteLoops = true ) {
407
- $sUrl = empty( $aQueryParams ) ? $sUrl : add_query_arg( $aQueryParams, $sUrl );
408
-
409
- $oReq = $this->loadRequest();
410
- // we prevent any repetitive redirect loops
411
- if ( $bProtectAgainstInfiniteLoops ) {
412
- if ( $oReq->cookie( 'icwp-isredirect' ) == 'yes' ) {
413
- return;
414
- }
415
- else {
416
- $oReq->setCookie( 'icwp-isredirect', 'yes', 5 );
417
- }
418
- }
419
-
420
- // based on: https://make.wordpress.org/plugins/2015/04/20/fixing-add_query_arg-and-remove_query_arg-usage/
421
- // we now escape the URL to be absolutely sure since we can't guarantee the URL coming through there
422
- $sUrl = esc_url_raw( $sUrl );
423
- $bSafe ? wp_redirect( $sUrl ) : wp_redirect( $sUrl );
424
- exit();
425
- }
426
-
427
- /**
428
- * @return string
429
- */
430
- public function getCurrentPage() {
431
- global $pagenow;
432
- return $pagenow;
433
- }
434
-
435
- /**
436
- * @return WP_Post
437
- */
438
- public function getCurrentPost() {
439
- global $post;
440
- return $post;
441
- }
442
-
443
- /**
444
- * @return int
445
- */
446
- public function getCurrentPostId() {
447
- $oPost = $this->getCurrentPost();
448
- return empty( $oPost->ID ) ? -1 : $oPost->ID;
449
- }
450
-
451
- /**
452
- * @param $nPostId
453
- * @return false|WP_Post
454
- */
455
- public function getPostById( $nPostId ) {
456
- return WP_Post::get_instance( $nPostId );
457
- }
458
-
459
- /**
460
- * @param string $sPageSlug
461
- * @param bool $bWpmsOnly
462
- * @return string
463
- */
464
- public function getUrl_AdminPage( $sPageSlug, $bWpmsOnly = false ) {
465
- $sUrl = sprintf( 'admin.php?page=%s', $sPageSlug );
466
- return $bWpmsOnly ? network_admin_url( $sUrl ) : admin_url( $sUrl );
467
- }
468
-
469
- /**
470
- * @param string $sPath
471
- * @param bool $bWpmsOnly
472
- * @return string
473
- */
474
- public function getAdminUrl( $sPath = '', $bWpmsOnly = false ) {
475
- return $bWpmsOnly ? network_admin_url( $sPath ) : admin_url( $sPath );
476
- }
477
-
478
- /**
479
- * @param bool $bWpmsOnly
480
- * @return string
481
- */
482
- public function getAdminUrl_Plugins( $bWpmsOnly = false ) {
483
- return $this->getAdminUrl( 'plugins.php', $bWpmsOnly );
484
- }
485
-
486
- /**
487
- * @param bool $bWpmsOnly
488
- * @return string
489
- */
490
- public function getAdminUrl_Themes( $bWpmsOnly = false ) {
491
- return $this->getAdminUrl( 'themes.php', $bWpmsOnly );
492
- }
493
-
494
- /**
495
- * @param bool $bWpmsOnly
496
- * @return string
497
- */
498
- public function getAdminUrl_Updates( $bWpmsOnly = false ) {
499
- return $this->getAdminUrl( 'update-core.php', $bWpmsOnly );
500
- }
501
-
502
- /**
503
- * @return string
504
- */
505
- public function getUrl_CurrentAdminPage() {
506
-
507
- $sPage = $this->getCurrentPage();
508
- $sUrl = self_admin_url( $sPage );
509
-
510
- //special case for plugin admin pages.
511
- if ( $sPage == 'admin.php' ) {
512
- $sSubPage = $this->loadRequest()->query( 'page' );
513
- if ( !empty( $sSubPage ) ) {
514
- $aQueryArgs = [
515
- 'page' => $sSubPage,
516
- ];
517
- $sUrl = add_query_arg( $aQueryArgs, $sUrl );
518
- }
519
- }
520
- return $sUrl;
521
- }
522
-
523
- /**
524
- * @param string
525
- * @return string
526
- */
527
- public function isCurrentPage( $sPage ) {
528
- return $sPage == $this->getCurrentPage();
529
- }
530
-
531
- /**
532
- * @param string
533
- * @return string
534
- */
535
- public function isPage_Updates() {
536
- return $this->isCurrentPage( 'update.php' );
537
- }
538
-
539
- /**
540
- * @param string $sUrlPath
541
- * @return bool
542
- */
543
- public function isLoginUrl( $sUrlPath ) {
544
- $sLoginUrlPath = @parse_url( wp_login_url(), PHP_URL_PATH );
545
- return ( !empty( $sUrlPath ) && ( rtrim( $sUrlPath, '/' ) == rtrim( $sLoginUrlPath, '/' ) ) );
546
- }
547
-
548
- /**
549
- * @return bool
550
- */
551
- public function isRequestLoginUrl() {
552
- return $this->isLoginUrl( $this->loadRequest()->getPath() );
553
- }
554
-
555
- /**
556
- * @return bool
557
- */
558
- public function isRequestUserLogin() {
559
- $oReq = $this->loadRequest();
560
- return $this->isRequestLoginUrl() && $oReq->isMethodPost()
561
- && !is_null( $oReq->post( 'log' ) ) && !is_null( $oReq->post( 'pwd' ) );
562
- }
563
-
564
- /**
565
- * @return bool
566
- */
567
- public function isRequestUserRegister() {
568
- $oReq = $this->loadRequest();
569
- return $oReq->isMethodPost() && !is_null( $oReq->post( 'user_login' ) )
570
- && !is_null( $oReq->post( 'user_email' ) ) && $this->isRequestLoginUrl();
571
- }
572
-
573
- /**
574
- * @return bool
575
- */
576
- public function isRequestUserResetPasswordStart() {
577
- $oReq = $this->loadRequest();
578
- return $this->isRequestLoginUrl() && $oReq->isMethodPost() && !is_null( $oReq->post( 'user_login' ) );
579
- }
580
-
581
- /**
582
- * @return int
583
- */
584
- public function getAuthCookieExpiration() {
585
- return (int)apply_filters( 'auth_cookie_expiration', 14*DAY_IN_SECONDS, 0, false );
586
- }
587
-
588
- /**
589
- * @param $sTermSlug
590
- * @return bool
591
- */
592
- public function getDoesWpSlugExist( $sTermSlug ) {
593
- return ( $this->getDoesWpPostSlugExist( $sTermSlug ) || term_exists( $sTermSlug ) );
594
- }
595
-
596
- /**
597
- * @param $sTermSlug
598
- * @return bool
599
- */
600
- public function getDoesWpPostSlugExist( $sTermSlug ) {
601
- $oDb = $this->loadDbProcessor();
602
- $sQuery = "
603
- SELECT ID FROM %s
604
- WHERE post_name = '%s'
605
- LIMIT 1
606
- ";
607
- $sQuery = sprintf(
608
- $sQuery,
609
- $oDb->getTable_Posts(),
610
- $sTermSlug
611
- );
612
- $nResult = $oDb->getVar( $sQuery );
613
- return !is_null( $nResult ) && $nResult > 0;
614
- }
615
-
616
- /**
617
- * @return string
618
- */
619
- public function getSiteName() {
620
- return function_exists( 'get_bloginfo' ) ? get_bloginfo( 'name' ) : 'WordPress Site';
621
- }
622
-
623
- /**
624
- * @return string
625
- */
626
- public function getSiteAdminEmail() {
627
- return function_exists( 'get_bloginfo' ) ? get_bloginfo( 'admin_email' ) : '';
628
- }
629
-
630
- /**
631
- * @return string
632
- */
633
- public function getCookieDomain() {
634
- return defined( 'COOKIE_DOMAIN' ) ? COOKIE_DOMAIN : false;
635
- }
636
-
637
- /**
638
- * @return string
639
- */
640
- public function getCookiePath() {
641
- return defined( 'COOKIEPATH' ) ? COOKIEPATH : '/';
642
- }
643
-
644
- /**
645
- * @return boolean
646
- */
647
- public function isAjax() {
648
- return defined( 'DOING_AJAX' ) && DOING_AJAX;
649
- }
650
-
651
- /**
652
- * @return boolean
653
- */
654
- public function isCron() {
655
- return defined( 'DOING_CRON' ) && DOING_CRON;
656
- }
657
-
658
- /**
659
- * @return boolean
660
- */
661
- public function isXmlrpc() {
662
- return defined( 'XMLRPC_REQUEST' ) && XMLRPC_REQUEST;
663
- }
664
-
665
- /**
666
- * @return boolean
667
- */
668
- public function isMobile() {
669
- return function_exists( 'wp_is_mobile' ) && wp_is_mobile();
670
- }
671
-
672
- /**
673
- * @return array
674
- */
675
- public function getAllUserLoginUsernames() {
676
- $aUsers = get_users( [ 'fields' => [ 'user_login' ] ] );
677
- $aLogins = [];
678
- foreach ( $aUsers as $oUser ) {
679
- $aLogins[] = $oUser->user_login;
680
- }
681
- return $aLogins;
682
- }
683
-
684
- /**
685
- * @return bool
686
- */
687
- public function isMultisite() {
688
- return function_exists( 'is_multisite' ) && is_multisite();
689
- }
690
-
691
- /**
692
- * @return bool
693
- */
694
- public function isMultisite_SubdomainInstall() {
695
- return $this->isMultisite() && defined( 'SUBDOMAIN_INSTALL' ) && SUBDOMAIN_INSTALL;
696
- }
697
-
698
- /**
699
- * @return bool
700
- */
701
- public function isRest() {
702
- $bIsRest = ( defined( 'REST_REQUEST' ) && REST_REQUEST ) || !empty( $_REQUEST[ 'rest_route' ] );
703
-
704
- global $wp_rewrite;
705
- if ( !$bIsRest && function_exists( 'rest_url' ) && is_object( $wp_rewrite ) ) {
706
- $sRestUrlBase = get_rest_url( get_current_blog_id(), '/' );
707
- $sRestPath = trim( parse_url( $sRestUrlBase, PHP_URL_PATH ), '/' );
708
- $sRequestPath = trim( $this->loadRequest()->getPath(), '/' );
709
- $bIsRest = !empty( $sRequestPath ) && !empty( $sRestPath )
710
- && ( strpos( $sRequestPath, $sRestPath ) === 0 );
711
- }
712
- return $bIsRest;
713
- }
714
-
715
- /**
716
- * @return string|null
717
- */
718
- public function getRestNamespace() {
719
- $sNameSpace = null;
720
-
721
- $sPath = $this->getRestPath();
722
-
723
- if ( !empty( $sPath ) ) {
724
- $aParts = explode( '/', $sPath );
725
- if ( !empty( $aParts ) ) {
726
- $sNameSpace = $aParts[ 0 ];
727
- }
728
- }
729
- return $sNameSpace;
730
- }
731
-
732
- /**
733
- * @return string|null
734
- */
735
- public function getRestPath() {
736
- $sPath = null;
737
-
738
- if ( $this->isRest() ) {
739
- $oReq = $this->loadRequest();
740
-
741
- $sPath = $oReq->request( 'rest_route' );
742
- if ( empty( $sPath ) && $this->isPermalinksEnabled() ) {
743
- $sFullUri = $this->loadWp()->getHomeUrl( $oReq->getPath() );
744
- $sPath = substr( $sFullUri, strlen( get_rest_url( get_current_blog_id() ) ) );
745
- }
746
- }
747
- return $sPath;
748
- }
749
-
750
- /**
751
- * @param string $sKey
752
- * @param string $sValue
753
- * @return bool
754
- */
755
- public function addOption( $sKey, $sValue ) {
756
- return $this->isMultisite() ? add_site_option( $sKey, $sValue ) : add_option( $sKey, $sValue );
757
- }
758
-
759
- /**
760
- * @param string $sKey
761
- * @param $sValue
762
- * @return boolean
763
- */
764
- public function updateOption( $sKey, $sValue ) {
765
- return $this->isMultisite() ? update_site_option( $sKey, $sValue ) : update_option( $sKey, $sValue );
766
- }
767
-
768
- /**
769
- * @param string $sKey
770
- * @param mixed $mDefault
771
- * @return mixed
772
- */
773
- public function getOption( $sKey, $mDefault = false ) {
774
- return $this->isMultisite() ? get_site_option( $sKey, $mDefault ) : get_option( $sKey, $mDefault );
775
- }
776
-
777
- /**
778
- * @param string $sKey
779
- * @return mixed
780
- */
781
- public function deleteOption( $sKey ) {
782
- return $this->isMultisite() ? delete_site_option( $sKey ) : delete_option( $sKey );
783
- }
784
-
785
- /**
786
- * @return string
787
- */
788
- public function getCurrentWpAdminPage() {
789
-
790
- $oReq = $this->loadRequest();
791
- $sScript = $oReq->getScriptName();
792
- if ( is_admin() && !empty( $sScript ) && basename( $sScript ) == 'admin.php' ) {
793
- $sCurrentPage = $oReq->query( 'page' );
794
- }
795
- return empty( $sCurrentPage ) ? '' : $sCurrentPage;
796
- }
797
-
798
- /**
799
- * @param int|null $nTime
800
- * @param bool $bShowTime
801
- * @param bool $bShowDate
802
- * @return string
803
- */
804
- public function getTimeStringForDisplay( $nTime = null, $bShowTime = true, $bShowDate = true ) {
805
- $nTime = empty( $nTime ) ? $this->loadRequest()->ts() : $nTime;
806
-
807
- $sFullTimeString = $bShowTime ? $this->getTimeFormat() : '';
808
- if ( empty( $sFullTimeString ) ) {
809
- $sFullTimeString = $bShowDate ? $this->getDateFormat() : '';
810
- }
811
- else {
812
- $sFullTimeString = $bShowDate ? ( $sFullTimeString.' '.$this->getDateFormat() ) : $sFullTimeString;
813
- }
814
- return date_i18n( $sFullTimeString, $this->getTimeAsGmtOffset( $nTime ) );
815
- }
816
-
817
- /**
818
- * @param int|null $nTime
819
- * @return string
820
- */
821
- public function getTimeStampForDisplay( $nTime = null ) {
822
- $nTime = empty( $nTime ) ? $this->loadRequest()->ts() : $nTime;
823
- return date_i18n( DATE_RFC2822, $this->getTimeAsGmtOffset( $nTime ) );
824
- }
825
-
826
- /**
827
- * @param int $nTime
828
- * @return int
829
- */
830
- public function getTimeAsGmtOffset( $nTime = null ) {
831
-
832
- $nTimezoneOffset = wp_timezone_override_offset();
833
- if ( $nTimezoneOffset === false ) {
834
- $nTimezoneOffset = $this->getOption( 'gmt_offset' );
835
- if ( empty( $nTimezoneOffset ) ) {
836
- $nTimezoneOffset = 0;
837
- }
838
- }
839
-
840
- $nTime = is_null( $nTime ) ? $this->loadRequest()->ts() : $nTime;
841
- return $nTime + ( $nTimezoneOffset*HOUR_IN_SECONDS );
842
- }
843
-
844
- /**
845
- * @return string
846
- */
847
- public function getTimeFormat() {
848
- $sFormat = $this->getOption( 'time_format' );
849
- if ( empty( $sFormat ) ) {
850
- $sFormat = 'H:i';
851
- }
852
- return $sFormat;
853
- }
854
-
855
- /**
856
- * @return string
857
- */
858
- public function getDateFormat() {
859
- $sFormat = $this->getOption( 'date_format' );
860
- if ( empty( $sFormat ) ) {
861
- $sFormat = 'F j, Y';
862
- }
863
- return $sFormat;
864
- }
865
-
866
- /**
867
- * @return false|WP_Automatic_Updater
868
- */
869
- public function getWpAutomaticUpdater() {
870
- if ( !isset( $this->oWpAutomaticUpdater ) ) {
871
- if ( !class_exists( 'WP_Automatic_Updater', false ) ) {
872
- include_once( ABSPATH.'wp-admin/includes/class-wp-upgrader.php' );
873
- }
874
- if ( class_exists( 'WP_Automatic_Updater', false ) ) {
875
- $this->oWpAutomaticUpdater = new WP_Automatic_Updater();
876
- }
877
- else {
878
- $this->oWpAutomaticUpdater = false;
879
- }
880
- }
881
- return $this->oWpAutomaticUpdater;
882
- }
883
-
884
- /**
885
- * Flushes the Rewrite rules and forces a re-commit to the .htaccess where applicable
886
- */
887
- public function resavePermalinks() {
888
- /** @var WP_Rewrite $wp_rewrite */
889
- global $wp_rewrite;
890
- if ( is_object( $wp_rewrite ) ) {
891
- $wp_rewrite->flush_rules( true );
892
- }
893
- }
894
-
895
- /**
896
- * @return bool
897
- */
898
- public function turnOffCache() {
899
- if ( !defined( 'DONOTCACHEPAGE' ) ) {
900
- define( 'DONOTCACHEPAGE', true );
901
- }
902
-
903
- add_action( 'shutdown', function () {
904
- // WP Fastest Cache
905
- if ( isset( $GLOBALS[ 'wp_fastest_cache' ] ) &&
906
- $GLOBALS[ 'wp_fastest_cache' ] instanceof \WpFastestCache
907
- && method_exists( $GLOBALS[ 'wp_fastest_cache' ], 'singleDeleteCache' ) ) {
908
- $nPostId = $this->getCurrentPostId();
909
- if ( $nPostId > 0 ) {
910
- $GLOBALS[ 'wp_fastest_cache' ]->singleDeleteCache( false, $this->getCurrentPostId() );
911
- }
912
- }
913
- } );
914
- return DONOTCACHEPAGE;
915
- }
916
-
917
- /**
918
- * @param string $sMessage
919
- * @param string $sTitle
920
- * @param bool $bTurnOffCachePage
921
- */
922
- public function wpDie( $sMessage, $sTitle = '', $bTurnOffCachePage = true ) {
923
- if ( $bTurnOffCachePage ) {
924
- $this->turnOffCache();
925
- }
926
- wp_die( $sMessage, $sTitle );
927
- }
928
-
929
- /**
930
- * @return string[]
931
- * @deprecated
932
- */
933
- public function getCoreChecksums() {
934
- return \FernleafSystems\Wordpress\Services\Services::WpGeneral()->getCoreChecksums();
935
- }
936
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/common/icwp-wpincludes.php DELETED
@@ -1,79 +0,0 @@
1
- <?php
2
-
3
- use FernleafSystems\Wordpress\Services\Services;
4
-
5
- class ICWP_WPSF_WpIncludes extends ICWP_WPSF_Foundation {
6
-
7
- /**
8
- * @var ICWP_WPSF_WpIncludes
9
- */
10
- protected static $oInstance = null;
11
-
12
- /**
13
- * @return ICWP_WPSF_WpIncludes
14
- */
15
- public static function GetInstance() {
16
- if ( is_null( self::$oInstance ) ) {
17
- self::$oInstance = new self();
18
- }
19
- return self::$oInstance;
20
- }
21
-
22
- public function __construct() {
23
- }
24
-
25
- /**
26
- * @return string
27
- */
28
- public function getUrl_Jquery() {
29
- return $this->getJsUrl( 'jquery/jquery.js' );
30
- }
31
-
32
- /**
33
- * @param string $sJsInclude
34
- * @return string
35
- */
36
- public function getJsUrl( $sJsInclude ) {
37
- return $this->getIncludeUrl( path_join( 'js', $sJsInclude ) );
38
- }
39
-
40
- /**
41
- * @param string $sInclude
42
- * @return string
43
- */
44
- public function getIncludeUrl( $sInclude ) {
45
- $sInclude = path_join( 'wp-includes', $sInclude );
46
- return $this->addIncludeModifiedParam( path_join( $this->loadWp()->getWpUrl(), $sInclude ), $sInclude );
47
- }
48
-
49
- /**
50
- * @param string $sUrl
51
- * @param string $sInclude
52
- * @return string
53
- */
54
- public function addIncludeModifiedParam( $sUrl, $sInclude ) {
55
- $nTime = \FernleafSystems\Wordpress\Services\Services::WpFs()
56
- ->getModifiedTime( path_join( ABSPATH, $sInclude ) );
57
- return add_query_arg( [ 'mtime' => $nTime ], $sUrl );
58
- }
59
-
60
- /**
61
- * Supports PHP 5.3+
62
- * @param string $sIncludeHandle
63
- * @param string $sAttribute
64
- * @param string $sValue
65
- * @return $this
66
- */
67
- public function addIncludeAttribute( $sIncludeHandle, $sAttribute, $sValue ) {
68
- add_filter( 'script_loader_tag',
69
- function ( $sTag, $sHandle ) use ( $sIncludeHandle, $sAttribute, $sValue ) {
70
- if ( $sHandle == $sIncludeHandle && strpos( $sTag, $sAttribute.'=' ) === false ) {
71
- $sTag = str_replace( ' src', sprintf( ' %s="%s" src', $sAttribute, $sValue ), $sTag );
72
- }
73
- return $sTag;
74
- },
75
- 10, 2
76
- );
77
- return $this;
78
- }
79
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/common/icwp-wpupgrades.php DELETED
@@ -1,358 +0,0 @@
1
- <?php
2
-
3
- class ICWP_WPSF_WpUpgrades extends ICWP_WPSF_Foundation {
4
-
5
- /**
6
- * @var ICWP_WPSF_WpUpgrades
7
- */
8
- protected static $oInstance = null;
9
-
10
- /**
11
- * @return ICWP_WPSF_WpUpgrades
12
- */
13
- public static function GetInstance() {
14
- if ( is_null( self::$oInstance ) ) {
15
- self::$oInstance = new self();
16
- }
17
- return self::$oInstance;
18
- }
19
- }
20
-
21
- require_once( ABSPATH.'wp-admin/includes/upgrade.php' );
22
- require_once( ABSPATH.'wp-admin/includes/class-wp-upgrader.php' );
23
-
24
- if ( class_exists( 'WP_Upgrader_Skin', false ) ) {
25
-
26
- /**
27
- * Class ICWP_Upgrader_Skin
28
- */
29
- class ICWP_Upgrader_Skin extends WP_Upgrader_Skin {
30
-
31
- public $m_aErrors;
32
-
33
- public $aFeedback;
34
-
35
- public function __construct() {
36
- parent::__construct();
37
- $this->done_header = true;
38
- }
39
-
40
- /**
41
- * @return array
42
- */
43
- public function getFeedback() {
44
- return $this->aFeedback;
45
- }
46
-
47
- function error( $errors ) {
48
- }
49
-
50
- function feedback( $string ) {
51
- }
52
- }
53
- }
54
-
55
- if ( class_exists( 'Plugin_Upgrader' ) ) {
56
- class ICWP_Plugin_Upgrader extends Plugin_Upgrader {
57
-
58
- protected $fModeOverwrite = true;
59
-
60
- public function install( $package, $args = [] ) {
61
-
62
- $defaults = [
63
- 'clear_update_cache' => true,
64
- ];
65
- $parsed_args = wp_parse_args( $args, $defaults );
66
-
67
- $this->init();
68
- $this->install_strings();
69
-
70
- add_filter( 'upgrader_source_selection', [ $this, 'check_package' ] );
71
- add_filter( 'upgrader_clear_destination', [ $this, 'clearStatCache' ] );
72
-
73
- $oResult = $this->run( [
74
- 'package' => $package,
75
- 'destination' => WP_PLUGIN_DIR,
76
- 'clear_destination' => $this->getOverwriteMode(),
77
- // key to overwrite and why we're extending the native wordpress class
78
- 'clear_working' => true,
79
- 'hook_extra' => [
80
- 'type' => 'plugin',
81
- 'action' => 'install',
82
- ]
83
- ] );
84
-
85
- remove_filter( 'upgrader_source_selection', [ $this, 'check_package' ] );
86
- remove_filter( 'upgrader_clear_destination', [ $this, 'clearStatCache' ] );
87
-
88
- if ( !$this->result || is_wp_error( $this->result ) ) {
89
- return $this->result;
90
- }
91
-
92
- // Force refresh of plugin update information
93
- wp_clean_plugins_cache( $parsed_args[ 'clear_update_cache' ] );
94
-
95
- return true;
96
- }
97
-
98
- /**
99
- * This is inserted right after clearing the target directory. It seems that some systems are slow
100
- * in updating filesystem "info" because we were receiving permission denied when trying to recreate
101
- * the install directory.
102
- */
103
- public function clearStatCache() {
104
- clearstatcache();
105
- sleep( 1 );
106
- }
107
-
108
- public function getOverwriteMode() {
109
- return $this->fModeOverwrite;
110
- }
111
-
112
- public function setOverwriteMode( $fOn = true ) {
113
- $this->fModeOverwrite = $fOn;
114
- }
115
- }
116
- }
117
-
118
- if ( class_exists( 'Bulk_Plugin_Upgrader_Skin', false ) ) {
119
- /**
120
- * Class ICWP_Bulk_Plugin_Upgrader_Skin
121
- */
122
- class ICWP_Bulk_Plugin_Upgrader_Skin extends Bulk_Plugin_Upgrader_Skin {
123
-
124
- /**
125
- * @var array
126
- */
127
- public $m_aErrors;
128
-
129
- /**
130
- * @var array
131
- */
132
- public $aFeedback;
133
-
134
- /**
135
- *
136
- */
137
- public function __construct() {
138
- parent::__construct( compact( 'nonce', 'url' ) );
139
- $this->m_aErrors = [];
140
- $this->aFeedback = [];
141
- }
142
-
143
- /**
144
- * @param string|array $errors
145
- */
146
- function error( $errors ) {
147
- $this->m_aErrors[] = $errors;
148
-
149
- if ( is_string( $errors ) ) {
150
- $this->feedback( $errors );
151
- }
152
- else if ( is_wp_error( $errors ) && $errors->get_error_code() ) {
153
- foreach ( $errors->get_error_messages() as $message ) {
154
- if ( $errors->get_error_data() ) {
155
- $this->feedback( $message.' '.$errors->get_error_data() );
156
- }
157
- else {
158
- $this->feedback( $message );
159
- }
160
- }
161
- }
162
- }
163
-
164
- /**
165
- * @return array
166
- */
167
- public function getFeedback() {
168
- return $this->aFeedback;
169
- }
170
-
171
- /**
172
- * @param string $string
173
- */
174
- function feedback( $string ) {
175
- if ( isset( $this->upgrader->strings[ $string ] ) ) {
176
- $string = $this->upgrader->strings[ $string ];
177
- }
178
-
179
- if ( strpos( $string, '%' ) !== false ) {
180
- $args = func_get_args();
181
- $args = array_splice( $args, 1 );
182
- if ( !empty( $args ) ) {
183
- $string = vsprintf( $string, $args );
184
- }
185
- }
186
- if ( empty( $string ) ) {
187
- return;
188
- }
189
- $this->aFeedback[] = $string;
190
- }
191
-
192
- function before( $title = '' ) {
193
- }
194
-
195
- function after( $title = '' ) {
196
- }
197
-
198
- function flush_output() {
199
- }
200
- /*
201
- function footer() {
202
- var_dump(debug_backtrace());
203
- die( 'testing' );
204
- }
205
- */
206
- }
207
- }
208
-
209
- if ( class_exists( 'Theme_Upgrader' ) ) {
210
-
211
- require_once ABSPATH.'wp-admin/includes/theme.php'; // to get themes_api
212
- class ICWP_Theme_Upgrader extends Theme_Upgrader {
213
-
214
- protected $fModeOverwrite = true;
215
-
216
- public function install( $package, $args = [] ) {
217
-
218
- $defaults = [
219
- 'clear_update_cache' => true,
220
- ];
221
- $parsed_args = wp_parse_args( $args, $defaults );
222
-
223
- $this->init();
224
- $this->install_strings();
225
-
226
- add_filter( 'upgrader_source_selection', [ $this, 'check_package' ] );
227
- add_filter( 'upgrader_post_install', [ $this, 'check_parent_theme_filter' ], 10, 3 );
228
- add_filter( 'upgrader_clear_destination', [ $this, 'clearStatCache' ] );
229
-
230
- $this->run( [
231
- 'package' => $package,
232
- 'destination' => get_theme_root(),
233
- 'clear_destination' => $this->getOverwriteMode(),
234
- 'clear_working' => true,
235
- 'hook_extra' => [
236
- 'type' => 'theme',
237
- 'action' => 'install',
238
- ],
239
- ] );
240
-
241
- remove_filter( 'upgrader_source_selection', [ $this, 'check_package' ] );
242
- remove_filter( 'upgrader_post_install', [ $this, 'check_parent_theme_filter' ] );
243
- remove_filter( 'upgrader_clear_destination', [ $this, 'clearStatCache' ] );
244
-
245
- if ( !$this->result || is_wp_error( $this->result ) ) {
246
- return $this->result;
247
- }
248
-
249
- // Refresh the Theme Update information
250
- wp_clean_themes_cache( $parsed_args[ 'clear_update_cache' ] );
251
-
252
- return true;
253
- }
254
-
255
- /**
256
- * This is inserted right after clearing the target directory. It seems that some systems are slow
257
- * in updating filesystem "info" because we were receiving permission denied when trying to recreate
258
- * the install directory.
259
- */
260
- public function clearStatCache() {
261
- clearstatcache();
262
- sleep( 1 );
263
- }
264
-
265
- public function getOverwriteMode() {
266
- return $this->fModeOverwrite;
267
- }
268
-
269
- public function setOverwriteMode( $fOn = true ) {
270
- $this->fModeOverwrite = $fOn;
271
- }
272
- }
273
- }
274
-
275
- if ( class_exists( 'Bulk_Theme_Upgrader_Skin', false ) ) {
276
-
277
- /**
278
- * Class Worpit_Bulk_Theme_Upgrader_Skin
279
- */
280
- class ICWP_Bulk_Theme_Upgrader_Skin extends Bulk_Theme_Upgrader_Skin {
281
-
282
- /**
283
- * @var array
284
- */
285
- public $m_aErrors;
286
-
287
- /**
288
- * @var array
289
- */
290
- public $aFeedback;
291
-
292
- /**
293
- */
294
- public function __construct() {
295
- parent::__construct( compact( 'title', 'nonce', 'url', 'theme' ) );
296
- $this->m_aErrors = [];
297
- $this->aFeedback = [];
298
- }
299
-
300
- /**
301
- * @param string|array $errors
302
- */
303
- function error( $errors ) {
304
- $this->m_aErrors[] = $errors;
305
-
306
- if ( is_string( $errors ) ) {
307
- $this->feedback( $errors );
308
- }
309
- else if ( is_wp_error( $errors ) && $errors->get_error_code() ) {
310
- foreach ( $errors->get_error_messages() as $message ) {
311
- if ( $errors->get_error_data() ) {
312
- $this->feedback( $message.' '.$errors->get_error_data() );
313
- }
314
- else {
315
- $this->feedback( $message );
316
- }
317
- }
318
- }
319
- }
320
-
321
- /**
322
- * @return array
323
- */
324
- public function getFeedback() {
325
- return $this->aFeedback;
326
- }
327
-
328
- /**
329
- * @param string $string
330
- */
331
- function feedback( $string ) {
332
- if ( isset( $this->upgrader->strings[ $string ] ) ) {
333
- $string = $this->upgrader->strings[ $string ];
334
- }
335
-
336
- if ( strpos( $string, '%' ) !== false ) {
337
- $args = func_get_args();
338
- $args = array_splice( $args, 1 );
339
- if ( !empty( $args ) ) {
340
- $string = vsprintf( $string, $args );
341
- }
342
- }
343
- if ( empty( $string ) ) {
344
- return;
345
- }
346
- $this->aFeedback[] = $string;
347
- }
348
-
349
- function before( $title = '' ) {
350
- }
351
-
352
- function after( $title = '' ) {
353
- }
354
-
355
- function flush_output() {
356
- }
357
- }
358
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/common/wp-admin-notices.php CHANGED
@@ -2,6 +2,10 @@
2
 
3
  use FernleafSystems\Wordpress\Services\Services;
4
 
 
 
 
 
5
  class ICWP_WPSF_WpAdminNotices extends ICWP_WPSF_Foundation {
6
 
7
  /**
@@ -9,11 +13,6 @@ class ICWP_WPSF_WpAdminNotices extends ICWP_WPSF_Foundation {
9
  */
10
  protected static $oInstance = null;
11
 
12
- /**
13
- * @var array
14
- */
15
- protected $aAdminNotices;
16
-
17
  /**
18
  * @var string
19
  */
@@ -35,67 +34,9 @@ class ICWP_WPSF_WpAdminNotices extends ICWP_WPSF_Foundation {
35
  }
36
 
37
  protected function __construct() {
38
- $this->sFlashMessage = '';
39
- add_action( 'admin_notices', [ $this, 'onWpAdminNotices' ] );
40
- add_action( 'network_admin_notices', [ $this, 'onWpAdminNotices' ] );
41
- }
42
-
43
- /**
44
- * @param array $aAjaxResponse
45
- * @return array
46
- */
47
- public function handleAuthAjax( $aAjaxResponse ) {
48
- if ( empty( $aAjaxResponse ) && $this->loadRequest()->request( 'exec' ) === 'dismiss_admin_notice' ) {
49
- $aAjaxResponse = $this->ajaxExec_DismissAdminNotice();
50
- }
51
- return $aAjaxResponse;
52
- }
53
-
54
- /**
55
- * @return array
56
- */
57
- protected function ajaxExec_DismissAdminNotice() {
58
- // Get all notices and if this notice exists, we set it to "hidden"
59
- $sNoticeId = sanitize_key( $this->loadRequest()->query( 'notice_id', '' ) );
60
- $aNotices = apply_filters( $this->getPrefix().'register_admin_notices', [] );
61
- if ( !empty( $sNoticeId ) && array_key_exists( $sNoticeId, $aNotices ) ) {
62
- $this->setMeta( $aNotices[ $sNoticeId ][ 'id' ] );
63
- }
64
- return [ 'success' => true ];
65
  }
66
 
67
  public function onWpAdminNotices() {
68
- do_action( $this->getPrefix().'generate_admin_notices' );
69
- foreach ( $this->getNotices() as $sKey => $sAdminNoticeContent ) {
70
- echo $sAdminNoticeContent;
71
- }
72
- $this->flashUserAdminNotice();
73
- }
74
-
75
- /**
76
- * @param string $sNoticeId
77
- * @return true
78
- */
79
- public function isDismissed( $sNoticeId ) {
80
- $aMeta = $this->getMeta( $sNoticeId );
81
- return ( isset( $aMeta[ 'time' ] ) && $aMeta[ 'time' ] > 0 );
82
- }
83
-
84
- /**
85
- * @param string $sNoticeId
86
- * @return false|string
87
- */
88
- public function getMeta( $sNoticeId ) {
89
- $mValue = [];
90
-
91
- $oMeta = $this->getCurrentUserMeta();
92
-
93
- $sCleanNotice = 'notice_'.str_replace( [ '-', '_' ], '', $sNoticeId );
94
- if ( isset( $oMeta->{$sCleanNotice} ) && is_array( $oMeta->{$sCleanNotice} ) ) {
95
- $mValue = $oMeta->{$sCleanNotice};
96
- }
97
-
98
- return $mValue;
99
  }
100
 
101
  /**
@@ -106,21 +47,6 @@ class ICWP_WPSF_WpAdminNotices extends ICWP_WPSF_Foundation {
106
  return Services::WpUsers()->metaVoForUser( rtrim( $this->getPrefix(), '-' ) );
107
  }
108
 
109
- /**
110
- * @param string $sNoticeId
111
- * @param array $aMeta
112
- */
113
- public function setMeta( $sNoticeId, $aMeta = [] ) {
114
- if ( !is_array( $aMeta ) ) {
115
- $aMeta = [];
116
- }
117
-
118
- $oMeta = $this->getCurrentUserMeta();
119
- $sCleanNotice = 'notice_'.str_replace( [ '-', '_' ], '', $sNoticeId );
120
- $oMeta->{$sCleanNotice} = array_merge( [ 'time' => $this->loadRequest()->ts() ], $aMeta );
121
- return;
122
- }
123
-
124
  /**
125
  * @return string
126
  */
@@ -128,105 +54,6 @@ class ICWP_WPSF_WpAdminNotices extends ICWP_WPSF_Foundation {
128
  return $this->sPrefix;
129
  }
130
 
131
- /**
132
- * @param string $sPrefix
133
- * @return $this
134
- */
135
- public function setPrefix( $sPrefix ) {
136
- $this->sPrefix = rtrim( $sPrefix, '-' ).'-';
137
- return $this;
138
- }
139
-
140
- /**
141
- * @return array
142
- */
143
- protected function getNotices() {
144
- if ( !isset( $this->aAdminNotices ) || !is_array( $this->aAdminNotices ) ) {
145
- $this->aAdminNotices = [];
146
- }
147
- return $this->aAdminNotices;
148
- }
149
-
150
- /**
151
- * @param string $sNoticeId
152
- * @return bool
153
- */
154
- protected function getNoticeAlreadyExists( $sNoticeId ) {
155
- return !empty( $this->aAdminNotices[ $sNoticeId ] );
156
- }
157
-
158
- /**
159
- * @param string $sNoticeId
160
- * @param string $sNotice
161
- * @return $this
162
- */
163
- public function addAdminNotice( $sNotice, $sNoticeId = '' ) {
164
- if ( !empty( $sNotice ) ) {
165
- if ( empty( $sNoticeId ) ) {
166
- $sNoticeId = md5( uniqid( '', true ) );
167
- }
168
- if ( !$this->getNoticeAlreadyExists( $sNoticeId ) ) {
169
- $aCurrentNotices = $this->getNotices();
170
- $aCurrentNotices[ $sNoticeId ] = $sNotice;
171
- $this->aAdminNotices = $aCurrentNotices;
172
- }
173
- }
174
- return $this;
175
- }
176
-
177
- /**
178
- * Use this to add a simple message to the admin notice collection. It'll wrap it up in basic html
179
- * @param string $sRawMessage
180
- * @param string $sType
181
- * @return $this
182
- */
183
- public function addRawAdminNotice( $sRawMessage, $sType = 'updated' ) {
184
- return $this->addAdminNotice( $this->wrapAdminNoticeHtml( $sRawMessage, $sType ) );
185
- }
186
-
187
- /**
188
- * Provides the basic HTML template for printing a WordPress Admin Notices
189
- * @param $sNotice - The message to be displayed.
190
- * @param $sMessageClass - either error or updated
191
- * @param $bPrint - if true, will echo. false will return the string
192
- * @return boolean|string
193
- */
194
- protected function wrapAdminNoticeHtml( $sNotice = '', $sMessageClass = 'updated', $bPrint = false ) {
195
- $sWrapper = '<div class="%s odp-admin-notice">%s</div>';
196
- $sFullNotice = sprintf( $sWrapper, $sMessageClass, $sNotice );
197
- if ( $bPrint ) {
198
- echo $sFullNotice;
199
- return true;
200
- }
201
- else {
202
- return $sFullNotice;
203
- }
204
- }
205
-
206
- /**
207
- * @param string $sMessage
208
- * @param bool $bError
209
- * @return $this
210
- */
211
- public function addFlashUserMessage( $sMessage, $bError = false ) {
212
- if ( Services::WpUsers()->isUserLoggedIn() ) {
213
- $this->getCurrentUserMeta()->flash_msg = ( $bError ? 'error' : 'updated' )
214
- .'::'.sanitize_text_field( $sMessage )
215
- .'::'.( Services::Request()->ts() + 300 );
216
- }
217
- return $this;
218
- }
219
-
220
- protected function flashUserAdminNotice() {
221
- $this->flushFlash();
222
- if ( $this->hasFlash() ) {
223
- $aParts = $this->getFlashParts();
224
- if ( empty( $aParts[ 2 ] ) || Services::Request()->ts() < $aParts[ 2 ] ) {
225
- echo $this->wrapAdminNoticeHtml( '<p>'.$aParts[ 1 ].'</p>', $aParts[ 0 ] );
226
- }
227
- }
228
- }
229
-
230
  /**
231
  * @return string
232
  */
@@ -249,15 +76,6 @@ class ICWP_WPSF_WpAdminNotices extends ICWP_WPSF_Foundation {
249
  return isset( $aParts[ 1 ] ) ? $aParts[ 1 ] : '';
250
  }
251
 
252
- /**
253
- * Requires that the flash has been flushed
254
- * @return bool
255
- */
256
- public function hasFlash() {
257
- $sFlash = $this->getFlash();
258
- return !empty( $sFlash );
259
- }
260
-
261
  /**
262
  * @return $this
263
  */
2
 
3
  use FernleafSystems\Wordpress\Services\Services;
4
 
5
+ /**
6
+ * Class ICWP_WPSF_WpAdminNotices
7
+ * @deprecated 8.4
8
+ */
9
  class ICWP_WPSF_WpAdminNotices extends ICWP_WPSF_Foundation {
10
 
11
  /**
13
  */
14
  protected static $oInstance = null;
15
 
 
 
 
 
 
16
  /**
17
  * @var string
18
  */
34
  }
35
 
36
  protected function __construct() {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
37
  }
38
 
39
  public function onWpAdminNotices() {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
40
  }
41
 
42
  /**
47
  return Services::WpUsers()->metaVoForUser( rtrim( $this->getPrefix(), '-' ) );
48
  }
49
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
50
  /**
51
  * @return string
52
  */
54
  return $this->sPrefix;
55
  }
56
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
57
  /**
58
  * @return string
59
  */
76
  return isset( $aParts[ 1 ] ) ? $aParts[ 1 ] : '';
77
  }
78
 
 
 
 
 
 
 
 
 
 
79
  /**
80
  * @return $this
81
  */
src/common/wp-comments.php DELETED
@@ -1,105 +0,0 @@
1
- <?php
2
-
3
- use FernleafSystems\Wordpress\Services\Services;
4
-
5
- class ICWP_WPSF_WpComments extends ICWP_WPSF_Foundation {
6
-
7
- /**
8
- * @var ICWP_WPSF_WpComments
9
- */
10
- protected static $oInstance = null;
11
-
12
- private function __construct() {
13
- }
14
-
15
- /**
16
- * @return ICWP_WPSF_WpComments
17
- */
18
- public static function GetInstance() {
19
- if ( is_null( self::$oInstance ) ) {
20
- self::$oInstance = new self();
21
- }
22
- return self::$oInstance;
23
- }
24
-
25
- /**
26
- * @param WP_Post|null $oPost - queries the current post if null
27
- * @return bool
28
- */
29
- public function isCommentsOpen( $oPost = null ) {
30
- if ( is_null( $oPost ) || !is_a( $oPost, 'WP_Post' ) ) {
31
- global $post;
32
- $oPost = $post;
33
- }
34
- return ( is_a( $oPost, 'WP_Post' ) ? ( $oPost->comment_status == 'open' ) : $this->isCommentsOpenByDefault() );
35
- }
36
-
37
- /**
38
- * @return bool
39
- */
40
- public function isCommentsOpenByDefault() {
41
- return ( $this->loadWp()->getOption( 'default_comment_status' ) == 'open' );
42
- }
43
-
44
- /**
45
- * @param string $sAuthorEmail
46
- * @return bool
47
- */
48
- public function isAuthorApproved( $sAuthorEmail ) {
49
-
50
- if ( !$this->loadDP()->validEmail( $sAuthorEmail ) ) {
51
- return false;
52
- }
53
-
54
- $oDb = $this->loadDbProcessor();
55
- $sQuery = "
56
- SELECT comment_approved
57
- FROM %s
58
- WHERE
59
- comment_author_email = '%s'
60
- AND comment_approved = '1'
61
- LIMIT 1
62
- ";
63
-
64
- $sQuery = sprintf(
65
- $sQuery,
66
- $oDb->getTable_Comments(),
67
- esc_sql( $sAuthorEmail )
68
- );
69
- return $oDb->getVar( $sQuery ) == 1;
70
- }
71
-
72
- /**
73
- * @param string $sAuthorEmail
74
- * @return bool
75
- */
76
- public function countApproved( $sAuthorEmail ) {
77
- $nCount = 0;
78
-
79
- if ( $this->loadDP()->validEmail( $sAuthorEmail ) ) {
80
- $oDb = $this->loadDbProcessor();
81
- $sQuery = "
82
- SELECT COUNT(*)
83
- FROM %s
84
- WHERE
85
- comment_author_email = '%s'
86
- AND comment_approved = 1
87
- ";
88
-
89
- $sQuery = sprintf(
90
- $sQuery,
91
- $oDb->getTable_Comments(),
92
- esc_sql( $sAuthorEmail )
93
- );
94
- $nCount = (int)$oDb->getVar( $sQuery );
95
- }
96
- return $nCount;
97
- }
98
-
99
- /**
100
- * @return bool
101
- */
102
- public function isCommentPost() {
103
- return $this->loadRequest()->isMethodPost() && $this->loadWp()->isCurrentPage( 'wp-comments-post.php' );
104
- }
105
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/common/wp-users.php DELETED
@@ -1,290 +0,0 @@
1
- <?php
2
-
3
- class ICWP_WPSF_WpUsers extends ICWP_WPSF_Foundation {
4
-
5
- /**
6
- * @var ICWP_WPSF_WpUsers
7
- */
8
- protected static $oInstance = null;
9
-
10
- private function __construct() {
11
- }
12
-
13
- /**
14
- * @return ICWP_WPSF_WpUsers
15
- */
16
- public static function GetInstance() {
17
- if ( is_null( self::$oInstance ) ) {
18
- self::$oInstance = new self();
19
- }
20
- return self::$oInstance;
21
- }
22
-
23
- /**
24
- * @param string $sKey
25
- * @param integer $nUserId -user ID
26
- * @return boolean
27
- */
28
- public function deleteUserMeta( $sKey, $nUserId = null ) {
29
- if ( empty( $nUserId ) ) {
30
- $nUserId = $this->getCurrentWpUserId();
31
- }
32
- else if ( $nUserId instanceof WP_User ) {
33
- $nUserId = $nUserId->ID;
34
- }
35
-
36
- $bSuccess = false;
37
- if ( $nUserId > 0 ) {
38
- $bSuccess = delete_user_meta( $nUserId, $sKey );
39
- }
40
- return $bSuccess;
41
- }
42
-
43
- /**
44
- * @param array $aLoginUrlParams
45
- */
46
- public function forceUserRelogin( $aLoginUrlParams = [] ) {
47
- $this->logoutUser();
48
- $this->loadWp()->redirectToLogin( $aLoginUrlParams );
49
- }
50
-
51
- /**
52
- * @param array $aArgs
53
- * @return WP_User[]
54
- */
55
- public function getAllUsers( $aArgs = [] ) {
56
- $aArgs = wp_parse_args(
57
- $aArgs,
58
- [
59
- 'blog_id' => 0,
60
- // 'fields' => array(
61
- // 'ID',
62
- // 'user_login',
63
- // 'user_email',
64
- // 'user_pass',
65
- // )
66
- ]
67
- );
68
- return function_exists( 'get_users' ) ? get_users( $aArgs ) : [];
69
- }
70
-
71
- /**
72
- * @return integer
73
- */
74
- public function getCurrentUserLevel() {
75
- $oUser = $this->getCurrentWpUser();
76
- return ( is_object( $oUser ) && ( $oUser instanceof WP_User ) ) ? $oUser->get( 'user_level' ) : -1;
77
- }
78
-
79
- /**
80
- * @return array
81
- */
82
- public function getLevelToRoleMap() {
83
- return [
84
- 0 => 'subscriber',
85
- 1 => 'contributor',
86
- 2 => 'author',
87
- 3 => 'editor',
88
- 8 => 'administrator'
89
- ];
90
- }
91
-
92
- /**
93
- * @param bool $bSlugsOnly
94
- * @return string[]|array[]
95
- */
96
- public function getAvailableUserRoles( $bSlugsOnly = true ) {
97
- require_once( ABSPATH.'wp-admin/includes/user.php' );
98
- return $bSlugsOnly ? array_keys( get_editable_roles() ) : get_editable_roles();
99
- }
100
-
101
- /**
102
- * @return null|WP_User
103
- */
104
- public function getCurrentWpUser() {
105
- return $this->isUserLoggedIn() ? wp_get_current_user() : null;
106
- }
107
-
108
- /**
109
- * @return int - 0 if not logged in or can't get the current User
110
- */
111
- public function getCurrentWpUserId() {
112
- $oUser = $this->getCurrentWpUser();
113
- return is_null( $oUser ) ? 0 : $oUser->ID;
114
- }
115
-
116
- /**
117
- * @return null|string
118
- */
119
- public function getCurrentWpUsername() {
120
- $oUser = $this->getCurrentWpUser();
121
- return is_null( $oUser ) ? null : $oUser->user_login;
122
- }
123
-
124
- /**
125
- * @param WP_User $oUser
126
- * @return string
127
- */
128
- public function getAdminUrl_ProfileEdit( $oUser = null ) {
129
- if ( $oUser instanceof WP_User ) {
130
- $sPath = 'user-edit.php?user_id='.$oUser->ID;
131
- }
132
- else {
133
- $sPath = 'profile.php';
134
- }
135
- return $this->loadWp()->getAdminUrl( $sPath );
136
- }
137
-
138
- /**
139
- * @param string $sEmail
140
- * @return WP_User|null
141
- */
142
- public function getUserByEmail( $sEmail ) {
143
- return $this->getUserBy( 'email', $sEmail );
144
- }
145
-
146
- /**
147
- * @param int $nId
148
- * @return WP_User|null
149
- */
150
- public function getUserById( $nId ) {
151
- return $this->getUserBy( 'id', $nId );
152
- }
153
-
154
- /**
155
- * @param $sUsername
156
- * @return null|WP_User
157
- */
158
- public function getUserByUsername( $sUsername ) {
159
- return $this->getUserBy( 'login', $sUsername );
160
- }
161
-
162
- /**
163
- * @param string $sKey
164
- * @param mixed $mValue
165
- * @return null|WP_User
166
- */
167
- public function getUserBy( $sKey, $mValue ) {
168
- $oU = function_exists( 'get_user_by' ) ? get_user_by( $sKey, $mValue ) : null;
169
- return empty( $oU ) ? null : $oU;
170
- }
171
-
172
- /**
173
- * @param string $sKey should be already prefixed
174
- * @param int|null $nUserId - if omitted get for current user
175
- * @return false|string
176
- */
177
- public function getUserMeta( $sKey, $nUserId = null ) {
178
- if ( empty( $nUserId ) ) {
179
- $nUserId = $this->getCurrentWpUserId();
180
- }
181
- else if ( $nUserId instanceof WP_User ) {
182
- $nUserId = $nUserId->ID;
183
- }
184
-
185
- $mResult = false;
186
- if ( $nUserId > 0 ) {
187
- $mResult = get_user_meta( $nUserId, $sKey, true );
188
- }
189
- return $mResult;
190
- }
191
-
192
- /**
193
- * @param WP_User $oUser
194
- * @return string|null
195
- * @see wp-login.php
196
- */
197
- public function getPasswordResetUrl( $oUser ) {
198
- $sUrl = null;
199
-
200
- $sResetKey = get_password_reset_key( $oUser );
201
- if ( !is_wp_error( $sResetKey ) ) {
202
- $sUrl = add_query_arg(
203
- [
204
- 'action' => 'rp',
205
- 'key' => $sResetKey,
206
- 'login' => $oUser->user_login,
207
- ],
208
- wp_login_url()
209
- );
210
- }
211
-
212
- return $sUrl;
213
- }
214
-
215
- /**
216
- * @param WP_User|null $oUser
217
- * @return bool
218
- */
219
- public function isUserAdmin( $oUser = null ) {
220
- if ( empty( $oUser ) ) {
221
- $bIsAdmin = $this->isUserLoggedIn() && current_user_can( 'manage_options' );
222
- }
223
- else {
224
- $bIsAdmin = user_can( $oUser, 'manage_options' );
225
- }
226
- return $bIsAdmin;
227
- }
228
-
229
- /**
230
- * @return bool
231
- */
232
- public function isUserLoggedIn() {
233
- return function_exists( 'is_user_logged_in' ) && is_user_logged_in();
234
- }
235
-
236
- /**
237
- * Fires the WordPress logout functions. If $bQuiet is true, it'll manually
238
- * call the WordPress logout code, so as not to fire any other logout actions
239
- * We might want to be "quiet" so as not to fire out own action hooks.
240
- * @param bool $bQuiet
241
- */
242
- public function logoutUser( $bQuiet = false ) {
243
- if ( $bQuiet ) {
244
- wp_destroy_current_session();
245
- wp_clear_auth_cookie();
246
- }
247
- else {
248
- wp_logout();
249
- }
250
- }
251
-
252
- /**
253
- * Updates the user meta data for the current (or supplied user ID)
254
- * @param string $sKey
255
- * @param mixed $mValue
256
- * @param WP_User|int $nUserId -user ID
257
- * @return boolean
258
- */
259
- public function updateUserMeta( $sKey, $mValue, $nUserId = null ) {
260
- if ( empty( $nUserId ) ) {
261
- $nUserId = $this->getCurrentWpUserId();
262
- }
263
- else if ( $nUserId instanceof WP_User ) {
264
- $nUserId = $nUserId->ID;
265
- }
266
-
267
- $bSuccess = false;
268
- if ( $nUserId > 0 ) {
269
- $bSuccess = update_user_meta( $nUserId, $sKey, $mValue );
270
- }
271
- return $bSuccess;
272
- }
273
-
274
- /**
275
- * @param string $sUsername
276
- * @return bool
277
- */
278
- public function setUserLoggedIn( $sUsername ) {
279
-
280
- $oUser = $this->getUserByUsername( $sUsername );
281
- $bSuccess = $oUser instanceof WP_User;
282
- if ( $bSuccess ) {
283
- wp_clear_auth_cookie();
284
- wp_set_current_user( $oUser->ID, $oUser->user_login );
285
- wp_set_auth_cookie( $oUser->ID, true );
286
- do_action( 'wp_login', $oUser->user_login, $oUser );
287
- }
288
- return $bSuccess;
289
- }
290
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/common/wp-widget.php CHANGED
@@ -2,6 +2,7 @@
2
 
3
  /**
4
  * Class ICWP_WPSF_WpWidget
 
5
  */
6
  abstract class ICWP_WPSF_WpWidget extends \WP_Widget {
7
 
2
 
3
  /**
4
  * Class ICWP_WPSF_WpWidget
5
+ * @deprecated 8.4
6
  */
7
  abstract class ICWP_WPSF_WpWidget extends \WP_Widget {
8
 
src/config/changelog.json CHANGED
@@ -356,7 +356,7 @@
356
  "timestamp": "1491523200",
357
  "name": "",
358
  "summary": "Introduction of Login Authentication Portal",
359
- "link": "https://icwp.io/86",
360
  "is_patch": false,
361
  "options_added": true,
362
  "options_removed": true
@@ -700,13 +700,13 @@
700
  {
701
  "type": "changed",
702
  "summary": "Major overhaul of Two-Factor / Multi-Factor Login Authentication",
703
- "link": "https://icwp.io/87",
704
  "version": "5.8.0"
705
  },
706
  {
707
  "type": "changed",
708
  "summary": "Introduction of Login Authentication Portal for improved Multi-Factor Authentication",
709
- "link": "https://icwp.io/86",
710
  "version": "5.8.0"
711
  },
712
  {
@@ -747,7 +747,7 @@
747
  {
748
  "type": "improvements",
749
  "summary": "Further preparation for Shield Central release",
750
- "link": "https://icwp.io/83",
751
  "version": "5.8.0"
752
  }
753
  ]
356
  "timestamp": "1491523200",
357
  "name": "",
358
  "summary": "Introduction of Login Authentication Portal",
359
+ "link": "https://shsec.io/86",
360
  "is_patch": false,
361
  "options_added": true,
362
  "options_removed": true
700
  {
701
  "type": "changed",
702
  "summary": "Major overhaul of Two-Factor / Multi-Factor Login Authentication",
703
+ "link": "https://shsec.io/87",
704
  "version": "5.8.0"
705
  },
706
  {
707
  "type": "changed",
708
  "summary": "Introduction of Login Authentication Portal for improved Multi-Factor Authentication",
709
+ "link": "https://shsec.io/86",
710
  "version": "5.8.0"
711
  },
712
  {
747
  {
748
  "type": "improvements",
749
  "summary": "Further preparation for Shield Central release",
750
+ "link": "https://shsec.io/83",
751
  "version": "5.8.0"
752
  }
753
  ]
src/config/feature-admin_access_restriction.php CHANGED
@@ -82,8 +82,8 @@
82
  "section": "section_enable_plugin_feature_admin_access_restriction",
83
  "default": "Y",
84
  "type": "checkbox",
85
- "link_info": "https://icwp.io/40",
86
- "link_blog": "https://icwp.io/wpsf02",
87
  "name": "Enable Security Admin",
88
  "summary": "Enforce Security Admin Access Restriction",
89
  "description": "Enable this with great care and consideration. When this Access Key option is enabled, you must specify a key below and use it to gain access to this plugin."
@@ -94,7 +94,7 @@
94
  "sensitive": true,
95
  "default": "",
96
  "type": "password",
97
- "link_info": "https://icwp.io/42",
98
  "link_blog": "",
99
  "name": "Security Admin Access Key",
100
  "summary": "Provide/Update Security Admin Access Key",
@@ -107,7 +107,7 @@
107
  "premium": true,
108
  "default": "",
109
  "type": "array",
110
- "link_info": "https://icwp.io/dk",
111
  "link_blog": "",
112
  "name": "Security Admins",
113
  "summary": "Persistent Security Admins",
@@ -119,7 +119,7 @@
119
  "default": 30,
120
  "type": "integer",
121
  "min": 1,
122
- "link_info": "https://icwp.io/41",
123
  "link_blog": "",
124
  "name": "Security Admin Timeout",
125
  "summary": "Specify An Automatic Timeout Interval For Security Admin Access",
@@ -130,8 +130,8 @@
130
  "section": "section_admin_access_restriction_areas",
131
  "default": "Y",
132
  "type": "checkbox",
133
- "link_info": "https://icwp.io/a0",
134
- "link_blog": "https://icwp.io/wpsf32",
135
  "name": "Pages",
136
  "summary": "Restrict Access To Key WordPress Posts And Pages Actions",
137
  "description": "Careful: This will restrict access to page/post creation, editing and deletion. Note: Selecting 'Edit' will also restrict all other options."
@@ -141,7 +141,7 @@
141
  "section": "section_admin_access_restriction_areas",
142
  "default": "N",
143
  "type": "checkbox",
144
- "link_info": "https://icwp.io/a0",
145
  "link_blog": "",
146
  "name": "Admin Users",
147
  "summary": "Restrict Access To Create/Delete/Modify Other Admin Users",
@@ -170,8 +170,8 @@
170
  "text": "Delete"
171
  }
172
  ],
173
- "link_info": "https://icwp.io/a0",
174
- "link_blog": "https://icwp.io/wpsf21",
175
  "summary": "Restrict Access To Key WordPress Plugin Actions",
176
  "description": "Careful: This will restrict access to plugin installation, update, activation and deletion. Note: Selecting 'Activate' will also restrict all other options."
177
  },
@@ -202,8 +202,8 @@
202
  "text": "Delete"
203
  }
204
  ],
205
- "link_info": "https://icwp.io/a0",
206
- "link_blog": "https://icwp.io/wpsf21",
207
  "summary": "Restrict Access To WordPress Theme Actions",
208
  "description": "Careful: This will restrict access to theme installation, update, activation and deletion."
209
  },
@@ -226,8 +226,8 @@
226
  "text": "Delete"
227
  }
228
  ],
229
- "link_info": "https://icwp.io/a0",
230
- "link_blog": "https://icwp.io/wpsf21",
231
  "summary": "Restrict Access To Key WordPress Posts And Pages Actions",
232
  "description": "Careful: This will restrict access to page/post creation, editing and deletion."
233
  },
@@ -237,8 +237,8 @@
237
  "premium": true,
238
  "default": "N",
239
  "type": "checkbox",
240
- "link_info": "https://icwp.io/dr",
241
- "link_blog": "https://icwp.io/ds",
242
  "name": "Enable White Label",
243
  "summary": "Activate Your White Label Settings",
244
  "description": "Use this option to turn on/off the whole White Label feature."
@@ -260,7 +260,7 @@
260
  "sensitive": true,
261
  "default": "Shield",
262
  "type": "text",
263
- "link_info": "https://icwp.io/dt",
264
  "link_blog": "",
265
  "name": "Plugin Name",
266
  "summary": "The Name Of The Plugin",
@@ -282,9 +282,9 @@
282
  "key": "wl_companyname",
283
  "section": "section_whitelabel",
284
  "sensitive": true,
285
- "default": "One Dollar Plugin",
286
  "type": "text",
287
- "link_info": "https://icwp.io/dt",
288
  "link_blog": "",
289
  "name": "Company Name",
290
  "summary": "The Name Of Your Company",
@@ -306,7 +306,7 @@
306
  "key": "wl_homeurl",
307
  "section": "section_whitelabel",
308
  "sensitive": true,
309
- "default": "https://icwp.io/7f",
310
  "type": "text",
311
  "link_info": "",
312
  "link_blog": "",
@@ -320,7 +320,7 @@
320
  "sensitive": true,
321
  "default": "pluginlogo_16x16.png",
322
  "type": "text",
323
- "link_info": "https://icwp.io/dt",
324
  "link_blog": "",
325
  "name": "Menu Icon",
326
  "summary": "Menu Icon URL",
@@ -344,7 +344,7 @@
344
  "sensitive": true,
345
  "default": "pluginlogo_banner-772x250.png",
346
  "type": "text",
347
- "link_info": "https://icwp.io/dt",
348
  "link_blog": "",
349
  "name": "Dashboard Logo",
350
  "summary": "Dashboard Logo URL",
@@ -381,6 +381,7 @@
381
  "siteurl",
382
  "home",
383
  "admin_email",
 
384
  "users_can_register",
385
  "comments_notify",
386
  "comment_moderation",
82
  "section": "section_enable_plugin_feature_admin_access_restriction",
83
  "default": "Y",
84
  "type": "checkbox",
85
+ "link_info": "https://shsec.io/40",
86
+ "link_blog": "https://shsec.io/wpsf02",
87
  "name": "Enable Security Admin",
88
  "summary": "Enforce Security Admin Access Restriction",
89
  "description": "Enable this with great care and consideration. When this Access Key option is enabled, you must specify a key below and use it to gain access to this plugin."
94
  "sensitive": true,
95
  "default": "",
96
  "type": "password",
97
+ "link_info": "https://shsec.io/42",
98
  "link_blog": "",
99
  "name": "Security Admin Access Key",
100
  "summary": "Provide/Update Security Admin Access Key",
107
  "premium": true,
108
  "default": "",
109
  "type": "array",
110
+ "link_info": "https://shsec.io/dk",
111
  "link_blog": "",
112
  "name": "Security Admins",
113
  "summary": "Persistent Security Admins",
119
  "default": 30,
120
  "type": "integer",
121
  "min": 1,
122
+ "link_info": "https://shsec.io/41",
123
  "link_blog": "",
124
  "name": "Security Admin Timeout",
125
  "summary": "Specify An Automatic Timeout Interval For Security Admin Access",
130
  "section": "section_admin_access_restriction_areas",
131
  "default": "Y",
132
  "type": "checkbox",
133
+ "link_info": "https://shsec.io/a0",
134
+ "link_blog": "https://shsec.io/wpsf32",
135
  "name": "Pages",
136
  "summary": "Restrict Access To Key WordPress Posts And Pages Actions",
137
  "description": "Careful: This will restrict access to page/post creation, editing and deletion. Note: Selecting 'Edit' will also restrict all other options."
141
  "section": "section_admin_access_restriction_areas",
142
  "default": "N",
143
  "type": "checkbox",
144
+ "link_info": "https://shsec.io/a0",
145
  "link_blog": "",
146
  "name": "Admin Users",
147
  "summary": "Restrict Access To Create/Delete/Modify Other Admin Users",
170
  "text": "Delete"
171
  }
172
  ],
173
+ "link_info": "https://shsec.io/a0",
174
+ "link_blog": "https://shsec.io/wpsf21",
175
  "summary": "Restrict Access To Key WordPress Plugin Actions",
176
  "description": "Careful: This will restrict access to plugin installation, update, activation and deletion. Note: Selecting 'Activate' will also restrict all other options."
177
  },
202
  "text": "Delete"
203
  }
204
  ],
205
+ "link_info": "https://shsec.io/a0",
206
+ "link_blog": "https://shsec.io/wpsf21",
207
  "summary": "Restrict Access To WordPress Theme Actions",
208
  "description": "Careful: This will restrict access to theme installation, update, activation and deletion."
209
  },
226
  "text": "Delete"
227
  }
228
  ],
229
+ "link_info": "https://shsec.io/a0",
230
+ "link_blog": "https://shsec.io/wpsf21",
231
  "summary": "Restrict Access To Key WordPress Posts And Pages Actions",
232
  "description": "Careful: This will restrict access to page/post creation, editing and deletion."
233
  },
237
  "premium": true,
238
  "default": "N",
239
  "type": "checkbox",
240
+ "link_info": "https://shsec.io/dr",
241
+ "link_blog": "https://shsec.io/ds",
242
  "name": "Enable White Label",
243
  "summary": "Activate Your White Label Settings",
244
  "description": "Use this option to turn on/off the whole White Label feature."
260
  "sensitive": true,
261
  "default": "Shield",
262
  "type": "text",
263
+ "link_info": "https://shsec.io/dt",
264
  "link_blog": "",
265
  "name": "Plugin Name",
266
  "summary": "The Name Of The Plugin",
282
  "key": "wl_companyname",
283
  "section": "section_whitelabel",
284
  "sensitive": true,
285
+ "default": "Example Company Name",
286
  "type": "text",
287
+ "link_info": "https://shsec.io/dt",
288
  "link_blog": "",
289
  "name": "Company Name",
290
  "summary": "The Name Of Your Company",
306
  "key": "wl_homeurl",
307
  "section": "section_whitelabel",
308
  "sensitive": true,
309
+ "default": "https://shsec.io/7f",
310
  "type": "text",
311
  "link_info": "",
312
  "link_blog": "",
320
  "sensitive": true,
321
  "default": "pluginlogo_16x16.png",
322
  "type": "text",
323
+ "link_info": "https://shsec.io/dt",
324
  "link_blog": "",
325
  "name": "Menu Icon",
326
  "summary": "Menu Icon URL",
344
  "sensitive": true,
345
  "default": "pluginlogo_banner-772x250.png",
346
  "type": "text",
347
+ "link_info": "https://shsec.io/dt",
348
  "link_blog": "",
349
  "name": "Dashboard Logo",
350
  "summary": "Dashboard Logo URL",
381
  "siteurl",
382
  "home",
383
  "admin_email",
384
+ "new_admin_email",
385
  "users_can_register",
386
  "comments_notify",
387
  "comment_moderation",
src/config/feature-audit_trail.php CHANGED
@@ -65,8 +65,8 @@
65
  "section": "section_enable_plugin_feature_audit_trail",
66
  "default": "Y",
67
  "type": "checkbox",
68
- "link_info": "https://icwp.io/5p",
69
- "link_blog": "https://icwp.io/a1",
70
  "name": "Enable Audit Trail",
71
  "summary": "Enable (or Disable) The Audit Trail module",
72
  "description": "Un-Checking this option will completely disable the Audit Trail module"
@@ -77,8 +77,8 @@
77
  "default": 14,
78
  "min": 1,
79
  "type": "integer",
80
- "link_info": "https://icwp.io/a2",
81
- "link_blog": "https://icwp.io/a1",
82
  "name": "Auto Clean",
83
  "summary": "Enable Audit Auto Cleaning",
84
  "description": "Events older than the number of days specified will be automatically cleaned from the database"
@@ -101,8 +101,8 @@
101
  "section": "section_enable_audit_contexts",
102
  "default": "Y",
103
  "type": "checkbox",
104
- "link_info": "https://icwp.io/a3",
105
- "link_blog": "https://icwp.io/a1",
106
  "name": "Users And Logins",
107
  "summary": "Enable Audit Context - Users And Logins",
108
  "description": "When this context is enabled, the audit trail will track activity relating to: Users And Logins"
@@ -112,8 +112,8 @@
112
  "section": "section_enable_audit_contexts",
113
  "default": "Y",
114
  "type": "checkbox",
115
- "link_info": "https://icwp.io/a3",
116
- "link_blog": "https://icwp.io/a1",
117
  "name": "Plugins",
118
  "summary": "Enable Audit Context - Plugins",
119
  "description": "When this context is enabled, the audit trail will track activity relating to: WordPress Plugins"
@@ -123,8 +123,8 @@
123
  "section": "section_enable_audit_contexts",
124
  "default": "Y",
125
  "type": "checkbox",
126
- "link_info": "https://icwp.io/a3",
127
- "link_blog": "https://icwp.io/a1",
128
  "name": "Themes",
129
  "summary": "Enable Audit Context - Themes",
130
  "description": "When this context is enabled, the audit trail will track activity relating to: WordPress Themes"
@@ -134,8 +134,8 @@
134
  "section": "section_enable_audit_contexts",
135
  "default": "Y",
136
  "type": "checkbox",
137
- "link_info": "https://icwp.io/a3",
138
- "link_blog": "https://icwp.io/a1",
139
  "name": "Posts And Pages",
140
  "summary": "Enable Audit Context - Posts And Pages",
141
  "description": "When this context is enabled, the audit trail will track activity relating to: Editing and publishing of posts and pages"
@@ -145,8 +145,8 @@
145
  "section": "section_enable_audit_contexts",
146
  "default": "Y",
147
  "type": "checkbox",
148
- "link_info": "https://icwp.io/a3",
149
- "link_blog": "https://icwp.io/a1",
150
  "name": "WordPress And Settings",
151
  "summary": "Enable Audit Context - WordPress And Settings",
152
  "description": "When this context is enabled, the audit trail will track activity relating to: WordPress upgrades and changes to particular WordPress settings"
@@ -156,8 +156,8 @@
156
  "section": "section_enable_audit_contexts",
157
  "default": "Y",
158
  "type": "checkbox",
159
- "link_info": "https://icwp.io/a3",
160
- "link_blog": "https://icwp.io/a1",
161
  "name": "Emails",
162
  "summary": "Enable Audit Context - Emails",
163
  "description": "When this context is enabled, the audit trail will track activity relating to: Email Sending"
@@ -167,8 +167,8 @@
167
  "section": "section_enable_audit_contexts",
168
  "default": "Y",
169
  "type": "checkbox",
170
- "link_info": "https://icwp.io/a4",
171
- "link_blog": "https://icwp.io/a1",
172
  "name": "Shield",
173
  "summary": "Enable Audit Context - Shield",
174
  "description": "When this context is enabled, the audit trail will track activity relating to: Shield"
65
  "section": "section_enable_plugin_feature_audit_trail",
66
  "default": "Y",
67
  "type": "checkbox",
68
+ "link_info": "https://shsec.io/5p",
69
+ "link_blog": "https://shsec.io/a1",
70
  "name": "Enable Audit Trail",
71
  "summary": "Enable (or Disable) The Audit Trail module",
72
  "description": "Un-Checking this option will completely disable the Audit Trail module"
77
  "default": 14,
78
  "min": 1,
79
  "type": "integer",
80
+ "link_info": "https://shsec.io/a2",
81
+ "link_blog": "https://shsec.io/a1",
82
  "name": "Auto Clean",
83
  "summary": "Enable Audit Auto Cleaning",
84
  "description": "Events older than the number of days specified will be automatically cleaned from the database"
101
  "section": "section_enable_audit_contexts",
102
  "default": "Y",
103
  "type": "checkbox",
104
+ "link_info": "https://shsec.io/a3",
105
+ "link_blog": "https://shsec.io/a1",
106
  "name": "Users And Logins",
107
  "summary": "Enable Audit Context - Users And Logins",
108
  "description": "When this context is enabled, the audit trail will track activity relating to: Users And Logins"
112
  "section": "section_enable_audit_contexts",
113
  "default": "Y",
114
  "type": "checkbox",
115
+ "link_info": "https://shsec.io/a3",
116
+ "link_blog": "https://shsec.io/a1",
117
  "name": "Plugins",
118
  "summary": "Enable Audit Context - Plugins",
119
  "description": "When this context is enabled, the audit trail will track activity relating to: WordPress Plugins"
123
  "section": "section_enable_audit_contexts",
124
  "default": "Y",
125
  "type": "checkbox",
126
+ "link_info": "https://shsec.io/a3",
127
+ "link_blog": "https://shsec.io/a1",
128
  "name": "Themes",
129
  "summary": "Enable Audit Context - Themes",
130
  "description": "When this context is enabled, the audit trail will track activity relating to: WordPress Themes"
134
  "section": "section_enable_audit_contexts",
135
  "default": "Y",
136
  "type": "checkbox",
137
+ "link_info": "https://shsec.io/a3",
138
+ "link_blog": "https://shsec.io/a1",
139
  "name": "Posts And Pages",
140
  "summary": "Enable Audit Context - Posts And Pages",
141
  "description": "When this context is enabled, the audit trail will track activity relating to: Editing and publishing of posts and pages"
145
  "section": "section_enable_audit_contexts",
146
  "default": "Y",
147
  "type": "checkbox",
148
+ "link_info": "https://shsec.io/a3",
149
+ "link_blog": "https://shsec.io/a1",
150
  "name": "WordPress And Settings",
151
  "summary": "Enable Audit Context - WordPress And Settings",
152
  "description": "When this context is enabled, the audit trail will track activity relating to: WordPress upgrades and changes to particular WordPress settings"
156
  "section": "section_enable_audit_contexts",
157
  "default": "Y",
158
  "type": "checkbox",
159
+ "link_info": "https://shsec.io/a3",
160
+ "link_blog": "https://shsec.io/a1",
161
  "name": "Emails",
162
  "summary": "Enable Audit Context - Emails",
163
  "description": "When this context is enabled, the audit trail will track activity relating to: Email Sending"
167
  "section": "section_enable_audit_contexts",
168
  "default": "Y",
169
  "type": "checkbox",
170
+ "link_info": "https://shsec.io/a4",
171
+ "link_blog": "https://shsec.io/a1",
172
  "name": "Shield",
173
  "summary": "Enable Audit Context - Shield",
174
  "description": "When this context is enabled, the audit trail will track activity relating to: Shield"
src/config/feature-autoupdates.php CHANGED
@@ -52,7 +52,7 @@
52
  "section": "section_enable_plugin_feature_automatic_updates_control",
53
  "default": "Y",
54
  "type": "checkbox",
55
- "link_info": "https://icwp.io/3w",
56
  "link_blog": "",
57
  "name": "Enable Automatic Updates",
58
  "summary": "Enable (or Disable) The Automatic Updates module",
@@ -63,7 +63,7 @@
63
  "section": "section_automatic_updates_for_wordpress_components",
64
  "default": "N",
65
  "type": "checkbox",
66
- "link_info": "https://icwp.io/3v",
67
  "link_blog": "",
68
  "name": "Disable All",
69
  "summary": "Completely Disable WordPress Automatic Updates",
@@ -88,7 +88,7 @@
88
  "text": "Major and Minor Versions"
89
  }
90
  ],
91
- "link_info": "https://icwp.io/3x",
92
  "link_blog": "",
93
  "name": "WordPress Core Updates",
94
  "summary": "Decide how the WordPress Core will automatically update, if at all",
@@ -156,7 +156,7 @@
156
  "premium": true,
157
  "default": "0",
158
  "type": "integer",
159
- "link_info": "https://icwp.io/e5",
160
  "link_blog": "",
161
  "name": "Update Delay",
162
  "summary": "Delay Automatic Updates For Period Of Stability",
@@ -181,7 +181,7 @@
181
  "text": "As Soon As Possible"
182
  }
183
  ],
184
- "link_info": "https://icwp.io/3x",
185
  "link_blog": "",
186
  "name": "WordPress Core Updates",
187
  "summary": "Decide how the WordPress Core will automatically update, if at all",
52
  "section": "section_enable_plugin_feature_automatic_updates_control",
53
  "default": "Y",
54
  "type": "checkbox",
55
+ "link_info": "https://shsec.io/3w",
56
  "link_blog": "",
57
  "name": "Enable Automatic Updates",
58
  "summary": "Enable (or Disable) The Automatic Updates module",
63
  "section": "section_automatic_updates_for_wordpress_components",
64
  "default": "N",
65
  "type": "checkbox",
66
+ "link_info": "https://shsec.io/3v",
67
  "link_blog": "",
68
  "name": "Disable All",
69
  "summary": "Completely Disable WordPress Automatic Updates",
88
  "text": "Major and Minor Versions"
89
  }
90
  ],
91
+ "link_info": "https://shsec.io/3x",
92
  "link_blog": "",
93
  "name": "WordPress Core Updates",
94
  "summary": "Decide how the WordPress Core will automatically update, if at all",
156
  "premium": true,
157
  "default": "0",
158
  "type": "integer",
159
+ "link_info": "https://shsec.io/e5",
160
  "link_blog": "",
161
  "name": "Update Delay",
162
  "summary": "Delay Automatic Updates For Period Of Stability",
181
  "text": "As Soon As Possible"
182
  }
183
  ],
184
+ "link_info": "https://shsec.io/3x",
185
  "link_blog": "",
186
  "name": "WordPress Core Updates",
187
  "summary": "Decide how the WordPress Core will automatically update, if at all",
src/config/feature-comments_filter.php CHANGED
@@ -92,8 +92,8 @@
92
  "section": "section_enable_plugin_feature_spam_comments_protection_filter",
93
  "default": "Y",
94
  "type": "checkbox",
95
- "link_info": "https://icwp.io/3z",
96
- "link_blog": "https://icwp.io/wpsf04",
97
  "name": "Enable SPAM Protection",
98
  "summary": "Enable (or Disable) The Comments SPAM Protection module",
99
  "description": "Un-Checking this option will completely disable the Comments SPAM Protection module"
@@ -104,7 +104,7 @@
104
  "default": 1,
105
  "min": 1,
106
  "type": "integer",
107
- "link_info": "https://icwp.io/fu",
108
  "link_blog": "",
109
  "name": "Trusted Commenter Minimum",
110
  "summary": "Minimum Number Of Approved Comments Before Commenter Is Trusted",
@@ -122,7 +122,7 @@
122
  "subscriber"
123
  ],
124
  "type": "array",
125
- "link_info": "https://icwp.io/fu",
126
  "link_blog": "",
127
  "name": "Trusted Users",
128
  "summary": "Don't Scan Comments For Users With The Following Roles",
@@ -133,8 +133,8 @@
133
  "section": "section_bot_comment_spam_protection_filter",
134
  "default": "N",
135
  "type": "checkbox",
136
- "link_info": "https://icwp.io/3n",
137
- "link_blog": "https://icwp.io/2n",
138
  "name": "GASP Protection",
139
  "summary": "Block Bot Comment SPAM",
140
  "description": "Taking the lead from the original GASP plugin for WordPress, we have extended it to include advanced spam-bot protection."
@@ -145,7 +145,7 @@
145
  "default": 10,
146
  "min": 0,
147
  "type": "integer",
148
- "link_info": "https://icwp.io/3o",
149
  "link_blog": "",
150
  "name": "Comments Cooldown",
151
  "summary": "Limit posting comments to X seconds after the page has loaded",
@@ -174,7 +174,7 @@
174
  "text": "Block And Redirect"
175
  }
176
  ],
177
- "link_info": "https://icwp.io/6j",
178
  "link_blog": "",
179
  "name": "Default SPAM Action",
180
  "summary": "How To Categorise Comments When Identified To Be SPAM",
@@ -185,8 +185,8 @@
185
  "section": "section_human_spam_filter",
186
  "default": "N",
187
  "type": "checkbox",
188
- "link_info": "https://icwp.io/57",
189
- "link_blog": "https://icwp.io/9w",
190
  "name": "Human SPAM Filter",
191
  "summary": "Enable (or Disable) The Human SPAM Filter module",
192
  "description": "Scans the content of WordPress comments for keywords that are indicative of SPAM and marks the comment according to your preferred setting below."
@@ -229,7 +229,7 @@
229
  "text": "Browser User Agent"
230
  }
231
  ],
232
- "link_info": "https://icwp.io/58",
233
  "link_blog": "",
234
  "name": "Comment Filter Items",
235
  "summary": "Select The Items To Scan For SPAM",
@@ -267,7 +267,7 @@
267
  "section": "section_recaptcha",
268
  "default": "N",
269
  "type": "checkbox",
270
- "link_info": "https://icwp.io/shld5",
271
  "link_blog": "",
272
  "name": "Google reCAPTCHA",
273
  "summary": "Enable Google reCAPTCHA For Comments",
@@ -297,7 +297,7 @@
297
  "text": "Invisible"
298
  }
299
  ],
300
- "link_info": "https://icwp.io/e4",
301
  "link_blog": "",
302
  "name": "reCAPTCHA Style",
303
  "summary": "How Google reCAPTCHA Will Be Displayed",
@@ -309,8 +309,8 @@
309
  "default": 600,
310
  "min": 0,
311
  "type": "integer",
312
- "link_info": "https://icwp.io/3o",
313
- "link_blog": "https://icwp.io/9v",
314
  "name": "Comment Token Expire",
315
  "summary": "A visitor has X seconds within which to post a comment",
316
  "description": "Default: 600 seconds (10 minutes). Each visitor is given a unique 'Token' so they can comment. This restricts spambots, but we need to force these tokens to expire and at the same time not bother the visitors."
@@ -321,7 +321,7 @@
321
  "sensitive": true,
322
  "default": "default",
323
  "type": "text",
324
- "link_info": "https://icwp.io/3p",
325
  "link_blog": "",
326
  "name": "Custom Checkbox Message",
327
  "summary": "If you want a custom checkbox message, please provide this here",
@@ -333,7 +333,7 @@
333
  "sensitive": true,
334
  "default": "default",
335
  "type": "text",
336
- "link_info": "https://icwp.io/3p",
337
  "link_blog": "",
338
  "name": "Custom Alert Message",
339
  "summary": "If you want a custom alert message, please provide this here",
@@ -345,7 +345,7 @@
345
  "sensitive": true,
346
  "default": "default",
347
  "type": "text",
348
- "link_info": "https://icwp.io/3p",
349
  "link_blog": "",
350
  "name": "Custom Wait Message",
351
  "summary": "If you want a custom submit-button wait message, please provide this here.",
@@ -357,7 +357,7 @@
357
  "sensitive": true,
358
  "default": "default",
359
  "type": "text",
360
- "link_info": "https://icwp.io/3p",
361
  "link_blog": "",
362
  "name": "Custom Reload Message",
363
  "summary": "If you want a custom message when the comment token has expired, please provide this here.",
92
  "section": "section_enable_plugin_feature_spam_comments_protection_filter",
93
  "default": "Y",
94
  "type": "checkbox",
95
+ "link_info": "https://shsec.io/3z",
96
+ "link_blog": "https://shsec.io/wpsf04",
97
  "name": "Enable SPAM Protection",
98
  "summary": "Enable (or Disable) The Comments SPAM Protection module",
99
  "description": "Un-Checking this option will completely disable the Comments SPAM Protection module"
104
  "default": 1,
105
  "min": 1,
106
  "type": "integer",
107
+ "link_info": "https://shsec.io/fu",
108
  "link_blog": "",
109
  "name": "Trusted Commenter Minimum",
110
  "summary": "Minimum Number Of Approved Comments Before Commenter Is Trusted",
122
  "subscriber"
123
  ],
124
  "type": "array",
125
+ "link_info": "https://shsec.io/fu",
126
  "link_blog": "",
127
  "name": "Trusted Users",
128
  "summary": "Don't Scan Comments For Users With The Following Roles",
133
  "section": "section_bot_comment_spam_protection_filter",
134
  "default": "N",
135
  "type": "checkbox",
136
+ "link_info": "https://shsec.io/3n",
137
+ "link_blog": "https://shsec.io/2n",
138
  "name": "GASP Protection",
139
  "summary": "Block Bot Comment SPAM",
140
  "description": "Taking the lead from the original GASP plugin for WordPress, we have extended it to include advanced spam-bot protection."
145
  "default": 10,
146
  "min": 0,
147
  "type": "integer",
148
+ "link_info": "https://shsec.io/3o",
149
  "link_blog": "",
150
  "name": "Comments Cooldown",
151
  "summary": "Limit posting comments to X seconds after the page has loaded",
174
  "text": "Block And Redirect"
175
  }
176
  ],
177
+ "link_info": "https://shsec.io/6j",
178
  "link_blog": "",
179
  "name": "Default SPAM Action",
180
  "summary": "How To Categorise Comments When Identified To Be SPAM",
185
  "section": "section_human_spam_filter",
186
  "default": "N",
187
  "type": "checkbox",
188
+ "link_info": "https://shsec.io/57",
189
+ "link_blog": "https://shsec.io/9w",
190
  "name": "Human SPAM Filter",
191
  "summary": "Enable (or Disable) The Human SPAM Filter module",
192
  "description": "Scans the content of WordPress comments for keywords that are indicative of SPAM and marks the comment according to your preferred setting below."
229
  "text": "Browser User Agent"
230
  }
231
  ],
232
+ "link_info": "https://shsec.io/58",
233
  "link_blog": "",
234
  "name": "Comment Filter Items",
235
  "summary": "Select The Items To Scan For SPAM",
267
  "section": "section_recaptcha",
268
  "default": "N",
269
  "type": "checkbox",
270
+ "link_info": "https://shsec.io/shld5",
271
  "link_blog": "",
272
  "name": "Google reCAPTCHA",
273
  "summary": "Enable Google reCAPTCHA For Comments",
297
  "text": "Invisible"
298
  }
299
  ],
300
+ "link_info": "https://shsec.io/e4",
301
  "link_blog": "",
302
  "name": "reCAPTCHA Style",
303
  "summary": "How Google reCAPTCHA Will Be Displayed",
309
  "default": 600,
310
  "min": 0,
311
  "type": "integer",
312
+ "link_info": "https://shsec.io/3o",
313
+ "link_blog": "https://shsec.io/9v",
314
  "name": "Comment Token Expire",
315
  "summary": "A visitor has X seconds within which to post a comment",
316
  "description": "Default: 600 seconds (10 minutes). Each visitor is given a unique 'Token' so they can comment. This restricts spambots, but we need to force these tokens to expire and at the same time not bother the visitors."
321
  "sensitive": true,
322
  "default": "default",
323
  "type": "text",
324
+ "link_info": "https://shsec.io/3p",
325
  "link_blog": "",
326
  "name": "Custom Checkbox Message",
327
  "summary": "If you want a custom checkbox message, please provide this here",
333
  "sensitive": true,
334
  "default": "default",
335
  "type": "text",
336
+ "link_info": "https://shsec.io/3p",
337
  "link_blog": "",
338
  "name": "Custom Alert Message",
339
  "summary": "If you want a custom alert message, please provide this here",
345
  "sensitive": true,
346
  "default": "default",
347
  "type": "text",
348
+ "link_info": "https://shsec.io/3p",
349
  "link_blog": "",
350
  "name": "Custom Wait Message",
351
  "summary": "If you want a custom submit-button wait message, please provide this here.",
357
  "sensitive": true,
358
  "default": "default",
359
  "type": "text",
360
+ "link_info": "https://shsec.io/3p",
361
  "link_blog": "",
362
  "name": "Custom Reload Message",
363
  "summary": "If you want a custom message when the comment token has expired, please provide this here.",
src/config/feature-firewall.php CHANGED
@@ -74,8 +74,8 @@
74
  "section": "section_enable_plugin_feature_wordpress_firewall",
75
  "default": "Y",
76
  "type": "checkbox",
77
- "link_info": "https://icwp.io/43",
78
- "link_blog": "https://icwp.io/wpsf01",
79
  "name": "Enable Firewall",
80
  "summary": "Enable (or Disable) The Firewall module",
81
  "description": "Un-Checking this option will completely disable the Firewall module"
@@ -224,7 +224,7 @@
224
  "section": "section_whitelist",
225
  "default": "",
226
  "type": "comma_separated_lists",
227
- "link_info": "https://icwp.io/2a",
228
  "link_blog": "",
229
  "name": "Whitelist Parameters",
230
  "summary": "Detail pages and parameters that are whitelisted (ignored by the firewall)",
74
  "section": "section_enable_plugin_feature_wordpress_firewall",
75
  "default": "Y",
76
  "type": "checkbox",
77
+ "link_info": "https://shsec.io/43",
78
+ "link_blog": "https://shsec.io/wpsf01",
79
  "name": "Enable Firewall",
80
  "summary": "Enable (or Disable) The Firewall module",
81
  "description": "Un-Checking this option will completely disable the Firewall module"
224
  "section": "section_whitelist",
225
  "default": "",
226
  "type": "comma_separated_lists",
227
+ "link_info": "https://shsec.io/2a",
228
  "link_blog": "",
229
  "name": "Whitelist Parameters",
230
  "summary": "Detail pages and parameters that are whitelisted (ignored by the firewall)",
src/config/feature-hack_protect.php CHANGED
@@ -138,8 +138,8 @@
138
  "section": "section_enable_plugin_feature_hack_protection_tools",
139
  "default": "Y",
140
  "type": "checkbox",
141
- "link_info": "https://icwp.io/wpsf38",
142
- "link_blog": "https://icwp.io/9x",
143
  "name": "Enable Hack Guard",
144
  "summary": "Enable (or Disable) The Hack Guard Module",
145
  "description": "Un-Checking this option will completely disable the Hack Guard module"
@@ -176,8 +176,8 @@
176
  "text": "Scan Enabled - No Email Notification"
177
  }
178
  ],
179
- "link_info": "https://icwp.io/du",
180
- "link_blog": "https://icwp.io/ah",
181
  "name": "Vulnerability Scanner",
182
  "summary": "Enable The Vulnerability Scanner",
183
  "description": "Scan all your WordPress assets for known security vulnerabilities."
@@ -239,8 +239,8 @@
239
  "text": "Scan Enabled - No Email Notification"
240
  }
241
  ],
242
- "link_info": "https://icwp.io/ew",
243
- "link_blog": "https://icwp.io/eo",
244
  "name": "Abandoned Plugin Scanner",
245
  "summary": "Enable The Abandoned Plugin Scanner",
246
  "description": "Scan your WordPress.org assets for whether they've been abandoned."
@@ -250,8 +250,8 @@
250
  "section": "section_scan_wcf",
251
  "default": "Y",
252
  "type": "checkbox",
253
- "link_info": "https://icwp.io/wpsf36",
254
- "link_blog": "https://icwp.io/wpsf37",
255
  "name": "WP Core File Scanner",
256
  "summary": "Automatically Scans WordPress Core Files For Alterations",
257
  "description": "Compares all WordPress core files on your site against the official WordPress files. WordPress Core files should never be altered for any reason."
@@ -261,8 +261,8 @@
261
  "section": "section_scan_wcf",
262
  "default": "N",
263
  "type": "checkbox",
264
- "link_info": "https://icwp.io/wpsf36",
265
- "link_blog": "https://icwp.io/wpsf37",
266
  "name": "Auto Repair",
267
  "summary": "Automatically Repair WordPress Core Files That Have Been Altered",
268
  "description": "Attempts to automatically repair WordPress Core files with the official WordPress file data, for files that have been altered or are missing."
@@ -307,7 +307,7 @@
307
  "text": "24 Times (scan every hour)"
308
  }
309
  ],
310
- "link_info": "https://icwp.io/b2",
311
  "link_blog": "",
312
  "name": "Scan Frequency",
313
  "summary": "Number Of Times To Automatically Scan Core Files In 24 Hours",
@@ -361,8 +361,8 @@
361
  "text": "Scan Enabled - Delete Files and Send Email Notification"
362
  }
363
  ],
364
- "link_info": "https://icwp.io/9y",
365
- "link_blog": "https://icwp.io/95",
366
  "name": "Unrecognised Files Scanner",
367
  "summary": "Scans Core Directories For Unrecognised Files",
368
  "description": "Scans for, and automatically deletes, any files in your core WordPress folders that are not part of your WordPress installation."
@@ -372,7 +372,7 @@
372
  "section": "section_scan_ufc",
373
  "default": "N",
374
  "type": "checkbox",
375
- "link_info": "https://icwp.io/95",
376
  "link_blog": "",
377
  "name": "Scan Uploads",
378
  "summary": "Scan Uploads Folder For PHP and Javascript",
@@ -392,8 +392,8 @@
392
  "mail.log"
393
  ],
394
  "type": "array",
395
- "link_info": "https://icwp.io/9z",
396
- "link_blog": "https://icwp.io/95",
397
  "name": "File Exclusions",
398
  "summary": "Provide A List Of Files To Be Excluded From The Scan",
399
  "description": "Take a new line for each file you wish to exclude from the scan. No commas are necessary."
@@ -436,8 +436,8 @@
436
  "text": "Automatic Scan Enabled"
437
  }
438
  ],
439
- "link_info": "https://icwp.io/fp",
440
- "link_blog": "https://icwp.io/fx",
441
  "name": "Automatic Malware Scan",
442
  "summary": "Enable Malware File Scanner",
443
  "description": "When enabled the Malware scanner will run automatically."
@@ -470,8 +470,8 @@
470
  "text": "Full"
471
  }
472
  ],
473
- "link_info": "https://icwp.io/fp",
474
- "link_blog": "https://icwp.io/fz",
475
  "name": "Automatic Malware Scan",
476
  "summary": "Enable Malware File Scanner",
477
  "description": "When enabled the Malware scanner will run automatically."
@@ -528,8 +528,8 @@
528
  "text": "Scan Enabled"
529
  }
530
  ],
531
- "link_info": "https://icwp.io/bl",
532
- "link_blog": "https://icwp.io/bm",
533
  "name": "Enable/Disable Guard",
534
  "summary": "Enable The Guard For Plugin And Theme Files",
535
  "description": "When enabled the Guard will automatically scan for changes to your Plugin and Theme files."
@@ -541,8 +541,8 @@
541
  "type": "integer",
542
  "default": 1,
543
  "min": 0,
544
- "link_info": "https://icwp.io/bn",
545
- "link_blog": "https://icwp.io/bm",
546
  "name": "Guard/Scan Depth",
547
  "summary": "How Deep Into The Plugin Directories To Scan And Guard",
548
  "description": "The Guard normally operates scan only the top level of a plugin folder. Increasing depth increases scan times."
@@ -559,7 +559,7 @@
559
  "htaccess"
560
  ],
561
  "type": "array",
562
- "link_info": "https://icwp.io/bo",
563
  "link_blog": "",
564
  "name": "File Types",
565
  "summary": "The File Types Included In The Scan",
@@ -571,7 +571,7 @@
571
  "premium": true,
572
  "type": "checkbox",
573
  "default": "Y",
574
- "link_info": "https://icwp.io/bp",
575
  "link_blog": "",
576
  "name": "Show Re-Install Links",
577
  "summary": "Show Re-Install Links For Plugins",
138
  "section": "section_enable_plugin_feature_hack_protection_tools",
139
  "default": "Y",
140
  "type": "checkbox",
141
+ "link_info": "https://shsec.io/wpsf38",
142
+ "link_blog": "https://shsec.io/9x",
143
  "name": "Enable Hack Guard",
144
  "summary": "Enable (or Disable) The Hack Guard Module",
145
  "description": "Un-Checking this option will completely disable the Hack Guard module"
176
  "text": "Scan Enabled - No Email Notification"
177
  }
178
  ],
179
+ "link_info": "https://shsec.io/du",
180
+ "link_blog": "https://shsec.io/ah",
181
  "name": "Vulnerability Scanner",
182
  "summary": "Enable The Vulnerability Scanner",
183
  "description": "Scan all your WordPress assets for known security vulnerabilities."
239
  "text": "Scan Enabled - No Email Notification"
240
  }
241
  ],
242
+ "link_info": "https://shsec.io/ew",
243
+ "link_blog": "https://shsec.io/eo",
244
  "name": "Abandoned Plugin Scanner",
245
  "summary": "Enable The Abandoned Plugin Scanner",
246
  "description": "Scan your WordPress.org assets for whether they've been abandoned."
250
  "section": "section_scan_wcf",
251
  "default": "Y",
252
  "type": "checkbox",
253
+ "link_info": "https://shsec.io/wpsf36",
254
+ "link_blog": "https://shsec.io/wpsf37",
255
  "name": "WP Core File Scanner",
256
  "summary": "Automatically Scans WordPress Core Files For Alterations",
257
  "description": "Compares all WordPress core files on your site against the official WordPress files. WordPress Core files should never be altered for any reason."
261
  "section": "section_scan_wcf",
262
  "default": "N",
263
  "type": "checkbox",
264
+ "link_info": "https://shsec.io/wpsf36",
265
+ "link_blog": "https://shsec.io/wpsf37",
266
  "name": "Auto Repair",
267
  "summary": "Automatically Repair WordPress Core Files That Have Been Altered",
268
  "description": "Attempts to automatically repair WordPress Core files with the official WordPress file data, for files that have been altered or are missing."
307
  "text": "24 Times (scan every hour)"
308
  }
309
  ],
310
+ "link_info": "https://shsec.io/b2",
311
  "link_blog": "",
312
  "name": "Scan Frequency",
313
  "summary": "Number Of Times To Automatically Scan Core Files In 24 Hours",
361
  "text": "Scan Enabled - Delete Files and Send Email Notification"
362
  }
363
  ],
364
+ "link_info": "https://shsec.io/9y",
365
+ "link_blog": "https://shsec.io/95",
366
  "name": "Unrecognised Files Scanner",
367
  "summary": "Scans Core Directories For Unrecognised Files",
368
  "description": "Scans for, and automatically deletes, any files in your core WordPress folders that are not part of your WordPress installation."
372
  "section": "section_scan_ufc",
373
  "default": "N",
374
  "type": "checkbox",
375
+ "link_info": "https://shsec.io/95",
376
  "link_blog": "",
377
  "name": "Scan Uploads",
378
  "summary": "Scan Uploads Folder For PHP and Javascript",
392
  "mail.log"
393
  ],
394
  "type": "array",
395
+ "link_info": "https://shsec.io/9z",
396
+ "link_blog": "https://shsec.io/95",
397
  "name": "File Exclusions",
398
  "summary": "Provide A List Of Files To Be Excluded From The Scan",
399
  "description": "Take a new line for each file you wish to exclude from the scan. No commas are necessary."
436
  "text": "Automatic Scan Enabled"
437
  }
438
  ],
439
+ "link_info": "https://shsec.io/fp",
440
+ "link_blog": "https://shsec.io/fx",
441
  "name": "Automatic Malware Scan",
442
  "summary": "Enable Malware File Scanner",
443
  "description": "When enabled the Malware scanner will run automatically."
470
  "text": "Full"
471
  }
472
  ],
473
+ "link_info": "https://shsec.io/fp",
474
+ "link_blog": "https://shsec.io/fz",
475
  "name": "Automatic Malware Scan",
476
  "summary": "Enable Malware File Scanner",
477
  "description": "When enabled the Malware scanner will run automatically."
528
  "text": "Scan Enabled"
529
  }
530
  ],
531
+ "link_info": "https://shsec.io/bl",
532
+ "link_blog": "https://shsec.io/bm",
533
  "name": "Enable/Disable Guard",
534
  "summary": "Enable The Guard For Plugin And Theme Files",
535
  "description": "When enabled the Guard will automatically scan for changes to your Plugin and Theme files."
541
  "type": "integer",
542
  "default": 1,
543
  "min": 0,
544
+ "link_info": "https://shsec.io/bn",
545
+ "link_blog": "https://shsec.io/bm",
546
  "name": "Guard/Scan Depth",
547
  "summary": "How Deep Into The Plugin Directories To Scan And Guard",
548
  "description": "The Guard normally operates scan only the top level of a plugin folder. Increasing depth increases scan times."
559
  "htaccess"
560
  ],
561
  "type": "array",
562
+ "link_info": "https://shsec.io/bo",
563
  "link_blog": "",
564
  "name": "File Types",
565
  "summary": "The File Types Included In The Scan",
571
  "premium": true,
572
  "type": "checkbox",
573
  "default": "Y",
574
+ "link_info": "https://shsec.io/bp",
575
  "link_blog": "",
576
  "name": "Show Re-Install Links",
577
  "summary": "Show Re-Install Links For Plugins",
src/config/feature-headers.php CHANGED
@@ -55,8 +55,8 @@
55
  "section": "section_enable_plugin_feature_headers",
56
  "default": "Y",
57
  "type": "checkbox",
58
- "link_info": "https://icwp.io/aj",
59
- "link_blog": "https://icwp.io/7c",
60
  "name": "Enable HTTP Headers",
61
  "summary": "Enable (or Disable) The HTTP Headers module",
62
  "description": "Un-Checking this option will completely disable the HTTP Headers module"
@@ -80,8 +80,8 @@
80
  "text": "On: Block All iFrames"
81
  }
82
  ],
83
- "link_info": "https://icwp.io/78",
84
- "link_blog": "https://icwp.io/7c",
85
  "name": "Block iFrames",
86
  "summary": "Block Remote iFrames Of This Site",
87
  "description": "The setting prevents any external website from embedding your site in an iFrame. This is useful for preventing so-called ClickJack attacks."
@@ -91,8 +91,8 @@
91
  "section": "section_security_headers",
92
  "default": "Y",
93
  "type": "checkbox",
94
- "link_info": "https://icwp.io/79",
95
- "link_blog": "https://icwp.io/7c",
96
  "name": "XSS Protection",
97
  "summary": "Employ Built-In Browser XSS Protection",
98
  "description": "Directs compatible browsers to block what they detect as Reflective XSS attacks."
@@ -102,8 +102,8 @@
102
  "section": "section_security_headers",
103
  "default": "Y",
104
  "type": "checkbox",
105
- "link_info": "https://icwp.io/7a",
106
- "link_blog": "https://icwp.io/7c",
107
  "name": "Prevent Mime-Sniff",
108
  "summary": "Turn-Off Browser Mime-Sniff",
109
  "description": "Reduces visitor exposure to malicious user-uploaded content."
@@ -156,7 +156,7 @@
156
  "text": "Disabled - Don't Send This Header"
157
  }
158
  ],
159
- "link_info": "https://icwp.io/a5",
160
  "link_blog": "",
161
  "name": "Referrer Policy",
162
  "summary": "Referrer Policy Header",
@@ -167,8 +167,8 @@
167
  "section": "section_content_security_policy",
168
  "default": "N",
169
  "type": "checkbox",
170
- "link_info": "https://icwp.io/7d",
171
- "link_blog": "https://icwp.io/7c",
172
  "name": "Enable Content Security Policy",
173
  "summary": "Enable (or Disable) The Content Security Policy module",
174
  "description": "Allows for permission and restriction of all resources loaded on your site."
55
  "section": "section_enable_plugin_feature_headers",
56
  "default": "Y",
57
  "type": "checkbox",
58
+ "link_info": "https://shsec.io/aj",
59
+ "link_blog": "https://shsec.io/7c",
60
  "name": "Enable HTTP Headers",
61
  "summary": "Enable (or Disable) The HTTP Headers module",
62
  "description": "Un-Checking this option will completely disable the HTTP Headers module"
80
  "text": "On: Block All iFrames"
81
  }
82
  ],
83
+ "link_info": "https://shsec.io/78",
84
+ "link_blog": "https://shsec.io/7c",
85
  "name": "Block iFrames",
86
  "summary": "Block Remote iFrames Of This Site",
87
  "description": "The setting prevents any external website from embedding your site in an iFrame. This is useful for preventing so-called ClickJack attacks."
91
  "section": "section_security_headers",
92
  "default": "Y",
93
  "type": "checkbox",
94
+ "link_info": "https://shsec.io/79",
95
+ "link_blog": "https://shsec.io/7c",
96
  "name": "XSS Protection",
97
  "summary": "Employ Built-In Browser XSS Protection",
98
  "description": "Directs compatible browsers to block what they detect as Reflective XSS attacks."
102
  "section": "section_security_headers",
103
  "default": "Y",
104
  "type": "checkbox",
105
+ "link_info": "https://shsec.io/7a",
106
+ "link_blog": "https://shsec.io/7c",
107
  "name": "Prevent Mime-Sniff",
108
  "summary": "Turn-Off Browser Mime-Sniff",
109
  "description": "Reduces visitor exposure to malicious user-uploaded content."
156
  "text": "Disabled - Don't Send This Header"
157
  }
158
  ],
159
+ "link_info": "https://shsec.io/a5",
160
  "link_blog": "",
161
  "name": "Referrer Policy",
162
  "summary": "Referrer Policy Header",
167
  "section": "section_content_security_policy",
168
  "default": "N",
169
  "type": "checkbox",
170
+ "link_info": "https://shsec.io/7d",
171
+ "link_blog": "https://shsec.io/7c",
172
  "name": "Enable Content Security Policy",
173
  "summary": "Enable (or Disable) The Content Security Policy module",
174
  "description": "Allows for permission and restriction of all resources loaded on your site."
src/config/feature-ips.php CHANGED
@@ -112,8 +112,8 @@
112
  "section": "section_enable_plugin_feature_ips",
113
  "default": "Y",
114
  "type": "checkbox",
115
- "link_info": "https://icwp.io/ea",
116
- "link_blog": "https://icwp.io/wpsf26",
117
  "name": "Enable IP Manager",
118
  "summary": "Enable (or Disable) The IP Manager module",
119
  "description": "Un-Checking this option will completely disable the IP Manager module"
@@ -123,8 +123,8 @@
123
  "section": "section_auto_black_list",
124
  "default": 10,
125
  "type": "integer",
126
- "link_info": "https://icwp.io/wpsf24",
127
- "link_blog": "https://icwp.io/wpsf26",
128
  "name": "Offense Limit",
129
  "summary": "Visitor IP address will be Black Listed after X bad actions on your site",
130
  "description": "A black mark is set against an IP address each time a visitor trips the defenses of the Shield plugin. When the number of these offenses exceeds specified limit, they are automatically blocked from accessing the site. Set this to 0 to turn off the Automatic IP Black List feature."
@@ -156,8 +156,8 @@
156
  "text": "Month"
157
  }
158
  ],
159
- "link_info": "https://icwp.io/wpsf25",
160
- "link_blog": "https://icwp.io/wpsf26",
161
  "name": "Auto Block Expiration",
162
  "summary": "After 1 'X' a black listed IP will be removed from the black list",
163
  "description": "Permanent and lengthy IP Black Lists are harmful to performance. You should allow IP addresses on the black list to be eventually removed over time. Shorter IP black lists are more efficient and a more intelligent use of an IP-based blocking system."
@@ -178,7 +178,7 @@
178
  "text": "With Shield Bot Protection"
179
  }
180
  ],
181
- "link_info": "https://icwp.io/f8",
182
  "link_blog": "",
183
  "name": "User Auto Unblock",
184
  "summary": "Allow Visitors To Unblock Their IP",
@@ -191,7 +191,7 @@
191
  "premium": true,
192
  "default": "default",
193
  "type": "text",
194
- "link_info": "https://icwp.io/e8",
195
  "link_blog": "",
196
  "name": "Login Failed",
197
  "summary": "Visitor Triggers The IP Offenses System Through A Failed Login",
@@ -225,8 +225,8 @@
225
  "text": "Immediate Block"
226
  }
227
  ],
228
- "link_info": "https://icwp.io/fo",
229
- "link_blog": "https://icwp.io/f7",
230
  "name": "404 Detect",
231
  "summary": "Identify A Bot When It Hits A 404",
232
  "description": "Detect When A Visitor Browses To A Non-Existent Page."
@@ -259,8 +259,8 @@
259
  "text": "Immediate Block"
260
  }
261
  ],
262
- "link_info": "https://icwp.io/fo",
263
- "link_blog": "https://icwp.io/f6",
264
  "name": "Link Cheese",
265
  "summary": "Tempt A Bot With A Fake Link To Follow",
266
  "description": "Detect A Bot That Follows A 'no-follow' Link."
@@ -268,8 +268,8 @@
268
  {
269
  "key": "track_xmlrpc",
270
  "section": "section_probes",
271
- "default": "log",
272
  "premium": true,
 
273
  "type": "select",
274
  "value_options": [
275
  {
@@ -293,8 +293,8 @@
293
  "text": "Immediate Block"
294
  }
295
  ],
296
- "link_info": "https://icwp.io/fo",
297
- "link_blog": "https://icwp.io/f7",
298
  "name": "XML-RPC Access",
299
  "summary": "Identify A Bot When It Accesses XML-RPC",
300
  "description": "If you don't use XML-RPC, why would anyone access it?"
@@ -304,6 +304,7 @@
304
  "section": "section_logins",
305
  "default": "transgression-single",
306
  "type": "select",
 
307
  "value_options": [
308
  {
309
  "value_key": "disabled",
@@ -326,8 +327,8 @@
326
  "text": "Immediate Block"
327
  }
328
  ],
329
- "link_info": "https://icwp.io/fn",
330
- "link_blog": "https://icwp.io/f7",
331
  "name": "Failed Login",
332
  "summary": "Detect Failed Login Attempts By Valid Usernames",
333
  "description": "Penalise a visitor who fails to login using a valid username."
@@ -360,8 +361,8 @@
360
  "text": "Immediate Block"
361
  }
362
  ],
363
- "link_info": "https://icwp.io/fn",
364
- "link_blog": "https://icwp.io/f7",
365
  "name": "Invalid Usernames",
366
  "summary": "Detect Invalid Username Logins",
367
  "description": "Identify A Bot When It Tries To Login With A Non-Existent Username."
@@ -394,8 +395,8 @@
394
  "text": "Immediate Block"
395
  }
396
  ],
397
- "link_info": "https://icwp.io/f5",
398
- "link_blog": "https://icwp.io/f7",
399
  "name": "Fake Web Crawler",
400
  "summary": "Detect Fake Search Engine Crawlers",
401
  "description": "Identify a Bot when it presents as an official web crawler, but analysis shows it's fake."
@@ -428,8 +429,8 @@
428
  "text": "Immediate Block"
429
  }
430
  ],
431
- "link_info": "https://icwp.io/fi",
432
- "link_blog": "https://icwp.io/f7",
433
  "name": "Empty User Agents",
434
  "summary": "Detect Requests With Empty User Agents",
435
  "description": "Identify a request as a bot if the user agent is not provided."
@@ -441,7 +442,7 @@
441
  "premium": true,
442
  "default": "default",
443
  "type": "text",
444
- "link_info": "https://icwp.io/e9",
445
  "link_blog": "",
446
  "name": "Remaining Offenses",
447
  "summary": "Visitor Triggers The IP Offenses System Through A Firewall Block",
112
  "section": "section_enable_plugin_feature_ips",
113
  "default": "Y",
114
  "type": "checkbox",
115
+ "link_info": "https://shsec.io/ea",
116
+ "link_blog": "https://shsec.io/wpsf26",
117
  "name": "Enable IP Manager",
118
  "summary": "Enable (or Disable) The IP Manager module",
119
  "description": "Un-Checking this option will completely disable the IP Manager module"
123
  "section": "section_auto_black_list",
124
  "default": 10,
125
  "type": "integer",
126
+ "link_info": "https://shsec.io/wpsf24",
127
+ "link_blog": "https://shsec.io/wpsf26",
128
  "name": "Offense Limit",
129
  "summary": "Visitor IP address will be Black Listed after X bad actions on your site",
130
  "description": "A black mark is set against an IP address each time a visitor trips the defenses of the Shield plugin. When the number of these offenses exceeds specified limit, they are automatically blocked from accessing the site. Set this to 0 to turn off the Automatic IP Black List feature."
156
  "text": "Month"
157
  }
158
  ],
159
+ "link_info": "https://shsec.io/wpsf25",
160
+ "link_blog": "https://shsec.io/wpsf26",
161
  "name": "Auto Block Expiration",
162
  "summary": "After 1 'X' a black listed IP will be removed from the black list",
163
  "description": "Permanent and lengthy IP Black Lists are harmful to performance. You should allow IP addresses on the black list to be eventually removed over time. Shorter IP black lists are more efficient and a more intelligent use of an IP-based blocking system."
178
  "text": "With Shield Bot Protection"
179
  }
180
  ],
181
+ "link_info": "https://shsec.io/f8",
182
  "link_blog": "",
183
  "name": "User Auto Unblock",
184
  "summary": "Allow Visitors To Unblock Their IP",
191
  "premium": true,
192
  "default": "default",
193
  "type": "text",
194
+ "link_info": "https://shsec.io/e8",
195
  "link_blog": "",
196
  "name": "Login Failed",
197
  "summary": "Visitor Triggers The IP Offenses System Through A Failed Login",
225
  "text": "Immediate Block"
226
  }
227
  ],
228
+ "link_info": "https://shsec.io/fo",
229
+ "link_blog": "https://shsec.io/f7",
230
  "name": "404 Detect",
231
  "summary": "Identify A Bot When It Hits A 404",
232
  "description": "Detect When A Visitor Browses To A Non-Existent Page."
259
  "text": "Immediate Block"
260
  }
261
  ],
262
+ "link_info": "https://shsec.io/fo",
263
+ "link_blog": "https://shsec.io/f6",
264
  "name": "Link Cheese",
265
  "summary": "Tempt A Bot With A Fake Link To Follow",
266
  "description": "Detect A Bot That Follows A 'no-follow' Link."
268
  {
269
  "key": "track_xmlrpc",
270
  "section": "section_probes",
 
271
  "premium": true,
272
+ "default": "log",
273
  "type": "select",
274
  "value_options": [
275
  {
293
  "text": "Immediate Block"
294
  }
295
  ],
296
+ "link_info": "https://shsec.io/fo",
297
+ "link_blog": "https://shsec.io/f7",
298
  "name": "XML-RPC Access",
299
  "summary": "Identify A Bot When It Accesses XML-RPC",
300
  "description": "If you don't use XML-RPC, why would anyone access it?"
304
  "section": "section_logins",
305
  "default": "transgression-single",
306
  "type": "select",
307
+ "default": "log",
308
  "value_options": [
309
  {
310
  "value_key": "disabled",
327
  "text": "Immediate Block"
328
  }
329
  ],
330
+ "link_info": "https://shsec.io/fn",
331
+ "link_blog": "https://shsec.io/f7",
332
  "name": "Failed Login",
333
  "summary": "Detect Failed Login Attempts By Valid Usernames",
334
  "description": "Penalise a visitor who fails to login using a valid username."
361
  "text": "Immediate Block"
362
  }
363
  ],
364
+ "link_info": "https://shsec.io/fn",
365
+ "link_blog": "https://shsec.io/f7",
366
  "name": "Invalid Usernames",
367
  "summary": "Detect Invalid Username Logins",
368
  "description": "Identify A Bot When It Tries To Login With A Non-Existent Username."
395
  "text": "Immediate Block"
396
  }
397
  ],
398
+ "link_info": "https://shsec.io/f5",
399
+ "link_blog": "https://shsec.io/f7",
400
  "name": "Fake Web Crawler",
401
  "summary": "Detect Fake Search Engine Crawlers",
402
  "description": "Identify a Bot when it presents as an official web crawler, but analysis shows it's fake."
429
  "text": "Immediate Block"
430
  }
431
  ],
432
+ "link_info": "https://shsec.io/fi",
433
+ "link_blog": "https://shsec.io/f7",
434
  "name": "Empty User Agents",
435
  "summary": "Detect Requests With Empty User Agents",
436
  "description": "Identify a request as a bot if the user agent is not provided."
442
  "premium": true,
443
  "default": "default",
444
  "type": "text",
445
+ "link_info": "https://shsec.io/e9",
446
  "link_blog": "",
447
  "name": "Remaining Offenses",
448
  "summary": "Visitor Triggers The IP Offenses System Through A Firewall Block",
src/config/feature-license.php CHANGED
@@ -115,7 +115,7 @@
115
  ],
116
  "definitions": {
117
  "license_store_url": "https://onedollarplugin.com/edd-sl/",
118
- "keyless_cp": "https://icwp.io/c5",
119
  "license_item_name": "Shield Security Pro",
120
  "license_item_id": "6047",
121
  "license_item_name_sc": "Shield Security Pro (via Shield Central)",
115
  ],
116
  "definitions": {
117
  "license_store_url": "https://onedollarplugin.com/edd-sl/",
118
+ "keyless_cp": "https://shsec.io/c5",
119
  "license_item_name": "Shield Security Pro",
120
  "license_item_id": "6047",
121
  "license_item_name_sc": "Shield Security Pro (via Shield Central)",
src/config/feature-lockdown.php CHANGED
@@ -64,7 +64,7 @@
64
  "section": "section_enable_plugin_feature_wordpress_lockdown",
65
  "default": "Y",
66
  "type": "checkbox",
67
- "link_info": "https://icwp.io/4r",
68
  "link_blog": "",
69
  "name": "Enable Lockdown",
70
  "summary": "Enable (or Disable) The Lockdown module",
@@ -75,8 +75,8 @@
75
  "section": "section_apixml",
76
  "default": "N",
77
  "type": "checkbox",
78
- "link_info": "https://icwp.io/e6",
79
- "link_blog": "https://icwp.io/fb",
80
  "name": "Disable XML-RPC",
81
  "summary": "Disable The XML-RPC System",
82
  "description": "Checking this option will completely turn off the whole XML-RPC system."
@@ -112,7 +112,7 @@
112
  "section": "section_permission_access_options",
113
  "default": "N",
114
  "type": "checkbox",
115
- "link_info": "https://icwp.io/4q",
116
  "link_blog": "",
117
  "name": "Disable File Editing",
118
  "summary": "Disable Ability To Edit Files From Within WordPress",
@@ -123,7 +123,7 @@
123
  "section": "section_permission_access_options",
124
  "default": "N",
125
  "type": "checkbox",
126
- "link_info": "https://icwp.io/4t",
127
  "link_blog": "",
128
  "name": "Force SSL Admin",
129
  "summary": "Forces WordPress Admin Dashboard To Be Delivered Over SSL",
@@ -134,7 +134,7 @@
134
  "section": "section_wordpress_obscurity_options",
135
  "default": "",
136
  "type": "text",
137
- "link_info": "https://icwp.io/43",
138
  "link_blog": "",
139
  "name": "Mask WordPress Version",
140
  "summary": "Prevents Public Display Of Your WordPress Version",
@@ -156,7 +156,7 @@
156
  "section": "section_wordpress_obscurity_options",
157
  "default": "Y",
158
  "type": "checkbox",
159
- "link_info": "https://icwp.io/wpsf23",
160
  "link_blog": "",
161
  "name": "Block Username Fishing",
162
  "summary": "Block the ability to discover WordPress usernames based on author IDs",
64
  "section": "section_enable_plugin_feature_wordpress_lockdown",
65
  "default": "Y",
66
  "type": "checkbox",
67
+ "link_info": "https://shsec.io/4r",
68
  "link_blog": "",
69
  "name": "Enable Lockdown",
70
  "summary": "Enable (or Disable) The Lockdown module",
75
  "section": "section_apixml",
76
  "default": "N",
77
  "type": "checkbox",
78
+ "link_info": "https://shsec.io/e6",
79
+ "link_blog": "https://shsec.io/fb",
80
  "name": "Disable XML-RPC",
81
  "summary": "Disable The XML-RPC System",
82
  "description": "Checking this option will completely turn off the whole XML-RPC system."
112
  "section": "section_permission_access_options",
113
  "default": "N",
114
  "type": "checkbox",
115
+ "link_info": "https://shsec.io/4q",
116
  "link_blog": "",
117
  "name": "Disable File Editing",
118
  "summary": "Disable Ability To Edit Files From Within WordPress",
123
  "section": "section_permission_access_options",
124
  "default": "N",
125
  "type": "checkbox",
126
+ "link_info": "https://shsec.io/4t",
127
  "link_blog": "",
128
  "name": "Force SSL Admin",
129
  "summary": "Forces WordPress Admin Dashboard To Be Delivered Over SSL",
134
  "section": "section_wordpress_obscurity_options",
135
  "default": "",
136
  "type": "text",
137
+ "link_info": "https://shsec.io/43",
138
  "link_blog": "",
139
  "name": "Mask WordPress Version",
140
  "summary": "Prevents Public Display Of Your WordPress Version",
156
  "section": "section_wordpress_obscurity_options",
157
  "default": "Y",
158
  "type": "checkbox",
159
+ "link_info": "https://shsec.io/wpsf23",
160
  "link_blog": "",
161
  "name": "Block Username Fishing",
162
  "summary": "Block the ability to discover WordPress usernames based on author IDs",
src/config/feature-login_protect.php CHANGED
@@ -122,8 +122,8 @@
122
  "section": "section_enable_plugin_feature_login_protection",
123
  "default": "Y",
124
  "type": "checkbox",
125
- "link_info": "https://icwp.io/51",
126
- "link_blog": "https://icwp.io/wpsf03",
127
  "name": "Enable Login Guard",
128
  "summary": "Enable (or Disable) The Login Guard Module",
129
  "description": "Un-Checking this option will completely disable the Login Guard module"
@@ -134,8 +134,8 @@
134
  "sensitive": true,
135
  "default": "",
136
  "type": "text",
137
- "link_info": "https://icwp.io/5q",
138
- "link_blog": "https://icwp.io/5r",
139
  "name": "Hide Login Page",
140
  "summary": "Rename The WordPress Login Page",
141
  "description": "Creating a path here will disable your 'wp-login.php'. Only letters and numbers are permitted: abc123"
@@ -145,8 +145,8 @@
145
  "section": "section_multifactor_authentication",
146
  "default": "N",
147
  "type": "checkbox",
148
- "link_info": "https://icwp.io/9r",
149
- "link_blog": "https://icwp.io/84",
150
  "name": "Multi-Factor Authentication",
151
  "summary": "Require All Active Authentication Factors",
152
  "description": "When enabled, all multi-factor authentication methods will be applied to a user login. Disable to only require one to pass."
@@ -158,7 +158,7 @@
158
  "default": 0,
159
  "min": 0,
160
  "type": "integer",
161
- "link_info": "https://icwp.io/b1",
162
  "link_blog": "",
163
  "name": "Multi-Factor By-Pass",
164
  "summary": "A User Can By-Pass Multi-Factor Authentication (MFA) For The Set Number Of Days",
@@ -170,8 +170,8 @@
170
  "premium": true,
171
  "default": "N",
172
  "type": "checkbox",
173
- "link_info": "https://icwp.io/dx",
174
- "link_blog": "https://icwp.io/dy",
175
  "name": "Allow Backup Codes",
176
  "summary": "Allow Users To Generate A Backup Code",
177
  "description": "Allow users to generate a backup code that can be used to login if MFA factors are unavailable."
@@ -181,8 +181,8 @@
181
  "section": "section_2fa_ga",
182
  "default": "N",
183
  "type": "checkbox",
184
- "link_info": "https://icwp.io/shld7",
185
- "link_blog": "https://icwp.io/shld6",
186
  "name": "Enable Google Authenticator",
187
  "summary": "Allow Users To Use Google Authenticator",
188
  "description": "When enabled, users will have the option to add Google Authenticator to their WordPress user profile."
@@ -192,8 +192,8 @@
192
  "section": "section_2fa_email",
193
  "default": "N",
194
  "type": "checkbox",
195
- "link_info": "https://icwp.io/3t",
196
- "link_blog": "https://icwp.io/9q",
197
  "name": "Enable Email Authentication",
198
  "summary": "Two-Factor Login Authentication By Email",
199
  "description": "All users will be required to verify their login by email-based two-factor authentication."
@@ -250,7 +250,7 @@
250
  "text": "[EDD] Customer"
251
  }
252
  ],
253
- "link_info": "https://icwp.io/4v",
254
  "link_blog": "",
255
  "name": "Enforce - Email Authentication",
256
  "summary": "All User Roles Subject To Email Authentication",
@@ -281,7 +281,7 @@
281
  "text": "Checkout (WooCommerce)"
282
  }
283
  ],
284
- "link_info": "https://icwp.io/dv",
285
  "link_blog": "",
286
  "name": "Protection Locations",
287
  "summary": "How Google reCAPTCHA Will Be Displayed",
@@ -293,8 +293,8 @@
293
  "default": "10",
294
  "min": 0,
295
  "type": "integer",
296
- "link_info": "https://icwp.io/3q",
297
- "link_blog": "https://icwp.io/9o",
298
  "name": "Login Cooldown Interval",
299
  "summary": "Limit login attempts to every X seconds",
300
  "description": "WordPress will process only ONE login attempt for every number of seconds specified. Zero (0) turns this off."
@@ -304,8 +304,8 @@
304
  "section": "section_brute_force_login_protection",
305
  "default": "N",
306
  "type": "checkbox",
307
- "link_info": "https://icwp.io/3r",
308
- "link_blog": "https://icwp.io/9n",
309
  "name": "Bot Protection",
310
  "summary": "Protect WP Login From Automated Login Attempts By Bots",
311
  "description": "Adds a dynamically (Javascript) generated checkbox to the login form that prevents bots using automated login techniques. Recommended: ON."
@@ -337,8 +337,8 @@
337
  "text": "Invisible"
338
  }
339
  ],
340
- "link_info": "https://icwp.io/9m",
341
- "link_blog": "https://icwp.io/shld5",
342
  "name": "Google reCAPTCHA",
343
  "summary": "Enable Google reCAPTCHA",
344
  "description": "Use Google reCAPTCHA on the login screen."
@@ -349,7 +349,7 @@
349
  "premium": true,
350
  "default": "N",
351
  "type": "checkbox",
352
- "link_info": "https://icwp.io/dw",
353
  "link_blog": "",
354
  "name": "AntiBot JS",
355
  "summary": "Load Anti-Bot JS For 3rd Party Login Forms",
@@ -374,8 +374,8 @@
374
  "section": "section_yubikey_authentication",
375
  "default": "N",
376
  "type": "checkbox",
377
- "link_info": "https://icwp.io/4f",
378
- "link_blog": "https://icwp.io/9t",
379
  "name": "Enable Yubikey Authentication",
380
  "summary": "Turn On / Off Yubikey Authentication On This Site",
381
  "description": "Combined with your Yubikey API Key (below) this will form the basis of your Yubikey Authentication."
@@ -386,7 +386,7 @@
386
  "sensitive": true,
387
  "default": "",
388
  "type": "text",
389
- "link_info": "https://icwp.io/4g",
390
  "link_blog": "",
391
  "name": "Yubikey App ID",
392
  "summary": "Your Unique Yubikey App ID",
@@ -398,7 +398,7 @@
398
  "sensitive": true,
399
  "default": "",
400
  "type": "text",
401
- "link_info": "https://icwp.io/4g",
402
  "link_blog": "",
403
  "name": "Yubikey API Key",
404
  "summary": "Your Unique Yubikey App API Key",
@@ -411,7 +411,7 @@
411
  "premium": true,
412
  "default": "default",
413
  "type": "text",
414
- "link_info": "https://icwp.io/dz",
415
  "link_blog": "",
416
  "name": "GASP Checkbox Text",
417
  "summary": "The Message Displayed Next To The GASP Checkbox",
@@ -424,7 +424,7 @@
424
  "premium": true,
425
  "default": "default",
426
  "type": "text",
427
- "link_info": "https://icwp.io/dz",
428
  "link_blog": "",
429
  "name": "GASP Alert Text",
430
  "summary": "The Message Displayed If The User Doesn't Check The Box",
122
  "section": "section_enable_plugin_feature_login_protection",
123
  "default": "Y",
124
  "type": "checkbox",
125
+ "link_info": "https://shsec.io/51",
126
+ "link_blog": "https://shsec.io/wpsf03",
127
  "name": "Enable Login Guard",
128
  "summary": "Enable (or Disable) The Login Guard Module",
129
  "description": "Un-Checking this option will completely disable the Login Guard module"
134
  "sensitive": true,
135
  "default": "",
136
  "type": "text",
137
+ "link_info": "https://shsec.io/5q",
138
+ "link_blog": "https://shsec.io/5r",
139
  "name": "Hide Login Page",
140
  "summary": "Rename The WordPress Login Page",
141
  "description": "Creating a path here will disable your 'wp-login.php'. Only letters and numbers are permitted: abc123"
145
  "section": "section_multifactor_authentication",
146
  "default": "N",
147
  "type": "checkbox",
148
+ "link_info": "https://shsec.io/9r",
149
+ "link_blog": "https://shsec.io/84",
150
  "name": "Multi-Factor Authentication",
151
  "summary": "Require All Active Authentication Factors",
152
  "description": "When enabled, all multi-factor authentication methods will be applied to a user login. Disable to only require one to pass."
158
  "default": 0,
159
  "min": 0,
160
  "type": "integer",
161
+ "link_info": "https://shsec.io/b1",
162
  "link_blog": "",
163
  "name": "Multi-Factor By-Pass",
164
  "summary": "A User Can By-Pass Multi-Factor Authentication (MFA) For The Set Number Of Days",
170
  "premium": true,
171
  "default": "N",
172
  "type": "checkbox",
173
+ "link_info": "https://shsec.io/dx",
174
+ "link_blog": "https://shsec.io/dy",
175
  "name": "Allow Backup Codes",
176
  "summary": "Allow Users To Generate A Backup Code",
177
  "description": "Allow users to generate a backup code that can be used to login if MFA factors are unavailable."
181
  "section": "section_2fa_ga",
182
  "default": "N",
183
  "type": "checkbox",
184
+ "link_info": "https://shsec.io/shld7",
185
+ "link_blog": "https://shsec.io/shld6",
186
  "name": "Enable Google Authenticator",
187
  "summary": "Allow Users To Use Google Authenticator",
188
  "description": "When enabled, users will have the option to add Google Authenticator to their WordPress user profile."
192
  "section": "section_2fa_email",
193
  "default": "N",
194
  "type": "checkbox",
195
+ "link_info": "https://shsec.io/3t",
196
+ "link_blog": "https://shsec.io/9q",
197
  "name": "Enable Email Authentication",
198
  "summary": "Two-Factor Login Authentication By Email",
199
  "description": "All users will be required to verify their login by email-based two-factor authentication."
250
  "text": "[EDD] Customer"
251
  }
252
  ],
253
+ "link_info": "https://shsec.io/4v",
254
  "link_blog": "",
255
  "name": "Enforce - Email Authentication",
256
  "summary": "All User Roles Subject To Email Authentication",
281
  "text": "Checkout (WooCommerce)"
282
  }
283
  ],
284
+ "link_info": "https://shsec.io/dv",
285
  "link_blog": "",
286
  "name": "Protection Locations",
287
  "summary": "How Google reCAPTCHA Will Be Displayed",
293
  "default": "10",
294
  "min": 0,
295
  "type": "integer",
296
+ "link_info": "https://shsec.io/3q",
297
+ "link_blog": "https://shsec.io/9o",
298
  "name": "Login Cooldown Interval",
299
  "summary": "Limit login attempts to every X seconds",
300
  "description": "WordPress will process only ONE login attempt for every number of seconds specified. Zero (0) turns this off."
304
  "section": "section_brute_force_login_protection",
305
  "default": "N",
306
  "type": "checkbox",
307
+ "link_info": "https://shsec.io/3r",
308
+ "link_blog": "https://shsec.io/9n",
309
  "name": "Bot Protection",
310
  "summary": "Protect WP Login From Automated Login Attempts By Bots",
311
  "description": "Adds a dynamically (Javascript) generated checkbox to the login form that prevents bots using automated login techniques. Recommended: ON."
337
  "text": "Invisible"
338
  }
339
  ],
340
+ "link_info": "https://shsec.io/9m",
341
+ "link_blog": "https://shsec.io/shld5",
342
  "name": "Google reCAPTCHA",
343
  "summary": "Enable Google reCAPTCHA",
344
  "description": "Use Google reCAPTCHA on the login screen."
349
  "premium": true,
350
  "default": "N",
351
  "type": "checkbox",
352
+ "link_info": "https://shsec.io/dw",
353
  "link_blog": "",
354
  "name": "AntiBot JS",
355
  "summary": "Load Anti-Bot JS For 3rd Party Login Forms",
374
  "section": "section_yubikey_authentication",
375
  "default": "N",
376
  "type": "checkbox",
377
+ "link_info": "https://shsec.io/4f",
378
+ "link_blog": "https://shsec.io/9t",
379
  "name": "Enable Yubikey Authentication",
380
  "summary": "Turn On / Off Yubikey Authentication On This Site",
381
  "description": "Combined with your Yubikey API Key (below) this will form the basis of your Yubikey Authentication."
386
  "sensitive": true,
387
  "default": "",
388
  "type": "text",
389
+ "link_info": "https://shsec.io/4g",
390
  "link_blog": "",
391
  "name": "Yubikey App ID",
392
  "summary": "Your Unique Yubikey App ID",
398
  "sensitive": true,
399
  "default": "",
400
  "type": "text",
401
+ "link_info": "https://shsec.io/4g",
402
  "link_blog": "",
403
  "name": "Yubikey API Key",
404
  "summary": "Your Unique Yubikey App API Key",
411
  "premium": true,
412
  "default": "default",
413
  "type": "text",
414
+ "link_info": "https://shsec.io/dz",
415
  "link_blog": "",
416
  "name": "GASP Checkbox Text",
417
  "summary": "The Message Displayed Next To The GASP Checkbox",
424
  "premium": true,
425
  "default": "default",
426
  "type": "text",
427
+ "link_info": "https://shsec.io/dz",
428
  "link_blog": "",
429
  "name": "GASP Alert Text",
430
  "summary": "The Message Displayed If The User Doesn't Check The Box",
src/config/feature-plugin.php CHANGED
@@ -25,6 +25,15 @@
25
  "can_dismiss": false,
26
  "type": "error"
27
  },
 
 
 
 
 
 
 
 
 
28
  "wizard_welcome": {
29
  "id": "wizard_welcome",
30
  "per_user": false,
@@ -104,7 +113,7 @@
104
  "section": "section_general_plugin_options",
105
  "default": "N",
106
  "type": "checkbox",
107
- "link_info": "https://icwp.io/7i",
108
  "link_blog": "",
109
  "name": "Enable Information Gathering",
110
  "summary": "Permit Anonymous Usage Information Gathering",
@@ -162,7 +171,7 @@
162
  "text": "HTTP_CLIENT_IP"
163
  }
164
  ],
165
- "link_info": "https://icwp.io/dn",
166
  "link_blog": "",
167
  "name": "Visitor IP Address",
168
  "summary": "Which Address Is Yours",
@@ -196,8 +205,8 @@
196
  "section": "section_general_plugin_options",
197
  "default": "N",
198
  "type": "checkbox",
199
- "link_info": "https://icwp.io/5v",
200
- "link_blog": "https://icwp.io/wpsf20",
201
  "name": "Show Plugin Badge",
202
  "summary": "Display Plugin Badge On Your Site",
203
  "description": "Enabling this option helps support the plugin by spreading the word about it on your website. The plugin badge also demonstrates to visitors that you take your website security seriously."
@@ -219,8 +228,8 @@
219
  "premium": true,
220
  "default": "Y",
221
  "type": "checkbox",
222
- "link_info": "https://icwp.io/do",
223
- "link_blog": "https://icwp.io/dp",
224
  "name": "Allow Import/Export",
225
  "summary": "Allow Import Of Options To, And Export Of Options From, This Site",
226
  "description": "Uncheck this box to completely disable import and export of options."
@@ -305,7 +314,7 @@
305
  "text": "Invisible"
306
  }
307
  ],
308
- "link_info": "https://icwp.io/dq",
309
  "link_blog": "",
310
  "name": "reCAPTCHA Style",
311
  "summary": "How Google reCAPTCHA Will Be Displayed By Default",
@@ -317,7 +326,7 @@
317
  "sensitive": true,
318
  "default": "",
319
  "type": "text",
320
- "link_info": "https://icwp.io/shld5",
321
  "link_blog": "",
322
  "name": "reCAPTCHA Site Key",
323
  "summary": "Google reCAPTCHA Site Key - Only v2 or Invisible. v3 NOT supported.",
@@ -329,7 +338,7 @@
329
  "sensitive": true,
330
  "default": "",
331
  "type": "text",
332
- "link_info": "https://icwp.io/shld5",
333
  "link_blog": "",
334
  "name": "reCAPTCHA Secret",
335
  "summary": "Google reCAPTCHA Secret Key - Only v2 or Invisible. v3 NOT supported.",
@@ -419,7 +428,7 @@
419
  "tracking_cron_handle": "plugin_tracking_cron",
420
  "tracking_post_url": "https://tracking.icontrolwp.com/track/plugin/shield",
421
  "importexport_cron_name": "autoimport",
422
- "href_privacy_policy": "https://icwp.io/wpshieldprivacypolicy",
423
  "db_classes": {
424
  "geoip": "\\FernleafSystems\\Wordpress\\Plugin\\Shield\\Databases\\GeoIp\\Handler",
425
  "notes": "\\FernleafSystems\\Wordpress\\Plugin\\Shield\\Databases\\AdminNotes\\Handler"
25
  "can_dismiss": false,
26
  "type": "error"
27
  },
28
+ "compat-sgoptimize": {
29
+ "id": "compat-sgoptimize",
30
+ "schedule": "conditions",
31
+ "valid_admin": true,
32
+ "plugin_admin": "ignore",
33
+ "plugin_page_only": false,
34
+ "can_dismiss": false,
35
+ "type": "warning"
36
+ },
37
  "wizard_welcome": {
38
  "id": "wizard_welcome",
39
  "per_user": false,
113
  "section": "section_general_plugin_options",
114
  "default": "N",
115
  "type": "checkbox",
116
+ "link_info": "https://shsec.io/7i",
117
  "link_blog": "",
118
  "name": "Enable Information Gathering",
119
  "summary": "Permit Anonymous Usage Information Gathering",
171
  "text": "HTTP_CLIENT_IP"
172
  }
173
  ],
174
+ "link_info": "https://shsec.io/dn",
175
  "link_blog": "",
176
  "name": "Visitor IP Address",
177
  "summary": "Which Address Is Yours",
205
  "section": "section_general_plugin_options",
206
  "default": "N",
207
  "type": "checkbox",
208
+ "link_info": "https://shsec.io/5v",
209
+ "link_blog": "https://shsec.io/wpsf20",
210
  "name": "Show Plugin Badge",
211
  "summary": "Display Plugin Badge On Your Site",
212
  "description": "Enabling this option helps support the plugin by spreading the word about it on your website. The plugin badge also demonstrates to visitors that you take your website security seriously."
228
  "premium": true,
229
  "default": "Y",
230
  "type": "checkbox",
231
+ "link_info": "https://shsec.io/do",
232
+ "link_blog": "https://shsec.io/dp",
233
  "name": "Allow Import/Export",
234
  "summary": "Allow Import Of Options To, And Export Of Options From, This Site",
235
  "description": "Uncheck this box to completely disable import and export of options."
314
  "text": "Invisible"
315
  }
316
  ],
317
+ "link_info": "https://shsec.io/dq",
318
  "link_blog": "",
319
  "name": "reCAPTCHA Style",
320
  "summary": "How Google reCAPTCHA Will Be Displayed By Default",
326
  "sensitive": true,
327
  "default": "",
328
  "type": "text",
329
+ "link_info": "https://shsec.io/shld5",
330
  "link_blog": "",
331
  "name": "reCAPTCHA Site Key",
332
  "summary": "Google reCAPTCHA Site Key - Only v2 or Invisible. v3 NOT supported.",
338
  "sensitive": true,
339
  "default": "",
340
  "type": "text",
341
+ "link_info": "https://shsec.io/shld5",
342
  "link_blog": "",
343
  "name": "reCAPTCHA Secret",
344
  "summary": "Google reCAPTCHA Secret Key - Only v2 or Invisible. v3 NOT supported.",
428
  "tracking_cron_handle": "plugin_tracking_cron",
429
  "tracking_post_url": "https://tracking.icontrolwp.com/track/plugin/shield",
430
  "importexport_cron_name": "autoimport",
431
+ "href_privacy_policy": "https://shsec.io/wpshieldprivacypolicy",
432
  "db_classes": {
433
  "geoip": "\\FernleafSystems\\Wordpress\\Plugin\\Shield\\Databases\\GeoIp\\Handler",
434
  "notes": "\\FernleafSystems\\Wordpress\\Plugin\\Shield\\Databases\\AdminNotes\\Handler"
src/config/feature-traffic.php CHANGED
@@ -60,8 +60,8 @@
60
  "section": "section_enable_plugin_feature_traffic",
61
  "default": "N",
62
  "type": "checkbox",
63
- "link_info": "https://icwp.io/ed",
64
- "link_blog": "https://icwp.io/ee",
65
  "name": "Enable Traffic Watch",
66
  "summary": "Enable (or Disable) The Traffic Watch Module",
67
  "description": "Un-Checking this option will completely disable the Traffic Watch module."
@@ -106,7 +106,7 @@
106
  "text": "Uptime Monitoring Services"
107
  }
108
  ],
109
- "link_info": "https://icwp.io/eb",
110
  "link_blog": "",
111
  "name": "Traffic Log Exclusions",
112
  "summary": "Select Which Types Of Requests To Exclude",
@@ -117,7 +117,7 @@
117
  "section": "section_traffic_options",
118
  "default": [],
119
  "type": "array",
120
- "link_info": "https://icwp.io/ec",
121
  "link_blog": "",
122
  "name": "Custom Exclusions",
123
  "summary": "Provide Custom Traffic Exclusions",
60
  "section": "section_enable_plugin_feature_traffic",
61
  "default": "N",
62
  "type": "checkbox",
63
+ "link_info": "https://shsec.io/ed",
64
+ "link_blog": "https://shsec.io/ee",
65
  "name": "Enable Traffic Watch",
66
  "summary": "Enable (or Disable) The Traffic Watch Module",
67
  "description": "Un-Checking this option will completely disable the Traffic Watch module."
106
  "text": "Uptime Monitoring Services"
107
  }
108
  ],
109
+ "link_info": "https://shsec.io/eb",
110
  "link_blog": "",
111
  "name": "Traffic Log Exclusions",
112
  "summary": "Select Which Types Of Requests To Exclude",
117
  "section": "section_traffic_options",
118
  "default": [],
119
  "type": "array",
120
+ "link_info": "https://shsec.io/ec",
121
  "link_blog": "",
122
  "name": "Custom Exclusions",
123
  "summary": "Provide Custom Traffic Exclusions",
src/config/feature-user_management.php CHANGED
@@ -75,7 +75,7 @@
75
  "section": "section_enable_plugin_feature_user_accounts_management",
76
  "default": "Y",
77
  "type": "checkbox",
78
- "link_info": "https://icwp.io/e3",
79
  "link_blog": "",
80
  "name": "Enable User Management",
81
  "summary": "Enable (or Disable) The User Management module",
@@ -88,7 +88,7 @@
88
  "sensitive": false,
89
  "default": "N",
90
  "type": "checkbox",
91
- "link_info": "https://icwp.io/e2",
92
  "link_blog": "",
93
  "name": "User Login Notification Email",
94
  "summary": "Send Email Notification To Each User Upon Successful Login",
@@ -158,8 +158,8 @@
158
  "section": "section_passwords",
159
  "type": "checkbox",
160
  "default": "N",
161
- "link_info": "https://icwp.io/e1",
162
- "link_blog": "https://icwp.io/c4",
163
  "name": "Enable Password Policies",
164
  "summary": "Enable The Password Policies Below",
165
  "description": "Turn on/off all password policies."
@@ -169,7 +169,7 @@
169
  "section": "section_passwords",
170
  "type": "checkbox",
171
  "default": "Y",
172
- "link_info": "https://icwp.io/by",
173
  "link_blog": "",
174
  "name": "Prevent Pwned Passwords",
175
  "summary": "Prevent Use Of Pwned Passwords",
@@ -252,8 +252,8 @@
252
  "premium": true,
253
  "type": "checkbox",
254
  "default": "N",
255
- "link_info": "https://icwp.io/fq",
256
- "link_blog": "https://icwp.io/fr",
257
  "name": "Allow Manual User Suspension",
258
  "summary": "Manually Suspend User Accounts To Prevent Login",
259
  "description": "Users may be suspended by administrators to prevent login."
@@ -264,8 +264,8 @@
264
  "premium": true,
265
  "type": "checkbox",
266
  "default": "Y",
267
- "link_info": "https://icwp.io/fs",
268
- "link_blog": "https://icwp.io/fr",
269
  "name": "Auto-Suspend Expired Passwords",
270
  "summary": "Automatically Suspend Users With Expired Passwords",
271
  "description": "Suspend login by users and require password reset to unsuspend."
@@ -277,8 +277,8 @@
277
  "type": "integer",
278
  "default": 0,
279
  "min": 0,
280
- "link_info": "https://icwp.io/ft",
281
- "link_blog": "https://icwp.io/fr",
282
  "name": "Auto-Suspend Idle Users",
283
  "summary": "Automatically Suspend Idle User Accounts",
284
  "description": "Prevent login by idle users and require password reset to unsuspend."
@@ -293,7 +293,7 @@
293
  "editor",
294
  "author"
295
  ],
296
- "link_info": "https://icwp.io/ft",
297
  "link_blog": "",
298
  "name": "Auto-Suspend Idle Users",
299
  "summary": "Automatically Suspend Idle User Accounts",
75
  "section": "section_enable_plugin_feature_user_accounts_management",
76
  "default": "Y",
77
  "type": "checkbox",
78
+ "link_info": "https://shsec.io/e3",
79
  "link_blog": "",
80
  "name": "Enable User Management",
81
  "summary": "Enable (or Disable) The User Management module",
88
  "sensitive": false,
89
  "default": "N",
90
  "type": "checkbox",
91
+ "link_info": "https://shsec.io/e2",
92
  "link_blog": "",
93
  "name": "User Login Notification Email",
94
  "summary": "Send Email Notification To Each User Upon Successful Login",
158
  "section": "section_passwords",
159
  "type": "checkbox",
160
  "default": "N",
161
+ "link_info": "https://shsec.io/e1",
162
+ "link_blog": "https://shsec.io/c4",
163
  "name": "Enable Password Policies",
164
  "summary": "Enable The Password Policies Below",
165
  "description": "Turn on/off all password policies."
169
  "section": "section_passwords",
170
  "type": "checkbox",
171
  "default": "Y",
172
+ "link_info": "https://shsec.io/by",
173
  "link_blog": "",
174
  "name": "Prevent Pwned Passwords",
175
  "summary": "Prevent Use Of Pwned Passwords",
252
  "premium": true,
253
  "type": "checkbox",
254
  "default": "N",
255
+ "link_info": "https://shsec.io/fq",
256
+ "link_blog": "https://shsec.io/fr",
257
  "name": "Allow Manual User Suspension",
258
  "summary": "Manually Suspend User Accounts To Prevent Login",
259
  "description": "Users may be suspended by administrators to prevent login."
264
  "premium": true,
265
  "type": "checkbox",
266
  "default": "Y",
267
+ "link_info": "https://shsec.io/fs",
268
+ "link_blog": "https://shsec.io/fr",
269
  "name": "Auto-Suspend Expired Passwords",
270
  "summary": "Automatically Suspend Users With Expired Passwords",
271
  "description": "Suspend login by users and require password reset to unsuspend."
277
  "type": "integer",
278
  "default": 0,
279
  "min": 0,
280
+ "link_info": "https://shsec.io/ft",
281
+ "link_blog": "https://shsec.io/fr",
282
  "name": "Auto-Suspend Idle Users",
283
  "summary": "Automatically Suspend Idle User Accounts",
284
  "description": "Prevent login by idle users and require password reset to unsuspend."
293
  "editor",
294
  "author"
295
  ],
296
+ "link_info": "https://shsec.io/ft",
297
  "link_blog": "",
298
  "name": "Auto-Suspend Idle Users",
299
  "summary": "Automatically Suspend Idle User Accounts",
src/features/admin_access_restriction.php CHANGED
@@ -504,83 +504,4 @@ class ICWP_WPSF_FeatureHandler_AdminAccessRestriction extends ICWP_WPSF_FeatureH
504
  protected function getNamespaceBase() {
505
  return 'SecurityAdmin';
506
  }
507
-
508
- /**
509
- * @return string
510
- * @deprecated 8.1
511
- */
512
- protected function getAccessKeyHash() {
513
- return $this->getOpt( 'admin_access_key' );
514
- }
515
-
516
- /**
517
- * @return bool
518
- * @deprecated 8.1
519
- */
520
- public function getAdminAccessArea_Options() {
521
- return $this->isOpt( 'admin_access_restrict_options', 'Y' );
522
- }
523
-
524
- /**
525
- * @return array
526
- * @deprecated 8.1
527
- */
528
- public function getAdminAccessArea_Plugins() {
529
- return $this->getAdminAccessArea( 'plugins' );
530
- }
531
-
532
- /**
533
- * @return array
534
- * @deprecated 8.1
535
- */
536
- public function getAdminAccessArea_Themes() {
537
- return $this->getAdminAccessArea( 'themes' );
538
- }
539
-
540
- /**
541
- * @return array
542
- * @deprecated 8.1
543
- */
544
- public function getAdminAccessArea_Posts() {
545
- return $this->getAdminAccessArea( 'posts' );
546
- }
547
-
548
- /**
549
- * @param string $sArea one of plugins, themes
550
- * @return array
551
- * @deprecated 8.1
552
- */
553
- public function getAdminAccessArea( $sArea = 'plugins' ) {
554
- $aSettings = $this->getOpt( 'admin_access_restrict_'.$sArea, [] );
555
- return !is_array( $aSettings ) ? [] : $aSettings;
556
- }
557
-
558
- /**
559
- * TODO: Bug where if $sType is defined, it'll be set to 'wp' anyway
560
- * @param string $sType - wp or wpms
561
- * @return array
562
- * @deprecated 8.1
563
- */
564
- public function getOptionsToRestrict( $sType = '' ) {
565
- $sType = empty( $sType ) ? ( Services::WpGeneral()->isMultisite() ? 'wpms' : 'wp' ) : 'wp';
566
- $aOptions = $this->getRestrictedOptions();
567
- return ( isset( $aOptions[ $sType.'_options' ] ) && is_array( $aOptions[ $sType.'_options' ] ) ) ? $aOptions[ $sType.'_options' ] : [];
568
- }
569
-
570
- /**
571
- * @return array
572
- * @deprecated 8.1
573
- */
574
- public function getRestrictedOptions() {
575
- $aOptions = $this->getDef( 'options_to_restrict' );
576
- return is_array( $aOptions ) ? $aOptions : [];
577
- }
578
-
579
- /**
580
- * @return bool
581
- * @deprecated 8.1
582
- */
583
- public function isAdminAccessAdminUsersEnabled() {
584
- return $this->isOpt( 'admin_access_restrict_admin_users', 'Y' );
585
- }
586
  }
504
  protected function getNamespaceBase() {
505
  return 'SecurityAdmin';
506
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
507
  }
src/features/audit_trail.php CHANGED
@@ -23,16 +23,6 @@ class ICWP_WPSF_FeatureHandler_AuditTrail extends ICWP_WPSF_FeatureHandler_BaseW
23
  && parent::isReadyToExecute();
24
  }
25
 
26
- /**
27
- * @return int
28
- * @deprecated 8.1 - TODO: Need to handle isPremium() within Options class
29
- */
30
- public function getMaxEntries() {
31
- /** @var AuditTrail\Options $oOpts */
32
- $oOpts = $this->getOptions();
33
- return $this->isPremium() ? (int)$oOpts->getOpt( 'audit_trail_max_entries' ) : $oOpts->getDefaultMaxEntries();
34
- }
35
-
36
  /**
37
  * @return array
38
  */
@@ -146,7 +136,7 @@ class ICWP_WPSF_FeatureHandler_AuditTrail extends ICWP_WPSF_FeatureHandler_BaseW
146
  $oOpts->isAuditThemes() ? $aAudit[] = __( 'themes', 'wp-simple-firewall' ) : $aNonAudit[] = __( 'themes', 'wp-simple-firewall' );
147
  $oOpts->isAuditPosts() ? $aAudit[] = __( 'posts', 'wp-simple-firewall' ) : $aNonAudit[] = __( 'posts', 'wp-simple-firewall' );
148
  $oOpts->isAuditEmails() ? $aAudit[] = __( 'emails', 'wp-simple-firewall' ) : $aNonAudit[] = __( 'emails', 'wp-simple-firewall' );
149
- $this->isAuditWp() ? $aAudit[] = 'WP' : $aNonAudit[] = 'WP';
150
 
151
  if ( empty( $aNonAudit ) ) {
152
  $aThis[ 'key_opts' ][ 'audit' ] = [
@@ -195,108 +185,4 @@ class ICWP_WPSF_FeatureHandler_AuditTrail extends ICWP_WPSF_FeatureHandler_BaseW
195
  protected function getNamespaceBase() {
196
  return 'AuditTrail';
197
  }
198
-
199
- /**
200
- * @return bool
201
- * @deprecated 8.1
202
- */
203
- public function isAuditEmails() {
204
- return $this->isOpt( 'enable_audit_context_emails', 'Y' );
205
- }
206
-
207
- /**
208
- * @return bool
209
- * @deprecated 8.1
210
- */
211
- public function isAuditPlugins() {
212
- return $this->isOpt( 'enable_audit_context_plugins', 'Y' );
213
- }
214
-
215
- /**
216
- * @return bool
217
- * @deprecated 8.1
218
- */
219
- public function isAuditPosts() {
220
- return $this->isOpt( 'enable_audit_context_posts', 'Y' );
221
- }
222
-
223
- /**
224
- * @return bool
225
- * @deprecated 8.1
226
- */
227
- public function isAuditShield() {
228
- return $this->isOpt( 'enable_audit_context_wpsf', 'Y' );
229
- }
230
-
231
- /**
232
- * @return bool
233
- * @deprecated 8.1
234
- */
235
- public function isAuditThemes() {
236
- return $this->isOpt( 'enable_audit_context_themes', 'Y' );
237
- }
238
-
239
- /**
240
- * @return bool
241
- * @deprecated 8.1
242
- */
243
- public function isAuditUsers() {
244
- return $this->isOpt( 'enable_audit_context_users', 'Y' );
245
- }
246
-
247
- /**
248
- * @return bool
249
- * @deprecated 8.1
250
- */
251
- public function isAuditWp() {
252
- return $this->isOpt( 'enable_audit_context_wordpress', 'Y' );
253
- }
254
-
255
- /**
256
- * @return int
257
- * @deprecated 8.1
258
- */
259
- public function getAutoCleanDays() {
260
- return (int)$this->getOpt( 'audit_trail_auto_clean' );
261
- }
262
-
263
- /**
264
- * @return bool
265
- * @deprecated 8.1
266
- */
267
- public function isEnabledAuditing() {
268
- /** @var AuditTrail\Options $oOpts */
269
- $oOpts = $this->getOptions();
270
- return $oOpts->isAuditEmails()
271
- || $oOpts->isAuditPlugins()
272
- || $oOpts->isAuditThemes()
273
- || $oOpts->isAuditPosts()
274
- || $oOpts->isAuditShield()
275
- || $oOpts->isAuditUsers()
276
- || $oOpts->isAuditWp();
277
- }
278
-
279
- /**
280
- * @return bool
281
- * @deprecated 8.1
282
- */
283
- public function isEnabledChangeTracking() {
284
- return !$this->isOpt( 'enable_change_tracking', 'disabled' );
285
- }
286
-
287
- /**
288
- * @return int
289
- * @deprecated 8.1
290
- */
291
- public function getDefaultMaxEntries() {
292
- return $this->getDef( 'audit_trail_default_max_entries' );
293
- }
294
-
295
- /**
296
- * @return Shield\Databases\AuditTrail\Handler
297
- * @deprecated 8.1.2
298
- */
299
- protected function loadDbHandler() {
300
- return new Shield\Databases\AuditTrail\Handler();
301
- }
302
  }
23
  && parent::isReadyToExecute();
24
  }
25
 
 
 
 
 
 
 
 
 
 
 
26
  /**
27
  * @return array
28
  */
136
  $oOpts->isAuditThemes() ? $aAudit[] = __( 'themes', 'wp-simple-firewall' ) : $aNonAudit[] = __( 'themes', 'wp-simple-firewall' );
137
  $oOpts->isAuditPosts() ? $aAudit[] = __( 'posts', 'wp-simple-firewall' ) : $aNonAudit[] = __( 'posts', 'wp-simple-firewall' );
138
  $oOpts->isAuditEmails() ? $aAudit[] = __( 'emails', 'wp-simple-firewall' ) : $aNonAudit[] = __( 'emails', 'wp-simple-firewall' );
139
+ $oOpts->isAuditWp() ? $aAudit[] = 'WP' : $aNonAudit[] = 'WP';
140
 
141
  if ( empty( $aNonAudit ) ) {
142
  $aThis[ 'key_opts' ][ 'audit' ] = [
185
  protected function getNamespaceBase() {
186
  return 'AuditTrail';
187
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
188
  }
src/features/autoupdates.php CHANGED
@@ -1,6 +1,7 @@
1
  <?php
2
 
3
  use FernleafSystems\Wordpress\Plugin\Shield;
 
4
  use FernleafSystems\Wordpress\Services\Services;
5
 
6
  class ICWP_WPSF_FeatureHandler_Autoupdates extends ICWP_WPSF_FeatureHandler_BaseWpsf {
@@ -15,144 +16,14 @@ class ICWP_WPSF_FeatureHandler_Autoupdates extends ICWP_WPSF_FeatureHandler_Base
15
  }
16
  }
17
 
18
- /**
19
- * @return string[]
20
- */
21
- public function getAutoupdatePlugins() {
22
- $aSelected = [];
23
- if ( $this->isAutoupdateIndividualPlugins() ) {
24
- $aSelected = $this->getOpt( 'selected_plugins', [] );
25
- if ( !is_array( $aSelected ) ) {
26
- $aSelected = [];
27
- }
28
- }
29
- return $aSelected;
30
- }
31
-
32
- /**
33
- * @return array
34
- */
35
- public function getDelayTracking() {
36
- $aTracking = $this->getOpt( 'delay_tracking', [] );
37
- if ( !is_array( $aTracking ) ) {
38
- $aTracking = [];
39
- }
40
- $aTracking = $this->loadDP()->mergeArraysRecursive(
41
- [
42
- 'core' => [],
43
- 'plugins' => [],
44
- 'themes' => [],
45
- ],
46
- $aTracking
47
- );
48
- $this->setOpt( 'delay_tracking', $aTracking );
49
-
50
- return $aTracking;
51
- }
52
-
53
- /**
54
- * @return int
55
- */
56
- public function getDelayUpdatesPeriod() {
57
- return $this->isPremium() ? $this->getOpt( 'update_delay', 0 )*DAY_IN_SECONDS : 0;
58
- }
59
-
60
- /**
61
- * @param array $aTrackingInfo
62
- * @return $this
63
- */
64
- public function setDelayTracking( $aTrackingInfo ) {
65
- return $this->setOpt( 'delay_tracking', $aTrackingInfo );
66
- }
67
-
68
- /**
69
- * @return bool
70
- */
71
- public function isDisableAllAutoUpdates() {
72
- return $this->isOpt( 'enable_autoupdate_disable_all', 'Y' );
73
- }
74
-
75
- /**
76
- * @return bool
77
- */
78
- public function isAutoupdateAllPlugins() {
79
- return $this->isOpt( 'enable_autoupdate_plugins', 'Y' );
80
- }
81
-
82
- /**
83
- * @premium
84
- * @return bool
85
- */
86
- public function isAutoupdateIndividualPlugins() {
87
- return $this->isOpt( 'enable_individual_autoupdate_plugins', 'Y' );
88
- }
89
-
90
- /**
91
- * @return bool
92
- */
93
- public function isDelayUpdates() {
94
- return $this->getDelayUpdatesPeriod() > 0;
95
- }
96
-
97
- /**
98
- * @param string $sPluginFile
99
- * @return bool
100
- */
101
- public function isPluginSetToAutoupdate( $sPluginFile ) {
102
- return in_array( $sPluginFile, $this->getAutoupdatePlugins() );
103
- }
104
-
105
- /**
106
- * @return bool
107
- */
108
- public function isSendAutoupdatesNotificationEmail() {
109
- return $this->isOpt( 'enable_upgrade_notification_email', 'Y' );
110
- }
111
-
112
- /**
113
- * @return string
114
- */
115
- public function getSelfAutoUpdateOpt() {
116
- return $this->getOpt( 'autoupdate_plugin_self' );
117
- }
118
-
119
- /**
120
- * @return bool
121
- */
122
- public function isAutoUpdateCoreMinor() {
123
- return !$this->isOpt( 'autoupdate_core', 'core_never' );
124
- }
125
-
126
- /**
127
- * @return bool
128
- */
129
- public function isAutoUpdateCoreMajor() {
130
- return $this->isOpt( 'autoupdate_core', 'core_major' );
131
- }
132
-
133
- /**
134
- * @param string $sPluginFile
135
- * @return $this
136
- */
137
- public function setPluginToAutoUpdate( $sPluginFile ) {
138
- $aPlugins = $this->getAutoupdatePlugins();
139
- $nKey = array_search( $sPluginFile, $aPlugins );
140
-
141
- if ( $nKey === false ) {
142
- $aPlugins[] = $sPluginFile;
143
- }
144
- else {
145
- unset( $aPlugins[ $nKey ] );
146
- }
147
-
148
- return $this->setOpt( 'selected_plugins', $aPlugins );
149
- }
150
-
151
  /**
152
  * @param array $aAllNotices
153
  * @return array
154
  */
155
  public function addInsightsNoticeData( $aAllNotices ) {
 
 
 
156
  $aNotices = [
157
  'title' => __( 'Automatic Updates', 'wp-simple-firewall' ),
158
  'messages' => []
@@ -160,7 +31,7 @@ class ICWP_WPSF_FeatureHandler_Autoupdates extends ICWP_WPSF_FeatureHandler_Base
160
  { //really disabled?
161
  $oWp = Services::WpGeneral();
162
  if ( $this->isModOptEnabled() ) {
163
- if ( $this->isDisableAllAutoUpdates() && !$oWp->getWpAutomaticUpdater()->is_disabled() ) {
164
  $aNotices[ 'messages' ][ 'disabled_auto' ] = [
165
  'title' => 'Auto Updates Not Really Disabled',
166
  'message' => __( 'Automatic Updates Are Not Disabled As Expected.', 'wp-simple-firewall' ),
@@ -184,6 +55,9 @@ class ICWP_WPSF_FeatureHandler_Autoupdates extends ICWP_WPSF_FeatureHandler_Base
184
  * @return array
185
  */
186
  public function addInsightsConfigData( $aAllData ) {
 
 
 
187
  $aThis = [
188
  'strings' => [
189
  'title' => __( 'Automatic Updates', 'wp-simple-firewall' ),
@@ -198,7 +72,7 @@ class ICWP_WPSF_FeatureHandler_Autoupdates extends ICWP_WPSF_FeatureHandler_Base
198
  }
199
  else {
200
 
201
- $bAllDisabled = $this->isDisableAllAutoUpdates();
202
  if ( $bAllDisabled ) {
203
  $aThis[ 'key_opts' ][ 'disabled' ] = [
204
  'name' => __( 'Disabled All', 'wp-simple-firewall' ),
@@ -222,7 +96,7 @@ class ICWP_WPSF_FeatureHandler_Autoupdates extends ICWP_WPSF_FeatureHandler_Base
222
  'href' => $this->getUrl_DirectLinkToOption( 'autoupdate_core' ),
223
  ];
224
 
225
- $bHasDelay = $this->isModOptEnabled() && $this->getDelayUpdatesPeriod();
226
  $aThis[ 'key_opts' ][ 'delay' ] = [
227
  'name' => __( 'Update Delay', 'wp-simple-firewall' ),
228
  'enabled' => $bHasDelay,
@@ -235,7 +109,7 @@ class ICWP_WPSF_FeatureHandler_Autoupdates extends ICWP_WPSF_FeatureHandler_Base
235
 
236
  $sName = $this->getCon()->getHumanName();
237
  $bSelfAuto = $this->isModOptEnabled()
238
- && in_array( $this->getSelfAutoUpdateOpt(), [ 'auto', 'immediate' ] );
239
  $aThis[ 'key_opts' ][ 'self' ] = [
240
  'name' => __( 'Self Auto-Update', 'wp-simple-firewall' ),
241
  'enabled' => $bSelfAuto,
@@ -258,4 +132,151 @@ class ICWP_WPSF_FeatureHandler_Autoupdates extends ICWP_WPSF_FeatureHandler_Base
258
  protected function getNamespaceBase() {
259
  return 'Autoupdates';
260
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
261
  }
1
  <?php
2
 
3
  use FernleafSystems\Wordpress\Plugin\Shield;
4
+ use FernleafSystems\Wordpress\Plugin\Shield\Modules\Autoupdates;
5
  use FernleafSystems\Wordpress\Services\Services;
6
 
7
  class ICWP_WPSF_FeatureHandler_Autoupdates extends ICWP_WPSF_FeatureHandler_BaseWpsf {
16
  }
17
  }
18
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
19
  /**
20
  * @param array $aAllNotices
21
  * @return array
22
  */
23
  public function addInsightsNoticeData( $aAllNotices ) {
24
+ /** @var Autoupdates\Options $oOpts */
25
+ $oOpts = $this->getOptions();
26
+
27
  $aNotices = [
28
  'title' => __( 'Automatic Updates', 'wp-simple-firewall' ),
29
  'messages' => []
31
  { //really disabled?
32
  $oWp = Services::WpGeneral();
33
  if ( $this->isModOptEnabled() ) {
34
+ if ( $oOpts->isDisableAllAutoUpdates() && !$oWp->getWpAutomaticUpdater()->is_disabled() ) {
35
  $aNotices[ 'messages' ][ 'disabled_auto' ] = [
36
  'title' => 'Auto Updates Not Really Disabled',
37
  'message' => __( 'Automatic Updates Are Not Disabled As Expected.', 'wp-simple-firewall' ),
55
  * @return array
56
  */
57
  public function addInsightsConfigData( $aAllData ) {
58
+ /** @var Autoupdates\Options $oOpts */
59
+ $oOpts = $this->getOptions();
60
+
61
  $aThis = [
62
  'strings' => [
63
  'title' => __( 'Automatic Updates', 'wp-simple-firewall' ),
72
  }
73
  else {
74
 
75
+ $bAllDisabled = $oOpts->isDisableAllAutoUpdates();
76
  if ( $bAllDisabled ) {
77
  $aThis[ 'key_opts' ][ 'disabled' ] = [
78
  'name' => __( 'Disabled All', 'wp-simple-firewall' ),
96
  'href' => $this->getUrl_DirectLinkToOption( 'autoupdate_core' ),
97
  ];
98
 
99
+ $bHasDelay = $this->isModOptEnabled() && $oOpts->getDelayUpdatesPeriod();
100
  $aThis[ 'key_opts' ][ 'delay' ] = [
101
  'name' => __( 'Update Delay', 'wp-simple-firewall' ),
102
  'enabled' => $bHasDelay,
109
 
110
  $sName = $this->getCon()->getHumanName();
111
  $bSelfAuto = $this->isModOptEnabled()
112
+ && in_array( $oOpts->getSelfAutoUpdateOpt(), [ 'auto', 'immediate' ] );
113
  $aThis[ 'key_opts' ][ 'self' ] = [
114
  'name' => __( 'Self Auto-Update', 'wp-simple-firewall' ),
115
  'enabled' => $bSelfAuto,
132
  protected function getNamespaceBase() {
133
  return 'Autoupdates';
134
  }
135
+
136
+ /**
137
+ * @return bool
138
+ * @deprecated 8.4
139
+ */
140
+ public function isSendAutoupdatesNotificationEmail() {
141
+ return $this->isOpt( 'enable_upgrade_notification_email', 'Y' );
142
+ }
143
+
144
+ /**
145
+ * @return bool
146
+ * @deprecated 8.4
147
+ */
148
+ public function isDisableAllAutoUpdates() {
149
+ return $this->isOpt( 'enable_autoupdate_disable_all', 'Y' );
150
+ }
151
+
152
+ /**
153
+ * @return bool
154
+ * @deprecated 8.4
155
+ */
156
+ public function isAutoUpdateCoreMajor() {
157
+ return $this->isOpt( 'autoupdate_core', 'core_major' );
158
+ }
159
+
160
+ /**
161
+ * @return bool
162
+ * @deprecated 8.4
163
+ */
164
+ public function isAutoUpdateCoreMinor() {
165
+ return !$this->isOpt( 'autoupdate_core', 'core_never' );
166
+ }
167
+
168
+ /**
169
+ * @return string
170
+ * @deprecated 8.4
171
+ */
172
+ public function getSelfAutoUpdateOpt() {
173
+ return $this->getOpt( 'autoupdate_plugin_self' );
174
+ }
175
+
176
+ /**
177
+ * @return array
178
+ * @deprecated 8.4
179
+ */
180
+ public function getDelayTracking() {
181
+ $aTracking = $this->getOpt( 'delay_tracking', [] );
182
+ if ( !is_array( $aTracking ) ) {
183
+ $aTracking = [];
184
+ }
185
+ $aTracking = Services::DataManipulation()->mergeArraysRecursive(
186
+ [
187
+ 'core' => [],
188
+ 'plugins' => [],
189
+ 'themes' => [],
190
+ ],
191
+ $aTracking
192
+ );
193
+ $this->setOpt( 'delay_tracking', $aTracking );
194
+
195
+ return $aTracking;
196
+ }
197
+
198
+ /**
199
+ * @return string[]
200
+ * @deprecated 8.4
201
+ */
202
+ public function getAutoupdatePlugins() {
203
+ $aSelected = [];
204
+ if ( $this->isAutoupdateIndividualPlugins() ) {
205
+ $aSelected = $this->getOpt( 'selected_plugins', [] );
206
+ if ( !is_array( $aSelected ) ) {
207
+ $aSelected = [];
208
+ }
209
+ }
210
+ return $aSelected;
211
+ }
212
+
213
+ /**
214
+ * @return int
215
+ * @deprecated 8.4
216
+ */
217
+ public function getDelayUpdatesPeriod() {
218
+ return $this->isPremium() ? $this->getOpt( 'update_delay', 0 )*DAY_IN_SECONDS : 0;
219
+ }
220
+
221
+ /**
222
+ * @param array $aTrackingInfo
223
+ * @return $this
224
+ * @deprecated 8.4
225
+ */
226
+ public function setDelayTracking( $aTrackingInfo ) {
227
+ return $this->setOpt( 'delay_tracking', $aTrackingInfo );
228
+ }
229
+
230
+ /**
231
+ * @return bool
232
+ * @deprecated 8.4
233
+ */
234
+ public function isAutoupdateAllPlugins() {
235
+ return $this->isOpt( 'enable_autoupdate_plugins', 'Y' );
236
+ }
237
+
238
+ /**
239
+ * @premium
240
+ * @return bool
241
+ * @deprecated 8.4
242
+ */
243
+ public function isAutoupdateIndividualPlugins() {
244
+ return $this->isOpt( 'enable_individual_autoupdate_plugins', 'Y' );
245
+ }
246
+
247
+ /**
248
+ * @return bool
249
+ * @deprecated 8.4
250
+ */
251
+ public function isDelayUpdates() {
252
+ return $this->getDelayUpdatesPeriod() > 0;
253
+ }
254
+
255
+ /**
256
+ * @param string $sPluginFile
257
+ * @return bool
258
+ * @deprecated 8.4
259
+ */
260
+ public function isPluginSetToAutoupdate( $sPluginFile ) {
261
+ return in_array( $sPluginFile, $this->getAutoupdatePlugins() );
262
+ }
263
+
264
+ /**
265
+ * @param string $sPluginFile
266
+ * @return $this
267
+ * @deprecated 8.4
268
+ */
269
+ public function setPluginToAutoUpdate( $sPluginFile ) {
270
+ $aPlugins = $this->getAutoupdatePlugins();
271
+ $nKey = array_search( $sPluginFile, $aPlugins );
272
+
273
+ if ( $nKey === false ) {
274
+ $aPlugins[] = $sPluginFile;
275
+ }
276
+ else {
277
+ unset( $aPlugins[ $nKey ] );
278
+ }
279
+
280
+ return $this->setOpt( 'selected_plugins', $aPlugins );
281
+ }
282
  }
src/features/base.php CHANGED
@@ -28,7 +28,7 @@ abstract class ICWP_WPSF_FeatureHandler_Base extends Shield\Deprecated\Foundatio
28
  private static $oEmailHandler;
29
 
30
  /**
31
- * @var ICWP_WPSF_Processor_Base
32
  */
33
  private $oProcessor;
34
 
@@ -37,11 +37,6 @@ abstract class ICWP_WPSF_FeatureHandler_Base extends Shield\Deprecated\Foundatio
37
  */
38
  private $oWizard;
39
 
40
- /**
41
- * @var Shield\Databases\Base\Handler
42
- */
43
- private $oDbh;
44
-
45
  /**
46
  * @var Shield\Modules\Base\Strings
47
  */
@@ -130,6 +125,9 @@ abstract class ICWP_WPSF_FeatureHandler_Base extends Shield\Deprecated\Foundatio
130
  add_filter( $this->prefix( 'is_event_supported' ), function ( $bSupported, $sEventTag ) {
131
  return $bSupported || $this->isSupportedEvent( $sEventTag );
132
  }, 10, 2 );
 
 
 
133
 
134
  add_action( 'admin_enqueue_scripts', [ $this, 'onWpEnqueueAdminJs' ], 100 );
135
 
@@ -1012,15 +1010,6 @@ abstract class ICWP_WPSF_FeatureHandler_Base extends Shield\Deprecated\Foundatio
1012
  return $this->oWizard;
1013
  }
1014
 
1015
- /**
1016
- * Saves the options to the WordPress Options store.
1017
- * @return void
1018
- * @deprecated 8.1
1019
- */
1020
- public function savePluginOptions() {
1021
- $this->saveModOptions();
1022
- }
1023
-
1024
  /**
1025
  * @return $this
1026
  */
@@ -1533,8 +1522,8 @@ abstract class ICWP_WPSF_FeatureHandler_Base extends Shield\Deprecated\Foundatio
1533
  'has_wizard' => $this->hasWizard(),
1534
  ],
1535
  'hrefs' => [
1536
- 'go_pro' => 'https://icwp.io/shieldgoprofeature',
1537
- 'goprofooter' => 'https://icwp.io/goprofooter',
1538
  'wizard_link' => $this->getUrl_WizardLanding(),
1539
  'wizard_landing' => $this->getUrl_WizardLanding()
1540
  ],
@@ -1924,53 +1913,21 @@ abstract class ICWP_WPSF_FeatureHandler_Base extends Shield\Deprecated\Foundatio
1924
  return !empty( $sId );
1925
  }
1926
 
1927
- /**
1928
- * @param string $sOpt
1929
- * @param int $nAt
1930
- * @return $this
1931
- * @deprecated 8.1 - TODO: Be careful when updating to use `Options` as this is newly-added in 8.1
1932
- */
1933
- protected function setOptAt( $sOpt, $nAt = null ) {
1934
- $nAt = is_null( $nAt ) ? Services::Request()->ts() : max( 0, (int)$nAt );
1935
- return $this->setOpt( $sOpt, $nAt );
1936
- }
1937
-
1938
  /**
1939
  * @return null|Shield\Modules\Base\ShieldOptions|mixed
1940
  */
1941
  public function getOptions() {
1942
  if ( !isset( $this->oOpts ) ) {
1943
-
1944
- if ( @class_exists( '\FernleafSystems\Wordpress\Plugin\Shield\Modules\Base\Options' ) ) {
1945
- $oOpts = $this->loadOptions()->setMod( $this );;
1946
- }
1947
- else {
1948
- $oOpts = new \ICWP_WPSF_OptionsVO();
1949
- }
1950
-
1951
  $oCon = $this->getCon();
1952
- $this->oOpts = $oOpts->setPathToConfig( $oCon->getPath_ConfigFile( $this->getSlug() ) )
1953
- ->setRebuildFromFile( $oCon->getIsRebuildOptionsFromFile() )
1954
- ->setOptionsStorageKey( $this->getOptionsStorageKey() )
1955
- ->setIfLoadOptionsFromStorage( !$oCon->getIsResetPlugin() );
 
1956
  }
1957
  return $this->oOpts;
1958
  }
1959
 
1960
- /**
1961
- * The primary DB for the
1962
- * @return null|Shield\Databases\Base\Handler|mixed
1963
- * @deprecated 8.1.2
1964
- */
1965
- public function getDbHandler() {
1966
- // TODO: remove this IF, as it's a stop-gap for the newer implementation (below)
1967
- if ( $this->oDbh instanceof Shield\Databases\Base\Handler ) {
1968
- return $this->oDbh;
1969
- }
1970
-
1971
- return $this->getPrimaryDbHandler();
1972
- }
1973
-
1974
  /**
1975
  * The primary DB for the
1976
  * @return null|Shield\Databases\Base\Handler|mixed
@@ -2051,23 +2008,17 @@ abstract class ICWP_WPSF_FeatureHandler_Base extends Shield\Deprecated\Foundatio
2051
  }
2052
 
2053
  /**
2054
- * @return \ICWP_WPSF_OptionsVO
2055
- * @deprecated 8.1
2056
- */
2057
- public function getOptionsVo() {
2058
- return $this->getOptions();
2059
- }
2060
-
2061
- /**
2062
- * @deprecated 8.1
2063
  */
2064
- private function getAjax() {
2065
- $this->loadAjaxHandler();
2066
  }
2067
 
2068
  /**
2069
  * @return Shield\Databases\Base\Handler|mixed|false
2070
- * @deprecated 8.1.2
2071
  */
2072
  protected function loadDbHandler() {
2073
  return false;
28
  private static $oEmailHandler;
29
 
30
  /**
31
+ * @var Shield\Modules\Base\BaseProcessor
32
  */
33
  private $oProcessor;
34
 
37
  */
38
  private $oWizard;
39
 
 
 
 
 
 
40
  /**
41
  * @var Shield\Modules\Base\Strings
42
  */
125
  add_filter( $this->prefix( 'is_event_supported' ), function ( $bSupported, $sEventTag ) {
126
  return $bSupported || $this->isSupportedEvent( $sEventTag );
127
  }, 10, 2 );
128
+ add_filter( $this->prefix( 'get_all_events' ), function ( $aEvents ) {
129
+ return array_merge( $aEvents, $this->getEvents() );
130
+ } );
131
 
132
  add_action( 'admin_enqueue_scripts', [ $this, 'onWpEnqueueAdminJs' ], 100 );
133
 
1010
  return $this->oWizard;
1011
  }
1012
 
 
 
 
 
 
 
 
 
 
1013
  /**
1014
  * @return $this
1015
  */
1522
  'has_wizard' => $this->hasWizard(),
1523
  ],
1524
  'hrefs' => [
1525
+ 'go_pro' => 'https://shsec.io/shieldgoprofeature',
1526
+ 'goprofooter' => 'https://shsec.io/goprofooter',
1527
  'wizard_link' => $this->getUrl_WizardLanding(),
1528
  'wizard_landing' => $this->getUrl_WizardLanding()
1529
  ],
1913
  return !empty( $sId );
1914
  }
1915
 
 
 
 
 
 
 
 
 
 
 
 
1916
  /**
1917
  * @return null|Shield\Modules\Base\ShieldOptions|mixed
1918
  */
1919
  public function getOptions() {
1920
  if ( !isset( $this->oOpts ) ) {
 
 
 
 
 
 
 
 
1921
  $oCon = $this->getCon();
1922
+ $this->oOpts = $this->loadOptions()->setMod( $this );
1923
+ $this->oOpts->setPathToConfig( $oCon->getPath_ConfigFile( $this->getSlug() ) )
1924
+ ->setRebuildFromFile( $oCon->getIsRebuildOptionsFromFile() )
1925
+ ->setOptionsStorageKey( $this->getOptionsStorageKey() )
1926
+ ->setIfLoadOptionsFromStorage( !$oCon->getIsResetPlugin() );
1927
  }
1928
  return $this->oOpts;
1929
  }
1930
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1931
  /**
1932
  * The primary DB for the
1933
  * @return null|Shield\Databases\Base\Handler|mixed
2008
  }
2009
 
2010
  /**
2011
+ * Saves the options to the WordPress Options store.
2012
+ * @return void
2013
+ * @deprecated 8.4
 
 
 
 
 
 
2014
  */
2015
+ public function savePluginOptions() {
2016
+ $this->saveModOptions();
2017
  }
2018
 
2019
  /**
2020
  * @return Shield\Databases\Base\Handler|mixed|false
2021
+ * @deprecated 8.4
2022
  */
2023
  protected function loadDbHandler() {
2024
  return false;
src/features/base_wpsf.php CHANGED
@@ -283,7 +283,7 @@ class ICWP_WPSF_FeatureHandler_BaseWpsf extends ICWP_WPSF_FeatureHandler_Base {
283
  * @return array
284
  */
285
  protected function getBaseDisplayData() {
286
- $sHelpUrl = $this->isWlEnabled() ? $this->getCon()->getLabels()[ 'AuthorURI' ] : 'https://icwp.io/b5';
287
 
288
  return Services::DataManipulation()->mergeArraysRecursive(
289
  parent::getBaseDisplayData(),
@@ -333,11 +333,13 @@ class ICWP_WPSF_FeatureHandler_BaseWpsf extends ICWP_WPSF_FeatureHandler_Base {
333
  */
334
  public function isVisitorWhitelisted() {
335
  if ( !isset( $this->bVisitorIsWhitelisted ) ) {
336
- /** @var \ICWP_WPSF_Processor_Ips $oPro */
337
- $oPro = $this->getCon()
338
- ->getModule_IPs()
339
- ->getProcessor();
340
- $this->bVisitorIsWhitelisted = $oPro->isIpOnWhiteList( Services::IP()->getRequestIp() );
 
 
341
  }
342
  return $this->bVisitorIsWhitelisted;
343
  }
283
  * @return array
284
  */
285
  protected function getBaseDisplayData() {
286
+ $sHelpUrl = $this->isWlEnabled() ? $this->getCon()->getLabels()[ 'AuthorURI' ] : 'https://shsec.io/b5';
287
 
288
  return Services::DataManipulation()->mergeArraysRecursive(
289
  parent::getBaseDisplayData(),
333
  */
334
  public function isVisitorWhitelisted() {
335
  if ( !isset( $this->bVisitorIsWhitelisted ) ) {
336
+ $oIpMod = $this->getCon()->getModule_IPs();
337
+ $oIp = ( new Shield\Modules\IPs\Components\LookupIpOnList() )
338
+ ->setMod( $oIpMod )
339
+ ->setIp( Services::IP()->getRequestIp() )
340
+ ->setList( $oIpMod::LIST_MANUAL_WHITE )
341
+ ->lookup();
342
+ $this->bVisitorIsWhitelisted = $oIp instanceof Shield\Databases\IPs\EntryVO;
343
  }
344
  return $this->bVisitorIsWhitelisted;
345
  }
src/features/events.php CHANGED
@@ -30,7 +30,7 @@ class ICWP_WPSF_FeatureHandler_Events extends ICWP_WPSF_FeatureHandler_BaseWpsf
30
 
31
  /**
32
  * @return Shield\Databases\Events\Handler
33
- * @deprecated 8.1.2
34
  */
35
  protected function loadDbHandler() {
36
  return new Shield\Databases\Events\Handler();
30
 
31
  /**
32
  * @return Shield\Databases\Events\Handler
33
+ * @deprecated 8.4
34
  */
35
  protected function loadDbHandler() {
36
  return new Shield\Databases\Events\Handler();
src/features/hack_protect.php CHANGED
@@ -1057,7 +1057,7 @@ class ICWP_WPSF_FeatureHandler_HackProtect extends ICWP_WPSF_FeatureHandler_Base
1057
 
1058
  /**
1059
  * @return bool
1060
- * @deprecated 8.2
1061
  */
1062
  public function isMalScanEnabled() {
1063
  return !$this->isOpt( 'mal_scan_enable', 'disabled' );
@@ -1065,7 +1065,7 @@ class ICWP_WPSF_FeatureHandler_HackProtect extends ICWP_WPSF_FeatureHandler_Base
1065
 
1066
  /**
1067
  * @return bool
1068
- * @deprecated 8.2
1069
  */
1070
  public function isMalAutoRepairPlugins() {
1071
  return $this->isOpt( 'mal_autorepair_plugins', 'Y' );
@@ -1073,7 +1073,7 @@ class ICWP_WPSF_FeatureHandler_HackProtect extends ICWP_WPSF_FeatureHandler_Base
1073
 
1074
  /**
1075
  * @return bool
1076
- * @deprecated 8.2
1077
  */
1078
  public function isMalScanAutoRepair() {
1079
  /** @var HackGuard\Options $oOpts */
@@ -1083,7 +1083,7 @@ class ICWP_WPSF_FeatureHandler_HackProtect extends ICWP_WPSF_FeatureHandler_Base
1083
 
1084
  /**
1085
  * @return bool
1086
- * @deprecated 8.2
1087
  */
1088
  public function isMalAutoRepairCore() {
1089
  return $this->isOpt( 'mal_autorepair_core', 'Y' );
@@ -1091,7 +1091,7 @@ class ICWP_WPSF_FeatureHandler_HackProtect extends ICWP_WPSF_FeatureHandler_Base
1091
 
1092
  /**
1093
  * @return bool
1094
- * @deprecated 8.2
1095
  */
1096
  public function isMalAutoRepairSurgical() {
1097
  return $this->isOpt( 'mal_autorepair_surgical', 'Y' );
@@ -1103,37 +1103,4 @@ class ICWP_WPSF_FeatureHandler_HackProtect extends ICWP_WPSF_FeatureHandler_Base
1103
  protected function getNamespaceBase() {
1104
  return 'HackGuard';
1105
  }
1106
-
1107
- /**
1108
- * @return Shield\Databases\Scanner\Handler
1109
- * @deprecated 8.1.2
1110
- */
1111
- protected function loadDbHandler() {
1112
- return new Shield\Databases\Scanner\Handler();
1113
- }
1114
-
1115
- /**
1116
- * @return int
1117
- * @deprecated 8.1
1118
- */
1119
- public function getScanFrequency() {
1120
- return (int)$this->getOpt( 'scan_frequency', 1 );
1121
- }
1122
-
1123
- /**
1124
- * @return string
1125
- * @deprecated 8.1
1126
- */
1127
- public function getScansTempDir() {
1128
- $sDir = $this->getCon()->getPluginCachePath( 'scans' );
1129
- return Services::WpFs()->mkdir( $sDir ) ? $sDir : false;
1130
- }
1131
-
1132
- /**
1133
- * @return string[]
1134
- * @deprecated 8.1
1135
- */
1136
- public function getAllScanSlugs() {
1137
- return $this->getDef( 'all_scan_slugs' );
1138
- }
1139
  }
1057
 
1058
  /**
1059
  * @return bool
1060
+ * @deprecated 8.4
1061
  */
1062
  public function isMalScanEnabled() {
1063
  return !$this->isOpt( 'mal_scan_enable', 'disabled' );
1065
 
1066
  /**
1067
  * @return bool
1068
+ * @deprecated 8.4
1069
  */
1070
  public function isMalAutoRepairPlugins() {
1071
  return $this->isOpt( 'mal_autorepair_plugins', 'Y' );
1073
 
1074
  /**
1075
  * @return bool
1076
+ * @deprecated 8.4
1077
  */
1078
  public function isMalScanAutoRepair() {
1079
  /** @var HackGuard\Options $oOpts */
1083
 
1084
  /**
1085
  * @return bool
1086
+ * @deprecated 8.4
1087
  */
1088
  public function isMalAutoRepairCore() {
1089
  return $this->isOpt( 'mal_autorepair_core', 'Y' );
1091
 
1092
  /**
1093
  * @return bool
1094
+ * @deprecated 8.4
1095
  */
1096
  public function isMalAutoRepairSurgical() {
1097
  return $this->isOpt( 'mal_autorepair_surgical', 'Y' );
1103
  protected function getNamespaceBase() {
1104
  return 'HackGuard';
1105
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1106
  }
src/features/headers.php CHANGED
@@ -1,6 +1,7 @@
1
  <?php
2
 
3
  use FernleafSystems\Wordpress\Plugin\Shield;
 
4
 
5
  class ICWP_WPSF_FeatureHandler_Headers extends ICWP_WPSF_FeatureHandler_BaseWpsf {
6
 
@@ -63,7 +64,7 @@ class ICWP_WPSF_FeatureHandler_Headers extends ICWP_WPSF_FeatureHandler_BaseWpsf
63
  protected function doExtraSubmitProcessing() {
64
  $aDomains = $this->getCspHosts();
65
  if ( !empty( $aDomains ) && is_array( $aDomains ) ) {
66
- $oDP = $this->loadDP();
67
  $aValidDomains = [];
68
  foreach ( $aDomains as $sDomain ) {
69
  $bValidDomain = false;
1
  <?php
2
 
3
  use FernleafSystems\Wordpress\Plugin\Shield;
4
+ use FernleafSystems\Wordpress\Services\Services;
5
 
6
  class ICWP_WPSF_FeatureHandler_Headers extends ICWP_WPSF_FeatureHandler_BaseWpsf {
7
 
64
  protected function doExtraSubmitProcessing() {
65
  $aDomains = $this->getCspHosts();
66
  if ( !empty( $aDomains ) && is_array( $aDomains ) ) {
67
+ $oDP = Services::Data();
68
  $aValidDomains = [];
69
  foreach ( $aDomains as $sDomain ) {
70
  $bValidDomain = false;
src/features/insights.php CHANGED
@@ -29,7 +29,7 @@ class ICWP_WPSF_FeatureHandler_Insights extends ICWP_WPSF_FeatureHandler_BaseWps
29
  $nNoticesCount += isset( $aNoticeSection[ 'count' ] ) ? $aNoticeSection[ 'count' ] : 0;
30
  }
31
 
32
- $sNavSection = $oReq->query( 'inav' );
33
  $sSubNavSection = $oReq->query( 'subnav' );
34
 
35
  /** @var ICWP_WPSF_FeatureHandler_Traffic $oTrafficMod */
@@ -63,6 +63,7 @@ class ICWP_WPSF_FeatureHandler_Insights extends ICWP_WPSF_FeatureHandler_BaseWps
63
  $oModPlugin = $oCon->getModule_Plugin();
64
  /** @var ICWP_WPSF_Processor_Plugin $oProPlugin */
65
  $oProPlugin = $oModPlugin->getProcessor();
 
66
 
67
  $bIsPro = $this->isPremium();
68
  $oCarbon = $oReq->carbon();
@@ -108,7 +109,7 @@ class ICWP_WPSF_FeatureHandler_Insights extends ICWP_WPSF_FeatureHandler_BaseWps
108
  'strings' => [
109
  'trans_limit' => sprintf(
110
  __( 'Offenses required for IP block: %s', 'wp-simple-firewall' ),
111
- sprintf( '<a href="%s" target="_blank">%s</a>', $oIpMod->getUrl_DirectLinkToOption( 'transgression_limit' ), $oIpMod->getOptTransgressionLimit() )
112
  ),
113
  'auto_expire' => sprintf(
114
  __( 'Black listed IPs auto-expire after: %s', 'wp-simple-firewall' ),
@@ -233,10 +234,10 @@ class ICWP_WPSF_FeatureHandler_Insights extends ICWP_WPSF_FeatureHandler_BaseWps
233
  ];
234
  break;
235
 
236
- case 'insights':
237
  case 'index':
238
  default:
239
- $sNavSection = 'insights';
240
  $aData = [
241
  'vars' => [
242
  'config_cards' => $this->getConfigCardsData(),
@@ -252,10 +253,12 @@ class ICWP_WPSF_FeatureHandler_Insights extends ICWP_WPSF_FeatureHandler_BaseWps
252
  'maxlength' => $this->getDef( 'license_key_length' ),
253
  ]
254
  ],
255
- 'ajax' => [],
 
 
256
  'hrefs' => [
257
- 'shield_pro_url' => 'https://icwp.io/shieldpro',
258
- 'shield_pro_more_info_url' => 'https://icwp.io/shld1',
259
  ],
260
  'flags' => [
261
  'show_ads' => false,
@@ -286,7 +289,7 @@ class ICWP_WPSF_FeatureHandler_Insights extends ICWP_WPSF_FeatureHandler_BaseWps
286
 
287
  $aTopNav = [
288
  'settings' => __( 'Settings', 'wp-simple-firewall' ),
289
- 'insights' => __( 'Overview', 'wp-simple-firewall' ),
290
  'scans' => __( 'Scans', 'wp-simple-firewall' ),
291
  'ips' => __( 'IP Lists', 'wp-simple-firewall' ),
292
  'audit' => __( 'Audit Trail', 'wp-simple-firewall' ),
@@ -343,7 +346,7 @@ class ICWP_WPSF_FeatureHandler_Insights extends ICWP_WPSF_FeatureHandler_BaseWps
343
  'show_guided_tour' => $oModPlugin->getIfShowIntroVideo(),
344
  ],
345
  'hrefs' => [
346
- 'go_pro' => 'https://icwp.io/shieldgoprofeature',
347
  'nav_home' => $this->getUrl_AdminPage(),
348
  'top_nav' => $aTopNav,
349
  'img_banner' => $oCon->getPluginUrl_Image( 'pluginlogo_banner-170x40.png' )
@@ -366,7 +369,7 @@ class ICWP_WPSF_FeatureHandler_Insights extends ICWP_WPSF_FeatureHandler_BaseWps
366
 
367
  $oCon = $this->getCon();
368
  $aStdDepsJs = [ $this->prefix( 'plugin' ) ];
369
- $sNav = Services::Request()->query( 'inav' );
370
  switch ( $sNav ) {
371
 
372
  case 'importexport':
@@ -383,6 +386,7 @@ class ICWP_WPSF_FeatureHandler_Insights extends ICWP_WPSF_FeatureHandler_BaseWps
383
  wp_enqueue_script( $sUnique );
384
  break;
385
 
 
386
  case 'reports':
387
 
388
  $aDeps = $aStdDepsJs;
@@ -765,58 +769,84 @@ class ICWP_WPSF_FeatureHandler_Insights extends ICWP_WPSF_FeatureHandler_BaseWps
765
  */
766
  protected function getStats() {
767
  $oCon = $this->getCon();
768
- /** @var Shield\Databases\Events\Handler $oDbhEvents */
769
- $oDbhEvents = $oCon->getModule_Events()->getDbHandler_Events();
770
  /** @var Shield\Databases\Events\Select $oSelEvents */
771
- $oSelEvents = $oDbhEvents->getQuerySelector();
 
 
772
 
773
  /** @var Shield\Databases\IPs\Select $oSelectIp */
774
  $oSelectIp = $oCon->getModule_IPs()
775
  ->getDbHandler_IPs()
776
  ->getQuerySelector();
777
 
778
- return [
779
  'login' => [
780
- 'title' => __( 'Login Blocks', 'wp-simple-firewall' ),
781
- 'val' => $oSelEvents->clearWheres()->sumEvent( 'login_block' ),
782
- 'tooltip' => __( 'Total login attempts blocked.', 'wp-simple-firewall' )
 
 
783
  ],
784
- 'firewall' => [
785
- 'title' => __( 'Firewall Blocks', 'wp-simple-firewall' ),
786
- 'val' => $oSelEvents->clearWheres()->sumEvent( 'firewall_block' ),
787
- 'tooltip' => __( 'Total requests blocked by firewall rules.', 'wp-simple-firewall' )
 
 
 
 
 
 
 
 
788
  ],
789
  'comments' => [
790
- 'title' => __( 'Comment Blocks', 'wp-simple-firewall' ),
791
- 'val' => $oSelEvents->clearWheres()->sumEvents( [
792
- 'spam_block_bot',
793
- 'spam_block_human',
794
- 'spam_block_recaptcha'
795
- ] ),
796
- 'tooltip' => __( 'Total SPAM comments blocked.', 'wp-simple-firewall' )
 
 
797
  ],
798
  'transgressions' => [
799
- 'title' => __( 'Offenses', 'wp-simple-firewall' ),
800
- 'val' => $oSelEvents->clearWheres()->sumEvent( 'ip_offense' ),
801
- 'tooltip' => __( 'Total offenses against the site.', 'wp-simple-firewall' )
 
 
802
  ],
803
- 'ip_blocks' => [
804
- 'title' => __( 'IP Blocks', 'wp-simple-firewall' ),
805
- 'val' => $oSelEvents->clearWheres()->sumEvent( 'conn_kill' ),
806
- 'tooltip' => __( 'Total connections blocked/killed after too many offenses.', 'wp-simple-firewall' )
 
 
807
  ],
808
- 'blackips' => [
809
- 'title' => __( 'Blacklist IPs', 'wp-simple-firewall' ),
810
- 'val' => $oSelectIp
811
- ->filterByLists(
812
- [
813
- ICWP_WPSF_FeatureHandler_Ips::LIST_AUTO_BLACK,
814
- ICWP_WPSF_FeatureHandler_Ips::LIST_MANUAL_BLACK
815
- ]
816
- )->count(),
817
- 'tooltip' => __( 'Current IP addresses with offenses against the site.', 'wp-simple-firewall' )
 
 
818
  ],
819
  ];
 
 
 
 
 
 
 
 
820
  }
821
 
822
  /**
29
  $nNoticesCount += isset( $aNoticeSection[ 'count' ] ) ? $aNoticeSection[ 'count' ] : 0;
30
  }
31
 
32
+ $sNavSection = $oReq->query( 'inav', 'overview' );
33
  $sSubNavSection = $oReq->query( 'subnav' );
34
 
35
  /** @var ICWP_WPSF_FeatureHandler_Traffic $oTrafficMod */
63
  $oModPlugin = $oCon->getModule_Plugin();
64
  /** @var ICWP_WPSF_Processor_Plugin $oProPlugin */
65
  $oProPlugin = $oModPlugin->getProcessor();
66
+ $oEvtsMod = $oCon->getModule_Events();
67
 
68
  $bIsPro = $this->isPremium();
69
  $oCarbon = $oReq->carbon();
109
  'strings' => [
110
  'trans_limit' => sprintf(
111
  __( 'Offenses required for IP block: %s', 'wp-simple-firewall' ),
112
+ sprintf( '<a href="%s" target="_blank">%s</a>', $oIpMod->getUrl_DirectLinkToOption( 'transgression_limit' ), $oIpOpts->getOffenseLimit() )
113
  ),
114
  'auto_expire' => sprintf(
115
  __( 'Black listed IPs auto-expire after: %s', 'wp-simple-firewall' ),
234
  ];
235
  break;
236
 
237
+ case 'overview':
238
  case 'index':
239
  default:
240
+ $sNavSection = 'overview';
241
  $aData = [
242
  'vars' => [
243
  'config_cards' => $this->getConfigCardsData(),
253
  'maxlength' => $this->getDef( 'license_key_length' ),
254
  ]
255
  ],
256
+ 'ajax' => [
257
+ 'render_chart_post' => $oEvtsMod->getAjaxActionData( 'render_chart_post', true ),
258
+ ],
259
  'hrefs' => [
260
+ 'shield_pro_url' => 'https://shsec.io/shieldpro',
261
+ 'shield_pro_more_info_url' => 'https://shsec.io/shld1',
262
  ],
263
  'flags' => [
264
  'show_ads' => false,
289
 
290
  $aTopNav = [
291
  'settings' => __( 'Settings', 'wp-simple-firewall' ),
292
+ 'overview' => __( 'Overview', 'wp-simple-firewall' ),
293
  'scans' => __( 'Scans', 'wp-simple-firewall' ),
294
  'ips' => __( 'IP Lists', 'wp-simple-firewall' ),
295
  'audit' => __( 'Audit Trail', 'wp-simple-firewall' ),
346
  'show_guided_tour' => $oModPlugin->getIfShowIntroVideo(),
347
  ],
348
  'hrefs' => [
349
+ 'go_pro' => 'https://shsec.io/shieldgoprofeature',
350
  'nav_home' => $this->getUrl_AdminPage(),
351
  'top_nav' => $aTopNav,
352
  'img_banner' => $oCon->getPluginUrl_Image( 'pluginlogo_banner-170x40.png' )
369
 
370
  $oCon = $this->getCon();
371
  $aStdDepsJs = [ $this->prefix( 'plugin' ) ];
372
+ $sNav = Services::Request()->query( 'inav', 'overview' );
373
  switch ( $sNav ) {
374
 
375
  case 'importexport':
386
  wp_enqueue_script( $sUnique );
387
  break;
388
 
389
+ case 'overview':
390
  case 'reports':
391
 
392
  $aDeps = $aStdDepsJs;
769
  */
770
  protected function getStats() {
771
  $oCon = $this->getCon();
 
 
772
  /** @var Shield\Databases\Events\Select $oSelEvents */
773
+ $oSelEvents = $oCon->getModule_Events()
774
+ ->getDbHandler_Events()
775
+ ->getQuerySelector();
776
 
777
  /** @var Shield\Databases\IPs\Select $oSelectIp */
778
  $oSelectIp = $oCon->getModule_IPs()
779
  ->getDbHandler_IPs()
780
  ->getQuerySelector();
781
 
782
+ $aStatsData = [
783
  'login' => [
784
+ 'id' => 'login_block',
785
+ 'title' => __( 'Login Blocks', 'wp-simple-firewall' ),
786
+ 'val' => sprintf( '%s: %s', __( 'Lifetime Total' ),
787
+ $oSelEvents->clearWheres()->sumEvent( 'login_block' ) ),
788
+ 'tooltip_p' => __( 'Total login attempts blocked.', 'wp-simple-firewall' ),
789
  ],
790
+ // 'firewall' => [
791
+ // 'id' => 'firewall_block',
792
+ // 'title' => __( 'Firewall Blocks', 'wp-simple-firewall' ),
793
+ // 'val' => $oSelEvents->clearWheres()->sumEvent( 'firewall_block' ),
794
+ // 'tooltip' => __( 'Total requests blocked by firewall rules.', 'wp-simple-firewall' )
795
+ // ],
796
+ 'bot_blocks' => [
797
+ 'id' => 'bot_blocks',
798
+ 'title' => __( 'Bot Detection', 'wp-simple-firewall' ),
799
+ 'val' => sprintf( '%s: %s', __( 'Lifetime Total' ),
800
+ $oSelEvents->clearWheres()->sumEventsLike( 'bottrack_' ) ),
801
+ 'tooltip_p' => __( 'Total requests identified as bots.', 'wp-simple-firewall' ),
802
  ],
803
  'comments' => [
804
+ 'id' => 'comment_block',
805
+ 'title' => __( 'Comment Blocks', 'wp-simple-firewall' ),
806
+ 'val' => sprintf( '%s: %s', __( 'Lifetime Total' ),
807
+ $oSelEvents->clearWheres()->sumEvents( [
808
+ 'spam_block_bot',
809
+ 'spam_block_human',
810
+ 'spam_block_recaptcha'
811
+ ] ) ),
812
+ 'tooltip_p' => __( 'Total SPAM comments blocked.', 'wp-simple-firewall' ),
813
  ],
814
  'transgressions' => [
815
+ 'id' => 'ip_offense',
816
+ 'title' => __( 'Offenses', 'wp-simple-firewall' ),
817
+ 'val' => sprintf( '%s: %s', __( 'Lifetime Total' ),
818
+ $oSelEvents->clearWheres()->sumEvent( 'ip_offense' ) ),
819
+ 'tooltip_p' => __( 'Total offenses against the site.', 'wp-simple-firewall' ),
820
  ],
821
+ 'conn_kills' => [
822
+ 'id' => 'conn_kill',
823
+ 'title' => __( 'Connection Killed', 'wp-simple-firewall' ),
824
+ 'val' => sprintf( '%s: %s', __( 'Lifetime Total' ),
825
+ $oSelEvents->clearWheres()->sumEvent( 'conn_kill' ) ),
826
+ 'tooltip_p' => __( 'Total connections blocked/killed after too many offenses.', 'wp-simple-firewall' ),
827
  ],
828
+ 'ip_blocked' => [
829
+ 'id' => 'ip_blocked',
830
+ 'title' => __( 'IP Blocked', 'wp-simple-firewall' ),
831
+ 'val' => sprintf( '%s: %s', __( 'Now' ),
832
+ $oSelectIp
833
+ ->filterByLists(
834
+ [
835
+ ICWP_WPSF_FeatureHandler_Ips::LIST_AUTO_BLACK,
836
+ ICWP_WPSF_FeatureHandler_Ips::LIST_MANUAL_BLACK
837
+ ]
838
+ )->count() ),
839
+ 'tooltip_p' => __( 'IP address exceeds offense limit and is blocked.', 'wp-simple-firewall' ),
840
  ],
841
  ];
842
+
843
+ foreach ( $aStatsData as $sKey => $sStatData ) {
844
+ $sSub = sprintf( __( 'previous %s %s', 'wp-simple-firewall' ), 7, __( 'days', 'wp-simple-firewall' ) );
845
+ $aStatsData[ $sKey ][ 'title_sub' ] = $sSub;
846
+ $aStatsData[ $sKey ][ 'tooltip_chart' ] = sprintf( '%s: %s.', __( 'Stats', 'wp-simple-firewall' ), $sSub );
847
+ }
848
+
849
+ return $aStatsData;
850
  }
851
 
852
  /**
src/features/ips.php CHANGED
@@ -173,7 +173,7 @@ class ICWP_WPSF_FeatureHandler_Ips extends ICWP_WPSF_FeatureHandler_BaseWpsf {
173
  $aIps = apply_filters( 'icwp_simple_firewall_whitelist_ips', $aIps );
174
 
175
  if ( !empty( $aIps ) && is_array( $aIps ) ) {
176
- /** @var ICWP_WPSF_Processor_Ips $oPro */
177
  $oPro = $this->getProcessor();
178
 
179
  $aWhiteIps = $oPro->getWhitelistIps();
@@ -191,122 +191,4 @@ class ICWP_WPSF_FeatureHandler_Ips extends ICWP_WPSF_FeatureHandler_BaseWpsf {
191
  protected function getNamespaceBase() {
192
  return 'IPs';
193
  }
194
-
195
- /**
196
- * @return string
197
- * @deprecated 8.1
198
- */
199
- public function getOptTransgressionLimit() {
200
- return $this->getOpt( 'transgression_limit' );
201
- }
202
-
203
- /**
204
- * @return bool
205
- * @deprecated 8.1
206
- */
207
- public function isAutoBlackListEnabled() {
208
- return ( $this->getOptTransgressionLimit() > 0 );
209
- }
210
-
211
- /**
212
- * @return bool
213
- * @deprecated 8.1
214
- */
215
- public function isEnabledTrack404() {
216
- return $this->isSelectOptionEnabled( 'track_404' );
217
- }
218
-
219
- /**
220
- * @return bool
221
- * @deprecated 8.1
222
- */
223
- public function isEnabledTrackFakeWebCrawler() {
224
- return $this->isSelectOptionEnabled( 'track_fakewebcrawler' );
225
- }
226
-
227
- /**
228
- * @return bool
229
- * @deprecated 8.1
230
- */
231
- public function isEnabledTrackLoginInvalid() {
232
- return $this->isSelectOptionEnabled( 'track_logininvalid' );
233
- }
234
-
235
- /**
236
- * @return bool
237
- * @deprecated 8.1
238
- */
239
- public function isEnabledTrackLoginFailed() {
240
- return $this->isSelectOptionEnabled( 'track_loginfailed' );
241
- }
242
-
243
- /**
244
- * @return bool
245
- * @deprecated 8.1
246
- */
247
- public function isEnabledTrackLinkCheese() {
248
- return $this->isSelectOptionEnabled( 'track_linkcheese' );
249
- }
250
-
251
- /**
252
- * @return bool
253
- * @deprecated 8.1
254
- */
255
- public function isEnabledTrackXmlRpc() {
256
- return $this->isSelectOptionEnabled( 'track_xmlrpc' );
257
- }
258
-
259
- /**
260
- * @param string $sOptionKey
261
- * @return bool
262
- * @deprecated 8.1
263
- */
264
- public function isTrackOptTransgression( $sOptionKey ) {
265
- return strpos( $this->getOpt( $sOptionKey ), 'transgression' ) !== false;
266
- }
267
-
268
- /**
269
- * @param string $sOptionKey
270
- * @return bool
271
- * @deprecated 8.1
272
- */
273
- public function isTrackOptDoubleTransgression( $sOptionKey ) {
274
- return $this->isOpt( $sOptionKey, 'transgression-double' );
275
- }
276
-
277
- /**
278
- * @param string $sOptionKey
279
- * @return bool
280
- * @deprecated 8.1
281
- */
282
- public function isTrackOptImmediateBlock( $sOptionKey ) {
283
- return $this->isOpt( $sOptionKey, 'block' );
284
- }
285
-
286
- /**
287
- * @param string $sOptionKey
288
- * @return bool
289
- * @deprecated 8.1
290
- */
291
- protected function isSelectOptionEnabled( $sOptionKey ) {
292
- $bOptPrem = $this->getOptions()->isOptPremium( $sOptionKey );
293
- return ( !$bOptPrem || $this->getCon()->isPremiumActive() ) && !$this->isOpt( $sOptionKey, 'disabled' );
294
- }
295
-
296
- /**
297
- * @return int
298
- * @deprecated 8.1
299
- */
300
- public function getAutoExpireTime() {
301
- $sConstant = strtoupper( $this->getOpt( 'auto_expire' ).'_IN_SECONDS' );
302
- return defined( $sConstant ) ? constant( $sConstant ) : ( DAY_IN_SECONDS*30 );
303
- }
304
-
305
- /**
306
- * @return Shield\Databases\IPs\Handler
307
- * @deprecated 8.1.2
308
- */
309
- protected function loadDbHandler() {
310
- return new Shield\Databases\IPs\Handler();
311
- }
312
  }
173
  $aIps = apply_filters( 'icwp_simple_firewall_whitelist_ips', $aIps );
174
 
175
  if ( !empty( $aIps ) && is_array( $aIps ) ) {
176
+ /** @var \ICWP_WPSF_Processor_Ips $oPro */
177
  $oPro = $this->getProcessor();
178
 
179
  $aWhiteIps = $oPro->getWhitelistIps();
191
  protected function getNamespaceBase() {
192
  return 'IPs';
193
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
194
  }
src/features/license.php CHANGED
@@ -578,8 +578,8 @@ class ICWP_WPSF_FeatureHandler_License extends ICWP_WPSF_FeatureHandler_BaseWpsf
578
  'connection_debug' => $this->getAjaxActionData( 'connection_debug' )
579
  ],
580
  'aHrefs' => [
581
- 'shield_pro_url' => 'https://icwp.io/shieldpro',
582
- 'shield_pro_more_info_url' => 'https://icwp.io/shld1',
583
  'iframe_url' => $this->getDef( 'landing_page_url' ),
584
  'keyless_cp' => $this->getDef( 'keyless_cp' ),
585
  ],
578
  'connection_debug' => $this->getAjaxActionData( 'connection_debug' )
579
  ],
580
  'aHrefs' => [
581
+ 'shield_pro_url' => 'https://shsec.io/shieldpro',
582
+ 'shield_pro_more_info_url' => 'https://shsec.io/shld1',
583
  'iframe_url' => $this->getDef( 'landing_page_url' ),
584
  'keyless_cp' => $this->getDef( 'keyless_cp' ),
585
  ],
src/features/login_protect.php CHANGED
@@ -184,7 +184,7 @@ class ICWP_WPSF_FeatureHandler_LoginProtect extends ICWP_WPSF_FeatureHandler_Bas
184
 
185
  /**
186
  * @return int
187
- * @deprecated 8.1.2
188
  */
189
  public function getCooldownInterval() {
190
  return (int)$this->getOpt( 'login_limit_interval' );
@@ -502,7 +502,7 @@ class ICWP_WPSF_FeatureHandler_LoginProtect extends ICWP_WPSF_FeatureHandler_Bas
502
  $aWarnings[] =
503
  __( '2FA by email demands that your WP site is properly configured to send email.', 'wp-simple-firewall' )
504
  .'<br/>'.__( 'This is a common problem and you may get locked out in the future if you ignore this.', 'wp-simple-firewall' )
505
- .' '.sprintf( '<a href="%s" target="_blank" class="alert-link">%s</a>', 'https://icwp.io/dd', __( 'Learn More.', 'wp-simple-firewall' ) );
506
  }
507
 
508
  return $aWarnings;
@@ -646,7 +646,7 @@ class ICWP_WPSF_FeatureHandler_LoginProtect extends ICWP_WPSF_FeatureHandler_Bas
646
 
647
  /**
648
  * @return bool
649
- * @deprecated 8.1.2
650
  */
651
  public function isCooldownEnabled() {
652
  return $this->getCooldownInterval() > 0;
184
 
185
  /**
186
  * @return int
187
+ * @deprecated 8.4
188
  */
189
  public function getCooldownInterval() {
190
  return (int)$this->getOpt( 'login_limit_interval' );
502
  $aWarnings[] =
503
  __( '2FA by email demands that your WP site is properly configured to send email.', 'wp-simple-firewall' )
504
  .'<br/>'.__( 'This is a common problem and you may get locked out in the future if you ignore this.', 'wp-simple-firewall' )
505
+ .' '.sprintf( '<a href="%s" target="_blank" class="alert-link">%s</a>', 'https://shsec.io/dd', __( 'Learn More.', 'wp-simple-firewall' ) );
506
  }
507
 
508
  return $aWarnings;
646
 
647
  /**
648
  * @return bool
649
+ * @deprecated 8.4
650
  */
651
  public function isCooldownEnabled() {
652
  return $this->getCooldownInterval() > 0;
src/features/plugin.php CHANGED
@@ -585,12 +585,12 @@ class ICWP_WPSF_FeatureHandler_Plugin extends ICWP_WPSF_FeatureHandler_BaseWpsf
585
  * @return $this
586
  */
587
  public function addUrlToImportExportWhitelistUrls( $sUrl ) {
588
- $sUrl = $this->loadDP()->validateSimpleHttpUrl( $sUrl );
589
  if ( $sUrl !== false ) {
590
  $aWhitelistUrls = $this->getImportExportWhitelist();
591
  $aWhitelistUrls[] = $sUrl;
592
- $this->setOpt( 'importexport_whitelist', $aWhitelistUrls )
593
- ->savePluginOptions();
594
  }
595
  return $this;
596
  }
@@ -600,15 +600,15 @@ class ICWP_WPSF_FeatureHandler_Plugin extends ICWP_WPSF_FeatureHandler_BaseWpsf
600
  * @return $this
601
  */
602
  public function removeUrlFromImportExportWhitelistUrls( $sUrl ) {
603
- $sUrl = $this->loadDP()->validateSimpleHttpUrl( $sUrl );
604
  if ( $sUrl !== false ) {
605
  $aWhitelistUrls = $this->getImportExportWhitelist();
606
  $sKey = array_search( $sUrl, $aWhitelistUrls );
607
  if ( $sKey !== false ) {
608
  unset( $aWhitelistUrls[ $sKey ] );
609
  }
610
- $this->setOpt( 'importexport_whitelist', $aWhitelistUrls )
611
- ->savePluginOptions();
612
  }
613
  return $this;
614
  }
@@ -625,7 +625,7 @@ class ICWP_WPSF_FeatureHandler_Plugin extends ICWP_WPSF_FeatureHandler_BaseWpsf
625
  * @return $this
626
  */
627
  protected function cleanImportExportWhitelistUrls() {
628
- $oDP = $this->loadDP();
629
 
630
  $aCleaned = [];
631
  $aWhitelistUrls = $this->getImportExportWhitelist();
@@ -643,7 +643,7 @@ class ICWP_WPSF_FeatureHandler_Plugin extends ICWP_WPSF_FeatureHandler_BaseWpsf
643
  * @return $this
644
  */
645
  protected function cleanImportExportMasterImportUrl() {
646
- $sUrl = $this->loadDP()->validateSimpleHttpUrl( $this->getImportExportMasterImportUrl() );
647
  if ( $sUrl === false ) {
648
  $sUrl = '';
649
  }
@@ -654,9 +654,8 @@ class ICWP_WPSF_FeatureHandler_Plugin extends ICWP_WPSF_FeatureHandler_BaseWpsf
654
  * @return $this
655
  */
656
  public function startImportExportHandshake() {
657
- $this->setOpt( 'importexport_handshake_expires_at', Services::Request()->ts() + 30 )
658
- ->savePluginOptions();
659
- return $this;
660
  }
661
 
662
  /**
@@ -794,7 +793,7 @@ class ICWP_WPSF_FeatureHandler_Plugin extends ICWP_WPSF_FeatureHandler_BaseWpsf
794
 
795
  /**
796
  * @return int
797
- * @deprecated 8.2.2
798
  */
799
  public function getLastCheckServerIpAt() {
800
  return $this->getOpt( 'this_server_ip_last_check_at', 0 );
585
  * @return $this
586
  */
587
  public function addUrlToImportExportWhitelistUrls( $sUrl ) {
588
+ $sUrl = Services::Data()->validateSimpleHttpUrl( $sUrl );
589
  if ( $sUrl !== false ) {
590
  $aWhitelistUrls = $this->getImportExportWhitelist();
591
  $aWhitelistUrls[] = $sUrl;
592
+ $this->setOpt( 'importexport_whitelist', $aWhitelistUrls );
593
+ $this->saveModOptions();
594
  }
595
  return $this;
596
  }
600
  * @return $this
601
  */
602
  public function removeUrlFromImportExportWhitelistUrls( $sUrl ) {
603
+ $sUrl = Services::Data()->validateSimpleHttpUrl( $sUrl );
604
  if ( $sUrl !== false ) {
605
  $aWhitelistUrls = $this->getImportExportWhitelist();
606
  $sKey = array_search( $sUrl, $aWhitelistUrls );
607
  if ( $sKey !== false ) {
608
  unset( $aWhitelistUrls[ $sKey ] );
609
  }
610
+ $this->setOpt( 'importexport_whitelist', $aWhitelistUrls );
611
+ $this->saveModOptions();
612
  }
613
  return $this;
614
  }
625
  * @return $this
626
  */
627
  protected function cleanImportExportWhitelistUrls() {
628
+ $oDP = Services::Data();
629
 
630
  $aCleaned = [];
631
  $aWhitelistUrls = $this->getImportExportWhitelist();
643
  * @return $this
644
  */
645
  protected function cleanImportExportMasterImportUrl() {
646
+ $sUrl = Services::Data()->validateSimpleHttpUrl( $this->getImportExportMasterImportUrl() );
647
  if ( $sUrl === false ) {
648
  $sUrl = '';
649
  }
654
  * @return $this
655
  */
656
  public function startImportExportHandshake() {
657
+ $this->setOpt( 'importexport_handshake_expires_at', Services::Request()->ts() + 30 );
658
+ return $this->saveModOptions();
 
659
  }
660
 
661
  /**
793
 
794
  /**
795
  * @return int
796
+ * @deprecated 8.4
797
  */
798
  public function getLastCheckServerIpAt() {
799
  return $this->getOpt( 'this_server_ip_last_check_at', 0 );
src/features/sessions.php CHANGED
@@ -44,7 +44,7 @@ class ICWP_WPSF_FeatureHandler_Sessions extends ICWP_WPSF_FeatureHandler_BaseWps
44
 
45
  /**
46
  * @return Shield\Databases\Session\Handler
47
- * @deprecated 8.1.2
48
  */
49
  protected function loadDbHandler() {
50
  return new Shield\Databases\Session\Handler();
44
 
45
  /**
46
  * @return Shield\Databases\Session\Handler
47
+ * @deprecated 8.4
48
  */
49
  protected function loadDbHandler() {
50
  return new Shield\Databases\Session\Handler();
src/features/statistics.php CHANGED
@@ -5,7 +5,7 @@ use FernleafSystems\Wordpress\Services\Services;
5
 
6
  /**
7
  * Class ICWP_WPSF_FeatureHandler_Statistics
8
- * @deprecated 8.1.2
9
  */
10
  class ICWP_WPSF_FeatureHandler_Statistics extends ICWP_WPSF_FeatureHandler_BaseWpsf {
11
 
5
 
6
  /**
7
  * Class ICWP_WPSF_FeatureHandler_Statistics
8
+ * @deprecated 8.4
9
  */
10
  class ICWP_WPSF_FeatureHandler_Statistics extends ICWP_WPSF_FeatureHandler_BaseWpsf {
11
 
src/features/traffic.php CHANGED
@@ -167,17 +167,9 @@ class ICWP_WPSF_FeatureHandler_Traffic extends ICWP_WPSF_FeatureHandler_BaseWpsf
167
 
168
  /**
169
  * @return Shield\Databases\Traffic\Handler
170
- * @deprecated 8.1.2
171
  */
172
  protected function loadDbHandler() {
173
  return new Shield\Databases\Traffic\Handler();
174
  }
175
-
176
- /**
177
- * @return int
178
- * @deprecated 8.1
179
- */
180
- public function getMaxEntries() {
181
- return (int)$this->getOpt( 'max_entries' );
182
- }
183
  }
167
 
168
  /**
169
  * @return Shield\Databases\Traffic\Handler
170
+ * @deprecated 8.4
171
  */
172
  protected function loadDbHandler() {
173
  return new Shield\Databases\Traffic\Handler();
174
  }
 
 
 
 
 
 
 
 
175
  }
src/features/user_management.php CHANGED
@@ -397,13 +397,4 @@ class ICWP_WPSF_FeatureHandler_UserManagement extends \ICWP_WPSF_FeatureHandler_
397
  protected function getNamespaceBase() {
398
  return 'UserManagement';
399
  }
400
-
401
- /**
402
- * Should have no default email. If no email is set, no notification is sent.
403
- * @return string
404
- * @deprecated 8.1
405
- */
406
- public function getAdminLoginNotificationEmail() {
407
- return $this->getOpt( 'enable_admin_login_email_notification', '' );
408
- }
409
  }
397
  protected function getNamespaceBase() {
398
  return 'UserManagement';
399
  }
 
 
 
 
 
 
 
 
 
400
  }
src/lib/src/AuditTrail/Auditor.php DELETED
@@ -1,14 +0,0 @@
1
- <?php
2
-
3
- namespace FernleafSystems\Wordpress\Plugin\Shield\AuditTrail;
4
-
5
- use FernleafSystems\Wordpress\Plugin\Shield\Databases\AuditTrail\EntryVO;
6
- use FernleafSystems\Wordpress\Services\Services;
7
-
8
- /**
9
- * Trait Auditor
10
- * @package FernleafSystems\Wordpress\Plugin\Shield\AuditTrail
11
- * @deprecated 8.1
12
- */
13
- trait Auditor {
14
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/lib/src/Controller/Controller.php CHANGED
@@ -564,6 +564,13 @@ class Controller extends Shield\Deprecated\Foundation {
564
  return $this;
565
  }
566
 
 
 
 
 
 
 
 
567
  /**
568
  * Displaying all views now goes through this central function and we work out
569
  * what to display based on the name of current hook/filter being processed.
@@ -1798,6 +1805,13 @@ class Controller extends Shield\Deprecated\Foundation {
1798
  return $this->getModule( 'audit_trail' );
1799
  }
1800
 
 
 
 
 
 
 
 
1801
  /**
1802
  * @return \ICWP_WPSF_FeatureHandler_Events
1803
  */
564
  return $this;
565
  }
566
 
567
+ /**
568
+ * @return array
569
+ */
570
+ public function getAllEvents() {
571
+ return apply_filters( $this->prefix( 'get_all_events' ), [] );
572
+ }
573
+
574
  /**
575
  * Displaying all views now goes through this central function and we work out
576
  * what to display based on the name of current hook/filter being processed.
1805
  return $this->getModule( 'audit_trail' );
1806
  }
1807
 
1808
+ /**
1809
+ * @return \ICWP_WPSF_FeatureHandler_CommentsFilter
1810
+ */
1811
+ public function getModule_Comments() {
1812
+ return $this->getModule( 'comments_filter' );
1813
+ }
1814
+
1815
  /**
1816
  * @return \ICWP_WPSF_FeatureHandler_Events
1817
  */
src/lib/src/Databases/AuditTrail/Handler.php CHANGED
@@ -9,10 +9,8 @@ use FernleafSystems\Wordpress\Services\Services;
9
  class Handler extends Base\Handler {
10
 
11
  public function autoCleanDb() {
12
- /** @var \ICWP_WPSF_FeatureHandler_AuditTrail $oMod */
13
- $oMod = $this->getMod();
14
  /** @var Options $oOpts */
15
- $oOpts = $oMod->getOptions();
16
  $this->cleanDb( $oOpts->getAutoCleanDays() );
17
  $this->trimDb( $oOpts->getMaxEntries() );
18
  }
9
  class Handler extends Base\Handler {
10
 
11
  public function autoCleanDb() {
 
 
12
  /** @var Options $oOpts */
13
+ $oOpts = $this->getMod()->getOptions();
14
  $this->cleanDb( $oOpts->getAutoCleanDays() );
15
  $this->trimDb( $oOpts->getMaxEntries() );
16
  }
src/lib/src/Databases/AuditTrail/Select.php CHANGED
@@ -83,16 +83,4 @@ class Select extends Base\Select {
83
  public function filterByUsername( $sUsername ) {
84
  return $this->addWhereEquals( 'wp_username', trim( $sUsername ) );
85
  }
86
-
87
- /**
88
- * @param string $sContext
89
- * @return $this
90
- * @deprecated 8.1
91
- */
92
- public function filterByContext( $sContext ) {
93
- if ( !empty( $sContext ) && strtolower( $sContext ) != 'all' ) {
94
- $this->addWhereEquals( 'context', $sContext );
95
- }
96
- return $this;
97
- }
98
  }
83
  public function filterByUsername( $sUsername ) {
84
  return $this->addWhereEquals( 'wp_username', trim( $sUsername ) );
85
  }
 
 
 
 
 
 
 
 
 
 
 
 
86
  }
src/lib/src/Databases/Base/BaseQuery.php CHANGED
@@ -104,6 +104,17 @@ abstract class BaseQuery {
104
  return $this;
105
  }
106
 
 
 
 
 
 
 
 
 
 
 
 
107
  /**
108
  * @param int $nNewerThanTimeStamp
109
  * @param string $sColumn
@@ -216,8 +227,28 @@ abstract class BaseQuery {
216
  */
217
  public function filterByBoundary_Day( $nTs ) {
218
  $oCbn = ( new Carbon() )->setTimestamp( $nTs );
219
- return $this->filterByCreatedAt( $oCbn->startOfDay()->timestamp, '>' )
220
- ->filterByCreatedAt( $oCbn->endOfDay()->timestamp, '<' );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
221
  }
222
 
223
  /**
@@ -226,8 +257,18 @@ abstract class BaseQuery {
226
  */
227
  public function filterByBoundary_Week( $nTs ) {
228
  $oCbn = ( new Carbon() )->setTimestamp( $nTs );
229
- return $this->filterByCreatedAt( $oCbn->startOfWeek()->timestamp, '>' )
230
- ->filterByCreatedAt( $oCbn->endOfWeek()->timestamp, '<' );
 
 
 
 
 
 
 
 
 
 
231
  }
232
 
233
  /**
104
  return $this;
105
  }
106
 
107
+ /**
108
+ * @param string $sColumn
109
+ * @param string $sLike
110
+ * @param string $sLeft
111
+ * @param string $sRight
112
+ * @return $this
113
+ */
114
+ public function addWhereLike( $sColumn, $sLike, $sLeft = '%', $sRight = '%' ) {
115
+ return $this->addWhere( $sColumn, $sLeft.$sLike.$sRight, 'LIKE' );
116
+ }
117
+
118
  /**
119
  * @param int $nNewerThanTimeStamp
120
  * @param string $sColumn
227
  */
228
  public function filterByBoundary_Day( $nTs ) {
229
  $oCbn = ( new Carbon() )->setTimestamp( $nTs );
230
+ return $this->filterByCreatedAt( $oCbn->endOfDay()->timestamp, '<=' )
231
+ ->filterByCreatedAt( $oCbn->startOfDay()->timestamp, '>=' );
232
+ }
233
+
234
+ /**
235
+ * @param int $nTs
236
+ * @return $this
237
+ */
238
+ public function filterByBoundary_Hour( $nTs ) {
239
+ $oCbn = ( new Carbon() )->setTimestamp( $nTs );
240
+ return $this->filterByCreatedAt( $oCbn->endOfHour()->timestamp, '<=' )
241
+ ->filterByCreatedAt( $oCbn->startOfHour()->timestamp, '>=' );
242
+ }
243
+
244
+ /**
245
+ * @param int $nTs
246
+ * @return $this
247
+ */
248
+ public function filterByBoundary_Month( $nTs ) {
249
+ $oCbn = ( new Carbon() )->setTimestamp( $nTs );
250
+ return $this->filterByCreatedAt( $oCbn->startOfMonth()->timestamp, '>=' )
251
+ ->filterByCreatedAt( $oCbn->endOfMonth()->timestamp, '<=' );
252
  }
253
 
254
  /**
257
  */
258
  public function filterByBoundary_Week( $nTs ) {
259
  $oCbn = ( new Carbon() )->setTimestamp( $nTs );
260
+ return $this->filterByCreatedAt( $oCbn->endOfWeek()->timestamp, '<=' )
261
+ ->filterByCreatedAt( $oCbn->startOfWeek()->timestamp, '>=' );
262
+ }
263
+
264
+ /**
265
+ * @param int $nTs
266
+ * @return $this
267
+ */
268
+ public function filterByBoundary_Year( $nTs ) {
269
+ $oCbn = ( new Carbon() )->setTimestamp( $nTs );
270
+ return $this->filterByCreatedAt( $oCbn->startOfYear()->timestamp, '>=' )
271
+ ->filterByCreatedAt( $oCbn->endOfYear()->timestamp, '<=' );
272
  }
273
 
274
  /**
src/lib/src/Databases/Events/Common.php ADDED
@@ -0,0 +1,26 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace FernleafSystems\Wordpress\Plugin\Shield\Databases\Events;
4
+
5
+ /**
6
+ * Trait Filters
7
+ * @package FernleafSystems\Wordpress\Plugin\Shield\Databases\Events
8
+ */
9
+ trait Common {
10
+
11
+ /**
12
+ * @param string $sEvent
13
+ * @return $this
14
+ */
15
+ public function filterByEvent( $sEvent ) {
16
+ return $this->filterByEvents( [ $sEvent ] );
17
+ }
18
+
19
+ /**
20
+ * @param string[] $aEvents
21
+ * @return $this
22
+ */
23
+ public function filterByEvents( $aEvents ) {
24
+ return $this->addWhereIn( 'event', $aEvents );
25
+ }
26
+ }
src/lib/src/Databases/Events/Delete.php CHANGED
@@ -6,4 +6,5 @@ use FernleafSystems\Wordpress\Plugin\Shield\Databases\Base;
6
 
7
  class Delete extends Base\Delete {
8
 
 
9
  }
6
 
7
  class Delete extends Base\Delete {
8
 
9
+ use Common;
10
  }
src/lib/src/Databases/Events/Select.php CHANGED
@@ -2,11 +2,12 @@
2
 
3
  namespace FernleafSystems\Wordpress\Plugin\Shield\Databases\Events;
4
 
5
- use Carbon\Carbon;
6
  use FernleafSystems\Wordpress\Plugin\Shield\Databases\Base;
7
 
8
  class Select extends Base\Select {
9
 
 
 
10
  /**
11
  * @param string $sEvent
12
  * @return int
@@ -25,6 +26,16 @@ class Select extends Base\Select {
25
  ->sum();
26
  }
27
 
 
 
 
 
 
 
 
 
 
 
28
  /**
29
  * @return int[]
30
  */
@@ -52,6 +63,17 @@ class Select extends Base\Select {
52
  ->first();
53
  }
54
 
 
 
 
 
 
 
 
 
 
 
 
55
  /**
56
  * @return string[]
57
  */
@@ -100,20 +122,4 @@ class Select extends Base\Select {
100
  public function filterByCountGreaterThan( $nGreaterThan ) {
101
  return $this->addWhere( 'count', (int)$nGreaterThan, '>' );
102
  }
103
-
104
- /**
105
- * @param string $sEvent
106
- * @return $this
107
- */
108
- public function filterByEvent( $sEvent ) {
109
- return $this->filterByEvents( [ $sEvent ] );
110
- }
111
-
112
- /**
113
- * @param string[] $aEvents
114
- * @return $this
115
- */
116
- public function filterByEvents( $aEvents ) {
117
- return $this->addWhereIn( 'event', $aEvents );
118
- }
119
  }
2
 
3
  namespace FernleafSystems\Wordpress\Plugin\Shield\Databases\Events;
4
 
 
5
  use FernleafSystems\Wordpress\Plugin\Shield\Databases\Base;
6
 
7
  class Select extends Base\Select {
8
 
9
+ use Common;
10
+
11
  /**
12
  * @param string $sEvent
13
  * @return int
26
  ->sum();
27
  }
28
 
29
+ /**
30
+ * @param string $sEvent
31
+ * @return int
32
+ */
33
+ public function sumEventsLike( $sEvent ) {
34
+ return (int)$this->addWhereLike( 'event', $sEvent )
35
+ ->setColumnsToSelect( [ 'count' ] )
36
+ ->sum();
37
+ }
38
+
39
  /**
40
  * @return int[]
41
  */
63
  ->first();
64
  }
65
 
66
+ /**
67
+ * @param string $sEvent
68
+ * @return EntryVO|null
69
+ */
70
+ public function getOldestForEvent( $sEvent ) {
71
+ return $this->filterByEvent( $sEvent )
72
+ ->setOrderBy( 'created_at', 'ASC' )
73
+ ->setResultsAsVo( true )
74
+ ->first();
75
+ }
76
+
77
  /**
78
  * @return string[]
79
  */
122
  public function filterByCountGreaterThan( $nGreaterThan ) {
123
  return $this->addWhere( 'count', (int)$nGreaterThan, '>' );
124
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
125
  }
src/lib/src/Databases/IPs/CommonFilters.php CHANGED
@@ -12,6 +12,14 @@ trait CommonFilters {
12
  return $this->addWhereEquals( 'ip', $sIp );
13
  }
14
 
 
 
 
 
 
 
 
 
15
  /**
16
  * @param string $nLastAccessAfter
17
  * @return $this
12
  return $this->addWhereEquals( 'ip', $sIp );
13
  }
14
 
15
+ /**
16
+ * @param bool $bIsRange
17
+ * @return $this
18
+ */
19
+ public function filterByIsRange( $bIsRange ) {
20
+ return $this->addWhereEquals( 'is_range', $bIsRange ? 1 : 0 );
21
+ }
22
+
23
  /**
24
  * @param string $nLastAccessAfter
25
  * @return $this
src/lib/src/Deprecated/Foundation.php CHANGED
@@ -21,6 +21,7 @@ class Foundation {
21
 
22
  /**
23
  * @return \ICWP_WPSF_DataProcessor
 
24
  */
25
  static public function loadDP() {
26
  $sKey = 'icwp-data';
@@ -30,52 +31,6 @@ class Foundation {
30
  return self::getService( $sKey );
31
  }
32
 
33
- /**
34
- * @return \ICWP_WPSF_WpFilesystem
35
- */
36
- static public function loadFS() {
37
- $sKey = 'icwp-wpfilesystem';
38
- if ( !self::isServiceReady( $sKey ) ) {
39
- self::setService( $sKey, \ICWP_WPSF_WpFilesystem::GetInstance() );
40
- }
41
- return self::getService( $sKey );
42
- }
43
-
44
- /**
45
- * @return \ICWP_WPSF_WpFunctions
46
- */
47
- static public function loadWp() {
48
- $sKey = 'icwp-wpfunctions';
49
- if ( !self::isServiceReady( $sKey ) ) {
50
- self::setService( $sKey, \ICWP_WPSF_WpFunctions::GetInstance() );
51
- }
52
- return self::getService( $sKey );
53
- }
54
-
55
- /**
56
- * @return \ICWP_WPSF_WpFunctions_Plugins
57
- * @deprecated
58
- */
59
- public function loadWpPlugins() {
60
- $sKey = 'icwp-wpfunctions-plugins';
61
- if ( !self::isServiceReady( $sKey ) ) {
62
- self::setService( $sKey, \ICWP_WPSF_WpFunctions_Plugins::GetInstance() );
63
- }
64
- return self::getService( $sKey );
65
- }
66
-
67
- /**
68
- * @return \ICWP_WPSF_WpFunctions_Themes
69
- * @deprecated
70
- */
71
- public function loadWpThemes() {
72
- $sKey = 'icwp-wpfunctions-themes';
73
- if ( !self::isServiceReady( $sKey ) ) {
74
- self::setService( $sKey, \ICWP_WPSF_WpFunctions_Themes::GetInstance() );
75
- }
76
- return self::getService( $sKey );
77
- }
78
-
79
  /**
80
  * @return \ICWP_WPSF_WpCron
81
  * @deprecated
@@ -88,40 +43,6 @@ class Foundation {
88
  return self::getService( $sKey );
89
  }
90
 
91
- /**
92
- * @return \ICWP_WPSF_WpUpgrades
93
- * @deprecated
94
- */
95
- static public function loadWpUpgrades() {
96
- $sKey = 'icwp-wpupgrades';
97
- if ( !self::isServiceReady( $sKey ) ) {
98
- self::setService( $sKey, \ICWP_WPSF_WpUpgrades::GetInstance() );
99
- }
100
- return self::getService( $sKey );
101
- }
102
-
103
- /**
104
- * @return \ICWP_WPSF_WpDb
105
- */
106
- static public function loadDbProcessor() {
107
- $sKey = 'icwp-wpdb';
108
- if ( !self::isServiceReady( $sKey ) ) {
109
- self::setService( $sKey, \ICWP_WPSF_WpDb::GetInstance() );
110
- }
111
- return self::getService( $sKey );
112
- }
113
-
114
- /**
115
- * @return \ICWP_WPSF_Request
116
- */
117
- public function loadRequest() {
118
- $sKey = 'icwp-request';
119
- if ( !self::isServiceReady( $sKey ) ) {
120
- self::setService( $sKey, \ICWP_WPSF_Request::GetInstance() );
121
- }
122
- return self::getService( $sKey );
123
- }
124
-
125
  /**
126
  * @return \ICWP_WPSF_ServiceProviders
127
  */
@@ -133,84 +54,6 @@ class Foundation {
133
  return self::getService( $sKey );
134
  }
135
 
136
- /**
137
- * @return \ICWP_WPSF_WpIncludes
138
- * @deprecated
139
- */
140
- static public function loadWpIncludes() {
141
- $sKey = 'icwp-wpincludes';
142
- if ( !self::isServiceReady( $sKey ) ) {
143
- self::setService( $sKey, \ICWP_WPSF_WpIncludes::GetInstance() );
144
- }
145
- return self::getService( $sKey );
146
- }
147
-
148
- /**
149
- * @param string $sTemplatePath
150
- * @return \ICWP_WPSF_Render
151
- * @deprecated
152
- */
153
- static public function loadRenderer( $sTemplatePath = '' ) {
154
- $sKey = 'icwp-render';
155
- if ( !self::isServiceReady( $sKey ) ) {
156
- self::setService( $sKey, \ICWP_WPSF_Render::GetInstance() );
157
- }
158
-
159
- /** @var \ICWP_WPSF_Render $oR */
160
- $oR = self::getService( $sKey );
161
- if ( !empty( $sTemplatePath ) ) {
162
- $oR->setTemplateRoot( $sTemplatePath );
163
- }
164
- return ( clone $oR );
165
- }
166
-
167
- /**
168
- * @return \ICWP_WPSF_WpAdminNotices
169
- * @deprecated
170
- */
171
- static public function loadWpNotices() {
172
- $sKey = 'wp-admin-notices';
173
- if ( !self::isServiceReady( $sKey ) ) {
174
- self::setService( $sKey, \ICWP_WPSF_WpAdminNotices::GetInstance() );
175
- }
176
- return self::getService( $sKey );
177
- }
178
-
179
- /**
180
- * @return \ICWP_WPSF_WpUsers
181
- * @deprecated
182
- */
183
- static public function loadWpUsers() {
184
- $sKey = 'wp-users';
185
- if ( !self::isServiceReady( $sKey ) ) {
186
- self::setService( $sKey, \ICWP_WPSF_WpUsers::GetInstance() );
187
- }
188
- return self::getService( $sKey );
189
- }
190
-
191
- /**
192
- * @return \ICWP_WPSF_WpComments
193
- * @deprecated
194
- */
195
- static public function loadWpComments() {
196
- $sKey = 'wp-comments';
197
- if ( !self::isServiceReady( $sKey ) ) {
198
- self::setService( $sKey, \ICWP_WPSF_WpComments::GetInstance() );
199
- }
200
- return self::getService( $sKey );
201
- }
202
-
203
- /**
204
- * @return \ICWP_WPSF_Edd
205
- */
206
- static public function loadEdd() {
207
- $sKey = 'icwp-edd';
208
- if ( !self::isServiceReady( $sKey ) ) {
209
- self::setService( $sKey, \ICWP_WPSF_Edd::GetInstance() );
210
- }
211
- return self::getService( $sKey );
212
- }
213
-
214
  /**
215
  * @return array
216
  */
@@ -248,12 +91,4 @@ class Foundation {
248
  $aDic[ $sServiceKey ] = $oService;
249
  self::$aDic = $aDic;
250
  }
251
-
252
- /**
253
- * @return \ICWP_WPSF_WpAdminNotices
254
- * @deprecated
255
- */
256
- static public function loadAdminNoticesProcessor() {
257
- return self::loadWpNotices();
258
- }
259
  }
21
 
22
  /**
23
  * @return \ICWP_WPSF_DataProcessor
24
+ * @deprecated 8.4.0
25
  */
26
  static public function loadDP() {
27
  $sKey = 'icwp-data';
31
  return self::getService( $sKey );
32
  }
33
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
34
  /**
35
  * @return \ICWP_WPSF_WpCron
36
  * @deprecated
43
  return self::getService( $sKey );
44
  }
45
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
46
  /**
47
  * @return \ICWP_WPSF_ServiceProviders
48
  */
54
  return self::getService( $sKey );
55
  }
56
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
57
  /**
58
  * @return array
59
  */
91
  $aDic[ $sServiceKey ] = $oService;
92
  self::$aDic = $aDic;
93
  }
 
 
 
 
 
 
 
 
94
  }
src/lib/src/Modules/Autoupdates/AjaxHandler.php CHANGED
@@ -29,16 +29,16 @@ class AjaxHandler extends Shield\Modules\Base\AjaxHandlerShield {
29
  * @return array
30
  */
31
  public function ajaxExec_TogglePluginAutoupdate() {
32
- /** @var \ICWP_WPSF_FeatureHandler_Autoupdates $oMod */
33
- $oMod = $this->getMod();
34
  $bSuccess = false;
35
  $sMessage = __( 'You do not have permissions to perform this action.', 'wp-simple-firewall' );
36
 
37
- if ( $oMod->isAutoupdateIndividualPlugins() && $this->getCon()->isPluginAdmin() ) {
38
  $oWpPlugins = Services::WpPlugins();
39
  $sFile = Services::Request()->post( 'pluginfile' );
40
  if ( $oWpPlugins->isInstalled( $sFile ) ) {
41
- $oMod->setPluginToAutoUpdate( $sFile );
42
 
43
  $sMessage = sprintf( __( 'Plugin "%s" will %s.', 'wp-simple-firewall' ),
44
  $oWpPlugins->getPluginAsVo( $sFile )->Name,
29
  * @return array
30
  */
31
  public function ajaxExec_TogglePluginAutoupdate() {
32
+ /** @var Options $oOpts */
33
+ $oOpts = $this->getOptions();
34
  $bSuccess = false;
35
  $sMessage = __( 'You do not have permissions to perform this action.', 'wp-simple-firewall' );
36
 
37
+ if ( $oOpts->isAutoupdateIndividualPlugins() && $this->getCon()->isPluginAdmin() ) {
38
  $oWpPlugins = Services::WpPlugins();
39
  $sFile = Services::Request()->post( 'pluginfile' );
40
  if ( $oWpPlugins->isInstalled( $sFile ) ) {
41
+ $oOpts->setPluginToAutoUpdate( $sFile );
42
 
43
  $sMessage = sprintf( __( 'Plugin "%s" will %s.', 'wp-simple-firewall' ),
44
  $oWpPlugins->getPluginAsVo( $sFile )->Name,
src/lib/src/Modules/Autoupdates/Options.php CHANGED
@@ -3,6 +3,139 @@
3
  namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Autoupdates;
4
 
5
  use FernleafSystems\Wordpress\Plugin\Shield\Modules\Base;
 
6
 
7
  class Options extends Base\ShieldOptions {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
8
  }
3
  namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Autoupdates;
4
 
5
  use FernleafSystems\Wordpress\Plugin\Shield\Modules\Base;
6
+ use FernleafSystems\Wordpress\Services\Services;
7
 
8
  class Options extends Base\ShieldOptions {
9
+
10
+ /**
11
+ * @return string[]
12
+ */
13
+ public function getAutoupdatePlugins() {
14
+ $aSelected = [];
15
+ if ( $this->isAutoupdateIndividualPlugins() ) {
16
+ $aSelected = $this->getOpt( 'selected_plugins', [] );
17
+ if ( !is_array( $aSelected ) ) {
18
+ $aSelected = [];
19
+ }
20
+ }
21
+ return $aSelected;
22
+ }
23
+
24
+ /**
25
+ * @return array
26
+ */
27
+ public function getDelayTracking() {
28
+ $aTracking = $this->getOpt( 'delay_tracking', [] );
29
+ if ( !is_array( $aTracking ) ) {
30
+ $aTracking = [];
31
+ }
32
+ $aTracking = Services::DataManipulation()->mergeArraysRecursive(
33
+ [
34
+ 'core' => [],
35
+ 'plugins' => [],
36
+ 'themes' => [],
37
+ ],
38
+ $aTracking
39
+ );
40
+ $this->setOpt( 'delay_tracking', $aTracking );
41
+
42
+ return $aTracking;
43
+ }
44
+
45
+ /**
46
+ * @return int
47
+ */
48
+ public function getDelayUpdatesPeriod() {
49
+ return $this->isPremium() ? $this->getOpt( 'update_delay', 0 )*DAY_IN_SECONDS : 0;
50
+ }
51
+
52
+ /**
53
+ * @return string
54
+ */
55
+ public function getSelfAutoUpdateOpt() {
56
+ return $this->getOpt( 'autoupdate_plugin_self' );
57
+ }
58
+
59
+ /**
60
+ * @return bool
61
+ */
62
+ public function isAutoUpdateCoreMajor() {
63
+ return $this->isOpt( 'autoupdate_core', 'core_major' );
64
+ }
65
+
66
+ /**
67
+ * @return bool
68
+ */
69
+ public function isAutoUpdateCoreMinor() {
70
+ return !$this->isOpt( 'autoupdate_core', 'core_never' );
71
+ }
72
+
73
+ /**
74
+ * @return bool
75
+ */
76
+ public function isAutoupdateAllPlugins() {
77
+ return $this->isOpt( 'enable_autoupdate_plugins', 'Y' );
78
+ }
79
+
80
+ /**
81
+ * @return bool
82
+ */
83
+ public function isAutoupdateIndividualPlugins() {
84
+ return $this->isPremium() && $this->isOpt( 'enable_individual_autoupdate_plugins', 'Y' );
85
+ }
86
+
87
+ /**
88
+ * @return bool
89
+ */
90
+ public function isDisableAllAutoUpdates() {
91
+ return $this->isOpt( 'enable_autoupdate_disable_all', 'Y' );
92
+ }
93
+
94
+ /**
95
+ * @return bool
96
+ */
97
+ public function isDelayUpdates() {
98
+ return $this->getDelayUpdatesPeriod() > 0;
99
+ }
100
+
101
+ /**
102
+ * @param string $sPluginFile
103
+ * @return bool
104
+ */
105
+ public function isPluginSetToAutoupdate( $sPluginFile ) {
106
+ return in_array( $sPluginFile, $this->getAutoupdatePlugins() );
107
+ }
108
+
109
+ /**
110
+ * @return bool
111
+ */
112
+ public function isSendAutoupdatesNotificationEmail() {
113
+ return $this->isOpt( 'enable_upgrade_notification_email', 'Y' );
114
+ }
115
+
116
+ /**
117
+ * @param array $aTrackingInfo
118
+ * @return $this
119
+ */
120
+ public function setDelayTracking( $aTrackingInfo ) {
121
+ return $this->setOpt( 'delay_tracking', $aTrackingInfo );
122
+ }
123
+
124
+ /**
125
+ * @param string $sPluginFile
126
+ * @return $this
127
+ */
128
+ public function setPluginToAutoUpdate( $sPluginFile ) {
129
+ $aPlugins = $this->getAutoupdatePlugins();
130
+ $nKey = array_search( $sPluginFile, $aPlugins );
131
+
132
+ if ( $nKey === false ) {
133
+ $aPlugins[] = $sPluginFile;
134
+ }
135
+ else {
136
+ unset( $aPlugins[ $nKey ] );
137
+ }
138
+
139
+ return $this->setOpt( 'selected_plugins', $aPlugins );
140
+ }
141
  }
src/lib/src/Modules/Base/AdminNotices.php CHANGED
@@ -13,7 +13,7 @@ class AdminNotices {
13
  /**
14
  * @var
15
  */
16
- static protected $nCount = 0;
17
 
18
  public function run() {
19
  $oMod = $this->getMod();
@@ -126,28 +126,28 @@ class AdminNotices {
126
  if ( $oNtc->plugin_page_only && !$oCon->isModulePage() ) {
127
  $oNtc->non_display_reason = 'plugin_page_only';
128
  }
129
- else if ( $oNtc->type == 'promo' && !$this->getOptions()->isShowPromoAdminNotices() ) {
130
  $oNtc->non_display_reason = 'promo_hidden';
131
  }
132
- else if ( $oNtc->valid_admin && !$oCon->isValidAdminArea() ) {
133
  $oNtc->non_display_reason = 'not_admin_area';
134
  }
135
- else if ( $oNtc->plugin_admin == 'yes' && !$oCon->isPluginAdmin() ) {
136
  $oNtc->non_display_reason = 'not_plugin_admin';
137
  }
138
- else if ( $oNtc->plugin_admin == 'no' && $oCon->isPluginAdmin() ) {
139
  $oNtc->non_display_reason = 'is_plugin_admin';
140
  }
141
- else if ( $oNtc->min_install_days > 0 && $oNtc->min_install_days > $oOpts->getInstallationDays() ) {
142
  $oNtc->non_display_reason = 'min_install_days';
143
  }
144
- else if ( static::$nCount > 0 && $oNtc->type !== 'error' ) {
145
  $oNtc->non_display_reason = 'max_nonerror_count';
146
  }
147
- else if ( $oNtc->can_dismiss && $this->isNoticeDismissed( $oNtc ) ) {
148
  $oNtc->non_display_reason = 'dismissed';
149
  }
150
- else if ( !$this->isDisplayNeeded( $oNtc ) ) {
151
  $oNtc->non_display_reason = 'not_needed';
152
  }
153
  else {
13
  /**
14
  * @var
15
  */
16
+ protected static $nCount = 0;
17
 
18
  public function run() {
19
  $oMod = $this->getMod();
126
  if ( $oNtc->plugin_page_only && !$oCon->isModulePage() ) {
127
  $oNtc->non_display_reason = 'plugin_page_only';
128
  }
129
+ elseif ( $oNtc->type == 'promo' && !$this->getOptions()->isShowPromoAdminNotices() ) {
130
  $oNtc->non_display_reason = 'promo_hidden';
131
  }
132
+ elseif ( $oNtc->valid_admin && !$oCon->isValidAdminArea() ) {
133
  $oNtc->non_display_reason = 'not_admin_area';
134
  }
135
+ elseif ( $oNtc->plugin_admin == 'yes' && !$oCon->isPluginAdmin() ) {
136
  $oNtc->non_display_reason = 'not_plugin_admin';
137
  }
138
+ elseif ( $oNtc->plugin_admin == 'no' && $oCon->isPluginAdmin() ) {
139
  $oNtc->non_display_reason = 'is_plugin_admin';
140
  }
141
+ elseif ( $oNtc->min_install_days > 0 && $oNtc->min_install_days > $oOpts->getInstallationDays() ) {
142
  $oNtc->non_display_reason = 'min_install_days';
143
  }
144
+ elseif ( static::$nCount > 0 && $oNtc->type !== 'error' ) {
145
  $oNtc->non_display_reason = 'max_nonerror_count';
146
  }
147
+ elseif ( $oNtc->can_dismiss && $this->isNoticeDismissed( $oNtc ) ) {
148
  $oNtc->non_display_reason = 'dismissed';
149
  }
150
+ elseif ( !$this->isDisplayNeeded( $oNtc ) ) {
151
  $oNtc->non_display_reason = 'not_needed';
152
  }
153
  else {
src/lib/src/Modules/Base/BaseModCon.php CHANGED
@@ -133,6 +133,9 @@ class BaseModCon extends Deprecated\Foundation {
133
  add_filter( $this->prefix( 'is_event_supported' ), function ( $bSupported, $sEventTag ) {
134
  return $bSupported || $this->isSupportedEvent( $sEventTag );
135
  }, 10, 2 );
 
 
 
136
 
137
  add_action( 'admin_enqueue_scripts', [ $this, 'onWpEnqueueAdminJs' ], 100 );
138
 
@@ -481,14 +484,6 @@ class BaseModCon extends Deprecated\Foundation {
481
  );
482
  }
483
 
484
- /**
485
- * @return \ICWP_WPSF_OptionsVO
486
- * @deprecated 8.1
487
- */
488
- public function getOptionsVo() {
489
- return $this->getOptions();
490
- }
491
-
492
  /**
493
  * @return bool
494
  */
@@ -1513,8 +1508,8 @@ class BaseModCon extends Deprecated\Foundation {
1513
  'has_wizard' => $this->hasWizard(),
1514
  ],
1515
  'hrefs' => [
1516
- 'go_pro' => 'https://icwp.io/shieldgoprofeature',
1517
- 'goprofooter' => 'https://icwp.io/goprofooter',
1518
  'wizard_link' => $this->getUrl_WizardLanding(),
1519
  'wizard_landing' => $this->getUrl_WizardLanding()
1520
  ],
@@ -1904,51 +1899,21 @@ class BaseModCon extends Deprecated\Foundation {
1904
  return !empty( $sId );
1905
  }
1906
 
1907
- /**
1908
- * @param string $sOpt
1909
- * @param int $nAt
1910
- * @return $this
1911
- */
1912
- protected function setOptAt( $sOpt, $nAt = null ) {
1913
- $nAt = is_null( $nAt ) ? Services::Request()->ts() : max( 0, (int)$nAt );
1914
- return $this->setOpt( $sOpt, $nAt );
1915
- }
1916
-
1917
  /**
1918
  * @return null|Shield\Modules\Base\ShieldOptions|mixed
1919
  */
1920
  public function getOptions() {
1921
  if ( !isset( $this->oOpts ) ) {
1922
-
1923
- $oOpts = $this->loadOptions()->setMod( $this );;
1924
-
1925
  $oCon = $this->getCon();
1926
- $this->oOpts = $oOpts->setPathToConfig( $oCon->getPath_ConfigFile( $this->getSlug() ) )
1927
- ->setRebuildFromFile( $oCon->getIsRebuildOptionsFromFile() )
1928
- ->setOptionsStorageKey( $this->getOptionsStorageKey() )
1929
- ->setIfLoadOptionsFromStorage( !$oCon->getIsResetPlugin() );
 
1930
  }
1931
  return $this->oOpts;
1932
  }
1933
 
1934
- /**
1935
- * @return null|Shield\Databases\Base\Handler|mixed
1936
- */
1937
- public function getDbHandler() {
1938
- if ( !isset( $this->oDbh ) ) {
1939
- $this->oDbh = $this->loadDbHandler();
1940
- if ( $this->oDbh instanceof Shield\Databases\Base\Handler ) {
1941
- try {
1942
- $this->oDbh->setMod( $this )
1943
- ->tableInit();
1944
- }
1945
- catch ( \Exception$oE ) {
1946
- }
1947
- }
1948
- }
1949
- return $this->oDbh;
1950
- }
1951
-
1952
  /**
1953
  * @return string
1954
  */
133
  add_filter( $this->prefix( 'is_event_supported' ), function ( $bSupported, $sEventTag ) {
134
  return $bSupported || $this->isSupportedEvent( $sEventTag );
135
  }, 10, 2 );
136
+ add_filter( $this->prefix( 'get_all_events' ), function ( $aEvents ) {
137
+ return array_merge( $aEvents, $this->getEvents() );
138
+ } );
139
 
140
  add_action( 'admin_enqueue_scripts', [ $this, 'onWpEnqueueAdminJs' ], 100 );
141
 
484
  );
485
  }
486
 
 
 
 
 
 
 
 
 
487
  /**
488
  * @return bool
489
  */
1508
  'has_wizard' => $this->hasWizard(),
1509
  ],
1510
  'hrefs' => [
1511
+ 'go_pro' => 'https://shsec.io/shieldgoprofeature',
1512
+ 'goprofooter' => 'https://shsec.io/goprofooter',
1513
  'wizard_link' => $this->getUrl_WizardLanding(),
1514
  'wizard_landing' => $this->getUrl_WizardLanding()
1515
  ],
1899
  return !empty( $sId );
1900
  }
1901
 
 
 
 
 
 
 
 
 
 
 
1902
  /**
1903
  * @return null|Shield\Modules\Base\ShieldOptions|mixed
1904
  */
1905
  public function getOptions() {
1906
  if ( !isset( $this->oOpts ) ) {
 
 
 
1907
  $oCon = $this->getCon();
1908
+ $this->oOpts = $this->loadOptions()->setMod( $this );
1909
+ $this->oOpts->setPathToConfig( $oCon->getPath_ConfigFile( $this->getSlug() ) )
1910
+ ->setRebuildFromFile( $oCon->getIsRebuildOptionsFromFile() )
1911
+ ->setOptionsStorageKey( $this->getOptionsStorageKey() )
1912
+ ->setIfLoadOptionsFromStorage( !$oCon->getIsResetPlugin() );
1913
  }
1914
  return $this->oOpts;
1915
  }
1916
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1917
  /**
1918
  * @return string
1919
  */
src/lib/src/Modules/Base/BaseProcessor.php CHANGED
@@ -43,7 +43,6 @@ class BaseProcessor extends Deprecated\Foundation {
43
  add_action( $oMod->prefix( 'daily_cron' ), [ $this, 'runDailyCron' ] );
44
  add_action( $oMod->prefix( 'hourly_cron' ), [ $this, 'runHourlyCron' ] );
45
  add_action( $oMod->prefix( 'deactivate_plugin' ), [ $this, 'deactivatePlugin' ] );
46
- add_action( $oMod->prefix( 'generate_admin_notices' ), [ $this, 'autoAddToAdminNotices' ] );
47
 
48
  /**
49
  * 2019-04-19:
@@ -74,11 +73,6 @@ class BaseProcessor extends Deprecated\Foundation {
74
  * @param \WP_User $oUser
75
  */
76
  public function onWpLogin( $sUsername, $oUser ) {
77
- /*
78
- if ( !$oUser instanceof WP_User ) {
79
- $oUser = $this->loadWpUsers()->getUserByUsername( $sUsername );
80
- }
81
- */
82
  }
83
 
84
  /**
@@ -215,26 +209,4 @@ class BaseProcessor extends Deprecated\Foundation {
215
  protected function prefix( $sSuffix = '', $sGlue = '-' ) {
216
  return $this->getMod()->prefix( $sSuffix, $sGlue );
217
  }
218
-
219
- /**
220
- * @return string
221
- * @deprecated
222
- */
223
- protected function ip() {
224
- return Services::IP()->getRequestIp();
225
- }
226
-
227
- /**
228
- * @return int
229
- * @deprecated 8
230
- */
231
- protected function time() {
232
- return Services::Request()->ts();
233
- }
234
-
235
- /**
236
- * @deprecated
237
- */
238
- public function autoAddToAdminNotices() {
239
- }
240
  }
43
  add_action( $oMod->prefix( 'daily_cron' ), [ $this, 'runDailyCron' ] );
44
  add_action( $oMod->prefix( 'hourly_cron' ), [ $this, 'runHourlyCron' ] );
45
  add_action( $oMod->prefix( 'deactivate_plugin' ), [ $this, 'deactivatePlugin' ] );
 
46
 
47
  /**
48
  * 2019-04-19:
73
  * @param \WP_User $oUser
74
  */
75
  public function onWpLogin( $sUsername, $oUser ) {
 
 
 
 
 
76
  }
77
 
78
  /**
209
  protected function prefix( $sSuffix = '', $sGlue = '-' ) {
210
  return $this->getMod()->prefix( $sSuffix, $sGlue );
211
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
212
  }
src/lib/src/Modules/Base/Options.php CHANGED
@@ -1025,7 +1025,7 @@ class Options {
1025
  /**
1026
  * @param string
1027
  * @return mixed|null
1028
- * @deprecated 8.1
1029
  */
1030
  public function getFeatureDefinition( $sDefinition ) {
1031
  return $this->getDef( $sDefinition );
1025
  /**
1026
  * @param string
1027
  * @return mixed|null
1028
+ * @deprecated 8.4
1029
  */
1030
  public function getFeatureDefinition( $sDefinition ) {
1031
  return $this->getDef( $sDefinition );
src/lib/src/Modules/Base/Strings.php CHANGED
@@ -66,7 +66,7 @@ class Strings {
66
  'pro_only_feature' => __( 'This is a pro-only feature', 'wp-simple-firewall' ),
67
  'go_pro' => __( 'Go Pro!', 'wp-simple-firewall' ),
68
  'go_pro_option' => sprintf( '<a href="%s" target="_blank">%s</a>',
69
- 'https://icwp.io/shieldgoprofeature', __( 'Please upgrade to Pro to control this option', 'wp-simple-firewall' ) ),
70
 
71
  'description' => __( 'Description', 'wp-simple-firewall' ),
72
  'loading' => __( 'Loading', 'wp-simple-firewall' ),
66
  'pro_only_feature' => __( 'This is a pro-only feature', 'wp-simple-firewall' ),
67
  'go_pro' => __( 'Go Pro!', 'wp-simple-firewall' ),
68
  'go_pro_option' => sprintf( '<a href="%s" target="_blank">%s</a>',
69
+ 'https://shsec.io/shieldgoprofeature', __( 'Please upgrade to Pro to control this option', 'wp-simple-firewall' ) ),
70
 
71
  'description' => __( 'Description', 'wp-simple-firewall' ),
72
  'loading' => __( 'Loading', 'wp-simple-firewall' ),
src/lib/src/Modules/CommentsFilter/AjaxHandler.php ADDED
@@ -0,0 +1,42 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\CommentsFilter;
4
+
5
+ use FernleafSystems\Wordpress\Plugin\Shield;
6
+ use FernleafSystems\Wordpress\Services\Services;
7
+
8
+ class AjaxHandler extends Shield\Modules\Base\AjaxHandlerShield {
9
+
10
+ /**
11
+ * @param string $sAction
12
+ * @return array
13
+ */
14
+ protected function processAjaxAction( $sAction ) {
15
+
16
+ switch ( $sAction ) {
17
+ case 'comment_token'.Services::IP()->getRequestIp():
18
+ $aResponse = $this->ajaxExec_GenCommentToken();
19
+ break;
20
+
21
+ default:
22
+ $aResponse = parent::processAjaxAction( $sAction );
23
+ }
24
+
25
+ return $aResponse;
26
+ }
27
+
28
+ /**
29
+ * @return array
30
+ */
31
+ private function ajaxExec_GenCommentToken() {
32
+ $oReq = Services::Request();
33
+ $sToken = ( new Shield\Modules\CommentsFilter\Token\Create() )
34
+ ->setMod( $this->getMod() )
35
+ ->run( $oReq->post( 'ts' ), $oReq->post( 'post_id' ) );
36
+
37
+ return [
38
+ 'success' => true,
39
+ 'token' => $sToken,
40
+ ];
41
+ }
42
+ }
src/lib/src/Modules/CommentsFilter/Scan/Human.php CHANGED
@@ -87,7 +87,7 @@ class Human {
87
  $oFs = Services::WpFs();
88
  $sBLFile = $oMod->getSpamBlacklistFile();
89
  if ( !$oFs->exists( $sBLFile ) ) {
90
- $sRawList = Services::HttpRequest()->getContent( $this->getMod()->getDef( 'url_spam_blacklist_terms' ) );
91
  $sList = '';
92
  if ( !empty( $sRawList ) ) {
93
  $sList = implode( "\n", array_map( 'base64_encode', array_filter( array_map( 'trim', explode( "\n", $sRawList ) ) ) ) );
87
  $oFs = Services::WpFs();
88
  $sBLFile = $oMod->getSpamBlacklistFile();
89
  if ( !$oFs->exists( $sBLFile ) ) {
90
+ $sRawList = Services::HttpRequest()->getContent( $this->getOptions()->getDef( 'url_spam_blacklist_terms' ) );
91
  $sList = '';
92
  if ( !empty( $sRawList ) ) {
93
  $sList = implode( "\n", array_map( 'base64_encode', array_filter( array_map( 'trim', explode( "\n", $sRawList ) ) ) ) );
src/lib/src/Modules/CommentsFilter/Token/Create.php ADDED
@@ -0,0 +1,45 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\CommentsFilter\Token;
4
+
5
+ use FernleafSystems\Wordpress\Plugin\Shield\Modules\ModConsumer;
6
+ use FernleafSystems\Wordpress\Services\Services;
7
+
8
+ class Create {
9
+
10
+ use ModConsumer;
11
+
12
+ /**
13
+ * @param int $nTs
14
+ * @param int $nPostId
15
+ * @return string
16
+ */
17
+ public function run( $nTs, $nPostId ) {
18
+ /** @var \ICWP_WPSF_FeatureHandler_CommentsFilter $oMod */
19
+ $oMod = $this->getMod();
20
+
21
+ $sToken = $this->generateNewToken( $nTs, $nPostId );
22
+
23
+ Services::WpGeneral()->setTransient(
24
+ $oMod->prefix( 'comtok-'.md5( sprintf( '%s-%s-%s', $nPostId, $nTs, Services::IP()->getRequestIp() ) ) ),
25
+ $sToken,
26
+ $oMod->getTokenExpireInterval()
27
+ );
28
+ error_log( $nTs );
29
+ error_log( $nPostId );
30
+ error_log( $sToken );
31
+
32
+ return $sToken;
33
+ }
34
+
35
+ /**
36
+ * @param int $nTs
37
+ * @param string $nPostId
38
+ * @return string
39
+ */
40
+ private function generateNewToken( $nTs, $nPostId ) {
41
+ return hash_hmac( 'sha1',
42
+ $nPostId.Services::IP()->getRequestIp().$nTs, $this->getCon()->getSiteInstallationId()
43
+ );
44
+ }
45
+ }
src/lib/src/Modules/Events/AjaxHandler.php CHANGED
@@ -3,6 +3,7 @@
3
  namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Events;
4
 
5
  use FernleafSystems\Wordpress\Plugin\Shield;
 
6
  use FernleafSystems\Wordpress\Services\Services;
7
 
8
  class AjaxHandler extends Shield\Modules\Base\AjaxHandlerShield {
@@ -18,6 +19,10 @@ class AjaxHandler extends Shield\Modules\Base\AjaxHandlerShield {
18
  $aResponse = $this->ajaxExec_RenderChart();
19
  break;
20
 
 
 
 
 
21
  default:
22
  $aResponse = parent::processAjaxAction( $sAction );
23
  }
@@ -28,42 +33,44 @@ class AjaxHandler extends Shield\Modules\Base\AjaxHandlerShield {
28
  /**
29
  * @return array
30
  */
31
- public function ajaxExec_RenderChart() {
32
  /** @var \ICWP_WPSF_FeatureHandler_Events $oMod */
33
  $oMod = $this->getMod();
34
 
35
  $aParams = $this->getAjaxFormParams();
36
- $sEvent = $aParams[ 'event' ];
 
 
 
 
 
 
 
 
 
 
 
 
37
 
38
- /** @var Shield\Databases\Events\Handler $oDbhEvts */
39
- $oDbhEvts = $oMod->getDbHandler_Events();
40
- $nDays = 0;
41
- $aSeries = [];
42
- $aLabels = [];
43
- $oNow = Services::Request()->carbon();
 
 
 
 
44
 
45
- do {
46
- /** @var Shield\Databases\Events\Select $oSelEvts */
47
- $oSelEvts = $oDbhEvts->getQuerySelector();
48
- $aSeries[] = $oSelEvts->filterByBoundary_Day( $oNow->timestamp )
49
- ->sumEvent( $sEvent );
50
- $aLabels[] = $oNow->toDateString();
51
- $oNow->subDay();
52
- $nDays++;
53
- } while ( $nDays < 7 );
54
 
55
  return [
56
  'success' => true,
57
- 'message' => 'asdf',
58
- 'chart' => [
59
- 'data' => [
60
- 'labels' => array_reverse( $aLabels ),
61
- 'series' => [
62
- array_reverse( $aSeries ),
63
- ]
64
- ],
65
- 'legend_names' => [ 'Total Offenses' ],
66
- ]
67
  ];
68
  }
69
  }
3
  namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Events;
4
 
5
  use FernleafSystems\Wordpress\Plugin\Shield;
6
+ use FernleafSystems\Wordpress\Plugin\Shield\Modules\Events;
7
  use FernleafSystems\Wordpress\Services\Services;
8
 
9
  class AjaxHandler extends Shield\Modules\Base\AjaxHandlerShield {
19
  $aResponse = $this->ajaxExec_RenderChart();
20
  break;
21
 
22
+ case 'render_chart_post':
23
+ $aResponse = $this->ajaxExec_RenderChartPost();
24
+ break;
25
+
26
  default:
27
  $aResponse = parent::processAjaxAction( $sAction );
28
  }
33
  /**
34
  * @return array
35
  */
36
+ private function ajaxExec_RenderChart() {
37
  /** @var \ICWP_WPSF_FeatureHandler_Events $oMod */
38
  $oMod = $this->getMod();
39
 
40
  $aParams = $this->getAjaxFormParams();
41
+ $oReq = new Events\Charts\ChartRequestVO();
42
+ $oReq->render_location = $aParams[ 'render_location' ];
43
+ $oReq->chart_params = $aParams[ 'chart_params' ];
44
+ $aChart = ( new Events\Charts\BuildData() )
45
+ ->setMod( $oMod )
46
+ ->build( $oReq );
47
+
48
+ return [
49
+ 'success' => true,
50
+ 'message' => 'no message',
51
+ 'chart' => $aChart
52
+ ];
53
+ }
54
 
55
+ /**
56
+ * @return array
57
+ */
58
+ private function ajaxExec_RenderChartPost() {
59
+ /** @var \ICWP_WPSF_FeatureHandler_Events $oMod */
60
+ $oMod = $this->getMod();
61
+ $oReq = Services::Request();
62
+ $oChartReq = new Events\Charts\ChartRequestVO();
63
+ $oChartReq->render_location = $oReq->post( 'render_location' );
64
+ $oChartReq->chart_params = $oReq->post( 'chart_params' );
65
 
66
+ $aChart = ( new Events\Charts\BuildData() )
67
+ ->setMod( $oMod )
68
+ ->build( $oChartReq );
 
 
 
 
 
 
69
 
70
  return [
71
  'success' => true,
72
+ 'message' => 'no message',
73
+ 'chart' => $aChart
 
 
 
 
 
 
 
 
74
  ];
75
  }
76
  }
src/lib/src/Modules/Events/Charts/BuildData.php ADDED
@@ -0,0 +1,130 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Events\Charts;
4
+
5
+ use FernleafSystems\Wordpress\Plugin\Shield\Modules\ModConsumer;
6
+ use FernleafSystems\Wordpress\Plugin\Shield\Databases\Events;
7
+ use FernleafSystems\Wordpress\Services\Services;
8
+
9
+ class BuildData {
10
+
11
+ use ModConsumer;
12
+
13
+ /**
14
+ * @param ChartRequestVO $oReq
15
+ * @return array
16
+ */
17
+ public function build( ChartRequestVO $oReq ) {
18
+ /** @var \ICWP_WPSF_FeatureHandler_Events $oMod */
19
+ $oMod = $this->getMod();
20
+
21
+ $this->preProcessRequest( $oReq );
22
+
23
+ /** @var Events\Handler $oDbhEvts */
24
+ $oDbhEvts = $oMod->getDbHandler_Events();
25
+
26
+ $nTick = 0;
27
+ $oTime = Services::Request()->carbon();
28
+
29
+ $aLabels = [];
30
+ $aSeries = [];
31
+ do {
32
+ $aLabels[] = $oTime->toDateString();
33
+
34
+ /** @var Events\Select $oSelEvts */
35
+ $oSelEvts = $oDbhEvts->getQuerySelector();
36
+ switch ( $oReq->interval ) {
37
+ case 'hourly':
38
+ $oSelEvts->filterByBoundary_Hour( $oTime->timestamp );
39
+ $oTime->subHour();
40
+ break;
41
+ case 'daily':
42
+ $oSelEvts->filterByBoundary_Day( $oTime->timestamp );
43
+ $oTime->subDay();
44
+ break;
45
+ case 'weekly':
46
+ $oSelEvts->filterByBoundary_Week( $oTime->timestamp );
47
+ $oTime->subWeek();
48
+ break;
49
+ case 'monthly':
50
+ $oSelEvts->filterByBoundary_Month( $oTime->timestamp );
51
+ $oTime->subMonth();
52
+ break;
53
+ case 'yearly':
54
+ $oSelEvts->filterByBoundary_Year( $oTime->timestamp );
55
+ $oTime->subYear();
56
+ break;
57
+ }
58
+
59
+ $aSeries[] = $oSelEvts->sumEvents( $oReq->events );
60
+
61
+ $nTick++;
62
+ } while ( $nTick < $oReq->ticks );
63
+
64
+ return [
65
+ 'data' => [
66
+ 'labels' => [],
67
+ 'series' => [
68
+ array_reverse( $aSeries ),
69
+ ]
70
+ ],
71
+ 'legend_names' => [],
72
+ ];
73
+ }
74
+
75
+ /**
76
+ * @param ChartRequestVO $oReq
77
+ */
78
+ protected function preProcessRequest( ChartRequestVO $oReq ) {
79
+
80
+ if ( empty( $oReq->interval ) ) {
81
+ switch ( $oReq->render_location ) {
82
+ case $oReq::LOCATION_STATCARD:
83
+ $oReq->interval = 'daily';
84
+ break;
85
+ default:
86
+ $oReq->interval = 'weekly';
87
+ break;
88
+ }
89
+ }
90
+
91
+ $aAll = array_keys( $this->getCon()->getAllEvents() );
92
+ if ( !empty( $oReq->chart_params[ 'stat_id' ] ) ) {
93
+ switch ( $oReq->chart_params[ 'stat_id' ] ) {
94
+ case 'comment_block':
95
+ $oReq->events = array_filter(
96
+ $aAll,
97
+ function ( $sEvent ) {
98
+ return strpos( $sEvent, 'spam_block_' ) === 0;
99
+ }
100
+ );
101
+ break;
102
+ case 'bot_blocks':
103
+ $oReq->events = array_filter(
104
+ $aAll,
105
+ function ( $sEvent ) {
106
+ return strpos( $sEvent, 'bottrack_' ) === 0;
107
+ }
108
+ );
109
+ break;
110
+ default:
111
+ $oReq->events = (array)$oReq->chart_params[ 'stat_id' ];
112
+ break;
113
+ }
114
+ }
115
+
116
+ if ( empty( $oReq->ticks ) ) {
117
+ switch ( $oReq->interval ) {
118
+ case 'daily':
119
+ $oReq->ticks = 7;
120
+ break;
121
+ case 'weekly':
122
+ $oReq->ticks = 8;
123
+ break;
124
+ default:
125
+ $oReq->ticks = 12;
126
+ break;
127
+ }
128
+ }
129
+ }
130
+ }
src/lib/src/Modules/Events/Charts/ChartRequestVO.php ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Events\Charts;
4
+
5
+ use FernleafSystems\Utilities\Data\Adapter\StdClassAdapter;
6
+
7
+ /**
8
+ * Class ChartRequestVO
9
+ * @package FernleafSystems\Wordpress\Plugin\Shield\Modules\Events\Charts
10
+ * @property string $render_location
11
+ * @property string $interval
12
+ * @property string $ticks
13
+ * @property string[] $events
14
+ * @property array $chart_params
15
+ */
16
+ class ChartRequestVO {
17
+
18
+ const LOCATION_STATCARD = 'insights-overview-statcard';
19
+ use StdClassAdapter;
20
+ }
src/lib/src/Modules/Events/Consolidate/ConsolidateAllEvents.php ADDED
@@ -0,0 +1,291 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Events\Consolidate;
4
+
5
+ use FernleafSystems\Wordpress\Plugin\Shield\Databases\Events;
6
+ use FernleafSystems\Wordpress\Plugin\Shield\Modules\ModConsumer;
7
+ use FernleafSystems\Wordpress\Services\Services;
8
+
9
+ class ConsolidateAllEvents {
10
+
11
+ use ModConsumer;
12
+
13
+ public function run() {
14
+ foreach ( $this->getAllEvents() as $sEvent ) {
15
+ $this->consolidateEventIntoHourly( $sEvent );
16
+ $this->consolidateEventIntoDaily( $sEvent );
17
+ $this->consolidateEventIntoWeekly( $sEvent );
18
+ $this->consolidateEventIntoMonthly( $sEvent );
19
+ $this->consolidateEventIntoYearly( $sEvent );
20
+ }
21
+ }
22
+
23
+ /**
24
+ * @param $sEvent
25
+ */
26
+ protected function consolidateEventIntoHourly( $sEvent ) {
27
+ /** @var \ICWP_WPSF_FeatureHandler_Events $oMod */
28
+ $oMod = $this->getMod();
29
+ $oDbH = $oMod->getDbHandler_Events();
30
+
31
+ $oTime = Services::Request()
32
+ ->carbon()
33
+ ->subHour( 1 )
34
+ ->startOfHour();
35
+
36
+ $nHourCount = 0;
37
+ do {
38
+ /** @var Events\Select $oSel */
39
+ $oSel = $oDbH->getQuerySelector();
40
+ $nRecords = $oSel->filterByBoundary_Hour( $oTime->timestamp )
41
+ ->filterByEvent( $sEvent )
42
+ ->count();
43
+
44
+ if ( $nRecords > 1 ) {
45
+ /** @var Events\Select $oSel */
46
+ $oSel = $oDbH->getQuerySelector();
47
+ /** @var Events\EntryVO[] $aRecords */
48
+ $nSum = $oSel->filterByBoundary_Hour( $oTime->timestamp )
49
+ ->sumEvent( $sEvent );
50
+ if ( $nSum > 0 ) {
51
+
52
+ /** @var Events\Delete $oDel */
53
+ $oDel = $oDbH->getQueryDeleter();
54
+ $oDel->filterByBoundary_Hour( $oTime->timestamp )
55
+ ->filterByEvent( $sEvent )
56
+ ->query();
57
+
58
+ $oEntry = new Events\EntryVO();
59
+ $oEntry->event = $sEvent;
60
+ $oEntry->count = $nSum;
61
+ $oEntry->created_at = $oTime->timestamp + 1;
62
+ /** @var Events\Insert $oQI */
63
+ $oQI = $oDbH->getQueryInserter();
64
+ $oQI->insert( $oEntry );
65
+ }
66
+ }
67
+
68
+ $nHourCount++;
69
+ $oTime->subHour();
70
+ } while ( $nHourCount < 48 );
71
+ }
72
+
73
+ /**
74
+ * Consolidates each event in Daily sums. Doesn't process events from the previous 48hrs.
75
+ * Processes event for the 7 days previous to the last 48 hours.
76
+ * @param $sEvent
77
+ */
78
+ protected function consolidateEventIntoDaily( $sEvent ) {
79
+ /** @var \ICWP_WPSF_FeatureHandler_Events $oMod */
80
+ $oMod = $this->getMod();
81
+ $oDbH = $oMod->getDbHandler_Events();
82
+
83
+ $oTime = Services::Request()
84
+ ->carbon()
85
+ ->subDays( 2 )
86
+ ->startOfDay();
87
+
88
+ $nDayCount = 0;
89
+ do {
90
+ /** @var Events\Select $oSel */
91
+ $oSel = $oDbH->getQuerySelector();
92
+ $nRecords = $oSel->filterByBoundary_Day( $oTime->timestamp )
93
+ ->filterByEvent( $sEvent )
94
+ ->count();
95
+
96
+ if ( $nRecords > 1 ) {
97
+ /** @var Events\Select $oSel */
98
+ $oSel = $oDbH->getQuerySelector();
99
+ /** @var Events\EntryVO[] $aRecords */
100
+ $nSum = $oSel->filterByBoundary_Day( $oTime->timestamp )
101
+ ->sumEvent( $sEvent );
102
+ if ( $nSum > 0 ) {
103
+
104
+ /** @var Events\Delete $oDel */
105
+ $oDel = $oDbH->getQueryDeleter();
106
+ $oDel->filterByBoundary_Day( $oTime->timestamp )
107
+ ->filterByEvent( $sEvent )
108
+ ->query();
109
+
110
+ $oEntry = new Events\EntryVO();
111
+ $oEntry->event = $sEvent;
112
+ $oEntry->count = $nSum;
113
+ $oEntry->created_at = $oTime->timestamp + 1;
114
+ /** @var Events\Insert $oQI */
115
+ $oQI = $oDbH->getQueryInserter();
116
+ $oQI->insert( $oEntry );
117
+ }
118
+ }
119
+
120
+ $nDayCount++;
121
+ $oTime->subDay();
122
+ } while ( $nDayCount < 13 );
123
+ }
124
+
125
+ /**
126
+ * Consolidates each event in weekly sums. Doesn't process events from the previous 2 whole weeks.
127
+ * Processes event for the previous 8 weeks.
128
+ * @param $sEvent
129
+ */
130
+ protected function consolidateEventIntoWeekly( $sEvent ) {
131
+ /** @var \ICWP_WPSF_FeatureHandler_Events $oMod */
132
+ $oMod = $this->getMod();
133
+ $oDbH = $oMod->getDbHandler_Events();
134
+
135
+ $oTime = Services::Request()
136
+ ->carbon()
137
+ ->subWeek( 2 )
138
+ ->startOfWeek();
139
+
140
+ $nWeekCount = 0;
141
+ do {
142
+ /** @var Events\Select $oSel */
143
+ $oSel = $oDbH->getQuerySelector();
144
+ $nRecords = $oSel->filterByBoundary_Week( $oTime->timestamp )
145
+ ->filterByEvent( $sEvent )
146
+ ->count();
147
+
148
+ if ( $nRecords > 1 ) {
149
+ /** @var Events\Select $oSel */
150
+ $oSel = $oDbH->getQuerySelector();
151
+ /** @var Events\EntryVO[] $aRecords */
152
+ $nSum = $oSel->filterByBoundary_Week( $oTime->timestamp )
153
+ ->sumEvent( $sEvent );
154
+
155
+ if ( $nSum > 0 ) {
156
+ /** @var Events\Delete $oDel */
157
+ $oDel = $oDbH->getQueryDeleter();
158
+ $oDel->filterByBoundary_Week( $oTime->timestamp )
159
+ ->filterByEvent( $sEvent )
160
+ ->query();
161
+
162
+ $oEntry = new Events\EntryVO();
163
+ $oEntry->event = $sEvent;
164
+ $oEntry->count = $nSum;
165
+ $oEntry->created_at = $oTime->timestamp + 1;
166
+ /** @var Events\Insert $oQI */
167
+ $oQI = $oDbH->getQueryInserter();
168
+ $oQI->insert( $oEntry );
169
+ }
170
+ }
171
+
172
+ $nWeekCount++;
173
+ $oTime->subWeek();
174
+ } while ( $nWeekCount < 8 );
175
+ }
176
+
177
+ /**
178
+ * Consolidates each event in Daily sums. Doesn't process events from the previous 48hrs.
179
+ * Processes event for the 7 days previous to the last 48 hours.
180
+ * @param $sEvent
181
+ */
182
+ protected function consolidateEventIntoMonthly( $sEvent ) {
183
+ /** @var \ICWP_WPSF_FeatureHandler_Events $oMod */
184
+ $oMod = $this->getMod();
185
+ $oDbH = $oMod->getDbHandler_Events();
186
+
187
+ $oTime = Services::Request()
188
+ ->carbon()
189
+ ->subMonth( 2 )
190
+ ->startOfMonth();
191
+
192
+ $nMonthCount = 0;
193
+ do {
194
+ /** @var Events\Select $oSel */
195
+ $oSel = $oDbH->getQuerySelector();
196
+ $nRecords = $oSel->filterByBoundary_Month( $oTime->timestamp )
197
+ ->filterByEvent( $sEvent )
198
+ ->count();
199
+
200
+ if ( $nRecords > 1 ) {
201
+ /** @var Events\Select $oSel */
202
+ $oSel = $oDbH->getQuerySelector();
203
+ /** @var Events\EntryVO[] $aRecords */
204
+ $nSum = $oSel->filterByBoundary_Month( $oTime->timestamp )
205
+ ->sumEvent( $sEvent );
206
+
207
+ if ( $nSum > 0 ) {
208
+ /** @var Events\Delete $oDel */
209
+ $oDel = $oDbH->getQueryDeleter();
210
+ $oDel->filterByBoundary_Month( $oTime->timestamp )
211
+ ->filterByEvent( $sEvent )
212
+ ->query();
213
+
214
+ $oEntry = new Events\EntryVO();
215
+ $oEntry->event = $sEvent;
216
+ $oEntry->count = $nSum;
217
+ $oEntry->created_at = $oTime->timestamp + 1;
218
+ /** @var Events\Insert $oQI */
219
+ $oQI = $oDbH->getQueryInserter();
220
+ $oQI->insert( $oEntry );
221
+ }
222
+ }
223
+
224
+ $nMonthCount++;
225
+ $oTime->subMonth();
226
+ } while ( $nMonthCount < 24 );
227
+ }
228
+
229
+ /**
230
+ * @param $sEvent
231
+ */
232
+ protected function consolidateEventIntoYearly( $sEvent ) {
233
+ /** @var \ICWP_WPSF_FeatureHandler_Events $oMod */
234
+ $oMod = $this->getMod();
235
+ $oDbH = $oMod->getDbHandler_Events();
236
+
237
+ $oTime = Services::Request()
238
+ ->carbon()
239
+ ->subYear( 2 )
240
+ ->startOfYear();
241
+
242
+ /** @var Events\Select $oSel */
243
+ $oSel = $oDbH->getQuerySelector();
244
+ $oOldest = $oSel->getOldestForEvent( $sEvent );
245
+
246
+ do {
247
+ /** @var Events\Select $oSel */
248
+ $oSel = $oDbH->getQuerySelector();
249
+ $nRecords = $oSel->filterByBoundary_Year( $oTime->timestamp )
250
+ ->filterByEvent( $sEvent )
251
+ ->count();
252
+
253
+ if ( $nRecords > 1 ) {
254
+ /** @var Events\Select $oSel */
255
+ $oSel = $oDbH->getQuerySelector();
256
+ /** @var Events\EntryVO[] $aRecords */
257
+ $nSum = $oSel->filterByBoundary_Year( $oTime->timestamp )
258
+ ->sumEvent( $sEvent );
259
+
260
+ if ( $nSum > 0 ) {
261
+ /** @var Events\Delete $oDel */
262
+ $oDel = $oDbH->getQueryDeleter();
263
+ $oDel->filterByBoundary_Year( $oTime->timestamp )
264
+ ->filterByEvent( $sEvent )
265
+ ->query();
266
+
267
+ $oEntry = new Events\EntryVO();
268
+ $oEntry->event = $sEvent;
269
+ $oEntry->count = $nSum;
270
+ $oEntry->created_at = $oTime->timestamp + 1;
271
+ /** @var Events\Insert $oQI */
272
+ $oQI = $oDbH->getQueryInserter();
273
+ $oQI->insert( $oEntry );
274
+ }
275
+ }
276
+
277
+ $oTime->subYear();
278
+ } while ( $oTime->timestamp > $oOldest->created_at );
279
+ }
280
+
281
+ /**
282
+ * @return string[]
283
+ */
284
+ protected function getAllEvents() {
285
+ /** @var \ICWP_WPSF_FeatureHandler_Events $oMod */
286
+ $oMod = $this->getMod();
287
+ /** @var Events\Select $oSel */
288
+ $oSel = $oMod->getDbHandler_Events()->getQuerySelector();
289
+ return $oSel->getAllEvents();
290
+ }
291
+ }
src/lib/src/Modules/HackGuard/Options.php CHANGED
@@ -311,7 +311,7 @@ class Options extends Base\ShieldOptions {
311
  public function getWcfFileExclusions() {
312
  $sPattern = null;
313
 
314
- $aExclusions = $this->getMod()->getDef( 'wcf_exclusions' );
315
  $aExclusions = is_array( $aExclusions ) ? $aExclusions : [];
316
  // Flywheel specific mods
317
  if ( defined( 'FLYWHEEL_PLUGIN_DIR' ) ) {
@@ -337,7 +337,7 @@ class Options extends Base\ShieldOptions {
337
  */
338
  public function getWcfMissingExclusions() {
339
  $sPattern = null;
340
- $aExclusions = $this->getMod()->getDef( 'wcf_exclusions_missing_only' );
341
  if ( is_array( $aExclusions ) && !empty( $aExclusions ) ) {
342
  $aQuoted = array_map(
343
  function ( $sExcl ) {
311
  public function getWcfFileExclusions() {
312
  $sPattern = null;
313
 
314
+ $aExclusions = $this->getOptions()->getDef( 'wcf_exclusions' );
315
  $aExclusions = is_array( $aExclusions ) ? $aExclusions : [];
316
  // Flywheel specific mods
317
  if ( defined( 'FLYWHEEL_PLUGIN_DIR' ) ) {
337
  */
338
  public function getWcfMissingExclusions() {
339
  $sPattern = null;
340
+ $aExclusions = $this->getOptions()->getDef( 'wcf_exclusions_missing_only' );
341
  if ( is_array( $aExclusions ) && !empty( $aExclusions ) ) {
342
  $aQuoted = array_map(
343
  function ( $sExcl ) {
src/lib/src/Modules/HackGuard/Strings.php CHANGED
@@ -364,7 +364,7 @@ class Strings extends Base\Strings {
364
  .'<br />'.__( "Disabling network intelligence turns off 'false positive confidence' levels.", 'wp-simple-firewall' )
365
  .' '.__( 'You will no longer benefit from the intelligence gathered from the entire network.', 'wp-simple-firewall' )
366
  .' '.__( 'All data shared is completely anonymous.', 'wp-simple-firewall' )
367
- .' '.' [<a href="https://icwp.io/moreinfomalnetwork">'.__( 'More Info', 'wp-simple-firewall' ).'</a>]'
368
  .'<br />'.__( 'The more sites that share this information, the stronger and smarter the network becomes.', 'wp-simple-firewall' );
369
  break;
370
 
364
  .'<br />'.__( "Disabling network intelligence turns off 'false positive confidence' levels.", 'wp-simple-firewall' )
365
  .' '.__( 'You will no longer benefit from the intelligence gathered from the entire network.', 'wp-simple-firewall' )
366
  .' '.__( 'All data shared is completely anonymous.', 'wp-simple-firewall' )
367
+ .' '.' [<a href="https://shsec.io/moreinfomalnetwork">'.__( 'More Info', 'wp-simple-firewall' ).'</a>]'
368
  .'<br />'.__( 'The more sites that share this information, the stronger and smarter the network becomes.', 'wp-simple-firewall' );
369
  break;
370
 
src/lib/src/Modules/IPs/AjaxHandler.php CHANGED
@@ -53,7 +53,9 @@ class AjaxHandler extends Shield\Modules\Base\AjaxHandlerShield {
53
  $sIp = preg_replace( '#[^/:.a-f\d]#i', '', ( isset( $aFormParams[ 'ip' ] ) ? $aFormParams[ 'ip' ] : '' ) );
54
  $sList = isset( $aFormParams[ 'list' ] ) ? $aFormParams[ 'list' ] : '';
55
 
56
- $bAcceptableIp = $oIpServ->isValidIp( $sIp ) || $oIpServ->isValidIp4Range( $sIp );
 
 
57
 
58
  $bIsBlackList = $sList != $oMod::LIST_MANUAL_WHITE;
59
 
53
  $sIp = preg_replace( '#[^/:.a-f\d]#i', '', ( isset( $aFormParams[ 'ip' ] ) ? $aFormParams[ 'ip' ] : '' ) );
54
  $sList = isset( $aFormParams[ 'list' ] ) ? $aFormParams[ 'list' ] : '';
55
 
56
+ $bAcceptableIp = $oIpServ->isValidIp( $sIp )
57
+ || $oIpServ->isValidIp4Range( $sIp )
58
+ || $oIpServ->isValidIp6Range( $sIp );
59
 
60
  $bIsBlackList = $sList != $oMod::LIST_MANUAL_WHITE;
61
 
src/lib/src/Modules/IPs/Components/LookupIpOnList.php ADDED
@@ -0,0 +1,105 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\IPs\Components;
4
+
5
+ use FernleafSystems\Wordpress\Plugin\Shield\Modules\ModConsumer;
6
+ use FernleafSystems\Wordpress\Plugin\Shield\Databases\IPs;
7
+ use FernleafSystems\Wordpress\Services\Services;
8
+
9
+ class LookupIpOnList {
10
+
11
+ use ModConsumer;
12
+
13
+ /**
14
+ * @var string
15
+ */
16
+ private $sIp;
17
+
18
+ /**
19
+ * @var string
20
+ */
21
+ private $sList;
22
+
23
+ /**
24
+ * @param bool $bIncludeRanges
25
+ * @return IPs\EntryVO|null
26
+ */
27
+ public function lookup( $bIncludeRanges = true ) {
28
+ $oIp = $this->lookupIp();
29
+ if ( $bIncludeRanges && !$oIp instanceof IPs\EntryVO ) {
30
+ foreach ( $this->lookupRange() as $oMaybeIp ) {
31
+ try {
32
+ if ( Services::IP()->checkIp( $this->getIp(), $oMaybeIp->ip ) ) {
33
+ $oIp = $oMaybeIp;
34
+ break;
35
+ }
36
+ }
37
+ catch ( \Exception $oE ) {
38
+ }
39
+ }
40
+ }
41
+ return $oIp;
42
+ }
43
+
44
+ /**
45
+ * @return IPs\EntryVO|null
46
+ */
47
+ public function lookupIp() {
48
+ /** @var \ICWP_WPSF_FeatureHandler_Ips $oMod */
49
+ $oMod = $this->getMod();
50
+ /** @var IPs\Select $oSelect */
51
+ $oSelect = $oMod->getDbHandler_IPs()->getQuerySelector();
52
+
53
+ return $oSelect->filterByIsRange( false )
54
+ ->filterByIp( $this->getIp() )
55
+ ->filterByList( $this->getList() )
56
+ ->first();
57
+ }
58
+
59
+ /**
60
+ * @return IPs\EntryVO[]
61
+ */
62
+ public function lookupRange() {
63
+ /** @var \ICWP_WPSF_FeatureHandler_Ips $oMod */
64
+ $oMod = $this->getMod();
65
+ /** @var IPs\Select $oSelect */
66
+ $oSelect = $oMod->getDbHandler_IPs()->getQuerySelector();
67
+
68
+ $aIps = $oSelect->filterByIsRange( true )
69
+ ->filterByList( $this->getList() )
70
+ ->query();
71
+ return is_array( $aIps ) ? $aIps : [];
72
+ }
73
+
74
+ /**
75
+ * @return string
76
+ */
77
+ public function getIp() {
78
+ return $this->sIp;
79
+ }
80
+
81
+ /**
82
+ * @return string
83
+ */
84
+ public function getList() {
85
+ return $this->sList;
86
+ }
87
+
88
+ /**
89
+ * @param string $sIp
90
+ * @return $this
91
+ */
92
+ public function setIp( $sIp ) {
93
+ $this->sIp = $sIp;
94
+ return $this;
95
+ }
96
+
97
+ /**
98
+ * @param string $sList
99
+ * @return $this
100
+ */
101
+ public function setList( $sList ) {
102
+ $this->sList = $sList;
103
+ return $this;
104
+ }
105
+ }
src/lib/src/Modules/IPs/Options.php CHANGED
@@ -137,7 +137,6 @@ class Options extends Base\ShieldOptions {
137
  * @return bool
138
  */
139
  protected function isSelectOptionEnabled( $sOptionKey ) {
140
- $bOptPrem = $this->isOptPremium( $sOptionKey );
141
- return ( !$bOptPrem || $this->getCon()->isPremiumActive() ) && !$this->isOpt( $sOptionKey, 'disabled' );
142
  }
143
  }
137
  * @return bool
138
  */
139
  protected function isSelectOptionEnabled( $sOptionKey ) {
140
+ return !$this->isOpt( $sOptionKey, 'disabled' );
 
141
  }
142
  }
src/lib/src/Modules/Plugin/AdminNotices.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\Services\Services;
7
 
8
  class AdminNotices extends Shield\Modules\Base\AdminNotices {
@@ -19,6 +20,10 @@ class AdminNotices extends Shield\Modules\Base\AdminNotices {
19
  $this->buildNotice_OverrideForceoff( $oNotice );
20
  break;
21
 
 
 
 
 
22
  case 'plugin-mailing-list-signup':
23
  $this->buildNotice_PluginMailingListSignup( $oNotice );
24
  break;
@@ -99,6 +104,31 @@ class AdminNotices extends Shield\Modules\Base\AdminNotices {
99
  ];
100
  }
101
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
102
  /**
103
  * @param Shield\Utilities\AdminNotices\NoticeVO $oNotice
104
  */
@@ -204,7 +234,7 @@ class AdminNotices extends Shield\Modules\Base\AdminNotices {
204
  'hrefs' => [
205
  'learn_more' => 'https://translate.fernleafsystems.com',
206
  'link_to_see' => $oMod->getLinkToTrackingDataDump(),
207
- 'link_to_moreinfo' => 'https://icwp.io/shieldtrackinginfo',
208
 
209
  ]
210
  ];
@@ -240,6 +270,10 @@ class AdminNotices extends Shield\Modules\Base\AdminNotices {
240
  $bNeeded = $this->getCon()->getIfForceOffActive();
241
  break;
242
 
 
 
 
 
243
  case 'plugin-update-available':
244
  $bNeeded = !Services::WpPost()->isPage_Updates()
245
  && Services::WpPlugins()->isUpdateAvailable( !Services::WpPost()->isPage_Updates() );
3
  namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Plugin;
4
 
5
  use FernleafSystems\Wordpress\Plugin\Shield;
6
+ use FernleafSystems\Wordpress\Plugin\Shield\Modules\Plugin;
7
  use FernleafSystems\Wordpress\Services\Services;
8
 
9
  class AdminNotices extends Shield\Modules\Base\AdminNotices {
20
  $this->buildNotice_OverrideForceoff( $oNotice );
21
  break;
22
 
23
+ case 'compat-sgoptimize':
24
+ $this->buildNotice_CompatSgOptimize( $oNotice );
25
+ break;
26
+
27
  case 'plugin-mailing-list-signup':
28
  $this->buildNotice_PluginMailingListSignup( $oNotice );
29
  break;
104
  ];
105
  }
106
 
107
+ /**
108
+ * @param Shield\Utilities\AdminNotices\NoticeVO $oNotice
109
+ */
110
+ private function buildNotice_CompatSgOptimize( $oNotice ) {
111
+ $sName = $this->getCon()->getHumanName();
112
+
113
+ $oNotice->render_data = [
114
+ 'notice_attributes' => [],
115
+ 'strings' => [
116
+ 'title' => sprintf( '%s: %s', __( 'Warning', 'wp-simple-firewall' ),
117
+ sprintf( __( 'Site Ground Optimizer plugin has a conflict', 'wp-simple-firewall' ), $sName ) ),
118
+ 'message' => sprintf(
119
+ __( 'The SG Optimizer plugin has 2 settings which are breaking your site and certain %s features.', 'wp-simple-firewall' ),
120
+ $sName
121
+ )
122
+ .' '.sprintf( 'The problematic options are: "Defer Render-blocking JS" and "Remove Query Strings From Static Resources".' ),
123
+ 'learn_more' => sprintf( 'Click here to learn more' ),
124
+ 'sgoptimizer_turnoff' => __( 'Click here to automatically turn off those options.', 'wp-simple-firewall' )
125
+ ],
126
+ 'ajax' => [
127
+ 'sgoptimizer_turnoff' => $this->getMod()->getAjaxActionData( 'sgoptimizer_turnoff', true )
128
+ ]
129
+ ];
130
+ }
131
+
132
  /**
133
  * @param Shield\Utilities\AdminNotices\NoticeVO $oNotice
134
  */
234
  'hrefs' => [
235
  'learn_more' => 'https://translate.fernleafsystems.com',
236
  'link_to_see' => $oMod->getLinkToTrackingDataDump(),
237
+ 'link_to_moreinfo' => 'https://shsec.io/shieldtrackinginfo',
238
 
239
  ]
240
  ];
270
  $bNeeded = $this->getCon()->getIfForceOffActive();
271
  break;
272
 
273
+ case 'compat-sgoptimize':
274
+ $bNeeded = ( new Plugin\Components\SiteGroundPluginCompatibility() )->testIsIncompatible();
275
+ break;
276
+
277
  case 'plugin-update-available':
278
  $bNeeded = !Services::WpPost()->isPage_Updates()
279
  && Services::WpPlugins()->isUpdateAvailable( !Services::WpPost()->isPage_Updates() );
src/lib/src/Modules/Plugin/AjaxHandler.php CHANGED
@@ -54,6 +54,10 @@ class AjaxHandler extends Shield\Modules\Base\AjaxHandlerShield {
54
  $aResponse = $this->ajaxExec_SendDeactivateSurvey();
55
  break;
56
 
 
 
 
 
57
  default:
58
  $aResponse = parent::processAjaxAction( $sAction );
59
  }
@@ -125,7 +129,7 @@ class AjaxHandler extends Shield\Modules\Base\AjaxHandlerShield {
125
  $bSuccess = false;
126
  $sMessage = __( 'No items selected.', 'wp-simple-firewall' );
127
  }
128
- else if ( !in_array( $oReq->post( 'bulk_action' ), [ 'delete' ] ) ) {
129
  $sMessage = __( 'Not a supported action.', 'wp-simple-firewall' );
130
  }
131
  else {
@@ -253,7 +257,7 @@ class AjaxHandler extends Shield\Modules\Base\AjaxHandlerShield {
253
  /**
254
  * @return array
255
  */
256
- protected function ajaxExec_AdminNotesInsert() {
257
  /** @var \ICWP_WPSF_FeatureHandler_Plugin $oMod */
258
  $oMod = $this->getMod();
259
  $bSuccess = false;
@@ -263,7 +267,7 @@ class AjaxHandler extends Shield\Modules\Base\AjaxHandlerShield {
263
  if ( !$oMod->getCanAdminNotes() ) {
264
  $sMessage = __( 'Sorry, Admin Notes is only available for Pro subscriptions.', 'wp-simple-firewall' );
265
  }
266
- else if ( empty( $sNote ) ) {
267
  $sMessage = __( 'Sorry, but it appears your note was empty.', 'wp-simple-firewall' );
268
  }
269
  else {
@@ -277,4 +281,17 @@ class AjaxHandler extends Shield\Modules\Base\AjaxHandlerShield {
277
  'message' => $sMessage
278
  ];
279
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
280
  }
54
  $aResponse = $this->ajaxExec_SendDeactivateSurvey();
55
  break;
56
 
57
+ case 'sgoptimizer_turnoff':
58
+ $aResponse = $this->ajaxExec_TurnOffSiteGroundOptions();
59
+ break;
60
+
61
  default:
62
  $aResponse = parent::processAjaxAction( $sAction );
63
  }
129
  $bSuccess = false;
130
  $sMessage = __( 'No items selected.', 'wp-simple-firewall' );
131
  }
132
+ elseif ( !in_array( $oReq->post( 'bulk_action' ), [ 'delete' ] ) ) {
133
  $sMessage = __( 'Not a supported action.', 'wp-simple-firewall' );
134
  }
135
  else {
257
  /**
258
  * @return array
259
  */
260
+ private function ajaxExec_AdminNotesInsert() {
261
  /** @var \ICWP_WPSF_FeatureHandler_Plugin $oMod */
262
  $oMod = $this->getMod();
263
  $bSuccess = false;
267
  if ( !$oMod->getCanAdminNotes() ) {
268
  $sMessage = __( 'Sorry, Admin Notes is only available for Pro subscriptions.', 'wp-simple-firewall' );
269
  }
270
+ elseif ( empty( $sNote ) ) {
271
  $sMessage = __( 'Sorry, but it appears your note was empty.', 'wp-simple-firewall' );
272
  }
273
  else {
281
  'message' => $sMessage
282
  ];
283
  }
284
+
285
+ /**
286
+ * @return array
287
+ */
288
+ private function ajaxExec_TurnOffSiteGroundOptions() {
289
+ $bSuccess = ( new Shield\Modules\Plugin\Components\SiteGroundPluginCompatibility() )
290
+ ->switchOffOptions();
291
+ return [
292
+ 'success' => $bSuccess,
293
+ 'message' => $bSuccess ? __( 'Switching-off conflicting options appears to have been successful.', 'wp-simple-firewall' )
294
+ : __( 'Switching-off conflicting options appears to have failed.', 'wp-simple-firewall' )
295
+ ];
296
+ }
297
  }
src/lib/src/Modules/Plugin/Components/BadgeWidget.php CHANGED
@@ -2,7 +2,7 @@
2
 
3
  namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Plugin\Components;
4
 
5
- class BadgeWidget extends \ICWP_WPSF_WpWidget {
6
 
7
  use \FernleafSystems\Wordpress\Plugin\Shield\Modules\ModConsumer;
8
 
@@ -29,6 +29,20 @@ class BadgeWidget extends \ICWP_WPSF_WpWidget {
29
  add_shortcode( 'SHIELD_BADGE', [ $this, 'renderBadge' ] );
30
  }
31
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
32
  /**
33
  * @param array $aNewInstance
34
  * @param array $aOldInstance
2
 
3
  namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Plugin\Components;
4
 
5
+ class BadgeWidget extends \WP_Widget {
6
 
7
  use \FernleafSystems\Wordpress\Plugin\Shield\Modules\ModConsumer;
8
 
29
  add_shortcode( 'SHIELD_BADGE', [ $this, 'renderBadge' ] );
30
  }
31
 
32
+ /**
33
+ * @param array $aWidgetArguments
34
+ * @param string $sTitle
35
+ * @param string $sContent
36
+ * @return string
37
+ */
38
+ protected function standardRender( $aWidgetArguments, $sTitle = '', $sContent = '' ) {
39
+ echo $aWidgetArguments[ 'before_widget' ];
40
+ if ( !empty( $sTitle ) ) {
41
+ echo $aWidgetArguments[ 'before_title' ].$sTitle.$aWidgetArguments[ 'after_title' ];
42
+ }
43
+ return $sContent.$aWidgetArguments[ 'after_widget' ];
44
+ }
45
+
46
  /**
47
  * @param array $aNewInstance
48
  * @param array $aOldInstance
src/lib/src/Modules/Plugin/Components/PluginBadge.php CHANGED
@@ -68,7 +68,7 @@ class PluginBadge {
68
  'is_floating' => $bFloating
69
  ],
70
  'hrefs' => [
71
- 'badge' => 'https://icwp.io/wpsecurityfirewall',
72
  'logo' => $oCon->getPluginUrl_Image( 'shield/shield-security-logo-colour-32px.png' ),
73
  ],
74
  'strings' => [
68
  'is_floating' => $bFloating
69
  ],
70
  'hrefs' => [
71
+ 'badge' => 'https://shsec.io/wpsecurityfirewall',
72
  'logo' => $oCon->getPluginUrl_Image( 'shield/shield-security-logo-colour-32px.png' ),
73
  ],
74
  'strings' => [
src/lib/src/Modules/Plugin/Components/SiteGroundPluginCompatibility.php ADDED
@@ -0,0 +1,69 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\Plugin\Components;
4
+
5
+ class SiteGroundPluginCompatibility {
6
+
7
+ /**
8
+ * @return bool
9
+ */
10
+ public function testIsIncompatible() {
11
+ $bIncompatExist = false;
12
+ if ( $this->isSGOptimizerPluginAsExpected() ) {
13
+ try {
14
+ foreach ( $this->getIncompatOptions() as $sOption ) {
15
+ if ( \SiteGround_Optimizer\Options\Options::is_enabled( $sOption ) ) {
16
+ $bIncompatExist = true;
17
+ break;
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
+ /**
61
+ * @return string[]
62
+ */
63
+ private function getIncompatOptions() {
64
+ return [
65
+ 'siteground_optimizer_remove_query_strings',
66
+ 'siteground_optimizer_optimize_javascript_async',
67
+ ];
68
+ }
69
+ }
src/lib/src/Modules/Plugin/Strings.php CHANGED
@@ -179,7 +179,7 @@ class Strings extends Base\Strings {
179
  $sSummary = __( 'Display Plugin Badge On Your Site', 'wp-simple-firewall' );
180
  $sDescription = __( 'Enabling this option helps support the plugin by spreading the word about it on your website.', 'wp-simple-firewall' )
181
  .' '.__( 'The plugin badge also lets visitors know your are taking your website security seriously.', 'wp-simple-firewall' )
182
- .sprintf( '<br /><strong><a href="%s" target="_blank">%s</a></strong>', 'https://icwp.io/wpsf20', __( 'Read this carefully before enabling this option.', 'wp-simple-firewall' ) );
183
  break;
184
 
185
  case 'delete_on_deactivate' :
179
  $sSummary = __( 'Display Plugin Badge On Your Site', 'wp-simple-firewall' );
180
  $sDescription = __( 'Enabling this option helps support the plugin by spreading the word about it on your website.', 'wp-simple-firewall' )
181
  .' '.__( 'The plugin badge also lets visitors know your are taking your website security seriously.', 'wp-simple-firewall' )
182
+ .sprintf( '<br /><strong><a href="%s" target="_blank">%s</a></strong>', 'https://shsec.io/wpsf20', __( 'Read this carefully before enabling this option.', 'wp-simple-firewall' ) );
183
  break;
184
 
185
  case 'delete_on_deactivate' :
src/lib/src/Modules/PluginControllerConsumer.php CHANGED
@@ -9,13 +9,19 @@ trait PluginControllerConsumer {
9
  /**
10
  * @var Controller
11
  */
 
 
 
 
 
 
12
  static private $oPluginController;
13
 
14
  /**
15
  * @return Controller
16
  */
17
  public function getCon() {
18
- return self::$oPluginController;
19
  }
20
 
21
  /**
@@ -23,7 +29,7 @@ trait PluginControllerConsumer {
23
  * @return $this
24
  */
25
  public function setCon( $oCon ) {
26
- self::$oPluginController = $oCon;
27
  return $this;
28
  }
29
  }
9
  /**
10
  * @var Controller
11
  */
12
+ private $oPlugCon;
13
+
14
+ /**
15
+ * @var Controller
16
+ * @deprecated 8.4
17
+ */
18
  static private $oPluginController;
19
 
20
  /**
21
  * @return Controller
22
  */
23
  public function getCon() {
24
+ return isset( $this->oPlugCon ) ? $this->oPlugCon : self::$oPluginController;
25
  }
26
 
27
  /**
29
  * @return $this
30
  */
31
  public function setCon( $oCon ) {
32
+ $this->oPlugCon = $oCon;
33
  return $this;
34
  }
35
  }
src/lib/src/Modules/SecurityAdmin/Options.php CHANGED
@@ -61,7 +61,7 @@ class Options extends Base\ShieldOptions {
61
  /**
62
  * @return array
63
  */
64
- public function getRestrictedOptions() {
65
  $aOptions = $this->getDef( 'options_to_restrict' );
66
  return is_array( $aOptions ) ? $aOptions : [];
67
  }
61
  /**
62
  * @return array
63
  */
64
+ private function getRestrictedOptions() {
65
  $aOptions = $this->getDef( 'options_to_restrict' );
66
  return is_array( $aOptions ) ? $aOptions : [];
67
  }
src/lib/src/Scans/Mal/FileScanner.php CHANGED
@@ -75,22 +75,41 @@ class FileScanner extends Shield\Scans\Base\Files\BaseFileScanner {
75
  else {
76
  $oAction = $this->getScanActionVO();
77
 
78
- // Remove lines that exceed our false positive confidence
79
  if ( $oAction->confidence_threshold > 0 ) {
80
- foreach ( $aLines as $nLineNum => $sLineContent ) {
81
- $nFpConfidence = $this->getFalsePositiveConfidenceForLine( $sFullPath, $sLineContent );
82
- if ( $nFpConfidence > $oAction->confidence_threshold ) {
83
- unset( $aLines[ $nLineNum ] );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
84
  }
85
  }
86
  }
 
 
 
87
 
88
- if ( !empty( $aLines ) ) {
89
- $nFalsePositiveConfidence = $this->getFalsePositiveConfidenceForFile( $sFullPath );
90
- if ( $oAction->confidence_threshold == 0 || $nFalsePositiveConfidence < $oAction->confidence_threshold ) {
91
- $oResultItem = $this->getResultItemFromLines( array_keys( $aLines ), $sFullPath, $sSig );
92
- $oResultItem->fp_confidence = $nFalsePositiveConfidence;
93
- }
94
  }
95
  }
96
  }
@@ -123,52 +142,6 @@ class FileScanner extends Shield\Scans\Base\Files\BaseFileScanner {
123
  || $this->isPluginFileValid( $sFullPath ) || $this->isThemeFileValid( $sFullPath );
124
  }
125
 
126
- /**
127
- * @param string $sFullPath
128
- * @param string $sLine
129
- * @return int
130
- */
131
- private function getFalsePositiveConfidenceForLine( $sFullPath, $sLine ) {
132
- /** @var ScanActionVO $oScanVO */
133
- $oScanVO = $this->getScanActionVO();
134
-
135
- $nConfidence = 0;
136
- $sFilePart = basename( $sFullPath );
137
- if ( $oScanVO->confidence_threshold > 0 && isset( $oScanVO->fp_signatures[ $sFilePart ] ) ) {
138
- $sHashLine = sha1( trim( $sLine ) );
139
- if ( isset( $oScanVO->fp_signatures[ $sFilePart ][ $sHashLine ] ) ) {
140
- $nConfidence = $oScanVO->fp_signatures[ $sFilePart ][ $sHashLine ];
141
- }
142
- }
143
- return (int)$nConfidence;
144
- }
145
-
146
- /**
147
- * @param string $sFilePath
148
- * @return int
149
- */
150
- private function getFalsePositiveConfidenceForFile( $sFilePath ) {
151
- /** @var ScanActionVO $oScanVO */
152
- $oScanVO = $this->getScanActionVO();
153
-
154
- $nConfidence = 0;
155
- $sFilePart = basename( $sFilePath );
156
- if ( $oScanVO->confidence_threshold > 0 && isset( $oScanVO->whitelist[ $sFilePart ] ) ) {
157
- try {
158
- $oHasher = new Utilities\File\Compare\CompareHash();
159
- foreach ( $oScanVO->whitelist[ $sFilePart ] as $sWlHash => $nHashConfidence ) {
160
- if ( $oHasher->isEqualFileSha1( $sFilePath, $sWlHash ) ) {
161
- $nConfidence = $nHashConfidence;
162
- break;
163
- }
164
- }
165
- }
166
- catch ( \InvalidArgumentException $oE ) {
167
- }
168
- }
169
- return (int)$nConfidence;
170
- }
171
-
172
  /**
173
  * @param string $sFullPath - normalized
174
  * @return bool
75
  else {
76
  $oAction = $this->getScanActionVO();
77
 
 
78
  if ( $oAction->confidence_threshold > 0 ) {
79
+ $bReportItem = false;
80
+ // 1. First check whether the FP of the whole file means we can filter it
81
+ $nFalsePositiveConfidence = ( new Shield\Scans\Mal\Utilities\FalsePositiveQuery() )
82
+ ->setMod( $this->getMod() )
83
+ ->queryPath( $sFullPath );
84
+ if ( $nFalsePositiveConfidence < $oAction->confidence_threshold ) {
85
+ // 2. Check each line and filter out fp confident lines
86
+ $aLineScores = ( new Shield\Scans\Mal\Utilities\FalsePositiveQuery() )
87
+ ->setMod( $this->getMod() )
88
+ ->queryFileLines( $sFullPath, array_keys( $aLines ) );
89
+ $aLines = array_filter(
90
+ $aLineScores,
91
+ function ( $nScore ) use ( $oAction ) {
92
+ return $nScore < $oAction->confidence_threshold;
93
+ }
94
+ );
95
+
96
+ if ( empty( $aLines ) ) {
97
+ // Now send False Positive report for entire file based on all file lines being FPs.
98
+ ( new Shield\Scans\Mal\Utilities\FalsePositiveReporter() )
99
+ ->setMod( $this->getMod() )
100
+ ->reportPath( $sFullPath, true );
101
+ }
102
+ else {
103
+ $bReportItem = true;
104
  }
105
  }
106
  }
107
+ else {
108
+ $bReportItem = true;
109
+ }
110
 
111
+ if ( $bReportItem ) {
112
+ $oResultItem = $this->getResultItemFromLines( array_keys( $aLines ), $sFullPath, $sSig );
 
 
 
 
113
  }
114
  }
115
  }
142
  || $this->isPluginFileValid( $sFullPath ) || $this->isThemeFileValid( $sFullPath );
143
  }
144
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
145
  /**
146
  * @param string $sFullPath - normalized
147
  * @return bool
src/lib/src/Scans/Mal/Scan.php CHANGED
@@ -24,18 +24,6 @@ class Scan extends Shield\Scans\Base\Files\BaseFileMapScan {
24
  $oScanVO = $this->getScanActionVO();
25
 
26
  $oScanVO->confidence_threshold = $oOpts->getMalConfidenceBoundary();
27
- if ( $oOpts->isMalUseNetworkIntelligence() ) {
28
- $oScanVO->whitelist = ( new Utilities\Whitelist() )
29
- ->setMod( $this->getMod() )
30
- ->retrieve();
31
- $oScanVO->fp_signatures = ( new Utilities\Signatures() )
32
- ->setMod( $this->getMod() )
33
- ->retrieve();
34
- }
35
- else {
36
- $oScanVO->whitelist = [];
37
- $oScanVO->fp_signatures = [];
38
- }
39
 
40
  $aPatterns = ( new Utilities\Patterns() )
41
  ->setMod( $this->getMod() )
24
  $oScanVO = $this->getScanActionVO();
25
 
26
  $oScanVO->confidence_threshold = $oOpts->getMalConfidenceBoundary();
 
 
 
 
 
 
 
 
 
 
 
 
27
 
28
  $aPatterns = ( new Utilities\Patterns() )
29
  ->setMod( $this->getMod() )
src/lib/src/Scans/Mal/ScanActionVO.php CHANGED
@@ -12,8 +12,6 @@ use FernleafSystems\Wordpress\Plugin\Shield\Scans\Base\BaseScanActionVO;
12
  * @property string[] $paths_whitelisted
13
  * @property string[] $patterns_regex
14
  * @property string[] $patterns_simple
15
- * @property string[][] $whitelist
16
- * @property int[] $fp_signatures
17
  * @property int $confidence_threshold
18
  */
19
  class ScanActionVO extends BaseScanActionVO {
12
  * @property string[] $paths_whitelisted
13
  * @property string[] $patterns_regex
14
  * @property string[] $patterns_simple
 
 
15
  * @property int $confidence_threshold
16
  */
17
  class ScanActionVO extends BaseScanActionVO {
src/lib/src/Scans/Mal/Utilities/FalsePositiveQuery.php ADDED
@@ -0,0 +1,81 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace FernleafSystems\Wordpress\Plugin\Shield\Scans\Mal\Utilities;
4
+
5
+ use FernleafSystems\Wordpress\Plugin\Shield\Modules;
6
+ use FernleafSystems\Wordpress\Services\Utilities\File\ExtractLinesFromFile;
7
+ use FernleafSystems\Wordpress\Services\Utilities\Integrations\WpHashes\Malware;
8
+
9
+ /**
10
+ * Class FalsePositiveQuery
11
+ * @package FernleafSystems\Wordpress\Plugin\Shield\Scans\Mal\Utilities
12
+ */
13
+ class FalsePositiveQuery {
14
+
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
+
50
+ $aData = ( new Malware\Confidence\Retrieve() )->retrieveForFile( $sFullPath );
51
+ if ( isset( $aData[ 'score' ] ) ) {
52
+ $nFpConfidence = (int)$aData[ 'score' ];
53
+ }
54
+ }
55
+ return $nFpConfidence;
56
+ }
57
+
58
+ /**
59
+ * @param string $sFile - path to file containing line
60
+ * @param string $sLine
61
+ * @return int
62
+ */
63
+ public function queryLine( $sFile, $sLine ) {
64
+ $nFpConfidence = 0;
65
+
66
+ /** @var Modules\HackGuard\Options $oOpts */
67
+ $oOpts = $this->getOptions();
68
+ if ( $oOpts->isMalUseNetworkIntelligence() ) {
69
+
70
+ try {
71
+ $aData = ( new Malware\Confidence\Retrieve() )->retrieveForFileLine( $sFile, $sLine );
72
+ if ( isset( $aData[ 'score' ] ) ) {
73
+ $nFpConfidence = (int)$aData[ 'score' ];
74
+ }
75
+ }
76
+ catch ( \Exception $oE ) {
77
+ }
78
+ }
79
+ return $nFpConfidence;
80
+ }
81
+ }
src/lib/src/Scans/Mal/Utilities/Signatures.php CHANGED
@@ -9,6 +9,7 @@ use FernleafSystems\Wordpress\Services\Utilities\Integrations\WpHashes\Malware;
9
  /**
10
  * Class Signatures
11
  * @package FernleafSystems\Wordpress\Plugin\Shield\Scans\Mal\Utilities
 
12
  */
13
  class Signatures {
14
 
9
  /**
10
  * Class Signatures
11
  * @package FernleafSystems\Wordpress\Plugin\Shield\Scans\Mal\Utilities
12
+ * @deprecated 8.4
13
  */
14
  class Signatures {
15
 
src/lib/src/Scans/Mal/Utilities/Whitelist.php CHANGED
@@ -9,6 +9,7 @@ use FernleafSystems\Wordpress\Services\Utilities\Integrations\WpHashes\Malware;
9
  /**
10
  * Class Whitelist
11
  * @package FernleafSystems\Wordpress\Plugin\Shield\Scans\Mal\Utilities
 
12
  */
13
  class Whitelist {
14
 
9
  /**
10
  * Class Whitelist
11
  * @package FernleafSystems\Wordpress\Plugin\Shield\Scans\Mal\Utilities
12
+ * @deprecated 8.4
13
  */
14
  class Whitelist {
15
 
src/lib/src/Scans/Ptg/Scan.php CHANGED
@@ -50,7 +50,6 @@ class Scan extends Shield\Scans\Base\BaseScan {
50
 
51
  // use live hashes if it's a WP.org plugin/theme
52
  if ( $bUseLiveHashes ) {
53
-
54
  try {
55
  $oNewRes = ( new WporgAssetScanner() )
56
  ->setScanActionVO( $oAction )
50
 
51
  // use live hashes if it's a WP.org plugin/theme
52
  if ( $bUseLiveHashes ) {
 
53
  try {
54
  $oNewRes = ( new WporgAssetScanner() )
55
  ->setScanActionVO( $oAction )
src/lib/src/Tables/Build/ScanMal.php CHANGED
@@ -52,7 +52,7 @@ class ScanMal extends ScanBase {
52
  $aStatus[] = sprintf( '%s: %s/100 [%s]',
53
  __( 'False Positive Confidence' ),
54
  sprintf( '<strong>%s</strong>', (int)$oIt->fp_confidence ),
55
- sprintf( '<a href="%s" target="_blank">%s&nearr;</a>', 'https://icwp.io/isthismalware', __( 'more info', 'wp-simple-firewall' ) )
56
  );
57
  }
58
 
52
  $aStatus[] = sprintf( '%s: %s/100 [%s]',
53
  __( 'False Positive Confidence' ),
54
  sprintf( '<strong>%s</strong>', (int)$oIt->fp_confidence ),
55
+ sprintf( '<a href="%s" target="_blank">%s&nearr;</a>', 'https://shsec.io/isthismalware', __( 'more info', 'wp-simple-firewall' ) )
56
  );
57
  }
58
 
src/lib/src/Tables/Build/Traffic.php CHANGED
@@ -132,7 +132,7 @@ class Traffic extends BaseBuild {
132
  }
133
 
134
  $sIpLink = sprintf( '<a href="%s" target="_blank" title="IP Whois">%s</a>%s',
135
- $oIpSrv->getIpWhoisLookup( $sIp ), $sIp,
136
  $aEntry[ 'is_you' ] ? ' <span style="font-size: smaller;">('.__( 'You', 'wp-simple-firewall' ).')</span>' : ''
137
  );
138
 
132
  }
133
 
134
  $sIpLink = sprintf( '<a href="%s" target="_blank" title="IP Whois">%s</a>%s',
135
+ $oIpSrv->getIpInfo( $sIp ), $sIp,
136
  $aEntry[ 'is_you' ] ? ' <span style="font-size: smaller;">('.__( 'You', 'wp-simple-firewall' ).')</span>' : ''
137
  );
138
 
src/lib/src/Tables/Render/Base.php CHANGED
@@ -231,13 +231,14 @@ class Base extends \WP_List_Table {
231
  }
232
 
233
  /**
234
- * TODO Put this into SErvice IPs and grab it from there
235
  * @param string $sIp
236
  * @return string
237
  */
238
  protected function getIpWhoisLookupLink( $sIp ) {
 
239
  return sprintf( '<a href="%s" target="_blank" class="ip-whois">%s</a>',
240
- Services::IP()->getIpWhoisLookup( $sIp ),
241
  $sIp
242
  );
243
  }
231
  }
232
 
233
  /**
234
+ * TODO Put this into Service IPs and grab it from there
235
  * @param string $sIp
236
  * @return string
237
  */
238
  protected function getIpWhoisLookupLink( $sIp ) {
239
+ $oIp = Services::IP();
240
  return sprintf( '<a href="%s" target="_blank" class="ip-whois">%s</a>',
241
+ $oIp->isValidIpRange( $sIp ) ? $oIp->getIpWhoisLookup( $sIp ) : $oIp->getIpInfo( $sIp ),
242
  $sIp
243
  );
244
  }
src/lib/src/Utilities/VisitorIpDetection.php DELETED
@@ -1,187 +0,0 @@
1
- <?php
2
-
3
- namespace FernleafSystems\Wordpress\Plugin\Shield\Utilities;
4
-
5
- use FernleafSystems\Wordpress\Services\Services;
6
-
7
- /**
8
- * Class VisitorIpDetection
9
- * @package FernleafSystems\Wordpress\Plugin\Shield\Utilities
10
- * @deprecated 8.3
11
- */
12
- class VisitorIpDetection {
13
-
14
- const DEFAULT_SOURCE = 'REMOTE_ADDR';
15
-
16
- /**
17
- * @var string[]
18
- */
19
- private $aPotentialHostIps;
20
-
21
- /**
22
- * @var string
23
- */
24
- private $sLastSuccessfulSource;
25
-
26
- /**
27
- * @var string
28
- */
29
- private $sPreferredSource;
30
-
31
- /**
32
- * @return string
33
- */
34
- public function detect() {
35
- return $this->runNormalDetection();
36
- }
37
-
38
- /**
39
- * Progressively removes Host IPs from the list so that these don't interfere with detection.
40
- * @return string
41
- */
42
- public function alternativeDetect() {
43
- do {
44
- $sIp = $this->runNormalDetection();
45
- if ( !empty( $sIp ) ) {
46
- break;
47
- }
48
-
49
- // Progressively remove a Host IP until there's none left.
50
- $aHostIps = $this->getPotentialHostIps();
51
- if ( empty( $aHostIps ) ) {
52
- break;
53
- }
54
- array_shift( $aHostIps );
55
- $this->setPotentialHostIps( $aHostIps );
56
- } while ( empty( $sIp ) );
57
-
58
- return $sIp;
59
- }
60
-
61
- /**
62
- * @return string
63
- */
64
- private function runNormalDetection() {
65
- $sSource = '';
66
- $aIps = $this->detectAndFilterFromSource( $this->getPreferredSource() );
67
-
68
- if ( empty( $aIps ) ) { // Couldn't detect IP from preferred source.
69
-
70
- foreach ( $this->getIpSourceOptions() as $sMaybeSource ) {
71
- $aIps = $this->detectAndFilterFromSource( $sMaybeSource );
72
- if ( !empty( $aIps ) ) {
73
- $sSource = $sMaybeSource;
74
- break;
75
- }
76
- }
77
- }
78
- else {
79
- $sSource = $this->getPreferredSource();
80
- }
81
-
82
- $this->sLastSuccessfulSource = $sSource;
83
- return empty( $aIps ) ? '' : array_shift( $aIps );
84
- }
85
-
86
- /**
87
- * @param string $sSource
88
- * @return string[]
89
- */
90
- protected function detectAndFilterFromSource( $sSource ) {
91
- return $this->filterIpsByViable( $this->getIpsFromSource( $sSource ) );
92
- }
93
-
94
- /**
95
- * @param string[] $aIps
96
- * @return string[]
97
- */
98
- protected function filterIpsByViable( $aIps ) {
99
- return array_values( array_filter(
100
- $aIps,
101
- function ( $sIp ) {
102
- $oIP = Services::IP();
103
- return ( $oIP->isValidIp_PublicRemote( $sIp )
104
- && !$oIP->checkIp( $sIp, $this->getPotentialHostIps() )
105
- && !$oIP->isCloudFlareIp( $sIp )
106
- );
107
- }
108
- ) );
109
- }
110
-
111
- /**
112
- * @param string $sSource
113
- * @return string[]
114
- */
115
- protected function getIpsFromSource( $sSource ) {
116
- $sRawSource = (string)Services::Request()->server( $sSource );
117
- return array_filter(
118
- empty( $sRawSource ) ? [] : array_map( 'trim', explode( ',', $sRawSource ) ),
119
- function ( $sIp ) {
120
- $sIp = trim( $sIp, ':' );
121
- /** @var string $sIp */
122
- $nSemi = strpos( $sIp, ':' );
123
- if ( $nSemi !== false ) {
124
- $sIp = substr( $sIp, 0, $nSemi );
125
- }
126
- return filter_var( $sIp, FILTER_VALIDATE_IP ) !== false;
127
- }
128
- );
129
- }
130
-
131
- /**
132
- * @return string[]
133
- */
134
- public function getPotentialHostIps() {
135
- return is_array( $this->aPotentialHostIps ) ? $this->aPotentialHostIps : [];
136
- }
137
-
138
- /**
139
- * @return string
140
- */
141
- public function getLastSuccessfulSource() {
142
- return (string)$this->sLastSuccessfulSource;
143
- }
144
-
145
- /**
146
- * @return string
147
- */
148
- public function getPreferredSource() {
149
- return empty( $this->sPreferredSource ) ? self::DEFAULT_SOURCE : $this->sPreferredSource;
150
- }
151
-
152
- /**
153
- * @param string[] $aPotentialHostIps
154
- * @return $this
155
- */
156
- public function setPotentialHostIps( $aPotentialHostIps ) {
157
- $this->aPotentialHostIps = $aPotentialHostIps;
158
- return $this;
159
- }
160
-
161
- /**
162
- * @param string $sDefaultSource
163
- * @return $this
164
- */
165
- public function setPreferredSource( $sDefaultSource ) {
166
- $this->sPreferredSource = $sDefaultSource;
167
- return $this;
168
- }
169
-
170
- /**
171
- * @return string[]
172
- */
173
- private function getIpSourceOptions() {
174
- return [
175
- 'REMOTE_ADDR',
176
- 'HTTP_CF_CONNECTING_IP',
177
- 'HTTP_X_FORWARDED_FOR',
178
- 'HTTP_X_FORWARDED',
179
- 'HTTP_X_REAL_IP',
180
- 'HTTP_X_SUCURI_CLIENTIP',
181
- 'HTTP_INCAP_CLIENT_IP',
182
- 'HTTP_X_SP_FORWARDED_IP',
183
- 'HTTP_FORWARDED',
184
- 'HTTP_CLIENT_IP'
185
- ];
186
- }
187
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/lib/vendor/composer/autoload_classmap.php CHANGED
@@ -25,7 +25,6 @@ return array(
25
  'Elliotchance\\Iterator\\PagedIteratorTest' => $vendorDir . '/elliotchance/iterator/tests/Elliotchance/Iterator/PagedIteratorTest.php',
26
  'FernleafSystems\\Utilities\\Data\\Adapter\\StdClassAdapter' => $vendorDir . '/fernleafsystems/utilities/src/Data/Adapter/StdClassAdapter.php',
27
  'FernleafSystems\\Utilities\\Response' => $vendorDir . '/fernleafsystems/utilities/src/Response.php',
28
- 'FernleafSystems\\Wordpress\\Plugin\\Shield\\AuditTrail\\Auditor' => $baseDir . '/src/AuditTrail/Auditor.php',
29
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\ChangeTrack\\Diff\\Base' => $baseDir . '/src/ChangeTrack/Diff/Base.php',
30
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\ChangeTrack\\Diff\\DiffComments' => $baseDir . '/src/ChangeTrack/Diff/DiffComments.php',
31
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\ChangeTrack\\Diff\\DiffMedia' => $baseDir . '/src/ChangeTrack/Diff/DiffMedia.php',
@@ -83,6 +82,7 @@ return array(
83
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Databases\\Comments\\Handler' => $baseDir . '/src/Databases/Comments/Handler.php',
84
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Databases\\Comments\\Insert' => $baseDir . '/src/Databases/Comments/Insert.php',
85
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Databases\\Comments\\Select' => $baseDir . '/src/Databases/Comments/Select.php',
 
86
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Databases\\Events\\Delete' => $baseDir . '/src/Databases/Events/Delete.php',
87
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Databases\\Events\\EntryVO' => $baseDir . '/src/Databases/Events/EntryVO.php',
88
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Databases\\Events\\Handler' => $baseDir . '/src/Databases/Events/Handler.php',
@@ -159,15 +159,20 @@ return array(
159
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Base\\ShieldOptions' => $baseDir . '/src/Modules/Base/ShieldOptions.php',
160
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Base\\Strings' => $baseDir . '/src/Modules/Base/Strings.php',
161
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\CommentsFilter\\AdminNotices' => $baseDir . '/src/Modules/CommentsFilter/AdminNotices.php',
 
162
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\CommentsFilter\\Options' => $baseDir . '/src/Modules/CommentsFilter/Options.php',
163
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\CommentsFilter\\Scan\\Bot' => $baseDir . '/src/Modules/CommentsFilter/Scan/Bot.php',
164
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\CommentsFilter\\Scan\\Human' => $baseDir . '/src/Modules/CommentsFilter/Scan/Human.php',
165
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\CommentsFilter\\Scan\\IsEmailTrusted' => $baseDir . '/src/Modules/CommentsFilter/Scan/IsEmailTrusted.php',
166
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\CommentsFilter\\Scan\\Scanner' => $baseDir . '/src/Modules/CommentsFilter/Scan/Scanner.php',
167
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\CommentsFilter\\Strings' => $baseDir . '/src/Modules/CommentsFilter/Strings.php',
 
168
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Email\\Options' => $baseDir . '/src/Modules/Email/Options.php',
169
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Email\\Strings' => $baseDir . '/src/Modules/Email/Strings.php',
170
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Events\\AjaxHandler' => $baseDir . '/src/Modules/Events/AjaxHandler.php',
 
 
 
171
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Events\\Options' => $baseDir . '/src/Modules/Events/Options.php',
172
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Events\\Strings' => $baseDir . '/src/Modules/Events/Strings.php',
173
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Firewall\\Options' => $baseDir . '/src/Modules/Firewall/Options.php',
@@ -208,6 +213,7 @@ return array(
208
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\IPs\\BotTrack\\TrackLoginInvalid' => $baseDir . '/src/Modules/IPs/BotTrack/TrackLoginInvalid.php',
209
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\IPs\\BotTrack\\TrackUserAgent' => $baseDir . '/src/Modules/IPs/BotTrack/TrackUserAgent.php',
210
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\IPs\\BotTrack\\TrackXmlRpc' => $baseDir . '/src/Modules/IPs/BotTrack/TrackXmlRpc.php',
 
211
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\IPs\\Components\\UnblockIpByFlag' => $baseDir . '/src/Modules/IPs/Components/UnblockIpByFlag.php',
212
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\IPs\\Options' => $baseDir . '/src/Modules/IPs/Options.php',
213
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\IPs\\Strings' => $baseDir . '/src/Modules/IPs/Strings.php',
@@ -231,6 +237,7 @@ return array(
231
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Plugin\\AjaxHandler' => $baseDir . '/src/Modules/Plugin/AjaxHandler.php',
232
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Plugin\\Components\\BadgeWidget' => $baseDir . '/src/Modules/Plugin/Components/BadgeWidget.php',
233
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Plugin\\Components\\PluginBadge' => $baseDir . '/src/Modules/Plugin/Components/PluginBadge.php',
 
234
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Plugin\\Options' => $baseDir . '/src/Modules/Plugin/Options.php',
235
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Plugin\\Strings' => $baseDir . '/src/Modules/Plugin/Strings.php',
236
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\SecurityAdmin\\AdminNotices' => $baseDir . '/src/Modules/SecurityAdmin/AdminNotices.php',
@@ -291,6 +298,7 @@ return array(
291
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Scans\\Mal\\Scan' => $baseDir . '/src/Scans/Mal/Scan.php',
292
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Scans\\Mal\\ScanActionVO' => $baseDir . '/src/Scans/Mal/ScanActionVO.php',
293
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Scans\\Mal\\ScanFromFileMap' => $baseDir . '/src/Scans/Mal/ScanFromFileMap.php',
 
294
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Scans\\Mal\\Utilities\\FalsePositiveReporter' => $baseDir . '/src/Scans/Mal/Utilities/FalsePositiveReporter.php',
295
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Scans\\Mal\\Utilities\\Patterns' => $baseDir . '/src/Scans/Mal/Utilities/Patterns.php',
296
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Scans\\Mal\\Utilities\\Signatures' => $baseDir . '/src/Scans/Mal/Utilities/Signatures.php',
@@ -379,7 +387,6 @@ return array(
379
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Utilities\\AdminNotices\\NoticeVO' => $baseDir . '/src/Utilities/AdminNotices/NoticeVO.php',
380
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Utilities\\ReCaptcha\\TestRequest' => $baseDir . '/src/Utilities/ReCaptcha/TestRequest.php',
381
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Utilities\\ReCaptcha\\WordpressPost' => $baseDir . '/src/Utilities/ReCaptcha/WordpressPost.php',
382
- 'FernleafSystems\\Wordpress\\Plugin\\Shield\\Utilities\\VisitorIpDetection' => $baseDir . '/src/Utilities/VisitorIpDetection.php',
383
  'FernleafSystems\\Wordpress\\Services\\Core\\AdminNotices' => $vendorDir . '/fernleafsystems/wordpress-services/src/Core/AdminNotices.php',
384
  'FernleafSystems\\Wordpress\\Services\\Core\\Comments' => $vendorDir . '/fernleafsystems/wordpress-services/src/Core/Comments.php',
385
  'FernleafSystems\\Wordpress\\Services\\Core\\CoreFileHashes' => $vendorDir . '/fernleafsystems/wordpress-services/src/Core/CoreFileHashes.php',
@@ -402,6 +409,7 @@ return array(
402
  'FernleafSystems\\Wordpress\\Services\\Core\\Upgrades\\PluginUpgrader' => $vendorDir . '/fernleafsystems/wordpress-services/src/Core/Upgrades/PluginUpgrader.php',
403
  'FernleafSystems\\Wordpress\\Services\\Core\\Upgrades\\ThemeUpgrader' => $vendorDir . '/fernleafsystems/wordpress-services/src/Core/Upgrades/ThemeUpgrader.php',
404
  'FernleafSystems\\Wordpress\\Services\\Core\\Upgrades\\UpgraderSkin' => $vendorDir . '/fernleafsystems/wordpress-services/src/Core/Upgrades/UpgraderSkin.php',
 
405
  'FernleafSystems\\Wordpress\\Services\\Core\\Users' => $vendorDir . '/fernleafsystems/wordpress-services/src/Core/Users.php',
406
  'FernleafSystems\\Wordpress\\Services\\Core\\VOs\\WpHttpResponseVo' => $vendorDir . '/fernleafsystems/wordpress-services/src/Core/VOs/WpHttpResponseVo.php',
407
  'FernleafSystems\\Wordpress\\Services\\Core\\VOs\\WpPluginVo' => $vendorDir . '/fernleafsystems/wordpress-services/src/Core/VOs/WpPluginVo.php',
@@ -502,14 +510,7 @@ return array(
502
  'FernleafSystems\\Wordpress\\Services\\Utilities\\WpOrg\\Wp\\Versions' => $vendorDir . '/fernleafsystems/wordpress-services/src/Utilities/WpOrg/Wp/Versions.php',
503
  'Html2Text\\Html2Text' => $vendorDir . '/soundasleep/html2text/src/Html2Text.php',
504
  'Html2Text\\Html2TextException' => $vendorDir . '/soundasleep/html2text/src/Html2TextException.php',
505
- 'ICWP_Bulk_Plugin_Upgrader_Skin' => $baseDir . '/../common/icwp-wpupgrades.php',
506
- 'ICWP_Bulk_Theme_Upgrader_Skin' => $baseDir . '/../common/icwp-wpupgrades.php',
507
- 'ICWP_Plugin_Upgrader' => $baseDir . '/../common/icwp-wpupgrades.php',
508
- 'ICWP_Theme_Upgrader' => $baseDir . '/../common/icwp-wpupgrades.php',
509
- 'ICWP_Upgrader_Skin' => $baseDir . '/../common/icwp-wpupgrades.php',
510
- 'ICWP_WPSF_BaseDbProcessor' => $baseDir . '/../processors/basedb.php',
511
  'ICWP_WPSF_DataProcessor' => $baseDir . '/../common/icwp-data.php',
512
- 'ICWP_WPSF_Edd' => $baseDir . '/../common/icwp-edd.php',
513
  'ICWP_WPSF_FeatureHandler_AdminAccessRestriction' => $baseDir . '/../features/admin_access_restriction.php',
514
  'ICWP_WPSF_FeatureHandler_AuditTrail' => $baseDir . '/../features/audit_trail.php',
515
  'ICWP_WPSF_FeatureHandler_Autoupdates' => $baseDir . '/../features/autoupdates.php',
@@ -532,17 +533,12 @@ return array(
532
  'ICWP_WPSF_FeatureHandler_Traffic' => $baseDir . '/../features/traffic.php',
533
  'ICWP_WPSF_FeatureHandler_UserManagement' => $baseDir . '/../features/user_management.php',
534
  'ICWP_WPSF_Foundation' => $baseDir . '/../common/icwp-foundation.php',
535
- 'ICWP_WPSF_OptionsVO' => $baseDir . '/../common/icwp-optionsvo.php',
536
- 'ICWP_WPSF_Plugin_Controller' => $baseDir . '/../../icwp-plugin-controller.php',
537
  'ICWP_WPSF_Processor_AdminAccessRestriction' => $baseDir . '/../processors/admin_access_restriction.php',
538
  'ICWP_WPSF_Processor_AdminAccess_Whitelabel' => $baseDir . '/../processors/adminaccess_whitelabel.php',
539
  'ICWP_WPSF_Processor_AuditTrail' => $baseDir . '/../processors/audit_trail.php',
540
  'ICWP_WPSF_Processor_AuditTrail_Auditor' => $baseDir . '/../processors/audit_trail_auditor.php',
541
  'ICWP_WPSF_Processor_AuditTrail_ChangeTracking' => $baseDir . '/../processors/audit_trail_changetracking.php',
542
  'ICWP_WPSF_Processor_Autoupdates' => $baseDir . '/../processors/autoupdates.php',
543
- 'ICWP_WPSF_Processor_Base' => $baseDir . '/../processors/base.php',
544
- 'ICWP_WPSF_Processor_BasePlugin' => $baseDir . '/../processors/base_plugin.php',
545
- 'ICWP_WPSF_Processor_BaseWpsf' => $baseDir . '/../processors/base_wpsf.php',
546
  'ICWP_WPSF_Processor_CommentsFilter' => $baseDir . '/../processors/comments_filter.php',
547
  'ICWP_WPSF_Processor_CommentsFilter_BotSpam' => $baseDir . '/../processors/commentsfilter_botspam.php',
548
  'ICWP_WPSF_Processor_CommentsFilter_GoogleRecaptcha' => $baseDir . '/../processors/commentsfilter_googlerecaptcha.php',
@@ -591,26 +587,13 @@ return array(
591
  'ICWP_WPSF_Processor_UserManagement_Passwords' => $baseDir . '/../processors/usermanagement_passwords.php',
592
  'ICWP_WPSF_Processor_UserManagement_Sessions' => $baseDir . '/../processors/usermanagement_sessions.php',
593
  'ICWP_WPSF_Processor_UserManagement_Suspend' => $baseDir . '/../processors/usermanagement_suspend.php',
594
- 'ICWP_WPSF_Query_Statistics_Base' => $baseDir . '/../query/base/statistics_base.php',
595
- 'ICWP_WPSF_Query_Statistics_Reporting' => $baseDir . '/../query/statistics/reporting.php',
596
- 'ICWP_WPSF_Render' => $baseDir . '/../common/icwp-render.php',
597
- 'ICWP_WPSF_Request' => $baseDir . '/../common/icwp-request.php',
598
  'ICWP_WPSF_ServiceProviders' => $baseDir . '/../common/icwp-serviceproviders.php',
599
  'ICWP_WPSF_Wizard_Base' => $baseDir . '/../wizards/base.php',
600
  'ICWP_WPSF_Wizard_BaseWpsf' => $baseDir . '/../wizards/base_wpsf.php',
601
  'ICWP_WPSF_Wizard_LoginProtect' => $baseDir . '/../wizards/login_protect.php',
602
  'ICWP_WPSF_Wizard_Plugin' => $baseDir . '/../wizards/plugin.php',
603
  'ICWP_WPSF_WpAdminNotices' => $baseDir . '/../common/wp-admin-notices.php',
604
- 'ICWP_WPSF_WpComments' => $baseDir . '/../common/wp-comments.php',
605
  'ICWP_WPSF_WpCron' => $baseDir . '/../common/icwp-wpcron.php',
606
- 'ICWP_WPSF_WpDb' => $baseDir . '/../common/icwp-wpdb.php',
607
- 'ICWP_WPSF_WpFilesystem' => $baseDir . '/../common/icwp-wpfilesystem.php',
608
- 'ICWP_WPSF_WpFunctions' => $baseDir . '/../common/icwp-wpfunctions.php',
609
- 'ICWP_WPSF_WpFunctions_Plugins' => $baseDir . '/../common/icwp-wpfunctions-plugins.php',
610
- 'ICWP_WPSF_WpFunctions_Themes' => $baseDir . '/../common/icwp-wpfunctions-themes.php',
611
- 'ICWP_WPSF_WpIncludes' => $baseDir . '/../common/icwp-wpincludes.php',
612
- 'ICWP_WPSF_WpUpgrades' => $baseDir . '/../common/icwp-wpupgrades.php',
613
- 'ICWP_WPSF_WpUsers' => $baseDir . '/../common/wp-users.php',
614
  'ICWP_WPSF_WpWidget' => $baseDir . '/../common/wp-widget.php',
615
  'JsonSerializable' => $vendorDir . '/nesbot/carbon/src/JsonSerializable.php',
616
  'LZCompressor\\LZContext' => $vendorDir . '/nullpunkt/lz-string-php/src/LZCompressor/LZContext.php',
25
  'Elliotchance\\Iterator\\PagedIteratorTest' => $vendorDir . '/elliotchance/iterator/tests/Elliotchance/Iterator/PagedIteratorTest.php',
26
  'FernleafSystems\\Utilities\\Data\\Adapter\\StdClassAdapter' => $vendorDir . '/fernleafsystems/utilities/src/Data/Adapter/StdClassAdapter.php',
27
  'FernleafSystems\\Utilities\\Response' => $vendorDir . '/fernleafsystems/utilities/src/Response.php',
 
28
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\ChangeTrack\\Diff\\Base' => $baseDir . '/src/ChangeTrack/Diff/Base.php',
29
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\ChangeTrack\\Diff\\DiffComments' => $baseDir . '/src/ChangeTrack/Diff/DiffComments.php',
30
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\ChangeTrack\\Diff\\DiffMedia' => $baseDir . '/src/ChangeTrack/Diff/DiffMedia.php',
82
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Databases\\Comments\\Handler' => $baseDir . '/src/Databases/Comments/Handler.php',
83
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Databases\\Comments\\Insert' => $baseDir . '/src/Databases/Comments/Insert.php',
84
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Databases\\Comments\\Select' => $baseDir . '/src/Databases/Comments/Select.php',
85
+ 'FernleafSystems\\Wordpress\\Plugin\\Shield\\Databases\\Events\\Common' => $baseDir . '/src/Databases/Events/Common.php',
86
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Databases\\Events\\Delete' => $baseDir . '/src/Databases/Events/Delete.php',
87
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Databases\\Events\\EntryVO' => $baseDir . '/src/Databases/Events/EntryVO.php',
88
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Databases\\Events\\Handler' => $baseDir . '/src/Databases/Events/Handler.php',
159
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Base\\ShieldOptions' => $baseDir . '/src/Modules/Base/ShieldOptions.php',
160
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Base\\Strings' => $baseDir . '/src/Modules/Base/Strings.php',
161
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\CommentsFilter\\AdminNotices' => $baseDir . '/src/Modules/CommentsFilter/AdminNotices.php',
162
+ 'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\CommentsFilter\\AjaxHandler' => $baseDir . '/src/Modules/CommentsFilter/AjaxHandler.php',
163
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\CommentsFilter\\Options' => $baseDir . '/src/Modules/CommentsFilter/Options.php',
164
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\CommentsFilter\\Scan\\Bot' => $baseDir . '/src/Modules/CommentsFilter/Scan/Bot.php',
165
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\CommentsFilter\\Scan\\Human' => $baseDir . '/src/Modules/CommentsFilter/Scan/Human.php',
166
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\CommentsFilter\\Scan\\IsEmailTrusted' => $baseDir . '/src/Modules/CommentsFilter/Scan/IsEmailTrusted.php',
167
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\CommentsFilter\\Scan\\Scanner' => $baseDir . '/src/Modules/CommentsFilter/Scan/Scanner.php',
168
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\CommentsFilter\\Strings' => $baseDir . '/src/Modules/CommentsFilter/Strings.php',
169
+ 'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\CommentsFilter\\Token\\Create' => $baseDir . '/src/Modules/CommentsFilter/Token/Create.php',
170
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Email\\Options' => $baseDir . '/src/Modules/Email/Options.php',
171
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Email\\Strings' => $baseDir . '/src/Modules/Email/Strings.php',
172
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Events\\AjaxHandler' => $baseDir . '/src/Modules/Events/AjaxHandler.php',
173
+ 'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Events\\Charts\\BuildData' => $baseDir . '/src/Modules/Events/Charts/BuildData.php',
174
+ 'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Events\\Charts\\ChartRequestVO' => $baseDir . '/src/Modules/Events/Charts/ChartRequestVO.php',
175
+ 'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Events\\Consolidate\\ConsolidateAllEvents' => $baseDir . '/src/Modules/Events/Consolidate/ConsolidateAllEvents.php',
176
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Events\\Options' => $baseDir . '/src/Modules/Events/Options.php',
177
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Events\\Strings' => $baseDir . '/src/Modules/Events/Strings.php',
178
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Firewall\\Options' => $baseDir . '/src/Modules/Firewall/Options.php',
213
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\IPs\\BotTrack\\TrackLoginInvalid' => $baseDir . '/src/Modules/IPs/BotTrack/TrackLoginInvalid.php',
214
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\IPs\\BotTrack\\TrackUserAgent' => $baseDir . '/src/Modules/IPs/BotTrack/TrackUserAgent.php',
215
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\IPs\\BotTrack\\TrackXmlRpc' => $baseDir . '/src/Modules/IPs/BotTrack/TrackXmlRpc.php',
216
+ 'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\IPs\\Components\\LookupIpOnList' => $baseDir . '/src/Modules/IPs/Components/LookupIpOnList.php',
217
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\IPs\\Components\\UnblockIpByFlag' => $baseDir . '/src/Modules/IPs/Components/UnblockIpByFlag.php',
218
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\IPs\\Options' => $baseDir . '/src/Modules/IPs/Options.php',
219
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\IPs\\Strings' => $baseDir . '/src/Modules/IPs/Strings.php',
237
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Plugin\\AjaxHandler' => $baseDir . '/src/Modules/Plugin/AjaxHandler.php',
238
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Plugin\\Components\\BadgeWidget' => $baseDir . '/src/Modules/Plugin/Components/BadgeWidget.php',
239
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Plugin\\Components\\PluginBadge' => $baseDir . '/src/Modules/Plugin/Components/PluginBadge.php',
240
+ 'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Plugin\\Components\\SiteGroundPluginCompatibility' => $baseDir . '/src/Modules/Plugin/Components/SiteGroundPluginCompatibility.php',
241
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Plugin\\Options' => $baseDir . '/src/Modules/Plugin/Options.php',
242
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Plugin\\Strings' => $baseDir . '/src/Modules/Plugin/Strings.php',
243
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\SecurityAdmin\\AdminNotices' => $baseDir . '/src/Modules/SecurityAdmin/AdminNotices.php',
298
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Scans\\Mal\\Scan' => $baseDir . '/src/Scans/Mal/Scan.php',
299
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Scans\\Mal\\ScanActionVO' => $baseDir . '/src/Scans/Mal/ScanActionVO.php',
300
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Scans\\Mal\\ScanFromFileMap' => $baseDir . '/src/Scans/Mal/ScanFromFileMap.php',
301
+ 'FernleafSystems\\Wordpress\\Plugin\\Shield\\Scans\\Mal\\Utilities\\FalsePositiveQuery' => $baseDir . '/src/Scans/Mal/Utilities/FalsePositiveQuery.php',
302
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Scans\\Mal\\Utilities\\FalsePositiveReporter' => $baseDir . '/src/Scans/Mal/Utilities/FalsePositiveReporter.php',
303
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Scans\\Mal\\Utilities\\Patterns' => $baseDir . '/src/Scans/Mal/Utilities/Patterns.php',
304
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Scans\\Mal\\Utilities\\Signatures' => $baseDir . '/src/Scans/Mal/Utilities/Signatures.php',
387
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Utilities\\AdminNotices\\NoticeVO' => $baseDir . '/src/Utilities/AdminNotices/NoticeVO.php',
388
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Utilities\\ReCaptcha\\TestRequest' => $baseDir . '/src/Utilities/ReCaptcha/TestRequest.php',
389
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Utilities\\ReCaptcha\\WordpressPost' => $baseDir . '/src/Utilities/ReCaptcha/WordpressPost.php',
 
390
  'FernleafSystems\\Wordpress\\Services\\Core\\AdminNotices' => $vendorDir . '/fernleafsystems/wordpress-services/src/Core/AdminNotices.php',
391
  'FernleafSystems\\Wordpress\\Services\\Core\\Comments' => $vendorDir . '/fernleafsystems/wordpress-services/src/Core/Comments.php',
392
  'FernleafSystems\\Wordpress\\Services\\Core\\CoreFileHashes' => $vendorDir . '/fernleafsystems/wordpress-services/src/Core/CoreFileHashes.php',
409
  'FernleafSystems\\Wordpress\\Services\\Core\\Upgrades\\PluginUpgrader' => $vendorDir . '/fernleafsystems/wordpress-services/src/Core/Upgrades/PluginUpgrader.php',
410
  'FernleafSystems\\Wordpress\\Services\\Core\\Upgrades\\ThemeUpgrader' => $vendorDir . '/fernleafsystems/wordpress-services/src/Core/Upgrades/ThemeUpgrader.php',
411
  'FernleafSystems\\Wordpress\\Services\\Core\\Upgrades\\UpgraderSkin' => $vendorDir . '/fernleafsystems/wordpress-services/src/Core/Upgrades/UpgraderSkin.php',
412
+ 'FernleafSystems\\Wordpress\\Services\\Core\\Upgrades\\UpgraderSkinLegacy' => $vendorDir . '/fernleafsystems/wordpress-services/src/Core/Upgrades/UpgraderSkinLegacy.php',
413
  'FernleafSystems\\Wordpress\\Services\\Core\\Users' => $vendorDir . '/fernleafsystems/wordpress-services/src/Core/Users.php',
414
  'FernleafSystems\\Wordpress\\Services\\Core\\VOs\\WpHttpResponseVo' => $vendorDir . '/fernleafsystems/wordpress-services/src/Core/VOs/WpHttpResponseVo.php',
415
  'FernleafSystems\\Wordpress\\Services\\Core\\VOs\\WpPluginVo' => $vendorDir . '/fernleafsystems/wordpress-services/src/Core/VOs/WpPluginVo.php',
510
  'FernleafSystems\\Wordpress\\Services\\Utilities\\WpOrg\\Wp\\Versions' => $vendorDir . '/fernleafsystems/wordpress-services/src/Utilities/WpOrg/Wp/Versions.php',
511
  'Html2Text\\Html2Text' => $vendorDir . '/soundasleep/html2text/src/Html2Text.php',
512
  'Html2Text\\Html2TextException' => $vendorDir . '/soundasleep/html2text/src/Html2TextException.php',
 
 
 
 
 
 
513
  'ICWP_WPSF_DataProcessor' => $baseDir . '/../common/icwp-data.php',
 
514
  'ICWP_WPSF_FeatureHandler_AdminAccessRestriction' => $baseDir . '/../features/admin_access_restriction.php',
515
  'ICWP_WPSF_FeatureHandler_AuditTrail' => $baseDir . '/../features/audit_trail.php',
516
  'ICWP_WPSF_FeatureHandler_Autoupdates' => $baseDir . '/../features/autoupdates.php',
533
  'ICWP_WPSF_FeatureHandler_Traffic' => $baseDir . '/../features/traffic.php',
534
  'ICWP_WPSF_FeatureHandler_UserManagement' => $baseDir . '/../features/user_management.php',
535
  'ICWP_WPSF_Foundation' => $baseDir . '/../common/icwp-foundation.php',
 
 
536
  'ICWP_WPSF_Processor_AdminAccessRestriction' => $baseDir . '/../processors/admin_access_restriction.php',
537
  'ICWP_WPSF_Processor_AdminAccess_Whitelabel' => $baseDir . '/../processors/adminaccess_whitelabel.php',
538
  'ICWP_WPSF_Processor_AuditTrail' => $baseDir . '/../processors/audit_trail.php',
539
  'ICWP_WPSF_Processor_AuditTrail_Auditor' => $baseDir . '/../processors/audit_trail_auditor.php',
540
  'ICWP_WPSF_Processor_AuditTrail_ChangeTracking' => $baseDir . '/../processors/audit_trail_changetracking.php',
541
  'ICWP_WPSF_Processor_Autoupdates' => $baseDir . '/../processors/autoupdates.php',
 
 
 
542
  'ICWP_WPSF_Processor_CommentsFilter' => $baseDir . '/../processors/comments_filter.php',
543
  'ICWP_WPSF_Processor_CommentsFilter_BotSpam' => $baseDir . '/../processors/commentsfilter_botspam.php',
544
  'ICWP_WPSF_Processor_CommentsFilter_GoogleRecaptcha' => $baseDir . '/../processors/commentsfilter_googlerecaptcha.php',
587
  'ICWP_WPSF_Processor_UserManagement_Passwords' => $baseDir . '/../processors/usermanagement_passwords.php',
588
  'ICWP_WPSF_Processor_UserManagement_Sessions' => $baseDir . '/../processors/usermanagement_sessions.php',
589
  'ICWP_WPSF_Processor_UserManagement_Suspend' => $baseDir . '/../processors/usermanagement_suspend.php',
 
 
 
 
590
  'ICWP_WPSF_ServiceProviders' => $baseDir . '/../common/icwp-serviceproviders.php',
591
  'ICWP_WPSF_Wizard_Base' => $baseDir . '/../wizards/base.php',
592
  'ICWP_WPSF_Wizard_BaseWpsf' => $baseDir . '/../wizards/base_wpsf.php',
593
  'ICWP_WPSF_Wizard_LoginProtect' => $baseDir . '/../wizards/login_protect.php',
594
  'ICWP_WPSF_Wizard_Plugin' => $baseDir . '/../wizards/plugin.php',
595
  'ICWP_WPSF_WpAdminNotices' => $baseDir . '/../common/wp-admin-notices.php',
 
596
  'ICWP_WPSF_WpCron' => $baseDir . '/../common/icwp-wpcron.php',
 
 
 
 
 
 
 
 
597
  'ICWP_WPSF_WpWidget' => $baseDir . '/../common/wp-widget.php',
598
  'JsonSerializable' => $vendorDir . '/nesbot/carbon/src/JsonSerializable.php',
599
  'LZCompressor\\LZContext' => $vendorDir . '/nullpunkt/lz-string-php/src/LZCompressor/LZContext.php',
src/lib/vendor/composer/autoload_static.php CHANGED
@@ -174,7 +174,6 @@ class ComposerStaticInitfcf2fe1888f1f5fc092770cdc8ef3cf4
174
  'Elliotchance\\Iterator\\PagedIteratorTest' => __DIR__ . '/..' . '/elliotchance/iterator/tests/Elliotchance/Iterator/PagedIteratorTest.php',
175
  'FernleafSystems\\Utilities\\Data\\Adapter\\StdClassAdapter' => __DIR__ . '/..' . '/fernleafsystems/utilities/src/Data/Adapter/StdClassAdapter.php',
176
  'FernleafSystems\\Utilities\\Response' => __DIR__ . '/..' . '/fernleafsystems/utilities/src/Response.php',
177
- 'FernleafSystems\\Wordpress\\Plugin\\Shield\\AuditTrail\\Auditor' => __DIR__ . '/../..' . '/src/AuditTrail/Auditor.php',
178
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\ChangeTrack\\Diff\\Base' => __DIR__ . '/../..' . '/src/ChangeTrack/Diff/Base.php',
179
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\ChangeTrack\\Diff\\DiffComments' => __DIR__ . '/../..' . '/src/ChangeTrack/Diff/DiffComments.php',
180
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\ChangeTrack\\Diff\\DiffMedia' => __DIR__ . '/../..' . '/src/ChangeTrack/Diff/DiffMedia.php',
@@ -232,6 +231,7 @@ class ComposerStaticInitfcf2fe1888f1f5fc092770cdc8ef3cf4
232
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Databases\\Comments\\Handler' => __DIR__ . '/../..' . '/src/Databases/Comments/Handler.php',
233
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Databases\\Comments\\Insert' => __DIR__ . '/../..' . '/src/Databases/Comments/Insert.php',
234
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Databases\\Comments\\Select' => __DIR__ . '/../..' . '/src/Databases/Comments/Select.php',
 
235
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Databases\\Events\\Delete' => __DIR__ . '/../..' . '/src/Databases/Events/Delete.php',
236
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Databases\\Events\\EntryVO' => __DIR__ . '/../..' . '/src/Databases/Events/EntryVO.php',
237
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Databases\\Events\\Handler' => __DIR__ . '/../..' . '/src/Databases/Events/Handler.php',
@@ -308,15 +308,20 @@ class ComposerStaticInitfcf2fe1888f1f5fc092770cdc8ef3cf4
308
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Base\\ShieldOptions' => __DIR__ . '/../..' . '/src/Modules/Base/ShieldOptions.php',
309
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Base\\Strings' => __DIR__ . '/../..' . '/src/Modules/Base/Strings.php',
310
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\CommentsFilter\\AdminNotices' => __DIR__ . '/../..' . '/src/Modules/CommentsFilter/AdminNotices.php',
 
311
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\CommentsFilter\\Options' => __DIR__ . '/../..' . '/src/Modules/CommentsFilter/Options.php',
312
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\CommentsFilter\\Scan\\Bot' => __DIR__ . '/../..' . '/src/Modules/CommentsFilter/Scan/Bot.php',
313
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\CommentsFilter\\Scan\\Human' => __DIR__ . '/../..' . '/src/Modules/CommentsFilter/Scan/Human.php',
314
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\CommentsFilter\\Scan\\IsEmailTrusted' => __DIR__ . '/../..' . '/src/Modules/CommentsFilter/Scan/IsEmailTrusted.php',
315
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\CommentsFilter\\Scan\\Scanner' => __DIR__ . '/../..' . '/src/Modules/CommentsFilter/Scan/Scanner.php',
316
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\CommentsFilter\\Strings' => __DIR__ . '/../..' . '/src/Modules/CommentsFilter/Strings.php',
 
317
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Email\\Options' => __DIR__ . '/../..' . '/src/Modules/Email/Options.php',
318
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Email\\Strings' => __DIR__ . '/../..' . '/src/Modules/Email/Strings.php',
319
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Events\\AjaxHandler' => __DIR__ . '/../..' . '/src/Modules/Events/AjaxHandler.php',
 
 
 
320
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Events\\Options' => __DIR__ . '/../..' . '/src/Modules/Events/Options.php',
321
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Events\\Strings' => __DIR__ . '/../..' . '/src/Modules/Events/Strings.php',
322
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Firewall\\Options' => __DIR__ . '/../..' . '/src/Modules/Firewall/Options.php',
@@ -357,6 +362,7 @@ class ComposerStaticInitfcf2fe1888f1f5fc092770cdc8ef3cf4
357
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\IPs\\BotTrack\\TrackLoginInvalid' => __DIR__ . '/../..' . '/src/Modules/IPs/BotTrack/TrackLoginInvalid.php',
358
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\IPs\\BotTrack\\TrackUserAgent' => __DIR__ . '/../..' . '/src/Modules/IPs/BotTrack/TrackUserAgent.php',
359
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\IPs\\BotTrack\\TrackXmlRpc' => __DIR__ . '/../..' . '/src/Modules/IPs/BotTrack/TrackXmlRpc.php',
 
360
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\IPs\\Components\\UnblockIpByFlag' => __DIR__ . '/../..' . '/src/Modules/IPs/Components/UnblockIpByFlag.php',
361
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\IPs\\Options' => __DIR__ . '/../..' . '/src/Modules/IPs/Options.php',
362
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\IPs\\Strings' => __DIR__ . '/../..' . '/src/Modules/IPs/Strings.php',
@@ -380,6 +386,7 @@ class ComposerStaticInitfcf2fe1888f1f5fc092770cdc8ef3cf4
380
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Plugin\\AjaxHandler' => __DIR__ . '/../..' . '/src/Modules/Plugin/AjaxHandler.php',
381
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Plugin\\Components\\BadgeWidget' => __DIR__ . '/../..' . '/src/Modules/Plugin/Components/BadgeWidget.php',
382
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Plugin\\Components\\PluginBadge' => __DIR__ . '/../..' . '/src/Modules/Plugin/Components/PluginBadge.php',
 
383
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Plugin\\Options' => __DIR__ . '/../..' . '/src/Modules/Plugin/Options.php',
384
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Plugin\\Strings' => __DIR__ . '/../..' . '/src/Modules/Plugin/Strings.php',
385
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\SecurityAdmin\\AdminNotices' => __DIR__ . '/../..' . '/src/Modules/SecurityAdmin/AdminNotices.php',
@@ -440,6 +447,7 @@ class ComposerStaticInitfcf2fe1888f1f5fc092770cdc8ef3cf4
440
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Scans\\Mal\\Scan' => __DIR__ . '/../..' . '/src/Scans/Mal/Scan.php',
441
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Scans\\Mal\\ScanActionVO' => __DIR__ . '/../..' . '/src/Scans/Mal/ScanActionVO.php',
442
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Scans\\Mal\\ScanFromFileMap' => __DIR__ . '/../..' . '/src/Scans/Mal/ScanFromFileMap.php',
 
443
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Scans\\Mal\\Utilities\\FalsePositiveReporter' => __DIR__ . '/../..' . '/src/Scans/Mal/Utilities/FalsePositiveReporter.php',
444
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Scans\\Mal\\Utilities\\Patterns' => __DIR__ . '/../..' . '/src/Scans/Mal/Utilities/Patterns.php',
445
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Scans\\Mal\\Utilities\\Signatures' => __DIR__ . '/../..' . '/src/Scans/Mal/Utilities/Signatures.php',
@@ -528,7 +536,6 @@ class ComposerStaticInitfcf2fe1888f1f5fc092770cdc8ef3cf4
528
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Utilities\\AdminNotices\\NoticeVO' => __DIR__ . '/../..' . '/src/Utilities/AdminNotices/NoticeVO.php',
529
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Utilities\\ReCaptcha\\TestRequest' => __DIR__ . '/../..' . '/src/Utilities/ReCaptcha/TestRequest.php',
530
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Utilities\\ReCaptcha\\WordpressPost' => __DIR__ . '/../..' . '/src/Utilities/ReCaptcha/WordpressPost.php',
531
- 'FernleafSystems\\Wordpress\\Plugin\\Shield\\Utilities\\VisitorIpDetection' => __DIR__ . '/../..' . '/src/Utilities/VisitorIpDetection.php',
532
  'FernleafSystems\\Wordpress\\Services\\Core\\AdminNotices' => __DIR__ . '/..' . '/fernleafsystems/wordpress-services/src/Core/AdminNotices.php',
533
  'FernleafSystems\\Wordpress\\Services\\Core\\Comments' => __DIR__ . '/..' . '/fernleafsystems/wordpress-services/src/Core/Comments.php',
534
  'FernleafSystems\\Wordpress\\Services\\Core\\CoreFileHashes' => __DIR__ . '/..' . '/fernleafsystems/wordpress-services/src/Core/CoreFileHashes.php',
@@ -551,6 +558,7 @@ class ComposerStaticInitfcf2fe1888f1f5fc092770cdc8ef3cf4
551
  'FernleafSystems\\Wordpress\\Services\\Core\\Upgrades\\PluginUpgrader' => __DIR__ . '/..' . '/fernleafsystems/wordpress-services/src/Core/Upgrades/PluginUpgrader.php',
552
  'FernleafSystems\\Wordpress\\Services\\Core\\Upgrades\\ThemeUpgrader' => __DIR__ . '/..' . '/fernleafsystems/wordpress-services/src/Core/Upgrades/ThemeUpgrader.php',
553
  'FernleafSystems\\Wordpress\\Services\\Core\\Upgrades\\UpgraderSkin' => __DIR__ . '/..' . '/fernleafsystems/wordpress-services/src/Core/Upgrades/UpgraderSkin.php',
 
554
  'FernleafSystems\\Wordpress\\Services\\Core\\Users' => __DIR__ . '/..' . '/fernleafsystems/wordpress-services/src/Core/Users.php',
555
  'FernleafSystems\\Wordpress\\Services\\Core\\VOs\\WpHttpResponseVo' => __DIR__ . '/..' . '/fernleafsystems/wordpress-services/src/Core/VOs/WpHttpResponseVo.php',
556
  'FernleafSystems\\Wordpress\\Services\\Core\\VOs\\WpPluginVo' => __DIR__ . '/..' . '/fernleafsystems/wordpress-services/src/Core/VOs/WpPluginVo.php',
@@ -651,14 +659,7 @@ class ComposerStaticInitfcf2fe1888f1f5fc092770cdc8ef3cf4
651
  'FernleafSystems\\Wordpress\\Services\\Utilities\\WpOrg\\Wp\\Versions' => __DIR__ . '/..' . '/fernleafsystems/wordpress-services/src/Utilities/WpOrg/Wp/Versions.php',
652
  'Html2Text\\Html2Text' => __DIR__ . '/..' . '/soundasleep/html2text/src/Html2Text.php',
653
  'Html2Text\\Html2TextException' => __DIR__ . '/..' . '/soundasleep/html2text/src/Html2TextException.php',
654
- 'ICWP_Bulk_Plugin_Upgrader_Skin' => __DIR__ . '/../..' . '/../common/icwp-wpupgrades.php',
655
- 'ICWP_Bulk_Theme_Upgrader_Skin' => __DIR__ . '/../..' . '/../common/icwp-wpupgrades.php',
656
- 'ICWP_Plugin_Upgrader' => __DIR__ . '/../..' . '/../common/icwp-wpupgrades.php',
657
- 'ICWP_Theme_Upgrader' => __DIR__ . '/../..' . '/../common/icwp-wpupgrades.php',
658
- 'ICWP_Upgrader_Skin' => __DIR__ . '/../..' . '/../common/icwp-wpupgrades.php',
659
- 'ICWP_WPSF_BaseDbProcessor' => __DIR__ . '/../..' . '/../processors/basedb.php',
660
  'ICWP_WPSF_DataProcessor' => __DIR__ . '/../..' . '/../common/icwp-data.php',
661
- 'ICWP_WPSF_Edd' => __DIR__ . '/../..' . '/../common/icwp-edd.php',
662
  'ICWP_WPSF_FeatureHandler_AdminAccessRestriction' => __DIR__ . '/../..' . '/../features/admin_access_restriction.php',
663
  'ICWP_WPSF_FeatureHandler_AuditTrail' => __DIR__ . '/../..' . '/../features/audit_trail.php',
664
  'ICWP_WPSF_FeatureHandler_Autoupdates' => __DIR__ . '/../..' . '/../features/autoupdates.php',
@@ -681,17 +682,12 @@ class ComposerStaticInitfcf2fe1888f1f5fc092770cdc8ef3cf4
681
  'ICWP_WPSF_FeatureHandler_Traffic' => __DIR__ . '/../..' . '/../features/traffic.php',
682
  'ICWP_WPSF_FeatureHandler_UserManagement' => __DIR__ . '/../..' . '/../features/user_management.php',
683
  'ICWP_WPSF_Foundation' => __DIR__ . '/../..' . '/../common/icwp-foundation.php',
684
- 'ICWP_WPSF_OptionsVO' => __DIR__ . '/../..' . '/../common/icwp-optionsvo.php',
685
- 'ICWP_WPSF_Plugin_Controller' => __DIR__ . '/../..' . '/../../icwp-plugin-controller.php',
686
  'ICWP_WPSF_Processor_AdminAccessRestriction' => __DIR__ . '/../..' . '/../processors/admin_access_restriction.php',
687
  'ICWP_WPSF_Processor_AdminAccess_Whitelabel' => __DIR__ . '/../..' . '/../processors/adminaccess_whitelabel.php',
688
  'ICWP_WPSF_Processor_AuditTrail' => __DIR__ . '/../..' . '/../processors/audit_trail.php',
689
  'ICWP_WPSF_Processor_AuditTrail_Auditor' => __DIR__ . '/../..' . '/../processors/audit_trail_auditor.php',
690
  'ICWP_WPSF_Processor_AuditTrail_ChangeTracking' => __DIR__ . '/../..' . '/../processors/audit_trail_changetracking.php',
691
  'ICWP_WPSF_Processor_Autoupdates' => __DIR__ . '/../..' . '/../processors/autoupdates.php',
692
- 'ICWP_WPSF_Processor_Base' => __DIR__ . '/../..' . '/../processors/base.php',
693
- 'ICWP_WPSF_Processor_BasePlugin' => __DIR__ . '/../..' . '/../processors/base_plugin.php',
694
- 'ICWP_WPSF_Processor_BaseWpsf' => __DIR__ . '/../..' . '/../processors/base_wpsf.php',
695
  'ICWP_WPSF_Processor_CommentsFilter' => __DIR__ . '/../..' . '/../processors/comments_filter.php',
696
  'ICWP_WPSF_Processor_CommentsFilter_BotSpam' => __DIR__ . '/../..' . '/../processors/commentsfilter_botspam.php',
697
  'ICWP_WPSF_Processor_CommentsFilter_GoogleRecaptcha' => __DIR__ . '/../..' . '/../processors/commentsfilter_googlerecaptcha.php',
@@ -740,26 +736,13 @@ class ComposerStaticInitfcf2fe1888f1f5fc092770cdc8ef3cf4
740
  'ICWP_WPSF_Processor_UserManagement_Passwords' => __DIR__ . '/../..' . '/../processors/usermanagement_passwords.php',
741
  'ICWP_WPSF_Processor_UserManagement_Sessions' => __DIR__ . '/../..' . '/../processors/usermanagement_sessions.php',
742
  'ICWP_WPSF_Processor_UserManagement_Suspend' => __DIR__ . '/../..' . '/../processors/usermanagement_suspend.php',
743
- 'ICWP_WPSF_Query_Statistics_Base' => __DIR__ . '/../..' . '/../query/base/statistics_base.php',
744
- 'ICWP_WPSF_Query_Statistics_Reporting' => __DIR__ . '/../..' . '/../query/statistics/reporting.php',
745
- 'ICWP_WPSF_Render' => __DIR__ . '/../..' . '/../common/icwp-render.php',
746
- 'ICWP_WPSF_Request' => __DIR__ . '/../..' . '/../common/icwp-request.php',
747
  'ICWP_WPSF_ServiceProviders' => __DIR__ . '/../..' . '/../common/icwp-serviceproviders.php',
748
  'ICWP_WPSF_Wizard_Base' => __DIR__ . '/../..' . '/../wizards/base.php',
749
  'ICWP_WPSF_Wizard_BaseWpsf' => __DIR__ . '/../..' . '/../wizards/base_wpsf.php',
750
  'ICWP_WPSF_Wizard_LoginProtect' => __DIR__ . '/../..' . '/../wizards/login_protect.php',
751
  'ICWP_WPSF_Wizard_Plugin' => __DIR__ . '/../..' . '/../wizards/plugin.php',
752
  'ICWP_WPSF_WpAdminNotices' => __DIR__ . '/../..' . '/../common/wp-admin-notices.php',
753
- 'ICWP_WPSF_WpComments' => __DIR__ . '/../..' . '/../common/wp-comments.php',
754
  'ICWP_WPSF_WpCron' => __DIR__ . '/../..' . '/../common/icwp-wpcron.php',
755
- 'ICWP_WPSF_WpDb' => __DIR__ . '/../..' . '/../common/icwp-wpdb.php',
756
- 'ICWP_WPSF_WpFilesystem' => __DIR__ . '/../..' . '/../common/icwp-wpfilesystem.php',
757
- 'ICWP_WPSF_WpFunctions' => __DIR__ . '/../..' . '/../common/icwp-wpfunctions.php',
758
- 'ICWP_WPSF_WpFunctions_Plugins' => __DIR__ . '/../..' . '/../common/icwp-wpfunctions-plugins.php',
759
- 'ICWP_WPSF_WpFunctions_Themes' => __DIR__ . '/../..' . '/../common/icwp-wpfunctions-themes.php',
760
- 'ICWP_WPSF_WpIncludes' => __DIR__ . '/../..' . '/../common/icwp-wpincludes.php',
761
- 'ICWP_WPSF_WpUpgrades' => __DIR__ . '/../..' . '/../common/icwp-wpupgrades.php',
762
- 'ICWP_WPSF_WpUsers' => __DIR__ . '/../..' . '/../common/wp-users.php',
763
  'ICWP_WPSF_WpWidget' => __DIR__ . '/../..' . '/../common/wp-widget.php',
764
  'JsonSerializable' => __DIR__ . '/..' . '/nesbot/carbon/src/JsonSerializable.php',
765
  'LZCompressor\\LZContext' => __DIR__ . '/..' . '/nullpunkt/lz-string-php/src/LZCompressor/LZContext.php',
174
  'Elliotchance\\Iterator\\PagedIteratorTest' => __DIR__ . '/..' . '/elliotchance/iterator/tests/Elliotchance/Iterator/PagedIteratorTest.php',
175
  'FernleafSystems\\Utilities\\Data\\Adapter\\StdClassAdapter' => __DIR__ . '/..' . '/fernleafsystems/utilities/src/Data/Adapter/StdClassAdapter.php',
176
  'FernleafSystems\\Utilities\\Response' => __DIR__ . '/..' . '/fernleafsystems/utilities/src/Response.php',
 
177
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\ChangeTrack\\Diff\\Base' => __DIR__ . '/../..' . '/src/ChangeTrack/Diff/Base.php',
178
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\ChangeTrack\\Diff\\DiffComments' => __DIR__ . '/../..' . '/src/ChangeTrack/Diff/DiffComments.php',
179
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\ChangeTrack\\Diff\\DiffMedia' => __DIR__ . '/../..' . '/src/ChangeTrack/Diff/DiffMedia.php',
231
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Databases\\Comments\\Handler' => __DIR__ . '/../..' . '/src/Databases/Comments/Handler.php',
232
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Databases\\Comments\\Insert' => __DIR__ . '/../..' . '/src/Databases/Comments/Insert.php',
233
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Databases\\Comments\\Select' => __DIR__ . '/../..' . '/src/Databases/Comments/Select.php',
234
+ 'FernleafSystems\\Wordpress\\Plugin\\Shield\\Databases\\Events\\Common' => __DIR__ . '/../..' . '/src/Databases/Events/Common.php',
235
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Databases\\Events\\Delete' => __DIR__ . '/../..' . '/src/Databases/Events/Delete.php',
236
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Databases\\Events\\EntryVO' => __DIR__ . '/../..' . '/src/Databases/Events/EntryVO.php',
237
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Databases\\Events\\Handler' => __DIR__ . '/../..' . '/src/Databases/Events/Handler.php',
308
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Base\\ShieldOptions' => __DIR__ . '/../..' . '/src/Modules/Base/ShieldOptions.php',
309
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Base\\Strings' => __DIR__ . '/../..' . '/src/Modules/Base/Strings.php',
310
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\CommentsFilter\\AdminNotices' => __DIR__ . '/../..' . '/src/Modules/CommentsFilter/AdminNotices.php',
311
+ 'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\CommentsFilter\\AjaxHandler' => __DIR__ . '/../..' . '/src/Modules/CommentsFilter/AjaxHandler.php',
312
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\CommentsFilter\\Options' => __DIR__ . '/../..' . '/src/Modules/CommentsFilter/Options.php',
313
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\CommentsFilter\\Scan\\Bot' => __DIR__ . '/../..' . '/src/Modules/CommentsFilter/Scan/Bot.php',
314
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\CommentsFilter\\Scan\\Human' => __DIR__ . '/../..' . '/src/Modules/CommentsFilter/Scan/Human.php',
315
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\CommentsFilter\\Scan\\IsEmailTrusted' => __DIR__ . '/../..' . '/src/Modules/CommentsFilter/Scan/IsEmailTrusted.php',
316
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\CommentsFilter\\Scan\\Scanner' => __DIR__ . '/../..' . '/src/Modules/CommentsFilter/Scan/Scanner.php',
317
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\CommentsFilter\\Strings' => __DIR__ . '/../..' . '/src/Modules/CommentsFilter/Strings.php',
318
+ 'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\CommentsFilter\\Token\\Create' => __DIR__ . '/../..' . '/src/Modules/CommentsFilter/Token/Create.php',
319
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Email\\Options' => __DIR__ . '/../..' . '/src/Modules/Email/Options.php',
320
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Email\\Strings' => __DIR__ . '/../..' . '/src/Modules/Email/Strings.php',
321
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Events\\AjaxHandler' => __DIR__ . '/../..' . '/src/Modules/Events/AjaxHandler.php',
322
+ 'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Events\\Charts\\BuildData' => __DIR__ . '/../..' . '/src/Modules/Events/Charts/BuildData.php',
323
+ 'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Events\\Charts\\ChartRequestVO' => __DIR__ . '/../..' . '/src/Modules/Events/Charts/ChartRequestVO.php',
324
+ 'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Events\\Consolidate\\ConsolidateAllEvents' => __DIR__ . '/../..' . '/src/Modules/Events/Consolidate/ConsolidateAllEvents.php',
325
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Events\\Options' => __DIR__ . '/../..' . '/src/Modules/Events/Options.php',
326
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Events\\Strings' => __DIR__ . '/../..' . '/src/Modules/Events/Strings.php',
327
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Firewall\\Options' => __DIR__ . '/../..' . '/src/Modules/Firewall/Options.php',
362
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\IPs\\BotTrack\\TrackLoginInvalid' => __DIR__ . '/../..' . '/src/Modules/IPs/BotTrack/TrackLoginInvalid.php',
363
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\IPs\\BotTrack\\TrackUserAgent' => __DIR__ . '/../..' . '/src/Modules/IPs/BotTrack/TrackUserAgent.php',
364
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\IPs\\BotTrack\\TrackXmlRpc' => __DIR__ . '/../..' . '/src/Modules/IPs/BotTrack/TrackXmlRpc.php',
365
+ 'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\IPs\\Components\\LookupIpOnList' => __DIR__ . '/../..' . '/src/Modules/IPs/Components/LookupIpOnList.php',
366
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\IPs\\Components\\UnblockIpByFlag' => __DIR__ . '/../..' . '/src/Modules/IPs/Components/UnblockIpByFlag.php',
367
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\IPs\\Options' => __DIR__ . '/../..' . '/src/Modules/IPs/Options.php',
368
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\IPs\\Strings' => __DIR__ . '/../..' . '/src/Modules/IPs/Strings.php',
386
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Plugin\\AjaxHandler' => __DIR__ . '/../..' . '/src/Modules/Plugin/AjaxHandler.php',
387
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Plugin\\Components\\BadgeWidget' => __DIR__ . '/../..' . '/src/Modules/Plugin/Components/BadgeWidget.php',
388
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Plugin\\Components\\PluginBadge' => __DIR__ . '/../..' . '/src/Modules/Plugin/Components/PluginBadge.php',
389
+ 'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Plugin\\Components\\SiteGroundPluginCompatibility' => __DIR__ . '/../..' . '/src/Modules/Plugin/Components/SiteGroundPluginCompatibility.php',
390
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Plugin\\Options' => __DIR__ . '/../..' . '/src/Modules/Plugin/Options.php',
391
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\Plugin\\Strings' => __DIR__ . '/../..' . '/src/Modules/Plugin/Strings.php',
392
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Modules\\SecurityAdmin\\AdminNotices' => __DIR__ . '/../..' . '/src/Modules/SecurityAdmin/AdminNotices.php',
447
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Scans\\Mal\\Scan' => __DIR__ . '/../..' . '/src/Scans/Mal/Scan.php',
448
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Scans\\Mal\\ScanActionVO' => __DIR__ . '/../..' . '/src/Scans/Mal/ScanActionVO.php',
449
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Scans\\Mal\\ScanFromFileMap' => __DIR__ . '/../..' . '/src/Scans/Mal/ScanFromFileMap.php',
450
+ 'FernleafSystems\\Wordpress\\Plugin\\Shield\\Scans\\Mal\\Utilities\\FalsePositiveQuery' => __DIR__ . '/../..' . '/src/Scans/Mal/Utilities/FalsePositiveQuery.php',
451
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Scans\\Mal\\Utilities\\FalsePositiveReporter' => __DIR__ . '/../..' . '/src/Scans/Mal/Utilities/FalsePositiveReporter.php',
452
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Scans\\Mal\\Utilities\\Patterns' => __DIR__ . '/../..' . '/src/Scans/Mal/Utilities/Patterns.php',
453
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Scans\\Mal\\Utilities\\Signatures' => __DIR__ . '/../..' . '/src/Scans/Mal/Utilities/Signatures.php',
536
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Utilities\\AdminNotices\\NoticeVO' => __DIR__ . '/../..' . '/src/Utilities/AdminNotices/NoticeVO.php',
537
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Utilities\\ReCaptcha\\TestRequest' => __DIR__ . '/../..' . '/src/Utilities/ReCaptcha/TestRequest.php',
538
  'FernleafSystems\\Wordpress\\Plugin\\Shield\\Utilities\\ReCaptcha\\WordpressPost' => __DIR__ . '/../..' . '/src/Utilities/ReCaptcha/WordpressPost.php',
 
539
  'FernleafSystems\\Wordpress\\Services\\Core\\AdminNotices' => __DIR__ . '/..' . '/fernleafsystems/wordpress-services/src/Core/AdminNotices.php',
540
  'FernleafSystems\\Wordpress\\Services\\Core\\Comments' => __DIR__ . '/..' . '/fernleafsystems/wordpress-services/src/Core/Comments.php',
541
  'FernleafSystems\\Wordpress\\Services\\Core\\CoreFileHashes' => __DIR__ . '/..' . '/fernleafsystems/wordpress-services/src/Core/CoreFileHashes.php',
558
  'FernleafSystems\\Wordpress\\Services\\Core\\Upgrades\\PluginUpgrader' => __DIR__ . '/..' . '/fernleafsystems/wordpress-services/src/Core/Upgrades/PluginUpgrader.php',
559
  'FernleafSystems\\Wordpress\\Services\\Core\\Upgrades\\ThemeUpgrader' => __DIR__ . '/..' . '/fernleafsystems/wordpress-services/src/Core/Upgrades/ThemeUpgrader.php',
560
  'FernleafSystems\\Wordpress\\Services\\Core\\Upgrades\\UpgraderSkin' => __DIR__ . '/..' . '/fernleafsystems/wordpress-services/src/Core/Upgrades/UpgraderSkin.php',
561
+ 'FernleafSystems\\Wordpress\\Services\\Core\\Upgrades\\UpgraderSkinLegacy' => __DIR__ . '/..' . '/fernleafsystems/wordpress-services/src/Core/Upgrades/UpgraderSkinLegacy.php',
562
  'FernleafSystems\\Wordpress\\Services\\Core\\Users' => __DIR__ . '/..' . '/fernleafsystems/wordpress-services/src/Core/Users.php',
563
  'FernleafSystems\\Wordpress\\Services\\Core\\VOs\\WpHttpResponseVo' => __DIR__ . '/..' . '/fernleafsystems/wordpress-services/src/Core/VOs/WpHttpResponseVo.php',
564
  'FernleafSystems\\Wordpress\\Services\\Core\\VOs\\WpPluginVo' => __DIR__ . '/..' . '/fernleafsystems/wordpress-services/src/Core/VOs/WpPluginVo.php',
659
  'FernleafSystems\\Wordpress\\Services\\Utilities\\WpOrg\\Wp\\Versions' => __DIR__ . '/..' . '/fernleafsystems/wordpress-services/src/Utilities/WpOrg/Wp/Versions.php',
660
  'Html2Text\\Html2Text' => __DIR__ . '/..' . '/soundasleep/html2text/src/Html2Text.php',
661
  'Html2Text\\Html2TextException' => __DIR__ . '/..' . '/soundasleep/html2text/src/Html2TextException.php',
 
 
 
 
 
 
662
  'ICWP_WPSF_DataProcessor' => __DIR__ . '/../..' . '/../common/icwp-data.php',
 
663
  'ICWP_WPSF_FeatureHandler_AdminAccessRestriction' => __DIR__ . '/../..' . '/../features/admin_access_restriction.php',
664
  'ICWP_WPSF_FeatureHandler_AuditTrail' => __DIR__ . '/../..' . '/../features/audit_trail.php',
665
  'ICWP_WPSF_FeatureHandler_Autoupdates' => __DIR__ . '/../..' . '/../features/autoupdates.php',
682
  'ICWP_WPSF_FeatureHandler_Traffic' => __DIR__ . '/../..' . '/../features/traffic.php',
683
  'ICWP_WPSF_FeatureHandler_UserManagement' => __DIR__ . '/../..' . '/../features/user_management.php',
684
  'ICWP_WPSF_Foundation' => __DIR__ . '/../..' . '/../common/icwp-foundation.php',
 
 
685
  'ICWP_WPSF_Processor_AdminAccessRestriction' => __DIR__ . '/../..' . '/../processors/admin_access_restriction.php',
686
  'ICWP_WPSF_Processor_AdminAccess_Whitelabel' => __DIR__ . '/../..' . '/../processors/adminaccess_whitelabel.php',
687
  'ICWP_WPSF_Processor_AuditTrail' => __DIR__ . '/../..' . '/../processors/audit_trail.php',
688
  'ICWP_WPSF_Processor_AuditTrail_Auditor' => __DIR__ . '/../..' . '/../processors/audit_trail_auditor.php',
689
  'ICWP_WPSF_Processor_AuditTrail_ChangeTracking' => __DIR__ . '/../..' . '/../processors/audit_trail_changetracking.php',
690
  'ICWP_WPSF_Processor_Autoupdates' => __DIR__ . '/../..' . '/../processors/autoupdates.php',
 
 
 
691
  'ICWP_WPSF_Processor_CommentsFilter' => __DIR__ . '/../..' . '/../processors/comments_filter.php',
692
  'ICWP_WPSF_Processor_CommentsFilter_BotSpam' => __DIR__ . '/../..' . '/../processors/commentsfilter_botspam.php',
693
  'ICWP_WPSF_Processor_CommentsFilter_GoogleRecaptcha' => __DIR__ . '/../..' . '/../processors/commentsfilter_googlerecaptcha.php',
736
  'ICWP_WPSF_Processor_UserManagement_Passwords' => __DIR__ . '/../..' . '/../processors/usermanagement_passwords.php',
737
  'ICWP_WPSF_Processor_UserManagement_Sessions' => __DIR__ . '/../..' . '/../processors/usermanagement_sessions.php',
738
  'ICWP_WPSF_Processor_UserManagement_Suspend' => __DIR__ . '/../..' . '/../processors/usermanagement_suspend.php',
 
 
 
 
739
  'ICWP_WPSF_ServiceProviders' => __DIR__ . '/../..' . '/../common/icwp-serviceproviders.php',
740
  'ICWP_WPSF_Wizard_Base' => __DIR__ . '/../..' . '/../wizards/base.php',
741
  'ICWP_WPSF_Wizard_BaseWpsf' => __DIR__ . '/../..' . '/../wizards/base_wpsf.php',
742
  'ICWP_WPSF_Wizard_LoginProtect' => __DIR__ . '/../..' . '/../wizards/login_protect.php',
743
  'ICWP_WPSF_Wizard_Plugin' => __DIR__ . '/../..' . '/../wizards/plugin.php',
744
  'ICWP_WPSF_WpAdminNotices' => __DIR__ . '/../..' . '/../common/wp-admin-notices.php',
 
745
  'ICWP_WPSF_WpCron' => __DIR__ . '/../..' . '/../common/icwp-wpcron.php',
 
 
 
 
 
 
 
 
746
  'ICWP_WPSF_WpWidget' => __DIR__ . '/../..' . '/../common/wp-widget.php',
747
  'JsonSerializable' => __DIR__ . '/..' . '/nesbot/carbon/src/JsonSerializable.php',
748
  'LZCompressor\\LZContext' => __DIR__ . '/..' . '/nullpunkt/lz-string-php/src/LZCompressor/LZContext.php',
src/lib/vendor/fernleafsystems/wordpress-services/src/Core/Plugins.php CHANGED
@@ -78,46 +78,27 @@ class Plugins {
78
  /**
79
  * @param string $sUrlToInstall
80
  * @param bool $bOverwrite
81
- * @param bool $bMaintenanceMode
82
  * @return array
83
  */
84
- public function install( $sUrlToInstall, $bOverwrite = true, $bMaintenanceMode = false ) {
85
 
86
- $aResult = [
87
- 'successful' => true,
88
- 'plugin_info' => '',
89
- 'errors' => []
90
- ];
91
-
92
- $oUpgraderSkin = new Upgrades\UpgraderSkin();
93
- $oUpgrader = new Upgrades\PluginUpgrader( $oUpgraderSkin );
94
- $oUpgrader->setOverwriteMode( $bOverwrite );
95
- if ( $bMaintenanceMode ) {
96
- $oUpgrader->maintenance_mode( true );
97
- }
98
-
99
- ob_start();
100
- $sInstallResult = $oUpgrader->install( $sUrlToInstall );
101
- ob_end_clean();
102
 
103
- if ( $bMaintenanceMode ) {
104
- $oUpgrader->maintenance_mode( false );
105
- }
106
-
107
- $aErrors = $oUpgraderSkin->getErrors();
108
- if ( isset( $aErrors[ 0 ] ) && is_wp_error( $aErrors[ 0 ] ) ) {
109
- /** @var \WP_Error $oErr */
110
- $oErr = $aErrors[ 0 ];
111
- $aResult[ 'successful' ] = false;
112
- $aResult[ 'errors' ] = $oErr->get_error_messages();
113
- }
114
- else {
115
- $aResult[ 'plugin_info' ] = $oUpgrader->plugin_info();
116
- }
117
 
118
- $aResult[ 'feedback' ] = $oUpgraderSkin->getFeedback();
119
- $aResult[ 'raw' ] = $sInstallResult;
120
- return $aResult;
 
 
 
121
  }
122
 
123
  /**
@@ -135,7 +116,7 @@ class Plugins {
135
  ] );
136
 
137
  if ( !is_wp_error( $api ) ) {
138
- return $this->install( $api->download_link, true, true );
139
  }
140
  return false;
141
  }
@@ -185,29 +166,17 @@ class Plugins {
185
  */
186
  public function update( $sFile ) {
187
 
188
- $aResult = [
189
- 'successful' => 1,
190
- 'errors' => []
191
- ];
192
-
193
- $oUpgraderSkin = new Upgrades\BulkPluginUpgraderSkin();
194
- ob_start();
195
- ( new Upgrades\PluginUpgrader( $oUpgraderSkin ) )->bulk_upgrade( [ $sFile ] );
196
- if ( ob_get_contents() ) {
197
- // for some reason this errors with no buffer present
198
- ob_end_clean();
199
- }
200
-
201
- $aErrors = $oUpgraderSkin->getErrors();
202
- if ( isset( $aErrors[ 0 ] ) && is_wp_error( $aErrors[ 0 ] ) ) {
203
- /** @var \WP_Error $oErr */
204
- $oErr = $aErrors[ 0 ];
205
- $aResult[ 'successful' ] = 0;
206
- $aResult[ 'errors' ] = $oErr->get_error_messages();
207
- }
208
- $aResult[ 'feedback' ] = $oUpgraderSkin->getFeedback();
209
 
210
- return $aResult;
 
 
 
 
211
  }
212
 
213
  /**
78
  /**
79
  * @param string $sUrlToInstall
80
  * @param bool $bOverwrite
 
81
  * @return array
82
  */
83
+ public function install( $sUrlToInstall, $bOverwrite = true ) {
84
 
85
+ $oSkin = Services::WpGeneral()->getWordpressIsAtLeastVersion( '5.3' ) ?
86
+ new Upgrades\UpgraderSkin()
87
+ : new Upgrades\UpgraderSkinLegacy();
88
+ $oUpgrader = new \Plugin_Upgrader( $oSkin );
89
+ add_filter( 'upgrader_package_options', function ( $aOptions ) use ( $bOverwrite ) {
90
+ $aOptions[ 'clear_destination' ] = $bOverwrite;
91
+ return $aOptions;
92
+ } );
 
 
 
 
 
 
 
 
93
 
94
+ $mResult = $oUpgrader->install( $sUrlToInstall );
 
 
 
 
 
 
 
 
 
 
 
 
 
95
 
96
+ return [
97
+ 'successful' => $mResult === true,
98
+ 'feedback' => $oSkin->getIcwpFeedback(),
99
+ 'plugin_info' => $oUpgrader->plugin_info(),
100
+ 'errors' => is_wp_error( $mResult ) ? $mResult->get_error_messages() : [ 'no errors' ]
101
+ ];
102
  }
103
 
104
  /**
116
  ] );
117
 
118
  if ( !is_wp_error( $api ) ) {
119
+ return $this->install( $api->download_link, true );
120
  }
121
  return false;
122
  }
166
  */
167
  public function update( $sFile ) {
168
 
169
+ $oSkin = Services::WpGeneral()->getWordpressIsAtLeastVersion( '5.3' ) ?
170
+ new Upgrades\UpgraderSkin()
171
+ : new Upgrades\UpgraderSkinLegacy();
172
+ $oUpgrader = new \Plugin_Upgrader( $oSkin );
173
+ $mResult = $oUpgrader->upgrade( $sFile );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
174
 
175
+ return [
176
+ 'successful' => $mResult === true,
177
+ 'feedback' => $oSkin->getIcwpFeedback(),
178
+ 'errors' => is_wp_error( $mResult ) ? $mResult->get_error_messages() : [ 'no errors' ]
179
+ ];
180
  }
181
 
182
  /**
src/lib/vendor/fernleafsystems/wordpress-services/src/Core/Themes.php CHANGED
@@ -65,7 +65,7 @@ class Themes {
65
  ->setWorkingSlug( $sSlug )
66
  ->getInfo();
67
  if ( !empty( $oTheme->download_link ) ) {
68
- $bSuccess = $this->install( $oTheme->download_link, true, true )[ 'successful' ];
69
  }
70
  }
71
  catch ( \Exception $oE ) {
@@ -76,45 +76,27 @@ class Themes {
76
  /**
77
  * @param string $sUrlToInstall
78
  * @param bool $bOverwrite
79
- * @param bool $bMaintenanceMode
80
  * @return array
81
  */
82
- public function install( $sUrlToInstall, $bOverwrite = true, $bMaintenanceMode = false ) {
83
-
84
- $aResult = [
85
- 'successful' => true,
86
- 'plugin_info' => '',
87
- 'errors' => []
 
 
 
 
 
 
 
 
 
 
 
 
88
  ];
89
-
90
- $oUpgraderSkin = new Upgrades\UpgraderSkin();
91
- $oUpgrader = new Upgrades\ThemeUpgrader( $oUpgraderSkin );
92
- $oUpgrader->setOverwriteMode( $bOverwrite );
93
- if ( $bMaintenanceMode ) {
94
- $oUpgrader->maintenance_mode( true );
95
- }
96
-
97
- ob_start();
98
- $oUpgrader->install( $sUrlToInstall );
99
- ob_end_clean();
100
-
101
- if ( $bMaintenanceMode ) {
102
- $oUpgrader->maintenance_mode( false );
103
- }
104
-
105
- $aErrors = $oUpgraderSkin->getErrors();
106
- if ( isset( $aErrors[ 0 ] ) && is_wp_error( $aErrors[ 0 ] ) ) {
107
- /** @var \WP_Error $oErr */
108
- $oErr = $aErrors[ 0 ];
109
- $aResult[ 'successful' ] = false;
110
- $aResult[ 'errors' ] = $oErr->get_error_messages();
111
- }
112
- else {
113
- $aResult[ 'theme_info' ] = $oUpgrader->theme_info();
114
- }
115
-
116
- $aResult[ 'feedback' ] = $oUpgraderSkin->getFeedback();
117
- return $aResult;
118
  }
119
 
120
  /**
@@ -159,29 +141,17 @@ class Themes {
159
  */
160
  public function update( $sFile ) {
161
 
162
- $aResult = [
163
- 'successful' => 1,
164
- 'errors' => []
165
- ];
 
166
 
167
- $oUpgraderSkin = new Upgrades\BulkThemeUpgraderSkin();
168
- $oUpgrader = new Upgrades\ThemeUpgrader( $oUpgraderSkin );
169
- ob_start();
170
- $oUpgrader->bulk_upgrade( [ $sFile ] );
171
- if ( ob_get_contents() ) {
172
- // for some reason this errors with no buffer present
173
- ob_end_clean();
174
- }
175
-
176
- $aErrors = $oUpgraderSkin->getErrors();
177
- if ( isset( $aErrors[ 0 ] ) && is_wp_error( $aErrors[ 0 ] ) ) {
178
- /** @var \WP_Error $oErr */
179
- $oErr = $aErrors[ 0 ];
180
- $aResult[ 'successful' ] = 0;
181
- $aResult[ 'errors' ] = $oErr->get_error_messages();
182
- }
183
- $aResult[ 'feedback' ] = $oUpgraderSkin->getFeedback();
184
- return $aResult;
185
  }
186
 
187
  /**
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 ) {
76
  /**
77
  * @param string $sUrlToInstall
78
  * @param bool $bOverwrite
 
79
  * @return array
80
  */
81
+ public function install( $sUrlToInstall, $bOverwrite = true ) {
82
+
83
+ $oSkin = Services::WpGeneral()->getWordpressIsAtLeastVersion( '5.3' ) ?
84
+ new Upgrades\UpgraderSkin()
85
+ : new Upgrades\UpgraderSkinLegacy();
86
+ $oUpgrader = new \Theme_Upgrader( $oSkin );
87
+ add_filter( 'upgrader_package_options', function ( $aOptions ) use ( $bOverwrite ) {
88
+ $aOptions[ 'clear_destination' ] = $bOverwrite;
89
+ return $aOptions;
90
+ } );
91
+
92
+ $mResult = $oUpgrader->install( $sUrlToInstall );
93
+
94
+ return [
95
+ 'successful' => $mResult === true,
96
+ 'feedback' => $oSkin->getIcwpFeedback(),
97
+ 'theme_info' => $oUpgrader->theme_info(),
98
+ 'errors' => is_wp_error( $mResult ) ? $mResult->get_error_messages() : [ 'no errors' ]
99
  ];
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
100
  }
101
 
102
  /**
141
  */
142
  public function update( $sFile ) {
143
 
144
+ $oSkin = Services::WpGeneral()->getWordpressIsAtLeastVersion( '5.3' ) ?
145
+ new Upgrades\UpgraderSkin()
146
+ : new Upgrades\UpgraderSkinLegacy();
147
+ $oUpgrader = new \Theme_Upgrader( $oSkin );
148
+ $mResult = $oUpgrader->upgrade( $sFile );
149
 
150
+ return [
151
+ 'successful' => $mResult === true,
152
+ 'feedback' => $oSkin->getIcwpFeedback(),
153
+ 'errors' => is_wp_error( $mResult ) ? $mResult->get_error_messages() : [ 'no errors' ]
154
+ ];
 
 
 
 
 
 
 
 
 
 
 
 
 
155
  }
156
 
157
  /**
src/lib/vendor/fernleafsystems/wordpress-services/src/Core/Upgrades/UpgraderSkin.php CHANGED
@@ -10,35 +10,29 @@ class UpgraderSkin extends \WP_Upgrader_Skin {
10
  /**
11
  * @var array
12
  */
13
- public $aErrors;
14
-
15
- /**
16
- * @var array
17
- */
18
- public $aFeedback;
19
 
20
  public function __construct() {
21
  parent::__construct();
22
- $this->done_header = true;
 
23
  }
24
 
25
  /**
26
- * @return array
27
  */
28
- public function getErrors() {
29
- return is_array( $this->aErrors ) ? $this->aErrors : [];
 
 
 
 
30
  }
31
 
32
  /**
33
- * @return array
34
  */
35
- public function getFeedback() {
36
  return is_array( $this->aFeedback ) ? $this->aFeedback : [];
37
  }
38
-
39
- function error( $errors ) {
40
- }
41
-
42
- function feedback( $string ) {
43
- }
44
  }
10
  /**
11
  * @var array
12
  */
13
+ private $aFeedback = [];
 
 
 
 
 
14
 
15
  public function __construct() {
16
  parent::__construct();
17
+ $this->done_header = true; // prevents text output
18
+ $this->done_footer = true; // prevents text output
19
  }
20
 
21
  /**
22
+ * @inheritDoc
23
  */
24
+ function feedback( $string, ...$args ) {
25
+ // overriding this prevent automatic echo of feedback
26
+ if ( empty( $this->aFeedback ) ) {
27
+ $this->aFeedback = [];
28
+ }
29
+ $this->aFeedback[] = $string;
30
  }
31
 
32
  /**
33
+ * @return string[]
34
  */
35
+ public function getIcwpFeedback() {
36
  return is_array( $this->aFeedback ) ? $this->aFeedback : [];
37
  }
 
 
 
 
 
 
38
  }
src/lib/vendor/fernleafsystems/wordpress-services/src/Core/Upgrades/UpgraderSkinLegacy.php ADDED
@@ -0,0 +1,38 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace FernleafSystems\Wordpress\Services\Core\Upgrades;
4
+
5
+ require_once( ABSPATH.'wp-admin/includes/upgrade.php' );
6
+ require_once( ABSPATH.'wp-admin/includes/class-wp-upgrader.php' );
7
+
8
+ class UpgraderSkinLegacy extends \WP_Upgrader_Skin {
9
+
10
+ /**
11
+ * @var array
12
+ */
13
+ private $aFeedback = [];
14
+
15
+ public function __construct() {
16
+ parent::__construct();
17
+ $this->done_header = true; // prevents text output
18
+ $this->done_footer = true; // prevents text output
19
+ }
20
+
21
+ /**
22
+ * @inheritDoc
23
+ */
24
+ function feedback( $string ) {
25
+ // overriding this prevent automatic echo of feedback
26
+ if ( empty( $this->aFeedback ) ) {
27
+ $this->aFeedback = [];
28
+ }
29
+ $this->aFeedback[] = $string;
30
+ }
31
+
32
+ /**
33
+ * @return string[]
34
+ */
35
+ public function getIcwpFeedback() {
36
+ return is_array( $this->aFeedback ) ? $this->aFeedback : [];
37
+ }
38
+ }
src/lib/vendor/fernleafsystems/wordpress-services/src/Core/VOs/WpPluginVo.php CHANGED
@@ -96,6 +96,13 @@ class WpPluginVo {
96
  return $mVal;
97
  }
98
 
 
 
 
 
 
 
 
99
  /**
100
  * @return bool
101
  */
96
  return $mVal;
97
  }
98
 
99
+ /**
100
+ * @return string
101
+ */
102
+ public function getInstallDir() {
103
+ return wp_normalize_path( trailingslashit( dirname( path_join( WP_PLUGIN_DIR, $this->file ) ) ) );
104
+ }
105
+
106
  /**
107
  * @return bool
108
  */
src/lib/vendor/fernleafsystems/wordpress-services/src/Core/VOs/WpThemeVo.php CHANGED
@@ -88,6 +88,13 @@ class WpThemeVo {
88
  return $mVal;
89
  }
90
 
 
 
 
 
 
 
 
91
  /**
92
  * @return bool
93
  */
88
  return $mVal;
89
  }
90
 
91
+ /**
92
+ * @return string
93
+ */
94
+ public function getInstallDir() {
95
+ return wp_normalize_path( trailingslashit( $this->wp_theme->get_stylesheet_directory() ) );
96
+ }
97
+
98
  /**
99
  * @return bool
100
  */
src/lib/vendor/fernleafsystems/wordpress-services/src/Utilities/DataManipulation.php CHANGED
@@ -16,6 +16,14 @@ class DataManipulation {
16
  return str_replace( [ "\r\n", "\r" ], "\n", file_get_contents( $sFullFilePath ) );
17
  }
18
 
 
 
 
 
 
 
 
 
19
  /**
20
  * @param array $aArrayToConvert
21
  * @return string
16
  return str_replace( [ "\r\n", "\r" ], "\n", file_get_contents( $sFullFilePath ) );
17
  }
18
 
19
+ /**
20
+ * @param string $sFullFilePath
21
+ * @return string
22
+ */
23
+ public function convertLineEndingsLinuxToDos( $sFullFilePath ) {
24
+ return str_replace( "\n", "\r\n", $this->convertLineEndingsDosToLinux( $sFullFilePath ) );
25
+ }
26
+
27
  /**
28
  * @param array $aArrayToConvert
29
  * @return string
src/lib/vendor/fernleafsystems/wordpress-services/src/Utilities/File/Compare/CompareHash.php CHANGED
@@ -13,13 +13,17 @@ class CompareHash {
13
  * @throws \InvalidArgumentException
14
  */
15
  public function isEqualFileMd5( $sPath, $sHashToCompare ) {
16
-
17
  if ( !Services::WpFs()->isFile( $sPath ) ) {
18
  throw new \InvalidArgumentException( 'File does not exist on disk to compare' );
19
  }
 
 
 
20
 
 
21
  return hash_equals( md5_file( $sPath ), $sHashToCompare )
22
- || hash_equals( md5( Services::DataManipulation()->convertLineEndingsDosToLinux( $sPath ) ), $sHashToCompare );
 
23
  }
24
 
25
  /**
@@ -29,13 +33,17 @@ class CompareHash {
29
  * @throws \InvalidArgumentException
30
  */
31
  public function isEqualFileSha1( $sPath, $sHashToCompare ) {
32
-
33
  if ( !Services::WpFs()->isFile( $sPath ) ) {
34
  throw new \InvalidArgumentException( 'File does not exist on disk to compare' );
35
  }
 
 
 
36
 
 
37
  return hash_equals( sha1_file( $sPath ), $sHashToCompare )
38
- || hash_equals( sha1( Services::DataManipulation()->convertLineEndingsDosToLinux( $sPath ) ), $sHashToCompare );
 
39
  }
40
 
41
  /**
@@ -50,10 +58,16 @@ class CompareHash {
50
  throw new \InvalidArgumentException( 'File does not exist on disk to compare' );
51
  }
52
 
53
- return $this->isEqualFileMd5(
54
- $sPath1,
55
- md5( Services::DataManipulation()->convertLineEndingsDosToLinux( $sPath2 ) )
56
- );
 
 
 
 
 
 
57
  }
58
 
59
  /**
@@ -68,9 +82,15 @@ class CompareHash {
68
  throw new \InvalidArgumentException( 'File does not exist on disk to compare' );
69
  }
70
 
71
- return $this->isEqualFileSha1(
72
- $sPath1,
73
- sha1( Services::DataManipulation()->convertLineEndingsDosToLinux( $sPath2 ) )
74
- );
 
 
 
 
 
 
75
  }
76
  }
13
  * @throws \InvalidArgumentException
14
  */
15
  public function isEqualFileMd5( $sPath, $sHashToCompare ) {
 
16
  if ( !Services::WpFs()->isFile( $sPath ) ) {
17
  throw new \InvalidArgumentException( 'File does not exist on disk to compare' );
18
  }
19
+ if ( !is_string( $sHashToCompare ) ) {
20
+ throw new \InvalidArgumentException( 'Provided user hash was not a string' );
21
+ }
22
 
23
+ $oDataManip = Services::DataManipulation();
24
  return hash_equals( md5_file( $sPath ), $sHashToCompare )
25
+ || hash_equals( md5( $oDataManip->convertLineEndingsDosToLinux( $sPath ) ), $sHashToCompare )
26
+ || hash_equals( md5( $oDataManip->convertLineEndingsLinuxToDos( $sPath ) ), $sHashToCompare );
27
  }
28
 
29
  /**
33
  * @throws \InvalidArgumentException
34
  */
35
  public function isEqualFileSha1( $sPath, $sHashToCompare ) {
 
36
  if ( !Services::WpFs()->isFile( $sPath ) ) {
37
  throw new \InvalidArgumentException( 'File does not exist on disk to compare' );
38
  }
39
+ if ( !is_string( $sHashToCompare ) ) {
40
+ throw new \InvalidArgumentException( 'Provided user hash was not a string' );
41
+ }
42
 
43
+ $oDataManip = Services::DataManipulation();
44
  return hash_equals( sha1_file( $sPath ), $sHashToCompare )
45
+ || hash_equals( sha1( $oDataManip->convertLineEndingsDosToLinux( $sPath ) ), $sHashToCompare )
46
+ || hash_equals( sha1( $oDataManip->convertLineEndingsLinuxToDos( $sPath ) ), $sHashToCompare );
47
  }
48
 
49
  /**
58
  throw new \InvalidArgumentException( 'File does not exist on disk to compare' );
59
  }
60
 
61
+ $oDataManip = Services::DataManipulation();
62
+ return
63
+ $this->isEqualFileMd5(
64
+ $sPath1,
65
+ md5( $oDataManip->convertLineEndingsDosToLinux( $sPath2 ) )
66
+ )
67
+ || $this->isEqualFileMd5(
68
+ $sPath1,
69
+ md5( $oDataManip->convertLineEndingsLinuxToDos( $sPath2 ) )
70
+ );
71
  }
72
 
73
  /**
82
  throw new \InvalidArgumentException( 'File does not exist on disk to compare' );
83
  }
84
 
85
+ $oDataManip = Services::DataManipulation();
86
+ return
87
+ $this->isEqualFileSha1(
88
+ $sPath1,
89
+ sha1( $oDataManip->convertLineEndingsDosToLinux( $sPath2 ) )
90
+ )
91
+ || $this->isEqualFileSha1(
92
+ $sPath1,
93
+ sha1( $oDataManip->convertLineEndingsLinuxToDos( $sPath2 ) )
94
+ );
95
  }
96
  }
src/lib/vendor/fernleafsystems/wordpress-services/src/Utilities/File/ExtractLinesFromFile.php CHANGED
@@ -2,8 +2,6 @@
2
 
3
  namespace FernleafSystems\Wordpress\Services\Utilities\File;
4
 
5
- use FernleafSystems\Wordpress\Services\Services;
6
-
7
  /**
8
  * Class ExtractLineFromFile
9
  * @package FernleafSystems\Wordpress\Services\Utilities\File
@@ -13,7 +11,7 @@ class ExtractLinesFromFile {
13
  /**
14
  * @param string $sPath
15
  * @param int[] $aLines
16
- * @return string
17
  * @throws \Exception
18
  */
19
  public function run( $sPath, $aLines ) {
2
 
3
  namespace FernleafSystems\Wordpress\Services\Utilities\File;
4
 
 
 
5
  /**
6
  * Class ExtractLineFromFile
7
  * @package FernleafSystems\Wordpress\Services\Utilities\File
11
  /**
12
  * @param string $sPath
13
  * @param int[] $aLines
14
+ * @return string[]
15
  * @throws \Exception
16
  */
17
  public function run( $sPath, $aLines ) {
src/lib/vendor/fernleafsystems/wordpress-services/src/Utilities/Integrations/WpHashes/ApiBase.php CHANGED
@@ -21,6 +21,16 @@ abstract class ApiBase {
21
  */
22
  private $oReq;
23
 
 
 
 
 
 
 
 
 
 
 
24
  /**
25
  * @return string
26
  */
@@ -88,8 +98,23 @@ abstract class ApiBase {
88
  * @return string
89
  */
90
  protected function fireRequest_GET() {
 
 
91
  $sUrl = add_query_arg( array_map( 'strtolower', $this->getQueryData() ), $this->getApiUrl() );
92
- return ( new HttpRequest() )->getContent( $sUrl );
 
 
 
 
 
 
 
 
 
 
 
 
 
93
  }
94
 
95
  /**
@@ -104,4 +129,20 @@ abstract class ApiBase {
104
  );
105
  return $oHttp->isSuccess() ? $oHttp->lastResponse->body : null;
106
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
107
  }
21
  */
22
  private $oReq;
23
 
24
+ /**
25
+ * @var bool
26
+ */
27
+ private $bUseQueryCache = false;
28
+
29
+ /**
30
+ * @var array
31
+ */
32
+ private static $aQueryCache = [];
33
+
34
  /**
35
  * @return string
36
  */
98
  * @return string
99
  */
100
  protected function fireRequest_GET() {
101
+ $sResponse = null;
102
+
103
  $sUrl = add_query_arg( array_map( 'strtolower', $this->getQueryData() ), $this->getApiUrl() );
104
+ $sSig = md5( $sUrl );
105
+
106
+ if ( $this->isUseQueryCache() && isset( self::$aQueryCache[ $sSig ] ) ) {
107
+ $sResponse = self::$aQueryCache[ $sSig ];
108
+ }
109
+
110
+ if ( is_null( $sResponse ) ) {
111
+ $sResponse = ( new HttpRequest() )->getContent( $sUrl );
112
+ if ( $this->isUseQueryCache() ) {
113
+ self::$aQueryCache[ $sSig ] = $sResponse;
114
+ }
115
+ }
116
+
117
+ return $sResponse;
118
  }
119
 
120
  /**
129
  );
130
  return $oHttp->isSuccess() ? $oHttp->lastResponse->body : null;
131
  }
132
+
133
+ /**
134
+ * @return bool
135
+ */
136
+ public function isUseQueryCache() {
137
+ return (bool)$this->bUseQueryCache;
138
+ }
139
+
140
+ /**
141
+ * @param bool $bUseQueryCache
142
+ * @return $this
143
+ */
144
+ public function setUseQueryCache( $bUseQueryCache ) {
145
+ $this->bUseQueryCache = $bUseQueryCache;
146
+ return $this;
147
+ }
148
  }
src/lib/vendor/fernleafsystems/wordpress-services/src/Utilities/Integrations/WpHashes/Hashes/ClassicPress.php CHANGED
@@ -18,6 +18,7 @@ class ClassicPress extends Base {
18
  * @return string[]|null
19
  */
20
  public function getHashes( $sVersion, $sHashAlgo = null ) {
 
21
  $oReq = $this->getRequestVO();
22
  $oReq->version = $sVersion;
23
  $oReq->hash = $sHashAlgo;
18
  * @return string[]|null
19
  */
20
  public function getHashes( $sVersion, $sHashAlgo = null ) {
21
+ /** @var RequestVO $oReq */
22
  $oReq = $this->getRequestVO();
23
  $oReq->version = $sVersion;
24
  $oReq->hash = $sHashAlgo;
src/lib/vendor/fernleafsystems/wordpress-services/src/Utilities/Integrations/WpHashes/Hashes/PluginThemeBase.php CHANGED
@@ -15,6 +15,7 @@ abstract class PluginThemeBase extends Base {
15
  * @return array|null
16
  */
17
  public function getHashes( $sSlug, $sVersion, $sHashAlgo = null ) {
 
18
  $oReq = $this->getRequestVO();
19
  $oReq->version = $sVersion;
20
  $oReq->hash = $sHashAlgo;
15
  * @return array|null
16
  */
17
  public function getHashes( $sSlug, $sVersion, $sHashAlgo = null ) {
18
+ /** @var RequestVO $oReq */
19
  $oReq = $this->getRequestVO();
20
  $oReq->version = $sVersion;
21
  $oReq->hash = $sHashAlgo;
src/lib/vendor/fernleafsystems/wordpress-services/src/Utilities/Integrations/WpHashes/Hashes/WordPress.php CHANGED
@@ -19,6 +19,7 @@ class WordPress extends Base {
19
  * @return string[]|null
20
  */
21
  public function getHashes( $sVersion, $sLocale = null, $sHashAlgo = null ) {
 
22
  $oReq = $this->getRequestVO();
23
  $oReq->version = $sVersion;
24
  $oReq->hash = $sHashAlgo;
19
  * @return string[]|null
20
  */
21
  public function getHashes( $sVersion, $sLocale = null, $sHashAlgo = null ) {
22
+ /** @var RequestVO $oReq */
23
  $oReq = $this->getRequestVO();
24
  $oReq->version = $sVersion;
25
  $oReq->hash = $sHashAlgo;
src/lib/vendor/fernleafsystems/wordpress-services/src/Utilities/Integrations/WpHashes/Malware/Confidence/Base.php CHANGED
@@ -6,7 +6,7 @@ use FernleafSystems\Wordpress\Services\Utilities\Integrations\WpHashes;
6
 
7
  abstract class Base extends WpHashes\ApiBase {
8
 
9
- const API_ENDPOINT = 'malware/fpconfidence';
10
 
11
  /**
12
  * @return RequestVO
6
 
7
  abstract class Base extends WpHashes\ApiBase {
8
 
9
+ const API_ENDPOINT = 'malware/fpconfidence/';
10
 
11
  /**
12
  * @return RequestVO
src/lib/vendor/fernleafsystems/wordpress-services/src/Utilities/Integrations/WpHashes/Malware/Confidence/RequestVO.php CHANGED
@@ -7,6 +7,7 @@ use FernleafSystems\Wordpress\Services\Utilities\Integrations;
7
  /**
8
  * Class RequestVO
9
  * @package FernleafSystems\Wordpress\Services\Utilities\Integrations\WpHashes\Malware\Confidence
 
10
  * @property string $file
11
  * @property string $hash
12
  * @property string $algo
7
  /**
8
  * Class RequestVO
9
  * @package FernleafSystems\Wordpress\Services\Utilities\Integrations\WpHashes\Malware\Confidence
10
+ * @property string $type
11
  * @property string $file
12
  * @property string $hash
13
  * @property string $algo
src/lib/vendor/fernleafsystems/wordpress-services/src/Utilities/Integrations/WpHashes/Malware/Confidence/Retrieve.php CHANGED
@@ -10,7 +10,23 @@ use FernleafSystems\Wordpress\Services\Services;
10
  */
11
  class Retrieve extends Base {
12
 
13
- const RESPONSE_DATA_KEY = 'hashes';
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
14
 
15
  /**
16
  * @param string $sFullPath
@@ -20,6 +36,7 @@ class Retrieve extends Base {
20
  public function retrieveForFile( $sFullPath, $sAlgo = 'sha1' ) {
21
  /** @var RequestVO $oReq */
22
  $oReq = $this->getRequestVO();
 
23
  $oReq->file = basename( $sFullPath );
24
  $oReq->hash = hash( $sAlgo, Services::DataManipulation()->convertLineEndingsDosToLinux( $sFullPath ) );
25
  $oReq->algo = $sAlgo;
@@ -35,6 +52,7 @@ class Retrieve extends Base {
35
  public function retrieveForFileLine( $sFullPath, $sLine, $sAlgo = 'sha1' ) {
36
  /** @var RequestVO $oReq */
37
  $oReq = $this->getRequestVO();
 
38
  $oReq->file = basename( $sFullPath );
39
  $oReq->hash = hash( $sAlgo, trim( $sLine ) );
40
  $oReq->algo = $sAlgo;
10
  */
11
  class Retrieve extends Base {
12
 
13
+ const RESPONSE_DATA_KEY = 'confidence';
14
+
15
+ /**
16
+ * @return string
17
+ */
18
+ protected function getApiUrl() {
19
+ $aData = array_filter( array_merge(
20
+ [
21
+ 'type' => false,
22
+ 'file' => false,
23
+ 'hash' => false,
24
+ 'algo' => false,
25
+ ],
26
+ $this->getRequestVO()->getRawDataAsArray()
27
+ ) );
28
+ return sprintf( '%s%s', parent::getApiUrl(), implode( '/', $aData ) );
29
+ }
30
 
31
  /**
32
  * @param string $sFullPath
36
  public function retrieveForFile( $sFullPath, $sAlgo = 'sha1' ) {
37
  /** @var RequestVO $oReq */
38
  $oReq = $this->getRequestVO();
39
+ $oReq->type = 'file';
40
  $oReq->file = basename( $sFullPath );
41
  $oReq->hash = hash( $sAlgo, Services::DataManipulation()->convertLineEndingsDosToLinux( $sFullPath ) );
42
  $oReq->algo = $sAlgo;
52
  public function retrieveForFileLine( $sFullPath, $sLine, $sAlgo = 'sha1' ) {
53
  /** @var RequestVO $oReq */
54
  $oReq = $this->getRequestVO();
55
+ $oReq->type = 'line';
56
  $oReq->file = basename( $sFullPath );
57
  $oReq->hash = hash( $sAlgo, trim( $sLine ) );
58
  $oReq->algo = $sAlgo;
src/lib/vendor/fernleafsystems/wordpress-services/src/Utilities/IpUtils.php CHANGED
@@ -141,7 +141,15 @@ class IpUtils {
141
  }
142
 
143
  /**
144
- * @param boolean $bAsHuman
 
 
 
 
 
 
 
 
145
  * @return int|string|bool - visitor IP Address as IP2Long
146
  */
147
  public function getRequestIp( $bAsHuman = true ) {
@@ -184,7 +192,7 @@ class IpUtils {
184
  /**
185
  * @param string $sIp
186
  * @param bool $flags
187
- * @return boolean
188
  */
189
  public function isValidIp( $sIp, $flags = null ) {
190
  return filter_var( trim( $sIp ), FILTER_VALIDATE_IP, $flags );
@@ -192,7 +200,7 @@ class IpUtils {
192
 
193
  /**
194
  * @param string $sIp
195
- * @return boolean
196
  */
197
  public function isValidIp4Range( $sIp ) {
198
  $bIsRange = false;
@@ -205,7 +213,20 @@ class IpUtils {
205
 
206
  /**
207
  * @param string $sIp
208
- * @return boolean
 
 
 
 
 
 
 
 
 
 
 
 
 
209
  */
210
  public function isValidIpOrRange( $sIp ) {
211
  return $this->isValidIp_PublicRemote( $sIp ) || $this->isValidIpRange( $sIp );
@@ -214,7 +235,7 @@ class IpUtils {
214
  /**
215
  * Assumes a valid IPv4 address is provided as we're only testing for a whether the IP is public or not.
216
  * @param string $sIp
217
- * @return boolean
218
  */
219
  public function isValidIp_PublicRange( $sIp ) {
220
  return $this->isValidIp( $sIp, FILTER_FLAG_NO_PRIV_RANGE );
@@ -222,7 +243,7 @@ class IpUtils {
222
 
223
  /**
224
  * @param string $sIp
225
- * @return boolean
226
  */
227
  public function isValidIp_PublicRemote( $sIp ) {
228
  return $this->isValidIp( $sIp, ( FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE ) );
@@ -230,14 +251,10 @@ class IpUtils {
230
 
231
  /**
232
  * @param string $sIp
233
- * @return boolean
234
  */
235
  public function isValidIpRange( $sIp ) {
236
- if ( strpos( $sIp, '/' ) == false ) {
237
- return false;
238
- }
239
- $aParts = explode( '/', $sIp );
240
- return filter_var( $aParts[ 0 ], FILTER_VALIDATE_IP, FILTER_FLAG_IPV4 ) && ( 0 < $aParts[ 1 ] && $aParts[ 1 ] < 33 );
241
  }
242
 
243
  /**
141
  }
142
 
143
  /**
144
+ * @param string $sIp
145
+ * @return string
146
+ */
147
+ public function getIpInfo( $sIp ) {
148
+ return sprintf( 'https://redirect.li/map/?ip=%s', $sIp );
149
+ }
150
+
151
+ /**
152
+ * @param bool $bAsHuman
153
  * @return int|string|bool - visitor IP Address as IP2Long
154
  */
155
  public function getRequestIp( $bAsHuman = true ) {
192
  /**
193
  * @param string $sIp
194
  * @param bool $flags
195
+ * @return bool
196
  */
197
  public function isValidIp( $sIp, $flags = null ) {
198
  return filter_var( trim( $sIp ), FILTER_VALIDATE_IP, $flags );
200
 
201
  /**
202
  * @param string $sIp
203
+ * @return bool
204
  */
205
  public function isValidIp4Range( $sIp ) {
206
  $bIsRange = false;
213
 
214
  /**
215
  * @param string $sIp
216
+ * @return bool
217
+ */
218
+ public function isValidIp6Range( $sIp ) {
219
+ $bIsRange = false;
220
+ if ( strpos( $sIp, '/' ) ) {
221
+ list( $sIp, $sCIDR ) = explode( '/', $sIp );
222
+ $bIsRange = $this->isValidIp( $sIp ) && ( (int)$sCIDR >= 0 && (int)$sCIDR <= 128 );
223
+ }
224
+ return $bIsRange;
225
+ }
226
+
227
+ /**
228
+ * @param string $sIp
229
+ * @return bool
230
  */
231
  public function isValidIpOrRange( $sIp ) {
232
  return $this->isValidIp_PublicRemote( $sIp ) || $this->isValidIpRange( $sIp );
235
  /**
236
  * Assumes a valid IPv4 address is provided as we're only testing for a whether the IP is public or not.
237
  * @param string $sIp
238
+ * @return bool
239
  */
240
  public function isValidIp_PublicRange( $sIp ) {
241
  return $this->isValidIp( $sIp, FILTER_FLAG_NO_PRIV_RANGE );
243
 
244
  /**
245
  * @param string $sIp
246
+ * @return bool
247
  */
248
  public function isValidIp_PublicRemote( $sIp ) {
249
  return $this->isValidIp( $sIp, ( FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE ) );
251
 
252
  /**
253
  * @param string $sIp
254
+ * @return bool
255
  */
256
  public function isValidIpRange( $sIp ) {
257
+ return $this->isValidIp4Range( $sIp ) || $this->isValidIp6Range( $sIp );
 
 
 
 
258
  }
259
 
260
  /**
src/lib/vendor/symfony/polyfill-mbstring/Mbstring.php CHANGED
@@ -512,7 +512,9 @@ final class Mbstring
512
  $offset = 0;
513
  } elseif ($offset = (int) $offset) {
514
  if ($offset < 0) {
515
- $haystack = self::mb_substr($haystack, 0, $offset, $encoding);
 
 
516
  $offset = 0;
517
  } else {
518
  $haystack = self::mb_substr($haystack, $offset, 2147483647, $encoding);
@@ -532,7 +534,7 @@ final class Mbstring
532
  return null;
533
  }
534
 
535
- if ($split_length < 1) {
536
  trigger_error('The length of each segment must be greater than zero', E_USER_WARNING);
537
 
538
  return false;
@@ -542,6 +544,10 @@ final class Mbstring
542
  $encoding = mb_internal_encoding();
543
  }
544
 
 
 
 
 
545
  $result = array();
546
  $length = mb_strlen($string, $encoding);
547
 
@@ -815,11 +821,16 @@ final class Mbstring
815
  return self::$internalEncoding;
816
  }
817
 
 
 
 
 
818
  $encoding = strtoupper($encoding);
819
 
820
  if ('8BIT' === $encoding || 'BINARY' === $encoding) {
821
  return 'CP850';
822
  }
 
823
  if ('UTF8' === $encoding) {
824
  return 'UTF-8';
825
  }
512
  $offset = 0;
513
  } elseif ($offset = (int) $offset) {
514
  if ($offset < 0) {
515
+ if (0 > $offset += self::mb_strlen($needle)) {
516
+ $haystack = self::mb_substr($haystack, 0, $offset, $encoding);
517
+ }
518
  $offset = 0;
519
  } else {
520
  $haystack = self::mb_substr($haystack, $offset, 2147483647, $encoding);
534
  return null;
535
  }
536
 
537
+ if (1 > $split_length = (int) $split_length) {
538
  trigger_error('The length of each segment must be greater than zero', E_USER_WARNING);
539
 
540
  return false;
544
  $encoding = mb_internal_encoding();
545
  }
546
 
547
+ if ('UTF-8' === $encoding = self::getEncoding($encoding)) {
548
+ return preg_split("/(.{{$split_length}})/u", $string, null, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
549
+ }
550
+
551
  $result = array();
552
  $length = mb_strlen($string, $encoding);
553
 
821
  return self::$internalEncoding;
822
  }
823
 
824
+ if ('UTF-8' === $encoding) {
825
+ return 'UTF-8';
826
+ }
827
+
828
  $encoding = strtoupper($encoding);
829
 
830
  if ('8BIT' === $encoding || 'BINARY' === $encoding) {
831
  return 'CP850';
832
  }
833
+
834
  if ('UTF8' === $encoding) {
835
  return 'UTF-8';
836
  }
src/processors/adminaccess_whitelabel.php CHANGED
@@ -57,7 +57,7 @@ class ICWP_WPSF_Processor_AdminAccess_Whitelabel extends Modules\BaseShield\Shie
57
 
58
  public function hideFromPluginEditor() {
59
  $oCon = $this->getCon();
60
- $sJs = $this->loadDP()->readFileContentsUsingInclude( $oCon->getPath_AssetJs( 'whitelabel.js' ) );
61
  echo sprintf( '<script type="text/javascript">%s</script>', sprintf( $sJs, $oCon->getPluginBaseFile() ) );
62
  }
63
 
57
 
58
  public function hideFromPluginEditor() {
59
  $oCon = $this->getCon();
60
+ $sJs = Services::Data()->readFileContentsUsingInclude( $oCon->getPath_AssetJs( 'whitelabel.js' ) );
61
  echo sprintf( '<script type="text/javascript">%s</script>', sprintf( $sJs, $oCon->getPluginBaseFile() ) );
62
  }
63
 
src/processors/audit_trail.php CHANGED
@@ -12,7 +12,7 @@ class ICWP_WPSF_Processor_AuditTrail extends Modules\BaseShield\ShieldProcessor
12
  if ( $oOpts->isEnabledAuditing() ) {
13
  $this->getSubProAuditor()->execute();
14
  }
15
- if ( $oOpts->isEnabledChangeTracking() ) {
16
  $this->getSubProChangeTracking()->execute();
17
  }
18
  }
12
  if ( $oOpts->isEnabledAuditing() ) {
13
  $this->getSubProAuditor()->execute();
14
  }
15
+ if ( false && $oOpts->isEnabledChangeTracking() ) {
16
  $this->getSubProChangeTracking()->execute();
17
  }
18
  }
src/processors/autoupdates.php CHANGED
@@ -34,8 +34,8 @@ class ICWP_WPSF_Processor_Autoupdates extends Modules\BaseShield\ShieldProcessor
34
  * filter. What this filter decides will ultimately determine the fate of any core upgrade.
35
  */
36
  public function run() {
37
- /** @var ICWP_WPSF_FeatureHandler_Autoupdates $oFO */
38
- $oFO = $this->getMod();
39
 
40
  $nFilterPriority = $this->getHookPriority();
41
  add_filter( 'allow_minor_auto_core_updates', [ $this, 'autoupdate_core_minor' ], $nFilterPriority );
@@ -46,11 +46,11 @@ class ICWP_WPSF_Processor_Autoupdates extends Modules\BaseShield\ShieldProcessor
46
  add_filter( 'auto_update_theme', [ $this, 'autoupdate_themes' ], $nFilterPriority, 2 );
47
  add_filter( 'auto_update_core', [ $this, 'autoupdate_core' ], $nFilterPriority, 2 );
48
 
49
- if ( $oFO->isOpt( 'enable_autoupdate_ignore_vcs', 'Y' ) ) {
50
  add_filter( 'automatic_updates_is_vcs_checkout', '__return_false', $nFilterPriority );
51
  }
52
 
53
- if ( !$oFO->isDisableAllAutoUpdates() ) {
54
  //more parameter options here for later
55
  add_filter( 'auto_core_update_send_email', [ $this, 'autoupdate_send_email' ], $nFilterPriority, 1 );
56
  add_filter( 'auto_core_update_email', [ $this, 'autoupdate_email_override' ], $nFilterPriority, 1 );
@@ -59,12 +59,12 @@ class ICWP_WPSF_Processor_Autoupdates extends Modules\BaseShield\ShieldProcessor
59
  add_action( 'set_site_transient_update_plugins', [ $this, 'trackUpdateTimesPlugins' ] );
60
  add_action( 'set_site_transient_update_themes', [ $this, 'trackUpdateTimesThemes' ] );
61
 
62
- if ( $oFO->isSendAutoupdatesNotificationEmail() ) {
63
  $this->trackAssetsVersions();
64
  add_action( 'automatic_updates_complete', [ $this, 'sendNotificationEmail' ] );
65
  }
66
 
67
- if ( $oFO->isAutoupdateIndividualPlugins() ) {
68
  // Adds automatic update indicator column to all plugins in plugin listing.
69
  add_filter( 'manage_plugins_columns', [ $this, 'fAddPluginsListAutoUpdateColumn' ] );
70
  }
@@ -72,9 +72,9 @@ class ICWP_WPSF_Processor_Autoupdates extends Modules\BaseShield\ShieldProcessor
72
  }
73
 
74
  public function onWpLoaded() {
75
- /** @var ICWP_WPSF_FeatureHandler_Autoupdates $oFO */
76
- $oFO = $this->getMod();
77
- if ( $oFO->isDisableAllAutoUpdates() ) {
78
  $this->disableAllAutoUpdates();
79
  }
80
  else {
@@ -126,10 +126,10 @@ class ICWP_WPSF_Processor_Autoupdates extends Modules\BaseShield\ShieldProcessor
126
  public function trackUpdateTimesCore( $oUpdates ) {
127
 
128
  if ( !empty( $oUpdates ) && isset( $oUpdates->updates ) && is_array( $oUpdates->updates ) ) {
129
- /** @var ICWP_WPSF_FeatureHandler_Autoupdates $oFO */
130
- $oFO = $this->getMod();
131
 
132
- $aTk = $oFO->getDelayTracking();
133
  $aItemTk = isset( $aTk[ 'core' ][ 'wp' ] ) ? $aTk[ 'core' ][ 'wp' ] : [];
134
  foreach ( $oUpdates->updates as $oUpdate ) {
135
  if ( 'autoupdate' == $oUpdate->response ) {
@@ -140,7 +140,7 @@ class ICWP_WPSF_Processor_Autoupdates extends Modules\BaseShield\ShieldProcessor
140
  }
141
  }
142
  $aTk[ 'core' ][ 'wp' ] = array_slice( $aItemTk, -5 );
143
- $oFO->setDelayTracking( $aTk );
144
  }
145
  }
146
 
@@ -163,12 +163,12 @@ class ICWP_WPSF_Processor_Autoupdates extends Modules\BaseShield\ShieldProcessor
163
  * @param string $sContext - plugins/themes
164
  */
165
  protected function trackUpdateTimeCommon( $oUpdates, $sContext ) {
 
 
166
 
167
  if ( !empty( $oUpdates ) && isset( $oUpdates->response ) && is_array( $oUpdates->response ) ) {
168
- /** @var ICWP_WPSF_FeatureHandler_Autoupdates $oFO */
169
- $oFO = $this->getMod();
170
 
171
- $aTk = $oFO->getDelayTracking();
172
  foreach ( $oUpdates->response as $sSlug => $oUpdate ) {
173
  $aItemTk = isset( $aTk[ $sContext ][ $sSlug ] ) ? $aTk[ $sContext ][ $sSlug ] : [];
174
  if ( is_array( $oUpdate ) ) {
@@ -183,7 +183,7 @@ class ICWP_WPSF_Processor_Autoupdates extends Modules\BaseShield\ShieldProcessor
183
  $aTk[ $sContext ][ $sSlug ] = array_slice( $aItemTk, -3 );
184
  }
185
  }
186
- $oFO->setDelayTracking( $aTk );
187
  }
188
  }
189
 
@@ -203,14 +203,14 @@ class ICWP_WPSF_Processor_Autoupdates extends Modules\BaseShield\ShieldProcessor
203
  * @return boolean
204
  */
205
  public function autoupdate_core_major( $bUpdate ) {
206
- /** @var ICWP_WPSF_FeatureHandler_Autoupdates $oFO */
207
- $oFO = $this->getMod();
208
 
209
- if ( $oFO->isDisableAllAutoUpdates() ) {
210
  $bUpdate = false;
211
  }
212
- else if ( !$oFO->isDelayUpdates() ) { // the delay is handles elsewhere
213
- $bUpdate = $oFO->isAutoUpdateCoreMajor();
214
  }
215
 
216
  return $bUpdate;
@@ -223,14 +223,14 @@ class ICWP_WPSF_Processor_Autoupdates extends Modules\BaseShield\ShieldProcessor
223
  * @return boolean
224
  */
225
  public function autoupdate_core_minor( $bUpdate ) {
226
- /** @var ICWP_WPSF_FeatureHandler_Autoupdates $oFO */
227
- $oFO = $this->getMod();
228
 
229
- if ( $oFO->isDisableAllAutoUpdates() ) {
230
  $bUpdate = false;
231
  }
232
- else if ( !$oFO->isDelayUpdates() ) {//TODO delay
233
- $bUpdate = $oFO->isAutoUpdateCoreMinor();
234
  }
235
  return $bUpdate;
236
  }
@@ -242,7 +242,7 @@ class ICWP_WPSF_Processor_Autoupdates extends Modules\BaseShield\ShieldProcessor
242
  * @return boolean
243
  */
244
  public function autoupdate_translations( $bUpdate ) {
245
- return $this->getMod()->isOpt( 'enable_autoupdate_translations', 'Y' );
246
  }
247
 
248
  /**
@@ -251,10 +251,10 @@ class ICWP_WPSF_Processor_Autoupdates extends Modules\BaseShield\ShieldProcessor
251
  * @return bool
252
  */
253
  public function autoupdate_core( $bDoAutoUpdate, $oCoreUpdate ) {
254
- /** @var ICWP_WPSF_FeatureHandler_Autoupdates $oFO */
255
- $oFO = $this->getMod();
256
 
257
- if ( $oFO->isDisableAllAutoUpdates() ) {
258
  $bDoAutoUpdate = false;
259
  }
260
  else if ( $this->isDelayed( $oCoreUpdate, 'core' ) ) {
@@ -270,10 +270,10 @@ class ICWP_WPSF_Processor_Autoupdates extends Modules\BaseShield\ShieldProcessor
270
  * @return boolean
271
  */
272
  public function autoupdate_plugins( $bDoAutoUpdate, $mItem ) {
273
- /** @var ICWP_WPSF_FeatureHandler_Autoupdates $oFO */
274
- $oFO = $this->getMod();
275
 
276
- if ( $oFO->isDisableAllAutoUpdates() ) {
277
  $bDoAutoUpdate = false;
278
  }
279
  else {
@@ -284,14 +284,14 @@ class ICWP_WPSF_Processor_Autoupdates extends Modules\BaseShield\ShieldProcessor
284
  }
285
 
286
  // first, is global auto updates for plugins set
287
- if ( $oFO->isAutoupdateAllPlugins() ) {
288
  $bDoAutoUpdate = true;
289
  }
290
- else if ( $oFO->isPluginSetToAutoupdate( $sFile ) ) {
291
  $bDoAutoUpdate = true;
292
  }
293
  else if ( $sFile === $this->getCon()->getPluginBaseFile() ) {
294
- $sAuto = $oFO->getSelfAutoUpdateOpt();
295
  if ( $sAuto === 'immediate' ) {
296
  $bDoAutoUpdate = true;
297
  }
@@ -310,10 +310,10 @@ class ICWP_WPSF_Processor_Autoupdates extends Modules\BaseShield\ShieldProcessor
310
  * @return boolean
311
  */
312
  public function autoupdate_themes( $bDoAutoUpdate, $mItem ) {
313
- /** @var ICWP_WPSF_FeatureHandler_Autoupdates $oFO */
314
- $oFO = $this->getMod();
315
 
316
- if ( $oFO->isDisableAllAutoUpdates() ) {
317
  $bDoAutoUpdate = false;
318
  }
319
  else {
@@ -341,15 +341,17 @@ class ICWP_WPSF_Processor_Autoupdates extends Modules\BaseShield\ShieldProcessor
341
  * @param string $sContext
342
  * @return bool
343
  */
344
- protected function isDelayed( $sSlug, $sContext = 'plugins' ) {
 
 
345
 
346
  $bDelayed = false;
347
 
348
  /** @var \ICWP_WPSF_FeatureHandler_Autoupdates $oFO */
349
  $oFO = $this->getMod();
350
- if ( $oFO->isDelayUpdates() ) {
351
 
352
- $aTk = $oFO->getDelayTracking();
353
 
354
  $sVersion = '';
355
  if ( $sContext == 'core' ) {
@@ -369,7 +371,7 @@ class ICWP_WPSF_Processor_Autoupdates extends Modules\BaseShield\ShieldProcessor
369
  }
370
 
371
  if ( !empty( $sVersion ) && isset( $aItemTk[ $sVersion ] ) ) {
372
- $bDelayed = ( Services::Request()->ts() - $aItemTk[ $sVersion ] < $oFO->getDelayUpdatesPeriod() );
373
  }
374
  }
375
 
@@ -382,9 +384,9 @@ class ICWP_WPSF_Processor_Autoupdates extends Modules\BaseShield\ShieldProcessor
382
  * @return boolean
383
  */
384
  public function autoupdate_send_email( $bSendEmail ) {
385
- /** @var ICWP_WPSF_FeatureHandler_Autoupdates $oFO */
386
- $oFO = $this->getMod();
387
- return $oFO->isSendAutoupdatesNotificationEmail();
388
  }
389
 
390
  /**
@@ -444,11 +446,12 @@ class ICWP_WPSF_Processor_Autoupdates extends Modules\BaseShield\ShieldProcessor
444
  if ( $sColumnName != 'icwp_autoupdate' ) {
445
  return;
446
  }
447
- /** @var ICWP_WPSF_FeatureHandler_Autoupdates $oFO */
448
- $oFO = $this->getMod();
 
449
  $bUpdate = Services::WpPlugins()->isPluginAutomaticallyUpdated( $sPluginBaseFileName );
450
- // $bUpdate = in_array( $sPluginBaseFileName, $oFO->getAutoupdatePlugins() );
451
- $bDisabled = $bUpdate && !in_array( $sPluginBaseFileName, $oFO->getAutoupdatePlugins() );
452
  echo $this->getPluginAutoupdateIconHtml( $sPluginBaseFileName, $bUpdate, $bDisabled );
453
  }
454
 
@@ -595,6 +598,6 @@ class ICWP_WPSF_Processor_Autoupdates extends Modules\BaseShield\ShieldProcessor
595
  * @return int
596
  */
597
  protected function getHookPriority() {
598
- return $this->getMod()->getDef( 'action_hook_priority' );
599
  }
600
  }
34
  * filter. What this filter decides will ultimately determine the fate of any core upgrade.
35
  */
36
  public function run() {
37
+ /** @var Modules\Autoupdates\Options $oOpts */
38
+ $oOpts = $this->getOptions();
39
 
40
  $nFilterPriority = $this->getHookPriority();
41
  add_filter( 'allow_minor_auto_core_updates', [ $this, 'autoupdate_core_minor' ], $nFilterPriority );
46
  add_filter( 'auto_update_theme', [ $this, 'autoupdate_themes' ], $nFilterPriority, 2 );
47
  add_filter( 'auto_update_core', [ $this, 'autoupdate_core' ], $nFilterPriority, 2 );
48
 
49
+ if ( $oOpts->isOpt( 'enable_autoupdate_ignore_vcs', 'Y' ) ) {
50
  add_filter( 'automatic_updates_is_vcs_checkout', '__return_false', $nFilterPriority );
51
  }
52
 
53
+ if ( !$oOpts->isDisableAllAutoUpdates() ) {
54
  //more parameter options here for later
55
  add_filter( 'auto_core_update_send_email', [ $this, 'autoupdate_send_email' ], $nFilterPriority, 1 );
56
  add_filter( 'auto_core_update_email', [ $this, 'autoupdate_email_override' ], $nFilterPriority, 1 );
59
  add_action( 'set_site_transient_update_plugins', [ $this, 'trackUpdateTimesPlugins' ] );
60
  add_action( 'set_site_transient_update_themes', [ $this, 'trackUpdateTimesThemes' ] );
61
 
62
+ if ( $oOpts->isSendAutoupdatesNotificationEmail() ) {
63
  $this->trackAssetsVersions();
64
  add_action( 'automatic_updates_complete', [ $this, 'sendNotificationEmail' ] );
65
  }
66
 
67
+ if ( $oOpts->isAutoupdateIndividualPlugins() ) {
68
  // Adds automatic update indicator column to all plugins in plugin listing.
69
  add_filter( 'manage_plugins_columns', [ $this, 'fAddPluginsListAutoUpdateColumn' ] );
70
  }
72
  }
73
 
74
  public function onWpLoaded() {
75
+ /** @var Modules\Autoupdates\Options $oOpts */
76
+ $oOpts = $this->getOptions();
77
+ if ( $oOpts->isDisableAllAutoUpdates() ) {
78
  $this->disableAllAutoUpdates();
79
  }
80
  else {
126
  public function trackUpdateTimesCore( $oUpdates ) {
127
 
128
  if ( !empty( $oUpdates ) && isset( $oUpdates->updates ) && is_array( $oUpdates->updates ) ) {
129
+ /** @var Modules\Autoupdates\Options $oOpts */
130
+ $oOpts = $this->getOptions();
131
 
132
+ $aTk = $oOpts->getDelayTracking();
133
  $aItemTk = isset( $aTk[ 'core' ][ 'wp' ] ) ? $aTk[ 'core' ][ 'wp' ] : [];
134
  foreach ( $oUpdates->updates as $oUpdate ) {
135
  if ( 'autoupdate' == $oUpdate->response ) {
140
  }
141
  }
142
  $aTk[ 'core' ][ 'wp' ] = array_slice( $aItemTk, -5 );
143
+ $oOpts->setDelayTracking( $aTk );
144
  }
145
  }
146
 
163
  * @param string $sContext - plugins/themes
164
  */
165
  protected function trackUpdateTimeCommon( $oUpdates, $sContext ) {
166
+ /** @var Modules\Autoupdates\Options $oOpts */
167
+ $oOpts = $this->getOptions();
168
 
169
  if ( !empty( $oUpdates ) && isset( $oUpdates->response ) && is_array( $oUpdates->response ) ) {
 
 
170
 
171
+ $aTk = $oOpts->getDelayTracking();
172
  foreach ( $oUpdates->response as $sSlug => $oUpdate ) {
173
  $aItemTk = isset( $aTk[ $sContext ][ $sSlug ] ) ? $aTk[ $sContext ][ $sSlug ] : [];
174
  if ( is_array( $oUpdate ) ) {
183
  $aTk[ $sContext ][ $sSlug ] = array_slice( $aItemTk, -3 );
184
  }
185
  }
186
+ $oOpts->setDelayTracking( $aTk );
187
  }
188
  }
189
 
203
  * @return boolean
204
  */
205
  public function autoupdate_core_major( $bUpdate ) {
206
+ /** @var Modules\Autoupdates\Options $oOpts */
207
+ $oOpts = $this->getOptions();
208
 
209
+ if ( $oOpts->isDisableAllAutoUpdates() ) {
210
  $bUpdate = false;
211
  }
212
+ else if ( !$oOpts->isDelayUpdates() ) { // the delay is handles elsewhere
213
+ $bUpdate = $oOpts->isAutoUpdateCoreMajor();
214
  }
215
 
216
  return $bUpdate;
223
  * @return boolean
224
  */
225
  public function autoupdate_core_minor( $bUpdate ) {
226
+ /** @var Modules\Autoupdates\Options $oOpts */
227
+ $oOpts = $this->getOptions();
228
 
229
+ if ( $oOpts->isDisableAllAutoUpdates() ) {
230
  $bUpdate = false;
231
  }
232
+ else if ( !$oOpts->isDelayUpdates() ) {//TODO delay
233
+ $bUpdate = $oOpts->isAutoUpdateCoreMinor();
234
  }
235
  return $bUpdate;
236
  }
242
  * @return boolean
243
  */
244
  public function autoupdate_translations( $bUpdate ) {
245
+ return $this->getOptions()->isOpt( 'enable_autoupdate_translations', 'Y' );
246
  }
247
 
248
  /**
251
  * @return bool
252
  */
253
  public function autoupdate_core( $bDoAutoUpdate, $oCoreUpdate ) {
254
+ /** @var Modules\Autoupdates\Options $oOpts */
255
+ $oOpts = $this->getOptions();
256
 
257
+ if ( $oOpts->isDisableAllAutoUpdates() ) {
258
  $bDoAutoUpdate = false;
259
  }
260
  else if ( $this->isDelayed( $oCoreUpdate, 'core' ) ) {
270
  * @return boolean
271
  */
272
  public function autoupdate_plugins( $bDoAutoUpdate, $mItem ) {
273
+ /** @var Modules\Autoupdates\Options $oOpts */
274
+ $oOpts = $this->getOptions();
275
 
276
+ if ( $oOpts->isDisableAllAutoUpdates() ) {
277
  $bDoAutoUpdate = false;
278
  }
279
  else {
284
  }
285
 
286
  // first, is global auto updates for plugins set
287
+ if ( $oOpts->isAutoupdateAllPlugins() ) {
288
  $bDoAutoUpdate = true;
289
  }
290
+ else if ( $oOpts->isPluginSetToAutoupdate( $sFile ) ) {
291
  $bDoAutoUpdate = true;
292
  }
293
  else if ( $sFile === $this->getCon()->getPluginBaseFile() ) {
294
+ $sAuto = $oOpts->getSelfAutoUpdateOpt();
295
  if ( $sAuto === 'immediate' ) {
296
  $bDoAutoUpdate = true;
297
  }
310
  * @return boolean
311
  */
312
  public function autoupdate_themes( $bDoAutoUpdate, $mItem ) {
313
+ /** @var Modules\Autoupdates\Options $oOpts */
314
+ $oOpts = $this->getOptions();
315
 
316
+ if ( $oOpts->isDisableAllAutoUpdates() ) {
317
  $bDoAutoUpdate = false;
318
  }
319
  else {
341
  * @param string $sContext
342
  * @return bool
343
  */
344
+ private function isDelayed( $sSlug, $sContext = 'plugins' ) {
345
+ /** @var Modules\Autoupdates\Options $oOpts */
346
+ $oOpts = $this->getOptions();
347
 
348
  $bDelayed = false;
349
 
350
  /** @var \ICWP_WPSF_FeatureHandler_Autoupdates $oFO */
351
  $oFO = $this->getMod();
352
+ if ( $oOpts->isDelayUpdates() ) {
353
 
354
+ $aTk = $oOpts->getDelayTracking();
355
 
356
  $sVersion = '';
357
  if ( $sContext == 'core' ) {
371
  }
372
 
373
  if ( !empty( $sVersion ) && isset( $aItemTk[ $sVersion ] ) ) {
374
+ $bDelayed = ( Services::Request()->ts() - $aItemTk[ $sVersion ] < $oOpts->getDelayUpdatesPeriod() );
375
  }
376
  }
377
 
384
  * @return boolean
385
  */
386
  public function autoupdate_send_email( $bSendEmail ) {
387
+ /** @var Modules\Autoupdates\Options $oOpts */
388
+ $oOpts = $this->getOptions();
389
+ return $oOpts->isSendAutoupdatesNotificationEmail();
390
  }
391
 
392
  /**
446
  if ( $sColumnName != 'icwp_autoupdate' ) {
447
  return;
448
  }
449
+ /** @var Modules\Autoupdates\Options $oOpts */
450
+ $oOpts = $this->getOptions();
451
+
452
  $bUpdate = Services::WpPlugins()->isPluginAutomaticallyUpdated( $sPluginBaseFileName );
453
+ // $bUpdate = in_array( $sPluginBaseFileName, $oOpts->getAutoupdatePlugins() );
454
+ $bDisabled = $bUpdate && !in_array( $sPluginBaseFileName, $oOpts->getAutoupdatePlugins() );
455
  echo $this->getPluginAutoupdateIconHtml( $sPluginBaseFileName, $bUpdate, $bDisabled );
456
  }
457
 
598
  * @return int
599
  */
600
  protected function getHookPriority() {
601
+ return $this->getOptions()->getDef( 'action_hook_priority' );
602
  }
603
  }
src/processors/base.php DELETED
@@ -1,265 +0,0 @@
1
- <?php
2
-
3
- use FernleafSystems\Wordpress\Plugin\Shield;
4
- use FernleafSystems\Wordpress\Services\Services;
5
-
6
- /**
7
- * Class ICWP_WPSF_Processor_Base
8
- * @deprecated 8.1
9
- */
10
- abstract class ICWP_WPSF_Processor_Base extends Shield\Deprecated\Foundation {
11
-
12
- use Shield\Modules\ModConsumer;
13
-
14
- /**
15
- * @var int
16
- */
17
- static protected $nPromoNoticesCount = 0;
18
-
19
- /**
20
- * @var ICWP_WPSF_Processor_Base[]
21
- */
22
- protected $aSubPros;
23
-
24
- /**
25
- * @var bool
26
- */
27
- private $bLoginCaptured;
28
-
29
- /**
30
- * @param \ICWP_WPSF_FeatureHandler_Base $oModCon
31
- */
32
- public function __construct( $oModCon ) {
33
- $this->setMod( $oModCon );
34
-
35
- add_action( 'init', [ $this, 'onWpInit' ], 9 );
36
- add_action( 'wp_loaded', [ $this, 'onWpLoaded' ] );
37
- { // Capture Logins
38
- add_action( 'wp_login', [ $this, 'onWpLogin' ], 10, 2 );
39
- if ( !Services::WpUsers()->isProfilePage() ) { // This can be fired during profile update.
40
- add_action( 'set_logged_in_cookie', [ $this, 'onWpSetLoggedInCookie' ], 5, 4 );
41
- }
42
- }
43
- add_action( $oModCon->prefix( 'plugin_shutdown' ), [ $this, 'onModuleShutdown' ] );
44
- add_action( $oModCon->prefix( 'daily_cron' ), [ $this, 'runDailyCron' ] );
45
- add_action( $oModCon->prefix( 'hourly_cron' ), [ $this, 'runHourlyCron' ] );
46
- add_action( $oModCon->prefix( 'deactivate_plugin' ), [ $this, 'deactivatePlugin' ] );
47
-
48
- /**
49
- * 2019-04-19:
50
- * wp_service_worker: added to prevent infinite page reloads triggered by an error with the PWA plugin.
51
- * It seems that using wp_localize_script() on a request with wp_service_worker=1 causes the worker
52
- * reload the page. Why exactly this happens hasn't been investigated, so we just skip any FRONTend
53
- * enqueues that might call wp_localize_script() for these requests.
54
- */
55
- if ( Services::Request()->query( 'wp_service_worker', 0 ) != 1 ) {
56
- add_action( 'wp_enqueue_scripts', [ $this, 'onWpEnqueueJs' ] );
57
- }
58
-
59
- $this->init();
60
- }
61
-
62
- public function onWpInit() {
63
- }
64
-
65
- public function onWpLoaded() {
66
- }
67
-
68
- public function onWpEnqueueJs() {
69
- }
70
-
71
- /**
72
- * @param string $sUsername
73
- * @param WP_User $oUser
74
- */
75
- public function onWpLogin( $sUsername, $oUser ) {
76
- /*
77
- if ( !$oUser instanceof WP_User ) {
78
- $oUser = $this->loadWpUsers()->getUserByUsername( $sUsername );
79
- }
80
- */
81
- }
82
-
83
- /**
84
- * @param string $sCookie
85
- * @param int $nExpire
86
- * @param int $nExpiration
87
- * @param int $nUserId
88
- */
89
- public function onWpSetLoggedInCookie( $sCookie, $nExpire, $nExpiration, $nUserId ) {
90
- }
91
-
92
- /**
93
- * @return bool
94
- */
95
- protected function isLoginCaptured() {
96
- return (bool)$this->bLoginCaptured;
97
- }
98
-
99
- public function runDailyCron() {
100
- }
101
-
102
- public function runHourlyCron() {
103
- }
104
-
105
- /**
106
- * @return $this
107
- */
108
- protected function setLoginCaptured() {
109
- $this->bLoginCaptured = true;
110
- return $this;
111
- }
112
-
113
- /**
114
- * @return int
115
- */
116
- protected function getPromoNoticesCount() {
117
- return self::$nPromoNoticesCount;
118
- }
119
-
120
- /**
121
- * @return $this
122
- */
123
- protected function incrementPromoNoticesCount() {
124
- self::$nPromoNoticesCount++;
125
- return $this;
126
- }
127
-
128
- public function onModuleShutdown() {
129
- }
130
-
131
- /**
132
- */
133
- public function init() {
134
- }
135
-
136
- /**
137
- * @return bool
138
- */
139
- public function isReadyToRun() {
140
- return true;
141
- }
142
-
143
- /**
144
- * Override to set what this processor does when it's "run"
145
- */
146
- public function run() {
147
- }
148
-
149
- /**
150
- * @param array $aNoticeData
151
- * @throws \Exception
152
- */
153
- protected function insertAdminNotice( $aNoticeData ) {
154
- $aAttrs = $aNoticeData[ 'notice_attributes' ];
155
- $bIsPromo = isset( $aAttrs[ 'type' ] ) && $aAttrs[ 'type' ] == 'promo';
156
- if ( $bIsPromo && $this->getPromoNoticesCount() > 0 ) {
157
- return;
158
- }
159
-
160
- $bCantDismiss = isset( $aNoticeData[ 'notice_attributes' ][ 'can_dismiss' ] )
161
- && !$aNoticeData[ 'notice_attributes' ][ 'can_dismiss' ];
162
-
163
- $oNotices = $this->loadWpNotices();
164
- if ( $bCantDismiss || !$oNotices->isDismissed( $aAttrs[ 'id' ] ) ) {
165
-
166
- $sRenderedNotice = $this->getMod()->renderAdminNotice( $aNoticeData );
167
- if ( !empty( $sRenderedNotice ) ) {
168
- $oNotices->addAdminNotice(
169
- $sRenderedNotice,
170
- $aNoticeData[ 'notice_attributes' ][ 'notice_id' ]
171
- );
172
- if ( $bIsPromo ) {
173
- $this->incrementPromoNoticesCount();
174
- }
175
- }
176
- }
177
- }
178
-
179
- /**
180
- * @param $sOptionKey
181
- * @param mixed $mDefault
182
- * @return mixed
183
- */
184
- public function getOption( $sOptionKey, $mDefault = false ) {
185
- return $this->getMod()->getOpt( $sOptionKey, $mDefault );
186
- }
187
-
188
- /**
189
- * We don't handle locale derivatives (yet)
190
- * @return string
191
- */
192
- protected function getGoogleRecaptchaLocale() {
193
- return Services::WpGeneral()->getLocale( '-' );
194
- }
195
-
196
- /**
197
- * @return ICWP_WPSF_Processor_Email
198
- */
199
- public function getEmailProcessor() {
200
- return $this->getMod()->getEmailProcessor();
201
- }
202
-
203
- /**
204
- * @param string $sKey
205
- * @return ICWP_WPSF_Processor_Base|mixed|null
206
- */
207
- protected function getSubPro( $sKey ) {
208
- $aProcessors = $this->getSubProcessors();
209
- if ( !isset( $aProcessors[ $sKey ] ) ) {
210
- $aMap = $this->getSubProMap();
211
- if ( !isset( $aMap[ $sKey ] ) ) {
212
- error_log( 'Sub processor key not set: '.$sKey );
213
- }
214
- $aProcessors[ $sKey ] = new $aMap[ $sKey ]( $this->getMod() );
215
- }
216
- return $aProcessors[ $sKey ];
217
- }
218
-
219
- /**
220
- * @return array
221
- */
222
- protected function getSubProMap() {
223
- return [];
224
- }
225
-
226
- /**
227
- */
228
- public function deactivatePlugin() {
229
- }
230
-
231
- /**
232
- * @return ICWP_WPSF_Processor_Base[]
233
- */
234
- protected function getSubProcessors() {
235
- if ( !isset( $this->aSubPros ) ) {
236
- $this->aSubPros = [];
237
- }
238
- return $this->aSubPros;
239
- }
240
-
241
- /**
242
- * @deprecated 8.1
243
- */
244
- public function autoAddToAdminNotices() {
245
- }
246
-
247
- /**
248
- * @return string
249
- * @deprecated 8.1
250
- */
251
- protected function ip() {
252
- return Services::IP()->getRequestIp();
253
- }
254
-
255
- /**
256
- * Will prefix and return any string with the unique plugin prefix.
257
- * @param string $sSuffix
258
- * @param string $sGlue
259
- * @return string
260
- * @deprecated 8.1
261
- */
262
- protected function prefix( $sSuffix = '', $sGlue = '-' ) {
263
- return $this->getCon()->prefix( $sSuffix, $sGlue );
264
- }
265
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/processors/base_plugin.php DELETED
@@ -1,10 +0,0 @@
1
- <?php
2
-
3
- use FernleafSystems\Wordpress\Services\Services;
4
-
5
- /**
6
- * Class ICWP_WPSF_Processor_BasePlugin
7
- * @deprecated 8.1
8
- */
9
- class ICWP_WPSF_Processor_BasePlugin extends ICWP_WPSF_Processor_BaseWpsf {
10
- }
 
 
 
 
 
 
 
 
 
 
src/processors/base_wpsf.php DELETED
@@ -1,198 +0,0 @@
1
- <?php
2
-
3
- use FernleafSystems\Wordpress\Plugin\Shield;
4
- use FernleafSystems\Wordpress\Services\Services;
5
-
6
- /**
7
- * Class ICWP_WPSF_Processor_BaseWpsf
8
- * @deprecated 8.1
9
- */
10
- abstract class ICWP_WPSF_Processor_BaseWpsf extends ICWP_WPSF_Processor_Base {
11
-
12
- const RECAPTCHA_JS_HANDLE = 'icwp-google-recaptcha';
13
-
14
- /**
15
- * @var array
16
- */
17
- private $aStatistics;
18
-
19
- /**
20
- * @var bool
21
- */
22
- private static $bRecaptchaEnqueue = false;
23
-
24
- /**
25
- * @var bool
26
- */
27
- private $bLogRequest;
28
-
29
- /**
30
- * Resets the object values to be re-used anew
31
- */
32
- public function init() {
33
- parent::init();
34
- $oFO = $this->getMod();
35
- add_filter( $oFO->prefix( 'collect_tracking_data' ), [ $this, 'tracking_DataCollect' ] );
36
- }
37
-
38
- /**
39
- * @return int
40
- */
41
- protected function getInstallationDays() {
42
- $nTimeInstalled = $this->getCon()
43
- ->getModule_Plugin()
44
- ->getInstallDate();
45
- if ( empty( $nTimeInstalled ) ) {
46
- return 0;
47
- }
48
- return (int)round( ( Services::Request()->ts() - $nTimeInstalled )/DAY_IN_SECONDS );
49
- }
50
-
51
- /**
52
- * @param WP_User $oUser
53
- * @return bool
54
- */
55
- protected function isUserSubjectToLoginIntent( $oUser = null ) {
56
- $bIsSubject = false;
57
-
58
- if ( !$oUser instanceof WP_User ) {
59
- $oUser = Services::WpUsers()->getCurrentWpUser();
60
- }
61
- if ( $oUser instanceof WP_User ) {
62
- $bIsSubject = apply_filters( $this->getCon()->prefix( 'user_subject_to_login_intent' ), false, $oUser );
63
- }
64
-
65
- return $bIsSubject;
66
- }
67
-
68
- /**
69
- * @return bool
70
- */
71
- protected function getRecaptchaTheme() {
72
- /** @var \ICWP_WPSF_FeatureHandler_BaseWpsf $oFO */
73
- $oFO = $this->getMod();
74
- return $this->isRecaptchaInvisible() ? 'light' : $oFO->getGoogleRecaptchaStyle();
75
- }
76
-
77
- /**
78
- * @return bool
79
- */
80
- protected function getIfLogRequest() {
81
- return isset( $this->bLogRequest ) ? (bool)$this->bLogRequest : !Services::WpGeneral()->isCron();
82
- }
83
-
84
- /**
85
- * @param bool $bLog
86
- * @return $this
87
- */
88
- protected function setIfLogRequest( $bLog ) {
89
- $this->bLogRequest = $bLog;
90
- return $this;
91
- }
92
-
93
- /**
94
- * @return bool
95
- */
96
- protected function isRecaptchaInvisible() {
97
- /** @var \ICWP_WPSF_FeatureHandler_BaseWpsf $oFO */
98
- $oFO = $this->getMod();
99
- return ( $oFO->getGoogleRecaptchaStyle() == 'invisible' );
100
- }
101
-
102
- public function registerGoogleRecaptchaJs() {
103
- $sJsUri = add_query_arg(
104
- [
105
- 'hl' => $this->getGoogleRecaptchaLocale(),
106
- 'onload' => 'onLoadIcwpRecaptchaCallback',
107
- 'render' => 'explicit',
108
- ],
109
- 'https://www.google.com/recaptcha/api.js'
110
- );
111
- wp_register_script( self::RECAPTCHA_JS_HANDLE, $sJsUri, [], false, true );
112
- wp_enqueue_script( self::RECAPTCHA_JS_HANDLE );
113
-
114
- // This also gives us the chance to remove recaptcha before it's printed, if it isn't needed
115
- add_action( 'wp_footer', [ $this, 'maybeDequeueRecaptcha' ], -100 );
116
- add_action( 'login_footer', [ $this, 'maybeDequeueRecaptcha' ], -100 );
117
-
118
- Services::Includes()
119
- ->addIncludeAttribute( self::RECAPTCHA_JS_HANDLE, 'async', 'async' )
120
- ->addIncludeAttribute( self::RECAPTCHA_JS_HANDLE, 'defer', 'defer' );
121
- /**
122
- * Change to recaptcha implementation now means
123
- * 1 - the form will not submit unless the recaptcha has been executed (either invisible or manual)
124
- */
125
- }
126
-
127
- /**
128
- * @return array
129
- */
130
- public function stats_Get() {
131
- if ( !isset( $this->aStatistics ) || !is_array( $this->aStatistics ) ) {
132
- $this->aStatistics = [];
133
- }
134
- return $this->aStatistics;
135
- }
136
-
137
- /**
138
- * Filter used to collect plugin data for tracking. Fired from the plugin processor only if the option is enabled
139
- * - it is not enabled by default.
140
- * Note that in this case we "mask" options that have been identified as "sensitive" - i.e. could contain
141
- * identifiable data.
142
- *
143
- * @param $aData
144
- * @return array
145
- */
146
- public function tracking_DataCollect( $aData ) {
147
- if ( !is_array( $aData ) ) {
148
- $aData = [];
149
- }
150
- $oFO = $this->getMod();
151
- $aData[ $oFO->getSlug() ] = [ 'options' => $oFO->collectOptionsForTracking() ];
152
- return $aData;
153
- }
154
-
155
- /**
156
- * If recaptcha is required, it prints the necessary snippet and does not remove the enqueue
157
- *
158
- * @throws \Exception
159
- */
160
- public function maybeDequeueRecaptcha() {
161
-
162
- if ( $this->isRecaptchaEnqueue() ) {
163
- /** @var ICWP_WPSF_FeatureHandler_BaseWpsf $oFO */
164
- $oFO = $this->getMod();
165
- echo $oFO->renderTemplate(
166
- 'snippets/google_recaptcha_js',
167
- [
168
- 'sitekey' => $oFO->getGoogleRecaptchaSiteKey(),
169
- 'size' => $this->isRecaptchaInvisible() ? 'invisible' : '',
170
- 'theme' => $this->getRecaptchaTheme(),
171
- 'invis' => $this->isRecaptchaInvisible(),
172
- ]
173
-
174
- );
175
- }
176
- else {
177
- wp_dequeue_script( self::RECAPTCHA_JS_HANDLE );
178
- }
179
- }
180
-
181
- /**
182
- * @return bool
183
- */
184
- public function isRecaptchaEnqueue() {
185
- return self::$bRecaptchaEnqueue;
186
- }
187
-
188
- /**
189
- * Note we don't provide a 'false' option here as if it's set to be needed somewhere,
190
- * it shouldn't be unset anywhere else.
191
- *
192
- * @return $this
193
- */
194
- public function setRecaptchaToEnqueue() {
195
- self::$bRecaptchaEnqueue = true;
196
- return $this;
197
- }
198
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/processors/basedb.php DELETED
@@ -1,103 +0,0 @@
1
- <?php
2
-
3
- use FernleafSystems\Wordpress\Plugin\Shield;
4
- use FernleafSystems\Wordpress\Services\Services;
5
-
6
- /**
7
- * Class ICWP_WPSF_BaseDbProcessor
8
- * @deprecated 8.1
9
- */
10
- class ICWP_WPSF_BaseDbProcessor extends ICWP_WPSF_Processor_BaseWpsf {
11
-
12
- /**
13
- * @var \FernleafSystems\Wordpress\Plugin\Shield\Databases\Base\Handler
14
- */
15
- protected $oDbh;
16
-
17
- /**
18
- * @var integer
19
- */
20
- protected $nAutoExpirePeriod = null;
21
-
22
- /**
23
- * @return bool
24
- */
25
- public function isReadyToRun() {
26
- try {
27
- return ( parent::isReadyToRun() && $this->getDbHandler()->isReady() );
28
- }
29
- catch ( \Exception $oE ) {
30
- return false;
31
- }
32
- }
33
-
34
- /**
35
- * @return Shield\Databases\Base\Handler
36
- */
37
- public function getDbHandler() {
38
- if ( !isset( $this->oDbh ) ) {
39
- $this->oDbh = $this->getMod()->getDbHandler();
40
- }
41
- return $this->oDbh;
42
- }
43
-
44
- /**
45
- * @return string
46
- */
47
- protected function getCreateTableSql() {
48
- }
49
-
50
- /**
51
- * @return array
52
- */
53
- protected function getTableColumnsByDefinition() {
54
- }
55
-
56
- public function runDailyCron() {
57
- try {
58
- if ( $this->getDbHandler()->isReady() ) {
59
- $this->cleanupDatabase();
60
- }
61
- }
62
- catch ( \Exception $oE ) {
63
- }
64
- }
65
-
66
- /**
67
- * @return bool|int
68
- */
69
- public function cleanupDatabase() {
70
- $nAutoExpirePeriod = $this->getAutoExpirePeriod();
71
- if ( is_null( $nAutoExpirePeriod ) || !$this->getDbHandler()->isTable() ) {
72
- return false;
73
- }
74
- $nTimeStamp = Services::Request()->ts() - $nAutoExpirePeriod;
75
- return $this->getDbHandler()->deleteRowsOlderThan( $nTimeStamp );
76
- }
77
-
78
- /**
79
- * 1 in 20 page loads will clean the databases. This ensures that even if the crons don't run
80
- * correctly, we'll keep it trim.
81
- */
82
- public function onModuleShutdown() {
83
- parent::onModuleShutdown();
84
- if ( rand( 1, 20 ) === 2 ) {
85
- $this->cleanupDatabase();
86
- }
87
- }
88
-
89
- /**
90
- * @return int
91
- */
92
- protected function getAutoExpirePeriod() {
93
- return null;
94
- }
95
-
96
- /**
97
- * @param string $sTableName
98
- * @throws \Exception
99
- * @deprecated 8.1
100
- */
101
- protected function initializeTable( $sTableName ) {
102
- }
103
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/processors/comments_filter.php CHANGED
@@ -63,7 +63,7 @@ class ICWP_WPSF_Processor_CommentsFilter extends Modules\BaseShield\ShieldProces
63
  }
64
 
65
  /**
66
- * @return ICWP_WPSF_Processor_CommentsFilter_BotSpam
67
  */
68
  private function getSubProGasp() {
69
  return $this->getSubPro( 'bot' );
63
  }
64
 
65
  /**
66
+ * @return \ICWP_WPSF_Processor_CommentsFilter_BotSpam
67
  */
68
  private function getSubProGasp() {
69
  return $this->getSubPro( 'bot' );
src/processors/commentsfilter_botspam.php CHANGED
@@ -11,66 +11,87 @@ class ICWP_WPSF_Processor_CommentsFilter_BotSpam extends Modules\BaseShield\Shie
11
  */
12
  private $sFormId;
13
 
14
- public function run() {
15
- add_action( 'comment_form', [ $this, 'printGaspFormItems' ], 1 );
16
- }
17
-
18
- public function printGaspFormItems() {
19
- echo $this->getGaspCommentsHtml();
20
- }
21
-
22
  /**
23
- * @return string
24
  */
25
- private function getGaspCommentsHtml() {
26
- /** @var \ICWP_WPSF_FeatureHandler_CommentsFilter $oMod */
27
- $oMod = $this->getMod();
28
 
29
- $sCommentWait = $oMod->getTextOpt( 'custom_message_comment_wait' );
30
- $aData = [
31
- 'form_id' => $this->getUniqueFormId(),
32
- 'ts' => Services::Request()->ts(),
33
- 'token' => $this->tokenCreateStore(),
34
- 'alert' => $oMod->getTextOpt( 'custom_message_alert' ),
35
- 'comment_reload' => $oMod->getTextOpt( 'custom_message_comment_reload' ),
36
- 'cooldown' => $oMod->getTokenCooldown(),
37
- 'expire' => $oMod->getTokenExpireInterval(),
38
- 'comment_wait' => $sCommentWait,
39
- 'js_comment_wait' => str_replace( '%s', '"+nRemaining+"', $sCommentWait ),
40
- 'confirm' => $oMod->getTextOpt( 'custom_message_checkbox' ),
41
- ];
42
 
43
- return $oMod->renderTemplate( 'snippets/comment_form_botbox.twig', $aData, true );
 
44
  }
45
 
46
- /**
47
- * @return string
48
- */
49
- private function tokenCreateStore() {
50
  /** @var \ICWP_WPSF_FeatureHandler_CommentsFilter $oMod */
51
  $oMod = $this->getMod();
 
 
 
 
 
 
 
 
 
 
 
 
52
 
53
  $nTs = Services::Request()->ts();
54
- $nPostId = Services::WpPost()->getCurrentPostId();
55
- $sToken = $this->generateNewToken( $nTs, $nPostId );
 
56
 
57
- Services::WpGeneral()->setTransient(
58
- $oMod->prefix( 'comtok-'.md5( sprintf( '%s-%s-%s', $nPostId, $nTs, Services::IP()->getRequestIp() ) ) ),
59
- $sToken,
60
- $oMod->getTokenExpireInterval()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
61
  );
62
-
63
- return $sToken;
64
  }
65
 
66
  /**
67
- * @param int $nTs
68
- * @param string $nPostId
69
- * @return string
70
  */
71
- private function generateNewToken( $nTs, $nPostId ) {
72
- $oMod = $this->getCon()->getModule_Plugin();
73
- return hash_hmac( 'sha1', $nPostId.Services::IP()->getRequestIp().$nTs, $oMod->getPluginInstallationId() );
 
 
 
 
 
 
 
 
 
 
 
74
  }
75
 
76
  /**
@@ -86,4 +107,20 @@ class ICWP_WPSF_Processor_CommentsFilter_BotSpam extends Modules\BaseShield\Shie
86
  }
87
  return $this->sFormId;
88
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
89
  }
11
  */
12
  private $sFormId;
13
 
 
 
 
 
 
 
 
 
14
  /**
15
+ * @var bool
16
  */
17
+ private $bFormItemPrinted = false;
 
 
18
 
19
+ public function run() {
20
+ add_action( 'wp', [ $this, 'onWp' ] );
21
+ add_action( 'wp_footer', [ $this, 'maybeDequeueScript' ] );
22
+ }
 
 
 
 
 
 
 
 
 
23
 
24
+ public function onWp() {
25
+ add_action( 'comment_form', [ $this, 'printGaspFormItems' ], 1 );
26
  }
27
 
28
+ public function onWpEnqueueJs() {
 
 
 
29
  /** @var \ICWP_WPSF_FeatureHandler_CommentsFilter $oMod */
30
  $oMod = $this->getMod();
31
+ $oConn = $this->getCon();
32
+
33
+ $sAsset = 'shield-comments';
34
+ $sUnique = $oMod->prefix( 'shield-comments' );
35
+ wp_register_script(
36
+ $sUnique,
37
+ $oConn->getPluginUrl_Js( $sAsset ),
38
+ [ 'jquery' ],
39
+ $oConn->getVersion(),
40
+ true
41
+ );
42
+ wp_enqueue_script( $sUnique );
43
 
44
  $nTs = Services::Request()->ts();
45
+ $aNonce = $oMod->getAjaxActionData( 'comment_token'.Services::IP()->getRequestIp() );
46
+ $aNonce[ 'ts' ] = $nTs;
47
+ $aNonce[ 'post_id' ] = Services::WpPost()->getCurrentPostId();
48
 
49
+ wp_localize_script(
50
+ $sUnique,
51
+ 'shield_comments',
52
+ [
53
+ 'ajax' => [
54
+ 'comment_token' => $aNonce,
55
+ ],
56
+ 'vars' => [
57
+ 'cbname' => 'cb_nombre'.rand(),
58
+ 'botts' => $nTs,
59
+ 'token' => 'not created',
60
+ 'uniq' => $this->getUniqueFormId(),
61
+ 'cooldown' => $oMod->getTokenCooldown(),
62
+ 'expires' => $oMod->getTokenExpireInterval(),
63
+ ],
64
+ 'strings' => [
65
+ 'label' => $oMod->getTextOpt( 'custom_message_checkbox' ),
66
+ 'alert' => $oMod->getTextOpt( 'custom_message_alert' ),
67
+ 'comment_reload' => $oMod->getTextOpt( 'custom_message_comment_reload' ),
68
+ 'js_comment_wait' => $oMod->getTextOpt( 'custom_message_comment_wait' ),
69
+ ],
70
+ 'flags' => [
71
+ 'gasp' => true,
72
+ 'recap' => $oMod->isGoogleRecaptchaEnabled(),
73
+ ]
74
+ ]
75
  );
 
 
76
  }
77
 
78
  /**
79
+ * If the comment form component hasn't been printed, there's no comment form to protect.
 
 
80
  */
81
+ public function maybeDequeueScript() {
82
+ if ( empty( $this->bFormItemPrinted ) ) {
83
+ wp_dequeue_script( $this->getCon()->prefix( 'shield-comments' ) );
84
+ }
85
+ }
86
+
87
+ public function printGaspFormItems() {
88
+ $this->bFormItemPrinted = true;
89
+ echo $this->getMod()
90
+ ->renderTemplate(
91
+ 'snippets/comment_form_botbox.twig',
92
+ [ 'uniq' => $this->getUniqueFormId() ],
93
+ true
94
+ );
95
  }
96
 
97
  /**
107
  }
108
  return $this->sFormId;
109
  }
110
+
111
+ /**
112
+ * @return string
113
+ * @deprecated 8.4
114
+ */
115
+ private function getGaspCommentsHtml() {
116
+ return '';
117
+ }
118
+
119
+ /**
120
+ * @return string
121
+ * @deprecated 8.4
122
+ */
123
+ public function tokenCreateStore() {
124
+ return '';
125
+ }
126
  }
src/processors/events.php CHANGED
@@ -110,4 +110,10 @@ class ICWP_WPSF_Processor_Events extends Shield\Modules\BaseShield\ShieldProcess
110
  $oMod->getDbHandler_Events()
111
  ->commitEvents( $oMod->getRegisteredEvents( true ) );
112
  }
 
 
 
 
 
 
113
  }
110
  $oMod->getDbHandler_Events()
111
  ->commitEvents( $oMod->getRegisteredEvents( true ) );
112
  }
113
+
114
+ public function runDailyCron() {
115
+ ( new Shield\Modules\Events\Consolidate\ConsolidateAllEvents() )
116
+ ->setMod( $this->getMod() )
117
+ ->run();
118
+ }
119
  }
src/processors/firewall.php CHANGED
@@ -228,7 +228,7 @@ class ICWP_WPSF_Processor_Firewall extends Modules\BaseShield\ShieldProcessor {
228
  */
229
  protected function getFirewallPatterns( $sKey = null ) {
230
  if ( !isset( $this->aPatterns ) ) {
231
- $this->aPatterns = $this->getMod()->getDef( 'firewall_patterns' );
232
  }
233
  if ( !empty( $sKey ) ) {
234
  return isset( $this->aPatterns[ $sKey ] ) ? $this->aPatterns[ $sKey ] : null;
228
  */
229
  protected function getFirewallPatterns( $sKey = null ) {
230
  if ( !isset( $this->aPatterns ) ) {
231
+ $this->aPatterns = $this->getOptions()->getDef( 'firewall_patterns' );
232
  }
233
  if ( !empty( $sKey ) ) {
234
  return isset( $this->aPatterns[ $sKey ] ) ? $this->aPatterns[ $sKey ] : null;
src/processors/hackprotect_scan_base.php CHANGED
@@ -408,11 +408,4 @@ abstract class ICWP_WPSF_Processor_ScanBase extends Shield\Modules\BaseShield\Sh
408
  $this->oScanner = $oScanner;
409
  return $this;
410
  }
411
-
412
- /**
413
- * @param Shield\Scans\Base\BaseScanActionVO $oAction
414
- * @deprecated 8.1
415
- */
416
- public function postScanActionProcess( $oAction ) {
417
- }
418
  }
408
  $this->oScanner = $oScanner;
409
  return $this;
410
  }
 
 
 
 
 
 
 
411
  }
src/processors/hackprotect_scan_mal.php CHANGED
@@ -162,7 +162,7 @@ class ICWP_WPSF_Processor_HackProtect_Mal extends ICWP_WPSF_Processor_ScanBase {
162
  else {
163
  $aContent[] = __( 'You should review these files and replace them with official versions if required.', 'wp-simple-firewall' );
164
  $aContent[] = __( 'Alternatively you can have the plugin attempt to repair/replace these files automatically.', 'wp-simple-firewall' )
165
- .' [<a href="https://icwp.io/g2">'.__( 'More Info', 'wp-simple-firewall' ).'</a>]';
166
  }
167
  }
168
 
@@ -172,7 +172,7 @@ class ICWP_WPSF_Processor_HackProtect_Mal extends ICWP_WPSF_Processor_ScanBase {
172
 
173
  if ( !$this->getCon()->isRelabelled() ) {
174
  $aContent[] = '';
175
- $aContent[] = '[ <a href="https://icwp.io/moreinfochecksum">'.__( 'More Info On This Scanner', 'wp-simple-firewall' ).'</a> ]';
176
  }
177
 
178
  return $aContent;
162
  else {
163
  $aContent[] = __( 'You should review these files and replace them with official versions if required.', 'wp-simple-firewall' );
164
  $aContent[] = __( 'Alternatively you can have the plugin attempt to repair/replace these files automatically.', 'wp-simple-firewall' )
165
+ .' [<a href="https://shsec.io/g2">'.__( 'More Info', 'wp-simple-firewall' ).'</a>]';
166
  }
167
  }
168
 
172
 
173
  if ( !$this->getCon()->isRelabelled() ) {
174
  $aContent[] = '';
175
+ $aContent[] = '[ <a href="https://shsec.io/moreinfochecksum">'.__( 'More Info On This Scanner', 'wp-simple-firewall' ).'</a> ]';
176
  }
177
 
178
  return $aContent;
src/processors/hackprotect_scan_ufc.php CHANGED
@@ -133,7 +133,7 @@ class ICWP_WPSF_Processor_HackProtect_Ufc extends ICWP_WPSF_Processor_ScanBase {
133
  $aContent[] = '';
134
 
135
  if ( !$oCon->isRelabelled() ) {
136
- $aContent[] = sprintf( '[ <a href="https://icwp.io/moreinfoufc">%s</a> ]', __( 'More Info On This Scanner', 'wp-simple-firewall' ) );
137
  }
138
 
139
  return $aContent;
133
  $aContent[] = '';
134
 
135
  if ( !$oCon->isRelabelled() ) {
136
+ $aContent[] = sprintf( '[ <a href="https://shsec.io/moreinfoufc">%s</a> ]', __( 'More Info On This Scanner', 'wp-simple-firewall' ) );
137
  }
138
 
139
  return $aContent;
src/processors/hackprotect_scan_wcf.php CHANGED
@@ -109,7 +109,7 @@ class ICWP_WPSF_Processor_HackProtect_Wcf extends ICWP_WPSF_Processor_ScanBase {
109
  else {
110
  $aContent[] = __( 'You should review these files and replace them with official versions if required.', 'wp-simple-firewall' );
111
  $aContent[] = __( 'Alternatively you can have the plugin attempt to repair/replace these files automatically.', 'wp-simple-firewall' )
112
- .' [<a href="https://icwp.io/moreinfochecksum">'.__( 'More Info', 'wp-simple-firewall' ).'</a>]';
113
  }
114
  }
115
 
@@ -119,7 +119,7 @@ class ICWP_WPSF_Processor_HackProtect_Wcf extends ICWP_WPSF_Processor_ScanBase {
119
 
120
  if ( !$this->getCon()->isRelabelled() ) {
121
  $aContent[] = '';
122
- $aContent[] = '[ <a href="https://icwp.io/moreinfochecksum">'.__( 'More Info On This Scanner', 'wp-simple-firewall' ).'</a> ]';
123
  }
124
 
125
  return $aContent;
109
  else {
110
  $aContent[] = __( 'You should review these files and replace them with official versions if required.', 'wp-simple-firewall' );
111
  $aContent[] = __( 'Alternatively you can have the plugin attempt to repair/replace these files automatically.', 'wp-simple-firewall' )
112
+ .' [<a href="https://shsec.io/moreinfochecksum">'.__( 'More Info', 'wp-simple-firewall' ).'</a>]';
113
  }
114
  }
115
 
119
 
120
  if ( !$this->getCon()->isRelabelled() ) {
121
  $aContent[] = '';
122
+ $aContent[] = '[ <a href="https://shsec.io/moreinfochecksum">'.__( 'More Info On This Scanner', 'wp-simple-firewall' ).'</a> ]';
123
  }
124
 
125
  return $aContent;
src/processors/hackprotect_scanner.php CHANGED
@@ -215,11 +215,4 @@ class ICWP_WPSF_Processor_HackProtect_Scanner extends ShieldProcessor {
215
  protected function getCronName() {
216
  return $this->getCon()->prefix( $this->getOptions()->getDef( 'cron_all_scans' ) );
217
  }
218
-
219
- /**
220
- * @param string $sScanSlug
221
- * @deprecated 8.1
222
- */
223
- public function launchScan( $sScanSlug ) {
224
- }
225
  }
215
  protected function getCronName() {
216
  return $this->getCon()->prefix( $this->getOptions()->getDef( 'cron_all_scans' ) );
217
  }
 
 
 
 
 
 
 
218
  }
src/processors/ips.php CHANGED
@@ -391,9 +391,15 @@ class ICWP_WPSF_Processor_Ips extends ShieldProcessor {
391
  /**
392
  * @param string $sIp
393
  * @return bool
 
394
  */
395
  public function isIpOnWhiteList( $sIp ) {
396
- return $this->isIpOnList( $sIp, ICWP_WPSF_FeatureHandler_Ips::LIST_MANUAL_WHITE );
 
 
 
 
 
397
  }
398
 
399
  /**
@@ -411,6 +417,7 @@ class ICWP_WPSF_Processor_Ips extends ShieldProcessor {
411
  * @param string $sIp
412
  * @param string $sList
413
  * @return bool
 
414
  */
415
  private function isIpOnList( $sIp, $sList ) {
416
  $bOnList = false;
391
  /**
392
  * @param string $sIp
393
  * @return bool
394
+ * @deprecated 8.4
395
  */
396
  public function isIpOnWhiteList( $sIp ) {
397
+ $oIp = ( new IPs\Components\LookupIpOnList() )
398
+ ->setMod( $this->getMod() )
399
+ ->setIp( $sIp )
400
+ ->setList( ICWP_WPSF_FeatureHandler_Ips::LIST_MANUAL_WHITE )
401
+ ->lookup();
402
+ return $oIp instanceof Databases\IPs\EntryVO;
403
  }
404
 
405
  /**
417
  * @param string $sIp
418
  * @param string $sList
419
  * @return bool
420
+ * @deprecated 8.4
421
  */
422
  private function isIpOnList( $sIp, $sList ) {
423
  $bOnList = false;
src/processors/lockdown.php CHANGED
@@ -92,7 +92,7 @@ class ICWP_WPSF_Processor_Lockdown extends Modules\BaseShield\ShieldProcessor {
92
  Services::WpGeneral()->wpDie( sprintf(
93
  __( 'The "author" query parameter has been blocked by %s to protect against user login name fishing.', 'wp-simple-firewall' )
94
  .sprintf( '<br /><a href="%s" target="_blank">%s</a>',
95
- 'https://icwp.io/7l',
96
  __( 'Learn More.', 'wp-simple-firewall' )
97
  ),
98
  $this->getCon()->getHumanName()
92
  Services::WpGeneral()->wpDie( sprintf(
93
  __( 'The "author" query parameter has been blocked by %s to protect against user login name fishing.', 'wp-simple-firewall' )
94
  .sprintf( '<br /><a href="%s" target="_blank">%s</a>',
95
+ 'https://shsec.io/7l',
96
  __( 'Learn More.', 'wp-simple-firewall' )
97
  ),
98
  $this->getCon()->getHumanName()
src/processors/loginprotect_intentprovider_email.php CHANGED
@@ -72,7 +72,7 @@ class ICWP_WPSF_Processor_LoginProtect_TwoFactorAuth extends ICWP_WPSF_Processor
72
  'value' => $this->fetchCodeFromRequest(),
73
  'placeholder' => __( 'This code was just sent to your registered Email address.', 'wp-simple-firewall' ),
74
  'text' => __( 'Email OTP', 'wp-simple-firewall' ),
75
- 'help_link' => 'https://icwp.io/3t'
76
  ];
77
  }
78
  return $aFields;
@@ -150,7 +150,7 @@ class ICWP_WPSF_Processor_LoginProtect_TwoFactorAuth extends ICWP_WPSF_Processor
150
  ];
151
 
152
  if ( !$this->getCon()->isRelabelled() ) {
153
- $aMessage[] = sprintf( '- <a href="%s" target="_blank">%s</a>', 'https://icwp.io/96', __( 'Why no login link?', 'wp-simple-firewall' ) );
154
  $aContent[] = '';
155
  }
156
 
72
  'value' => $this->fetchCodeFromRequest(),
73
  'placeholder' => __( 'This code was just sent to your registered Email address.', 'wp-simple-firewall' ),
74
  'text' => __( 'Email OTP', 'wp-simple-firewall' ),
75
+ 'help_link' => 'https://shsec.io/3t'
76
  ];
77
  }
78
  return $aFields;
150
  ];
151
 
152
  if ( !$this->getCon()->isRelabelled() ) {
153
+ $aMessage[] = sprintf( '- <a href="%s" target="_blank">%s</a>', 'https://shsec.io/96', __( 'Why no login link?', 'wp-simple-firewall' ) );
154
  $aContent[] = '';
155
  }
156
 
src/processors/loginprotect_intentprovider_ga.php CHANGED
@@ -208,7 +208,7 @@ class ICWP_WPSF_Processor_LoginProtect_GoogleAuthenticator extends ICWP_WPSF_Pro
208
  'value' => '',
209
  'placeholder' => __( 'Please use your Google Authenticator App to retrieve your code.', 'wp-simple-firewall' ),
210
  'text' => __( 'Google Authenticator Code', 'wp-simple-firewall' ),
211
- 'help_link' => 'https://icwp.io/wpsf42',
212
  'extras' => [
213
  'onkeyup' => "this.value=this.value.replace(/[^\d]/g,'')"
214
  ]
208
  'value' => '',
209
  'placeholder' => __( 'Please use your Google Authenticator App to retrieve your code.', 'wp-simple-firewall' ),
210
  'text' => __( 'Google Authenticator Code', 'wp-simple-firewall' ),
211
+ 'help_link' => 'https://shsec.io/wpsf42',
212
  'extras' => [
213
  'onkeyup' => "this.value=this.value.replace(/[^\d]/g,'')"
214
  ]
src/processors/loginprotect_intentprovider_yubikey.php CHANGED
@@ -252,7 +252,7 @@ class ICWP_WPSF_Processor_LoginProtect_Yubikey extends ICWP_WPSF_Processor_Login
252
  'placeholder' => __( 'Use your Yubikey to generate a new code.', 'wp-simple-firewall' ),
253
  'value' => '',
254
  'text' => __( 'Yubikey OTP', 'wp-simple-firewall' ),
255
- 'help_link' => 'https://icwp.io/4i'
256
  ];
257
  }
258
  return $aFields;
252
  'placeholder' => __( 'Use your Yubikey to generate a new code.', 'wp-simple-firewall' ),
253
  'value' => '',
254
  'text' => __( 'Yubikey OTP', 'wp-simple-firewall' ),
255
+ 'help_link' => 'https://shsec.io/4i'
256
  ];
257
  }
258
  return $aFields;
src/processors/loginprotect_wplogin.php CHANGED
@@ -42,6 +42,8 @@ class ICWP_WPSF_Processor_LoginProtect_WpLogin extends Modules\BaseShield\Shield
42
  * @return bool - true if conflict exists
43
  */
44
  protected function checkForPluginConflict() {
 
 
45
 
46
  $sMessage = '';
47
  $bConflicted = false;
@@ -75,7 +77,7 @@ class ICWP_WPSF_Processor_LoginProtect_WpLogin extends Modules\BaseShield\Shield
75
  __( 'Warning', 'wp-simple-firewall' ),
76
  $sMessage
77
  );
78
- $this->loadWpNotices()->addRawAdminNotice( $sNoticeMessage, 'error' );
79
  }
80
 
81
  return $bConflicted;
@@ -84,7 +86,9 @@ class ICWP_WPSF_Processor_LoginProtect_WpLogin extends Modules\BaseShield\Shield
84
  /**
85
  * @return bool
86
  */
87
- protected function checkForUnsupportedConfiguration() {
 
 
88
  $sPath = Services::Request()->getPath();
89
  if ( empty( $sPath ) ) {
90
 
@@ -93,7 +97,7 @@ class ICWP_WPSF_Processor_LoginProtect_WpLogin extends Modules\BaseShield\Shield
93
  __( 'Warning', 'wp-simple-firewall' ),
94
  __( 'Your login URL is unchanged because your current hosting/PHP configuration cannot parse the necessary information.', 'wp-simple-firewall' )
95
  );
96
- $this->loadWpNotices()->addRawAdminNotice( $sNoticeMessage, 'error' );
97
  return true;
98
  }
99
  return false;
42
  * @return bool - true if conflict exists
43
  */
44
  protected function checkForPluginConflict() {
45
+ /** @var \ICWP_WPSF_FeatureHandler_LoginProtect $oMod */
46
+ $oMod = $this->getMod();
47
 
48
  $sMessage = '';
49
  $bConflicted = false;
77
  __( 'Warning', 'wp-simple-firewall' ),
78
  $sMessage
79
  );
80
+ $oMod->setFlashAdminNotice( $sNoticeMessage, true );
81
  }
82
 
83
  return $bConflicted;
86
  /**
87
  * @return bool
88
  */
89
+ private function checkForUnsupportedConfiguration() {
90
+ /** @var \ICWP_WPSF_FeatureHandler_LoginProtect $oMod */
91
+ $oMod = $this->getMod();
92
  $sPath = Services::Request()->getPath();
93
  if ( empty( $sPath ) ) {
94
 
97
  __( 'Warning', 'wp-simple-firewall' ),
98
  __( 'Your login URL is unchanged because your current hosting/PHP configuration cannot parse the necessary information.', 'wp-simple-firewall' )
99
  );
100
+ $oMod->setFlashAdminNotice( $sNoticeMessage, true );
101
  return true;
102
  }
103
  return false;
src/processors/plugin_tracking.php CHANGED
@@ -75,9 +75,9 @@ class ICWP_WPSF_Processor_Plugin_Tracking extends Shield\Modules\BaseShield\Shie
75
  * Cron callback
76
  */
77
  public function runDailyCron() {
78
- /** @var ICWP_WPSF_FeatureHandler_Plugin $oFO */
79
- $oFO = $this->getMod();
80
- if ( $oFO->isTrackingEnabled() ) {
81
  $this->sendTrackingData();
82
  }
83
  }
75
  * Cron callback
76
  */
77
  public function runDailyCron() {
78
+ /** @var \ICWP_WPSF_FeatureHandler_Plugin $oMod */
79
+ $oMod = $this->getMod();
80
+ if ( $oMod->isTrackingEnabled() ) {
81
  $this->sendTrackingData();
82
  }
83
  }
src/processors/sessions.php CHANGED
@@ -202,21 +202,4 @@ class ICWP_WPSF_Processor_Sessions extends Modules\BaseShield\ShieldProcessor {
202
  $oSel = $oMod->getDbHandler_Sessions()->getQuerySelector();
203
  return $oSel->retrieveUserSession( $sSessionId, $sUsername );
204
  }
205
-
206
- /**
207
- * @return $this
208
- * @deprecated 8.3.0
209
- */
210
- private function clearCurrentSession() {
211
- $this->oCurrent = null;
212
- return $this;
213
- }
214
-
215
- /**
216
- * @return string
217
- * @deprecated 8.3.0
218
- */
219
- private function getSessionId() {
220
- return $this->getCon()->getSessionId();
221
- }
222
  }
202
  $oSel = $oMod->getDbHandler_Sessions()->getQuerySelector();
203
  return $oSel->retrieveUserSession( $sSessionId, $sUsername );
204
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
205
  }
src/processors/traffic_logger.php CHANGED
@@ -45,15 +45,15 @@ class ICWP_WPSF_Processor_TrafficLogger extends ShieldProcessor {
45
  * @return bool
46
  */
47
  protected function isCustomExcluded() {
48
- /** @var ICWP_WPSF_FeatureHandler_Traffic $oFO */
49
- $oFO = $this->getMod();
50
  $oReq = Services::Request();
51
 
52
  $sAgent = $oReq->getUserAgent();
53
  $sPath = $oReq->getPath().( empty( $_GET ) ? '' : '?'.http_build_query( $_GET ) );
54
 
55
  $bExcluded = false;
56
- foreach ( $oFO->getCustomExclusions() as $sExcl ) {
57
  if ( stripos( $sAgent, $sExcl ) !== false || stripos( $sPath, $sExcl ) !== false ) {
58
  $bExcluded = true;
59
  }
45
  * @return bool
46
  */
47
  protected function isCustomExcluded() {
48
+ /** @var \ICWP_WPSF_FeatureHandler_Traffic $oMod */
49
+ $oMod = $this->getMod();
50
  $oReq = Services::Request();
51
 
52
  $sAgent = $oReq->getUserAgent();
53
  $sPath = $oReq->getPath().( empty( $_GET ) ? '' : '?'.http_build_query( $_GET ) );
54
 
55
  $bExcluded = false;
56
+ foreach ( $oMod->getCustomExclusions() as $sExcl ) {
57
  if ( stripos( $sAgent, $sExcl ) !== false || stripos( $sPath, $sExcl ) !== false ) {
58
  $bExcluded = true;
59
  }
src/processors/usermanagement_passwords.php CHANGED
@@ -14,7 +14,6 @@ class ICWP_WPSF_Processor_UserManagement_Passwords extends Modules\BaseShield\Sh
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
- add_filter( 'login_message', [ $this, 'addPasswordResetMessage' ] );
18
  }
19
 
20
  /**
@@ -41,7 +40,7 @@ class ICWP_WPSF_Processor_UserManagement_Passwords extends Modules\BaseShield\Sh
41
  private function captureLogin( $oUser ) {
42
  $sPassword = $this->getLoginPassword();
43
 
44
- if ( $this->loadRequest()->isMethodPost() && !$this->isLoginCaptured()
45
  && $oUser instanceof WP_User && !empty( $sPassword ) ) {
46
  $this->setLoginCaptured();
47
  try {
@@ -63,18 +62,6 @@ class ICWP_WPSF_Processor_UserManagement_Passwords extends Modules\BaseShield\Sh
63
  }
64
  }
65
 
66
- /**
67
- * @param string $sMessage
68
- * @return string
69
- */
70
- public function addPasswordResetMessage( $sMessage = '' ) {
71
- $sFlushed = $this->loadWpNotices()
72
- ->flushFlash()
73
- ->getFlashText();
74
- // we overwrite rather than augment the message
75
- return empty( $sFlushed ) ? $sMessage : sprintf( '<p class="message">%s</p>', $sFlushed );
76
- }
77
-
78
  /**
79
  * @param WP_User $oUser
80
  */
@@ -87,15 +74,15 @@ class ICWP_WPSF_Processor_UserManagement_Passwords extends Modules\BaseShield\Sh
87
  }
88
 
89
  private function processExpiredPassword() {
90
- /** @var \ICWP_WPSF_FeatureHandler_UserManagement $oFO */
91
- $oFO = $this->getMod();
92
- if ( $oFO->isPassExpirationEnabled() ) {
93
  $nPassStartedAt = (int)$this->getCon()->getCurrentUserMeta()->pass_started_at;
94
  if ( $nPassStartedAt > 0 ) {
95
- if ( Services::Request()->ts() - $nPassStartedAt > $oFO->getPassExpireTimeout() ) {
96
  $this->getCon()->fireEvent( 'pass_expired' );
97
  $this->redirectToResetPassword(
98
- sprintf( __( 'Your password has expired (after %s days).', 'wp-simple-firewall' ), $oFO->getPassExpireDays() )
99
  );
100
  }
101
  }
@@ -103,7 +90,7 @@ class ICWP_WPSF_Processor_UserManagement_Passwords extends Modules\BaseShield\Sh
103
  }
104
 
105
  private function processFailedCheckPassword() {
106
- /** @var ICWP_WPSF_FeatureHandler_UserManagement $oFO */
107
  $oFO = $this->getMod();
108
  $oMeta = $this->getCon()->getCurrentUserMeta();
109
 
@@ -186,12 +173,12 @@ class ICWP_WPSF_Processor_UserManagement_Passwords extends Modules\BaseShield\Sh
186
  * @throws \Exception
187
  */
188
  protected function applyPasswordChecks( $sPassword ) {
189
- /** @var ICWP_WPSF_FeatureHandler_UserManagement $oFO */
190
- $oFO = $this->getMod();
191
 
192
  $this->testPasswordMeetsMinimumLength( $sPassword );
193
  $this->testPasswordMeetsMinimumStrength( $sPassword );
194
- if ( $oFO->isPassPreventPwned() ) {
195
  $this->sendRequestToPwnedRange( $sPassword );
196
  }
197
  }
@@ -258,12 +245,12 @@ class ICWP_WPSF_Processor_UserManagement_Passwords extends Modules\BaseShield\Sh
258
  * @param string $sPass
259
  * @return bool
260
  * @throws \Exception
261
- * @deprecated 8.1.2
262
  */
263
  protected function sendRequestToPwned( $sPass ) {
264
  $oHttpReq = Services::HttpRequest();
265
  $bSuccess = $oHttpReq->get(
266
- sprintf( '%s/%s', $this->getMod()->getDef( 'pwned_api_url_password_single' ), hash( 'sha1', $sPass ) ),
267
  [
268
  'headers' => [ 'user-agent' => sprintf( '%s WP Plugin-v%s', 'Shield', $this->getCon()->getVersion() ) ]
269
  ]
@@ -321,7 +308,7 @@ class ICWP_WPSF_Processor_UserManagement_Passwords extends Modules\BaseShield\Sh
321
  $sSubHash = substr( $sPassHash, 0, 5 );
322
 
323
  $bSuccess = $oHttpReq->get(
324
- sprintf( '%s/%s', $this->getMod()->getDef( 'pwned_api_url_password_range' ), $sSubHash ),
325
  [
326
  'headers' => [ 'user-agent' => sprintf( '%s WP Plugin-v%s', 'Shield', $this->getCon()->getVersion() ) ]
327
  ]
@@ -383,7 +370,7 @@ class ICWP_WPSF_Processor_UserManagement_Passwords extends Modules\BaseShield\Sh
383
 
384
  // Edd: edd_user_pass; Woo: password;
385
  foreach ( [ 'pwd', 'pass1' ] as $sKey ) {
386
- $sP = $this->loadRequest()->post( $sKey );
387
  if ( !empty( $sP ) ) {
388
  $sPass = $sP;
389
  break;
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
  /**
40
  private function captureLogin( $oUser ) {
41
  $sPassword = $this->getLoginPassword();
42
 
43
+ if ( Services::Request()->isPost() && !$this->isLoginCaptured()
44
  && $oUser instanceof WP_User && !empty( $sPassword ) ) {
45
  $this->setLoginCaptured();
46
  try {
62
  }
63
  }
64
 
 
 
 
 
 
 
 
 
 
 
 
 
65
  /**
66
  * @param WP_User $oUser
67
  */
74
  }
75
 
76
  private function processExpiredPassword() {
77
+ /** @var \ICWP_WPSF_FeatureHandler_UserManagement $oMod */
78
+ $oMod = $this->getMod();
79
+ if ( $oMod->isPassExpirationEnabled() ) {
80
  $nPassStartedAt = (int)$this->getCon()->getCurrentUserMeta()->pass_started_at;
81
  if ( $nPassStartedAt > 0 ) {
82
+ if ( Services::Request()->ts() - $nPassStartedAt > $oMod->getPassExpireTimeout() ) {
83
  $this->getCon()->fireEvent( 'pass_expired' );
84
  $this->redirectToResetPassword(
85
+ sprintf( __( 'Your password has expired (after %s days).', 'wp-simple-firewall' ), $oMod->getPassExpireDays() )
86
  );
87
  }
88
  }
90
  }
91
 
92
  private function processFailedCheckPassword() {
93
+ /** @var \ICWP_WPSF_FeatureHandler_UserManagement $oFO */
94
  $oFO = $this->getMod();
95
  $oMeta = $this->getCon()->getCurrentUserMeta();
96
 
173
  * @throws \Exception
174
  */
175
  protected function applyPasswordChecks( $sPassword ) {
176
+ /** @var \ICWP_WPSF_FeatureHandler_UserManagement $oMod */
177
+ $oMod = $this->getMod();
178
 
179
  $this->testPasswordMeetsMinimumLength( $sPassword );
180
  $this->testPasswordMeetsMinimumStrength( $sPassword );
181
+ if ( $oMod->isPassPreventPwned() ) {
182
  $this->sendRequestToPwnedRange( $sPassword );
183
  }
184
  }
245
  * @param string $sPass
246
  * @return bool
247
  * @throws \Exception
248
+ * @deprecated 8.4
249
  */
250
  protected function sendRequestToPwned( $sPass ) {
251
  $oHttpReq = Services::HttpRequest();
252
  $bSuccess = $oHttpReq->get(
253
+ sprintf( '%s/%s', $this->getOptions()->getDef( 'pwned_api_url_password_single' ), hash( 'sha1', $sPass ) ),
254
  [
255
  'headers' => [ 'user-agent' => sprintf( '%s WP Plugin-v%s', 'Shield', $this->getCon()->getVersion() ) ]
256
  ]
308
  $sSubHash = substr( $sPassHash, 0, 5 );
309
 
310
  $bSuccess = $oHttpReq->get(
311
+ sprintf( '%s/%s', $this->getOptions()->getDef( 'pwned_api_url_password_range' ), $sSubHash ),
312
  [
313
  'headers' => [ 'user-agent' => sprintf( '%s WP Plugin-v%s', 'Shield', $this->getCon()->getVersion() ) ]
314
  ]
370
 
371
  // Edd: edd_user_pass; Woo: password;
372
  foreach ( [ 'pwd', 'pass1' ] as $sKey ) {
373
+ $sP = Services::Request()->post( $sKey );
374
  if ( !empty( $sP ) ) {
375
  $sPass = $sP;
376
  break;
src/query/base/statistics_base.php DELETED
@@ -1,252 +0,0 @@
1
- <?php
2
-
3
- require_once( dirname( dirname( __DIR__ ) ).'/lib/vendor/autoload.php' );
4
-
5
- class ICWP_WPSF_Query_Statistics_Base extends ICWP_WPSF_Query_Base {
6
-
7
- /**
8
- * @var ICWP_WPSF_FeatureHandler_Statistics
9
- */
10
- protected $oFO;
11
-
12
- /**
13
- * @var int
14
- */
15
- protected $nDateFrom;
16
-
17
- /**
18
- * @var int
19
- */
20
- protected $nDateTo;
21
-
22
- /**
23
- * @var int
24
- */
25
- protected $nQueryLimit;
26
-
27
- /**
28
- * @var array
29
- */
30
- protected $aStatKeys;
31
-
32
- /**
33
- * @var bool
34
- */
35
- protected $bSelectDeleted;
36
-
37
- /**
38
- * @return StatisticsReportingVO[]
39
- */
40
- protected function runQuery() {
41
-
42
- $sQuery = $this->buildQuery();
43
- $mResult = $this->loadDbProcessor()->selectCustom( $sQuery, OBJECT );
44
-
45
- // TODO: NOT PHP 5.2!
46
- if ( is_array( $mResult ) ) {
47
- include_once( __DIR__.'/StatisticsReportingVO.php' );
48
- $mResult = array_map(
49
- function ( $oData ) {
50
- return new StatisticsReportingVO( $oData );
51
- },
52
- $mResult
53
- );
54
- }
55
- else {
56
- $mResult = array();
57
- }
58
- return $mResult;
59
- }
60
-
61
- /**
62
- * @param bool $bIsCount
63
- * @return string
64
- */
65
- protected function buildQuery( $bIsCount = false ) {
66
- $sQuery = "
67
- SELECT %s
68
- FROM `%s`
69
- WHERE
70
- `created_at` > %s AND `created_at` < %s
71
- AND `deleted_at` %s 0
72
- %s
73
- %s
74
- ";
75
-
76
- $sStatPart = $this->buildStatKeyQuery();
77
- return sprintf( $sQuery,
78
- $bIsCount ? 'COUNT(*) AS total' : '*',
79
- $this->getMod()->getFullEventsTableName(),
80
- $this->getDateFrom(),
81
- $this->getDateTo(),
82
- $this->isSelectDeleted() ? '>' : '=',
83
- empty( $sStatPart ) ? $sStatPart : 'AND '.$sStatPart,
84
- $this->getQueryLimit()
85
- );
86
- }
87
-
88
- protected function deleteAllFromTo() {
89
- $this->loadDbProcessor()->doSql( $this->buildDeleteQuery() );
90
- }
91
-
92
- /**
93
- * @return string
94
- */
95
- protected function buildDeleteQuery() {
96
- $sQuery = "
97
- DELETE FROM `%s`
98
- WHERE
99
- `created_at` > %s AND `created_at` < %s
100
- %s
101
- ";
102
-
103
- $sStatPart = $this->buildStatKeyQuery();
104
- return sprintf( $sQuery,
105
- $this->getMod()->getFullEventsTableName(),
106
- $this->getDateFrom(),
107
- $this->getDateTo(),
108
- empty( $sStatPart ) ? $sStatPart : 'AND '.$sStatPart
109
- );
110
- }
111
-
112
- /**
113
- * @return string
114
- */
115
- protected function buildStatKeyQuery() {
116
-
117
- $sQuery = '';
118
- if ( $this->hasStatKeys() ) {
119
- $aKeys = $this->getStatKeys();
120
- if ( count( $aKeys ) == 1 ) {
121
- $sQuery = sprintf( '`stat_key` = "%s"', $aKeys[ 0 ] );
122
- }
123
- else {
124
- $sQuery = sprintf( '`stat_key` IN ("%s")', implode( '","', $aKeys ) );
125
- }
126
- }
127
-
128
- return $sQuery;
129
- }
130
-
131
- /**
132
- * @return int
133
- */
134
- public function getDateFrom() {
135
- return isset( $this->nDateFrom ) ? (int)$this->nDateFrom : 0;
136
- }
137
-
138
- /**
139
- * @return int
140
- */
141
- public function getDateTo() {
142
- return isset( $this->nDateTo ) ? (int)$this->nDateTo : $this->loadRequest()->ts();
143
- }
144
-
145
- /**
146
- * @return int
147
- */
148
- public function getQueryLimit() {
149
- return isset( $this->nQueryLimit ) ? 'LIMIT '.$this->nQueryLimit : '';
150
- }
151
-
152
- /**
153
- * @return array
154
- */
155
- public function getStatKeys() {
156
- if ( !isset( $this->aStatKeys ) ) {
157
- $this->aStatKeys = array();
158
- }
159
- return $this->aStatKeys;
160
- }
161
-
162
- /**
163
- * @return bool
164
- */
165
- public function hasStatKeys() {
166
- return ( count( $this->getStatKeys() ) > 0 );
167
- }
168
-
169
- /**
170
- * @return bool
171
- */
172
- public function isSelectDeleted() {
173
- return isset( $this->bSelectDeleted ) ? (bool)$this->bSelectDeleted : false;
174
- }
175
-
176
- /**
177
- * @param int $nDateFrom
178
- * @return $this
179
- */
180
- public function setDateFrom( $nDateFrom ) {
181
- $this->nDateFrom = $nDateFrom;
182
- return $this;
183
- }
184
-
185
- /**
186
- * @param int $nDateTo
187
- * @return $this
188
- */
189
- public function setDateTo( $nDateTo ) {
190
- $this->nDateTo = $nDateTo;
191
- return $this;
192
- }
193
-
194
- /**
195
- * @param int $nLimit
196
- * @return $this
197
- */
198
- public function setQueryLimit( $nLimit ) {
199
- $this->nQueryLimit = $nLimit;
200
- return $this;
201
- }
202
-
203
- /**
204
- * @param string $sStatKey
205
- * @return $this
206
- */
207
- public function addStatKey( $sStatKey ) {
208
- $aKeys = $this->getStatKeys();
209
- $sStatKey = esc_sql( trim( $sStatKey ) );
210
- if ( !in_array( $sStatKey, $aKeys ) ) {
211
- $aKeys[] = $sStatKey;
212
- }
213
- return $this->setStatKeys( $aKeys );
214
- }
215
-
216
- /**
217
- * @param bool $bSelectDeleted
218
- * @return $this
219
- */
220
- public function setSelectDeleted( $bSelectDeleted ) {
221
- $this->bSelectDeleted = $bSelectDeleted;
222
- return $this;
223
- }
224
-
225
- /**
226
- * @param array $aKeys
227
- * @return $this
228
- */
229
- public function setStatKeys( $aKeys ) {
230
- if ( !is_array( $aKeys ) ) {
231
- $aKeys = array();
232
- }
233
- $this->aStatKeys = $aKeys;
234
- return $this;
235
- }
236
-
237
- /**
238
- * @return ICWP_WPSF_FeatureHandler_Statistics
239
- */
240
- protected function getMod() {
241
- return $this->oFO;
242
- }
243
-
244
- /**
245
- * @param ICWP_WPSF_FeatureHandler_Statistics $oFeature
246
- * @return $this
247
- */
248
- public function setFeature( $oFeature ) {
249
- $this->oFO = $oFeature;
250
- return $this;
251
- }
252
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/query/statistics/reporting.php DELETED
@@ -1,11 +0,0 @@
1
- <?php
2
-
3
- class ICWP_WPSF_Query_Statistics_Reporting extends ICWP_WPSF_Query_Statistics_Base {
4
-
5
- /**
6
- * @return int
7
- */
8
- public function countQuery() {
9
- return $this->loadDbProcessor()->doSql( $this->buildQuery() );
10
- }
11
- }
 
 
 
 
 
 
 
 
 
 
 
src/wizards/base.php CHANGED
@@ -274,7 +274,7 @@ abstract class ICWP_WPSF_Wizard_Base extends \FernleafSystems\Wordpress\Plugin\S
274
  ],
275
  'hrefs' => [
276
  'dashboard' => $oFO->getUrl_AdminPage(),
277
- 'goprofooter' => 'https://icwp.io/goprofooter',
278
  ],
279
  'ajax' => [
280
  'content' => $oFO->getAjaxActionData( 'wiz_process_step' ),
@@ -342,7 +342,7 @@ abstract class ICWP_WPSF_Wizard_Base extends \FernleafSystems\Wordpress\Plugin\S
342
  ],
343
  'hrefs' => [
344
  'dashboard' => $oFO->getUrl_AdminPage(),
345
- 'goprofooter' => 'https://icwp.io/goprofooter',
346
  ],
347
  'ajax' => [
348
  'content' => $oFO->getAjaxActionData( 'wiz_process_step' ),
@@ -439,7 +439,7 @@ abstract class ICWP_WPSF_Wizard_Base extends \FernleafSystems\Wordpress\Plugin\S
439
  ],
440
  'hrefs' => [
441
  'dashboard' => $oMod->getUrl_AdminPage(),
442
- 'gopro' => 'https://icwp.io/ap',
443
  ],
444
  'imgs' => [],
445
  'data' => [
274
  ],
275
  'hrefs' => [
276
  'dashboard' => $oFO->getUrl_AdminPage(),
277
+ 'goprofooter' => 'https://shsec.io/goprofooter',
278
  ],
279
  'ajax' => [
280
  'content' => $oFO->getAjaxActionData( 'wiz_process_step' ),
342
  ],
343
  'hrefs' => [
344
  'dashboard' => $oFO->getUrl_AdminPage(),
345
+ 'goprofooter' => 'https://shsec.io/goprofooter',
346
  ],
347
  'ajax' => [
348
  'content' => $oFO->getAjaxActionData( 'wiz_process_step' ),
439
  ],
440
  'hrefs' => [
441
  'dashboard' => $oMod->getUrl_AdminPage(),
442
+ 'gopro' => 'https://shsec.io/ap',
443
  ],
444
  'imgs' => [],
445
  'data' => [
src/wizards/base_wpsf.php CHANGED
@@ -1,5 +1,7 @@
1
  <?php
2
 
 
 
3
  /**
4
  * Class ICWP_WPSF_Wizard_BaseWpsf
5
  */
@@ -36,7 +38,7 @@ abstract class ICWP_WPSF_Wizard_BaseWpsf extends ICWP_WPSF_Wizard_Base {
36
 
37
  switch ( $sStep ) {
38
  case 'security_admin_verify':
39
- $aAdditional = array( 'current_index' => $this->loadRequest()->post( 'current_index' ) );
40
  break;
41
  default:
42
  $aAdditional = parent::getRenderData_SlideExtra( $sStep );
@@ -91,7 +93,7 @@ abstract class ICWP_WPSF_Wizard_BaseWpsf extends ICWP_WPSF_Wizard_Base {
91
  * @return \FernleafSystems\Utilities\Response
92
  */
93
  private function wizardSecurityAdminVerify() {
94
- $sKey = $this->loadRequest()->post( 'AccessKey' );
95
 
96
  $oResponse = new \FernleafSystems\Utilities\Response();
97
 
1
  <?php
2
 
3
+ use FernleafSystems\Wordpress\Services\Services;
4
+
5
  /**
6
  * Class ICWP_WPSF_Wizard_BaseWpsf
7
  */
38
 
39
  switch ( $sStep ) {
40
  case 'security_admin_verify':
41
+ $aAdditional = array( 'current_index' => Services::Request()->post( 'current_index' ) );
42
  break;
43
  default:
44
  $aAdditional = parent::getRenderData_SlideExtra( $sStep );
93
  * @return \FernleafSystems\Utilities\Response
94
  */
95
  private function wizardSecurityAdminVerify() {
96
+ $sKey = Services::Request()->post( 'AccessKey' );
97
 
98
  $oResponse = new \FernleafSystems\Utilities\Response();
99
 
src/wizards/login_protect.php CHANGED
@@ -150,10 +150,10 @@ class ICWP_WPSF_Wizard_LoginProtect extends ICWP_WPSF_Wizard_BaseWpsf {
150
  * @return \FernleafSystems\Utilities\Response
151
  */
152
  private function processMultiSelect() {
153
- /** @var ICWP_WPSF_FeatureHandler_LoginProtect $oFO */
154
  $oFO = $this->getMod();
155
 
156
- $bEnabledMulti = $this->loadRequest()->post( 'multiselect' ) === 'Y';
157
  $oFO->setIsChainedAuth( $bEnabledMulti );
158
  $sMessage = sprintf( __( 'Multi-Factor Authentication was %s for the site.', 'wp-simple-firewall' ),
159
  $bEnabledMulti ? __( 'enabled', 'wp-simple-firewall' ) : __( 'disabled', 'wp-simple-firewall' )
150
  * @return \FernleafSystems\Utilities\Response
151
  */
152
  private function processMultiSelect() {
153
+ /** @var \ICWP_WPSF_FeatureHandler_LoginProtect $oFO */
154
  $oFO = $this->getMod();
155
 
156
+ $bEnabledMulti = Services::Request()->post( 'multiselect' ) === 'Y';
157
  $oFO->setIsChainedAuth( $bEnabledMulti );
158
  $sMessage = sprintf( __( 'Multi-Factor Authentication was %s for the site.', 'wp-simple-firewall' ),
159
  $bEnabledMulti ? __( 'enabled', 'wp-simple-firewall' ) : __( 'disabled', 'wp-simple-firewall' )
src/wizards/plugin.php CHANGED
@@ -194,7 +194,7 @@ class ICWP_WPSF_Wizard_Plugin extends ICWP_WPSF_Wizard_BaseWpsf {
194
  case 'ip_detect':
195
  $aAdditional = [
196
  'hrefs' => [
197
- 'visitor_ip' => 'https://icwp.io/visitorip',
198
  ]
199
  ];
200
  break;
@@ -203,7 +203,7 @@ class ICWP_WPSF_Wizard_Plugin extends ICWP_WPSF_Wizard_BaseWpsf {
203
  case 'import':
204
  $aAdditional = [
205
  'hrefs' => [
206
- 'blog_importexport' => 'https://icwp.io/av'
207
  ],
208
  'imgs' => [
209
  'shieldnetworkmini' => $oConn->getPluginUrl_Image( 'shield/shieldnetworkmini.png' ),
@@ -219,7 +219,7 @@ class ICWP_WPSF_Wizard_Plugin extends ICWP_WPSF_Wizard_BaseWpsf {
219
  'user_email' => $oUser->user_email
220
  ],
221
  'hrefs' => [
222
- 'privacy_policy' => $this->getMod()->getDef( 'href_privacy_policy' )
223
  ],
224
  ];
225
  break;
@@ -272,7 +272,7 @@ class ICWP_WPSF_Wizard_Plugin extends ICWP_WPSF_Wizard_BaseWpsf {
272
  case 'import':
273
  $aAdditional = [
274
  'hrefs' => [
275
- 'blog_importexport' => 'https://icwp.io/av'
276
  ],
277
  'imgs' => [
278
  'shieldnetworkmini' => $oConn->getPluginUrl_Image( 'shield/shieldnetworkmini.png' ),
194
  case 'ip_detect':
195
  $aAdditional = [
196
  'hrefs' => [
197
+ 'visitor_ip' => 'https://shsec.io/visitorip',
198
  ]
199
  ];
200
  break;
203
  case 'import':
204
  $aAdditional = [
205
  'hrefs' => [
206
+ 'blog_importexport' => 'https://shsec.io/av'
207
  ],
208
  'imgs' => [
209
  'shieldnetworkmini' => $oConn->getPluginUrl_Image( 'shield/shieldnetworkmini.png' ),
219
  'user_email' => $oUser->user_email
220
  ],
221
  'hrefs' => [
222
+ 'privacy_policy' => $this->getOptions()->getDef( 'href_privacy_policy' )
223
  ],
224
  ];
225
  break;
272
  case 'import':
273
  $aAdditional = [
274
  'hrefs' => [
275
+ 'blog_importexport' => 'https://shsec.io/av'
276
  ],
277
  'imgs' => [
278
  'shieldnetworkmini' => $oConn->getPluginUrl_Image( 'shield/shieldnetworkmini.png' ),
templates/php/snippets/module-help-admin_access_restriction.php CHANGED
@@ -32,11 +32,11 @@
32
  do the 1st option - which is the recommended.
33
  </p>
34
  <ol>
35
- <li>Follow the guide <a href="https://icwp.io/am" target="_blank">set out here</a>. Once you've
36
  turned off the Shield plugin you can go in and set the access key to whatever you like,
37
  and then remove the 'forceoff' file.
38
  </li>
39
- <li>Follow the guide <a href="https://icwp.io/al" target="_blank">set out here</a> to reset
40
  your plugin to default settings.
41
  </li>
42
  </ol>
32
  do the 1st option - which is the recommended.
33
  </p>
34
  <ol>
35
+ <li>Follow the guide <a href="https://shsec.io/am" target="_blank">set out here</a>. Once you've
36
  turned off the Shield plugin you can go in and set the access key to whatever you like,
37
  and then remove the 'forceoff' file.
38
  </li>
39
+ <li>Follow the guide <a href="https://shsec.io/al" target="_blank">set out here</a> to reset
40
  your plugin to default settings.
41
  </li>
42
  </ol>
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://icwp.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://icontrolwp.freshdesk.com/support/solutions/folders/3000000263</a> .
5
  </p>
templates/php/snippets/module-help-headers.php CHANGED
@@ -1,4 +1,4 @@
1
  <h5>What are HTTP Headers?</h5>
2
  <p>This can be a complex topic, but it's worth taking some time to read about it.</p>
3
- <p>We've provided some further <a href="https://icwp.io/aj" target="_blank">in-depth articles</a>
4
  on the topic to help you get your... "head" around it. ;)</p>
1
  <h5>What are HTTP Headers?</h5>
2
  <p>This can be a complex topic, but it's worth taking some time to read about it.</p>
3
+ <p>We've provided some further <a href="https://shsec.io/aj" target="_blank">in-depth articles</a>
4
  on the topic to help you get your... "head" around it. ;)</p>
templates/php/snippets/module-help-login_protect.php CHANGED
@@ -6,7 +6,7 @@
6
  <dt>What is multi-factor authentication (MFA)?</dt>
7
  <dd>
8
  <p>See the link below for a complete explanation of MFA.</p>
9
- <p><a href="https://icwp.io/ai" target="_blank">
10
  https://www.icontrolwp.com/blog/security-multi-two-factor-authentication-wordpress/</a> .
11
  </p>
12
  <p>Please read this. It really helps.</p>
6
  <dt>What is multi-factor authentication (MFA)?</dt>
7
  <dd>
8
  <p>See the link below for a complete explanation of MFA.</p>
9
+ <p><a href="https://shsec.io/ai" target="_blank">
10
  https://www.icontrolwp.com/blog/security-multi-two-factor-authentication-wordpress/</a> .
11
  </p>
12
  <p>Please read this. It really helps.</p>
templates/php/snippets/module-help-plugin.php CHANGED
@@ -16,7 +16,7 @@
16
  public IP address.
17
  </p>
18
  <p>Don't know your IP address? Go here:
19
- (<a href="https://icwp.io/an" target="_blank">We've done it for you</a> ;)
20
  </p>
21
  <p>With this result, you should be able to select the correct item from the list. The higher up the
22
  list the better, with <code>REMOTE_ADDR</code> being the #1 preferred, if possible.</p>
16
  public IP address.
17
  </p>
18
  <p>Don't know your IP address? Go here:
19
+ (<a href="https://shsec.io/an" target="_blank">We've done it for you</a> ;)
20
  </p>
21
  <p>With this result, you should be able to select the correct item from the list. The higher up the
22
  list the better, with <code>REMOTE_ADDR</code> being the #1 preferred, if possible.</p>
templates/php/snippets/pro.php DELETED
@@ -1,310 +0,0 @@
1
- <?php
2
- /** @var string[] $strings */
3
- /** @var string[] $inputs */
4
- /** @var string[] $flags */
5
- /** @var string[] $vars */
6
- /** @var string[] $aLicenseAjax */
7
- /** @var array $aLicKeyInput */
8
- $aLicKeyInput = $inputs[ 'license_key' ];
9
- ?>
10
- <style>
11
- #accordionTop .accordion-toggle h3 {
12
- color: #333333;
13
- }
14
- #accordionTop .accordion-toggle {
15
- background-color: rgba(255, 255, 255, 0.6);
16
- }
17
- #accordionTop .accordion-toggle:hover {
18
- text-decoration: none;
19
- background-color: rgba(255, 255, 255, 0.6);
20
- }
21
- #accordionTop .accordion-toggle:focus {
22
- box-shadow: none;
23
- }
24
- #accordionTop .accordion-group {
25
- margin-bottom: 15px;
26
- }
27
- #accordionTop .accordion-inner {
28
- background-color: rgba(255, 255, 255, 0.6);
29
- }
30
- #ButtonBuyNow {
31
- margin: 20px;
32
- }
33
- #ButtonActivate {
34
- margin: 5px 2px 0;
35
- }
36
- .licenseForm button {
37
- display: block;
38
- }
39
-
40
- </style>
41
-
42
- <div class="container-fluid">
43
- <div class="row content-help">
44
- <div class="col-5">
45
- <div class="module-headline">
46
- <h4>License Summary</h4>
47
- </div>
48
- <table class="table table-hover table-sm table-responsive">
49
- <?php foreach ( $vars[ 'license_table' ] as $varKey => $licenseValue ) : ?>
50
- <?php $sClasses = ( $varKey == 'last_errors' && !empty( $licenseValue ) ) ? 'table-warning' : ''; ?>
51
- <tr>
52
- <th scope="row"><?php echo $strings[ $varKey ]; ?>:</th>
53
- <td class="<?php echo $sClasses; ?>"><?php echo $licenseValue; ?></td>
54
- </tr>
55
- <?php endforeach; ?>
56
- </table>
57
- <hr />
58
-
59
- <h4>License Actions</h4>
60
-
61
- <div class="row">
62
- <div class="col card">
63
- <form method="post" class="licenseForm">
64
- <?php foreach ( $ajax[ 'license_handling' ] as $sAjKey => $sAjVal ) : ?>
65
- <input type="hidden" name="<?php echo $sAjKey; ?>" value="<?php echo $sAjVal; ?>" />
66
- <?php endforeach; ?>
67
- <input type="hidden" name="license-action" value="check" />
68
- <div class="form-group">
69
- <label>Check License Availability For This Site</label>
70
- <button class="btn btn-info" type="submit"
71
- <?php echo $flags[ 'button_enabled_check' ] ? '' : 'disabled="disabled"'; ?> >
72
- Check License
73
- </button>
74
-
75
-
76
- <div class="form-text text-muted">
77
- <p class="font-weight-bold"><br />Be sure to have first activated your URL in your
78
- <a target="_blank" href="<?php echo $aHrefs[ 'keyless_cp' ]; ?>">Keyless Activation control panel</a>.</p>
79
- <ul>
80
- <li>URL To Activate: <?php echo $vars[ 'activation_url' ]; ?></li>
81
- <li>Licenses may only be checked once in 20 seconds. Checks more frequent than this will
82
- automatically be skipped</li>
83
- </ul>
84
- </div>
85
- </div>
86
- </form>
87
-
88
- <form method="post" id="ConnectionDebug">
89
- <?php foreach ( $ajax[ 'connection_debug' ] as $sAjKey => $sAjVal ) : ?>
90
- <input type="hidden" name="<?php echo $sAjKey; ?>" value="<?php echo $sAjVal; ?>" />
91
- <?php endforeach; ?>
92
- <button class="btn btn-link btn-sm float-right p-0" type="submit">[Debug]</button>
93
- </form>
94
- </div>
95
- </div>
96
-
97
- <?php if ( false && $flags[ 'has_license_key' ] ) : ?>
98
- <div class="row">
99
- <div class="col card">
100
- <form method="post" class="licenseForm">
101
-
102
- <?php foreach ( $ajax[ 'license_handling' ] as $sAjKey => $sAjVal ) : ?>
103
- <input type="hidden" name="<?php echo $sAjKey; ?>" value="<?php echo $sAjVal; ?>" />
104
- <?php endforeach; ?>
105
-
106
- <input type="hidden" name="license-action" value="remove" />
107
- <div class="form-group">
108
- <label>Remove Current License</label>
109
- <button class="btn btn-warning" type="submit"
110
- <?php echo $flags[ 'button_enabled_remove' ] ? '' : 'disabled="disabled"'; ?> >
111
- Remove
112
- </button>
113
- <span class="form-text text-muted">Important: This will remove all Shield Security Pro features.</span>
114
- </div>
115
- </form>
116
- </div>
117
- </div>
118
- <?php endif; ?>
119
- </div>
120
-
121
- <div class="col-7">
122
- <div id="accordion">
123
-
124
- <div class="card gopro-card">
125
- <div class="card-header" id="headingOne">
126
- <h5 class="mb-0">
127
- <button class="btn btn-link" data-toggle="collapse" data-target="#collone"
128
- aria-expanded="true" aria-controls="collone">
129
- &rarr; Shield Pro gives you ...
130
- </button>
131
- </h5>
132
- </div>
133
-
134
- <div id="collone" class="collapse show" aria-labelledby="headingOne"
135
- data-parent="#accordion">
136
- <div class="card-body">
137
- <dl>
138
- <dt>Plugin and Theme Vulnerability Scanner</dt>
139
- <dd>Alerts to plugin/theme vulnerabilities.
140
- Shield can then automatically upgrade as updates become available.
141
- </dd>
142
-
143
- <dt>Catch Hacks Immediately - Plugins and Themes Guard</dt>
144
- <dd>Be alerted to ANY unauthorized changes to plugins/themes.</dd>
145
-
146
- <dt>Powerful User Password Policies</dt>
147
- <dd>Ensures that all users maintain strong passwords.</dd>
148
-
149
- <dt>Support for WooCommerce &amp; other 3rd party plugins</dt>
150
- <dd>Provide tighter security for your WooCommerce customers.
151
- </dd>
152
-
153
- <dt>Exclusive Customer Support</dt>
154
- <dd>Technical support for Shield is exclusive to Pro customers.</dd>
155
-
156
- <dt>Import and Export of plugin options</dt>
157
- <dd>Automatically import settings directly from 1 site to another.</dd>
158
-
159
- <dt>Exclusive Early-Access </dt>
160
- <dd>Be 1st to get new security features, as soon as they're available.</dd>
161
-
162
- <dt>Unlimited Audit Trail</dt>
163
- <dd>Retain logs for as long as you need - no limits.</dd>
164
-
165
- <dt>Customize text shown to visitors</dt>
166
- <dd>Edit customer-facing messages/text of the Shield plugin.</dd>
167
- </dl>
168
- </div>
169
- </div>
170
- </div>
171
-
172
- <div class="card gopro-card">
173
- <div class="card-header" id="headingTwo">
174
- <h5 class="mb-0">
175
- <button class="btn btn-link collapsed" data-toggle="collapse" data-target="#colltwo"
176
- aria-expanded="false" aria-controls="colltwo">
177
- &rarr; Coming Soon...
178
- </button>
179
- </h5>
180
- </div>
181
- <div id="colltwo" class="collapse" aria-labelledby="headingTwo" data-parent="#accordion">
182
- <div class="card-body">
183
- <dl>
184
- <dt>White Label</dt>
185
- <dd>Re-Brand Shield Security as your own!</dd>
186
-
187
- <dt>Select individual plugins for automatic updates</dt>
188
- <dd>You'll soon be able to select individual plugins for automatic updates. Right now it's all or nothing.</dd>
189
-
190
- <dt>Improved performance/optimizations for PHP 5.6+</dt>
191
- <dd>We're rewriting your favourite security plugin to take full advantage of the latest PHP developments.</dd>
192
-
193
- <dt>Statistic and Reporting</dt>
194
- <dd>You'll be able to review and generate reports on keys security events on your sites.</dd>
195
-
196
- <dt>And Much More...</dt>
197
- <dd>With your continued support, we'll add more and more features...</dd>
198
- </dl>
199
- </div>
200
- </div>
201
- </div>
202
-
203
- <div class="card gopro-card">
204
- <div class="card-header" id="headingThree">
205
- <h5 class="mb-0">
206
- <button class="btn btn-link collapsed" data-toggle="collapse" data-target="#collthree"
207
- aria-expanded="false" aria-controls="collthree">
208
- &rarr; How To Get Shield Pro?
209
- </button>
210
- </h5>
211
- </div>
212
- <div id="collthree" class="collapse" aria-labelledby="headingThree" data-parent="#accordion">
213
- <div class="card-body">
214
-
215
- <p>One Dollar Plugin is our new Plugin Store. We're building a collection of high-quality,
216
- highly reliable WordPress plugins for a fraction of the normal cost - making premium WordPress
217
- plugins available to everyone.
218
- </p>
219
- <p>Shield Pro is our first One Dollar Plugin and is sold for the equivalent of
220
- $1/month per site ($12/year)</p>
221
- <ol>
222
- <li>Just grab a new license from the
223
- <a href="https://icwp.io/buyshieldpro" target="_blank">One Dollar Plugin here</a>.</li>
224
- <li>Activate your license on your sites using the 'Activate Key' button.</li>
225
- </ol>
226
-
227
- <p class="text-center">
228
- <a href="https://icwp.io/buyshieldpro" target="_blank" id="ButtonBuyNow"
229
- class="btn btn-large btn-success">
230
- Upgrade To Shield Pro Now &rarr;</a>
231
- </p>
232
- </div>
233
- </div>
234
- </div>
235
- </div>
236
- </div>
237
-
238
- </div>
239
- </div>
240
- <hr />
241
-
242
- <script type="text/javascript">
243
- var iCWP_WPSF_LicenseHandler = new function () {
244
-
245
- var bRequestCurrentlyRunning = false;
246
-
247
- /**
248
- */
249
- var submitLicenseForm = function ( event ) {
250
- iCWP_WPSF_BodyOverlay.show();
251
-
252
- if ( bRequestCurrentlyRunning ) {
253
- return false;
254
- }
255
- bRequestCurrentlyRunning = true;
256
- event.preventDefault();
257
-
258
- var $oForm = jQuery( this );
259
- jQuery.post( ajaxurl, $oForm.serialize(),
260
- function ( oResponse ) {
261
- if ( typeof oResponse !== 'undefined' && typeof oResponse.data !== 'undefined' ) {
262
- iCWP_WPSF_Toaster.showMessage( oResponse.data.message, oResponse.data.success );
263
- // iCWP_WPSF_Growl.showMessage( oResponse.data.message, oResponse.data.success );
264
- }
265
- }
266
- ).always( function () {
267
- bRequestCurrentlyRunning = false;
268
- // iCWP_WPSF_BodyOverlay.hide();
269
- setTimeout( function () {
270
- location.reload( true );
271
- }, 2000 );
272
- }
273
- );
274
- };
275
-
276
- this.initialise = function () {
277
- jQuery( document ).ready( function () {
278
- jQuery( document ).on( "submit", "form.licenseForm", submitLicenseForm );
279
- } );
280
- };
281
- }();
282
-
283
- var iCWP_WPSF_ConnectionDebug = new function () {
284
- /**
285
- */
286
- var connectionDebug = function ( event ) {
287
- iCWP_WPSF_BodyOverlay.show();
288
- event.preventDefault();
289
-
290
- var $oForm = jQuery( this );
291
- jQuery.post( ajaxurl, $oForm.serialize(),
292
- function ( oResponse ) {
293
- alert( oResponse.data.message );
294
- }
295
- ).always( function () {
296
- iCWP_WPSF_BodyOverlay.hide();
297
- }
298
- );
299
- };
300
-
301
- this.initialise = function () {
302
- jQuery( document ).ready( function () {
303
- jQuery( document ).on( "submit", "form#ConnectionDebug", connectionDebug );
304
- } );
305
- };
306
- }();
307
-
308
- iCWP_WPSF_LicenseHandler.initialise();
309
- iCWP_WPSF_ConnectionDebug.initialise();
310
- </script>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
templates/twig/features/feature-base.twig CHANGED
@@ -10,7 +10,7 @@
10
  </div>
11
  <div class="page-header">
12
  <h2>
13
- <a id="pluginlogo_32" class="header-icon32" href="https://icwp.io/2k" target="_blank"></a>
14
  {{ sPageTitle|raw }}
15
  </h2>
16
  </div>
10
  </div>
11
  <div class="page-header">
12
  <h2>
13
+ <a id="pluginlogo_32" class="header-icon32" href="https://shsec.io/2k" target="_blank"></a>
14
  {{ sPageTitle|raw }}
15
  </h2>
16
  </div>
templates/twig/notices/compat-sgoptimize.twig ADDED
@@ -0,0 +1,25 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {% extends "/notices/base-error.twig" %}
2
+
3
+ {% block notice_body %}
4
+ <p>{{ strings.message }}
5
+ <a href="https://shsec.io/g7" target="_blank">{{ strings.learn_more }}</a>
6
+ <p><a href="#" id="SGOptimizerTurnOff">{{ strings.sgoptimizer_turnoff }}</a></p>
7
+
8
+ <script type="text/javascript">
9
+ jQuery( document ).on(
10
+ 'click',
11
+ 'a#SGOptimizerTurnOff',
12
+ function () {
13
+ iCWP_WPSF_BodyOverlay.show();
14
+ jQuery.get( ajaxurl, {{ ajax.sgoptimizer_turnoff|raw }} )
15
+ .always(
16
+ function () {
17
+ location.reload();
18
+ }
19
+ );
20
+ }
21
+ );
22
+
23
+
24
+ </script>
25
+ {% endblock %}
templates/twig/notices/rate-plugin.twig CHANGED
@@ -4,7 +4,7 @@
4
  <p>A lot of work goes into Shield Security, and we need your help to spread the word about it. :)
5
  <p>Even just a 1-liner review that says you're happy with it, will help us immensely.</p>
6
  <p>
7
- <a href="https://icwp.io/wpsfreview" class="button button-primary" target="_blank">
8
  Click to leave a review on WordPress.org &rarr;</a>
9
  </p>
10
  {% endblock %}
4
  <p>A lot of work goes into Shield Security, and we need your help to spread the word about it. :)
5
  <p>Even just a 1-liner review that says you're happy with it, will help us immensely.</p>
6
  <p>
7
+ <a href="https://shsec.io/wpsfreview" class="button button-primary" target="_blank">
8
  Click to leave a review on WordPress.org &rarr;</a>
9
  </p>
10
  {% endblock %}
templates/twig/snippets/comment_form_botbox.twig CHANGED
@@ -1,82 +1 @@
1
-
2
- <p id="{{ form_id }}"></p>
3
- <input type="hidden" id="_sugar_sweet_email" name="sugar_sweet_email" value="" />
4
- <input type="hidden" id="_botts" name="botts" value="{{ ts }}" />
5
- <input type="hidden" id="_comment_token" name="comment_token" value="{{ token }}" />
6
-
7
- <script type="text/javascript">
8
-
9
- function reenableButton{{ form_id }}() {
10
- let nRemaining = {{ cooldown }} - nTimerCounter{{ form_id }};
11
- subbutton{{ form_id }}.value = "{{ js_comment_wait|raw }}";
12
- if ( nTimerCounter{{ form_id }} >= {{ cooldown }} ) {
13
- subbutton{{ form_id }}.value = origButtonValue{{ form_id }};
14
- subbutton{{ form_id }}.disabled = false;
15
- clearInterval( sCountdownTimer{{ form_id }} );
16
- }
17
- nTimerCounter{{ form_id }}++;
18
- }
19
-
20
- function redisableButton{{ form_id }}() {
21
- subbutton{{ form_id }}.value = "{{ comment_reload }}";
22
- subbutton{{ form_id }}.disabled = true;
23
- }
24
-
25
- let oForm = document.getElementById( '{{ form_id }}' );
26
- let cb{{ form_id }} = document.createElement( 'input' );
27
- cb{{ form_id }}.type = 'checkbox';
28
- cb{{ form_id }}.id = 'checkbox{{ form_id }}';
29
- cb{{ form_id }}.name = 'checkbox{{ form_id }}';
30
- cb{{ form_id }}.style.width = '25px';
31
- cb{{ form_id }}.onclick = function () {
32
- cb_name{{ form_id }}.value = cb{{ form_id }}.name;
33
- };
34
-
35
- let label{{ form_id }} = document.createElement( 'label' );
36
- let labelspan{{ form_id }} = document.createElement( 'span' );
37
- label{{ form_id }}.htmlFor = 'checkbox{{ form_id }}';
38
- labelspan{{ form_id }}.innerHTML = "{{ confirm|raw }}";
39
-
40
- let cb_name{{ form_id }} = document.createElement( 'input' );
41
- cb_name{{ form_id }}.type = 'hidden';
42
- cb_name{{ form_id }}.name = 'cb_nombre';
43
-
44
- oForm.appendChild( label{{ form_id }} );
45
- label{{ form_id }}.appendChild( cb{{ form_id }} );
46
- label{{ form_id }}.appendChild( labelspan{{ form_id }} );
47
- oForm.appendChild( cb_name{{ form_id }} );
48
-
49
- let frm{{ form_id }} = cb{{ form_id }}.form;
50
- frm{{ form_id }}.onsubmit = function() {
51
- if ( cb{{ form_id }}.checked !== true ) {
52
- alert( "{{ alert|raw }}" );
53
- return false;
54
- }
55
- return true;
56
- };
57
-
58
- {% if cooldown > 0 or expire > 0 %}
59
-
60
- let subbuttonList{{ form_id }} = frm{{ form_id }}.querySelectorAll( 'input[type="submit"]' );
61
-
62
- if ( typeof (subbuttonList{{ form_id }} ) !== "undefined" ) {
63
-
64
- subbutton{{ form_id }} = subbuttonList{{ form_id }}[ 0 ];
65
-
66
- if ( typeof (subbutton{{ form_id }}) !== "undefined" ) {
67
-
68
- {% if cooldown > 0 %}
69
- subbutton{{ form_id }}.disabled = true;
70
- origButtonValue{{ form_id }} = subbutton{{ form_id }}.value;
71
- nTimerCounter{{ form_id }} = 0;
72
- reenableButton{{ form_id }}();
73
- sCountdownTimer{{ form_id }} = setInterval( reenableButton{{ form_id }}, 1000 );
74
- {% endif %}
75
-
76
- {% if expire > 0 %}
77
- sTimeoutTimer{{ form_id }} = setTimeout( redisableButton{{ form_id }}, (1000 * {{ expire }} -1000) );
78
- {% endif %}
79
- }
80
- }
81
- {% endif %}
82
- </script>
1
+ <p id="{{ uniq }}"></p>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
templates/twig/wizard/slides/welcome/optin.twig CHANGED
@@ -18,7 +18,7 @@
18
  <hr />
19
 
20
  <h6>Bonus: Join our new Facebook group</h6>
21
- <p><a href="https://icwp.io/cu" target="_blank">
22
  Click here to request access to our new Facebook group</a> where you can ask questions
23
  and help others with WordPress security and in particular, the Shield Security plugin.</p>
24
 
18
  <hr />
19
 
20
  <h6>Bonus: Join our new Facebook group</h6>
21
+ <p><a href="https://shsec.io/cu" target="_blank">
22
  Click here to request access to our new Facebook group</a> where you can ask questions
23
  and help others with WordPress security and in particular, the Shield Security plugin.</p>
24
 
templates/twig/wpadmin_pages/base.twig CHANGED
@@ -41,3 +41,6 @@
41
  </div>
42
  </div>
43
  {% endif %}
 
 
 
41
  </div>
42
  </div>
43
  {% endif %}
44
+
45
+ {% block inline_scripts %}
46
+ {% endblock %}
templates/twig/wpadmin_pages/insights/base.twig CHANGED
@@ -95,4 +95,7 @@
95
  {% endblock %}
96
 
97
  {% block inline_styles %}
 
 
 
98
  {% endblock %}
95
  {% endblock %}
96
 
97
  {% block inline_styles %}
98
+ {% endblock %}
99
+
100
+ {% block inline_scripts %}
101
  {% endblock %}
templates/twig/wpadmin_pages/insights/insights/stats.twig DELETED
@@ -1,14 +0,0 @@
1
- <div class="row" id="SectionStats">
2
- {% for stat in vars.insight_stats %}
3
- <div class="col-md-2">
4
- <div class="stat card text-center"
5
- {% if stat.tooltip is defined %}
6
- title="{{ stat.tooltip }}"
7
- {% endif %}
8
- >
9
- <div class="card-header">{{ stat.title }}</div>
10
- <div class="card-body">{{ stat.val }}</div>
11
- </div>
12
- </div>
13
- {% endfor %}
14
- </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
templates/twig/wpadmin_pages/insights/license/license.twig CHANGED
@@ -206,20 +206,17 @@
206
  <div id="collthree" class="collapse" aria-labelledby="headingThree" data-parent="#accordion">
207
  <div class="card-body">
208
 
209
- <p>One Dollar Plugin is our new Plugin Store. We're building a collection of high-quality,
210
- highly reliable WordPress plugins for a fraction of the normal cost - making premium WordPress
211
- plugins available to everyone.
212
- </p>
213
- <p>Shield Pro is our first One Dollar Plugin and is sold for the equivalent of
214
- $1/month per site ($12/year)</p>
215
  <ol>
216
  <li>Just grab a new license from the
217
- <a href="https://icwp.io/buyshieldpro" target="_blank">One Dollar Plugin here</a>.</li>
218
- <li>Activate your license on your sites using the 'Activate Key' button.</li>
 
219
  </ol>
220
 
221
  <p class="text-center">
222
- <a href="https://icwp.io/buyshieldpro" target="_blank" id="ButtonBuyNow"
223
  class="btn btn-large btn-success">
224
  Upgrade To Shield Pro Now &rarr;</a>
225
  </p>
206
  <div id="collthree" class="collapse" aria-labelledby="headingThree" data-parent="#accordion">
207
  <div class="card-body">
208
 
209
+ <p>Shield Security Pro is available from our online store and may be purchased
210
+ in US Dollar, &euro;uros, or &pound;GBPounds</p>
 
 
 
 
211
  <ol>
212
  <li>Just grab a new license from the
213
+ <a href="https://shsec.io/buyshieldpro" target="_blank">Shield Pro store</a>.</li>
214
+ <li>Register your site URL with our control panel.</li>
215
+ <li>Activate your license on your sites using the 'Check License' button.</li>
216
  </ol>
217
 
218
  <p class="text-center">
219
+ <a href="https://shsec.io/buyshieldpro" target="_blank" id="ButtonBuyNow"
220
  class="btn btn-large btn-success">
221
  Upgrade To Shield Pro Now &rarr;</a>
222
  </p>
templates/twig/wpadmin_pages/insights/original/audit_trail.twig DELETED
@@ -1,34 +0,0 @@
1
- <div id="SectionAuditTrail" class="insights_widget card w-100">
2
- <div class="card-header">
3
- <h5 class="card-title">Audit Trail</h5>
4
- <h6 class="card-subtitle mb-2 text-muted">20 most recent Audit Trail events</h6>
5
- </div>
6
- <div class="card-body overflow_container">
7
- {% if flags.has_audit_trail_entries %}
8
- <div class="overflow_inner">
9
- <table class="table table-hover table-sm mb-0">
10
- <thead><tr>
11
- <th>Date</th>
12
- <th>Message</th>
13
- <th>User</th>
14
- <th>IP</th>
15
- </tr></thead>
16
- <tbody>
17
- {% for audit_entry in vars.audit_trail_recent %}
18
- <tr>
19
- <td>{{ audit_entry.created_at }}</td>
20
- <td>{{ audit_entry.message }}</td>
21
- <td>{{ audit_entry.wp_username }}</td>
22
- <td>{{ audit_entry.ip }}</td>
23
- </tr>
24
- {% endfor %}
25
- </tbody>
26
- </table>
27
- </div>
28
- {#<a href="#" class="card-link">Card link</a>#}
29
- {#<a href="#" class="card-link">Another link</a>#}
30
- {% else %}
31
- <h6 class="card-subtitle mb-2 text-muted">No Audit Trail entries (yet).</h6>
32
- {% endif %}
33
- </div>
34
- </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
templates/twig/wpadmin_pages/insights/original/index.twig DELETED
@@ -1,202 +0,0 @@
1
- {% extends '/wpadmin_pages/base.twig' %}
2
-
3
- {% block h1heading %}<h1>{{ strings.page_title }}</h1>{% endblock %}
4
-
5
- {% block page_head %}
6
- {% endblock %}
7
-
8
- {% block page_main %}
9
- <div class="row">
10
- {#<div class="col-6">#}
11
- {#{% include '/wpadmin_pages/insights/original/title.twig' %}#}
12
- {#</div>#}
13
- <div class="col-12">
14
- {% include '/wpadmin_pages/insights/original/stats.twig' %}
15
- </div>
16
- </div>
17
- <div class="row">
18
- <div class="col-lg-7">
19
- {% include '/wpadmin_pages/insights/original/notices.twig' %}
20
- </div>
21
- <div class="col-lg-5">
22
- {% include '/wpadmin_pages/insights/notes/admin_notes.twig' %}
23
- </div>
24
- </div>
25
- <div class="row">
26
- <div class="col-lg-7">
27
- {% include '/wpadmin_pages/insights/audit/audit_trail.twig' %}
28
- </div>
29
- <div class="col-lg-5">
30
- {% include '/wpadmin_pages/insights/original/recent_events.twig' %}
31
- </div>
32
- </div>
33
- <div class="row">
34
- <div class="col">
35
- </div>
36
- </div>
37
- {% endblock %}
38
-
39
- {% block page_foot %}
40
- {% include '/wpadmin_pages/insights/original/mod_summary.twig' %}
41
- {% endblock %}
42
-
43
- {% block inline_styles %}
44
- <style>
45
- #odp-body-container {
46
- font-size: 13px;
47
- }
48
- .insights_widget.card {
49
- border: 1px solid rgba(0, 0, 0, 0.1);
50
- border-radius: 3px;
51
- box-shadow: 2px 2px 1px rgba(0, 0, 0, 0.07);
52
- padding: 0 0 3px 0;
53
- max-width: 100%;
54
- }
55
- .insights_widget .card-body {
56
- padding: 0 0 0 0;
57
- }
58
- .overflow_container {
59
- max-height: 350px;
60
- overflow-x: hidden;
61
- overflow-y: auto;
62
- padding-bottom: 1rem;
63
- }
64
- .overflow_inner {
65
- padding: 1rem;
66
- }
67
- .overflow_inner dl {
68
- margin-bottom: 1px;
69
- }
70
- .title_row th,
71
- .title_row td {
72
- background-color: #fbfbfb;
73
- }
74
- .message_row th,
75
- .message_row td {
76
- border-top: 1px solid #e9edf1; /** make bootstrap border lighter */
77
- }
78
- td.cell_delete_note {
79
- max-width: 20px;
80
- }
81
- .icon-sign {
82
- font-size: 14px;
83
- }
84
- #NewAdminNote textarea {
85
- height: 64px;
86
- }
87
- .btn.note_delete {
88
- padding: 0.1rem 0.3rem 0.3rem;
89
- line-height: 9px;
90
- }
91
- #SectionAuditTrail table {
92
- font-size: 12px;
93
- }
94
- #wpbody-content {
95
- padding-bottom: 0;
96
- }
97
- #wpfooter {
98
- display: none;
99
- }
100
- #odp-PageFoot {
101
- position: sticky;
102
- bottom: 0;
103
- z-index: 2;
104
- margin-top: 30px;
105
- }
106
- #SectionStats {
107
- }
108
- .stat.card {
109
- text-align: center;
110
- padding: 0;
111
- }
112
- .stat.card .card-body {
113
- font-size: 18px;
114
- }
115
- #ModSummary {
116
- border-bottom: 1px solid #aaaaaa;
117
- border-top: 1px solid #aaaaaa;
118
- background-color: #ffffff;
119
- }
120
- table td.mod-summary {
121
- height: 52px;
122
- text-align: center;
123
- }
124
- table td.mod-summary:hover {
125
- background-color: #f0f0f0;
126
- }
127
- .mod-summary > a {
128
- width: 32px;
129
- height: 32px;
130
- margin: auto;
131
- display: inline-block;
132
- }
133
- .mod-summary > a:hover {
134
- background-color: #f6f6f6;
135
- }
136
- .mod-summary.state-enabled > a {
137
- color: rgba(69, 119, 0, 1);
138
- }
139
- .mod-summary.state-disabled > a {
140
- color: rgba(173, 84, 0, 0.85);
141
- }
142
- .mod-icon {
143
- font-size: 32px;
144
- display: block;
145
- }
146
- .mod-icon:hover {
147
- text-decoration: none;
148
- }
149
- .mod-plugin .mod-icon:before {
150
- content: "\f111";
151
- }
152
- .mod-admin_access_restriction .mod-icon:before {
153
- content: "\f332";
154
- }
155
- .mod-firewall .mod-icon:before {
156
- content: "\f479";
157
- }
158
- .mod-user_management .mod-icon:before {
159
- content: "\f307";
160
- }
161
- .mod-hack_protect .mod-icon:before {
162
- content: "\f153";
163
- }
164
- .mod-headers .mod-icon:before {
165
- content: "\f163";
166
- }
167
- .mod-login_protect .mod-icon:before {
168
- content: "\f112";
169
- }
170
- .mod-comments_filter .mod-icon:before {
171
- content: "\f125";
172
- }
173
- .mod-autoupdates .mod-icon:before {
174
- content: "\f463";
175
- }
176
- .mod-lockdown .mod-icon:before {
177
- content: "\f160";
178
- }
179
- .mod-audit_trail .mod-icon:before {
180
- content: "\f115";
181
- }
182
- .mod-traffic .mod-icon:before {
183
- content: "\f177";
184
- }
185
- .mod-ips .mod-icon:before {
186
- content: "\f230";
187
- }
188
- .mod-license .mod-icon:before {
189
- content: "\f525";
190
- }
191
- </style>
192
-
193
-
194
- <script type="text/javascript">
195
- jQuery( document ).ready( function () {
196
- jQuery( '.stat.card' ).tooltip( {
197
- placement: 'bottom',
198
- trigger: 'hover'
199
- } );
200
- } );
201
- </script>
202
- {% endblock %}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
templates/twig/wpadmin_pages/insights/original/mod_summary.twig DELETED
@@ -1,21 +0,0 @@
1
- <div class="row" style="height: 15px;background-color: #e4e4e4;">
2
- <div class="col"></div>
3
- </div>
4
- <div class="row" id="ModSummary">
5
- <div class="col">
6
- <table class="table table-borderless mb-0 w-100">
7
- <tr>
8
- {% for mod in vars.summary %}
9
- <td class="mod-summary mod-{{ mod.slug }}
10
- state-{% if mod.enabled %}enabled{% else %}disabled{% endif %}"
11
- >
12
- <a class="mod-icon dashicons"
13
- href="{{ mod.href }}"
14
- title="{{ mod.name }}">
15
- </a>
16
- </td>
17
- {% endfor %}
18
- </tr>
19
- </table>
20
- </div>
21
- </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
templates/twig/wpadmin_pages/insights/original/notices.twig DELETED
@@ -1,58 +0,0 @@
1
- <div id="SectionNotices" class="insights_widget card w-100">
2
- <div class="card-header">
3
- <h5 class="card-title">Security Notices
4
- <span class="badge {% if flags.has_notices %}badge-warning{% else %}badge-success{% endif %}">
5
- {{ vars.insight_notices_count }}
6
- </span>
7
- </h5>
8
- <h6 class="card-subtitle text-muted">Potential security issues on your site <em>right now</em></h6>
9
- </div>
10
- <div class="card-body overflow_container">
11
-
12
- {% if flags.has_notices %}
13
- <div class="overflow_inner p-0">
14
- <table class="table mb-0">
15
- {% for notice_section in vars.insight_notices %}
16
- {% if notice_section.count > 0 %}
17
- <tr class="title_row">
18
- <th colspan="4">
19
- <h6 class="m-0">
20
- {{ notice_section.title }}
21
- <span class="text-muted" style="font-size: smaller;">({{ notice_section.count }})</span>
22
- </h6>
23
- </th>
24
- </tr>
25
-
26
- {% for notice in notice_section.messages %}
27
- <tr class="message_row">
28
- <td class="empty_cell">&nbsp;</td>
29
- <th>{{ notice.title }}:</th>
30
- <td>
31
- <span class="icon-exclamation-sign icon-sign">&#9888;</span> {{ notice.message }}
32
- {% if notice.rec is defined %}
33
- <br /><span class="text-muted">
34
- <span class="icon-exclamation-sign icon-sign">&#9755;</span>
35
- {{ notice.rec|default('') }}</span>
36
- {% endif %}
37
- </td>
38
- <td class="text-right text-nowrap">
39
- {% if notice.href is not empty %}
40
- <a href="{{ notice.href }}" target="_blank">
41
- {{ notice.action|default( strings.more_info ) }} &nearr;
42
- </a>
43
- {% endif %}
44
- </td>
45
- </tr>
46
- {% endfor %}
47
- {% endif %}
48
- {% endfor %}
49
- </table>
50
- </div>
51
- {% else %}
52
- <div class="alert alert-success">
53
- There are no important security notices at this time. This is a wonderful thing! :)
54
- </div>
55
- {% endif %}
56
-
57
- </div>
58
- </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
templates/twig/wpadmin_pages/insights/original/recent_events.twig DELETED
@@ -1,18 +0,0 @@
1
- <div id="SectionRecentEvents" class="insights_widget card w-100">
2
- <div class="card-header">
3
- <h5 class="card-title">Recent Events</h5>
4
- <h6 class="card-subtitle text-muted">{{ strings.box_receve_subtitle }}</h6>
5
- </div>
6
- <div class="card-body overflow_container">
7
- <div class="overflow_inner">
8
- {% for insight_event in vars.insight_events %}
9
- <dl class="row">
10
- <dt class="col-6">{{ insight_event.name }}:</dt>
11
- <dd class="col-6">{{ insight_event.val }}</dd>
12
- </dl>
13
- {% endfor %}
14
- </div>
15
- {#<a href="#" class="card-link">Card link</a>#}
16
- {#<a href="#" class="card-link">Another link</a>#}
17
- </div>
18
- </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
templates/twig/wpadmin_pages/insights/original/stats.twig DELETED
@@ -1,20 +0,0 @@
1
- <div class="row" id="SectionStats">
2
- {% for stat in vars.insight_stats %}
3
- <div class="col-md-1">
4
- <div class="stat card"
5
- {% if stat.tooltip is defined %}
6
- title="{{ stat.tooltip }}"
7
- {% endif %}
8
- >
9
- <div class="card-header">
10
- {{ stat.title }}
11
- </div>
12
- <div class="card-body">
13
- {{ stat.val }}
14
- </div>
15
- </div>
16
- </div>
17
- {% endfor %}
18
- </div>
19
-
20
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
templates/twig/wpadmin_pages/insights/original/title.twig DELETED
@@ -1,23 +0,0 @@
1
- <div id="SectionTitle" class="insights_widget card w-100">
2
- <div class="card-header">
3
- <h5 class="card-title mb-0">{{ strings.box_welcome_title }}</h5>
4
- </div>
5
- <div class="card-body overflow_container">
6
- <div class="overflow_inner">
7
- <p>This <span class="font-weight-bold">Insights Dashboard</span> is new with v6.7.0
8
- <br />It's a 1st version, designed
9
- to provide a high-level summary of your WordPress site security, and Shield activity.</p>
10
- <p>If you have suggestions or feedback, please let us know
11
- <a href="https://icwp.io/shieldsecuritygroupfb" target="_blank">
12
- in our <span class="badge badge-info">new</span> Facebook group</a>.</p>
13
- {% if flags.is_pro %}
14
- <p>Thank you for your support with your Shield Pro purchase. :)</p>
15
- {% else %}
16
- <p>For just $1/month, get all the extra features and 1-on-1 technical support -
17
- <a href="https://icwp.io/cw" class="btn btn-outline-success">Go Pro Today!</a></p>
18
- {% endif %}
19
- </div>
20
- {#<a href="#" class="card-link">Card link</a>#}
21
- {#<a href="#" class="card-link">Another link</a>#}
22
- </div>
23
- </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
templates/twig/wpadmin_pages/insights/{insights → overview}/index.twig RENAMED
@@ -3,15 +3,15 @@
3
  {% block page_main %}
4
  <div class="row">
5
  <div class="col-12 insights_section">
6
- {% include '/wpadmin_pages/insights/insights/stats.twig' %}
7
  </div>
8
  </div>
9
  <div class="row">
10
  <div class="col-xl-6 col-lg-12 insights_section">
11
- {% include '/wpadmin_pages/insights/insights/notices.twig' %}
12
  </div>
13
  <div class="col-xl-6 col-lg-12 insights_section">
14
- {% include '/wpadmin_pages/insights/insights/recent_events.twig' %}
15
  </div>
16
  </div>
17
 
@@ -58,16 +58,61 @@
58
  data-featherlight-iframe-height="675">&nbsp;</a>
59
  {% endif %}
60
  </div>
61
- <script>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
62
  jQuery( '#collapseConfigSummary' ).on( 'shown.bs.collapse', function () {
63
  jQuery( 'html, body' ).animate( {
64
  scrollTop: jQuery( "#collapseConfigSummaryTitle" ).offset().top - 115
65
  }, 750 );
66
  } );
67
  {% if flags.show_guided_tour %}
68
- window.onload = function () {
69
- jQuery( '#IntroVideo' ).click();
70
- };
71
  {% endif %}
72
  </script>
73
  {% endblock %}
3
  {% block page_main %}
4
  <div class="row">
5
  <div class="col-12 insights_section">
6
+ {% include '/wpadmin_pages/insights/overview/stats.twig' %}
7
  </div>
8
  </div>
9
  <div class="row">
10
  <div class="col-xl-6 col-lg-12 insights_section">
11
+ {% include '/wpadmin_pages/insights/overview/notices.twig' %}
12
  </div>
13
  <div class="col-xl-6 col-lg-12 insights_section">
14
+ {% include '/wpadmin_pages/insights/overview/recent_events.twig' %}
15
  </div>
16
  </div>
17
 
58
  data-featherlight-iframe-height="675">&nbsp;</a>
59
  {% endif %}
60
  </div>
61
+ {% endblock %}
62
+
63
+ {% block inline_styles %}
64
+ <style>
65
+ .ct-series-a .ct-line {
66
+ /* Set the colour of this series line */
67
+ stroke: green;
68
+ /* Control the thikness of your lines */
69
+ stroke-width: 2px;
70
+ /* Create a dashed line with a pattern */
71
+ stroke-dasharray: 3px 1px;
72
+ }
73
+
74
+ /* This selector overrides the points style on line charts. Points on line charts are actually just very short strokes. This allows you to customize even the point size in CSS */
75
+ .ct-series-a .ct-point {
76
+ /* Colour of your points */
77
+ stroke: green;
78
+ /* Size of your points */
79
+ stroke-width: 4px;
80
+ /* Make your points appear as squares */
81
+ stroke-linecap: square;
82
+ }
83
+ </style>
84
+ {% endblock %}
85
+
86
+ {% block inline_scripts %}
87
+ <script type="text/javascript">
88
+ jQuery( document ).ready( function () {
89
+
90
+ {% for stat in vars.insight_stats %}
91
+ jQuery( '#statcard-{{ stat.id }} .card-body .stat-chart' ).icwpWpsfAjaxChart(
92
+ {
93
+ 'ajax_render':{{ ajax.render_chart_post|raw }},
94
+ 'req_params': {
95
+ 'render_location': "insights-overview-statcard",
96
+ 'chart_params': {
97
+ 'stat_id': "{{ stat.id }}",
98
+ }
99
+ }
100
+ }
101
+ );
102
+ {% endfor %}
103
+
104
+ } );
105
+ </script>
106
+ <script type="text/javascript">
107
  jQuery( '#collapseConfigSummary' ).on( 'shown.bs.collapse', function () {
108
  jQuery( 'html, body' ).animate( {
109
  scrollTop: jQuery( "#collapseConfigSummaryTitle" ).offset().top - 115
110
  }, 750 );
111
  } );
112
  {% if flags.show_guided_tour %}
113
+ window.onload = function () {
114
+ jQuery( '#IntroVideo' ).click();
115
+ };
116
  {% endif %}
117
  </script>
118
  {% endblock %}
templates/twig/wpadmin_pages/insights/{insights → overview}/notices.twig RENAMED
File without changes
templates/twig/wpadmin_pages/insights/{insights → overview}/recent_events.twig RENAMED
File without changes
templates/twig/wpadmin_pages/insights/overview/stats.twig ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <div class="row" id="SectionStats">
2
+ {% for stat in vars.insight_stats %}
3
+ <div class="col-lg-2 col-md-4">
4
+ <div class="stat card text-center" id="statcard-{{ stat.id }}"
5
+ {% if stat.tooltip is defined %}title="{{ stat.tooltip }}"{% endif %}
6
+ >
7
+ <div class="card-header">
8
+ <p class="mb-0">{{ stat.title }}</p>
9
+ <span>({{ stat.title_sub }})</span>
10
+ </div>
11
+ <div class="card-body">
12
+ <div class="stat-chart"
13
+ {% if stat.tooltip_chart is defined %}title="{{ stat.tooltip_chart }}"{% endif %}></div>
14
+ <h6 style="font-size: small" {% if stat.tooltip_p is defined %}title="{{ stat.tooltip_p }}"{% endif %}>{{ stat.val }}</h6>
15
+ </div>
16
+ </div>
17
+ </div>
18
+ {% endfor %}
19
+ </div>
templates/twig/wpadmin_pages/insights/reports/index.twig CHANGED
@@ -19,11 +19,9 @@
19
  </div>
20
  {% endblock %}
21
 
22
- {% block inline_styles %}
23
-
24
- <script>
25
- let $oChartOffense;
26
 
 
27
  jQuery( document ).ready( function () {
28
 
29
  let $oChartOffense = jQuery( '#ChartDaily' ).icwpWpsfAjaxChart(
19
  </div>
20
  {% endblock %}
21
 
22
+ {% block inline_scripts %}
 
 
 
23
 
24
+ <script type="text/javascript">
25
  jQuery( document ).ready( function () {
26
 
27
  let $oChartOffense = jQuery( '#ChartDaily' ).icwpWpsfAjaxChart(
unsupported.php CHANGED
@@ -17,7 +17,7 @@ function icwp_wpsf_unsupported_php() {
17
 
18
  sprintf( 'Shield Security Plugin - Unsupported PHP Version: %s', PHP_VERSION ),
19
  implode( '<br/>', $aText ),
20
- 'https://icwp.io/dl',
21
  sprintf( 'Click here for more info' ),
22
  add_query_arg(
23
  array(
17
 
18
  sprintf( 'Shield Security Plugin - Unsupported PHP Version: %s', PHP_VERSION ),
19
  implode( '<br/>', $aText ),
20
+ 'https://shsec.io/dl',
21
  sprintf( 'Click here for more info' ),
22
  add_query_arg(
23
  array(