Shield Security for WordPress - Version 6.8.0

Version Description

  • Current Release = Released: 11th June, 2018 - Release Notes

  • (v.0) ADDED: [PRO] White Label - ability to re-brand the entire Shield Security plugin to your company brand.

  • (v.0) ADDED: [PRO] Option for all users to receive notification email upon login to their accounts.

  • (v.0) IMPROVED: Completely rebuilt the bot and reCAPTCHA login protection system.

  • (v.0) IMPROVED: Import/Export system hugely improved with respect to automated push of options from Master sites.

  • (v.0) IMPROVED: A different approach to sessions management that should handle sessions a bit better.

  • (v.0) IMPROVED: Expired user sessions are cleaned from the DB using a cron, and on Insights Dashboard load.

Download this release

Release Info

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

Code changes from version 6.7.2 to 6.8.0

Files changed (169) hide show
  1. changelog.html +55 -55
  2. icwp-plugin-controller.php +25 -20
  3. icwp-wpsf.php +3 -3
  4. init.php +5 -1
  5. languages/wp-simple-firewall-nl_NL.mo +0 -0
  6. plugin-spec.php +7 -6
  7. readme.txt +42 -32
  8. resources/css/plugin.css +1 -0
  9. resources/images/pluginlogo_128x128.png +0 -0
  10. resources/js/plugin.js +16 -3
  11. src/common/icwp-data.php +9 -0
  12. src/common/icwp-foundation.php +17 -8
  13. src/common/icwp-render.php +2 -2
  14. src/common/icwp-wpfunctions-plugins.php +2 -2
  15. src/common/icwp-wpfunctions.php +2 -2
  16. src/common/lib/composer.lock +18 -23
  17. src/common/lib/vendor/composer/autoload_psr4.php +1 -1
  18. src/common/lib/vendor/composer/autoload_static.php +5 -8
  19. src/common/lib/vendor/composer/installed.json +88 -93
  20. src/common/lib/vendor/nesbot/carbon/src/Carbon/Carbon.php +626 -71
  21. src/common/lib/vendor/nesbot/carbon/src/Carbon/CarbonInterval.php +499 -56
  22. src/common/lib/vendor/nesbot/carbon/src/Carbon/CarbonPeriod.php +1429 -0
  23. src/common/lib/vendor/nesbot/carbon/src/Carbon/Lang/af.php +14 -14
  24. src/common/lib/vendor/nesbot/carbon/src/Carbon/Lang/bg.php +14 -14
  25. src/common/lib/vendor/nesbot/carbon/src/Carbon/Lang/bs_BA.php +31 -0
  26. src/common/lib/vendor/nesbot/carbon/src/Carbon/Lang/ca.php +19 -14
  27. src/common/lib/vendor/nesbot/carbon/src/Carbon/Lang/cs.php +14 -14
  28. src/common/lib/vendor/nesbot/carbon/src/Carbon/Lang/cy.php +29 -0
  29. src/common/lib/vendor/nesbot/carbon/src/Carbon/Lang/da.php +14 -14
  30. src/common/lib/vendor/nesbot/carbon/src/Carbon/Lang/de.php +28 -22
  31. src/common/lib/vendor/nesbot/carbon/src/Carbon/Lang/el.php +14 -14
  32. src/common/lib/vendor/nesbot/carbon/src/Carbon/Lang/en.php +23 -14
  33. src/common/lib/vendor/nesbot/carbon/src/Carbon/Lang/eo.php +14 -14
  34. src/common/lib/vendor/nesbot/carbon/src/Carbon/Lang/es.php +19 -14
  35. src/common/lib/vendor/nesbot/carbon/src/Carbon/Lang/et.php +14 -14
  36. src/common/lib/vendor/nesbot/carbon/src/Carbon/Lang/fi.php +14 -14
  37. src/common/lib/vendor/nesbot/carbon/src/Carbon/Lang/fo.php +14 -14
  38. src/common/lib/vendor/nesbot/carbon/src/Carbon/Lang/fr.php +17 -12
  39. src/common/lib/vendor/nesbot/carbon/src/Carbon/Lang/gl.php +7 -7
  40. src/common/lib/vendor/nesbot/carbon/src/Carbon/Lang/gu.php +14 -14
  41. src/common/lib/vendor/nesbot/carbon/src/Carbon/Lang/hi.php +31 -0
  42. src/common/lib/vendor/nesbot/carbon/src/Carbon/Lang/is.php +31 -0
  43. src/common/lib/vendor/nesbot/carbon/src/Carbon/Lang/it.php +20 -15
  44. src/common/lib/vendor/nesbot/carbon/src/Carbon/Lang/ko.php +2 -2
  45. src/common/lib/vendor/nesbot/carbon/src/Carbon/Lang/mk.php +7 -7
  46. src/common/lib/vendor/nesbot/carbon/src/Carbon/Lang/ne.php +31 -0
  47. src/common/lib/vendor/nesbot/carbon/src/Carbon/Lang/nl.php +15 -10
  48. src/common/lib/vendor/nesbot/carbon/src/Carbon/Lang/no.php +19 -14
  49. src/common/lib/vendor/nesbot/carbon/src/Carbon/Lang/oc.php +40 -0
  50. src/common/lib/vendor/nesbot/carbon/src/Carbon/Lang/pl.php +19 -14
  51. src/common/lib/vendor/nesbot/carbon/src/Carbon/Lang/ps.php +14 -14
  52. src/common/lib/vendor/nesbot/carbon/src/Carbon/Lang/pt.php +14 -14
  53. src/common/lib/vendor/nesbot/carbon/src/Carbon/Lang/pt_BR.php +14 -14
  54. src/common/lib/vendor/nesbot/carbon/src/Carbon/Lang/sh.php +31 -0
  55. src/common/lib/vendor/nesbot/carbon/src/Carbon/Lang/sl.php +5 -0
  56. src/common/lib/vendor/nesbot/carbon/src/Carbon/Lang/sq.php +14 -14
  57. src/common/lib/vendor/nesbot/carbon/src/Carbon/Lang/sr_Cyrl.php +5 -0
  58. src/common/lib/vendor/nesbot/carbon/src/Carbon/Lang/sr_Cyrl_ME.php +5 -0
  59. src/common/lib/vendor/nesbot/carbon/src/Carbon/Lang/sr_Latn_ME.php +5 -0
  60. src/common/lib/vendor/nesbot/carbon/src/Carbon/Lang/sv.php +14 -14
  61. src/common/lib/vendor/nesbot/carbon/src/Carbon/Lang/sw.php +31 -0
  62. src/common/lib/vendor/nesbot/carbon/src/JsonSerializable.php +16 -0
  63. src/common/lib/vendor/symfony/polyfill-mbstring/Mbstring.php +21 -21
  64. src/common/lib/vendor/symfony/translation/PluralizationRules.php +1 -0
  65. src/common/lib/vendor/symfony/translation/composer.json +1 -1
  66. src/common/wp-admin-notices.php +1 -1
  67. src/common/wp-comments.php +1 -1
  68. src/config/changelog.json +4 -4
  69. src/config/feature-admin_access_restriction.php +57 -33
  70. src/config/feature-audit_trail.php +18 -18
  71. src/config/feature-autoupdates.php +3 -3
  72. src/config/feature-comments_filter.php +16 -16
  73. src/config/feature-firewall.php +3 -3
  74. src/config/feature-hack_protect.php +19 -19
  75. src/config/feature-headers.php +11 -11
  76. src/config/feature-ips.php +5 -5
  77. src/config/feature-license.php +2 -2
  78. src/config/feature-lockdown.php +5 -5
  79. src/config/feature-login_protect.php +78 -60
  80. src/config/feature-plugin.php +6 -6
  81. src/config/feature-user_management.php +16 -2
  82. src/features/admin_access_restriction.php +49 -22
  83. src/features/audit_trail.php +2 -2
  84. src/features/autoupdates.php +2 -1
  85. src/features/base.php +24 -6
  86. src/features/base_wpsf.php +13 -2
  87. src/features/headers.php +1 -1
  88. src/features/insights.php +11 -8
  89. src/features/license.php +6 -4
  90. src/features/login_protect.php +100 -18
  91. src/features/plugin.php +32 -33
  92. src/features/user_management.php +65 -4
  93. src/processors/admin_access_restriction.php +13 -2
  94. src/processors/adminaccess_whitelabel.php +59 -23
  95. src/processors/audit_trail.php +6 -6
  96. src/processors/audit_trail_auditor_base.php +3 -3
  97. src/processors/autoupdates.php +1 -1
  98. src/processors/base.php +16 -21
  99. src/processors/base_plugin.php +1 -1
  100. src/processors/base_wpsf.php +21 -1
  101. src/processors/basedb.php +7 -7
  102. src/processors/comments_filter.php +1 -1
  103. src/processors/commentsfilter_antibotspam.php +8 -8
  104. src/processors/commentsfilter_googlerecaptcha.php +1 -1
  105. src/processors/commentsfilter_humanspam.php +2 -2
  106. src/processors/cronbase.php +7 -3
  107. src/processors/email.php +24 -20
  108. src/processors/firewall.php +5 -5
  109. src/processors/hack_protect.php +3 -3
  110. src/processors/hackprotect_corechecksumscan.php +9 -6
  111. src/processors/hackprotect_filecleanerscan.php +8 -5
  112. src/processors/hackprotect_pluginvulnerabilities.php +6 -5
  113. src/processors/hackprotect_ptguard.php +4 -4
  114. src/processors/hackprotect_wpvulnscan.php +6 -4
  115. src/processors/ips.php +7 -7
  116. src/processors/license.php +1 -1
  117. src/processors/lockdown.php +1 -1
  118. src/processors/login_protect.php +2 -2
  119. src/processors/loginprotect_base.php +309 -0
  120. src/processors/loginprotect_cooldown.php +33 -51
  121. src/processors/loginprotect_gasp.php +68 -107
  122. src/processors/loginprotect_googleauthenticator.php +12 -16
  123. src/processors/loginprotect_googlerecaptcha.php +25 -61
  124. src/processors/loginprotect_intent.php +55 -27
  125. src/processors/loginprotect_intentprovider_base.php +16 -10
  126. src/processors/loginprotect_track.php +2 -2
  127. src/processors/loginprotect_twofactorauth.php +18 -19
  128. src/processors/loginprotect_wplogin.php +3 -3
  129. src/processors/loginprotect_yubikey.php +12 -14
  130. src/processors/plugin.php +5 -1
  131. src/processors/plugin_badge.php +9 -6
  132. src/processors/plugin_importexport.php +69 -4
  133. src/processors/plugin_notes.php +3 -3
  134. src/processors/plugin_tracking.php +2 -2
  135. src/processors/sessions.php +59 -15
  136. src/processors/statistics.php +9 -9
  137. src/processors/statistics_reporting.php +5 -5
  138. src/processors/user_management.php +63 -19
  139. src/processors/usermanagement_sessions.php +63 -26
  140. src/query/sessions_terminate.php +40 -0
  141. src/query/statistics_base.php +1 -1
  142. src/wizards/base.php +16 -9
  143. src/wizards/plugin.php +3 -3
  144. templates/html/plugin_badge.html +1 -1
  145. templates/php/index.php +13 -3
  146. templates/php/notices/rate-plugin.php +1 -1
  147. templates/php/page/login_intent.php +9 -7
  148. templates/php/snippets/module-help-admin_access_restriction.php +2 -2
  149. templates/php/snippets/module-help-firewall.php +1 -1
  150. templates/php/snippets/module-help-headers.php +1 -1
  151. templates/php/snippets/module-help-login_protect.php +1 -1
  152. templates/php/snippets/module-help-plugin.php +1 -1
  153. templates/php/snippets/plugin_badge.php +1 -1
  154. templates/php/snippets/plugin_badge_widget.php +1 -1
  155. templates/php/snippets/pro.php +8 -6
  156. templates/php/snippets/widget_dashboard_plugin.php +7 -3
  157. templates/php/widgets/icwp_common_widgets.php +3 -3
  158. templates/twig/emails/user_management/user_login_notification.twig +0 -0
  159. templates/twig/features/feature-base.twig +1 -1
  160. templates/twig/snippets/widget_common.twig +3 -3
  161. templates/twig/wizard/pages/base.twig +1 -1
  162. templates/twig/wizard/slides/common/base.twig +21 -5
  163. templates/twig/wizard/slides/common/base_finish.twig +5 -3
  164. templates/twig/wizard/slides/common/base_start.twig +2 -1
  165. templates/twig/wizard/slides/common/no_access.twig +1 -1
  166. templates/twig/wizard/slides/welcome/optin.twig +1 -1
  167. templates/twig/wpadmin_pages/insights/index.twig +1 -1
  168. templates/twig/wpadmin_pages/insights/recent_events.twig +1 -1
  169. templates/twig/wpadmin_pages/insights/title.twig +4 -4
changelog.html CHANGED
@@ -3821,9 +3821,9 @@ img {
3821
  </style>
3822
  <h2>Changelog: Shield Security for WordPress</h2>
3823
  <p>= 6.5 Series =<br>
3824
- <em>Released: 5th March, 2018</em> - <a href="http://icwp.io/bu">Release Notes</a></p>
3825
  <ul>
3826
- <li><strong>(v.0)</strong> IMPROVED: <a href="http://icwp.io/bq">Plugin Guard</a> better handles the case where a plugin/theme has been entirely renamed/removed.</li>
3827
  <li><strong>(v.0)</strong> IMPROVED: Attempts to access the XML-RPC system when it’s disabled will now result in a transgression increment in the IP Black List</li>
3828
  <li><strong>(v.0)</strong> IMPROVED: Try to prevent black listing the server’s own public IP address where visitor IP address detection is not correctly configured.</li>
3829
  <li><strong>(v.0)</strong> ADDED: [<strong>PRO</strong>] Provisional support for not processing 2FA logins for Woocommerce Social Login plugin.</li>
@@ -3831,26 +3831,26 @@ img {
3831
  <li><strong>(v.0)</strong> FIXED: A few small bugs</li>
3832
  </ul>
3833
  <p>= 6.4 Series =<br>
3834
- <em>Released: 26th February, 2018</em> - <a href="http://icwp.io/br">Release Notes</a></p>
3835
  <ul>
3836
  <li><strong>(v.1-4)</strong> FIXED: Various Fixes</li>
3837
- <li><strong>(v.0)</strong> ADDED: [<strong>PRO</strong>] New Scanner to <a href="http://icwp.io/bq">detect file changes for active plugins and themes</a></li>
3838
- <li><strong>(v.0)</strong> IMPROVED: Automatic updates for vulnerable plugins ignores <a href="http://icwp.io/bc">automatic updates delay setting</a></li>
3839
  <li><strong>(v.0)</strong> CHANGED: Email notifications for scanners will now link to the Wizard where possible, instead of listing files.</li>
3840
  </ul>
3841
  <p>= 6.3 Series =<br>
3842
- <em>Released: 12th February, 2018</em> - <a href="http://icwp.io/bc">Release Notes</a></p>
3843
  <ul>
3844
  <li><strong>(v.3)</strong> FIXED: Bug with automatic updates delay setting</li>
3845
  <li><strong>(v.2)</strong> CHANGED: Changed a text that seems to cause servers to swallow-up emails. <a
3846
- href="http://icwp.io/bi">See here for more reliable email</a></li>
3847
  <li><strong>(v.1)</strong> FIXED: Options page javascript to work around conflicts.</li>
3848
- <li><strong>(v.0)</strong> ADDED: [<strong>PRO</strong>] <a href="http://icwp.io/bc">Automatic updates stability delay</a></li>
3849
- <li><strong>(v.0)</strong> IMPROVED: Complete <a href="http://icwp.io/bd">plugin UI rebuild</a>, using the new Bootstrap 4.</li>
3850
  <li><strong>(v.0)</strong> FIXED: A few bugs with Google Authenticator.</li>
3851
  </ul>
3852
  <p>= 6.2 Series =<br>
3853
- <em>Released: 31st January, 2018</em> - <a href="http://icwp.io/b6">Release Notes</a></p>
3854
  <ul>
3855
  <li><strong>(v.2)</strong> FIXED: Fix for IP Manager PHP error.</li>
3856
  <li><strong>(v.2)</strong> IMPROVED: Two-factor verification email.</li>
@@ -3863,7 +3863,7 @@ img {
3863
  <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>
3864
  </ul>
3865
  <p>= 6.1 Series =<br>
3866
- <em>Released: 15th January, 2018</em> - <a href="http://icwp.io/ay">Release Notes</a></p>
3867
  <ul>
3868
  <li><strong>(v.1)</strong> FIXED: Verify link missing from the two-factor authentication verification email.</li>
3869
  <li><strong>(v.0)</strong> ADDED: 3x more Shield Wizards: Multi-factor Authentication, Core File Scanning, Unrecognised File Scanning.</li>
@@ -3876,9 +3876,9 @@ img {
3876
  <em>Released: 18th December, 2017</em></p>
3877
  <ul>
3878
  <li><strong>(v.0)</strong> ADDED: All-new Shield Welcome and Setup Wizard - more helpful guided wizards to come.</li>
3879
- <li><strong>(v.0)</strong> ADDED: [<strong>PRO</strong>] <a href="http://icwp.io/at">Shield options import and export</a></li>
3880
  <li><strong>(v.0)</strong> ADDED: [<strong>PRO</strong>] In conjunction with import/export - Shield Security Network: automated options syncing.</li>
3881
- <li><strong>(v.0)</strong> CHANGED: Going forward, new features and options will <a href="http://icwp.io/au">support only PHP 5.4+</a>. Existing features will remain unaffected.</li>
3882
  </ul>
3883
  <p>= 5.20 Series =<br>
3884
  <em>Released: 11th December, 2017</em></p>
@@ -3957,19 +3957,19 @@ img {
3957
  <li><strong>(v.2)</strong> IMPROVEMENTS: Small adjustment to handling of Shield User sessions in conjunction with WordPress sessions.</li>
3958
  <li><strong>(v.2)</strong> FIX: Restore display of help links for options.</li>
3959
  <li><strong>(v.1)</strong> FIX: PHP 5.2 incompatibility.</li>
3960
- <li><strong>(v.0)</strong> ADDED: New option for <a href="http://icwp.io/94">Unrecognised File Scanner</a> to scan the Uploads folder for JS and PHP files.</li>
3961
  <li><strong>(v.0)</strong> ADDED: Option to provide custom list of files to be excluded from the <a
3962
- href="http://icwp.io/94">Unrecognised File Scanner</a>.</li>
3963
  </ul>
3964
  <p>= 5.12 Series =<br>
3965
  <em>Released: 3rd August, 2017</em></p>
3966
  <ul>
3967
- <li><strong>(v.2)</strong> IMPROVEMENTS: Improved support for Windows IIS hosting for <a href="http://icwp.io/94">Unrecognised File Scanner</a></li>
3968
  <li><strong>(v.2)</strong> CHANGED: Removed the email-based 2FA automatic login link.</li>
3969
  <li><strong>(v.2)</strong> FIX: Potential bug with Shield not recognising plugin configuration updates and not rebuilding options accordingly.</li>
3970
- <li><strong>(v.1)</strong> ADDED: A few more exclusions for the <a href="http://icwp.io/94">Unrecognised File Scanner</a></li>
3971
  <li><strong>(v.1)</strong> FIX: Fix for Fatal error.</li>
3972
- <li><strong>(v.0)</strong> ADDED: <a href="http://icwp.io/94">Unrecognised File Scanner</a> release. Automatically detect and delete<br>
3973
  any files present in core WordPress directories that aren’t part of your core installation.</li>
3974
  <li><strong>(v.0)</strong> ADDED: Updated Firewall rules for SQL under the ‘Aggressive’ rule set.</li>
3975
  </ul>
@@ -3977,14 +3977,14 @@ any files present in core WordPress directories that aren’t part of your core
3977
  <em>Released: 26th July, 2017</em></p>
3978
  <ul>
3979
  <li><strong>(v.1)</strong> FIX: JSON syntax</li>
3980
- <li><strong>(v.0)</strong> IMPROVEMENTS: Final preparation for <a href="http://icwp.io/83">Shield Central</a> release.</li>
3981
  </ul>
3982
  <p>= 5.10 Series =<br>
3983
  <em>Released: 19th June, 2017</em></p>
3984
  <ul>
3985
  <li><strong>(v.2)</strong> FIXED: Fatal error with GASP + Password Reset.</li>
3986
  <li><strong>(v.2)</strong> FIXED: Fatal error with failing reCAPTCHA HTTP requests.</li>
3987
- <li><strong>(v.1)</strong> IMPROVEMENTS: Further preparation for <a href="http://icwp.io/83">Shield Central</a> release.</li>
3988
  <li><strong>(v.0)</strong> ADDED: More in-depth reporting and statistics gathering - options for reports will be made available<br>
3989
  in a later release.</li>
3990
  </ul>
@@ -4007,8 +4007,8 @@ Protects existing, legitimate sessions from being forcefully expired.</li>
4007
  <li><strong>(v.2)</strong> CHANGE: Changed timeout for two-factor authentication email to 5 minutes to account for slower email-sending providers.</li>
4008
  <li><strong>(v.2)</strong> CHANGE: Added further clarification to the Login Notification email indicating that two-factor authentication was pending.</li>
4009
  <li><strong>(v.1)</strong> FIXED: Fixed a couple of bugs with the Login Authentication Portal, for certain edge cases.</li>
4010
- <li><strong>(v.0)</strong> CHANGE: Major overhaul of <a href="http://icwp.io/87">Two-Factor / Multi-Factor Login Authentication</a>.</li>
4011
- <li><strong>(v.0)</strong> CHANGE: <a href="http://icwp.io/86">Introduction of Login Authentication Portal</a> for improved Multi-Factor Authentication.</li>
4012
  <li><strong>(v.0)</strong> ADDED: Option to choose between two-factor or multi-factor login authentication.</li>
4013
  <li><strong>(v.0)</strong> ADDED: Administrators can remove Google Authenticator from another user’s profile.</li>
4014
  <li><strong>(v.0)</strong> ADDED: When Security Admin is active, only Security Admins may remove Google Authenticator from other admins.</li>
@@ -4016,18 +4016,18 @@ Protects existing, legitimate sessions from being forcefully expired.</li>
4016
  <li><strong>(v.0)</strong> CHANGE: Email-based login authentication no longer uses a separate database table.</li>
4017
  <li><strong>(v.0)</strong> FIXED: Core file scanning now adequately handles Windows/Unix new lines during scan.</li>
4018
  <li><strong>(v.0)</strong> FIXED: Certain crons weren’t setup correctly.</li>
4019
- <li><strong>(v.0)</strong> IMPROVEMENTS: Further preparation for <a href="http://icwp.io/83">Shield Central</a> release.</li>
4020
  </ul>
4021
  <p>= 5.7 Series =</p>
4022
  <ul>
4023
  <li><strong>(v.3)</strong> FIXED: Attempt to improve the Google Authenticator flow for more reliable activation.</li>
4024
  <li><strong>(v.2)</strong> IMPROVEMENTS: More admin notices when saving Google Authenticator settings.</li>
4025
- <li><strong>(v.2)</strong> IMPROVEMENTS: Further preparation for <a href="http://icwp.io/83">Shield Central</a> release.</li>
4026
  <li><strong>(v.1)</strong> Skipped</li>
4027
  <li><strong>(v.0)</strong> ADDED: Shortcode for displaying plugin badge in pages/posts.</li>
4028
  <li><strong>(v.0)</strong> CHANGE: Enabled JS eval() for the Content Security Policy by default.</li>
4029
  <li><strong>(v.0)</strong> IMPROVEMENTS: Replace YAML configuration files with JSON.</li>
4030
- <li><strong>(v.0)</strong> IMPROVEMENTS: Preparation for <a href="http://icwp.io/83">Shield Central</a> release.</li>
4031
  <li><strong>(v.0)</strong> IMPROVEMENTS: Security Admin notices are more refined and optimized.</li>
4032
  <li><strong>(v.0)</strong> IMPROVEMENTS: Removed unnecessary files/code.</li>
4033
  </ul>
@@ -4094,7 +4094,7 @@ or service authenticates the request it will be honoured, whether anonymous or n
4094
  </li>
4095
  <li>
4096
  <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
4097
- href="http://icwp.io/7j">more info</a></p>
4098
  </li>
4099
  <li>
4100
  <p><strong>(v.1)</strong> IMPROVED: Better XMLRPC Lockdown - prevents ANY XMLRPC command processing.</p>
@@ -4153,7 +4153,7 @@ or service authenticates the request it will be honoured, whether anonymous or n
4153
  <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>
4154
  <li><strong>(v.2)</strong> ADDED: Exclusion for Swedish license files that don’t exist in the SVN repo.</li>
4155
  <li><strong>(v.2)</strong> ADDED: Parameter exclusion for reCAPTCHA.</li>
4156
- <li><strong>(v.2)</strong> CHANGED: <a href="http://icwp.io/7b">HTTP Security Headers</a> module is enabled by default on new installs.</li>
4157
  <li><strong>(v.1)</strong> FIXED: Nasty bug that caused an infinite loop bug in some configurations.</li>
4158
  <li><strong>(v.0)</strong> ADDED: Per-site plugin statistics gathering - summary display on admin dashboard.</li>
4159
  <li><strong>(v.0)</strong> ADDED: HTML class to the “I’m a human” checkbox field.</li>
@@ -4164,11 +4164,11 @@ or service authenticates the request it will be honoured, whether anonymous or n
4164
  </ul>
4165
  <p>= 5.3 Series =</p>
4166
  <ul>
4167
- <li><strong>(v.2)</strong> IMPROVED: <a href="http://icwp.io/7b">HTTP Security Headers</a> Content Security Policy now supports specifying HTTPS for domains/hosts.</li>
4168
  <li><strong>(v.2)</strong> FIXED: Human Comment SPAM Feature didn’t fire under certain circumstances.</li>
4169
  <li><strong>(v.2)</strong> FIXED: Fixed parsing of Human Comment SPAM dictionary words.</li>
4170
  <li><strong>(v.1)</strong> TRANSLATIONS: Dutch (32%)</li>
4171
- <li><strong>(v.0)</strong> ADDED: New Feature - <a href="http://icwp.io/7b">HTTP Security Headers</a>.</li>
4172
  <li><strong>(v.0)</strong> FIXED: Prevent renaming WP Login to “/login”</li>
4173
  </ul>
4174
  <p>= 5.2 Series =</p>
@@ -4177,7 +4177,7 @@ or service authenticates the request it will be honoured, whether anonymous or n
4177
  <li><strong>(v.0)</strong> CHANGED: Logic for brute force login checking is improved - they all run before username/password checking</li>
4178
  <li><strong>(v.0)</strong> FIXED: Certain older versions of PHP don’t like combined IPv4 and IPv6 filter flags</li>
4179
  <li><strong>(v.0)</strong> FIXED: Google reCAPTCHA for WordPress sites that have restrictive settings for sockets etc.</li>
4180
- <li><strong>(v.0)</strong> REMOVED: <a href="http://icwp.io/75">Plugin vulnerabilities scanner</a>. It’s out-of-date and unsuitable.</li>
4181
  </ul>
4182
  <p>= 5.1 Series =</p>
4183
  <ul>
@@ -4190,17 +4190,17 @@ or service authenticates the request it will be honoured, whether anonymous or n
4190
  <p>= 5.0 Series =</p>
4191
  <ul>
4192
  <li><strong>(v.3)</strong> FIXED: Issue with setting session cookies with PHP 7</li>
4193
- <li><strong>(v.2)</strong> FIXED: <a href="http://icwp.io/5s">Rename WordPress Login URL</a> bug</li>
4194
  <li><strong>(v.2)</strong> CHANGED: reCAPTCHA text usage corrected throughout plugin.</li>
4195
  <li><strong>(v.1)</strong> CHANGED: Removed the whole ‘wp-content’ directory from the <a
4196
- href="http://icwp.io/wpsf40">Core File Scanner</a> feature.</li>
4197
  <li><strong>(v.1)</strong> CHANGED: A WordPress filter to change the plugin badge text content (see FAQ)</li>
4198
  <li><strong>(v.1)</strong> CHANGED: Tweaked the plugin badge styling.</li>
4199
  <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>
4200
  <li><strong>(v.1)</strong> ADDED: In-plugin links to blogs and info articles for Google ReCaptcha and <a
4201
- href="http://icwp.io/wpsf43">Google Authenticator</a></li>
4202
  <li><strong>(v.0)</strong> NEW: WordPress Simple Firewall plugin has been re-branded and is called <strong>Shield</strong></li>
4203
- <li><strong>(v.0)</strong> ADDED: NEW feature - <a href="http://icwp.io/shld2">Google ReCaptcha</a> for Comment SPAM and Login protection.</li>
4204
  <li><strong>(v.0)</strong> ADDED: Support for this plugin is now Premium. Added Premium Support page that links to Helpdesk.</li>
4205
  <li><strong>(v.0)</strong> CHANGED: Refactor of comment spam code.</li>
4206
  <li><strong>(v.0)</strong> CHANGED: Core File Scanner now handles the odd Hungarian distribution.</li>
@@ -4208,8 +4208,8 @@ or service authenticates the request it will be honoured, whether anonymous or n
4208
  <p>= 4.17 Series =<br>
4209
  <em>Released: 17th February, 2016</em></p>
4210
  <ul>
4211
- <li><strong>(v.0)</strong> ADDED: NEW feature - <a href="http://icwp.io/wpsf43">Google Authenticator Login option</a>.</li>
4212
- <li><strong>(v.0)</strong> ADDED: <a href="http://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>
4213
  <li><strong>(v.0)</strong> ADDED: NEW - if you already have a logged-in session and you open the login screen, you’ll be provided with a link to go straight to the admin area.</li>
4214
  <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>
4215
  <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>
@@ -4220,16 +4220,16 @@ or service authenticates the request it will be honoured, whether anonymous or n
4220
  <p>= 4.16 Series =<br>
4221
  <em>Released: 20th January, 2016</em></p>
4222
  <ul>
4223
- <li><strong>(v.2)</strong> CHANGED: Further changes and improvements to the <a href="http://icwp.io/wpsf40">Core File Scanner</a>.</li>
4224
- <li><strong>(v.2)</strong> CHANGED: Improvements to the <a href="http://icwp.io/wpsf27">automatic black list system</a> for failed login attempts.</li>
4225
  <li><strong>(v.2)</strong> TRANSLATIONS: Turkish (100%)</li>
4226
- <li><strong>(v.1)</strong> CHANGED: Improved the contents of the <a href="http://icwp.io/wpsf40">Core File Scanner</a> notification email with links to original source files.</li>
4227
  <li><strong>(v.1)</strong> CHANGED: Now also excluding the /wp-content/languages/ directory since translations may update independently.</li>
4228
  <li><strong>(v.1)</strong> CHANGED: Handles the special case of <a
4229
  href="https://wordpress.org/support/topic/problem-with-checksum-hashes">old index.php files</a></li>
4230
- <li><strong>(v.0)</strong> ADDED: Feature: <a href="http://icwp.io/wpsf40">Automatically scans WordPress Core files</a> and detects alterations from the default WordPress Core File data</li>
4231
  <li><strong>(v.0)</strong> ADDED: Feature: to automatically attempt to repair/replace WordPress Core files that are discovered which have been altered.</li>
4232
- <li><strong>(v.0)</strong> ADDED: Option to toggle the <a href="http://icwp.io/wpsf41">Plugin Vulnerabilities cron</a>.</li>
4233
  <li><strong>(v.0)</strong> ADDED: Two-Factor Authentication links now honour the WordPress ‘redirect_to’ parameter.</li>
4234
  </ul>
4235
  <p>= 4.15 Series =<br>
@@ -4250,7 +4250,7 @@ or service authenticates the request it will be honoured, whether anonymous or n
4250
  <li><strong>(v.1)</strong> ADDED: Added ‘Unique Plugin Installation ID’ to be utilized in the future.</li>
4251
  <li><strong>(v.1)</strong> FIXED: WordPress Comments bug where some comments didn’t pass through the SPAM filters in a certain scenario.</li>
4252
  <li><strong>(v.0)</strong> ADDED: <a
4253
- href="http://icwp.io/wpsf33">Custom Automatic Update Notifications Email</a> that runs separately to the in-built WordPress core notification email.</li>
4254
  <li><strong>(v.0)</strong> ADDED: Filter to remove the admin area IP address footer text</li>
4255
  <li><strong>(v.0)</strong> CHANGED: Added native support for PayPal return links - whitelisting “verify_sign” parameter.</li>
4256
  <li><strong>(v.0)</strong> CHANGED: Tweak patterns for matching on ‘WordPress terms’.</li>
@@ -4274,14 +4274,14 @@ or service authenticates the request it will be honoured, whether anonymous or n
4274
  <em>Released: 10th October, 2015</em></p>
4275
  <ul>
4276
  <li><strong>(v.0)</strong> NEW: Option to completely disable the XML-RPC system. <a
4277
- href="http://icwp.io/wpsf31">more info</a></li>
4278
  <li><strong>(v.0)</strong> CHANGED: Logged-in users are automatically forwarded to the WordPress admin only if they are Administrators.</li>
4279
  </ul>
4280
  <p>= 4.11 Series =<br>
4281
  <em>Released: 5th October, 2015</em></p>
4282
  <ul>
4283
  <li><strong>(v.0)</strong> NEW: Ability to now completely block the update/changing of certain WordPress site options. <a
4284
- href="http://icwp.io/wpsf30">more info</a></li>
4285
  <li><strong>(v.0)</strong> FIXED: Various small bugs with the IP Manager UI ajax.</li>
4286
  <li><strong>(v.0)</strong> FIXED: Uncaught PHP Exception when a site’s hosting isn’t properly configured to handle IPv6 addresses.</li>
4287
  <li><strong>(v.0)</strong> TRANSLATIONS: Danish - 57%, Czech - 100%, Finnish - 94%</li>
@@ -4339,11 +4339,11 @@ or service authenticates the request it will be honoured, whether anonymous or n
4339
  </li>
4340
  <li>
4341
  <p><strong>(v.1)</strong> ADDED: Ability to reset plugin options to default using ‘reset’ flag file. <a
4342
- href="http://icwp.io/wpsf28">more info</a></p>
4343
  </li>
4344
  <li>
4345
  <p><strong>(v.0)</strong> NEW FEATURE: ‘FABLE’ - <a
4346
- href="http://icwp.io/wpsf27">Fully Automatic Black Listing Engine</a>.</p>
4347
  </li>
4348
  </ul>
4349
  <p>Simply put, FABLE will automatically block all malicious traffic by IP, based on their activity. This Security Plugin will track malicious behaviour<br>
@@ -4376,7 +4376,7 @@ will outright block any access they have to your WordPress site.</p>
4376
  <li><strong>(v.8)</strong> FIX: Some server email programs can’t handle colons (:) in the email subject (because supporting all characters would be waaay too radical man).</li>
4377
  <li><strong>(v.8)</strong> ADDED: Function to better get the WordPress home URL to prevent interference from other plugins.</li>
4378
  <li><strong>(v.8)</strong> CHANGED: Updated Text For <a
4379
- href="http://icwp.io/6e">Author Scan Block</a> feature.</li>
4380
  <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>
4381
  <li><strong>(v.6)</strong> ADDED: New Option: prevent detection of usernames using the ?author=N query. (location under section: Lockdown -&gt; Obscurity)</li>
4382
  <li><strong>(v.6)</strong> FIXED: Infinite redirect loop logic prevents redirect for rejected comment SPAM that’s posted in bulk. This results in email notifications for spam comments.</li>
@@ -4393,7 +4393,7 @@ will outright block any access they have to your WordPress site.</p>
4393
  <li><strong>(v.2)</strong> FIX: Work around a WordPress inline plugin update Javascript bug.</li>
4394
  <li><strong>(v.1)</strong> FIX: Fix syntax support for earlier versions of PHP.</li>
4395
  <li><strong>(v.0)</strong> FEATURE: Plugin Vulnerabilities Detection: If you’re running plugins with known vulnerabilities you will be warned - <a
4396
- href="http://icwp.io/wpsf22">more info</a></li>
4397
  </ul>
4398
  <p>= 4.8 Series =<br>
4399
  <em>Released: 21st June, 2015</em></p>
@@ -4435,8 +4435,8 @@ will outright block any access they have to your WordPress site.</p>
4435
  <em>Released: 10th April, 2015</em></p>
4436
  <ul>
4437
  <li><strong>(v.3)</strong> SECURITY: Added protection against XSS vulnerability in WordPress comments. <a
4438
- href="http://icwp.io/63">Learn More</a> - Note: This is not a vulnerability with the Firewall plugin.</li>
4439
- <li><strong>(v.3)</strong> SECURITY: Added extra precautions to WordPress URL redirects. <a href="http://icwp.io/64">Learn More</a>.</li>
4440
  <li><strong>(v.3)</strong> TRANSLATIONS: Russian (70%), Czech (67%)</li>
4441
  <li><strong>(v.2)</strong> FIX: Bug with the database table verification logic.</li>
4442
  <li><strong>(v.2)</strong> TRANSLATIONS: Russian (New- 54%), Romanian (100%), Turkish (89%), Czech (53%)</li>
@@ -4445,7 +4445,7 @@ will outright block any access they have to your WordPress site.</p>
4445
  <li><strong>(v.1)</strong> UPDATED: Updated Czech(41%) and Spanish (60%) translations</li>
4446
  <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>
4447
  <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
4448
- href="http://icwp.io/5x">Learn More</a></li>
4449
  <li><strong>(v.0)</strong> UPDATED: Updated Czech(38%) translations</li>
4450
  </ul>
4451
  <p>= 4.5 Series =<br>
@@ -4481,7 +4481,7 @@ will outright block any access they have to your WordPress site.</p>
4481
  <li><strong>(v.5)</strong> CHANGED: Streamlined the detection of whitelisting and added in-plugin notification if <strong>you</strong> are whitelisted</li>
4482
  <li><strong>(v.4)</strong> FIXES: Work around for cases where PHP can’t successfully run parse_url()</li>
4483
  <li><strong>(v.2)</strong> IMPROVED: Refactoring for better code organisation</li>
4484
- <li>ADDED: New Feature - <a href="http://icwp.io/5s">Rename WP Login Page</a>.</li>
4485
  <li>ADDED: UI indicators on whether plugins will be automatically updated in the plugins listing.</li>
4486
  <li>CHANGED: IP Address WhiteList is now global for the whole plugin, and can be accessed under the “Dashboard” area</li>
4487
  <li>IMPROVED: Firewall processing code is simplified and more efficient.</li>
@@ -4639,7 +4639,7 @@ will outright block any access they have to your WordPress site.</p>
4639
  <p>= 2.6.3 =</p>
4640
  <ul>
4641
  <li>ADDED: More in-line plugin links to help/blog resources</li>
4642
- <li>ENHANCED: <a href="http://icwp.io/5b">Admin Access Protection</a> is further enhanced in 3 ways:</li>
4643
  </ul>
4644
  <ol>
4645
  <li>More robust cookie values using MD5s</li>
@@ -4710,7 +4710,7 @@ will outright block any access they have to your WordPress site.</p>
4710
  <p>= 2.5.0 =</p>
4711
  <ul>
4712
  <li>FEATURE: Two-Factor Authenticated Login using <a
4713
- href="http://icwp.io/4i">Yubikey</a> One Time Passwords (OTP).</li>
4714
  </ul>
4715
  <p>= 2.4.3 =</p>
4716
  <ul>
@@ -4832,7 +4832,7 @@ entries and was forcing users to re-login every 24hrs.</li>
4832
  </ul>
4833
  <p>= 1.8.1 =</p>
4834
  <ul>
4835
- <li>ADDED: Feature- Access Key Restriction <a href="http://icwp.io/2s">more info</a>.</li>
4836
  <li>ADDED: Feature- WordPress Lockdown. Currently only provides 1 option, but more to come.</li>
4837
  </ul>
4838
  <p>= 1.7.3 =</p>
3821
  </style>
3822
  <h2>Changelog: Shield Security for WordPress</h2>
3823
  <p>= 6.5 Series =<br>
3824
+ <em>Released: 5th March, 2018</em> - <a href="https://icwp.io/bu">Release Notes</a></p>
3825
  <ul>
3826
+ <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>
3827
  <li><strong>(v.0)</strong> IMPROVED: Attempts to access the XML-RPC system when it’s disabled will now result in a transgression increment in the IP Black List</li>
3828
  <li><strong>(v.0)</strong> IMPROVED: Try to prevent black listing the server’s own public IP address where visitor IP address detection is not correctly configured.</li>
3829
  <li><strong>(v.0)</strong> ADDED: [<strong>PRO</strong>] Provisional support for not processing 2FA logins for Woocommerce Social Login plugin.</li>
3831
  <li><strong>(v.0)</strong> FIXED: A few small bugs</li>
3832
  </ul>
3833
  <p>= 6.4 Series =<br>
3834
+ <em>Released: 26th February, 2018</em> - <a href="https://icwp.io/br">Release Notes</a></p>
3835
  <ul>
3836
  <li><strong>(v.1-4)</strong> FIXED: Various Fixes</li>
3837
+ <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>
3838
+ <li><strong>(v.0)</strong> IMPROVED: Automatic updates for vulnerable plugins ignores <a href="https://icwp.io/bc">automatic updates delay setting</a></li>
3839
  <li><strong>(v.0)</strong> CHANGED: Email notifications for scanners will now link to the Wizard where possible, instead of listing files.</li>
3840
  </ul>
3841
  <p>= 6.3 Series =<br>
3842
+ <em>Released: 12th February, 2018</em> - <a href="https://icwp.io/bc">Release Notes</a></p>
3843
  <ul>
3844
  <li><strong>(v.3)</strong> FIXED: Bug with automatic updates delay setting</li>
3845
  <li><strong>(v.2)</strong> CHANGED: Changed a text that seems to cause servers to swallow-up emails. <a
3846
+ href="https://icwp.io/bi">See here for more reliable email</a></li>
3847
  <li><strong>(v.1)</strong> FIXED: Options page javascript to work around conflicts.</li>
3848
+ <li><strong>(v.0)</strong> ADDED: [<strong>PRO</strong>] <a href="https://icwp.io/bc">Automatic updates stability delay</a></li>
3849
+ <li><strong>(v.0)</strong> IMPROVED: Complete <a href="https://icwp.io/bd">plugin UI rebuild</a>, using the new Bootstrap 4.</li>
3850
  <li><strong>(v.0)</strong> FIXED: A few bugs with Google Authenticator.</li>
3851
  </ul>
3852
  <p>= 6.2 Series =<br>
3853
+ <em>Released: 31st January, 2018</em> - <a href="https://icwp.io/b6">Release Notes</a></p>
3854
  <ul>
3855
  <li><strong>(v.2)</strong> FIXED: Fix for IP Manager PHP error.</li>
3856
  <li><strong>(v.2)</strong> IMPROVED: Two-factor verification email.</li>
3863
  <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>
3864
  </ul>
3865
  <p>= 6.1 Series =<br>
3866
+ <em>Released: 15th January, 2018</em> - <a href="https://icwp.io/ay">Release Notes</a></p>
3867
  <ul>
3868
  <li><strong>(v.1)</strong> FIXED: Verify link missing from the two-factor authentication verification email.</li>
3869
  <li><strong>(v.0)</strong> ADDED: 3x more Shield Wizards: Multi-factor Authentication, Core File Scanning, Unrecognised File Scanning.</li>
3876
  <em>Released: 18th December, 2017</em></p>
3877
  <ul>
3878
  <li><strong>(v.0)</strong> ADDED: All-new Shield Welcome and Setup Wizard - more helpful guided wizards to come.</li>
3879
+ <li><strong>(v.0)</strong> ADDED: [<strong>PRO</strong>] <a href="https://icwp.io/at">Shield options import and export</a></li>
3880
  <li><strong>(v.0)</strong> ADDED: [<strong>PRO</strong>] In conjunction with import/export - Shield Security Network: automated options syncing.</li>
3881
+ <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>
3882
  </ul>
3883
  <p>= 5.20 Series =<br>
3884
  <em>Released: 11th December, 2017</em></p>
3957
  <li><strong>(v.2)</strong> IMPROVEMENTS: Small adjustment to handling of Shield User sessions in conjunction with WordPress sessions.</li>
3958
  <li><strong>(v.2)</strong> FIX: Restore display of help links for options.</li>
3959
  <li><strong>(v.1)</strong> FIX: PHP 5.2 incompatibility.</li>
3960
+ <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>
3961
  <li><strong>(v.0)</strong> ADDED: Option to provide custom list of files to be excluded from the <a
3962
+ href="https://icwp.io/94">Unrecognised File Scanner</a>.</li>
3963
  </ul>
3964
  <p>= 5.12 Series =<br>
3965
  <em>Released: 3rd August, 2017</em></p>
3966
  <ul>
3967
+ <li><strong>(v.2)</strong> IMPROVEMENTS: Improved support for Windows IIS hosting for <a href="https://icwp.io/94">Unrecognised File Scanner</a></li>
3968
  <li><strong>(v.2)</strong> CHANGED: Removed the email-based 2FA automatic login link.</li>
3969
  <li><strong>(v.2)</strong> FIX: Potential bug with Shield not recognising plugin configuration updates and not rebuilding options accordingly.</li>
3970
+ <li><strong>(v.1)</strong> ADDED: A few more exclusions for the <a href="https://icwp.io/94">Unrecognised File Scanner</a></li>
3971
  <li><strong>(v.1)</strong> FIX: Fix for Fatal error.</li>
3972
+ <li><strong>(v.0)</strong> ADDED: <a href="https://icwp.io/94">Unrecognised File Scanner</a> release. Automatically detect and delete<br>
3973
  any files present in core WordPress directories that aren’t part of your core installation.</li>
3974
  <li><strong>(v.0)</strong> ADDED: Updated Firewall rules for SQL under the ‘Aggressive’ rule set.</li>
3975
  </ul>
3977
  <em>Released: 26th July, 2017</em></p>
3978
  <ul>
3979
  <li><strong>(v.1)</strong> FIX: JSON syntax</li>
3980
+ <li><strong>(v.0)</strong> IMPROVEMENTS: Final preparation for <a href="https://icwp.io/83">Shield Central</a> release.</li>
3981
  </ul>
3982
  <p>= 5.10 Series =<br>
3983
  <em>Released: 19th June, 2017</em></p>
3984
  <ul>
3985
  <li><strong>(v.2)</strong> FIXED: Fatal error with GASP + Password Reset.</li>
3986
  <li><strong>(v.2)</strong> FIXED: Fatal error with failing reCAPTCHA HTTP requests.</li>
3987
+ <li><strong>(v.1)</strong> IMPROVEMENTS: Further preparation for <a href="https://icwp.io/83">Shield Central</a> release.</li>
3988
  <li><strong>(v.0)</strong> ADDED: More in-depth reporting and statistics gathering - options for reports will be made available<br>
3989
  in a later release.</li>
3990
  </ul>
4007
  <li><strong>(v.2)</strong> CHANGE: Changed timeout for two-factor authentication email to 5 minutes to account for slower email-sending providers.</li>
4008
  <li><strong>(v.2)</strong> CHANGE: Added further clarification to the Login Notification email indicating that two-factor authentication was pending.</li>
4009
  <li><strong>(v.1)</strong> FIXED: Fixed a couple of bugs with the Login Authentication Portal, for certain edge cases.</li>
4010
+ <li><strong>(v.0)</strong> CHANGE: Major overhaul of <a href="https://icwp.io/87">Two-Factor / Multi-Factor Login Authentication</a>.</li>
4011
+ <li><strong>(v.0)</strong> CHANGE: <a href="https://icwp.io/86">Introduction of Login Authentication Portal</a> for improved Multi-Factor Authentication.</li>
4012
  <li><strong>(v.0)</strong> ADDED: Option to choose between two-factor or multi-factor login authentication.</li>
4013
  <li><strong>(v.0)</strong> ADDED: Administrators can remove Google Authenticator from another user’s profile.</li>
4014
  <li><strong>(v.0)</strong> ADDED: When Security Admin is active, only Security Admins may remove Google Authenticator from other admins.</li>
4016
  <li><strong>(v.0)</strong> CHANGE: Email-based login authentication no longer uses a separate database table.</li>
4017
  <li><strong>(v.0)</strong> FIXED: Core file scanning now adequately handles Windows/Unix new lines during scan.</li>
4018
  <li><strong>(v.0)</strong> FIXED: Certain crons weren’t setup correctly.</li>
4019
+ <li><strong>(v.0)</strong> IMPROVEMENTS: Further preparation for <a href="https://icwp.io/83">Shield Central</a> release.</li>
4020
  </ul>
4021
  <p>= 5.7 Series =</p>
4022
  <ul>
4023
  <li><strong>(v.3)</strong> FIXED: Attempt to improve the Google Authenticator flow for more reliable activation.</li>
4024
  <li><strong>(v.2)</strong> IMPROVEMENTS: More admin notices when saving Google Authenticator settings.</li>
4025
+ <li><strong>(v.2)</strong> IMPROVEMENTS: Further preparation for <a href="https://icwp.io/83">Shield Central</a> release.</li>
4026
  <li><strong>(v.1)</strong> Skipped</li>
4027
  <li><strong>(v.0)</strong> ADDED: Shortcode for displaying plugin badge in pages/posts.</li>
4028
  <li><strong>(v.0)</strong> CHANGE: Enabled JS eval() for the Content Security Policy by default.</li>
4029
  <li><strong>(v.0)</strong> IMPROVEMENTS: Replace YAML configuration files with JSON.</li>
4030
+ <li><strong>(v.0)</strong> IMPROVEMENTS: Preparation for <a href="https://icwp.io/83">Shield Central</a> release.</li>
4031
  <li><strong>(v.0)</strong> IMPROVEMENTS: Security Admin notices are more refined and optimized.</li>
4032
  <li><strong>(v.0)</strong> IMPROVEMENTS: Removed unnecessary files/code.</li>
4033
  </ul>
4094
  </li>
4095
  <li>
4096
  <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
4097
+ href="https://icwp.io/7j">more info</a></p>
4098
  </li>
4099
  <li>
4100
  <p><strong>(v.1)</strong> IMPROVED: Better XMLRPC Lockdown - prevents ANY XMLRPC command processing.</p>
4153
  <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>
4154
  <li><strong>(v.2)</strong> ADDED: Exclusion for Swedish license files that don’t exist in the SVN repo.</li>
4155
  <li><strong>(v.2)</strong> ADDED: Parameter exclusion for reCAPTCHA.</li>
4156
+ <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>
4157
  <li><strong>(v.1)</strong> FIXED: Nasty bug that caused an infinite loop bug in some configurations.</li>
4158
  <li><strong>(v.0)</strong> ADDED: Per-site plugin statistics gathering - summary display on admin dashboard.</li>
4159
  <li><strong>(v.0)</strong> ADDED: HTML class to the “I’m a human” checkbox field.</li>
4164
  </ul>
4165
  <p>= 5.3 Series =</p>
4166
  <ul>
4167
+ <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>
4168
  <li><strong>(v.2)</strong> FIXED: Human Comment SPAM Feature didn’t fire under certain circumstances.</li>
4169
  <li><strong>(v.2)</strong> FIXED: Fixed parsing of Human Comment SPAM dictionary words.</li>
4170
  <li><strong>(v.1)</strong> TRANSLATIONS: Dutch (32%)</li>
4171
+ <li><strong>(v.0)</strong> ADDED: New Feature - <a href="https://icwp.io/7b">HTTP Security Headers</a>.</li>
4172
  <li><strong>(v.0)</strong> FIXED: Prevent renaming WP Login to “/login”</li>
4173
  </ul>
4174
  <p>= 5.2 Series =</p>
4177
  <li><strong>(v.0)</strong> CHANGED: Logic for brute force login checking is improved - they all run before username/password checking</li>
4178
  <li><strong>(v.0)</strong> FIXED: Certain older versions of PHP don’t like combined IPv4 and IPv6 filter flags</li>
4179
  <li><strong>(v.0)</strong> FIXED: Google reCAPTCHA for WordPress sites that have restrictive settings for sockets etc.</li>
4180
+ <li><strong>(v.0)</strong> REMOVED: <a href="https://icwp.io/75">Plugin vulnerabilities scanner</a>. It’s out-of-date and unsuitable.</li>
4181
  </ul>
4182
  <p>= 5.1 Series =</p>
4183
  <ul>
4190
  <p>= 5.0 Series =</p>
4191
  <ul>
4192
  <li><strong>(v.3)</strong> FIXED: Issue with setting session cookies with PHP 7</li>
4193
+ <li><strong>(v.2)</strong> FIXED: <a href="https://icwp.io/5s">Rename WordPress Login URL</a> bug</li>
4194
  <li><strong>(v.2)</strong> CHANGED: reCAPTCHA text usage corrected throughout plugin.</li>
4195
  <li><strong>(v.1)</strong> CHANGED: Removed the whole ‘wp-content’ directory from the <a
4196
+ href="https://icwp.io/wpsf40">Core File Scanner</a> feature.</li>
4197
  <li><strong>(v.1)</strong> CHANGED: A WordPress filter to change the plugin badge text content (see FAQ)</li>
4198
  <li><strong>(v.1)</strong> CHANGED: Tweaked the plugin badge styling.</li>
4199
  <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>
4200
  <li><strong>(v.1)</strong> ADDED: In-plugin links to blogs and info articles for Google ReCaptcha and <a
4201
+ href="https://icwp.io/wpsf43">Google Authenticator</a></li>
4202
  <li><strong>(v.0)</strong> NEW: WordPress Simple Firewall plugin has been re-branded and is called <strong>Shield</strong></li>
4203
+ <li><strong>(v.0)</strong> ADDED: NEW feature - <a href="https://icwp.io/shld2">Google ReCaptcha</a> for Comment SPAM and Login protection.</li>
4204
  <li><strong>(v.0)</strong> ADDED: Support for this plugin is now Premium. Added Premium Support page that links to Helpdesk.</li>
4205
  <li><strong>(v.0)</strong> CHANGED: Refactor of comment spam code.</li>
4206
  <li><strong>(v.0)</strong> CHANGED: Core File Scanner now handles the odd Hungarian distribution.</li>
4208
  <p>= 4.17 Series =<br>
4209
  <em>Released: 17th February, 2016</em></p>
4210
  <ul>
4211
+ <li><strong>(v.0)</strong> ADDED: NEW feature - <a href="https://icwp.io/wpsf43">Google Authenticator Login option</a>.</li>
4212
+ <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>
4213
  <li><strong>(v.0)</strong> ADDED: NEW - if you already have a logged-in session and you open the login screen, you’ll be provided with a link to go straight to the admin area.</li>
4214
  <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>
4215
  <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>
4220
  <p>= 4.16 Series =<br>
4221
  <em>Released: 20th January, 2016</em></p>
4222
  <ul>
4223
+ <li><strong>(v.2)</strong> CHANGED: Further changes and improvements to the <a href="https://icwp.io/wpsf40">Core File Scanner</a>.</li>
4224
+ <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>
4225
  <li><strong>(v.2)</strong> TRANSLATIONS: Turkish (100%)</li>
4226
+ <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>
4227
  <li><strong>(v.1)</strong> CHANGED: Now also excluding the /wp-content/languages/ directory since translations may update independently.</li>
4228
  <li><strong>(v.1)</strong> CHANGED: Handles the special case of <a
4229
  href="https://wordpress.org/support/topic/problem-with-checksum-hashes">old index.php files</a></li>
4230
+ <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>
4231
  <li><strong>(v.0)</strong> ADDED: Feature: to automatically attempt to repair/replace WordPress Core files that are discovered which have been altered.</li>
4232
+ <li><strong>(v.0)</strong> ADDED: Option to toggle the <a href="https://icwp.io/wpsf41">Plugin Vulnerabilities cron</a>.</li>
4233
  <li><strong>(v.0)</strong> ADDED: Two-Factor Authentication links now honour the WordPress ‘redirect_to’ parameter.</li>
4234
  </ul>
4235
  <p>= 4.15 Series =<br>
4250
  <li><strong>(v.1)</strong> ADDED: Added ‘Unique Plugin Installation ID’ to be utilized in the future.</li>
4251
  <li><strong>(v.1)</strong> FIXED: WordPress Comments bug where some comments didn’t pass through the SPAM filters in a certain scenario.</li>
4252
  <li><strong>(v.0)</strong> ADDED: <a
4253
+ href="https://icwp.io/wpsf33">Custom Automatic Update Notifications Email</a> that runs separately to the in-built WordPress core notification email.</li>
4254
  <li><strong>(v.0)</strong> ADDED: Filter to remove the admin area IP address footer text</li>
4255
  <li><strong>(v.0)</strong> CHANGED: Added native support for PayPal return links - whitelisting “verify_sign” parameter.</li>
4256
  <li><strong>(v.0)</strong> CHANGED: Tweak patterns for matching on ‘WordPress terms’.</li>
4274
  <em>Released: 10th October, 2015</em></p>
4275
  <ul>
4276
  <li><strong>(v.0)</strong> NEW: Option to completely disable the XML-RPC system. <a
4277
+ href="https://icwp.io/wpsf31">more info</a></li>
4278
  <li><strong>(v.0)</strong> CHANGED: Logged-in users are automatically forwarded to the WordPress admin only if they are Administrators.</li>
4279
  </ul>
4280
  <p>= 4.11 Series =<br>
4281
  <em>Released: 5th October, 2015</em></p>
4282
  <ul>
4283
  <li><strong>(v.0)</strong> NEW: Ability to now completely block the update/changing of certain WordPress site options. <a
4284
+ href="https://icwp.io/wpsf30">more info</a></li>
4285
  <li><strong>(v.0)</strong> FIXED: Various small bugs with the IP Manager UI ajax.</li>
4286
  <li><strong>(v.0)</strong> FIXED: Uncaught PHP Exception when a site’s hosting isn’t properly configured to handle IPv6 addresses.</li>
4287
  <li><strong>(v.0)</strong> TRANSLATIONS: Danish - 57%, Czech - 100%, Finnish - 94%</li>
4339
  </li>
4340
  <li>
4341
  <p><strong>(v.1)</strong> ADDED: Ability to reset plugin options to default using ‘reset’ flag file. <a
4342
+ href="https://icwp.io/wpsf28">more info</a></p>
4343
  </li>
4344
  <li>
4345
  <p><strong>(v.0)</strong> NEW FEATURE: ‘FABLE’ - <a
4346
+ href="https://icwp.io/wpsf27">Fully Automatic Black Listing Engine</a>.</p>
4347
  </li>
4348
  </ul>
4349
  <p>Simply put, FABLE will automatically block all malicious traffic by IP, based on their activity. This Security Plugin will track malicious behaviour<br>
4376
  <li><strong>(v.8)</strong> FIX: Some server email programs can’t handle colons (:) in the email subject (because supporting all characters would be waaay too radical man).</li>
4377
  <li><strong>(v.8)</strong> ADDED: Function to better get the WordPress home URL to prevent interference from other plugins.</li>
4378
  <li><strong>(v.8)</strong> CHANGED: Updated Text For <a
4379
+ href="https://icwp.io/6e">Author Scan Block</a> feature.</li>
4380
  <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>
4381
  <li><strong>(v.6)</strong> ADDED: New Option: prevent detection of usernames using the ?author=N query. (location under section: Lockdown -&gt; Obscurity)</li>
4382
  <li><strong>(v.6)</strong> FIXED: Infinite redirect loop logic prevents redirect for rejected comment SPAM that’s posted in bulk. This results in email notifications for spam comments.</li>
4393
  <li><strong>(v.2)</strong> FIX: Work around a WordPress inline plugin update Javascript bug.</li>
4394
  <li><strong>(v.1)</strong> FIX: Fix syntax support for earlier versions of PHP.</li>
4395
  <li><strong>(v.0)</strong> FEATURE: Plugin Vulnerabilities Detection: If you’re running plugins with known vulnerabilities you will be warned - <a
4396
+ href="https://icwp.io/wpsf22">more info</a></li>
4397
  </ul>
4398
  <p>= 4.8 Series =<br>
4399
  <em>Released: 21st June, 2015</em></p>
4435
  <em>Released: 10th April, 2015</em></p>
4436
  <ul>
4437
  <li><strong>(v.3)</strong> SECURITY: Added protection against XSS vulnerability in WordPress comments. <a
4438
+ href="https://icwp.io/63">Learn More</a> - Note: This is not a vulnerability with the Firewall plugin.</li>
4439
+ <li><strong>(v.3)</strong> SECURITY: Added extra precautions to WordPress URL redirects. <a href="https://icwp.io/64">Learn More</a>.</li>
4440
  <li><strong>(v.3)</strong> TRANSLATIONS: Russian (70%), Czech (67%)</li>
4441
  <li><strong>(v.2)</strong> FIX: Bug with the database table verification logic.</li>
4442
  <li><strong>(v.2)</strong> TRANSLATIONS: Russian (New- 54%), Romanian (100%), Turkish (89%), Czech (53%)</li>
4445
  <li><strong>(v.1)</strong> UPDATED: Updated Czech(41%) and Spanish (60%) translations</li>
4446
  <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>
4447
  <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
4448
+ href="https://icwp.io/5x">Learn More</a></li>
4449
  <li><strong>(v.0)</strong> UPDATED: Updated Czech(38%) translations</li>
4450
  </ul>
4451
  <p>= 4.5 Series =<br>
4481
  <li><strong>(v.5)</strong> CHANGED: Streamlined the detection of whitelisting and added in-plugin notification if <strong>you</strong> are whitelisted</li>
4482
  <li><strong>(v.4)</strong> FIXES: Work around for cases where PHP can’t successfully run parse_url()</li>
4483
  <li><strong>(v.2)</strong> IMPROVED: Refactoring for better code organisation</li>
4484
+ <li>ADDED: New Feature - <a href="https://icwp.io/5s">Rename WP Login Page</a>.</li>
4485
  <li>ADDED: UI indicators on whether plugins will be automatically updated in the plugins listing.</li>
4486
  <li>CHANGED: IP Address WhiteList is now global for the whole plugin, and can be accessed under the “Dashboard” area</li>
4487
  <li>IMPROVED: Firewall processing code is simplified and more efficient.</li>
4639
  <p>= 2.6.3 =</p>
4640
  <ul>
4641
  <li>ADDED: More in-line plugin links to help/blog resources</li>
4642
+ <li>ENHANCED: <a href="https://icwp.io/5b">Admin Access Protection</a> is further enhanced in 3 ways:</li>
4643
  </ul>
4644
  <ol>
4645
  <li>More robust cookie values using MD5s</li>
4710
  <p>= 2.5.0 =</p>
4711
  <ul>
4712
  <li>FEATURE: Two-Factor Authenticated Login using <a
4713
+ href="https://icwp.io/4i">Yubikey</a> One Time Passwords (OTP).</li>
4714
  </ul>
4715
  <p>= 2.4.3 =</p>
4716
  <ul>
4832
  </ul>
4833
  <p>= 1.8.1 =</p>
4834
  <ul>
4835
+ <li>ADDED: Feature- Access Key Restriction <a href="https://icwp.io/2s">more info</a>.</li>
4836
  <li>ADDED: Feature- WordPress Lockdown. Currently only provides 1 option, but more to come.</li>
4837
  </ul>
4838
  <p>= 1.7.3 =</p>
icwp-plugin-controller.php CHANGED
@@ -394,7 +394,7 @@ class ICWP_WPSF_Plugin_Controller extends ICWP_WPSF_Foundation {
394
  * @uses die()
395
  */
396
  private function downloadOptionsExport() {
397
- $oDp = $this->loadDataProcessor();
398
  if ( $oDp->query( 'icwp_shield_export' ) == 1 ) {
399
  $aExportOptions = apply_filters( $this->prefix( 'gather_options_for_export' ), array() );
400
  if ( !empty( $aExportOptions ) && is_array( $aExportOptions ) ) {
@@ -492,7 +492,7 @@ class ICWP_WPSF_Plugin_Controller extends ICWP_WPSF_Foundation {
492
 
493
  if ( $this->getPluginSpec_Menu( 'has_submenu' ) ) {
494
 
495
- $aPluginMenuItems = apply_filters( $this->prefix( 'filter_plugin_submenu_items' ), array() );
496
  if ( !empty( $aPluginMenuItems ) ) {
497
  foreach ( $aPluginMenuItems as $sMenuTitle => $aMenu ) {
498
  list( $sMenuItemText, $sMenuItemId, $aMenuCallBack, $bShowItem ) = $aMenu;
@@ -714,7 +714,7 @@ class ICWP_WPSF_Plugin_Controller extends ICWP_WPSF_Foundation {
714
  $oConOptions->update_first_detected = array();
715
  }
716
  if ( !isset( $oConOptions->update_first_detected[ $sNewVersion ] ) ) {
717
- $oConOptions->update_first_detected[ $sNewVersion ] = $this->loadDataProcessor()->time();
718
  }
719
 
720
  // a bit of cleanup to remove the old-style entries which would gather foreva-eva
@@ -768,18 +768,12 @@ class ICWP_WPSF_Plugin_Controller extends ICWP_WPSF_Foundation {
768
  $sNewVersion = $oWpPlugins->getUpdateNewVersion( $sFile );
769
  if ( !empty( $sNewVersion ) ) {
770
  $nFirstDetected = isset( $oConOptions->update_first_detected[ $sNewVersion ] ) ? $oConOptions->update_first_detected[ $sNewVersion ] : 0;
771
- $nTimeUpdateAvailable = $this->loadDataProcessor()->time() - $nFirstDetected;
772
  $bDoAutoUpdate = ( $nFirstDetected > 0 && ( $nTimeUpdateAvailable > DAY_IN_SECONDS*$nAutoupdateDays ) );
773
  }
774
  break;
775
 
776
  case 'pass' :
777
- // Add block to version 6.0 if PHP < 5.3
778
- $sNewVersion = $oWpPlugins->getUpdateNewVersion( $sFile );
779
- if ( version_compare( $sNewVersion, '6.0.0', '>=' ) && !$this->loadDataProcessor()
780
- ->getPhpVersionIsAtLeast( '5.3.0' ) ) {
781
- $bDoAutoUpdate = false;
782
- }
783
  break;
784
 
785
  default:
@@ -814,7 +808,18 @@ class ICWP_WPSF_Plugin_Controller extends ICWP_WPSF_Foundation {
814
  * @return array
815
  */
816
  public function getPluginLabels() {
817
- return array_map( 'stripslashes', apply_filters( $this->prefix( 'plugin_labels' ), $this->getPluginSpec_Labels() ) );
 
 
 
 
 
 
 
 
 
 
 
818
  }
819
 
820
  /**
@@ -966,13 +971,6 @@ class ICWP_WPSF_Plugin_Controller extends ICWP_WPSF_Foundation {
966
  protected function getPluginSpec_Labels( $sKey = '' ) {
967
  $oConOptions = $this->getPluginControllerOptions();
968
  $aLabels = isset( $oConOptions->plugin_spec[ 'labels' ] ) ? $oConOptions->plugin_spec[ 'labels' ] : array();
969
- //Prep the icon urls
970
- if ( !empty( $aLabels[ 'icon_url_16x16' ] ) ) {
971
- $aLabels[ 'icon_url_16x16' ] = $this->getPluginUrl_Image( $aLabels[ 'icon_url_16x16' ] );
972
- }
973
- if ( !empty( $aLabels[ 'icon_url_32x32' ] ) ) {
974
- $aLabels[ 'icon_url_32x32' ] = $this->getPluginUrl_Image( $aLabels[ 'icon_url_32x32' ] );
975
- }
976
 
977
  if ( empty( $sKey ) ) {
978
  return $aLabels;
@@ -1108,7 +1106,7 @@ class ICWP_WPSF_Plugin_Controller extends ICWP_WPSF_Foundation {
1108
 
1109
  $aFormSubmitOptions = array( 'plugin_form_submit', 'icwp_link_action' );
1110
 
1111
- $oDp = $this->loadDataProcessor();
1112
  foreach ( $aFormSubmitOptions as $sOption ) {
1113
  if ( !is_null( $oDp->request( $sOption, false ) ) ) {
1114
  return true;
@@ -1445,6 +1443,13 @@ class ICWP_WPSF_Plugin_Controller extends ICWP_WPSF_Foundation {
1445
  return (bool)$this->getPluginSpec_Property( 'enable_premium' );
1446
  }
1447
 
 
 
 
 
 
 
 
1448
  /**
1449
  */
1450
  protected function saveCurrentPluginControllerOptions() {
@@ -1527,7 +1532,7 @@ class ICWP_WPSF_Plugin_Controller extends ICWP_WPSF_Foundation {
1527
  */
1528
  public function getUniqueRequestId( $bSetSessionIfNeeded = true ) {
1529
  if ( !isset( self::$sRequestId ) ) {
1530
- $oDp = $this->loadDataProcessor();
1531
  self::$sRequestId = md5( $this->getSessionId( $bSetSessionIfNeeded ).$oDp->loadIpService()
1532
  ->getRequestIp().$oDp->time() );
1533
  }
394
  * @uses die()
395
  */
396
  private function downloadOptionsExport() {
397
+ $oDp = $this->loadDP();
398
  if ( $oDp->query( 'icwp_shield_export' ) == 1 ) {
399
  $aExportOptions = apply_filters( $this->prefix( 'gather_options_for_export' ), array() );
400
  if ( !empty( $aExportOptions ) && is_array( $aExportOptions ) ) {
492
 
493
  if ( $this->getPluginSpec_Menu( 'has_submenu' ) ) {
494
 
495
+ $aPluginMenuItems = apply_filters( $this->prefix( 'submenu_items' ), array() );
496
  if ( !empty( $aPluginMenuItems ) ) {
497
  foreach ( $aPluginMenuItems as $sMenuTitle => $aMenu ) {
498
  list( $sMenuItemText, $sMenuItemId, $aMenuCallBack, $bShowItem ) = $aMenu;
714
  $oConOptions->update_first_detected = array();
715
  }
716
  if ( !isset( $oConOptions->update_first_detected[ $sNewVersion ] ) ) {
717
+ $oConOptions->update_first_detected[ $sNewVersion ] = $this->loadDP()->time();
718
  }
719
 
720
  // a bit of cleanup to remove the old-style entries which would gather foreva-eva
768
  $sNewVersion = $oWpPlugins->getUpdateNewVersion( $sFile );
769
  if ( !empty( $sNewVersion ) ) {
770
  $nFirstDetected = isset( $oConOptions->update_first_detected[ $sNewVersion ] ) ? $oConOptions->update_first_detected[ $sNewVersion ] : 0;
771
+ $nTimeUpdateAvailable = $this->loadDP()->time() - $nFirstDetected;
772
  $bDoAutoUpdate = ( $nFirstDetected > 0 && ( $nTimeUpdateAvailable > DAY_IN_SECONDS*$nAutoupdateDays ) );
773
  }
774
  break;
775
 
776
  case 'pass' :
 
 
 
 
 
 
777
  break;
778
 
779
  default:
808
  * @return array
809
  */
810
  public function getPluginLabels() {
811
+
812
+ $aLabels = array_map( 'stripslashes', apply_filters( $this->prefix( 'plugin_labels' ), $this->getPluginSpec_Labels() ) );
813
+
814
+ $oDP = $this->loadDP();
815
+ foreach ( array( '16x16', '32x32', '128x128' ) as $sSize ) {
816
+ $sKey = 'icon_url_'.$sSize;
817
+ if ( !empty( $aLabels[ $sKey ] ) && !$oDP->validUrl( $aLabels[ $sKey ] ) ) {
818
+ $aLabels[ $sKey ] = $this->getPluginUrl_Image( $aLabels[ $sKey ] );
819
+ }
820
+ }
821
+
822
+ return $aLabels;
823
  }
824
 
825
  /**
971
  protected function getPluginSpec_Labels( $sKey = '' ) {
972
  $oConOptions = $this->getPluginControllerOptions();
973
  $aLabels = isset( $oConOptions->plugin_spec[ 'labels' ] ) ? $oConOptions->plugin_spec[ 'labels' ] : array();
 
 
 
 
 
 
 
974
 
975
  if ( empty( $sKey ) ) {
976
  return $aLabels;
1106
 
1107
  $aFormSubmitOptions = array( 'plugin_form_submit', 'icwp_link_action' );
1108
 
1109
+ $oDp = $this->loadDP();
1110
  foreach ( $aFormSubmitOptions as $sOption ) {
1111
  if ( !is_null( $oDp->request( $sOption, false ) ) ) {
1112
  return true;
1443
  return (bool)$this->getPluginSpec_Property( 'enable_premium' );
1444
  }
1445
 
1446
+ /**
1447
+ * @return bool
1448
+ */
1449
+ public function isRelabelled() {
1450
+ return apply_filters( $this->prefix( 'is_relabelled' ), false );
1451
+ }
1452
+
1453
  /**
1454
  */
1455
  protected function saveCurrentPluginControllerOptions() {
1532
  */
1533
  public function getUniqueRequestId( $bSetSessionIfNeeded = true ) {
1534
  if ( !isset( self::$sRequestId ) ) {
1535
+ $oDp = $this->loadDP();
1536
  self::$sRequestId = md5( $this->getSessionId( $bSetSessionIfNeeded ).$oDp->loadIpService()
1537
  ->getRequestIp().$oDp->time() );
1538
  }
icwp-wpsf.php CHANGED
@@ -1,13 +1,13 @@
1
  <?php
2
  /*
3
  * Plugin Name: Shield Security
4
- * Plugin URI: http://icwp.io/2f
5
  * Description: Powerful, Easy-To-Use #1 Rated WordPress Security System
6
- * Version: 6.7.2
7
  * Text Domain: wp-simple-firewall
8
  * Domain Path: /languages/
9
  * Author: One Dollar Plugin
10
- * Author URI: http://icwp.io/bv
11
  */
12
 
13
  /**
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: 6.8.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
  /**
init.php CHANGED
@@ -39,8 +39,12 @@ class ICWP_Wordpress_Simple_Firewall extends ICWP_WPSF_Foundation {
39
  return self::$oPluginController;
40
  }
41
 
 
 
 
 
42
  public function getPluginsListUpdateMessage( $sMessage ) {
43
- return _wpsf__( 'Upgrade Now To Keep Your Firewall Up-To-Date With The Latest Features.' );
44
  }
45
 
46
  /**
39
  return self::$oPluginController;
40
  }
41
 
42
+ /**
43
+ * @param string $sMessage
44
+ * @return string
45
+ */
46
  public function getPluginsListUpdateMessage( $sMessage ) {
47
+ return _wpsf__( 'Upgrade Now To Keep Your Security Up-To-Date With The Latest Features.' );
48
  }
49
 
50
  /**
languages/wp-simple-firewall-nl_NL.mo CHANGED
Binary file
plugin-spec.php CHANGED
@@ -1,7 +1,7 @@
1
  {
2
  "properties": {
3
- "version": "6.7.2",
4
- "release_timestamp": 1527681600,
5
  "slug_parent": "icwp",
6
  "slug_plugin": "wpsf",
7
  "human_name": "Shield",
@@ -67,15 +67,16 @@
67
  "Title": "Shield Security",
68
  "Author": "One Dollar Plugin",
69
  "AuthorName": "One Dollar Plugin",
70
- "PluginURI": "http://icwp.io/2f",
71
- "AuthorURI": "http://icwp.io/bv",
72
  "icon_url_16x16": "pluginlogo_16x16.png",
73
- "icon_url_32x32": "pluginlogo_32x32.png"
 
74
  },
75
  "plugin_meta": [
76
  {
77
  "name": "5&#10025; Rate This Plugin",
78
- "href": "http://icwp.io/wpsf29"
79
  }
80
  ],
81
  "action_links": {
1
  {
2
  "properties": {
3
+ "version": "6.8.0",
4
+ "release_timestamp": 1528704978,
5
  "slug_parent": "icwp",
6
  "slug_plugin": "wpsf",
7
  "human_name": "Shield",
67
  "Title": "Shield Security",
68
  "Author": "One Dollar Plugin",
69
  "AuthorName": "One Dollar Plugin",
70
+ "PluginURI": "https://icwp.io/2f",
71
+ "AuthorURI": "https://icwp.io/bv",
72
  "icon_url_16x16": "pluginlogo_16x16.png",
73
+ "icon_url_32x32": "pluginlogo_32x32.png",
74
+ "icon_url_128x128": "pluginlogo_128x128.png"
75
  },
76
  "plugin_meta": [
77
  {
78
  "name": "5&#10025; Rate This Plugin",
79
+ "href": "https://icwp.io/wpsf29"
80
  }
81
  ],
82
  "action_links": {
readme.txt CHANGED
@@ -1,6 +1,6 @@
1
  === Shield Security for WordPress ===
2
  Contributors: onedollarplugin, paultgoodchild
3
- Donate link: http://icwp.io/bw
4
  License: GPLv3
5
  License URI: http://www.gnu.org/licenses/gpl.html
6
  Tags: security, all in one, scan, firewall, two factor authentication, spam, wordfence, cerber, ithemes
@@ -8,7 +8,7 @@ Requires at least: 3.5.0
8
  Requires PHP: 5.2.4
9
  Recommended PHP: 5.4
10
  Tested up to: 4.9
11
- Stable tag: 6.7.2
12
 
13
  Complete All-In-One Protection for your WordPress sites, that makes Security Easy for Everyone - it doesn't have to be hard anymore.
14
 
@@ -95,9 +95,9 @@ From November 2017, Shield Security now has a Pro version for those that need to
95
  ### Dedicated Premium Support
96
 
97
  The Shield Security team prioritises email technical support over the WordPress.org forums.
98
- Individual, dedicated technical support is only available to customers who have [purchased Shield Pro](http://icwp.io/ab).
99
 
100
- Learn more on going Pro at [our One Dollar Plugin store](http://icwp.io/ab).
101
 
102
  = Our Mission =
103
 
@@ -124,19 +124,19 @@ downloading and installing Shield now
124
  * Exclusive membership to a private security group where you can learn more about WordPress security.
125
 
126
  = Super Admin Security Protection =
127
- The **only** WordPress security plugin with a WordPress-independent security key to protect itself. [more info](http://icwp.io/wpsf05)
128
 
129
  = Audit Trail Activity Monitor =
130
  With the Audit Trail you can review all major actions that have taken place on your WordPress site, by all users.
131
 
132
  = Firewall Protection =
133
- Blocks all web requests to the site that violate the firewall security rules! [more info](http://icwp.io/wpsf06)
134
 
135
  = Brute Force Login Guard and Two-Factor Authentication =
136
- Provides effective security against Brute Force Hacking and email based Two-Factor Authenticated login. [more info](http://icwp.io/wpsf07)
137
 
138
  = Comment SPAM (Full replacement and upgrade from Akismet) =
139
- Blocks **ALL** automatic Bot-SPAM, and catches Human Comments SPAM without sending data to 3rd parties or charging subscription fees. [more info](http://icwp.io/wpsf08)
140
 
141
  = FABLE - Fully Automatic Black Listing Engine =
142
  No more manual IP Black lists. This plugin handles the blocking of IP addresses for hosts that are naughty.
@@ -160,9 +160,9 @@ and force users to verify themselves when they login.
160
 
161
  Three core security features provide layers to protect the WordPress Login system.
162
 
163
- 1. [Email-based 2-Factor Login Authentication](http://icwp.io/2v) based on IP address! (prevents brute force login attacks)
164
- 1. [Login Cooldown Interval](http://icwp.io/2t) - WordPress will only process 1 login per interval in seconds (prevents brute force login attacks)
165
- 1. [GASP Anti-Bot Login Form Protection](http://icwp.io/2u) - Adds 2 protection checks for all WordPress login attempts (prevents brute force login attacks using Bots)
166
 
167
  These options alone will protect and secure your WordPress sites from nearly all forms of Brute Force login attacks.
168
 
@@ -215,7 +215,7 @@ A new menu item will appear on the left-hand side called 'Shield'.
215
 
216
  == Frequently Asked Questions ==
217
 
218
- Please see the dedicated [help centre](http://icwp.io/firewallhelp) for details on features and some FAQs.
219
 
220
  = How does the Shield compare with other WordPress Security Plugins? =
221
 
@@ -306,15 +306,15 @@ When enabled the plugin will prevent more than 1 login attempt to your site ever
306
  of 60 seconds, only 1 login attempt will be processed every 60 seconds. If you login incorrectly, you wont be able to attempt another
307
  login for a further 60 seconds.
308
 
309
- More Info: http://icwp.io/2t
310
 
311
  = How does the GASP Login Guard work? =
312
 
313
- This is best [described on the blog](http://icwp.io/2u)
314
 
315
  = How does the 2-factor authentication work? =
316
 
317
- [2-Factor Authentication is best described here](http://icwp.io/2v).
318
 
319
  = I'm getting an update message although I have auto update enabled? =
320
 
@@ -346,28 +346,38 @@ Possible options are: network_admin, administrator, editor, author, contributor,
346
  == Changelog ==
347
 
348
  Our policy was to never restrict security features to Pro upgrades.
349
- [This has now changed](http://icwp.io/bs).
350
 
351
  Shield Pro brings exclusive features to the serious webmaster to maximise site security. You'll also have access to our email technical support team.
352
- If you don't want to support the work, no problem! You can still continue to use Shield Security and its free features in-full.
353
 
354
- You can [go Pro for just $1/month](http://icwp.io/aa).
355
 
356
- = 6.7.2 - Current Release =
357
- *Released: 30th May, 2018* - [Release Notes](http://icwp.io/cx)
358
 
359
- * **(v.2)** ADDED: [**PRO**] Admin Notes feature - Notes can now be easily deleted (editing will not be possible).
360
- * **(v.0)** UPDATED: Some translations.
361
- * **(v.2)** FIXED: A few bugs with the Insights Dashboard.
362
- * **(v.2)** FIXED: Removed the dependency on jQuery with Invisible reCAPTCHA.
 
 
 
 
 
363
 
364
- Note: The Insights Dashboard is only available on sites with PHP v5.4.0 and above.
 
 
 
 
 
365
 
366
  = 6.7 Series =
367
- *Released: 21st May, 2018* - [Release Notes](http://icwp.io/cx)
368
 
369
  * **(v.2)** ADDED: [**PRO**] Admin Notes feature - Notes can now be easily deleted (editing will not be possible).
370
- * **(v.0)** UPDATED: Some translations.
371
  * **(v.2)** FIXED: A few bugs with the Insights Dashboard.
372
  * **(v.2)** FIXED: Removed the dependency on jQuery with Invisible reCAPTCHA.
373
  * **(v.1)** FIXED: A few bugs with the Insights Dashboard
@@ -382,7 +392,7 @@ Note: The Insights Dashboard is only available on sites with PHP v5.4.0 and abov
382
  * **(v.0)** IMPROVED: Compatibility with AIO Events Cal - they like to force their old Twig libraries on everyone else.
383
 
384
  = 6.6 Series =
385
- *Released: 19th March, 2018* - [Release Notes](http://icwp.io/c3)
386
 
387
  * **(v.7)** IMPROVED: reCAPTCHA JS is only included on pages where it's actually used by Shield.
388
  * **(v.7)** IMPROVED: Upgrade Bootstrap library to 4.1.0.
@@ -392,17 +402,17 @@ Note: The Insights Dashboard is only available on sites with PHP v5.4.0 and abov
392
  * **(v.6)** ADDED: Workaround for a [ridiculous NGG bug](https://wordpress.org/support/topic/forcefully-executing-wp_footer-not-compatible-with-other-plugins/).
393
  * **(v.1-4)** FIXED: Various small fixes and improvements
394
  * **(v.4)** FIXED: PHP Fatal Error on wp object cache.
395
- * **(v.0)** NEW: [**PRO**] [Keyless Activation of Pro licenses](http://icwp.io/c1).
396
- * **(v.0)** ADDED: [WordPress Password Policies](http://icwp.io/c2).
397
  * **(v.0)** ADDED: Pwned Passwords Detection.
398
  * **(v.0)** IMPROVED: Major rewrite of plugin AJAX handling.
399
  * **(v.0)** IMPROVED: Notices to indicate the time of the last scans.
400
  * **(v.0)** FIXED: A few bugs
401
 
402
  = 6.5 Series =
403
- *Released: 5th March, 2018* - [Release Notes](http://icwp.io/bu)
404
 
405
- * **(v.0)** IMPROVED: [Plugin Guard](http://icwp.io/bq) better handles the case where a plugin/theme has been entirely renamed/removed.
406
  * **(v.0)** IMPROVED: Attempts to access the XML-RPC system when it's disabled will now result in a transgression increment in the IP Black List
407
  * **(v.0)** IMPROVED: Try to prevent black listing the server's own public IP address where visitor IP address detection is not correctly configured.
408
  * **(v.0)** ADDED: [**PRO**] Provisional support for not processing 2FA logins for Woocommerce Social Login plugin.
1
  === Shield Security for WordPress ===
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: security, all in one, scan, firewall, two factor authentication, spam, wordfence, cerber, ithemes
8
  Requires PHP: 5.2.4
9
  Recommended PHP: 5.4
10
  Tested up to: 4.9
11
+ Stable tag: 6.8.0
12
 
13
  Complete All-In-One Protection for your WordPress sites, that makes Security Easy for Everyone - it doesn't have to be hard anymore.
14
 
95
  ### Dedicated Premium Support
96
 
97
  The Shield Security team prioritises email technical support over the WordPress.org forums.
98
+ Individual, dedicated technical support is only available to customers who have [purchased Shield Pro](https://icwp.io/ab).
99
 
100
+ Learn more on going Pro at [our One Dollar Plugin store](https://icwp.io/ab).
101
 
102
  = Our Mission =
103
 
124
  * Exclusive membership to a private security group where you can learn more about WordPress security.
125
 
126
  = Super Admin Security Protection =
127
+ The **only** WordPress security plugin with a WordPress-independent security key to protect itself. [more info](https://icwp.io/wpsf05)
128
 
129
  = Audit Trail Activity Monitor =
130
  With the Audit Trail you can review all major actions that have taken place on your WordPress site, by all users.
131
 
132
  = Firewall Protection =
133
+ Blocks all web requests to the site that violate the firewall security rules! [more info](https://icwp.io/wpsf06)
134
 
135
  = Brute Force Login Guard and Two-Factor Authentication =
136
+ Provides effective security against Brute Force Hacking and email based Two-Factor Authenticated login. [more info](https://icwp.io/wpsf07)
137
 
138
  = Comment SPAM (Full replacement and upgrade from Akismet) =
139
+ 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)
140
 
141
  = FABLE - Fully Automatic Black Listing Engine =
142
  No more manual IP Black lists. This plugin handles the blocking of IP addresses for hosts that are naughty.
160
 
161
  Three core security features provide layers to protect the WordPress Login system.
162
 
163
+ 1. [Email-based 2-Factor Login Authentication](https://icwp.io/2v) based on IP address! (prevents brute force login attacks)
164
+ 1. [Login Cooldown Interval](https://icwp.io/2t) - WordPress will only process 1 login per interval in seconds (prevents brute force login attacks)
165
+ 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)
166
 
167
  These options alone will protect and secure your WordPress sites from nearly all forms of Brute Force login attacks.
168
 
215
 
216
  == Frequently Asked Questions ==
217
 
218
+ Please see the dedicated [help centre](https://icwp.io/firewallhelp) for details on features and some FAQs.
219
 
220
  = How does the Shield compare with other WordPress Security Plugins? =
221
 
306
  of 60 seconds, only 1 login attempt will be processed every 60 seconds. If you login incorrectly, you wont be able to attempt another
307
  login for a further 60 seconds.
308
 
309
+ More Info: https://icwp.io/2t
310
 
311
  = How does the GASP Login Guard work? =
312
 
313
+ This is best [described on the blog](https://icwp.io/2u)
314
 
315
  = How does the 2-factor authentication work? =
316
 
317
+ [2-Factor Authentication is best described here](https://icwp.io/2v).
318
 
319
  = I'm getting an update message although I have auto update enabled? =
320
 
346
  == Changelog ==
347
 
348
  Our policy was to never restrict security features to Pro upgrades.
349
+ [This has now changed](https://icwp.io/bs).
350
 
351
  Shield Pro brings exclusive features to the serious webmaster to maximise site security. You'll also have access to our email technical support team.
352
+ You will always be able to use Shield Security and its free features in-full.
353
 
354
+ [Go Pro for just $1/month](https://icwp.io/aa).
355
 
356
+ = 6.8.0 - Current Release =
357
+ *Released: 11th June, 2018* - [Release Notes](https://icwp.io/d4)
358
 
359
+ * **(v.0)** ADDED: [**PRO**] White Label - ability to re-brand the entire Shield Security plugin to your company brand.
360
+ * **(v.0)** ADDED: [**PRO**] Option for all users to receive notification email upon login to their accounts.
361
+ * **(v.0)** IMPROVED: Completely rebuilt the bot and reCAPTCHA login protection system.
362
+ * **(v.0)** IMPROVED: Import/Export system hugely improved with respect to automated push of options from Master sites.
363
+ * **(v.0)** IMPROVED: A different approach to sessions management that should handle sessions a bit better.
364
+ * **(v.0)** IMPROVED: Expired user sessions are cleaned from the DB using a cron, and on Insights Dashboard load.
365
+
366
+ = 6.8 Series =
367
+ *Released: 11th June, 2018* - [Release Notes](https://icwp.io/d4)
368
 
369
+ * **(v.0)** ADDED: [**PRO**] White Label - ability to re-brand the entire Shield Security plugin to your company brand.
370
+ * **(v.0)** ADDED: [**PRO**] Option for all users to receive notification email upon login to their accounts.
371
+ * **(v.0)** IMPROVED: Completely rebuilt the bot and reCAPTCHA login protection system.
372
+ * **(v.0)** IMPROVED: Import/Export system hugely improved with respect to automated push of options from Master sites.
373
+ * **(v.0)** IMPROVED: A different approach to sessions management that should handle sessions a bit better.
374
+ * **(v.0)** IMPROVED: Expired user sessions are cleaned from the DB using a cron, and on Insights Dashboard load.
375
 
376
  = 6.7 Series =
377
+ *Released: 21st May, 2018* - [Release Notes](https://icwp.io/cx)
378
 
379
  * **(v.2)** ADDED: [**PRO**] Admin Notes feature - Notes can now be easily deleted (editing will not be possible).
380
+ * **(v.2)** UPDATED: Some translations.
381
  * **(v.2)** FIXED: A few bugs with the Insights Dashboard.
382
  * **(v.2)** FIXED: Removed the dependency on jQuery with Invisible reCAPTCHA.
383
  * **(v.1)** FIXED: A few bugs with the Insights Dashboard
392
  * **(v.0)** IMPROVED: Compatibility with AIO Events Cal - they like to force their old Twig libraries on everyone else.
393
 
394
  = 6.6 Series =
395
+ *Released: 19th March, 2018* - [Release Notes](https://icwp.io/c3)
396
 
397
  * **(v.7)** IMPROVED: reCAPTCHA JS is only included on pages where it's actually used by Shield.
398
  * **(v.7)** IMPROVED: Upgrade Bootstrap library to 4.1.0.
402
  * **(v.6)** ADDED: Workaround for a [ridiculous NGG bug](https://wordpress.org/support/topic/forcefully-executing-wp_footer-not-compatible-with-other-plugins/).
403
  * **(v.1-4)** FIXED: Various small fixes and improvements
404
  * **(v.4)** FIXED: PHP Fatal Error on wp object cache.
405
+ * **(v.0)** NEW: [**PRO**] [Keyless Activation of Pro licenses](https://icwp.io/c1).
406
+ * **(v.0)** ADDED: [WordPress Password Policies](https://icwp.io/c2).
407
  * **(v.0)** ADDED: Pwned Passwords Detection.
408
  * **(v.0)** IMPROVED: Major rewrite of plugin AJAX handling.
409
  * **(v.0)** IMPROVED: Notices to indicate the time of the last scans.
410
  * **(v.0)** FIXED: A few bugs
411
 
412
  = 6.5 Series =
413
+ *Released: 5th March, 2018* - [Release Notes](https://icwp.io/bu)
414
 
415
+ * **(v.0)** IMPROVED: [Plugin Guard](https://icwp.io/bq) better handles the case where a plugin/theme has been entirely renamed/removed.
416
  * **(v.0)** IMPROVED: Attempts to access the XML-RPC system when it's disabled will now result in a transgression increment in the IP Black List
417
  * **(v.0)** IMPROVED: Try to prevent black listing the server's own public IP address where visitor IP address detection is not correctly configured.
418
  * **(v.0)** ADDED: [**PRO**] Provisional support for not processing 2FA logins for Woocommerce Social Login plugin.
resources/css/plugin.css CHANGED
@@ -666,6 +666,7 @@ input:checked + .icwp-slider:before {
666
 
667
  height: 128px;
668
  width: 128px;
 
669
 
670
  margin-bottom: 7px;
671
  position: relative;
666
 
667
  height: 128px;
668
  width: 128px;
669
+ background-size: 128px 128px;
670
 
671
  margin-bottom: 7px;
672
  position: relative;
resources/images/pluginlogo_128x128.png ADDED
Binary file
resources/js/plugin.js CHANGED
@@ -31,6 +31,21 @@ var iCWP_WPSF_OptionsPages = new function () {
31
  jQuery( document ).on( "click", "a.icwp-carousel-1", moveCarousel1 );
32
  jQuery( document ).on( "click", "a.icwp-carousel-2", moveCarousel2 );
33
  jQuery( document ).on( "click", "a.icwp-carousel-3", moveCarousel3 );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
34
  } );
35
  };
36
 
@@ -70,13 +85,11 @@ var iCWP_WPSF_OptionsFormSubmit = new function () {
70
  else {
71
  sMessage = oResponse.data.message;
72
  }
73
- /** TODO: div#icwpOptionsFormContainer no longer exists */
74
- jQuery( 'div#icwpOptionsFormContainer' ).html( oResponse.data.options_form );
75
  iCWP_WPSF_Growl.showMessage( sMessage, oResponse.success );
76
  }
77
  ).always( function () {
78
  bRequestCurrentlyRunning = false;
79
- iCWP_WPSF_BodyOverlay.hide();
80
  }
81
  );
82
  };
31
  jQuery( document ).on( "click", "a.icwp-carousel-1", moveCarousel1 );
32
  jQuery( document ).on( "click", "a.icwp-carousel-2", moveCarousel2 );
33
  jQuery( document ).on( "click", "a.icwp-carousel-3", moveCarousel3 );
34
+
35
+ /** Track active tab */
36
+ jQuery( document ).on( "click", "#ModuleOptionsNav a.nav-link", function ( e ) {
37
+ e.preventDefault();
38
+ jQuery( this ).tab( 'show' );
39
+ jQuery( 'html,body' ).scrollTop( 0 );
40
+ } );
41
+ jQuery( document ).on( "shown.bs.tab", "#ModuleOptionsNav a.nav-link", function ( e ) {
42
+ window.location.hash = jQuery( e.target ).attr( "href" ).substr( 1 );
43
+ } );
44
+
45
+ var sActiveTabHash = window.location.hash;
46
+ if ( sActiveTabHash ) {
47
+ jQuery( '#ModuleOptionsNav a[href="' + window.location.hash + '"]' ).tab( 'show' );
48
+ }
49
  } );
50
  };
51
 
85
  else {
86
  sMessage = oResponse.data.message;
87
  }
 
 
88
  iCWP_WPSF_Growl.showMessage( sMessage, oResponse.success );
89
  }
90
  ).always( function () {
91
  bRequestCurrentlyRunning = false;
92
+ location.reload( true );
93
  }
94
  );
95
  };
src/common/icwp-data.php CHANGED
@@ -243,12 +243,21 @@ class ICWP_WPSF_DataProcessor extends ICWP_WPSF_Foundation {
243
  }
244
 
245
  /**
 
246
  * @return boolean
247
  */
248
  public function validEmail( $sEmail ) {
249
  return ( !empty( $sEmail ) && function_exists( 'is_email' ) && is_email( $sEmail ) );
250
  }
251
 
 
 
 
 
 
 
 
 
252
  /**
253
  * @param string $sRawList
254
  * @return array
243
  }
244
 
245
  /**
246
+ * @param string $sEmail
247
  * @return boolean
248
  */
249
  public function validEmail( $sEmail ) {
250
  return ( !empty( $sEmail ) && function_exists( 'is_email' ) && is_email( $sEmail ) );
251
  }
252
 
253
+ /**
254
+ * @param string $sUrl
255
+ * @return bool
256
+ */
257
+ public function validUrl( $sUrl ) {
258
+ return filter_var( $sUrl, FILTER_VALIDATE_URL );
259
+ }
260
+
261
  /**
262
  * @param string $sRawList
263
  * @return array
src/common/icwp-foundation.php CHANGED
@@ -93,13 +93,6 @@ class ICWP_WPSF_Foundation {
93
  return self::$oDp;
94
  }
95
 
96
- /**
97
- * @return ICWP_WPSF_DataProcessor
98
- */
99
- static public function loadDataProcessor() {
100
- return self::loadDP();
101
- }
102
-
103
  /**
104
  * @return ICWP_WPSF_WpFilesystem
105
  */
@@ -308,7 +301,7 @@ class ICWP_WPSF_Foundation {
308
  /**
309
  * @return ICWP_WPSF_WpComments
310
  */
311
- static public function loadWpCommentsProcessor() {
312
  if ( !isset( self::$oWpComments ) ) {
313
  self::requireCommonLib( 'wp-comments.php' );
314
  self::$oWpComments = ICWP_WPSF_WpComments::GetInstance();
@@ -339,4 +332,20 @@ class ICWP_WPSF_Foundation {
339
  static public function requireCommonLib( $sFile ) {
340
  require_once( dirname( __FILE__ ).DIRECTORY_SEPARATOR.$sFile );
341
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
342
  }
93
  return self::$oDp;
94
  }
95
 
 
 
 
 
 
 
 
96
  /**
97
  * @return ICWP_WPSF_WpFilesystem
98
  */
301
  /**
302
  * @return ICWP_WPSF_WpComments
303
  */
304
+ static public function loadWpComments() {
305
  if ( !isset( self::$oWpComments ) ) {
306
  self::requireCommonLib( 'wp-comments.php' );
307
  self::$oWpComments = ICWP_WPSF_WpComments::GetInstance();
332
  static public function requireCommonLib( $sFile ) {
333
  require_once( dirname( __FILE__ ).DIRECTORY_SEPARATOR.$sFile );
334
  }
335
+
336
+ /**
337
+ * @deprecated
338
+ * @return ICWP_WPSF_WpComments
339
+ */
340
+ static public function loadWpCommentsProcessor() {
341
+ return self::loadWpComments();
342
+ }
343
+
344
+ /**
345
+ * @deprecated
346
+ * @return ICWP_WPSF_DataProcessor
347
+ */
348
+ static public function loadDataProcessor() {
349
+ return self::loadDP();
350
+ }
351
  }
src/common/icwp-render.php CHANGED
@@ -176,7 +176,7 @@ class ICWP_WPSF_Render extends ICWP_WPSF_Foundation {
176
  * @return string
177
  */
178
  public function getTemplate() {
179
- $this->sTemplate = $this->loadDataProcessor()
180
  ->addExtensionToFilePath( $this->sTemplate, $this->getEngineStub() );
181
  return $this->sTemplate;
182
  }
@@ -213,7 +213,7 @@ class ICWP_WPSF_Render extends ICWP_WPSF_Foundation {
213
  if ( empty( $sTemplate ) ) {
214
  $sTemplate = $this->getTemplate();
215
  }
216
- $sTemplate = $this->loadDataProcessor()->addExtensionToFilePath( $sTemplate, $this->getEngineStub() );
217
  return path_join( $this->getTemplateRoot(), $sTemplate );
218
  }
219
 
176
  * @return string
177
  */
178
  public function getTemplate() {
179
+ $this->sTemplate = $this->loadDP()
180
  ->addExtensionToFilePath( $this->sTemplate, $this->getEngineStub() );
181
  return $this->sTemplate;
182
  }
213
  if ( empty( $sTemplate ) ) {
214
  $sTemplate = $this->getTemplate();
215
  }
216
+ $sTemplate = $this->loadDP()->addExtensionToFilePath( $sTemplate, $this->getEngineStub() );
217
  return path_join( $this->getTemplateRoot(), $sTemplate );
218
  }
219
 
src/common/icwp-wpfunctions-plugins.php CHANGED
@@ -490,7 +490,7 @@ class ICWP_WPSF_WpFunctions_Plugins extends ICWP_WPSF_Foundation {
490
  public function setActivePluginLoadPosition( $sFile, $nDesiredPosition = 0 ) {
491
  $oWp = $this->loadWp();
492
 
493
- $aActive = $this->loadDataProcessor()
494
  ->setArrayValueToPosition(
495
  $oWp->getOption( 'active_plugins' ),
496
  $sFile,
@@ -499,7 +499,7 @@ class ICWP_WPSF_WpFunctions_Plugins extends ICWP_WPSF_Foundation {
499
  $oWp->updateOption( 'active_plugins', $aActive );
500
 
501
  if ( $oWp->isMultisite() ) {
502
- $aActive = $this->loadDataProcessor()
503
  ->setArrayValueToPosition( $oWp->getOption( 'active_sitewide_plugins' ), $sFile, $nDesiredPosition );
504
  $oWp->updateOption( 'active_sitewide_plugins', $aActive );
505
  }
490
  public function setActivePluginLoadPosition( $sFile, $nDesiredPosition = 0 ) {
491
  $oWp = $this->loadWp();
492
 
493
+ $aActive = $this->loadDP()
494
  ->setArrayValueToPosition(
495
  $oWp->getOption( 'active_plugins' ),
496
  $sFile,
499
  $oWp->updateOption( 'active_plugins', $aActive );
500
 
501
  if ( $oWp->isMultisite() ) {
502
+ $aActive = $this->loadDP()
503
  ->setArrayValueToPosition( $oWp->getOption( 'active_sitewide_plugins' ), $sFile, $nDesiredPosition );
504
  $oWp->updateOption( 'active_sitewide_plugins', $aActive );
505
  }
src/common/icwp-wpfunctions.php CHANGED
@@ -408,7 +408,7 @@ class ICWP_WPSF_WpFunctions extends ICWP_WPSF_Foundation {
408
  }
409
 
410
  public function redirectHere() {
411
- $this->doRedirect( $this->loadDataProcessor()->getRequestUri() );
412
  }
413
 
414
  /**
@@ -853,7 +853,7 @@ class ICWP_WPSF_WpFunctions extends ICWP_WPSF_Foundation {
853
  }
854
  }
855
 
856
- $nTime = is_null( $nTime ) ? $this->loadDataProcessor()->time() : $nTime;
857
  return $nTime + ( $nTimezoneOffset*HOUR_IN_SECONDS );
858
  }
859
 
408
  }
409
 
410
  public function redirectHere() {
411
+ $this->doRedirect( $this->loadDP()->getRequestUri() );
412
  }
413
 
414
  /**
853
  }
854
  }
855
 
856
+ $nTime = is_null( $nTime ) ? $this->loadDP()->time() : $nTime;
857
  return $nTime + ( $nTimezoneOffset*HOUR_IN_SECONDS );
858
  }
859
 
src/common/lib/composer.lock CHANGED
@@ -87,16 +87,16 @@
87
  },
88
  {
89
  "name": "nesbot/carbon",
90
- "version": "1.25.0",
91
  "source": {
92
  "type": "git",
93
  "url": "https://github.com/briannesbitt/Carbon.git",
94
- "reference": "cbcf13da0b531767e39eb86e9687f5deba9857b4"
95
  },
96
  "dist": {
97
  "type": "zip",
98
- "url": "https://api.github.com/repos/briannesbitt/Carbon/zipball/cbcf13da0b531767e39eb86e9687f5deba9857b4",
99
- "reference": "cbcf13da0b531767e39eb86e9687f5deba9857b4",
100
  "shasum": ""
101
  },
102
  "require": {
@@ -108,14 +108,9 @@
108
  "phpunit/phpunit": "^4.8.35 || ^5.7"
109
  },
110
  "type": "library",
111
- "extra": {
112
- "branch-alias": {
113
- "dev-master": "1.23-dev"
114
- }
115
- },
116
  "autoload": {
117
  "psr-4": {
118
- "Carbon\\": "src/Carbon/"
119
  }
120
  },
121
  "notification-url": "https://packagist.org/downloads/",
@@ -136,20 +131,20 @@
136
  "datetime",
137
  "time"
138
  ],
139
- "time": "2018-03-19T15:50:49+00:00"
140
  },
141
  {
142
  "name": "symfony/polyfill-mbstring",
143
- "version": "v1.7.0",
144
  "source": {
145
  "type": "git",
146
  "url": "https://github.com/symfony/polyfill-mbstring.git",
147
- "reference": "78be803ce01e55d3491c1397cf1c64beb9c1b63b"
148
  },
149
  "dist": {
150
  "type": "zip",
151
- "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/78be803ce01e55d3491c1397cf1c64beb9c1b63b",
152
- "reference": "78be803ce01e55d3491c1397cf1c64beb9c1b63b",
153
  "shasum": ""
154
  },
155
  "require": {
@@ -161,7 +156,7 @@
161
  "type": "library",
162
  "extra": {
163
  "branch-alias": {
164
- "dev-master": "1.7-dev"
165
  }
166
  },
167
  "autoload": {
@@ -195,20 +190,20 @@
195
  "portable",
196
  "shim"
197
  ],
198
- "time": "2018-01-30T19:27:44+00:00"
199
  },
200
  {
201
  "name": "symfony/translation",
202
- "version": "v2.8.36",
203
  "source": {
204
  "type": "git",
205
  "url": "https://github.com/symfony/translation.git",
206
- "reference": "72235c1121ae282254e897e342c001f3d4bb7e8d"
207
  },
208
  "dist": {
209
  "type": "zip",
210
- "url": "https://api.github.com/repos/symfony/translation/zipball/72235c1121ae282254e897e342c001f3d4bb7e8d",
211
- "reference": "72235c1121ae282254e897e342c001f3d4bb7e8d",
212
  "shasum": ""
213
  },
214
  "require": {
@@ -225,7 +220,7 @@
225
  "symfony/yaml": "~2.2|~3.0.0"
226
  },
227
  "suggest": {
228
- "psr/log": "To use logging capability in translator",
229
  "symfony/config": "",
230
  "symfony/yaml": ""
231
  },
@@ -259,7 +254,7 @@
259
  ],
260
  "description": "Symfony Translation Component",
261
  "homepage": "https://symfony.com",
262
- "time": "2018-01-18T13:56:23+00:00"
263
  },
264
  {
265
  "name": "twig/twig",
87
  },
88
  {
89
  "name": "nesbot/carbon",
90
+ "version": "1.29.2",
91
  "source": {
92
  "type": "git",
93
  "url": "https://github.com/briannesbitt/Carbon.git",
94
+ "reference": "ed6aa898982f441ccc9b2acdec51490f2bc5d337"
95
  },
96
  "dist": {
97
  "type": "zip",
98
+ "url": "https://api.github.com/repos/briannesbitt/Carbon/zipball/ed6aa898982f441ccc9b2acdec51490f2bc5d337",
99
+ "reference": "ed6aa898982f441ccc9b2acdec51490f2bc5d337",
100
  "shasum": ""
101
  },
102
  "require": {
108
  "phpunit/phpunit": "^4.8.35 || ^5.7"
109
  },
110
  "type": "library",
 
 
 
 
 
111
  "autoload": {
112
  "psr-4": {
113
+ "": "src/"
114
  }
115
  },
116
  "notification-url": "https://packagist.org/downloads/",
131
  "datetime",
132
  "time"
133
  ],
134
+ "time": "2018-05-29T15:23:46+00:00"
135
  },
136
  {
137
  "name": "symfony/polyfill-mbstring",
138
+ "version": "v1.8.0",
139
  "source": {
140
  "type": "git",
141
  "url": "https://github.com/symfony/polyfill-mbstring.git",
142
+ "reference": "3296adf6a6454a050679cde90f95350ad604b171"
143
  },
144
  "dist": {
145
  "type": "zip",
146
+ "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/3296adf6a6454a050679cde90f95350ad604b171",
147
+ "reference": "3296adf6a6454a050679cde90f95350ad604b171",
148
  "shasum": ""
149
  },
150
  "require": {
156
  "type": "library",
157
  "extra": {
158
  "branch-alias": {
159
+ "dev-master": "1.8-dev"
160
  }
161
  },
162
  "autoload": {
190
  "portable",
191
  "shim"
192
  ],
193
+ "time": "2018-04-26T10:06:28+00:00"
194
  },
195
  {
196
  "name": "symfony/translation",
197
+ "version": "v2.8.41",
198
  "source": {
199
  "type": "git",
200
  "url": "https://github.com/symfony/translation.git",
201
+ "reference": "c6a27966a92fa361bf2c3a938abc6dee91f7ad67"
202
  },
203
  "dist": {
204
  "type": "zip",
205
+ "url": "https://api.github.com/repos/symfony/translation/zipball/c6a27966a92fa361bf2c3a938abc6dee91f7ad67",
206
+ "reference": "c6a27966a92fa361bf2c3a938abc6dee91f7ad67",
207
  "shasum": ""
208
  },
209
  "require": {
220
  "symfony/yaml": "~2.2|~3.0.0"
221
  },
222
  "suggest": {
223
+ "psr/log-implementation": "To use logging capability in translator",
224
  "symfony/config": "",
225
  "symfony/yaml": ""
226
  },
254
  ],
255
  "description": "Symfony Translation Component",
256
  "homepage": "https://symfony.com",
257
+ "time": "2018-05-21T09:59:10+00:00"
258
  },
259
  {
260
  "name": "twig/twig",
src/common/lib/vendor/composer/autoload_psr4.php CHANGED
@@ -11,5 +11,5 @@ return array(
11
  'Symfony\\Polyfill\\Mbstring\\' => array($vendorDir . '/symfony/polyfill-mbstring'),
12
  'Symfony\\Component\\Translation\\' => array($vendorDir . '/symfony/translation'),
13
  'FernleafSystems\\Utilities\\' => array($vendorDir . '/fernleafsystems/utilities/src'),
14
- 'Carbon\\' => array($vendorDir . '/nesbot/carbon/src/Carbon'),
15
  );
11
  'Symfony\\Polyfill\\Mbstring\\' => array($vendorDir . '/symfony/polyfill-mbstring'),
12
  'Symfony\\Component\\Translation\\' => array($vendorDir . '/symfony/translation'),
13
  'FernleafSystems\\Utilities\\' => array($vendorDir . '/fernleafsystems/utilities/src'),
14
+ '' => array($vendorDir . '/nesbot/carbon/src'),
15
  );
src/common/lib/vendor/composer/autoload_static.php CHANGED
@@ -28,10 +28,6 @@ class ComposerStaticInit18a31866e67f0a0bfffdc031786ecae1
28
  array (
29
  'FernleafSystems\\Utilities\\' => 26,
30
  ),
31
- 'C' =>
32
- array (
33
- 'Carbon\\' => 7,
34
- ),
35
  );
36
 
37
  public static $prefixDirsPsr4 = array (
@@ -55,10 +51,10 @@ class ComposerStaticInit18a31866e67f0a0bfffdc031786ecae1
55
  array (
56
  0 => __DIR__ . '/..' . '/fernleafsystems/utilities/src',
57
  ),
58
- 'Carbon\\' =>
59
- array (
60
- 0 => __DIR__ . '/..' . '/nesbot/carbon/src/Carbon',
61
- ),
62
  );
63
 
64
  public static $prefixesPsr0 = array (
@@ -76,6 +72,7 @@ class ComposerStaticInit18a31866e67f0a0bfffdc031786ecae1
76
  return \Closure::bind(function () use ($loader) {
77
  $loader->prefixLengthsPsr4 = ComposerStaticInit18a31866e67f0a0bfffdc031786ecae1::$prefixLengthsPsr4;
78
  $loader->prefixDirsPsr4 = ComposerStaticInit18a31866e67f0a0bfffdc031786ecae1::$prefixDirsPsr4;
 
79
  $loader->prefixesPsr0 = ComposerStaticInit18a31866e67f0a0bfffdc031786ecae1::$prefixesPsr0;
80
 
81
  }, null, ClassLoader::class);
28
  array (
29
  'FernleafSystems\\Utilities\\' => 26,
30
  ),
 
 
 
 
31
  );
32
 
33
  public static $prefixDirsPsr4 = array (
51
  array (
52
  0 => __DIR__ . '/..' . '/fernleafsystems/utilities/src',
53
  ),
54
+ );
55
+
56
+ public static $fallbackDirsPsr4 = array (
57
+ 0 => __DIR__ . '/..' . '/nesbot/carbon/src',
58
  );
59
 
60
  public static $prefixesPsr0 = array (
72
  return \Closure::bind(function () use ($loader) {
73
  $loader->prefixLengthsPsr4 = ComposerStaticInit18a31866e67f0a0bfffdc031786ecae1::$prefixLengthsPsr4;
74
  $loader->prefixDirsPsr4 = ComposerStaticInit18a31866e67f0a0bfffdc031786ecae1::$prefixDirsPsr4;
75
+ $loader->fallbackDirsPsr4 = ComposerStaticInit18a31866e67f0a0bfffdc031786ecae1::$fallbackDirsPsr4;
76
  $loader->prefixesPsr0 = ComposerStaticInit18a31866e67f0a0bfffdc031786ecae1::$prefixesPsr0;
77
 
78
  }, null, ClassLoader::class);
src/common/lib/vendor/composer/installed.json CHANGED
@@ -82,19 +82,86 @@
82
  ],
83
  "description": "Collection of simple utilities and traits"
84
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
85
  {
86
  "name": "symfony/polyfill-mbstring",
87
- "version": "v1.7.0",
88
- "version_normalized": "1.7.0.0",
89
  "source": {
90
  "type": "git",
91
  "url": "https://github.com/symfony/polyfill-mbstring.git",
92
- "reference": "78be803ce01e55d3491c1397cf1c64beb9c1b63b"
93
  },
94
  "dist": {
95
  "type": "zip",
96
- "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/78be803ce01e55d3491c1397cf1c64beb9c1b63b",
97
- "reference": "78be803ce01e55d3491c1397cf1c64beb9c1b63b",
98
  "shasum": ""
99
  },
100
  "require": {
@@ -103,11 +170,11 @@
103
  "suggest": {
104
  "ext-mbstring": "For best performance"
105
  },
106
- "time": "2018-01-30T19:27:44+00:00",
107
  "type": "library",
108
  "extra": {
109
  "branch-alias": {
110
- "dev-master": "1.7-dev"
111
  }
112
  },
113
  "installation-source": "dist",
@@ -145,17 +212,17 @@
145
  },
146
  {
147
  "name": "symfony/translation",
148
- "version": "v2.8.36",
149
- "version_normalized": "2.8.36.0",
150
  "source": {
151
  "type": "git",
152
  "url": "https://github.com/symfony/translation.git",
153
- "reference": "72235c1121ae282254e897e342c001f3d4bb7e8d"
154
  },
155
  "dist": {
156
  "type": "zip",
157
- "url": "https://api.github.com/repos/symfony/translation/zipball/72235c1121ae282254e897e342c001f3d4bb7e8d",
158
- "reference": "72235c1121ae282254e897e342c001f3d4bb7e8d",
159
  "shasum": ""
160
  },
161
  "require": {
@@ -172,11 +239,11 @@
172
  "symfony/yaml": "~2.2|~3.0.0"
173
  },
174
  "suggest": {
175
- "psr/log": "To use logging capability in translator",
176
  "symfony/config": "",
177
  "symfony/yaml": ""
178
  },
179
- "time": "2018-01-18T13:56:23+00:00",
180
  "type": "library",
181
  "extra": {
182
  "branch-alias": {
@@ -211,17 +278,17 @@
211
  },
212
  {
213
  "name": "nesbot/carbon",
214
- "version": "1.25.0",
215
- "version_normalized": "1.25.0.0",
216
  "source": {
217
  "type": "git",
218
  "url": "https://github.com/briannesbitt/Carbon.git",
219
- "reference": "cbcf13da0b531767e39eb86e9687f5deba9857b4"
220
  },
221
  "dist": {
222
  "type": "zip",
223
- "url": "https://api.github.com/repos/briannesbitt/Carbon/zipball/cbcf13da0b531767e39eb86e9687f5deba9857b4",
224
- "reference": "cbcf13da0b531767e39eb86e9687f5deba9857b4",
225
  "shasum": ""
226
  },
227
  "require": {
@@ -232,17 +299,12 @@
232
  "friendsofphp/php-cs-fixer": "~2",
233
  "phpunit/phpunit": "^4.8.35 || ^5.7"
234
  },
235
- "time": "2018-03-19T15:50:49+00:00",
236
  "type": "library",
237
- "extra": {
238
- "branch-alias": {
239
- "dev-master": "1.23-dev"
240
- }
241
- },
242
  "installation-source": "dist",
243
  "autoload": {
244
  "psr-4": {
245
- "Carbon\\": "src/Carbon/"
246
  }
247
  },
248
  "notification-url": "https://packagist.org/downloads/",
@@ -263,72 +325,5 @@
263
  "datetime",
264
  "time"
265
  ]
266
- },
267
- {
268
- "name": "twig/twig",
269
- "version": "v1.35.3",
270
- "version_normalized": "1.35.3.0",
271
- "source": {
272
- "type": "git",
273
- "url": "https://github.com/twigphp/Twig.git",
274
- "reference": "b48680b6eb7d16b5025b9bfc4108d86f6b8af86f"
275
- },
276
- "dist": {
277
- "type": "zip",
278
- "url": "https://api.github.com/repos/twigphp/Twig/zipball/b48680b6eb7d16b5025b9bfc4108d86f6b8af86f",
279
- "reference": "b48680b6eb7d16b5025b9bfc4108d86f6b8af86f",
280
- "shasum": ""
281
- },
282
- "require": {
283
- "php": ">=5.3.3"
284
- },
285
- "require-dev": {
286
- "psr/container": "^1.0",
287
- "symfony/debug": "^2.7",
288
- "symfony/phpunit-bridge": "^3.3"
289
- },
290
- "time": "2018-03-20T04:25:58+00:00",
291
- "type": "library",
292
- "extra": {
293
- "branch-alias": {
294
- "dev-master": "1.35-dev"
295
- }
296
- },
297
- "installation-source": "dist",
298
- "autoload": {
299
- "psr-0": {
300
- "Twig_": "lib/"
301
- },
302
- "psr-4": {
303
- "Twig\\": "src/"
304
- }
305
- },
306
- "notification-url": "https://packagist.org/downloads/",
307
- "license": [
308
- "BSD-3-Clause"
309
- ],
310
- "authors": [
311
- {
312
- "name": "Fabien Potencier",
313
- "email": "fabien@symfony.com",
314
- "homepage": "http://fabien.potencier.org",
315
- "role": "Lead Developer"
316
- },
317
- {
318
- "name": "Armin Ronacher",
319
- "email": "armin.ronacher@active-4.com",
320
- "role": "Project Founder"
321
- },
322
- {
323
- "name": "Twig Team",
324
- "homepage": "http://twig.sensiolabs.org/contributors",
325
- "role": "Contributors"
326
- }
327
- ],
328
- "description": "Twig, the flexible, fast, and secure template language for PHP",
329
- "homepage": "http://twig.sensiolabs.org",
330
- "keywords": [
331
- "templating"
332
- ]
333
  }
334
  ]
82
  ],
83
  "description": "Collection of simple utilities and traits"
84
  },
85
+ {
86
+ "name": "twig/twig",
87
+ "version": "v1.35.3",
88
+ "version_normalized": "1.35.3.0",
89
+ "source": {
90
+ "type": "git",
91
+ "url": "https://github.com/twigphp/Twig.git",
92
+ "reference": "b48680b6eb7d16b5025b9bfc4108d86f6b8af86f"
93
+ },
94
+ "dist": {
95
+ "type": "zip",
96
+ "url": "https://api.github.com/repos/twigphp/Twig/zipball/b48680b6eb7d16b5025b9bfc4108d86f6b8af86f",
97
+ "reference": "b48680b6eb7d16b5025b9bfc4108d86f6b8af86f",
98
+ "shasum": ""
99
+ },
100
+ "require": {
101
+ "php": ">=5.3.3"
102
+ },
103
+ "require-dev": {
104
+ "psr/container": "^1.0",
105
+ "symfony/debug": "^2.7",
106
+ "symfony/phpunit-bridge": "^3.3"
107
+ },
108
+ "time": "2018-03-20T04:25:58+00:00",
109
+ "type": "library",
110
+ "extra": {
111
+ "branch-alias": {
112
+ "dev-master": "1.35-dev"
113
+ }
114
+ },
115
+ "installation-source": "dist",
116
+ "autoload": {
117
+ "psr-0": {
118
+ "Twig_": "lib/"
119
+ },
120
+ "psr-4": {
121
+ "Twig\\": "src/"
122
+ }
123
+ },
124
+ "notification-url": "https://packagist.org/downloads/",
125
+ "license": [
126
+ "BSD-3-Clause"
127
+ ],
128
+ "authors": [
129
+ {
130
+ "name": "Fabien Potencier",
131
+ "email": "fabien@symfony.com",
132
+ "homepage": "http://fabien.potencier.org",
133
+ "role": "Lead Developer"
134
+ },
135
+ {
136
+ "name": "Armin Ronacher",
137
+ "email": "armin.ronacher@active-4.com",
138
+ "role": "Project Founder"
139
+ },
140
+ {
141
+ "name": "Twig Team",
142
+ "homepage": "http://twig.sensiolabs.org/contributors",
143
+ "role": "Contributors"
144
+ }
145
+ ],
146
+ "description": "Twig, the flexible, fast, and secure template language for PHP",
147
+ "homepage": "http://twig.sensiolabs.org",
148
+ "keywords": [
149
+ "templating"
150
+ ]
151
+ },
152
  {
153
  "name": "symfony/polyfill-mbstring",
154
+ "version": "v1.8.0",
155
+ "version_normalized": "1.8.0.0",
156
  "source": {
157
  "type": "git",
158
  "url": "https://github.com/symfony/polyfill-mbstring.git",
159
+ "reference": "3296adf6a6454a050679cde90f95350ad604b171"
160
  },
161
  "dist": {
162
  "type": "zip",
163
+ "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/3296adf6a6454a050679cde90f95350ad604b171",
164
+ "reference": "3296adf6a6454a050679cde90f95350ad604b171",
165
  "shasum": ""
166
  },
167
  "require": {
170
  "suggest": {
171
  "ext-mbstring": "For best performance"
172
  },
173
+ "time": "2018-04-26T10:06:28+00:00",
174
  "type": "library",
175
  "extra": {
176
  "branch-alias": {
177
+ "dev-master": "1.8-dev"
178
  }
179
  },
180
  "installation-source": "dist",
212
  },
213
  {
214
  "name": "symfony/translation",
215
+ "version": "v2.8.41",
216
+ "version_normalized": "2.8.41.0",
217
  "source": {
218
  "type": "git",
219
  "url": "https://github.com/symfony/translation.git",
220
+ "reference": "c6a27966a92fa361bf2c3a938abc6dee91f7ad67"
221
  },
222
  "dist": {
223
  "type": "zip",
224
+ "url": "https://api.github.com/repos/symfony/translation/zipball/c6a27966a92fa361bf2c3a938abc6dee91f7ad67",
225
+ "reference": "c6a27966a92fa361bf2c3a938abc6dee91f7ad67",
226
  "shasum": ""
227
  },
228
  "require": {
239
  "symfony/yaml": "~2.2|~3.0.0"
240
  },
241
  "suggest": {
242
+ "psr/log-implementation": "To use logging capability in translator",
243
  "symfony/config": "",
244
  "symfony/yaml": ""
245
  },
246
+ "time": "2018-05-21T09:59:10+00:00",
247
  "type": "library",
248
  "extra": {
249
  "branch-alias": {
278
  },
279
  {
280
  "name": "nesbot/carbon",
281
+ "version": "1.29.2",
282
+ "version_normalized": "1.29.2.0",
283
  "source": {
284
  "type": "git",
285
  "url": "https://github.com/briannesbitt/Carbon.git",
286
+ "reference": "ed6aa898982f441ccc9b2acdec51490f2bc5d337"
287
  },
288
  "dist": {
289
  "type": "zip",
290
+ "url": "https://api.github.com/repos/briannesbitt/Carbon/zipball/ed6aa898982f441ccc9b2acdec51490f2bc5d337",
291
+ "reference": "ed6aa898982f441ccc9b2acdec51490f2bc5d337",
292
  "shasum": ""
293
  },
294
  "require": {
299
  "friendsofphp/php-cs-fixer": "~2",
300
  "phpunit/phpunit": "^4.8.35 || ^5.7"
301
  },
302
+ "time": "2018-05-29T15:23:46+00:00",
303
  "type": "library",
 
 
 
 
 
304
  "installation-source": "dist",
305
  "autoload": {
306
  "psr-4": {
307
+ "": "src/"
308
  }
309
  },
310
  "notification-url": "https://packagist.org/downloads/",
325
  "datetime",
326
  "time"
327
  ]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
328
  }
329
  ]
src/common/lib/vendor/nesbot/carbon/src/Carbon/Carbon.php CHANGED
@@ -18,6 +18,7 @@ use DateTime;
18
  use DateTimeInterface;
19
  use DateTimeZone;
20
  use InvalidArgumentException;
 
21
  use Symfony\Component\Translation\TranslatorInterface;
22
 
23
  /**
@@ -51,8 +52,13 @@ use Symfony\Component\Translation\TranslatorInterface;
51
  * @property-read string $timezoneName
52
  * @property-read string $tzName
53
  */
54
- class Carbon extends DateTime
55
  {
 
 
 
 
 
56
  /**
57
  * The day constants.
58
  */
@@ -87,6 +93,7 @@ class Carbon extends DateTime
87
  const MONTHS_PER_YEAR = 12;
88
  const MONTHS_PER_QUARTER = 3;
89
  const WEEKS_PER_YEAR = 52;
 
90
  const DAYS_PER_WEEK = 7;
91
  const HOURS_PER_DAY = 24;
92
  const MINUTES_PER_HOUR = 60;
@@ -227,6 +234,20 @@ class Carbon extends DateTime
227
  */
228
  protected static $lastErrors;
229
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
230
  /**
231
  * Will UTF8 encoding be used to print localized date/time ?
232
  *
@@ -255,6 +276,53 @@ class Carbon extends DateTime
255
  */
256
  protected static $yearsOverflow = true;
257
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
258
  /**
259
  * Add microseconds to now on PHP < 7.1 and 7.1.3 if set to true,
260
  * let microseconds to 0 on those PHP versions if false.
@@ -341,6 +409,26 @@ class Carbon extends DateTime
341
  return static::$yearsOverflow;
342
  }
343
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
344
  /**
345
  * Creates a DateTimeZone from a string, DateTimeZone or integer offset.
346
  *
@@ -371,13 +459,25 @@ class Carbon extends DateTime
371
  $object = $tzName;
372
  }
373
 
374
- $tz = @timezone_open((string) $object);
375
 
376
- if ($tz === false) {
377
- throw new InvalidArgumentException('Unknown or bad timezone ('.$object.')');
378
  }
379
 
380
- return $tz;
 
 
 
 
 
 
 
 
 
 
 
 
381
  }
382
 
383
  ///////////////////////////////////////////////////////////////////
@@ -446,16 +546,18 @@ class Carbon extends DateTime
446
  /**
447
  * Create a Carbon instance from a DateTime one.
448
  *
449
- * @param \DateTime $date
450
  *
451
  * @return static
452
  */
453
- public static function instance(DateTime $date)
454
  {
455
  if ($date instanceof static) {
456
  return clone $date;
457
  }
458
 
 
 
459
  return new static($date->format('Y-m-d H:i:s.u'), $date->getTimezone());
460
  }
461
 
@@ -616,7 +718,7 @@ class Carbon extends DateTime
616
  $year = 9999;
617
  }
618
 
619
- $instance = static::createFromFormat('Y-n-j G:i:s', sprintf('%s-%s-%s %s:%02s:%02s', $year, $month, $day, $hour, $minute, $second), $tz);
620
 
621
  if ($fixYear !== null) {
622
  $instance->addYears($fixYear);
@@ -744,6 +846,13 @@ class Carbon extends DateTime
744
  return static::today($tz)->setTimeFromTimeString($time);
745
  }
746
 
 
 
 
 
 
 
 
747
  /**
748
  * Create a Carbon instance from a specific format.
749
  *
@@ -757,15 +866,32 @@ class Carbon extends DateTime
757
  */
758
  public static function createFromFormat($format, $time, $tz = null)
759
  {
760
- if ($tz !== null) {
761
- $date = parent::createFromFormat($format, $time, static::safeCreateDateTimeZone($tz));
762
- } else {
763
- $date = parent::createFromFormat($format, $time);
764
- }
765
-
766
  $lastErrors = parent::getLastErrors();
767
 
768
- if ($date instanceof DateTime) {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
769
  $instance = static::instance($date);
770
  $instance::setLastErrors($lastErrors);
771
 
@@ -834,6 +960,32 @@ class Carbon extends DateTime
834
  return new static('@'.$timestamp);
835
  }
836
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
837
  /**
838
  * Get a copy of the instance.
839
  *
@@ -854,6 +1006,23 @@ class Carbon extends DateTime
854
  return static::now($this->getTimezone());
855
  }
856
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
857
  /**
858
  * Return the Carbon instance passed through, a now instance in the same timezone
859
  * if null given or parse the input if string given.
@@ -872,12 +1041,7 @@ class Carbon extends DateTime
872
  return static::parse($date, $this->getTimezone());
873
  }
874
 
875
- if (!$date instanceof DateTime && !$date instanceof DateTimeInterface) {
876
- throw new InvalidArgumentException(
877
- 'Expected null, string, DateTime or DateTimeInterface, '.
878
- (is_object($date) ? get_class($date) : gettype($date)).' given'
879
- );
880
- }
881
 
882
  return $date instanceof self ? $date : static::instance($date);
883
  }
@@ -1202,6 +1366,38 @@ class Carbon extends DateTime
1202
  return $this;
1203
  }
1204
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1205
  /**
1206
  * Get the days of the week
1207
  *
@@ -1872,7 +2068,7 @@ class Carbon extends DateTime
1872
  *
1873
  * @param \Carbon\Carbon|\DateTimeInterface|mixed $date1
1874
  * @param \Carbon\Carbon|\DateTimeInterface|mixed $date2
1875
- * @param bool $equal Indicates if a > and < comparison should be used or <= or >=
1876
  *
1877
  * @return bool
1878
  */
@@ -1974,7 +2170,7 @@ class Carbon extends DateTime
1974
  }
1975
 
1976
  /**
1977
- * Determines if the instance is a weekday
1978
  *
1979
  * @return bool
1980
  */
@@ -1984,7 +2180,7 @@ class Carbon extends DateTime
1984
  }
1985
 
1986
  /**
1987
- * Determines if the instance is a weekend day
1988
  *
1989
  * @return bool
1990
  */
@@ -1994,7 +2190,7 @@ class Carbon extends DateTime
1994
  }
1995
 
1996
  /**
1997
- * Determines if the instance is yesterday
1998
  *
1999
  * @return bool
2000
  */
@@ -2004,7 +2200,7 @@ class Carbon extends DateTime
2004
  }
2005
 
2006
  /**
2007
- * Determines if the instance is today
2008
  *
2009
  * @return bool
2010
  */
@@ -2014,7 +2210,7 @@ class Carbon extends DateTime
2014
  }
2015
 
2016
  /**
2017
- * Determines if the instance is tomorrow
2018
  *
2019
  * @return bool
2020
  */
@@ -2024,7 +2220,7 @@ class Carbon extends DateTime
2024
  }
2025
 
2026
  /**
2027
- * Determines if the instance is within the next week
2028
  *
2029
  * @return bool
2030
  */
@@ -2034,7 +2230,7 @@ class Carbon extends DateTime
2034
  }
2035
 
2036
  /**
2037
- * Determines if the instance is within the last week
2038
  *
2039
  * @return bool
2040
  */
@@ -2044,7 +2240,27 @@ class Carbon extends DateTime
2044
  }
2045
 
2046
  /**
2047
- * Determines if the instance is within the next month
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2048
  *
2049
  * @return bool
2050
  */
@@ -2054,7 +2270,7 @@ class Carbon extends DateTime
2054
  }
2055
 
2056
  /**
2057
- * Determines if the instance is within the last month
2058
  *
2059
  * @return bool
2060
  */
@@ -2064,7 +2280,7 @@ class Carbon extends DateTime
2064
  }
2065
 
2066
  /**
2067
- * Determines if the instance is within next year
2068
  *
2069
  * @return bool
2070
  */
@@ -2074,7 +2290,7 @@ class Carbon extends DateTime
2074
  }
2075
 
2076
  /**
2077
- * Determines if the instance is within the previous year
2078
  *
2079
  * @return bool
2080
  */
@@ -2084,7 +2300,7 @@ class Carbon extends DateTime
2084
  }
2085
 
2086
  /**
2087
- * Determines if the instance is in the future, ie. greater (after) than now
2088
  *
2089
  * @return bool
2090
  */
@@ -2094,7 +2310,7 @@ class Carbon extends DateTime
2094
  }
2095
 
2096
  /**
2097
- * Determines if the instance is in the past, ie. less (before) than now
2098
  *
2099
  * @return bool
2100
  */
@@ -2104,7 +2320,7 @@ class Carbon extends DateTime
2104
  }
2105
 
2106
  /**
2107
- * Determines if the instance is a leap year
2108
  *
2109
  * @return bool
2110
  */
@@ -2139,15 +2355,13 @@ class Carbon extends DateTime
2139
  {
2140
  $date = $date ?: static::now($this->tz);
2141
 
2142
- if (!($date instanceof DateTime) && !($date instanceof DateTimeInterface)) {
2143
- throw new InvalidArgumentException('Expected DateTime (or instanceof) object as argument.');
2144
- }
2145
 
2146
  return $this->format($format) === $date->format($format);
2147
  }
2148
 
2149
  /**
2150
- * Determines if the instance is in the current year
2151
  *
2152
  * @return bool
2153
  */
@@ -2169,40 +2383,152 @@ class Carbon extends DateTime
2169
  }
2170
 
2171
  /**
2172
- * Determines if the instance is in the current month
2173
  *
2174
  * @return bool
2175
  */
2176
- public function isCurrentMonth()
2177
  {
2178
- return $this->isSameMonth();
2179
  }
2180
 
2181
  /**
2182
- * Checks if the passed in date is in the same month as the instance month (and year if needed).
2183
  *
2184
  * @param \Carbon\Carbon|\DateTimeInterface|null $date The instance to compare with or null to use current day.
2185
  * @param bool $ofSameYear Check if it is the same month in the same year.
2186
  *
2187
  * @return bool
2188
  */
2189
- public function isSameMonth($date = null, $ofSameYear = false)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2190
  {
 
 
2191
  return $this->isSameAs($ofSameYear ? 'Y-m' : 'm', $date);
2192
  }
2193
 
2194
  /**
2195
- * Checks if the passed in date is the same day as the instance current day.
2196
  *
2197
- * @param \Carbon\Carbon|\DateTimeInterface $date
 
 
 
 
 
 
 
 
 
 
2198
  *
2199
  * @return bool
2200
  */
2201
- public function isSameDay($date)
2202
  {
2203
  return $this->isSameAs('Y-m-d', $date);
2204
  }
2205
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2206
  /**
2207
  * Checks if this day is a specific day of the week.
2208
  *
@@ -2285,6 +2611,78 @@ class Carbon extends DateTime
2285
  return $this->dayOfWeek === static::SATURDAY;
2286
  }
2287
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2288
  /**
2289
  * Checks if the (date)time string is in a given format.
2290
  *
@@ -3178,6 +3576,19 @@ class Carbon extends DateTime
3178
  /////////////////////////// DIFFERENCES ///////////////////////////
3179
  ///////////////////////////////////////////////////////////////////
3180
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3181
  /**
3182
  * Get the difference in years
3183
  *
@@ -3283,7 +3694,7 @@ class Carbon extends DateTime
3283
  }
3284
 
3285
  $period = new DatePeriod($start, $ci, $end);
3286
- $values = array_filter(iterator_to_array($period), function (DateTime $date) use ($callback) {
3287
  return call_user_func($callback, Carbon::instance($date));
3288
  });
3289
 
@@ -3512,7 +3923,14 @@ class Carbon extends DateTime
3512
  }
3513
 
3514
  if (count($interval) === 0) {
3515
- $count = 1;
 
 
 
 
 
 
 
3516
  $unit = $short ? 's' : 'second';
3517
  $interval[] = static::translator()->transChoice($unit, $count, array(':count' => $count));
3518
  }
@@ -3531,6 +3949,22 @@ class Carbon extends DateTime
3531
  $transId = $isNow ? ($isFuture ? 'from_now' : 'ago') : ($isFuture ? 'after' : 'before');
3532
 
3533
  if ($parts === 1) {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3534
  // Some langs have special pluralization for past and future tense.
3535
  $key = $unit.'_'.$transId;
3536
  $count = isset($count) ? $count : 1;
@@ -4024,27 +4458,9 @@ class Carbon extends DateTime
4024
  return $this->addSeconds((int) ($this->diffInSeconds($this->resolveCarbon($date), false) / 2));
4025
  }
4026
 
4027
- /**
4028
- * Check if its the birthday. Compares the date/month values of the two dates.
4029
- *
4030
- * @param \Carbon\Carbon|\DateTimeInterface|null $date The instance to compare with or null to use current day.
4031
- *
4032
- * @return bool
4033
- */
4034
- public function isBirthday($date = null)
4035
- {
4036
- return $this->isSameAs('md', $date);
4037
- }
4038
-
4039
- /**
4040
- * Check if today is the last day of the Month
4041
- *
4042
- * @return bool
4043
- */
4044
- public function isLastOfMonth()
4045
- {
4046
- return $this->day === $this->daysInMonth;
4047
- }
4048
 
4049
  /**
4050
  * Return a serialized string of the instance.
@@ -4087,4 +4503,143 @@ class Carbon extends DateTime
4087
  {
4088
  return static::instance(parent::__set_state($array));
4089
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4090
  }
18
  use DateTimeInterface;
19
  use DateTimeZone;
20
  use InvalidArgumentException;
21
+ use JsonSerializable;
22
  use Symfony\Component\Translation\TranslatorInterface;
23
 
24
  /**
52
  * @property-read string $timezoneName
53
  * @property-read string $tzName
54
  */
55
+ class Carbon extends DateTime implements JsonSerializable
56
  {
57
+ const NO_ZERO_DIFF = 01;
58
+ const JUST_NOW = 02;
59
+ const ONE_DAY_WORDS = 04;
60
+ const TWO_DAY_WORDS = 010;
61
+
62
  /**
63
  * The day constants.
64
  */
93
  const MONTHS_PER_YEAR = 12;
94
  const MONTHS_PER_QUARTER = 3;
95
  const WEEKS_PER_YEAR = 52;
96
+ const WEEKS_PER_MONTH = 4;
97
  const DAYS_PER_WEEK = 7;
98
  const HOURS_PER_DAY = 24;
99
  const MINUTES_PER_HOUR = 60;
234
  */
235
  protected static $lastErrors;
236
 
237
+ /**
238
+ * The custom Carbon JSON serializer.
239
+ *
240
+ * @var callable|null
241
+ */
242
+ protected static $serializer;
243
+
244
+ /**
245
+ * The registered string macros.
246
+ *
247
+ * @var array
248
+ */
249
+ protected static $localMacros = array();
250
+
251
  /**
252
  * Will UTF8 encoding be used to print localized date/time ?
253
  *
276
  */
277
  protected static $yearsOverflow = true;
278
 
279
+ /**
280
+ * Indicates if years are compared with month by default so isSameMonth and isSameQuarter have $ofSameYear set
281
+ * to true by default.
282
+ *
283
+ * @var bool
284
+ */
285
+ protected static $compareYearWithMonth = false;
286
+
287
+ /**
288
+ * Options for diffForHumans().
289
+ *
290
+ * @var int
291
+ */
292
+ protected static $humanDiffOptions = self::NO_ZERO_DIFF;
293
+
294
+ /**
295
+ * @param int $humanDiffOptions
296
+ */
297
+ public static function setHumanDiffOptions($humanDiffOptions)
298
+ {
299
+ static::$humanDiffOptions = $humanDiffOptions;
300
+ }
301
+
302
+ /**
303
+ * @param int $humanDiffOption
304
+ */
305
+ public static function enableHumanDiffOption($humanDiffOption)
306
+ {
307
+ static::$humanDiffOptions = static::getHumanDiffOptions() | $humanDiffOption;
308
+ }
309
+
310
+ /**
311
+ * @param int $humanDiffOption
312
+ */
313
+ public static function disableHumanDiffOption($humanDiffOption)
314
+ {
315
+ static::$humanDiffOptions = static::getHumanDiffOptions() & ~$humanDiffOption;
316
+ }
317
+
318
+ /**
319
+ * @return int
320
+ */
321
+ public static function getHumanDiffOptions()
322
+ {
323
+ return static::$humanDiffOptions;
324
+ }
325
+
326
  /**
327
  * Add microseconds to now on PHP < 7.1 and 7.1.3 if set to true,
328
  * let microseconds to 0 on those PHP versions if false.
409
  return static::$yearsOverflow;
410
  }
411
 
412
+ /**
413
+ * Get the month comparison default behavior.
414
+ *
415
+ * @return bool
416
+ */
417
+ public static function compareYearWithMonth($compareYearWithMonth = true)
418
+ {
419
+ static::$compareYearWithMonth = $compareYearWithMonth;
420
+ }
421
+
422
+ /**
423
+ * Get the month comparison default behavior.
424
+ *
425
+ * @return bool
426
+ */
427
+ public static function shouldCompareYearWithMonth()
428
+ {
429
+ return static::$compareYearWithMonth;
430
+ }
431
+
432
  /**
433
  * Creates a DateTimeZone from a string, DateTimeZone or integer offset.
434
  *
459
  $object = $tzName;
460
  }
461
 
462
+ $tz = @timezone_open($object = (string) $object);
463
 
464
+ if ($tz !== false) {
465
+ return $tz;
466
  }
467
 
468
+ // Work-around for a bug fixed in PHP 5.5.10 https://bugs.php.net/bug.php?id=45528
469
+ // See: https://stackoverflow.com/q/14068594/2646927
470
+ // @codeCoverageIgnoreStart
471
+ if (strpos($object, ':') !== false) {
472
+ try {
473
+ return static::createFromFormat('O', $object)->getTimezone();
474
+ } catch (InvalidArgumentException $e) {
475
+ //
476
+ }
477
+ }
478
+ // @codeCoverageIgnoreEnd
479
+
480
+ throw new InvalidArgumentException('Unknown or bad timezone ('.$object.')');
481
  }
482
 
483
  ///////////////////////////////////////////////////////////////////
546
  /**
547
  * Create a Carbon instance from a DateTime one.
548
  *
549
+ * @param \DateTime|\DateTimeInterface $date
550
  *
551
  * @return static
552
  */
553
+ public static function instance($date)
554
  {
555
  if ($date instanceof static) {
556
  return clone $date;
557
  }
558
 
559
+ static::expectDateTime($date);
560
+
561
  return new static($date->format('Y-m-d H:i:s.u'), $date->getTimezone());
562
  }
563
 
718
  $year = 9999;
719
  }
720
 
721
+ $instance = static::createFromFormat('!Y-n-j G:i:s', sprintf('%s-%s-%s %s:%02s:%02s', $year, $month, $day, $hour, $minute, $second), $tz);
722
 
723
  if ($fixYear !== null) {
724
  $instance->addYears($fixYear);
846
  return static::today($tz)->setTimeFromTimeString($time);
847
  }
848
 
849
+ private static function createFromFormatAndTimezone($format, $time, $tz)
850
+ {
851
+ return $tz !== null
852
+ ? parent::createFromFormat($format, $time, static::safeCreateDateTimeZone($tz))
853
+ : parent::createFromFormat($format, $time);
854
+ }
855
+
856
  /**
857
  * Create a Carbon instance from a specific format.
858
  *
866
  */
867
  public static function createFromFormat($format, $time, $tz = null)
868
  {
869
+ // First attempt to create an instance, so that error messages are based on the unmodified format.
870
+ $date = self::createFromFormatAndTimezone($format, $time, $tz);
 
 
 
 
871
  $lastErrors = parent::getLastErrors();
872
 
873
+ if (($mock = static::getTestNow()) && ($date instanceof DateTime || $date instanceof DateTimeInterface)) {
874
+ // Set timezone from mock if custom timezone was neither given directly nor as a part of format.
875
+ // First let's skip the part that will be ignored by the parser.
876
+ $nonEscaped = '(?<!\\\\)(\\\\{2})*';
877
+
878
+ $nonIgnored = preg_replace("/^.*{$nonEscaped}!/s", '', $format);
879
+
880
+ if ($tz === null && !preg_match("/{$nonEscaped}[eOPT]/", $nonIgnored)) {
881
+ $tz = $mock->getTimezone();
882
+ }
883
+
884
+ // Prepend mock datetime only if the format does not contain non escaped unix epoch reset flag.
885
+ if (!preg_match("/{$nonEscaped}[!|]/", $format)) {
886
+ $format = static::MOCK_DATETIME_FORMAT.' '.$format;
887
+ $time = $mock->format(static::MOCK_DATETIME_FORMAT).' '.$time;
888
+ }
889
+
890
+ // Regenerate date from the modified format to base result on the mocked instance instead of now.
891
+ $date = self::createFromFormatAndTimezone($format, $time, $tz);
892
+ }
893
+
894
+ if ($date instanceof DateTime || $date instanceof DateTimeInterface) {
895
  $instance = static::instance($date);
896
  $instance::setLastErrors($lastErrors);
897
 
960
  return new static('@'.$timestamp);
961
  }
962
 
963
+ /**
964
+ * Make a Carbon instance from given variable if possible.
965
+ *
966
+ * Always return a new instance. Parse only strings and only these likely to be dates (skip intervals
967
+ * and recurrences). Throw an exception for invalid format, but otherwise return null.
968
+ *
969
+ * @param mixed $var
970
+ *
971
+ * @return static|null
972
+ */
973
+ public static function make($var)
974
+ {
975
+ if ($var instanceof DateTime || $var instanceof DateTimeInterface) {
976
+ return static::instance($var);
977
+ }
978
+
979
+ if (is_string($var)) {
980
+ $var = trim($var);
981
+ $first = substr($var, 0, 1);
982
+
983
+ if (is_string($var) && $first !== 'P' && $first !== 'R' && preg_match('/[a-z0-9]/i', $var)) {
984
+ return static::parse($var);
985
+ }
986
+ }
987
+ }
988
+
989
  /**
990
  * Get a copy of the instance.
991
  *
1006
  return static::now($this->getTimezone());
1007
  }
1008
 
1009
+ /**
1010
+ * Throws an exception if the given object is not a DateTime and does not implement DateTimeInterface.
1011
+ *
1012
+ * @param mixed $date
1013
+ *
1014
+ * @throws \InvalidArgumentException
1015
+ */
1016
+ protected static function expectDateTime($date)
1017
+ {
1018
+ if (!$date instanceof DateTime && !$date instanceof DateTimeInterface) {
1019
+ throw new InvalidArgumentException(
1020
+ 'Expected null, string, DateTime or DateTimeInterface, '.
1021
+ (is_object($date) ? get_class($date) : gettype($date)).' given'
1022
+ );
1023
+ }
1024
+ }
1025
+
1026
  /**
1027
  * Return the Carbon instance passed through, a now instance in the same timezone
1028
  * if null given or parse the input if string given.
1041
  return static::parse($date, $this->getTimezone());
1042
  }
1043
 
1044
+ static::expectDateTime($date);
 
 
 
 
 
1045
 
1046
  return $date instanceof self ? $date : static::instance($date);
1047
  }
1366
  return $this;
1367
  }
1368
 
1369
+ /**
1370
+ * Set the year, month, and date for this instance to that of the passed instance.
1371
+ *
1372
+ * @param \Carbon\Carbon|\DateTimeInterface $date
1373
+ *
1374
+ * @return static
1375
+ */
1376
+ public function setDateFrom($date)
1377
+ {
1378
+ $date = static::instance($date);
1379
+
1380
+ $this->setDate($date->year, $date->month, $date->day);
1381
+
1382
+ return $this;
1383
+ }
1384
+
1385
+ /**
1386
+ * Set the hour, day, and time for this instance to that of the passed instance.
1387
+ *
1388
+ * @param \Carbon\Carbon|\DateTimeInterface $date
1389
+ *
1390
+ * @return static
1391
+ */
1392
+ public function setTimeFrom($date)
1393
+ {
1394
+ $date = static::instance($date);
1395
+
1396
+ $this->setTime($date->hour, $date->minute, $date->second);
1397
+
1398
+ return $this;
1399
+ }
1400
+
1401
  /**
1402
  * Get the days of the week
1403
  *
2068
  *
2069
  * @param \Carbon\Carbon|\DateTimeInterface|mixed $date1
2070
  * @param \Carbon\Carbon|\DateTimeInterface|mixed $date2
2071
+ * @param bool $equal Indicates if an equal to comparison should be done
2072
  *
2073
  * @return bool
2074
  */
2170
  }
2171
 
2172
  /**
2173
+ * Determines if the instance is a weekday.
2174
  *
2175
  * @return bool
2176
  */
2180
  }
2181
 
2182
  /**
2183
+ * Determines if the instance is a weekend day.
2184
  *
2185
  * @return bool
2186
  */
2190
  }
2191
 
2192
  /**
2193
+ * Determines if the instance is yesterday.
2194
  *
2195
  * @return bool
2196
  */
2200
  }
2201
 
2202
  /**
2203
+ * Determines if the instance is today.
2204
  *
2205
  * @return bool
2206
  */
2210
  }
2211
 
2212
  /**
2213
+ * Determines if the instance is tomorrow.
2214
  *
2215
  * @return bool
2216
  */
2220
  }
2221
 
2222
  /**
2223
+ * Determines if the instance is within the next week.
2224
  *
2225
  * @return bool
2226
  */
2230
  }
2231
 
2232
  /**
2233
+ * Determines if the instance is within the last week.
2234
  *
2235
  * @return bool
2236
  */
2240
  }
2241
 
2242
  /**
2243
+ * Determines if the instance is within the next quarter.
2244
+ *
2245
+ * @return bool
2246
+ */
2247
+ public function isNextQuarter()
2248
+ {
2249
+ return $this->quarter === $this->nowWithSameTz()->addQuarter()->quarter;
2250
+ }
2251
+
2252
+ /**
2253
+ * Determines if the instance is within the last quarter.
2254
+ *
2255
+ * @return bool
2256
+ */
2257
+ public function isLastQuarter()
2258
+ {
2259
+ return $this->quarter === $this->nowWithSameTz()->subQuarter()->quarter;
2260
+ }
2261
+
2262
+ /**
2263
+ * Determines if the instance is within the next month.
2264
  *
2265
  * @return bool
2266
  */
2270
  }
2271
 
2272
  /**
2273
+ * Determines if the instance is within the last month.
2274
  *
2275
  * @return bool
2276
  */
2280
  }
2281
 
2282
  /**
2283
+ * Determines if the instance is within next year.
2284
  *
2285
  * @return bool
2286
  */
2290
  }
2291
 
2292
  /**
2293
+ * Determines if the instance is within the previous year.
2294
  *
2295
  * @return bool
2296
  */
2300
  }
2301
 
2302
  /**
2303
+ * Determines if the instance is in the future, ie. greater (after) than now.
2304
  *
2305
  * @return bool
2306
  */
2310
  }
2311
 
2312
  /**
2313
+ * Determines if the instance is in the past, ie. less (before) than now.
2314
  *
2315
  * @return bool
2316
  */
2320
  }
2321
 
2322
  /**
2323
+ * Determines if the instance is a leap year.
2324
  *
2325
  * @return bool
2326
  */
2355
  {
2356
  $date = $date ?: static::now($this->tz);
2357
 
2358
+ static::expectDateTime($date);
 
 
2359
 
2360
  return $this->format($format) === $date->format($format);
2361
  }
2362
 
2363
  /**
2364
+ * Determines if the instance is in the current year.
2365
  *
2366
  * @return bool
2367
  */
2383
  }
2384
 
2385
  /**
2386
+ * Determines if the instance is in the current month.
2387
  *
2388
  * @return bool
2389
  */
2390
+ public function isCurrentQuarter()
2391
  {
2392
+ return $this->isSameQuarter();
2393
  }
2394
 
2395
  /**
2396
+ * Checks if the passed in date is in the same quarter as the instance quarter (and year if needed).
2397
  *
2398
  * @param \Carbon\Carbon|\DateTimeInterface|null $date The instance to compare with or null to use current day.
2399
  * @param bool $ofSameYear Check if it is the same month in the same year.
2400
  *
2401
  * @return bool
2402
  */
2403
+ public function isSameQuarter($date = null, $ofSameYear = null)
2404
+ {
2405
+ $date = $date ? static::instance($date) : static::now($this->tz);
2406
+
2407
+ static::expectDateTime($date);
2408
+
2409
+ $ofSameYear = is_null($ofSameYear) ? static::shouldCompareYearWithMonth() : $ofSameYear;
2410
+
2411
+ return $this->quarter === $date->quarter && (!$ofSameYear || $this->isSameYear($date));
2412
+ }
2413
+
2414
+ /**
2415
+ * Determines if the instance is in the current month.
2416
+ *
2417
+ * @param bool $ofSameYear Check if it is the same month in the same year.
2418
+ *
2419
+ * @return bool
2420
+ */
2421
+ public function isCurrentMonth($ofSameYear = null)
2422
+ {
2423
+ return $this->isSameMonth($ofSameYear);
2424
+ }
2425
+
2426
+ /**
2427
+ * Checks if the passed in date is in the same month as the instance´s month.
2428
+ *
2429
+ * Note that this defaults to only comparing the month while ignoring the year.
2430
+ * To test if it is the same exact month of the same year, pass in true as the second parameter.
2431
+ *
2432
+ * @param \Carbon\Carbon|\DateTimeInterface|null $date The instance to compare with or null to use the current date.
2433
+ * @param bool $ofSameYear Check if it is the same month in the same year.
2434
+ *
2435
+ * @return bool
2436
+ */
2437
+ public function isSameMonth($date = null, $ofSameYear = null)
2438
  {
2439
+ $ofSameYear = is_null($ofSameYear) ? static::shouldCompareYearWithMonth() : $ofSameYear;
2440
+
2441
  return $this->isSameAs($ofSameYear ? 'Y-m' : 'm', $date);
2442
  }
2443
 
2444
  /**
2445
+ * Determines if the instance is in the current day.
2446
  *
2447
+ * @return bool
2448
+ */
2449
+ public function isCurrentDay()
2450
+ {
2451
+ return $this->isSameDay();
2452
+ }
2453
+
2454
+ /**
2455
+ * Checks if the passed in date is the same exact day as the instance´s day.
2456
+ *
2457
+ * @param \Carbon\Carbon|\DateTimeInterface|null $date The instance to compare with or null to use the current date.
2458
  *
2459
  * @return bool
2460
  */
2461
+ public function isSameDay($date = null)
2462
  {
2463
  return $this->isSameAs('Y-m-d', $date);
2464
  }
2465
 
2466
+ /**
2467
+ * Determines if the instance is in the current hour.
2468
+ *
2469
+ * @return bool
2470
+ */
2471
+ public function isCurrentHour()
2472
+ {
2473
+ return $this->isSameHour();
2474
+ }
2475
+
2476
+ /**
2477
+ * Checks if the passed in date is the same exact hour as the instance´s hour.
2478
+ *
2479
+ * @param \Carbon\Carbon|\DateTimeInterface|null $date The instance to compare with or null to use the current date.
2480
+ *
2481
+ * @return bool
2482
+ */
2483
+ public function isSameHour($date = null)
2484
+ {
2485
+ return $this->isSameAs('Y-m-d H', $date);
2486
+ }
2487
+
2488
+ /**
2489
+ * Determines if the instance is in the current minute.
2490
+ *
2491
+ * @return bool
2492
+ */
2493
+ public function isCurrentMinute()
2494
+ {
2495
+ return $this->isSameMinute();
2496
+ }
2497
+
2498
+ /**
2499
+ * Checks if the passed in date is the same exact minute as the instance´s minute.
2500
+ *
2501
+ * @param \Carbon\Carbon|\DateTimeInterface|null $date The instance to compare with or null to use the current date.
2502
+ *
2503
+ * @return bool
2504
+ */
2505
+ public function isSameMinute($date = null)
2506
+ {
2507
+ return $this->isSameAs('Y-m-d H:i', $date);
2508
+ }
2509
+
2510
+ /**
2511
+ * Determines if the instance is in the current second.
2512
+ *
2513
+ * @return bool
2514
+ */
2515
+ public function isCurrentSecond()
2516
+ {
2517
+ return $this->isSameSecond();
2518
+ }
2519
+
2520
+ /**
2521
+ * Checks if the passed in date is the same exact second as the instance´s second.
2522
+ *
2523
+ * @param \Carbon\Carbon|\DateTimeInterface|null $date The instance to compare with or null to use the current date.
2524
+ *
2525
+ * @return bool
2526
+ */
2527
+ public function isSameSecond($date = null)
2528
+ {
2529
+ return $this->isSameAs('Y-m-d H:i:s', $date);
2530
+ }
2531
+
2532
  /**
2533
  * Checks if this day is a specific day of the week.
2534
  *
2611
  return $this->dayOfWeek === static::SATURDAY;
2612
  }
2613
 
2614
+ /**
2615
+ * Check if its the birthday. Compares the date/month values of the two dates.
2616
+ *
2617
+ * @param \Carbon\Carbon|\DateTimeInterface|null $date The instance to compare with or null to use current day.
2618
+ *
2619
+ * @return bool
2620
+ */
2621
+ public function isBirthday($date = null)
2622
+ {
2623
+ return $this->isSameAs('md', $date);
2624
+ }
2625
+
2626
+ /**
2627
+ * Check if today is the last day of the Month
2628
+ *
2629
+ * @return bool
2630
+ */
2631
+ public function isLastOfMonth()
2632
+ {
2633
+ return $this->day === $this->daysInMonth;
2634
+ }
2635
+
2636
+ /**
2637
+ * Check if the instance is start of day / midnight.
2638
+ *
2639
+ * @param bool $checkMicroseconds check time at microseconds precision
2640
+ * /!\ Warning, this is not reliable with PHP < 7.1.4
2641
+ *
2642
+ * @return bool
2643
+ */
2644
+ public function isStartOfDay($checkMicroseconds = false)
2645
+ {
2646
+ return $checkMicroseconds
2647
+ ? $this->format('H:i:s.u') === '00:00:00.000000'
2648
+ : $this->format('H:i:s') === '00:00:00';
2649
+ }
2650
+
2651
+ /**
2652
+ * Check if the instance is end of day.
2653
+ *
2654
+ * @param bool $checkMicroseconds check time at microseconds precision
2655
+ * /!\ Warning, this is not reliable with PHP < 7.1.4
2656
+ *
2657
+ * @return bool
2658
+ */
2659
+ public function isEndOfDay($checkMicroseconds = false)
2660
+ {
2661
+ return $checkMicroseconds
2662
+ ? $this->format('H:i:s.u') === '23:59:59.999999'
2663
+ : $this->format('H:i:s') === '23:59:59';
2664
+ }
2665
+
2666
+ /**
2667
+ * Check if the instance is start of day / midnight.
2668
+ *
2669
+ * @return bool
2670
+ */
2671
+ public function isMidnight()
2672
+ {
2673
+ return $this->isStartOfDay();
2674
+ }
2675
+
2676
+ /**
2677
+ * Check if the instance is midday.
2678
+ *
2679
+ * @return bool
2680
+ */
2681
+ public function isMidday()
2682
+ {
2683
+ return $this->format('G:i:s') === static::$midDayAt.':00:00';
2684
+ }
2685
+
2686
  /**
2687
  * Checks if the (date)time string is in a given format.
2688
  *
3576
  /////////////////////////// DIFFERENCES ///////////////////////////
3577
  ///////////////////////////////////////////////////////////////////
3578
 
3579
+ /**
3580
+ * Get the difference as a CarbonInterval instance
3581
+ *
3582
+ * @param \Carbon\Carbon|\DateTimeInterface|string|null $date
3583
+ * @param bool $absolute Get the absolute of the difference
3584
+ *
3585
+ * @return CarbonInterval
3586
+ */
3587
+ public function diffAsCarbonInterval($date = null, $absolute = true)
3588
+ {
3589
+ return CarbonInterval::instance($this->diff($this->resolveCarbon($date), $absolute));
3590
+ }
3591
+
3592
  /**
3593
  * Get the difference in years
3594
  *
3694
  }
3695
 
3696
  $period = new DatePeriod($start, $ci, $end);
3697
+ $values = array_filter(iterator_to_array($period), function ($date) use ($callback) {
3698
  return call_user_func($callback, Carbon::instance($date));
3699
  });
3700
 
3923
  }
3924
 
3925
  if (count($interval) === 0) {
3926
+ if ($isNow && static::getHumanDiffOptions() & self::JUST_NOW) {
3927
+ $key = 'diff_now';
3928
+ $translation = static::translator()->trans($key);
3929
+ if ($translation !== $key) {
3930
+ return $translation;
3931
+ }
3932
+ }
3933
+ $count = static::getHumanDiffOptions() & self::NO_ZERO_DIFF ? 1 : 0;
3934
  $unit = $short ? 's' : 'second';
3935
  $interval[] = static::translator()->transChoice($unit, $count, array(':count' => $count));
3936
  }
3949
  $transId = $isNow ? ($isFuture ? 'from_now' : 'ago') : ($isFuture ? 'after' : 'before');
3950
 
3951
  if ($parts === 1) {
3952
+ if ($isNow && $unit === 'day') {
3953
+ if ($count === 1 && static::getHumanDiffOptions() & self::ONE_DAY_WORDS) {
3954
+ $key = $isFuture ? 'diff_tomorrow' : 'diff_yesterday';
3955
+ $translation = static::translator()->trans($key);
3956
+ if ($translation !== $key) {
3957
+ return $translation;
3958
+ }
3959
+ }
3960
+ if ($count === 2 && static::getHumanDiffOptions() & self::TWO_DAY_WORDS) {
3961
+ $key = $isFuture ? 'diff_after_tomorrow' : 'diff_before_yesterday';
3962
+ $translation = static::translator()->trans($key);
3963
+ if ($translation !== $key) {
3964
+ return $translation;
3965
+ }
3966
+ }
3967
+ }
3968
  // Some langs have special pluralization for past and future tense.
3969
  $key = $unit.'_'.$transId;
3970
  $count = isset($count) ? $count : 1;
4458
  return $this->addSeconds((int) ($this->diffInSeconds($this->resolveCarbon($date), false) / 2));
4459
  }
4460
 
4461
+ ///////////////////////////////////////////////////////////////////
4462
+ /////////////////////////// SERIALIZATION /////////////////////////
4463
+ ///////////////////////////////////////////////////////////////////
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4464
 
4465
  /**
4466
  * Return a serialized string of the instance.
4503
  {
4504
  return static::instance(parent::__set_state($array));
4505
  }
4506
+
4507
+ /**
4508
+ * Prepare the object for JSON serialization.
4509
+ *
4510
+ * @return array|string
4511
+ */
4512
+ public function jsonSerialize()
4513
+ {
4514
+ if (static::$serializer) {
4515
+ return call_user_func(static::$serializer, $this);
4516
+ }
4517
+
4518
+ $carbon = $this;
4519
+
4520
+ return call_user_func(function () use ($carbon) {
4521
+ return get_object_vars($carbon);
4522
+ });
4523
+ }
4524
+
4525
+ /**
4526
+ * JSON serialize all Carbon instances using the given callback.
4527
+ *
4528
+ * @param callable $callback
4529
+ *
4530
+ * @return void
4531
+ */
4532
+ public static function serializeUsing($callback)
4533
+ {
4534
+ static::$serializer = $callback;
4535
+ }
4536
+
4537
+ ///////////////////////////////////////////////////////////////////
4538
+ /////////////////////////////// MACRO /////////////////////////////
4539
+ ///////////////////////////////////////////////////////////////////
4540
+
4541
+ /**
4542
+ * Register a custom macro.
4543
+ *
4544
+ * @param string $name
4545
+ * @param object|callable $macro
4546
+ *
4547
+ * @return void
4548
+ */
4549
+ public static function macro($name, $macro)
4550
+ {
4551
+ static::$localMacros[$name] = $macro;
4552
+ }
4553
+
4554
+ /**
4555
+ * Mix another object into the class.
4556
+ *
4557
+ * @param object $mixin
4558
+ *
4559
+ * @return void
4560
+ */
4561
+ public static function mixin($mixin)
4562
+ {
4563
+ $reflection = new \ReflectionClass($mixin);
4564
+ $methods = $reflection->getMethods(
4565
+ \ReflectionMethod::IS_PUBLIC | \ReflectionMethod::IS_PROTECTED
4566
+ );
4567
+
4568
+ foreach ($methods as $method) {
4569
+ $method->setAccessible(true);
4570
+
4571
+ static::macro($method->name, $method->invoke($mixin));
4572
+ }
4573
+ }
4574
+
4575
+ /**
4576
+ * Checks if macro is registered.
4577
+ *
4578
+ * @param string $name
4579
+ *
4580
+ * @return bool
4581
+ */
4582
+ public static function hasMacro($name)
4583
+ {
4584
+ return isset(static::$localMacros[$name]);
4585
+ }
4586
+
4587
+ /**
4588
+ * Dynamically handle calls to the class.
4589
+ *
4590
+ * @param string $method
4591
+ * @param array $parameters
4592
+ *
4593
+ * @throws \BadMethodCallException
4594
+ *
4595
+ * @return mixed
4596
+ */
4597
+ public static function __callStatic($method, $parameters)
4598
+ {
4599
+ if (!static::hasMacro($method)) {
4600
+ throw new \BadMethodCallException("Method $method does not exist.");
4601
+ }
4602
+
4603
+ if (static::$localMacros[$method] instanceof Closure && method_exists('Closure', 'bind')) {
4604
+ return call_user_func_array(Closure::bind(static::$localMacros[$method], null, get_called_class()), $parameters);
4605
+ }
4606
+
4607
+ return call_user_func_array(static::$localMacros[$method], $parameters);
4608
+ }
4609
+
4610
+ /**
4611
+ * Dynamically handle calls to the class.
4612
+ *
4613
+ * @param string $method
4614
+ * @param array $parameters
4615
+ *
4616
+ * @throws \BadMethodCallException|\ReflectionException
4617
+ *
4618
+ * @return mixed
4619
+ */
4620
+ public function __call($method, $parameters)
4621
+ {
4622
+ if (!static::hasMacro($method)) {
4623
+ throw new \BadMethodCallException("Method $method does not exist.");
4624
+ }
4625
+
4626
+ $macro = static::$localMacros[$method];
4627
+
4628
+ $reflexion = new \ReflectionFunction($macro);
4629
+ $reflectionParameters = $reflexion->getParameters();
4630
+ $expectedCount = count($reflectionParameters);
4631
+ $actualCount = count($parameters);
4632
+ if ($expectedCount > $actualCount && $reflectionParameters[$expectedCount - 1]->name === 'self') {
4633
+ for ($i = $actualCount; $i < $expectedCount - 1; $i++) {
4634
+ $parameters[] = $reflectionParameters[$i]->getDefaultValue();
4635
+ }
4636
+ $parameters[] = $this;
4637
+ }
4638
+
4639
+ if ($macro instanceof Closure && method_exists($macro, 'bindTo')) {
4640
+ return call_user_func_array($macro->bindTo($this, get_class($this)), $parameters);
4641
+ }
4642
+
4643
+ return call_user_func_array($macro, $parameters);
4644
+ }
4645
  }
src/common/lib/vendor/nesbot/carbon/src/Carbon/CarbonInterval.php CHANGED
@@ -11,8 +11,12 @@
11
 
12
  namespace Carbon;
13
 
 
14
  use DateInterval;
15
  use InvalidArgumentException;
 
 
 
16
  use Symfony\Component\Translation\TranslatorInterface;
17
 
18
  /**
@@ -29,6 +33,14 @@ use Symfony\Component\Translation\TranslatorInterface;
29
  * @property int $seconds Total seconds of the current interval.
30
  * @property-read int $dayzExcludeWeeks Total days remaining in the final week of the current instance (days % 7).
31
  * @property-read int $daysExcludeWeeks alias of dayzExcludeWeeks
 
 
 
 
 
 
 
 
32
  *
33
  * @method static CarbonInterval years($years = 1) Create instance specifying a number of years.
34
  * @method static CarbonInterval year($years = 1) Alias for years()
@@ -82,12 +94,77 @@ class CarbonInterval extends DateInterval
82
  */
83
  protected static $translator;
84
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
85
  /**
86
  * Before PHP 5.4.20/5.5.4 instead of FALSE days will be set to -99999 when the interval instance
87
- * was created by DateTime:diff().
88
  */
89
  const PHP_DAYS_FALSE = -99999;
90
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
91
  /**
92
  * Determine if the interval was created via DateTime:diff() or not.
93
  *
@@ -126,7 +203,7 @@ class CarbonInterval extends DateInterval
126
  $spec .= $months > 0 ? $months.static::PERIOD_MONTHS : '';
127
 
128
  $specDays = 0;
129
- $specDays += $weeks > 0 ? $weeks * Carbon::DAYS_PER_WEEK : 0;
130
  $specDays += $days > 0 ? $days : 0;
131
 
132
  $spec .= $specDays > 0 ? $specDays.static::PERIOD_DAYS : '';
@@ -147,6 +224,69 @@ class CarbonInterval extends DateInterval
147
  parent::__construct($spec);
148
  }
149
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
150
  /**
151
  * Create a new CarbonInterval instance from specific values.
152
  * This is an alias for the constructor that allows better fluent
@@ -168,6 +308,19 @@ class CarbonInterval extends DateInterval
168
  return new static($years, $months, $weeks, $days, $hours, $minutes, $seconds);
169
  }
170
 
 
 
 
 
 
 
 
 
 
 
 
 
 
171
  /**
172
  * Provide static helpers to create instances. Allows CarbonInterval::years(3).
173
  *
@@ -213,10 +366,16 @@ class CarbonInterval extends DateInterval
213
  case 'second':
214
  return new static(null, null, null, null, null, null, $arg);
215
  }
 
 
 
 
 
 
216
  }
217
 
218
  /**
219
- * Creates a CarbonInterval from string
220
  *
221
  * Format:
222
  *
@@ -278,8 +437,8 @@ class CarbonInterval extends DateInterval
278
  case 'weeks':
279
  case 'w':
280
  $weeks += $intValue;
281
- if ($fraction != 0) {
282
- $parts[] = array(null, $fraction * Carbon::DAYS_PER_WEEK, 'd');
283
  }
284
  break;
285
 
@@ -287,8 +446,8 @@ class CarbonInterval extends DateInterval
287
  case 'days':
288
  case 'd':
289
  $days += $intValue;
290
- if ($fraction != 0) {
291
- $parts[] = array(null, $fraction * Carbon::HOURS_PER_DAY, 'h');
292
  }
293
  break;
294
 
@@ -296,8 +455,8 @@ class CarbonInterval extends DateInterval
296
  case 'hours':
297
  case 'h':
298
  $hours += $intValue;
299
- if ($fraction != 0) {
300
- $parts[] = array(null, $fraction * Carbon::MINUTES_PER_HOUR, 'm');
301
  }
302
  break;
303
 
@@ -305,8 +464,8 @@ class CarbonInterval extends DateInterval
305
  case 'minutes':
306
  case 'm':
307
  $minutes += $intValue;
308
- if ($fraction != 0) {
309
- $seconds += round($fraction * Carbon::SECONDS_PER_MINUTE);
310
  }
311
  break;
312
 
@@ -333,23 +492,45 @@ class CarbonInterval extends DateInterval
333
  *
334
  * @param DateInterval $di
335
  *
336
- * @throws \InvalidArgumentException
337
- *
338
  * @return static
339
  */
340
  public static function instance(DateInterval $di)
341
  {
342
- if (static::wasCreatedFromDiff($di)) {
343
- throw new InvalidArgumentException('Can not instance a DateInterval object created from DateTime::diff().');
344
- }
345
-
346
- $instance = new static($di->y, $di->m, 0, $di->d, $di->h, $di->i, $di->s);
347
  $instance->invert = $di->invert;
348
- $instance->days = $di->days;
349
 
350
  return $instance;
351
  }
352
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
353
  ///////////////////////////////////////////////////////////////////
354
  /////////////////////// LOCALIZATION //////////////////////////////
355
  ///////////////////////////////////////////////////////////////////
@@ -369,7 +550,7 @@ class CarbonInterval extends DateInterval
369
  }
370
 
371
  /**
372
- * Get the translator instance in use
373
  *
374
  * @return \Symfony\Component\Translation\TranslatorInterface
375
  */
@@ -379,7 +560,7 @@ class CarbonInterval extends DateInterval
379
  }
380
 
381
  /**
382
- * Set the translator instance to use
383
  *
384
  * @param TranslatorInterface $translator
385
  */
@@ -389,7 +570,7 @@ class CarbonInterval extends DateInterval
389
  }
390
 
391
  /**
392
- * Get the current translator locale
393
  *
394
  * @return string
395
  */
@@ -399,7 +580,7 @@ class CarbonInterval extends DateInterval
399
  }
400
 
401
  /**
402
- * Set the current translator locale
403
  *
404
  * @param string $locale
405
  */
@@ -413,16 +594,20 @@ class CarbonInterval extends DateInterval
413
  ///////////////////////////////////////////////////////////////////
414
 
415
  /**
416
- * Get a part of the CarbonInterval object
417
  *
418
  * @param string $name
419
  *
420
  * @throws \InvalidArgumentException
421
  *
422
- * @return int
423
  */
424
  public function __get($name)
425
  {
 
 
 
 
426
  switch ($name) {
427
  case 'years':
428
  return $this->y;
@@ -443,11 +628,11 @@ class CarbonInterval extends DateInterval
443
  return $this->s;
444
 
445
  case 'weeks':
446
- return (int) floor($this->d / Carbon::DAYS_PER_WEEK);
447
 
448
  case 'daysExcludeWeeks':
449
  case 'dayzExcludeWeeks':
450
- return $this->d % Carbon::DAYS_PER_WEEK;
451
 
452
  default:
453
  throw new InvalidArgumentException(sprintf("Unknown getter '%s'", $name));
@@ -455,7 +640,7 @@ class CarbonInterval extends DateInterval
455
  }
456
 
457
  /**
458
- * Set a part of the CarbonInterval object
459
  *
460
  * @param string $name
461
  * @param int $val
@@ -474,7 +659,7 @@ class CarbonInterval extends DateInterval
474
  break;
475
 
476
  case 'weeks':
477
- $this->d = $val * Carbon::DAYS_PER_WEEK;
478
  break;
479
 
480
  case 'dayz':
@@ -505,11 +690,94 @@ class CarbonInterval extends DateInterval
505
  */
506
  public function weeksAndDays($weeks, $days)
507
  {
508
- $this->dayz = ($weeks * Carbon::DAYS_PER_WEEK) + $days;
509
 
510
  return $this;
511
  }
512
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
513
  /**
514
  * Allow fluent calls on the setters... CarbonInterval::years(3)->months(5)->day().
515
  *
@@ -523,6 +791,10 @@ class CarbonInterval extends DateInterval
523
  */
524
  public function __call($name, $args)
525
  {
 
 
 
 
526
  $arg = count($args) === 0 ? 1 : $args[0];
527
 
528
  switch ($name) {
@@ -538,7 +810,7 @@ class CarbonInterval extends DateInterval
538
 
539
  case 'weeks':
540
  case 'week':
541
- $this->dayz = $arg * Carbon::DAYS_PER_WEEK;
542
  break;
543
 
544
  case 'days':
@@ -569,24 +841,27 @@ class CarbonInterval extends DateInterval
569
  /**
570
  * Get the current interval in a human readable format in the current locale.
571
  *
 
 
572
  * @return string
573
  */
574
- public function forHumans()
575
  {
576
  $periods = array(
577
- 'year' => $this->years,
578
- 'month' => $this->months,
579
- 'week' => $this->weeks,
580
- 'day' => $this->daysExcludeWeeks,
581
- 'hour' => $this->hours,
582
- 'minute' => $this->minutes,
583
- 'second' => $this->seconds,
584
  );
585
 
586
  $parts = array();
587
- foreach ($periods as $unit => $count) {
 
588
  if ($count > 0) {
589
- $parts[] = static::translator()->transChoice($unit, $count, array(':count' => $count));
590
  }
591
  }
592
 
@@ -604,7 +879,31 @@ class CarbonInterval extends DateInterval
604
  }
605
 
606
  /**
607
- * Add the passed interval to the current instance
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
608
  *
609
  * @param DateInterval $interval
610
  *
@@ -629,22 +928,48 @@ class CarbonInterval extends DateInterval
629
  }
630
 
631
  /**
632
- * Get the interval_spec string
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
633
  *
634
  * @return string
635
  */
636
- public function spec()
637
  {
638
  $date = array_filter(array(
639
- static::PERIOD_YEARS => $this->y,
640
- static::PERIOD_MONTHS => $this->m,
641
- static::PERIOD_DAYS => $this->d,
642
  ));
643
 
644
  $time = array_filter(array(
645
- static::PERIOD_HOURS => $this->h,
646
- static::PERIOD_MINUTES => $this->i,
647
- static::PERIOD_SECONDS => $this->s,
648
  ));
649
 
650
  $specString = static::PERIOD_PREFIX;
@@ -664,24 +989,142 @@ class CarbonInterval extends DateInterval
664
  }
665
 
666
  /**
667
- * Comparing with passed interval
668
  *
669
- * @param DateInterval $interval
 
 
 
 
 
 
 
 
 
 
 
670
  *
671
  * @return int
672
  */
673
- public function compare(DateInterval $interval)
674
  {
675
  $current = Carbon::now();
676
- $passed = $current->copy()->add($interval);
677
- $current->add($this);
678
 
679
  if ($current < $passed) {
680
  return -1;
681
- } elseif ($current > $passed) {
 
682
  return 1;
683
  }
684
 
685
  return 0;
686
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
687
  }
11
 
12
  namespace Carbon;
13
 
14
+ use Closure;
15
  use DateInterval;
16
  use InvalidArgumentException;
17
+ use ReflectionClass;
18
+ use ReflectionFunction;
19
+ use ReflectionMethod;
20
  use Symfony\Component\Translation\TranslatorInterface;
21
 
22
  /**
33
  * @property int $seconds Total seconds of the current interval.
34
  * @property-read int $dayzExcludeWeeks Total days remaining in the final week of the current instance (days % 7).
35
  * @property-read int $daysExcludeWeeks alias of dayzExcludeWeeks
36
+ * @property-read float $totalYears Number of years equivalent to the interval.
37
+ * @property-read float $totalMonths Number of months equivalent to the interval.
38
+ * @property-read float $totalWeeks Number of weeks equivalent to the interval.
39
+ * @property-read float $totalDays Number of days equivalent to the interval.
40
+ * @property-read float $totalDayz Alias for totalDays.
41
+ * @property-read float $totalHours Number of hours equivalent to the interval.
42
+ * @property-read float $totalMinutes Number of minutes equivalent to the interval.
43
+ * @property-read float $totalSeconds Number of seconds equivalent to the interval.
44
  *
45
  * @method static CarbonInterval years($years = 1) Create instance specifying a number of years.
46
  * @method static CarbonInterval year($years = 1) Alias for years()
94
  */
95
  protected static $translator;
96
 
97
+ /**
98
+ * @var array|null
99
+ */
100
+ protected static $cascadeFactors;
101
+
102
+ /**
103
+ * @var array|null
104
+ */
105
+ private static $flipCascadeFactors;
106
+
107
+ /**
108
+ * The registered macros.
109
+ *
110
+ * @var array
111
+ */
112
+ protected static $macros = array();
113
+
114
  /**
115
  * Before PHP 5.4.20/5.5.4 instead of FALSE days will be set to -99999 when the interval instance
116
+ * was created by DateTime::diff().
117
  */
118
  const PHP_DAYS_FALSE = -99999;
119
 
120
+ /**
121
+ * Mapping of units and factors for cascading.
122
+ *
123
+ * Should only be modified by changing the factors or referenced constants.
124
+ *
125
+ * @return array
126
+ */
127
+ public static function getCascadeFactors()
128
+ {
129
+ return static::$cascadeFactors ?: array(
130
+ 'minutes' => array(Carbon::SECONDS_PER_MINUTE, 'seconds'),
131
+ 'hours' => array(Carbon::MINUTES_PER_HOUR, 'minutes'),
132
+ 'dayz' => array(Carbon::HOURS_PER_DAY, 'hours'),
133
+ 'months' => array(Carbon::DAYS_PER_WEEK * Carbon::WEEKS_PER_MONTH, 'dayz'),
134
+ 'years' => array(Carbon::MONTHS_PER_YEAR, 'months'),
135
+ );
136
+ }
137
+
138
+ private static function standardizeUnit($unit)
139
+ {
140
+ $unit = rtrim($unit, 'sz').'s';
141
+
142
+ return $unit === 'days' ? 'dayz' : $unit;
143
+ }
144
+
145
+ private static function getFlipCascadeFactors()
146
+ {
147
+ if (!self::$flipCascadeFactors) {
148
+ self::$flipCascadeFactors = array();
149
+ foreach (static::getCascadeFactors() as $to => $tuple) {
150
+ list($factor, $from) = $tuple;
151
+
152
+ self::$flipCascadeFactors[self::standardizeUnit($from)] = array(self::standardizeUnit($to), $factor);
153
+ }
154
+ }
155
+
156
+ return self::$flipCascadeFactors;
157
+ }
158
+
159
+ /**
160
+ * @param array $cascadeFactors
161
+ */
162
+ public static function setCascadeFactors(array $cascadeFactors)
163
+ {
164
+ self::$flipCascadeFactors = null;
165
+ static::$cascadeFactors = $cascadeFactors;
166
+ }
167
+
168
  /**
169
  * Determine if the interval was created via DateTime:diff() or not.
170
  *
203
  $spec .= $months > 0 ? $months.static::PERIOD_MONTHS : '';
204
 
205
  $specDays = 0;
206
+ $specDays += $weeks > 0 ? $weeks * static::getDaysPerWeek() : 0;
207
  $specDays += $days > 0 ? $days : 0;
208
 
209
  $spec .= $specDays > 0 ? $specDays.static::PERIOD_DAYS : '';
224
  parent::__construct($spec);
225
  }
226
 
227
+ /**
228
+ * Returns the factor for a given source-to-target couple.
229
+ *
230
+ * @param string $source
231
+ * @param string $target
232
+ *
233
+ * @return int|null
234
+ */
235
+ public static function getFactor($source, $target)
236
+ {
237
+ $source = self::standardizeUnit($source);
238
+ $target = self::standardizeUnit($target);
239
+ $factors = static::getFlipCascadeFactors();
240
+ if (isset($factors[$source])) {
241
+ list($to, $factor) = $factors[$source];
242
+ if ($to === $target) {
243
+ return $factor;
244
+ }
245
+ }
246
+
247
+ return null;
248
+ }
249
+
250
+ /**
251
+ * Returns current config for days per week.
252
+ *
253
+ * @return int
254
+ */
255
+ public static function getDaysPerWeek()
256
+ {
257
+ return static::getFactor('dayz', 'weeks') ?: Carbon::DAYS_PER_WEEK;
258
+ }
259
+
260
+ /**
261
+ * Returns current config for hours per day.
262
+ *
263
+ * @return int
264
+ */
265
+ public static function getHoursPerDay()
266
+ {
267
+ return static::getFactor('hours', 'dayz') ?: Carbon::HOURS_PER_DAY;
268
+ }
269
+
270
+ /**
271
+ * Returns current config for minutes per hour.
272
+ *
273
+ * @return int
274
+ */
275
+ public static function getMinutesPerHours()
276
+ {
277
+ return static::getFactor('minutes', 'hours') ?: Carbon::MINUTES_PER_HOUR;
278
+ }
279
+
280
+ /**
281
+ * Returns current config for seconds per minute.
282
+ *
283
+ * @return int
284
+ */
285
+ public static function getSecondsPerMinutes()
286
+ {
287
+ return static::getFactor('seconds', 'minutes') ?: Carbon::SECONDS_PER_MINUTE;
288
+ }
289
+
290
  /**
291
  * Create a new CarbonInterval instance from specific values.
292
  * This is an alias for the constructor that allows better fluent
308
  return new static($years, $months, $weeks, $days, $hours, $minutes, $seconds);
309
  }
310
 
311
+ /**
312
+ * Get a copy of the instance.
313
+ *
314
+ * @return static
315
+ */
316
+ public function copy()
317
+ {
318
+ $date = new static($this->spec());
319
+ $date->invert = $this->invert;
320
+
321
+ return $date;
322
+ }
323
+
324
  /**
325
  * Provide static helpers to create instances. Allows CarbonInterval::years(3).
326
  *
366
  case 'second':
367
  return new static(null, null, null, null, null, null, $arg);
368
  }
369
+
370
+ if (static::hasMacro($name)) {
371
+ return call_user_func_array(
372
+ array(new static(0), $name), $args
373
+ );
374
+ }
375
  }
376
 
377
  /**
378
+ * Creates a CarbonInterval from string.
379
  *
380
  * Format:
381
  *
437
  case 'weeks':
438
  case 'w':
439
  $weeks += $intValue;
440
+ if ($fraction) {
441
+ $parts[] = array(null, $fraction * static::getDaysPerWeek(), 'd');
442
  }
443
  break;
444
 
446
  case 'days':
447
  case 'd':
448
  $days += $intValue;
449
+ if ($fraction) {
450
+ $parts[] = array(null, $fraction * static::getHoursPerDay(), 'h');
451
  }
452
  break;
453
 
455
  case 'hours':
456
  case 'h':
457
  $hours += $intValue;
458
+ if ($fraction) {
459
+ $parts[] = array(null, $fraction * static::getMinutesPerHours(), 'm');
460
  }
461
  break;
462
 
464
  case 'minutes':
465
  case 'm':
466
  $minutes += $intValue;
467
+ if ($fraction) {
468
+ $seconds += round($fraction * static::getSecondsPerMinutes());
469
  }
470
  break;
471
 
492
  *
493
  * @param DateInterval $di
494
  *
 
 
495
  * @return static
496
  */
497
  public static function instance(DateInterval $di)
498
  {
499
+ $instance = new static(static::getDateIntervalSpec($di));
 
 
 
 
500
  $instance->invert = $di->invert;
 
501
 
502
  return $instance;
503
  }
504
 
505
+ /**
506
+ * Make a CarbonInterval instance from given variable if possible.
507
+ *
508
+ * Always return a new instance. Parse only strings and only these likely to be intervals (skip dates
509
+ * and recurrences). Throw an exception for invalid format, but otherwise return null.
510
+ *
511
+ * @param mixed $var
512
+ *
513
+ * @return static|null
514
+ */
515
+ public static function make($var)
516
+ {
517
+ if ($var instanceof DateInterval) {
518
+ return static::instance($var);
519
+ }
520
+
521
+ if (is_string($var)) {
522
+ $var = trim($var);
523
+
524
+ if (substr($var, 0, 1) === 'P') {
525
+ return new static($var);
526
+ }
527
+
528
+ if (preg_match('/^(?:\h*\d+(?:\.\d+)?\h*[a-z]+)+$/i', $var)) {
529
+ return static::fromString($var);
530
+ }
531
+ }
532
+ }
533
+
534
  ///////////////////////////////////////////////////////////////////
535
  /////////////////////// LOCALIZATION //////////////////////////////
536
  ///////////////////////////////////////////////////////////////////
550
  }
551
 
552
  /**
553
+ * Get the translator instance in use.
554
  *
555
  * @return \Symfony\Component\Translation\TranslatorInterface
556
  */
560
  }
561
 
562
  /**
563
+ * Set the translator instance to use.
564
  *
565
  * @param TranslatorInterface $translator
566
  */
570
  }
571
 
572
  /**
573
+ * Get the current translator locale.
574
  *
575
  * @return string
576
  */
580
  }
581
 
582
  /**
583
+ * Set the current translator locale.
584
  *
585
  * @param string $locale
586
  */
594
  ///////////////////////////////////////////////////////////////////
595
 
596
  /**
597
+ * Get a part of the CarbonInterval object.
598
  *
599
  * @param string $name
600
  *
601
  * @throws \InvalidArgumentException
602
  *
603
+ * @return int|float
604
  */
605
  public function __get($name)
606
  {
607
+ if (substr($name, 0, 5) === 'total') {
608
+ return $this->total(substr($name, 5));
609
+ }
610
+
611
  switch ($name) {
612
  case 'years':
613
  return $this->y;
628
  return $this->s;
629
 
630
  case 'weeks':
631
+ return (int) floor($this->d / static::getDaysPerWeek());
632
 
633
  case 'daysExcludeWeeks':
634
  case 'dayzExcludeWeeks':
635
+ return $this->d % static::getDaysPerWeek();
636
 
637
  default:
638
  throw new InvalidArgumentException(sprintf("Unknown getter '%s'", $name));
640
  }
641
 
642
  /**
643
+ * Set a part of the CarbonInterval object.
644
  *
645
  * @param string $name
646
  * @param int $val
659
  break;
660
 
661
  case 'weeks':
662
+ $this->d = $val * static::getDaysPerWeek();
663
  break;
664
 
665
  case 'dayz':
690
  */
691
  public function weeksAndDays($weeks, $days)
692
  {
693
+ $this->dayz = ($weeks * static::getDaysPerWeek()) + $days;
694
 
695
  return $this;
696
  }
697
 
698
+ /**
699
+ * Register a custom macro.
700
+ *
701
+ * @param string $name
702
+ * @param object|callable $macro
703
+ *
704
+ * @return void
705
+ */
706
+ public static function macro($name, $macro)
707
+ {
708
+ static::$macros[$name] = $macro;
709
+ }
710
+
711
+ /**
712
+ * Register macros from a mixin object.
713
+ *
714
+ * @param object $mixin
715
+ *
716
+ * @throws \ReflectionException
717
+ *
718
+ * @return void
719
+ */
720
+ public static function mixin($mixin)
721
+ {
722
+ $reflection = new ReflectionClass($mixin);
723
+
724
+ $methods = $reflection->getMethods(
725
+ ReflectionMethod::IS_PUBLIC | ReflectionMethod::IS_PROTECTED
726
+ );
727
+
728
+ foreach ($methods as $method) {
729
+ $method->setAccessible(true);
730
+
731
+ static::macro($method->name, $method->invoke($mixin));
732
+ }
733
+ }
734
+
735
+ /**
736
+ * Check if macro is registered.
737
+ *
738
+ * @param string $name
739
+ *
740
+ * @return bool
741
+ */
742
+ public static function hasMacro($name)
743
+ {
744
+ return isset(static::$macros[$name]);
745
+ }
746
+
747
+ /**
748
+ * Call given macro.
749
+ *
750
+ * @param string $name
751
+ * @param array $parameters
752
+ *
753
+ * @return mixed
754
+ */
755
+ protected function callMacro($name, $parameters)
756
+ {
757
+ $macro = static::$macros[$name];
758
+
759
+ $reflection = new ReflectionFunction($macro);
760
+
761
+ $reflectionParameters = $reflection->getParameters();
762
+
763
+ $expectedCount = count($reflectionParameters);
764
+ $actualCount = count($parameters);
765
+
766
+ if ($expectedCount > $actualCount && $reflectionParameters[$expectedCount - 1]->name === 'self') {
767
+ for ($i = $actualCount; $i < $expectedCount - 1; $i++) {
768
+ $parameters[] = $reflectionParameters[$i]->getDefaultValue();
769
+ }
770
+
771
+ $parameters[] = $this;
772
+ }
773
+
774
+ if ($macro instanceof Closure && method_exists($macro, 'bindTo')) {
775
+ $macro = $macro->bindTo($this, get_class($this));
776
+ }
777
+
778
+ return call_user_func_array($macro, $parameters);
779
+ }
780
+
781
  /**
782
  * Allow fluent calls on the setters... CarbonInterval::years(3)->months(5)->day().
783
  *
791
  */
792
  public function __call($name, $args)
793
  {
794
+ if (static::hasMacro($name)) {
795
+ return $this->callMacro($name, $args);
796
+ }
797
+
798
  $arg = count($args) === 0 ? 1 : $args[0];
799
 
800
  switch ($name) {
810
 
811
  case 'weeks':
812
  case 'week':
813
+ $this->dayz = $arg * static::getDaysPerWeek();
814
  break;
815
 
816
  case 'days':
841
  /**
842
  * Get the current interval in a human readable format in the current locale.
843
  *
844
+ * @param bool $short (false by default), returns short units if true
845
+ *
846
  * @return string
847
  */
848
+ public function forHumans($short = false)
849
  {
850
  $periods = array(
851
+ 'year' => array('y', $this->years),
852
+ 'month' => array('m', $this->months),
853
+ 'week' => array('w', $this->weeks),
854
+ 'day' => array('d', $this->daysExcludeWeeks),
855
+ 'hour' => array('h', $this->hours),
856
+ 'minute' => array('min', $this->minutes),
857
+ 'second' => array('s', $this->seconds),
858
  );
859
 
860
  $parts = array();
861
+ foreach ($periods as $unit => $options) {
862
+ list($shortUnit, $count) = $options;
863
  if ($count > 0) {
864
+ $parts[] = static::translator()->transChoice($short ? $shortUnit : $unit, $count, array(':count' => $count));
865
  }
866
  }
867
 
879
  }
880
 
881
  /**
882
+ * Convert the interval to a CarbonPeriod.
883
+ *
884
+ * @return CarbonPeriod
885
+ */
886
+ public function toPeriod()
887
+ {
888
+ return CarbonPeriod::createFromArray(
889
+ array_merge(array($this), func_get_args())
890
+ );
891
+ }
892
+
893
+ /**
894
+ * Invert the interval.
895
+ *
896
+ * @return $this
897
+ */
898
+ public function invert()
899
+ {
900
+ $this->invert = $this->invert ? 0 : 1;
901
+
902
+ return $this;
903
+ }
904
+
905
+ /**
906
+ * Add the passed interval to the current instance.
907
  *
908
  * @param DateInterval $interval
909
  *
928
  }
929
 
930
  /**
931
+ * Multiply current instance given number of times
932
+ *
933
+ * @param float $factor
934
+ *
935
+ * @return $this
936
+ */
937
+ public function times($factor)
938
+ {
939
+ if ($factor < 0) {
940
+ $this->invert = $this->invert ? 0 : 1;
941
+ $factor = -$factor;
942
+ }
943
+
944
+ $this->years = round($this->years * $factor);
945
+ $this->months = round($this->months * $factor);
946
+ $this->dayz = round($this->dayz * $factor);
947
+ $this->hours = round($this->hours * $factor);
948
+ $this->minutes = round($this->minutes * $factor);
949
+ $this->seconds = round($this->seconds * $factor);
950
+
951
+ return $this;
952
+ }
953
+
954
+ /**
955
+ * Get the interval_spec string of a date interval.
956
+ *
957
+ * @param DateInterval $interval
958
  *
959
  * @return string
960
  */
961
+ public static function getDateIntervalSpec(DateInterval $interval)
962
  {
963
  $date = array_filter(array(
964
+ static::PERIOD_YEARS => $interval->y,
965
+ static::PERIOD_MONTHS => $interval->m,
966
+ static::PERIOD_DAYS => $interval->d,
967
  ));
968
 
969
  $time = array_filter(array(
970
+ static::PERIOD_HOURS => $interval->h,
971
+ static::PERIOD_MINUTES => $interval->i,
972
+ static::PERIOD_SECONDS => $interval->s,
973
  ));
974
 
975
  $specString = static::PERIOD_PREFIX;
989
  }
990
 
991
  /**
992
+ * Get the interval_spec string.
993
  *
994
+ * @return string
995
+ */
996
+ public function spec()
997
+ {
998
+ return static::getDateIntervalSpec($this);
999
+ }
1000
+
1001
+ /**
1002
+ * Comparing 2 date intervals.
1003
+ *
1004
+ * @param DateInterval $a
1005
+ * @param DateInterval $b
1006
  *
1007
  * @return int
1008
  */
1009
+ public static function compareDateIntervals(DateInterval $a, DateInterval $b)
1010
  {
1011
  $current = Carbon::now();
1012
+ $passed = $current->copy()->add($b);
1013
+ $current->add($a);
1014
 
1015
  if ($current < $passed) {
1016
  return -1;
1017
+ }
1018
+ if ($current > $passed) {
1019
  return 1;
1020
  }
1021
 
1022
  return 0;
1023
  }
1024
+
1025
+ /**
1026
+ * Comparing with passed interval.
1027
+ *
1028
+ * @param DateInterval $interval
1029
+ *
1030
+ * @return int
1031
+ */
1032
+ public function compare(DateInterval $interval)
1033
+ {
1034
+ return static::compareDateIntervals($this, $interval);
1035
+ }
1036
+
1037
+ /**
1038
+ * Convert overflowed values into bigger units.
1039
+ *
1040
+ * @return $this
1041
+ */
1042
+ public function cascade()
1043
+ {
1044
+ foreach (static::getFlipCascadeFactors() as $source => $cascade) {
1045
+ list($target, $factor) = $cascade;
1046
+
1047
+ if ($source === 'dayz' && $target === 'weeks') {
1048
+ continue;
1049
+ }
1050
+
1051
+ $value = $this->$source;
1052
+ $this->$source = $modulo = $value % $factor;
1053
+ $this->$target += ($value - $modulo) / $factor;
1054
+ }
1055
+
1056
+ return $this;
1057
+ }
1058
+
1059
+ /**
1060
+ * Get amount of given unit equivalent to the interval.
1061
+ *
1062
+ * @param string $unit
1063
+ *
1064
+ * @throws \InvalidArgumentException
1065
+ *
1066
+ * @return float
1067
+ */
1068
+ public function total($unit)
1069
+ {
1070
+ $realUnit = $unit = strtolower($unit);
1071
+
1072
+ if (in_array($unit, array('days', 'weeks'))) {
1073
+ $realUnit = 'dayz';
1074
+ } elseif (!in_array($unit, array('seconds', 'minutes', 'hours', 'dayz', 'months', 'years'))) {
1075
+ throw new InvalidArgumentException("Unknown unit '$unit'.");
1076
+ }
1077
+
1078
+ $result = 0;
1079
+ $cumulativeFactor = 0;
1080
+ $unitFound = false;
1081
+
1082
+ foreach (static::getFlipCascadeFactors() as $source => $cascade) {
1083
+ list($target, $factor) = $cascade;
1084
+
1085
+ if ($source === $realUnit) {
1086
+ $unitFound = true;
1087
+ $result += $this->$source;
1088
+ $cumulativeFactor = 1;
1089
+ }
1090
+
1091
+ if ($factor === false) {
1092
+ if ($unitFound) {
1093
+ break;
1094
+ }
1095
+
1096
+ $result = 0;
1097
+ $cumulativeFactor = 0;
1098
+
1099
+ continue;
1100
+ }
1101
+
1102
+ if ($target === $realUnit) {
1103
+ $unitFound = true;
1104
+ }
1105
+
1106
+ if ($cumulativeFactor) {
1107
+ $cumulativeFactor *= $factor;
1108
+ $result += $this->$target * $cumulativeFactor;
1109
+
1110
+ continue;
1111
+ }
1112
+
1113
+ $result = ($result + $this->$source) / $factor;
1114
+ }
1115
+
1116
+ if (isset($target) && !$cumulativeFactor) {
1117
+ $result += $this->$target;
1118
+ }
1119
+
1120
+ if (!$unitFound) {
1121
+ throw new \InvalidArgumentException("Unit $unit have no configuration to get total from other units.");
1122
+ }
1123
+
1124
+ if ($unit === 'weeks') {
1125
+ return $result / static::getDaysPerWeek();
1126
+ }
1127
+
1128
+ return $result;
1129
+ }
1130
  }
src/common/lib/vendor/nesbot/carbon/src/Carbon/CarbonPeriod.php ADDED
@@ -0,0 +1,1429 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Carbon package.
5
+ *
6
+ * (c) Brian Nesbitt <brian@nesbot.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Carbon;
13
+
14
+ use BadMethodCallException;
15
+ use Closure;
16
+ use Countable;
17
+ use DateInterval;
18
+ use DateTime;
19
+ use DateTimeInterface;
20
+ use InvalidArgumentException;
21
+ use Iterator;
22
+ use ReflectionClass;
23
+ use ReflectionFunction;
24
+ use ReflectionMethod;
25
+ use RuntimeException;
26
+
27
+ /**
28
+ * Substitution of DatePeriod with some modifications and many more features.
29
+ * Fully compatible with PHP 5.3+!
30
+ *
31
+ * @method static CarbonPeriod start($date, $inclusive = null) Create instance specifying start date.
32
+ * @method static CarbonPeriod since($date, $inclusive = null) Alias for start().
33
+ * @method static CarbonPeriod sinceNow($inclusive = null) Create instance with start date set to now.
34
+ * @method static CarbonPeriod end($date = null, $inclusive = null) Create instance specifying end date.
35
+ * @method static CarbonPeriod until($date = null, $inclusive = null) Alias for end().
36
+ * @method static CarbonPeriod untilNow($inclusive = null) Create instance with end date set to now.
37
+ * @method static CarbonPeriod dates($start, $end = null) Create instance with start and end date.
38
+ * @method static CarbonPeriod between($start, $end = null) Create instance with start and end date.
39
+ * @method static CarbonPeriod recurrences($recurrences = null) Create instance with maximum number of recurrences.
40
+ * @method static CarbonPeriod times($recurrences = null) Alias for recurrences().
41
+ * @method static CarbonPeriod options($options = null) Create instance with options.
42
+ * @method static CarbonPeriod toggle($options, $state = null) Create instance with options toggled on or off.
43
+ * @method static CarbonPeriod filter($callback, $name = null) Create instance with filter added to the stack.
44
+ * @method static CarbonPeriod push($callback, $name = null) Alias for filter().
45
+ * @method static CarbonPeriod prepend($callback, $name = null) Create instance with filter prepened to the stack.
46
+ * @method static CarbonPeriod filters(array $filters) Create instance with filters stack.
47
+ * @method static CarbonPeriod interval($interval) Create instance with given date interval.
48
+ * @method static CarbonPeriod each($interval) Create instance with given date interval.
49
+ * @method static CarbonPeriod every($interval) Create instance with given date interval.
50
+ * @method static CarbonPeriod step($interval) Create instance with given date interval.
51
+ * @method static CarbonPeriod stepBy($interval) Create instance with given date interval.
52
+ * @method static CarbonPeriod invert() Create instance with inverted date interval.
53
+ * @method static CarbonPeriod years($years = 1) Create instance specifying a number of years for date interval.
54
+ * @method static CarbonPeriod year($years = 1) Alias for years().
55
+ * @method static CarbonPeriod months($months = 1) Create instance specifying a number of months for date interval.
56
+ * @method static CarbonPeriod month($months = 1) Alias for months().
57
+ * @method static CarbonPeriod weeks($weeks = 1) Create instance specifying a number of weeks for date interval.
58
+ * @method static CarbonPeriod week($weeks = 1) Alias for weeks().
59
+ * @method static CarbonPeriod days($days = 1) Create instance specifying a number of days for date interval.
60
+ * @method static CarbonPeriod dayz($days = 1) Alias for days().
61
+ * @method static CarbonPeriod day($days = 1) Alias for days().
62
+ * @method static CarbonPeriod hours($hours = 1) Create instance specifying a number of hours for date interval.
63
+ * @method static CarbonPeriod hour($hours = 1) Alias for hours().
64
+ * @method static CarbonPeriod minutes($minutes = 1) Create instance specifying a number of minutes for date interval.
65
+ * @method static CarbonPeriod minute($minutes = 1) Alias for minutes().
66
+ * @method static CarbonPeriod seconds($seconds = 1) Create instance specifying a number of seconds for date interval.
67
+ * @method static CarbonPeriod second($seconds = 1) Alias for seconds().
68
+ * @method CarbonPeriod start($date, $inclusive = null) Change the period start date.
69
+ * @method CarbonPeriod since($date, $inclusive = null) Alias for start().
70
+ * @method CarbonPeriod sinceNow($inclusive = null) Change the period start date to now.
71
+ * @method CarbonPeriod end($date = null, $inclusive = null) Change the period end date.
72
+ * @method CarbonPeriod until($date = null, $inclusive = null) Alias for end().
73
+ * @method CarbonPeriod untilNow($inclusive = null) Change the period end date to now.
74
+ * @method CarbonPeriod dates($start, $end = null) Change the period start and end date.
75
+ * @method CarbonPeriod recurrences($recurrences = null) Change the maximum number of recurrences.
76
+ * @method CarbonPeriod times($recurrences = null) Alias for recurrences().
77
+ * @method CarbonPeriod options($options = null) Change the period options.
78
+ * @method CarbonPeriod toggle($options, $state = null) Toggle given options on or off.
79
+ * @method CarbonPeriod filter($callback, $name = null) Add a filter to the stack.
80
+ * @method CarbonPeriod push($callback, $name = null) Alias for filter().
81
+ * @method CarbonPeriod prepend($callback, $name = null) Prepend a filter to the stack.
82
+ * @method CarbonPeriod filters(array $filters = array()) Set filters stack.
83
+ * @method CarbonPeriod interval($interval) Change the period date interval.
84
+ * @method CarbonPeriod invert() Invert the period date interval.
85
+ * @method CarbonPeriod years($years = 1) Set the years portion of the date interval.
86
+ * @method CarbonPeriod year($years = 1) Alias for years().
87
+ * @method CarbonPeriod months($months = 1) Set the months portion of the date interval.
88
+ * @method CarbonPeriod month($months = 1) Alias for months().
89
+ * @method CarbonPeriod weeks($weeks = 1) Set the weeks portion of the date interval.
90
+ * @method CarbonPeriod week($weeks = 1) Alias for weeks().
91
+ * @method CarbonPeriod days($days = 1) Set the days portion of the date interval.
92
+ * @method CarbonPeriod dayz($days = 1) Alias for days().
93
+ * @method CarbonPeriod day($days = 1) Alias for days().
94
+ * @method CarbonPeriod hours($hours = 1) Set the hours portion of the date interval.
95
+ * @method CarbonPeriod hour($hours = 1) Alias for hours().
96
+ * @method CarbonPeriod minutes($minutes = 1) Set the minutes portion of the date interval.
97
+ * @method CarbonPeriod minute($minutes = 1) Alias for minutes().
98
+ * @method CarbonPeriod seconds($seconds = 1) Set the seconds portion of the date interval.
99
+ * @method CarbonPeriod second($seconds = 1) Alias for seconds().
100
+ */
101
+ class CarbonPeriod implements Iterator, Countable
102
+ {
103
+ /**
104
+ * Built-in filters.
105
+ *
106
+ * @var string
107
+ */
108
+ const RECURRENCES_FILTER = 'Carbon\CarbonPeriod::filterRecurrences';
109
+ const END_DATE_FILTER = 'Carbon\CarbonPeriod::filterEndDate';
110
+
111
+ /**
112
+ * Special value which can be returned by filters to end iteration. Also a filter.
113
+ *
114
+ * @var string
115
+ */
116
+ const END_ITERATION = 'Carbon\CarbonPeriod::endIteration';
117
+
118
+ /**
119
+ * Available options.
120
+ *
121
+ * @var int
122
+ */
123
+ const EXCLUDE_START_DATE = 1;
124
+ const EXCLUDE_END_DATE = 2;
125
+
126
+ /**
127
+ * Number of maximum attempts before giving up on finding next valid date.
128
+ *
129
+ * @var int
130
+ */
131
+ const NEXT_MAX_ATTEMPTS = 1000;
132
+
133
+ /**
134
+ * The registered macros.
135
+ *
136
+ * @var array
137
+ */
138
+ protected static $macros = array();
139
+
140
+ /**
141
+ * Underlying date interval instance. Always present, one day by default.
142
+ *
143
+ * @var CarbonInterval
144
+ */
145
+ protected $dateInterval;
146
+
147
+ /**
148
+ * Whether current date interval was set by default.
149
+ *
150
+ * @var bool
151
+ */
152
+ protected $isDefaultInterval;
153
+
154
+ /**
155
+ * The filters stack.
156
+ *
157
+ * @var array
158
+ */
159
+ protected $filters = array();
160
+
161
+ /**
162
+ * Period start date. Applied on rewind. Always present, now by default.
163
+ *
164
+ * @var Carbon
165
+ */
166
+ protected $startDate;
167
+
168
+ /**
169
+ * Period end date. For inverted interval should be before the start date. Applied via a filter.
170
+ *
171
+ * @var Carbon|null
172
+ */
173
+ protected $endDate;
174
+
175
+ /**
176
+ * Limit for number of recurrences. Applied via a filter.
177
+ *
178
+ * @var int|null
179
+ */
180
+ protected $recurrences;
181
+
182
+ /**
183
+ * Iteration options.
184
+ *
185
+ * @var int
186
+ */
187
+ protected $options;
188
+
189
+ /**
190
+ * Index of current date. Always sequential, even if some dates are skipped by filters.
191
+ * Equal to null only before the first iteration.
192
+ *
193
+ * @var int
194
+ */
195
+ protected $key;
196
+
197
+ /**
198
+ * Current date. May temporarily hold unaccepted value when looking for a next valid date.
199
+ * Equal to null only before the first iteration.
200
+ *
201
+ * @var Carbon
202
+ */
203
+ protected $current;
204
+
205
+ /**
206
+ * Timezone of current date. Taken from the start date.
207
+ *
208
+ * @var \DateTimeZone|null
209
+ */
210
+ protected $timezone;
211
+
212
+ /**
213
+ * The cached validation result for current date.
214
+ *
215
+ * @var bool|static::END_ITERATION
216
+ */
217
+ protected $validationResult;
218
+
219
+ /**
220
+ * Create a new instance.
221
+ *
222
+ * @return static
223
+ */
224
+ public static function create()
225
+ {
226
+ return static::createFromArray(func_get_args());
227
+ }
228
+
229
+ /**
230
+ * Create a new instance from an array of parameters.
231
+ *
232
+ * @param array $params
233
+ *
234
+ * @return static
235
+ */
236
+ public static function createFromArray(array $params)
237
+ {
238
+ // PHP 5.3 equivalent of new static(...$params).
239
+ $reflection = new ReflectionClass(get_class());
240
+
241
+ return $reflection->newInstanceArgs($params);
242
+ }
243
+
244
+ /**
245
+ * Create CarbonPeriod from ISO 8601 string.
246
+ *
247
+ * @param string $iso
248
+ * @param int|null $options
249
+ *
250
+ * @return static
251
+ */
252
+ public static function createFromIso($iso, $options = null)
253
+ {
254
+ $params = static::parseIso8601($iso);
255
+
256
+ $instance = static::createFromArray($params);
257
+
258
+ if ($options !== null) {
259
+ $instance->setOptions($options);
260
+ }
261
+
262
+ return $instance;
263
+ }
264
+
265
+ /**
266
+ * Return whether given interval contains non zero value of any time unit.
267
+ *
268
+ * @param \DateInterval $interval
269
+ *
270
+ * @return bool
271
+ */
272
+ protected static function intervalHasTime(DateInterval $interval)
273
+ {
274
+ // The array_key_exists and get_object_vars are used as a workaround to check microsecond support.
275
+ // Both isset and property_exists will fail on PHP 7.0.14 - 7.0.21 due to the following bug:
276
+ // https://bugs.php.net/bug.php?id=74852
277
+ return $interval->h || $interval->i || $interval->s || array_key_exists('f', get_object_vars($interval)) && $interval->f;
278
+ }
279
+
280
+ /**
281
+ * Return whether given callable is a string pointing to one of Carbon's is* methods
282
+ * and should be automatically converted to a filter callback.
283
+ *
284
+ * @param callable $callable
285
+ *
286
+ * @return bool
287
+ */
288
+ protected static function isCarbonPredicateMethod($callable)
289
+ {
290
+ return is_string($callable) && substr($callable, 0, 2) === 'is' && (method_exists('Carbon\Carbon', $callable) || Carbon::hasMacro($callable));
291
+ }
292
+
293
+ /**
294
+ * Return whether given variable is an ISO 8601 specification.
295
+ *
296
+ * Note: Check is very basic, as actual validation will be done later when parsing.
297
+ * We just want to ensure that variable is not any other type of a valid parameter.
298
+ *
299
+ * @param mixed $var
300
+ *
301
+ * @return bool
302
+ */
303
+ protected static function isIso8601($var)
304
+ {
305
+ if (!is_string($var)) {
306
+ return false;
307
+ }
308
+
309
+ // Match slash but not within a timezone name.
310
+ $part = '[a-z]+(?:[_-][a-z]+)*';
311
+
312
+ preg_match("#\b$part/$part\b|(/)#i", $var, $match);
313
+
314
+ return isset($match[1]);
315
+ }
316
+
317
+ /**
318
+ * Parse given ISO 8601 string into an array of arguments.
319
+ *
320
+ * @param string $iso
321
+ *
322
+ * @return array
323
+ */
324
+ protected static function parseIso8601($iso)
325
+ {
326
+ $result = array();
327
+
328
+ $interval = null;
329
+ $start = null;
330
+ $end = null;
331
+
332
+ foreach (explode('/', $iso) as $key => $part) {
333
+ if ($key === 0 && preg_match('/^R([0-9]*)$/', $part, $match)) {
334
+ $parsed = strlen($match[1]) ? (int) $match[1] : null;
335
+ } elseif ($interval === null && $parsed = CarbonInterval::make($part)) {
336
+ $interval = $part;
337
+ } elseif ($start === null && $parsed = Carbon::make($part)) {
338
+ $start = $part;
339
+ } elseif ($end === null && $parsed = Carbon::make(static::addMissingParts($start, $part))) {
340
+ $end = $part;
341
+ } else {
342
+ throw new InvalidArgumentException("Invalid ISO 8601 specification: $iso.");
343
+ }
344
+
345
+ $result[] = $parsed;
346
+ }
347
+
348
+ return $result;
349
+ }
350
+
351
+ /**
352
+ * Add missing parts of the target date from the soure date.
353
+ *
354
+ * @param string $source
355
+ * @param string $target
356
+ *
357
+ * @return string
358
+ */
359
+ protected static function addMissingParts($source, $target)
360
+ {
361
+ $pattern = '/'.preg_replace('/[0-9]+/', '[0-9]+', preg_quote($target, '/')).'$/';
362
+
363
+ $result = preg_replace($pattern, $target, $source, 1, $count);
364
+
365
+ return $count ? $result : $target;
366
+ }
367
+
368
+ /**
369
+ * Register a custom macro.
370
+ *
371
+ * @param string $name
372
+ * @param object|callable $macro
373
+ *
374
+ * @return void
375
+ */
376
+ public static function macro($name, $macro)
377
+ {
378
+ static::$macros[$name] = $macro;
379
+ }
380
+
381
+ /**
382
+ * Register macros from a mixin object.
383
+ *
384
+ * @param object $mixin
385
+ *
386
+ * @throws \ReflectionException
387
+ *
388
+ * @return void
389
+ */
390
+ public static function mixin($mixin)
391
+ {
392
+ $reflection = new ReflectionClass($mixin);
393
+
394
+ $methods = $reflection->getMethods(
395
+ ReflectionMethod::IS_PUBLIC | ReflectionMethod::IS_PROTECTED
396
+ );
397
+
398
+ foreach ($methods as $method) {
399
+ $method->setAccessible(true);
400
+
401
+ static::macro($method->name, $method->invoke($mixin));
402
+ }
403
+ }
404
+
405
+ /**
406
+ * Check if macro is registered.
407
+ *
408
+ * @param string $name
409
+ *
410
+ * @return bool
411
+ */
412
+ public static function hasMacro($name)
413
+ {
414
+ return isset(static::$macros[$name]);
415
+ }
416
+
417
+ /**
418
+ * Provide static proxy for instance aliases.
419
+ *
420
+ * @param string $method
421
+ * @param array $parameters
422
+ *
423
+ * @return mixed
424
+ */
425
+ public static function __callStatic($method, $parameters)
426
+ {
427
+ return call_user_func_array(
428
+ array(new static, $method), $parameters
429
+ );
430
+ }
431
+
432
+ /**
433
+ * CarbonPeriod constructor.
434
+ *
435
+ * @throws InvalidArgumentException
436
+ */
437
+ public function __construct()
438
+ {
439
+ // Parse and assign arguments one by one. First argument may be an ISO 8601 spec,
440
+ // which will be first parsed into parts and then processed the same way.
441
+ $arguments = func_get_args();
442
+
443
+ if (count($arguments) && static::isIso8601($iso = $arguments[0])) {
444
+ array_splice($arguments, 0, 1, static::parseIso8601($iso));
445
+ }
446
+
447
+ foreach ($arguments as $argument) {
448
+ if ($this->dateInterval === null && $parsed = CarbonInterval::make($argument)) {
449
+ $this->setDateInterval($parsed);
450
+ } elseif ($this->startDate === null && $parsed = Carbon::make($argument)) {
451
+ $this->setStartDate($parsed);
452
+ } elseif ($this->endDate === null && $parsed = Carbon::make($argument)) {
453
+ $this->setEndDate($parsed);
454
+ } elseif ($this->recurrences === null && $this->endDate === null && is_numeric($argument)) {
455
+ $this->setRecurrences($argument);
456
+ } elseif ($this->options === null && (is_int($argument) || $argument === null)) {
457
+ $this->setOptions($argument);
458
+ } else {
459
+ throw new InvalidArgumentException('Invalid constructor parameters.');
460
+ }
461
+ }
462
+
463
+ if ($this->startDate === null) {
464
+ $this->setStartDate(Carbon::now());
465
+ }
466
+
467
+ if ($this->dateInterval === null) {
468
+ $this->setDateInterval(CarbonInterval::day());
469
+
470
+ $this->isDefaultInterval = true;
471
+ }
472
+
473
+ if ($this->options === null) {
474
+ $this->setOptions(0);
475
+ }
476
+ }
477
+
478
+ /**
479
+ * Change the period date interval.
480
+ *
481
+ * @param DateInterval|string $interval
482
+ *
483
+ * @throws \InvalidArgumentException
484
+ *
485
+ * @return $this
486
+ */
487
+ public function setDateInterval($interval)
488
+ {
489
+ if (!$interval = CarbonInterval::make($interval)) {
490
+ throw new InvalidArgumentException('Invalid interval.');
491
+ }
492
+
493
+ if ($interval->spec() === 'PT0S') {
494
+ throw new InvalidArgumentException('Empty interval is not accepted.');
495
+ }
496
+
497
+ $this->dateInterval = $interval;
498
+
499
+ $this->isDefaultInterval = false;
500
+
501
+ $this->handleChangedParameters();
502
+
503
+ return $this;
504
+ }
505
+
506
+ /**
507
+ * Invert the period date interval.
508
+ *
509
+ * @return $this
510
+ */
511
+ public function invertDateInterval()
512
+ {
513
+ $interval = $this->dateInterval->invert();
514
+
515
+ return $this->setDateInterval($interval);
516
+ }
517
+
518
+ /**
519
+ * Set start and end date.
520
+ *
521
+ * @param DateTime|DateTimeInterface|string $start
522
+ * @param DateTime|DateTimeInterface|string|null $end
523
+ *
524
+ * @return $this
525
+ */
526
+ public function setDates($start, $end)
527
+ {
528
+ $this->setStartDate($start);
529
+ $this->setEndDate($end);
530
+
531
+ return $this;
532
+ }
533
+
534
+ /**
535
+ * Change the period options.
536
+ *
537
+ * @param int|null $options
538
+ *
539
+ * @throws \InvalidArgumentException
540
+ *
541
+ * @return $this
542
+ */
543
+ public function setOptions($options)
544
+ {
545
+ if (!is_int($options) && !is_null($options)) {
546
+ throw new InvalidArgumentException('Invalid options.');
547
+ }
548
+
549
+ $this->options = $options ?: 0;
550
+
551
+ $this->handleChangedParameters();
552
+
553
+ return $this;
554
+ }
555
+
556
+ /**
557
+ * Get the period options.
558
+ *
559
+ * @return int
560
+ */
561
+ public function getOptions()
562
+ {
563
+ return $this->options;
564
+ }
565
+
566
+ /**
567
+ * Toggle given options on or off.
568
+ *
569
+ * @param int $options
570
+ * @param bool|null $state
571
+ *
572
+ * @throws \InvalidArgumentException
573
+ *
574
+ * @return $this
575
+ */
576
+ public function toggleOptions($options, $state = null)
577
+ {
578
+ if ($state === null) {
579
+ $state = ($this->options & $options) !== $options;
580
+ }
581
+
582
+ return $this->setOptions($state ?
583
+ $this->options | $options :
584
+ $this->options & ~$options
585
+ );
586
+ }
587
+
588
+ /**
589
+ * Toggle EXCLUDE_START_DATE option.
590
+ *
591
+ * @param bool $state
592
+ *
593
+ * @return $this
594
+ */
595
+ public function excludeStartDate($state = true)
596
+ {
597
+ return $this->toggleOptions(static::EXCLUDE_START_DATE, $state);
598
+ }
599
+
600
+ /**
601
+ * Toggle EXCLUDE_END_DATE option.
602
+ *
603
+ * @param bool $state
604
+ *
605
+ * @return $this
606
+ */
607
+ public function excludeEndDate($state = true)
608
+ {
609
+ return $this->toggleOptions(static::EXCLUDE_END_DATE, $state);
610
+ }
611
+
612
+ /**
613
+ * Get the underlying date interval.
614
+ *
615
+ * @return CarbonInterval
616
+ */
617
+ public function getDateInterval()
618
+ {
619
+ return $this->dateInterval->copy();
620
+ }
621
+
622
+ /**
623
+ * Get start date of the period.
624
+ *
625
+ * @return Carbon
626
+ */
627
+ public function getStartDate()
628
+ {
629
+ return $this->startDate->copy();
630
+ }
631
+
632
+ /**
633
+ * Get end date of the period.
634
+ *
635
+ * @return Carbon|null
636
+ */
637
+ public function getEndDate()
638
+ {
639
+ if ($this->endDate) {
640
+ return $this->endDate->copy();
641
+ }
642
+ }
643
+
644
+ /**
645
+ * Get number of recurrences.
646
+ *
647
+ * @return int|null
648
+ */
649
+ public function getRecurrences()
650
+ {
651
+ return $this->recurrences;
652
+ }
653
+
654
+ /**
655
+ * Returns true if the start date should be excluded.
656
+ *
657
+ * @return bool
658
+ */
659
+ public function isStartExcluded()
660
+ {
661
+ return ($this->options & static::EXCLUDE_START_DATE) !== 0;
662
+ }
663
+
664
+ /**
665
+ * Returns true if the end date should be excluded.
666
+ *
667
+ * @return bool
668
+ */
669
+ public function isEndExcluded()
670
+ {
671
+ return ($this->options & static::EXCLUDE_END_DATE) !== 0;
672
+ }
673
+
674
+ /**
675
+ * Add a filter to the stack.
676
+ *
677
+ * @param callable $callback
678
+ * @param string $name
679
+ *
680
+ * @return $this
681
+ */
682
+ public function addFilter($callback, $name = null)
683
+ {
684
+ $tuple = $this->createFilterTuple(func_get_args());
685
+
686
+ $this->filters[] = $tuple;
687
+
688
+ $this->handleChangedParameters();
689
+
690
+ return $this;
691
+ }
692
+
693
+ /**
694
+ * Prepend a filter to the stack.
695
+ *
696
+ * @param callable $callback
697
+ * @param string $name
698
+ *
699
+ * @return $this
700
+ */
701
+ public function prependFilter($callback, $name = null)
702
+ {
703
+ $tuple = $this->createFilterTuple(func_get_args());
704
+
705
+ array_unshift($this->filters, $tuple);
706
+
707
+ $this->handleChangedParameters();
708
+
709
+ return $this;
710
+ }
711
+
712
+ /**
713
+ * Create a filter tuple from raw parameters.
714
+ *
715
+ * Will create an automatic filter callback for one of Carbon's is* methods.
716
+ *
717
+ * @param array $parameters
718
+ *
719
+ * @return array
720
+ */
721
+ protected function createFilterTuple(array $parameters)
722
+ {
723
+ $method = array_shift($parameters);
724
+
725
+ if (!$this->isCarbonPredicateMethod($method)) {
726
+ return array($method, array_shift($parameters));
727
+ }
728
+
729
+ return array(function ($date) use ($method, $parameters) {
730
+ return call_user_func_array(array($date, $method), $parameters);
731
+ }, $method);
732
+ }
733
+
734
+ /**
735
+ * Remove a filter by instance or name.
736
+ *
737
+ * @param callable|string $filter
738
+ *
739
+ * @return $this
740
+ */
741
+ public function removeFilter($filter)
742
+ {
743
+ $key = is_callable($filter) ? 0 : 1;
744
+
745
+ $this->filters = array_values(array_filter(
746
+ $this->filters,
747
+ function ($tuple) use ($key, $filter) {
748
+ return $tuple[$key] !== $filter;
749
+ }
750
+ ));
751
+
752
+ $this->updateInternalState();
753
+
754
+ $this->handleChangedParameters();
755
+
756
+ return $this;
757
+ }
758
+
759
+ /**
760
+ * Return whether given instance or name is in the filter stack.
761
+ *
762
+ * @param callable|string $filter
763
+ *
764
+ * @return bool
765
+ */
766
+ public function hasFilter($filter)
767
+ {
768
+ $key = is_callable($filter) ? 0 : 1;
769
+
770
+ foreach ($this->filters as $tuple) {
771
+ if ($tuple[$key] === $filter) {
772
+ return true;
773
+ }
774
+ }
775
+
776
+ return false;
777
+ }
778
+
779
+ /**
780
+ * Get filters stack.
781
+ *
782
+ * @return array
783
+ */
784
+ public function getFilters()
785
+ {
786
+ return $this->filters;
787
+ }
788
+
789
+ /**
790
+ * Set filters stack.
791
+ *
792
+ * @param array $filters
793
+ *
794
+ * @return $this
795
+ */
796
+ public function setFilters(array $filters)
797
+ {
798
+ $this->filters = $filters;
799
+
800
+ $this->updateInternalState();
801
+
802
+ $this->handleChangedParameters();
803
+
804
+ return $this;
805
+ }
806
+
807
+ /**
808
+ * Reset filters stack.
809
+ *
810
+ * @return $this
811
+ */
812
+ public function resetFilters()
813
+ {
814
+ $this->filters = array();
815
+
816
+ if ($this->endDate !== null) {
817
+ $this->filters[] = array(static::END_DATE_FILTER, null);
818
+ }
819
+
820
+ if ($this->recurrences !== null) {
821
+ $this->filters[] = array(static::RECURRENCES_FILTER, null);
822
+ }
823
+
824
+ $this->handleChangedParameters();
825
+
826
+ return $this;
827
+ }
828
+
829
+ /**
830
+ * Update properties after removing built-in filters.
831
+ *
832
+ * @return void
833
+ */
834
+ protected function updateInternalState()
835
+ {
836
+ if (!$this->hasFilter(static::END_DATE_FILTER)) {
837
+ $this->endDate = null;
838
+ }
839
+
840
+ if (!$this->hasFilter(static::RECURRENCES_FILTER)) {
841
+ $this->recurrences = null;
842
+ }
843
+ }
844
+
845
+ /**
846
+ * Add a recurrences filter (set maximum number of recurrences).
847
+ *
848
+ * @param int|null $recurrences
849
+ *
850
+ * @throws \InvalidArgumentException
851
+ *
852
+ * @return $this
853
+ */
854
+ public function setRecurrences($recurrences)
855
+ {
856
+ if (!is_numeric($recurrences) && !is_null($recurrences) || $recurrences < 0) {
857
+ throw new InvalidArgumentException('Invalid number of recurrences.');
858
+ }
859
+
860
+ if ($recurrences === null) {
861
+ return $this->removeFilter(static::RECURRENCES_FILTER);
862
+ }
863
+
864
+ $this->recurrences = (int) $recurrences;
865
+
866
+ if (!$this->hasFilter(static::RECURRENCES_FILTER)) {
867
+ return $this->addFilter(static::RECURRENCES_FILTER);
868
+ }
869
+
870
+ $this->handleChangedParameters();
871
+
872
+ return $this;
873
+ }
874
+
875
+ /**
876
+ * Recurrences filter callback (limits number of recurrences).
877
+ *
878
+ * @param \Carbon\Carbon $current
879
+ * @param int $key
880
+ *
881
+ * @return bool|static::END_ITERATION
882
+ */
883
+ protected function filterRecurrences($current, $key)
884
+ {
885
+ if ($key < $this->recurrences) {
886
+ return true;
887
+ }
888
+
889
+ return static::END_ITERATION;
890
+ }
891
+
892
+ /**
893
+ * Change the period start date.
894
+ *
895
+ * @param DateTime|DateTimeInterface|string $date
896
+ * @param bool|null $inclusive
897
+ *
898
+ * @throws \InvalidArgumentException
899
+ *
900
+ * @return $this
901
+ */
902
+ public function setStartDate($date, $inclusive = null)
903
+ {
904
+ if (!$date = Carbon::make($date)) {
905
+ throw new InvalidArgumentException('Invalid start date.');
906
+ }
907
+
908
+ $this->startDate = $date;
909
+
910
+ if ($inclusive !== null) {
911
+ $this->toggleOptions(static::EXCLUDE_START_DATE, !$inclusive);
912
+ }
913
+
914
+ return $this;
915
+ }
916
+
917
+ /**
918
+ * Change the period end date.
919
+ *
920
+ * @param DateTime|DateTimeInterface|string|null $date
921
+ * @param bool|null $inclusive
922
+ *
923
+ * @throws \InvalidArgumentException
924
+ *
925
+ * @return $this
926
+ */
927
+ public function setEndDate($date, $inclusive = null)
928
+ {
929
+ if (!is_null($date) && !$date = Carbon::make($date)) {
930
+ throw new InvalidArgumentException('Invalid end date.');
931
+ }
932
+
933
+ if (!$date) {
934
+ return $this->removeFilter(static::END_DATE_FILTER);
935
+ }
936
+
937
+ $this->endDate = $date;
938
+
939
+ if ($inclusive !== null) {
940
+ $this->toggleOptions(static::EXCLUDE_END_DATE, !$inclusive);
941
+ }
942
+
943
+ if (!$this->hasFilter(static::END_DATE_FILTER)) {
944
+ return $this->addFilter(static::END_DATE_FILTER);
945
+ }
946
+
947
+ $this->handleChangedParameters();
948
+
949
+ return $this;
950
+ }
951
+
952
+ /**
953
+ * End date filter callback.
954
+ *
955
+ * @param \Carbon\Carbon $current
956
+ *
957
+ * @return bool|static::END_ITERATION
958
+ */
959
+ protected function filterEndDate($current)
960
+ {
961
+ if (!$this->isEndExcluded() && $current == $this->endDate) {
962
+ return true;
963
+ }
964
+
965
+ if ($this->dateInterval->invert ? $current > $this->endDate : $current < $this->endDate) {
966
+ return true;
967
+ }
968
+
969
+ return static::END_ITERATION;
970
+ }
971
+
972
+ /**
973
+ * End iteration filter callback.
974
+ *
975
+ * @return static::END_ITERATION
976
+ */
977
+ protected function endIteration()
978
+ {
979
+ return static::END_ITERATION;
980
+ }
981
+
982
+ /**
983
+ * Handle change of the parameters.
984
+ *
985
+ * @return void
986
+ */
987
+ protected function handleChangedParameters()
988
+ {
989
+ $this->validationResult = null;
990
+ }
991
+
992
+ /**
993
+ * Validate current date and stop iteration when necessary.
994
+ *
995
+ * Returns true when current date is valid, false if it is not, or static::END_ITERATION
996
+ * when iteration should be stopped.
997
+ *
998
+ * @return bool|static::END_ITERATION
999
+ */
1000
+ protected function validateCurrentDate()
1001
+ {
1002
+ if ($this->current === null) {
1003
+ $this->rewind();
1004
+ }
1005
+
1006
+ // Check after the first rewind to avoid repeating the initial validation.
1007
+ if ($this->validationResult !== null) {
1008
+ return $this->validationResult;
1009
+ }
1010
+
1011
+ return $this->validationResult = $this->checkFilters();
1012
+ }
1013
+
1014
+ /**
1015
+ * Check whether current value and key pass all the filters.
1016
+ *
1017
+ * @return bool|static::END_ITERATION
1018
+ */
1019
+ protected function checkFilters()
1020
+ {
1021
+ $current = $this->prepareForReturn($this->current);
1022
+
1023
+ foreach ($this->filters as $tuple) {
1024
+ $result = call_user_func(
1025
+ $tuple[0], $current->copy(), $this->key, $this
1026
+ );
1027
+
1028
+ if ($result === static::END_ITERATION) {
1029
+ return static::END_ITERATION;
1030
+ }
1031
+
1032
+ if (!$result) {
1033
+ return false;
1034
+ }
1035
+ }
1036
+
1037
+ return true;
1038
+ }
1039
+
1040
+ /**
1041
+ * Prepare given date to be returned to the external logic.
1042
+ *
1043
+ * @param Carbon $date
1044
+ *
1045
+ * @return Carbon
1046
+ */
1047
+ protected function prepareForReturn(Carbon $date)
1048
+ {
1049
+ $date = $date->copy();
1050
+
1051
+ if ($this->timezone) {
1052
+ $date->setTimezone($this->timezone);
1053
+ }
1054
+
1055
+ return $date;
1056
+ }
1057
+
1058
+ /**
1059
+ * Check if the current position is valid.
1060
+ *
1061
+ * @return bool
1062
+ */
1063
+ public function valid()
1064
+ {
1065
+ return $this->validateCurrentDate() === true;
1066
+ }
1067
+
1068
+ /**
1069
+ * Return the current key.
1070
+ *
1071
+ * @return int|null
1072
+ */
1073
+ public function key()
1074
+ {
1075
+ if ($this->valid()) {
1076
+ return $this->key;
1077
+ }
1078
+ }
1079
+
1080
+ /**
1081
+ * Return the current date.
1082
+ *
1083
+ * @return Carbon|null
1084
+ */
1085
+ public function current()
1086
+ {
1087
+ if ($this->valid()) {
1088
+ return $this->prepareForReturn($this->current);
1089
+ }
1090
+ }
1091
+
1092
+ /**
1093
+ * Move forward to the next date.
1094
+ *
1095
+ * @throws \RuntimeException
1096
+ *
1097
+ * @return void
1098
+ */
1099
+ public function next()
1100
+ {
1101
+ if ($this->current === null) {
1102
+ $this->rewind();
1103
+ }
1104
+
1105
+ if ($this->validationResult !== static::END_ITERATION) {
1106
+ $this->key++;
1107
+
1108
+ $this->incrementCurrentDateUntilValid();
1109
+ }
1110
+ }
1111
+
1112
+ /**
1113
+ * Rewind to the start date.
1114
+ *
1115
+ * Iterating over a date in the UTC timezone avoids bug during backward DST change.
1116
+ *
1117
+ * @see https://bugs.php.net/bug.php?id=72255
1118
+ * @see https://bugs.php.net/bug.php?id=74274
1119
+ * @see https://wiki.php.net/rfc/datetime_and_daylight_saving_time
1120
+ *
1121
+ * @throws \RuntimeException
1122
+ *
1123
+ * @return void
1124
+ */
1125
+ public function rewind()
1126
+ {
1127
+ $this->key = 0;
1128
+ $this->current = $this->startDate->copy();
1129
+ $this->timezone = static::intervalHasTime($this->dateInterval) ? $this->current->getTimezone() : null;
1130
+
1131
+ if ($this->timezone) {
1132
+ $this->current->setTimezone('UTC');
1133
+ }
1134
+
1135
+ $this->validationResult = null;
1136
+
1137
+ if ($this->isStartExcluded() || $this->validateCurrentDate() === false) {
1138
+ $this->incrementCurrentDateUntilValid();
1139
+ }
1140
+ }
1141
+
1142
+ /**
1143
+ * Keep incrementing the current date until a valid date is found or the iteration is ended.
1144
+ *
1145
+ * @throws \RuntimeException
1146
+ *
1147
+ * @return void
1148
+ */
1149
+ protected function incrementCurrentDateUntilValid()
1150
+ {
1151
+ $attempts = 0;
1152
+
1153
+ do {
1154
+ $this->current->add($this->dateInterval);
1155
+
1156
+ $this->validationResult = null;
1157
+
1158
+ if (++$attempts > static::NEXT_MAX_ATTEMPTS) {
1159
+ throw new RuntimeException('Could not find next valid date.');
1160
+ }
1161
+ } while ($this->validateCurrentDate() === false);
1162
+ }
1163
+
1164
+ /**
1165
+ * Format the date period as ISO 8601.
1166
+ *
1167
+ * @return string
1168
+ */
1169
+ public function toIso8601String()
1170
+ {
1171
+ $parts = array();
1172
+
1173
+ if ($this->recurrences !== null) {
1174
+ $parts[] = 'R'.$this->recurrences;
1175
+ }
1176
+
1177
+ $parts[] = $this->startDate->toIso8601String();
1178
+
1179
+ $parts[] = $this->dateInterval->spec();
1180
+
1181
+ if ($this->endDate !== null) {
1182
+ $parts[] = $this->endDate->toIso8601String();
1183
+ }
1184
+
1185
+ return implode('/', $parts);
1186
+ }
1187
+
1188
+ /**
1189
+ * Convert the date period into a string.
1190
+ *
1191
+ * @return string
1192
+ */
1193
+ public function toString()
1194
+ {
1195
+ $translator = Carbon::getTranslator();
1196
+
1197
+ $parts = array();
1198
+
1199
+ $format = !$this->startDate->isStartOfDay() || $this->endDate && !$this->endDate->isStartOfDay()
1200
+ ? 'Y-m-d H:i:s'
1201
+ : 'Y-m-d';
1202
+
1203
+ if ($this->recurrences !== null) {
1204
+ $parts[] = $translator->transChoice('period_recurrences', $this->recurrences, array(':count' => $this->recurrences));
1205
+ }
1206
+
1207
+ $parts[] = $translator->trans('period_interval', array(':interval' => $this->dateInterval->forHumans()));
1208
+
1209
+ $parts[] = $translator->trans('period_start_date', array(':date' => $this->startDate->format($format)));
1210
+
1211
+ if ($this->endDate !== null) {
1212
+ $parts[] = $translator->trans('period_end_date', array(':date' => $this->endDate->format($format)));
1213
+ }
1214
+
1215
+ $result = implode(' ', $parts);
1216
+
1217
+ return mb_strtoupper(mb_substr($result, 0, 1)).mb_substr($result, 1);
1218
+ }
1219
+
1220
+ /**
1221
+ * Format the date period as ISO 8601.
1222
+ *
1223
+ * @return string
1224
+ */
1225
+ public function spec()
1226
+ {
1227
+ return $this->toIso8601String();
1228
+ }
1229
+
1230
+ /**
1231
+ * Convert the date period into an array without changing current iteration state.
1232
+ *
1233
+ * @return array
1234
+ */
1235
+ public function toArray()
1236
+ {
1237
+ $state = array(
1238
+ $this->key,
1239
+ $this->current ? $this->current->copy() : null,
1240
+ $this->validationResult,
1241
+ );
1242
+
1243
+ $result = iterator_to_array($this);
1244
+
1245
+ list(
1246
+ $this->key,
1247
+ $this->current,
1248
+ $this->validationResult
1249
+ ) = $state;
1250
+
1251
+ return $result;
1252
+ }
1253
+
1254
+ /**
1255
+ * Count dates in the date period.
1256
+ *
1257
+ * @return int
1258
+ */
1259
+ public function count()
1260
+ {
1261
+ return count($this->toArray());
1262
+ }
1263
+
1264
+ /**
1265
+ * Return the first date in the date period.
1266
+ *
1267
+ * @return Carbon|null
1268
+ */
1269
+ public function first()
1270
+ {
1271
+ if ($array = $this->toArray()) {
1272
+ return $array[0];
1273
+ }
1274
+ }
1275
+
1276
+ /**
1277
+ * Return the last date in the date period.
1278
+ *
1279
+ * @return Carbon|null
1280
+ */
1281
+ public function last()
1282
+ {
1283
+ if ($array = $this->toArray()) {
1284
+ return $array[count($array) - 1];
1285
+ }
1286
+ }
1287
+
1288
+ /**
1289
+ * Call given macro.
1290
+ *
1291
+ * @param string $name
1292
+ * @param array $parameters
1293
+ *
1294
+ * @return mixed
1295
+ */
1296
+ protected function callMacro($name, $parameters)
1297
+ {
1298
+ $macro = static::$macros[$name];
1299
+
1300
+ $reflection = new ReflectionFunction($macro);
1301
+
1302
+ $reflectionParameters = $reflection->getParameters();
1303
+
1304
+ $expectedCount = count($reflectionParameters);
1305
+ $actualCount = count($parameters);
1306
+
1307
+ if ($expectedCount > $actualCount && $reflectionParameters[$expectedCount - 1]->name === 'self') {
1308
+ for ($i = $actualCount; $i < $expectedCount - 1; $i++) {
1309
+ $parameters[] = $reflectionParameters[$i]->getDefaultValue();
1310
+ }
1311
+
1312
+ $parameters[] = $this;
1313
+ }
1314
+
1315
+ if ($macro instanceof Closure && method_exists($macro, 'bindTo')) {
1316
+ $macro = $macro->bindTo($this, get_class($this));
1317
+ }
1318
+
1319
+ return call_user_func_array($macro, $parameters);
1320
+ }
1321
+
1322
+ /**
1323
+ * Convert the date period into a string.
1324
+ *
1325
+ * @return string
1326
+ */
1327
+ public function __toString()
1328
+ {
1329
+ return $this->toString();
1330
+ }
1331
+
1332
+ /**
1333
+ * Add aliases for setters.
1334
+ *
1335
+ * CarbonPeriod::days(3)->hours(5)->invert()
1336
+ * ->sinceNow()->until('2010-01-10')
1337
+ * ->filter(...)
1338
+ * ->count()
1339
+ *
1340
+ * Note: We use magic method to let static and instance aliases with the same names.
1341
+ *
1342
+ * @param string $method
1343
+ * @param array $parameters
1344
+ *
1345
+ * @return mixed
1346
+ */
1347
+ public function __call($method, $parameters)
1348
+ {
1349
+ if (static::hasMacro($method)) {
1350
+ return $this->callMacro($method, $parameters);
1351
+ }
1352
+
1353
+ $first = count($parameters) >= 1 ? $parameters[0] : null;
1354
+ $second = count($parameters) >= 2 ? $parameters[1] : null;
1355
+
1356
+ switch ($method) {
1357
+ case 'start':
1358
+ case 'since':
1359
+ return $this->setStartDate($first, $second);
1360
+
1361
+ case 'sinceNow':
1362
+ return $this->setStartDate(new Carbon, $first);
1363
+
1364
+ case 'end':
1365
+ case 'until':
1366
+ return $this->setEndDate($first, $second);
1367
+
1368
+ case 'untilNow':
1369
+ return $this->setEndDate(new Carbon, $first);
1370
+
1371
+ case 'dates':
1372
+ case 'between':
1373
+ return $this->setDates($first, $second);
1374
+
1375
+ case 'recurrences':
1376
+ case 'times':
1377
+ return $this->setRecurrences($first);
1378
+
1379
+ case 'options':
1380
+ return $this->setOptions($first);
1381
+
1382
+ case 'toggle':
1383
+ return $this->toggleOptions($first, $second);
1384
+
1385
+ case 'filter':
1386
+ case 'push':
1387
+ return $this->addFilter($first, $second);
1388
+
1389
+ case 'prepend':
1390
+ return $this->prependFilter($first, $second);
1391
+
1392
+ case 'filters':
1393
+ return $this->setFilters($first ?: array());
1394
+
1395
+ case 'interval':
1396
+ case 'each':
1397
+ case 'every':
1398
+ case 'step':
1399
+ case 'stepBy':
1400
+ return $this->setDateInterval($first);
1401
+
1402
+ case 'invert':
1403
+ return $this->invertDateInterval();
1404
+
1405
+ case 'years':
1406
+ case 'year':
1407
+ case 'months':
1408
+ case 'month':
1409
+ case 'weeks':
1410
+ case 'week':
1411
+ case 'days':
1412
+ case 'dayz':
1413
+ case 'day':
1414
+ case 'hours':
1415
+ case 'hour':
1416
+ case 'minutes':
1417
+ case 'minute':
1418
+ case 'seconds':
1419
+ case 'second':
1420
+ return $this->setDateInterval(call_user_func(
1421
+ // Override default P1D when instantiating via fluent setters.
1422
+ array($this->isDefaultInterval ? new CarbonInterval('PT0S') : $this->dateInterval, $method),
1423
+ count($parameters) === 0 ? 1 : $first
1424
+ ));
1425
+ }
1426
+
1427
+ throw new BadMethodCallException("Method $method does not exist.");
1428
+ }
1429
+ }
src/common/lib/vendor/nesbot/carbon/src/Carbon/Lang/af.php CHANGED
@@ -10,20 +10,20 @@
10
  */
11
 
12
  return array(
13
- 'year' => '1 jaar|:count jare',
14
- 'y' => '1 jaar|:count jare',
15
- 'month' => '1 maand|:count maande',
16
- 'm' => '1 maand|:count maande',
17
- 'week' => '1 week|:count weke',
18
- 'w' => '1 week|:count weke',
19
- 'day' => '1 dag|:count dae',
20
- 'd' => '1 dag|:count dae',
21
- 'hour' => '1 uur|:count ure',
22
- 'h' => '1 uur|:count ure',
23
- 'minute' => '1 minuut|:count minute',
24
- 'min' => '1 minuut|:count minute',
25
- 'second' => '1 sekond|:count sekondes',
26
- 's' => '1 sekond|:count sekondes',
27
  'ago' => ':time terug',
28
  'from_now' => ':time van nou af',
29
  'after' => ':time na',
10
  */
11
 
12
  return array(
13
+ 'year' => ':count jaar|:count jare',
14
+ 'y' => ':count jaar|:count jare',
15
+ 'month' => ':count maand|:count maande',
16
+ 'm' => ':count maand|:count maande',
17
+ 'week' => ':count week|:count weke',
18
+ 'w' => ':count week|:count weke',
19
+ 'day' => ':count dag|:count dae',
20
+ 'd' => ':count dag|:count dae',
21
+ 'hour' => ':count uur|:count ure',
22
+ 'h' => ':count uur|:count ure',
23
+ 'minute' => ':count minuut|:count minute',
24
+ 'min' => ':count minuut|:count minute',
25
+ 'second' => ':count sekond|:count sekondes',
26
+ 's' => ':count sekond|:count sekondes',
27
  'ago' => ':time terug',
28
  'from_now' => ':time van nou af',
29
  'after' => ':time na',
src/common/lib/vendor/nesbot/carbon/src/Carbon/Lang/bg.php CHANGED
@@ -10,20 +10,20 @@
10
  */
11
 
12
  return array(
13
- 'year' => '1 година|:count години',
14
- 'y' => '1 година|:count години',
15
- 'month' => '1 месец|:count месеца',
16
- 'm' => '1 месец|:count месеца',
17
- 'week' => '1 седмица|:count седмици',
18
- 'w' => '1 седмица|:count седмици',
19
- 'day' => '1 ден|:count дни',
20
- 'd' => '1 ден|:count дни',
21
- 'hour' => '1 час|:count часа',
22
- 'h' => '1 час|:count часа',
23
- 'minute' => '1 минута|:count минути',
24
- 'min' => '1 минута|:count минути',
25
- 'second' => '1 секунда|:count секунди',
26
- 's' => '1 секунда|:count секунди',
27
  'ago' => 'преди :time',
28
  'from_now' => ':time от сега',
29
  'after' => 'след :time',
10
  */
11
 
12
  return array(
13
+ 'year' => ':count година|:count години',
14
+ 'y' => ':count година|:count години',
15
+ 'month' => ':count месец|:count месеца',
16
+ 'm' => ':count месец|:count месеца',
17
+ 'week' => ':count седмица|:count седмици',
18
+ 'w' => ':count седмица|:count седмици',
19
+ 'day' => ':count ден|:count дни',
20
+ 'd' => ':count ден|:count дни',
21
+ 'hour' => ':count час|:count часа',
22
+ 'h' => ':count час|:count часа',
23
+ 'minute' => ':count минута|:count минути',
24
+ 'min' => ':count минута|:count минути',
25
+ 'second' => ':count секунда|:count секунди',
26
+ 's' => ':count секунда|:count секунди',
27
  'ago' => 'преди :time',
28
  'from_now' => ':time от сега',
29
  'after' => 'след :time',
src/common/lib/vendor/nesbot/carbon/src/Carbon/Lang/bs_BA.php ADDED
@@ -0,0 +1,31 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Carbon package.
5
+ *
6
+ * (c) Brian Nesbitt <brian@nesbot.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ return array(
13
+ 'year' => ':count godina|:count godine|:count godina',
14
+ 'y' => ':count godina|:count godine|:count godina',
15
+ 'month' => ':count mjesec|:count mjeseca|:count mjeseci',
16
+ 'm' => ':count mjesec|:count mjeseca|:count mjeseci',
17
+ 'week' => ':count nedjelja|:count nedjelje|:count nedjelja',
18
+ 'w' => ':count nedjelja|:count nedjelje|:count nedjelja',
19
+ 'day' => ':count dan|:count dana|:count dana',
20
+ 'd' => ':count dan|:count dana|:count dana',
21
+ 'hour' => ':count sat|:count sata|:count sati',
22
+ 'h' => ':count sat|:count sata|:count sati',
23
+ 'minute' => ':count minut|:count minuta|:count minuta',
24
+ 'min' => ':count minut|:count minuta|:count minuta',
25
+ 'second' => ':count sekund|:count sekunda|:count sekundi',
26
+ 's' => ':count sekund|:count sekunda|:count sekundi',
27
+ 'ago' => 'prije :time',
28
+ 'from_now' => 'za :time',
29
+ 'after' => 'nakon :time',
30
+ 'before' => ':time ranije',
31
+ );
src/common/lib/vendor/nesbot/carbon/src/Carbon/Lang/ca.php CHANGED
@@ -10,22 +10,27 @@
10
  */
11
 
12
  return array(
13
- 'year' => '1 any|:count anys',
14
- 'y' => '1 any|:count anys',
15
- 'month' => '1 mes|:count mesos',
16
- 'm' => '1 mes|:count mesos',
17
- 'week' => '1 setmana|:count setmanes',
18
- 'w' => '1 setmana|:count setmanes',
19
- 'day' => '1 dia|:count dies',
20
- 'd' => '1 dia|:count dies',
21
- 'hour' => '1 hora|:count hores',
22
- 'h' => '1 hora|:count hores',
23
- 'minute' => '1 minut|:count minuts',
24
- 'min' => '1 minut|:count minuts',
25
- 'second' => '1 segon|:count segons',
26
- 's' => '1 segon|:count segons',
27
  'ago' => 'fa :time',
28
  'from_now' => 'dins de :time',
29
  'after' => ':time després',
30
  'before' => ':time abans',
 
 
 
 
 
31
  );
10
  */
11
 
12
  return array(
13
+ 'year' => ':count any|:count anys',
14
+ 'y' => ':count any|:count anys',
15
+ 'month' => ':count mes|:count mesos',
16
+ 'm' => ':count mes|:count mesos',
17
+ 'week' => ':count setmana|:count setmanes',
18
+ 'w' => ':count setmana|:count setmanes',
19
+ 'day' => ':count dia|:count dies',
20
+ 'd' => ':count dia|:count dies',
21
+ 'hour' => ':count hora|:count hores',
22
+ 'h' => ':count hora|:count hores',
23
+ 'minute' => ':count minut|:count minuts',
24
+ 'min' => ':count minut|:count minuts',
25
+ 'second' => ':count segon|:count segons',
26
+ 's' => ':count segon|:count segons',
27
  'ago' => 'fa :time',
28
  'from_now' => 'dins de :time',
29
  'after' => ':time després',
30
  'before' => ':time abans',
31
+ 'diff_now' => 'ara mateix',
32
+ 'diff_yesterday' => 'ahir',
33
+ 'diff_tomorrow' => 'demà',
34
+ 'diff_before_yesterday' => "abans d'ahir",
35
+ 'diff_after_tomorrow' => 'demà passat',
36
  );
src/common/lib/vendor/nesbot/carbon/src/Carbon/Lang/cs.php CHANGED
@@ -10,20 +10,20 @@
10
  */
11
 
12
  return array(
13
- 'year' => '1 rok|:count roky|:count let',
14
- 'y' => '1 rok|:count roky|:count let',
15
- 'month' => '1 měsíc|:count měsíce|:count měsíců',
16
- 'm' => '1 měsíc|:count měsíce|:count měsíců',
17
- 'week' => '1 týden|:count týdny|:count týdnů',
18
- 'w' => '1 týden|:count týdny|:count týdnů',
19
- 'day' => '1 den|:count dny|:count dní',
20
- 'd' => '1 den|:count dny|:count dní',
21
- 'hour' => '1 hodinu|:count hodiny|:count hodin',
22
- 'h' => '1 hodinu|:count hodiny|:count hodin',
23
- 'minute' => '1 minutu|:count minuty|:count minut',
24
- 'min' => '1 minutu|:count minuty|:count minut',
25
- 'second' => '1 sekundu|:count sekundy|:count sekund',
26
- 's' => '1 sekundu|:count sekundy|:count sekund',
27
  'ago' => ':time nazpět',
28
  'from_now' => 'za :time',
29
  'after' => ':time později',
10
  */
11
 
12
  return array(
13
+ 'year' => ':count rok|:count roky|:count let',
14
+ 'y' => ':count rok|:count roky|:count let',
15
+ 'month' => ':count měsíc|:count měsíce|:count měsíců',
16
+ 'm' => ':count měsíc|:count měsíce|:count měsíců',
17
+ 'week' => ':count týden|:count týdny|:count týdnů',
18
+ 'w' => ':count týden|:count týdny|:count týdnů',
19
+ 'day' => ':count den|:count dny|:count dní',
20
+ 'd' => ':count den|:count dny|:count dní',
21
+ 'hour' => ':count hodinu|:count hodiny|:count hodin',
22
+ 'h' => ':count hodinu|:count hodiny|:count hodin',
23
+ 'minute' => ':count minutu|:count minuty|:count minut',
24
+ 'min' => ':count minutu|:count minuty|:count minut',
25
+ 'second' => ':count sekundu|:count sekundy|:count sekund',
26
+ 's' => ':count sekundu|:count sekundy|:count sekund',
27
  'ago' => ':time nazpět',
28
  'from_now' => 'za :time',
29
  'after' => ':time později',
src/common/lib/vendor/nesbot/carbon/src/Carbon/Lang/cy.php ADDED
@@ -0,0 +1,29 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /*
3
+ * This file is part of the Carbon package.
4
+ *
5
+ * (c) Brian Nesbitt <brian@nesbot.com>
6
+ *
7
+ * For the full copyright and license information, please view the LICENSE
8
+ * file that was distributed with this source code.
9
+ */
10
+ return array(
11
+ 'year' => '1 flwyddyn|:count blynedd',
12
+ 'y' => ':countbl',
13
+ 'month' => '1 mis|:count fis',
14
+ 'm' => ':countmi',
15
+ 'week' => ':count wythnos',
16
+ 'w' => ':countw',
17
+ 'day' => ':count diwrnod',
18
+ 'd' => ':countd',
19
+ 'hour' => ':count awr',
20
+ 'h' => ':counth',
21
+ 'minute' => ':count munud',
22
+ 'min' => ':countm',
23
+ 'second' => ':count eiliad',
24
+ 's' => ':counts',
25
+ 'ago' => ':time yn ôl',
26
+ 'from_now' => ':time o hyn ymlaen',
27
+ 'after' => ':time ar ôl',
28
+ 'before' => ':time o\'r blaen',
29
+ );
src/common/lib/vendor/nesbot/carbon/src/Carbon/Lang/da.php CHANGED
@@ -10,20 +10,20 @@
10
  */
11
 
12
  return array(
13
- 'year' => '1 år|:count år',
14
- 'y' => '1 år|:count år',
15
- 'month' => '1 måned|:count måneder',
16
- 'm' => '1 måned|:count måneder',
17
- 'week' => '1 uge|:count uger',
18
- 'w' => '1 uge|:count uger',
19
- 'day' => '1 dag|:count dage',
20
- 'd' => '1 dag|:count dage',
21
- 'hour' => '1 time|:count timer',
22
- 'h' => '1 time|:count timer',
23
- 'minute' => '1 minut|:count minutter',
24
- 'min' => '1 minut|:count minutter',
25
- 'second' => '1 sekund|:count sekunder',
26
- 's' => '1 sekund|:count sekunder',
27
  'ago' => ':time siden',
28
  'from_now' => 'om :time',
29
  'after' => ':time efter',
10
  */
11
 
12
  return array(
13
+ 'year' => ':count år|:count år',
14
+ 'y' => ':count år|:count år',
15
+ 'month' => ':count måned|:count måneder',
16
+ 'm' => ':count måned|:count måneder',
17
+ 'week' => ':count uge|:count uger',
18
+ 'w' => ':count uge|:count uger',
19
+ 'day' => ':count dag|:count dage',
20
+ 'd' => ':count dag|:count dage',
21
+ 'hour' => ':count time|:count timer',
22
+ 'h' => ':count time|:count timer',
23
+ 'minute' => ':count minut|:count minutter',
24
+ 'min' => ':count minut|:count minutter',
25
+ 'second' => ':count sekund|:count sekunder',
26
+ 's' => ':count sekund|:count sekunder',
27
  'ago' => ':time siden',
28
  'from_now' => 'om :time',
29
  'after' => ':time efter',
src/common/lib/vendor/nesbot/carbon/src/Carbon/Lang/de.php CHANGED
@@ -10,31 +10,37 @@
10
  */
11
 
12
  return array(
13
- 'year' => '1 Jahr|:count Jahre',
14
- 'y' => '1J|:countJ',
15
- 'month' => '1 Monat|:count Monate',
16
- 'm' => '1Mon|:countMon',
17
- 'week' => '1 Woche|:count Wochen',
18
- 'w' => '1Wo|:countWo',
19
- 'day' => '1 Tag|:count Tage',
20
- 'd' => '1Tg|:countTg',
21
- 'hour' => '1 Stunde|:count Stunden',
22
- 'h' => '1Std|:countStd',
23
- 'minute' => '1 Minute|:count Minuten',
24
- 'min' => '1Min|:countMin',
25
- 'second' => '1 Sekunde|:count Sekunden',
26
- 's' => '1Sek|:countSek',
27
  'ago' => 'vor :time',
28
  'from_now' => 'in :time',
29
  'after' => ':time später',
30
  'before' => ':time zuvor',
31
 
32
- 'year_from_now' => '1 Jahr|:count Jahren',
33
- 'month_from_now' => '1 Monat|:count Monaten',
34
- 'week_from_now' => '1 Woche|:count Wochen',
35
- 'day_from_now' => '1 Tag|:count Tagen',
36
- 'year_ago' => '1 Jahr|:count Jahren',
37
- 'month_ago' => '1 Monat|:count Monaten',
38
- 'week_ago' => '1 Woche|:count Wochen',
39
- 'day_ago' => '1 Tag|:count Tagen',
 
 
 
 
 
 
40
  );
10
  */
11
 
12
  return array(
13
+ 'year' => ':count Jahr|:count Jahre',
14
+ 'y' => ':countJ|:countJ',
15
+ 'month' => ':count Monat|:count Monate',
16
+ 'm' => ':countMon|:countMon',
17
+ 'week' => ':count Woche|:count Wochen',
18
+ 'w' => ':countWo|:countWo',
19
+ 'day' => ':count Tag|:count Tage',
20
+ 'd' => ':countTg|:countTg',
21
+ 'hour' => ':count Stunde|:count Stunden',
22
+ 'h' => ':countStd|:countStd',
23
+ 'minute' => ':count Minute|:count Minuten',
24
+ 'min' => ':countMin|:countMin',
25
+ 'second' => ':count Sekunde|:count Sekunden',
26
+ 's' => ':countSek|:countSek',
27
  'ago' => 'vor :time',
28
  'from_now' => 'in :time',
29
  'after' => ':time später',
30
  'before' => ':time zuvor',
31
 
32
+ 'year_from_now' => ':count Jahr|:count Jahren',
33
+ 'month_from_now' => ':count Monat|:count Monaten',
34
+ 'week_from_now' => ':count Woche|:count Wochen',
35
+ 'day_from_now' => ':count Tag|:count Tagen',
36
+ 'year_ago' => ':count Jahr|:count Jahren',
37
+ 'month_ago' => ':count Monat|:count Monaten',
38
+ 'week_ago' => ':count Woche|:count Wochen',
39
+ 'day_ago' => ':count Tag|:count Tagen',
40
+
41
+ 'diff_now' => 'Gerade eben',
42
+ 'diff_yesterday' => 'Gestern',
43
+ 'diff_tomorrow' => 'Heute',
44
+ 'diff_before_yesterday' => 'Vorgestern',
45
+ 'diff_after_tomorrow' => 'Übermorgen',
46
  );
src/common/lib/vendor/nesbot/carbon/src/Carbon/Lang/el.php CHANGED
@@ -10,20 +10,20 @@
10
  */
11
 
12
  return array(
13
- 'year' => '1 χρόνος|:count χρόνια',
14
- 'y' => '1 χρόνος|:count χρόνια',
15
- 'month' => '1 μήνας|:count μήνες',
16
- 'm' => '1 μήνας|:count μήνες',
17
- 'week' => '1 εβδομάδα|:count εβδομάδες',
18
- 'w' => '1 εβδομάδα|:count εβδομάδες',
19
- 'day' => '1 μέρα|:count μέρες',
20
- 'd' => '1 μέρα|:count μέρες',
21
- 'hour' => '1 ώρα|:count ώρες',
22
- 'h' => '1 ώρα|:count ώρες',
23
- 'minute' => '1 λεπτό|:count λεπτά',
24
- 'min' => '1 λεπτό|:count λεπτά',
25
- 'second' => '1 δευτερόλεπτο|:count δευτερόλεπτα',
26
- 's' => '1 δευτερόλεπτο|:count δευτερόλεπτα',
27
  'ago' => 'πριν από :time',
28
  'from_now' => 'σε :time από τώρα',
29
  'after' => ':time μετά',
10
  */
11
 
12
  return array(
13
+ 'year' => ':count χρόνος|:count χρόνια',
14
+ 'y' => ':count χρόνος|:count χρόνια',
15
+ 'month' => ':count μήνας|:count μήνες',
16
+ 'm' => ':count μήνας|:count μήνες',
17
+ 'week' => ':count εβδομάδα|:count εβδομάδες',
18
+ 'w' => ':count εβδομάδα|:count εβδομάδες',
19
+ 'day' => ':count μέρα|:count μέρες',
20
+ 'd' => ':count μέρα|:count μέρες',
21
+ 'hour' => ':count ώρα|:count ώρες',
22
+ 'h' => ':count ώρα|:count ώρες',
23
+ 'minute' => ':count λεπτό|:count λεπτά',
24
+ 'min' => ':count λεπτό|:count λεπτά',
25
+ 'second' => ':count δευτερόλεπτο|:count δευτερόλεπτα',
26
+ 's' => ':count δευτερόλεπτο|:count δευτερόλεπτα',
27
  'ago' => 'πριν από :time',
28
  'from_now' => 'σε :time από τώρα',
29
  'after' => ':time μετά',
src/common/lib/vendor/nesbot/carbon/src/Carbon/Lang/en.php CHANGED
@@ -10,22 +10,31 @@
10
  */
11
 
12
  return array(
13
- 'year' => '1 year|:count years',
14
- 'y' => '1yr|:countyrs',
15
- 'month' => '1 month|:count months',
16
- 'm' => '1mo|:countmos',
17
- 'week' => '1 week|:count weeks',
18
- 'w' => '1w|:countw',
19
- 'day' => '1 day|:count days',
20
- 'd' => '1d|:countd',
21
- 'hour' => '1 hour|:count hours',
22
- 'h' => '1h|:counth',
23
- 'minute' => '1 minute|:count minutes',
24
- 'min' => '1m|:countm',
25
- 'second' => '1 second|:count seconds',
26
- 's' => '1s|:counts',
27
  'ago' => ':time ago',
28
  'from_now' => ':time from now',
29
  'after' => ':time after',
30
  'before' => ':time before',
 
 
 
 
 
 
 
 
 
31
  );
10
  */
11
 
12
  return array(
13
+ 'year' => ':count year|:count years',
14
+ 'y' => ':countyr|:countyrs',
15
+ 'month' => ':count month|:count months',
16
+ 'm' => ':countmo|:countmos',
17
+ 'week' => ':count week|:count weeks',
18
+ 'w' => ':countw|:countw',
19
+ 'day' => ':count day|:count days',
20
+ 'd' => ':countd|:countd',
21
+ 'hour' => ':count hour|:count hours',
22
+ 'h' => ':counth|:counth',
23
+ 'minute' => ':count minute|:count minutes',
24
+ 'min' => ':countm|:countm',
25
+ 'second' => ':count second|:count seconds',
26
+ 's' => ':counts|:counts',
27
  'ago' => ':time ago',
28
  'from_now' => ':time from now',
29
  'after' => ':time after',
30
  'before' => ':time before',
31
+ 'diff_now' => 'just now',
32
+ 'diff_yesterday' => 'yesterday',
33
+ 'diff_tomorrow' => 'tomorrow',
34
+ 'diff_before_yesterday' => 'before yesterday',
35
+ 'diff_after_tomorrow' => 'after tomorrow',
36
+ 'period_recurrences' => 'once|:count times',
37
+ 'period_interval' => 'every :interval',
38
+ 'period_start_date' => 'from :date',
39
+ 'period_end_date' => 'to :date',
40
  );
src/common/lib/vendor/nesbot/carbon/src/Carbon/Lang/eo.php CHANGED
@@ -10,20 +10,20 @@
10
  */
11
 
12
  return array(
13
- 'year' => '1 jaro|:count jaroj',
14
- 'y' => '1 jaro|:count jaroj',
15
- 'month' => '1 monato|:count monatoj',
16
- 'm' => '1 monato|:count monatoj',
17
- 'week' => '1 semajno|:count semajnoj',
18
- 'w' => '1 semajno|:count semajnoj',
19
- 'day' => '1 tago|:count tagoj',
20
- 'd' => '1 tago|:count tagoj',
21
- 'hour' => '1 horo|:count horoj',
22
- 'h' => '1 horo|:count horoj',
23
- 'minute' => '1 minuto|:count minutoj',
24
- 'min' => '1 minuto|:count minutoj',
25
- 'second' => '1 sekundo|:count sekundoj',
26
- 's' => '1 sekundo|:count sekundoj',
27
  'ago' => 'antaŭ :time',
28
  'from_now' => 'je :time',
29
  'after' => ':time poste',
10
  */
11
 
12
  return array(
13
+ 'year' => ':count jaro|:count jaroj',
14
+ 'y' => ':count jaro|:count jaroj',
15
+ 'month' => ':count monato|:count monatoj',
16
+ 'm' => ':count monato|:count monatoj',
17
+ 'week' => ':count semajno|:count semajnoj',
18
+ 'w' => ':count semajno|:count semajnoj',
19
+ 'day' => ':count tago|:count tagoj',
20
+ 'd' => ':count tago|:count tagoj',
21
+ 'hour' => ':count horo|:count horoj',
22
+ 'h' => ':count horo|:count horoj',
23
+ 'minute' => ':count minuto|:count minutoj',
24
+ 'min' => ':count minuto|:count minutoj',
25
+ 'second' => ':count sekundo|:count sekundoj',
26
+ 's' => ':count sekundo|:count sekundoj',
27
  'ago' => 'antaŭ :time',
28
  'from_now' => 'je :time',
29
  'after' => ':time poste',
src/common/lib/vendor/nesbot/carbon/src/Carbon/Lang/es.php CHANGED
@@ -10,22 +10,27 @@
10
  */
11
 
12
  return array(
13
- 'year' => '1 año|:count años',
14
- 'y' => '1 año|:count años',
15
- 'month' => '1 mes|:count meses',
16
- 'm' => '1 mes|:count meses',
17
- 'week' => '1 semana|:count semanas',
18
- 'w' => '1 semana|:count semanas',
19
- 'day' => '1 día|:count días',
20
- 'd' => '1 día|:count días',
21
- 'hour' => '1 hora|:count horas',
22
- 'h' => '1 hora|:count horas',
23
- 'minute' => '1 minuto|:count minutos',
24
- 'min' => '1 minuto|:count minutos',
25
- 'second' => '1 segundo|:count segundos',
26
- 's' => '1 segundo|:count segundos',
27
  'ago' => 'hace :time',
28
  'from_now' => 'dentro de :time',
29
  'after' => ':time después',
30
  'before' => ':time antes',
 
 
 
 
 
31
  );
10
  */
11
 
12
  return array(
13
+ 'year' => ':count año|:count años',
14
+ 'y' => ':count año|:count años',
15
+ 'month' => ':count mes|:count meses',
16
+ 'm' => ':count mes|:count meses',
17
+ 'week' => ':count semana|:count semanas',
18
+ 'w' => ':count semana|:count semanas',
19
+ 'day' => ':count día|:count días',
20
+ 'd' => ':count día|:count días',
21
+ 'hour' => ':count hora|:count horas',
22
+ 'h' => ':count hora|:count horas',
23
+ 'minute' => ':count minuto|:count minutos',
24
+ 'min' => ':count minuto|:count minutos',
25
+ 'second' => ':count segundo|:count segundos',
26
+ 's' => ':count segundo|:count segundos',
27
  'ago' => 'hace :time',
28
  'from_now' => 'dentro de :time',
29
  'after' => ':time después',
30
  'before' => ':time antes',
31
+ 'diff_now' => 'ahora mismo',
32
+ 'diff_yesterday' => 'ayer',
33
+ 'diff_tomorrow' => 'mañana',
34
+ 'diff_before_yesterday' => 'anteayer',
35
+ 'diff_after_tomorrow' => 'pasado mañana',
36
  );
src/common/lib/vendor/nesbot/carbon/src/Carbon/Lang/et.php CHANGED
@@ -10,20 +10,20 @@
10
  */
11
 
12
  return array(
13
- 'year' => '1 aasta|:count aastat',
14
- 'y' => '1 aasta|:count aastat',
15
- 'month' => '1 kuu|:count kuud',
16
- 'm' => '1 kuu|:count kuud',
17
- 'week' => '1 nädal|:count nädalat',
18
- 'w' => '1 nädal|:count nädalat',
19
- 'day' => '1 päev|:count päeva',
20
- 'd' => '1 päev|:count päeva',
21
- 'hour' => '1 tund|:count tundi',
22
- 'h' => '1 tund|:count tundi',
23
- 'minute' => '1 minut|:count minutit',
24
- 'min' => '1 minut|:count minutit',
25
- 'second' => '1 sekund|:count sekundit',
26
- 's' => '1 sekund|:count sekundit',
27
  'ago' => ':time tagasi',
28
  'from_now' => ':time pärast',
29
  'after' => ':time pärast',
10
  */
11
 
12
  return array(
13
+ 'year' => ':count aasta|:count aastat',
14
+ 'y' => ':count aasta|:count aastat',
15
+ 'month' => ':count kuu|:count kuud',
16
+ 'm' => ':count kuu|:count kuud',
17
+ 'week' => ':count nädal|:count nädalat',
18
+ 'w' => ':count nädal|:count nädalat',
19
+ 'day' => ':count päev|:count päeva',
20
+ 'd' => ':count päev|:count päeva',
21
+ 'hour' => ':count tund|:count tundi',
22
+ 'h' => ':count tund|:count tundi',
23
+ 'minute' => ':count minut|:count minutit',
24
+ 'min' => ':count minut|:count minutit',
25
+ 'second' => ':count sekund|:count sekundit',
26
+ 's' => ':count sekund|:count sekundit',
27
  'ago' => ':time tagasi',
28
  'from_now' => ':time pärast',
29
  'after' => ':time pärast',
src/common/lib/vendor/nesbot/carbon/src/Carbon/Lang/fi.php CHANGED
@@ -10,20 +10,20 @@
10
  */
11
 
12
  return array(
13
- 'year' => '1 vuosi|:count vuotta',
14
- 'y' => '1 vuosi|:count vuotta',
15
- 'month' => '1 kuukausi|:count kuukautta',
16
- 'm' => '1 kuukausi|:count kuukautta',
17
- 'week' => '1 viikko|:count viikkoa',
18
- 'w' => '1 viikko|:count viikkoa',
19
- 'day' => '1 päivä|:count päivää',
20
- 'd' => '1 päivä|:count päivää',
21
- 'hour' => '1 tunti|:count tuntia',
22
- 'h' => '1 tunti|:count tuntia',
23
- 'minute' => '1 minuutti|:count minuuttia',
24
- 'min' => '1 minuutti|:count minuuttia',
25
- 'second' => '1 sekunti|:count sekuntia',
26
- 's' => '1 sekunti|:count sekuntia',
27
  'ago' => ':time sitten',
28
  'from_now' => ':time tästä hetkestä',
29
  'after' => ':time sen jälkeen',
10
  */
11
 
12
  return array(
13
+ 'year' => ':count vuosi|:count vuotta',
14
+ 'y' => ':count vuosi|:count vuotta',
15
+ 'month' => ':count kuukausi|:count kuukautta',
16
+ 'm' => ':count kuukausi|:count kuukautta',
17
+ 'week' => ':count viikko|:count viikkoa',
18
+ 'w' => ':count viikko|:count viikkoa',
19
+ 'day' => ':count päivä|:count päivää',
20
+ 'd' => ':count päivä|:count päivää',
21
+ 'hour' => ':count tunti|:count tuntia',
22
+ 'h' => ':count tunti|:count tuntia',
23
+ 'minute' => ':count minuutti|:count minuuttia',
24
+ 'min' => ':count minuutti|:count minuuttia',
25
+ 'second' => ':count sekunti|:count sekuntia',
26
+ 's' => ':count sekunti|:count sekuntia',
27
  'ago' => ':time sitten',
28
  'from_now' => ':time tästä hetkestä',
29
  'after' => ':time sen jälkeen',
src/common/lib/vendor/nesbot/carbon/src/Carbon/Lang/fo.php CHANGED
@@ -10,20 +10,20 @@
10
  */
11
 
12
  return array(
13
- 'year' => '1 ár|:count ár',
14
- 'y' => '1 ár|:count ár',
15
- 'month' => '1 mánaður|:count mánaðir',
16
- 'm' => '1 mánaður|:count mánaðir',
17
- 'week' => '1 vika|:count vikur',
18
- 'w' => '1 vika|:count vikur',
19
- 'day' => '1 dag|:count dagar',
20
- 'd' => '1 dag|:count dagar',
21
- 'hour' => '1 tími|:count tímar',
22
- 'h' => '1 tími|:count tímar',
23
- 'minute' => '1 minutt|:count minuttir',
24
- 'min' => '1 minutt|:count minuttir',
25
- 'second' => '1 sekund|:count sekundir',
26
- 's' => '1 sekund|:count sekundir',
27
  'ago' => ':time síðan',
28
  'from_now' => 'um :time',
29
  'after' => ':time aftaná',
10
  */
11
 
12
  return array(
13
+ 'year' => ':count ár|:count ár',
14
+ 'y' => ':count ár|:count ár',
15
+ 'month' => ':count mánaður|:count mánaðir',
16
+ 'm' => ':count mánaður|:count mánaðir',
17
+ 'week' => ':count vika|:count vikur',
18
+ 'w' => ':count vika|:count vikur',
19
+ 'day' => ':count dag|:count dagar',
20
+ 'd' => ':count dag|:count dagar',
21
+ 'hour' => ':count tími|:count tímar',
22
+ 'h' => ':count tími|:count tímar',
23
+ 'minute' => ':count minutt|:count minuttir',
24
+ 'min' => ':count minutt|:count minuttir',
25
+ 'second' => ':count sekund|:count sekundir',
26
+ 's' => ':count sekund|:count sekundir',
27
  'ago' => ':time síðan',
28
  'from_now' => 'um :time',
29
  'after' => ':time aftaná',
src/common/lib/vendor/nesbot/carbon/src/Carbon/Lang/fr.php CHANGED
@@ -10,22 +10,27 @@
10
  */
11
 
12
  return array(
13
- 'year' => '1 an|:count ans',
14
- 'y' => '1 an|:count ans',
15
  'month' => ':count mois',
16
  'm' => ':count mois',
17
- 'week' => '1 semaine|:count semaines',
18
- 'w' => '1 sem.|:count sem.',
19
- 'day' => '1 jour|:count jours',
20
- 'd' => '1 j.|:count j.',
21
- 'hour' => '1 heure|:count heures',
22
- 'h' => '1 h|:count h.',
23
- 'minute' => '1 minute|:count minutes',
24
- 'min' => '1 min.|:count min.',
25
- 'second' => '1 seconde|:count secondes',
26
- 's' => '1 sec.|:count sec.',
27
  'ago' => 'il y a :time',
28
  'from_now' => 'dans :time',
29
  'after' => ':time après',
30
  'before' => ':time avant',
 
 
 
 
 
31
  );
10
  */
11
 
12
  return array(
13
+ 'year' => ':count an|:count ans',
14
+ 'y' => ':count an|:count ans',
15
  'month' => ':count mois',
16
  'm' => ':count mois',
17
+ 'week' => ':count semaine|:count semaines',
18
+ 'w' => ':count sem.',
19
+ 'day' => ':count jour|:count jours',
20
+ 'd' => ':count j.',
21
+ 'hour' => ':count heure|:count heures',
22
+ 'h' => ':count h.',
23
+ 'minute' => ':count minute|:count minutes',
24
+ 'min' => ':count min.',
25
+ 'second' => ':count seconde|:count secondes',
26
+ 's' => ':count sec.',
27
  'ago' => 'il y a :time',
28
  'from_now' => 'dans :time',
29
  'after' => ':time après',
30
  'before' => ':time avant',
31
+ 'diff_now' => "à l'instant",
32
+ 'diff_yesterday' => 'hier',
33
+ 'diff_tomorrow' => 'demain',
34
+ 'diff_before_yesterday' => 'avant-hier',
35
+ 'diff_after_tomorrow' => 'après-demain',
36
  );
src/common/lib/vendor/nesbot/carbon/src/Carbon/Lang/gl.php CHANGED
@@ -10,13 +10,13 @@
10
  */
11
 
12
  return array(
13
- 'year' => '1 ano|:count anos',
14
- 'month' => '1 mes|:count meses',
15
- 'week' => '1 semana|:count semanas',
16
- 'day' => '1 día|:count días',
17
- 'hour' => '1 hora|:count horas',
18
- 'minute' => '1 minuto|:count minutos',
19
- 'second' => '1 segundo|:count segundos',
20
  'ago' => 'fai :time',
21
  'from_now' => 'dentro de :time',
22
  'after' => ':time despois',
10
  */
11
 
12
  return array(
13
+ 'year' => ':count ano|:count anos',
14
+ 'month' => ':count mes|:count meses',
15
+ 'week' => ':count semana|:count semanas',
16
+ 'day' => ':count día|:count días',
17
+ 'hour' => ':count hora|:count horas',
18
+ 'minute' => ':count minuto|:count minutos',
19
+ 'second' => ':count segundo|:count segundos',
20
  'ago' => 'fai :time',
21
  'from_now' => 'dentro de :time',
22
  'after' => ':time despois',
src/common/lib/vendor/nesbot/carbon/src/Carbon/Lang/gu.php CHANGED
@@ -10,20 +10,20 @@
10
  */
11
 
12
  return array(
13
- 'year' => '1 વર્ષ|:count વર્ષો',
14
- 'y' => '1વર્ષ|:countવર્ષો',
15
- 'month' => '1 મહિનો|:count મહિના',
16
- 'm' => '1મહિનો|:countમહિના',
17
- 'week' => '1 અઠવાડિયું|:count અઠવાડિયા',
18
- 'w' => '1અઠ.|:countઅઠ.',
19
- 'day' => '1 દિવસ|:count દિવસો',
20
- 'd' => '1દિ.|:countદિ.',
21
- 'hour' => '1 કલાક|:count કલાકો',
22
- 'h' => '1ક.|:countક.',
23
- 'minute' => '1 મિનિટ|:count મિનિટ',
24
- 'min' => '1મિ.|:countમિ.',
25
- 'second' => '1 સેકેન્ડ|:count સેકેન્ડ',
26
- 's' => '1સે.|:countસે.',
27
  'ago' => ':time પહેલા',
28
  'from_now' => ':time અત્યારથી',
29
  'after' => ':time પછી',
10
  */
11
 
12
  return array(
13
+ 'year' => ':count વર્ષ|:count વર્ષો',
14
+ 'y' => ':countવર્ષ|:countવર્ષો',
15
+ 'month' => ':count મહિનો|:count મહિના',
16
+ 'm' => ':countમહિનો|:countમહિના',
17
+ 'week' => ':count અઠવાડિયું|:count અઠવાડિયા',
18
+ 'w' => ':countઅઠ.|:countઅઠ.',
19
+ 'day' => ':count દિવસ|:count દિવસો',
20
+ 'd' => ':countદિ.|:countદિ.',
21
+ 'hour' => ':count કલાક|:count કલાકો',
22
+ 'h' => ':countક.|:countક.',
23
+ 'minute' => ':count મિનિટ|:count મિનિટ',
24
+ 'min' => ':countમિ.|:countમિ.',
25
+ 'second' => ':count સેકેન્ડ|:count સેકેન્ડ',
26
+ 's' => ':countસે.|:countસે.',
27
  'ago' => ':time પહેલા',
28
  'from_now' => ':time અત્યારથી',
29
  'after' => ':time પછી',
src/common/lib/vendor/nesbot/carbon/src/Carbon/Lang/hi.php ADDED
@@ -0,0 +1,31 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Carbon package.
5
+ *
6
+ * (c) Brian Nesbitt <brian@nesbot.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ return array(
13
+ 'year' => '1 वर्ष|:count वर्षों',
14
+ 'y' => '1 वर्ष|:count वर्षों',
15
+ 'month' => '1 माह|:count महीने',
16
+ 'm' => '1 माह|:count महीने',
17
+ 'week' => '1 सप्ताह|:count सप्ताह',
18
+ 'w' => '1 सप्ताह|:count सप्ताह',
19
+ 'day' => '1 दिन|:count दिनों',
20
+ 'd' => '1 दिन|:count दिनों',
21
+ 'hour' => '1 घंटा|:count घंटे',
22
+ 'h' => '1 घंटा|:count घंटे',
23
+ 'minute' => '1 मिनट|:count मिनटों',
24
+ 'min' => '1 मिनट|:count मिनटों',
25
+ 'second' => '1 सेकंड|:count सेकंड',
26
+ 's' => '1 सेकंड|:count सेकंड',
27
+ 'ago' => ':time पूर्व',
28
+ 'from_now' => ':time से',
29
+ 'after' => ':time के बाद',
30
+ 'before' => ':time के पहले',
31
+ );
src/common/lib/vendor/nesbot/carbon/src/Carbon/Lang/is.php ADDED
@@ -0,0 +1,31 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Carbon package.
5
+ *
6
+ * (c) Brian Nesbitt <brian@nesbot.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ return array(
13
+ 'year' => '1 ár|:count ár',
14
+ 'y' => '1 ár|:count ár',
15
+ 'month' => '1 mánuður|:count mánuðir',
16
+ 'm' => '1 mánuður|:count mánuðir',
17
+ 'week' => '1 vika|:count vikur',
18
+ 'w' => '1 vika|:count vikur',
19
+ 'day' => '1 dagur|:count dagar',
20
+ 'd' => '1 dagur|:count dagar',
21
+ 'hour' => '1 klukkutími|:count klukkutímar',
22
+ 'h' => '1 klukkutími|:count klukkutímar',
23
+ 'minute' => '1 mínúta|:count mínútur',
24
+ 'min' => '1 mínúta|:count mínútur',
25
+ 'second' => '1 sekúnda|:count sekúndur',
26
+ 's' => '1 sekúnda|:count sekúndur',
27
+ 'ago' => ':time síðan',
28
+ 'from_now' => ':time síðan',
29
+ 'after' => ':time eftir',
30
+ 'before' => ':time fyrir',
31
+ );
src/common/lib/vendor/nesbot/carbon/src/Carbon/Lang/it.php CHANGED
@@ -10,22 +10,27 @@
10
  */
11
 
12
  return array(
13
- 'year' => '1 anno|:count anni',
14
- 'y' => '1 anno|:count anni',
15
- 'month' => '1 mese|:count mesi',
16
- 'm' => '1 mese|:count mesi',
17
- 'week' => '1 settimana|:count settimane',
18
- 'w' => '1 settimana|:count settimane',
19
- 'day' => '1 giorno|:count giorni',
20
- 'd' => '1 giorno|:count giorni',
21
- 'hour' => '1 ora|:count ore',
22
- 'h' => '1 ora|:count ore',
23
- 'minute' => '1 minuto|:count minuti',
24
- 'min' => '1 minuto|:count minuti',
25
- 'second' => '1 secondo|:count secondi',
26
- 's' => '1 secondo|:count secondi',
27
  'ago' => ':time fa',
28
- 'from_now' => ':time da adesso',
29
  'after' => ':time dopo',
30
  'before' => ':time prima',
 
 
 
 
 
31
  );
10
  */
11
 
12
  return array(
13
+ 'year' => ':count anno|:count anni',
14
+ 'y' => ':count anno|:count anni',
15
+ 'month' => ':count mese|:count mesi',
16
+ 'm' => ':count mese|:count mesi',
17
+ 'week' => ':count settimana|:count settimane',
18
+ 'w' => ':count settimana|:count settimane',
19
+ 'day' => ':count giorno|:count giorni',
20
+ 'd' => ':count giorno|:count giorni',
21
+ 'hour' => ':count ora|:count ore',
22
+ 'h' => ':count ora|:count ore',
23
+ 'minute' => ':count minuto|:count minuti',
24
+ 'min' => ':count minuto|:count minuti',
25
+ 'second' => ':count secondo|:count secondi',
26
+ 's' => ':count secondo|:count secondi',
27
  'ago' => ':time fa',
28
+ 'from_now' => 'tra :time',
29
  'after' => ':time dopo',
30
  'before' => ':time prima',
31
+ 'diff_now' => 'proprio ora',
32
+ 'diff_yesterday' => 'ieri',
33
+ 'diff_tomorrow' => 'domani',
34
+ 'diff_before_yesterday' => "l'altro ieri",
35
+ 'diff_after_tomorrow' => 'dopodomani',
36
  );
src/common/lib/vendor/nesbot/carbon/src/Carbon/Lang/ko.php CHANGED
@@ -26,6 +26,6 @@ return array(
26
  's' => ':count 초',
27
  'ago' => ':time 전',
28
  'from_now' => ':time 후',
29
- 'after' => ':time ',
30
- 'before' => ':time ',
31
  );
26
  's' => ':count 초',
27
  'ago' => ':time 전',
28
  'from_now' => ':time 후',
29
+ 'after' => ':time 이후',
30
+ 'before' => ':time 이전',
31
  );
src/common/lib/vendor/nesbot/carbon/src/Carbon/Lang/mk.php CHANGED
@@ -10,13 +10,13 @@
10
  */
11
 
12
  return array(
13
- 'year' => '1 година|:count години',
14
- 'month' => '1 месец|:count месеци',
15
- 'week' => '1 седмица|:count седмици',
16
- 'day' => '1 ден|:count дена',
17
- 'hour' => '1 час|:count часа',
18
- 'minute' => '1 минута|:count минути',
19
- 'second' => '1 секунда|:count секунди',
20
  'ago' => 'пред :time',
21
  'from_now' => ':time од сега',
22
  'after' => 'по :time',
10
  */
11
 
12
  return array(
13
+ 'year' => ':count година|:count години',
14
+ 'month' => ':count месец|:count месеци',
15
+ 'week' => ':count седмица|:count седмици',
16
+ 'day' => ':count ден|:count дена',
17
+ 'hour' => ':count час|:count часа',
18
+ 'minute' => ':count минута|:count минути',
19
+ 'second' => ':count секунда|:count секунди',
20
  'ago' => 'пред :time',
21
  'from_now' => ':time од сега',
22
  'after' => 'по :time',
src/common/lib/vendor/nesbot/carbon/src/Carbon/Lang/ne.php ADDED
@@ -0,0 +1,31 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Carbon package.
5
+ *
6
+ * (c) Brian Nesbitt <brian@nesbot.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ return array(
13
+ 'year' => ':count वर्ष',
14
+ 'y' => ':count वर्ष',
15
+ 'month' => ':count महिना',
16
+ 'm' => ':count महिना',
17
+ 'week' => ':count हप्ता',
18
+ 'w' => ':count हप्ता',
19
+ 'day' => ':count दिन',
20
+ 'd' => ':count दिन',
21
+ 'hour' => ':count घण्टा',
22
+ 'h' => ':count घण्टा',
23
+ 'minute' => ':count मिनेट',
24
+ 'min' => ':count मिनेट',
25
+ 'second' => ':count सेकेण्ड',
26
+ 's' => ':count सेकेण्ड',
27
+ 'ago' => ':time पहिले',
28
+ 'from_now' => ':time देखि',
29
+ 'after' => ':time पछि',
30
+ 'before' => ':time अघि',
31
+ );
src/common/lib/vendor/nesbot/carbon/src/Carbon/Lang/nl.php CHANGED
@@ -12,20 +12,25 @@
12
  return array(
13
  'year' => ':count jaar',
14
  'y' => ':count jaar',
15
- 'month' => '1 maand|:count maanden',
16
- 'm' => '1 maand|:count maanden',
17
- 'week' => '1 week|:count weken',
18
- 'w' => '1 week|:count weken',
19
- 'day' => '1 dag|:count dagen',
20
- 'd' => '1 dag|:count dagen',
21
  'hour' => ':count uur',
22
  'h' => ':count uur',
23
- 'minute' => '1 minuut|:count minuten',
24
- 'min' => '1 minuut|:count minuten',
25
- 'second' => '1 seconde|:count seconden',
26
- 's' => '1 seconde|:count seconden',
27
  'ago' => ':time geleden',
28
  'from_now' => 'over :time',
29
  'after' => ':time later',
30
  'before' => ':time eerder',
 
 
 
 
 
31
  );
12
  return array(
13
  'year' => ':count jaar',
14
  'y' => ':count jaar',
15
+ 'month' => ':count maand|:count maanden',
16
+ 'm' => ':count maand|:count maanden',
17
+ 'week' => ':count week|:count weken',
18
+ 'w' => ':count week|:count weken',
19
+ 'day' => ':count dag|:count dagen',
20
+ 'd' => ':count dag|:count dagen',
21
  'hour' => ':count uur',
22
  'h' => ':count uur',
23
+ 'minute' => ':count minuut|:count minuten',
24
+ 'min' => ':count minuut|:count minuten',
25
+ 'second' => ':count seconde|:count seconden',
26
+ 's' => ':count seconde|:count seconden',
27
  'ago' => ':time geleden',
28
  'from_now' => 'over :time',
29
  'after' => ':time later',
30
  'before' => ':time eerder',
31
+ 'diff_now' => 'nu',
32
+ 'diff_yesterday' => 'gisteren',
33
+ 'diff_tomorrow' => 'morgen',
34
+ 'diff_after_tomorrow' => 'overmorgen',
35
+ 'diff_before_yesterday' => 'eergisteren',
36
  );
src/common/lib/vendor/nesbot/carbon/src/Carbon/Lang/no.php CHANGED
@@ -10,22 +10,27 @@
10
  */
11
 
12
  return array(
13
- 'year' => '1 år|:count år',
14
- 'y' => '1 år|:count år',
15
- 'month' => '1 måned|:count måneder',
16
- 'm' => '1 måned|:count måneder',
17
- 'week' => '1 uke|:count uker',
18
- 'w' => '1 uke|:count uker',
19
- 'day' => '1 dag|:count dager',
20
- 'd' => '1 dag|:count dager',
21
- 'hour' => '1 time|:count timer',
22
- 'h' => '1 time|:count timer',
23
- 'minute' => '1 minutt|:count minutter',
24
- 'min' => '1 minutt|:count minutter',
25
- 'second' => '1 sekund|:count sekunder',
26
- 's' => '1 sekund|:count sekunder',
27
  'ago' => ':time siden',
28
  'from_now' => 'om :time',
29
  'after' => ':time etter',
30
  'before' => ':time før',
 
 
 
 
 
31
  );
10
  */
11
 
12
  return array(
13
+ 'year' => ':count år|:count år',
14
+ 'y' => ':count år|:count år',
15
+ 'month' => ':count måned|:count måneder',
16
+ 'm' => ':count måned|:count måneder',
17
+ 'week' => ':count uke|:count uker',
18
+ 'w' => ':count uke|:count uker',
19
+ 'day' => ':count dag|:count dager',
20
+ 'd' => ':count dag|:count dager',
21
+ 'hour' => ':count time|:count timer',
22
+ 'h' => ':count time|:count timer',
23
+ 'minute' => ':count minutt|:count minutter',
24
+ 'min' => ':count minutt|:count minutter',
25
+ 'second' => ':count sekund|:count sekunder',
26
+ 's' => ':count sekund|:count sekunder',
27
  'ago' => ':time siden',
28
  'from_now' => 'om :time',
29
  'after' => ':time etter',
30
  'before' => ':time før',
31
+ 'diff_now' => 'akkurat nå',
32
+ 'diff_yesterday' => 'i går',
33
+ 'diff_tomorrow' => 'i morgen',
34
+ 'diff_before_yesterday' => 'i forgårs',
35
+ 'diff_after_tomorrow' => 'i overmorgen',
36
  );
src/common/lib/vendor/nesbot/carbon/src/Carbon/Lang/oc.php ADDED
@@ -0,0 +1,40 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Carbon package.
5
+ *
6
+ * (c) Brian Nesbitt <brian@nesbot.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ \Symfony\Component\Translation\PluralizationRules::set(function ($number) {
13
+ return $number == 1 ? 0 : 1;
14
+ }, 'oc');
15
+
16
+ return array(
17
+ 'year' => ':count an|:count ans',
18
+ 'y' => ':count an|:count ans',
19
+ 'month' => ':count mes|:count meses',
20
+ 'm' => ':count mes|:count meses',
21
+ 'week' => ':count setmana|:count setmanas',
22
+ 'w' => ':count setmana|:count setmanas',
23
+ 'day' => ':count jorn|:count jorns',
24
+ 'd' => ':count jorn|:count jorns',
25
+ 'hour' => ':count ora|:count oras',
26
+ 'h' => ':count ora|:count oras',
27
+ 'minute' => ':count minuta|:count minutas',
28
+ 'min' => ':count minuta|:count minutas',
29
+ 'second' => ':count segonda|:count segondas',
30
+ 's' => ':count segonda|:count segondas',
31
+ 'ago' => 'fa :time',
32
+ 'from_now' => 'dins :time',
33
+ 'after' => ':time aprèp',
34
+ 'before' => ':time abans',
35
+ 'diff_now' => 'ara meteis',
36
+ 'diff_yesterday' => 'ièr',
37
+ 'diff_tomorrow' => 'deman',
38
+ 'diff_before_yesterday' => 'ièr delà',
39
+ 'diff_after_tomorrow' => 'deman passat',
40
+ );
src/common/lib/vendor/nesbot/carbon/src/Carbon/Lang/pl.php CHANGED
@@ -10,22 +10,27 @@
10
  */
11
 
12
  return array(
13
- 'year' => '1 rok|:count lata|:count lat',
14
- 'y' => '1 rok|:count lata|:count lat',
15
- 'month' => '1 miesiąc|:count miesiące|:count miesięcy',
16
- 'm' => '1 miesiąc|:count miesiące|:count miesięcy',
17
- 'week' => '1 tydzień|:count tygodnie|:count tygodni',
18
- 'w' => '1 tydzień|:count tygodnie|:count tygodni',
19
- 'day' => '1 dzień|:count dni|:count dni',
20
- 'd' => '1 dzień|:count dni|:count dni',
21
- 'hour' => '1 godzina|:count godziny|:count godzin',
22
- 'h' => '1 godzina|:count godziny|:count godzin',
23
- 'minute' => '1 minuta|:count minuty|:count minut',
24
- 'min' => '1 minuta|:count minuty|:count minut',
25
- 'second' => '1 sekunda|:count sekundy|:count sekund',
26
- 's' => '1 sekunda|:count sekundy|:count sekund',
27
  'ago' => ':time temu',
28
  'from_now' => ':time od teraz',
29
  'after' => ':time po',
30
  'before' => ':time przed',
 
 
 
 
 
31
  );
10
  */
11
 
12
  return array(
13
+ 'year' => ':count rok|:count lata|:count lat',
14
+ 'y' => ':countr|:countl',
15
+ 'month' => ':count miesiąc|:count miesiące|:count miesięcy',
16
+ 'm' => ':countmies',
17
+ 'week' => ':count tydzień|:count tygodnie|:count tygodni',
18
+ 'w' => ':counttyg',
19
+ 'day' => ':count dzień|:count dni|:count dni',
20
+ 'd' => ':countd',
21
+ 'hour' => ':count godzina|:count godziny|:count godzin',
22
+ 'h' => ':countg',
23
+ 'minute' => ':count minuta|:count minuty|:count minut',
24
+ 'min' => ':countm',
25
+ 'second' => ':count sekunda|:count sekundy|:count sekund',
26
+ 's' => ':counts',
27
  'ago' => ':time temu',
28
  'from_now' => ':time od teraz',
29
  'after' => ':time po',
30
  'before' => ':time przed',
31
+ 'diff_now' => 'przed chwilą',
32
+ 'diff_yesterday' => 'wczoraj',
33
+ 'diff_tomorrow' => 'jutro',
34
+ 'diff_before_yesterday' => 'przedwczoraj',
35
+ 'diff_after_tomorrow' => 'pojutrze',
36
  );
src/common/lib/vendor/nesbot/carbon/src/Carbon/Lang/ps.php CHANGED
@@ -10,20 +10,20 @@
10
  */
11
 
12
  return array(
13
- 'year' => '1 کال|:count کاله',
14
- 'y' => '1کال|:countکاله',
15
- 'month' => '1 مياشت|:count مياشتي',
16
- 'm' => '1مياشت|:countمياشتي',
17
- 'week' => '1 اونۍ|:count اونۍ',
18
- 'w' => '1اونۍ|:countاونۍ',
19
- 'day' => '1 ورځ|:count ورځي',
20
- 'd' => '1ورځ|:countورځي',
21
- 'hour' => '1 ساعت|:count ساعته',
22
- 'h' => '1ساعت|:countساعته',
23
- 'minute' => '1 دقيقه|:count دقيقې',
24
- 'min' => '1دقيقه|:countدقيقې',
25
- 'second' => '1 ثانيه|:count ثانيې',
26
- 's' => '1ثانيه|:countثانيې',
27
  'ago' => ':time دمخه',
28
  'from_now' => ':time له اوس څخه',
29
  'after' => ':time وروسته',
10
  */
11
 
12
  return array(
13
+ 'year' => ':count کال|:count کاله',
14
+ 'y' => ':countکال|:countکاله',
15
+ 'month' => ':count مياشت|:count مياشتي',
16
+ 'm' => ':countمياشت|:countمياشتي',
17
+ 'week' => ':count اونۍ|:count اونۍ',
18
+ 'w' => ':countاونۍ|:countاونۍ',
19
+ 'day' => ':count ورځ|:count ورځي',
20
+ 'd' => ':countورځ|:countورځي',
21
+ 'hour' => ':count ساعت|:count ساعته',
22
+ 'h' => ':countساعت|:countساعته',
23
+ 'minute' => ':count دقيقه|:count دقيقې',
24
+ 'min' => ':countدقيقه|:countدقيقې',
25
+ 'second' => ':count ثانيه|:count ثانيې',
26
+ 's' => ':countثانيه|:countثانيې',
27
  'ago' => ':time دمخه',
28
  'from_now' => ':time له اوس څخه',
29
  'after' => ':time وروسته',
src/common/lib/vendor/nesbot/carbon/src/Carbon/Lang/pt.php CHANGED
@@ -10,20 +10,20 @@
10
  */
11
 
12
  return array(
13
- 'year' => '1 ano|:count anos',
14
- 'y' => '1 ano|:count anos',
15
- 'month' => '1 mês|:count meses',
16
- 'm' => '1 mês|:count meses',
17
- 'week' => '1 semana|:count semanas',
18
- 'w' => '1 semana|:count semanas',
19
- 'day' => '1 dia|:count dias',
20
- 'd' => '1 dia|:count dias',
21
- 'hour' => '1 hora|:count horas',
22
- 'h' => '1 hora|:count horas',
23
- 'minute' => '1 minuto|:count minutos',
24
- 'min' => '1 minuto|:count minutos',
25
- 'second' => '1 segundo|:count segundos',
26
- 's' => '1 segundo|:count segundos',
27
  'ago' => ':time atrás',
28
  'from_now' => 'em :time',
29
  'after' => ':time depois',
10
  */
11
 
12
  return array(
13
+ 'year' => ':count ano|:count anos',
14
+ 'y' => ':count ano|:count anos',
15
+ 'month' => ':count mês|:count meses',
16
+ 'm' => ':count mês|:count meses',
17
+ 'week' => ':count semana|:count semanas',
18
+ 'w' => ':count semana|:count semanas',
19
+ 'day' => ':count dia|:count dias',
20
+ 'd' => ':count dia|:count dias',
21
+ 'hour' => ':count hora|:count horas',
22
+ 'h' => ':count hora|:count horas',
23
+ 'minute' => ':count minuto|:count minutos',
24
+ 'min' => ':count minuto|:count minutos',
25
+ 'second' => ':count segundo|:count segundos',
26
+ 's' => ':count segundo|:count segundos',
27
  'ago' => ':time atrás',
28
  'from_now' => 'em :time',
29
  'after' => ':time depois',
src/common/lib/vendor/nesbot/carbon/src/Carbon/Lang/pt_BR.php CHANGED
@@ -10,20 +10,20 @@
10
  */
11
 
12
  return array(
13
- 'year' => '1 ano|:count anos',
14
- 'y' => '1 ano|:count anos',
15
- 'month' => '1 mês|:count meses',
16
- 'm' => '1 mês|:count meses',
17
- 'week' => '1 semana|:count semanas',
18
- 'w' => '1 semana|:count semanas',
19
- 'day' => '1 dia|:count dias',
20
- 'd' => '1 dia|:count dias',
21
- 'hour' => '1 hora|:count horas',
22
- 'h' => '1 hora|:count horas',
23
- 'minute' => '1 minuto|:count minutos',
24
- 'min' => '1 minuto|:count minutos',
25
- 'second' => '1 segundo|:count segundos',
26
- 's' => '1 segundo|:count segundos',
27
  'ago' => 'há :time',
28
  'from_now' => 'em :time',
29
  'after' => 'após :time',
10
  */
11
 
12
  return array(
13
+ 'year' => ':count ano|:count anos',
14
+ 'y' => ':count ano|:count anos',
15
+ 'month' => ':count mês|:count meses',
16
+ 'm' => ':count mês|:count meses',
17
+ 'week' => ':count semana|:count semanas',
18
+ 'w' => ':count semana|:count semanas',
19
+ 'day' => ':count dia|:count dias',
20
+ 'd' => ':count dia|:count dias',
21
+ 'hour' => ':count hora|:count horas',
22
+ 'h' => ':count hora|:count horas',
23
+ 'minute' => ':count minuto|:count minutos',
24
+ 'min' => ':count minuto|:count minutos',
25
+ 'second' => ':count segundo|:count segundos',
26
+ 's' => ':count segundo|:count segundos',
27
  'ago' => 'há :time',
28
  'from_now' => 'em :time',
29
  'after' => 'após :time',
src/common/lib/vendor/nesbot/carbon/src/Carbon/Lang/sh.php ADDED
@@ -0,0 +1,31 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Carbon package.
5
+ *
6
+ * (c) Brian Nesbitt <brian@nesbot.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ return array(
13
+ 'year' => ':count godina|:count godine|:count godina',
14
+ 'y' => ':count godina|:count godine|:count godina',
15
+ 'month' => ':count mesec|:count meseca|:count meseci',
16
+ 'm' => ':count mesec|:count meseca|:count meseci',
17
+ 'week' => ':count nedelja|:count nedelje|:count nedelja',
18
+ 'w' => ':count nedelja|:count nedelje|:count nedelja',
19
+ 'day' => ':count dan|:count dana|:count dana',
20
+ 'd' => ':count dan|:count dana|:count dana',
21
+ 'hour' => ':count čas|:count časa|:count časova',
22
+ 'h' => ':count čas|:count časa|:count časova',
23
+ 'minute' => ':count minut|:count minuta|:count minuta',
24
+ 'min' => ':count minut|:count minuta|:count minuta',
25
+ 'second' => ':count sekund|:count sekunda|:count sekundi',
26
+ 's' => ':count sekund|:count sekunda|:count sekundi',
27
+ 'ago' => 'pre :time',
28
+ 'from_now' => 'za :time',
29
+ 'after' => 'nakon :time',
30
+ 'before' => ':time raniјe',
31
+ );
src/common/lib/vendor/nesbot/carbon/src/Carbon/Lang/sl.php CHANGED
@@ -35,4 +35,9 @@ return array(
35
  'from_now' => 'čez :time',
36
  'after' => 'čez :time',
37
  'before' => 'pred :time',
 
 
 
 
 
38
  );
35
  'from_now' => 'čez :time',
36
  'after' => 'čez :time',
37
  'before' => 'pred :time',
38
+ 'diff_now' => 'ravnokar',
39
+ 'diff_yesterday' => 'včeraj',
40
+ 'diff_tomorrow' => 'jutri',
41
+ 'diff_before_yesterday' => 'predvčerajšnjim',
42
+ 'diff_after_tomorrow' => 'pojutrišnjem',
43
  );
src/common/lib/vendor/nesbot/carbon/src/Carbon/Lang/sq.php CHANGED
@@ -10,20 +10,20 @@
10
  */
11
 
12
  return array(
13
- 'year' => '1 vit|:count vjet',
14
- 'y' => '1 vit|:count vjet',
15
- 'month' => '1 muaj|:count muaj',
16
- 'm' => '1 muaj|:count muaj',
17
- 'week' => '1 javë|:count javë',
18
- 'w' => '1 javë|:count javë',
19
- 'day' => '1 ditë|:count ditë',
20
- 'd' => '1 ditë|:count ditë',
21
- 'hour' => '1 orë|:count orë',
22
- 'h' => '1 orë|:count orë',
23
- 'minute' => '1 minutë|:count minuta',
24
- 'min' => '1 minutë|:count minuta',
25
- 'second' => '1 sekondë|:count sekonda',
26
- 's' => '1 sekondë|:count sekonda',
27
  'ago' => ':time më parë',
28
  'from_now' => ':time nga tani',
29
  'after' => ':time pas',
10
  */
11
 
12
  return array(
13
+ 'year' => ':count vit|:count vjet',
14
+ 'y' => ':count vit|:count vjet',
15
+ 'month' => ':count muaj|:count muaj',
16
+ 'm' => ':count muaj|:count muaj',
17
+ 'week' => ':count javë|:count javë',
18
+ 'w' => ':count javë|:count javë',
19
+ 'day' => ':count ditë|:count ditë',
20
+ 'd' => ':count ditë|:count ditë',
21
+ 'hour' => ':count orë|:count orë',
22
+ 'h' => ':count orë|:count orë',
23
+ 'minute' => ':count minutë|:count minuta',
24
+ 'min' => ':count minutë|:count minuta',
25
+ 'second' => ':count sekondë|:count sekonda',
26
+ 's' => ':count sekondë|:count sekonda',
27
  'ago' => ':time më parë',
28
  'from_now' => ':time nga tani',
29
  'after' => ':time pas',
src/common/lib/vendor/nesbot/carbon/src/Carbon/Lang/sr_Cyrl.php CHANGED
@@ -35,4 +35,9 @@ return array(
35
  'week_from_now' => '{1} :count недељу|{2,3,4} :count недеље|[5,Inf[ :count недеља',
36
  'week_ago' => '{1} :count недељу|{2,3,4} :count недеље|[5,Inf[ :count недеља',
37
 
 
 
 
 
 
38
  );
35
  'week_from_now' => '{1} :count недељу|{2,3,4} :count недеље|[5,Inf[ :count недеља',
36
  'week_ago' => '{1} :count недељу|{2,3,4} :count недеље|[5,Inf[ :count недеља',
37
 
38
+ 'diff_now' => 'управо сада',
39
+ 'diff_yesterday' => 'јуче',
40
+ 'diff_tomorrow' => 'сутра',
41
+ 'diff_before_yesterday' => 'прекјуче',
42
+ 'diff_after_tomorrow' => 'прекосутра',
43
  );
src/common/lib/vendor/nesbot/carbon/src/Carbon/Lang/sr_Cyrl_ME.php CHANGED
@@ -35,4 +35,9 @@ return array(
35
  'week_from_now' => '{1} :count недјељу|{2,3,4} :count недјеље|[5,Inf[ :count недјеља',
36
  'week_ago' => '{1} :count недјељу|{2,3,4} :count недјеље|[5,Inf[ :count недјеља',
37
 
 
 
 
 
 
38
  );
35
  'week_from_now' => '{1} :count недјељу|{2,3,4} :count недјеље|[5,Inf[ :count недјеља',
36
  'week_ago' => '{1} :count недјељу|{2,3,4} :count недјеље|[5,Inf[ :count недјеља',
37
 
38
+ 'diff_now' => 'управо сада',
39
+ 'diff_yesterday' => 'јуче',
40
+ 'diff_tomorrow' => 'сутра',
41
+ 'diff_before_yesterday' => 'прекјуче',
42
+ 'diff_after_tomorrow' => 'прекосјутра',
43
  );
src/common/lib/vendor/nesbot/carbon/src/Carbon/Lang/sr_Latn_ME.php CHANGED
@@ -35,4 +35,9 @@ return array(
35
  'week_from_now' => '{1} :count nedjelju|{2,3,4} :count nedjelje|[5,Inf[ :count nedjelja',
36
  'week_ago' => '{1} :count nedjelju|{2,3,4} :count nedjelje|[5,Inf[ :count nedjelja',
37
 
 
 
 
 
 
38
  );
35
  'week_from_now' => '{1} :count nedjelju|{2,3,4} :count nedjelje|[5,Inf[ :count nedjelja',
36
  'week_ago' => '{1} :count nedjelju|{2,3,4} :count nedjelje|[5,Inf[ :count nedjelja',
37
 
38
+ 'diff_now' => 'upravo sada',
39
+ 'diff_yesterday' => 'juče',
40
+ 'diff_tomorrow' => 'sutra',
41
+ 'diff_before_yesterday' => 'prekjuče',
42
+ 'diff_after_tomorrow' => 'preksutra',
43
  );
src/common/lib/vendor/nesbot/carbon/src/Carbon/Lang/sv.php CHANGED
@@ -10,20 +10,20 @@
10
  */
11
 
12
  return array(
13
- 'year' => '1 år|:count år',
14
- 'y' => '1 år|:count år',
15
- 'month' => '1 månad|:count månader',
16
- 'm' => '1 månad|:count månader',
17
- 'week' => '1 vecka|:count veckor',
18
- 'w' => '1 vecka|:count veckor',
19
- 'day' => '1 dag|:count dagar',
20
- 'd' => '1 dag|:count dagar',
21
- 'hour' => '1 timme|:count timmar',
22
- 'h' => '1 timme|:count timmar',
23
- 'minute' => '1 minut|:count minuter',
24
- 'min' => '1 minut|:count minuter',
25
- 'second' => '1 sekund|:count sekunder',
26
- 's' => '1 sekund|:count sekunder',
27
  'ago' => ':time sedan',
28
  'from_now' => 'om :time',
29
  'after' => ':time efter',
10
  */
11
 
12
  return array(
13
+ 'year' => ':count år|:count år',
14
+ 'y' => ':count år|:count år',
15
+ 'month' => ':count månad|:count månader',
16
+ 'm' => ':count månad|:count månader',
17
+ 'week' => ':count vecka|:count veckor',
18
+ 'w' => ':count vecka|:count veckor',
19
+ 'day' => ':count dag|:count dagar',
20
+ 'd' => ':count dag|:count dagar',
21
+ 'hour' => ':count timme|:count timmar',
22
+ 'h' => ':count timme|:count timmar',
23
+ 'minute' => ':count minut|:count minuter',
24
+ 'min' => ':count minut|:count minuter',
25
+ 'second' => ':count sekund|:count sekunder',
26
+ 's' => ':count sekund|:count sekunder',
27
  'ago' => ':time sedan',
28
  'from_now' => 'om :time',
29
  'after' => ':time efter',
src/common/lib/vendor/nesbot/carbon/src/Carbon/Lang/sw.php ADDED
@@ -0,0 +1,31 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Carbon package.
5
+ *
6
+ * (c) Brian Nesbitt <brian@nesbot.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ return array(
13
+ 'year' => 'mwaka 1|miaka :count',
14
+ 'y' => 'mwaka 1|miaka :count',
15
+ 'month' => 'mwezi 1|miezi :count',
16
+ 'm' => 'mwezi 1|miezi :count',
17
+ 'week' => 'wiki 1|wiki :count',
18
+ 'w' => 'wiki 1|wiki :count',
19
+ 'day' => 'siku 1|siku :count',
20
+ 'd' => 'siku 1|siku :count',
21
+ 'hour' => 'saa 1|masaa :count',
22
+ 'h' => 'saa 1|masaa :count',
23
+ 'minute' => 'dakika 1|dakika :count',
24
+ 'min' => 'dakika 1|dakika :count',
25
+ 'second' => 'sekunde 1|sekunde :count',
26
+ 's' => 'sekunde 1|sekunde :count',
27
+ 'ago' => ':time ziliyopita',
28
+ 'from_now' => ':time kwanzia sasa',
29
+ 'after' => ':time baada',
30
+ 'before' => ':time kabla',
31
+ );
src/common/lib/vendor/nesbot/carbon/src/JsonSerializable.php ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ interface JsonSerializable
4
+ {
5
+ /**
6
+ * Specify data which should be serialized to JSON.
7
+ *
8
+ * @link http://php.net/manual/en/jsonserializable.jsonserialize.php
9
+ *
10
+ * @return mixed data which can be serialized by <b>json_encode</b>,
11
+ * which is a value of any type other than a resource.
12
+ *
13
+ * @since 5.4.0
14
+ */
15
+ public function jsonSerialize();
16
+ }
src/common/lib/vendor/symfony/polyfill-mbstring/Mbstring.php CHANGED
@@ -78,7 +78,7 @@ final class Mbstring
78
 
79
  public static function mb_convert_encoding($s, $toEncoding, $fromEncoding = null)
80
  {
81
- if (is_array($fromEncoding) || false !== strpos($fromEncoding, ',')) {
82
  $fromEncoding = self::mb_detect_encoding($s, $fromEncoding);
83
  } else {
84
  $fromEncoding = self::getEncoding($fromEncoding);
@@ -140,16 +140,16 @@ final class Mbstring
140
 
141
  public static function mb_decode_numericentity($s, $convmap, $encoding = null)
142
  {
143
- if (null !== $s && !is_scalar($s) && !(is_object($s) && method_exists($s, '__toString'))) {
144
  trigger_error('mb_decode_numericentity() expects parameter 1 to be string, '.gettype($s).' given', E_USER_WARNING);
145
  return null;
146
  }
147
 
148
- if (!is_array($convmap) || !$convmap) {
149
  return false;
150
  }
151
 
152
- if (null !== $encoding && !is_scalar($encoding)) {
153
  trigger_error('mb_decode_numericentity() expects parameter 3 to be string, '.gettype($s).' given', E_USER_WARNING);
154
  return ''; // Instead of null (cf. mb_encode_numericentity).
155
  }
@@ -170,7 +170,7 @@ final class Mbstring
170
  $s = iconv($encoding, 'UTF-8//IGNORE', $s);
171
  }
172
 
173
- $cnt = floor(count($convmap) / 4) * 4;
174
 
175
  for ($i = 0; $i < $cnt; $i += 4) {
176
  // collector_decode_htmlnumericentity ignores $convmap[$i + 3]
@@ -197,21 +197,21 @@ final class Mbstring
197
 
198
  public static function mb_encode_numericentity($s, $convmap, $encoding = null, $is_hex = false)
199
  {
200
- if (null !== $s && !is_scalar($s) && !(is_object($s) && method_exists($s, '__toString'))) {
201
  trigger_error('mb_encode_numericentity() expects parameter 1 to be string, '.gettype($s).' given', E_USER_WARNING);
202
  return null;
203
  }
204
 
205
- if (!is_array($convmap) || !$convmap) {
206
  return false;
207
  }
208
 
209
- if (null !== $encoding && !is_scalar($encoding)) {
210
  trigger_error('mb_encode_numericentity() expects parameter 3 to be string, '.gettype($s).' given', E_USER_WARNING);
211
  return null; // Instead of '' (cf. mb_decode_numericentity).
212
  }
213
 
214
- if (null !== $is_hex && !is_scalar($is_hex)) {
215
  trigger_error('mb_encode_numericentity() expects parameter 4 to be boolean, '.gettype($s).' given', E_USER_WARNING);
216
  return null;
217
  }
@@ -234,9 +234,9 @@ final class Mbstring
234
 
235
  static $ulenMask = array("\xC0" => 2, "\xD0" => 2, "\xE0" => 3, "\xF0" => 4);
236
 
237
- $cnt = floor(count($convmap) / 4) * 4;
238
  $i = 0;
239
- $len = strlen($s);
240
  $result = '';
241
 
242
  while ($i < $len) {
@@ -305,7 +305,7 @@ final class Mbstring
305
  static $ulenMask = array("\xC0" => 2, "\xD0" => 2, "\xE0" => 3, "\xF0" => 4);
306
 
307
  $i = 0;
308
- $len = strlen($s);
309
 
310
  while ($i < $len) {
311
  $ulen = $s[$i] < "\x80" ? 1 : $ulenMask[$s[$i] & "\xF0"];
@@ -314,7 +314,7 @@ final class Mbstring
314
 
315
  if (isset($map[$uchr])) {
316
  $uchr = $map[$uchr];
317
- $nlen = strlen($uchr);
318
 
319
  if ($nlen == $ulen) {
320
  $nlen = $i;
@@ -404,7 +404,7 @@ final class Mbstring
404
  if (null === $encodingList) {
405
  $encodingList = self::$encodingList;
406
  } else {
407
- if (!is_array($encodingList)) {
408
  $encodingList = array_map('trim', explode(',', $encodingList));
409
  }
410
  $encodingList = array_map('strtoupper', $encodingList);
@@ -441,7 +441,7 @@ final class Mbstring
441
  return self::$encodingList;
442
  }
443
 
444
- if (!is_array($encodingList)) {
445
  $encodingList = array_map('trim', explode(',', $encodingList));
446
  }
447
  $encodingList = array_map('strtoupper', $encodingList);
@@ -467,7 +467,7 @@ final class Mbstring
467
  {
468
  $encoding = self::getEncoding($encoding);
469
  if ('CP850' === $encoding || 'ASCII' === $encoding) {
470
- return strlen($s);
471
  }
472
 
473
  return @iconv_strlen($s, $encoding);
@@ -679,13 +679,13 @@ final class Mbstring
679
  public static function mb_chr($code, $encoding = null)
680
  {
681
  if (0x80 > $code %= 0x200000) {
682
- $s = chr($code);
683
  } elseif (0x800 > $code) {
684
- $s = chr(0xC0 | $code >> 6).chr(0x80 | $code & 0x3F);
685
  } elseif (0x10000 > $code) {
686
- $s = chr(0xE0 | $code >> 12).chr(0x80 | $code >> 6 & 0x3F).chr(0x80 | $code & 0x3F);
687
  } else {
688
- $s = chr(0xF0 | $code >> 18).chr(0x80 | $code >> 12 & 0x3F).chr(0x80 | $code >> 6 & 0x3F).chr(0x80 | $code & 0x3F);
689
  }
690
 
691
  if ('UTF-8' !== $encoding = self::getEncoding($encoding)) {
@@ -735,7 +735,7 @@ final class Mbstring
735
 
736
  while (isset($m[$i])) {
737
  if (0x80 > $m[$i]) {
738
- $entities .= chr($m[$i++]);
739
  continue;
740
  }
741
  if (0xF0 <= $m[$i]) {
78
 
79
  public static function mb_convert_encoding($s, $toEncoding, $fromEncoding = null)
80
  {
81
+ if (\is_array($fromEncoding) || false !== strpos($fromEncoding, ',')) {
82
  $fromEncoding = self::mb_detect_encoding($s, $fromEncoding);
83
  } else {
84
  $fromEncoding = self::getEncoding($fromEncoding);
140
 
141
  public static function mb_decode_numericentity($s, $convmap, $encoding = null)
142
  {
143
+ if (null !== $s && !\is_scalar($s) && !(\is_object($s) && \method_exists($s, '__toString'))) {
144
  trigger_error('mb_decode_numericentity() expects parameter 1 to be string, '.gettype($s).' given', E_USER_WARNING);
145
  return null;
146
  }
147
 
148
+ if (!\is_array($convmap) || !$convmap) {
149
  return false;
150
  }
151
 
152
+ if (null !== $encoding && !\is_scalar($encoding)) {
153
  trigger_error('mb_decode_numericentity() expects parameter 3 to be string, '.gettype($s).' given', E_USER_WARNING);
154
  return ''; // Instead of null (cf. mb_encode_numericentity).
155
  }
170
  $s = iconv($encoding, 'UTF-8//IGNORE', $s);
171
  }
172
 
173
+ $cnt = floor(\count($convmap) / 4) * 4;
174
 
175
  for ($i = 0; $i < $cnt; $i += 4) {
176
  // collector_decode_htmlnumericentity ignores $convmap[$i + 3]
197
 
198
  public static function mb_encode_numericentity($s, $convmap, $encoding = null, $is_hex = false)
199
  {
200
+ if (null !== $s && !\is_scalar($s) && !(\is_object($s) && \method_exists($s, '__toString'))) {
201
  trigger_error('mb_encode_numericentity() expects parameter 1 to be string, '.gettype($s).' given', E_USER_WARNING);
202
  return null;
203
  }
204
 
205
+ if (!\is_array($convmap) || !$convmap) {
206
  return false;
207
  }
208
 
209
+ if (null !== $encoding && !\is_scalar($encoding)) {
210
  trigger_error('mb_encode_numericentity() expects parameter 3 to be string, '.gettype($s).' given', E_USER_WARNING);
211
  return null; // Instead of '' (cf. mb_decode_numericentity).
212
  }
213
 
214
+ if (null !== $is_hex && !\is_scalar($is_hex)) {
215
  trigger_error('mb_encode_numericentity() expects parameter 4 to be boolean, '.gettype($s).' given', E_USER_WARNING);
216
  return null;
217
  }
234
 
235
  static $ulenMask = array("\xC0" => 2, "\xD0" => 2, "\xE0" => 3, "\xF0" => 4);
236
 
237
+ $cnt = floor(\count($convmap) / 4) * 4;
238
  $i = 0;
239
+ $len = \strlen($s);
240
  $result = '';
241
 
242
  while ($i < $len) {
305
  static $ulenMask = array("\xC0" => 2, "\xD0" => 2, "\xE0" => 3, "\xF0" => 4);
306
 
307
  $i = 0;
308
+ $len = \strlen($s);
309
 
310
  while ($i < $len) {
311
  $ulen = $s[$i] < "\x80" ? 1 : $ulenMask[$s[$i] & "\xF0"];
314
 
315
  if (isset($map[$uchr])) {
316
  $uchr = $map[$uchr];
317
+ $nlen = \strlen($uchr);
318
 
319
  if ($nlen == $ulen) {
320
  $nlen = $i;
404
  if (null === $encodingList) {
405
  $encodingList = self::$encodingList;
406
  } else {
407
+ if (!\is_array($encodingList)) {
408
  $encodingList = array_map('trim', explode(',', $encodingList));
409
  }
410
  $encodingList = array_map('strtoupper', $encodingList);
441
  return self::$encodingList;
442
  }
443
 
444
+ if (!\is_array($encodingList)) {
445
  $encodingList = array_map('trim', explode(',', $encodingList));
446
  }
447
  $encodingList = array_map('strtoupper', $encodingList);
467
  {
468
  $encoding = self::getEncoding($encoding);
469
  if ('CP850' === $encoding || 'ASCII' === $encoding) {
470
+ return \strlen($s);
471
  }
472
 
473
  return @iconv_strlen($s, $encoding);
679
  public static function mb_chr($code, $encoding = null)
680
  {
681
  if (0x80 > $code %= 0x200000) {
682
+ $s = \chr($code);
683
  } elseif (0x800 > $code) {
684
+ $s = \chr(0xC0 | $code >> 6).\chr(0x80 | $code & 0x3F);
685
  } elseif (0x10000 > $code) {
686
+ $s = \chr(0xE0 | $code >> 12).\chr(0x80 | $code >> 6 & 0x3F).\chr(0x80 | $code & 0x3F);
687
  } else {
688
+ $s = \chr(0xF0 | $code >> 18).\chr(0x80 | $code >> 12 & 0x3F).\chr(0x80 | $code >> 6 & 0x3F).\chr(0x80 | $code & 0x3F);
689
  }
690
 
691
  if ('UTF-8' !== $encoding = self::getEncoding($encoding)) {
735
 
736
  while (isset($m[$i])) {
737
  if (0x80 > $m[$i]) {
738
+ $entities .= \chr($m[$i++]);
739
  continue;
740
  }
741
  if (0xF0 <= $m[$i]) {
src/common/lib/vendor/symfony/translation/PluralizationRules.php CHANGED
@@ -107,6 +107,7 @@ class PluralizationRules
107
  case 'nl':
108
  case 'nn':
109
  case 'no':
 
110
  case 'om':
111
  case 'or':
112
  case 'pa':
107
  case 'nl':
108
  case 'nn':
109
  case 'no':
110
+ case 'oc':
111
  case 'om':
112
  case 'or':
113
  case 'pa':
src/common/lib/vendor/symfony/translation/composer.json CHANGED
@@ -31,7 +31,7 @@
31
  "suggest": {
32
  "symfony/config": "",
33
  "symfony/yaml": "",
34
- "psr/log": "To use logging capability in translator"
35
  },
36
  "autoload": {
37
  "psr-4": { "Symfony\\Component\\Translation\\": "" },
31
  "suggest": {
32
  "symfony/config": "",
33
  "symfony/yaml": "",
34
+ "psr/log-implementation": "To use logging capability in translator"
35
  },
36
  "autoload": {
37
  "psr-4": { "Symfony\\Component\\Translation\\": "" },
src/common/wp-admin-notices.php CHANGED
@@ -233,7 +233,7 @@ class ICWP_WPSF_WpAdminNotices extends ICWP_WPSF_Foundation {
233
  */
234
  public function flushFlashMessage() {
235
 
236
- $oDp = $this->loadDataProcessor();
237
  $sCookieName = $this->getActionPrefix().'flash';
238
  $this->sFlashMessage = $oDp->FetchCookie( $sCookieName, '' );
239
  if ( !empty( $this->sFlashMessage ) ) {
233
  */
234
  public function flushFlashMessage() {
235
 
236
+ $oDp = $this->loadDP();
237
  $sCookieName = $this->getActionPrefix().'flash';
238
  $this->sFlashMessage = $oDp->FetchCookie( $sCookieName, '' );
239
  if ( !empty( $this->sFlashMessage ) ) {
src/common/wp-comments.php CHANGED
@@ -54,7 +54,7 @@ class ICWP_WPSF_WpComments extends ICWP_WPSF_Foundation {
54
  */
55
  public function isCommentAuthorPreviouslyApproved( $sAuthorEmail ) {
56
 
57
- if ( !$this->loadDataProcessor()->validEmail( $sAuthorEmail ) ) {
58
  return false;
59
  }
60
 
54
  */
55
  public function isCommentAuthorPreviouslyApproved( $sAuthorEmail ) {
56
 
57
+ if ( !$this->loadDP()->validEmail( $sAuthorEmail ) ) {
58
  return false;
59
  }
60
 
src/config/changelog.json CHANGED
@@ -356,7 +356,7 @@
356
  "timestamp": "1491523200",
357
  "name": "",
358
  "summary": "Introduction of Login Authentication Portal",
359
- "link": "http://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": "http://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": "http://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": "http://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://icwp.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://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
  {
748
  "type": "improvements",
749
  "summary": "Further preparation for Shield Central release",
750
+ "link": "https://icwp.io/83",
751
  "version": "5.8.0"
752
  }
753
  ]
src/config/feature-admin_access_restriction.php CHANGED
@@ -75,8 +75,8 @@
75
  "section": "section_enable_plugin_feature_admin_access_restriction",
76
  "default": "Y",
77
  "type": "checkbox",
78
- "link_info": "http://icwp.io/40",
79
- "link_blog": "http://icwp.io/wpsf02",
80
  "name": "Enable Security Admin",
81
  "summary": "Enforce Security Admin Access Restriction",
82
  "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."
@@ -87,7 +87,7 @@
87
  "sensitive": true,
88
  "default": "",
89
  "type": "password",
90
- "link_info": "http://icwp.io/42",
91
  "link_blog": "",
92
  "name": "Security Admin Access Key",
93
  "summary": "Provide/Update Security Admin Access Key",
@@ -99,7 +99,8 @@
99
  "section": "section_admin_access_restriction_settings",
100
  "default": 30,
101
  "type": "integer",
102
- "link_info": "http://icwp.io/41",
 
103
  "link_blog": "",
104
  "name": "Security Admin Timeout",
105
  "summary": "Specify An Automatic Timeout Interval For Security Admin Access",
@@ -110,8 +111,8 @@
110
  "section": "section_admin_access_restriction_areas",
111
  "default": "Y",
112
  "type": "checkbox",
113
- "link_info": "http://icwp.io/a0",
114
- "link_blog": "http://icwp.io/wpsf32",
115
  "name": "Pages",
116
  "summary": "Restrict Access To Key WordPress Posts And Pages Actions",
117
  "description": "Careful: This will restrict access to page/post creation, editing and deletion. Note: Selecting 'Edit' will also restrict all other options."
@@ -121,7 +122,7 @@
121
  "section": "section_admin_access_restriction_areas",
122
  "default": "N",
123
  "type": "checkbox",
124
- "link_info": "http://icwp.io/a0",
125
  "link_blog": "",
126
  "name": "Admin Users",
127
  "summary": "Restrict Access To Create/Delete/Modify Other Admin Users",
@@ -150,8 +151,8 @@
150
  "text": "Delete"
151
  }
152
  ],
153
- "link_info": "http://icwp.io/a0",
154
- "link_blog": "http://icwp.io/wpsf21",
155
  "summary": "Restrict Access To Key WordPress Plugin Actions",
156
  "description": "Careful: This will restrict access to plugin installation, update, activation and deletion. Note: Selecting 'Activate' will also restrict all other options."
157
 
@@ -183,8 +184,8 @@
183
  "text": "Delete"
184
  }
185
  ],
186
- "link_info": "http://icwp.io/a0",
187
- "link_blog": "http://icwp.io/wpsf21",
188
  "summary": "Restrict Access To WordPress Theme Actions",
189
  "description": "Careful: This will restrict access to theme installation, update, activation and deletion."
190
  },
@@ -207,14 +208,14 @@
207
  "text": "Delete"
208
  }
209
  ],
210
- "link_info": "http://icwp.io/a0",
211
- "link_blog": "http://icwp.io/wpsf21",
212
  "summary": "Restrict Access To Key WordPress Posts And Pages Actions",
213
  "description": "Careful: This will restrict access to page/post creation, editing and deletion."
214
  },
215
  {
216
  "key": "whitelabel_enable",
217
- "section": "section_non_ui",
218
  "premium": true,
219
  "default": "N",
220
  "type": "checkbox",
@@ -225,9 +226,8 @@
225
  "description": "Use this option to turn on/off the whole White Label feature."
226
  },
227
  {
228
- "key": "_wl_hide_updates",
229
- "section": "section_non_ui",
230
- "premium": true,
231
  "default": "Y",
232
  "type": "checkbox",
233
  "link_info": "",
@@ -237,10 +237,10 @@
237
  "description": "Hides the availability of Shield updates from non-security admins."
238
  },
239
  {
240
- "key": "_wl_namemain",
 
241
  "sensitive": true,
242
- "section": "section_non_ui",
243
- "default": "Shield Security",
244
  "type": "text",
245
  "link_info": "",
246
  "link_blog": "",
@@ -249,10 +249,10 @@
249
  "description": "The Name Of The Plugin."
250
  },
251
  {
252
- "key": "_wl_namemenu",
 
253
  "sensitive": true,
254
- "section": "section_non_ui",
255
- "default": "",
256
  "type": "text",
257
  "link_info": "",
258
  "link_blog": "",
@@ -261,9 +261,21 @@
261
  "description": "The Main Menu Title Of The Plugin. If left empty, the Plugin Name will be used."
262
  },
263
  {
264
- "key": "_wl_description",
 
 
 
 
 
 
 
 
 
 
 
 
 
265
  "sensitive": true,
266
- "section": "section_non_ui",
267
  "default": "Secure Your Sites With The World's Most Powerful WordPress Security Plugin",
268
  "type": "text",
269
  "link_info": "",
@@ -273,10 +285,10 @@
273
  "description": "The Tag Line Of The Plugin."
274
  },
275
  {
276
- "key": "_wl_homeurl",
 
277
  "sensitive": true,
278
- "section": "section_non_ui",
279
- "default": "http://icwp.io/home",
280
  "type": "text",
281
  "link_info": "",
282
  "link_blog": "",
@@ -285,16 +297,28 @@
285
  "description": "When a user clicks the home link for this plugin, this is where they'll be directed."
286
  },
287
  {
288
- "key": "_wl_iconurl",
 
 
 
 
 
 
 
 
 
 
 
 
 
289
  "sensitive": true,
290
- "section": "section_non_ui",
291
  "default": "",
292
  "type": "text",
293
  "link_info": "",
294
  "link_blog": "",
295
- "name": "Icon URL",
296
- "summary": "Plugin Icon URL",
297
- "description": "The URL of the icon displayed in the menu and in the admin pages."
298
  }
299
  ],
300
  "definitions": {
75
  "section": "section_enable_plugin_feature_admin_access_restriction",
76
  "default": "Y",
77
  "type": "checkbox",
78
+ "link_info": "https://icwp.io/40",
79
+ "link_blog": "https://icwp.io/wpsf02",
80
  "name": "Enable Security Admin",
81
  "summary": "Enforce Security Admin Access Restriction",
82
  "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."
87
  "sensitive": true,
88
  "default": "",
89
  "type": "password",
90
+ "link_info": "https://icwp.io/42",
91
  "link_blog": "",
92
  "name": "Security Admin Access Key",
93
  "summary": "Provide/Update Security Admin Access Key",
99
  "section": "section_admin_access_restriction_settings",
100
  "default": 30,
101
  "type": "integer",
102
+ "min": 1,
103
+ "link_info": "https://icwp.io/41",
104
  "link_blog": "",
105
  "name": "Security Admin Timeout",
106
  "summary": "Specify An Automatic Timeout Interval For Security Admin Access",
111
  "section": "section_admin_access_restriction_areas",
112
  "default": "Y",
113
  "type": "checkbox",
114
+ "link_info": "https://icwp.io/a0",
115
+ "link_blog": "https://icwp.io/wpsf32",
116
  "name": "Pages",
117
  "summary": "Restrict Access To Key WordPress Posts And Pages Actions",
118
  "description": "Careful: This will restrict access to page/post creation, editing and deletion. Note: Selecting 'Edit' will also restrict all other options."
122
  "section": "section_admin_access_restriction_areas",
123
  "default": "N",
124
  "type": "checkbox",
125
+ "link_info": "https://icwp.io/a0",
126
  "link_blog": "",
127
  "name": "Admin Users",
128
  "summary": "Restrict Access To Create/Delete/Modify Other Admin Users",
151
  "text": "Delete"
152
  }
153
  ],
154
+ "link_info": "https://icwp.io/a0",
155
+ "link_blog": "https://icwp.io/wpsf21",
156
  "summary": "Restrict Access To Key WordPress Plugin Actions",
157
  "description": "Careful: This will restrict access to plugin installation, update, activation and deletion. Note: Selecting 'Activate' will also restrict all other options."
158
 
184
  "text": "Delete"
185
  }
186
  ],
187
+ "link_info": "https://icwp.io/a0",
188
+ "link_blog": "https://icwp.io/wpsf21",
189
  "summary": "Restrict Access To WordPress Theme Actions",
190
  "description": "Careful: This will restrict access to theme installation, update, activation and deletion."
191
  },
208
  "text": "Delete"
209
  }
210
  ],
211
+ "link_info": "https://icwp.io/a0",
212
+ "link_blog": "https://icwp.io/wpsf21",
213
  "summary": "Restrict Access To Key WordPress Posts And Pages Actions",
214
  "description": "Careful: This will restrict access to page/post creation, editing and deletion."
215
  },
216
  {
217
  "key": "whitelabel_enable",
218
+ "section": "section_whitelabel",
219
  "premium": true,
220
  "default": "N",
221
  "type": "checkbox",
226
  "description": "Use this option to turn on/off the whole White Label feature."
227
  },
228
  {
229
+ "key": "wl_hide_updates",
230
+ "section": "section_whitelabel",
 
231
  "default": "Y",
232
  "type": "checkbox",
233
  "link_info": "",
237
  "description": "Hides the availability of Shield updates from non-security admins."
238
  },
239
  {
240
+ "key": "wl_pluginnamemain",
241
+ "section": "section_whitelabel",
242
  "sensitive": true,
243
+ "default": "Shield",
 
244
  "type": "text",
245
  "link_info": "",
246
  "link_blog": "",
249
  "description": "The Name Of The Plugin."
250
  },
251
  {
252
+ "key": "wl_namemenu",
253
+ "section": "section_whitelabel",
254
  "sensitive": true,
255
+ "default": "Shield Security",
 
256
  "type": "text",
257
  "link_info": "",
258
  "link_blog": "",
261
  "description": "The Main Menu Title Of The Plugin. If left empty, the Plugin Name will be used."
262
  },
263
  {
264
+ "key": "wl_companyname",
265
+ "section": "section_whitelabel",
266
+ "sensitive": true,
267
+ "default": "One Dollar Plugin",
268
+ "type": "text",
269
+ "link_info": "",
270
+ "link_blog": "",
271
+ "name": "Company Name",
272
+ "summary": "The Name Of Your Company",
273
+ "description": "Provide the name of your company."
274
+ },
275
+ {
276
+ "key": "wl_description",
277
+ "section": "section_whitelabel",
278
  "sensitive": true,
 
279
  "default": "Secure Your Sites With The World's Most Powerful WordPress Security Plugin",
280
  "type": "text",
281
  "link_info": "",
285
  "description": "The Tag Line Of The Plugin."
286
  },
287
  {
288
+ "key": "wl_homeurl",
289
+ "section": "section_whitelabel",
290
  "sensitive": true,
291
+ "default": "https://icwp.io/7f",
 
292
  "type": "text",
293
  "link_info": "",
294
  "link_blog": "",
297
  "description": "When a user clicks the home link for this plugin, this is where they'll be directed."
298
  },
299
  {
300
+ "key": "wl_menuiconurl",
301
+ "section": "section_whitelabel",
302
+ "sensitive": true,
303
+ "default": "",
304
+ "type": "text",
305
+ "link_info": "",
306
+ "link_blog": "",
307
+ "name": "Menu Icon",
308
+ "summary": "Menu Icon URL",
309
+ "description": "The URL of the icon displayed in the menu."
310
+ },
311
+ {
312
+ "key": "wl_dashboardlogourl",
313
+ "section": "section_whitelabel",
314
  "sensitive": true,
 
315
  "default": "",
316
  "type": "text",
317
  "link_info": "",
318
  "link_blog": "",
319
+ "name": "Dashboard Logo",
320
+ "summary": "Dashboard Logo URL",
321
+ "description": "The URL of the logo displayed in the main dashboard. Should be 128x128px"
322
  }
323
  ],
324
  "definitions": {
src/config/feature-audit_trail.php CHANGED
@@ -53,8 +53,8 @@
53
  "section": "section_enable_plugin_feature_audit_trail",
54
  "default": "Y",
55
  "type": "checkbox",
56
- "link_info": "http://icwp.io/5p",
57
- "link_blog": "http://icwp.io/a1",
58
  "name": "Enable Audit Trail",
59
  "summary": "Enable (or Disable) The Audit Trail module",
60
  "description": "Un-Checking this option will completely disable the Audit Trail module"
@@ -65,8 +65,8 @@
65
  "section": "section_audit_trail_options",
66
  "default": 14,
67
  "type": "integer",
68
- "link_info": "http://icwp.io/a2",
69
- "link_blog": "http://icwp.io/a1",
70
  "name": "Auto Clean",
71
  "summary": "Enable Audit Auto Cleaning",
72
  "description": "Events older than the number of days specified will be automatically cleaned from the database"
@@ -88,8 +88,8 @@
88
  "section": "section_enable_audit_contexts",
89
  "default": "Y",
90
  "type": "checkbox",
91
- "link_info": "http://icwp.io/a3",
92
- "link_blog": "http://icwp.io/a1",
93
  "name": "Users And Logins",
94
  "summary": "Enable Audit Context - Users And Logins",
95
  "description": "When this context is enabled, the audit trail will track activity relating to: Users And Logins"
@@ -99,8 +99,8 @@
99
  "section": "section_enable_audit_contexts",
100
  "default": "Y",
101
  "type": "checkbox",
102
- "link_info": "http://icwp.io/a3",
103
- "link_blog": "http://icwp.io/a1",
104
  "name": "Plugins",
105
  "summary": "Enable Audit Context - Plugins",
106
  "description": "When this context is enabled, the audit trail will track activity relating to: WordPress Plugins"
@@ -110,8 +110,8 @@
110
  "section": "section_enable_audit_contexts",
111
  "default": "Y",
112
  "type": "checkbox",
113
- "link_info": "http://icwp.io/a3",
114
- "link_blog": "http://icwp.io/a1",
115
  "name": "Themes",
116
  "summary": "Enable Audit Context - Themes",
117
  "description": "When this context is enabled, the audit trail will track activity relating to: WordPress Themes"
@@ -121,8 +121,8 @@
121
  "section": "section_enable_audit_contexts",
122
  "default": "Y",
123
  "type": "checkbox",
124
- "link_info": "http://icwp.io/a3",
125
- "link_blog": "http://icwp.io/a1",
126
  "name": "Posts And Pages",
127
  "summary": "Enable Audit Context - Posts And Pages",
128
  "description": "When this context is enabled, the audit trail will track activity relating to: Editing and publishing of posts and pages"
@@ -132,8 +132,8 @@
132
  "section": "section_enable_audit_contexts",
133
  "default": "Y",
134
  "type": "checkbox",
135
- "link_info": "http://icwp.io/a3",
136
- "link_blog": "http://icwp.io/a1",
137
  "name": "WordPress And Settings",
138
  "summary": "Enable Audit Context - WordPress And Settings",
139
  "description": "When this context is enabled, the audit trail will track activity relating to: WordPress upgrades and changes to particular WordPress settings"
@@ -143,8 +143,8 @@
143
  "section": "section_enable_audit_contexts",
144
  "default": "Y",
145
  "type": "checkbox",
146
- "link_info": "http://icwp.io/a3",
147
- "link_blog": "http://icwp.io/a1",
148
  "name": "Emails",
149
  "summary": "Enable Audit Context - Emails",
150
  "description": "When this context is enabled, the audit trail will track activity relating to: Email Sending"
@@ -154,8 +154,8 @@
154
  "section": "section_enable_audit_contexts",
155
  "default": "Y",
156
  "type": "checkbox",
157
- "link_info": "http://icwp.io/a4",
158
- "link_blog": "http://icwp.io/a1",
159
  "name": "Shield",
160
  "summary": "Enable Audit Context - Shield",
161
  "description": "When this context is enabled, the audit trail will track activity relating to: Shield"
53
  "section": "section_enable_plugin_feature_audit_trail",
54
  "default": "Y",
55
  "type": "checkbox",
56
+ "link_info": "https://icwp.io/5p",
57
+ "link_blog": "https://icwp.io/a1",
58
  "name": "Enable Audit Trail",
59
  "summary": "Enable (or Disable) The Audit Trail module",
60
  "description": "Un-Checking this option will completely disable the Audit Trail module"
65
  "section": "section_audit_trail_options",
66
  "default": 14,
67
  "type": "integer",
68
+ "link_info": "https://icwp.io/a2",
69
+ "link_blog": "https://icwp.io/a1",
70
  "name": "Auto Clean",
71
  "summary": "Enable Audit Auto Cleaning",
72
  "description": "Events older than the number of days specified will be automatically cleaned from the database"
88
  "section": "section_enable_audit_contexts",
89
  "default": "Y",
90
  "type": "checkbox",
91
+ "link_info": "https://icwp.io/a3",
92
+ "link_blog": "https://icwp.io/a1",
93
  "name": "Users And Logins",
94
  "summary": "Enable Audit Context - Users And Logins",
95
  "description": "When this context is enabled, the audit trail will track activity relating to: Users And Logins"
99
  "section": "section_enable_audit_contexts",
100
  "default": "Y",
101
  "type": "checkbox",
102
+ "link_info": "https://icwp.io/a3",
103
+ "link_blog": "https://icwp.io/a1",
104
  "name": "Plugins",
105
  "summary": "Enable Audit Context - Plugins",
106
  "description": "When this context is enabled, the audit trail will track activity relating to: WordPress Plugins"
110
  "section": "section_enable_audit_contexts",
111
  "default": "Y",
112
  "type": "checkbox",
113
+ "link_info": "https://icwp.io/a3",
114
+ "link_blog": "https://icwp.io/a1",
115
  "name": "Themes",
116
  "summary": "Enable Audit Context - Themes",
117
  "description": "When this context is enabled, the audit trail will track activity relating to: WordPress Themes"
121
  "section": "section_enable_audit_contexts",
122
  "default": "Y",
123
  "type": "checkbox",
124
+ "link_info": "https://icwp.io/a3",
125
+ "link_blog": "https://icwp.io/a1",
126
  "name": "Posts And Pages",
127
  "summary": "Enable Audit Context - Posts And Pages",
128
  "description": "When this context is enabled, the audit trail will track activity relating to: Editing and publishing of posts and pages"
132
  "section": "section_enable_audit_contexts",
133
  "default": "Y",
134
  "type": "checkbox",
135
+ "link_info": "https://icwp.io/a3",
136
+ "link_blog": "https://icwp.io/a1",
137
  "name": "WordPress And Settings",
138
  "summary": "Enable Audit Context - WordPress And Settings",
139
  "description": "When this context is enabled, the audit trail will track activity relating to: WordPress upgrades and changes to particular WordPress settings"
143
  "section": "section_enable_audit_contexts",
144
  "default": "Y",
145
  "type": "checkbox",
146
+ "link_info": "https://icwp.io/a3",
147
+ "link_blog": "https://icwp.io/a1",
148
  "name": "Emails",
149
  "summary": "Enable Audit Context - Emails",
150
  "description": "When this context is enabled, the audit trail will track activity relating to: Email Sending"
154
  "section": "section_enable_audit_contexts",
155
  "default": "Y",
156
  "type": "checkbox",
157
+ "link_info": "https://icwp.io/a4",
158
+ "link_blog": "https://icwp.io/a1",
159
  "name": "Shield",
160
  "summary": "Enable Audit Context - Shield",
161
  "description": "When this context is enabled, the audit trail will track activity relating to: Shield"
src/config/feature-autoupdates.php CHANGED
@@ -48,7 +48,7 @@
48
  "section": "section_enable_plugin_feature_automatic_updates_control",
49
  "default": "Y",
50
  "type": "checkbox",
51
- "link_info": "http://icwp.io/3w",
52
  "link_blog": "",
53
  "name": "Enable Automatic Updates",
54
  "summary": "Enable (or Disable) The Automatic Updates module",
@@ -59,7 +59,7 @@
59
  "section": "section_automatic_updates_for_wordpress_components",
60
  "default": "N",
61
  "type": "checkbox",
62
- "link_info": "http://icwp.io/3v",
63
  "link_blog": "",
64
  "name": "Disable All",
65
  "summary": "Completely Disable WordPress Automatic Updates",
@@ -84,7 +84,7 @@
84
  "text": "Major and Minor Versions"
85
  }
86
  ],
87
- "link_info": "http://icwp.io/3x",
88
  "link_blog": "",
89
  "name": "WordPress Core Updates",
90
  "summary": "Decide how the WordPress Core will automatically update, if at all",
48
  "section": "section_enable_plugin_feature_automatic_updates_control",
49
  "default": "Y",
50
  "type": "checkbox",
51
+ "link_info": "https://icwp.io/3w",
52
  "link_blog": "",
53
  "name": "Enable Automatic Updates",
54
  "summary": "Enable (or Disable) The Automatic Updates module",
59
  "section": "section_automatic_updates_for_wordpress_components",
60
  "default": "N",
61
  "type": "checkbox",
62
+ "link_info": "https://icwp.io/3v",
63
  "link_blog": "",
64
  "name": "Disable All",
65
  "summary": "Completely Disable WordPress Automatic Updates",
84
  "text": "Major and Minor Versions"
85
  }
86
  ],
87
+ "link_info": "https://icwp.io/3x",
88
  "link_blog": "",
89
  "name": "WordPress Core Updates",
90
  "summary": "Decide how the WordPress Core will automatically update, if at all",
src/config/feature-comments_filter.php CHANGED
@@ -84,8 +84,8 @@
84
  "section": "section_enable_plugin_feature_spam_comments_protection_filter",
85
  "default": "Y",
86
  "type": "checkbox",
87
- "link_info": "http://icwp.io/3z",
88
- "link_blog": "http://icwp.io/wpsf04",
89
  "name": "Enable SPAM Protection",
90
  "summary": "Enable (or Disable) The Comments SPAM Protection module",
91
  "description": "Un-Checking this option will completely disable the Comments SPAM Protection module"
@@ -95,8 +95,8 @@
95
  "section": "section_bot_comment_spam_protection_filter",
96
  "default": "N",
97
  "type": "checkbox",
98
- "link_info": "http://icwp.io/3n",
99
- "link_blog": "http://icwp.io/2n",
100
  "name": "GASP Protection",
101
  "summary": "Block Bot Comment SPAM",
102
  "description": "Taking the lead from the original GASP plugin for WordPress, we have extended it to include advanced spam-bot protection."
@@ -106,7 +106,7 @@
106
  "section": "section_bot_comment_spam_protection_filter",
107
  "default": 10,
108
  "type": "integer",
109
- "link_info": "http://icwp.io/3o",
110
  "link_blog": "",
111
  "name": "Comments Cooldown",
112
  "summary": "Limit posting comments to X seconds after the page has loaded",
@@ -135,7 +135,7 @@
135
  "text": "Reject And Redirect"
136
  }
137
  ],
138
- "link_info": "http://icwp.io/6j",
139
  "link_blog": "",
140
  "name": "Default SPAM Action",
141
  "summary": "How To Categorise Comments When Identified To Be SPAM",
@@ -146,8 +146,8 @@
146
  "section": "section_human_spam_filter",
147
  "default": "N",
148
  "type": "checkbox",
149
- "link_info": "http://icwp.io/57",
150
- "link_blog": "http://icwp.io/9w",
151
  "name": "Human SPAM Filter",
152
  "summary": "Enable (or Disable) The Human SPAM Filter module",
153
  "description": "Scans the content of WordPress comments for keywords that are indicative of SPAM and marks the comment according to your preferred setting below."
@@ -190,7 +190,7 @@
190
  "text": "Browser User Agent"
191
  }
192
  ],
193
- "link_info": "http://icwp.io/58",
194
  "link_blog": "",
195
  "name": "Comment Filter Items",
196
  "summary": "Select The Items To Scan For SPAM",
@@ -228,7 +228,7 @@
228
  "section": "section_recaptcha",
229
  "default": "N",
230
  "type": "checkbox",
231
- "link_info": "http://icwp.io/shld5",
232
  "link_blog": "",
233
  "name": "Google reCAPTCHA",
234
  "summary": "Enable Google reCAPTCHA For Comments",
@@ -273,8 +273,8 @@
273
  "section": "section_bot_comment_spam_protection_filter",
274
  "default": 600,
275
  "type": "integer",
276
- "link_info": "http://icwp.io/3o",
277
- "link_blog": "http://icwp.io/9v",
278
  "name": "Comment Token Expire",
279
  "summary": "A visitor has X seconds within which to post a comment",
280
  "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."
@@ -285,7 +285,7 @@
285
  "sensitive": true,
286
  "default": "default",
287
  "type": "text",
288
- "link_info": "http://icwp.io/3p",
289
  "link_blog": "",
290
  "name": "Custom Checkbox Message",
291
  "summary": "If you want a custom checkbox message, please provide this here",
@@ -297,7 +297,7 @@
297
  "sensitive": true,
298
  "default": "default",
299
  "type": "text",
300
- "link_info": "http://icwp.io/3p",
301
  "link_blog": "",
302
  "name": "Custom Alert Message",
303
  "summary": "If you want a custom alert message, please provide this here",
@@ -309,7 +309,7 @@
309
  "sensitive": true,
310
  "default": "default",
311
  "type": "text",
312
- "link_info": "http://icwp.io/3p",
313
  "link_blog": "",
314
  "name": "Custom Wait Message",
315
  "summary": "If you want a custom submit-button wait message, please provide this here.",
@@ -321,7 +321,7 @@
321
  "sensitive": true,
322
  "default": "default",
323
  "type": "text",
324
- "link_info": "http://icwp.io/3p",
325
  "link_blog": "",
326
  "name": "Custom Reload Message",
327
  "summary": "If you want a custom message when the comment token has expired, please provide this here.",
84
  "section": "section_enable_plugin_feature_spam_comments_protection_filter",
85
  "default": "Y",
86
  "type": "checkbox",
87
+ "link_info": "https://icwp.io/3z",
88
+ "link_blog": "https://icwp.io/wpsf04",
89
  "name": "Enable SPAM Protection",
90
  "summary": "Enable (or Disable) The Comments SPAM Protection module",
91
  "description": "Un-Checking this option will completely disable the Comments SPAM Protection module"
95
  "section": "section_bot_comment_spam_protection_filter",
96
  "default": "N",
97
  "type": "checkbox",
98
+ "link_info": "https://icwp.io/3n",
99
+ "link_blog": "https://icwp.io/2n",
100
  "name": "GASP Protection",
101
  "summary": "Block Bot Comment SPAM",
102
  "description": "Taking the lead from the original GASP plugin for WordPress, we have extended it to include advanced spam-bot protection."
106
  "section": "section_bot_comment_spam_protection_filter",
107
  "default": 10,
108
  "type": "integer",
109
+ "link_info": "https://icwp.io/3o",
110
  "link_blog": "",
111
  "name": "Comments Cooldown",
112
  "summary": "Limit posting comments to X seconds after the page has loaded",
135
  "text": "Reject And Redirect"
136
  }
137
  ],
138
+ "link_info": "https://icwp.io/6j",
139
  "link_blog": "",
140
  "name": "Default SPAM Action",
141
  "summary": "How To Categorise Comments When Identified To Be SPAM",
146
  "section": "section_human_spam_filter",
147
  "default": "N",
148
  "type": "checkbox",
149
+ "link_info": "https://icwp.io/57",
150
+ "link_blog": "https://icwp.io/9w",
151
  "name": "Human SPAM Filter",
152
  "summary": "Enable (or Disable) The Human SPAM Filter module",
153
  "description": "Scans the content of WordPress comments for keywords that are indicative of SPAM and marks the comment according to your preferred setting below."
190
  "text": "Browser User Agent"
191
  }
192
  ],
193
+ "link_info": "https://icwp.io/58",
194
  "link_blog": "",
195
  "name": "Comment Filter Items",
196
  "summary": "Select The Items To Scan For SPAM",
228
  "section": "section_recaptcha",
229
  "default": "N",
230
  "type": "checkbox",
231
+ "link_info": "https://icwp.io/shld5",
232
  "link_blog": "",
233
  "name": "Google reCAPTCHA",
234
  "summary": "Enable Google reCAPTCHA For Comments",
273
  "section": "section_bot_comment_spam_protection_filter",
274
  "default": 600,
275
  "type": "integer",
276
+ "link_info": "https://icwp.io/3o",
277
+ "link_blog": "https://icwp.io/9v",
278
  "name": "Comment Token Expire",
279
  "summary": "A visitor has X seconds within which to post a comment",
280
  "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."
285
  "sensitive": true,
286
  "default": "default",
287
  "type": "text",
288
+ "link_info": "https://icwp.io/3p",
289
  "link_blog": "",
290
  "name": "Custom Checkbox Message",
291
  "summary": "If you want a custom checkbox message, please provide this here",
297
  "sensitive": true,
298
  "default": "default",
299
  "type": "text",
300
+ "link_info": "https://icwp.io/3p",
301
  "link_blog": "",
302
  "name": "Custom Alert Message",
303
  "summary": "If you want a custom alert message, please provide this here",
309
  "sensitive": true,
310
  "default": "default",
311
  "type": "text",
312
+ "link_info": "https://icwp.io/3p",
313
  "link_blog": "",
314
  "name": "Custom Wait Message",
315
  "summary": "If you want a custom submit-button wait message, please provide this here.",
321
  "sensitive": true,
322
  "default": "default",
323
  "type": "text",
324
+ "link_info": "https://icwp.io/3p",
325
  "link_blog": "",
326
  "name": "Custom Reload Message",
327
  "summary": "If you want a custom message when the comment token has expired, please provide this here.",
src/config/feature-firewall.php CHANGED
@@ -71,8 +71,8 @@
71
  "section": "section_enable_plugin_feature_wordpress_firewall",
72
  "default": "Y",
73
  "type": "checkbox",
74
- "link_info": "http://icwp.io/43",
75
- "link_blog": "http://icwp.io/wpsf01",
76
  "name": "Enable Firewall",
77
  "summary": "Enable (or Disable) The Firewall module",
78
  "description": "Un-Checking this option will completely disable the Firewall module"
@@ -221,7 +221,7 @@
221
  "section": "section_whitelist",
222
  "default": "",
223
  "type": "comma_separated_lists",
224
- "link_info": "http://icwp.io/2a",
225
  "link_blog": "",
226
  "name": "Whitelist Parameters",
227
  "summary": "Detail pages and parameters that are whitelisted (ignored by the firewall)",
71
  "section": "section_enable_plugin_feature_wordpress_firewall",
72
  "default": "Y",
73
  "type": "checkbox",
74
+ "link_info": "https://icwp.io/43",
75
+ "link_blog": "https://icwp.io/wpsf01",
76
  "name": "Enable Firewall",
77
  "summary": "Enable (or Disable) The Firewall module",
78
  "description": "Un-Checking this option will completely disable the Firewall module"
221
  "section": "section_whitelist",
222
  "default": "",
223
  "type": "comma_separated_lists",
224
+ "link_info": "https://icwp.io/2a",
225
  "link_blog": "",
226
  "name": "Whitelist Parameters",
227
  "summary": "Detail pages and parameters that are whitelisted (ignored by the firewall)",
src/config/feature-hack_protect.php CHANGED
@@ -94,8 +94,8 @@
94
  "section": "section_enable_plugin_feature_hack_protection_tools",
95
  "default": "Y",
96
  "type": "checkbox",
97
- "link_info": "http://icwp.io/wpsf38",
98
- "link_blog": "http://icwp.io/9x",
99
  "name": "Enable Hack Guard",
100
  "summary": "Enable (or Disable) The Hack Guard Module",
101
  "description": "Un-Checking this option will completely disable the Hack Guard module"
@@ -120,7 +120,7 @@
120
  "text": "Enabled - No Email Notification"
121
  }
122
  ],
123
- "link_info": "http://icwp.io/ah",
124
  "link_blog": "",
125
  "name": "Vulnerability Scanner",
126
  "summary": "Enable The Vulnerability Scanner",
@@ -168,8 +168,8 @@
168
  "section": "section_core_file_integrity_scan",
169
  "default": "Y",
170
  "type": "checkbox",
171
- "link_info": "http://icwp.io/wpsf36",
172
- "link_blog": "http://icwp.io/wpsf37",
173
  "name": "Core File Scanner",
174
  "summary": "Scans WordPress Core Files For Alterations",
175
  "description": "Compares all WordPress core files on your site against the official WordPress files. WordPress Core files should never be altered for any reason."
@@ -179,8 +179,8 @@
179
  "section": "section_core_file_integrity_scan",
180
  "default": "N",
181
  "type": "checkbox",
182
- "link_info": "http://icwp.io/wpsf36",
183
- "link_blog": "http://icwp.io/wpsf37",
184
  "name": "Auto Repair",
185
  "summary": "Automatically Repair WordPress Core Files That Have Been Altered",
186
  "description": "Attempts to automatically repair WordPress Core files with the official WordPress file data, for files that have been altered or are missing."
@@ -225,7 +225,7 @@
225
  "text": "24 Times (scan every hour)"
226
  }
227
  ],
228
- "link_info": "http://icwp.io/b2",
229
  "link_blog": "",
230
  "name": "Scan Frequency",
231
  "summary": "Number Of Times To Automatically Scan Core Files In 24 Hours",
@@ -254,8 +254,8 @@
254
  "text": "Auto Delete Files and Email Report"
255
  }
256
  ],
257
- "link_info": "http://icwp.io/9y",
258
- "link_blog": "http://icwp.io/95",
259
  "name": "Unrecognised Files Scanner",
260
  "summary": "Scans Core Directories For Unrecognised Files",
261
  "description": "Scans for, and automatically deletes, any files in your core WordPress folders that are not part of your WordPress installation."
@@ -265,7 +265,7 @@
265
  "section": "section_unrecognised_file_scan",
266
  "default": "N",
267
  "type": "checkbox",
268
- "link_info": "http://icwp.io/95",
269
  "link_blog": "",
270
  "name": "Scan Uploads",
271
  "summary": "Scan Uploads Folder For PHP and Javascript",
@@ -285,8 +285,8 @@
285
  "mail.log"
286
  ],
287
  "type": "array",
288
- "link_info": "http://icwp.io/9z",
289
- "link_blog": "http://icwp.io/95",
290
  "name": "File Exclusions",
291
  "summary": "Provide A List Of Files To Be Excluded From The Scan",
292
  "description": "Take a new line for each file you wish to exclude from the scan. No commas are necessary."
@@ -329,8 +329,8 @@
329
  "text": "Scan Enabled"
330
  }
331
  ],
332
- "link_info": "http://icwp.io/bl",
333
- "link_blog": "http://icwp.io/bm",
334
  "name": "Enable/Disable Guard",
335
  "summary": "Enable The Guard For Plugin And Theme Files",
336
  "description": "When enabled the Guard will automatically scan for changes to your Plugin and Theme files."
@@ -341,8 +341,8 @@
341
  "type": "integer",
342
  "default": 1,
343
  "min": 0,
344
- "link_info": "http://icwp.io/bn",
345
- "link_blog": "http://icwp.io/bm",
346
  "name": "Guard/Scan Depth",
347
  "summary": "How Deep Into The Plugin Directories To Scan And Guard",
348
  "description": "The Guard normally operates scan only the top level of a plugin folder. Increasing depth increases scan times."
@@ -358,7 +358,7 @@
358
  "htaccess"
359
  ],
360
  "type": "array",
361
- "link_info": "http://icwp.io/bo",
362
  "link_blog": "",
363
  "name": "File Types",
364
  "summary": "The File Types Included In The Scan",
@@ -369,7 +369,7 @@
369
  "section": "section_pluginthemes_guard",
370
  "type": "checkbox",
371
  "default": "Y",
372
- "link_info": "http://icwp.io/bp",
373
  "link_blog": "",
374
  "name": "Show Re-Install Links",
375
  "summary": "Show Re-Install Links For Plugins",
94
  "section": "section_enable_plugin_feature_hack_protection_tools",
95
  "default": "Y",
96
  "type": "checkbox",
97
+ "link_info": "https://icwp.io/wpsf38",
98
+ "link_blog": "https://icwp.io/9x",
99
  "name": "Enable Hack Guard",
100
  "summary": "Enable (or Disable) The Hack Guard Module",
101
  "description": "Un-Checking this option will completely disable the Hack Guard module"
120
  "text": "Enabled - No Email Notification"
121
  }
122
  ],
123
+ "link_info": "https://icwp.io/ah",
124
  "link_blog": "",
125
  "name": "Vulnerability Scanner",
126
  "summary": "Enable The Vulnerability Scanner",
168
  "section": "section_core_file_integrity_scan",
169
  "default": "Y",
170
  "type": "checkbox",
171
+ "link_info": "https://icwp.io/wpsf36",
172
+ "link_blog": "https://icwp.io/wpsf37",
173
  "name": "Core File Scanner",
174
  "summary": "Scans WordPress Core Files For Alterations",
175
  "description": "Compares all WordPress core files on your site against the official WordPress files. WordPress Core files should never be altered for any reason."
179
  "section": "section_core_file_integrity_scan",
180
  "default": "N",
181
  "type": "checkbox",
182
+ "link_info": "https://icwp.io/wpsf36",
183
+ "link_blog": "https://icwp.io/wpsf37",
184
  "name": "Auto Repair",
185
  "summary": "Automatically Repair WordPress Core Files That Have Been Altered",
186
  "description": "Attempts to automatically repair WordPress Core files with the official WordPress file data, for files that have been altered or are missing."
225
  "text": "24 Times (scan every hour)"
226
  }
227
  ],
228
+ "link_info": "https://icwp.io/b2",
229
  "link_blog": "",
230
  "name": "Scan Frequency",
231
  "summary": "Number Of Times To Automatically Scan Core Files In 24 Hours",
254
  "text": "Auto Delete Files and Email Report"
255
  }
256
  ],
257
+ "link_info": "https://icwp.io/9y",
258
+ "link_blog": "https://icwp.io/95",
259
  "name": "Unrecognised Files Scanner",
260
  "summary": "Scans Core Directories For Unrecognised Files",
261
  "description": "Scans for, and automatically deletes, any files in your core WordPress folders that are not part of your WordPress installation."
265
  "section": "section_unrecognised_file_scan",
266
  "default": "N",
267
  "type": "checkbox",
268
+ "link_info": "https://icwp.io/95",
269
  "link_blog": "",
270
  "name": "Scan Uploads",
271
  "summary": "Scan Uploads Folder For PHP and Javascript",
285
  "mail.log"
286
  ],
287
  "type": "array",
288
+ "link_info": "https://icwp.io/9z",
289
+ "link_blog": "https://icwp.io/95",
290
  "name": "File Exclusions",
291
  "summary": "Provide A List Of Files To Be Excluded From The Scan",
292
  "description": "Take a new line for each file you wish to exclude from the scan. No commas are necessary."
329
  "text": "Scan Enabled"
330
  }
331
  ],
332
+ "link_info": "https://icwp.io/bl",
333
+ "link_blog": "https://icwp.io/bm",
334
  "name": "Enable/Disable Guard",
335
  "summary": "Enable The Guard For Plugin And Theme Files",
336
  "description": "When enabled the Guard will automatically scan for changes to your Plugin and Theme files."
341
  "type": "integer",
342
  "default": 1,
343
  "min": 0,
344
+ "link_info": "https://icwp.io/bn",
345
+ "link_blog": "https://icwp.io/bm",
346
  "name": "Guard/Scan Depth",
347
  "summary": "How Deep Into The Plugin Directories To Scan And Guard",
348
  "description": "The Guard normally operates scan only the top level of a plugin folder. Increasing depth increases scan times."
358
  "htaccess"
359
  ],
360
  "type": "array",
361
+ "link_info": "https://icwp.io/bo",
362
  "link_blog": "",
363
  "name": "File Types",
364
  "summary": "The File Types Included In The Scan",
369
  "section": "section_pluginthemes_guard",
370
  "type": "checkbox",
371
  "default": "Y",
372
+ "link_info": "https://icwp.io/bp",
373
  "link_blog": "",
374
  "name": "Show Re-Install Links",
375
  "summary": "Show Re-Install Links For Plugins",
src/config/feature-headers.php CHANGED
@@ -51,8 +51,8 @@
51
  "section": "section_enable_plugin_feature_headers",
52
  "default": "Y",
53
  "type": "checkbox",
54
- "link_info": "http://icwp.io/7c",
55
- "link_blog": "http://icwp.io/7c",
56
  "name": "Enable HTTP Headers",
57
  "summary": "Enable (or Disable) The HTTP Headers module",
58
  "description": "Un-Checking this option will completely disable the HTTP Headers module"
@@ -76,8 +76,8 @@
76
  "text": "On: Block All iFrames"
77
  }
78
  ],
79
- "link_info": "http://icwp.io/78",
80
- "link_blog": "http://icwp.io/7c",
81
  "name": "Block iFrames",
82
  "summary": "Block Remote iFrames Of This Site",
83
  "description": "The setting prevents any external website from embedding your site in an iFrame. This is useful for preventing so-called ClickJack attacks."
@@ -87,8 +87,8 @@
87
  "section": "section_security_headers",
88
  "default": "Y",
89
  "type": "checkbox",
90
- "link_info": "http://icwp.io/79",
91
- "link_blog": "http://icwp.io/7c",
92
  "name": "XSS Protection",
93
  "summary": "Employ Built-In Browser XSS Protection",
94
  "description": "Directs compatible browsers to block what they detect as Reflective XSS attacks."
@@ -98,8 +98,8 @@
98
  "section": "section_security_headers",
99
  "default": "Y",
100
  "type": "checkbox",
101
- "link_info": "http://icwp.io/7a",
102
- "link_blog": "http://icwp.io/7c",
103
  "name": "Prevent Mime-Sniff",
104
  "summary": "Turn-Off Browser Mime-Sniff",
105
  "description": "Reduces visitor exposure to malicious user-uploaded content."
@@ -152,7 +152,7 @@
152
  "text": "Don't Send This Header"
153
  }
154
  ],
155
- "link_info": "http://icwp.io/a5",
156
  "link_blog": "",
157
  "name": "Referrer Policy",
158
  "summary": "Referrer Policy Header",
@@ -163,8 +163,8 @@
163
  "section": "section_content_security_policy",
164
  "default": "N",
165
  "type": "checkbox",
166
- "link_info": "http://icwp.io/7d",
167
- "link_blog": "http://icwp.io/7c",
168
  "name": "Enable Content Security Policy",
169
  "summary": "Enable (or Disable) The Content Security Policy module",
170
  "description": "Allows for permission and restriction of all resources loaded on your site."
51
  "section": "section_enable_plugin_feature_headers",
52
  "default": "Y",
53
  "type": "checkbox",
54
+ "link_info": "https://icwp.io/7c",
55
+ "link_blog": "https://icwp.io/7c",
56
  "name": "Enable HTTP Headers",
57
  "summary": "Enable (or Disable) The HTTP Headers module",
58
  "description": "Un-Checking this option will completely disable the HTTP Headers module"
76
  "text": "On: Block All iFrames"
77
  }
78
  ],
79
+ "link_info": "https://icwp.io/78",
80
+ "link_blog": "https://icwp.io/7c",
81
  "name": "Block iFrames",
82
  "summary": "Block Remote iFrames Of This Site",
83
  "description": "The setting prevents any external website from embedding your site in an iFrame. This is useful for preventing so-called ClickJack attacks."
87
  "section": "section_security_headers",
88
  "default": "Y",
89
  "type": "checkbox",
90
+ "link_info": "https://icwp.io/79",
91
+ "link_blog": "https://icwp.io/7c",
92
  "name": "XSS Protection",
93
  "summary": "Employ Built-In Browser XSS Protection",
94
  "description": "Directs compatible browsers to block what they detect as Reflective XSS attacks."
98
  "section": "section_security_headers",
99
  "default": "Y",
100
  "type": "checkbox",
101
+ "link_info": "https://icwp.io/7a",
102
+ "link_blog": "https://icwp.io/7c",
103
  "name": "Prevent Mime-Sniff",
104
  "summary": "Turn-Off Browser Mime-Sniff",
105
  "description": "Reduces visitor exposure to malicious user-uploaded content."
152
  "text": "Don't Send This Header"
153
  }
154
  ],
155
+ "link_info": "https://icwp.io/a5",
156
  "link_blog": "",
157
  "name": "Referrer Policy",
158
  "summary": "Referrer Policy Header",
163
  "section": "section_content_security_policy",
164
  "default": "N",
165
  "type": "checkbox",
166
+ "link_info": "https://icwp.io/7d",
167
+ "link_blog": "https://icwp.io/7c",
168
  "name": "Enable Content Security Policy",
169
  "summary": "Enable (or Disable) The Content Security Policy module",
170
  "description": "Allows for permission and restriction of all resources loaded on your site."
src/config/feature-ips.php CHANGED
@@ -85,7 +85,7 @@
85
  "section": "section_enable_plugin_feature_ips",
86
  "default": "Y",
87
  "type": "checkbox",
88
- "link_info": "http://icwp.io/wpsf26",
89
  "link_blog": "",
90
  "name": "Enable IP Manager",
91
  "summary": "Enable (or Disable) The IP Manager module",
@@ -96,8 +96,8 @@
96
  "section": "section_auto_black_list",
97
  "default": 10,
98
  "type": "integer",
99
- "link_info": "http://icwp.io/wpsf24",
100
- "link_blog": "http://icwp.io/wpsf26",
101
  "name": "Transgression Limit",
102
  "summary": "Visitor IP address will be Black Listed after X bad actions on your site",
103
  "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 transgressions exceeds specified limit, they are automatically blocked from accessing the site. Set this to 0 to turn off the Automatic IP Black List feature."
@@ -125,8 +125,8 @@
125
  "text": "Week"
126
  }
127
  ],
128
- "link_info": "http://icwp.io/wpsf25",
129
- "link_blog": "http://icwp.io/wpsf26",
130
  "name": "Auto Block Expiration",
131
  "summary": "After 1 'X' a black listed IP will be removed from the black list",
132
  "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."
85
  "section": "section_enable_plugin_feature_ips",
86
  "default": "Y",
87
  "type": "checkbox",
88
+ "link_info": "https://icwp.io/wpsf26",
89
  "link_blog": "",
90
  "name": "Enable IP Manager",
91
  "summary": "Enable (or Disable) The IP Manager module",
96
  "section": "section_auto_black_list",
97
  "default": 10,
98
  "type": "integer",
99
+ "link_info": "https://icwp.io/wpsf24",
100
+ "link_blog": "https://icwp.io/wpsf26",
101
  "name": "Transgression Limit",
102
  "summary": "Visitor IP address will be Black Listed after X bad actions on your site",
103
  "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 transgressions exceeds specified limit, they are automatically blocked from accessing the site. Set this to 0 to turn off the Automatic IP Black List feature."
125
  "text": "Week"
126
  }
127
  ],
128
+ "link_info": "https://icwp.io/wpsf25",
129
+ "link_blog": "https://icwp.io/wpsf26",
130
  "name": "Auto Block Expiration",
131
  "summary": "After 1 'X' a black listed IP will be removed from the black list",
132
  "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."
src/config/feature-license.php CHANGED
@@ -2,7 +2,7 @@
2
  "slug": "license",
3
  "properties": {
4
  "slug": "license",
5
- "name": "Shield Pro",
6
  "tagline": "The Best In WordPress Security, Only Better.",
7
  "auto_enabled": true,
8
  "show_module_menu_item": true,
@@ -126,7 +126,7 @@
126
  ],
127
  "definitions": {
128
  "license_store_url": "https://onedollarplugin.com/edd-sl/",
129
- "keyless_cp": "http://icwp.io/c5",
130
  "license_item_name": "Shield Security Pro",
131
  "license_item_id": "6047",
132
  "license_item_name_sc": "Shield Security Pro (via Shield Central)",
2
  "slug": "license",
3
  "properties": {
4
  "slug": "license",
5
+ "name": "Pro Security",
6
  "tagline": "The Best In WordPress Security, Only Better.",
7
  "auto_enabled": true,
8
  "show_module_menu_item": true,
126
  ],
127
  "definitions": {
128
  "license_store_url": "https://onedollarplugin.com/edd-sl/",
129
+ "keyless_cp": "https://icwp.io/c5",
130
  "license_item_name": "Shield Security Pro",
131
  "license_item_id": "6047",
132
  "license_item_name_sc": "Shield Security Pro (via Shield Central)",
src/config/feature-lockdown.php CHANGED
@@ -60,7 +60,7 @@
60
  "section": "section_enable_plugin_feature_wordpress_lockdown",
61
  "default": "Y",
62
  "type": "checkbox",
63
- "link_info": "http://icwp.io/4r",
64
  "link_blog": "",
65
  "name": "Enable Lockdown",
66
  "summary": "Enable (or Disable) The Lockdown module",
@@ -108,7 +108,7 @@
108
  "section": "section_permission_access_options",
109
  "default": "N",
110
  "type": "checkbox",
111
- "link_info": "http://icwp.io/4q",
112
  "link_blog": "",
113
  "name": "Disable File Editing",
114
  "summary": "Disable Ability To Edit Files From Within WordPress",
@@ -119,7 +119,7 @@
119
  "section": "section_permission_access_options",
120
  "default": "N",
121
  "type": "checkbox",
122
- "link_info": "http://icwp.io/4t",
123
  "link_blog": "",
124
  "name": "Force SSL Admin",
125
  "summary": "Forces WordPress Admin Dashboard To Be Delivered Over SSL",
@@ -130,7 +130,7 @@
130
  "section": "section_wordpress_obscurity_options",
131
  "default": "",
132
  "type": "text",
133
- "link_info": "http://icwp.io/43",
134
  "link_blog": "",
135
  "name": "Mask WordPress Version",
136
  "summary": "Prevents Public Display Of Your WordPress Version",
@@ -152,7 +152,7 @@
152
  "section": "section_wordpress_obscurity_options",
153
  "default": "Y",
154
  "type": "checkbox",
155
- "link_info": "http://icwp.io/wpsf23",
156
  "link_blog": "",
157
  "name": "Block Username Fishing",
158
  "summary": "Block the ability to discover WordPress usernames based on author IDs",
60
  "section": "section_enable_plugin_feature_wordpress_lockdown",
61
  "default": "Y",
62
  "type": "checkbox",
63
+ "link_info": "https://icwp.io/4r",
64
  "link_blog": "",
65
  "name": "Enable Lockdown",
66
  "summary": "Enable (or Disable) The Lockdown module",
108
  "section": "section_permission_access_options",
109
  "default": "N",
110
  "type": "checkbox",
111
+ "link_info": "https://icwp.io/4q",
112
  "link_blog": "",
113
  "name": "Disable File Editing",
114
  "summary": "Disable Ability To Edit Files From Within WordPress",
119
  "section": "section_permission_access_options",
120
  "default": "N",
121
  "type": "checkbox",
122
+ "link_info": "https://icwp.io/4t",
123
  "link_blog": "",
124
  "name": "Force SSL Admin",
125
  "summary": "Forces WordPress Admin Dashboard To Be Delivered Over SSL",
130
  "section": "section_wordpress_obscurity_options",
131
  "default": "",
132
  "type": "text",
133
+ "link_info": "https://icwp.io/43",
134
  "link_blog": "",
135
  "name": "Mask WordPress Version",
136
  "summary": "Prevents Public Display Of Your WordPress Version",
152
  "section": "section_wordpress_obscurity_options",
153
  "default": "Y",
154
  "type": "checkbox",
155
+ "link_info": "https://icwp.io/wpsf23",
156
  "link_blog": "",
157
  "name": "Block Username Fishing",
158
  "summary": "Block the ability to discover WordPress usernames based on author IDs",
src/config/feature-login_protect.php CHANGED
@@ -117,8 +117,8 @@
117
  "section": "section_enable_plugin_feature_login_protection",
118
  "default": "Y",
119
  "type": "checkbox",
120
- "link_info": "http://icwp.io/51",
121
- "link_blog": "http://icwp.io/wpsf03",
122
  "name": "Enable Login Guard",
123
  "summary": "Enable (or Disable) The Login Guard Module",
124
  "description": "Un-Checking this option will completely disable the Login Guard module"
@@ -129,8 +129,8 @@
129
  "sensitive": true,
130
  "default": "",
131
  "type": "text",
132
- "link_info": "http://icwp.io/5q",
133
- "link_blog": "http://icwp.io/5r",
134
  "name": "Hide Login Page",
135
  "summary": "Rename The WordPress Login Page",
136
  "description": "Creating a path here will disable your 'wp-login.php'. Only letters and numbers are permitted: abc123"
@@ -140,8 +140,8 @@
140
  "section": "section_multifactor_authentication",
141
  "default": "N",
142
  "type": "checkbox",
143
- "link_info": "http://icwp.io/9r",
144
- "link_blog": "http://icwp.io/84",
145
  "name": "Multi-Factor Authentication",
146
  "summary": "Require All Active Authentication Factors",
147
  "description": "When enabled, all multi-factor authentication methods will be applied to a user login. Disable to only require one to pass."
@@ -152,7 +152,7 @@
152
  "premium": true,
153
  "default": 0,
154
  "type": "integer",
155
- "link_info": "http://icwp.io/b1",
156
  "link_blog": "",
157
  "name": "Multi-Factor By-Pass",
158
  "summary": "A User Can By-Pass Multi-Factor Authentication (MFA) For The Set Number Of Days",
@@ -163,8 +163,8 @@
163
  "section": "section_2fa_ga",
164
  "default": "N",
165
  "type": "checkbox",
166
- "link_info": "http://icwp.io/shld7",
167
- "link_blog": "http://icwp.io/shld6",
168
  "name": "Enable Google Authenticator",
169
  "summary": "Allow Users To Use Google Authenticator",
170
  "description": "When enabled, users will have the option to add Google Authenticator to their WordPress user profile."
@@ -174,8 +174,8 @@
174
  "section": "section_2fa_email",
175
  "default": "N",
176
  "type": "checkbox",
177
- "link_info": "http://icwp.io/3t",
178
- "link_blog": "http://icwp.io/9q",
179
  "name": "Enable Email Authentication",
180
  "summary": "Two-Factor Login Authentication By Email",
181
  "description": "All users will be required to verify their login by email-based two-factor authentication."
@@ -212,93 +212,111 @@
212
  "text": "Administrators"
213
  }
214
  ],
215
- "link_info": "http://icwp.io/4v",
216
  "link_blog": "",
217
  "name": "Enforce - Email Authentication",
218
  "summary": "All User Roles Subject To Email Authentication",
219
  "description": "Enforces email-based authentication on all users with the selected roles. Note: This setting only applies to email authentication."
220
  },
221
  {
222
- "key": "enable_google_recaptcha_login",
223
- "section": "section_recaptcha",
224
- "default": "N",
225
- "type": "checkbox",
226
- "link_info": "http://icwp.io/9m",
227
- "link_blog": "http://icwp.io/shld5",
228
- "name": "Google reCAPTCHA",
229
- "summary": "Enable Google reCAPTCHA",
230
- "description": "Use Google reCAPTCHA on the login screen."
231
- },
232
- {
233
- "key": "google_recaptcha_style_login",
234
- "section": "section_recaptcha",
235
- "premium": true,
236
- "default": "default",
237
- "type": "select",
238
  "value_options": [
239
  {
240
- "value_key": "default",
241
- "text": "Default"
242
  },
243
  {
244
- "value_key": "light",
245
- "text": "Light Theme"
246
  },
247
  {
248
- "value_key": "dark",
249
- "text": "Dark Theme"
250
  },
251
  {
252
- "value_key": "invisible",
253
- "text": "Invisible reCAPTCHA"
254
  }
255
  ],
256
  "link_info": "",
257
  "link_blog": "",
258
- "name": "reCAPTCHA Style",
259
  "summary": "How Google reCAPTCHA Will Be Displayed",
260
- "description": "You can choose the reCAPTCHA display format that best suits your site, including the new Invisible Recaptcha."
 
 
 
 
 
 
 
 
 
 
 
261
  },
262
  {
263
  "key": "enable_login_gasp_check",
264
  "section": "section_brute_force_login_protection",
265
  "default": "N",
266
  "type": "checkbox",
267
- "link_info": "http://icwp.io/3r",
268
- "link_blog": "http://icwp.io/9n",
269
  "name": "Bot Protection",
270
  "summary": "Protect WP Login From Automated Login Attempts By Bots",
271
  "description": "Adds a dynamically (Javascript) generated checkbox to the login form that prevents bots using automated login techniques. Recommended: ON."
272
  },
273
  {
274
- "key": "login_limit_interval",
275
  "section": "section_brute_force_login_protection",
276
- "default": "10",
277
- "type": "integer",
278
- "link_info": "http://icwp.io/3q",
279
- "link_blog": "http://icwp.io/9o",
280
- "name": "Login Cooldown Interval",
281
- "summary": "Limit login attempts to every X seconds",
282
- "description": "WordPress will process only ONE login attempt for every number of seconds specified. Zero (0) turns this off."
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
283
  },
284
  {
285
  "key": "enable_user_register_checking",
286
- "section": "section_brute_force_login_protection",
287
- "default": "Y",
288
- "type": "checkbox",
289
- "link_info": "http://icwp.io/9p",
290
- "link_blog": "",
291
- "name": "User Registration",
292
- "summary": "Apply Brute Force Protection To User Registration And Lost Passwords",
293
- "description": "When enabled, settings in this section will also apply to new user registration and users trying to reset passwords."
294
  },
295
  {
296
  "key": "enable_yubikey",
297
  "section": "section_yubikey_authentication",
298
  "default": "N",
299
  "type": "checkbox",
300
- "link_info": "http://icwp.io/4f",
301
- "link_blog": "http://icwp.io/9t",
302
  "name": "Enable Yubikey Authentication",
303
  "summary": "Turn On / Off Yubikey Authentication On This Site",
304
  "description": "Combined with your Yubikey API Key (below) this will form the basis of your Yubikey Authentication."
@@ -309,7 +327,7 @@
309
  "sensitive": true,
310
  "default": "",
311
  "type": "text",
312
- "link_info": "http://icwp.io/4g",
313
  "link_blog": "",
314
  "name": "Yubikey App ID",
315
  "summary": "Your Unique Yubikey App ID",
@@ -321,7 +339,7 @@
321
  "sensitive": true,
322
  "default": "",
323
  "type": "text",
324
- "link_info": "http://icwp.io/4g",
325
  "link_blog": "",
326
  "name": "Yubikey API Key",
327
  "summary": "Your Unique Yubikey App API Key",
117
  "section": "section_enable_plugin_feature_login_protection",
118
  "default": "Y",
119
  "type": "checkbox",
120
+ "link_info": "https://icwp.io/51",
121
+ "link_blog": "https://icwp.io/wpsf03",
122
  "name": "Enable Login Guard",
123
  "summary": "Enable (or Disable) The Login Guard Module",
124
  "description": "Un-Checking this option will completely disable the Login Guard module"
129
  "sensitive": true,
130
  "default": "",
131
  "type": "text",
132
+ "link_info": "https://icwp.io/5q",
133
+ "link_blog": "https://icwp.io/5r",
134
  "name": "Hide Login Page",
135
  "summary": "Rename The WordPress Login Page",
136
  "description": "Creating a path here will disable your 'wp-login.php'. Only letters and numbers are permitted: abc123"
140
  "section": "section_multifactor_authentication",
141
  "default": "N",
142
  "type": "checkbox",
143
+ "link_info": "https://icwp.io/9r",
144
+ "link_blog": "https://icwp.io/84",
145
  "name": "Multi-Factor Authentication",
146
  "summary": "Require All Active Authentication Factors",
147
  "description": "When enabled, all multi-factor authentication methods will be applied to a user login. Disable to only require one to pass."
152
  "premium": true,
153
  "default": 0,
154
  "type": "integer",
155
+ "link_info": "https://icwp.io/b1",
156
  "link_blog": "",
157
  "name": "Multi-Factor By-Pass",
158
  "summary": "A User Can By-Pass Multi-Factor Authentication (MFA) For The Set Number Of Days",
163
  "section": "section_2fa_ga",
164
  "default": "N",
165
  "type": "checkbox",
166
+ "link_info": "https://icwp.io/shld7",
167
+ "link_blog": "https://icwp.io/shld6",
168
  "name": "Enable Google Authenticator",
169
  "summary": "Allow Users To Use Google Authenticator",
170
  "description": "When enabled, users will have the option to add Google Authenticator to their WordPress user profile."
174
  "section": "section_2fa_email",
175
  "default": "N",
176
  "type": "checkbox",
177
+ "link_info": "https://icwp.io/3t",
178
+ "link_blog": "https://icwp.io/9q",
179
  "name": "Enable Email Authentication",
180
  "summary": "Two-Factor Login Authentication By Email",
181
  "description": "All users will be required to verify their login by email-based two-factor authentication."
212
  "text": "Administrators"
213
  }
214
  ],
215
+ "link_info": "https://icwp.io/4v",
216
  "link_blog": "",
217
  "name": "Enforce - Email Authentication",
218
  "summary": "All User Roles Subject To Email Authentication",
219
  "description": "Enforces email-based authentication on all users with the selected roles. Note: This setting only applies to email authentication."
220
  },
221
  {
222
+ "key": "bot_protection_locations",
223
+ "section": "section_brute_force_login_protection",
224
+ "type": "multiple_select",
225
+ "default": [ "login" ],
 
 
 
 
 
 
 
 
 
 
 
 
226
  "value_options": [
227
  {
228
+ "value_key": "login",
229
+ "text": "Login"
230
  },
231
  {
232
+ "value_key": "register",
233
+ "text": "Register"
234
  },
235
  {
236
+ "value_key": "password",
237
+ "text": "Lost Password"
238
  },
239
  {
240
+ "value_key": "checkout_woo",
241
+ "text": "Checkout (WooCommerce)"
242
  }
243
  ],
244
  "link_info": "",
245
  "link_blog": "",
246
+ "name": "Protection Locations",
247
  "summary": "How Google reCAPTCHA Will Be Displayed",
248
+ "description": "Choose for which forms bot protection measures will be deployed."
249
+ },
250
+ {
251
+ "key": "login_limit_interval",
252
+ "section": "section_brute_force_login_protection",
253
+ "default": "10",
254
+ "type": "integer",
255
+ "link_info": "https://icwp.io/3q",
256
+ "link_blog": "https://icwp.io/9o",
257
+ "name": "Login Cooldown Interval",
258
+ "summary": "Limit login attempts to every X seconds",
259
+ "description": "WordPress will process only ONE login attempt for every number of seconds specified. Zero (0) turns this off."
260
  },
261
  {
262
  "key": "enable_login_gasp_check",
263
  "section": "section_brute_force_login_protection",
264
  "default": "N",
265
  "type": "checkbox",
266
+ "link_info": "https://icwp.io/3r",
267
+ "link_blog": "https://icwp.io/9n",
268
  "name": "Bot Protection",
269
  "summary": "Protect WP Login From Automated Login Attempts By Bots",
270
  "description": "Adds a dynamically (Javascript) generated checkbox to the login form that prevents bots using automated login techniques. Recommended: ON."
271
  },
272
  {
273
+ "key": "enable_google_recaptcha_login",
274
  "section": "section_brute_force_login_protection",
275
+ "default": "disabled",
276
+ "type": "select",
277
+ "value_options": [
278
+ {
279
+ "value_key": "disabled",
280
+ "text": "Disabled"
281
+ },
282
+ {
283
+ "value_key": "default",
284
+ "text": "Default Style"
285
+ },
286
+ {
287
+ "value_key": "light",
288
+ "text": "Light Theme"
289
+ },
290
+ {
291
+ "value_key": "dark",
292
+ "text": "Dark Theme"
293
+ },
294
+ {
295
+ "value_key": "invisible",
296
+ "text": "Invisible reCAPTCHA"
297
+ }
298
+ ],
299
+ "link_info": "https://icwp.io/9m",
300
+ "link_blog": "https://icwp.io/shld5",
301
+ "name": "Google reCAPTCHA",
302
+ "summary": "Enable Google reCAPTCHA",
303
+ "description": "Use Google reCAPTCHA on the login screen."
304
+ },
305
+ {
306
+ "key": "google_recaptcha_style_login",
307
+ "section": "section_non_ui"
308
  },
309
  {
310
  "key": "enable_user_register_checking",
311
+ "section": "section_non_ui"
 
 
 
 
 
 
 
312
  },
313
  {
314
  "key": "enable_yubikey",
315
  "section": "section_yubikey_authentication",
316
  "default": "N",
317
  "type": "checkbox",
318
+ "link_info": "https://icwp.io/4f",
319
+ "link_blog": "https://icwp.io/9t",
320
  "name": "Enable Yubikey Authentication",
321
  "summary": "Turn On / Off Yubikey Authentication On This Site",
322
  "description": "Combined with your Yubikey API Key (below) this will form the basis of your Yubikey Authentication."
327
  "sensitive": true,
328
  "default": "",
329
  "type": "text",
330
+ "link_info": "https://icwp.io/4g",
331
  "link_blog": "",
332
  "name": "Yubikey App ID",
333
  "summary": "Your Unique Yubikey App ID",
339
  "sensitive": true,
340
  "default": "",
341
  "type": "text",
342
+ "link_info": "https://icwp.io/4g",
343
  "link_blog": "",
344
  "name": "Yubikey API Key",
345
  "summary": "Your Unique Yubikey App API Key",
src/config/feature-plugin.php CHANGED
@@ -111,7 +111,7 @@
111
  "section": "section_general_plugin_options",
112
  "default": "N",
113
  "type": "checkbox",
114
- "link_info": "http://icwp.io/7i",
115
  "link_blog": "",
116
  "name": "Enable Information Gathering",
117
  "summary": "Permit Anonymous Usage Information Gathering",
@@ -199,8 +199,8 @@
199
  "section": "section_general_plugin_options",
200
  "default": "N",
201
  "type": "checkbox",
202
- "link_info": "http://icwp.io/5v",
203
- "link_blog": "http://icwp.io/wpsf20",
204
  "name": "Show Plugin Badge",
205
  "summary": "Display Plugin Badge On Your Site",
206
  "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."
@@ -318,7 +318,7 @@
318
  "sensitive": true,
319
  "default": "",
320
  "type": "text",
321
- "link_info": "http://icwp.io/shld5",
322
  "link_blog": "",
323
  "name": "reCAPTCHA Secret",
324
  "summary": "Google reCAPTCHA Secret Key",
@@ -330,7 +330,7 @@
330
  "sensitive": true,
331
  "default": "",
332
  "type": "text",
333
- "link_info": "http://icwp.io/shld5",
334
  "link_blog": "",
335
  "name": "reCAPTCHA Site Key",
336
  "summary": "Google reCAPTCHA Site Key",
@@ -398,7 +398,7 @@
398
  "tracking_cron_handle": "plugin_tracking_cron",
399
  "tracking_post_url": "https://tracking.icontrolwp.com/track/plugin/shield",
400
  "importexport_cron_name": "autoimport",
401
- "href_privacy_policy": "http://icwp.io/wpshieldprivacypolicy",
402
  "db_notes_name": "notes",
403
  "db_notes_table_columns": [
404
  "id",
111
  "section": "section_general_plugin_options",
112
  "default": "N",
113
  "type": "checkbox",
114
+ "link_info": "https://icwp.io/7i",
115
  "link_blog": "",
116
  "name": "Enable Information Gathering",
117
  "summary": "Permit Anonymous Usage Information Gathering",
199
  "section": "section_general_plugin_options",
200
  "default": "N",
201
  "type": "checkbox",
202
+ "link_info": "https://icwp.io/5v",
203
+ "link_blog": "https://icwp.io/wpsf20",
204
  "name": "Show Plugin Badge",
205
  "summary": "Display Plugin Badge On Your Site",
206
  "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."
318
  "sensitive": true,
319
  "default": "",
320
  "type": "text",
321
+ "link_info": "https://icwp.io/shld5",
322
  "link_blog": "",
323
  "name": "reCAPTCHA Secret",
324
  "summary": "Google reCAPTCHA Secret Key",
330
  "sensitive": true,
331
  "default": "",
332
  "type": "text",
333
+ "link_info": "https://icwp.io/shld5",
334
  "link_blog": "",
335
  "name": "reCAPTCHA Site Key",
336
  "summary": "Google reCAPTCHA Site Key",
398
  "tracking_cron_handle": "plugin_tracking_cron",
399
  "tracking_post_url": "https://tracking.icontrolwp.com/track/plugin/shield",
400
  "importexport_cron_name": "autoimport",
401
+ "href_privacy_policy": "https://icwp.io/wpshieldprivacypolicy",
402
  "db_notes_name": "notes",
403
  "db_notes_table_columns": [
404
  "id",
src/config/feature-user_management.php CHANGED
@@ -70,6 +70,19 @@
70
  "summary": "Enable (or Disable) The User Management module",
71
  "description": "Un-Checking this option will completely disable the User Management module"
72
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
73
  {
74
  "key": "enable_admin_login_email_notification",
75
  "section": "section_admin_login_notification",
@@ -131,7 +144,7 @@
131
  "section": "section_passwords",
132
  "type": "checkbox",
133
  "default": "N",
134
- "link_info": "http://icwp.io/c4",
135
  "link_blog": "",
136
  "name": "Enable Password Policies",
137
  "summary": "Enable The Password Policies Below",
@@ -142,7 +155,7 @@
142
  "section": "section_passwords",
143
  "type": "checkbox",
144
  "default": "Y",
145
- "link_info": "http://icwp.io/by",
146
  "link_blog": "",
147
  "name": "Prevent Pwned Passwords",
148
  "summary": "Prevent Use Of Pwned Passwords",
@@ -241,6 +254,7 @@
241
  }
242
  ],
243
  "definitions": {
 
244
  "pwned_api_url_password_single": "https://api.pwnedpasswords.com/pwnedpassword/",
245
  "pwned_api_url_password_range": "https://api.pwnedpasswords.com/range/"
246
  }
70
  "summary": "Enable (or Disable) The User Management module",
71
  "description": "Un-Checking this option will completely disable the User Management module"
72
  },
73
+ {
74
+ "key": "enable_user_login_email_notification",
75
+ "section": "section_admin_login_notification",
76
+ "premium": true,
77
+ "sensitive": false,
78
+ "default": "N",
79
+ "type": "checkbox",
80
+ "link_info": "",
81
+ "link_blog": "",
82
+ "name": "User Login Notification Email",
83
+ "summary": "Send Email Notification To Each User Upon Successful Login",
84
+ "description": "A notification is sent to each user when a successful login occurs for their account."
85
+ },
86
  {
87
  "key": "enable_admin_login_email_notification",
88
  "section": "section_admin_login_notification",
144
  "section": "section_passwords",
145
  "type": "checkbox",
146
  "default": "N",
147
+ "link_info": "https://icwp.io/c4",
148
  "link_blog": "",
149
  "name": "Enable Password Policies",
150
  "summary": "Enable The Password Policies Below",
155
  "section": "section_passwords",
156
  "type": "checkbox",
157
  "default": "Y",
158
+ "link_info": "https://icwp.io/by",
159
  "link_blog": "",
160
  "name": "Prevent Pwned Passwords",
161
  "summary": "Prevent Use Of Pwned Passwords",
254
  }
255
  ],
256
  "definitions": {
257
+ "cron_name_sessionscleanup": "sessionscleanup",
258
  "pwned_api_url_password_single": "https://api.pwnedpasswords.com/pwnedpassword/",
259
  "pwned_api_url_password_range": "https://api.pwnedpasswords.com/range/"
260
  }
src/features/admin_access_restriction.php CHANGED
@@ -247,12 +247,13 @@ class ICWP_WPSF_FeatureHandler_AdminAccessRestriction extends ICWP_WPSF_FeatureH
247
  protected function setSaveUserResponse() {
248
  if ( $this->isAccessKeyRequest() ) {
249
  $bSuccess = $this->doCheckHasPermissionToSubmit();
 
 
250
  if ( $bSuccess ) {
251
- $sMessage = sprintf( _wpsf__( '%s Security Admin key accepted.' ), self::getConn()->getHumanName() );
252
  }
253
  else {
254
- $sMessage = sprintf( _wpsf__( '%s Security Admin key not accepted.' ), self::getConn()
255
- ->getHumanName() );
256
  }
257
  $this->loadAdminNoticesProcessor()
258
  ->addFlashMessage( $sMessage, $bSuccess ? 'updated' : 'error' );
@@ -313,18 +314,20 @@ class ICWP_WPSF_FeatureHandler_AdminAccessRestriction extends ICWP_WPSF_FeatureH
313
  * @return array
314
  */
315
  public function getWhitelabelOptions() {
316
- $sMain = $this->getOpt( 'wl_namemain' );
317
  $sMenu = $this->getOpt( 'wl_namemenu' );
318
  if ( empty( $sMenu ) ) {
319
  $sMenu = $sMain;
320
  }
321
 
322
  return array(
323
- 'name_main' => $sMain,
324
- 'name_menu' => $sMenu,
325
- 'description' => $this->getOpt( 'wl_description' ),
326
- 'url_home' => $this->getOpt( 'wl_homeurl' ),
327
- 'url_icon' => $this->getOpt( 'wl_iconurl' ),
 
 
328
  );
329
  }
330
 
@@ -401,6 +404,7 @@ class ICWP_WPSF_FeatureHandler_AdminAccessRestriction extends ICWP_WPSF_FeatureH
401
  protected function loadStrings_SectionTitles( $aOptionsParams ) {
402
 
403
  $sSectionSlug = $aOptionsParams[ 'slug' ];
 
404
  switch ( $sSectionSlug ) {
405
 
406
  case 'section_enable_plugin_feature_admin_access_restriction' :
@@ -432,11 +436,18 @@ class ICWP_WPSF_FeatureHandler_AdminAccessRestriction extends ICWP_WPSF_FeatureH
432
  break;
433
 
434
  case 'section_whitelabel' :
435
- $sTitle = _wpsf__( 'Shield White Label' );
436
  $aSummary = array(
437
- sprintf( _wpsf__( 'Purpose - %s' ),
 
438
  sprintf( _wpsf__( 'Rename and re-brand the %s plugin for your client site installations.' ),
439
- $this->getConn()->getHumanName() ) ),
 
 
 
 
 
 
440
  );
441
  $sTitleShort = _wpsf__( 'White Label' );
442
  break;
@@ -458,6 +469,7 @@ class ICWP_WPSF_FeatureHandler_AdminAccessRestriction extends ICWP_WPSF_FeatureH
458
  protected function loadStrings_Options( $aOptionsParams ) {
459
 
460
  $sKey = $aOptionsParams[ 'key' ];
 
461
  switch ( $sKey ) {
462
 
463
  case 'enable_admin_access_restriction' :
@@ -533,9 +545,9 @@ class ICWP_WPSF_FeatureHandler_AdminAccessRestriction extends ICWP_WPSF_FeatureH
533
  case 'wl_hide_updates' :
534
  $sName = _wpsf__( 'Hide Updates' );
535
  $sSummary = _wpsf__( 'Hide Plugin Updates From Non-Security Admins' );
536
- $sDescription = _wpsf__( 'Do not show the availability of updates to non-security administrators.' );
537
  break;
538
- case 'wl_namemain' :
539
  $sName = _wpsf__( 'Plugin Name' );
540
  $sSummary = _wpsf__( 'The Name Of The Plugin' );
541
  $sDescription = _wpsf__( 'The name of the plugin that will be displayed to your site users.' );
@@ -545,6 +557,11 @@ class ICWP_WPSF_FeatureHandler_AdminAccessRestriction extends ICWP_WPSF_FeatureH
545
  $sSummary = _wpsf__( 'The Main Menu Title Of The Plugin' );
546
  $sDescription = sprintf( _wpsf__( 'The Main Menu Title Of The Plugin. If left empty, the "%s" will be used.' ), _wpsf__( 'Plugin Name' ) );
547
  break;
 
 
 
 
 
548
  case 'wl_description' :
549
  $sName = _wpsf__( 'Description' );
550
  $sSummary = _wpsf__( 'The Description Of The Plugin' );
@@ -555,10 +572,17 @@ class ICWP_WPSF_FeatureHandler_AdminAccessRestriction extends ICWP_WPSF_FeatureH
555
  $sSummary = _wpsf__( 'Plugin Home Page URL' );
556
  $sDescription = _wpsf__( "When a user clicks the home link for this plugin, this is where they'll be directed." );
557
  break;
558
- case 'wl_iconurl' :
559
- $sName = _wpsf__( 'Icon URL' );
560
- $sSummary = _wpsf__( 'Plugin Icon URL' );
561
- $sDescription = _wpsf__( 'The URL of the icon displayed in the menu and in the admin pages.' );
 
 
 
 
 
 
 
562
  break;
563
 
564
  default:
@@ -576,10 +600,6 @@ class ICWP_WPSF_FeatureHandler_AdminAccessRestriction extends ICWP_WPSF_FeatureH
576
  */
577
  protected function doPrePluginOptionsSave() {
578
 
579
- if ( $this->getOpt( 'admin_access_timeout' ) < 1 ) {
580
- $this->getOptionsVo()->resetOptToDefault( 'admin_access_timeout' );
581
- }
582
-
583
  // Restricting Activate Plugins also means restricting the rest.
584
  $aPluginsRestrictions = $this->getAdminAccessArea_Plugins();
585
  if ( in_array( 'activate_plugins', $aPluginsRestrictions ) ) {
@@ -613,5 +633,12 @@ class ICWP_WPSF_FeatureHandler_AdminAccessRestriction extends ICWP_WPSF_FeatureH
613
  array_unique( array_merge( $aPostRestrictions, array( 'create', 'publish', 'delete' ) ) )
614
  );
615
  }
 
 
 
 
 
 
 
616
  }
617
  }
247
  protected function setSaveUserResponse() {
248
  if ( $this->isAccessKeyRequest() ) {
249
  $bSuccess = $this->doCheckHasPermissionToSubmit();
250
+
251
+ $sPluginName = $this->getConn()->getHumanName();
252
  if ( $bSuccess ) {
253
+ $sMessage = sprintf( _wpsf__( '%s Security Admin key accepted.' ), $sPluginName );
254
  }
255
  else {
256
+ $sMessage = sprintf( _wpsf__( '%s Security Admin key not accepted.' ), $sPluginName );
 
257
  }
258
  $this->loadAdminNoticesProcessor()
259
  ->addFlashMessage( $sMessage, $bSuccess ? 'updated' : 'error' );
314
  * @return array
315
  */
316
  public function getWhitelabelOptions() {
317
+ $sMain = $this->getOpt( 'wl_pluginnamemain' );
318
  $sMenu = $this->getOpt( 'wl_namemenu' );
319
  if ( empty( $sMenu ) ) {
320
  $sMenu = $sMain;
321
  }
322
 
323
  return array(
324
+ 'name_main' => $sMain,
325
+ 'name_menu' => $sMenu,
326
+ 'name_company' => $this->getOpt( 'wl_companyname' ),
327
+ 'description' => $this->getOpt( 'wl_description' ),
328
+ 'url_home' => $this->getOpt( 'wl_homeurl' ),
329
+ 'url_icon' => $this->getOpt( 'wl_menuiconurl' ),
330
+ 'url_dashboardlogourl' => $this->getOpt( 'wl_dashboardlogourl' ),
331
  );
332
  }
333
 
404
  protected function loadStrings_SectionTitles( $aOptionsParams ) {
405
 
406
  $sSectionSlug = $aOptionsParams[ 'slug' ];
407
+ $sPluginName = $this->getConn()->getHumanName();
408
  switch ( $sSectionSlug ) {
409
 
410
  case 'section_enable_plugin_feature_admin_access_restriction' :
436
  break;
437
 
438
  case 'section_whitelabel' :
439
+ $sTitle = _wpsf__( 'White Label' );
440
  $aSummary = array(
441
+ sprintf( '%s - %s',
442
+ _wpsf__( 'Purpose' ),
443
  sprintf( _wpsf__( 'Rename and re-brand the %s plugin for your client site installations.' ),
444
+ $sPluginName )
445
+ ),
446
+ sprintf( '%s - %s',
447
+ _wpsf__( 'Important' ),
448
+ sprintf( _wpsf__( 'The Security Admin system must be active for these settings to apply.' ),
449
+ $sPluginName )
450
+ )
451
  );
452
  $sTitleShort = _wpsf__( 'White Label' );
453
  break;
469
  protected function loadStrings_Options( $aOptionsParams ) {
470
 
471
  $sKey = $aOptionsParams[ 'key' ];
472
+ $sPluginName = $this->getConn()->getHumanName();
473
  switch ( $sKey ) {
474
 
475
  case 'enable_admin_access_restriction' :
545
  case 'wl_hide_updates' :
546
  $sName = _wpsf__( 'Hide Updates' );
547
  $sSummary = _wpsf__( 'Hide Plugin Updates From Non-Security Admins' );
548
+ $sDescription = sprintf( _wpsf__( 'Hide available %s updates from non-security administrators.' ), $sPluginName );
549
  break;
550
+ case 'wl_pluginnamemain' :
551
  $sName = _wpsf__( 'Plugin Name' );
552
  $sSummary = _wpsf__( 'The Name Of The Plugin' );
553
  $sDescription = _wpsf__( 'The name of the plugin that will be displayed to your site users.' );
557
  $sSummary = _wpsf__( 'The Main Menu Title Of The Plugin' );
558
  $sDescription = sprintf( _wpsf__( 'The Main Menu Title Of The Plugin. If left empty, the "%s" will be used.' ), _wpsf__( 'Plugin Name' ) );
559
  break;
560
+ case 'wl_companyname' :
561
+ $sName = _wpsf__( 'Company Name' );
562
+ $sSummary = _wpsf__( 'The Name Of Your Company' );
563
+ $sDescription = _wpsf__( 'Provide the name of your company.' );
564
+ break;
565
  case 'wl_description' :
566
  $sName = _wpsf__( 'Description' );
567
  $sSummary = _wpsf__( 'The Description Of The Plugin' );
572
  $sSummary = _wpsf__( 'Plugin Home Page URL' );
573
  $sDescription = _wpsf__( "When a user clicks the home link for this plugin, this is where they'll be directed." );
574
  break;
575
+ case 'wl_menuiconurl' :
576
+ $sName = _wpsf__( 'Menu Icon' );
577
+ $sSummary = _wpsf__( 'Menu Icon URL' );
578
+ $sDescription = _wpsf__( 'The URL of the icon to display in the menu.' )
579
+ .' '.sprintf( _wpsf__( 'The %s should measure %s.' ), _wpsf__( 'icon' ), '32px x 32px' );
580
+ break;
581
+ case 'wl_dashboardlogourl' :
582
+ $sName = _wpsf__( 'Dashboard Logo' );
583
+ $sSummary = _wpsf__( 'Dashboard Logo URL' );
584
+ $sDescription = _wpsf__( 'The URL of the logo to display in the admin pages.' )
585
+ .' '.sprintf( _wpsf__( 'The %s should measure %s.' ), _wpsf__( 'logo' ), '128px x 128px' );
586
  break;
587
 
588
  default:
600
  */
601
  protected function doPrePluginOptionsSave() {
602
 
 
 
 
 
603
  // Restricting Activate Plugins also means restricting the rest.
604
  $aPluginsRestrictions = $this->getAdminAccessArea_Plugins();
605
  if ( in_array( 'activate_plugins', $aPluginsRestrictions ) ) {
633
  array_unique( array_merge( $aPostRestrictions, array( 'create', 'publish', 'delete' ) ) )
634
  );
635
  }
636
+
637
+ if ( !filter_var( $this->getOpt( 'wl_menuiconurl' ), FILTER_VALIDATE_URL ) ) {
638
+ $this->setOpt( 'wl_menuiconurl', '' );
639
+ }
640
+ if ( !filter_var( $this->getOpt( 'wl_dashboardlogourl' ), FILTER_VALIDATE_URL ) ) {
641
+ $this->setOpt( 'wl_dashboardlogourl', '' );
642
+ }
643
  }
644
  }
src/features/audit_trail.php CHANGED
@@ -162,7 +162,7 @@ class ICWP_WPSF_FeatureHandler_AuditTrail extends ICWP_WPSF_FeatureHandler_BaseW
162
  protected function getContentCustomActionsData() {
163
  $aContexts = array(
164
  'all' => 'All', //special
165
- 'wpsf' => 'Shield',
166
  'wordpress' => 'WordPress',
167
  'users' => 'Users',
168
  'posts' => 'Posts',
@@ -277,7 +277,7 @@ class ICWP_WPSF_FeatureHandler_AuditTrail extends ICWP_WPSF_FeatureHandler_BaseW
277
  ->setColumns( array( 'wp_username' ) )
278
  ->setResultsAsVo( false )
279
  ->all();
280
- $aData[ 'messages' ][] = 'Shield Security Audit Entries deleted';
281
  }
282
  catch ( Exception $oE ) {
283
  }
162
  protected function getContentCustomActionsData() {
163
  $aContexts = array(
164
  'all' => 'All', //special
165
+ 'wpsf' => $this->getConn()->getHumanName(),
166
  'wordpress' => 'WordPress',
167
  'users' => 'Users',
168
  'posts' => 'Posts',
277
  ->setColumns( array( 'wp_username' ) )
278
  ->setResultsAsVo( false )
279
  ->all();
280
+ $aData[ 'messages' ][] = sprintf( '%s Audit Entries deleted', $this->getConn()->getHumanName() );
281
  }
282
  catch ( Exception $oE ) {
283
  }
src/features/autoupdates.php CHANGED
@@ -237,6 +237,7 @@ class ICWP_WPSF_FeatureHandler_Autoupdates extends ICWP_WPSF_FeatureHandler_Base
237
  protected function loadStrings_Options( $aOptionsParams ) {
238
 
239
  $sKey = $aOptionsParams[ 'key' ];
 
240
  switch ( $sKey ) {
241
 
242
  case 'enable_autoupdates' :
@@ -309,7 +310,7 @@ class ICWP_WPSF_FeatureHandler_Autoupdates extends ICWP_WPSF_FeatureHandler_Base
309
  case 'update_delay' :
310
  $sName = _wpsf__( 'Update Delay' );
311
  $sSummary = _wpsf__( 'Delay Automatic Updates For Period Of Stability' );
312
- $sDescription = _wpsf__( 'Shield will delay upgrades until the new update has been available for the set number of days.' )
313
  .'<br />'._wpsf__( "This helps ensure updates are more stable before they're automatically applied to your site." );
314
  break;
315
 
237
  protected function loadStrings_Options( $aOptionsParams ) {
238
 
239
  $sKey = $aOptionsParams[ 'key' ];
240
+ $sPlugName = $this->getConn()->getHumanName();
241
  switch ( $sKey ) {
242
 
243
  case 'enable_autoupdates' :
310
  case 'update_delay' :
311
  $sName = _wpsf__( 'Update Delay' );
312
  $sSummary = _wpsf__( 'Delay Automatic Updates For Period Of Stability' );
313
+ $sDescription = sprintf( _wpsf__( '%s will delay upgrades until the new update has been available for the set number of days.' ), $sPlugName )
314
  .'<br />'._wpsf__( "This helps ensure updates are more stable before they're automatically applied to your site." );
315
  break;
316
 
src/features/base.php CHANGED
@@ -60,6 +60,11 @@ abstract class ICWP_WPSF_FeatureHandler_Base extends ICWP_WPSF_Foundation {
60
  */
61
  protected static $bForceOffFileExists;
62
 
 
 
 
 
 
63
  /**
64
  * @var ICWP_WPSF_FeatureHandler_Email
65
  */
@@ -118,7 +123,7 @@ abstract class ICWP_WPSF_FeatureHandler_Base extends ICWP_WPSF_Foundation {
118
  }
119
 
120
  $nMenuPriority = isset( $aModProps[ 'menu_priority' ] ) ? $aModProps[ 'menu_priority' ] : 100;
121
- add_filter( $this->prefix( 'filter_plugin_submenu_items' ), array( $this, 'filter_addPluginSubMenuItem' ), $nMenuPriority );
122
  add_filter( $this->prefix( 'collect_module_summary_data' ), array( $this, 'addModuleSummaryData' ), $nMenuPriority );
123
  add_filter( $this->prefix( 'collect_notices' ), array( $this, 'addInsightsNoticeData' ) );
124
  add_action( $this->prefix( 'plugin_shutdown' ), array( $this, 'action_doFeatureShutdown' ) );
@@ -255,7 +260,7 @@ abstract class ICWP_WPSF_FeatureHandler_Base extends ICWP_WPSF_Foundation {
255
  if ( !empty( $aPhpReqs ) ) {
256
 
257
  if ( !empty( $aPhpReqs[ 'version' ] ) ) {
258
- $bMeetsReqs = $bMeetsReqs && $this->loadDataProcessor()
259
  ->getPhpVersionIsAtLeast( $aPhpReqs[ 'version' ] );
260
  }
261
  if ( !empty( $aPhpReqs[ 'functions' ] ) && is_array( $aPhpReqs[ 'functions' ] ) ) {
@@ -334,7 +339,9 @@ abstract class ICWP_WPSF_FeatureHandler_Base extends ICWP_WPSF_Foundation {
334
  */
335
  public function onWpInit() {
336
  $this->runWizards();
337
- $this->updateHandler();
 
 
338
 
339
  // GDPR
340
  if ( $this->isPremium() ) {
@@ -567,7 +574,7 @@ abstract class ICWP_WPSF_FeatureHandler_Base extends ICWP_WPSF_Foundation {
567
  * @param array $aItems
568
  * @return array
569
  */
570
- public function filter_addPluginSubMenuItem( $aItems ) {
571
  $sMenuTitleName = $this->getOptionsVo()->getFeatureProperty( 'menu_title' );
572
  if ( is_null( $sMenuTitleName ) ) {
573
  $sMenuTitleName = $this->getMainFeatureName();
@@ -907,6 +914,9 @@ abstract class ICWP_WPSF_FeatureHandler_Base extends ICWP_WPSF_Foundation {
907
  ->setIsPremiumLicensed( $this->isPremium() )
908
  ->setNeedSave( true );
909
  }
 
 
 
910
  $this->store();
911
  }
912
 
@@ -1298,7 +1308,15 @@ abstract class ICWP_WPSF_FeatureHandler_Base extends ICWP_WPSF_Foundation {
1298
  $this->setOpt( $sOptionKey, $sOptionValue );
1299
  }
1300
  }
 
1301
  $this->savePluginOptions();
 
 
 
 
 
 
 
1302
  }
1303
 
1304
  /**
@@ -1481,8 +1499,8 @@ abstract class ICWP_WPSF_FeatureHandler_Base extends ICWP_WPSF_Foundation {
1481
  'has_wizard' => $this->hasWizard(),
1482
  ),
1483
  'hrefs' => array(
1484
- 'go_pro' => 'http://icwp.io/shieldgoprofeature',
1485
- 'goprofooter' => 'http://icwp.io/goprofooter',
1486
  'wizard_link' => $this->getUrl_WizardLanding(),
1487
  'wizard_landing' => $this->getUrl_WizardLanding()
1488
  ),
60
  */
61
  protected static $bForceOffFileExists;
62
 
63
+ /**
64
+ * @var boolean
65
+ */
66
+ protected $bImportExportWhitelistNotify = false;
67
+
68
  /**
69
  * @var ICWP_WPSF_FeatureHandler_Email
70
  */
123
  }
124
 
125
  $nMenuPriority = isset( $aModProps[ 'menu_priority' ] ) ? $aModProps[ 'menu_priority' ] : 100;
126
+ add_filter( $this->prefix( 'submenu_items' ), array( $this, 'supplySubMenuItem' ), $nMenuPriority );
127
  add_filter( $this->prefix( 'collect_module_summary_data' ), array( $this, 'addModuleSummaryData' ), $nMenuPriority );
128
  add_filter( $this->prefix( 'collect_notices' ), array( $this, 'addInsightsNoticeData' ) );
129
  add_action( $this->prefix( 'plugin_shutdown' ), array( $this, 'action_doFeatureShutdown' ) );
260
  if ( !empty( $aPhpReqs ) ) {
261
 
262
  if ( !empty( $aPhpReqs[ 'version' ] ) ) {
263
+ $bMeetsReqs = $bMeetsReqs && $this->loadDP()
264
  ->getPhpVersionIsAtLeast( $aPhpReqs[ 'version' ] );
265
  }
266
  if ( !empty( $aPhpReqs[ 'functions' ] ) && is_array( $aPhpReqs[ 'functions' ] ) ) {
339
  */
340
  public function onWpInit() {
341
  $this->runWizards();
342
+ if ( $this->getIsUpgrading() ) {
343
+ $this->updateHandler();
344
+ }
345
 
346
  // GDPR
347
  if ( $this->isPremium() ) {
574
  * @param array $aItems
575
  * @return array
576
  */
577
+ public function supplySubMenuItem( $aItems ) {
578
  $sMenuTitleName = $this->getOptionsVo()->getFeatureProperty( 'menu_title' );
579
  if ( is_null( $sMenuTitleName ) ) {
580
  $sMenuTitleName = $this->getMainFeatureName();
914
  ->setIsPremiumLicensed( $this->isPremium() )
915
  ->setNeedSave( true );
916
  }
917
+
918
+ // we set the flag that options have been updated. (only use this flag if it's a MANUAL options update)
919
+ $this->bImportExportWhitelistNotify = $this->getOptionsVo()->getNeedSave();
920
  $this->store();
921
  }
922
 
1308
  $this->setOpt( $sOptionKey, $sOptionValue );
1309
  }
1310
  }
1311
+
1312
  $this->savePluginOptions();
1313
+
1314
+ // only use this flag when the options are being updated with a MANUAL save.
1315
+ if ( isset( $this->bImportExportWhitelistNotify ) && $this->bImportExportWhitelistNotify ) {
1316
+ if ( !wp_next_scheduled( $this->prefix( 'importexport_notify' ) ) ) {
1317
+ wp_schedule_single_event( $this->loadDP()->time() + 15, $this->prefix( 'importexport_notify' ) );
1318
+ }
1319
+ }
1320
  }
1321
 
1322
  /**
1499
  'has_wizard' => $this->hasWizard(),
1500
  ),
1501
  'hrefs' => array(
1502
+ 'go_pro' => 'https://icwp.io/shieldgoprofeature',
1503
+ 'goprofooter' => 'https://icwp.io/goprofooter',
1504
  'wizard_link' => $this->getUrl_WizardLanding(),
1505
  'wizard_landing' => $this->getUrl_WizardLanding()
1506
  ),
src/features/base_wpsf.php CHANGED
@@ -43,7 +43,7 @@ class ICWP_WPSF_FeatureHandler_BaseWpsf extends ICWP_WPSF_FeatureHandler_Base {
43
  if ( !is_array( $aConfig ) ) {
44
  $aConfig = array();
45
  }
46
- return array_merge(
47
  array(
48
  'key' => '',
49
  'secret' => '',
@@ -51,6 +51,10 @@ class ICWP_WPSF_FeatureHandler_BaseWpsf extends ICWP_WPSF_FeatureHandler_Base {
51
  ),
52
  $aConfig
53
  );
 
 
 
 
54
  }
55
 
56
  /**
@@ -99,6 +103,13 @@ class ICWP_WPSF_FeatureHandler_BaseWpsf extends ICWP_WPSF_FeatureHandler_Base {
99
  return $aAjaxData;
100
  }
101
 
 
 
 
 
 
 
 
102
  /**
103
  * @param bool $bRenderEmbeddedContent
104
  * @return array
@@ -136,7 +147,7 @@ class ICWP_WPSF_FeatureHandler_BaseWpsf extends ICWP_WPSF_FeatureHandler_Base {
136
  'has_session' => $this->hasSession()
137
  ),
138
  'hrefs' => array(
139
- 'aar_forget_key' => 'http://icwp.io/b5',
140
  )
141
  )
142
  );
43
  if ( !is_array( $aConfig ) ) {
44
  $aConfig = array();
45
  }
46
+ $aConfig = array_merge(
47
  array(
48
  'key' => '',
49
  'secret' => '',
51
  ),
52
  $aConfig
53
  );
54
+ if ( !$this->isPremium() && $aConfig[ 'style' ] != 'light' ) {
55
+ $aConfig[ 'style' ] = 'light'; // hard-coded light style for non-pro
56
+ }
57
+ return $aConfig;
58
  }
59
 
60
  /**
103
  return $aAjaxData;
104
  }
105
 
106
+ /**
107
+ * @return string
108
+ */
109
+ public function getPluginDefaultRecipientAddress() {
110
+ return apply_filters( $this->prefix( 'report_email_address' ), $this->loadWp()->getSiteAdminEmail() );
111
+ }
112
+
113
  /**
114
  * @param bool $bRenderEmbeddedContent
115
  * @return array
147
  'has_session' => $this->hasSession()
148
  ),
149
  'hrefs' => array(
150
+ 'aar_forget_key' => 'https://icwp.io/b5',
151
  )
152
  )
153
  );
src/features/headers.php CHANGED
@@ -46,7 +46,7 @@ class ICWP_WPSF_FeatureHandler_Headers extends ICWP_WPSF_FeatureHandler_BaseWpsf
46
  protected function doExtraSubmitProcessing() {
47
  $aDomains = $this->getCspHosts();
48
  if ( !empty( $aDomains ) && is_array( $aDomains ) ) {
49
- $oDP = $this->loadDataProcessor();
50
  $aValidDomains = array();
51
  foreach ( $aDomains as $sDomain ) {
52
  $bValidDomain = false;
46
  protected function doExtraSubmitProcessing() {
47
  $aDomains = $this->getCspHosts();
48
  if ( !empty( $aDomains ) && is_array( $aDomains ) ) {
49
+ $oDP = $this->loadDP();
50
  $aValidDomains = array();
51
  foreach ( $aDomains as $sDomain ) {
52
  $bValidDomain = false;
src/features/insights.php CHANGED
@@ -44,8 +44,8 @@ class ICWP_WPSF_FeatureHandler_Insights extends ICWP_WPSF_FeatureHandler_BaseWps
44
  'admin_notes_delete' => $this->getAjaxActionData( 'admin_notes_delete' ),
45
  ),
46
  'hrefs' => array(
47
- 'shield_pro_url' => 'http://icwp.io/shieldpro',
48
- 'shield_pro_more_info_url' => 'http://icwp.io/shld1',
49
  ),
50
  'flags' => array(
51
  'has_audit_trail_entries' => !empty( $aRecentAuditTrail ),
@@ -115,7 +115,7 @@ class ICWP_WPSF_FeatureHandler_Insights extends ICWP_WPSF_FeatureHandler_BaseWps
115
 
116
  $bSuccess = false;
117
  if ( !$oMod->getCanAdminNotes() ) {
118
- $sMessage = _wpsf__( 'Sorry, Admin Notes is only available for Shield Pro.' );
119
  }
120
  else if ( empty( $sNote ) ) {
121
  $sMessage = _wpsf__( 'Sorry, but it appears your note was empty.' );
@@ -187,12 +187,15 @@ class ICWP_WPSF_FeatureHandler_Insights extends ICWP_WPSF_FeatureHandler_BaseWps
187
  * @return array
188
  */
189
  protected function getDisplayStrings() {
 
190
  return $this->loadDP()->mergeArraysRecursive(
191
  parent::getDisplayStrings(),
192
  array(
193
- 'recommendation' => ucfirst( _wpsf__( 'recommendation' ) ),
194
- 'suggestion' => ucfirst( _wpsf__( 'suggestion' ) ),
195
-
 
 
196
  )
197
  );
198
  }
@@ -518,7 +521,7 @@ class ICWP_WPSF_FeatureHandler_Insights extends ICWP_WPSF_FeatureHandler_BaseWps
518
  'pro' => array(
519
  'title' => _wpsf__( 'Pro' ),
520
  'val' => $this->isPremium() ? _wpsf__( 'Yes' ) : _wpsf__( 'No' ),
521
- 'tooltip' => _wpsf__( 'Is this site running Shield Pro' )
522
  ),
523
  );
524
  }
@@ -592,7 +595,7 @@ class ICWP_WPSF_FeatureHandler_Insights extends ICWP_WPSF_FeatureHandler_BaseWps
592
  'insights_last_comment_block_at' => _wpsf__( 'Comment SPAM Block' ),
593
  'insights_xml_block_at' => _wpsf__( 'XML-RPC Block' ),
594
  'insights_restapi_block_at' => _wpsf__( 'Anonymous Rest API Block' ),
595
- 'insights_last_transgression_at' => _wpsf__( 'Shield Transgression' ),
596
  'insights_last_ip_block_at' => _wpsf__( 'IP Connection Blocked' ),
597
  );
598
  }
44
  'admin_notes_delete' => $this->getAjaxActionData( 'admin_notes_delete' ),
45
  ),
46
  'hrefs' => array(
47
+ 'shield_pro_url' => 'https://icwp.io/shieldpro',
48
+ 'shield_pro_more_info_url' => 'https://icwp.io/shld1',
49
  ),
50
  'flags' => array(
51
  'has_audit_trail_entries' => !empty( $aRecentAuditTrail ),
115
 
116
  $bSuccess = false;
117
  if ( !$oMod->getCanAdminNotes() ) {
118
+ $sMessage = _wpsf__( 'Sorry, Admin Notes is only available for Pro subscriptions.' );
119
  }
120
  else if ( empty( $sNote ) ) {
121
  $sMessage = _wpsf__( 'Sorry, but it appears your note was empty.' );
187
  * @return array
188
  */
189
  protected function getDisplayStrings() {
190
+ $sName = $this->getConn()->getHumanName();
191
  return $this->loadDP()->mergeArraysRecursive(
192
  parent::getDisplayStrings(),
193
  array(
194
+ 'page_title' => sprintf( _wpsf__( '%s Security Insights' ), $sName ),
195
+ 'recommendation' => ucfirst( _wpsf__( 'recommendation' ) ),
196
+ 'suggestion' => ucfirst( _wpsf__( 'suggestion' ) ),
197
+ 'box_welcome_title' => sprintf( _wpsf__( 'Welcome To %s Security Insights Dashboard' ), $sName ),
198
+ 'box_receve_subtitle' => sprintf( _wpsf__( 'Some of the most recent %s events' ), $sName )
199
  )
200
  );
201
  }
521
  'pro' => array(
522
  'title' => _wpsf__( 'Pro' ),
523
  'val' => $this->isPremium() ? _wpsf__( 'Yes' ) : _wpsf__( 'No' ),
524
+ 'tooltip' => sprintf( _wpsf__( 'Is this site running %s Pro' ), $oConn->getHumanName() )
525
  ),
526
  );
527
  }
595
  'insights_last_comment_block_at' => _wpsf__( 'Comment SPAM Block' ),
596
  'insights_xml_block_at' => _wpsf__( 'XML-RPC Block' ),
597
  'insights_restapi_block_at' => _wpsf__( 'Anonymous Rest API Block' ),
598
+ 'insights_last_transgression_at' => sprintf( _wpsf__( '%s Transgression' ), $this->getConn()->getHumanName() ),
599
  'insights_last_ip_block_at' => _wpsf__( 'IP Connection Blocked' ),
600
  );
601
  }
src/features/license.php CHANGED
@@ -57,8 +57,8 @@ class ICWP_WPSF_FeatureHandler_License extends ICWP_WPSF_FeatureHandler_BaseWpsf
57
  'connection_debug' => $this->getAjaxActionData( 'connection_debug' )
58
  ),
59
  'aHrefs' => array(
60
- 'shield_pro_url' => 'http://icwp.io/shieldpro',
61
- 'shield_pro_more_info_url' => 'http://icwp.io/shld1',
62
  'iframe_url' => $this->getDef( 'landing_page_url' ),
63
  'keyless_cp' => $this->getDef( 'keyless_cp' ),
64
  ),
@@ -562,7 +562,8 @@ class ICWP_WPSF_FeatureHandler_License extends ICWP_WPSF_FeatureHandler_BaseWpsf
562
  * @return boolean
563
  */
564
  public function getIfShowModuleMenuItem() {
565
- return parent::getIfShowModuleMenuItem() && self::getConn()->isPremiumExtensionsEnabled();
 
566
  }
567
 
568
  /**
@@ -643,13 +644,14 @@ class ICWP_WPSF_FeatureHandler_License extends ICWP_WPSF_FeatureHandler_BaseWpsf
643
  */
644
  protected function loadStrings_SectionTitles( $aOptionsParams ) {
645
 
 
646
  switch ( $aOptionsParams[ 'slug' ] ) {
647
 
648
  case 'section_license_options' :
649
  $sTitle = _wpsf__( 'License Options' );
650
  $sTitleShort = _wpsf__( 'License Options' );
651
  $aSummary = array(
652
- sprintf( _wpsf__( 'Purpose - %s' ), _wpsf__( 'Activate Shield Pro Extensions.' ) ),
653
  sprintf( _wpsf__( 'Recommendation - %s' ), _wpsf__( 'TODO.' ) )
654
  );
655
  break;
57
  'connection_debug' => $this->getAjaxActionData( 'connection_debug' )
58
  ),
59
  'aHrefs' => array(
60
+ 'shield_pro_url' => 'https://icwp.io/shieldpro',
61
+ 'shield_pro_more_info_url' => 'https://icwp.io/shld1',
62
  'iframe_url' => $this->getDef( 'landing_page_url' ),
63
  'keyless_cp' => $this->getDef( 'keyless_cp' ),
64
  ),
562
  * @return boolean
563
  */
564
  public function getIfShowModuleMenuItem() {
565
+ return parent::getIfShowModuleMenuItem() && self::getConn()->isPremiumExtensionsEnabled()
566
+ && $this->getConn()->getHasPermissionToManage();
567
  }
568
 
569
  /**
644
  */
645
  protected function loadStrings_SectionTitles( $aOptionsParams ) {
646
 
647
+ $sName = $this->getConn()->getHumanName();
648
  switch ( $aOptionsParams[ 'slug' ] ) {
649
 
650
  case 'section_license_options' :
651
  $sTitle = _wpsf__( 'License Options' );
652
  $sTitleShort = _wpsf__( 'License Options' );
653
  $aSummary = array(
654
+ sprintf( _wpsf__( 'Purpose - %s' ), sprintf( _wpsf__( 'Activate %s Pro Extensions.' ), $sName ) ),
655
  sprintf( _wpsf__( 'Recommendation - %s' ), _wpsf__( 'TODO.' ) )
656
  );
657
  break;
src/features/login_protect.php CHANGED
@@ -62,11 +62,6 @@ class ICWP_WPSF_FeatureHandler_LoginProtect extends ICWP_WPSF_FeatureHandler_Bas
62
  $this->getOptionsVo()->resetOptToDefault( 'login_limit_interval' );
63
  }
64
 
65
- $aTwoFactorAuthRoles = $this->getOpt( 'two_factor_auth_user_roles' );
66
- if ( empty( $aTwoFactorAuthRoles ) || !is_array( $aTwoFactorAuthRoles ) ) {
67
- $this->setOpt( 'two_factor_auth_user_roles', $this->getTwoFactorUserAuthRoles( true ) );
68
- }
69
-
70
  $this->cleanLoginUrlPath();
71
  }
72
 
@@ -75,6 +70,27 @@ class ICWP_WPSF_FeatureHandler_LoginProtect extends ICWP_WPSF_FeatureHandler_Bas
75
  if ( !is_numeric( $nSkipDays ) || $nSkipDays < 0 ) {
76
  $this->getOptionsVo()->resetOptToDefault( 'mfa_skip' );
77
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
78
  }
79
 
80
  /**
@@ -121,7 +137,7 @@ class ICWP_WPSF_FeatureHandler_LoginProtect extends ICWP_WPSF_FeatureHandler_Bas
121
 
122
  $sEmailSubject = _wpsf__( 'Email Sending Verification' );
123
  return $this->getEmailProcessor()
124
- ->sendEmailTo( $sEmail, $sEmailSubject, $aMessage );
125
  }
126
 
127
  /**
@@ -135,6 +151,7 @@ class ICWP_WPSF_FeatureHandler_LoginProtect extends ICWP_WPSF_FeatureHandler_Bas
135
  }
136
 
137
  /**
 
138
  * @return bool
139
  */
140
  public function getIsCheckingUserRegistrations() {
@@ -142,10 +159,51 @@ class ICWP_WPSF_FeatureHandler_LoginProtect extends ICWP_WPSF_FeatureHandler_Bas
142
  }
143
 
144
  /**
145
- * @param boolean $fAsDefaults
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
146
  * @return array
147
  */
148
- protected function getTwoFactorUserAuthRoles( $fAsDefaults = false ) {
149
  $aTwoAuthRoles = array(
150
  'type' => 'multiple_select',
151
  0 => _wpsf__( 'Subscribers' ),
@@ -154,7 +212,7 @@ class ICWP_WPSF_FeatureHandler_LoginProtect extends ICWP_WPSF_FeatureHandler_Bas
154
  3 => _wpsf__( 'Editors' ),
155
  8 => _wpsf__( 'Administrators' )
156
  );
157
- if ( $fAsDefaults ) {
158
  unset( $aTwoAuthRoles[ 'type' ] );
159
  unset( $aTwoAuthRoles[ 0 ] );
160
  return array_keys( $aTwoAuthRoles );
@@ -303,8 +361,8 @@ class ICWP_WPSF_FeatureHandler_LoginProtect extends ICWP_WPSF_FeatureHandler_Bas
303
  /**
304
  * @return bool
305
  */
306
- public function getIsGoogleRecaptchaEnabled() {
307
- return ( $this->getOptIs( 'enable_google_recaptcha_login', 'Y' ) && $this->getIsGoogleRecaptchaReady() );
308
  }
309
 
310
  /**
@@ -332,6 +390,21 @@ class ICWP_WPSF_FeatureHandler_LoginProtect extends ICWP_WPSF_FeatureHandler_Bas
332
  return $sStyle;
333
  }
334
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
335
  /**
336
  * @return bool
337
  */
@@ -354,7 +427,7 @@ class ICWP_WPSF_FeatureHandler_LoginProtect extends ICWP_WPSF_FeatureHandler_Bas
354
  public function setIfCanSendEmail( $bCan ) {
355
  $nCurrentDateAt = $this->getCanSendEmailVerifiedAt();
356
  if ( $bCan ) {
357
- $nDateAt = ( $nCurrentDateAt <= 0 ) ? $this->loadDataProcessor()->time() : $nCurrentDateAt;
358
  }
359
  else {
360
  $nDateAt = 0;
@@ -491,7 +564,7 @@ class ICWP_WPSF_FeatureHandler_LoginProtect extends ICWP_WPSF_FeatureHandler_Bas
491
 
492
  case 'section_brute_force_login_protection' :
493
  $sTitle = _wpsf__( 'Brute Force Login Protection' );
494
- $sTitleShort = _wpsf__( 'Brute Force' );
495
  $aSummary = array(
496
  sprintf( _wpsf__( 'Purpose - %s' ), _wpsf__( 'Blocks brute force hacking attacks against your login and registration pages.' ) ),
497
  sprintf( _wpsf__( 'Recommendation - %s' ), _wpsf__( 'Use of this feature is highly recommend.' ) )
@@ -575,8 +648,10 @@ class ICWP_WPSF_FeatureHandler_LoginProtect extends ICWP_WPSF_FeatureHandler_Bas
575
 
576
  case 'enable_google_recaptcha_login' :
577
  $sName = _wpsf__( 'Google reCAPTCHA' );
578
- $sSummary = _wpsf__( 'Enable Google reCAPTCHA' );
579
- $sDescription = _wpsf__( 'Use Google reCAPTCHA on the login screen.' );
 
 
580
  break;
581
 
582
  case 'google_recaptcha_style_login' :
@@ -585,6 +660,13 @@ class ICWP_WPSF_FeatureHandler_LoginProtect extends ICWP_WPSF_FeatureHandler_Bas
585
  $sDescription = _wpsf__( 'You can choose the reCAPTCHA display format that best suits your site, including the new Invisible Recaptcha' );
586
  break;
587
 
 
 
 
 
 
 
 
588
  case 'enable_login_gasp_check' :
589
  $sName = _wpsf__( 'Bot Protection' );
590
  $sSummary = _wpsf__( 'Protect WP Login From Automated Login Attempts By Bots' );
@@ -593,9 +675,9 @@ class ICWP_WPSF_FeatureHandler_LoginProtect extends ICWP_WPSF_FeatureHandler_Bas
593
  break;
594
 
595
  case 'login_limit_interval' :
596
- $sName = _wpsf__( 'Login Cooldown Interval' );
597
- $sSummary = _wpsf__( 'Limit login attempts to every X seconds' );
598
- $sDescription = _wpsf__( 'WordPress will process only ONE login attempt for every number of seconds specified.' )
599
  .'<br />'._wpsf__( 'Zero (0) turns this off.' )
600
  .' '.sprintf( _wpsf__( 'Default: "%s".' ), $this->getOptionsVo()
601
  ->getOptDefault( 'login_limit_interval' ) );
62
  $this->getOptionsVo()->resetOptToDefault( 'login_limit_interval' );
63
  }
64
 
 
 
 
 
 
65
  $this->cleanLoginUrlPath();
66
  }
67
 
70
  if ( !is_numeric( $nSkipDays ) || $nSkipDays < 0 ) {
71
  $this->getOptionsVo()->resetOptToDefault( 'mfa_skip' );
72
  }
73
+
74
+ $this->updateHandler();
75
+ }
76
+
77
+ /**
78
+ */
79
+ protected function updateHandler() {
80
+
81
+ // v6.8.0: reCAPTCHA options restructure
82
+
83
+ // These can be removed eventually and are used to migrate old recaptcha settings to new structure
84
+ if ( $this->getOpt( 'enable_google_recaptcha_login' ) == 'Y' ) {
85
+ $this->setOpt( 'enable_google_recaptcha_login', $this->getOpt( 'google_recaptcha_style_login' ) );
86
+ }
87
+ if ( $this->getIsCheckingUserRegistrations() ) {
88
+ $this->setOpt( 'bot_protection_locations', array_merge( $this->getBotProtectionLocations(), array(
89
+ 'register',
90
+ 'password'
91
+ ) ) )
92
+ ->setOpt( 'enable_user_register_checking', 'N' );
93
+ }
94
  }
95
 
96
  /**
137
 
138
  $sEmailSubject = _wpsf__( 'Email Sending Verification' );
139
  return $this->getEmailProcessor()
140
+ ->sendEmailWithWrap( $sEmail, $sEmailSubject, $aMessage );
141
  }
142
 
143
  /**
151
  }
152
 
153
  /**
154
+ * @deprecated
155
  * @return bool
156
  */
157
  public function getIsCheckingUserRegistrations() {
159
  }
160
 
161
  /**
162
+ * @return bool
163
+ */
164
+ public function isProtectLogin() {
165
+ return $this->isProtect( 'login' );
166
+ }
167
+
168
+ /**
169
+ * @return bool
170
+ */
171
+ public function isProtectLostPassword() {
172
+ return $this->isProtect( 'password' );
173
+ }
174
+
175
+ /**
176
+ * @return bool
177
+ */
178
+ public function isProtectRegister() {
179
+ return $this->isProtect( 'register' );
180
+ }
181
+
182
+ /**
183
+ * @param string $sLocationKey - see config for keys, e.g. login, register, password, checkout_woo
184
+ * @return bool
185
+ */
186
+ public function isProtect( $sLocationKey ) {
187
+ return in_array( $sLocationKey, $this->getBotProtectionLocations() );
188
+ }
189
+
190
+ /**
191
+ * @return array
192
+ */
193
+ public function getEmail2FaRoles() {
194
+ $aRoles = $this->getOpt( 'two_factor_auth_user_roles', array() );
195
+ if ( empty( $aRoles ) || !is_array( $aRoles ) ) {
196
+ $aRoles = $this->getOptEmailTwoFactorRolesDefaults();
197
+ $this->setOpt( 'two_factor_auth_user_roles', $aRoles );
198
+ }
199
+ return $aRoles;
200
+ }
201
+
202
+ /**
203
+ * @param boolean $bAsOptDefaults
204
  * @return array
205
  */
206
+ protected function getOptEmailTwoFactorRolesDefaults( $bAsOptDefaults = true ) {
207
  $aTwoAuthRoles = array(
208
  'type' => 'multiple_select',
209
  0 => _wpsf__( 'Subscribers' ),
212
  3 => _wpsf__( 'Editors' ),
213
  8 => _wpsf__( 'Administrators' )
214
  );
215
+ if ( $bAsOptDefaults ) {
216
  unset( $aTwoAuthRoles[ 'type' ] );
217
  unset( $aTwoAuthRoles[ 0 ] );
218
  return array_keys( $aTwoAuthRoles );
361
  /**
362
  * @return bool
363
  */
364
+ public function isGoogleRecaptchaEnabled() {
365
+ return ( !$this->getOptIs( 'enable_google_recaptcha_login', 'disabled' ) && $this->getIsGoogleRecaptchaReady() );
366
  }
367
 
368
  /**
390
  return $sStyle;
391
  }
392
 
393
+ /**
394
+ * @return array
395
+ */
396
+ public function getBotProtectionLocations() {
397
+ $aLocs = $this->getOpt( 'bot_protection_locations' );
398
+ return is_array( $aLocs ) ? $aLocs : $this->getOptionsVo()->getOptDefault( 'bot_protection_locations' );
399
+ }
400
+
401
+ /**
402
+ * @return bool
403
+ */
404
+ public function isCooldownEnabled() {
405
+ return $this->getOpt( 'login_limit_interval' ) > 0;
406
+ }
407
+
408
  /**
409
  * @return bool
410
  */
427
  public function setIfCanSendEmail( $bCan ) {
428
  $nCurrentDateAt = $this->getCanSendEmailVerifiedAt();
429
  if ( $bCan ) {
430
+ $nDateAt = ( $nCurrentDateAt <= 0 ) ? $this->loadDP()->time() : $nCurrentDateAt;
431
  }
432
  else {
433
  $nDateAt = 0;
564
 
565
  case 'section_brute_force_login_protection' :
566
  $sTitle = _wpsf__( 'Brute Force Login Protection' );
567
+ $sTitleShort = _wpsf__( 'reCAPTCHA & Bots' );
568
  $aSummary = array(
569
  sprintf( _wpsf__( 'Purpose - %s' ), _wpsf__( 'Blocks brute force hacking attacks against your login and registration pages.' ) ),
570
  sprintf( _wpsf__( 'Recommendation - %s' ), _wpsf__( 'Use of this feature is highly recommend.' ) )
648
 
649
  case 'enable_google_recaptcha_login' :
650
  $sName = _wpsf__( 'Google reCAPTCHA' );
651
+ $sSummary = _wpsf__( 'Protect WordPress Account Access Requests With Google reCAPTCHA' );
652
+ $sDescription = _wpsf__( 'Use Google reCAPTCHA on the user account forms such as login, register, etc.' ).'<br />'
653
+ .sprintf( _wpsf__( 'Use of any theme other than "%s", requires a Pro license.' ), _wpsf__( 'Light Theme' ) )
654
+ .'<br/>'.sprintf( '%s - %s', _wpsf__( 'Note' ), _wpsf__( "You'll need to setup your Google reCAPTCHA API Keys in 'General' settings." ) );
655
  break;
656
 
657
  case 'google_recaptcha_style_login' :
660
  $sDescription = _wpsf__( 'You can choose the reCAPTCHA display format that best suits your site, including the new Invisible Recaptcha' );
661
  break;
662
 
663
+ case 'bot_protection_locations' :
664
+ $sName = _wpsf__( 'Protection Locations' );
665
+ $sSummary = _wpsf__( 'Which Forms Should Be Protected' );
666
+ $sDescription = _wpsf__( 'Choose the forms for which bot protection measures will be deployed.' ).'<br />'
667
+ .sprintf( _wpsf__( 'Note - %s' ), sprintf( _wpsf__( "Use with 3rd party systems such as %s, requires a Pro license." ), 'WooCommerce' ) );
668
+ break;
669
+
670
  case 'enable_login_gasp_check' :
671
  $sName = _wpsf__( 'Bot Protection' );
672
  $sSummary = _wpsf__( 'Protect WP Login From Automated Login Attempts By Bots' );
675
  break;
676
 
677
  case 'login_limit_interval' :
678
+ $sName = _wpsf__( 'Cooldown Period' );
679
+ $sSummary = _wpsf__( 'Limit account access requests to every X seconds' );
680
+ $sDescription = _wpsf__( 'WordPress will process only ONE account access attempt per number of seconds specified.' )
681
  .'<br />'._wpsf__( 'Zero (0) turns this off.' )
682
  .' '.sprintf( _wpsf__( 'Default: "%s".' ), $this->getOptionsVo()
683
  ->getOptDefault( 'login_limit_interval' ) );
src/features/plugin.php CHANGED
@@ -10,7 +10,7 @@ class ICWP_WPSF_FeatureHandler_Plugin extends ICWP_WPSF_FeatureHandler_BaseWpsf
10
 
11
  protected function doPostConstruction() {
12
  add_action( 'deactivate_plugin', array( $this, 'onWpHookDeactivatePlugin' ), 1, 1 );
13
- add_filter( $this->prefix( 'report_email_address' ), array( $this, 'getPluginReportEmail' ) );
14
  add_filter( $this->prefix( 'globally_disabled' ), array( $this, 'filter_IsPluginGloballyDisabled' ) );
15
  add_filter( $this->prefix( 'google_recaptcha_config' ), array( $this, 'supplyGoogleRecaptchaConfig' ), 10, 0 );
16
  $this->setVisitorIp();
@@ -49,15 +49,6 @@ class ICWP_WPSF_FeatureHandler_Plugin extends ICWP_WPSF_FeatureHandler_BaseWpsf
49
  );
50
  }
51
 
52
- /**
53
- * @return ICWP_WPSF_Processor_Plugin_ImportExport
54
- */
55
- protected function getImportProcessor() {
56
- /** @var ICWP_WPSF_Processor_Plugin $oP */
57
- $oP = $this->getProcessor();
58
- return $oP->getSubProcessorImportExport();
59
- }
60
-
61
  /**
62
  * @return bool
63
  */
@@ -313,26 +304,21 @@ class ICWP_WPSF_FeatureHandler_Plugin extends ICWP_WPSF_FeatureHandler_BaseWpsf
313
  * @return $this
314
  */
315
  public function setTrackingLastSentAt() {
316
- return $this->setOpt( 'tracking_last_sent_at', $this->loadDataProcessor()->time() );
317
  }
318
 
319
  /**
320
  * @return bool
321
  */
322
  public function readyToSendTrackingData() {
323
- return ( ( $this->loadDataProcessor()->time() - $this->getTrackingLastSentAt() ) > WEEK_IN_SECONDS );
324
  }
325
 
326
  /**
327
- * @param $sEmail
328
  * @return string
329
  */
330
- public function getPluginReportEmail( $sEmail ) {
331
- $sReportEmail = $this->getOpt( 'block_send_email_address' );
332
- if ( $this->loadDataProcessor()->validEmail( $sReportEmail ) ) {
333
- $sEmail = $sReportEmail;
334
- }
335
- return $sEmail;
336
  }
337
 
338
  /**
@@ -342,11 +328,11 @@ class ICWP_WPSF_FeatureHandler_Plugin extends ICWP_WPSF_FeatureHandler_BaseWpsf
342
 
343
  $nInstalledAt = $this->getPluginInstallationTime();
344
  if ( empty( $nInstalledAt ) || $nInstalledAt <= 0 ) {
345
- $this->setOpt( 'installation_time', $this->loadDataProcessor()->time() );
346
  }
347
 
348
  if ( $this->isTrackingEnabled() && !$this->isTrackingPermissionSet() ) {
349
- $this->setOpt( 'tracking_permission_set_at', $this->loadDataProcessor()->time() );
350
  }
351
 
352
  $this->cleanRecaptchaKey( 'google_recaptcha_site_key' );
@@ -427,6 +413,13 @@ class ICWP_WPSF_FeatureHandler_Plugin extends ICWP_WPSF_FeatureHandler_BaseWpsf
427
  return !empty( $sMaster ) && ( rtrim( $this->loadWp()->getHomeUrl(), '/' ) != $sMaster );
428
  }
429
 
 
 
 
 
 
 
 
430
  /**
431
  * @return int
432
  */
@@ -466,7 +459,7 @@ class ICWP_WPSF_FeatureHandler_Plugin extends ICWP_WPSF_FeatureHandler_BaseWpsf
466
  * @return bool
467
  */
468
  public function isImportExportPermitted() {
469
- return $this->getOptIs( 'importexport_enable', 'Y' );
470
  }
471
 
472
  /**
@@ -577,7 +570,7 @@ class ICWP_WPSF_FeatureHandler_Plugin extends ICWP_WPSF_FeatureHandler_BaseWpsf
577
  $aOptionData = $this->getOptionsVo()->getRawData_SingleOption( 'visitor_address_source' );
578
  $aValueOptions = $aOptionData[ 'value_options' ];
579
 
580
- $oDp = $this->loadDataProcessor();
581
  $aMap = array();
582
  $aEmpties = array();
583
  foreach ( $aValueOptions as $aOptionValue ) {
@@ -675,7 +668,7 @@ class ICWP_WPSF_FeatureHandler_Plugin extends ICWP_WPSF_FeatureHandler_BaseWpsf
675
  * @return bool
676
  */
677
  public function getCanAdminNotes() {
678
- return true||$this->isPremium();
679
  }
680
 
681
  /**
@@ -692,12 +685,12 @@ class ICWP_WPSF_FeatureHandler_Plugin extends ICWP_WPSF_FeatureHandler_BaseWpsf
692
  */
693
  protected function loadStrings_SectionTitles( $aOptionsParams ) {
694
 
695
- $sSectionSlug = $aOptionsParams[ 'slug' ];
696
- switch ( $sSectionSlug ) {
697
 
698
  case 'section_global_security_options' :
699
  $sTitle = _wpsf__( 'Global Security Plugin Disable' );
700
- $sTitleShort = sprintf( _wpsf__( 'Disable %s' ), $this->getConn()->getHumanName() );
701
  break;
702
 
703
  case 'section_defaults' :
@@ -726,9 +719,15 @@ class ICWP_WPSF_FeatureHandler_Plugin extends ICWP_WPSF_FeatureHandler_BaseWpsf
726
  $sTitle = _wpsf__( 'Google' );
727
  $sTitleShort = _wpsf__( 'Google' );
728
  $aSummary = array(
729
- sprintf( _wpsf__( 'Purpose - %s' ), _wpsf__( 'Setup Google reCAPTCHA for use across Shield.' ) ),
730
- sprintf( _wpsf__( 'Recommendation - %s' ), _wpsf__( 'Use of this feature is highly recommend.' ).' '._wpsf__( 'Note: you must create your own Google reCAPTCHA API Keys.' ) ),
731
- sprintf( _wpsf__( 'Note - %s' ), _wpsf__( 'Invisible Google reCAPTCHA is available with Shield Pro.' ) )
 
 
 
 
 
 
732
  );
733
  break;
734
 
@@ -738,7 +737,7 @@ class ICWP_WPSF_FeatureHandler_Plugin extends ICWP_WPSF_FeatureHandler_BaseWpsf
738
  break;
739
 
740
  default:
741
- throw new Exception( sprintf( 'A section slug was defined but with no associated strings. Slug: "%s".', $sSectionSlug ) );
742
  }
743
  $aOptionsParams[ 'title' ] = $sTitle;
744
  $aOptionsParams[ 'summary' ] = ( isset( $aSummary ) && is_array( $aSummary ) ) ? $aSummary : array();
@@ -803,7 +802,7 @@ class ICWP_WPSF_FeatureHandler_Plugin extends ICWP_WPSF_FeatureHandler_BaseWpsf
803
  $sSummary = _wpsf__( 'Display Plugin Badge On Your Site' );
804
  $sDescription = _wpsf__( 'Enabling this option helps support the plugin by spreading the word about it on your website.' )
805
  .' '._wpsf__( 'The plugin badge also lets visitors know your are taking your website security seriously.' )
806
- .sprintf( '<br /><strong><a href="%s" target="_blank">%s</a></strong>', 'http://icwp.io/wpsf20', _wpsf__( 'Read this carefully before enabling this option.' ) );
807
  break;
808
 
809
  case 'delete_on_deactivate' :
@@ -837,7 +836,7 @@ class ICWP_WPSF_FeatureHandler_Plugin extends ICWP_WPSF_FeatureHandler_BaseWpsf
837
  $sName = _wpsf__( 'Master Import Site' );
838
  $sSummary = _wpsf__( 'Automatically Import Options From This Site URL' );
839
  $sDescription = _wpsf__( "Supplying a site URL here will make this site an 'Options Slave'." )
840
- .'<br />'._wpsf__( 'Options will be automatically imported from the Master Import site each day.' )
841
  .'<br />'.sprintf( '%s: %s', _wpsf__( 'Warning' ), _wpsf__( 'Use of this feature will overwrite existing options and replace them with those from the Master Import Site.' ) );
842
  break;
843
 
10
 
11
  protected function doPostConstruction() {
12
  add_action( 'deactivate_plugin', array( $this, 'onWpHookDeactivatePlugin' ), 1, 1 );
13
+ add_filter( $this->prefix( 'report_email_address' ), array( $this, 'supplyPluginReportEmail' ) );
14
  add_filter( $this->prefix( 'globally_disabled' ), array( $this, 'filter_IsPluginGloballyDisabled' ) );
15
  add_filter( $this->prefix( 'google_recaptcha_config' ), array( $this, 'supplyGoogleRecaptchaConfig' ), 10, 0 );
16
  $this->setVisitorIp();
49
  );
50
  }
51
 
 
 
 
 
 
 
 
 
 
52
  /**
53
  * @return bool
54
  */
304
  * @return $this
305
  */
306
  public function setTrackingLastSentAt() {
307
+ return $this->setOpt( 'tracking_last_sent_at', $this->loadDP()->time() );
308
  }
309
 
310
  /**
311
  * @return bool
312
  */
313
  public function readyToSendTrackingData() {
314
+ return ( ( $this->loadDP()->time() - $this->getTrackingLastSentAt() ) > WEEK_IN_SECONDS );
315
  }
316
 
317
  /**
 
318
  * @return string
319
  */
320
+ public function supplyPluginReportEmail() {
321
+ return $this->getOpt( 'block_send_email_address' );
 
 
 
 
322
  }
323
 
324
  /**
328
 
329
  $nInstalledAt = $this->getPluginInstallationTime();
330
  if ( empty( $nInstalledAt ) || $nInstalledAt <= 0 ) {
331
+ $this->setOpt( 'installation_time', $this->loadDP()->time() );
332
  }
333
 
334
  if ( $this->isTrackingEnabled() && !$this->isTrackingPermissionSet() ) {
335
+ $this->setOpt( 'tracking_permission_set_at', $this->loadDP()->time() );
336
  }
337
 
338
  $this->cleanRecaptchaKey( 'google_recaptcha_site_key' );
413
  return !empty( $sMaster ) && ( rtrim( $this->loadWp()->getHomeUrl(), '/' ) != $sMaster );
414
  }
415
 
416
+ /**
417
+ * @return bool
418
+ */
419
+ public function hasImportExportWhitelistSites() {
420
+ return ( count( $this->getImportExportWhitelist() ) > 0 );
421
+ }
422
+
423
  /**
424
  * @return int
425
  */
459
  * @return bool
460
  */
461
  public function isImportExportPermitted() {
462
+ return $this->isPremium() && $this->getOptIs( 'importexport_enable', 'Y' );
463
  }
464
 
465
  /**
570
  $aOptionData = $this->getOptionsVo()->getRawData_SingleOption( 'visitor_address_source' );
571
  $aValueOptions = $aOptionData[ 'value_options' ];
572
 
573
+ $oDp = $this->loadDP();
574
  $aMap = array();
575
  $aEmpties = array();
576
  foreach ( $aValueOptions as $aOptionValue ) {
668
  * @return bool
669
  */
670
  public function getCanAdminNotes() {
671
+ return $this->isPremium();
672
  }
673
 
674
  /**
685
  */
686
  protected function loadStrings_SectionTitles( $aOptionsParams ) {
687
 
688
+ $sName = $this->getConn()->getHumanName();
689
+ switch ( $aOptionsParams[ 'slug' ] ) {
690
 
691
  case 'section_global_security_options' :
692
  $sTitle = _wpsf__( 'Global Security Plugin Disable' );
693
+ $sTitleShort = sprintf( _wpsf__( 'Disable %s' ), $sName );
694
  break;
695
 
696
  case 'section_defaults' :
719
  $sTitle = _wpsf__( 'Google' );
720
  $sTitleShort = _wpsf__( 'Google' );
721
  $aSummary = array(
722
+ sprintf( '%s - %s', _wpsf__( 'Purpose' ), sprintf( _wpsf__( 'Setup Google reCAPTCHA for use across %s.' ), $sName ) ),
723
+ sprintf( '%s - %s',
724
+ _wpsf__( 'Recommendation' ),
725
+ sprintf( _wpsf__( 'Use of this feature is highly recommend.' ).' '
726
+ .sprintf( '%s: %s', _wpsf__( 'Note' ), _wpsf__( 'You must create your own Google reCAPTCHA API Keys.' ) )
727
+ )
728
+ .sprintf( '<br/><a href="%s" target="_blank">%s</a>', 'https://www.google.com/recaptcha/admin', _wpsf__( 'API Keys' ) )
729
+ ),
730
+ sprintf( '%s - %s', _wpsf__( 'Note' ), sprintf( _wpsf__( 'Invisible Google reCAPTCHA is available with %s Pro.' ), $sName ) )
731
  );
732
  break;
733
 
737
  break;
738
 
739
  default:
740
+ throw new Exception( sprintf( 'A section slug was defined but with no associated strings. Slug: "%s".', $aOptionsParams[ 'slug' ] ) );
741
  }
742
  $aOptionsParams[ 'title' ] = $sTitle;
743
  $aOptionsParams[ 'summary' ] = ( isset( $aSummary ) && is_array( $aSummary ) ) ? $aSummary : array();
802
  $sSummary = _wpsf__( 'Display Plugin Badge On Your Site' );
803
  $sDescription = _wpsf__( 'Enabling this option helps support the plugin by spreading the word about it on your website.' )
804
  .' '._wpsf__( 'The plugin badge also lets visitors know your are taking your website security seriously.' )
805
+ .sprintf( '<br /><strong><a href="%s" target="_blank">%s</a></strong>', 'https://icwp.io/wpsf20', _wpsf__( 'Read this carefully before enabling this option.' ) );
806
  break;
807
 
808
  case 'delete_on_deactivate' :
836
  $sName = _wpsf__( 'Master Import Site' );
837
  $sSummary = _wpsf__( 'Automatically Import Options From This Site URL' );
838
  $sDescription = _wpsf__( "Supplying a site URL here will make this site an 'Options Slave'." )
839
+ .'<br />'._wpsf__( 'Options will be automatically exported from the Master site each day.' )
840
  .'<br />'.sprintf( '%s: %s', _wpsf__( 'Warning' ), _wpsf__( 'Use of this feature will overwrite existing options and replace them with those from the Master Import Site.' ) );
841
  break;
842
 
src/features/user_management.php CHANGED
@@ -15,6 +15,9 @@ class ICWP_WPSF_FeatureHandler_UserManagement extends ICWP_WPSF_FeatureHandler_B
15
  return $this->getUserSessionsData();
16
  }
17
 
 
 
 
18
  protected function getUserSessionsData() {
19
  $aActiveSessions = $this->getActiveSessionsData();
20
 
@@ -61,13 +64,58 @@ class ICWP_WPSF_FeatureHandler_UserManagement extends ICWP_WPSF_FeatureHandler_B
61
  }
62
 
63
  /**
 
64
  * @return ICWP_WPSF_SessionVO[]
65
  */
66
- public function getActiveSessionsData() {
 
 
 
 
 
 
 
 
67
  return $this->getSessionsProcessor()
68
  ->queryGetActiveSessions();
69
  }
70
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
71
  /**
72
  * @return bool
73
  */
@@ -77,7 +125,7 @@ class ICWP_WPSF_FeatureHandler_UserManagement extends ICWP_WPSF_FeatureHandler_B
77
 
78
  protected function doExtraSubmitProcessing() {
79
  $sAdminEmail = $this->getOpt( 'enable_admin_login_email_notification' );
80
- if ( !$this->loadDataProcessor()->validEmail( $sAdminEmail ) ) {
81
  $this->getOptionsVo()->resetOptToDefault( 'enable_admin_login_email_notification' );
82
  }
83
 
@@ -135,8 +183,15 @@ class ICWP_WPSF_FeatureHandler_UserManagement extends ICWP_WPSF_FeatureHandler_B
135
  /**
136
  * @return bool
137
  */
138
- public function isSendEmailLoginNotification() {
139
- return $this->loadDP()->validEmail( $this->getOpt( 'enable_admin_login_email_notification' ) );
 
 
 
 
 
 
 
140
  }
141
 
142
  /**
@@ -334,6 +389,12 @@ class ICWP_WPSF_FeatureHandler_UserManagement extends ICWP_WPSF_FeatureHandler_B
334
  .'<br />'._wpsf__( 'No email address - No Notification.' );
335
  break;
336
 
 
 
 
 
 
 
337
  case 'session_timeout_interval' :
338
  $sName = _wpsf__( 'Session Timeout' );
339
  $sSummary = _wpsf__( 'Specify How Many Days After Login To Automatically Force Re-Login' );
15
  return $this->getUserSessionsData();
16
  }
17
 
18
+ /**
19
+ * @return array
20
+ */
21
  protected function getUserSessionsData() {
22
  $aActiveSessions = $this->getActiveSessionsData();
23
 
64
  }
65
 
66
  /**
67
+ * @param bool $bCleanFirst
68
  * @return ICWP_WPSF_SessionVO[]
69
  */
70
+ public function getActiveSessionsData( $bCleanFirst = true ) {
71
+ // we first clean the sessions.
72
+ if ( $bCleanFirst ) {
73
+ /** @var ICWP_WPSF_Processor_UserManagement $oProc */
74
+ $oProc = $this->getProcessor();
75
+ $oProc->getProcessorSessions()
76
+ ->cleanExpiredSessions();
77
+ }
78
+
79
  return $this->getSessionsProcessor()
80
  ->queryGetActiveSessions();
81
  }
82
 
83
+ /**
84
+ * Should have no default email. If no email is set, no notification is sent.
85
+ * @return string
86
+ */
87
+ public function getAdminLoginNotificationEmail() {
88
+ return $this->getOpt( 'enable_admin_login_email_notification', '' );
89
+ }
90
+
91
+ /**
92
+ * @return int
93
+ */
94
+ public function getSessionIdleTimeoutInterval() {
95
+ return $this->getOpt( 'session_idle_timeout_interval' )*HOUR_IN_SECONDS;
96
+ }
97
+
98
+ /**
99
+ * @return int
100
+ */
101
+ public function getSessionTimeoutInterval() {
102
+ return $this->getOpt( 'session_timeout_interval' )*DAY_IN_SECONDS;
103
+ }
104
+
105
+ /**
106
+ * @return bool
107
+ */
108
+ public function hasSessionIdleTimeout() {
109
+ return $this->isModuleEnabled() && ( $this->getSessionIdleTimeoutInterval() > 0 );
110
+ }
111
+
112
+ /**
113
+ * @return bool
114
+ */
115
+ public function hasSessionTimeoutInterval() {
116
+ return $this->isModuleEnabled() && ( $this->getSessionTimeoutInterval() > 0 );
117
+ }
118
+
119
  /**
120
  * @return bool
121
  */
125
 
126
  protected function doExtraSubmitProcessing() {
127
  $sAdminEmail = $this->getOpt( 'enable_admin_login_email_notification' );
128
+ if ( !$this->loadDP()->validEmail( $sAdminEmail ) ) {
129
  $this->getOptionsVo()->resetOptToDefault( 'enable_admin_login_email_notification' );
130
  }
131
 
183
  /**
184
  * @return bool
185
  */
186
+ public function isSendAdminEmailLoginNotification() {
187
+ return $this->loadDP()->validEmail( $this->getAdminLoginNotificationEmail() );
188
+ }
189
+
190
+ /**
191
+ * @return bool
192
+ */
193
+ public function isSendUserEmailLoginNotification() {
194
+ return $this->isPremium() && $this->getOptIs( 'enable_user_login_email_notification', 'Y' );
195
  }
196
 
197
  /**
389
  .'<br />'._wpsf__( 'No email address - No Notification.' );
390
  break;
391
 
392
+ case 'enable_user_login_email_notification' :
393
+ $sName = _wpsf__( 'User Login Notification Email' );
394
+ $sSummary = _wpsf__( 'Send Email Notification To Each User Upon Successful Login' );
395
+ $sDescription = _wpsf__( 'A notification is sent to each user when a successful login occurs for their account.' );
396
+ break;
397
+
398
  case 'session_timeout_interval' :
399
  $sName = _wpsf__( 'Session Timeout' );
400
  $sSummary = _wpsf__( 'Specify How Many Days After Login To Automatically Force Re-Login' );
src/processors/admin_access_restriction.php CHANGED
@@ -129,7 +129,7 @@ class ICWP_WPSF_Processor_AdminAccessRestriction extends ICWP_WPSF_Processor_Bas
129
  }
130
 
131
  $oWpUsers = $this->loadWpUsers();
132
- $oDp = $this->loadDataProcessor();
133
 
134
  /** @var string $sRequestedCapability */
135
  $sRequestedCapability = $aArgs[ 0 ];
@@ -324,9 +324,20 @@ class ICWP_WPSF_Processor_AdminAccessRestriction extends ICWP_WPSF_Processor_Bas
324
 
325
  /** @var ICWP_WPSF_FeatureHandler_AdminAccessRestriction $oFO */
326
  $oFO = $this->getFeature();
 
327
 
328
  /** @var string $sRequestedCapability */
329
  $sRequestedCapability = $aArgs[ 0 ];
 
 
 
 
 
 
 
 
 
 
330
  $aEditCapabilities = array( 'activate_plugins', 'delete_plugins', 'install_plugins', 'update_plugins' );
331
 
332
  if ( in_array( $sRequestedCapability, $aEditCapabilities ) ) {
@@ -450,7 +461,7 @@ class ICWP_WPSF_Processor_AdminAccessRestriction extends ICWP_WPSF_Processor_Bas
450
  'js_snippets' => array(
451
  'options_to_restrict' => "'".implode( "','", $oFO->getOptionsToRestrict() )."'",
452
  ),
453
- 'ajax' => array(
454
  'sec_admin_login_box' => $oFO->getAjaxActionData( 'sec_admin_login_box', true )
455
  )
456
  );
129
  }
130
 
131
  $oWpUsers = $this->loadWpUsers();
132
+ $oDp = $this->loadDP();
133
 
134
  /** @var string $sRequestedCapability */
135
  $sRequestedCapability = $aArgs[ 0 ];
324
 
325
  /** @var ICWP_WPSF_FeatureHandler_AdminAccessRestriction $oFO */
326
  $oFO = $this->getFeature();
327
+ $oDp = $this->loadDP();
328
 
329
  /** @var string $sRequestedCapability */
330
  $sRequestedCapability = $aArgs[ 0 ];
331
+
332
+ // special case for plugin info thickbox for changelog
333
+ $bIsChangelog = defined( 'IFRAME_REQUEST' )
334
+ && ( $sRequestedCapability === 'install_plugins' )
335
+ && ( $oDp->query( 'section' ) == 'changelog' )
336
+ && $oDp->query( 'plugin' );
337
+ if ( $bIsChangelog ) {
338
+ return $aAllCaps;
339
+ }
340
+
341
  $aEditCapabilities = array( 'activate_plugins', 'delete_plugins', 'install_plugins', 'update_plugins' );
342
 
343
  if ( in_array( $sRequestedCapability, $aEditCapabilities ) ) {
461
  'js_snippets' => array(
462
  'options_to_restrict' => "'".implode( "','", $oFO->getOptionsToRestrict() )."'",
463
  ),
464
+ 'ajax' => array(
465
  'sec_admin_login_box' => $oFO->getAjaxActionData( 'sec_admin_login_box', true )
466
  )
467
  );
src/processors/adminaccess_whitelabel.php CHANGED
@@ -13,12 +13,52 @@ class ICWP_WPSF_Processor_AdminAccess_Whitelabel extends ICWP_WPSF_Processor_Bas
13
  public function run() {
14
  /** @var ICWP_WPSF_FeatureHandler_AdminAccessRestriction $oFO */
15
  $oFO = $this->getFeature();
16
- add_filter( $this->getController()->prefix( 'plugin_labels' ), array( $this, 'doRelabelPlugin' ) );
 
17
  add_filter( 'plugin_row_meta', array( $this, 'fRemoveDetailsMetaLink' ), 200, 2 );
18
- if ( $oFO->isWlHideUpdates() && $this->isNeedToHideUpdates() ) {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
19
  add_filter( 'site_transient_update_plugins', array( $this, 'hidePluginUpdatesFromUI' ) );
20
  }
21
- add_action( 'admin_print_footer_scripts-plugin-editor.php', array( $this, 'hideFromPluginEditor' ) );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
22
  }
23
 
24
  public function hideFromPluginEditor() {
@@ -39,13 +79,12 @@ class ICWP_WPSF_Processor_AdminAccess_Whitelabel extends ICWP_WPSF_Processor_Bas
39
 
40
  // these are the old white labelling keys which will be replaced upon final release of white labelling.
41
  $sServiceName = $aWhiteLabels[ 'name_main' ];
42
- if ( !empty( $sServiceName ) ) {
43
- $aPluginLabels[ 'Name' ] = $sServiceName;
44
- $aPluginLabels[ 'Title' ] = $sServiceName;
45
- $aPluginLabels[ 'Author' ] = $sServiceName;
46
- $aPluginLabels[ 'AuthorName' ] = $sServiceName;
47
- $aPluginLabels[ 'MenuTitle' ] = $aWhiteLabels[ 'name_menu' ];
48
- }
49
  $sTagLine = $aWhiteLabels[ 'description' ];
50
  if ( !empty( $sTagLine ) ) {
51
  $aPluginLabels[ 'Description' ] = $sTagLine;
@@ -58,13 +97,14 @@ class ICWP_WPSF_Processor_AdminAccess_Whitelabel extends ICWP_WPSF_Processor_Bas
58
  }
59
 
60
  $sIconUrl = $aWhiteLabels[ 'url_icon' ];
61
- if ( !empty( $sIcon16 ) ) {
62
  $aPluginLabels[ 'icon_url_16x16' ] = $sIconUrl;
 
63
  }
64
 
65
- $sIcon32 = $this->getOption( 'icon_url_32x32' );
66
- if ( !empty( $sIcon32 ) ) {
67
- $aPluginLabels[ 'icon_url_32x32' ] = $sIconUrl;
68
  }
69
 
70
  return $aPluginLabels;
@@ -85,17 +125,14 @@ class ICWP_WPSF_Processor_AdminAccess_Whitelabel extends ICWP_WPSF_Processor_Bas
85
  }
86
 
87
  /**
 
88
  * @param stdClass $oPlugins
89
  * @return stdClass
90
  */
91
  public function hidePluginUpdatesFromUI( $oPlugins ) {
92
- $oCon = $this->getController();
93
-
94
- if ( !$oCon->getHasPermissionToManage() ) {
95
- $sFile = $oCon->getPluginBaseFile();
96
- if ( isset( $oPlugins->response[ $sFile ] ) ) {
97
- unset( $oPlugins->response[ $sFile ] );
98
- }
99
  }
100
  return $oPlugins;
101
  }
@@ -105,7 +142,6 @@ class ICWP_WPSF_Processor_AdminAccess_Whitelabel extends ICWP_WPSF_Processor_Bas
105
  */
106
  private function isNeedToHideUpdates() {
107
  $oWp = $this->loadWp();
108
- return is_admin() && !$oWp->isCron()
109
- && ( in_array( $oWp->getCurrentPage(), array( 'plugins.php', 'update-core.php' ) ) );
110
  }
111
  }
13
  public function run() {
14
  /** @var ICWP_WPSF_FeatureHandler_AdminAccessRestriction $oFO */
15
  $oFO = $this->getFeature();
16
+ add_filter( $this->prefix( 'is_relabelled' ), '__return_true' );
17
+ add_filter( $oFO->prefix( 'plugin_labels' ), array( $this, 'doRelabelPlugin' ) );
18
  add_filter( 'plugin_row_meta', array( $this, 'fRemoveDetailsMetaLink' ), 200, 2 );
19
+ add_action( 'init', array( $this, 'onWpInit' ) );
20
+ add_action( 'admin_print_footer_scripts-plugin-editor.php', array( $this, 'hideFromPluginEditor' ) );
21
+ }
22
+
23
+ public function onWpInit() {
24
+ /** @var ICWP_WPSF_FeatureHandler_AdminAccessRestriction $oFO */
25
+ $oFO = $this->getFeature();
26
+ $oCon = $this->getController();
27
+
28
+ if ( $oFO->isWlHideUpdates() && $this->isNeedToHideUpdates()
29
+ && !$oCon->getHasPermissionToManage() ) {
30
+ $this->hideUpdates();
31
+ }
32
+ }
33
+
34
+ /**
35
+ * Depending on the page, we hide the update data,
36
+ * or we adjust the number of displayed updates counts
37
+ */
38
+ protected function hideUpdates() {
39
+ $sCurrent = $this->loadWp()->getCurrentPage();
40
+ if ( in_array( $sCurrent, array( 'plugins.php', 'update-core.php' ) ) ) {
41
  add_filter( 'site_transient_update_plugins', array( $this, 'hidePluginUpdatesFromUI' ) );
42
  }
43
+ else {
44
+ add_filter( 'wp_get_update_data', array( $this, 'adjustUpdateDataCount' ) );
45
+ }
46
+ }
47
+
48
+ /**
49
+ * Adjusts the available updates count so as not to include Shield updates if they're hidden
50
+ * @param array $aUpdateData
51
+ * @return array
52
+ */
53
+ public function adjustUpdateDataCount( $aUpdateData ) {
54
+
55
+ $sFile = $this->getController()->getPluginBaseFile();
56
+ if ( $this->loadWpPlugins()->isUpdateAvailable( $sFile ) ) {
57
+ $aUpdateData[ 'counts' ][ 'total' ]--;
58
+ $aUpdateData[ 'counts' ][ 'plugins' ]--;
59
+ }
60
+
61
+ return $aUpdateData;
62
  }
63
 
64
  public function hideFromPluginEditor() {
79
 
80
  // these are the old white labelling keys which will be replaced upon final release of white labelling.
81
  $sServiceName = $aWhiteLabels[ 'name_main' ];
82
+ $aPluginLabels[ 'Name' ] = $sServiceName;
83
+ $aPluginLabels[ 'Title' ] = $sServiceName;
84
+ $aPluginLabels[ 'Author' ] = $aWhiteLabels[ 'name_company' ];
85
+ $aPluginLabels[ 'AuthorName' ] = $aWhiteLabels[ 'name_company' ];
86
+ $aPluginLabels[ 'MenuTitle' ] = $aWhiteLabels[ 'name_menu' ];
87
+
 
88
  $sTagLine = $aWhiteLabels[ 'description' ];
89
  if ( !empty( $sTagLine ) ) {
90
  $aPluginLabels[ 'Description' ] = $sTagLine;
97
  }
98
 
99
  $sIconUrl = $aWhiteLabels[ 'url_icon' ];
100
+ if ( !empty( $sIconUrl ) ) {
101
  $aPluginLabels[ 'icon_url_16x16' ] = $sIconUrl;
102
+ $aPluginLabels[ 'icon_url_32x32' ] = $sIconUrl;
103
  }
104
 
105
+ $sLogoUrl = $aWhiteLabels[ 'url_dashboardlogourl' ];
106
+ if ( !empty( $sLogoUrl ) ) {
107
+ $aPluginLabels[ 'icon_url_128x128' ] = $sLogoUrl;
108
  }
109
 
110
  return $aPluginLabels;
125
  }
126
 
127
  /**
128
+ * Hides the update if the page loaded is the plugins page or the updates page.
129
  * @param stdClass $oPlugins
130
  * @return stdClass
131
  */
132
  public function hidePluginUpdatesFromUI( $oPlugins ) {
133
+ $sFile = $this->getController()->getPluginBaseFile();
134
+ if ( isset( $oPlugins->response[ $sFile ] ) ) {
135
+ unset( $oPlugins->response[ $sFile ] );
 
 
 
 
136
  }
137
  return $oPlugins;
138
  }
142
  */
143
  private function isNeedToHideUpdates() {
144
  $oWp = $this->loadWp();
145
+ return is_admin() && !$oWp->isCron();
 
146
  }
147
  }
src/processors/audit_trail.php CHANGED
@@ -25,14 +25,14 @@ class ICWP_WPSF_Processor_AuditTrail extends ICWP_WPSF_BaseDbProcessor {
25
  }
26
 
27
  /**
28
- * @param ICWP_WPSF_FeatureHandler_AuditTrail $oFeatureOptions
29
  */
30
- public function __construct( ICWP_WPSF_FeatureHandler_AuditTrail $oFeatureOptions ) {
31
- parent::__construct( $oFeatureOptions, $oFeatureOptions->getAuditTrailTableName() );
32
  }
33
 
34
- public function action_doFeatureProcessorShutdown() {
35
- parent::action_doFeatureProcessorShutdown();
36
  if ( !$this->getFeature()->isPluginDeleting() ) {
37
  $this->commitAuditTrial();
38
  }
@@ -41,7 +41,7 @@ class ICWP_WPSF_Processor_AuditTrail extends ICWP_WPSF_BaseDbProcessor {
41
  /**
42
  */
43
  public function run() {
44
- if ( !$this->readyToRun() ) {
45
  return;
46
  }
47
 
25
  }
26
 
27
  /**
28
+ * @param ICWP_WPSF_FeatureHandler_AuditTrail $oModCon
29
  */
30
+ public function __construct( ICWP_WPSF_FeatureHandler_AuditTrail $oModCon ) {
31
+ parent::__construct( $oModCon, $oModCon->getAuditTrailTableName() );
32
  }
33
 
34
+ public function onModuleShutdown() {
35
+ parent::onModuleShutdown();
36
  if ( !$this->getFeature()->isPluginDeleting() ) {
37
  $this->commitAuditTrial();
38
  }
41
  /**
42
  */
43
  public function run() {
44
+ if ( !$this->isReadyToRun() ) {
45
  return;
46
  }
47
 
src/processors/audit_trail_auditor_base.php CHANGED
@@ -15,7 +15,7 @@ class ICWP_WPSF_AuditTrail_Auditor_Base extends ICWP_WPSF_Foundation {
15
  * @param string $sWpUsername
16
  */
17
  public function add( $sContext, $sEvent, $nCategory, $sMessage = '', $sWpUsername = '' ) {
18
- $oDp = $this->loadDataProcessor();
19
 
20
  if ( empty( $sWpUsername ) ) {
21
  $oCurrentUser = $this->loadWpUsers()->getCurrentWpUser();
@@ -24,11 +24,11 @@ class ICWP_WPSF_AuditTrail_Auditor_Base extends ICWP_WPSF_Foundation {
24
  $sWpUsername = 'WP Cron';
25
  }
26
  else {
27
- $sWpUsername = 'Unidentified';
28
  }
29
  }
30
  else {
31
- $sWpUsername = $oCurrentUser->get( 'user_login' );
32
  }
33
  }
34
 
15
  * @param string $sWpUsername
16
  */
17
  public function add( $sContext, $sEvent, $nCategory, $sMessage = '', $sWpUsername = '' ) {
18
+ $oDp = $this->loadDP();
19
 
20
  if ( empty( $sWpUsername ) ) {
21
  $oCurrentUser = $this->loadWpUsers()->getCurrentWpUser();
24
  $sWpUsername = 'WP Cron';
25
  }
26
  else {
27
+ $sWpUsername = 'unidentified';
28
  }
29
  }
30
  else {
31
+ $sWpUsername = $oCurrentUser->user_login;
32
  }
33
  }
34
 
src/processors/autoupdates.php CHANGED
@@ -490,7 +490,7 @@ class ICWP_WPSF_Processor_Autoupdates extends ICWP_WPSF_Processor_BaseWpsf {
490
 
491
  $sTitle = sprintf( _wpsf__( "Notice: %s" ), _wpsf__( "Automatic Updates Completed" ) );
492
  $this->getEmailProcessor()
493
- ->sendEmailTo( $this->getOption( 'override_email_address' ), $sTitle, $aEmailContent );
494
  }
495
 
496
  /**
490
 
491
  $sTitle = sprintf( _wpsf__( "Notice: %s" ), _wpsf__( "Automatic Updates Completed" ) );
492
  $this->getEmailProcessor()
493
+ ->sendEmailWithWrap( $this->getOption( 'override_email_address' ), $sTitle, $aEmailContent );
494
  }
495
 
496
  /**
src/processors/base.php CHANGED
@@ -9,7 +9,7 @@ abstract class ICWP_WPSF_Processor_Base extends ICWP_WPSF_Foundation {
9
  /**
10
  * @var ICWP_WPSF_FeatureHandler_Base
11
  */
12
- protected $oFeatureOptions;
13
 
14
  /**
15
  * @var int
@@ -22,17 +22,14 @@ abstract class ICWP_WPSF_Processor_Base extends ICWP_WPSF_Foundation {
22
  protected $aSubProcessors;
23
 
24
  /**
25
- * @param ICWP_WPSF_FeatureHandler_Base $oFeatureOptions
26
  */
27
- public function __construct( $oFeatureOptions ) {
28
- $this->oFeatureOptions = $oFeatureOptions;
29
- add_action( $oFeatureOptions->prefix( 'plugin_shutdown' ), array(
30
- $this,
31
- 'action_doFeatureProcessorShutdown'
32
- ) );
33
- add_action( $oFeatureOptions->prefix( 'generate_admin_notices' ), array( $this, 'autoAddToAdminNotices' ) );
34
  if ( method_exists( $this, 'addToAdminNotices' ) ) {
35
- add_action( $oFeatureOptions->prefix( 'generate_admin_notices' ), array( $this, 'addToAdminNotices' ) );
36
  }
37
  $this->init();
38
  }
@@ -109,9 +106,15 @@ abstract class ICWP_WPSF_Processor_Base extends ICWP_WPSF_Foundation {
109
  return true;
110
  }
111
 
 
 
 
112
  public function action_doFeatureProcessorShutdown() {
113
  }
114
 
 
 
 
115
  /**
116
  */
117
  public function init() {
@@ -120,7 +123,7 @@ abstract class ICWP_WPSF_Processor_Base extends ICWP_WPSF_Foundation {
120
  /**
121
  * @return bool
122
  */
123
- protected function readyToRun() {
124
  return true;
125
  }
126
 
@@ -141,7 +144,7 @@ abstract class ICWP_WPSF_Processor_Base extends ICWP_WPSF_Foundation {
141
  }
142
 
143
  $bCantDismiss = isset( $aNoticeData[ 'notice_attributes' ][ 'can_dismiss' ] )
144
- && !$aNoticeData[ 'notice_attributes' ][ 'can_dismiss' ];
145
 
146
  $oNotices = $this->loadAdminNoticesProcessor();
147
  if ( !$oNotices->isDismissed( $aAttrs[ 'id' ] ) || $bCantDismiss ) {
@@ -187,14 +190,6 @@ abstract class ICWP_WPSF_Processor_Base extends ICWP_WPSF_Foundation {
187
  return str_replace( '_', '-', $this->loadWp()->getLocale() );
188
  }
189
 
190
- /**
191
- * @return mixed
192
- */
193
- public function getPluginDefaultRecipientAddress() {
194
- return apply_filters( $this->getFeature()->prefix( 'report_email_address' ), $this->loadWp()
195
- ->getSiteAdminEmail() );
196
- }
197
-
198
  /**
199
  * @return ICWP_WPSF_Processor_Email
200
  */
@@ -206,7 +201,7 @@ abstract class ICWP_WPSF_Processor_Base extends ICWP_WPSF_Foundation {
206
  * @return ICWP_WPSF_FeatureHandler_Base
207
  */
208
  protected function getFeature() {
209
- return $this->oFeatureOptions;
210
  }
211
 
212
  /**
9
  /**
10
  * @var ICWP_WPSF_FeatureHandler_Base
11
  */
12
+ protected $oModCon;
13
 
14
  /**
15
  * @var int
22
  protected $aSubProcessors;
23
 
24
  /**
25
+ * @param ICWP_WPSF_FeatureHandler_Base $oModCon
26
  */
27
+ public function __construct( $oModCon ) {
28
+ $this->oModCon = $oModCon;
29
+ add_action( $oModCon->prefix( 'plugin_shutdown' ), array( $this, 'onModuleShutdown' ) );
30
+ add_action( $oModCon->prefix( 'generate_admin_notices' ), array( $this, 'autoAddToAdminNotices' ) );
 
 
 
31
  if ( method_exists( $this, 'addToAdminNotices' ) ) {
32
+ add_action( $oModCon->prefix( 'generate_admin_notices' ), array( $this, 'addToAdminNotices' ) );
33
  }
34
  $this->init();
35
  }
106
  return true;
107
  }
108
 
109
+ /**
110
+ * @deprecated remove next release
111
+ */
112
  public function action_doFeatureProcessorShutdown() {
113
  }
114
 
115
+ public function onModuleShutdown() {
116
+ }
117
+
118
  /**
119
  */
120
  public function init() {
123
  /**
124
  * @return bool
125
  */
126
+ public function isReadyToRun() {
127
  return true;
128
  }
129
 
144
  }
145
 
146
  $bCantDismiss = isset( $aNoticeData[ 'notice_attributes' ][ 'can_dismiss' ] )
147
+ && !$aNoticeData[ 'notice_attributes' ][ 'can_dismiss' ];
148
 
149
  $oNotices = $this->loadAdminNoticesProcessor();
150
  if ( !$oNotices->isDismissed( $aAttrs[ 'id' ] ) || $bCantDismiss ) {
190
  return str_replace( '_', '-', $this->loadWp()->getLocale() );
191
  }
192
 
 
 
 
 
 
 
 
 
193
  /**
194
  * @return ICWP_WPSF_Processor_Email
195
  */
201
  * @return ICWP_WPSF_FeatureHandler_Base
202
  */
203
  protected function getFeature() {
204
+ return $this->oModCon;
205
  }
206
 
207
  /**
src/processors/base_plugin.php CHANGED
@@ -127,7 +127,7 @@ class ICWP_WPSF_Processor_BasePlugin extends ICWP_WPSF_Processor_BaseWpsf {
127
  'help' => _wpsf__( 'Dropping support for PHP 5.2 and 5.3' )
128
  ),
129
  'hrefs' => array(
130
- 'help' => 'http://icwp.io/aq',
131
  )
132
  );
133
  $this->insertAdminNotice( $aRenderData );
127
  'help' => _wpsf__( 'Dropping support for PHP 5.2 and 5.3' )
128
  ),
129
  'hrefs' => array(
130
+ 'help' => 'https://icwp.io/aq',
131
  )
132
  );
133
  $this->insertAdminNotice( $aRenderData );
src/processors/base_wpsf.php CHANGED
@@ -54,6 +54,23 @@ abstract class ICWP_WPSF_Processor_BaseWpsf extends ICWP_WPSF_Processor_Base {
54
  return (int)round( ( $this->loadDP()->time() - $nTimeInstalled )/DAY_IN_SECONDS );
55
  }
56
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
57
  /**
58
  * @return bool
59
  */
@@ -88,7 +105,10 @@ abstract class ICWP_WPSF_Processor_BaseWpsf extends ICWP_WPSF_Processor_Base {
88
  ->getGoogleRecaptchaLib( $oFO->getGoogleRecaptchaSecretKey() )
89
  ->verify( $sCaptchaResponse, $this->ip() );
90
  if ( empty( $oResponse ) || !$oResponse->isSuccess() ) {
91
- throw new Exception( _wpsf__( 'Whoops.' ).' '._wpsf__( 'Google reCAPTCHA verification failed.' ), 2 );
 
 
 
92
  }
93
  }
94
  return true;
54
  return (int)round( ( $this->loadDP()->time() - $nTimeInstalled )/DAY_IN_SECONDS );
55
  }
56
 
57
+ /**
58
+ * @param WP_User $oUser
59
+ * @return bool
60
+ */
61
+ protected function isUserSubjectToLoginIntent( $oUser = null ) {
62
+ $oWpUsers = $this->loadWpUsers();
63
+ if ( !is_a( $oUser, 'WP_User' ) ) {
64
+ if ( $oWpUsers->isUserLoggedIn() ) {
65
+ $oUser = $oWpUsers->getCurrentWpUser();
66
+ }
67
+ else {
68
+ return false; // If we can't get a valid WP_User, then always false.
69
+ }
70
+ }
71
+ return apply_filters( $this->prefix( 'user_subject_to_login_intent' ), false, $oUser );
72
+ }
73
+
74
  /**
75
  * @return bool
76
  */
105
  ->getGoogleRecaptchaLib( $oFO->getGoogleRecaptchaSecretKey() )
106
  ->verify( $sCaptchaResponse, $this->ip() );
107
  if ( empty( $oResponse ) || !$oResponse->isSuccess() ) {
108
+ throw new Exception(
109
+ _wpsf__( 'Whoops.' ).' '._wpsf__( 'Google reCAPTCHA verification failed.' )
110
+ .( $this->loadWp()->isAjax() ? ' '._wpsf__( 'Maybe refresh the page and try again.' ) : '' ),
111
+ 2 );
112
  }
113
  }
114
  return true;
src/processors/basedb.php CHANGED
@@ -25,12 +25,12 @@ abstract class ICWP_WPSF_BaseDbProcessor extends ICWP_WPSF_Processor_BaseWpsf {
25
  protected $nAutoExpirePeriod = null;
26
 
27
  /**
28
- * @param ICWP_WPSF_FeatureHandler_Base $oFeatureOptions
29
  * @param string $sTableName
30
  * @throws Exception
31
  */
32
- public function __construct( $oFeatureOptions, $sTableName = null ) {
33
- parent::__construct( $oFeatureOptions );
34
  $this->setTableName( $sTableName );
35
  $this->createCleanupCron();
36
  $this->initializeTable();
@@ -40,8 +40,8 @@ abstract class ICWP_WPSF_BaseDbProcessor extends ICWP_WPSF_Processor_BaseWpsf {
40
  /**
41
  * @return bool
42
  */
43
- protected function readyToRun() {
44
- return ( parent::readyToRun() && $this->getTableExists() );
45
  }
46
 
47
  /**
@@ -280,8 +280,8 @@ abstract class ICWP_WPSF_BaseDbProcessor extends ICWP_WPSF_Processor_BaseWpsf {
280
  * 1 in 10 page loads will clean the databases. This ensures that even if the crons don't run
281
  * correctly, we'll keep it trim.
282
  */
283
- public function action_doFeatureProcessorShutdown() {
284
- parent::action_doFeatureProcessorShutdown();
285
  if ( rand( 1, 10 ) === 1 ) {
286
  $this->cleanupDatabase();
287
  }
25
  protected $nAutoExpirePeriod = null;
26
 
27
  /**
28
+ * @param ICWP_WPSF_FeatureHandler_Base $oModCon
29
  * @param string $sTableName
30
  * @throws Exception
31
  */
32
+ public function __construct( $oModCon, $sTableName = null ) {
33
+ parent::__construct( $oModCon );
34
  $this->setTableName( $sTableName );
35
  $this->createCleanupCron();
36
  $this->initializeTable();
40
  /**
41
  * @return bool
42
  */
43
+ public function isReadyToRun() {
44
+ return ( parent::isReadyToRun() && $this->getTableExists() );
45
  }
46
 
47
  /**
280
  * 1 in 10 page loads will clean the databases. This ensures that even if the crons don't run
281
  * correctly, we'll keep it trim.
282
  */
283
+ public function onModuleShutdown() {
284
+ parent::onModuleShutdown();
285
  if ( rand( 1, 10 ) === 1 ) {
286
  $this->cleanupDatabase();
287
  }
src/processors/comments_filter.php CHANGED
@@ -21,7 +21,7 @@ class ICWP_WPSF_Processor_CommentsFilter extends ICWP_WPSF_Processor_BaseWpsf {
21
  $oBotSpamProcessor->run();
22
  }
23
 
24
- if ( $this->getIsOption( 'enable_comments_human_spam_filter', 'Y' ) && $this->loadWpCommentsProcessor()
25
  ->isCommentPost() ) {
26
  require_once( dirname( __FILE__ ).'/commentsfilter_humanspam.php' );
27
  $oHumanSpamProcessor = new ICWP_WPSF_Processor_CommentsFilter_HumanSpam( $oFO );
21
  $oBotSpamProcessor->run();
22
  }
23
 
24
+ if ( $this->getIsOption( 'enable_comments_human_spam_filter', 'Y' ) && $this->loadWpComments()
25
  ->isCommentPost() ) {
26
  require_once( dirname( __FILE__ ).'/commentsfilter_humanspam.php' );
27
  $oHumanSpamProcessor = new ICWP_WPSF_Processor_CommentsFilter_HumanSpam( $oFO );
src/processors/commentsfilter_antibotspam.php CHANGED
@@ -36,10 +36,10 @@ class ICWP_WPSF_Processor_CommentsFilter_AntiBotSpam extends ICWP_WPSF_BaseDbPro
36
  private $aRawCommentData;
37
 
38
  /**
39
- * @param ICWP_WPSF_FeatureHandler_CommentsFilter $oFeatureOptions
40
  */
41
- public function __construct( ICWP_WPSF_FeatureHandler_CommentsFilter $oFeatureOptions ) {
42
- parent::__construct( $oFeatureOptions, $oFeatureOptions->getCommentsFilterTableName() );
43
  }
44
 
45
  /**
@@ -59,7 +59,7 @@ class ICWP_WPSF_Processor_CommentsFilter_AntiBotSpam extends ICWP_WPSF_BaseDbPro
59
  return $fIfDoCheck;
60
  }
61
 
62
- $oWpComments = $this->loadWpCommentsProcessor();
63
 
64
  // 1st are comments enabled on this post?
65
  $nPostId = $this->getRawCommentData( 'comment_post_ID' );
@@ -79,7 +79,7 @@ class ICWP_WPSF_Processor_CommentsFilter_AntiBotSpam extends ICWP_WPSF_BaseDbPro
79
  /**
80
  */
81
  public function run() {
82
- if ( !$this->readyToRun() ) {
83
  return;
84
  }
85
 
@@ -172,7 +172,7 @@ class ICWP_WPSF_Processor_CommentsFilter_AntiBotSpam extends ICWP_WPSF_BaseDbPro
172
  $sStatKey = '';
173
  $sExplanation = '';
174
 
175
- $oDp = $this->loadDataProcessor();
176
  $sFieldCheckboxName = $oDp->FetchPost( 'cb_nombre' );
177
  $sFieldHoney = $oDp->FetchPost( 'sugar_sweet_email' );
178
  $sFieldCommentToken = $oDp->FetchPost( 'comment_token' );
@@ -231,7 +231,7 @@ class ICWP_WPSF_Processor_CommentsFilter_AntiBotSpam extends ICWP_WPSF_BaseDbPro
231
  }
232
  else if ( function_exists( 'WPWall_Init' ) ) {
233
  // Compatibility with shoutbox WP Wall Plugin http://wordpress.org/plugins/wp-wall/
234
- if ( !is_null( $this->loadDataProcessor()->FetchPost( 'submit_wall_post' ) ) ) {
235
  $bCheck = false;
236
  }
237
  }
@@ -244,7 +244,7 @@ class ICWP_WPSF_Processor_CommentsFilter_AntiBotSpam extends ICWP_WPSF_BaseDbPro
244
  */
245
  protected function getUniqueFormId() {
246
  if ( !isset( $this->sFormId ) ) {
247
- $oDp = $this->loadDataProcessor();
248
  $sId = $oDp->GenerateRandomLetter().$oDp->GenerateRandomString( rand( 7, 23 ), 7 );
249
  $this->sFormId = preg_replace(
250
  '#[^a-zA-Z0-9]#', '',
36
  private $aRawCommentData;
37
 
38
  /**
39
+ * @param ICWP_WPSF_FeatureHandler_CommentsFilter $oModCon
40
  */
41
+ public function __construct( ICWP_WPSF_FeatureHandler_CommentsFilter $oModCon ) {
42
+ parent::__construct( $oModCon, $oModCon->getCommentsFilterTableName() );
43
  }
44
 
45
  /**
59
  return $fIfDoCheck;
60
  }
61
 
62
+ $oWpComments = $this->loadWpComments();
63
 
64
  // 1st are comments enabled on this post?
65
  $nPostId = $this->getRawCommentData( 'comment_post_ID' );
79
  /**
80
  */
81
  public function run() {
82
+ if ( !$this->isReadyToRun() ) {
83
  return;
84
  }
85
 
172
  $sStatKey = '';
173
  $sExplanation = '';
174
 
175
+ $oDp = $this->loadDP();
176
  $sFieldCheckboxName = $oDp->FetchPost( 'cb_nombre' );
177
  $sFieldHoney = $oDp->FetchPost( 'sugar_sweet_email' );
178
  $sFieldCommentToken = $oDp->FetchPost( 'comment_token' );
231
  }
232
  else if ( function_exists( 'WPWall_Init' ) ) {
233
  // Compatibility with shoutbox WP Wall Plugin http://wordpress.org/plugins/wp-wall/
234
+ if ( !is_null( $this->loadDP()->FetchPost( 'submit_wall_post' ) ) ) {
235
  $bCheck = false;
236
  }
237
  }
244
  */
245
  protected function getUniqueFormId() {
246
  if ( !isset( $this->sFormId ) ) {
247
+ $oDp = $this->loadDP();
248
  $sId = $oDp->GenerateRandomLetter().$oDp->GenerateRandomString( rand( 7, 23 ), 7 );
249
  $this->sFormId = preg_replace(
250
  '#[^a-zA-Z0-9]#', '',
src/processors/commentsfilter_googlerecaptcha.php CHANGED
@@ -25,7 +25,7 @@ class ICWP_WPSF_Processor_CommentsFilter_GoogleRecaptcha extends ICWP_WPSF_Proce
25
  * The WP Query is alive and well at this stage so we can assume certain data is available.
26
  */
27
  public function setup() {
28
- if ( $this->loadWpCommentsProcessor()->isCommentsOpen() ) {
29
  add_action( 'wp_enqueue_scripts', array( $this, 'registerGoogleRecaptchaJs' ), 99 );
30
  add_action( 'comment_form_after_fields', array( $this, 'printGoogleRecaptchaCheck' ) );
31
  }
25
  * The WP Query is alive and well at this stage so we can assume certain data is available.
26
  */
27
  public function setup() {
28
+ if ( $this->loadWpComments()->isCommentsOpen() ) {
29
  add_action( 'wp_enqueue_scripts', array( $this, 'registerGoogleRecaptchaJs' ), 99 );
30
  add_action( 'comment_form_after_fields', array( $this, 'printGoogleRecaptchaCheck' ) );
31
  }
src/processors/commentsfilter_humanspam.php CHANGED
@@ -26,7 +26,7 @@ class ICWP_WPSF_Processor_CommentsFilter_HumanSpam extends ICWP_WPSF_Processor_C
26
  return $fIfDoCheck;
27
  }
28
 
29
- $oWpComments = $this->loadWpCommentsProcessor();
30
 
31
  // 1st are comments enabled on this post?
32
  $nPostId = $this->getRawCommentData( 'comment_post_ID' );
@@ -77,7 +77,7 @@ class ICWP_WPSF_Processor_CommentsFilter_HumanSpam extends ICWP_WPSF_Processor_C
77
  $aCommentData[ 'comment_author_url' ],
78
  $aCommentData[ 'comment_content' ],
79
  $this->ip(),
80
- substr( $this->loadDataProcessor()->FetchServer( 'HTTP_USER_AGENT', '' ), 0, 254 )
81
  );
82
  }
83
 
26
  return $fIfDoCheck;
27
  }
28
 
29
+ $oWpComments = $this->loadWpComments();
30
 
31
  // 1st are comments enabled on this post?
32
  $nPostId = $this->getRawCommentData( 'comment_post_ID' );
77
  $aCommentData[ 'comment_author_url' ],
78
  $aCommentData[ 'comment_content' ],
79
  $this->ip(),
80
+ substr( $this->loadDP()->FetchServer( 'HTTP_USER_AGENT', '' ), 0, 254 )
81
  );
82
  }
83
 
src/processors/cronbase.php CHANGED
@@ -32,7 +32,9 @@ abstract class ICWP_WPSF_Processor_CronBase extends ICWP_WPSF_Processor_BaseWpsf
32
  * @return string
33
  */
34
  protected function getCronRecurrence() {
35
- return $this->prefix( sprintf( 'per-day-%s', $this->getCronFrequency() ) );
 
 
36
  }
37
 
38
  /**
@@ -41,9 +43,11 @@ abstract class ICWP_WPSF_Processor_CronBase extends ICWP_WPSF_Processor_BaseWpsf
41
  abstract protected function getCronCallback();
42
 
43
  /**
44
- * @return int
45
  */
46
- abstract protected function getCronFrequency();
 
 
47
 
48
  /**
49
  * @return string
32
  * @return string
33
  */
34
  protected function getCronRecurrence() {
35
+ $sFreq = $this->getCronFrequency();
36
+ $aStdIntervals = array_keys( wp_get_schedules() );
37
+ return in_array( $sFreq, $aStdIntervals ) ? $sFreq : $this->prefix( sprintf( 'per-day-%s', $sFreq ) );
38
  }
39
 
40
  /**
43
  abstract protected function getCronCallback();
44
 
45
  /**
46
+ * @return int|string
47
  */
48
+ protected function getCronFrequency() {
49
+ return 'daily';
50
+ }
51
 
52
  /**
53
  * @return string
src/processors/email.php CHANGED
@@ -10,8 +10,6 @@ class ICWP_WPSF_Processor_Email extends ICWP_WPSF_Processor_BaseWpsf {
10
 
11
  const Slug = 'email';
12
 
13
- protected $m_sRecipientAddress;
14
-
15
  /**
16
  * @var string
17
  */
@@ -43,10 +41,10 @@ class ICWP_WPSF_Processor_Email extends ICWP_WPSF_Processor_BaseWpsf {
43
  protected $bEmailIsThrottled;
44
 
45
  /**
46
- * @param ICWP_WPSF_FeatureHandler_Email $oFeatureOptions
47
  */
48
- public function __construct( ICWP_WPSF_FeatureHandler_Email $oFeatureOptions ) {
49
- parent::__construct( $oFeatureOptions );
50
  }
51
 
52
  public function init() {
@@ -86,27 +84,38 @@ class ICWP_WPSF_Processor_Email extends ICWP_WPSF_Processor_BaseWpsf {
86
  }
87
 
88
  /**
 
89
  * @param string $sAddress
90
  * @param string $sSubject
91
  * @param array $aMessage
92
  * @return boolean
93
- * @uses wp_mail
94
  */
95
- public function sendEmailTo( $sAddress = '', $sSubject = '', $aMessage = array() ) {
 
 
 
 
 
 
96
 
 
 
 
 
 
 
 
 
97
  $this->updateEmailThrottle();
98
- // We make it appear to have "succeeded" if the throttle is applied.
99
  if ( $this->bEmailIsThrottled ) {
100
  return true;
101
  }
102
 
103
- $aMessage = array_merge( $this->getEmailHeader(), $aMessage, $this->getEmailFooter() );
104
-
105
  $this->emailFilters( true );
106
  $bSuccess = wp_mail(
107
  $this->verifyEmailAddress( $sAddress ),
108
  wp_specialchars_decode( sprintf( '[%s] %s', $this->loadWp()->getSiteName(), $sSubject ) ),
109
- '<html>'.implode( "<br />", $aMessage ).'</html>'
110
  );
111
  $this->emailFilters( false );
112
 
@@ -185,7 +194,7 @@ class ICWP_WPSF_Processor_Email extends ICWP_WPSF_Processor_BaseWpsf {
185
  * @return boolean
186
  */
187
  public function sendEmail( $sEmailSubject, $aMessage ) {
188
- return $this->sendEmailTo( null, $sEmailSubject, $aMessage );
189
  }
190
 
191
  /**
@@ -246,17 +255,12 @@ class ICWP_WPSF_Processor_Email extends ICWP_WPSF_Processor_BaseWpsf {
246
  }
247
  }
248
 
249
- public function setDefaultRecipientAddress( $insEmailAddress ) {
250
- $this->m_sRecipientAddress = $insEmailAddress;
251
- }
252
-
253
  /**
254
- * @param string $sEmailAddress
255
  * @return string
256
  */
257
- public function verifyEmailAddress( $sEmailAddress = '' ) {
258
- return $this->loadDP()
259
- ->validEmail( $sEmailAddress ) ? $sEmailAddress : $this->getPluginDefaultRecipientAddress();
260
  }
261
 
262
  public function getThrottleLimit() {
10
 
11
  const Slug = 'email';
12
 
 
 
13
  /**
14
  * @var string
15
  */
41
  protected $bEmailIsThrottled;
42
 
43
  /**
44
+ * @param ICWP_WPSF_FeatureHandler_Email $oModCon
45
  */
46
+ public function __construct( ICWP_WPSF_FeatureHandler_Email $oModCon ) {
47
+ parent::__construct( $oModCon );
48
  }
49
 
50
  public function init() {
84
  }
85
 
86
  /**
87
+ * Wraps up a message with header and footer
88
  * @param string $sAddress
89
  * @param string $sSubject
90
  * @param array $aMessage
91
  * @return boolean
 
92
  */
93
+ public function sendEmailWithWrap( $sAddress = '', $sSubject = '', $aMessage = array() ) {
94
+ return $this->send(
95
+ $sAddress,
96
+ $sSubject,
97
+ '<html>'.implode( "<br />", array_merge( $this->getEmailHeader(), $aMessage, $this->getEmailFooter() ) ).'</html>'
98
+ );
99
+ }
100
 
101
+ /**
102
+ * @uses wp_mail
103
+ * @param string $sAddress
104
+ * @param string $sSubject
105
+ * @param string $sMessageBody
106
+ * @return bool
107
+ */
108
+ public function send( $sAddress = '', $sSubject = '', $sMessageBody = '' ) {
109
  $this->updateEmailThrottle();
 
110
  if ( $this->bEmailIsThrottled ) {
111
  return true;
112
  }
113
 
 
 
114
  $this->emailFilters( true );
115
  $bSuccess = wp_mail(
116
  $this->verifyEmailAddress( $sAddress ),
117
  wp_specialchars_decode( sprintf( '[%s] %s', $this->loadWp()->getSiteName(), $sSubject ) ),
118
+ $sMessageBody
119
  );
120
  $this->emailFilters( false );
121
 
194
  * @return boolean
195
  */
196
  public function sendEmail( $sEmailSubject, $aMessage ) {
197
+ return $this->sendEmailWithWrap( null, $sEmailSubject, $aMessage );
198
  }
199
 
200
  /**
255
  }
256
  }
257
 
 
 
 
 
258
  /**
259
+ * @param string $sEmail
260
  * @return string
261
  */
262
+ public function verifyEmailAddress( $sEmail = '' ) {
263
+ return $this->loadDP()->validEmail( $sEmail ) ? $sEmail : $this->loadWp()->getSiteAdminEmail();
 
264
  }
265
 
266
  public function getThrottleLimit() {
src/processors/firewall.php CHANGED
@@ -63,7 +63,7 @@ class ICWP_WPSF_Processor_Firewall extends ICWP_WPSF_Processor_BaseWpsf {
63
  */
64
  protected function getIfPerformFirewallScan() {
65
  $bPerformScan = true;
66
- $oDp = $this->loadDataProcessor();
67
 
68
  if ( count( $this->getRawRequestParams() ) == 0 ) {
69
  $bPerformScan = false;
@@ -277,7 +277,7 @@ class ICWP_WPSF_Processor_Firewall extends ICWP_WPSF_Processor_BaseWpsf {
277
 
278
  if ( $this->getIsOption( 'block_send_email', 'Y' ) ) {
279
 
280
- $sRecipient = $this->getPluginDefaultRecipientAddress();
281
  if ( $this->sendBlockEmail( $sRecipient ) ) {
282
  $this->addToAuditEntry( sprintf( _wpsf__( 'Successfully sent Firewall Block email alert to: %s' ), $sRecipient ) );
283
  }
@@ -361,7 +361,7 @@ class ICWP_WPSF_Processor_Firewall extends ICWP_WPSF_Processor_BaseWpsf {
361
  return $this->aPageParams;
362
  }
363
 
364
- $oDp = $this->loadDataProcessor();
365
  $this->aPageParams = $this->getRawRequestParams();
366
  $aWhitelistPages = $this->getWhitelistPages();
367
  $aRequestUriParts = $oDp->getRequestUriParts();
@@ -411,7 +411,7 @@ class ICWP_WPSF_Processor_Firewall extends ICWP_WPSF_Processor_BaseWpsf {
411
  */
412
  protected function getRawRequestParams() {
413
  if ( !isset( $this->aRawRequestParams ) ) {
414
- $this->aRawRequestParams = $this->loadDataProcessor()
415
  ->getRawRequestParams( $this->getIsOption( 'include_cookie_checks', 'Y' ) );
416
  }
417
  return $this->aRawRequestParams;
@@ -477,7 +477,7 @@ class ICWP_WPSF_Processor_Firewall extends ICWP_WPSF_Processor_BaseWpsf {
477
  $sEmailSubject = _wpsf__( 'Firewall Block Alert' );
478
 
479
  return $this->getEmailProcessor()
480
- ->sendEmailTo( $sRecipient, $sEmailSubject, $aMessage );
481
  }
482
 
483
  /**
63
  */
64
  protected function getIfPerformFirewallScan() {
65
  $bPerformScan = true;
66
+ $oDp = $this->loadDP();
67
 
68
  if ( count( $this->getRawRequestParams() ) == 0 ) {
69
  $bPerformScan = false;
277
 
278
  if ( $this->getIsOption( 'block_send_email', 'Y' ) ) {
279
 
280
+ $sRecipient = $oFO->getPluginDefaultRecipientAddress();
281
  if ( $this->sendBlockEmail( $sRecipient ) ) {
282
  $this->addToAuditEntry( sprintf( _wpsf__( 'Successfully sent Firewall Block email alert to: %s' ), $sRecipient ) );
283
  }
361
  return $this->aPageParams;
362
  }
363
 
364
+ $oDp = $this->loadDP();
365
  $this->aPageParams = $this->getRawRequestParams();
366
  $aWhitelistPages = $this->getWhitelistPages();
367
  $aRequestUriParts = $oDp->getRequestUriParts();
411
  */
412
  protected function getRawRequestParams() {
413
  if ( !isset( $this->aRawRequestParams ) ) {
414
+ $this->aRawRequestParams = $this->loadDP()
415
  ->getRawRequestParams( $this->getIsOption( 'include_cookie_checks', 'Y' ) );
416
  }
417
  return $this->aRawRequestParams;
477
  $sEmailSubject = _wpsf__( 'Firewall Block Alert' );
478
 
479
  return $this->getEmailProcessor()
480
+ ->sendEmailWithWrap( $sRecipient, $sEmailSubject, $aMessage );
481
  }
482
 
483
  /**
src/processors/hack_protect.php CHANGED
@@ -15,7 +15,7 @@ class ICWP_WPSF_Processor_HackProtect extends ICWP_WPSF_Processor_BaseWpsf {
15
  /** @var ICWP_WPSF_FeatureHandler_HackProtect $oFO */
16
  $oFO = $this->getFeature();
17
 
18
- $sPath = $this->loadDataProcessor()->getRequestPath();
19
  if ( !empty( $sPath ) && ( strpos( $sPath, '/wp-admin/admin-ajax.php' ) !== false ) ) {
20
  $this->revSliderPatch_LFI();
21
  $this->revSliderPatch_AFU();
@@ -160,7 +160,7 @@ class ICWP_WPSF_Processor_HackProtect extends ICWP_WPSF_Processor_BaseWpsf {
160
  }
161
 
162
  protected function revSliderPatch_LFI() {
163
- $oDp = $this->loadDataProcessor();
164
 
165
  $sAction = $oDp->query( 'action', '' );
166
  $sFileExt = strtolower( $oDp->getExtension( $oDp->query( 'img', '' ) ) );
@@ -172,7 +172,7 @@ class ICWP_WPSF_Processor_HackProtect extends ICWP_WPSF_Processor_BaseWpsf {
172
  }
173
 
174
  protected function revSliderPatch_AFU() {
175
- $oDp = $this->loadDataProcessor();
176
 
177
  $sAction = strtolower( $oDp->request( 'action', '' ) );
178
  $sClientAction = strtolower( $oDp->request( 'client_action', '' ) );
15
  /** @var ICWP_WPSF_FeatureHandler_HackProtect $oFO */
16
  $oFO = $this->getFeature();
17
 
18
+ $sPath = $this->loadDP()->getRequestPath();
19
  if ( !empty( $sPath ) && ( strpos( $sPath, '/wp-admin/admin-ajax.php' ) !== false ) ) {
20
  $this->revSliderPatch_LFI();
21
  $this->revSliderPatch_AFU();
160
  }
161
 
162
  protected function revSliderPatch_LFI() {
163
+ $oDp = $this->loadDP();
164
 
165
  $sAction = $oDp->query( 'action', '' );
166
  $sFileExt = strtolower( $oDp->getExtension( $oDp->query( 'img', '' ) ) );
172
  }
173
 
174
  protected function revSliderPatch_AFU() {
175
+ $oDp = $this->loadDP();
176
 
177
  $sAction = strtolower( $oDp->request( 'action', '' ) );
178
  $sClientAction = strtolower( $oDp->request( 'client_action', '' ) );
src/processors/hackprotect_corechecksumscan.php CHANGED
@@ -253,18 +253,21 @@ class ICWP_WPSF_Processor_HackProtect_CoreChecksumScan extends ICWP_WPSF_Process
253
  $oFO->canRunWizards() ? $this->buildEmailBody( $aFiles ) : $this->buildEmailBody_Legacy( $aFiles )
254
  );
255
 
256
- $aContent[] = '';
257
- $aContent[] = '[ <a href="http://icwp.io/moreinfochecksum">'._wpsf__( 'More Info On This Scanner' ).' ]</a>';
 
 
258
 
 
259
  $this->getEmailProcessor()
260
- ->sendEmailTo(
261
- $this->getPluginDefaultRecipientAddress(),
262
  sprintf( _wpsf__( 'Warning - %s' ), _wpsf__( 'Modified Core WordPress Files Discovered' ) ),
263
  $aContent
264
  );
265
 
266
  $this->addToAuditEntry(
267
- sprintf( _wpsf__( 'Sent Checksum Scan Notification email alert to: %s' ), $this->getPluginDefaultRecipientAddress() )
268
  );
269
  }
270
 
@@ -338,7 +341,7 @@ class ICWP_WPSF_Processor_HackProtect_CoreChecksumScan extends ICWP_WPSF_Process
338
  else {
339
  $aContent[] = _wpsf__( 'You should review these files and replace them with official versions if required.' );
340
  $aContent[] = _wpsf__( 'Alternatively you can have the plugin attempt to repair/replace these files automatically.' )
341
- .' [<a href="http://icwp.io/moreinfochecksum">'._wpsf__( 'More Info' ).']</a>';
342
  }
343
 
344
  return $aContent;
253
  $oFO->canRunWizards() ? $this->buildEmailBody( $aFiles ) : $this->buildEmailBody_Legacy( $aFiles )
254
  );
255
 
256
+ if ( !$oFO->getConn()->isRelabelled() ) {
257
+ $aContent[] = '';
258
+ $aContent[] = '[ <a href="https://icwp.io/moreinfochecksum">'._wpsf__( 'More Info On This Scanner' ).' ]</a>';
259
+ }
260
 
261
+ $sTo = $oFO->getPluginDefaultRecipientAddress();
262
  $this->getEmailProcessor()
263
+ ->sendEmailWithWrap(
264
+ $sTo,
265
  sprintf( _wpsf__( 'Warning - %s' ), _wpsf__( 'Modified Core WordPress Files Discovered' ) ),
266
  $aContent
267
  );
268
 
269
  $this->addToAuditEntry(
270
+ sprintf( _wpsf__( 'Sent Checksum Scan Notification email alert to: %s' ), $sTo )
271
  );
272
  }
273
 
341
  else {
342
  $aContent[] = _wpsf__( 'You should review these files and replace them with official versions if required.' );
343
  $aContent[] = _wpsf__( 'Alternatively you can have the plugin attempt to repair/replace these files automatically.' )
344
+ .' [<a href="https://icwp.io/moreinfochecksum">'._wpsf__( 'More Info' ).']</a>';
345
  }
346
 
347
  return $aContent;
src/processors/hackprotect_filecleanerscan.php CHANGED
@@ -237,18 +237,21 @@ class ICWP_WPSF_Processor_HackProtect_FileCleanerScan extends ICWP_WPSF_Processo
237
  $oFO->canRunWizards() ? $this->buildEmailBody( $aFiles ) : $this->buildEmailBody_Legacy( $aFiles )
238
  );
239
 
240
- $aContent[] = '';
241
- $aContent[] = '[ <a href="http://icwp.io/moreinfochecksum">'._wpsf__( 'More Info On This Scanner' ).' ]</a>';
 
 
242
 
 
243
  $this->getEmailProcessor()
244
- ->sendEmailTo(
245
- $this->getPluginDefaultRecipientAddress(),
246
  sprintf( _wpsf__( 'Warning - %s' ), _wpsf__( 'Unrecognised WordPress Files Detected' ) ),
247
  $aContent
248
  );
249
 
250
  $this->addToAuditEntry(
251
- sprintf( _wpsf__( 'Sent Unrecognised File Scan Notification email alert to: %s' ), $this->getPluginDefaultRecipientAddress() )
252
  );
253
  }
254
 
237
  $oFO->canRunWizards() ? $this->buildEmailBody( $aFiles ) : $this->buildEmailBody_Legacy( $aFiles )
238
  );
239
 
240
+ if ( !$oFO->getConn()->isRelabelled() ) {
241
+ $aContent[] = '';
242
+ $aContent[] = '[ <a href="https://icwp.io/moreinfochecksum">'._wpsf__( 'More Info On This Scanner' ).' ]</a>';
243
+ }
244
 
245
+ $sTo = $oFO->getPluginDefaultRecipientAddress();
246
  $this->getEmailProcessor()
247
+ ->sendEmailWithWrap(
248
+ $sTo,
249
  sprintf( _wpsf__( 'Warning - %s' ), _wpsf__( 'Unrecognised WordPress Files Detected' ) ),
250
  $aContent
251
  );
252
 
253
  $this->addToAuditEntry(
254
+ sprintf( _wpsf__( 'Sent Unrecognised File Scan Notification email alert to: %s' ), $sTo )
255
  );
256
  }
257
 
src/processors/hackprotect_pluginvulnerabilities.php CHANGED
@@ -53,10 +53,11 @@ if ( !class_exists( 'ICWP_WPSF_Processor_HackProtect_PluginVulnerabilities', fal
53
  }
54
 
55
  public function cron_dailyPluginVulnerabilitiesScan() {
 
 
56
 
57
  $aPlugins = $this->loadWpPlugins()->getPlugins();
58
 
59
- $sRecipient = $this->getPluginDefaultRecipientAddress();
60
  foreach( $aPlugins as $sPluginFile => $aPluginData ) {
61
  $aPluginVulnerabilityData = $this->getPluginVulnerabilityData( $sPluginFile, $aPluginData );
62
  if ( is_array( $aPluginVulnerabilityData ) ) {
@@ -64,7 +65,7 @@ if ( !class_exists( 'ICWP_WPSF_Processor_HackProtect_PluginVulnerabilities', fal
64
  }
65
  }
66
 
67
- $this->sendVulnerabilityNotification( $sRecipient );
68
  }
69
 
70
  /**
@@ -109,7 +110,7 @@ if ( !class_exists( 'ICWP_WPSF_Processor_HackProtect_PluginVulnerabilities', fal
109
  $sEmailSubject = sprintf( _wpsf__( 'Warning - %s' ), _wpsf__( 'Plugin(s) Discovered With Known Security Vulnerabilities.' ) );
110
 
111
  $bSendSuccess = $this->getEmailProcessor()
112
- ->sendEmailTo( $sRecipient, $sEmailSubject, $this->aEmailContents );
113
 
114
  if ( $bSendSuccess ) {
115
  $this->addToAuditEntry( sprintf( _wpsf__( 'Successfully sent Plugin Vulnerability Notification email alert to: %s' ), $sRecipient ) );
@@ -215,7 +216,7 @@ if ( !class_exists( 'ICWP_WPSF_Processor_HackProtect_PluginVulnerabilities', fal
215
  $oWp = $this->loadWp();
216
  $oFO = $this->getFeature();
217
 
218
- $sSource = $oFO->getDefinition( 'plugin_vulnerabilities_data_source' );
219
  $sRawSource = $this->loadFS()->getUrlContent( $sSource );
220
  if ( $sRawSource === false ) {
221
  return false;
@@ -234,7 +235,7 @@ if ( !class_exists( 'ICWP_WPSF_Processor_HackProtect_PluginVulnerabilities', fal
234
  */
235
  protected function getCronName() {
236
  $oFO = $this->getFeature();
237
- return $oFO->prefix( $oFO->getDefinition( 'notifications_cron_name' ) );
238
  }
239
  }
240
 
53
  }
54
 
55
  public function cron_dailyPluginVulnerabilitiesScan() {
56
+ /** @var ICWP_WPSF_FeatureHandler_HackProtect $oFO */
57
+ $oFO = $this->getFeature();
58
 
59
  $aPlugins = $this->loadWpPlugins()->getPlugins();
60
 
 
61
  foreach( $aPlugins as $sPluginFile => $aPluginData ) {
62
  $aPluginVulnerabilityData = $this->getPluginVulnerabilityData( $sPluginFile, $aPluginData );
63
  if ( is_array( $aPluginVulnerabilityData ) ) {
65
  }
66
  }
67
 
68
+ $this->sendVulnerabilityNotification( $oFO->getPluginDefaultRecipientAddress() );
69
  }
70
 
71
  /**
110
  $sEmailSubject = sprintf( _wpsf__( 'Warning - %s' ), _wpsf__( 'Plugin(s) Discovered With Known Security Vulnerabilities.' ) );
111
 
112
  $bSendSuccess = $this->getEmailProcessor()
113
+ ->sendEmailWithWrap( $sRecipient, $sEmailSubject, $this->aEmailContents );
114
 
115
  if ( $bSendSuccess ) {
116
  $this->addToAuditEntry( sprintf( _wpsf__( 'Successfully sent Plugin Vulnerability Notification email alert to: %s' ), $sRecipient ) );
216
  $oWp = $this->loadWp();
217
  $oFO = $this->getFeature();
218
 
219
+ $sSource = $oFO->getDef( 'plugin_vulnerabilities_data_source' );
220
  $sRawSource = $this->loadFS()->getUrlContent( $sSource );
221
  if ( $sRawSource === false ) {
222
  return false;
235
  */
236
  protected function getCronName() {
237
  $oFO = $this->getFeature();
238
+ return $oFO->prefix( $oFO->getDef( 'notifications_cron_name' ) );
239
  }
240
  }
241
 
src/processors/hackprotect_ptguard.php CHANGED
@@ -487,16 +487,16 @@ class ICWP_WPSF_Processor_HackProtect_PTGuard extends ICWP_WPSF_Processor_CronBa
487
  $aContent[] = '';
488
  }
489
 
490
- $sRecipient = $this->getPluginDefaultRecipientAddress();
491
  $sEmailSubject = sprintf( _wpsf__( 'Warning - %s' ), _wpsf__( 'Plugins/Themes Have Been Altered' ) );
492
  $bSendSuccess = $this->getEmailProcessor()
493
- ->sendEmailTo( $sRecipient, $sEmailSubject, $aContent );
494
 
495
  if ( $bSendSuccess ) {
496
- $this->addToAuditEntry( sprintf( _wpsf__( 'Successfully sent Plugin/Theme Guard email alert to: %s' ), $sRecipient ) );
497
  }
498
  else {
499
- $this->addToAuditEntry( sprintf( _wpsf__( 'Failed to send Plugin/Theme Guard email alert to: %s' ), $sRecipient ) );
500
  }
501
  }
502
 
487
  $aContent[] = '';
488
  }
489
 
490
+ $sTo = $oFO->getPluginDefaultRecipientAddress();
491
  $sEmailSubject = sprintf( _wpsf__( 'Warning - %s' ), _wpsf__( 'Plugins/Themes Have Been Altered' ) );
492
  $bSendSuccess = $this->getEmailProcessor()
493
+ ->sendEmailWithWrap( $sTo, $sEmailSubject, $aContent );
494
 
495
  if ( $bSendSuccess ) {
496
+ $this->addToAuditEntry( sprintf( _wpsf__( 'Successfully sent Plugin/Theme Guard email alert to: %s' ), $sTo ) );
497
  }
498
  else {
499
+ $this->addToAuditEntry( sprintf( _wpsf__( 'Failed to send Plugin/Theme Guard email alert to: %s' ), $sTo ) );
500
  }
501
  }
502
 
src/processors/hackprotect_wpvulnscan.php CHANGED
@@ -202,6 +202,8 @@ class ICWP_WPSF_Processor_HackProtect_WpVulnScan extends ICWP_WPSF_Processor_Bas
202
  if ( empty( $this->aNotifEmail ) ) {
203
  return true;
204
  }
 
 
205
  $oWp = $this->loadWp();
206
  $oConn = $this->getController();
207
 
@@ -216,15 +218,15 @@ class ICWP_WPSF_Processor_HackProtect_WpVulnScan extends ICWP_WPSF_Processor_Bas
216
  $this->aNotifEmail[] = sprintf( _wpsf__( 'Go To Your Plugins: %s' ), $oWp->getAdminUrl_Plugins( $oConn->getIsWpmsNetworkAdminOnly() ) );
217
 
218
  $sSubject = sprintf( _wpsf__( 'Warning - %s' ), _wpsf__( 'Plugin(s) Discovered With Known Security Vulnerabilities.' ) );
219
- $sRecipient = $this->getPluginDefaultRecipientAddress();
220
  $bSendSuccess = $this->getEmailProcessor()
221
- ->sendEmailTo( $sRecipient, $sSubject, $this->aNotifEmail );
222
 
223
  if ( $bSendSuccess ) {
224
- $this->addToAuditEntry( sprintf( _wpsf__( 'Successfully sent Plugin Vulnerability Notification email alert to: %s' ), $sRecipient ) );
225
  }
226
  else {
227
- $this->addToAuditEntry( sprintf( _wpsf__( 'Failed to send Plugin Vulnerability Notification email alert to: %s' ), $sRecipient ) );
228
  }
229
  return $bSendSuccess;
230
  }
202
  if ( empty( $this->aNotifEmail ) ) {
203
  return true;
204
  }
205
+ /** @var ICWP_WPSF_FeatureHandler_HackProtect $oFO */
206
+ $oFO = $this->getFeature();
207
  $oWp = $this->loadWp();
208
  $oConn = $this->getController();
209
 
218
  $this->aNotifEmail[] = sprintf( _wpsf__( 'Go To Your Plugins: %s' ), $oWp->getAdminUrl_Plugins( $oConn->getIsWpmsNetworkAdminOnly() ) );
219
 
220
  $sSubject = sprintf( _wpsf__( 'Warning - %s' ), _wpsf__( 'Plugin(s) Discovered With Known Security Vulnerabilities.' ) );
221
+ $sTo = $oFO->getPluginDefaultRecipientAddress();
222
  $bSendSuccess = $this->getEmailProcessor()
223
+ ->sendEmailWithWrap( $sTo, $sSubject, $this->aNotifEmail );
224
 
225
  if ( $bSendSuccess ) {
226
+ $this->addToAuditEntry( sprintf( _wpsf__( 'Successfully sent Plugin Vulnerability Notification email alert to: %s' ), $sTo ) );
227
  }
228
  else {
229
+ $this->addToAuditEntry( sprintf( _wpsf__( 'Failed to send Plugin Vulnerability Notification email alert to: %s' ), $sTo ) );
230
  }
231
  return $bSendSuccess;
232
  }
src/processors/ips.php CHANGED
@@ -19,23 +19,23 @@ class ICWP_WPSF_Processor_Ips extends ICWP_WPSF_BaseDbProcessor {
19
  protected $bVisitorIsWhitelisted;
20
 
21
  /**
22
- * @param ICWP_WPSF_FeatureHandler_Ips $oFeatureOptions
23
  */
24
- public function __construct( ICWP_WPSF_FeatureHandler_Ips $oFeatureOptions ) {
25
- parent::__construct( $oFeatureOptions, $oFeatureOptions->getIpListsTableName() );
26
  }
27
 
28
  /**
29
  * @return bool
30
  */
31
- protected function readyToRun() {
32
- return ( parent::readyToRun() && $this->loadIpService()->isValidIp_PublicRemote( $this->ip() ) );
33
  }
34
 
35
  /**
36
  */
37
  public function run() {
38
- if ( !$this->readyToRun() ) {
39
  return;
40
  }
41
 
@@ -64,7 +64,7 @@ class ICWP_WPSF_Processor_Ips extends ICWP_WPSF_BaseDbProcessor {
64
  $this->setIpTransgressed(); // We now black mark this IP
65
  }
66
  $this->addToAuditEntry(
67
- sprintf( _wpsf__( '404 detected at "%s"' ), $this->loadDataProcessor()->getRequestPath() ),
68
  2, 'request_tracking_404'
69
  );
70
  }
19
  protected $bVisitorIsWhitelisted;
20
 
21
  /**
22
+ * @param ICWP_WPSF_FeatureHandler_Ips $oModCon
23
  */
24
+ public function __construct( ICWP_WPSF_FeatureHandler_Ips $oModCon ) {
25
+ parent::__construct( $oModCon, $oModCon->getIpListsTableName() );
26
  }
27
 
28
  /**
29
  * @return bool
30
  */
31
+ public function isReadyToRun() {
32
+ return ( parent::isReadyToRun() && $this->loadIpService()->isValidIp_PublicRemote( $this->ip() ) );
33
  }
34
 
35
  /**
36
  */
37
  public function run() {
38
+ if ( !$this->isReadyToRun() ) {
39
  return;
40
  }
41
 
64
  $this->setIpTransgressed(); // We now black mark this IP
65
  }
66
  $this->addToAuditEntry(
67
+ sprintf( _wpsf__( '404 detected at "%s"' ), $this->loadDP()->getRequestPath() ),
68
  2, 'request_tracking_404'
69
  );
70
  }
src/processors/license.php CHANGED
@@ -32,7 +32,7 @@ class ICWP_WPSF_Processor_License extends ICWP_WPSF_Processor_BaseWpsf {
32
  break;
33
 
34
  case 'license_check':
35
- if ( !wp_next_scheduled( $oFO->prefix( 'license_check' ) ) ) {
36
  wp_schedule_single_event( $oDp->time() + 12, $oFO->prefix( 'adhoc_cron_license_check' ) );
37
  }
38
  break;
32
  break;
33
 
34
  case 'license_check':
35
+ if ( !wp_next_scheduled( $oFO->prefix( 'adhoc_cron_license_check' ) ) ) {
36
  wp_schedule_single_event( $oDp->time() + 12, $oFO->prefix( 'adhoc_cron_license_check' ) );
37
  }
38
  break;
src/processors/lockdown.php CHANGED
@@ -215,7 +215,7 @@ class ICWP_WPSF_Processor_Lockdown extends ICWP_WPSF_Processor_BaseWpsf {
215
  $this->loadWp()->wpDie( sprintf(
216
  _wpsf__( 'The "author" query parameter has been blocked by %s to protect against user login name fishing.' )
217
  .sprintf( '<br /><a href="%s" target="_blank">%s</a>',
218
- 'http://icwp.io/7l',
219
  _wpsf__( 'Learn More.' )
220
  ),
221
  $this->getController()->getHumanName()
215
  $this->loadWp()->wpDie( sprintf(
216
  _wpsf__( 'The "author" query parameter has been blocked by %s to protect against user login name fishing.' )
217
  .sprintf( '<br /><a href="%s" target="_blank">%s</a>',
218
+ 'https://icwp.io/7l',
219
  _wpsf__( 'Learn More.' )
220
  ),
221
  $this->getController()->getHumanName()
src/processors/login_protect.php CHANGED
@@ -29,11 +29,11 @@ class ICWP_WPSF_Processor_LoginProtect extends ICWP_WPSF_Processor_BaseWpsf {
29
  $this->getProcessorGasp()->run();
30
  }
31
 
32
- if ( $this->getOption( 'login_limit_interval' ) > 0 && ( $oWp->isRequestUserLogin() || $oWp->isRequestUserRegister() ) ) {
33
  $this->getProcessorCooldown()->run();
34
  }
35
 
36
- if ( $oFO->getIsGoogleRecaptchaEnabled() && $oFO->getIsGoogleRecaptchaReady() ) {
37
  $this->getProcessorGoogleRecaptcha()->run();
38
  }
39
 
29
  $this->getProcessorGasp()->run();
30
  }
31
 
32
+ if ( $oFO->isCooldownEnabled() && $this->loadDP()->isMethodPost() ) {
33
  $this->getProcessorCooldown()->run();
34
  }
35
 
36
+ if ( $oFO->isGoogleRecaptchaEnabled() ) {
37
  $this->getProcessorGoogleRecaptcha()->run();
38
  }
39
 
src/processors/loginprotect_base.php CHANGED
@@ -8,6 +8,275 @@ require_once( dirname( __FILE__ ).'/base_wpsf.php' );
8
 
9
  abstract class ICWP_WPSF_Processor_LoginProtect_Base extends ICWP_WPSF_Processor_BaseWpsf {
10
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
11
  protected function setLoginAsFailed( $sStatToIncrement ) {
12
  /** @var ICWP_WPSF_FeatureHandler_LoginProtect $oFO */
13
  $oFO = $this->getFeature();
@@ -19,4 +288,44 @@ abstract class ICWP_WPSF_Processor_LoginProtect_Base extends ICWP_WPSF_Processor
19
  $this->doStatIncrement( $sStatToIncrement );
20
  $this->setIpTransgressed(); // We now black mark this IP
21
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
22
  }
8
 
9
  abstract class ICWP_WPSF_Processor_LoginProtect_Base extends ICWP_WPSF_Processor_BaseWpsf {
10
 
11
+ /**
12
+ * @var string
13
+ */
14
+ private $sActionToAudit;
15
+
16
+ /**
17
+ * @var string
18
+ */
19
+ private $sUserToAudit;
20
+
21
+ /**
22
+ */
23
+ public function run() {
24
+ /** @var ICWP_WPSF_FeatureHandler_LoginProtect $oFO */
25
+ $oFO = $this->getFeature();
26
+ $b3rdParty = $oFO->getIfSupport3rdParty();
27
+
28
+ if ( $oFO->isProtectLogin() ) {
29
+ // We give it a priority of 10 so that we can jump in before WordPress does its own validation.
30
+ add_filter( 'authenticate', array( $this, 'checkReqLogin_Wp' ), 10, 3 );
31
+
32
+ add_action( 'login_form', array( $this, 'printLoginFormItems' ), 100 );
33
+ add_filter( 'login_form_middle', array( $this, 'provideLoginFormItems' ), 100 );
34
+
35
+ if ( $b3rdParty ) {
36
+ add_action( 'edd_login_fields_after', array( $this, 'printLoginFormItems' ), 10 );
37
+
38
+ add_action( 'woocommerce_login_form', array( $this, 'printLoginFormItems_Woo' ), 100 );
39
+ add_filter( 'woocommerce_process_login_errors', array( $this, 'checkReqLogin_Woo' ), 10, 2 );
40
+ }
41
+ }
42
+
43
+ if ( $oFO->isProtectLostPassword() ) {
44
+ add_action( 'lostpassword_form', array( $this, 'printLoginFormItems' ) );
45
+ add_action( 'lostpassword_post', array( $this, 'checkReqLostPassword_Wp' ), 10, 1 );
46
+
47
+ if ( $b3rdParty ) {
48
+ add_action( 'woocommerce_lostpassword_form', array( $this, 'printLoginFormItems' ), 10 );
49
+ }
50
+ }
51
+
52
+ if ( $oFO->isProtectRegister() ) {
53
+ add_action( 'register_form', array( $this, 'printLoginFormItems' ) );
54
+ // add_action( 'register_post', array( $this, 'checkReqRegistration_Wp' ), 10, 1 );
55
+ add_filter( 'registration_errors', array( $this, 'checkReqRegistrationErrors_Wp' ), 10, 2 );
56
+
57
+ if ( $b3rdParty ) {
58
+ add_action( 'bp_before_registration_submit_buttons', array( $this, 'printLoginFormItems_Bp' ), 10 );
59
+ add_action( 'bp_signup_validate', array( $this, 'checkReqRegistration_Bp' ), 10 );
60
+
61
+ add_action( 'edd_register_form_fields_before_submit', array( $this, 'printLoginFormItems' ), 10 );
62
+ add_action( 'edd_process_register_form', array( $this, 'checkReqRegistration_Edd' ), 10 );
63
+
64
+ add_action( 'woocommerce_register_form', array( $this, 'printLoginFormItems' ), 10 );
65
+ add_filter( 'woocommerce_process_registration_errors', array( $this, 'checkReqRegistration_Woo' ), 10, 2 );
66
+ }
67
+ }
68
+
69
+ if ( $b3rdParty && $oFO->isProtect( 'checkout_woo' ) ) {
70
+ add_action( 'woocommerce_after_checkout_registration_form', array( $this, 'printRegistrationFormItems_Woo' ), 10 );
71
+ add_action( 'woocommerce_after_checkout_validation', array( $this, 'checkReqCheckout_Woo' ), 10, 2 );
72
+ }
73
+ }
74
+
75
+ /**
76
+ * @throws Exception
77
+ */
78
+ protected function performCheckWithException() {
79
+ }
80
+
81
+ /**
82
+ */
83
+ protected function performCheckWithDie() {
84
+ try {
85
+ $this->performCheckWithException();
86
+ }
87
+ catch ( Exception $oE ) {
88
+ $this->loadWp()->wpDie( $oE->getMessage() );
89
+ }
90
+ }
91
+
92
+ /**
93
+ * @param WP_Error $oWpError
94
+ * @param string $sUsername
95
+ * @return WP_Error
96
+ */
97
+ public function checkReqLogin_Woo( $oWpError, $sUsername ) {
98
+ try {
99
+ $this->setUserToAudit( $sUsername )
100
+ ->setActionToAudit( 'woo-login' )
101
+ ->performCheckWithException();
102
+ }
103
+ catch ( Exception $oE ) {
104
+ $oWpError = $this->giveMeWpError( $oWpError );
105
+ $oWpError->add( $this->prefix( rand() ), $oE->getMessage() );
106
+ }
107
+ return $oWpError;
108
+ }
109
+
110
+ /**
111
+ * Should be a filter added to WordPress's "authenticate" filter, but before WordPress performs
112
+ * it's own authentication (theirs is priority 30, so we could go in at around 20).
113
+ * @param null|WP_User|WP_Error $oUserOrError
114
+ * @param string $sUsername
115
+ * @param string $sPassword
116
+ * @return WP_User|WP_Error
117
+ */
118
+ public function checkReqLogin_Wp( $oUserOrError, $sUsername, $sPassword ) {
119
+ try {
120
+ if ( !is_wp_error( $oUserOrError ) && !empty( $sUsername ) && !empty( $sPassword ) ) {
121
+ $this->setUserToAudit( $sUsername )
122
+ ->setActionToAudit( 'login' )
123
+ ->performCheckWithException();
124
+ }
125
+ }
126
+ catch ( Exception $oE ) {
127
+ $oUserOrError = $this->giveMeWpError( $oUserOrError );
128
+ $oUserOrError->add( $this->prefix( rand() ), $oE->getMessage() );
129
+ }
130
+ return $oUserOrError;
131
+ }
132
+
133
+ /**
134
+ * @param WP_Error $oWpError
135
+ * @return WP_Error
136
+ */
137
+ public function checkReqLostPassword_Wp( $oWpError ) {
138
+ try {
139
+ $this->setUserToAudit( $this->loadDP()->post( 'user_login', '' ) )
140
+ ->setActionToAudit( 'reset-password' )
141
+ ->performCheckWithException();
142
+ }
143
+ catch ( Exception $oE ) {
144
+ $oWpError = $this->giveMeWpError( $oWpError );
145
+ $oWpError->add( $this->prefix( rand() ), $oE->getMessage() );
146
+ }
147
+ return $oWpError;
148
+ }
149
+
150
+ /**
151
+ * @param string $sUsername
152
+ */
153
+ public function checkReqRegistration_Wp( $sUsername ) {
154
+ return $this->setUserToAudit( $sUsername )
155
+ ->setActionToAudit( 'register' )
156
+ ->performCheckWithDie();
157
+ }
158
+
159
+ /**
160
+ * see class-wc-checkout.php
161
+ * @param WP_Error $oWpError
162
+ * @param array $aPostedData
163
+ * @return WP_Error
164
+ */
165
+ public function checkReqCheckout_Woo( $aPostedData, $oWpError ) {
166
+ try {
167
+ $this->setActionToAudit( 'woo-checkout' )
168
+ ->performCheckWithException();
169
+ }
170
+ catch ( Exception $oE ) {
171
+ $oWpError = $this->giveMeWpError( $oWpError );
172
+ $oWpError->add( $this->prefix( rand() ), $oE->getMessage() );
173
+ }
174
+ return $oWpError;
175
+ }
176
+
177
+ /**
178
+ */
179
+ public function checkReqRegistration_Edd() {
180
+ try {
181
+ $this->setActionToAudit( 'edd-register' )
182
+ ->performCheckWithException();
183
+ }
184
+ catch ( Exception $oE ) {
185
+ if ( function_exists( 'edd_set_error' ) ) {
186
+ edd_set_error( $this->prefix( rand() ), $oE->getMessage() );
187
+ }
188
+ }
189
+ }
190
+
191
+ /**
192
+ * @param WP_Error $oWpError
193
+ * @param string $sUsername
194
+ * @return WP_Error
195
+ */
196
+ public function checkReqRegistration_Woo( $oWpError, $sUsername ) {
197
+ try {
198
+ $this->setUserToAudit( $sUsername )
199
+ ->setActionToAudit( 'woo-register' )
200
+ ->performCheckWithException();
201
+ }
202
+ catch ( Exception $oE ) {
203
+ $oWpError = $this->giveMeWpError( $oWpError );
204
+ $oWpError->add( $this->prefix( rand() ), $oE->getMessage() );
205
+ }
206
+ return $oWpError;
207
+ }
208
+
209
+ /**
210
+ * @param WP_Error $oWpError
211
+ * @param string $sUsername
212
+ * @return WP_Error
213
+ */
214
+ public function checkReqRegistrationErrors_Wp( $oWpError, $sUsername ) {
215
+ try {
216
+ $this->setUserToAudit( $sUsername )
217
+ ->setActionToAudit( 'register' )
218
+ ->performCheckWithException();
219
+ }
220
+ catch ( Exception $oE ) {
221
+ $oWpError = $this->giveMeWpError( $oWpError );
222
+ $oWpError->add( $this->prefix( rand() ), $oE->getMessage() );
223
+ }
224
+ return $oWpError;
225
+ }
226
+
227
+ /**
228
+ * @return bool
229
+ */
230
+ public function checkReqRegistration_Bp() {
231
+ return $this->performCheckWithDie();
232
+ }
233
+
234
+ /**
235
+ * @return string
236
+ */
237
+ protected function buildLoginFormItems() {
238
+ return '';
239
+ }
240
+
241
+ /**
242
+ * @return void
243
+ */
244
+ public function printLoginFormItems() {
245
+ echo $this->buildLoginFormItems();
246
+ }
247
+
248
+ /**
249
+ * @return void
250
+ */
251
+ public function printLoginFormItems_Woo() {
252
+ $this->printLoginFormItems();
253
+ }
254
+
255
+ /**
256
+ * see form-billing.php
257
+ * @param WP_Checkout $oCheckout
258
+ * @return void
259
+ */
260
+ public function printRegistrationFormItems_Woo( $oCheckout ) {
261
+ if ( $oCheckout instanceof WC_Checkout && $oCheckout->get_checkout_fields( 'account' ) ) {
262
+ $this->printLoginFormItems();
263
+ }
264
+ }
265
+
266
+ /**
267
+ * @return void
268
+ */
269
+ public function printLoginFormItems_Bp() {
270
+ $this->printLoginFormItems();
271
+ }
272
+
273
+ /**
274
+ * @return string
275
+ */
276
+ public function provideLoginFormItems() {
277
+ return $this->buildLoginFormItems();
278
+ }
279
+
280
  protected function setLoginAsFailed( $sStatToIncrement ) {
281
  /** @var ICWP_WPSF_FeatureHandler_LoginProtect $oFO */
282
  $oFO = $this->getFeature();
288
  $this->doStatIncrement( $sStatToIncrement );
289
  $this->setIpTransgressed(); // We now black mark this IP
290
  }
291
+
292
+ /**
293
+ * @param WP_Error $oMaybeWpError
294
+ * @return WP_Error
295
+ */
296
+ protected function giveMeWpError( $oMaybeWpError ) {
297
+ return is_wp_error( $oMaybeWpError ) ? $oMaybeWpError : new WP_Error();
298
+ }
299
+
300
+ /**
301
+ * @return string
302
+ */
303
+ protected function getActionToAudit() {
304
+ return empty( $this->sActionToAudit ) ? 'unknown-action' : $this->sActionToAudit;
305
+ }
306
+
307
+ /**
308
+ * @return string
309
+ */
310
+ protected function getUserToAudit() {
311
+ return empty( $this->sUserToAudit ) ? 'unknown' : $this->sUserToAudit;
312
+ }
313
+
314
+ /**
315
+ * @param string $sActionToAudit
316
+ * @return $this
317
+ */
318
+ protected function setActionToAudit( $sActionToAudit ) {
319
+ $this->sActionToAudit = $sActionToAudit;
320
+ return $this;
321
+ }
322
+
323
+ /**
324
+ * @param string $sUserToAudit
325
+ * @return $this
326
+ */
327
+ protected function setUserToAudit( $sUserToAudit ) {
328
+ $this->sUserToAudit = sanitize_user( $sUserToAudit );
329
+ return $this;
330
+ }
331
  }
src/processors/loginprotect_cooldown.php CHANGED
@@ -4,7 +4,7 @@ if ( class_exists( 'ICWP_WPSF_Processor_LoginProtect_Cooldown', false ) ) {
4
  return;
5
  }
6
 
7
- require_once( dirname(__FILE__ ).'/loginprotect_base.php' );
8
 
9
  class ICWP_WPSF_Processor_LoginProtect_Cooldown extends ICWP_WPSF_Processor_LoginProtect_Base {
10
 
@@ -14,60 +14,35 @@ class ICWP_WPSF_Processor_LoginProtect_Cooldown extends ICWP_WPSF_Processor_Logi
14
  private $bCooldownUpdated = false;
15
 
16
  /**
 
17
  */
18
- public function run() {
19
- /** @var ICWP_WPSF_FeatureHandler_LoginProtect $oFO */
20
- $oFO = $this->getFeature();
21
 
22
- // We give it a priority of 10 so that we can jump in before WordPress does its own validation.
23
- add_filter( 'authenticate', array( $this, 'checkLoginInterval' ), 10, 1 );
24
 
25
- // apply to user registrations if set to do so.
26
- if ( $oFO->getIsCheckingUserRegistrations() ) {
27
- add_filter( 'registration_errors', array( $this, 'checkLoginInterval' ), 10, 2 );
28
- }
29
- }
30
-
31
- /**
32
- * Should be a filter added to WordPress's "authenticate" filter, but before WordPress performs
33
- * it's own authentication (theirs is priority 30, so we could go in at around 20).
34
- *
35
- * @param null|WP_User|WP_Error $oUserOrError
36
- * @return WP_User|WP_Error
37
- */
38
- public function checkLoginInterval( $oUserOrError ) {
39
- if ( !$this->loadWp()->isRequestUserLogin() || $this->bCooldownUpdated ) {
40
- return $oUserOrError;
41
- }
42
-
43
- // If we're outside the interval, let the login process proceed as per normal and
44
- // update our last login time.
45
- $bWithinCooldownPeriod = $this->getIsWithinCooldownPeriod();
46
- if ( !$bWithinCooldownPeriod ) {
47
  $this->updateLastLoginTime();
48
- $this->doStatIncrement( 'login.cooldown.success' );
49
- return $oUserOrError;
50
- }
51
-
52
- // At this point someone has attempted to login within the previous login wait interval
53
- // So we remove WordPress's authentication filter and our own user check authentication
54
- // And finally return a WP_Error which will be reflected back to the user.
55
 
56
- $sErrorString = _wpsf__( "Login Cooldown in effect." ).' '
57
- .sprintf(
58
- _wpsf__( "You must wait %s seconds before attempting to %s again." ),
59
- $this->getLoginCooldownInterval() - $this->getSecondsSinceLastLogin(),
60
- $this->loadWp()->isRequestUserLogin() ? _wpsf__( 'login' ) : _wpsf__( 'register' )
61
- );
62
-
63
- if ( !is_wp_error( $oUserOrError ) ) {
64
- $oUserOrError = new WP_Error();
 
 
 
 
 
 
 
 
 
65
  }
66
- $oUserOrError->add( 'wpsf_logininterval', $sErrorString );
67
-
68
- $this->setLoginAsFailed( 'login.cooldown.fail' );
69
-
70
- return $oUserOrError;
71
  }
72
 
73
  /**
@@ -96,19 +71,19 @@ class ICWP_WPSF_Processor_LoginProtect_Cooldown extends ICWP_WPSF_Processor_Logi
96
  */
97
  protected function updateLastLoginTime() {
98
  $this->bCooldownUpdated = true;
 
99
  $this->loadFS()->touch( $this->getLastLoginTimeFilePath(), $this->time() );
100
  }
101
 
102
  /**
103
  * @return bool
104
  */
105
- protected function getIsWithinCooldownPeriod() {
106
  // Is there an interval set?
107
  $nCooldown = $this->getLoginCooldownInterval();
108
  if ( empty( $nCooldown ) || $nCooldown <= 0 ) {
109
  return false;
110
  }
111
-
112
  return ( $this->getSecondsSinceLastLogin() < $nCooldown );
113
  }
114
 
@@ -118,4 +93,11 @@ class ICWP_WPSF_Processor_LoginProtect_Cooldown extends ICWP_WPSF_Processor_Logi
118
  protected function getSecondsSinceLastLogin() {
119
  return ( $this->time() - $this->getLastLoginTime() );
120
  }
 
 
 
 
 
 
 
121
  }
4
  return;
5
  }
6
 
7
+ require_once( dirname( __FILE__ ).'/loginprotect_base.php' );
8
 
9
  class ICWP_WPSF_Processor_LoginProtect_Cooldown extends ICWP_WPSF_Processor_LoginProtect_Base {
10
 
14
  private $bCooldownUpdated = false;
15
 
16
  /**
17
+ * @throws Exception
18
  */
19
+ protected function performCheckWithException() {
 
 
20
 
21
+ if ( !$this->isCooldownAlreadyUpdated() ) {
 
22
 
23
+ $bWithinCooldownPeriod = $this->isWithinCooldownPeriod();
24
+ $nRemaining = $this->getLoginCooldownInterval() - $this->getSecondsSinceLastLogin();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
25
  $this->updateLastLoginTime();
 
 
 
 
 
 
 
26
 
27
+ // At this point someone has attempted to login within the previous login wait interval
28
+ // So we remove WordPress's authentication filter and our own user check authentication
29
+ // And finally return a WP_Error which will be reflected back to the user.
30
+ if ( $bWithinCooldownPeriod ) {
31
+
32
+ $sErrorString = _wpsf__( "Login Cooldown in effect." ).' '
33
+ .sprintf(
34
+ _wpsf__( "You must wait %s seconds before attempting this action again." ),
35
+ $nRemaining
36
+ );
37
+
38
+ $this->setLoginAsFailed( 'login.cooldown.fail' );
39
+ $this->addToAuditEntry( _wpsf__( 'Cooldown triggered and request (login/register/lost-password) was blocked.' ) );
40
+ throw new Exception( $sErrorString );
41
+ }
42
+ else {
43
+ $this->doStatIncrement( 'login.cooldown.success' );
44
+ }
45
  }
 
 
 
 
 
46
  }
47
 
48
  /**
71
  */
72
  protected function updateLastLoginTime() {
73
  $this->bCooldownUpdated = true;
74
+ $this->loadFS()->deleteFile( $this->getLastLoginTimeFilePath() );
75
  $this->loadFS()->touch( $this->getLastLoginTimeFilePath(), $this->time() );
76
  }
77
 
78
  /**
79
  * @return bool
80
  */
81
+ private function isWithinCooldownPeriod() {
82
  // Is there an interval set?
83
  $nCooldown = $this->getLoginCooldownInterval();
84
  if ( empty( $nCooldown ) || $nCooldown <= 0 ) {
85
  return false;
86
  }
 
87
  return ( $this->getSecondsSinceLastLogin() < $nCooldown );
88
  }
89
 
93
  protected function getSecondsSinceLastLogin() {
94
  return ( $this->time() - $this->getLastLoginTime() );
95
  }
96
+
97
+ /**
98
+ * @return bool
99
+ */
100
+ protected function isCooldownAlreadyUpdated() {
101
+ return (bool)$this->bCooldownUpdated;
102
+ }
103
  }
src/processors/loginprotect_gasp.php CHANGED
@@ -4,106 +4,21 @@ if ( class_exists( 'ICWP_WPSF_Processor_LoginProtect_Gasp', false ) ) {
4
  return;
5
  }
6
 
7
- require_once( dirname( __FILE__ ).'/base_wpsf.php' );
8
 
9
- class ICWP_WPSF_Processor_LoginProtect_Gasp extends ICWP_WPSF_Processor_BaseWpsf {
10
-
11
- /**
12
- */
13
- public function run() {
14
- /** @var ICWP_WPSF_FeatureHandler_LoginProtect $oFO */
15
- $oFO = $this->getFeature();
16
-
17
- // Add GASP checking to the login form.
18
- add_action( 'login_form', array( $this, 'printGaspLoginCheck_Action' ), 100 );
19
- add_filter( 'login_form_middle', array( $this, 'printGaspLoginCheck_Filter' ) );
20
-
21
- // before username/password check (20)
22
- add_filter( 'authenticate', array( $this, 'checkLoginForGasp_Filter' ), 12, 2 );
23
-
24
- $b3rdParty = $oFO->getIfSupport3rdParty();
25
- if ( $b3rdParty ) {
26
- add_action( 'woocommerce_login_form', array( $this, 'printGaspLoginCheck_Action' ), 10 );
27
- add_action( 'edd_login_fields_after', array( $this, 'printGaspLoginCheck_Action' ), 10 );
28
- }
29
-
30
- // apply to user registrations if set to do so.
31
- if ( $oFO->getIsCheckingUserRegistrations() ) {
32
- //print the checkbox code:
33
- add_action( 'register_form', array( $this, 'printGaspLoginCheck_Action' ) );
34
- add_action( 'lostpassword_form', array( $this, 'printGaspLoginCheck_Action' ) );
35
-
36
- //verify the checkbox is present:
37
- add_action( 'register_post', array( $this, 'checkRegisterForGasp_Action' ), 10, 1 );
38
- add_action( 'lostpassword_post', array( $this, 'checkResetPasswordForGasp_Action' ), 10 );
39
-
40
- if ( $b3rdParty ) {
41
- add_action( 'woocommerce_lostpassword_form', array( $this, 'printGaspLoginCheck_Action' ), 10 );
42
- add_action( 'edd_register_form_fields_before_submit', array(
43
- $this,
44
- 'printGaspLoginCheck_Action'
45
- ), 10 );
46
-
47
- // Buddypress custom registration page.
48
- add_action( 'bp_before_registration_submit_buttons', array( $this, 'printGaspLoginCheck_Action' ), 10 );
49
- add_action( 'bp_signup_validate', array( $this, 'checkRegisterForGasp_Action' ), 10 );
50
- }
51
- }
52
- }
53
-
54
- /**
55
- */
56
- public function printGaspLoginCheck_Action() {
57
- echo $this->getGaspLoginHtml();
58
- }
59
 
60
  /**
61
  * @return string
62
  */
63
- public function printGaspLoginCheck_Filter() {
64
  return $this->getGaspLoginHtml();
65
  }
66
 
67
- /**
68
- * @param $oUser
69
- * @param $sUsername
70
- * @return WP_Error
71
- */
72
- public function checkLoginForGasp_Filter( $oUser, $sUsername ) {
73
- if ( !$this->loadWp()->isRequestUserLogin() ) {
74
- return $oUser;
75
- }
76
-
77
- if ( empty( $sUsername ) || is_wp_error( $oUser ) ) {
78
- return $oUser;
79
- }
80
- if ( $this->doGaspChecks( $sUsername, _wpsf__( 'login' ) ) ) {
81
- return $oUser;
82
- }
83
- //This doesn't actually ever get returned because we die() within doGaspChecks()
84
- return new WP_Error( 'wpsf_gaspfail', _wpsf__( 'Bot Checking Failed.' ) );
85
- }
86
-
87
- /**
88
- * @param string $sSanitizedUsername
89
- * @return void
90
- */
91
- public function checkRegisterForGasp_Action( $sSanitizedUsername ) {
92
- $this->doGaspChecks( $sSanitizedUsername, 'register' );
93
- }
94
-
95
- /**
96
- * @return void
97
- */
98
- public function checkResetPasswordForGasp_Action() {
99
- $sSanitizedUsername = sanitize_user( $this->loadDataProcessor()->FetchPost( 'user_login', '' ) );
100
- $this->doGaspChecks( $sSanitizedUsername, 'reset-password' );
101
- }
102
-
103
  /**
104
  * @return string
105
  */
106
- protected function getGaspLoginHtml() {
107
 
108
  $sLabel = $this->getTextImAHuman();
109
  $sAlert = $this->getTextPleaseCheckBox();
@@ -171,47 +86,93 @@ class ICWP_WPSF_Processor_LoginProtect_Gasp extends ICWP_WPSF_Processor_BaseWpsf
171
  }
172
 
173
  /**
174
- * Uses wpDie()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
175
  * @param string $sUsername
176
  * @param string $sActionAttempted - one of 'login', 'register', 'reset-password'
177
- * @return bool
 
178
  */
179
  protected function doGaspChecks( $sUsername, $sActionAttempted = 'login' ) {
180
- $oDp = $this->loadDataProcessor();
181
- $sGaspCheckBox = $oDp->FetchPost( $this->getGaspCheckboxName() );
182
- $sHoney = $oDp->FetchPost( 'icwp_wpsf_login_email' );
183
 
184
- $bDie = false;
185
  $sDieMessage = '';
186
  if ( empty( $sGaspCheckBox ) ) {
187
- $sAuditMessage = sprintf( _wpsf__( 'User "%s" attempted to %s but GASP checkbox was not present.' ), $sUsername, $sActionAttempted ).' '._wpsf__( 'Probably a BOT.' );
 
 
 
188
  $this->addToAuditEntry( $sAuditMessage, 3, $sActionAttempted.'_protect_block_gasp_checkbox' );
189
  $this->doStatIncrement( $sActionAttempted.'.gasp.checkbox.fail' );
190
-
191
  $sDieMessage = _wpsf__( "You must check that box to say you're not a bot." );
192
- $bDie = true;
193
  }
194
  else if ( !empty( $sHoney ) ) {
195
- $sAuditMessage = sprintf( _wpsf__( 'User "%s" attempted to %s but they were caught by the GASP honeypot.' ), $sUsername, $sActionAttempted ).' '._wpsf__( 'Probably a BOT.' );
 
 
 
196
  $this->addToAuditEntry( $sAuditMessage, 3, $sActionAttempted.'_protect_block_gasp_honeypot' );
197
  $this->doStatIncrement( $sActionAttempted.'.gasp.honeypot.fail' );
198
-
199
  $sDieMessage = sprintf( _wpsf__( 'You appear to be a bot - terminating %s attempt.' ), $sActionAttempted );
200
- $bDie = true;
 
 
201
  }
202
 
203
- if ( $bDie ) {
204
  /** @var ICWP_WPSF_FeatureHandler_LoginProtect $oFO */
205
  $oFO = $this->getFeature();
206
  $oFO->setOptInsightsAt( sprintf( 'last_%s_block_at', $sActionAttempted ) );
207
  $this->setIpTransgressed(); // We now black mark this IP
208
-
209
- $this->loadWp()
210
- ->wpDie( $sDieMessage );
211
- return false;
212
  }
213
 
214
- return true;
215
  }
216
 
217
  /**
4
  return;
5
  }
6
 
7
+ require_once( dirname( __FILE__ ).'/loginprotect_base.php' );
8
 
9
+ class ICWP_WPSF_Processor_LoginProtect_Gasp extends ICWP_WPSF_Processor_LoginProtect_Base {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
10
 
11
  /**
12
  * @return string
13
  */
14
+ protected function buildLoginFormItems() {
15
  return $this->getGaspLoginHtml();
16
  }
17
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
18
  /**
19
  * @return string
20
  */
21
+ private function getGaspLoginHtml() {
22
 
23
  $sLabel = $this->getTextImAHuman();
24
  $sAlert = $this->getTextPleaseCheckBox();
86
  }
87
 
88
  /**
89
+ * @throws Exception
90
+ */
91
+ protected function performCheckWithException() {
92
+ $oDp = $this->loadDP();
93
+ $sGaspCheckBox = $oDp->post( $this->getGaspCheckboxName() );
94
+ $sHoney = $oDp->post( 'icwp_wpsf_login_email' );
95
+
96
+ $sUsername = $this->getUserToAudit();
97
+ $sActionAttempted = $this->getActionToAudit();
98
+
99
+ $bValid = false;
100
+ $sError = '';
101
+ if ( empty( $sGaspCheckBox ) ) {
102
+ $sAuditMessage = sprintf(
103
+ _wpsf__( 'User "%s" attempted to %s but GASP checkbox was not present.' ),
104
+ $sUsername, $sActionAttempted
105
+ ).' '._wpsf__( 'Probably a BOT.' );
106
+ $this->addToAuditEntry( $sAuditMessage, 3, $sActionAttempted.'_protect_block_gasp_checkbox' );
107
+ $this->setLoginAsFailed( $sActionAttempted.'.gasp.checkbox.fail' );
108
+ $sError = _wpsf__( "You must check that box to say you're not a bot." );
109
+ }
110
+ else if ( !empty( $sHoney ) ) {
111
+ $sAuditMessage = sprintf(
112
+ _wpsf__( 'User "%s" attempted to %s but they were caught by the GASP honeypot.' ),
113
+ $sUsername, $sActionAttempted
114
+ ).' '._wpsf__( 'Probably a BOT.' );
115
+ $this->addToAuditEntry( $sAuditMessage, 3, $sActionAttempted.'_protect_block_gasp_honeypot' );
116
+ $this->setLoginAsFailed( $sActionAttempted.'.gasp.honeypot.fail' );
117
+ $sError = sprintf( _wpsf__( 'You appear to be a bot - terminating %s attempt.' ), $sActionAttempted );
118
+ }
119
+ else {
120
+ $bValid = true;
121
+ }
122
+
123
+ if ( !$bValid ) {
124
+ /** @var ICWP_WPSF_FeatureHandler_LoginProtect $oFO */
125
+ $oFO = $this->getFeature();
126
+ $oFO->setOptInsightsAt( sprintf( 'last_%s_block_at', $sActionAttempted ) );
127
+ $this->setIpTransgressed(); // We now black mark this IP
128
+ throw new Exception( $sError );
129
+ }
130
+ }
131
+
132
+ /**
133
  * @param string $sUsername
134
  * @param string $sActionAttempted - one of 'login', 'register', 'reset-password'
135
+ * @return bool - true if validation successful
136
+ * @throws Exception
137
  */
138
  protected function doGaspChecks( $sUsername, $sActionAttempted = 'login' ) {
139
+ $oDp = $this->loadDP();
140
+ $sGaspCheckBox = $oDp->post( $this->getGaspCheckboxName() );
141
+ $sHoney = $oDp->post( 'icwp_wpsf_login_email' );
142
 
143
+ $bValid = false;
144
  $sDieMessage = '';
145
  if ( empty( $sGaspCheckBox ) ) {
146
+ $sAuditMessage = sprintf(
147
+ _wpsf__( 'User "%s" attempted to %s but GASP checkbox was not present.' ),
148
+ empty( $sUsername ) ? 'unknown' : $sUsername, $sActionAttempted
149
+ ).' '._wpsf__( 'Probably a BOT.' );
150
  $this->addToAuditEntry( $sAuditMessage, 3, $sActionAttempted.'_protect_block_gasp_checkbox' );
151
  $this->doStatIncrement( $sActionAttempted.'.gasp.checkbox.fail' );
 
152
  $sDieMessage = _wpsf__( "You must check that box to say you're not a bot." );
 
153
  }
154
  else if ( !empty( $sHoney ) ) {
155
+ $sAuditMessage = sprintf(
156
+ _wpsf__( 'User "%s" attempted to %s but they were caught by the GASP honeypot.' ),
157
+ empty( $sUsername ) ? 'unknown' : $sUsername, $sActionAttempted
158
+ ).' '._wpsf__( 'Probably a BOT.' );
159
  $this->addToAuditEntry( $sAuditMessage, 3, $sActionAttempted.'_protect_block_gasp_honeypot' );
160
  $this->doStatIncrement( $sActionAttempted.'.gasp.honeypot.fail' );
 
161
  $sDieMessage = sprintf( _wpsf__( 'You appear to be a bot - terminating %s attempt.' ), $sActionAttempted );
162
+ }
163
+ else {
164
+ $bValid = true;
165
  }
166
 
167
+ if ( !$bValid ) {
168
  /** @var ICWP_WPSF_FeatureHandler_LoginProtect $oFO */
169
  $oFO = $this->getFeature();
170
  $oFO->setOptInsightsAt( sprintf( 'last_%s_block_at', $sActionAttempted ) );
171
  $this->setIpTransgressed(); // We now black mark this IP
172
+ throw new Exception( $sDieMessage );
 
 
 
173
  }
174
 
175
+ return $bValid;
176
  }
177
 
178
  /**
src/processors/loginprotect_googleauthenticator.php CHANGED
@@ -85,7 +85,7 @@ class ICWP_WPSF_Processor_LoginProtect_GoogleAuthenticator extends ICWP_WPSF_Pro
85
  * @param int $nSavingUserId
86
  */
87
  public function handleEditOtherUserProfileSubmit( $nSavingUserId ) {
88
- $oDp = $this->loadDataProcessor();
89
 
90
  // Can only edit other users if you're admin/security-admin
91
  if ( $this->getController()->getHasPermissionToManage() ) {
@@ -212,16 +212,15 @@ class ICWP_WPSF_Processor_LoginProtect_GoogleAuthenticator extends ICWP_WPSF_Pro
212
  $oLoginTrack = $this->getLoginTrack();
213
 
214
  // Mulifactor or not
215
- $bNeedToCheckThisFactor = $oFO->isChainedAuth() || !$this->getLoginTrack()->hasSuccessfulFactorAuth();
216
  $bErrorOnFailure = $bNeedToCheckThisFactor && $oLoginTrack->isFinalFactorRemainingToTrack();
217
  $oLoginTrack->addUnSuccessfulFactor( $this->getStub() );
218
 
219
- if ( !$bNeedToCheckThisFactor || empty( $oUser ) || is_wp_error( $oUser ) ) {
220
  return $oUser;
221
  }
222
 
223
- $bIsUser = is_object( $oUser ) && ( $oUser instanceof WP_User );
224
- if ( $bIsUser && $this->hasValidatedProfile( $oUser ) ) {
225
 
226
  $oError = new WP_Error();
227
 
@@ -267,7 +266,7 @@ class ICWP_WPSF_Processor_LoginProtect_GoogleAuthenticator extends ICWP_WPSF_Pro
267
  'value' => '',
268
  'placeholder' => _wpsf__( 'Please use your Google Authenticator App to retrieve your code.' ),
269
  'text' => _wpsf__( 'Google Authenticator Code' ),
270
- 'help_link' => 'http://icwp.io/wpsf42',
271
  'extras' => array(
272
  'onkeyup' => "this.value=this.value.replace(/[^\d]/g,'')"
273
  )
@@ -289,10 +288,10 @@ class ICWP_WPSF_Processor_LoginProtect_GoogleAuthenticator extends ICWP_WPSF_Pro
289
  $aEmailContent[] = $this->generateGaRemovalConfirmationLink();
290
 
291
  $sRecipient = $oUser->get( 'user_email' );
292
- if ( $this->loadDataProcessor()->validEmail( $sRecipient ) ) {
293
  $sEmailSubject = _wpsf__( 'Google Authenticator Removal Confirmation' );
294
  $bSendSuccess = $this->getEmailProcessor()
295
- ->sendEmailTo( $sRecipient, $sEmailSubject, $aEmailContent );
296
  }
297
  return $bSendSuccess;
298
  }
@@ -342,16 +341,15 @@ class ICWP_WPSF_Processor_LoginProtect_GoogleAuthenticator extends ICWP_WPSF_Pro
342
  }
343
 
344
  /**
345
- * @param bool $bIsSuccess
 
346
  */
347
- protected function auditLogin( $bIsSuccess ) {
348
  if ( $bIsSuccess ) {
349
  $this->addToAuditEntry(
350
  sprintf(
351
  _wpsf__( 'User "%s" verified their identity using Google Authenticator Two-Factor Authentication.' ),
352
- $this->loadWpUsers()->getCurrentWpUser()->get( 'user_login' )
353
- ),
354
- 2, 'login_protect_ga_verified'
355
  );
356
  $this->doStatIncrement( 'login.googleauthenticator.verified' );
357
  }
@@ -359,9 +357,7 @@ class ICWP_WPSF_Processor_LoginProtect_GoogleAuthenticator extends ICWP_WPSF_Pro
359
  $this->addToAuditEntry(
360
  sprintf(
361
  _wpsf__( 'User "%s" failed to verify their identity using Google Authenticator Two-Factor Authentication.' ),
362
- $this->loadWpUsers()->getCurrentWpUser()->get( 'user_login' )
363
- ),
364
- 2, 'login_protect_ga_failed'
365
  );
366
  $this->doStatIncrement( 'login.googleauthenticator.fail' );
367
  }
85
  * @param int $nSavingUserId
86
  */
87
  public function handleEditOtherUserProfileSubmit( $nSavingUserId ) {
88
+ $oDp = $this->loadDP();
89
 
90
  // Can only edit other users if you're admin/security-admin
91
  if ( $this->getController()->getHasPermissionToManage() ) {
212
  $oLoginTrack = $this->getLoginTrack();
213
 
214
  // Mulifactor or not
215
+ $bNeedToCheckThisFactor = $oFO->isChainedAuth() || !$this->getLoginTrack()->hasSuccessfulFactor();
216
  $bErrorOnFailure = $bNeedToCheckThisFactor && $oLoginTrack->isFinalFactorRemainingToTrack();
217
  $oLoginTrack->addUnSuccessfulFactor( $this->getStub() );
218
 
219
+ if ( !$bNeedToCheckThisFactor || !( $oUser instanceof WP_User ) || is_wp_error( $oUser ) ) {
220
  return $oUser;
221
  }
222
 
223
+ if ( $this->hasValidatedProfile( $oUser ) ) {
 
224
 
225
  $oError = new WP_Error();
226
 
266
  'value' => '',
267
  'placeholder' => _wpsf__( 'Please use your Google Authenticator App to retrieve your code.' ),
268
  'text' => _wpsf__( 'Google Authenticator Code' ),
269
+ 'help_link' => 'https://icwp.io/wpsf42',
270
  'extras' => array(
271
  'onkeyup' => "this.value=this.value.replace(/[^\d]/g,'')"
272
  )
288
  $aEmailContent[] = $this->generateGaRemovalConfirmationLink();
289
 
290
  $sRecipient = $oUser->get( 'user_email' );
291
+ if ( $this->loadDP()->validEmail( $sRecipient ) ) {
292
  $sEmailSubject = _wpsf__( 'Google Authenticator Removal Confirmation' );
293
  $bSendSuccess = $this->getEmailProcessor()
294
+ ->sendEmailWithWrap( $sRecipient, $sEmailSubject, $aEmailContent );
295
  }
296
  return $bSendSuccess;
297
  }
341
  }
342
 
343
  /**
344
+ * @param WP_User $oUser
345
+ * @param bool $bIsSuccess
346
  */
347
+ protected function auditLogin( $oUser, $bIsSuccess ) {
348
  if ( $bIsSuccess ) {
349
  $this->addToAuditEntry(
350
  sprintf(
351
  _wpsf__( 'User "%s" verified their identity using Google Authenticator Two-Factor Authentication.' ),
352
+ $oUser->user_login ), 2, 'login_protect_ga_verified'
 
 
353
  );
354
  $this->doStatIncrement( 'login.googleauthenticator.verified' );
355
  }
357
  $this->addToAuditEntry(
358
  sprintf(
359
  _wpsf__( 'User "%s" failed to verify their identity using Google Authenticator Two-Factor Authentication.' ),
360
+ $oUser->user_login ), 2, 'login_protect_ga_failed'
 
 
361
  );
362
  $this->doStatIncrement( 'login.googleauthenticator.fail' );
363
  }
src/processors/loginprotect_googlerecaptcha.php CHANGED
@@ -9,92 +9,56 @@ require_once( dirname( __FILE__ ).'/loginprotect_base.php' );
9
  class ICWP_WPSF_Processor_LoginProtect_GoogleRecaptcha extends ICWP_WPSF_Processor_LoginProtect_Base {
10
 
11
  /**
 
12
  */
13
  public function run() {
14
- /** @var ICWP_WPSF_FeatureHandler_LoginProtect $oFO */
15
- $oFO = $this->getFeature();
16
- if ( !$oFO->getIsGoogleRecaptchaReady() ) {
17
- return;
18
- }
19
-
20
  add_action( 'wp_enqueue_scripts', array( $this, 'registerGoogleRecaptchaJs' ), 99 );
21
  add_action( 'login_enqueue_scripts', array( $this, 'registerGoogleRecaptchaJs' ), 99 );
 
22
 
23
- add_action( 'login_form', array( $this, 'printGoogleRecaptchaCheck' ), 100 );
24
- add_filter( 'login_form_middle', array( $this, 'printGoogleRecaptchaCheck_Filter' ), 100 );
25
-
26
- if ( $oFO->getIfSupport3rdParty() ) {
27
- add_action( 'woocommerce_login_form', array( $this, 'printGoogleRecaptchaCheck' ), 100 );
28
- add_action( 'bp_before_registration_submit_buttons', array( $this, 'printGoogleRecaptchaCheck' ), 10 );
29
- add_action( 'bp_signup_validate', array( $this, 'checkGoogleRecaptcha_Action' ), 10 );
 
 
 
 
30
  }
31
-
32
- // before username/password check (20)
33
- add_filter( 'authenticate', array( $this, 'checkLoginForGoogleRecaptcha_Filter' ), 15, 3 );
34
  }
35
 
36
  /**
37
  * @return string
38
  */
39
- public function printGoogleRecaptchaCheck_Filter() {
40
  $this->setRecaptchaToEnqueue();
41
- return $this->getGoogleRecaptchaHtml();
42
  }
43
 
44
  /**
 
45
  */
46
- public function printGoogleRecaptchaCheck() {
47
  $this->setRecaptchaToEnqueue();
48
- echo $this->getGoogleRecaptchaHtml();
49
  }
50
 
51
  /**
52
  * @return string
53
  */
54
- protected function getGoogleRecaptchaHtml() {
55
- $sNonInvisStyle = '<style>@media screen {#rc-imageselect, .icwpg-recaptcha iframe {transform:scale(0.90);-webkit-transform:scale(0.90);transform-origin:0 0;-webkit-transform-origin:0 0;}</style>';
56
- return sprintf( '%s<div class="icwpg-recaptcha"></div>', $this->isRecaptchaInvisible() ? '' : $sNonInvisStyle );
57
- }
58
-
59
- public function checkGoogleRecaptcha_Action() {
60
- try {
61
- $this->checkRequestRecaptcha();
62
- }
63
- catch ( Exception $oE ) {
64
- $this->loadWp()
65
- ->wpDie( 'Google reCAPTCHA checking failed.' );
66
- }
67
  }
68
 
69
  /**
70
- * This jumps in before user password is tested. If we fail the ReCaptcha check, we'll
71
- * block testing of username and password
72
- * @param WP_User|WP_Error $oUser
73
- * @return WP_Error
74
  */
75
- public function checkLoginForGoogleRecaptcha_Filter( $oUser ) {
76
- if ( !$this->loadWp()->isRequestUserLogin() ) {
77
- return $oUser;
78
- }
79
-
80
- // we haven't already failed before now
81
- if ( !is_wp_error( $oUser ) ) {
82
-
83
- try {
84
- $this->checkRequestRecaptcha();
85
- }
86
- catch ( Exception $oE ) {
87
- $sCode = ( $oE->getCode() == 1 ) ? 'shield_google_recaptcha_empty' : 'shield_google_recaptcha_failed';
88
- $oUser = new WP_Error( $sCode, $oE->getMessage() );
89
- }
90
-
91
- if ( is_wp_error( $oUser ) ) {
92
- $this->setLoginAsFailed( 'login.recaptcha.fail' );
93
- }
94
- else {
95
- $this->doStatIncrement( 'login.recaptcha.verified' );
96
- }
97
- }
98
- return $oUser;
99
  }
100
  }
9
  class ICWP_WPSF_Processor_LoginProtect_GoogleRecaptcha extends ICWP_WPSF_Processor_LoginProtect_Base {
10
 
11
  /**
12
+ * We no longer check if recaptcha is ready, we just use run(). So check beforehand.
13
  */
14
  public function run() {
15
+ parent::run();
 
 
 
 
 
16
  add_action( 'wp_enqueue_scripts', array( $this, 'registerGoogleRecaptchaJs' ), 99 );
17
  add_action( 'login_enqueue_scripts', array( $this, 'registerGoogleRecaptchaJs' ), 99 );
18
+ }
19
 
20
+ /**
21
+ * @throws Exception
22
+ */
23
+ protected function performCheckWithException() {
24
+ try {
25
+ $this->checkRequestRecaptcha();
26
+ $this->doStatIncrement( 'login.recaptcha.verified' );
27
+ }
28
+ catch ( Exception $oE ) {
29
+ $this->setLoginAsFailed( 'login.recaptcha.fail' );
30
+ throw $oE;
31
  }
 
 
 
32
  }
33
 
34
  /**
35
  * @return string
36
  */
37
+ public function provideLoginFormItems() {
38
  $this->setRecaptchaToEnqueue();
39
+ return parent::provideLoginFormItems();
40
  }
41
 
42
  /**
43
+ * @return void
44
  */
45
+ public function printLoginFormItems() {
46
  $this->setRecaptchaToEnqueue();
47
+ parent::printLoginFormItems();
48
  }
49
 
50
  /**
51
  * @return string
52
  */
53
+ protected function buildLoginFormItems() {
54
+ return $this->getGoogleRecaptchaHtml();
 
 
 
 
 
 
 
 
 
 
 
55
  }
56
 
57
  /**
58
+ * @return string
 
 
 
59
  */
60
+ private function getGoogleRecaptchaHtml() {
61
+ $sNonInvisStyle = '<style>@media screen {#rc-imageselect, .icwpg-recaptcha iframe {transform:scale(0.90);-webkit-transform:scale(0.90);transform-origin:0 0;-webkit-transform-origin:0 0;}</style>';
62
+ return sprintf( '%s<div class="icwpg-recaptcha"></div>', $this->isRecaptchaInvisible() ? '' : $sNonInvisStyle );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
63
  }
64
  }
src/processors/loginprotect_intent.php CHANGED
@@ -13,6 +13,11 @@ class ICWP_WPSF_Processor_LoginProtect_Intent extends ICWP_WPSF_Processor_BaseWp
13
  */
14
  private $oLoginTrack;
15
 
 
 
 
 
 
16
  /**
17
  */
18
  public function run() {
@@ -21,6 +26,9 @@ class ICWP_WPSF_Processor_LoginProtect_Intent extends ICWP_WPSF_Processor_BaseWp
21
  add_action( 'init', array( $this, 'onWpInit' ), 0 );
22
  add_action( 'wp_logout', array( $this, 'onWpLogout' ) );
23
 
 
 
 
24
  if ( $oFO->getIfSupport3rdParty() ) {
25
  add_action( 'wc_social_login_before_user_login', array( $this, 'onWcSocialLogin' ) );
26
  }
@@ -74,8 +82,7 @@ class ICWP_WPSF_Processor_LoginProtect_Intent extends ICWP_WPSF_Processor_BaseWp
74
 
75
  // process the current login intent
76
  if ( $oWpUsers->isUserLoggedIn() ) {
77
-
78
- if ( $this->isCurrentUserSubjectToLoginIntent() ) {
79
  $this->processLoginIntent();
80
  }
81
  else if ( $this->hasLoginIntent() ) {
@@ -90,11 +97,8 @@ class ICWP_WPSF_Processor_LoginProtect_Intent extends ICWP_WPSF_Processor_BaseWp
90
  /**
91
  * hooked to 'init' and only run if a user is logged in
92
  */
93
- protected function processLoginIntent() {
94
  $oWpUsers = $this->loadWpUsers();
95
- if ( !$oWpUsers->isUserLoggedIn() ) {
96
- return;
97
- }
98
 
99
  /** @var ICWP_WPSF_FeatureHandler_LoginProtect $oFO */
100
  $oFO = $this->getFeature();
@@ -111,16 +115,7 @@ class ICWP_WPSF_Processor_LoginProtect_Intent extends ICWP_WPSF_Processor_BaseWp
111
  return;
112
  }
113
 
114
- $oLoginTracker = $this->getLoginTrack();
115
- do_action( $oFO->prefix( 'login-intent-validation' ) );
116
- if ( $oFO->isChainedAuth() ) {
117
- $bLoginIntentValidated = !$oLoginTracker->hasUnSuccessfulFactorAuth();
118
- }
119
- else {
120
- $bLoginIntentValidated = $oLoginTracker->hasSuccessfulFactorAuth();
121
- }
122
-
123
- if ( $bLoginIntentValidated ) {
124
 
125
  if ( $oDp->post( 'skip_mfa' ) === 'Y' ) { // store the browser hash
126
  $oFO->addMfaLoginHash( $oWpUsers->getCurrentWpUser() );
@@ -130,7 +125,7 @@ class ICWP_WPSF_Processor_LoginProtect_Intent extends ICWP_WPSF_Processor_BaseWp
130
  $this->loadAdminNoticesProcessor()->addFlashMessage(
131
  _wpsf__( 'Success' ).'! '._wpsf__( 'Thank you for authenticating your login.' ) );
132
 
133
- $oFO->setOptInsightsAt( 'last_idle_logout_at' );
134
  }
135
  else {
136
  $this->loadAdminNoticesProcessor()->addFlashMessage(
@@ -159,7 +154,7 @@ class ICWP_WPSF_Processor_LoginProtect_Intent extends ICWP_WPSF_Processor_BaseWp
159
  /** @var ICWP_WPSF_FeatureHandler_LoginProtect $oFO */
160
  $oFO = $this->getFeature();
161
 
162
- $this->resetLoginIntent();
163
 
164
  // support for WooCommerce Social Login
165
  if ( $oFO->getIfSupport3rdParty() ) {
@@ -175,10 +170,9 @@ class ICWP_WPSF_Processor_LoginProtect_Intent extends ICWP_WPSF_Processor_BaseWp
175
  public function initLoginIntent( $oUser ) {
176
  if ( $oUser instanceof WP_User ) {
177
 
178
- /** @var ICWP_WPSF_FeatureHandler_LoginProtect $oFO */
179
- $oFO = $this->getFeature();
180
- if ( !$oFO->canUserMfaSkip( $oUser ) ) {
181
- $oF = $this->getFeature();
182
  $nTimeout = (int)apply_filters(
183
  $oF->prefix( 'login_intent_timeout' ),
184
  $oF->getDef( 'login_intent_timeout' )
@@ -222,13 +216,15 @@ class ICWP_WPSF_Processor_LoginProtect_Intent extends ICWP_WPSF_Processor_BaseWp
222
  }
223
 
224
  /**
 
 
 
225
  * @return bool
226
  */
227
- protected function isCurrentUserSubjectToLoginIntent() {
228
  /** @var ICWP_WPSF_FeatureHandler_LoginProtect $oFO */
229
  $oFO = $this->getFeature();
230
- return !$oFO->canUserMfaSkip( $this->loadWpUsers()->getCurrentWpUser() )
231
- && apply_filters( $this->prefix( 'user_subject_to_login_intent' ), false );
232
  }
233
 
234
  /**
@@ -305,7 +301,7 @@ class ICWP_WPSF_Processor_LoginProtect_Intent extends ICWP_WPSF_Processor_BaseWp
305
  'login_intent_flag' => $oFO->getLoginIntentRequestFlag()
306
  ),
307
  'hrefs' => array(
308
- 'form_action' => $this->loadDataProcessor()->getRequestUri(),
309
  'css_bootstrap' => $oCon->getPluginUrl_Css( 'bootstrap4.min.css' ),
310
  'js_bootstrap' => $oCon->getPluginUrl_Js( 'bootstrap4.min.js' ),
311
  'shield_logo' => 'https://ps.w.org/wp-simple-firewall/assets/banner-772x250.png',
@@ -314,7 +310,8 @@ class ICWP_WPSF_Processor_LoginProtect_Intent extends ICWP_WPSF_Processor_BaseWp
314
  'favicon' => $oCon->getPluginUrl_Image( 'pluginlogo_24x24.png' ),
315
  ),
316
  'flags' => array(
317
- 'can_skip_mfa' => $oFO->getMfaSkipEnabled()
 
318
  )
319
  );
320
 
@@ -366,6 +363,37 @@ class ICWP_WPSF_Processor_LoginProtect_Intent extends ICWP_WPSF_Processor_BaseWp
366
  return $this->oLoginTrack;
367
  }
368
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
369
  /**
370
  * @param ICWP_WPSF_Processor_LoginProtect_Track $oLoginTrack
371
  * @return $this
13
  */
14
  private $oLoginTrack;
15
 
16
+ /**
17
+ * @var bool
18
+ */
19
+ private $bLoginIntentProcessed;
20
+
21
  /**
22
  */
23
  public function run() {
26
  add_action( 'init', array( $this, 'onWpInit' ), 0 );
27
  add_action( 'wp_logout', array( $this, 'onWpLogout' ) );
28
 
29
+ // 100 priority is important as this takes priority
30
+ add_filter( $oFO->prefix( 'user_subject_to_login_intent' ), array( $this, 'applyUserCanMfaSkip' ), 100, 2 );
31
+
32
  if ( $oFO->getIfSupport3rdParty() ) {
33
  add_action( 'wc_social_login_before_user_login', array( $this, 'onWcSocialLogin' ) );
34
  }
82
 
83
  // process the current login intent
84
  if ( $oWpUsers->isUserLoggedIn() ) {
85
+ if ( $this->isUserSubjectToLoginIntent() ) {
 
86
  $this->processLoginIntent();
87
  }
88
  else if ( $this->hasLoginIntent() ) {
97
  /**
98
  * hooked to 'init' and only run if a user is logged in
99
  */
100
+ private function processLoginIntent() {
101
  $oWpUsers = $this->loadWpUsers();
 
 
 
102
 
103
  /** @var ICWP_WPSF_FeatureHandler_LoginProtect $oFO */
104
  $oFO = $this->getFeature();
115
  return;
116
  }
117
 
118
+ if ( $this->getIsLoginIntentValid() ) {
 
 
 
 
 
 
 
 
 
119
 
120
  if ( $oDp->post( 'skip_mfa' ) === 'Y' ) { // store the browser hash
121
  $oFO->addMfaLoginHash( $oWpUsers->getCurrentWpUser() );
125
  $this->loadAdminNoticesProcessor()->addFlashMessage(
126
  _wpsf__( 'Success' ).'! '._wpsf__( 'Thank you for authenticating your login.' ) );
127
 
128
+ $oFO->setOptInsightsAt( 'last_2fa_login_at' );
129
  }
130
  else {
131
  $this->loadAdminNoticesProcessor()->addFlashMessage(
154
  /** @var ICWP_WPSF_FeatureHandler_LoginProtect $oFO */
155
  $oFO = $this->getFeature();
156
 
157
+ $this->removeLoginIntent();
158
 
159
  // support for WooCommerce Social Login
160
  if ( $oFO->getIfSupport3rdParty() ) {
170
  public function initLoginIntent( $oUser ) {
171
  if ( $oUser instanceof WP_User ) {
172
 
173
+ /** @var ICWP_WPSF_FeatureHandler_LoginProtect $oF */
174
+ $oF = $this->getFeature();
175
+ if ( !$oF->canUserMfaSkip( $oUser ) ) {
 
176
  $nTimeout = (int)apply_filters(
177
  $oF->prefix( 'login_intent_timeout' ),
178
  $oF->getDef( 'login_intent_timeout' )
216
  }
217
 
218
  /**
219
+ * Must be set with a higher priority than other filters as it will override them
220
+ * @param bool $bIsSubjectTo
221
+ * @param WP_User $oUser
222
  * @return bool
223
  */
224
+ public function applyUserCanMfaSkip( $bIsSubjectTo, $oUser ) {
225
  /** @var ICWP_WPSF_FeatureHandler_LoginProtect $oFO */
226
  $oFO = $this->getFeature();
227
+ return $bIsSubjectTo && !$oFO->canUserMfaSkip( $oUser );
 
228
  }
229
 
230
  /**
301
  'login_intent_flag' => $oFO->getLoginIntentRequestFlag()
302
  ),
303
  'hrefs' => array(
304
+ 'form_action' => $this->loadDP()->getRequestUri(),
305
  'css_bootstrap' => $oCon->getPluginUrl_Css( 'bootstrap4.min.css' ),
306
  'js_bootstrap' => $oCon->getPluginUrl_Js( 'bootstrap4.min.js' ),
307
  'shield_logo' => 'https://ps.w.org/wp-simple-firewall/assets/banner-772x250.png',
310
  'favicon' => $oCon->getPluginUrl_Image( 'pluginlogo_24x24.png' ),
311
  ),
312
  'flags' => array(
313
+ 'can_skip_mfa' => $oFO->getMfaSkipEnabled(),
314
+ 'show_what_is_this' => !$oFO->isPremium(), // white label mitigation
315
  )
316
  );
317
 
363
  return $this->oLoginTrack;
364
  }
365
 
366
+ /**
367
+ * assume that a user is logged in.
368
+ * @return bool
369
+ */
370
+ private function getIsLoginIntentValid() {
371
+ /** @var ICWP_WPSF_FeatureHandler_LoginProtect $oFO */
372
+ $oFO = $this->getFeature();
373
+ if ( !$this->isLoginIntentProcessed() ) {
374
+ // This action sets up the necessary login tracker info
375
+ do_action( $oFO->prefix( 'login-intent-validation' ), $this->loadWpUsers()->getCurrentWpUser() );
376
+ $this->setLoginIntentProcessed();
377
+ }
378
+ $oTrk = $this->getLoginTrack();
379
+ return $oFO->isChainedAuth() ? $oTrk->hasUnSuccessfulFactor() : $oTrk->hasSuccessfulFactor();
380
+ }
381
+
382
+ /**
383
+ * @return bool
384
+ */
385
+ public function isLoginIntentProcessed() {
386
+ return (bool)$this->bLoginIntentProcessed;
387
+ }
388
+
389
+ /**
390
+ * @return $this
391
+ */
392
+ public function setLoginIntentProcessed() {
393
+ $this->bLoginIntentProcessed = true;
394
+ return $this;
395
+ }
396
+
397
  /**
398
  * @param ICWP_WPSF_Processor_LoginProtect_Track $oLoginTrack
399
  * @return $this
src/processors/loginprotect_intentprovider_base.php CHANGED
@@ -29,7 +29,7 @@ abstract class ICWP_WPSF_Processor_LoginProtect_IntentProviderBase extends ICWP_
29
  }
30
 
31
  // Necessary so we don't show user intent to people without it
32
- add_filter( $oFO->prefix( 'user_subject_to_login_intent' ), array( $this, 'userSubjectToLoginIntent_Filter' ) );
33
 
34
  add_action( 'show_user_profile', array( $this, 'addOptionsToUserProfile' ) );
35
  add_action( 'personal_options_update', array( $this, 'handleUserProfileSubmit' ) );
@@ -41,10 +41,10 @@ abstract class ICWP_WPSF_Processor_LoginProtect_IntentProviderBase extends ICWP_
41
  }
42
 
43
  /**
 
44
  */
45
- public function validateLoginIntent() {
46
  $oLoginTrack = $this->getLoginTrack();
47
- $oUser = $this->loadWpUsers()->getCurrentWpUser();
48
 
49
  $sFactor = $this->getStub();
50
  if ( !$this->isProfileReady( $oUser ) ) {
@@ -53,11 +53,11 @@ abstract class ICWP_WPSF_Processor_LoginProtect_IntentProviderBase extends ICWP_
53
  else {
54
  if ( $this->processOtp( $oUser, $this->fetchCodeFromRequest() ) ) {
55
  $oLoginTrack->addSuccessfulFactor( $sFactor );
56
- $this->auditLogin( true );
57
  }
58
  else {
59
  $oLoginTrack->addUnSuccessfulFactor( $sFactor );
60
- $this->auditLogin( false );
61
  }
62
  }
63
  }
@@ -74,6 +74,10 @@ abstract class ICWP_WPSF_Processor_LoginProtect_IntentProviderBase extends ICWP_
74
  * @return bool
75
  */
76
  protected function hasValidatedProfile( $oUser ) {
 
 
 
 
77
  $oWpUsers = $this->loadWpUsers();
78
 
79
  $sKey = $this->getStub().'_validated';
@@ -251,9 +255,10 @@ abstract class ICWP_WPSF_Processor_LoginProtect_IntentProviderBase extends ICWP_
251
  abstract public function addLoginIntentField( $aFields );
252
 
253
  /**
254
- * @param bool $bIsSuccess
 
255
  */
256
- abstract protected function auditLogin( $bIsSuccess );
257
 
258
  /**
259
  * @return string
@@ -270,11 +275,12 @@ abstract class ICWP_WPSF_Processor_LoginProtect_IntentProviderBase extends ICWP_
270
  }
271
 
272
  /**
273
- * @param bool $bIsSubjectTo
 
274
  * @return bool
275
  */
276
- public function userSubjectToLoginIntent_Filter( $bIsSubjectTo ) {
277
- return ( $bIsSubjectTo || $this->getCurrentUserHasValidatedProfile() );
278
  }
279
 
280
  /**
29
  }
30
 
31
  // Necessary so we don't show user intent to people without it
32
+ add_filter( $oFO->prefix( 'user_subject_to_login_intent' ), array( $this, 'filterUserSubjectToIntent' ), 10, 2 );
33
 
34
  add_action( 'show_user_profile', array( $this, 'addOptionsToUserProfile' ) );
35
  add_action( 'personal_options_update', array( $this, 'handleUserProfileSubmit' ) );
41
  }
42
 
43
  /**
44
+ * @param WP_User $oUser
45
  */
46
+ public function validateLoginIntent( $oUser ) {
47
  $oLoginTrack = $this->getLoginTrack();
 
48
 
49
  $sFactor = $this->getStub();
50
  if ( !$this->isProfileReady( $oUser ) ) {
53
  else {
54
  if ( $this->processOtp( $oUser, $this->fetchCodeFromRequest() ) ) {
55
  $oLoginTrack->addSuccessfulFactor( $sFactor );
56
+ $this->auditLogin( $oUser, true );
57
  }
58
  else {
59
  $oLoginTrack->addUnSuccessfulFactor( $sFactor );
60
+ $this->auditLogin( $oUser, false );
61
  }
62
  }
63
  }
74
  * @return bool
75
  */
76
  protected function hasValidatedProfile( $oUser ) {
77
+ if ( !( $oUser instanceof WP_User ) ) {
78
+ return false;
79
+ }
80
+
81
  $oWpUsers = $this->loadWpUsers();
82
 
83
  $sKey = $this->getStub().'_validated';
255
  abstract public function addLoginIntentField( $aFields );
256
 
257
  /**
258
+ * @param WP_User $oUser
259
+ * @param bool $bIsSuccess
260
  */
261
+ abstract protected function auditLogin( $oUser, $bIsSuccess );
262
 
263
  /**
264
  * @return string
275
  }
276
 
277
  /**
278
+ * @param bool $bIsSubjectTo
279
+ * @param WP_User $oUser
280
  * @return bool
281
  */
282
+ public function filterUserSubjectToIntent( $bIsSubjectTo, $oUser ) {
283
+ return ( $bIsSubjectTo || $this->hasValidatedProfile( $oUser ) );
284
  }
285
 
286
  /**
src/processors/loginprotect_track.php CHANGED
@@ -108,14 +108,14 @@ class ICWP_WPSF_Processor_LoginProtect_Track {
108
  /**
109
  * @return bool
110
  */
111
- public function hasSuccessfulFactorAuth() {
112
  return ( $this->getCountFactorsSuccessful() > 0 );
113
  }
114
 
115
  /**
116
  * @return bool
117
  */
118
- public function hasUnSuccessfulFactorAuth() {
119
  return ( $this->getCountFactorsUnsuccessful() > 0 );
120
  }
121
 
108
  /**
109
  * @return bool
110
  */
111
+ public function hasSuccessfulFactor() {
112
  return ( $this->getCountFactorsSuccessful() > 0 );
113
  }
114
 
115
  /**
116
  * @return bool
117
  */
118
+ public function hasUnSuccessfulFactor() {
119
  return ( $this->getCountFactorsUnsuccessful() > 0 );
120
  }
121
 
src/processors/loginprotect_twofactorauth.php CHANGED
@@ -36,16 +36,15 @@ class ICWP_WPSF_Processor_LoginProtect_TwoFactorAuth extends ICWP_WPSF_Processor
36
  }
37
 
38
  /**
39
- * @param bool $bIsSuccess
 
40
  */
41
- protected function auditLogin( $bIsSuccess ) {
42
  if ( $bIsSuccess ) {
43
  $this->addToAuditEntry(
44
  sprintf(
45
  _wpsf__( 'User "%s" verified their identity using Email Two-Factor Authentication.' ),
46
- $this->loadWpUsers()->getCurrentWpUser()->get( 'user_login' )
47
- ),
48
- 2, 'login_protect_two_factor_verified'
49
  );
50
  $this->doStatIncrement( 'login.twofactor.verified' );
51
  }
@@ -53,9 +52,7 @@ class ICWP_WPSF_Processor_LoginProtect_TwoFactorAuth extends ICWP_WPSF_Processor
53
  $this->addToAuditEntry(
54
  sprintf(
55
  _wpsf__( 'User "%s" failed to verify their identity using Email Two-Factor Authentication.' ),
56
- $this->loadWpUsers()->getCurrentWpUser()->get( 'user_login' )
57
- ),
58
- 2, 'login_protect_two_factor_failed'
59
  );
60
  $this->doStatIncrement( 'login.twofactor.failed' );
61
  }
@@ -86,7 +83,7 @@ class ICWP_WPSF_Processor_LoginProtect_TwoFactorAuth extends ICWP_WPSF_Processor
86
  'value' => $this->fetchCodeFromRequest(),
87
  'placeholder' => _wpsf__( 'This code was just sent to your registered Email address.' ),
88
  'text' => _wpsf__( 'Email OTP' ),
89
- 'help_link' => 'http://icwp.io/3t'
90
  );
91
  }
92
  return $aFields;
@@ -108,13 +105,11 @@ class ICWP_WPSF_Processor_LoginProtect_TwoFactorAuth extends ICWP_WPSF_Processor
108
  * @param WP_User $oUser
109
  * @return bool
110
  */
111
- public function getIsUserSubjectToEmailAuthentication( $oUser ) {
112
- $nUserLevel = $oUser->get( 'user_level' );
113
-
114
- $aSubjectedUserLevels = $this->getFeature()->getOpt( 'two_factor_auth_user_roles' );
115
- if ( empty( $aSubjectedUserLevels ) || !is_array( $aSubjectedUserLevels ) ) {
116
- $aSubjectedUserLevels = array( 1, 2, 3, 8 ); // by default all roles except subscribers!
117
- }
118
 
119
  // see: https://codex.wordpress.org/Roles_and_Capabilities#User_Level_to_Role_Conversion
120
 
@@ -188,13 +183,17 @@ class ICWP_WPSF_Processor_LoginProtect_TwoFactorAuth extends ICWP_WPSF_Processor
188
  sprintf( _wpsf__( 'Username: %s' ), $oUser->get( 'user_login' ) ),
189
  sprintf( _wpsf__( 'IP Address: %s' ), $sIpAddress ),
190
  '',
191
- sprintf( '- <a href="%s" target="_blank">%s</a>', 'http://icwp.io/96', _wpsf__( 'Why no login link?' ) ),
192
- ''
193
  );
 
 
 
 
 
 
194
  $sEmailSubject = _wpsf__( 'Two-Factor Login Verification' );
195
 
196
  $bResult = $this->getEmailProcessor()
197
- ->sendEmailTo( $sEmail, $sEmailSubject, $aMessage );
198
  if ( $bResult ) {
199
  $sAuditMessage = sprintf( _wpsf__( 'User "%s" was sent an email to verify their Identity using Two-Factor Login Auth for IP address "%s".' ), $oUser->get( 'user_login' ), $sIpAddress );
200
  $this->addToAuditEntry( $sAuditMessage, 2, 'login_protect_two_factor_email_send' );
36
  }
37
 
38
  /**
39
+ * @param WP_User $oUser
40
+ * @param bool $bIsSuccess
41
  */
42
+ protected function auditLogin( $oUser, $bIsSuccess ) {
43
  if ( $bIsSuccess ) {
44
  $this->addToAuditEntry(
45
  sprintf(
46
  _wpsf__( 'User "%s" verified their identity using Email Two-Factor Authentication.' ),
47
+ $oUser->user_login ), 2, 'login_protect_two_factor_verified'
 
 
48
  );
49
  $this->doStatIncrement( 'login.twofactor.verified' );
50
  }
52
  $this->addToAuditEntry(
53
  sprintf(
54
  _wpsf__( 'User "%s" failed to verify their identity using Email Two-Factor Authentication.' ),
55
+ $oUser->user_login ), 2, 'login_protect_two_factor_failed'
 
 
56
  );
57
  $this->doStatIncrement( 'login.twofactor.failed' );
58
  }
83
  'value' => $this->fetchCodeFromRequest(),
84
  'placeholder' => _wpsf__( 'This code was just sent to your registered Email address.' ),
85
  'text' => _wpsf__( 'Email OTP' ),
86
+ 'help_link' => 'https://icwp.io/3t'
87
  );
88
  }
89
  return $aFields;
105
  * @param WP_User $oUser
106
  * @return bool
107
  */
108
+ private function getIsUserSubjectToEmailAuthentication( $oUser ) {
109
+ /** @var ICWP_WPSF_FeatureHandler_LoginProtect $oFO */
110
+ $oFO = $this->getFeature();
111
+ $nUserLevel = $oUser->user_level;
112
+ $aSubjectedUserLevels = $oFO->getEmail2FaRoles();
 
 
113
 
114
  // see: https://codex.wordpress.org/Roles_and_Capabilities#User_Level_to_Role_Conversion
115
 
183
  sprintf( _wpsf__( 'Username: %s' ), $oUser->get( 'user_login' ) ),
184
  sprintf( _wpsf__( 'IP Address: %s' ), $sIpAddress ),
185
  '',
 
 
186
  );
187
+
188
+ if ( !$this->getController()->isRelabelled() ) {
189
+ $aMessage[] = sprintf( '- <a href="%s" target="_blank">%s</a>', 'https://icwp.io/96', _wpsf__( 'Why no login link?' ) );
190
+ $aContent[] = '';
191
+ }
192
+
193
  $sEmailSubject = _wpsf__( 'Two-Factor Login Verification' );
194
 
195
  $bResult = $this->getEmailProcessor()
196
+ ->sendEmailWithWrap( $sEmail, $sEmailSubject, $aMessage );
197
  if ( $bResult ) {
198
  $sAuditMessage = sprintf( _wpsf__( 'User "%s" was sent an email to verify their Identity using Two-Factor Login Auth for IP address "%s".' ), $oUser->get( 'user_login' ), $sIpAddress );
199
  $this->addToAuditEntry( $sAuditMessage, 2, 'login_protect_two_factor_email_send' );
src/processors/loginprotect_wplogin.php CHANGED
@@ -84,7 +84,7 @@ class ICWP_WPSF_Processor_LoginProtect_WpLogin extends ICWP_WPSF_Processor_BaseW
84
  * @return bool
85
  */
86
  protected function checkForUnsupportedConfiguration() {
87
- $oDp = $this->loadDataProcessor();
88
  $aRequestParts = $oDp->getRequestUriParts();
89
  if ( $aRequestParts === false || empty( $aRequestParts['path'] ) ) {
90
 
@@ -108,7 +108,7 @@ class ICWP_WPSF_Processor_LoginProtect_WpLogin extends ICWP_WPSF_Processor_BaseW
108
 
109
  // Next block option is where it's a direct attempt to access the old login URL
110
  if ( !$bDoBlock ) {
111
- $sPath = trim( $this->loadDataProcessor()->getRequestPath(), '/' );
112
  $aPossiblePaths = array(
113
  trim( home_url( 'wp-login.php', 'relative' ), '/' ),
114
  trim( home_url( 'wp-signup.php', 'relative' ), '/' ),
@@ -181,7 +181,7 @@ class ICWP_WPSF_Processor_LoginProtect_WpLogin extends ICWP_WPSF_Processor_BaseW
181
  * @return string
182
  */
183
  public function blockRegisterUrlRedirect( $sUrl ) {
184
- $aParts = $this->loadDataProcessor()->getRequestUriParts();
185
  if ( is_array( $aParts ) && !empty( $aParts[ 'path' ] ) && strpos( $aParts[ 'path' ], 'wp-register.php' ) ) {
186
  $this->doWpLoginFailedRedirect404();
187
  die();
84
  * @return bool
85
  */
86
  protected function checkForUnsupportedConfiguration() {
87
+ $oDp = $this->loadDP();
88
  $aRequestParts = $oDp->getRequestUriParts();
89
  if ( $aRequestParts === false || empty( $aRequestParts['path'] ) ) {
90
 
108
 
109
  // Next block option is where it's a direct attempt to access the old login URL
110
  if ( !$bDoBlock ) {
111
+ $sPath = trim( $this->loadDP()->getRequestPath(), '/' );
112
  $aPossiblePaths = array(
113
  trim( home_url( 'wp-login.php', 'relative' ), '/' ),
114
  trim( home_url( 'wp-signup.php', 'relative' ), '/' ),
181
  * @return string
182
  */
183
  public function blockRegisterUrlRedirect( $sUrl ) {
184
+ $aParts = $this->loadDP()->getRequestUriParts();
185
  if ( is_array( $aParts ) && !empty( $aParts[ 'path' ] ) && strpos( $aParts[ 'path' ], 'wp-register.php' ) ) {
186
  $this->doWpLoginFailedRedirect404();
187
  die();
src/processors/loginprotect_yubikey.php CHANGED
@@ -29,13 +29,14 @@ class ICWP_WPSF_Processor_LoginProtect_Yubikey extends ICWP_WPSF_Processor_Login
29
  */
30
  public function addOptionsToUserProfile( $oUser ) {
31
  $oCon = $this->getController();
 
32
 
33
  $bValidatedProfile = $this->hasValidatedProfile( $oUser );
34
  $aData = array(
35
  'has_validated_profile' => $bValidatedProfile,
36
- 'is_my_user_profile' => ( $oUser->ID == $this->loadWpUsers()->getCurrentWpUserId() ),
37
  'i_am_valid_admin' => $oCon->getHasPermissionToManage(),
38
- 'user_to_edit_is_admin' => $this->loadWpUsers()->isUserAdmin( $oUser ),
39
  'strings' => array(
40
  'description_otp_code' => _wpsf__( 'This is your unique Yubikey Device ID.' ),
41
  'description_otp' => _wpsf__( 'Provide a One Time Password from your Yubikey.' ),
@@ -116,7 +117,7 @@ class ICWP_WPSF_Processor_LoginProtect_Yubikey extends ICWP_WPSF_Processor_Login
116
  $oFO = $this->getFeature();
117
  $oLoginTrack = $this->getLoginTrack();
118
 
119
- $bNeedToCheckThisFactor = $oFO->isChainedAuth() || !$oLoginTrack->hasSuccessfulFactorAuth();
120
  $bErrorOnFailure = $bNeedToCheckThisFactor && $oLoginTrack->isFinalFactorRemainingToTrack();
121
  $oLoginTrack->addUnSuccessfulFactor( ICWP_WPSF_Processor_LoginProtect_Track::Factor_Yubikey );
122
 
@@ -208,24 +209,21 @@ class ICWP_WPSF_Processor_LoginProtect_Yubikey extends ICWP_WPSF_Processor_Login
208
  }
209
 
210
  /**
211
- * @param bool $bIsSuccess
 
212
  */
213
- protected function auditLogin( $bIsSuccess ) {
214
  if ( $bIsSuccess ) {
215
  $this->addToAuditEntry(
216
- sprintf( _wpsf__( 'User "%s" successfully logged in using a validated Yubikey One Time Password.' ), $this->loadWpUsers()
217
- ->getCurrentWpUser()
218
- ->get( 'user_login' ) ),
219
- 2, 'login_protect_yubikey_login_success'
220
  );
221
  $this->doStatIncrement( 'login.yubikey.verified' );
222
  }
223
  else {
224
  $this->addToAuditEntry(
225
- sprintf( _wpsf__( 'User "%s" failed to verify their identity using Yubikey One Time Password.' ), $this->loadWpUsers()
226
- ->getCurrentWpUser()
227
- ->get( 'user_login' ) ),
228
- 2, 'login_protect_yubikey_failed'
229
  );
230
  $this->doStatIncrement( 'login.yubikey.failed' );
231
  }
@@ -243,7 +241,7 @@ class ICWP_WPSF_Processor_LoginProtect_Yubikey extends ICWP_WPSF_Processor_Login
243
  'placeholder' => _wpsf__( 'Use your Yubikey to generate a new code.' ),
244
  'value' => '',
245
  'text' => _wpsf__( 'Yubikey OTP' ),
246
- 'help_link' => 'http://icwp.io/4i'
247
  );
248
  }
249
  return $aFields;
29
  */
30
  public function addOptionsToUserProfile( $oUser ) {
31
  $oCon = $this->getController();
32
+ $oWpUsers = $this->loadWpUsers();
33
 
34
  $bValidatedProfile = $this->hasValidatedProfile( $oUser );
35
  $aData = array(
36
  'has_validated_profile' => $bValidatedProfile,
37
+ 'is_my_user_profile' => ( $oUser->ID == $oWpUsers->getCurrentWpUserId() ),
38
  'i_am_valid_admin' => $oCon->getHasPermissionToManage(),
39
+ 'user_to_edit_is_admin' => $oWpUsers->isUserAdmin( $oUser ),
40
  'strings' => array(
41
  'description_otp_code' => _wpsf__( 'This is your unique Yubikey Device ID.' ),
42
  'description_otp' => _wpsf__( 'Provide a One Time Password from your Yubikey.' ),
117
  $oFO = $this->getFeature();
118
  $oLoginTrack = $this->getLoginTrack();
119
 
120
+ $bNeedToCheckThisFactor = $oFO->isChainedAuth() || !$oLoginTrack->hasSuccessfulFactor();
121
  $bErrorOnFailure = $bNeedToCheckThisFactor && $oLoginTrack->isFinalFactorRemainingToTrack();
122
  $oLoginTrack->addUnSuccessfulFactor( ICWP_WPSF_Processor_LoginProtect_Track::Factor_Yubikey );
123
 
209
  }
210
 
211
  /**
212
+ * @param WP_User $oUser
213
+ * @param bool $bIsSuccess
214
  */
215
+ protected function auditLogin( $oUser, $bIsSuccess ) {
216
  if ( $bIsSuccess ) {
217
  $this->addToAuditEntry(
218
+ sprintf( _wpsf__( 'User "%s" successfully logged in using a validated Yubikey One Time Password.' ),
219
+ $oUser->user_login ), 2, 'login_protect_yubikey_login_success'
 
 
220
  );
221
  $this->doStatIncrement( 'login.yubikey.verified' );
222
  }
223
  else {
224
  $this->addToAuditEntry(
225
+ sprintf( _wpsf__( 'User "%s" failed to verify their identity using Yubikey One Time Password.' ),
226
+ $oUser->user_login ), 2, 'login_protect_yubikey_failed'
 
 
227
  );
228
  $this->doStatIncrement( 'login.yubikey.failed' );
229
  }
241
  'placeholder' => _wpsf__( 'Use your Yubikey to generate a new code.' ),
242
  'value' => '',
243
  'text' => _wpsf__( 'Yubikey OTP' ),
244
+ 'help_link' => 'https://icwp.io/4i'
245
  );
246
  }
247
  return $aFields;
src/processors/plugin.php CHANGED
@@ -34,6 +34,10 @@ class ICWP_WPSF_Processor_Plugin extends ICWP_WPSF_Processor_BasePlugin {
34
  $this->getTrackingProcessor()->run();
35
  }
36
 
 
 
 
 
37
  add_action( 'wp_loaded', array( $this, 'onWpLoaded' ) );
38
  add_action( 'in_admin_footer', array( $this, 'printVisitorIpFooter' ) );
39
 
@@ -44,7 +48,7 @@ class ICWP_WPSF_Processor_Plugin extends ICWP_WPSF_Processor_BasePlugin {
44
 
45
  case 'importexport_export':
46
  case 'importexport_handshake':
47
- case 'importexport_updatenotify':
48
  if ( $oFO->isImportExportPermitted() ) {
49
  $this->getSubProcessorImportExport()->runAction();
50
  }
34
  $this->getTrackingProcessor()->run();
35
  }
36
 
37
+ if ( $oFO->isImportExportPermitted() ) {
38
+ $this->getSubProcessorImportExport()->run();
39
+ }
40
+
41
  add_action( 'wp_loaded', array( $this, 'onWpLoaded' ) );
42
  add_action( 'in_admin_footer', array( $this, 'printVisitorIpFooter' ) );
43
 
48
 
49
  case 'importexport_export':
50
  case 'importexport_handshake':
51
+ case 'importexport_updatenotified':
52
  if ( $oFO->isImportExportPermitted() ) {
53
  $this->getSubProcessorImportExport()->runAction();
54
  }
src/processors/plugin_badge.php CHANGED
@@ -24,7 +24,7 @@ class ICWP_WPSF_Processor_Plugin_Badge extends ICWP_WPSF_Processor_BaseWpsf {
24
  }
25
 
26
  public function includeJquery() {
27
- wp_enqueue_script( 'jquery', null, array(), false, true );
28
  }
29
 
30
  /**
@@ -32,11 +32,15 @@ class ICWP_WPSF_Processor_Plugin_Badge extends ICWP_WPSF_Processor_BaseWpsf {
32
  * @return array
33
  */
34
  public function gatherPluginWidgetContent( $aContent ) {
 
 
 
35
 
36
- $sFooter = sprintf( _wpsf__( '%s is provided by %s' ),
37
- $this->getController()->getHumanName(),
38
- sprintf( '<a href="%s">One Dollar Plugin</a>', 'http://icwp.io/7f' )
39
  );
 
40
  $aDisplayData = array(
41
  'sInstallationDays' => sprintf( _wpsf__( 'Days Installed: %s' ), $this->getInstallationDays() ),
42
  'sFooter' => $sFooter,
@@ -46,8 +50,7 @@ class ICWP_WPSF_Processor_Plugin_Badge extends ICWP_WPSF_Processor_BaseWpsf {
46
  if ( !is_array( $aContent ) ) {
47
  $aContent = array();
48
  }
49
- $aContent[] = $this->getFeature()
50
- ->renderTemplate( 'snippets/widget_dashboard_plugin.php', $aDisplayData );
51
  return $aContent;
52
  }
53
 
24
  }
25
 
26
  public function includeJquery() {
27
+ wp_enqueue_script( 'jquery', null, array(), false, true );
28
  }
29
 
30
  /**
32
  * @return array
33
  */
34
  public function gatherPluginWidgetContent( $aContent ) {
35
+ /** @var ICWP_WPSF_FeatureHandler_Plugin $oFO */
36
+ $oFO = $this->getFeature();
37
+ $oCon = $this->getController();
38
 
39
+ $aLabels = $oCon->getPluginLabels();
40
+ $sFooter = sprintf( _wpsf__( '%s is provided by %s' ), $oCon->getHumanName(),
41
+ sprintf( '<a href="%s">%s</a>', $aLabels[ 'AuthorURI' ], $aLabels[ 'Author' ] )
42
  );
43
+
44
  $aDisplayData = array(
45
  'sInstallationDays' => sprintf( _wpsf__( 'Days Installed: %s' ), $this->getInstallationDays() ),
46
  'sFooter' => $sFooter,
50
  if ( !is_array( $aContent ) ) {
51
  $aContent = array();
52
  }
53
+ $aContent[] = $oFO->renderTemplate( 'snippets/widget_dashboard_plugin.php', $aDisplayData );
 
54
  return $aContent;
55
  }
56
 
src/processors/plugin_importexport.php CHANGED
@@ -12,6 +12,8 @@ class ICWP_WPSF_Processor_Plugin_ImportExport extends ICWP_WPSF_Processor_BaseWp
12
  /** @var ICWP_WPSF_FeatureHandler_Plugin $oFO */
13
  $oFO = $this->getFeature();
14
 
 
 
15
  if ( $oFO->hasImportExportMasterImportUrl() ) {
16
  try {
17
  $this->setupCronImport();
@@ -22,6 +24,31 @@ class ICWP_WPSF_Processor_Plugin_ImportExport extends ICWP_WPSF_Processor_BaseWp
22
  }
23
  }
24
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
25
  public function runAction() {
26
  $oDP = $this->loadDP();
27
  switch ( $oDP->query( 'shield_action' ) ) {
@@ -34,8 +61,8 @@ class ICWP_WPSF_Processor_Plugin_ImportExport extends ICWP_WPSF_Processor_BaseWp
34
  add_action( 'init', array( $this, 'runOptionsExportHandshake' ) );
35
  break;
36
 
37
- case 'importexport_updatenotify':
38
- add_action( 'init', array( $this, 'runOptionsUpdateNotify' ) );
39
  break;
40
 
41
  default:
@@ -62,11 +89,40 @@ class ICWP_WPSF_Processor_Plugin_ImportExport extends ICWP_WPSF_Processor_BaseWp
62
  }
63
 
64
  /**
65
- * TODO: set a cron to run in a minute to push out notifications to whitelisted sites.
66
  */
67
- public function runOptionsUpdateNotify() {
68
  /** @var ICWP_WPSF_FeatureHandler_Plugin $oFO */
69
  $oFO = $this->getFeature();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
70
  }
71
 
72
  /**
@@ -163,9 +219,13 @@ class ICWP_WPSF_Processor_Plugin_ImportExport extends ICWP_WPSF_Processor_BaseWp
163
  * @throws Exception
164
  */
165
  protected function setupCronImport() {
 
 
166
  $this->loadWpCronProcessor()
167
  ->setNextRun( strtotime( 'tomorrow 1am' ) - get_option( 'gmt_offset' )*HOUR_IN_SECONDS + rand( 0, 1800 ) )
168
  ->createCronJob( $this->getCronName(), array( $this, 'cron_autoImport' ) );
 
 
169
  add_action( $this->getFeature()->prefix( 'delete_plugin' ), array( $this, 'deleteCron' ) );
170
  }
171
 
@@ -256,6 +316,11 @@ class ICWP_WPSF_Processor_Plugin_ImportExport extends ICWP_WPSF_Processor_BaseWp
256
  'options_imported'
257
  );
258
  $oFO->setImportExportLastImportHash( md5( serialize( $aParts[ 'data' ] ) ) );
 
 
 
 
 
259
  }
260
 
261
  // if it's network enabled, we save the new master URL.
12
  /** @var ICWP_WPSF_FeatureHandler_Plugin $oFO */
13
  $oFO = $this->getFeature();
14
 
15
+ add_action( $this->prefix( 'importexport_notify' ), array( $this, 'runWhitelistNotify' ) );
16
+
17
  if ( $oFO->hasImportExportMasterImportUrl() ) {
18
  try {
19
  $this->setupCronImport();
24
  }
25
  }
26
 
27
+ public function runWhitelistNotify() {
28
+ /** @var ICWP_WPSF_FeatureHandler_Plugin $oFO */
29
+ $oFO = $this->getFeature();
30
+
31
+ if ( $oFO->hasImportExportWhitelistSites() ) {
32
+
33
+ $oFs = $this->loadFS();
34
+ foreach ( $oFO->getImportExportWhitelist() as $sUrl ) {
35
+ $oFs->getUrl(
36
+ $sUrl,
37
+ array(
38
+ 'blocking' => false,
39
+ 'body' => array( 'shield_action' => 'importexport_updatenotified' )
40
+ )
41
+ );
42
+ }
43
+
44
+ $this->addToAuditEntry(
45
+ _wpsf__( 'Sent notifications to whitelisted sites for required options import.' ),
46
+ 1,
47
+ 'options_import_notify'
48
+ );
49
+ }
50
+ }
51
+
52
  public function runAction() {
53
  $oDP = $this->loadDP();
54
  switch ( $oDP->query( 'shield_action' ) ) {
61
  add_action( 'init', array( $this, 'runOptionsExportHandshake' ) );
62
  break;
63
 
64
+ case 'importexport_updatenotified':
65
+ add_action( 'init', array( $this, 'runOptionsUpdateNotified' ) );
66
  break;
67
 
68
  default:
89
  }
90
 
91
  /**
92
+ * We've been notified that there's an update to pull in from the master site so we set a cron to do this.
93
  */
94
+ public function runOptionsUpdateNotified() {
95
  /** @var ICWP_WPSF_FeatureHandler_Plugin $oFO */
96
  $oFO = $this->getFeature();
97
+
98
+ $sCronHook = $oFO->prefix( 'importexport_updatenotified' );
99
+ if ( wp_next_scheduled( $sCronHook ) ) {
100
+ wp_clear_scheduled_hook( $sCronHook );
101
+ }
102
+
103
+ if ( !wp_next_scheduled( $sCronHook ) ) {
104
+
105
+ wp_schedule_single_event( $this->loadDP()->time() + 12, $sCronHook );
106
+
107
+ preg_match( '#.*WordPress/.*\s+(.*)\s?#', $this->loadDP()->server( 'HTTP_USER_AGENT' ), $aMatches );
108
+ if ( !empty( $aMatches[ 1 ] ) && filter_var( $aMatches[ 1 ], FILTER_VALIDATE_URL ) ) {
109
+ $sUrl = parse_url( $aMatches[ 1 ], PHP_URL_HOST );
110
+ if ( !empty( $sUrl ) ) {
111
+ $sUrl = 'Site: '.$sUrl;
112
+ }
113
+ }
114
+ else {
115
+ $sUrl = '';
116
+ }
117
+
118
+ $this->addToAuditEntry(
119
+ _wpsf__( 'Received notification that options import required.' )
120
+ .' '.sprintf( _wpsf__( 'Current master site: %s' ), $oFO->getImportExportMasterImportUrl() ),
121
+ 1,
122
+ 'options_import_notified',
123
+ $sUrl
124
+ );
125
+ }
126
  }
127
 
128
  /**
219
  * @throws Exception
220
  */
221
  protected function setupCronImport() {
222
+ /** @var ICWP_WPSF_FeatureHandler_Plugin $oFO */
223
+ $oFO = $this->getFeature();
224
  $this->loadWpCronProcessor()
225
  ->setNextRun( strtotime( 'tomorrow 1am' ) - get_option( 'gmt_offset' )*HOUR_IN_SECONDS + rand( 0, 1800 ) )
226
  ->createCronJob( $this->getCronName(), array( $this, 'cron_autoImport' ) );
227
+ // For auto update whitelist notifications:
228
+ add_action( $oFO->prefix( 'importexport_updatenotified' ), array( $this, 'cron_autoImport' ) );
229
  add_action( $this->getFeature()->prefix( 'delete_plugin' ), array( $this, 'deleteCron' ) );
230
  }
231
 
316
  'options_imported'
317
  );
318
  $oFO->setImportExportLastImportHash( md5( serialize( $aParts[ 'data' ] ) ) );
319
+
320
+ // Fix for the overwriting of the Master Site URL with an empty string.
321
+ if ( !$oFO->hasImportExportMasterImportUrl() ) {
322
+ $oFO->setImportExportMasterImportUrl( $sMasterSiteUrl );
323
+ }
324
  }
325
 
326
  // if it's network enabled, we save the new master URL.
src/processors/plugin_notes.php CHANGED
@@ -9,11 +9,11 @@ require_once( dirname( __FILE__ ).'/basedb.php' );
9
  class ICWP_WPSF_Processor_Plugin_Notes extends ICWP_WPSF_BaseDbProcessor {
10
 
11
  /**
12
- * @param ICWP_WPSF_FeatureHandler_Plugin $oFeatureOptions
13
  * @throws Exception
14
  */
15
- public function __construct( ICWP_WPSF_FeatureHandler_Plugin $oFeatureOptions ) {
16
- parent::__construct( $oFeatureOptions, $oFeatureOptions->getDbNameNotes() );
17
  }
18
 
19
  public function run() {
9
  class ICWP_WPSF_Processor_Plugin_Notes extends ICWP_WPSF_BaseDbProcessor {
10
 
11
  /**
12
+ * @param ICWP_WPSF_FeatureHandler_Plugin $oModCon
13
  * @throws Exception
14
  */
15
+ public function __construct( ICWP_WPSF_FeatureHandler_Plugin $oModCon ) {
16
+ parent::__construct( $oModCon, $oModCon->getDbNameNotes() );
17
  }
18
 
19
  public function run() {
src/processors/plugin_tracking.php CHANGED
@@ -44,7 +44,7 @@ class ICWP_WPSF_Processor_Plugin_Tracking extends ICWP_WPSF_Processor_BasePlugin
44
  'hrefs' => array(
45
  'learn_more' => 'http://translate.icontrolwp.com',
46
  'link_to_see' => $oFO->getLinkToTrackingDataDump(),
47
- 'link_to_moreinfo' => 'http://icwp.io/shieldtrackinginfo',
48
 
49
  )
50
  );
@@ -98,7 +98,7 @@ class ICWP_WPSF_Processor_Plugin_Tracking extends ICWP_WPSF_Processor_BasePlugin
98
  * @return array
99
  */
100
  protected function getBaseTrackingData() {
101
- $oDP = $this->loadDataProcessor();
102
  $oWP = $this->loadWp();
103
  $oWpPlugins = $this->loadWpPlugins();
104
  return array(
44
  'hrefs' => array(
45
  'learn_more' => 'http://translate.icontrolwp.com',
46
  'link_to_see' => $oFO->getLinkToTrackingDataDump(),
47
+ 'link_to_moreinfo' => 'https://icwp.io/shieldtrackinginfo',
48
 
49
  )
50
  );
98
  * @return array
99
  */
100
  protected function getBaseTrackingData() {
101
+ $oDP = $this->loadDP();
102
  $oWP = $this->loadWp();
103
  $oWpPlugins = $this->loadWpPlugins();
104
  return array(
src/processors/sessions.php CHANGED
@@ -19,35 +19,58 @@ class ICWP_WPSF_Processor_Sessions extends ICWP_WPSF_BaseDbProcessor {
19
  private $oCurrent;
20
 
21
  /**
22
- * @param ICWP_WPSF_Processor_Sessions $oFeatureOptions
 
 
 
 
 
23
  */
24
- public function __construct( ICWP_WPSF_FeatureHandler_Sessions $oFeatureOptions ) {
25
- parent::__construct( $oFeatureOptions, $oFeatureOptions->getSessionsTableName() );
26
  }
27
 
28
  public function run() {
29
- if ( $this->readyToRun() ) {
30
- add_action( 'wp_login', array( $this, 'onWpLogin' ), 5, 2 );
31
- add_action( 'wp_logout', array( $this, 'onWpLogout' ), 0 );
32
  add_action( 'wp_loaded', array( $this, 'onWpLoaded' ), 0 );
33
  add_filter( 'login_message', array( $this, 'printLinkToAdmin' ) );
34
  }
35
  }
36
 
37
  /**
38
- * @param string $sUsername
39
- * @param WP_User $oUser
 
 
40
  */
41
- public function onWpLogin( $sUsername, $oUser ) {
42
- $this->activateUserSession( $sUsername, $oUser );
 
 
 
 
43
  }
44
 
45
  /**
 
 
 
 
46
  */
47
- public function onWpLogout() {
48
  $this->terminateCurrentSession();
49
  }
50
 
 
 
 
 
 
 
 
 
51
  /**
52
  */
53
  public function onWpLoaded() {
@@ -68,6 +91,14 @@ class ICWP_WPSF_Processor_Sessions extends ICWP_WPSF_BaseDbProcessor {
68
  }
69
  }
70
 
 
 
 
 
 
 
 
 
71
  /**
72
  * Only show Go To Admin link for Authors and above.
73
  * @param string $sMessage
@@ -105,12 +136,17 @@ class ICWP_WPSF_Processor_Sessions extends ICWP_WPSF_BaseDbProcessor {
105
  return false;
106
  }
107
 
 
 
 
 
108
  // If they have a currently active session, terminate it (i.e. we replace it)
109
  $oSession = $this->queryGetSession( $sUsername, $this->getSessionId() );
110
  if ( !empty( $oSession ) ) {
111
  $this->queryTerminateSession( $oSession );
112
  }
113
  $this->queryCreateSession( $sUsername, $this->getSessionId() );
 
114
  return true;
115
  }
116
 
@@ -242,10 +278,9 @@ class ICWP_WPSF_Processor_Sessions extends ICWP_WPSF_BaseDbProcessor {
242
  }
243
  $this->doStatIncrement( 'user.session.terminate' );
244
 
245
- require_once( dirname( dirname( __FILE__ ) ).'/query/sessions_terminate.php' );
246
- $oTerminate = new ICWP_WPSF_Query_Sessions_Terminate();
247
- return $oTerminate->setTable( $this->getTableName() )
248
- ->forUserSession( $oSession );
249
  }
250
 
251
  /**
@@ -267,6 +302,15 @@ class ICWP_WPSF_Processor_Sessions extends ICWP_WPSF_BaseDbProcessor {
267
  return $oRetrieve->setTable( $this->getTableName() );
268
  }
269
 
 
 
 
 
 
 
 
 
 
270
  /**
271
  * @return ICWP_WPSF_Query_Sessions_Update
272
  */
19
  private $oCurrent;
20
 
21
  /**
22
+ * @var int
23
+ */
24
+ private $nSessionAlreadyCreatedUserId = 0;
25
+
26
+ /**
27
+ * @param ICWP_WPSF_Processor_Sessions $oModCon
28
  */
29
+ public function __construct( ICWP_WPSF_FeatureHandler_Sessions $oModCon ) {
30
+ parent::__construct( $oModCon, $oModCon->getSessionsTableName() );
31
  }
32
 
33
  public function run() {
34
+ if ( $this->isReadyToRun() ) {
35
+ add_action( 'set_logged_in_cookie', array( $this, 'onWpSetLoggedInCookie' ), 5, 4 ); //login
36
+ add_action( 'clear_auth_cookie', array( $this, 'onWpClearAuthCookie' ), 0 ); //logout
37
  add_action( 'wp_loaded', array( $this, 'onWpLoaded' ), 0 );
38
  add_filter( 'login_message', array( $this, 'printLinkToAdmin' ) );
39
  }
40
  }
41
 
42
  /**
43
+ * @param string $sCookie
44
+ * @param int $nExpire
45
+ * @param int $nExpiration
46
+ * @param int $nUserId
47
  */
48
+ public function onWpSetLoggedInCookie( $sCookie, $nExpire, $nExpiration, $nUserId ) {
49
+ $oUser = $this->loadWpUsers()
50
+ ->getUserById( $nUserId );
51
+ if ( $oUser instanceof WP_User && !$this->isSessionAlreadyCreatedForUser( $oUser ) ) {
52
+ $this->activateUserSession( $oUser->user_login, $oUser );
53
+ }
54
  }
55
 
56
  /**
57
+ * @param string $sCookie
58
+ * @param int $nExpire
59
+ * @param int $nExpiration
60
+ * @param int $nUserId
61
  */
62
+ public function onWpClearAuthCookie() {
63
  $this->terminateCurrentSession();
64
  }
65
 
66
+ /**
67
+ * @param string $sUsername
68
+ * @param WP_User $oUser
69
+ */
70
+ public function onWpLogin( $sUsername, $oUser ) {
71
+ $this->activateUserSession( $sUsername, $oUser );
72
+ }
73
+
74
  /**
75
  */
76
  public function onWpLoaded() {
91
  }
92
  }
93
 
94
+ /**
95
+ * @param WP_User $oUser
96
+ * @return bool
97
+ */
98
+ private function isSessionAlreadyCreatedForUser( $oUser ) {
99
+ return $this->nSessionAlreadyCreatedUserId > 0 && $this->nSessionAlreadyCreatedUserId == $oUser->ID;
100
+ }
101
+
102
  /**
103
  * Only show Go To Admin link for Authors and above.
104
  * @param string $sMessage
136
  return false;
137
  }
138
 
139
+ if ( $this->isSessionAlreadyCreatedForUser( $oUser ) ) {
140
+ return true;
141
+ }
142
+
143
  // If they have a currently active session, terminate it (i.e. we replace it)
144
  $oSession = $this->queryGetSession( $sUsername, $this->getSessionId() );
145
  if ( !empty( $oSession ) ) {
146
  $this->queryTerminateSession( $oSession );
147
  }
148
  $this->queryCreateSession( $sUsername, $this->getSessionId() );
149
+ $this->nSessionAlreadyCreatedUserId = $oUser->ID;
150
  return true;
151
  }
152
 
278
  }
279
  $this->doStatIncrement( 'user.session.terminate' );
280
 
281
+ return $this->getSessionTerminator()
282
+ ->setTable( $this->getTableName() )
283
+ ->forUserSession( $oSession );
 
284
  }
285
 
286
  /**
302
  return $oRetrieve->setTable( $this->getTableName() );
303
  }
304
 
305
+ /**
306
+ * @return ICWP_WPSF_Query_Sessions_Terminate
307
+ */
308
+ public function getSessionTerminator() {
309
+ require_once( $this->getQueryDir().'sessions_terminate.php' );
310
+ $oRetrieve = new ICWP_WPSF_Query_Sessions_Terminate();
311
+ return $oRetrieve->setTable( $this->getTableName() );
312
+ }
313
+
314
  /**
315
  * @return ICWP_WPSF_Query_Sessions_Update
316
  */
src/processors/statistics.php CHANGED
@@ -14,21 +14,21 @@ class ICWP_WPSF_Processor_Statistics extends ICWP_WPSF_BaseDbProcessor {
14
  private $oReportingProcessor;
15
 
16
  /**
17
- * @param ICWP_WPSF_FeatureHandler_Statistics $oFeatureOptions
18
  */
19
- public function __construct( ICWP_WPSF_FeatureHandler_Statistics $oFeatureOptions ) {
20
- parent::__construct( $oFeatureOptions, $oFeatureOptions->getStatisticsTableName() );
21
  }
22
 
23
  public function run() {
24
  /** @var ICWP_WPSF_FeatureHandler_Statistics $oFO */
25
  $oFO = $this->getFeature();
26
- if ( $this->readyToRun() ) {
27
  add_filter( $oFO->prefix( 'dashboard_widget_content' ), array( $this, 'gatherStatsSummaryWidgetContent' ), 10 );
28
  }
29
 
30
  // Reporting stats run or destroy
31
- if ( $this->loadDataProcessor()->getPhpVersionIsAtLeast( '5.3.0' ) ) {
32
  $this->getReportingProcessor()
33
  ->run();
34
  }
@@ -220,7 +220,7 @@ class ICWP_WPSF_Processor_Statistics extends ICWP_WPSF_BaseDbProcessor {
220
  );
221
 
222
  $aDisplayData = array(
223
- 'sHeading' => _wpsf__( 'Shield Statistics' ),
224
  'aAllStats' => $aAllStats,
225
  'aKeyStats' => $aKeyStats,
226
  );
@@ -325,8 +325,8 @@ class ICWP_WPSF_Processor_Statistics extends ICWP_WPSF_BaseDbProcessor {
325
  return $this->query_selectAll( array( 'stat_key', 'tally' ) );
326
  }
327
 
328
- public function action_doFeatureProcessorShutdown() {
329
- parent::action_doFeatureProcessorShutdown();
330
  if ( !$this->getFeature()->isPluginDeleting() ) {
331
  $this->commit();
332
  }
@@ -336,7 +336,7 @@ class ICWP_WPSF_Processor_Statistics extends ICWP_WPSF_BaseDbProcessor {
336
  * @return array
337
  */
338
  protected function getTableColumnsByDefinition() {
339
- $aDef = $this->getFeature()->getDefinition( 'statistics_table_columns' );
340
  return ( is_array( $aDef ) ? $aDef : array() );
341
  }
342
 
14
  private $oReportingProcessor;
15
 
16
  /**
17
+ * @param ICWP_WPSF_FeatureHandler_Statistics $oModCon
18
  */
19
+ public function __construct( ICWP_WPSF_FeatureHandler_Statistics $oModCon ) {
20
+ parent::__construct( $oModCon, $oModCon->getStatisticsTableName() );
21
  }
22
 
23
  public function run() {
24
  /** @var ICWP_WPSF_FeatureHandler_Statistics $oFO */
25
  $oFO = $this->getFeature();
26
+ if ( $this->isReadyToRun() ) {
27
  add_filter( $oFO->prefix( 'dashboard_widget_content' ), array( $this, 'gatherStatsSummaryWidgetContent' ), 10 );
28
  }
29
 
30
  // Reporting stats run or destroy
31
+ if ( $this->loadDP()->getPhpVersionIsAtLeast( '5.3.0' ) ) {
32
  $this->getReportingProcessor()
33
  ->run();
34
  }
220
  );
221
 
222
  $aDisplayData = array(
223
+ 'sHeading' => sprintf( _wpsf__( '%s Statistics' ), $this->getController()->getHumanName() ),
224
  'aAllStats' => $aAllStats,
225
  'aKeyStats' => $aKeyStats,
226
  );
325
  return $this->query_selectAll( array( 'stat_key', 'tally' ) );
326
  }
327
 
328
+ public function onModuleShutdown() {
329
+ parent::onModuleShutdown();
330
  if ( !$this->getFeature()->isPluginDeleting() ) {
331
  $this->commit();
332
  }
336
  * @return array
337
  */
338
  protected function getTableColumnsByDefinition() {
339
+ $aDef = $this->getFeature()->getDef( 'statistics_table_columns' );
340
  return ( is_array( $aDef ) ? $aDef : array() );
341
  }
342
 
src/processors/statistics_reporting.php CHANGED
@@ -14,10 +14,10 @@ class ICWP_WPSF_Processor_Statistics_Reporting extends ICWP_WPSF_BaseDbProcessor
14
  private $oConsolidation;
15
 
16
  /**
17
- * @param ICWP_WPSF_FeatureHandler_Statistics $oFeatureOptions
18
  */
19
- public function __construct( ICWP_WPSF_FeatureHandler_Statistics $oFeatureOptions ) {
20
- parent::__construct( $oFeatureOptions, $oFeatureOptions->getReportingTableName() );
21
  }
22
 
23
  public function run() {
@@ -71,8 +71,8 @@ class ICWP_WPSF_Processor_Statistics_Reporting extends ICWP_WPSF_BaseDbProcessor
71
  return (bool)$mResult;
72
  }
73
 
74
- public function action_doFeatureProcessorShutdown() {
75
- parent::action_doFeatureProcessorShutdown();
76
  if ( !$this->getFeature()->isPluginDeleting() ) {
77
  $this->commit();
78
  }
14
  private $oConsolidation;
15
 
16
  /**
17
+ * @param ICWP_WPSF_FeatureHandler_Statistics $oModCon
18
  */
19
+ public function __construct( ICWP_WPSF_FeatureHandler_Statistics $oModCon ) {
20
+ parent::__construct( $oModCon, $oModCon->getReportingTableName() );
21
  }
22
 
23
  public function run() {
71
  return (bool)$mResult;
72
  }
73
 
74
+ public function onModuleShutdown() {
75
+ parent::onModuleShutdown();
76
  if ( !$this->getFeature()->isPluginDeleting() ) {
77
  $this->commit();
78
  }
src/processors/user_management.php CHANGED
@@ -61,15 +61,35 @@ class ICWP_WPSF_Processor_UserManagement extends ICWP_WPSF_Processor_BaseWpsf {
61
  $oUser = $this->loadWpUsers()->getUserByUsername( $sUsername );
62
  if ( $oUser instanceof WP_User ) {
63
 
64
- $this->setPasswordStartedAt( $oUser ); // used by Password Policies
 
 
 
 
65
 
66
- /** @var ICWP_WPSF_FeatureHandler_UserManagement $oFO */
67
- $oFO = $this->getFeature();
68
- if ( $oFO->isSendEmailLoginNotification() ) {
69
- $this->sendLoginEmailNotification( $oUser );
70
- }
71
- $this->setUserLastLoginTime( $oUser );
 
 
 
 
 
 
 
 
 
 
 
 
72
  }
 
 
 
 
73
  }
74
 
75
  /**
@@ -139,10 +159,12 @@ class ICWP_WPSF_Processor_UserManagement extends ICWP_WPSF_Processor_BaseWpsf {
139
  * @param WP_User $oUser
140
  * @return bool
141
  */
142
- protected function sendLoginEmailNotification( $oUser ) {
143
  if ( !( $oUser instanceof WP_User ) ) {
144
  return false;
145
  }
 
 
146
 
147
  $aUserCapToRolesMap = array(
148
  'network_admin' => 'manage_network',
@@ -195,13 +217,43 @@ class ICWP_WPSF_Processor_UserManagement extends ICWP_WPSF_Processor_BaseWpsf {
195
  return $this
196
  ->getFeature()
197
  ->getEmailProcessor()
198
- ->sendEmailTo(
199
- $this->getOption( 'enable_admin_login_email_notification' ),
200
  sprintf( _wpsf__( 'Notice - %s' ), sprintf( _wpsf__( '%s Just Logged Into %s' ), $sHumanName, $sHomeUrl ) ),
201
  $aMessage
202
  );
203
  }
204
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
205
  /**
206
  * @return ICWP_WPSF_Processor_UserManagement_Passwords
207
  */
@@ -218,7 +270,7 @@ class ICWP_WPSF_Processor_UserManagement extends ICWP_WPSF_Processor_BaseWpsf {
218
  /**
219
  * @return ICWP_WPSF_Processor_UserManagement_Sessions
220
  */
221
- protected function getProcessorSessions() {
222
  if ( !isset( $this->oProcessorSessions ) ) {
223
  require_once( dirname( __FILE__ ).'/usermanagement_sessions.php' );
224
  /** @var ICWP_WPSF_FeatureHandler_UserManagement $oFO */
@@ -228,14 +280,6 @@ class ICWP_WPSF_Processor_UserManagement extends ICWP_WPSF_Processor_BaseWpsf {
228
  return $this->oProcessorSessions;
229
  }
230
 
231
- /**
232
- * @param string $sWpUsername
233
- * @return array|bool
234
- */
235
- public function getActiveUserSessionRecords( $sWpUsername = '' ) {
236
- return $this->getProcessorSessions()->getActiveSessionRecordsForUsername( $sWpUsername );
237
- }
238
-
239
  /**
240
  * @return string
241
  */
61
  $oUser = $this->loadWpUsers()->getUserByUsername( $sUsername );
62
  if ( $oUser instanceof WP_User ) {
63
 
64
+ $this->setPasswordStartedAt( $oUser )// used by Password Policies
65
+ ->setUserLastLoginTime( $oUser )
66
+ ->sendLoginNotifications( $oUser );
67
+ }
68
+ }
69
 
70
+ /**
71
+ * @param WP_User $oUser - not checking that user is valid
72
+ * @return $this
73
+ */
74
+ private function sendLoginNotifications( $oUser ) {
75
+ /** @var ICWP_WPSF_FeatureHandler_UserManagement $oFO */
76
+ $oFO = $this->getFeature();
77
+ $bAdmin = $oFO->isSendAdminEmailLoginNotification();
78
+ $bUser = $oFO->isSendUserEmailLoginNotification();
79
+
80
+ // do some magic logic so we don't send both to the same person (the assumption being that the admin
81
+ // email recipient is actually an admin (or they'll maybe not get any).
82
+ if ( $bAdmin && $bUser && ( $oFO->getAdminLoginNotificationEmail() === $oUser->user_email ) ) {
83
+ $bUser = false;
84
+ }
85
+
86
+ if ( $bAdmin ) {
87
+ $this->sendAdminLoginEmailNotification( $oUser );
88
  }
89
+ if ( $bUser && !$this->isUserSubjectToLoginIntent( $oUser ) ) {
90
+ $this->sendUserLoginEmailNotification( $oUser );
91
+ }
92
+ return $this;
93
  }
94
 
95
  /**
159
  * @param WP_User $oUser
160
  * @return bool
161
  */
162
+ private function sendAdminLoginEmailNotification( $oUser ) {
163
  if ( !( $oUser instanceof WP_User ) ) {
164
  return false;
165
  }
166
+ /** @var ICWP_WPSF_FeatureHandler_UserManagement $oFO */
167
+ $oFO = $this->getFeature();
168
 
169
  $aUserCapToRolesMap = array(
170
  'network_admin' => 'manage_network',
217
  return $this
218
  ->getFeature()
219
  ->getEmailProcessor()
220
+ ->sendEmailWithWrap(
221
+ $oFO->getAdminLoginNotificationEmail(),
222
  sprintf( _wpsf__( 'Notice - %s' ), sprintf( _wpsf__( '%s Just Logged Into %s' ), $sHumanName, $sHomeUrl ) ),
223
  $aMessage
224
  );
225
  }
226
 
227
+ /**
228
+ * @param WP_User $oUser
229
+ * @return bool
230
+ */
231
+ private function sendUserLoginEmailNotification( $oUser ) {
232
+ $aMessage = array(
233
+ sprintf( _wpsf__( '%s is notifying you of a successful login to your WordPress account.' ), $this->getController()
234
+ ->getHumanName() ),
235
+ '',
236
+ _wpsf__( 'Details for this login are below:' ),
237
+ '- '.sprintf( _wpsf__( 'Site URL: %s' ), $this->loadWp()->getHomeUrl() ),
238
+ '- '.sprintf( _wpsf__( 'Username: %s' ), $oUser->get( 'user_login' ) ),
239
+ '- '.sprintf( _wpsf__( 'IP Address: %s' ), $this->ip() ),
240
+ '- '.sprintf( _wpsf__( 'Login Time: %s' ), $this->loadWp()->getTimeStampForDisplay() ),
241
+ '',
242
+ _wpsf__( 'If this is unexpected or suspicious, please contact your site administrator immediately.' ),
243
+ '',
244
+ _wpsf__( 'Thanks.' )
245
+ );
246
+
247
+ return $this
248
+ ->getFeature()
249
+ ->getEmailProcessor()
250
+ ->sendEmailWithWrap(
251
+ $oUser->user_email,
252
+ sprintf( _wpsf__( 'Notice - %s' ), _wpsf__( 'A login to your WordPress account just occurred' ) ),
253
+ $aMessage
254
+ );
255
+ }
256
+
257
  /**
258
  * @return ICWP_WPSF_Processor_UserManagement_Passwords
259
  */
270
  /**
271
  * @return ICWP_WPSF_Processor_UserManagement_Sessions
272
  */
273
+ public function getProcessorSessions() {
274
  if ( !isset( $this->oProcessorSessions ) ) {
275
  require_once( dirname( __FILE__ ).'/usermanagement_sessions.php' );
276
  /** @var ICWP_WPSF_FeatureHandler_UserManagement $oFO */
280
  return $this->oProcessorSessions;
281
  }
282
 
 
 
 
 
 
 
 
 
283
  /**
284
  * @return string
285
  */
src/processors/usermanagement_sessions.php CHANGED
@@ -4,15 +4,43 @@ if ( class_exists( 'ICWP_WPSF_Processor_UserManagement_Sessions', false ) ) {
4
  return;
5
  }
6
 
7
- require_once( dirname( __FILE__ ).'/base_wpsf.php' );
8
 
9
- class ICWP_WPSF_Processor_UserManagement_Sessions extends ICWP_WPSF_Processor_BaseWpsf {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
10
 
11
  public function run() {
12
- add_filter( 'wp_login_errors', array( $this, 'addLoginMessage' ) );
13
- add_filter( 'auth_cookie_expiration', array( $this, 'setTimeoutCookieExpiration_Filter' ), 100, 1 );
14
- add_action( 'wp_loaded', array( $this, 'onWpLoaded' ), 1 ); // Check the current every page load.
15
- add_action( 'wp_login', array( $this, 'onWpLogin' ), 10, 1 );
 
 
 
 
 
 
 
 
 
 
 
 
16
  }
17
 
18
  /**
@@ -85,6 +113,30 @@ class ICWP_WPSF_Processor_UserManagement_Sessions extends ICWP_WPSF_Processor_Ba
85
  }
86
  }
87
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
88
  /**
89
  * @return int
90
  */
@@ -98,16 +150,14 @@ class ICWP_WPSF_Processor_UserManagement_Sessions extends ICWP_WPSF_Processor_Ba
98
  else {
99
  $oSess = $oFO->getSession();
100
  $nTime = $this->time();
101
- $nTimeout = $this->getSessionTimeoutInterval();
102
- $nIdleTimeout = $this->getSessionIdleTimeoutInterval();
103
 
104
  $nForceLogOutCode = 0; // when it's == 0 it's a valid session
105
 
106
  // timeout interval
107
- if ( $nTimeout > 0 && ( $nTime - $oSess->getLoggedInAt() > $nTimeout ) ) {
108
  $nForceLogOutCode = 1;
109
  } // idle timeout interval
110
- else if ( $nIdleTimeout > 0 && ( ( $nTime - $oSess->getLastActivityAt() ) > $nIdleTimeout ) ) {
111
  $oFO->setOptInsightsAt( 'last_idle_logout_at' );
112
  $nForceLogOutCode = 2;
113
  } // login ip address lock
@@ -124,22 +174,9 @@ class ICWP_WPSF_Processor_UserManagement_Sessions extends ICWP_WPSF_Processor_Ba
124
  * @return integer
125
  */
126
  public function setTimeoutCookieExpiration_Filter( $nTimeout ) {
127
- $nSessionTimeoutInterval = $this->getSessionTimeoutInterval();
128
- return ( $nSessionTimeoutInterval > 0 ) ? $nSessionTimeoutInterval : $nTimeout;
129
- }
130
-
131
- /**
132
- * @return integer
133
- */
134
- protected function getSessionTimeoutInterval() {
135
- return $this->getOption( 'session_timeout_interval' )*DAY_IN_SECONDS;
136
- }
137
-
138
- /**
139
- * @return integer
140
- */
141
- protected function getSessionIdleTimeoutInterval() {
142
- return $this->getOption( 'session_idle_timeout_interval' )*HOUR_IN_SECONDS;
143
  }
144
 
145
  /**
4
  return;
5
  }
6
 
7
+ require_once( dirname( __FILE__ ).'/cronbase.php' );
8
 
9
+ class ICWP_WPSF_Processor_UserManagement_Sessions extends ICWP_WPSF_Processor_CronBase {
10
+
11
+ /**
12
+ * @return callable
13
+ */
14
+ protected function getCronCallback() {
15
+ return array( $this, 'cron_runSessionsCleanup' );
16
+ }
17
+
18
+ /**
19
+ * @return string
20
+ */
21
+ protected function getCronName() {
22
+ /** @var ICWP_WPSF_FeatureHandler_UserManagement $oFO */
23
+ $oFO = $this->getFeature();
24
+ return $oFO->prefix( $oFO->getDef( 'cron_name_sessionscleanup' ) );
25
+ }
26
 
27
  public function run() {
28
+ if ( $this->isReadyToRun() ) {
29
+ parent::run();
30
+ add_filter( 'wp_login_errors', array( $this, 'addLoginMessage' ) );
31
+ add_filter( 'auth_cookie_expiration', array( $this, 'setTimeoutCookieExpiration_Filter' ), 100, 1 );
32
+ add_action( 'wp_loaded', array( $this, 'onWpLoaded' ), 1 ); // Check the current every page load.
33
+ add_action( 'wp_login', array( $this, 'onWpLogin' ), 10, 1 );
34
+ }
35
+ }
36
+
37
+ /**
38
+ * @return bool
39
+ */
40
+ public function isReadyToRun() {
41
+ /** @var ICWP_WPSF_FeatureHandler_UserManagement $oFO */
42
+ $oFO = $this->getFeature();
43
+ return ( parent::isReadyToRun() && $oFO->getSessionsProcessor()->isReadyToRun() );
44
  }
45
 
46
  /**
113
  }
114
  }
115
 
116
+ public function cleanExpiredSessions() {
117
+ /** @var ICWP_WPSF_FeatureHandler_UserManagement $oFO */
118
+ $oFO = $this->getFeature();
119
+ $oTerminator = $oFO->getSessionsProcessor()
120
+ ->getSessionTerminator();
121
+
122
+ $nNow = $this->time();
123
+ // We use 14 as an outside case. If it's 2 days, WP cookie will expire anyway.
124
+ // And if User Management is active, then it'll draw in that value.
125
+ $oTerminator->forExpiredLoginAt( $nNow - apply_filters( 'auth_cookie_expiration', 14*DAY_IN_SECONDS ) );
126
+
127
+ // Default is ZERO, so we don't want to terminate all sessions if it's never set.
128
+ if ( $oFO->hasSessionIdleTimeout() ) {
129
+ $oTerminator->forExpiredLoginIdle( $nNow - $oFO->getSessionIdleTimeoutInterval() );
130
+ }
131
+ }
132
+
133
+ /**
134
+ * A cron that will automatically cleanout expired/idle sessions.
135
+ */
136
+ public function cron_runSessionsCleanup() {
137
+ $this->cleanExpiredSessions();
138
+ }
139
+
140
  /**
141
  * @return int
142
  */
150
  else {
151
  $oSess = $oFO->getSession();
152
  $nTime = $this->time();
 
 
153
 
154
  $nForceLogOutCode = 0; // when it's == 0 it's a valid session
155
 
156
  // timeout interval
157
+ if ( $oFO->hasSessionTimeoutInterval() && ( $nTime - $oSess->getLoggedInAt() > $oFO->getSessionTimeoutInterval() ) ) {
158
  $nForceLogOutCode = 1;
159
  } // idle timeout interval
160
+ else if ( $oFO->hasSessionIdleTimeout() && ( $nTime - $oSess->getLastActivityAt() > $oFO->getSessionIdleTimeoutInterval() ) ) {
161
  $oFO->setOptInsightsAt( 'last_idle_logout_at' );
162
  $nForceLogOutCode = 2;
163
  } // login ip address lock
174
  * @return integer
175
  */
176
  public function setTimeoutCookieExpiration_Filter( $nTimeout ) {
177
+ /** @var ICWP_WPSF_FeatureHandler_UserManagement $oFO */
178
+ $oFO = $this->getFeature();
179
+ return $oFO->hasSessionTimeoutInterval() ? $oFO->getSessionTimeoutInterval() : $nTimeout;
 
 
 
 
 
 
 
 
 
 
 
 
 
180
  }
181
 
182
  /**
src/query/sessions_terminate.php CHANGED
@@ -24,6 +24,46 @@ class ICWP_WPSF_Query_Sessions_Terminate extends ICWP_WPSF_Query_Base {
24
  return $this->query_terminateForUser( $oSession->getUsername(), $oSession->getId() );
25
  }
26
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
27
  /**
28
  * @param string $sWpUsername
29
  * @param string $sSessionId
24
  return $this->query_terminateForUser( $oSession->getUsername(), $oSession->getId() );
25
  }
26
 
27
+ /**
28
+ * @param int $bOlderThan
29
+ * @return bool
30
+ */
31
+ public function forExpiredLoginAt( $bOlderThan ) {
32
+ return $this->query_terminateExpired( $bOlderThan, 'logged_in_at' );
33
+ }
34
+
35
+ /**
36
+ * @param int $bOlderThan
37
+ * @return bool
38
+ */
39
+ public function forExpiredLoginIdle( $bOlderThan ) {
40
+ return $this->query_terminateExpired( $bOlderThan, 'last_activity_at' );
41
+ }
42
+
43
+ /**
44
+ * @param int $nOlderThan
45
+ * @param string $sColumn
46
+ * @return bool
47
+ */
48
+ protected function query_terminateExpired( $nOlderThan, $sColumn = 'logged_in_at' ) {
49
+
50
+ $sQuery = "
51
+ DELETE FROM `%s`
52
+ WHERE `%s` < %s
53
+ ";
54
+ $sQuery = sprintf(
55
+ $sQuery,
56
+ $this->getTable(),
57
+ esc_sql( $sColumn ),
58
+ (int)$nOlderThan
59
+ );
60
+
61
+ $nCount = $this->loadDbProcessor()
62
+ ->doSql( $sQuery );
63
+
64
+ return is_numeric( $nCount );
65
+ }
66
+
67
  /**
68
  * @param string $sWpUsername
69
  * @param string $sSessionId
src/query/statistics_base.php CHANGED
@@ -143,7 +143,7 @@ class ICWP_WPSF_Query_Statistics_Base extends ICWP_WPSF_Query_Base {
143
  * @return int
144
  */
145
  public function getDateTo() {
146
- return isset( $this->nDateTo ) ? (int)$this->nDateTo : $this->loadDataProcessor()->time();
147
  }
148
 
149
  /**
143
  * @return int
144
  */
145
  public function getDateTo() {
146
+ return isset( $this->nDateTo ) ? (int)$this->nDateTo : $this->loadDP()->time();
147
  }
148
 
149
  /**
src/wizards/base.php CHANGED
@@ -288,7 +288,7 @@ abstract class ICWP_WPSF_Wizard_Base extends ICWP_WPSF_Foundation {
288
  ),
289
  'hrefs' => array(
290
  'dashboard' => $oFO->getUrl_AdminPage(),
291
- 'goprofooter' => 'http://icwp.io/goprofooter',
292
  ),
293
  'ajax' => array(
294
  'content' => $oFO->getAjaxActionData( 'wiz_process_step' ),
@@ -307,7 +307,8 @@ abstract class ICWP_WPSF_Wizard_Base extends ICWP_WPSF_Foundation {
307
  $oCon = $this->getModCon()->getConn();
308
  return array(
309
  'strings' => array(
310
- 'page_title' => 'Twig Page'
 
311
  ),
312
  'data' => array(),
313
  'hrefs' => array(
@@ -338,13 +339,15 @@ abstract class ICWP_WPSF_Wizard_Base extends ICWP_WPSF_Foundation {
338
  * @return array
339
  */
340
  protected function getRenderData_PageWizard() {
 
341
  /** @var ICWP_WPSF_FeatureHandler_Plugin $oFO */
342
  $oFO = $this->getModCon();
343
  return $this->loadDP()->mergeArraysRecursive(
344
  $this->getRenderData_TwigPageBase(),
345
  array(
346
  'strings' => array(
347
- 'page_title' => $this->getPageTitle()
 
348
  ),
349
  'data' => array(
350
  'wizard_slug' => $this->getWizardSlug(),
@@ -353,7 +356,7 @@ abstract class ICWP_WPSF_Wizard_Base extends ICWP_WPSF_Foundation {
353
  ),
354
  'hrefs' => array(
355
  'dashboard' => $oFO->getUrl_AdminPage(),
356
- 'goprofooter' => 'http://icwp.io/goprofooter',
357
  ),
358
  'ajax' => array(
359
  'content' => $oFO->getAjaxActionData( 'wiz_process_step' ),
@@ -440,18 +443,22 @@ abstract class ICWP_WPSF_Wizard_Base extends ICWP_WPSF_Foundation {
440
  */
441
  protected function getRenderData_SlideBase() {
442
  $oFO = $this->getModCon();
 
443
  $aWizards = $this->getModuleWizardsForRender();
444
  return array(
445
- 'flags' => array(
 
 
 
446
  'is_premium' => $oFO->isPremium(),
447
  'has_other_wizards' => false
448
  ),
449
- 'hrefs' => array(
450
  'dashboard' => $oFO->getUrl_AdminPage(),
451
- 'gopro' => 'http://icwp.io/ap',
452
  ),
453
- 'imgs' => array(),
454
- 'data' => array(
455
  'mod_wizards_count' => count( $aWizards ),
456
  'mod_wizards' => $aWizards
457
  ),
288
  ),
289
  'hrefs' => array(
290
  'dashboard' => $oFO->getUrl_AdminPage(),
291
+ 'goprofooter' => 'https://icwp.io/goprofooter',
292
  ),
293
  'ajax' => array(
294
  'content' => $oFO->getAjaxActionData( 'wiz_process_step' ),
307
  $oCon = $this->getModCon()->getConn();
308
  return array(
309
  'strings' => array(
310
+ 'page_title' => 'Twig Page',
311
+ 'plugin_name' => $oCon->getHumanName()
312
  ),
313
  'data' => array(),
314
  'hrefs' => array(
339
  * @return array
340
  */
341
  protected function getRenderData_PageWizard() {
342
+ $oCon = $this->getModCon()->getConn();
343
  /** @var ICWP_WPSF_FeatureHandler_Plugin $oFO */
344
  $oFO = $this->getModCon();
345
  return $this->loadDP()->mergeArraysRecursive(
346
  $this->getRenderData_TwigPageBase(),
347
  array(
348
  'strings' => array(
349
+ 'page_title' => $this->getPageTitle(),
350
+ 'plugin_name' => $oCon->getHumanName()
351
  ),
352
  'data' => array(
353
  'wizard_slug' => $this->getWizardSlug(),
356
  ),
357
  'hrefs' => array(
358
  'dashboard' => $oFO->getUrl_AdminPage(),
359
+ 'goprofooter' => 'https://icwp.io/goprofooter',
360
  ),
361
  'ajax' => array(
362
  'content' => $oFO->getAjaxActionData( 'wiz_process_step' ),
443
  */
444
  protected function getRenderData_SlideBase() {
445
  $oFO = $this->getModCon();
446
+ $oCon = $this->getModCon()->getConn();
447
  $aWizards = $this->getModuleWizardsForRender();
448
  return array(
449
+ 'strings' => array(
450
+ 'plugin_name' => $oCon->getHumanName()
451
+ ),
452
+ 'flags' => array(
453
  'is_premium' => $oFO->isPremium(),
454
  'has_other_wizards' => false
455
  ),
456
+ 'hrefs' => array(
457
  'dashboard' => $oFO->getUrl_AdminPage(),
458
+ 'gopro' => 'https://icwp.io/ap',
459
  ),
460
+ 'imgs' => array(),
461
+ 'data' => array(
462
  'mod_wizards_count' => count( $aWizards ),
463
  'mod_wizards' => $aWizards
464
  ),
src/wizards/plugin.php CHANGED
@@ -197,7 +197,7 @@ class ICWP_WPSF_Wizard_Plugin extends ICWP_WPSF_Wizard_BaseWpsf {
197
  case 'ip_detect':
198
  $aAdditional = array(
199
  'hrefs' => array(
200
- 'visitor_ip' => 'http://icwp.io/visitorip',
201
  )
202
  );
203
  break;
@@ -206,7 +206,7 @@ class ICWP_WPSF_Wizard_Plugin extends ICWP_WPSF_Wizard_BaseWpsf {
206
  case 'import':
207
  $aAdditional = array(
208
  'hrefs' => array(
209
- 'blog_importexport' => 'http://icwp.io/av'
210
  ),
211
  'imgs' => array(
212
  'shieldnetworkmini' => $oConn->getPluginUrl_Image( 'shield/shieldnetworkmini.png' ),
@@ -281,7 +281,7 @@ class ICWP_WPSF_Wizard_Plugin extends ICWP_WPSF_Wizard_BaseWpsf {
281
  case 'import':
282
  $aAdditional = array(
283
  'hrefs' => array(
284
- 'blog_importexport' => 'http://icwp.io/av'
285
  ),
286
  'imgs' => array(
287
  'shieldnetworkmini' => $oConn->getPluginUrl_Image( 'shield/shieldnetworkmini.png' ),
197
  case 'ip_detect':
198
  $aAdditional = array(
199
  'hrefs' => array(
200
+ 'visitor_ip' => 'https://icwp.io/visitorip',
201
  )
202
  );
203
  break;
206
  case 'import':
207
  $aAdditional = array(
208
  'hrefs' => array(
209
+ 'blog_importexport' => 'https://icwp.io/av'
210
  ),
211
  'imgs' => array(
212
  'shieldnetworkmini' => $oConn->getPluginUrl_Image( 'shield/shieldnetworkmini.png' ),
281
  case 'import':
282
  $aAdditional = array(
283
  'hrefs' => array(
284
+ 'blog_importexport' => 'https://icwp.io/av'
285
  ),
286
  'imgs' => array(
287
  'shieldnetworkmini' => $oConn->getPluginUrl_Image( 'shield/shieldnetworkmini.png' ),
templates/html/plugin_badge.html CHANGED
@@ -82,7 +82,7 @@
82
  </style>
83
  <div id="icwpWpsfSiteBadge">
84
  <a id="icwpWpsfCloseButton" onclick="getElementById('icwpWpsfSiteBadge').remove();">x</a>
85
- <a href="http://icwp.io/wpsecurityfirewall" target="_blank" title="This site is protected by the Shield Security plugin.">
86
  <img src="%s" alt="%s Logo" />
87
  <div class="badge-text">%s</div>
88
  </a>
82
  </style>
83
  <div id="icwpWpsfSiteBadge">
84
  <a id="icwpWpsfCloseButton" onclick="getElementById('icwpWpsfSiteBadge').remove();">x</a>
85
+ <a href="https://icwp.io/wpsecurityfirewall" target="_blank" title="This site is protected by the Shield Security plugin.">
86
  <img src="%s" alt="%s Logo" />
87
  <div class="badge-text">%s</div>
88
  </a>
templates/php/index.php CHANGED
@@ -2,10 +2,10 @@
2
  $sBaseDirName = dirname( __FILE__ ).DIRECTORY_SEPARATOR;
3
 
4
  if ( $flags[ 'wrap_page_content' ] ) : ?>
5
- <div class="wrap">
6
  <div class="bootstrap-wpadmin1 icwp-options-page <?php echo $data[ 'mod_slug' ]; ?>">
7
  <h1 style="height: 0; display: none"></h1>
8
- <?php endif;?>
9
 
10
  <?php if ( $flags[ 'access_restricted' ] ) : ?>
11
  <?php include( $sBaseDirName.'access_restricted.php' ); ?>
@@ -16,7 +16,7 @@ if ( $flags[ 'wrap_page_content' ] ) : ?>
16
  <?php if ( $flags[ 'wrap_page_content' ] ) : ?>
17
  </div><!-- / bootstrap-wpadmin -->
18
  </div><!-- / wrap -->
19
- <?php endif;?>
20
 
21
  <?php include_once( $sBaseDirName.'index_footer.php' ); ?>
22
 
@@ -24,3 +24,13 @@ if ( $flags[ 'wrap_page_content' ] ) : ?>
24
  if ( $help_video[ 'show' ] ) {
25
  include_once( $sBaseDirName.'snippets/help_video_player.php' );
26
  }
 
 
 
 
 
 
 
 
 
 
2
  $sBaseDirName = dirname( __FILE__ ).DIRECTORY_SEPARATOR;
3
 
4
  if ( $flags[ 'wrap_page_content' ] ) : ?>
5
+ <div class="wrap">
6
  <div class="bootstrap-wpadmin1 icwp-options-page <?php echo $data[ 'mod_slug' ]; ?>">
7
  <h1 style="height: 0; display: none"></h1>
8
+ <?php endif; ?>
9
 
10
  <?php if ( $flags[ 'access_restricted' ] ) : ?>
11
  <?php include( $sBaseDirName.'access_restricted.php' ); ?>
16
  <?php if ( $flags[ 'wrap_page_content' ] ) : ?>
17
  </div><!-- / bootstrap-wpadmin -->
18
  </div><!-- / wrap -->
19
+ <?php endif; ?>
20
 
21
  <?php include_once( $sBaseDirName.'index_footer.php' ); ?>
22
 
24
  if ( $help_video[ 'show' ] ) {
25
  include_once( $sBaseDirName.'snippets/help_video_player.php' );
26
  }
27
+ ?>
28
+
29
+ <?php if ( !empty( $aPluginLabels[ 'icon_url_128x128' ] ) ) : ?>
30
+ <style>
31
+ #TopPluginIcon {
32
+ background-image: url( "<?php echo $aPluginLabels[ 'icon_url_128x128' ]; ?>" ) !important;
33
+ }
34
+ </style>
35
+ <?php endif;
36
+
templates/php/notices/rate-plugin.php CHANGED
@@ -5,6 +5,6 @@
5
  WordPress community.
6
  <br/><span style="text-decoration: underline">We simply need your help help to reach others</span>...</p>
7
  <p>
8
- <a href="http://icwp.io/wpsfreview" class="button button-primary" target="_blank">
9
  Please, could you leave us a review on WordPress.org?</a>
10
  </p>
5
  WordPress community.
6
  <br/><span style="text-decoration: underline">We simply need your help help to reach others</span>...</p>
7
  <p>
8
+ <a href="https://icwp.io/wpsfreview" class="button button-primary" target="_blank">
9
  Please, could you leave us a review on WordPress.org?</a>
10
  </p>
templates/php/page/login_intent.php CHANGED
@@ -157,14 +157,16 @@
157
  </p>
158
  </div>
159
  </div>
160
- <div class="row">
161
- <div class="col">
162
- <p id="WhatIsThis" class="text-center">
163
- <a href="<?php echo $hrefs[ 'what_is_this' ]; ?>" class="btn btn-link"
164
- target="_blank"><?php echo $strings[ 'what_is_this' ]; ?></a>
165
- </p>
 
 
166
  </div>
167
- </div>
168
  </div>
169
  </div>
170
  </div>
157
  </p>
158
  </div>
159
  </div>
160
+ <?php if ( $flags['show_what_is_this'] ) : ?>
161
+ <div class="row">
162
+ <div class="col">
163
+ <p id="WhatIsThis" class="text-center">
164
+ <a href="<?php echo $hrefs[ 'what_is_this' ]; ?>" class="btn btn-link"
165
+ target="_blank"><?php echo $strings[ 'what_is_this' ]; ?></a>
166
+ </p>
167
+ </div>
168
  </div>
169
+ <?php endif; ?>
170
  </div>
171
  </div>
172
  </div>
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="http://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="http://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://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>
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="http://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://icwp.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="http://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://icwp.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="http://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://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>
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="http://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://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>
templates/php/snippets/plugin_badge.php CHANGED
@@ -82,7 +82,7 @@
82
  </style>
83
  <div id="icwpWpsfSiteBadge">
84
  <a id="icwpWpsfCloseButton">x</a>
85
- <a href="http://icwp.io/wpsecurityfirewall" target="_blank"
86
  title="This site is protected by the Shield Security plugin."
87
  %s>
88
  <img src="%s" alt="%s Logo" />
82
  </style>
83
  <div id="icwpWpsfSiteBadge">
84
  <a id="icwpWpsfCloseButton">x</a>
85
+ <a href="https://icwp.io/wpsecurityfirewall" target="_blank"
86
  title="This site is protected by the Shield Security plugin."
87
  %s>
88
  <img src="%s" alt="%s Logo" />
templates/php/snippets/plugin_badge_widget.php CHANGED
@@ -28,7 +28,7 @@
28
  }
29
  </style>
30
  <div id="icwpWpsfSiteBadge" class="icwp_wpsf_site_badge">
31
- <a href="http://icwp.io/wpsecurityfirewall" target="_blank" title="<?php echo $strings['plugin_name']; ?>">
32
  <img src="<?php echo $hrefs['img_src']; ?>" alt="<?php echo $strings['plugin_name']; ?> Logo" />
33
  <div class="badge-text">
34
  This Site Is Protected By <span style="font-style: italic;">The</span><br/><?php echo $strings['plugin_name']; ?> &rarr;
28
  }
29
  </style>
30
  <div id="icwpWpsfSiteBadge" class="icwp_wpsf_site_badge">
31
+ <a href="https://icwp.io/wpsecurityfirewall" target="_blank" title="<?php echo $strings['plugin_name']; ?>">
32
  <img src="<?php echo $hrefs['img_src']; ?>" alt="<?php echo $strings['plugin_name']; ?> Logo" />
33
  <div class="badge-text">
34
  This Site Is Protected By <span style="font-style: italic;">The</span><br/><?php echo $strings['plugin_name']; ?> &rarr;
templates/php/snippets/pro.php CHANGED
@@ -71,17 +71,19 @@ $aLicKeyInput = $inputs[ 'license_key' ];
71
  <?php echo $flags[ 'button_enabled_check' ] ? '' : 'disabled="disabled"'; ?> >
72
  Check License
73
  </button>
74
- <span class="form-text text-muted">
 
 
 
 
75
  <ul>
76
  <li>URL To Activate: <?php echo $vars[ 'activation_url' ]; ?></li>
77
  <li>Licenses may only be checked once in 20 seconds. Checks more frequent than this will
78
  automatically be skipped</li>
79
  </ul>
80
- </span>
81
  </div>
82
  </form>
83
- <p class="font-weight-bold">Be sure to have first activated your URL in your
84
- <a target="_blank" href="<?php echo $aHrefs[ 'keyless_cp' ]; ?>">Keyless Activation control panel</a>.</p>
85
 
86
  <form method="post" id="ConnectionDebug">
87
  <?php foreach ( $ajax[ 'connection_debug' ] as $sAjKey => $sAjVal ) : ?>
@@ -218,12 +220,12 @@ $aLicKeyInput = $inputs[ 'license_key' ];
218
  $1/month per site ($12/year)</p>
219
  <ol>
220
  <li>Just grab a new license from the
221
- <a href="http://icwp.io/buyshieldpro" target="_blank">One Dollar Plugin here</a>.</li>
222
  <li>Activate your license on your sites using the 'Activate Key' button.</li>
223
  </ol>
224
 
225
  <p class="text-center">
226
- <a href="http://icwp.io/buyshieldpro" target="_blank" id="ButtonBuyNow"
227
  class="btn btn-large btn-success">
228
  Upgrade To Shield Pro Now &rarr;</a>
229
  </p>
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 ) : ?>
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>
templates/php/snippets/widget_dashboard_plugin.php CHANGED
@@ -1,4 +1,8 @@
1
- <p><?echo $sInstallationDays; ?></p>
2
  <hr />
3
- <p><?php echo $sFooter; ?>
4
- <br /><span style="font-size: x-small;"><?php echo $sIpAddress; ?></span></p>
 
 
 
 
1
+ <p><?php echo $sInstallationDays; ?></p>
2
  <hr />
3
+ <p>
4
+ <?php if ( !empty( $sFooter ) ) : ?>
5
+ <?php echo $sFooter; ?><br />
6
+ <?php endif; ?>
7
+ <span style="font-size: x-small;"><?php echo $sIpAddress; ?></span>
8
+ </p>
templates/php/widgets/icwp_common_widgets.php CHANGED
@@ -13,13 +13,13 @@
13
  <div class="row">
14
  <div class="span6">
15
  <ul>
16
- <li><a href="http://icwp.io/2b" target="_blank"><strong>Manage <u>All</u> Your WordPress Sites In 1 Place!</strong></a></li>
17
- <li><a href="http://icwp.io/2c" target="_blank">Check out all our other WordPress Plugins</a></li>
18
  </ul>
19
  </div>
20
  <div class="span5">
21
  <ul>
22
- <li><a href="http://icwp.io/2d" target="_blank"><strong>Visit the plugin Help & Support page</strong></a>.</li>
23
  <li><a href="http://wordpress.org/extend/plugins/wp-simple-firewall/" target="_blank">Show some love, and give this a 5 star rating on WordPress.org.</a></li>
24
  </ul>
25
  </div>
13
  <div class="row">
14
  <div class="span6">
15
  <ul>
16
+ <li><a href="https://icwp.io/2b" target="_blank"><strong>Manage <u>All</u> Your WordPress Sites In 1 Place!</strong></a></li>
17
+ <li><a href="https://icwp.io/2c" target="_blank">Check out all our other WordPress Plugins</a></li>
18
  </ul>
19
  </div>
20
  <div class="span5">
21
  <ul>
22
+ <li><a href="https://icwp.io/2d" target="_blank"><strong>Visit the plugin Help & Support page</strong></a>.</li>
23
  <li><a href="http://wordpress.org/extend/plugins/wp-simple-firewall/" target="_blank">Show some love, and give this a 5 star rating on WordPress.org.</a></li>
24
  </ul>
25
  </div>
templates/twig/emails/user_management/user_login_notification.twig ADDED
File without changes
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="http://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://icwp.io/2k" target="_blank"></a>
14
  {{ sPageTitle|raw }}
15
  </h2>
16
  </div>
templates/twig/snippets/widget_common.twig CHANGED
@@ -13,13 +13,13 @@
13
  <div class="row">
14
  <div class="span6">
15
  <ul>
16
- <li><a href="http://icwp.io/2b" target="_blank"><strong>Manage <u>All</u> Your WordPress Sites In 1 Place!</strong></a></li>
17
- <li><a href="http://icwp.io/2c" target="_blank">Check out all our other WordPress Plugins</a></li>
18
  </ul>
19
  </div>
20
  <div class="span5">
21
  <ul>
22
- <li><a href="http://icwp.io/2d" target="_blank"><strong>Visit the plugin Help & Support page</strong></a>.</li>
23
  <li><a href="http://wordpress.org/extend/plugins/wp-simple-firewall/" target="_blank">Show some love, and give this a 5 star rating on WordPress.org.</a></li>
24
  </ul>
25
  </div>
13
  <div class="row">
14
  <div class="span6">
15
  <ul>
16
+ <li><a href="https://icwp.io/2b" target="_blank"><strong>Manage <u>All</u> Your WordPress Sites In 1 Place!</strong></a></li>
17
+ <li><a href="https://icwp.io/2c" target="_blank">Check out all our other WordPress Plugins</a></li>
18
  </ul>
19
  </div>
20
  <div class="span5">
21
  <ul>
22
+ <li><a href="https://icwp.io/2d" target="_blank"><strong>Visit the plugin Help & Support page</strong></a>.</li>
23
  <li><a href="http://wordpress.org/extend/plugins/wp-simple-firewall/" target="_blank">Show some love, and give this a 5 star rating on WordPress.org.</a></li>
24
  </ul>
25
  </div>
templates/twig/wizard/pages/base.twig CHANGED
@@ -5,7 +5,7 @@
5
  <div id="FooterWizardBanner" class="container-fluid">
6
  <div id="WizardBanner" class="row">
7
  <div class="col-lg-5 offset-lg-3 col-md-5 offset-md-3 col-sm-8 col-8">
8
- <p>Support future development and get exclusive Shield features:
9
  <br />Vulnerability Scanner; Options Import; Email Support+more.</p>
10
  </div>
11
  <div class="col-lg-1 col-md-2 col-sm-2 col-4">
5
  <div id="FooterWizardBanner" class="container-fluid">
6
  <div id="WizardBanner" class="row">
7
  <div class="col-lg-5 offset-lg-3 col-md-5 offset-md-3 col-sm-8 col-8">
8
+ <p>Support future development and get exclusive {{ strings.plugin_name }} features:
9
  <br />Vulnerability Scanner; Options Import; Email Support+more.</p>
10
  </div>
11
  <div class="col-lg-1 col-md-2 col-sm-2 col-4">
templates/twig/wizard/slides/common/base.twig CHANGED
@@ -6,12 +6,12 @@
6
  <div class="container-fluid">
7
  <div class="row">
8
  <div class="col-6">
9
- {% block slide_header_previous %}
10
  <button class="btn btn-outline-secondary btn-block ButtonPreviousSlide">&larr; Previous Step </button>
11
  {% endblock %}
12
  </div>
13
  <div class="col-6">
14
- {% block slide_header_next %}
15
  <button class="btn btn-info btn-block ButtonNextSlide">Next Step &rarr;</button>
16
  {% endblock %}
17
  </div>
@@ -31,9 +31,25 @@
31
  <div class="slide-footer">
32
  {% block slide_footer %}
33
  <hr />
34
- <p>Click 'Next Step' at the top to continue.
35
- Or <a href="{{ hrefs.dashboard }}" style="color: inherit; text-decoration: underline;">go back</a>
36
- to Shield Dashboard</p>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
37
  {% endblock %}
38
  </div>
39
  </div>
6
  <div class="container-fluid">
7
  <div class="row">
8
  <div class="col-6">
9
+ {% block slide_header_previous_top %}
10
  <button class="btn btn-outline-secondary btn-block ButtonPreviousSlide">&larr; Previous Step </button>
11
  {% endblock %}
12
  </div>
13
  <div class="col-6">
14
+ {% block slide_header_next_top %}
15
  <button class="btn btn-info btn-block ButtonNextSlide">Next Step &rarr;</button>
16
  {% endblock %}
17
  </div>
31
  <div class="slide-footer">
32
  {% block slide_footer %}
33
  <hr />
34
+ <div class="row">
35
+ <div class="col">
36
+ <p>Click 'Next Step' at the top to continue.
37
+ Or <a href="{{ hrefs.dashboard }}" style="color: inherit; text-decoration: underline;">go back</a>
38
+ to {{ strings.plugin_name }} Dashboard</p>
39
+ </div>
40
+ </div>
41
+ <div class="row">
42
+ <div class="col-6">
43
+ {% block slide_header_previous_bottom %}
44
+ <button class="btn btn-outline-secondary btn-block ButtonPreviousSlide">&larr; Previous Step </button>
45
+ {% endblock %}
46
+ </div>
47
+ <div class="col-6">
48
+ {% block slide_header_next_top_bottom %}
49
+ <button class="btn btn-info btn-block ButtonNextSlide">Next Step &rarr;</button>
50
+ {% endblock %}
51
+ </div>
52
+ </div>
53
  {% endblock %}
54
  </div>
55
  </div>
templates/twig/wizard/slides/common/base_finish.twig CHANGED
@@ -1,6 +1,6 @@
1
  {% extends 'wizard/slides/common/base.twig' %}
2
 
3
- {% block slide_header_next %}{% endblock %}
4
 
5
  {% block slide_body_middle %}
6
 
@@ -47,5 +47,7 @@
47
  {% block slide_footer %}
48
  <hr />
49
  <a href="{{ hrefs.dashboard }}" class="btn btn-outline-secondary">
50
- &#x21d0; Return To The Shield WordPress Dashboard</a>
51
- {% endblock %}
 
 
1
  {% extends 'wizard/slides/common/base.twig' %}
2
 
3
+ {% block slide_header_next_top %}{% endblock %}
4
 
5
  {% block slide_body_middle %}
6
 
47
  {% block slide_footer %}
48
  <hr />
49
  <a href="{{ hrefs.dashboard }}" class="btn btn-outline-secondary">
50
+ &#x21d0; Return To The {{ strings.plugin_name }} WordPress Dashboard</a>
51
+ {% endblock %}
52
+
53
+ {% block slide_header_next_bottom %}{% endblock %}
templates/twig/wizard/slides/common/base_start.twig CHANGED
@@ -1,3 +1,4 @@
1
  {% extends 'wizard/slides/common/base.twig' %}
2
 
3
- {% block slide_header_previous %}{% endblock %}
 
1
  {% extends 'wizard/slides/common/base.twig' %}
2
 
3
+ {% block slide_header_previous_top %}{% endblock %}
4
+ {% block slide_header_previous_bottom %}{% endblock %}
templates/twig/wizard/slides/common/no_access.twig CHANGED
@@ -8,7 +8,7 @@
8
  <p>To run this wizard you need to have certain privileges that are not met by your current account level.</p>
9
  <p>If this doesn't seem right, please contact your site administrator.</p>
10
 
11
- <a href="{{ hrefs.dashboard }}" class="btn btn-outline-secondary">Go Back To Shield Dashboard</a>
12
 
13
  {% endblock %}
14
 
8
  <p>To run this wizard you need to have certain privileges that are not met by your current account level.</p>
9
  <p>If this doesn't seem right, please contact your site administrator.</p>
10
 
11
+ <a href="{{ hrefs.dashboard }}" class="btn btn-outline-secondary">Go Back To {{ strings.plugin_name }} Dashboard</a>
12
 
13
  {% endblock %}
14
 
templates/twig/wizard/slides/welcome/optin.twig CHANGED
@@ -42,7 +42,7 @@
42
  <hr />
43
 
44
  <h6>Bonus: Join our new Facebook group</h6>
45
- <p><a href="http://icwp.io/cu" target="_blank">
46
  Click here to request access to our new Facebook group</a> where you can ask questions
47
  and help others with WordPress security and in particular, the Shield Security plugin.</p>
48
 
42
  <hr />
43
 
44
  <h6>Bonus: Join our new Facebook group</h6>
45
+ <p><a href="https://icwp.io/cu" target="_blank">
46
  Click here to request access to our new Facebook group</a> where you can ask questions
47
  and help others with WordPress security and in particular, the Shield Security plugin.</p>
48
 
templates/twig/wpadmin_pages/insights/index.twig CHANGED
@@ -1,6 +1,6 @@
1
  {% extends '/wpadmin_pages/base.twig' %}
2
 
3
- {% block h1heading %}<h1>Shield Security Insights</h1>{% endblock %}
4
 
5
  {% block body_head %}
6
  {% endblock %}
1
  {% extends '/wpadmin_pages/base.twig' %}
2
 
3
+ {% block h1heading %}<h1>{{ strings.page_title }}</h1>{% endblock %}
4
 
5
  {% block body_head %}
6
  {% endblock %}
templates/twig/wpadmin_pages/insights/recent_events.twig CHANGED
@@ -1,7 +1,7 @@
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 mb-2 text-muted">Some of the most recent Shield events</h6>
5
  </div>
6
  <div class="card-body overflow_container">
7
  <div class="overflow_inner">
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 mb-2 text-muted">{{ strings.box_receve_subtitle }}</h6>
5
  </div>
6
  <div class="card-body overflow_container">
7
  <div class="overflow_inner">
templates/twig/wpadmin_pages/insights/title.twig CHANGED
@@ -1,20 +1,20 @@
1
  <div id="SectionTitle" class="insights_widget card w-100">
2
  <div class="card-header">
3
- <h5 class="card-title mb-0">Welcome To Shield Security Insights Dashboard</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 Shield 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="http://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="http://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>#}
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>#}